- 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>
198 lines
6.2 KiB
Python
Executable File
198 lines
6.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# pylint: disable=invalid-name
|
|
# *****************************************************************************
|
|
# This program is free software; you can redistribute it and/or modify it under
|
|
# the terms of the GNU General Public License as published by the Free Software
|
|
# Foundation; either version 2 of the License, or (at your option) any later
|
|
# version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
# details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License along with
|
|
# this program; if not, write to the Free Software Foundation, Inc.,
|
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
#
|
|
# Module authors:
|
|
# Markus Zolliker <markus.zolliker@psi.ch>
|
|
# *****************************************************************************
|
|
"""server for a string communicator
|
|
|
|
Usage:
|
|
|
|
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.
|
|
|
|
Use cases, mainly for test purposes:
|
|
|
|
- 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 argparse
|
|
from pathlib import Path
|
|
import socket
|
|
import time
|
|
import os
|
|
from ast import literal_eval
|
|
from socketserver import BaseRequestHandler, ThreadingTCPServer
|
|
|
|
# Add import path for inplace usage
|
|
sys.path.insert(0, str(Path(__file__).absolute().parents[1]))
|
|
|
|
from frappy.lib import get_class, formatException, mkthread
|
|
|
|
|
|
class Logger:
|
|
def debug(self, *args):
|
|
pass
|
|
|
|
def log(self, level, *args):
|
|
pass
|
|
|
|
def info(self, *args):
|
|
print(*args)
|
|
|
|
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):
|
|
parser = argparse.ArgumentParser(description="Relay to a communicator (simulated HW or other)")
|
|
parser.add_argument("-v", "--verbose",
|
|
help="output full communication",
|
|
action='store_true', default=False)
|
|
parser.add_argument("cls",
|
|
type=str,
|
|
help="communicator class.\n",)
|
|
parser.add_argument('-p',
|
|
'--port',
|
|
action='store',
|
|
help='server port or uri',
|
|
default=2089)
|
|
parser.add_argument('-o',
|
|
'--options',
|
|
action='store',
|
|
nargs='*',
|
|
help='options in the form key=value',
|
|
default=None)
|
|
return parser.parse_args(argv)
|
|
|
|
|
|
def main(argv=None):
|
|
if argv is None:
|
|
argv = sys.argv
|
|
|
|
args = parse_argv(argv[1:])
|
|
options = {'description': ''}
|
|
for item in args.options or ():
|
|
key, eq, value = item.partition('=')
|
|
if not eq:
|
|
raise ValueError(f"missing '=' in {item}")
|
|
try:
|
|
value = literal_eval(value)
|
|
except Exception:
|
|
pass
|
|
options[key] = value
|
|
srv = Server(int(args.port), args.cls, options, args.verbose)
|
|
srv.serve_forever()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main(sys.argv))
|