Skip to content

Commit

Permalink
*squish*
Browse files Browse the repository at this point in the history
- Woodway calibration gets actual Woodway speed
- Check if BLE and Woodway connected before starting session
- try-except around protocol folder load
- Add connection bool for session manager to check
- Shift treeview focus each time the protocol step is incremented
- Save To File active only when changes have occurred
- Loaded in protocol files are saved in current patient's directory
- Protocol file revisions are appended more robustly
- Belt speed display shows full float to tenths place
- Bump patch number
  • Loading branch information
wsarce committed Jun 27, 2022
1 parent 4b1eb38 commit 1d5e34d
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 27 deletions.
2 changes: 1 addition & 1 deletion config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ enable-e4: false
enable-woodway: false
first-time: true
fps: 8
logs-dir: C:\cometrics\1.1.1\logs
logs-dir: C:\cometrics\1.1.4\logs
patient-concerns: concerns.yml
phases:
- Assessment
Expand Down
92 changes: 67 additions & 25 deletions output_view_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from ttkwidgets import TickScale
from pywoodway.treadmill import SplitBelt, find_treadmills
from tkinter_utils import build_treeview, clear_treeview, AddWoodwayProtocolStep, AddBleProtocolStep, \
CalibrateVibrotactors, CalibrateWoodway
CalibrateVibrotactors, CalibrateWoodway, select_focus
from ui_params import treeview_bind_tag_dict, treeview_tags, treeview_bind_tags, crossmark, checkmark
from pytactor import VibrotactorArray, VibrotactorArraySide
from pyempatica.empaticae4 import EmpaticaE4, EmpaticaDataStreams, EmpaticaClient, EmpaticaServerConnectError
Expand Down Expand Up @@ -280,6 +280,7 @@ def __init__(self, parent, height, width, field_font, header_font, button_size,
self.woodway_incline = 0
self.session_started = False
self.changed_protocol = True
self.__connected = False
if woodway_thresh:
self.calibrated = True
self.woodway_thresh = woodway_thresh
Expand Down Expand Up @@ -399,8 +400,11 @@ def __init__(self, parent, height, width, field_font, header_font, button_size,

self.woodway_dir = os.path.join(self.session_dir, "Woodway")
if os.path.exists(self.woodway_dir):
latest_protocol = max(pathlib.Path(self.woodway_dir).glob("*.json"), key=lambda f: f.stat().st_ctime)
self.__load_protocol_from_file(latest_protocol)
try:
latest_protocol = max(pathlib.Path(self.woodway_dir).glob("*.json"), key=lambda f: f.stat().st_ctime)
self.__load_protocol_from_file(latest_protocol)
except ValueError:
print("WARNING: Protocol folder exists with no protocols!")

def disable_ui_elements(self):
self.__disable_ui_elements()
Expand Down Expand Up @@ -442,10 +446,7 @@ def start_session(self):
def stop_session(self):
if self.woodway:
self.session_started = False
self.woodway.belt_a.set_speed(0.0)
self.woodway.belt_b.set_speed(0.0)
self.woodway.set_elevations(0.0)
self.woodway.close()
self.disconnect_woodway()

def next_protocol_step(self, current_time):
if current_time == 1:
Expand All @@ -454,6 +455,7 @@ def next_protocol_step(self, current_time):
if (self.step_time - current_time) == 0:
self.selected_step += 1
self.__update_woodway_protocol()
select_focus(self.prot_treeview, self.prot_treeview_parents[self.selected_step])

def __update_woodway_protocol(self):
if self.selected_step == len(self.protocol_steps):
Expand All @@ -470,6 +472,9 @@ def __update_woodway(self):
self.__write_incline(self.woodway_incline)
self.__write_speed()

def is_connected(self):
return self.__connected

def is_calibrated(self):
return self.calibrated

Expand Down Expand Up @@ -515,6 +520,8 @@ def __load_protocol_from_file(self, selected_file=None):
with open(self.prot_file, 'r') as f:
self.protocol_steps = json.load(f)['Steps']
self.repopulate_treeview()
self.changed_protocol = True
self.prot_save_button['state'] = 'active'
else:
messagebox.showwarning("Warning", "No file selected, please try again!")
except Exception as ex:
Expand All @@ -525,19 +532,24 @@ def __save_protocol_to_file(self):
if self.changed_protocol:
if self.prot_file:
file_dir = os.path.join(self.session_dir, "Woodway")
if not os.path.exists(file_dir):
os.mkdir(file_dir)
if pathlib.Path(self.prot_file).parent != file_dir:
self.prot_file = os.path.join(file_dir, pathlib.Path(self.prot_file).name)
file_count = len(glob.glob1(file_dir, "*.json"))
if file_count > 1:
if file_count > 0:
new_file = os.path.join(pathlib.Path(self.prot_file).parent,
pathlib.Path(self.prot_file).stem[:-3] + f"_V{file_count}.json")
'_'.join(pathlib.Path(self.prot_file).stem.split('_')[:-1]) + f"_V{file_count}.json")
else:
new_file = os.path.join(pathlib.Path(self.prot_file).parent,
pathlib.Path(self.prot_file).stem + f"_V{file_count}.json")
with open(new_file, 'w') as f:
x = {"Steps": self.protocol_steps}
json.dump(x, f)
self.__load_protocol_from_file(selected_file=new_file)
if not self.changed_protocol:
messagebox.showinfo("Success", "Protocol file saved!")
messagebox.showinfo("Success", "Protocol file saved!")
self.changed_protocol = False
self.prot_save_button['state'] = 'disabled'
else:
file_dir = os.path.join(self.session_dir, "Woodway")
if not os.path.exists(file_dir):
Expand All @@ -548,8 +560,9 @@ def __save_protocol_to_file(self):
with open(self.prot_file, 'w') as f:
x = {"Steps": self.protocol_steps}
json.dump(x, f)
if not self.changed_protocol:
messagebox.showinfo("Success", "Protocol file saved!")
messagebox.showinfo("Success", "Protocol file saved!")
self.changed_protocol = False
self.prot_save_button['state'] = 'disabled'
else:
messagebox.showwarning("Warning", "No filename supplied! Can't save, please try again!")
except Exception as ex:
Expand Down Expand Up @@ -583,6 +596,8 @@ def __delete_protocol_step(self):
if self.selected_step:
self.protocol_steps.pop(self.selected_step - 1)
self.repopulate_treeview()
self.changed_protocol = True
self.prot_save_button['state'] = 'active'

def __connect_to_woodway(self):
try:
Expand All @@ -591,6 +606,7 @@ def __connect_to_woodway(self):
self.woodway = SplitBelt(b_port.name, a_port.name)
self.woodway.start_belts(True, False, True, False)
self.__enable_ui_elements()
self.__connected = True
messagebox.showinfo("Success!", "Woodway Split Belt treadmill connected!")
else:
messagebox.showerror("Error", "No treadmills found! Check serial numbers and connections!")
Expand All @@ -606,33 +622,34 @@ def disconnect_woodway(self):
self.woodway = None
self.__disable_ui_elements()
self.__enable_connect_button()
self.__connected = False

def __write_speed(self):
if self.session_started:
self.belt_speed_l.set(self.woodway_speed_l)
self.belt_speed_r.set(self.woodway_speed_r)
if self.woodway:
self.belt_speed_l_value.config(text=f"{int(self.woodway_speed_l)} MPH")
self.belt_speed_r_value.config(text=f"{int(self.woodway_speed_r)} MPH")
self.belt_speed_l_value.config(text=f"{float(self.woodway_speed_l):.1f} MPH")
self.belt_speed_r_value.config(text=f"{float(self.woodway_speed_r):.1f} MPH")
self.woodway.set_speed(self.woodway_speed_l, self.woodway_speed_r)

def __write_l_speed(self, speed):
if self.session_started:
self.belt_speed_l.set(speed)
self.belt_speed_l.set(float(speed))
if self.woodway:
self.belt_speed_l_value.config(text=f"{float(speed):.1f} MPH")
self.woodway.belt_a.set_speed(float(speed))

def __write_r_speed(self, speed):
if self.session_started:
self.belt_speed_r.set(speed)
self.belt_speed_r.set(float(speed))
if self.woodway:
self.belt_speed_r_value.config(text=f"{float(speed):.1f} MPH")
self.woodway.belt_b.set_speed(float(speed))

def __write_incline(self, incline):
if self.session_started:
self.belt_incline_l.set(incline)
self.belt_incline_l.set(float(incline))
if self.woodway:
self.belt_incline_l_value.config(text=f"{float(incline):.1f}\u00b0")
self.woodway.set_elevations(float(incline))
Expand All @@ -652,6 +669,7 @@ def __init__(self, parent, height, width, field_font, header_font, button_size,
self.step_time = 0
self.session_started = False
self.changed_protocol = True
self.__connected = False
self.r_ble_1_3_value, self.r_ble_4_6_value, self.r_ble_7_9_value, self.r_ble_10_12_value = 0, 0, 0, 0
self.l_ble_1_3_value, self.l_ble_4_6_value, self.l_ble_7_9_value, self.l_ble_10_12_value = 0, 0, 0, 0
if ble_thresh[0] and ble_thresh[1]:
Expand Down Expand Up @@ -754,15 +772,15 @@ def __init__(self, parent, height, width, field_font, header_font, button_size,
label.place(x=int(width * 0.6) + int(slider_count * slider_separation), y=slider_separation_h, anchor=N)
temp_slider = Scale(parent, orient="vertical", variable=slider_vars[i][1], showvalue=False,
command=slider_vars[i][0], length=int(height * 0.35), from_=255, to=0)
temp_slider.place(x=int(width * 0.6) + int(slider_count * slider_separation), y=slider_separation_h + 20, anchor=N)
temp_slider.place(x=int(width * 0.6) + int(slider_count * slider_separation), y=slider_separation_h + 25, anchor=N)
self.slider_objects.append(temp_slider)
slider_count += 1
slider_separation_h = 40
label = Label(parent, text="Freq", font=field_font, anchor=CENTER, width=6)
label.place(x=int(width * 0.60) - slider_separation, y=slider_separation_h, anchor=N)
self.freq_slider = Scale(parent, orient="vertical", variable=slider_vars[12][1], showvalue=False,
command=slider_vars[12][0], length=int(height * 0.75), from_=7, to=0)
self.freq_slider.place(x=int(width * 0.60) - slider_separation, y=slider_separation_h + 20, anchor=N)
self.freq_slider.place(x=int(width * 0.60) - slider_separation, y=slider_separation_h + 25, anchor=N)

self.calibrate_button = Button(parent, text='Calibrate Vibrotactor Threshold', font=field_font,
command=self.__calibrate_ble)
Expand All @@ -773,8 +791,11 @@ def __init__(self, parent, height, width, field_font, header_font, button_size,
self.__disable_ui_elements()
self.ble_dir = os.path.join(self.session_dir, "BLE")
if os.path.exists(self.ble_dir):
latest_protocol = max(pathlib.Path(self.ble_dir).glob("*.json"), key=lambda f: f.stat().st_ctime)
self.__load_protocol_from_file(latest_protocol)
try:
latest_protocol = max(pathlib.Path(self.ble_dir).glob("*.json"), key=lambda f: f.stat().st_ctime)
self.__load_protocol_from_file(latest_protocol)
except ValueError:
print("WARNING: Protocol folder exists with no protocols!")
# endregion

def __enable_ui_elements(self):
Expand All @@ -794,8 +815,10 @@ def disable_ui_elements(self):
self.prot_load_button.config(state='disabled')
self.calibrate_button.config(state='disabled')

def __disable_ui_elements(self):
def __enable_connect_button(self):
self.ble_connect_button.config(state='active')

def __disable_ui_elements(self):
self.ble_disconnect_button.config(state='disabled')
self.freq_slider.config(state='disabled')
for slider in self.slider_objects:
Expand Down Expand Up @@ -829,6 +852,9 @@ def stop_session(self):
self.left_vta.stop_imu()
self.disconnect_ble()

def is_connected(self):
return self.__connected

def is_calibrated(self):
return self.calibrated

Expand Down Expand Up @@ -859,6 +885,7 @@ def next_protocol_step(self, current_time):
if (self.step_time - current_time) == 0:
self.selected_step += 1
self.__update_ble_protocol()
select_focus(self.prot_treeview, self.prot_treeview_parents[self.selected_step])

def __update_ble_protocol(self):
if self.selected_step + 1 == len(self.protocol_steps):
Expand Down Expand Up @@ -915,6 +942,8 @@ def __load_protocol_from_file(self, selected_file=None):
with open(self.prot_file, 'r') as f:
self.protocol_steps = json.load(f)['Steps']
self.repopulate_treeview()
self.changed_protocol = True
self.prot_save_button['state'] = 'active'
else:
messagebox.showwarning("Warning", "No file selected, please try again!")
except Exception as ex:
Expand Down Expand Up @@ -947,16 +976,23 @@ def __delete_protocol_step(self):
if self.selected_step:
self.protocol_steps.pop(self.selected_step - 1)
self.repopulate_treeview()
self.changed_protocol = True
self.prot_save_button['state'] = 'active'

def __save_protocol_to_file(self):
try:
if self.changed_protocol:
if self.prot_file:
file_dir = os.path.join(self.session_dir, "BLE")
if not os.path.exists(file_dir):
os.mkdir(file_dir)
if pathlib.Path(self.prot_file).parent != file_dir:
self.prot_file = os.path.join(file_dir, pathlib.Path(self.prot_file).name)
file_count = len(glob.glob1(file_dir, "*.json"))
if file_count > 1:
if file_count > 0:
new_file = os.path.join(pathlib.Path(self.prot_file).parent,
pathlib.Path(self.prot_file).stem[:-3] + f"_V{file_count}.json")
'_'.join(pathlib.Path(self.prot_file).stem.split('_')[
:-1]) + f"_V{file_count}.json")
else:
new_file = os.path.join(pathlib.Path(self.prot_file).parent,
pathlib.Path(self.prot_file).stem + f"_V{file_count}.json")
Expand All @@ -965,6 +1001,8 @@ def __save_protocol_to_file(self):
json.dump(x, f)
self.__load_protocol_from_file(selected_file=new_file)
messagebox.showinfo("Success", "Protocol file saved!")
self.changed_protocol = False
self.prot_save_button['state'] = 'disabled'
else:
file_dir = os.path.join(self.session_dir, "BLE")
if not os.path.exists(file_dir):
Expand All @@ -976,6 +1014,8 @@ def __save_protocol_to_file(self):
x = {"Steps": self.protocol_steps}
json.dump(x, f)
messagebox.showinfo("Success", "Protocol file saved!")
self.changed_protocol = False
self.prot_save_button['state'] = 'disabled'
else:
messagebox.showwarning("Warning", "No filename supplied! Can't save, please try again!")
except Exception as ex:
Expand All @@ -984,6 +1024,7 @@ def __save_protocol_to_file(self):
def disconnect_ble(self):
VibrotactorArray.disconnect_ble_devices(self.ble_instance)
self.__disable_ui_elements()
self.__connected = False

def __connect_to_ble(self):
self.ble_connect_thread = threading.Thread(target=self.__connect_ble_thread)
Expand All @@ -1006,6 +1047,7 @@ def __connect_ble_thread(self):
self.left_vta = vta
self.__enable_ui_elements()
messagebox.showinfo("Success!", "Vibrotactor arrays are connected!")
self.__connected = True
break
else:
response = messagebox.askyesno("Error", "Could not connect to both vibrotactor arrays!\nTry again?")
Expand Down
8 changes: 8 additions & 0 deletions session_manager_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,10 @@ def start_session(self):
ble_thresh_r, ble_thresh_l, woodway_thresh = None, None, None
if self.config.get_ble():
if self.ovu.ble_view:
if not self.ovu.ble_view.is_connected():
messagebox.showwarning("Warning", "Vibrotactors must be connected before starting session!")
print("WARNING: Vibrotactors must be connected before starting session")
return
if not self.ovu.ble_view.is_calibrated():
messagebox.showwarning("Warning", "Vibrotactors must be calibrated before starting session!")
print("WARNING: Vibrotactors must be calibrated before starting session")
Expand All @@ -363,6 +367,10 @@ def start_session(self):
return
if self.config.get_woodway():
if self.ovu.woodway_view:
if not self.ovu.woodway_view.is_connected():
messagebox.showwarning("Warning", "Woodway must be connected before starting session!")
print("WARNING: Woodway must be connected before starting session")
return
if not self.ovu.woodway_view.is_calibrated():
messagebox.showwarning("Warning", "Woodway must be calibrated before starting session!")
print("WARNING: Woodway must be calibrated before starting session")
Expand Down
2 changes: 2 additions & 0 deletions tkinter_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,7 @@ def calibration_thread(self, start_value, increasing):
for i in range(0, 4):
time.sleep(0.25)
if not self.calibrating:
self.calibrated_speed_increasing = np.average(self.woodway.get_speeds())
self.woodway.set_speed(float(0.0), float(0.0))
return
else:
Expand All @@ -613,6 +614,7 @@ def calibration_thread(self, start_value, increasing):
for i in range(0, 4):
time.sleep(0.25)
if not self.calibrating:
self.calibrated_speed_decreasing = np.average(self.woodway.get_speeds())
self.woodway.set_speed(float(0.0), float(0.0))
if self.calibrated_speed_increasing and self.calibrated_speed_decreasing:
self.calibrated_speed = np.average([self.calibrated_speed_increasing,
Expand Down
2 changes: 1 addition & 1 deletion ui_params.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
cometrics_version = "1.1.3"
cometrics_version = "1.1.4"
ui_title = f"cometrics v{cometrics_version}"

cometrics_data_root = fr'C:\cometrics'
Expand Down

0 comments on commit 1d5e34d

Please sign in to comment.