add sim-server again based on socketserver
- fix ls370test config file + fix issues with frappy_psi.ls370res + add frappy_psi.ls370sim Change-Id: Ie61e3ea01c4b9c7c1286426504e50acf9413a8ba Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34957 Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch> Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
This commit is contained in:
parent
c2673952f4
commit
142add9109
226
bin/sim-server
226
bin/sim-server
@ -22,22 +22,35 @@
|
|||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
bin/stringio-server <communciator> <server port>
|
bin/sim-server <communicator class> -p <server port> [-o <option1>=<value> <option2>=<value>]
|
||||||
|
|
||||||
open a server on <server port> to communicate with the string based <communicator> over TCP/IP.
|
open a server on <server port> to communicate with the string based <communicator> over TCP/IP.
|
||||||
|
|
||||||
Use cases, mainly for test purposes:
|
Use cases, mainly for test purposes:
|
||||||
- as a T, if the hardware allows only one connection, and more than one is needed
|
|
||||||
- relay to a communicator not using TCP/IP, if Frappy should run on an other host
|
|
||||||
- relay to a hardware simulation written as a communicator
|
- relay to a hardware simulation written as a communicator
|
||||||
|
|
||||||
|
> bin/sim-server frappy_psi.ls370sim.Ls370Sim
|
||||||
|
|
||||||
|
- relay to a communicator not using TCP/IP, if Frappy should run on an other host
|
||||||
|
|
||||||
|
> bin/sim-server frappy.io.StringIO -o uri=serial:///dev/tty...
|
||||||
|
|
||||||
|
- as a T, if the hardware allows only one connection, and more than one is needed:
|
||||||
|
|
||||||
|
> bin/sim-server frappy.io.StringIO -o uri=tcp://<host>:<port>
|
||||||
|
|
||||||
|
typically using communicator class frappy.io.StringIO
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import argparse
|
import argparse
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import asyncore
|
|
||||||
import socket
|
import socket
|
||||||
import time
|
import time
|
||||||
|
import os
|
||||||
|
from ast import literal_eval
|
||||||
|
from socketserver import BaseRequestHandler, ThreadingTCPServer
|
||||||
|
|
||||||
# Add import path for inplace usage
|
# Add import path for inplace usage
|
||||||
sys.path.insert(0, str(Path(__file__).absolute().parents[1]))
|
sys.path.insert(0, str(Path(__file__).absolute().parents[1]))
|
||||||
@ -45,92 +58,6 @@ sys.path.insert(0, str(Path(__file__).absolute().parents[1]))
|
|||||||
from frappy.lib import get_class, formatException, mkthread
|
from frappy.lib import get_class, formatException, mkthread
|
||||||
|
|
||||||
|
|
||||||
class LineHandler(asyncore.dispatcher_with_send):
|
|
||||||
|
|
||||||
def __init__(self, sock):
|
|
||||||
self.buffer = b""
|
|
||||||
asyncore.dispatcher_with_send.__init__(self, sock)
|
|
||||||
self.crlf = 0
|
|
||||||
|
|
||||||
def handle_line(self, line):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def handle_read(self):
|
|
||||||
data = self.recv(8192)
|
|
||||||
if data:
|
|
||||||
parts = data.split(b"\n")
|
|
||||||
if len(parts) == 1:
|
|
||||||
self.buffer += data
|
|
||||||
else:
|
|
||||||
self.handle_line((self.buffer + parts[0]).decode('latin_1'))
|
|
||||||
for part in parts[1:-1]:
|
|
||||||
if part[-1] == b"\r":
|
|
||||||
self.crlf = True
|
|
||||||
part = part[:-1]
|
|
||||||
else:
|
|
||||||
self.crlf = False
|
|
||||||
self.handle_line(part.decode('latin_1'))
|
|
||||||
self.buffer = parts[-1]
|
|
||||||
|
|
||||||
def send_line(self, line):
|
|
||||||
self.send((line + ("\r\n" if self.crlf else "\n")).encode('latin_1'))
|
|
||||||
|
|
||||||
|
|
||||||
class LineServer(asyncore.dispatcher):
|
|
||||||
|
|
||||||
def __init__(self, port, line_handler_cls, handler_args):
|
|
||||||
asyncore.dispatcher.__init__(self)
|
|
||||||
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
self.set_reuse_addr()
|
|
||||||
self.bind(('0.0.0.0', port))
|
|
||||||
self.listen(5)
|
|
||||||
print('accept connections at port', port)
|
|
||||||
self.line_handler_cls = line_handler_cls
|
|
||||||
self.handler_args = handler_args
|
|
||||||
|
|
||||||
def handle_accept(self):
|
|
||||||
pair = self.accept()
|
|
||||||
if pair is not None:
|
|
||||||
sock, addr = pair
|
|
||||||
print("Incoming connection from %s" % repr(addr))
|
|
||||||
self.line_handler_cls(sock, self.handler_args)
|
|
||||||
|
|
||||||
def loop(self):
|
|
||||||
asyncore.loop()
|
|
||||||
|
|
||||||
|
|
||||||
class Server(LineServer):
|
|
||||||
|
|
||||||
class Dispatcher:
|
|
||||||
def announce_update(self, *_):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def announce_update_error(self, *_):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwds):
|
|
||||||
super().__init__(*args, **kwds)
|
|
||||||
self.secnode = None
|
|
||||||
self.dispatcher = self.Dispatcher()
|
|
||||||
|
|
||||||
|
|
||||||
class Handler(LineHandler):
|
|
||||||
def __init__(self, sock, handler_args):
|
|
||||||
super().__init__(sock)
|
|
||||||
self.module = handler_args['module']
|
|
||||||
self.verbose = handler_args['verbose']
|
|
||||||
|
|
||||||
def handle_line(self, line):
|
|
||||||
try:
|
|
||||||
reply = self.module.communicate(line.strip())
|
|
||||||
if self.verbose:
|
|
||||||
print('%-40s | %s' % (line, reply))
|
|
||||||
except Exception:
|
|
||||||
print(formatException(verbose=True))
|
|
||||||
return
|
|
||||||
self.send_line(reply)
|
|
||||||
|
|
||||||
|
|
||||||
class Logger:
|
class Logger:
|
||||||
def debug(self, *args):
|
def debug(self, *args):
|
||||||
pass
|
pass
|
||||||
@ -144,43 +71,126 @@ class Logger:
|
|||||||
exception = error = warn = info
|
exception = error = warn = info
|
||||||
|
|
||||||
|
|
||||||
|
class TcpRequestHandler(BaseRequestHandler):
|
||||||
|
def setup(self):
|
||||||
|
print(f'connection opened from {self.client_address}')
|
||||||
|
self.running = True
|
||||||
|
self.request.settimeout(1)
|
||||||
|
self.data = b''
|
||||||
|
|
||||||
|
def finish(self):
|
||||||
|
"""called when handle() terminates, i.e. the socket closed"""
|
||||||
|
# close socket
|
||||||
|
try:
|
||||||
|
self.request.shutdown(socket.SHUT_RDWR)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
print(f'connection closed from {self.client_address}')
|
||||||
|
self.request.close()
|
||||||
|
|
||||||
|
def poller(self):
|
||||||
|
while True:
|
||||||
|
time.sleep(1.0)
|
||||||
|
self.module.doPoll()
|
||||||
|
|
||||||
|
def handle(self):
|
||||||
|
"""handle a new connection"""
|
||||||
|
# do a copy of the options, as they are consumed
|
||||||
|
self.module = self.server.modulecls(
|
||||||
|
'mod', Logger(), dict(self.server.options), self.server)
|
||||||
|
self.module.earlyInit()
|
||||||
|
|
||||||
|
mkthread(self.poller)
|
||||||
|
while self.running:
|
||||||
|
try:
|
||||||
|
newdata = self.request.recv(1024)
|
||||||
|
if not newdata:
|
||||||
|
return
|
||||||
|
except socket.timeout:
|
||||||
|
# no new data during read, continue
|
||||||
|
continue
|
||||||
|
self.data += newdata
|
||||||
|
while self.running:
|
||||||
|
message, sep, self.data = self.data.partition(b'\n')
|
||||||
|
if not sep:
|
||||||
|
break
|
||||||
|
cmd = message.decode('latin-1')
|
||||||
|
try:
|
||||||
|
reply = self.module.communicate(cmd.strip())
|
||||||
|
if self.server.verbose:
|
||||||
|
print('%-40s | %s' % (cmd, reply))
|
||||||
|
except Exception:
|
||||||
|
print(formatException(verbose=True))
|
||||||
|
return
|
||||||
|
outdata = reply.encode('latin-1') + b'\n'
|
||||||
|
try:
|
||||||
|
self.request.sendall(outdata)
|
||||||
|
except Exception as e:
|
||||||
|
print(repr(e))
|
||||||
|
self.running = False
|
||||||
|
|
||||||
|
|
||||||
|
class Server(ThreadingTCPServer):
|
||||||
|
allow_reuse_address = os.name != 'nt' # False on Windows systems
|
||||||
|
|
||||||
|
class Dispatcher:
|
||||||
|
def announce_update(self, *_):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def announce_update_error(self, *_):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __init__(self, port, modulecls, options, verbose=False):
|
||||||
|
super().__init__(('', port), TcpRequestHandler,
|
||||||
|
bind_and_activate=True)
|
||||||
|
self.secnode = None
|
||||||
|
self.dispatcher = self.Dispatcher()
|
||||||
|
self.verbose = verbose
|
||||||
|
self.modulecls = get_class(modulecls)
|
||||||
|
self.options = options
|
||||||
|
print(f'started sim-server listening on port {port}')
|
||||||
|
|
||||||
|
|
||||||
def parse_argv(argv):
|
def parse_argv(argv):
|
||||||
parser = argparse.ArgumentParser(description="Simulate HW with a serial interface")
|
parser = argparse.ArgumentParser(description="Relay to a communicator (simulated HW or other)")
|
||||||
parser.add_argument("-v", "--verbose",
|
parser.add_argument("-v", "--verbose",
|
||||||
help="output full communication",
|
help="output full communication",
|
||||||
action='store_true', default=False)
|
action='store_true', default=False)
|
||||||
parser.add_argument("cls",
|
parser.add_argument("cls",
|
||||||
type=str,
|
type=str,
|
||||||
help="simulator class.\n",)
|
help="communicator class.\n",)
|
||||||
parser.add_argument('-p',
|
parser.add_argument('-p',
|
||||||
'--port',
|
'--port',
|
||||||
action='store',
|
action='store',
|
||||||
help='server port or uri',
|
help='server port or uri',
|
||||||
default=2089)
|
default=2089)
|
||||||
|
parser.add_argument('-o',
|
||||||
|
'--options',
|
||||||
|
action='store',
|
||||||
|
nargs='*',
|
||||||
|
help='options in the form key=value',
|
||||||
|
default=None)
|
||||||
return parser.parse_args(argv)
|
return parser.parse_args(argv)
|
||||||
|
|
||||||
|
|
||||||
def poller(pollfunc):
|
|
||||||
while True:
|
|
||||||
time.sleep(1.0)
|
|
||||||
pollfunc()
|
|
||||||
|
|
||||||
|
|
||||||
def main(argv=None):
|
def main(argv=None):
|
||||||
if argv is None:
|
if argv is None:
|
||||||
argv = sys.argv
|
argv = sys.argv
|
||||||
|
|
||||||
args = parse_argv(argv[1:])
|
args = parse_argv(argv[1:])
|
||||||
|
options = {'description': ''}
|
||||||
opts = {'description': 'simulator'}
|
for item in args.options or ():
|
||||||
|
key, eq, value = item.partition('=')
|
||||||
handler_args = {'verbose': args.verbose}
|
if not eq:
|
||||||
srv = Server(int(args.port), Handler, handler_args)
|
raise ValueError(f"missing '=' in {item}")
|
||||||
module = get_class(args.cls)(args.cls, Logger(), opts, srv)
|
try:
|
||||||
handler_args['module'] = module
|
value = literal_eval(value)
|
||||||
module.earlyInit()
|
except Exception:
|
||||||
mkthread(poller, module.doPoll)
|
pass
|
||||||
srv.loop()
|
options[key] = value
|
||||||
|
srv = Server(int(args.port), args.cls, options, args.verbose)
|
||||||
|
srv.serve_forever()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -63,7 +63,12 @@ def parse_result(reply):
|
|||||||
|
|
||||||
class LakeShoreIO(HasIO):
|
class LakeShoreIO(HasIO):
|
||||||
def set_param(self, cmd, *args):
|
def set_param(self, cmd, *args):
|
||||||
head = ','.join([cmd] + [f'{a:g}' for a in args])
|
args = [f'{a:g}' for a in args]
|
||||||
|
if ' ' in cmd.strip():
|
||||||
|
args.insert(0, cmd)
|
||||||
|
else:
|
||||||
|
args[0] = cmd + args[0]
|
||||||
|
head = ','.join(args)
|
||||||
tail = cmd.replace(' ', '?')
|
tail = cmd.replace(' ', '?')
|
||||||
reply = self.io.communicate(f'{head};{tail}')
|
reply = self.io.communicate(f'{head};{tail}')
|
||||||
return parse_result(reply)
|
return parse_result(reply)
|
||||||
@ -99,7 +104,7 @@ class Switcher(LakeShoreIO, ChannelSwitcher):
|
|||||||
if channelno is None:
|
if channelno is None:
|
||||||
self.status = 'ERROR', 'no enabled channel'
|
self.status = 'ERROR', 'no enabled channel'
|
||||||
return
|
return
|
||||||
self.set_param(f'SCAN {channelno},0')
|
self.set_param('SCAN ', channelno, 0)
|
||||||
|
|
||||||
def doPoll(self):
|
def doPoll(self):
|
||||||
"""poll buttons
|
"""poll buttons
|
||||||
@ -160,7 +165,7 @@ class Switcher(LakeShoreIO, ChannelSwitcher):
|
|||||||
self.measure_delay = chan.dwell
|
self.measure_delay = chan.dwell
|
||||||
|
|
||||||
def set_active_channel(self, chan):
|
def set_active_channel(self, chan):
|
||||||
self.set_param(f'SCAN {chan.channel},0')
|
self.set_param('SCAN ', chan.channel, 0)
|
||||||
chan._last_range_change = time.monotonic()
|
chan._last_range_change = time.monotonic()
|
||||||
self.set_delays(chan)
|
self.set_delays(chan)
|
||||||
|
|
||||||
@ -227,7 +232,7 @@ class ResChannel(LakeShoreIO, Channel):
|
|||||||
now = time.monotonic()
|
now = time.monotonic()
|
||||||
if now + 0.5 < max(self._last_range_change, self.switcher._start_switch) + self.pause:
|
if now + 0.5 < max(self._last_range_change, self.switcher._start_switch) + self.pause:
|
||||||
return None
|
return None
|
||||||
result = self.get_param(f'RDGR{self.channel}')
|
result = self.get_param(f'RDGR?{self.channel}')
|
||||||
if self.autorange:
|
if self.autorange:
|
||||||
self.fix_autorange()
|
self.fix_autorange()
|
||||||
if now + 0.5 > self._last_range_change + self.pause:
|
if now + 0.5 > self._last_range_change + self.pause:
|
||||||
@ -251,7 +256,7 @@ class ResChannel(LakeShoreIO, Channel):
|
|||||||
|
|
||||||
def read_value(self):
|
def read_value(self):
|
||||||
if self.channel == self.switcher.value == self.switcher.target:
|
if self.channel == self.switcher.value == self.switcher.target:
|
||||||
value = self._read_value()
|
value = self.get_value()
|
||||||
if value is not None:
|
if value is not None:
|
||||||
return value
|
return value
|
||||||
return self.value # return previous value
|
return self.value # return previous value
|
||||||
@ -264,7 +269,7 @@ class ResChannel(LakeShoreIO, Channel):
|
|||||||
|
|
||||||
@CommonReadHandler(rdgrng_params)
|
@CommonReadHandler(rdgrng_params)
|
||||||
def read_rdgrng(self):
|
def read_rdgrng(self):
|
||||||
iscur, exc, rng, autorange, excoff = self.get_param(f'RDGRNG{self.channel}')
|
iscur, exc, rng, autorange, excoff = self.get_param(f'RDGRNG?{self.channel}')
|
||||||
self._prev_rdgrng = iscur, exc
|
self._prev_rdgrng = iscur, exc
|
||||||
if autorange: # pressed autorange button
|
if autorange: # pressed autorange button
|
||||||
if not self._toggle_autorange:
|
if not self._toggle_autorange:
|
||||||
@ -293,8 +298,7 @@ class ResChannel(LakeShoreIO, Channel):
|
|||||||
excoff = 1
|
excoff = 1
|
||||||
rng = change['range']
|
rng = change['range']
|
||||||
if self.autorange:
|
if self.autorange:
|
||||||
if rng < self.minrange:
|
rng = max(rng, self.minrange)
|
||||||
rng = self.minrange
|
|
||||||
self.set_param(f'RDGRNG {self.channel}', iscur, exc, rng, 0, excoff)
|
self.set_param(f'RDGRNG {self.channel}', iscur, exc, rng, 0, excoff)
|
||||||
self.read_range()
|
self.read_range()
|
||||||
|
|
||||||
|
@ -16,84 +16,38 @@
|
|||||||
# Module authors:
|
# Module authors:
|
||||||
# Markus Zolliker <markus.zolliker@psi.ch>
|
# Markus Zolliker <markus.zolliker@psi.ch>
|
||||||
# *****************************************************************************
|
# *****************************************************************************
|
||||||
"""a very simple simulator for a LakeShore Model 370"""
|
"""a very simple simulator for LakeShore Models 370 and 372
|
||||||
|
|
||||||
|
reduced to the functionality actually used in e.g. frappy_psi.ls370res
|
||||||
|
"""
|
||||||
|
|
||||||
|
import time
|
||||||
from frappy.modules import Communicator
|
from frappy.modules import Communicator
|
||||||
|
|
||||||
|
|
||||||
class Ls370Sim(Communicator):
|
class _Ls37xSim(Communicator):
|
||||||
CHANNEL_COMMANDS = [
|
# commands containing %d for the channel number
|
||||||
('RDGR?%d', '1.0'),
|
|
||||||
('RDGST?%d', '0'),
|
|
||||||
('RDGRNG?%d', '0,5,5,0,0'),
|
|
||||||
('INSET?%d', '1,5,5,0,0'),
|
|
||||||
('FILTER?%d', '1,5,80'),
|
|
||||||
]
|
|
||||||
OTHER_COMMANDS = [
|
|
||||||
('*IDN?', 'LSCI,MODEL370,370184,05302003'),
|
|
||||||
('SCAN?', '3,1'),
|
|
||||||
('*OPC?', '1'),
|
|
||||||
]
|
|
||||||
|
|
||||||
def earlyInit(self):
|
|
||||||
super().earlyInit()
|
|
||||||
self._data = dict(self.OTHER_COMMANDS)
|
|
||||||
for fmt, v in self.CHANNEL_COMMANDS:
|
|
||||||
for chan in range(1,17):
|
|
||||||
self._data[fmt % chan] = v
|
|
||||||
|
|
||||||
def communicate(self, command):
|
|
||||||
self.comLog('> %s' % command)
|
|
||||||
# simulation part, time independent
|
|
||||||
for channel in range(1,17):
|
|
||||||
_, _, _, _, excoff = self._data['RDGRNG?%d' % channel].split(',')
|
|
||||||
if excoff == '1':
|
|
||||||
self._data['RDGST?%d' % channel] = '6'
|
|
||||||
else:
|
|
||||||
self._data['RDGST?%d' % channel] = '0'
|
|
||||||
|
|
||||||
chunks = command.split(';')
|
|
||||||
reply = []
|
|
||||||
for chunk in chunks:
|
|
||||||
if '?' in chunk:
|
|
||||||
reply.append(self._data[chunk])
|
|
||||||
else:
|
|
||||||
for nqarg in (1,0):
|
|
||||||
if nqarg == 0:
|
|
||||||
qcmd, arg = chunk.split(' ', 1)
|
|
||||||
qcmd += '?'
|
|
||||||
else:
|
|
||||||
qcmd, arg = chunk.split(',', nqarg)
|
|
||||||
qcmd = qcmd.replace(' ', '?', 1)
|
|
||||||
if qcmd in self._data:
|
|
||||||
self._data[qcmd] = arg
|
|
||||||
break
|
|
||||||
reply = ';'.join(reply)
|
|
||||||
self.comLog('< %s' % reply)
|
|
||||||
return reply
|
|
||||||
|
|
||||||
|
|
||||||
class Ls372Sim(Communicator):
|
|
||||||
CHANNEL_COMMANDS = [
|
CHANNEL_COMMANDS = [
|
||||||
('RDGR?%d', '1.0'),
|
('RDGR?%d', '1.0'),
|
||||||
('RDGK?%d', '1.5'),
|
('RDGK?%d', '1.5'),
|
||||||
('RDGST?%d', '0'),
|
('RDGST?%d', '0'),
|
||||||
('RDGRNG?%d', '0,5,5,0,0'),
|
('RDGRNG?%d', '0,5,5,0,0'),
|
||||||
('INSET?%d', '1,5,5,0,0'),
|
('INSET?%d', '1,3,3,0,0'),
|
||||||
('FILTER?%d', '1,5,80'),
|
('FILTER?%d', '1,5,80'),
|
||||||
]
|
]
|
||||||
|
# commands not related to a channel
|
||||||
OTHER_COMMANDS = [
|
OTHER_COMMANDS = [
|
||||||
('*IDN?', 'LSCI,MODEL372,372184,05302003'),
|
|
||||||
('SCAN?', '3,1'),
|
('SCAN?', '3,1'),
|
||||||
('PID?1', '10,10,0'),
|
|
||||||
('*OPC?', '1'),
|
('*OPC?', '1'),
|
||||||
]
|
]
|
||||||
|
|
||||||
def earlyInit(self):
|
def earlyInit(self):
|
||||||
super().earlyInit()
|
super().earlyInit()
|
||||||
|
self._res = {}
|
||||||
|
self._start = time.time()
|
||||||
self._data = dict(self.OTHER_COMMANDS)
|
self._data = dict(self.OTHER_COMMANDS)
|
||||||
for fmt, v in self.CHANNEL_COMMANDS:
|
for fmt, v in self.CHANNEL_COMMANDS:
|
||||||
for chan in range(1,17):
|
for chan in range(1, 17):
|
||||||
self._data[fmt % chan] = v
|
self._data[fmt % chan] = v
|
||||||
|
|
||||||
def communicate(self, command):
|
def communicate(self, command):
|
||||||
@ -105,6 +59,10 @@ class Ls372Sim(Communicator):
|
|||||||
self._data['RDGST?%d' % channel] = '6'
|
self._data['RDGST?%d' % channel] = '6'
|
||||||
else:
|
else:
|
||||||
self._data['RDGST?%d' % channel] = '0'
|
self._data['RDGST?%d' % channel] = '0'
|
||||||
|
channel = int(self._data['SCAN?'].split(',', 1)[0])
|
||||||
|
self._res[channel] = channel + (time.time() - self._start) / 3600
|
||||||
|
strvalue = f'{self._res[channel]:g}'
|
||||||
|
self._data['RDGR?%d' % channel] = self._data['RDGK?%d' % channel] = strvalue
|
||||||
|
|
||||||
chunks = command.split(';')
|
chunks = command.split(';')
|
||||||
reply = []
|
reply = []
|
||||||
@ -112,7 +70,7 @@ class Ls372Sim(Communicator):
|
|||||||
if '?' in chunk:
|
if '?' in chunk:
|
||||||
reply.append(self._data[chunk])
|
reply.append(self._data[chunk])
|
||||||
else:
|
else:
|
||||||
for nqarg in (1,0):
|
for nqarg in (1, 0):
|
||||||
if nqarg == 0:
|
if nqarg == 0:
|
||||||
qcmd, arg = chunk.split(' ', 1)
|
qcmd, arg = chunk.split(' ', 1)
|
||||||
qcmd += '?'
|
qcmd += '?'
|
||||||
@ -125,3 +83,16 @@ class Ls372Sim(Communicator):
|
|||||||
reply = ';'.join(reply)
|
reply = ';'.join(reply)
|
||||||
self.comLog('< %s' % reply)
|
self.comLog('< %s' % reply)
|
||||||
return reply
|
return reply
|
||||||
|
|
||||||
|
|
||||||
|
class Ls370Sim(_Ls37xSim):
|
||||||
|
OTHER_COMMANDS = _Ls37xSim.OTHER_COMMANDS + [
|
||||||
|
('*IDN?', 'LSCI,MODEL370,370184,05302003'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Ls372Sim(_Ls37xSim):
|
||||||
|
OTHER_COMMANDS = _Ls37xSim.OTHER_COMMANDS + [
|
||||||
|
('*IDN?', 'LSCI,MODEL372,372184,05302003'),
|
||||||
|
('PID?1', '10,10,0'),
|
||||||
|
]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user