Jump to content

Rust solution for rugged approach


Recommended Posts

Hi all,

 

After years of using the C++ API bindings for my projects, I gave Rust a try some weeks ago. Your API for Rust is just great! Thanks a lot for this!

 

I've ported all of my projects, but I'm missing one last thing:

What's the best way to implement the brick and bricklet configurations in the enumeration callback (as described in your rugged approach)? To be able to create bricklet objects, for instance, access to the IpConnection is required.

 

One could use an atomic reference pointer to be able to clone the IP connection as an input for the spawned enumerate callback receiver (to avoid Rusts' "lifetime conflicts"). However, at least at the moment, that doesn't play well with some of my other struct implementations and rustc doesn't like it. It's not unlikely that I'm doing something wrong, given that I started working with Rust only three weeks ago. I'm a very experienced C/C++ developer, but the way Rust does some things is really different!

 

As a starting point, I've used your authenticate example (https://raw.githubusercontent.com/Tinkerforge/generators/master/rust/example_authenticate.rs). In your example in line 8, you used an IpConnectionRequestSender as input for the authenticate callback function. I'm looking for something similar for the enumeration callback. Unfortunately, I can't figure out a good way that is both elegant and thread-safe. How can I get thread-safe access to the IpConnection object in my spawned enumeration callback?

 

Has someone found a good way to do it? I would appreciate any suggestions.

 

Cheers!

Link to comment
Share on other sites

Hi,

You've found a design mistake in the bindings. The IpConnectionRequestSender has the purpose to allow multi-threaded calling of IpConnection functions and device creation, but for some reason I've forgot to accept the RequestSender in the [device]::new functions.

 

The attached version (which will probably be released as 2.0.11 soon) fixes this. With this version everything you can do with an IpConnection should also be possible with the RequestSender, that can be cloned for as many threads as needed. Also attached is an example, that shows how to use the RequestSender in an enumerate callback handler.

 

To use the local version of the bindings, extract the src folder and Cargo.toml into a tinkerforge folder inside your project's src folder, remove the tinkerforge=... line in your Cargo.toml and add

[dependencies.tinkerforge]
path = "src/tinkerforge"

instead.

 

Erik

tinkerforge_rust_bindings_2_0_11.zip

rust_ipcon_fix_example.zip

Link to comment
Share on other sites

Hi Erik,

 

thanks a lot. I tried your fix and it works nicely.

 

Another question came up when using the new version:

What happens with the thread (that was spawned from within the enumerate callback function), when the bricklet cable gets disconnected? One could check for EnumerationType::Disconnected and do some manual clean up work (that's not the problem). But how do I make sure, that the spawned thread (related to the previously connected bricklet) will be joined? Is manual clean up required?

 

In cases without using the enumerate callback function, I guess the spawned thread joins automatically when the callback_receiver object (ConvertingCallbackReceiver) is dropped, which in turn happens after the bricklet object goes out of scope? How will the binding handle the "enumerate callback function" case?

 

Will the callback_receiver object be dropped automatically after the cable is disconnected? Or do I have to store the JoinHandle so I can do the manual clean up inside the "EnumerationType::Disconnected" condition?

 

 

Background for my question:

I'm trying to develop Rust code that properly responds to changed hardware components while running, so one could achieve Hot Plugging support.

 

Thanks again for your great work. I really appreciate it!

 

 

Cheers,

Claudio

Link to comment
Share on other sites

Another question came up when using the new version:

What happens with the thread (that was spawned from within the enumerate callback function), when the bricklet cable gets disconnected? One could check for EnumerationType::Disconnected and do some manual clean up work (that's not the problem). But how do I make sure, that the spawned thread (related to the previously connected bricklet) will be joined? Is manual clean up required?

You mean threads spawned in the enumerate callback to handle callbacks of the found devices? If you use the

thread::spawn(move || {
    for x in receiver {
        //[...]
    }
}

pattern, then the thread will join, when the corresponding sender is dropped in the IpConnection, e.g. when the IpConnection is dropped. Another way would be to explicitly drop the receiver, but then you can not use the for loop pattern anymore.

 

In cases without using the enumerate callback function, I guess the spawned thread joins automatically when the callback_receiver object (ConvertingCallbackReceiver) is dropped, which in turn happens after the bricklet object goes out of scope?

Dropping the bricklet does not close callback channels. Instead you have to either drop the IpConnection or the receiver. The device object does not hold any information abount the callback channels.

 

Background for my question:

I'm trying to develop Rust code that properly responds to changed hardware components while running, so one could achieve Hot Plugging support.

 

Unfortunately, hot plugging devices to already powered stacks is not supported (for electrical reasons as well as in software). The EnumerationType::Disconnected is only sent, if the Brick Daemon on a PC notices that the USB connection to a stack is lost.

Link to comment
Share on other sites

Thanks for clarification!

 

Unfortunately, hot plugging devices to already powered stacks is not supported (for electrical reasons as well as in software). The EnumerationType::Disconnected is only sent, if the Brick Daemon on a PC notices that the USB connection to a stack is lost.

 

Brick Viewer creates new tabs with data plots, after you connect bricklets to the powered brick. At least it does it for coprocessor-based bricklets (Accelerometer V2 in my case). It doesn't remove tabs from the GUI for previously connected bricklets though (they just stay there and won't update). Is that what you mean with "hot plugging devices to already powered stacks is not supported"? At least your software supports adding new devices while in operation.

 

Bricklets without a co-processor won't automatically show up in Brick Viewer while in operation (I tried with some older bricklets).

Link to comment
Share on other sites

Is that what you mean with "hot plugging devices to already powered stacks is not supported"? At least your software supports adding new devices while in operation.

 

Bricklets without a co-processor won't automatically show up in Brick Viewer while in operation (I tried with some older bricklets).

Not supported in this case means "works in some cases, but not in enough to support it.": The firmware of bricklets with co-processor sends an enumerate when booting and bricks poll their ports with an exponentional decreasing rate, which in conjunction allows hot plugging. But bricks are not able to tell if a bricklet gets disconnected. So if your use case only requires adding co-processor bricklets and never removing them, you could reasonably expect this to work, but as I've said it is not supported.

Link to comment
Share on other sites

Thanks again!

 

I fully understand your warning that hot plugging might damage the electronics. What I noticed just today is that you even display a message in Brick Viewer in the bottom left corner saying "Hot plugging is not supported! Please reset ...". This small message can easily be overseen though. Maybe it's worth to display this message in a new window with bigger font size.

 

 

Just another quick question regarding your

 

hot plugging devices to already powered stacks is not supported (for electrical reasons ...)

 

note.

 

Would it help to use Isolator bricklets between the master brick and my Accelerometer V2 bricklets? Could it make the unsafe hot plugging of co-processor bricklets a little less unsafe with an isolated bricklet? Although there is an "Open Bricklet" button in Brick Viewer (plugins folder, isolator.py, line 72), the API doesn't give a hint on its ability to manually control the isolator state. Does the term "Open" in this case indicate that the port is open and power/data is connected?

 

 

My use case would be to check whether a co-processor bricklet got replaced by another one of the same kind (using the very same uid and brick position). The hardware replacement would only happen in case the bricklet got damaged. Say, the software wouldn't get connection to the bricklet anymore, even after re-establishing the IP connection to the brick daemon and resetting the master brick. In that case, I would just replace the bricklet and - in best case - wouldn't have to disconnect the RED and master brick from power supply.

 

What one could additionally do is to reset the master brick after the replacement and reopen the IP connection to go back to its default state.

 

 

Thanks for your support!

Link to comment
Share on other sites

Hot-connecting of the new Bricklets with 7p connector works in the majority of the cases. What definitely does not work is hot-unplugging. There is no reliable mechanism that could be used to find out if a Bricklet is not plugged in anymore.

 

The biggest problem with the unplugging is the routing tables that the Brick Daemon and each Brick has*.

 

However, if you replace a Bricklet with another Bricklet of the same kind and the same UID, there is no problem with the routing tables. The only problem is that the new Bricklet does not have the settings anymore that you set in the old Bricklet. So maybe you can have a loop that reads a configuration (that is set to some non-default value) every few seconds and assumes that a new Bricklet was put in if it changes back to default?

 

 

* Example: You have a stack that is connected to another stack with RS485 and your Bricklet is connected to a slave in the stack. If you call a function in your program the data will be send to the Brick Daemon. The Brick Daemon sends it to the correct USB port to a Master Brick, the Master Brick sends it via RS485 to the other Master Brick which sends it to the slave in the stack which send it to the correct Bricklet port. Every Brick in the chain and the Brick Daemon has a "routing table" that it uses to determine where a packet has to go.

 

If you now unplug this Bricklet and plug it in somewhere else, the routing tables will be permanently broken and messages to this Bricklet may or may not be routed correctly.

Link to comment
Share on other sites

  • 4 weeks later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...