add pty-gpib-emulator from github

This commit is contained in:
2020-09-09 05:17:31 +00:00
parent 63554da593
commit 3fe71a6f3b
22 changed files with 997 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
##
## **** Device Class ****
## by Aidan Macdonald (aidan.plenert.macdonald@gmail.com)
##
## Standard framework for the a Device that is attached
## to the PTY_Interface
##
class Device:
##
##%% No initialization
##
##%% handleCMD(pty_interface, command, arguments)
## A device attached to a PTY_Interface will handle commands
## passed to it from the PTY_Interface by a call made to
## this function.
##
## pty_interface will be a referance to the interface that called
## this function
##
## command will be a string with the text of the command read
## from the pty device
##
## arguments is an array of the arguments to said command read
## from the pty device
##
## The string returned by this function will be written to
## the pty device
##
def handleCMD(self, fpl, cmd, args=[]):
return ""

View File

@@ -0,0 +1,87 @@
##
## **** PTY_Interface Class ****
## by Aidan Macdonald (aidan.plenert.macdonald@gmail.com)
##
## This class generates a pty device that emulates the
## tty device created by GPIB controllers such as
## Prologix GPIB-USB Controller (prologix.biz)
##
## This class does NOT emulate the controller, but the
## controller must be attached to this PTY Interface
##
## I may switch between the terms tty device and pty device.
## They refer to the same thing.
##
import os
import Device, Prologix
class Interface:
##%% Initialization - Interface(default_device, default_address)
## accepts arguments default_device and default_address
## the default_device will be connected at the default_address
##
## If the Interface address (PTY_Interface.addr) is changed, the
## no longer have anything attached and will become functionless.
## UNLESS, the default device handles the address change and copies itself
## to the new address (Prologix_GPIB_USB does this, OR there is
## another device attached there.
##
## if no arguments are given at initialization, defaults are
## Device.Prologix_GPIB-USB at address 0
##
def __init__(self,
default_device=Prologix.Prologix_GPIB_USB(),
default_address=0 ):
self.m, self.s = os.openpty()
self.devices = {default_address:default_device}
self.addr = default_address
##%% printFilename()
## Prints the path to the PTY device generated (ie /dev/pts/2)
##
def printFilename(self):
name = os.ttyname(self.s)
print(name)
return name
##%% run(readLen)
## Runs the emulator which will read all statements written from the outside
## to the pty device at the location given by self.printFilename(), then
## runs what was read through self.handleInput(tty_in) and writes output
## back to the pty device to be read from the outside
##
def run(self, readLen=100):
self.running = True
while (self.running):
tty_in = os.read(self.m, readLen)
for tty_out in self.handleInput(tty_in):
os.write(self.m, tty_out)
##%% handleInput(tty_in)
## Recieves raw input that was read from the pty device, and
## breaks it up and send it to the device listed at the current
## address. That device must handle the command via dev.handleCMD(cmd, args)
##
def handleInput(self, tty_in):
tty_in = tty_in.strip()
if tty_in != "" and self.addr in self.devices:
dev = self.devices[self.addr]
for cmd in tty_in.strip().split(";"):
p = cmd.strip().split(" ")
yield dev.handleCMD(self, p[0], p[1:]) + "\n"
yield "" # Must be a null string to prevent infinite loop
##%% addDevice(device, address)
## Preferred way of connecting a device to the interface
## at a given address
##
def addDevice(self, dev, addr):
self.devices[addr] = dev

View File

