View Javadoc
1   package org.opentrafficsim.trafficcontrol.ccol;
2   
3   import java.io.BufferedReader;
4   import java.io.IOException;
5   import java.io.InputStreamReader;
6   import java.io.PrintWriter;
7   import java.net.ServerSocket;
8   import java.net.Socket;
9   import java.rmi.RemoteException;
10  import java.util.Set;
11  
12  import org.djunits.unit.DurationUnit;
13  import org.djunits.value.vdouble.scalar.Duration;
14  import org.djunits.value.vdouble.scalar.Time;
15  import org.opentrafficsim.core.network.Network;
16  import org.opentrafficsim.core.network.NetworkException;
17  import org.opentrafficsim.core.object.InvisibleObjectInterface;
18  import org.opentrafficsim.road.network.lane.object.sensor.TrafficLightSensor;
19  import org.opentrafficsim.road.network.lane.object.trafficlight.TrafficLight;
20  import org.opentrafficsim.trafficcontrol.TrafficControlException;
21  import org.opentrafficsim.trafficcontrol.TrafficController;
22  
23  import nl.tudelft.simulation.dsol.SimRuntimeException;
24  import nl.tudelft.simulation.dsol.simtime.SimTimeDoubleUnit;
25  import nl.tudelft.simulation.dsol.simulators.DEVSSimulator;
26  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
27  import nl.tudelft.simulation.event.EventInterface;
28  import nl.tudelft.simulation.event.EventProducer;
29  import nl.tudelft.simulation.event.EventType;
30  
31  /**
32   * Communication link with a CCOL traffic control program.
33   * <p>
34   * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
35   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
36   * <p>
37   * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jan 23, 2017 <br>
38   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
39   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
40   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
41   */
42  public class CCOL extends EventProducer implements TrafficController
43  {
44      /** */
45      private static final long serialVersionUID = 20170126L;
46  
47      /** Name of this CCOL traffic controller. */
48      final String id;
49  
50      /** The simulator. */
51      final DEVSSimulator<Time, Duration, SimTimeDoubleUnit> simulator;
52  
53      /** TCP port for incoming connection. */
54      static int PORT = 4321;
55  
56      /** The evaluation interval of a CCOL controller. */
57      final static Duration EVALUATION_INTERVAL = new Duration(0.1, DurationUnit.SECOND);
58  
59      /** Socket used to listen for the incoming connection from the CCOL controller. */
60      ServerSocket serverSocket;
61  
62      /** Socket for communication with the CCOL controller. */
63      private Socket clientSocket = null;
64  
65      /** Receives data from the CCOL process. */
66      private BufferedReader ccolReader = null;
67  
68      /** Sends data to the CCOL process. */
69      private PrintWriter ccolWriter = null;
70  
71      /** Thread that blocks until accept returns. */
72      Thread acceptThread;
73  
74      /**
75       * Construct a new CCOL communication link.
76       * @param id String; id of the traffic controller
77       * @param controlProgram String; name of the CCOL program that this CCOL link must communicate with
78       * @param trafficLights Set&lt;TrafficLight&gt;; the traffic lights. The ids of the traffic lights must end with two digits
79       *            that match the stream numbers as used in the traffic control program
80       * @param sensors Set&lt;TrafficLightSensor&gt;; the traffic sensors. The ids of the traffic sensors must end with three
81       *            digits; the first two of those must match the stream and sensor numbers used in the traffic control program
82       * @param simulator DEVSSimulator&lt;Time, Duration, SimTimeDoubleUnit&gt;; the simulation engine
83       * @throws TrafficControlException on failure to initialize the connection to the external CCOL program
84       * @throws SimRuntimeException on failure to schedule the first evaluation event
85       */
86      public CCOL(final String id, final String controlProgram, final Set<TrafficLight> trafficLights,
87              final Set<TrafficLightSensor> sensors, final DEVSSimulator<Time, Duration, SimTimeDoubleUnit> simulator)
88              throws TrafficControlException, SimRuntimeException
89      {
90          this.id = id;
91          this.simulator = simulator;
92          try
93          {
94              // Set up a listening socket
95              this.serverSocket = new ServerSocket(PORT);
96              Runnable acceptTask = new Runnable()
97              {
98                  @Override
99                  public void run()
100                 {
101                     try
102                     {
103                         setClientSocket(CCOL.this.serverSocket.accept());
104                     }
105                     catch (IOException exception)
106                     {
107                         exception.printStackTrace();
108                     }
109                 }
110             };
111             this.acceptThread = new Thread(acceptTask);
112             this.acceptThread.start();
113             // Start up the external CCOL program
114             Runtime.getRuntime().exec(controlProgram + " localhost:" + this.serverSocket.getLocalPort());
115         }
116         catch (IOException e)
117         {
118             e.printStackTrace();
119         }
120         this.simulator.scheduleEventRel(Duration.ZERO, this, this, "step", null);
121         this.simulator.addListener(this, SimulatorInterface.END_OF_REPLICATION_EVENT);
122     }
123 
124     /**
125      * Set the client socket (called from the accept thread).
126      * @param socket Socket; the socket returned by accept
127      */
128     void setClientSocket(final Socket socket)
129     {
130         if (null != this.clientSocket)
131         {
132             System.err.println("clientSocket already set");
133             return;
134         }
135         this.clientSocket = socket;
136         try
137         {
138             this.ccolReader = new BufferedReader(new InputStreamReader(this.clientSocket.getInputStream()));
139             this.ccolWriter = new PrintWriter(this.clientSocket.getOutputStream());
140         }
141         catch (IOException exception)
142         {
143             exception.printStackTrace();
144         }
145         // Close the server socket to release resources and ensure that we cannot accept further connections
146         try
147         {
148             this.serverSocket.close();
149         }
150         catch (IOException exception)
151         {
152             exception.printStackTrace();
153         }
154     }
155 
156     /**
157      * Retrieve the client socket for shutdown.
158      * @return Socket; the socket for communication with the CCOL client
159      */
160     Socket getClientSocket()
161     {
162         return this.clientSocket;
163     }
164 
165     /**
166      * Let the CCOL engine determine the new state of the traffic lights and update the traffic lights accordingly.
167      * @throws TrafficControlException when the CCOL engine reports an error or communication with the CCOL engine fails
168      * @throws SimRuntimeException when scheduling the next evaluation fails
169      */
170     @SuppressWarnings("unused")
171     private void step() throws TrafficControlException, SimRuntimeException
172     {
173         // TODO time should be formatted as date, hour, etc.
174         String message = String.format("STEP %s", this.simulator.getSimulatorTime());
175         this.ccolWriter.print(message);
176         try
177         {
178             String result = this.ccolReader.readLine();
179             // TODO parse the result and update the state of traffic lights accordingly
180             // Protocol must ensure that we know it when all updates have been received.
181         }
182         catch (IOException exception)
183         {
184             exception.printStackTrace();
185         }
186         // Schedule the next step.
187         this.simulator.scheduleEventRel(EVALUATION_INTERVAL, this, this, "step", null);
188     }
189 
190     /** {@inheritDoc} */
191     @Override
192     public void notify(EventInterface event) throws RemoteException
193     {
194         EventType eventType = event.getType();
195         if (eventType.equals(SimulatorInterface.END_OF_REPLICATION_EVENT))
196         {
197             if (null != this.serverSocket)
198             {
199                 try
200                 {
201                     this.serverSocket.close();
202                 }
203                 catch (IOException exception)
204                 {
205                     exception.printStackTrace();
206                 }
207                 this.serverSocket = null;
208             }
209             if (null != this.clientSocket)
210             {
211                 try
212                 {
213                     this.clientSocket.close();
214                 }
215                 catch (IOException exception)
216                 {
217                     exception.printStackTrace();
218                 }
219                 this.clientSocket = null;
220             }
221         }
222         // Tracing etc. not implemented yet.
223     }
224 
225     /** {@inheritDoc} */
226     @Override
227     public String getId()
228     {
229         return this.id;
230     }
231 
232     /** {@inheritDoc} */
233     @Override
234     public String getFullId()
235     {
236         return this.id;
237     }
238 
239     /** {@inheritDoc} */
240     @Override
241     public void updateDetector(final String detectorId, final boolean detectingGTU)
242     {
243         // FIXME: format of messages is TBD
244         String message = String.format("DET %s %s", detectorId, detectingGTU);
245         this.ccolWriter.print(message);
246     }
247 
248     /** {@inheritDoc} */
249     @Override
250     public InvisibleObjectInterface clone(SimulatorInterface.TimeDoubleUnit newSimulator, Network newNetwork) throws NetworkException
251     {
252         // FIXME: implement the clone() for CCOL
253         return null;
254     }
255 
256 }