001package jmri.jmrix.tams; 002 003import javax.annotation.Nonnull; 004import jmri.Turnout; 005import org.slf4j.Logger; 006import org.slf4j.LoggerFactory; 007 008/** 009 * Implement turnout manager for Tams systems. Reworked to support binary 010 * commands and polling of command station. 011 * <p> 012 * Based on work by Bob Jacobsen and Kevin Dickerson. 013 * 014 * @author Jan Boen 015 */ 016public class TamsTurnoutManager extends jmri.managers.AbstractTurnoutManager implements TamsListener { 017 018 public TamsTurnoutManager(TamsSystemConnectionMemo memo) { 019 super(memo); 020 //Request status of turnout changes 021 TamsMessage m = TamsMessage.getXEvtTrn(); 022 memo.getTrafficController().sendTamsMessage(m, TamsTurnoutManager.this); 023 memo.getTrafficController().addPollMessage(m, TamsTurnoutManager.this); 024 } 025 026 /** 027 * {@inheritDoc} 028 */ 029 @Override 030 @Nonnull 031 public TamsSystemConnectionMemo getMemo() { 032 return (TamsSystemConnectionMemo) memo; 033 } 034 035 /** 036 * {@inheritDoc} 037 */ 038 @Nonnull 039 @Override 040 protected Turnout createNewTurnout(@Nonnull String systemName, String userName) throws IllegalArgumentException { 041 int addr; 042 try { 043 addr = Integer.parseInt(systemName.substring(getSystemPrefix().length() + 1)); 044 } catch (NumberFormatException e) { 045 log.error("failed to convert systemName {} to a turnout address", systemName); 046 throw new IllegalArgumentException("Failed to convert systemName '"+systemName+"' to a Turnout address"); 047 } 048 Turnout t = new TamsTurnout(addr, getSystemPrefix(), getMemo().getTrafficController()); 049 t.setUserName(userName); 050 return t; 051 } 052 053 /** 054 * Validates to contain at least 1 number . . . 055 * <p> 056 * TODO: check validateIntegerSystemNameFormat if min / max values are known. 057 * {@inheritDoc} 058 */ 059 @Override 060 @Nonnull 061 public String validateSystemNameFormat(@Nonnull String name, @Nonnull java.util.Locale locale) throws jmri.NamedBean.BadSystemNameException { 062 return validateTrimmedMin1NumberSystemNameFormat(name,locale); 063 } 064 065 @Override 066 public boolean allowMultipleAdditions(@Nonnull String systemName) { 067 return true; 068 } 069 070 boolean noWarnDelete = false; 071 072 @Override 073 public void message(TamsMessage m) { 074 //TamsMessages are ignored 075 } 076 077 @Override 078 public void reply(TamsReply r) {//To listen for Turnout status changes 079 //TamsMessage tm = TamsMessage.getXEvtTrn(); 080 if ( getMemo().getTrafficController().replyType == 'T') {//Only handle Turnout events 081 log.debug("*** Tams Turnout Reply ***"); 082 if ( getMemo().getTrafficController().replyBinary ) {//Typical polling message 083 log.debug("Reply to binary command = {}", r.toString()); 084 if ((r.getNumDataElements() > 1) && (r.getElement(0) > 0x00) && (r.getElement(0) != 'T')) { 085 //Here we break up a long turnout related TamsReply into individual turnout status' 086 for (int i = 1; i < r.getNumDataElements() - 1; i = i + 2) { 087 //create a new TamsReply and pass it to the decoder 088 TamsReply tr = new TamsReply(); 089 tr.setBinary(true); 090 tr.setElement(0, r.getElement(i)); 091 tr.setElement(1, r.getElement(i + 1)); 092 log.debug("Going to pass this to the decoder = {}", tr.toString()); 093 //The decodeTurnoutState will do the actual decoding of each individual turnout 094 decodeTurnoutState(tr, getSystemPrefix(), getMemo().getTrafficController()); 095 } 096 } 097 } else {//xSR is an ASCII message 098 //Nothing to do really 099 log.debug("Reply to ASCII command = {}", r.toString()); 100 } 101 } 102 } 103 104 void decodeTurnoutState(TamsReply r, String prefix, TamsTrafficController tc) { 105 //reply to XEvtSen consists of 2 bytes per turnout 106 //1: LSB of turnout address (A0 .. A7) 107 //2: MSB of turnout address (A8 .. A10) incl. direction 108 //bit# 7 6 5 4 3 2 1 0 109 //+-----+-----+-----+-----+-----+-----+-----+-----+ 110 //|Color| Sts | 0 | 0 | 0 | A10 | A9 | A8 | 111 //+-----+-----+-----+-----+-----+-----+-----+-----+ 112 //Color 1 = straight (green), 0 = turnout (red) 113 //Sts 1 = on, 0 = off 114 //A10..A8 MSBs of turnout address 115 int lowerByte = r.getElement(0) & 0xff; 116 int upperByte = r.getElement(1) & 0xff; 117 log.debug("Decoding turnout"); 118 //log.debug("Lower Byte: {}", lowerByte); 119 //log.debug("Upper Byte: {}", upperByte); 120 //Core logic to be added here 121 int turnoutAddress = (upperByte & 0x07) * 256 + lowerByte; 122 String turnoutName = prefix + "T" + Integer.toString(turnoutAddress); 123 int turnoutState = Turnout.THROWN; 124 if ((upperByte & 0x80) == 0x80) {//Only need bit #7 125 turnoutState = Turnout.CLOSED; 126 } 127 log.debug("Turnout Address: -{}-, state: {}", turnoutName, turnoutState); 128 129 //OK. Now how do we get the turnout to update in JMRI? 130 //Next line provided via JMRI dev's 131 TamsTurnout ttu = (TamsTurnout)provideTurnout(turnoutName); 132 ttu.setCommandedStateFromCS(turnoutState); 133 ttu.setKnownStateFromCS(turnoutState); 134 } 135 136 @Override 137 public void dispose() { 138 getMemo().getTrafficController().removePollMessage(TamsMessage.getXEvtTrn(), this); 139 super.dispose(); 140 } 141 142 private final static Logger log = LoggerFactory.getLogger(TamsTurnoutManager.class); 143 144}