Coverage for /home/pi/Software/model-railway-signalling/model_railway_signals/editor/objects/objects_sensors.py: 100%
126 statements
« prev ^ index » next coverage.py v7.2.7, created at 2024-04-10 15:08 +0100
« prev ^ index » next coverage.py v7.2.7, created at 2024-04-10 15:08 +0100
1#-------------------------------------------------------------------------------------------------------------
2# This module contains all the functions for managing track sensor objects
3#-------------------------------------------------------------------------------------------------------------
4#
5# External API functions intended for use by other editor modules:
6# create_track_sensor() - Create a new object (and associated library objects) - returns object ID
7# delete_track_sensor(object_id) - Hard Delete an object (all configuration and library objects)
8# update_track_sensor(object_id, new_object) - Update the configuration (and delete/create library objects)
9# paste_track_sensor(object) - Create a copy of an object (and create the associated library objects)
10# delete_track_sensor_object(object_id) - Soft delete - delete the library objects (prior to recreating)
11# redraw_track_sensor_object(object_id) - Create the associated library objects
12# default_track_sensor_object - The dictionary of default baseline configuration values
13# remove_references_to_section (sec_id) - remove section references from the route tables
14# update_references_to_section (old_id, new_id) - update section_id in the route tables
15# remove_references_to_point (point_id) - remove point references from the route tables
16# update_references_to_point(old_pt_id, new_pt_id) - update point_id in the route tables
17#
18# Makes the following external API calls to other editor modules:
19# run_layout.schematic_callback - the callback specified when creating the library objects
20# objects_common.set_bbox - to create/update the boundary box for the canvas drawing objects
21# objects_common.new_item_id - to get the next 'free' type-specific Item ID (when creating objects)
22# objects_common.find_initial_canvas_position - to find the next 'free' canvas position
23# objects_common.track_sensor - to find the object_id from a given item_id
24#
25# Accesses the following external editor objects directly:
26# objects_common.schematic_objects - the master dictionary of Schematic Objects
27# objects_common.track_sensor_index - the type-specific index for this onject type
28# objects_common.default_object - The dictionary of object common configuration elements
29# objects_common.object_type - The enumeration of supported object types
30# objects_common.canvas - Reference to the Tkinter drawing canvas
31#
32# Makes the following external API calls to library modules:
33# track_sensors.create_track_sensor(id) - Create the library object
34# track_sensors.delete_track_sensor(id) - Delete the library object
35# track_sensors.track_sensor_exists - to find out if the specified Item ID already exists
36# gpio_sensors.add_gpio_sensor_callback - To set up a GPIO Sensor triggered callback
37# gpio_sensors.remove_gpio_sensor_callbacks - To remove any GPIO Sensor triggered callbacks
38#
39#-------------------------------------------------------------------------------------------------------------
41import uuid
42import copy
44from ...library import track_sensors
45from ...library import gpio_sensors
47from .. import run_layout
48from . import objects_common
50#-------------------------------------------------------------------------------------------------------------
51# Default Object parameters (i.e. state at creation)
52#-------------------------------------------------------------------------------------------------------------
54default_track_sensor_object = copy.deepcopy(objects_common.default_object)
55default_track_sensor_object["item"] = objects_common.object_type.track_sensor
56default_track_sensor_object["passedsensor"] = ""
57# The "routeahead" element comprises a list of routes: [main, lh1, lh2, rh1, rh2]
58# Each route comprises: [[p1, p2, p3, p4, p5, p6, p7], section_id]
59# Each point element in the point list comprises [point_id, point_state]
60default_track_sensor_object["routeahead"] = [
61 [[[0,False],[0,False],[0,False],[0,False],[0,False],[0,False]],0],
62 [[[0,False],[0,False],[0,False],[0,False],[0,False],[0,False]],0],
63 [[[0,False],[0,False],[0,False],[0,False],[0,False],[0,False]],0],
64 [[[0,False],[0,False],[0,False],[0,False],[0,False],[0,False]],0],
65 [[[0,False],[0,False],[0,False],[0,False],[0,False],[0,False]],0] ]
66default_track_sensor_object["routebehind"] = [
67 [[[0,False],[0,False],[0,False],[0,False],[0,False],[0,False]],0],
68 [[[0,False],[0,False],[0,False],[0,False],[0,False],[0,False]],0],
69 [[[0,False],[0,False],[0,False],[0,False],[0,False],[0,False]],0],
70 [[[0,False],[0,False],[0,False],[0,False],[0,False],[0,False]],0],
71 [[[0,False],[0,False],[0,False],[0,False],[0,False],[0,False]],0] ]
73#------------------------------------------------------------------------------------
74# Function to remove all references to a point from both of the Track Sensor's
75# route tables (the "routeahead" and "routebehind" dictionary elements).
76# Each route table comprises a list of routes: [main, lh1, lh2, rh1, rh2]
77# Each route comprises: [[p1, p2, p3, p4, p5, p6, p7], section_id]
78# Each point element in the point list comprises [point_id, point_state]
79#------------------------------------------------------------------------------------
81def remove_references_to_point(point_id:int):
82 remove_points_from_route_table(point_id, "routeahead")
83 remove_points_from_route_table(point_id, "routebehind")
84 return()
86def remove_points_from_route_table(point_id:int, dict_key:str):
87 # Iterate through all the track sensors on the schematic
88 for track_sensor_id in objects_common.track_sensor_index:
89 # Get the Object ID of the track sensor
90 sensor_object = objects_common.track_sensor(track_sensor_id)
91 # Iterate through each route in the route table
92 route_table = objects_common.schematic_objects[sensor_object][dict_key]
93 for index1, route in enumerate(route_table):
94 list_of_points = route[0]
95 # Create a new 'blank' list for copying the points (that haven't been deleted) across
96 # We do this to 'tidy up' the list (i.e. remove the 'blanks' caused by the point removal)
97 new_list_of_points = [[0,False],[0,False],[0,False],[0,False],[0,False],[0,False]]
98 index2 = 0
99 # Iterate through each point in the route to build up the new list of (retained) points
100 for point_entry in list_of_points:
101 if point_entry[0] != point_id:
102 new_list_of_points[index2] = point_entry
103 index2 = index2 +1
104 # Replace the list of points for the route
105 objects_common.schematic_objects[sensor_object][dict_key][index1][0] = new_list_of_points
106 return()
108#------------------------------------------------------------------------------------
109# Function to update all references to a point in both of the Track Sensor's
110# route tables (the "routeahead" and "routebehind" dictionary elements)
111# Each route table comprises a list of routes: [main, lh1, lh2, rh1, rh2]
112# Each route comprises: [[p1, p2, p3, p4, p5, p6, p7], section_id]
113# Each point element in the point list comprises [point_id, point_state]
114#------------------------------------------------------------------------------------
116def update_references_to_point(old_point_id:int, new_point_id:int):
117 update_point_in_route_table(old_point_id, new_point_id, "routeahead")
118 update_point_in_route_table(old_point_id, new_point_id, "routebehind")
119 return()
121def update_point_in_route_table(old_point_id:int, new_point_id:int, dict_key:str):
122 # Iterate through all the Track Sections on the schematic
123 for track_sensor_id in objects_common.track_sensor_index:
124 # Get the Object ID of the signal
125 sensor_object = objects_common.track_sensor(track_sensor_id)
126 # Iterate through each route in the interlocking table and then the points on each route
127 route_table = objects_common.schematic_objects[sensor_object][dict_key]
128 for index1, route in enumerate(route_table):
129 list_of_points = route[0]
130 for index2, point_entry in enumerate(list_of_points):
131 if point_entry[0] == old_point_id:
132 objects_common.schematic_objects[sensor_object][dict_key][index1][0][index2][0] = new_point_id
133 return()
135#------------------------------------------------------------------------------------
136# Function to remove references to a Track Section from both of the Track Sensor's
137# route tables (the "routeahead" and "routebehind" dictionary elements).
138# Each route table comprises a list of routes: [main, lh1, lh2, rh1, rh2]
139# Each route comprises: [[p1, p2, p3, p4, p5, p6, p7], section_id]
140# Each point element in the point list comprises [point_id, point_state]
141#------------------------------------------------------------------------------------
143def remove_references_to_section(section_id:int):
144 remove_sections_from_route_table(section_id, "routeahead")
145 remove_sections_from_route_table(section_id, "routebehind")
146 return()
148def remove_sections_from_route_table(section_id:int, dict_key:str):
149 # Iterate through all the track sensors on the schematic
150 for track_sensor_id in objects_common.track_sensor_index:
151 # Get the Object ID of the track sensor
152 sensor_object = objects_common.track_sensor(track_sensor_id)
153 # Iterate through each route in the route table
154 route_table = objects_common.schematic_objects[sensor_object][dict_key]
155 for index1, route in enumerate(route_table):
156 # If we find a match then remove the track section reference
157 if route[1] == section_id:
158 objects_common.schematic_objects[sensor_object][dict_key][index1][1] = 0
159 return()
161#------------------------------------------------------------------------------------
162# Function to update references to a Track Section in both of the Track Sensor's
163# route tables (the "routeahead" and "routebehind" dictionary elements).
164# Each route table comprises a list of routes: [main, lh1, lh2, rh1, rh2]
165# Each route comprises: [[p1, p2, p3, p4, p5, p6, p7], section_id]
166# Each point element in the point list comprises [point_id, point_state]
167#------------------------------------------------------------------------------------
169def update_references_to_section(old_section_id:int, new_section_id:int):
170 update_section_in_route_table(old_section_id, new_section_id, "routeahead")
171 update_section_in_route_table(old_section_id, new_section_id, "routebehind")
172 return()
174def update_section_in_route_table(old_section_id:int, new_section_id:int, dict_key:str):
175 # Iterate through all the track sensors on the schematic
176 for track_sensor_id in objects_common.track_sensor_index:
177 # Get the Object ID of the track sensor
178 sensor_object = objects_common.track_sensor(track_sensor_id)
179 # Iterate through each route in the route table
180 route_table = objects_common.schematic_objects[sensor_object][dict_key]
181 for index1, route in enumerate(route_table):
182 # If we find a match then update the track section reference
183 if route[1] == old_section_id:
184 objects_common.schematic_objects[sensor_object][dict_key][index1][1] = new_section_id
185 return()
187#-------------------------------------------------------------------------------------------------------------
188# Function to to update an object's configuration (and delete/create the associated library objects)
189#-------------------------------------------------------------------------------------------------------------
191def update_track_sensor(object_id, new_object_configuration):
192 # We need to track whether the Item ID has changed
193 old_item_id = objects_common.schematic_objects[object_id]["itemid"]
194 new_item_id = new_object_configuration["itemid"]
195 # Delete the existing object, copy across the new config and redraw
196 delete_track_sensor_object(object_id)
197 objects_common.schematic_objects[object_id] = copy.deepcopy(new_object_configuration)
198 redraw_track_sensor_object(object_id)
199 # Check to see if the Type-specific ID has been changed
200 if old_item_id != new_item_id:
201 # Update the type-specific index
202 del objects_common.track_sensor_index[str(old_item_id)]
203 objects_common.track_sensor_index[str(new_item_id)] = object_id
204 return()
206#------------------------------------------------------------------------------------------------------------------
207# Function to create the associated library objects - Called when the object is first created or after an
208# object configuration updat (where the library objects are deleted and then re-created in the new configuration)
209#------------------------------------------------------------------------------------------------------------------
211def redraw_track_sensor_object(object_id):
212 # Create the associated library object
213 x = objects_common.schematic_objects[object_id]["posx"]
214 y = objects_common.schematic_objects[object_id]["posy"]
215 item_id = objects_common.schematic_objects[object_id]["itemid"]
216 callback = run_layout.schematic_callback
217 canvas_tags = track_sensors.create_track_sensor(objects_common.canvas, item_id, x, y, callback=callback)
218 # Store the tkinter tags for the library object and Create/update the selection rectangle
219 objects_common.schematic_objects[object_id]["tags"] = canvas_tags
220 objects_common.set_bbox(object_id, objects_common.canvas.bbox(canvas_tags))
221 # If an external GPIO sensor is specified then map this to the Track Sensor
222 gpio_sensor = objects_common.schematic_objects[object_id]["passedsensor"]
223 if gpio_sensor != "": gpio_sensors.add_gpio_sensor_callback(gpio_sensor, sensor_passed=item_id)
224 return()
226#------------------------------------------------------------------------------------------------------------------
227# Function to Create a new default object (and create the associated library objects)
228#------------------------------------------------------------------------------------------------------------------
230def create_track_sensor():
231 # Generate a new object from the default configuration with a new UUID
232 object_id = str(uuid.uuid4())
233 objects_common.schematic_objects[object_id] = copy.deepcopy(default_track_sensor_object)
234 # Find the initial canvas position for the new object
235 x, y = objects_common.find_initial_canvas_position()
236 item_id = objects_common.new_item_id(exists_function=track_sensors.track_sensor_exists)
237 # Add the specific elements for this particular instance of the object
238 objects_common.schematic_objects[object_id]["itemid"] = item_id
239 objects_common.schematic_objects[object_id]["posx"] = x
240 objects_common.schematic_objects[object_id]["posy"] = y
241 # Add the new object to the type-specific index
242 objects_common.track_sensor_index[str(item_id)] = object_id
243 # Create the associated library objects
244 redraw_track_sensor_object(object_id)
245 return(object_id)
247#------------------------------------------------------------------------------------------------------------------
248# Function to create a copy of an existing object (and create associated library objects) - returns Object ID
249#------------------------------------------------------------------------------------------------------------------
251def paste_track_sensor(object_to_paste, deltax:int, deltay:int):
252 # Create a new UUID for the pasted object
253 new_object_id = str(uuid.uuid4())
254 objects_common.schematic_objects[new_object_id] = copy.deepcopy(object_to_paste)
255 # Assign a new type-specific ID for the object and add to the index
256 new_id = objects_common.new_item_id(exists_function=track_sensors.track_sensor_exists)
257 objects_common.schematic_objects[new_object_id]["itemid"] = new_id
258 objects_common.track_sensor_index[str(new_id)] = new_object_id
259 # Add the specific elements for this particular instance of the object
260 objects_common.schematic_objects[new_object_id]["itemid"] = new_id
261 # Set the position for the "pasted" object (offset from the original position)
262 objects_common.schematic_objects[new_object_id]["posx"] += deltax
263 objects_common.schematic_objects[new_object_id]["posy"] += deltay
264 # Now set the default values for all elements we don't want to copy
265 objects_common.schematic_objects[new_object_id]["passedsensor"] = default_track_sensor_object["passedsensor"]
266 objects_common.schematic_objects[new_object_id]["routeahead"] = default_track_sensor_object["routeahead"]
267 objects_common.schematic_objects[new_object_id]["routebehind"] = default_track_sensor_object["routebehind"]
268 # Set the Boundary box for the new object to None so it gets created on re-draw
269 objects_common.schematic_objects[new_object_id]["bbox"] = None
270 # Create the associated library objects
271 redraw_track_sensor_object(new_object_id)
272 return(new_object_id)
274#------------------------------------------------------------------------------------------------------------------
275# Function to "soft delete" an object (delete the associated library objects) - Called to delete the library
276# objects prior to re-creating in their new configuration - also called as part of a 'hard delete'.
277#------------------------------------------------------------------------------------------------------------------
279def delete_track_sensor_object(object_id):
280 global button_mappings
281 # Delete the associated library objects
282 item_id = objects_common.schematic_objects[object_id]["itemid"]
283 track_sensors.delete_track_sensor(item_id)
284 # Delete the track sensor mapping for the intermediate sensor (if any)
285 linked_gpio_sensor = objects_common.schematic_objects[object_id]["passedsensor"]
286 if linked_gpio_sensor != "": gpio_sensors.remove_gpio_sensor_callback(linked_gpio_sensor)
287 return()
289#------------------------------------------------------------------------------------------------------------------
290# Function to 'hard delete' a schematic object (including all associated library objects).
291#------------------------------------------------------------------------------------------------------------------
293def delete_track_sensor(object_id):
294 # Delete the associated library objects
295 delete_track_sensor_object(object_id)
296 # "Hard Delete" the selected object - deleting the boundary box rectangle and deleting
297 # the object from the dictionary of schematic objects (and associated dictionary keys)
298 objects_common.canvas.delete(objects_common.schematic_objects[object_id]["bbox"])
299 del objects_common.track_sensor_index[str(objects_common.schematic_objects[object_id]["itemid"])]
300 del objects_common.schematic_objects[object_id]
301 return()
303###################################################################################################################