static char AperiodicHandler_id[] = "$Header: /u/oreilly/rov/tmacs/RCS/AperiodicHandler.cc,v 1.13 1998/03/18 16:25:45 oreilly Exp $";


/*
$Log: AperiodicHandler.cc,v $
Revision 1.13  1998/03/18 16:25:45  oreilly
*** empty log message ***

Revision 1.12  1997/12/12 17:49:59  oreilly
*** empty log message ***

Revision 1.11  1997/10/03 09:26:38  oreilly
Use local SList iterator

Revision 1.10  97/09/10  21:07:58  21:07:58  oreilly (Thomas C. O'Reilly)
*** empty log message ***

Revision 1.9  97/09/10  20:31:12  20:31:12  oreilly (Thomas C. O'Reilly)
*** empty log message ***

Revision 1.8  97/08/12  14:34:02  14:34:02  oreilly (Thomas C. O'Reilly)
DmGroupItem structure is passed between AperiodicHandler and dmonitor

Revision 1.7  97/04/30  19:32:08  19:32:08  oreilly (Thomas C. O'Reilly)
Check that DmGuiObject is in list when unregistering it

Revision 1.6  97/03/20  12:28:42  12:28:42  oreilly (Thomas C. O'Reilly)
*** empty log message ***

Revision 1.5  96/10/28  09:11:45  09:11:45  oreilly (Thomas C. O'Reilly)
*** empty log message ***

Revision 1.4  96/10/18  14:15:24  14:15:24  oreilly (Thomas C. O'Reilly)
Using workprocs to read input pipe

Revision 1.3  96/07/22  10:41:47  10:41:47  oreilly (Thomas C. O'Reilly)
First external release

*/
#include <stdlib.h>
#include <unistd.h>
#include <values.h>
#include <sys/ioctl.h>
#include <Vk/VkApp.h>
#include <Vk/VkErrorDialog.h>
#include "AperiodicHandler.h"
#include "DmGuiApp.h"
#include "DmMonitor.h"

RegisteredDmItem::RegisteredDmItem(const char *name, DM_Item handle)
{
  _itemName = strdup(name);
  _handle = handle;
}


AperiodicHandler::AperiodicHandler()
{
  _monitorPid = _monitorSem = 0;
  _inputId = NoInput;

  MBool debug = FALSE;

  const char *monitorProcess = "dmonitor";
  dprintf(stderr, "semBCreate()...\n");
  if ((_monitorSem = semBCreate(SEM_Q_FIFO, SEM_EMPTY)) == SEM_NULL)
  {
    fprintf(stderr, 
	    "AperiodicHandler::AperiodicHandler() - "
	    "couldn't create _monitorSem\n");
    exit(1);
  }
  dprintf(stderr, "done\n");  
  int readFd[2];
  int writeFd[2];
  if (pipe(readFd) == -1)
  {
    perror("AperiodicHandler::AperiodicHandler() - pipe(readFd)");
    exit(1);
  }
  _readPipe = readFd[0];
  dprintf(stderr, "pipe 1: readFd = %d, writeFd = %d\n", readFd[0], readFd[1]);

  if (pipe(writeFd) == -1)
  {
    perror("AperiodicHandler::AperiodicHandler() - pipe(writeFd)");
    exit(1);
  }
  _writePipe = writeFd[1];
  dprintf(stderr, "pipe 2: readFd = %d, writeFd = %d\n", 
	  writeFd[0], writeFd[1]);  
  char readFdStr[10];
  char writeFdStr[10];
  char semKeyStr[10];
  
  // Start DM aperiodic monitor program, and create a pipe to read messages
  switch (_monitorPid = fork())
  {    
    case -1:
    perror("AperiodicHandler::AperiodicHandler() - fork()");
    exit(1);
    break;
    
    case 0:
    /* In child process */
    if (close(readFd[0]))
    {
      perror("AperiodicHandler::AperiodicHandler() - close(readFd[0])");
      exit(1);
    }
    if (close(writeFd[1]))
    {
      perror("AperiodicHandler::AperiodicHandler() - close(writeFd[1])");
      exit(1);      
    }

    sprintf(readFdStr, "%d", writeFd[0]);
    sprintf(writeFdStr, "%d", readFd[1]);
    sprintf(semKeyStr, "%d", _monitorSem);
    
    /* Fire up monitor process, passing pipe descriptors as arguments */
    execlp(monitorProcess, monitorProcess, readFdStr, writeFdStr, semKeyStr,
	   NULL);
    
    perror("AperiodicHandler::AperiodicHandler() - "
	   "execlp of monitor process");

    exit(1);


    default:
    if (close(readFd[1]))
    {
      perror("AperiodicHandler::AperiodicHandler() - close(readFd[1])");
      exit(1);      
    }
    if (close(writeFd[0]))
    {
      perror("AperiodicHandler::AperiodicHandler() - close(writeFd[0])");
      exit(1);      
    }   
  }
}


