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: ").append( String.format("0x%1X", getElement(0) & 0xFF));
062            text.append(" len: ").append(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 ").append(String.format("%1$X.%2$X.%3$X",
074                            (getElement(2) & 0xFF), (getElement(3) & 0xFF), (getElement(4) & 0xFF)));
075                        switch (getElement(6) & 0xFF) {
076                            case Constants.CMD_LIGHT_ON_RAMP:
077                                text.append(" ON RAMP ");
078                                text.append((getElement(7) & 0xFF) / 256.0);
079                                break;
080                            case Constants.CMD_LIGHT_ON_FAST:
081                                text.append(" ON FAST ");
082                                text.append((getElement(7) & 0xFF) / 256.0);
083                                break;
084                            case Constants.CMD_LIGHT_OFF_FAST:
085                                text.append(" OFF FAST ");
086                                text.append((getElement(7) & 0xFF) / 256.0);
087                                break;
088                            case Constants.CMD_LIGHT_OFF_RAMP:
089                                text.append(" OFF ");
090                                text.append((getElement(7) & 0xFF) / 256.0);
091                                break;
092                            case Constants.CMD_LIGHT_CHG:
093                                text.append(" CHG ");
094                                text.append((getElement(7) & 0xFF) / 256.0);
095                                break;
096                            default:
097                                text.append(" Unknown cmd: ").append(StringUtil.twoHexFromInt(getElement(6) & 0xFF));
098                                break;
099                        }
100                    } else {
101                        text.append(" !! Length wrong: ").append(len);
102                    }
103                    break;
104                // i wrote this then figured the POLL are replies
105//             case Constants.POLL_REQ_BUTTON :
106//              text.append("Poll Button ");
107//              int button = ((getElement(2) & Constants.BUTTON_BITS_ID) >> 4) + 1;
108//              text.append(button);
109//              int op = getElement(2) & Constants.BUTTON_BITS_OP;
110//              if (op == Constants.BUTTON_HELD) {
111//               text.append(" HELD");
112//              } else if (op == Constants.BUTTON_REL) {
113//               text.append(" RELEASED");
114//              } else if (op == Constants.BUTTON_TAP) {
115//               text.append(" TAP");
116//              }
117//              break;
118//             case Constants.POLL_REQ_BUTTON_RESET :
119//              text.append("Reset by Button at Power Cycle");
120//              break;
121                case Constants.FUNCTION_REQ_X10:
122                    text.append("Send Cmd X10 ");
123                    if ((getElement(3) & Constants.FLAG_BIT_X10_CMDUNIT) == Constants.FLAG_X10_RECV_CMD) {
124                        text.append(X10Sequence.formatCommandByte(getElement(2) & 0xFF));
125                    } else {
126                        text.append(X10Sequence.formatAddressByte(getElement(2) & 0xFF));
127                    }
128                    break;
129//             case Constants.POLL_REQ_X10 :
130//              text.append("Poll Cmd X10 ");
131//                    if ((getElement(3)& Constants.FLAG_BIT_X10_CMDUNIT) == Constants.FLAG_X10_RECV_CMD) {
132//                     text.append(X10Sequence.formatCommandByte(getElement(2) & 0xFF));
133//                    } else {
134//                     text.append(X10Sequence.formatAddressByte(getElement(2)& 0xFF));
135//                    }
136//              break;
137                default: {
138                    text.append(" Unknown command: ").append(StringUtil.twoHexFromInt(getElement(1) & 0xFF));
139                    text.append(" len: ").append(len);
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 length 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//    public boolean isPoll() { return getElement(1)==48;}
170//    public boolean isXmt()  { return getElement(1)==17;}
171//    public int getAddr() { return getElement(0); }
172    // static methods to return a formatted message
173    public static SerialMessage getPoll(int addr) {
174        // Powerline implementation does not currently poll
175        return null;
176    }
177
178    /**
179     * create an Insteon message with the X10 address
180     * @param housecode  value of the housecode of X10 address
181     * @param devicecode value of the devicecode of X10 address
182     *
183     * @return message   formated message with parameters
184     */
185    public static SpecificMessage getX10Address(int housecode, int devicecode) {
186        SpecificMessage m = new SpecificMessage(4);
187        m.setInterlocked(false);
188        m.setElement(0, Constants.HEAD_STX);
189        m.setElement(1, Constants.FUNCTION_REQ_X10);
190        m.setElement(2, (X10Sequence.encode(housecode) << 4) + X10Sequence.encode(devicecode));
191        m.setElement(3, 0x00);  //  0x00 Means address
192        return m;
193    }
194
195    /**
196     * create an Insteon message with the X10 address and dim steps
197     *
198     * @param housecode  value of the housecode of X10 address
199     * @param devicecode value of the devicecode of X10 address
200     * @param dimcode    value of how dim to set the light
201     *
202     * @return message   formated message with parameters
203     */
204    public static SpecificMessage getX10AddressDim(int housecode, int devicecode, int dimcode) {
205        SpecificMessage m = new SpecificMessage(4);
206        m.setInterlocked(false);
207        m.setElement(0, Constants.HEAD_STX);
208        m.setElement(1, Constants.FUNCTION_REQ_X10);
209        if (dimcode > 0) {
210            m.setElement(2, 0x04 | ((dimcode & 0x1f) << 3));
211        } else {
212            m.setElement(2, 0x04);
213        }
214        m.setElement(3, (X10Sequence.encode(housecode) << 4) + X10Sequence.encode(devicecode));
215        m.setElement(3, 0x80);  //  0x00 Means address
216        return m;
217    }
218
219    public static SpecificMessage getX10FunctionDim(int housecode, int function, int dimcode) {
220        SpecificMessage m = new SpecificMessage(2);
221        m.setInterlocked(true);
222        if (dimcode > 0) {
223            m.setElement(0, 0x06 | ((dimcode & 0x1f) << 3));
224        } else {
225            m.setElement(0, 0x06);
226        }
227        m.setElement(1, (X10Sequence.encode(housecode) << 4) + function);
228        return m;
229    }
230
231    public static SpecificMessage getX10Function(int housecode, int function) {
232        SpecificMessage m = new SpecificMessage(4);
233//        m.setInterlocked(true);
234        m.setInterlocked(false);
235        m.setElement(0, Constants.HEAD_STX);
236        m.setElement(1, Constants.FUNCTION_REQ_X10);
237        m.setElement(2, (X10Sequence.encode(housecode) << 4) + function);
238        m.setElement(3, 0x80);  //  0x80 means function
239        return m;
240    }
241
242    public static SpecificMessage getExtCmd(int housecode, int devicecode, int function, int dimcode) {
243        SpecificMessage m = new SpecificMessage(5);
244        m.setInterlocked(true);
245        m.setElement(0, 0x07);
246        m.setElement(1, (X10Sequence.encode(housecode) << 4) + X10Sequence.FUNCTION_EXTENDED_CODE);
247        m.setElement(2, X10Sequence.encode(devicecode));
248        m.setElement(3, dimcode);
249        m.setElement(4, function);
250        return m;
251    }
252
253    public static SpecificMessage getInsteonAddress(int idhighbyte, int idmiddlebyte, int idlowbyte) {
254        SpecificMessage m = new SpecificMessage(8);
255//        m.setInterlocked(true);
256        m.setInterlocked(false);
257        m.setElement(0, Constants.HEAD_STX);
258        m.setElement(1, Constants.FUNCTION_REQ_STD);
259        m.setElement(2, idhighbyte);
260        m.setElement(3, idmiddlebyte);
261        m.setElement(4, idlowbyte);
262        m.setElement(5, 0x0F);
263        m.setElement(6, 0x11);
264        m.setElement(7, 0xFF);
265        return m;
266    }
267
268    public static SpecificMessage getInsteonFunction(int idhighbyte, int idmiddlebyte, int idlowbyte,
269            int function, int flag, int cmd1, int cmd2) {
270        SpecificMessage m = new SpecificMessage(8);
271//        m.setInterlocked(true);
272        m.setInterlocked(false);
273        m.setElement(0, Constants.HEAD_STX);
274        m.setElement(1, Constants.FUNCTION_REQ_STD);
275        m.setElement(2, idhighbyte);
276        m.setElement(3, idmiddlebyte);
277        m.setElement(4, idlowbyte);
278        m.setElement(5, flag);
279        m.setElement(6, cmd1);
280        m.setElement(7, cmd2);
281        return m;
282    }
283
284}