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

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

Revision 274, 23.6 KB checked in by jdalbey, 7 years ago (diff)

Multi file commit: adding and elaborating on comments and javadoc.

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