diff --git a/.vscode/launch.json b/.vscode/launch.json index b54df0a..266e7b4 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "type": "debugpy", "request": "launch", // "program": "__main__.py", - "module": "examples.mid_level.example_mid_level", + "module": "examples.dyscom.example_dyscom_write_csv", "justMyCode": false, // "args": ["COM3"], "console": "integratedTerminal" diff --git a/HINTS.md b/HINTS.md index e50b1f8..1ba8509 100644 --- a/HINTS.md +++ b/HINTS.md @@ -24,6 +24,12 @@ This page describes implementation details. - The acknowledge needs to handled manually by using _PacketBuffer_ object from device - _PacketBuffer_ reads data from connection and separates packets from data stream +## Logging +- Library creates a custom logger, see class _Logger_ +- By default some information is logged to console +- Set log level to DEBUG to get more detailed information +- For more performance, disable logger + ## General layer - Contains functions to get common information like device serial or firmware version @@ -57,6 +63,7 @@ This page describes implementation details. - Device sends now _DlSendLiveData_ packets with measurement data - Call _stop()_ to end measurement - Call _power_module()_ to power off measurement module +- Important: all storage related functions are untested # Deviation from Instruction for Use @@ -66,8 +73,12 @@ This page describes implementation details. - Strings are 1 byte less long (null termination is not an extra byte) in acknowledge packets - Datetime parameters have a different order +### DL_init +- Init state seems always be UNUSED +- Output data rate depends on init params filter property + ### DL_get_ack for type file by name -- Addition parameter mode (1 byte) +- Additional parameter mode (1 byte) - Undefined = 0 - Multiblock = 1 - Singleblock = 2 diff --git a/README.md b/README.md index 5b92f42..1b5c4d1 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,8 @@ Python 3.11 or higher - Located in folder `examples` - Run examples with `python -m examples..` - Example: `python -m examples.dyscom.example_dyscom_fastplotlib` + - All examples try to find the serial port that a science mode device is connected to automatically + - If that fails, provide serial port name as parameter, e.g. `python -m examples.. COM3` - Examples have own dependencies, see [Dependencies for examples](#dependencies-for-examples) - General layer - `example_general.py` diff --git a/__main__.py b/__main__.py index 1407cc3..e9d8441 100644 --- a/__main__.py +++ b/__main__.py @@ -4,139 +4,27 @@ import sys import asyncio -import matplotlib.pyplot as plt -import numpy as np +from science_mode_4.device_p24 import DeviceP24 +from science_mode_4.utils.serial_port_connection import SerialPortConnection -from src.science_mode_4 import LayerDyscom, LayerLowLevel,\ - Commands, Connector, Channel, ChannelPoint,\ - SerialPortConnection,\ - DeviceI24,\ - Ads129xOutputDataRate, Ads129xPowerMode,\ - PacketDyscomGetAckOperationMode, PacketDyscomSendLiveData,\ - DyscomInitParams, DyscomPowerModulePowerType, DyscomPowerModuleType, DyscomSignalType - -# print(science_mode_4.__version__) - -def send_channel_config(low_level_layer: LayerLowLevel, connector: Connector): - """Sends channel update""" - # device can store up to 10 channel config commands - for channel in Channel: - # send_channel_config does not wait for an acknowledge - low_level_layer.send_channel_config(True, channel, connector, - [ChannelPoint(4000, 20), ChannelPoint(4000, -20), - ChannelPoint(4000, 0)]) - async def main() -> int: """Main function""" - connection = SerialPortConnection("COM6") + devices = SerialPortConnection.list_science_mode_device_ports() + connection = SerialPortConnection(devices[0].device) # devices = UsbConnection.list_science_mode_devices() # connection = UsbConnection(devices[0]) # connection = NullConnection() connection.open() - device = DeviceI24(connection) + device = DeviceP24(connection) await device.initialize() - # general = device.get_layer_general() - # print(f"device id: {general.device_id}") - # print(f"firmware version: {general.firmware_version}") - # print(f"science mode version: {general.science_mode_version}") - - dyscom: LayerDyscom = device.get_layer_dyscom() - # fss: DyscomGetFileSystemStatusResult = await dyscom.get_file_system_status() - # print(f"Ready {fss.file_system_ready}, used size {fss.used_size}, free size {fss.free_size}") - # fbn: DyscomGetFileByNameResult = await dyscom.get_file_by_name() - # print(f"Filename {fbn.filename}, block offset {fbn.block_offset}, filesize {fbn.filesize}, nr of blocks {fbn.number_of_blocks}") - # fv: str = await dyscom.get_firmware_version() - # print(f"Firmware version {fv}") - # nrof = await dyscom.get_list_of_measurement_meta_info() - # print(f"Number of measurement meta info {nrof}") - # did = await dyscom.get_device_id() - # print(f"Device ID {did}") - # fi = await dyscom.get_file_info() - # print(f"File info {fi.filename} {fi.filesize} {fi.checksum}") - # b = await dyscom.get_battery() - # print(f"Battery {b.voltage} {b.current} {b.percentage} {b.temperature} {b.energy_state}") - - # sys_ack: DyscomSysResult = await dyscom.sys(DyscomSysType.DEVICE_STORAGE) - # print(f"Sys {sys_ack.sys_type} {sys_ack.state} {sys_ack.filename}") - - await dyscom.power_module(DyscomPowerModuleType.MEASUREMENT, DyscomPowerModulePowerType.SWITCH_ON) - init_params = DyscomInitParams() - init_params.register_map_ads129x.config_register_1.output_data_rate = Ads129xOutputDataRate.HR_MODE_500_SPS__LP_MODE_250_SPS - init_params.register_map_ads129x.config_register_1.power_mode = Ads129xPowerMode.LOW_POWER - await dyscom.init(init_params) - - fig, ax = plt.subplots() - ax.set(xlabel="Sample Time (µs)", ylabel="Current (mA)", - title="Current measurement") - ax.grid() - plt.ion() - plt.show() - - def update_ylim(data: list[float]): - if len(data) == 0: - return - - new_min = data[0] - new_max = data[0] - for x in data: - new_min = min(new_min, x) - new_max = max(new_max, x) - - offset = (new_max - new_min) * 0.1 - plt.ylim(new_min - offset, new_max + offset) - - plot_buffer: list[float] = [] - plot_data, = ax.plot(np.linspace(0, 100, len(plot_buffer)), plot_buffer) - update_ylim(plot_buffer) - used_signals: set[DyscomSignalType] = set() - await dyscom.start() - - for x in range(1000): - if x % 100 == 0: - dyscom.send_get_operation_mode() - - while True: - ack = dyscom.packet_buffer.get_packet_from_buffer() - if ack: - if ack.command == Commands.DlGetAck: - om_ack: PacketDyscomGetAckOperationMode = ack - print(f"Operation mode {om_ack.operation_mode}") - elif ack.command == Commands.DlSendLiveData: - sld: PacketDyscomSendLiveData = ack - if sld.status_error: - print(f"SendLiveData status error {sld.samples}") - break - if sld.number % 50 == 0: - # print(f"Append {sld.value} {sld.signal_type}") - for s in sld.samples: - used_signals.add(s.signal_type) - if len(plot_buffer) > 250: - plot_buffer.pop(0) - plot_buffer.append(sld.time_offset) # samples[1].value - plot_data.remove() - plot_data, = ax.plot(np.linspace(0, len(plot_buffer), len(plot_buffer)), plot_buffer, color = "b") - # plot_data.set_xdata(np.linspace(0, 100, len(plot_buffer))) - # plot_data.set_ydata(plot_buffer) - # update_ylim(plot_buffer) - fig.canvas.draw() - fig.canvas.flush_events() - - else: - break - - await asyncio.sleep(0.01) - - # wait until all acknowledges are received - await asyncio.sleep(0.5) - - await dyscom.stop() - await dyscom.power_module(DyscomPowerModuleType.MEASUREMENT, DyscomPowerModulePowerType.SWITCH_OFF) - - print(used_signals) + general = device.get_layer_general() + print(f"Device id: {general.device_id}") + print(f"Firmware version: {general.firmware_version}") + print(f"Science mode version: {general.science_mode_version}") connection.close() diff --git a/examples/dyscom/example_dyscom_fastplotlib.py b/examples/dyscom/example_dyscom_fastplotlib.py index d1af4fa..43df477 100644 --- a/examples/dyscom/example_dyscom_fastplotlib.py +++ b/examples/dyscom/example_dyscom_fastplotlib.py @@ -79,13 +79,13 @@ async def device_communication() -> int: if ack: # because there are multiple get commands, we need to additionally check kind, # which is always associated DyscomGetType - if ack.command == Commands.DlGetAck and ack.kind == DyscomGetType.OPERATION_MODE: + if ack.command == Commands.DL_GET_ACK and ack.kind == DyscomGetType.OPERATION_MODE: om_ack: PacketDyscomGetAckOperationMode = ack - print(f"Operation mode {om_ack.operation_mode}") + print(f"Operation mode {om_ack.operation_mode.name}") # check if measurement is still active if om_ack.result_error != ResultAndError.NO_ERROR: break - elif ack.command == Commands.DlSendLiveData: + elif ack.command == Commands.DL_SEND_LIVE_DATA: live_data_counter += 1 sld: PacketDyscomSendLiveData = ack @@ -111,9 +111,6 @@ async def device_communication() -> int: await asyncio.sleep(0.01) - # wait until all acknowledges are received - await asyncio.sleep(0.5) - # stop measurement await dyscom.stop() # turn power module off diff --git a/examples/dyscom/example_dyscom_pyplot.py b/examples/dyscom/example_dyscom_pyplot.py index 36f2103..96fe81c 100644 --- a/examples/dyscom/example_dyscom_pyplot.py +++ b/examples/dyscom/example_dyscom_pyplot.py @@ -9,6 +9,7 @@ from science_mode_4.dyscom.dyscom_get_operation_mode import PacketDyscomGetAckOperationMode from science_mode_4.dyscom.dyscom_send_live_data import PacketDyscomSendLiveData from science_mode_4.dyscom.dyscom_types import DyscomInitParams, DyscomPowerModulePowerType, DyscomPowerModuleType, DyscomSignalType +from science_mode_4.utils.logger import logger from examples.utils.example_utils import ExampleUtils from examples.utils.pyplot_utils import PyPlotHelper @@ -18,6 +19,9 @@ async def main() -> int: plot_helper = PyPlotHelper({0: ["BI", "blue"]}, 250) + # disable logger to increase performance + logger().disabled = True + # get comport from command line argument com_port = ExampleUtils.get_comport_from_commandline_argument() # create serial port connection @@ -55,10 +59,10 @@ async def main() -> int: while True: ack = dyscom.packet_buffer.get_packet_from_buffer(live_data_counter == 0) if ack: - if ack.command == Commands.DlGetAck: + if ack.command == Commands.DL_GET_ACK: om_ack: PacketDyscomGetAckOperationMode = ack - print(f"Operation mode {om_ack.operation_mode}") - elif ack.command == Commands.DlSendLiveData: + print(f"Operation mode {om_ack.operation_mode.name}") + elif ack.command == Commands.DL_SEND_LIVE_DATA: live_data_counter += 1 sld: PacketDyscomSendLiveData = ack @@ -77,9 +81,6 @@ async def main() -> int: await asyncio.sleep(0.01) - # wait until all acknowledges are received - await asyncio.sleep(0.5) - # stop measurement await dyscom.stop() # turn power module off diff --git a/examples/dyscom/example_dyscom_write_csv.py b/examples/dyscom/example_dyscom_write_csv.py index d58154c..09706c2 100644 --- a/examples/dyscom/example_dyscom_write_csv.py +++ b/examples/dyscom/example_dyscom_write_csv.py @@ -11,9 +11,10 @@ from science_mode_4.dyscom.ads129x.ads129x_config_register_1 import Ads129xOutputDataRate, Ads129xPowerMode from science_mode_4.dyscom.dyscom_get_operation_mode import PacketDyscomGetAckOperationMode from science_mode_4.dyscom.dyscom_send_live_data import PacketDyscomSendLiveData -from science_mode_4.dyscom.dyscom_types import DyscomGetType, DyscomInitParams, DyscomPowerModulePowerType,\ +from science_mode_4.dyscom.dyscom_types import DyscomFilterType, DyscomGetType, DyscomInitParams, DyscomPowerModulePowerType,\ DyscomPowerModuleType, DyscomSignalType from science_mode_4.protocol.types import ResultAndError +from science_mode_4.utils.logger import logger from examples.utils.example_utils import ExampleUtils from examples.utils.csv_utils import CsvHelper @@ -27,6 +28,9 @@ def main(): async def device_communication() -> int: """Communication with science mode device""" + # disable logger to increase performance + logger().disabled = True + # get comport from command line argument com_port = ExampleUtils.get_comport_from_commandline_argument() # create serial port connection @@ -47,6 +51,7 @@ async def device_communication() -> int: await dyscom.power_module(DyscomPowerModuleType.MEASUREMENT, DyscomPowerModulePowerType.SWITCH_ON) # call init with 4k sample rate and enable signal types init_params = DyscomInitParams() + init_params.filter = DyscomFilterType.PREDEFINED_FILTER_3 init_params.signal_type = [DyscomSignalType.BI, DyscomSignalType.EMG_1,\ DyscomSignalType.EMG_2, DyscomSignalType.BREATHING, DyscomSignalType.TEMPERATURE] init_params.register_map_ads129x.config_register_1.output_data_rate = Ads129xOutputDataRate.HR_MODE_4_KSPS__LP_MODE_2_KSPS @@ -73,13 +78,13 @@ async def device_communication() -> int: if ack: # because there are multiple get commands, we need to additionally check kind, # which is always associated DyscomGetType - if ack.command == Commands.DlGetAck and ack.kind == DyscomGetType.OPERATION_MODE: + if ack.command == Commands.DL_GET_ACK and ack.kind == DyscomGetType.OPERATION_MODE: om_ack: PacketDyscomGetAckOperationMode = ack - print(f"Operation mode {om_ack.operation_mode}") + print(f"Operation mode {om_ack.operation_mode.name}") # check if measurement is still active if om_ack.result_error != ResultAndError.NO_ERROR: break - elif ack.command == Commands.DlSendLiveData: + elif ack.command == Commands.DL_SEND_LIVE_DATA: live_data_counter += 1 total_count += 1 @@ -90,7 +95,7 @@ async def device_communication() -> int: csv_helper.append_values(ack.number, [sld.samples[0].value, sld.samples[1].value,\ sld.samples[2].value, sld.samples[3].value,\ - sld.samples[4].value], sld.time_offset) + sld.samples[4].value], sld.time_offset) else: # print(f"Live data acknowledges per iteration {live_data_counter}") @@ -101,8 +106,6 @@ async def device_communication() -> int: # print stats end_time = timer() print(f"Samples: {total_count}, duration: {end_time - start_time}, sample rate: {total_count / (end_time - start_time)}") - # wait until all acknowledges are received - await asyncio.sleep(0.5) # stop measurement await dyscom.stop() diff --git a/examples/low_level/example_low_level_plot.py b/examples/low_level/example_low_level_plot.py index ea7e042..5ea2da0 100644 --- a/examples/low_level/example_low_level_plot.py +++ b/examples/low_level/example_low_level_plot.py @@ -50,7 +50,7 @@ async def main() -> int: # get new data from connection ack = low_level_layer.packet_buffer.get_packet_from_buffer() if ack: - if ack.command == Commands.LowLevelChannelConfigAck: + if ack.command == Commands.LOW_LEVEL_CHANNEL_CONFIG_ACK: ll_config_ack: PacketLowLevelChannelConfigAck = ack measurement_sample_time = ll_config_ack.sampling_time_in_microseconds measurement_samples.extend(ll_config_ack.measurement_samples) diff --git a/examples/utils/fastplotlib_utils.py b/examples/utils/fastplotlib_utils.py index 8c6f3a8..ce2715c 100644 --- a/examples/utils/fastplotlib_utils.py +++ b/examples/utils/fastplotlib_utils.py @@ -19,7 +19,7 @@ def __init__(self, sub_plot, max_value_count: int, color: str): self._sub_plot = sub_plot # we use an array with zero as start data - y_data = np.array([0] * max_value_count) + y_data = np.array([0.0] * max_value_count, dtype=np.float32) self._line = sub_plot.add_line(y_data, name="values", colors=color) # this queue is used to synchronize data between background and main thread @@ -88,7 +88,7 @@ def __init__(self, channels: dict[int, tuple[str, str]], max_value_count: int): sub_plot_counter += 1 - # set animation function that is called regulary to update plots + # set animation function that is called regularly to update plots self._figure.add_animations(self._animation) # show figure self._figure.show(maintain_aspect=False) diff --git a/pyproject.toml b/pyproject.toml index 37248a0..0692517 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "science_mode_4" -version = "0.0.9" +version = "0.0.10" authors = [ { name="Marc Hofmann", email="marc-hofmann@gmx.de" }, ] diff --git a/src/science_mode_4/__init__.py b/src/science_mode_4/__init__.py index ee20746..b8a003f 100644 --- a/src/science_mode_4/__init__.py +++ b/src/science_mode_4/__init__.py @@ -15,3 +15,4 @@ from .device_i24 import * __version__ = version("science_mode_4") +logger().info("Library version %s", __version__) diff --git a/src/science_mode_4/device.py b/src/science_mode_4/device.py index 0c1033c..8f73cb3 100644 --- a/src/science_mode_4/device.py +++ b/src/science_mode_4/device.py @@ -74,7 +74,7 @@ def capabilities(self) -> set[DeviceCapability]: async def initialize(self): """Initialize device to get basic information (serial, versions) and stop any active stimulation/measurement""" - if [DeviceCapability.LOW_LEVEL, DeviceCapability.MID_LEVEL] in self._capabilities: + if {DeviceCapability.LOW_LEVEL, DeviceCapability.MID_LEVEL}.issubset(self._capabilities): # get stim status to see if low/mid level is initialized or running stim_status = await self.get_layer_general().get_stim_status() if stim_status.stim_status == StimStatus.LOW_LEVEL_INITIALIZED: diff --git a/src/science_mode_4/dyscom/dyscom_get.py b/src/science_mode_4/dyscom/dyscom_get.py index 66fef3a..aaa699b 100644 --- a/src/science_mode_4/dyscom/dyscom_get.py +++ b/src/science_mode_4/dyscom/dyscom_get.py @@ -13,7 +13,7 @@ class PacketDyscomGet(Packet): def __init__(self): super().__init__() - self._command = Commands.DlGet + self._command = Commands.DL_GET self._type = DyscomGetType.BATTERY @@ -29,7 +29,7 @@ class PacketDyscomGetAck(PacketAck): def __init__(self, data: bytes): super().__init__(data) - self._command = Commands.DlGetAck + self._command = Commands.DL_GET_ACK self._result_error = ResultAndError.NO_ERROR self._type = DyscomGetType.BATTERY diff --git a/src/science_mode_4/dyscom/dyscom_get_battery_status.py b/src/science_mode_4/dyscom/dyscom_get_battery_status.py index c98cb6a..9979e06 100644 --- a/src/science_mode_4/dyscom/dyscom_get_battery_status.py +++ b/src/science_mode_4/dyscom/dyscom_get_battery_status.py @@ -23,7 +23,7 @@ class PacketDyscomGetBatteryStatus(PacketDyscomGet): def __init__(self): super().__init__() - self._command = Commands.DlGet + self._command = Commands.DL_GET self._type = DyscomGetType.BATTERY self._kind = int(self._type) @@ -52,13 +52,13 @@ def __init__(self, data: bytes): @property def voltage(self) -> int: - """Getter for voltage, [0, 65535] in milli volt""" + """Getter for voltage, [0, 65535] in millivolt""" return self._voltage @property def current(self) -> int: - """Getter for current, [-327675, 327675] in milli ampere""" + """Getter for current, [-327675, 327675] in milliampere""" return self._current diff --git a/src/science_mode_4/dyscom/dyscom_get_device_id.py b/src/science_mode_4/dyscom/dyscom_get_device_id.py index d4c03d7..0599000 100644 --- a/src/science_mode_4/dyscom/dyscom_get_device_id.py +++ b/src/science_mode_4/dyscom/dyscom_get_device_id.py @@ -12,7 +12,7 @@ class PacketDyscomGetDeviceId(PacketDyscomGet): def __init__(self): super().__init__() - self._command = Commands.DlGet + self._command = Commands.DL_GET self._type = DyscomGetType.DEVICE_ID self._kind = int(self._type) 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 c1e52bd..78ddfd4 100644 --- a/src/science_mode_4/dyscom/dyscom_get_file_info.py +++ b/src/science_mode_4/dyscom/dyscom_get_file_info.py @@ -21,7 +21,7 @@ class PacketDyscomGetFileInfo(PacketDyscomGet): def __init__(self): super().__init__() - self._command = Commands.DlGet + self._command = Commands.DL_GET self._type = DyscomGetType.FILE_INFO self._kind = int(self._type) diff --git a/src/science_mode_4/dyscom/dyscom_get_file_system_status.py b/src/science_mode_4/dyscom/dyscom_get_file_system_status.py index b90c455..6908ff9 100644 --- a/src/science_mode_4/dyscom/dyscom_get_file_system_status.py +++ b/src/science_mode_4/dyscom/dyscom_get_file_system_status.py @@ -21,7 +21,7 @@ class PacketDyscomGetFileSystemStatus(PacketDyscomGet): def __init__(self): super().__init__() - self._command = Commands.DlGet + self._command = Commands.DL_GET self._type = DyscomGetType.FILESYSTEM_STATUS self._kind = int(self._type) diff --git a/src/science_mode_4/dyscom/dyscom_get_firmware_version.py b/src/science_mode_4/dyscom/dyscom_get_firmware_version.py index 65378ef..5d7ba9c 100644 --- a/src/science_mode_4/dyscom/dyscom_get_firmware_version.py +++ b/src/science_mode_4/dyscom/dyscom_get_firmware_version.py @@ -12,7 +12,7 @@ class PacketDyscomGetFirmwareVersion(PacketDyscomGet): def __init__(self): super().__init__() - self._command = Commands.DlGet + self._command = Commands.DL_GET self._type = DyscomGetType.FIRMWARE_VERSION self._kind = int(self._type) diff --git a/src/science_mode_4/dyscom/dyscom_get_list_of_measurement_meta_info.py b/src/science_mode_4/dyscom/dyscom_get_list_of_measurement_meta_info.py index 2109a0d..9d76e9c 100644 --- a/src/science_mode_4/dyscom/dyscom_get_list_of_measurement_meta_info.py +++ b/src/science_mode_4/dyscom/dyscom_get_list_of_measurement_meta_info.py @@ -11,7 +11,7 @@ class PacketDyscomGetListOfMeasurementMetaInfo(PacketDyscomGet): def __init__(self): super().__init__() - self._command = Commands.DlGet + self._command = Commands.DL_GET self._type = DyscomGetType.LIST_OF_MEASUREMENT_META_INFO self._kind = int(self._type) diff --git a/src/science_mode_4/dyscom/dyscom_get_operation_mode.py b/src/science_mode_4/dyscom/dyscom_get_operation_mode.py index 54e6dbe..cc52002 100644 --- a/src/science_mode_4/dyscom/dyscom_get_operation_mode.py +++ b/src/science_mode_4/dyscom/dyscom_get_operation_mode.py @@ -11,7 +11,7 @@ class PacketDyscomGetOperationMode(PacketDyscomGet): def __init__(self): super().__init__() - self._command = Commands.DlGet + self._command = Commands.DL_GET self._type = DyscomGetType.OPERATION_MODE self._kind = int(self._type) diff --git a/src/science_mode_4/dyscom/dyscom_init.py b/src/science_mode_4/dyscom/dyscom_init.py index 68bcf1f..0693586 100644 --- a/src/science_mode_4/dyscom/dyscom_init.py +++ b/src/science_mode_4/dyscom/dyscom_init.py @@ -21,7 +21,7 @@ class PacketDyscomInit(Packet): def __init__(self, params: DyscomInitParams = DyscomInitParams()): super().__init__() - self._command = Commands.DlInit + self._command = Commands.DL_INIT self._params = params @@ -47,11 +47,11 @@ class PacketDyscomInitAck(PacketAck): def __init__(self, data: bytes): super().__init__(data) - self._command = Commands.DlInitAck + self._command = Commands.DL_INIT_ACK self._result_error = ResultAndError.NO_ERROR self._register_map_ads129x: Ads129x self._measurement_file_id: str - self._init_state = DyscomInitState.SUCESS + self._init_state = DyscomInitState.SUCCESS self._frequency_out = DyscomFrequencyOut.SAMPLES_PER_SECOND_4K if not data is None: diff --git a/src/science_mode_4/dyscom/dyscom_layer.py b/src/science_mode_4/dyscom/dyscom_layer.py index 1695461..f0c8614 100644 --- a/src/science_mode_4/dyscom/dyscom_layer.py +++ b/src/science_mode_4/dyscom/dyscom_layer.py @@ -1,6 +1,7 @@ """Provides low level layer""" from science_mode_4.layer import Layer +from science_mode_4.utils.logger import logger from .dyscom_types import DyscomGetOperationModeType, DyscomPowerModuleType, DyscomPowerModulePowerType, DyscomSysType from .dyscom_init import DyscomInitResult, PacketDyscomInit, PacketDyscomInitAck, DyscomInitParams from .dyscom_get_file_system_status import PacketDyscomGetFileSystemStatus, PacketDyscomGetAckFileSystemStatus,\ @@ -30,6 +31,7 @@ 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") + logger().info("Dyscom init, state: %s, frequency: %s", ack.init_state.name, ack.frequency_out.name) return DyscomInitResult(ack.register_map_ads129x, ack.init_state, ack.frequency_out) @@ -38,6 +40,8 @@ async def get_file_system_status(self) -> DyscomGetFileSystemStatusResult: p = PacketDyscomGetFileSystemStatus() ack: PacketDyscomGetAckFileSystemStatus = await self._send_packet_and_wait(p) self._check_result_error(ack.result_error, "DyscomGetFileSystemStatus") + logger().info("Dyscom get file system status, ready: %s, used size: %d, free size: %s",\ + ack.file_system_ready, ack.used_size, ack.free_size) return DyscomGetFileSystemStatusResult(ack.file_system_ready, ack.used_size, ack.free_size) @@ -46,6 +50,7 @@ async def get_list_of_measurement_meta_info(self) -> int: p = PacketDyscomGetListOfMeasurementMetaInfo() ack: PacketDyscomGetAckListOfMeasurementMetaInfo = await self._send_packet_and_wait(p) self._check_result_error(ack.result_error, "DyscomGetListOfMeasurementMetaInfo") + logger().info("Dyscom get list of mmi, number of measurements: %d", ack.number_of_measurements) return ack.number_of_measurements @@ -55,6 +60,8 @@ async def get_file_by_name(self) -> DyscomGetFileByNameResult: p = PacketDyscomGetFileByName() ack: PacketDyscomGetAckFileByName = await self._send_packet_and_wait(p) self._check_result_error(ack.result_error, "DyscomGetFileByName") + logger().info("Dyscom get file by name, filename: %s, block offset: %d, filesize: %d, number of blocks: %d, mode: %s",\ + ack.filename, ack.block_offset, ack.filesize, ack.number_of_blocks, ack.mode.name) return DyscomGetFileByNameResult(ack.filename, ack.block_offset, ack.filesize, ack.number_of_blocks, ack.mode) @@ -63,6 +70,7 @@ async def get_device_id(self) -> str: p = PacketDyscomGetDeviceId() ack: PacketDyscomGetAckDeviceId = await self._send_packet_and_wait(p) self._check_result_error(ack.result_error, "DyscomGetDeviceId") + logger().info("Dyscom get device id: %s", ack.device_id) return ack.device_id @@ -71,6 +79,7 @@ async def get_firmware_version(self) -> str: p = PacketDyscomGetFirmwareVersion() ack: PacketDyscomGetAckFirmwareVersion = await self._send_packet_and_wait(p) self._check_result_error(ack.result_error, "DyscomGetFirmwareVersion") + logger().info("Dyscom get firmware version: %s", ack.firmware_version) return ack.firmware_version @@ -79,6 +88,8 @@ async def get_file_info(self) -> DyscomGetFileInfoResult: p = PacketDyscomGetFileInfo() ack: PacketDyscomGetAckFileInfo = await self._send_packet_and_wait(p) self._check_result_error(ack.result_error, "DyscomGetFileInfo") + logger().info("Dyscom get file info, filename: %s, filesize: %d, checksum: %d",\ + ack.filename, ack.filesize, ack.checksum) return DyscomGetFileInfoResult(ack.filename, ack.filesize, ack.checksum) @@ -87,6 +98,8 @@ async def get_battery(self) -> DyscomGetBatteryResult: p = PacketDyscomGetBatteryStatus() ack: PacketDyscomGetAckBatteryStatus = await self._send_packet_and_wait(p) self._check_result_error(ack.result_error, "DyscomGetBatteryStatus") + logger().info("Dyscom get battery, voltage: %d, current: %d, percentage: %d, temperature: %d, energy state: %s",\ + ack.voltage, ack.current, ack.percentage, ack.temperature, ack.energy_state) return DyscomGetBatteryResult(ack.voltage, ack.current, ack.percentage, ack.temperature, ack.energy_state) @@ -95,12 +108,14 @@ async def get_operation_mode(self) -> DyscomGetOperationModeType: p = PacketDyscomGetOperationMode() ack: PacketDyscomGetAckOperationMode = await self._send_packet_and_wait(p) self._check_result_error(ack.result_error, "DyscomGetFirmwareVersion") + logger().info("Dyscom get operation mode: %s", ack.operation_mode.name) return ack.operation_mode def send_get_operation_mode(self): """Sends get dyscom get type operation mode and returns immediately without waiting for response""" p = PacketDyscomGetOperationMode() + logger().info("Dyscom send get operation mode") self._send_packet(p) @@ -109,6 +124,7 @@ async def power_module(self, module: DyscomPowerModuleType, power: DyscomPowerMo p = PacketDyscomPowerModule(module, power) ack: PacketDyscomPowerModuleAck = await self._send_packet_and_wait(p) self._check_result_error(ack.result_error, "DyscomStart") + logger().info("Dyscom power module, module: %s, power: %s", ack.module.name, ack.power.name) return DyscomPowerModuleResult(ack.module, ack.power) @@ -117,6 +133,7 @@ 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") + 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) @@ -131,6 +148,7 @@ async def start(self): p = PacketDyscomStart() ack: PacketDyscomStartAck = await self._send_packet_and_wait(p) self._check_result_error(ack.result_error, "DyscomStart") + logger().info("Dyscom start") async def stop(self): @@ -138,3 +156,4 @@ async def stop(self): p = PacketDyscomStop() ack: PacketDyscomStopAck = await self._send_packet_and_wait(p) self._check_result_error(ack.result_error, "DyscomStop") + logger().info("Dyscom stop") diff --git a/src/science_mode_4/dyscom/dyscom_power_module.py b/src/science_mode_4/dyscom/dyscom_power_module.py index 5340054..fc839c9 100644 --- a/src/science_mode_4/dyscom/dyscom_power_module.py +++ b/src/science_mode_4/dyscom/dyscom_power_module.py @@ -20,7 +20,7 @@ class PacketDyscomPowerModule(Packet): def __init__(self, module: DyscomPowerModuleType = DyscomPowerModuleType.MEASUREMENT, power = DyscomPowerModulePowerType.SWITCH_OFF): super().__init__() - self._command = Commands.DlPowerModule + self._command = Commands.DL_POWER_MODULE self._module = module self._power = power @@ -38,7 +38,7 @@ class PacketDyscomPowerModuleAck(PacketAck): def __init__(self, data: bytes): super().__init__(data) - self._command = Commands.DlPowerModuleAck + self._command = Commands.DL_POWER_MODULE_ACK self._result_error = ResultAndError.NO_ERROR self._module = DyscomPowerModuleType.MEASUREMENT self._power = DyscomPowerModulePowerType.SWITCH_OFF diff --git a/src/science_mode_4/dyscom/dyscom_send_file.py b/src/science_mode_4/dyscom/dyscom_send_file.py index 45df2cc..3192970 100644 --- a/src/science_mode_4/dyscom/dyscom_send_file.py +++ b/src/science_mode_4/dyscom/dyscom_send_file.py @@ -17,7 +17,7 @@ class PacketDyscomSendFile(PacketAck): def __init__(self, data: bytes): super().__init__(data) - self._command = Commands.DlSendFile + self._command = Commands.DL_SEND_FILE self._block_number = 0 self._block_size = 0 self._data: bytes = bytes() @@ -52,7 +52,7 @@ class PacketDyscomSendFileAck(Packet): def __init__(self, block_number: int = 0): super().__init__() - self._command = Commands.DlSendFileAck + self._command = Commands.DL_SEND_FILE_ACK self._block_number = block_number diff --git a/src/science_mode_4/dyscom/dyscom_send_live_data.py b/src/science_mode_4/dyscom/dyscom_send_live_data.py index 9399e3d..757fb73 100644 --- a/src/science_mode_4/dyscom/dyscom_send_live_data.py +++ b/src/science_mode_4/dyscom/dyscom_send_live_data.py @@ -13,7 +13,7 @@ class PacketDyscomSendLiveData(PacketAck): def __init__(self, data: bytes): super().__init__(data) - self._command = Commands.DlSendLiveData + self._command = Commands.DL_SEND_LIVE_DATA self._number_of_channels = 0 self._time_offset = 0 self._samples: list[DyscomElectrodeSample] = [] diff --git a/src/science_mode_4/dyscom/dyscom_send_measurement_meta_info.py b/src/science_mode_4/dyscom/dyscom_send_measurement_meta_info.py index 6ff445c..7b290b0 100644 --- a/src/science_mode_4/dyscom/dyscom_send_measurement_meta_info.py +++ b/src/science_mode_4/dyscom/dyscom_send_measurement_meta_info.py @@ -14,7 +14,7 @@ class PacketDyscomSendMeasurementMetaInfo(PacketAck): def __init__(self, data: bytes): super().__init__(data) - self._command = Commands.DlSendFile + self._command = Commands.DL_SEND_FILE self._init_params: DyscomInitParams = DyscomInitParams() self._file_name = "" self._file_size = 0 diff --git a/src/science_mode_4/dyscom/dyscom_start.py b/src/science_mode_4/dyscom/dyscom_start.py index 40e53e1..edbe96c 100644 --- a/src/science_mode_4/dyscom/dyscom_start.py +++ b/src/science_mode_4/dyscom/dyscom_start.py @@ -11,7 +11,7 @@ class PacketDyscomStart(Packet): def __init__(self): super().__init__() - self._command = Commands.DlStart + self._command = Commands.DL_START class PacketDyscomStartAck(PacketAck): @@ -20,7 +20,7 @@ class PacketDyscomStartAck(PacketAck): def __init__(self, data: bytes): super().__init__(data) - self._command = Commands.DlStartAck + self._command = Commands.DL_START_ACK self._result_error = ResultAndError.NO_ERROR if not data is None: diff --git a/src/science_mode_4/dyscom/dyscom_stop.py b/src/science_mode_4/dyscom/dyscom_stop.py index c4310dc..42b76f9 100644 --- a/src/science_mode_4/dyscom/dyscom_stop.py +++ b/src/science_mode_4/dyscom/dyscom_stop.py @@ -11,7 +11,7 @@ class PacketDyscomStop(Packet): def __init__(self): super().__init__() - self._command = Commands.DlStop + self._command = Commands.DL_STOP class PacketDyscomStopAck(PacketAck): @@ -20,7 +20,7 @@ class PacketDyscomStopAck(PacketAck): def __init__(self, data: bytes): super().__init__(data) - self._command = Commands.DlStopAck + self._command = Commands.DL_STOP_ACK self._result_error = ResultAndError.NO_ERROR if not data is None: diff --git a/src/science_mode_4/dyscom/dyscom_sys.py b/src/science_mode_4/dyscom/dyscom_sys.py index 604ab95..ef834e0 100644 --- a/src/science_mode_4/dyscom/dyscom_sys.py +++ b/src/science_mode_4/dyscom/dyscom_sys.py @@ -23,7 +23,7 @@ class PacketDyscomSys(Packet): def __init__(self, sys_type: DyscomSysType = DyscomSysType.UNDEFINED, filename: str = ""): super().__init__() - self._command = Commands.DlSys + self._command = Commands.DL_SYS self._sys_type = sys_type self._filename = filename @@ -41,7 +41,7 @@ class PacketDyscomSysAck(PacketAck): def __init__(self, data: bytes): super().__init__(data) - self._command = Commands.DlSysAck + self._command = Commands.DL_SYS_ACK self._result_error = ResultAndError.NO_ERROR self._sys_type = DyscomSysType.UNDEFINED self._state = DyscomSysState.UNDEFINED diff --git a/src/science_mode_4/dyscom/dyscom_types.py b/src/science_mode_4/dyscom/dyscom_types.py index b594071..d269283 100644 --- a/src/science_mode_4/dyscom/dyscom_types.py +++ b/src/science_mode_4/dyscom/dyscom_types.py @@ -24,7 +24,7 @@ class DyscomFrequencyOut(IntEnum): class DyscomInitState(IntEnum): """Represent dyscom type for init state""" UNUSED = 0 - SUCESS = 1 + SUCCESS = 1 ERROR_STORAGE_INIT = 2 ERROR_STORAGE_WRITE = 3 ERROR_STORAGE_FULL = 4 @@ -121,8 +121,8 @@ class DyscomFileByNameMode(IntEnum): class DyscomEnergyFlag(IntEnum): """Represents dyscom energy flag type""" UNDEFINED = 0 - MULTIBLOCK = 1 - SINGLEBLOCK = 2 + CABLE_CONNECTED = 1 + DEVICE_IS_LOADING = 2 class DyscomSysType(IntEnum): diff --git a/src/science_mode_4/general/general_device_id.py b/src/science_mode_4/general/general_device_id.py index 1524896..0b5c6f6 100644 --- a/src/science_mode_4/general/general_device_id.py +++ b/src/science_mode_4/general/general_device_id.py @@ -10,7 +10,7 @@ class PacketGeneralGetDeviceId(Packet): def __init__(self): super().__init__() - self._command = Commands.GetDeviceId + self._command = Commands.GET_DEVICE_ID class PacketGeneralGetDeviceIdAck(PacketAck): @@ -19,7 +19,7 @@ class PacketGeneralGetDeviceIdAck(PacketAck): def __init__(self, data: bytes): super().__init__(data) - self._command = Commands.GetDeviceIdAck + self._command = Commands.GET_DEVICE_ID_ACK self._result_error = ResultAndError.NO_ERROR self._device_id = "" diff --git a/src/science_mode_4/general/general_error.py b/src/science_mode_4/general/general_error.py index c8e685c..5a8585e 100644 --- a/src/science_mode_4/general/general_error.py +++ b/src/science_mode_4/general/general_error.py @@ -11,7 +11,7 @@ class PacketGeneralError(PacketAck): def __init__(self, data: bytes): super().__init__(data) - self._command = Commands.GeneralError + self._command = Commands.GENERAL_ERROR self._result_error = ResultAndError.NO_ERROR if not data is None: diff --git a/src/science_mode_4/general/general_layer.py b/src/science_mode_4/general/general_layer.py index 60430f3..eace781 100644 --- a/src/science_mode_4/general/general_layer.py +++ b/src/science_mode_4/general/general_layer.py @@ -3,6 +3,7 @@ from science_mode_4.protocol.packet_number_generator import PacketNumberGenerator from science_mode_4.protocol.packet_factory import PacketFactory from science_mode_4.layer import Layer +from science_mode_4.utils import logger from science_mode_4.utils.packet_buffer import PacketBuffer from .general_reset import PacketGeneralReset, PacketGeneralResetAck from .general_stim_status import GetStimStatusResult, PacketGeneralGetStimStatus, PacketGeneralGetStimStatusAck @@ -40,7 +41,7 @@ def science_mode_version(self) -> str | None: async def initialize(self): - """Calls initialze commands""" + """Calls initialize commands""" await self.get_device_id() await self.get_version() @@ -51,11 +52,13 @@ async def get_device_id(self) -> str: ack: PacketGeneralGetDeviceIdAck = await self._send_packet_and_wait(p) self._check_result_error(ack.result_error, "GetDeviceId") self._device_id = ack.device_id + logger().info("Get device id: %s", ack.device_id) return self._device_id async def reset(self): """Sends reset command and waits for response""" + logger().info("Reset",) p = PacketGeneralReset() # maybe we get no ack because the device resets before sending ack ack: PacketGeneralResetAck = await self._send_packet_and_wait(p) @@ -68,6 +71,7 @@ async def get_stim_status(self) -> GetStimStatusResult: ack: PacketGeneralGetStimStatusAck = await self._send_packet_and_wait(p) if not ack.successful: raise ValueError("Error get stim status") + logger().info("Get stim status: %s, active: %r", ack.stim_status.name, ack.high_voltage_on) return GetStimStatusResult(ack.stim_status, ack.high_voltage_on) @@ -79,4 +83,5 @@ async def get_version(self) -> GetExtendedVersionResult: raise ValueError("Error get extended version") self._firmware_version = ack.firmware_version self._science_mode_version = ack.science_mode_version + logger().info("Get version, firmware version: %s science mode version: %s", ack.firmware_version, ack.science_mode_version) return GetExtendedVersionResult(self._firmware_version, self._science_mode_version) diff --git a/src/science_mode_4/general/general_reset.py b/src/science_mode_4/general/general_reset.py index 8879896..5324f77 100644 --- a/src/science_mode_4/general/general_reset.py +++ b/src/science_mode_4/general/general_reset.py @@ -11,7 +11,7 @@ class PacketGeneralReset(Packet): def __init__(self): super().__init__() - self._command = Commands.Reset + self._command = Commands.RESET class PacketGeneralResetAck(PacketAck): @@ -20,7 +20,7 @@ class PacketGeneralResetAck(PacketAck): def __init__(self, data: bytes): super().__init__(data) - self._command = Commands.ResetAck + self._command = Commands.RESET_ACK self._result_error = ResultAndError.NO_ERROR if not data is None: diff --git a/src/science_mode_4/general/general_stim_status.py b/src/science_mode_4/general/general_stim_status.py index 11ed32b..74e10f7 100644 --- a/src/science_mode_4/general/general_stim_status.py +++ b/src/science_mode_4/general/general_stim_status.py @@ -17,7 +17,7 @@ class PacketGeneralGetStimStatus(Packet): def __init__(self): super().__init__() - self._command = Commands.GetStimStatus + self._command = Commands.GET_STIM_STATUS class PacketGeneralGetStimStatusAck(PacketAck): @@ -26,7 +26,7 @@ class PacketGeneralGetStimStatusAck(PacketAck): def __init__(self, data: bytes): super().__init__(data) - self._command = Commands.GetStimStatusAck + self._command = Commands.GET_STIM_STATUS_ACK self._successful: bool = False self._stim_status = StimStatus.NO_LEVEL_INITIALIZED self._high_voltage_on: bool = False diff --git a/src/science_mode_4/general/general_unknown_command.py b/src/science_mode_4/general/general_unknown_command.py index b431c6b..a7c768b 100644 --- a/src/science_mode_4/general/general_unknown_command.py +++ b/src/science_mode_4/general/general_unknown_command.py @@ -11,7 +11,7 @@ class PacketGeneralUnknownCommand(PacketAck): def __init__(self, data: bytes): super().__init__(data) - self._command = Commands.UnkownCommand + self._command = Commands.UNKNOWN_COMMAND self._result_error = ResultAndError.NO_ERROR if not data is None: diff --git a/src/science_mode_4/general/general_version.py b/src/science_mode_4/general/general_version.py index 8b5adce..f39e3aa 100644 --- a/src/science_mode_4/general/general_version.py +++ b/src/science_mode_4/general/general_version.py @@ -17,7 +17,7 @@ class PacketGeneralGetExtendedVersion(Packet): def __init__(self): super().__init__() - self._command = Commands.GetExtendedVersion + self._command = Commands.GET_EXTENDED_VERSION class PacketGeneralGetExtendedVersionAck(PacketAck): @@ -26,7 +26,7 @@ class PacketGeneralGetExtendedVersionAck(PacketAck): def __init__(self, data: bytes): super().__init__(data) - self._command = Commands.GetExtendedVersionAck + self._command = Commands.GET_EXTENDED_VERSION_ACK self._successful = False self._firmware_version = "" self._science_mode_version = "" diff --git a/src/science_mode_4/low_level/low_level_channel_config.py b/src/science_mode_4/low_level/low_level_channel_config.py index 23b5971..1f9a07d 100644 --- a/src/science_mode_4/low_level/low_level_channel_config.py +++ b/src/science_mode_4/low_level/low_level_channel_config.py @@ -14,7 +14,7 @@ class PacketLowLevelChannelConfig(Packet): def __init__(self): super().__init__() - self._command = Commands.LowLevelChannelConfig + self._command = Commands.LOW_LEVEL_CHANNEL_CONFIG self._execute_stimulation = False self._channel: Channel = Channel.RED self._connector: Connector = Connector.YELLOW @@ -89,7 +89,7 @@ class PacketLowLevelChannelConfigAck(PacketAck): def __init__(self, data: bytes): super().__init__(data) - self._command = Commands.LowLevelChannelConfigAck + self._command = Commands.LOW_LEVEL_CHANNEL_CONFIG_ACK self._result = LowLevelResult.SUCCESSFUL self._connector = 0 self._channel = 0 diff --git a/src/science_mode_4/low_level/low_level_init.py b/src/science_mode_4/low_level/low_level_init.py index 0a18d8a..cb312c1 100644 --- a/src/science_mode_4/low_level/low_level_init.py +++ b/src/science_mode_4/low_level/low_level_init.py @@ -13,7 +13,7 @@ class PacketLowLevelInit(Packet): def __init__(self): super().__init__() - self._command = Commands.LowLevelInit + self._command = Commands.LOW_LEVEL_INIT self._mode = LowLevelMode.NO_MEASUREMENT self._high_voltage_source = LowLevelHighVoltageSource.STANDARD @@ -57,7 +57,7 @@ class PacketLowLevelInitAck(PacketAck): def __init__(self, data: bytes): super().__init__(data) - self._command = Commands.LowLevelInitAck + self._command = Commands.LOW_LEVEL_INIT_ACK self._result_error = ResultAndError.NO_ERROR if not data is None: diff --git a/src/science_mode_4/low_level/low_level_layer.py b/src/science_mode_4/low_level/low_level_layer.py index b68ad6b..404e074 100644 --- a/src/science_mode_4/low_level/low_level_layer.py +++ b/src/science_mode_4/low_level/low_level_layer.py @@ -2,7 +2,7 @@ from science_mode_4.protocol.channel_point import ChannelPoint from science_mode_4.protocol.types import Channel, Connector -from science_mode_4.utils.packet_buffer import PacketBuffer +from science_mode_4.utils.logger import logger from science_mode_4.layer import Layer from .low_level_channel_config import PacketLowLevelChannelConfig from .low_level_init import PacketLowLevelInit, PacketLowLevelInitAck @@ -17,12 +17,6 @@ class LayerLowLevel(Layer): """ - @property - def packet_buffer(self) -> PacketBuffer: - """Getter for packet buffer""" - return self._packet_buffer - - async def init(self, mode: LowLevelMode, high_voltage_source: LowLevelHighVoltageSource): """Send low level init command and waits for response""" p = PacketLowLevelInit() @@ -30,6 +24,7 @@ async def init(self, mode: LowLevelMode, high_voltage_source: LowLevelHighVoltag p.high_voltage_source = high_voltage_source ack: PacketLowLevelInitAck = await self._send_packet_and_wait(p) self._check_result_error(ack.result_error, "LowLevelInit") + logger().info("Low level init") async def stop(self): @@ -37,6 +32,7 @@ async def stop(self): p = PacketLowLevelStop() ack: PacketLowLevelStopAck = await self._send_packet_and_wait(p) self._check_result_error(ack.result_error, "LowLevelStop") + logger().info("Low level stop") def send_init(self, mode: LowLevelMode, high_voltage_source: LowLevelHighVoltageSource): @@ -46,6 +42,7 @@ def send_init(self, mode: LowLevelMode, high_voltage_source: LowLevelHighVoltage p.high_voltage_source = high_voltage_source self._send_packet(p) self._packet_buffer.add_open_acknowledge(p) + logger().info("Low level send init") def send_channel_config(self, execute_stimulation: bool, channel: Channel, @@ -58,6 +55,7 @@ def send_channel_config(self, execute_stimulation: bool, channel: Channel, p.points = points self._send_packet(p) self._packet_buffer.add_open_acknowledge(p) + logger().info("Low level send channel config") def send_stop(self): @@ -65,3 +63,4 @@ def send_stop(self): p = PacketLowLevelStop() self._send_packet(p) self._packet_buffer.add_open_acknowledge(p) + logger().info("Low level send stop") diff --git a/src/science_mode_4/low_level/low_level_stop.py b/src/science_mode_4/low_level/low_level_stop.py index eb77b13..7847b65 100644 --- a/src/science_mode_4/low_level/low_level_stop.py +++ b/src/science_mode_4/low_level/low_level_stop.py @@ -11,7 +11,7 @@ class PacketLowLevelStop(Packet): def __init__(self): super().__init__() - self._command = Commands.LowLevelStop + self._command = Commands.LOW_LEVEL_STOP class PacketLowLevelStopAck(PacketAck): @@ -20,7 +20,7 @@ class PacketLowLevelStopAck(PacketAck): def __init__(self, data: bytes): super().__init__(data) - self._command = Commands.LowLevelStopAck + self._command = Commands.LOW_LEVEL_STOP_ACK self._result_error = ResultAndError.NO_ERROR if not data is None: diff --git a/src/science_mode_4/mid_level/mid_level_current_data.py b/src/science_mode_4/mid_level/mid_level_current_data.py index 121d549..c2e33c5 100644 --- a/src/science_mode_4/mid_level/mid_level_current_data.py +++ b/src/science_mode_4/mid_level/mid_level_current_data.py @@ -12,7 +12,7 @@ class PacketMidLevelGetCurrentData(Packet): def __init__(self): super().__init__() - self._command = Commands.MidLevelGetCurrentData + self._command = Commands.MID_LEVEL_GET_CURRENT_DATA def get_data(self) -> bytes: @@ -27,7 +27,7 @@ class PacketMidLevelGetCurrentDataAck(PacketAck): def __init__(self, data: bytes): super().__init__(data) - self._command = Commands.MidLevelGetCurrentDataAck + self._command = Commands.MID_LEVEL_GET_CURRENT_DATA_ACK self._result_error = ResultAndError.NO_ERROR self._data_selection = 0 self._is_stimulation_active_per_channel = [False] * 8 diff --git a/src/science_mode_4/mid_level/mid_level_init.py b/src/science_mode_4/mid_level/mid_level_init.py index 65d84d2..3f6b2d0 100644 --- a/src/science_mode_4/mid_level/mid_level_init.py +++ b/src/science_mode_4/mid_level/mid_level_init.py @@ -12,7 +12,7 @@ class PacketMidLevelInit(Packet): def __init__(self): super().__init__() - self._command = Commands.MidLevelInit + self._command = Commands.MID_LEVEL_INIT self._do_stop_on_all_errors = False @@ -40,7 +40,7 @@ class PacketMidLevelInitAck(PacketAck): def __init__(self, data: bytes): super().__init__(data) - self._command = Commands.MidLevelInitAck + self._command = Commands.MID_LEVEL_INIT_ACK self._result_error = ResultAndError.NO_ERROR if not data is None: diff --git a/src/science_mode_4/mid_level/mid_level_layer.py b/src/science_mode_4/mid_level/mid_level_layer.py index 36d336f..ed4bed5 100644 --- a/src/science_mode_4/mid_level/mid_level_layer.py +++ b/src/science_mode_4/mid_level/mid_level_layer.py @@ -1,6 +1,7 @@ """Provides mid level layer""" from science_mode_4.layer import Layer +from science_mode_4.utils.logger import logger from .mid_level_current_data import PacketMidLevelGetCurrentData, PacketMidLevelGetCurrentDataAck from .mid_level_stop import PacketMidLevelStop, PacketMidLevelStopAck from .mid_level_update import PacketMidLevelUpdate, PacketMidLevelUpdateAck @@ -18,6 +19,7 @@ async def init(self, do_stop_on_all_errors: bool): p.do_stop_on_all_errors = do_stop_on_all_errors ack: PacketMidLevelInitAck = await self._send_packet_and_wait(p) self._check_result_error(ack.result_error, "MidLevelInit") + logger().info("Mid level init") async def stop(self): @@ -25,6 +27,7 @@ async def stop(self): p = PacketMidLevelStop() ack: PacketMidLevelStopAck = await self._send_packet_and_wait(p) self._check_result_error(ack.result_error, "MidLevelStop") + logger().info("Mid level stop") async def update(self, channel_configuration: list[MidLevelChannelConfiguration]): @@ -33,6 +36,7 @@ async def update(self, channel_configuration: list[MidLevelChannelConfiguration] p.channel_configuration = channel_configuration ack: PacketMidLevelUpdateAck = await self._send_packet_and_wait(p) self._check_result_error(ack.result_error, "MidLevelUpdate") + logger().info("Mid level update") async def get_current_data(self) -> list[bool]: @@ -42,4 +46,5 @@ async def get_current_data(self) -> list[bool]: self._check_result_error(ack.result_error, "MidLevelGetCurrentData") if True in ack.channel_error: raise ValueError(f"Error mid level get current data channel error {ack.channel_error}") + logger().info("Mid level get current data, active channels: %s", ack.is_stimulation_active_per_channel) return ack.is_stimulation_active_per_channel diff --git a/src/science_mode_4/mid_level/mid_level_stop.py b/src/science_mode_4/mid_level/mid_level_stop.py index 8d010a2..baeb23e 100644 --- a/src/science_mode_4/mid_level/mid_level_stop.py +++ b/src/science_mode_4/mid_level/mid_level_stop.py @@ -10,7 +10,7 @@ class PacketMidLevelStop(Packet): def __init__(self): super().__init__() - self._command = Commands.MidLevelStop + self._command = Commands.MID_LEVEL_STOP class PacketMidLevelStopAck(PacketAck): @@ -19,7 +19,7 @@ class PacketMidLevelStopAck(PacketAck): def __init__(self, data: bytes): super().__init__(data) - self._command = Commands.MidLevelStopAck + self._command = Commands.MID_LEVEL_STOP_ACK self._result_error = ResultAndError.NO_ERROR if not data is None: diff --git a/src/science_mode_4/mid_level/mid_level_update.py b/src/science_mode_4/mid_level/mid_level_update.py index cb162b6..6567147 100644 --- a/src/science_mode_4/mid_level/mid_level_update.py +++ b/src/science_mode_4/mid_level/mid_level_update.py @@ -13,7 +13,7 @@ class PacketMidLevelUpdate(Packet): def __init__(self): super().__init__() - self._command = Commands.MidLevelUpdate + self._command = Commands.MID_LEVEL_UPDATE self._channel_configuration: list[MidLevelChannelConfiguration] = [None] * 8 @@ -49,7 +49,7 @@ class PacketMidLevelUpdateAck(PacketAck): def __init__(self, data: bytes): super().__init__(data) - self._command = Commands.MidLevelUpdateAck + self._command = Commands.MID_LEVEL_UPDATE_ACK self._result_error = ResultAndError.NO_ERROR if not data is None: diff --git a/src/science_mode_4/protocol/commands.py b/src/science_mode_4/protocol/commands.py index b4dbd3b..8b6ffc0 100644 --- a/src/science_mode_4/protocol/commands.py +++ b/src/science_mode_4/protocol/commands.py @@ -1,50 +1,55 @@ """Provides all science mode command numbers""" -class Commands: - """Class with all commands""" - - GetDeviceId: int = 52 - GetDeviceIdAck: int = 53 - Reset: int = 58 - ResetAck: int = 59 - GetStimStatus: int = 62 - GetStimStatusAck: int = 63 - GetExtendedVersion: int = 68 - GetExtendedVersionAck: int = 69 - GeneralError: int = 66 - UnkownCommand: int = 67 - - LowLevelInit: int = 0 - LowLevelInitAck: int = 1 - LowLevelChannelConfig: int = 2 - LowLevelChannelConfigAck: int = 3 - LowLevelStop: int = 4 - LowLevelStopAck: int = 5 - - MidLevelInit: int = 30 - MidLevelInitAck: int = 31 - MidLevelUpdate: int = 32 - MidLevelUpdateAck: int = 33 - MidLevelStop: int = 34 - MidLevelStopAck: int = 35 - MidLevelGetCurrentData: int = 36 - MidLevelGetCurrentDataAck: int = 37 - - DlInit: int = 100 - DlInitAck: int = 101 - DlStart: int = 102 - DlStartAck: int = 103 - DlStop: int = 104 - DlStopAck: int = 105 - DlGet: int = 109 - DlGetAck: int = 110 - DlPowerModule: int = 111 - DlPowerModuleAck: int = 112 - DlSys: int = 114 - DlSysAck: int = 115 - - DlSendLiveData: int = 106 - DlSendFile: int = 107 - DlSendFileAck: int = 113 - DlMmi: int = 108 +from enum import IntEnum + + +class Commands(IntEnum): + """Enum with all commands""" + + UNDEFINED = -1 + + GET_DEVICE_ID = 52 + GET_DEVICE_ID_ACK = 53 + RESET = 58 + RESET_ACK = 59 + GET_STIM_STATUS = 62 + GET_STIM_STATUS_ACK = 63 + GET_EXTENDED_VERSION = 68 + GET_EXTENDED_VERSION_ACK = 69 + GENERAL_ERROR = 66 + UNKNOWN_COMMAND = 67 + + LOW_LEVEL_INIT = 0 + LOW_LEVEL_INIT_ACK = 1 + LOW_LEVEL_CHANNEL_CONFIG = 2 + LOW_LEVEL_CHANNEL_CONFIG_ACK = 3 + LOW_LEVEL_STOP = 4 + LOW_LEVEL_STOP_ACK = 5 + + MID_LEVEL_INIT = 30 + MID_LEVEL_INIT_ACK = 31 + MID_LEVEL_UPDATE = 32 + MID_LEVEL_UPDATE_ACK = 33 + MID_LEVEL_STOP = 34 + MID_LEVEL_STOP_ACK = 35 + MID_LEVEL_GET_CURRENT_DATA = 36 + MID_LEVEL_GET_CURRENT_DATA_ACK = 37 + + DL_INIT = 100 + DL_INIT_ACK = 101 + DL_START = 102 + DL_START_ACK = 103 + DL_STOP = 104 + DL_STOP_ACK = 105 + DL_GET = 109 + DL_GET_ACK = 110 + DL_POWER_MODULE = 111 + DL_POWER_MODULE_ACK = 112 + DL_SYS = 114 + DL_SYS_ACK = 115 + + DL_SEND_LIVE_DATA = 106 + DL_SEND_FILE = 107 + DL_SEND_FILE_ACK = 113 + DL_MMI = 108 diff --git a/src/science_mode_4/protocol/packet.py b/src/science_mode_4/protocol/packet.py index a3c7d34..77b2daf 100644 --- a/src/science_mode_4/protocol/packet.py +++ b/src/science_mode_4/protocol/packet.py @@ -1,18 +1,21 @@ """Provides base class for packets""" +from .commands import Commands + + class Packet(): """Base class for all packets""" def __init__(self): - self._command = -1 + self._command = Commands.UNDEFINED self._kind = -1 self._number = 0 @property - def command(self) -> int: + def command(self) -> Commands: """Getter for command""" return self._command @@ -46,11 +49,11 @@ def create_copy(self) -> "Packet": def __repr__(self) -> str: - return f"command {self._command} - nr {self._number}" + return f"command: {self._command.name}, nr {self._number}" def __str__(self) -> str: - return f"command {self._command} - nr {self._number}" + return f"command: {self._command.name}, nr {self._number}" class PacketAck(Packet): diff --git a/src/science_mode_4/protocol/packet_factory.py b/src/science_mode_4/protocol/packet_factory.py index 9147e3d..01f0863 100644 --- a/src/science_mode_4/protocol/packet_factory.py +++ b/src/science_mode_4/protocol/packet_factory.py @@ -1,5 +1,6 @@ -"""Provides a paket factory class""" +"""Provides a packet factory class""" +from science_mode_4.utils.logger import logger from .packet import Packet, PacketAck @@ -12,28 +13,12 @@ def __init__(self): # keys for dict: command, kind, packet class self.data: dict[int, int, Packet] = {} # register all subclasses of Packet (exclude Packet and PacketAck, because these are base classes) - self.handle_class(Packet) - - - def handle_class(self, cls: type[Packet]): - """Register all subclasses from cls""" - for x in cls.__subclasses__(): - # print(f"Handle type {x.__name__}") - - # ignore PacketAck - if x is not PacketAck: - if issubclass(x, PacketAck): - self.register_packet(x(None)) - else: - self.register_packet(x()) - - # handle subclasses of this class - self.handle_class(x) + self._handle_class(Packet) def register_packet(self, packet: Packet): """Register a packet""" - # print(f"Register type {packet.__class__.__name__} command {packet.command} kind {packet.kind}") + logger().debug("Register packet: %s", packet.__class__.__name__) self.data[packet.command, packet.kind] = packet @@ -54,4 +39,19 @@ def create_packet_with_data(self, command: int, number: int, data: bytes) -> Pac proto = self.data[command, kind] copy = proto.create_copy_with_data(data) copy.number = number + logger().debug("Read package, %s", copy) return copy + + + def _handle_class(self, cls: type[Packet]): + """Register all subclasses from cls""" + for x in cls.__subclasses__(): + # ignore PacketAck + if x is not PacketAck: + if issubclass(x, PacketAck): + self.register_packet(x(None)) + else: + self.register_packet(x()) + + # handle subclasses of this class + self._handle_class(x) diff --git a/src/science_mode_4/protocol/protocol.py b/src/science_mode_4/protocol/protocol.py index a9d4de3..b60540d 100644 --- a/src/science_mode_4/protocol/protocol.py +++ b/src/science_mode_4/protocol/protocol.py @@ -2,6 +2,7 @@ from science_mode_4.utils.byte_builder import ByteBuilder from science_mode_4.utils.crc16 import Crc16 +from science_mode_4.utils import logger from .packet import Packet @@ -22,7 +23,7 @@ def packet_to_bytes(packet: Packet) -> bytes: # command and packet number bb.set_bit_to_position(packet.command, 0, 10) bb.set_bit_to_position(packet.number, 10, 6) - # swap command and packet number to ensure little endianess + # swap command and packet number to ensure little endianness bb.swap(0, 2) # append packet data bb.append_bytes(packet.get_data()) @@ -46,7 +47,8 @@ def packet_to_bytes(packet: Packet) -> bytes: # stop byte bb.append_byte(Protocol.STOP_BYTE) - # print(f"Outgoing {bb}") + logger().debug("Build package, %s", packet) + logger().debug("Outgoing data, %s", bb) result = bb.get_bytes() return bytes(result) diff --git a/src/science_mode_4/protocol/protocol_helper.py b/src/science_mode_4/protocol/protocol_helper.py index 9b27d1a..2d11658 100644 --- a/src/science_mode_4/protocol/protocol_helper.py +++ b/src/science_mode_4/protocol/protocol_helper.py @@ -20,7 +20,6 @@ def send_packet(packet: Packet, packet_number: int, packet_buffer: PacketBuffer) packet.number = packet_number packet_buffer.add_open_acknowledge(packet) - # print(f"O {packet}") packet_buffer.connection.write(Protocol.packet_to_bytes(packet)) @@ -46,14 +45,14 @@ async def send_packet_and_wait(packet: Packet, packet_number: int, packet_buffer return ack # check if we got an error - if ack.command == Commands.GeneralError: + if ack.command == Commands.GENERAL_ERROR: ge: PacketGeneralError = ack - raise ValueError(f"General error packet {ge.error}") - if ack.command == Commands.UnkownCommand: + raise ValueError(f"General error packet {ge.result_error.name}") + if ack.command == Commands.UNKNOWN_COMMAND: uc: PacketGeneralUnknownCommand = ack - raise ValueError(f"Unknown command packet {uc.error}") + raise ValueError(f"Unknown command packet {uc.result_error.name}") - # discard ackowledge and continue + # discard acknowledge and continue # no acknowledge arrived, sleep and check again break diff --git a/src/science_mode_4/protocol/types.py b/src/science_mode_4/protocol/types.py index 8dd1af3..c875406 100644 --- a/src/science_mode_4/protocol/types.py +++ b/src/science_mode_4/protocol/types.py @@ -4,7 +4,7 @@ class Channel(IntEnum): - """Representa a channel""" + """Represents a channel""" RED = 0 BLUE = 1 BLACK = 2 @@ -12,13 +12,13 @@ class Channel(IntEnum): class Connector(IntEnum): - """Representa a cable connector""" + """Represents a cable connector""" YELLOW = 0 GREEN = 1 class ResultAndError(IntEnum): - """Represent science mode type ResultAndError""" + """Represents science mode type ResultAndError""" NO_ERROR = 0 TRANSFER_ERROR = 1 PARAMETER_ERROR = 2 diff --git a/src/science_mode_4/utils/__init__.py b/src/science_mode_4/utils/__init__.py index 75a3c3d..83bc6f6 100644 --- a/src/science_mode_4/utils/__init__.py +++ b/src/science_mode_4/utils/__init__.py @@ -4,6 +4,7 @@ from .byte_builder import * from .connection import * from .crc16 import * +from .logger import * from .null_connection import * from .packet_buffer import * from .serial_port_connection import * diff --git a/src/science_mode_4/utils/byte_builder.py b/src/science_mode_4/utils/byte_builder.py index 7f65693..2d7f906 100644 --- a/src/science_mode_4/utils/byte_builder.py +++ b/src/science_mode_4/utils/byte_builder.py @@ -8,14 +8,14 @@ class ByteBuilder(): def __init__(self, data: int = 0, byte_count: int = 0): - self.data = BitVector.init_from_int(data, byte_count * 8) + self._data = BitVector.init_from_int(data, byte_count * 8) def get_bit_from_position(self, bit_position: int, bit_count: int) -> int: """Returns bits starting with bit_position and a count of bit_count""" result = 0 for x in range(bit_count): - result |= (self.data[bit_position + x] << x) + result |= (self._data[bit_position + x] << x) return result @@ -47,7 +47,7 @@ def append_bytes(self, value: bytes): def extend_byte_builder(self, value: "ByteBuilder"): """Extends current data with value""" - self.data.extend(value.get_bytes()) + self._data.extend(value.get_bytes()) def set_bit_to_position(self, value: int, bit_position: int, bit_count: int): @@ -55,10 +55,10 @@ def set_bit_to_position(self, value: int, bit_position: int, bit_count: int): Set bits starting with bit_position and a count of bit_count to value This method extends data to make room for value """ - new_length = max(len(self.data), bit_position + bit_count) - self.data.set_length(new_length) + new_length = max(len(self._data), bit_position + bit_count) + self._data.set_length(new_length) for x in range(bit_count): - self.data[bit_position + x] = (value >> x) & 0x1 + self._data[bit_position + x] = (value >> x) & 0x1 def set_bytes_to_position(self, value: bytes, byte_position: int, byte_count: int): @@ -79,27 +79,31 @@ def swap(self, start: int, count: int): def get_bytes(self) -> bytes: """Returns data as bytes""" - return self.data.get_bytes() + return self._data.get_bytes() def clear(self): """Resets data""" - self.data = BitVector() + self._data = BitVector() + + + def __len__(self) -> int: + return (len(self._data) + 7) // 8 def __repr__(self) -> str: b = self.get_bytes() - return f"{len(b)} - {b.hex(" ").upper()}" + return f"length: {len(b)}, bytes: {b.hex(" ").upper()}" def __str__(self) -> str: b = self.get_bytes() - return f"{len(b)} - {b.hex(" ").upper()}" + return f"length: {len(b)}, bytes: {b.hex(" ").upper()}" def _append_byte(self, value: int): """Append value at the end of data, value is treated as byte""" - start = len(self.data) - self.data.set_length(start + 8) + start = len(self._data) + self._data.set_length(start + 8) for x in range(8): - self.data[start + x] = (value >> x) & 0x1 + self._data[start + x] = (value >> x) & 0x1 diff --git a/src/science_mode_4/utils/logger.py b/src/science_mode_4/utils/logger.py new file mode 100644 index 0000000..fe7828b --- /dev/null +++ b/src/science_mode_4/utils/logger.py @@ -0,0 +1,42 @@ +"""Provides a logger""" + +import logging + + +class Logger: + """Singleton for custom logger. By default a StreamHandler with a custom formatter is added + and log level set to INFO""" + + _instance = None + + + def __new__(cls): + if cls._instance is None: + cls._instance = super(Logger, cls).__new__(cls) + # because __init__ would be called every time we use Logger, initialize everything here + cls._logger = logging.getLogger("science_mode_4") + cls._console_handler = None + cls._initialize(cls) + return cls._instance + + + @property + def logger(self) -> logging.Logger: + """Getter for logger""" + return self._logger + + + def _initialize(self): + self._logger.setLevel(logging.INFO) + + formatter = logging.Formatter("{asctime} - {levelname} - {message}", style="{", datefmt="%Y-%m-%d %H:%M") + + self._console_handler = logging.StreamHandler() + self._console_handler.setFormatter(formatter) + self._logger.addHandler(self._console_handler) + + + +def logger() -> logging.Logger: + """Shortcut to access custom logger""" + return Logger().logger diff --git a/src/science_mode_4/utils/packet_buffer.py b/src/science_mode_4/utils/packet_buffer.py index c63aa7a..96c455b 100644 --- a/src/science_mode_4/utils/packet_buffer.py +++ b/src/science_mode_4/utils/packet_buffer.py @@ -4,6 +4,7 @@ from science_mode_4.protocol.packet_factory import PacketFactory from science_mode_4.protocol.protocol import Protocol from science_mode_4.protocol.commands import Commands +from science_mode_4.utils.logger import logger from .connection import Connection @@ -12,6 +13,7 @@ class PacketBuffer(): def __init__(self, conn: Connection, packet_factory: PacketFactory): self._buffer: bytes = b"" + # dict with command and packet number as key and open count as value self._open_acknowledges: dict[tuple[int, int], int] = {} self._connection = conn self._packet_factory = packet_factory @@ -54,8 +56,10 @@ def remove_open_acknowledge(self, packet: Packet): def print_open_acknowledge(self): """Print open acknowledges""" + logger().info("Open acknowledges") for key, value in self._open_acknowledges.items(): - print(f"{key} {value}") + if value != 0: + logger().info("Command: %s, number: %d, value: %d", Commands(key[0]).name, key[1], value) def get_packet_from_buffer(self, do_update_buffer = True) -> Packet | None: @@ -75,8 +79,8 @@ def get_packet_from_buffer(self, do_update_buffer = True) -> Packet | None: key = ack_data[0], ack_data[1] wait_ack = self._open_acknowledges.get(key) if wait_ack is None: - if ack_data[0] not in [Commands.DlSendLiveData]: - print(f"Unexpected acknowledge command {ack_data[0]}, number {ack_data[1]}") + if ack_data[0] not in [Commands.DL_SEND_LIVE_DATA]: + logger().warning("Unexpected acknowledge command: %s, number: %d", Commands(ack_data[0]).name, ack_data[1]) else: self._open_acknowledges[ack_data[0], ack_data[1]] -= 1 diff --git a/src/science_mode_4/utils/serial_port_connection.py b/src/science_mode_4/utils/serial_port_connection.py index 044f134..3f75cc2 100644 --- a/src/science_mode_4/utils/serial_port_connection.py +++ b/src/science_mode_4/utils/serial_port_connection.py @@ -3,7 +3,10 @@ import serial import serial.tools.list_ports import serial.tools.list_ports_common + from .connection import Connection +from .logger import logger + class SerialPortConnection(Connection): """Serial connection class""" @@ -51,7 +54,7 @@ def read(self) -> bytes: result = [] if self._ser.in_waiting > 0: result = self._ser.read_all() - # print(f"Incoming {len(result)} - {result.hex(" ").upper()}") + logger().debug("Incoming data, length: %d, bytes: %s", len(result), result.hex(" ").upper()) return bytes(result)