001package jmri.jmrix.easydcc; 002 003import jmri.Consist; 004import jmri.ConsistListener; 005import jmri.DccLocoAddress; 006 007/** 008 * This is the Consist definition for a consist on an EasyDCC system. it uses 009 * the EasyDcc specific commands to build a consist. 010 * 011 * @author Paul Bender Copyright (C) 2006 012 */ 013public class EasyDccConsist extends jmri.implementation.DccConsist { 014 015 private final EasyDccSystemConnectionMemo _memo; 016 017 // Initialize a consist for the specific address. 018 // The Default consist type is an advanced consist 019 public EasyDccConsist(int address, EasyDccSystemConnectionMemo memo) { 020 super(address); 021 _memo = memo; 022 } 023 024 // Initialize a consist for the specific address. 025 // The Default consist type is an advanced consist 026 public EasyDccConsist(DccLocoAddress address, EasyDccSystemConnectionMemo memo) { 027 super(address); 028 _memo = memo; 029 } 030 031 // Set the Consist Type. 032 @Override 033 public void setConsistType(int consist_type) { 034 switch (consist_type) { 035 case Consist.ADVANCED_CONSIST: 036 case Consist.CS_CONSIST: 037 consistType = consist_type; 038 break; 039 default: 040 log.error("Consist Type Not Supported"); 041 notifyConsistListeners(new DccLocoAddress(0, false), ConsistListener.NotImplemented); 042 break; 043 } 044 } 045 046 /** 047 * Is this address allowed? 048 * On EasyDCC systems, all addresses but 0 can be used in a consist 049 * (either an Advanced Consist or a Standard Consist). 050 * {@inheritDoc} 051 */ 052 @Override 053 public boolean isAddressAllowed(DccLocoAddress address) { 054 return address.getNumber() != 0; 055 } 056 057 /** 058 * Is there a size limit for this consist? 059 * 060 * @return 8 for EasyDcc Standard Consist, 061 * -1 for Decoder Assisted Consists (no limit), 062 * 0 for any other consist type 063 * {@inheritDoc} 064 */ 065 @Override 066 public int sizeLimit() { 067 switch (consistType) { 068 case ADVANCED_CONSIST: 069 return -1; 070 case CS_CONSIST: 071 return 8; 072 default: 073 return 0; 074 } 075 } 076 077 /** 078 * Does the consist contain the specified address? 079 * {@inheritDoc} 080 */ 081 @Override 082 public boolean contains(DccLocoAddress address) { 083 if (consistType == ADVANCED_CONSIST || consistType == CS_CONSIST) { 084 return consistList.contains(address); 085 } else { 086 log.error("Consist Type Not Supported"); 087 notifyConsistListeners(address, ConsistListener.NotImplemented); 088 } 089 return false; 090 } 091 092 /** 093 * Get the relative direction setting for a specific 094 * locomotive in the consist. 095 * {@inheritDoc} 096 */ 097 @Override 098 public boolean getLocoDirection(DccLocoAddress address) { 099 if (consistType == ADVANCED_CONSIST || consistType == CS_CONSIST) { 100 return consistDir.getOrDefault(address, false); 101 } else { 102 log.error("Consist Type Not Supported"); 103 notifyConsistListeners(address, ConsistListener.NotImplemented); 104 } 105 return false; 106 } 107 108 /** 109 * Add an Address to the internal consist list object. 110 */ 111 private synchronized void addToConsistList(DccLocoAddress locoAddress, boolean directionNormal) { 112 if (!(consistList.contains(locoAddress))) { 113 consistList.add(locoAddress); 114 } 115 consistDir.put(locoAddress, directionNormal); 116 if (consistType == CS_CONSIST && consistList.size() == 8) { 117 notifyConsistListeners(locoAddress, 118 ConsistListener.OPERATION_SUCCESS 119 | ConsistListener.CONSIST_FULL); 120 } else { 121 notifyConsistListeners(locoAddress, 122 ConsistListener.OPERATION_SUCCESS); 123 } 124 } 125 126 /** 127 * Remove an address from the internal consist list object. 128 */ 129 private synchronized void removeFromConsistList(DccLocoAddress locoAddress) { 130 consistDir.remove(locoAddress); 131 consistList.remove(locoAddress); 132 notifyConsistListeners(locoAddress, ConsistListener.OPERATION_SUCCESS); 133 } 134 135 /** 136 * Add a Locomotive to a Consist. 137 * 138 * @param locoAddress is the Locomotive address to add to the locomotive 139 * @param directionNormal is True if the locomotive is traveling 140 * the same direction as the consist, or false otherwise. 141 */ 142 @Override 143 public synchronized void add(DccLocoAddress locoAddress, boolean directionNormal) { 144 if (consistType == ADVANCED_CONSIST) { 145 addToConsistList(locoAddress, directionNormal); 146 addToAdvancedConsist(locoAddress, directionNormal); 147 //set the value in the roster entry for CV19 148 setRosterEntryCVValue(locoAddress); 149 } else if (consistType == CS_CONSIST) { 150 if (consistList.size() < 8) { 151 addToConsistList(locoAddress, directionNormal); 152 addToCSConsist(locoAddress, directionNormal); 153 } else { 154 notifyConsistListeners(locoAddress, 155 ConsistListener.CONSIST_ERROR 156 | ConsistListener.CONSIST_FULL); 157 } 158 } else { 159 log.error("Consist Type Not Supported"); 160 notifyConsistListeners(locoAddress, ConsistListener.NotImplemented); 161 } 162 } 163 164 /** 165 * Restore a Locomotive to an Advanced Consist, but don't write to 166 * the command station. This is used for restoring the consist 167 * from a file or adding a consist read from the command station. 168 * 169 * @param locoAddress is the Locomotive address to add to the locomotive 170 * @param directionNormal is True if the locomotive is traveling 171 * the same direction as the consist, or false otherwise. 172 */ 173 @Override 174 public synchronized void restore(DccLocoAddress locoAddress, boolean directionNormal) { 175 switch (consistType) { 176 case ADVANCED_CONSIST: 177 case CS_CONSIST: 178 addToConsistList(locoAddress, directionNormal); 179 break; 180 default: 181 log.error("Consist Type Not Supported"); 182 notifyConsistListeners(locoAddress, ConsistListener.NotImplemented); 183 break; 184 } 185 } 186 187 /** 188 * Remove a Locomotive from this Consist. 189 * 190 * @param locoAddress is the Locomotive address to add to the locomotive 191 */ 192 @Override 193 public synchronized void remove(DccLocoAddress locoAddress) { 194 switch (consistType) { 195 case ADVANCED_CONSIST: 196 //reset the value in the roster entry for CV19 197 resetRosterEntryCVValue(locoAddress); 198 removeFromAdvancedConsist(locoAddress); 199 removeFromConsistList(locoAddress); 200 break; 201 case CS_CONSIST: 202 removeFromCSConsist(locoAddress); 203 removeFromConsistList(locoAddress); 204 break; 205 default: 206 log.error("Consist Type Not Supported"); 207 notifyConsistListeners(locoAddress, ConsistListener.NotImplemented); 208 break; 209 } 210 } 211 212 /** 213 * Add a Locomotive to an Advanced Consist. 214 * 215 * @param locoAddress is the Locomotive address to add to the locomotive 216 * @param directionNormal is True if the locomotive is traveling 217 * the same direction as the consist, or false otherwise. 218 */ 219 @Override 220 protected synchronized void addToAdvancedConsist(DccLocoAddress locoAddress, boolean directionNormal) { 221 log.debug("Add Locomotive {} to advanced consist {} With Direction Normal {}.", 222 locoAddress, consistAddress, directionNormal); 223 // create the message and fill it, 224 byte[] contents = jmri.NmraPacket.consistControl(locoAddress.getNumber(), 225 locoAddress.isLongAddress(), 226 consistAddress.getNumber(), 227 directionNormal); 228 // consistControl() returns null instead of throwing whenever either 229 // address fails its own validity check. 230 if (contents == null) { 231 log.error("Unable to build advanced consist control packet for {} / consist {} -- invalid address", 232 locoAddress, consistAddress); 233 notifyConsistListeners(locoAddress, ConsistListener.CONSIST_ERROR); 234 return; 235 } 236 EasyDccMessage msg = new EasyDccMessage(4 + 3 * contents.length); 237 msg.setOpCode('S'); 238 msg.setElement(1, ' '); 239 msg.setElement(2, '0'); 240 msg.setElement(3, '5'); 241 int j = 4; 242 for (int i = 0; i < contents.length; i++) { 243 // the buffer is sized 3 chars per content byte (" XX" -- space 244 // then two hex digits). 245 msg.setElement(j, ' '); 246 msg.addIntAsTwoHex(contents[i] & 0xFF, j + 1); 247 j += 3; 248 } 249 250 // send it 251 _memo.getTrafficController().sendEasyDccMessage(msg, null); 252 } 253 254 /** 255 * Remove a Locomotive from an Advanced Consist 256 * 257 * @param locoAddress is the Locomotive address to add to the locomotive 258 */ 259 @Override 260 protected synchronized void removeFromAdvancedConsist(DccLocoAddress locoAddress) { 261 log.debug(" Remove Locomotive {} from advanced consist {}", locoAddress, consistAddress); 262 // create the message and fill it, 263 byte[] contents = jmri.NmraPacket.consistControl(locoAddress.getNumber(), 264 locoAddress.isLongAddress(), 265 0, true); 266 // consistControl() returns null instead of throwing whenever 267 // locoAddress fails its own validity check (see addToAdvancedConsist()). 268 if (contents == null) { 269 log.error("Unable to build advanced consist control packet to remove {} -- invalid address", locoAddress); 270 notifyConsistListeners(locoAddress, ConsistListener.CONSIST_ERROR); 271 return; 272 } 273 EasyDccMessage msg = new EasyDccMessage(4 + 3 * contents.length); 274 msg.setOpCode('S'); 275 msg.setElement(1, ' '); 276 msg.setElement(2, '0'); 277 msg.setElement(3, '5'); 278 int j = 4; 279 for (int i = 0; i < contents.length; i++) { 280 // the buffer is sized 3 chars per content byte (" XX" -- space 281 // then two hex digits). 282 msg.setElement(j, ' '); 283 msg.addIntAsTwoHex(contents[i] & 0xFF, j + 1); 284 j += 3; 285 } 286 287 // send it 288 _memo.getTrafficController().sendEasyDccMessage(msg, null); 289 } 290 291 /** 292 * Add a Locomotive to an EasyDCC Standard Consist. 293 * 294 * @param locoAddress is the Locomotive address to add to the locomotive 295 * @param directionNormal is True if the locomotive is traveling 296 * the same direction as the consist, or false otherwise. 297 */ 298 private synchronized void addToCSConsist(DccLocoAddress locoAddress, boolean directionNormal) { 299 log.debug("Add Locomotive {} to Standard Consist {} With Direction Normal {}.", 300 locoAddress, consistAddress, directionNormal); 301 EasyDccMessage m; 302 if (directionNormal) { 303 m = EasyDccMessage.getAddConsistNormal(consistAddress.getNumber(), locoAddress); 304 } else { 305 m = EasyDccMessage.getAddConsistReverse(consistAddress.getNumber(), locoAddress); 306 } 307 _memo.getTrafficController().sendEasyDccMessage(m, null); 308 } 309 310 /** 311 * Remove a Locomotive from an EasyDCC Standard Consist. 312 * 313 * @param LocoAddress is the Locomotive address to add to the locomotive 314 */ 315 public synchronized void removeFromCSConsist(DccLocoAddress LocoAddress) { 316 log.debug("Remove Locomotive {} from Standard Consist {}.", LocoAddress, consistAddress); 317 EasyDccMessage m = EasyDccMessage.getSubtractConsist(consistAddress.getNumber(), LocoAddress); 318 _memo.getTrafficController().sendEasyDccMessage(m, null); 319 } 320 321 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(EasyDccConsist.class); 322 323}