Initial load of driver

r1083 | dcl | 2006-08-25 14:46:51 +1000 (Fri, 25 Aug 2006) | 2 lines
This commit is contained in:
Douglas Clowes
2006-08-25 14:46:51 +10:00
parent 650db574c5
commit 8b3816b612
15 changed files with 2928 additions and 0 deletions

View File

@@ -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

View File

@@ -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 <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <time.h>
#include <errno.h>
#include <libiberty.h>
#include <sys/time.h>
#include <sys/types.h>
int usage(int argc, char* argv[])
{
fprintf(stderr, "usage: %s <DEVn/CTRn> <PORT>\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;
}

View File

@@ -0,0 +1,11 @@
/*
* Monitor.h
*
*/
#ifndef _MONITOR_H_
#define _MONITOR_H_
#define DEFAULT_COUNTER_DEVICE "DEV1/CTR0"
#define DEFAULT_LISTEN_PORT 3000
#endif

View File

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

View File

@@ -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

View File

@@ -0,0 +1,696 @@
#include "display.h"
#include "utility.h"
#include "params.h"
#include "sock.h"
#include "cntr.h"
#include <ctype.h>
#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,
"<tr><td>%s</td>\r\n"
"<td><select name=\"%s\">\r\n",
title,
name);
buffer->length += strlen(bp);
}
/**
* Add an option line to a drop down selection box
*
* \param buffer where the output is appended
* \param name text displayed in the box
* \param value text returned in the form
* \param selected if this is the default selection to be displayed
*
* 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_option(BUFFER* buffer, char* name, char* value, bool selected)
{
char* bp = &buffer->body[buffer->length];
snprintf(bp, sizeof(buffer->body) - buffer->length,
"<option value=\"%s\"%s>%s</option>\r\n", value,
selected ? " selected=\"selected\"" : "",
name);
buffer->length += strlen(bp);
}
/**
* Close out the selection, column and row of a drop down box
*
* \param buffer where the output is appended
*/
static void add_select_end(BUFFER* buffer)
{
char* bp = &buffer->body[buffer->length];
snprintf(bp, sizeof(buffer->body) - buffer->length,
"</select></td></tr>\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,
"<tr><td>%s</td>"
"<td><input type=\"text\" name=\"%s\" size=12 value=\"%d\"></td>"
"</tr>\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,
"<tr><td>%s</td>"
"<td><input type=\"text\" name=\"%s\" size=12 value=\"%.12g\"></td>"
"</tr>\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,
"<tr><td>%s</td>"
"<td><input type=\"text\" name=\"%s\" size=12 value=\"%llu\"></td>"
"</tr>\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,
"<html><head><title>Counter Form</title></head>\r\n"
"<body>\r\n"
"<form method=\"post\" action=\"/form\">\r\n"
"<table align=\"center\" border=\"0\">\r\n"
"<tr><th align=\"right\">Field</th>\r\n"
"<th align=\"left\">Value</th></tr>\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,
"<tr><td>\r\n"
"<input type=\"submit\" name=\"Send\" size=9 value=\"Update\">\r\n"
"</td><td>"
"<input type=\"reset\" name=\"Reset\" size=9 value=\"Reset\">\r\n"
"</td></tr></table>\r\n"
"</form>\r\n"
"</body>\r\n"
"</html>\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,
"<tr><td>%s</td>"
"<td>%s</td>"
"</tr>\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,
"<tr><td>%s</td>"
"<td>%d</td>"
"</tr>\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,
"<tr><td>%s</td>"
"<td>%.2f</td>"
"</tr>\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,
"<tr><td>%s</td>"
"<td>%llu</td>"
"</tr>\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,
"<html><head><title>Counter Page</title></head>\r\n"
"<body>\r\n"
"<table align=\"center\" border=\"0\"><tr><td>\r\n"
"<table align=\"center\" border=\"0\">\r\n"
"<tr><th align=\"right\">Field</th>\r\n"
"<th align=\"left\">Value</th></tr>\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 = "<font color=\"BLUE\">LOW</font>";
else if (cp->range_error == 2)
result = "<font color=\"RED\">HIGH</font>";
else
result = "<font color=\"GREEN\">OK</font>";
show_text(&buffer, "Range Check", result);
}
else
show_text(&buffer, "Range Check", "Disabled");
add_text(&buffer, "</table></td><td valign=\"top\"><table>\r\n");
add_text(&buffer, "<tr><td><a href=\"/form\">\r\n"
"<button>Form</button>\r\n"
"</a></td></tr>\r\n");
add_text(&buffer, "<tr><td><a href=\"/cmd=start\">\r\n"
"<button>Start</button>\r\n"
"</a></td></tr>\r\n");
add_text(&buffer, "<tr><td><a href=\"/cmd=stop\">\r\n"
"<button>Stop</button>\r\n"
"</a></td></tr>\r\n");
add_text(&buffer, "<tr><td><a href=\"/cmd=pause\">\r\n"
"<button>Pause</button>\r\n"
"</a></td></tr>\r\n");
add_text(&buffer, "<tr><td><a href=\"/cmd=continue\">\r\n"
"<button>Continue</button>\r\n"
"</a></td></tr>\r\n");
add_text(&buffer, "</table></td></tr>\r\n");
add_text(&buffer, "</table>\r\n"
"</body>\r\n"
"</html>\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, "<html>\r\n"
"<head>\r\n"
"<title>Title</title>\r\n"
"</head>\r\n"
"<body>\r\n");
add_text(&buffer, "</body>\r\n"
"</html>\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, "<html>\r\n"
"<head>\r\n"
"<title>Title</title>\r\n"
"</head>\r\n"
"<body>\r\n");
add_text(&buffer, "</body>\r\n"
"</html>\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);
}

View File

@@ -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

View File

@@ -0,0 +1,105 @@
#include "hctr.h"
#include <stdlib.h>
#include <string.h>
#include <NIDAQmx.h>
#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);
}

View File

@@ -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 <stdbool.h>
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

View File

@@ -0,0 +1,384 @@
#include "params.h"
#include "sock.h"
#include <ctype.h>
#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;
}

View File

@@ -0,0 +1,44 @@
#ifndef _PARAM_H_
#define _PARAM_H_
#include "utility.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/**
* 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

View File

@@ -0,0 +1,504 @@
#include "sock.h"
#include "utility.h"
#include "display.h"
#include "cntr.h"
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* 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);
}

View File

@@ -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

View File

@@ -0,0 +1,129 @@
/*
* Utility functions and structures
*/
#include "utility.h"
#include <unistd.h>
#include <stdio.h>
#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 <msec> 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 <stdarg.h>
#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

View File

@@ -0,0 +1,50 @@
/* vim: ts=8 sts=2 tw=60
*
* General utility structure and function definitions
*/
#ifndef _UTILITY_H_
#define _UTILITY_H_
#include <sys/time.h>
#include <stdbool.h>
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