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
« 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#------------------------------------------------------------------------------------
41import uuid
42import copy
44from ...library import block_instruments
46from . import objects_common
47from . import objects_signals
48from .. import run_layout
50#------------------------------------------------------------------------------------
51# Default Block Instrument Objects (i.e. state at creation)
52#------------------------------------------------------------------------------------
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"] = ""
61#------------------------------------------------------------------------------------
62# Internal function Update references from instruments linked to this one
63#------------------------------------------------------------------------------------
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()
75#------------------------------------------------------------------------------------
76# Internal function to Remove references from instruments linked to this one
77#------------------------------------------------------------------------------------
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()
89#------------------------------------------------------------------------------------
90# Function to to update an instrument object after a configuration change
91#------------------------------------------------------------------------------------
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()
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#------------------------------------------------------------------------------------
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()
136#------------------------------------------------------------------------------------
137# Function to Create a new default Block Instrument (and draw it on the canvas)
138#------------------------------------------------------------------------------------
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)
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#------------------------------------------------------------------------------------
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)
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#------------------------------------------------------------------------------------
190def delete_instrument_object(object_id):
191 block_instruments.delete_instrument(objects_common.schematic_objects[object_id]["itemid"])
192 return()
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#------------------------------------------------------------------------------------
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()
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#------------------------------------------------------------------------------------
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()
225####################################################################################