File: ArClientBase.h

package info (click to toggle)
libaria 2.8.0%2Brepack-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 13,628 kB
  • ctags: 16,574
  • sloc: cpp: 135,490; makefile: 925; python: 597; java: 570; ansic: 182
file content (424 lines) | stat: -rw-r--r-- 15,861 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
#ifndef NLCLIENTBASE_H
#define NLCLIENTBASE_H

#include "Aria.h"
#include "ArClientCommands.h"
#include "ArServerCommands.h"
#include "ArNetPacket.h"
#include "ArNetPacketSenderTcp.h"
#include "ArNetPacketReceiverTcp.h"
#include "ArNetPacketReceiverUdp.h"
#include "ArClientData.h"

/**
   @brief The base client class

   You need to connect a client to a server using blockingConnect() with
   the address for the server.  Then you should add handlers to
   the client to receive information from the server (with addHandler(),
   request(), and requestOnce()).  Then call run() or runAsync()
   (or loopOnce() if you really know what you're doing).
   You can also add a callback that will get called every cycle of the
   client.
   
   User and password information may be required by the server.
   For details on that see ArServerBase.  The most important thing is
   the modification to blockingConnect(), it can now take a user and
   password argument and upon failure you'll want to check wasRejected()
   to determine whether the client couldn't find the server or if the user and
   password were rejected.  Another relevant function is setServerKey()
   to set the key (string of text) we need to connect to the server.

   This class should be thread safe... The only thing you cannot do is
   call 'addHandler' or 'remHandler' from within a handler for that
   specific piece of data.   
 **/

class ArClientBase : public ArASyncTask
{
public:
  
  /// The state of the connection
  enum ClientState {
    STATE_NO_CONNECTION, ///< The client has not connected
    STATE_FAILED_CONNECTION, ///< The client tried to connect and failed
    STATE_OPENED_SOCKET, ///< Client opened socket, waiting for intro from srvr
    STATE_EXCHANGED_INTROS, ///< Client and server have exchanged introductions
    STATE_REJECTED, ///< Client was rejected by server
    STATE_WAITING_LIST, ///< Client was connected to server, waiting for list
    STATE_CONNECTED, ///< Client is connected to server
    STATE_LOST_CONNECTION ///< Client lost connection to server
  };
  
  enum {
    CLIENT_KEY_LENGTH = 16
  };

  enum NonBlockingConnectReturn
  {
    NON_BLOCKING_CONTINUE, ///< Client didn't connect or fail to connect yet
    NON_BLOCKING_CONNECTED, ///< Client connected
    NON_BLOCKING_FAILED ///< Client couldn't connect
  };

  /// Constructor
  AREXPORT ArClientBase();
  /// Destructor
  AREXPORT virtual ~ArClientBase();
  
  /// Sets the optional name of the connected robot for debugging purposes.
  AREXPORT virtual void setRobotName(const char *name, 
				                             bool debugLogging = false,
                                     int robotId = 0);

  /// Returns the optional name of the connected robot for debugging purposes.
  AREXPORT virtual const char *getRobotName() const;

  /// Returns the entire string used to prefix log entries for this client base.
  AREXPORT virtual const char *getLogPrefix() const;

  /// Returns if we're doing debug logging or not
  AREXPORT bool getDebugLogging(void);

  /// Connect to a server
  AREXPORT bool blockingConnect(const char *host, int port, bool log = true,
				const char *user = NULL, 
				const char *password = NULL,
				const char *openOnIP = NULL);

  /// Disconnect from a server
  /**
   * This method blocks while the connection is being shutdown. 
  **/
  AREXPORT bool disconnect(void);

  /// Disconnect from a server next time through the loop
  /**
   * This method does not block while the connection is being
   * shutdown, and the disconnect only happens the next time the
   * client base is in its loop
  **/
  AREXPORT void disconnectSoon(void);

  /** @return true if we're connected to a server */
  AREXPORT bool isConnected(void) { return myState == STATE_CONNECTED;}
  
  /** @return true if a server connection attempt failed because the server rejected the username or password, false if the connection failed for another reason, or the username/password were accepted. */
  AREXPORT bool wasRejected(void) { return myState == STATE_REJECTED; }

