source: tmcsimulator/trunk/src/atmsdriver/model/Highways.java @ 212

Revision 212, 18.4 KB checked in by jtorres, 9 years ago (diff)

branches/FEPSimulator/tests/HighwaysParserTest.cpp: Unit test for HighwaysParser?.cpp. HighwaysTest?.java: now passing toXML(), added test for toCondensedFormat() method. LoadHighwaysTest?.java: passing. LoadSadDotsTest?.java: conformed to changes, not passing. Highway.java/Highways.java: converted class attributes to List instead of ArrayList?. Highways.java: corrected toCondensedFormat() method comments and added comments throughout file.

Line 
1package atmsdriver.model;
2
3import atmsdriver.model.Station.DIRECTION;
4import java.io.File;
5import java.io.FileNotFoundException;
6import java.io.IOException;
7import java.io.PrintWriter;
8import java.io.StringWriter;
9import java.io.Writer;
10import java.net.Socket;
11import java.util.ArrayList;
12import java.util.Collections;
13import java.util.HashMap;
14import java.util.List;
15import java.util.Map;
16import java.util.Scanner;
17import java.util.Set;
18import java.util.logging.Level;
19import java.util.logging.Logger;
20import javax.xml.parsers.DocumentBuilder;
21import javax.xml.parsers.DocumentBuilderFactory;
22import javax.xml.transform.OutputKeys;
23import javax.xml.transform.Transformer;
24import javax.xml.transform.TransformerFactory;
25import javax.xml.transform.dom.DOMSource;
26import javax.xml.transform.stream.StreamResult;
27import org.w3c.dom.Document;
28import org.w3c.dom.Element;
29import tmcsim.common.SimulationException;
30
31/**
32 * The Highways class aggregates all Highway instances within a geographic
33 * region, and all of the FEPLines within an electronic detector network, in the
34 * same geographic region. An instance of Highways.java comprises the underlying
35 * model for the ATMSDriver application.
36 *
37 * Highways uses method writeToFEP() to communicate with the FEP Simulator. It
38 * creates a socket client which sends the FEP Simulator a highways status
39 * message over the socket. This message is sent in the format required by the
40 * FEP Simulator.
41 *
42 * Currently, there is no driving logic within the highways class. It is only
43 * done through an instance of the ConsoleDriver.java class. Eventually, it will
44 * receive incident data (from exchange.xml or better yet, directly from the
45 * TMCSimulator itself) and drive the highways using resident logic.
46 *
47 * @author John A. Torres
48 */
49final public class Highways
50{
51
52    final private String FEPHostName;
53    final private int FEPPortNum;
54   
55    final private List<FEPLine> lines;
56    final public List<Highway> highways;
57
58    public Highways(String highwaysMapFileName, String FEPHostName, int FEPPortNum)
59    {
60        // load FEP Lines
61        lines = loadLines(highwaysMapFileName);
62        // configure and load highways
63        this.highways = configureHighways();
64
65        // write to FEP host and port number
66        this.FEPHostName = FEPHostName;
67        this.FEPPortNum = FEPPortNum;
68    }
69
70    private ArrayList<Highway> configureHighways()
71    {
72        System.out.println("Loading highways...");
73        // The list of highways to return
74        ArrayList<Highway> highways = new ArrayList<Highway>();
75       
76        // map of hwy number to its list of stations
77        Map<Integer, ArrayList<Station>> highwayMap = new HashMap<>();
78       
79        // iterate through FEPLines and get data to add to the above map
80        for (FEPLine line : lines)
81        {
82            // grab all stations from the current FEPLine
83            ArrayList<Station> lineStations = (ArrayList<Station>) line.stations;
84            // iterate through each station in the list of stations
85            for (Station station : lineStations)
86            {
87                Integer hwyNum = station.routeNumber;
88               
89                // if the map does not contain an entry for the highway, create
90                // a new entry (key/value pair) for the highway and instantiate
91                // the empty list of stations
92                if (!highwayMap.containsKey(hwyNum))
93                {
94                    ArrayList<Station> stnList = new ArrayList<>();
95                    stnList.add(station);
96                    highwayMap.put(hwyNum, stnList);
97                } 
98                // if the map does have an entry for the highway, add the current
99                // station to its list of stations
100                else
101                {
102                    highwayMap.get(hwyNum).add(station);
103                }
104            }
105        }
106       
107        // get the set of highway numbers
108        Set<Integer> hwyKeys = highwayMap.keySet();
109        // get the highway number and associated stations and create a new hwy
110        // and add the hwy to this.highways
111        for (Integer hwyKey : hwyKeys)
112        {
113            ArrayList<Station> hwyStations = highwayMap.get(hwyKey);
114            Collections.sort(hwyStations);
115            System.out.println("Loaded highway " + hwyKey + "...");
116            highways.add(new Highway(hwyKey,
117                    hwyStations));
118        }
119        System.out.println("");
120        return highways;
121    }
122
123    /**
124     * Applies specified color to the specified highway stretch. Route number
125     * and direction specify the highway. Postmile and range specify the stretch
126     * of specified highway. Dot color is the color to be applied to the
127     * stretch.
128     *
129     * @param routeNumber highway route number
130     * @param direction highway direction
131     * @param postmile origin postmile value
132     * @param range range from origin postmile
133     * @param dotColor the color to be applied to specified highway stretch
134     */
135    public void applyColorToHighwayStretch(Integer routeNumber, Station.DIRECTION direction,
136            Double postmile, Double range, LoopDetector.DOTCOLOR dotColor)
137    {
138        System.out.println("Applying " + dotColor.name() + " dots to highway "
139                + routeNumber + " " + direction.name() + " at postmile "
140                + postmile + " with a range of " + range + " miles...");
141
142        // Get the highway by route number
143        Highway highway = getHighwayByRouteNumber(routeNumber);
144
145        // start value for highway section, and end value for highway section
146        // by postmile
147        Double startPost;
148        Double endPost;
149
150        // postmiles increase from s to n and w to e
151        // if the direction is south or west
152        if (direction.equals(Station.DIRECTION.SOUTH) || direction.equals(Station.DIRECTION.WEST))
153        {
154            // add range value to startPost to get
155            // the end postmile value of the highway section
156            startPost = postmile;
157            endPost = postmile + range;
158
159            // iterate through the stations, if within the specified highway
160            // stretch, update the station by direction and apply dot color
161            for (Station station : highway.stations)
162            {
163                if (station.postmile >= startPost && station.postmile <= endPost)
164                {
165                    station.updateByDirection(direction, dotColor);
166                }
167            }
168        } // if the direction is north or east
169        else
170        {
171            // subtract range value from startPost
172            // to get the end postmile value of the highway section
173            startPost = postmile;
174            endPost = postmile - range;
175
176            // iterate through the stations, if within the specified highway
177            // section, update the station by direction and apply dot color
178            for (Station station : highway.stations)
179            {
180                if (station.postmile <= startPost && station.postmile >= endPost)
181                {
182                    station.updateByDirection(direction, dotColor);
183                }
184            }
185        }
186        System.out.println("");
187    }
188   
189    /**
190     * Loads all FEPLines from the specified highways map file.
191     *
192     * @param highwaysMapFileName
193     * @return List of FEPLines
194     */
195    private ArrayList<FEPLine> loadLines(String highwaysMapFileName)
196    {
197        ArrayList<FEPLine> lines = new ArrayList<>();
198        try
199        {
200            Scanner sc = new Scanner(new File(highwaysMapFileName));
201            // first line of file contains number of FEP Lines
202            String firstLine = sc.nextLine();
203            Scanner linesc = new Scanner(firstLine);
204            int numLines = linesc.nextInt();
205            linesc.close();
206            // FOR each FEP Line
207            for (int i = 0; i < numLines; i++)
208            {
209                lines.add(loadLine(sc));
210            }
211            sc.close();
212
213        } catch (FileNotFoundException ex)
214        {
215            Logger.getLogger(Highways.class.getName()).log(Level.SEVERE, null, ex);
216        }
217        return lines;
218    }
219   
220    /**
221     * Loads a single FEP Line from the highways map file.
222     *
223     * @param sc scanner at the current FEPLine line
224     * @return FEPLine
225     */
226    private FEPLine loadLine(Scanner sc)
227    {
228        String line = sc.nextLine();
229        Scanner scline = new Scanner(line);
230        // Get the attributes of this FEP Line
231        int lineNum = scline.nextInt();
232        int count = scline.nextInt();
233        int numStations = scline.nextInt();
234       
235        // initialze stations array
236        ArrayList<Station> stations = new ArrayList<>();
237        // Read all the stations for thie FEP Line
238        for (int i = 0; i < numStations; i++)
239        {
240            stations.add(loadStation(sc, lineNum));
241        }
242
243        return new FEPLine(lineNum, stations, count);
244    }
245   
246    /**
247     * Loads a single Station from the highways map file
248     * @param sc scanner at the current station line
249     * @param lineNum the FEPLine number for the station
250     * @return Station
251     */
252    private Station loadStation(Scanner sc, int lineNum)
253    {
254        String line = sc.nextLine();
255        Scanner scline = new Scanner(line);
256       
257        int ldsID = scline.nextInt();
258        int drop = scline.nextInt();
259        int fwy = scline.nextInt();
260        DIRECTION dir = DIRECTION.toDirection(scline.next());
261        double postmile = scline.nextDouble();
262        int numLoops = scline.nextInt();
263        String location = getStationLoc(line);
264        ArrayList<LoopDetector> loops = new ArrayList<>();
265        for (int i = 0; i < numLoops; i++)
266        {
267            loops.add(loadLoop(sc));
268        }
269
270        return new Station(lineNum, ldsID, drop, location, loops, fwy, dir, postmile);
271    }
272   
273    /**
274     * Loads a single loop from the highways map file
275     *
276     * @param sc scanner at the current loop line
277     * @return LoopDetector
278     */
279    private LoopDetector loadLoop(Scanner sc)
280    {
281        String line = sc.nextLine();
282        Scanner scline = new Scanner(line);
283
284        int loopID = scline.nextInt();
285        String loopLoc = getLoopLoc(line);
286        scline.close();
287        return new LoopDetector(loopID, loopLoc);
288    }
289
290    /**
291     * Scans the LoopDetector line and grabs the String location from the line.
292     *
293     * @param line the line containing the location
294     * @return A String loop location.
295     */
296    private String getLoopLoc(String line)
297    {
298        Scanner sc = new Scanner(line);
299        sc.nextInt();
300
301     // GRABS FROM CURRENT TO END OF LINE
302        sc.useDelimiter("\\z");
303        String loc = sc.next().trim();
304        sc.close();
305        return loc;
306    }
307
308    /**
309     * Scans the Station line and grabs the String location from the line.
310     *
311     * @param line the line containing the location
312     * @return A String station location.
313     */
314    private String getStationLoc(String line)
315    {
316        Scanner scline = new Scanner(line);
317        scline.nextInt();
318        scline.nextInt();
319        scline.nextInt();
320        scline.next();
321        scline.nextDouble();
322        scline.nextInt();
323
324        // GRABS FROM CURRENT TO END OF LINE
325        scline.useDelimiter("\\z");
326        String loc = scline.next().trim();
327        scline.close();
328        return loc;
329    }
330   
331    /**
332     * Creates a socket client that writes the Highways data to the FEP Simulator.
333     *
334     * @throws SimulationException
335     */
336    public void writeToFEP() throws SimulationException
337    {
338        try
339        {
340            // Create the socket to the FEP Simulator
341            Socket sock = new Socket(FEPHostName, FEPPortNum);
342            PrintWriter out = new PrintWriter(sock.getOutputStream(), true);
343           
344            // Print the number of bytes the highways data message contains
345            System.out.println("BYTES: " + this.toCondensedFormat(false).toCharArray().length + 1);
346           
347            // Write the highways data over the socket
348            out.println(this.toCondensedFormat(false));
349           
350            // close the socket
351            sock.close();
352        } catch (IOException ex)
353        {
354            Logger.getLogger(Highways.class.getName()).log(Level.SEVERE, null, ex);
355            System.out.println("Highway Model failed writing to FEPSim.");
356            throw new SimulationException(SimulationException.BINDING);
357        }
358    }
359   
360    /** Returns a string of highways data. If MetaDataOnly is true, you get a full
361     *  dump of the highways meta data, which does not include dynamic loop values,
362     *  and does include the string location names. If MetaDataOnly is false,
363     *  dynamic loop values are included, and unnecessary information like string
364     *  location values are included.
365     *
366     *  The FEPSimulator takes in the toCondensedFormat() output, with a MetaDataOnly
367     *  value of false, over the socket.
368     *
369     *  The MetaDataOnly flag should be used to get a full dump of the highways
370     *  information. This was used to get the highways_fullmap.txt output.
371     *
372     * @param MetaDataOnly Whether you want meta data, or a full dump for FEPSim
373     * @return String, highways data in condensed format
374     *
375     * Example toCondensedFormat(MetaDataOnly = false) output:
376     *
377     * 43                       // "number of lines"
378     * 32 0 13                  // "line id" "count num" "number of stations"
379     * 1210831 1 5 S 0.9 8      // "station id" "drop num" "route num"...
380     *                          //      ..."direction" "postmile" "number of loops"
381     * 1210832  0.0 0  ML_1     // "loop id" "occ" "vol"
382     * 1210833  0.0 0  ML_2     // ..
383     * 1210834  0.0 0  ML_3     // ..
384     * 1210835  0.0 0  ML_4     // ..
385     * 1210836  0.0 0  PASSAGE  // ..
386     * 1210837  0.0 0  DEMAND   // ..
387     * 1210838  0.0 0  QUEUE    // ..
388     * 1210839  0.0 0  RAMP_OFF // ..
389     * ...
390     *
391     * Example toCondensedFormat(MetaDataOnly = true) output:
392     *
393     * 43                           // "number of lines"
394     * 32 0 13                      // "line id" "count num" "number of stations"
395     * 1210831 1 5 S 0.9 8 CALAFIA  // "station id" "drop num" "route num"...
396     *                              //      ..."direction" "postmile"...
397     *                              //      ..."number of loops" "string location"
398     * 1210832 ML_1                 // "loop id" "loop location"
399     * 1210833 ML_2                 // "            "
400     * 1210834 ML_3                 // "            "
401     * 1210835 ML_4                 // "            "
402     * 1210836 PASSAGE              // "            "
403     * 1210837 DEMAND               // "            "
404     * 1210838 QUEUE                // "            "
405     * 1210839 RAMP_OFF             // "            "
406     * ...
407     */
408    public String toCondensedFormat(boolean MetaDataOnly)
409    {
410        // first line: number of FEPLines
411        StringBuilder build = new StringBuilder();
412        build.append(lines.size());
413        build.append("\n");
414        // append each fep line to the string
415        for(FEPLine line : lines)
416        {
417            build.append(line.toCondensedFormat(MetaDataOnly));
418        }
419        // return the full condensed format string
420        return build.toString();
421    }
422   
423    /**
424     * Returns the Highways model data in XML format.
425     *
426     * @return highways data in XML format
427     */
428    public String toXML()
429    {
430        String xml = null;
431        try
432        {
433            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
434            DocumentBuilder builder = factory.newDocumentBuilder();
435            Document theDoc = builder.newDocument();
436
437            Element networkElement = theDoc.createElement(XML_TAGS.NETWORK.tag);
438            theDoc.appendChild(networkElement);
439
440            for (FEPLine line : lines)
441            {
442                line.toXML(networkElement);
443            }
444
445            Transformer tf = TransformerFactory.newInstance().newTransformer();
446
447            tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
448            tf.setOutputProperty(OutputKeys.INDENT, "yes");
449            tf.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
450
451            Writer out = new StringWriter();
452            tf.transform(new DOMSource(theDoc), new StreamResult(out));
453            xml = out.toString();
454            out.close();
455        } catch (Exception ex)
456        {
457            Logger.getLogger(Highways.class.getName()).log(Level.SEVERE, null, ex);
458        }
459        return xml;
460
461    }
462
463    /**
464     * Returns a highway by given highway number.
465     *
466     * @param routeNum
467     * @return Highway with specified route number, or null if no highway with
468     *          the specified route num
469     */
470    public Highway getHighwayByRouteNumber(Integer routeNum)
471    {
472        Highway returnHwy = null;
473        // search through highways and check routeNums
474        for (Highway hwy : highways)
475        {
476            if (hwy.routeNumber.equals(routeNum))
477            {
478                returnHwy = hwy;
479                break;
480            }
481        }
482        return returnHwy;
483    }
484
485    /** Return a string representation of the Highways */
486    public String toString()
487    {
488        StringBuilder result = new StringBuilder();
489        for (Highway hwy: highways)
490        {
491            // Consider each route direction
492            for (DIRECTION dir: DIRECTION.values())
493            {
494                String rowLabel = ""+String.format("%3s ",hwy.routeNumber)+dir.getLetter()+' ';
495                StringBuilder lineout = new StringBuilder();
496                // Examine every station on this highway and direction
497                for (Station stat: hwy.stations)
498                {
499                    //lineout.append("" + dir.getLetter() + stat.postmile);
500                    lineout.append(stat.getColorByDirection(dir));
501                    //lineout.append("  ");
502                }
503                // See if there were stations for this direction
504                String checkMe = lineout.toString().trim();
505                // if any stations were colored, output the line
506                if (checkMe.length() > 1)
507                {
508                    result.append(rowLabel);
509                    result.append(lineout + "\n");
510                }
511            }
512        }
513        result.append("\n");
514        return result.toString();
515    }
516    /**
517     * XML tags used in writeToXML()
518     */
519    private static enum XML_TAGS
520    {
521
522        NETWORK("Network");
523
524        String tag;
525
526        private XML_TAGS(String n)
527        {
528            tag = n;
529        }
530    }
531}
Note: See TracBrowser for help on using the repository browser.