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}