From 49a1daed6e6e268dc5614b6b18fb8762b9593a8f Mon Sep 17 00:00:00 2001 From: Bruno Crivelari Sanches Date: Tue, 9 Jul 2024 19:02:37 -0300 Subject: [PATCH] Added a tab for configuring static ip addresses on CommandStation with Ethernet shields --- ex_installer/ex_commandstation.py | 155 ++++++++++++++++++++++++++++-- 1 file changed, 147 insertions(+), 8 deletions(-) diff --git a/ex_installer/ex_commandstation.py b/ex_installer/ex_commandstation.py index 01f6abf..085cbce 100644 --- a/ex_installer/ex_commandstation.py +++ b/ex_installer/ex_commandstation.py @@ -208,7 +208,8 @@ def setup_config_frame(self): tab_list = [ "General", "WiFi Options", - "TrackManager Config" + "TrackManager Config", + "Ethernet Options" ] for tab in tab_list: self.config_tabview.add(tab) @@ -223,6 +224,8 @@ def setup_config_frame(self): self.wifi_tab_frame.grid(**tab_frame_options) self.track_tab_frame = ctk.CTkFrame(self.config_tabview.tab("TrackManager Config"), border_width=0) self.track_tab_frame.grid(**tab_frame_options) + self.ethernet_tab_frame = ctk.CTkFrame(self.config_tabview.tab("Ethernet Options"), border_width=0) + self.ethernet_tab_frame.grid(**tab_frame_options) # Create options frames self.switch_frame = ctk.CTkFrame(self.general_tab_frame, border_width=0) @@ -329,6 +332,27 @@ def setup_config_frame(self): command=self.set_ethernet, font=self.instruction_font) CreateToolTip(self.ethernet_switch, ethernet_tip, "https://dcc-ex.com/reference/hardware/ethernet-boards.html") + + # Set up Ethernet Widgets + self.ethernet_static_ip_switch = ctk.CTkSwitch(self.ethernet_tab_frame, text="Static IP address", + onvalue="on", offvalue="off", + width=100, command=self.set_ethernet_static_ip, + font=self.instruction_font) + self.ethernet_ip_frame = ctk.CTkFrame(self.ethernet_tab_frame, border_width=0) + self.ethernet_ip_label = ctk.CTkLabel(self.ethernet_ip_frame, text="IP:", + font=self.instruction_font) + self.ethernet_ip_a_entry = ctk.CTkEntry(self.ethernet_ip_frame, + placeholder_text="192", + width=50, fg_color="white", font=self.instruction_font) + self.ethernet_ip_b_entry = ctk.CTkEntry(self.ethernet_ip_frame, + placeholder_text="168", + width=50, fg_color="white", font=self.instruction_font) + self.ethernet_ip_c_entry = ctk.CTkEntry(self.ethernet_ip_frame, + placeholder_text="1", + width=50, fg_color="white", font=self.instruction_font) + self.ethernet_ip_d_entry = ctk.CTkEntry(self.ethernet_ip_frame, + placeholder_text="200", + width=50, fg_color="white", font=self.instruction_font) # Track Manager Options self.track_modes_enabled = ctk.StringVar(self, value="off") @@ -450,21 +474,36 @@ def setup_config_frame(self): self.track_b_label.grid(column=0, row=1, sticky="e", **grid_options) self.track_b_combo.grid(column=1, row=1, sticky="w", **grid_options) self.track_b_id_label.grid(column=2, row=1, sticky="e", **grid_options) - self.track_b_entry.grid(column=3, row=1, sticky="w", **grid_options) + self.track_b_entry.grid(column=3, row=1, sticky="w", **grid_options) + + # Layout ethernet_frame + self.ethernet_tab_frame.grid_columnconfigure(0, weight=1) + self.ethernet_tab_frame.grid_rowconfigure((0, 1), weight=1) + self.ethernet_static_ip_switch.grid(column=0, row=0, **grid_options) + self.ethernet_ip_frame.grid_columnconfigure((0, 1, 2, 3, 4), weight=1) + self.ethernet_ip_frame.grid_rowconfigure(0, weight=1) + self.ethernet_ip_label.grid(column=0, row=0,sticky="e", **grid_options) + self.ethernet_ip_a_entry.grid(column=1, row=0,sticky="e", **grid_options) + self.ethernet_ip_b_entry.grid(column=2, row=0,sticky="e", **grid_options) + self.ethernet_ip_c_entry.grid(column=3, row=0,sticky="e", **grid_options) + self.ethernet_ip_d_entry.grid(column=4, row=0,sticky="e", **grid_options) + + # Layout WiFi tab + self.wifi_options_frame.grid(column=0, row=0, sticky="nsew") # Layout general tab self.general_tab_frame.grid_columnconfigure(0, weight=1) self.general_tab_frame.grid_columnconfigure(1, weight=2) self.general_tab_frame.grid_rowconfigure(0, weight=1) self.switch_frame.grid(column=0, row=0, **grid_options) - self.options_frame.grid(column=1, row=0, **grid_options) - - # Layout WiFi tab - self.wifi_options_frame.grid(column=0, row=0, sticky="nsew") + self.options_frame.grid(column=1, row=0, **grid_options) # Layout TrackManager tab self.track_modes_frame.grid(column=0, row=0, sticky="nsew") + # Layout Ethernet tab + self.ethernet_ip_frame.grid(column=0, row=1) + # Layout config_frame self.config_frame.grid_columnconfigure(0, weight=1) self.config_frame.grid_rowconfigure(1, weight=1) @@ -581,6 +620,7 @@ def set_wifi(self): if self.wifi_switch.get() == "on": if self.ethernet_switch.get() == "on": self.ethernet_switch.deselect() + self.refresh_ethernet_tab_option() self.config_tabview._segmented_button._buttons_dict["WiFi Options"].configure(state="normal") self.set_wifi_widgets() self.log.debug("WiFi enabled") @@ -611,6 +651,16 @@ def set_wifi_widgets(self): self.wifi_pwd_entry.configure(placeholder_text="Enter your WiFi password") self.log.debug("WiFi ST mode selected") + def refresh_ethernet_tab_option(self): + """ + Function to refresh the tab button (used by wifi too) + """ + if self.ethernet_switch.get() == "on": + self.config_tabview._segmented_button._buttons_dict["Ethernet Options"].configure(state="normal") + else: + self.config_tabview._segmented_button._buttons_dict["Ethernet Options"].configure(state="disabled") + self.set_ethernet_static_ip() + def set_ethernet(self): """ Function to enable Ethernet support @@ -618,10 +668,27 @@ def set_ethernet(self): if self.ethernet_switch.get() == "on": if self.wifi_switch.get() == "on": self.wifi_switch.deselect() - self.set_wifi() + self.set_wifi() self.log.debug("Ethernet enabled") else: - self.log.debug("Ethernet disabled") + self.log.debug("Ethernet disabled") + self.refresh_ethernet_tab_option() + + def set_ethernet_static_ip(self): + if self.ethernet_static_ip_switch.get() == "on": + self.ethernet_ip_label.grid() + self.ethernet_ip_a_entry.grid() + self.ethernet_ip_b_entry.grid() + self.ethernet_ip_c_entry.grid() + self.ethernet_ip_d_entry.grid() + self.log.debug("Static IP enabled") + else: + self.ethernet_ip_label.grid_remove() + self.ethernet_ip_a_entry.grid_remove() + self.ethernet_ip_b_entry.grid_remove() + self.ethernet_ip_c_entry.grid_remove() + self.ethernet_ip_d_entry.grid_remove() + self.log.debug("Static IP disabled") def decrement_channel(self): """ @@ -648,6 +715,8 @@ def display_config_screen(self): self.config_frame.grid() self.set_display() self.set_wifi() + self.set_ethernet() + self.set_ethernet_static_ip() self.set_track_modes() self.set_advanced_config() self.check_motor_driver(self.motor_driver_combo.get()) @@ -780,6 +849,57 @@ def delete_config_files(self): self.process_error(f"Failed to delete one or more files: {file_list}") self.log.error("Failed to delete: %s", file_list) + def ethernet_is_ip_equal(self, ip1, ip2): + for i in range(4): + if(ip1[i] != ip2[i]): + return False + return True + + def ethernet_check_for_reserved_ips(self, ip): + # from https://en.wikipedia.org/wiki/Reserved_IP_addresses + if self.ethernet_is_ip_equal(ip, [255, 255, 255, 255]): + return True + if self.ethernet_is_ip_equal(ip, [240, 0, 0, 0]): + return True + if self.ethernet_is_ip_equal(ip, [233, 252, 0, 0]): + return True + if self.ethernet_is_ip_equal(ip, [224, 0, 0, 0]): + return True + if self.ethernet_is_ip_equal(ip, [203, 0, 113, 0]): + return True + if self.ethernet_is_ip_equal(ip, [198, 0, 0, 0]): + return True + if self.ethernet_is_ip_equal(ip, [198, 51, 100, 0]): + return True + if self.ethernet_is_ip_equal(ip, [192, 0, 0, 0]): + return True + if self.ethernet_is_ip_equal(ip, [192, 0, 2, 0]): + return True + if self.ethernet_is_ip_equal(ip, [192, 88, 99, 0]): + return True + if self.ethernet_is_ip_equal(ip, [192, 168, 0, 0]): + return True + if self.ethernet_is_ip_equal(ip, [172, 16, 0, 0]): + return True + if self.ethernet_is_ip_equal(ip, [169, 254, 0, 0]): + return True + if self.ethernet_is_ip_equal(ip, [127, 0, 0, 0]): + return True + if self.ethernet_is_ip_equal(ip, [100, 64, 0, 0]): + return True + if self.ethernet_is_ip_equal(ip, [10, 0, 0, 0]): + return True + if self.ethernet_is_ip_equal(ip, [0, 0, 0, 0]): + return True + return False + + def ethernet_get_ip_number(self, entry): + num = entry.get() + if not num.isnumeric(): + return (False, num) + num = int(num) + return ((num >= 0) and (num <= 255), num) + def generate_config(self): """ Function to validate options and return any errors @@ -838,6 +958,25 @@ def generate_config(self): param_errors.append("Can not have both Ethernet and WiFi enabled") else: config_list.append("#define ENABLE_ETHERNET true\n") + if self.ethernet_static_ip_switch.get() == "on": + ip_entries = [self.ethernet_ip_a_entry, self.ethernet_ip_b_entry, self.ethernet_ip_c_entry, self.ethernet_ip_d_entry] + ip_data = [] + for entry in ip_entries: + ip_num = self.ethernet_get_ip_number(entry) + if not ip_num[0]: + param_errors.append("Invalid static IP value") + ip_data = None + break + + ip_data.append(ip_num[1]) + + if ip_data: + #check for know ip addresses + if self.ethernet_check_for_reserved_ips(ip_data): + param_errors.append("IP value is reserved") + else: + line = "#define IP_ADDRESS { " + str(ip_data[0]) + ', ' + str(ip_data[1]) + ', ' + str(ip_data[2]) + ', ' + str(ip_data[3]) + ' }\n' + config_list.append(line) if self.override_current_limit.get() == "on": try: int(self.current_limit.get())