001package jmri.jmrit.logix; 002 003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 004import java.util.Iterator; 005import java.util.List; 006import java.util.Objects; 007import javax.swing.Timer; 008import jmri.BeanSetting; 009import jmri.Block; 010import jmri.InstanceManager; 011import jmri.Turnout; 012 013import org.slf4j.Logger; 014import org.slf4j.LoggerFactory; 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 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)) { 079 if (_toPortal != null) { 080 return _toPortal.getName(); 081 } 082 } 083 if (_toPortal != null && _toPortal.getName().equals(name)) { 084 if (_fromPortal != null) { 085 return _fromPortal.getName(); 086 } 087 } 088 return null; 089 } 090 091 @SuppressFBWarnings(value="BC_UNCONFIRMED_CAST_OF_RETURN_VALUE", justification="OBlock extends Block") 092 public void setName(String name) { 093 log.debug("OPath \"{}\" setName to \"{}\"", _name, name); 094 if (name == null || name.length() == 0) { 095 return; 096 } 097 String oldName = _name; 098 _name = name; 099 OBlock block = (OBlock) getBlock(); 100 block.pseudoPropertyChange("pathName", oldName, _name); // for IndicatorTrack icons 101 InstanceManager.getDefault(WarrantManager.class).pathNameChange(block, oldName, _name); 102 if (_fromPortal != null) { 103 if (_fromPortal.addPath(this)) { 104 return; 105 } 106 } 107 if (_toPortal != null) { 108 _toPortal.addPath(this); 109 } 110 } 111 112 public String getName() { 113 return _name; 114 } 115 116 public void setFromPortal(Portal p) { 117 if (p != null) { 118 log.debug("OPath \"{}\" setFromPortal= \"{}\"", _name, p.getName()); 119 } 120 _fromPortal = p; 121 } 122 123 public Portal getFromPortal() { 124 return _fromPortal; 125 } 126 127 public void setToPortal(Portal p) { 128 if (p != null) { 129 log.debug("OPath \"{}\" setToPortal= \"{}\"", _name, p.getName()); 130 } 131 _toPortal = p; 132 } 133 134 public Portal getToPortal() { 135 return _toPortal; 136 } 137 138 /** 139 * Set path turnout commanded state and lock state 140 * 141 * @param delay following actions in seconds 142 * @param set when true, command turnout to settings, false don't set 143 * command - just do lock setting 144 * @param lockState set when lock==true, lockState unset when lock==false 145 * @param lock If lockState==0 setLocked() is not called. (lockState 146 * should be 1,2,3) 147 */ 148 public void setTurnouts(int delay, boolean set, int lockState, boolean lock) { 149 if (delay > 0) { 150 if (!_timerActive) { 151 // Create a timer if one does not exist 152 if (_timer == null) { 153 _listener = new TimeTurnout(); 154 _timer = new Timer(2000, _listener); 155 _timer.setRepeats(false); 156 } 157 _listener.setList(getSettings()); 158 _listener.setParams(set, lockState, lock); 159 _timer.setInitialDelay(delay * 1000); 160 _timer.start(); 161 _timerActive = true; 162 } else { 163 log.warn("timer already active for delayed turnout action on path {}", this); 164 } 165 } else { 166 fireTurnouts(getSettings(), set, lockState, lock); 167 } 168 } 169 170 private void fireTurnouts(List<BeanSetting> list, boolean set, int lockState, boolean lock) { 171 for (BeanSetting bs : list) { 172 Turnout t = (Turnout) bs.getBean(); 173 if (set) { 174 t.setCommandedState(bs.getSetting()); 175 } 176 if (lockState > 0) { 177 t.setLocked(lockState, lock); 178 } 179 } 180 } 181 182 public void dispose() { 183 if (_fromPortal != null) { 184 _fromPortal.removePath(this); 185 } 186 if (_toPortal != null) { 187 _toPortal.removePath(this); 188 } 189 } 190 191 /** 192 * Class for defining ActionListener for ACTION_DELAYED_TURNOUT 193 */ 194 class TimeTurnout implements java.awt.event.ActionListener { 195 196 private List<BeanSetting> list; 197 private int lockState; 198 boolean set; 199 boolean lock; 200 201 public TimeTurnout() { 202 // no actions required to construct 203 } 204 205 void setList(List<BeanSetting> l) { 206 list = l; 207 } 208 209 void setParams(boolean s, int ls, boolean l) { 210 set = s; 211 lockState = ls; 212 lock = l; 213 } 214 215 @Override 216 public void actionPerformed(java.awt.event.ActionEvent event) { 217 fireTurnouts(list, set, lockState, lock); 218 // Turn Timer OFF 219 if (_timer != null) { 220 _timer.stop(); 221 _timerActive = false; 222 } 223 } 224 } 225 226 public String getDescription() { 227 StringBuilder sb = new StringBuilder("\""); 228 sb.append(_name); 229 sb.append("\" from portal \""); 230 sb.append(_fromPortal==null?"null":_fromPortal.getName()); 231 sb.append("\" to portal \""); 232 sb.append(_toPortal==null?"null":_toPortal.getName()); 233 sb.append("\""); 234 return sb.toString(); 235 } 236 237 @Override 238 public String toString() { 239 StringBuilder sb = new StringBuilder("OPath \""); 240 sb.append(_name); 241 sb.append("\" on block \""); 242 sb.append(getBlock()==null?"null":getBlock().getDisplayName()); 243 sb.append("\" from portal \""); 244 sb.append(_fromPortal==null?"null":_fromPortal.getName()); 245 sb.append("\" to portal \""); 246 sb.append(_toPortal==null?"null":_toPortal.getName()); 247 sb.append("\" sets "); 248 sb.append(getSettings().size()); 249 sb.append("\" turnouts."); 250 return sb.toString(); 251 } 252 253 /** 254 * {@inheritDoc} Does not allow duplicate settings. 255 */ 256 @Override 257 public void addSetting(BeanSetting t) { 258 for (BeanSetting bs : getSettings()) { 259 if (bs.getBeanName().equals(t.getBeanName())) { 260 log.error("TO setting for \"{}\" already set to {}", t.getBeanName(), bs.getSetting()); 261 return; 262 } 263 } 264 super.addSetting(t); 265 } 266 267 @Override 268 public int hashCode() { 269 int hash = 7; 270 hash = 67 * hash + Objects.hashCode(this.getBlock()); 271 hash = 67 * hash + Objects.hashCode(this._fromPortal); 272 hash = 67 * hash + Objects.hashCode(this._toPortal); 273 hash = 67 * hash + Objects.hashCode(this.getSettings()); 274 return hash; 275 } 276 277 /** 278 * {@inheritDoc} 279 * 280 * Override to indicate logical equality for use as paths in OBlocks. 281 */ 282 @Override 283 public boolean equals(Object obj) { 284 if (obj == this) { 285 return true; 286 } 287 if (obj == null) { 288 return false; 289 } 290 if (getClass() != obj.getClass()) { 291 return false; 292 } 293 OPath path = (OPath) obj; 294 if (getBlock() != path.getBlock()) { 295 return false; 296 } 297 Portal fromPort = path.getFromPortal(); 298 Portal toPort = path.getToPortal(); 299 int numPortals = 0; 300 if (fromPort != null) { 301 numPortals++; 302 } 303 if (toPort != null) { 304 numPortals++; 305 } 306 if (_fromPortal != null) { 307 numPortals--; 308 } 309 if (_toPortal != null) { 310 numPortals--; 311 } 312 if (numPortals != 0) { 313 return false; 314 } 315 if (_fromPortal != null && !_fromPortal.equals(fromPort) && !_fromPortal.equals(toPort)) { 316 return false; 317 } 318 if (_toPortal != null && !_toPortal.equals(toPort) && !_toPortal.equals(fromPort)) { 319 return false; 320 } 321 List<BeanSetting> settings = path.getSettings(); 322 if (settings.size() != getSettings().size()) { 323 return false; 324 } 325 Iterator<BeanSetting> iter = settings.iterator(); 326 Iterator<BeanSetting> it = getSettings().iterator(); 327 boolean found = false; 328 while (iter.hasNext()) { 329 BeanSetting beanSetting = iter.next(); 330 while (it.hasNext()) { 331 found = false; 332 BeanSetting bs = it.next(); 333 if (bs.getBean().getSystemName().equals(beanSetting.getBean().getSystemName()) 334 && bs.getSetting() == beanSetting.getSetting()) { 335 found = true; 336 break; 337 } 338 } 339 if (!found) { 340 return false; 341 } 342 } 343 return true; 344 } 345 346 private static final Logger log = LoggerFactory.getLogger(OPath.class); 347 348}