Exemple #1
0
    def inject_type(self, name, typeobj):
        """
        Given a module-like object that defines a type, add it to our type system so that
        it can be used with the iotile tool and with other annotated API functions.
        """

        name = self._canonicalize_type(name)
        _, is_complex, _ = self.split_type(name)

        if self.is_known_type(name):
            raise ArgumentError(
                "attempting to inject a type that is already defined",
                type=name)

        if (not is_complex) and self._is_factory(typeobj):
            if name in self.type_factories:
                raise ArgumentError(
                    "attempted to inject a complex type factory that is already defined",
                    type=name)
            self.type_factories[name] = typeobj
        else:
            self._validate_type(typeobj)
            self.known_types[name] = typeobj

        if not hasattr(typeobj, "default_formatter"):
            raise ArgumentError(
                "type is invalid, does not have default_formatter function",
                type=typeobj,
                methods=dir(typeobj))
Exemple #2
0
    def instantiate_type(self, type_or_name, base, subtypes):
        """Instantiate a complex type."""

        if isinstance(type_or_name, str):
            type_or_name = self._canonicalize_type(type_or_name)

        if not self._is_known_type_factory(base):
            raise ArgumentError("unknown complex base type specified",
                                passed_type=type_or_name,
                                base_type=base)

        base_type = self._get_known_type_factory(base)

        # Make sure all of the subtypes are valid
        for sub_type in subtypes:
            try:
                self.get_proxy_for_type(sub_type)
            except KeyValueException as exc:
                raise ArgumentError(
                    "could not instantiate subtype for complex type",
                    passed_type=type_or_name,
                    sub_type=sub_type,
                    error=exc)

        typeobj = base_type.Build(*subtypes, type_system=self)
        self.inject_type(type_or_name, typeobj)
Exemple #3
0
    def split_type(self, type_or_name):
        """
        Given a potentially complex type, split it into its base type and specializers
        """
        if isinstance(type_or_name, str):
            name = self._canonicalize_type(type_or_name)
            if '(' not in name:
                return name, False, []

            base, sub = name.split('(')
            if len(sub) == 0 or sub[-1] != ')':
                raise ArgumentError(
                    "syntax error in complex type, no matching ) found",
                    passed_type=type_or_name,
                    basetype=base,
                    subtype_string=sub)

            sub = sub[:-1]

            subs = sub.split(',')
            return base, True, subs
        elif utils.is_class_from_typing(type_or_name):
            base = getattr(typing, utils.get_typing_type_name(type_or_name))
            subs = utils.get_typing_type_args(type_or_name)
            return base, bool(subs), subs
        else:
            raise ArgumentError('Cannot split the given type.',
                                type_or_name=type_or_name)
def _process_binary_trigger(trigger_value, condition):
    """Create an InputTrigger object."""

    ops = {0: ">", 1: "<", 2: ">=", 3: "<=", 4: "==", 5: 'always'}

    sources = {0: 'value', 1: 'count'}

    encoded_source = condition & 0b1
    encoded_op = condition >> 1

    oper = ops.get(encoded_op, None)
    source = sources.get(encoded_source, None)

    if oper is None:
        raise ArgumentError("Unknown operation in binary trigger",
                            condition=condition,
                            operation=encoded_op,
                            known_ops=ops)
    if source is None:
        raise ArgumentError("Unknown value source in binary trigger",
                            source=source,
                            known_sources=sources)

    if oper == 'always':
        return TrueTrigger()

    return InputTrigger(source, oper, trigger_value)
