multiple fixes

This commit is contained in:
2026-04-23 15:59:30 +02:00
parent dcf5944b8e
commit 9682ccaa70
3 changed files with 328 additions and 61 deletions
+121 -48
View File
@@ -28,15 +28,55 @@ DEFAULT_CFG = """[this]
instrument={instrument}
frappy_ports = 15000-15009
"""
MARCHESRC = ['/home/software/marche']
MARCHESRC = ['/home/software/marche', '/home/l_samenv/marche']
CFGDIRS = ['/home/linse/config', '/home/l_samenv/linse_config']
def read_config(filename):
parser = ConfigParser()
parser.optxform = str
parser.read([str(filename)])
return dict(parser)
return {k: dict(parser[k].items()) for k in parser.sections()}
def change_mode(path, *, user=None, group=None, other=None):
# adds given modes
# example change_mode(path, user='+w', group='w', other='-r')
prev = path.stat().st_mode
mask = 0
bits = 0
for access in (user, group, other):
mask *= 8
bits *= 8
if access is not None:
mode = None
try:
for char in access:
if char in '+-=':
if mode is None:
mode = char
else:
raise ValueError(f'{char} must appear first in mode')
else:
if mode is None:
mode = '='
bitpos = 'xwr'.index(char)
if mode != '=':
mask |= 1 << bitpos
if mode != '-':
bits |= 1 << bitpos
if mode == '=':
mask |= 7
except KeyError:
raise ValueError(f'illegal access mode {access!r}')
mode = prev & ~mask | bits
if mode != prev:
path.chmod(mode)
def write_content(filename, content, **kwds):
path = Path(filename)
path.write_text(content)
change_mode(path, **kwds)
def write_config(filename, newvalues):
@@ -46,6 +86,7 @@ def write_config(filename, newvalues):
parser.read_dict(newvalues)
with open(filename, 'w') as f:
parser.write(f)
change_mode(Path(filename), group='+w')
class ArgError(ValueError):
@@ -53,26 +94,41 @@ class ArgError(ValueError):
class Config:
this = None
def __init__(self):
configfile = Path('~/.config/linsetools.cfg').expanduser()
configfile = Path('/sq_sw/linse/etc/linsetools.cfg')
self.sections = read_config(configfile)
self.instruments = {}
for key, section in self.sections.items():
if key == 'this':
host = socket.gethostname().split('.')[0]
section = {'instrument': host, 'frappy_ports': '15000-15009'}
mandatory = set(section)
this = self.sections.get('this', {})
section.update(this)
self.sections['this'] = section
if set(this) & mandatory != mandatory:
write_config(configfile, self.sections)
self.this = section['instrument']
self.instruments[self.this] = section
else:
head, _, tail = key.partition('.')
if tail and head == 'instrument':
self.instruments[tail] = section
instconfig = Path('/home/linse/.config/instrument.cfg')
if instconfig.is_file():
self.instruments = read_config(instconfig)
else:
host = socket.gethostname().split('.')[0]
self.instruments = {'this': {'instrument': host, 'frappy_ports': '15000-15009'}}
try:
write_config(instconfig, self.instruments)
except PermissionError:
pass
this = self.instruments.pop('this', None)
if this:
self.this = this['instrument']
self.instruments[self.this] = this
def parse_args(self, argmap, args, other=None):
result = {}
for arg in args:
if arg in self.instruments:
if 'instrument' in result:
raise ArgError('cannot give instrument twice')
result['instrument'] = arg
elif arg in argmap:
kind = argmap.get(arg, other)
if kind is None:
raise ArgError(f'unexpected argument: {arg!r}')
if kind in result:
raise ArgError(f'cannot give {kind} twice')
result[kind] = arg
return result
class Logger:
@@ -109,14 +165,14 @@ for marchedir in MARCHESRC:
break
STATUS_MAP = { # values are (<busy: bool), <target state:bool>, name)
STATUS_MAP = { # values are (<target state:bool>, <busy:bool>, name)
mj.DEAD: (False, False, 'DEAD'),
mj.NOT_RUNNING: (False, False, 'NOT RUNNING'),
mj.STARTING: (True, True, 'STARTING'),
mj.INITIALIZING: (True, True, 'INITIALIZING'),
mj.RUNNING: (False, True, 'RUNNING'),
mj.WARNING: (False, True, 'WARNING'),
mj.STOPPING: (True, False, 'STOPPING'),
mj.RUNNING: (True, False, 'RUNNING'),
mj.WARNING: (True, False, 'WARNING'),
mj.STOPPING: (False, True, 'STOPPING'),
mj.NOT_AVAILABLE: (False, False, 'NOT AVAILABLE'),
}
@@ -125,7 +181,7 @@ def wait_status(cl, service):
delay = 0.2
while True:
sts = cl.getServiceStatus(service)
if STATUS_MAP[sts][0]: # busy
if STATUS_MAP[sts][1]: # busy
if delay > 1.5: # this happens after about 5 sec
return False
time.sleep(delay)
@@ -136,25 +192,30 @@ def wait_status(cl, service):
class MarcheControl:
port = 8124
otherarg = None
argmap = {k: 'action' for k in ('start', 'restart', 'stop', 'gui', 'cli', 'list', 'listcfg')}
def __init__(self, host, port=None, user=None, instrument=None):
self.config = Config()
def __init__(self, instrument=None, host='localhost', port=None, user=None, config=None):
self.config = config or Config()
self.host = host
self.instance = instrument or 'this'
self.instrument = self.config.this if self.instance == 'this' else self.instance
self.ins_config = self.config.instruments[self.insttrument]
self.ins_config = self.config.instruments[self.instrument]
self.user = user or self.instrument # SINQ instruments
if not (Path('/home') / self.user).is_dir():
self.user = 'l_samenv'
if port is not None:
self.port = port
self._client = None
self.argmap = {k: 'action' for k in ('start', 'restart', 'stop', 'gui', 'cli', 'list')}
self.argmap.update((k, 'instrument') for k in self.config.instruments)
def connect(self):
if self._client is None:
# TODO: may need more generic solution for last arg
print(self.host, self.port, self.user)
self._client = Client(self.host, self.port, self.user, self.user.upper() + 'LNS')
if self.user == 'l_samenv':
x = 1
arg = f'{2**4+x}lns{x}'
else:
arg = self.user.upper() + 'LNS'
self._client = Client(self.host, self.port, self.user, arg)
# TODO; do we need disconnect?
@@ -186,26 +247,38 @@ class MarcheControl:
self.connect()
self._client.reloadJobs()
def _run(self, action, other):
def list(self, service='all', prt=print):
self.connect()
for key, value in self._client.getAllServiceInfo().items():
if service != 'all' and service != key:
continue
for inst, value in value['instances'].items():
name = '.'.join([key, inst] if inst else [key])
state = STATUS_MAP[value['state']][2].lower()
desc = value['desc']
prt(f'{name:30s} {state:12s} {desc}')
def do(self, action, other):
if action == 'start':
self.start(other)
elif action == 'restart':
self.restart(other)
elif action == 'stop':
self.stop(other)
elif action == 'list':
self.list()
else:
raise ArgError(f'unknown action {action!r}')
def run(self, *args):
self._run(self.parse_args(*args))
def parse_args(self, *args):
result = {}
for arg in args:
if arg in self.argmap:
kind = self.argmap.get(arg, 'other')
if kind in result:
raise ArgError(f'duplicate {kind}')
result[kind] = arg
return result
@classmethod
def do_on_ins(cls, args):
config = Config()
argdict = config.parse_args(cls.argmap, args, cls.otherarg)
instrument = argdict.pop('instrument', None)
if instrument is None and argdict.get('action') == 'list': # TODO: make more generic if needed
instruments = list(config.instruments)
else:
instruments = [instrument]
for ins in instruments:
control = cls(ins, config=config)
control.do(**argdict)