def test_upload_is_only_valid_if_chunk_count_matches_expected(self):
        storage = LongMessageStorage(MemoryStorage(), MemoryStorage())
        chunks = [b'12345', b'1234568', b'98765432']

        checksum = hashlib.md5()
        for chunk in chunks:
            checksum.update(chunk)

        expected_md5 = checksum.hexdigest()

        handler = LongMessageHandler(storage)
        handler.select_long_message_type(LongMessageType.FIRMWARE_DATA)

        expectations = ((len(chunks), LongMessageStatus.READY),
                        (0, LongMessageStatus.READY),
                        (len(chunks) - 1, LongMessageStatus.VALIDATION_ERROR),
                        (len(chunks) + 1, LongMessageStatus.VALIDATION_ERROR))

        for length, expected_status in expectations:
            handler.init_transfer(expected_md5, length)
            for chunk in chunks:
                handler.upload_message(chunk)
            handler.finalize_message()

            status = handler.read_status()
            self.assertEqual(expected_status, status.status)
Exemple #2
0
    def test_reading_unused_message_returns_zero(self):
        persistent = MemoryStorage()
        temp = MemoryStorage()

        storage = LongMessageStorage(persistent, temp)
        handler = LongMessageHandler(storage)
        ble = LongMessageProtocol(handler)

        ble.handle_write(0, [2])  # select long message 2
        result = ble.handle_read()

        # unused long message response is a 0 byte
        self.assertEqual(b'\x00', result)
    def test_upload_message_with_one_byte_is_accepted(self):
        persistent = MemoryStorage()
        temp = MemoryStorage()

        storage = LongMessageStorage(persistent, temp)
        handler = LongMessageHandler(storage)
        ble = LongMessageProtocol(handler)

        ble.handle_write(0, [2])  # select long message 2
        ble.handle_write(1, bytes([0] * 16))  # init
        self.assertEqual(
            LongMessageProtocol.RESULT_SUCCESS,
            ble.handle_write(MessageType.UPLOAD_MESSAGE, bytes([2])))
Exemple #4
0
    def test_read_returns_hash(self):
        persistent = MemoryStorage()
        persistent.write(2, b'abcd')

        md5_hash = hashlib.md5(b'abcd').hexdigest()

        temp = MemoryStorage()

        storage = LongMessageStorage(persistent, temp)
        handler = LongMessageHandler(storage)
        ble = LongMessageProtocol(handler)

        ble.handle_write(0, [2])  # select long message 2 (persistent)
        result = ble.handle_read()

        # reading a valid message returns its status, md5 hash and length
        self.assertEqual("03" + md5_hash + "00000004", bytes2hexdigest(result))
Exemple #5
0
    # noinspection PyBroadException
    try:
        device_name = device_storage.read('device-name').decode("ascii")

        if 0 == len(device_name) or len(device_name) > 15:
            device_name = f'Revvy_{serial}'
    except Exception:
        device_name = f'Revvy_{serial}'

    print(f'Device name: {device_name}')

    device_name = Observable(device_name)
    device_name.subscribe(
        lambda v: device_storage.write('device-name', v.encode("utf-8")))

    long_message_storage = LongMessageStorage(ble_storage, MemoryStorage())
    extract_asset_longmessage(long_message_storage, writeable_assets_dir)

    with Robot() as robot:
        robot.assets.add_source(writeable_assets_dir)

        long_message_handler = LongMessageHandler(long_message_storage)
        robot_manager = RobotBLEController(
            robot, sw_version,
            RevvyBLE(device_name, serial, long_message_handler))

        lmi = LongMessageImplementation(robot_manager, long_message_storage,
                                        writeable_assets_dir, False)
        long_message_handler.on_upload_started(lmi.on_upload_started)
        long_message_handler.on_upload_progress(lmi.on_upload_progress)
        long_message_handler.on_upload_finished(lmi.on_transmission_finished)
def start_revvy(config: RobotConfig = None):
    current_installation = os.path.dirname(os.path.realpath(__file__))
    os.chdir(current_installation)

    # base directories
    package_data_dir = os.path.join(current_installation, 'data')

    print('Revvy run from {} ({})'.format(current_installation, __file__))

    # prepare environment

    serial = getserial()

    manifest = read_json('manifest.json')

    sound_files = {
        'alarm_clock': 'alarm_clock.mp3',
        'bell': 'bell.mp3',
        'buzzer': 'buzzer.mp3',
        'car_horn': 'car-horn.mp3',
        'cat': 'cat.mp3',
        'dog': 'dog.mp3',
        'duck': 'duck.mp3',
        'engine_revving': 'engine-revving.mp3',
        'lion': 'lion.mp3',
        'oh_no': 'oh-no.mp3',
        'robot': 'robot.mp3',
        'robot2': 'robot2.mp3',
        'siren': 'siren.mp3',
        'ta_da': 'tada.mp3',
        'uh_oh': 'uh-oh.mp3',
        'yee_haw': 'yee-haw.mp3',
    }

    def sound_path(file):
        return os.path.join(package_data_dir, 'assets', file)

    sound_paths = {key: sound_path(sound_files[key]) for key in sound_files}

    device_name = Observable("ROS")

    ble_storage_dir = os.path.join(current_installation, 'ble')
    ble_storage = FileStorage(ble_storage_dir)
    long_message_storage = LongMessageStorage(ble_storage, MemoryStorage())
    long_message_handler = LongMessageHandler(long_message_storage)

    ble = RevvyBLE(device_name, serial, long_message_handler)

    # if the robot has never been configured, set the default configuration for the simple robot
    initial_config = default_robot_config

    with RevvyTransportI2C() as transport:
        robot_control = RevvyControl(transport.bind(0x2D))

        robot = RobotManager(robot_control, ble, sound_paths,
                             manifest['version'], initial_config)

        lmi = LongMessageImplementation(robot, config is not None)
        long_message_handler.on_upload_started(lmi.on_upload_started)
        long_message_handler.on_upload_finished(lmi.on_transmission_finished)
        long_message_handler.on_message_updated(lmi.on_message_updated)

        # noinspection PyBroadException
        try:
            robot.start()

            print("Press Enter to exit")
            input()
            # manual exit
            ret_val = RevvyStatusCode.OK
        except EOFError:
            robot.needs_interrupting = False
            while not robot.exited:
                time.sleep(1)
            ret_val = robot.status_code
        except KeyboardInterrupt:
            # manual exit or update request
            ret_val = robot.status_code
        except Exception:
            print(traceback.format_exc())
            ret_val = RevvyStatusCode.ERROR
        finally:
            print('stopping')
            robot.stop()

        print('terminated.')
        return ret_val
