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