Skip to content

Commit

Permalink
v0.2
Browse files Browse the repository at this point in the history
  • Loading branch information
Paolo-Beci committed May 9, 2024
1 parent 03a1ae2 commit e5e9af8
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 68 deletions.
68 changes: 50 additions & 18 deletions backend/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,37 @@
LOCALHOST_IP = "127.0.0.1"
BUFFER_SIZE = 1024

# Global variables
udp_socket = None
cannelloni_thread0 = None
cannelloni_thread1 = None
cannellonipy_handle0 = None
cannellonipy_handle1 = None
data_thread = None
is_running = None

# Controller
def start_connection_controller(IP_SCANNER, CAN0_PORT, CAN1_PORT, UDP_PORT, PATH_DBC_CAN0, PATH_DBC_CAN1, label_connected, connect_button):
def start_connection_controller(IP_SCANNER, CAN0_PORT, CAN1_PORT, UDP_PORT, PATH_DBC_CAN0, PATH_DBC_CAN1, label_connected, connect_button, disconnect_button):
global udp_socket, cannelloni_sockets, is_running, data_thread
udp_socket = open_stream_udp(int(UDP_PORT))
cannelloni_sockets = open_stream_cannelloni(IP_SCANNER, CAN0_PORT, CAN1_PORT)
time.sleep(1)

if udp_socket and cannelloni_sockets[0].udp_pcb and cannelloni_sockets[1].udp_pcb:
print("Connection established")
read_data_cannelloni(udp_socket, cannelloni_sockets, PATH_DBC_CAN0, PATH_DBC_CAN1, UDP_PORT)
label_connected.pack()
is_running = True
label_connected.grid(row=19, column=1, columnspan=10)
connect_button.config(state="disabled")
disconnect_button.config(state="active")
data_thread = threading.Thread(target=read_data_cannelloni, args=(udp_socket, cannelloni_sockets, PATH_DBC_CAN0, PATH_DBC_CAN1, UDP_PORT), daemon=True)
data_thread.start()
else:
print("Connection failed")
disconnect()
print("Failed to establish connection, disconnecting...")

# Opens a stream from the specified scanner IP and ports
def open_stream_cannelloni(IP_SCANNER, CAN0_PORT, CAN1_PORT):
global cannelloni_thread0, cannelloni_thread1, cannellonipy_handle0, cannellonipy_handle1
try:
# Create a cannellonipy handle
cannellonipy_handle0 = CannelloniHandle()
Expand Down Expand Up @@ -60,38 +75,34 @@ def open_stream_udp(UDP_PORT):
# Stream JSON data to PlotJuggler via UDP
def send_stream_to_plotjuggler(udp_socket, json_data, UDP_PORT):
try:
print(f"Sending JSON data: {json_data}")
# Serialize the JSON data to a string
json_string = json.dumps(json_data)
# Encode the JSON string to bytes
json_bytes = json_string.encode('utf-8')
json_bytes = json.dumps(json_data).encode('utf-8')
# Send JSON data to the server
udp_socket.sendto(json_bytes, (LOCALHOST_IP, int(UDP_PORT))) # Check with cmd: nc -ul <UDP_PORT>
print(f"Sending JSON data: {json_data}") #DEBUG

except Exception as e:
print(f"Error sending data via UDP: {e}")

# Read data from the SCanner via cannelloni
def read_data_cannelloni(udp_socket, cannelloni_sockets, PATH_DBC_CAN0, PATH_DBC_CAN1, UDP_PORT):
global is_running
try:
while True:
while is_running:
# Get the received frames form cannelloni
received_frames_can0 = cannelloni_sockets[0].get_received_can_frames()
received_frames_can1 = cannelloni_sockets[1].get_received_can_frames()

# Convert Cannelloni data to JSON
json_data0 = cannelloni_to_json(received_frames_can0, PATH_DBC_CAN0)
json_data1 = cannelloni_to_json(received_frames_can1, PATH_DBC_CAN1)

# Merge the two JSON objects streams
if json_data0 and json_data1:
if received_frames_can0 and received_frames_can1:
# Convert Cannelloni data to JSON
json_data0 = cannelloni_to_json(received_frames_can0, PATH_DBC_CAN0)
json_data1 = cannelloni_to_json(received_frames_can1, PATH_DBC_CAN1)

