def trace_power_flow(): # 1. Define the network structure in order from left to right # This makes it easy to find neighbors. ds_names = ["DS-1A1", "DS-1B1"] # 2. Build the list of all tags we need to read # Using readBlocking for a single, efficient tag read tag_paths_to_read = [ '[Ignition_Common_IO_Gtwy]Cables/SES/SESA_Out1', # Assumes a tag for the source status '[Ignition_Common_IO_Gtwy]Cables/SES/SESB_Out3' ] for name in ds_names: # Assuming your UDT instances are in a folder named 'DS' base_path = "[Ignition_Common_IO_Gtwy]Yard/SATB1_YARD_{}".format(name) tag_paths_to_read.append("{}/Breaker Closed Way 01".format(base_path)) tag_paths_to_read.append("{}/Breaker Closed Way 02".format(base_path)) # 3. Read all tags at once try: tag_values = system.tag.readBlocking(tag_paths_to_read) except Exception as e: # Log error if tags can't be read system.util.getLogger("PowerTrace").error("Error reading tags: {}".format(e)) return # Create a dictionary for easy access to tag values # e.g., values['UT-HS1']['Switch_L_Status'] values = {} values['SESA-FRD1_Live'] = tag_values[0].value values['SESB-FRD3_Live'] = tag_values[1].value read_idx = 2 for name in ds_names: values[name] = { 'Way01': tag_values[read_idx].value, 'Way02': tag_values[read_idx + 1].value } read_idx += 2 # 4. The Tracing Algorithm # This dictionary will store the final state: e.g., energized_state['UT P1.2'] = 'DS-1A1' energized_state = {} # A queue for our BFS traversal, storing (ut_name, source) # Using a list as a queue: append to add, pop(0) to remove from front q = [] # Initialize the queue with active sources if values['SESA-FRD1_Live']: q.append( ("DS-1A1", "SESA-FRD1") ) if values['SESB-FRD3_Live']: q.append( ("DS-1B1", "SESB-FRD3") ) # Process the queue until it's empty visited = set() # Keep track of UTs we've already processed to prevent infinite loops while q: current_ds_name, source = q.pop(0) if current_ds_name in visited: continue visited.add(current_ds_name) energized_state[current_ds_name] = source current_ut_index = ds_names.index(current_ds_name) # Check for propagation to the RIGHT (-->) if current_ut_index < len(ds_names) - 1: neighbor_name = ds_names[current_ut_index + 1] # Condition: Current UT's right switch is closed AND Neighbor's left switch is closed if values[current_ds_name]['Way02'] and values[neighbor_name]['Way02']: if neighbor_name not in visited: q.append( (neighbor_name, source) ) # Check for propagation to the LEFT (<--) if current_ut_index > 0: neighbor_name = ds_names[current_ut_index - 1] # Condition: Current UT's left switch is closed AND Neighbor's right switch is closed if values[current_ds_name]['Way02'] and values[neighbor_name]['Way02']: if neighbor_name not in visited: q.append( (neighbor_name, source) ) print energized_state # 5. Prepare and write the results back to Ignition tags tag_paths_to_write = [] values_to_write = [] # Set UT states for name in ds_names: is_energized = name in energized_state power_source = energized_state.get(name, 'None') tag_paths_to_write.append("[Ignition_Common_IO_Gtwy]Yard/SATB1_YARD_{}/Data/isEnergized".format(name)) values_to_write.append(is_energized) tag_paths_to_write.append("[Ignition_Common_IO_Gtwy]Yard/SATB1_YARD_{}/Data/PowerSource".format(name)) values_to_write.append(power_source) # Write all values in a single, efficient call if tag_paths_to_write: system.tag.writeBlocking(tag_paths_to_write, values_to_write)