diff options
author | Kevin O'Connor <kevin@koconnor.net> | 2021-08-24 13:40:47 -0400 |
---|---|---|
committer | Kevin O'Connor <kevin@koconnor.net> | 2021-08-30 12:12:01 -0400 |
commit | 1e4041a96badaf29533879a2c353981da60cbc67 (patch) | |
tree | d4533d8a2bb5f5e69a58f069d064f548d86e4b37 | |
parent | 5fd1c9853d243cccdb53c75cae0a1a48b882368d (diff) | |
download | kutter-1e4041a96badaf29533879a2c353981da60cbc67.tar.gz kutter-1e4041a96badaf29533879a2c353981da60cbc67.tar.xz kutter-1e4041a96badaf29533879a2c353981da60cbc67.zip |
motan: Pass dataset parameters in parenthesis
Replace names like "trapq:toolhead:x" with "trapq(toolhead,x)".
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
-rw-r--r-- | docs/Debugging.md | 4 | ||||
-rw-r--r-- | scripts/motan/analyzers.py | 57 | ||||
-rwxr-xr-x | scripts/motan/motan_graph.py | 6 | ||||
-rw-r--r-- | scripts/motan/readlog.py | 100 |
4 files changed, 97 insertions, 70 deletions
diff --git a/docs/Debugging.md b/docs/Debugging.md index a0e15322..a9d35383 100644 --- a/docs/Debugging.md +++ b/docs/Debugging.md @@ -122,7 +122,7 @@ Graphs can be generated with a command like the following: One can use the `-g` option to specify the datasets to graph (it takes a Python literal containing a list of lists). For example: ``` -~/klipper/scripts/motan/motan_graph.py mylog -g '[["trapq:toolhead:velocity"], ["trapq:toolhead:accel"]]' +~/klipper/scripts/motan/motan_graph.py mylog -g '[["trapq(toolhead,velocity)"], ["trapq(toolhead,accel)"]]' ``` The list of available datasets can be found using the `-l` option - @@ -134,7 +134,7 @@ for example: It is also possible to specify matplotlib plot options for each dataset: ``` -~/klipper/scripts/motan/motan_graph.py mylog -g '[["trapq:toolhead:velocity?color=red"]]' +~/klipper/scripts/motan/motan_graph.py mylog -g '[["trapq(toolhead,velocity)?color=red&alpha=0.4"]]' ``` Many matplotlib options are available; some examples are "color", "label", "alpha", and "linestyle". diff --git a/scripts/motan/analyzers.py b/scripts/motan/analyzers.py index 9e3f3b57..bdb70011 100644 --- a/scripts/motan/analyzers.py +++ b/scripts/motan/analyzers.py @@ -4,6 +4,7 @@ # # This file may be distributed under the terms of the GNU GPLv3 license. import collections +import readlog ###################################################################### @@ -15,12 +16,13 @@ AHandlers = {} # Calculate a derivative (position to velocity, or velocity to accel) class GenDerivative: + ParametersMin = ParametersMax = 1 DataSets = [ - ('derivative:<dataset>', 'Derivative of the given dataset'), + ('derivative(<dataset>)', 'Derivative of the given dataset'), ] - def __init__(self, amanager, params): + def __init__(self, amanager, name_parts): self.amanager = amanager - self.source = params + self.source = name_parts[1] amanager.setup_dataset(self.source) def get_label(self): label = self.amanager.get_label(self.source) @@ -46,28 +48,30 @@ AHandlers["derivative"] = GenDerivative # Calculate a kinematic stepper position from the toolhead requested position class GenKinematicPosition: + ParametersMin = ParametersMax = 1 DataSets = [ - ('kin:<stepper>', 'Stepper position derived from toolhead kinematics'), + ('kin(<stepper>)', 'Stepper position derived from toolhead kinematics'), ] - def __init__(self, amanager, params): + def __init__(self, amanager, name_parts): self.amanager = amanager + stepper = name_parts[1] status = self.amanager.get_initial_status() kin = status['configfile']['settings']['printer']['kinematics'] if kin not in ['cartesian', 'corexy']: raise amanager.error("Unsupported kinematics '%s'" % (kin,)) - if params not in ['stepper_x', 'stepper_y', 'stepper_z']: - raise amanager.error("Unknown stepper '%s'" % (params,)) - if kin == 'corexy' and params in ['stepper_x', 'stepper_y']: - self.source1 = 'trapq:toolhead:x' - self.source2 = 'trapq:toolhead:y' - if params == 'stepper_x': + if stepper not in ['stepper_x', 'stepper_y', 'stepper_z']: + raise amanager.error("Unknown stepper '%s'" % (stepper,)) + if kin == 'corexy' and stepper in ['stepper_x', 'stepper_y']: + self.source1 = 'trapq(toolhead,x)' + self.source2 = 'trapq(toolhead,y)' + if stepper == 'stepper_x': self.generate_data = self.generate_data_corexy_plus else: self.generate_data = self.generate_data_corexy_minus amanager.setup_dataset(self.source1) amanager.setup_dataset(self.source2) else: - self.source1 = 'trapq:toolhead:' + params[-1:] + self.source1 = 'trapq(toolhead,%s)' % (stepper[-1:],) self.source2 = None self.generate_data = self.generate_data_passthrough amanager.setup_dataset(self.source1) @@ -89,15 +93,13 @@ AHandlers["kin"] = GenKinematicPosition # Calculate a position deviation class GenDeviation: + ParametersMin = ParametersMax = 2 DataSets = [ - ('deviation:<dataset1>-<dataset2>', 'Difference between datasets'), + ('deviation(<dataset1>,<dataset2>)', 'Difference between datasets'), ] - def __init__(self, amanager, params): + def __init__(self, amanager, name_parts): self.amanager = amanager - parts = params.split('-') - if len(parts) != 2: - raise amanager.error("Invalid deviation '%s'" % (params,)) - self.source1, self.source2 = parts + self.source1, self.source2 = name_parts[1:] amanager.setup_dataset(self.source1) amanager.setup_dataset(self.source2) def get_label(self): @@ -117,20 +119,16 @@ AHandlers["deviation"] = GenDeviation ###################################################################### -# List datasets +# Analyzer management and data generation ###################################################################### +# Return a description of available analyzers def list_datasets(): datasets = [] for ah in sorted(AHandlers.keys()): datasets += AHandlers[ah].DataSets return datasets - -###################################################################### -# Data generation -###################################################################### - # Manage raw and generated data samples class AnalyzerManager: error = None @@ -159,15 +157,18 @@ class AnalyzerManager: return self.raw_datasets[name] if name in self.gen_datasets: return self.gen_datasets[name] - nparts = name.split(':') - if nparts[0] in self.lmanager.available_dataset_types(): + name_parts = readlog.name_split(name) + if name_parts[0] in self.lmanager.available_dataset_types(): hdl = self.lmanager.setup_dataset(name) self.raw_datasets[name] = hdl else: - cls = AHandlers.get(nparts[0]) + cls = AHandlers.get(name_parts[0]) if cls is None: raise self.error("Unknown dataset '%s'" % (name,)) - hdl = cls(self, ':'.join(nparts[1:])) + num_param = len(name_parts) - 1 + if num_param < cls.ParametersMin or num_param > cls.ParametersMax: + raise self.error("Invalid parameters to dataset '%s'" % (name,)) + hdl = cls(self, name_parts) self.gen_datasets[name] = hdl self.datasets[name] = [] return hdl diff --git a/scripts/motan/motan_graph.py b/scripts/motan/motan_graph.py index 9826f1a2..6571a201 100755 --- a/scripts/motan/motan_graph.py +++ b/scripts/motan/motan_graph.py @@ -125,9 +125,9 @@ def main(): # Default graphs to draw graph_descs = [ - ["trapq:toolhead:velocity?color=green"], - ["trapq:toolhead:accel?color=green"], - ["deviation:stepq:stepper_x-kin:stepper_x?color=blue"], + ["trapq(toolhead,velocity)?color=green"], + ["trapq(toolhead,accel)?color=green"], + ["deviation(stepq(stepper_x),kin(stepper_x))?color=blue"], ] if options.graph is not None: graph_descs = ast.literal_eval(options.graph) diff --git a/scripts/motan/readlog.py b/scripts/motan/readlog.py index 7cd2fbd5..e47e9e7f 100644 --- a/scripts/motan/readlog.py +++ b/scripts/motan/readlog.py @@ -18,20 +18,21 @@ LogHandlers = {} # Extract requested position, velocity, and accel from a trapq log class HandleTrapQ: - ParametersSubscriptionId = 2 - ParametersMin = ParametersMax = 3 + SubscriptionIdParts = 2 + ParametersMin = ParametersMax = 2 DataSets = [ - ('trapq:<name>:velocity', 'Requested velocity for the given trapq'), - ('trapq:<name>:<axis>', 'Requested axis (x, y, or z) position'), - ('trapq:<name>:<axis>_velocity', 'Requested axis velocity'), - ('trapq:<name>:<axis>_accel', 'Requested axis acceleration'), + ('trapq(<name>,velocity)', 'Requested velocity for the given trapq'), + ('trapq(<name>,accel)', 'Requested acceleration for the given trapq'), + ('trapq(<name>,<axis>)', 'Requested axis (x, y, or z) position'), + ('trapq(<name>,<axis>_velocity)', 'Requested axis velocity'), + ('trapq(<name>,<axis>_accel)', 'Requested axis acceleration'), ] - def __init__(self, lmanager, name): + def __init__(self, lmanager, name, name_parts): self.name = name self.jdispatch = lmanager.get_jdispatch() self.cur_data = [(0., 0., 0., 0., (0., 0., 0.), (0., 0., 0.))] self.data_pos = 0 - tq, trapq_name, datasel = name.split(':') + tq, trapq_name, datasel = name_parts ptypes = {} ptypes['velocity'] = { 'label': '%s velocity' % (trapq_name,), @@ -113,26 +114,27 @@ LogHandlers["trapq"] = HandleTrapQ # Extract positions from queue_step log class HandleStepQ: - ParametersSubscriptionId = 2 - ParametersMin = 2 - ParametersMax = 3 + SubscriptionIdParts = 2 + ParametersMin = 1 + ParametersMax = 2 DataSets = [ - ('stepq:<stepper>', 'Commanded position of the given stepper'), - ('stepq:<stepper>:raw', 'Commanded position without smoothing'), + ('stepq(<stepper>)', 'Commanded position of the given stepper'), + ('stepq(<stepper>,<time>)', 'Commanded position with smooth time'), ] - def __init__(self, lmanager, name): + def __init__(self, lmanager, name, name_parts): self.name = name + self.stepper_name = name_parts[1] self.jdispatch = lmanager.get_jdispatch() self.step_data = [(0., 0., 0.), (0., 0., 0.)] # [(time, half_pos, pos)] self.data_pos = 0 self.smooth_time = 0.010 - name_parts = name.split(':') if len(name_parts) == 3: - if name_parts[2] != 'raw': - raise error("Unknown stepq data selection '%s'" % (name,)) - self.smooth_time = 0. + try: + self.smooth_time = float(name_parts[2]) + except ValueError: + raise error("Invalid stepq smooth time '%s'" % (name_parts[2],)) def get_label(self): - label = '%s position' % (self.name.split(':')[1],) + label = '%s position' % (self.stepper_name,) return {'label': label, 'units': 'Position\n(mm)'} def pull_data(self, req_time): smooth_time = self.smooth_time @@ -205,17 +207,6 @@ LogHandlers["stepq"] = HandleStepQ ###################################################################### -# List datasets -###################################################################### - -def list_datasets(): - datasets = [] - for lh in sorted(LogHandlers.keys()): - datasets += LogHandlers[lh].DataSets - return datasets - - -###################################################################### # Log reading ###################################################################### @@ -279,6 +270,40 @@ class JsonDispatcher: for mq in self.queues.get(qid, []): mq.append(json_msg['params']) + +###################################################################### +# Dataset and log tracking +###################################################################### + +# Split a string by commas while keeping parenthesis intact +def param_split(line): + out = [] + level = prev = 0 + for i, c in enumerate(line): + if not level and c == ',': + out.append(line[prev:i]) + prev = i+1 + elif c == '(': + level += 1 + elif level and c== ')': + level -= 1 + out.append(line[prev:]) + return out + +# Split a dataset name (eg, "abc(def,ghi)") into parts +def name_split(name): + if '(' not in name or not name.endswith(')'): + raise error("Malformed dataset name '%s'" % (name,)) + aname, aparams = name.split('(', 1) + return [aname] + param_split(aparams[:-1]) + +# Return a description of possible datasets +def list_datasets(): + datasets = [] + for lh in sorted(LogHandlers.keys()): + datasets += LogHandlers[lh].DataSets + return datasets + # Main log access management class LogManager: error = error @@ -325,15 +350,16 @@ class LogManager: def setup_dataset(self, name): if name in self.datasets: return self.datasets[name] - parts = name.split(':') - cls = LogHandlers.get(parts[0]) + name_parts = name_split(name) + cls = LogHandlers.get(name_parts[0]) if cls is None: - raise error("Unknown dataset '%s'" % (parts[0],)) - if len(parts) < cls.ParametersMin or len(parts) > cls.ParametersMax: - raise error("Invalid number of parameters for %s" % (parts[0],)) - subscription_id = ":".join(parts[:cls.ParametersSubscriptionId]) + raise error("Unknown dataset '%s'" % (name_parts[0],)) + len_pp = len(name_parts) - 1 + if len_pp < cls.ParametersMin or len_pp > cls.ParametersMax: + raise error("Invalid number of parameters for '%s'" % (name,)) + subscription_id = ":".join(name_parts[:cls.SubscriptionIdParts]) if subscription_id not in self.log_subscriptions: raise error("Dataset '%s' not in capture" % (subscription_id,)) - self.datasets[name] = hdl = cls(self, name) + self.datasets[name] = hdl = cls(self, name, name_parts) self.jdispatch.add_handler(name, subscription_id) return hdl |