diff --git a/data_centre/plugin_collection.py b/data_centre/plugin_collection.py index 56dd144..3d9338b 100644 --- a/data_centre/plugin_collection.py +++ b/data_centre/plugin_collection.py @@ -221,6 +221,7 @@ class DisplayPlugin(Plugin): #display_text.insert(END, 'test from DisplayPlugin') display.display_text.insert(END, '{} \n'.format(display.body_title)) + class ModulationReceiverPlugin(Plugin): def __init__(self, plugin_collection): super().__init__(plugin_collection) @@ -230,6 +231,24 @@ class ModulationReceiverPlugin(Plugin): raise NotImplementedError +class AutomationSourcePlugin(Plugin): + @property + def frame_key(self): + return self.__class__.__name__ + + def __init__(self, plugin_collection): + super().__init__(plugin_collection) + + def get_frame_data(self): + raise NotImplementedError + + def recall_frame_data(self, data): + raise NotImplementedError + + def get_frame_diff(self, last_frame, current_frame): + raise NotImplementedError + + # adapted from https://github.com/gdiepen/python_plugin_example class PluginCollection(object): """Upon creation, this class will read the plugins package for modules @@ -303,7 +322,7 @@ class PluginCollection(object): for (_, c) in clsmembers: # Only add classes that are a sub class of Plugin, but NOT Plugin itself # or one of the base classes - ignore_list = [ Plugin, ActionsPlugin, SequencePlugin, MidiFeedbackPlugin, DisplayPlugin, ModulationReceiverPlugin ] + ignore_list = [ Plugin, ActionsPlugin, SequencePlugin, MidiFeedbackPlugin, DisplayPlugin, ModulationReceiverPlugin, AutomationSourcePlugin ] if issubclass(c, Plugin) & (c not in ignore_list): print(' Found plugin class: %s.%s' % (c.__module__,c.__name__)) self.plugins.append(c(self)) diff --git a/plugins/WJSendPlugin.py b/plugins/WJSendPlugin.py index f57b064..8b07b8f 100644 --- a/plugins/WJSendPlugin.py +++ b/plugins/WJSendPlugin.py @@ -1,10 +1,10 @@ import serial from serial import Serial import data_centre.plugin_collection -from data_centre.plugin_collection import ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationReceiverPlugin +from data_centre.plugin_collection import ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationReceiverPlugin, AutomationSourcePlugin import threading -class WJSendPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin,ModulationReceiverPlugin): +class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationReceiverPlugin, AutomationSourcePlugin): disabled = False#True ser = None # from http://depot.univ-nc.nc/sources/boxtream-0.9999/boxtream/switchers/panasonic.py @@ -42,9 +42,44 @@ class WJSendPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin,ModulationReceiver self.pc.actions.tk.after(500, self.refresh) #tk.after(500, self.refresh) + # methods/vars for AutomationSourcePlugin + last_record = {} + def get_frame_data(self): + diff = self.last_record.copy() + #self.last_record = {} + return diff + + def get_frame_diff(self, last_frame, current_frame): + lf = last_frame.get(self.frame_key) + cf = current_frame.get(self.frame_key) + + if cf is None or not cf: + return {} + + if lf is None or not lf: + return { self.frame_key: cf.copy() } + + diff = {} + for queue,message in cf.items(): + if lf.get(queue) is None or lf.get(queue)!=message: + diff[queue] = message + + #print (">>>>>> returning diff\n%s\n<<<<<" % diff) + return diff + + def recall_frame_data(self, data): + if data is None: + return + # print(">>>>recall from data:\n\t%s\n" %data) + for queue, item in data.items(): + self.send_buffered(queue, item, record = False) + + + # methods for ModulationReceiverPlugin - receives changes to the in-built modulation levels (-1 to +1) + # experimental & hardcoded ! def set_modulation_value(self, param, value): # take modulation value and throw it to local parameter - print("||||| wjsend set_modulation_value for param %s with value %s!" % (param, value)) + print("||||| wjsend received set_modulation_value for param %s with value %s!" % (param, value)) if param==0: self.set_mix((0.5+value)/2) elif param==1: @@ -56,6 +91,21 @@ class WJSendPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin,ModulationReceiver else: print("unknown param %s!" % param) + #methods for DisplayPlugin + def show_plugin(self, display, display_mode): + from tkinter import Text, END + #super(DisplayPlugin).show_plugin(display, display_mode) + #print("show plugin?") + display.display_text.insert(END, '{} \n'.format(display.body_title)) + display.display_text.insert(END, "test from WJSendPlugin!\n\n") + + for queue, last in self.last.items(): + display.display_text.insert(END, "last %s:\t%s\n" % (queue,self.last.get(queue))) + + def get_display_modes(self): + return ["WJMXSEND","NAV_WJMX"] + + # methods for SerialPlugin (todo!) def open_serial(self, port='/dev/ttyUSB0', baudrate=9600): if self.ser is not None: self.ser.close() @@ -79,19 +129,60 @@ class WJSendPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin,ModulationReceiver import traceback traceback.print_exc() - def show_plugin(self, display, display_mode): - from tkinter import Text, END - #super(DisplayPlugin).show_plugin(display, display_mode) - #print("show plugin?") - display.display_text.insert(END, '{} \n'.format(display.body_title)) - display.display_text.insert(END, "test from WJSendPlugin!\n\n") + def send_serial_string(self, string): + try: + print("sending string %s " % string) + output = b'\2' + string.encode('ascii') + b'\3' + self.ser.write(output) #.encode()) + print("sent string '%s'" % output) #.encode('ascii')) + #if 'S' in string: + # self.get_device_status() + except Exception as e: + print("%s: send_serial_string failed for '%s'" % (e,string)) #.encode() - for queue, last in self.last.items(): - display.display_text.insert(END, "last %s:\t%s\n" % (queue,self.last.get(queue))) + queue = {} + def refresh(self): + #print("refresh called!") + if not self.ser or self.ser is None: + self.open_serial() - def get_display_modes(self): - return ["WJMXSEND","NAV_WJMX"] + try: + for queue, command in self.queue.items(): + self.send_buffered(queue, command) + #self.queue.clear() + except Exception: + print ("!!! CAUGHT EXCEPTION running queue !!!") + import traceback + print(traceback.format_exc()) + finally: + self.queue.clear() + if self.ser is not None: + self.pc.shaders.root.after(self.THROTTLE, self.refresh) + + def send(self, queue, output): + #self.send_buffered(queue,output) + self.queue[queue] = output + + last = {} + def send_buffered(self, queue, output, record = True): + if self.last.get(queue)!=output: + self.send_serial_string(output) + self.last[queue] = output + if record: + print("### send_buffered is setting last_record[%s] to %s" % (queue,output)) + self.last_record[queue] = output + + def send_append(self, command, value): + # append value to the command as a hex value + self.send(command.split(':')[0], "{}{:02X}".format(command,int(255*value))) + + def send_append_pad(self, pad, command, value): + # append value, padded to length + self.send(command.split(':')[0], ("{}{:0%iX}"%pad).format(command,int(255*value))) + + + # methods for ActionPlugin @property def parserlist(self): return [ @@ -106,42 +197,9 @@ class WJSendPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin,ModulationReceiver ( r"^wj_send_append_([:0-9a-zA-Z]*)$", self.send_append ), ] - def send_serial_string(self, string): - try: - print("sending string %s " % string) - output = b'\2' + string.encode('ascii') + b'\3' - self.ser.write(output) #.encode()) - print("sent string '%s'" % output) #.encode('ascii')) - #if 'S' in string: - # self.get_device_status() - except Exception as e: - print("%s: send_serial_string failed for '%s'" % (e,string.encode())) - - queue = {} - def refresh(self): - #print("refresh called!") - if not self.ser or self.ser is None: - self.open_serial() - - try: - for queue, command in self.queue.items(): - self.send_buffered(queue, command) - self.queue.clear() - except: - print ("!!! CAUGHT EXCEPTION running queue !!!") - - if self.ser is not None: - self.pc.shaders.root.after(self.THROTTLE, self.refresh) - - def send(self, queue, output): - #self.send_buffered(queue,output) - self.queue[queue] = output - - last = {} - def send_buffered(self, queue, output): - if self.last.get(queue)!=output: - self.send_serial_string(output) - self.last[queue] = output + # methods for handling some Panasonic control settings + # todo: come up with a way to represent this programmatically + # todo: add more! colour_x = 127 colour_y = 127 @@ -155,6 +213,7 @@ class WJSendPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin,ModulationReceiver output = "VCC:{}{:02X}{:02X}".format(chan, self.colour_x,self.colour_y) self.send('VCC', output) + # RGB control of matte colour! back_colour_x = 127 back_colour_y = 127 back_colour_z = 127 @@ -186,7 +245,7 @@ class WJSendPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin,ModulationReceiver output = "VBW:{:02X}{:02X}{:02X}".format(self.back_wash_colour_x,self.back_wash_colour_y,self.back_wash_colour_z) self.send('VBW', output) - + # positioner joystick position_x = 127 position_y = 127 def set_position(self, mode, dim, value): @@ -198,15 +257,8 @@ class WJSendPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin,ModulationReceiver output = "VPS:{}{:02X}{:02X}".format(mode,self.position_x,self.position_y) self.send('VPS:{}'.format(mode), output) + # wipe / mix level def set_mix(self, value): output = "VMM:{:04X}".format(int(255*255*value)) self.send('VMM', output) - - def send_append(self, command, value): - # append value to the command as a hex value - self.send(command.split(':')[0], "{}{:02X}".format(command,int(255*value))) - - def send_append_pad(self, pad, command, value): - # append value, padded to length - self.send(command.split(':')[0], ("{}{:0%iX}"%pad).format(command,int(255*value)))