Compare commits

59 Commits
master ... tmp

Author SHA1 Message Date
89d9e184bc unify Frappy.show and Frappy.show_state
+ fix indentation
2024-12-03 14:56:50 +01:00
423366a5c5 improve meaning handling
- Moveables with meaning 'temperature' are added to
  the 'temperature_regulation' target list
- Devices with meaning 'temperature_regulation' are added
  to 'temperature' target list, but with much lower priority
  (should be considered only when no sample quantity is detected)
2024-12-03 11:47:22 +01:00
d06b552526 FrappyConfig.__call__: fix bug when main is None 2024-12-03 11:47:22 +01:00
dmc
4f1834105b fix meaning bug 2024-12-03 11:47:22 +01:00
84f70f8204 improve mechanism to determine aliases
- meanings from name guessing has sligthly lower importance then
  given meanings
- then temperature meaning is missing, take temperature_regulation
2024-12-03 11:47:22 +01:00
771e0fdce7 improve config guess 2024-12-03 11:47:22 +01:00
dmc
2189323983 try to avoid 'None' as target
use '-' instead
2024-12-03 11:47:21 +01:00
cbb857f439 try to treat FrappyNode.target == 'None' properly 2024-12-03 11:47:21 +01:00
d30bb11bc1 do not show fm.error
as it will flood log output

- how to handle this properly?
2024-12-03 11:47:21 +01:00
0869f8b5d9 no warning 'does not match target' while restarting 2024-12-03 11:47:21 +01:00
173ef914d7 fix 2 issues with starting stopping
- secnode devices were not disconnected before stopping
  leading to error messages
