001package jmri.jmrit.withrottle;
002
003import java.beans.PropertyChangeEvent;
004import java.beans.PropertyChangeListener;
005import java.util.ArrayList;
006import jmri.InstanceManager;
007import jmri.Turnout;
008import jmri.TurnoutManager;
009import org.apache.commons.lang3.math.NumberUtils;
010import org.slf4j.Logger;
011import org.slf4j.LoggerFactory;
012
013/**
014 *
015 *
016 * @author Brett Hoffman Copyright (C) 2010
017 */
018public class TurnoutController extends AbstractController implements PropertyChangeListener {
019
020    private TurnoutManager manager = null;
021    private final boolean isTurnoutCreationAllowed = InstanceManager.getDefault(WiThrottlePreferences.class).isAllowTurnoutCreation();
022
023
024    public TurnoutController() {
025        manager = InstanceManager.getNullableDefault(jmri.TurnoutManager.class);
026        if (manager == null) {
027            log.info("No turnout manager instance.");
028            isValid = false;
029        } else {
030
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            Turnout t = manager.getBySystemName(sysName);
046            if (t != null) {
047                Object o = t.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 turnout command message
059     * <p>
060     * Format: PTA[command][turnoutname]
061     *   where command is 'C'losed, 'T'hrown, '2'oggle and
062     *   turnoutname is a complete system name or a turnout number only
063     *     if number only, system prefix and letter will be added
064     * Checks for existing turnout and verifies it is allowed, or  
065     *   if Create Turnout preference enabled, will attempt to create turnout
066     * Then sends command to alter state of turnout
067     * Can return HM error messages to client
068     * @param message Command string to be parsed
069     * @param deviceServer client to send responses (error messages) back to
070     */
071    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( value="SLF4J_FORMAT_SHOULD_BE_CONST",
072        justification="I18N of warn Message also sent to deviceServer")
073    @Override
074    void handleMessage(String message, DeviceServer deviceServer) {
075        if (message.charAt(0) == 'A') {
076            String tName = message.substring(2);
077            //first look for existing turnout with name passed in
078            Turnout t = manager.getTurnout(tName);
079            //if not found that way AND input is all numeric, prepend system prefix + type and try again
080            if (t == null && NumberUtils.isDigits(tName)) { 
081                tName = manager.getSystemPrefix() + manager.typeLetter() + tName;
082                t = manager.getTurnout(tName);
083            }
084            //this turnout IS known to JMRI
085            if (t != null) {                
086                //send error if this turnout is not allowed
087                Object o = t.getProperty("WifiControllable");
088                if (o != null && Boolean.valueOf(o.toString())==false) {
089                    String msg = Bundle.getMessage("ErrorTurnoutNotAllowed", t.getSystemName());
090                    log.warn(msg);
091                    deviceServer.sendAlertMessage(msg);
092                    return;
093                }            
094            //turnout is NOT known to JMRI, attempt to create it (if allowed)
095            } else {
096                //check if turnout creation is allowed
097                if (!isTurnoutCreationAllowed) {
098                    String msg = Bundle.getMessage("ErrorTurnoutNotDefined", message.substring(2));
099                    log.warn(msg);
100                    deviceServer.sendAlertMessage(msg);                    
101                    return;
102                } else {
103                    try {
104                        t = manager.provideTurnout(message.substring(2));
105                    } catch (IllegalArgumentException e) {
106                        String msg = Bundle.getMessage("ErrorCreatingTurnout", e.getLocalizedMessage());
107                        log.warn(msg);
108                        deviceServer.sendAlertMessage(msg);
109                        return;
110                    }
111                    String msg = Bundle.getMessage("InfoCreatedTurnout", t.getSystemName());
112                    log.debug(msg);
113                    deviceServer.sendInfoMessage(msg);                    
114                }
115            }
116            
117            switch (message.charAt(1)) {
118                case '2':
119                    if (t.getCommandedState() == Turnout.CLOSED) {
120                        t.setCommandedState(Turnout.THROWN);
121                    } else {
122                        t.setCommandedState(Turnout.CLOSED);
123                    }
124                    break;
125                case 'C':
126                    t.setCommandedState(Turnout.CLOSED);
127                    break;
128                case 'T':
129                    t.setCommandedState(Turnout.THROWN);
130                    break;
131                default:
132                    log.warn("Message \"{}\" unknown.", message);
133                    break;
134            }
135        }
136    }
137
138
139
140    /**
141     * Send Info on turnouts to devices, not specific to any one turnout.
142     * <p>
143     * Format: PTT]\[turnoutText}|{turnoutKey]\[stateText}|{stateKey]\[stateText}|{stateKey...
144     */
145    public void sendTitles() {
146        if (listeners == null) {
147            return;
148        }
149
150        StringBuilder labels = new StringBuilder("PTT");    //  Panel Turnout Titles
151
152        labels.append("]\\[").append(Bundle.getMessage("MenuItemTurnoutTable")).append("}|{Turnout");
153        labels.append("]\\[").append(manager.getClosedText()).append("}|{2");
154        labels.append("]\\[").append(manager.getThrownText()).append("}|{4");
155        labels.append("]\\[").append(Bundle.getMessage("StateUnknown")).append("}|{1");
156        labels.append("]\\[").append(Bundle.getMessage("StateInconsistent")).append("}|{8");
157
158        String message = labels.toString();
159
160        for (ControllerInterface listener : listeners) {
161            listener.sendPacketToDevice(message);
162        }
163
164    }
165
166    /**
167     * Send list of turnouts Format:
168     * PTL]\[SysName}|{UsrName}|{CurrentState]\[SysName}|{UsrName}|{CurrentState
169     * <p>
170     * States: 1 - UNKNOWN, 2 - CLOSED, 4 - THROWN
171     */
172    public void sendList() {
173        if (listeners == null) {
174            return;
175        }
176        if (canBuildList) {
177            buildList(manager);
178        }
179
180        if (sysNameList.isEmpty()) {
181            return;
182        }
183
184        StringBuilder list = new StringBuilder("PTL");  //  Panel Turnout List
185
186        for (String sysName : sysNameList) {
187            Turnout t = manager.getBySystemName(sysName);
188            if (t != null) {
189                list.append("]\\[").append(sysName);
190                list.append("}|{");
191                if (t.getUserName() != null) {
192                    list.append(t.getUserName());
193                }
194                list.append("}|{").append(t.getKnownState());
195                if (canBuildList) {
196                    t.addPropertyChangeListener(this);
197                }
198            }
199        }
200        String message = list.toString();
201
202        for (ControllerInterface listener : listeners) {
203            listener.sendPacketToDevice(message);
204        }
205    }
206
207    /**
208     *
209     */
210    @Override
211    public void propertyChange(PropertyChangeEvent evt) {
212        if (evt.getPropertyName().equals("KnownState")) {
213            Turnout t = (Turnout) evt.getSource();
214            sendTurnoutState(t);
215        }
216    }
217
218    public void sendTurnoutState(Turnout t) {
219        String message;
220
221        message = "PTA" + t.getKnownState() + t.getSystemName();
222
223        for (ControllerInterface listener : listeners) {
224            listener.sendPacketToDevice(message);
225        }
226    }
227
228    @Override
229    public void register() {
230        for (String sysName : sysNameList) {
231            Turnout t = manager.getBySystemName(sysName);
232            if (t != null) {
233                t.addPropertyChangeListener(this);
234                if (log.isDebugEnabled()) {
235                    log.debug("Add listener to Turnout: {}", t.getSystemName());
236                }
237            }
238
239        }
240    }
241
242    @Override
243    public void deregister() {
244        if (sysNameList.isEmpty()) {
245            return;
246        }
247
248        for (String sysName : sysNameList) {
249            Turnout t = manager.getBySystemName(sysName);
250
251            if (t != null) {
252                t.removePropertyChangeListener(this);
253                if (log.isDebugEnabled()) {
254                    log.debug("Remove listener from Turnout: {}", t.getSystemName());
255                }
256            }
257
258        }
259    }
260
261    private final static Logger log = LoggerFactory.getLogger(TurnoutController.class);
262}