Exemple #5
0
    def _parse_port(self, port):
        if port is None or len(port) == 0:
            return

        if '@' in port:
            raise ArgumentError("Configuration files are not yet supported as part of a port argument", port=port)

        pairs = port.split(';')
        for pair in pairs:
            name, _, value = pair.partition('=')
            if len(name) == 0 or len(value) == 0:
                continue

            name = name.strip()
            value = value.strip()

            if name == 'device':
                device_name = value
                if device_name in DEVICE_ALIASES:
                    device_name = DEVICE_ALIASES[device_name]
                if device_name in KNOWN_DEVICES:
                    self._default_device_info = KNOWN_DEVICES.get(device_name)
                else:
                    raise ArgumentError("Unknown device name or alias, please select from known_devices", device_name=device_name, known_devices=[x for x in viewkeys(DEVICE_ALIASES)])
            elif name == 'serial':
                self._jlink_serial = value
            elif name == 'mux':
                mux = value
                if mux in KNOWN_MULTIPLEX_FUNCS:
                    self._mux_func = KNOWN_MULTIPLEX_FUNCS[mux]
                else:
                    raise ArgumentError("Unknown multiplexer, please select from known_multiplex_funcs", mux=mux, known_multiplex_funcs=[x for x in viewkeys(KNOWN_MULTIPLEX_FUNCS)])
Exemple #6
0
    def inject_type(self, type_or_name, typeobj):
        """
        Given a module-like object that defines a type, add it to our type system so that
        it can be used with the iotile tool and with other annotated API functions.

        type_or_name could be a string name or a type from typing module
        """
        # if type_or_name is a type from typing module
        if not isinstance(type_or_name, str):
            if type_or_name in self._complex_type_proxies:
                raise ArgumentError(
                    "attempting to inject a type that is already defined",
                    type=type_or_name)
            self._complex_type_proxies[type_or_name] = typeobj
            return

        name = self._canonicalize_type(type_or_name)
        _, is_complex, _ = self.split_type(name)

        if self.is_known_type(name):
            raise ArgumentError(
                "attempting to inject a type that is already defined",
                type=name)

        if (not is_complex) and self._is_factory(typeobj):
            if name in self.type_factories:
                raise ArgumentError(
                    "attempted to inject a complex type factory that is already defined",
                    type=name)
            self.type_factories[name] = typeobj

            mapped_complex_type = getattr(typeobj, 'MAPPED_COMPLEX_TYPE', None)
            if mapped_complex_type:
                self._mapped_complex_types[mapped_complex_type] = typeobj

        elif inspect.isclass(typeobj):
            self.known_types[name] = typeobj
        else:
            self._validate_type(typeobj)

            actual_type = getattr(typeobj, 'MAPPED_BUILTIN_TYPE', None)
            if actual_type is not None:
                self._mapped_builtin_types[actual_type] = typeobj

            for alias in getattr(typeobj, 'MAPPED_TYPE_NAMES', []):
                self.known_types[alias] = typeobj

            self.known_types[name] = typeobj

        if not hasattr(typeobj, "default_formatter"):
            raise ArgumentError(
                "type is invalid, does not have default_formatter function",
                type=typeobj,
                methods=dir(typeobj))
    def set_min_and_max(self, min_value, max_value):
        #min_value and max_value should be positive integers
        if min_value < 0 or max_value < 0:
            raise ArgumentError("Both values should be positive")

        #max_value should be greater than min_value
        if min_value > max_value:
            raise ArgumentError("max_value should be greater than min_value")

        args = struct.pack("<LL", min_value, max_value)
        self.rpc(0x90, 0x00, args)
Exemple #8
0
    def _validate_encryption_settings(self):
        if self.encryption_type == 0:
            return

        if self.encryption_type < 0 or self.encryption_type > self._MAX_ENCRYPTION_TYPE:
            raise ArgumentError("Out of range encryption_type for v2 advertisement: %s" % self.encryption_type)

        if self.encryption_type == self.NULL_KEY and self.reboot_key != self._NULL_KEY:
            raise ArgumentError("Invalid (non-null) encryption key specified for null encryption mode")

        if not isinstance(self.reboot_key, bytes) and len(self.reboot_key) != 16:
            raise ArgumentError("Invalid reboot key specified, expected a length 16 bytes object, was: %r"
                                % self.reboot_key)
