示例#1
0
    def test_split_data(self):
        action = master_api.basic_action()
        fields = {'action_type': 1, 'action_number': 2}

        sequence = []
        for i in range(1, 18):
            sequence.append(action.create_input(i, fields))

        pty = DummyPty(sequence)
        SetUpTestInjections(controller_serial=pty)

        comm = MasterCommunicator(init_master=False)
        comm.start()

        for i in range(1, 18):
            data = action.create_output(i, {'resp': 'OK'})

            ready = threading.Event()

            def write():
                pty.master_reply(data[:i])
                ready.set()
                pty.master_wait()
                pty.fd.write(data[i:])

            thread = threading.Thread(target=write)
            thread.start()

            ready.wait(2)
            output = comm.do_command(action, fields)
            self.assertEqual('OK', output['resp'])
            thread.join(2)
            assert not thread.is_alive()
示例#2
0
    def test_background_consumer_passthrough(self):
        action = master_api.basic_action()
        fields = {'action_type': 1, 'action_number': 2}

        pty = DummyPty([action.create_input(1, fields)])
        SetUpTestInjections(controller_serial=pty)

        got_output = {'passed': False}

        def callback(output_):
            """ Callback that check if the correct result was returned for OL. """
            self.assertEqual([(3, int(12 * 10.0 / 6.0))], output_['outputs'])
            got_output['passed'] = True

        consumer = BackgroundConsumer(master_api.output_list(), 0, callback, True)
        comm = MasterCommunicator(init_master=False)
        comm.enable_passthrough()
        comm.register_consumer(consumer)
        comm.start()

        pty.fd.write(bytearray(b'OL\x00\x01'))
        pty.fd.write(bytearray(b'\x03\x0c\r\n'))

        pty.master_reply(action.create_output(1, {'resp': 'OK'}))
        output = comm.do_command(action, fields)
        self.assertEqual('OK', output['resp'])

        try:
            consumer._consume()
        except:
            pass  # Just ensure it has at least consumed once

        self.assertEqual(True, got_output['passed'])
        self.assertEqual(bytearray(b'OL\x00\x01\x03\x0c\r\n'), comm.get_passthrough_data())
示例#3
0
    def test_background_consumer(self):
        action = master_api.basic_action()
        fields = {'action_type': 1, 'action_number': 2}

        pty = DummyPty([action.create_input(1, fields)])
        SetUpTestInjections(controller_serial=pty)

        got_output = {'phase': 1}

        def callback(output):
            """ Callback that check if the correct result was returned for OL. """
            if got_output['phase'] == 1:
                self.assertEqual([(3, int(12 * 10.0 / 6.0))], output['outputs'])
                got_output['phase'] = 2
            elif got_output['phase'] == 2:
                self.assertEqual([(3, int(12 * 10.0 / 6.0)), (5, int(6 * 10.0 / 6.0))],
                                 output['outputs'])
                got_output['phase'] = 3

        comm = MasterCommunicator(init_master=False)
        comm.enable_passthrough()
        comm.register_consumer(BackgroundConsumer(master_api.output_list(), 0, callback))
        comm.start()

        pty.fd.write(bytearray(b'OL\x00\x01\x03\x0c\r\n'))
        pty.fd.write(bytearray(b'junkOL\x00\x02\x03\x0c\x05\x06\r\n here'))

        pty.master_reply(action.create_output(1, {'resp': 'OK'}))
        output = comm.do_command(action, fields)
        self.assertEqual('OK', output['resp'])

        time.sleep(0.2)
        self.assertEqual(3, got_output['phase'])
        self.assertEqual(bytearray(b'junk here'), comm.get_passthrough_data())
示例#4
0
    def test_timeout(self):
        action = master_api.basic_action()
        fields = {'action_type': 1, 'action_number': 2}

        pty = DummyPty([action.create_input(1, fields)])
        SetUpTestInjections(controller_serial=pty)

        comm = MasterCommunicator(init_master=False)
        comm.start()

        self.assertRaises(CommunicationTimedOutException, comm.do_command, action, fields)
示例#5
0
    def test_do_command(self):
        action = master_api.basic_action()
        fields = {'action_type': 1, 'action_number': 2}

        pty = DummyPty([action.create_input(1, fields)])
        SetUpTestInjections(controller_serial=pty)

        comm = MasterCommunicator(init_master=False)
        comm.start()

        pty.master_reply(action.create_output(1, {'resp': 'OK'}))
        output = comm.do_command(action, fields)
        self.assertEqual('OK', output['resp'])
