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

Revision 343, 24.1 KB checked in by jdalbey, 7 years ago (diff)

Fix defect #117. Update HighwaysTest?.java for new highway model.

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