- target of secnodes not properly updated
2024-12-03 11:47:21 +01:00
cb99a73935 FrappyNode.doInit must super call SecNodeDevice.doInit 2024-12-03 11:47:21 +01:00
666cefb6a6 fix error with predef_aliases 2024-12-03 11:47:21 +01:00
4e260decb4 FrappyNode.target must be set after stop but before restart 2024-12-03 11:47:21 +01:00
163e0194b1 FrappyNode.target must be set before restarting servers 2024-12-03 11:47:21 +01:00
81dd4aad23 fix envlist calculation
+ fix pathed loggers mechanism
2024-12-03 11:47:21 +01:00
62da0bd55e add timestamp and ppms setups 2024-12-03 11:47:21 +01:00
a5f6047b75 frappy.show: do not show nicos column if equal to frappy 2024-12-03 11:47:21 +01:00
3cec0ea357 frappy.show_state: do not show partial status 2024-12-03 11:47:21 +01:00
3c41e7cf0c nicer output of server state
- more outstanding output of frappy server state
- frappy() does now alse an update if needed
- frappy.show() to show state only
2024-12-03 11:47:21 +01:00
2dcf8f0cc3 improve detect_changes
check also if envlist and aliases have to be rebuilt
2024-12-03 11:47:21 +01:00
24d76e4d2d [wip] auto sample environment change, basic version 2024-12-03 11:47:21 +01:00
5bd6eeff98 frappy.has_changed() should not be triggered repeatedly
fix bug
2024-12-03 11:47:21 +01:00
57e6a73599 rework frappy to be a device instead of a command
+ improve proposed config mechanism
2024-12-03 11:47:21 +01:00
5d2e4aeedd fix bad prefix in frappy_main setup 2024-12-03 11:47:21 +01:00
0e9216629b se_ prefix may be override by SE_PREFIX env var. 2024-12-03 11:45:14 +01:00
b978489695 improve frappy() command
- only informational effect without arguments
- consider also cfg from sea
- on nicos restart, frappy nodes look also for sea device
2024-12-03 11:45:14 +01:00
0642ab7d09 remove disconnected from state 2023-10-17 14:38:48 +02:00
7b08743a3f add proposed into state 2023-10-17 14:37:19 +02:00
062341ab8a better text 2023-10-17 14:23:48 +02:00
2c01121ccc catch error when shutting frappy (sea remove) 2023-10-17 14:18:34 +02:00
03359d5b15 add disconnected services to proposed 2023-10-17 14:07:59 +02:00
9b56a6fb95 fix clearing target on secnode 2023-10-17 14:00:28 +02:00
15cefee221 set secnode target to '' on disconnect 2023-10-17 13:59:16 +02:00
9e489d8cfd [WIP] fix disconencted info 2023-10-17 13:55:52 +02:00
b97eff8afb [WIP] add <disconnected> info 2023-10-17 13:54:36 +02:00
aa40e0255b [WIP} add frappy.update() 2023-10-17 13:39:55 +02:00
15a839bf6c add frappy.has_changed() to startup 2023-10-17 13:07:57 +02:00
91dde94e24 [WIP] remove givencfgs from result 2023-10-17 13:07:40 +02:00
ee56a23d0b [WIP] bug fix 2023-10-17 12:54:54 +02:00
f5ac31f25f [WIP] add 'server' to title line 2023-10-17 11:29:13 +02:00
a54a4054d9 [WIP] do not show given column 2023-10-17 11:21:36 +02:00
da56f6da6d [WIP] fix call to to_consider in handle_notifcations 2023-10-17 11:17:58 +02:00
04b5c1921b [WIP] remove 'strict' arg 2023-10-17 11:04:33 +02:00
45fd71077c [WIP] use get_server_state instead of propose_cfgs 2023-10-17 11:02:27 +02:00
0edda9f5c8 [WIP] show server state at startup only when initial guess failed 2023-10-17 08:40:11 +02:00
0f39271ff1 [WIP] fix 'auto' type 2023-10-17 08:28:34 +02:00
093b27804c [WIP] show_server_state arg for frappy.has_changed() 2023-10-16 16:03:47 +02:00
b945dd94ec [WIP] do not show table at setup 2023-10-16 16:00:23 +02:00
6195bfb6e5 [WIP] rename to frappy.has_changed() 2023-10-16 15:57:14 +02:00
d4e2b12016 [WIP] _previous_shown is service state 2023-10-16 11:54:45 +02:00
0e059c644c [WIP] frappy_changed() -> frappy.changed() 2023-10-16 11:50:59 +02:00
03d4ffb0bc [WIP] show_config: line by line 2023-10-16 11:40:54 +02:00
ed983d36ae [WIP] frappy(): show all servers config 2023-10-16 11:39:19 +02:00
c2473d9e51 [WIP] rename frappy_config to frappy in setup 2023-10-16 11:30:49 +02:00
fedfe3a278 [WIP] fix old .guess_cfg method 2023-10-16 11:29:35 +02:00
0f1538c156 [WIP] frappy() is now a device 2023-10-16 11:16:18 +02:00
c73390195d [WIP] change frappy_config to frappy
work to be done:
- frappy() is now called as e device

work done:
- handle_notifications is not only waiting for triggers,
  but rechecks servers all 60 seconds
2023-09-25 17:26:46 +02:00
56fec16247 improve frappy server management
- do connect in background when the frappy server is not running
  on startup
2023-09-20 17:25:07 +02:00
5 changed files with 14 additions and 671 deletions

View File

