AllenNeuralDynamics / python-newscale

Python library for controlling the micromanipulator systems from New Scale Technologies
MIT License
1 stars 0 forks source link

USB Control #1

Closed Poofjunior closed 2 years ago

Poofjunior commented 2 years ago

Opening PR mainly so you can see progress. Note: useable draft here, but PoE Interface is untested and many multistage commands are in the process of being implemented.

Let's not merge yet until we test the PoE stuff unless we are ok with just popping in a useable draft into main. AFAIK, this is mergeable if we just need USB working.

Quickstart

from newscale.stages import USBXYZStage

stage = USBXYZStage('/dev/ttyUSB0')
stage.move_absolute(x=5,y=10, z=15)  # movements are in mm (floats)

The above code was tested and should work. Try it in the examples folder.

The Generic Interface Problem

I solved this with Polymorphism.

An M3LinearSmartStage object communicates through a generic Interface object to communicate with the hardware.

Interface is a generic base class for the specific kind of interface (Serial, PoE, custom) that the stage is using to communicate. Under the hood these child classes basically do slightly different things when they implement the self.send and self.read functions.

The nice thing about this is that we can stack these stages in real life--and they could be using totally different interfaces!

from newscale.stages import M3LinearSmartStage, MultiStage
from newscale.interfaces import SerialInterface, PoEInterface

ser_interface = SerialInterface(port='/dev/ttyUSB0')
x_stage = M3LinearSmartStage(ser_interface, "01")
y_stage = M3LinearSmartStage(ser_interface, "02")
z_stage = M3LinearSmartStage(ser_interface, "03")

poe_interface = PoEInterface(address="192.16.8.1.10")
u_stage = M3LinearSmartStage(poe_interface, "01")

multistage = MultiStage(x=x_stage, y=y_stage, z=z_stage, u=u_stage)
multistage.move_absolute(x=2.5, y=5, z=7.5, u=10)  # Heck Yeah!

I can see the above being useful if we add an extra degree of freedom to a 3-axis stage to do something like position 2 lick spouts and position the spacing between them.

Conveniences

I made some "convenience" functions so you don't have to mess with all this generic interface stuff if you just have a 3-axis stage and an interface and want to get up and running quickly. For that, use USBXYZInterface or PoEXYZStage

Extensions

If we want to spin a custom PCB that talks to "N" stages, all we would need to do is make another child class that inherits from Interface and implement the read and send functions.

Debugging

This library uses logging under the hood. Each class has an instance-level logger from which you can pull debug messages. To conform with python library logging standards, there are no handlers, so you must add them yourself. A quick-and-dirty way to print logs for debugging is to add this to the top of your script where your import statements live:

import logging
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

# The rest of your code
chronopoulos commented 2 years ago

please add "bitstream" module to list of requirements. and let me know when the poe interface is ready to test, preferable with a new script in examples/

Poofjunior commented 2 years ago

please add "bitstream" module to list of requirements. and let me know when the poe interface is ready to test, preferable with a new script in examples/

Done! (actually added bitstring.)

chronopoulos commented 2 years ago

i just added your datasheets to our new Documentation page on aind.tech:

https://aind.tech/docs

the URL's should be static, feel free to link to these in the README, then remove the PDFs from the repo please.

Poofjunior commented 2 years ago

i just added your datasheets to our new Documentation page on aind.tech:

https://aind.tech/docs

the URL's should be static, feel free to link to these in the README, then remove the PDFs from the repo please.

Woot--thanks tremendously! I'm just worried about having vendor links change on us. These are now linked in the readme from both the vendor and us.

Poofjunior commented 2 years ago

Thanks for digging through the details. I think this is ready to go when you get back.

I would also totally encourage you to take the docs for a test drive. I littered them with a ton of examples, so folks don't have to read too much unless they want to. (Bonus: I can now forget all those weird Newscale nuances I had to learn to get this stuff working.) Sphinx is now also a project dependency, so you can pip install -e . again and then head over to the docs folder and type make html to get the docs. Long term, it would be great to pop them online.

chronopoulos commented 2 years ago

Thanks for digging through the details. I think this is ready to go when you get back.

I would also totally encourage you to take the docs for a test drive. I littered them with a ton of examples, so folks don't have to read too much unless they want to. (Bonus: I can now forget all those weird Newscale nuances I had to learn to get this stuff working.) Sphinx is now also a project dependency, so you can pip install -e . again and then head over to the docs folder and type make html to get the docs. Long term, it would be great to pop them online.

i tried it, it works and it looks good! i did have to manually "pip install Sphinx" because it was listed as an optional dependency (which it should be). maybe put something in the docs/readme.md explaining how to install optional deps (i actually don't know which command it is).

also, this PR isn't about documentation so i don't see any reason to hold things up on account of that, but: to me, the most useful documentation of a class or function is if i can run help(functionName) in the interpreter and get the docstring which lists example usage. this is usually all i need and it's a lot quicker than thinking about optional arguments and keywords, and all the polymorphism possibilities. so if you're going to invest more time in documenting this library, i would start with that.