From 3275396d4b5b4d0cb11011138f89bb8f1f055f54 Mon Sep 17 00:00:00 2001 From: Aaron Tang Date: Thu, 22 Aug 2019 15:40:47 -0500 Subject: [PATCH 1/9] Updated Version 1.5 for Windows (Python2 and Python3) libraries. Adds functionality to get the current version number. Added source files, and built files. --- builds/python/windows_amd64/_rehamovelib.pyd | Bin 70656 -> 72192 bytes builds/python/windows_amd64/rehamove.py | 14 +- builds/python/windows_amd64/rehamovelib.py | 4 + builds/python2/windows_amd64/_rehamovelib.pyd | Bin 71168 -> 71680 bytes builds/python2/windows_amd64/rehamove.py | 14 +- builds/python2/windows_amd64/rehamovelib.py | 4 + src/python/windows_amd64/build.bat | 34 +- src/python/windows_amd64/rehamove.py | 88 +++- src/python/windows_amd64/rehamovelib.c | 453 +++++++++++------- src/python/windows_amd64/rehamovelib.i | 23 +- 10 files changed, 429 insertions(+), 205 deletions(-) diff --git a/builds/python/windows_amd64/_rehamovelib.pyd b/builds/python/windows_amd64/_rehamovelib.pyd index 313a2470dc0b115f105d07c1ea46f225f997950c..b2d224ccc2c5c41d9d0142df1479e8d6fed31bb7 100644 GIT binary patch delta 17764 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 DQmv5Q delta 16961 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 diff --git a/builds/python/windows_amd64/rehamove.py b/builds/python/windows_amd64/rehamove.py index dd9d898..5140b07 100644 --- a/builds/python/windows_amd64/rehamove.py +++ b/builds/python/windows_amd64/rehamove.py @@ -2,13 +2,21 @@ class Rehamove: + current_version = "v1.5" + channel0 = ['r', 'red'] channel1 = ['b', 'blue'] - channel2 = ['g', 'gray', 'grey', 'black'] + channel2 = ['g1', 'gray1', 'grey1', 'black'] + channel3 = ['g2', 'gray2', 'grey2', 'white'] def __init__(self, port_name): self.rehamove = rehamovelib.open_port(port_name) + def version(self): + c_version = rehamovelib.get_version() + print("Rehamove Version: Python-side " + str(Rehamove.current_version) + ", C-side " + str(c_version)) + return Rehamove.current_version + def get_channel(self, channel): chosen_channel = channel if isinstance(channel, str): @@ -19,10 +27,12 @@ def get_channel(self, channel): chosen_channel = 1 elif channel in Rehamove.channel2: chosen_channel = 2 + elif channel in Rehamove.channel3: + chosen_channel = 3 else: chosen_channel = 0 # Default elif isinstance(channel, int): - if channel < 0 and channel > 2: + if channel < 0 and channel > 3: chosen_channel = 0 # Default else: chosen_channel = 0 diff --git a/builds/python/windows_amd64/rehamovelib.py b/builds/python/windows_amd64/rehamovelib.py index 3503ba4..b17fe8e 100644 --- a/builds/python/windows_amd64/rehamovelib.py +++ b/builds/python/windows_amd64/rehamovelib.py @@ -95,6 +95,7 @@ class Rehamove(object): __repr__ = _swig_repr port_name = property(_rehamovelib.Rehamove_port_name_get, _rehamovelib.Rehamove_port_name_set) device = property(_rehamovelib.Rehamove_device_get, _rehamovelib.Rehamove_device_set) + battery = property(_rehamovelib.Rehamove_battery_get, _rehamovelib.Rehamove_battery_set) def __init__(self): _rehamovelib.Rehamove_swiginit(self, _rehamovelib.new_Rehamove()) @@ -104,6 +105,9 @@ def __init__(self): _rehamovelib.Rehamove_swigregister(Rehamove) +def get_version(): + return _rehamovelib.get_version() + def open_port(port_name): return _rehamovelib.open_port(port_name) diff --git a/builds/python2/windows_amd64/_rehamovelib.pyd b/builds/python2/windows_amd64/_rehamovelib.pyd index bb8841d2f82488051a7228a43f874b0d757b4a98..d74623cfbdbaa8a38ece19a99db463251c913a61 100644 GIT binary patch delta 14591 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 diff --git a/builds/python2/windows_amd64/rehamove.py b/builds/python2/windows_amd64/rehamove.py index dd9d898..5140b07 100644 --- a/builds/python2/windows_amd64/rehamove.py +++ b/builds/python2/windows_amd64/rehamove.py @@ -2,13 +2,21 @@ class Rehamove: + current_version = "v1.5" + channel0 = ['r', 'red'] channel1 = ['b', 'blue'] - channel2 = ['g', 'gray', 'grey', 'black'] + channel2 = ['g1', 'gray1', 'grey1', 'black'] + channel3 = ['g2', 'gray2', 'grey2', 'white'] def __init__(self, port_name): self.rehamove = rehamovelib.open_port(port_name) + def version(self): + c_version = rehamovelib.get_version() + print("Rehamove Version: Python-side " + str(Rehamove.current_version) + ", C-side " + str(c_version)) + return Rehamove.current_version + def get_channel(self, channel): chosen_channel = channel if isinstance(channel, str): @@ -19,10 +27,12 @@ def get_channel(self, channel): chosen_channel = 1 elif channel in Rehamove.channel2: chosen_channel = 2 + elif channel in Rehamove.channel3: + chosen_channel = 3 else: chosen_channel = 0 # Default elif isinstance(channel, int): - if channel < 0 and channel > 2: + if channel < 0 and channel > 3: chosen_channel = 0 # Default else: chosen_channel = 0 diff --git a/builds/python2/windows_amd64/rehamovelib.py b/builds/python2/windows_amd64/rehamovelib.py index 3503ba4..b17fe8e 100644 --- a/builds/python2/windows_amd64/rehamovelib.py +++ b/builds/python2/windows_amd64/rehamovelib.py @@ -95,6 +95,7 @@ class Rehamove(object): __repr__ = _swig_repr port_name = property(_rehamovelib.Rehamove_port_name_get, _rehamovelib.Rehamove_port_name_set) device = property(_rehamovelib.Rehamove_device_get, _rehamovelib.Rehamove_device_set) + battery = property(_rehamovelib.Rehamove_battery_get, _rehamovelib.Rehamove_battery_set) def __init__(self): _rehamovelib.Rehamove_swiginit(self, _rehamovelib.new_Rehamove()) @@ -104,6 +105,9 @@ def __init__(self): _rehamovelib.Rehamove_swigregister(Rehamove) +def get_version(): + return _rehamovelib.get_version() + def open_port(port_name): return _rehamovelib.open_port(port_name) diff --git a/src/python/windows_amd64/build.bat b/src/python/windows_amd64/build.bat index 1e08941..a0daa6b 100644 --- a/src/python/windows_amd64/build.bat +++ b/src/python/windows_amd64/build.bat @@ -1,18 +1,34 @@ -SET build_directory=../../../builds/python/windows_amd64 SET precompiled_directory=../../../hasomed_precompiled/smpt_rm3_msvc2015_x86_amd64_static +SET python3_build_directory=../../../builds/python/windows_amd64 +SET python2_build_directory=../../../builds/python2/windows_amd64 -echo rehamove-integration-lib: Performing the build (Python on Windows) +echo rehamove-integration-lib: Performing the build (Python3 on Windows) swig -python rehamovelib.i cl /LD /MD rehamovelib.c rehamovelib_wrap.c /Fe_rehamovelib.pyd /I "C:/Users/Agggron/AppData/Local/Programs/Python/Python37/include" /I "%precompiled_directory%/include/low-level" /I "%precompiled_directory%/include/general" /link "%precompiled_directory%/lib/libsmpt.lib" "C:/Users/Agggron/AppData/Local/Programs/Python/Python37/libs/python37.lib" -echo rehamove-integration-lib: Moving built files into the builds directory -copy rehamove.py "%build_directory%" -move rehamovelib.py "%build_directory%" -move _rehamovelib.pyd "%build_directory%" - -echo rehamove-integration-lib: Cleaning up intermediate build files +echo rehamove-integration-lib: Moving built files into the builds directory and cleaning up files. +move _rehamovelib.pyd "%python3_build_directory%" +copy rehamove.py "%python3_build_directory%" +move rehamovelib.py "%python3_build_directory%" +del rehamovelib.obj +del rehamovelib_wrap.obj +del _rehamovelib.lib +del _rehamovelib.exp del rehamovelib_wrap.c + + +echo rehamove-integration-lib: Performing the build (Python2 on Windows) +swig -python rehamovelib.i +cl /LD /MD rehamovelib.c rehamovelib_wrap.c /Fe_rehamovelib.pyd /I "C:/Python27/include" /I "%precompiled_directory%/include/low-level" /I "%precompiled_directory%/include/general" /link "%precompiled_directory%/lib/libsmpt.lib" "C:/Python27/libs/python27.lib" + +echo rehamove-integration-lib: Moving built files into the builds directory and cleaning up files. +move _rehamovelib.pyd "%python2_build_directory%" +copy rehamove.py "%python2_build_directory%" +move rehamovelib.py "%python2_build_directory%" del rehamovelib.obj del rehamovelib_wrap.obj del _rehamovelib.lib -del _rehamovelib.exp \ No newline at end of file +del _rehamovelib.exp +del rehamovelib_wrap.c + +echo rehamove-integration-lib: Build complete. \ No newline at end of file diff --git a/src/python/windows_amd64/rehamove.py b/src/python/windows_amd64/rehamove.py index 0be55ae..5140b07 100644 --- a/src/python/windows_amd64/rehamove.py +++ b/src/python/windows_amd64/rehamove.py @@ -1,18 +1,66 @@ -import rehamovelib +import rehamovelib class Rehamove: + + current_version = "v1.5" + + channel0 = ['r', 'red'] + channel1 = ['b', 'blue'] + channel2 = ['g1', 'gray1', 'grey1', 'black'] + channel3 = ['g2', 'gray2', 'grey2', 'white'] + def __init__(self, port_name): self.rehamove = rehamovelib.open_port(port_name) + def version(self): + c_version = rehamovelib.get_version() + print("Rehamove Version: Python-side " + str(Rehamove.current_version) + ", C-side " + str(c_version)) + return Rehamove.current_version + + def get_channel(self, channel): + chosen_channel = channel + if isinstance(channel, str): + channel = channel.lower() + if channel in Rehamove.channel0: + chosen_channel = 0 + elif channel in Rehamove.channel1: + chosen_channel = 1 + elif channel in Rehamove.channel2: + chosen_channel = 2 + elif channel in Rehamove.channel3: + chosen_channel = 3 + else: + chosen_channel = 0 # Default + elif isinstance(channel, int): + if channel < 0 and channel > 3: + chosen_channel = 0 # Default + else: + chosen_channel = 0 + return chosen_channel + def pulse(self, channel, current, pulse_width): - rehamovelib.pulse(self.rehamove, channel, current, pulse_width) + if self.rehamove == None: + print("python Rehamove pulse() ERROR! Rehamove object does not exist.") + return -1 + chosen_channel = self.get_channel(channel) + result = rehamovelib.pulse(self.rehamove, chosen_channel, current, pulse_width) + if result != 0: + print("python Rehamove pulse() ERROR!") + return -1 + else: + print("python Rehamove pulse() sent.") + return 0 def custom_pulse(self, channel, points_array): + if self.rehamove == None: + print("python Rehamove custom_pulse() ERROR! Rehamove object does not exist.") + return -1 + chosen_channel = self.get_channel(channel) original_length = len(points_array) num_points = len(points_array) # Error handling (warning) if too many points. if num_points > 16: - print("custom_pulse(): WARNING! Maximum of 16 points allowed, truncating points array.") + print("python Rehamove custom_pulse() WARNING! Maximum of 16 points allowed, truncating points array.") num_points = 16 # Error handling (exception) if malformed points. @@ -20,10 +68,9 @@ def custom_pulse(self, channel, points_array): for i in range(0, num_points): current = points_array[i][0] pulse_width = points_array[i][1] - #print("Point {}: {} mA {} us".format(i, current, pulse_width)) except: - print("custom_pulse: ERROR! Malformed points array, should be: [ (current0, pulse_width0), (current1, pulse_width1), ... ]") - return + print("python Rehamove custom_pulse() ERROR! Malformed points array, should be: [ (current0, pulse_width0), (current1, pulse_width1), ... ]") + return -1 # Handle if the user supplies less than 16 points: fill up empty points in the array. remaining_points = 16 - num_points @@ -48,12 +95,31 @@ def custom_pulse(self, channel, points_array): c14, w14 = points_array[14][0], points_array[14][1] c15, w15 = points_array[15][0], points_array[15][1] - rehamovelib.custom_pulse(self.rehamove, channel, original_length, c0, w0, c1, w1, c2, w2, c3, w3, c4, w4, c5, w5, c6, w6, c7, w7, c8, w8, c9, w9, c10, w10, c11, w11, c12, w12, c13, w13, c14, w14, c15, w15) + result = rehamovelib.custom_pulse(self.rehamove, chosen_channel, original_length, c0, w0, c1, w1, c2, w2, c3, w3, c4, w4, c5, w5, c6, w6, c7, w7, c8, w8, c9, w9, c10, w10, c11, w11, c12, w12, c13, w13, c14, w14, c15, w15) + if result != 0: + print("python Rehamove custom_pulse() ERROR!") + return -1 + else: + print("python Rehamove custom_pulse() sent.") + return 0 - #print("PYTHON custom_pulse {} {}, {} {} {} {} {} {} {} {} / {} {} {} {} {} {} {} {} / {} {} {} {} {} {} {} {} / {} {} {} {} {} {} {} {}".format(self.rehamove, channel, c0, w0, c1, w1, c2, w2, c3, w3, c4, w4, c5, w5, c6, w6, c7, w7, c8, w8, c9, w9, c10, w10, c11, w11, c12, w12, c13, w13, c14, w14, c15, w15)) - def battery(self): - rehamovelib.battery(self.rehamove) + if self.rehamove == None: + print("python Rehamove ERROR! Rehamove object does not exist.") + return -1 + result = rehamovelib.battery_request(self.rehamove) + if result != 0: + print("python Rehamove battery() ERROR!") + return -1 + else: + battery_level = rehamovelib.get_battery(self.rehamove) + print("python Rehamove battery(): " + str(battery_level) + "%") + return battery_level def __del__(self): - rehamovelib.close_port(self.rehamove) + # Only close the port if we have a Rehamove object to close + if self.rehamove != None: + result = rehamovelib.close_port(self.rehamove) + if result != 0: + print("python Rehamove close_port() ERROR!") + diff --git a/src/python/windows_amd64/rehamovelib.c b/src/python/windows_amd64/rehamovelib.c index 3d3f06f..c2df0cf 100644 --- a/src/python/windows_amd64/rehamovelib.c +++ b/src/python/windows_amd64/rehamovelib.c @@ -1,191 +1,167 @@ +#include "smpt_ll_client.h" #include #include #include -#include -#include "smpt_ll_client.h" + +#if defined(__linux__) +#include /* Not for Windows */ +#endif + +#define VERSION_NUMBER "v1.5" +#define TIMEOUT_COUNTER 10000000 typedef struct { char port_name[64]; Smpt_device device; -} Rehamove; + int battery; +} Rehamove; -/* -Function declarations in this section. -*/ +char * get_version(); Rehamove * open_port(const char * port_name); -void close_port(Rehamove * r); -void pulse(Rehamove * r, char * channel, int current, int pulse_width); -void custom_pulse(Rehamove * r, char * channel, int num_points, float c0, int w0, float c1, int w1, float c2, int w2, float c3, int w3, float c4, int w4, float c5, int w5, float c6, int w6, float c7, int w7, float c8, int w8, float c9, int w9, float c10, int w10, float c11, int w11, float c12, int w12, float c13, int w13, float c14, int w14, float c15, int w15); -void battery(Rehamove * r); +int close_port(Rehamove * r); +int pulse(Rehamove * r, int channel, float current, int pulse_width); +int get_battery(Rehamove * r); +int battery_request(Rehamove * r); +int custom_pulse(Rehamove * r, int channel, int num_points, float c0, int w0, float c1, int w1, float c2, int w2, float c3, int w3, float c4, int w4, float c5, int w5, float c6, int w6, float c7, int w7, float c8, int w8, float c9, int w9, float c10, int w10, float c11, int w11, float c12, int w12, float c13, int w13, float c14, int w14, float c15, int w15); + +void print_device(Smpt_device d, const char * filename); +void error_callback(const char * message) { + printf("ERROR CALLBACK: %s\n", message); +} + +/* USER-FACING FUNCTIONS */ -/* -Actual implementation details below. -*/ +char * get_version() { + return VERSION_NUMBER; +} Rehamove * open_port(const char * port_name) { - Rehamove * r = (Rehamove *) calloc(1, sizeof(Rehamove)); + smpt_init_error_callback(&error_callback); - for (int i = 0; i < strlen(port_name); i++) { - (r->port_name)[i] = *(port_name + i); - } - (r->port_name)[strlen(port_name)] = '\0'; + uint8_t packet_number = 1; /* The packet_number can be used for debugging purposes */ - printf("open_port(): Created port_name %s\n", r->port_name); + Rehamove * r = (Rehamove *) calloc(1, sizeof(Rehamove)); - Smpt_device device = {0}; - int open_port_result = smpt_open_serial_port(&device, r->port_name); - if (open_port_result == 0) { - printf("open_port(): ERROR! Unable to connect to port %s.\n", r->port_name); + for (int i = 0; i < strlen(port_name); i++) { + (r->port_name)[i] = *(port_name + i); } - else { - printf("open_port(): Successfully opened port.\n"); - } - r->device = device; - return r; -} + (r->port_name)[strlen(port_name)] = '\0'; + r->battery = -1; -void close_port(Rehamove * r) { - int result = smpt_close_serial_port(&(r->device)); - if (result == 0) { - printf("close_port(): ERROR! Unable to close port %s !\n", r->port_name); - return; - } - printf("close_port(): Successfully closed port %s .\n", r->port_name); - free(r); - return; -} - -void print_channel_config(Smpt_ll_channel_config * s) { - - printf("PRINTING CHANNEL CONFIG\n"); - printf("enable_stimulation: %d\n", s->enable_stimulation); - printf("channel: %d\n", s->channel); - printf("modify_demux: %d\n", s->modify_demux); - printf("number_of_points: %d\n", s->number_of_points); - printf("Smpt_Length_Points: %d\n", Smpt_Length_Points); - printf("points array: "); - for (int i = 0; i < Smpt_Length_Points; i++) { - Smpt_point p = *(s->points + i); - printf("(time %d float %f control_mode %d interpolation_mode %d) ", p.time, p.current, p.control_mode, p.interpolation_mode); - } - printf("\n"); - printf("Smpt_Length_Demux_Config: %d\n", Smpt_Length_Demux_Config); - printf("demux_config array: "); - for (int i = 0; i < Smpt_Length_Demux_Config; i++) { - printf("(%d) ", *(s->demux_config + i)); + Smpt_device device = {0}; + r->device = device; + bool open_port_result = smpt_open_serial_port(&(r->device), port_name); + if (!open_port_result) { + printf("open_port() ERROR: Opening connection to port %s failed!\n", port_name); + free(r); + return NULL; } - printf("\n"); - printf("demux_length: %d\n", s->demux_length); - printf("packet_number: %d\n", s->packet_number); -} -void pulse(Rehamove * r, char * channel, int current, int pulse_width) { - - uint8_t packet_number = 0; /* The packet_number can be used for debugging purposes */ Smpt_ll_init ll_init = {0}; /* Struct for ll_init command */ - Smpt_ll_channel_config ll_channel_config = {0}; /* Struct for ll_channel_config command */ - /* Clear ll_init struct and set the data */ smpt_clear_ll_init(&ll_init); - - //print_channel_config(&ll_channel_config); - ll_init.packet_number = packet_number; /* Send the ll_init command to stimulation unit */ - smpt_send_ll_init(&(r->device), &ll_init); + bool ll_init_result = smpt_send_ll_init(&(r->device), &ll_init); + if (!ll_init_result) { + printf("open_port() ERROR: Sending device initialization message failed!\n"); + free(r); + return NULL; + } - packet_number++; + int counter = 0; + while (!smpt_new_packet_received(&(r->device))) { + if (counter > TIMEOUT_COUNTER) { + printf("open_port() ERROR: Receiving device initialization message timed out!\n"); + free(r); + return NULL; + } + counter += 1; + } + Smpt_ack ack; + smpt_last_ack(&(r->device), &ack); - /* Set the data */ - ll_channel_config.enable_stimulation = true; - - char lowercase[64]; - for (int i = 0; i < strlen(channel); i++) { - lowercase[i] = tolower(channel[i]); + if ((ack.result != Smpt_Result_Successful) || (ack.command_number != Smpt_Cmd_Ll_Init_Ack)) { + printf("open_port() ERROR: Unsuccessful device initialization response! Expected: command %d result %d, Received: command %d result %d.\n", Smpt_Cmd_Ll_Init_Ack, Smpt_Result_Successful, ack.command_number, ack.result); + free(r); + return NULL; } - lowercase[strlen(channel)] = '\0'; - int chosen_channel = Smpt_Channel_Blue; - if (strncmp(lowercase, "red", strlen("red")) == 0) { - chosen_channel = Smpt_Channel_Red; - } - else if (strncmp(lowercase, "blue", strlen("blue")) == 0) { - chosen_channel = Smpt_Channel_Blue; - } - else if (strncmp(lowercase, "black", strlen("gray")) == 0) { - chosen_channel = Smpt_Channel_Black; + Smpt_ll_init_ack init_ack; + smpt_get_ll_init_ack(&(r->device), &init_ack); + printf("open_port() SUCCESS: Connected to port %s and initialized device.\n", port_name); + return r; +} + +int pulse(Rehamove * r, int channel, float current, int pulse_width) { + if (r == NULL) { + printf("pulse() ERROR: No Rehamove object found!\n"); + return 1; } - ll_channel_config.channel = chosen_channel; /* Use blue channel */ + uint8_t packet_number = 1; + + Smpt_ll_channel_config ll_channel_config = {0}; /* Struct for ll_channel_config command */ + /* Set the data */ + ll_channel_config.enable_stimulation = true; + ll_channel_config.channel = channel; ll_channel_config.number_of_points = 3; /* Set the number of points*/ ll_channel_config.packet_number = packet_number; - /* Set the stimulation pulse */ - /* First point, current: 20 mA, positive, pulse width: 200 µs */ - ll_channel_config.points[0].current = current; + ll_channel_config.points[0].current = current; ll_channel_config.points[0].time = pulse_width; - - /* Second point, pause 100 µs */ - ll_channel_config.points[1].time = pulse_width / 2; - - /* Third point, current: -20 mA, negative, pulse width: 200 µs */ + ll_channel_config.points[1].time = pulse_width / 2; ll_channel_config.points[2].current = current * -1; ll_channel_config.points[2].time = pulse_width; - //print_channel_config(&ll_channel_config); - /* Send the ll_channel_list command to the stimulation unit */ - smpt_send_ll_channel_config(&(r->device), &ll_channel_config); + bool ll_config_result = smpt_send_ll_channel_config(&(r->device), &ll_channel_config); + if (!ll_config_result) { + printf("pulse() ERROR: Pulse command not sent!\n"); + return 1; + } - //print_channel_config(&ll_channel_config); + int counter = 0; + while (!smpt_new_packet_received(&(r->device))) { + if (counter > TIMEOUT_COUNTER) { + printf("pulse() ERROR: Receiving pulse response timed out!\n"); + return 1; + } + counter += 1; + } - packet_number++; + Smpt_ack ack; + smpt_last_ack(&(r->device), &ack); - /* Send the ll_stop command to the stimulation unit */ - smpt_send_ll_stop(&(r->device), packet_number); - //printf("pulse(): Successfully finished.\n"); + if ((ack.result != Smpt_Result_Successful) || (ack.command_number != Smpt_Cmd_Ll_Channel_Config_Ack)) { + // Common error situation -> electrode error (e.g. electrode not connected). + if (ack.result == Smpt_Result_Electrode_Error) { + printf("pulse() ERROR: Unsuccessful pulse response - electrode error! Expected: command %d result %d, Received: command %d result %d.\n", Smpt_Cmd_Ll_Channel_Config_Ack, Smpt_Result_Successful, ack.command_number, ack.result); + return 1; + } + // Default error message -> specify the code. + printf("pulse() ERROR: Unsuccessful pulse response! Expected: command %d result %d, Received: command %d result %d.\n", Smpt_Cmd_Ll_Channel_Config_Ack, Smpt_Result_Successful, ack.command_number, ack.result); + return 1; + } else { + Smpt_ll_channel_config_ack channel_config_ack; + smpt_get_ll_channel_config_ack(&(r->device), &channel_config_ack); + } + return 0; } -void custom_pulse(Rehamove * r, char * channel, int num_points, float c0, int w0, float c1, int w1, float c2, int w2, float c3, int w3, float c4, int w4, float c5, int w5, float c6, int w6, float c7, int w7, float c8, int w8, float c9, int w9, float c10, int w10, float c11, int w11, float c12, int w12, float c13, int w13, float c14, int w14, float c15, int w15) { +int custom_pulse(Rehamove * r, int channel, int num_points, float c0, int w0, float c1, int w1, float c2, int w2, float c3, int w3, float c4, int w4, float c5, int w5, float c6, int w6, float c7, int w7, float c8, int w8, float c9, int w9, float c10, int w10, float c11, int w11, float c12, int w12, float c13, int w13, float c14, int w14, float c15, int w15) { - //printf("C custom_pulse: %p %s, %f %d %f %d %f %d %f %d / %f %d %f %d %f %d %f %d / %f %d %f %d %f %d %f %d / %f %d %f %d %f %d %f %d", r, channel, c0, w0, c1, w1, c2, w2, c3, w3, c4, w4, c5, w5, c6, w6, c7, w7, c8, w8, c9, w9, c10, w10, c11, w11, c12, w12, c13, w13, c14, w14, c15, w15); + if (r == NULL) { + printf("custom_pulse() ERROR: No Rehamove object found!\n"); + return 1; + } - uint8_t packet_number = 0; /* The packet_number can be used for debugging purposes */ - Smpt_ll_init ll_init = {0}; /* Struct for ll_init command */ + uint8_t packet_number = 2; Smpt_ll_channel_config ll_channel_config = {0}; /* Struct for ll_channel_config command */ - - /* Clear ll_init struct and set the data */ - smpt_clear_ll_init(&ll_init); - - ll_init.packet_number = packet_number; - - /* Send the ll_init command to stimulation unit */ - smpt_send_ll_init(&(r->device), &ll_init); - - packet_number++; - /* Set the data */ ll_channel_config.enable_stimulation = true; - - char lowercase[64]; - for (int i = 0; i < strlen(channel); i++) { - lowercase[i] = tolower(channel[i]); - } - lowercase[strlen(channel)] = '\0'; - - int chosen_channel = Smpt_Channel_Blue; - if (strncmp(lowercase, "red", strlen("red")) == 0) { - chosen_channel = Smpt_Channel_Red; - } - else if (strncmp(lowercase, "blue", strlen("blue")) == 0) { - chosen_channel = Smpt_Channel_Blue; - } - else if (strncmp(lowercase, "black", strlen("gray")) == 0) { - chosen_channel = Smpt_Channel_Black; - } - - ll_channel_config.channel = chosen_channel; /* Use blue channel */ + ll_channel_config.channel = channel; ll_channel_config.number_of_points = num_points; /* Set the number of points*/ ll_channel_config.packet_number = packet_number; @@ -223,53 +199,186 @@ void custom_pulse(Rehamove * r, char * channel, int num_points, float c0, int w0 ll_channel_config.points[15].current = c15; ll_channel_config.points[15].time = w15; - //print_channel_config(&ll_channel_config); - /* Send the ll_channel_list command to the stimulation unit */ - smpt_send_ll_channel_config(&(r->device), &ll_channel_config); + bool ll_config_result = smpt_send_ll_channel_config(&(r->device), &ll_channel_config); + if (!ll_config_result) { + printf("custom_pulse() ERROR: Pulse command not sent!\n"); + return 1; + } - packet_number++; + int counter = 0; + while (!smpt_new_packet_received(&(r->device))) { + if (counter > TIMEOUT_COUNTER) { + printf("custom_pulse() ERROR: Receiving pulse response timed out!\n"); + return 1; + } + } - /* Send the ll_stop command to the stimulation unit */ - smpt_send_ll_stop(&(r->device), packet_number); - //printf("custom_pulse(): Successfully finished.\n"); + Smpt_ack ack; + smpt_last_ack(&(r->device), &ack); + + if ((ack.result != Smpt_Result_Successful) || (ack.command_number != Smpt_Cmd_Ll_Channel_Config_Ack)) { + // Common error situation -> electrode error (e.g. electrode not connected). + if (ack.result == Smpt_Result_Electrode_Error) { + printf("custom_pulse() ERROR: Unsuccessful pulse response - electrode error! Expected: command %d result %d, Received: command %d result %d.\n", Smpt_Cmd_Ll_Channel_Config_Ack, Smpt_Result_Successful, ack.command_number, ack.result); + return 1; + } + // Default error message -> specify the code. + printf("custom_pulse() ERROR: Unsuccessful pulse response! Expected: command %d result %d, Received: command %d result %d.\n", Smpt_Cmd_Ll_Channel_Config_Ack, Smpt_Result_Successful, ack.command_number, ack.result); + return 1; + } else { + Smpt_ll_channel_config_ack channel_config_ack; + smpt_get_ll_channel_config_ack(&(r->device), &channel_config_ack); + } + return 0; } -void battery(Rehamove * r) { - // Annoyingly have to close and reopen a new port. - smpt_close_serial_port(&(r->device)); - printf("close(): Successfully finished.\n"); +int close_port(Rehamove * r) { + if (r == NULL) { + printf("close_port() ERROR: No Rehamove object found!\n"); + return 1; + } - Smpt_device device = {0}; - int open_port_result = smpt_open_serial_port(&device, r->port_name); - if (open_port_result == 0) { - printf("open(): ERROR! Unable to connect to port %s.\n", r->port_name); + uint8_t packet_number = 1; + bool ll_stop_result = smpt_send_ll_stop(&(r->device), packet_number); + if (!ll_stop_result) { + printf("close_port() ERROR: Stopping device request not sent!\n"); + return 1; } - else { - printf("open(): Successfully opened port.\n"); + + int counter = 0; + while (!smpt_new_packet_received(&(r->device))) { + if (counter > TIMEOUT_COUNTER) { + printf("close_port() ERROR: Receiving device stop response timed out!\n"); + return 1; + } } - r->device = device; - /* Send the call to get the battery status. Signature is: - - SMPT_API bool smpt_send_get_battery_status ( Smpt_device *const device, - uint8_t packet_number - ) - */ - uint8_t packet_number = 42; /* The packet_number can be used for debugging purposes */ - bool send_result = smpt_send_get_battery_status(&(r->device), packet_number); + Smpt_ack ack; + smpt_last_ack(&(r->device), &ack); + if ((ack.result != Smpt_Result_Successful) || (ack.command_number != Smpt_Cmd_Ll_Stop_Ack)) { + // Default error message -> specify the code. + printf("close_port() ERROR: Unsuccessful device stop response! Expected: command %d result %d, Received: command %d result %d.\n", Smpt_Cmd_Ll_Stop_Ack, Smpt_Result_Successful, ack.command_number, ack.result); + return 1; + } + bool close_port_result = smpt_close_serial_port(&(r->device)); + if (!close_port_result) { + printf("close_port() ERROR! Close port request not sent!\n"); + return 1; + } + free(r); + printf("close_port() SUCCESS: Stopped device and closed port successfully.\n"); + return 0; +} + +int get_battery(Rehamove * r) { + if (r == NULL) { + printf("get_battery() ERROR: No Rehamove object found!\n"); + return -1; + } + return r->battery; +} + +int battery_request(Rehamove * r) { + if (r == NULL) { + printf("battery_request() ERROR: No Rehamove object found!\n"); + return 1; + } + uint8_t packet_number = 42; + bool battery_result = smpt_send_get_battery_status(&(r->device), packet_number); + if (!battery_result) { + printf("battery_result() ERROR: Battery request not sent!\n"); + return 1; + } int counter = 0; while (!smpt_new_packet_received(&(r->device))) { - - counter += 1; + if (counter > TIMEOUT_COUNTER) { + printf("battery_result() ERROR: Receiving battery response timed out!\n"); + return 1; + } } - + Smpt_ack ack; smpt_last_ack(&(r->device), &ack); - Smpt_get_battery_status_ack battery_ack; - smpt_get_get_battery_status_ack(&(r->device), &battery_ack); - - printf("Battery life is at %d %%.\n", battery_ack.battery_level); + if ((ack.result != Smpt_Result_Successful) || (ack.command_number != Smpt_Cmd_Get_Battery_Status_Ack)) { + // Default error message -> specify the code. + printf("battery_result() ERROR: Unsuccessful battery response! Expected: command %d result %d, Received: command %d result %d.\n", Smpt_Cmd_Get_Battery_Status_Ack, Smpt_Result_Successful, ack.command_number, ack.result); + return 1; + } else { + Smpt_get_battery_status_ack battery_ack; + smpt_get_get_battery_status_ack(&(r->device), &battery_ack); + r->battery = battery_ack.battery_level; + } + return 0; } + + +/* FOR DEBUGGING */ + +void print_device(Smpt_device d, const char * filename) { + + FILE * file = fopen(filename, "w+, css=UTF-8"); + if (file == NULL) { + printf("Could not print to file %s\n", filename); + return; + } + + fprintf(file, "PRINTING DEVICE.\n"); + fprintf(file, "packet_length %d\n", d.packet_length); + fprintf(file, "packet array of size %d: ", Smpt_Length_Max_Packet_Size); + for (int i = 0; i < Smpt_Length_Max_Packet_Size; i++) { + fprintf(file, "%d ", (d.packet)[i]); + } + fprintf(file, "\n"); + fprintf(file, "cmd_list c:\n"); + Smpt_cmd_list c = d.cmd_list; + fprintf(file, "c.acks_length %d\n", c.acks_length); + fprintf(file, "c.acks_current_index %d\n", c.acks_current_index); + fprintf(file, "c.acks array of size %d: ( ", Smpt_Length_Number_Of_Acks); + for (int i = 0; i < Smpt_Length_Number_Of_Acks; i++) { + Smpt_ack a = *(c.acks + i); + fprintf(file, "[packet %d command %d result %d] ", a.packet_number, a.command_number, a.result); + } + fprintf(file, ")\n"); + fprintf(file, "c.requests_current_index %d\n", c.requests_current_index); + fprintf(file, "c.requests_expected_index %d\n", c.requests_expected_index); + fprintf(file, "c.number_of_expected %d\n", c.number_of_expected); + fprintf(file, "c.requests array of size %d: ( ", Smpt_Length_Number_Of_Acks); + for (int i = 0; i < Smpt_Length_Number_Of_Acks; i++) { + Smpt_cmd a = *(c.requests + i); + fprintf(file, "[packet %d command %d] ", a.packet_number, a.command_number); + } + fprintf(file, ")\n"); + fprintf(file, "c.new_ack_available %d\n", c.new_ack_available); + //fprintf(file, "serial_port_handle %p\n", d.serial_port_handle_); + fprintf(file, "current_packet_number %d\n", d.current_packet_number); + fprintf(file, "serial_port_name array of size %d: ( ", Smpt_Length_Serial_Port_Chars); + for (int i = 0; i < Smpt_Length_Serial_Port_Chars; i++) { + fprintf(file, "%c ", *(d.serial_port_name + i)); + } + fprintf(file, ")\n"); + fprintf(file, "packet_input_buffer p:\n"); + Packet_input_buffer p = d.packet_input_buffer; + fprintf(file, "p.buffer %p\n", p.buffer); + fprintf(file, "p.buffer_state %p\n", p.buffer_state); + fprintf(file, "p.write_row_length_count %d\n", p.write_row_length_count); + fprintf(file, "p.write_row_count %d\n", p.write_row_count); + fprintf(file, "p.read_row_count %d\n", p.read_row_count); + fprintf(file, "p.ignore_next_byte %d\n", p.ignore_next_byte); + fprintf(file, "p.number_of_rows %d\n", p.number_of_rows); + fprintf(file, "p.row_length %d\n", p.row_length); + fprintf(file, "packet_input_buffer_data array of size (%d * %d): ( ", Smpt_Length_Packet_Input_Buffer_Rows, Smpt_Length_Max_Packet_Size); + for (int i = 0; i < Smpt_Length_Packet_Input_Buffer_Rows * Smpt_Length_Max_Packet_Size; i++) { + fprintf(file, "%d ", *(d.packet_input_buffer_data + i)); + } + fprintf(file, ")\n"); + fprintf(file, "packet_input_buffer_state array of size %d: ( ", Smpt_Length_Packet_Input_Buffer_Rows); + for (int i = 0; i < Smpt_Length_Packet_Input_Buffer_Rows; i++) { + fprintf(file, "%d ", *(d.packet_input_buffer_state + i)); + } + fprintf(file, ")\n"); + fprintf(file, "END PRINT DEVICE.\n"); + fclose(file); +} \ No newline at end of file diff --git a/src/python/windows_amd64/rehamovelib.i b/src/python/windows_amd64/rehamovelib.i index c3801b2..e17258e 100644 --- a/src/python/windows_amd64/rehamovelib.i +++ b/src/python/windows_amd64/rehamovelib.i @@ -3,28 +3,33 @@ #include #include #include -#include #include "smpt_ll_client.h" typedef struct { char port_name[64]; Smpt_device device; + int battery; } Rehamove; +extern char * get_version(); extern Rehamove * open_port(const char * port_name); -extern void close_port(Rehamove * r); -extern void pulse(Rehamove * r, char * channel, int current, int pulse_width); -extern void custom_pulse(Rehamove * r, char * channel, int num_points, float c0, int w0, float c1, int w1, float c2, int w2, float c3, int w3, float c4, int w4, float c5, int w5, float c6, int w6, float c7, int w7, float c8, int w8, float c9, int w9, float c10, int w10, float c11, int w11, float c12, int w12, float c13, int w13, float c14, int w14, float c15, int w15); -extern void battery(Rehamove * r); +extern int pulse(Rehamove * r, int channel, float current, int pulse_width); +extern int close_port(Rehamove * r); +extern int get_battery(Rehamove * r); +extern int battery_request(Rehamove * r); +extern int custom_pulse(Rehamove * r, int channel, int num_points, float c0, int w0, float c1, int w1, float c2, int w2, float c3, int w3, float c4, int w4, float c5, int w5, float c6, int w6, float c7, int w7, float c8, int w8, float c9, int w9, float c10, int w10, float c11, int w11, float c12, int w12, float c13, int w13, float c14, int w14, float c15, int w15); %} typedef struct { char port_name[64]; Smpt_device device; + int battery; } Rehamove; +extern char * get_version(); extern Rehamove * open_port(const char * port_name); -extern void close_port(Rehamove * r); -extern void pulse(Rehamove * r, char * channel, int current, int pulse_width); -extern void custom_pulse(Rehamove * r, char * channel, int num_points, float c0, int w0, float c1, int w1, float c2, int w2, float c3, int w3, float c4, int w4, float c5, int w5, float c6, int w6, float c7, int w7, float c8, int w8, float c9, int w9, float c10, int w10, float c11, int w11, float c12, int w12, float c13, int w13, float c14, int w14, float c15, int w15); -extern void battery(Rehamove * r); \ No newline at end of file +extern int pulse(Rehamove * r, int channel, float current, int pulse_width); +extern int close_port(Rehamove * r); +extern int get_battery(Rehamove * r); +extern int battery_request(Rehamove * r); +extern int custom_pulse(Rehamove * r, int channel, int num_points, float c0, int w0, float c1, int w1, float c2, int w2, float c3, int w3, float c4, int w4, float c5, int w5, float c6, int w6, float c7, int w7, float c8, int w8, float c9, int w9, float c10, int w10, float c11, int w11, float c12, int w12, float c13, int w13, float c14, int w14, float c15, int w15); \ No newline at end of file From 4db14af71c7779d0e18e4307802ad9a188c23278 Mon Sep 17 00:00:00 2001 From: Aaron Tang Date: Thu, 22 Aug 2019 16:52:44 -0500 Subject: [PATCH 2/9] updated README with 8-22-2019 update --- README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index e0b3f8c..576d94c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This is the **rehamoveIntegrationLib**, a collection of libraries (for non-commercial use only) that interface with the RehaMove 3 medical device. -![image](extra/video.png) +![image](https://github.com/humancomputerintegration/rehamove-integration-lib/tree/master/extra/video.png) (click [here](https://youtu.be/IyL0C_fEE2A) for our youtube video) **What does this do?** The RehaMove 3 is a medical device that sends out electrical signals, which can be used clinically and in research, e.g. by doing functional electrical stimulation (FES) and/or electrical muscle stimulation (EMS). Existing documentation is provided by the manufacturer Hasomed to control the RehaMove via C code using a precompiled C library. **Instead, our libraries extend this functionality to Python and C#**, allowing the user to send commands in these other programming languages. This can be used for rapid prototyping, and/or integration with engines such as Unity3D. @@ -22,7 +22,7 @@ We support several versions of the library for different systems. After download #### 1.1.1 Linux -Download [this directory for 64-bit OS](builds/python/linux_amd64/) or [this directory for ARM](builds/python/linux_ARM/). +Download [this directory for 64-bit OS](https://github.com/humancomputerintegration/rehamove-integration-lib/tree/master/builds/python/linux_amd64/) or [this directory for ARM](https://github.com/humancomputerintegration/rehamove-integration-lib/tree/master/builds/python/linux_ARM/). For Linux, we support two versions (for AMD64 and for ARM architectures). Make sure you have the following files: @@ -32,7 +32,7 @@ For Linux, we support two versions (for AMD64 and for ARM architectures). Make s #### 1.1.2 Windows -Download [this directory](builds/python/windows_amd64/). +Download [this directory](https://github.com/humancomputerintegration/rehamove-integration-lib/tree/master/builds/python/windows_amd64/). For Windows, we support AMD64 architectures. Make sure you have the following files: @@ -46,7 +46,7 @@ MacOS support has not been tested but we expect this to work. We are working on ### 1.2 C\# (for Unity3D in Windows) -Download [this directory](builds/csharp). +Download [this directory](https://github.com/humancomputerintegration/rehamove-integration-lib/tree/master/builds/csharp). We support C# for Unity3D integration on Windows-only. Make sure you have the following files: @@ -63,7 +63,7 @@ Theoretically our build might work also on Linux and Mac; if you got the sharp t This section explains how to use our libraries, including example code demonstrating imports and calling the library functions. See our video tutorial: -[![Video Tutorial for using our libraries](extra/video.png)](https://youtu.be/IyL0C_fEE2A) +[![Video Tutorial for using our libraries](https://github.com/humancomputerintegration/rehamove-integration-lib/tree/master/extra/video.png)](https://youtu.be/IyL0C_fEE2A) ### 2.1 Python @@ -342,4 +342,8 @@ Full LICENSE AT: https://creativecommons.org/licenses/by-nc/2.0/ - Fixed memory-related issue requiring needing to open and close ports repeatedly. - Added error handling for unsuccessful opening/closing the port, and unsuccessful method calls. Failure to open the port and/or initialize the device will return a NULL Rehamove object that should not be able to run any of the class methods. - Fixed issue with stalling while waiting for a response for a battery query -> user now sees a timeout. -- Allow the output channels to be called in any case (e.g. "BLUE", "bLuE", or "blue") \ No newline at end of file +- Allow the output channels to be called in any case (e.g. "BLUE", "bLuE", or "blue") + +8-22-2019 Update +- Added callable function version() to get the version of the Python-side and C-side of the current library. +- Allow the output channels to be called with integers as well (e.g. 0 = red, 1 = blue, 2 = gray1, 3 = gray2) \ No newline at end of file From 52bb10cd35729507a18e071cf32e4e7ca6bdedfb Mon Sep 17 00:00:00 2001 From: Human Computer Integration Lab Date: Thu, 22 Aug 2019 18:08:21 -0500 Subject: [PATCH 3/9] pedro and aaron updated after v1.5 --- README.md | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 576d94c..eaf05cf 100644 --- a/README.md +++ b/README.md @@ -20,11 +20,15 @@ This is the **rehamoveIntegrationLib**, a collection of libraries (for non-comme We support several versions of the library for different systems. After downloading the files, **move the files to your working directory** (where you will run Python from). These instructions assume use of the 64-bit version of Python 3; we also have libraries for the 64-bit version of Python2. -#### 1.1.1 Linux +#### 1.1.1 Linux (both AMD64 and ARM v7+) or MacOS (AMD64, untested for now) -Download [this directory for 64-bit OS](https://github.com/humancomputerintegration/rehamove-integration-lib/tree/master/builds/python/linux_amd64/) or [this directory for ARM](https://github.com/humancomputerintegration/rehamove-integration-lib/tree/master/builds/python/linux_ARM/). +* [Nighly Linux AMD64 zip](lab.plopes.org/rehamove/python-linux_amd64.zip) +* [Nighly Linux ARM V7+ zip](lab.plopes.org/rehamove/python-linux_ARM.zip) +* [Most-recent source code for AMD64](https://github.com/humancomputerintegration/rehamove-integration-lib/tree/master/builds/python/linux_amd64/) +* [Most-recent source code for ARM V7+](https://github.com/humancomputerintegration/rehamove-integration-lib/tree/master/builds/python/linux_ARM) +* MacOS is untested now, we will update soon. -For Linux, we support two versions (for AMD64 and for ARM architectures). Make sure you have the following files: +On Linux (or MacOS, which is untested for now) make sure you have the following files (for your desired architecture, either AMD64 or ARM V7+): 1. `rehamove.py` 2. `rehamovelib.py` @@ -32,21 +36,19 @@ For Linux, we support two versions (for AMD64 and for ARM architectures). Make s #### 1.1.2 Windows -Download [this directory](https://github.com/humancomputerintegration/rehamove-integration-lib/tree/master/builds/python/windows_amd64/). +* [Nighly Windows 64-bit (only) zip](lab.plopes.org/rehamove/python-windows_amd64.zip) +* [Most-recent source code for Windows 64-bit (only)](https://github.com/humancomputerintegration/rehamove-integration-lib/tree/master/builds/python/windows_amd64/) -For Windows, we support AMD64 architectures. Make sure you have the following files: +For Windows, we support 64-bit (aka AMD64) architectures. Make sure you have the following files: 1. `rehamove.py` 2. `rehamovelib.py` 3. `_rehamovelib.pyd` -#### 1.1.3 MacOS - -MacOS support has not been tested but we expect this to work. We are working on it. If you have tested, please report to us. - ### 1.2 C\# (for Unity3D in Windows) -Download [this directory](https://github.com/humancomputerintegration/rehamove-integration-lib/tree/master/builds/csharp). +* [Nighly Windows 64-bit (only) zip for C#](lab.plopes.org/rehamove/csharp-windows_amd64.zip) +* [Most-recent source code for Windows 64-bit (only) C\#](https://github.com/humancomputerintegration/rehamove-integration-lib/tree/master/builds/csharp). We support C# for Unity3D integration on Windows-only. Make sure you have the following files: @@ -55,8 +57,7 @@ We support C# for Unity3D integration on Windows-only. Make sure you have the fo After downloading the files, **move the downloaded files into the Assets folder of your Unity project**. -Theoretically our build might work also on Linux and Mac; if you got the sharp to run on those platforms, write us an email. - +(Theoretically our build might work also on Linux and Mac; if you got the sharp to run on those platforms, write us an email. ) ## 2. Controlling the Rehamove via our library @@ -337,13 +338,14 @@ Full LICENSE AT: https://creativecommons.org/licenses/by-nc/2.0/ ## 8. Update History -8-19-2019 Update +8-22-2019 +- Added callable function version() to get the version of the Python-side and C-side of the current library. +- Allow the output channels to be called with integers as well (e.g. 0 = red, 1 = blue, 2 = gray1, 3 = gray2) + +8-19-2019 - Fixed issue regarding inconsistent pulses when pulse commands are sent in rapid succession. - Fixed memory-related issue requiring needing to open and close ports repeatedly. - Added error handling for unsuccessful opening/closing the port, and unsuccessful method calls. Failure to open the port and/or initialize the device will return a NULL Rehamove object that should not be able to run any of the class methods. - Fixed issue with stalling while waiting for a response for a battery query -> user now sees a timeout. - Allow the output channels to be called in any case (e.g. "BLUE", "bLuE", or "blue") -8-22-2019 Update -- Added callable function version() to get the version of the Python-side and C-side of the current library. -- Allow the output channels to be called with integers as well (e.g. 0 = red, 1 = blue, 2 = gray1, 3 = gray2) \ No newline at end of file From 696e6cdab6c706e5cac9b47348097f4109b042a3 Mon Sep 17 00:00:00 2001 From: Human Computer Integration Lab Date: Thu, 22 Aug 2019 18:16:17 -0500 Subject: [PATCH 4/9] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index eaf05cf..8d58a26 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,8 @@ We support several versions of the library for different systems. After download #### 1.1.1 Linux (both AMD64 and ARM v7+) or MacOS (AMD64, untested for now) -* [Nighly Linux AMD64 zip](lab.plopes.org/rehamove/python-linux_amd64.zip) -* [Nighly Linux ARM V7+ zip](lab.plopes.org/rehamove/python-linux_ARM.zip) +* [Nighly Linux AMD64 zip](https://lab.plopes.org/rehamove/python-linux_amd64.zip) +* [Nighly Linux ARM V7+ zip](https://lab.plopes.org/rehamove/python-linux_ARM.zip) * [Most-recent source code for AMD64](https://github.com/humancomputerintegration/rehamove-integration-lib/tree/master/builds/python/linux_amd64/) * [Most-recent source code for ARM V7+](https://github.com/humancomputerintegration/rehamove-integration-lib/tree/master/builds/python/linux_ARM) * MacOS is untested now, we will update soon. @@ -36,7 +36,7 @@ On Linux (or MacOS, which is untested for now) make sure you have the following #### 1.1.2 Windows -* [Nighly Windows 64-bit (only) zip](lab.plopes.org/rehamove/python-windows_amd64.zip) +* [Nighly Windows 64-bit (only) zip](https://lab.plopes.org/rehamove/python-windows_amd64.zip) * [Most-recent source code for Windows 64-bit (only)](https://github.com/humancomputerintegration/rehamove-integration-lib/tree/master/builds/python/windows_amd64/) For Windows, we support 64-bit (aka AMD64) architectures. Make sure you have the following files: @@ -47,7 +47,7 @@ For Windows, we support 64-bit (aka AMD64) architectures. Make sure you have the ### 1.2 C\# (for Unity3D in Windows) -* [Nighly Windows 64-bit (only) zip for C#](lab.plopes.org/rehamove/csharp-windows_amd64.zip) +* [Nighly Windows 64-bit (only) zip for C#](https://lab.plopes.org/rehamove/csharp-windows_amd64.zip) * [Most-recent source code for Windows 64-bit (only) C\#](https://github.com/humancomputerintegration/rehamove-integration-lib/tree/master/builds/csharp). We support C# for Unity3D integration on Windows-only. Make sure you have the following files: From 8826aa02a31a8134582a47f0a23384d502bafa9b Mon Sep 17 00:00:00 2001 From: Human Computer Integration Lab Date: Thu, 22 Aug 2019 18:21:21 -0500 Subject: [PATCH 5/9] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8d58a26..cccfe2d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Rehamove Integration Lib: Python and C# Extensions for RehaMove 3 -This is the **rehamoveIntegrationLib**, a collection of libraries (for non-commercial use only) that interface with the RehaMove 3 medical device. +This is the **rehamoveIntegrationLib**, a collection of libraries (for non-commercial use only) that interface with the RehaMove 3 medical device. You can find the project's official page [here](https://lab.plopes.org/rehalib). ![image](https://github.com/humancomputerintegration/rehamove-integration-lib/tree/master/extra/video.png) (click [here](https://youtu.be/IyL0C_fEE2A) for our youtube video) From 58633ce781ba16d9262a504743ca70c3058471c1 Mon Sep 17 00:00:00 2001 From: Human Computer Integration Lab Date: Thu, 22 Aug 2019 18:24:54 -0500 Subject: [PATCH 6/9] v1.5 is out! celebrate! --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cccfe2d..68afe30 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Rehamove Integration Lib: Python and C# Extensions for RehaMove 3 +# Rehamove Integration Lib: Python and C# Extensions for RehaMove 3 (v1.5) This is the **rehamoveIntegrationLib**, a collection of libraries (for non-commercial use only) that interface with the RehaMove 3 medical device. You can find the project's official page [here](https://lab.plopes.org/rehalib). @@ -338,11 +338,11 @@ Full LICENSE AT: https://creativecommons.org/licenses/by-nc/2.0/ ## 8. Update History -8-22-2019 +8-22-2019 version 1.5 is out - Added callable function version() to get the version of the Python-side and C-side of the current library. - Allow the output channels to be called with integers as well (e.g. 0 = red, 1 = blue, 2 = gray1, 3 = gray2) -8-19-2019 +8-19-2019 (no version names) - Fixed issue regarding inconsistent pulses when pulse commands are sent in rapid succession. - Fixed memory-related issue requiring needing to open and close ports repeatedly. - Added error handling for unsuccessful opening/closing the port, and unsuccessful method calls. Failure to open the port and/or initialize the device will return a NULL Rehamove object that should not be able to run any of the class methods. From e1828ed089cbce6e0339d8462cbd98e0b41362fd Mon Sep 17 00:00:00 2001 From: Human Computer Integration Lab Date: Thu, 22 Aug 2019 18:32:33 -0500 Subject: [PATCH 7/9] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 68afe30..decae39 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,9 @@ This section describes the list of functions that our libraries currently suppor ### 3.1 Python -* `r = Rehamove(port_name)`: Constructor to initialize the device. **Save the return value to a variable! This return value is needed to invoke the other functions.** Takes in one argument (the port_name, for example **/dev/ttyUSB0**). Returns a Rehamove object. This function automatically opens the port. If an error occurs during device initialization, we return a NULL object, that should not be able to run any of the other functions. If this happens, it is best to retry initialization and store in a different variable. +* `r = Rehamove(port_name)`: Constructor to initialize the device. **Save the return value to a variable! This return value is the object upon which you invoke the other functions.** The call for `Rehamove()` takes in one argument, which is the port_name (for example **/dev/ttyUSB0** on Linux or COM3 on Windows, etc.). It returns a `Rehamove` object, so (as mentioned) remember to assign it. The creation of a `Rehamove` object automatically opens the port, unless an error occurs while opening the port (e.g., wrong port name, etc). + +* Error handling when creating new `Rehamove` object: we currently do not have exception handling but we have error printing. Thus, your best option to handle this in code is to test whether your variable that holds the object is `None` (e.g., `if r == None`). One nice way to have a `while` loop that attempts connections until the return object is not `None` -- this allows you to have apython script that infitely tries to get the port to connect to the device. * `r.pulse(channel_name, current, pulse_width)`: Sends a single pulse. Takes in three arguments: * a character string for the channel (e.g. `"red"`, `"blue"`, or `"black"`). Also, using integers also works (0 = red, 1 = blue, 2 = black/grey). From 006bdcdc674179b675a9e52fb9572657162dd310 Mon Sep 17 00:00:00 2001 From: Aaron Tang Date: Thu, 22 Aug 2019 18:46:28 -0500 Subject: [PATCH 8/9] Added example: can have a while loop that continually tries to connect to a port, and once it successfully connects to that port, can use that Rehamove object normally. --- src/python/examples/connect_midway.py | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/python/examples/connect_midway.py diff --git a/src/python/examples/connect_midway.py b/src/python/examples/connect_midway.py new file mode 100644 index 0000000..8aa7b86 --- /dev/null +++ b/src/python/examples/connect_midway.py @@ -0,0 +1,9 @@ +from rehamove import * +import time +r = Rehamove("COM3") # while USB device is unplugged +while r.rehamove == None: # plug in the USB device at some point during the loop + r = Rehamove("COM3") + time.sleep(0.5) +r.battery() +r.pulse(1, 6, 200) +r.version() \ No newline at end of file From 7706110f6d96648ae9fb74b7cd3b3d47f5fe726e Mon Sep 17 00:00:00 2001 From: Aaron Tang Date: Thu, 22 Aug 2019 18:51:06 -0500 Subject: [PATCH 9/9] Updated README with known bugs and features. --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index decae39..cd4806e 100644 --- a/README.md +++ b/README.md @@ -318,10 +318,10 @@ Both of the output DLLs (`rehamovelib.dll` and `UnityRehamove.dll`) can be impor This is a list of known bugs/features in our libraries that we hope to fix and/or implement soon. Please feel free to contact us if you find more! -- Main bug: timing is inconsistent, some pulses are being dropped. Fix is coming! (Fixed in Python, will fix in C#) -- Sparse exception handling (fixed in Python, will fix in C#) -- For C# (on Unity3D), the port does not automatically close. Please use Unity3D's `onApplicationQuit()` method to call our library's `close()` function. -- Sometimes when removing and replugging in the USB port connecting to the Rehamove, the next time a Rehamove object is initialized (i.e. with **r = Rehamove("COM3")**) that initialization fails -> but subsequent initializations should succeed. Can be worked around by simply trying the initialization again. +- C#: Timing is inconsistent, and some pulses may be dropped if pulses are sent too quickly. +- C#: Sparse error handling. +- C# in Unity3D: The port does not automatically close. Please use Unity3D's `onApplicationQuit()` method to call our library's `close()` function. +- Python on Windows: Sometimes when disconnecting and reconnecting the USB device, the Rehamove object initialization (e.g. **r = Rehamove("COM3")**) will fail with an **"Unsuccessful device initialization response!"**. Subsequent initializations should succeed, so this can be worked around by trying to initialize the Rehamove object again. ## 6. Contact