001package jmri.jmris;
002
003import java.beans.PropertyChangeEvent;
004import java.beans.PropertyChangeListener;
005import java.io.IOException;
006import java.util.HashMap;
007import java.util.Map;
008
009import jmri.InstanceManager;
010import jmri.JmriException;
011import jmri.SignalHead;
012import jmri.server.json.JsonException;
013import org.slf4j.Logger;
014import org.slf4j.LoggerFactory;
015
016/**
017 * Abstract interface between a JMRI signal head and a network connection
018 *
019 * @author Paul Bender Copyright (C) 2010
020 */
021abstract public class AbstractSignalHeadServer {
022
023    private final HashMap<String, SignalHeadListener> signalHeads;
024    private static final Logger log = LoggerFactory.getLogger(AbstractSignalHeadServer.class);
025
026    public AbstractSignalHeadServer(){
027        signalHeads = new HashMap<>();
028    }
029
030    /*
031     * Protocol Specific Abstract Functions
032     */
033    abstract public void sendStatus(String signalHead, int Status) throws IOException;
034
035    abstract public void sendErrorStatus(String signalHead) throws IOException;
036
037    abstract public void parseStatus(String statusString) throws JmriException, IOException, JsonException;
038
039    synchronized protected void addSignalHeadToList(String signalHeadName) {
040        if (!signalHeads.containsKey(signalHeadName)) {
041            SignalHead sh = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(signalHeadName);
042            if(sh!=null) {
043               SignalHeadListener shl = new SignalHeadListener(signalHeadName);
044               sh.addPropertyChangeListener(shl);
045               signalHeads.put(signalHeadName, shl );
046               log.debug("Added listener to signalHead {}", signalHeadName);
047            }
048        }
049    }
050
051    synchronized protected void removeSignalHeadFromList(String signalHeadName) {
052        if (signalHeads.containsKey(signalHeadName)) {
053            SignalHead sh = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(signalHeadName);
054            if(sh!=null) {
055               sh.removePropertyChangeListener(signalHeads.get(signalHeadName));
056               signalHeads.remove(signalHeadName);
057            }
058        }
059    }
060
061    public void setSignalHeadAppearance(String signalHeadName, String signalHeadState) {
062        this.setSignalHeadAppearance(signalHeadName, this.appearanceForName(signalHeadState));
063    }
064
065    protected void setSignalHeadAppearance(String signalHeadName, int signalHeadState) {
066        SignalHead signalHead;
067        try {
068            addSignalHeadToList(signalHeadName);
069            signalHead = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(signalHeadName);
070            if (signalHead == null) {
071                // only log, since this may be from a remote system
072                log.error("SignalHead {} is not available.", signalHeadName);
073            } else {
074                if (signalHead.getAppearance() != signalHeadState || signalHead.getHeld()) {
075                    if (signalHeadState == SignalHead.HELD) {
076                        signalHead.setHeld(true);
077                    } else {
078                        if (signalHead.getHeld()) signalHead.setHeld(false);
079                        signalHead.setAppearance(signalHeadState);
080                    }
081                } else {
082                    try {
083                        sendStatus(signalHeadName, signalHeadState);
084                    } catch (IOException ex) {
085                        log.error("Error sending appearance", ex);
086                    }
087                }
088            }
089        } catch (Exception ex) {
090            log.error("Exception setting signalHead {} appearance:", signalHeadName, ex);
091        }
092    }
093
094    protected String nameForAppearance(int appearance) {
095        switch (appearance) {
096            case SignalHead.DARK:
097                return "DARK";
098            case SignalHead.RED:
099                return "RED";
100            case SignalHead.FLASHRED:
101                return "FLASHRED";
102            case SignalHead.YELLOW:
103                return "YELLOW";
104            case SignalHead.FLASHYELLOW:
105                return "FLASHYELLOW";
106            case SignalHead.GREEN:
107                return "GREEN";
108            case SignalHead.FLASHGREEN:
109                return "FLASHGREEN";
110            case SignalHead.LUNAR:
111                return "LUNAR";
112            case SignalHead.FLASHLUNAR:
113                return "FLASHLUNAR";
114            case SignalHead.HELD:
115                return "HELD";
116            default:
117                return "UNKNOWN";
118        }
119    }
120
121    protected int appearanceForName(String name) {
122        if (name.equals("DARK")) {
123            return SignalHead.DARK;
124        } else if (name.equals("RED")) {
125            return SignalHead.RED;
126        } else if (name.equals("FLASHRED")) {
127            return SignalHead.FLASHRED;
128        } else if (name.equals("YELLOW")) {
129            return SignalHead.YELLOW;
130        } else if (name.equals("FLASHYELLOW")) {
131            return SignalHead.FLASHYELLOW;
132        } else if (name.equals("GREEN")) {
133            return SignalHead.GREEN;
134        } else if (name.equals("FLASHGREEN")) {
135            return SignalHead.FLASHGREEN;
136        } else if (name.equals("LUNAR")) {
137            return SignalHead.LUNAR;
138        } else if (name.equals("FLASHLUNARDARK")) {
139            return SignalHead.DARK;
140        } else if (name.equals("FLASHLUNAR")) {
141            return SignalHead.FLASHLUNAR;
142        } else {
143            return SignalHead.DARK;
144        }
145    }
146
147    public void dispose() {
148        for (Map.Entry<String, SignalHeadListener> signalHead : this.signalHeads.entrySet()) {
149            SignalHead sh = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(signalHead.getKey());
150            if(sh != null) {
151               sh.removePropertyChangeListener(signalHead.getValue());
152            }
153        }
154        this.signalHeads.clear();
155    }
156
157    class SignalHeadListener implements PropertyChangeListener {
158
159        String name = null;
160        SignalHead signalHead = null;
161
162        SignalHeadListener(String signalHeadName) {
163            name = signalHeadName;
164            signalHead = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(signalHeadName);
165        }
166
167        // update state as state of signalHead changes
168        @Override
169        public void propertyChange(PropertyChangeEvent e) {
170            if (e.getPropertyName().equals("Appearance") || e.getPropertyName().equals("Held")) {
171                SignalHead sh = (SignalHead) e.getSource();
172                int state = sh.getAppearance();
173                if (sh.getHeld()) {
174                    state = SignalHead.HELD;
175                }
176                try {
177                    sendStatus(name, state);
178                } catch (IOException ie) {
179                    // if we get an error, de-register
180                    if (log.isDebugEnabled()) {
181                        log.debug("Unable to send status, removing listener from signalHead {}", name);
182                    }
183                    signalHead.removePropertyChangeListener(this);
184                    removeSignalHeadFromList(name);
185                }
186            }
187        }
188    }
189}