diff --git a/.vscode/launch.json b/.vscode/launch.json index 8a51cd3..f906b3e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,8 +8,8 @@ "name": "Python Debugger: Current File", "type": "debugpy", "request": "launch", - // "program": "src/__main__.py", - "module": "examples.dyscom.example_dyscom_send_file", + "program": "src/__main__.py", + // "module": "examples.dyscom.example_dyscom_send_file", "justMyCode": false, // "args": ["COM3"], "console": "integratedTerminal" diff --git a/HINTS.md b/HINTS.md index ee0c4c3..eaee21e 100644 --- a/HINTS.md +++ b/HINTS.md @@ -102,16 +102,10 @@ This page describes implementation details. - https://www.reddit.com/r/macOSVMs/comments/1gb8egp/macos_sonoma_virtualbox_bootloop_afterduring/?rdt=48615 -# Deviation from Instruction for Use - -## Dyscom commands - -### Common -- Datetime parameters have a different order +## I24 protocol hints ### DL_init - Init state seems always be UNUSED -- Strings are 1 byte longer than in other commands - Output data rate depends on init params filter property - Setting a filter overwrite other settings - ADS129x register channel 1-4 settings @@ -121,26 +115,10 @@ This page describes implementation details. ### DL_get for type file system status and list of measurement info - Return never meaningful values, probably not implemented on I24 side -### DL_get_ack for type file by name -- Additional parameter mode (1 byte) - - Undefined = 0 - - Multiblock = 1 - - Singleblock = 2 - ### DL_get for type battery - Dl get types (table 23) - 0 -> Battery (was Unused) -### DL_get_ack for type battery -- Energy state, 1 byte, is a flag, bit 1: cable connected, bit 2: device is loading -- Percentage, 1 byte, [0, 100] in percent -- Temperature, 1 byte, [-128, 127] in degrees -- Current, 4 bytes, [-327675, 327675] in milliampere -- Voltage, 4 bytes, [0, 65535] in millivolt - -### DL_send_file_ack -- Block number, 4 byte, block number of DL_send_file - ### DL_send_live_data - SignalType for each sample is always 0 - Contains always 5 samples, regardless of selected signal types in DL_init diff --git a/README.md b/README.md index cf189a4..ff9c164 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## Introduction -Python implementation of HasomedScience ScienceMode 4 protocol for P24 (https://github.com/ScienceMode/ScienceMode4_P24) and I24 (https://github.com/ScienceMode/ScienceMode4_I24) devices. To use this library see section [Installation](#installation). +Pure Python implementation of HasomedScience ScienceMode 4 protocol for P24 (https://github.com/ScienceMode/ScienceMode4_P24) and I24 (https://github.com/ScienceMode/ScienceMode4_I24) devices. To use this library see section [Installation](#installation). Library and examples are tested under Windows, Linux and MacOS. ## Requirements @@ -125,4 +125,7 @@ Python 3.11 or higher - Fixed error with PacketLowLevelChannelConfigAck result ## 0.0.17 -- Added sample that demonstrates how to read measurement data files from I24 devices \ No newline at end of file +- Added sample that demonstrates how to read measurement data files from I24 devices + +## 0.0.18 +- More documentation \ No newline at end of file diff --git a/examples/dyscom/example_dyscom_get.py b/examples/dyscom/example_dyscom_get.py index ee66057..72eb0e7 100644 --- a/examples/dyscom/example_dyscom_get.py +++ b/examples/dyscom/example_dyscom_get.py @@ -46,10 +46,20 @@ async def main() -> int: file_system_status = await dyscom.get_file_system_status() print(file_system_status) - #### + # this is the default calibration filename calibration_filename = f"rehaingest_{device_id}.cal" + #### + # get calibration file content calibration_content = await dyscom.get_file_content(calibration_filename) print(f"Calibration content length: {len(calibration_content)}") + # I24 devices calculates checksum from first 2 bytes of the file content + calculated_checksum = (calibration_content[0] << 8) | calibration_content[1] + print(f"Calculated calibration content checksum: {calculated_checksum}") + + #### + # get file info for calibration file + file_info = await dyscom.get_file_info(calibration_filename) + print(f"Calibration file info checksum: {file_info.checksum}") # close serial port connection connection.close() diff --git a/src/__main__.py b/src/__main__.py index 437c1f2..c440681 100644 --- a/src/__main__.py +++ b/src/__main__.py @@ -6,7 +6,7 @@ from science_mode_4.device_i24 import DeviceI24 from science_mode_4.dyscom.dyscom_types import DyscomFilterType, DyscomInitFlag, DyscomInitParams, DyscomPowerModulePowerType,\ - DyscomPowerModuleType, DyscomSignalType + DyscomPowerModuleType, DyscomSignalType, DyscomSysType from science_mode_4.utils.serial_port_connection import SerialPortConnection @@ -24,8 +24,24 @@ async def main() -> int: # to have a defined state await device.initialize() + # get dyscom layer to call low level commands dyscom = device.get_layer_dyscom() + device_id = await dyscom.get_device_id() + + #### + calibration_filename = f"rehaingest_{device_id}.cal" + calibration_content = await dyscom.get_file_content(calibration_filename) + print(f"Calibration content length: {len(calibration_content)}") + calculated_checksum = (calibration_content[0] << 8) | calibration_content[1] + print(f"Calculated calibration content checksum: {calculated_checksum}") + + #### + file_info = await dyscom.get_file_info(calibration_filename) + print(f"Calibration file info checksum: {file_info.checksum}") + + s_y_s = await dyscom.sys(DyscomSysType.DEVICE_STORAGE) + print(f"Sys {s_y_s.state.name}") # call enable measurement power module and memory card for measurement await dyscom.power_module(DyscomPowerModuleType.MEASUREMENT, DyscomPowerModulePowerType.SWITCH_ON) diff --git a/src/science_mode_4/device.py b/src/science_mode_4/device.py index 4d81142..1ad9cd2 100644 --- a/src/science_mode_4/device.py +++ b/src/science_mode_4/device.py @@ -84,7 +84,8 @@ async def initialize(self): if DeviceCapability.DYSCOM in self._capabilities: # get operation mode to see if dyscom measurement is running operation_mode = await self.get_layer_dyscom().get_operation_mode() - if operation_mode in [DyscomGetOperationModeType.LIVE_MEASURING_PRE, + if operation_mode in [DyscomGetOperationModeType.UNDEFINED, + DyscomGetOperationModeType.LIVE_MEASURING_PRE, DyscomGetOperationModeType.LIVE_MEASURING, DyscomGetOperationModeType.RECORD_PRE, DyscomGetOperationModeType.RECORD, diff --git a/src/science_mode_4/dyscom/ads129x/ads129x.py b/src/science_mode_4/dyscom/ads129x/ads129x.py index a206aa4..f2ccae4 100644 --- a/src/science_mode_4/dyscom/ads129x/ads129x.py +++ b/src/science_mode_4/dyscom/ads129x/ads129x.py @@ -31,7 +31,7 @@ class Ads129x: channel_4_setting_register = Ads129xChannelSettingsRegister() # CH4SET positive_signal_derivation_register = 0x02 # RLD_SENSP - negative_signal_derivation_register = 0xEA # RLD_SENSN + negative_signal_derivation_register = 0x02 # RLD_SENSN positive_signal_lead_off_detection_register = 0 # LOFF_SENSP negative_signal_lead_off_detection_register = 0 # LOFF_SENSN diff --git a/src/science_mode_4/dyscom/dyscom_get_file_info.py b/src/science_mode_4/dyscom/dyscom_get_file_info.py index 962e9f4..47168ec 100644 --- a/src/science_mode_4/dyscom/dyscom_get_file_info.py +++ b/src/science_mode_4/dyscom/dyscom_get_file_info.py @@ -30,8 +30,10 @@ def get_data(self) -> bytes: bb = ByteBuilder() bb.append_bytes(super().get_data()) bb.append_bytes(DyscomHelper.str_to_bytes(self._filename, 128)) - # maybe more parameters are necessary here - # file_size, file_checksum + # file size + bb.append_value(0, 4, True) + # checksum + bb.append_value(0, 2, True) return bb.get_bytes() diff --git a/src/science_mode_4/dyscom/dyscom_layer.py b/src/science_mode_4/dyscom/dyscom_layer.py index 2171908..9783e67 100644 --- a/src/science_mode_4/dyscom/dyscom_layer.py +++ b/src/science_mode_4/dyscom/dyscom_layer.py @@ -6,8 +6,8 @@ from science_mode_4.layer import Layer from science_mode_4.protocol.commands import Commands from science_mode_4.utils.logger import logger -from .dyscom_types import DyscomFrequencyOut, DyscomGetOperationModeType, DyscomPowerModuleType,\ - DyscomPowerModulePowerType, DyscomSignalType, DyscomSysType +from .dyscom_types import DyscomFrequencyOut, DyscomGetOperationModeType, DyscomInitState, DyscomPowerModuleType,\ + DyscomPowerModulePowerType, DyscomSignalType, DyscomSysState, DyscomSysType from .dyscom_init import DyscomInitResult, PacketDyscomInit, PacketDyscomInitAck, DyscomInitParams from .dyscom_get_file_system_status import PacketDyscomGetFileSystemStatus, PacketDyscomGetAckFileSystemStatus,\ DyscomGetFileSystemStatusResult @@ -36,6 +36,9 @@ async def init(self, params: DyscomInitParams) -> DyscomInitResult: p = PacketDyscomInit(params) ack: PacketDyscomInitAck = await self.send_packet_and_wait(p) self._check_result_error(ack.result_error, "DyscomInit") + if ack.init_state not in [DyscomInitState.UNUSED, DyscomInitState.SUCCESS]: + raise ValueError(f"Dyscom error init {ack.init_state.name}") + logger().info("Dyscom init, measurement_file_id: %s, state: %s, frequency: %s",\ ack.measurement_file_id, ack.init_state.name, ack.frequency_out.name) return DyscomInitResult(ack.register_map_ads129x, ack.measurement_file_id, ack.init_state, ack.frequency_out) @@ -139,6 +142,9 @@ async def sys(self, sys_type: DyscomSysType, filename: str = "") -> DyscomSysRes p = PacketDyscomSys(sys_type, filename) ack: PacketDyscomSysAck = await self.send_packet_and_wait(p) self._check_result_error(ack.result_error, "DyscomSys") + if ack.state not in [DyscomSysState.SUCCESSFUL]: + raise ValueError(f"Dyscom error sys {ack.state.name}") + logger().info("Dyscom sys, type: %s, state: %s, filename: %s", ack.sys_type.name, ack.state.name, ack.filename) return DyscomSysResult(ack.sys_type, ack.state, ack.filename)