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}