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

Revision 128, 14.7 KB checked in by jdalbey, 9 years ago (diff)

ATMSBatchDriver.java Added a GUI to display event queue. Highways.java modified to throw an exception when writeToFEP can't connect to FEP_Sim.

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