001package jmri.jmris; 002 003import java.beans.PropertyChangeListener; 004import java.io.IOException; 005import java.util.ArrayList; 006 007import jmri.*; 008import org.slf4j.Logger; 009import org.slf4j.LoggerFactory; 010 011/** 012 * Abstract interface between the JMRI Throttles and a network connection 013 * 014 * @author Paul Bender Copyright (C) 2015 015 */ 016abstract public class AbstractThrottleServer implements ThrottleListener { 017 018 private static final Logger log = LoggerFactory.getLogger(AbstractThrottleServer.class); 019 protected ArrayList<Throttle> throttleList; 020 021 public AbstractThrottleServer(){ 022 throttleList = new ArrayList<>(); 023 } 024 025 /* 026 * Protocol Specific Abstract Functions 027 */ 028 abstract public void sendStatus(LocoAddress address) throws IOException; 029 030 abstract public void sendErrorStatus() throws IOException; 031 032 abstract public void sendThrottleFound(LocoAddress address) throws IOException; 033 034 abstract public void sendThrottleReleased(LocoAddress address) throws IOException; 035 036 abstract public void parsecommand(String statusString) throws JmriException, IOException; 037 038 /* 039 * Set Throttle Speed and Direction 040 * 041 * @param l LocoAddress of the locomotive to change speed of. 042 * @param speed float representing the speed, -1 for emergency stop. 043 * @param isForward boolean, true if forward, false if reverse or 044 * undefined. 045 */ 046 public void setThrottleSpeedAndDirection(LocoAddress l, float speed, boolean isForward) { 047 // get the throttle for the address. 048 throttleList.forEach(t -> { 049 if (t.getLocoAddress() == l) { 050 // set the speed and direction. 051 t.setSpeedSetting(speed); 052 t.setIsForward(isForward); 053 } 054 }); 055 } 056 057 /** 058 * Set Throttle Functions on/off. 059 * 060 * @param l LocoAddress of the locomotive to change speed of. 061 * @param fList an ArrayList of boolean values indicating whether the 062 * function is active or not. 063 */ 064 public void setThrottleFunctions(LocoAddress l, ArrayList<Boolean> fList) { 065 // get the throttle for the address. 066 throttleList.forEach(t -> { 067 if (t.getLocoAddress() == l) { 068 setFunctionsByThrottle(t,fList); 069 } 070 }); 071 } 072 073 /** 074 * Set Throttle Functions on/off. 075 * 076 * @param t Throttle to change speed of. 077 * @param fList an ArrayList of boolean values indicating whether the 078 * function is active or not. 079 */ 080 protected void setFunctionsByThrottle(Throttle t, ArrayList<Boolean> fList){ 081 for (int i = 0; i < fList.size(); i++) { 082 if ( i > t.getFunctions().length-1) { 083 log.error("Unable to set Function {} on Throttle {}",i,t.getLocoAddress()); 084 try { 085 sendErrorStatus(); 086 } catch (IOException ioe) { 087 log.error("Error writing to network port"); 088 } 089 } else { 090 t.setFunction(i, fList.get(i)); 091 } 092 } 093 } 094 095 /* 096 * Request a throttle for the specified address from the default 097 * Throttle Manager. 098 * 099 * @param l LocoAddress of the locomotive to request. 100 */ 101 public void requestThrottle(LocoAddress l) { 102 ThrottleManager t = InstanceManager.throttleManagerInstance(); 103 boolean result; 104 result = t.requestThrottle(l, this, false); 105 if (!result) { 106 try { 107 sendErrorStatus(); 108 } catch (IOException ioe) { 109 log.error("Error writing to network port"); 110 } 111 } 112 } 113 114 /* 115 * Release a throttle for the specified address from the default 116 * Throttle Manager. 117 * 118 * @param l LocoAddress of the locomotive to request. 119 */ 120 public void releaseThrottle(LocoAddress l) { 121 ThrottleManager t = InstanceManager.throttleManagerInstance(); 122 t.cancelThrottleRequest(l, this); 123 if (l instanceof DccLocoAddress) { 124 throttleList.forEach(throttle -> { 125 if (throttle.getLocoAddress() == l) { 126 t.releaseThrottle((DccThrottle) throttle, this); 127 throttleList.remove(throttle); 128 try { 129 sendThrottleReleased(l); 130 } catch (IOException ioe) { 131 log.error("Error writing to network port"); 132 } 133 } 134 }); 135 } 136 } 137 138 /** 139 * {@inheritDoc} 140 */ 141 @Override 142 public void notifyThrottleFound(DccThrottle t) { 143 throttleList.add(t); 144 t.addPropertyChangeListener(new ThrottlePropertyChangeListener(this, t)); 145 try { 146 sendThrottleFound(t.getLocoAddress()); 147 } catch (java.io.IOException ioe) { 148 //Something failed writing data to the port. 149 } 150 } 151 152 /** 153 * {@inheritDoc} 154 */ 155 @Override 156 public void notifyFailedThrottleRequest(LocoAddress address, String reason) { 157 try { 158 sendErrorStatus(); 159 } catch (java.io.IOException ioe) { 160 //Something failed writing data to the port. 161 } 162 } 163 164 /** 165 * No steal or share decisions made locally 166 * <p> 167 * {@inheritDoc} 168 */ 169 @Override 170 public void notifyDecisionRequired(LocoAddress address, DecisionType question) { 171 } 172 173 // internal class used to propagate back end throttle changes 174 // to the clients. 175 static class ThrottlePropertyChangeListener implements PropertyChangeListener { 176 177 protected AbstractThrottleServer clientserver = null; 178 protected Throttle throttle = null; 179 180 ThrottlePropertyChangeListener(AbstractThrottleServer ts, Throttle t) { 181 clientserver = ts; 182 throttle = t; 183 } 184 185 // update the state of this throttle if any of the properties change 186 @Override 187 public void propertyChange(java.beans.PropertyChangeEvent e) { 188 switch (e.getPropertyName()) { 189 case Throttle.SPEEDSETTING: 190 case Throttle.SPEEDSTEPS: 191 case Throttle.ISFORWARD: 192 try { 193 clientserver.sendStatus(throttle.getLocoAddress()); 194 } catch (IOException ioe) { 195 log.error("Error writing to network port"); 196 } 197 break; 198 default: 199 for (int i = 0; i <= 28; i++) { 200 if (e.getPropertyName().equals("F" + i)) { 201 try { 202 clientserver.sendStatus(throttle.getLocoAddress()); 203 } catch (IOException ioe) { 204 log.error("Error writing to network port"); 205 } 206 break; // stop the loop, only one function property will be matched. 207 } else if (e.getPropertyName().equals("F" + i + "Momentary")) { 208 try { 209 clientserver.sendStatus(throttle.getLocoAddress()); 210 } catch (IOException ioe) { 211 log.error("Error writing to network port"); 212 } 213 break; // stop the loop, only one function property will be matched. 214 } 215 } 216 break; 217 } 218 219 log.debug("Property change event received {} / {}", e.getPropertyName(), e.getNewValue()); 220 } 221 } 222 223}