示例#6
0
    def test_passthrough(self):
        pty = DummyPty([b'data from passthrough'])
        SetUpTestInjections(controller_serial=pty)

        comm = MasterCommunicator(init_master=False)
        comm.enable_passthrough()
        comm.start()

        pty.master_reply(bytearray(b'got it!'))
        comm.send_passthrough_data(bytearray(b'data from passthrough'))
        self.assertEqual(bytearray(b'got it!'), comm.get_passthrough_data())
示例#7
0
def setup_minimal_master_platform(port):
    # type: (str) -> None
    config = ConfigParser()
    config.read(constants.get_config_file())

    platform = Platform.get_platform()
    Injectable.value(controller_serial=Serial(port, 115200))

    if platform == Platform.Type.DUMMY:
        Injectable.value(maintenance_communicator=None)
        Injectable.value(master_controller=MasterDummyController())
    elif platform in Platform.CoreTypes:
        from master.core import ucan_communicator
        _ = ucan_communicator
        core_cli_serial_port = config.get('OpenMotics', 'cli_serial')
        Injectable.value(cli_serial=Serial(core_cli_serial_port, 115200))
        Injectable.value(master_communicator=CoreCommunicator())
        Injectable.value(maintenance_communicator=None)
        Injectable.value(memory_file=MemoryFile())
        Injectable.value(master_controller=MasterCoreController())
    elif platform in Platform.ClassicTypes:
        Injectable.value(
            eeprom_db=constants.get_eeprom_extension_database_file())
        from master.classic import eeprom_extension
        _ = eeprom_extension
        Injectable.value(master_communicator=MasterCommunicator())
        Injectable.value(maintenance_communicator=None)
        Injectable.value(master_controller=MasterClassicController())
    else:
        logger.warning('Unhandled master implementation for %s', platform)
示例#8
0
    def test_timeout_ongoing(self):
        action = master_api.basic_action()
        fields = {'action_type': 1, 'action_number': 2}

        pty = DummyPty([action.create_input(1, fields),
                        action.create_input(2, fields)])
        SetUpTestInjections(controller_serial=pty)

        comm = MasterCommunicator(init_master=False)
        comm.start()

        self.assertRaises(CommunicationTimedOutException, comm.do_command, action, fields,
                          timeout=0.1)

        pty.master_reply(action.create_output(2, {'resp': 'OK'}))
        output = comm.do_command(action, fields)
        self.assertEqual('OK', output['resp'])
示例#9
0
    def test_crc_checking(self):
        action = master_api.sensor_humidity_list()

        fields1 = {}
        for i in range(0, 32):
            fields1['hum%d' % i] = master_api.Svt(master_api.Svt.RAW, i)
        fields1['crc'] = bytearray([ord('C'), 1, 240])

        fields2 = {}
        for i in range(0, 32):
            fields2['hum%d' % i] = master_api.Svt(master_api.Svt.RAW, 2 * i)
        fields2['crc'] = bytearray([ord('C'), 0, 0])

        pty = DummyPty([action.create_input(1),
                        action.create_input(2)])
        SetUpTestInjections(controller_serial=pty)

        comm = MasterCommunicator(init_master=False)
        comm.start()

        pty.master_reply(action.create_output(1, fields1))
        output = comm.do_command(action)
        self.assertEqual(bytearray(b'\x00'), output['hum0'].get_byte())
        self.assertEqual(bytearray(b'\x01'), output['hum1'].get_byte())

        pty.master_reply(action.create_output(2, fields2))
        with self.assertRaises(CrcCheckFailedException):
            comm.do_command(action)
示例#10
0
    def test_passthrough(self):
        """ Test the passthrough. """
        master_pty = DummyPty(
            [bytearray(b'response'),
             bytearray(b'more response')])
        passthrough_mock = SerialMock([
            sin(bytearray(b'data for the passthrough')),
            sout(bytearray(b'response')),
            sin(bytearray(b'more data')),
            sout(bytearray(b'more response'))
        ])
        SetUpTestInjections(controller_serial=master_pty,
                            passthrough_serial=passthrough_mock)

        master_communicator = MasterCommunicator(init_master=False)
        master_communicator.enable_passthrough()
        master_communicator.start()

        SetUpTestInjections(master_communicator=master_communicator)

        passthrough = PassthroughService()
        passthrough.start()

        master_pty.fd.write(bytearray(b'data for the passthrough'))
        master_pty.master_wait()
        master_pty.fd.write(bytearray(b'more data'))
        master_pty.master_wait()
        time.sleep(0.2)

        self.assertEqual(
            33,
            master_communicator.get_communication_statistics()['bytes_read'])
        self.assertEqual(
            21,
            master_communicator.get_communication_statistics()
            ['bytes_written'])

        self.assertEqual(21, passthrough_mock.bytes_read)
        self.assertEqual(33, passthrough_mock.bytes_written)

        passthrough.stop()
