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

Revision 152, 18.4 KB checked in by jdalbey, 9 years ago (diff)

Coordinator.java: new method getATMStime added for use by ATMSBatchDriver.

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