Exemple #9
0
    def _parse_conn_string(self, conn_string):
        """Parse a connection string passed from 'debug -c' or 'connect_direct'
            Returns True if any settings changed in the debug port, which
            would require a jlink disconnection """
        disconnection_required = False
        """If device not in conn_string, set to default info"""
        if conn_string is None or 'device' not in conn_string:
            if self._default_device_info is not None and self._device_info != self._default_device_info:
                disconnection_required = True
                self._device_info = self._default_device_info

        if conn_string is None or len(conn_string) == 0:
            return disconnection_required

        if '@' in conn_string:
            raise ArgumentError(
                "Configuration files are not yet supported as part of a connection string argument",
                conn_string=conn_string)

        pairs = conn_string.split(';')
        for pair in pairs:
            name, _, value = pair.partition('=')
            if len(name) == 0 or len(value) == 0:
                continue

            name = name.strip()
            value = value.strip()

            if name == 'device':
                if value in DEVICE_ALIASES:
                    device_name = DEVICE_ALIASES[value]
                if device_name in KNOWN_DEVICES:
                    device_info = KNOWN_DEVICES.get(device_name)
                    if self._device_info != device_info:
                        self._device_info = device_info
                        disconnection_required = True
                else:
                    raise ArgumentError(
                        "Unknown device name or alias, please select from known_devices",
                        device_name=value,
                        known_devices=[x for x in viewkeys(DEVICE_ALIASES)])
            elif name == 'channel':
                if self._mux_func is not None:
                    if self._channel != int(value):
                        self._channel = int(value)
                        disconnection_required = True
                else:
                    print(
                        "Warning: multiplexing architecture not selected, channel will not be set"
                    )
        return disconnection_required
Exemple #10
0
    def add_config(self, config_id, config_data):
        """Add a configuration variable to the MIB block"""

        if config_id < 0 or config_id >= 2**16:
            raise ArgumentError(
                "Config ID in mib block is not a non-negative 2-byte number",
                config_data=config_id,
                data=config_data)

        if config_id in self.configs:
            raise ArgumentError("Attempted to add the same command ID twice.",
                                config_data=config_id,
                                old_data=self.configs[config_id],
                                new_data=config_data)

        self.configs[config_id] = config_data
Exemple #11
0
    def __init__(self, parent):
        if not isinstance(parent, TileBusProxyObject):
            raise ArgumentError(
                "Attempting to initialize a TileBusProxyPlugin with an invalid parent object",
                parent=parent)

        self._proxy = parent
Exemple #12
0
    async def debug(self, conn_id, name, cmd_args):
        """Asynchronously complete a named debug command.

        The command name and arguments are passed to the underlying device adapter
        and interpreted there.  If the command is long running, progress_callback
        may be used to provide status updates.

        Args:
            conn_id (int): A unique identifer that will refer to this connection
            name (string): the name of the debug command we want to invoke
            cmd_args (dict): any arguments that we want to send with this command.
        """
        known_commands = {
            'read_memory': self._jlink_async.debug_read_memory,
            'write_memory': self._jlink_async.debug_write_memory,
            'program_flash': self._jlink_async.program_flash
        }

        self._ensure_connection(conn_id, True)

        func = known_commands.get(name)
        if name is None:
            raise ArgumentError("Unsupported command: %s" % name)

        return await func(self._device_info, self._control_info, cmd_args)
Exemple #13
0
    async def _try_connect(self, connection_string):
        """If the connecton string settings are different, try and connect to an attached device"""
        if self._parse_conn_string(connection_string):
            if self.connected is True:
                info = {"reason": "Reconnection", "expected": True}
                self.notify_event(connection_string, 'disconnection', info)
                self.connected = False
                await self.stop()

            if self._mux_func is not None:
                self._mux_func(self._channel)

            if self._device_info is None:
                raise ArgumentError(
                    "Missing device name or alias, specify using device=name in port string "
                    "or -c device=name in connect_direct or debug command",
                    known_devices=[x for x in DEVICE_ALIASES.keys()])

            try:
                await self._jlink_async.connect_jlink(
                    self._jlink_serial, self._device_info.jlink_name)
                self.connected = True
            except pylink.errors.JLinkException as exc:
                if exc.code == exc.VCC_FAILURE:
                    raise HardwareError(
                        "No target power detected",
                        code=exc.code,
                        suggestion="Check jlink connection and power wiring")

                raise
            except:
                raise
