001package jmri.jmrix.powerline.cm11;
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    boolean interlocked = false;
043
044    @Override
045    public void setInterlocked(boolean v) {
046        interlocked = v;
047    }
048
049    @Override
050    public boolean getInterlocked() {
051        return interlocked;
052    }
053
054    @SuppressWarnings("fallthrough")
055    @Override
056    public String toMonitorString() {
057        // check for valid length
058        int len = getNumDataElements();
059        StringBuilder text = new StringBuilder();
060        switch (getElement(0) & 0xFF) {
061            case Constants.MACRO_LOAD:
062                text.append("Macro load reply");
063                break;
064            case Constants.MACRO_INITIATED:
065                text.append("Macro Poll");
066                break;
067            case Constants.TIME_REQ_CP11:
068                text.append("Power Fail Poll");
069                break;
070            case Constants.TIMER_DOWNLOAD:
071                text.append("Set CM11 time");
072                break;
073            case Constants.EXT_CMD_HEADER: // extended command
074                text.append("Extended Cmd");
075                if (len == 5) {
076                    text.append(" house ");
077                    text.append(X10Sequence.houseValueToText(X10Sequence.decode((getElement(1) >> 4) & 0x0F)));
078                    text.append(" address device ");
079                    text.append(X10Sequence.decode(getElement(2) & 0x0F));
080                    int d = getElement(3) & 0xFF;
081                    switch (getElement(4) & 0xFF) {
082                        case X10Sequence.EXTCMD_DIM:
083                            text.append(" Direct Dim: ");
084                            if ((d & 0x3F) <= 0x3E) {
085                                text.append(((d & 0x3F) / 0.63) + "%");
086                            } else if (d == 0x3F) {
087                                text.append("Full On");
088                            } else {
089                                text.append(" data: 0x");
090                                text.append(StringUtil.twoHexFromInt(d));
091                            }
092//               switch ((d >> 6) & 0x03) {
093//               case 0:
094//                text.append(" 3.7 Sec");
095//                break;
096//               case 1:
097//                text.append(" 30 Sec");
098//                break;
099//               case 2:
100//                text.append(" 1 Min");
101//                break;
102//               case 3:
103//                text.append(" 5 Min");
104//                break;
105//               }
106                            break;
107                        default:
108                            text.append(" cmd: 0x");
109                            text.append(StringUtil.twoHexFromInt(getElement(4) & 0xFF));
110                            text.append(" data: 0x");
111                            text.append(StringUtil.twoHexFromInt(getElement(3) & 0xFF));
112                    }
113                } else {
114                    text.append(" wrong length: " + len);
115                }
116                break;
117            case Constants.POLL_ACK:
118                if (len == 1) {
119                    text.append("Poll Ack");
120                    break;
121                } // else fall through
122            case Constants.CHECKSUM_OK:
123                if (len == 1) {
124                    text.append("OK for transmission");
125                    break;
126                } // else fall through
127            default: {
128                if (len == 2) {
129                    text.append(Constants.formatHeaderByte(getElement(0 & 0xFF)));
130                    if ((getElement(0) & 0x02) == 0x02) {
131                        text.append(" ");
132                        text.append(X10Sequence.formatCommandByte(getElement(1) & 0xFF));
133                    } else {
134                        text.append(" ");
135                        text.append(X10Sequence.formatAddressByte(getElement(1) & 0xFF));
136                    }
137                } else {
138                    text.append("Reply was not expected, len: " + len);
139                    text.append(" value: " + Constants.formatHeaderByte(getElement(0 & 0xFF)));
140                }
141            }
142        }
143        return text + "\n";
144    }
145
146    /**
147     * This ctor interprets the byte array as a sequence of characters to send.
148     *
149     * @param a Array of bytes to send
150     * @param l lenght of expected reply
151     */
152    public SpecificMessage(byte[] a, int l) {
153        super(a, l);
154    }
155
156    int responseLength = -1;  // -1 is an invalid value, indicating it hasn't been set
157
158    @Override
159    public void setResponseLength(int l) {
160        responseLength = l;
161    }
162
163    @Override
164    public int getResponseLength() {
165        return responseLength;
166    }
167
168    // static methods to recognize a message
169    @Override
170    public boolean isPoll() {
171        return getElement(1) == 48;
172    }
173
174    @Override
175    public boolean isXmt() {
176        return getElement(1) == 17;
177    }
178
179    @Override
180    public int getAddr() {
181        return getElement(0);
182    }
183
184    // static methods to return a formatted message
185    static public SerialMessage getPoll(int addr) {
186        // eventually this will have to include logic for reading
187        // various bytes on the card, but our supported
188        // cards don't require that yet
189        // SerialMessage m = new SerialMessage(1);
190        // m.setResponseLength(2);
191        // m.setElement(0, addr);
192        //  m.setTimeout(SHORT_TIMEOUT);    // minumum reasonable timeout
193
194        // Powerline implementation does not currently poll
195        return null;
196    }
197
198    static public SpecificMessage setCM11Time(int housecode) {
199        SpecificMessage msg = new SpecificMessage(7);
200        msg.setElement(0, 0x9B);
201        msg.setElement(5, 0x01);
202        msg.setElement(6, housecode << 4);
203        return msg;
204    }
205
206    static public SpecificMessage getAddress(int housecode, int devicecode) {
207        SpecificMessage m = new SpecificMessage(2);
208        m.setInterlocked(true);
209        m.setElement(0, 0x04);
210        m.setElement(1, (X10Sequence.encode(housecode) << 4) + X10Sequence.encode(devicecode));
211        return m;
212    }
213
214    static public SpecificMessage getAddressDim(int housecode, int devicecode, int dimcode) {
215        SpecificMessage m = new SpecificMessage(2);
216        m.setInterlocked(true);
217        if (dimcode > 0) {
218            m.setElement(0, 0x04 | ((dimcode & 0x1f) << 3));
219        } else {
220            m.setElement(0, 0x04);
221        }
222        m.setElement(1, (X10Sequence.encode(housecode) << 4) + X10Sequence.encode(devicecode));
223        return m;
224    }
225
226    static public SpecificMessage getFunctionDim(int housecode, int function, int dimcode) {
227        SpecificMessage m = new SpecificMessage(2);
228        m.setInterlocked(true);
229        if (dimcode > 0) {
230            m.setElement(0, 0x06 | ((dimcode & 0x1f) << 3));
231        } else {
232            m.setElement(0, 0x06);
233        }
234        m.setElement(1, (X10Sequence.encode(housecode) << 4) + function);
235        return m;
236    }
237
238    static public SpecificMessage getFunction(int housecode, int function) {
239        SpecificMessage m = new SpecificMessage(2);
240        m.setInterlocked(true);
241        m.setElement(0, 0x06);
242        m.setElement(1, (X10Sequence.encode(housecode) << 4) + function);
243        return m;
244    }
245
246    static public SpecificMessage getExtCmd(int housecode, int devicecode, int function, int dimcode) {
247        SpecificMessage m = new SpecificMessage(5);
248        m.setInterlocked(true);
249        m.setElement(0, 0x07);
250        m.setElement(1, (X10Sequence.encode(housecode) << 4) + X10Sequence.FUNCTION_EXTENDED_CODE);
251        m.setElement(2, X10Sequence.encode(devicecode));
252        m.setElement(3, dimcode);
253        m.setElement(4, function);
254        return m;
255    }
256}
257
258