Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New to Ember.Net - advice needed #18

Closed
AstecSteveB opened this issue Mar 11, 2016 · 58 comments
Closed

New to Ember.Net - advice needed #18

AstecSteveB opened this issue Mar 11, 2016 · 58 comments
Assignees
Milestone

Comments

@AstecSteveB
Copy link

I've gone through the console application tutorial and can parse the tree loaded into TinyEmber+. I'm struggling to get data changes once running - creating a handler for PropertyChanged of a Parameter is not getting called. I can get a data update by using the same method as described in the static approach to reacting to changes. However, my driver will need to receive all parameter changes for a dynamic environment. I am wondering if I am missing a trick to get my consumer updated? I am running the Ember+ Viewer and can see update messages received as I change data in TinyEmber+ - this is what I need in my application :-)

@andreashuber-lawo
Copy link
Contributor

I've gone through the console application tutorial and can parse the tree loaded into TinyEmber+. I'm struggling to get data changes once running - creating a handler for PropertyChanged of a Parameter is not getting called.

Strange, you should be able to get exactly the same notifications through the dynamic Interface as you do through the static interface (it's the same implementation after all).

However, my driver will need to receive all parameter changes for a dynamic environment. I am wondering if I am missing a trick to get my consumer updated? I am running the Ember+ Viewer and can see update messages received as I change data in TinyEmber+ - this is what I need in my application :-)

For this purpose you will need to subscribe to the PropertyChanged event of all parameters.

@AstecSteveB
Copy link
Author

I've parsed the tree down to the Parameter level and attached a handler to the PropertyChanged event. After changing the same parameter value in TinyEmber+, the PropertyChanged event does not fire. Re-reading the documentation it says that the consumer will automatically send GetDirectory commands to get updates for all declared parameters. If you could update your dynamic console application example to show this working that would be great.

@AstecSteveB
Copy link
Author

On a slightly different subject - I used the sample code to try and walk a Junger frame at a customer site and it threw and exception with: "The remote host has failed to answer a KeepAliveRequest within half the timeout period". The Enmber+ Viewer can connect and I am able to view the tree. However, in the message window it reports a Glow Error: Glow DTD version mismatch: found version 2.10, expected 2.31!

@andreashuber-lawo
Copy link
Contributor

If you could update your dynamic console application example to show this working that would be great.

http://lawo.github.io/ember-plus-sharp/html/7cdb703a-14dd-42d3-8ea2-28a9b8af6663.htm

@andreashuber-lawo
Copy link
Contributor

On a slightly different subject - I used the sample code to try and walk a Junger frame at a customer site and it threw and exception with: "The remote host has failed to answer a KeepAliveRequest within half the timeout period".

The standard keep-alive timeout is 3 seconds. Some providers struggle to answer KA requests when they are busy, please see whether increasing the timeout changes anything.

http://lawo.github.io/ember-plus-sharp/html/7345C981.htm

The Enmber+ Viewer can connect and I am able to view the tree. However, in the message window it reports a Glow Error: Glow DTD version mismatch: found version 2.10, expected 2.31!

I'm not the author of the Ember+ Viewer, but I believe you can safely ignore that message.

@andreashuber-lawo
Copy link
Contributor

I assume this has been resolved. If not, feel free to reopen.

@AstecSteveB
Copy link
Author

Yes, thank you. With our driver architecture I will investigate the S101 interface as I need a more polling style approach

@andreashuber-lawo
Copy link
Contributor

Yes, thank you. With our driver architecture I will investigate the S101 interface as I need a more polling style approach

You can do polling with Ember+ Sharp, I just don't understand why you'd want that? Can you please explain?

@AstecSteveB
Copy link
Author

The drivers run as hidden console applications. They handle multiple physical devices with each Device being polled at a custom frequency. When the polling is due, a worker thread is launched that runs to completion. I'd therefore want to create the connection to the Ember device on start-up (or after an error), and then read the contents of the configured tree items during the poll task.

@andreashuber-lawo
Copy link
Contributor

When the polling is due, a worker thread is launched that runs to completion. I'd therefore want to create the connection to the Ember device on start-up (or after an error), and then read the contents of the configured tree items during the poll task.

You can certainly do that, namely by awaiting Consumer.CreateAsync and then immediately calling Consumer.Dispose. You can still call the Consumer.Root getter afterwards and process the retrieved data.

However, as you might have noticed already, Consumer.CreateAsync is often rather resource-hungry, especially for large trees. This is because the data describing the part of the tree you are interested in needs to be queried, received and parsed. But, after this initial setup phase, only changes are sent back and forth, which tends to be orders of magnitude cheaper. So, if at all possible, I would advise you to keep your Consumer objects connected, which gives you immediate access to all the current data when the polling is due.

@AstecSteveB
Copy link
Author

