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

Revision 191, 11.2 KB checked in by jdalbey, 9 years ago (diff)

Highways.java: Added toString() method. Station.java: Added getColorByDirection method. Added testToString to HighwaysTest?.

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