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

Revision 194, 17.8 KB checked in by jdalbey, 9 years ago (diff)

TrafficModelEventDriver?: Clean up code and comments. Highways.java improve formatting of toString output.

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