  /// Gets the state of the client
  AREXPORT ClientState getState(void) { return myState; }

  /// Adds a functor for some particular data
  AREXPORT bool addHandler(const char *name, 
			    ArFunctor1 <ArNetPacket *> *functor);

  /// Removes a functor for some particular data by name
  AREXPORT bool remHandler(const char *name, ArFunctor1<ArNetPacket *> *functor);

  /// Request some data every @a mSec milliseconds
  AREXPORT bool request(const char *name, long mSec, 
				ArNetPacket *packet = NULL);

  /// Don't want this data anymore
  AREXPORT bool requestStop(const char *name);
  
  /// Request some data (or send a command) just once
  AREXPORT bool requestOnce(const char *name, 
			                      ArNetPacket *packet = NULL,
                            bool quiet = false);

  /// Request some data (or send a command) just once by UDP 
  AREXPORT bool requestOnceUdp(const char *name, 
			       ArNetPacket *packet = NULL, 
			       bool quiet = false);

  /// Request some data (or send a command) just once with a string as argument
  AREXPORT bool requestOnceWithString(const char *name, const char *str);
  
  /// Sees if this data exists
  AREXPORT bool dataExists(const char *name);

  /// Gets the name of the host we tried to connect to
  AREXPORT const char *getHost(void);

  
  /// Sets the 'key' needed to connect to the server
  AREXPORT void setServerKey(const char *serverKey, bool log = true);
  
  /// Enforces the that the server is using this protocol version
  AREXPORT void enforceProtocolVersion(const char *protocolVersion, 
				       bool log = true);

  /// Enforces that the robots that connect are this type
  AREXPORT void enforceType(ArServerCommands::Type type, bool log = true);

  /// Gets the last time a packet was received
  AREXPORT ArTime getLastPacketReceived(void);

  /// Sets the backup timeout
  AREXPORT void setBackupTimeout(double timeoutInMins);

  /// Runs the client in this thread
  AREXPORT virtual void run(void);
  
  /// Runs the client in its own thread
  AREXPORT virtual void runAsync(void);

  /// Stops the thread and sets the stopped flag
  AREXPORT virtual void stopRunning(void);

  /// Returns whether the client has been stopped (regardless of whether its in its own thread)
  AREXPORT virtual bool isStopped();

  /// Print out or data with descriptions
  AREXPORT void logDataList(void);

  /// Adds a functor to call every cycle
  AREXPORT void addCycleCallback(ArFunctor *functor);
  
  /// Removes a functor called every cycle
  AREXPORT void remCycleCallback(ArFunctor *functor);

  /// Send a packet over TCP
  AREXPORT bool sendPacketTcp(ArNetPacket *packet);
  /// Send a packet over UDP (unless client only wants tcp then sends over tcp)
  AREXPORT bool sendPacketUdp(ArNetPacket *packet);

  /// Sets the time to allow for connection (default 3)
  AREXPORT void setConnectTimeoutTime(int sec);

  /// Gets the time allowed for connection
  AREXPORT int getConnectTimeoutTime(void);

  /// Logs the tracking information (packet and byte counts)
  AREXPORT void logTracking(bool terse);
  
  /// Clears the tracking information (resets counters)
  AREXPORT void resetTracking(void);

  /// Adds a call for when the server shuts down 
  AREXPORT void addServerShutdownCB(ArFunctor *functor,
			       ArListPos::Pos position = ArListPos::LAST);

  /// Removes a call for when the server shuts down
  AREXPORT void remServerShutdownCB(ArFunctor *functor);

  /// Adds a call for when a disconnection has occured because of error
  AREXPORT void addDisconnectOnErrorCB(ArFunctor *functor,
			       ArListPos::Pos position = ArListPos::LAST);

  /// Removes a call for when a disconnection has occured because of error
  AREXPORT void remDisconnectOnErrorCB(ArFunctor *functor);

  /// Run the loop once
  AREXPORT void loopOnce(void);

  /// Process the packet wherever it came from
  AREXPORT void processPacket(ArNetPacket *packet, bool tcp);

  /// Process a packet from udp (just hands off to processPacket)
  AREXPORT void processPacketUdp(ArNetPacket *packet,
				struct sockaddr_in *sin);

