001package jmri.jmrix.loconet.duplexgroup.swing;
002
003import javax.annotation.CheckForNull;
004import jmri.jmrix.loconet.LnConstants;
005import jmri.jmrix.loconet.LocoNetMessage;
006import jmri.jmrix.loconet.LocoNetSystemConnectionMemo;
007import jmri.jmrix.loconet.duplexgroup.LnDplxGrpInfoImplConstants;
008
009import org.slf4j.Logger;
010import org.slf4j.LoggerFactory;
011
012/**
013 * Implements a class to handle message creation and message interpretation of
014 * LocoNet messages associated with IPL. IPL is a mechanism which allows
015 * identification and firmware programming of some types of Digitrax hardware.
016 *
017 * @author B. Milhaupt Copyright 2010, 2011, 2018
018 */
019public class LnIPLImplementation extends javax.swing.JComponent implements jmri.jmrix.loconet.LocoNetListener {
020
021    /**
022     * Constructor for LnIPMImplementation for a given
023     * LocoNetSystemConnectionMemo as provided by the instantiating
024     * method.
025     *
026     * @param lnMemo LocoNetSystemConnectionMemo for the LocoNet communication
027     *               interface
028     */
029    public LnIPLImplementation(LocoNetSystemConnectionMemo lnMemo) {
030        super();
031        thisone = this;
032        memo = lnMemo;
033
034        moreInit();
035    }
036
037    private void moreInit() {
038        waitingForIplReply = false;
039        // connect to the LnTrafficController
040        connect(memo.getLnTrafficController());
041
042        swingTmrIplQuery = new javax.swing.Timer(LnDplxGrpInfoImplConstants.IPL_QUERY_DELAY, new java.awt.event.ActionListener() {
043            @Override
044            public void actionPerformed(java.awt.event.ActionEvent e) {
045                swingTmrIplQuery.stop();
046                waitingForIplReply = false;
047                int oldvalue = 9999;
048                int newvalue = 0;
049                thisone.firePropertyChange("LnIPLEndOfDeviceQuery", oldvalue, newvalue); // NOI18N
050            }
051        });
052    }
053
054    /**
055     * Create a LocoNet packet which queries UR92(s) for Duplex group
056     * identification information. The invoking method is responsible for
057     * sending the message to LocoNet.
058     *
059     * @return a LocoNetMessage containing the packet required to query UR92
060     *         device Duplex Group Identity information
061     */
062    public static final LocoNetMessage createQueryAllIplDevicesPacket() {
063        LocoNetMessage m = new LocoNetMessage(LnConstants.RE_IPL_OP_LEN);
064        // fill in data for an IPL Query of UR92s message
065        m.setElement(0, LnConstants.OPC_PEER_XFER);
066        m.setElement(1, LnConstants.RE_IPL_OP_LEN);
067        m.setElement(2, LnConstants.RE_IPL_IDENTITY_OPERATION);
068        m.setElement(3, LnConstants.RE_IPL_OP_QUERY);
069        m.setElement(4, LnConstants.RE_IPL_MFR_ALL);
070        m.setElement(5, LnConstants.RE_IPL_DIGITRAX_HOST_ALL);
071        m.setElement(6, LnConstants.RE_IPL_DIGITRAX_SLAVE_ALL);
072        m.setElement(7, LnConstants.RE_IPL_MFR_ALL);
073        m.setElement(8, LnConstants.RE_IPL_OP_HFW_QUERY);
074        m.setElement(9, LnConstants.RE_IPL_OP_HSNM_QUERY);
075        m.setElement(10, LnConstants.RE_IPL_OP_SFW_QUERY);
076        m.setElement(11, LnConstants.RE_IPL_OP_HSN0_QUERY);
077        m.setElement(12, LnConstants.RE_IPL_OP_HSN1_QUERY);
078        m.setElement(13, LnConstants.RE_IPL_OP_HSN2_QUERY);
079        m.setElement(14, LnConstants.RE_IPL_OP_SSNM_QUERY);
080        m.setElement(15, LnConstants.RE_IPL__OP_SSN0_QUERY);
081        m.setElement(16, LnConstants.RE_IPL_OP_SSN1_QUERY);
082        m.setElement(17, LnConstants.RE_IPL_OP_SSN2_QUERY);
083        m.setElement(18, LnConstants.RE_IPL_OP_SSN3_QUERY);
084        return m;
085    }
086
087    public void sendIplQueryAllDevices() {
088        jmri.jmrix.loconet.LnTrafficController tc = memo.getLnTrafficController();
089        tc.sendLocoNetMessage(createQueryAllIplDevicesPacket());
090        waitingForIplReply = true;
091    }
092
093    /**
094     * Create a LocoNet packet which queries IPL devices by specific host
095     * manufacturer and specific host device type. The invoking method is
096     * responsible for sending the message to LocoNet.
097     * <p>
098     * Note: Different devices may only respond to IPL Identity requests if the
099     * host manufacturer and host type are defined. Others devices will respond
100     * when host manufacturer and host type are left as zero.
101     *
102     * @param hostMfr    the host manufacturer number
103     * @param hostDevice the host device type number
104     * @return a LocoNetMessage containing the packet required to request IPL
105     *         identity information from devices of the specified host
106     *         manufacturer and host device type.
107     */
108    public static final LocoNetMessage createIplSpecificHostQueryPacket(
109            Integer hostMfr,
110            Integer hostDevice) {
111        LocoNetMessage m = createQueryAllIplDevicesPacket();
112        m.setElement(4, hostMfr & 0x7F);
113        m.setElement(5, hostDevice & 0x7F);
114        return m;
115    }
116
117    /**
118     * Create a LocoNet packet which queries IPL devices by specific slave
119     * manufacturer and specific slave device type. The invoking method is
120     * responsible for sending the message to LocoNet.
121     * <p>
122     * Note: Some devices have no "slave" device and may not respond to this
123     * message. Other devices may only respond if both manufacturer and device
124     * type information is specified for both host and slave.
125     *
126     * @param slaveMfr    the slave manufacturer number
127     * @param slaveDevice the slave device type number
128     * @return a LocoNetMessage containing the packet required to request IPL
129     *         identity information from devices of the specified slave
130     *         manufacturer and slave device type.
131     */
132    public static final LocoNetMessage createIplSpecificSlaveQueryPacket(
133            Integer slaveMfr,
134            Integer slaveDevice) {
135        LocoNetMessage m = createQueryAllIplDevicesPacket();
136        m.setElement(7, slaveMfr & 0x7F);
137        m.setElement(6, slaveDevice & 0x7F);
138        return m;
139    }
140
141    /**
142     * Create a LocoNet packet which queries IPL devices by specific host
143     * manufacturer, specific host device type, specific slave manufacturer and
144     * specific slave device type. The invoking method is responsible for
145     * sending the message to LocoNet.
146     * <p>
147     * Note: Different devices respond differently depending on whether host
148     * and/or slave manufacturer and/or device type information are provided.
149     *
150     * @param hostMfr     the host manufacturer number
151     * @param hostDevice  the host device type number
152     * @param slaveMfr    the slave manufacturer number
153     * @param slaveDevice the slave device type number
154     * @return a LocoNetMessage containing the packet required to request IPL
155     *         identity information from devices of the specified host and slave
156     *         manufacturers and host and slave device types.
157     */
158    public static final LocoNetMessage createIplSpecificSlaveQueryPacket(
159            Integer hostMfr,
160            Integer hostDevice,
161            Integer slaveMfr,
162            Integer slaveDevice) {
163        LocoNetMessage m = createQueryAllIplDevicesPacket();
164        m.setElement(4, hostMfr & 0x7F);
165        m.setElement(5, hostDevice & 0x7F);
166        m.setElement(7, slaveMfr & 0x7F);
167        m.setElement(6, slaveDevice & 0x7F);
168        return m;
169    }
170
171    /**
172     * Create a LocoNet packet which queries UR92 devices for IPL
173     * identification information. The invoking method is responsible for
174     * sending the message to LocoNet.
175     *
176     * @return a LocoNetMessage containing the packet required to query UR92
177     *         devices for IPL identification information
178     */
179    public static final LocoNetMessage createIplUr92QueryPacket() {
180        return createIplSpecificHostQueryPacket(
181                LnConstants.RE_IPL_MFR_DIGITRAX,
182                LnConstants.RE_IPL_DIGITRAX_HOST_UR92);
183    }
184
185    /**
186     * Create a LocoNet packet which queries DT402x throttles for IPL
187     * identification information. The invoking method is responsible for
188     * sending the message to LocoNet.
189     *
190     * @return a LocoNetMessage containing the packet required to query DT402x
191     *         devices for IPL identification information
192     */
193    public static final LocoNetMessage createIplDt402QueryPacket() {
194        return createIplSpecificHostQueryPacket(
195                LnConstants.RE_IPL_MFR_DIGITRAX,
196                LnConstants.RE_IPL_DIGITRAX_HOST_DT402);
197    }
198
199    /**
200     * Create a LocoNet packet which queries (some) UT4 throttles for IPL
201     * identification information. The invoking method is responsible for
202     * sending the message to LocoNet.
203     * <p>
204     * Note that UT4 and UT4R devices may not respond to this query. UT4D
205     * devices may respond to this query.
206     *
207     * @return a LocoNetMessage containing the packet required to query (some)
208     *         UT4 devices for IPL identification information
209     */
210    public static final LocoNetMessage createIplUt4QueryPacket() {
211        return createIplSpecificHostQueryPacket(
212                LnConstants.RE_IPL_MFR_DIGITRAX,
213                LnConstants.RE_IPL_DIGITRAX_HOST_UT4);
214    }
215
216    /**
217     * Create a LocoNet packet which queries DCS51 devices for IPL
218     * identification information. The invoking method is responsible for
219     * sending the message to LocoNet.
220     *
221     * @return a LocoNetMessage containing the packet required to query DCS51
222     *         devices for IPL identification information
223     */
224    public static final LocoNetMessage createIplDcs51QueryPacket() {
225        return createIplSpecificHostQueryPacket(
226                LnConstants.RE_IPL_MFR_DIGITRAX,
227                LnConstants.RE_IPL_DIGITRAX_HOST_DCS51);
228    }
229
230    /**
231     * Create a LocoNet packet which queries DCS52 devices for IPL
232     * identification information. The invoking method is responsible for
233     * sending the message to LocoNet.
234     *
235     * @return a LocoNetMessage containing the packet required to query DCS52
236     *         devices for IPL identification information
237     */
238    public static final LocoNetMessage createIplDcs52QueryPacket() {
239        return createIplSpecificHostQueryPacket(
240                LnConstants.RE_IPL_MFR_DIGITRAX,
241                LnConstants.RE_IPL_DIGITRAX_HOST_DCS52);
242    }
243
244    /**
245     * Create a LocoNet packet which queries PR3 devices for IPL identification
246     * information. The invoking method is responsible for sending the message
247     * to LocoNet.
248     *
249     * @return a LocoNetMessage containing the packet required to query PR3
250     *         devices for IPL identification information
251     */
252    public static final LocoNetMessage createIplPr3QueryPacket() {
253        return createIplSpecificHostQueryPacket(
254                LnConstants.RE_IPL_MFR_DIGITRAX,
255                LnConstants.RE_IPL_DIGITRAX_HOST_PR3);
256    }
257
258    /**
259     * Checks message m to determine if it contains a IPL Identity Report
260     * message.
261     *
262     * @param m  LocoNetMessage to be checked for an IPL Identity Query message
263     * @return true if message is report of IPL Identity
264     */
265    public static final boolean isIplIdentityQueryMessage(LocoNetMessage m) {
266        if ((m.getOpCode() == LnConstants.OPC_PEER_XFER)
267                && (m.getElement(1) == LnConstants.RE_IPL_OP_LEN)) {
268            // Message is a peer-to-peer message of appropriate length for
269            // IPL Report message.  Check the individual message type
270            if (m.getElement(2) == LnConstants.RE_IPL_IDENTITY_OPERATION) {
271                // To be sure the message is a IPL Identity Report operation, check the
272                // operation type.
273                if (m.getElement(3) == LnConstants.RE_IPL_OP_QUERY) {
274                    return true;
275                }
276            }
277        }
278        return false;
279    }
280
281    /**
282     * Checks message m to determine if it contains a IPL Identity Report
283     * message.
284     *
285     * @param m  LocoNet message to check for an IPL Identity Report
286     * @return true if message is report of IPL Identity
287     */
288    public static final boolean isIplIdentityReportMessage(LocoNetMessage m) {
289        if ((m.getOpCode() == LnConstants.OPC_PEER_XFER)
290                && (m.getElement(1) == LnConstants.RE_IPL_OP_LEN)) {
291            // Message is a peer-to-peer message of appropriate length for
292            // IPL Report message.  Check the individual message type
293            if (m.getElement(2) == LnConstants.RE_IPL_IDENTITY_OPERATION) {
294                // To be sure the message is a IPL Identity Report operation, check the
295                // operation type.
296                if (m.getElement(3) == LnConstants.RE_IPL_OP_REPORT) {
297                    return true;
298                }
299            }
300        }
301        return false;
302    }
303
304    /**
305     * Check message m to determine if it contains an IPL Identity Report
306     * message for a specific host manufacturer and specific host device type.
307     *
308     * @param m message to analyse
309     * @param hostMfr    the host manufacturer number
310     * @param hostDevice the host device type number
311     * @return true if message is report of UR92 IPL Identity
312     */
313    public static final boolean isIplSpecificIdentityReportMessage(LocoNetMessage m,
314            Integer hostMfr, Integer hostDevice) {
315        if (!isIplIdentityReportMessage(m)) {
316            return false;
317        }
318        if ((m.getElement(4) == (hostMfr & 0x7F))
319                && (m.getElement(5) == (hostDevice & 0x7F))) {
320            return true;
321        }
322        return false;
323    }
324
325    /**
326     * Check message m to determine if it contains a UR92 IPL Identity Report
327     * message.
328     *
329     * @param m message to analyse
330     * @return true if message is report of UR92 IPL Identity
331     */
332    public static final boolean isIplUr92IdentityReportMessage(LocoNetMessage m) {
333        return isIplSpecificIdentityReportMessage(m,
334                LnConstants.RE_IPL_MFR_DIGITRAX,
335                LnConstants.RE_IPL_DIGITRAX_HOST_UR92);
336    }
337
338    /**
339     * Check message m to determine if it contains a DT402 IPL Identity Report
340     * message.
341     *
342     * @param m message to analyse
343     * @return true if message is report of DT402 IPL Identity
344     */
345    public static final boolean isIplDt402IdentityReportMessage(LocoNetMessage m) {
346        return isIplSpecificIdentityReportMessage(m,
347                LnConstants.RE_IPL_MFR_DIGITRAX,
348                LnConstants.RE_IPL_DIGITRAX_HOST_DT402);
349    }
350
351    /**
352     * Check message m to determine if it contains a UT4 IPL Identity Report
353     * message.
354     *
355     * @param m message to analyse
356     * @return true if message is report of UT4 IPL Identity
357     */
358    public static final boolean isIplUt4IdentityReportMessage(LocoNetMessage m) {
359        return isIplSpecificIdentityReportMessage(m,
360                LnConstants.RE_IPL_MFR_DIGITRAX,
361                LnConstants.RE_IPL_DIGITRAX_HOST_UT4);
362    }
363
364    /**
365     * Check message m to determine if it contains a DSC51 IPL Identity Report
366     * message.
367     *
368     * @param m message to analyse
369     * @return true if message is report of DCS51 IPL Identity
370     */
371    public static final boolean isIplDcs51IdentityReportMessage(LocoNetMessage m) {
372        return isIplSpecificIdentityReportMessage(m,
373                LnConstants.RE_IPL_MFR_DIGITRAX,
374                LnConstants.RE_IPL_DIGITRAX_HOST_DCS51);
375    }
376
377    /**
378     * Check message m to determine if it contains a DSC52 IPL Identity Report
379     * message.
380     *
381     * @param m message to analyse
382     * @return true if message is report of DCS52 IPL Identity
383     */
384    public static final boolean isIplDcs52IdentityReportMessage(LocoNetMessage m) {
385        return isIplSpecificIdentityReportMessage(m,
386                LnConstants.RE_IPL_MFR_DIGITRAX,
387                LnConstants.RE_IPL_DIGITRAX_HOST_DCS52);
388    }
389
390    /**
391     * Check message m to determine if it contains a UR93 IPL Identity Report
392     * message.
393     *
394     * @param m message to analyse
395     * @return true if message is report of UR92 IPL Identity
396     */
397    public static final boolean isIplUr93IdentityReportMessage(LocoNetMessage m) {
398        return isIplSpecificIdentityReportMessage(m,
399                LnConstants.RE_IPL_MFR_DIGITRAX,
400                LnConstants.RE_IPL_DIGITRAX_HOST_UR93);
401    }
402
403    /**
404     * Check message m to determine if it contains a PR3 IPL Identity Report
405     * message.
406     *
407     * @param m message to analyse
408     * @return true if message is report of PR3 IPL Identity
409     */
410    public static final boolean isIplPr3IdentityReportMessage(LocoNetMessage m) {
411        return isIplSpecificIdentityReportMessage(m,
412                LnConstants.RE_IPL_MFR_DIGITRAX,
413                LnConstants.RE_IPL_DIGITRAX_HOST_PR3);
414    }
415
416    public static final boolean isIplDt402DIdentityReportMessage(LocoNetMessage m) {
417        if (!isIplDt402IdentityReportMessage(m)) {
418            return false;
419        }
420        if (!isIplRf24SlaveIdentityReportMessage(m)) {
421            return false;
422        } else {
423            return true;
424        }
425    }
426
427    public static final boolean isIplUt4DIdentityReportMessage(LocoNetMessage m) {
428        if (!isIplUt4IdentityReportMessage(m)) {
429            return false;
430        }
431        if (!isIplRf24SlaveIdentityReportMessage(m)) {
432            return false;
433        } else {
434            return true;
435        }
436    }
437
438    public static final boolean isIplPr4IdentityReportMessage(LocoNetMessage m) {
439        return isIplSpecificIdentityReportMessage(m,
440                LnConstants.RE_IPL_MFR_DIGITRAX,
441                LnConstants.RE_IPL_DIGITRAX_HOST_PR4);
442    }
443
444    public static final boolean isIplBxp88IdentityReportMessage(LocoNetMessage m) {
445        return isIplSpecificIdentityReportMessage(m,
446                LnConstants.RE_IPL_MFR_DIGITRAX,
447                LnConstants.RE_IPL_DIGITRAX_HOST_BXP88);
448    }
449
450    public static final boolean isIplLnwiIdentityReportMessage(LocoNetMessage m) {
451        return isIplSpecificIdentityReportMessage(m,
452                LnConstants.RE_IPL_MFR_DIGITRAX,
453                LnConstants.RE_IPL_DIGITRAX_HOST_LNWI);
454    }
455
456    public static final boolean isIplDcs240IdentityReportMessage(LocoNetMessage m) {
457        return isIplSpecificIdentityReportMessage(m,
458                LnConstants.RE_IPL_MFR_DIGITRAX,
459                LnConstants.RE_IPL_DIGITRAX_HOST_DCS240);
460    }
461
462    public static final boolean isIplDcs210IdentityReportMessage(LocoNetMessage m) {
463        return isIplSpecificIdentityReportMessage(m,
464                LnConstants.RE_IPL_MFR_DIGITRAX,
465                LnConstants.RE_IPL_DIGITRAX_HOST_DCS210);
466    }
467
468    public static final boolean isIplDcs210PlusIdentityReportMessage(LocoNetMessage m) {
469        return isIplSpecificIdentityReportMessage(m,
470                LnConstants.RE_IPL_MFR_DIGITRAX,
471                LnConstants.RE_IPL_DIGITRAX_HOST_DCS210PLUS);
472    }
473
474    public static final boolean isIplDcs240PlusIdentityReportMessage(LocoNetMessage m) {
475        return isIplSpecificIdentityReportMessage(m,
476                LnConstants.RE_IPL_MFR_DIGITRAX,
477                LnConstants.RE_IPL_DIGITRAX_HOST_DCS240PLUS);
478    }
479
480    public static final boolean isIplDt500DIdentityReportMessage(LocoNetMessage m) {
481        if (!isIplDt500IdentityReportMessage(m)) {
482            return false;
483        }
484        if (!isIplRf24SlaveIdentityReportMessage(m)) {
485            return false;
486        } else {
487            return true;
488        }
489    }
490
491    /**
492     * Check message m to determine if it contains a DT500 IPL Identity Report
493     * message.
494     *
495     * @param m message to analyse
496     * @return true if message is report of DT500 IPL Identity
497     */
498    public static final boolean isIplDt500IdentityReportMessage(LocoNetMessage m) {
499        return isIplSpecificIdentityReportMessage(m,
500                LnConstants.RE_IPL_MFR_DIGITRAX,
501                LnConstants.RE_IPL_DIGITRAX_HOST_DT500);
502    }
503
504    /**
505     * Determine if message is IPL Identity Report with RF24 as slave device.
506     *
507     * @param m message to analyse
508     * @return true if m contains IPL Identity Report with RF24 as slave, else
509     *         false
510     */
511    private static final boolean isIplRf24SlaveIdentityReportMessage(LocoNetMessage m) {
512        if ((extractIplIdentitySlaveManufacturer(m) == LnConstants.RE_IPL_MFR_DIGITRAX)
513                && (extractIplIdentitySlaveDevice(m) == LnConstants.RE_IPL_DIGITRAX_SLAVE_RF24)) {
514            return true;
515        } else {
516            return false;
517        }
518    }
519
520    /**
521     * Extract the IPL Host manufacturer and Device information from m and
522     * return the interpreted information as a String.
523     *
524     * @param m LocoNet Message containg the IPL Identity report
525     * @return String containing the interpreted IPL Host Manufacturer and
526     *         Device. If m is not a valid IPL Identity report, returns null.
527     */
528    public static final String extractInterpretedIplHostDevice(LocoNetMessage m) {
529        if (!isIplIdentityReportMessage(m)) {
530            return null;
531        }
532        if (isIplDt402DIdentityReportMessage(m)) {
533            return interpretHostManufacturerDevice(
534                    LnConstants.RE_IPL_MFR_DIGITRAX,
535                    LnConstants.RE_IPL_DIGITRAX_HOST_DT402,
536                    LnConstants.RE_IPL_MFR_DIGITRAX,
537                    LnConstants.RE_IPL_DIGITRAX_SLAVE_RF24);
538        }
539        if (isIplUt4DIdentityReportMessage(m)) {
540            return interpretHostManufacturerDevice(
541                    LnConstants.RE_IPL_MFR_DIGITRAX,
542                    LnConstants.RE_IPL_DIGITRAX_HOST_UT4,
543                    LnConstants.RE_IPL_MFR_DIGITRAX,
544                    LnConstants.RE_IPL_DIGITRAX_SLAVE_RF24);
545        }
546        if (isIplDt500DIdentityReportMessage(m)) {
547            return interpretHostManufacturerDevice(
548                    LnConstants.RE_IPL_MFR_DIGITRAX,
549                    LnConstants.RE_IPL_DIGITRAX_HOST_DT500,
550                    LnConstants.RE_IPL_MFR_DIGITRAX,
551                    LnConstants.RE_IPL_DIGITRAX_SLAVE_RF24);
552        }
553        if (isIplUr92IdentityReportMessage(m)) {
554            return interpretHostManufacturerDevice(
555                    LnConstants.RE_IPL_MFR_DIGITRAX,
556                    LnConstants.RE_IPL_DIGITRAX_HOST_UR92,
557                    LnConstants.RE_IPL_MFR_DIGITRAX,
558                    LnConstants.RE_IPL_DIGITRAX_SLAVE_RF24);
559        }
560
561        return interpretHostManufacturerDevice(extractIplIdentityHostManufacturer(m), extractIplIdentityHostDevice(m));
562    }
563
564    /**
565     * Extract the IPL Slave manufacturer and Device information from m.
566     *
567     * @param m IPL Identity message
568     * @return String containing the interpreted IPL Slave Manufacturer and
569     *         Device. If m is not a valid IPL Identity report, returns null.
570     */
571    public static final String extractInterpretedIplSlaveDevice(LocoNetMessage m) {
572        if (!isIplIdentityReportMessage(m)) {
573            return null;
574        }
575        return interpretSlaveManufacturerDevice(extractIplIdentitySlaveManufacturer(m), extractIplIdentitySlaveDevice(m));
576
577    }
578
579    /**
580     * Get the IPL host manufacturer number from an IPL Identity report
581     * message.
582     * <p>
583     * The invoking method should ensure that message m is an IPL Identity
584     * message before invoking this method.
585     *
586     * @param m IPL Identity message
587     * @return Integer containing the IPL host manufacturer number
588     */
589    public static final Integer extractIplIdentityHostManufacturer(LocoNetMessage m) {
590        return m.getElement(4);
591    }
592
593    /**
594     * Get the host device number from an IPL Identity report message.
595     * <p>
596     * The invoking method should ensure that message m is is an IPL Identity
597     * message before invoking this method.
598     *
599     * @param m IPL Identity message
600     * @return Integer containing the IPL device number
601     */
602    public static final Integer extractIplIdentityHostDevice(LocoNetMessage m) {
603        return m.getElement(5);
604    }
605
606    /**
607     * Get the slave manufacturer number from an IPL Identity report
608     * message.
609     * <p>
610     * The invoking method should ensure that message m is is an IPL Identity
611     * message before invoking this method.
612     * <p>
613     * NOTE: Not all IPL-capable devices implement a slave manufacturer number.
614     *
615     * @param m IPL Identity message
616     * @return Integer containing the IPL slave manufacturer number
617     */
618    public static final Integer extractIplIdentitySlaveManufacturer(LocoNetMessage m) {
619        return m.getElement(7);
620    }
621
622    /**
623     * Get the slave device number from an IPL Identity report message.
624     * <p>
625     * The invoking method should ensure that message m is is an IPL Identity
626     * message before invoking this method.
627     * <p>
628     * NOTE: Not all IPL-capable devices implement a slave device number.
629     *
630     * @param m IPL Identity message
631     * @return Integer containing the IPL slave device number
632     */
633    public static final Integer extractIplIdentitySlaveDevice(LocoNetMessage m) {
634        return m.getElement(6);
635    }
636
637    /**
638     * Get the host firmware revision number from an IPL Identity report
639     * message.
640     * <p>
641     * The invoking method should ensure that message m is is an IPL Identity
642     * message before invoking this method.
643     * <p>
644     * NOTE: Not all IPL-capable devices implement a host firmware revision
645     * number.
646     *
647     * @param m IPL Identity message
648     * @return String containing the IPL host firmware revision in the format
649     *         x.y
650     */
651    public static final String extractIplIdentityHostFrimwareRev(LocoNetMessage m) {
652        StringBuilder s = new StringBuilder();
653        s.append(Integer.toString((m.getElement(8) & 0x78) >> 3));
654        s.append(".");
655        s.append(Integer.toString((m.getElement(8) & 0x07)));
656        return s.toString();
657    }
658
659    /**
660     * Get the host firmware revision number from an IPL Identity report
661     * message.
662     * <p>
663     * The invoking method should ensure that message m is is an IPL Identity
664     * message before invoking this method..
665     * <p>
666     * NOTE: Not all IPL-capable devices implement a host firmware revision
667     * number.
668     *
669     * @param m IPL Identity message
670     * @return Integer containing the IPL host firmware revision
671     */
672    public static final Integer extractIplIdentityHostFrimwareRevNum(LocoNetMessage m) {
673        return (m.getElement(8));
674    }
675
676    /**
677     * Get the Slave firmware revision number from an IPL Identity report
678     * message.
679     * <p>
680     * The invoking method should ensure that message m is is an IPL Identity
681     * message before invoking this method..
682     * <p>
683     * NOTE: Not all IPL-capable devices implement a Slave firmware revision
684     * number.
685     *
686     * @param m IPL Identity message
687     * @return Integer containing the IPL Slave firmware revision
688     */
689    public static final Integer extractIplIdentitySlaveFrimwareRevNum(LocoNetMessage m) {
690        return ((m.getElement(10) & 0x7F) + ((m.getElement(9) & 0x1) << 7));
691    }
692
693    /**
694     * Get the slave firmware revision number from an IPL Identity report
695     * message.
696     * <p>
697     * The invoking method should ensure that message m is is an IPL Identity
698     * message before invoking this method..
699     * <p>
700     * NOTE: Not all IPL-capable devices implement a slave firmware revision
701     * number.
702     *
703     * @param m IPL Identity message
704     * @return String containing the IPL slave firmware revision in the format
705     *         x.y
706     */
707    public static final String extractIplIdentitySlaveFrimwareRev(LocoNetMessage m) {
708        StringBuilder s = new StringBuilder();
709        s.append(Integer.toString(((m.getElement(10) & 0x78) >> 3) + ((m.getElement(9) & 0x1) << 4)));
710        s.append(".");
711        s.append(Integer.toString((m.getElement(10) & 0x07)));
712        return s.toString();
713    }
714
715    /**
716     * Get the host serial number from an IPL Identity report message.
717     * <p>
718     * The invoking method should ensure that message m is is an IPL Identity
719     * message before invoking this method..
720     * <p>
721     * NOTE: Not all IPL-capable devices implement a host serial number.
722     *
723     * @param m IPL Identity message
724     * @return Long containing the IPL host serial number
725     */
726    public static final Long extractIplIdentityHostSerialNumber(LocoNetMessage m) {
727        Long sn;
728        Integer di_f1;
729        di_f1 = m.getElement(9);
730        sn = (long) (m.getElement(11) + ((di_f1 & 0x2) << 6));
731        sn += (((long) m.getElement(12)) << 8) + (((long) di_f1 & 0x4) << 13);
732        sn += (((long) m.getElement(13)) << 16) + (((long) di_f1 & 0x8) << 20);
733        return sn;
734    }
735
736    /**
737     * Get the slave serial number from an IPL Identity report message.
738     * <p>
739     * The invoking method should ensure that message m is is an IPL Identity
740     * message before invoking this method.
741     * <p>
742     * NOTE: Not all IPL-capable devices implement a slave serial number.
743     *
744     * @param m IPL Identity message
745     * @return Long containing the IPL slave serial number
746     */
747    public static final Long extractIplIdentitySlaveSerialNumber(LocoNetMessage m) {
748        Long sn;
749        Integer di_f2;
750        di_f2 = m.getElement(14);
751        sn = (long) (m.getElement(15) + ((di_f2 & 0x1) << 7));
752        sn += (((long) m.getElement(16)) << 8) + (((long) di_f2 & 0x2) << 14);
753        sn += (((long) m.getElement(17)) << 16) + (((long) di_f2 & 0x4) << 21);
754        sn += (((long) m.getElement(18)) << 24) + (((long) di_f2 & 0x8) << 28);
755        return sn;
756    }
757
758    /**
759     * Interpret IPL Identity Host Manufacturer and Host Device number as a
760     * string.
761     * <p>
762     * NOTE: Some IPL-capable devices cannot be completely determined based
763     * solely on Host Manufacturer number and Host Device number.
764     * <p>
765     * NOTE: Some members of a device family do not support IPL. An interpreted
766     * IPL Host Manufacturer number and Host Device number might imply that all
767     * members do support IPL. As an example, UT4 and UT4R devices do not appear
768     * to support IPL, while UT4D appears to support IPL. This method will
769     * return "Digitrax UT4(x)" in response to appropriate Host Manufacturer
770     * number and appropriate Host Device number.
771     *
772     * @param hostMfr  host manufacturer number
773     * @param hostDevice  host device number
774     * @param slaveMfr  slave manufacturer number
775     * @param slaveDevice  slave device number
776     * @return String containing Manufacturer name and Device model.
777     */
778    public static final String interpretHostManufacturerDevice(Integer hostMfr, Integer hostDevice,
779            Integer slaveMfr, Integer slaveDevice) {
780        int manuf = hostMfr & 0x7f;
781        int device = hostDevice & 0x7f;
782        int slave = slaveDevice & 0x7f;
783        int smanuf = slaveMfr & 0x7f;
784        String mfgName = getManufacturer(manuf);
785        String devName = getDeviceName(manuf, device, smanuf, slave);
786        if (mfgName == null) {
787            log.debug("Unknown Unknown Host Manufacturer/Device [{}]",Integer.toHexString(manuf));
788            return "Unknown Host Manufacturer/Device";
789        } else if (devName == null) {
790            log.debug("Unknown Device Manufacturer[{}] Device[{}] Slave manufacturer[{}] Slave device[{}]",
791                    Integer.toHexString(manuf),
792                    Integer.toHexString(device),
793                    Integer.toHexString(smanuf),
794                    Integer.toHexString(slave));
795            return mfgName+" Unknown Device";
796        }
797        return mfgName+" "+devName;
798    }
799
800    /**
801     * Interpret IPL Identity Host Manufacturer and Host Device number as a
802     * string.
803     * <p>
804     * NOTE: Some IPL-capable devices cannot be completely determined based
805     * solely on Host Manufacturer number and Host Device number.
806     * <p>
807     * NOTE: Some members of a device family do not support IPL. An interpreted
808     * IPL Host Manufacturer number and Host Device number might imply that all
809     * members do support IPL. As an example, UT4 and UT4R devices do not appear
810     * to support IPL, while UT4D appears to support IPL. This method will
811     * return "Digitrax UT4(x)" in response to appropriate Host Manufacturer
812     * number and appropriate Host Device number.
813     *
814     * @param hostMfr  host manufacturer number
815     * @param hostDevice  host device number
816     * @return String containing Manufacturer name and Device model.
817     */
818    public static final String interpretHostManufacturerDevice(Integer hostMfr, Integer hostDevice) {
819        return interpretHostManufacturerDevice(hostMfr, hostDevice, 0, 0);
820    }
821
822    /**
823     * Interpret IPL Identity Slave Manufacturer and Slave Device number as a
824     * string.
825     * <p>
826     * NOTE: Some IPL-capable devices may not be completely determined based
827     * solely on Slave Manufacturer number and Slave Device number.
828     *
829     * @param slaveMfr  slave manufacturer number
830     * @param slaveDevice  slave device number
831     * @return String containing Slave Manufacturer name and Device model.
832     */
833    public static final String interpretSlaveManufacturerDevice(Integer slaveMfr, Integer slaveDevice) {
834        String s;
835        s = "Unknown Slave Manufacturer/Device";
836        int sMfr = slaveMfr & 0x7f;
837        int sDevice = slaveDevice & 0x7F;
838        switch (sMfr) {
839            case LnConstants.RE_IPL_MFR_DIGITRAX: {
840                switch (sDevice) {
841                    case LnConstants.RE_IPL_DIGITRAX_SLAVE_RF24:
842                        s = "Digitrax RF24"; // NOI18N
843                        break;
844                    default:
845                        s = "Digitrax (unknown Slave Device)";
846                        break;
847                }
848                break;
849            }
850            case LnConstants.RE_IPL_MFR_RR_CIRKITS: {
851                s = "RR-CirKits (unknown Slave Device)";
852                break;
853            }
854            default:
855                break;
856        }
857        return s;
858    }
859    LnIPLImplementation thisone;
860    private LocoNetSystemConnectionMemo memo;
861
862    /**
863     * Connect this instance's LocoNetListener to the LocoNet Traffic Controller.
864     *
865     * @param t  a LocoNet Traffic Controller
866     */
867    public void connect(jmri.jmrix.loconet.LnTrafficController t) {
868        if (t != null) {
869            // connect to the LnTrafficController
870            t.addLocoNetListener(~0, this);
871        }
872    }
873    private javax.swing.Timer swingTmrIplQuery;
874
875    /**
876     * Break connection with the LnTrafficController and stop timers.
877     */
878    public void dispose() {
879        if (swingTmrIplQuery != null) {
880            swingTmrIplQuery.stop();
881        }
882        if (memo.getLnTrafficController() != null) {
883            memo.getLnTrafficController().removeLocoNetListener(~0, this);
884        }
885    }
886
887    /**
888     * Process all incoming LocoNet messages to look for IPL operations. Ignores
889     * all other LocoNet messages.
890     *
891     * @param m  incoming LocoNet message to be examined
892     */
893    @Override
894    public void message(LocoNetMessage m) {
895
896        if (handleMessageIplDeviceQuery(m)) {
897            return;
898        } else if (handleMessageIplDeviceReport(m)) {
899            return;
900        }
901
902        return;
903    }
904
905    private boolean handleMessageIplDeviceQuery(LocoNetMessage m) {
906        if (isIplIdentityQueryMessage(m)) {
907            Integer deviceType = 256 * extractIplIdentityHostManufacturer(m)
908                    + extractIplIdentityHostDevice(m);
909            int oldvalue = 99999;
910            int newvalue = deviceType;
911            thisone.firePropertyChange("IplDeviceTypeQuery", oldvalue, newvalue); // NOI18N
912            if (waitingForIplReply == true) {
913                swingTmrIplQuery.restart();
914            }
915            return true;
916        }
917        return false;
918    }
919
920    private boolean handleMessageIplDeviceReport(LocoNetMessage m) {
921        if (isIplIdentityReportMessage(m)) {
922            Integer deviceType = 256 * extractIplIdentityHostManufacturer(m)
923                    + extractIplIdentityHostDevice(m);
924            int oldvalue = 99999;
925            int newvalue = deviceType;
926            thisone.firePropertyChange("IplDeviceTypeReport", oldvalue, newvalue); // NOI18N
927            if (waitingForIplReply == true) {
928                waitingForIplReply = false;
929                swingTmrIplQuery.stop();
930            }
931            return true;
932        }
933        return false;
934    }
935    public boolean isIplQueryTimerRunning() {
936        return swingTmrIplQuery.isRunning();
937    }
938
939    public static boolean isValidMfgDevice(int mfg, int deviceType) {
940        return (LnIPLImplementation.interpretHostManufacturerDevice(mfg, deviceType)
941                .compareTo("Unknown Host Manufacturer/Device")
942                != 0);
943    }
944
945    /**
946     * provides string representation for an IPL manufacturer number
947     * @param manuf IPL device manufacturer code number
948     * @return manufacturer name, or null if no known manufacturer name
949     */
950    @CheckForNull
951    public static String getManufacturer(int manuf) {
952        switch (manuf) {
953            case LnConstants.RE_IPL_MFR_DIGITRAX:
954                return LnConstants.DIGITRAX_STRING;
955            case LnConstants.RE_IPL_MFR_RR_CIRKITS:
956                return LnConstants.RR_CIRKITS_STRING;
957            default:
958                return null;
959        }
960    }
961
962    @CheckForNull
963    public static String getDeviceName(int manuf, int device, int slaveManuf, int slave) {
964        if (getManufacturer(manuf) == null) {
965            return null;
966        }
967        for (DeviceTypes t: DeviceTypes.values()) {
968            if ((manuf == t.getManufacturer()) &&
969                    (device == t.getDeviceIdNumber()) &&
970                    (slaveManuf == t.getSlaveManufacturer()) &&
971                    (slave == t.getSlaveDeviceIdNumber())) {
972                return t.getDeviceName();
973            }
974        }
975        return null;
976    }
977
978    public enum DeviceTypes {
979        UT4D(LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_HOST_UT4,
980            LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_SLAVE_RF24,
981            LnConstants.DIGITRAX_STRING, "UT4D"),   // NOI18N
982        UT4X(LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_HOST_UT4,
983            0,0,
984            LnConstants.DIGITRAX_STRING, "UT4(x)"),   // NOI18N
985        DCS51(LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_HOST_DCS51,
986            0,0,LnConstants.DIGITRAX_STRING, "DCS51"),   // NOI18N
987        DCS52(LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_HOST_DCS52,
988            0,0,LnConstants.DIGITRAX_STRING, "DCS52"),   // NOI18N
989        DT402D(LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_HOST_DT402,
990            LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_SLAVE_RF24,
991            LnConstants.DIGITRAX_STRING, "DT402D"),   // NOI18N
992        DT402X(LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_HOST_DT402,
993            0,0, LnConstants.DIGITRAX_STRING, "DT402(x)"),   // NOI18N
994        PR3(LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_HOST_PR3,
995            0,0, LnConstants.DIGITRAX_STRING, "PR3"),   // NOI18N
996        UR92(LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_HOST_UR92,
997            LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_SLAVE_RF24,
998            LnConstants.DIGITRAX_STRING, "UR92"),   // NOI18N
999        DB210OPTO(LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_HOST_DB210OPTO,
1000            0,0, LnConstants.DIGITRAX_STRING, "DB210Opto"),   // NOI18N
1001        DB210(LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_HOST_DB210,
1002            0,0, LnConstants.DIGITRAX_STRING, "DB210"),   // NOI18N
1003        DB220(LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_HOST_DB220,
1004            0,0, LnConstants.DIGITRAX_STRING, "DB220"),   // NOI18N
1005        PR4(LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_HOST_PR4,
1006            0,0, LnConstants.DIGITRAX_STRING, "PR4"),   // NOI18N
1007        BXP88(LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_HOST_BXP88,
1008            0,0, LnConstants.DIGITRAX_STRING, "BXP88"),   // NOI18N
1009        LNWI(LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_HOST_LNWI,
1010            0,0, LnConstants.DIGITRAX_STRING, "LNWI"),   // NOI18N
1011        DCS210(LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_HOST_DCS210,
1012            0,0, LnConstants.DIGITRAX_STRING, "DCS210"),   // NOI18N
1013        DCS240(LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_HOST_DCS240,
1014            0,0, LnConstants.DIGITRAX_STRING, "DCS240"),   // NOI18N
1015        DT500D(LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_HOST_DT500,
1016            LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_SLAVE_RF24,
1017            LnConstants.DIGITRAX_STRING, "DT500D"),   // NOI18N
1018        DT500X(LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_HOST_DT500,
1019            0,0, LnConstants.DIGITRAX_STRING, "DT500(x)"),   // NOI18N
1020        DT602X(LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_HOST_DT602,
1021            0,0, LnConstants.DIGITRAX_STRING, "DT602(x)"),   // NOI18N
1022        BXPA1(LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_HOST_BXPA1,
1023            0,0, LnConstants.DIGITRAX_STRING, "BXPA1"),   // NOI18N
1024        DCS210plus(LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_HOST_DCS210PLUS,
1025            0,0, LnConstants.DIGITRAX_STRING, "DCS210+"),   // NOI18N
1026        DCS240plus(LnConstants.RE_IPL_MFR_DIGITRAX, LnConstants.RE_IPL_DIGITRAX_HOST_DCS240PLUS,
1027                0,0, LnConstants.DIGITRAX_STRING, "DCS240+"),   // NOI18N
1028        RR_CKTS_TC64(LnConstants.RE_IPL_MFR_RR_CIRKITS, LnConstants.RE_IPL_RRCIRKITS_HOST_TC64,
1029            0,0, LnConstants.RR_CIRKITS_STRING, "TC-64"),
1030        RR_CKTS_TC_MKII(LnConstants.RE_IPL_MFR_RR_CIRKITS, LnConstants.RE_IPL_RRCIRKITS_HOST_TC64_MKII,
1031            0,0, LnConstants.RR_CIRKITS_STRING, "TC-64 Mk-II"),
1032        RR_CKTS_LNCP(LnConstants.RE_IPL_MFR_RR_CIRKITS, LnConstants.RE_IPL_RRCIRKITS_HOST_LNCP,
1033            0,0, LnConstants.RR_CIRKITS_STRING, "LNCP"),
1034        RR_CKTS_MOTORMan(LnConstants.RE_IPL_MFR_RR_CIRKITS, LnConstants.RE_IPL_RRCIRKITS_HOST_MOTORMAN,
1035            0,0, LnConstants.RR_CIRKITS_STRING, "MotorMan"),
1036        RR_CKTS_MOTORMANII(LnConstants.RE_IPL_MFR_RR_CIRKITS, LnConstants.RE_IPL_RRCIRKITS_HOST_MOTORMAN_II,
1037            0,0, LnConstants.RR_CIRKITS_STRING, "MotorMan-II"),
1038        RR_CKTS_SIGNALMAN(LnConstants.RE_IPL_MFR_RR_CIRKITS, LnConstants.RE_IPL_RRCIRKITS_HOST_SIGNALMAN,
1039            0,0, LnConstants.RR_CIRKITS_STRING, "SignalMan"),
1040        RR_CKTS_TOWERMAN(LnConstants.RE_IPL_MFR_RR_CIRKITS, LnConstants.RE_IPL_RRCIRKITS_HOST_TOWERMAN,
1041            0,0, LnConstants.RR_CIRKITS_STRING, "TowerMan"),
1042        RR_CKTS_WATCHMAN(LnConstants.RE_IPL_MFR_RR_CIRKITS, LnConstants.RE_IPL_RRCIRKITS_HOST_WATCHMAN,
1043            0,0, LnConstants.RR_CIRKITS_STRING, "WatchMan");
1044
1045        private int manufacturer;
1046        private int deviceIdNumber;
1047        private int slaveManufacturer;
1048        private int slaveDeviceIdNumber;
1049        private String manufacturerName;
1050        private String deviceName;
1051
1052        private DeviceTypes(int mfg, int devId, int slaveMfg, int slaveDevId,
1053                String mfgName, String devName) {
1054            this.manufacturer = mfg & 0x7f;
1055            this.deviceIdNumber = devId & 0x7f;
1056            this.slaveManufacturer = slaveMfg & 0x7f;
1057            this.slaveDeviceIdNumber = slaveDevId & 0x7f;
1058            this.manufacturerName = mfgName;
1059            this.deviceName = devName;
1060        }
1061        public final int getManufacturer() {
1062            return manufacturer;
1063        }
1064        public final int getDeviceIdNumber() {
1065            return deviceIdNumber;
1066        }
1067        public final int getSlaveManufacturer() {
1068            return slaveManufacturer;
1069        }
1070        public final int getSlaveDeviceIdNumber() {
1071            return slaveDeviceIdNumber;
1072        }
1073        public final boolean isDeviceMatch(int mfg, int devId, int slaveMfg, int slaveDevId) {
1074            return (mfg==manufacturer) && (devId == deviceIdNumber) &&
1075                    (slaveMfg == slaveManufacturer) && (slaveDevId == slaveDeviceIdNumber);
1076        }
1077        public final boolean isDeviceMatch(int mfg, int devId) {
1078            return isDeviceMatch(mfg, devId, 0, 0);
1079        }
1080        public final String getManufacturerName() {
1081            return manufacturerName;
1082        }
1083        public final String getDeviceName() {
1084            return deviceName;
1085        }
1086    }
1087
1088    private boolean waitingForIplReply;
1089
1090    private final static Logger log = LoggerFactory.getLogger(LnIPLImplementation.class);
1091}