Exemple #14
0
    def __init__(self, time, stream, value):
        self.time = time
        self.stream = stream
        self.value = value

        if self.stream.stream_type != self.stream.InputType:
            raise ArgumentError("Invalid stimulus applied to non-input stream", stream=self.stream)
Exemple #15
0
    def validate_for_v2(self):
        """Verify the advertisement options are allowed for a v2 advertisement packet."""

        if self.battery_level < 0 or self.battery_level > self._MAX_BATTERY_LEVEL_V2:
            raise ArgumentError("Out of range battery_level for v2 advertisement: %s" % self.battery_level)

        self._validate_encryption_settings()
Exemple #16
0
    def format_value(self, value, type, format=None, **kwargs):
        """
        Convert value to type and format it as a string

        type must be a known type in the type system and format,
        if given, must specify a valid formatting option for the
        specified type.
        """

        typed_val = self.convert_to_type(value, type, **kwargs)
        typeobj = self.get_type(type)

        #Allow types to specify default formatting functions as 'default_formatter'
        #otherwise if not format is specified, just convert the value to a string
        if format is None:
            if hasattr(typeobj, 'default_formatter'):
                format_func = getattr(typeobj, 'default_formatter')
                return format_func(typed_val, **kwargs)

            return str(typed_val)

        formatter = "format_%s" % str(format)
        if not hasattr(typeobj, formatter):
            raise ArgumentError("Unknown format for type",
                                type=type,
                                format=format,
                                formatter_function=formatter)

        format_func = getattr(typeobj, formatter)
        return format_func(typed_val, **kwargs)
Exemple #17
0
    def _try_connect(self, connection_string):
        """If the connecton string settings are different, try and connect to an attached device"""
        if self._parse_conn_string(connection_string):
            self._trigger_callback('on_disconnect', self.id, self._connection_id)

            self.stop_sync()

            if self._mux_func is not None:
                self._mux_func(self._channel)

            if self._device_info is None:
                raise ArgumentError("Missing device name or alias, specify using device=name in port string or -c device=name in connect_direct or debug command", known_devices=[x for x in viewkeys(DEVICE_ALIASES)])

            try:
                self.jlink = pylink.JLink()
                self.jlink.open(serial_no=self._jlink_serial)
                self.jlink.set_tif(pylink.enums.JLinkInterfaces.SWD)
                self.jlink.connect(self._device_info.jlink_name)
                self.jlink.set_little_endian()
            except pylink.errors.JLinkException as exc:
                if exc.code == exc.VCC_FAILURE:
                    raise HardwareError("No target power detected", code=exc.code, suggestion="Check jlink connection and power wiring")

                raise
            except:
                raise

            self._control_thread = JLinkControlThread(self.jlink)
            self._control_thread.start()

            self.set_config('probe_required', True)
            self.set_config('probe_supported', True)
Exemple #18
0
    def FromString(cls, desc):
        """Create a new stimulus from a description string.

        The string must have the format:

        [time: ][system ]input X = Y
        where X and Y are integers.  The time, if given must
        be a time_interval, which is an integer followed by a
        time unit such as second(s), minute(s), etc.

        Args:
            desc (str): A string description of the stimulus.

        Returns:
            SimulationStimulus: The parsed stimulus object.
        """
        if language.stream is None:
            language.get_language()

        parse_exp = Optional(
            time_interval('time') - Literal(':').suppress()) - language.stream(
                'stream') - Literal('=').suppress() - number('value')

        try:
            data = parse_exp.parseString(desc)
            time = 0
            if 'time' in data:
                time = data['time'][0]

            return SimulationStimulus(time, data['stream'][0], data['value'])
        except (ParseException, ParseSyntaxException):
            raise ArgumentError("Could not parse stimulus descriptor",
                                descriptor=desc)