@@ -0,0 +1,129 @@
##
## <<<< Powersupply Emulators >>>>
## by Aidan Macdonald (aidan.plenert.macdonald@gmail.com)
##
## Contains classes that emulate powersupplies by
## building off the Device framework
##
import Device, Prologix
## **** Power Supply class ****
## by Aidan Macdonald (aidan.plenert.macdonald@gmail.com)
##
## General model of a basic Power Supply
##
class PowerSupply(Prologix.Prologix_GPIB_USB):
##%% Initialization PowerSupply(volt, current)
## Creates a framework for a Powersupply, but only binds
## a reset call to "RST?". All other functions have been
## left unbound so that children can bind them to respective
## commands
##
## voltage and current default to 10
##
def __init__(self, volt=10, cur=10):
Prologix.Prologix_GPIB_USB.__init__(self)
self.addCMD("RST?", self.reset)
self.voltage = volt
self.current = cur
##%% reset(pty, args)
## returns that the device has been reset
##
def reset(self, fpl, args=[]):
return "Power Supply has been reset"
##%% version(pty, args)
## overrides the version function from superclass
## left unbound here, because it was bound in the
## superclass.
##
def version(self, fpl, args=[]):
return "Power Supply Version 1.GEN"
##%% on(pty, args)
## turns power supply on
##
def on(self, fpl, args=[]):
return self.version(fpl, args) + " in now ON"
##%% off(pty, args)
## turns power supply off
##
def off(self, fpl, args=[]):
return self.version(fpl, args) + " in now OFF"
##%% getVoltage(pty, args)
## returns voltage
##
def getVoltage(self, fpl, args=[]):
return self.voltage
##%% getCurrent(pty, args)
## returns current
##
def getCurrent(self, fpl, args=[]):
return self.current
##%% setVoltage(pty, args)
## sets voltage
##
def setVoltage(self, fpl, args=[]):
try:
self.voltage = float(args[0])
return ""
except:
return "ERROR"
##%% setCurrent(pty, args)
## sets current
##
def setCurrent(self, fpl, args=[]):
try:
self.current = float(args[0])
return ""
except:
return "ERROR"
##
## **** E3631A Power Supply Class ****
## by Aidan Macdonald (aidan.plenert.macdonald@gmail.com)
##
## Emulates the E3631A Power Supply
##
class E3631A(PowerSupply):
##%% Initialization - E3631A(voltage, current)
## see PowerSupply.PowerSupply documentation
##
## adds bindings for all of the PowerSupply functions
##
def __init__(self, volt=10, cur=10):
PowerSupply.__init__(self, volt, cur)
self.addCMD("OUTP", self.outp)
self.addCMD("MEAS:VOLT:DC", self.getVoltage)
self.addCMD("MEAS:CURR:DC", self.getCurrent)
self.addCMD("SET:VOLT:DC", self.setVoltage)
self.addCMD("SET:CURR:DC", self.setCurrent)
##%% version(pty, args)
## returns version
##
def version(self, fpl, args=[]):
return "E3631A Power Supply"
##%% outp(pty, args)
## function binding for the "OUTP" command
## Used to turn the device on and off
##
def outp(self, fpl, args=[]):
if args[0] == "ON":
return self.on(fpl, args)
elif args[0] == "OFF":
return self.off(fpl, args)
return ""

View File

@@ -0,0 +1,85 @@
##
## **** Prologix_GPIB_USB Class ****
##
## A Device that emulates the functionality of the
## Prologix GPIB-USB Controller (prologix.biz)
##
import Device, PTY_Interface
class Prologix_GPIB_USB(Device.Device):
##%% Initialization - Prologix_GPIB_USB()
## Does not take arguments, but is VERY important
## All classes that attempt to inherit this class,
## must call this initialization.
##
## This creates a dictionary containing all the commands
## to be run. If this dictionary is not created, this class
## does nothing
##
def __init__(self):
self.cmds = { "++ver":self.version, # "COMMAND":HANDLER
"++addr":self.addr,
"++kill":self.kill}
##%% handleCMD(pty_interface, command, arguments)
## See Device.handleCMD documentation
##
## In Prologix_GPIB_USB, this function makes use of the
## map made in initialization to find the command and
## its associated handler, then call the handler and pass in
## the pty_interface and the arguments
##
def handleCMD(self, fpl, cmd, args=[]):
out = Device.Device.handleCMD(self, fpl, cmd, args)
if out != "":
return out
elif cmd in self.cmds:
return self.cmds[cmd](fpl, args)
else:
return ""
##%% version(pty, args)
## Returns the device version
##
def version(self, fpl, args=[]):
return "Prologix Version 1.1 Simulator (Python)"
##%% addr(pty, args)
## If no arguments are given, returns the current address
## of the pty interface
##
## if args are given, then an integer is parsed out of
## the first argument and set as the address. This ensures
## that a device exists at the following address
##
## Any errors return "ERROR"
##
def addr(self, fpl, args=[]):
if args == []:
return str(fpl.addr)
else:
try:
fpl.addr = int(args[0])
if not fpl.addr in fpl.devices:
fpl.addDevice(PrologixAdapter(), fpl.addr)
return ""
except:
return "ERROR"
##%% kill(pty, args)
## Stops the pty interface from running
##
def kill(self, fpl, args=[]):
fpl.running = False
return ""
##%% addCMD(command, handler_function)
## Adds a command to the dictionary
## Handler function must be of the form
## func(pty_interface, arguments):
##
def addCMD(self, cmd, func):
self.cmds[cmd] = func

