001package jmri.jmrit.logixng.expressions; 002 003import java.beans.*; 004import java.util.*; 005 006import jmri.*; 007import jmri.Block; 008import jmri.BlockManager; 009import jmri.jmrit.display.layoutEditor.LayoutBlock; 010import jmri.jmrit.display.layoutEditor.LayoutBlockManager; 011import jmri.jmrit.logixng.*; 012import jmri.jmrit.logixng.util.LogixNG_SelectNamedBean; 013import jmri.jmrit.logixng.util.LogixNG_SelectEnum; 014import jmri.jmrit.logixng.util.LogixNG_SelectString; 015import jmri.jmrit.logixng.util.parser.*; 016 017/** 018 * This expression evaluates the state of a Block. 019 * The supported characteristics are: 020 * <ul> 021 * <li>Is [not] Occupied (based on occupancy sensor state)</li> 022 * <li>Is [not] Unoccupied (based on occupancy sensor state)</li> 023 * <li>Is [not] Other (UNKNOWN, INCONSISTENT, UNDETECTED)</li> 024 * <li>Is [not] Allocated (based on the LayoutBlock useAlternateColor)</li> 025 * <li>Value [not] equals string</li> 026 * </ul> 027 * @author Daniel Bergqvist Copyright 2021 028 * @author Dave Sand Copyright 2021 029 */ 030public class ExpressionBlock extends AbstractDigitalExpression 031 implements PropertyChangeListener { 032 033 private final LogixNG_SelectNamedBean<Block> _selectNamedBean = 034 new LogixNG_SelectNamedBean<>( 035 this, Block.class, InstanceManager.getDefault(BlockManager.class)); 036 037 private Is_IsNot_Enum _is_IsNot = Is_IsNot_Enum.Is; 038 039 private final LogixNG_SelectEnum<BlockState> _selectEnum = 040 new LogixNG_SelectEnum<>(this, BlockState.values(), BlockState.Occupied); 041 042 private final LogixNG_SelectString _selectBlockValue = 043 new LogixNG_SelectString(this); 044 045 046 public ExpressionBlock(String sys, String user) 047 throws BadUserNameException, BadSystemNameException { 048 super(sys, user); 049 } 050 051 @Override 052 public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws ParserException { 053 DigitalExpressionManager manager = InstanceManager.getDefault(DigitalExpressionManager.class); 054 String sysName = systemNames.get(getSystemName()); 055 String userName = userNames.get(getSystemName()); 056 if (sysName == null) sysName = manager.getAutoSystemName(); 057 ExpressionBlock copy = new ExpressionBlock(sysName, userName); 058 copy.setComment(getComment()); 059 060 _selectNamedBean.copy(copy._selectNamedBean); 061 _selectEnum.copy(copy._selectEnum); 062 _selectBlockValue.copy(copy._selectBlockValue); 063 064 copy.set_Is_IsNot(_is_IsNot); 065 066 return manager.registerExpression(copy); 067 } 068 069 public LogixNG_SelectNamedBean<Block> getSelectNamedBean() { 070 return _selectNamedBean; 071 } 072 073 public LogixNG_SelectEnum<BlockState> getSelectEnum() { 074 return _selectEnum; 075 } 076 077 public LogixNG_SelectString getSelectBlockValue() { 078 return _selectBlockValue; 079 } 080 081 public void set_Is_IsNot(Is_IsNot_Enum is_IsNot) { 082 _is_IsNot = is_IsNot; 083 } 084 085 public Is_IsNot_Enum get_Is_IsNot() { 086 return _is_IsNot; 087 } 088 089 /** {@inheritDoc} */ 090 @Override 091 public LogixNG_Category getCategory() { 092 return LogixNG_Category.ITEM; 093 } 094 095 /** 096 * A block is considered to be allocated if the related layout block has use extra color enabled. 097 * @param block The block whose allocation state is requested. 098 * @return true if the layout block is using the extra color. 099 */ 100 public boolean isBlockAllocated(Block block) { 101 boolean result = false; 102 LayoutBlock layoutBlock = InstanceManager.getDefault(LayoutBlockManager.class).getLayoutBlock(block); 103 if (layoutBlock != null) { 104 result = layoutBlock.getUseExtraColor(); 105 } 106 return result; 107 } 108 109 /** {@inheritDoc} */ 110 @Override 111 public boolean evaluate() throws JmriException { 112 ConditionalNG conditionalNG = getConditionalNG(); 113 114 Block block = _selectNamedBean.evaluateNamedBean(conditionalNG); 115 116 if (block == null) return false; 117 118 BlockState checkBlockState = _selectEnum.evaluateEnum(conditionalNG); 119 120 int currentState = block.getState(); 121 122 switch (checkBlockState) { 123 case Other: 124 if (currentState != Block.OCCUPIED && currentState != Block.UNOCCUPIED) { 125 currentState = BlockState.Other.getID(); 126 } else { 127 currentState = 0; 128 } 129 break; 130 131 case Allocated: 132 boolean cuurrentAllocation = isBlockAllocated(block); 133 currentState = cuurrentAllocation ? BlockState.Allocated.getID() : 0; 134 break; 135 136 case ValueMatches: 137 String blockValue = _selectBlockValue.evaluateValue(conditionalNG); 138 currentState = blockValue.equals(block.getValue()) 139 ? BlockState.ValueMatches.getID() : 0; 140 break; 141 142 default: 143 break; 144 } 145 146 if (_is_IsNot == Is_IsNot_Enum.Is) { 147 return currentState == checkBlockState.getID(); 148 } else { 149 return currentState != checkBlockState.getID(); 150 } 151 } 152 153 @Override 154 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 155 throw new UnsupportedOperationException("Not supported."); 156 } 157 158 @Override 159 public int getChildCount() { 160 return 0; 161 } 162 163 @Override 164 public String getShortDescription(Locale locale) { 165 return Bundle.getMessage(locale, "Block_Short"); 166 } 167 168 @Override 169 public String getLongDescription(Locale locale) { 170 String namedBean = _selectNamedBean.getDescription(locale); 171 String state; 172 173 if (_selectEnum.isDirectAddressing()) { 174 BlockState blockState = _selectEnum.getEnum(); 175 176 if (blockState == BlockState.ValueMatches) { 177 String bundleKey = "Block_Long_Value"; 178 String equalsString = _is_IsNot == Is_IsNot_Enum.Is ? Bundle.getMessage("Block_Equal") : Bundle.getMessage("Block_NotEqual"); 179 return Bundle.getMessage(locale, bundleKey, namedBean, equalsString, _selectBlockValue.getDescription(locale)); 180 } else if (blockState == BlockState.Other) { 181 state = Bundle.getMessage(locale, "AddressByDirect", blockState._text); 182 return Bundle.getMessage(locale, "Block_Long", namedBean, "", state); 183 } else { 184 state = Bundle.getMessage(locale, "AddressByDirect", blockState._text); 185 } 186 } else { 187 state = _selectEnum.getDescription(locale); 188 } 189 190 return Bundle.getMessage(locale, "Block_Long", namedBean, _is_IsNot.toString(), state); 191 } 192 193 /** {@inheritDoc} */ 194 @Override 195 public void setup() { 196 // Do nothing 197 } 198 199 /** {@inheritDoc} */ 200 @Override 201 public void registerListenersForThisClass() { 202 if (!_listenersAreRegistered) { 203 _selectNamedBean.addPropertyChangeListener(this); 204 _listenersAreRegistered = true; 205 } 206 } 207 208 /** {@inheritDoc} */ 209 @Override 210 public void unregisterListenersForThisClass() { 211 if (_listenersAreRegistered) { 212 _selectNamedBean.removePropertyChangeListener(this); 213 _listenersAreRegistered = false; 214 } 215 } 216 217 /** {@inheritDoc} */ 218 @Override 219 public void propertyChange(PropertyChangeEvent evt) { 220 // Only execute the ConditionalNG if a property that this expression 221 // checks has changed. For example, a reporter in the block may 222 // change the value of the block often. 223 String propertyName = evt.getPropertyName(); 224 if (Block.PROPERTY_VALUE.equals(propertyName)) { 225 if (_selectEnum.isDirectAddressing() && (_selectEnum.getEnum() != BlockState.ValueMatches)) { 226 return; 227 } 228 } else if (Block.PROPERTY_ALLOCATED.equals(propertyName)) { 229 if (_selectEnum.isDirectAddressing() && (_selectEnum.getEnum() != BlockState.Allocated)) { 230 return; 231 } 232 } else if (!Block.PROPERTY_STATE.equals(propertyName)) { 233 return; 234 } 235 getConditionalNG().execute(); 236 } 237 238 /** {@inheritDoc} */ 239 @Override 240 public void disposeMe() { 241 } 242 243 public enum BlockState { 244 Occupied(2, Bundle.getMessage("Block_StateOccupied")), 245 NotOccupied(4, Bundle.getMessage("Block_StateNotOccupied")), 246 Other(-1, Bundle.getMessage("Block_StateOther")), 247 Allocated(-2, Bundle.getMessage("Block_Allocated")), 248 ValueMatches(-3, Bundle.getMessage("Block_ValueMatches")); 249 250 private final int _id; 251 private final String _text; 252 253 private BlockState(int id, String text) { 254 this._id = id; 255 this._text = text; 256 } 257 258 public int getID() { 259 return _id; 260 } 261 262 @Override 263 public String toString() { 264 return _text; 265 } 266 } 267 268 /** {@inheritDoc} */ 269 @Override 270 public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) { 271 log.debug("getUsageReport :: ExpressionBlock: bean = {}, report = {}", cdl, report); 272 _selectNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Expression); 273 } 274 275 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ExpressionBlock.class); 276 277}