Exemple #19
0
 def _get_known_type_factory(self, type_or_name):
     if type_or_name in self.type_factories:
         return self.type_factories[type_or_name]
     if type_or_name in self._mapped_complex_types:
         return self._mapped_complex_types[type_or_name]
     raise ArgumentError('Type factory not found.',
                         type_or_name=type_or_name)
Exemple #20
0
    def FromFile(cls, in_path):
        """Load a previously saved ascii representation of this simulation trace.

        Args:
            in_path (str): The path of the input file that we should load.

        Returns:
            SimulationTrace: The loaded trace object.
        """

        with open(in_path, "rb") as infile:
            in_data = json.load(infile)

        if not ('trace', 'selectors') in in_data:
            raise ArgumentError("Invalid trace file format",
                                keys=in_data.keys(),
                                expected=('trace', 'selectors'))

        selectors = [
            DataStreamSelector.FromString(x) for x in in_data['selectors']
        ]
        readings = [
            IOTileReading(x['time'],
                          DataStream.FromString(x['stream']).encode(),
                          x['value'],
                          reading_id=x['reading_id']) for x in in_data['trace']
        ]

        return SimulationTrace(readings, selectors=selectors)
Exemple #21
0
    def _extract_arg_value(cls, arg_name, arg_type, remaining):
        """Try to find the value for a keyword argument."""

        next_arg = None
        should_consume = False
        if len(remaining) > 0:
            next_arg = remaining[0]
            should_consume = True

            if next_arg == '--':
                next_arg = None

        # Generally we just return the next argument, however if the type
        # is bool we allow not specifying anything to mean true if there
        # is no ambiguity
        if arg_type == "bool":
            if next_arg is None or next_arg.startswith('-'):
                next_arg = True
                should_consume = False
        else:
            if next_arg is None:
                raise ArgumentError(
                    "Could not find value for keyword argument",
                    argument=arg_name)

        if should_consume:
            remaining.pop(0)

        return next_arg
Exemple #22
0
def render_template_inplace(template_path, info, dry_run=False, extra_filters=None, resolver=None):
    """Render a template file in place.

    This function expects template path to be a path to a file
    that ends in .tpl.  It will be rendered to a file in the
    same directory with the .tpl suffix removed.

    Args:
        template_path (str): The path to the template file
            that we want to render in place.
        info (dict): A dictionary of variables passed into the template to
            perform substitutions.
        dry_run (bool): Whether to actually render the output file or just return
            the file path that would be generated.
        extra_filters (dict of str -> callable): An optional group of filters that
            will be made available to the template.  The dict key will be the
            name at which callable is made available.
        resolver (ProductResolver): The specific ProductResolver class to use in the
            find_product filter.

    Returns:
        str: The path to the output file generated.
    """

    filters = {}
    if resolver is not None:
        filters['find_product'] = _create_resolver_filter(resolver)

    if extra_filters is not None:
        filters.update(extra_filters)

    basedir = os.path.dirname(template_path)
    template_name = os.path.basename(template_path)

    if not template_name.endswith('.tpl'):
        raise ArgumentError("You must specify a filename that ends in .tpl", filepath=template_path)

    out_path = os.path.join(basedir, template_name[:-4])

    if basedir == '':
        basedir = '.'

    env = Environment(loader=FileSystemLoader(basedir),
                      trim_blocks=True, lstrip_blocks=True)

    # Load any filters the user wants us to use
    for name, func in viewitems(filters):
        env.filters[name] = func

    template = env.get_template(template_name)
    result = template.render(info)

    if not dry_run:
        with open(out_path, 'wb') as outfile:
            outfile.write(result.encode('utf-8'))

    return out_path
