001package jmri.jmrit.logix; 002 003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 004 005import java.util.Iterator; 006import java.util.List; 007import java.util.Objects; 008 009import javax.swing.Timer; 010 011import jmri.BeanSetting; 012import jmri.Block; 013import jmri.InstanceManager; 014import jmri.Turnout; 015 016/** 017 * Extends jmri.Path. An OPath is a route that traverses a Block from one 018 * boundary to another. The dest parameter of Path (renamed to owner) is 019 * used to reference the Block to which this OPath belongs. (Not a 020 * destination Block as might be inferred from the naming in Path.java) 021 * <p> 022 * An OPath inherits the List of BeanSettings for all the turnouts needed to 023 * traverse the Block. It also has references to the Portals (block boundary 024 * objects) through which it enters or exits the block. One of these may be 025 * null, if the OPath dead ends within the block. 026 * 027 * @author Pete Cressman Copyright (C) 2009 028 */ 029public class OPath extends jmri.Path { 030 031 private Portal _fromPortal; 032 private Portal _toPortal; 033 private String _name; 034 private Timer _timer; 035 private boolean _timerActive = false; 036 private TimeTurnout _listener; 037 038 /** 039 * Create an OPath object with default directions of NONE, and no setting 040 * element. 041 * 042 * @param owner Block owning the path 043 * @param name name of the path 044 */ 045 public OPath(Block owner, String name) { 046 super(owner, 0, 0); 047 _name = name; 048 } 049 050 /** 051 * Create an OPath object with default directions of NONE, and no setting 052 * element. 053 * 054 * @param owner Block owning the path 055 * @param name name of the path 056 * @param entry Portal where path enters 057 * @param exit Portal where path exits 058 * @param settings array of turnout settings of the path 059 */ 060 public OPath(String name, OBlock owner, Portal entry, Portal exit, List<BeanSetting> settings) { 061 super(owner, 0, 0); 062 _name = name; 063 _fromPortal = entry; 064 _toPortal = exit; 065 if (settings != null) { 066 for (BeanSetting setting : settings) { 067 OPath.this.addSetting(setting); 068 } 069 } 070 if (log.isDebugEnabled()) { 071 log.debug("OPath Ctor: name= {}, block= {}, fromPortal= {}, toPortal= {}", 072 name, owner.getDisplayName(), (_fromPortal == null ? "null" : _fromPortal.getName()), 073 (_toPortal == null ? "null" : _toPortal.getName())); 074 } 075 } 076 077 protected String getOppositePortalName(String name) { 078 if ( _fromPortal != null && _fromPortal.getName().equals(name) && _toPortal != null ) { 079 return _toPortal.getName(); 080 } 081 if ( _toPortal != null && _toPortal.getName().equals(name) && _fromPortal != null ) { 082 return _fromPortal.getName(); 083 } 084 return null; 085 } 086 087 @SuppressFBWarnings(value="BC_UNCONFIRMED_CAST_OF_RETURN_VALUE", justification="OBlock extends Block") 088 public void setName(String name) { 089 log.debug("OPath \"{}\" setName to \"{}\"", _name, name); 090 if (name == null || name.length() == 0) { 091 return; 092 } 093 String oldName = _name; 094 _name = name; 095 OBlock block = (OBlock) getBlock(); 096 block.pseudoPropertyChange("pathName", oldName, _name); // for IndicatorTrack icons 097 InstanceManager.getDefault(WarrantManager.class).pathNameChange(block, oldName, _name); 098 if ( _fromPortal != null && _fromPortal.addPath(this ) ) { 099 return; 100 } 101 if (_toPortal != null) { 102 _toPortal.addPath(this); 103 } 104 } 105 106 public String getName() { 107 return _name; 108 } 109 110 public void setFromPortal(Portal p) { 111 if (p != null) { 112 log.debug("OPath \"{}\" setFromPortal= \"{}\"", _name, p.getName()); 113 } 114 _fromPortal = p; 115 } 116 117 public Portal getFromPortal() { 118 return _fromPortal; 119 } 120 121 public void setToPortal(Portal p) { 122 if (p != null) { 123 log.debug("OPath \"{}\" setToPortal= \"{}\"", _name, p.getName()); 124 } 125 _toPortal = p; 126 } 127 128 public Portal getToPortal() { 129 return _toPortal; 130 } 131 132 /** 133 * Set path turnout commanded state and lock state 134 * 135 * @param delay following actions in seconds 136 * @param set when true, command turnout to settings, false don't set 137 * command - just do lock setting 138 * @param lockState set when lock==true, lockState unset when lock==false 139 * @param lock If lockState==0 setLocked() is not called. (lockState 140 * should be 1,2,3) 141 */ 142 public void setTurnouts(int delay, boolean set, int lockState, boolean lock) { 143 if (delay > 0) { 144 if (!_timerActive) { 145 // Create a timer if one does not exist 146 if (_timer == null) { 147 _listener = new TimeTurnout(); 148 _timer = new Timer(2000, _listener); 149 _timer.setRepeats(false); 150 } 151 _listener.setList(getSettings()); 152 _listener.setParams(set, lockState, lock); 153 _timer.setInitialDelay(delay * 1000); 154 _timer.start(); 155 _timerActive = true; 156 } else { 157 log.warn("timer already active for delayed turnout action on path {}", this); 158 } 159 } else { 160 fireTurnouts(getSettings(), set, lockState, lock); 161 } 162 } 163 164 private void fireTurnouts(List<BeanSetting> list, boolean set, int lockState, boolean lock) { 165 for (BeanSetting bs : list) { 166 Turnout t = (Turnout) bs.getBean(); 167 if (set) { 168 t.setCommandedState(bs.getSetting()); 169 } 170 if (lockState > 0) { 171 t.setLocked(lockState, lock); 172 } 173 } 174 } 175 176 public void dispose() { 177 if (_fromPortal != null) { 178 _fromPortal.removePath(this); 179 } 180 if (_toPortal != null) { 181 _toPortal.removePath(this); 182 } 183 } 184 185 /** 186 * Class for defining ActionListener for ACTION_DELAYED_TURNOUT 187 */ 188 private class TimeTurnout implements java.awt.event.ActionListener { 189 190 private List<BeanSetting> list; 191 private int lockState; 192 boolean set; 193 boolean lock; 194 195 public TimeTurnout() { 196 // no actions required to construct 197 } 198 199 void setList(List<BeanSetting> l) { 200 list = l; 201 } 202 203 void setParams(boolean s, int ls, boolean l) { 204 set = s; 205 lockState = ls; 206 lock = l; 207 } 208 209 @Override 210 public void actionPerformed(java.awt.event.ActionEvent event) { 211 fireTurnouts(list, set, lockState, lock); 212 // Turn Timer OFF 213 if (_timer != null) { 214 _timer.stop(); 215 _timerActive = false; 216 } 217 } 218 } 219 220 public String getDescription() { 221 StringBuilder sb = new StringBuilder("\""); 222 sb.append(_name); 223 sb.append("\" from portal \""); 224 sb.append(_fromPortal==null?"null":_fromPortal.getName()); 225 sb.append("\" to portal \""); 226 sb.append(_toPortal==null?"null":_toPortal.getName()); 227 sb.append("\""); 228 return sb.toString(); 229 } 230 231 @Override 232 public String toString() { 233 StringBuilder sb = new StringBuilder("OPath \""); 234 sb.append(_name); 235 sb.append("\" on block \""); 236 sb.append(getBlock()==null?"null":getBlock().getDisplayName()); 237 sb.append("\" from portal \""); 238 sb.append(_fromPortal==null?"null":_fromPortal.getName()); 239 sb.append("\" to portal \""); 240 sb.append(_toPortal==null?"null":_toPortal.getName()); 241 sb.append("\" sets "); 242 sb.append(getSettings().size()); 243 sb.append("\" turnouts."); 244 return sb.toString(); 245 } 246 247 /** 248 * {@inheritDoc} Does not allow duplicate settings. 249 */ 250 @Override 251 public void addSetting(BeanSetting t) { 252 for (BeanSetting bs : getSettings()) { 253 if (bs.getBeanName().equals(t.getBeanName())) { 254 log.error("TO setting for \"{}\" already set to {}", t.getBeanName(), bs.getSetting()); 255 return; 256 } 257 } 258 super.addSetting(t); 259 } 260 261 @Override 262 public int hashCode() { 263 int hash = 7; 264 hash = 67 * hash + Objects.hashCode(this.getBlock()); 265 hash = 67 * hash + Objects.hashCode(this._fromPortal); 266 hash = 67 * hash + Objects.hashCode(this._toPortal); 267 hash = 67 * hash + Objects.hashCode(this.getSettings()); 268 return hash; 269 } 270 271 /** 272 * {@inheritDoc} 273 * 274 * Override to indicate logical equality for use as paths in OBlocks. 275 */ 276 @Override 277 public boolean equals(Object obj) { 278 if (obj == this) { 279 return true; 280 } 281 if (obj == null) { 282 return false; 283 } 284 if (getClass() != obj.getClass()) { 285 return false; 286 } 287 OPath path = (OPath) obj; 288 if (getBlock() != path.getBlock()) { 289 return false; 290 } 291 Portal fromPort = path.getFromPortal(); 292 Portal toPort = path.getToPortal(); 293 int numPortals = 0; 294 if (fromPort != null) { 295 numPortals++; 296 } 297 if (toPort != null) { 298 numPortals++; 299 } 300 if (_fromPortal != null) { 301 numPortals--; 302 } 303 if (_toPortal != null) { 304 numPortals--; 305 } 306 if (numPortals != 0) { 307 return false; 308 } 309 if (_fromPortal != null && !_fromPortal.equals(fromPort) && !_fromPortal.equals(toPort)) { 310 return false; 311 } 312 if (_toPortal != null && !_toPortal.equals(toPort) && !_toPortal.equals(fromPort)) { 313 return false; 314 } 315 List<BeanSetting> settings = path.getSettings(); 316 if (settings.size() != getSettings().size()) { 317 return false; 318 } 319 Iterator<BeanSetting> iter = settings.iterator(); 320 Iterator<BeanSetting> it = getSettings().iterator(); 321 boolean found = false; 322 while (iter.hasNext()) { 323 BeanSetting beanSetting = iter.next(); 324 while (it.hasNext()) { 325 found = false; 326 BeanSetting bs = it.next(); 327 if (bs.getBean().getSystemName().equals(beanSetting.getBean().getSystemName()) 328 && bs.getSetting() == beanSetting.getSetting()) { 329 found = true; 330 break; 331 } 332 } 333 if (!found) { 334 return false; 335 } 336 } 337 return true; 338 } 339 340 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(OPath.class); 341 342}