Thanks. I did run tests based on your description above but I only got updated parameter data if I disposed of the consumer and then re-created it (I can't await the on-change method described in the documentation as it blocks until the data changes). If I still use the consumer approach I will look to implement a static interface to greatly reduce the number of tree elements.

@andreashuber-lawo
Copy link
Contributor

I did run tests based on your description above but I only got updated parameter data if I disposed of the consumer and then re-created it

No, if you keep your consumer alive then the values of all parameters should always be identical to the values of their respective provider parameters. Changes are automatically and continually propagated back and forth until you call Consumer.Dispose(). If that doesn't work for you then this sounds like a bug.

(I can't await the on-change method described in the documentation as it blocks until the data changes).

If I understand your scenario correctly, then you don't have to wait for the PropertyChanged event to fire. In your polling handler, you'd simply take the already existing Consumer object and grab whatever data you need from the tree and process it (remember that all properties of all elements are continually kept in sync).

@AstecSteveB
Copy link
Author

OK, I'll re-run my test and if it doesn't work I'll post the code here. Thanks again!

@andreashuber-lawo
Copy link
Contributor

Quick question: To implement the polling interval, are you by any chance calling Thread.Sleep?

@AstecSteveB
Copy link
Author

Yes, it was used in my previous test where I created a loop to monitor the tree values.

@andreashuber-lawo
Copy link
Contributor

Yes, it was used in my previous test where I created a loop to monitor the tree values.

Ahh, this is almost certainly the problem then. Thread.Sleep blocks the current thread, which is something you should almost never do in code that uses async and await. Please try something like await Task.Delay(10000); instead.

@AstecSteveB
Copy link
Author

Good spot! Using Task.Delay allows the tree updates to come through (it shows my lack of knowledge of the async environment). I'll need to think about a different driver implementation as the code has to be wrapped by an AsyncPump.Run() and this doesn't exit unless the client/consumer is disposed. I could implement a poll-style with static elements to reduce the tree size but connecting/disconnecting seems every inefficient and will put a needless load on the remote device. The other approach is to have the AsyncPump.Run() active for the lifetime of the driver. Now that I am getting updates this seems the best approach.
Thanks for your patience!

@andreashuber-lawo
Copy link
Contributor

I'll need to think about a different driver implementation as the code has to be wrapped by an AsyncPump.Run() and this doesn't exit unless the client/consumer is disposed.

Right, you must call both Consumer.Dispose() and S101Client.Dispose(), otherwise AsyncPump.Run() will never complete.

The other approach is to have the AsyncPump.Run() active for the lifetime of the driver. Now that I am getting updates this seems the best approach.

Correct.

@AstecSteveB
Copy link
Author

Using the updated test application to try against a real Junger frame, I am getting an error of form: 'The provider failed to send the children for the element with the path..."
The S101Client timeout is set to 30000 and the buffer size is set to 32000.
I've also set the TcpClient receive timeout to 30000.
Are there any other timeouts that can be configured, assuming this is a timeout issue?

@andreashuber-lawo
Copy link
Contributor

The S101Client timeout is set to 30000 and the buffer size is set to 32000.

The default for the buffer size of 8192 should be adequate for pretty much all applications.

I've also set the TcpClient receive timeout to 30000.

That's probably not necessary either...

Are there any other timeouts that can be configured, assuming this is a timeout issue?

Yes, most likely you need to increase the default of 10000 passed to Consumer.CreateAsync:

http://lawo.github.io/ember-plus-sharp/html/8F30E950.htm

@AstecSteveB
Copy link
Author

I've set all timeouts to 60000 and now I don't get an error but the Consumer.CreateAsync hasn't returned (after apprimately 5 minutes of waiting). The Ember+ Viewer can connect and view all items. Note that the Junger Frame has 20 modules present.

@andreashuber-lawo
Copy link
Contributor

I've set all timeouts to 60000 and now I don't get an error but the Consumer.CreateAsync hasn't returned (after apprimately 5 minutes of waiting).

Hmm, that's odd. Can you please share the snippet of code that you use to connect? Thanks.

@AstecSteveB
Copy link
Author

Please find attached log file as requested:

log.zip

@andreashuber-lawo
Copy link
Contributor

Thank you very much, this confirms my suspicion. I will fix this as soon as I can.

@AstecSteveB
Copy link
Author

No problem. Thank you for your support.

andreashuber-lawo added a commit that referenced this issue Apr 4, 2016
@andreashuber-lawo
Copy link
Contributor

The bug should be fixed. As expected, the performance hit is rather hefty (+~70%). I will try to optimize this further. May I ask you to try again with the current release?

Thanks for your patience!

@AstecSteveB
Copy link
Author

Using the static tree data is being returned 👍
When I tried to use the dynamic tree I got the error: 'The remote host has failed to answer a KeepAliveRequest within half the timeout period'
Is it possible to create a static tree programmatically as I could convert my driver configuration to a tree before the CreateAsync?

@andreashuber-lawo
Copy link
Contributor

When I tried to use the dynamic tree I got the error: 'The remote host has failed to answer a KeepAliveRequest within half the timeout period'

Depending on how long you can afford to wait for Consumer.CreateAsync to return, you might be able to fix this by increasing the timeout passed to S101Client. Your mileage may vary though, internally we have providers where the retrieval of the full tree takes more than 5 minutes and the resulting in-memory data structure uses more than 100MB. Consequently, nobody ever downloads the full tree for these providers.

Is it possible to create a static tree programmatically as I could convert my driver configuration to a tree before the CreateAsync?

Not currently, but it shouldn't be too hard to implement.

@andreashuber-lawo
Copy link
Contributor

Let me clarify: There's no way to use the static interface programmatically. After all, the compiler must know the types so that you can access their properties in your program. However, the dynamic interface can be extended, so that only the relevant nodes are requested from the provider.

@AstecSteveB
Copy link
Author

Let me clarify: There's no way to use the static interface programmatically. After all, the compiler must know the types so that you can access their properties in your program. However, the dynamic interface can be extended, so that only the relevant nodes are requested from the provider.

This was my understanding. It would be the most efficient approach for my driver interface too. Is this something you could implement in the near future?

@andreashuber-lawo
Copy link
Contributor

Is this something you could implement in the near future?

Define "near future" ;-). Seriously, I'm currently swamped with other work, but I'll probably have something usable in two weeks. Can you test and implement with the static interface in the mean time?

@AstecSteveB
Copy link
Author

If you could have something to test in two weeks that would be fantastic - I am not wishing to add to your stress levels and thank you for your continued support. I can perform some more basic testing but with the configurable nature of our system there can be many permutations of driver setup so the static approach isn't really a viable solution.

@andreashuber-lawo
Copy link
Contributor

BTW, have you had a look at the mixed interface part of the tutorial yet? Depending on how configurable your system is, the mixed interface approach could help you to confine the full retrieval of data to the branches you're interested in.

@AstecSteveB
Copy link
Author

I have taken another look at the mixed mode. I would need to perform a detailed site survey to review how all the frames are populated (currently not all the equipment has Ember enabled). This would only be a temporary solution as it would be fixed for one manufacturer.

@andreashuber-lawo
Copy link
Contributor

Ok, understood. I'll let you know as soon as I have something usable.

@andreashuber-lawo
Copy link
Contributor

As always, things took longer than anticipated. Now that I'm on it, it turns out that #21 will require quite a bit of effort to get right. With all the other stuff that's currently on my plate, I'm afraid it will be at least another two weeks before I might have something for you to try. Sorry!

@AstecSteveB
Copy link
Author

Ok. Thanks for your continued support.

@andreashuber-lawo
Copy link
Contributor

andreashuber-lawo commented Apr 22, 2016

I've pushed a commit that shows how the interface for #21 will probably look like. If you like, you can have a look at:

@andreashuber-lawo
Copy link
Contributor

It looks like I'm done. Please have a look at the new release and let me know whether the new feature works as you would expect. Here are direct links to the documentation:

@AstecSteveB
Copy link
Author

Sorry, I've been busy on other projects. I'll try and test this later today.

Thanks

@AstecSteveB
Copy link
Author

Sorry for the delay. I've rebuilt by solution and confirmed that by testing with TinyEmber the original approach works. I'm trying to understand how I define the items I am interested in. For example, using the Sapphire example, if I just wanted to access "Sapphire/Sources/FPGM 1/Fader/dB Value" and "Sapphire/Sources/FPGM 2/Fader/dB Value" how would this be achieved?

@andreashuber-lawo
Copy link
Contributor

In the Main Method you'd call CreateAsync and pass ChildrenRetrievalPolicy.DirectOnly. When CreateAsync returns, the Children property of Consumer.Root will contain the node with the identifier "Sapphire", but the "Sapphire" node itself does not yet have any children. You'd then set the ChildrenRetrievalPolicy property of the "Sapphire" node to ChildrenRetrievalPolicy.DirectOnly and await Consumer.SendAsync. When SendAsync returns the Children property of the "Sapphire" should contain its direct children (the nodes "identity" and "Sources"). You'd then repeat the process with the "Sources" node and so forth until you have all children you are interested in.

@AstecSteveB
Copy link
Author

Perfect. I'll implement and report back

@AstecSteveB
Copy link
Author

This is working fine with TinyEmber 👍 . I'll now test it with a Junger frame

@AstecSteveB
Copy link
Author

Pulling Meter data back from a Junger frame:

ember

@andreashuber-lawo
Copy link
Contributor

Congratulations! 👍

@AstecSteveB
Copy link
Author

Thank you so much for your support

@andreashuber-lawo
Copy link
Contributor

You're welcome, I'm glad I could help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants