class MqttClientConnectorTest(unittest.TestCase): """ This test case class contains very basic unit tests for MqttClientConnector. It should not be considered complete, but serve as a starting point for the student implementing additional functionality within their Programming the IoT environment. """ @classmethod def setUpClass(self): logging.basicConfig( format='%(asctime)s:%(module)s:%(levelname)s:%(message)s', level=logging.DEBUG) logging.info("Testing MqttClientConnector class...") self.cfg = ConfigUtil() self.mcc = MqttClientConnector( clientID='CDAMqttClientConnectorTest001') def setUp(self): pass def tearDown(self): pass @unittest.skip("Ignore for now.") def testConnectAndDisconnect(self): delay = self.cfg.getInteger(ConfigConst.MQTT_GATEWAY_SERVICE, ConfigConst.KEEP_ALIVE_KEY, ConfigConst.DEFAULT_KEEP_ALIVE) self.mcc.connect() sleep(delay + 5) self.mcc.disconnect() @unittest.skip("Ignore for now.") def testConnectAndPublish(self): qos = 1 delay = self.cfg.getInteger(ConfigConst.MQTT_GATEWAY_SERVICE, ConfigConst.KEEP_ALIVE_KEY, ConfigConst.DEFAULT_KEEP_ALIVE) listener = DefaultDataMessageListener() self.mcc.connect() self.mcc.subscribeToTopic( ResourceNameEnum.CDA_MGMT_STATUS_MSG_RESOURCE, qos) sleep(5) self.mcc.publishMessage(ResourceNameEnum.CDA_MGMT_STATUS_MSG_RESOURCE, "TEST: This is the CDA message payload.", qos) sleep(5) self.mcc.unsubscribeFromTopic( ResourceNameEnum.CDA_MGMT_STATUS_MSG_RESOURCE) sleep(5) sleep(delay) self.mcc.disconnect() @unittest.skip("Ignore for now.") def testIntegrateWithGdaSubscribeCdaCmdTopic(self): qos = 1 delay = self.cfg.getInteger(ConfigConst.MQTT_GATEWAY_SERVICE, ConfigConst.KEEP_ALIVE_KEY, ConfigConst.DEFAULT_KEEP_ALIVE) listener = DefaultDataMessageListener() self.mcc.connect() self.mcc.subscribeToTopic( ResourceNameEnum.CDA_MGMT_STATUS_CMD_RESOURCE, qos) sleep(delay) self.mcc.disconnect() @unittest.skip("Ignore for now.") def testIntegrateWithGdaPublishCdaMgmtTopic(self): qos = 1 delay = self.cfg.getInteger(ConfigConst.MQTT_GATEWAY_SERVICE, ConfigConst.KEEP_ALIVE_KEY, ConfigConst.DEFAULT_KEEP_ALIVE) listener = DefaultDataMessageListener() self.mcc.connect() self.mcc.publishMessage(ResourceNameEnum.CDA_MGMT_STATUS_MSG_RESOURCE, "TEST: This is the CDA message payload.", qos) sleep(5) self.mcc.disconnect() def testActuatorCmdPubSub(self): qos = 1 # NOTE: delay can be anything you'd like - the sleep() calls are simply to slow things down a bit for observation delay = self.cfg.getInteger(ConfigConst.MQTT_GATEWAY_SERVICE, ConfigConst.KEEP_ALIVE_KEY, ConfigConst.DEFAULT_KEEP_ALIVE) actuatorData = ActuatorData() payload = DataUtil().actuatorDataToJson(actuatorData) self.mcc.connectClient() sleep(3) self.mcc.publishMessage( resource=ResourceNameEnum.CDA_ACTUATOR_CMD_RESOURCE, msg=payload, qos=qos) sleep(3) self.mcc.disconnectClient()
class MqttClientConnectorTest(unittest.TestCase): """ This test case class contains very basic unit tests for MqttClientConnector. It should not be considered complete, but serve as a starting point for the student implementing additional functionality within their Programming the IoT environment. """ @classmethod def setUpClass(self): logging.basicConfig( format='%(asctime)s:%(module)s:%(levelname)s:%(message)s', level=logging.DEBUG) logging.info("Testing MqttClientConnector class...") self.cfg = ConfigUtil() self.mcc = MqttClientConnector() def setUp(self): pass def tearDown(self): pass @unittest.skip("Ignore for now.") def testConnectAndDisconnect(self): delay = self.cfg.getInteger(ConfigConst.MQTT_GATEWAY_SERVICE, ConfigConst.KEEP_ALIVE_KEY, ConfigConst.DEFAULT_KEEP_ALIVE) self.mcc.connectClient() sleep(delay + 5) self.mcc.disconnectClient() @unittest.skip("Ignore for now.") def testConnectAndCDAManagementStatusPubSub(self): qos = 1 delay = self.cfg.getInteger(ConfigConst.MQTT_GATEWAY_SERVICE, ConfigConst.KEEP_ALIVE_KEY, ConfigConst.DEFAULT_KEEP_ALIVE) self.mcc.connectClient() self.mcc.subscribeToTopic( resource=ResourceNameEnum.CDA_MGMT_STATUS_MSG_RESOURCE, qos=qos) sleep(5) self.mcc.publishMessage( resource=ResourceNameEnum.CDA_MGMT_STATUS_MSG_RESOURCE, msg="TEST: This is the CDA message payload.", qos=qos) sleep(5) self.mcc.unsubscribeFromTopic( resource=ResourceNameEnum.CDA_MGMT_STATUS_MSG_RESOURCE) sleep(5) sleep(delay) self.mcc.disconnectClient() @unittest.skip("Ignore for now.") def testActuatorCmdPubSub(self): qos = 0 delay = self.cfg.getInteger(ConfigConst.MQTT_GATEWAY_SERVICE, ConfigConst.KEEP_ALIVE_KEY, ConfigConst.DEFAULT_KEEP_ALIVE) actuatorData = ActuatorData() actuatorData.setCommand(7) self.mcc.setDataMessageListener(DefaultDataMessageListener()) payload = DataUtil().actuatorDataToJson(actuatorData) self.mcc.connectClient() sleep(5) self.mcc.publishMessage( resource=ResourceNameEnum.CDA_ACTUATOR_CMD_RESOURCE, msg=payload, qos=qos) sleep(delay + 5) self.mcc.disconnectClient() @unittest.skip("Ignore for now.") def testCDAManagementStatusSubscribe(self): qos = 1 delay = self.cfg.getInteger(ConfigConst.MQTT_GATEWAY_SERVICE, ConfigConst.KEEP_ALIVE_KEY, ConfigConst.DEFAULT_KEEP_ALIVE) self.mcc.connectClient() self.mcc.subscribeToTopic( resource=ResourceNameEnum.CDA_MGMT_STATUS_CMD_RESOURCE, qos=qos) sleep(delay) self.mcc.disconnectClient() @unittest.skip("Ignore for now.") def testCDAManagementStatusPublish(self): """ Uncomment this test when integration between the GDA and CDA using MQTT. """ qos = 1 delay = self.cfg.getInteger(ConfigConst.MQTT_GATEWAY_SERVICE, ConfigConst.KEEP_ALIVE_KEY, ConfigConst.DEFAULT_KEEP_ALIVE) self.mcc.connectClient() self.mcc.publishMessage( resource=ResourceNameEnum.CDA_MGMT_STATUS_MSG_RESOURCE, msg="TEST: This is the CDA message payload.", qos=qos) sleep(delay) self.mcc.disconnectClient()
class DeviceDataManager(IDataMessageListener): """ Shell representation of class for student implementation. """ def __init__(self, enableMqtt: bool = True, enableCoap: bool = False): # set enable connection self.enableMqtt = enableMqtt self.enableCoap = enableCoap # load config self.configUtil = ConfigUtil() self.enableEmulator = self.configUtil.getBoolean(ConfigConst.CONSTRAINED_DEVICE, ConfigConst.ENABLE_EMULATOR_KEY) self.stepmotor = StepMotorAdapterTask() # create system Perf manager self.sysPerfManager = SystemPerformanceManager() # set sensor config self.sam = SensorAdapterManager(useEmulator=self.enableEmulator) #set actuator config self.aam = ActuatorAdapterManager(useEmulator=self.enableEmulator) #set data generation config self.enableHandleTempChangeOnDevice = self.configUtil.getBoolean(ConfigConst.CONSTRAINED_DEVICE, ConfigConst.ENABLE_HANDLE_TEMP_CHANGE_ON_DEVICE_KEY) self.triggerHvacTempFloor = self.configUtil.getFloat(ConfigConst.CONSTRAINED_DEVICE, ConfigConst.TRIGGER_HVAC_TEMP_FLOOR_KEY); self.triggerHvacTempCeiling = self.configUtil.getFloat(ConfigConst.CONSTRAINED_DEVICE, ConfigConst.TRIGGER_HVAC_TEMP_CEILING_KEY); #self.stepmotor.moveTo(-40,-40,True) # set Mqtt client if enableMqtt is True: self.mqttClient = MqttClientConnector() #self.mqttClient.subscribeToTopic(ResourceNameEnum.CDA_ACTUATOR_RESPONSE_RESOURCE.value) #self.mqttClient.subscribeToTopic(ResourceNameEnum.CDA_ACTUATOR_CMD_RESOURCE.value) self.mqttClient.subscribeToTopic("ProgrammingIoT/StepMotor/Instruction") #self.mqttClient.mc.message_callback_add(callback=self.actuatorCallBack, sub=ResourceNameEnum.CDA_ACTUATOR_CMD_RESOURCE.value) self.mqttClient.mc.message_callback_add(callback=self.stepMotorCallBack, sub="ProgrammingIoT/StepMotor/Instruction") def actuatorCallBack(self,client, userdata, message): logging.info("MQTT CallBack:%s,%s" % (message.topic, message.payload)) self._handleIncomingDataAnalysis(message) def stepMotorCallBack(self,client, userdata, message): logging.info("MQTT CallBack:%s,%s" % (message.topic, message.payload)) point = str(message.payload.decode()).split(",") if len(point) == 2: self.stepmotor.moveTo(int(point[0]),int(point[1]),True) def handleActuatorCommandResponse(self, data: ActuatorData) -> bool: logging.info("handleActuatorCommandResponse called") #pass data to handler super().handleActuatorCommandResponse(data) #translate data to json and set updstream du = DataUtil() self._handleUpstreamTransmission(ResourceNameEnum.CDA_ACTUATOR_RESPONSE_RESOURCE.value, du.actuatorDataToJson(data)) def handleIncomingMessage(self, resourceEnum: ResourceNameEnum, msg: str) -> bool: logging.info("handleIncommingMessage called") #translate json to object and pass it to analysis du = DataUtil() du.jsonToActuatorData(msg) self._handleIncomingDataAnalysis(msg) def handleSensorMessage(self, data: SensorData) -> bool: logging.info("handleSensorMessage called") super().handleSensorMessage(data) # translate sensor data to json and handle it du = DataUtil() self._handleUpstreamTransmission(ResourceNameEnum.CDA_SENSOR_MSG_RESOURCE.value, du.sensorDataToJson(data)) self._handleSensorDataAnalysis(data) def handleSystemPerformanceMessage(self, data: SystemPerformanceData) -> bool: logging.info("handleSystemPerformanceMessage called") #translate sys perf data to json and set upstream du = DataUtil() self._handleUpstreamTransmission(ResourceNameEnum.CDA_SYSTEM_PERF_MSG_RESOURC.value, du.systemPerformanceDataToJson(data)) def startManager(self): #start manager self.sysPerfManager.startManager() self.sam.startManager() self.sam.setDataMessageListener(self) self.mqttClient.connectClient() def stopManager(self): #stop manager self.sysPerfManager.stopManager() self.sam.stopManager() self.mqttClient.disconnectClient() def _handleIncomingDataAnalysis(self, msg: str): """ Call this from handleIncomeMessage() to determine if there's any action to take on the message. Steps to take: 1) Validate msg: Most will be ActuatorData, but you may pass other info as well. 2) Convert msg: Use DataUtil to convert if appropriate. 3) Act on msg: Determine what - if any - action is required, and execute. """ logging.info("_handleIncomingDataAnalysis called,msg:"+str(msg)) #du = DataUtil() #self.aam.sendActuatorCommand(du.jsonToActuatorData(msg)) def _handleSensorDataAnalysis(self, data: SensorData): """ Call this from handleSensorMessage() to determine if there's any action to take on the message. Steps to take: 1) Check config: Is there a rule or flag that requires immediate processing of data? 2) Act on data: If # 1 is true, determine what - if any - action is required, and execute. """ logging.info("_handleSensorDataAnalysis called,msg:"+str(data)) if self.enableHandleTempChangeOnDevice is True: hvac = ActuatorData(ActuatorData.HVAC_ACTUATOR_TYPE) if data.getValue() < self.triggerHvacTempCeiling and data.getValue() > self.triggerHvacTempFloor: # start hvac when in trigger range hvac.setCommand(ActuatorData.COMMAND_ON) else: # stop hvac when not in range hvac.setCommand(ActuatorData.COMMAND_OFF) # send command self.aam.sendActuatorCommand(hvac) def handleActuatorCommandMessage(self, data: ActuatorData) -> bool: if data: logging.info("Processing actuator command message.") # TODO: add further validation before sending the command self.aam.sendActuatorCommand(data) return True else: logging.warning("Received invalid ActuatorData command message. Ignoring.") return False def _handleUpstreamTransmission(self, resourceName: ResourceNameEnum, msg: str): """ Call this from handleActuatorCommandResponse(), handlesensorMessage(), and handleSystemPerformanceMessage() to determine if the message should be sent upstream. Steps to take: 1) Check connection: Is there a client connection configured (and valid) to a remote MQTT or CoAP server? 2) Act on msg: If # 1 is true, send message upstream using one (or both) client connections. """ logging.info("_handleUpstreamTransmission called") # send message to mqtt broker if self.enableMqtt is True: self.mqttClient.publishMessage(resourceName, msg)