From 553225e68cdcc0b3a30c5026babb91ae014308bd Mon Sep 17 00:00:00 2001 From: Moritz Koenemann Date: Fri, 16 Sep 2022 11:49:25 +0200 Subject: [PATCH 1/2] Sketch new API in 'measurement.py'. --- pytrms/measurement.py | 75 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/pytrms/measurement.py b/pytrms/measurement.py index 6a5ea71..5891d0a 100644 --- a/pytrms/measurement.py +++ b/pytrms/measurement.py @@ -6,6 +6,8 @@ class Measurement(Iterable): """Base class for PTRMS-measurements or batch processing. + ---------- OLD ideas ------------ + Every instance is associated with exactly one `.filename` to a datafile. The start time of the measurement is given by `.timezero`. @@ -13,6 +15,79 @@ class Measurement(Iterable): In the online case, this would slowly produce the current trace, one after another. In the offline case, this would quickly iterate over the traces in the given measurement file. + + ---------- NEW ideas ------------ + + A 'Measurement'... + - comprises of one or more consecutive datafiles (i.e. there is no gap in time). + - is in one of three states: Preparing, Running, Finished. + - can be scripted while preparing, but cannot be changed once running (compare this + to an 'Instrument', which remains in control as long as the connection is upheld). + - only one 'Measurement' can run on one 'Instrument' at a time (but several can be + queued). + + + >>> ptr = connect('localhost') + >>> m = Measurement(ptr) # no! + >>> m = Measurement() + + >>> m.run(ptr) # ? + >>> ptr.run(m) # ! + + >>> ptr.run_repeated(m, 5) + >>> ptr.run([m, m2, meas, Measurement()]) + >>> ptr.run() + >>> ptr.run_quick() + + + >>> m.datafiles + [] + + >>> m.current_file # not accessible... + >>> ptr.current_file + None + + >>> m.wait() # doesn't make sense + AttributeError + + >>> ptr.wait_until(cycle=123) # blocks until cycle 123 comes along (raise if that's not going to happen) + ... + >>> ptr.wait_for(cycles=123) # blocks for 123 cycles... + ... + + >>> m.schedule({'DPS_Udrift': 432}, cycle=15, repeat_every=15, repeat_until=60 [,repeat_for=4]) + >>> m.generate_schedule() + + + >>> list(m.generate_schedule()) + {'cycle': 15, 'updates': {'DPS_Udrift': 432}} + {'cycle': 30, 'updates': {'DPS_Udrift': 432}} + {'cycle': 45, 'updates': {'DPS_Udrift': 432}} + {'cycle': 60, 'updates': {'DPS_Udrift': 432}} + + When the measurement has been prepared, let the measurement run. This will + block the execution of the script at this point! Internally, the control is + given to the 'Instrument': + + >>> m.run_forever() # call this only when the measurement has been prepared + ... + >>> m.run() # this implies that the Measurement is predefined + >>> m # the Instrument is not Busy, the Measurement is!! + Running + >>> m.run_for(seconds=120) # blocks for two minutes! + ... + + # .. or the measurementwas stopped for some reason + + + >>> ptr.get_schedule() # same in green, but read out the instrument buffer + + >>> m.blocks(marker='AddData/PTR_Instrument/DPS_Udrift') # every set of instrument parameters forms a 'block', until any parameter changes + + + >>> m.blocks(marker=pytrms.markers.Periodic(20)) + + """ def is_local(self): From ba8f68e69adb753ab1b585c5d561bd1039a22193 Mon Sep 17 00:00:00 2001 From: Moritz Koenemann Date: Fri, 16 Sep 2022 13:07:58 +0200 Subject: [PATCH 2/2] Flesh out the 'Measurement' API -- give execution to 'Instrument'. --- pytrms/measurement.py | 71 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 13 deletions(-) diff --git a/pytrms/measurement.py b/pytrms/measurement.py index 5891d0a..ee2eac5 100644 --- a/pytrms/measurement.py +++ b/pytrms/measurement.py @@ -19,12 +19,13 @@ class Measurement(Iterable): ---------- NEW ideas ------------ A 'Measurement'... - - comprises of one or more consecutive datafiles (i.e. there is no gap in time). - - is in one of three states: Preparing, Running, Finished. - - can be scripted while preparing, but cannot be changed once running (compare this + - ..comprises of one or more consecutive datafiles (i.e. there is no gap in time). + - ..is in one of three states: Preparing, Running, Finished. + - ..can be scripted while preparing, but cannot be changed once running (compare this to an 'Instrument', which remains in control as long as the connection is upheld). - - only one 'Measurement' can run on one 'Instrument' at a time (but several can be + - Only one 'Measurement' can run on one 'Instrument' at a time (but several can be queued). + - ..generates a schedule of parameter changes. >>> ptr = connect('localhost') @@ -55,29 +56,73 @@ class Measurement(Iterable): >>> ptr.wait_for(cycles=123) # blocks for 123 cycles... ... - >>> m.schedule({'DPS_Udrift': 432}, cycle=15, repeat_every=15, repeat_until=60 [,repeat_for=4]) + >>> m.schedule({'DPS_Udrift': 432}, cycle=0, repeat_every=15, repeat_until=60 [,repeat_for=4]) # example... >>> m.generate_schedule() >>> list(m.generate_schedule()) - {'cycle': 15, 'updates': {'DPS_Udrift': 432}} - {'cycle': 30, 'updates': {'DPS_Udrift': 432}} - {'cycle': 45, 'updates': {'DPS_Udrift': 432}} - {'cycle': 60, 'updates': {'DPS_Udrift': 432}} + {'cycle': 15, 'updates': {'DPS_Udrift': 432}, 'block': 1} + {'cycle': 30, 'updates': {'DPS_Udrift': 432}, 'block': 1} + {'cycle': 45, 'updates': {'DPS_Udrift': 432}, 'block': 1} + {'cycle': 60, 'updates': {'DPS_Udrift': 432}, 'block': 1} + + # clear schedule(?) makes no sense! + + >>> m.schedule({'DPS_Udrift': 600}, cycle= 0, repeat_every=20) + >>> m.schedule({'DPS_Udrift': 300}, cycle=10, repeat_every=20, tag='H3O+') + >>> list(take(4, m.generate_schedule())) + {'cycle': 0, 'updates': {'DPS_Udrift': 600}, 'block': 1} + {'cycle': 10, 'updates': {'DPS_Udrift': 300}, 'block': 'H3O+'} + {'cycle': 20, 'updates': {'DPS_Udrift': 600}, 'block': 1} + {'cycle': 30, 'updates': {'DPS_Udrift': 300}, 'block': 'H3O+'} + ... + + [given a schedule like this, the 'Instrument' can already do its thing!] + + >>> m.blocks() + [, ] + + # the following has the same effect, but requires less brain damage to grasp: + + >>> block1 = pytrms.tools.Block({'DPS_Udrift': 600}, duration=10, tag=1) + >>> block2 = pytrms.tools.Block({'DPS_Udrift': 300}, duration=10, tag='H3O+') + >>> m.schedule(block1) + >>> m.schedule(block2) # just override the above definitions + + this restarts the schedule as defined up to this point + >>> m.repeat_forever() # no! -> see ptr.run(..) instead + >>> m.repeat(5) # no! + + instead: + >>> for i in range(5): + >>> m.schedule(block1) + >>> m.schedule(block2) + + [rough idea] + Blocks will be averaged automatically by "some server". But this is the other + side of the lawn, the 'E' part, whereas we are at the 'A' part and the + 'Instrument' connects to the 'M' part. In fact, the 'Instrument' (or whatever + executor runs in the background) can connect to the 'E' part from here as well! + That would mean, we could install a callback or a notification or something in + the future... go full cycle and give feedback to the TPS-parameters! + All good and well. The 'Measurement' is only a convenience wrapper after all. + Let's implement the tough part - the 'Instrument' - first and see... When the measurement has been prepared, let the measurement run. This will block the execution of the script at this point! Internally, the control is given to the 'Instrument': - >>> m.run_forever() # call this only when the measurement has been prepared + >>> m.run(m) # run the Measurement once... + >>> m.run_forever(m) # ...or repeat the schedule forever... ... - >>> m.run() # this implies that the Measurement is predefined >>> m # the Instrument is not Busy, the Measurement is!! Running - >>> m.run_for(seconds=120) # blocks for two minutes! + >>> m.run_for(seconds=120) # blocks for two minutes + >>> m.run_for(cycles=120) # blocks for 120 cycles + >>> m.run_for(reps=5) # blocks for five repetitions ... - # .. or the measurementwas stopped for some reason + # .. or the measurement was stopped for some reason >>> ptr.get_schedule() # same in green, but read out the instrument buffer