Add option to stop when the script ends
This commit is contained in:
parent
1e46aaa176
commit
154235dbe5
@ -38,10 +38,12 @@ class LedScriptUpdateHandler(FileSystemEventHandler):
|
|||||||
|
|
||||||
|
|
||||||
class LedScript:
|
class LedScript:
|
||||||
|
"""
|
||||||
|
Class representing a script to control the state of a LED
|
||||||
|
"""
|
||||||
ARRAY_DTYPE = [("dt", "f8"), ("dtsum", "f8"), ("led", "i4"), ("line", "i4")]
|
ARRAY_DTYPE = [("dt", "f8"), ("dtsum", "f8"), ("led", "i4"), ("line", "i4")]
|
||||||
def __init__(self, script:np.ndarray|str|int=0, auto_update=False, verbose=False):
|
def __init__(self, script:np.ndarray|str|int=0, auto_update=False, verbose=False):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
script : np.ndarray|str|int
|
script : np.ndarray|str|int
|
||||||
@ -96,6 +98,21 @@ class LedScript:
|
|||||||
self.t_start = time.time()
|
self.t_start = time.time()
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
def is_done(self, t: None|float=None) -> bool:
|
||||||
|
"""
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
t : None|float, optional
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
bool
|
||||||
|
Whether <t> is past the script length
|
||||||
|
"""
|
||||||
|
if t is None: t = time.time()
|
||||||
|
return t >= self.script["dtsum"][-1]
|
||||||
|
|
||||||
|
|
||||||
def get_state(self, t: None|float=None) -> int:
|
def get_state(self, t: None|float=None) -> int:
|
||||||
"""
|
"""
|
||||||
Get the LED statefrom an absolute time (relative to when `start` was called)
|
Get the LED statefrom an absolute time (relative to when `start` was called)
|
||||||
@ -248,12 +265,12 @@ class LedScript:
|
|||||||
# for just checking scripts, with ignore_errors=True this function returns all errors in an array
|
# for just checking scripts, with ignore_errors=True this function returns all errors in an array
|
||||||
errors = []
|
errors = []
|
||||||
if ignore_errors:
|
if ignore_errors:
|
||||||
def raiseError(*args, **kwargs):
|
def raise_error(*args, **kwargs):
|
||||||
e = InvalidScript(*args, **kwargs)
|
e = InvalidScript(*args, **kwargs)
|
||||||
errors.append(e)
|
errors.append(e)
|
||||||
print(e)
|
print(e)
|
||||||
else:
|
else:
|
||||||
def raiseError(*args, **kwargs):
|
def raise_error(*args, **kwargs):
|
||||||
raise InvalidScript(*args, **kwargs)
|
raise InvalidScript(*args, **kwargs)
|
||||||
|
|
||||||
# parse into <cumulative t in seconds, t state in seconds, state>
|
# parse into <cumulative t in seconds, t state in seconds, state>
|
||||||
@ -266,13 +283,13 @@ class LedScript:
|
|||||||
# 1) Separate statements
|
# 1) Separate statements
|
||||||
matches = [ match for match in re.finditer(r"[^ \t\n\r]+", l) ]
|
matches = [ match for match in re.finditer(r"[^ \t\n\r]+", l) ]
|
||||||
if len(matches) < 2:
|
if len(matches) < 2:
|
||||||
raiseError(i+1, f"Line has only one statement: '{l}'.", fix="The line needs to have the duration and led state separated by a space.")
|
raise_error(i+1, f"Line has only one statement: '{l}'.", fix="The line needs to have the duration and led state separated by a space.")
|
||||||
continue
|
continue
|
||||||
# 2) parse the duration
|
# 2) parse the duration
|
||||||
s_duration = matches[0].group()
|
s_duration = matches[0].group()
|
||||||
duration_match = re.fullmatch(r"(\d+[hms]?)+", s_duration)
|
duration_match = re.fullmatch(r"(\d+[hms]?)+", s_duration)
|
||||||
if not duration_match:
|
if not duration_match:
|
||||||
raiseError(i+1, f"Invalid duration: '{s_duration}'.", fix="Duration needs to consist of one ore more <digits><h/m/s> pairs, without spaces.")
|
raise_error(i+1, f"Invalid duration: '{s_duration}'.", fix="Duration needs to consist of one ore more <digits><h/m/s> pairs, without spaces.")
|
||||||
continue
|
continue
|
||||||
duration = 0.0
|
duration = 0.0
|
||||||
for m in re.findall(r"\d+[hms]?", s_duration):
|
for m in re.findall(r"\d+[hms]?", s_duration):
|
||||||
@ -280,21 +297,21 @@ class LedScript:
|
|||||||
try:
|
try:
|
||||||
t = int(m[:-1])
|
t = int(m[:-1])
|
||||||
except:
|
except:
|
||||||
raiseError(i+1, f"Invalid number in duration '{m[-1]}': '{m[:-1]}'")
|
raise_error(i+1, f"Invalid number in duration '{m[-1]}': '{m[:-1]}'")
|
||||||
continue
|
continue
|
||||||
t *= timesuffixes[m[-1]]
|
t *= timesuffixes[m[-1]]
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
t = int(m)
|
t = int(m)
|
||||||
except:
|
except:
|
||||||
raiseError(i+1, f"Invalid number in duration: '{m}'")
|
raise_error(i+1, f"Invalid number in duration: '{m}'")
|
||||||
continue
|
continue
|
||||||
duration += t
|
duration += t
|
||||||
# 3) parse the state
|
# 3) parse the state
|
||||||
s_state = matches[1].group()
|
s_state = matches[1].group()
|
||||||
state_match = re.fullmatch(r"(?:(\d+%?)|(on)|(off))(#.*)?", s_state, re.IGNORECASE)
|
state_match = re.fullmatch(r"(?:(\d+%?)|(on)|(off))(#.*)?", s_state, re.IGNORECASE)
|
||||||
if not state_match:
|
if not state_match:
|
||||||
raiseError(i+1, f"Not a valid LED state: '{s_state}'", fix="LED state needs to be either 'on', 'off', an integer between [0,100], optionally ending with '%'")
|
raise_error(i+1, f"Not a valid LED state: '{s_state}'", fix="LED state needs to be either 'on', 'off', an integer between [0,100], optionally ending with '%'")
|
||||||
continue
|
continue
|
||||||
state_percent, state_on, state_off, ends_with_comment = state_match.groups()
|
state_percent, state_on, state_off, ends_with_comment = state_match.groups()
|
||||||
if state_percent:
|
if state_percent:
|
||||||
@ -302,25 +319,25 @@ class LedScript:
|
|||||||
try:
|
try:
|
||||||
state = int(state_percent)
|
state = int(state_percent)
|
||||||
except:
|
except:
|
||||||
raiseError(i+1, f"Invalid number in led state: '{m}'")
|
raise_error(i+1, f"Invalid number in led state: '{m}'")
|
||||||
continue
|
continue
|
||||||
elif state_on:
|
elif state_on:
|
||||||
state = 100
|
state = 100
|
||||||
elif state_off:
|
elif state_off:
|
||||||
state = 0
|
state = 0
|
||||||
else:
|
else:
|
||||||
raiseError(i+1, f"Not a valid LED state: '{s_state}'")
|
raise_error(i+1, f"Not a valid LED state: '{s_state}'")
|
||||||
continue
|
continue
|
||||||
if state > 100:
|
if state > 100:
|
||||||
raiseError(i+1, f"State is larger than 100%: '{state}'")
|
raise_error(i+1, f"State is larger than 100%: '{state}'")
|
||||||
continue
|
continue
|
||||||
elif state < 0:
|
elif state < 0:
|
||||||
raiseError(i+1, f"State is smaller than 0%: '{state}'")
|
raise_error(i+1, f"State is smaller than 0%: '{state}'")
|
||||||
# 4) Check for missing comment symbol # AFTER parsing duration and state, since the source of an error might be an invalid duration or state
|
# 4) Check for missing comment symbol # AFTER parsing duration and state, since the source of an error might be an invalid duration or state
|
||||||
if not ends_with_comment and len(matches) >= 3:
|
if not ends_with_comment and len(matches) >= 3:
|
||||||
rest = matches[2].group()
|
rest = matches[2].group()
|
||||||
if not rest.startswith("#"):
|
if not rest.startswith("#"):
|
||||||
raiseError(i+1, f"Garbage after statement: '{rest}'.", fix="If this was meant to be a comment, use '#'.")
|
raise_error(i+1, f"Garbage after statement: '{rest}'.", fix="If this was meant to be a comment, use '#'.")
|
||||||
continue
|
continue
|
||||||
# 5) Calculate cumulative duration
|
# 5) Calculate cumulative duration
|
||||||
if len(states) == 0:
|
if len(states) == 0:
|
||||||
|
@ -24,6 +24,7 @@ def measure(
|
|||||||
flush_after:int|None=None,
|
flush_after:int|None=None,
|
||||||
use_buffer=False,
|
use_buffer=False,
|
||||||
max_measurements: int=None,
|
max_measurements: int=None,
|
||||||
|
stop_on_script_end: bool=False,
|
||||||
verbose: bool=False,
|
verbose: bool=False,
|
||||||
command_queue: None|Queue=None,
|
command_queue: None|Queue=None,
|
||||||
data_queue: None|Queue=None
|
data_queue: None|Queue=None
|
||||||
@ -51,6 +52,8 @@ def measure(
|
|||||||
Number of measurements to perform before returning.
|
Number of measurements to perform before returning.
|
||||||
Note: If use_buffer=True, a few more than max_measurements might be performed
|
Note: If use_buffer=True, a few more than max_measurements might be performed
|
||||||
The default is None.
|
The default is None.
|
||||||
|
stop_on_script_end : bool, optional
|
||||||
|
Stop when the script end is reached.
|
||||||
verbose : bool, optional
|
verbose : bool, optional
|
||||||
If True, print some messages. The default is False.
|
If True, print some messages. The default is False.
|
||||||
command_queue : None|Connection, optional
|
command_queue : None|Connection, optional
|
||||||
@ -77,7 +80,6 @@ def measure(
|
|||||||
# and without microseconds
|
# and without microseconds
|
||||||
if not "time" in data.metadata:
|
if not "time" in data.metadata:
|
||||||
data.metadata["time"] = datetime.datetime.now().replace(microsecond=0).astimezone().isoformat()
|
data.metadata["time"] = datetime.datetime.now().replace(microsecond=0).astimezone().isoformat()
|
||||||
data.metadata["test"] = "TEST"
|
|
||||||
vm_dev.reset(True)
|
vm_dev.reset(True)
|
||||||
if use_buffer:
|
if use_buffer:
|
||||||
vm_dev.buffer_measure(delta_t, verbose=True)
|
vm_dev.buffer_measure(delta_t, verbose=True)
|
||||||
@ -131,7 +133,9 @@ def measure(
|
|||||||
time.sleep(dt_sleep)
|
time.sleep(dt_sleep)
|
||||||
t_iter_start = time.time()
|
t_iter_start = time.time()
|
||||||
# 5) update LED
|
# 5) update LED
|
||||||
new_led_val = led_script.get_state()
|
if stop_on_script_end and led_script.is_done(t_iter_start):
|
||||||
|
break
|
||||||
|
new_led_val = led_script.get_state(t_iter_start)
|
||||||
if new_led_val != led_val:
|
if new_led_val != led_val:
|
||||||
try:
|
try:
|
||||||
led_dev.set_level(new_led_val)
|
led_dev.set_level(new_led_val)
|
||||||
@ -144,7 +148,6 @@ def measure(
|
|||||||
pass
|
pass
|
||||||
data.flush(verbose=verbose)
|
data.flush(verbose=verbose)
|
||||||
led_dev.off()
|
led_dev.off()
|
||||||
print(data.metadata)
|
|
||||||
print("Measurement stopped" + " "*50)
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user