diff --git a/README.md b/README.md index 48098cfad41518e5a5a9eb31b8824d5924668bd9..042b5e9c9ace33dcab4b6ac272c79684a8e52e0d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,165 @@ # Data collector -Ici on fera la documentation utilisateur ^^ +This tool is made to collect sensor data from KUKA KR 3 R540 robotic arm. +It collects the system vairables via a custom KRL submodule and trace data from +the KUKA Trace module. + + +For each of the 6 motors, the following variables are measured: +- Position +- Torque +- Current draw +- Temperature + +This program tries to collect the data in real time. The main latency factor is +the network quality. An ethernet connection is preffered over Wi-Fi. The data +is buffered on the robot side but has a hard-coded limit of 20000 samples. +The sampling rate has to be configured in accordance of the length of an +acquisition and with the network connection quality. + +The data is acquired from a full-range motion of a motor. Each motor moves +independently from each other. + +The data collected by the program can be found in the data folder, +each data file having an explicit name with the date of the colelction +and details on the configuration. + +**Example** : +`[2024-05-17] 10h57 data [20%-60%] [36ms] [class 0] [10 10 10 10 10 10] - Robot 2` + +**Format** : +`[date] hour data [speed] [sampling rate] [load class] [number of iteration by axis] - N° of the robot` + +**NB** : _The file obtained using KUKA Trace has a `_TRACE` suffix_ + +Summary +--- + +- [UI Description](#ui-description) + - [Collection settings](#collection-settings) + - [KUKA traces](#kuka-traces) + - [Robots loads](#robots-loads) + - [Latency test](#latency-test) + - [Gripper commands](#gripper-commands) + - [Collected data plot](#collected-data-plot) +- [Program Structure](#program-structure) +- [KRL Submodule](#krl-submodule) + - [Global variables](#global-variables) + - `Data_collector.sub` + - [KUKA Trace Configuration](#kuka-trace-configuration) + - The `Axis_Main.src` program + +## UI Description + +The User interface is divided in 6 functions. + + + +### Collection settings + +This section defines the configuration for the data acquisition. + +The following parameters can be configured: +- Number of movement iteration per axis +- Sampling rate +- Speed range for the test + +Due to software limitations from the KRL environment, the sampling rate is +bound to be a multiple of 12. + +The speed of the motors movement varies from min to max with the specified step. +This value is defined as a percentage of the max value (i.e. 0 to 100). +The speed range can be set to constant and thus only one acquisition is made. + +### KUKA traces + +This selectors is used to select the KUKA Trace configuration that will be +run along side of the system variable collection. The collected data is the same +as with the system variables but with more precision and reliability. +The acquisition is done by the RTOS of the robot. + +### Robots loads + +Enter here the load on each robot, with weight or bungee coords +It will be displayed in the result dataset for data processing + +### Latency test + +Launch a latency test on the connected robots to print network timings +Plots a graph of the latency versus time and distributions with histograms + +### Gripper commands + +Command the gripper of the selected robot : open or close + +### Collected data plot + +Accessible without robot connection, this section plots data by selecting +a excel file and hitting corresponding buttons + +## Program Structure + +The code is organized in two main parts in two folders : +[`kuka`](./kuka) and [`ui`](./ui) + +The `kuka` folder contains the classes to communicate with the robot controller +and its varaiables. [`KUKA_Handler`](./kuka/handler.py), +herited from [`openshowvar`](./kuka/kukavarproxy.py), +is responsible of the connection to the robot controler. +[`KUKA_Reader`](./kuka/reader.py) contains all the functions to operate a data +collection, as buffer readings and formating the result into a +[Pandas `DataFrame`](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.html). + +The [`ui`](./ui) folder contains all the classes related to the user interface +of the application. Python files with `ui_` prefixes generate the frames shown +in the main window. The main window is generated by the +[`MainWindow`](./ui/mainwindow.py) class, and latency measurement and robot +measurement are in [`Measure_latency`](./ui/measure_latency.py) and +[`Measure_robot`](./ui/measure_robot.py). +The [`Measure_robot`](./ui/measure_robot.py) class contains the functions to +execute a data collection, +with the dynamic UI in [`CollectionGraphWindow`](./ui/graph_window.py) +showing the robot data buffer state. + +The [`main`](main.py) python file contains the main loop at the basis of the UI. +It is done by a main class, and allows organization with class variables and +class methods instead of global variables and functions. + +## KRL Submodule + +This section describes how to deploy the data collection on a cell. + +**TODO**: Finish this part, add the files to the repo + +### Global variables + +The following declaration must be added in the `System/$config.dat` file +on the robot KUKA workspace : + +``` +... +``` + +### `Data_collector.sub` + +To allow for data collection, please be sure to add the `Data_collector.sub` +to the list of running submodules. This submodules takes up to **3 minutes** to +properly initialize the internal data buffers. + +This progam can be found in [`robot/KRL/`](./robot/KRL). + +### KUKA Trace Configuration + +Copy the provided configuration files found in [`robot/configurations/`](./robot/configurations) to +the `TRACE` folder of the robot. + +**Example path** : `\\192.168.1.151\...\TRACE\` + +### The `Axis_Main.src` program + +The data collection uses the `Axis_Main.src` program to create the data +to collect. It must be running in `AUT` mode at `100%` of run speed to produce +valid data. + +This progam can be found in [`robot/KRL/`](./robot/KRL)`. \ No newline at end of file diff --git a/images/GUI.png b/images/GUI.png new file mode 100644 index 0000000000000000000000000000000000000000..98ed7ea9140616481ac2ca1a8d3ec92e22056369 Binary files /dev/null and b/images/GUI.png differ diff --git a/images/KUKA.jpg b/images/KUKA.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9f010d34a0954ef432060f500a67caa1aab80a91 Binary files /dev/null and b/images/KUKA.jpg differ diff --git a/main.py b/main.py index cb09748433651549bedc687eefcce69a8731c5f3..609bcf3357aba0546bf15631b3fefb8c51f2e9d1 100644 --- a/main.py +++ b/main.py @@ -216,15 +216,15 @@ class MainProgram (MainWindow): self.data.enable_plot_buttons(False) def trace_selected_variables (self): - if(self.collection_settings._do_tq): + if(self.data._do_tq): self.dataframe.plot(x="Sample_time_s",y=[f"Torque_A{i}" for i in range(1,7,1)], grid=True),plt.ylabel("Motor Torque (N.m)") - if(self.collection_settings._do_curr): + if(self.data._do_curr): self.dataframe.plot(x="Sample_time_s",y=[f"Current_A{i}" for i in range(1,7,1)], grid=True),plt.ylabel("Motor Current (%)") - if(self.collection_settings._do_temp): + if(self.data._do_temp): self.dataframe.plot(x="Sample_time_s",y=[f"Temperature_A{i}" for i in range(1,7,1)], grid=True),plt.ylabel("Motor Temperature (°K)") - if(self.collection_settings._do_posact): - self.dataframe.plot(x="Sample_time_s",y=[f"Command_A{i}" for i in range(1,7,1)], grid=True),plt.ylabel("Robot command position in grid (mm))") - if(self.collection_settings._do_posreal): + if(self.data._do_posact): + self.dataframe.plot(x="Sample_time_s",y=[f"Position_Command_A{i}" for i in range(1,7,1)], grid=True),plt.ylabel("Robot command position in grid (mm))") + if(self.data._do_posreal): self.dataframe.plot(x="Sample_time_s",y=[f"Position_A{i}" for i in range(1,7,1)], grid=True),plt.ylabel("Real Robot position in grid (mm))") plt.pause(0.1) # Alternative to plt.show() that is not blocking @@ -272,8 +272,8 @@ class MainProgram (MainWindow): if event == '-doconst_speed-': self.collection_settings.update_speed_entry() - if 'domeas' in event: - self.collection_settings.update_domeas() + if '-do_' in event: + self.data.update_do() ## ---- Robot selector Events ---- ## if "rob_select" in event: diff --git a/main_refactor.py b/main_refactor.py index cb09748433651549bedc687eefcce69a8731c5f3..4a4ba6e6f2adfe03786eeb8132d92fcdaf34d782 100644 --- a/main_refactor.py +++ b/main_refactor.py @@ -272,8 +272,8 @@ class MainProgram (MainWindow): if event == '-doconst_speed-': self.collection_settings.update_speed_entry() - if 'domeas' in event: - self.collection_settings.update_domeas() + if 'do' in event: + self.collection_settings.update_do() ## ---- Robot selector Events ---- ## if "rob_select" in event: diff --git a/robot/KRL/placeholder.txt b/robot/KRL/placeholder.txt new file mode 100644 index 0000000000000000000000000000000000000000..22cbe50235f4b1595bcc417bf415dd0f106893a1 --- /dev/null +++ b/robot/KRL/placeholder.txt @@ -0,0 +1 @@ +This file is meant to be removed \ No newline at end of file diff --git a/robot/configurations/placeholder.txt b/robot/configurations/placeholder.txt new file mode 100644 index 0000000000000000000000000000000000000000..0d4904a3c93b6274553fe5ba10ad87f053fd14b5 --- /dev/null +++ b/robot/configurations/placeholder.txt @@ -0,0 +1 @@ +This file is meant to be removed later \ No newline at end of file diff --git a/temp/__ui.py b/temp/__ui.py index 92fdd82754cb1b860bb219ae0205af81d53bccc2..8edc1173b3ef650aa82abfe3b80388b6155bcfa4 100644 --- a/temp/__ui.py +++ b/temp/__ui.py @@ -52,11 +52,11 @@ class UI_Collection_Settings (sg.Frame): for i in range(1,7): self._input_iterations.append(sg.InputText('1',key=f'num_of_iter{i}',size=(5,1), font=("Consolas", 10))) - self.domeas_tq = sg.Checkbox('$TORQUE_AXIS_ACT', key='-domeas_tq-', size=(25, 1), enable_events=True, default=True) - self.domeas_curr = sg.Checkbox('$CURR_ACT', key='-domeas_curr-', size=(25, 1), enable_events=True, default=True) - self.domeas_tptr = sg.Checkbox('$MOT_TEMP', key='-domeas_tprt-', size=(25, 1),enable_events=True, default=True) - self.domeas_posact = sg.Checkbox('$AXIS_ACT', key='-domeas_posact-', size=(25, 1), enable_events=True, default=False) - self.domeas_posreal = sg.Checkbox('$AXIS_ACT_MEAS', key='-domeas_posreal-', size=(25, 1), enable_events=True, default=False) + self.do_tq = sg.Checkbox('$TORQUE_AXIS_ACT', key='-do_tq-', size=(25, 1), enable_events=True, default=True) + self.do_curr = sg.Checkbox('$CURR_ACT', key='-do_curr-', size=(25, 1), enable_events=True, default=True) + self.do_tptr = sg.Checkbox('$MOT_TEMP', key='-do_tprt-', size=(25, 1),enable_events=True, default=True) + self.do_posact = sg.Checkbox('$AXIS_ACT', key='-do_posact-', size=(25, 1), enable_events=True, default=False) + self.do_posreal = sg.Checkbox('$AXIS_ACT_MEAS', key='-do_posreal-', size=(25, 1), enable_events=True, default=False) self.sample_rate = sg.Combo(['12','24', '36', '48', '60'], default_value='12', key='-Sys_sampling-') @@ -74,11 +74,11 @@ class UI_Collection_Settings (sg.Frame): [ sg.Text("Number of iterations from axis A1 to A6 : "), sg.Push(), *self._input_iterations, sg.Button("All = A1",key='-equal_iter-',font=("Consolas", 10)) ], [ [sg.Text('Variables to collect :')], - [self.domeas_tq, sg.Text('Motor torque of an axis (torque mode)')], - [self.domeas_curr, sg.Text('Motor current of an axis')], - [self.domeas_tptr, sg.Text('Motor temperature of an axis (~ precision)')], - [self.domeas_posact, sg.Text('Axis-specific setpoint position of the robot')], - [self.domeas_posreal, sg.Text('Axis-specific real position of the robot')] + [self.do_tq, sg.Text('Motor torque of an axis (torque mode)')], + [self.do_curr, sg.Text('Motor current of an axis')], + [self.do_tptr, sg.Text('Motor temperature of an axis (~ precision)')], + [self.do_posact, sg.Text('Axis-specific setpoint position of the robot')], + [self.do_posreal, sg.Text('Axis-specific real position of the robot')] ], [ sg.Text('Variable sampling rate (ms):'), self.sample_rate], [ @@ -128,15 +128,15 @@ class UI_Collection_Settings (sg.Frame): return self._robot_status[cell] = self._connected if connected else self._not_connected - def update_domeas (self): + def update_do (self): """_summary_ - Update the state of the variables domeas_XXX if a checkbox is clicked + Update the state of the variables do_XXX if a checkbox is clicked """ - self._do_tq = bool(self.domeas_tq.get()) - self._do_curr = bool(self.domeas_curr.get()) - self._do_temp = bool(self.domeas_tptr.get()) - self._do_posact = bool(self.domeas_posact.get()) - self._do_posreal = bool(self.domeas_posreal.get()) + self._do_tq = bool(self.do_tq.get()) + self._do_curr = bool(self.do_curr.get()) + self._do_temp = bool(self.do_tptr.get()) + self._do_posact = bool(self.do_posact.get()) + self._do_posreal = bool(self.do_posreal.get()) def check_configuration(self): """_summary_ @@ -183,11 +183,11 @@ class UI_Collection_Settings (sg.Frame): """ self._disabled = v - self.domeas_tq.update(disabled=v) - self.domeas_curr.update(disabled=v) - self.domeas_tptr.update(disabled=v) - self.domeas_posact.update(disabled=v) - self.domeas_posreal.update(disabled=v) + self.do_tq.update(disabled=v) + self.do_curr.update(disabled=v) + self.do_tptr.update(disabled=v) + self.do_posact.update(disabled=v) + self.do_posreal.update(disabled=v) self._input_dataset_name.update(disabled=v, text_color="#000") self._input_working_dir.update(disabled=v, text_color="#000") diff --git a/temp/main.py b/temp/main.py index a8e8a57a04467cc3be33f57a3917fe1b2546986d..e1165c9bb80ce72f4ff2665fedd4619e3c0715ef 100644 --- a/temp/main.py +++ b/temp/main.py @@ -181,8 +181,8 @@ while True: if event == '-doconst_speed-': window.collection_settings.update_speed_entry() - if 'domeas' in event: - window.collection_settings.update_domeas() + if 'do' in event: + window.collection_settings.update_do() ######################## Robot Selector Events ################# if "rob_select" in event: cell = int(event.split(':')[1][:-1]) diff --git a/ui/__init__.py b/ui/__init__.py index d1ae18cf76944ed6a85a06bc8878ad7af04c1109..d25840128b226d606fbc16c7c7bd2de98b9c1d4d 100644 --- a/ui/__init__.py +++ b/ui/__init__.py @@ -1,11 +1,11 @@ -from .ui_colelction_settings import UI_Collection_Settings +from .ui_collection_settings import UI_Collection_Settings from .ui_data import UI_Data from .ui_gripper import UI_Gripper from .ui_latency import UI_Latency from .ui_robot_load import UI_RobotLoad from .ui_trace import UI_KUKATrace from .mainwindow import MainWindow -from .ui_graph_window import CollectionGraphWindow +from .graph_window import CollectionGraphWindow from .measure_latency import Measure_latency from .measure_robot import Measure_robot diff --git a/ui/ui_graph_window.py b/ui/graph_window.py similarity index 100% rename from ui/ui_graph_window.py rename to ui/graph_window.py diff --git a/ui/mainwindow.py b/ui/mainwindow.py index 680be6ee4db50cbbc8c7a085a5b2e81660ebd85f..4911269504767cc6085e5afdeb0e02096a353fdb 100644 --- a/ui/mainwindow.py +++ b/ui/mainwindow.py @@ -1,6 +1,6 @@ import PySimpleGUI as sg -from .ui_colelction_settings import UI_Collection_Settings +from .ui_collection_settings import UI_Collection_Settings from .ui_data import UI_Data from .ui_gripper import UI_Gripper from .ui_latency import UI_Latency diff --git a/ui/measure_latency.py b/ui/measure_latency.py index afbba87e4e5c4361e1b727ba2d616ab865c4e6c6..96a423ae14c646cf27e165af2a57cb8425b37638 100644 --- a/ui/measure_latency.py +++ b/ui/measure_latency.py @@ -5,21 +5,43 @@ import traceback from time import time_ns class Measure_latency(sg.Window): + """Measurement window for network latency + """ + + # Flag to close the progress window when done measuring_delay_done = False - N = 100 - M = 12 + + # Test parameters + N = 100 # Number of requests + M = 12 # Size of the array i = 0 + + # Buffers acc_time_individual_request = None acc_time_1 = None - acc_time_16 = None + acc_time_M = None + + # X-Axis x = None x2 = None def __init__ (self, name: str, *args, **kwargs): + """ : Class constructor + With __make_layout, generate a pysimplegui window to be shown in parallel + when a latency measure sequence on a robot is lauched + plots latency graphs for single variable and an array of a given size + Return : sg.Window + """ self.name = name super().__init__("Measure Running", self.__make_layout(), *args, *kwargs) def __make_layout (self): + """Creates this window Layout + + Returns: + List[]: This window layout + """ + self._layout = [ [sg.Text('Measuring TCP/IP communication latency...')], [sg.Push(), sg.ProgressBar(self.N, orientation='h', size=(20, 20), key='-progress-'), sg.Push()], @@ -28,30 +50,43 @@ class Measure_latency(sg.Window): return self._layout def measure_latency(self, robot): - self.acc_time_16 = np.zeros(self.N) + """Runs a latency measurement + Args: + robot (KUKA_Handler): The robot handler + """ + + # Preparing the buffers + self.acc_time_M = np.zeros(self.N) self.acc_time_1 = np.zeros(self.N) self.acc_time_individual_request = np.ones(self.M * self.N) * 12 + # Preparing the graphs x axis self.x = np.arange(0, self.N) self.x2 = np.arange(0, self.N, 1/self.M) try: for self.i in range (self.N): + + # Reading a whole array at once a = time_ns() robot.KUKA_ReadVar("__TAB_1[]") b = time_ns() time_16 = (b-a) / 1e6 - self.acc_time_16[self.i] = (time_16) + # Recording the request time + self.acc_time_M[self.i] = (time_16) + # Reading an array, one index at a time a = time_ns() for i in range(self.M): c = time_ns() robot.KUKA_ReadVar(f"__TAB_1[{i}]") d = time_ns() + # Recording the individual request time self.acc_time_individual_request[self.i * self.M + i] = (d - c) / 1e6 b = time_ns() time_1 = (b-a) / 1e6 + # Recording the whole array request time self.acc_time_1[self.i] = (time_1) self.measuring_delay_done = True @@ -62,11 +97,15 @@ class Measure_latency(sg.Window): traceback.print_exception(e) def _print_results (self): + """Shows the results of the latency measurement to the user using + `matplotlib` + """ + plt.figure(figsize=(12.8,6.4)) plt.subplot(2,3,(1,3)) plt.plot(self.x2, self.acc_time_individual_request, label="1 var", alpha=0.25) plt.plot(self.x, self.acc_time_1, label=f"{self.M} vars using a for loop") - plt.plot(self.x, self.acc_time_16, label=f"{self.M} vars using an array") + plt.plot(self.x, self.acc_time_M, label=f"{self.M} vars using an array") plt.xlabel("Sample"), plt.ylabel("Response Time (ms)"), plt.legend() plt.subplot(2,3,4) @@ -80,39 +119,29 @@ class Measure_latency(sg.Window): plt.title(f"Distribution for {self.M} vars request using a for loop") plt.subplot(2,3,6) - plt.hist(self.acc_time_16, bins=50) + plt.hist(self.acc_time_M, bins=50) plt.xlabel("Response time (ms)") plt.title(f"Distribution for {self.M} vars array request") plt.suptitle(self.name) plt.tight_layout() plt.pause(0.1) - - # plt.figure() - # plt.subplot(1,2,1) - # df = pd.DataFrame({ - # f"Response Time - {self.M} vars, for loop": self.acc_time_1, - # f"Response Time - {self.M} vars, array": self.acc_time_16 - # }) - # df.boxplot() - - # plt.subplot(1,2,2) - # df = pd.DataFrame({ - # f"Response Time - 1 var": self.acc_time_individual_request, - # }) - # df.boxplot() - # plt.show() def _poll(self): + """Updates this window. MUST BE CALLED BY THE MAIN THREAD. + + Returns: + bool: This window is still opened + """ + event, value = self.read(timeout=10) + if event == sg.WIN_CLOSED or self.measuring_delay_done: self.close() self._print_results() self.measuring_delay_done = False return False - # if event == "--latency-done--": - self['-progress-'].update(self.i + 1) return True \ No newline at end of file diff --git a/ui/measure_robot.py b/ui/measure_robot.py index 3ee58b3f790af8d030b6b0f1cef5afc36b185ecd..fac695183b069f9093435a8d95ae9d390e5d6207 100644 --- a/ui/measure_robot.py +++ b/ui/measure_robot.py @@ -8,18 +8,27 @@ from ui import CollectionGraphWindow from kuka import KUKA_DataReader, KUKA_Handler class Measure_robot (CollectionGraphWindow): + """Measurement window for a robot + """ + + # Flag used to close the window when done collecting_data_done = False - N = 100 # WIP : A definir en fonction de la config - i = 0 + + # Collected data data = None + # Latency data [Not used] latencies = np.zeros(0) - reads = np.zeros(0) - writes = np.zeros(0) - is_tracing = False - def __init__ (self, handler: KUKA_Handler, cell: int, file_prefix: str, temp_dir: str = ".\\temp"): + """Creates a new measurement window + + Args: + handler (KUKA_Handler): The Kuka Handler + cell (int): The number of the cell (from 1 to 3) + file_prefix (str): The prefix for the output file name + temp_dir (str, optional): The temporary working dir for the Kuka Trace parsing. Defaults to ".\temp". + """ super().__init__(cell) @@ -30,6 +39,19 @@ class Measure_robot (CollectionGraphWindow): self.temp_dir = temp_dir def generate_file_name (self, A_iter, speed, sampling, load): + """Creates a suffix for the output file name containing the acquisition + parameters + + Args: + A_iter (List[int]): Number of per-axis iterations + speed (str|int|slice): The speed (range) of the acquisition + sampling (int|str): The sampling rate of the system variables + load (int): The class of the acquisition + + Returns: + str: The standardized suffix + """ + self.settings = "[" + (speed if type(speed) != slice else f'{speed.start}%-{speed.stop}') + "%] " self.settings += f"[{sampling}ms] " self.settings += f"[class {load}] " @@ -39,7 +61,18 @@ class Measure_robot (CollectionGraphWindow): self.file_name = self.file_prefix + " " + self.settings + "- " + self.name return self.file_name - def measure_sequence (self, A_iter, speed, sampling, trace_sampling, load: int = -1, lock: Semaphore = None, done: Callable = None): + def measure_sequence (self, A_iter, speed, sampling, trace_sampling, load: int = 0, lock: Semaphore = None, done: Callable = None): + """Runs the acquisition and saves the result in .xlsx files + + Args: + A_iter (List[int]): The number of iteration for each axis + speed (str|int|slice): The speed (range) of the acquisition + sampling (str|int): The sampling rate of the system variables + trace_sampling (str): The name of the configuration for KUKA Trace + load (int, optional): The class of the acquisition. Defaults to 0. + lock (Semaphore, optional): The semaphore used to sync multiple robots. Defaults to None. + done (Callable, optional): The function used to declare the end of a run. Defaults to None. + """ self.generate_file_name(A_iter, speed, sampling, load) @@ -65,6 +98,9 @@ class Measure_robot (CollectionGraphWindow): self.export_measures() def export_measures (self): + """Exports the internally-stored DataFrames to .xlsx files + """ + file_name = self.file_name + ".xlsx" trace_file_name = self.file_name + "_TRACE" + ".xlsx" @@ -80,13 +116,23 @@ class Measure_robot (CollectionGraphWindow): print("Lost data from " + self.name) def _poll (self): + """Updates this window. MUST BE CALLED BY THE MAIN THREAD. + + Returns: + bool: This window is still opened + """ + event, value = self.read(timeout=10) + if event == sg.WIN_CLOSED or event == '-colexit-': self.close() return False + if self.collecting_data_done : self.collecting_data_done = False self._status.update("Collection Done !",text_color="#0c2") else: self.redraw() + return True + diff --git a/ui/ui_colelction_settings.py b/ui/ui_collection_settings.py similarity index 81% rename from ui/ui_colelction_settings.py rename to ui/ui_collection_settings.py index 37c3b887686027421395b39cffe9e28ff7b6691e..a13926cf3457c9e3f049861acd34ca490954f82b 100644 --- a/ui/ui_colelction_settings.py +++ b/ui/ui_collection_settings.py @@ -9,11 +9,7 @@ class UI_Collection_Settings (sg.Frame): # Class flags _disabled = False _constant_speed = True - _do_tq = True - _do_curr = True - _do_temp = True - _do_posact = False - _do_posreal = False + # Robot staus variables _not_connected = "#99c" _connected = "#3f3" @@ -26,7 +22,7 @@ class UI_Collection_Settings (sg.Frame): sg elements like checkbox and buttons are stored in the class to be accessed easily Return : sg.Frame """ - super().__init__("Collection Settings", self.__make_layout()) + super().__init__("Collection Settings", self.__make_layout(), expand_x=True) def __make_layout (self): @@ -40,14 +36,7 @@ class UI_Collection_Settings (sg.Frame): self._input_iterations = [] for i in range(1,7): self._input_iterations.append(sg.InputText('1',key=f'num_of_iter{i}',size=(5,1), font=("Consolas", 10))) - - self.domeas_tq = sg.Checkbox('$TORQUE_AXIS_ACT', key='-domeas_tq-', size=(25, 1), enable_events=True, default=True) - self.domeas_curr = sg.Checkbox('$CURR_ACT', key='-domeas_curr-', size=(25, 1), enable_events=True, default=True) - self.domeas_tptr = sg.Checkbox('$MOT_TEMP', key='-domeas_tprt-', size=(25, 1),enable_events=True, default=True) - self.domeas_posact = sg.Checkbox('$AXIS_ACT', key='-domeas_posact-', size=(25, 1), enable_events=True, default=False) - self.domeas_posreal = sg.Checkbox('$AXIS_ACT_MEAS', key='-domeas_posreal-', size=(25, 1), enable_events=True, default=False) - self.sample_rate = sg.Combo(['12','24', '36', '48', '60'], default_value='12', key='-Sys_sampling-') self.doconst_speed = sg.Checkbox('Constant', key='-doconst_speed-', size=(25, 1),enable_events=True, default=True) @@ -61,14 +50,6 @@ class UI_Collection_Settings (sg.Frame): [ sg.Text("Dataset name :"), self._input_dataset_name, sg.Button("Auto Name", key="-dataset_auto-name-", font=("Consolas", 10)) ], [ sg.Text("Working directory :"), self._input_working_dir, self._input_browse_dir ], [ sg.Text("Number of iterations from axis A1 to A6 : "), sg.Push(), *self._input_iterations, sg.Button("All = A1",key='-equal_iter-',font=("Consolas", 10)) ], - [ - [sg.Text('Variables to collect :')], - [self.domeas_tq, sg.Text('Motor torque of an axis (torque mode)')], - [self.domeas_curr, sg.Text('Motor current of an axis')], - [self.domeas_tptr, sg.Text('Motor temperature of an axis (~ precision)')], - [self.domeas_posact, sg.Text('Axis-specific setpoint position of the robot')], - [self.domeas_posreal, sg.Text('Axis-specific real position of the robot')] - ], [ sg.Text('Variable sampling rate (ms):'), self.sample_rate], [ sg.Text("Robot speed :"), self.doconst_speed, sg.Push(), @@ -116,16 +97,6 @@ class UI_Collection_Settings (sg.Frame): self._robot_status[cell] = self._errored return self._robot_status[cell] = self._connected if connected else self._not_connected - - def update_domeas (self): - """_summary_ - Update the state of the variables domeas_XXX if a checkbox is clicked - """ - self._do_tq = bool(self.domeas_tq.get()) - self._do_curr = bool(self.domeas_curr.get()) - self._do_temp = bool(self.domeas_tptr.get()) - self._do_posact = bool(self.domeas_posact.get()) - self._do_posreal = bool(self.domeas_posreal.get()) def check_configuration(self): """_summary_ @@ -171,12 +142,6 @@ class UI_Collection_Settings (sg.Frame): v (bool): disable the entries of the entire frame ? """ self._disabled = v - - self.domeas_tq.update(disabled=v) - self.domeas_curr.update(disabled=v) - self.domeas_tptr.update(disabled=v) - self.domeas_posact.update(disabled=v) - self.domeas_posreal.update(disabled=v) self._input_dataset_name.update(disabled=v, text_color="#000") self._input_working_dir.update(disabled=v, text_color="#000") diff --git a/ui/ui_data.py b/ui/ui_data.py index 70da1ed717341457084609fbb5efe96cc2e65abc..c1242bf3a158b1d6e5cdf53de77afe93ac384313 100644 --- a/ui/ui_data.py +++ b/ui/ui_data.py @@ -5,6 +5,12 @@ class UI_Data (sg.Frame): _disabled = False + _do_tq = True + _do_curr = True + _do_temp = True + _do_posact = False + _do_posreal = False + def __init__ (self): """_summary_ : Class constructor With __make_layout, generate a pysimplegui frame to be integrated in the main window for launching measuring sequence and data printing @@ -17,7 +23,7 @@ class UI_Data (sg.Frame): def __make_layout (self): data_path = os.path.dirname(os.path.realpath(__file__)) + "/data" - self._btn_start = sg.Button('START SEQUENCE', key='-BTN_start-', button_color='white', size=(25, 2),expand_x=True) + self._btn_start = sg.Button('START SEQUENCE', key='-BTN_start-', button_color='white', size=(15, 2),expand_x=True) self._input_data = sg.Input(default_text=data_path, key='-data_path-', size=(40, 1), font=("Consolas", 10)) self.import_xlsx = sg.FileBrowse("Browse EXCEL",initial_folder=data_path, file_types=(('Excel Files', '*.xlsx'),), key='-browse_xlsx-') @@ -26,15 +32,30 @@ class UI_Data (sg.Frame): self.trace_selvariables = sg.Button('Trace Selected variables', key='-trace_selvar-', disabled=True, expand_x=True) self.trace_samples = sg.Button('Trace Sample collection history', key='-trace_sample-', disabled=True, expand_x=True) self.trace_latency = sg.Button('Trace Sample latency distribution', key='-trace_latency-', disabled=True, expand_x=True) + + self.do_tq = sg.Checkbox('Motor torque', key='-do_tq-', size=(25, 1), enable_events=True, default=True) + self.do_curr = sg.Checkbox('Motor current', key='-do_curr-', size=(25, 1), enable_events=True, default=True) + self.do_tptr = sg.Checkbox('Motor temperature', key='-do_tprt-', size=(25, 1),enable_events=True, default=True) + self.do_posact = sg.Checkbox('Robot command position', key='-do_posact-', size=(25, 1), enable_events=True, default=False) + self.do_posreal = sg.Checkbox('Robot measured position', key='-do_posreal-', size=(25, 1), enable_events=True, default=False) self._layout = [ [ self._btn_start ], - [sg.Frame("", border_width=0, layout = [ - [ self._input_data, self.import_xlsx, self.open_xlsx ], - [ self.trace_selvariables, self.trace_samples ], - [ self.trace_latency ] - ]) - ] + [ sg.Frame("Quickview of the colelcted data in excel file :", border_width=0, expand_y=True, layout = [ + [ sg.VPush() ], + [ self._input_data, self.import_xlsx, self.open_xlsx ], + [ self.trace_selvariables, self.trace_samples ], + [ self.trace_latency ], + [ sg.VPush() ] + ]), + sg.Frame("Variables to plot :", border_width=0, layout=[ + [self.do_tq], + [self.do_curr], + [self.do_tptr], + [self.do_posact], + [self.do_posreal] + ]), + ], ] return self._layout @@ -59,3 +80,13 @@ class UI_Data (sg.Frame): self.trace_selvariables.update(disabled=not(enable)) self.trace_samples.update(disabled=not(enable)) self.trace_latency.update(disabled=not(enable)) + + def update_do (self): + """_summary_ + Update the state of the variables do_XXX if a checkbox is clicked + """ + self._do_tq = bool(self.do_tq.get()) + self._do_curr = bool(self.do_curr.get()) + self._do_temp = bool(self.do_tptr.get()) + self._do_posact = bool(self.do_posact.get()) + self._do_posreal = bool(self.do_posreal.get()) \ No newline at end of file