Skip to content

Python module (GPLv3+) to provide scpi functionality to an instrument

License

Notifications You must be signed in to change notification settings

GMrZhang/scpi

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

scpi

Python module to provide scpi functionality to an instrument under a GPLv3+ license.

license GPLv3+ 4 - Beta Codeship badge

about about

This project has started as a toy-project to test the Skippy Tango device server. But it has evolved to become a python library to provide scpi commands tree functionality in the instrument side.

SCPI library is based on these standards, but does NOT (fully) complain them yet.

Basic features

  • Command three definition with readonly and read/write attributes.
  • Special commands (hint, they start by '*')
  • Concatenation of commands (hint ';' separator).
  • Listen network connections (only local/loopback or open to an interface).
  • Channels keywords in a command (even more than one channel separation).
  • Array-like answers (hint '#NMMMMMMMMMxxxxx...\n')

Features requested (Wish/ToDo List)

  • Data formats for the arrays ('ASCII' but also binary floats with 1, 2, 4 or 8 Byte codification).
  • List the minimum special commands to be setup for an instrument (hint '*IDN?').
  • Support for IPv6.
  • Enumerate type to the command setters (hint, allowedArgins).
  • Listen more channels than network (TBD what other channels can be).
  • "autodoc" using the scpi tree.
  • Read commands with parameters after the '?' separator.
  • Write commands without parameters (no need a ' ' separator).
  • Lock write access: for one of the clients or internally by the server. Side by the "SYST:LOCK" from the standard, that is included also, this is a "SYST:WLOCK".
    • Even there is an access lock, should be allowed the command "SYST:LOCK:OWNEr?"
    • Tests pending
  • Avoid the internal Logger and use the python logging.
  • Compatibility with python3 without losing python2.
  • Compile with cython to optimise the execution.

Other ideas to study

  • Event subscription. Unknown if scpi has something about this in the specs.
  • Use SSL.
  • Authentication and ACLs.

Installation

It has been thought to use setuptools (and someday cython will be introduced to have a compiled option to increase performance with long sets of commands).

$ python setup.py build
$ python setup.py install --prefix $MYUSR

The install has been set with a prefix to highlight the current development stage and avoid to use in production unless you change it.

Usage

Different aproaches has been prepared to have instrument side support. The most simple shall be to simply build a scpi object in your instrument:

import scpi
scpiObj = scpi.scpi()

With no paramenter configuration, the object assumes the communication will be made by network and only listen the localhost (in ipv4 and ipv6). See the help for further information.

The object created doesn't have any command configured, except the ones build internally for its behaviour configuration (That is the 'DataFormat' and soon the Write Lock functionality).

There are some different ways to prepare this object to respond to specific requests. They can be build before and passed to the constructor, or use a command to add:

currentObj = AttrTest()
scpiObj.addCommand('source:current:upper',
                   readcb=currentObj.upperLimit,
                   writecb=currentObj.upperLimit)

The AttrTest() object can be found in the commands.py file and it's used in the test approach. What the previous code generates are:

  • 'source' to the root in the scpi command tree,
  • 'current' as an intermediate node, and
  • 'upper' a leaf that will be readable and writable.
SOURce:
        CURRent:
                UPPEr

With this sample code, the object shall be listening on the (loopback) network and sending to the port 5025 the string 'SOUR:CURR:UPPE?', we will receive back an string with the representation of the execution of 'currentObj.upperLimit()'.

Special commands

There is a set of minimum commands that shall be implemented in any device that like to be scpi compiant. Those commands are tagged starting with '*' symbol.

1) Identify yourself

The most important of them is the identification: '*IDN?'. As it finishes with a '?' symbol, it means a request to the instrument. The answer is a string with 4 elements coma separated:

  • Manufacturer: identical for all the instruments of a single company
  • Instrument: Common for all the instrument in the same class, but It shall never contain the word 'MODEL'.
  • Serial Number: specific for the responding instrument to the request.
  • Version (and revision) of the software embedded in the instrument.

To build this command, one should have an function that returns the string that will be sent back. For example, in the code there is a class InstrumentIdentification() where the 4 fields can be set and there is one method that returns the string. Then, to a scpi object one can add it:

scpiObj.addSpecialCommand('IDN',identity.idn)

With this one have the most very basic functional scpi listener. And mandatory for the Skippy Tango device server, because this identification is used to distinguish between the supported instruments to load the corresponding set of commands.

2) Other special commands

This section can be extended but now it is only a list of the excepted mandatory commands that the SCPI shall have (even many commercial products didn't do).

  • '*CLS': Clear Status Command
  • '*ESE[?]': Event Status Enable
  • '*ESR?': Event Status Register
  • '*OPC[?]': Operation Complete
  • '*RST': Reset
  • '*SRE[?]': Service Request Enable
  • '*STB?': Status Byte
  • '*TST?': Self-test
  • '*WAI': Wait-to-continue

Channels in the instrument

It has been shown how to setup a minimal tree of commands, but often this kind of instruments have components that are channels. Like an oscilloscope, an electrometer, or any other that one can develope.

Those commands have the peculiarity that their key work ends with a number (specifically 2 decimal digit number string starting with a 0 if need be), at the same time their left part of the keyword has this variable lenght feature.

There has been implemented one way to add this channel feature, and only one channel element can be set in the branch of the tree.

nChannels = 8
chCmd = 'channel'
chObj = scpiObj.addChannel(chCmd, nChannels, scpiObj._commandTree)
chCurrentObj = ChannelTest(nChannels)
chVoltageObj = ChannelTest(nChannels)
for (subcomponent, subCmdObj) in [('current', chCurrentObj),
                                  ('voltage', chVoltageObj)]:
    subcomponentObj = scpiObj.addComponent(subcomponent, chObj)
    for (attrName, attrFunc) in [('upper', 'upperLimit'),
                                 ('lower', 'lowerLimit'),
                                 ('value', 'readTest')]:
        if hasattr(subCmdObj, attrFunc):
            cbFunc = getattr(subCmdObj, attrFunc)
            if attrName == 'value':
                default = True
            else:
                default = False
            attrObj = scpiObj.addAttribute(attrName, subcomponentObj,
                                           cbFunc, default=default)

With this iterative way, the scpi tree will have a component near the root that will accept channel differentiation in the readings and writtings. The tree representation will be like:

CHANnelNN:
        CURRent: (default 'value') 
                UPPEr
                LOWEr
                VALUe
        VOLTage: (default 'value') 
                UPPEr
                LOWEr
                VALUe

where the NN following the 'channel' key will be a string number between '01' and '08' (supporting up to '99' if it is setup this way).

Then the command 'CHAN01:CURR:VALU?' will call a different read method than a command 'CHAN05:CURR:VALU?'.

About

Python module (GPLv3+) to provide scpi functionality to an instrument

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Python 100.0%