From 1b7f322b22a0a3bc959955b1e105e50fec9f82da Mon Sep 17 00:00:00 2001 From: Pariterre Date: Fri, 19 Jan 2024 14:23:28 -0500 Subject: [PATCH 1/6] Moved rehastim devices in a dedicated folder --- Examples/motomed_example.py | 2 +- Examples/motomed_stim_example.py | 2 +- examples/rehastim2_example.py | 2 +- examples/rehastimp24_example.py | 2 +- pyScienceMode/__init__.py | 4 +- .../rehastim2.py} | 44 ++++++- .../rehastimP24.py} | 111 ++++++++++++---- .../rehastim_generic.py} | 121 ++++-------------- 8 files changed, 155 insertions(+), 133 deletions(-) rename pyScienceMode/{rehastim2_interface.py => devices/rehastim2.py} (91%) rename pyScienceMode/{rehastimP24_interface.py => devices/rehastimP24.py} (84%) rename pyScienceMode/{sciencemode.py => devices/rehastim_generic.py} (83%) diff --git a/Examples/motomed_example.py b/Examples/motomed_example.py index 6d6d560..57237b8 100644 --- a/Examples/motomed_example.py +++ b/Examples/motomed_example.py @@ -1,5 +1,5 @@ import time -from pyScienceMode import Rehastim2 +from pyScienceMode.devices.rehastim2 import Rehastim2 port = "/dev/ttyUSB0" # Enter the port on which the stimulator is connected diff --git a/Examples/motomed_stim_example.py b/Examples/motomed_stim_example.py index 1c206cf..cc58fe2 100644 --- a/Examples/motomed_stim_example.py +++ b/Examples/motomed_stim_example.py @@ -1,5 +1,5 @@ import time -from pyScienceMode import Rehastim2 as St +from pyScienceMode.devices.rehastim2 import Rehastim2 as St from pyScienceMode import Channel as Ch from pyScienceMode import Modes, Device diff --git a/examples/rehastim2_example.py b/examples/rehastim2_example.py index 874e9f6..d4c4ac9 100644 --- a/examples/rehastim2_example.py +++ b/examples/rehastim2_example.py @@ -1,5 +1,5 @@ # Import Stimulator class -from pyScienceMode import Rehastim2 as St +from pyScienceMode.devices.rehastim2 import Rehastim2 as St from pyScienceMode import Modes, Device # Import Channel class diff --git a/examples/rehastimp24_example.py b/examples/rehastimp24_example.py index 3d2268e..3237496 100644 --- a/examples/rehastimp24_example.py +++ b/examples/rehastimp24_example.py @@ -1,5 +1,5 @@ from pyScienceMode import Channel, Point, Device, Modes -from pyScienceMode import RehastimP24 as St +from pyScienceMode.devices.rehastimP24 import RehastimP24 as St """ This example shows how to use the RehastimP24 device. diff --git a/pyScienceMode/__init__.py b/pyScienceMode/__init__.py index dc26e77..0b43aae 100644 --- a/pyScienceMode/__init__.py +++ b/pyScienceMode/__init__.py @@ -1,8 +1,6 @@ from .motomed_interface import _Motomed -from .sciencemode import RehastimGeneric +from .devices.rehastim_generic import RehastimGeneric from . import utils -from .rehastim2_interface import Rehastim2 -from .rehastimP24_interface import RehastimP24 from . import acks from .channel import Channel, Point from .enums import Rehastim2Commands, RehastimP24Commands, Modes, Device diff --git a/pyScienceMode/rehastim2_interface.py b/pyScienceMode/devices/rehastim2.py similarity index 91% rename from pyScienceMode/rehastim2_interface.py rename to pyScienceMode/devices/rehastim2.py index 7e80a9e..30d8a19 100644 --- a/pyScienceMode/rehastim2_interface.py +++ b/pyScienceMode/devices/rehastim2.py @@ -3,15 +3,17 @@ See ScienceMode2 - Description and protocol for more information. """ from typing import Tuple +import serial import time -from .acks import ( + +from ..acks import ( stop_stimulation_ack, start_stimulation_ack, init_stimulation_ack, get_mode_ack, rehastim_error, ) -from .utils import ( +from ..utils import ( signed_int, check_stimulation_interval, check_inter_pulse_interval, @@ -21,10 +23,10 @@ calc_electrode_number, packet_construction, ) -from .sciencemode import RehastimGeneric -from .motomed_interface import _Motomed -from .enums import Device -from .channel import Channel +from .rehastim_generic import RehastimGeneric +from ..motomed_interface import _Motomed +from ..enums import Device +from ..channel import Channel class Rehastim2(RehastimGeneric): @@ -97,6 +99,22 @@ def set_stimulation_signal(self, list_channels: list): self.mode.append(list_channels[i].get_mode()) self.given_channels.append(list_channels[i].get_no_channel()) + def check_serial_port(self): + raise RuntimeError("Checking the serial port is not supported for Rehastim2.") + + def close_port(self): + self.port.close() + + def _setup_device(self): + self.port = serial.Serial( + self.port_name, + self.BAUD_RATE, + bytesize=serial.EIGHTBITS, + parity=serial.PARITY_EVEN, + stopbits=serial.STOPBITS_ONE, + timeout=0.1, + ) + def _send_packet(self, cmd: str) -> str: """ Calls the methode that construct the packet according to the command. @@ -175,6 +193,20 @@ def _packet_init_stimulation(self) -> bytes: packet = packet_construction(self.packet_count, "InitChannelListMode", data_stimulation) return packet + def _get_last_device_ack(self): + """ + Get the last ack received by the device. + """ + while 1: + packet = self._read_packet() + if packet and len(packet) != 0: + break + if packet and not self.error_occured: + if self.show_log and packet[-1][6] in [t.value for t in self.Rehastim2Commands]: + print(f"Ack received by rehastim: {self.Rehastim2Commands(packet[-1][6]).name}") + self.ack_received.append(packet[-1]) + return packet[-1] + def _packet_start_stimulation(self) -> bytes: """ Returns the packet for the StartChannelListMode. diff --git a/pyScienceMode/rehastimP24_interface.py b/pyScienceMode/devices/rehastimP24.py similarity index 84% rename from pyScienceMode/rehastimP24_interface.py rename to pyScienceMode/devices/rehastimP24.py index 9da1f8e..e3afef8 100644 --- a/pyScienceMode/rehastimP24_interface.py +++ b/pyScienceMode/devices/rehastimP24.py @@ -1,14 +1,11 @@ import time -from .utils import ( - check_unique_channel, - calc_electrode_number, - generic_error_check, - check_list_channel_order, -) -from .sciencemode import RehastimGeneric + from sciencemode import sciencemode -from .enums import Device, HighVoltage, StimStatus -from .channel import Point, Channel + +from ..utils import check_unique_channel, calc_electrode_number, generic_error_check, check_list_channel_order +from .rehastim_generic import RehastimGeneric +from ..enums import Device, HighVoltage, StimStatus +from ..channel import Point, Channel class RehastimP24(RehastimGeneric): @@ -59,7 +56,7 @@ def get_extended_version(self) -> tuple: Microcontroller version. """ extended_version_ack = sciencemode.ffi.new("Smpt_get_extended_version_ack*") - packet_number = self.get_next_packet_number() + packet_number = self._get_next_packet_number() sciencemode.lib.smpt_send_get_extended_version(self.device, packet_number) if self.show_log is True: print( @@ -82,7 +79,7 @@ def get_device_id(self) -> str: Device id. """ device_id_ack = sciencemode.ffi.new("Smpt_get_device_id_ack*") - packet_number = self.get_next_packet_number() + packet_number = self._get_next_packet_number() sciencemode.lib.smpt_send_get_device_id(self.device, packet_number) if self.show_log is True: @@ -107,7 +104,7 @@ def get_stim_status(self) -> tuple: """ stim_status_ack = sciencemode.ffi.new("Smpt_get_stim_status_ack*") - packet_number = self.get_next_packet_number() + packet_number = self._get_next_packet_number() sciencemode.lib.smpt_send_get_stim_status(self.device, packet_number) if self.show_log is True: @@ -132,7 +129,7 @@ def get_battery_status(self) -> tuple: Battery voltage. """ battery_status_ack = sciencemode.ffi.new("Smpt_get_battery_status_ack*") - packet_number = self.get_next_packet_number() + packet_number = self._get_next_packet_number() sciencemode.lib.smpt_send_get_battery_status(self.device, packet_number) if self.show_log is True: @@ -156,7 +153,7 @@ def get_main_status(self): Main status. """ main_status_ack = sciencemode.ffi.new("Smpt_get_main_status_ack*") - packet_number = self.get_next_packet_number() + packet_number = self._get_next_packet_number() sciencemode.lib.smpt_send_get_main_status(self.device, packet_number) if self.show_log is True: @@ -171,13 +168,16 @@ def reset(self): """ Reset the device. General Level command. """ - packet_number = self.get_next_packet_number() + packet_number = self._get_next_packet_number() ret = sciencemode.lib.smpt_send_reset(self.device, packet_number) if self.show_log is True: print("Command sent to rehastim:", self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Reset).name) self._get_last_ack() + def close_port(self): + sciencemode.lib.smpt_close_serial_port(self.device) + def get_all(self): """ Get all the device information. General Level command. @@ -196,6 +196,62 @@ def get_all(self): main_status_success, ) + def _setup_device(self): + self.device = sciencemode.ffi.new("Smpt_device*") + self.com = sciencemode.ffi.new("char[]", self.port_name.encode()) + self.cmd = sciencemode.ffi.new("Smpt_cmd*") + self.ack = sciencemode.ffi.new("Smpt_ack*") + self.ml_get_current_data_ack = sciencemode.ffi.new("Smpt_ml_get_current_data_ack*") + self.ll_channel_config_ack = sciencemode.ffi.new("Smpt_ll_channel_config_ack*") + self.ll_init_ack = sciencemode.ffi.new("Smpt_ll_init_ack*") + self.ml_update = sciencemode.ffi.new("Smpt_ml_update*") + + if not self.check_serial_port(): + raise RuntimeError(f"Failed to access port {self.port_name}.") + + if not self._open_serial_port(): + raise RuntimeError(f"Unable to open port {self.port_name}.") + + def check_serial_port(self): + ret = sciencemode.lib.smpt_check_serial_port(self.com) + if self.show_log: + print(f"Port check for {self.port_name} : {'successful' if ret else 'unsuccessful'}") + return ret + + def _open_serial_port(self): + """ + Try to open the serial port.Used for the RehastimP24 + """ + ret = sciencemode.lib.smpt_open_serial_port(self.device, self.com) + if self.show_log: + print(f"Open {self.port_name} : {'successful' if ret else 'unsuccessful'}") + return ret + + def _get_next_packet_number(self): + """ + Get the next packet to send another command. Used for the RehastimP24 + """ + if hasattr(self, "device") and self.device is not None: + packet_number = sciencemode.lib.smpt_packet_number_generator_next(self.device) + return packet_number + + def _get_current_data(self): + """ + Retrieve current data from the rehastimP24 mid level stimulation. + """ + ml_get_current_data = sciencemode.ffi.new("Smpt_ml_get_current_data*") + ml_get_current_data.data_selection = sciencemode.lib.Smpt_Ml_Data_Channels + ml_get_current_data.packet_number = self.get_next_packet_number() + + ret = sciencemode.lib.smpt_send_ml_get_current_data(self.device, ml_get_current_data) + if not ret: + print("Failed to get current data.") + if self.show_log is True: + print( + "Command sent to rehastim:", + self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Ml_Get_Current_Data).name, + ) + @staticmethod def _channel_number_to_channel_connector(no_channel): """ @@ -232,6 +288,17 @@ def _channel_number_to_channel_connector(no_channel): # Low level commands + def _get_last_device_ack(self): + """ + Get the last ack received by the device. + """ + while not sciencemode.lib.smpt_new_packet_received(self.device): + time.sleep(0.005) + ret = sciencemode.lib.smpt_last_ack(self.device, self.ack) + if self.show_log is True: + print("Ack received by rehastimP24: ", self.RehastimP24Commands(self.ack.command_number).name) + return ret + def ll_init(self): """ Initialize the lower level of the device. The low-level is used for defining a custom shaped pulse. @@ -242,7 +309,7 @@ def ll_init(self): ll_init.high_voltage_level = ( sciencemode.lib.Smpt_High_Voltage_Default ) # This switches on the high voltage source - ll_init.packet_number = self.get_next_packet_number() + ll_init.packet_number = self._get_next_packet_number() if not sciencemode.lib.smpt_send_ll_init(self.device, ll_init): raise RuntimeError("Low level initialization failed.") @@ -251,7 +318,7 @@ def ll_init(self): "Command sent to rehastim: {}".format(self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Ll_Init).name), ) - self.get_next_packet_number() + self._get_next_packet_number() self._get_last_ack() self.check_ll_init_ack() @@ -339,7 +406,7 @@ def start_stim_one_channel_stimulation( ) for _ in range(stim_sequence): - ll_config.packet_number = self.get_next_packet_number() + ll_config.packet_number = self._get_next_packet_number() sciencemode.lib.smpt_send_ll_channel_config(self.device, ll_config) if self.show_log is True: print( @@ -387,7 +454,7 @@ def end_stim_one_channel(self): """ Stop the device lower level. """ - packet_number = self.get_next_packet_number() + packet_number = self._get_next_packet_number() if not sciencemode.lib.smpt_send_ll_stop(self.device, packet_number): raise RuntimeError("Low level stop failed.") self.log( @@ -425,7 +492,7 @@ def init_stimulation(self, list_channels: list, stop_all_on_error: bool = True): ml_init = sciencemode.ffi.new("Smpt_ml_init*") ml_init.stop_all_channels_on_error = stop_all_on_error - ml_init.packet_number = self.get_next_packet_number() + ml_init.packet_number = self._get_next_packet_number() if not sciencemode.lib.smpt_send_ml_init(self.device, ml_init): raise RuntimeError("Failed to start stimulation") @@ -464,7 +531,7 @@ def start_stimulation(self, upd_list_channels: list, stimulation_duration: int | self.list_channels = upd_list_channels self._safety = safety self._current_stim_duration = stimulation_duration - self.ml_update.packet_number = self.get_next_packet_number() + self.ml_update.packet_number = self._get_next_packet_number() for channel in upd_list_channels: if safety and not channel.is_pulse_symmetric(): @@ -554,7 +621,7 @@ def end_stimulation(self): """ Stop the mid level stimulation. """ - packet_number = self.get_next_packet_number() + packet_number = self._get_next_packet_number() if not sciencemode.lib.smpt_send_ml_stop(self.device, packet_number): raise RuntimeError("Failure to stop stimulation.") diff --git a/pyScienceMode/sciencemode.py b/pyScienceMode/devices/rehastim_generic.py similarity index 83% rename from pyScienceMode/sciencemode.py rename to pyScienceMode/devices/rehastim_generic.py index 2184b60..4588aff 100644 --- a/pyScienceMode/sciencemode.py +++ b/pyScienceMode/devices/rehastim_generic.py @@ -3,14 +3,14 @@ See ScienceMode2 - Description and protocol for more information. """ +from abc import ABC, abstractmethod import threading -import serial import time import numpy as np -from .utils import packet_construction, signed_int -from .acks import ( +from ..utils import packet_construction, signed_int +from ..acks import ( motomed_error_ack, rehastim_error, init_stimulation_ack, @@ -18,15 +18,13 @@ stop_stimulation_ack, start_stimulation_ack, ) -from .enums import Rehastim2Commands, RehastimP24Commands, Device - -from sciencemode import sciencemode +from ..enums import Rehastim2Commands, RehastimP24Commands, Device # Notes : # This code needs to be used in parallel with the "ScienceMode2 - Description and protocol" document -class RehastimGeneric: +class RehastimGeneric(ABC): """ Class used for the sciencemode communication protocol. @@ -81,33 +79,7 @@ def __init__( """ self.device_type = device_type self.port_name = port - if self.device_type == Device.Rehastim2.value: - self.port = serial.Serial( - port, - self.BAUD_RATE, - bytesize=serial.EIGHTBITS, - parity=serial.PARITY_EVEN, - stopbits=serial.STOPBITS_ONE, - timeout=0.1, - ) - - elif self.device_type == Device.Rehastimp24.value: - self.device = sciencemode.ffi.new("Smpt_device*") - self.com = sciencemode.ffi.new("char[]", self.port_name.encode()) - self.cmd = sciencemode.ffi.new("Smpt_cmd*") - self.ack = sciencemode.ffi.new("Smpt_ack*") - self.ml_get_current_data_ack = sciencemode.ffi.new("Smpt_ml_get_current_data_ack*") - self.ll_channel_config_ack = sciencemode.ffi.new("Smpt_ll_channel_config_ack*") - self.ll_init_ack = sciencemode.ffi.new("Smpt_ll_init_ack*") - self.ml_update = sciencemode.ffi.new("Smpt_ml_update*") - - if not self.check_serial_port(): - raise RuntimeError(f"Failed to access port {self.port_name}.") - - if not self.open_serial_port(): - raise RuntimeError(f"Unable to open port {self.port_name}.") - else: - raise ValueError("Device type not recognized") + self._setup_device() self.port_open = True self.time_last_cmd = 0 @@ -146,32 +118,6 @@ def __init__( if self.reha_connected and not self.__comparison_thread_started: self._start_thread_catch_ack() - def check_serial_port(self): - """ - Verify if the serial port is available and functional. Used for the RehastimP24 - """ - ret = sciencemode.lib.smpt_check_serial_port(self.com) - if self.show_log: - print(f"Port check for {self.port_name} : {'successful' if ret else 'unsuccessful'}") - return ret - - def open_serial_port(self): - """ - Try to open the serial port.Used for the RehastimP24 - """ - ret = sciencemode.lib.smpt_open_serial_port(self.device, self.com) - if self.show_log: - print(f"Open {self.port_name} : {'successful' if ret else 'unsuccessful'}") - return ret - - def get_next_packet_number(self): - """ - Get the next packet to send another command. Used for the RehastimP24 - """ - if hasattr(self, "device") and self.device is not None: - packet_number = sciencemode.lib.smpt_packet_number_generator_next(self.device) - return packet_number - def log(self, status_msg: str, full_msg: str = None): """ Log messages based on the show_log mode. @@ -185,23 +131,9 @@ def log(self, status_msg: str, full_msg: str = None): if self.show_log is True or self.show_log == "Status": print(status_msg) - def _get_current_data(self): - """ - Retrieve current data from the rehastimP24 mid level stimulation. - """ - ml_get_current_data = sciencemode.ffi.new("Smpt_ml_get_current_data*") - if self.device_type == Device.Rehastimp24.value: - ml_get_current_data.data_selection = sciencemode.lib.Smpt_Ml_Data_Channels - ml_get_current_data.packet_number = self.get_next_packet_number() - - ret = sciencemode.lib.smpt_send_ml_get_current_data(self.device, ml_get_current_data) - if not ret: - print("Failed to get current data.") - if self.show_log is True: - print( - "Command sent to rehastim:", - self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Ml_Get_Current_Data).name, - ) + @abstractmethod + def _setup_device(self): + """Setup device specific parameters.""" def _get_last_ack(self, init: bool = False) -> bytes: """ @@ -233,23 +165,13 @@ def _get_last_ack(self, init: bool = False) -> bytes: self.last_ack = None return last_ack - if self.device_type == Device.Rehastimp24.value: - while not sciencemode.lib.smpt_new_packet_received(self.device): - time.sleep(0.005) - ret = sciencemode.lib.smpt_last_ack(self.device, self.ack) - if self.show_log is True: - print("Ack received by rehastimP24: ", self.RehastimP24Commands(self.ack.command_number).name) - return ret - elif self.device_type == Device.Rehastim2.value: - while 1: - packet = self._read_packet() - if packet and len(packet) != 0: - break - if packet and not self.error_occured: - if self.show_log and packet[-1][6] in [t.value for t in self.Rehastim2Commands]: - print(f"Ack received by rehastim: {self.Rehastim2Commands(packet[-1][6]).name}") - self.ack_received.append(packet[-1]) - return packet[-1] + return self._get_last_device_ack() + + @abstractmethod + def _get_last_device_ack(self): + """ + Get the last ack received by the device. + """ def _return_list_ack_received(self) -> list: """ @@ -465,14 +387,17 @@ def _init_ack(packet_count: int) -> bytes: packet = packet_construction(packet_count, "InitAck", [0]) return packet + @abstractmethod + def check_serial_port(self): + """ + Verify if the serial port is available and functional. Used for the RehastimP24 + """ + + @abstractmethod def close_port(self): """ Closes the port. """ - if self.device_type == Device.Rehastimp24.value: - sciencemode.lib.smpt_close_serial_port(self.device) - elif self.device_type == Device.Rehastim2.value: - self.port.close() def _read_packet(self) -> list: """ From 4cf11691799add46cb962bd03332036c26eb1087 Mon Sep 17 00:00:00 2001 From: Pariterre Date: Fri, 19 Jan 2024 16:25:45 -0500 Subject: [PATCH 2/6] Added start_stimulation in the API --- docs/rehastim2_example.md | 2 +- examples/rehastim2_example.py | 2 +- pyScienceMode/devices/rehastim2.py | 16 +++------------- pyScienceMode/devices/rehastimP24.py | 17 +++-------------- pyScienceMode/devices/rehastim_generic.py | 17 +++++++++++++++++ 5 files changed, 25 insertions(+), 29 deletions(-) diff --git a/docs/rehastim2_example.md b/docs/rehastim2_example.md index e41a4ef..87d5f7d 100644 --- a/docs/rehastim2_example.md +++ b/docs/rehastim2_example.md @@ -107,7 +107,7 @@ After a disconnection, init_channel must be called. """ stimulator.init_channel(stimulation_interval=15, list_channels=list_channels) -stimulator.start_stimulation(2, list_channels) +stimulator.start_stimulation(stimulation_duration=2, upd_list_channels=list_channels) stimulator.end_stimulation() diff --git a/examples/rehastim2_example.py b/examples/rehastim2_example.py index d4c4ac9..386f12c 100644 --- a/examples/rehastim2_example.py +++ b/examples/rehastim2_example.py @@ -103,7 +103,7 @@ """ stimulator.init_channel(stimulation_interval=15, list_channels=list_channels) -stimulator.start_stimulation(2, list_channels) +stimulator.start_stimulation(stimulation_duration=2, upd_list_channels=list_channels) stimulator.end_stimulation() diff --git a/pyScienceMode/devices/rehastim2.py b/pyScienceMode/devices/rehastim2.py index 30d8a19..82399c3 100644 --- a/pyScienceMode/devices/rehastim2.py +++ b/pyScienceMode/devices/rehastim2.py @@ -340,19 +340,9 @@ def init_channel( self._send_packet("InitChannelListMode") self._get_last_ack() - def start_stimulation(self, stimulation_duration: float = None, upd_list_channels: list = None): - """ - Update a stimulation. - Warning: only the channel that has been initiated can be updated. - - Parameters - ---------- - stimulation_duration: float - Time of the stimulation after the update. - upd_list_channels: list[channel] - List of the channels that will be updated - """ - + def start_stimulation( + self, stimulation_duration: float = None, upd_list_channels: list = None, safety: bool = False + ): if upd_list_channels is not None: new_electrode_number = calc_electrode_number(upd_list_channels) diff --git a/pyScienceMode/devices/rehastimP24.py b/pyScienceMode/devices/rehastimP24.py index e3afef8..689a4f3 100644 --- a/pyScienceMode/devices/rehastimP24.py +++ b/pyScienceMode/devices/rehastimP24.py @@ -502,20 +502,9 @@ def init_stimulation(self, list_channels: list, stop_all_on_error: bool = True): ) self._get_last_ack() - def start_stimulation(self, upd_list_channels: list, stimulation_duration: int | float = None, safety: bool = True): - """ - Start the mid level stimulation on the device. - - Parameters - ---------- - stimulation_duration : int | float - Duration of the stimulation in seconds. - upd_list_channels : list - Channels to stimulate. - safety : bool - Set to True if you want to check the pulse symmetry. False otherwise. - """ - + def start_stimulation( + self, stimulation_duration: float = None, upd_list_channels: list = None, safety: bool = True + ): if not stimulation_duration: raise ValueError("Please indicate the stimulation duration") elif not isinstance(stimulation_duration, int | float): diff --git a/pyScienceMode/devices/rehastim_generic.py b/pyScienceMode/devices/rehastim_generic.py index 4588aff..39c20ed 100644 --- a/pyScienceMode/devices/rehastim_generic.py +++ b/pyScienceMode/devices/rehastim_generic.py @@ -476,6 +476,23 @@ def _packet_watchdog(self) -> bytes: packet = packet_construction(self.packet_count, "Watchdog") return packet + @abstractmethod + def start_stimulation( + self, stimulation_duration: float = None, upd_list_channels: list = None, safety: bool = True + ): + """ + Start the stimulation. + + Parameters + ---------- + stimulation_duration : float + Duration of the stimulation. + upd_list_channels : list + List of channels to update. + safety : bool + If True, the stimulation will stop if the stimulation duration is reached. + """ + def get_angle(self) -> float: """ Returns the angle of the Rehastim. From 068c896b94abf316661a2c039f9e1622732db822 Mon Sep 17 00:00:00 2001 From: Pariterre Date: Mon, 22 Jan 2024 14:50:01 -0500 Subject: [PATCH 3/6] Augmented the abstraction and left some todo --- pyScienceMode/devices/rehastim2.py | 5 +++-- pyScienceMode/devices/rehastimP24.py | 27 ++++++++++++++--------- pyScienceMode/devices/rehastim_generic.py | 12 ++++++++++ 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/pyScienceMode/devices/rehastim2.py b/pyScienceMode/devices/rehastim2.py index 82399c3..a51bf10 100644 --- a/pyScienceMode/devices/rehastim2.py +++ b/pyScienceMode/devices/rehastim2.py @@ -343,6 +343,7 @@ def init_channel( def start_stimulation( self, stimulation_duration: float = None, upd_list_channels: list = None, safety: bool = False ): + # TODO: This should be factored out to rehastim_generic.py if upd_list_channels is not None: new_electrode_number = calc_electrode_number(upd_list_channels) @@ -352,12 +353,12 @@ def start_stimulation( self.list_channels = upd_list_channels self.set_stimulation_signal(self.list_channels) self._send_packet("StartChannelListMode") - time_start_stim = time.time() self._get_last_ack() - self.stimulation_active = True + self.stimulation_active = True # BUG: This is false at the exit of the function if stimulation_duration is set if stimulation_duration is not None: + time_start_stim = time.time() if stimulation_duration < time.time() - time_start_stim: raise RuntimeError("Asked stimulation duration too short") time.sleep(stimulation_duration - (time.time() - time_start_stim)) diff --git a/pyScienceMode/devices/rehastimP24.py b/pyScienceMode/devices/rehastimP24.py index 689a4f3..5b01e7f 100644 --- a/pyScienceMode/devices/rehastimP24.py +++ b/pyScienceMode/devices/rehastimP24.py @@ -36,7 +36,7 @@ def __init__(self, port: str, show_log: bool | str = False): self._current_no_channel = None self._current_stim_sequence = None self._current_pulse_interval = None - self._current_stim_duration = None + self._current_stim_duration = None # TODO: This variable seems to be unused. self.device_type = Device.Rehastimp24.value self._safety = True @@ -505,11 +505,13 @@ def init_stimulation(self, list_channels: list, stop_all_on_error: bool = True): def start_stimulation( self, stimulation_duration: float = None, upd_list_channels: list = None, safety: bool = True ): + # TODO: Why isn't it allowed to stimulate for as long as we want (like in the Rehastim2)? if not stimulation_duration: raise ValueError("Please indicate the stimulation duration") elif not isinstance(stimulation_duration, int | float): raise TypeError("Please provide a int or float type for stimulation duration") + # TODO: This should be factored into rehastim_generic.py if upd_list_channels is not None: new_electrode_number = calc_electrode_number(upd_list_channels) if new_electrode_number != self.electrode_number: @@ -522,6 +524,7 @@ def start_stimulation( self._current_stim_duration = stimulation_duration self.ml_update.packet_number = self._get_next_packet_number() + # TODO: This should be factored into rehastim_generic.py for channel in upd_list_channels: if safety and not channel.is_pulse_symmetric(): raise ValueError( @@ -537,16 +540,17 @@ def start_stimulation( "Or specify specific stimulation points.".format(channel._no_channel) ) self._send_stimulation_update() + self.stimulation_started = True # BUG: This is not true as the stimulation is paused at that point. - start_time = time.time() - while (time.time() - start_time) < stimulation_duration: - self._get_current_data() - self._get_last_ack() - self.check_stimulation_errors() - time.sleep(0.005) - - self.pause_stimulation() - self.stimulation_started = True + # TODO: This should be factored into rehastim_generic.py + if stimulation_duration is not None: + start_time = time.time() # BUG: time.time() is not precise enough for this purpose, use time.perf_counter() + while (time.time() - start_time) < stimulation_duration: + self._get_current_data() + self._get_last_ack() + self.check_stimulation_errors() + time.sleep(0.005) + self.pause_stimulation() def pause_stimulation(self): """ @@ -601,6 +605,7 @@ def update_stimulation(self, upd_list_channels: list, stimulation_duration: int stimulation_duration : int | float Duration of the updated stimulation in seconds. """ + # TODO: This method is useless as _current_stim_duration is never used. if stimulation_duration is not None: self._current_stim_duration = stimulation_duration @@ -610,6 +615,8 @@ def end_stimulation(self): """ Stop the mid level stimulation. """ + self.pause_stimulation() # Make sure the stimulation is paused before stopping it. + packet_number = self._get_next_packet_number() if not sciencemode.lib.smpt_send_ml_stop(self.device, packet_number): diff --git a/pyScienceMode/devices/rehastim_generic.py b/pyScienceMode/devices/rehastim_generic.py index 39c20ed..7f8bf7d 100644 --- a/pyScienceMode/devices/rehastim_generic.py +++ b/pyScienceMode/devices/rehastim_generic.py @@ -493,6 +493,18 @@ def start_stimulation( If True, the stimulation will stop if the stimulation duration is reached. """ + @abstractmethod + def pause_stimulation(self): + """ + Pause the stimulation. + """ + + @abstractmethod + def end_stimulation(self): + """ + End the stimulation. + """ + def get_angle(self) -> float: """ Returns the angle of the Rehastim. From f74095887b56e5506ccf205761341fd373e0a9f6 Mon Sep 17 00:00:00 2001 From: Pariterre Date: Tue, 23 Jan 2024 10:49:30 -0500 Subject: [PATCH 4/6] Used a proper logger --- Examples/motomed_example.py | 8 +++- Examples/motomed_stim_example.py | 16 ++++--- examples/perf_test.py | 10 +++-- examples/rehastimp24_example.py | 7 ++- pyScienceMode/__init__.py | 1 + pyScienceMode/devices/rehastim2.py | 2 +- pyScienceMode/devices/rehastimP24.py | 53 ++++++++++++----------- pyScienceMode/devices/rehastim_generic.py | 15 ++++--- pyScienceMode/logger.py | 16 +++++++ pyScienceMode/motomed_interface.py | 11 +++-- pyScienceMode/utils.py | 10 +++-- tests/test_update_parameter_rehastim2.py | 1 - 12 files changed, 99 insertions(+), 51 deletions(-) create mode 100644 pyScienceMode/logger.py diff --git a/Examples/motomed_example.py b/Examples/motomed_example.py index 57237b8..82969ef 100644 --- a/Examples/motomed_example.py +++ b/Examples/motomed_example.py @@ -1,14 +1,18 @@ +import logging import time + from pyScienceMode.devices.rehastim2 import Rehastim2 +logger = logging.getLogger("pyScienceMode") + port = "/dev/ttyUSB0" # Enter the port on which the stimulator is connected motomed = Rehastim2(port, show_log=True, with_motomed=True).motomed motomed.init_phase_training(arm_training=True) -print(motomed.get_motomed_mode()) +logger.info(motomed.get_motomed_mode()) motomed.start_phase(speed=50, gear=5, active=False, go_forward=False, spasm_detection=True) time.sleep(2) motomed.set_gear(10) time.sleep(200) motomed.stop_training() -print(motomed.get_phase_result()) +logger.info(motomed.get_phase_result()) diff --git a/Examples/motomed_stim_example.py b/Examples/motomed_stim_example.py index cc58fe2..b034de5 100644 --- a/Examples/motomed_stim_example.py +++ b/Examples/motomed_stim_example.py @@ -1,8 +1,12 @@ +import logging import time + from pyScienceMode.devices.rehastim2 import Rehastim2 as St from pyScienceMode import Channel as Ch from pyScienceMode import Modes, Device +logger = logging.getLogger("pyScienceMode") + def init_rehastim(): # Create a list of channels @@ -58,8 +62,8 @@ def init_rehastim(): if (10 <= angle_crank < 20 or 180 <= angle_crank < 220) and (tric_delt_stim or bic_delt_stim): stimulator.pause_stimulation() tric_delt_stim, bic_delt_stim = False, False - print("angle crank", angle_crank) - print("stimulation_state", (tric_delt_stim or bic_delt_stim)) + logger.info(f"angle crank {angle_crank}") + logger.info(f"stimulation_state {tric_delt_stim or bic_delt_stim}") if 20 <= angle_crank < 180 and not tric_delt_stim: list_channels[0].set_amplitude(0) @@ -69,8 +73,8 @@ def init_rehastim(): stimulator.start_stimulation(upd_list_channels=list_channels) tric_delt_stim = True bic_delt_stim = False - print("angle crank", angle_crank) - print("stimulation_state", tric_delt_stim) + logger.info(f"angle crank {angle_crank}") + logger.info(f"stimulation_state {tric_delt_stim}") if (220 <= angle_crank < 360 or 0 <= angle_crank < 10) and not bic_delt_stim: list_channels[0].set_amplitude(15) @@ -80,7 +84,7 @@ def init_rehastim(): stimulator.start_stimulation(upd_list_channels=list_channels) bic_delt_stim = True tric_delt_stim = False - print("angle crank", angle_crank) - print("stimulation_state", bic_delt_stim) + logger.info(f"angle crank {angle_crank}") + logger.info(f"stimulation_state {bic_delt_stim}") time.sleep(0.01) diff --git a/examples/perf_test.py b/examples/perf_test.py index 1449d34..a4bf407 100644 --- a/examples/perf_test.py +++ b/examples/perf_test.py @@ -1,3 +1,5 @@ +import logging + from pyScienceMode import Channel, Point, Device, Modes from pyScienceMode import RehastimP24 as St from pyScienceMode import Rehastim2 as St2 @@ -7,6 +9,8 @@ from biosiglive import ViconClient, DeviceType import numpy as np +logger = logging.getLogger("pyScienceMode") + """ This file is used to test the performance of both devices (RehastimP24 and Rehastim2). The tests are done with Vicon Nexus and the biosiglive library. @@ -397,7 +401,7 @@ def diff_frequency_ll_ml(frequency): for point in channel_1.list_point: list_points.append(point) - print(point.pulse_width, point.amplitude) + logger.info(point.pulse_width, point.amplitude) sleep(2) stimulatorp24.start_stim_one_channel_stimulation( no_channel=1, points=list_points, stim_sequence=100, pulse_interval=1000 / frequency @@ -434,7 +438,7 @@ def communication_speed_P24(): sciencemode.lib.smpt_send_ll_channel_config(stimulatorp24.device, ll_config) sleep(waiting_time) waiting_time *= 0.9 - print(waiting_time) + logger.info(waiting_time) def limit_parameters(device: Device): @@ -481,7 +485,7 @@ def communication_speed_r2(): channel_1.set_amplitude(amplitude) sleep(waiting_time) waiting_time *= 0.9 - print(waiting_time) + logger.info(waiting_time) def decalage(freq1, freq2, freq3, device: Device): diff --git a/examples/rehastimp24_example.py b/examples/rehastimp24_example.py index 3237496..7085270 100644 --- a/examples/rehastimp24_example.py +++ b/examples/rehastimp24_example.py @@ -1,6 +1,11 @@ +import logging + from pyScienceMode import Channel, Point, Device, Modes from pyScienceMode.devices.rehastimP24 import RehastimP24 as St +logger = logging.getLogger("pyScienceMode") + + """ This example shows how to use the RehastimP24 device. There are several commands divided into three levels (general, low and mid). @@ -61,7 +66,7 @@ """ # stimulator.get_battery_status() -print(stimulator.get_stim_status()) +logger.info(stimulator.get_stim_status()) # stimulator.get_main_status() # stimulator.get_all() # stimulator.reset() diff --git a/pyScienceMode/__init__.py b/pyScienceMode/__init__.py index 0b43aae..5009874 100644 --- a/pyScienceMode/__init__.py +++ b/pyScienceMode/__init__.py @@ -1,3 +1,4 @@ +from .logger import logger from .motomed_interface import _Motomed from .devices.rehastim_generic import RehastimGeneric from . import utils diff --git a/pyScienceMode/devices/rehastim2.py b/pyScienceMode/devices/rehastim2.py index a51bf10..e9e155b 100644 --- a/pyScienceMode/devices/rehastim2.py +++ b/pyScienceMode/devices/rehastim2.py @@ -203,7 +203,7 @@ def _get_last_device_ack(self): break if packet and not self.error_occured: if self.show_log and packet[-1][6] in [t.value for t in self.Rehastim2Commands]: - print(f"Ack received by rehastim: {self.Rehastim2Commands(packet[-1][6]).name}") + self.log(f"Ack received by rehastim: {self.Rehastim2Commands(packet[-1][6]).name}") self.ack_received.append(packet[-1]) return packet[-1] diff --git a/pyScienceMode/devices/rehastimP24.py b/pyScienceMode/devices/rehastimP24.py index 5b01e7f..9686c39 100644 --- a/pyScienceMode/devices/rehastimP24.py +++ b/pyScienceMode/devices/rehastimP24.py @@ -59,9 +59,8 @@ def get_extended_version(self) -> tuple: packet_number = self._get_next_packet_number() sciencemode.lib.smpt_send_get_extended_version(self.device, packet_number) if self.show_log is True: - print( - "Command sent to rehastim:", - self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Get_Extended_Version).name, + self.log( + f"Command sent to rehastim: {self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Get_Extended_Version).name}", ) self._get_last_ack() ret = sciencemode.lib.smpt_get_get_extended_version_ack(self.device, extended_version_ack) @@ -83,7 +82,9 @@ def get_device_id(self) -> str: sciencemode.lib.smpt_send_get_device_id(self.device, packet_number) if self.show_log is True: - print("Command sent to rehastim:", self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Get_Device_Id).name) + self.log( + f"Command sent to rehastim: {self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Get_Device_Id).name}" + ) self._get_last_ack() ret = sciencemode.lib.smpt_get_get_device_id_ack(self.device, device_id_ack) @@ -108,7 +109,9 @@ def get_stim_status(self) -> tuple: sciencemode.lib.smpt_send_get_stim_status(self.device, packet_number) if self.show_log is True: - print("Command sent to rehastim:", self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Get_Stim_Status).name) + self.log( + f"Command sent to rehastim: {self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Get_Stim_Status).name}" + ) self._get_last_ack() ret = sciencemode.lib.smpt_get_get_stim_status_ack(self.device, stim_status_ack) @@ -133,8 +136,8 @@ def get_battery_status(self) -> tuple: sciencemode.lib.smpt_send_get_battery_status(self.device, packet_number) if self.show_log is True: - print( - "Command sent to rehastim:", self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Get_Battery_Status).name + self.log( + f"Command sent to rehastim: {self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Get_Battery_Status).name}" ) self._get_last_ack() @@ -157,7 +160,9 @@ def get_main_status(self): sciencemode.lib.smpt_send_get_main_status(self.device, packet_number) if self.show_log is True: - print("Command sent to rehastim:", self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Get_Main_Status).name) + self.log( + f"Command sent to rehastim: {self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Get_Main_Status).name}" + ) self._get_last_ack() ret = sciencemode.lib.smpt_get_get_main_status_ack(self.device, main_status_ack) @@ -172,7 +177,7 @@ def reset(self): ret = sciencemode.lib.smpt_send_reset(self.device, packet_number) if self.show_log is True: - print("Command sent to rehastim:", self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Reset).name) + self.log(f"Command sent to rehastim: {self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Reset).name}") self._get_last_ack() def close_port(self): @@ -215,7 +220,7 @@ def _setup_device(self): def check_serial_port(self): ret = sciencemode.lib.smpt_check_serial_port(self.com) if self.show_log: - print(f"Port check for {self.port_name} : {'successful' if ret else 'unsuccessful'}") + self.log(f"Port check for {self.port_name} : {'successful' if ret else 'unsuccessful'}") return ret def _open_serial_port(self): @@ -224,7 +229,7 @@ def _open_serial_port(self): """ ret = sciencemode.lib.smpt_open_serial_port(self.device, self.com) if self.show_log: - print(f"Open {self.port_name} : {'successful' if ret else 'unsuccessful'}") + self.log(f"Open {self.port_name} : {'successful' if ret else 'unsuccessful'}") return ret def _get_next_packet_number(self): @@ -245,11 +250,10 @@ def _get_current_data(self): ret = sciencemode.lib.smpt_send_ml_get_current_data(self.device, ml_get_current_data) if not ret: - print("Failed to get current data.") + self.log("Failed to get current data.") if self.show_log is True: - print( - "Command sent to rehastim:", - self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Ml_Get_Current_Data).name, + self.log( + f"Command sent to rehastim: {self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Ml_Get_Current_Data).name}" ) @staticmethod @@ -296,7 +300,7 @@ def _get_last_device_ack(self): time.sleep(0.005) ret = sciencemode.lib.smpt_last_ack(self.device, self.ack) if self.show_log is True: - print("Ack received by rehastimP24: ", self.RehastimP24Commands(self.ack.command_number).name) + self.log(f"Ack received by rehastimP24: {self.RehastimP24Commands(self.ack.command_number).name}") return ret def ll_init(self): @@ -315,7 +319,7 @@ def ll_init(self): raise RuntimeError("Low level initialization failed.") self.log( "Low level initialized", - "Command sent to rehastim: {}".format(self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Ll_Init).name), + f"Command sent to rehastim: {self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Ll_Init).name}", ) self._get_next_packet_number() @@ -409,9 +413,8 @@ def start_stim_one_channel_stimulation( ll_config.packet_number = self._get_next_packet_number() sciencemode.lib.smpt_send_ll_channel_config(self.device, ll_config) if self.show_log is True: - print( - "Command sent to rehastim:", - self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Ll_Channel_Config).name, + self.log( + f"Command sent to rehastim: {self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Ll_Channel_Config).name}", ) time.sleep(pulse_interval / 1000) self._get_last_ack() @@ -459,7 +462,7 @@ def end_stim_one_channel(self): raise RuntimeError("Low level stop failed.") self.log( "Low level stopped", - "Command sent to rehastim: {}".format(self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Ll_Stop).name), + f"Command sent to rehastim: {self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Ll_Stop).name}", ) self._get_last_ack() @@ -498,7 +501,7 @@ def init_stimulation(self, list_channels: list, stop_all_on_error: bool = True): raise RuntimeError("Failed to start stimulation") self.log( "Stimulation initialized", - "Command sent to rehastim: {}".format(self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Ml_Init).name), + f"Command sent to rehastim: {self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Ml_Init).name}", ) self._get_last_ack() @@ -590,7 +593,7 @@ def _send_stimulation_update(self): raise RuntimeError("Failed to send stimulation update") self.log( "Stimulation started", - "Command sent to rehastim: {}".format(self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Ml_Update).name), + f"Command sent to rehastim: {self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Ml_Update).name}", ) self._get_last_ack() @@ -616,14 +619,14 @@ def end_stimulation(self): Stop the mid level stimulation. """ self.pause_stimulation() # Make sure the stimulation is paused before stopping it. - + packet_number = self._get_next_packet_number() if not sciencemode.lib.smpt_send_ml_stop(self.device, packet_number): raise RuntimeError("Failure to stop stimulation.") self.log( "Stimulation stopped", - "Command sent to rehastim: {}".format(self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Ml_Stop).name), + f"Command sent to rehastim: {self.RehastimP24Commands(sciencemode.lib.Smpt_Cmd_Ml_Stop).name}", ) self._get_last_ack() self.stimulation_started = False diff --git a/pyScienceMode/devices/rehastim_generic.py b/pyScienceMode/devices/rehastim_generic.py index 7f8bf7d..094a53b 100644 --- a/pyScienceMode/devices/rehastim_generic.py +++ b/pyScienceMode/devices/rehastim_generic.py @@ -4,6 +4,7 @@ """ from abc import ABC, abstractmethod +import logging import threading import time @@ -23,6 +24,8 @@ # Notes : # This code needs to be used in parallel with the "ScienceMode2 - Description and protocol" document +logger = logging.getLogger("pyScienceMode") + class RehastimGeneric(ABC): """ @@ -127,9 +130,9 @@ def log(self, status_msg: str, full_msg: str = None): - full_msg: The additional message to show when show_log is True. """ if self.show_log is True and full_msg: - print(full_msg) + logger.info(full_msg) if self.show_log is True or self.show_log == "Status": - print(status_msg) + logger.info(status_msg) @abstractmethod def _setup_device(self): @@ -209,7 +212,7 @@ def _thread_catch_ack(self): And retrieve the data sent by the motomed if motomed flag is true. """ - print("thread started") + self.log("Thread 'catch ack' started") time_to_sleep = 0.005 while self.stimulation_active and self.device_type == Device.Rehastim2.value: tic = time.time() @@ -226,9 +229,9 @@ def _thread_catch_ack(self): if self.Rehastim2Commands(packet[6]).name == "MotomedError": ack = motomed_error_ack(signed_int(packet[7:8])) if signed_int(packet[7:8]) in [-4, -6]: - print(f"Ack received by rehastim: {ack}") + self.log(f"Ack received by rehastim: {ack}") elif self.Rehastim2Commands(packet[6]).name != "ActualValues": - print(f"Ack received by rehastim: {self.Rehastim2Commands(packet[6]).name}") + self.log(f"Ack received by rehastim: {self.Rehastim2Commands(packet[6]).name}") if packet[6] == self.Rehastim2Commands["ActualValues"].value: self._actual_values_ack(packet) elif packet[6] == Rehastim2Commands["PhaseResult"].value: @@ -352,7 +355,7 @@ def send_generic_packet(self, cmd: str, packet: bytes) -> (None, str): if self.show_log: if self.Rehastim2Commands(packet[6]).name != "Watchdog": - print(f"Command sent to Rehastim : {self.Rehastim2Commands(packet[6]).name}") + self.log(f"Command sent to Rehastim : {self.Rehastim2Commands(packet[6]).name}") self.command_send.append(packet) with self.lock: diff --git a/pyScienceMode/logger.py b/pyScienceMode/logger.py new file mode 100644 index 0000000..5d015e7 --- /dev/null +++ b/pyScienceMode/logger.py @@ -0,0 +1,16 @@ +import logging +import logging.config + +logging_config = { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "output": {"format": "%(asctime)s - %(name)s.%(levelname)s:\t%(message)s"}, + }, + "datefmt": "%Y-%m-%d %H:%M:%S", + "handlers": {"console": {"class": "logging.StreamHandler", "formatter": "output", "stream": "ext://sys.stdout"}}, + "loggers": { + "pyScienceMode": {"handlers": ["console"], "level": logging.DEBUG}, + }, +} +logging.config.dictConfig(logging_config) diff --git a/pyScienceMode/motomed_interface.py b/pyScienceMode/motomed_interface.py index ed349af..931e290 100644 --- a/pyScienceMode/motomed_interface.py +++ b/pyScienceMode/motomed_interface.py @@ -3,6 +3,11 @@ See ScienceMode2 - Description and protocol for more information. """ +import logging +from time import sleep +import numpy as np + + from .acks import ( get_motomed_mode_ack, init_phase_training_ack, @@ -21,8 +26,8 @@ from .enums import Rehastim2Commands from .utils import packet_construction, signed_int -from time import sleep -import numpy as np + +logger = logging.getLogger("pyScienceMode") class _Motomed: @@ -251,7 +256,7 @@ def _stop_phase_training(self): stop_phase_ack = self._calling_ack(self.rehastim._get_last_ack()) self.is_phase_training = False if stop_phase_ack == "PhaseResult": - print("Result of the phase available.") + logger.info("Result of the phase available.") elif stop_phase_ack != "Stop phase training sent to MOTOmed": raise RuntimeError("Error starting phase : " + str(stop_phase_ack)) diff --git a/pyScienceMode/utils.py b/pyScienceMode/utils.py index 0f3a097..f027f8f 100644 --- a/pyScienceMode/utils.py +++ b/pyScienceMode/utils.py @@ -1,6 +1,10 @@ +import logging + import crccheck from .enums import ErrorCode, Rehastim2Commands +logger = logging.getLogger("pyScienceMode") + """ This code provides utility functions for working with the Rehastim device, including packet construction and data validation. It also includes functions to convert and check various parameters used in Rehastim communication. @@ -84,9 +88,9 @@ def check_unique_channel(list_channels: list = None) -> bool: active_channel = [] for i in range(len(list_channels)): if list_channels[i].get_no_channel() in active_channel: - print( - "Warning : 2 channel no%s" % list_channels[i].get_no_channel() - + " in list_channels given. The first one given will be used." + logger.info( + f"Warning : 2 channel no{list_channels[i].get_no_channel()} in list_channels given. " + "The first one given will be used." ) list_channels.pop(i) return False diff --git a/tests/test_update_parameter_rehastim2.py b/tests/test_update_parameter_rehastim2.py index 31cacce..fbd2b46 100644 --- a/tests/test_update_parameter_rehastim2.py +++ b/tests/test_update_parameter_rehastim2.py @@ -72,7 +72,6 @@ def test_update_frequency(port, frequency): ) list_channels.append(channel_1) stimulator.init_channel(list_channels=list_channels, stimulation_interval=round(1 / frequency * 1000)) - print(stimulator.stimulation_interval) assert stimulator.stimulation_interval == round(1 / frequency * 1000) stimulator.disconnect() stimulator.close_port() From cc2fcfd7baa2594d5266915a33787f5ad787974d Mon Sep 17 00:00:00 2001 From: Pariterre Date: Tue, 23 Jan 2024 11:14:00 -0500 Subject: [PATCH 5/6] Cleaned the imports --- Examples/motomed_example.py | 3 +-- Examples/motomed_stim_example.py | 6 +----- examples/perf_test.py | 13 +++++-------- examples/rehastim2_example.py | 9 +++------ examples/rehastimp24_example.py | 6 +----- pyScienceMode/devices/rehastim_generic.py | 4 +--- pyScienceMode/logger.py | 1 + pyScienceMode/motomed_interface.py | 9 +-------- pyScienceMode/utils.py | 4 +--- tests/test_raise_error_rehastim2.py | 3 ++- tests/test_raise_error_rehastimP24.py | 5 +++-- tests/test_update_parameter_rehastim2.py | 3 ++- tests/test_update_parameter_rehastimp24.py | 3 ++- 13 files changed, 24 insertions(+), 45 deletions(-) diff --git a/Examples/motomed_example.py b/Examples/motomed_example.py index 82969ef..57e9f60 100644 --- a/Examples/motomed_example.py +++ b/Examples/motomed_example.py @@ -1,9 +1,8 @@ -import logging import time +from pyScienceMode import logger from pyScienceMode.devices.rehastim2 import Rehastim2 -logger = logging.getLogger("pyScienceMode") port = "/dev/ttyUSB0" # Enter the port on which the stimulator is connected diff --git a/Examples/motomed_stim_example.py b/Examples/motomed_stim_example.py index b034de5..979713c 100644 --- a/Examples/motomed_stim_example.py +++ b/Examples/motomed_stim_example.py @@ -1,11 +1,7 @@ -import logging import time +from pyScienceMode import Channel as Ch, Modes, Device, logger from pyScienceMode.devices.rehastim2 import Rehastim2 as St -from pyScienceMode import Channel as Ch -from pyScienceMode import Modes, Device - -logger = logging.getLogger("pyScienceMode") def init_rehastim(): diff --git a/examples/perf_test.py b/examples/perf_test.py index a4bf407..8c61538 100644 --- a/examples/perf_test.py +++ b/examples/perf_test.py @@ -1,15 +1,12 @@ -import logging - -from pyScienceMode import Channel, Point, Device, Modes -from pyScienceMode import RehastimP24 as St -from pyScienceMode import Rehastim2 as St2 +from biosiglive import ViconClient, DeviceType +from pyScienceMode import Channel, Point, Device, Modes, logger +from pyScienceMode.devices.rehastim2 import Rehastim2 as St2 +from pyScienceMode.devices.rehastimP24 import RehastimP24 as St import random -from time import sleep from sciencemode import sciencemode -from biosiglive import ViconClient, DeviceType +from time import sleep import numpy as np -logger = logging.getLogger("pyScienceMode") """ This file is used to test the performance of both devices (RehastimP24 and Rehastim2). diff --git a/examples/rehastim2_example.py b/examples/rehastim2_example.py index 386f12c..495502f 100644 --- a/examples/rehastim2_example.py +++ b/examples/rehastim2_example.py @@ -1,11 +1,8 @@ -# Import Stimulator class -from pyScienceMode.devices.rehastim2 import Rehastim2 as St -from pyScienceMode import Modes, Device - -# Import Channel class -from pyScienceMode import Channel as Ch from time import sleep +from pyScienceMode import Modes, Device, Channel as Ch +from pyScienceMode.devices.rehastim2 import Rehastim2 as St + # Create a list of channels list_channels = [] diff --git a/examples/rehastimp24_example.py b/examples/rehastimp24_example.py index 7085270..dbaf8cb 100644 --- a/examples/rehastimp24_example.py +++ b/examples/rehastimp24_example.py @@ -1,10 +1,6 @@ -import logging - -from pyScienceMode import Channel, Point, Device, Modes +from pyScienceMode import Channel, Point, Device, Modes, logger from pyScienceMode.devices.rehastimP24 import RehastimP24 as St -logger = logging.getLogger("pyScienceMode") - """ This example shows how to use the RehastimP24 device. diff --git a/pyScienceMode/devices/rehastim_generic.py b/pyScienceMode/devices/rehastim_generic.py index 094a53b..9e10c19 100644 --- a/pyScienceMode/devices/rehastim_generic.py +++ b/pyScienceMode/devices/rehastim_generic.py @@ -4,7 +4,6 @@ """ from abc import ABC, abstractmethod -import logging import threading import time @@ -20,12 +19,11 @@ start_stimulation_ack, ) from ..enums import Rehastim2Commands, RehastimP24Commands, Device +from ..logger import logger # Notes : # This code needs to be used in parallel with the "ScienceMode2 - Description and protocol" document -logger = logging.getLogger("pyScienceMode") - class RehastimGeneric(ABC): """ diff --git a/pyScienceMode/logger.py b/pyScienceMode/logger.py index 5d015e7..00739f7 100644 --- a/pyScienceMode/logger.py +++ b/pyScienceMode/logger.py @@ -14,3 +14,4 @@ }, } logging.config.dictConfig(logging_config) +logger = logging.getLogger("pyScienceMode") diff --git a/pyScienceMode/motomed_interface.py b/pyScienceMode/motomed_interface.py index 931e290..4e736c3 100644 --- a/pyScienceMode/motomed_interface.py +++ b/pyScienceMode/motomed_interface.py @@ -3,11 +3,6 @@ See ScienceMode2 - Description and protocol for more information. """ -import logging -from time import sleep -import numpy as np - - from .acks import ( get_motomed_mode_ack, init_phase_training_ack, @@ -25,9 +20,7 @@ ) from .enums import Rehastim2Commands from .utils import packet_construction, signed_int - - -logger = logging.getLogger("pyScienceMode") +from .logger import logger class _Motomed: diff --git a/pyScienceMode/utils.py b/pyScienceMode/utils.py index f027f8f..c368a3c 100644 --- a/pyScienceMode/utils.py +++ b/pyScienceMode/utils.py @@ -1,9 +1,7 @@ -import logging - import crccheck from .enums import ErrorCode, Rehastim2Commands +from .logger import logger -logger = logging.getLogger("pyScienceMode") """ This code provides utility functions for working with the Rehastim device, including packet construction and data diff --git a/tests/test_raise_error_rehastim2.py b/tests/test_raise_error_rehastim2.py index 9de977b..ae41f7c 100644 --- a/tests/test_raise_error_rehastim2.py +++ b/tests/test_raise_error_rehastim2.py @@ -1,6 +1,7 @@ import pytest -from pyScienceMode import Rehastim2 as St2 + from pyScienceMode import Channel, Device, Modes +from pyScienceMode.devices.rehastim2 import Rehastim2 as St2 # Connect the Rehastimp24 device to the computer. # Then you can run the whole file (except for the test_electrode_error) or just one test. diff --git a/tests/test_raise_error_rehastimP24.py b/tests/test_raise_error_rehastimP24.py index 10a292e..8118053 100644 --- a/tests/test_raise_error_rehastimP24.py +++ b/tests/test_raise_error_rehastimP24.py @@ -1,6 +1,7 @@ import pytest -from pyScienceMode import RehastimP24 as Stp24 -from pyScienceMode import Channel, Point, Device, Modes + +from pyScienceMode import Channel, Device, Modes +from pyScienceMode.devices.rehastimP24 import RehastimP24 as Stp24 # Connect the Rehastimp24 device to the computer. # Then you can run the whole file (except for the test_electrode_error) or just one test. diff --git a/tests/test_update_parameter_rehastim2.py b/tests/test_update_parameter_rehastim2.py index fbd2b46..7a99a76 100644 --- a/tests/test_update_parameter_rehastim2.py +++ b/tests/test_update_parameter_rehastim2.py @@ -1,6 +1,7 @@ import pytest -from pyScienceMode import Rehastim2 as St2 + from pyScienceMode import Channel, Device, Modes +from pyScienceMode.devices.rehastim2 import Rehastim2 as St2 # Connect the Rehastim2 device to the computer. diff --git a/tests/test_update_parameter_rehastimp24.py b/tests/test_update_parameter_rehastimp24.py index 45cf267..7112a36 100644 --- a/tests/test_update_parameter_rehastimp24.py +++ b/tests/test_update_parameter_rehastimp24.py @@ -1,6 +1,7 @@ import pytest -from pyScienceMode import RehastimP24 as Stp24 + from pyScienceMode import Channel, Device, Modes +from pyScienceMode.devices.rehastimP24 import RehastimP24 as Stp24 # Connect the Rehastimp24 device to the computer. Then connect channel 1 to a stim box or to the skin, and start the From 26198b5d3a761dc49dd78776cb1a71a6b1f1269f Mon Sep 17 00:00:00 2001 From: Pariterre Date: Tue, 23 Jan 2024 15:44:12 -0500 Subject: [PATCH 6/6] Removed the common logging configuration as it is best practise for modules --- Examples/motomed_example.py | 25 ++++++++++++++++++- Examples/motomed_stim_example.py | 25 ++++++++++++++++++- examples/perf_test.py | 30 ++++++++++++++++++++++- examples/rehastimp24_example.py | 27 +++++++++++++++++++- pyScienceMode/__init__.py | 1 - pyScienceMode/devices/rehastim_generic.py | 5 +++- pyScienceMode/logger.py | 17 ------------- pyScienceMode/motomed_interface.py | 4 ++- pyScienceMode/utils.py | 6 ++++- 9 files changed, 115 insertions(+), 25 deletions(-) delete mode 100644 pyScienceMode/logger.py diff --git a/Examples/motomed_example.py b/Examples/motomed_example.py index 57e9f60..29ee2e7 100644 --- a/Examples/motomed_example.py +++ b/Examples/motomed_example.py @@ -1,9 +1,32 @@ import time +import logging +import logging.config -from pyScienceMode import logger from pyScienceMode.devices.rehastim2 import Rehastim2 +def setup_logger(): + logging_config = { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "output": {"format": "%(asctime)s - %(name)s.%(levelname)s:\t%(message)s"}, + }, + "datefmt": "%Y-%m-%d %H:%M:%S", + "handlers": { + "console": {"class": "logging.StreamHandler", "formatter": "output", "stream": "ext://sys.stdout"} + }, + "loggers": { + "pyScienceMode": {"handlers": ["console"], "level": logging.DEBUG}, + }, + } + logging.config.dictConfig(logging_config) + + +setup_logger() +logger = logging.getLogger("pyScienceMode") + + port = "/dev/ttyUSB0" # Enter the port on which the stimulator is connected motomed = Rehastim2(port, show_log=True, with_motomed=True).motomed diff --git a/Examples/motomed_stim_example.py b/Examples/motomed_stim_example.py index 979713c..d8f39cb 100644 --- a/Examples/motomed_stim_example.py +++ b/Examples/motomed_stim_example.py @@ -1,9 +1,29 @@ +import logging +import logging.config import time -from pyScienceMode import Channel as Ch, Modes, Device, logger +from pyScienceMode import Channel as Ch, Modes, Device from pyScienceMode.devices.rehastim2 import Rehastim2 as St +def setup_logger(): + logging_config = { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "output": {"format": "%(asctime)s - %(name)s.%(levelname)s:\t%(message)s"}, + }, + "datefmt": "%Y-%m-%d %H:%M:%S", + "handlers": { + "console": {"class": "logging.StreamHandler", "formatter": "output", "stream": "ext://sys.stdout"} + }, + "loggers": { + "pyScienceMode": {"handlers": ["console"], "level": logging.DEBUG}, + }, + } + logging.config.dictConfig(logging_config) + + def init_rehastim(): # Create a list of channels @@ -39,6 +59,9 @@ def init_rehastim(): if __name__ == "__main__": + setup_logger() + logger = logging.getLogger("pyScienceMode") + stimulator, list_channels = init_rehastim() motomed = stimulator.motomed diff --git a/examples/perf_test.py b/examples/perf_test.py index 8c61538..e45fe58 100644 --- a/examples/perf_test.py +++ b/examples/perf_test.py @@ -1,5 +1,8 @@ +import logging +import logging.config + from biosiglive import ViconClient, DeviceType -from pyScienceMode import Channel, Point, Device, Modes, logger +from pyScienceMode import Channel, Point, Device, Modes from pyScienceMode.devices.rehastim2 import Rehastim2 as St2 from pyScienceMode.devices.rehastimP24 import RehastimP24 as St import random @@ -16,6 +19,24 @@ """ +def setup_logger(): + logging_config = { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "output": {"format": "%(asctime)s - %(name)s.%(levelname)s:\t%(message)s"}, + }, + "datefmt": "%Y-%m-%d %H:%M:%S", + "handlers": { + "console": {"class": "logging.StreamHandler", "formatter": "output", "stream": "ext://sys.stdout"} + }, + "loggers": { + "pyScienceMode": {"handlers": ["console"], "level": logging.DEBUG}, + }, + } + logging.config.dictConfig(logging_config) + + def get_trigger(): """ This function is used to get the trigger from Vicon. @@ -396,6 +417,7 @@ def diff_frequency_ll_ml(frequency): stimulatorp24.start_stimulation(upd_list_channels=list_channels, stimulation_duration=2, safety=True) stimulatorp24.end_stimulation() + logger = logging.getLogger("pyScienceMode") for point in channel_1.list_point: list_points.append(point) logger.info(point.pulse_width, point.amplitude) @@ -430,6 +452,8 @@ def communication_speed_P24(): ll_config.points[0].current = list_points[0].amplitude ll_config.points[1].time = list_points[1].pulse_width ll_config.points[1].current = list_points[1].amplitude + + logger = logging.getLogger("pyScienceMode") while True: ll_config.packet_number = sciencemode.lib.smpt_packet_number_generator_next(stimulatorp24.device) sciencemode.lib.smpt_send_ll_channel_config(stimulatorp24.device, ll_config) @@ -476,6 +500,8 @@ def communication_speed_r2(): stimulator2.init_channel(stimulation_interval=8, list_channels=list_channels) amplitude = 10 waiting_time = 1 + + logger = logging.getLogger("pyScienceMode") while True: stimulator2.start_stimulation(stimulation_duration=0.35) amplitude *= 1.01 @@ -555,6 +581,8 @@ def exe(): """ Test to see if the python program do the same thing as the .exe program. """ + setup_logger() + stimulatorp24 = St(port="COM4", show_log=True) channel_1 = Channel(no_channel=1, amplitude=20, pulse_width=350, frequency=50, device_type=Device.Rehastimp24) list_channels.append(channel_1) diff --git a/examples/rehastimp24_example.py b/examples/rehastimp24_example.py index dbaf8cb..674badd 100644 --- a/examples/rehastimp24_example.py +++ b/examples/rehastimp24_example.py @@ -1,4 +1,7 @@ -from pyScienceMode import Channel, Point, Device, Modes, logger +import logging +import logging.config + +from pyScienceMode import Channel, Point, Device, Modes from pyScienceMode.devices.rehastimP24 import RehastimP24 as St @@ -9,6 +12,28 @@ to be able to use commands from another one. """ + +def setup_logger(): + logging_config = { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "output": {"format": "%(asctime)s - %(name)s.%(levelname)s:\t%(message)s"}, + }, + "datefmt": "%Y-%m-%d %H:%M:%S", + "handlers": { + "console": {"class": "logging.StreamHandler", "formatter": "output", "stream": "ext://sys.stdout"} + }, + "loggers": { + "pyScienceMode": {"handlers": ["console"], "level": logging.DEBUG}, + }, + } + logging.config.dictConfig(logging_config) + + +setup_logger() +logger = logging.getLogger("pyScienceMode") + # list which contains the channels you want to use list_channels = [] diff --git a/pyScienceMode/__init__.py b/pyScienceMode/__init__.py index 5009874..0b43aae 100644 --- a/pyScienceMode/__init__.py +++ b/pyScienceMode/__init__.py @@ -1,4 +1,3 @@ -from .logger import logger from .motomed_interface import _Motomed from .devices.rehastim_generic import RehastimGeneric from . import utils diff --git a/pyScienceMode/devices/rehastim_generic.py b/pyScienceMode/devices/rehastim_generic.py index 9e10c19..bc5ee29 100644 --- a/pyScienceMode/devices/rehastim_generic.py +++ b/pyScienceMode/devices/rehastim_generic.py @@ -4,6 +4,7 @@ """ from abc import ABC, abstractmethod +import logging import threading import time @@ -19,7 +20,9 @@ start_stimulation_ack, ) from ..enums import Rehastim2Commands, RehastimP24Commands, Device -from ..logger import logger + + +logger = logging.getLogger("pyScienceMode") # Notes : # This code needs to be used in parallel with the "ScienceMode2 - Description and protocol" document diff --git a/pyScienceMode/logger.py b/pyScienceMode/logger.py deleted file mode 100644 index 00739f7..0000000 --- a/pyScienceMode/logger.py +++ /dev/null @@ -1,17 +0,0 @@ -import logging -import logging.config - -logging_config = { - "version": 1, - "disable_existing_loggers": False, - "formatters": { - "output": {"format": "%(asctime)s - %(name)s.%(levelname)s:\t%(message)s"}, - }, - "datefmt": "%Y-%m-%d %H:%M:%S", - "handlers": {"console": {"class": "logging.StreamHandler", "formatter": "output", "stream": "ext://sys.stdout"}}, - "loggers": { - "pyScienceMode": {"handlers": ["console"], "level": logging.DEBUG}, - }, -} -logging.config.dictConfig(logging_config) -logger = logging.getLogger("pyScienceMode") diff --git a/pyScienceMode/motomed_interface.py b/pyScienceMode/motomed_interface.py index 4e736c3..7a2c44f 100644 --- a/pyScienceMode/motomed_interface.py +++ b/pyScienceMode/motomed_interface.py @@ -2,6 +2,7 @@ Motomed Interface class used to control and get data from Motomed while connected to the rehamove2. See ScienceMode2 - Description and protocol for more information. """ +import logging from .acks import ( get_motomed_mode_ack, @@ -20,7 +21,8 @@ ) from .enums import Rehastim2Commands from .utils import packet_construction, signed_int -from .logger import logger + +logger = logging.getLogger("pyScienceMode") class _Motomed: diff --git a/pyScienceMode/utils.py b/pyScienceMode/utils.py index c368a3c..b6efde9 100644 --- a/pyScienceMode/utils.py +++ b/pyScienceMode/utils.py @@ -1,6 +1,10 @@ +import logging + import crccheck from .enums import ErrorCode, Rehastim2Commands -from .logger import logger + + +logger = logging.getLogger("pyScienceMode") """