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}