/*************************************************************************\
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
*     National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
*     Operator of Los Alamos National Laboratory.
* EPICS BASE Versions 3.13.7
* and higher are distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution. 
\*************************************************************************/
/*
 *      $Id$
 *
 *      Author  Jeffrey O. Hill
 *              johill@lanl.gov
 *              505 665 1831
 *
 *      Experimental Physics and Industrial Control System (EPICS)
 *
 *      Copyright 1991, the Regents of the University of California,
 *      and the University of Chicago Board of Governors.
 *
 *      This software was produced under  U.S. Government contracts:
 *      (W-7405-ENG-36) at the Los Alamos National Laboratory,
 *      and (W-31-109-ENG-38) at Argonne National Laboratory.
 *
 *      Initial development by:
 *              The Controls and Automation Group (AT-8)
 *              Ground Test Accelerator
 *              Accelerator Technology Division
 *              Los Alamos National Laboratory
 *
 *      Co-developed with
 *              The Controls and Computing Group
 *              Accelerator Systems Division
 *              Advanced Photon Source
 *              Argonne National Laboratory
 *
 *
 * History
 * $Log$
 * Revision 1.13  1998/10/27 18:28:19  jhill
 * fixed warnings
 *
 * Revision 1.12  1998/10/23 00:28:19  jhill
 * fixed HP-UX warnings
 *
 * Revision 1.11  1998/05/29 20:13:42  jhill
 * made assert() call portable to 64 bit arch
 *
 * Revision 1.10  1998/02/18 22:44:44  jhill
 * fixed warning
 *
 * Revision 1.9  1997/08/05 00:46:57  jhill
 * fixed warnings
 *
 * Revision 1.8  1997/06/13 09:15:51  jhill
 * connect proto changes
 *
 * Revision 1.7  1997/04/10 19:33:53  jhill
 * API changes
 *
 * Revision 1.6  1996/11/02 00:53:54  jhill
 * many improvements
 *
 * Revision 1.5  1996/09/16 18:23:56  jhill
 * vxWorks port changes
 *
 * Revision 1.4  1996/09/04 20:12:04  jhill
 * added arg to serverToolDebugFunc()
 *
 * Revision 1.3  1996/08/13 22:56:12  jhill
 * added init for mutex class
 *
 * Revision 1.2  1996/08/05 19:25:17  jhill
 * removed unused code
 *
 * Revision 1.1.1.1  1996/06/20 00:28:15  jhill
 * ca server installation
 *
 *
 */

#define CAS_VERSION_GLOBAL

#define caServerGlobal
#include "server.h"
#include "casCtxIL.h" // casCtx in line func

static const osiTime CAServerMaxBeaconPeriod (5.0 /* sec */);
static const osiTime CAServerMinBeaconPeriod (1.0e-3 /* sec */);


//
// caServerI::show()
//
void caServerI::show (unsigned level) const
{
        int			bytes_reserved;

        printf( "Channel Access Server Status V%d.%d\n",
                CA_PROTOCOL_VERSION, CA_MINOR_VERSION);

	this->osiMutex::show(level);

        this->osiLock();
	const tsDLIterBD<casStrmClient> eolSC;
	tsDLIterBD<casStrmClient> iterCl(this->clientList.first());
        while ( iterCl!=eolSC ) {
                iterCl->show(level);
		++iterCl;
        }
        this->dgClient.show(level);

	const tsDLIterBD<casIntfOS> eolIOS;
	tsDLIterBD<casIntfOS> iterIF(this->intfList.first());
        while ( iterIF!=eolIOS ) {
		iterIF->show(level);
		++iterIF;
	}

        this->osiUnlock();

        bytes_reserved = 0u;
#if 0
        bytes_reserved += sizeof(casClient) *
                                ellCount(&this->freeClientQ);
        bytes_reserved += sizeof(casChannel) *
                                ellCount(&this->freeChanQ);
        bytes_reserved += sizeof(casEventBlock) *
                                ellCount(&this->freeEventQ);
        bytes_reserved += sizeof(casAsyncIIO) *
                                ellCount(&this->freePendingIO);
#endif
        if (level>=1) {
                printf(
"There are currently %d bytes on the server's free list\n",
                        bytes_reserved);
#if 0
                printf(
"%d client(s), %d channel(s), %d event(s) (monitors), and %d IO blocks\n",
                        ellCount(&this->freeClientQ),
                        ellCount(&this->freeChanQ),
                        ellCount(&this->freeEventQ),
                        ellCount(&this->freePendingIO));
#endif
                printf( 
	"The server's integer resource id conversion table:\n");
                this->osiLock();
                this->uintResTable<casRes>::show(level);
                this->osiUnlock();
        }

        // @@@@@@ caPrintAddrList(&destAddr);

        return;
}


