def _validate_and_strip_checksum(line): """Verify the simple 16-bit checksum and remove it from the line. Args: line: the line to check the checksum of. Returns: A copy of the line with the checksum stripped out. """ match = _RESPONSE_MATCH.match(line) if not match: raise lifescan.MissingChecksum(line) response, checksum_string = match.groups() try: checksum_given = int(checksum_string, 16) checksum_calculated = _calculate_checksum(bytes(response, 'ascii')) if checksum_given != checksum_calculated: raise exceptions.InvalidChecksum(checksum_given, checksum_calculated) except ValueError: raise exceptions.InvalidChecksum(checksum_given, None) return response
def _read_text_response(self) -> Sequence[bytes]: all_lines: List[bytes] = [] while True: line = self._readline() if not line.endswith(b"\r\n"): raise exceptions.InvalidResponse( f"Corrupted response line: {line!r}") all_lines.append(line) if line == b"]\r\n": break if all_lines[0] != b"[\r\n": raise exceptions.InvalidResponse( f"Unexpected first response line: {all_lines!r}") wire_checksum = int(all_lines[-2][:-2], base=16) calculated_checksum = _crc8_maxim(b"".join(all_lines[:-2])) if wire_checksum != calculated_checksum: raise exceptions.InvalidChecksum(wire_checksum, calculated_checksum) return [line[:-2] for line in all_lines[1:-2]]
def _fetch_device_information(self): data = self._send_command('mem') res = _parse_result(data) self.info_ = res['info'] self.device_results_ = res['reslog'] self.device_checksum_ = res['checksum'] self.device_version_ = self.info_['device_version_'] self.software_revision_ = self.info_['software_revision_'] self.device_serialno_ = self.info_['device_serialno_'] self.device_glucose_unit_ = self.info_['device_glucose_unit_'] self.device_current_date_time_ = self.info_[ 'device_current_date_time_'] self.device_nrresults_ = self.info_['device_nrresults_'] # exclude the last line which is the checksum itself. # every line is ended with a windows end-of-line \r\n # except line 6 ends with a linux end-of-line \n n = self.device_nrresults_ if n > 0: wnl = (ord('\r') + ord('\n')) * (_INFO_SIZE + n ) # windows end of line lnl = ord('\n') # linux end of line ce = self.device_checksum_ # expected cg = (sum(ord(c) for c in ''.join(data[:-1])) + wnl + lnl) % ( 2**16) # gotten if ce != cg: raise exceptions.InvalidChecksum(ce, cg)
def _verify_checksum(message, expected_checksum_hex): """Calculate the simple checksum of the message and compare with expected. Args: message: (str) message to calculate the checksum of. expected_checksum_hex: (str) hexadecimal string representing the checksum expected to match the message. Raises: InvalidChecksum: if the message checksum calculated does not match the one received. """ expected_checksum = int(expected_checksum_hex, 16) calculated_checksum = sum(ord(c) for c in message) if expected_checksum != calculated_checksum: raise exceptions.InvalidChecksum(expected_checksum, calculated_checksum)
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 _verify_checksum(message: AnyStr, expected_checksum_hex: AnyStr) -> None: """Calculate the simple checksum of the message and compare with expected. Args: message: (str) message to calculate the checksum of. expected_checksum_hex: hexadecimal string representing the checksum expected to match the message. Raises: InvalidChecksum: if the message checksum calculated does not match the one received. """ expected_checksum = int(expected_checksum_hex, 16) if isinstance(message, bytes): all_bytes = (c for c in message) else: all_bytes = (ord(c) for c in message) calculated_checksum = sum(all_bytes) if expected_checksum != calculated_checksum: raise exceptions.InvalidChecksum(expected_checksum, calculated_checksum)
def get_readings(self) -> Generator[common.AnyReading, None, None]: """Iterates over the reading values stored in the glucometer. Args: unit: The glucose unit to use for the output. Yields: A tuple (date, value) of the readings in the glucometer. The value is a floating point in the unit specified; if no unit is specified, the default unit in the glucometer will be used. Raises: exceptions.InvalidResponse: if the response does not match what ' expected. """ data = self._send_command("xmem") # The first line is empty, the second is the serial number, the third # the version, the fourth the current time, and the fifth the record # count.. The last line has a checksum and the end. count = int(data[4]) if count != (len(data) - 6): raise exceptions.InvalidResponse("\n".join(data)) # Extract the checksum from the last line. checksum_match = _CHECKSUM_RE.match(data[-1]) if not checksum_match: raise exceptions.InvalidResponse("\n".join(data)) expected_checksum = int(checksum_match.group("checksum"), 16) # exclude the last line in the checksum calculation, as that's the # checksum itself. The final \r\n is added separately. calculated_checksum = sum(ord(c) for c in "\r\n".join(data[:-1])) + 0xD + 0xA if expected_checksum != calculated_checksum: raise exceptions.InvalidChecksum(expected_checksum, calculated_checksum) for line in data[5:-1]: match = _READING_RE.match(line) if not match: raise exceptions.InvalidResponse(line) if match.group("type") != "G": logging.warning("Non-glucose readings are not supported, ignoring.") continue if match.group("reading") == "HI ": value = float("inf") else: value = float(match.group("reading")) day = int(match.group("day")) month = _MONTH_MATCHES[match.group("month")] year = int(match.group("year")) hour, minute = map(int, match.group("time").split(":")) timestamp = datetime.datetime(year, month, day, hour, minute) # The reading, if present, is always in mg/dL even if the glucometer # is set to mmol/L. yield common.GlucoseReading(timestamp, value)
def validate_checksum(self): expected_checksum = self.checksum received_checksum = self._STRUCT.unpack(self.cmd[_IDX_CHECKSUM:])[0] if received_checksum != expected_checksum: raise exceptions.InvalidChecksum(expected_checksum, received_checksum)
def get_readings(self): """Iterates over the reading values stored in the glucometer. Args: unit: The glucose unit to use for the output. Yields: A tuple (date, value) of the readings in the glucometer. The value is a floating point in the unit specified; if no unit is specified, the default unit in the glucometer will be used. Raises: exceptions.InvalidResponse: if the response does not match what expected. """ data = self._send_command('xmem') # The first line is empty, the second is the serial number, the third the # version, the fourth the current time, and the fifth the record count.. The # last line has a checksum and the end. count = int(data[4]) if count != (len(data) - 6): raise exceptions.InvalidResponse('\n'.join(data)) # Extract the checksum from the last line. checksum_match = _CHECKSUM_RE.match(data[-1]) if not checksum_match: raise exceptions.InvalidResponse('\n'.join(data)) expected_checksum = int(checksum_match.group('checksum'), 16) # exclude the last line in the checksum calculation, as that's the checksum # itself. The final \r\n is added separately. calculated_checksum = sum(ord(c) for c in '\r\n'.join(data[:-1])) + 0xd + 0xa if expected_checksum != calculated_checksum: raise exceptions.InvalidChecksum(expected_checksum, calculated_checksum) for line in data[5:-1]: match = _READING_RE.match(line) if not match: raise exceptions.InvalidResponse(line) if match.group('type') != 'G': print('Non-glucose readings are not supported, ignoring.', file=sys.stderr) continue if match.group('reading') == 'HI ': value = float("inf") else: value = float(match.group('reading')) day = int(match.group('day')) month = _MONTH_MATCHES[match.group('month')] year = int(match.group('year')) hour, minute = map(int, match.group('time').split(':')) timestamp = datetime.datetime(year, month, day, hour, minute) # The reading, if present, is always in mg/dL even if the glucometer is # set to mmol/L. yield common.Reading(timestamp, value)