View File

@@ -0,0 +1,77 @@
##
## <<<< Source Measurement Unit Emulators >>>>
## by Aidan Macdonald (aidan.plenert.macdonald@gmail.com)
##
## Contains classes that emulate Source Measurement Units
##
import Device, Prologix
## **** Source Measurement Class ****
## by Aidan Macdonald (aidan.plenert.macdonald@gmail.com)
##
## General model of a basic Source Measurement Unit
##
class SourceMeasurement(Prologix.Prologix_GPIB_USB):
##%% Initialization SourceMeasurement()
## Creates the framework
##
def __init__(self):
Prologix.Prologix_GPIB_USB.__init__(self)
## **** Keithley 2400 Class ****
## by Aidan Macdonald
##
## Support given for some commands
##
from random import uniform as rand
class Keithley_2400(SourceMeasurement):
##%% Initialization - Keithley_2400()
## Loads commands
##
def __init__(self):
SourceMeasurement.__init__(self)
self.addCMD("IDN?", self.version)
self.addCMD("*RST", self.noth)
self.addCMD("*CLS", self.noth)
self.addCMD("ABOR", self.noth)
self.addCMD("*CLS", self.noth)
self.addCMD("SYST:BEEP:STAT", self.noth)
self.addCMD("SOURCE:CLEAR:AUTO", self.noth)
self.addCMD("FORM:ELEM", self.noth)
self.addCMD(":SOUR:FUNC", self.noth)
self.addCMD("read?", self.randFloats)
## noth(pty, args)
## Does nothing
##
def noth(self, fpl, args):
pass # Do nothing
def version(self, fpl, args):
return 'KEITHLEY INSTRUMENTS INC.,MODEL 2400.EMULATOR'
## randFloats(pty, args)
## returns a string of random floats
##
def randFloats(self, fpl, args):
return str(rand(0, 2)) + ", " str(rand(0, 2))
## handleCMD(fpl, cmd, args)
## Modified to be case insensitive
##
def handleCMD(self, fpl, cmd, args):
SourceMeasurement.handleCMD(self, fpl, cmd.upper(), args)

View File

@@ -0,0 +1,12 @@
#!/usr/bin/python
import PTY_Interface
import PowerSupply
pty = PTY_Interface.Interface()
pty.addDevice(PowerSupply.PowerSupply(), 5)
pty.addDevice(PowerSupply.E3631A(), 3)
pty.printFilename()
pty.run()

View File

@@ -0,0 +1,5 @@
#!/bin/bash
NAME=`echo $1 | awk -F'.' '{ print $1 }'`
cat $1 | grep "##" | sed s/"##"//g | sed s/"\*\*"/"="/g | sed s/"<<<<"/"="/g | sed s/">>>>"/"="/g | sed s/"%%"/"*"/g > $2

View File

@@ -0,0 +1,14 @@
#!/bin/bash
cat < $1 &
echo "++ver" > $1
echo "++addr" > $1
echo "++addr 12" > $1
echo "++addr" > $1
while true
do
read CMD
echo $CMD > $1
done