json_data = {**json_data0, **json_data1}
print(json_data)
# Send the JSON data to PlotJuggler via UDP
send_stream_to_plotjuggler(udp_socket, json_data, UDP_PORT)
else:
print("No data received")
return

except Exception as e:
print(f"Error reading data from SCanner: {e}")
Expand Down Expand Up @@ -134,9 +145,30 @@ def cannelloni_to_json(frame_cannelloni, dbc_path):
except Exception as e:
print(f"Error converting Cannelloni data to JSON: {e}")

# Disconnect from the serial and UDP servers
def disconnect():
global udp_socket, cannelloni_thread0, cannelloni_thread1, data_thread, is_running, cannellonipy_handle0, cannellonipy_handle1
try:
print("Disconnecting...")
if udp_socket:
# Close the app UDP socket
udp_socket.close()
if cannelloni_thread0 and cannelloni_thread1:
# Close the cannellonipy UDP sockets
cannellonipy_handle0.udp_pcb.close()
cannellonipy_handle1.udp_pcb.close()
# Join the threads
cannelloni_thread0.join()
cannelloni_thread1.join()
if data_thread:
# Join the read data thread
is_running = False
data_thread.join()
except Exception as e:
print(f"Error disconnecting: {e}")


# CANNELLONI DATA FORMAT
# ------------- CANNELLONI DATA FORMAT -------------
# ## Data Frames
# Each data frame can contain several CAN frames.
# The header of a data frame contains the following
Expand Down
2 changes: 1 addition & 1 deletion cannellonipy
Submodule cannellonipy updated 1 files
+5 −0 cannellonipy.py
125 changes: 76 additions & 49 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import tkinter as tk
from tkinter import filedialog
from backend.functions import start_connection_controller
from backend.functions import start_connection_controller, disconnect
import threading
import time

class GUI:
def __init__(self):
Expand All @@ -17,8 +18,8 @@ def __init__(self):
self.CAN1_PORT = 5000
self.UDP_PORT = 9870

# Flag
connected = False
global controller_thread
controller_thread = None

# ---------- WINDOW SETTINGS ---------------------------------------
self.app.title("SCanner Adapter")
Expand All @@ -27,83 +28,89 @@ def __init__(self):
self.screen_width = self.app.winfo_screenwidth()
self.screen_height = self.app.winfo_screenheight()
self.window_width = 500
self.window_height = 620
self.window_height = 640
self.x_coordinate = (self.screen_width - self.window_width) // 2
self.y_coordinate = (self.screen_height - self.window_height) // 2

# Set the geometry of the window to position it on the center of the screen
self.app.geometry(f"{self.window_width}x{self.window_height}+{self.x_coordinate}+{self.y_coordinate}")
self.frame = tk.Frame(self.app)
self.frame.pack(pady=20)

# ---------- GUI ELEMENTS --------------------------------------------
self.label = tk.Label(self.app, text="Hello, this is the SCanner Adapter! \nYou have to enter all the required values on the form below \n and than press connect!")
self.label.pack(pady=10)
self.label = tk.Label(self.frame, text="Hello, this is the SCanner Adapter! \nYou have to enter all the required values on the form below \n and than press connect!")
self.label.grid(row=0, column=1, columnspan=10, pady=10)

# Path to CONFIG
self.label_path_config_model = tk.Label(self.app, text="Select path to CONFIG model to use:")
self.label_path_config_model.pack()
self.textbox_path_config_model = tk.Text(self.app, height=1, width=50)
self.textbox_path_config_model.pack()
self.label_path_config_model = tk.Label(self.frame, text="Select path to CONFIG model to use:")
self.label_path_config_model.grid(row=1, column=1, columnspan=10)
self.textbox_path_config_model = tk.Text(self.frame, height=1, width=50)
self.textbox_path_config_model.grid(row=2, column=1, columnspan=10)
self.textbox_path_config_model.insert("1.0", self.PATH_CONFIG_MODEL)
self.browse_button_config_model = tk.Button(self.app, text="Browse", command=self.browse_file_config_model)
self.browse_button_config_model.pack(pady=10)
self.browse_button_config_model = tk.Button(self.frame, text="Browse", command=self.browse_file_config_model)
self.browse_button_config_model.grid(row=3, column=1, columnspan=10, pady=10)

