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

Revision 157, 18.8 KB checked in by jdalbey, 9 years ago (diff)

ATMSBatchDriver: Added popup dialog to confirm proceeding if FEP Sim connection failed.

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.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    /**
363     * This method verifies that the CAD Simulator Host and Port values are not
364     * null. Also, if a CAD Position or User ID do not exist in the properties
365     * file, the user is prompted to enter values. These values are written to
366     * the properties file. If the user cancels the process of entering these
367     * values, the verification fails.
368     *
369     * @param propertiesFile File path (absolute or relative) to the properties
370     * file containing configuration data.
371     * @return True if the properties file is valid, false if not.
372     * @throws SimulationException if there is an exception in verifying the
373     * properties file, or if the user cancels input.
374     */
375    private boolean verifyProperties(String propertiesFile)
376            throws SimulationException
377    {
378
379        // Load the properties file.
380        try
381        {
382            cadClientProp = new Properties();
383            cadClientProp.load(new FileInputStream(propertiesFile));
384        } catch (Exception e)
385        {
386            cadClientLogger.logp(Level.SEVERE, "SimulationManager",
387                    "Constructor", "Exception in reading properties file.", e);
388
389            throw new SimulationException(SimulationException.INITIALIZE_ERROR,
390                    e);
391        }
392
393
394        // Ensure that the properties file does not have null values for the
395        // CAD Simulator's connection information.
396        if (cadClientProp.getProperty(PROPERTIES.CAD_SIM_HOST.name) == null
397                || cadClientProp.getProperty(PROPERTIES.CAD_SIM_PORT.name) == null)
398        {
399            cadClientLogger.logp(Level.SEVERE, "SimulationManager",
400                    "Constructor", "Null value in properties file.");
401            throw new SimulationException(SimulationException.INITIALIZE_ERROR);
402        }
403
404        return true;
405    }
406
407    /**
408     * Format a time in seconds as HH:MM:SS
409     *
410     * @param l
411     * @return
412     */
413    private String formatInterval(final long l)
414    {
415        final long hr = TimeUnit.SECONDS.toHours(l);
416        final long min = TimeUnit.SECONDS.toMinutes(l - TimeUnit.HOURS.toSeconds(hr));
417        final long sec = TimeUnit.SECONDS.toSeconds(l - TimeUnit.HOURS.toSeconds(hr) - TimeUnit.MINUTES.toSeconds(min));
418        return String.format("%02d:%02d:%02d", hr, min, sec);
419    }
420
421    public void ensureProperShutdown()
422    {
423        Runtime.getRuntime().addShutdownHook(new Thread()
424        {
425            public void run()
426            {
427                try
428                {
429                    theCoorInt.unregisterForCallback(client);
430                } catch (RemoteException e)
431                {
432                    e.printStackTrace();
433                }
434            }
435        });
436    }
437
438    /**
439     * Construct the CADClient with the properties file path, either from the
440     * command line arguments or default.
441     *
442     * @param args Command line arguments.
443     */
444    public static void main(String[] args)
445    {
446        if (System.getProperty("CONFIG_DIR") == null)
447        {
448            System.setProperty("CONFIG_DIR", "config");
449        }
450
451        try
452        {
453            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
454            new ATMSBatchDriver(System.getProperty("CONFIG_DIR") + System.getProperty("file.separator") + CONFIG_FILE_NAME);
455
456        } catch (Exception e)
457        {
458            cadClientLogger.logp(Level.SEVERE, "SimulationManager", "Main",
459                    "Error initializing application.");
460
461            JOptionPane.showMessageDialog(new JWindow(), e.getMessage(),
462                    "Error - Program Exiting", JOptionPane.ERROR_MESSAGE);
463
464            System.exit(-1);
465        }
466
467    }
468
469    class WriteToFEPThread extends Thread
470    {
471
472        public void run()
473        {
474            System.out.println("WriteToFEP Thread starting.");
475            // Run indefinitely
476            while (true)
477            {
478                try {
479                    // Write the highway network status to the FEP Simulator
480                    highways.writeToFEP();
481                } catch (SimulationException ex) 
482                {
483                    // Ask user if they want to proceed without FEP Sim connection
484                    int reply = JOptionPane.showConfirmDialog(null, "Failed to connect to FEP Sim, proceed anyway?", "Network Failure", JOptionPane.YES_NO_OPTION);
485                    if (reply == JOptionPane.NO_OPTION)
486                    {
487                      System.exit(0);
488                    }
489                    System.out.println("Skipping writeToFEP...");
490                }
491
492                // Wait for FEP Sim to process the data we just sent
493                try
494                {
495                    Thread.sleep(FEPSIM_INTERVAL);
496                }
497                catch (InterruptedException ie)
498                {
499                    ie.printStackTrace();
500                }
501            }
502
503        }
504    }
505}
Note: See TracBrowser for help on using the repository browser.