Coverage for /home/pi/Software/model-railway-signalling/model_railway_signals/editor/objects/objects_instruments.py: 100%

85 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2024-04-05 17:29 +0100

1#------------------------------------------------------------------------------------ 

2# This module contains all the functions for managing Block Instrument objects 

3#------------------------------------------------------------------------------------ 

4# 

5# External API functions intended for use by other editor modules: 

6# create_instrument() - Create a default block instrument object on the schematic 

7# delete_instrument(object_id) - Hard Delete an object when deleted from the schematic 

8# update_instrument(obj_id,new_obj) - Update the configuration of an existing instrument object 

9# paste_instrument(object) - Paste a copy of an object to create a new one (returns new object_id) 

10# delete_instrument_object(object_id) - Soft delete the drawing object (prior to recreating) 

11# redraw_instrument_object(object_id) - Redraw the object on the canvas following an update 

12# default_instrument_object - The dictionary of default values for the object 

13# mqtt_update_instruments(pub_list, sub_list) - Configure MQTT publish/subscribe 

14# 

15# Makes the following external API calls to other editor modules: 

16# objects_common.set_bbox - to create/update the boundary box for the schematic object 

17# objects_common.find_initial_canvas_position - to find the next 'free' canvas position 

18# objects_common.new_item_id - to find the next 'free' item ID when creating objects 

19# objects_signals.update_references_to_instrument - when the instrument ID is changed 

20# objects_signals.remove_references_to_instrument - when the instrument is deleted 

21# 

22# Accesses the following external editor objects directly: 

23# run_layout.schematic_callback - setting the object callbacks when created/recreated 

24# objects_common.schematic_objects - the master dictionary of Schematic Objects 

25# objects_common.instrument_index - The index of Instrument Objects (for iterating) 

26# objects_common.default_object - The common dictionary element for all objects 

27# objects_common.object_type - The Enumeration of supported objects 

28# objects_common.canvas - Reference to the Tkinter drawing canvas 

29# 

30# Makes the following external API calls to library modules: 

31# block_instruments.instrument_exists - Common function to see if a given item exists 

32# block_instruments.delete_instrument(id) - delete library drawing object (part of soft delete) 

33# block_instruments.create_block_instrument(id) - To create the library object (create or redraw) 

34# block_instruments.update_linked_instrument(old_id, new_id) - update the linked instrument reference 

35# block_instruments.get_tags(id) - get the canvas 'tags' for the instrument drawing objects 

36# block_instruments.reset_mqtt_configuration - reset MQTT networking prior to reconfiguration 

37# block_instruments.set_instruments_to_publish_state(IDs) - configure MQTT networking 

38# block_instruments.subscribe_to_remote_instrument(ID) - configure MQTT networking 

39#------------------------------------------------------------------------------------ 

40 

41import uuid 

42import copy 

43 

44from ...library import block_instruments 

45 

46from . import objects_common 

47from . import objects_signals 

48from .. import run_layout 

49 

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

51# Default Block Instrument Objects (i.e. state at creation) 

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

53 

54default_instrument_object = copy.deepcopy(objects_common.default_object) 

55default_instrument_object["item"] = objects_common.object_type.instrument 

56default_instrument_object["itemtype"] = block_instruments.instrument_type.single_line.value 

57default_instrument_object["bellsound"] = "bell-ring-01.wav" 

58default_instrument_object["keysound"] = "telegraph-key-01.wav" 

59default_instrument_object["linkedto"] = "" 

60 

61#------------------------------------------------------------------------------------ 

62# Internal function Update references from instruments linked to this one 

63#------------------------------------------------------------------------------------ 

64 

65def update_references_to_instrument(old_inst_id:int, new_inst_id:int): 

66 # Iterate through all the instruments on the schematic 

67 for instrument_id in objects_common.instrument_index: 

68 # get the instrument object (so we can query the ID of the linked instrument) 

69 instrument_object = objects_common.instrument(instrument_id) 

70 if objects_common.schematic_objects[instrument_object]["linkedto"] == str(old_inst_id): 

71 objects_common.schematic_objects[instrument_object]["linkedto"] = str(new_inst_id) 

72 block_instruments.update_linked_instrument(int(instrument_id), str(new_inst_id)) 

