From 4bc048c064de0061676dbe2987384f3e257ab1fb Mon Sep 17 00:00:00 2001 From: lagier Date: Fri, 28 Feb 2020 07:10:51 +0100 Subject: [PATCH 01/27] renamed id26/calc_controllers into controllers --- id26/{calc_controllers => controllers}/counter_offsets.py | 4 ++-- id26/{calc_controllers => controllers}/monochromator.py | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename id26/{calc_controllers => controllers}/counter_offsets.py (98%) rename id26/{calc_controllers => controllers}/monochromator.py (100%) diff --git a/id26/calc_controllers/counter_offsets.py b/id26/controllers/counter_offsets.py similarity index 98% rename from id26/calc_controllers/counter_offsets.py rename to id26/controllers/counter_offsets.py index 4ca5e8d..67806fa 100644 --- a/id26/calc_controllers/counter_offsets.py +++ b/id26/controllers/counter_offsets.py @@ -7,7 +7,7 @@ from id26.scripts.beamline_parameters import ID26Parameters, switch_instance ''' -package: id26.calc_controllers.counter_offsets +package: id26.controllers.counter_offsets class: ID26FastShutter name: fsh opiom: $opiom_cc1 @@ -97,7 +97,7 @@ config example: outputs: - name: out1 -package: id26.calc_controllers.counter_offsets +package: id26.controllers.counter_offsets class: ID26CounterOffsets name: counter_offset inputs: diff --git a/id26/calc_controllers/monochromator.py b/id26/controllers/monochromator.py similarity index 100% rename from id26/calc_controllers/monochromator.py rename to id26/controllers/monochromator.py -- GitLab From 18675d10f00030a519fe5e76aff9999af74e5508 Mon Sep 17 00:00:00 2001 From: lagier Date: Fri, 28 Feb 2020 07:26:59 +0100 Subject: [PATCH 02/27] moved attenuator and mono_encoders from scripts to controllers --- id26/{scripts => controllers}/attenuator.py | 2 +- id26/{scripts => controllers}/mono_encoders.py | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename id26/{scripts => controllers}/attenuator.py (98%) rename id26/{scripts => controllers}/mono_encoders.py (100%) diff --git a/id26/scripts/attenuator.py b/id26/controllers/attenuator.py similarity index 98% rename from id26/scripts/attenuator.py rename to id26/controllers/attenuator.py index e0fea16..5eaa3ee 100644 --- a/id26/scripts/attenuator.py +++ b/id26/controllers/attenuator.py @@ -2,7 +2,7 @@ from bliss import setup_globals from id26.scripts.beamline_parameters import ID26Parameters, switch_instance ''' -package: id26.scripts.attenuator +package: id26.controllers.attenuator name: att class: ID26attenuator wago: $wcid26c diff --git a/id26/scripts/mono_encoders.py b/id26/controllers/mono_encoders.py similarity index 100% rename from id26/scripts/mono_encoders.py rename to id26/controllers/mono_encoders.py -- GitLab From 46758c22c923c1f8485e283f3df7c38fca9508d2 Mon Sep 17 00:00:00 2001 From: lagier Date: Fri, 28 Feb 2020 10:13:49 +0100 Subject: [PATCH 03/27] general params added --- id26/controllers/parameters.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 id26/controllers/parameters.py diff --git a/id26/controllers/parameters.py b/id26/controllers/parameters.py new file mode 100644 index 0000000..0a82dcb --- /dev/null +++ b/id26/controllers/parameters.py @@ -0,0 +1,19 @@ +from bliss import setup_globals +from bliss.common.standard import info +from id26.scripts.beamline_parameters import ID26Parameters, switch_instance +from id26.scripts.energy import angletoenergy, energytoangle + +class ID26Vars (ID26Parameters): + + def __init__ (self, name, config_dict): + param_name = name + + self.bli = config_dict #['bli'] + _defaults = {} + for item in config_dict['defaults']: + _defaults.update (item.items()) + + super().__init__(param_name, _defaults) + + def __info__(self): + return (info(self.parameters)) -- GitLab From 91ff9050f0134c032e108cb14ae6d07a8063b57c Mon Sep 17 00:00:00 2001 From: lagier Date: Mon, 2 Mar 2020 08:59:16 +0100 Subject: [PATCH 04/27] ID26OffsetShutter class self.name added to ID26FastShutter --- id26/controllers/counter_offsets.py | 118 +++++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 2 deletions(-) diff --git a/id26/controllers/counter_offsets.py b/id26/controllers/counter_offsets.py index 67806fa..61a488f 100644 --- a/id26/controllers/counter_offsets.py +++ b/id26/controllers/counter_offsets.py @@ -1,4 +1,5 @@ from bliss import setup_globals + from id26.scripts.beamline_parameters import ID26Parameters, switch_instance @@ -19,7 +20,8 @@ class ID26FastShutter (ID26Parameters): def __init__ (self, name, config_dict): self.opiom = config_dict['opiom'] self.multiplexer = config_dict['multiplexer'] - + self.name = name + def __info__(self): msg = "Shutter is " + self._fstate() @@ -29,6 +31,7 @@ class ID26FastShutter (ID26Parameters): return(msg) def _fstate(self): + print ('.multiplexer.getOutputStat(\'SOFT_SHUTTER\')',self.multiplexer.getOutputStat('SOFT_SHUTTER')) return (self.multiplexer.getOutputStat('SOFT_SHUTTER')) def open (self): @@ -70,13 +73,124 @@ class ID26FastShutter (ID26Parameters): class ID26OffsetShutter (ID26Parameters): - def __init__ (self): + def __init__ (self, name, config_dict): + param_name = name + _def = {} + _def ['shutter'] = 'fshutter' + _cb = {} + _cb ['shutter'] = self._cb_toggle + + super ().__init__(param_name, _def, callbacks = _cb) + + self.chopper = config_dict ['chopper'] + self.fshutter = config_dict ['fshutter'] + + self._cb_toggle (self.parameters.shutter) + + def __info__(self): + return "\n"+self.parameters.__info__() + + def _cb_toggle (self, shu): + if shu == 'chopper': + self._off_shu = ID26OffsetShutter1 (self.chopper) + elif shu == 'fshutter': + self._off_shu = ID26OffsetShutter2 (self.fshutter) + else: + print ('shutter nust be one of : [chopper, fshutter]') + return self.parameters.shutter + + def open (self): + self._off_shu.open() + + def close (self): + self._off_shu.close() + + def state (self): + return self._off_shu.state() + #return 'OPEN' or 'CLOSE' + + def is_open (self): + return True if self._off_shu.state() == 'OPEN' else False + + def is_closed (self): + return True if self._off_shu.state() == 'CLOSE' else False + + +class ID26OffsetShutter1: + + def __init__(self, chopper): + self.chopp = chopper + + def open (self): + print ('chopper used',self.chopp.name) + #self.chopp.move(value) + + def close (self): + print ('chopper used') + #self.chopp.move(0) + + def state (self): + print ('chopper used') + return 'CLOSE' if self.chopp.position == 0 else 'OPEN' + + +class ID26OffsetShutter2: + + def __init__(self, shutter_info): + + for f in shutter_info: + self.f=f + keys = f.keys() + if 'fshutter' in keys: + self.shutter = f['fshutter'] + if 'attenuator' in keys: + self.atten = f['attenuator'] + + + def open (self): + print ('opening', self.shutter.name) + self.shutter.open() + print ('opening', self.atten.name) + self.atten.factor(63) + + def close (self): + print ('closing', self.shutter.name) + self.shutter.close() + print ('closing', self.atten.name) + self.atten.factor(0) + + def state (self): + status1 = self.shutter._fstate() + + status2 = 'CLOSE' if self.atten.factor() == 0 else 'OPEN' + status2 = 'OPEN' if self.atten.factor() == 63 else 'CLOSE' + + print (self.shutter.name, 'status:', status1) + print (self.atten.name, 'status:', status2) + + if status1 == 'CLOSE' and status2 == 'CLOSE': + return 'CLOSE' + if status1 == 'OPEN' and status2 == 'OPEN': + return 'OPEN' + return 'FAULT' + + + +class ID26OffsetShutter3: + + def __init__(self, *args, **kwargs): pass def open (self): + print ('not implemented') pass def close (self): + print ('not implemented') + pass + + def state (self): + print ('not implemented') pass -- GitLab From f0138fb4d8e5ad858b89b23ecac1051ad3523244 Mon Sep 17 00:00:00 2001 From: lagier Date: Mon, 2 Mar 2020 08:59:30 +0100 Subject: [PATCH 05/27] self.name added to attenuators --- id26/controllers/attenuator.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/id26/controllers/attenuator.py b/id26/controllers/attenuator.py index 5eaa3ee..a72b256 100644 --- a/id26/controllers/attenuator.py +++ b/id26/controllers/attenuator.py @@ -33,7 +33,8 @@ class Id26Attenuator (ID26Parameters): def __init__ (self, name, config_dict): self.wago = config_dict['wago'] self.filters = [] - + self.name = name + for f in config_dict['filters']: wagokey, desc, attenuation = f.keys() self.filters.append([f[wagokey],f[attenuation],f[desc]]) -- GitLab From 8e442f4dd09c6d0017273e1db98ab796b6012556 Mon Sep 17 00:00:00 2001 From: lagier Date: Mon, 2 Mar 2020 09:00:00 +0100 Subject: [PATCH 06/27] DarkCounter from BM23 fixed --- id26/controllers/BM23CalcCounters.py | 72 ++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 id26/controllers/BM23CalcCounters.py diff --git a/id26/controllers/BM23CalcCounters.py b/id26/controllers/BM23CalcCounters.py new file mode 100644 index 0000000..782d747 --- /dev/null +++ b/id26/controllers/BM23CalcCounters.py @@ -0,0 +1,72 @@ + +from bliss.config import settings +from bliss.common.scans import ct +from bliss.controllers.counter import CalcCounterController + +class DarkCounter(CalcCounterController): + + def __init__(self, name, config): + + self.dark_object = config.get("openclose") + + self.background = 0.0 + self.type = config.get('type') + CalcCounterController.__init__(self, name, config) + + self.dark_setting_name = f"{self.name}_dark_setting" + self.dark_setting = settings.HashSetting(self.dark_setting_name) + + def get_default_chain_parameters(self, scan_params, acq_params): + self.scan_params = scan_params + return acq_params + + def is_integrating_counter(self, cnt): + return True if self.type == 'integrating' else False + + + def get_input_counter_from_tag(self, tag): + for cnt in self.inputs: + if self.tags[cnt.name] == tag: + return cnt + + return None + + def take_background(self): + + # Close beam + dark_object_state = self.dark_object.state() + self.dark_object.close() + + if self.dark_object.is_closed(): + scan_ct = ct(1, tuple(self.inputs), run=False) + scan_ct._data_watch_callback = None + scan_ct.run() + + data_background = scan_ct.get_data() + + for cnt in self.inputs: + tag = self.tags[cnt.name] + print ('Offset {0}: {1}'.format(cnt.name, data_background[cnt.name][0])) + self.dark_setting[tag] = data_background[cnt.name][0] + + if dark_object_state == "OPEN": + self.dark_object.open() + + else: + print("Close functions did not succeed, Backgrounds have not been changed !!!") + + def calc_function(self, input_dict): + print(f"{self.get_current_parameters()}") + value = {} + #print(input_dict) + for tag in input_dict.keys(): + cnt = self.get_input_counter_from_tag(tag) + #print(tag,cnt) + if self.is_integrating_counter(cnt): + #print(f"---- {cnt.name} -- INTEGRATING") + value[tag] = input_dict[tag] - self.dark_setting[tag] * self.scan_params["count_time"] + else: + #print(f"---- {cnt.name} -- NOT INTEGRATING") + value[tag] = input_dict[tag] - self.dark_setting[tag] + + return value -- GitLab From 3e42f00d0dfe5413ac09b98b62c546402e8ddc3c Mon Sep 17 00:00:00 2001 From: bliss administrator Date: Mon, 2 Mar 2020 13:21:28 +0100 Subject: [PATCH 07/27] ID26OffsetShutter fix --- id26/controllers/counter_offsets.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/id26/controllers/counter_offsets.py b/id26/controllers/counter_offsets.py index 61a488f..a0e1478 100644 --- a/id26/controllers/counter_offsets.py +++ b/id26/controllers/counter_offsets.py @@ -91,13 +91,16 @@ class ID26OffsetShutter (ID26Parameters): return "\n"+self.parameters.__info__() def _cb_toggle (self, shu): + + #print ('in cb', shu) if shu == 'chopper': self._off_shu = ID26OffsetShutter1 (self.chopper) elif shu == 'fshutter': self._off_shu = ID26OffsetShutter2 (self.fshutter) else: - print ('shutter nust be one of : [chopper, fshutter]') + print ('shutter nust be one of : [\'chopper\', \'fshutter\']') return self.parameters.shutter + return shu def open (self): self._off_shu.open() @@ -122,16 +125,16 @@ class ID26OffsetShutter1: self.chopp = chopper def open (self): - print ('chopper used',self.chopp.name) - #self.chopp.move(value) + #print ('chopper used',self.chopp.name) + self.chopp.move(1) def close (self): - print ('chopper used') - #self.chopp.move(0) + #print ('chopper used') + self.chopp.move(0) def state (self): - print ('chopper used') - return 'CLOSE' if self.chopp.position == 0 else 'OPEN' + #print ('chopper used') + return 'CLOSE' if self.chopp.position < 0.2 else 'OPEN' class ID26OffsetShutter2: -- GitLab From 1993aafb612592911718ca8ebb08f23a8d6f08ba Mon Sep 17 00:00:00 2001 From: bliss administrator Date: Wed, 4 Mar 2020 12:27:58 +0100 Subject: [PATCH 08/27] offset counters with new bliss and bm23 darkcounter latest version that I also modified --- id26/controllers/BM23CalcCounters.py | 30 ++++--- id26/controllers/BM23CalcCounters.py.mercredi | 78 +++++++++++++++++++ .../{counter_offsets.py => auto_offsets.py} | 72 +---------------- id26/controllers/fast_shutter.py | 75 ++++++++++++++++++ 4 files changed, 176 insertions(+), 79 deletions(-) create mode 100644 id26/controllers/BM23CalcCounters.py.mercredi rename id26/controllers/{counter_offsets.py => auto_offsets.py} (78%) create mode 100644 id26/controllers/fast_shutter.py diff --git a/id26/controllers/BM23CalcCounters.py b/id26/controllers/BM23CalcCounters.py index 782d747..b01a75f 100644 --- a/id26/controllers/BM23CalcCounters.py +++ b/id26/controllers/BM23CalcCounters.py @@ -1,7 +1,9 @@ from bliss.config import settings + from bliss.common.scans import ct from bliss.controllers.counter import CalcCounterController +from bliss.common.counter import IntegratingCounter class DarkCounter(CalcCounterController): @@ -10,19 +12,26 @@ class DarkCounter(CalcCounterController): self.dark_object = config.get("openclose") self.background = 0.0 - self.type = config.get('type') CalcCounterController.__init__(self, name, config) self.dark_setting_name = f"{self.name}_dark_setting" self.dark_setting = settings.HashSetting(self.dark_setting_name) + def get_default_chain_parameters(self, scan_params, acq_params): self.scan_params = scan_params return acq_params def is_integrating_counter(self, cnt): - return True if self.type == 'integrating' else False - + info_str = cnt.__info__() + for info in info_str.split(): + if info.find("counter type") != -1: + if info.find("integrating") != -1: + return True + else: + return False + + return False def get_input_counter_from_tag(self, tag): for cnt in self.inputs: @@ -31,14 +40,14 @@ class DarkCounter(CalcCounterController): return None - def take_background(self): + def take_background(self, time=1.0): # Close beam dark_object_state = self.dark_object.state() self.dark_object.close() if self.dark_object.is_closed(): - scan_ct = ct(1, tuple(self.inputs), run=False) + scan_ct = ct(time, self.inputs, run=False) scan_ct._data_watch_callback = None scan_ct.run() @@ -46,9 +55,10 @@ class DarkCounter(CalcCounterController): for cnt in self.inputs: tag = self.tags[cnt.name] - print ('Offset {0}: {1}'.format(cnt.name, data_background[cnt.name][0])) self.dark_setting[tag] = data_background[cnt.name][0] - + + self.dark_setting['dark_time'] = time + if dark_object_state == "OPEN": self.dark_object.open() @@ -58,13 +68,11 @@ class DarkCounter(CalcCounterController): def calc_function(self, input_dict): print(f"{self.get_current_parameters()}") value = {} - #print(input_dict) for tag in input_dict.keys(): cnt = self.get_input_counter_from_tag(tag) - #print(tag,cnt) - if self.is_integrating_counter(cnt): + if isinstance(cnt, IntegratingCounter): #print(f"---- {cnt.name} -- INTEGRATING") - value[tag] = input_dict[tag] - self.dark_setting[tag] * self.scan_params["count_time"] + value[tag] = input_dict[tag] - self.dark_setting[tag] * self.scan_params["count_time"] / self.dark_setting['dark_time'] else: #print(f"---- {cnt.name} -- NOT INTEGRATING") value[tag] = input_dict[tag] - self.dark_setting[tag] diff --git a/id26/controllers/BM23CalcCounters.py.mercredi b/id26/controllers/BM23CalcCounters.py.mercredi new file mode 100644 index 0000000..b9fdf02 --- /dev/null +++ b/id26/controllers/BM23CalcCounters.py.mercredi @@ -0,0 +1,78 @@ + +from bliss.config import settings +from bliss.common.scans import ct +from bliss.controllers.counter import CalcCounterController +from bliss.common.counter import IntegratingCounter + +class DarkCounter(CalcCounterController): + + def __init__(self, name, config): + + self.dark_object = config.get("openclose") + + self.background = 0.0 + CalcCounterController.__init__(self, name, config) + + self.dark_setting_name = f"{self.name}_dark_setting" + self.dark_setting = settings.HashSetting(self.dark_setting_name) + + def get_default_chain_parameters(self, scan_params, acq_params): + self.scan_params = scan_params + return acq_params + + def is_integrating_counter(self, cnt): + info_str = cnt.__info__() + for info in info_str.split(): + if info.find("counter type") != -1: + if info.find("integrating") != -1: + return True + else: + return False + + return False + + def get_input_counter_from_tag(self, tag): + for cnt in self.inputs: + if self.tags[cnt.name] == tag: + return cnt + + return None + + def take_background(self, time=1.0): + + # Close beam + dark_object_state = self.dark_object.state() + self.dark_object.close() + + if self.dark_object.is_closed(): + scan_ct = ct(time, self.inputs, run=False) + scan_ct._data_watch_callback = None + scan_ct.run() + + data_background = scan_ct.get_data() + + for cnt in self.inputs: + tag = self.tags[cnt.name] + self.dark_setting[tag] = data_background[cnt.name][0] + + self.dark_time = time + + if dark_object_state == "OPEN": + self.dark_object.open() + + else: + print("Close functions did not succeed, Backgrounds have not been changed !!!") + + def calc_function(self, input_dict): + print(f"{self.get_current_parameters()}") + value = {} + for tag in input_dict.keys(): + cnt = self.get_input_counter_from_tag(tag) + if isinstance(cnt, IntegratingCounter): + print(f"---- {cnt.name} -- INTEGRATING") + value[tag] = input_dict[tag] - self.dark_setting[tag] * self.scan_params["count_time"] / self.dark_time + else: + print(f"---- {cnt.name} -- NOT INTEGRATING") + value[tag] = input_dict[tag] - self.dark_setting[tag] + + return value diff --git a/id26/controllers/counter_offsets.py b/id26/controllers/auto_offsets.py similarity index 78% rename from id26/controllers/counter_offsets.py rename to id26/controllers/auto_offsets.py index a0e1478..1e50c26 100644 --- a/id26/controllers/counter_offsets.py +++ b/id26/controllers/auto_offsets.py @@ -7,70 +7,6 @@ from id26.scripts.beamline_parameters import ID26Parameters, switch_instance # Out [10]: {'ACQ_TRIG': ['COUNT', 'ZAP'], 'SOFT_SHUTTER': ['CLOSE', 'OPEN']} -''' -package: id26.controllers.counter_offsets -class: ID26FastShutter -name: fsh -opiom: $opiom_cc1 -multiplexer: multiplexer_opiom_cc1 -''' - -class ID26FastShutter (ID26Parameters): - - def __init__ (self, name, config_dict): - self.opiom = config_dict['opiom'] - self.multiplexer = config_dict['multiplexer'] - self.name = name - - - def __info__(self): - msg = "Shutter is " + self._fstate() - msg += "\nAutomatic opening during counting: " -# msg += 'ON' if self.opiom.registers()['IMA']&0xe else 'OFF' - msg += 'ON' if self.opiom.registers()['IMA']&0x2 else 'OFF' - return(msg) - - def _fstate(self): - print ('.multiplexer.getOutputStat(\'SOFT_SHUTTER\')',self.multiplexer.getOutputStat('SOFT_SHUTTER')) - return (self.multiplexer.getOutputStat('SOFT_SHUTTER')) - - def open (self): - self.multiplexer.switch('soft_shutter','open') - - def close (self): - self.multiplexer.switch('soft_shutter','close') - - def open_fast (self): - if self.opiom.comm_ack('IMA 0x01 0x01') != 'OK': - raise RuntimeError ('Bad answer from opiom') - - def close_fast (self): - if self.opiom.comm_ack('IMA 0x00 0x01') != 'OK': - raise RuntimeError ('Bad answer from opiom') - - def on (self): - ''' - Activates the open/close of shutter before/after counting - "IMA 0x%02x 0x0e", mode << 1 ; mode = 1 - "IMA 0x%02x 0x02", mode << 1 ; mode = 1 is enough - the opiom is not programmed for that yet !!! - ''' - if 1: - print ("Would activate the open/close of shutter before/after counting") - print ("But the OPIOM IS NOT READY FOR THAT ...") - elif self.opiom.comm_ack('IMA 0x{0:02x} 0x0e'.format(1<<1)) != 'OK': - raise RuntimeError ('Bad answer from opiom') - - def off (self): - ''' - Activates the open/close of shutter before/after counting - "IMA 0x%02x 0x0e", mode << 1 ; mode = 0 - the opiom is not programmed for that yet !!! - ''' - if 0: - if self.opiom.comm_ack('IMA 0x{0:02x} 0x0e'.format(0<<1)) != 'OK': - raise RuntimeError ('Bad answer from opiom') - class ID26OffsetShutter (ID26Parameters): def __init__ (self, name, config_dict): @@ -94,9 +30,9 @@ class ID26OffsetShutter (ID26Parameters): #print ('in cb', shu) if shu == 'chopper': - self._off_shu = ID26OffsetShutter1 (self.chopper) + self._off_shu = ID26OffsetShutterChopper (self.chopper) elif shu == 'fshutter': - self._off_shu = ID26OffsetShutter2 (self.fshutter) + self._off_shu = ID26OffsetShutterFShAtt (self.fshutter) else: print ('shutter nust be one of : [\'chopper\', \'fshutter\']') return self.parameters.shutter @@ -119,7 +55,7 @@ class ID26OffsetShutter (ID26Parameters): return True if self._off_shu.state() == 'CLOSE' else False -class ID26OffsetShutter1: +class ID26OffsetShutterChopper: def __init__(self, chopper): self.chopp = chopper @@ -137,7 +73,7 @@ class ID26OffsetShutter1: return 'CLOSE' if self.chopp.position < 0.2 else 'OPEN' -class ID26OffsetShutter2: +class ID26OffsetShutterFShAtt: def __init__(self, shutter_info): diff --git a/id26/controllers/fast_shutter.py b/id26/controllers/fast_shutter.py new file mode 100644 index 0000000..27c5162 --- /dev/null +++ b/id26/controllers/fast_shutter.py @@ -0,0 +1,75 @@ +from bliss import setup_globals + +from id26.scripts.beamline_parameters import ID26Parameters, switch_instance + + +#TEST_ZAP [10]: multiplexer_opiom_cc1.getAllPossibleValues() +# Out [10]: {'ACQ_TRIG': ['COUNT', 'ZAP'], 'SOFT_SHUTTER': ['CLOSE', 'OPEN']} + + +''' +package: id26.controllers.fast_shutter +class: ID26FastShutter +name: fsh +opiom: $opiom_cc1 +multiplexer: multiplexer_opiom_cc1 +''' + +class ID26FastShutter (ID26Parameters): + + def __init__ (self, name, config_dict): + self.opiom = config_dict['opiom'] + self.multiplexer = config_dict['multiplexer'] + self.name = name + + + def __info__(self): + msg = "Shutter is " + self._fstate() + msg += "\nAutomatic opening during counting: " +# msg += 'ON' if self.opiom.registers()['IMA']&0xe else 'OFF' + msg += 'ON' if self.opiom.registers()['IMA']&0x2 else 'OFF' + return(msg) + + def _fstate(self): + print ('.multiplexer.getOutputStat(\'SOFT_SHUTTER\')',self.multiplexer.getOutputStat('SOFT_SHUTTER')) + return (self.multiplexer.getOutputStat('SOFT_SHUTTER')) + + def open (self): + self.multiplexer.switch('soft_shutter','open') + + def close (self): + self.multiplexer.switch('soft_shutter','close') + + def open_fast (self): + if self.opiom.comm_ack('IMA 0x01 0x01') != 'OK': + raise RuntimeError ('Bad answer from opiom') + + def close_fast (self): + if self.opiom.comm_ack('IMA 0x00 0x01') != 'OK': + raise RuntimeError ('Bad answer from opiom') + + def on (self): + ''' + Activates the open/close of shutter before/after counting + "IMA 0x%02x 0x0e", mode << 1 ; mode = 1 + "IMA 0x%02x 0x02", mode << 1 ; mode = 1 is enough + the opiom is not programmed for that yet !!! + ''' + if 1: + print ("Would activate the open/close of shutter before/after counting") + print ("But the OPIOM IS NOT READY FOR THAT ...") + elif self.opiom.comm_ack('IMA 0x{0:02x} 0x0e'.format(1<<1)) != 'OK': + raise RuntimeError ('Bad answer from opiom') + + def off (self): + ''' + Activates the open/close of shutter before/after counting + "IMA 0x%02x 0x0e", mode << 1 ; mode = 0 + the opiom is not programmed for that yet !!! + ''' + if 0: + if self.opiom.comm_ack('IMA 0x{0:02x} 0x0e'.format(0<<1)) != 'OK': + raise RuntimeError ('Bad answer from opiom') + + + -- GitLab From 0b914af7cf9053fe6d43ec24911d42c3422083cc Mon Sep 17 00:00:00 2001 From: bliss administrator Date: Wed, 4 Mar 2020 17:23:01 +0100 Subject: [PATCH 09/27] ID26Monochromator monox buggy motion removed --- id26/controllers/monochromator.py | 41 +++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/id26/controllers/monochromator.py b/id26/controllers/monochromator.py index 5baac38..042956c 100644 --- a/id26/controllers/monochromator.py +++ b/id26/controllers/monochromator.py @@ -22,6 +22,7 @@ controller: """ import math +import numpy from bliss import setup_globals from bliss.config import settings @@ -96,6 +97,7 @@ class ID26Monochromator(CalcController, ID26Parameters): def calc_from_real(self, positions_dict): + #print (positions_dict) theta = positions_dict["theta"] self._add ('monox_oldpos', positions_dict["monox"], nosetup = True) self.parameters.monox_oldpos = positions_dict["monox"] @@ -106,11 +108,18 @@ class ID26Monochromator(CalcController, ID26Parameters): def calc_to_real(self, positions_dict): + #print (positions_dict) + #positions_dict can be an array ...so energy, theta and monox as well ... + #energy = positions_dict["calc"] + + #energy = numpy.array(positions_dict["calc"]) energy = positions_dict["calc"] + theta = self.crystal_plane.bragg_angle(energy * ur.keV).to(ur.deg).magnitude - monox = self._monox_position(energy) + #monox = self._monox_position(energy) - return {"theta": theta, "monox": monox} + #return {"theta": theta, "monox": monox} + return {"theta": theta} def _monox_dpos(self): return (self.parameters.monox_de / ( @@ -120,17 +129,33 @@ class ID26Monochromator(CalcController, ID26Parameters): def _monox_position(self, energy): # the position of monox is defined as the center of the second crystal - + # as positions_dict can be an array ...energy can as well, so _newpos and _new_pos ... + # energy IS ALWAYS AN ARRAY NOW HERE + _newpos = (self.mono_vertical_shift * energy * self.g_mo_d) / HC_OVER_E _limits = self._tagged["monox"][0].limits - if abs(self.parameters.monox_oldpos - _newpos) > self._monox_dpos(): - _new_pos = _limits[1] if _newpos > _limits[1] else _newpos - _new_pos = _limits[0] if _newpos < _limits[0] else _new_pos - return _new_pos + #print ('energy',energy) + #print ('newpos',_newpos) + + _newpos = list(map (lambda x:min(_limits[1],x),_newpos)) + _newpos = list(map (lambda x:max(_limits[0],x),_newpos)) + + _val = self._monox_dpos() + _old = self.parameters.monox_oldpos + + _newpos = list(map (lambda x:x if abs(_old-x)>_val else _old,_newpos)) + + + return numpy.array(_newpos) +''' + if (abs(self.parameters.monox_oldpos - _newpos) > self._monox_dpos()).any(): + _new_pos = list(map (lambda x:min(_limits[1],x),_newpos)) + _new_pos = list(map (lambda x:max(_limits[0],x),_new_pos)) + return numpy.array(_new_pos) else: return self.parameters.monox_oldpos - +''' """ SPEC -- GitLab From 641f34d50671105cc6d63a7ff69aeb103c7f1c37 Mon Sep 17 00:00:00 2001 From: bliss administrator Date: Thu, 5 Mar 2020 18:18:03 +0100 Subject: [PATCH 10/27] ID26OffsetShutterChopper modified to move it several times --- id26/controllers/auto_offsets.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/id26/controllers/auto_offsets.py b/id26/controllers/auto_offsets.py index 1e50c26..0ae2152 100644 --- a/id26/controllers/auto_offsets.py +++ b/id26/controllers/auto_offsets.py @@ -61,12 +61,21 @@ class ID26OffsetShutterChopper: self.chopp = chopper def open (self): - #print ('chopper used',self.chopp.name) + print ('openning chopper ',self.chopp.name) self.chopp.move(1) + for i in range(3): + if self.state() is not 'OPEN': + print ('openning chopper ',self.chopp.name) + self.chopp.move(1) def close (self): - #print ('chopper used') + + print ('closing chopper ',self.chopp.name) self.chopp.move(0) + for i in range(3): + if self.state() is not 'CLOSE': + print ('closing chopper ',self.chopp.name) + self.chopp.move(0) def state (self): #print ('chopper used') -- GitLab From 6a494e7e72b641db75704f60101496f4e03acb8c Mon Sep 17 00:00:00 2001 From: bliss administrator Date: Thu, 5 Mar 2020 18:18:29 +0100 Subject: [PATCH 11/27] new ID26SumCounter CalcCounter --- id26/controllers/calc_counters.py | 33 +++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 id26/controllers/calc_counters.py diff --git a/id26/controllers/calc_counters.py b/id26/controllers/calc_counters.py new file mode 100644 index 0000000..e623acc --- /dev/null +++ b/id26/controllers/calc_counters.py @@ -0,0 +1,33 @@ +from bliss.controllers.counter import CalcCounterController + + +class ID26SumCounter (CalcCounterController):#, ID26Parameters): + + def __init__ (self, name, config_dict): + + CalcCounterController.__init__(self, name, config_dict) + + ''' + param_name = 'calc_counters_parameters' + defaults = {} + + ID26Parameters.__init__(self, + param_name, + defaults, + ) + ''' + + def calc_function(self, input_dict): + csum=0 + for cnt in self.inputs: + csum += input_dict[self.tags[cnt.name]] + + #csum = csum / float(len(self.inputs)) + + + return {self.tags[self.outputs[0].name]: csum} + #print ('inputs',self.inputs) + #print ('tagss',self.tags) + #print ('counter',self.counter) + #print ('outputs',self.outputs) + -- GitLab From d68ef35f90be609302d36b58e24abd66f028ee64 Mon Sep 17 00:00:00 2001 From: bliss administrator Date: Thu, 5 Mar 2020 18:19:16 +0100 Subject: [PATCH 12/27] beamline_parameters.py method resert_parameters renamed _reset_parameters --- id26/scripts/beamline_parameters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/id26/scripts/beamline_parameters.py b/id26/scripts/beamline_parameters.py index e3322e4..87e295a 100644 --- a/id26/scripts/beamline_parameters.py +++ b/id26/scripts/beamline_parameters.py @@ -125,7 +125,7 @@ class ID26Parameters: @switch_instance - def reset_parameters(self): + def _reset_parameters(self): """ Deletes all parameters of the object from Redis to re-start with the default values -- GitLab From 846851c47f266cb2f27df62b2560963239046f69 Mon Sep 17 00:00:00 2001 From: bliss administrator Date: Mon, 9 Mar 2020 17:40:49 +0100 Subject: [PATCH 13/27] id26moco class --- id26/controllers/id26moco.py | 118 +++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 id26/controllers/id26moco.py diff --git a/id26/controllers/id26moco.py b/id26/controllers/id26moco.py new file mode 100644 index 0000000..ce75af7 --- /dev/null +++ b/id26/controllers/id26moco.py @@ -0,0 +1,118 @@ +import gevent +import math + +from bliss.controllers.moco import Moco +from bliss.config import static +from bliss import setup_globals + +class id26moco(Moco): + + def __init__(self, name, config_tree): + + Moco.__init__(self, name, config_tree) + self.MOCO_MAX = 2 + self.PERCENTAGE = 5 + + self.set_voltage() + + def set_voltage(self): + + self.outbeam("VOLT", "NORM", "UNIP", 10, "NOAUTO", silent=True) + self.slope = 1 + self.phase = 150 + self.tau = 0.1 + + print("MoCo box outbeam set to voltage.") + print(f"Slope set to {self.slope} and phase {self.phase}. You may check this using moco_scan(phase).") + + self.MOCO_MAX = 2 + + def moco_set_current(self): + + self.outbeam("CURR", "NORM", "UNIP", 5e-6, "NOAUTO") + self.slope = 1e-6 + self.phase = 120 + self.tau = 0.1 + + print("MoCo box outbeam set to current.") + print(f"Slope set to {self.slope} and phase {self.phase}. You may check this using moco_scan(phase).") + + self.MOCO_MAX = 1e-6 + + def start_regul(self, percent=None): + + if percent is None: + percent = self.PERCENTAGE + + self.stop() + + moco_amp = self.set_amp(percent) + + _aux = (moco_amp/(percent/100)) # this is the FWHM of the RC in units of qgths + + if _aux == 0: + print("Error in moco_set, _aux ==0 moco_slope not properly calculated") + return + else: + moco_slope = (self.MOCO_MAX/ _aux) /10 + + self.slope = moco_slope + print(f"Moco slope set to {moco_slope}.\n") + + self.go() + + print("MoCo feedback started.") + print("Waiting for feedback to reach maximum...") + + gevent.sleep(5) + + def set_amp(self, percent=None): + + if percent is None: + percent = self.PERCENTAGE + + moco_amp = self.calc_amp(percent) + self.amplitude = moco_amp + print(f"MoCo box amplitude set. (moco_amp={moco_amp}) ") + + return moco_amp + + def calc_amp(self, percent=None): + + if percent is None: + percent = self.PERCENTAGE + + energy = setup_globals.energy + g_mo_d = energy.controller.parameters.g_mo_d + + if g_mo_d > 3.0: + # energy resolution + resolution = 7000.0 + elif (1.5 < g_mo_d) and (g_mo_d < 3): + # energy resolution + resolution=35000.0 + elif g_mo_d < 1.5: + # energy resolution + resolution = 70000.0 + + ene_pos = energy.position + bragg_pos = energy.controller.calc_to_real({"calc": ene_pos})["theta"] + winkel = math.radians(bragg_pos) + + try: + delta_bragg = ((1.0 / resolution) * math.sin(winkel)) / math.cos(winkel) + print(f"delta_bragg = {delta_bragg}") + except: + raise RuntimeError(f"MOCO ({self.name}: in calc_amp: Division by 0") + + # conversion factor for qgth2 to microrad + delta_qgth2 = delta_bragg * 1000000.0 / 68.2875 + rc_fwhm = delta_bragg * 1000000.0 * math.sqrt(2.0) + moco_amp = delta_qgth2 * math.sqrt(2.0) * percent / 100.0 + + print(f"I assume a resolving power of {resolution}") + print(f"At {ene_pos} KeV ({bragg_pos} degrees) the rc FWHM is {rc_fwhm} microrad and {delta_qgth2*math.sqrt(2)} in units of qgth2.") + print(f"I determine a MoCo amplitude at {percent} percent of the FWHM, i.e. around {moco_amp}") + + return moco_amp + -- GitLab From e583fdd0ba86a19b4a4c5d8deb8cc385b70da1f2 Mon Sep 17 00:00:00 2001 From: lagier Date: Tue, 10 Mar 2020 16:37:56 +0100 Subject: [PATCH 14/27] fscan.py prepared for testing on bassoon: zaptime and fscan provided... --- id26/scripts/fscan.py | 477 ++++++++++++++++++------------------------ 1 file changed, 199 insertions(+), 278 deletions(-) diff --git a/id26/scripts/fscan.py b/id26/scripts/fscan.py index 09531af..bf80654 100644 --- a/id26/scripts/fscan.py +++ b/id26/scripts/fscan.py @@ -2,9 +2,12 @@ import numpy import datetime import time +from bliss.scanning.scan import Scan, ScanState, DataWatchCallback from bliss.scanning.chain import AcquisitionChain, ChainPreset + + + from bliss.scanning.toolbox import ChainBuilder -from bliss.scanning.scan import Scan, ScanState, DataWatchCallback from bliss.scanning.acquisition.musst import MusstAcquisitionMaster, MusstAcquisitionSlave from bliss.common.motor_group import TrajectoryGroup @@ -16,7 +19,8 @@ from bliss.data.node import is_zerod from bliss import setup_globals from id26.musst_prg.zap_musst_prg import zap_musst_program_data -from id26.scripts.fscan_pm600_prog import pm600_prog_mono_trajectory, pm600_prog_white_opiom_trig +from id26.scripts.fscan_pm600_prog import pm600_prog_mono_trajectory, pm600_prog_white_opiom_trig +from id26.controllers.BM23CalcCounters import DarkCounter from id26.scripts.energy import stepstoenergy , angletoenergy #TODO from id26.opiom1_multiplexer import ZapMultiplexerPreset @@ -25,10 +29,21 @@ from id26.scripts.energy import stepstoenergy , angletoenergy #TODO define id26 global settings : zap.id_linked # if id_linked no zingzing, nscans=1 +# +# GLOBALS +# + +MONO_PM600 = setup_globals.mono +ENERGY = setup_globals.energy +ENC_PM = setup_globals.musst_enc_pm +ENC_HDH = setup_globals.musst_enc_hdh +MUSST = setup_globals.musst_cc1 +OPIOM = setup_globals.multiplexer_opiom_cc1 + + def testmc(): pass -def fscan (energy_start, energy_end, duration, energy_step, *counters, nscans=1, nintervals=10, backlash=20000, save=True ): # ID26CTL> fscan2 A[energy]-0.005 A[energy]+0.005 10 0.002 # $1 : energy_start in keV: : A[energy]-0.005 # $2 : energy_end : : A[energy]+0.005 @@ -38,14 +53,48 @@ def fscan (energy_start, energy_end, duration, energy_step, *counters, nscans=1, # $6 : [nintervals] : 10 # $7 : [backlash] : 20000 - print ("Running fscan {0}\n".format(fscan.__module__)) + +class ZapMultiplexerPreset (ChainPreset): + def __init__(self, multiplexer): + ChainPreset.__init__(self) + self.multiplexer = multiplexer + + + def prepare(self, acq_chain): + pass + + def start(self, acq_chain): + print ('OPIOM switches to ZAP mode') + self.multiplexer.switch('ACQ_TRIG','ZAP') + + def stop(self, acq_chain): + print ('OPIOM switches to COUNT mode') + self.multiplexer.switch('ACQ_TRIG','COUNT') + + +def fscan (energy_start, energy_end, duration, energy_step, *counters, nscans=1, nintervals=10, backlash=20000, save=True ): + """ + Start hardware triggered time scan on detectors with MUSST card as the master, but itself triggered by the PM600 controller. + To select the detectors, enable/disable them using the ACTIVE_MG measurement group. + Args: + energy_start: + energy_end: + duration: + energy_step: + nscans: + nintervals: + backlash: + save: choose to save or not the data [default: True] + Returns: + Scan: the scan handler + """ + print ("Running fscan {0}\n".format(fscan.__module__)) print ("Initialising {0}\n".format((energy_start, energy_end, duration, energy_step))) - energy_start, nbpoints, exp_time = fscan_initialize (energy_start, energy_end, duration, energy_step) + energy_start, nbpoints, exp_time = fscan_energy_start (energy_start, energy_end, duration, energy_step) -# print ("Initialised with energy_start {0} nbpoints {1} and exp_time {2}\n".format(energy_start, nbpoints, exp_time))) - print ("Initialised with energy_start ",energy_start,"nbpoints ",nbpoints,"and exp_time", exp_time,"\n") + print ("Initialised with energy_start: {0}, nbpoints:, {1} and exp_time: {2}\n".format(energy_start, nbpoints, exp_time)) if nscans > 1: raise RuntimeError ("nscans over 1 not implemented yet") @@ -56,23 +105,23 @@ def fscan (energy_start, energy_end, duration, energy_step, *counters, nscans=1, chain = fscan_build_acquisition_chain (exp_time, 0, nbpoints, *counters) - print ("Build chain {0}\n".format(chain)) + print ("Built chain {0}\n".format(chain)) print ("Programming trajectories {0}\n".format((energy_start, energy_end, duration, energy_step, nscans, nintervals, backlash))) #WHITE comment below - #group = fscan_program_trajectories (energy_start, energy_end, duration, energy_step, nscans, nintervals, backlash) + group = fscan_program_trajectories (energy_start, energy_end, duration, energy_step, nscans, nintervals, backlash) #WHITE uncomment: - start_cmd = pm600_prog_white_opiom_trig (setup_globals.mono) + #start_cmd = pm600_prog_white_opiom_trig (MONO_PM600) #WHITE uncomment: - setup_globals.mono.controller.raw_write_read(start_cmd) #1XS2 + #MONO_PM600.controller.raw_write_read(start_cmd) #1XS2 - - #group.move_to_start() -> unused (pm600, IDs) motors already moved at initialize phase to have precise step0/energy_start + #group.move_to_start() -> unused (pm600, IDs) motors already moved at fscan_energy_start phase to have precise step0/energy_start #WHITE comment below - #REAL uncomment but then will be MOVING!!! group.move_to_end() #-> calls prepare_move(finalpos);pm600.start_trajectory;pm600.stop_trajectory; -> 1XS2 + #REAL uncomment but then will be MOVING!!! + group.move_to_end() #-> calls prepare_move(finalpos);pm600.start_trajectory;pm600.stop_trajectory; -> 1XS2 #saving_parameters = setup_globals.SCAN_SAVING.get() @@ -81,7 +130,6 @@ def fscan (energy_start, energy_end, duration, energy_step, *counters, nscans=1, 'type': fscan, 'count_time': exp_time, 'data_dim': 2, - 'caramel_mou':10, } scan = Scan(chain, name='fscan', @@ -98,76 +146,82 @@ def fscan (energy_start, energy_end, duration, energy_step, *counters, nscans=1, return scan -#NO MONO MOTION SCAN : programming empty traj with only output register to trig musst. -def fscan_time (exp_time, nbpoints, *counters, save=False ): - - print ("Running fscan_time {0}\n".format(fscan_time.__module__)) - - print ("Building acquisition chain {0}\n".format((exp_time, 0, nbpoints))) - - chain = fscan_build_acquisition_chain (exp_time, 0, nbpoints, *counters) - - print ("Build chain {0}\n".format(chain)) - - print ("Programming EMPTY trajectory\n") +def fscan_energy_start (energy_start, energy_end, duration, energy_step): - start_cmd = pm600_prog_white_opiom_trig (setup_globals.mono) + #TODO if nscans>1 add backlash to motions + #TODO if id_linked adapt energy start and end to gaps possibilities - setup_globals.mono.controller.raw_write_read (start_cmd) #1XS2 + ''' + Will move mono to energy_start, check the position it has really reached, and recalc energy_start form it + ''' + + if energy_start > energy_end: + raise RuntimeError( + "Energy must go incrementing. start ({0}) end ({1})".format(energy_start, energy_end) + ) + + if energy_start == energy_end or energy_step == 0: + raise RuntimeError( + "Rather run fscan_time." + ) - scan_info = {'title': 'fscan_time ({1}, {0})'.format(nbpoints, exp_time), - 'npoints':nbpoints, - 'type': fscan_time, - 'count_time': exp_time, - 'data_dim': 2, - } + mono_init_angle = MONO_PM600.position #in angle + mono_init_steps = float(MONO_PM600.controller.raw_write_read("1OA\r").split(':')[1]) # Out [1]: 2295137.0 - scan = Scan(chain, name='fscan', - scan_info=scan_info, - save=save, - data_watch_callback=CScanDisplay('elapsed_time') ) + #TODO check positions are within limits: bliss does it for me, but to be checked... + + #mv energy FSCAN_PAR["_ene_start"] + # means: + #TODO Move mono and ID to starting position (calculated from given energy). + # Move only mono (fscan2_mono_init() when ID not linked, fscan2_undu_init() elseway + # "1SV%i\r", FSCAN_PAR["comeback_speed"]) + # Move mono means also monox and phaseplate ...id26_newEnergy_with_monox.mac + #now MOVE MONOX and PM600 for now on - print ("Ready to run scan {0}".format(scan._next_scan_number()+1)) + ENERGY.move (energy_start) - scan.run() + step0 = float(MONO_PM600.controller.raw_write_read("1OA\r").split(':')[1]) # Out [1]: 2295137.0 + enc_pm_0 = ENC_PM.read() # Out [2]: -219960 + enc_hdh_0 = ENC_HDH.read() # Out [3]: -244601 - fscan_back_to_idle() + #QUESTION: + #uncomment when really MOVING: energy_start = stepstoenergy (step0) #this is from dial mono position, like this in fscan2.mac !!! + #OR + energy_start = ENC_HDH.energy () #this is from hdh encoder position - return scan - + if energy_start > energy_end: + raise RuntimeError( + "Energy must go incrementing. start ({0}) end ({1})".format(energy_start, energy_end) + ) + + if energy_start == energy_end or energy_step == 0: + raise RuntimeError( + "Rather run fscan_time." + ) -def fscan_back_to_idle(): - ''' - TODO - if ID_linked move back undulators to the beg of scan... - fscan2_cleanup_standard : - _mono_wait_idle_state(FSCAN_PAR["scan_duration"] + 60) - _mono_speed(FSCAN_PAR["comeback_speed"]) - END -''' + nbpoints = round((energy_end - energy_start) / (energy_step)) + exp_time = duration/nbpoints - setup_globals.mono.controller.raw_write_read("1WP11111111\r") - setup_globals.mono.controller.sock.flush() + return energy_start, nbpoints, exp_time + def fscan_build_acquisition_chain (exp_time, latency_time, nbpoints, *counters): - period = exp_time + latency_time # scan_params = {'npoints': nbpoints, # 'count_time':min(.01,exp_time), # } - + + period = exp_time + latency_time chain = AcquisitionChain() # OPIOM MULTIPLEXER PRESET - opiom1_preset = ZapMultiplexerPreset() - chain.add_preset(opiom1_preset) + chain.add_preset (ZapMultiplexerPreset(OPIOM)) # MUSST MASTER - musst_board = setup_globals.musst_cc1 - musst_clock = _musst_set_tmrcfg (musst_board,nbpoints*period) + musst_clock = _musst_set_tmrcfg (MUSST, nbpoints*period) if musst_clock is 0: raise RuntimeError ("no compatible clock frq on MUSST") @@ -179,7 +233,7 @@ def fscan_build_acquisition_chain (exp_time, latency_time, nbpoints, *counters): period = musst_period / musst_clock exp_time = musst_exposure_time / musst_clock - musst_master = MusstAcquisitionMaster(musst_board, + musst_master = MusstAcquisitionMaster (MUSST, program_data = zap_musst_program_data, program_start_name = 'FTIMESCAN', program_abort_name = 'CLEAN', @@ -188,32 +242,33 @@ def fscan_build_acquisition_chain (exp_time, latency_time, nbpoints, *counters): 'NPTS': int(nbpoints) }) - print ("MusstAcquisitionMaster : {0} / program FTIMESCAN inputs: {1}".format(musst_board.name,musst_master.vars)) + print ("MusstAcquisitionMaster : {0} / program FTIMESCAN inputs: {1}".format(MUSST.name,musst_master.vars)) # MUSST DEVICE musst_store_list=['time_start','mono_enc_start','hdh_enc_start','time_stop','mono_enc_stop','hdh_enc_stop'] - musst_device = MusstAcquisitionSlave(musst_board, + musst_device = MusstAcquisitionSlave(MUSST, store_list = musst_store_list) print ("MusstAcquisitionSlave storelist {}".format(musst_store_list)) chain.add(musst_master, musst_device) - #TODO conversions - + #TODO more conversions channel + # check channels with chain.nodes_list[0].channels[0].name + conversion = lambda data : (data / musst_clock) musst_master.add_external_channel(musst_device, 'time_start', rename='elapsed_time', conversion=conversion, dtype=numpy.float) - - offset = setup_globals.musst_enc_pm.offset_user - factor = setup_globals.musst_enc_pm.factor + + offset = ENC_PM.parameters.offset_user + factor = ENC_PM.parameters.factor conversion = lambda data : (data + offset / factor) musst_master.add_external_channel(musst_device, 'mono_enc_start', rename='mono_angle', conversion=conversion, dtype=numpy.float) - offset = setup_globals.musst_enc_hdh.offset_user - factor = setup_globals.musst_enc_hdh.factor + offset = ENC_HDH.parameters.offset_user + factor = ENC_HDH.parameters.factor conversion = lambda data : (data + offset / factor) musst_master.add_external_channel(musst_device, 'hdh_enc_start', rename='hdh_angle', conversion=conversion, dtype=numpy.float) @@ -229,18 +284,23 @@ def fscan_build_acquisition_chain (exp_time, latency_time, nbpoints, *counters): for node in builder.get_nodes_by_controller_type(CT2Controller): node.set_parameters(acq_params={"npoints":nbpoints, - "acq_mode":AcqMode.ExtGate, - "acq_expo_time":exp_time, - "acq_point_period":None, - "prepare_once":True, - "start_once":True, - }) + "acq_mode":AcqMode.ExtGate, + "acq_expo_time":exp_time, + "acq_point_period":None, + "prepare_once":True, + "start_once":True, + }) for child_node in node.children: child_node.set_parameters(acq_params={"count_time": exp_time}) chain.add (musst_master,node) + for node in builder.get_nodes_by_controller_type(DarkCounter): + + chain.add (musst_master,node) + + print (chain._tree) for node in builder.get_nodes_not_ready(): @@ -254,7 +314,6 @@ def fscan_build_acquisition_chain (exp_time, latency_time, nbpoints, *counters): return chain - def fscan_program_trajectories (energy_start, energy_end, duration, energy_step, nscans, nintervals, backlash): @@ -264,7 +323,7 @@ def fscan_program_trajectories (energy_start, energy_end, duration, energy_step, #load trajectories into the motor controller: TODO reprogram traj only if necessary, only new trajectorie.... #Si(111) - pm600_trajectory = pm600_prog_mono_trajectory (setup_globals.mono,\ + pm600_trajectory = pm600_prog_mono_trajectory (MONO_PM600,\ energy_start, energy_end, duration, \ nscans, nintervals, backlash) print (pm600_trajectory.pvt) @@ -281,194 +340,13 @@ def fscan_program_trajectories (energy_start, energy_end, duration, energy_step, raise (err) #check what traj was loaded in controller - setup_globals.mono.controller.trajectory_list(pm600_trajectory) + MONO_PM600.controller.trajectory_list(pm600_trajectory) return group -def fscan_initialize (energy_start, energy_end, duration, energy_step): - - #TODO if nscans>1 add backlash to motions - #TODO if id_linked adapt energy start and end to gaps possibilities - - if energy_start > energy_end: - raise RuntimeError( - "Energy must go incrementing. start ({0}) end ({1})".format(energy_start, energy_end) - ) - - if energy_start == energy_end or energy_step == 0: - raise RuntimeError( - "Rather run fscan_time." - ) - - nbpoints = round((energy_end - energy_start) / (energy_step)) - exp_time = duration/nbpoints - mono_init_angle = setup_globals.mono.position #in angle - mono_init_steps = float(setup_globals.mono.controller.raw_write_read("1OA\r").split(':')[1]) #in dial/steps - - #TODO check positions are within limits: I hope bliss does it for me, but to be checked... - - #mv energy FSCAN_PAR["_ene_start"] - # means: - #TODO Move mono and ID to starting position (calculated from given energy). - # Move only mono (fscan2_mono_init() when ID not linked, fscan2_undu_init() elseway - # "1SV%i\r", FSCAN_PAR["comeback_speed"]) - # Move mono means also monox and phaseplate ...id26_newEnergy_with_monox.mac - #now MOVE MONOX and PM600 for now on - - setup_globals.energy.move (energy_start) #only simu motors attached ... - - step0 = float(setup_globals.mono.controller.raw_write_read("1OA\r").split(':')[1]) - #enc_pm_0 = setup_globals.musst_cc1.get_channel_by_name('encoder_pm600').value - #enc_hdh_0 = setup_globals.musst_cc1.get_channel_by_name('encoder_hdh').value - enc_pm_0 = setup_globals.musst_enc_pm.read() - enc_hdh_0 = setup_globals.musst_enc_hdh.read() - - #uncomment when really MOVING: energy_start = stepstoenergy (step0) #this is from dial mono position, like this in fscan2.mac !!! - #OR uncomment when really MOVING: energy_start = setup_globals.musst_enc_hdh.energy () #this is from hdh encoder position - - if energy_start > energy_end: - raise RuntimeError( - "Energy must go incrementing. start ({0}) end ({1})".format(energy_start, energy_end) - ) - - if energy_start == energy_end or energy_step == 0: - raise RuntimeError( - "Rather run fscan_time." - ) - - return energy_start, nbpoints, exp_time - - -def zaptime(nbpoints, exp_time, *counters, latency_time=0, save=True): - """ - Start hardware triggered time scan on detectors with MUSST card as the master. - To select the detectors, enable/disable them using the ACTIVE_MG measurement group. - Args: - nbpoints: number of acquistion points - exp_time: exposure time in second - latency_time: time to wait between 2 points [default: 0] - save: choose to save or not the data [default: True] - Returns: - Scan: the scan handler - """ - - print ("Running zaptime {0}\n".format(zaptime.__module__)) - - period = exp_time+latency_time -# scan_params={'npoints': nbpoints, -# 'count_time':min(.01,exp_time), -# } - - chain = AcquisitionChain() - - # OPIOM MULTIPLEXER PRESET - opiom1_preset = ZapMultiplexerPreset() - - chain.add_preset(opiom1_preset) - - # MUSST MASTER - musst_board = setup_globals.musst_cc1 - musst_clock = _musst_set_tmrcfg (musst_board,nbpoints*period) - - if musst_clock is 0: - return - - # round (ceil) timing here to the upper tick of the musst clock - musst_period = int(numpy.ceil(period * musst_clock)) - musst_exposure_time = int(exp_time * musst_clock) - - period = musst_period / musst_clock - exp_time = musst_exposure_time / musst_clock - - musst_master = MusstAcquisitionMaster(musst_board, - program_data = zap_musst_program_data, - program_start_name = 'FTIMESCAN', - program_abort_name = 'CLEAN', - vars={'PERIOD': musst_period, - 'ACQTIME': musst_exposure_time, - 'NPTS': int(nbpoints) - }) - - print ("MusstAcquisitionMaster : {0} / program FTIMESCAN inputs: {1}".format(musst_board.name,musst_master.vars)) - - # MUSST DEVICE - - musst_store_list=['time_start','mono_enc_start','hdh_enc_start','time_stop','mono_enc_stop','hdh_enc_stop'] - - musst_device = MusstAcquisitionSlave(musst_board, - store_list = musst_store_list) - - print ("MusstAcquisitionSlave : storelist {}".format(musst_store_list)) - - chain.add(musst_master, musst_device) - - conversion = lambda data : (data / musst_clock) - musst_master.add_external_channel(musst_device, 'time_start', rename='elapsed_time', - conversion=conversion, dtype=numpy.float) - conversion = lambda data : (data / 1) - musst_master.add_external_channel(musst_device, 'mono_enc_start', rename='mono_angle', - conversion=conversion, dtype=numpy.float) - conversion = lambda data : (data / 1) - musst_master.add_external_channel(musst_device, 'hdh_enc_start', rename='hdh_angle', - conversion=conversion, dtype=numpy.float) - - # CHAIN BUILDER for P201 counters - - builder = ChainBuilder(counters) -# builder.print_tree() - - for node in builder.get_nodes_by_controller_type(CT2Controller): - node.set_parameters(acq_params={"npoints":nbpoints, - "acq_mode":AcqMode.ExtGate, - "acq_expo_time":exp_time, - "acq_point_period":None, - "prepare_once":True, - "start_once":True, - }) - - for child_node in node.children: - child_node.set_parameters(acq_params={"count_time": exp_time}) - chain.add (musst_master,node) - - - print (chain._tree) - - - - for node in builder.get_nodes_not_ready(): - print ("WARNING: NOT in the acquisition chain:") - print(f"-->{ node.get_repr_str() }") - for child in node.children: - print(" |") - print(f" { child.get_repr_str() }") - print("\n") - - - - saving_parameters = setup_globals.SCAN_SAVING.get() - - scan_info = {'title': 'zaptime ({0}, {1}, {2})'.format(nbpoints,exp_time,latency_time), - 'npoints':nbpoints, - 'type': zaptime, - 'count_time': exp_time, - 'data_dim': 2, - } - - scan = Scan(chain,name='zaptime', - scan_info=scan_info, - save=save, - data_watch_callback=CScanDisplay('elapsed_time') ) - - print ("Ready to run scan {0}".format(scan._next_scan_number()+1)) - - scan.run() - - return scan - - -def _musst_set_tmrcfg (musst_board,scan_duration): +def _musst_set_tmrcfg (musst_board, scan_duration): for musst_new_clock in [(50e6,'50MHZ'),(10e6,'10MHZ'),(1e6,'1MHZ'),(100e3,'100KHZ'),(10e3,'10KHZ'),(1e3,'1KHZ')]: max_duration = (2**31)/musst_new_clock[0] # 31 bit because seb is reading the musst timer in signed @@ -479,9 +357,28 @@ def _musst_set_tmrcfg (musst_board,scan_duration): return clock print ('Sorry, cannot manage a so long scan duration {0:.2f} mn, \ - please change the zaptime parameters, maximum is {1:.2f} mn'.format(scan_duration/60,max_duration/60)) + please change the fscan parameters, maximum is {1:.2f} mn'.format(scan_duration/60,max_duration/60)) + return 0 + +def fscan_back_to_idle(): + ''' + TODO + if ID_linked move back undulators to the beg of scan... + fscan2_cleanup_standard : + _mono_wait_idle_state(FSCAN_PAR["scan_duration"] + 60) + _mono_speed(FSCAN_PAR["comeback_speed"]) + END +''' + + MONO_PM600.controller.raw_write_read("1WP11111111\r") + MONO_PM600.controller.sock.flush() + + + + + class CScanDisplay(DataWatchCallback): @@ -542,21 +439,6 @@ class CScanDisplay(DataWatchCallback): return -class ZapMultiplexerPreset(ChainPreset): - def __init__(self): - ChainPreset.__init__(self) - - multiplexer = setup_globals.multiplexer_opiom_cc1 - - def prepare(self, acq_chain): - pass - - def start(self, acq_chain): - self.multiplexer.switch('ACQ_TRIG','ZAP') - - def stop(self, acq_chain): - self.multiplexer.switch('ACQ_TRIG','COUNT') - class PM600TrajectoryID26(object): @@ -604,3 +486,42 @@ so unused outside of ID control, maybe... and it will be a trajectory on ID hopefully ''' + +def zaptime(nbpoints, exp_time, latency_time, *counters, save=False): + """ + Start hardware triggered time scan on detectors with MUSST card as the master. + To select the detectors, enable/disable them using the ACTIVE_MG measurement group. + Args: + nbpoints: number of acquistion points + exp_time: exposure time in second + latency_time: time to wait between 2 points [default: 0] + save: choose to save or not the data [default: False] + Returns: + Scan: the scan handler + """ + + print ("Running zaptime {0}\n".format(zaptime.__module__)) + + chain = fscan_build_acquisition_chain (exp_time, latency_time, nbpoints, *counters) + + print ("Built chain {0}\n".format(chain)) + + scan_info = {'title': 'zaptime ({0}, {1}, {2}, {3})'.format(nbpoints,exp_time,latency_time, counters), + 'npoints':nbpoints, + 'type': zaptime, + 'count_time': exp_time, + 'data_dim': 2, + } + + scan = Scan(chain,name='zaptime', + scan_info=scan_info, + save=save, + data_watch_callback=CScanDisplay('elapsed_time') ) + + print ("Ready to run scan {0}".format(scan._next_scan_number()+1)) + + scan.run() + + return scan + + -- GitLab From 991a5be8d7f830ffc395aa2b4a4f76063c8f2ad3 Mon Sep 17 00:00:00 2001 From: lagier Date: Tue, 10 Mar 2020 16:38:23 +0100 Subject: [PATCH 15/27] beamcheck.py initial commit --- id26/scripts/beamcheck.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 id26/scripts/beamcheck.py diff --git a/id26/scripts/beamcheck.py b/id26/scripts/beamcheck.py new file mode 100644 index 0000000..3246c92 --- /dev/null +++ b/id26/scripts/beamcheck.py @@ -0,0 +1,22 @@ +''' +https://bliss.gitlab-pages.esrf.fr/bliss/master/scan_engine_preset.html + +To pause a scan¶ + +As Preset callbacks are executed synchronously they can easily pause it, just by not returning imediatly from prepare, start or stop callback methods. If for example you need to pause a scan if there is no beam, you just have to check in a loop the condition. As an example, wait to start a scan if the beam is not present. +''' + +class Preset(ScanPreset): + def __init__(self, diode, beam_trigger_value): + self._diode = diode + self._beam_trigger_value + + def prepare(self,scan): + beam_value = self._diode.read() + while beam_value < self._beam_trigger_value: + print("Waiting for beam") + print(f"read {beam_value} expect {self._beam_trigger_value}",end='\r') + gevent.sleep(1) + beam_value = self._diode.read() + + -- GitLab From 4a35fbafad10b349bd16a052503e16e1fd4574a6 Mon Sep 17 00:00:00 2001 From: bliss administrator Date: Wed, 11 Mar 2020 08:52:51 +0100 Subject: [PATCH 16/27] BM23CalcCounters.py removed debug info printing --- id26/controllers/BM23CalcCounters.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/id26/controllers/BM23CalcCounters.py b/id26/controllers/BM23CalcCounters.py index b01a75f..5511bc4 100644 --- a/id26/controllers/BM23CalcCounters.py +++ b/id26/controllers/BM23CalcCounters.py @@ -66,15 +66,15 @@ class DarkCounter(CalcCounterController): print("Close functions did not succeed, Backgrounds have not been changed !!!") def calc_function(self, input_dict): - print(f"{self.get_current_parameters()}") + #print(f"\n\n---------DARK") value = {} for tag in input_dict.keys(): cnt = self.get_input_counter_from_tag(tag) if isinstance(cnt, IntegratingCounter): - #print(f"---- {cnt.name} -- INTEGRATING") + #print(f"\n---- {cnt.name} -- INTEGRATING") value[tag] = input_dict[tag] - self.dark_setting[tag] * self.scan_params["count_time"] / self.dark_setting['dark_time'] else: - #print(f"---- {cnt.name} -- NOT INTEGRATING") + #print(f"\n---- {cnt.name} -- NOT INTEGRATING") value[tag] = input_dict[tag] - self.dark_setting[tag] return value -- GitLab From 0ed4e09d2a389ceadd9d8480dc43612992647edf Mon Sep 17 00:00:00 2001 From: bliss administrator Date: Wed, 11 Mar 2020 11:39:54 +0100 Subject: [PATCH 17/27] fscan tests - to be continued --- id26/scripts/fscan.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/id26/scripts/fscan.py b/id26/scripts/fscan.py index bf80654..79435db 100644 --- a/id26/scripts/fscan.py +++ b/id26/scripts/fscan.py @@ -5,8 +5,6 @@ import time from bliss.scanning.scan import Scan, ScanState, DataWatchCallback from bliss.scanning.chain import AcquisitionChain, ChainPreset - - from bliss.scanning.toolbox import ChainBuilder from bliss.scanning.acquisition.musst import MusstAcquisitionMaster, MusstAcquisitionSlave @@ -105,13 +103,14 @@ def fscan (energy_start, energy_end, duration, energy_step, *counters, nscans=1, chain = fscan_build_acquisition_chain (exp_time, 0, nbpoints, *counters) + print ("Built chain {0}\n".format(chain)) print ("Programming trajectories {0}\n".format((energy_start, energy_end, duration, energy_step, nscans, nintervals, backlash))) #WHITE comment below group = fscan_program_trajectories (energy_start, energy_end, duration, energy_step, nscans, nintervals, backlash) - + #WHITE uncomment: #start_cmd = pm600_prog_white_opiom_trig (MONO_PM600) #WHITE uncomment: @@ -119,10 +118,7 @@ def fscan (energy_start, energy_end, duration, energy_step, *counters, nscans=1, #group.move_to_start() -> unused (pm600, IDs) motors already moved at fscan_energy_start phase to have precise step0/energy_start - #WHITE comment below - #REAL uncomment but then will be MOVING!!! - group.move_to_end() #-> calls prepare_move(finalpos);pm600.start_trajectory;pm600.stop_trajectory; -> 1XS2 - + #saving_parameters = setup_globals.SCAN_SAVING.get() scan_info = {'title': 'fscan ({0}, {1}, {2}, {3})'.format(energy_start, energy_end, duration, energy_step), @@ -139,8 +135,14 @@ def fscan (energy_start, energy_end, duration, energy_step, *counters, nscans=1, print ("Ready to run scan {0}".format(scan._next_scan_number()+1)) + #WHITE comment below + #REAL uncomment but then will be MOVING!!! + + return scan, group + + group.move_to_end() #-> calls prepare_move(finalpos);pm600.start_trajectory;pm600.stop_trajectory; -> 1XS2 scan.run() - + fscan_back_to_idle() return scan -- GitLab From 3673da5495459d177749b4f17583dd616b5c78d5 Mon Sep 17 00:00:00 2001 From: lagier Date: Tue, 17 Mar 2020 16:59:17 +0100 Subject: [PATCH 18/27] position_instances.py initial commit for PositionInstances Class --- id26/scripts/position_instances.py | 167 +++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 id26/scripts/position_instances.py diff --git a/id26/scripts/position_instances.py b/id26/scripts/position_instances.py new file mode 100644 index 0000000..5d0f51f --- /dev/null +++ b/id26/scripts/position_instances.py @@ -0,0 +1,167 @@ +from bliss import current_session +from bliss.shell.cli.user_dialog import UserChoice +from bliss.shell.cli.pt_widgets import BlissDialog, button_dialog + +from id26.scripts.beamline_parameters import ID26Parameters, switch_instance + +#BLISS session script: +#oldpos0 = PositionInstances ('old0',mot1, mot2, ...) +#oldpos_ps = PositionInstances ('old1',mot3) + +#TODO how to purge unwanted parameters set from redis +#TODO list below +#TODO check inputs and types +#TODO write documentation + +class ID26Positions (ID26Parameters): + + def __init__(self, *axis_list):# = None ): + param_name = current_session.name+'_old_positions' + positions_dict = {} + + if axis_list is None: + pass + #TODO take all motors + + for axis in axis_list: + positions_dict[axis.name] = None #axis.position + + super().__init__(param_name, positions_dict) + + + + @switch_instance + def update (self, *axis_list): + ''' + to reassign savedpos to current positions without going through setup() UI dialogs + ''' + + print ('updating',self.instance_name) + parameters_dict = self.parameters.to_dict(export_properties=True) + ret = {} + if axis_list: + for axis in axis_list: + if axis.name in parameters_dict: + ret[axis.name]=axis.position + else: + for name in self.parameters.to_dict(export_properties=False): + axis=current_session.config.get(name) + ret[name]=axis.position + + + parameters_dict.update(ret) + self.parameters.from_dict(parameters_dict) + + + + + @switch_instance + def move (self, *axis_list): + ''' + to move to those positions + ''' + positions_dict = self.parameters.to_dict() + for axis in axis_list: + #TODO group motions + if axis.name in positions_dict: + axis.move(positions_dict[axis.name]) + + + @switch_instance + def move_all (self): + ''' + to move to those positions + ''' + #TODO group motions + positions_dict = self.parameters.to_dict(export_properties=False) + for name in positions_dict: + axis=current_session.config.get(name) + axis.move(positions_dict[name]) + + + @switch_instance + def assign (self, axis, new_position): + ''' + to assign some other position to one particular saved axis (before move_to() call for instance) + can also be done with setup() in an interactive way + ''' + parameters_dict = self.parameters.to_dict(export_properties=True) + ret = {} + if axis.name in parameters_dict: + ret[axis.name]=new_position + parameters_dict.update(ret) + self.parameters.from_dict(parameters_dict) + else: + print (axis.name,'not in the saved positions list, sorry.') + + + @switch_instance + def add (self, *axis_list): + ''' + add new axis with their current position + ''' + for axis in axis_list: + self.parameters.add(axis.name,axis.position) + + @switch_instance + def remove (self, *axis_list): + ''' + remove axis from the saved positions + ''' + for axis in axis_list: + self.parameters.remove('.'+axis.name) + + @switch_instance + def remove_case (self, instance_name): + ''' + remove that instance of saved positions + ''' + self.parameters.remove(instance_name) + + + @switch_instance + def show_all (self): + """ + Shows all positions saved in different instances + - Property attributes are identified with an # (hash) + - parameters taken from default are identified with an * (asterisk) + - responsabilty of user to clear unused instances to free redis mem + """ + + self.parameters.show_table() + + + + + + + + + + +class PositionInstances (ID26Positions): + ''' + bli + + useroldpos=Positionscase('oldpos1', simumot, simumono) + + useroldpos #show current saved values + useroldpos.show_all() #show all existing instances of current saved values + useroldpos.save() #write to disk + useroldpos.setup() # launches a dialog to edit values + useroldpos.update(*axis) #update values to current position for listed axis, all axis if None + useroldpos.add(*axis) + useroldpos.remove(*axis) + useroldpos.move_to(*axis) #move listed axes to saved position + useroldpos.move_all_to() #move all saved axes to saved position + useroldpos.parameters.simumot = 2 #update one particular position to given value + useroldpos.assign(simumot,2) #idem + ''' + + def __init__(self, instance_name, *axis): + print ("Initialising {0}".format(instance_name)) + super().__init__(*axis) + self.instance_name = instance_name + self.update (*axis) + + -- GitLab From 58a917e29273576aff4e2d7b93183e3433f2615b Mon Sep 17 00:00:00 2001 From: lagier Date: Wed, 18 Mar 2020 15:58:47 +0100 Subject: [PATCH 19/27] ready for first release to users - missing some documentation still, and a better save() --- id26/scripts/position_instances.py | 344 ++++++++++++++++------------- 1 file changed, 189 insertions(+), 155 deletions(-) diff --git a/id26/scripts/position_instances.py b/id26/scripts/position_instances.py index 5d0f51f..606470b 100644 --- a/id26/scripts/position_instances.py +++ b/id26/scripts/position_instances.py @@ -1,167 +1,201 @@ -from bliss import current_session +from bliss import current_session, global_map +from bliss.common.axis import Axis +from bliss.common.standard import move from bliss.shell.cli.user_dialog import UserChoice from bliss.shell.cli.pt_widgets import BlissDialog, button_dialog from id26.scripts.beamline_parameters import ID26Parameters, switch_instance -#BLISS session script: -#oldpos0 = PositionInstances ('old0',mot1, mot2, ...) -#oldpos_ps = PositionInstances ('old1',mot3) - -#TODO how to purge unwanted parameters set from redis -#TODO list below -#TODO check inputs and types -#TODO write documentation - -class ID26Positions (ID26Parameters): - - def __init__(self, *axis_list):# = None ): - param_name = current_session.name+'_old_positions' - positions_dict = {} - - if axis_list is None: - pass - #TODO take all motors - - for axis in axis_list: - positions_dict[axis.name] = None #axis.position - - super().__init__(param_name, positions_dict) - - - - @switch_instance - def update (self, *axis_list): - ''' - to reassign savedpos to current positions without going through setup() UI dialogs - ''' - - print ('updating',self.instance_name) - parameters_dict = self.parameters.to_dict(export_properties=True) - ret = {} - if axis_list: - for axis in axis_list: - if axis.name in parameters_dict: - ret[axis.name]=axis.position - else: - for name in self.parameters.to_dict(export_properties=False): - axis=current_session.config.get(name) - ret[name]=axis.position - - - parameters_dict.update(ret) - self.parameters.from_dict(parameters_dict) - - - - - @switch_instance - def move (self, *axis_list): - ''' - to move to those positions - ''' - positions_dict = self.parameters.to_dict() - for axis in axis_list: - #TODO group motions - if axis.name in positions_dict: - axis.move(positions_dict[axis.name]) - - - @switch_instance - def move_all (self): - ''' - to move to those positions - ''' - #TODO group motions - positions_dict = self.parameters.to_dict(export_properties=False) - for name in positions_dict: - axis=current_session.config.get(name) - axis.move(positions_dict[name]) - - - @switch_instance - def assign (self, axis, new_position): - ''' - to assign some other position to one particular saved axis (before move_to() call for instance) - can also be done with setup() in an interactive way - ''' - parameters_dict = self.parameters.to_dict(export_properties=True) - ret = {} - if axis.name in parameters_dict: - ret[axis.name]=new_position - parameters_dict.update(ret) - self.parameters.from_dict(parameters_dict) - else: - print (axis.name,'not in the saved positions list, sorry.') - - - @switch_instance - def add (self, *axis_list): - ''' - add new axis with their current position - ''' - for axis in axis_list: - self.parameters.add(axis.name,axis.position) - - @switch_instance - def remove (self, *axis_list): - ''' - remove axis from the saved positions - ''' - for axis in axis_list: - self.parameters.remove('.'+axis.name) - - @switch_instance - def remove_case (self, instance_name): - ''' - remove that instance of saved positions - ''' - self.parameters.remove(instance_name) - - - @switch_instance - def show_all (self): - """ - Shows all positions saved in different instances - - Property attributes are identified with an # (hash) - - parameters taken from default are identified with an * (asterisk) - - responsabilty of user to clear unused instances to free redis mem - """ - - self.parameters.show_table() - - - - - - - - - - -class PositionInstances (ID26Positions): - ''' - bli +# TODO NOT URGENT list below + + +class ID26Positions(ID26Parameters): + def __init__(self, *axis_list): + param_name = current_session.name + "_old_positions" + positions_dict = {} + + if len(axis_list) is 0: + axis_list = list(global_map.get_axes_iter()) + + for axis in axis_list: + if isinstance(axis, Axis): + positions_dict[axis.name] = None + else: + print("{0} not an Axis in current session.".format(axis)) + print("hint: do not use string names for axis list, but objects.") + + super().__init__(param_name, positions_dict) + print("axis names: {0}".format(list(map(lambda x: x, positions_dict)))) + + def _purge(self): + """ + will remove all existing instances of current bliss session saved positions from redis . + make sure that' s what you want before proceeding ... + """ + self.parameters.purge() + + @switch_instance + def update(self, *axis_list): + """ + to reassign saved pos to current positions in one go without going through .assign(), nor .setup() UI dialogs + """ + + parameters_dict = self.parameters.to_dict(export_properties=True) + ret = {} + if len(axis_list): + for axis in axis_list: + if isinstance(axis, Axis) and axis.name in parameters_dict: + ret[axis.name] = axis.position + else: + for name in self.parameters.to_dict(export_properties=False): + axis = current_session.config.get(name) + ret[name] = axis.position + print("updating {0} with {1}".format(self.instance_name, ret)) + + parameters_dict.update(ret) + self.parameters.from_dict(parameters_dict) + + @switch_instance + def move(self, *axis_list): + """ + to move to those positions + """ + motion = () + positions_dict = self.parameters.to_dict() + for axis in axis_list: + if ( + isinstance(axis, Axis) + and axis.name in positions_dict + and positions_dict[axis.name] + ): + motion += (axis, positions_dict[axis.name]) + + if len(axis_list) is 0: + print( + "You must specify the list of axis you want to move, or use .move_all()" + ) + else: + if len(motion): + move(*motion) + else: + print("nothing to move...") + + @switch_instance + def move_all(self): + """ + to move to those positions + """ + motion = () + positions_dict = self.parameters.to_dict(export_properties=False) + for name in positions_dict: + axis = current_session.config.get(name) + if positions_dict[name]: + motion += (axis, positions_dict[name]) + if len(motion): + move(*motion) + else: + print("nothing to move...") + + @switch_instance + def assign(self, axis, new_position): + """ + to assign some other position to one particular saved axis (before move_to() call for instance) + can also be done with setup() in an interactive way + """ + parameters_dict = self.parameters.to_dict(export_properties=True) + ret = {} + if isinstance(axis, Axis) and axis.name in parameters_dict: + ret[axis.name] = new_position + parameters_dict.update(ret) + self.parameters.from_dict(parameters_dict) + else: + print( + "{0} not an axis in the saved positions list, sorry.".format( + axis.name if isinstance(axis, Axis) else axis + ) + ) + + @switch_instance + def add(self, *axis_list): + """ + add new axis with their current position + """ + for axis in axis_list: + if isinstance(axis, Axis): + self.parameters.add(axis.name, None) + print("{0} added".format(axis.name)) + else: + print("{0} not an axis".format(axis)) + if len(axis_list) is 0: + print("Nothing added, please specify axis you want to add") + + @switch_instance + def remove(self, *axis_list): + """ + remove axis from the saved positions + """ + for axis in axis_list: + if isinstance(axis, Axis): + self.parameters.remove("." + axis.name) + print("{0} removed".format(axis.name)) + else: + print("{0} not an axis".format(axis)) + if len(axis_list) is 0: + print( + "Nothing removed, please specify axis you want to be removed from the instances" + ) + + @switch_instance + def remove_instance(self, instance_name): + """ + remove that instance of saved positions + """ + if instance_name in self.parameters.instances: + self.parameters.remove(instance_name) + print("{0} removed.".format(instance_name)) + else: + print("{0} unknown instance ".format(instance_name)) + + @switch_instance + def show_all(self): + """ + Shows all positions saved in different instances + - Property attributes are identified with an # (hash) + - parameters taken from default are identified with an * (asterisk) + - responsabilty of user to clear unused instances to free redis mem + """ + # TODO NOT URGENT do sth more relevant for the positions. + self.parameters.show_table() + + +class PositionInstances(ID26Positions): + """ + + user_instance=PositionsInstances('instance_name', axis1, axis2, ...) + if no axis is listed, all the configured motors in the current session are taken. + user_instance=PositionsInstances('instance_name') - useroldpos=Positionscase('oldpos1', simumot, simumono) - - useroldpos #show current saved values - useroldpos.show_all() #show all existing instances of current saved values - useroldpos.save() #write to disk - useroldpos.setup() # launches a dialog to edit values - useroldpos.update(*axis) #update values to current position for listed axis, all axis if None + Usage: + + useroldpos = PositionsInstances('oldpos1', simumot, simumono) + + useroldpos.update(*axis = None) #update values to current position for listed axis, all axis if None useroldpos.add(*axis) useroldpos.remove(*axis) - useroldpos.move_to(*axis) #move listed axes to saved position - useroldpos.move_all_to() #move all saved axes to saved position - useroldpos.parameters.simumot = 2 #update one particular position to given value - useroldpos.assign(simumot,2) #idem - ''' + useroldpos.move(*axis) #move listed axes to saved position + useroldpos.move_all() #move all saved axes to saved position + useroldpos.assign(simumot,2) #update one particular position to given value + useroldpos.setup() # launches a dialog to edit values + + othersetofpos = PositionsInstances('oldpos2', simumot, simumono) + + useroldpos.show_all() #show all existing instances of current saved values + + useroldpos.save(directory) #write to disk a yml file + + """ def __init__(self, instance_name, *axis): - print ("Initialising {0}".format(instance_name)) + print("Initialising {0}".format(instance_name)) super().__init__(*axis) self.instance_name = instance_name - self.update (*axis) - - -- GitLab From ec5e4b942fa33d7a22353d2429be004fc0705cbd Mon Sep 17 00:00:00 2001 From: lagier Date: Wed, 18 Mar 2020 16:39:44 +0100 Subject: [PATCH 20/27] beamline_parameters.py : add .yml extension to files saved to disk with .save() method --- id26/scripts/beamline_parameters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/id26/scripts/beamline_parameters.py b/id26/scripts/beamline_parameters.py index 87e295a..09d4871 100644 --- a/id26/scripts/beamline_parameters.py +++ b/id26/scripts/beamline_parameters.py @@ -118,7 +118,7 @@ class ID26Parameters: Allows to save in a file a parameter set """ if self.param_name != None and len(directory)>1: - file_name = prefix + self.param_name + suffix + file_name = prefix + self.param_name + suffix +'.yml' sep = '' if directory [-1] == '/' else '/' file_name = directory + sep + file_name self.parameters.to_file(file_name) -- GitLab From 833bd19e01b772683781252d2dd9f8de05f88bd0 Mon Sep 17 00:00:00 2001 From: lagier Date: Wed, 18 Mar 2020 16:40:44 +0100 Subject: [PATCH 21/27] position_instances.py: write its own .save(0 method to save all the instances and only the current one like beamline_parameters.py does --- id26/scripts/position_instances.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/id26/scripts/position_instances.py b/id26/scripts/position_instances.py index 606470b..4717ab3 100644 --- a/id26/scripts/position_instances.py +++ b/id26/scripts/position_instances.py @@ -168,6 +168,20 @@ class ID26Positions(ID26Parameters): self.parameters.show_table() + @switch_instance + def save(self, directory, suffix=''): + """ + saves to disk a yml files with instances content + directory must be specified + """ + if self.param_name != None and len(directory)>1: + file_name = self.param_name + suffix + '.yml' + sep = '' if directory [-1] == '/' else '/' + file_name = directory + sep + file_name + self.parameters.to_file(file_name, *self.parameters.instances) + + print ('Saving instances of saved positions on: {0}'.format(file_name)) + class PositionInstances(ID26Positions): """ -- GitLab From 4348f2aabe8e64a972d14095041ea04f73916e9c Mon Sep 17 00:00:00 2001 From: lagier Date: Thu, 19 Mar 2020 15:39:02 +0100 Subject: [PATCH 22/27] some doc inline --- id26/scripts/position_instances.py | 109 ++++++++++++++++++++++------- 1 file changed, 84 insertions(+), 25 deletions(-) diff --git a/id26/scripts/position_instances.py b/id26/scripts/position_instances.py index 4717ab3..57f62dd 100644 --- a/id26/scripts/position_instances.py +++ b/id26/scripts/position_instances.py @@ -11,7 +11,7 @@ from id26.scripts.beamline_parameters import ID26Parameters, switch_instance class ID26Positions(ID26Parameters): def __init__(self, *axis_list): - param_name = current_session.name + "_old_positions" + param_name = current_session.name + "_positions" positions_dict = {} if len(axis_list) is 0: @@ -29,7 +29,7 @@ class ID26Positions(ID26Parameters): def _purge(self): """ - will remove all existing instances of current bliss session saved positions from redis . + remove all existing instances of current bliss session saved positions from redis . make sure that' s what you want before proceeding ... """ self.parameters.purge() @@ -37,7 +37,7 @@ class ID26Positions(ID26Parameters): @switch_instance def update(self, *axis_list): """ - to reassign saved pos to current positions in one go without going through .assign(), nor .setup() UI dialogs + assign current positions """ parameters_dict = self.parameters.to_dict(export_properties=True) @@ -58,7 +58,7 @@ class ID26Positions(ID26Parameters): @switch_instance def move(self, *axis_list): """ - to move to those positions + move motors to stored positions """ motion = () positions_dict = self.parameters.to_dict() @@ -83,7 +83,7 @@ class ID26Positions(ID26Parameters): @switch_instance def move_all(self): """ - to move to those positions + move all motors to stored positions """ motion = () positions_dict = self.parameters.to_dict(export_properties=False) @@ -99,7 +99,7 @@ class ID26Positions(ID26Parameters): @switch_instance def assign(self, axis, new_position): """ - to assign some other position to one particular saved axis (before move_to() call for instance) + assign a position to one particular axis (before move() call for instance) can also be done with setup() in an interactive way """ parameters_dict = self.parameters.to_dict(export_properties=True) @@ -118,7 +118,7 @@ class ID26Positions(ID26Parameters): @switch_instance def add(self, *axis_list): """ - add new axis with their current position + add new axis to the set """ for axis in axis_list: if isinstance(axis, Axis): @@ -132,7 +132,7 @@ class ID26Positions(ID26Parameters): @switch_instance def remove(self, *axis_list): """ - remove axis from the saved positions + remove axis from the set """ for axis in axis_list: if isinstance(axis, Axis): @@ -148,7 +148,7 @@ class ID26Positions(ID26Parameters): @switch_instance def remove_instance(self, instance_name): """ - remove that instance of saved positions + remove instance """ if instance_name in self.parameters.instances: self.parameters.remove(instance_name) @@ -157,7 +157,7 @@ class ID26Positions(ID26Parameters): print("{0} unknown instance ".format(instance_name)) @switch_instance - def show_all(self): + def show(self): """ Shows all positions saved in different instances - Property attributes are identified with an # (hash) @@ -185,27 +185,86 @@ class ID26Positions(ID26Parameters): class PositionInstances(ID26Positions): """ - user_instance=PositionsInstances('instance_name', axis1, axis2, ...) - if no axis is listed, all the configured motors in the current session are taken. - user_instance=PositionsInstances('instance_name') - + Class provinding tools to store/retrieve positions of a set of axis along other user actions. + the positions are made persistant in time, so that they can be retrieved even in case of quitting bliss session. + they belong to one particular bliss session and cannot be shared between sessions. + Usage: - useroldpos = PositionsInstances('oldpos1', simumot, simumono) + * user_instance = PositionInstances ('instance_name' , mot1, mot2, mot3 ...) + + loads as user_instance any previously initialised PositionInstances object with the name 'instance_name', + or create it if it is the first time it is instantiated. + + if a list of axis is passed as arguments, slots are prepared for that set in redis database. + if no axis is passed, slots are prepared for all the motors of the session. + + * user_instance.update () + + stores in user_instance (named 'instance_name' in redis) the current positions of the motors in the set. + + * user_instance.update (mot1, mot2) + + stores in user_instance the current positions of the listed motors only. + + * user_instance.show () + + shows all positions sets stored among their different existing instances in redis database. + this is the responsabilty of user to clear unused instances to free redis memory with the next command. + + * user_instance.remove_instance ('unwanted_instance_name') + + remove specified instance from the database. + + * user_instance.remove (mot1, mot2) + + remove axis slots from the instances. + + * user_instance.add (mot3, mot4, mot5) + + add some axis slots. do not forget to assign them their positions after. (.update(), .assign() or .setup()) + + * user_instance.assign (mot3, 3) + + assigns user_instance a position value of 3 for axis mot3 + + * user_instance.setup () + + launches a dialog window to assign positions manually to user_instance + + * user_instance.move (mot3, mot4) + + move all axis listed to the position that was stored for them in user_instance + + * user_instance.move_all () + + move all axis of the set to the position that was stored (if any) for them in user_instance. + + * user_instance.save (directory, suffix = '') + + saves to disk a yml files with all instances content. + a directory must be specified. + a suffix can be given, + the filename is : {session}_positions{suffix}.yml + + + Example: + + pos0 = PositionsInstances('oldpos0', gap) + pos0.update() + #stores gap position - useroldpos.update(*axis = None) #update values to current position for listed axis, all axis if None - useroldpos.add(*axis) - useroldpos.remove(*axis) - useroldpos.move(*axis) #move listed axes to saved position - useroldpos.move_all() #move all saved axes to saved position - useroldpos.assign(simumot,2) #update one particular position to given value - useroldpos.setup() # launches a dialog to edit values + user_script_aligning_mot1() - othersetofpos = PositionsInstances('oldpos2', simumot, simumono) + pos1 = PositionsInstances('oldpos1', mot1) + #create a slot for mot1, gap slot is kept. + pos1.update() + #stores new positions for gap and mot1 - useroldpos.show_all() #show all existing instances of current saved values + pos0.move(gap) + #moves back gap to pos0 stored position - useroldpos.save(directory) #write to disk a yml file + user_script_aligning_continue() """ -- GitLab From adc1d08a6a19d6011127834b4d3ab44ece1b9d8b Mon Sep 17 00:00:00 2001 From: lagier Date: Mon, 23 Mar 2020 12:10:10 +0100 Subject: [PATCH 23/27] some docs tests --- docs/bliss_overview.md | 101 +++++++++++++++++++++++++++++++++++++++++ docs/config_elettra.md | 60 ++++++++++++++++++++++++ docs/readme.md | 3 ++ readme.md | 3 ++ 4 files changed, 167 insertions(+) create mode 100644 docs/bliss_overview.md create mode 100644 docs/config_elettra.md create mode 100644 docs/readme.md create mode 100644 readme.md diff --git a/docs/bliss_overview.md b/docs/bliss_overview.md new file mode 100644 index 0000000..146233b --- /dev/null +++ b/docs/bliss_overview.md @@ -0,0 +1,101 @@ +# BLISS architecture overview + +![BLISS architecture](img/bliss_architecture.png) + +The system has 6 big components: + +* **Beacon**, the server that brings services for the whole system +* **communication objects** to deal with various protocols +* **hardware controllers** with ad hoc frameworks that can handle different kinds of equipment +* **scanning engine** + - continuous scans as first-class citizens + - acquisition chain +* **data management** + - publishing, for online data analysis and data visualization (**Flint** tool) + - **Nexus Writer**, tool from Data Analysis Unit that archives data in Nexus HDF5 format +* **BLISS shell** + - powerful command line interface for users + +![BLISS data flow](img/bliss_architecture.svg) + +## Beacon + +BLISS is built on top of [Beacon](beacon.md), which provides essential services for +the whole system: + +* beamline homepage +* configuration files hosting & housekeeping +* configuration editor +* log viewer +* data management, through the use of [Redis](http://redis.io) + - publishing of data produced during scans, for online data analysis, visualization or saving + - settings, i.e. runtime configuration properties + - channels, to exchange data between different BLISS objects in different processes + +### Beamline homepage + +Beacon offers a homepage to get access to the beamline web applications. On ESRF +beamlines, a web server is configured to display the homepage when pointing a +web browser to the main beamline computer. + +![Beamline homepage](img/beacon_homepage.png) + +### Configuration + +Beacon serves configuration files in [YAML format](http://yaml.org). + +YAML has been chosen because the standard native types (list, dictionary, numbers, unicode strings...) can be easily mapped to Python equivalents, it handles comments (contrary to JSON), and also because it is human-readable (contrary to XML). + +The set of YAML files provide a simple, yet flexible mechanism to describe devices within a BLISS system. + +#### YAML files + +![YAML tree example](img/yaml_tree.svg) + +Objects are identified in the system by an unique **name**. BLISS reserves the YAML key *name* as the entry point for an object configuration. +When loading static configuration data, Beacon goes all over the configuration database directories, and builds an internal representation of the objects that are defined in YAML mapping nodes from the files. Ultimately this structure is flattened and exposed as a Python dictionary with key-value pairs, keys being object names and values being the corresponding configuration information. + +The following YAML lines show an example of the configuration of a movable axis (motor) called `rotY`: + +```yaml +# motion.yml +class: IcePAP +host: iceid311 +plugin: emotion +axes: +- name: rotY + address: 3 + steps_per_unit: 100 + acceleration: 16.0 + velocity: 2.0 +``` + +The information contained in the YAML files is interpreted when the corresponding object is loaded. This interpretation depends on the type of object, in order to create the expected Python object. BLISS has **configuration plugins**, which can be used to extend supported object types. In the example above, the `emotion` (ESRF Motion) plugin is used to interpret the contents of the YAML file, in order to instantiate an `Axis` object. + +#### Configuration editor + +The configuration editor allows to browse through YAML files, and to edit them. + +![Configuration editor](img/beacon_config_app.png) + +### Log viewer + +Beacon centralizes log messages from BLISS applications. A simple web application allows to +look at the log messages. + +![Log viewer](img/beacon_log_viewer.png) + +## Sessions + +A BLISS session represents an experimental setup associated with experiment control sequences. + +A session has: + +* a list of objects from configuration +* a setup file + +Sessions can be nested, by specifying *include-session* in the session YAML configuration file. + +Sessions are loaded by the [BLISS shell](bliss_shell.md), giving access to experimental setup and associated procedures within an integrated command line interface. + + diff --git a/docs/config_elettra.md b/docs/config_elettra.md new file mode 100644 index 0000000..3cbfed3 --- /dev/null +++ b/docs/config_elettra.md @@ -0,0 +1,60 @@ +## Configuring an Elettra (or ePicea) 4 channels electrometer as a Counter Controller + +This section explains how to configure it to get BPM values as counters + +### Example YAML configuration: + +```yaml + +- class: Elettra + module: tango_elettra + name: el2 + uri: //id20ctrl2:20000/id20/elettra/ss1 + counters: + - counter_name: el2x + measure: Y + - counter_name: el2y + measure: Z + - counter_name: el2i + measure: integration_time + - counter_name: el2n + measure: samples_number + - counter_name: c1 + measure: current1 + - counter_name: c2 + measure: current2 + - counter_name: c3 + measure: current3 + - counter_name: c4 + measure: current4 + - counter_name: ctot + measure: current_total + +``` + +### Tango Server + +The Tango server package is called elettraAH in blissinstaller. +A conda package will be provided in the next future. + +http://wikiserv.esrf.fr/bliss/index.php/CAENels_picoammeter#Tango_Device_Server + +### Usage + +To setup the measuring range, give maximum current value you expect to measure in Amps: + + el2.range = 0.00025 + +It is recommended to setup measurement offsets too, for each different measuring range. One can specify the integration time, default is 1 sec. offset_reset command will remove any offset. + + el2.offset_measure (1) + + el2.offset_reset + + el2.offset + +### Restrictions + +Only AH501 models have been tested so far. AH401 and TetrAMM will come soon. + +## TO BE CONTINUED \ No newline at end of file diff --git a/docs/readme.md b/docs/readme.md new file mode 100644 index 0000000..e8f5d61 --- /dev/null +++ b/docs/readme.md @@ -0,0 +1,3 @@ +# README test + +hellooooo from docs \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..c31ef8f --- /dev/null +++ b/readme.md @@ -0,0 +1,3 @@ +# README test + +hellooooo from id26 \ No newline at end of file -- GitLab From 8f0d3537e2837ff91a4f843a3e6818c25203353e Mon Sep 17 00:00:00 2001 From: lagier Date: Mon, 23 Mar 2020 15:06:49 +0100 Subject: [PATCH 24/27] markdown docs for position_instances --- docs/bliss_overview.md | 101 ------------------------------ docs/config_elettra.md | 60 ------------------ docs/position_instances.md | 125 +++++++++++++++++++++++++++++++++++++ docs/readme.md | 3 - readme.md | 3 - 5 files changed, 125 insertions(+), 167 deletions(-) delete mode 100644 docs/bliss_overview.md delete mode 100644 docs/config_elettra.md create mode 100644 docs/position_instances.md delete mode 100644 docs/readme.md delete mode 100644 readme.md diff --git a/docs/bliss_overview.md b/docs/bliss_overview.md deleted file mode 100644 index 146233b..0000000 --- a/docs/bliss_overview.md +++ /dev/null @@ -1,101 +0,0 @@ -# BLISS architecture overview - -![BLISS architecture](img/bliss_architecture.png) - -The system has 6 big components: - -* **Beacon**, the server that brings services for the whole system -* **communication objects** to deal with various protocols -* **hardware controllers** with ad hoc frameworks that can handle different kinds of equipment -* **scanning engine** - - continuous scans as first-class citizens - - acquisition chain -* **data management** - - publishing, for online data analysis and data visualization (**Flint** tool) - - **Nexus Writer**, tool from Data Analysis Unit that archives data in Nexus HDF5 format -* **BLISS shell** - - powerful command line interface for users - -![BLISS data flow](img/bliss_architecture.svg) - -## Beacon - -BLISS is built on top of [Beacon](beacon.md), which provides essential services for -the whole system: - -* beamline homepage -* configuration files hosting & housekeeping -* configuration editor -* log viewer -* data management, through the use of [Redis](http://redis.io) - - publishing of data produced during scans, for online data analysis, visualization or saving - - settings, i.e. runtime configuration properties - - channels, to exchange data between different BLISS objects in different processes - -### Beamline homepage - -Beacon offers a homepage to get access to the beamline web applications. On ESRF -beamlines, a web server is configured to display the homepage when pointing a -web browser to the main beamline computer. - -![Beamline homepage](img/beacon_homepage.png) - -### Configuration - -Beacon serves configuration files in [YAML format](http://yaml.org). - -YAML has been chosen because the standard native types (list, dictionary, numbers, unicode strings...) can be easily mapped to Python equivalents, it handles comments (contrary to JSON), and also because it is human-readable (contrary to XML). - -The set of YAML files provide a simple, yet flexible mechanism to describe devices within a BLISS system. - -#### YAML files - -![YAML tree example](img/yaml_tree.svg) - -Objects are identified in the system by an unique **name**. BLISS reserves the YAML key *name* as the entry point for an object configuration. -When loading static configuration data, Beacon goes all over the configuration database directories, and builds an internal representation of the objects that are defined in YAML mapping nodes from the files. Ultimately this structure is flattened and exposed as a Python dictionary with key-value pairs, keys being object names and values being the corresponding configuration information. - -The following YAML lines show an example of the configuration of a movable axis (motor) called `rotY`: - -```yaml -# motion.yml -class: IcePAP -host: iceid311 -plugin: emotion -axes: -- name: rotY - address: 3 - steps_per_unit: 100 - acceleration: 16.0 - velocity: 2.0 -``` - -The information contained in the YAML files is interpreted when the corresponding object is loaded. This interpretation depends on the type of object, in order to create the expected Python object. BLISS has **configuration plugins**, which can be used to extend supported object types. In the example above, the `emotion` (ESRF Motion) plugin is used to interpret the contents of the YAML file, in order to instantiate an `Axis` object. - -#### Configuration editor - -The configuration editor allows to browse through YAML files, and to edit them. - -![Configuration editor](img/beacon_config_app.png) - -### Log viewer - -Beacon centralizes log messages from BLISS applications. A simple web application allows to -look at the log messages. - -![Log viewer](img/beacon_log_viewer.png) - -## Sessions - -A BLISS session represents an experimental setup associated with experiment control sequences. - -A session has: - -* a list of objects from configuration -* a setup file - -Sessions can be nested, by specifying *include-session* in the session YAML configuration file. - -Sessions are loaded by the [BLISS shell](bliss_shell.md), giving access to experimental setup and associated procedures within an integrated command line interface. - - diff --git a/docs/config_elettra.md b/docs/config_elettra.md deleted file mode 100644 index 3cbfed3..0000000 --- a/docs/config_elettra.md +++ /dev/null @@ -1,60 +0,0 @@ -## Configuring an Elettra (or ePicea) 4 channels electrometer as a Counter Controller - -This section explains how to configure it to get BPM values as counters - -### Example YAML configuration: - -```yaml - -- class: Elettra - module: tango_elettra - name: el2 - uri: //id20ctrl2:20000/id20/elettra/ss1 - counters: - - counter_name: el2x - measure: Y - - counter_name: el2y - measure: Z - - counter_name: el2i - measure: integration_time - - counter_name: el2n - measure: samples_number - - counter_name: c1 - measure: current1 - - counter_name: c2 - measure: current2 - - counter_name: c3 - measure: current3 - - counter_name: c4 - measure: current4 - - counter_name: ctot - measure: current_total - -``` - -### Tango Server - -The Tango server package is called elettraAH in blissinstaller. -A conda package will be provided in the next future. - -http://wikiserv.esrf.fr/bliss/index.php/CAENels_picoammeter#Tango_Device_Server - -### Usage - -To setup the measuring range, give maximum current value you expect to measure in Amps: - - el2.range = 0.00025 - -It is recommended to setup measurement offsets too, for each different measuring range. One can specify the integration time, default is 1 sec. offset_reset command will remove any offset. - - el2.offset_measure (1) - - el2.offset_reset - - el2.offset - -### Restrictions - -Only AH501 models have been tested so far. AH401 and TetrAMM will come soon. - -## TO BE CONTINUED \ No newline at end of file diff --git a/docs/position_instances.md b/docs/position_instances.md new file mode 100644 index 0000000..e731fd4 --- /dev/null +++ b/docs/position_instances.md @@ -0,0 +1,125 @@ +# PositionInstances ID26 BLISS Class + + +Class provinding tools to store/retrieve positions of a set of axis along other user actions. +The positions are made persistant in time, so that they can be retrieved even in case of quitting bliss session. +They belong to one particular bliss session and cannot be shared between sessions. + +## Usage: + +* from id26.scripts.position_instances import PositionInstances + +* user_instance = PositionInstances ('instance_name' , mot1, mot2, mot3 ...) + - loads as 'user_instance' any previously initialised PositionInstances object with the name 'instance_name', or create it if it is the first time it is instantiated. + - if a list of axis is passed as arguments, slots are prepared for that set in redis database. + - if no axis is passed, slots are prepared for all the motors of the session. + +* user_instance.update () + - stores in 'user_instance' (named 'instance_name' in redis) the current positions of the motors in the set. + +* user_instance.update (mot1, mot2) + - stores in 'user_instance' the current positions of the listed motors only. + +* user_instance.show () + - shows all positions sets stored among their different existing instances in redis database. + - ** this is the user responsabilty to clear unused instances ** to free redis memory with the next command. + +* user_instance.remove_instance ('unwanted_instance_name') + - remove specified instance from the database. + +* user_instance.remove (mot1, mot2) + - remove axis slots from the instances. + +* user_instance.add (mot3, mot4, mot5) + - add some axis slots. do not forget to assign them their positions after. (.update(), .assign() or .setup()) + +* user_instance.assign (mot3, 3) + - assigns 'user_instance' a position value of 3 for axis mot3 + +* user_instance.setup () + - launches a dialog window to assign positions manually to 'user_instance' + +* user_instance.move (mot3, mot4) + - move all axis listed to the position that was stored for them in 'user_instance' + +* user_instance.move_all () + - move all axis of the set to the position that was stored (if any) for them in 'user_instance'. + +* user_instance.save (directory, suffix = '') + - saves to disk a yml files with all instances content. + - a directory must be specified. + - a suffix can be given, + - the filename is : {session}_positions{suffix}.yml + + +## Example: +'''python + +MCL [5]: pos0 = PositionInstances ('oldpos0', gap) +Initialising oldpos0 +axis names: ['gap'] + +MCL [6]: pos0.update() +updating oldpos0 with {'gap': 3.0} + +MCL [7]: #gap position is stored + +MCL [8]: user_script_aligning_mot1() +Moving gap from 3 to 10 +Aligning mot1 bla bla + +MCL [9]: pos1 = PositionInstances ('oldpos1',mot1) +Initialising oldpos1 +axis names: ['mot1'] + +MCL [10]: #a slot for mot1 is created, gap is kept + +MCL [11]: pos1.show() +* asterisks means value not stored in database (default is taken) +# hash means a computed attribute (property) + + oldpos1 (SELECTED) oldpos0 oldpos2 default +------------- -------------------- ------------------ ------------------ ------------------ + gap * None 3.0 3.0 None + mot1 * None * None * None None +creation_date # 2020-03-23-14:40 # 2020-03-23-14:38 # 2020-03-19-15:29 # 2020-03-19-15:29 +last_accessed # 2020-03-23-14:40 # 2020-03-23-14:39 # 2020-03-23-14:38 # 2020-03-19-15:29 + +MCL [12]: pos1.update() +updating oldpos1 with {'gap': 10.0, 'mot1': 29.0} + +MCL [13]: pos1.show() +* asterisks means value not stored in database (default is taken) +# hash means a computed attribute (property) + + oldpos1 (SELECTED) oldpos0 oldpos2 default +------------- -------------------- ------------------ ------------------ ------------------ + gap 10.0 3.0 3.0 None + mot1 29.0 * None * None None +creation_date # 2020-03-23-14:40 # 2020-03-23-14:38 # 2020-03-19-15:29 # 2020-03-19-15:29 +last_accessed # 2020-03-23-14:42 # 2020-03-23-14:39 # 2020-03-23-14:38 # 2020-03-19-15:29 + +MCL [14]: pos0.move(gap) +Moving gap from 10 to 3 + +MCL [15]: #gap moved back to pos0 stored position + +MCL [16]: #As I dont need oldpos2 instance, I will clear it + +MCL [17]: pos0.remove_instance('oldpos2') +oldpos2 removed. + +MCL [18]: pos0.show() +* asterisks means value not stored in database (default is taken) +# hash means a computed attribute (property) + + oldpos0 (SELECTED) oldpos1 default +------------- -------------------- ------------------ ------------------ + gap 10.0 10.0 None + mot1 29.0 29.0 None +creation_date # 2020-03-23-14:38 # 2020-03-23-14:40 # 2020-03-19-15:29 +last_accessed # 2020-03-23-14:53 # 2020-03-23-14:52 # 2020-03-19-15:29 + +MCL [19]: user_script_aligning_continue() + +''' diff --git a/docs/readme.md b/docs/readme.md deleted file mode 100644 index e8f5d61..0000000 --- a/docs/readme.md +++ /dev/null @@ -1,3 +0,0 @@ -# README test - -hellooooo from docs \ No newline at end of file diff --git a/readme.md b/readme.md deleted file mode 100644 index c31ef8f..0000000 --- a/readme.md +++ /dev/null @@ -1,3 +0,0 @@ -# README test - -hellooooo from id26 \ No newline at end of file -- GitLab From 8d3db95567204ff4b2e63299fb7fc540f4358c31 Mon Sep 17 00:00:00 2001 From: Marie Claire Lagier Date: Mon, 23 Mar 2020 15:20:41 +0100 Subject: [PATCH 25/27] Update position_instances.md --- docs/position_instances.md | 170 +++++++++++++++++++------------------ 1 file changed, 86 insertions(+), 84 deletions(-) diff --git a/docs/position_instances.md b/docs/position_instances.md index e731fd4..b1fc4fa 100644 --- a/docs/position_instances.md +++ b/docs/position_instances.md @@ -2,50 +2,63 @@ Class provinding tools to store/retrieve positions of a set of axis along other user actions. + The positions are made persistant in time, so that they can be retrieved even in case of quitting bliss session. + They belong to one particular bliss session and cannot be shared between sessions. ## Usage: -* from id26.scripts.position_instances import PositionInstances +* **from id26.scripts.position_instances import PositionInstances** -* user_instance = PositionInstances ('instance_name' , mot1, mot2, mot3 ...) +* **user_instance = PositionInstances ('instance_name' , mot1, mot2, mot3 ...)** - loads as 'user_instance' any previously initialised PositionInstances object with the name 'instance_name', or create it if it is the first time it is instantiated. - if a list of axis is passed as arguments, slots are prepared for that set in redis database. - if no axis is passed, slots are prepared for all the motors of the session. -* user_instance.update () + +* **user_instance.update ()** - stores in 'user_instance' (named 'instance_name' in redis) the current positions of the motors in the set. -* user_instance.update (mot1, mot2) + +* **user_instance.update (mot1, mot2)** - stores in 'user_instance' the current positions of the listed motors only. -* user_instance.show () + +* **user_instance.show ()** - shows all positions sets stored among their different existing instances in redis database. - - ** this is the user responsabilty to clear unused instances ** to free redis memory with the next command. + - **this is the user responsabilty to clear unused instances** to free redis memory with the next command. + -* user_instance.remove_instance ('unwanted_instance_name') +* **user_instance.remove_instance ('unwanted_instance_name')** - remove specified instance from the database. -* user_instance.remove (mot1, mot2) + +* **user_instance.remove (mot1, mot2)** - remove axis slots from the instances. -* user_instance.add (mot3, mot4, mot5) + +* **user_instance.add (mot3, mot4, mot5)** - add some axis slots. do not forget to assign them their positions after. (.update(), .assign() or .setup()) + -* user_instance.assign (mot3, 3) +* **user_instance.assign (mot3, 3)** - assigns 'user_instance' a position value of 3 for axis mot3 -* user_instance.setup () + +* **user_instance.setup ()** - launches a dialog window to assign positions manually to 'user_instance' -* user_instance.move (mot3, mot4) + +* **user_instance.move (mot3, mot4)** - move all axis listed to the position that was stored for them in 'user_instance' -* user_instance.move_all () + +* **user_instance.move_all ()** - move all axis of the set to the position that was stored (if any) for them in 'user_instance'. -* user_instance.save (directory, suffix = '') + +* **user_instance.save (directory, suffix = '')** - saves to disk a yml files with all instances content. - a directory must be specified. - a suffix can be given, @@ -53,73 +66,62 @@ They belong to one particular bliss session and cannot be shared between session ## Example: -'''python - -MCL [5]: pos0 = PositionInstances ('oldpos0', gap) -Initialising oldpos0 -axis names: ['gap'] - -MCL [6]: pos0.update() -updating oldpos0 with {'gap': 3.0} - -MCL [7]: #gap position is stored - -MCL [8]: user_script_aligning_mot1() -Moving gap from 3 to 10 -Aligning mot1 bla bla - -MCL [9]: pos1 = PositionInstances ('oldpos1',mot1) -Initialising oldpos1 -axis names: ['mot1'] - -MCL [10]: #a slot for mot1 is created, gap is kept - -MCL [11]: pos1.show() -* asterisks means value not stored in database (default is taken) -# hash means a computed attribute (property) - - oldpos1 (SELECTED) oldpos0 oldpos2 default -------------- -------------------- ------------------ ------------------ ------------------ - gap * None 3.0 3.0 None - mot1 * None * None * None None -creation_date # 2020-03-23-14:40 # 2020-03-23-14:38 # 2020-03-19-15:29 # 2020-03-19-15:29 -last_accessed # 2020-03-23-14:40 # 2020-03-23-14:39 # 2020-03-23-14:38 # 2020-03-19-15:29 - -MCL [12]: pos1.update() -updating oldpos1 with {'gap': 10.0, 'mot1': 29.0} - -MCL [13]: pos1.show() -* asterisks means value not stored in database (default is taken) -# hash means a computed attribute (property) - - oldpos1 (SELECTED) oldpos0 oldpos2 default -------------- -------------------- ------------------ ------------------ ------------------ - gap 10.0 3.0 3.0 None - mot1 29.0 * None * None None -creation_date # 2020-03-23-14:40 # 2020-03-23-14:38 # 2020-03-19-15:29 # 2020-03-19-15:29 -last_accessed # 2020-03-23-14:42 # 2020-03-23-14:39 # 2020-03-23-14:38 # 2020-03-19-15:29 - -MCL [14]: pos0.move(gap) -Moving gap from 10 to 3 - -MCL [15]: #gap moved back to pos0 stored position - -MCL [16]: #As I dont need oldpos2 instance, I will clear it - -MCL [17]: pos0.remove_instance('oldpos2') -oldpos2 removed. - -MCL [18]: pos0.show() -* asterisks means value not stored in database (default is taken) -# hash means a computed attribute (property) - - oldpos0 (SELECTED) oldpos1 default -------------- -------------------- ------------------ ------------------ - gap 10.0 10.0 None - mot1 29.0 29.0 None -creation_date # 2020-03-23-14:38 # 2020-03-23-14:40 # 2020-03-19-15:29 -last_accessed # 2020-03-23-14:53 # 2020-03-23-14:52 # 2020-03-19-15:29 - -MCL [19]: user_script_aligning_continue() - -''' +
+  MCL [5]: pos0 = PositionInstances ('oldpos0', gap)
+  Initialising oldpos0
+  axis names: ['gap']
+  
+  MCL [6]: pos0.update()
+  updating oldpos0 with {'gap': 3.0}
+  
+  MCL [7]: #gap position is stored
+  
+  MCL [8]: user_script_aligning_mot1()
+  Moving gap from 3 to 10
+  Aligning mot1 bla bla
+  
+  MCL [9]: pos1 = PositionInstances ('oldpos1',mot1)
+  Initialising oldpos1
+  axis names: ['mot1']
+  
+  MCL [10]: #a slot for mot1 is created, gap is kept
+  
+  MCL [11]: pos1.show()
+ 
+                   oldpos1 (SELECTED)             oldpos0             oldpos2             default
+            gap                * None                 3.0                 3.0                None
+           mot1                * None              * None              * None                None
+  creation_date    # 2020-03-23-14:40  # 2020-03-23-14:38  # 2020-03-19-15:29  # 2020-03-19-15:29
+  last_accessed    # 2020-03-23-14:40  # 2020-03-23-14:39  # 2020-03-23-14:38  # 2020-03-19-15:29
+  
+  MCL [12]: pos1.update()
+  updating oldpos1 with {'gap': 10.0, 'mot1': 29.0}
+  
+  MCL [13]: pos1.show()
+ 
+                   oldpos1 (SELECTED)             oldpos0             oldpos2             default
+            gap                  10.0                 3.0                 3.0                None
+           mot1                  29.0              * None              * None                None
+  creation_date    # 2020-03-23-14:40  # 2020-03-23-14:38  # 2020-03-19-15:29  # 2020-03-19-15:29
+  last_accessed    # 2020-03-23-14:42  # 2020-03-23-14:39  # 2020-03-23-14:38  # 2020-03-19-15:29
+  
+  MCL [14]: pos0.move(gap)
+  Moving gap from 10 to 3
+  
+  MCL [15]: #gap moved back to pos0 stored position
+  
+  MCL [16]: #As I dont need oldpos2 instance, I will clear it
+  
+  MCL [17]: pos0.remove_instance('oldpos2')
+  oldpos2 removed.
+  
+  MCL [18]: pos0.show()
+  
+                   oldpos0 (SELECTED)             oldpos1             default
+            gap                  10.0                10.0                None
+           mot1                  29.0                29.0                None
+  creation_date    # 2020-03-23-14:38  # 2020-03-23-14:40  # 2020-03-19-15:29
+  last_accessed    # 2020-03-23-14:53  # 2020-03-23-14:52  # 2020-03-19-15:29
+  
+  MCL [19]: user_script_aligning_continue()
+
\ No newline at end of file -- GitLab From d63f0b9e9da8838b795fb5c788989d4e8c86702c Mon Sep 17 00:00:00 2001 From: lagier Date: Wed, 25 Mar 2020 16:52:51 +0100 Subject: [PATCH 26/27] PositionInstances.load() created - not in doc yet --- id26/scripts/position_instances.py | 33 ++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/id26/scripts/position_instances.py b/id26/scripts/position_instances.py index 57f62dd..4eac71c 100644 --- a/id26/scripts/position_instances.py +++ b/id26/scripts/position_instances.py @@ -6,7 +6,7 @@ from bliss.shell.cli.pt_widgets import BlissDialog, button_dialog from id26.scripts.beamline_parameters import ID26Parameters, switch_instance -# TODO NOT URGENT list below + class ID26Positions(ID26Parameters): @@ -164,7 +164,7 @@ class ID26Positions(ID26Parameters): - parameters taken from default are identified with an * (asterisk) - responsabilty of user to clear unused instances to free redis mem """ - # TODO NOT URGENT do sth more relevant for the positions. + self.parameters.show_table() @@ -182,6 +182,30 @@ class ID26Positions(ID26Parameters): print ('Saving instances of saved positions on: {0}'.format(file_name)) + @switch_instance + def load(self, directory, suffix=''): + """ + loads from a previously saved file + directory must be specified + old1.load (directory) + loads only position for instance referenced by the calling object. (old1 in the example) + """ + if self.param_name != None and len(directory)>1: + file_name = self.param_name + suffix + '.yml' + sep = '' if directory [-1] == '/' else '/' + file_name = directory + sep + file_name + + #self.parameters,from_file (file_name, instance_name = self.instance_name) does not work + + with open(file_name) as file: + try: + self.parameters._from_yml(file.read(), instance_name=self.instance_name) + print ("Loading instance '{0}' of saved positions saved from '{1}'".format(self.instance_name, file_name)) + except KeyError: + print ("Can't find instance '{0}' in file '{1}'" .format(self.instance_name, file_name)) + + + class PositionInstances(ID26Positions): """ @@ -247,6 +271,11 @@ class PositionInstances(ID26Positions): a suffix can be given, the filename is : {session}_positions{suffix}.yml + * user_instance.load (directory, suffix = '') + + loads from a previously saved file + directory must be specified + loads only position for instance referenced by the calling object. Example: -- GitLab From 07185f1466c9f9d167e37abd0931b0a25d3c54e3 Mon Sep 17 00:00:00 2001 From: lagier Date: Thu, 26 Mar 2020 14:24:56 +0100 Subject: [PATCH 27/27] position_instances doc --- docs/position_instances.md | 211 +++++++++++++---------------- id26/scripts/position_instances.py | 96 +++++-------- 2 files changed, 133 insertions(+), 174 deletions(-) diff --git a/docs/position_instances.md b/docs/position_instances.md index e731fd4..154afca 100644 --- a/docs/position_instances.md +++ b/docs/position_instances.md @@ -1,125 +1,106 @@ # PositionInstances ID26 BLISS Class - - Class provinding tools to store/retrieve positions of a set of axis along other user actions. The positions are made persistant in time, so that they can be retrieved even in case of quitting bliss session. They belong to one particular bliss session and cannot be shared between sessions. - ## Usage: - -* from id26.scripts.position_instances import PositionInstances - -* user_instance = PositionInstances ('instance_name' , mot1, mot2, mot3 ...) - - loads as 'user_instance' any previously initialised PositionInstances object with the name 'instance_name', or create it if it is the first time it is instantiated. - - if a list of axis is passed as arguments, slots are prepared for that set in redis database. +- **from id26.scripts.position_instances import PositionInstances** +- **user_instance = PositionInstances ('instance_name' , mot1, mot2, mot3 ...)** + loads as 'user_instance' session object any previously initialised PositionInstances with the name 'instance_name', or create it if it is the first time it is instantiated. + - if a list of axis is passed as arguments, slots are prepared for that set of axis in redis database. - if no axis is passed, slots are prepared for all the motors of the session. - -* user_instance.update () - - stores in 'user_instance' (named 'instance_name' in redis) the current positions of the motors in the set. - -* user_instance.update (mot1, mot2) - - stores in 'user_instance' the current positions of the listed motors only. - -* user_instance.show () - - shows all positions sets stored among their different existing instances in redis database. - - ** this is the user responsabilty to clear unused instances ** to free redis memory with the next command. - -* user_instance.remove_instance ('unwanted_instance_name') - - remove specified instance from the database. - -* user_instance.remove (mot1, mot2) - - remove axis slots from the instances. - -* user_instance.add (mot3, mot4, mot5) - - add some axis slots. do not forget to assign them their positions after. (.update(), .assign() or .setup()) - -* user_instance.assign (mot3, 3) - - assigns 'user_instance' a position value of 3 for axis mot3 - -* user_instance.setup () - - launches a dialog window to assign positions manually to 'user_instance' - -* user_instance.move (mot3, mot4) - - move all axis listed to the position that was stored for them in 'user_instance' - -* user_instance.move_all () - - move all axis of the set to the position that was stored (if any) for them in 'user_instance'. - -* user_instance.save (directory, suffix = '') - - saves to disk a yml files with all instances content. + +- **user_instance.update ()** + stores in 'user_instance' object (attached to 'instance_name' in redis) the current positions of the axis in the set. +- **user_instance.update (mot1, mot2)** + stores in 'user_instance' object the current positions of the listed axis only. +- **user_instance.assign (mot3, 3.21)** + stores in 'user_instance' the specified position value for the specified axis. value can be None. +- **user_instance.setup ()** + launches a dialog window to enter positions to be stored in 'user_instance'. Position can be None +- **user_instance.move (mot3, mot4)** + move all axis listed to the position that was stored (if any) for them in 'user_instance' +- **user_instance.move_all ()** + move all axis of the set to the position that was stored (if any) for them in 'user_instance'. +- **user_instance.load (directory, suffix='')** + stores positions read from a previously saved file (see .save() below) + - directory (path) must be specified + - a suffix can be given, the filename is : {session}_positions{suffix}.yml + +The following functions act on all the position instances existing in redis database. The behaviour will be the same whatever is the session object calling it. +- **user_instance.show ()** + shows all positions sets stored among their different existing instances in redis database. It might happen that some instances are no longer useful. **note that this is the user responsibility to clear unused instances** to free redis memory with the next command. +- **user_instance.remove_instance ('unwanted_instance_name')** +remove specified instance from the database. + +- **user_instance.add (mot3, mot4, mot5)** + add some axis slots. do not forget to assign them some positions after. (.update(), .assign() or .setup()). +- **user_instance.remove (mot1, mot2)** + remove axis slots from the instances. + +- **user_instance.save (directory, suffix = '')** + saves to disk a yml files with all instances content. - a directory must be specified. - a suffix can be given, - - the filename is : {session}_positions{suffix}.yml - - + - the filename is : {session}_positions{suffix}.yml + ## Example: -'''python - -MCL [5]: pos0 = PositionInstances ('oldpos0', gap) -Initialising oldpos0 -axis names: ['gap'] - -MCL [6]: pos0.update() -updating oldpos0 with {'gap': 3.0} - -MCL [7]: #gap position is stored - -MCL [8]: user_script_aligning_mot1() -Moving gap from 3 to 10 -Aligning mot1 bla bla - -MCL [9]: pos1 = PositionInstances ('oldpos1',mot1) -Initialising oldpos1 -axis names: ['mot1'] - -MCL [10]: #a slot for mot1 is created, gap is kept - -MCL [11]: pos1.show() -* asterisks means value not stored in database (default is taken) -# hash means a computed attribute (property) - - oldpos1 (SELECTED) oldpos0 oldpos2 default -------------- -------------------- ------------------ ------------------ ------------------ - gap * None 3.0 3.0 None - mot1 * None * None * None None -creation_date # 2020-03-23-14:40 # 2020-03-23-14:38 # 2020-03-19-15:29 # 2020-03-19-15:29 -last_accessed # 2020-03-23-14:40 # 2020-03-23-14:39 # 2020-03-23-14:38 # 2020-03-19-15:29 - -MCL [12]: pos1.update() -updating oldpos1 with {'gap': 10.0, 'mot1': 29.0} - -MCL [13]: pos1.show() -* asterisks means value not stored in database (default is taken) -# hash means a computed attribute (property) - - oldpos1 (SELECTED) oldpos0 oldpos2 default -------------- -------------------- ------------------ ------------------ ------------------ - gap 10.0 3.0 3.0 None - mot1 29.0 * None * None None -creation_date # 2020-03-23-14:40 # 2020-03-23-14:38 # 2020-03-19-15:29 # 2020-03-19-15:29 -last_accessed # 2020-03-23-14:42 # 2020-03-23-14:39 # 2020-03-23-14:38 # 2020-03-19-15:29 - -MCL [14]: pos0.move(gap) -Moving gap from 10 to 3 - -MCL [15]: #gap moved back to pos0 stored position - -MCL [16]: #As I dont need oldpos2 instance, I will clear it - -MCL [17]: pos0.remove_instance('oldpos2') -oldpos2 removed. - -MCL [18]: pos0.show() -* asterisks means value not stored in database (default is taken) -# hash means a computed attribute (property) - - oldpos0 (SELECTED) oldpos1 default -------------- -------------------- ------------------ ------------------ - gap 10.0 10.0 None - mot1 29.0 29.0 None -creation_date # 2020-03-23-14:38 # 2020-03-23-14:40 # 2020-03-19-15:29 -last_accessed # 2020-03-23-14:53 # 2020-03-23-14:52 # 2020-03-19-15:29 - -MCL [19]: user_script_aligning_continue() +
+ MCL [5]: pos0 = PositionInstances ('oldpos0', gap)
+ Initialising oldpos0
+ axis names: ['gap']
+  
+ MCL [6]: pos0.update()
+ updating oldpos0 with {'gap': 3.0}
+  
+ MCL [7]: #gap position is stored
+  
+ MCL [8]: user_script_aligning_mot1()
+ Moving gap from 3 to 10
+ Aligning mot1 bla bla
+  
+ MCL [9]: pos1 = PositionInstances ('oldpos1',mot1)
+ Initialising oldpos1
+ axis names: ['mot1']
+  
+ MCL [10]: #a slot for mot1 is created, gap is kept
+  
+ MCL [11]: pos1.show()
+  
+ oldpos1 (SELECTED)             oldpos0             oldpos2             default
+ gap          None                 3.0                 3.0                None
+ mot1         None                None                None                None
+ creation_date    # 2020-03-23-14:40  # 2020-03-23-14:38  # 2020-03-19-15:29  # 2020-03-19-15:29
+ last_accessed    # 2020-03-23-14:40  # 2020-03-23-14:39  # 2020-03-23-14:38  # 2020-03-19-15:29
+  
+ MCL [12]: pos1.update()
+ updating oldpos1 with {'gap': 10.0, 'mot1': 29.0}
+  
+ MCL [13]: pos1.show()
+  
+ oldpos1 (SELECTED)             oldpos0             oldpos2             default
+ gap           10.0                3.0                 3.0                None
+ mot1          29.0               None                None                None
+ creation_date    # 2020-03-23-14:40  # 2020-03-23-14:38  # 2020-03-19-15:29  # 2020-03-19-15:29
+ last_accessed    # 2020-03-23-14:42  # 2020-03-23-14:39  # 2020-03-23-14:38  # 2020-03-19-15:29
+  
+ MCL [14]: pos0.move(gap)
+ Moving gap from 10 to 3
+  
+ MCL [15]: #gap moved back to pos0 stored position
+  
+ MCL [16]: #As I dont need oldpos2 instance, I will clear it
+  
+ MCL [17]: pos0.remove_instance('oldpos2')
+ oldpos2 removed.
+  
+ MCL [18]: pos0.show()
+  
+ oldpos0 (SELECTED)             oldpos1             default
+ gap          10.0                10.0                None
+ mot1         29.0                29.0                None
+ creation_date    # 2020-03-23-14:38  # 2020-03-23-14:40  # 2020-03-19-15:29
+ last_accessed    # 2020-03-23-14:53  # 2020-03-23-14:52  # 2020-03-19-15:29
+  
+ MCL [19]: user_script_aligning_c[ontinue()
+
-''' diff --git a/id26/scripts/position_instances.py b/id26/scripts/position_instances.py index 4eac71c..651b46b 100644 --- a/id26/scripts/position_instances.py +++ b/id26/scripts/position_instances.py @@ -215,67 +215,45 @@ class PositionInstances(ID26Positions): Usage: - * user_instance = PositionInstances ('instance_name' , mot1, mot2, mot3 ...) - - loads as user_instance any previously initialised PositionInstances object with the name 'instance_name', - or create it if it is the first time it is instantiated. - - if a list of axis is passed as arguments, slots are prepared for that set in redis database. - if no axis is passed, slots are prepared for all the motors of the session. - - * user_instance.update () - - stores in user_instance (named 'instance_name' in redis) the current positions of the motors in the set. - - * user_instance.update (mot1, mot2) - - stores in user_instance the current positions of the listed motors only. - - * user_instance.show () - - shows all positions sets stored among their different existing instances in redis database. - this is the responsabilty of user to clear unused instances to free redis memory with the next command. - - * user_instance.remove_instance ('unwanted_instance_name') - - remove specified instance from the database. - - * user_instance.remove (mot1, mot2) - - remove axis slots from the instances. - - * user_instance.add (mot3, mot4, mot5) - - add some axis slots. do not forget to assign them their positions after. (.update(), .assign() or .setup()) - - * user_instance.assign (mot3, 3) + - **user_instance = PositionInstances ('instance_name' , mot1, mot2, mot3 ...)** + loads as 'user_instance' session object any previously initialised PositionInstances with the name 'instance_name', or create it if it is the first time it is instantiated. + - if a list of axis is passed as arguments, slots are prepared for that set of axis in redis database. + - if no axis is passed, slots are prepared for all the motors of the session. - assigns user_instance a position value of 3 for axis mot3 - - * user_instance.setup () - - launches a dialog window to assign positions manually to user_instance - - * user_instance.move (mot3, mot4) - - move all axis listed to the position that was stored for them in user_instance - - * user_instance.move_all () - - move all axis of the set to the position that was stored (if any) for them in user_instance. - - * user_instance.save (directory, suffix = '') - - saves to disk a yml files with all instances content. - a directory must be specified. - a suffix can be given, - the filename is : {session}_positions{suffix}.yml - - * user_instance.load (directory, suffix = '') + - **user_instance.update ()** + stores in 'user_instance' object (attached to 'instance_name' in redis) the current positions of the axis in the set. + - **user_instance.update (mot1, mot2)** + stores in 'user_instance' object the current positions of the listed axis only. + - **user_instance.assign (mot3, 3.21)** + stores in 'user_instance' the specified position value for the specified axis. value can be None. + - **user_instance.setup ()** + launches a dialog window to enter positions to be stored in 'user_instance'. Position can be None + - **user_instance.move (mot3, mot4)** + move all axis listed to the position that was stored (if any) for them in 'user_instance' + - **user_instance.move_all ()** + move all axis of the set to the position that was stored (if any) for them in 'user_instance'. + - **user_instance.load (directory, suffix='')** + stores positions read from a previously saved file (see .save() below) + - directory (path) must be specified + - a suffix can be given, the filename is : {session}_positions{suffix}.yml + + The following functions act on all the position instances existing in redis database. The behaviour will be the same whatever is the session object calling it. + - **user_instance.show ()** + shows all positions sets stored among their different existing instances in redis database. It might happen that some instances are no longer useful. **note that this is the user responsibility to clear unused instances** to free redis memory with the next command. + - **user_instance.remove_instance ('unwanted_instance_name')** + remove specified instance from the database. + + - **user_instance.add (mot3, mot4, mot5)** + add some axis slots. do not forget to assign them some positions after. (.update(), .assign() or .setup()). + - **user_instance.remove (mot1, mot2)** + remove axis slots from the instances. + + - **user_instance.save (directory, suffix = '')** + saves to disk a yml files with all instances content. + - a directory must be specified. + - a suffix can be given, + - the filename is : {session}_positions{suffix}.yml - loads from a previously saved file - directory must be specified - loads only position for instance referenced by the calling object. Example: -- GitLab