示例#11
0
    def test_misalignment(self):
        action = master_api.basic_action()
        fields = {'action_type': 1, 'action_number': 2}

        pty = DummyPty([action.create_input(1, fields),
                        action.create_input(2, fields)])
        SetUpTestInjections(controller_serial=pty)

        comm = MasterCommunicator(init_master=False)
        comm.start()

        data = action.create_output(1, {'resp': 'OK'})
        # Only send partial data.
        pty.master_reply(data[:-4])
        pty.master_reply(b'\r\n')
        with self.assertRaises(CommunicationTimedOutException):
            comm.do_command(action, fields)

        # Next message processed correctly.
        pty.master_reply(action.create_output(2, {'resp': 'OK'}))
        output = comm.do_command(action, fields)
        self.assertEqual('OK', output['resp'])
示例#12
0
def setup_target_platform(target_platform, message_client_name):
    # type: (str, Optional[str]) -> None
    config = ConfigParser()
    config.read(constants.get_config_file())

    config_lock = Lock()
    metrics_lock = Lock()

    config_database_file = constants.get_config_database_file()

    # Debugging options
    try:
        debug_logger = config.get('OpenMotics', 'debug_logger')
        if debug_logger:
            logging.getLogger(debug_logger).setLevel(logging.DEBUG)
    except NoOptionError:
        pass

    # Webserver / Presentation layer
    try:
        https_port = int(config.get('OpenMotics', 'https_port'))
    except NoOptionError:
        https_port = 443
    try:
        http_port = int(config.get('OpenMotics', 'http_port'))
    except NoOptionError:
        http_port = 80
    Injectable.value(https_port=https_port)
    Injectable.value(http_port=http_port)
    Injectable.value(ssl_private_key=constants.get_ssl_private_key_file())
    Injectable.value(ssl_certificate=constants.get_ssl_certificate_file())

    # TODO: Clean up dependencies more to reduce complexity

    # IOC announcements
    # When below modules are imported, the classes are registerd in the IOC graph. This is required for
    # instances that are used in @Inject decorated functions below, and is also needed to specify
    # abstract implementations depending on e.g. the platform (classic vs core) or certain settings (classic
    # thermostats vs gateway thermostats)
    from plugins import base
    from gateway import (metrics_controller, webservice, scheduling, observer,
                         gateway_api, metrics_collector,
                         maintenance_controller, user_controller,
                         pulse_counter_controller, metrics_caching, watchdog,
                         output_controller, room_controller, sensor_controller,
                         shutter_controller, group_action_controller,
                         module_controller, ventilation_controller)
    from cloud import events
    _ = (metrics_controller, webservice, scheduling, observer, gateway_api,
         metrics_collector, maintenance_controller, base, events,
         user_controller, pulse_counter_controller, metrics_caching, watchdog,
         output_controller, room_controller, sensor_controller,
         shutter_controller, group_action_controller, module_controller,
         ventilation_controller)

    # IPC
    message_client = None
    if message_client_name is not None:
        message_client = MessageClient(message_client_name)
    Injectable.value(message_client=message_client)

    # Cloud API
    Injectable.value(gateway_uuid=config.get('OpenMotics', 'uuid'))

    try:
        parsed_url = urlparse(config.get('OpenMotics', 'vpn_check_url'))
    except NoOptionError:
        parsed_url = urlparse('')
    Injectable.value(cloud_endpoint=parsed_url.hostname)
    Injectable.value(cloud_port=parsed_url.port)
    Injectable.value(cloud_ssl=parsed_url.scheme == 'https')
    Injectable.value(cloud_api_version=0)

    cloud_url = urlunparse(
        (parsed_url.scheme, parsed_url.netloc, '', '', '', ''))
    Injectable.value(cloud_url=cloud_url or None)

    try:
        firmware_url = config.get('OpenMotics', 'firmware_url')
    except NoOptionError:
        path = '/portal/firmware_metadata'
        firmware_url = urlunparse(
            (parsed_url.scheme, parsed_url.netloc, path, '', '', ''))
    Injectable.value(firmware_url=firmware_url or None)

    # User Controller
    Injectable.value(user_db=config_database_file)
    Injectable.value(user_db_lock=config_lock)
    Injectable.value(token_timeout=3600)
    Injectable.value(
        config={
            'username': config.get('OpenMotics', 'cloud_user'),
            'password': config.get('OpenMotics', 'cloud_pass')
        })

    # Metrics Controller
    Injectable.value(metrics_db=constants.get_metrics_database_file())
    Injectable.value(metrics_db_lock=metrics_lock)

    # Energy Controller
    try:
        power_serial_port = config.get('OpenMotics', 'power_serial')
    except NoOptionError:
        power_serial_port = ''
    if power_serial_port:
        Injectable.value(power_db=constants.get_power_database_file())
        Injectable.value(power_store=PowerStore())
        # TODO: make non blocking?
        Injectable.value(power_serial=RS485(
            Serial(power_serial_port, 115200, timeout=None)))
        Injectable.value(power_communicator=PowerCommunicator())
        Injectable.value(power_controller=PowerController())
        Injectable.value(p1_controller=P1Controller())
    else:
        Injectable.value(power_serial=None)
        Injectable.value(power_store=None)
        Injectable.value(
            power_communicator=None)  # TODO: remove from gateway_api
        Injectable.value(power_controller=None)
        Injectable.value(p1_controller=None)

    # Pulse Controller
    Injectable.value(pulse_db=constants.get_pulse_counter_database_file())

    # Master Controller
    try:
        controller_serial_port = config.get('OpenMotics', 'controller_serial')
    except NoOptionError:
        controller_serial_port = ''

    if controller_serial_port:
        Injectable.value(controller_serial=Serial(
            controller_serial_port, 115200, exclusive=True))
    if target_platform in [Platform.Type.DUMMY, Platform.Type.ESAFE]:
        Injectable.value(maintenance_communicator=None)
        Injectable.value(passthrough_service=None)
        Injectable.value(master_controller=MasterDummyController())
        Injectable.value(eeprom_db=None)
        from gateway.hal.master_controller_dummy import DummyEepromObject
        Injectable.value(eeprom_extension=DummyEepromObject())
    elif target_platform in Platform.CoreTypes:
        # FIXME don't create singleton for optional controller?
        from master.core import ucan_communicator, slave_communicator
        _ = ucan_communicator, slave_communicator
        core_cli_serial_port = config.get('OpenMotics', 'cli_serial')
        Injectable.value(cli_serial=Serial(core_cli_serial_port, 115200))
        Injectable.value(passthrough_service=None)  # Mark as "not needed"
        # TODO: Remove; should not be needed for Core
        Injectable.value(
            eeprom_db=constants.get_eeprom_extension_database_file())

        Injectable.value(master_communicator=CoreCommunicator())
        Injectable.value(
            maintenance_communicator=MaintenanceCoreCommunicator())
        Injectable.value(memory_file=MemoryFile())
        Injectable.value(master_controller=MasterCoreController())
    elif target_platform in Platform.ClassicTypes:
        # FIXME don't create singleton for optional controller?
        from master.classic import eeprom_extension
        _ = eeprom_extension
        leds_i2c_address = config.get('OpenMotics', 'leds_i2c_address')
        passthrough_serial_port = config.get('OpenMotics',
                                             'passthrough_serial')
        Injectable.value(
            eeprom_db=constants.get_eeprom_extension_database_file())
        Injectable.value(leds_i2c_address=int(leds_i2c_address, 16))
        if passthrough_serial_port:
            Injectable.value(
                passthrough_serial=Serial(passthrough_serial_port, 115200))
            from master.classic.passthrough import PassthroughService
            _ = PassthroughService  # IOC announcement
        else:
            Injectable.value(passthrough_service=None)
        Injectable.value(master_communicator=MasterCommunicator())
        Injectable.value(
            maintenance_communicator=MaintenanceClassicCommunicator())
        Injectable.value(master_controller=MasterClassicController())
    else:
        logger.warning('Unhandled master implementation for %s',
                       target_platform)

    if target_platform in [Platform.Type.DUMMY, Platform.Type.ESAFE]:
        Injectable.value(frontpanel_controller=None)
    elif target_platform in Platform.CoreTypes:
        Injectable.value(frontpanel_controller=FrontpanelCoreController())
    elif target_platform in Platform.ClassicTypes:
        Injectable.value(frontpanel_controller=FrontpanelClassicController())
    else:
        logger.warning('Unhandled frontpanel implementation for %s',
                       target_platform)

    # Thermostats
    thermostats_gateway_feature = Feature.get_or_none(
        name='thermostats_gateway')
    thermostats_gateway_enabled = thermostats_gateway_feature is not None and thermostats_gateway_feature.enabled
    if target_platform not in Platform.ClassicTypes or thermostats_gateway_enabled:
        Injectable.value(thermostat_controller=ThermostatControllerGateway())
    else:
        Injectable.value(thermostat_controller=ThermostatControllerMaster())
