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

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#------------------------------------------------------------------------------------------------------------- 

40 

41import uuid 

42import copy 

43 

44from ...library import track_sensors 

45from ...library import gpio_sensors 

46 

47from .. import run_layout 

48from . import objects_common 

49 

50#------------------------------------------------------------------------------------------------------------- 

51# Default Object parameters (i.e. state at creation) 

52#------------------------------------------------------------------------------------------------------------- 

53 

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] ] 

72 

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#------------------------------------------------------------------------------------ 

80 

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() 

85 

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() 

107 

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#------------------------------------------------------------------------------------ 

115 

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() 

120 

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() 

134 

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#------------------------------------------------------------------------------------ 

142 

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() 

147 

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() 

160 

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#------------------------------------------------------------------------------------ 

168 

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() 

173 

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() 

186 

187#------------------------------------------------------------------------------------------------------------- 

188# Function to to update an object's configuration (and delete/create the associated library objects) 

189#------------------------------------------------------------------------------------------------------------- 

190 

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() 

205 

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#------------------------------------------------------------------------------------------------------------------ 

210 

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() 

225 

226#------------------------------------------------------------------------------------------------------------------ 

227# Function to Create a new default object (and create the associated library objects) 

228#------------------------------------------------------------------------------------------------------------------ 

229 

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) 

246 

247#------------------------------------------------------------------------------------------------------------------ 

248# Function to create a copy of an existing object (and create associated library objects) - returns Object ID 

249#------------------------------------------------------------------------------------------------------------------ 

250 

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) 

273 

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#------------------------------------------------------------------------------------------------------------------ 

278 

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() 

288 

289#------------------------------------------------------------------------------------------------------------------ 

290# Function to 'hard delete' a schematic object (including all associated library objects). 

291#------------------------------------------------------------------------------------------------------------------ 

292 

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() 

302 

303###################################################################################################################