001package jmri.jmrit.logixng.expressions;
002
003import java.beans.*;
004import java.util.*;
005
006import jmri.*;
007import jmri.jmrit.logixng.*;
008import jmri.jmrit.logixng.util.*;
009import jmri.jmrit.logixng.util.parser.*;
010
011/**
012 * This expression checks the flank of the change of the state of a sensor.
013 *
014 * @author Daniel Bergqvist Copyright 2022
015 */
016public class ExpressionSensorEdge extends AbstractDigitalExpression
017        implements PropertyChangeListener {
018
019    private final LogixNG_SelectNamedBean<Sensor> _selectNamedBean =
020            new LogixNG_SelectNamedBean<>(
021                    this, Sensor.class, InstanceManager.getDefault(SensorManager.class), this);
022
023    private final LogixNG_SelectEnum<SensorState> _selectEnumFromState =
024            new LogixNG_SelectEnum<>(this, SensorState.values(), SensorState.Active, this);
025
026    private final LogixNG_SelectEnum<SensorState> _selectEnumToState =
027            new LogixNG_SelectEnum<>(this, SensorState.values(), SensorState.Active, this);
028
029    private boolean _onlyTrueOnce = false;
030
031    SensorState lastSensorState = null;
032    SensorState currentSensorState = null;
033
034    public ExpressionSensorEdge(String sys, String user)
035            throws BadUserNameException, BadSystemNameException {
036        super(sys, user);
037    }
038
039    @Override
040    public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws ParserException {
041        DigitalExpressionManager manager = InstanceManager.getDefault(DigitalExpressionManager.class);
042        String sysName = systemNames.get(getSystemName());
043        String userName = userNames.get(getSystemName());
044        if (sysName == null) sysName = manager.getAutoSystemName();
045        ExpressionSensorEdge copy = new ExpressionSensorEdge(sysName, userName);
046        copy.setComment(getComment());
047        _selectNamedBean.copy(copy._selectNamedBean);
048        _selectEnumFromState.copy(copy._selectEnumFromState);
049        _selectEnumToState.copy(copy._selectEnumToState);
050        copy.setOnlyTrueOnce(_onlyTrueOnce);
051        return manager.registerExpression(copy);
052    }
053
054    public LogixNG_SelectNamedBean<Sensor> getSelectNamedBean() {
055        return _selectNamedBean;
056    }
057
058    public LogixNG_SelectEnum<SensorState> getSelectEnumFromState() {
059        return _selectEnumFromState;
060    }
061
062    public LogixNG_SelectEnum<SensorState> getSelectEnumToState() {
063        return _selectEnumToState;
064    }
065
066    public void setOnlyTrueOnce(boolean onlyTrueOnce) {
067        _onlyTrueOnce = onlyTrueOnce;
068    }
069
070    public boolean getOnlyTrueOnce() {
071        return _onlyTrueOnce;
072    }
073
074    /** {@inheritDoc} */
075    @Override
076    public Category getCategory() {
077        return Category.ITEM;
078    }
079
080    /** {@inheritDoc} */
081    @Override
082    public boolean evaluate() throws JmriException {
083        Sensor sensor = _selectNamedBean.evaluateNamedBean(getConditionalNG());
084
085        if (sensor == null) return false;
086
087        SensorState checkSensorFromState = _selectEnumFromState.evaluateEnum(getConditionalNG());
088        SensorState checkSensorToState = _selectEnumToState.evaluateEnum(getConditionalNG());
089
090        boolean result = (lastSensorState == checkSensorFromState)
091                && (currentSensorState == checkSensorToState);
092
093        if (_onlyTrueOnce) {
094            lastSensorState = null;
095            currentSensorState = null;
096        }
097
098        return result;
099    }
100
101    @Override
102    public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException {
103        throw new UnsupportedOperationException("Not supported.");
104    }
105
106    @Override
107    public int getChildCount() {
108        return 0;
109    }
110
111    @Override
112    public String getShortDescription(Locale locale) {
113        return Bundle.getMessage(locale, "SensorEdge_Short");
114    }
115
116    @Override
117    public String getLongDescription(Locale locale) {
118        String namedBean = _selectNamedBean.getDescription(locale);
119        String fromState = _selectEnumFromState.getDescription(locale);
120        String toState = _selectEnumToState.getDescription(locale);
121
122        if (_onlyTrueOnce) {
123            return Bundle.getMessage(locale, "SensorEdge_LongOnlyTrueOnce", namedBean, fromState, toState);
124        } else {
125            return Bundle.getMessage(locale, "SensorEdge_Long", namedBean, fromState, toState);
126        }
127    }
128
129    /** {@inheritDoc} */
130    @Override
131    public void setup() {
132        // Do nothing
133    }
134
135    /** {@inheritDoc} */
136    @Override
137    public void registerListenersForThisClass() {
138        if (!_listenersAreRegistered) {
139            _selectNamedBean.addPropertyChangeListener("KnownState", this);
140            _selectNamedBean.registerListeners();
141            _listenersAreRegistered = true;
142        }
143    }
144
145    /** {@inheritDoc} */
146    @Override
147    public void unregisterListenersForThisClass() {
148        if (_listenersAreRegistered) {
149            _selectNamedBean.removePropertyChangeListener("KnownState", this);
150            _selectNamedBean.unregisterListeners();
151            _listenersAreRegistered = false;
152            lastSensorState = null;
153            currentSensorState = null;
154        }
155    }
156
157    /** {@inheritDoc} */
158    @Override
159    public void propertyChange(PropertyChangeEvent evt) {
160        if ("KnownState".equals(evt.getPropertyName())) {
161            Object oldState = evt.getOldValue();
162            Object newState = evt.getNewValue();
163            lastSensorState = oldState != null ? SensorState.get((int) oldState) : null;
164            currentSensorState = newState != null ? SensorState.get((int) newState) : null;
165            getConditionalNG().execute();
166        }
167    }
168
169    /** {@inheritDoc} */
170    @Override
171    public void disposeMe() {
172    }
173
174
175    public enum SensorState {
176        Unknown(Sensor.INACTIVE, Bundle.getMessage("BeanStateUnknown")),
177        Inconsistent(Sensor.ACTIVE, Bundle.getMessage("BeanStateInconsistent")),
178        Inactive(Sensor.INACTIVE, Bundle.getMessage("SensorStateInactive")),
179        Active(Sensor.ACTIVE, Bundle.getMessage("SensorStateActive"));
180
181        private final int _id;
182        private final String _text;
183
184        private SensorState(int id, String text) {
185            this._id = id;
186            this._text = text;
187        }
188
189        static public SensorState get(int id) {
190            switch (id) {
191                case Sensor.UNKNOWN:
192                    return Inactive;
193
194                case Sensor.INCONSISTENT:
195                    return Active;
196
197                case Sensor.INACTIVE:
198                    return Inactive;
199
200                case Sensor.ACTIVE:
201                    return Active;
202
203                default:
204                    return null;
205            }
206        }
207
208        public int getID() {
209            return _id;
210        }
211
212        @Override
213        public String toString() {
214            return _text;
215        }
216
217    }
218
219    /** {@inheritDoc} */
220    @Override
221    public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) {
222        log.debug("getUsageReport :: ExpressionSensorEdge: bean = {}, report = {}", cdl, report);
223        _selectNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Expression);
224    }
225
226    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ExpressionSensorEdge.class);
227}