Package: generic-scpi-driver
A generic driver generator for devices controlled via (virtual) COM ports using SCPI commands.
Creates a python class for controlling your device.This class is compatible with the ARTIQ experimental control system and,
if desired, a network ARTIQ controller is also generated.InstallationInstall the package and its dependancies with:pip install generic-scpi-driverIf you’d like to use the ARTIQ network controller generation, instead install with:pip install generic-scpi-driver[artiq]UsageBasicsTo make a driver, simply inherit from the GenericDriver class. To define commands, call_register_queryon the class (not on objects of the class). For example:fromgeneric_scpi_driverimportGenericDriverclassSimpleDriver(GenericDriver):'''A driver for my simple SCPI device'''SimpleDriver._register_query("get_identity","*IDN")This creates a class calledSimpleDriverwhich has a constuctor__init__, aclose()method
and a new method calledget_identity()which takes no parameters and returns a string. You could open a connection to your device like this:dev=SimpleDriver(id="COM10",baud_rate=57600)# This sends the command "*IDN\n" to the device and returns the responseidentity=dev.get_identity()ParametersFor more complex commands, you can specify parameters for the command:SimpleDriver._register_query("set_voltage","VOLT",args=[GenericDriver.Arg(name="channel"),GenericDriver.Arg(name="voltage"),])This would allow you to call:# Using positional argumentsdev.set_voltage(0,5.4)# ...or keyword argumentsdev.set_voltage(channel=0,voltage=5.4)Parameters can be validated by passing a custom function (which may accept any
single parameter and must return a string to be sent to the device, or throw an error):defcheck_voltage_in_limits(v):voltage=float(v)ifvoltage>10:raiseValueError("Voltage too high")returnstr(voltage)SimpleDriver._register_query("set_voltage","VOLT",args=[GenericDriver.Arg(name="channel",validator=lambda:str(int(x))),GenericDriver.Arg(name="voltage",validator=check_voltage_in_limits,default=0.0,])Return valuesReturn values are, by default, the string returned by the SCPI device in response to your command.
If you’d prefer to process these, you can pass aresponse_parserfunction:SimpleDriver._register_query("count_foobars","COUN",response_parser=int,)SimpleDriver._register_query("list_foobars","LIST",response_parser=lambdax:x.split(","),)If your device doesn’t give any response at all, you can setresponse_parser=Noneand the driver won’t attempt to listen for a respose.Error checkingYou can also add error checking to your commands. Pass a function asresponse_validatorand it will be called with the output from the device
(not the parsed output ofresponse_parser) as its input. Theresponse_validator’s return value will be ignored: it’s only job is to raise
an exception if needed. E.g.defcheck_for_error(s):if"error"ins.lower():raiseRuntimeError("Error returned by device:{}".format(s))SimpleDriver._register_query("do_something","DOOO",response_validator=check_for_error,)Asyncronous operationBy default, all methods are syncronous. If you’d prefer async operation, passcoroutine=Trueto_register_query. This creates a new thread for the serial call and returns anasynciocoroutine. Note that you have to call these using an async loop which is a whole topic of python
programming. This is particularly useful for ARTIQ drivers, since ARTIQ handles coroutines
automatically.Custom methodsThe method generation is intended to be quite flexible, but if you really need custom logic there’s
nothing to stop you writing your own methods. You can useself.instrto access thepyvisa.Resourcefor your device. Use the wrapperswith_handlerto cause the driver to issue a
VISA.flush()if an error occurs andwith_lockto ensure that only one method access the device
at a time (only relevant in multi-threaded applications).fromgeneric_scpi_driverimportGenericDriver,with_lock,with_handlerclassSimpleDriver(GenericDriver):'''A driver for my simple SCPI device'''@with_handler@with_lockdefdo_complex_thing(self):'''Do something complex'''response=self.instr.query("COMP 1 2 3")returnint(response)+5Startup checkingIt can be useful to check on startup if communicatio with a device has been
established successfully. To do this, define a method in the class calledcheck_connection. Return value is ignored, but this method will be called
when the object is constucted and has the chance to raise an exception. Example:fromgeneric_scpi_driverimportGenericDriver,with_lock,with_handlerclassSimpleDriver(GenericDriver):'''A driver for my simple SCPI device'''defcheck_connection(self):idn=self.get_identity()ifidn!="My device":raiseRuntimeError(f"Bad device identity: got '{idn}'")# Note that it's fine to define functions later which get used in methods# defined previouslySimpleDriver._register_query("get_identity","*IDN")Simulation modeThe constuctor accepts a keyword parametersimulation=Trueto return a simulation device, for running
offline unit tests. This won’t work unless you also register a simulator device with a methodquerywhich
takes a string and returns a string. For example:classSimulator:defquery(s):ifs=="*IDN":return"Simulator device"else:return"ERROR"classSimpleDriver(GenericDriver):passSimpleDriver._register_simulator(Simulator)SimpleDriver._register_query("get_identity","*IDN")dev=SimpleDriver(id="fake",simulation=True)dev.get_identity()# returns "Simulator device"ARTIQ ControllersTo get a network controller for use by the ARTIQ controller manager, just make a python module like:fromgeneric_scpi_driverimportget_controller_funcfrom.my_driverimportSimpleDriver# Makes a controller called "SimpleDriver" which listens to port 3300 by defaultmain=get_controller_func("SimpleDriver",3300,SimpleDriver)if__name__=="__main__":main()Register thismainfunction in yoursetup.pylike so:setup(...entry_points={"console_scripts":["artiq_simple_device=my_driver_package.my_driver_controller:main",]},)After installing your package usingpip install -e .as normal, you should be able to callartiq_simple_deviceon the command line to launch a controller for your device.DevelopmentFor developing the package, you’ll need a few more packages. Install with:pip install -e .[dev,artiq]Authorsgeneric_scpi_driverwas written byCharles Baynham.