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.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.setDataMessageListener(listener)
        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()

    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.connect()

        sleep(5)

        self.mcc.publishMessage(resource=ResourceNameEnum.CDA_ACTUATOR_CMD_RESOURCE, msg=payload, qos=qos)

        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()
Esempio n. 2
0
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()
Esempio n. 3
0
class DeviceDataManager(IDataMessageListener):
    """
	Shell representation of class for student implementation.
	
	"""
    def __init__(self, enableMqtt: bool = True, enableCoap: bool = False):
        """
		Initialization of class.
		Create an instance of DeviceDataManager
		"""

        self.configUtil = ConfigUtil()

        self.enableEmulator = self.configUtil.getBoolean(
            ConfigConst.CONSTRAINED_DEVICE, ConfigConst.ENABLE_EMULATOR_KEY)

        self.enableRedis = False

        self.sysPerfManager = SystemPerformanceManager()
        self.sysPerfManager.setDataMessageListener(self)
        self.sensorAdapterManager = SensorAdapterManager(
            useEmulator=self.enableEmulator)
        self.sensorAdapterManager.setDataMessageListener(self)
        self.actuatorAdapterManager = ActuatorAdapterManager(
            useEmulator=self.enableEmulator)
        self.actuatorAdapterManager.setDataMessageListener(self)
        ##add by miaoyao @10/30/2020
        if self.enableRedis:
            self.redisClient = RedisPersistenceAdapter()

        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)
        ##add by miaoyao for final project

        self.enableHandleSoilHumidityChangeOnDevice = self.configUtil.getBoolean(
            ConfigConst.CONSTRAINED_DEVICE,
            ConfigConst.ENABLE_HANDLE_SOIL_HUMIDITY_CHANGE_ON_DEVICE_KEY)

        self.triggerWaterDeviceHumiFloor = self.configUtil.getFloat(
            ConfigConst.CONSTRAINED_DEVICE,
            ConfigConst.TRIGGER_WATER_SOIL_HUMI_FLOOR_KEY)

        self.triggerWaterDeviceHumiCeiling = self.configUtil.getFloat(
            ConfigConst.CONSTRAINED_DEVICE,
            ConfigConst.TRIGGER_WATER_SOIL_HUMI_CEILING_KEY)

        ##add by miaoyao @11/02/2020
        ##self.enableMqtt = self.configUtil.getBoolean(ConfigConst.CONSTRAINED_DEVICE, ConfigConst.ENABLE_MQTT_KEY)
        self.enableMqtt = enableMqtt
        self.enableCoap = enableCoap

        if self.enableMqtt:
            self.mqttClient = MqttClientConnector()
            self.mqttClient.setDataMessageListener(self)
        if self.enableCoap:
            self.coapClient = CoapClientConnector()
            self.coapClient.setDataMessageListener(self)

    def handleActuatorCommandResponse(self, data: ActuatorData) -> bool:
        """
		handle the ActuatorCommandResponse
		
		@return bool
		"""

        # Use the DataUtil class to convert the ActuatorData to JSON.
        logging.info(
            "[CDA_CALLBACK]----->>>The handleActuatorCommandResponse method is being called"
        )
        adJson = DataUtil.actuatorDataToJson(self, data)
        self._handleUpstreamTransmission(
            ResourceNameEnum.CDA_ACTUATOR_RESPONSE_RESOURCE, adJson)

    def handleActuatorCommandMessage(self, data: ActuatorData) -> bool:
        """
		handle the handleActuatorCommandMessage
		
		@return bool
		"""
        logging.info(
            "[CDA_CALLBACK]----->>>The handleActuatorCommandMessage method is being called"
        )
        if data:
            logging.info("Processing actuator command message.")

            # TODO: add further validation before sending the command
            self.actuatorAdapterManager.sendActuatorCommand(data)
            return True
        else:
            logging.warning(
                "Received invalid ActuatorData command message. Ignoring.")
            return False

    def handleIncomingMessage(self, resourceEnum: ResourceNameEnum,
                              msg: str) -> bool:
        """
		handle the IncomingMessage
		
		@return bool
		"""
        logging.info(
            "[CDA_CALLBACK]----->>>The handleIncomingMessage method is being called"
        )
        # Use the DataUtil class to convert the msg content (which should be JSON) to an ActuatorData instance
        ad = DataUtil.jsonToActuatorData(self, msg)
        self._handleIncomingDataAnalysis(msg)

    def handleSensorMessage(self, data: SensorData) -> bool:
        """
		handle the SensorMessage
		
		@return bool
		"""
        logging.info(
            "[CDA_CALLBACK]----->>>The handleSensorMessage method is being called"
        )
        # Use the DataUtil class to convert the SensorData to JSON
        sdJosn = DataUtil.sensorDataToJson(self, data)
        self._handleUpstreamTransmission(
            ResourceNameEnum.CDA_SENSOR_MSG_RESOURCE, sdJosn)
        self._handleSensorDataAnalysis(data)

        if self.enableRedis:
            self.redisClient.storeData(
                ResourceNameEnum.CDA_SENSOR_MSG_RESOURCE, data)

    def handleSystemPerformanceMessage(self,
                                       data: SystemPerformanceData) -> bool:
        """
		handle the SystemPerformanceMessage
		
		@return bool
		"""
        logging.info(
            "[CDA_CALLBACK]----->>>The handleSystemPerformanceMessage method is being called"
        )
        spmJson = DataUtil.systemPerformanceDataToJson(self, data)
        self._handleUpstreamTransmission(
            ResourceNameEnum.CDA_SYSTEM_PERF_MSG_RESOURCE, spmJson)

    def startManager(self):
        """
		Start the DeviceDataManager. 
		Calls startManager() on the sysPerfManager instance.
		Calls startManager() on the sensorAdapterManager instance.
		
		"""
        logging.info("----->>>The DeviceDataManager will be started")
        self.sysPerfManager.startManager()
        self.sensorAdapterManager.startManager()
        if self.enableRedis:
            self.redisClient.connectClient()

        if self.enableMqtt:
            self.mqttClient.connectClient()

    def stopManager(self):
        """
		Stop the DeviceDataManager. 
		Calls stopManager() on the sysPerfManager instance.
		Calls stopManager() on the sensorAdapterManager instance.
		
		"""
        self.sysPerfManager.stopManager()
        self.sensorAdapterManager.stopManager()

        if self.enableRedis:
            self.redisClient.disconnectClient()
        if self.enableMqtt:
            self.mqttClient.disconnectClient()

        logging.info("----->>>The DeviceDataManager stopped")

    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(
            "[CDA_CALLBACK]----->>>The _handleIncomingDataAnalysis method is being called"
        )
        ad = DataUtil.jsonToActuatorData(self, msg)
        self.actuatorAdapterManager.sendActuatorCommand(ad)

    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(
            "[CDA_CALLBACK]----->>>The _handleSensorDataAnalysis method is being called"
        )
        """
		"""
        if self.enableHandleTempChangeOnDevice and data.getSensorType(
        ) == SensorData.TEMP_SENSOR_TYPE:

            ad = ActuatorData(actuatorType=ActuatorData.HVAC_ACTUATOR_TYPE)
            value = data.getValue()
            if value >= self.triggerHvacTempFloor and value <= self.triggerHvacTempCeiling:
                ad.setCommand(ActuatorData.COMMAND_OFF)
            else:
                ad.setCommand(ActuatorData.COMMAND_ON)

            self.actuatorAdapterManager.sendActuatorCommand(ad)
        """
		"""
        if self.enableHandleSoilHumidityChangeOnDevice and data.getSensorType(
        ) == SensorData.SOIL_HUMIDITY_SENSOR_TYPE:

            ad = ActuatorData(
                actuatorType=ActuatorData.SPRINKLER_ACTUATOR_TYPE)
            value = data.getValue()
            if value >= self.triggerWaterDeviceHumiCeiling:
                ad.setCommand(ActuatorData.COMMAND_OFF)
                self.actuatorAdapterManager.sendActuatorCommand(ad)
            elif value <= self.triggerWaterDeviceHumiFloor:
                ad.setCommand(ActuatorData.COMMAND_ON)
                self.actuatorAdapterManager.sendActuatorCommand(ad)
                self.coapClient.sendGetRequest(
                    ResourceNameEnum.CDA_ACTUATOR_CMD_RESOURCE, False, 5)
            else:
                self.coapClient.sendGetRequest(
                    ResourceNameEnum.CDA_CLOUD_ACTUATOR_CMD_RESOURCE, False, 5)

    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(
            "[callback]----->>>The _handleUpstreamTransmission method is being called"
        )
        if self.enableMqtt:
            self.mqttClient.publishMessage(resourceName, msg)
        """