@ -27,8 +27,6 @@ connected to a SEC node
"""
import threading
import socket
import json
from nicos import config, session
from nicos.core import Override, Param, Moveable, status, POLLER, SIMULATION, DeviceAlias, \
Device, anytype, listof, MASTER
@ -40,8 +38,6 @@ from nicos.commands.basic import AddSetup, CreateAllDevices, CreateDevice
from nicos.utils import loggers
from servicemanager import FrappyManager, SeaManager, Reconnect, Keep
SECOP_UDP_PORT = 10767
SERVICES = FrappyManager.services
@ -117,21 +113,6 @@ def get_frappy_config():
return None
def send_other_udp(uri, instrument, device=None):
"""inform the feeder about the start of a frappy server"""
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
msg = {
'SECoP': 'for_other_node',
'uri': uri,
'instrument': instrument,
}
if device:
msg['device'] = device
msg = json.dumps(msg, ensure_ascii=False, separators=(',', ':')).encode('utf-8')
sock.sendto(msg, ('255.255.255.255', SECOP_UDP_PORT))
class FrappyConfig(Device):
# respect the order: e.g. temperature_regulation must be after temperature
# because it will not be added to envlist when temperature is the same device
@ -346,25 +327,9 @@ class FrappyConfig(Device):
CreateDevice(nodename)
cleanup_defunct()
CreateAllDevices()
fm = FrappyManager()
ins = config.instrument
fm.get_server_state(ins, new_cfg)
recorders = {}
for service, secnode in secnodes.items():
if services.get(service) and secnode:
cfg = fm.frappy_cfgs.get(service)
seacfg = fm.frappy2sea.get(cfg)
if secnode() and not seacfg:
if cfg:
recorders[service] = f'localhost:{fm.info[ins].get(service, 0)}/{cfg}'
else:
recorders[service] = secnode.uri
secnode._secnode.connect()
if recorders:
try:
fm.sea.sea_recorder(ins, recorders)
except Exception:
pass
self.set_envalias()
for secnode in remove_cfg:
secnode.disable()
@ -632,6 +597,8 @@ class FrappyConfig(Device):
to_remove = to_remove.difference(addedenv)
prevenv = [k for k in session.experiment.envlist if k not in to_remove]
envlist = prevenv + [k for k in addedenv if k not in prevenv]
if set(envlist) == set(prevenv):
envlist = None
predef_changes = []
for aliasname, cfg in predef_aliases:
@ -642,7 +609,7 @@ class FrappyConfig(Device):
def check_envalias(self):
envlist, new_aliases, predef_aliases = self.needed_envalias()
if set(envlist) != set(session.experiment.envlist):
if envlist:
return f"envlist should be {', '.join(envlist)}"
anew = [k for k, v in new_aliases.items() if v is not None]
removed = set(new_aliases).difference(anew)
@ -693,19 +660,15 @@ class FrappyConfig(Device):
applyAliasConfig() # for other aliases
prev = set(session.experiment.envlist)
remove = ', '.join(prev.difference(envlist))
session.experiment.setEnvironment(envlist)
show = []
keep = ', '.join(d for d in envlist if d in prev)
if keep:
show.append(f'keep {keep}')
add = ', '.join(d for d in envlist if d not in prev)
if add:
show.append(f'add {add}')
if remove:
show.append(f'remove {remove}')
session.log.info('environment: %s', '; '.join(show))
if envlist is not None:
prev = session.experiment.envlist
removed = set(prev).difference(envlist)
session.experiment.setEnvironment(envlist)
if removed:
session.log.info('removed %s from environment', ', '.join(removed))
added = set(envlist).difference(prev)
if added:
session.log.info('added %s to environment', ', '.join(added))
class FrappyNode(SecNodeDevice, Moveable):
@ -867,10 +830,8 @@ class FrappyNode(SecNodeDevice, Moveable):
if available_cfg is not None:
raise ValueError('use "frappy_list()" to get a list of valid frappy configurations')
uri = 'localhost:%d' % info[self.service]
send_other_udp(uri, config.instrument, device=cfg)
else:
uri = cfg
send_other_udp(uri, config.instrument)
if uri != self.uri:
self._disconnect(keeptarget=True)
if uri:

View File

@ -1,494 +0,0 @@
# *****************************************************************************
#
# 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>
#
# *****************************************************************************
"""collection of commands and devices for lab experiments
the commands are probably better not used, as the standard scan and sweep
commands are overwritten - this might be confusing for users working also
in neutron instruments
"""
import os
from os.path import join
import inspect
import shutil
import re
from glob import glob
import time
import math
from nicos import session
from nicos.core import Override, Param, Measurable, Moveable, Device, \
status, Readable
from nicos.core.errors import NicosError
from nicos.commands import helparglist, usercommand, parallel_safe
from nicos.commands.device import maw
import nicos.devices.generic.paramdev as paramdev
import nicos.commands.scan
from nicos_sinq.frappy_sinq.timestamp import Timestamp, Det
class FunDevice(Moveable):
"""wrap a device in a function
see usercommand fun
"""
temporary = True
_dev = None
_value = None
def __init__(self, func, dev_or_name=None, unit=None, devname=None, **kwargs):
self._func = func
self._kwargs = kwargs
if isinstance(dev_or_name, Device):
self._dev = dev_or_name
self._devname = dev_or_name.name
if devname:
# or should we raise an error?
name = devname
else:
name = self._devname
else:
name = dev_or_name or devname
self._devname = devname
self._dev = session.devices.get(devname)
if self._dev and unit is None:
unit = self._dev.unit
Moveable.__init__(self, name, unit=unit or '')
def doRead(self, maxage=0):
if self._devname:
if not self._dev:
try:
self._dev = session.devices[self._devname]
if not self.unit:
self.unit = self._dev.unit
except KeyError:
raise NicosError('device %r does not exist' % self._devname)
if self._value is None:
self._value = self._dev.read(maxage)
return self._value
def doStart(self, value):
if self._devname:
oldvalue = self.doRead()
else:
oldvalue = self._value
# append args depending in function signature
argnames, varargs, varkw = inspect.getfullargspec(self._func)[0:3]
args = self._dev, oldvalue
kwds = dict(self._kwargs)
if 'oldvalue' in argnames:
kwds['oldvalue'] = oldvalue
args = args[:1]
if 'dev' in argnames:
kwds['dev'] = self._dev
args = ()
if not varkw:
for k in kwds:
if k not in argnames:
kwds.pop(k)
result = self._func(value, *args[:len(argnames) - 1], **kwds)
self._value = value if result is None else result
def doIsCompleted(self):
return True
def doStop(self):
if self._dev:
self._dev.stop()
@usercommand
@helparglist('dev_or_name[, unit[, devname=..., **kwargs]]')
def fun(dev_or_name=None, unit=None, devname=None, **kwargs):
"""decorator for turning a function into a device for scans
Usage:
A function wrapped around a device (the argument might be a device name or a device)
@fun(dev="mydevice"):
def func(value, dev):
do_something_before_move()
maw(dev, value)
do_something_after_move()
scan(func, ...)
func will inherit the name and unit from the device.
func may have 1-3 positional arguments:
- value: the value to move to
- dev: the device given (in above example mydevice)
- oldvalue: the value before the move
A function turned into a device:
@fun("myname", "K")
def func(value):
result = do_some_thing(value)
return result
if no name is given, the function name is used.
in this case the final result may be returned, if different from value
"""
def decorator(func, name=dev_or_name):
if name is None:
name = func.__name__
return FunDevice(func, name, unit, devname, **kwargs)
return decorator
class Range(tuple):
def __new__(cls, astext, *args):
return tuple.__new__(cls, args)
def __init__(self, astext, *args):
super().__init__()
self.astext = astext
def __repr__(self):
return self.astext
def __add__(self, other):
astext = '%s+%s' % (repr(self), repr(other))
try:
if self[-1] == other[0]:
other = other[1:]
except IndexError:
pass
return Range(astext, *self, *other)
@usercommand
@helparglist('start, step, end')
def lnr(start, step_or_end=None, end=None, n=None):
"""linear range
to be used as argument for the scan command
Alternative form:
>>> lnr(start, end, n=n) # n+1 points
ranges might be added, adjacent equal points are omitted automatically
>>> list(lnr(0,1,4) + lnr(4,2,10) + [15])
> [0,1,2,3,4,6,8,10,15]
"""
if end is None:
astext = 'lnr(%s,%s,n=%s)' % (start, step_or_end, n)
if step_or_end is None:
# single value
return Range('lnr(%s)' % start, start)
end = step_or_end
if (n or 1) == 1:
# two values
return Range('lnr(%s,%s)' % (start, end), start, end)
n = abs(n)
step = (end - start) / n
else:
step = step_or_end
if n is not None or step == 0:
raise ValueError('illegal arguments')
astext = 'lnr(%s,%s,%s)' % (start, step_or_end, end)
n = int(round(abs((end - start) / step)))
if (start < end) == (step < 0):
step = -step
return Range(astext, start, *tuple(start + i * step for i in range(1,n)), end)
@usercommand
@helparglist('start, factor, end')
def lgr(start, factor_or_end, end=None, n=0):
"""logarithmic range
factor is the factor between two points (0.5 and 2 are equivalent)
Alternative form:
>>> lgr(start, end, n=n) # n+1 points
ranges might be added, adjacent equal points are omitted automatically
list(lnr(0,1,4) + lgr(4,2,15) + [30]) == [0,1,2,3,4,8,15,30]
"""
if end is None:
end = factor_or_end
if start <= 0 or end <= 0 or n <= 0:
raise ValueError('illegal arguments')
astext = 'lgr(%s,%s,n=%s)' % (start, end, n)
factor = (end / start) ** (1 / n)
else:
factor = factor_or_end
if start <= 0 or end <= 0 or factor <= 0:
raise ValueError('illegal arguments')
if (start > end) == (factor > 1):
factor = 1 / factor
astext = 'lgr(%s,%.18g,%s)' % (start, factor, end)
n = int(round(abs(math.log2(end / start) / math.log2(factor))))
return Range(astext, start,
*(start * factor ** i for i in range(1, n)),
end)
class WithTimeoutDev(Moveable):
"""Wrapper for better timeout/settling mechanism"""
parameters = {
'dev': Param('dev', type=lambda x=None: x, settable=True, mandatory=True),
'tolerance': Param('tolerance', type=float,
settable=True, default=1, mandatory=False),
'settle': Param('time to settle within tolerance', type=float,
settable=True, default=1, mandatory=False),
'timeout': Param('timeout for less progress than tolerance (defaults to settle)', type=float,
settable=True, default=-1, mandatory=False),
}
temporary = True
_start_time = None
def _getCache(self):
return None
def doStart(self, value):
self._start_time = time.time()
self._settle_start = None
self._settle_done = 0
self._mindif = abs(value - self.dev())
self.dev.doStart(value)
def doStop(self):
self._start_time = None
self.dev.doStop()
def doRead(self, maxage=0):
return self.dev.doRead(maxage)
def doStatus(self, maxage=0):
if not self._start_time:
return status.OK, ''
now = time.time()
dif = abs(self.doRead(maxage) - self.target)
if dif < self.tolerance:
if not self._settle_start:
self.log.info('settling %s' % self.dev)
self._settle_start = now - self._settle_done
if now > self._settle_start + self.settle:
self._start_time = None
return status.OK, ''
else:
if now > self._start_time + (self.timeout or self.settle):
self._start_time = None
self.log.info('timeout waiting for %s' % self.dev)
return status.OK, ''
if self._settle_start:
self._settle_done = now - self._settle_start
self._settle_start = None
if dif < self._mindif:
self._mindif = dif - self.tolerance
self._start_time = now
return status.BUSY, ''
@usercommand
@helparglist('dev, tolerance [,settle [,timeout]]')
@parallel_safe
def WithTimeout(dev, tolerance, settle=300, timeout=0):
if isinstance(dev, WithTimeoutDev):
dev.tolerance = tolerance
dev.settle = settle
dev.timeout = timeout
return dev
return WithTimeoutDev(dev.name, dev=dev, tolerance=tolerance, settle=settle, timeout=timeout,
unit=dev.unit, fmtstr=dev.fmtstr)
@usercommand
@helparglist('dev, target[, tolerance[,settle[, timeout]]]')
def maw_tmo(dev, target, tolerance, settle=300, timeout=0):
"""wait for target reached or no progress within timeout
wait for either target within tolerance for a total of <settle> seconds
or no progress more than tolerance for <timeout> seconds
"""
maw(WithTimeout(dev, tolerance, settle, timeout), target)
class ReadonlyParamDevice(paramdev.ReadonlyParamDevice):
"""turn a parameter into a temporary Readable"""
temporary = True
def __init__(self, devname_param):
devname, pname = devname_param.split('.')
paramdev.ReadonlyParamDevice.__init__(self, devname_param, device=devname, parameter=pname)
def _getCache(self):
return None
class ParamDevice(paramdev.ParamDevice):
"""turn a parameter into a temporary Moveable"""
temporary = True
def __init__(self, devname_param):
devname, pname = devname_param.split('.')
paramdev.ParamDevice.__init__(self, devname_param, device=devname, parameter=pname)
def _getCache(self):
return None
@usercommand
@helparglist('"dev.param, dev.param, ..."')
@parallel_safe
def out(*args):
"""may be used as an argument in a scan for producing output columns for parameters
can not be used in SetEnvironment
For a single parameter:
scan(dev, ...., out('tt.raw'))
For multiple parameters:
scan(dev, ...., *out('tt, tt.raw, mf.ramp'))
"""
result = []
for arg in args:
for devpar in arg.split(','):
devpar = devpar.strip()
if '.' in devpar:
result.append(ReadonlyParamDevice(devpar))
else:
result.append(session.devices[devpar])
if len(result) == 1:
return result[0]
return result
@usercommand
@helparglist('"dev.param"')
@parallel_safe
def param(devpar):
"""turning a parameter into a moveable device
Usage:
scan(param(dev), ....)
"""
return ParamDevice(devpar)
def copy_all(srcdir, dstdir):
"""copy all files from srcdir to dstdir"""
files = glob(join(srcdir, '*'))
for src in files:
shutil.copy2(src, dstdir)
return files
@usercommand
@helparglist('script path | instrument, proposal')
@parallel_safe
def copy_scripts(ins_or_dir, proposal=None):
"""copy scripts from another proposal or any directory"""
if os.path.isdir(ins_or_dir):
dirname = ins_or_dir
else:
data = join(os.environ['NICOS_DATA'], ins_or_dir)
if isinstance(proposal, int):
pat = re.compile('.*/(.*\D|)0*%d$' % proposal)
else:
pat = re.compile('.*/%s$' % re.escape(proposal))
for dirname in reversed(sorted(glob(join(data, '20*', '*')))):
if pat.match(dirname):
break
else:
raise FileNotFoundError('directory for %s/%s not found' % (ins_or_dir, proposal))
copy_all(join(dirname, 'scripts'), join(session.devices['Exp'].scriptpath))
def handle_args(args):
detectors = []
put_timestamp = False
for det in session.experiment.detectors:
if isinstance(det, Timestamp) and det.show:
put_timestamp = True
ret = []
moveable_seen = False
for arg in args:
if moveable_seen and isinstance(arg, Readable):
if put_timestamp:
ret.append(TimestampReadable())
put_timestamp = False
#ret.append(arg)
ret.append(Det(arg))
elif isinstance(arg, str) and '.' in arg:
ret.append(param(arg))
moveable_seen = True
elif isinstance(arg, Measurable):
if arg not in session.experiment.detectors:
ret.append(arg)
else:
if isinstance(arg, Moveable):
moveable_seen = True
ret.append(arg)
return ret
@usercommand
def scan(*args, **kwargs):
nicos.commands.scan.scan(*handle_args(args), **kwargs)
scan.help_arglist = nicos.commands.scan.scan.help_arglist
scan.__doc__ = nicos.commands.scan.scan.__doc__
@usercommand
def sweep(dev, start, end, *args, **kwargs):
nicos.commands.scan.sweep(dev, start, end, *handle_args(args), **kwargs)
sweep.help_arglist = nicos.commands.scan.sweep.help_arglist
sweep.__doc__ = nicos.commands.scan.sweep.__doc__
class TimestampReadable(Readable):
"""timestamp as Readable"""
temporary = True
def __init__(self):
Readable.__init__(self, 'timestamp', unit='s')
def _getCache(self):
"""no cache needed"""
self._cache = None
def doRead(self, maxage=0):
return time.time()
def doStatus(self, maxage=0):
return 100, ''
def valueInfo(self):
return Readable.valueInfo(self)

View File

@ -9,7 +9,7 @@ devices = {
description='main SEC node', unit='',
prefix=environ.get('SE_PREFIX', ''), auto_create=True, service='main',
),
'timestamp': device('nicos_sinq.frappy_sinq.lab.Timestamp', description='time, a dummy detector'),
'timestamp': device('nicos_linse.common.lab.Timestamp', description='time, a dummy detector'),
}
startupcode = '''

View File

@ -2,7 +2,7 @@ description = 'timestamp dummy detector for offline measurements'
group = 'optional'
devices = {
'timestamp': device('nicos_sinq.frappy_sinq.lab.Timestamp', description='time, a dummy detector'),
'timestamp': device('nicos_linse.common.lab.Timestamp', description='time, a dummy detector'),
}
startupcode = 'SetDetectors(timestamp)'

View File

@ -1,124 +0,0 @@
# *****************************************************************************
#
# 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>
#
# *****************************************************************************
"""helper devices for lab experiments"""
import time
from nicos.core import Override, Param, Measurable
from nicos.core.params import Value
class Timestamp(Measurable):
"""to be used as a 'detector' in lab experiments
just waiting the preset time
"""
parameter_overrides = {
'unit': Override(default='sec', mandatory=False),
}
parameters = {
'show': Param('show timestamp in data file', type=bool,
settable=True, default=True, mandatory=False),
}
_preset = 0
_time_used = 0
_start = 0
def _getCache(self):
"""no cache needed"""
self._cache = None
def doRead(self, maxage=0):
return time.time()
def doStatus(self, maxage=0):
return 100, ''
def doSetPreset(self, t=0, **preset):
self._start = 0
self._preset = t
self._value = 0
def doStart(self):
self._start = time.time()
def doPause(self):
self._time_used = time.time() - self._start
return True
def doResume(self):
self._start = time.time() - self._time_used
def doFinish(self):
pass
def doStop(self):
pass
def doIsCompleted(self):
return time.time() > self._start + self._preset
def valueInfo(self):
if self.show:
return Measurable.valueInfo(self)
return ()
class Det(Measurable):
"""wrap a Readable into a Detector
just for placing the result in a scan file in the detector part
"""
temporary = True
def __init__(self, dev):
self._dev = dev
Measurable.__init__(self, str(dev), unit=dev.unit, fmtstr=dev.fmtstr)
def _getCache(self):
"""no cache needed"""
self._cache = None
def doRead(self, maxage=0):
return self._dev.doRead(maxage)
def doStatus(self, maxage=0):
return self._dev.doStatus(maxage)
def doSetPreset(self, t=5, **preset):
self._start = 0
self._preset = t
def doStart(self):
self._start = time.time()
def doFinish(self):
pass
def doStop(self):
self._start = 0
def doIsCompleted(self):
return time.time() > self._start + self._preset
def valueInfo(self):
return Value(self.name, unit=self.unit, fmtstr=self.fmtstr),