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

Revision 172, 19.0 KB checked in by jtorres, 9 years ago (diff)

Cleaned config/vds_data folder, renamed files and moved all older vds data into config/vds_data/old_vds_data. Set up atmsBatchDriver to use new lds.txt and loop.txt file names in constructor

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        // Initialize the highway model
144        incidents = new HashMap<String, List<String>> ();
145        highways = new Highways(
146        "config/vds_data/lds.txt",
147        "config/vds_data/loop.txt",
148        "config/vds_data/highwaysMeta.txt",
149        "192.168.251.46", 8080);  //IP address of FEP Sim Linux VM
150//       "localhost", 8080);
151        // Create console driver but don't start run() method
152        console = new ConsoleDriver(highways);
153       
154        connect(cadClientProp.getProperty(PROPERTIES.CAD_SIM_HOST.name).trim(),
155                cadClientProp.getProperty(PROPERTIES.CAD_RMI_PORT.name).trim());
156
157        // READ THE BATCH FILE OF COMMANDS and put in a queue
158        readBatchFile();
159        // Launch the display
160        theView = new ATMSBatchViewer(this, new ArrayList<String>(incidents.keySet()));
161        theView.setVisible(true);
162        theView.update("0:00", "1:11", eventQueue);
163
164        // Create a timer that fetches the simulation time every second.
165        Timer timer = new Timer(ONE_SECOND, new ActionListener()
166        {
167            // Every second, see if an event should be launched
168            public void actionPerformed(ActionEvent e)
169            {
170                String currentClock = "";
171                String currentATMStime = "";
172                Date simClock = new Date();               
173                // Obtain the simulation time from the CAD server
174                try
175                {
176                    long simtime = theCoorInt.getCurrentSimulationTime();
177                    currentClock = formatInterval(simtime);
178                    // For Debugging, show the ATMS time
179//                    long ATMStime = theCoorInt.getATMStime();       
180//                    Date atmsdate = new Date(ATMStime);
181//                    currentATMStime = formatter.format(atmsdate);
182                    try {
183                        simClock = formatter.parse(currentClock);
184                    } catch (ParseException ex) {
185                        Logger.getLogger(ATMSBatchDriver.class.getName()).log(Level.SEVERE, null, ex);
186                        System.out.println("Invalid simulation clock time found in ATMSDriverClient");
187                        System.exit(-1);
188                    }                   
189                    //System.out.println("Current clock: " + currentClock);
190                } catch (RemoteException ex)
191                {
192                    System.out.println("Remote Exception reading sim or ATMS clock time");
193                    Logger.getLogger(ATMSBatchDriver.class.getName()).log(Level.SEVERE, null, ex);
194                }
195                // If we have any events left to process
196                if (!eventQueue.isEmpty())
197                {
198                    // Get the time to launch the next event
199                    String nextEvent = eventQueue.peek();
200//                    String eventTimeField = nextEvent.substring(0,8);
201                    Scanner evtScan = new Scanner(nextEvent);
202                    String inci = evtScan.next();     
203                    String eventTimeField = evtScan.next();
204                    Date eventTime = new Date();
205                    try {
206                        eventTime = formatter.parse(eventTimeField);
207                    } catch (ParseException ex) {
208                        Logger.getLogger(ATMSBatchDriver.class.getName()).log(Level.WARNING, null, ex);
209                        System.out.println("Unable to parse event time: " + nextEvent + " skipping.");
210                        eventQueue.remove();
211                    }
212                    //System.out.println("Next event will be launched at: " + formatter.format(eventTime));
213                    // Check the queue of events to see if the first
214                    // item should be launched.  IF so,
215                    // issue that command and remove it from queue.
216                    if (eventTime.before(simClock) || eventTime.equals(simClock))
217                    {
218                        System.out.println("LAUNCHING EVENT at " + nextEvent );
219                        // Extract fields from event and prepare them
220                        Scanner lineScan = new Scanner(nextEvent);
221                        try
222                        {
223                        lineScan.next(); // skip incident number field
224                        lineScan.next(); // skip time field
225                        int routeNumber = lineScan.nextInt();
226                        Station.DIRECTION dir = Station.DIRECTION.toDirection(lineScan.next());
227                        double postmile = lineScan.nextDouble();
228                        double range = lineScan.nextDouble();
229                        ConsoleDriver.DOTCOLOR dotcolor = ConsoleDriver.DOTCOLOR.toDotColor(lineScan.next());
230                        // apply colorization to highways
231                        console.applyColorToHighwayStretch(routeNumber, dir, postmile, range, dotcolor);
232                        // Remove this event from the queue, we're done with it.
233                        eventQueue.remove();
234                        }
235                        catch (InputMismatchException ex)
236                        {
237                            System.out.println("Wrong format data in batch event file: " + nextEvent + " \nskipping.");
238                            eventQueue.remove();
239                        }
240                    }
241                   
242                    theView.update(currentClock, currentATMStime, eventQueue);
243                }
244            }
245        });
246        timer.start();
247
248        // Start the FEP thread (to update ATMS every 30 sec). (See class def below)
249        Thread wtfep = new WriteToFEPThread();
250        wtfep.start();
251
252        ensureProperShutdown();
253    }
254
255    private void readBatchFile()
256    {
257        FileInputStream fis;
258        try {
259            fis = new FileInputStream("config/vds_data/atmsBatchEvents.txt");
260            eventQueue = new LinkedList<String>();
261            // Read all lines from the file of events
262            Scanner scan = new Scanner(fis);
263            while (scan.hasNext())
264            {
265                // Read a line and add it to the event queue
266                String line = scan.nextLine();
267                if (line.charAt(0) != '#')
268                {
269                    eventQueue.add(line);
270                    // Parse the incident from the line
271                    Scanner lineScan = new Scanner(line);
272                    String incident = lineScan.next();
273                    // Add the line to the list for the corresponding incident
274                    List evtList;
275                    if (incidents.containsKey(incident))
276                    {
277                        evtList = incidents.get(incident);
278                    }
279                    else
280                    {
281                        evtList = new ArrayList<String>();
282                    }               
283                    evtList.add(line);
284                    // and put it back in the map
285                    incidents.put(incident, evtList);
286                }
287            }
288        } catch (FileNotFoundException ex) {
289            Logger.getLogger(ATMSBatchDriver.class.getName()).log(Level.SEVERE, null, ex);
290        }
291        System.out.println("Events file read, " + eventQueue.size() + " events queued.");
292    }
293   
294    /** Clear an incident.  For each event associated with an incident,
295     * turn the dots in its range Green and remove it from the event queue.
296     * @param incidentNumber incident to be cleared.
297     */
298    public void clearIncident(String incidentNumber)
299    {
300        boolean ok = incidents.containsKey(incidentNumber);
301        if (!ok) 
302        {
303            System.out.println("Sorry, that incident number isn't found.");
304            return;
305        }
306        System.out.println("Clearing incident " + incidentNumber);
307        List<String> events = incidents.get(incidentNumber);
308        // Process each event associated with this incident
309        for (String event: events)
310        {
311            System.out.println("Event: " + event + " cleared.");
312            eventQueue.remove(event);
313            // Extract fields from event and prepare them
314            Scanner lineScan = new Scanner(event);
315            try
316            {
317                lineScan.next(); // skip incident number field
318                lineScan.next(); // skip time field
319                int routeNumber = lineScan.nextInt();
320                Station.DIRECTION dir = Station.DIRECTION.toDirection(lineScan.next());
321                double postmile = lineScan.nextDouble();
322                double range = lineScan.nextDouble();
323                // apply colorization to highways, forcing to green, indicating cleared
324                console.applyColorToHighwayStretch(routeNumber, dir, postmile, range, ConsoleDriver.DOTCOLOR.GREEN);
325            }
326            catch (InputMismatchException ex)
327            {
328                System.out.println("Internal error, please report to programmers." + event);
329            }
330           
331        }
332        // Now refresh the view with the updated queue of events
333        theView.update("0:00", "0:00", eventQueue);
334    }
335   
336    /**
337     * Connect to the Coordinator's RMI object, and register this object for
338     * callback with the Coordinator.
339     *
340     * @param hostname Host name of the CAD Simulator.
341     * @param portNumber Port number of the CAD Simulator RMI communication.
342     * @throws SimulationException if there is an error creating the RMI
343     * connection.
344     */
345    protected void connect(String hostname, String portNumber)
346            throws SimulationException
347    {
348
349        String coorIntURL = "";
350
351        try
352        {
353            coorIntURL = "rmi://" + hostname + ":" + portNumber
354                    + "/coordinator";
355            theCoorInt = (CoordinatorInterface) Naming.lookup(coorIntURL);
356            theCoorInt.registerForCallback(this);
357        } catch (Exception e)
358        {
359            throw new SimulationException(SimulationException.CAD_SIM_CONNECT,
360                    e);
361           
362        }
363    }
364
365    /**
366     * This method verifies that the CAD Simulator Host and Port values are not
367     * null. Also, if a CAD Position or User ID do not exist in the properties
368     * file, the user is prompted to enter values. These values are written to
369     * the properties file. If the user cancels the process of entering these
370     * values, the verification fails.
371     *
372     * @param propertiesFile File path (absolute or relative) to the properties
373     * file containing configuration data.
374     * @return True if the properties file is valid, false if not.
375     * @throws SimulationException if there is an exception in verifying the
376     * properties file, or if the user cancels input.
377     */
378    private boolean verifyProperties(String propertiesFile)
379            throws SimulationException
380    {
381
382        // Load the properties file.
383        try
384        {
385            cadClientProp = new Properties();
386            cadClientProp.load(new FileInputStream(propertiesFile));
387        } catch (Exception e)
388        {
389            cadClientLogger.logp(Level.SEVERE, "SimulationManager",
390                    "Constructor", "Exception in reading properties file.", e);
391
392            throw new SimulationException(SimulationException.INITIALIZE_ERROR,
393                    e);
394        }
395
396
397        // Ensure that the properties file does not have null values for the
398        // CAD Simulator's connection information.
399        if (cadClientProp.getProperty(PROPERTIES.CAD_SIM_HOST.name) == null
400                || cadClientProp.getProperty(PROPERTIES.CAD_SIM_PORT.name) == null)
401        {
402            cadClientLogger.logp(Level.SEVERE, "SimulationManager",
403                    "Constructor", "Null value in properties file.");
404            throw new SimulationException(SimulationException.INITIALIZE_ERROR);
405        }
406
407        return true;
408    }
409
410    /**
411     * Format a time in seconds as HH:MM:SS
412     *
413     * @param l
414     * @return
415     */
416    private String formatInterval(final long l)
417    {
418        final long hr = TimeUnit.SECONDS.toHours(l);
419        final long min = TimeUnit.SECONDS.toMinutes(l - TimeUnit.HOURS.toSeconds(hr));
420        final long sec = TimeUnit.SECONDS.toSeconds(l - TimeUnit.HOURS.toSeconds(hr) - TimeUnit.MINUTES.toSeconds(min));
421        return String.format("%02d:%02d:%02d", hr, min, sec);
422    }
423
424    public void ensureProperShutdown()
425    {
426        Runtime.getRuntime().addShutdownHook(new Thread()
427        {
428            public void run()
429            {
430                try
431                {
432                    theCoorInt.unregisterForCallback(client);
433                } catch (RemoteException e)
434                {
435                    e.printStackTrace();
436                }
437            }
438        });
439    }
440
441    /**
442     * Construct the CADClient with the properties file path, either from the
443     * command line arguments or default.
444     *
445     * @param args Command line arguments.
446     */
447    public static void main(String[] args)
448    {
449        if (System.getProperty("CONFIG_DIR") == null)
450        {
451            System.setProperty("CONFIG_DIR", "config");
452        }
453
454        try
455        {
456            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
457            new ATMSBatchDriver(System.getProperty("CONFIG_DIR") + System.getProperty("file.separator") + CONFIG_FILE_NAME);
458
459        } catch (Exception e)
460        {
461            cadClientLogger.logp(Level.SEVERE, "SimulationManager", "Main",
462                    "Error initializing application.");
463
464            JOptionPane.showMessageDialog(new JWindow(), e.getMessage(),
465                    "Error - Program Exiting", JOptionPane.ERROR_MESSAGE);
466
467            System.exit(-1);
468        }
469
470    }
471
472    class WriteToFEPThread extends Thread
473    {
474
475        public void run()
476        {
477            System.out.println("WriteToFEP Thread starting.");
478            // Run indefinitely
479            while (true)
480            {
481                try {
482                    // Write the highway network status to the FEP Simulator
483                    highways.writeToFEP();
484                } catch (SimulationException ex) 
485                {
486                    // Ask user if they want to proceed without FEP Sim connection
487                    int reply = JOptionPane.showConfirmDialog(null, "Failed to connect to FEP Sim, proceed anyway?", "Network Failure", JOptionPane.YES_NO_OPTION);
488                    if (reply == JOptionPane.NO_OPTION)
489                    {
490                      System.exit(0);
491                    }
492                    System.out.println("Skipping writeToFEP...");
493                }
494
495                // Wait for FEP Sim to process the data we just sent
496                try
497                {
498                    Thread.sleep(FEPSIM_INTERVAL);
499                }
500                catch (InterruptedException ie)
501                {
502                    ie.printStackTrace();
503                }
504            }
505
506        }
507    }
508}
Note: See TracBrowser for help on using the repository browser.