# IP Scanner
self.label_ip_scanner = tk.Label(self.app, text="IP SCanner:")
self.label_ip_scanner.pack()
self.textbox_ip_scanner = tk.Text(self.app, height=1, width=30)
self.label_ip_scanner = tk.Label(self.frame, text="IP SCanner:")
self.label_ip_scanner.grid(row=4, column=1, columnspan=10)
self.textbox_ip_scanner = tk.Text(self.frame, height=1, width=30)
self.textbox_ip_scanner.insert("1.0", self.IP_SCANNER)
self.textbox_ip_scanner.pack()
self.textbox_ip_scanner.grid(row=5, column=1, columnspan=10)

# CAN0 Port
self.label_can0_port = tk.Label(self.app, text="CAN0 port:")
self.label_can0_port.pack()
self.textbox_can0_port = tk.Text(self.app, height=1, width=30)
self.label_can0_port = tk.Label(self.frame, text="CAN0 port:")
self.label_can0_port.grid(row=6, column=1, columnspan=10)
self.textbox_can0_port = tk.Text(self.frame, height=1, width=30)
self.textbox_can0_port.insert("1.0", self.CAN0_PORT)
self.textbox_can0_port.pack()
self.textbox_can0_port.grid(row=7, column=1, columnspan=10)

# CAN1 Port
self.label_can1_port = tk.Label(self.app, text="CAN1 port:")
self.label_can1_port.pack()
self.textbox_can1_port = tk.Text(self.app, height=1, width=30)
self.label_can1_port = tk.Label(self.frame, text="CAN1 port:")
self.label_can1_port.grid(row=8, column=1, columnspan=10)
self.textbox_can1_port = tk.Text(self.frame, height=1, width=30)
self.textbox_can1_port.insert("1.0", self.CAN1_PORT)
self.textbox_can1_port.pack()
self.textbox_can1_port.grid(row=9, column=1, columnspan=10)

# Local port JSON relay
self.label_udp_port = tk.Label(self.app, text="Local port JSON UDP server (for PlotJuggler):")
self.label_udp_port.pack()
self.textbox_udp_port = tk.Text(self.app, height=1, width=30)
self.label_udp_port = tk.Label(self.frame, text="Local port JSON UDP server (for PlotJuggler):")
self.label_udp_port.grid(row=10, column=1, columnspan=10)
self.textbox_udp_port = tk.Text(self.frame, height=1, width=30)
self.textbox_udp_port.insert("1.0", self.UDP_PORT)
self.textbox_udp_port.pack()
self.textbox_udp_port.grid(row=11, column=1, columnspan=10)

# Path dbc CAN0
self.label_path_dbc_can0 = tk.Label(self.app, text="Select path to CAN0 DBC:")
self.label_path_dbc_can0.pack()
self.textbox_path_dbc_can0 = tk.Text(self.app, height=1, width=50)
self.textbox_path_dbc_can0.pack()
self.label_path_dbc_can0 = tk.Label(self.frame, text="Select path to CAN0 DBC:")
self.label_path_dbc_can0.grid(row=12, column=1, columnspan=10)
self.textbox_path_dbc_can0 = tk.Text(self.frame, height=1, width=50)
self.textbox_path_dbc_can0.grid(row=13, column=1, columnspan=10)
self.textbox_path_dbc_can0.insert("1.0", self.PATH_DBC_CAN0)
self.browse_button_can0 = tk.Button(self.app, text="Browse", command=self.browse_file_can0)
self.browse_button_can0.pack()
self.browse_button_can0 = tk.Button(self.frame, text="Browse", command=self.browse_file_can0)
self.browse_button_can0.grid(row=14, column=1, columnspan=10)