示例#13
0
    def test_bytes_counter(self):
        action = master_api.basic_action()
        fields = {'action_type': 1, 'action_number': 2}

        pty = DummyPty([action.create_input(1, fields)])
        SetUpTestInjections(controller_serial=pty)

        comm = MasterCommunicator(init_master=False)
        comm.enable_passthrough()
        comm.start()

        pty.fd.write(bytearray(b'hello'))
        pty.master_reply(action.create_output(1, {'resp': 'OK'}))
        comm.do_command(action, fields)
        self.assertEqual(bytearray(b'hello'), comm.get_passthrough_data())

        self.assertEqual(21, comm.get_communication_statistics()['bytes_written'])
        self.assertEqual(5 + 18, comm.get_communication_statistics()['bytes_read'])
示例#14
0
    def test_maintenance_passthrough(self):
        action = master_api.basic_action()
        fields = {'action_type': 1, 'action_number': 2}

        pty = DummyPty([master_api.to_cli_mode().create_input(0),
                        bytearray(b'error list\r\n'), bytearray(b'exit\r\n')])
        SetUpTestInjections(controller_serial=pty)

        comm = MasterCommunicator(init_master=False)
        comm.enable_passthrough()
        comm.start()

        ready = threading.Event()

        def get_passthrough():
            """ Background thread that reads the passthrough data. """
            self.assertEqual('Before maintenance', comm.get_passthrough_data())
            ready.set()
            self.assertEqual('After maintenance', comm.get_passthrough_data())

        thread = threading.Thread(target=get_passthrough)
        thread.start()

        pty.fd.write(bytearray(b'Before maintenance'))
        ready.wait(2)
        comm.start_maintenance_mode()

        pty.fd.write(bytearray(b'OK'))
        time.sleep(0.2)
        self.assertRaises(InMaintenanceModeException, comm.do_command, action, fields)
        self.assertEqual(bytearray(b'OK'), comm.get_maintenance_data())

        comm.send_maintenance_data(bytearray(b'error list\r\n'))
        pty.fd.write(bytearray(b'the list\n'))
        time.sleep(0.2)
        self.assertEqual(bytearray(b'the list\n'), comm.get_maintenance_data())

        comm.stop_maintenance_mode()
        pty.fd.write(bytearray(b'After maintenance'))

        thread.join(2)
        assert not thread.is_alive()
