diff --git a/site_ansto/hardsup/Monitor/device.c b/site_ansto/hardsup/Monitor/device.c new file mode 100644 index 00000000..634d9ae5 --- /dev/null +++ b/site_ansto/hardsup/Monitor/device.c @@ -0,0 +1,683 @@ +/* + * Abstraction of the counter device. + * + */ +#include "device.h" +#include "params.h" +#include "sock.h" +#include "hware.h" +#include +#include +#include + +#define HWARE_TEST(functionCall) \ + if( hware_failed(error=(functionCall)) ) \ + goto Error; \ + else + +/* + * get a pointer to the current sample + */ +static SAMPLE* cur_sample(DEVICE* device) +{ + return &device->sample_array[device->sample_index]; +} + +/* + * get a pointer to the num'th previous sample + */ +static SAMPLE* prv_sample(DEVICE* device, int num) +{ + int idx = (device->sample_index + SAMPLE_ARRAY_SZ - num) % SAMPLE_ARRAY_SZ; + return &device->sample_array[idx]; +} + +void make_report(DEVICE* device) +{ + dbg_printf(0, "make_report\n"); + int i; + SAMPLE* sp = cur_sample(device); + device->report = *sp; + device->report.average_rate = device->report.counter_rate; + device->report.minimum_rate = device->report.counter_rate; + device->report.maximum_rate = device->report.counter_rate; + for (i = 1; i <= device->params.report_period; ++i) + { + SAMPLE* psp; + psp = prv_sample(device, i); + if (psp->valid) + { + device->report.time_delta = time_diff(&sp->timestamp, &psp->timestamp); + device->report.count_delta = sp->count64 - psp->count64; + if (device->report.time_delta > 0.0) + device->report.average_rate = device->report.count_delta + / device->report.time_delta; + if (i < device->params.report_period) + { + device->report.num_polls += psp->num_polls; + if (psp->counter_rate < device->report.minimum_rate) + device->report.minimum_rate = psp->counter_rate; + if (psp->counter_rate > device->report.maximum_rate) + device->report.maximum_rate = psp->counter_rate; + } + } + } + sp = &device->report; +} + +#if 0 +/* + * given two samples, compute the count-rate + */ +static double compute_rate(DEVICE* device, SAMPLE* cur, SAMPLE* prv) +{ + double result = 0.0; + uInt32 delta_counter; + if (device->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(DEVICE* device, SAMPLE* cur, SAMPLE* prv) +{ + double result = 0.0; + if (cur == prv) + result = cur->counter_rate; + else + result = compute_rate(device, cur, prv); + return result; +} +#endif + +void device_send(DEVICE* device, int n) +{ + SAMPLE* sp = cur_sample(device); + 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 device_read(DEVICE* device, int n) +{ + SAMPLE* sp = cur_sample(device); + BUFFER buffer; + buffer.length = 0; + snprintf(buffer.body, sizeof(buffer.body), + "READ %c%c%c%c %s %.6f %10llu %8.2f\r\n", + device->state == counter_idle ? 'I' : + device->state == counter_stopped ? 'S' : + device->state == counter_running ? 'R' : + device->state == counter_paused ? 'P' : '?', + device->terminal_due ? 'T' : ' ', + device->range_error == 0 ? ' ' : 'R', + device->range_gated ? 'G' : ' ', + make_timestamp(&sp->timestamp), + device->accumulated.tv_sec + .000001 * device->accumulated.tv_usec, + sp->counter_value, + sp->counter_rate); + buffer.length = strlen(buffer.body); + sock_send(n, &buffer); +} + +void device_print(DEVICE* device, FILE* fd) +{ + SAMPLE* sp = cur_sample(device); + 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 device_sample(DEVICE* device) +{ + SAMPLE* psp = cur_sample(device); + dbg_printf(0, "device_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", + device->sample_index, + psp->num_polls, + make_timestamp(&psp->timestamp), + psp->counter_value, + psp->count64, + psp->count_delta, + psp->time_delta, + psp->counter_rate); + device->sample_timer = device->current_time; + ++device->sample_counter; + + if (++device->sample_index >= SAMPLE_ARRAY_SZ) + device->sample_index = 0; + SAMPLE* sp = cur_sample(device); + *sp = *psp; + sp->valid = true; + sp->num_polls = 0; + sp->sample_counter = device->sample_counter; + sp->poll_counter = device->poll_counter; +} + +void device_report(DEVICE* device) +{ + dbg_printf(0, "device_report\n"); + /* + * Set the time for this report + */ + device->report_timer = device->current_time; + BUFFER buffer; + SAMPLE* sp; + sp = &device->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 device_init(DEVICE** cpp, char* name) +{ + int error = 0; + char errBuff[2048]={'\0'}; + SAMPLE* sp = NULL; + DEVICE* device = (DEVICE*) malloc(sizeof(DEVICE)); + *cpp = device; + memset(device, 0, sizeof(DEVICE)); + strncpy(device->name, name, sizeof(device->name)); + device->params.poll_period = 1000; /* milliseconds between polls */ + device->params.sample_period = 10; /* polls between sample calcs */ + device->params.report_period = 3; /* samples between reports */ + device->state = counter_stopped; + struct timeval now; + gettimeofday(&now, NULL); + device->current_time = now; + device->previous_time = now; + device->sample_timer = now; + device->report_timer = now; + HWARE_TEST(hware_ctor(name, &device->private_data)); + sp = cur_sample(device); + sp->timestamp = now; + sp->counter_value = device->current_count; + sp->valid = true; + device_sample(device); + return 0; +Error: + hware_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 device_start(DEVICE *device) +{ + int error = 0; + char errBuff[2048]={'\0'}; + struct timeval now; + int value; + SAMPLE* sp = NULL; + SAMPLE* psp = NULL; + PARAMETERS* pp = &device->params; + /* start the counter object */ + gettimeofday(&now, NULL); + device->current_time = now; + device->start_time = device->current_time; + device->current_count = device->params.initial_count; + device->accumulated.tv_sec = 0; + device->accumulated.tv_usec = 0; + device->poll_counter = 0; + device->sample_counter = 0; + device->terminal_due = false; + device->state = counter_running; + device->previous_time = device->current_time; + dbg_printf(0, "Setting input line to %d\n", pp->source); + HWARE_TEST(hware_source(device->private_data, pp->source)); + device->source = pp->source; + HWARE_TEST(hware_read(device->private_data, &device->count64)); + sp = cur_sample(device); + sp->timestamp = device->current_time; + sp->counter_value = device->current_count; + psp = prv_sample(device, 1); + psp->timestamp = device->current_time; + psp->counter_value = device->current_count; + make_report(device); + if (pp->output_line == 1) + value = 1; + else if (pp->output_line == 2) + value = 0; + else + value = -1; + dbg_printf(0, "Setting output line to %d\n", value); + HWARE_TEST(hware_outp(device->private_data, value)); + return error; +Error: + device_errmsg(errBuff, sizeof(errBuff)); + printf("DAQmx Error: %s\n", errBuff); + return error; +} + +int device_stop(DEVICE *device) +{ + int error = 0; + char errBuff[2048]={'\0'}; + PARAMETERS* pp = &device->params; + int value; + device->stop_time = device->current_time; + device->state = counter_stopped; + if (pp->output_line == 1) + value = 0; + else if (pp->output_line == 2) + value = 1; + else + value = -1; + dbg_printf(0, "Setting output line to %d\n", value); + HWARE_TEST(hware_outp(device->private_data, value)); + dbg_printf(0, "Setting input line to %d\n", pp->source); + HWARE_TEST(hware_source(device->private_data, pp->source)); + device->source = pp->source; + return error; +Error: + device_errmsg(errBuff, sizeof(errBuff)); + printf("DAQmx Error: %s\n", errBuff); + return error; +} + +int device_pause(DEVICE *device) +{ + if (device->state == counter_running) + device->state = counter_paused; + return 0; +} + +int device_resume(DEVICE *device) +{ + if (device->state == counter_paused) + device->state = counter_running; + return 0; +} + +int device_command(void* counter, const char* command) +{ + DEVICE* device = static_cast(counter); + if (strncasecmp(command, "pause", 5) == 0) + return device_pause(device); + else if (strncasecmp(command, "continue", 8) == 0) + return device_resume(device); + else if (strncasecmp(command, "resume", 6) == 0) + return device_resume(device); + else if (strncasecmp(command, "start", 6) == 0) + return device_start(device); + else if (strncasecmp(command, "stop", 4) == 0) + return device_stop(device); + return 0; +} + +static void device_event(DEVICE *device, char* event) +{ + BUFFER buffer; + sprintf(buffer.body, "EVENT %s %s\r\n", + event, + make_timestamp(&device->current_time)); + buffer.length = strlen(buffer.body); + dbg_printf(0, "%s", buffer.body); + //sock_report(&buffer, 0); + sock_report(&buffer, 1); + sock_report(&buffer, 2); +} + +static void device_range_check(DEVICE* device, int mode) +{ + PARAMETERS* pp = &device->params; + if (pp->range_check_enable) + { + if (pp->range_mode == mode) + { + double test; + SAMPLE* sp = cur_sample(device); + if (mode == 1) + test = sp->counter_rate; + else if (mode == 2) + test = sp->counter_rate; + else + test = device->report.average_rate; + if (pp->range_low > 0 && pp->range_low > test) + { + if (device->range_error != 1) + device_event(device, "RANGE OUT LOW"); + device->range_error = 1; + if (pp->range_gate_enable) + device->range_gated = true; + else + device->range_gated = false; + } + else if (pp->range_high > 0 && pp->range_high < test) + { + if (device->range_error != 2) + device_event(device, "RANGE OUT HIGH"); + device->range_error = 2; + if (pp->range_gate_enable) + device->range_gated = true; + else + device->range_gated = false; + } + else + { + if (device->range_error != 0) + device_event(device, "RANGE IN"); + device->range_error = 0; + device->range_gated = false; + } + } + } + else + { + /* If range check has been disabled while in error - reset */ + if (device->range_error != 0) + device_event(device, "RANGE IN"); + device->range_error = 0; + device->range_gated = false; + } +} + +static void device_test_term(DEVICE* device) +{ + PARAMETERS* pp = &device->params; + SAMPLE* sp = cur_sample(device); + SAMPLE* psp = prv_sample(device, 1); + + if (!device->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)) + { + device->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 + ) + { + device->terminal_due = true; + } + } + } + else if (pp->terminal_check_type == 2) + { + if ((uint64) device->accumulated.tv_sec >= pp->terminal_count) + device->terminal_due = true; + } + if (device->terminal_due) + { + device_event(device, "TERMINAL"); + make_report(device); + device_stop(device); + } + } +} + +/* + * poll the physical counter + */ +int device_poll(DEVICE* device) +{ + PARAMETERS* pp = &device->params; + char errBuff[2048]={'\0'}; + unsigned long long current_count_local; + int count_delta_local; + int error=0; + SAMPLE* sp = NULL; + SAMPLE* psp = NULL; + + /* read the value from the hardware counter to a temp */ + ++device->poll_counter; + HWARE_TEST(hware_read(device->private_data, ¤t_count_local)); + dbg_printf(0, "device_poll = %llu @ %s\n", + current_count_local, + make_timestamp(&device->current_time)); + + sp = cur_sample(device); + psp = prv_sample(device, 1); + + /* calculate the number since last time and save new value */ + count_delta_local = current_count_local - device->count64; + device->count64 = current_count_local; + sp->num_polls += 1; + /* + * If the counter is running and not gated increment the count and runtime + */ + if (device->state == counter_running && + !(device->params.range_gate_enable && device->range_gated)) + { + if (device->params.direction == COUNT_DOWN) + { + device->current_count -= count_delta_local; + } + else + { + device->current_count += count_delta_local; + } + /* + * Add the time difference to the accumulated time + */ + device->accumulated.tv_sec += device->current_time.tv_sec - sp->timestamp.tv_sec; + /* prevent negative tv_usec by borrowing one second in microseconds */ + device->accumulated.tv_usec += 1000000; + device->accumulated.tv_usec += device->current_time.tv_usec; + device->accumulated.tv_usec -= sp->timestamp.tv_usec; + if (device->accumulated.tv_usec >= 1000000) + { + /* carry the seconds */ + device->accumulated.tv_sec += device->accumulated.tv_usec / 1000000; + device->accumulated.tv_usec %= 1000000; + } + /* pay back the borrowed second */ + device->accumulated.tv_sec -= 1; + } + + /* calculate and check the count-rate between polls */ + sp->count_delta = device->count64 - sp->count64; + sp->time_delta = time_diff(&device->current_time, &sp->timestamp); + sp->counter_rate = (double) sp->count_delta / sp->time_delta; + device_range_check(device, 2); /* poll range check */ + + device->previous_time = device->current_time; + + /* save counter values in the sample */ + sp->counter_value = device->current_count; + sp->count64 = device->count64; + sp->timestamp = device->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 */ + device_test_term(device); + + /* check if it is time to roll the sample */ + if (device_time_to_next_sample(device) <= 0) + { + /* check if it is time to roll the report */ + if (device_time_to_next_report(device) <= 0) + { + make_report(device); + device_range_check(device, 0); /* report range check */ + device_report(device); + } + device_range_check(device, 1); /* sample range check */ + device_sample(device); + } + + /* set the source line */ + if (device->source != pp->source) + { + device->source = pp->source; + HWARE_TEST(hware_source(device->private_data, device->source)); + } + + /* drive the output line */ + if (device->output_line != pp->output_line) + { + int value; + device->output_line = pp->output_line; + if (pp->output_line == 1) + { + if (device->state == counter_running || device->state == counter_paused) + value = 1; + else + value = 0; + } + else if (pp->output_line == 2) + { + if (device->state == counter_running || device->state == counter_paused) + value = 0; + else + value = 1; + } + else + value = -1; + HWARE_TEST(hware_outp(device->private_data, value)); + } + return error; +Error: + device_errmsg(errBuff, sizeof(errBuff)); + printf("DAQmx Error: %s\n", errBuff); + return error; +} + +void device_term(DEVICE* device) +{ + if (device->private_data) + { + hware_dtor(&device->private_data); + device->private_data = NULL; + } + device->state = counter_idle; +} + +bool device_fatal(int error) +{ + return hware_failed(error); +} + +void device_errmsg(char* buff, int len) +{ + hware_errmsg(buff, len); +} + +double device_time_to_next_report(DEVICE* device) +{ + uint64 last_report; + uint64 timeofday; + int timeout; + struct timeval now; + now = device->current_time; + last_report = 1000 * (uint64) device->report_timer.tv_sec; + last_report += (uint64) device->report_timer.tv_usec / 1000; + timeofday = 1000 * (uint64) now.tv_sec; + timeofday += (uint64) now.tv_usec / 1000; + timeout = device->params.poll_period * device->params.sample_period * + device->params.report_period; + if ((last_report / timeout) != (timeofday / timeout)) + return 0.0; + timeout = timeout - timeofday % timeout; + return 0.001 * timeout; +} + +double device_time_to_next_sample(DEVICE* device) +{ + uint64 last_sample; + uint64 timeofday; + int timeout; + struct timeval now; + now = device->current_time; + last_sample = 1000 * (uint64) device->sample_timer.tv_sec; + last_sample += (uint64) device->sample_timer.tv_usec / 1000; + timeofday = 1000 * (uint64) now.tv_sec; + timeofday += (uint64) now.tv_usec / 1000; + timeout = device->params.poll_period * device->params.sample_period; + if ((last_sample / timeout) != (timeofday / timeout)) + return 0.0; + timeout = timeout - timeofday % timeout; + return 0.001 * timeout; +} diff --git a/site_ansto/hardsup/Monitor/device.h b/site_ansto/hardsup/Monitor/device.h new file mode 100644 index 00000000..49e3b1f2 --- /dev/null +++ b/site_ansto/hardsup/Monitor/device.h @@ -0,0 +1,122 @@ +#ifndef _DEVICE_H_ +#define _DEVICE_H_ + +#define MAX_DEVICES 8 + +#define SAMPLE_ARRAY_SZ 1000 + +#include "utility.h" +#include "params.h" + +typedef enum counter_state_t +{ + /** The counter has not yet been created or has been destroyed */ + counter_idle = 0, + /** The counter has not yet been started or has been stopped */ + counter_stopped, + /** The counter is counting */ + counter_running, + /** the counter has been paused */ + counter_paused +} DEVICE_STATE; + +/** + * Logical counter sample + */ +typedef struct sample_t +{ + /** sample number */ + int sample_counter; + /** poll number */ + int poll_counter; + /** time of last read */ + struct timeval timestamp; + /** logical counter value */ + uint64 counter_value; + /** extended physical counter value */ + uint64 count64; + /** counts between current and previous */ + int count_delta; + /** number of polls */ + int num_polls; + /** this data is valid */ + bool valid; + /** time between current and previous */ + double time_delta; + /** computed */ + double counter_rate; + /** computed */ + double average_rate; + /** computed */ + double minimum_rate; + /** computed */ + double maximum_rate; +} SAMPLE, *pSAMPLE; + +typedef struct counter_t +{ + char name[64]; + DEVICE_STATE state; + /** time of last start */ + struct timeval start_time; + /** time of last stop */ + struct timeval stop_time; + /** time of this read */ + struct timeval current_time; + /** time of last read */ + struct timeval previous_time; + /** time of next sample closure */ + struct timeval sample_timer; + /** time of next report generation */ + struct timeval report_timer; + /** accumulated runtime */ + struct timeval accumulated; + /** Current value of logical 64-bit counter */ + uint64 current_count; + /** an array of samples to be used for reporting */ + SAMPLE sample_array[SAMPLE_ARRAY_SZ]; + /** calculated values for reporting */ + SAMPLE report; + /** index into the sample array of the current sample */ + int sample_index; + /** number of polls */ + int poll_counter; + /** number of samples */ + int sample_counter; + /** is a terminal count exception due */ + bool terminal_due; + /** error: 0:none, 1:low, 2:high */ + int range_error; + /** is a range exception gate active */ + bool range_gated; + /** Extended physical counter value */ + uint64 count64; + /** Control parameters */ + PARAMETERS params; + /** active value of parameter source */ + int source; + /** active value of parameter output_line */ + int output_line; + struct device_private_t* private_data; +} DEVICE, *pDEVICE; + +void make_report(DEVICE* device); +void device_sample(DEVICE* device); +void device_send(DEVICE* device, int n); +void device_read(DEVICE* device, int n); +void device_print(DEVICE* device, FILE* fd); +void device_report(DEVICE* device); +int device_init(DEVICE** cpp, char* name); +int device_start(DEVICE* device); +int device_stop(DEVICE* device); +int device_pause(DEVICE* device); +int device_resume(DEVICE* device); +int device_command(void* device, const char* cmd); +int device_poll(DEVICE* device); +void device_term(DEVICE* device); +bool device_fatal(int error); +void device_errmsg(char* buff, int len); +double device_time_to_next_sample(DEVICE* device); +double device_time_to_next_report(DEVICE* device); + +#endif