# Path dbc CAN1
self.label_path_dbc_can1 = tk.Label(self.app, text="Select path to CAN1 DBC:")
self.label_path_dbc_can1.pack()
self.textbox_path_dbc_can1 = tk.Text(self.app, height=1, width=50)
self.textbox_path_dbc_can1.pack()
self.label_path_dbc_can1 = tk.Label(self.frame, text="Select path to CAN1 DBC:")
self.label_path_dbc_can1.grid(row=15, column=1, columnspan=10)
self.textbox_path_dbc_can1 = tk.Text(self.frame, height=1, width=50)
self.textbox_path_dbc_can1.grid(row=16, column=1, columnspan=10)
self.textbox_path_dbc_can1.insert("1.0", self.PATH_DBC_CAN1)
self.browse_button_can1 = tk.Button(self.app, text="Browse", command=self.browse_file_can1)
self.browse_button_can1.pack()
self.browse_button_can1 = tk.Button(self.frame, text="Browse", command=self.browse_file_can1)
self.browse_button_can1.grid(row=17, column=1, columnspan=10)

# Connect button
self.connect_button = tk.Button(self.app, text="Connect", command=self.connect_thread, font=("Arial", 14, "bold"), width=15, height=2)
self.connect_button.pack(pady=20)
self.connect_button = tk.Button(self.frame, text="Connect", command=self.connect_thread, font=("Arial", 14, "bold"), width=8, height=2)
self.connect_button.grid(row=18, column=0, columnspan=5, pady=15)

# Disconnect button
self.disconnect_button = tk.Button(self.frame, text="Disconnect", command=self.disconnect, font=("Arial", 14, "bold"), width=8, height=2, state="disabled")
self.disconnect_button.grid(row=18, column=7, columnspan=5, pady=15)

# Connected status
self.label_connected = tk.Label(self.app, text="CONNECTED!", font=("Arial", 14, "bold"))
self.label_connected.forget()
self.label_connected = tk.Label(self.frame, text="CONNECTED!", font=("Arial", 14, "bold"))
self.label_connected.grid_forget()

# Connected status
self.label_config_loaded = tk.Label(self.app, text="CONFIG loaded!", font=("Arial", 14, "bold"))
self.label_config_loaded.forget()
self.label_config_loaded = tk.Label(self.frame, text="CONFIG loaded!", font=("Arial", 14, "bold"))
self.label_config_loaded.grid_forget()

# Start the Tkinter event loop
self.app.mainloop()
Expand All @@ -116,7 +123,7 @@ def browse_file_config_model(self):
self.textbox_path_config_model.delete("1.0", tk.END)
self.textbox_path_config_model.insert("1.0", self.PATH_CONFIG_MODEL)

self.label_config_loaded.pack()
self.label_config_loaded.grid(row=20, column=1, columnspan=10)
self.startup()

def browse_file_can0(self):
Expand All @@ -130,8 +137,10 @@ def browse_file_can1(self):
self.textbox_path_dbc_can1.insert("1.0", self.PATH_DBC_CAN1)

def connect_thread(self):
global controller_thread
# Start a new thread for the connect function
threading.Thread(target=self.connect, daemon=True).start()
controller_thread = threading.Thread(target=self.connect, daemon=True)
controller_thread.start()

def connect(self):
# Get the content of each textbox widget
Expand All @@ -143,7 +152,25 @@ def connect(self):
self.PATH_DBC_CAN1 = self.textbox_path_dbc_can1.get("1.0", "end-1c")
self.save_to_config_file() # Save the paths to the CONFIG file for future reuse

start_connection_controller(self.IP_SCANNER, self.CAN0_PORT, self.CAN1_PORT, self.UDP_PORT, self.PATH_DBC_CAN0, self.PATH_DBC_CAN1, self.label_connected, self.connect_button)
start_connection_controller(self.IP_SCANNER, self.CAN0_PORT, self.CAN1_PORT, self.UDP_PORT,
self.PATH_DBC_CAN0, self.PATH_DBC_CAN1, self.label_connected,
self.connect_button, self.disconnect_button)

def disconnect(self):
global controller_thread
# Stop the controller thread if it's running
if controller_thread:
disconnect()
time.sleep(1)
controller_thread.join()
self.connect_button.config(state="active")
self.disconnect_button.config(state="disabled")
self.label_connected.grid_forget()
self.label_config_loaded.grid_forget()
controller_thread = None
print("Disconnected")
else:
return

def startup(self):
with open(self.PATH_CONFIG_MODEL, 'r') as f:
Expand Down

0 comments on commit e5e9af8

Please sign in to comment.