001package jmri.jmrit.withrottle; 002 003import java.beans.PropertyChangeEvent; 004import java.beans.PropertyChangeListener; 005import java.util.ArrayList; 006import java.util.HashMap; 007import java.util.Map; 008import jmri.InstanceManager; 009import jmri.NamedBeanHandle; 010import jmri.Route; 011import jmri.RouteManager; 012import jmri.Sensor; 013 014/** 015 * 016 * 017 * @author Brett Hoffman Copyright (C) 2010 018 */ 019public class RouteController extends AbstractController implements PropertyChangeListener { 020 021 private RouteManager manager = null; 022 private HashMap<NamedBeanHandle<Sensor>, Route> indication; // Monitor turnouts for aligned status 023 024 public RouteController() { 025 manager = InstanceManager.getNullableDefault(jmri.RouteManager.class); 026 if (manager == null) { 027 log.info("No route manager instance."); 028 isValid = false; 029 } else { 030 indication = new HashMap<>(); 031 isValid = true; 032 } 033 } 034 035 @Override 036 boolean verifyCreation() { 037 038 return isValid; 039 } 040 041 @Override 042 public void filterList() { 043 ArrayList<String> tempList = new ArrayList<>(0); 044 for (String sysName : sysNameList) { 045 Route r = manager.getBySystemName(sysName); 046 if (r != null) { 047 Object o = r.getProperty("WifiControllable"); 048 if (o == null || Boolean.valueOf(o.toString())) { 049 // Only skip if 'false' 050 tempList.add(sysName); 051 } 052 } 053 } 054 sysNameList = tempList; 055 } 056 057 /** 058 * parse and process a route command message 059 * <p> 060 * Format: PRA[command][routename] 061 * where command is always '2' for Toggle 062 * routename is a complete system name 063 * Can return HM error messages to client 064 * @param message Command string to be parsed 065 * @param deviceServer client to send responses (error messages) back to 066 */ 067 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( value="SLF4J_FORMAT_SHOULD_BE_CONST", 068 justification="I18N of warn Message also sent to deviceServer") 069 @Override 070 void handleMessage(String message, DeviceServer deviceServer) { 071 String rName = message.substring(2); 072 try { 073 if (message.charAt(0) == 'A' && message.charAt(1) == '2') { 074 Route r = manager.getBySystemName(rName); 075 if (r != null) { 076 r.setRoute(); 077 log.debug("Route '{}' set.", rName); 078 } else { 079 String msg = Bundle.getMessage("ErrorRouteNotDefined", rName); 080 log.warn(msg); 081 deviceServer.sendAlertMessage(msg); 082 } 083 } else { 084 String msg = Bundle.getMessage("ErrorRouteBadMessage", message); 085 log.warn(msg); 086 deviceServer.sendAlertMessage(msg); 087 } 088 089 } catch (NullPointerException exb) { 090 String msg = Bundle.getMessage("ErrorRouteOther", rName); 091 log.warn(msg); 092 deviceServer.sendAlertMessage(msg); 093 } 094 } 095 096 /** 097 * Send Info on routes to devices, not specific to any one route. 098 * <p> 099 * Format: PRT]\[routeText}|{routeKey]\[stateText}|{stateKey]\[stateText}|{stateKey... 100 */ 101 public void sendTitles() { 102 if (listeners == null) { 103 return; 104 } 105 106 StringBuilder labels = new StringBuilder("PRT"); // Panel Route Titles 107 108 labels.append("]\\[").append(Bundle.getMessage("MenuItemRouteTable")).append("}|{Route"); // should Route be translated? 109 labels.append("]\\[").append(Bundle.getMessage("StateActive")).append("}|{2"); 110 labels.append("]\\[").append(Bundle.getMessage("StateInactive")).append("}|{4"); 111 labels.append("]\\[").append(Bundle.getMessage("StateUnknown")).append("}|{0"); 112 labels.append("]\\[").append(Bundle.getMessage("StateInconsistent")).append("}|{8"); 113 labels.append("]\\[").append(Bundle.getMessage("StateUnknown")).append("}|{"); //define no feedback as Unknown 114 115 String message = labels.toString(); 116 117 for (ControllerInterface listener : listeners) { 118 listener.sendPacketToDevice(message); 119 } 120 121 } 122 123 private final jmri.NamedBeanHandleManager nbhm = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class); 124 125 /** 126 * Send list of routes Format: 127 * PRL]\[SysName}|{UsrName}|{CurrentState]\[SysName}|{UsrName}|{CurrentState 128 * <p> 129 * States: 1 - UNKNOWN, 2 - ACTIVE, 4 - INACTIVE (based on turnoutsAligned 130 * sensor, if used) 131 */ 132 public void sendList() { 133 if (listeners == null) { 134 return; 135 } 136 if (canBuildList) { 137 buildList(manager); 138 } 139 if (sysNameList.isEmpty()) { 140 return; 141 } 142 143 StringBuilder list = new StringBuilder("PRL"); // Panel Route List 144 145 for (String sysName : sysNameList) { 146 Route r = manager.getBySystemName(sysName); 147 if (r != null) { 148 list.append("]\\[").append(sysName); 149 list.append("}|{"); 150 if (r.getUserName() != null) { 151 list.append(r.getUserName()); 152 } 153 list.append("}|{"); 154 String turnoutsAlignedSensor = r.getTurnoutsAlignedSensor(); 155 if (turnoutsAlignedSensor != null && !turnoutsAlignedSensor.isEmpty()) { //only set if found 156 try { 157 Sensor routeAligned = InstanceManager.sensorManagerInstance() 158 .provideSensor(turnoutsAlignedSensor); 159 list.append(routeAligned.getKnownState()); 160 } catch (IllegalArgumentException ex) { 161 log.warn("Failed to provide turnoutsAlignedSensor \"{}\" in sendList", turnoutsAlignedSensor); 162 } 163 } 164 } 165 } 166 String message = list.toString(); 167 168 for (ControllerInterface listener : listeners) { 169 listener.sendPacketToDevice(message); 170 } 171 } 172 173 /** 174 * This is on the aligned sensor, not the route itself. 175 */ 176 @Override 177 public void propertyChange(PropertyChangeEvent evt) { 178 if (Sensor.PROPERTY_KNOWN_STATE.equals(evt.getPropertyName())) { 179 Sensor s = (Sensor) evt.getSource(); 180 for (Map.Entry<NamedBeanHandle<Sensor>, Route> entry : indication.entrySet()) { 181 if (entry.getKey().getBean() == s) { 182 Route r = entry.getValue(); 183 String message = "PRA" + s.getKnownState() + r.getSystemName(); 184 185 for (ControllerInterface listener : listeners) { 186 listener.sendPacketToDevice(message); 187 } 188 return; 189 } 190 } 191 } 192 } 193 194 /** 195 * Register this as a listener of each managed route's aligned sensor. 196 */ 197 @Override 198 public void register() { 199 for (String sysName : sysNameList) { 200 Route r = manager.getBySystemName(sysName); 201 if (r != null) { 202 String turnoutsAlignedSensor = r.getTurnoutsAlignedSensor(); 203 if ( turnoutsAlignedSensor != null && !turnoutsAlignedSensor.isEmpty()) { //only set if found 204 Sensor sensor = InstanceManager.sensorManagerInstance().provideSensor(turnoutsAlignedSensor); 205 NamedBeanHandle<Sensor> routeAligned = nbhm.getNamedBeanHandle(turnoutsAlignedSensor, sensor); 206 indication.put(routeAligned, r); 207 sensor.addPropertyChangeListener(this, routeAligned.getName(), "Wi Throttle Route Controller"); 208 log.debug("Add listener to Sensor: {} for Route: {}", routeAligned.getName(), r.getSystemName()); 209 } 210 } 211 } 212 } 213 214 /** 215 * Remove this from each managed route's aligned sensor. 216 */ 217 @Override 218 public void deregister() { 219 if (sysNameList.isEmpty()) { 220 return; 221 } 222 223 indication.keySet().forEach( namedSensor -> { 224 namedSensor.getBean().removePropertyChangeListener(this); 225 if (log.isDebugEnabled()) { 226 log.debug("Removing listener from Sensor: {} for Route: {}", 227 namedSensor.getName(), indication.get(namedSensor).getSystemName()); 228 } 229 }); 230 indication = new HashMap<>(); 231 } 232 233 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(RouteController.class); 234 235}