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

Support: client vs device context #561

Open
chrsoo opened this issue Sep 6, 2024 · 5 comments
Open

Support: client vs device context #561

chrsoo opened this issue Sep 6, 2024 · 5 comments

Comments

@chrsoo
Copy link

chrsoo commented Sep 6, 2024

Hi,

I am writing a wrapper library for INDIGO as a way of learning Rust and assuming something useful comes out of it, an alternative to C for developing INDIGO clients, servers, and drivers.

Currently I am focusing on adding Rust support for creating remote clients, as this would be my first real world usecase.

As all Rust novices my issues mainly revolve about ownership and borrowing, hence the following question:

  • Who owns the indigo_client->client_context field?

I understand that a client application can stuff any data it desires in the client_context field and INDIGO will leave this untouched. Is this correct?

If correct, this serves me very well as I use the field for linking the INDIGO C-library to my Rust code through an unsafe raw pointer (unsafe from Rust's point of view). The alternative would be to maintain a global state where all clients are registered in a singleton in order to handle the asynchronous callbacks from INDIGO. I way prefer the first option.

I also need to link the devices to my Rust code. Most of the callbacks provide a client parameter that can I could leverage to find the right Rust counterpart, but not so for attach and detach. These two callbacks only have references to the indigo_device struct and I need a way to link them back to my Rust code. Again, I need to either store an unsafe Rust pointer in the indigo_device struct... or use a singleton managing a global list of all devices...

At first I thought I could use the device_context in the same way as the client_ context, but then I realised that this field has its own INDIGO managed struct. Then I tried using private_data until I found that whatever I write to this field is reset each time the callbacks are invoked. Now I understand this field to be owned by the device driver code, so also off-limits.

  • Is there a way to link the device callbacks to my Rust code without resorting to a globally maintained state?

Any feedback or suggestions to the above are welcome!

Assuming I understand things correctly, I think a generic purpose "user" field on indigo_device where I as a downstream INDIGO developer can store a pointer to my own code, would be great. Similar to how I use the client_context to link to my own Rust Client struct. Also, if I am abusing the client_context it would be great with the same field on the indigo_client struct.

/Chris

@polakovic
Copy link
Member

I understand that a client application can stuff any data it desires in the client_context field and INDIGO will leave this untouched. Is this correct?

Yes, if you write your own "client" connected to INDIGO bus, it is true and it may work as you described.

Nevertheless, real world client applications usually build some own representation of the hierarchy service (or server) > device > property group > property > item updated by define_property(), update_property() and delete_property() callback methods on the single INDIGO client connected to the bus. The rest of the application actually operates on this model.

I also need to link the devices to my Rust code. Most of the callbacks provide a client parameter that can I could leverage to find the right Rust counterpart, but not so for attach and detach. These two callbacks only have references to the indigo_device struct and I need a way to link them back to my Rust code. Again, I need to either store an unsafe Rust pointer in the indigo_device struct... or use a singleton managing a global list of all devices...

I'm not sure I understand. In INDIGO there is indigo_device structure/API intended for writing drivers and indigo_client structure/API for writing clients. If you want to write client, you will receive define_property, update_property and delete_property callbacks and each of them has property structure as a parameter which contains device name. These callbacks have also device structure as a parameter, but it is not necessary the actual device, in a distributed setup it is protocol adapter providing connection to a remote bus with actual devices connected, you generally shouldn't use it. All you need to talk to the device is its name and if you need to associate some data with the device, you should associate it with the name (e.g. in the model I mentioned above). device_context/private_data is used exclusively by the driver, you can't use them.

It is very exiting somebody is going to try to use rust for INDIGO development, keep us informed :)

@chrsoo
Copy link
Author

chrsoo commented Sep 7, 2024

... on the single INDIGO client connected to the bus. The rest of the application actually operates on this model.

I was assuming that I could connect multiple clients to the bus in the same runtime process, and have been structuring the solution accordingly.

I'm not sure I understand. In INDIGO there is indigo_device structure/API intended for writing drivers and indigo_client structure/API for writing clients...

The reason I was assuming that I need to handle the device callbacks for client code, is that I get an error disconnecting the client: it tells me that the root device is still attached in spite of first detaching the client and getting the corresponding callback, before disconnecting.

My conclusion from this issue was that I also needed to detach all attached devices in addition to detaching the client, which is why I was looking into the device's attach/detach functions and corresponding callbacks on the indigo_device struct.

After your feedback, I now understand that I should be able to create a working remote client based on only on the indigo_client struct with corresponding functions and callbacks, so I will debug the detach issue and refactor accordingly.

