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