Ejemplo n.º 1
0
/* Scan task, one per PLC */
static void PLC_scan_task(PLC *plc)
{
    ScanList *list;
    epicsTimeStamp    next_schedule, start_time, end_time;
    double            timeout, delay, quantum;
    eip_bool          transfer_ok, reset_next_schedule;

    quantum = epicsThreadSleepQuantum();
    timeout = (double)ETHERIP_TIMEOUT/1000.0;
scan_loop: /* --------- The Scan Loop for one PLC -------- */
    if (epicsMutexLock(plc->lock) != epicsMutexLockOK)
    {
        EIP_printf_time(1, "drvEtherIP scan task for PLC '%s'"
                   " cannot take plc->lock\n", plc->name);
        return;
    }
    if (!assert_PLC_connect(plc))
    {   /* don't rush since connection takes network bandwidth */
        epicsMutexUnlock(plc->lock);
        EIP_printf_time(2, "drvEtherIP: PLC '%s' is disconnected\n", plc->name);
        epicsThreadSleep(timeout);
        goto scan_loop;
    }
    EIP_printf_time(10, "drvEtherIP scan PLC '%s'\n", plc->name);
    reset_next_schedule = true;
    epicsTimeGetCurrent(&start_time);
    for (list = DLL_first(ScanList,&plc->scanlists);
         list;  list = DLL_next(ScanList,list))
    {
        if (! list->enabled)
            continue;
        if (epicsTimeLessThanEqual(&list->scheduled_time, &start_time))
        {
            epicsTimeGetCurrent(&list->scan_time);
            transfer_ok = process_ScanList(plc->connection, list);
            epicsTimeGetCurrent(&end_time);
            list->last_scan_time =
                epicsTimeDiffInSeconds(&end_time, &list->scan_time);
            /* update statistics */
            if (list->last_scan_time > list->max_scan_time)
                list->max_scan_time = list->last_scan_time;
            if (list->last_scan_time < list->min_scan_time  ||
                list->min_scan_time == 0.0)
                list->min_scan_time = list->last_scan_time;
            if (transfer_ok) /* re-schedule exactly */
            {
                list->scheduled_time = list->scan_time;
                epicsTimeAddSeconds(&list->scheduled_time, list->period);
            }
            else
            {  	/* end_time+fixed delay, ignore extra due to error */
                list->scheduled_time = end_time;
                epicsTimeAddSeconds(&list->scheduled_time, timeout);
                ++list->list_errors;
                ++plc->plc_errors;
                disconnect_PLC(plc);
                epicsMutexUnlock(plc->lock);
                goto scan_loop;
            }
        }
        /* Update time for list that's due next */
        if (reset_next_schedule ||
            epicsTimeLessThan(&list->scheduled_time, &next_schedule))
        {
            reset_next_schedule = false;
            next_schedule = list->scheduled_time;
        }
    }
    epicsMutexUnlock(plc->lock);
    /* fallback for empty/degenerate scan list */
    if (reset_next_schedule)
        delay = EIP_MIN_TIMEOUT;
    else
    {
        epicsTimeGetCurrent(&start_time);
        delay = epicsTimeDiffInSeconds(&next_schedule, &start_time);
        if (delay > 60.0)
        {
            char      tsString[50];
            printf("Scanlist %g secs has scheduling problem, delay = %g sec\n",
                  list->period, delay);
            epicsTimeToStrftime(tsString, sizeof(tsString),
                                "%Y/%m/%d %H:%M:%S.%04f", &list->scan_time);
            printf("  'Scan time'    : %s\n", tsString);
            epicsTimeToStrftime(tsString, sizeof(tsString),
                                "%Y/%m/%d %H:%M:%S.%04f", &start_time);
            printf("  'Current time' : %s\n", tsString);
            epicsTimeToStrftime(tsString, sizeof(tsString),
                                "%Y/%m/%d %H:%M:%S.%04f", &next_schedule);
            printf("  'Next    time' : %s\n", tsString);
            /* Attempt to hack around this by waiting a minute,
             * hoping that the clock looks better by then.
             * Also resetting the scheduled time to 'now'.
             */
            delay = 60.0;
            list->scheduled_time = start_time;
            ++list->sched_errors;
        }
    }
    /* Sleep until next turn. */
    if (delay > 0.0)
        epicsThreadSleep(delay);
    else if (delay <= -quantum)
    {
        EIP_printf(8, "drvEtherIP scan task slow, %g sec delay\n", delay);
        ++plc->slow_scans; /* hmm, "plc" not locked... */
    }
    goto scan_loop;
}
Ejemplo n.º 2
0
static void periodicTask(void *arg)
{
    periodic_scan_list *ppsl = (periodic_scan_list *)arg;
    epicsTimeStamp next, reported;
    unsigned int overruns = 0;
    double report_delay = OVERRUN_REPORT_DELAY;
    double overtime = 0.0;
    double over_min = 0.0;
    double over_max = 0.0;
    const double penalty = (ppsl->period >= 2) ? 1 : (ppsl->period / 2);

    taskwdInsert(0, NULL, NULL);
    epicsEventSignal(startStopEvent);

    epicsTimeGetCurrent(&next);
    reported = next;

    while (ppsl->scanCtl != ctlExit) {
        double delay;
        epicsTimeStamp now;

        if (ppsl->scanCtl == ctlRun)
            scanList(&ppsl->scan_list);

        epicsTimeAddSeconds(&next, ppsl->period);
        epicsTimeGetCurrent(&now);
        delay = epicsTimeDiffInSeconds(&next, &now);
        if (delay <= 0.0) {
            if (overtime == 0.0) {
                overtime = over_min = over_max = -delay;
            }
            else {
                overtime -= delay;
                if (over_min + delay > 0)
                    over_min = -delay;
                if (over_max + delay < 0)
                    over_max = -delay;
            }
            delay = penalty;
            ppsl->overruns++;
            next = now;
            epicsTimeAddSeconds(&next, delay);
            if (++overruns >= 10 &&
                epicsTimeDiffInSeconds(&now, &reported) > report_delay) {
                errlogPrintf("\ndbScan warning from '%s' scan thread:\n"
                    "\tScan processing averages %.2f seconds (%.2f .. %.2f).\n"
                    "\tOver-runs have now happened %u times in a row.\n"
                    "\tTo fix this, move some records to a slower scan rate.\n",
                    ppsl->name, ppsl->period + overtime / overruns,
                    ppsl->period + over_min, ppsl->period + over_max, overruns);

                reported = now;
                if (report_delay < (OVERRUN_REPORT_MAX / 2))
                    report_delay *= 2;
                else
                    report_delay = OVERRUN_REPORT_MAX;
            }
        }
        else {
            overruns = 0;
            report_delay = OVERRUN_REPORT_DELAY;
            overtime = 0.0;
        }

        epicsEventWaitWithTimeout(ppsl->loopEvent, delay);
    }

    taskwdRemove(0);
    epicsEventSignal(startStopEvent);
}