Skip to content

Commit

Permalink
v1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Paolo-Beci committed Jul 15, 2024
1 parent 27eb2dd commit e94e043
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 59 deletions.
4 changes: 3 additions & 1 deletion CONFIG.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
PATH_DBC_CAN0=Path
PATH_DBC_CAN1=Path
UDP_PORT=5000
IP_SCANNER=0.0.0.0
CAN0_PORT=9600
CAN1_PORT=9601
UDP_PORT=5000
CAN_SOCKET0=vcan0
CAN_SOCKET1=vcan1
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ CAN1_PORT=9601
```
Physical CAN mode:
```txt
MODE=Physical
MODE=PhysicalCAN
PATH_DBC_CAN0=Path
PATH_DBC_CAN1=Path
UDP_PORT=5000
Expand All @@ -30,6 +30,14 @@ CAN_SOCKET1=can1
```
The `PATH_DBC_CAN0` and `PATH_DBC_CAN1` parameters are the paths to the DBC files of the CAN0 and CAN1 buses respectively. The `IP_SCANNER` parameter is the IP address of the scanner. The `CAN0_PORT` and `CAN1_PORT` parameters are the ports of the CAN0 and CAN1 buses respectively. The `UDP_PORT` parameter is the port of the local UDP server. The `CAN_SOCKET0` and `CAN_SOCKET1` parameters are the names of the physical CAN0 and CAN1 sockets respectively (only supported in Linux).

### Command-line run option
It is possible to run the program directly from the command line (for example) with the following command:
```bash
./SerialToUdpTranslator-v2.8.2-Win-x64.exe --config ./CONFIG.txt --mode Cannelloni --nogui
#./NameOfTheExecutable --config ./CONFIG.txt --nogui
```
Where you can specify the path of the configuration file `--config` -> `CONFIG.txt` file (explained above), the functioning mode `--mode` ("Cannelloni" of "PhysicalCAN") and the `--nogui` flag to run the program without the GUI.

## How to use the app: Physical CAN mode
This mode supports the input from physical `can0` and `can1` buses. You can select the bitrate and the DBC file for each bus. The output is a JSON stream that is sent to a UDP server.

Expand Down
23 changes: 16 additions & 7 deletions backend/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,32 +30,41 @@ def start_connection_controller(IP_SCANNER, CAN0_PORT, CAN1_PORT, UDP_PORT, PATH
time.sleep(1)

if MODE == "Cannelloni":
print("Starting Cannelloni connection...")
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:
is_running = True
label_connected.grid(row=21, column=1, columnspan=10)
connect_button.config(state="disabled")
disconnect_button.config(state="active")
if label_connected:
label_connected.grid(row=21, column=1, columnspan=10)
if connect_button:
connect_button.config(state="disabled")
if disconnect_button:
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:
disconnect()
print("Failed to establish connection, disconnecting...")

elif MODE == "Physical CAN":
elif MODE == "PhysicalCAN":
can_bus0 = open_stream_can(CAN_SOCKET0)
can_bus1 = open_stream_can(CAN_SOCKET1)
if udp_socket and can_bus0 and can_bus1:
is_running = True
label_connected.grid(row=21, column=1, columnspan=10)
connect_button.config(state="disabled")
disconnect_button.config(state="active")
if label_connected:
label_connected.grid(row=21, column=1, columnspan=10)
if connect_button:
connect_button.config(state="disabled")
if disconnect_button:
disconnect_button.config(state="active")
data_thread = threading.Thread(target=read_data_can, args=(udp_socket, can_bus0, can_bus1, PATH_DBC_CAN0, PATH_DBC_CAN1, UDP_PORT), daemon=True)
data_thread.start()
else:
disconnect()
print("Failed to establish connection, disconnecting...")
else:
print("Invalid mode: ", MODE)

# Opens a stream from the specified scanner IP and ports
def open_stream_cannelloni(IP_SCANNER, CAN0_PORT, CAN1_PORT):
Expand Down
134 changes: 84 additions & 50 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,43 @@
from backend.functions import start_connection_controller, disconnect
import threading
import time
import argparse

class GUI:
def __init__(self):
# Create a new instance of Tkinter application
self.app = tk.Tk()

def __init__(self, config_path=None, mode=None, direct_connect=False):
# Config parameters with default values
self.MODE = "Select"
self.PATH_CONFIG_MODEL = "Path"
self.PATH_DBC_CAN0 = "Path"
self.PATH_DBC_CAN1 = "Path"
self.PATH_CONFIG_MODEL = "Path"
self.UDP_PORT = 9870
self.IP_SCANNER = "0.0.0.0"
self.CAN0_PORT = 4000
self.CAN1_PORT = 5000
self.UDP_PORT = 9870
self.MODE = "Cannelloni"
self.CAN_SOCKET0 = "vcan0"
self.CAN_SOCKET1 = "vcan1"

global controller_thread
controller_thread = None
self.connect_button = None
self.label_connected = None
self.label_connected = None
self.disconnect_button = None

# Load configuration if a path was provided
if config_path:
self.PATH_CONFIG_MODEL = config_path
self.startup()

if mode:
self.MODE = mode

if direct_connect:
self.connect_thread(direct=True)
return

# Create a new instance of Tkinter application
self.app = tk.Tk()

# ---------- WINDOW SETTINGS ---------------------------------------
self.app.title("SCanner Adapter")
Expand All @@ -45,11 +62,16 @@ def __init__(self):
self.label.grid(row=0, column=1, columnspan=10, pady=10)

# Functioning mode
self.label_mode = tk.Label(self.frame, text="Select functioning mode to use (Physical CAN is Linux only):")
self.label_mode = tk.Label(self.frame, text="Select functioning mode to use (PhysicalCAN is Linux only):")
self.label_mode.grid(row=1, column=1, columnspan=10)
options = ["Select", "Cannelloni", "Physical CAN"] # Update the naming in the backend if modified
options = ["Select", "Cannelloni", "PhysicalCAN"] # Update the naming in the backend if modified
self.selected_option = StringVar()
self.selected_option.set(options[0])
# Set the provided mode if any
if mode:
self.selected_option.set(mode)
self.update_ui_based_on_mode(mode)
else:
self.selected_option.set(options[0])
self.dropdown = OptionMenu(self.frame, self.selected_option, *options, command=self.update_ui_based_on_mode)
self.dropdown.grid(row=2, column=1, columnspan=10, pady=10)

Expand Down Expand Up @@ -121,7 +143,7 @@ def update_ui_based_on_mode(self, selected_mode):
for widget in self.frame.grid_slaves(row=11):
widget.grid_forget()

if selected_mode == "Physical CAN":
if selected_mode == "PhysicalCAN":
# CAN Socket 1
self.label_can_socket0 = tk.Label(self.frame, text="CAN0 socket:")
self.label_can_socket0.grid(row=6, column=1, columnspan=10)
Expand Down Expand Up @@ -180,40 +202,45 @@ def browse_file_can1(self):
self.textbox_path_dbc_can1.delete("1.0", tk.END)
self.textbox_path_dbc_can1.insert("1.0", self.PATH_DBC_CAN1)

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

def connect(self):
# Get the content of each textbox widget
if hasattr(self, 'textbox_ip_scanner'):
self.IP_SCANNER = self.textbox_ip_scanner.get("1.0", "end-1c")
if hasattr(self, 'textbox_can0_port'):
self.CAN0_PORT = self.textbox_can0_port.get("1.0", "end-1c")
if hasattr(self, 'textbox_can1_port'):
self.CAN1_PORT = self.textbox_can1_port.get("1.0", "end-1c")
if hasattr(self, 'textbox_can_socket0'):
self.CAN_SOCKET0 = self.textbox_can_socket0.get("1.0", "end-1c")
if hasattr(self, 'textbox_can_socket1'):
self.CAN_SOCKET1 = self.textbox_can_socket1.get("1.0", "end-1c")
self.UDP_PORT = self.textbox_udp_port.get("1.0", "end-1c")
self.PATH_DBC_CAN0 = self.textbox_path_dbc_can0.get("1.0", "end-1c")
self.PATH_DBC_CAN1 = self.textbox_path_dbc_can1.get("1.0", "end-1c")
# Get the selected mode
self.MODE = self.selected_option.get()
# Save the paths to the CONFIG file for future reuse
self.save_to_config_file()

print("Mode: ", self.MODE)
if self.MODE != "Select":
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,
def connect(self, direct=False):
if not direct:
# Get the content of each textbox widget
if hasattr(self, 'textbox_ip_scanner'):
self.IP_SCANNER = self.textbox_ip_scanner.get("1.0", "end-1c")
if hasattr(self, 'textbox_can0_port'):
self.CAN0_PORT = self.textbox_can0_port.get("1.0", "end-1c")
if hasattr(self, 'textbox_can1_port'):
self.CAN1_PORT = self.textbox_can1_port.get("1.0", "end-1c")
if hasattr(self, 'textbox_can_socket0'):
self.CAN_SOCKET0 = self.textbox_can_socket0.get("1.0", "end-1c")
if hasattr(self, 'textbox_can_socket1'):
self.CAN_SOCKET1 = self.textbox_can_socket1.get("1.0", "end-1c")
self.UDP_PORT = self.textbox_udp_port.get("1.0", "end-1c")
self.PATH_DBC_CAN0 = self.textbox_path_dbc_can0.get("1.0", "end-1c")
self.PATH_DBC_CAN1 = self.textbox_path_dbc_can1.get("1.0", "end-1c")
# Get the selected mode
self.MODE = self.selected_option.get()
# Save the paths to the CONFIG file for future reuse
self.save_to_config_file()

try:
print("Mode: ", self.MODE)
if self.MODE != "Select":
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, self.MODE,
self.CAN_SOCKET0, self.CAN_SOCKET1)
else:
return
else:
print("Please select a valid mode")
return
except Exception as e:
print(f"Connection failed: {e}")

def disconnect(self):
global controller_thread
Expand All @@ -235,9 +262,7 @@ def startup(self):
with open(self.PATH_CONFIG_MODEL, 'r') as f:
for line in f:
key, value = line.strip().split("=")
if key == "MODE":
self.MODE = value
elif key == "PATH_DBC_CAN0":
if key == "PATH_DBC_CAN0":
self.PATH_DBC_CAN0 = value
elif key == "PATH_DBC_CAN1":
self.PATH_DBC_CAN1 = value
Expand Down Expand Up @@ -273,17 +298,19 @@ def update_interface(self):
if hasattr(self, 'textbox_can_socket1'):
self.textbox_can_socket1.delete("1.0", tk.END)
self.textbox_can_socket1.insert("1.0", self.CAN_SOCKET1)
self.textbox_udp_port.delete("1.0", tk.END)
self.textbox_udp_port.insert("1.0", self.UDP_PORT)
self.textbox_path_dbc_can0.delete("1.0", tk.END)
self.textbox_path_dbc_can0.insert("1.0", self.PATH_DBC_CAN0)
self.textbox_path_dbc_can1.delete("1.0", tk.END)
self.textbox_path_dbc_can1.insert("1.0", self.PATH_DBC_CAN1)
if hasattr(self, 'textbox_udp_port'):
self.textbox_udp_port.delete("1.0", tk.END)
self.textbox_udp_port.insert("1.0", self.UDP_PORT)
if hasattr(self, 'textbox_path_dbc_can0'):
self.textbox_path_dbc_can0.delete("1.0", tk.END)
self.textbox_path_dbc_can0.insert("1.0", self.PATH_DBC_CAN0)
if hasattr(self, 'textbox_path_dbc_can1'):
self.textbox_path_dbc_can1.delete("1.0", tk.END)
self.textbox_path_dbc_can1.insert("1.0", self.PATH_DBC_CAN1)

def save_to_config_file(self):
if self.PATH_CONFIG_MODEL != "Path":
with open(self.PATH_CONFIG_MODEL, 'w') as f:
f.write(f"MODE={self.MODE}\n")
f.write(f"PATH_DBC_CAN0={self.PATH_DBC_CAN0}\n")
f.write(f"PATH_DBC_CAN1={self.PATH_DBC_CAN1}\n")
f.write(f"UDP_PORT={self.UDP_PORT}\n")
Expand All @@ -293,4 +320,11 @@ def save_to_config_file(self):
f.write(f"CAN_SOCKET0={self.CAN_SOCKET0}\n")
f.write(f"CAN_SOCKET1={self.CAN_SOCKET1}")

GUI()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Start the Serial CSV to UDP JSON Translator GUI.")
parser.add_argument('--config', type=str, help='Path to the configuration file')
parser.add_argument('--mode', type=str, default="Cannelloni", help='Functioning mode to use: Cannelloni or PhysicalCAN')
parser.add_argument('--nogui', action='store_true', default=False, help='Connect directly without showing the GUI')
args = parser.parse_args()

gui = GUI(config_path=args.config, mode=args.mode, direct_connect=args.nogui)

0 comments on commit e94e043

Please sign in to comment.