001package jmri.jmrix.tmcc; 002 003import jmri.Turnout; 004import jmri.implementation.AbstractTurnout; 005import org.slf4j.Logger; 006import org.slf4j.LoggerFactory; 007 008/** 009 * Extend jmri.AbstractTurnout for TMCC serial layouts. 010 * <p> 011 * This object doesn't listen to the TMCC communications. This is because it 012 * should be the only object that is sending messages for this turnout; more 013 * than one Turnout object pointing to a single device is not allowed. 014 * 015 * @author Bob Jacobsen Copyright (C) 2003, 2006 016 * with edits/additions by 017 * @author Timothy Jump Copyright (C) 2025 018 */ 019 020public class SerialTurnout extends AbstractTurnout { 021 022 // data members 023 int _number; // turnout number 024 private SerialTrafficController tc = null; 025 protected String _prefix = "T"; // default to "T" 026 027 /** 028 * Create a turnout. TMCC turnouts use the number 1-99 as their 029 * numerical identification. The TMCC SC-2 reserves 0 as a special reset 030 * address, but the TMCC SC-1 allows 0 to be a turnout; however, the SC-1 031 * documentation examples and callouts all use 1 as the first turnout 032 * address. 033 * 034 * @param prefix the connection prefix 035 * @param number the TMCC turnout number from 1 to 99 036 * @param memo the connection memo 037 */ 038 public SerialTurnout(String prefix, int number, TmccSystemConnectionMemo memo) { 039 super(prefix + "T" + number); 040 tc = memo.getTrafficController(); 041 _number = number; 042 _prefix = prefix; 043 // At construction, don't register for messages (see package doc) 044 } 045 046 /** 047 * {@inheritDoc} 048 */ 049 @Override 050 protected void forwardCommandChangeToLayout(int newState) { 051 052 // sort out states for TMCC (SW/Turnout) 053 if (_number < 100) { 054 if ((newState & Turnout.CLOSED) != 0) { 055 // first look for the double case, which we can't handle 056 if ((newState & Turnout.THROWN) != 0) { 057 // this is the disaster case! 058 log.error("Cannot command both CLOSED and THROWN {}", newState); 059 return; 060 } else { 061 // send a CLOSED command 062 sendMessage(true ^ getInverted()); 063 } 064 } else { 065 // send a THROWN command 066 sendMessage(false ^ getInverted()); 067 } 068 } 069 070 // sort out states for TMCC (ACC/Aux1) 071 if (_number >= 100 && _number < 110) { 072 if ((newState & Turnout.CLOSED) != 0) { 073 // first look for the double case, which we can't handle 074 if ((newState & Turnout.THROWN) != 0) { 075 // this is the disaster case! 076 log.error("Cannot command both CLOSED and THROWN {}", newState); 077 return; 078 } else { 079 // send a CLOSED command 080 sendMessage(true ^ getInverted()); 081 } 082 } else { 083 // send a THROWN command 084 sendMessage(false ^ getInverted()); 085 } 086 } 087 088 // sort out states for TMCC (ACC/Aux2) 089 if (_number >= 110 && _number < 120) { 090 if ((newState & Turnout.CLOSED) != 0) { 091 // first look for the double case, which we can't handle 092 if ((newState & Turnout.THROWN) != 0) { 093 // this is the disaster case! 094 log.error("Cannot command both CLOSED and THROWN {}", newState); 095 return; 096 } else { 097 // send a CLOSED command 098 sendMessage(true ^ getInverted()); 099 } 100 } else { 101 // send a THROWN command 102 sendMessage(false ^ getInverted()); 103 } 104 } 105 } 106 107 @Override 108 protected void turnoutPushbuttonLockout(boolean _pushButtonLockout) { 109 if (log.isDebugEnabled()) { 110 log.debug("Send command to {} Pushbutton {}T{}", (_pushButtonLockout ? "Lock" : "Unlock"), _prefix, _number); 111 } 112 } 113 114 protected void sendMessage(boolean closed) { 115 SerialMessage m = new SerialMessage(); 116 m.setOpCode(0xFE); 117 118 if (_number < 100) { 119 if (closed) { 120 m.putAsWord(0x4000 + _number * 128); 121 } else { 122 m.putAsWord(0x401F + _number * 128); 123 } 124 } 125 126 if (_number >= 100 && _number < 110) { 127 if (closed) { 128 m.putAsWord(0x8008 + ((_number - 100) * 128)); 129 } else { 130 m.putAsWord(0x800B + ((_number - 100) * 128)); 131 } 132 } 133 134 if (_number >= 110 && _number < 120) { 135 if (closed) { 136 m.putAsWord(0x800C + ((_number - 110) * 128)); 137 } else { 138 m.putAsWord(0x800F + ((_number - 110) * 128)); 139 } 140 } 141 142 tc.sendSerialMessage(m, null); 143 tc.sendSerialMessage(m, null); 144 tc.sendSerialMessage(m, null); 145 tc.sendSerialMessage(m, null); 146 } 147 148 private final static Logger log = LoggerFactory.getLogger(SerialTurnout.class); 149 150}