Exemple #23
0
def encrypt_v2_packet(message: bytes, key: bytes, nonce: bytes):
    """Lowlevel internal routine to perform AES-CCM packet encryption.

    This function expects to be called with a 31 byte v2 advertisement packet
    and it returns the correct encrypted version of that same packet according
    to the given key and nonce.  Note that the key and nonce are themselves
    derived partially from the contents of the packet.  For clarify, this
    method expects them to be provided as is and does not attempt to calculate
    them since the source keying material could come from many different
    sources.
    """

    if AES is None:
        raise NotFoundError(
            "Missing pycryptodome dependency, cannot encrypt data") from err

    if len(key) != 16:
        raise ArgumentError(
            "Invalid encryption key that should be 128 bits long, was %d bytes"
            % len(key))

    if len(nonce) != 16:
        raise ArgumentError(
            "Invalid AES CCM nonce that should be 16 bytes long, was %d bytes"
            % len(nonce))

    if len(message) != 31:
        raise ArgumentError(
            "Invalid v2 packet data with incorrect length, expected=31, found=%d"
            % len(message))

    header = message[:7]
    aad = message[7:21]
    body = message[21:27]
    #_original_tag = message[27:31]

    cipher = AES.new(key, AES.MODE_CCM, nonce, mac_len=4)

    # Ignoring mypy errors since it misidentifies what options are supported by this cipher mode
    cipher.update(aad)  # type: ignore
    encrypted, tag = cipher.encrypt_and_digest(body)  # type: ignore

    encrypted_packet = header + aad + encrypted + tag
    return encrypted_packet
Exemple #24
0
def _select_ftdi_channel(channel):
    """Select multiplexer channel. Currently uses a FTDI chip via pylibftdi"""
    if channel < 0 or channel > 8:
        raise ArgumentError(
            "FTDI-selected multiplexer only has channels 0-7 valid, make sure you specify channel with -c channel=number",
            channel=channel)
    from pylibftdi import BitBangDevice
    bb = BitBangDevice(auto_detach=False)
    bb.direction = 0b111
    bb.port = channel
Exemple #25
0
    def anchor_stream(self, stream_id, converter="rtc"):
        """Mark a stream as containing anchor points."""

        if isinstance(converter, str):
            converter = self._known_converters.get(converter)

            if converter is None:
                raise ArgumentError("Unknown anchor converter string: %s" % converter, known_converters=list(self._known_converters))

        self._anchor_streams[stream_id] = converter
Exemple #26
0
    def add_command(self, cmd_id, handler):
        """Add a command to the TBBlock.

        The cmd_id must be a non-negative 2 byte number.
        handler should be the command handler
        """

        if cmd_id < 0 or cmd_id >= 2**16:
            raise ArgumentError(
                "Command ID in mib block is not a non-negative 2-byte number",
                cmd_id=cmd_id,
                handler=handler)

        if cmd_id in self.commands:
            raise ArgumentError("Attempted to add the same command ID twice.",
                                cmd_id=cmd_id,
                                existing_handler=self.commands[cmd_id],
                                new_handler=handler)

        self.commands[cmd_id] = handler
