/****************************************************************************/
/* Copyright (c) 2000 MBARI                                                 */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Summary  :                                                               */
/* Filename : ExternalComms.h                                               */
/* Author   :                                                               */
/* Project  :                                                               */
/* Version  : 1.0                                                           */
/* Created  : 02/07/2000                                                    */
/* Modified :                                                               */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/****************************************************************************/
#ifndef _EXTERNALCOMMS_H
#define _EXTERNALCOMMS_H

#include "Task.h"
#include "SystemInterfaces.h"
#include "EventService.h"
#include "DynamicArray.h"
#include "ExternalMessage.h"

#define AltexSocketPort 1025
#define MaxRequestBytes 512
#define MaxReplyBytes 1024

#define ExternalCommsName "externalComms"

/* 
We want ExternalComms::run() to wait for either off-board client
messages (via a socket) or QNX messages from onboard processes
(via Receive()). QNX will allow us to do this, but in a somewhat
roundabout way. We must replace an internal QNX "C" function, 
_select_receive(), which is called by the select() system call. 
Activity on a socket results in a "select proxy" being fired. 
Our _select_receive() does a Receive(); if it receives a message 
from the "select proxy", it returns this info to select() which 
processes it. If _select_receive() receives a message from any other 
process, it calls our function processReceivedMsg(), which calls 
ExternalComms::processOnboardMsg().
*/
extern "C" {

  // _select_receive() is called by the select() system call
  pid_t _select_receive(pid_t selectProxy);

  // Since processOnboardMsg() is called by _select_receive(), it
  // must be given extern "C" linkage, even though it's a C++
  // function.
  void processReceivedMsg(char *msg);
};


/*
CLASS 
ExternalComms

DESCRIPTION
Provides interface between onboard and offboard processes. Provides
the following services to offboard clients:

* Provides list of current TaskInterface objects

* TaskInterface method invocation

* Subscription to Task events

* Notification of Task events


AUTHOR
Tom O'Reilly
*/
class ExternalComms : public Task {

public:

  ///////////////////////////////////////////////////////////////////
  // Constructor
  ExternalComms();

  ///////////////////////////////////////////////////////////////////
  // Destructor
  ~ExternalComms();

  ///////////////////////////////////////////////////////////////////
  // Wait for and process messages, forward event notification to
  // offboard clients
  virtual void run();

protected:

  class ClientSocketStream {

  public:
    ClientSocketStream(int socket);
    ~ClientSocketStream();

    int read(SharedObject::Request *request);
    int write(SharedObject::Reply *reply);

  protected:
    int _socket;
  };

  ///////////////////////////////////////////////////////////////////
  // This function is called by the 'select()' callback 'C' code,
  // and it then invokes protected methods of ExternalComms
  friend void processReceivedMsg(char *msg);

  ///////////////////////////////////////////////////////////////////
  // Create socket and start listening on it
  int createSocket();

  ///////////////////////////////////////////////////////////////////
  // Container for TaskInterface objects
  SystemInterfaces *_system;

  ///////////////////////////////////////////////////////////////////
  // Socket to which clients initially connect, at port AltexSocketPort
  int _socket;

  ///////////////////////////////////////////////////////////////////
  // Array of client sockets
  DynamicArray<int> _clientSocket;

  ///////////////////////////////////////////////////////////////////
  // Buffer for requests; MaxRequestBytes allocated
  ExternalMessage *_externalMessage;

  ///////////////////////////////////////////////////////////////////
  // Buffer for replies; MaxReplyBytes allocated
  char *_replyPtr;

  ///////////////////////////////////////////////////////////////////
  // Send list of TaskInterface objects to offboard client
  void listTasks(ExternalMessage *message, int client);

  ///////////////////////////////////////////////////////////////////
  // Invoke TaskInterface method for offboard client
  void invokeTaskMethod(ExternalMessage *message, int client);

  ///////////////////////////////////////////////////////////////////
  // Subscribe to specified event from specified TaskInterface
  void subscribe(ExternalMessage *message, int client);

  ///////////////////////////////////////////////////////////////////
  // Process message from offboard process
  void processExternalMsg(ExternalMessage *message, int client);

  ///////////////////////////////////////////////////////////////////
  // Process message from onboard process
  void processOnboardMsg(char *msg);

  ///////////////////////////////////////////////////////////////////
  // Forward Task event notification to offboard subscribers
  void forwardNotification(TaskInterface *eventSource, EventCode eventCode);

  EventService *_eventService;


private:

  struct Subscription {
    int clientSocket;
    TaskInterface *task;
    EventCode eventCode;
  };

  DynamicArray <Subscription *> _subscribers;

};


#endif

