def find_device(device, debug): if device is None: devices = glob.glob('/dev/sg[0-9]*') else: devices = [device] for device in devices: try: if debug: print("Trying", device, ' ', end='') sd = SCSIDevice(device) s = SCSI(sd) i = s.inquiry().result vendor = i['t10_vendor_identification'].decode( "latin1", "backslashreplace").strip() product = i['product_identification'].decode( "latin1", "backslashreplace").strip() if debug: print(vendor, product) if vendor == 'YULIN' and product == 'PROGRAMMER': print("Device", device) return sd except Exception as e: print("Exception", traceback.format_exc()) pass raise BaseException("Cannot find JQ6500 (YULIN PROGRAMMER) device")
def main(): i = 1 page_code = 0 evpd = 0 while i < len(sys.argv): if sys.argv[i] == '--help': return usage() if sys.argv[i] == '-p': del sys.argv[i] page_code = int(sys.argv[i], 16) evpd = 1 del sys.argv[i] continue i += 1 if len(sys.argv) < 2: return usage() device = sys.argv[1] sd = SCSIDevice(device) s = SCSI(sd) i = s.testunitready() if not evpd: inquiry_standard(s) return if page_code == INQUIRY.VPD.SUPPORTED_VPD_PAGES: inquiry_supported_vpd_pages(s) return if page_code == INQUIRY.VPD.BLOCK_LIMITS: inquiry_block_limits(s) return if page_code == INQUIRY.VPD.BLOCK_DEVICE_CHARACTERISTICS: inquiry_block_dev_char(s) return if page_code == INQUIRY.VPD.LOGICAL_BLOCK_PROVISIONING: inquiry_logical_block_prov(s) return if page_code == INQUIRY.VPD.UNIT_SERIAL_NUMBER: inquiry_unit_serial_number(s) return if page_code == INQUIRY.VPD.DEVICE_IDENTIFICATION: inquiry_device_identification(s) return print 'No pretty print for this page, page_code=0x%02x' % page_code print '=============================================\n' i = s.inquiry(evpd=1, page_code=page_code).result for k, v in i.iteritems(): print '%s - %s' % (k, v)
def main(): device = '' for i in range(len(sys.argv)): if sys.argv[i] == '-f': del sys.argv[i] device = sys.argv[i] del sys.argv[i] break if not device: usage() exit(1) scsi = SCSI(SCSIDevice(device)) i = scsi.inquiry().result if i['peripheral_device_type'] != INQUIRY.DEVICE_TYPE.MEDIA_CHANGER_DEVICE: print '%s is not a MediaChanger device' % device exit(1) eaa = scsi.modesense6(page_code=MODESENSE6.PAGE_CODE.ELEMENT_ADDRESS_ASSIGNMENT).result['mode_pages'][0] # get the data transfer elements dte = scsi.readelementstatus( start=eaa['first_data_transfer_element_address'], num=eaa['num_data_transfer_elements'], element_type=READELEMENTSTATUS.ELEMENT_TYPE.DATA_TRANSFER, voltag=1, curdata=1, dvcid=1, alloclen=16384).result['element_status_pages'][0]['element_descriptors'] # get all the storage elements se = scsi.readelementstatus( start=eaa['first_storage_element_address'], num=eaa['num_storage_elements'], element_type=READELEMENTSTATUS.ELEMENT_TYPE.STORAGE, voltag=1, curdata=1, dvcid=1, alloclen=16384).result['element_status_pages'][0]['element_descriptors'] # get all the medium transport elements mte = scsi.readelementstatus( start=eaa['first_medium_transport_element_address'], num=eaa['num_medium_transport_elements'], element_type=READELEMENTSTATUS.ELEMENT_TYPE.MEDIUM_TRANSPORT, voltag=1, curdata=1, dvcid=1, alloclen=16384).result['element_status_pages'][0]['element_descriptors'] if sys.argv[1] == 'status': return status(scsi, dte, se) if sys.argv[1] == 'load': return load(scsi, mte, dte, se, int(sys.argv[2]), int(sys.argv[3])) if sys.argv[1] == 'unload': return unload(scsi, mte, dte, se, int(sys.argv[2]), int(sys.argv[3])) usage() exit(1)
def main(): device = '' for i in range(len(sys.argv)): if sys.argv[i] == '-f': del sys.argv[i] device = sys.argv[i] del sys.argv[i] break if not device: usage() exit(1) scsi = SCSI(init_device(device)) i = scsi.inquiry().result if i['peripheral_device_type'] != INQUIRY.DEVICE_TYPE.MEDIA_CHANGER_DEVICE: print('%s is not a MediaChanger device' % device) exit(1) eaa = scsi.modesense6(page_code=MODESENSE6.PAGE_CODE.ELEMENT_ADDRESS_ASSIGNMENT).result['mode_pages'][0] # get the data transfer elements dte = scsi.readelementstatus( start=eaa['first_data_transfer_element_address'], num=eaa['num_data_transfer_elements'], element_type=READELEMENTSTATUS.ELEMENT_TYPE.DATA_TRANSFER, voltag=1, curdata=1, dvcid=1, alloclen=16384).result['element_status_pages'][0]['element_descriptors'] # get all the storage elements se = scsi.readelementstatus( start=eaa['first_storage_element_address'], num=eaa['num_storage_elements'], element_type=READELEMENTSTATUS.ELEMENT_TYPE.STORAGE, voltag=1, curdata=1, dvcid=1, alloclen=16384).result['element_status_pages'][0]['element_descriptors'] # get all the medium transport elements mte = scsi.readelementstatus( start=eaa['first_medium_transport_element_address'], num=eaa['num_medium_transport_elements'], element_type=READELEMENTSTATUS.ELEMENT_TYPE.MEDIUM_TRANSPORT, voltag=1, curdata=1, dvcid=1, alloclen=16384).result['element_status_pages'][0]['element_descriptors'] if sys.argv[1] == 'status': return status(scsi, dte, se) if sys.argv[1] == 'load': return load(scsi, mte, dte, se, int(sys.argv[2]), int(sys.argv[3])) if sys.argv[1] == 'unload': return unload(scsi, mte, dte, se, int(sys.argv[2]), int(sys.argv[3])) usage() exit(1)
def main(): dev = MockInquiry() dev.opcodes = spc s = SCSI(dev) # cdb for standard page request i = s.inquiry(alloclen=128) cdb = i.cdb assert cdb[0] == s.device.opcodes.INQUIRY.value assert cdb[1:3] == bytearray(2) assert scsi_ba_to_int(cdb[3:5]) == 128 assert cdb[5] == 0 cdb = i.unmarshall_cdb(cdb) assert cdb['opcode'] == s.device.opcodes.INQUIRY.value assert cdb['evpd'] == 0 assert cdb['page_code'] == 0 assert cdb['alloc_len'] == 128 d = Inquiry.unmarshall_cdb(Inquiry.marshall_cdb(cdb)) assert d == cdb # supported vpd pages i = s.inquiry(evpd=1, page_code=0x88, alloclen=300) cdb = i.cdb assert cdb[0] == s.device.opcodes.INQUIRY.value assert cdb[1] == 0x01 assert cdb[2] == 0x88 assert scsi_ba_to_int(cdb[3:5]) == 300 assert cdb[5] == 0 cdb = i.unmarshall_cdb(cdb) assert cdb['opcode'] == s.device.opcodes.INQUIRY.value assert cdb['evpd'] == 1 assert cdb['page_code'] == 0x88 assert cdb['alloc_len'] == 300 d = Inquiry.unmarshall_cdb(Inquiry.marshall_cdb(cdb)) assert d == cdb
def main(): dev = MockInquiryStandard() dev.opcodes = sbc s = SCSI(dev) i = s.inquiry().result assert i['peripheral_qualifier'] == 1 assert i['peripheral_device_type'] == 5 assert i['rmb'] == 1 assert i['version'] == 7 assert i['normaca'] == 1 assert i['hisup'] == 0 assert i['response_data_format'] == 3 assert i['additional_length'] == 64 assert i['sccs'] == 1 assert i['acc'] == 0 assert i['tpgs'] == 3 assert i['3pc'] == 1 assert i['protect'] == 1 assert i['encserv'] == 1 assert i['vs'] == 1 assert i['multip'] == 1 assert i['addr16'] == 1 assert i['wbus16'] == 1 assert i['sync'] == 1 assert i['cmdque'] == 1 assert i['vs2'] == 1 assert i['clocking'] == 2 assert i['qas'] == 0 assert i['ius'] == 1 assert i['t10_vendor_identification'].decode("utf-8") == 'abcdefgh' assert i['product_identification'].decode("utf-8") == 'iiiiiiiijjjjjjjj' assert i['product_revision_level'].decode("utf-8") == 'revn' d = Inquiry.unmarshall_datain(Inquiry.marshall_datain(i)) assert d == i dev = MockLBP() dev.opcodes = sbc s = SCSI(dev) i = s.inquiry(evpd=1, page_code=INQUIRY.VPD.LOGICAL_BLOCK_PROVISIONING).result assert i['peripheral_qualifier'] == 0 assert i['peripheral_qualifier'] == 0 assert i['threshold_exponent'] == 0x12 assert i['lbpu'] == 1 assert i['lpbws'] == 1 assert i['lbpws10'] == 1 assert i['lbprz'] == 1 assert i['anc_sup'] == 1 assert i['dp'] == 1 assert i['provisioning_type'] == INQUIRY.PROVISIONING_TYPE.THIN_PROVISIONED d = Inquiry.unmarshall_datain(Inquiry.marshall_datain(i), evpd=1) assert d == i dev = MockUSN() dev.opcodes = sbc s = SCSI(dev) i = s.inquiry(evpd=1, page_code=INQUIRY.VPD.UNIT_SERIAL_NUMBER).result assert i['peripheral_qualifier'] == 0 assert i['peripheral_qualifier'] == 0 assert i['unit_serial_number'].decode("utf-8") == "ABCD" d = Inquiry.unmarshall_datain(Inquiry.marshall_datain(i), evpd=1) assert d == i dev = MockReferrals() dev.opcodes = sbc s = SCSI(dev) i = s.inquiry(evpd=1, page_code=INQUIRY.VPD.REFERRALS).result assert i['peripheral_qualifier'] == 0 assert i['peripheral_qualifier'] == 0 assert i['user_data_segment_size'] == 23 assert i['user_data_segment_multiplier'] == 37 d = Inquiry.unmarshall_datain(Inquiry.marshall_datain(i), evpd=1) assert d == i dev = MockExtendedInquiry() dev.opcodes = sbc s = SCSI(dev) i = s.inquiry(evpd=1, page_code=INQUIRY.VPD.EXTENDED_INQUIRY_DATA).result assert i['peripheral_qualifier'] == 0 assert i['peripheral_qualifier'] == 0 assert i['activate_microcode'] == 1 assert i['spt'] == 2 assert i['grd_chk'] == 1 assert i['app_chk'] == 1 assert i['ref_chk'] == 1 assert i['uask_sup'] == 1 assert i['group_sup'] == 1 assert i['prior_sup'] == 0 assert i['headsup'] == 0 assert i['ordsup'] == 1 assert i['simpsup'] == 1 assert i['wu_sup'] == 0 assert i['crd_sup'] == 1 assert i['nv_sup'] == 0 assert i['v_sup'] == 1 assert i['p_i_i_sup'] == 1 assert i['luiclr'] == 1 assert i['r_sup'] == 1 assert i['cbcs'] == 1 assert i['multi_it_nexus_microcode_download'] == 3 assert i['extended_self_test_completion_minutes'] == 15 assert i['poa_sup'] == 1 assert i['hra_sup'] == 1 assert i['vsa_sup'] == 1 assert i['maximum_supported_sense_data_length'] == 5 d = Inquiry.unmarshall_datain(Inquiry.marshall_datain(i), evpd=1) assert d == i dev = MockDevId() dev.opcodes = sbc s = SCSI(dev) i = s.inquiry(evpd=1, page_code=INQUIRY.VPD.DEVICE_IDENTIFICATION).result assert i['peripheral_qualifier'] == 0 assert i['peripheral_qualifier'] == 0 dd = i['designator_descriptors'] assert len(dd) == 2 # T10 designation descriptor assert dd[0]['association'] == 2 assert dd[0]['code_set'] == 2 assert dd[0]['designator_length'] == 8 assert dd[0]['designator_type'] == 1 assert dd[0]['piv'] == 1 assert dd[0]['protocol_identifier'] == 5 assert dd[0]['designator']['t10_vendor_id'].decode("utf-8") == 'Test T10' assert dd[0]['designator']['vendor_specific_id'].decode("utf-8") == '' # EUI-64 designation descriptor assert dd[1]['association'] == 2 assert dd[1]['code_set'] == 1 assert dd[1]['designator_length'] == 8 assert dd[1]['designator_type'] == 2 assert dd[1]['piv'] == 0 assert not hasattr(dd[1], 'protocol_identifier') assert dd[1]['designator']['ieee_company_id'] == 0x112233 assert dd[1]['designator']['vendor_specific_extension_id'].decode("utf-8") == 'abcde' d = Inquiry.unmarshall_datain(Inquiry.marshall_datain(i), evpd=1) assert d == i
class Device(object): def __init__(self, device): if not device: raise exceptions.CommandLineError( '--device parameter is required, should point to the disk ' 'device representing the meter.') self.device_name_ = device self.scsi_device_ = SCSIDevice(device, readwrite=True) self.scsi_ = SCSI(self.scsi_device_) self.scsi_.blocksize = _REGISTER_SIZE def connect(self): inq = self.scsi_.inquiry() logging.debug('Device connected: %r', inq.result) vendor = inq.result['t10_vendor_identification'][:32] if vendor != b'LifeScan': raise exceptions.ConnectionFailed( 'Device %s is not a LifeScan glucometer.' % self.device_name_) def disconnect(self): return def _send_request(self, lba, request_format, request_obj, response_format): """Send a request to the meter, and read its response. Args: lba: (int) 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 _query_string(self, selector): response = self._send_request(3, _QUERY_REQUEST, {'selector': selector}, _QUERY_RESPONSE) # Unfortunately the CString implementation in construct does not support # multi-byte encodings, so we need to discard the terminating null byte # ourself. return response.value[:-1] def get_meter_info(self): return common.MeterInfo( 'OneTouch %s glucometer' % self._query_string('model'), serial_number=self.get_serial_number(), version_info=('Software version: ' + self.get_version(), ), native_unit=self.get_glucose_unit()) def get_serial_number(self): return self._query_string('serial') def get_version(self): return self._query_string('software') def get_datetime(self): response = self._send_request(3, _READ_RTC_REQUEST, None, _READ_RTC_RESPONSE) return response.timestamp def set_datetime(self, date=datetime.datetime.now()): self._send_request(3, _WRITE_RTC_REQUEST, {'timestamp': date}, lifescan_binary_protocol.COMMAND_SUCCESS) # The device does not return the new datetime, so confirm by calling # READ RTC again. return self.get_datetime() def zero_log(self): self._send_request(3, _MEMORY_ERASE_REQUEST, None, lifescan_binary_protocol.COMMAND_SUCCESS) def get_glucose_unit(self): response = self._send_request(4, _READ_PARAMETER_REQUEST, {'selector': 'unit'}, _READ_UNIT_RESPONSE) return response.unit def _get_reading_count(self): response = self._send_request(3, _READ_RECORD_COUNT_REQUEST, None, _READ_RECORD_COUNT_RESPONSE) return response.count def _get_reading(self, record_id): response = self._send_request(3, _READ_RECORD_REQUEST, {'record_id': record_id}, _READ_RECORD_RESPONSE) return common.GlucoseReading(response.timestamp, float(response.value), meal=response.meal) def get_readings(self): record_count = self._get_reading_count() for record_id in range(record_count): yield self._get_reading(record_id)
class Device(object): def __init__(self, device): if not device: raise exceptions.CommandLineError( '--device parameter is required, should point to the disk device ' 'representing the meter.') self.device_name_ = device self.scsi_device_ = SCSIDevice(device, readwrite=True) self.scsi_ = SCSI(self.scsi_device_) self.scsi_.blocksize = _REGISTER_SIZE def _send_message(self, cmd, lba): """Send a request to the meter, and read its response. Args: cmd: (bytes) the raw command to send the device, without preamble or checksum. lba: (int) the address of the block register to use, known valid addresses are 3, 4 and 5. Returns: (bytes) The raw response from the meter. No preamble or coda is present, and the checksum has already been validated. """ self.scsi_.write10(lba, 1, _encode_message(cmd)) response = self.scsi_.read10(lba, 1) # TODO: validate that the response is valid. return _extract_message(response.datain) def connect(self): inq = self.scsi_.inquiry() vendor = inq.result['t10_vendor_identification'][:32] if vendor != b'LifeScan': raise exceptions.ConnectionFailed( 'Device %s is not a LifeScan glucometer.' % self.device_name_) def disconnect(self): return def get_meter_info(self): return common.MeterInfo( 'OneTouch %s glucometer' % self._query_string(_QUERY_KEY_MODEL), serial_number=self.get_serial_number(), version_info=( 'Software version: ' + self.get_version(),), native_unit=self.get_glucose_unit()) 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 _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_serial_number(self): return self._query_string(_QUERY_KEY_SERIAL) def get_version(self): return self._query_string(_QUERY_KEY_SOFTWARE) 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 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 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 _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 get_glucose_unit(self): unit_value = self._read_parameter(_PARAMETER_KEY_UNIT) if unit_value == b'\x00\x00\x00\x00': return common.UNIT_MGDL elif unit_value == b'\x01\x00\x00\x00': return common.UNIT_MMOLL else: raise exceptions.InvalidGlucoseUnit('%r' % unit_value) 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 get_readings(self): record_count = self._get_reading_count() for record_number in range(record_count): yield self._get_reading(record_number)
class Device: def __init__(self, device): if not device: raise exceptions.CommandLineError( '--device parameter is required, should point to the disk ' 'device representing the meter.') self.device_name_ = device self.scsi_device_ = SCSIDevice(device, readwrite=True) self.scsi_ = SCSI(self.scsi_device_) self.scsi_.blocksize = _REGISTER_SIZE def connect(self): inq = self.scsi_.inquiry() logging.debug('Device connected: %r', inq.result) vendor = inq.result['t10_vendor_identification'][:32] if vendor != b'LifeScan': raise exceptions.ConnectionFailed( 'Device %s is not a LifeScan glucometer.' % self.device_name_) def disconnect(self): # pylint: disable=no-self-use return def _send_request(self, lba, request_format, request_obj, response_format): """Send a request to the meter, and read its response. Args: lba: (int) 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 _query_string(self, selector): response = self._send_request( 3, _QUERY_REQUEST, {'selector': selector}, _QUERY_RESPONSE) return response.value def get_meter_info(self): return common.MeterInfo( 'OneTouch %s glucometer' % self._query_string('model'), serial_number=self.get_serial_number(), version_info=( 'Software version: ' + self.get_version(),), native_unit=self.get_glucose_unit()) def get_serial_number(self): return self._query_string('serial') def get_version(self): return self._query_string('software') def get_datetime(self): response = self._send_request( 3, _READ_RTC_REQUEST, None, _READ_RTC_RESPONSE) return response.timestamp def set_datetime(self, date=datetime.datetime.now()): self._send_request( 3, _WRITE_RTC_REQUEST, {'timestamp': date}, _COMMAND_SUCCESS) # The device does not return the new datetime, so confirm by calling # READ RTC again. return self.get_datetime() def zero_log(self): self._send_request( 3, _MEMORY_ERASE_REQUEST, None, _COMMAND_SUCCESS) def get_glucose_unit(self): response = self._send_request( 4, _READ_PARAMETER_REQUEST, {'selector': 'unit'}, _READ_UNIT_RESPONSE) return response.unit def _get_reading_count(self): response = self._send_request( 3, _READ_RECORD_COUNT_REQUEST, None, _READ_RECORD_COUNT_RESPONSE) return response.count def _get_reading(self, record_id): response = self._send_request( 3, _READ_RECORD_REQUEST, {'record_id': record_id}, _READ_RECORD_RESPONSE) return common.GlucoseReading( response.timestamp, float(response.value), meal=response.meal) def get_readings(self): record_count = self._get_reading_count() for record_id in range(record_count): yield self._get_reading(record_id)
class Device(driver.GlucometerDevice): def __init__(self, device: Optional[str]) -> None: if not device: raise exceptions.CommandLineError( "--device parameter is required, should point to the disk " "device representing the meter.") super().__init__(device) self.device_name_ = device self.scsi_device_ = SCSIDevice(device, readwrite=True) self.scsi_ = SCSI(self.scsi_device_) self.scsi_.blocksize = _REGISTER_SIZE def connect(self) -> None: inq = self.scsi_.inquiry() logging.debug("Device connected: %r", inq.result) vendor = inq.result["t10_vendor_identification"][:32] if vendor != b"LifeScan": raise exceptions.ConnectionFailed( f"Device {self.device_name_} is not a LifeScan glucometer.") def disconnect(self) -> None: # pylint: disable=no-self-use return 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 _query_string(self, selector: str) -> str: response = self._send_request(3, _QUERY_REQUEST, {"selector": selector}, _QUERY_RESPONSE) return response.value def get_meter_info(self) -> common.MeterInfo: model = self._query_string("model") return common.MeterInfo( f"OneTouch {model} glucometer", serial_number=self.get_serial_number(), version_info=(f"Software version: {self.get_version()}", ), native_unit=self.get_glucose_unit(), ) def get_serial_number(self) -> str: return self._query_string("serial") def get_version(self) -> str: return self._query_string("software") def get_datetime(self) -> datetime.datetime: response = self._send_request(3, _READ_RTC_REQUEST, None, _READ_RTC_RESPONSE) return response.timestamp def _set_device_datetime(self, date: datetime.datetime) -> datetime.datetime: self._send_request(3, _WRITE_RTC_REQUEST, {"timestamp": date}, _COMMAND_SUCCESS) # The device does not return the new datetime, so confirm by calling # READ RTC again. return self.get_datetime() def zero_log(self) -> None: self._send_request(3, _MEMORY_ERASE_REQUEST, None, _COMMAND_SUCCESS) def get_glucose_unit(self) -> common.Unit: response = self._send_request(4, _READ_PARAMETER_REQUEST, {"selector": "unit"}, _READ_UNIT_RESPONSE) return response.unit def _get_reading_count(self) -> int: response = self._send_request(3, _READ_RECORD_COUNT_REQUEST, None, _READ_RECORD_COUNT_RESPONSE) return response.count def _get_reading(self, record_id: int) -> common.GlucoseReading: response = self._send_request(3, _READ_RECORD_REQUEST, {"record_id": record_id}, _READ_RECORD_RESPONSE) return common.GlucoseReading(response.timestamp, float(response.value), meal=response.meal) def get_readings(self) -> Generator[common.AnyReading, None, None]: record_count = self._get_reading_count() for record_id in range(record_count): yield self._get_reading(record_id)