But...

  • What is the difference between local vs remote clients?

The client is clearly always local as it would otherwise be a server... so I guess it is about receiving property events from local vs remote devices?

It is very exiting somebody is going to try to use rust for INDIGO development, keep us informed :)

Will do. As said, if anything useful comes out of it I will be happy to share the code. So far I am just trying to make sense of both Rust and INDIGO at the same time and there is no point in making others suffer from my first, faltering steps ... :-)

@polakovic
Copy link
Member

I was assuming that I could connect multiple clients to the bus in the same runtime process, and have been structuring the solution accordingly.

Sure, you can connect multiple clients, but it may be less efficient, because each of them will probably need some kind of "model" updated by bus events.

My conclusion from this issue was that I also needed to detach all attached devices in addition to detaching the client, which is why I was looking into the device's attach/detach functions and corresponding callbacks on the indigo_device struct.

Strictly speaking it is not necessary, but you save resources and also some drivers are mutually exclusive (e.g. we have two different QHY camera drivers).

After your feedback, I now understand that I should be able to create a working remote client based on only on the indigo_client struct with corresponding functions and callbacks, so I will debug the detach issue and refactor accordingly.

Remote client basically mean client connected to different bus. Buses are connected by network protocol adapters, that one on server side acts as client on server bus and that one on client side acts as driver on client bus.

What is the difference between local vs remote clients?

The client is clearly always local as it would otherwise be a server... so I guess it is about receiving property events from local vs remote devices?

That's nice, that there is no substantial difference. Splitting bus to two buses connected over network should be transparent for the client.

If you have RPi running INDIGO Sky server (with INDIGO bus with connected devices) and macOS client (with INDIGO bus with connected client) connected over network, the only difference for client connected to server bus and client bus is, that on the first one device is called "Some Device" while on client bus it is called "Some Device @ indigosky".

But don't forget about INDIGO agents. The point is to keep application logic as close to the devices as possible to avoid issues with network throughput and reliability. In such case you don't connect from the client to the devices directly, but to the agent running on remote server.

@chrsoo
Copy link
Author

chrsoo commented Sep 17, 2024

(Peter) I'm not sure I understand. In INDIGO there is indigo_device structure/API intended for writing drivers and indigo_client structure/API for writing clients. If you want to write client, you will receive define_property, update_property and delete_property callbacks and each of them has property structure as a parameter which contains device name. These callbacks have also device structure as a parameter, but it is not necessary the actual device, in a distributed setup it is protocol adapter providing connection to a remote bus with actual devices connected, you generally shouldn't use it. All you need to talk to the device is its name and if you need to associate some data with the device, you should associate it with the name (e.g. in the model I mentioned above). device_context/private_data is used exclusively by the driver, you can't use them.
...

So, when I call indigo_device_connect I should construct a new indigo_device using only its name? Everything else is ignored?

(me) My conclusion from this issue was that I also needed to detach all attached devices in addition to detaching the client, which is why I was looking into the device's attach/detach functions and corresponding callbacks on the indigo_device struct.

(Peter) Strictly speaking it is not necessary, but you save resources and also some drivers are mutually exclusive (e.g. we have two different QHY camera drivers).

Sorry if I am being a bit daft, but I am still confused... Assuming I write a remote client running on MacOS that connects to a bus running on an INDIGO Sky server, do I ever need to detach or attach devices running on the INDIGO Sky server from the code running on MacOS?

If I do, it appears that the only way is to use the device API based on the indigo_device struct which contradicts what you say above about client development, right?

@polakovic
Copy link
Member

So, when I call indigo_device_connect I should construct a new indigo_device using only its name? Everything else is ignored?

Actually, device in indigo_device_connect is just a name (string). From the client side device is identified by the name only, indigo_device is used on the driver side.

Sorry if I am being a bit daft, but I am still confused... Assuming I write a remote client running on MacOS that connects to a bus running on an INDIGO Sky server, do I ever need to detach or attach devices running on the INDIGO Sky server from the code running on MacOS?

No, attaching or detaching devices is server side operation. But if you really want, can do it indirectly from the client by manipulating server "device" properties.

If I do, it appears that the only way is to use the device API based on the indigo_device struct which contradicts what you say above about client development, right?

indigo_device structure and API is intended only for server side (driver) development. On the client side you need to use indigo_client structure. Maybe look at this example:

https://github.com/indigo-astronomy/indigo/blob/master/indigo_examples/remote_server_client.c

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

No branches or pull requests

2 participants