AperiodicHandler::~AperiodicHandler()
{
  cleanup();
  
}


int AperiodicHandler::registerObj(DmGuiObject *dmGuiObject) 
{
  DmObject **dmObject;
  RegisteredDmItem **dmItem;
  char buf[512];
  RequestMessage requestMsg;
  
  SListIter<DmObject *> consumerDmObjects(dmGuiObject->_consumerDmObjectList);
  
  consumerDmObjects.reset();

  while (dmObject = consumerDmObjects())
  {
    MBool found = FALSE;
    MBool sendRequest = FALSE;
    SListIter<RegisteredDmItem *> dmItems(_dmItemList);
    
    dmItems.reset();  
    void *guiListPtr;
      
    while (dmItem = dmItems())
    {
      if ((*dmObject)->getHandle() == (*dmItem)->_handle)
      {
	/* Handle has been registered */
	found = TRUE;

	/* If handle currently has no associated DmGuiObjects, we'll 
	   need to send a register request to the monitor process */
	if ((*dmItem)->length() == 0)
	{
	  guiListPtr = (void *)(*dmItem);
	  sendRequest = TRUE;
	}
	  
	/* Add dmGuiObject to handle's list */
	(*dmItem)->addDmGuiObject(dmGuiObject);
	break;
      }
    }
    
    if (!found)
    {
      /* Add new handle to registered items */
      RegisteredDmItem *newItem = 
	new RegisteredDmItem((*dmObject)->getName(), 
			     (*dmObject)->getHandle());

      /* Add dmGuiObject to handle's list */
      newItem->addDmGuiObject(dmGuiObject);
      /* Add new DM item to registered items */
      _dmItemList.append(newItem);

      guiListPtr = (void *)newItem;
      sendRequest = TRUE;
    }
      
    if (sendRequest)
    {
      /* Write register info to monitor process */
      strcpy(requestMsg.itemName, (*dmObject)->getName());
      requestMsg.cmd = AddItem;

      // arrayIndex currently not used
      requestMsg.arrayIndex = 0;
      
      // NOTE: Might need DM_STATIC depending on requested period
      requestMsg.period = DM_ASYNC;
      requestMsg.guiListPtr = guiListPtr;
	
      if (writeRequest(&requestMsg) == -1)
	return -1;
    }
  }

  return 0;
}


int AperiodicHandler::unregisterObj(DmGuiObject *guiObj)
{
  // Go through all registered handles, make sure guiObj is removed
  RegisteredDmItem **dmItem;

  SListIter<RegisteredDmItem *> dmItems(_dmItemList);
  dmItems.reset();
  MBool found = FALSE;
  
  while (dmItem = dmItems())
  {
    int ret = (*dmItem)->removeDmGuiObject(guiObj);
    if (ret == 0)
      found = TRUE;
    
    if (ret == 0 && 
	 (*dmItem)->length() == 0)
    {
      /* Note that even though there are no more DmGuiObjects in this
	 RegisteredDmItem, we do NOT want to remove the RegisteredDmItem. 
	 Instead, just leave it and its DmGuiObject iterator intact.
	 (Removing it would also remove the associated DmGuiObject iterator, 
	 which is referenced directly by the asynchronously-running 
	 dmonitor process. This could result in invalid memory references.)
	 */

      /* Write unregister info to monitor process */
      RequestMessage requestMsg;
      strcpy(requestMsg.itemName, (*dmItem)->_itemName);
      requestMsg.cmd = RemoveItem;
      // arrayIndex currently not used
      requestMsg.arrayIndex = 0;
      requestMsg.period = DM_ASYNC;
      requestMsg.guiListPtr = (void *)NULL;
	
      if (writeRequest(&requestMsg) == -1)
	return -1;

    }
  }
  if (!found)
    return -1;
  else
    return 0;
}


void AperiodicHandler::start() 
{
  _inputId = XtAppAddInput(theApplication->appContext(), _readPipe,
			   (XtPointer )XtInputReadMask, 
			   &AperiodicHandler::dataReadyCallback, this);
}



void AperiodicHandler::dataReadyCallback(
					 XtPointer clientData,
					 int *fid,
					 XtInputId *id
					 )
{
  AperiodicHandler *obj = (AperiodicHandler *)clientData;

  // Stop monitoring pipe while working on pipe data
  XtRemoveInput(obj->_inputId);
  obj->_inputId = NoInput;
  
  XtAppAddWorkProc(theApplication->appContext(), 
		   &AperiodicHandler::newDataWorkProc, obj);
}


