def exit_process(signum, frame): global running logservice.log(logservice.LOG_INFO, 'Got SIGINT, shutting down...') signal.setitimer(signal.ITIMER_REAL, 0, 0) # Kill any running patches and wait for them to exit patchservice.kill_running_patch() # Clean up shared memory eventservice.shutdown() interface_process.terminate() time.sleep(0.1) interface_process.join() display_process.terminate() time.sleep(0.1) display_process.join() web_process.terminate() time.sleep(0.1) web_process.join() bc_process.terminate() time.sleep(0.1) bc_process.join() running = 0
def loadpatch(parent): # TODO: provide a quick way to get to running patch global running_patch global patch_menu # If the running one is not the one that's to be loaded, then load it. if (running_patch is None) or (running_patch["patchfilepath"] != parent["patchfilepath"]): kill_running_patch() # Clear our state management now that nothing is running patch_menu = {} controllermodel.controller_processes = [] controllermodel.controller_count.value = 0 # Load the config with open(parent["patchfilepath"]) as data_file: data = json.load(data_file) # TODO: What if the data's not there? for controllerdata in data["program"]["controllers"][:controllermodel.MAX_CONTROLLERS]: controllername = controllerdata["module"] logservice.log(logservice.LOG_INFO, 'Loading controller "{0}"'.format(controllername)) controllermodule = importlib.import_module(controllername) # Start up the worker for this controller cw = controllerworker.ControllerWorker(controllermodule, data, controllermodel.controller_queues[controllermodel.controller_count.value]) controllermodel.controller_count.value += 1 controller_process = Process(target=cw.run) # Remember our process for later controllermodel.controller_processes.append(controller_process) running_patch = parent # Add the menu # TODO: support multiple menus patch_menu = [{ 'text': 'brightness', 'integervalue': 'brightness', 'min': 0, 'max': 255 }, { 'text': 'reset', 'action': 'reset' }] # Start the process controller_process.start() return patch_menu
def refresh(): removed = [] for device_filename in os.listdir(systemmodel.DEV_INPUT): if device_filename.startswith('js') and device_filename not in device_names: # TODO: We can write to these guys too # Open the file, blocking time.sleep(0.5) f = open("/dev/input/" + device_filename, 'rb') # Get the device name JSIOCGNAME(len) buf = array.array('c', ['\0'] * 64) ioctl(f, 0x80006a13 + (0x10000 * len(buf)), buf) name = buf.tostring() logservice.log(logservice.LOG_INFO, 'New game controller: {0}'.format(device_filename)) displaymodel.alert("New gamepad:", device_filename) # Add to the device list devices[f.fileno()] = { "type" : gamepadmodel.DEVICE_TYPE_GAMEPAD, "filename" : device_filename, "name" : name, "file" : f, "getevent" : getevent } device_names.add(device_filename) return removed
def __init__(self, controllermodule, config, q): """ Instantiates a new Controller. :param controllermodule: A controller instance. :param config: The parsed json defining a given patch :param q: The message queue assigned to this worker """ signal.signal(signal.SIGINT, signal.SIG_IGN) self.config = config self.q = q self.controller = controllermodule.Controller(config) self.clocked = isinstance(self.controller, BaseClockedController) # Setup the thread we are on self.configure_thread() #TODO: sync start time across threads # Start absolute system time self.t = precision_timer.getabsolutetime() logservice.log(logservice.LOG_DEBUG, 'Start time for controller worker {0:.6f}'.format(self.t)) self.next_t = self.t
def __init__(self, config): # Create a thread that just listens to semaphore events self.event_thread = EventThread() self.event_thread.start() logservice.log(logservice.LOG_ERR, 'Initialized value debug')
def process(): event = None while 1: # TODO Monitor udev for new devices instead? # Wait for a device to have data ready, abort after 5 seconds and refresh the device list r, w, x = select(devices.keys(), [], [], 5) for fd in r: # Get a handier reference to the device that has data ready device = devices[fd] # Decode the available chunks try: event = device["getevent"](device) except IOError: logservice.log(logservice.LOG_INFO, "Error reading from device, removing") del devices[fd] if event is not None: # Add the key of the source device event["source"] = fd # If it's a responseq item, send to its destination if (device["type"] == responseqio.DEVICE_TYPE_RESPONSEQ): if event["target"] in devices: devices[event["target"]]["message"](devices[event["target"]], event) else: logservice.log(logservice.LOG_INFO, "Got a message for a missing device. Discarding.") else: # Filter and handle the events for handler in handlers.values(): if handler["filter"](device, event): handler["handler"](device, event) # After select times out, refresh the devices # TODO: this will also happen after an event which we don't want refresh()
def getpatchname(patchpath, patchfilename): try: with open(os.path.join(patchpath, patchfilename)) as data_file: data = json.load(data_file) return data["program"]["name"] except Exception as e: logservice.log(logservice.LOG_ERR, 'Error getting patch name for "{0}": {1}'.format(patchfilename, e)) logservice.log(logservice.LOG_ERR, traceback.format_exc()) return patchfilename[:16]
def configure_thread(self): # TODO: setup prctl for embedded platform try: os.nice(-15) except OSError: logservice.log(logservice.LOG_ERR, 'Unable to set nice level on worker. Leaving as-is') # If mach/osx, allow thread to access precision timers if not systemmodel.EMBEDDED: logservice.log(logservice.LOG_INFO, 'Configuring thread access to timers') precision_timer.configurethread(0, 5, 10)
def run(): signal.signal(signal.SIGINT, signal.SIG_IGN) # Initialize our possible input devices devio.initialize() # Set up our filters and callbacks devio.add_handler("interface_game_buttons", filter_buttons, handle_buttons) devio.add_handler("interface_stdin", filter_stdin, handle_stdin) devio.add_handler("patchmessages", filter_patch, handle_patch) # Loop forever while 1: # If we get an exception, show the error and start over try: # Show welcome message and sleep for 1s displaymodel.display_line_one("nw2s:b2/dsp v0.1") displaymodel.display_line_two("") time.sleep(1.0) # Show the main menu oledmenuservice.navigateroot() # Hand off control to the input manager devio.process() except Exception as e: logservice.log(logservice.LOG_ERR, 'Error in interface worker thread: {0}'.format(e)) logservice.log(logservice.LOG_ERR, traceback.format_exc()) # Reset the menu oledmenuservice.reset() # Show the error: displaymodel.display_line_one('Error:') displaymodel.display_line_two(str(e)[:16]) time.sleep(2.0)
def getevent(device): try: # gamepads spit 8 bytes per chunk data = device["file"].read(8) # unpack the data... time, value, type, number = struct.unpack('IhBB', data) # ...and make it look like something useful return { "type" : type, "number" : number, "value" : value } except IOError as err: logservice.log(logservice.LOG_INFO, "Error reading from js device") device_names.remove(device["filename"]) del devices[device["file"].fileno()] raise err
def run(self): while self.running: try: # Wait for the value to go above 0 self.sem.acquire() except sysv_ipc.Error as ex: pass # Semaphore available, something changed for x in (p for p in enumerate(izip(self.triggers, eventmodel.triggers)) if p[1][0] != p[1][1]): logservice.log(logservice.LOG_ERR, 'changed trigger: ' + str(x)) self.triggers[x[0]] = x[1][1] for x in (p for p in enumerate(izip(self.cvalues, eventmodel.cvalues)) if p[1][0] != p[1][1]): logservice.log(logservice.LOG_ERR, 'changed cvalue: ' + str(x)) self.cvalues[x[0]] = x[1][1] for x in (p for p in enumerate(izip(self.dvalues, eventmodel.dvalues)) if p[1][0] != p[1][1]): logservice.log(logservice.LOG_ERR, 'changed dvalue: ' + str(x)) self.dvalues[x[0]] = x[1][1]
def __init__(self, config): # Events self.event_sem = eventservice.get_event_semaphore() # Sources self.sources = [] self.devices = {} # Storage for raw button states self.buttons = {} # Storage for the active cell states self.cells = {} # Track serial numbers issues to cells self.serial = 0 # Assume a basic tempo self.tempo = 120 logservice.log(logservice.LOG_ERR, 'Initialized otogrid')
def getevent(device): event = None # We're getting weird errors when disconnecting device. Make sure it's there first. if not os.path.exists(device["filename"]): return None # Pulling the event out of the buffer by hand - if it's ready try: event = device["monome"].next_event() except RuntimeError as e: logservice.log(logservice.LOG_ERR, 'Error catching monome event: {0}'.format(e)) logservice.log(logservice.LOG_ERR, traceback.format_exc()) device_names_invalid.add(device['filename']) event = None # TODO: Maybe we get more than one event at a time? If we miss some, refactor this if event is not None: # This event that goes on the queue needs to be pickleable, so just putting in the important info qevent = { "type": DEVICE_TYPE_MONOME, "device": { "type": DEVICE_TYPE_MONOME, "name": device["monome"].name, "protocol": device["monome"].protocol, "rows": device["monome"].rows, "columns": device["monome"].columns }, "event": {} } # TODO: Support other types of monome events if isinstance(event, monome.MonomeGridEvent): qevent["event"]["pressed"] = event.pressed qevent["event"]["x"] = event.x qevent["event"]["y"] = event.y logservice.log(logservice.LOG_DEBUG, str(qevent)) return qevent # Otherwise, we'll try again later. return None
def run(): signal.signal(signal.SIGINT, signal.SIG_IGN) try: if not os.path.exists(DISPLAY_DEVICE_PATH): # If we don't have a device, just send data to dev null and log it. logservice.log(logservice.LOG_ERR, "No such display device: " + DISPLAY_DEVICE_PATH) display_device_path = "/dev/null" logservice.log(logservice.LOG_INFO, "Opening display device " + display_device_path) display_device = open(display_device_path, "a") while 1: # See if anything is on the queue displaymessage = displaymodel.getmessage() # If so, then show it for 2 seconds if displaymessage is not None: line1 = '{0: <16}'.format(displaymessage["line1"][:16]) line2 = '{0: <16}'.format(displaymessage["line2"][:16]) display_device.write(line1 + line2) display_device.flush() time.sleep(2) # TODO: Don't do this if we have more waiting display_device.write(displaymodel.get_data()) display_device.flush() elif displaymodel.is_modified(): display_device.write(displaymodel.get_data()) display_device.flush() print(displaymodel.get_data()) #powernap.nap(0.05) time.sleep(0.05) display_device.close() except Exception as e: logservice.log(logservice.LOG_ERR, 'Exception in displayworker thread: {0}'.format(e))
web_process.terminate() time.sleep(0.1) web_process.join() bc_process.terminate() time.sleep(0.1) bc_process.join() running = 0 # Setup Ctrl-C to exit gracefully signal.signal(signal.SIGINT, exit_process) # Log any interesting information as we get started logservice.log(logservice.LOG_INFO, "Platform: " + platform.platform()) # Start up the display worker display_process = Process(target=displayworker.run) display_process.start() # Start up the interface worker interface_process = Process(target=interfaceworker.run) interface_process.start() # Start up web worker web_process = Process(target=webworker.run) web_process.start() # Start broadcast worker bc_process = Process(target=broadcastworker.run)
def refresh(): removed = [] for device in devices.keys(): # See if the file is still there if not os.path.exists(devices[device]["filename"]): # If not, remove it from all the places we track them device_names.remove(devices[device]["filename"]) del devices[device] removed.append(device) # If the file is gone, we can also remove it from our bad guy list. toremove = [] for filename in device_names_invalid: # If not, remove it from all the places we track them if not os.path.exists(filename): toremove.append(filename) for filename in toremove: device_names_invalid.remove(filename) # Refresh the device list every once in a while for device_filename in glob.glob(monomemodel.GRID_DEV_GLOB): if (device_filename not in device_names) and (device_filename not in device_names_invalid): try: # Sleep for a bit just to see if udev needs time time.sleep(0.5) # Try to create the device device = monome.Monome(device_filename) # Get the serial number, names, etc. devices[device.fileno()] = { "type": DEVICE_TYPE_MONOME, "filename": device_filename, "monome": device, "getevent": getevent, "message": message } # TODO - display in window logservice.log(logservice.LOG_INFO, 'New monome controller: {0}'.format(device_filename)) displaymodel.alert("New monome:", device_filename) # TODO: Need to remove them too device_names.add(device_filename) # Set Rotation device.rotation = monome.ROTATE_0 # TODO: Why is this crashing the device?! # Clear the device # device.led_all(1) # time.sleep(0.1) # device.led_all(0) # time.sleep(0.1) except Exception: # Add to the invalid list device_names_invalid.add(device_filename) logservice.log(logservice.LOG_INFO, 'Device {0} was not a monome or there was an error trying.'.format(device_filename)) # Pass back any that got removed return removed
def button_handler(self, source, device, event): if source not in self.sources: self.sources.append(source) self.devices[source] = device self.cells[source] = [] self.buttons[source] = [[0 for x in xrange(0, device['columns'])] for x in xrange(0, device['rows'])] logservice.log(logservice.LOG_ERR, "Added a new oto source: " + str(source)) logservice.log(logservice.LOG_ERR, "Columns: " + str(device['columns'])) logservice.log(logservice.LOG_ERR, "Rows: " + str(device['rows'])) # Add this cell direction = CELL_UP # If it was added on an edge, correct the direction if event['x'] == 0: direction = CELL_RIGHT if event['x'] == device['columns'] - 1: direction = CELL_LEFT if event['y'] == 0: direction = CELL_DOWN remove_action = False # If it collides with an existing cell, then remove them both for cell in self.cells[source]: if cell['row'] == event['y'] and cell['column'] == event['x'] and cell['serial'] != self.serial: self.cells[source].remove(cell) remove_action = True if not remove_action: self.cells[source].append( { 'direction': direction, 'column': event['x'], 'row': event['y'], 'serial' : self.serial }) self.serial += 1 x = event["x"] y = event["y"] self.buttons[source][y][x] ^= 1 if self.buttons[source][y][x]: responseq.put({ "type": monomeio.DEVICE_TYPE_MONOME, "command": monomemodel.COMMAND_LED_ON, "target": source, "x": x, "y": y }) else: responseq.put({ "type": monomeio.DEVICE_TYPE_MONOME, "command": monomemodel.COMMAND_LED_OFF, "target": source, "x": x, "y": y })
def clock(self, t): logservice.log(logservice.LOG_ERR, 'clocked template tick')
def __init__(self, config): self.last_trigger_t = 0.0 self.sem = eventservice.get_event_semaphore() logservice.log(logservice.LOG_ERR, 'Initialized probability trigger')
def __init__(self, config): # Not much to initialize at the moment. logservice.log(logservice.LOG_ERR, 'Initialized template')