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}