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