/* Callback from driver for every received tag, for ao record: * Check if * * 1) pact set -> this is the "finshed the write" callback * 2) pact not set -> this is the "new value" callback * Tag value is either different from current record value * or there is no current record value: * 2a) disconnected; process record to set WRITE/INVALID * 2b) PLC's value != record's idea of the current value * 2c) record is UDF, so this is the first time we get a value * from the PLC after a reboot * Causing process if necessary to update the record. * That process()/scanOnce() call should NOT cause the record to write * to the PLC because the xxx_write method will notice that * the PLC and record value agree. * It will, however, trigger CA monitors. * * Problem: Alarms are handled before "write_xx" is called. * So we have to set udf in here, * then process() can recognize udf and finally it will * call write_xx. */ static void check_ao_callback(void *arg) { aoRecord *rec = (aoRecord *) arg; struct rset *rset= (struct rset *)(rec->rset); DevicePrivate *pvt = (DevicePrivate *)rec->dpvt; double dbl; CN_DINT dint; eip_bool process = false; /* We are about the check and even set val, & rval -> lock */ dbScanLock((dbCommon *)rec); if (rec->pact) { if (rec->tpro) printf("EIP check_ao_callback('%s'), pact=%d\n", rec->name, rec->pact); (*rset->process) ((dbCommon *)rec); dbScanUnlock((dbCommon *)rec); return; } /* Check if record's (R)VAL is current */ if (!check_data((dbCommon *)rec)) { if (rec->tpro) printf("EIP check_ao_callback('%s'), no data\n", rec->name); (*rset->process) ((dbCommon *)rec); dbScanUnlock((dbCommon *)rec); return; } if (get_CIP_typecode(pvt->tag->data) == T_CIP_REAL) { if (rec->tpro) printf("EIP check_ao_callback('%s') w/ real data\n", rec->name); if (get_CIP_double(pvt->tag->data, pvt->element, &dbl) && (rec->udf || rec->sevr == INVALID_ALARM || rec->val != dbl)) { if (rec->tpro) printf("'%s': got %g from driver\n", rec->name, dbl); if (!rec->udf && pvt->special & SPCO_FORCE) { if (rec->tpro) printf("'%s': will re-write record's value %g\n", rec->name, rec->val); } else { rec->val = rec->pval = dbl; rec->udf = false; if (rec->tpro) printf("'%s': updated record's value %g\n", rec->name, rec->val); } process = true; } } else { if (rec->tpro) printf("EIP check_ao_callback('%s') w/ int. data\n", rec->name); if (get_CIP_DINT(pvt->tag->data, pvt->element, &dint) && (rec->udf || rec->sevr == INVALID_ALARM || rec->rval != dint)) { if (rec->tpro) printf("AO '%s': got %ld from driver\n", rec->name, (long)dint); if (!rec->udf && pvt->special & SPCO_FORCE) { if (rec->tpro) printf("AO '%s': will re-write record's rval 0x%X\n", rec->name, (unsigned int)rec->rval); } else { /* back-convert raw value into val (copied from ao init) */ dbl = (double)dint + (double)rec->roff; if (rec->aslo!=0.0) dbl *= rec->aslo; dbl += rec->aoff; switch (rec->linr) { case menuConvertNO_CONVERSION: rec->val = rec->pval = dbl; rec->udf = false; break; case menuConvertLINEAR: case menuConvertSLOPE: dbl = dbl*rec->eslo + rec->eoff; rec->val = rec->pval = dbl; rec->udf = false; break; default: if (cvtRawToEngBpt(&dbl,rec->linr,rec->init, (void *)&rec->pbrk, &rec->lbrk)!=0) break; /* cannot back-convert */ rec->val = rec->pval = dbl; rec->udf = false; } if (rec->tpro) printf("'%s': updated record's value to %g\n", rec->name, rec->val); } process = true; } } dbScanUnlock((dbCommon *)rec); /* Does record need processing and is not periodic? */ if (process && rec->scan < SCAN_1ST_PERIODIC) scanOnce(rec); }
/* Read all tags in Scanlist, * using MultiRequests for as many as possible. * Called by scan task, PLC is locked. * * Returns OK when the transactions worked out, * even if the read requests for the tags * returned no data. */ static eip_bool process_ScanList(EIPConnection *c, ScanList *scanlist) { TagInfo *info, *info_position; size_t count, requests_size, responses_size; size_t multi_request_size = 0, multi_response_size = 0; size_t send_size, i, elements; CN_USINT *send_request, *multi_request, *request; const CN_USINT *response, *single_response, *data; EncapsulationRRData rr_data; size_t single_response_size, data_size; epicsTimeStamp start_time, end_time; double transfer_time; TagCallback *cb; eip_bool ok; EIP_printf_time(10, "EIP process_ScanList %g s\n", scanlist->period); info = DLL_first(TagInfo, &scanlist->taginfos); while (info) { /* keep position, we'll loop several times: * 0) in determine_MultiRequest_count * 1) to send out the requests * 2) to handle the responses */ info_position = info; count = determine_MultiRequest_count( c->transfer_buffer_limit, info, &requests_size, &responses_size, &multi_request_size, &multi_response_size); EIP_printf(10, "EIP process_ScanList %lu items\n", (unsigned long)count); if (count == 0) /* Empty, or nothing fits in one request. */ return true; /* send <count> requests as one transfer */ send_size = CM_Unconnected_Send_size(multi_request_size); EIP_printf(10, " ------------------- New Request ------------\n"); TransactionID tid; generateTransactionId(&tid); if (!(send_request = EIP_make_SendRRData(c, send_size, &tid))) return false; multi_request = make_CM_Unconnected_Send(send_request, multi_request_size, c->slot); if (!(multi_request && prepare_CIP_MultiRequest(multi_request, count))) return false; /* Add read/write requests to the multi requests */ for (i=0; i<count; info=DLL_next(TagInfo, info)) { if (info->cip_r_request_size <= 0 || info->cip_w_request_size <= 0) continue; EIP_printf(10, "Request #%d (%s):\n", i, info->string_tag); if (info->is_writing) { request = CIP_MultiRequest_item(multi_request, i, info->cip_w_request_size); if (epicsMutexLock(info->data_lock) != epicsMutexLockOK) { EIP_printf_time(1, "EIP process_ScanList '%s': " "no data lock (write)\n", info->string_tag); info->is_writing = false; return false; } ok = request && make_CIP_WriteData( request, info->cip_w_request_size, info->tag, (CIP_Type)get_CIP_typecode(info->data), info->elements, info->data + CIP_Typecode_size); epicsMutexUnlock(info->data_lock); } else { /* reading, !is_writing */ request = CIP_MultiRequest_item( multi_request, i, info->cip_r_request_size); ok = request && make_CIP_ReadData(request, info->tag, info->elements); } if (!ok) return false; ++i; /* increment here, not in for() -> skip empty tags */ } /* for i=0..count */ epicsTimeGetCurrent(&start_time); if (!EIP_send_connection_buffer(c)) { EIP_printf_time(2, "EIP process_ScanList: Error while sending request\n"); return false; } /* read & disassemble response */ if (!EIP_read_connection_buffer(c)) { EIP_printf_time(2, "EIP process_ScanList: No response\n"); return false; } epicsTimeGetCurrent(&end_time); transfer_time = epicsTimeDiffInSeconds(&end_time, &start_time); response = EIP_unpack_RRData(c->buffer, &rr_data); /* Verify transmission ID */ TransactionID rid; extractTransactionId(&rr_data.header,&rid); if (! compareTransactionIds(&tid, &rid)) { char tidText[32], gidText[32]; transactionIdString(&tid,tidText,sizeof(tidText)); transactionIdString(&rid,gidText,sizeof(gidText)); EIP_printf_time(2, "EIP process_ScanList: Mismatch in transaction ID\n"); EIP_printf(2, "expected %s received %s\n", tidText, gidText); return false; } if (! check_CIP_MultiRequest_Response(response, rr_data.data_length)) { EIP_printf_time(2, "EIP process_ScanList: Error in response\n"); for (info=info_position,i=0; i<count; info=DLL_next(TagInfo, info)) { if (info->cip_r_request_size <= 0) continue; EIP_printf(2, "Tag %i: '%s'\n", i, info->string_tag); ++i; } if (EIP_verbosity >= 2) dump_CIP_MultiRequest_Response_Error(response, rr_data.data_length); return false; } /* Handle individual read/write responses */ for (info=info_position, i=0; i<count; info=DLL_next(TagInfo, info)) { if (info->cip_r_request_size <= 0 || info->cip_w_request_size <= 0) continue; info->transfer_time = transfer_time; single_response = get_CIP_MultiRequest_Response( response, rr_data.data_length, i, &single_response_size); if (! single_response) return false; if (EIP_verbosity >= 10) { EIP_printf(10, "Response #%d (%s):\n", i, info->string_tag); EIP_dump_raw_MR_Response(single_response, 0); } if (epicsMutexLock(info->data_lock) != epicsMutexLockOK) { EIP_printf_time(1, "EIP process_ScanList '%s': " "no data lock (receive)\n", info->string_tag); return false; } if (info->is_writing) { if (!check_CIP_WriteData_Response(single_response, single_response_size)) { EIP_printf_time(0, "EIP: CIPWrite failed for '%s'\n", info->string_tag); info->valid_data_size = 0; } info->is_writing = false; } else /* not writing, reading */ { data = check_CIP_ReadData_Response( single_response, single_response_size, &data_size); if (info->do_write) { /* Possible: Read request ... network delay ... response * and record requested write during the delay. * Ignore the read, because that would replace the data * that device support wants us to write in the next scan */ EIP_printf(8, "EIP '%s': Device support requested write " "in middle of read cycle.\n", info->string_tag); } else { if (data_size > 0 && reserve_tag_data(info, data_size)) { memcpy(info->data, data, data_size); info->valid_data_size = data_size; if (EIP_verbosity >= 10) { elements = CIP_Type_size(get_CIP_typecode(data)); if (elements > 0) { /* response = UINT type, raw data */ elements = (data_size-2) / elements; EIP_printf(10, "Data (%d elements): ", elements); dump_raw_CIP_data(data, elements); } else { EIP_printf(10, "Data: "); EIP_hexdump(0, data, data_size); } } } else info->valid_data_size = 0; } } epicsMutexUnlock(info->data_lock); /* Call all registered callbacks for this tag * so that records can show new value */ for (cb = DLL_first(TagCallback, &info->callbacks); cb; cb=DLL_next(TagCallback, cb)) (*cb->callback) (cb->arg); ++i; } /* "info" now on next unread TagInfo or 0 */ } /* while "info" ... */ return true; }