001package jmri.jmrix.can.cbus;
002
003import javax.annotation.Nonnull;
004import jmri.jmrix.AbstractMessage;
005import jmri.jmrix.can.CanMessage;
006
007// import org.slf4j.Logger;
008// import org.slf4j.LoggerFactory;
009
010/**
011 * Class to enable storage and OPC calculation
012 * according to CBUS Event Data.
013 * 
014 * @author Steve Young Copyright (C) 2020
015 */
016public class CbusEventDataElements {
017    
018    private int _dat1;
019    private int _dat2;
020    private int _dat3;
021    private int _numElements;
022    
023    /**
024     * ENUM of the event state.
025     * <p>
026     * Events generally have on, off or unknown status.
027     * <p>
028     * They can also be asked to request their current status via the network,
029     * or toggled to the opposite state that it is currently at.
030     */
031    public enum EvState{
032        ON, OFF, UNKNOWN, REQUEST, TOGGLE;
033    }
034    
035    /**
036     * Create Data Elements for a CBUS Event
037     */
038    public CbusEventDataElements(){
039        _numElements = 0;
040    }
041    
042    /**
043     * Get a ready-to-send CanMessage with event details.
044     * 
045     * @param canId CAN ID
046     * @param nn Node Number
047     * @param en Event Number
048     * @param state Event State
049     * @return ready to send CanMessage
050     */
051    public CanMessage getCanMessage(int canId, int nn, int en, @Nonnull EvState state){
052    
053        CanMessage m = new CanMessage(canId);
054        CbusMessage.setPri(m, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY);
055        
056        m.setElement(1, nn >> 8);
057        m.setElement(2, nn & 0xff);
058        m.setElement(3, en >> 8);
059        m.setElement(4, en & 0xff);
060        
061        switch (state) {
062            case ON:
063            case OFF:
064                setCanMessageData(m);
065                return(finishEvent(m,nn>0,state));
066            default:
067                return(finishRequest(m,nn>0));
068        }
069    }
070    
071    private void setCanMessageData( CanMessage m) {
072        m.setNumDataElements(5+_numElements);
073        switch (_numElements) {
074            case 1:
075                m.setElement(5, _dat1);
076                break;
077            case 2:
078                m.setElement(5, _dat1);
079                m.setElement(6, _dat2);
080                break;
081            case 3:
082                m.setElement(5, _dat1);
083                m.setElement(6, _dat2);
084                m.setElement(7, _dat3);
085                break;
086            default:
087                break;
088        }
089    }
090    
091    private CanMessage finishEvent(CanMessage m, boolean isLong, EvState state) {
092        int opc;
093        switch (_numElements) {
094            case 1:
095                opc = (isLong ? CbusConstants.CBUS_ACON1 : CbusConstants.CBUS_ASON1 );
096                break;
097            case 2:
098                opc = (isLong ? CbusConstants.CBUS_ACON2 : CbusConstants.CBUS_ASON2);
099                break;
100            case 3:
101                opc = (isLong ? CbusConstants.CBUS_ACON3 : CbusConstants.CBUS_ASON3 );
102                break;
103            default:
104                opc = (isLong ? CbusConstants.CBUS_ACON : CbusConstants.CBUS_ASON );
105                break;
106        }
107        if (state==EvState.OFF){
108            opc++;
109        }
110        m.setElement(0, opc);
111        return m;
112    }
113    
114    private CanMessage finishRequest( CanMessage m, boolean isLong) {
115        m.setNumDataElements(5);
116        if (isLong) {
117            m.setElement(0, CbusConstants.CBUS_AREQ);
118        } else {
119            m.setElement(0, CbusConstants.CBUS_ASRQ);
120        }
121        return m;
122    }
123    
124    /**
125     * Set Number of Event Data Elements (bytes).
126     * @param elements 0-3
127     */
128    public void setNumElements(int elements){
129        if (elements<0 || elements > 3){
130            throw new IllegalArgumentException("" + elements + " Event Data Elements Invalid");
131        }
132        _numElements = elements;
133    }
134    
135    /**
136     * Get Number of Event Data Elements (bytes).
137     * @return Number of Data Bytes
138     */
139    public int getNumElements() {
140        return _numElements;
141    }
142    
143    /**
144     * Set value of a single event Data Byte.
145     * @param index Event Index: 1, 2 or 3
146     * @param value Byte value 0-255
147     */
148    public void setData(int index, int value) {
149        if (value < 0 || value > 255) {
150            throw new IllegalArgumentException("Data Value " + value + " Invalid");
151        }
152        switch (index) {
153            case 1:
154                _dat1 = value;
155                break;
156            case 2:
157                _dat2 = value;
158                break;
159            case 3:
160                _dat3 = value;
161                break;
162            default:
163                throw new IllegalArgumentException("Data Index " + index + " Invalid");
164        }
165    }
166    
167    /**
168     * Get value of a single event Data Byte.
169     * @param index Event Index: 1, 2 or 3
170     * @return Byte value 0-255 , -1 for unset
171     */
172    public int getData(int index) {
173        
174        if ( getNumElements()<index ){
175            return -1;
176        }
177        switch (index) {
178            case 1:
179                return _dat1;
180            case 2:
181                return _dat2;
182            case 3:
183                return _dat3;
184            default:
185                throw new IllegalArgumentException("Data Index " + index + " Invalid");
186        }
187    }
188    
189    public static int getNumEventDataElements(AbstractMessage m){
190        if ( CbusFilterType.allFilters(m.getElement(0)).contains(CbusFilterType.CFED1) ){
191            return 1;
192        }
193        else if ( CbusFilterType.allFilters(m.getElement(0)).contains(CbusFilterType.CFED2) ){
194            return 2;
195        }
196        else if ( CbusFilterType.allFilters(m.getElement(0)).contains(CbusFilterType.CFED3) ){
197            return 3;
198        }
199        return 0;
200    }
201    
202    /**
203     * Set Event Data from CAN Frame.
204     * @param m CanMessage or CanReply
205     */
206    public void setDataFromFrame(AbstractMessage m) {
207        setNumElements(getNumEventDataElements(m));
208        for ( int i=0; i<getNumEventDataElements(m); i++ ){
209            setData(i+1, m.getElement(i+5));
210        }
211    }
212    
213    /**
214     * Get the event state from a CAN Frame.
215     * @param m CanMessage or CanReply
216     * @return Event State ENUM of Off, On or Request
217     */    
218    public static final EvState getEvState(AbstractMessage m) {
219        EvState state = EvState.OFF;
220        if (CbusOpCodes.isOnEvent(CbusMessage.getOpcode(m))) {
221            state = EvState.ON;
222        }
223        if (CbusOpCodes.isEventRequest(CbusMessage.getOpcode(m))) {
224            state = EvState.REQUEST;
225        }
226        return state;
227    }
228
229    public static String getJmriString(int nn, int en){
230        StringBuilder jmriAddress = new StringBuilder(13);
231        jmriAddress.append("+");
232        if ( nn > 0 ) {
233            jmriAddress.append("N");
234            jmriAddress.append(nn);
235            jmriAddress.append("E");
236        }
237        jmriAddress.append(en);
238        return jmriAddress.toString();
239    }
240    
241    // private static final Logger log = LoggerFactory.getLogger(CbusEventDataElements.class);
242
243}