001package jmri.jmrit.operations.locations.schedules;
002
003import org.slf4j.Logger;
004import org.slf4j.LoggerFactory;
005
006import jmri.InstanceManager;
007import jmri.beans.PropertyChangeSupport;
008import jmri.jmrit.operations.locations.*;
009import jmri.jmrit.operations.rollingstock.cars.CarManager;
010import jmri.jmrit.operations.setup.Control;
011import jmri.jmrit.operations.trains.schedules.TrainSchedule;
012import jmri.jmrit.operations.trains.schedules.TrainScheduleManager;
013
014/**
015 * Represents one schedule item of a schedule
016 *
017 * @author Daniel Boudreau Copyright (C) 2009, 2010, 2013, 2014
018 */
019public class ScheduleItem extends PropertyChangeSupport implements java.beans.PropertyChangeListener {
020
021    public static final String NONE = ""; // NOI18N
022
023    protected String _id = NONE;
024    protected int _sequenceId = 0; // used to determine order in schedule
025    protected String _random = NONE; // used to determine if random set out is needed
026    protected String _setoutTrainScheduleId = NONE; // which day of the week to deliver car
027    protected String _type = NONE; // the type of car
028    protected String _road = NONE; // the car road
029    protected String _load = NONE; // the car load requested
030    protected String _ship = NONE; // the car load shipped
031    protected Location _destination = null; // car destination after load
032    protected Track _trackDestination = null;// car destination track after load
033    protected String _pickupTrainScheduleId = NONE; // which day of the week to pickup car
034    protected int _count = 1; // the number of times this type of car must be dropped
035    protected int _wait = 0; // how many trains this car must wait before being picked up
036    protected int _hits = 0; // how many times this schedule item has been used
037    protected String _comment = NONE;
038
039    public static final String TRAIN_SCHEDULE_CHANGED_PROPERTY = "trainScheduleId"; // NOI18N
040    public static final String COUNT_CHANGED_PROPERTY = "scheduleItemCount"; // NOI18N
041    public static final String TYPE_CHANGED_PROPERTY = "scheduleItemType"; // NOI18N
042    public static final String ROAD_CHANGED_PROPERTY = "scheduleItemRoad"; // NOI18N
043    public static final String LOAD_CHANGED_PROPERTY = "scheduleItemLoad"; // NOI18N
044    public static final String DESTINATION_CHANGED_PROPERTY = "scheduleItemDestination"; // NOI18N
045    public static final String DESTINATION_TRACK_CHANGED_PROPERTY = "scheduleItemDestinationTrack"; // NOI18N
046    public static final String WAIT_CHANGED_PROPERTY = "scheduleItemWait"; // NOI18N
047    public static final String HITS_CHANGED_PROPERTY = "scheduleItemHits"; // NOI18N
048    public static final String DISPOSE = "scheduleItemDispose"; // NOI18N
049
050    /**
051     *
052     * @param id ScheduleItem string id
053     * @param type car type for schedule
054     */
055    public ScheduleItem(String id, String type) {
056        log.debug("New schedule item, car type ({}) id: {}", type, id);
057        _type = type;
058        _id = id;
059    }
060
061    public String getId() {
062        return _id;
063    }
064
065    public String getTypeName() {
066        return _type;
067    }
068
069    /**
070     * Sets the type of car requested.
071     *
072     * @param type The car type requested.
073     */
074    public void setTypeName(String type) {
075        String old = _type;
076        _type = type;
077        firePropertyChange(TYPE_CHANGED_PROPERTY, old, type);
078    }
079
080    public String getRandom() {
081        return _random;
082    }
083
084    public void setRandom(String value) {
085        String old = _random;
086        _random = value;
087        firePropertyChange("scheduleItemRandomValueChanged", old, value); // NOI18N
088    }
089    
090    /**
091     * Method determines by random if a car is accepted by a scheduleItem
092     * 
093     * @return true if car is accepted by the scheduleItem
094     */
095    public boolean doRandom() {
096        // make an adjustment based on the number of cars of a given type
097        int numberOfCars = InstanceManager.getDefault(CarManager.class).getByTypeList(getTypeName()).size();
098        int adjustment = 100 + numberOfCars;
099        _calculatedRandom = adjustment * Math.random();
100        try {
101            int value = Integer.parseInt(getRandom());
102            log.debug("Selected random {}, created random {}", getRandom(), _calculatedRandom);
103            if (_calculatedRandom <= value) {
104                return true;
105            }
106        } catch (NumberFormatException e) {
107            log.error("Schedule item ({}) random value ({}) isn't a number", getId(), getRandom());
108        }
109        return false;
110    }
111    
112    double _calculatedRandom;
113    
114    public double getCalculatedRandom() {
115        return _calculatedRandom;
116    }
117
118    public String getSetoutTrainScheduleId() {
119        return _setoutTrainScheduleId;
120    }
121    
122    public String getSetoutTrainScheduleName() {
123        String name = "";
124        TrainSchedule sch = InstanceManager.getDefault(TrainScheduleManager.class)
125                .getScheduleById(getSetoutTrainScheduleId());
126        if (sch != null) {
127            name = sch.getName();
128        }
129        return name;
130    }
131
132    public void setSetoutTrainScheduleId(String id) {
133        String old = _setoutTrainScheduleId;
134        _setoutTrainScheduleId = id;
135        firePropertyChange(TRAIN_SCHEDULE_CHANGED_PROPERTY, old, id);
136    }
137
138    public String getPickupTrainScheduleId() {
139        return _pickupTrainScheduleId;
140    }
141    
142    public String getPickupTrainScheduleName() {
143        String name = "";
144        TrainSchedule sch = InstanceManager.getDefault(TrainScheduleManager.class)
145                .getScheduleById(getPickupTrainScheduleId());
146        if (sch != null) {
147            name = sch.getName();
148        }
149        return name;
150    }
151
152    public void setPickupTrainScheduleId(String id) {
153        String old = _pickupTrainScheduleId;
154        _pickupTrainScheduleId = id;
155        firePropertyChange(TRAIN_SCHEDULE_CHANGED_PROPERTY, old, id);
156    }
157
158    public String getRoadName() {
159        return _road;
160    }
161
162    /**
163     * Sets the requested car road name.
164     *
165     * @param road The car road requested.
166     */
167    public void setRoadName(String road) {
168        String old = _road;
169        _road = road;
170        firePropertyChange(ROAD_CHANGED_PROPERTY, old, road);
171    }
172
173    /**
174     * Sets the car load requested.
175     *
176     * @param load The load name requested.
177     */
178    public void setReceiveLoadName(String load) {
179        String old = _load;
180        _load = load;
181        firePropertyChange(LOAD_CHANGED_PROPERTY, old, load);
182    }
183
184    public String getReceiveLoadName() {
185        return _load;
186    }
187
188    /**
189     * Sets the car load that will ship.
190     *
191     * @param load The car load shipped.
192     */
193    public void setShipLoadName(String load) {
194        String old = _ship;
195        _ship = load;
196        firePropertyChange(LOAD_CHANGED_PROPERTY, old, load);
197    }
198
199    public String getShipLoadName() {
200        return _ship;
201    }
202
203    public int getSequenceId() {
204        return _sequenceId;
205    }
206
207    public void setSequenceId(int sequence) {
208        // property change not needed
209        _sequenceId = sequence;
210    }
211
212    /**
213     * How many times a car type needs to use the schedule item before going to
214     * the next item in the schedule. Used in sequential mode. Default is one.
215     * 
216     * @return the number of times a car type needs to use the schedule item
217     */
218    public int getCount() {
219        return _count;
220    }
221
222    public void setCount(int count) {
223        int old = _count;
224        _count = count;
225        firePropertyChange(COUNT_CHANGED_PROPERTY, old, count);
226    }
227
228    public int getWait() {
229        return _wait;
230    }
231
232    public void setWait(int wait) {
233        int old = _wait;
234        _wait = wait;
235        firePropertyChange(WAIT_CHANGED_PROPERTY, old, wait);
236    }
237
238    public int getHits() {
239        return _hits;
240    }
241
242    public void setHits(int hit) {
243        int old = _hits;
244        _hits = hit;
245        firePropertyChange(HITS_CHANGED_PROPERTY, old, hit);
246    }
247
248    public Location getDestination() {
249        return _destination;
250    }
251
252    public void setDestination(Location destination) {
253        Location old = _destination;
254        _destination = destination;
255        String oldName = "null"; // NOI18N
256        if (old != null) {
257            oldName = old.getName();
258        }
259        String newName = "null"; // NOI18N
260        if (_destination != null) {
261            newName = _destination.getName();
262        }
263        firePropertyChange(DESTINATION_CHANGED_PROPERTY, oldName, newName);
264    }
265
266    public String getDestinationName() {
267        if (_destination != null) {
268            return _destination.getName();
269        }
270        return NONE;
271    }
272
273    public String getDestinationId() {
274        if (_destination != null) {
275            return _destination.getId();
276        }
277        return NONE;
278    }
279
280    public Track getDestinationTrack() {
281        return _trackDestination;
282    }
283
284    public void setDestinationTrack(Track track) {
285        Track old = _trackDestination;
286        _trackDestination = track;
287        String oldName = "null"; // NOI18N
288        if (old != null) {
289            oldName = old.getName();
290        }
291        String newName = "null"; // NOI18N
292        if (_trackDestination != null) {
293            newName = _trackDestination.getName();
294        }
295        firePropertyChange(DESTINATION_TRACK_CHANGED_PROPERTY, oldName, newName);
296    }
297
298    public String getDestinationTrackName() {
299        if (_trackDestination != null) {
300            return _trackDestination.getName();
301        }
302        return NONE;
303    }
304
305    public String getDestinationTrackId() {
306        if (_trackDestination != null) {
307            return _trackDestination.getId();
308        }
309        return NONE;
310    }
311
312    public void setComment(String comment) {
313        _comment = comment;
314    }
315
316    public String getComment() {
317        return _comment;
318    }
319    
320    public void copyScheduleItem (ScheduleItem si) {
321        setComment(si.getComment());
322        setCount(si.getCount());
323        setDestination(si.getDestination());
324        setDestinationTrack(si.getDestinationTrack());
325        setPickupTrainScheduleId(si.getPickupTrainScheduleId());
326        setRandom(si.getRandom());
327        setReceiveLoadName(si.getReceiveLoadName());
328        setRoadName(si.getRoadName());
329        setSetoutTrainScheduleId(si.getSetoutTrainScheduleId());
330        setShipLoadName(si.getShipLoadName());
331        setWait(si.getWait());
332    }
333
334    public void dispose() {
335        firePropertyChange(DISPOSE, null, DISPOSE);
336    }
337
338    /**
339     * Construct this Entry from XML. This member has to remain synchronized
340     * with the detailed DTD in operations-config.xml
341     *
342     * @param e Consist XML element
343     */
344    public ScheduleItem(org.jdom2.Element e) {
345        org.jdom2.Attribute a;
346        if ((a = e.getAttribute(Xml.ID)) != null) {
347            _id = a.getValue();
348        } else {
349            log.warn("no id attribute in Schedule Item element when reading operations");
350        }
351        if ((a = e.getAttribute(Xml.SEQUENCE_ID)) != null) {
352            _sequenceId = Integer.parseInt(a.getValue());
353        }
354        if ((a = e.getAttribute(Xml.RANDOM)) != null) {
355            _random = a.getValue();
356        }
357        if ((a = e.getAttribute(Xml.TRAIN_SCHEDULE_ID)) != null) {
358            _setoutTrainScheduleId = a.getValue();
359        }
360        if ((a = e.getAttribute(Xml.PICKUP_TRAIN_SCHEDULE_ID)) != null) {
361            _pickupTrainScheduleId = a.getValue();
362        }
363        if ((a = e.getAttribute(Xml.COUNT)) != null) {
364            _count = Integer.parseInt(a.getValue());
365        }
366        if ((a = e.getAttribute(Xml.WAIT)) != null) {
367            _wait = Integer.parseInt(a.getValue());
368        }
369        if ((a = e.getAttribute(Xml.TYPE)) != null) {
370            _type = a.getValue();
371        }
372        if ((a = e.getAttribute(Xml.ROAD)) != null) {
373            _road = a.getValue();
374        }
375        if ((a = e.getAttribute(Xml.LOAD)) != null) {
376            _load = a.getValue();
377        }
378        if ((a = e.getAttribute(Xml.SHIP)) != null) {
379            _ship = a.getValue();
380        }
381        if ((a = e.getAttribute(Xml.DESTINATION_ID)) != null) {
382            _destination = InstanceManager.getDefault(LocationManager.class).getLocationById(a.getValue());
383        }
384        if ((a = e.getAttribute(Xml.DEST_TRACK_ID)) != null && _destination != null) {
385            _trackDestination = _destination.getTrackById(a.getValue());
386        }
387        if ((a = e.getAttribute(Xml.COMMENT)) != null) {
388            _comment = a.getValue();
389        }
390        if ((a = e.getAttribute(Xml.HITS)) != null) {
391            _hits = Integer.parseInt(a.getValue());
392        }
393    }
394
395    /**
396     * Create an XML element to represent this Entry. This member has to remain
397     * synchronized with the detailed DTD in operations-config.xml.
398     *
399     * @return Contents in a JDOM Element
400     */
401    public org.jdom2.Element store() {
402        org.jdom2.Element e = new org.jdom2.Element(Xml.ITEM);
403        e.setAttribute(Xml.ID, getId());
404        e.setAttribute(Xml.SEQUENCE_ID, Integer.toString(getSequenceId()));
405        e.setAttribute(Xml.RANDOM, getRandom());
406        e.setAttribute(Xml.TRAIN_SCHEDULE_ID, getSetoutTrainScheduleId());
407        e.setAttribute(Xml.PICKUP_TRAIN_SCHEDULE_ID, getPickupTrainScheduleId());
408        e.setAttribute(Xml.COUNT, Integer.toString(getCount()));
409        e.setAttribute(Xml.WAIT, Integer.toString(getWait()));
410        e.setAttribute(Xml.TYPE, getTypeName());
411        e.setAttribute(Xml.ROAD, getRoadName());
412        e.setAttribute(Xml.LOAD, getReceiveLoadName());
413        e.setAttribute(Xml.SHIP, getShipLoadName());
414        if (!getDestinationId().equals(NONE)) {
415            e.setAttribute(Xml.DESTINATION_ID, getDestinationId());
416        }
417        if (!getDestinationTrackId().equals(NONE)) {
418            e.setAttribute(Xml.DEST_TRACK_ID, getDestinationTrackId());
419        }
420        e.setAttribute(Xml.COMMENT, getComment());
421        e.setAttribute(Xml.HITS, Integer.toString(getHits()));
422        return e;
423    }
424
425    @Override
426    public void propertyChange(java.beans.PropertyChangeEvent e) {
427        if (Control.SHOW_PROPERTY) {
428            log.debug("ScheduleItem ({}) id ({}) sees property change: ({}) old: ({}) new: ({})", getTypeName(),
429                    getId(), e.getPropertyName(), e.getOldValue(), e.getNewValue()); // NOI18N
430        }
431    }
432
433    private final static Logger log = LoggerFactory.getLogger(ScheduleItem.class);
434
435}