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