-
Notifications
You must be signed in to change notification settings - Fork 3
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
Message Encoding #43
Comments
I like the idea to encode Transport Layer messages with some simple protocol (e.g. json) in order to reduce complexity in basic message handling. That way it is easier to write additional Components in different languages. For example I see someone using Labview for Instrument control. However, I guess it is quite some work to use Apache avro in Labview (at least a quick research did not show an answer). |
OTOH, you'll then need some way to distribute schema information, do handshake, version comparison, etc. (what Avro does behind the scenes. Wouldn't/couldn't Labview be a Driver component accessed via an Actor? Or can we provide a translation component? Can Labview interface with a C/C++/C# API (I guess yes)? |
I have to admit, Avro was a bit of a roadblock for me when I looked at yaq. If it enables a rather easy backend to provide simple commands on the "frontend", i.e. in the case of yaq it looks a bit like if we were talking to a pymeasure |
I see the advantage of avro, that you know the capabilities of the other side. But I see the same problem as @bklebel and am still hesitant, whether I want to use Avro. I want to try it first. My original proposal was a more simple setup: The user (director) has to know the possible attributes to get/set and methods to call. For example you would send "get, some_property" and the Actor will try to read "some_property" (for example via "getattr" in Python). |
For which the Actor has to send some kind of schema to the Director (which is built into Avro already, including version compatibility etc). @bklebel e.g., I'm not sure where using Avro actually complicates things for your students. The protocol would autoconstruct the Avro stuff/schema from e.g. any pymeasure Instrument, no need for the students to touch that. The pymeasure-leco interface is not something that students will write. Message content will be serialized/deserialized by pyleco Components (or e.g. the logging solution). Where do you see your students interacting with Avro directly? |
I disagree. Hardware devices do not typically tell you what commands you might send them, you have to look that up in the manual. Of course, that manual then too needs to be written, and stored in a safe space, as a matter of fact, we have an Instrument in our lab for which we do not have a manual, and which does not have a clear brand/name/serial number/most anything to search for a manual, and being able to request the command set would be nice, no doubt.
And what if I want to use some obscure Driver I found on the internet, for some obscure Device? I cannot expect that whatever we put into pyleco will be able to handle it out of the box. Somebody will need to implement that. When I compare our system to a LEGO set, e.g. a police station, or something bigger, some stuff from movies, if I don't consider building it as play now, but only toying around with the finished thing (whatever finished means), it is not like I can only ever play with it if I put everything together as it is described. I can start fooling around with individual bricks from the start if I have a fancy. It is kind of strongly suggested to go through with building it, but it is still optional. Parts of that might also boil down to good documentation, the cake everybody wants to eat but almost nobody wants to make (it seems to me, including myself). I will need to think more about it. |
No, it is not necessary to send the schema etc. Still a device may send its capabilities (via some command. Maybe we start with a simple message layer (I'm for json) and fiddle in parallel with avro. The header could indicate the version/type of content (json, avrò, binary...) |
I agree. At least, Avro is encoded in json, and will not be invalidated if we add more additional commands. I was a bit hesitant to propose such a pathway, because we would essentially work on two message formats in parallel. However, it could possibly be the best way to handle the usecases we are aiming for. In general, we could also put the Avro implementation in the backlog, focus on something more simple now, get it going/running rather quickly, and look at the more sophisticated options with Avro in a future version. Possibly we could in the first version already allow Avro, but not specify it completely, although I am not sure whether that would be wise. |
I fear you'll have to unpack that a little more. I must be missing something here. Note: I see this as primarily about if we want to require schemas or not. Forget about Avro or JSON for the moment, how we achieve propagating this information this is an implementation (well, specification) detail. Do I understand you correctly that to get a setup running with pyleco, users of a laser system will have to
Mind that the people doing step 2 and step 3 will probably not be the same, as one of the main intentions behind our Python packages is to avoid people needlessly redoing work, because they reuse the work of others who came before:
I guess 80% of pymeasure's value proposition is that user do not have to look that up in the manual, as it's a tab-completion away. |
So, let's go through this:
I'm not sure how much we can stretch the LEGO analogy, but for me the bricks are the Components (Actor, ...), and they fit together, and you can assemble something from them, without consulting device manuals to see if that new brick you found has the thing you need. |
Now, there is of course a wrinkle here, in that Python is not statically typed, so sometimes it will be hard to find out the type of values to exchange with some Driver interface (boolean, int, float,...), so it will be hard to fully populate widgets without some additional information. |
In conclusion, I guess we could allow an Actor to not include schemata for the instrument driver packages (or single-model code) whose API it interfaces, but I would consider that to offer "degraded experience" in LECO. So, optional/MAY if you prefer, but IMO we should plan/design the base case to include schema exchange. If we want to use something other than Avro to achieve this, that's fine, soo. I suspect we (and others who might code LECO implementations) will have to implement much of the machinery that would come with Avro ourselves. Ultimately it will be more and duplicated work, but maybe it makes sense because we need only a very small subset of Avro's capability. |
Where is the difference between looking at the pymeasure API documentation or looking at the schema returned by an actor? I see the advantages of a schema (I was never against a command returning a schema), such that I am for "an Actor SHOULD provide a schema of its capabilities". For the data protocol, Schemata might be more difficult, but that is another story. |
The schema can be processed/consumed by a program, i.e. the Director code, Actor code. Also, the schema is consistent across all instrument libraries, pymeasure, instrumentkit, etc
Yes, maybe more (type information), and it's homogeneous across interface instrument deiver libraries (same point as above). |
I might be a bit old-fashioned, but I currently do not use an IDE with tab-completion for python, and for pymeasure I actually typically look at the source code, or at least at the instruments docs. Even so, for completions I need already some idea of what the instruments contributors would have called something, and though it helps not to make typing mistakes, I do not magically get exactly what I want/need in a particular instance. If I missed out on existing magic like this hitherto, and you can point me to it, I would be pleased. I guess I agree that this discussion is fundamentally about how much and what should be automatic (for example autogenerated GUIs for Directors would rely on Actors providing this information). However, that also touches the scope we want to go to, and amount of work we are prepared to invest, the size this protocol would grow to have, and through that the entry-threshold to use it. |
I am definitely not assuming an interactive environment (well, depends on your definition of "interactive"), and the information does not have to be present in the Director object (that might be convenient though, and would be the case for Avro as that is part of the handshake), but the Director can get that information from a connected Actor when required.
Excellent example! If your Director consumes a sequence definition, and if your Director has an idea of the capabilities of the Actor (by getting a capability description via a schema), the Director can check your sequence definition against that schema when you load it, and will be able to determine that on line 135 you mis-spelled Mixing languages is something we want to enable by communicating via zmq. How would you look up the API definition of an Actor that is implemented in C++, and for which you might only have a compiled shared library? In any case, maybe a misconception: The schema thing is not necessarily/primarily for you (the user), but for the Components of the protocol, to be able to automatically use information for the good of the user (check sequence definitions, reject invalid inputs, autocreate GUIs,...). |
Anyway, it looks to me like you are not convinced of the benefits of mandating/baselining a schema/capability description for Actors. |
Oh nice: I did not know, that you could use the question mark in that way |
It turns out I was looking quite exactly for what you showed, with which I was not quite familiar. Sadly I tend not to be 100% up to date with nice tools, and it seems I quite totally missed out on IPython. I should look into it. What is tied to that however, is that at least in the labs I am working in, the typical person who would work on something like that is even less knowledgeable than me (in python), and their supervisors increasingly so. What most people know is "this is the code in this script here, and you can execute that with writing 'python script.py' in the terminal", and not necessarily much more in regard to such convenience tools like IPython REPL. Still, the example you showed is what I call interactive, but if we can get Directors to be used as simple as that, and the Actors passing all the necessary information, it could be workable to query device capabilities like that with having a live Director session open to guide the development of the static script. However, again, spinning up a Director involves having all the configuration at hand, all of which is one more thing to do. Finding out about these possibilities is again one more thing to do, and not always the most probable outcome.
That is so far the most convincing argument for me - indeed, having a way of requesting the capabilities in a more or less straightforward manner here would be great.
That too is a good example of how it could be very nice. Not sure it would come out like this though |
Well, my approach starts a little differently:
At this point, I already studied at the manuals and the API definition - it will be somewhat familiar to me.
Yes, well, I agree, that sounds quite nice. However, in my parallel universe, I have money to hire a scientific software engineer who will deal with that in their way. I am not sure whether this is the same around the globe, maybe we are here an outlier (I would hope so, but I am not sure) - it would be nice to have more data on who and how this would be used, to tailor it to the specific scope, need, and probability of it being used. |
I am not sure this is so much the case, I am just trying to wrap my head around it, and can only extrapolate from my own experiences, until somebody takes the time to bring in theirs, or explain a bit more, like you did, which I am quite happy about! I am looking forward to content of #45! Also, I have to be honest, when I argue along the lines that if we ask people to write Avro-compliant Actor behavior they will run away screaming, I do feel like running away from Avro myself. In fact, I have been quite actively running away from it, to the effect of never having passed the first few lines of the specification document. I am repeating myself, I should take a deeper look into it I guess.... |
If you use two question marks, you get more info, mainly the source code. 😀 |
That is also my experience: I know my devices, as I have to study its capabilites before I use it anyways. I'm managing the experiment and have a few students doing there Bachelor or Masters thesis (so a high rotation of short term colaborators). Therefore, I mainly maintain the code for the instrument interaction myself, as I want to use it in the long run.
I looked at the Avro specification, but am unsure yet, how it will work out in the end. I want to try it out, but finishing the Transport Layer definition (and my dayjob) is my priority. I have a working version of an Actor, small Director and Coordinator (according to the current PR state), so they could serve to try Avro out. As a message layer (for the control messages) I use json. |
I'm pretty sure it's approximately the same everywhere -- what you are describing is what people call "programming in academia". Interfacing between this world and "proper" software engineering is the job of "research software engineers". Fun fact, this turned out to be part of my tasks in the previous job (Python "guru"--not my word, got called that-- for 15 experimentalists), as well as in the current one (outside academia now, still in R&D). |
Ok, I suspected as much....then our question might be whom we want to "target" with leco and pyleco: research software engineers, or BSc/MSc/PhD students? Can we do both - something simple for students, which can be extended to sth nice by engineers? Or at least once they become research software engineers? But, once something works somehow, will the engineers really go and make it nice, or will they rather be busy patching up whatever does not work for somebody who cannot fix it themselves? Sadly we do not have a research software engineer in my group (as dedicated position, although it might be half of my night-job besides doing a PhD), nor at my institute (although perhaps in the whole institution somebody does have the money to pay for one), so I cannot judge on the typical scenarios that unfold. I understand that as a research software engineer, one might not be the one designing the experiment, so it might be the first time somebody looks in the manual, when they actually already need to develop a Driver. |
Well, ideally yes. Who is pymeasure "for", in your opinion? The nice thing about coding is that you can hide complexity ("abstract it away") from others as needed!
Btw, this is also why I don't fully get your reticence about Avro w.r.t inexperienced students -- ideally, the students use pyleco, and have no contact with Avro at all (maybe aside from writing a small dictionary to help define Actor Parameter type info). |
A short introduction into Avro: https://towardsdatascience.com/getting-started-with-apache-avro-and-python-59239ae7f5e |
I took a look again at json-schema and I like it, except for the fact, that method calls are missing. open-rpc is an interface description for JSON-RPC, so similar to json-schema, but for JSON-RPC, which in turn is a standard for encoding procedure calls. I propose to
A Matthew Burkard has Python packages to send JSON-RPC requests (client side) and to handle them (server side): jsonrpc2-objects, jsonrpc2-pyclient, openrpc |
Thanks for these links! I'll try to dig into them. |
I found protocol buffers. Maybe they can serve as message content: |
I guess there are |
btw. json-rpc already includes a "conversation_id", which would be redundant with the one in the content header frame. |
To make the decision more simple, I propose the following setup:
Regarding properties, I have two ideas:
My favourite version is version 2.
The only question remains, how we call our getters and setters:
I'm open to allow also other serialization schemes (with a marker in the content type header, see #53 ), but I like the idea to have a human readable, simple scheme for the network itself (sign ins etc.) and as a default implementation. Do you see any problem with json-rpc, @bilderbuchi , @bklebel ? Btw. the specification of json-rpc is a really quick read and contains some examples. |
As a consequence, the question about the message types (#29 ) turns into a question, which method calls each Component should offer. For example a Coordinator should allow sign ins/sign outs and an Actor should allow to get/set some properties.
As you can do more than one method call in a single message (they can be handled in parallel and in any order!), these individual "id"s are complementary to our message conversation_id. |
One reason to implement "proper leco protocol" messages (sign in, etc.) also as remote call of methods: Users of leco won't need ever to care about message formatting. They only care about calling procedures remotely. Leco procedures will do the transition from call to message and from message to call. Even writing a new actor will be easy, as users have to offer certain methods (getting / setting properties, locking resources...), but don't have to react directly to messages. |
If I understand correctly (and to summarise back),
This sounds good to me so far. If an instrument library (Driver) used with LECO uses properties or not for its Parameters is abstracted away behind the Actor, so it boils down how we map our messages/message types to methods.
I'd lean towards the latter. We have to take care that in the list of possible methods, we don't have a collision between our LECO messages and any Actors Parameter/Action names, so prefixing Parameter-stuff with On one GET(parametername) method vs generating many |
I realised I'm not sure about this. Do our messages always map cleanly to method calls on the receiving component? For one example, let's say a Component sends a I can definitely see the benefit of using some schematised approach to describe our actors' capabilities, and to encode our messages. Maybe the RPC thing is not that applicable to all messages? Maybe only in some way for the interaction with an Actor, but even then I'm not sure of the benefit of RPC vs. having a known set of Actor-internal methods/functions to react to messages? |
Thanks for finding that difficulty. Yes, doing sign in / out via rpc would need some extra code outside pure rpc in the Coordinator, which needs that extra information not contained in the rpc call itself. So there we need some special messages. |
In my current actor implementation, each parameter or method call is directed towards the instrument, unless you specify explicitly to talk to the actor instead of the instrument. We could use "actor.xy" for the "xy" method of the actor instead of the instrument method. That way we do not have to prefix a call with "call_". |
In the end, calling these "Actor internal methods" is already rpc: if I send this message, that method will be called. With a known rpc protocol, the translation is clearly defined. We can describe it ourselves and thus have more work and less compatibility (due to a proprietary format). |
I went through the json-rpc specification, and it seems good to me for our purpose. Also, the OpenRPC implementation (at least the given examples) look nice, simple and straightforward. A small thing which I observed: if we use OpenRPC from Matthew Burkard, we might be restricted by the way json-rpc methods are registered, through the function/method name. It might not be practical to use a period, since we cannot have that in a function name in python after all. I do not have much more to say right now, about prefixes and separations and so on, I will come back to it in the evening. |
I looked at the code, you can give the "@method" decorator a name, even with periods, so that's not a problem. |
I looked through #29 and all message types, except for sign-in /sign-out (and their coordinator variants) could be implemented with RPC calls. The question is, how the responses to these messages look like. For simplicity, I'd form the response according to json-rpc. |
For all components, we would require a single method:
For all components we could allow (they can have it, but do not have to have it):
Actors can offer some more methods of a standardized set:
|
A quote from the current protocol definition (LECO):
Isn't that an argument for RPC? |
I thought about our desired "locking mechanism": For example: you call "sign_in()" (without parameter) of the coordinator or some "move_motor(5)" of an actor. In both cases the recipient (Coordinator or Actor) accesses the internally stored original message to verify, that the sender of the message may sign_in / move that motor. So for these features, we need an instance variable with the message (or a global variable etc., depending on the language and implementation). |
Update: I implemented the RPC approach (even for sign-in etc.) and it works as expected, so we can go totally with RPC. |
Just chiming in to say the RPC paradigm fits well with an experiment control and recording setup. json-rpc seems like a good first choice for proving out how the different pieces of the framework will communicate and work together. I have reservations of a vanilla json protocol from a speed point of view with the additional overhead parsing, for example when recording data points. From an end user's perspective, if the protocol can't keep up with their measurements, they simply won't use it. This is a future problem but "fast yet reliable" is one of the main goals of pyleco, worth keeping in mind. One of the benefits of Avro is the data packing for speed, besides cross platform compatibility, however it seems to be more opinionated about the core structure of the schema. I think this would be a fork in the road for Avro. A possible next step, in the future, could be gRPC, which doesn't seem to have an opinionated structure for the schema and so it would be compatible with json-rpc. In reality this is just the first choice, and as such it can be iterated on, but wanted to bring up gRPC as an option for a fast implementation of a json-rpc schema. |
One idea was, that the header indicates which type of serialization the message contains. That allows more than one technique. I'm not sure, how much faster a binary protocol is, which requires some serialization as well, in comparison to plain json. The message size might be smaller, but the serialization/deserialization? |
The only real way would be to run a test of pyleco with binary vs plain json, but I'd be surprised if the binary protocol isn't 2-5x faster. |
Part out of #20
How do we encode the content of messages?
We already have a header frame, which could indicate different encoding styles. For example we could allow binary (for file transfer), json (for easy implementation), avro (for RPC via Apache Avro RPC), etc.
Some ideas (from the beginning of #20):
@bilderbuchi: I read a bit on the subway today, it really looks like a good option; this would also take some of the handshaking, capability listing, reply tracking, message verification (with schema), RPC burden from us. zmq would probably mostly be the transport layer (so one frame per avro message; plus maybe the topic frame). JSON or binary option. Implementations for several languages.
Specification here.
Yaq notes on why/how they use avro: https://yeps.yaq.fyi/107/
The text was updated successfully, but these errors were encountered: