Files
sics/site_ansto/hardsup/Monitor/cntr.c
Douglas Clowes 816e49012b wrap around check not working - disable it for now
r1121 | dcl | 2006-10-09 12:21:46 +1000 (Mon, 09 Oct 2006) | 2 lines
2012-11-15 12:47:26 +11:00

650 lines
17 KiB
C

/*
* Abstraction of the counter device.
*
*/
#include "cntr.h"
#include "params.h"
#include "sock.h"
#include "hctr.h"
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#define HCTR_TEST(functionCall) \
if( hctr_failed(error=(functionCall)) ) \
goto Error; \
else
COUNTER counter;
/*
* get a pointer to the current sample
*/
static SAMPLE* cur_sample(COUNTER* cp)
{
return &cp->sample_array[cp->sample_index];
}
/*
* get a pointer to the num'th previous sample
*/
static SAMPLE* prv_sample(COUNTER* cp, int num)
{
int idx = (cp->sample_index + SAMPLE_ARRAY_SZ - num) % SAMPLE_ARRAY_SZ;
return &cp->sample_array[idx];
}
void make_report(COUNTER* cp)
{
dprintf(0, "make_report\n");
int i;
SAMPLE* sp = cur_sample(cp);
cp->report = *sp;
cp->report.average_rate = cp->report.counter_rate;
cp->report.minimum_rate = cp->report.counter_rate;
cp->report.maximum_rate = cp->report.counter_rate;
for (i = 1; i <= cp->params.report_period; ++i)
{
SAMPLE* psp;
psp = prv_sample(cp, i);
if (psp->valid)
{
cp->report.time_delta = time_diff(&sp->timestamp, &psp->timestamp);
cp->report.count_delta = sp->count64 - psp->count64;
if (cp->report.time_delta > 0.0)
cp->report.average_rate = cp->report.count_delta
/ cp->report.time_delta;
if (i < cp->params.report_period)
{
cp->report.num_polls += psp->num_polls;
if (psp->counter_rate < cp->report.minimum_rate)
cp->report.minimum_rate = psp->counter_rate;
if (psp->counter_rate > cp->report.maximum_rate)
cp->report.maximum_rate = psp->counter_rate;
}
}
}
sp = &cp->report;
}
#if 0
/*
* given two samples, compute the count-rate
*/
static double compute_rate(COUNTER* cp, SAMPLE* cur, SAMPLE* prv)
{
double result = 0.0;
uInt32 delta_counter;
if (cp->params.direction == COUNT_DOWN)
delta_counter = prv->counter_value - cur->counter_value;
else
delta_counter = cur->counter_value - prv->counter_value;
double delta_timer = time_diff(&cur->timestamp, &prv->timestamp);
result = 1.0 * delta_counter / delta_timer;
return result;
}
/*
* given two samples, compute the average rate
*/
static double compute_average(COUNTER* cp, SAMPLE* cur, SAMPLE* prv)
{
double result = 0.0;
if (cur == prv)
result = cur->counter_rate;
else
result = compute_rate(cp, cur, prv);
return result;
}
#endif
void cntr_send(COUNTER* cp, int n)
{
SAMPLE* sp = cur_sample(cp);
BUFFER buffer;
buffer.length = 0;
snprintf(buffer.body, sizeof(buffer.body),
"Time: %s, Count: %10llu, Delta: %6d, Time: %8.6f, Rate: %8.2f, Ave: %8.2f\r\n",
make_timestamp(&sp->timestamp),
sp->counter_value,
sp->count_delta,
sp->time_delta,
sp->counter_rate,
sp->average_rate);
buffer.length = strlen(buffer.body);
sock_send(n, &buffer);
}
void cntr_read(COUNTER* cp, int n)
{
SAMPLE* sp = cur_sample(cp);
BUFFER buffer;
buffer.length = 0;
snprintf(buffer.body, sizeof(buffer.body),
"READ %c%c%c%c %s %.6f %10llu %8.2f\r\n",
cp->state == counter_idle ? 'I' :
cp->state == counter_stopped ? 'S' :
cp->state == counter_running ? 'R' :
cp->state == counter_paused ? 'P' : '?',
cp->terminal_due ? 'T' : ' ',
cp->range_error == 0 ? ' ' : 'R',
cp->range_gated ? 'G' : ' ',
make_timestamp(&sp->timestamp),
cp->accumulated.tv_sec + .000001 * cp->accumulated.tv_usec,
sp->counter_value,
sp->counter_rate);
buffer.length = strlen(buffer.body);
sock_send(n, &buffer);
}
void cntr_print(COUNTER* cp, FILE* fd)
{
SAMPLE* sp = cur_sample(cp);
fprintf(fd, "Time: %s, Count: %10llu, Delta: %6d, Time: %8.6f, Rate: %8.2f, Ave: %8.2f\r\n",
make_timestamp(&sp->timestamp),
sp->counter_value,
sp->count_delta,
sp->time_delta,
sp->counter_rate,
sp->average_rate);
fflush(fd);
}
/*
* Finalise the current sample and move on to the next
*/
void cntr_sample(COUNTER* cp)
{
SAMPLE* psp = cur_sample(cp);
dprintf(0, "cntr_sample: %4d\r\n"
" polls: %4d\r\n"
" time: %4s\r\n"
" counter: %10llu\r\n"
" count64: %10llu\r\n"
" c_delta: %d\r\n"
" t_delta: %6.3f\r\n"
" rate: %10g\n",
cp->sample_index,
psp->num_polls,
make_timestamp(&psp->timestamp),
psp->counter_value,
psp->count64,
psp->count_delta,
psp->time_delta,
psp->counter_rate);
cp->sample_timer = cp->current_time;
++cp->sample_counter;
if (++cp->sample_index >= SAMPLE_ARRAY_SZ)
cp->sample_index = 0;
SAMPLE* sp = cur_sample(cp);
*sp = *psp;
sp->valid = true;
sp->num_polls = 0;
sp->sample_counter = cp->sample_counter;
sp->poll_counter = cp->poll_counter;
}
void cntr_report(COUNTER* cp)
{
dprintf(0, "cntr_report\n");
/*
* Set the time for this report
*/
cp->report_timer = cp->current_time;
BUFFER buffer;
SAMPLE* sp;
sp = &cp->report;
char* str = make_timestamp(&sp->timestamp);
snprintf(buffer.body, sizeof(buffer.body),
"%s (%6.3f), %10llu (%8d), %8.2f (%8.2f,%8.2f,%8.2f)\r\n",
str,
sp->time_delta,
sp->counter_value,
sp->count_delta,
sp->counter_rate,
sp->minimum_rate,
sp->average_rate,
sp->maximum_rate);
buffer.length = strlen(buffer.body);
//fputs(buffer.body, stdout);
sock_report(&buffer, 1);
snprintf(buffer.body, sizeof(buffer.body),
"REPORT %s %10llu %8.2f (%8.2f,%8.2f,%8.2f)\r\n",
str,
sp->counter_value,
sp->counter_rate,
sp->minimum_rate,
sp->average_rate,
sp->maximum_rate);
buffer.length = strlen(buffer.body);
//fputs(buffer.body, stdout);
sock_report(&buffer, 2);
}
/**
* Initialise the counter
*
* Initialise all of the control data associated with the logical counter.
*
* Create a 64-bit physical counter and start it.
*/
int cntr_init(COUNTER* cp, char* name)
{
int error = 0;
char errBuff[2048]={'\0'};
memset(cp, 0, sizeof(COUNTER));
strncpy(cp->name, name, sizeof(cp->name));
cp->params.poll_period = 1000; /* milliseconds between polls */
cp->params.sample_period = 10; /* polls between sample calcs */
cp->params.report_period = 3; /* samples between reports */
cp->state = counter_stopped;
struct timeval now;
gettimeofday(&now, NULL);
cp->current_time = now;
cp->previous_time = now;
cp->sample_timer = now;
cp->report_timer = now;
HCTR_TEST(hctr_ctor(name, &cp->private_data));
SAMPLE* sp = cur_sample(cp);
sp->timestamp = now;
sp->counter_value = cp->current_count;
sp->valid = true;
cntr_sample(cp);
return 0;
Error:
hctr_errmsg(errBuff, sizeof(errBuff));
printf("DAQmx Error: %s\n", errBuff);
return error;
return 0;
}
/**
* Start the logical counter
*
* Read the value of the physical counter and set the state to running
*/
int cntr_start(COUNTER *cp)
{
int error = 0;
char errBuff[2048]={'\0'};
struct timeval now;
int value;
PARAMETERS* pp = &cp->params;
/* start the counter object */
gettimeofday(&now, NULL);
cp->current_time = now;
cp->start_time = cp->current_time;
cp->current_count = cp->params.initial_count;
cp->accumulated.tv_sec = 0;
cp->accumulated.tv_usec = 0;
cp->poll_counter = 0;
cp->sample_counter = 0;
cp->terminal_due = false;
cp->state = counter_running;
cp->previous_time = cp->current_time;
HCTR_TEST(hctr_read(cp->private_data, &cp->count64));
SAMPLE* sp = cur_sample(cp);
sp->timestamp = cp->current_time;
sp->counter_value = cp->current_count;
SAMPLE* psp = prv_sample(cp, 1);
psp->timestamp = cp->current_time;
psp->counter_value = cp->current_count;
make_report(cp);
if (pp->output_line == 1)
value = 1;
else if (pp->output_line == 2)
value = 0;
else
value = -1;
dprintf(0, "Setting output line to %d\n", value);
HCTR_TEST(hctr_outp(cp->private_data, value));
return error;
Error:
cntr_errmsg(errBuff, sizeof(errBuff));
printf("DAQmx Error: %s\n", errBuff);
return error;
}
int cntr_stop(COUNTER *cp)
{
int error = 0;
char errBuff[2048]={'\0'};
PARAMETERS* pp = &cp->params;
int value;
cp->stop_time = cp->current_time;
cp->state = counter_stopped;
if (pp->output_line == 1)
value = 0;
else if (pp->output_line == 2)
value = 1;
else
value = -1;
dprintf(0, "Setting output line to %d\n", value);
HCTR_TEST(hctr_outp(cp->private_data, value));
return error;
Error:
cntr_errmsg(errBuff, sizeof(errBuff));
printf("DAQmx Error: %s\n", errBuff);
return error;
}
int cntr_pause(COUNTER *cp)
{
if (cp->state == counter_running)
cp->state = counter_paused;
return 0;
}
int cntr_resume(COUNTER *cp)
{
if (cp->state == counter_paused)
cp->state = counter_running;
return 0;
}
static void cntr_event(COUNTER *cp, char* event)
{
BUFFER buffer;
sprintf(buffer.body, "EVENT %s %s\r\n",
event,
make_timestamp(&cp->current_time));
buffer.length = strlen(buffer.body);
dprintf(0, "%s", buffer.body);
//sock_report(&buffer, 0);
sock_report(&buffer, 1);
sock_report(&buffer, 2);
}
static void cntr_range_check(COUNTER* cp, int mode)
{
PARAMETERS* pp = &cp->params;
if (pp->range_check_enable)
{
if (pp->range_mode == mode)
{
double test;
SAMPLE* sp = cur_sample(cp);
if (mode == 1)
test = sp->counter_rate;
else if (mode == 2)
test = sp->counter_rate;
else
test = cp->report.average_rate;
if (pp->range_low > 0 && pp->range_low > test)
{
if (cp->range_error != 1)
cntr_event(cp, "RANGE OUT LOW");
cp->range_error = 1;
if (pp->range_gate_enable)
cp->range_gated = true;
else
cp->range_gated = false;
}
else if (pp->range_high > 0 && pp->range_high < test)
{
if (cp->range_error != 2)
cntr_event(cp, "RANGE OUT HIGH");
cp->range_error = 2;
if (pp->range_gate_enable)
cp->range_gated = true;
else
cp->range_gated = false;
}
else
{
if (cp->range_error != 0)
cntr_event(cp, "RANGE IN");
cp->range_error = 0;
cp->range_gated = false;
}
}
}
else
{
/* If range check has been disabled while in error - reset */
if (cp->range_error != 0)
cntr_event(cp, "RANGE IN");
cp->range_error = 0;
cp->range_gated = false;
}
}
static void cntr_test_term(COUNTER* cp)
{
PARAMETERS* pp = &cp->params;
SAMPLE* sp = cur_sample(cp);
SAMPLE* psp = prv_sample(cp, 1);
if (!cp->terminal_due)
{
if (pp->terminal_check_type == 1)
{
if (pp->direction == COUNT_DOWN)
{
/*
* decremented to or through terminal
*/
// TODO FIXME improve wraparound handling
if ((sp->counter_value <= pp->terminal_count &&
psp->counter_value > pp->terminal_count) ||
(sp->counter_value > psp->counter_value &&
psp->counter_value > pp->terminal_count))
{
cp->terminal_due = true;
}
}
else
{
/*
* incremented to or through terminal
*/
// TODO FIXME improve wraparound handling
if (
#if 1
sp->counter_value >= pp->terminal_count
#else
(sp->counter_value >= pp->terminal_count &&
psp->counter_value < pp->terminal_count) ||
(sp->counter_value < psp->counter_value &&
psp->counter_value > pp->terminal_count)
#endif
)
{
cp->terminal_due = true;
}
}
}
else if (pp->terminal_check_type == 2)
{
if (cp->accumulated.tv_sec >= pp->terminal_count)
cp->terminal_due = true;
}
if (cp->terminal_due)
{
cntr_event(cp, "TERMINAL");
make_report(cp);
cntr_stop(cp);
}
}
}
/*
* poll the physical counter
*/
int cntr_poll(COUNTER* cp)
{
PARAMETERS* pp = &cp->params;
char errBuff[2048]={'\0'};
unsigned long long current_count_local;
int count_delta_local;
int error=0;
/* read the value from the hardware counter to a temp */
++cp->poll_counter;
HCTR_TEST(hctr_read(cp->private_data, &current_count_local));
dprintf(0, "cntr_poll = %llu @ %s\n",
current_count_local,
make_timestamp(&cp->current_time));
SAMPLE* sp = cur_sample(cp);
SAMPLE* psp = prv_sample(cp, 1);
/* calculate the number since last time and save new value */
count_delta_local = current_count_local - cp->count64;
cp->count64 = current_count_local;
sp->num_polls += 1;
/*
* If the counter is running and not gated increment the count and runtime
*/
if (cp->state == counter_running &&
!(cp->params.range_gate_enable && cp->range_gated))
{
if (cp->params.direction == COUNT_DOWN)
{
cp->current_count -= count_delta_local;
}
else
{
cp->current_count += count_delta_local;
}
/*
* Add the time difference to the accumulated time
*/
cp->accumulated.tv_sec += cp->current_time.tv_sec - sp->timestamp.tv_sec;
/* prevent negative tv_usec by borrowing one second in microseconds */
cp->accumulated.tv_usec += 1000000;
cp->accumulated.tv_usec += cp->current_time.tv_usec;
cp->accumulated.tv_usec -= sp->timestamp.tv_usec;
if (cp->accumulated.tv_usec >= 1000000)
{
/* carry the seconds */
cp->accumulated.tv_sec += cp->accumulated.tv_usec / 1000000;
cp->accumulated.tv_usec %= 1000000;
}
/* pay back the borrowed second */
cp->accumulated.tv_sec -= 1;
}
/* calculate and check the count-rate between polls */
sp->count_delta = cp->count64 - sp->count64;
sp->time_delta = time_diff(&cp->current_time, &sp->timestamp);
sp->counter_rate = (double) sp->count_delta / sp->time_delta;
cntr_range_check(cp, 2); /* poll range check */
cp->previous_time = cp->current_time;
/* save counter values in the sample */
sp->counter_value = cp->current_count;
sp->count64 = cp->count64;
sp->timestamp = cp->current_time;
/* calculate the count-rate for this sample so far */
sp->count_delta = sp->count64 - psp->count64;
sp->time_delta = time_diff(&sp->timestamp, &psp->timestamp);
sp->counter_rate = (double) sp->count_delta / sp->time_delta;
/* test for the occurrence of a terminal event */
cntr_test_term(cp);
/* check if it is time to roll the sample */
if (cntr_time_to_next_sample(cp) <= 0)
{
/* check if it is time to roll the report */
if (cntr_time_to_next_report(cp) <= 0)
{
make_report(cp);
cntr_range_check(cp, 0); /* report range check */
cntr_report(cp);
}
cntr_range_check(cp, 1); /* sample range check */
cntr_sample(cp);
}
/* drive the output line */
if (cp->output_line != pp->output_line)
{
int value;
cp->output_line = pp->output_line;
if (pp->output_line == 1)
{
if (cp->state == counter_running || cp->state == counter_paused)
value = 1;
else
value = 0;
}
else if (pp->output_line == 2)
{
if (cp->state == counter_running || cp->state == counter_paused)
value = 0;
else
value = 1;
}
else
value = -1;
HCTR_TEST(hctr_outp(cp->private_data, value));
}
return error;
Error:
cntr_errmsg(errBuff, sizeof(errBuff));
printf("DAQmx Error: %s\n", errBuff);
return error;
}
void cntr_term(COUNTER* cp)
{
if (cp->private_data)
{
hctr_dtor(&cp->private_data);
cp->private_data = NULL;
}
cp->state = counter_idle;
}
bool cntr_fatal(int error)
{
return hctr_failed(error);
}
void cntr_errmsg(char* buff, int len)
{
hctr_errmsg(buff, len);
}
double cntr_time_to_next_report(COUNTER* cp)
{
uint64 last_report;
uint64 timeofday;
int timeout;
struct timeval now;
now = cp->current_time;
last_report = 1000 * (uint64) cp->report_timer.tv_sec;
last_report += (uint64) cp->report_timer.tv_usec / 1000;
timeofday = 1000 * (uint64) now.tv_sec;
timeofday += (uint64) now.tv_usec / 1000;
timeout = cp->params.poll_period * cp->params.sample_period *
cp->params.report_period;
if ((last_report / timeout) != (timeofday / timeout))
return 0.0;
timeout = timeout - timeofday % timeout;
return 0.001 * timeout;
}
double cntr_time_to_next_sample(COUNTER* cp)
{
uint64 last_sample;
uint64 timeofday;
int timeout;
struct timeval now;
now = cp->current_time;
last_sample = 1000 * (uint64) cp->sample_timer.tv_sec;
last_sample += (uint64) cp->sample_timer.tv_usec / 1000;
timeofday = 1000 * (uint64) now.tv_sec;
timeofday += (uint64) now.tv_usec / 1000;
timeout = cp->params.poll_period * cp->params.sample_period;
if ((last_sample / timeout) != (timeofday / timeout))
return 0.0;
timeout = timeout - timeofday % timeout;
return 0.001 * timeout;
}