001package jmri.jmrix.loconet.locoio;
002
003import jmri.jmrix.loconet.LnTrafficController;
004import jmri.jmrix.loconet.LocoNetMessage;
005
006/**
007 * Manage the communication to/from a LocoIO board.
008 * 
009 * Uses the LOCONETSV1MODE programming mode.
010 *
011 * Uses LnProgrammer LOCOIO_PEER_CODE_SV_VER1 message format, comparable to DecoderPro LOCONETSV1MODE
012 * Currently (4.11.6) this tool does not work with the HDL LocoIO rev 3 and newer boards,
013 * with risk of breaking the stored config.
014 *
015 * @see jmri.jmrix.loconet.LnOpsModeProgrammer#message(LocoNetMessage)
016 *
017 * Programming SV's
018 * <p>
019 * The SV's in a LocoIO hardware module can be programmed using LocoNet OPC_PEER_XFER messages.
020 * <p>
021 * Commands for setting SV's:
022 * <p>
023 * PC to LocoIO LocoNet message (OPC_PEER_XFER)
024 * <pre><code>
025 * Code LOCOIO_SV_READ _or_ LOCOIO_SV_WRITE ----
026 * 0xE5 OPC_PEER_XFER
027 * OPC_PEER_XFER 0x10 Message length
028 * SRCL 0x50 0x50 // low address byte of LocoBuffer
029 * DSTL LocoIO low address
030 * DSTH 0x01 0x01 // Fixed LocoIO high address PXCT1
031 * D1 LOCOIO_SV_READ _or_ LOCOIO_SV_WRITE // Read/Write command
032 * D2 SV number
033 * D3 0x00 0x00
034 * D4 0x00 Data to Write PXCT2
035 * D5 LocoIO Sub-address
036 * D6 0x00 0x00
037 * D7 0x00 0x00
038 * D8 0x00 0x00
039 * CHK Checksum Checksum
040 * </code></pre>
041 *
042 * LocoIO to PC reply message (OPC_PEER_XFER)
043 * <pre><code>
044 * Code LOCOIO_SV_READ _or_ LOCOIO_SV_WRITE ----
045 * 0xE5 OPC_PEER_XFER
046 * 0x10 Message length
047 * SRCL LocoIO low address
048 * DSTL 0x50 0x50 // low address byte of LocoBuffer
049 * DSTH 0x01 0x01 // high address byte of LocoBuffer
050 * PXCT1 MSB LocoIO version // High order bit of LocoIO version
051 * D1 LOCOIO_SV_READ _or_ LOCOIO_SV_WRITE // Original Command
052 * D2 SV number requested
053 * D3 LSBs LocoIO version // Lower 7 bits of LocoIO version
054 * D4 0x00 0x00 PXCT2 MSB Requested Data // High order bit of requested data
055 * D5 LocoIO Sub-address
056 * D6 Requested Data 0x00
057 * D7 Requested Data + 1 0x00
058 * D8 Requested Data + 2 Written Data
059 * CHK Checksum Checksum
060 * </code></pre>
061 *
062 * @author John Plocher 2006, 2007
063 */
064public class LocoIO {
065
066    public static final int LOCOIO_SV_WRITE = 0x01;
067    public static final int LOCOIO_SV_READ = 0x02;
068    public static final int LOCOIO_BROADCAST_ADDRESS = 0x1000; // LocoIO broadcast
069
070    public static final int LOCOIO_PEER_CODE_7BIT_ADDRS = 0x00;
071    public static final int LOCOIO_PEER_CODE_ANSI_TEXT = 0x00; // not used
072    public static final int LOCOIO_PEER_CODE_SV_VER1 = 0x08;
073    public static final int LOCOIO_PEER_CODE_SV_VER2 = 0x09; // not used
074
075    /**
076     * Create a new instance of LocoIO.
077     */
078    public LocoIO() {
079    }
080
081    public static int SENSOR_ADR(int a1, int a2) {
082        return (((a2 & 0x0f) * 128) + (a1 & 0x7f)) + 1;
083    }
084
085    /**
086     * Compose a LocoNet message from the given ingredients for reading
087     * the value of one specific SV from a given LocoIO.
088     *
089     * @param locoIOAddress base address of the LocoIO board to read from
090     * @param locoIOSubAddress subAddress of the LocoIO board
091     * @param cv the SV index to query
092     * @return complete message to send
093     */
094    public static LocoNetMessage readCV(int locoIOAddress, int locoIOSubAddress, int cv) {
095        int[] contents = {LOCOIO_SV_READ, cv, 0, 0, locoIOSubAddress, 0, 0, 0};
096
097        return LocoNetMessage.makePeerXfr(
098                0x1050, // B'cast locobuffer address
099                locoIOAddress,
100                contents, // CV and SubAddr to read
101                LOCOIO_PEER_CODE_SV_VER1
102        );
103    }
104
105    /**
106     * Compose a LocoNet message from the given ingredients for reading
107     * the value of one specific SV from a given LocoIO.
108     *
109     * @param locoIOAddress base address of the LocoIO board to read from
110     * @param locoIOSubAddress subAddress of the LocoIO board
111     * @param cv the SV index to change
112     * @param data the new value to store in the board's SV
113     * @return complete message to send
114     */
115    public static LocoNetMessage writeCV(int locoIOAddress, int locoIOSubAddress, int cv, int data) {
116        int[] contents = {LOCOIO_SV_WRITE, cv, 0, data, locoIOSubAddress, 0, 0, 0};
117
118        return LocoNetMessage.makePeerXfr(
119                0x1050, // B'cast locobuffer address
120                locoIOAddress,
121                contents, // CV and SubAddr to read
122                LOCOIO_PEER_CODE_SV_VER1
123        );
124    }
125
126    /**
127     * Compose and send a message out onto LocoNet changing the LocoIO hardware board
128     * address of all connected LocoIO boards.
129     * <p>
130     * User is warned that this is a broadcast type operation.
131     *
132     * @param address the new base address of the LocoIO board to change
133     * @param subAddress the new subAddress of the board
134     * @param ln the TrafficController to use for sending the message
135     */
136    public static void programLocoIOAddress(int address, int subAddress, LnTrafficController ln) {
137        LocoNetMessage msg;
138        msg = LocoIO.writeCV(0x0100, 0, 1, address & 0xFF);
139        ln.sendLocoNetMessage(msg);
140        if (subAddress != 0) {
141            msg = LocoIO.writeCV(0x0100, 0, 2, subAddress);
142            ln.sendLocoNetMessage(msg);
143        }
144    }
145
146    /**
147     * Send out a probe of all connected LocoIO units on a given LocoNet connection.
148     *
149     * @param ln the TrafficController to use for sending the message
150     */
151    public static void probeLocoIOs(LnTrafficController ln) {
152        LocoNetMessage msg;
153        msg = LocoIO.readCV(0x0100, 0, 2);
154        ln.sendLocoNetMessage(msg);
155    }
156
157}