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}