  /// Sets it so we only get TCP data from the server not UDP
  AREXPORT void setTcpOnlyFromServer(void);
  /// Sets it so we only send TCP data to the server
  AREXPORT void setTcpOnlyToServer(void);

  /// Returns whether we only get TCP data from the server not UDP
  AREXPORT bool isTcpOnlyFromServer(void);
  /// Returns whether we only send TCP data to the server
  AREXPORT bool isTcpOnlyToServer(void);
  /// Gets a (probably) unique key that can be used to identify the client (after connection)
  AREXPORT void getClientKey(unsigned char key[CLIENT_KEY_LENGTH]);
  

  /// Starts the process of disconnecting from the server.
  /**
   * This method should be called in threads that should not block
   * (such as the main GUI thread).  The call should be followed by 
   * a delay of one second, followed by a call to 
   * finishNonBlockingDisconnect().
  **/
  AREXPORT bool startNonBlockingDisconnect();

  /// Finishes the process of disconnecting from the server.
  /**
   * This method should be used in conjunction with 
   * startNonBlockingDisconnect in threads that should not block
   * (such as the main GUI thread).  
  **/
  AREXPORT void finishNonBlockingDisconnect();

  /// Gets the name of the data a packet is for
  AREXPORT const char *getName(ArNetPacket *packet, bool internalCall = false);
  /// Gets the name of the data a command is
  AREXPORT const char *getName(unsigned int command, bool internalCall = false);
  // the function for the thread
  AREXPORT virtual void * runThread(void *arg);
  /// Internal function to get the socket (no one should need this)
  AREXPORT struct in_addr *getTcpAddr(void) { return myTcpSocket.inAddr(); }
  /// Internal function that'll do the connection (mainly for switch connections)
  AREXPORT bool internalBlockingConnect(
	  const char *host, int port, bool log, const char *user, 
	  const char *password, ArSocket *tcpSocket, 
	  const char *openOnIP = NULL);
  /// Internal function that'll start the non blocking connection (mainly for switch connections)
  AREXPORT NonBlockingConnectReturn internalNonBlockingConnectStart(
	  const char *host, int port, bool log, const char *user, 
	  const char *password, ArSocket *tcpSocket, 
	  const char *openOnIP = NULL);
  /// Internal function that'll start the non blocking connection (mainly for switch connections)
  AREXPORT NonBlockingConnectReturn internalNonBlockingConnectContinue(void);
  /// Internal function to get the tcp socket
  AREXPORT ArSocket *getTcpSocket(void) { return &myTcpSocket; }
  /// Internal function to get the udp socket
  AREXPORT ArSocket *getUdpSocket(void) { return &myUdpSocket; }
  /// Internal function get get the data map
  AREXPORT const std::map<unsigned int, ArClientData *> *getDataMap(void)
    { return &myIntDataMap; }
  /// Returns the command number from the name
  AREXPORT unsigned int findCommandFromName(const char *name);
  /// Request some data every mSec milliseconds by command not name
  AREXPORT bool requestByCommand(unsigned int command, long mSec, 
				 ArNetPacket *packet = NULL);
  /// Don't want this data anymore, by command not name
  AREXPORT bool requestStopByCommand(unsigned int command);
  
  /// Request some data (or send a command) just once by command not name
  AREXPORT bool requestOnceByCommand(unsigned int command,
			    ArNetPacket *packet = NULL);
  /// Request some data (or send a command) just once by command not name
  AREXPORT bool requestOnceByCommandUdp(unsigned int command,
			    ArNetPacket *packet = NULL);
  /// Gets if we received the list of commands
  bool getReceivedDataList(void) { return myReceivedDataList; }
  /// Gets if we received the list of arguments and
  bool getReceivedArgRetList(void) { return myReceivedArgRetList; }
  /// Gets if we received the list of commands
  bool getReceivedGroupAndFlagsList(void) { return myReceivedGroupAndFlagsList; }
  /// Tells us the reason the connection was rejected (see ArServerCommands for details)
  int getRejected(void) { return myRejected; }
  /// Tells us the reason the connection was rejected (see ArServerCommands for details)
  const char *getRejectedString(void) { return myRejectedString; }
protected:
  
