001package jmri.jmrit.operations.rollingstock.engines.tools; 002 003import java.util.ArrayList; 004import java.util.List; 005 006import javax.swing.JPanel; 007 008import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 009import jmri.InstanceManager; 010import jmri.jmrit.operations.rollingstock.engines.*; 011import jmri.jmrix.nce.*; 012import jmri.util.swing.JmriJOptionPane; 013 014/** 015 * Routine to synchronize operation's engines with NCE consist memory. 016 * 017 * NCE Consists are stored in Command Station (CS) memory starting at address 018 * xF500 and ending xFAFF. NCE supports up to 127 consists, numbered 1 to 127. 019 * They track the lead loco, rear loco, and four mid locos in the consist file. 020 * Consist lead locos are stored in memory locations xF500 through xF5FF. 021 * Consist rear locos are stored in memory locations xF600 through xF6FF. Mid 022 * consist locos (four max) are stored in memory locations xF700 through xFAFF. 023 * If a long address is in use, bits 6 and 7 of the high byte are set. Example: 024 * Long address 3 = 0xc0 0x03 Short address 3 = 0x00 0x03 025 * <p> 026 * NCE file format: 027 * <p> 028 * :F500 (con 0 lead loco) (con 1 lead loco) ....... (con 7 lead loco) :F510 029 * (con 8 lead loco) ........ (con 15 lead loco) . . :F5F0 (con 120 lead loco) 030 * ..... (con 127 lead loco) 031 * <p> 032 * :F600 (con 0 rear loco) (con 1 rear loco) ....... (con 7 rear loco) . . :F6F0 033 * (con 120 rear loco) ..... (con 127 rear loco) 034 * <p> 035 * :F700 (con 0 mid loco1) (con 0 mid loco2) (con 0 mid loco3) (con 0 mid loco4) 036 * . . :FAF0 (con 126 mid loco1) .. (con 126 mid loco4)(con 127 mid loco1) .. 037 * (con 127 mid loco4) :0000 038 * 039 * @author Dan Boudreau Copyright (C) 2008, 2015 040 */ 041public class NceConsistEngines extends Thread implements jmri.jmrix.nce.NceListener { 042 043 private boolean syncOK = true; // used to flag status messages 044 EngineManager engineManager = InstanceManager.getDefault(EngineManager.class); 045 List<Engine> engineList; 046 List<String> consists; 047 048 javax.swing.JLabel textConsist = new javax.swing.JLabel(); 049 javax.swing.JLabel indexNumber = new javax.swing.JLabel(); 050 051 private static final int CS_CONSIST_MEM = 0xF500; // start of NCE CS Consist memory 052 private static final int CS_CON_MEM_REAR = 0x100; // array offset rear consist locos 053 private static final int CS_CON_MEM_MID = 0x200; // array offset mid consist locos 054 055 private static final int REPLY_16 = 16; // reply length of 16 byte expected 056 private int replyLen = 0; // expected byte length 057 private int waiting = 0; // to catch responses not intended for this module 058 private int index = 0; // byte index when reading NCE consist memory 059 private static final int CONSIST_LNTH = 128 * 6 * 2; // 128 consists x 6 engines per consists x 2 bytes 060 private static final int NUM_CONSIST_READS = CONSIST_LNTH / REPLY_16; // read 16 bytes each time from NCE memory 061 062 private static final String NCE = "nce_"; // NOI18N 063// private static final int LEAD_BLOCK_NUMBER = 0; // mid locos blocking 2 through 5 064// private static final int REAR_BLOCK_NUMBER = 8; // rear blocking needs to be greater than 5 065 066 private static byte[] nceConsistData = new byte[CONSIST_LNTH]; 067 068 NceTrafficController tc; 069 070 public NceConsistEngines(NceTrafficController tc) { 071 super(); 072 this.tc = tc; 073 } 074 075 @Override 076 // we use a thread so the status frame will work! 077 public void run() { 078 if (tc == null) { 079 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("NceSynchronizationFailed"), Bundle 080 .getMessage("NceConsist"), JmriJOptionPane.ERROR_MESSAGE); 081 return; 082 } 083 if (JmriJOptionPane.showConfirmDialog(null, Bundle.getMessage("SynchronizeWithNce"), Bundle 084 .getMessage("NceConsist"), JmriJOptionPane.YES_NO_OPTION) != JmriJOptionPane.YES_OPTION) { 085 return; 086 } 087 // reset 088 index = 0; 089 waiting = 0; 090 syncOK = true; 091 092 // create a status frame 093 JPanel ps = new JPanel(); 094 jmri.util.JmriJFrame fstatus = new jmri.util.JmriJFrame(Bundle.getMessage("ReadingNceConsistMemory")); 095 fstatus.setLocationRelativeTo(null); 096 fstatus.setSize(300, 100); 097 098 ps.add(textConsist); 099 ps.add(indexNumber); 100 fstatus.getContentPane().add(ps); 101 textConsist.setText(Bundle.getMessage("ReadNumber")); 102 textConsist.setVisible(true); 103 indexNumber.setVisible(true); 104 fstatus.setVisible(true); 105 106 // now copy NCE memory into array 107 for (int readIndex = 0; readIndex < NUM_CONSIST_READS; readIndex++) { 108 109 indexNumber.setText(Integer.toString(readIndex)); 110 fstatus.setVisible(true); 111 112 getNceConsist(readIndex); 113 114 if (!syncOK) { 115 break; 116 } 117 } 118 // kill status panel 119 fstatus.dispose(); 120 121 if (syncOK) { 122 // now check each engine in the operations to see if there are any matches 123 engineList = engineManager.getByNumberList(); 124 consists = new ArrayList<>(); 125 126 // look for lead engines 127 for (int consistNum = 1; consistNum < 128; consistNum++) { 128 InstanceManager.getDefault(ConsistManager.class).deleteConsist(NCE + consistNum); 129 int engNum = getEngineNumberFromArray(consistNum, 0, 2); 130 if (engNum != 0) { 131 log.debug("NCE consist {} has lead engine {}", consistNum, engNum); 132 boolean engMatch = false; 133 for (Engine engine : engineList) { 134 if (engine.getNumber().equals(Integer.toString(engNum))) { 135 log.debug("found lead engine match {}", engine.getNumber()); 136 Consist engConsist = InstanceManager.getDefault(ConsistManager.class).newConsist(NCE + consistNum); 137 engConsist.setConsistNumber(consistNum); // load the consist number 138 engine.setConsist(engConsist); 139 engine.setBlocking(Engine.DEFAULT_BLOCKING_ORDER); 140 engMatch = true; 141 consists.add(Integer.toString(consistNum)); 142 break; 143 } 144 } 145 if (!engMatch) { 146 log.info("Lead engine {} not found in operations for NCE consist {}", engNum, consistNum); // NOI18N 147 } 148 } 149 } 150 // look for rear engines 151 syncEngines(CS_CON_MEM_REAR, 2); 152 // look for mid engines 153 syncEngines(CS_CON_MEM_MID, 8); 154 syncEngines(CS_CON_MEM_MID + 2, 8); 155 syncEngines(CS_CON_MEM_MID + 4, 8); 156 syncEngines(CS_CON_MEM_MID + 6, 8); 157 } 158 159 if (syncOK) { 160 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("SuccessfulSynchronization"), Bundle 161 .getMessage("NceConsist"), JmriJOptionPane.INFORMATION_MESSAGE); 162 } else { 163 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("SynchronizationFailed"), Bundle 164 .getMessage("NceConsist"), JmriJOptionPane.ERROR_MESSAGE); 165 } 166 } 167 168 private void syncEngines(int offset, int step) { 169 for (int consistNum = 1; consistNum < 128; consistNum++) { 170 int engNum = getEngineNumberFromArray(consistNum, offset, step); 171 if (engNum != 0) { 172 log.debug("NCE consist {} has engine {}", consistNum, engNum); 173 boolean engMatch = false; 174 for (Engine engine : engineList) { 175 if (engine.getNumber().equals(Integer.toString(engNum))) { 176 log.debug("found engine match {}", engine.getNumber()); 177 engMatch = true; 178 Consist engConsist = InstanceManager.getDefault(ConsistManager.class).getConsistByName(NCE + consistNum); 179 if (engConsist != null) { 180 engine.setConsist(engConsist); 181 if (offset == CS_CON_MEM_REAR) { 182 engine.setBlocking(Engine.NCE_REAR_BLOCK_NUMBER); // place rear loco at end of consist 183 } else { 184 engine.setBlocking(engConsist.getSize()); // mid block numbers 2 through 5 185 } 186 break; 187 } 188 log.warn("Engine ({}) needs lead engine {} for consist {}", engNum, getEngineNumberFromArray( 189 consistNum, 0, 2), consistNum); 190 JmriJOptionPane.showMessageDialog(null, Bundle 191 .getMessage("NceConsistNeedsLeadEngine", engNum, 192 getEngineNumberFromArray(consistNum, 0, 2), consistNum), Bundle 193 .getMessage("NceConsist"), JmriJOptionPane.ERROR_MESSAGE); 194 syncOK = false; 195 } 196 } 197 if (!engMatch) { 198 log.warn("Engine {} not found in operations for NCE consist {}", engNum, consistNum); 199 if (consists.contains(Integer.toString(consistNum))) { 200 JmriJOptionPane.showMessageDialog(null, Bundle 201 .getMessage("NceConsistMissingEngineNumber", engNum, consistNum), 202 Bundle.getMessage("NceConsist"), JmriJOptionPane.ERROR_MESSAGE); 203 syncOK = false; 204 } 205 } 206 } 207 } 208 } 209 210 private int getEngineNumberFromArray(int consistNumber, int offset, int step) { 211 int engH = ((nceConsistData[consistNumber * step + offset] << 8) & 0x3FFF); 212 int engL = nceConsistData[(consistNumber * step) + offset + 1] & 0xFF; 213 return engH + engL; 214 } 215 216 // Read 16 bytes of NCE CS memory 217 private void getNceConsist(int cR) { 218 219 NceMessage m = readConsistMemory(cR); 220 tc.sendNceMessage(m, this); 221 // wait for read to complete 222 readWait(); 223 } 224 225 // wait up to 10 seconds per read 226 private synchronized boolean readWait() { 227 int waitcount = 10; 228 while (waiting > 0) { 229 try { 230 wait(1000); // 10 x 1000mSec = 10 seconds. 231 } catch (InterruptedException e) { 232 Thread.currentThread().interrupt(); // retain if needed later 233 } 234 if (waitcount-- < 0) { 235 log.error("read timeout"); 236 syncOK = false; // need to quit 237 return false; 238 } 239 } 240 return true; 241 } 242 243 // Reads 16 bytes of NCE consist memory 244 private NceMessage readConsistMemory(int num) { 245 246 int nceConsistAddr = (num * REPLY_16) + CS_CONSIST_MEM; 247 replyLen = REPLY_16; // Expect 16 byte response 248 waiting++; 249 250 byte[] bl = NceBinaryCommand.accMemoryRead(nceConsistAddr); 251 NceMessage m = NceMessage.createBinaryMessage(tc, bl, REPLY_16); 252 return m; 253 } 254 255 @Override 256 public void message(NceMessage m) { 257 } // ignore replies 258 259 @Override 260 @SuppressFBWarnings(value = {"NN_NAKED_NOTIFY", "NO_NOTIFY_NOT_NOTIFYALL"}, justification = "Only want to notify this thread" ) 261 public void reply(NceReply r) { 262 263 if (waiting <= 0) { 264 log.error("unexpected response"); 265 return; 266 } 267 if (r.getNumDataElements() != replyLen) { 268 log.error("reply length incorrect"); 269 return; 270 } 271 272 // load data buffer 273 for (int i = 0; i < REPLY_16; i++) { 274 nceConsistData[index++] = (byte) r.getElement(i); 275 } 276 waiting--; 277 278 // wake up thread 279 synchronized (this) { 280 notify(); 281 } 282 } 283 284 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(NceConsistEngines.class); 285}