class DMXServerThread(threading.Thread): wrapper = None TICK_INTERVAL = 1 # in ms SPEED = 3 # speed multiplier targetValues = [0, 0, 0, 0, 0, 0, 0, 0, 0] currentValues = [0, 0, 0, 0, 0, 0, 0, 0, 0] def __init__(self): print "DMXServerThread Init" threading.Thread.__init__(self) self.wrapper = ClientWrapper() def DmxSent(self, state): if not state.Succeeded(): print "DMX Sending Failed for some reason" self.wrapper.Stop() def SendDMXFrame(self): # continuously schedule the next function call self.wrapper.AddEvent(self.TICK_INTERVAL, self.SendDMXFrame) # if current values are within range of target, set to target; prevent target oscillation for i, v in enumerate(self.currentValues): diff = abs(self.targetValues[i] - v) if diff > 0 and diff <= self.SPEED: #print "Forcing channel %s to %s" % (i, v) self.currentValues[i] = v # Don't flood the dmx controller with unnecessary messages if self.currentValues == self.targetValues: return # compute next current value for each channel & add to frame data = array.array('B') for i, v in enumerate(self.targetValues): newVal = self.currentValues[i] + (cmp( self.targetValues[i] - self.currentValues[i], 0) * self.SPEED) #print newVal if (newVal > 255): newVal = 255 if (newVal < 0): newVal = 0 self.currentValues[i] = newVal data.append(self.currentValues[i]) self.wrapper.Client().SendDmx(1, data, self.DmxSent) def setTargets(self, _targetValues={}): for k, v in _targetValues.iteritems(): if not isinstance(k, int) or k > 10: print "Target channel is not an int or is out of range" return self.targetValues[k] = v def stop(self): self.wrapper.Stop() def run(self): self.wrapper.AddEvent(self.TICK_INTERVAL, self.SendDMXFrame) self.wrapper.Run()
class OlaThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) self.data = None def run(self): def onData(data): # TODO check on status before assigning data self.data = data #print(data) self.wrapper = ClientWrapper() self.client = self.wrapper.Client() universe = 1 self.client.RegisterUniverse(universe, self.client.REGISTER, onData) print('running thread') self.wrapper.Run() def exit(self): try: self.wrapper.Terminate() except: self.wrapper.Stop() def getData(self): # return or print? print(self.data)
class DMXAdapter(LightAdapter): def __init__(self): pass def start(self): from ola.ClientWrapper import ClientWrapper from ola.DMXConstants import DMX_MIN_SLOT_VALUE, DMX_MAX_SLOT_VALUE, DMX_UNIVERSE_SIZE self._universe = 0 self._wrapper = ClientWrapper() self._client = self._wrapper.Client() self.run() # TODO We could use OLA's internal event scheduling system, but currently # I'm not aware of any obvious reason to do so. Nevertheless it bears # further investigation. def _sent_callback(self, status): if (not status.Succeeded()): # TODO catch this and report it in our heartbeat log.error("Failed to send DMX: %s" % status.message) # Always stop, as we call ourselves from a loop self._wrapper.Stop() def send(self, data): self._client.SendDmx(0, data, self._sent_callback) self._wrapper.Run() def run(self): dt_start = time.time_ns() # awkwardly wait around while we waight for an animator to be assigned while not self.animator: time.sleep(1) while True: dt = time.time_ns() - dt_start dt_start = time.time_ns() self.animator.step(dt / (10**9)) with self.animator.lock: self.send(self.animator.lightstate) # TODO use configuration time.sleep(1 / 60)
class DMX(Thread): def __init__(self, lights, effects): super().__init__() self.lights = lights self.running = False self.array = array.array('B', [0] * 512) self.effects = effects self.bpm = 30 def run(self): self.wrapper = ClientWrapper() self.running = True while self.running: self.computeEffects() self.computeDMX() self.sendDMX() time.sleep(50e-3) # time.sleep(1) def stop(self): self.running = False def computeEffects(self): t = time.perf_counter() effect_duration = 60 / self.bpm t0_1 = (t % effect_duration) / effect_duration for effect in self.effects: effect(t0_1) def computeDMX(self): self.array = array.array('B', [0] * 512) for l in self.lights: l.writeDMX(self.array) def sendDMX(self): client = self.wrapper.Client() universe = 0 client.SendDmx(universe, self.array, lambda x: self.wrapper.Stop()) self.wrapper.Run()
class Artnet(Thread): def __init__(self, settings): self._w = 0 self._h = 0 self._mapping = None self._settings = settings self._serial_device = self._settings.SERIAL_DEVICE self._load_config() self.wrapper = None self.universe = 1 super(Artnet, self).__init__() def _load_config(self): configfile = open(self._settings.WALL_CONFIG_FILE, 'r') data = simplejson.load(configfile) configfile.close() self._w = int(data['w']) self._h = int(data['h']) self._mapping = {} for k, v in data['mapping'].items(): self._mapping[int(k)] = int(v) def _dmxdata(self, data): pindex = 0 fdata = [] for y in range(self._h): for x in range(self._w): r = data[pindex * 3] g = data[pindex * 3 + 1] b = data[pindex * 3 + 2] color = correct_rgb((r, g, b)) fdata.extend([0xFF, self._mapping[y * self._w + x]]) fdata.extend(color) pindex += 1 self._serial_device.write("".join([chr(v) for v in fdata])) def run(self): if self.wrapper: self.wrapper.Stop() try: self.wrapper = ClientWrapper() except OLADNotRunningException: print "Start olad first" sys.exit(1) client = self.wrapper.Client() client.RegisterUniverse(self.universe, client.REGISTER, self._dmxdata) self._load_config() self.wrapper.Run() def play(self): self.start() def stop(self): self.wrapper.Stop()
class OLAThread(threading.Thread): """connect to olad in a threaded way.""" def __init__(self): """create new OLAThread instance.""" # super().__init__() # super(threading.Thread, self).__init__() threading.Thread.__init__(self) self.wrapper = None self.client = None self.state = OLAThread_States.standby self.flag_connected = False self.flag_wait_for_ola = False # self.start() def run(self): """run state engine in threading.""" print("run") print("self.state: {}".format(self.state)) while self.state is not OLAThread_States.standby: if self.state is OLAThread_States.waiting: # print("state - waiting") self.ola_waiting_for_connection() elif self.state is OLAThread_States.connected: self.ola_connected() elif self.state is OLAThread_States.running: self.ola_wrapper_run() # elif self.state is OLAThread_States.stopping: # pass # elif self.state is OLAThread_States.starting: # pass def ola_wrapper_run(self): """run ola wrapper.""" print("run ola wrapper.") try: self.wrapper.Run() except KeyboardInterrupt: self.wrapper.Stop() print("\nstopped") except socket.error as error: print("connection to OLAD lost:") print(" error: " + error.__str__()) self.flag_connected = False self.state = OLAThread_States.waiting # except Exception as error: # print(error) def ola_waiting_for_connection(self): """connect to ola.""" print("waiting for olad....") self.flag_connected = False self.flag_wait_for_ola = True while (not self.flag_connected) & self.flag_wait_for_ola: try: # print("get wrapper") self.wrapper = ClientWrapper() except OLADNotRunningException: time.sleep(0.5) else: self.flag_connected = True self.state = OLAThread_States.connected if self.flag_connected: self.flag_wait_for_ola = False print("get client") self.client = self.wrapper.Client() else: print("\nstopped waiting for olad.") def ola_connected(self): """ just switch to running mode. this can be overriden in a subclass. """ self.state = OLAThread_States.running # dmx frame sending def dmx_send_frame(self, universe, data): """send data as one dmx frame.""" if self.flag_connected: try: # temp_array = array.array('B') # for channel_index in range(0, self.channel_count): # temp_array.append(self.channels[channel_index]) # print("temp_array:{}".format(temp_array)) # print("send frame..") self.wrapper.Client().SendDmx( universe, data, # temp_array, self.dmx_send_callback) # print("done.") except OLADNotRunningException: self.wrapper.Stop() print("olad not running anymore.") else: # throw error pass def dmx_send_callback(self, state): """react on ola state.""" if not state.Succeeded(): self.wrapper.Stop() self.state = OLAThread_States.waiting print("warning: dmxSent does not Succeeded.") else: # print("send frame succeeded.") pass # managment functions def start_ola(self): """switch to state running.""" print("start_ola") if self.state == OLAThread_States.standby: self.state = OLAThread_States.waiting self.start() def stop_ola(self): """stop ola wrapper.""" if self.flag_wait_for_ola: print("stop search for ola wrapper.") self.flag_wait_for_ola = False if self.flag_connected: print("stop ola wrapper.") self.wrapper.Stop() # stop thread self.state = OLAThread_States.standby # wait for thread to finish. self.join()
class InteractiveModeController(cmd.Cmd): """Interactive mode!""" def __init__(self, universe, uid, sub_device, pid_location): """Create a new InteractiveModeController. Args: universe: uid: sub_device: pid_location: """ cmd.Cmd.__init__(self) self._universe = universe self._uid = uid self._sub_device = sub_device self.pid_store = PidStore.GetStore(pid_location) self.wrapper = ClientWrapper() self.client = self.wrapper.Client() self.rdm_api = RDMAPI(self.client, self.pid_store) self._uids = [] self._response_printer = ResponsePrinter() # tuple of (sub_device, command_class, pid) self._outstanding_request = None self.prompt = '> ' def emptyline(self): pass def do_exit(self, s): """Exit the interpreter.""" return True def do_EOF(self, s): print('') return self.do_exit('') def do_uid(self, line): """Sets the active UID.""" args = line.split() if len(args) != 1: print('*** Requires a single UID argument') return uid = UID.FromString(args[0]) if uid is None: print('*** Invalid UID') return if uid not in self._uids: print('*** UID not found') return self._uid = uid print('Fetching queued messages...') self._FetchQueuedMessages() self.wrapper.Run() def complete_uid(self, text, line, start_index, end_index): tokens = line.split() if len(tokens) > 1 and text == '': return [] uids = [str(uid) for uid in self._uids if str(uid).startswith(text)] return uids def do_subdevice(self, line): """Sets the sub device.""" args = line.split() if len(args) != 1: print('*** Requires a single int argument') return try: sub_device = int(args[0]) except ValueError: print('*** Requires a single int argument') return if sub_device < 0 or sub_device > PidStore.ALL_SUB_DEVICES: print('*** Argument must be between 0 and 0x%hx' % PidStore.ALL_SUB_DEVICES) return self._sub_device = sub_device def do_print(self, line): """Prints the current universe, UID and sub device.""" print(textwrap.dedent("""\ Universe: %d UID: %s Sub Device: %d""" % ( self._universe, self._uid, self._sub_device))) def do_uids(self, line): """List the UIDs for this universe.""" self.client.FetchUIDList(self._universe, self._DisplayUids) self.wrapper.Run() def _DisplayUids(self, state, uids): self._uids = [] if state.Succeeded(): self._UpdateUids(uids) for uid in uids: print(str(uid)) self.wrapper.Stop() def do_full_discovery(self, line): """Run full RDM discovery for this universe.""" self.client.RunRDMDiscovery(self._universe, True, self._DiscoveryDone) self.wrapper.Run() def do_incremental_discovery(self, line): """Run incremental RDM discovery for this universe.""" self.client.RunRDMDiscovery(self._universe, False, self._DiscoveryDone) self.wrapper.Run() def _DiscoveryDone(self, state, uids): if state.Succeeded(): self._UpdateUids(uids) self.wrapper.Stop() def _UpdateUids(self, uids): self._uids = [] for uid in uids: self._uids.append(uid) def do_list(self, line): """List the pids available.""" names = [] for pid in self.pid_store.Pids(): names.append('%s (0x%04hx)' % (pid.name.lower(), pid.value)) if self._uid: for pid in self.pid_store.ManufacturerPids(self._uid.manufacturer_id): names.append('%s (0x%04hx)' % (pid.name.lower(), pid.value)) names.sort() print('\n'.join(names)) def do_queued(self, line): """Fetch all the queued messages.""" self._FetchQueuedMessages() self.wrapper.Run() def do_get(self, line): """Send a GET command.""" self.GetOrSet(PidStore.RDM_GET, line) def complete_get(self, text, line, start_index, end_index): return self.CompleteGetOrSet(PidStore.RDM_GET, text, line) def do_set(self, line): """Send a SET command.""" self.GetOrSet(PidStore.RDM_SET, line) def complete_set(self, text, line, start_index, end_index): return self.CompleteGetOrSet(PidStore.RDM_SET, text, line) def CompleteGetOrSet(self, request_type, text, line): if len(line.split(' ')) > 2: return [] pids = [pid for pid in self.pid_store.Pids() if pid.name.lower().startswith(text)] if self._uid: for pid in self.pid_store.ManufacturerPids(self._uid.manufacturer_id): if pid.name.lower().startswith(text): pids.append(pid) # now check if this type of request is supported pid_names = sorted([pid.name.lower() for pid in pids if pid.RequestSupported(request_type)]) return pid_names def GetOrSet(self, request_type, line): if self._uid is None: print('*** No UID selected, use the uid command') return args = line.split() command = 'get' if request_type == PidStore.RDM_SET: command = 'set' if len(args) < 1: print('%s <pid> [args]' % command) return pid = None try: pid = self.pid_store.GetPid(int(args[0], 0), self._uid.manufacturer_id) except ValueError: pid = self.pid_store.GetName(args[0].upper(), self._uid.manufacturer_id) if pid is None: print('*** Unknown pid %s' % args[0]) return if not pid.RequestSupported(request_type): print('*** PID does not support command') return if request_type == PidStore.RDM_SET: method = self.rdm_api.Set else: method = self.rdm_api.Get try: if method(self._universe, self._uid, self._sub_device, pid, self._RDMRequestComplete, args[1:]): self._outstanding_request = (self._sub_device, request_type, pid) self.wrapper.Run() except PidStore.ArgsValidationError as e: args, help_string = pid.GetRequestDescription(request_type) print('Usage: %s %s %s' % (command, pid.name.lower(), args)) print(help_string) print('') print('*** %s' % e) return def _FetchQueuedMessages(self): """Fetch messages until the queue is empty.""" pid = self.pid_store.GetName('QUEUED_MESSAGE', self._uid.manufacturer_id) self.rdm_api.Get(self._universe, self._uid, PidStore.ROOT_DEVICE, pid, self._QueuedMessageComplete, ['error']) def _RDMRequestComplete(self, response, unpacked_data, unpack_exception): if not self._CheckForAckOrNack(response): return # at this stage the response is either a ack or nack self._outstanding_request = None self.wrapper.Stop() if response.response_type == OlaClient.RDM_NACK_REASON: print('Got nack with reason: %s' % response.nack_reason) elif unpack_exception: print(unpack_exception) else: self._response_printer.PrintResponse(self._uid, response.pid, unpacked_data) if response.queued_messages: print('%d queued messages remain' % response.queued_messages) def _QueuedMessageComplete(self, response, unpacked_data, unpack_exception): if not self._CheckForAckOrNack(response): return if response.response_type == OlaClient.RDM_NACK_REASON: if (self._outstanding_request and response.sub_device == self._outstanding_request[0] and response.command_class == self._outstanding_request[1] and response.pid == self._outstanding_request[2].value): # we found what we were looking for self._outstanding_request = None self.wrapper.StopIfNoEvents() elif (response.nack_reason == RDMNack.NR_UNKNOWN_PID and response.pid == self.pid_store.GetName('QUEUED_MESSAGE').value): print('Device doesn\'t support queued messages') self.wrapper.StopIfNoEvents() else: print('Got nack for 0x%04hx with reason: %s' % ( response.pid, response.nack_reason)) elif unpack_exception: print('Invalid Param data: %s' % unpack_exception) else: status_messages_pid = self.pid_store.GetName('STATUS_MESSAGES') if (response.pid == status_messages_pid.value and unpacked_data.get('messages', []) == []): self.wrapper.StopIfNoEvents() return self._response_printer.PrintResponse(self._uid, response.pid, unpacked_data) if response.queued_messages: self._FetchQueuedMessages() else: self.wrapper.StopIfNoEvents() def _CheckForAckOrNack(self, response): """Check for all the different error conditions. Returns: True if this response was an ACK or NACK, False for all other cases. """ if not response.status.Succeeded(): print(response.status.message) self.wrapper.Stop() return False if response.response_code != OlaClient.RDM_COMPLETED_OK: print(response.ResponseCodeAsString()) self.wrapper.Stop() return False if response.response_type == OlaClient.RDM_ACK_TIMER: # schedule the fetch self.wrapper.AddEvent(response.ack_timer, self._FetchQueuedMessages) return False return True
reader = conn.makefile('r') print "Client recieved, starting loop" try: while process_line(reader.readline()): pass finally: print "Exiting server" s.close() reader.close() print("Bringing up house") empty = array('B', [DMX_MIN_SLOT_VALUE] * DMX_UNIVERSE_SIZE) empty[106] = DMX_MAX_SLOT_VALUE empty[107] = DMX_MAX_SLOT_VALUE client.SendDmx(UNIVERSE, empty) print("Can you see the house?") if not raw_input("y/n").startswith('y'): print("Aborting") exit(0) try: start_server() finally: empty = array('B', [DMX_MIN_SLOT_VALUE] * DMX_UNIVERSE_SIZE) client.SendDmx(UNIVERSE, empty) client_wrapper.Stop()
class OLA(QThread): """ Separate Thread that launch OLA server and run OLA Client It runs only if OLA server is responding """ # signal that there is a new frame for the selected universe universeChanged = pyqtSignal() # signal that there is a new universes_list to display universesList = pyqtSignal() # signal that there is a new list of devices to display devicesList = pyqtSignal() # signal that there is a new list of inputs portsto display inPortsList = pyqtSignal() # signal that there is a new list of inputs portsto display outPortsList = pyqtSignal() def __init__(self): QThread.__init__(self) self.server = None self.client = None try: # launch OLA server self.server = OlaServer() except: # OLA server does not work properly print('OLA server not responding') sleep(0.1) # start the thread if self.server: self.start() else: print('no server is running, cannot start a client') def __del__(self): self.wait() def run(self): """ the running thread """ try: self.wrapper = ClientWrapper() self.client = self.wrapper.Client() if debug: print('connected to OLA server') self.wrapper.Run() except OLADNotRunningException: if debug: print('cannot connect to OLA') def stop(self): """ stop the OLA client wrapper """ if self.client: self.wrapper.Stop() if debug: print('OLA client is stopped') if self.server: self.server.stop() if debug: print('OLA server is stopped') return True
class pixel_multiplier(object): class universe_listener(object): class universe_sender(object): def __init__(self, universe, pixel_groups): self.universe = universe self.pixel_groups = [ group for group in pixel_groups if group.destination_universe == universe ] self.pixel_groups.sort() def __lt__(self, other): return self.universe < other.universe def generate_frame(self, received_frame): new_frame = array.array('B') for group in self.pixel_groups: new_frame.extend( (group.destination_offset - len(new_frame)) * [0]) pixel = received_frame[group. source_offset:group.source_offset + 3] new_frame.extend(pixel * group.number_of_pixels) return new_frame def __init__(self, universe, pixel_groups, ola_client): self.universe = universe self.ola_client = ola_client self.pixel_groups = [ group for group in pixel_groups if group.source_universe == universe ] self.destination_universes = { group.destination_universe for group in self.pixel_groups } self.senders = [ self.universe_sender(u, self.pixel_groups) for u in self.destination_universes ] self.senders.sort() ola_client.RegisterUniverse(universe, ola_client.REGISTER, self.frame_received) def __lt__(self, other): return self.universe < other.universe def frame_received(self, received_frame): def frame_sent(state): pass for sender in self.senders: new_frame = sender.generate_frame(received_frame) self.ola_client.SendDmx(sender.universe, new_frame, frame_sent) class pixel_group(object): def __init__(self, source_universe, source_channel, destination_universe, destination_channel, number_of_pixels): self.source_universe = source_universe self.source_offset = source_channel - 1 self.destination_universe = destination_universe self.destination_offset = destination_channel - 1 self.number_of_pixels = number_of_pixels def __lt__(self, other): return self.destination_offset < other.destination_offset def __init__(self, groups): self.pixel_groups = [ self.pixel_group(group[0], group[1], group[2], group[3], group[4]) for group in groups ] self.source_universes = { group.source_universe for group in self.pixel_groups } self.ola_client_wrapper = ClientWrapper() self.ola_client = self.ola_client_wrapper.Client() self.listeners = [ self.universe_listener(u, self.pixel_groups, self.ola_client) for u in self.source_universes ] self.listeners.sort() def run(self): self.ola_client_wrapper.Run() def stop(self): self.ola_client_wrapper.Stop()
class InteractiveModeController(cmd.Cmd): """Interactive mode!""" def __init__(self, universe, uid, sub_device, pid_file): """Create a new InteractiveModeController. Args: universe: uid: sub_device: pid_file: """ cmd.Cmd.__init__(self) self._universe = universe self._uid = uid self._sub_device = sub_device self.pid_store = PidStore.GetStore(pid_file) self.wrapper = ClientWrapper() self.client = self.wrapper.Client() self.rdm_api = RDMAPI(self.client, self.pid_store) self._uids = [] self._response_printer = ResponsePrinter() # tuple of (sub_device, command_class, pid) self._outstanding_request = None self.prompt = '> ' def emptyline(self): pass def do_exit(self, s): """Exit the intrepreter.""" return True def do_EOF(self, s): print '' return self.do_exit('') def do_uid(self, line): """Sets the active UID.""" args = line.split() if len(args) != 1: print '*** Requires a single UID argument' return uid = UID.FromString(args[0]) if uid is None: print '*** Invalid UID' return if uid not in self._uids: print '*** UID not found' return self._uid = uid print 'Fetching queued messages...' self._FetchQueuedMessages() self.wrapper.Run() def complete_uid(self, text, line, start_index, end_index): tokens = line.split() if len(tokens) > 1 and text == '': return [] uids = [str(uid) for uid in self._uids if str(uid).startswith(text)] return uids def do_subdevice(self, line): """Sets the sub device.""" args = line.split() if len(args) != 1: print '*** Requires a single int argument' return try: sub_device = int(args[0]) except ValueError: print '*** Requires a single int argument' return if sub_device < 0 or sub_device > PidStore.ALL_SUB_DEVICES: print('*** Argument must be between 0 and 0x%hx' % PidStore.ALL_SUB_DEVICES) return self._sub_device = sub_device def do_print(self, l): """Prints the current universe, UID and sub device.""" print textwrap.dedent("""\ Universe: %d UID: %s Sub Device: %d""" % (self._universe, self._uid, self._sub_device)) def do_uids(self, l): """List the UIDs for this universe.""" self.client.FetchUIDList(self._universe, self._DisplayUids) self.wrapper.Run() def _DisplayUids(self, state, uids): self._uids = [] if state.Succeeded(): self._UpdateUids(uids) for uid in uids: print str(uid) self.wrapper.Stop() def do_full_discovery(self, l): """Run full RDM discovery for this universe.""" self.client.RunRDMDiscovery(self._universe, True, self._DiscoveryDone) self.wrapper.Run() def do_incremental_discovery(self, l): """Run incremental RDM discovery for this universe.""" self.client.RunRDMDiscovery(self._universe, False, self._DiscoveryDone) self.wrapper.Run() def _DiscoveryDone(self, state, uids): if state.Succeeded(): self._UpdateUids(uids) self.wrapper.Stop() def _UpdateUids(self, uids): self._uids = [] for uid in uids: self._uids.append(uid) def do_list(self, line): """List the pids available.""" names = [] for pid in self.pid_store.Pids(): names.append('%s (0x%04hx)' % (pid.name.lower(), pid.value)) if self._uid: for pid in self.pid_store.ManufacturerPids( self._uid.manufacturer_id): names.append('%s (0x%04hx)' % (pid.name.lower(), pid.value)) names.sort() print '\n'.join(names) def do_queued(self, line): """Fetch all the queued messages.""" self._FetchQueuedMessages() self.wrapper.Run() def do_get(self, line): """Send a GET command.""" self.GetOrSet(PidStore.RDM_GET, line) def complete_get(self, text, line, start_index, end_index): return self.CompleteGetOrSet(PidStore.RDM_GET, text, line) def do_set(self, line): """Send a SET command.""" self.GetOrSet(PidStore.RDM_SET, line) def complete_set(self, text, line, start_index, end_index): return self.CompleteGetOrSet(PidStore.RDM_SET, text, line) def CompleteGetOrSet(self, request_type, text, line): if len(line.split(' ')) > 2: return [] pids = [ pid for pid in self.pid_store.Pids() if pid.name.lower().startswith(text) ] if self._uid: for pid in self.pid_store.ManufacturerPids( self._uid.manufacturer_id): if pid.name.lower().startswith(text): pids.append(pid) # now check if this type of request is supported pid_names = [ pid.name.lower() for pid in pids if pid.RequestSupported(request_type) ] pid_names.sort() return pid_names def GetOrSet(self, request_type, line): if self._uid is None: print '*** No UID selected, use the uid command' return args = line.split() command = 'get' if request_type == PidStore.RDM_SET: command = 'set' if len(args) < 1: print '%s <pid> [args]' % command return pid = None try: pid = self.pid_store.GetPid(int(args[0], 0), self._uid.manufacturer_id) except ValueError: pid = self.pid_store.GetName(args[0].upper(), self._uid.manufacturer_id) if pid is None: print '*** Unknown pid %s' % args[0] return rdm_args = args[1:] if not pid.RequestSupported(request_type): print '*** PID does not support command' return if request_type == PidStore.RDM_SET: method = self.rdm_api.Set else: method = self.rdm_api.Get try: if method(self._universe, self._uid, self._sub_device, pid, self._RDMRequestComplete, args[1:]): self._outstanding_request = (self._sub_device, request_type, pid) self.wrapper.Run() except PidStore.ArgsValidationError, e: args, help_string = pid.GetRequestDescription(request_type) print 'Usage: %s %s %s' % (command, pid.name.lower(), args) print help_string print '' print '*** %s' % e return
else: print("get client") client = wrapper.Client() if universe == -1: print("request universes") print(client.FetchUniverses(get_unused_universes)) wrapper.Run() print("wait for universes...") try: while not global_flag_got_universes: time.sleep(0.1) print(".. ({})".format(global_flag_got_universes)) except KeyboardInterrupt: print("\nstopped waiting for universes.") print("add event to a SendDMXFrame") wrapper.AddEvent(TICK_INTERVAL, SendDMXFrame) print("run") try: wrapper.Run() except KeyboardInterrupt: wrapper.Stop() print("\nstopped") except socket.error as error: print("connection to OLAD lost:") print(" error: " + error.__str__()) # except Exception as error: # print(error)
class Controller: def __init__(self, config, inputdevice): self.config = config self.current_frame = [0] * 512 self.scene_updated = False self.input_device = InputDevice(inputdevice) self.wrapper = ClientWrapper() self.current_scene = self.config["scenes"][self.config["start_scene"] - 1] self.nextStep(True) """ start dmx transmission """ def run(self): self.wrapper.AddEvent(self.config["frame_duration"], self.nextFrame) self.wrapper.Run() """ calculate the dmx values for a new frame and send the frame """ def nextFrame(self): #starttime = datetime.datetime.now() self.wrapper.AddEvent(self.config["frame_duration"], self.nextFrame) self.handleKeypress() if self.fade_frames > 0: # interpolate dmx values during a fade for i in range( len(self.current_scene["steps"][self.next_step] ["values"])): self.current_frame[i] = int( round(self.current_frame[i] + float(self.current_scene["steps"][self.next_step] ["values"][i] - self.current_frame[i]) / self.fade_frames)) self.fade_frames -= 1 else: # no fade, copy dmx values from scene if necessary if not self.scene_updated: for i in range( len(self.current_scene["steps"][self.next_step] ["values"])): self.current_frame[i] = self.current_scene["steps"][ self.next_step]["values"][i] self.scene_updated = True if self.hold_frames > 0: self.hold_frames -= 1 else: self.nextStep() data = array.array('B', self.current_frame) self.wrapper.Client().SendDmx(self.config["universe"], data) #delta = datetime.datetime.now() - starttime #print("Time spent: %d microseconds" % delta.microseconds) """ check if user pressed a key and try to match keypress to a scene """ def handleKeypress(self): a, b, c = select([self.input_device], [], [], 0) # wait until we can read if not a: return for event in self.input_device.read(): # only track key down events if event.type == ecodes.EV_KEY and event.value == 1: if event.code == 16: # q pressed => quit self.wrapper.Stop() else: # iterate over all scenes and check if a key_trigger # matches current keypress action_triggered = False for scene in self.config["scenes"]: if event.code in scene["trigger_keys"]: self.current_scene = scene self.nextStep(True) action_triggered = True break if action_triggered == False: print("Unmapped key code: %d" % event.code) """ progress to the next step in a scene """ def nextStep(self, newScene=False): if newScene == True: self.next_step = 0 else: if self.current_scene["order"] == "random": step = randint(0, len(self.current_scene["steps"]) - 1) while step == self.next_step: step = randint(0, len(self.current_scene["steps"]) - 1) self.next_step = step else: if self.next_step < (len(self.current_scene["steps"]) - 1): self.next_step += 1 elif self.current_scene["repeat"] == True: self.next_step = 0 else: return self.scene_updated = False self.hold_frames = int( round(self.current_scene["steps"][self.next_step]["hold"] / self.config["frame_duration"])) + 1 self.fade_frames = int( round(self.current_scene["steps"][self.next_step]["fade"] / self.config["frame_duration"])) print('Playing scene: %-30s Step: %02d/%02d' % (self.current_scene["name"], self.next_step + 1, len(self.current_scene["steps"])))
class DMX_Pixel_LED (Lighting_Controller): def __init__(self): Lighting_Controller.__init__(self) # dmx bits self.wrapper = ClientWrapper() self.client = self.wrapper.Client() self.universe = 0 # type: int self.wait_on_dmx = threading.Event() self.dmx_succeeded = False def update(self, leds): """ Send leds array to DMX controller :return: True if dmx update was successful """ if self.origin != Lighting_Controller.NORTH: leds = self.flip_leds(leds, 4) self.client.SendDmx(self.universe, leds, self.dmx_callback) self.wait_on_dmx.clear() self.dmx_succeeded = False self.wrapper.Run() self.wait_on_dmx.wait() return self.dmx_succeeded def update_sequence(self, sequence, delays, repetitions): self.dmx_succeeded = True idx = 0 num_delays = len(delays) for _ in range(repetitions): for leds in sequence: if self.origin != Lighting_Controller.NORTH: leds = self.flip_leds(leds, 4) self.client.SendDmx(self.universe, leds, self.dmx_callback) sleep(delays[idx]) if not self.dmx_succeeded: break idx = (idx + 1) % num_delays return self.dmx_succeeded def set_origin(self, pole): """ Sets origin of lights to either begin on NORTH or SOUTH pole. This will reverse the leds list when running from SOUTH. :param pole: Lighting_Controller.NORTH or Lighting_Controller.SOUTH :return: NONE """ if pole != self.origin and pole in [Lighting_Controller.NORTH, Lighting_Controller.SOUTH]: self.origin = pole def flip_leds(self, leds, bytes_per_led): """ Reverses the led byte array :param leds: :return: """ num_bytes = len(leds) byte_array = array.array('B', [0 for i in range(num_bytes)]) jdx = 0 for idx in range(num_bytes - bytes_per_led, 0, -bytes_per_led): byte_array[jdx] = leds[idx] byte_array[jdx+1] = leds[idx+1] byte_array[jdx+2] = leds[idx+2] byte_array[jdx+3] = leds[idx+3] jdx += 4 return byte_array def fade(self, location, color, milliseconds): """ Transitions light to new color over time period in milliseconds :param location: index in self.devices to effect :param color: :param milliseconds: transition time to new color :return: None """ pass def dmx_callback(self, status): """ :param status: status code returned by SendDmx() :return: None """ self.dmx_succeeded = status.Succeeded() self.wrapper.Stop() self.wait_on_dmx.set()
class DmxSender(Thread): def __init__(self): Thread.__init__(self) self.Terminated = False # self.term=term ## Stop the thread if called def stop(self): self.Terminated = True def run(self): '''Wrapper, Framerate''' print "THREAD" self._wrapper = ClientWrapper() self._activesender = True self.univloglevel = 0 self.base = com_sql.ComSql() # SQL Framerate try: engine = self.base.requete_sql( "SELECT * FROM dmx_engine WHERE id=1") #setting for e in range(len(engine)): freq_ms = engine[e]['freq_ms'] except: print "default to 40 fps" freq_ms = 25 # FOR TEST # freq_ms = 500 self._tick_interval = int(freq_ms) # in milliseconds print "freq_ms" print self._tick_interval # list of scens to play self.scen_ids = [] # dict to store each scenari instance self.my_scens = {} # SQL Universes try: prefs = self.base.requete_sql( "SELECT * FROM dmx_preferences WHERE id=1") #setting for p in range(len(prefs)): self.univ_qty = prefs[p]['univ_qty'] except: print "default to 1 universe" self.univ_qty = 1 print "univ_qty" print self.univ_qty # array to store full frame self.WholeDmxFrame = [0] * 512 * self.univ_qty # send the first one self.SendDmxFrame() self._wrapper.Run() def BlackOut(self): '''Reset all values to zero''' self.WholeDmxFrame = [0] * 512 * self.univ_qty def AssignChannels(self, offset, values): '''Assign channels values according to address''' self.WholeDmxFrame[offset:offset + len(values)] = values def SendDmxFrame(self): '''Ask frame for each scenari and make the whole frame, repeated every tick_interval''' if self._activesender: # Schedule an event to run in the future if self.univloglevel > 0: print "Schedule next" self._wrapper.AddEvent(self._tick_interval, self.SendDmxFrame) if self.univloglevel > 1: print "before sending %s" % time.time() # send data to universes # print "SPLIT" SplittedFrame = self.USplit(self.WholeDmxFrame, 512) u = 1 for FramePart in SplittedFrame: UniverseFrame = list(FramePart) if self.univloglevel > 0: print "FRAME_FOR_UNIV %s" % u # print UniverseFrame try: data = array.array('B', UniverseFrame) self._wrapper.Client().SendDmx(u, data) except: print "Dmx frame not sent. Reset all." self.ResetAll() u += 1 if self.univloglevel > 1: print "before computing %s" % time.time() #for each scenari in list for scenarid in self.scen_ids: try: # create scenari instance if needed if not self.my_scens.has_key(scenarid): scen = PlayScenari(scenarid, self._tick_interval) # store instance in dict, only once self.my_scens[scenarid] = scen print self.my_scens # for each instance, compute frame scen = self.my_scens[scenarid] scen.ComputeNextFrame() # print "calling %s" % scen.new_frame # add partial frame to full one self.AssignChannels(scen.patch, scen.new_frame) # print "FRAME" # print self.WholeDmxFrame except: print "NOT STARTED" if self.univloglevel > 1: print "after computing %s" % time.time() print "---" def ChangeUnivLogLevel(self): self.univloglevel += 1 if self.univloglevel > 2: self.univloglevel = 0 return False else: return True def ChangeLogLevel(self, scenarid): if self.my_scens.has_key(scenarid): # set loglevel for this instance scen = self.my_scens[scenarid] print scen scen.loglevel += 1 if scen.loglevel > 2: scen.loglevel = 0 return False else: return True def HaltDmxSender(self): if self._activesender == True: self._activesender = False return True def ResumeDmxSender(self): if self._activesender == False: self._activesender = True self.SendDmxFrame() return True def CloseDmxSender(self): self._wrapper.Stop() def StartScenari(self, scenarid): if not scenarid in self.scen_ids: # add id into list self.scen_ids.append(scenarid) return True def StopScenari(self, scenarid): if scenarid in self.scen_ids: # remove id into list self.scen_ids.remove(scenarid) return True def StatusScenari(self, scenarid): if scenarid in self.scen_ids: return True def ResetScenari(self, scenarid): if self.my_scens.has_key(scenarid): # remove instance for this id self.my_scens.pop(scenarid) return True def StopAll(self): self.scen_ids = [] def ResetAll(self): self.my_scens = {} def USplit(self, l, n): return zip(*(l[i::n] for i in range(n)))
class Midi2Dmx(threading.Thread): def __init__(self, driver_name="DMX Bridge", universe=0): """ midi->dmx bridge :param driver_name: The midi name of the bridge. This will show up in Logic :param universe: The DMX universe to connect to """ self.driver_name = driver_name self.appname = "{} - {}".format(__appname__, driver_name) # initialize a default dmx frame self.midi_source = MIDIDestination(driver_name) self.frame = [0] * 255 self.universe = universe # this is the starting note for all midi channels self.note_offset = 24 # this is the number of dmx channels per midi channel # each midi channel will support 32 notes. This will allow # 16 fixtures via 16 midi channels. self.dmx_offset = 32 # MacOS X related stuff self.NSUserNotification = objc.lookUpClass('NSUserNotification') self.NSUserNotificationCenter = objc.lookUpClass( 'NSUserNotificationCenter') self.dmx_wrapper = None self.dmx_client = None self.dmx_tick = 100 self.midi_tick = 10 super(Midi2Dmx, self).__init__() def run(self): """Start up the service safely""" self.initialize() if self.dmx_wrapper: self.dmx_wrapper.Run() def stop(self): """Stop the service safely""" if self.dmx_wrapper: self.notify( "Stopping...", "Stopping the DMX Bridge. Midi Events " "will NOT be sent to the DMX device") self.dmx_wrapper.Stop() else: self.notify("DMX is not running", "Stop command issued to an inactive " "DMX bridge.") def initialize(self): """ Zero out dmx, set up events """ try: self.dmx_wrapper = ClientWrapper() self.dmx_client = self.dmx_wrapper.Client() except: self.notify( "OLAD is not running", "Attept to connect to OLAD failed. " "Please start it and try again.") return self.dmx_wrapper.AddEvent(self.dmx_tick, self.send_to_dmx) self.dmx_wrapper.AddEvent(self.dmx_tick / 2, self.get_midi_data) def notify(self, subtitle, info_text): """Send an os x notification""" title = "{} - Universe {}".format(self.appname, self.universe) rumps.notification(title, subtitle, info_text, sound=False) def dmx_frame_sent(self, state): """SendDMX callback""" if not state.Succeeded(): self.dmx_wrapper.Stop() def send_to_dmx(self): """Send the frame to the uDMX device""" self.dmx_wrapper.AddEvent(self.dmx_tick, self.send_to_dmx) dmx_frame = self.build_dmx_frame() self.dmx_client.SendDmx(self.universe, dmx_frame, self.dmx_frame_sent) def update_frame(self, channel, note, velocity): """Translate midi note to dmx channel and velocity to value""" value = velocity * 2 dmx_channel = (note - self.note_offset) + ( (channel - 1) * self.dmx_offset) self.frame[dmx_channel] = value def build_dmx_frame(self): """Translate our internal frame structure into a proper dmx frame""" dmx_frame = array.array("B") for frame_channel in self.frame: dmx_frame.append(frame_channel) return dmx_frame def get_midi_data(self): """Get midi data from the midi source""" self.dmx_wrapper.AddEvent(self.dmx_tick, self.get_midi_data) midi_data = self.midi_source.recv() if len(midi_data) > 0: print midi_data for s in split_seq(midi_data, 3): self.parse_midi_data(s) def parse_midi_data(self, midi_data): """Parse the midi data""" # we're going to ignore non-note traffic # sysex data and such. note_on = False modifier = 0 midi_channel = midi_data[0] if midi_channel in range(144, 159): modifier = 143 note_on = True if midi_channel in range(128, 143): modifier = 127 note_on = False if midi_channel in range(144, 159) or midi_channel in range(128, 143): channel = midi_channel - modifier note = midi_data[1] # make sure our velocity is '0' for the note-off # event. if note_on: velocity = midi_data[2] else: velocity = 0 self.update_frame(channel, note, velocity)