def _read_parameter(self, parameter_key): response = self._send_message( _READ_PARAMETER_REQUEST + parameter_key, 4) if response[0:2] != b'\x03\x06': raise lifescan.MalformedCommand( 'invalid response, expected 03 06, received %02x %02x' % ( response[0], response[1])) return response[2:]
def get_datetime(self): response = self._send_message(_READ_RTC_REQUEST, 3) if response[0:2] != b'\x04\06': raise lifescan.MalformedCommand( 'invalid response, expected 04 06, received %02x %02x' % ( response[0], response[1])) (timestamp,) = _STRUCT_TIMESTAMP.unpack(response[2:]) return _convert_timestamp(timestamp)
def read_from(self, serial): self.cmd.extend(serial.read(3)) if self.cmd[_IDX_STX] != _STX: raise lifescan.MalformedCommand( 'at position %s expected %02x, received %02x' % ( _IDX_STX, _STX, self.cmd[_IDX_STX])) # the length includes prelude and appendix, which are six bytes total. if self.length > 6: self.cmd.extend(serial.read(self.length - 6)) self.cmd.extend(serial.read(3)) if self.cmd[_IDX_ETX] != _ETX: raise lifescan.MalformedCommand( 'at position %s expected %02x, received %02x' % ( _IDX_ETX, _ETX, self.cmd[_IDX_ETX]))
def _get_reading_count(self): response = self._send_message(_READ_RECORD_COUNT_REQUEST, 3) if response[0:2] != b'\x04\06': raise lifescan.MalformedCommand( 'invalid response, expected 04 06, received %02x %02x' % ( response[0], response[1])) (record_count,) = _STRUCT_RECORDID.unpack(response[2:]) return record_count
def _query_string(self, query_key): response = self._send_message(_QUERY_REQUEST + query_key, 3) if response[0:2] != b'\x04\06': raise lifescan.MalformedCommand( 'invalid response, expected 04 06, received %02x %02x' % ( response[0], response[1])) # Strings are encoded in wide characters (LE), but they should # only contain ASCII characters. Note that the string is # null-terminated, so the last character should be dropped. return response[2:].decode('utf-16-le')[:-1]
def _send_request(self, request_format, request_obj, response_format): try: request = request_format.build(request_obj) self._send_packet(request) response_pkt = self._read_packet() return response_format.parse(response_pkt.message) except construct.ConstructError as e: raise lifescan.MalformedCommand(str(e))
def get_glucose_unit(self): result = self._send_command(_READ_GLUCOSE_UNIT) response = self._read_response() if response.data[2] == 0: return common.UNIT_MGDL elif response.data[2] == 1: return common.UNIT_MMOLL else: raise lifescan.MalformedCommand( 'at position PM1 invalid value %02x for unit' % response.data[2])
def _extract_message(register): """Parse the message preamble and verify checksums.""" stx, length = _STRUCT_PREAMBLE.unpack_from(register) if stx != _STX: raise lifescan.MalformedCommand('invalid STX byte: %02x' % stx) if length > _REGISTER_SIZE: raise lifescan.MalformedCommand('invalid length: %d > REGISTER_SIZE' % length) # 2 is the length of the checksum, so it should be ignored. calculated_checksum = lifescan.crc_ccitt(register[:(length - 2)]) coda_offset = length - _STRUCT_CODA.size etx, encoded_checksum = _STRUCT_CODA.unpack_from(register[coda_offset:]) if etx != _ETX: raise lifescan.MalformedCommand('invalid ETX byte: %02x' % etx) if encoded_checksum != calculated_checksum: raise exceptions.InvalidChecksum(encoded_checksum, calculated_checksum) response = register[_STRUCT_PREAMBLE.size:coda_offset] return response
def _read_packet(self) -> construct.Container: raw_pkt = self.buffered_reader_.parse_stream(self.serial_).data logging.debug("received packet: %r", raw_pkt) # discard the checksum and copy pkt = raw_pkt.value if not pkt.link_control.disconnect and ( pkt.link_control.sequence_number != self.expect_receive_): raise lifescan.MalformedCommand( f"at position 2[0b] expected {self.expect_receive_:02x}, received {pkt.link_connect.sequence_count:02x}" ) return pkt
def _read_packet(self): raw_pkt = self.buffered_reader_.parse_stream(self.serial_).data logging.debug('received packet: %r', raw_pkt) # discard the checksum and copy pkt = raw_pkt.value if not pkt.link_control.disconnect and ( pkt.link_control.sequence_number != self.expect_receive_): raise lifescan.MalformedCommand( 'at position 2[0b] expected %02x, received %02x' % (self.expect_receive_, pkt.link_connect.sequence_count)) return pkt
def _send_request( self, request_format: construct.struct, request_obj: Optional[Dict[str, Any]], response_format: construct.Struct, ) -> construct.Container: try: request = request_format.build(request_obj) self._send_packet(request) response_pkt = self._read_packet() return response_format.parse(response_pkt.message) except construct.ConstructError as e: raise lifescan.MalformedCommand(str(e))
def _get_reading(self, record_number): request = (_READ_RECORD_REQUEST_PREFIX + _STRUCT_RECORDID.pack(record_number) + _READ_RECORD_REQUEST_SUFFIX) response = self._send_message(request, 3) if response[0:2] != b'\x04\06': raise lifescan.MalformedCommand( 'invalid response, expected 04 06, received %02x %02x' % ( response[0], response[1])) (unused_const1, unused_const2, unused_counter, unused_const3, unused_counter2, timestamp, value, unused_flags, unused_const4, unused_const5) = _STRUCT_RECORD.unpack(response) return common.Reading(_convert_timestamp(timestamp), float(value))
def _send_request( self, lba: int, request_format: construct.Struct, request_obj: Optional[Dict[str, Any]], response_format: construct.Struct, ) -> construct.Container: """Send a request to the meter, and read its response. Args: lba: the address of the block register to use, known valid addresses are 3, 4 and 5. request_format: a construct format identifier of the request to send request_obj: the object to format with the provided identifier response_format: a construct format identifier to parse the returned message with. Returns: The Container object parsed from the response received by the meter. Raises: lifescan.MalformedCommand if Construct fails to build the request or parse the response. """ try: request = request_format.build(request_obj) request_raw = _PACKET.build( {"data": { "value": { "message": request } }}) logging.debug("Request sent: %s", binascii.hexlify(request_raw)) self.scsi_.write10(lba, 1, request_raw) response_raw = self.scsi_.read10(lba, 1) logging.debug("Response received: %s", binascii.hexlify(response_raw.datain)) response_pkt = _PACKET.parse(response_raw.datain).data logging.debug("Response packet: %r", response_pkt) response = response_format.parse(response_pkt.value.message) logging.debug("Response parsed: %r", response) return response except construct.ConstructError as e: raise lifescan.MalformedCommand(str(e))
def set_datetime(self, date=datetime.datetime.now()): epoch = datetime.datetime.utcfromtimestamp(_EPOCH_BASE) delta = date - epoch timestamp = int(delta.total_seconds()) timestamp_bytes = _STRUCT_TIMESTAMP.pack(timestamp) response = self._send_message(_WRITE_RTC_REQUEST + timestamp_bytes, 3) if response[0:2] != b'\x04\06': raise lifescan.MalformedCommand( 'invalid response, expected 04 06, received %02x %02x' % ( response[0], response[1])) # The device does not return the new datetime, so confirm by # calling READ RTC again. return self.get_datetime()
def _send_request(self, request_format, request_obj, response_format): try: request = request_format.build(request_obj) self._send_packet(request, acknowledge=False, disconnect=False) self.sent_counter_ = not self.sent_counter_ self._read_ack() response_pkt = self._read_packet() assert not response_pkt.link_control.acknowledge self.expect_receive_ = not self.expect_receive_ self._send_ack() return response_format.parse(response_pkt.message) except construct.ConstructError as e: raise lifescan.MalformedCommand(str(e))
def _read_response(self): response = _Packet() response.read_from(self.serial_) if not response.disconnect and response.sent_counter != self.expect_receive_: raise lifescan.MalformedCommand( 'at position 2[0b] expected %02x, received %02x' % ( self.expect_receive_, response.expect_receive)) if not response.acknowledge: self.expect_receive_ = not self.expect_receive_ response.validate_checksum() if not response.acknowledge: self._send_command('', acknowledge=True) return response
def zero_log(self): response = self._send_message(_MEMORY_ERASE_REQUEST, 3) if response[0:2] != b'\x04\06': raise lifescan.MalformedCommand( 'invalid response, expected 04 06, received %02x %02x' % ( response[0], response[1]))
def connect(self): try: self._send_packet(b'', disconnect=True) self._read_ack() except construct.ConstructError as e: raise lifescan.MalformedCommand(str(e))