Example #1
0
/* 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);
}
Example #2
0
/* 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;
}