001package jmri.jmrix.can.cbus.simulator; 002 003import java.util.ArrayList; 004import jmri.jmrix.AbstractMessage; 005import jmri.jmrix.can.CanReply; 006import jmri.jmrix.can.CanSystemConnectionMemo; 007import jmri.jmrix.can.cbus.CbusConstants; 008import jmri.jmrix.can.cbus.swing.simulator.CsPane; 009import org.slf4j.Logger; 010import org.slf4j.LoggerFactory; 011 012/** 013 * Simulating a MERG CBUS Command Station. 014 * Can operate as stand-alone or managed via @CbusSimulator. 015 * 016 * @author Steve Young Copyright (C) 2018 2019 017 * @see CbusSimulator 018 * @see CbusDummyCSSession 019 * @since 4.15.2 020 */ 021public class CbusDummyCS extends CbusSimCanListener { 022 023 private ArrayList<CbusDummyCSSession> _csSessions; 024 private int _simType; 025 private int _maxSessions; 026 private int _currentSessions; 027 private boolean _trackOn; 028 private boolean _estop; 029 private CsPane _pane; 030 031 public ArrayList<String> csTypes = new ArrayList<>(); 032 public ArrayList<String> csTypesTip = new ArrayList<>(); 033 034 protected static int DEFAULT_CS_TIMEOUT = 60000; // ms 035 protected static int DEFAULT_SESSION_START_SPDDIR = 128; // default DCC speed direction on start session 036 037 public CbusDummyCS( CanSystemConnectionMemo sysmemo ){ 038 super(sysmemo,null); 039 init(); 040 } 041 042 private void init() { 043 044 _csSessions = new ArrayList<>(); 045 _maxSessions = 32; 046 _currentSessions = 0; 047 setDelay(100); 048 _trackOn = true; 049 _estop = false; 050 _pane = null; 051 052 csTypes.add(Bundle.getMessage("cSDisabled")); 053 csTypesTip.add(null); 054 csTypes.add(Bundle.getMessage("csStandard")); 055 csTypesTip.add("Based on CANCMD v3"); 056 setDummyType(1); 057 } 058 059 public int getNumberSessions(){ 060 return _currentSessions; 061 } 062 063 public void resetCS() { 064 for ( int i=0 ; (i < _csSessions.size()) ; i++) { 065 destroySession(_csSessions.get(i)); 066 } 067 _csSessions = null; 068 _csSessions = new ArrayList<>(); 069 _currentSessions = 0; 070 if ( _pane != null ){ 071 _pane.setNumSessions(_currentSessions); 072 } 073 } 074 075 public void setDummyType(int type){ 076 _simType = type; 077 if ( type == 0 ){ 078 resetCS(); 079 } 080 log.info("Simulated Command Station: {}", csTypes.get(_simType) ); 081 } 082 083 public int getDummyType() { 084 return _simType; 085 } 086 087 public void setPane(CsPane pane) { 088 _pane = pane; 089 } 090 091 // move to private in future 092 public boolean getResponseRSTAT() { 093 log.debug("estop {}",_estop); 094 return false; 095 } 096 097 private int getNextSession() { 098 log.debug("max sessions {}",_maxSessions); 099 ArrayList<Integer> nxtSessionList = new ArrayList<>(); 100 for ( int i=0 ; (i < _csSessions.size()) ; i++) { 101 nxtSessionList.add(_csSessions.get(i).getSessionNum()); 102 } 103 for ( int i=1 ; (i < 257 ) ; i++) { 104 if (!nxtSessionList.contains(i)){ 105 return i; 106 } 107 } 108 return 1000; 109 } 110 111 private void setTrackPower(Boolean trueorfalse) { 112 _trackOn = trueorfalse; 113 CanReply r = new CanReply(1); // num elements 114 if (_trackOn) { 115 r.setElement(0, CbusConstants.CBUS_TON); 116 } else { 117 r.setElement(0, CbusConstants.CBUS_TOF); 118 } 119 send.sendWithDelay(r,getSendIn(),getSendOut(),getDelay()); 120 } 121 122 protected void setEstop(Boolean estop) { 123 _estop = estop; 124 if (_estop) { 125 CanReply r = new CanReply(1); 126 r.setElement(0, CbusConstants.CBUS_ESTOP); 127 send.sendWithDelay(r,getSendIn(),getSendOut(),getDelay()); 128 for ( int i=0 ; (i < _csSessions.size()) ; i++) { 129 _csSessions.get(i).setSpd(1); 130 } 131 } 132 } 133 134 private int getExistingSession( int rcvdIntAddr, Boolean rcvdIsLong ) { 135 for ( int i=0 ; (i < _csSessions.size()) ; i++) { 136 if ( 137 ( _csSessions.get(i).getrcvdIntAddr() == rcvdIntAddr ) && 138 ( _csSessions.get(i).getisLong().equals(rcvdIsLong) ) && 139 ( _csSessions.get(i).getIsDispatched() == false ) 140 ) { 141 return _csSessions.get(i).getSessionNum(); 142 } 143 } 144 return -1; 145 } 146 147 private void processrloc( int rcvdIntAddr, Boolean rcvdIsLong ) { 148 149 // check for existing session 150 int exSession = getExistingSession( rcvdIntAddr, rcvdIsLong ); 151 if ( exSession > -1 ) { 152 int locoaddr = rcvdIntAddr; 153 if (rcvdIsLong) { 154 locoaddr = locoaddr | 0xC000; 155 } 156 CanReply r = new CanReply(4); 157 r.setElement(0, CbusConstants.CBUS_ERR); 158 r.setElement(1, (locoaddr / 256)); // addr hi 159 r.setElement(2, locoaddr & 0xff); // addr low 160 r.setElement(3, 2); 161 send.sendWithDelay(r,getSendIn(),getSendOut(),getDelay()); 162 return; 163 } 164 165 int sessionid=getNextSession(); 166 167 CbusDummyCSSession session = new CbusDummyCSSession( this, sessionid,rcvdIntAddr, rcvdIsLong); 168 _csSessions.add(session); 169 _currentSessions++; 170 if ( _pane != null ){ 171 _pane.setNumSessions(_currentSessions); 172 } 173 session.sendPloc(); 174 } 175 176 private void processQloc( int session ) { 177 for ( int i=0 ; (i < _csSessions.size()) ; i++) { 178 if ( _csSessions.get(i).getSessionNum() == session && ( 179 !_csSessions.get(i).getIsDispatched() )) { 180 _csSessions.get(i).sendPloc(); 181 return; 182 } 183 } 184 CanReply r = new CanReply(4); 185 r.setElement(0, CbusConstants.CBUS_ERR); 186 r.setElement(1, session); 187 r.setElement(2, 0); 188 r.setElement(3, 3); 189 send.sendWithDelay(r,getSendIn(),getSendOut(),getDelay()); 190 } 191 192 private void processDspd ( int session, int speeddir) { 193 for ( int i=0 ; (i < _csSessions.size()) ; i++) { 194 if ( _csSessions.get(i).getSessionNum() == session && ( 195 !_csSessions.get(i).getIsDispatched() ) ) { 196 _csSessions.get(i).setSpd(speeddir); 197 if ((speeddir & 0x7f) != 1) { 198 setEstop(false); 199 } 200 return; 201 } 202 } 203 CanReply r = new CanReply(4); 204 r.setElement(0, CbusConstants.CBUS_ERR); 205 r.setElement(1, session); 206 r.setElement(2, 0); 207 r.setElement(3, 3); 208 send.sendWithDelay(r,getSendIn(),getSendOut(),getDelay()); 209 } 210 211 private void processDkeep ( int session ) { 212 for ( int i=0 ; (i < _csSessions.size()) ; i++) { 213 if ( ( _csSessions.get(i).getSessionNum() == session ) && ( 214 !_csSessions.get(i).getIsDispatched() )) { 215 _csSessions.get(i).keepAlive(); 216 return; 217 } 218 } 219 // send error if no session present 220 CanReply r = new CanReply(4); 221 r.setElement(0, CbusConstants.CBUS_ERR); 222 r.setElement(1, session); 223 r.setElement(2, 0); 224 r.setElement(3, 3); 225 send.sendWithDelay(r,getSendIn(),getSendOut(),getDelay()); 226 } 227 228 protected void destroySession (CbusDummyCSSession session) { 229 for ( int i=0 ; (i < _csSessions.size()) ; i++) { 230 if ( _csSessions.get(i) == session ) { 231 232 _csSessions.get(i).dispose(); 233 234 _csSessions.set(i, null); 235 _csSessions.remove(i); 236 _currentSessions--; 237 if ( _pane != null ){ 238 _pane.setNumSessions(_currentSessions); 239 } 240 return; 241 } 242 } 243 log.error("session not found to destroy"); 244 } 245 246 private void processKloc ( int session ) { 247 for ( int i=0 ; (i < _csSessions.size()) ; i++) { 248 if ( _csSessions.get(i).getSessionNum() == session && ( 249 !_csSessions.get(i).getIsDispatched() )) { 250 destroySession(_csSessions.get(i) ); 251 return; 252 } 253 } 254 // session not present error sent 255 CanReply r = new CanReply(4); 256 r.setElement(0, CbusConstants.CBUS_ERR); 257 r.setElement(1, session); 258 r.setElement(2, 0); 259 r.setElement(3, 3); 260 send.sendWithDelay(r,getSendIn(),getSendOut(),getDelay()); 261 } 262 263 /** 264 * {@inheritDoc} 265 */ 266 @Override 267 protected void startProcessFrame(AbstractMessage m) { 268 // log.warn("dummy node canframe {}",m); 269 if ( getDummyType() == 0 ) { 270 return; 271 } 272 int opc = m.getElement(0); 273 int session = m.getElement(1); 274 switch (opc) { 275 case CbusConstants.CBUS_RTON: 276 setTrackPower(true); 277 break; 278 case CbusConstants.CBUS_RTOF: 279 setTrackPower(false); 280 break; 281 case CbusConstants.CBUS_RESTP: 282 setEstop(true); 283 break; 284 case CbusConstants.CBUS_RLOC: 285 int rcvdIntAddr = (m.getElement(1) & 0x3f) * 256 + m.getElement(2); 286 boolean rcvdIsLong = (m.getElement(1) & 0xc0) != 0; 287 processrloc(rcvdIntAddr,rcvdIsLong); 288 break; 289 case CbusConstants.CBUS_QLOC: 290 processQloc( session ); 291 break; 292 case CbusConstants.CBUS_DSPD: 293 processDspd( session, m.getElement(2) ); 294 break; 295 case CbusConstants.CBUS_DKEEP: 296 processDkeep( session ); 297 break; 298 case CbusConstants.CBUS_KLOC: 299 processKloc( session ); 300 break; 301 default: 302 break; 303 } 304 } 305 306 /** 307 * {@inheritDoc} 308 */ 309 @Override 310 public void dispose(){ 311 super.dispose(); 312 resetCS(); 313 } 314 315 private static final Logger log = LoggerFactory.getLogger(CbusDummyCS.class); 316 317}