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

Revision 228, 18.7 KB checked in by jdalbey, 8 years ago (diff)

System Tests updated to work with new integrated Traffic Mgr. All system tests now passing.

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 (java.net.ConnectException ex)
353        {
354            //Logger.getLogger(Highways.class.getName()).log(Level.SEVERE, null, ex);
355            System.out.println("writeToFEP() can't connect, no data sent to FEP.");
356            throw new SimulationException(SimulationException.BINDING);
357        } catch (IOException ex)
358        {
359            //Logger.getLogger(Highways.class.getName()).log(Level.SEVERE, null, ex);
360            System.out.println("Highway Model failed writing to FEPSim.");
361            throw new SimulationException(SimulationException.BINDING);
362        }
363    }
364   
365    /** Returns a string of highways data. If MetaDataOnly is true, you get a full
366     *  dump of the highways meta data, which does not include dynamic loop values,
367     *  and does include the string location names. If MetaDataOnly is false,
368     *  dynamic loop values are included, and unnecessary information like string
369     *  location values are included.
370     *
371     *  The FEPSimulator takes in the toCondensedFormat() output, with a MetaDataOnly
372     *  value of false, over the socket.
373     *
374     *  The MetaDataOnly flag should be used to get a full dump of the highways
375     *  information. This was used to get the highways_fullmap.txt output.
376     *
377     * @param MetaDataOnly Whether you want meta data, or a full dump for FEPSim
378     * @return String, highways data in condensed format
379     *
380     * Example toCondensedFormat(MetaDataOnly = false) output:
381     *
382     * 43                       // "number of lines"
383     * 32 0 13                  // "line id" "count num" "number of stations"
384     * 1210831 1 5 S 0.9 8      // "station id" "drop num" "route num"...
385     *                          //      ..."direction" "postmile" "number of loops"
386     * 1210832  0.0 0  ML_1     // "loop id" "occ" "vol"
387     * 1210833  0.0 0  ML_2     // ..
388     * 1210834  0.0 0  ML_3     // ..
389     * 1210835  0.0 0  ML_4     // ..
390     * 1210836  0.0 0  PASSAGE  // ..
391     * 1210837  0.0 0  DEMAND   // ..
392     * 1210838  0.0 0  QUEUE    // ..
393     * 1210839  0.0 0  RAMP_OFF // ..
394     * ...
395     *
396     * Example toCondensedFormat(MetaDataOnly = true) output:
397     *
398     * 43                           // "number of lines"
399     * 32 0 13                      // "line id" "count num" "number of stations"
400     * 1210831 1 5 S 0.9 8 CALAFIA  // "station id" "drop num" "route num"...
401     *                              //      ..."direction" "postmile"...
402     *                              //      ..."number of loops" "string location"
403     * 1210832 ML_1                 // "loop id" "loop location"
404     * 1210833 ML_2                 // "            "
405     * 1210834 ML_3                 // "            "
406     * 1210835 ML_4                 // "            "
407     * 1210836 PASSAGE              // "            "
408     * 1210837 DEMAND               // "            "
409     * 1210838 QUEUE                // "            "
410     * 1210839 RAMP_OFF             // "            "
411     * ...
412     */
413    public String toCondensedFormat(boolean MetaDataOnly)
414    {
415        // first line: number of FEPLines
416        StringBuilder build = new StringBuilder();
417        build.append(lines.size());
418        build.append("\n");
419        // append each fep line to the string
420        for(FEPLine line : lines)
421        {
422            build.append(line.toCondensedFormat(MetaDataOnly));
423        }
424        // return the full condensed format string
425        return build.toString();
426    }
427   
428    /**
429     * Returns the Highways model data in XML format.
430     *
431     * @return highways data in XML format
432     */
433    public String toXML()
434    {
435        String xml = null;
436        try
437        {
438            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
439            DocumentBuilder builder = factory.newDocumentBuilder();
440            Document theDoc = builder.newDocument();
441
442            Element networkElement = theDoc.createElement(XML_TAGS.NETWORK.tag);
443            theDoc.appendChild(networkElement);
444
445            for (FEPLine line : lines)
446            {
447                line.toXML(networkElement);
448            }
449
450            Transformer tf = TransformerFactory.newInstance().newTransformer();
451
452            tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
453            tf.setOutputProperty(OutputKeys.INDENT, "yes");
454            tf.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
455
456            Writer out = new StringWriter();
457            tf.transform(new DOMSource(theDoc), new StreamResult(out));
458            xml = out.toString();
459            out.close();
460        } catch (Exception ex)
461        {
462            Logger.getLogger(Highways.class.getName()).log(Level.SEVERE, null, ex);
463        }
464        return xml;
465
466    }
467
468    /**
469     * Returns a highway by given highway number.
470     *
471     * @param routeNum
472     * @return Highway with specified route number, or null if no highway with
473     *          the specified route num
474     */
475    public Highway getHighwayByRouteNumber(Integer routeNum)
476    {
477        Highway returnHwy = null;
478        // search through highways and check routeNums
479        for (Highway hwy : highways)
480        {
481            if (hwy.routeNumber.equals(routeNum))
482            {
483                returnHwy = hwy;
484                break;
485            }
486        }
487        return returnHwy;
488    }
489
490    /** Return a string representation of the Highways */
491    public String toString()
492    {
493        StringBuilder result = new StringBuilder();
494        for (Highway hwy: highways)
495        {
496            // Consider each route direction
497            for (DIRECTION dir: DIRECTION.values())
498            {
499                String rowLabel = ""+String.format("%3s ",hwy.routeNumber)+dir.getLetter()+' ';
500                StringBuilder lineout = new StringBuilder();
501                // Examine every station on this highway and direction
502                for (Station stat: hwy.stations)
503                {
504                    //lineout.append("" + dir.getLetter() + stat.postmile);
505                    lineout.append(stat.getColorByDirection(dir));
506                    //lineout.append("  ");
507                }
508                // See if there were stations for this direction
509                String checkMe = lineout.toString().trim();
510                // if any stations were colored, output the line
511                if (checkMe.length() > 1)
512                {
513                    result.append(rowLabel);
514                    result.append(lineout + "\n");
515                }
516            }
517        }
518        result.append("\n");
519        return result.toString();
520    }
521    /**
522     * XML tags used in writeToXML()
523     */
524    private static enum XML_TAGS
525    {
526
527        NETWORK("Network");
528
529        String tag;
530
531        private XML_TAGS(String n)
532        {
533            tag = n;
534        }
535    }
536}
Note: See TracBrowser for help on using the repository browser.