Boolean AperiodicHandler::newDataWorkProc(XtPointer clientData)
{
  AperiodicHandler *obj = (AperiodicHandler *)clientData;
  return obj->processNewData();
}


Boolean AperiodicHandler::processNewData()
{
  char errorBuf[errorMsgSize];
  MBool debug = ((DmGuiApp *)theApplication)->debug();
  long nBytes;
  MonitoredGroupItem groupItem;
  
  dprintf(stderr, "processNewData()\n");
  
  if (ioctl(_readPipe, FIONREAD,  &nBytes) == EINVAL)
  {
    perror("AperiodicHandler::handleNewData()");
    exit(1);
  }
  else
    dprintf(stderr, 
	    "processNewData() - %d bytes in pipe, sizeofDmGroupItem=%d\n", 
	    nBytes, sizeof(MonitoredGroupItem));
  
  if (!nBytes)
  {
    // No more data in pipe
    dprintf(stderr, "processNewData() - no more data\n");
    
    // Start monitoring pipe again
    _inputId = XtAppAddInput(theApplication->appContext(), _readPipe,
			     (XtPointer )XtInputReadMask, 
			     &AperiodicHandler::dataReadyCallback, this);

    // Done with work proc
    return True;
  }
  
  if (nBytes < sizeof(MonitoredGroupItem))
    // Full packet not available yet
    return False;
  
  if ((nBytes = read(_readPipe, &groupItem, sizeof(MonitoredGroupItem)))
      != sizeof(MonitoredGroupItem))
  {
    perror("AperiodicHandler::processNewData() - read()");
    exit(1);
  }

  RegisteredDmItem *dmItem = (RegisteredDmItem *)groupItem.guiListPtr;
  
  dprintf(stderr, "AperiodicHandler::processNewData(), handle=%x\n",
	  groupItem.handle);

  SListIter<DmGuiObject *> objects(dmItem->_objectList);
  
  objects.reset();
  DmGuiObject **dmGuiObject;
  // Update all the DmGuiObjects associated with changed item
  while (dmGuiObject = objects.next())
  {
    dprintf(stderr, "DmGuiObject \"%s\"... ", (*dmGuiObject)->name());
    
    if ((*dmGuiObject)->consumeEnabled())
    {
      dprintf(stderr, "updateGui()...");
      (*dmGuiObject)->updateGui(dmItem->_handle);
      dprintf(stderr, "done.\n");
      if ((*dmGuiObject)->error())
      {
	sprintf(errorBuf,
		"AperiodicHandler::processNewData() - error from "
		"updateGui() function of \"%s\"\n", 
		(*dmGuiObject)->name());

	fprintf(stderr, "%s\n", errorBuf);
	(*dmGuiObject)->prtErrorMsg();
	(*dmGuiObject)->clearError();
      }
    }
    else
      dprintf(stderr, "consume not enabled\n");
  }

  return False;
}


DmGuiApp *AperiodicHandler::getDmGuiApp() 
{
  return (DmGuiApp *)theApplication;
}


int AperiodicHandler::writeRequest(RequestMessage *request)
{
  int nBytes;
  if ((nBytes = write(_writePipe, request, sizeof(RequestMessage))) !=
      sizeof(RequestMessage))
  {
    char buf[512];
    sprintf(buf, "AperiodicHandler::writeRequest() - "
	    "write to pipe failed, %d bytes", nBytes);
    perror(buf);
    return -1;
  }

  /* Notify monitor of newly registered info */
  semGive(_monitorSem);
  return 0;
}

extern "C" 
{
  STATUS semDelete( SEM_ID sem );
};


int AperiodicHandler::cleanup()
{
  MBool debug = ((DmGuiApp *)theApplication)->debug();

  /* Don't monitor spurious input from dead/dying process */
  if (_inputId != NoInput)
  {
    XtRemoveInput(_inputId);
    _inputId = NoInput;
  }
  
  if (_monitorSem) {
    dprintf(stderr, "destroy _monitorSem...");
    semDelete(_monitorSem);
    dprintf(stderr, "Done\n");

    _monitorSem = 0;
  }

  dprintf(stderr, "AperiodicHandler() - _monitorPid = %d\n", _monitorPid);
  if (_monitorPid)
  { 
    dprintf(stderr, "Terminate monitor process...");  

    // Terminate child process(es)
    if (kill(_monitorPid, SIGTERM) == -1) 
    {   
      perror("AperiodicHandler::~AperiodicHandler() - failed to "
	     "terminate monitor process");
    }
    dprintf(stderr, "Done\n");
  
    _monitorPid = 0;
  }
  
  return 0;
}