  /// Optional robot name for logging
  std::string myRobotName;
  /// Optional prefix to be inserted at the start of log messages
  std::string myLogPrefix;
  /// If we're enforcing the version of the protocol or not
  std::string myEnforceProtocolVersion;
  /// if we're a particular type
  ArServerCommands::Type myEnforceType;

  AREXPORT bool setupPacket(ArNetPacket *packet);
  ArTime myLastPacketReceived;
  std::list<ArFunctor *> myServerShutdownCBList;
  std::list<ArFunctor *> myDisconnectOnErrorCBList;
  std::list<ArFunctor *> myCycleCallbacks;
  void clear(void);
  // does the first part of connection
  bool internalConnect(const char *host, int port, bool obsolete,
		       const char *openOnIP);
  void internalStartUdp(void);
  void buildList(ArNetPacket *packet);
  void internalSwitchState(ClientState state);
  bool myReceivedDataList;
  bool myReceivedArgRetList;
  bool myReceivedGroupAndFlagsList;
  ClientState myState;
  ArTime myStateStarted;
  bool myUdpConfirmedFrom;
  bool myUdpConfirmedTo;
  // if we only send tcp
  bool myTcpOnlyTo;
  // if we only receive tcp from the server
  bool myTcpOnlyFrom;

  bool myIsRunningAsync;
  bool myDisconnectSoon;
  bool myIsStartedDisconnect;
  bool myIsStopped;

  bool myQuiet;
  bool myDebugLogging;
  ArLog::LogLevel myVerboseLogLevel;
  std::string myHost;
  std::string myUser;
  std::string myPassword;
  // the time we allow for connections
  int myTimeoutTime;
  // the time we started our connection
  ArTime myStartedConnection;
  ArTime myStartedUdpConnection;
  ArTime myStartedShutdown;
  
  enum NonBlockingConnectState
  {
    NON_BLOCKING_STATE_NONE,
    NON_BLOCKING_STATE_TCP,
    NON_BLOCKING_STATE_UDP
  };
  
  NonBlockingConnectState myNonBlockingConnectState;
  

  ArMutex myDataMutex;
  ArMutex myClientMutex;
  ArMutex myMapsMutex;
  ArMutex myStateMutex;
  ArMutex myCallbackMutex;
  ArMutex myCycleCallbackMutex;
  ArMutex myPacketTrackingMutex;
  // our map of names to ints
  std::map<std::string, unsigned int> myNameIntMap;
  // our map of ints to functors
  std::map<unsigned int, ArClientData *> myIntDataMap;
  
  struct sockaddr_in myUdpSin;
  bool myUdpSinValid;
  // the port the server said it was using
  unsigned int myServerReportedUdpPort;
  // the port the server actually is using
  unsigned int myServerSentUdpPort;
  unsigned int myUdpPort;
  long myAuthKey;
  long myIntroKey;
  std::string myServerKey;
  double myBackupTimeout;
  // this is a key we have for identifying ourselves moderately uniquely
  unsigned char myClientKey[16];
  ArNetPacketSenderTcp myTcpSender;
  ArNetPacketReceiverTcp myTcpReceiver;
  ArNetPacketReceiverUdp myUdpReceiver;
  ArSocket myTcpSocket;
  ArSocket myUdpSocket;
  ArFunctor2C<ArClientBase, ArNetPacket *, bool> myProcessPacketCB;
  ArFunctor2C<ArClientBase, ArNetPacket *, struct sockaddr_in *> myProcessPacketUdpCB;

  int myRejected;
  char myRejectedString[32000];

  ArTime myTrackingStarted;
  class Tracker
  {
  public:
    Tracker() { reset(); }
    virtual ~Tracker() {}
    void reset(void) 
      { myPacketsTcp = 0; myBytesTcp = 0; myPacketsUdp = 0; myBytesUdp = 0; }
    long myPacketsTcp;
    long myBytesTcp;
    long myPacketsUdp;
    long myBytesUdp;
  };
  AREXPORT void trackPacketSent(ArNetPacket *packet, bool tcp);
  AREXPORT void trackPacketReceived(ArNetPacket *packet, bool tcp);
  std::map<unsigned int, Tracker *> myTrackingSentMap;
  std::map<unsigned int, Tracker *> myTrackingReceivedMap;

};

#endif // NLCLIENTBASE_H