Warning: Can't use blame annotator:
svn blame failed on trunk/src/atmsdriver/model/Station.java: ("Can't find a temporary directory: Internal error", 20014)

source: tmcsimulator/trunk/src/atmsdriver/model/Station.java @ 237

Revision 237, 12.2 KB checked in by jtorres, 8 years ago (diff)

Added new package atmsdriver.batchbuilder. Includes BatchBuilderGUI, TimeFrame?, TimeFrames?, and TrafficLaneEvent? classes. Added some auxillary methods to Highways.java, Highway.java, and Station.java.

RevLine 
1package atmsdriver.model;
2
3import atmsdriver.model.LoopDetector.DOTCOLOR;
4import java.util.ArrayList;
5import java.util.List;
6import org.w3c.dom.Document;
7import org.w3c.dom.Element;
8
9/**
10 * A Station (LDS or Loop Detector Station) represents a group of lane detectors
11 * across all lanes at a particular point on a highway. A station is identified
12 * by its highway number and postmile. A station has an associated direction
13 * used to establish which direction is Main and which is Opposite. The MLTotVol
14 * and OppTotVol for a station can be dynamically updated. A station has other
15 * attributes: lineNum, ldsID, drop, and location which are used by the FEP. A
16 * station can be compared to other stations by its postmile.
17 *
18 * @author John A. Torres
19 * @version 9/10/2017
20 */
21public class Station implements Comparable
22{
23
24    /* Static Station meta data */
25    final public int lineID;
26    final public int ldsID; // double check
27    final public int drop;
28    final public String location;
29    final public List<LoopDetector> loops;
30    final public int routeNumber;
31    final public double postmile;
32    final public DIRECTION direction;
33
34    /* Dynamic Station data */
35    private int MLTotVol;
36    private int OppTotVol;
37
38    /* Constructor */
39    public Station(int lineID, int ldsID, int drop,
40            String location, List<LoopDetector> loops, int hwy,
41            DIRECTION direction, double postmile)
42    {
43        this.lineID = lineID;
44        this.ldsID = ldsID;
45        this.drop = drop;
46        this.loops = loops;
47        this.location = location;
48        this.postmile = postmile;
49        this.direction = direction;
50        this.routeNumber = hwy;
51
52        this.MLTotVol = getMLTotVol();
53        this.OppTotVol = getOPPTotVol();
54    }
55
56    /**
57     * Calculates the total ML Volume.
58     *
59     * @return total ML volume.
60     */
61    private int getMLTotVol()
62    {
63        int mlTotVol = 0;
64        for (LoopDetector loop : loops)
65        {
66            if (loop.loopLocation.startsWith("ML"))
67            {
68                mlTotVol += loop.vol;
69            }
70        }
71        return mlTotVol;
72    }
73
74    /**
75     * Calculates the total OPP Volume
76     *
77     * @return total OPP volume.
78     */
79    private int getOPPTotVol()
80    {
81        int oppTotVol = 0;
82        for (LoopDetector loop : loops)
83        {
84            if (loop.loopLocation.startsWith("OS"))
85            {
86                oppTotVol += loop.vol;
87            }
88        }
89        return oppTotVol;
90    }
91
92    /**
93     * Returns a string of highways data. If MetaDataOnly is true, you get a
94     * full dump of the highways meta data, which does not include dynamic loop
95     * values, and does include the string location names. If MetaDataOnly is
96     * false, dynamic loop values are included, and unnecessary information like
97     * string location values are included.
98     *
99     * The FEPSimulator takes in the toCondensedFormat() output, with a
100     * MetaDataOnly value of false, over the socket.
101     *
102     * The MetaDataOnly flag should be used to get a full dump of the highways
103     * information. This was used to get the highways_fullmap.txt output.
104     *
105     * @param MetaDataOnly Whether you want meta data, or a full dump for FEPSim
106     * @return String, highways data in condensed format
107     */
108    public String toCondensedFormat(boolean MetaDataOnly)
109    {
110        StringBuilder build = new StringBuilder();
111        build.append(Integer.toString(this.ldsID));
112        build.append(" ");
113        build.append(Integer.toString(this.drop));
114        build.append(" ");
115        build.append(Integer.toString(this.routeNumber));
116        build.append(" ");
117        build.append(this.direction.getLetter());
118        build.append(" ");
119        build.append(Double.toString(this.postmile));
120        build.append(" ");
121        build.append(Integer.toString(loops.size()));
122        build.append(" ");
123        if (MetaDataOnly)
124        {
125            build.append(this.location);
126        }
127        build.append("\n");
128        for (LoopDetector loop : loops)
129        {
130            build.append(loop.toCondensedFormat(MetaDataOnly));
131        }
132        return build.toString();
133    }
134
135    /**
136     * Compare this Station to another by postmile. Note: This might be better
137     * as a Comparator since it checks only one field.
138     */
139    @Override
140    public int compareTo(Object otherStation)
141    {
142        // check for identity
143        if (this == otherStation)
144        {
145            return 0;
146        }
147        // check that Object is of type Station, if not throw exception
148        if (!(otherStation instanceof Station))
149        {
150            throw new ClassCastException("A Station object expected.");
151        }
152
153        // get difference of values
154        double otherStationPostmile = ((Station) otherStation).postmile;
155        double val = this.postmile - otherStationPostmile;
156
157        // set appropriate comparable return value
158        int retval = 0;
159        if (val > 0)
160        {
161            retval = 1;
162        }
163        else if (val < 0)
164        {
165            retval = -1;
166        }
167
168        return retval;
169    }
170
171    /**
172     * Determine which lane fields to update based on given direction and update
173     * all the loop detectors with the given color.
174     *
175     * @param direction desired highway direction
176     * @param dotColor desired dot color
177     */
178    public void updateByDirection(DIRECTION direction, DOTCOLOR dotColor)
179    {
180        String laneDir = "OS";
181        if (direction.equals(this.direction))
182        {
183            laneDir = "ML";
184        }
185        outputUpdateMessage(dotColor, laneDir);
186
187        for (LoopDetector loop : loops)
188        {
189            if (loop.loopLocation.startsWith(laneDir))
190            {
191                // UPDATE LOOP WITH VALUES
192                loop.updateLoop(dotColor.volume(), dotColor.occupancy());
193            }
194        }
195
196        this.MLTotVol = getMLTotVol();
197        this.OppTotVol = getOPPTotVol();
198    }
199   
200    /**
201     * Return the color for the lanes in a given direction.
202     *
203     * @param direction
204     * @return character representing color of lanes in given direction '@' =
205     * red, '+' = yellow, '-' = green
206     */
207    public char getColorByDirection(DIRECTION direction)
208    {
209        /* For now just use the color of the first lane.
210         * TODO: Average the color in all the lanes for the given direction */
211
212        String laneDir = "";
213        if (direction.equals(this.direction))
214        {
215            laneDir = "ML";
216        }
217        else if (direction.equals(this.direction.getOpposite()))
218        {
219            laneDir = "OS";
220        }
221        // Examine all the lanes in a given direction
222        for (LoopDetector loop : loops)
223        {
224            if (loop.loopLocation.substring(0,2).equals(laneDir))
225            {
226                // Return color according to loop volume
227                if (loop.vol == 1)
228                {
229                    return '@';
230                }
231                if (loop.vol == 3)
232                {
233                    return '+';
234                }
235                if (loop.vol == 0)
236                {
237                    return '-';
238                }
239            }
240        }
241        // Default case for when the route is not on this direction
242        return ' ';
243
244    }
245
246    /**
247     * Output for updateByDirection. Logs the update to the console.
248     *
249     * @param dotcolor
250     * @param OPP_ML
251     */
252    private void outputUpdateMessage(DOTCOLOR dotcolor, String OPP_ML)
253    {
254        System.out.printf("Updating %-3.3s %-5.5s %-3.3s lanes\t %-12.12s "
255                + "at postmile %-6.6s to %-7.7s\n",
256                Integer.toString(this.routeNumber), this.direction.name(),
257                OPP_ML, this.location, Double.toString(this.postmile),
258                dotcolor.name());
259    }
260
261    /**
262     * XML tags used for toXML() method.
263     */
264    private static enum XML_TAGS
265    {
266
267        STATION("Station"),
268        LDS_ID("LDS_ID"),
269        LINE_NUM("Line_Num"),
270        DROP("Drop"),
271        LOOPS("Loops"),
272        LOCATION("Location"),
273        POST_MILE("Post_Mile"),
274        DIRECTION("Direction"),
275        FREEWAY("Freeway"),
276        ML_TOT_VOL("ML_Tot_Vol"),
277        OPP_TOT_VOL("Opp_Tot_Vol");
278
279        String tag;
280
281        private XML_TAGS(String n)
282        {
283            tag = n;
284        }
285    }
286
287    /**
288     * Returns the Station data in XMLFormat.
289     *
290     * @param currElem The current XML <Station> element
291     */
292    public void toXML(Element currElem)
293    {
294        Document theDoc = currElem.getOwnerDocument();
295
296        Element stationElement = theDoc.createElement(XML_TAGS.STATION.tag);
297        currElem.appendChild(stationElement);
298
299        Element ldsIDElement = theDoc.createElement(XML_TAGS.LDS_ID.tag);
300        ldsIDElement.appendChild(theDoc.createTextNode(String.valueOf(this.ldsID)));
301        stationElement.appendChild(ldsIDElement);
302
303        Element lineNumElement = theDoc.createElement(XML_TAGS.LINE_NUM.tag);
304        lineNumElement.appendChild(theDoc.createTextNode(String.valueOf(this.lineID)));
305        stationElement.appendChild(lineNumElement);
306
307        Element dropElement = theDoc.createElement(XML_TAGS.DROP.tag);
308        dropElement.appendChild(theDoc.createTextNode(String.valueOf(this.drop)));
309        stationElement.appendChild(dropElement);
310
311        Element locationElement = theDoc.createElement(XML_TAGS.LOCATION.tag);
312        locationElement.appendChild(theDoc.createTextNode(this.location));
313        stationElement.appendChild(locationElement);
314
315        Element postMileElement = theDoc.createElement(XML_TAGS.POST_MILE.tag);
316        postMileElement.appendChild(theDoc.createTextNode(String.valueOf(this.postmile)));
317        stationElement.appendChild(postMileElement);
318
319        Element directionElement = theDoc.createElement(XML_TAGS.DIRECTION.tag);
320        directionElement.appendChild(theDoc.createTextNode("" + this.direction.getLetter()));
321        stationElement.appendChild(directionElement);
322
323        Element freewayElement = theDoc.createElement(XML_TAGS.FREEWAY.tag);
324        freewayElement.appendChild(theDoc.createTextNode(String.valueOf(this.routeNumber)));
325        stationElement.appendChild(freewayElement);
326
327        Element mlElement = theDoc.createElement(XML_TAGS.ML_TOT_VOL.tag);
328        mlElement.appendChild(theDoc.createTextNode(String.valueOf(this.MLTotVol)));
329        stationElement.appendChild(mlElement);
330
331        Element oppElement = theDoc.createElement(XML_TAGS.OPP_TOT_VOL.tag);
332        oppElement.appendChild(theDoc.createTextNode(String.valueOf(this.OppTotVol)));
333        stationElement.appendChild(oppElement);
334
335        Element loopsElement = theDoc.createElement(XML_TAGS.LOOPS.tag);
336        stationElement.appendChild(loopsElement);
337
338        for (LoopDetector loop : loops)
339        {
340            loop.toXML(loopsElement);
341        }
342    }
343
344    /**
345     * Enum for freeway direction.
346     *
347     * @author John A. Torres
348     * @version 9/10/2017
349     */
350    public static enum DIRECTION
351    {
352        NORTH,
353        SOUTH,
354        EAST,
355        WEST;
356
357        // All the first letters of the values, in order.
358        private static String allLetters = "NSEW";
359
360        /**
361         * Return the first letter of this enum.
362         *
363         * @return String first letter of this enum.
364         */
365        public String getLetter()
366        {
367            return this.toString().substring(0, 1);
368        }
369       
370        public DIRECTION getOpposite()
371        {
372            switch (this)
373            {
374                case NORTH:
375                    return SOUTH;
376                case SOUTH:
377                    return NORTH;
378                case EAST:
379                    return WEST;
380                case WEST:
381                    return EAST;
382            }
383            return null;
384        }
385
386        /**
387         * Returns a direction given its first character.
388         *
389         * @param letter the first character of a direction
390         * @return direction corresponding to letter
391         * @pre letter must be one of allLetters
392         */
393        public static DIRECTION toDirection(String letter)
394        {
395            if(letter.indexOf(letter.charAt(0)) == -1)
396            {
397                return null;
398            }
399            return values()[allLetters.indexOf(letter.charAt(0))];
400        }
401    }
402   
403    @Override
404    public String toString()
405    {
406        return Integer.toString(this.ldsID);
407    }
408}
Note: See TracBrowser for help on using the repository browser.