Exemple #27
0
    def get_proxy_for_type(self, type_or_name):
        """Return the type object corresponding to a given type_or_name.

        type_or_name could be:
        - a simple builtin type like str, int, etc
        - a string name of a known type
        - a string name of an unknown complex type where base type is a known type factory
        - a complex type class from typing module: Dict[T, T] or List[T]
        - a string name of an unknown type (maybe a complex where base type is unknown type factory)
        If type_or_name does not fit these criteria then None would be returned.

        If type_or_name is a string type name and it is not found in known types, this triggers the loading of
        external types until a matching type is found or until there
        are no more external type sources.
        """
        if not isinstance(type_or_name, str) and not self.is_known_type(
                type_or_name) and not utils.is_class_from_typing(type_or_name):
            return None

        if isinstance(type_or_name, str):
            type_or_name = self._canonicalize_type(type_or_name)

        # If type_or_name is a:
        # - a simple builtin type like str, int, etc
        # - a string name of a known type
        if self.is_known_type(type_or_name):
            return self._get_proxy_for_known_type(type_or_name)

        # here type_or_name could be a string name or a complex type class from typing module
        base_type, is_complex, subtypes = self.split_type(type_or_name)

        # If type_or_name is a:
        # - a string name of an unknown complex type where base type is a known type factory
        # - a complex type class from typing module: Dict[T, T] or List[T]
        if is_complex and self._is_known_type_factory(base_type):
            self.instantiate_type(type_or_name, base_type, subtypes)
            return self.get_proxy_for_type(type_or_name)

        # If type_or_name is a:
        # - a string name of an unknown type (maybe a complex where base type is unknown type factory)

        # If we're here, this is a string type name that we don't know anything about, so go find it.
        self._load_registered_type_sources(type_or_name)

        # If we've loaded everything and we still can't find it then there's a configuration error somewhere
        if not (self.is_known_type(type_or_name) or
                (is_complex and base_type in self.type_factories)):
            raise ArgumentError(
                "get_proxy_for_type called on unknown type",
                type=type_or_name,
                failed_external_sources=[x[0] for x in self.failed_sources])

        return self.get_proxy_for_type(type_or_name)
Exemple #28
0
    def add_report(self, report, ignore_errors=False):
        """Add all anchors from a report."""

        if not isinstance(report, SignedListReport):
            if ignore_errors:
                return

            raise ArgumentError("You can only add SignedListReports to a UTCAssigner", report=report)

        for reading in report.visible_readings:
            self.add_reading(reading)

        self.add_point(report.report_id, report.sent_timestamp, report.received_time)
Exemple #29
0
 def _get_proxy_for_known_type(self, type_or_name):
     """
     Returns:
         type proxy object or None
     """
     if type_or_name in self.known_types:
         return self.known_types[type_or_name]
     if type_or_name in self._mapped_builtin_types:
         return self._mapped_builtin_types[type_or_name]
     if type_or_name in self._complex_type_proxies:
         return self._complex_type_proxies[type_or_name]
     raise ArgumentError('Proxy object not found.',
                         type_or_name=type_or_name)
Exemple #30
0
def timestamp_to_integer(timestamp) -> int:
    """Safely convert a datetime or integer to an iotile compatible encoded time.

    The encoding is that an integer number of seconds is stored in a uint32
    and is interpreted in one of two ways depending on whether the high bit is set.

    - If the high bit (1 << 31) is set then the value should be interpreted as
      the number of seconds since 1/1/2000 in UTC.  So it is a utc timestamp
      with a different, more recent epoch.

    - Otherwise, the value be interpreted as the number of
      seconds since the device rebooted.  This uptime cannot be directly
      converted to UTC time unless the observer has access to a valid utc
      clock and can associate a specific uptime value to UTC time to establish
      an offset.
    """

    if isinstance(timestamp, int):
        return timestamp

    if not isinstance(timestamp, datetime.datetime):
        raise ArgumentError(
            "Unknown timelike object that is not an int or datetime: %r" %
            timestamp)

    if timestamp.tzinfo is None:
        delta = (timestamp - _Y2K_NAIVE).total_seconds()
    else:
        delta = (timestamp - _Y2K_AWARE).total_seconds()

    # We only have 31 bits of range to pack this timestamp since we set the high bit to indicate UTC
    if delta < 0:
        raise ArgumentError(
            "Cannot represent times before 1/1/2000, received: %s" % timestamp)
    if delta > _MAX_TIMESTAMP:
        raise ArgumentError("Timestamp too far in the future, received: %s" %
                            timestamp)

    return (1 << 31) | int(delta)