class ConfigWriter(object): """A controller that applies configuration to a universe.""" (DMX_START_ADDRESS, DEVICE_LABEL, PERSONALITY, COMPLETE) = range(4) def __init__(self, wrapper, pid_store): self.wrapper = wrapper self.pid_store = pid_store self.client = self.wrapper.Client() self.rdm_api = RDMAPI(self.client, self.pid_store) def Run(self, universe, configuration): """Run the collector. Args: universe: The universe to collect configuration: The config to apply """ self.universe = universe self.configuration = configuration self.uids = list(configuration.keys()) self.client.RunRDMDiscovery(self.universe, True, self._HandleUIDList) self.wrapper.Run() def _HandleUIDList(self, state, uids): """Called when the UID list arrives.""" if not state.Succeeded(): raise DiscoveryException(state.message) found_uids = set() for uid in uids: found_uids.add(uid) logging.debug(uid) for uid in self.configuration.keys(): if uid not in found_uids: print('Device %s has been removed' % uid) self._SetNextUID() def _SetNextUID(self): """Start setting the info for the next UID.""" if not self.uids: self.wrapper.Stop() return self.uid = self.uids.pop() print('Doing %s' % self.uid) self.work_state = self.DMX_START_ADDRESS self._NextState() def _NextState(self): """Move to the next state of information fetching.""" if self.work_state == self.DMX_START_ADDRESS: address = self.configuration[self.uid].get('dmx_start_address') self.work_state = self.DEVICE_LABEL if address is not None: pid = self.pid_store.GetName('DMX_START_ADDRESS') self._SetPid(pid, [address]) return if self.work_state == self.DEVICE_LABEL: label = self.configuration[self.uid].get('label') self.work_state = self.PERSONALITY if label is not None: pid = self.pid_store.GetName('DEVICE_LABEL') self._SetPid(pid, [label]) return if self.work_state == self.PERSONALITY: personality = self.configuration[self.uid].get('personality') self.work_state = self.COMPLETE if personality is not None: pid = self.pid_store.GetName('DMX_PERSONALITY') self._SetPid(pid, [personality]) return # this one is done, onto the next UID self._SetNextUID() def _SetPid(self, pid, values): self.rdm_api.Set(self.universe, self.uid, PidStore.ROOT_DEVICE, pid, self._RDMRequestComplete, values) logging.debug('Sent %s request' % pid) self.outstanding_pid = pid def _RDMRequestComplete(self, response, unpacked_data, unpack_exception): if not response.status.Succeeded(): print(response.status.message) self.wrapper.Stop() return if response.response_code != OlaClient.RDM_COMPLETED_OK: print(response.ResponseCodeAsString()) self.wrapper.Stop() return if response.response_type == OlaClient.RDM_ACK_TIMER: # schedule the fetch logging.debug('Got ack timer for %d ms' % response.ack_timer) self.wrapper.AddEvent(response.ack_timer, self._FetchQueuedMessages) return # at this stage the response is either a ack or nack if response.response_type == OlaClient.RDM_NACK_REASON: print('Got nack with reason: %s' % response.nack_reason) self._NextState()
def testSetParamsWithNack(self): """uses client to send an RDM set with mocked olad. Regression test that confirms sent message is correct and sends fixed response message.""" sockets = socket.socketpair() wrapper = ClientWrapper(sockets[0]) pid_store = PidStore.GetStore(pid_store_path) client = wrapper.Client() rdm_api = RDMAPI(client, pid_store) class results: got_request = False got_response = False def DataCallback(self): # request and response for # ola_rdm_set.py -u 1 --uid 7a70:ffffff00 DMX_PERSONALITY 10 # against olad dummy plugin # enable logging in rpc/StreamRpcChannel.py data = sockets[1].recv(4096) expected = binascii.unhexlify( "2b000010080110001a0a52444d436f6d6d616e6422190801120908f0f401150" "0ffffff180020e0012a010a30013800") self.assertEqual(data, expected, msg="Regression check failed. If protocol change " "was intended set expected to: " + str(binascii.hexlify(data))) results.got_request = True response = binascii.unhexlify( "2f0000100802100022290800100218002202000628e001300138004a0908f0f" "4011500ffffff520908f0f40115ac107de05831") sent_bytes = sockets[1].send(response) self.assertEqual(sent_bytes, len(response)) def ResponseCallback(self, response, data, unpack_exception): results.got_response = True self.assertEqual(response.response_type, client.RDM_NACK_REASON) self.assertEqual(response.pid, 0xe0) self.assertEqual(response.nack_reason, RDMNack.NR_DATA_OUT_OF_RANGE) wrapper.AddEvent(0, wrapper.Stop) wrapper._ss.AddReadDescriptor(sockets[1], lambda: DataCallback(self)) uid = UID.FromString("7a70:ffffff00") pid = pid_store.GetName("DMX_PERSONALITY") rdm_api.Set(1, uid, 0, pid, lambda x, y, z: ResponseCallback(self, x, y, z), args=["10"]) wrapper.Run() sockets[0].close() sockets[1].close() self.assertTrue(results.got_request) self.assertTrue(results.got_response)