Coverage for /home/pi/Software/model-railway-signalling/model_railway_signals/library/signals_ground_disc.py: 84%
67 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 is used for creating and managing Ground disc signal objects
3# --------------------------------------------------------------------------------
5from . import signals_common
6from . import dcc_control
7from . import file_interface
8from . import common
10import logging
11import enum
13# -------------------------------------------------------------------------
14# Classes used externally when creating/updating Ground Disk signals
15# -------------------------------------------------------------------------
17# Define the superset of signal sub types that can be created
18class ground_disc_sub_type(enum.Enum):
19 standard = 1
20 shunt_ahead = 2
22# -------------------------------------------------------------------------
23# Public API function to create a Ground Disc Signal (drawing objects and
24# internal state). By default the Signal is "NOT CLEAR" (i.e. set to DANGER)
25# -------------------------------------------------------------------------
27def create_ground_disc_signal (canvas, sig_id:int, x:int, y:int,
28 signal_subtype=ground_disc_sub_type.standard,
29 sig_callback = None,
30 orientation:int = 0,
31 sig_passed_button: bool = False):
32 logging.info ("Signal "+str(sig_id)+": Creating Ground Disc Signal")
33 # Do some basic validation on the parameters we have been given
34 if signals_common.sig_exists(sig_id): 34 ↛ 35line 34 didn't jump to line 35, because the condition on line 34 was never true
35 logging.error ("Signal "+str(sig_id)+": Signal already exists")
36 elif sig_id < 1: 36 ↛ 37line 36 didn't jump to line 37, because the condition on line 36 was never true
37 logging.error ("Signal "+str(sig_id)+": Signal ID must be greater than zero")
38 elif orientation != 0 and orientation != 180: 38 ↛ 39line 38 didn't jump to line 39, because the condition on line 38 was never true
39 logging.error ("Signal "+str(sig_id)+": Invalid orientation angle - only 0 and 180 currently supported")
40 else:
41 # Define the "Tag" for all drawing objects for this signal instance
42 sig_id_tag = "signal"+str(sig_id)
43 # Draw the signal base
44 line_coords = common.rotate_line (x,y,0,0,0,-11,orientation)
45 canvas.create_line (line_coords,width=2,tags=sig_id_tag)
46 line_coords = common.rotate_line (x,y,0,-11,5,-11,orientation)
47 canvas.create_line (line_coords,width=2,tags=sig_id_tag)
48 # Draw the White disc of the signal
49 oval_coords = common.rotate_line (x,y,+5,-21,+21,-5,orientation)
50 canvas.create_oval(oval_coords,fill="white",outline="black",tags=sig_id_tag)
51 # Draw the banner arms for the signal
52 if signal_subtype == ground_disc_sub_type.shunt_ahead: arm_colour="yellow3"
53 else: arm_colour = "red"
54 line_coords = common.rotate_line(x,y,+13,-21,+13,-5,orientation)
55 sigon = canvas.create_line(line_coords,fill=arm_colour,width=3,tags=sig_id_tag)
56 line_coords = common.rotate_line(x,y,+18,-19,+8,-7,orientation)
57 sigoff = canvas.create_line(line_coords,fill=arm_colour,width=3,tags=sig_id_tag)
59 # Create all of the signal elements common to all signal types
60 signals_common.create_common_signal_elements (canvas, sig_id, x, y,
61 signal_type = signals_common.sig_type.ground_disc,
62 ext_callback = sig_callback,
63 orientation = orientation,
64 sig_passed_button = sig_passed_button,
65 tag = sig_id_tag)
67 # Add all of the signal-specific elements we need to manage Ground Position light signal types
68 signals_common.signals[str(sig_id)]["sig_subtype"] = signal_subtype # Type-specific - signal subtype
69 signals_common.signals[str(sig_id)]["sigon"] = sigon # Type-specific - drawing object
70 signals_common.signals[str(sig_id)]["sigoff"] = sigoff # Type-specific - drawing object
72 # Get the initial state for the signal (if layout state has been successfully loaded)
73 # Note that each element of 'loaded_state' will be 'None' if no data was loaded
74 loaded_state = file_interface.get_initial_item_state("signals",sig_id)
75 # Set the initial state from the "loaded" state - We only need to set the 'override' and
76 # 'sigclear' for ground signals - everything else gets set when the signal is updated
77 if loaded_state["override"]: signals_common.set_signal_override(sig_id)
78 if loaded_state["sigclear"]: signals_common.toggle_signal(sig_id)
79 # Update the signal to show the initial aspect (and send out DCC commands)
80 update_ground_disc_signal(sig_id)
81 # finally Lock the signal if required
82 if loaded_state["siglocked"]: signals_common.lock_signal(sig_id)
83 # Publish the initial state to the broker (for other nodes to consume). Note that changes will
84 # only be published if the MQTT interface has been configured for publishing updates for this
85 # signal. This allows publish/subscribe to be configured prior to signal creation
86 signals_common.publish_signal_state(sig_id)
87 return ()
89# -------------------------------------------------------------------------
90# Internal function to Refresh the aspects of a ground disc signal
91# Note that we expect this function to only ever get called on a state
92# change therefore we don't track the displayed aspect of the signal
93# -------------------------------------------------------------------------
95def update_ground_disc_signal (sig_id:int):
97 # Establish what the signal should be displaying based on the state
98 if not signals_common.signals[str(sig_id)]["sigclear"]:
99 if signals_common.signals[str(sig_id)]["sig_subtype"] == ground_disc_sub_type.shunt_ahead:
100 aspect_to_set = signals_common.signal_state_type.CAUTION
101 else:
102 aspect_to_set = signals_common.signal_state_type.DANGER
103 log_message = " (signal is ON)"
104 elif signals_common.signals[str(sig_id)]["override"]: 104 ↛ 105line 104 didn't jump to line 105, because the condition on line 104 was never true
105 if signals_common.signals[str(sig_id)]["sig_subtype"] == ground_disc_sub_type.shunt_ahead:
106 aspect_to_set = signals_common.signal_state_type.CAUTION
107 else:
108 aspect_to_set = signals_common.signal_state_type.DANGER
109 log_message = " (signal is OVERRIDDEN)"
110 else:
111 aspect_to_set = signals_common.signal_state_type.PROCEED
112 log_message = " (signal is OFF)"
114 # Only refresh the signal if the aspect has been changed
115 if aspect_to_set != signals_common.signals[str(sig_id)]["sigstate"]:
116 logging.info ("Signal "+str(sig_id)+": Changing aspect to " + str(aspect_to_set).rpartition('.')[-1] + log_message)
117 signals_common.signals[str(sig_id)]["sigstate"] = aspect_to_set
119 if signals_common.signals[str(sig_id)]["sigstate"] == signals_common.signal_state_type.PROCEED:
120 signals_common.signals[str(sig_id)]["canvas"].itemconfigure(signals_common.signals[str(sig_id)]["sigoff"],state='normal')
121 signals_common.signals[str(sig_id)]["canvas"].itemconfigure(signals_common.signals[str(sig_id)]["sigon"],state='hidden')
122 dcc_control.update_dcc_signal_element(sig_id,True,element="main_signal")
124 elif ( signals_common.signals[str(sig_id)]["sigstate"] == signals_common.signal_state_type.DANGER or 124 ↛ 132line 124 didn't jump to line 132, because the condition on line 124 was never false
125 signals_common.signals[str(sig_id)]["sigstate"] == signals_common.signal_state_type.CAUTION ):
126 signals_common.signals[str(sig_id)]["canvas"].itemconfigure(signals_common.signals[str(sig_id)]["sigoff"],state='hidden')
127 signals_common.signals[str(sig_id)]["canvas"].itemconfigure(signals_common.signals[str(sig_id)]["sigon"],state='normal')
128 dcc_control.update_dcc_signal_element(sig_id,False,element="main_signal")
130 # Publish the signal changes to the broker (for other nodes to consume). Note that state changes will only
131 # be published if the MQTT interface has been successfully configured for publishing updates for this signal
132 signals_common.publish_signal_state(sig_id)
134 return ()
137###############################################################################