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 @ 353

Revision 353, 24.9 KB checked in by jdalbey, 7 years ago (diff)

StationComparator?.java added to json output can be ordered by route then postmile.

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