# Implementing service functions

For each service API call there must be an associated service implementation function. When the service manager function xxxProcessCommand receives the command from the dispatcher, it determines the associated service implementation function to call.

Service command

The service implementation functions perform whatever processing needs to be done. The service can call dispatcher functions and binding functions if necessary. Typical dispatcher functions invoked are:

- dispGetParms is used to access the service's default parameters.

- dispQueueEvent is used to enqueue a Natural Access DISP_EVENT that was manually allocated and filled in.

- dispMakeAndQueueEvent is a wrapper on top of dispQueueEvent that also allocates the DISP_EVENT structure.

If a service needs to invoke the functionality of another service, it does so with the service's SPI functions.

Service implementation functions are restricted from calling Natural Access API functions. Natural Access API functions do not operate appropriately if invoked from the service implementation.

## Arguments to service implementation functions

The service implementation function receives the DISP_COMMAND message buffer. It unmarshals the function arguments and performs the processing required for the command.

Note: Because services call the SPI functions, it is recommended that the source of the command (DISP_COMMAND addr.source) be preserved (possibly in the mgrcontext) for asynchronous functions. This information is needed later for proper delivery of the associate event.

Once the processing is complete, values for any out arguments must be marshalled back into the DISP_COMMAND message buffer and the proper return code set.

If there are no out arguments and successful completion occurred, return SUCCESS. If there are out arguments and successful completion occurred, return SUCCESS_RESPONSE. If there is an error, return the appropriate error code.

The SUCCESS_RESPONSE return value is used when data is returned to the caller from a synchronous function call. This data is returned in the originating DISP_COMMAND structure passed into the service implementation function. When the Natural Access dispatcher receives SUCCESS_RESPONSE as a return from xxxProcessCommand, it adds information to indicate that there is an immediate command response (not a separate new command) and that the message must be copied back over the network to the service interface. The dispatcher also translates the SUCCESS_RESPONSE return value into a SUCCESS return value to the service interface.

## Handling service parameters as function arguments

Applications use ctaGetParms to retrieve default parameter structures to pass to API functions. Applications may modify the defaults retrieved and pass the new structure to the API or they may pass NULL to tell the service that it should use the context defaults.

If a service implementation function is passed a NULL value for the parameter value, call dispGetParms to retrieve the context default values. If a service needs to call another service with its SPI, it can use any parameter passed to it from its SPI/API to determine how to call the next service's SPI. It can also require a call to dispGetParms to retrieve the defaults and modify the values before calling the SPI, or it can elect to pass a NULL pointer so that the next service will retrieve its own default parameters.

Note: The modification is only valid for the current SPI call when retrieving and directly modifying parameter structure values to be passed to another service's SPI. To modify parameter defaults permanently, call dispSetParmByName.

Default values are only set the first time that a service is defined for an entire system. If two services with the same service interface, but different service implementations, are registered with the dispatcher, only the defaults of the first service to be registered are used.

Services with the same service ID must have the same standard parameter descriptors. They must define the same structures including the fields, formats and units. The dispatcher verifies that matching services register the same standard parameters. An error returns if this is not the case.

tik service

<<<< excerpt from tikcmd.c >>>>

/*----------------------------------------------------------
tikCmdStartTimer
- Implementation function for tikStartTimer
---------------------------------------------------------*/
DWORD tikCmdStartTimer( TIK_CHANNEL_OBJECT *ptikChannel, DISP_COMMAND* m )
{
TIK_CMD_COM_OBJ cmd = { 0 };
TIK_START_PARMS start;
DWORD ret = SUCCESS;
if ( ptikChannel == NULL || m == NULL )
{
return CTAERR_BAD_ARGUMENT;
}
/* Check channel owner. */
if ( ptikChannel->owner != CTA_NULL_SVCID &&
ptikChannel->owner != m->addr.source )
{
return TIKERR_OWNER_CONFLICT;
}
/* channel state must be either IDLE or STARTED. */
if ( !(ptikChannel->state == CHANNEL_TIMER_IDLE ||
ptikChannel->state == CHANNEL_TIMER_STARTED) )
{
return CTAERR_INVALID_SEQUENCE;
}
/* Check if this function call included explicit
* TIK_START_PARMS structure.
* - If present, use passed in structure
* - If not present (i.e., NULL passed), get the default
* TIK_START_PARMS structure via dispGetParms and use it.
*/
if ( m->size1 == 0 && m->dataptr1 == NULL )
{
if( (ret=
dispGetParms( ptikChannel->ctahd,
TIK_START_PARMID,
&start,
sizeof(TIK_START_PARMS) )) != SUCCESS )
{
return ret;
}
}
else
{
DWORD size = ( m->size1 < sizeof(TIK_START_PARMS) )
? m->size1
: sizeof(TIK_START_PARMS);
memcpy( &start, m->dataptr1, size );
}
/* Prepare command (message buffer) to send to TICK server. */
cmd.size = sizeof( TIK_CMD_COM_OBJ );
cmd.timestamp = time(NULL);
cmd.client_id = ptikChannel->channelId;
cmd.command = TIKSVR_START;
cmd.data1 = ptikChannel->channelId;
cmd.data2 = start.Frequency;
cmd.data3 = start.NumTicks;
/* Update channel_info structure. */
ptikChannel->ChannelInfo.requested += cmd.data3;
/* If CTA_TRACEMASK_DEBUG_BIT0 is enabled,
* log state transition information.
*/
if ( TRACEMASK_BIT0_SET(ptikChannel->tracemask) )
{
ret = tikLogStateTransition( ptikChannel,
ptikChannel->state,
CHANNEL_TIMER_STARTED,
"tikCmdStartTimer" );
}
/* Set channel state to STARTED. */
ptikChannel->state = CHANNEL_TIMER_STARTED;
/* Set channel owner. Also used as reply address. */
if ( ptikChannel->owner == CTA_NULL_SVCID )
{
ptikChannel->owner = m->addr.source;
}
if( (ret= tikSendClientCommand( ptikChannel, &cmd )) != SUCCESS )
{
return ret;
}
/* Expecting ticks, queue an asynchronous read request. */
if( (ret=tikReadServerResponse( ptikChannel )) != SUCCESS )
{
return ret;
}
ptikChannel->ChannelInfo.calls++;
return SUCCESS;
} /* end tikCmdStartTimer() */