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

Revision 237, 20.2 KB checked in by jtorres, 8 years ago (diff)

Added new package atmsdriver.batchbuilder. Includes BatchBuilderGUI, TimeFrame?, TimeFrames?, and TrafficLaneEvent? classes. Added some auxillary methods to Highways.java, Highway.java, and Station.java.

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