From bd2cc68e01cbe95e64df5607bd2e64c01e523080 Mon Sep 17 00:00:00 2001 From: doctea Date: Wed, 29 Jan 2020 21:15:27 +0000 Subject: [PATCH 1/6] make set_param_layer_modulation (non-offset version) work --- actions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions.py b/actions.py index caf4281..40f8f96 100644 --- a/actions.py +++ b/actions.py @@ -913,7 +913,7 @@ class Actions(object): ( r"stop_shader_layer_([0-2])", self.shaders.stop_shader ), ( r"set_the_shader_param_([0-3])_layer_([0-2])_continuous", self.shaders.set_param_layer_to_amount ), ( r"modulate_param_([0-3])_to_amount_continuous", self.shaders.modulate_param_to_amount ), - ( r"set_param_([0-3])_layer_([0-2])_modulation_level_continuous", self.shaders.set_param_layer_offset_modulation_level ), + ( r"set_param_([0-3])_layer_([0-2])_modulation_level_continuous", self.shaders.set_param_layer_modulation_level ), ( r"set_param_([0-3])_layer_offset_([0-2])_modulation_level_continuous", self.shaders.set_param_layer_offset_modulation_level ), ( r"reset_selected_modulation", self.shaders.reset_selected_modulation ), ( r"reset_modulation_([0-3])", self.shaders.reset_modulation ), From af27ea633d45d8027b8bc87d40141fb4bf3a5974 Mon Sep 17 00:00:00 2001 From: Tristan Rowley Date: Fri, 31 Jan 2020 18:56:41 +0000 Subject: [PATCH 2/6] fix for crash when using keypad to turn shaders on (no previous state stored invalid key) --- plugins/MidiFeedbackAPCKey25Plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/MidiFeedbackAPCKey25Plugin.py b/plugins/MidiFeedbackAPCKey25Plugin.py index f393eb4..1315ad3 100644 --- a/plugins/MidiFeedbackAPCKey25Plugin.py +++ b/plugins/MidiFeedbackAPCKey25Plugin.py @@ -225,7 +225,7 @@ class MidiFeedbackAPCKey25Plugin(MidiFeedbackPlugin): #print("in update device status is %s" % self.status) for i,c in self.status.items(): #'print("comparing\n%s to\n%s" % (c, self.last_state[i])) - if self.last_state is None or self.last_state[i]!=c: + if self.last_state is None or (i not in self.last_state or self.last_state[i]!=c): #print("got command: %s: %s" % (i,c)) self.send_command(**c) self.last_state = deepcopy(self.status) From 17c165af18938f39cb1f44f44d93a895fc3fe8c4 Mon Sep 17 00:00:00 2001 From: Tristan Rowley Date: Fri, 31 Jan 2020 18:59:51 +0000 Subject: [PATCH 3/6] rename MidiActionsTestPlugin to generic TestPlugin, remove mention of it in feedback plugin --- plugins/MidiFeedbackAPCKey25Plugin.py | 4 +--- plugins/{MidiActionsTestPlugin.py => TestPlugin.py} | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) rename plugins/{MidiActionsTestPlugin.py => TestPlugin.py} (96%) diff --git a/plugins/MidiFeedbackAPCKey25Plugin.py b/plugins/MidiFeedbackAPCKey25Plugin.py index 1315ad3..f324a02 100644 --- a/plugins/MidiFeedbackAPCKey25Plugin.py +++ b/plugins/MidiFeedbackAPCKey25Plugin.py @@ -89,12 +89,10 @@ class MidiFeedbackAPCKey25Plugin(MidiFeedbackPlugin): def feedback_plugin_status(self): from data_centre.plugin_collection import SequencePlugin - from plugins.MidiActionsTestPlugin import MidiActionsTestPlugin - try: from plugins.ShaderLoopRecordPlugin import ShaderLoopRecordPlugin for plugin in self.pc.get_plugins(SequencePlugin): - if isinstance(plugin, ShaderLoopRecordPlugin): #MidiActionsTestPlugin): + if isinstance(plugin, ShaderLoopRecordPlugin): NOTE_PLAY_STATUS = 65 NOTE_RECORD_STATUS = 66 diff --git a/plugins/MidiActionsTestPlugin.py b/plugins/TestPlugin.py similarity index 96% rename from plugins/MidiActionsTestPlugin.py rename to plugins/TestPlugin.py index 9c99d9b..1ee973c 100644 --- a/plugins/MidiActionsTestPlugin.py +++ b/plugins/TestPlugin.py @@ -1,7 +1,7 @@ import data_centre.plugin_collection from data_centre.plugin_collection import ActionsPlugin, SequencePlugin -class MidiActionsTestPlugin(ActionsPlugin,SequencePlugin): +class TestPlugin(ActionsPlugin,SequencePlugin): disabled = False def __init__(self, plugin_collection): From 880e3899f42fb1d49113089b2fe0218972c14e05 Mon Sep 17 00:00:00 2001 From: Tristan Rowley Date: Fri, 31 Jan 2020 20:09:00 +0000 Subject: [PATCH 4/6] don't barf if we send a message with parameters to a method handler without one, made the dynamic action/plugin code easier to understand, dynamic route regexes check start/end to disambiguate substrings --- actions.py | 85 ++++++++++++++++++-------------- data_centre/plugin_collection.py | 17 +++---- plugins/TestPlugin.py | 16 +++--- 3 files changed, 63 insertions(+), 55 deletions(-) diff --git a/actions.py b/actions.py index caf4281..bda7640 100644 --- a/actions.py +++ b/actions.py @@ -1,6 +1,7 @@ import subprocess import tracemalloc import data_centre.length_setter as length_setter +from inspect import signature import sys import shlex import os @@ -907,19 +908,19 @@ class Actions(object): @property def parserlist(self): return { - ( r"play_shader_([0-9])_([0-9])", self.shaders.play_that_shader ), - ( r"toggle_shader_layer_([0-2])", self.toggle_shader_layer ), - ( r"start_shader_layer_([0-2])", self.shaders.start_shader ), - ( r"stop_shader_layer_([0-2])", self.shaders.stop_shader ), - ( r"set_the_shader_param_([0-3])_layer_([0-2])_continuous", self.shaders.set_param_layer_to_amount ), - ( r"modulate_param_([0-3])_to_amount_continuous", self.shaders.modulate_param_to_amount ), - ( r"set_param_([0-3])_layer_([0-2])_modulation_level_continuous", self.shaders.set_param_layer_offset_modulation_level ), - ( r"set_param_([0-3])_layer_offset_([0-2])_modulation_level_continuous", self.shaders.set_param_layer_offset_modulation_level ), - ( r"reset_selected_modulation", self.shaders.reset_selected_modulation ), - ( r"reset_modulation_([0-3])", self.shaders.reset_modulation ), - ( r"select_shader_modulation_slot_([0-3])", self.shaders.select_shader_modulation_slot ), - ( r"set_shader_speed_layer_offset_([0-2])_amount", self.shaders.set_speed_offset_to_amount ), - ( r"set_shader_speed_layer_([0-2])_amount", self.shaders.set_speed_layer_to_amount ), + ( r"^play_shader_([0-9])_([0-9])$", self.shaders.play_that_shader ), + ( r"^toggle_shader_layer_([0-2])$", self.toggle_shader_layer ), + ( r"^start_shader_layer_([0-2])$", self.shaders.start_shader ), + ( r"^stop_shader_layer_([0-2])$", self.shaders.stop_shader ), + ( r"^set_the_shader_param_([0-3])_layer_([0-2])_continuous$", self.shaders.set_param_layer_to_amount ), + ( r"^modulate_param_([0-3])_to_amount_continuous$", self.shaders.modulate_param_to_amount ), + ( r"^set_param_([0-3])_layer_([0-2])_modulation_level_continuous$", self.shaders.set_param_layer_offset_modulation_level ), + ( r"^set_param_([0-3])_layer_offset_([0-2])_modulation_level_continuous$", self.shaders.set_param_layer_offset_modulation_level ), + ( r"^reset_selected_modulation$", self.shaders.reset_selected_modulation ), + ( r"^reset_modulation_([0-3])$", self.shaders.reset_modulation ), + ( r"^select_shader_modulation_slot_([0-3])$", self.shaders.select_shader_modulation_slot ), + ( r"^set_shader_speed_layer_offset_([0-2])_amount$", self.shaders.set_speed_offset_to_amount ), + ( r"^set_shader_speed_layer_([0-2])_amount$", self.shaders.set_speed_layer_to_amount ), } def detect_types(self, args): @@ -942,34 +943,46 @@ class Actions(object): return (found_method, args) + return None, None + def call_method_name(self, method_name, argument=None): - # if the target method doesnt exist, call the handler - if not hasattr(self, method_name): - self.call_parse_method_name(method_name, argument) - return + method = None + arguments = None - if argument is not None: - getattr(self, method_name)(argument) - else: - getattr(self, method_name)() + # first check if we have a native method to handle this for us + # todo: assess whether it would still be performant/desirable to be able to override core actions with plugins? + if hasattr(self, method_name): + method = getattr(self, method_name) + if argument is not None: + arguments = [argument] - - def call_parse_method_name(self, method_name, argument): - # first test if a registered plugin handles this for us - from data_centre.plugin_collection import ActionsPlugin - for plugin in self.data.plugins.get_plugins(ActionsPlugin): - if plugin.is_handled(method_name): - print ("Plugin %s is handling %s" % (plugin, method_name)) - method, arguments = plugin.get_callback_for_method(method_name, argument) - method(*arguments) - return - - # if not then fall back to using internal method - try: + # if not then check if its handled by one of our parserlist dynamic route methods + if method is None: method, arguments = self.get_callback_for_method(method_name, argument) - method(*arguments) - except: + + # if still nothing then test if a registered plugin handles this for us -- perhaps this ought to go first? + if method is None: + from data_centre.plugin_collection import ActionsPlugin + for plugin in self.data.plugins.get_plugins(ActionsPlugin): + if plugin.is_handled(method_name): + print ("Plugin %s is handling %s" % (plugin, method_name)) + method, arguments = plugin.get_callback_for_method(method_name, argument) + break # only deal with the first plugin + + if method is None: print ("Failed to find a method for '%s'" % method_name) import traceback traceback.print_exc() + return + + try: + #print ("for method_name %s, arguments is %s and len is %s" % (method_name, arguments, len(signature(method).parameters))) + if arguments is not None and len(signature(method).parameters)==len(arguments): # only pass arguments if count matches method sig + method(*arguments) + else: + method() + except: + print ("Exception calling action for '%s' with arguments ( %s ) " % ( method_name, arguments)) + import traceback + traceback.print_exc() diff --git a/data_centre/plugin_collection.py b/data_centre/plugin_collection.py index 10cea59..12a4012 100644 --- a/data_centre/plugin_collection.py +++ b/data_centre/plugin_collection.py @@ -39,12 +39,12 @@ class SequencePlugin(Plugin): @property def parserlist(self): return [ - ( r"run_automation", self.run_automation ), - ( r"stop_automation", self.stop_automation ), - ( r"toggle_pause_automation", self.toggle_pause_automation ), - ( r"pause_automation", self.pause_automation ), - ( r"toggle_loop_automation", self.toggle_loop_automation ), - ( r"set_automation_speed", self.set_speed ), + ( r"^run_automation$", self.run_automation ), + ( r"^stop_automation$", self.stop_automation ), + ( r"^toggle_pause_automation$", self.toggle_pause_automation ), + ( r"^pause_automation$", self.pause_automation ), + ( r"^toggle_loop_automation$", self.toggle_loop_automation ), + ( r"^set_automation_speed$", self.set_speed ), ] def set_speed(self, speed): @@ -194,11 +194,6 @@ class ActionsPlugin(Plugin): return (found_method, args) - #def call_parse_method_name(self, method_name, argument): - # method, arguments = self.actions.get_callback_for_method(method_name, argument) - # method(*arguments) - - # adapted from https://github.com/gdiepen/python_plugin_example class PluginCollection(object): """Upon creation, this class will read the plugins package for modules diff --git a/plugins/TestPlugin.py b/plugins/TestPlugin.py index 1ee973c..78666fd 100644 --- a/plugins/TestPlugin.py +++ b/plugins/TestPlugin.py @@ -10,13 +10,13 @@ class TestPlugin(ActionsPlugin,SequencePlugin): @property def parserlist(self): return [ - ( r"test_plugin", self.test_plugin ), - ( r"cycle_shaders", self.cycle_shaders ), - ( r"run_automation", self.run_automation ), - ( r"stop_automation", self.stop_automation ), - ( r"toggle_pause_automation", self.toggle_pause_automation ), - ( r"pause_automation", self.pause_automation ), - ( r"toggle_loop_automation", self.toggle_loop_automation ), + ( r"^test_plugin$", self.test_plugin ), + ( r"^cycle_shaders$", self.cycle_shaders ), + ( r"^run_automation$", self.run_automation ), + ( r"^stop_automation$", self.stop_automation ), + ( r"^toggle_pause_automation$", self.toggle_pause_automation ), + ( r"^pause_automation$", self.pause_automation ), + ( r"^toggle_loop_automation$", self.toggle_loop_automation ), ] def test_plugin(self): @@ -42,7 +42,7 @@ class TestPlugin(ActionsPlugin,SequencePlugin): frequency = 50 def run_sequence(self, position): self.pc.actions.call_method_name( - "set_the_shader_param_0_layer_0_continuous", position + "set_the_shader_param_3_layer_0_continuous", position ) self.pc.actions.call_method_name( From 530aed79d2af53e4949dc2b48a34cde312913746 Mon Sep 17 00:00:00 2001 From: Tristan Rowley Date: Fri, 31 Jan 2020 20:12:15 +0000 Subject: [PATCH 5/6] MultiActionsPlugin to allow mapping multiple actions to one binding using && to separate actions, eg action_1&&action_2 --- plugins/MultiActionsPlugin.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 plugins/MultiActionsPlugin.py diff --git a/plugins/MultiActionsPlugin.py b/plugins/MultiActionsPlugin.py new file mode 100644 index 0000000..1038482 --- /dev/null +++ b/plugins/MultiActionsPlugin.py @@ -0,0 +1,19 @@ +import data_centre.plugin_collection +from data_centre.plugin_collection import ActionsPlugin + +class MultiActionsPlugin(ActionsPlugin): + disabled = False + + def __init__(self, plugin_collection): + super().__init__(plugin_collection) + + @property + def parserlist(self): + return [ + ( r"(.*)&&(.*)", self.run_multi ), + ] + + def run_multi(self, action1, action2, value): + print("multi running %s and %s with value %s" % (action1, action2, value)) + self.pc.actions.call_method_name(action1, value) + self.pc.actions.call_method_name(action2, value) From add640847f61ed59e86bae9b954c9856bc4e1369 Mon Sep 17 00:00:00 2001 From: Tristan Rowley Date: Fri, 31 Jan 2020 20:20:14 +0000 Subject: [PATCH 6/6] update ACTIONS docs + generator --- ACTIONS.md | 17 +++++++++-------- dotfiles/generate-list-actions.sh | 4 ++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/ACTIONS.md b/ACTIONS.md index 38f368a..c99205b 100644 --- a/ACTIONS.md +++ b/ACTIONS.md @@ -1,6 +1,6 @@ # Auto-generated Actions list -Fri 10 Jan 17:25:14 UTC 2020 +Fri 31 Jan 20:19:55 UTC 2020 for branch=feature_plugins @@ -173,13 +173,14 @@ for branch=feature_plugins * set_shader_speed_layer_([0-2])_amount ### Plugin routes - * test_plugin (from MidiActionsTestPlugin) - * cycle_shaders (from MidiActionsTestPlugin) - * run_automation (from MidiActionsTestPlugin) - * stop_automation (from MidiActionsTestPlugin) - * toggle_pause_automation (from MidiActionsTestPlugin) - * pause_automation (from MidiActionsTestPlugin) - * toggle_loop_automation (from MidiActionsTestPlugin) + * (.*)&&(.*) (from MultiActionsPlugin) + * test_plugin (from TestPlugin) + * cycle_shaders (from TestPlugin) + * run_automation (from TestPlugin) + * stop_automation (from TestPlugin) + * toggle_pause_automation (from TestPlugin) + * pause_automation (from TestPlugin) + * toggle_loop_automation (from TestPlugin) ---- diff --git a/dotfiles/generate-list-actions.sh b/dotfiles/generate-list-actions.sh index e5afe73..363c7bb 100755 --- a/dotfiles/generate-list-actions.sh +++ b/dotfiles/generate-list-actions.sh @@ -12,11 +12,11 @@ grep " def " actions.py | grep -v "^#" | sed -e 's/ def //' | sed -e 's/self//' echo echo "## Dynamic routes" -grep '( r"' actions.py | sed -e 's/\(.*\)"\(.*\)"\(.*\)/ * \2/' +grep '( r"' actions.py | sed -e 's/\(.*\)"\(.*\)"\(.*\)/ * \2/' | sed -e 's/\$//' | sed -e 's/\^//' echo echo "### Plugin routes" -grep "( r\"" plugins/*.py | sed -e 's/plugins\/\(.*\)\.py:\(.*\)\( r\"\)\(.*\)\"\(.*\)/ * \4\t(from \1)/' | grep -v "open_serial" +grep "( r\"" plugins/*.py | sed -e 's/plugins\/\(.*\)\.py:\(.*\)\( r\"\)\(.*\)\"\(.*\)/ * \4\t(from \1)/' | grep -v "open_serial" | sed -e 's/\$//' | sed -e 's/\^//' echo echo "----"