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}