示例#15
0
    def test_maintenance_mode(self):
        action = master_api.basic_action()
        fields = {'action_type': 1, 'action_number': 2}

        pty = DummyPty([master_api.to_cli_mode().create_input(0),
                        bytearray(b'error list\r\n'), bytearray(b'exit\r\n')])
        SetUpTestInjections(controller_serial=pty)

        comm = MasterCommunicator(init_master=False)
        comm.start()

        comm.start_maintenance_mode()
        pty.fd.write(bytearray(b'OK'))
        self.assertRaises(InMaintenanceModeException, comm.do_command, action, fields)
        self.assertEqual(bytearray(b'OK'), comm.get_maintenance_data())

        comm.send_maintenance_data(bytearray(b'error list\r\n'))
        pty.fd.write(bytearray(b'the list\n'))
        self.assertEqual(bytearray(b'the list\n'), comm.get_maintenance_data())

        comm.stop_maintenance_mode()
示例#16
0
    def test_passthrough_with_commands(self):
        action = master_api.basic_action()
        fields = {'action_type': 1, 'action_number': 2}

        pty = DummyPty([action.create_input(1, fields),
                        action.create_input(2, fields),
                        action.create_input(3, fields)])
        SetUpTestInjections(controller_serial=pty)

        comm = MasterCommunicator(init_master=False)
        comm.enable_passthrough()
        comm.start()

        pty.master_reply(bytearray(b'hello') + action.create_output(1, {'resp': 'OK'}))
        self.assertEqual('OK', comm.do_command(action, fields)['resp'])
        self.assertEqual(bytearray(b'hello'), comm.get_passthrough_data())

        pty.master_reply(action.create_output(2, {'resp': 'OK'}) + bytearray(b'world'))
        self.assertEqual('OK', comm.do_command(action, fields)['resp'])
        self.assertEqual(bytearray(b'world'), comm.get_passthrough_data())

        pty.master_reply(bytearray(b'hello') + action.create_output(3, {'resp': 'OK'}) + bytearray(b' world'))
        self.assertEqual('OK', comm.do_command(action, fields)['resp'])
        self.assertEqual(bytearray(b'hello world'), comm.get_passthrough_data())