Open glopesdev opened 3 months ago
This was at least partially addressed in #11 . The interface is named SerialCommutator
since we have tentative plans for a USB/HID version. I don't know if it gets at everything in this proposal and if things are missing feel free to suggest so.
@jonnew I was previously using a vanilla serial operator instead of this custom one. I see you have some nice validation logic in this bespoke operator that I would like to use. However, we were also using few other commands from the provided API (https://open-ephys.github.io/commutator-docs/coax-commutator/user-guide/remote-control.html) and I am not sure how to access those via this operator since the serial object is not exposed.
I can think of a few solutions:
Bonsai.Harp
does. Have a single operator that receives "CommutatorMessages" (these could potentially have different types that correspond to different API calls) and outputs the async output from the commutator (these could either by just json strings or even the same "CommutatorMessages"). I understand. The operators in here are aimed at ease of use. I guess my suggestion would be just to use QuaternionToTwist
in your own workflow with mechanisms for sending the other commands.
Not to say that adding e.g. {print:}
to this interface would not be useful, but I couldn't think of a way to do it in a way that maintained the simplicity looking for. Suggestions are welcome.
We should refactor the
TurnMotor
operator to be either aCommutatorDevice
or simplyCommutator
and expose a more general interface to the entire device functionality.Detailed reasons below:
While it is possible to format the JSON string with the
Format
operator, this locks us to a specific communication protocol. If we ever need to upgrade or extend the details of the JSON API in any way (or swap the protocol to binary, etc), we would not be able to easily upgrade old workflows. With the operator abstracting away the communication internals, we can get away with just upgrading the package, even for hand-crafted workflows.I don't currently see any particular advantage of exposing the internal JSON strings, especially given the protocol does not comply to an existing standard and there is little cost or complexity added in doing this abstraction.
Input validation is definitely an important consideration. Similar to the above, having the packaged node would make it much easier to upgrade legacy workflows to benefit from improved validation. For example, in the future we could extend this operator to drop or throw if inputs are sent too fast, etc.
This is where having a general
CommutatorDevice
node would really make this interface shine and again allow us to extend the device or API with extra functionality by simply updating the package.The easiest way to do this would have been for the commutator to use one of the standard control protocols already integrated into bonsai such as Harp or even ONI itself. This would have allowed us to leverage a bunch of existing infrastructure for configuration, control, logging and synchronization with other devices. In fact, if it was Harp we could even have generated the entire Bonsai interface automatically.
All the questions you asked about how to dynamically parameterize device configuration and having multiple control streams have already been solved for all Harp devices for example. I don't know what microcontroller the commutator uses, but there is now an existing Harp core for the RP2040 (https://github.com/AllenNeuralDynamics/harp.core.rp2040) which would have been perfect for this. A simple version could also be designed on top of other microcontroller architectures, including software forms of clock synchronization which have been implemented for other devices.
Assuming this is not possible for the current hardware / firmware combination:
This is definitely a possibility and I think we could make this pattern work by using a
BehaviorSubject
in the backend. Whether this is appropriate depends on how big is the full API, and on whether a simple LED toggle is really the only extra functionality. If there are other configuration or control commands which would be useful to expose as streams (now or in the future) this might quickly become too complicated to evolve.I'm not sure I understood this suggestion correctly, but I'm interpreting this to be the same pattern we use for the Harp
Device
node, which is basically a node which takes a sequence of commands and formats / pipes them to the serial port.I think this would definitely be flexible enough for all present and future needs. Basically in this pattern we need just two concepts:
TurnMotor
,EnableLed
, etc), which format inputs into the corresponding serializable JSON strings (or other protocol) to send to the serial port.CommutatorDevice
node which establishes a connection to the commutator and receives formatted strings.CommutatorDevice
node can declare a "strongly-typed" input. This would immediately disallow accidentally sending random or malformed strings to the device.I think the last two bullet points would on their own also justify having a
CommutatorDevice
node.Even though
SerialWriteLine
may suffice to send arbitrary strings to the serial port, this operator does not make any assumptions on port configuration properties such as baud rate, etc. Even if the commutator uses the default settings of the bonsai node for now, there is no guarantee this won't change in the future, either on the side of the commutator, or the side of bonsai, so relying on this alignment to be stable in the future is dangerous IMO. Having a common device node would ensure the alignment is enforced and the port configured correctly.The last point is optional but easy to implement and important for validation to ensure the commutator does not go into an undefined state by accidentally receiving gibberish data from upstream.
This could actually be done very easily by linking both properties via the same
ExternalizedMapping
. However, I do agree that it is still hacky because of all the issues discussed above.Seeing the manual control of the motor with keys and LED control functionality added to the
AutoCommutator
workflow actually makes me feel that what we should really remove is theAutoCommutator
node itself.It feels there are currently two things being conflated in this package:
The commutator device is actually a complex device, so we need all of its functionality to be exposed in a structured way. This is so we can freely compose what we want to do with it in arbitrary ways, e.g. reset commutator on trials, blink LED to signal something, apply a slower drift correction algorithm, untwist commutator with computer vision tracking, etc the list will just keep growing.
There is a need for a "plug-and-play" commutator solution that users can drop into their acquisition workflows to get started quickly doing some recordings.
I feel that 2. could just as easily be provided simply as an example in the commutator docs, with no need for an embedded workflow. This recommended example usage workflow could be updated regularly as we improve the pattern without requiring updates to the commutator package.
That said, I don't disagree we could also make it into an embedded workflow in the package, although in that case I feel it should be fairly minimalistic, e.g. without assuming specific keys or options, since the more we assume the higher is the risk that it will not work for someone's specific workflow.
Originally posted by @glopesdev in https://github.com/open-ephys/bonsai-commutator/issues/2#issuecomment-2282905486