2378 lines
65 KiB
C
2378 lines
65 KiB
C
/* dbCaLink.c */
|
||
/* share/src/db $Id$ */
|
||
|
||
/****************************************************************
|
||
*
|
||
* Author: Nicholas T. Karonis
|
||
* Date: 01-01-92
|
||
*
|
||
* 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
|
||
*
|
||
* Modification Log:
|
||
* -----------------
|
||
* .01 07-16-92 jba changed VALID_ALARM to INVALID_ALARM
|
||
* .01 07-16-92 jba changes made to remove compile warning msgs
|
||
*
|
||
****************************************************************/
|
||
|
||
#include <errno.h>
|
||
|
||
#include <cadef.h>
|
||
#include <caerr.h>
|
||
#include <db_access.h>
|
||
|
||
/* needed for calloc */
|
||
#include <stdlib.h>
|
||
|
||
/* needed for sprintf */
|
||
#include <stdio.h>
|
||
|
||
/* needed for vxTas */
|
||
#include <vxLib.h>
|
||
|
||
/* needed for PVNAME_SZ and FLDNAME_SZ */
|
||
#include <dbDefs.h>
|
||
|
||
/* needed for struct link */
|
||
#include <link.h>
|
||
|
||
/* needed for FastLock stuff */
|
||
#include <fast_lock.h>
|
||
|
||
/* needed for alarm handling */
|
||
#include <alarm.h>
|
||
|
||
/* needed for spawnTask priority parms */
|
||
#include <taskLib.h>
|
||
|
||
/* needed for #define memcpy() */
|
||
#include <string.h>
|
||
|
||
#include <task_params.h>
|
||
|
||
#include <errMdef.h>
|
||
#include <calink.h>
|
||
|
||
#define PVARNAMELENGTH ((PVNAME_SZ)+(FLDNAME_SZ)+2)
|
||
#define NODESPERCALLOC 10
|
||
|
||
#define BOOL char
|
||
|
||
/*******************/
|
||
/* */
|
||
/* Data Structures */
|
||
/* */
|
||
/*******************/
|
||
|
||
/* for input links */
|
||
|
||
struct input_pvar
|
||
{
|
||
struct input_pvar *next;
|
||
void *pdest_dbaddr;
|
||
char source_name[PVARNAMELENGTH];
|
||
chid cid;
|
||
char dest_name[PVARNAMELENGTH];
|
||
void *pvalue;
|
||
short dest_old_dbr_type;
|
||
short dest_old_dbr_sts_type; /* for checking in my_event_handler() */
|
||
short revised_dest_new_dbr_type;
|
||
unsigned short source_severity;
|
||
int dest_old_dbr_element_size; /* for calloc() and memcpy() */
|
||
FAST_LOCK lock;
|
||
BOOL maximize_severity;
|
||
BOOL needs_reading;
|
||
long dest_nelements; /* for calloc() and check in my_event_handler() */
|
||
long last_rcvd_nelements; /* set in my_event_handler() and used in */
|
||
/* dbCaGetLink()'s call to dbCaDbPut() */
|
||
}; /* end struct input_pvar */
|
||
|
||
/* for output links */
|
||
|
||
struct output_pvar
|
||
{
|
||
struct output_pvar *next;
|
||
struct output_pvar *write_next;
|
||
void *psource_dbaddr;
|
||
void *pvalue;
|
||
char dest_name[PVARNAMELENGTH];
|
||
char source_name[PVARNAMELENGTH];
|
||
chid cid;
|
||
short source_old_dbr_type; /* for output demon ca_put() */
|
||
short source_new_dbr_type; /* for dbCaPutLink() call to dbGetField() */
|
||
short revised_source_new_dbr_type;
|
||
FAST_LOCK lock;
|
||
BOOL valid_value;
|
||
BOOL on_writelist;
|
||
long source_nelements; /* for calloc() and check on dbCaPutLink() */
|
||
long last_nelements_written; /* set by dbCaPutLink(); ca_array_put() uses */
|
||
}; /* end struct output_pvar */
|
||
|
||
/*******************************/
|
||
/* */
|
||
/* Internally Global Variables */
|
||
/* */
|
||
/*******************************/
|
||
|
||
static struct input_pvar *Input_pvar_avail_hdr;
|
||
static struct input_pvar *Pvar_inputlist_hdr;
|
||
|
||
static struct output_pvar *Output_pvar_avail_hdr;
|
||
static struct output_pvar *Pvar_outputlist_hdr;
|
||
static struct output_pvar *Pvar_writelist_hdr;
|
||
static struct output_pvar *Pvar_disconnectedlist_hdr;
|
||
|
||
static FAST_LOCK Buffer;
|
||
|
||
/* Binary Semaphore to signal inspect Pvar_outputlist_hdr */
|
||
/* static FAST_LOCK LookAtBuffer; */
|
||
static SEM_ID LookAtBuffer;
|
||
|
||
/****************************************/
|
||
/* */
|
||
/* Externally Visibile Remote Functions */
|
||
/* */
|
||
/****************************************/
|
||
|
||
/* in dbCaDbLink.c */
|
||
|
||
long dbCaNameToAddr();
|
||
long dbCaCopyPvar();
|
||
long dbCaMaximizeSeverity();
|
||
long dbCaDbPut();
|
||
short dbCaNewDbfToNewDbr(); /* conversion */
|
||
/* these two should go away when channel access understands new db technology */
|
||
short dbCaNewDbrToOldDbr(); /* conversion */
|
||
short dbCaOldDbrToNewDbr(); /* conversion */
|
||
short dbCaOldDbrToOldDbrSts(); /* conversion */
|
||
/* these should be part of db access ... currently in dbCaDbLink.c */
|
||
char *dbCaRecnameFromDbCommon();
|
||
short dbCaDbfFromDbAddr();
|
||
long dbCaNelementsFromDbAddr();
|
||
|
||
/**************************************/
|
||
/* */
|
||
/* Externally Visible Local Functions */
|
||
/* */
|
||
/**************************************/
|
||
|
||
long dbCaAddInlink(); /* called for each input CA_LINK DURING rec init */
|
||
long dbCaAddOutlink(); /* called for each output CA_LINK DURING rec init */
|
||
long dbCaGetLink(); /* called by rec proc when inputing on CA_LINK */
|
||
void dbCaLinkInit(); /* called once BEFORE any rec init */
|
||
void dbCaProcessInlinks(); /* SPAWNED once AFTER all rec init */
|
||
void dbCaProcessOutlinks(); /* SPAWNED once AFTER all rec init */
|
||
long dbCaPutLink(); /* called by rec proc when outputing on CA_LINK */
|
||
|
||
/***************************/
|
||
/* */
|
||
/* Private Local Functions */
|
||
/* */
|
||
/***************************/
|
||
|
||
static long flush_channel_request_buffer();
|
||
static void init_global_vars();
|
||
static void my_connection_handler();
|
||
static void my_event_handler();
|
||
static struct input_pvar *pop_input_pvar();
|
||
static struct output_pvar *pop_output_pvar();
|
||
static long process_asynch_events();
|
||
static void process_asynch_events_task();
|
||
static void push_input_pvar();
|
||
static void push_output_pvar();
|
||
static long queue_add_event_from_input_pvar();
|
||
static long queue_ca_array_put();
|
||
static long queue_ca_build_and_connect();
|
||
static long queue_ca_search();
|
||
static long task_initialize_channel_access();
|
||
|
||
/****************************************************************
|
||
*
|
||
* EXTERNALLY VISIBLE FUNCTIONS
|
||
*
|
||
****************************************************************/
|
||
|
||
/****************************************************************
|
||
*
|
||
* long dbCaAddInlink(plink, precord, dest_fieldname)
|
||
* struct link *plink;
|
||
* void *precord;
|
||
* char *dest_fieldname;
|
||
*
|
||
* Description:
|
||
* During record initialization a record was found to have an input link
|
||
* whose source is in a different physical database (on another IOC). This
|
||
* function registers that input link as remote and changes he link type from
|
||
* PV_LINK to CA_LINK.
|
||
*
|
||
* Input:
|
||
* struct link *plink pointer to input link structure
|
||
* char *dest_recname name of destination record
|
||
* char *dest_fieldname name of destiantion field
|
||
* short dest_new_dbf_type dest DBF type using NEW db technology
|
||
* int dest_fld_size size of destination field
|
||
*
|
||
* Output: None.
|
||
*
|
||
* Returns:
|
||
* any rc from dbNameToAddr()
|
||
* S_dbCa_nullarg - received a NULL pointer in one of the input args
|
||
* S_dbCa_failedmalloc - could not dynamically allocate memory
|
||
*
|
||
* Notes:
|
||
* In registering this input link, the db address of the destination process
|
||
* variable is recorded in a separate list through a function call to
|
||
* dbCaNameToAddr() found in dbCaLink.c.
|
||
*
|
||
****************************************************************/
|
||
|
||
long dbCaAddInlink(plink, pdest_record, dest_fieldname)
|
||
struct link *plink;
|
||
void *pdest_record;
|
||
char *dest_fieldname;
|
||
{
|
||
|
||
struct input_pvar *pi;
|
||
char *dest_recname;
|
||
char errmsg[100];
|
||
long rc;
|
||
|
||
if (plink)
|
||
{
|
||
if (pdest_record)
|
||
{
|
||
if (dest_fieldname)
|
||
{
|
||
if (pi = pop_input_pvar())
|
||
{
|
||
if (dest_recname = dbCaRecnameFromDbCommon(pdest_record))
|
||
{
|
||
/* moving dest name into struct input_pvar */
|
||
strncpy(pi->dest_name, dest_recname, PVNAME_SZ);
|
||
pi->dest_name[PVNAME_SZ] = '\0';
|
||
strcat(pi->dest_name, ".");
|
||
strncat(pi->dest_name, dest_fieldname, FLDNAME_SZ);
|
||
|
||
/* in dbCaLink.c */
|
||
rc = dbCaNameToAddr(pi->dest_name,
|
||
&(pi->pdest_dbaddr));
|
||
|
||
if (RTN_SUCCESS(rc))
|
||
{
|
||
/* new DBF -> old DBR */
|
||
pi->dest_old_dbr_type =
|
||
dbCaNewDbrToOldDbr(dbCaNewDbfToNewDbr(dbCaDbfFromDbAddr(pi->pdest_dbaddr)));
|
||
|
||
if (dbr_type_is_valid(pi->dest_old_dbr_type))
|
||
{
|
||
pi->revised_dest_new_dbr_type =
|
||
dbCaOldDbrToNewDbr(pi->dest_old_dbr_type);
|
||
|
||
pi->dest_old_dbr_element_size =
|
||
(int) dbr_size[pi->dest_old_dbr_type];
|
||
|
||
if ((pi->dest_nelements =
|
||
dbCaNelementsFromDbAddr(pi->pdest_dbaddr))
|
||
!= -1L)
|
||
{
|
||
/* memory allocation */
|
||
if (pi->pvalue =
|
||
(void *) calloc ((unsigned int) pi->dest_nelements,
|
||
(unsigned) pi->dest_old_dbr_element_size))
|
||
{
|
||
/* printf("dbCaAddInlink(): nelements %d * dest old dbr size %d = %d bytes at starting at >%x<\n", (unsigned int) pi->dest_nelements, (unsigned) pi->dest_old_dbr_element_size, (unsigned int) pi->dest_nelements * (unsigned) pi->dest_old_dbr_element_size, pi->pvalue); */
|
||
|
||
/* old DBR -> old DBR_STS */
|
||
pi->dest_old_dbr_sts_type =
|
||
dbCaOldDbrToOldDbrSts(pi->dest_old_dbr_type);
|
||
|
||
/* moving source name */
|
||
/* into struct input_pvar */
|
||
strncpy(pi->source_name,
|
||
plink->value.pv_link.pvname,
|
||
PVNAME_SZ);
|
||
pi->source_name[PVNAME_SZ] = '\0';
|
||
strcat(pi->source_name, ".");
|
||
strncat(pi->source_name,
|
||
plink->value.pv_link.fldname,
|
||
FLDNAME_SZ);
|
||
|
||
/* preserving MS/NMS specification */
|
||
if (plink->value.pv_link.maximize_sevr)
|
||
pi->maximize_severity = TRUE;
|
||
else
|
||
pi->maximize_severity = FALSE;
|
||
|
||
/* pushing this input_pvar */
|
||
/* onto the list */
|
||
|
||
pi->next = Pvar_inputlist_hdr;
|
||
Pvar_inputlist_hdr = pi;
|
||
|
||
/* changing the link type and */
|
||
/* connecting this input_pvar */
|
||
/* the the struct link */
|
||
|
||
plink->type = CA_LINK;
|
||
plink->value.ca_link = (void *) pi;
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_failedmalloc;
|
||
sprintf(errmsg,
|
||
"ERROR: dbCaAddInlink() unable to calloc %d bytes for pvalue",
|
||
pi->dest_nelements * pi->dest_old_dbr_element_size);
|
||
errMessage(S_dbCa_failedmalloc, errmsg);
|
||
|
||
push_input_pvar(pi);
|
||
} /* endif */
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_dbfailure;
|
||
|
||
errMessage(S_dbCa_dbfailure,
|
||
"ERROR: dbCaAddInlink() unable to find nelements");
|
||
|
||
push_input_pvar(pi);
|
||
} /* endif */
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_dbfailure;
|
||
|
||
errMessage(S_dbCa_dbfailure,
|
||
"ERROR: dbCaAddInlink() unable to convert new DBF->old DBR");
|
||
|
||
push_input_pvar(pi);
|
||
} /* endif */
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_dbfailure;
|
||
|
||
sprintf(errmsg,
|
||
"ERROR: dbCaAddInlink() unable to find db addr for >%s<",
|
||
pi->dest_name);
|
||
errMessage(S_dbCa_dbfailure, errmsg);
|
||
|
||
push_input_pvar(pi);
|
||
} /* endif */
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_dbfailure;
|
||
errMessage(S_dbCa_dbfailure,
|
||
"ERROR: dbCaAddInlink() found NULL dest_recname");
|
||
|
||
push_input_pvar(pi);
|
||
} /* endif */
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_failedmalloc;
|
||
errMessage(S_dbCa_failedmalloc,
|
||
"ERROR: dbCaAddInlink() could not pop_input_pvar()");
|
||
} /* endif */
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_nullarg;
|
||
errMessage(S_dbCa_nullarg,
|
||
"ERROR: dbCaAddInlink() got NULL dest_fieldname");
|
||
} /* endif */
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_nullarg;
|
||
errMessage(S_dbCa_nullarg,
|
||
"ERROR: dbCaAddInlink() got NULL pdest_record");
|
||
} /* endif */
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_nullarg;
|
||
errMessage(S_dbCa_nullarg,
|
||
"ERROR: dbCaAddInlink() got NULL plink");
|
||
} /* endif */
|
||
|
||
return rc;
|
||
|
||
} /* end dbCaAddInlink() */
|
||
|
||
/****************************************************************
|
||
*
|
||
* Description:
|
||
* During record initialization a record was found to have an output link
|
||
* whose destination is in a different physical database (on another IOC). This
|
||
* function registers that output link as remote and changes he link type from
|
||
* PV_LINK to CA_LINK.
|
||
*
|
||
* Input:
|
||
* struct link *plink pointer to output link structure
|
||
* char *source_recname name of source record
|
||
* char *source_fieldname name of source field
|
||
* short source_new_dbf_type source DBF type using NEW db technology
|
||
* int source_fld_size size of source field
|
||
*
|
||
* Output: None.
|
||
*
|
||
* Returns:
|
||
* any rc from dbNameToAddr()
|
||
* S_dbCa_nullarg - received a NULL pointer in one of the input args
|
||
* S_dbCa_failedmalloc - could not dynamically allocate memory
|
||
*
|
||
* Notes:
|
||
* In registering this output link, the db address of the source process
|
||
* variable is recorded in a separate list through a function call to
|
||
* dbCaNameToAddr() found in dbCaLink.c.
|
||
*
|
||
****************************************************************/
|
||
|
||
long dbCaAddOutlink(plink, psource_record, source_fieldname)
|
||
struct link *plink;
|
||
void *psource_record;
|
||
char *source_fieldname;
|
||
{
|
||
|
||
struct output_pvar *po;
|
||
int source_old_dbr_element_size;
|
||
char *source_recname;
|
||
char errmsg[100];
|
||
long rc;
|
||
|
||
if (plink)
|
||
{
|
||
if (psource_record)
|
||
{
|
||
if (source_fieldname)
|
||
{
|
||
if (po = pop_output_pvar())
|
||
{
|
||
if (source_recname =
|
||
dbCaRecnameFromDbCommon(psource_record))
|
||
{
|
||
/* moving source name into struct output_pvar */
|
||
|
||
strncpy(po->source_name, source_recname, PVNAME_SZ);
|
||
po->source_name[PVNAME_SZ] = '\0';
|
||
strcat(po->source_name, ".");
|
||
strncat(po->source_name, source_fieldname, FLDNAME_SZ);
|
||
|
||
/* in dbCaDblink.c */
|
||
rc = dbCaNameToAddr(po->source_name,
|
||
&(po->psource_dbaddr));
|
||
|
||
if (RTN_SUCCESS(rc))
|
||
{
|
||
/* new DBF -> old DBR */
|
||
po->source_old_dbr_type =
|
||
dbCaNewDbrToOldDbr(
|
||
dbCaNewDbfToNewDbr(
|
||
dbCaDbfFromDbAddr(po->psource_dbaddr)));
|
||
|
||
if (dbr_type_is_valid(po->source_old_dbr_type))
|
||
{
|
||
po->revised_source_new_dbr_type =
|
||
dbCaOldDbrToNewDbr(po->source_old_dbr_type);
|
||
|
||
source_old_dbr_element_size =
|
||
(int) dbr_size[po->source_old_dbr_type];
|
||
|
||
if ((po->source_nelements =
|
||
dbCaNelementsFromDbAddr(po->psource_dbaddr))
|
||
!= -1L)
|
||
{
|
||
/* memory allocation */
|
||
if (po->pvalue =
|
||
(void *) calloc ((unsigned int) po->source_nelements,
|
||
(unsigned) source_old_dbr_element_size))
|
||
{
|
||
/* moving dest name into */
|
||
/* struct output_pvar */
|
||
|
||
strncpy(po->dest_name,
|
||
plink->value.pv_link.pvname,
|
||
PVNAME_SZ);
|
||
po->dest_name[PVNAME_SZ] = '\0';
|
||
strcat(po->dest_name, ".");
|
||
strncat(po->dest_name,
|
||
plink->value.pv_link.fldname,
|
||
FLDNAME_SZ);
|
||
|
||
/* for dbGetField() */
|
||
po->source_new_dbr_type =
|
||
dbCaNewDbfToNewDbr(
|
||
dbCaDbfFromDbAddr(
|
||
po->psource_dbaddr));
|
||
|
||
po->valid_value = FALSE;
|
||
po->on_writelist = FALSE;
|
||
|
||
/* pushing this output_pvar */
|
||
/* onto the list */
|
||
|
||
po->next = Pvar_outputlist_hdr;
|
||
Pvar_outputlist_hdr = po;
|
||
|
||
/* changing the link type and */
|
||
/* connecting this output_pvar */
|
||
/* the the struct link */
|
||
|
||
plink->type = CA_LINK;
|
||
plink->value.ca_link = (void *) po;
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_failedmalloc;
|
||
|
||
sprintf(errmsg,
|
||
"ERROR: dbCaAddOutlink() could not calloc %d bytes for pvar",
|
||
po->source_nelements*source_old_dbr_element_size);
|
||
errMessage(S_dbCa_failedmalloc, errmsg);
|
||
|
||
push_output_pvar(po);
|
||
|
||
} /* endif */
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_dbfailure;
|
||
|
||
errMessage(S_dbCa_dbfailure,
|
||
"ERROR: dbCaAddOutlink() unable to find nelements");
|
||
|
||
push_output_pvar(po);
|
||
|
||
} /* endif */
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_dbfailure;
|
||
|
||
errMessage(S_dbCa_dbfailure,
|
||
"ERROR: dbCaAddOutlink() unable to convert new DBF->old DBR");
|
||
|
||
push_output_pvar(po);
|
||
|
||
} /* endif */
|
||
}
|
||
else
|
||
{
|
||
push_output_pvar(po);
|
||
} /* endif */
|
||
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_dbfailure;
|
||
errMessage(S_dbCa_dbfailure,
|
||
"ERROR: dbCaAddInlink() found NULL dest_recname");
|
||
|
||
push_output_pvar(po);
|
||
|
||
} /* endif */
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_failedmalloc;
|
||
|
||
errMessage(S_dbCa_failedmalloc,
|
||
"ERROR: dbCaAddOutlink() could not pop_output_pvar()");
|
||
} /* endif */
|
||
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_nullarg;
|
||
errMessage(S_dbCa_nullarg,
|
||
"ERROR: dbCaAddOutlink() NULL source_fieldname");
|
||
} /* endif */
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_nullarg;
|
||
errMessage(S_dbCa_nullarg,
|
||
"ERROR: dbCaAddOutlink() NULL psource_record");
|
||
} /* endif */
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_nullarg;
|
||
errMessage(S_dbCa_nullarg,
|
||
"ERROR: dbCaAddOutlink() got NULL plink");
|
||
} /* endif */
|
||
|
||
return rc;
|
||
|
||
} /* end dbCaAddOutlink() */
|
||
|
||
/****************************************************************
|
||
*
|
||
* long dbCaGetLink(plink)
|
||
* struct link *plink;
|
||
*
|
||
* Description:
|
||
* A record is being processed that has an input link that refers to a
|
||
* process variable on another physical database (another IOC). This function
|
||
* copies the value in the temporary store of the input node into the
|
||
* destination. If MS is specified on this link, the severity of the source
|
||
* record is appropriately propogated to the destination record.
|
||
*
|
||
* Input:
|
||
* struct link *plink pointer to input link structure
|
||
*
|
||
* Output: None.
|
||
*
|
||
* Returns:
|
||
* 0 - Success
|
||
* S_dbCa_nullarg - received a NULL pointer in one of the input args
|
||
* S_dbCa_foundnull - found a NULL pointer where one should not be
|
||
* any rc from dbPut()
|
||
*
|
||
* Notes:
|
||
* Presumably this link was properly registered during record initialization
|
||
* by calling dbCaAddInlink(). If the monitor connection is broken, an alarm
|
||
* condition is raised and no data is transferred. The value is only copied
|
||
* from the temporary store to the destination if its value changed since the
|
||
* last read. In copying the value from the temporary store to the destination,
|
||
* a call is made to dbCaDbPut() in dbCaDbLink.c which ultimately calls dbPut().
|
||
*
|
||
****************************************************************/
|
||
|
||
long dbCaGetLink(plink)
|
||
struct link *plink;
|
||
{
|
||
|
||
struct input_pvar *pi;
|
||
long rc;
|
||
|
||
/* printf("ENTER dbCaGetLink()\n"); */
|
||
if (plink)
|
||
{
|
||
if (pi = (struct input_pvar *) plink->value.ca_link)
|
||
{
|
||
|
||
FASTLOCK(&(pi->lock));
|
||
|
||
if (ca_field_type(pi->cid) == TYPENOTCONN)
|
||
/* channel is disconnected. either */
|
||
/* NEVER connected or LOST connection. */
|
||
|
||
/* in dbCaDblink.c */
|
||
rc = dbCaMaximizeSeverity(pi->pdest_dbaddr,
|
||
(unsigned short) INVALID_ALARM, (unsigned short) LINK_ALARM);
|
||
else
|
||
{
|
||
/* printf("dbCaGetLink() found channel connected and needs_reading %c\n", (pi->needs_reading ? 'T' :'F')); */
|
||
rc = 0L;
|
||
|
||
if (pi->maximize_severity) /* if input link is MS */
|
||
/* in dbCaDblink.c */
|
||
rc = dbCaMaximizeSeverity(pi->pdest_dbaddr,
|
||
pi->source_severity, (unsigned short) LINK_ALARM);
|
||
|
||
if (RTN_SUCCESS(rc) && pi->needs_reading)
|
||
{
|
||
/* printf("dbCaGetLink() calling dbCaDbPut() sourceaddr >%x<\n", pi->pvalue); */
|
||
pi->needs_reading = FALSE;
|
||
|
||
/* in dbCaDbLink.c */
|
||
rc = dbCaDbPut(pi->pdest_dbaddr,
|
||
pi->revised_dest_new_dbr_type, pi->pvalue,
|
||
pi->last_rcvd_nelements);
|
||
} /* endif */
|
||
} /* endif */
|
||
|
||
FASTUNLOCK(&(pi->lock));
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_foundnull;
|
||
errMessage(S_dbCa_foundnull, "ERROR: dbCaGetLink() got NULL pi");
|
||
} /* endif */
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_nullarg;
|
||
errMessage(S_dbCa_nullarg, "ERROR: dbCaGetLink() found NULL plink");
|
||
} /* endif */
|
||
|
||
/* printf("EXIT dbCaGetLink()\n"); */
|
||
return rc;
|
||
|
||
} /* end dbCaGetLink() */
|
||
|
||
/****************************************************************
|
||
*
|
||
* void dbCaLinkInit(count)
|
||
* int count;
|
||
*
|
||
* Description:
|
||
* This function is called twice during IOC initialization. The first time
|
||
* it is called it initializes the all the appropriate memory. The second time
|
||
* it is called it spawns two new vxWorks tasks, one to process remote input
|
||
* links and the other to process remote output links.
|
||
*
|
||
* Input:
|
||
* int count indicates which call this is (count = {1,2})
|
||
*
|
||
* Output: None.
|
||
*
|
||
* Returns: None.
|
||
*
|
||
* Notes:
|
||
* The first call is this function is made at the beginning of IOC
|
||
* initialization and the second call is made at the very end of IOC
|
||
* initialization. During its first invocation, a call is made to
|
||
* dbCaDbInlinkInit() in dbCaDblink.c to initialize that environment as well.
|
||
*
|
||
****************************************************************/
|
||
|
||
void dbCaLinkInit(count)
|
||
int count;
|
||
{
|
||
|
||
if (count == 1)
|
||
init_global_vars();
|
||
|
||
if (count == 2)
|
||
{
|
||
taskSpawn(DB_CA_INPUT_NAME, DB_CA_INPUT_PRI, DB_CA_INPUT_OPT,
|
||
DB_CA_INPUT_STACK, (FUNCPTR) dbCaProcessInlinks,
|
||
0,0,0,0,0,0,0,0,0,0);
|
||
|
||
taskSpawn(DB_CA_OUTPUT_NAME, DB_CA_OUTPUT_PRI, DB_CA_OUTPUT_OPT,
|
||
DB_CA_OUTPUT_STACK, (FUNCPTR) dbCaProcessOutlinks,
|
||
0,0,0,0,0,0,0,0,0,0);
|
||
} /* endif */
|
||
|
||
} /* end dbCaLinkInit() */
|
||
|
||
/****************************************************************
|
||
*
|
||
* void dbCaProcessInlinks()
|
||
*
|
||
* Description:
|
||
* This function is spawned during IOC initialization (initRecSup()) AFTER
|
||
* processing all record initialization routines. It process input links that
|
||
* originate on remote process variables.
|
||
*
|
||
* This function establishes monitors on the remote source process
|
||
* variables and registers the my_event_handler() and the event handler routine.
|
||
* It then waits forever for events to occur.
|
||
*
|
||
* Input: None.
|
||
*
|
||
* Output: None.
|
||
*
|
||
* Returns: None.
|
||
*
|
||
* Notes:
|
||
* If the database on this IOC does NOT have any input links that refer to
|
||
* remote pvars, this function ends immediately and the spawned vxWorks task
|
||
* dies. Either this function terminates immediately or it never terminates.
|
||
*
|
||
****************************************************************/
|
||
|
||
void dbCaProcessInlinks()
|
||
{
|
||
|
||
struct input_pvar *pi;
|
||
long status;
|
||
|
||
if (Pvar_inputlist_hdr)
|
||
{
|
||
/* have at least one remote db input link */
|
||
|
||
status = task_initialize_channel_access();
|
||
|
||
/* queueing ca_search()'s */
|
||
|
||
for (pi = Pvar_inputlist_hdr;
|
||
RTN_SUCCESS(status) && pi;
|
||
pi = pi->next)
|
||
status = queue_ca_search(pi->source_name, &(pi->cid));
|
||
|
||
if (RTN_SUCCESS(status))
|
||
{
|
||
status = flush_channel_request_buffer();
|
||
|
||
for (pi = Pvar_inputlist_hdr;
|
||
RTN_SUCCESS(status) && pi;
|
||
pi = pi->next)
|
||
status = queue_add_event_from_input_pvar(pi);
|
||
|
||
if (RTN_SUCCESS(status))
|
||
if (RTN_SUCCESS(flush_channel_request_buffer()))
|
||
process_asynch_events((float) 0);
|
||
} /* endif */
|
||
} /* endif */
|
||
|
||
} /* end dbCaProcessInlinks() */
|
||
|
||
/****************************************************************
|
||
*
|
||
* void dbCaProcessOutlinks()
|
||
*
|
||
* Description:
|
||
* This function is spawned during IOC initialization (initRecSup()) AFTER
|
||
* processing all record initialization routines. It process output links that
|
||
* refer to remote process variables. This function is the output demon.
|
||
*
|
||
* This function establishes the connections to the remote destination
|
||
* process variables and registers my_connection_handler() as the connection
|
||
* hanlder routine for each connection. It then waits to be signaled. Once
|
||
* signaled, it writes all the values in Write List returns the nodes in the
|
||
* Write List back to the Output List.
|
||
*
|
||
* Input: None.
|
||
*
|
||
* Output: None.
|
||
*
|
||
* Returns: None.
|
||
*
|
||
* Notes:
|
||
* If the database on this IOC does NOT have any output links that refer
|
||
* to remote pvars, this function ends immediately and the spawned vxWorks task
|
||
* dies. Either this function terminates immediately or it never termintates.
|
||
*
|
||
* This function is signaled during record processing to inspect the nodes
|
||
* on the Write List. There is no guarantee that the connections will be in
|
||
* tact. Nodes whose connections are broken are placed onto the Disconnected
|
||
* List (temporarily) and returned to the Write List (not the Output List)
|
||
* at the end. When connections are re-established, the connection handler
|
||
* my_connection_handler() signals this function to inspect the Write List
|
||
* again.
|
||
*
|
||
****************************************************************/
|
||
|
||
void dbCaProcessOutlinks()
|
||
{
|
||
|
||
struct output_pvar *po;
|
||
long status;
|
||
BOOL done;
|
||
|
||
|
||
if (Pvar_outputlist_hdr)
|
||
{
|
||
/* have at least one remote db output link */
|
||
|
||
status = task_initialize_channel_access();
|
||
|
||
taskSpawn(DB_CA_PROC_ASYNCH_EV_TASK_NAME, DB_CA_PROC_ASYNCH_EV_TASK_PRI,
|
||
DB_CA_PROC_ASYNCH_EV_TASK_OPT, DB_CA_PROC_ASYNCH_EV_TASK_STACK,
|
||
(FUNCPTR) process_asynch_events_task, taskIdSelf(),
|
||
0,0,0,0,0,0,0,0,0);
|
||
|
||
/* queueing ca_build_and_connect()'s */
|
||
|
||
for (po = Pvar_outputlist_hdr;
|
||
RTN_SUCCESS(status)&& po;
|
||
po = po->next)
|
||
status = queue_ca_build_and_connect(po);
|
||
|
||
if (RTN_SUCCESS(status))
|
||
status = flush_channel_request_buffer();
|
||
|
||
/* printf("\n-- ENTERING OUTLINK MAIN LOOP\n\n"); */
|
||
|
||
while (RTN_SUCCESS(status))
|
||
{
|
||
|
||
/* P(LookAtBuffer) */
|
||
/* FASTLOCK(&LookAtBuffer); */
|
||
semTake(LookAtBuffer, WAIT_FOREVER);
|
||
/* printf("Output demon just woke up\n"); */
|
||
|
||
/* grabbing ALL the nodes in the writelist list ... one at a time */
|
||
|
||
done = FALSE;
|
||
|
||
while (!done && RTN_SUCCESS(status))
|
||
{
|
||
FASTLOCK(&Buffer);
|
||
if (po = Pvar_writelist_hdr)
|
||
Pvar_writelist_hdr = po->write_next;
|
||
FASTUNLOCK(&Buffer);
|
||
|
||
if (po)
|
||
{
|
||
FASTLOCK(&(po->lock));
|
||
|
||
if (po->valid_value)
|
||
{
|
||
/* we have a valid value here ... needs to be written */
|
||
if (ca_field_type(po->cid) == TYPENOTCONN)
|
||
{
|
||
/* raising an alarm disconnected */
|
||
status = dbCaMaximizeSeverity(po->psource_dbaddr,
|
||
(unsigned short) INVALID_ALARM,
|
||
(unsigned short) LINK_ALARM);
|
||
|
||
/* pushing this node onto the disconnected list */
|
||
po->write_next = Pvar_disconnectedlist_hdr;
|
||
Pvar_disconnectedlist_hdr = po;
|
||
}
|
||
else
|
||
{
|
||
po->on_writelist = FALSE;
|
||
/* printf("Output demon just before ca_array_put()\n"); */
|
||
status = queue_ca_array_put(po);
|
||
if (RTN_SUCCESS(status))
|
||
status = flush_channel_request_buffer();
|
||
} /* endif */
|
||
}
|
||
else
|
||
/* the value here is invalid ... */
|
||
/* simply remove node from writelist */
|
||
po->on_writelist = FALSE;
|
||
|
||
FASTUNLOCK(&(po->lock));
|
||
}
|
||
else
|
||
done = TRUE;
|
||
} /* endwhile */
|
||
|
||
/* moving the nodes on the disconnected */
|
||
/* list back onto the writelist */
|
||
if (RTN_SUCCESS(status) && Pvar_disconnectedlist_hdr)
|
||
{
|
||
|
||
/* there are nodes on the disconnected list */
|
||
|
||
FASTLOCK(&Buffer);
|
||
|
||
if (Pvar_writelist_hdr)
|
||
{
|
||
/* moving nodes one at a time */
|
||
while (Pvar_disconnectedlist_hdr)
|
||
{
|
||
po = Pvar_disconnectedlist_hdr;
|
||
Pvar_disconnectedlist_hdr = po->write_next;
|
||
|
||
po->write_next = Pvar_writelist_hdr;
|
||
Pvar_writelist_hdr = po;
|
||
} /* endwhile */
|
||
}
|
||
else
|
||
{
|
||
/* moving the whole list at once */
|
||
Pvar_writelist_hdr = Pvar_disconnectedlist_hdr;
|
||
Pvar_disconnectedlist_hdr = (struct output_pvar *) NULL;
|
||
} /* endif */
|
||
|
||
FASTUNLOCK(&Buffer);
|
||
|
||
} /* endif */
|
||
} /* endwhile */
|
||
} /* endif */
|
||
|
||
/* printf("dbCaProcessOutlinks() FOUND NO REMOTE OUTPUT PVARS\n"); */
|
||
|
||
} /* end dbCaProcessOutlinks() */
|
||
|
||
/****************************************************************
|
||
*
|
||
* long dbCaPutLink(plink, poptions, pnrequest)
|
||
* struct link *plink;
|
||
* long *poptions;
|
||
* long *pnrequest;
|
||
*
|
||
* Description:
|
||
* A record is being processed that has an output link that refers to a
|
||
* process variable on another physical database (another IOC). This function
|
||
* copies the value from the source pvar into the temporary store of the output
|
||
* node. It then places the output node onto the Write List and signals the
|
||
* output demon (dbCaProcessOutlinks()).
|
||
*
|
||
* Input:
|
||
* struct link *plink pointer to output link structure
|
||
* long *poptions as passed to dbGetField()
|
||
* long *pnrequest as passed to dbGetField()
|
||
*
|
||
* Output: None.
|
||
*
|
||
* Returns:
|
||
* 0 - Success or raised alarm
|
||
* S_dbCa_nullarg - received a NULL pointer in one of the input args
|
||
* S_dbCa_foundnull - found a NULL pointer where one should not be
|
||
* any rc from dbGetField()
|
||
*
|
||
* Notes:
|
||
* Presumably this link was properly registered during record initialization
|
||
* by calling dbCaAddOutlink(). In copying the value from the source pvar to
|
||
* the temporary store, a call is made to dbCaCopyPvar() in dbCaDblink.c which
|
||
* ultimately makes a call to dbGetField(), hence the need for the two arguments
|
||
* poptions and pnrequest.
|
||
*
|
||
* The connection handler (my_connection_handler()) is called whenever
|
||
* the connection status of the channel changes. If the connection status
|
||
* goes from disconnected->connected, the connection hanlder checks if the
|
||
* value has ever been written to the remote pvar. If it has, it immediately
|
||
* schedules the nodes value to be written again, even though the record was
|
||
* not processed, and signals the output demon. Hence the need to record the
|
||
* fact that the value was ever written.
|
||
*
|
||
* As this function is called during record processing, it cannot wait for
|
||
* anything for an extended period of time. Before copying the value to the
|
||
* temporary store, it must lock the node on the Output List. This function
|
||
* is contending for the lock with the output demon and the connection handler.
|
||
* The output demon locks the node and then issues a ca_put() ... that could
|
||
* take too long. Therefore, a newly locking mechanism is employed here,
|
||
* FASTLOCKNOWAIT. It attempts to grab the LOCK. If it succeeds it returns
|
||
* TRUE. If it fails it returns FALSE. In either case, it returns immediately.
|
||
*
|
||
****************************************************************/
|
||
|
||
long dbCaPutLink(plink, poptions, pnrequest)
|
||
struct link *plink;
|
||
long *poptions;
|
||
long *pnrequest;
|
||
{
|
||
|
||
struct output_pvar *po;
|
||
char errmsg[100];
|
||
long rc;
|
||
|
||
if (plink)
|
||
{
|
||
if (poptions)
|
||
{
|
||
if (pnrequest)
|
||
{
|
||
if (po = (struct output_pvar *) plink->value.ca_link)
|
||
{
|
||
if (po->source_nelements <= *pnrequest)
|
||
{
|
||
/* NOTE: there are three unlocks associated with */
|
||
/* this lock. only ONE unlock will be executed */
|
||
/* there are three of them for an optimization */
|
||
/* reason. We want to assure that the node is */
|
||
/* unlocked BEFORE V(LookAtBuffer), otherwise the */
|
||
/* other task will wake up and HAVE to switch back */
|
||
/* to this task in order to unlock the node and then */
|
||
/* switch back again to resume processing. */
|
||
|
||
if (FASTLOCKNOWAIT(&(po->lock)))
|
||
{
|
||
/* copying pvar value to output_pvar struct */
|
||
/* in dbCaDblink.c */
|
||
/* printf("dbCaPutLink() got LOCK and calling dbCaCopyPvar()\n"); */
|
||
rc = dbCaCopyPvar(po->psource_dbaddr,
|
||
po->revised_source_new_dbr_type,
|
||
po->pvalue, poptions, pnrequest);
|
||
|
||
if (RTN_SUCCESS(rc))
|
||
{
|
||
/* successful copy */
|
||
|
||
/* checking if just overwrote last value that */
|
||
/* was never sent and raising an alarm if */
|
||
/* necessary. */
|
||
if (po->valid_value && po->on_writelist)
|
||
/* in dbCaDblink.c */
|
||
rc = dbCaMaximizeSeverity(po->psource_dbaddr,
|
||
(unsigned short) INVALID_ALARM,
|
||
(unsigned short) LINK_ALARM);
|
||
|
||
po->valid_value = TRUE;
|
||
po->last_nelements_written = *pnrequest;
|
||
|
||
if (!(po->on_writelist))
|
||
{
|
||
/* not on writelist ... placing there */
|
||
po->on_writelist = TRUE;
|
||
|
||
/* placing node onto write list */
|
||
FASTLOCK(&Buffer);
|
||
po->write_next = Pvar_writelist_hdr;
|
||
Pvar_writelist_hdr = po;
|
||
FASTUNLOCK(&Buffer);
|
||
|
||
/* V(LookAtBuffer) awakens */
|
||
/* ProcessOutlinks() */
|
||
|
||
/* printf("dbCaPutLink() V(LookAtBuffer)\n"); */
|
||
/* FASTUNLOCK(&LookAtBuffer); */
|
||
semGive(LookAtBuffer);
|
||
} /* endif */
|
||
}
|
||
else
|
||
{
|
||
/* failed copy */
|
||
po->valid_value = FALSE;
|
||
|
||
/* raising an alarm to reflect failed copy */
|
||
/* have (arbitrarily) decided to change the */
|
||
/* rc in doing so ... could just as easily */
|
||
/* not have changed the rc and returned what */
|
||
/* was returned from dbCaCopyPvar() instead */
|
||
/* in dbCaDblink.c */
|
||
rc = dbCaMaximizeSeverity(po->psource_dbaddr,
|
||
(unsigned short) INVALID_ALARM,
|
||
(unsigned short) LINK_ALARM);
|
||
} /* endif */
|
||
|
||
FASTUNLOCK(&(po->lock));
|
||
}
|
||
else
|
||
{
|
||
/* printf("dbCaPutLink() could not get LOCK\n"); */
|
||
/* could not get lock immediately */
|
||
/* aborting write and raising an alarm */
|
||
|
||
/* in dbCaDblink.c */
|
||
rc = dbCaMaximizeSeverity(po->psource_dbaddr,
|
||
(unsigned short) INVALID_ALARM,
|
||
(unsigned short) LINK_ALARM);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_dbfailure;
|
||
sprintf(errmsg,
|
||
"ERROR: dbCaPutLink() requested %ld max %ld",
|
||
*pnrequest, po->source_nelements);
|
||
errMessage(S_dbCa_dbfailure, errmsg);
|
||
} /* endif */
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_foundnull;
|
||
errMessage(S_dbCa_foundnull,
|
||
"ERROR: dbCaPutLink() found NULL po");
|
||
} /* endif */
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_nullarg;
|
||
errMessage(S_dbCa_nullarg,
|
||
"ERROR: dbCaPutLink() got NULL pnrequest");
|
||
} /* endif */
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_nullarg;
|
||
errMessage(S_dbCa_nullarg,
|
||
"ERROR: dbCaPutLink() got NULL poptions");
|
||
} /* endif */
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_nullarg;
|
||
errMessage(S_dbCa_nullarg,
|
||
"ERROR: dbCaPutLink() got NULL plink");
|
||
} /* endif */
|
||
|
||
return rc;
|
||
|
||
} /* end dbCaPutLink() */
|
||
|
||
/****************************************************************
|
||
*
|
||
* PRIVATE FUNCTIONS
|
||
*
|
||
****************************************************************/
|
||
|
||
/****************************************************************
|
||
*
|
||
* static long flush_channel_request_buffer()
|
||
*
|
||
* Description:
|
||
* Flushes the channel access buffer by calling ca_flush_io().
|
||
*
|
||
* Input: None.
|
||
*
|
||
* Output: None.
|
||
*
|
||
* Returns:
|
||
* 0 - Success
|
||
* S_dbCa_unknownECA - unknown status rc from ca_flush_io()
|
||
*
|
||
* Notes: None.
|
||
*
|
||
****************************************************************/
|
||
|
||
static long flush_channel_request_buffer()
|
||
{
|
||
|
||
int status;
|
||
char errmsg[100];
|
||
long rc;
|
||
|
||
status = ca_flush_io();
|
||
|
||
switch (status)
|
||
{
|
||
case ECA_NORMAL:
|
||
rc = 0L;
|
||
break;
|
||
default:
|
||
rc = S_dbCa_unknownECA;
|
||
sprintf(errmsg,
|
||
"ERROR: flush_channel_request_buffer() unrecognizable status returned %d",
|
||
status);
|
||
errMessage(S_dbCa_unknownECA, errmsg);
|
||
break;
|
||
} /* end switch() */
|
||
|
||
return rc;
|
||
|
||
} /* end flush_channel_request_buffer() */
|
||
|
||
/****************************************************************
|
||
*
|
||
* static void init_global_vars()
|
||
*
|
||
* Description:
|
||
* Initializes the linked list headers to NULL. Initializes the
|
||
* necessary binary semaphores appropriately.
|
||
*
|
||
* Input: None.
|
||
*
|
||
* Output: None.
|
||
*
|
||
* Returns: None.
|
||
*
|
||
* Notes: None.
|
||
*
|
||
****************************************************************/
|
||
|
||
static void init_global_vars()
|
||
{
|
||
|
||
/* Avail Headers */
|
||
Input_pvar_avail_hdr = (struct input_pvar *) NULL;
|
||
Output_pvar_avail_hdr = (struct output_pvar *) NULL;
|
||
|
||
/* Input List and Output List */
|
||
Pvar_inputlist_hdr = (struct input_pvar *) NULL;
|
||
Pvar_outputlist_hdr = (struct output_pvar *) NULL;
|
||
|
||
/* Write List and Disconnected List */
|
||
Pvar_writelist_hdr = (struct output_pvar *) NULL;
|
||
Pvar_disconnectedlist_hdr = (struct output_pvar *) NULL;
|
||
|
||
/* Buffer LOCK */
|
||
FASTLOCKINIT(&Buffer);
|
||
|
||
/* Binary Semaphore: initial value = 0 */
|
||
/* FASTLOCKINIT(&LookAtBuffer); */
|
||
LookAtBuffer = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY);
|
||
/* FASTLOCK(&LookAtBuffer); */
|
||
/* semTake(LookAtBuffer, WAIT_FOREVER); */
|
||
|
||
} /* end init_global_vars() */
|
||
|
||
/****************************************************************
|
||
*
|
||
* static void my_connection_handler(arg)
|
||
* struct connection_handler_args arg;
|
||
*
|
||
* Description:
|
||
* The connection status of one of the output channels has changed. If
|
||
* the connection has gone from disconnected->connected, this routine re-writes
|
||
* the value to the destiantion pvar.
|
||
*
|
||
* Input:
|
||
* struct connection_handler_args arg
|
||
*
|
||
* from epicsH/cadef.h
|
||
* struct connection_handler_args
|
||
* {
|
||
* chid chid; Channel id
|
||
* long op; External codes for channel access operations
|
||
* };
|
||
*
|
||
* Output: None.
|
||
*
|
||
* Returns: None.
|
||
*
|
||
* Notes:
|
||
* No matter where the node started when this function started, if the
|
||
* connection is now in tact and the node's value was ever writen, the node
|
||
* ends up on the Write List and the output demon is signaled.
|
||
*
|
||
****************************************************************/
|
||
|
||
static void my_connection_handler(arg)
|
||
struct connection_handler_args arg;
|
||
{
|
||
|
||
struct output_pvar *po;
|
||
|
||
/* printf("my_connection_handler() change in connection connection\n"); */
|
||
if (arg.op == CA_OP_CONN_UP)
|
||
{
|
||
/* printf("my_connection_handler() detected DOWN--->UP\n"); */
|
||
/* connection on this channel re-established */
|
||
|
||
if (po = (struct output_pvar *) ca_puser(arg.chid))
|
||
{
|
||
|
||
FASTLOCK(&(po->lock));
|
||
|
||
if (po->valid_value)
|
||
{
|
||
/* printf("my_connection_handler() value needs to be written\n"); */
|
||
/* This value has been ca_put()'d in the past. */
|
||
/* Need to do it now immediately to more closely */
|
||
/* simulate database activities when all records */
|
||
/* reside on a single IOC ... the source record */
|
||
/* (and hence the whole database) assumes that */
|
||
/* the target record has the last value written. */
|
||
/* This helps to make that assumption reliable. */
|
||
|
||
if (!(po->on_writelist))
|
||
{
|
||
/* printf("my_connection_handler() placing value onto writelist\n"); */
|
||
po->on_writelist = TRUE;
|
||
|
||
/* placing node onto write list */
|
||
FASTLOCK(&Buffer);
|
||
po->write_next = Pvar_writelist_hdr;
|
||
Pvar_writelist_hdr = po;
|
||
FASTUNLOCK(&Buffer);
|
||
} /* endif */
|
||
|
||
/* optimization to place unlock here ... before */
|
||
/* V() ... but need it after *else* also */
|
||
|
||
FASTUNLOCK(&(po->lock));
|
||
|
||
/* printf("my_connection_handler() signalling V() output demon\n"); */
|
||
/* V(LookAtBuffer) to awaken ProcessOutlinks() */
|
||
/* FASTUNLOCK(&LookAtBuffer); */
|
||
semGive(LookAtBuffer);
|
||
}
|
||
else
|
||
FASTUNLOCK(&(po->lock));
|
||
}
|
||
else
|
||
errMessage(S_dbCa_foundnull,
|
||
"ERROR: my_connection_handler() found NULL po");
|
||
} /* endif */
|
||
|
||
} /* end my_connection_handler() */
|
||
|
||
/****************************************************************
|
||
*
|
||
* static void my_event_handler(arg)
|
||
* struct event_handler_args arg;
|
||
*
|
||
* Description:
|
||
* This function is the user-supplied event handler routine. It gets
|
||
* called whenever the an event occurs associated with one the remote source
|
||
* pvars. It copies the value from the incoming event_handler_args to the
|
||
* temporary store of the input node.
|
||
*
|
||
* Input:
|
||
* struct event_handler_args arg
|
||
* from epicsH/cadef.h
|
||
* struct event_handler_args
|
||
* {
|
||
* void *usr; User argument supplied when event added
|
||
* chid chid; Channel id
|
||
* long type; the type of the value returned (long aligned)
|
||
* long count; the element count of the item returned
|
||
* void *dbr; Pointer to the value returned
|
||
* };
|
||
*
|
||
* Output: None.
|
||
*
|
||
* Returns: None.
|
||
*
|
||
* Notes:
|
||
* Presumably a monitor has been established on these remote source
|
||
* pvars and a wait for events has already been issued by dbCaProcessInlinks().
|
||
*
|
||
* After the value is copied to the temporary store, it is later read
|
||
* into the destination pvar during record processing via a call to
|
||
* dbCaGetLink(). The indication that this node's value has been changed is
|
||
* an optimization to avoid moving redundant data during record processing.
|
||
*
|
||
* arg.type is coming in as an old database DBR_STS_XXX type. We cannot
|
||
* convert it to new database DBR_XXX type here because this file only
|
||
* understands old database types as a result of including db_access.h
|
||
*
|
||
* Because the arg.type is DBR_STS_XXX type, arg.dbr is a pointer to a data
|
||
* structure that has short status, short severity, and xxxxx value. Here are
|
||
* the names of the different data structures based on the DBR_STS_XXX type as
|
||
* copied from db_access.h
|
||
*
|
||
* DBR_STS_STRING returns a string status structure (dbr_sts_string)
|
||
* DBR_STS_SHORT returns a short status structure (dbr_sts_short)
|
||
* DBR_STS_INT returns a short status structure (dbr_sts_int)
|
||
* DBR_STS_FLOAT returns a float status structure (dbr_sts_float)
|
||
* DBR_STS_ENUM returns an enum status structure (dbr_sts_enum)
|
||
* DBR_STS_CHAR returns a char status structure (dbr_sts_char)
|
||
* DBR_STS_LONG returns a long status structure (dbr_sts_long)
|
||
* DBR_STS_DOUBLE returns a double status structure (dbr_sts_double)
|
||
*
|
||
* Currently, DBR_STS_INT and DBR_STS_SHORT are defined to be the same
|
||
* thing, therefore they both cannot reside as distinct cases in the switch.
|
||
*
|
||
****************************************************************/
|
||
|
||
static void my_event_handler(arg)
|
||
struct event_handler_args arg;
|
||
{
|
||
|
||
struct input_pvar *pi;
|
||
char errmsg[100];
|
||
|
||
/* for debugging */
|
||
/*
|
||
char buff[100];
|
||
char sevr_buff[20];
|
||
*/
|
||
char print_line[500];
|
||
|
||
if (pi = (struct input_pvar *) arg.usr)
|
||
{
|
||
if (pi->pvalue)
|
||
{
|
||
/* printf("my_event_handler(): GOT dbr type %d nelements %ld EXP dbr type %d max %ld destaddr >%x<\n", arg.type, arg.count, pi->dest_old_dbr_sts_type, pi->dest_nelements, pi->pvalue); */
|
||
/* checking that channel access gave us what we asked for */
|
||
if (arg.type == pi->dest_old_dbr_sts_type)
|
||
{
|
||
/* checking that we did not receive */
|
||
/* more than we allocated for */
|
||
if (arg.count <= pi->dest_nelements)
|
||
{
|
||
/* sprintf(print_line, "my_event_handler(): spvar >%s< --> dpvar >%s< ", pi->source_name, pi->dest_name); */
|
||
FASTLOCK(&(pi->lock));
|
||
|
||
switch (arg.type)
|
||
{
|
||
case DBR_STS_STRING:
|
||
pi->needs_reading = TRUE;
|
||
pi->last_rcvd_nelements = arg.count;
|
||
|
||
/* bcopy( */
|
||
/* (char *) ((struct dbr_sts_string *) arg.dbr)->value, */
|
||
/* (char *) pi->pvalue, */
|
||
/* (arg.count * pi->dest_old_dbr_element_size)); */
|
||
memcpy(
|
||
(char *) pi->pvalue,
|
||
(char *) ((struct dbr_sts_string *) arg.dbr)->value,
|
||
(arg.count * pi->dest_old_dbr_element_size));
|
||
pi->source_severity =
|
||
(unsigned short) ((struct dbr_sts_string *) arg.dbr)->severity;
|
||
|
||
/* strcat(print_line, "DBR_STS_STRING "); */
|
||
/* sprintf(buff, ">%s< ", */
|
||
/* (char *) ((struct dbr_sts_string *) arg.dbr)->value); */
|
||
/* strcat(print_line, buff); */
|
||
break;
|
||
/* case DBR_STS_INT: */
|
||
case DBR_STS_SHORT:
|
||
pi->needs_reading = TRUE;
|
||
pi->last_rcvd_nelements = arg.count;
|
||
|
||
/* bcopy( */
|
||
/* (char *) &(((struct dbr_sts_short *) arg.dbr)->value), */
|
||
/* (char *) pi->pvalue, */
|
||
/* (arg.count * pi->dest_old_dbr_element_size)); */
|
||
memcpy(
|
||
(char *) pi->pvalue,
|
||
(char *) &(((struct dbr_sts_short *) arg.dbr)->value),
|
||
(arg.count * pi->dest_old_dbr_element_size));
|
||
pi->source_severity =
|
||
(unsigned short) ((struct dbr_sts_short *) arg.dbr)->severity;
|
||
|
||
/* strcat(print_line, "DBR_STS_SHORT "); */
|
||
/* sprintf(buff, ">%d< ", */
|
||
/* (short) ((struct dbr_sts_short *) arg.dbr)->value); */
|
||
/* strcat(print_line, buff); */
|
||
break;
|
||
case DBR_STS_FLOAT:
|
||
pi->needs_reading = TRUE;
|
||
pi->last_rcvd_nelements = arg.count;
|
||
|
||
/* bcopy( */
|
||
/* (char *) &(((struct dbr_sts_float *) arg.dbr)->value), */
|
||
/* (char *) pi->pvalue, */
|
||
/* (arg.count * pi->dest_old_dbr_element_size)); */
|
||
memcpy(
|
||
(char *) pi->pvalue,
|
||
(char *) &(((struct dbr_sts_float *) arg.dbr)->value),
|
||
(arg.count * pi->dest_old_dbr_element_size));
|
||
pi->source_severity =
|
||
(unsigned short) ((struct dbr_sts_float *) arg.dbr)->severity;
|
||
|
||
/* strcat(print_line, "DBR_STS_FLOAT "); */
|
||
/* sprintf(buff, ">%f< ", */
|
||
/* (float) ((struct dbr_sts_float *) arg.dbr)->value); */
|
||
/* strcat(print_line, buff); */
|
||
break;
|
||
case DBR_STS_ENUM:
|
||
pi->needs_reading = TRUE;
|
||
pi->last_rcvd_nelements = arg.count;
|
||
|
||
/* bcopy( */
|
||
/* (char *) &(((struct dbr_sts_enum *) arg.dbr)->value), */
|
||
/* (char *) pi->pvalue, */
|
||
/* (arg.count * pi->dest_old_dbr_element_size)); */
|
||
memcpy(
|
||
(char *) pi->pvalue,
|
||
(char *) &(((struct dbr_sts_enum *) arg.dbr)->value),
|
||
(arg.count * pi->dest_old_dbr_element_size));
|
||
pi->source_severity =
|
||
(unsigned short) ((struct dbr_sts_enum *) arg.dbr)->severity;
|
||
|
||
/* strcat(print_line, "DBR_STS_SHORT "); */
|
||
/* sprintf(buff, ">%d< ", */
|
||
/* (short) ((struct dbr_sts_enum *) arg.dbr)->value); */
|
||
/* strcat(print_line, buff); */
|
||
break;
|
||
case DBR_STS_CHAR:
|
||
pi->needs_reading = TRUE;
|
||
pi->last_rcvd_nelements = arg.count;
|
||
|
||
/* bcopy( */
|
||
/* (char *) &(((struct dbr_sts_char *) arg.dbr)->value), */
|
||
/* (char *) pi->pvalue, */
|
||
/* (arg.count * pi->dest_old_dbr_element_size)); */
|
||
memcpy(
|
||
(char *) pi->pvalue,
|
||
(char *) &(((struct dbr_sts_char *) arg.dbr)->value),
|
||
(arg.count * pi->dest_old_dbr_element_size));
|
||
pi->source_severity =
|
||
(unsigned short) ((struct dbr_sts_char *) arg.dbr)->severity;
|
||
|
||
/* strcat(print_line, "DBR_STS_CHAR "); */
|
||
/* sprintf(buff, ">%c< ", */
|
||
/* (unsigned char) ((struct dbr_sts_char *) arg.dbr)->value); */
|
||
break;
|
||
case DBR_STS_LONG:
|
||
pi->needs_reading = TRUE;
|
||
pi->last_rcvd_nelements = arg.count;
|
||
|
||
/* bcopy( */
|
||
/* (char *) &(((struct dbr_sts_long *) arg.dbr)->value), */
|
||
/* (char *) pi->pvalue, */
|
||
/* (arg.count * pi->dest_old_dbr_element_size)); */
|
||
memcpy(
|
||
(char *) pi->pvalue,
|
||
(char *) &(((struct dbr_sts_long *) arg.dbr)->value),
|
||
(arg.count * pi->dest_old_dbr_element_size));
|
||
pi->source_severity =
|
||
(unsigned short) ((struct dbr_sts_long *) arg.dbr)->severity;
|
||
|
||
/* strcat(print_line, "DBR_STS_LONG "); */
|
||
/* sprintf(buff, ">%ld< ", */
|
||
/* (long) ((struct dbr_sts_long *) arg.dbr)->value); */
|
||
break;
|
||
case DBR_STS_DOUBLE:
|
||
pi->needs_reading = TRUE;
|
||
pi->last_rcvd_nelements = arg.count;
|
||
|
||
/* bcopy( */
|
||
/* (char *) &(((struct dbr_sts_double *) arg.dbr)->value), */
|
||
/* (char *) pi->pvalue, */
|
||
/* (arg.count * pi->dest_old_dbr_element_size)); */
|
||
memcpy(
|
||
(char *) pi->pvalue,
|
||
(char *) &(((struct dbr_sts_double *) arg.dbr)->value),
|
||
(arg.count * pi->dest_old_dbr_element_size));
|
||
pi->source_severity =
|
||
(unsigned short) ((struct dbr_sts_double *) arg.dbr)->severity;
|
||
|
||
/* strcat(print_line, "DBR_STS_DOUBLE "); */
|
||
/* sprintf(buff, ">%f< ", */
|
||
/* (double) ((struct dbr_sts_double *) arg.dbr)->value); */
|
||
/* strcat(print_line, buff); */
|
||
break;
|
||
default:
|
||
strcat(print_line, "UNKNOWN ");
|
||
strcat(print_line, "**** ");
|
||
break;
|
||
} /* end switch() */
|
||
|
||
FASTUNLOCK(&(pi->lock));
|
||
|
||
}
|
||
else
|
||
{
|
||
sprintf(errmsg,
|
||
"ERROR: my_event_handler() ca delivered %ld elements while maximum is %ld",
|
||
arg.count, pi->dest_nelements);
|
||
|
||
errMessage(S_dbCa_cafailure, errmsg);
|
||
} /* endif */
|
||
}
|
||
else
|
||
{
|
||
sprintf(errmsg,
|
||
"ERROR: my_event_handler() ca delivered DBR %d while %d was expected",
|
||
arg.type, pi->dest_old_dbr_sts_type);
|
||
|
||
errMessage(S_dbCa_cafailure, errmsg);
|
||
} /* endif */
|
||
|
||
}
|
||
else
|
||
errMessage(S_dbCa_foundnull,
|
||
"ERROR: my_event_handler() found NULL pi->pvalue");
|
||
}
|
||
else
|
||
errMessage(S_dbCa_foundnull, "ERROR: my_event_handler() found NULL pi");
|
||
|
||
} /* end my_event_handler() */
|
||
|
||
/****************************************************************
|
||
*
|
||
* static struct input_pvar *pop_input_pvar()
|
||
*
|
||
* Description:
|
||
* Pops a fresh input pvar node off the avail list.
|
||
*
|
||
* Input: None.
|
||
*
|
||
* Output: None.
|
||
*
|
||
* Returns:
|
||
* address of fresh node
|
||
* NULL if unable to allocate a node
|
||
*
|
||
* Notes:
|
||
* The LOCKs associated with the nodes are only initialized immediately
|
||
* after acquiring the memory for the node while the rest of the fields in
|
||
* the structure are initialized every time the node is popped off the list.
|
||
*
|
||
* It is also assumed that -1 is an invalid dest_old_dbr_sts_type as well
|
||
* as an invalid record severity.
|
||
*
|
||
****************************************************************/
|
||
|
||
static struct input_pvar *pop_input_pvar()
|
||
{
|
||
|
||
struct input_pvar *rc;
|
||
int i;
|
||
|
||
if (Input_pvar_avail_hdr != (struct input_pvar *) NULL)
|
||
{
|
||
rc = Input_pvar_avail_hdr;
|
||
Input_pvar_avail_hdr = rc->next;
|
||
}
|
||
else
|
||
{
|
||
if ((Input_pvar_avail_hdr
|
||
= (struct input_pvar *) calloc
|
||
((unsigned) NODESPERCALLOC,
|
||
(unsigned) (sizeof (struct input_pvar))))
|
||
!= (struct input_pvar *) NULL)
|
||
{
|
||
for (rc = Input_pvar_avail_hdr, i=0; i < (NODESPERCALLOC-1); i ++)
|
||
{
|
||
FASTLOCKINIT(&(rc->lock));
|
||
rc->next = rc + 1;
|
||
rc++;
|
||
} /* endfor */
|
||
FASTLOCKINIT(&(rc->lock));
|
||
rc->next = (struct input_pvar *) NULL;
|
||
|
||
rc = Input_pvar_avail_hdr;
|
||
Input_pvar_avail_hdr = rc->next;
|
||
}
|
||
else
|
||
rc = (struct input_pvar *) NULL;
|
||
} /* endif */
|
||
|
||
if (rc)
|
||
{
|
||
rc->next = (struct input_pvar *) NULL;
|
||
rc->pdest_dbaddr = (void *) NULL;
|
||
rc->source_name[0] = '\0';
|
||
rc->dest_name[0] = '\0';
|
||
rc->pvalue = (void *) NULL;
|
||
rc->maximize_severity = FALSE;
|
||
rc->dest_old_dbr_element_size = 0;
|
||
rc->dest_old_dbr_type = (short) -1; /* hopefully -1 is not */
|
||
/* valid old_dbr_type */
|
||
rc->revised_dest_new_dbr_type = (short) -1; /* hopefully -1 is not */
|
||
/* valid new_dbr_type */
|
||
rc->dest_old_dbr_sts_type = (short) -1; /* hopefully -1 is not */
|
||
/* valid old_dbr_sts_type */
|
||
rc->source_severity = (unsigned short) -1; /* hopefully -1 is not a */
|
||
/* valid status/severity */
|
||
rc->needs_reading = FALSE;
|
||
rc->dest_nelements = (long) -1;
|
||
rc->last_rcvd_nelements = (long) -1;
|
||
} /* endif */
|
||
|
||
return rc;
|
||
|
||
} /* end pop_input_pvar() */
|
||
|
||
/****************************************************************
|
||
*
|
||
* static struct output_pvar *pop_output_pvar()
|
||
*
|
||
* Description:
|
||
* Pops a fresh output pvar node off the avail list.
|
||
*
|
||
* Input: None.
|
||
*
|
||
* Output: None.
|
||
*
|
||
* Returns:
|
||
* address of fresh node
|
||
* NULL if unable to allocate a node
|
||
*
|
||
* Notes:
|
||
* The LOCKs associated with the nodes are only initialized immediately
|
||
* after acquiring the memory for the node while the rest of the fields in
|
||
* the structure are initialized every time the node is popped off the list.
|
||
*
|
||
* It is also assumed that -1 is an invalid source_old_dbr_type.
|
||
*
|
||
****************************************************************/
|
||
|
||
static struct output_pvar *pop_output_pvar()
|
||
{
|
||
|
||
struct output_pvar *rc;
|
||
int i;
|
||
|
||
if (Output_pvar_avail_hdr != (struct output_pvar *) NULL)
|
||
{
|
||
rc = Output_pvar_avail_hdr;
|
||
Output_pvar_avail_hdr = rc->next;
|
||
}
|
||
else
|
||
{
|
||
if ((Output_pvar_avail_hdr =
|
||
(struct output_pvar *) calloc
|
||
((unsigned) NODESPERCALLOC,
|
||
(unsigned) (sizeof (struct output_pvar))))
|
||
!= (struct output_pvar *) NULL)
|
||
{
|
||
for (rc = Output_pvar_avail_hdr, i=0; i < (NODESPERCALLOC-1); i ++)
|
||
{
|
||
FASTLOCKINIT(&(rc->lock));
|
||
rc->next = rc + 1;
|
||
rc++;
|
||
} /* endfor */
|
||
FASTLOCKINIT(&(rc->lock));
|
||
rc->next = (struct output_pvar *) NULL;
|
||
|
||
rc = Output_pvar_avail_hdr;
|
||
Output_pvar_avail_hdr = rc->next;
|
||
}
|
||
else
|
||
rc = (struct output_pvar *) NULL;
|
||
} /* endif */
|
||
|
||
if (rc)
|
||
{
|
||
rc->next = (struct output_pvar *) NULL;
|
||
rc->write_next = (struct output_pvar *) NULL;
|
||
rc->psource_dbaddr = (void *) NULL;
|
||
rc->pvalue = (void *) NULL;
|
||
rc->dest_name[0] = '\0';
|
||
rc->source_name[0] = '\0';
|
||
rc->source_old_dbr_type = (short) -1; /* hope -1 invalid dbr_type */
|
||
rc->source_new_dbr_type = (short) -1; /* hope -1 invalid dbr_type */
|
||
rc->revised_source_new_dbr_type = (short) -1; /* hope -1 invalid */
|
||
/* dbr_type */
|
||
rc->source_nelements = -1L;
|
||
rc->last_nelements_written = -1L;
|
||
rc->valid_value = FALSE;
|
||
rc->on_writelist = FALSE;
|
||
} /* endif */
|
||
|
||
return rc;
|
||
|
||
} /* end pop_output_pvar() */
|
||
|
||
/****************************************************************
|
||
*
|
||
* static long process_asynch_events(seconds)
|
||
* float seconds;
|
||
*
|
||
* Description:
|
||
* Queues a waits for events on remote monitors by calling ca_pend_event().
|
||
*
|
||
* Input:
|
||
* float seconds number of seconds to wait for events (0 means forever)
|
||
*
|
||
* Output: None.
|
||
*
|
||
* Returns:
|
||
* 0 - Success
|
||
* S_dbCa_ECA_EVDISALLOW - attempted to execute from within an event handler
|
||
* S_dbCa_unknownECA - unrecognizable status rc from ca_pend_event()
|
||
*
|
||
* Notes:
|
||
* If seconds = 0 then no status is ever returned.
|
||
*
|
||
****************************************************************/
|
||
|
||
static long process_asynch_events(seconds)
|
||
float seconds;
|
||
{
|
||
|
||
int status;
|
||
char errmsg[100];
|
||
long rc;
|
||
|
||
status = ca_pend_event(seconds);
|
||
|
||
/* from here down won't get executed if */
|
||
/* ca_pend_event(0.0) i.e., waiting forever */
|
||
|
||
switch (status)
|
||
{
|
||
case ECA_NORMAL:
|
||
rc = 0L;
|
||
break;
|
||
case ECA_TIMEOUT:
|
||
rc = 0L;
|
||
break;
|
||
case ECA_EVDISALLOW:
|
||
rc = S_dbCa_ECA_EVDISALLOW;
|
||
errMessage(S_dbCa_ECA_EVDISALLOW,
|
||
"ERROR: process_asynch_events() cannot call inside evt hndlr");
|
||
break;
|
||
default:
|
||
rc = S_dbCa_unknownECA;
|
||
sprintf(errmsg,
|
||
"ERROR: process_asynch_events() unrecognizable status returned %d",
|
||
status);
|
||
errMessage(S_dbCa_unknownECA, errmsg);
|
||
break;
|
||
} /* end switch() */
|
||
|
||
return rc;
|
||
|
||
} /* end process_asynch_events() */
|
||
|
||
/****************************************************************
|
||
*
|
||
* static void process_asynch_events_task(parent_task_id)
|
||
* int parent_task_id;
|
||
*
|
||
* Description:
|
||
* issues a single call to process_asynch_events(0.0). this is a trick
|
||
* suggested by Jeff Hill so that connections that are lost may be
|
||
* re-connected when the other IOC is re-booted. without this trick,
|
||
* a lost connection to another IOC will remain lost, even if the other
|
||
* IOC successfully re-boots.
|
||
*
|
||
* Input:
|
||
* int parent_task_id vxWorks taskid of dbCaProcessOutlinks
|
||
*
|
||
* Output: None.
|
||
*
|
||
* Returns:
|
||
*
|
||
* Notes:
|
||
*
|
||
****************************************************************/
|
||
|
||
static void process_asynch_events_task(parent_task_id)
|
||
int parent_task_id;
|
||
{
|
||
|
||
ca_import(parent_task_id);
|
||
|
||
/* kludge suggested by Jeff Hill so that we can have reliable */
|
||
/* connection management for vxWorks hosted CA clients */
|
||
process_asynch_events((float) 0.0);
|
||
|
||
} /* end process_asynch_events_task() */
|
||
|
||
/****************************************************************
|
||
*
|
||
* static void push_input_pvar(ip)
|
||
* struct input_pvar *ip;
|
||
*
|
||
* Description:
|
||
* Pushes input pvar back onto avail stack.
|
||
*
|
||
* Input:
|
||
* struct input_pvar *ip node to be pushed
|
||
*
|
||
* Output: None.
|
||
*
|
||
* Returns: None.
|
||
*
|
||
* Notes: None.
|
||
*
|
||
****************************************************************/
|
||
|
||
static void push_input_pvar(ip)
|
||
struct input_pvar *ip;
|
||
{
|
||
if (ip)
|
||
{
|
||
ip->next = Input_pvar_avail_hdr;
|
||
Input_pvar_avail_hdr = ip;
|
||
} /* endif */
|
||
|
||
} /* end push_input_pvar() */
|
||
|
||
/****************************************************************
|
||
*
|
||
* static void push_output_pvar(op)
|
||
* struct output_pvar *op;
|
||
*
|
||
* Description:
|
||
* Pushes output pvar back onto avail stack.
|
||
*
|
||
* Input:
|
||
* struct output_pvar *op node to be pushed
|
||
*
|
||
* Output: None.
|
||
*
|
||
* Returns: None.
|
||
*
|
||
* Notes: None.
|
||
*
|
||
****************************************************************/
|
||
|
||
static void push_output_pvar(op)
|
||
struct output_pvar *op;
|
||
{
|
||
if (op)
|
||
{
|
||
op->next = Output_pvar_avail_hdr;
|
||
Output_pvar_avail_hdr = op;
|
||
} /* endif */
|
||
|
||
} /* end push_output_pvar() */
|
||
|
||
/****************************************************************
|
||
*
|
||
* static long queue_add_event_from_input_pvar(pi)
|
||
* struct input_pvar *pi;
|
||
*
|
||
* Description:
|
||
* Queues a monitor on a remote source process input variable and registers
|
||
* my_event_handler() as the event hanlder by calling ca_add_event().
|
||
*
|
||
* Input:
|
||
* struct input_pvar *pi pointer to input pvar structure to estab monitor
|
||
*
|
||
* Output: None.
|
||
*
|
||
* Returns:
|
||
* 0 - Success
|
||
* S_dbCa_nullarg - got NULL pi
|
||
* S_dbCa_ECA_BADCHID - ECA_BADCHID
|
||
* S_dbCa_ECA_BADTYPE - ECA_BADTYPE
|
||
* S_dbCa_ECA_ALLOCMEM - ECA_ALLOCMEM
|
||
* S_dbCa_ECA_ADDFAIL - ECA_ADDFAIL
|
||
* S_dbCa_unknownECA - unknown rc from ca_add_event()
|
||
*
|
||
* Notes: None.
|
||
*
|
||
****************************************************************/
|
||
|
||
static long queue_add_event_from_input_pvar(pi)
|
||
struct input_pvar *pi;
|
||
{
|
||
|
||
int status;
|
||
char errmsg[100];
|
||
long rc;
|
||
|
||
if (pi)
|
||
{
|
||
|
||
status = ca_add_event((chtype) pi->dest_old_dbr_sts_type,
|
||
pi->cid, my_event_handler, pi, (evid *) NULL);
|
||
|
||
switch (status)
|
||
{
|
||
case ECA_NORMAL:
|
||
rc = 0L;
|
||
break;
|
||
case ECA_BADCHID:
|
||
rc = S_dbCa_ECA_BADCHID;
|
||
errMessage(S_dbCa_ECA_BADCHID,
|
||
"ERROR: queue_add_event_from_input_pvar() unconnected/corrupted CHID");
|
||
break;
|
||
case ECA_BADTYPE:
|
||
rc = S_dbCa_ECA_BADTYPE;
|
||
errMessage(S_dbCa_ECA_BADTYPE,
|
||
"ERROR: queue_add_event_from_input_pvar() unknown GET_TYPE");
|
||
break;
|
||
case ECA_ALLOCMEM:
|
||
rc = S_dbCa_ECA_ALLOCMEM;
|
||
errMessage(S_dbCa_ECA_ALLOCMEM,
|
||
"ERROR: queue_add_event_from_input_pvar() unable to alloc mem");
|
||
break;
|
||
case ECA_ADDFAIL:
|
||
rc = S_dbCa_ECA_ADDFAIL;
|
||
errMessage(S_dbCa_ECA_ADDFAIL,
|
||
"ERROR: queue_add_event_from_input_pvar() local db event add failed");
|
||
break;
|
||
default:
|
||
rc = S_dbCa_unknownECA;
|
||
sprintf(errmsg,
|
||
"ERROR: queue_add_event_from_input_pvar() unrecognizable status returned %d",
|
||
status);
|
||
errMessage(S_dbCa_unknownECA, errmsg);
|
||
} /* end switch() */
|
||
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_nullarg;
|
||
errMessage(S_dbCa_nullarg,
|
||
"ERROR: queue_add_event_from_input_pvar() got NULL pi");
|
||
} /* endif */
|
||
|
||
return rc;
|
||
|
||
} /* end queue_add_event_from_input_pvar() */
|
||
|
||
/****************************************************************
|
||
*
|
||
* static long queue_ca_build_and_connect(po)
|
||
* struct output_pvar *po;
|
||
*
|
||
* Description:
|
||
* Queues a search and establishes connections to remote destination pvars
|
||
* for output links and registers my_connection_handler() as the connection
|
||
* handler routine by calling ca_build_and_connect().
|
||
*
|
||
* Input:
|
||
* struct output_pvar *po pointer to output node that needs connection
|
||
*
|
||
* Output: None.
|
||
*
|
||
* Returns:
|
||
* 0 - Success
|
||
* S_dbCa_nullarg - got NULL po
|
||
* S_dbCa_ECA_BADTYPE - ECA_BADTYPE
|
||
* S_dbCa_ECA_STRTOBIG - ECA_STRTOBIG
|
||
* S_dbCa_ECA_ALLOCMEM - ECA_ALLOCMEM
|
||
* S_dbCa_ECA_GETFAIL - ECA_GETFAIL
|
||
* S_dbCa_unknownECA - unknown rc from ca_build_and_connect()
|
||
*
|
||
* Notes: None.
|
||
*
|
||
****************************************************************/
|
||
|
||
static long queue_ca_build_and_connect(po)
|
||
struct output_pvar *po;
|
||
{
|
||
|
||
int status;
|
||
char errmsg[100];
|
||
long rc;
|
||
|
||
if (po)
|
||
{
|
||
/* pointing channel to this output_pvar struct */
|
||
/* for later use by my_connection_handler() */
|
||
|
||
status = ca_build_and_connect(po->dest_name, (chtype) TYPENOTCONN,
|
||
(unsigned long) 0, &(po->cid), (void *) NULL,
|
||
my_connection_handler, (void *) po);
|
||
|
||
switch (status)
|
||
{
|
||
case ECA_NORMAL:
|
||
rc = 0L;
|
||
break;
|
||
case ECA_BADTYPE:
|
||
rc = S_dbCa_ECA_BADTYPE;
|
||
errMessage(S_dbCa_ECA_BADTYPE,
|
||
"ERROR: queue_ca_build_and_connect() unknown GET_TYPE");
|
||
break;
|
||
case ECA_STRTOBIG:
|
||
rc = S_dbCa_ECA_STRTOBIG;
|
||
errMessage(S_dbCa_ECA_STRTOBIG,
|
||
"ERROR: queue_ca_build_and_connect() string too big");
|
||
break;
|
||
case ECA_ALLOCMEM:
|
||
rc = S_dbCa_ECA_ALLOCMEM;
|
||
errMessage(S_dbCa_ECA_ALLOCMEM,
|
||
"ERROR: queue_ca_build_and_connect() unable to allocate memory");
|
||
break;
|
||
case ECA_GETFAIL:
|
||
rc = S_dbCa_ECA_GETFAIL;
|
||
errMessage(S_dbCa_ECA_GETFAIL,
|
||
"ERROR: queue_ca_build_and_connect() a local database get failed");
|
||
break;
|
||
default:
|
||
rc = S_dbCa_unknownECA;
|
||
sprintf(errmsg,
|
||
"ERROR: queue_ca_build_and_connect() unrecognizable status returned %d",
|
||
status);
|
||
errMessage(S_dbCa_unknownECA, errmsg);
|
||
break;
|
||
} /* end switch() */
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_nullarg;
|
||
errMessage(S_dbCa_nullarg,
|
||
"ERROR: queue_ca_build_and_connect() got NULL po");
|
||
} /* endif */
|
||
|
||
return rc;
|
||
|
||
} /* end queue_ca_build_and_connect() */
|
||
|
||
/****************************************************************
|
||
*
|
||
* Description:
|
||
* Queues the writing of the value from the temporary store of an output
|
||
* node by calling ca_put().
|
||
*
|
||
* Input:
|
||
* struct output_pvar *po pointer to the node that has the output value
|
||
*
|
||
* Output: None.
|
||
*
|
||
* Returns:
|
||
* 0 - Success
|
||
* S_dbCa_nullarg - got NULL po
|
||
* S_dbCa_ECA_BADCHID - ECA_BADCHID
|
||
* S_dbCa_ECA_BADTYPE - ECA_BADTYPE
|
||
* S_dbCa_ECA_BADCOUNT - ECA_BADCOUNT
|
||
* S_dbCa_ECA_STRTOBIG - ECA_STRTOBIG
|
||
* S_dbCa_ECA_PUTFAIL - ECA_PUTFAIL
|
||
* S_dbCa_unknownECA - unknown rc from ca_put()
|
||
*
|
||
* Notes: None.
|
||
*
|
||
****************************************************************/
|
||
|
||
static long queue_ca_array_put(po)
|
||
struct output_pvar *po;
|
||
{
|
||
|
||
int status;
|
||
char errmsg[100];
|
||
long rc;
|
||
|
||
if (po)
|
||
{
|
||
status = ca_array_put(po->source_old_dbr_type,
|
||
(unsigned long) po->last_nelements_written, po->cid, po->pvalue);
|
||
|
||
switch (status)
|
||
{
|
||
case ECA_NORMAL:
|
||
rc = 0L;
|
||
break;
|
||
case ECA_BADCHID:
|
||
rc = S_dbCa_ECA_BADCHID;
|
||
errMessage(S_dbCa_ECA_BADCHID,
|
||
"ERROR: queue_ca_put() unconnected/corrupted CHID");
|
||
break;
|
||
case ECA_BADTYPE:
|
||
rc = S_dbCa_ECA_BADTYPE;
|
||
errMessage(S_dbCa_ECA_BADTYPE,
|
||
"ERROR: queue_ca_put() unknown GET_TYPE");
|
||
break;
|
||
case ECA_BADCOUNT:
|
||
rc = S_dbCa_ECA_BADCOUNT;
|
||
errMessage(S_dbCa_ECA_BADCOUNT,
|
||
"ERROR: queue_ca_put() requested count larger than actual chnl elmnt count");
|
||
break;
|
||
case ECA_STRTOBIG:
|
||
rc = S_dbCa_ECA_STRTOBIG;
|
||
errMessage(S_dbCa_ECA_STRTOBIG,
|
||
"ERROR: queue_ca_put() unusually large string supplied");
|
||
break;
|
||
case ECA_PUTFAIL:
|
||
rc = S_dbCa_ECA_PUTFAIL;
|
||
errMessage(S_dbCa_ECA_PUTFAIL,
|
||
"ERROR: queue_ca_put() a local database put failed");
|
||
break;
|
||
default:
|
||
rc = S_dbCa_unknownECA;
|
||
sprintf(errmsg,
|
||
"ERROR: queue_ca_put() unrecognizable status returned %d",
|
||
status);
|
||
errMessage(S_dbCa_unknownECA, errmsg);
|
||
} /* end switch() */
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_nullarg;
|
||
errMessage(S_dbCa_nullarg, "ERROR: queue_ca_put() got NULL po");
|
||
} /* endif */
|
||
|
||
return rc;
|
||
|
||
} /* end queue_ca_array_put() */
|
||
|
||
/****************************************************************
|
||
*
|
||
* static long queue_ca_search(name, pcid)
|
||
* char *name;
|
||
* chid *pcid;
|
||
*
|
||
* Description:
|
||
* Queues a ca_search().
|
||
*
|
||
* Input:
|
||
* char *name name of remote pvar
|
||
* chid *pcid pointer to channel
|
||
*
|
||
* Output: None.
|
||
*
|
||
* Returns:
|
||
* 0 - Success
|
||
* S_dbCa_nullarg - got NULL name or NULL pcid
|
||
* S_dbCa_ECA_BADTYPE - ECA_BADTYPE
|
||
* S_dbCa_ECA_STRTOBIG - ECA_STRTOBIG
|
||
* S_dbCa_ECA_ALLOCMEM - ECA_ALLOCMEM
|
||
* S_dbCa_ECA_GETFAIL - ECA_GETFAIL
|
||
* S_dbCa_unknownECA - unknown rc from ca_search()
|
||
*
|
||
* Notes: None.
|
||
*
|
||
****************************************************************/
|
||
|
||
static long queue_ca_search(name, pcid)
|
||
char *name;
|
||
chid *pcid;
|
||
{
|
||
|
||
int status;
|
||
char errmsg[100];
|
||
long rc;
|
||
|
||
if (name)
|
||
{
|
||
if (pcid)
|
||
{
|
||
status = ca_search(name, pcid);
|
||
|
||
switch (status)
|
||
{
|
||
case ECA_NORMAL:
|
||
rc = 0L;
|
||
break;
|
||
case ECA_BADTYPE:
|
||
rc = S_dbCa_ECA_BADTYPE;
|
||
errMessage(S_dbCa_ECA_BADTYPE,
|
||
"ERROR: queue_ca_search() unknown GET_TYPE");
|
||
break;
|
||
case ECA_STRTOBIG:
|
||
rc = S_dbCa_ECA_STRTOBIG;
|
||
errMessage(S_dbCa_ECA_STRTOBIG,
|
||
"ERROR: queue_ca_search() string too big");
|
||
break;
|
||
case ECA_ALLOCMEM:
|
||
rc = S_dbCa_ECA_ALLOCMEM;
|
||
errMessage(S_dbCa_ECA_ALLOCMEM,
|
||
"ERROR: queue_ca_search() unable to allocate memory");
|
||
break;
|
||
case ECA_GETFAIL:
|
||
rc = S_dbCa_ECA_GETFAIL;
|
||
errMessage(S_dbCa_ECA_GETFAIL,
|
||
"ERROR: queue_ca_search() a local database get failed");
|
||
break;
|
||
default:
|
||
rc = S_dbCa_unknownECA;
|
||
sprintf(errmsg,
|
||
"ERROR: queue_ca_search() unrecognizable status returned %d",
|
||
status);
|
||
errMessage(S_dbCa_unknownECA, errmsg);
|
||
break;
|
||
} /* end switch() */
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_nullarg;
|
||
errMessage(S_dbCa_nullarg,
|
||
"ERROR: queue_ca_search() got NULL pcid");
|
||
} /* endif */
|
||
}
|
||
else
|
||
{
|
||
rc = S_dbCa_nullarg;
|
||
errMessage(S_dbCa_nullarg, "ERROR: queue_ca_search() got NULL name");
|
||
} /* endif */
|
||
|
||
return rc;
|
||
|
||
} /* end queue_ca_search() */
|
||
|
||
/****************************************************************
|
||
*
|
||
* static long task_initialize_channel_access()
|
||
*
|
||
* Description:
|
||
* Initializes channel access for calling task by calling
|
||
* ca_task_initialize().
|
||
*
|
||
* Input: None.
|
||
*
|
||
* Output: None.
|
||
*
|
||
* Returns:
|
||
* 0 - Success
|
||
* S_dbCa_ECA_ALLOCMEM - ECA_ALLOCMEM
|
||
* S_dbCa_unknownECA - unknown rc from ca_task_initialize()
|
||
*
|
||
* Notes:
|
||
* Must be called exactly once by each task making calls to channel access.
|
||
*
|
||
****************************************************************/
|
||
|
||
static long task_initialize_channel_access()
|
||
{
|
||
|
||
int status;
|
||
char errmsg[100];
|
||
long rc;
|
||
|
||
status = ca_task_initialize();
|
||
|
||
switch (status)
|
||
{
|
||
case ECA_NORMAL:
|
||
rc = 0L;
|
||
break;
|
||
case ECA_ALLOCMEM:
|
||
rc = S_dbCa_ECA_ALLOCMEM;
|
||
errMessage(S_dbCa_ECA_ALLOCMEM,
|
||
"ERROR: task_initialize_channel_access() unable to allocate memory");
|
||
break;
|
||
default:
|
||
rc = S_dbCa_unknownECA;
|
||
sprintf(errmsg,
|
||
"ERROR: task_initialize_channel_access() unrecognizable status returned %d",
|
||
status);
|
||
errMessage(S_dbCa_unknownECA, errmsg);
|
||
break;
|
||
} /* end switch() */
|
||
|
||
return rc;
|
||
|
||
} /* end task_initialize_channel_access() */
|