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    @Deprecated( since="5.13.5", forRemoval=true) // deprecated super
153    public SpecificMessage(byte[] a, int l) {
154        super(a, l);
155    }
156
157    int responseLength = -1;  // -1 is an invalid value, indicating it hasn't been set
158
159    @Override
160    public void setResponseLength(int l) {
161        responseLength = l;
162    }
163
164    @Override
165    public int getResponseLength() {
166        return responseLength;
167    }
168
169    // static methods to recognize a message
170    @Override
171    public boolean isPoll() {
172        return getElement(1) == 48;
173    }
174
175    @Override
176    public boolean isXmt() {
177        return getElement(1) == 17;
178    }
179
180    @Override
181    public int getAddr() {
182        return getElement(0);
183    }
184
185    // static methods to return a formatted message
186    static public SerialMessage getPoll(int addr) {
187        // eventually this will have to include logic for reading
188        // various bytes on the card, but our supported
189        // cards don't require that yet
190        // SerialMessage m = new SerialMessage(1);
191        // m.setResponseLength(2);
192        // m.setElement(0, addr);
193        //  m.setTimeout(SHORT_TIMEOUT);    // minumum reasonable timeout
194
195        // Powerline implementation does not currently poll
196        return null;
197    }
198
199    static public SpecificMessage setCM11Time(int housecode) {
200        SpecificMessage msg = new SpecificMessage(7);
201        msg.setElement(0, 0x9B);
202        msg.setElement(5, 0x01);
203        msg.setElement(6, housecode << 4);
204        return msg;
205    }
206
207    static public SpecificMessage getAddress(int housecode, int devicecode) {
208        SpecificMessage m = new SpecificMessage(2);
209        m.setInterlocked(true);
210        m.setElement(0, 0x04);
211        m.setElement(1, (X10Sequence.encode(housecode) << 4) + X10Sequence.encode(devicecode));
212        return m;
213    }
214
215    static public SpecificMessage getAddressDim(int housecode, int devicecode, int dimcode) {
216        SpecificMessage m = new SpecificMessage(2);
217        m.setInterlocked(true);
218        if (dimcode > 0) {
219            m.setElement(0, 0x04 | ((dimcode & 0x1f) << 3));
220        } else {
221            m.setElement(0, 0x04);
222        }
223        m.setElement(1, (X10Sequence.encode(housecode) << 4) + X10Sequence.encode(devicecode));
224        return m;
225    }
226
227    static public SpecificMessage getFunctionDim(int housecode, int function, int dimcode) {
228        SpecificMessage m = new SpecificMessage(2);
229        m.setInterlocked(true);
230        if (dimcode > 0) {
231            m.setElement(0, 0x06 | ((dimcode & 0x1f) << 3));
232        } else {
233            m.setElement(0, 0x06);
234        }
235        m.setElement(1, (X10Sequence.encode(housecode) << 4) + function);
236        return m;
237    }
238
239    static public SpecificMessage getFunction(int housecode, int function) {
240        SpecificMessage m = new SpecificMessage(2);
241        m.setInterlocked(true);
242        m.setElement(0, 0x06);
243        m.setElement(1, (X10Sequence.encode(housecode) << 4) + function);
244        return m;
245    }
246
247    static public SpecificMessage getExtCmd(int housecode, int devicecode, int function, int dimcode) {
248        SpecificMessage m = new SpecificMessage(5);
249        m.setInterlocked(true);
250        m.setElement(0, 0x07);
251        m.setElement(1, (X10Sequence.encode(housecode) << 4) + X10Sequence.FUNCTION_EXTENDED_CODE);
252        m.setElement(2, X10Sequence.encode(devicecode));
253        m.setElement(3, dimcode);
254        m.setElement(4, function);
255        return m;
256    }
257}
258
259