001package jmri.implementation; 002 003import java.util.HashMap; 004import javax.annotation.Nonnull; 005import jmri.CommandStation; 006import jmri.InstanceManager; 007import jmri.NmraPacket; 008import jmri.SignalMast; 009import org.slf4j.Logger; 010import org.slf4j.LoggerFactory; 011 012/** 013 * This class implements a SignalMast that uses <b>Extended Accessory Decoder 014 * Control Packet Format</b> 015 * and outputs that packet to the DCC System via the generic CommandStation 016 * interface. 017 * <p> 018 * This implementation writes out to the physical signal when it's commanded to 019 * change appearance, and updates its internal state when it hears commands from 020 * other places. 021 * <p> 022 * System name specifies the creation information: 023 * <pre> 024 * IF$dsm:basic:one-searchlight(123) 025 * </pre> The name is a colon-separated series of terms: 026 * <ul> 027 * <li>IF$dsm - defines signal masts of this type 028 * <li>basic - name of the signaling system 029 * <li>one-searchlight - name of the particular aspect map 030 * <li>(123) - DCC address for the decoder 031 * </ul> 032 * <p> 033 * Based upon {@link jmri.implementation.DccSignalHead} by Alex Shepherd 034 * 035 * @author Kevin Dickerson Copyright (c) 2012 036 */ 037public class DccSignalMast extends AbstractSignalMast { 038 039 public DccSignalMast(String sys, String user) { 040 super(sys, user); 041 configureFromName(sys); 042 } 043 044 public DccSignalMast(String sys) { 045 super(sys); 046 configureFromName(sys); 047 } 048 049 public DccSignalMast(String sys, String user, String mastSubType) { 050 super(sys, user); 051 theMastType = mastSubType; 052 configureFromName(sys); 053 } 054 055 private String theMastType = "F$dsm"; 056 private boolean useAddressOffSet = false; 057 058 protected void configureFromName(String systemName) { 059 // split out the basic information 060 String[] parts = systemName.split(":"); 061 if (parts.length < 3) { 062 log.error("SignalMast system name needs at least three parts: {}", systemName); 063 throw new IllegalArgumentException("System name needs at least three parts: " + systemName); 064 } 065 if (!parts[0].endsWith(theMastType)) { 066 log.warn("First part of SignalMast system name is incorrect {} : {}", systemName, theMastType); 067 } else { 068 String commandStationPrefix = parts[0].substring(0, parts[0].indexOf("$") - 1); 069 java.util.List<jmri.CommandStation> connList = jmri.InstanceManager.getList(jmri.CommandStation.class); 070 071 for (jmri.CommandStation station : connList) { 072 log.trace(" check against {} with letter {}", station, station.getSystemPrefix()); 073 if (station.getSystemPrefix().equals(commandStationPrefix)) { 074 c = station; 075 break; 076 } 077 } 078 079 if (c == null) { 080 c = InstanceManager.getNullableDefault(CommandStation.class); 081 log.error("No match against the command station for \"{}\", so will use the default {}", commandStationPrefix, c); 082 } 083 } 084 String system = parts[1]; 085 String mast = parts[2]; 086 087 mast = mast.substring(0, mast.indexOf("(")); 088 log.trace("In configureFromName setMastType to {}", mast); 089 setMastType(mast); 090 091 String tmp = parts[2].substring(parts[2].indexOf("(") + 1, parts[2].indexOf(")")); 092 try { 093 dccSignalDecoderAddress = Integer.parseInt(tmp); 094 } catch (NumberFormatException e) { 095 log.warn("DCC accessory address SystemName {} is not in the correct format", systemName); 096 } 097 configureSignalSystemDefinition(system); 098 configureAspectTable(system, mast); 099 } 100 101 protected HashMap<String, Integer> appearanceToOutput = new HashMap<>(); 102 103 public void setOutputForAppearance(String appearance, int number) { 104 if (appearanceToOutput.containsKey(appearance)) { 105 log.debug("Appearance {} is already defined as {}", appearance, appearanceToOutput.get(appearance)); 106 appearanceToOutput.remove(appearance); 107 } 108 appearanceToOutput.put(appearance, number); 109 } 110 111 public int getOutputForAppearance(String appearance) { 112 if (!appearanceToOutput.containsKey(appearance)) { 113 log.error("Trying to get appearance {} but it has not been configured", appearance); 114 return -1; 115 } 116 return appearanceToOutput.get(appearance); 117 } 118 119 /* 120 0. "Stop" 121 1. "Take Siding" 122 2. "Stop-Orders" 123 3. "Stop-Proceed" 124 4. "Restricting" 125 5. "Permissive" 126 6. "Slow-Approach" 127 7. "Slow" 128 8. "Slow-Medium" 129 9. "Slow-Limited" 130 10. "Slow-Clear" 131 11. "Medium-Approach" 132 12. "Medium-Slow" 133 13. "Medium" 134 14. "Medium-Ltd" 135 15. "Medium-Clr" 136 16. "Limited-Approach" 137 17. "Limited-Slow" 138 18. "Limited-Med" 139 19. "Limited" 140 20. "Limited-Clear" 141 21. "Approach" 142 22. "Advance-Appr" 143 23. "Appr-Slow" 144 24. "Adv-Appr-Slow" 145 25. "Appr-Medium" 146 26. "Adv-Appr-Med" 147 27. "Appr-Limited" 148 28. "Adv-Appr-Ltd" 149 29. "Clear" 150 30. "Cab-Speed" 151 31. "Dark" */ 152 protected int packetSendCount = 3; // default 3 153 154 @Override 155 public void setAspect(@Nonnull String aspect) { 156 if (appearanceToOutput.containsKey(aspect) && appearanceToOutput.get(aspect) != -1) { 157 if (getLit()) { 158 if (useAddressOffSet) { 159 c.sendPacket(NmraPacket.accSignalDecoderPkt(dccSignalDecoderAddress, appearanceToOutput.get(aspect)), packetSendCount); 160 } else { 161 c.sendPacket(NmraPacket.altAccSignalDecoderPkt(dccSignalDecoderAddress, appearanceToOutput.get(aspect)), packetSendCount); 162 } 163 } 164 } else { 165 log.warn("Trying to set aspect ({}) that has not been configured on mast {}", aspect, getDisplayName()); 166 } 167 super.setAspect(aspect); 168 } 169 170 171 public void useAddressOffSet(boolean boo) { 172 useAddressOffSet = boo; 173 } 174 175 public boolean useAddressOffSet() { 176 return useAddressOffSet; 177 } 178 179 @Override 180 public void setLit(boolean newLit) { 181 if (!allowUnLit() || newLit == getLit()) { 182 return; 183 } 184 super.setLit(newLit); 185 if (newLit) { 186 String newAspect = getAspect(); 187 if (newAspect != null){ 188 setAspect(newAspect); 189 } 190 } else { 191 if (useAddressOffSet) { 192 c.sendPacket(NmraPacket.accSignalDecoderPkt(dccSignalDecoderAddress, unLitId), packetSendCount); 193 } else { 194 c.sendPacket(NmraPacket.altAccSignalDecoderPkt(dccSignalDecoderAddress, unLitId), packetSendCount); 195 } 196 } 197 } 198 199 int unLitId = 31; 200 201 public void setUnlitId(int i) { 202 unLitId = i; 203 } 204 205 public int getUnlitId() { 206 return unLitId; 207 } 208 209 public int getDccSignalMastAddress() { 210 return dccSignalDecoderAddress; 211 } 212 213 public CommandStation getCommandStation() { 214 return c; 215 } 216 217 protected CommandStation c; 218 219 protected int dccSignalDecoderAddress; 220 221 public static String isDCCAddressUsed(int addr) { 222 for (SignalMast mast : InstanceManager.getDefault(jmri.SignalMastManager.class).getNamedBeanSet()) { 223 if (mast instanceof jmri.implementation.DccSignalMast) { 224 if (((DccSignalMast) mast).getDccSignalMastAddress() == addr) { 225 return ((DccSignalMast) mast).getDisplayName(); 226 } 227 } 228 } 229 return null; 230 } 231 232 /** 233 * Set Number of times the packet should be sent. 234 * @param count - less than 1 is treated as 1. 235 */ 236 public void setDccSignalMastPacketSendCount(int count) { 237 if (count >= 0) { 238 packetSendCount = count; 239 } else { 240 packetSendCount = 1; 241 } 242 } 243 244 /** 245 * Get the number of times the packet should be sent to the track. 246 * 247 * @return the count. 248 */ 249 public int getDccSignalMastPacketSendCount() { 250 return packetSendCount; 251 } 252 253 private final static Logger log = LoggerFactory.getLogger(DccSignalMast.class); 254 255}