Exemple #7
0
def start_revvy(config: RobotConfig = None):
    current_installation = os.path.dirname(os.path.realpath(__file__))
    os.chdir(current_installation)

    # base directories
    writeable_data_dir = os.path.join(current_installation, '..', '..', '..',
                                      'user')
    package_data_dir = os.path.join(current_installation, 'data')

    ble_storage_dir = os.path.join(writeable_data_dir, 'ble')
    data_dir = os.path.join(writeable_data_dir, 'data')

    def log_uncaught_exception(exctype, value, tb):
        log_message = 'Uncaught exception: {}\n' \
                      'Value: {}\n' \
                      'Traceback: \n\t{}\n' \
                      '\n'.format(exctype, value, "\t".join(traceback.format_tb(tb)))
        print(log_message)
        logfile = os.path.join(data_dir, 'revvy_crash.log')

        with open(logfile, 'a') as logf:
            logf.write(log_message)

    sys.excepthook = log_uncaught_exception

    # self-test
    if not check_manifest(os.path.join(current_installation, 'manifest.json')):
        print('Revvy not started because manifest is invalid')
        return RevvyStatusCode.INTEGRITY_ERROR

    print('Revvy run from {} ({})'.format(current_installation, __file__))

    # prepare environment

    serial = getserial()

    manifest = read_json('manifest.json')

    device_storage = FileStorage(data_dir)
    ble_storage = FileStorage(ble_storage_dir)

    sound_files = {
        'alarm_clock': 'alarm_clock.mp3',
        'bell': 'bell.mp3',
        'buzzer': 'buzzer.mp3',
        'car_horn': 'car-horn.mp3',
        'cat': 'cat.mp3',
        'dog': 'dog.mp3',
        'duck': 'duck.mp3',
        'engine_revving': 'engine-revving.mp3',
        'lion': 'lion.mp3',
        'oh_no': 'oh-no.mp3',
        'robot': 'robot.mp3',
        'robot2': 'robot2.mp3',
        'siren': 'siren.mp3',
        'ta_da': 'tada.mp3',
        'uh_oh': 'uh-oh.mp3',
        'yee_haw': 'yee-haw.mp3',
    }

    def sound_path(file):
        return os.path.join(package_data_dir, 'assets', file)

    sound_paths = {key: sound_path(sound_files[key]) for key in sound_files}

    dnp = DeviceNameProvider(device_storage, lambda: 'Revvy_{}'.format(serial))
    device_name = Observable(dnp.get_device_name())
    device_name.subscribe(dnp.update_device_name)

    long_message_storage = LongMessageStorage(ble_storage, MemoryStorage())
    long_message_handler = LongMessageHandler(long_message_storage)

    ble = RevvyBLE(device_name, serial, long_message_handler)

    # if the robot has never been configured, set the default configuration for the simple robot
    initial_config = config
    if config is None:
        status = long_message_storage.read_status(
            LongMessageType.CONFIGURATION_DATA)
        if status.status != LongMessageStatus.READY:
            initial_config = default_robot_config

    with RevvyTransportI2C() as transport:
        robot_control = RevvyControl(transport.bind(0x2D))
        bootloader_control = BootloaderControl(transport.bind(0x2B))

        updater = McuUpdater(robot_control, bootloader_control)
        update_manager = McuUpdateManager(
            os.path.join(package_data_dir, 'firmware'), updater)
        update_manager.update_if_necessary()

        robot = RobotManager(robot_control, ble, sound_paths,
                             manifest['version'], initial_config)

        lmi = LongMessageImplementation(robot, config is not None)
        long_message_handler.on_upload_started(lmi.on_upload_started)
        long_message_handler.on_upload_finished(lmi.on_transmission_finished)
        long_message_handler.on_message_updated(lmi.on_message_updated)

        # noinspection PyBroadException
        try:
            robot.start()

            print("Press Enter to exit")
            input()
            # manual exit
            ret_val = RevvyStatusCode.OK
        except EOFError:
            robot.needs_interrupting = False
            while not robot.exited:
                time.sleep(1)
            ret_val = robot.status_code
        except KeyboardInterrupt:
            # manual exit or update request
            ret_val = robot.status_code
        except Exception:
            print(traceback.format_exc())
            ret_val = RevvyStatusCode.ERROR
        finally:
            print('stopping')
            robot.stop()

        print('terminated.')
        return ret_val