Read-Sensor-Resistances/test.py

165 lines
7.8 KiB
Python
Raw Normal View History

2022-05-15 19:48:22 +00:00
from ui import Frame
from read_arduino import *
2022-08-08 22:51:36 +00:00
from serial_plotter import *
2022-08-18 09:56:58 +00:00
from multiprocessing import *
import os, sys, json, wx, importlib, warnings
2022-05-15 19:48:22 +00:00
import serial.tools.list_ports
import numpy as np
import matplotlib.pyplot as plt
2022-05-15 19:48:22 +00:00
warnings.filterwarnings("ignore", category=DeprecationWarning)
# acknowledgement: https://stackoverflow.com/a/68666505. handles splash screens
if '_PYIBoot_SPLASH' in os.environ and importlib.util.find_spec("pyi_splash"):
import pyi_splash
2022-08-18 09:56:58 +00:00
pyi_splash.update_text('UI Loaded ...')
pyi_splash.close()
2022-05-15 19:48:22 +00:00
HEIGHT = 800
WIDTH = 800
#global ani, t1
2022-08-08 22:51:36 +00:00
2023-03-04 05:37:01 +00:00
# TODO: add the option to change the font size of the graph
2022-05-15 19:48:22 +00:00
def main():
#################### USER INPUTS ###########################
2022-08-18 09:56:58 +00:00
r_ref = np.array(
[frame.r_ref_1.GetValue(), frame.r_ref_2.GetValue(), frame.r_ref_3.GetValue(), frame.r_ref_4.GetValue(),
0]) # resisters for each An port, where n is an integer from 0-3. Use 0 if none. in Ohms
r_ref[r_ref == ''] = '0' # correcting the emply values
resistors = r_ref.astype(np.float32).tolist() # convert string to numbers
2022-05-15 19:48:22 +00:00
input_voltage = float(frame.input_voltage.GetValue())
2023-02-07 06:57:05 +00:00
# bit_rate = float(frame.adjusted_volt.GetValue())
2022-05-15 19:48:22 +00:00
port = frame.dev_list.GetValue()
2023-02-07 06:57:05 +00:00
# typical window size: 50
2022-05-15 19:48:22 +00:00
window_size = int(frame.m_textCtrl26.GetValue())
2023-03-07 05:15:07 +00:00
font_size = int(frame.fontSize.GetValue())
2022-05-15 19:48:22 +00:00
#################### END USER INPUTS ########################
# filename, disable customize name but should inform the user the current name of the data file on the front end
# alternatively, could be default input if no other input given. But if there is might have problem...
dat_folder = "RecordedData"
os.makedirs(dat_folder, exist_ok=True)
filename = os.path.join(os.getcwd(), dat_folder, f"{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.csv")
2022-08-18 09:56:58 +00:00
delay = 1000 # millisec per data point, defined in the firmware
# for now we have 5 reference resistors, there is an extra one (we set 4 on the UI) just in case if needed in the future
2022-05-15 19:48:22 +00:00
if not (len(resistors) == 5):
raise ValueError(f"expecting 5 resistor values, but got {len(resistors)}!!!")
2023-03-07 05:15:07 +00:00
gen_settings(resistors, input_voltage, port, filename, window_size, font_size, delay)
2022-05-15 19:48:22 +00:00
2023-03-07 05:15:07 +00:00
def gen_settings(resistors, input_voltage, port, filename, window_size, font_size, delay):
2022-05-15 19:48:22 +00:00
"""
2023-04-17 03:17:45 +00:00
export all inputs from main() to a .json file. We have everything stored in a .json file instead of having the program to call
the values from the UI directly because 1. we dont have to import the ui.py all the time (so we can freely change ui.py to whatever
else and only need to modify this one) and 2. we can have other program to access one variable from the ui at the same time. This adds
flexibility to the program. We parameterize all the value for the same reason.
2022-08-18 09:56:58 +00:00
:param resistors: list of reference resistances of the sensors
:param input_voltage: Vin of the voltage divider
:param bits: ADC resolution for the board. Usualy 12-bits for ESP32 (and 10 bits for Arduino Mega)
:param port: serial port the board is connected to on the computer
:param filename: file name and absolute directory which the serial data will be recorded to
:param window_size: numbers of x values to display on the graph, or the x interval
:param delay: amount of time to wait for a task's next start. 1000 ms for now to be consistent with the firmware
2022-05-15 19:48:22 +00:00
"""
name = "settings.json"
settings = {}
settings["refRes"] = resistors
settings["sensor_ports"] = np.where(np.array(resistors) > 0)[0].tolist()
settings["v_in"] = input_voltage
2022-05-15 19:48:22 +00:00
settings["port"] = port
2023-02-07 06:57:05 +00:00
# settings["resolution"] = bits
2022-05-15 19:48:22 +00:00
settings["winSize"] = window_size
2023-03-07 05:15:07 +00:00
settings["fontSize"] = font_size
2022-05-15 19:48:22 +00:00
settings["file_name"] = filename
settings["delay"] = delay
2023-03-06 06:14:29 +00:00
settings["font_size"] = frame.fontSize.GetValue()
global sensors
sensors = len(settings['sensor_ports'])
2023-04-14 11:20:34 +00:00
# TODO: get the input voltage from the UI and save it to the settings.json
2022-05-15 19:48:22 +00:00
open(name, 'w').write(json.dumps(settings, indent=4))
open(filename, "a", newline="", encoding="utf-8")
2022-08-12 07:33:27 +00:00
def run(e):
"""
2023-04-17 03:17:45 +00:00
run the read_arduino.py and Serial Plotter in parallel, but the plotter is in the main thread and other one can be in a separate thread, since
matplot's FuncAnimation doesn't like to not be in the main thread (Big L for them..)
2022-08-12 07:33:27 +00:00
"""
global ani_1, t1, ani_2, ani_3, ani_4 # the variables to call the plotter and the read_arduino.py, we want them to get tossed around between functions and even programs
2022-08-12 07:33:27 +00:00
main()
if 't1' in globals():
t1.terminate() # end the previous serial reads, if there is any
2022-08-18 09:56:58 +00:00
2022-08-12 07:33:27 +00:00
# place the read() function from read_arduino into another process to run it in background
t1 = Process(target=read, args=())
t1.start()
# run the plotter. Note that we should not put the plotter class, or function, in another process since
2022-08-12 07:33:27 +00:00
# matplot's FuncAnimation doesn't like that
2022-08-18 09:56:58 +00:00
plotter = SerialPlotter(frame)
if (sensors == 1) :
ani_1 = plotter.plotting_1()
elif (sensors == 2):
ani_1 = plotter.plotting_1()
ani_2 = plotter.plotting_2()
elif (sensors == 3) :
ani_1 = plotter.plotting_1()
ani_2 = plotter.plotting_2()
ani_3 = plotter.plotting_3()
elif (sensors == 4) :
ani_1 = plotter.plotting_1()
ani_2 = plotter.plotting_2()
ani_3 = plotter.plotting_3()
ani_4 = plotter.plotting_4()
#plotter.fig_1.show()
#plotter.fig_2.show()
plt.show()
2022-05-15 19:48:22 +00:00
2022-08-12 07:33:27 +00:00
frame.btLaunch.SetLabelText("Plot") # change the text on the "launch" button to "plot"
2022-05-15 19:48:22 +00:00
if not frame.show_msg.GetValue():
frame.Hide()
2022-07-17 20:29:44 +00:00
2022-05-15 19:48:22 +00:00
if __name__ == '__main__':
2022-08-18 09:56:58 +00:00
console_title = 'Program Crashed! Here are the details' # this is there for now to redirect any errors
# Acknowledgement: https://stackoverflow.com/a/27694505
if sys.platform.startswith('win'):
# On Windows calling this function is necessary.
freeze_support()
2022-08-18 09:56:58 +00:00
app = wx.App(useBestVisual=True)
2022-05-15 19:48:22 +00:00
frame = Frame(None)
2022-08-18 09:56:58 +00:00
app.SetTopWindow(frame)
# set where the uncaught errors should be displayed
console = wx.PyOnDemandOutputWindow(console_title)
console.SetParent(frame)
sys.stderr = console
2023-04-17 03:17:45 +00:00
# instrad of print(), add something similar `sys.stdout = console` (without the two `) to print to allow us redirect the output elsewhere
# BUT it only redirects the print() in this program, not the print() on other modules, like read_arduino.py
print("this is an Beta version of the design. This debug terminal will be gone on official release\n")
2023-04-14 11:20:34 +00:00
ports = [f"{comport.device}: {comport.description}" for comport in serial.tools.list_ports.comports()] # get all available ports
frame.dev_list.AppendItems(ports)
2023-04-14 11:20:34 +00:00
frame.SetTitle("SeeDatResistance - Beta 0.1.2")
2022-05-15 19:48:22 +00:00
frame.btLaunch.Bind(wx.EVT_BUTTON, run)
if os.path.isfile("settings.json"):
2022-08-18 09:56:58 +00:00
try:
2023-04-17 03:17:45 +00:00
print("Found existing settings.json, auto-fill previous inputs!\n")
2022-08-18 09:56:58 +00:00
settings = json.load(open('settings.json', 'r'))
frame.r_ref_1.SetValue(str(settings["refRes"][0]))
frame.r_ref_2.SetValue(str(settings["refRes"][1]))
frame.r_ref_3.SetValue(str(settings["refRes"][2]))
frame.r_ref_4.SetValue(str(settings["refRes"][3]))
frame.input_voltage.SetValue(str(settings["v_in"]))
2023-02-07 06:57:05 +00:00
# frame.adjusted_volt.SetValue(str(settings["resolution"]))
2022-08-18 09:56:58 +00:00
frame.m_textCtrl26.SetValue(str(settings["winSize"]))
if settings["port"] in ports: # auto-select device port if exist
frame.dev_list.SetValue(settings["port"])
except json.decoder.JSONDecodeError: # invalid settings file, ignore
pass
2022-05-15 19:48:22 +00:00
frame.Show()
app.MainLoop()
2022-08-12 07:33:27 +00:00
if 't1' in globals():
t1.terminate() # gracefully end the read_arduino process if it has been started