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 EasyDccMessage msg = new EasyDccMessage(4 + 3 * contents.length); 229 msg.setOpCode('S'); 230 msg.setElement(1, ' '); 231 msg.setElement(2, '0'); 232 msg.setElement(3, '5'); 233 int j = 4; 234 for (int i = 0; i < contents.length; i++) { 235 j++; 236 msg.setElement(j, ' '); 237 msg.addIntAsTwoHex(contents[i] & 0xFF, j); 238 j += 2; 239 } 240 241 // send it 242 _memo.getTrafficController().sendEasyDccMessage(msg, null); 243 } 244 245 /** 246 * Remove a Locomotive from an Advanced Consist 247 * 248 * @param locoAddress is the Locomotive address to add to the locomotive 249 */ 250 @Override 251 protected synchronized void removeFromAdvancedConsist(DccLocoAddress locoAddress) { 252 log.debug(" Remove Locomotive {} from advanced consist {}", locoAddress, consistAddress); 253 // create the message and fill it, 254 byte[] contents = jmri.NmraPacket.consistControl(locoAddress.getNumber(), 255 locoAddress.isLongAddress(), 256 0, true); 257 EasyDccMessage msg = new EasyDccMessage(4 + 3 * contents.length); 258 msg.setOpCode('S'); 259 msg.setElement(1, ' '); 260 msg.setElement(2, '0'); 261 msg.setElement(3, '5'); 262 int j = 4; 263 for (int i = 0; i < contents.length; i++) { 264 j++; 265 msg.setElement(j, ' '); 266 msg.addIntAsTwoHex(contents[i] & 0xFF, j); 267 j += 2; 268 } 269 270 // send it 271 _memo.getTrafficController().sendEasyDccMessage(msg, null); 272 } 273 274 /** 275 * Add a Locomotive to an EasyDCC Standard Consist. 276 * 277 * @param locoAddress is the Locomotive address to add to the locomotive 278 * @param directionNormal is True if the locomotive is traveling 279 * the same direction as the consist, or false otherwise. 280 */ 281 private synchronized void addToCSConsist(DccLocoAddress locoAddress, boolean directionNormal) { 282 log.debug("Add Locomotive {} to Standard Consist {} With Direction Normal {}.", 283 locoAddress, consistAddress, directionNormal); 284 EasyDccMessage m; 285 if (directionNormal) { 286 m = EasyDccMessage.getAddConsistNormal(consistAddress.getNumber(), locoAddress); 287 } else { 288 m = EasyDccMessage.getAddConsistReverse(consistAddress.getNumber(), locoAddress); 289 } 290 _memo.getTrafficController().sendEasyDccMessage(m, null); 291 } 292 293 /** 294 * Remove a Locomotive from an EasyDCC Standard Consist. 295 * 296 * @param LocoAddress is the Locomotive address to add to the locomotive 297 */ 298 public synchronized void removeFromCSConsist(DccLocoAddress LocoAddress) { 299 log.debug("Remove Locomotive {} from Standard Consist {}.", LocoAddress, consistAddress); 300 EasyDccMessage m = EasyDccMessage.getSubtractConsist(consistAddress.getNumber(), LocoAddress); 301 _memo.getTrafficController().sendEasyDccMessage(m, null); 302 } 303 304 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(EasyDccConsist.class); 305 306}