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