I am trying to send a message to an agent in a specific state from Main. I have already done this many times, but this time AnyLogic is returning the NullPointerException error.
Here is the code I am using to send the message:
User chosen_user = randomWhere(users, t -> t.inState(User.Innactive));
users.get(chosen_user.getIndex()).receive("message");
Using traceln(chosen_users.getIndex()) for printing the index, everything works fine. Just when I plug the index in the function get() it returns the error.
Even if I plug just a random number, let's say users.get(1).receive("message"), it still returns the same error (my population has 700 agents).
Any thoughts?
It will just be because no user is in the Innactive state at some particular time so your randomWhere call returns null.
You also don't need the indirection of using the index; just send the message directly to it using the send function:
if (chosen_users != null) {
send("message", chosen_users);
}
(Your naming is misleading too since chosen_users is always one User agent (or null).)
Related
The addition of ServerValue.increment() (Add increment() for atomic field value increments #2437) was a great news as it allows field values to be increased atomically in Firebase RTDB.
I have an application that keeps inventories and this function has been key because it allows updating the inventory regardless of whether the user is offline at times. However, I started to notice that sometimes the function is executed twice, which completely misstates the inventory in the wrong way.
To isolate the problem I decided to do the following test, which shows that ServerValue.Increment() works wrong when the connection goes from Online to Offline:
Make a for loop function from 1 to 200:
for (var i = 1; i <= 200; i++) {
testBloc.incrementTest(i);
print('Pos: $i');
}
The function incrementTest(i) must increment two variables: position (count from 1 in 1 up to 200) and sum (add 1 + 2 + 3, ..., + 200 which should result in 20,100)
Future<bool> incrementTest(int value) async {
try {
db.child('test/position')
.set(ServerValue.increment(1));
db.child('test/sum')
.set(ServerValue.increment(value));
} catch (e) {
print(e);
}
return true;
}
Note that db refers to the Firebase instance (FirebaseDatabase.instance.reference())
With this, comes the tests:
Test 1: 100% Online. PASSED
The function works properly, reaching the two variables to the correct result (in the Firebase console):
position: 200
sum: 20100
Test 2: 100% Offline. PASSED
To do this I used a physical device in airplane mode, then I executed the for loop function, and when the function finished executing I deactivated airplane mode and checked the result in the firebase console, which was satisfactory:
position: 200
sum: 20100
Test 3: Start Online and then go to Offline. FAILED
It is a typical operating scenario when the Internet Connection goes down. Even worse when the connections are intermittent, you are traveling on a subway or you are in a low coverage site for which Offline Persistence is a desired feature. To simulate it, what I did was run the for loop function in online mode, and before it finished, I put the physical device in airplane mode. Later I went Online to finish the test and see the results on the Firebase console. The results obtained are incorrect in all cases. Here are some of the results:
As you can see, the Increment was erroneously repeated 10, 18 and 9 times more.
How can I avoid this behavior?
Is there any other way to increment atomically a number in Firebase that works properly online / Offline ?
firebaser here
That's an interesting edge-case in the increment behavior. Between the client and the server neither can be certain whether the increment was executed or not, so it ends up being retried from the client upon the reconnect. This problem can only occur with the increment operation as far as I can tell, as all the other write operations are idempotent except for transactions, but those don't work while offline.
It is possible to ensure each increment happens only once, but it'll take some work:
First, add a nonce to write operation that unique identifies this operation. You can use a push key for this, but any other UUID also works fine. Combine this with your original set() call into a single multi-path update call, writing the nonce to a top-level node with a server-side timestamp as its value.
Now in your security rules for the top-level location, only allow the write if there is no existing data. This ensures the secondary writes you're seeing get rejected, and since security rules are checked across multi-path updates as a whole, the faulty increment will get rejected too.
You'll probably want to periodically clean up the node with nonce keys, based on the timestamp value in there. It won't matter for performance (since you're never searching here outside of during the cleanup), but may help control the storage cost for the nonces.
I haven't used this approach for this specific use-case yet, but have done it for others. If you'd include a client-side retry, the above essentially builds your own multi-path transaction mechanism, which is what I needed it for in the past. But since you don't need that here, it's simpler without that.
Based on #puf answer, you can proceed as follows:
Future<bool> incrementTest(int value, int dateOfToday) async {
var id = db.push().key;
Map<String, dynamic> _updates = {
'test/position': ServerValue.increment(1),
'test/sum': ServerValue.increment(value),
'test/nonce/$id': dateOfToday,
};
db.child('previousPath').update(_updates)
.catchError((error) => print('Increment Duplication Rejected ${error.message}'));
return true;
}
Then, in Firebase Security Rules, you need to add a rule in test/nonce/id location. Something as follows:
{
"previousPath": {
"test": {
".read": "auth != null", //It depends on your root rules
".write": "auth != null", //It depends on your root rules
"nonce": {
"$nonce_id": {
".validate": "!data.exists()" //THE MAGIC IS HERE
}
}
}
}
}
In this way, when the device tries to write to the database again (wrongly), Firebase will reject it since it already had a write with that same ID before.
I hope it serves someone else!!!
I'm looking to fetch recorded data using LogBook in a custom Movesense firmware. How do I get the correct byte stream offset for the next GET call when receiving HTTP_CONTINUE?
I'm trying to implement these steps as described in DataStorage.md:
### /Logbook usage ###
To get recording from the Movesense sensors EEPROM storage, you need to:
1. Do **GET** on */Logbook/Entries*. This returns a list of LogEntry objects. If the status was HTTP_OK, the list is complete. If the result code is HTTP_CONTINUE, you must GET again with the parameter StartAfterId set to the Id of the last entry you received and you'll get the next entries.
2. Choose the Log that you are interested in and notice the Id of it.
3. Fetch the descriptors with **GET** to */Logbook/byId/<Id>/Descriptors*. This returns a bytestream with the similar HTTP_CONTINUE handling as above. However you **must** keep re-requesting the **GET** until you get error or HTTP_OK, or the Logbook service will stay "in the middle of the stream" (we hope to remove this limitation in the future).
4. Fetch the data with **GET** to */Logbook/byId/<Id>/Data*. This returns also a bytestream (just like the */Logbook/Descriptors* above).
5. Convert the data using the converter tools or classes. (To Be Continued...)
The problem is basically the same for step 3 and 4. I receive a whiteboard::ByteStream object in the onGetResult callback function but I don't know how to get the correct offset information from it.
I've found a number of different methods seemingly concerning different aspects of number of bytes in ByteStream.h (length, fullSize, transmitted, payloadSize and serializationLength) but I just can't get it working properly.
Basically I would like to do something like this in onGetResult:
if (resultCode == whiteboard::HTTP_CODE_CONTINUE) {
const whiteboard::ByteStream &byteStream = rResultData.convertTo<const whiteboard::ByteStream &>();
currentEntryOffset += byteStream.length();
asyncGet(WB_RES::LOCAL::MEM_LOGBOOK_BYID_LOGID_DESCRIPTORS(), AsyncRequestOptions::Empty, currentEntryIdToFetch, currentEntryOffset);
return;
}
The basic idea is to do the same call again.
So if you do:
asyncGet(WB_RES::LOCAL::MEM_LOGBOOK_BYID_LOGID_DESCRIPTORS(),AsyncRequestOptions::Empty, currentEntryIdToFetch);
and get the response HTTP_CONTINUE, do:
asyncGet(WB_RES::LOCAL::MEM_LOGBOOK_BYID_LOGID_DESCRIPTORS(),AsyncRequestOptions::Empty, currentEntryIdToFetch);
Until you get HTTP_CONTINUE or an error.
If the result code is HTTP_CONTINUE, you must GET again with the parameter StartAfterId set to the Id of the last entry you received and you'll get the next entries.
Might be a bit cryptic but do another asyncGet to the exact same resource until you get HTTP_OK or an http error code.
Also, note that you need to decode the data, a python script can be found here in this answer
I'm trying to retrieve the serial number from a drive using the MC_BR_GetHardwareInfo function block. Since the documentation lacks any kind of example code on this topic I'm getting nowhere.
Which information should I provide to the function block in order to get the desired serial number?
Below sample will crash in the PLC, probably because the function block requires certain pointers to be addressed:
MC_HARDWARE_INFO_REF hwinfo;
MC_BR_GetHardwareInfo(&hwinfo);
You are probably getting a page fault, because you provide the MC_BR_GetHardwareInfo function block (FUB) a wrong type, which leads to random behavior.
A function block is basically a function which requires a reference to a specific type as parameter. This type contains the actual in- and outputs which are used, internal state variables, etc. We need this, because of the synchronous execution of the code. This means unlike a function, you need to call a FUB until it is done.
Let's take a look to the help of the FUB:
Guid: 056444ea-2a15-4af6-a5ae-0675894b17d3
So the FUB needs a reference to the Axis object of which you want to know the HW info and an Execute command. It will give you some status bits, an error code and the actual data you want to have within the structure HardwareInfo of the type MC_HARDWARE_INFO_REF.
First we need to instantiate the FUB by create a variable of its type. We do this in the local *.var file of the task:
VAR
fbGetHwInfo : MC_BR_GetHardwareInfo := (0);
END_VAR
Then we call set the parameters of the FUB and call it, which might look like this:
void _CYCLIC ProgramCyclic(void)
{
//should be set by the application or in watch/monitor; now it only
//executes once
fbGetHwInfo.Execute = 1;
//reference to your axis object; when using a wizard the first axis
//will be gAxis01 on default
fbGetHwInfo.Axis = (UDINT)&gAxis01;
//call the FUB
MC_BR_GetHardwareInfo(&fbGetHwInfo);
if(fbGetHwInfo.Error == 1)
{
//TODO: errorhandling
}
else if(fbGetHwInfo.Done == 1)
{
//TODO use output
//fbGetHwInfo.HardwareInfo
}
}
typically you would do this in some statemachine. Also you probably have to wait until the network to the drive is initialized. You could check this with the MC_BR_ReadDriveStatus FUB. Just for testing it should be enough to wait for some seconds after reboot and set the Execute flag in monitor mode.
[I am new to ADO.NET and the Entity Framework, so forgive me if this questions seems odd.]
In my WPF application a user can switch between different databases at run time. When they do this I want to be able to do a quick check that the database is still available. What I have easily available is the ObjectContext. The test I am preforming is getting the count on the total records of a very small table and if it returns results then it passed, if I get an exception then it fails. I don't like this test, it seemed the easiest to do with the ObjectContext.
I have tried setting the connection timeout it in the connection string and on the ObjectConntext and either seem to change anything for the first scenario, while the second one is already fast so it isn't noticeable if it changes anything.
Scenario One
If the connect was down when before first access it takes about 30 seconds before it gives me the exception that the underlying provider failed.
Scenario Two
If the database was up when I started the application and I access it, and then the connect drops while using the test is quick and returns almost instantly.
I want the first scenario described to be as quick as the second one.
Please let me know how best to resolve this, and if there is a better way to test the connectivity to a DB quickly please advise.
There really is no easy or quick way to resolve this. The ConnectionTimeout value is getting ignored with the Entity Framework. The solution I used is creating a method that checks if a context is valid by passing in the location you which to validate and then it getting the count from a known very small table. If this throws an exception the context is not valid otherwise it is. Here is some sample code showing this.
public bool IsContextValid(SomeDbLocation location)
{
bool isValid = false;
try
{
context = GetContext(location);
context.SomeSmallTable.Count();
isValid = true;
}
catch
{
isValid = false;
}
return isValid;
}
You may need to use context.Database.Connection.Open()
Strange one. We have a multi-threaded app which pulls messages off a MSMQ Queue and then subsequently performs actions based on the messages. All of this is done using DTC.
Sometimes, for some reason I can't describe, we get message read errors when pulling Messages off the queue.
The code that is being used in the app:
Message[] allMessagesOnQueue = this.messageQueue.GetAllMessages();
foreach (Message currentMessage in allMessagesOnQueue)
{
if ((currentMessage.Body is IAMessageIDealWith))
{
// do something;
}
}
When the currentMessage.Body is accessed, at times it throws an exception:
System.InvalidOperationException: Property Body was not retrieved when receiving the message. Ensure that the PropertyFilter is set correctly.
Now - this only happens some of the time - and it appears as though the MessageReadPropertyFilter on the queue has the Body property set to false.
As to how it gets like this is a bit of a mystery. The Body property is one of the defaults and we absolutley never explicitly set it to false.
Has anyone else seen this kind of behaivour or has some idea why this value is getting set to be false?
As alluded to earlier, you could explicitly set the boolean values on the System.Messaging.MessagePropertyFilter object that is accessible on your messageQueue object via the MessageReadPropertyFilter property.
If you want all data to be extracted from a message when received or peaked, use:
this.messageQueue.MessageReadPropertyFilter.SetAll(); // add this line
Message[] allMessagesOnQueue = this.messageQueue.GetAllMessages();
// ...
That may hurt performance of reading many messages, so if you want just a few additional properties, create a new MessagePropertyFilter with custom flags:
// Specify to retrieve selected properties.
MessagePropertyFilter filter= new MessagePropertyFilter();
filter.ClearAll();
filter.Body = true;
filter.Priority = true;
this.messageQueue.MessageReadPropertyFilter = filter;
Message[] allMessagesOnQueue = this.messageQueue.GetAllMessages();
// ...
You can also set it back to default using:
this.messageQueue.MessageReadPropertyFilter.SetDefaults();
More info here: http://msdn.microsoft.com/en-us/library/system.messaging.messagequeue.messagereadpropertyfilter.aspx
I have seen it as well, and have tried initializing it with the properties I'm accessing explicitly set, and not setting them anywhere else. I periodically get the same error you are getting, my app is multi-threaded as well, what I ended up doing is trapping that error and reconnecting to MSMQ when I get it.
Sometimes, for some reason I can't describe, we get message read errors when pulling Messages off the queue.
Are you using the same MessageQueue instance from more than one thread, without locking? In that case, you will encounter spurious changes in MessageReadPropertyFilter - at least I did, when I tried.
Why? Because
Only the GetAllMessages method is thread safe.
What can you do? Either
wrap a lock (_messageQueue) around all access to your messageQueue OR
create multiple MessageQueue instances, one per thread