/** @file transferArrayRecord.c * @brief Transfer from waveform record * * $Author: iitsuka $ * $Date: 2014-09-16 14:30:42 +0900 (2014/09/16 (火)) $ * $Revision: 119 $ */ #include #include #include #include #include #include #include "dbDefs.h" #include "epicsPrint.h" #include "alarm.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "dbScan.h" #include "devSup.h" #include "errMdef.h" #include "recSup.h" #include "recGbl.h" #include "cantProceed.h" #include "menuYesNo.h" /** @cond */ #define GEN_SIZE_OFFSET /** @endcond */ #include "transferArrayRecord.h" /** @cond */ #undef GEN_SIZE_OFFSET /** @endcond */ #include "epicsExport.h" #include "epicsString.h" #include "epicsEvent.h" /** @cond */ //#define DO_TEST //#define TEST_SPEED //#define DEBUG #if defined(DO_TEST) #define TEST_1_25 (1UL << 0U) #define TEST_1_26 (1UL << 1U) #define TEST_2_48 (1UL << 2U) #define TEST_2_49 (1UL << 3U) #define TEST_2_52 (1UL << 4U) #define TEST_2_54 (1UL << 5U) #endif /* defined(DO_TEST) */ /** @endcond */ #define VERSION (200) /**< version number */ /** waveform specification structure */ typedef struct WAVEFORM_SPEC { long nelm; /**< number of elements */ short ftvl; /**< field type of value */ } WAVEFORM_SPEC; /** internal information structure */ typedef struct TA_RECINFO { void *inpbuff; /**< buffer for inp link */ size_t inpsize; /**< size of inpbuff */ void *outbuff; /**< buffer for out link */ size_t outsize; /**< size of outbuff */ WAVEFORM_SPEC wsinp; /**< waveform specification of inp link */ WAVEFORM_SPEC wsout; /**< waveform specification of out link */ epicsMutexId mutexId; /**< mutex id */ epicsEventId eventId; /**< event id */ volatile epicsBoolean done; /**< The flag that indicates put-link done */ } TA_RECINFO; static void monitor(transferArrayRecord *prec); static long transferValue(transferArrayRecord *prec, TA_RECINFO *pinfo); /** @brief record error process * @param[in] prec The pointer to transferArrayRecord structure * @param[in] status status code * @param[in] pmsg The pointer to message to print console * @return status is returned as it is */ static long rec_error(transferArrayRecord *prec, long status, const char *pmsg) { recGblRecordError(status, prec, pmsg); return status; } /** @brief get specification of waveform * @param[in] prec The pointer to a transferArrayRecord structure * @retval 0 End successful * @retval other Error occurred */ static long get_waveform_spec(transferArrayRecord *prec) { TA_RECINFO *pinfo; long status; pinfo = (TA_RECINFO *)(prec->_inf); status = dbGetNelements(&(prec->inp), &(pinfo->wsinp.nelm)); pinfo->wsinp.ftvl = dbGetLinkDBFtype(&(prec->inp)); if ((status != 0L) || (pinfo->wsinp.ftvl == -1)) { return rec_error(prec, S_dev_badInpType, "bad inp field"); } status = dbGetNelements(&(prec->out), &(pinfo->wsout.nelm)); pinfo->wsout.ftvl = dbGetLinkDBFtype(&(prec->out)); if ((status != 0L) || (pinfo->wsout.ftvl == -1)) { return rec_error(prec, S_dev_badOutType, "bad out field"); } #if defined(DEBUG) (void)printf("\n[%s] inp(%s:%ld) out(%s:%ld)", prec->name, ((pinfo->wsinp.nelm == 0L) ? "--" : pamapdbfType[pinfo->wsinp.ftvl].strvalue), pinfo->wsinp.nelm, ((pinfo->wsout.nelm == 0L) ? "--" : pamapdbfType[pinfo->wsout.ftvl].strvalue), pinfo->wsout.nelm); (void)fflush(stdout); #endif// defined(DEBUG) return 0; } /** @brief get pointer of buffer for out-link * @param[in] prec The pointer to transferArrayRecord structure * @return The pointer to buffer */ static void *get_out_buff(transferArrayRecord *prec) { TA_RECINFO *pinfo; size_t bytes; pinfo = (TA_RECINFO *)(prec->_inf); bytes = dbValueSize(prec->ftvl) * pinfo->wsout.nelm; if (pinfo->outbuff == NULL) { pinfo->outbuff = mallocMustSucceed(bytes, "transferArray: cannot allocate outbuff"); pinfo->outsize = bytes; } else { #if defined(DO_TEST) if ((prec->test & TEST_1_25) != 0U) { pinfo->outsize--; } #endif/* defined(DO_TEST) */ if (pinfo->outsize < bytes) { free(pinfo->outbuff); pinfo->outbuff = mallocMustSucceed(bytes, "transferArray: cannot reallocate outbuff"); pinfo->outsize = bytes; } } return pinfo->outbuff; } /** @brief get pointer of buffer for inp-link * @param[in] prec The pointer to transferArrayRecord structure * @return The pointer to buffer */ static void *get_inp_buff(transferArrayRecord *prec) { TA_RECINFO *pinfo; size_t bytes; pinfo = (TA_RECINFO *)(prec->_inf); bytes = dbValueSize(prec->ftvl) * (prec->tasi + prec->tatc); if (pinfo->inpbuff == NULL) { pinfo->inpbuff = mallocMustSucceed(bytes, "transferArray: cannot allocate inpbuff"); pinfo->inpsize = bytes; } else { #if defined(DO_TEST) if ((prec->test & TEST_1_26) != 0U) { pinfo->inpsize--; } #endif/* defined(DO_TEST) */ if (pinfo->inpsize < bytes) { free(pinfo->inpbuff); pinfo->inpbuff = mallocMustSucceed(bytes, "transferArray: cannot reallocate inpbuff"); pinfo->inpsize = bytes; } } return pinfo->inpbuff; } /** Initialize specific record * @param[in,out] prec Pointer to transferArrayRecord structure * @param[in] pass Initialization pass * - 0 : 1st pass * - 1 : 2nd pass(Link resolved) * @return 0 is certainly returned */ static long init_record(transferArrayRecord *prec, int pass) { TA_RECINFO *pinfo; if (pass == 0) { prec->_ver = VERSION; if (prec->nelm <= 0) { prec->nelm = 1; } if (prec->ftvl > DBF_ENUM) { prec->ftvl = DBF_UCHAR; } prec->bptr = callocMustSucceed(prec->nelm, dbValueSize(prec->ftvl), "transferArray calloc failed"); if (prec->nelm == 1) { prec->nord = 1; } else { prec->nord = 0; } return 0L; } /* wf.siml must be a CONSTANT or a PV_LINK or a DB_LINK */ if (prec->siml.type == CONSTANT) { recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm); } /* allocate memory for internal usage */ pinfo = (TA_RECINFO *)mallocMustSucceed(sizeof(TA_RECINFO), "transferArray: cannot allocate record memory"); pinfo->outbuff = NULL; pinfo->outsize = 0U; pinfo->inpbuff = NULL; pinfo->inpsize = 0U; pinfo->mutexId = epicsMutexMustCreate(); pinfo->eventId = epicsEventMustCreate(epicsEventEmpty); pinfo->done = epicsFalse; prec->_inf = pinfo; return 0L; } /** @brief Data read-out with offset specification * @param[in] prec Pointer to transferArray record * @param[in] pBuffer The pointer to a buffer space * @param[in] nRequest The number of elements to read * @param[out] pnGet The pointer to the read number of elements * @param[in] offset Offset * @retval 0 End successful * @retval other Error occurred */ static long get_inp_value(transferArrayRecord *prec, void *pBuffer, long nRequest, long *pnGet, size_t offset) { struct link *pLink; TA_RECINFO *pinfo; char *pInpBuff; long status, request, *p_request; size_t size; pLink = (struct link *)(&(prec->inp)); pinfo = (TA_RECINFO *)(prec->dpvt); if (offset == 0U) { *pnGet = nRequest; status = dbGetLink(pLink, prec->ftvl, pBuffer, NULL, pnGet); } else { pInpBuff = get_inp_buff(prec); request = nRequest + (long)offset; p_request = &request; /* for suppress warning of compiling */ status = dbGetLink(pLink, prec->ftvl, pInpBuff, NULL, p_request); #if defined(DO_TEST) if ((prec->test & TEST_2_48) != 0U) { status = -1L; } else if ((prec->test & TEST_2_49) != 0U) { request = offset; } #endif /* defined(TEST) */ if (status == 0L) { if (request <= offset) { *pnGet = 0L; } else { *pnGet = request - (long)offset; size = dbValueSize(prec->ftvl); memcpy(pBuffer, pInpBuff + offset * size, (*pnGet) * size); } } } #if defined(DEBUG) if (status == 0L) { const unsigned char *pp = (const unsigned char *)pBuffer; size_t i; (void)fprintf(stderr, "\nnRequest=%ld:", nRequest); for (i = 0U; i < 16U; i++) { (void)fprintf(stderr, "%02X ", *(pp + i)); } } #endif// defined(DEBUG) return status; } /** Process record * @param[in] prec Pointer to transferArray record * @retval 0 End successful * @retval other Error occurred */ static long process(transferArrayRecord *prec) { long status; prec->pact = TRUE; status = transferValue(prec, (TA_RECINFO *)(prec->_inf)); prec->udf = FALSE; recGblGetTimeStamp(prec); monitor(prec); /* process the forward scan link record */ recGblFwdLink(prec); prec->pact = FALSE; return status; } /** Convert dbAddr definition * @param[out] paddr Pointer to DBADDR structre * @return 0 is certainly returned */ static long cvt_dbaddr(DBADDR *paddr) { transferArrayRecord *prec = (transferArrayRecord *)paddr->precord; paddr->pfield = prec->bptr; paddr->no_elements = prec->nelm; paddr->field_type = prec->ftvl; paddr->field_size = dbValueSize(prec->ftvl); paddr->dbr_field_type = prec->ftvl; return 0L; } /** Get array information * @param[in] paddr Pointer to DBADDR structure * @param[out] no_elements Pointer to number of elements * @param[out] offset Pointer to offset of array * @return 0 is certainly returned */ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) { transferArrayRecord *prec = (transferArrayRecord *)paddr->precord; *no_elements = prec->nord; *offset = 0; return 0L; } /** Put array information * @param[in] paddr Pointer to DBADDR structure * @param[out] nNew New value of number of read elements * @return 0 is certainly returned */ static long put_array_info(DBADDR *paddr, long nNew) { transferArrayRecord *prec = (transferArrayRecord *)paddr->precord; prec->nord = nNew; if (prec->nord > prec->nelm) { prec->nord = prec->nelm; } return 0L; } /** Raising Monitors * @param[in,out] prec Pointer to transferArray record */ static void monitor(transferArrayRecord *prec) { unsigned short monitor_mask = 0; unsigned int hash = 0; monitor_mask = recGblResetAlarms(prec); if (prec->mpst == transferArrayPOST_Always) { monitor_mask |= DBE_VALUE; } if (prec->apst == transferArrayPOST_Always) { monitor_mask |= DBE_LOG; } /* Calculate hash if we are interested in OnChange events. */ if ((prec->mpst == transferArrayPOST_OnChange) || (prec->apst == transferArrayPOST_OnChange)) { hash = epicsMemHash((char *)prec->bptr, prec->nord * dbValueSize(prec->ftvl), 0); /* Only post OnChange values if the hash is different. */ if (hash != prec->hash) { if (prec->mpst == transferArrayPOST_OnChange) { monitor_mask |= DBE_VALUE; } if (prec->apst == transferArrayPOST_OnChange) { monitor_mask |= DBE_LOG; } /* Store hash for next process. */ prec->hash = hash; /* Post HASH. */ db_post_events(prec, &prec->hash, DBE_VALUE); } } if (monitor_mask != 0U) { db_post_events(prec, prec->bptr, monitor_mask); } } /** Callback function for dbCaPutLinkCallback * @param[in] usrPvt Pointer to user private data */ static void callback(void *usrPvt) { transferArrayRecord *prec = (transferArrayRecord *)usrPvt; TA_RECINFO *pinfo = (TA_RECINFO *)(prec->_inf); epicsMutexMustLock(pinfo->mutexId); pinfo->done = epicsTrue; epicsEventSignal(pinfo->eventId); epicsMutexUnlock(pinfo->mutexId); } /** Put data * @param[in,out] prec Pointer to transferArray record * @param[in] pOutBuff Pointer to put data * @param[in] nPut Number of elements to put * @retval 0 End successful * @retval other Error occurred */ static long myPutLink(transferArrayRecord *prec, char *pOutBuff, long nPut) { TA_RECINFO *pinfo; long status; /* Put data */ if (prec->out.type != CA_LINK) { /* Put data to db-link record */ status = dbPutLink(&(prec->out), prec->ftvl, pOutBuff, nPut); } else { /* Reset pinfo->done certainly */ pinfo = (TA_RECINFO *)(prec->_inf); epicsMutexMustLock(pinfo->mutexId); do { pinfo->done = epicsFalse; } while (epicsEventTryWait(pinfo->eventId) == epicsEventWaitOK); /* Put data to ca-link record */ status = dbCaPutLinkCallback(&(prec->out), prec->ftvl, pOutBuff, nPut, callback, prec); if (status == 0L) { epicsMutexUnlock(pinfo->mutexId); epicsEventWaitWithTimeout(pinfo->eventId, 1.0); /* wait for set pinfo->done */ epicsMutexMustLock(pinfo->mutexId); if (pinfo->done != epicsFalse) { pinfo->done = epicsFalse; } else { (void)fprintf(stderr, "\n%s(%d) [%s] dbCaPutLink callback timeout\n", __FILE__, __LINE__, prec->name); status = -1L; } } epicsMutexUnlock(pinfo->mutexId); } return status; } /** Transfer the value * @param[in,out] prec Pointer to transferArray record * @param[in] pinfo Pointer to internal infromation * @retval 0 End successful * @retval other Error occurred */ static long transferValue(transferArrayRecord *prec, TA_RECINFO *pinfo) { long status; long nCopy, nRead, *p_nRead, nReadReq, nPut; char *pOutBuff, *p; status = get_waveform_spec(prec); if (status != 0L) { recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM); return status; } else if ((prec->ftvl == DBF_STRING) || (pinfo->wsinp.ftvl == DBF_STRING) || (pinfo->wsout.ftvl == DBF_STRING)) { recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM); return rec_error(prec, S_db_badDbrtype, "string-type not support"); } else if (prec->ftvl != pinfo->wsout.ftvl) { recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM); return rec_error(prec, S_db_badDbrtype, "invalid FTVL"); } /* check NELM of self record */ else if (prec->nelm < prec->tatc) { recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM); return rec_error(prec, S_db_badField, "illegal tatc field"); } /* check NELM of INP field record */ else if (pinfo->wsinp.nelm <= prec->tasi) { recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM); return rec_error(prec, S_db_badField, "illegal tasi field"); } /* check NELM of OUT field record */ else if (pinfo->wsout.nelm <= prec->tadi) { recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM); return rec_error(prec, S_db_badField, "illegal tadi field"); } if ((prec->tadi == 0) && (prec->tazf == menuYesNoYES)) { pOutBuff = NULL; } else { nRead = pinfo->wsout.nelm; p_nRead = &nRead; /* for suppress warning of compiling */ pOutBuff = get_out_buff(prec); status = dbGetLink(&(prec->out), prec->ftvl, pOutBuff, NULL, p_nRead); #if defined(DO_TEST) if ((prec->test & TEST_2_52) != 0U) { status = -1L; } #endif/* defined(DO_TEST) */ if (status != 0L) { (void)fprintf(stderr, "\n%s(%d) %s:dbGetLink() error(%ld)", __FILE__, __LINE__, prec->name, status); recGblSetSevr(prec, LINK_ALARM, MAJOR_ALARM); return status; } } if (pinfo->wsinp.nelm < prec->tasi + prec->tatc) { recGblSetSevr(prec, LINK_ALARM, MINOR_ALARM); nReadReq = pinfo->wsinp.nelm - prec->tasi; } else { nReadReq = prec->tatc; } /* Read waveform from INP field */ status = get_inp_value(prec, prec->bptr, nReadReq, &nRead, prec->tasi); if (status != 0L) { (void)fprintf(stderr, "\n%s(%d) %s:get_inp_value() error(%ld)", __FILE__, __LINE__, prec->name, status); recGblSetSevr(prec, LINK_ALARM, MAJOR_ALARM); } else { prec->nord = nRead; if (pinfo->wsout.nelm < prec->tadi + nRead) { recGblSetSevr(prec, LINK_ALARM, MINOR_ALARM); nCopy = pinfo->wsout.nelm - prec->tadi; } else { nCopy = nRead; } if (pOutBuff == NULL) { pOutBuff = prec->bptr; } else { p = pOutBuff + dbValueSize(prec->ftvl) * prec->tadi; memcpy(p, prec->bptr, dbValueSize(prec->ftvl) * nCopy); } /* Calculate size of put */ if (prec->tazf == menuYesNoNO) { nPut = pinfo->wsout.nelm; } else { nPut = prec->tadi + nCopy; } /* Put waveform to OUT field */ status = myPutLink(prec, pOutBuff, nPut); #if defined(DO_TEST) if ((prec->test & TEST_2_54) != 0) { status = -1L; } #endif/* defined(DO_TEST) */ if (status != 0L) { (void)fprintf(stderr, "\n%s(%d) %s:dbPutLink() error(%ld)\n", __FILE__, __LINE__, prec->name, status); recGblSetSevr(prec, LINK_ALARM, MAJOR_ALARM); } } return status; } /** @cond */ /* Create RSET - Record Support Entry Table*/ rset transferArrayRSET = { RSETNUMBER, NULL, /* report */ NULL, /* initialize */ init_record, process, NULL, /* special */ NULL, /* get_value */ cvt_dbaddr, get_array_info, put_array_info, NULL, /* get_units */ NULL, /* get_precision */ NULL, /* get_enum_str */ NULL, /* get_enum_strs */ NULL, /* put_enum_str */ NULL, /* get_graphic_double */ NULL, /* get_control_double */ NULL /* get_alarm_double */ }; epicsExportAddress(rset, transferArrayRSET); /** @endcond */