001package jmri.jmrix.loconet; 002 003import java.util.Map; 004import javax.annotation.Nonnull; 005import jmri.NmraPacket; 006import jmri.implementation.DccSignalMast; 007import org.slf4j.Logger; 008import org.slf4j.LoggerFactory; 009 010/** 011 * Extend jmri.SignalMast for signals implemented by an LNCP. 012 * <p> 013 * This implementation writes out to the physical signal when it's commanded to 014 * change appearance, and updates its internal state when it hears commands from 015 * other places. 016 * <p> 017 * {@link #setAspect} does not immediately change the local aspect. Instead, it produces 018 * the message on the network, waiting for that to return and do the local state change, 019 * notification, etc. 020 * <p> 021 * This is a specific implementation for the RR-cirkits LNCP interface board. 022 * A more general implementation, which can work with any system(s), is available 023 * in {@link jmri.implementation.DccSignalMast}. 024 * 025 * @author Kevin Dickerson Copyright (C) 2002 026 */ 027public class LNCPSignalMast extends DccSignalMast implements LocoNetListener { 028 029 public LNCPSignalMast(String sys, String user) { 030 super(sys, user, "F$lncpsm"); // NOI18N 031 packetSendCount = 1; 032 configureFromName(sys); 033 init(); 034 } 035 036 public LNCPSignalMast(String sys) { 037 super(sys, null, "F$lncpsm"); // NOI18N 038 packetSendCount = 1; 039 configureFromName(sys); 040 init(); 041 } 042 043 void init() { 044 if ((c instanceof SlotManager) && (((SlotManager) c).getSystemConnectionMemo() != null)) { 045 tc = ((SlotManager) c).getSystemConnectionMemo().getLnTrafficController(); 046 } else { 047 tc = jmri.InstanceManager.getDefault(LnTrafficController.class); 048 } 049 050 //We cheat, and store the two bytes that make up an NMRA packet for later use in decoding a message from the LocoNet 051 int lowAddr = ((dccSignalDecoderAddress - 1) & 0x03); // Output Pair Address 052 int boardAddr = ((dccSignalDecoderAddress - 1) >> 2); // Board Address 053 int midAddr = boardAddr & 0x3F; 054 055 int highAddr = ((~boardAddr) >> 6) & 0x07; 056 057 dccByteAddr1 = ((byte) (0x80 | midAddr)); 058 dccByteAddr2 = ((byte) (0x01 | (highAddr << 4) | (lowAddr << 1))); 059 tc.addLocoNetListener(~0, this); 060 } 061 062 LnTrafficController tc; 063 064 // implementing classes will typically have a function/listener to get 065 // updates from the layout, which will then call 066 // public void firePropertyChange(String propertyName, 067 // Object oldValue, 068 // Object newValue) 069 // _once_ if anything has changed state (or set the commanded state directly) 070 @Override 071 public void message(LocoNetMessage l) { 072 if (l.getOpCode() != LnConstants.OPC_IMM_PACKET) { 073 return; 074 } 075 076 int val7f = l.getElement(2); /* fixed value of 0x7f */ 077 078 if (val7f != 0x7f) { 079 return; 080 } 081 082 int reps = l.getElement(3); 083 int len = ((reps & 0x70) >> 4); 084 if (len != 3) { 085 return; 086 } 087 int dhi = l.getElement(4); 088 int im1 = l.getElement(5); 089 int im2 = l.getElement(6); 090 int im3 = l.getElement(7); 091 092 byte[] packet = new byte[len]; 093 packet[0] = (byte) (im1 + ((dhi & 0x01) != 0 ? 0x80 : 0)); 094 packet[1] = (byte) (im2 + ((dhi & 0x02) != 0 ? 0x80 : 0)); 095 096 if (myAddress(packet[0], packet[1])) { 097 packet[2] = (byte) (im3 + ((dhi & 0x04) != 0 ? 0x80 : 0)); 098 int aspect = packet[2]; 099 for (Map.Entry<String, Integer> entry : appearanceToOutput.entrySet()) { 100 if (entry.getValue() == aspect) { 101 setKnownState(entry.getKey()); 102 return; 103 } 104 } 105 log.error("Aspect for id {} on signal mast {} not found", aspect, this.getDisplayName()); 106 } 107 } 108 109 @Override 110 public void setAspect(@Nonnull String aspect) { 111 if (appearanceToOutput.containsKey(aspect) && appearanceToOutput.get(aspect) != -1) { 112 c.sendPacket(NmraPacket.altAccSignalDecoderPkt(dccSignalDecoderAddress, appearanceToOutput.get(aspect)), packetSendCount); 113 } else { 114 log.warn("Trying to set aspect ({}) that has not been configured on mast {}", aspect, getDisplayName()); 115 } 116 // super.setAspect(aspect); // see note in class description 117 } 118 119 public void setKnownState(String aspect) { 120 String oldAspect = this.aspect; 121 this.aspect = aspect; 122 this.speed = (String) getSignalSystem().getProperty(aspect, "speed"); // NOI18N 123 firePropertyChange("Aspect", oldAspect, aspect); // NOI18N 124 } 125 126 @Override 127 public void dispose() { 128 tc.removeLocoNetListener(~0, this); 129 super.dispose(); 130 } 131 132 byte dccByteAddr1; 133 byte dccByteAddr2; 134 135 private boolean myAddress(byte a1, byte a2) { 136 if (a1 != dccByteAddr1) { 137 return false; 138 } 139 return (a2 == dccByteAddr2); 140 } 141 142 private final static Logger log = LoggerFactory.getLogger(LNCPSignalMast.class); 143 144}