001package jmri.jmrix.powerline.cp290;
002
003import jmri.jmrix.powerline.SerialMessage;
004import jmri.jmrix.powerline.X10Sequence;
005import jmri.util.StringUtil;
006
007/**
008 * Contains the data payload of a serial packet.
009 * <p>
010 * The transmission protocol can come in one of several forms:
011 * <ul>
012 * <li>If the interlocked parameter is false (default), the packet is just sent.
013 * If the response length is not zero, a reply of that length is expected.
014 * <li>If the interlocked parameter is true, the transmission will require a CRC
015 * interlock, which will be automatically added. (Design note: this is done to
016 * make sure that the messages remain atomic)
017 * </ul>
018 *
019 * @author Bob Jacobsen Copyright (C) 2001,2003, 2006, 2007, 2008
020 */
021public class SpecificMessage extends SerialMessage {
022    // is this logically an abstract class?
023
024    public SpecificMessage(int l) {
025        super(l);
026        setResponseLength(0);  // only polls require a response
027        setBinary(true);
028        setTimeout(5000);
029    }
030
031    /**
032     * This ctor interprets the String as the exact sequence to send,
033     * byte-for-byte.
034     *
035     * @param m message
036     * @param l response length in bytes
037     */
038    public SpecificMessage(String m, int l) {
039        super(m, l);
040    }
041
042    /**
043     * This ctor interprets the byte array as a sequence of characters to send.
044     * @deprecated 5.13.5, unused, requires further development.
045     * @param a Array of bytes to send
046     * @param l length of expected reply
047     */
048    @Deprecated( since="5.13.5", forRemoval=true)
049    public SpecificMessage(byte[] a, int l) {
050        super(StringUtil.hexStringFromBytes(a).replaceAll("\\s", ""), l);
051    }
052
053    /**
054     * Find 1st byte that's not 0xFF, or -1 if none
055     * @return -1 or index of first valid byte
056     */
057    int startIndex() {
058        int len = getNumDataElements();
059        for (int i = 0; i < len; i++) {
060            if ((getElement(i) & 0xFF) != 0xFF) {
061                return i;
062            }
063        }
064        return -1;
065    }
066
067    /**
068     * Translate packet to text
069     */
070    @Override
071    public String toMonitorString() {
072        String test = Constants.toMonitorString(this);
073//        // check for valid length
074//     String val = "???";
075//     int len = getNumDataElements();
076//     boolean goodSync = true;
077//     boolean goodCheckSum = true;
078//     int sum = 0;
079//     String cmd;
080//     String stat;
081//     String hCode;
082//     String bCode;
083//     String dev;
084//        switch (len) {
085//        case 7:
086//         for (int i = 0; i < 6; i++) {
087//          if ((getElement(i) & 0xFF) != 0xFF) {
088//           goodSync = false;
089//          }
090//         }
091//         val = Constants.statusToText(getElement(6));
092//         break;
093//        case 12:
094//         for (int i = 0; i < 6; i++) {
095//          if ((getElement(i) & 0xFF) != 0xFF) {
096//           goodSync = false;
097//          }
098//         }
099//         for (int i = 7; i < 12; i++) {
100//          sum = (sum + (getElement(i) &0xFF)) & 0xFF;
101//         }
102//         stat = Constants.statusToText(getElement(6));
103//         cmd = Constants.commandToText(getElement(7) & 0x0F, -1);
104//         hCode = Constants.houseCodeToText((getElement(7) >> 4) & 0x0F);
105//         dev = Constants.deviceToText(getElement(8), getElement(9));
106//         bCode = Constants.houseCodeToText((getElement(10) >> 4) & 0x0F);
107//         if (sum != (getElement(12) & 0xFF)) {
108//          goodCheckSum = false;
109//         }
110//         val = "Cmd Echo: " + cmd + " stat: " + stat + " House: " + hCode + " Device:" + dev + " base: " + bCode;
111//         if (!goodSync) {
112//          val = val + " BAD SYNC";
113//         }
114//         if (!goodCheckSum) {
115//          val = val + " BAD CHECKSUM: " + (getElement(11) & 0xFF) + " vs " + sum;
116//         }
117//         break;
118//        case 22:
119//         for (int i = 0; i < 16; i++) {
120//          if ((getElement(i) & 0xFF) != 0xFF) {
121//           goodSync = false;
122//          }
123//         }
124//         for (int i = 17; i < 21; i++) {
125//          sum = (sum + (getElement(i) &0xFF)) & 0xFF;
126//         }
127//         cmd = Constants.commandToText((getElement(17) & 0x0F), ((getElement(17) & 0xF0) >> 4));
128//         hCode = Constants.houseCodeToText((getElement(18) >> 4) & 0x0F);
129//         dev = Constants.deviceToText(getElement(19), getElement(20));
130//         if (sum != (getElement(21) & 0xFF)) {
131//          goodCheckSum = false;
132//         }
133//         val = cmd + " House: " + hCode + " Device:" + dev;
134//         if (!goodSync) {
135//          val = val + " BAD SYNC";
136//         }
137//         if (!goodCheckSum) {
138//          val = val + " BAD CHECKSUM: " + (getElement(21) & 0xFF) + " vs " + sum;
139//         }
140//         break;
141//        default:
142//         val = "UNK " + toString();
143//         break;
144//        }
145        return "Send[" + getNumDataElements() + "]: " + test + "\n";
146    }
147
148    int responseLength = -1;  // -1 is an invalid value, indicating it hasn't been set
149
150    @Override
151    public void setResponseLength(int l) {
152        responseLength = l;
153    }
154
155    @Override
156    public int getResponseLength() {
157        return responseLength;
158    }
159
160    // static methods to recognize a message
161    @Override
162    public boolean isPoll() {
163        return getElement(1) == 48;
164    }
165
166    @Override
167    public boolean isXmt() {
168        return getElement(1) == 17;
169    }
170
171    @Override
172    public int getAddr() {
173        return getElement(0);
174    }
175
176    // static methods to return a formatted message
177    static public SerialMessage getPoll(int addr) {
178        // eventually this will have to include logic for reading
179        // various bytes on the card, but our supported
180        // cards don't require that yet
181        // SerialMessage m = new SerialMessage(1);
182        // m.setResponseLength(2);
183        // m.setElement(0, addr);
184        //  m.setTimeout(SHORT_TIMEOUT);    // minumum reasonable timeout
185
186        // Powerline implementation does not currently poll
187        return null;
188    }
189
190    static public SpecificMessage getAddress(int housecode, int devicecode) {
191        SpecificMessage m = new SpecificMessage(2);
192        m.setElement(0, 0x04);
193        m.setElement(1, (X10Sequence.encode(housecode) << 4) + X10Sequence.encode(devicecode));
194        return m;
195    }
196
197    static public SpecificMessage getAddressDim(int housecode, int devicecode, int dimcode) {
198        SpecificMessage m = new SpecificMessage(2);
199        if (dimcode > 0) {
200            m.setElement(0, 0x04 | ((dimcode & 0x1f) << 3));
201        } else {
202            m.setElement(0, 0x04);
203        }
204        m.setElement(1, (X10Sequence.encode(housecode) << 4) + X10Sequence.encode(devicecode));
205        return m;
206    }
207
208    static public SpecificMessage getFunctionDim(int housecode, int function, int dimcode) {
209        SpecificMessage m = new SpecificMessage(2);
210        if (dimcode > 0) {
211            m.setElement(0, 0x06 | ((dimcode & 0x1f) << 3));
212        } else {
213            m.setElement(0, 0x06);
214        }
215        m.setElement(1, (X10Sequence.encode(housecode) << 4) + function);
216        return m;
217    }
218
219    static public SpecificMessage getFunction(int housecode, int function) {
220        SpecificMessage m = new SpecificMessage(2);
221        m.setElement(0, 0x06);
222        m.setElement(1, (X10Sequence.encode(housecode) << 4) + function);
223        return m;
224    }
225}
226
227