View Javadoc
1   package org.opentrafficsim.imb.connector;
2   
3   import java.util.HashMap;
4   import java.util.Map;
5   
6   import org.opentrafficsim.imb.IMBException;
7   import org.opentrafficsim.imb.transceiver.Transceiver;
8   
9   import nl.tno.imb.TByteBuffer;
10  import nl.tno.imb.TConnection;
11  import nl.tno.imb.TEventEntry;
12  import nl.tudelft.simulation.language.Throw;
13  
14  /**
15   * Make a connection to the IMB bus, allow messages to be posted, and register callbacks from IMB to OTS.
16   * <p>
17   * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
18   * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
19   * <p>
20   * @version $Revision$, $LastChangedDate$, by $Author$, initial version Aug 19, 2016 <br>
21   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
22   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
23   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
24   */
25  public class IMBConnector implements Connector
26  {
27      /** Communication link to the observers. */
28      private final TConnection connection;
29  
30      /** Registration of callback ids from IMB to OTS. */
31      protected Map<String, Transceiver> imbTransceiverMap = new HashMap<>();
32  
33      /**
34       * Construct a new connection for sending events to IMB
35       * @param host String; name of the IMB hub
36       * @param port int; port number of the IMB hub
37       * @param modelName String; local model name
38       * @param modelId int; model id
39       * @param federation String; federation on the IMB hub
40       * @throws IMBException when a connection to the IMB hub could not be established
41       */
42      public IMBConnector(final String host, final int port, final String modelName, final int modelId, final String federation)
43              throws IMBException
44      {
45          Throw.whenNull(host, "host cannot be null");
46          Throw.whenNull(modelName, "modelName cannot be null");
47          Throw.whenNull(modelId, "modelId cannot be null");
48          Throw.whenNull(federation, "federation cannot be null");
49          Throw.when(port <= 0 || port > 65535, IMBException.class, "port should be beween 1 and 65535");
50  
51          this.connection = new TConnection(host, port, modelName, modelId, federation);
52          Throw.when(!this.connection.isConnected(), IMBException.class, "No connection to IMB hub on " + host + ":" + port);
53      }
54      
55      /**
56       * Construct an IMBConnector that re-uses an existing TConnection.
57       * @param connection TConnection; the existing TConnection
58       * @throws IMBException when the connection is not connected to an IMB hub
59       */
60      public IMBConnector(final TConnection connection) throws IMBException
61      {
62          Throw.whenNull(connection, "conneciton cannot be null");
63          
64          this.connection = connection;
65          Throw.when(!this.connection.isConnected(), IMBException.class, "No connection to IMB hub");
66      }
67  
68      /** {@inheritDoc} */
69      @Override
70      public void register(final String imbEventName, final Transceiver transceiver) throws IMBException
71      {
72          Throw.whenNull(imbEventName, "imbEventName cannot be null");
73          Throw.whenNull(transceiver, "transceiver cannot be null");
74  
75          // Already registered?
76          if (this.imbTransceiverMap.containsKey(imbEventName))
77          {
78              Throw.when(!this.imbTransceiverMap.get(imbEventName).equals(transceiver), IMBException.class,
79                      "Cannot switch transceiver for imbEventName " + imbEventName);
80              return;
81          }
82          // we receive messages including the federation name
83          this.imbTransceiverMap.put(imbEventName, transceiver);
84  
85          // Link to the listening thread for incoming IMB messages.
86          TEventEntry messageEvent = this.connection.subscribe(imbEventName);
87          messageEvent.onNormalEvent = new TEventEntry.TOnNormalEvent()
88          {
89              @Override
90              public void dispatch(TEventEntry aEvent, TByteBuffer aPayload)
91              {
92                  String shortIMBEventName = aEvent.getEventName().substring(aEvent.getEventName().indexOf('.') + 1);
93                  if (!IMBConnector.this.imbTransceiverMap.containsKey(shortIMBEventName))
94                  {
95                      // TODO error handling
96                      System.err.println("Could not find imbEventName " + shortIMBEventName + " in imbTransceiverMap");
97                  }
98                  try
99                  {
100                     // TODO synchronized?
101                     IMBConnector.this.imbTransceiverMap.get(shortIMBEventName).handleMessageFromIMB(shortIMBEventName,
102                             aPayload);
103                 }
104                 catch (IMBException exception)
105                 {
106                     // TODO error handling
107                     exception.printStackTrace();
108                 }
109             }
110         };
111 
112     }
113 
114     /** {@inheritDoc} */
115     @Override
116     public final boolean postIMBMessage(final String eventName, final IMBEventType imbEventType, final Object[] args)
117             throws IMBException
118     {
119         TByteBuffer payload = new TByteBuffer();
120         payload.writeStart(0);
121         payload.write(imbEventType.getEventEntry());
122         for (Object o : args)
123         {
124             Class<?> objectClass = o.getClass();
125             if (objectClass.equals(String.class))
126                 payload.write((String) o);
127             else if (objectClass.equals(Double.class))
128                 payload.write(((Double) o).doubleValue());
129             else if (objectClass.equals(Float.class))
130                 payload.write(((Float) o).floatValue());
131             else if (objectClass.equals(Integer.class))
132                 payload.write(((Integer) o).intValue());
133             else if (objectClass.equals(Long.class))
134                 payload.write(((Long) o).longValue());
135             else if (objectClass.equals(Byte.class))
136                 payload.write(((Byte) o).byteValue());
137             else if (objectClass.equals(Character.class))
138                 payload.write(((Character) o).charValue());
139             else if (objectClass.equals(Boolean.class))
140                 payload.write(((Boolean) o).booleanValue());
141             else if (objectClass.equals(byte[].class))
142                 payload.write(new TByteBuffer((byte[]) o));
143             else
144                 throw new IMBException("cannot pack object of type " + objectClass.toString() + " in payload");
145         }
146         int result = this.connection.signalEvent(eventName, TEventEntry.EK_NORMAL_EVENT, payload);
147         if (result < 0)
148         {
149             throw new IMBException("IMB error in signalEvent code " + result);
150         }
151         return result >= 0;
152     }
153 
154     /** {@inheritDoc} */
155     @Override
156     public final String getHost()
157     {
158         return this.connection.getRemoteHost();
159     }
160 
161     /** {@inheritDoc} */
162     @Override
163     public final int getPort()
164     {
165         return this.connection.getRemotePort();
166     }
167 
168     /** {@inheritDoc} */
169     @Override
170     public final String getModelName()
171     {
172         return this.connection.getOwnerName();
173     }
174 
175     /** {@inheritDoc} */
176     @Override
177     public final int getModelId()
178     {
179         return this.connection.getOwnerID();
180     }
181 
182     /** {@inheritDoc} */
183     @Override
184     public final String getFederation()
185     {
186         return this.connection.getFederation();
187     }
188 
189     /** {@inheritDoc} */
190     @Override
191     public final String toString()
192     {
193         return "IMBConnector [connection=" + this.connection + "]";
194     }
195 
196 }