73 return() 

74 

75#------------------------------------------------------------------------------------ 

76# Internal function to Remove references from instruments linked to this one 

77#------------------------------------------------------------------------------------ 

78 

79def remove_references_to_instrument(deleted_inst_id:int): 

80 # Iterate through all the instruments on the schematic 

81 for instrument_id in objects_common.instrument_index: 

82 # get the instrument object (so we can query the ID of the linked instrument) 

83 instrument_object = objects_common.instrument(instrument_id) 

84 if objects_common.schematic_objects[instrument_object]["linkedto"] == str(deleted_inst_id): 

85 objects_common.schematic_objects[instrument_object]["linkedto"] = "" 

86 block_instruments.update_linked_instrument(int(instrument_id), "") 

87 return() 

88 

89#------------------------------------------------------------------------------------ 

90# Function to to update an instrument object after a configuration change 

91#------------------------------------------------------------------------------------ 

92 

93def update_instrument(object_id, new_object_configuration): 

94 # We need to track whether the Item ID has changed 

95 old_item_id = objects_common.schematic_objects[object_id]["itemid"] 

96 new_item_id = new_object_configuration["itemid"] 

97 # Delete the existing instrument object, copy across the new config and redraw 

98 delete_instrument_object(object_id) 

99 objects_common.schematic_objects[object_id] = copy.deepcopy(new_object_configuration) 

100 redraw_instrument_object(object_id) 

101 # Check to see if the Type-specific ID has been changed 

102 if old_item_id != new_item_id: 

103 # Update the type-specific index 

104 del objects_common.instrument_index[str(old_item_id)] 

105 objects_common.instrument_index[str(new_item_id)] = object_id 

106 # Update any signal 'block ahead' references when the ID is changed 

107 objects_signals.update_references_to_instrument(old_item_id, new_item_id) 

108 # Update any references from linked instruments when the ID is changed 

109 update_references_to_instrument(old_item_id, new_item_id) 

110 return() 

111 

112#------------------------------------------------------------------------------------ 

113# Function to redraw an Instrument object on the schematic. Called when the object 

114# is first reated or after the object configuration has been updated. 

115#------------------------------------------------------------------------------------ 

116 

117def redraw_instrument_object(object_id): 

118 # Turn the instrument type value back into the required enumeration type 

119 instrument_type = block_instruments.instrument_type(objects_common.schematic_objects[object_id]["itemtype"]) 

120 # Create the new Block Instrument object 

121 canvas_tags = block_instruments.create_instrument ( 

122 canvas = objects_common.canvas, 

123 inst_id = objects_common.schematic_objects[object_id]["itemid"], 

124 inst_type = instrument_type, 

125 x = objects_common.schematic_objects[object_id]["posx"], 

126 y = objects_common.schematic_objects[object_id]["posy"], 

127 callback = run_layout.schematic_callback, 

128 bell_sound_file = objects_common.schematic_objects[object_id]["bellsound"], 

129 telegraph_sound_file = objects_common.schematic_objects[object_id]["keysound"], 

130 linked_to = objects_common.schematic_objects[object_id]["linkedto"]) 

131 # Create/update the canvas "tags" and selection rectangle for the instrument 

132 objects_common.schematic_objects[object_id]["tags"] = canvas_tags 

133 objects_common.set_bbox (object_id, objects_common.canvas.bbox(canvas_tags)) 

134 return() 

135 

136#------------------------------------------------------------------------------------ 

137# Function to Create a new default Block Instrument (and draw it on the canvas) 

138#------------------------------------------------------------------------------------ 

139 

140def create_instrument(item_type): 

141 # Generate a new object from the default configuration with a new UUID  

142 object_id = str(uuid.uuid4()) 

143 objects_common.schematic_objects[object_id] = copy.deepcopy(default_instrument_object) 

144 # Find the initial canvas position for the new object and assign the item ID 

145 x, y = objects_common.find_initial_canvas_position() 

146 item_id = objects_common.new_item_id(exists_function=block_instruments.instrument_exists) 

147 # Add the specific elements for this particular instance of the instrument 

148 objects_common.schematic_objects[object_id]["itemid"] = item_id 

149 objects_common.schematic_objects[object_id]["itemtype"] = item_type 

