001package jmri.jmrix.can.cbus;
002
003import javax.annotation.Nonnull;
004
005import jmri.jmrix.can.CanSystemConnectionMemo;
006
007public class CbusEvent extends CbusEventDataElements {
008    
009    private int _nn;
010    private int _en;
011    protected EvState _state;
012    protected String _name;
013    private final CanSystemConnectionMemo _memo;
014    
015    /**
016     * Create a new event
017     * <p>
018     * New events have an unknown on or off status
019     *
020     * @param nn Node Number
021     * @param en Event Number
022     */
023    public CbusEvent( int nn, int en){
024        super();
025        this._nn = nn;
026        this._en = en;
027        this._state = EvState.UNKNOWN;
028        this._name = "";
029        this._memo = null;
030    }
031    
032    /**
033     * Create a new event by Connection
034     * <p>
035     * New events have an unknown on or off status
036     *
037     * @param memo System Connection
038     * @param nn Node Number
039     * @param en Event Number
040     */
041    public CbusEvent( CanSystemConnectionMemo memo, int nn, int en){
042        super();
043        this._nn = nn;
044        this._en = en;
045        this._state = EvState.UNKNOWN;
046        this._name = "";
047        this._memo = memo;
048    }
049
050    /**
051     * Get state of the event
052     *
053     * @return the enum event state, on off or unknown.
054     *
055     */
056    public EvState getState() {
057        return _state;
058    }
059    
060    /**
061     * Set current state of the event.
062     * <p>
063     * Does NOT send update to layout.
064     *
065     * @param newval the enum event state ie ON, OFF, UNKNOWN
066     */
067    public void setState( EvState newval ) {
068        _state = newval;
069    }    
070
071    /**
072     * Get event event number
073     *
074     * @return event Number
075     *
076     */
077    public int getEn() {
078        return _en;
079    }
080
081    /**
082     * Get event node number.
083     *
084     * @return node Number
085     */
086    public int getNn(){
087        return _nn;
088    }
089
090    /**
091     * Set event event number.
092     *
093     * @param en Event Number, not restricted so can be -1 for unknown
094     */
095    public void setEn ( int en ) {
096        _en = en;
097    }
098
099    /**
100     * Set event node number.
101     *
102     * @param nn Node Number, not restricted so can be -1 for unknown
103     */
104    public void setNn ( int nn ) {
105        _nn = nn;
106    }
107
108    /**
109     * Set event name.
110     *
111     * @param name new Event Name
112     */
113    public void setName( String name ) {
114        _name = name;
115    }
116
117    /**
118     * Set event name only if there is no existing name.
119     *
120     * @param name Event Name
121     */
122    public void setNameIfNoName (@Nonnull String name){
123        if (_name.isEmpty()) {
124            _name = name;
125        }
126    }
127
128    /**
129     * Get event name.
130     *
131     * @return the Event Name
132     */
133    @Nonnull
134    public String getName() {
135        return _name;
136    }
137
138    /**
139     * Get Node name.
140     * <p>
141     * Helper method, node name not stored in event, retrieved via @CbusNameService
142     *
143     * @return Node Name
144     */
145    public String getNodeName() {
146        return new CbusNameService(_memo).getNodeName( getNn() );
147    }
148
149    /**
150     * Test if a node and event number combination matches this event.
151     * 
152     * @param nn Node Number
153     * @param en Event Number
154     *
155     * @return true on match, else false
156     */
157    public boolean matches(int nn, int en) {
158        return (nn == _nn) && (en == _en);
159    }
160
161    /** 
162     * {@inheritDoc} 
163     * <p>
164     * Custom method to compare Node Number and Event Number.
165     */
166    @Override
167    public boolean equals(Object o) {
168        return ((o instanceof CbusEvent) && matches(((CbusEvent) o).getNn(),((CbusEvent) o).getEn()));
169    }
170
171    /** {@inheritDoc} */
172    @Override
173    public int hashCode() {
174        return java.util.Objects.hash(getEn(), getNn());
175    }
176
177    /**
178     * Send ON event CAN frame.
179     * <p>
180     * Long event if Node num greater than 0, else short.
181     */
182    public void sendOn(){
183        sendEvent(EvState.ON);
184    }
185
186    /**
187     * Send OFF event CAN frame.
188     * <p>
189     * Long event if Node num greater than 0, else short.
190     */
191    public void sendOff(){
192        sendEvent(EvState.OFF);
193    }
194
195    /**
196     * Send event status request CAN frame.
197     * <p>
198     * Long request if Node num greater than 0, else short.
199     */
200    public void sendRequest(){
201        sendEvent(EvState.REQUEST);
202    }
203
204    /**
205     * Send event CAN frame via ENUM.
206     * <p>
207     * Also updates the event status as per the enum value.
208     * <p>
209     * If current state unknown, toggle sends event off.
210     * <p>
211     * Long event if Node num greater than 0, else short.
212     *
213     * @param state The enum state requested to be sent, ie ON, OFF, REQUEST, TOGGLE
214     */
215    public void sendEvent(EvState state) {
216        CanSystemConnectionMemo memo;
217        if (_memo==null) {
218            memo = jmri.InstanceManager.getDefault(CanSystemConnectionMemo.class);
219        } else {
220            memo = _memo;
221        }
222        if ( state == EvState.TOGGLE ) {
223            if ( _state == EvState.OFF )  {
224                state =EvState.ON;
225            }
226            else {
227                state =EvState.OFF;
228            }
229        }
230        _state = state;
231        
232        jmri.util.ThreadingUtil.runOnLayout( () -> {
233            memo.getTrafficController().sendCanMessage(getCanMessage(memo.getTrafficController().getCanid(),_nn,_en,_state), null);
234        } );
235    }
236
237    /**
238     * Get a String with event overview.
239     * 
240     * @return includes event name and node name if known
241     */
242    @Override
243    public String toString() {
244        StringBuilder addevbuf = new StringBuilder(50);
245        if ( _nn > 0 ) {
246            addevbuf.append (Bundle.getMessage("OPC_NN")).append (":");
247            addevbuf.append (_nn).append (" ");
248            if ( !getNodeName().isEmpty() ) {
249                addevbuf.append (getNodeName()).append (" ");
250            }
251        }
252        addevbuf.append (Bundle.getMessage("OPC_EN")).append (":");
253        addevbuf.append (_en).append (" ");
254        if ( !getName().isEmpty() ) {
255            addevbuf.append ( getName() ).append (" ");
256        }
257        return addevbuf.toString();
258    }
259
260    // private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CbusEvent.class);
261
262}