diff --git a/site_ansto/hardsup/Monitor/Makefile b/site_ansto/hardsup/Monitor/Makefile new file mode 100644 index 00000000..a3d14e68 --- /dev/null +++ b/site_ansto/hardsup/Monitor/Makefile @@ -0,0 +1,34 @@ +SHELL = /bin/sh + +LIBS = nidaqmx +LIBFLAGS = -l$(LIBS) +TARGET = Monitor + +OBJS = $(TARGET).o params.o utility.o sock.o display.o cntr.o hctr.o + +CDEBUG = -ggdb3 -Wall +LDFLAGS += -g + +CFLAGS += $(CDEBUG) + +all: $(TARGET) + +clean: + rm -f $(OBJS) $(TARGET) core + +$(TARGET) : $(OBJS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBFLAGS) -ggdb3 + +$(TARGET).o: $(TARGET).c Monitor.h params.h utility.h sock.h cntr.h + +cntr.o: cntr.c Monitor.h cntr.h params.h sock.h + +display.o: display.c Monitor.h display.h utility.h params.h sock.h cntr.h + +hctr.o: hctr.c hctr.h + +params.o: params.c Monitor.h params.h sock.h + +sock.o: sock.c Monitor.h sock.h utility.h display.h cntr.h + +utility.o: utility.c Monitor.h utility.h diff --git a/site_ansto/hardsup/Monitor/Monitor.c b/site_ansto/hardsup/Monitor/Monitor.c new file mode 100644 index 00000000..bced8345 --- /dev/null +++ b/site_ansto/hardsup/Monitor/Monitor.c @@ -0,0 +1,168 @@ +/********************************************************************* + * + * ANSI C program: + * Monitor.c + * + * Description: + * This program counts digital events on a NI-6602 + * Counter Input Channel. The Device, Counter Channel, Initial Count, Count + * Direction, and are all configurable. + * + * Edges are counted on the counter's default input terminal (refer + * to the I/O Connections Overview section below for more + * information), but could easily be modified to count edges on a + * PFI or RTSI line. + * + * Instructions for Running: + * 1. Select the Device and Channel which corresponds to the counter + * you want to count on. + * 2. Select the TCP/IP port number you want to listen on. + * 3. Enter the Initial Count, Count Direction, to specify how you want + * the counter to count. + * + * Steps: + * 1. Create a task. + * 2. Create a Counter Input channel to Count Events. The Edge + * parameter is used to determine if the counter will increment + * on rising or falling edges. + * 3. Call the Start function to arm the counter and begin + * counting. The counter will be preloaded with the Initial + * Count. + * 4. The counter will be continually polled. + * 5. Call the Clear Task function to clear the Task. + * 6. Display an error if any. + * + *********************************************************************/ + +#define CNTR_CHK(func) if (cntr_fatal(error=(func))) goto Error; else + +#include "Monitor.h" +#include "utility.h" +#include "params.h" +#include "sock.h" +#include "cntr.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int usage(int argc, char* argv[]) +{ + fprintf(stderr, "usage: %s \n", + argv[0]); + return EXIT_FAILURE; +} + +int main(int argc, char* argv[]) +{ + int error=0; + char errBuff[2048]={'\0'}; + struct timeval now; + uint64 last_poll; + char* device = DEFAULT_COUNTER_DEVICE; + int port = DEFAULT_LISTEN_PORT; + int idx = 1; + + if (idx >= argc) + return usage(argc, argv); + if (argv[idx][0] == '-') + { + if (tolower(argv[idx][1]) == 'd') + { + if (isdigit(argv[idx][2])) + set_debug_level(argv[idx][2] - '0'); + else + set_debug_level(0); + } + ++idx; + } + + if (idx >= argc) + return usage(argc, argv); + if (tolower(argv[idx][0]) == 'd' && + tolower(argv[idx][1]) == 'e' && + tolower(argv[idx][2]) == 'v' && + isdigit(argv[idx][3])) + { + device = argv[idx]; + ++idx; + } + else + return usage(argc, argv); + + if (idx >= argc) + return usage(argc, argv); + if (isdigit(argv[idx][0])) + { + port = atoi(argv[idx]); + ++idx; + } + else + return usage(argc, argv); + + memset(&counter, 0, sizeof(counter)); + + CNTR_CHK(cntr_init(&counter, device)); +// CNTR_CHK(cntr_start(&counter)); + sock_init(port); + + printf("Continuously polling. Press Ctrl+C to interrupt\n"); + gettimeofday(&now, NULL); + last_poll = 1000 * (uint64) now.tv_sec + now.tv_usec / 1000; + while (1) + { + COUNTER* cp = &counter; + PARAMETERS* pp = &cp->params; + uint64 timeofday; + int timeout = 0; + do { + sock_check(timeout); + gettimeofday(&now, NULL); + timeofday = 1000 * (uint64) now.tv_sec + now.tv_usec / 1000; + if (timeofday / pp->poll_period > last_poll / pp->poll_period) + timeout = 0; + else + { + timeout = pp->poll_period - timeofday % pp->poll_period; + if (timeout < 0) + { + if (timeout < 0) + dprintf(0, "Poll timeout < 0 at %d\n", timeout); + timeout = 0; + } + } + //dprintf(0, "Poll timeout = %d\n", timeout); + } while (timeout > 0); + cp->current_time = now; +#if 1 + dprintf(0, "-%s %s %.3f %s %.3f %4d\n", + make_timestamp(&cp->current_time), + make_timestamp(&cp->sample_timer), + cntr_time_to_next_sample(cp), + make_timestamp(&cp->report_timer), + cntr_time_to_next_report(cp), + cp->sample_index); +#endif + CNTR_CHK(cntr_poll(cp)); + last_poll = timeofday; + } + +Error: + puts(""); + if (cntr_fatal(error)) + { + cntr_errmsg(errBuff, sizeof(errBuff)); + printf("DAQmx Error: %s\n", errBuff); + } + cntr_term(&counter); + printf("End of program\n"); + return 0; +} diff --git a/site_ansto/hardsup/Monitor/Monitor.h b/site_ansto/hardsup/Monitor/Monitor.h new file mode 100644 index 00000000..177a3ffb --- /dev/null +++ b/site_ansto/hardsup/Monitor/Monitor.h @@ -0,0 +1,11 @@ +/* + * Monitor.h + * + */ +#ifndef _MONITOR_H_ +#define _MONITOR_H_ + +#define DEFAULT_COUNTER_DEVICE "DEV1/CTR0" +#define DEFAULT_LISTEN_PORT 3000 + +#endif diff --git a/site_ansto/hardsup/Monitor/cntr.c b/site_ansto/hardsup/Monitor/cntr.c new file mode 100644 index 00000000..0e54e308 --- /dev/null +++ b/site_ansto/hardsup/Monitor/cntr.c @@ -0,0 +1,584 @@ +/* + * Abstraction of the counter device. + * + */ +#include "cntr.h" +#include "params.h" +#include "sock.h" +#include "hctr.h" +#include +#include +#include + +#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; + /* 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)); + make_report(cp); + return error; +Error: + cntr_errmsg(errBuff, sizeof(errBuff)); + printf("DAQmx Error: %s\n", errBuff); + return error; +} + +int cntr_stop(COUNTER *cp) +{ + cp->stop_time = cp->current_time; + cp->state = counter_stopped; + return 0; +} + +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 + */ + 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 + */ + 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 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) +{ + 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, ¤t_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); + } + 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; +} diff --git a/site_ansto/hardsup/Monitor/cntr.h b/site_ansto/hardsup/Monitor/cntr.h new file mode 100644 index 00000000..a444999d --- /dev/null +++ b/site_ansto/hardsup/Monitor/cntr.h @@ -0,0 +1,116 @@ +#ifndef _COUNTER_H_ +#define _COUNTER_H_ + +#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 +} COUNTER_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]; + COUNTER_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; + struct counter_private_t* private_data; +} COUNTER, *pCOUNTER; + +extern COUNTER counter; + +void make_report(COUNTER* cp); +void cntr_sample(COUNTER* cp); +void cntr_send(COUNTER* cp, int n); +void cntr_read(COUNTER* cp, int n); +void cntr_print(COUNTER* cp, FILE* fd); +void cntr_report(COUNTER* cp); +int cntr_init(COUNTER* cp, char* name); +int cntr_start(COUNTER *cp); +int cntr_stop(COUNTER *cp); +int cntr_pause(COUNTER *cp); +int cntr_resume(COUNTER *cp); +int cntr_poll(COUNTER* cp); +void cntr_term(COUNTER* cp); +bool cntr_fatal(int error); +void cntr_errmsg(char* buff, int len); +double cntr_time_to_next_sample(COUNTER* cp); +double cntr_time_to_next_report(COUNTER* cp); +#endif diff --git a/site_ansto/hardsup/Monitor/display.c b/site_ansto/hardsup/Monitor/display.c new file mode 100644 index 00000000..6de0a481 --- /dev/null +++ b/site_ansto/hardsup/Monitor/display.c @@ -0,0 +1,696 @@ +#include "display.h" +#include "utility.h" +#include "params.h" +#include "sock.h" +#include "cntr.h" + +#include + +#define MIN_REFRESH 5 +#define MAX_REFRESH 30 + +/** + * Add a drop down selection box in a two-column table row + * + * \param buffer where the output is appended + * \param title text to go in column one + * \param name name of the selection object + * + * The browser will return name=value for the selection, using the name from + * the selection box and the value from the option selected. + */ +static void add_select_begin(BUFFER* buffer, char* title, char* name) +{ + char* bp = &buffer->body[buffer->length]; + snprintf(bp, sizeof(buffer->body) - buffer->length, + "%s\r\n" + "\r\n"); + buffer->length += strlen(bp); +} + +/** + * Add arbitrary text to a buffer and adjust the length + * + * \param buffer where the text is appended + */ +static void add_text(BUFFER* buffer, char* text) +{ + char* bp = &buffer->body[buffer->length]; + snprintf(bp, sizeof(buffer->body) - buffer->length, "%s", text); + buffer->length += strlen(bp); +} + +/** + * Add a two-column row for an integer + * + * \param buffer where the text is appended + * \param title is displayed in column one + * \param name is returned by the browser + * \param value is displayed in column two and returned by the browser + * + * The title is displayed in column one and the integer in column two. The + * name=value is returned by the browser. + */ +static void add_int(BUFFER* buffer, char* title, char* name, int value) +{ + char* bp = &buffer->body[buffer->length]; + snprintf(bp, sizeof(buffer->body) - buffer->length, + "%s" + "" + "\r\n", + title, + name, + value); + buffer->length += strlen(bp); +} + +/** + * Add a two-column row for a double floating point + * + * \param buffer where the text is appended + * \param title is displayed in column one + * \param name is returned by the browser + * \param value is displayed in column two and returned by the browser + * + * The title is displayed in column one and the double in column two. The + * name=value is returned by the browser. + */ +static void add_double(BUFFER* buffer, char* title, char* name, double value) +{ + char* bp = &buffer->body[buffer->length]; + snprintf(bp, sizeof(buffer->body) - buffer->length, + "%s" + "" + "\r\n", + title, + name, + value); + buffer->length += strlen(bp); +} + +/** + * Add a two-column row for a counter value + * + * \param buffer where the text is appended + * \param title is displayed in column one + * \param name is returned by the browser + * \param value is displayed in column two and returned by the browser + * + * The title is displayed in column one and the counter value in column two. + * The name=value is returned by the browser. + */ +static void add_counter(BUFFER* buffer, char* title, char* name, uint64 value) +{ + char* bp = &buffer->body[buffer->length]; + snprintf(bp, sizeof(buffer->body) - buffer->length, + "%s" + "" + "\r\n", + title, + name, + value); + buffer->length += strlen(bp); +} + +/** + * Display the counter control form on socket + * + * \param n index of socket + * + * The counter control form has fields to display and change counter control + * parameters. It contains buttons to update the counter parameters or reset + * them to the last displayed values. + */ +void put_form(int n) +{ + dprintf(0, "put_form\n"); + BUFFER html; + BUFFER buffer; + PARAMETERS* pp = &counter.params; + char *bp; + buffer.length = 0; + bp = &buffer.body[buffer.length]; + snprintf(bp, sizeof(buffer.body) - buffer.length, + "Counter Form\r\n" + "\r\n" + "
\r\n" + "\r\n" + "\r\n" + "\r\n"); + buffer.length += strlen(bp); + add_select_begin(&buffer, "Direction", "direction"); + add_option(&buffer, "Up", "up", pp->direction == COUNT_UP); + add_option(&buffer, "Down", "down", !pp->direction == COUNT_UP); + add_select_end(&buffer); + add_int(&buffer, "Scan", "scan", pp->poll_period); + add_int(&buffer, "Sample", "sample", pp->sample_period); + add_int(&buffer, "Report", "report", pp->report_period); + add_counter(&buffer, "Initial", "initial", pp->initial_count); + add_counter(&buffer, "Terminal", "terminal", pp->terminal_count); + add_select_begin(&buffer, "Terminal Event", "te_check"); + add_option(&buffer, "None", "none", pp->terminal_check_type == 0); + add_option(&buffer, "Counter", "counter", pp->terminal_check_type == 1); + add_option(&buffer, "Timer", "timer", pp->terminal_check_type == 2); + add_select_end(&buffer); + add_double(&buffer, "Range Low", "range_low", pp->range_low); + add_double(&buffer, "Range High", "range_high", pp->range_high); + add_select_begin(&buffer, "Range Event", "re_check"); + add_option(&buffer, "Enabled", "enabled", pp->range_check_enable == true); + add_option(&buffer, "Disabled", "Disabled", pp->range_check_enable == false); + add_select_end(&buffer); + add_select_begin(&buffer, "Range Gate", "range_gate"); + add_option(&buffer, "Enabled", "enabled", pp->range_gate_enable == true); + add_option(&buffer, "Disabled", "Disabled", pp->range_gate_enable == false); + add_select_end(&buffer); + add_select_begin(&buffer, "Range Mode", "range_mode"); + add_option(&buffer, "Report", "0", pp->range_mode == 0); + add_option(&buffer, "Sample", "1", pp->range_mode == 1); + add_option(&buffer, "Poll", "2", pp->range_mode == 2); + add_select_end(&buffer); + bp = &buffer.body[buffer.length]; + snprintf(bp, sizeof(buffer.body) - buffer.length, + "
FieldValue
\r\n" + "\r\n" + "" + "\r\n" + "
\r\n" + "
\r\n" + "\r\n" + "\r\n" ); + buffer.length += strlen(bp); + bp = &buffer.body[buffer.length]; + html.length = 0; + bp = &html.body[html.length]; + snprintf(bp, sizeof(html.body) - html.length, + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/html\r\n" + "Content-Length: %d\r\n" + "\r\n", + buffer.length); + strcat(html.body, buffer.body); + html.length = strlen(html.body); + sock_send(n, &html); +} + +static void show_text(BUFFER* buffer, char* title, char* value) +{ + char* bp = &buffer->body[buffer->length]; + snprintf(bp, sizeof(buffer->body) - buffer->length, + "%s" + "%s" + "\r\n", + title, + value); + buffer->length += strlen(bp); +} + +static void show_int(BUFFER* buffer, char* title, int value) +{ + char* bp = &buffer->body[buffer->length]; + snprintf(bp, sizeof(buffer->body) - buffer->length, + "%s" + "%d" + "\r\n", + title, + value); + buffer->length += strlen(bp); +} + +static void show_real(BUFFER* buffer, char* title, double value) +{ + char* bp = &buffer->body[buffer->length]; + snprintf(bp, sizeof(buffer->body) - buffer->length, + "%s" + "%.2f" + "\r\n", + title, + value); + buffer->length += strlen(bp); +} + +static void show_counter(BUFFER* buffer, char* title, unsigned long long value) +{ + char* bp = &buffer->body[buffer->length]; + snprintf(bp, sizeof(buffer->body) - buffer->length, + "%s" + "%llu" + "\r\n", + title, + value); + buffer->length += strlen(bp); +} + +static void show_time(BUFFER* buffer, + char* title, + struct timeval* value, + bool days_flag) +{ + char time_str[40]; + time_str[0] = '\0'; + if (days_flag) + { + snprintf(time_str, sizeof(time_str), + "%ld ", + value->tv_sec / (24 * 3600)); + } + strcat(time_str, make_timestamp(value)); + show_text(buffer, title, time_str); +} + +/** + * Display the counter display page on socket + * + * \param n index of socket + * + * The counter display page shows the state of the counter and contains + * buttons which allow commands to be issued to the counter. It will be + * periodically refreshed. + */ +void put_page(int n) +{ + dprintf(0, "put_page\n"); + BUFFER html; + BUFFER buffer; + COUNTER* cp = &counter; + PARAMETERS* pp = &counter.params; + SAMPLE* sp; + char *bp; + int refresh; +#if 1 + { + refresh = cntr_time_to_next_report(cp) + 1; + } +#else + make_report(cp); + refresh = (pp->poll_period * pp->sample_period * pp->report_period + 500) / 1000; +#endif + if (refresh < MIN_REFRESH) + refresh = MIN_REFRESH; + if (refresh > MAX_REFRESH) + refresh = MAX_REFRESH; + sp = &cp->report; + buffer.length = 0; + bp = &buffer.body[buffer.length]; + snprintf(bp, sizeof(buffer.body) - buffer.length, + "Counter Page\r\n" + "\r\n" + "\r\n"); + add_text(&buffer, "
\r\n" + "\r\n" + "\r\n" + "\r\n"); + buffer.length += strlen(bp); + show_text(&buffer, "State", cp->state == counter_stopped ? "STOPPED" : + cp->state == counter_running ? "RUNNING" : + cp->state == counter_paused ? "PAUSED" : "IDLE"); + show_text(&buffer, "Direction", pp->direction == COUNT_UP ? "UP" : "DOWN"); + show_int(&buffer, "Scan", pp->poll_period); + show_int(&buffer, "Sample", pp->sample_period); + show_int(&buffer, "Report", pp->report_period); + show_time(&buffer, "Start Time", &cp->start_time, false); + show_time(&buffer, "Current Time", &cp->current_time, false); + { + struct timeval tv = cp->stop_time; + if (cp->state == counter_running || cp->state == counter_paused) + { + tv = cp->current_time; + } + time_sub(&tv, &cp->start_time); + show_time(&buffer, "Elapsed", &tv, true); + } + show_time(&buffer, "Runtime", &cp->accumulated, true); + show_time(&buffer, "Report Time", &sp->timestamp, false); + show_counter(&buffer, "Counter", sp->counter_value); + show_int(&buffer, "Num Polls", sp->num_polls); + show_int(&buffer, "Poll Number", sp->poll_counter); + show_int(&buffer, "Sample Number", sp->sample_counter); + show_real(&buffer, "Rate", sp->counter_rate); + show_real(&buffer, "Min", sp->minimum_rate); + show_real(&buffer, "Ave", sp->average_rate); + show_real(&buffer, "Max", sp->maximum_rate); + if (pp->range_check_enable) + { + char* result; + if (cp->range_error == 1) + result = "LOW"; + else if (cp->range_error == 2) + result = "HIGH"; + else + result = "OK"; + show_text(&buffer, "Range Check", result); + } + else + show_text(&buffer, "Range Check", "Disabled"); + add_text(&buffer, "
FieldValue
\r\n"); + add_text(&buffer, "\r\n"); + add_text(&buffer, "\r\n"); + add_text(&buffer, "\r\n"); + add_text(&buffer, "\r\n"); + add_text(&buffer, "\r\n"); + add_text(&buffer, "
\r\n" + "\r\n" + "
\r\n" + "\r\n" + "
\r\n" + "\r\n" + "
\r\n" + "\r\n" + "
\r\n" + "\r\n" + "
\r\n" + "\r\n" + "\r\n"); + html.length = 0; + bp = &html.body[html.length]; + snprintf(bp, sizeof(html.body) - html.length, + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/html\r\n" + "Content-Length: %d\r\n" + "Refresh: %d\r\n" + "\r\n", + buffer.length, + refresh); + strcat(html.body, buffer.body); + html.length = strlen(html.body); + sock_send(n, &html); +} + +/** + * Process the control form in the buffer on the socket + * + * \param n index of socket + * \param bp buffer containing form input + * + * The form is sent by a browser and contains name=value pairs which are used + * to set the counter parameters. + */ +void process_form(int n, BUFFER* bp) +{ + dprintf(0, "process_form\n"); + int i, j; + int state; + char name[80]; + char value[80]; + char hex_str[3]; + unsigned int hex_val; + char* cp; + state = 0; + j = 0; + for (i = 0; i < bp->length; ++i) + { + cp = &bp->body[i]; + if (state == 0) + { + if (*cp == '=') + { + name[j++] = '\0'; + j = 0; + state = 1; + } + else if (j < 80 - 1) + name[j++] = tolower(*cp); + } + else if (state == 1) + { + if (*cp == '&') + { + value[j++] = '\0'; + j = 0; + state = 9; + } + else if (*cp == '%') + { + hex_str[0] = '\0'; + state = 2; + } + else if (j < 80 - 1) + value[j++] = tolower(*cp); + } + else if (state == 2) + { + hex_str[0] = tolower(*cp); + state = 3; + } + else if (state == 3) + { + hex_str[1] = tolower(*cp); + hex_str[2] = '\0'; + hex_val = strtoul(hex_str, NULL, 16); + if (hex_val < ' ' || hex_val > '~') + hex_val = '?'; + if (j < 80 - 1) + value[j++] = tolower(hex_val); + state = 1; + } + if (state == 9) + { + dprintf(0, "name=\"%s\", value=\"%s\"\n", name, value); + if (param_set(&counter.params, name, value)) + ; + else if (strcmp(name, "xxx")) + { + } + state = 0; + } + } +} + +/** + * Cause the browser to refresh the page + * + * \param n index of socket + * + * Used after a command button to redirect to the display page. Uses + * HTTP 303 REDIRECT to get the browser to display the page. + */ +void put_page_refresh(int n) +{ + dprintf(0, "put_page_refresh\n"); + BUFFER html; + BUFFER buffer; + char* bp; + COUNTER* cp = &counter; + + int refresh; +#if 1 + { + refresh = cntr_time_to_next_report(cp) + 1; + } +#else + PARAMETERS* pp = &counter.params; + make_report(cp); + refresh = (pp->poll_period * pp->sample_period * pp->report_period + 500) / 1000; +#endif + if (refresh < MIN_REFRESH) + refresh = MIN_REFRESH; + if (refresh > MAX_REFRESH) + refresh = MAX_REFRESH; + + buffer.length = 0; + add_text(&buffer, "\r\n" + "\r\n" + "Title\r\n" + "\r\n" + "\r\n"); + add_text(&buffer, "\r\n" + "\r\n"); + html.length = 0; + bp = &html.body[html.length]; + snprintf(bp, sizeof(html.body) - html.length, + "HTTP/1.1 303 REDIRECT\r\n" + "Location: page\r\n" + "Content-Type: text/html\r\n" + "Content-Length: %d\r\n" + "\r\n", + buffer.length); + strcat(html.body, buffer.body); + html.length = strlen(html.body); + sock_send(n, &html); +} + +/** + * Cause the browser to refresh the form + * + * \param n index of socket + * + * Used after a command button to redirect to the control form. Uses + * HTTP 303 REDIRECT to get the browser to display the form. + */ +void put_form_refresh(int n) +{ + dprintf(0, "put_form_refresh\n"); + BUFFER html; + BUFFER buffer; + char* bp; + COUNTER* cp = &counter; + + int refresh; +#if 1 + { + refresh = cntr_time_to_next_report(cp) + 1; + } +#else + PARAMETERS* pp = &counter.params; + make_report(cp); + refresh = (pp->poll_period * pp->sample_period * pp->report_period + 500) / 1000; +#endif + if (refresh < MIN_REFRESH) + refresh = MIN_REFRESH; + if (refresh > MAX_REFRESH) + refresh = MAX_REFRESH; + + buffer.length = 0; + add_text(&buffer, "\r\n" + "\r\n" + "Title\r\n" + "\r\n" + "\r\n"); + add_text(&buffer, "\r\n" + "\r\n"); + html.length = 0; + bp = &html.body[html.length]; + snprintf(bp, sizeof(html.body) - html.length, + "HTTP/1.1 303 REDIRECT\r\n" + "Location: form\r\n" + "Content-Type: text/html\r\n" + "Content-Length: %d\r\n" + "\r\n", + buffer.length); + strcat(html.body, buffer.body); + html.length = strlen(html.body); + sock_send(n, &html); +} + +/** + * Process the command in the buffer on the socket + * + * \param n index of socket + * \param bp buffer containing command + * + * Handles non-browser commands like SICS commands. + */ +void process_command(int n, BUFFER* bp) +{ + dprintf(0, "process_command(%d, %s)\n", n, bp->body); + bool sics = false; + int error = 1; + char command[80]; + char param[80]; + int len = 0; + char* cp = bp->body; + while (isspace(*cp)) + ++cp; + len = 0; + while (*cp && !isspace(*cp)) + { + if (len < 80 - 1) + command[len++] = *cp; + ++cp; + } + command[len] = '\0'; + if (strcasecmp(command, "SICS") == 0) + { + sics = true; + while (isspace(*cp)) + ++cp; + len = 0; + while (*cp && !isspace(*cp)) + { + if (len < 80 - 1) + command[len++] = *cp; + ++cp; + } + command[len] = '\0'; + } + while (isspace(*cp)) + ++cp; + if (strcasecmp(command, "START") == 0) + error = cntr_start(&counter); + else if (strcasecmp(command, "STOP") == 0) + error = cntr_stop(&counter); + else if (strcasecmp(command, "PAUSE") == 0) + error = cntr_pause(&counter); + else if (strcasecmp(command, "RESUME") == 0) + error = cntr_resume(&counter); + else if (strcasecmp(command, "REPORT") == 0) + { + int match = 1; + error = 0; + if (sics) + match = 2; + len = 0; + while (*cp && !isspace(*cp)) + { + if (len < 80 - 1) + param[len++] = *cp; + ++cp; + } + param[len] = '\0'; + if (strcasecmp(param, "OFF") == 0) + match = 0; + sock_set_match(n, match); + } + else if (strcasecmp(command, "SET") == 0) + { + /* set parameter */ + dprintf(0, "SET %s\n", cp); + if (param_set_cmd(&counter.params, cp)) + error = 0; + } + else if (strcasecmp(command, "GET") == 0) + { + /* get parameter */ + param_get_cmd(&counter.params, cp, n); + return; + } + else if (strcasecmp(command, "READ") == 0) + { + cntr_read(&counter, n); + return; + } + else if (!sics) + { + cntr_send(&counter, n); + return; + } + if (error == 0) + sock_ok(n); + else + sock_err(n); +} diff --git a/site_ansto/hardsup/Monitor/display.h b/site_ansto/hardsup/Monitor/display.h new file mode 100644 index 00000000..f550d6d8 --- /dev/null +++ b/site_ansto/hardsup/Monitor/display.h @@ -0,0 +1,15 @@ +/* + * Processing of display interaction + */ +#ifndef _DISPLAY_H_ +#define _DISPLAY_H_ +#include "utility.h" + +void put_page(int n); +void put_form(int n); +void process_form(int n, BUFFER* bp); +void put_page_refresh(int n); +void put_form_refresh(int n); +void process_command(int n, BUFFER* bp); + +#endif diff --git a/site_ansto/hardsup/Monitor/hctr.c b/site_ansto/hardsup/Monitor/hctr.c new file mode 100644 index 00000000..e898c6fd --- /dev/null +++ b/site_ansto/hardsup/Monitor/hctr.c @@ -0,0 +1,105 @@ +#include "hctr.h" +#include +#include +#include + +#define DAQmxErrChk(functionCall) \ + if( DAQmxFailed(error=(functionCall)) ) \ + goto Error; \ + else + +/** + * This structure encapsulates the data that is private to + * the implementation of the NI DAQ counter interface + */ +typedef struct counter_private_t +{ + /** NIDAQ opaque task handle */ + TaskHandle taskHandle; + /** Actual physical counter value, as returned by NIDAQ + * read function */ + uInt32 count32; + /** extended 64-bit counter value */ + unsigned long long count64; +} COUNTER_PRIVATE; + +int hctr_ctor(const char* device_name, pHCTR* ptr) +{ + int error = 0; + *ptr = (COUNTER_PRIVATE*) malloc(sizeof(COUNTER_PRIVATE)); + memset(*ptr, 0, sizeof(COUNTER_PRIVATE)); + + /*********************************************/ + // Create a DAQmx task to hold the counter + /*********************************************/ + DAQmxErrChk (DAQmxCreateTask("",&(*ptr)->taskHandle)); + + /*********************************************/ + // Create a DAQmx counter within the task + /*********************************************/ + DAQmxErrChk ( + DAQmxCreateCICountEdgesChan((*ptr)->taskHandle, + device_name, + "", + DAQmx_Val_Rising, + (*ptr)->count32, + DAQmx_Val_CountUp)); + + /*********************************************/ + // Start the DAQmx task + /*********************************************/ + DAQmxErrChk (DAQmxStartTask((*ptr)->taskHandle)); + + return 0; +Error: + free(*ptr); + *ptr = NULL; + return error; +} + +int hctr_read(pHCTR hctr, unsigned long long* value) +{ + int error = 0; + uInt32 ctr; + DAQmxErrChk (DAQmxReadCounterScalarU32(hctr->taskHandle, + 1.0, + &ctr, + NULL)); + hctr->count64 += ctr - hctr->count32; + hctr->count32 = ctr; + *value = hctr->count64; + return 0; +Error: + return error; +} + +int hctr_dtor(pHCTR* hctr) +{ + if( hctr && *hctr && (*hctr)->taskHandle!=0 ) + { + /*********************************************/ + // DAQmx Stop Code + /*********************************************/ + DAQmxStopTask((*hctr)->taskHandle); + DAQmxClearTask((*hctr)->taskHandle); + } + (*hctr)->taskHandle = 0; + free (*hctr); + *hctr = NULL; + return 0; +} + +bool hctr_failed(int error) +{ + if (DAQmxFailed(error)) + return true; + else + return false; +} + +void hctr_errmsg(char* buff, int len) +{ + *buff = '\0'; + DAQmxGetExtendedErrorInfo(buff, len); +} + diff --git a/site_ansto/hardsup/Monitor/hctr.h b/site_ansto/hardsup/Monitor/hctr.h new file mode 100644 index 00000000..f45ae51e --- /dev/null +++ b/site_ansto/hardsup/Monitor/hctr.h @@ -0,0 +1,72 @@ +/* + * This is an encapsulation of a National Instruments counter. + * + * It presents a simple 64-bit counter abstraction. When the counter is + * created, it commences counting at zero until it is destroyed. + * + * The counter can be read and returns a 64-bit unsigned value. + */ +#ifndef _HCTR_H_ +#define _HCTR_H_ + +#include + +struct counter_private_t; +typedef struct counter_private_t* pHCTR; + +/** + * Create a 64-bit counter and start it counting + * + * \param device_name the name of the device (e.g. "dev1/ctr0") + * \param ptr address of pointer to opaque private data structure + * + * \return + * 0 OK + * !0 Error + */ +int hctr_ctor(const char* device_name, pHCTR* ptr); + +/** + * Read the value of the 64-bit counter + * + * \param hctr pointer to opaque private data structure + * \param value address of unsigned 64-bit value to receive the output + * + * \return + * 0 OK + * !0 Error + */ +int hctr_read(pHCTR hctr, unsigned long long* value); + +/** + * Destroy the 64-bit counter + * + * \param ptr address of pointer to opaque private data structure + * + * \return + * 0 OK + * !0 Error + */ +int hctr_dtor(pHCTR* hctr); + +/** + * Tests returned error value to see if it represents failure + * + * \param error a value returned from another hctr function + * + * \return + * true the error was a failure + * false the error was not a failure (warning) + */ +bool hctr_failed(int error); + +/** + * Retrieves a textual representation of the most recent error + * + * \param buff a pointer to the buffer to receive the text + * \param len the length of the provided buffer + */ +void hctr_errmsg(char* buff, int len); + +#endif + diff --git a/site_ansto/hardsup/Monitor/params.c b/site_ansto/hardsup/Monitor/params.c new file mode 100644 index 00000000..656d63f8 --- /dev/null +++ b/site_ansto/hardsup/Monitor/params.c @@ -0,0 +1,384 @@ +#include "params.h" +#include "sock.h" + +#include + +#define CMD_DIRECTION 1 +#define CMD_SCAN 2 +#define CMD_SAMPLE 3 +#define CMD_REPORT 4 +#define CMD_INITIAL 5 +#define CMD_TERMINAL 6 +#define CMD_RANGE_LOW 7 +#define CMD_RANGE_HIGH 8 +#define CMD_TE_CHECK 9 +#define CMD_RE_CHECK 10 +#define CMD_RANGE_GATE 11 +#define CMD_RANGE_MODE 12 + +#define TXT_DIRECTION "DIRECTION" +#define TXT_SCAN "SCAN" +#define TXT_SAMPLE "SAMPLE" +#define TXT_REPORT "REPORT" +#define TXT_INITIAL "INITIAL" +#define TXT_TERMINAL "TERMINAL" +#define TXT_RANGE_LOW "RANGE_LOW" +#define TXT_RANGE_HIGH "RANGE_HIGH" +#define TXT_TE_CHECK "TE_CHECK" +#define TXT_RE_CHECK "RE_CHECK" +#define TXT_RANGE_GATE "RANGE_GATE" +#define TXT_RANGE_MODE "RANGE_MODE" + +static struct param_command_t { + int cmd; + char* txt; +} param_command[] = { + {CMD_DIRECTION, TXT_DIRECTION}, + {CMD_SCAN, TXT_SCAN}, + {CMD_SAMPLE, TXT_SAMPLE}, + {CMD_REPORT, TXT_REPORT}, + {CMD_INITIAL, TXT_INITIAL}, + {CMD_TERMINAL, TXT_TERMINAL}, + {CMD_RANGE_LOW, TXT_RANGE_LOW}, + {CMD_RANGE_HIGH, TXT_RANGE_HIGH}, + {CMD_TE_CHECK, TXT_TE_CHECK}, + {CMD_RE_CHECK, TXT_RE_CHECK}, + {CMD_RANGE_GATE, TXT_RANGE_GATE}, + {CMD_RANGE_MODE, TXT_RANGE_MODE}, + {0, NULL} +}; + +bool param_set(pPARAMETERS pp, char* name, char* value) +{ + bool result = false; + switch (name[0]) + { + case 'd': + case 'D': + if (strcasecmp(name, TXT_DIRECTION) == 0) + { + result = true; + dprintf(0, "Direction=%s", pp->direction ? "Down" : "Up"); + if (strcasecmp(value, "down") == 0) + pp->direction = COUNT_DOWN; + else + pp->direction = COUNT_UP; + dprintf(0, "=>%s\n", pp->direction ? "Down" : "Up"); + } + break; + case 'i': + case 'I': + if (strcasecmp(name, TXT_INITIAL) == 0) + { + result = true; + dprintf(0, "Initial=%llu", pp->initial_count); + pp->initial_count = strtoull(value, NULL, 10); + dprintf(0, "=>%llu\n", pp->initial_count); + } + break; + case 'r': + case 'R': + if (strcasecmp(name, TXT_REPORT) == 0) + { + result = true; + dprintf(0, "Report=%d", pp->report_period); + pp->report_period = strtol(value, NULL, 10); + if (pp->report_period < 1) + pp->report_period = 1; + else if (pp->report_period > 1000) + pp->report_period = 1000; + dprintf(0, "=>%d\n", pp->report_period); + } + else if (strcasecmp(name, TXT_RANGE_MODE) == 0) + { + result = true; + dprintf(0, "range_mode=%d", pp->range_mode); + pp->range_mode = strtol(value, NULL, 10); + if (pp->range_mode < 0) + pp->range_mode = 0; + else if (pp->range_mode > 2) + pp->range_mode = 0; + dprintf(0, "=>%d\n", pp->range_mode); + } + else if (strcasecmp(name, TXT_RANGE_GATE) == 0) + { + result = true; + dprintf(0, "range_gate=%s", + pp->range_gate_enable ? "Enabled" : "Disabled"); + if (strcasecmp(value, "enabled") == 0) + pp->range_gate_enable = true; + else + pp->range_gate_enable = false; + dprintf(0, "=>%s\n", + pp->range_gate_enable ? "Enabled" : "Disabled"); + } + else if (strcasecmp(name, TXT_RE_CHECK) == 0) + { + result = true; + dprintf(0, "RE_Check=%s", + pp->range_check_enable ? "Enabled" : "Disabled"); + if (strcasecmp(value, "enabled") == 0) + pp->range_check_enable = true; + else + pp->range_check_enable = false; + dprintf(0, "=>%s\n", + pp->range_check_enable ? "Enabled" : "Disabled"); + } + else if (strcasecmp(name, TXT_RANGE_HIGH) == 0) + { + result = true; + dprintf(0, "RangeHigh=%g", pp->range_high); + pp->range_high = strtod(value, NULL); + if (pp->range_high < 0.0) + pp->range_high = 0.0; + dprintf(0, "=>%g\n", pp->range_high); + } + else if (strcasecmp(name, TXT_RANGE_LOW) == 0) + { + result = true; + dprintf(0, "RangeLow=%g", pp->range_low); + pp->range_low = strtod(value, NULL); + if (pp->range_low < 0.0) + pp->range_low = 0.0; + dprintf(0, "=>%g\n", pp->range_low); + } + break; + case 's': + case 'S': + if (strcasecmp(name, TXT_SCAN) == 0) + { + result = true; + dprintf(0, "Scan=%d", pp->poll_period); + pp->poll_period = strtol(value, NULL, 10); + if (pp->poll_period < 1) + pp->poll_period = 1; + else if (pp->poll_period > 1000) + pp->poll_period = 1000; + dprintf(0, "=>%d\n", pp->poll_period); + } + else if (strcasecmp(name, TXT_SAMPLE) == 0) + { + result = true; + dprintf(0, "Sample=%d", pp->sample_period); + pp->sample_period = strtol(value, NULL, 10); + if (pp->sample_period < 1) + pp->sample_period = 1; + else if (pp->sample_period > 1000) + pp->sample_period = 1000; + dprintf(0, "=>%d\n", pp->sample_period); + } + break; + case 't': + case 'T': + if (strcasecmp(name, TXT_TERMINAL) == 0) + { + result = true; + dprintf(0, "Sample=%llu", pp->terminal_count); + pp->terminal_count = strtoull(value, NULL, 10); + dprintf(0, "=>%llu\n", pp->terminal_count); + } + else if (strcasecmp(name, TXT_TE_CHECK) == 0) + { + result = true; + dprintf(0, "TE_Check=%s", + pp->terminal_check_type == 0 ? "None" : + pp->terminal_check_type == 1 ? "Counter" : + pp->terminal_check_type == 2 ? "Timer" : "Unknown"); + if (strcasecmp(value, "counter") == 0) + pp->terminal_check_type = 1; + else if (strcasecmp(value, "timer") == 0) + pp->terminal_check_type = 2; + else + pp->terminal_check_type = 0; + dprintf(0, "=>%s\n", + pp->terminal_check_type == 0 ? "None" : + pp->terminal_check_type == 1 ? "Counter" : + pp->terminal_check_type == 2 ? "Timer" : "Unknown"); + } + break; + default: + dprintf(0, "Unknown Parameter: \"%s\" = \"%s\"\n", name, value); + break; + } + return result; +} + +bool param_set_cmd(pPARAMETERS pp, char* cmd) +{ + char name[100]; + char value[100]; + int len; + while (isspace(*cmd)) + ++cmd; + len = 0; + while (*cmd && !isspace(*cmd) && *cmd != '=') + { + if (len < 100 - 1) + name[len++] = *cmd; + ++cmd; + } + name[len] = '\0'; + + while (isspace(*cmd)) + ++cmd; + if (*cmd != '=') + return false; + ++cmd; + while (isspace(*cmd)) + ++cmd; + len = 0; + while (*cmd && !isspace(*cmd)) + { + if (len < 100 - 1) + value[len++] = *cmd; + ++cmd; + } + value[len] = '\0'; + + return param_set(pp, name, value); +} + +bool param_get_cmd(pPARAMETERS pp, char* cmd, int n) +{ + char name[100]; + int len; + bool result = false; + BUFFER buffer; + buffer.length = 0; + + while (isspace(*cmd)) + ++cmd; + len = 0; + while (*cmd && !isspace(*cmd) && *cmd != '=') + { + if (len < 100 - 1) + name[len++] = *cmd; + ++cmd; + } + name[len] = '\0'; + + switch (name[0]) + { + case 'a': + case 'A': + if (strcasecmp(name, "ALL") == 0) + { + int i; + for (i = 0; param_command[i].txt; ++i) + param_get_cmd(pp, param_command[i].txt, n); + strcpy(buffer.body, "OK"); + result = true; + } + break; + case 'd': + case 'D': + if (strcasecmp(name, TXT_DIRECTION) == 0) + { + result = true; + snprintf(buffer.body, sizeof(buffer.body), + "GET %s=%s", name, + pp->direction ? "Down" : "Up"); + } + break; + case 'i': + case 'I': + if (strcasecmp(name, TXT_INITIAL) == 0) + { + result = true; + snprintf(buffer.body, sizeof(buffer.body), + "GET %s=%llu", name, + pp->initial_count); + } + break; + case 'r': + case 'R': + if (strcasecmp(name, TXT_REPORT) == 0) + { + result = true; + snprintf(buffer.body, sizeof(buffer.body), + "GET %s=%d", name, + pp->report_period); + } + else if (strcasecmp(name, TXT_RANGE_GATE) == 0) + { + result = true; + snprintf(buffer.body, sizeof(buffer.body), + "GET %s=%s", name, + pp->range_gate_enable ? "Enabled" : "Disabled"); + } + else if (strcasecmp(name, TXT_RE_CHECK) == 0) + { + result = true; + snprintf(buffer.body, sizeof(buffer.body), + "GET %s=%s", name, + pp->range_check_enable ? "Enabled" : "Disabled"); + } + else if (strcasecmp(name, TXT_RANGE_MODE) == 0) + { + result = true; + snprintf(buffer.body, sizeof(buffer.body), + "GET %s=%d", name, + pp->range_mode); + } + else if (strcasecmp(name, TXT_RANGE_HIGH) == 0) + { + result = true; + snprintf(buffer.body, sizeof(buffer.body), + "GET %s=%g", name, + pp->range_high); + } + else if (strcasecmp(name, TXT_RANGE_LOW) == 0) + { + result = true; + snprintf(buffer.body, sizeof(buffer.body), + "GET %s=%g", name, + pp->range_low); + } + break; + case 's': + case 'S': + if (strcasecmp(name, TXT_SCAN) == 0) + { + result = true; + snprintf(buffer.body, sizeof(buffer.body), + "GET %s=%d", name, + pp->poll_period); + } + else if (strcasecmp(name, TXT_SAMPLE) == 0) + { + result = true; + snprintf(buffer.body, sizeof(buffer.body), + "GET %s=%d", name, + pp->sample_period); + } + break; + case 't': + case 'T': + if (strcasecmp(name, TXT_TERMINAL) == 0) + { + result = true; + snprintf(buffer.body, sizeof(buffer.body), + "GET %s=%llu", name, + pp->terminal_count); + } + else if (strcasecmp(name, TXT_TE_CHECK) == 0) + { + result = true; + snprintf(buffer.body, sizeof(buffer.body), + "GET %s=%s", name, + pp->terminal_check_type == 0 ? "None" : + pp->terminal_check_type == 1 ? "Counter" : + pp->terminal_check_type == 2 ? "Timer" : "Unknown"); + } + break; + default: + dprintf(0, "Unknown GET Parameter: \"%s\"\n", name); + break; + } + if (!result) + snprintf(buffer.body, sizeof(buffer.body), "GET ERR"); + strcat(buffer.body, "\r\n"); + buffer.length = strlen(buffer.body); + sock_send(n, &buffer); + return result; +} diff --git a/site_ansto/hardsup/Monitor/params.h b/site_ansto/hardsup/Monitor/params.h new file mode 100644 index 00000000..3891c0fd --- /dev/null +++ b/site_ansto/hardsup/Monitor/params.h @@ -0,0 +1,44 @@ +#ifndef _PARAM_H_ +#define _PARAM_H_ + +#include "utility.h" +#include +#include +#include + +/** + * Counter Control Parameters + */ +typedef struct parameter_t +{ + /** direction of counting for the logical counter */ + enum { COUNT_UP = 0, COUNT_DOWN } direction; + /** counter read period in milliseconds */ + int poll_period; + /** sample period in number of reads per sample */ + int sample_period; + /** report period in number of samples per report */ + int report_period; + /** value of the logical counter on START, normally zero */ + uint64 initial_count; + /** value for terminal count exception if enabled */ + uint64 terminal_count; + /** low limit for acceptable range or zero */ + double range_low; + /** high limit for acceptable range or zero */ + double range_high; + /** type = 0:none, 1:count, 2:time */ + int terminal_check_type; + /** true if range exception enabled */ + bool range_check_enable; + /** true if range gating enabled */ + bool range_gate_enable; + /** mode = 0:report, 1:sample, 2:poll */ + int range_mode; + +} PARAMETERS, *pPARAMETERS; + +bool param_set(pPARAMETERS pp, char* name, char* value); +bool param_set_cmd(pPARAMETERS pp, char* cmd); +bool param_get_cmd(pPARAMETERS pp, char* cmd, int n); +#endif diff --git a/site_ansto/hardsup/Monitor/sock.c b/site_ansto/hardsup/Monitor/sock.c new file mode 100644 index 00000000..dd396007 --- /dev/null +++ b/site_ansto/hardsup/Monitor/sock.c @@ -0,0 +1,504 @@ +#include "sock.h" +#include "utility.h" +#include "display.h" +#include "cntr.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include /* superset of previous */ + +#define LINE_LEN 1024 +#define MAX_SOCK 50 + +/** + * Mode of the socket + */ +typedef enum terminal_mode_t +{ + /** unknown or uninitialised */ + term_idle = 0, + /** telnet socket */ + term_tty, + /** web page GET */ + term_page, + /** web form POST */ + term_form, + /** SOAP request */ + term_soap +} TERM_MODE; + +/** + * Terminal Control Structure + * + * Maintains the state of the connection + */ +typedef struct terminal_t +{ + /** file descriptor for the socket */ + int fd; + /** mode of the connection */ + TERM_MODE mode; + /** current state of the connection */ + int state; + /** value to match for reports */ + int match; + /** TOD socket connected */ + struct timeval connect_time; + /** address of peer */ + struct sockaddr_in addr; + /** function to handle input ready */ + void (*input)(int idx); + /** input line buffer */ + char line[LINE_LEN]; + /** length of text in line */ + int line_len; + /** URL for GET/POST */ + char url[LINE_LEN]; + /** value from Content-Length header */ + int content_length; +} TERMINAL, *pTERMINAL; + +/** + * Array of terminal control structures + */ +static TERMINAL fdv[MAX_SOCK]; + +/** + * This structure parallels the terminal control structure + */ +static struct pollfd fds[MAX_SOCK]; +/** Number of active connections */ +static int num_fds = 0; +/** descriptor of the listen socket */ +static int sock_l; + +/** + * Initialise the socket interface + * + * Opens and binds the listen socket and listens for incomming + * connections. + * + * \param addr the TCP/IP port number on which to listen + */ +void sock_init(int addr) +{ + dprintf(0, "sock_init\n"); + int status; + long flags; + int i; + int one = 1; + struct sockaddr_in my_addr; + memset(fdv, 0, sizeof(fdv)); + for (i = 0; i < MAX_SOCK; ++i) + { + fdv[i].fd = -1; + } + memset(fds, 0, sizeof(fds)); + status = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (status < 0) + { + perror("socket"); + exit(EXIT_FAILURE); + } + sock_l = status; + flags = fcntl(sock_l, F_GETFL); + flags |= O_NONBLOCK; + fcntl(sock_l, F_SETFL, flags); + setsockopt(sock_l, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + memset(&my_addr, 0, sizeof(my_addr)); + my_addr.sin_family = AF_INET; + my_addr.sin_port = htons(addr); + my_addr.sin_addr.s_addr = INADDR_ANY; + status = bind(sock_l, (struct sockaddr*) &my_addr, sizeof(my_addr)); + if (status < 0) + { + perror("bind"); + exit(EXIT_FAILURE); + } + status = listen(sock_l, 5); + if (status < 0) + { + perror("listen"); + exit(EXIT_FAILURE); + } + fds[0].fd = sock_l; + fds[0].events = POLLIN | POLLOUT; + fdv[0].fd = sock_l; + fdv[0].input = sock_accept; + ++num_fds; +} + +/** + * Check for socket activity + * + * Polls active sockets for activity: connections on the listen socket or + * received data on the active sockets. + * + * \param timeout wait time in milliseconds + */ +void sock_check(int timeout) +{ + int i; + int ready; + ready = poll(fds, num_fds, timeout); + if (ready < 0) + { + perror("poll"); + return; + } + if (ready == 0) + return; + dprintf(0, "sock_check, ready=%d\n", ready); + for (i = 0; i < MAX_SOCK; ++i) + { + if (fds[i].revents) + { + if (fds[i].revents & POLLIN) + fdv[i].input(i); + if (--ready) + break; + } + } +} + +/** + * Close and remove an active or disconnected socket + * + * \param n index of socket + */ +void sock_close(int n) +{ + dprintf(0, "sock_close\n"); + shutdown(fdv[n].fd, SHUT_RDWR); + close(fdv[n].fd); + if (n != num_fds) + { + fdv[n] = fdv[num_fds]; + fds[n] = fds[num_fds]; + } + fdv[num_fds].fd = -1; + --num_fds; +} + +/** + * Handle a line of input from a socket + * + * \param n index of socket + */ +void sock_line(int n) +{ + char *cp = &fdv[n].line[0]; + char *up; + dprintf(0, "%3d: %s", fdv[n].state, fdv[n].line); + if (fdv[n].line[fdv[n].line_len - 1] != '\n') + dprintf(0, "\n"); + switch(fdv[n].state) + { + case 0: + /* + * we are looking for HTTP GET or POST or a SICS command + */ + if (toupper(cp[0]) == 'G' && + toupper(cp[1]) == 'E' && + toupper(cp[2]) == 'T' && + isspace(cp[3]) && + strstr(cp, "HTTP/")) + { + cp+= 4; + while (isspace(*cp)) + ++cp; + up = &fdv[n].url[0]; + while (*cp && !isspace(*cp)) + *up++ = *cp++; + *up = '\0'; + fdv[n].state = 1; + } + else if (toupper(cp[0]) == 'P' && + toupper(cp[1]) == 'O' && + toupper(cp[2]) == 'S' && + toupper(cp[3]) == 'T' && + isspace(cp[4]) && + strstr(cp, "HTTP/")) + { + cp+= 4; + while (isspace(*cp)) + ++cp; + up = &fdv[n].url[0]; + while (*cp && !isspace(*cp)) + *up++ = *cp++; + *up = '\0'; + fdv[n].content_length = 0; + fdv[n].state = 3; + } + else if (toupper(cp[0]) == 'S' && + toupper(cp[1]) == 'I' && + toupper(cp[2]) == 'C' && + toupper(cp[3]) == 'S' && + isspace(cp[4])) + { + BUFFER buf; + memcpy(buf.body, fdv[n].line, fdv[n].line_len); + buf.length = fdv[n].line_len; + buf.body[buf.length] = '\0'; + process_command(n, &buf); + fdv[n].content_length = 0; + fdv[n].state = 0; + } + else + { + BUFFER buf; + memcpy(buf.body, fdv[n].line, fdv[n].line_len); + buf.length = fdv[n].line_len; + buf.body[buf.length] = '\0'; + process_command(n, &buf); + fdv[n].content_length = 0; + fdv[n].state = 0; + } + break; + case 1: + /* + * We are scanning the header of a GET + */ + while (*cp == '\r' || *cp == '\n') + ++cp; + if (!*cp) + { + if (strncasecmp(fdv[n].url, "/form", 5) == 0) + put_form(n); + else if (strncasecmp(fdv[n].url, "/cmd=", 5) == 0) + { + if (strncasecmp(fdv[n].url, "/cmd=pause", 10) == 0) + cntr_pause(&counter); + else if (strncasecmp(fdv[n].url, "/cmd=continue", 10) == 0) + cntr_resume(&counter); + else if (strncasecmp(fdv[n].url, "/cmd=resume", 10) == 0) + cntr_resume(&counter); + else if (strncasecmp(fdv[n].url, "/cmd=start", 10) == 0) + cntr_start(&counter); + else if (strncasecmp(fdv[n].url, "/cmd=stop", 10) == 0) + cntr_stop(&counter); + put_page_refresh(n); + } + else + put_page(n); + fdv[n].state = 0; + } + break; + case 3: + /* + * we are scanning the header of a POST + */ + while (*cp == '\r' || *cp == '\n') + ++cp; + if (!*cp) + { + if (fdv[n].content_length == 0) + fdv[n].state = 5; + else + fdv[n].state = 4; + break; + } + if (strncasecmp("Content-Length", cp, 14) == 0 && + (cp = strchr(&cp[14], ':'))) + { + fdv[n].content_length = atoi(&cp[1]); + dprintf(0, "Content Length = %d\n", fdv[n].content_length); + } + break; + case 4: + /* + * we are scanning the body of a POST + */ + dprintf(0, "Content-Length: %d, Line-Length: %d\n", + fdv[n].content_length, fdv[n].line_len); + fdv[n].content_length -= fdv[n].line_len; + if (fdv[n].content_length <= 0) + { + BUFFER buf; + memcpy(buf.body, fdv[n].line, fdv[n].line_len); + buf.length = fdv[n].line_len; + process_form(n, &buf); + fdv[n].state = 5; + } + break; + default: + break; + } + /* + * If we have reached the end of a POST, send the new form + */ + if (fdv[n].state == 5) + { + put_form(n); + fdv[n].state = 0; + } +} + +/** + * Handle data received on a socket + * + * \param n index of socket + */ +void sock_input(int n) +{ + dprintf(0, "sock_input(%d)\n", n); + ssize_t sz; + char buffer[1024]; + sz = recv(fdv[n].fd, &buffer, sizeof(buffer), 0); + if (sz == 0) + { + sock_close(n); + return; + } + if (sz < 0) + { + if (errno == EAGAIN) /* AKA EWOULDBLOCK */ + dprintf(0, "EAGAIN:"); + else if (errno == ESPIPE) /* Illegal seek (on pipe or socket) */ + dprintf(0, "ESPIPE:"); + else + perror("recv"); + return; + } + int i; + for (i = 0; i < sz; ++i) + { + if (fdv[n].line_len < LINE_LEN - 1) + fdv[n].line[fdv[n].line_len++] = buffer[i]; + if (buffer[i] == '\n' || + (fdv[n].state == 4 && + fdv[n].line_len == fdv[n].content_length)) { + fdv[n].line[fdv[n].line_len] = '\0'; + sock_line(n); + fdv[n].line_len = 0; + } + } + return; +} + +/** + * Handle connection arrived on listen socket + * + * \param n index of socket + */ +void sock_accept(int n) +{ + dprintf(0, "sock_accept(%d)\n", n); + int sock_n; + struct sockaddr_in my_addr; + socklen_t my_len = sizeof(my_addr); + sock_n = accept(fdv[n].fd, (struct sockaddr*) &my_addr, &my_len); + if (sock_n < 0) + { + perror("accept"); + return; + } + if (num_fds < MAX_SOCK) + { + fdv[num_fds].fd = sock_n; + fdv[num_fds].addr = my_addr; + fdv[num_fds].input = sock_input; + fdv[num_fds].line_len = 0; + fdv[num_fds].state = 0; + fds[num_fds].fd = sock_n; + fds[num_fds].events = POLLIN; + ++num_fds; + } + else + { + /* maximum connections active */ + /* TODO log error */ + close(sock_n); + } +} + +/** + * Send data to active connection + * + * The data in the buffer is sent to the socket + * + * \param n index of socket + * \param bp pointer to buffer to send + */ +void sock_send(int n, BUFFER* bp) +{ + int status; + status = send(fdv[n].fd, bp->body, bp->length, MSG_NOSIGNAL); + if (status < 0) + { + if (errno == EAGAIN) /* flooded */ + ; + else if (errno == EPIPE) + { + perror("send EPIPE"); + sock_close(n); + } + else + { + perror("send"); + sock_close(n); + } + } +} + +/** + * The report in the buffer is sent to all active sockets which match + * + * \param bp pointer to buffer containing report + * \param match value to match for this report + */ +void sock_report(BUFFER* bp, int match) +{ + int i; + for (i = 1; i < num_fds; ++i) + { + if (fdv[i].match == match) + sock_send(i, bp); + } +} + +/** + * Set the match parameter for the socket + * + * \param n index of socket + * \param match value to match for this socket + */ +void sock_set_match(int n, int match) +{ + fdv[n].match = match; +} + +/** + * Send an OK to the socket + * + * \param n index of socket + */ +void sock_ok(int n) +{ + BUFFER buffer; + strcpy(buffer.body, "OK\r\n"); + buffer.length = strlen(buffer.body); + sock_send(n, &buffer); +} + +/** + * Send an ERR to the socket + * + * \param n index of socket + */ +void sock_err(int n) +{ + BUFFER buffer; + strcpy(buffer.body, "ERR\r\n"); + buffer.length = strlen(buffer.body); + sock_send(n, &buffer); +} diff --git a/site_ansto/hardsup/Monitor/sock.h b/site_ansto/hardsup/Monitor/sock.h new file mode 100644 index 00000000..4c6f6b1f --- /dev/null +++ b/site_ansto/hardsup/Monitor/sock.h @@ -0,0 +1,16 @@ +#ifndef _SOCK_H_ +#define _SOCK_H_ +#include "utility.h" + +void sock_init(int addr); +void sock_check(int timeout); +void sock_close(int n); +void sock_line(int n); +void sock_input(int n); +void sock_accept(int n); +void sock_send(int n, BUFFER* buffer); +void sock_report(BUFFER* bp, int match); +void sock_set_match(int n, int match); +void sock_ok(int n); +void sock_err(int n); +#endif diff --git a/site_ansto/hardsup/Monitor/utility.c b/site_ansto/hardsup/Monitor/utility.c new file mode 100644 index 00000000..db574bd3 --- /dev/null +++ b/site_ansto/hardsup/Monitor/utility.c @@ -0,0 +1,129 @@ +/* + * Utility functions and structures + */ +#include "utility.h" +#include +#include + +#define NUM_TIMESTAMPS 20 +#define SZ_TIMESTAMP 40 + +static char ts_array[NUM_TIMESTAMPS][SZ_TIMESTAMP]; +static int ts_index = -1; + +char* make_timestamp(const struct timeval* tv) +{ + char *str; + if (ts_index < 0 || ts_index >= NUM_TIMESTAMPS) + ts_index = 0; + str = ts_array[ts_index++]; + struct timeval now; + if (tv == NULL) + { + tv = &now; + gettimeofday(&now, NULL); + } + snprintf(str, SZ_TIMESTAMP, "%02d:%02d:%02d.%06d", + (int) (tv->tv_sec / 3600 % 24), + (int) (tv->tv_sec / 60 % 60), + (int) (tv->tv_sec % 60), + (int) (tv->tv_usec)); + return str; +} + +double time_diff(struct timeval* later, struct timeval* earlier) +{ + double delta = + ((double) later->tv_sec - (double) earlier->tv_sec) + + 0.000001 * ((double) later->tv_usec - (double) earlier->tv_usec); + return delta; +} + +/* + * subtract the earlier time value from the later time value + */ +int time_sub(struct timeval* later, struct timeval* earlier) +{ + if (later->tv_sec < earlier->tv_sec || + (later->tv_sec == earlier->tv_sec && + later->tv_usec < earlier->tv_usec)) + return -1; + later->tv_sec -= earlier->tv_sec; + if (later->tv_usec < earlier->tv_usec) + { + later->tv_sec--; + later->tv_usec = 1000000 + later->tv_usec - earlier->tv_usec; + } + else + { + later->tv_usec -= earlier->tv_usec; + } + return 0; +} + +int time_cmp(struct timeval* later, struct timeval* earlier) +{ + double delta = (1.0 * later->tv_sec + 0.000001 * later->tv_usec) - + (1.0 * earlier->tv_sec + 0.000001 * earlier->tv_usec); + if (delta == 0.0) + return 0; + else if (delta > 0.0) + return 1; + else return -1; +} + +/* + * Advance the timer by milliseconds until it is in the future + */ +void time_adv(struct timeval* tmr, int msec) +{ + struct timeval now; + gettimeofday(&now, NULL); + while (tmr->tv_sec < now.tv_sec || + (tmr->tv_sec == now.tv_sec && + tmr->tv_usec <= now.tv_usec)) + { + tmr->tv_sec += msec / 1000; + tmr->tv_usec += (msec % 1000) * 1000; + if (tmr->tv_usec >= 1000000) + { + tmr->tv_sec += tmr->tv_usec / 1000000; + tmr->tv_usec = tmr->tv_usec % 1000000; + } + } +} + +#define DEBUG_FILE "debug_file.txt" +#include +#ifndef dprintf +static int debug_level = -1; +int set_debug_level(int new_level) +{ + int temp = debug_level; + debug_level = new_level; + return temp; +} + +int dprintf(int level, const char* format, ...) +{ + int result = 0; + int iRet = 0; + va_list ap; + + if (level <= debug_level) + { + FILE* debug_file = fopen(DEBUG_FILE, "a"); + if (debug_file) + { + va_start(ap, format); + iRet = fprintf(debug_file, "%s ", make_timestamp(NULL)); + result = iRet; + iRet = vfprintf(debug_file, format, ap); + result += iRet; + va_end(ap); + fclose(debug_file); + } + } + return result; +} +#endif diff --git a/site_ansto/hardsup/Monitor/utility.h b/site_ansto/hardsup/Monitor/utility.h new file mode 100644 index 00000000..e07a988f --- /dev/null +++ b/site_ansto/hardsup/Monitor/utility.h @@ -0,0 +1,50 @@ +/* vim: ts=8 sts=2 tw=60 + * + * General utility structure and function definitions + */ +#ifndef _UTILITY_H_ +#define _UTILITY_H_ + +#include +#include +typedef unsigned long long uint64; + +/** + * Text Buffer for accumulating text for input or output + */ +typedef struct buffer_t +{ + /** length of text in the body of this buffer */ + int length; + /** the body of the text, null terminated string */ + char body[65536]; +} BUFFER, *pBUFFER; + +/** + * Convert a time value to a string + * + * Makes a string version of the time for printing. Uses + * only the time of day to produce a string in the format + * HH:MM:SS.UUUUUU at microsecond resolution. + * + * Uses a circular set of internal static buffers allowing + * several calls without overwriting - useful for + * formatting multiple times in one print function call. + * + * \param tv pointer to a timeval structure or NULL for + * current time + * + * \return address of the string given by str + */ +char* make_timestamp(const struct timeval* tv); +double time_diff(struct timeval* later, struct timeval* earlier); +int time_sub(struct timeval* later, struct timeval* earlier); +int time_cmp(struct timeval* later, struct timeval* earlier); +void time_adv(struct timeval* tmr, int msec); +#if 1 +int set_debug_level(int new_level); +int dprintf(int level, const char* format, ...); +#else +#define dprintf fprintf +#endif +#endif