I’m trying to make a component to receive data from a web socket. The code of the current version is very simple and it actually works, but in a strange way; so I’d be glad for any insights on its behavior.
The component has a text input parameter for the url (as an item), and a text output, also as a single string.
It uses the WebSocketSharp library to connect and manage the stream.
This is the code responsible for the connection and data output:
The main issue is that although the data does change in the output parameter when receiving new messages from the web socket, the solution itself isn’t recomputed, so other components do not receive an updated data.
I was able to solve this by adding a “Data” component connected to the output of this component, and refreshing it with a timer every second or so. But I suspect there should be a better solution.
The other problem is that the output data is not a single item, as it’s supposed to be, but a tree with two branches: first branch with a single item, and the second branch with the text received from the web socket.
The second weird solution was to disable the component after it has connected to the web socket stream - this way it keeps receiving messages, and the output of the component is a single string.
You are assigning e3.Data to the output after the component has been updated, so you need to update it again when you receive the message, something like
but keep in mind that the component (and its downstream components) will run once before receiving the message. Then you have to handle this or prevent the component from completing SolveInstance until it receives the message.
The second strange behavior I think is because you are assigning to the output a data in a phase outside the component’s native flow (after it is updated), leaving behind all the internal processes it needs to do to complete the new update. @DavidRutten can help you hack into this in the right way.
I would assign the retrieved data to a class level variable inside the component, assign it from that variable to the correct output from within SolveInstance, and make the OnMessage callback first assign the data and then call ExpireSolution(true);
Hi @Dani_Abalde and @DavidRutten, thank you for prompt reply, works perfect so far!
In addition to the message (string) variable I declared two other variables outside SolveInstance, to prevent the component from creating a new web socket connection each time it’s recomputed:
The WebSocket object; so it’s created just once.
A boolean that indicates if the connection to web socket has been established already
Well, there is another bug happening after the changes -
I have a panel connected to the output of the web socket. The data in the panel is updated after every new message from the web socket (as expected), but if I’m moving or resizing the panel while it is receiving new data it causes GH to crash after a few seconds:
First alert:
…and then RSOD (GH version of BSOD )
*as you can see, even after the errors the WS stream is still connected and the panel text is updated.
I thought this might be caused by too frequent updates of the data from WS. Does it make sense?
If so, will it help to store the data in a variable inside the component on every web socket message, but to output it in regular intervals, let’s say, every 100-500 ms?
To be fair the RSOD is really the RSOSYFN*. It’s a protective measure which can be disabled from the View or display menu (can never remember which one).
And is this happening on the UI thread, or are you just calling methods on whatever thread the message came in on?
Not really. The computer cares about the ordering of events, but not how quickly they follow each other. It is possible that rapid succession of some events may highlight a problem that was there all along though.
I wasn’t able to find this in any of the menus, however, I’m not sure it’s a good idea to disable protective stuff.
I guess it’s the second option, but it seems to be beyond my knowledge in gh development.
In general: OnMessage event stores the message text in a component class variable and calls ExpireSolution. Then, the data from the class variable is sent to the first output in the next solution.
The first might lead to problems due to non-atomic writes/reads of that class level variable, this can be solved by putting a lock on an object every time you write or read the data (it’ll be atomic if you’re assigning single class references, it won’t be if you’re assigning more than one, or value-types whose memory layout exceeds 64-bits).
The latter is just in general a really bad idea if you’re not on the UI thread.
Here’s what I recommend for access to your class level variables:
As for the expiration, you’re probably better off scheduling a solution on the GH_Document a few milliseconds in the future. You’ll have to provide a callback for the schedule so you can expire your component before it happens:
// Inside the socket message handler:
GH_Document doc = OnPingDocument();
if (doc != null)
doc.ScheduleSolution(10, ScheduleCallback);
// your callback delegate.
private void ScheduleCallback(GH_Document doc)
{
ExpireSolution(false);
}
Thanks again David, you are a genius!
Although the debugging in this case is just dragging a panel around the canvas hoping it won’t crash, the issue seems to be solved.
Which processing method? If you call methods on Grasshopper that change the state of the application, such as ExpireSolution(), then you need to make sure you’re doing so on the UI thread. You can use Rhino.RhinoApp.InvokeOnUiThread for this.
The only method in Grasshopper that doesn’t suffer from this thread-cross-contamination is GH_Document.ScheduleSolution.