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

source: tmcsimulator/trunk/src/tmcsim/client/ATMSBatchDriver.java @ 140

Revision 140, 18.1 KB checked in by jdalbey, 9 years ago (diff)

ATMSBatchDriver: Added comments to events file and changed range to decimal.

RevLine 
1package tmcsim.client;
2
3import atmsdriver.ATMSDriver;
4import atmsdriver.ConsoleDriver;
5import atmsdriver.ExchangeInfo;
6import atmsdriver.model.Highways;
7import atmsdriver.model.Station;
8import java.awt.event.ActionEvent;
9import java.awt.event.ActionListener;
10import java.io.FileInputStream;
11import java.io.FileNotFoundException;
12import java.rmi.Naming;
13import java.rmi.RemoteException;
14import java.rmi.server.UnicastRemoteObject;
15import java.text.ParseException;
16import java.text.SimpleDateFormat;
17import java.util.ArrayList;
18import java.util.Date;
19import java.util.HashMap;
20import java.util.InputMismatchException;
21import java.util.LinkedList;
22import java.util.List;
23import java.util.Map;
24import java.util.Properties;
25import java.util.Queue;
26import java.util.Scanner;
27import java.util.concurrent.TimeUnit;
28import java.util.logging.Level;
29import java.util.logging.Logger;
30import javax.swing.JOptionPane;
31import javax.swing.JWindow;
32import javax.swing.Timer;
33import javax.swing.UIManager;
34import tmcsim.common.SimulationException;
35import tmcsim.interfaces.CADClientInterface;
36import tmcsim.interfaces.CoordinatorInterface;
37
38/**
39 * Skeleton for ATMS Driver that reads a "batch" file of highway
40 * status update commands.
41 * It operates as a client of the
42 * CAD server, using RMI to poll the server every second for the current
43 * simulation clock time.  It uses the simulation clock time
44 * to fire update commands at the desired time.
45 * Note: Sim Mgr must be running before starting this application.
46 * TODO: We probably want to be able to "override" a command, to force
47 * clearing an incident.
48
49 * @author jdalbey
50 */
51public class ATMSBatchDriver extends UnicastRemoteObject implements
52        CADClientInterface
53{
54    private static final String CONFIG_FILE_NAME = "cad_client_config.properties";
55    private final static int ONE_SECOND = 1000;
56    private static final int FEPSIM_INTERVAL = 30000;
57    private final static SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
58    /**
59     * Error logger.
60     */
61    private static Logger cadClientLogger = Logger.getLogger("tmcsim.client");
62
63    @Override
64    public void refresh() throws RemoteException
65    {
66        System.out.println("ATMSBatchDriver.refresh() was invoked.");
67    }
68
69    /**
70     * Enumeration containing properties name values. See CADClient class
71     * description for more information.
72     *
73     * @author Matthew Cechini
74     * @see CADClient
75     */
76    private static enum PROPERTIES
77    {
78        CAD_SIM_HOST("CADSimulatorHost"), CAD_SIM_PORT("CADSimulatorSocketPort"), CAD_RMI_PORT(
79        "CADRmiPort"), CLIENT_CAD_POS("CADPosition"), CLIENT_USER_ID(
80        "CADUserID"), KEYBOARD_TYPE("KeyboardType"), DISPLAY_TYPE(
81        "DisplayType");
82        public String name;
83
84        private PROPERTIES(String n)
85        {
86            name = n;
87        }
88    }
89    /**
90     * CADClientSocket Object to handle socket communication between the Client
91     * and CAD Simulator.
92     */
93    private CADClientSocket theClientSocket;
94
95    /**
96     * Properties object for the CADClient class.
97     */
98    private Properties cadClientProp;
99    /**
100     * RMI interface for communication with the remote Coordinator.
101     */
102    private static CoordinatorInterface theCoorInt;
103    /**
104     * reference to itself to be used for disconnecting from CADSimulator
105     */
106    private CADClientInterface client = this;
107
108    /**
109     * Highways in traffic network
110     */
111    final private Highways highways;
112   
113    /**
114     * Queue of batch events
115     */
116    private Queue<String> eventQueue;
117    /**
118     * Map of incidents to events
119     */
120    private Map<String, List<String>> incidents;
121   
122    /** Instance of ConsoleDriver that contains the highway model */
123    private ConsoleDriver console;
124   
125    /** GUI for this driver */
126    private ATMSBatchViewer theView;
127   
128    /**
129     * Constructor. Initialize data from parsed properties file. Create a socket
130     * connection to the CADSimulator.
131     *
132     * @param propertiesFile File path (absolute or relative) to the properties
133     * file containing configuration data.
134     */
135    public ATMSBatchDriver(String propertiesFile) throws SimulationException,
136            RemoteException
137    {
138        if (!verifyProperties(propertiesFile))
139        {
140            System.exit(0);
141        }
142        incidents = new HashMap<String, List<String>> ();
143        highways = new Highways(
144        "config/vds_data/lds.txt",
145        "config/vds_data/loop.txt",
146        "config/vds_data/highwaysMeta.txt",
147        "192.168.251.46", 8080);  //IP address of FEP Sim Linux VM
148//        "localhost", 8080);
149        // Create console driver but don't start run() method
150        console = new ConsoleDriver(highways);
151       
152        connect(cadClientProp.getProperty(PROPERTIES.CAD_SIM_HOST.name).trim(),
153                cadClientProp.getProperty(PROPERTIES.CAD_RMI_PORT.name).trim());
154
155        // READ THE BATCH FILE OF COMMANDS and put in a queue
156        readBatchFile();
157        // Launch the display
158        theView = new ATMSBatchViewer(this, new ArrayList<String>(incidents.keySet()));
159        theView.setVisible(true);
160        theView.update("0:00", eventQueue);
161
162        // Create a timer that fetches the simulation time every second.
163        Timer timer = new Timer(ONE_SECOND, new ActionListener()
164        {
165            // Every second, see if an event should be launched
166            public void actionPerformed(ActionEvent e)
167            {
168                String currentClock = "";
169                Date simClock = new Date();               
170                // Obtain the simulation time from the CAD server
171                try
172                {
173                    long simtime = theCoorInt.getCurrentSimulationTime();
174                    currentClock = formatInterval(simtime);
175                    try {
176                        simClock = formatter.parse(currentClock);
177                    } catch (ParseException ex) {
178                        Logger.getLogger(ATMSBatchDriver.class.getName()).log(Level.SEVERE, null, ex);
179                        System.out.println("Invalid simulation clock time found in ATMSDriverClient");
180                        System.exit(-1);
181                    }                   
182                    //System.out.println("Current clock: " + currentClock);
183                } catch (RemoteException ex)
184                {
185                    Logger.getLogger(ATMSBatchDriver.class.getName()).log(Level.SEVERE, null, ex);
186                }
187                // If we have any events left to process
188                if (!eventQueue.isEmpty())
189                {
190                    // Get the time to launch the next event
191                    String nextEvent = eventQueue.peek();
192//                    String eventTimeField = nextEvent.substring(0,8);
193                    Scanner evtScan = new Scanner(nextEvent);
194                    String inci = evtScan.next();     
195                    String eventTimeField = evtScan.next();
196                    Date eventTime = new Date();
197                    try {
198                        eventTime = formatter.parse(eventTimeField);
199                    } catch (ParseException ex) {
200                        Logger.getLogger(ATMSBatchDriver.class.getName()).log(Level.WARNING, null, ex);
201                        System.out.println("Unable to parse event time: " + nextEvent + " skipping.");
202                        eventQueue.remove();
203                    }
204                    //System.out.println("Next event will be launched at: " + formatter.format(eventTime));
205                    // Check the queue of events to see if the first
206                    // item should be launched.  IF so,
207                    // issue that command and remove it from queue.
208                    if (eventTime.before(simClock) || eventTime.equals(simClock))
209                    {
210                        System.out.println("LAUNCHING EVENT at " + nextEvent );
211                        // Extract fields from event and prepare them
212                        Scanner lineScan = new Scanner(nextEvent);
213                        try
214                        {
215                        lineScan.next(); // skip incident number field
216                        lineScan.next(); // skip time field
217                        int routeNumber = lineScan.nextInt();
218                        Station.DIRECTION dir = Station.DIRECTION.toDirection(lineScan.next());
219                        double postmile = lineScan.nextDouble();
220                        double range = lineScan.nextDouble();
221                        ConsoleDriver.DOTCOLOR dotcolor = ConsoleDriver.DOTCOLOR.toDotColor(lineScan.next());
222                        // apply colorization to highways
223                        console.applyColorToHighwayStretch(routeNumber, dir, postmile, range, dotcolor);
224                        // Remove this event from the queue, we're done with it.
225                        eventQueue.remove();
226                        }
227                        catch (InputMismatchException ex)
228                        {
229                            System.out.println("Wrong format data in batch event file: " + nextEvent + " \nskipping.");
230                            eventQueue.remove();
231                        }
232                    }
233                    theView.update(currentClock, eventQueue);
234                }
235            }
236        });
237        timer.start();
238
239        // Start the FEP thread (to update ATMS every 30 sec). (See class def below)
240        Thread wtfep = new WriteToFEPThread();
241        wtfep.start();
242
243        ensureProperShutdown();
244    }
245
246    private void readBatchFile()
247    {
248        FileInputStream fis;
249        try {
250            fis = new FileInputStream("config/vds_data/atmsBatchEvents.txt");
251            eventQueue = new LinkedList<String>();
252            // Read all lines from the file of events
253            Scanner scan = new Scanner(fis);
254            while (scan.hasNext())
255            {
256                // Read a line and add it to the event queue
257                String line = scan.nextLine();
258                if (line.charAt(0) != '#')
259                {
260                    eventQueue.add(line);
261                    // Parse the incident from the line
262                    Scanner lineScan = new Scanner(line);
263                    String incident = lineScan.next();
264                    // Add the line to the list for the corresponding incident
265                    List evtList;
266                    if (incidents.containsKey(incident))
267                    {
268                        evtList = incidents.get(incident);
269                    }
270                    else
271                    {
272                        evtList = new ArrayList<String>();
273                    }               
274                    evtList.add(line);
275                    // and put it back in the map
276                    incidents.put(incident, evtList);
277                }
278            }
279        } catch (FileNotFoundException ex) {
280            Logger.getLogger(ATMSBatchDriver.class.getName()).log(Level.SEVERE, null, ex);
281        }
282        System.out.println("Events file read, " + eventQueue.size() + " events queued.");
283    }
284   
285    /** Clear an incident.  For each event associated with an incident,
286     * turn the dots in its range Green and remove it from the event queue.
287     * @param incidentNumber incident to be cleared.
288     */
289    public void clearIncident(String incidentNumber)
290    {
291        boolean ok = incidents.containsKey(incidentNumber);
292        if (!ok) 
293        {
294            System.out.println("Sorry, that incident number isn't found.");
295            return;
296        }
297        System.out.println("Clearing incident " + incidentNumber);
298        List<String> events = incidents.get(incidentNumber);
299        // Process each event associated with this incident
300        for (String event: events)
301        {
302            System.out.println("Event: " + event + " cleared.");
303            eventQueue.remove(event);
304            // Extract fields from event and prepare them
305            Scanner lineScan = new Scanner(event);
306            try
307            {
308                lineScan.next(); // skip incident number field
309                lineScan.next(); // skip time field
310                int routeNumber = lineScan.nextInt();
311                Station.DIRECTION dir = Station.DIRECTION.toDirection(lineScan.next());
312                double postmile = lineScan.nextDouble();
313                double range = lineScan.nextDouble();
314                // apply colorization to highways, forcing to green, indicating cleared
315                console.applyColorToHighwayStretch(routeNumber, dir, postmile, range, ConsoleDriver.DOTCOLOR.GREEN);
316            }
317            catch (InputMismatchException ex)
318            {
319                System.out.println("Internal error, please report to programmers." + event);
320            }
321           
322        }
323        // Now refresh the view with the updated queue of events
324        theView.update("0:00", eventQueue);
325    }
326   
327    /**
328     * Connect to the Coordinator's RMI object, and register this object for
329     * callback with the Coordinator.
330     *
331     * @param hostname Host name of the CAD Simulator.
332     * @param portNumber Port number of the CAD Simulator RMI communication.
333     * @throws SimulationException if there is an error creating the RMI
334     * connection.
335     */
336    protected void connect(String hostname, String portNumber)
337            throws SimulationException
338    {
339
340        String coorIntURL = "";
341
342        try
343        {
344            coorIntURL = "rmi://" + hostname + ":" + portNumber
345                    + "/coordinator";
346            theCoorInt = (CoordinatorInterface) Naming.lookup(coorIntURL);
347            theCoorInt.registerForCallback(this);
348        } catch (Exception e)
349        {
350            throw new SimulationException(SimulationException.CAD_SIM_CONNECT,
351                    e);
352        }
353    }
354
355    /**
356     * This method verifies that the CAD Simulator Host and Port values are not
357     * null. Also, if a CAD Position or User ID do not exist in the properties
358     * file, the user is prompted to enter values. These values are written to
359     * the properties file. If the user cancels the process of entering these
360     * values, the verification fails.
361     *
362     * @param propertiesFile File path (absolute or relative) to the properties
363     * file containing configuration data.
364     * @return True if the properties file is valid, false if not.
365     * @throws SimulationException if there is an exception in verifying the
366     * properties file, or if the user cancels input.
367     */
368    private boolean verifyProperties(String propertiesFile)
369            throws SimulationException
370    {
371
372        // Load the properties file.
373        try
374        {
375            cadClientProp = new Properties();
376            cadClientProp.load(new FileInputStream(propertiesFile));
377        } catch (Exception e)
378        {
379            cadClientLogger.logp(Level.SEVERE, "SimulationManager",
380                    "Constructor", "Exception in reading properties file.", e);
381
382            throw new SimulationException(SimulationException.INITIALIZE_ERROR,
383                    e);
384        }
385
386
387        // Ensure that the properties file does not have null values for the
388        // CAD Simulator's connection information.
389        if (cadClientProp.getProperty(PROPERTIES.CAD_SIM_HOST.name) == null
390                || cadClientProp.getProperty(PROPERTIES.CAD_SIM_PORT.name) == null)
391        {
392            cadClientLogger.logp(Level.SEVERE, "SimulationManager",
393                    "Constructor", "Null value in properties file.");
394            throw new SimulationException(SimulationException.INITIALIZE_ERROR);
395        }
396
397        return true;
398    }
399
400    /**
401     * Format a time in seconds as HH:MM:SS
402     *
403     * @param l
404     * @return
405     */
406    private String formatInterval(final long l)
407    {
408        final long hr = TimeUnit.SECONDS.toHours(l);
409        final long min = TimeUnit.SECONDS.toMinutes(l - TimeUnit.HOURS.toSeconds(hr));
410        final long sec = TimeUnit.SECONDS.toSeconds(l - TimeUnit.HOURS.toSeconds(hr) - TimeUnit.MINUTES.toSeconds(min));
411        return String.format("%02d:%02d:%02d", hr, min, sec);
412    }
413
414    public void ensureProperShutdown()
415    {
416        Runtime.getRuntime().addShutdownHook(new Thread()
417        {
418            public void run()
419            {
420                try
421                {
422                    theCoorInt.unregisterForCallback(client);
423                } catch (RemoteException e)
424                {
425                    e.printStackTrace();
426                }
427            }
428        });
429    }
430
431    /**
432     * Construct the CADClient with the properties file path, either from the
433     * command line arguments or default.
434     *
435     * @param args Command line arguments.
436     */
437    public static void main(String[] args)
438    {
439        if (System.getProperty("CONFIG_DIR") == null)
440        {
441            System.setProperty("CONFIG_DIR", "config");
442        }
443
444        try
445        {
446            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
447            new ATMSBatchDriver(System.getProperty("CONFIG_DIR") + System.getProperty("file.separator") + CONFIG_FILE_NAME);
448
449        } catch (Exception e)
450        {
451            cadClientLogger.logp(Level.SEVERE, "SimulationManager", "Main",
452                    "Error initializing application.");
453
454            JOptionPane.showMessageDialog(new JWindow(), e.getMessage(),
455                    "Error - Program Exiting", JOptionPane.ERROR_MESSAGE);
456
457            System.exit(-1);
458        }
459
460    }
461
462    class WriteToFEPThread extends Thread
463    {
464
465        public void run()
466        {
467            System.out.println("WriteToFEP Thread starting.");
468            // Run indefinitely
469            while (true)
470            {
471                try {
472                    // Write the highway network status to the FEP Simulator
473                    highways.writeToFEP();
474                } catch (SimulationException ex) 
475                {
476                    System.out.println("Skipping writeToFEP...");
477                }
478
479                // Wait for FEP Sim to process the data we just sent
480                try
481                {
482                    Thread.sleep(FEPSIM_INTERVAL);
483                }
484                catch (InterruptedException ie)
485                {
486                    ie.printStackTrace();
487                }
488            }
489
490        }
491    }
492}
Note: See TracBrowser for help on using the repository browser.