001package jmri.jmrix.powerline.simulator;
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, 2009
020 * @author Ken Cameron Copyright (C) 2010
021 */
022public class SpecificMessage extends SerialMessage {
023    // is this logically an abstract class?
024
025    public SpecificMessage(int l) {
026        super(l);
027        setResponseLength(0);  // only polls require a response
028        setBinary(true);
029        setTimeout(5000);
030    }
031
032    /**
033     * This ctor interprets the String as the exact sequence to send,
034     * byte-for-byte.
035     *
036     * @param m message
037     * @param l response length in bytes
038     */
039    public SpecificMessage(String m, int l) {
040        super(m, l);
041    }
042
043    boolean interlocked = false;
044
045    @Override
046    public void setInterlocked(boolean v) {
047        interlocked = v;
048    }
049
050    @Override
051    public boolean getInterlocked() {
052        return interlocked;
053    }
054
055    @Override
056    public String toMonitorString() {
057        // check for valid length
058        int len = getNumDataElements();
059        StringBuilder text = new StringBuilder();
060        if ((getElement(0) & 0xFF) != Constants.HEAD_STX) {
061            text.append("INVALID HEADER: " + String.format("0x%1X", getElement(0) & 0xFF));
062            text.append(" len: " + len);
063        } else {
064            switch (getElement(1) & 0xFF) {
065                case Constants.FUNCTION_REQ_STD:
066                    text.append("Send Cmd ");
067                    if (len == 8 || len == 22) {
068                        if ((getElement(5) & Constants.FLAG_BIT_STDEXT) == Constants.FLAG_STD) {
069                            text.append(" Std");
070                        } else if (len == 22) {
071                            text.append(" Ext");
072                        }
073                        text.append(" addr " + String.format("%1$X.%2$X.%3$X", (getElement(2) & 0xFF), (getElement(3) & 0xFF), (getElement(4) & 0xFF)));
074                        switch (getElement(6) & 0xFF) {
075                            case Constants.CMD_LIGHT_ON_RAMP:
076                                text.append(" ON RAMP ");
077                                text.append((getElement(7) & 0xFF) / 256.0);
078                                break;
079                            case Constants.CMD_LIGHT_ON_FAST:
080                                text.append(" ON FAST ");
081                                text.append((getElement(7) & 0xFF) / 256.0);
082                                break;
083                            case Constants.CMD_LIGHT_OFF_FAST:
084                                text.append(" OFF FAST ");
085                                text.append((getElement(7) & 0xFF) / 256.0);
086                                break;
087                            case Constants.CMD_LIGHT_OFF_RAMP:
088                                text.append(" OFF ");
089                                text.append((getElement(7) & 0xFF) / 256.0);
090                                break;
091                            case Constants.CMD_LIGHT_CHG:
092                                text.append(" CHG ");
093                                text.append((getElement(7) & 0xFF) / 256.0);
094                                break;
095                            default:
096                                text.append(" Unknown cmd: " + StringUtil.twoHexFromInt(getElement(6) & 0xFF));
097                                break;
098                        }
099                    } else {
100                        text.append(" !! Length wrong: " + len);
101                    }
102                    break;
103                // i wrote this then figured the POLL are replies
104//             case Constants.POLL_REQ_BUTTON :
105//              text.append("Poll Button ");
106//              int button = ((getElement(2) & Constants.BUTTON_BITS_ID) >> 4) + 1;
107//              text.append(button);
108//              int op = getElement(2) & Constants.BUTTON_BITS_OP;
109//              if (op == Constants.BUTTON_HELD) {
110//               text.append(" HELD");
111//              } else if (op == Constants.BUTTON_REL) {
112//               text.append(" RELEASED");
113//              } else if (op == Constants.BUTTON_TAP) {
114//               text.append(" TAP");
115//              }
116//              break;
117//             case Constants.POLL_REQ_BUTTON_RESET :
118//              text.append("Reset by Button at Power Cycle");
119//              break;
120                case Constants.FUNCTION_REQ_X10:
121                    text.append("Send Cmd X10 ");
122                    if ((getElement(3) & Constants.FLAG_BIT_X10_CMDUNIT) == Constants.FLAG_X10_RECV_CMD) {
123                        text.append(X10Sequence.formatCommandByte(getElement(2) & 0xFF));
124                    } else {
125                        text.append(X10Sequence.formatAddressByte(getElement(2) & 0xFF));
126                    }
127                    break;
128//             case Constants.POLL_REQ_X10 :
129//              text.append("Poll Cmd X10 ");
130//                    if ((getElement(3)& Constants.FLAG_BIT_X10_CMDUNIT) == Constants.FLAG_X10_RECV_CMD) {
131//                     text.append(X10Sequence.formatCommandByte(getElement(2) & 0xFF));
132//                    } else {
133//                     text.append(X10Sequence.formatAddressByte(getElement(2)& 0xFF));
134//                    }
135//              break;
136                default: {
137                    text.append(" Unknown command: " + StringUtil.twoHexFromInt(getElement(1) & 0xFF));
138                    text.append(" len: " + len);
139                }
140            }
141        }
142        return text + "\n";
143    }
144
145    /**
146     * This ctor interprets the byte array as a sequence of characters to send.
147     *
148     * @param a Array of bytes to send
149     * @param l length expected reply
150     */
151    public SpecificMessage(byte[] a, int l) {
152        super(a, l);
153    }
154
155    int responseLength = -1;  // -1 is an invalid value, indicating it hasn't been set
156
157    @Override
158    public void setResponseLength(int l) {
159        responseLength = l;
160    }
161
162    @Override
163    public int getResponseLength() {
164        return responseLength;
165    }
166
167    // static methods to recognize a message
168//    public boolean isPoll() { return getElement(1)==48;}
169//    public boolean isXmt()  { return getElement(1)==17;}
170//    public int getAddr() { return getElement(0); }
171    // static methods to return a formatted message
172    static public SerialMessage getPoll(int addr) {
173        // Powerline implementation does not currently poll
174        return null;
175    }
176
177    /**
178     * create an Insteon message with the X10 address
179     * @param housecode  value of the housecode of X10 address
180     * @param devicecode value of the devicecode of X10 address
181     *
182     * @return message   formated message with parameters
183     */
184    static public SpecificMessage getX10Address(int housecode, int devicecode) {
185        SpecificMessage m = new SpecificMessage(4);
186        m.setInterlocked(false);
187        m.setElement(0, Constants.HEAD_STX);
188        m.setElement(1, Constants.FUNCTION_REQ_X10);
189        m.setElement(2, (X10Sequence.encode(housecode) << 4) + X10Sequence.encode(devicecode));
190        m.setElement(3, 0x00);  //  0x00 Means address
191        return m;
192    }
193
194    /**
195     * create an Insteon message with the X10 address and dim steps
196     *
197     * @param housecode  value of the housecode of X10 address
198     * @param devicecode value of the devicecode of X10 address
199     * @param dimcode    value of how dim to set the light
200     *
201     * @return message   formated message with parameters
202     */
203    static public SpecificMessage getX10AddressDim(int housecode, int devicecode, int dimcode) {
204        SpecificMessage m = new SpecificMessage(4);
205        m.setInterlocked(false);
206        m.setElement(0, Constants.HEAD_STX);
207        m.setElement(1, Constants.FUNCTION_REQ_X10);
208        if (dimcode > 0) {
209            m.setElement(2, 0x04 | ((dimcode & 0x1f) << 3));
210        } else {
211            m.setElement(2, 0x04);
212        }
213        m.setElement(3, (X10Sequence.encode(housecode) << 4) + X10Sequence.encode(devicecode));
214        m.setElement(3, 0x80);  //  0x00 Means address
215        return m;
216    }
217
218    static public SpecificMessage getX10FunctionDim(int housecode, int function, int dimcode) {
219        SpecificMessage m = new SpecificMessage(2);
220        m.setInterlocked(true);
221        if (dimcode > 0) {
222            m.setElement(0, 0x06 | ((dimcode & 0x1f) << 3));
223        } else {
224            m.setElement(0, 0x06);
225        }
226        m.setElement(1, (X10Sequence.encode(housecode) << 4) + function);
227        return m;
228    }
229
230    static public SpecificMessage getX10Function(int housecode, int function) {
231        SpecificMessage m = new SpecificMessage(4);
232//        m.setInterlocked(true);
233        m.setInterlocked(false);
234        m.setElement(0, Constants.HEAD_STX);
235        m.setElement(1, Constants.FUNCTION_REQ_X10);
236        m.setElement(2, (X10Sequence.encode(housecode) << 4) + function);
237        m.setElement(3, 0x80);  //  0x80 means function
238        return m;
239    }
240
241    static public SpecificMessage getExtCmd(int housecode, int devicecode, int function, int dimcode) {
242        SpecificMessage m = new SpecificMessage(5);
243        m.setInterlocked(true);
244        m.setElement(0, 0x07);
245        m.setElement(1, (X10Sequence.encode(housecode) << 4) + X10Sequence.FUNCTION_EXTENDED_CODE);
246        m.setElement(2, X10Sequence.encode(devicecode));
247        m.setElement(3, dimcode);
248        m.setElement(4, function);
249        return m;
250    }
251
252    static public SpecificMessage getInsteonAddress(int idhighbyte, int idmiddlebyte, int idlowbyte) {
253        SpecificMessage m = new SpecificMessage(8);
254//        m.setInterlocked(true);
255        m.setInterlocked(false);
256        m.setElement(0, Constants.HEAD_STX);
257        m.setElement(1, Constants.FUNCTION_REQ_STD);
258        m.setElement(2, idhighbyte);
259        m.setElement(3, idmiddlebyte);
260        m.setElement(4, idlowbyte);
261        m.setElement(5, 0x0F);
262        m.setElement(6, 0x11);
263        m.setElement(7, 0xFF);
264        return m;
265    }
266
267    static public SpecificMessage getInsteonFunction(int idhighbyte, int idmiddlebyte, int idlowbyte, int function, int flag, int cmd1, int cmd2) {
268        SpecificMessage m = new SpecificMessage(8);
269//        m.setInterlocked(true);
270        m.setInterlocked(false);
271        m.setElement(0, Constants.HEAD_STX);
272        m.setElement(1, Constants.FUNCTION_REQ_STD);
273        m.setElement(2, idhighbyte);
274        m.setElement(3, idmiddlebyte);
275        m.setElement(4, idlowbyte);
276        m.setElement(5, flag);
277        m.setElement(6, cmd1);
278        m.setElement(7, cmd2);
279        return m;
280    }
281
282}