source: tmcsimulator/trunk/src/tmcsim/client/ATMSDriverClient.java @ 112

Revision 112, 13.1 KB checked in by jdalbey, 9 years ago (diff)

ATMSDriverClient.java Enhanced data validation and additional logic to launch events that are earlier than simulation time.

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