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