150 objects_common.schematic_objects[object_id]["posx"] = x 

151 objects_common.schematic_objects[object_id]["posy"] = y 

152 # Add the new object to the index of instruments 

153 objects_common.instrument_index[str(item_id)] = object_id 

154 # Draw the object on the canvas 

155 redraw_instrument_object(object_id) 

156 return(object_id) 

157 

158#------------------------------------------------------------------------------------ 

159# Function to Paste a copy of an existing Block Instrument - returns the new Object ID 

160# Note that only the basic instrument configuration is used. Underlying configuration 

161# such as the linked instruments is set back to the defaults as it will need to be 

162# configured specific to the new instrument 

163#------------------------------------------------------------------------------------ 

164 

165def paste_instrument(object_to_paste, deltax:int, deltay:int): 

166 # Create a new UUID for the pasted object 

167 new_object_id = str(uuid.uuid4()) 

168 objects_common.schematic_objects[new_object_id] = copy.deepcopy(object_to_paste) 

169 # Assign a new type-specific ID for the object and add to the index 

170 new_id = objects_common.new_item_id(exists_function=block_instruments.instrument_exists) 

171 objects_common.schematic_objects[new_object_id]["itemid"] = new_id 

172 objects_common.instrument_index[str(new_id)] = new_object_id 

173 # Set the position for the "pasted" object (offset from the original position) 

174 objects_common.schematic_objects[new_object_id]["posx"] += deltax 

175 objects_common.schematic_objects[new_object_id]["posy"] += deltay 

176 # Now set the default values for all elements we don't want to copy: 

177 objects_common.schematic_objects[new_object_id]["linkedto"] = default_instrument_object["linkedto"] 

178 # Set the Boundary box for the new object to None so it gets created on re-draw 

179 objects_common.schematic_objects[new_object_id]["bbox"] = None 

180 # Draw the new object 

181 redraw_instrument_object(new_object_id) 

182 return(new_object_id) 

183 

184#------------------------------------------------------------------------------------ 

185# Function to "soft delete" the instrument object from the canvas - Primarily used to 

186# delete the block instrument in its current configuration prior to re-creating in its 

187# new configuration - also called as part of a hard delete (below). 

188#------------------------------------------------------------------------------------ 

189 

190def delete_instrument_object(object_id): 

191 block_instruments.delete_instrument(objects_common.schematic_objects[object_id]["itemid"]) 

192 return() 

193 

194#------------------------------------------------------------------------------------ 

195# Function to 'hard delete' a block instrument (drawing objects and the main 

196# dictionary entry). Function called when object is deleted from the schematic. 

197#------------------------------------------------------------------------------------ 

198 

199def delete_instrument(object_id): 

200 # Soft delete the associated library objects from the canvas 

201 delete_instrument_object(object_id) 

202 # Remove any references to the instrument from other (linked) instruments 

203 remove_references_to_instrument(objects_common.schematic_objects[object_id]["itemid"]) 

204 # Remove any references to the block instrument from the signal interlocking tables 

205 objects_signals.remove_references_to_instrument(objects_common.schematic_objects[object_id]["itemid"]) 

206 # "Hard Delete" the selected object - deleting the boundary box rectangle and deleting 

207 # the object from the dictionary of schematic objects (and associated dictionary keys) 

208 objects_common.canvas.delete(objects_common.schematic_objects[object_id]["bbox"]) 

209 del objects_common.instrument_index[str(objects_common.schematic_objects[object_id]["itemid"])] 

210 del objects_common.schematic_objects[object_id] 

211 return() 

212 

213#------------------------------------------------------------------------------------ 

214# Function to update the MQTT networking configuration for instruments, namely 

215# subscribing to remote instruments and setting local instruments to publish state 

216#------------------------------------------------------------------------------------ 

217 

218def mqtt_update_instruments(instruments_to_publish:list, instruments_to_subscribe_to:list): 

219 block_instruments.reset_mqtt_configuration() 

220 block_instruments.set_instruments_to_publish_state(*instruments_to_publish) 

221 for instrument_identifier in instruments_to_subscribe_to: 

222 block_instruments.subscribe_to_remote_instrument(instrument_identifier) 

223 return() 

224 

225####################################################################################