class ActuatorAdapterManagerTest(unittest.TestCase):
    """
	This test case class contains very basic unit tests for
	ActuatorSimAdapterManager. 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 ActuatorAdapterManager class...")
        self.configUtil = ConfigUtil()
        self.enableEmulator = self.configUtil.getBoolean(
            ConfigConst.CONSTRAINED_DEVICE, ConfigConst.ENABLE_EMULATOR_KEY)
        self.defaultMsgListener = DefaultDataMessageListener()
        self.actuatorAdapterMgr = ActuatorAdapterManager(
            useEmulator=self.enableEmulator)
        self.actuatorAdapterMgr.setDataMessageListener(self.defaultMsgListener)

    def setUp(self):
        pass

    def tearDown(self):
        pass

    def testHumidifierSimulation(self):
        ad = ActuatorData(actuatorType=ActuatorData.HUMIDIFIER_ACTUATOR_TYPE)
        ad.setValue(50.0)

        ad.setCommand(ActuatorData.COMMAND_ON)
        self.actuatorAdapterMgr.sendActuatorCommand(ad)

        ad.setCommand(ActuatorData.COMMAND_OFF)
        self.actuatorAdapterMgr.sendActuatorCommand(ad)

    def testHvacSimulation(self):
        ad = ActuatorData(actuatorType=ActuatorData.HVAC_ACTUATOR_TYPE)
        ad.setValue(22.5)

        ad.setCommand(ActuatorData.COMMAND_ON)
        self.actuatorAdapterMgr.sendActuatorCommand(ad)

        ad.setCommand(ActuatorData.COMMAND_OFF)
        self.actuatorAdapterMgr.sendActuatorCommand(ad)

    def testLEDSimulation(self):
        ad = ActuatorData(actuatorType=ActuatorData.LED_DISPLAY_ACTUATOR_TYPE)
        ad.setStateData(stateData="Test stateData")

        ad.setCommand(ActuatorData.COMMAND_ON)
        self.actuatorAdapterMgr.sendActuatorCommand(ad)

        ad.setCommand(ActuatorData.COMMAND_OFF)
        self.actuatorAdapterMgr.sendActuatorCommand(ad)
class ActuatorEmulatorManagerTest(unittest.TestCase):
    """
	This test case class contains very basic unit tests for
	ActuatorSimAdapterManager. It should not be considered complete,
	but serve as a starting point for the student implementing
	additional functionality within their Programming the IoT
	environment.
	
	NOTE: This test requires the sense_emu_gui to be running
	and must have access to the underlying libraries that
	support the pisense module. On Windows, one way to do
	this is by installing pisense and sense-emu within the
	Bash on Ubuntu on Windows environment and then execute this
	test case from the command line, as it will likely fail
	if run within an IDE in native Windows.
	
	"""
    @classmethod
    def setUpClass(self):
        logging.basicConfig(
            format='%(asctime)s:%(module)s:%(levelname)s:%(message)s',
            level=logging.DEBUG)
        logging.info(
            "Testing ActuatorAdapterManager class [using SenseHAT emulator]..."
        )

        self.defaultMsgListener = DefaultDataMessageListener()
        self.actuatorAdapterMgr = ActuatorAdapterManager(useEmulator=True)
        self.actuatorAdapterMgr.setDataMessageListener(self.defaultMsgListener)

    def setUp(self):
        pass

    def tearDown(self):
        pass

    def testHumidifierEmulation(self):
        ad = ActuatorData(actuatorType=ConfigConst.HUMIDIFIER_ACTUATOR_TYPE)
        ad.setValue(50.0)

        ad.setCommand(ConfigConst.COMMAND_ON)
        self.actuatorAdapterMgr.sendActuatorCommand(ad)

        ad.setCommand(ConfigConst.COMMAND_OFF)
        self.actuatorAdapterMgr.sendActuatorCommand(ad)

    def testHvacEmulation(self):
        ad = ActuatorData(actuatorType=ConfigConst.HVAC_ACTUATOR_TYPE)
        ad.setValue(22.5)

        ad.setCommand(ConfigConst.COMMAND_ON)
        self.actuatorAdapterMgr.sendActuatorCommand(ad)

        ad.setCommand(ConfigConst.COMMAND_OFF)
        self.actuatorAdapterMgr.sendActuatorCommand(ad)

    def testLedDisplayEmulation(self):
        ad = ActuatorData(actuatorType=ConfigConst.LED_DISPLAY_ACTUATOR_TYPE)
        ad.setCommand(ConfigConst.COMMAND_ON)
        ad.setStateData("What's up?")
        self.actuatorAdapterMgr.sendActuatorCommand(ad)

        ad.setCommand(ConfigConst.COMMAND_OFF)
        self.actuatorAdapterMgr.sendActuatorCommand(ad)
Ejemplo 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)
        """