//
// caServerI::caServerI()
//
caServerI::caServerI (caServer &tool, unsigned nPV) :
caServerOS (*this),
casEventRegistry (* (osiMutex *) this),
dgClient (*this),
//
// Set up periodic beacon interval
// (exponential back off to a plateau
// from this intial period)
//
beaconPeriod (CAServerMinBeaconPeriod),
adapter (tool),
debugLevel (0u),
pvCountEstimate (nPV<100u?100u:nPV), 
haveBeenInitialized (FALSE)
{
	caStatus	status;
	int 	resLibStatus;
	
	assert (&adapter != NULL);
	
	if (this->osiMutex::init ()) {
		errMessage (S_cas_noMemory, "CA server mutex init");
		return;
	}
	
	status = casEventRegistry::initRegistry ();
	if (status) {
		errMessage (status, "CA server event registry init");
		return;
	}
	
	status = caServerIO::init (*this);
	if (status) {
		errMessage (status, "CA server IO internals init");
		return;
	}
	
	if (this->intfList.count()==0u) {
		errMessage (S_cas_noInterface, "CA server internals init");
		return;
	}
	
	status = caServerOS::init();
	if (status) {
		errMessage (status, "CA server OS dependent init");
		return;
	}
	
	status = this->dgClient.init();
	if (status) {
		errMessage (status, "CA server DG init");
		return;
	}
	
	//
	// hash table size may need adjustment here?
	//
	resLibStatus = this->uintResTable<casRes>::init
		(this->pvCountEstimate*2u);
	if (resLibStatus) {
		errMessage (S_cas_noMemory, 
			"integer resource id table init failed");
		return;
	}
	
	this->haveBeenInitialized = TRUE;
	return;
}



/*
 * caServerI::~caServerI()
 */
caServerI::~caServerI()
{

	this->osiLock();

	//
	// delete all clients
	//
	tsDLIterBD<casStrmClient> iter(this->clientList.first());
	tsDLIterBD<casStrmClient> eol;
	tsDLIterBD<casStrmClient> tmp;
	while ( iter!=eol ) {
		tmp = iter;
		++tmp;
		//
		// destructor takes client out of list
		//
		casStrmClient *pC = iter;
		delete pC;
		iter = tmp;
	}

	casIntfOS *pIF;
	while ( (pIF = this->intfList.get()) ) {
		delete pIF;
	}

	this->osiUnlock();
}


//
// caServerI::installClient()
//
void caServerI::installClient(casStrmClient *pClient)
{
	this->osiLock();
	this->clientList.add(*pClient);
	this->osiUnlock();
}


//
// caServerI::removeClient()
//
void caServerI::removeClient(casStrmClient *pClient)
{
	this->osiLock();
	this->clientList.remove(*pClient);
	this->osiUnlock();
}


//
// caServerI::connectCB()
//
void caServerI::connectCB(casIntfOS &intf)
{
        casStreamOS *pNewClient;
        caStatus status;
 
	pNewClient = intf.newStreamClient(*this);
	if (!pNewClient) {
                errMessage(S_cas_noMemory, NULL);
                return;
	}

        status = pNewClient->init();
        if (status) {
                errMessage(status, NULL);
                delete pNewClient;
        }
}


//
// caServerI::advanceBeaconPeriod()
//
// compute delay to the next beacon
//
void caServerI::advanceBeaconPeriod()
{
	//
	// return if we are already at the plateau
	//
	if (this->beaconPeriod >= CAServerMaxBeaconPeriod) {
		return;
	}

	this->beaconPeriod += this->beaconPeriod;

	if (this->beaconPeriod >= CAServerMaxBeaconPeriod) {
		this->beaconPeriod = CAServerMaxBeaconPeriod;
	}
}

//
// casVerifyFunc()
//
void casVerifyFunc(const char *pFile, unsigned line, const char *pExp)
{
        fprintf(stderr, "the expression \"%s\" didnt evaluate to boolean true \n",
			pExp);
        fprintf(stderr,
"and therefore internal problems are suspected at line %u in \"%s\"\n",
                        line, pFile);
        fprintf(stderr,
		"Please forward above text to johill@lanl.gov - thanks\n");
}
 
//
// serverToolDebugFunc()
// 
void serverToolDebugFunc(const char *pFile, unsigned line, const char *pComment)
{
       fprintf(stderr,
"Bad server tool response detected at line %u in \"%s\" because \"%s\"\n",
                line, pFile, pComment);
}

//
// caServerI::addAddr()
//
caStatus caServerI::addAddr(const caNetAddr &addr, int autoBeaconAddr,
			int addConfigBeaconAddr)
{
        caStatus stat;
        casIntfOS *pIntf;
 
        pIntf = new casIntfOS(*this);
        if (pIntf) {
                stat = pIntf->init(addr, this->dgClient, 
				autoBeaconAddr, addConfigBeaconAddr);
                if (stat==S_cas_success) {
			this->osiLock();
                        this->intfList.add(*pIntf);
			this->osiUnlock();
                }
                else {
                        errMessage(stat, NULL); 
                        delete pIntf;
                }
        }
        else {
                stat = S_cas_noMemory;
        }
        return stat;
}


//
// caServerI::sendBeacon()
// (implemented here because this has knowledge of the protocol)
//
void caServerI::sendBeacon()
{
	//
	// send a broadcast beacon over each configured
	// interface unless EPICS_CA_AUTO_ADDR_LIST specifies
	// otherwise. Also send a beacon to all configured
	// addresses.
	// 
	this->osiLock();
	tsDLIterBD<casIntfOS> iter(this->intfList.first());
	while ( iter ) {
		iter->requestBeacon();
		iter++;
	}
	this->osiUnlock();
 
	//
	// double the period between beacons (but dont exceed max)
	//
	this->advanceBeaconPeriod();
}

//
// casEventRegistry::~casEventRegistry()
//
// (must not be inline because it is virtual)
//
casEventRegistry::~casEventRegistry()
{
	this->destroyAllEntries();
}
