view kk-data-provider.c @ 258:5296f3bcd160

Implement DDS clients reconnect On network send() failures try to reconnect to the server before returning an error. This allows to restart the network servers controlling the DDSes wiothout having to restart the clients.
author Daniele Nicolodi <daniele.nicolodi@obspm.fr>
date Tue, 16 Jun 2015 14:31:35 +0200
parents 9b35a2b2c759
children
line wrap: on
line source

/* FXAnalise data provider which directly interfaces with the KK FX80E counter */

#include <ansi_c.h>
#include <userint.h>
#include <formatio.h>
#include <utility.h>
#include <inifile.h>

#include "config.h"
#include "data-provider.h"
#include "KKFX80E.h"


#define FX_MODE_PI 0x02
#define FX_MODE_LAMBDA 0x03

#define DEFAULT_PORT "COM4:115200"
#define DEFAULT_NCHAN 4
#define DEFAULT_MODE FX_MODE_PI
#define TIMEOUT 1 /* seconds */
#define MAXRETRY 100

#define strneq(a, b, len) (strncmp((a), (b), (len)) == 0)


const char * FX_ModeString(int mode)
{
	const static char *modes[] = {
		[FX_MODE_PI] = "PI",
		[FX_MODE_LAMBDA] = "LAMBDA",
	};

	return modes[mode];
}


static char * FX_Command(unsigned char cmd, unsigned mask, unsigned value)
{
	char *resp;
	int rv, retry;
	unsigned int header;
	
	/* send command */
	rv = FX_Send(cmd);
	if (! rv)
		return NULL;
	
	/* wait successfull reply */
	for (retry = 0; retry < MAXRETRY; retry++) {
		rv = FX_Recv(&resp, TIMEOUT);
		if (! rv)
			return NULL;
		header = strtoul(resp, NULL, 16);
		if ((header & mask) == value)
			return resp;
	}
	
	/* max retry reached */
	return NULL;
}


int CVICALLBACK KKDataProvider (void *functionData)
{
	int main_thread_id;
	int rv;
	char *resp;
	int nchan = DEFAULT_NCHAN;
	int mode = DEFAULT_MODE;
	char port[256];
	char buffer[16];
	char header[16];
	struct event event;
	
	/* get main thread id to post messages to it */
	main_thread_id = CmtGetMainThreadID();
	
	/* configuration file path */ 
	char path[MAX_PATHNAME_LEN];
	GetIniFilePath(path); 

	/* load configuration file */ 
	IniText configuration = Ini_New(TRUE);
	Ini_ReadFromFile(configuration, path);

	/* serial port name */
	rv = Ini_GetStringIntoBuffer(configuration, "KK", "port", port, sizeof(port));
	if (rv < 1)
		strncpy(port, DEFAULT_PORT, sizeof(port));

	/* gate type */
	rv = Ini_GetStringIntoBuffer(configuration, "KK", "mode", buffer, sizeof(buffer));
	if (rv < 1)
		mode = DEFAULT_MODE;
	else if (stricmp(buffer, "PI") == 0)
		mode = FX_MODE_PI;
	else if (stricmp(buffer, "LAMBDA") == 0)
		mode = FX_MODE_LAMBDA;
	else
		send_message(main_thread_id, "KK Counter: unrecognised mode parameter '%s'", buffer);
	
	/* channel number */
	rv = Ini_GetInt(configuration, "KK", "nchan", &nchan);
	if (rv < 1)
		nchan = DEFAULT_NCHAN;
	
	/* free */ 
	Ini_Dispose(configuration);

	/* initialize library */
	FX_Init();
	
	/* connect to KK FX80E counter */
	rv = FX_Open(port);
	if (! rv) {
		send_message(main_thread_id, FX_Error());
		goto error;
	}

	/* get counter hardware version string */
	resp = FX_Command(0x81, 0xFFFF, 0x7001);
	if (! resp) {
		send_message(main_thread_id, FX_Error());
		goto error;
	}
	send_message(main_thread_id, "KK Counter: version %s", resp + 4);
	
	/* set report interval 1sec */
	resp = FX_Command(0x29, 0x0F00, 0x0900);
	if (! resp) {
		send_message(main_thread_id, FX_Error());
		goto error;
	}
	
	/* set mode */
	resp = FX_Command(0x40 + mode, 0x7000, (mode << 12));
	if (! resp) {
		send_message(main_thread_id, FX_Error());
		goto error;
	}
	send_message(main_thread_id, "KK Counter: set mode %s", FX_ModeString(mode));
	
	/* disable scrambler */
	resp = FX_Command(0x50, 0xFFFF, (mode << 12) + 0x0900);
	if (! resp) {
 		send_message(main_thread_id, FX_Error());
 		goto error;
 	}
	
	/* read nchan channels */
	resp = FX_Command(0x30 + nchan, 0xFFFF, (mode << 12) + 0x0900);
	if (! resp) {
		send_message(main_thread_id, FX_Error());
		goto error;
	}

	/* enable synchronization */
	resp = FX_Command(0x0F, 0x00, 0x00);
	if (! resp) {
		send_message(main_thread_id, FX_Error());
		goto error;
	}
	
	/* expected packet header */
	snprintf(header, sizeof(header), "%X", (mode << 12) + 0x0900);
	
	while (acquiring) {
		/* receive data from counter */
		FX_Recv(&resp, TIMEOUT);
		if (! resp) {
			send_message(main_thread_id, FX_Error());
			break;
		}
		
		/* data packets */
		if (strneq(resp, header, 4)) {
			
			/* timestamp */
			gettimeofday(&event.time, NULL);
			
			/* parse received data */
			rv = Scan(resp + 6, "%f; %f; %f; %f; %f; %f; %f; %f",
				&event.data[0], &event.data[1], &event.data[2], &event.data[3],
				&event.data[4], &event.data[5], &event.data[6], &event.data[7]);
			if (rv != nchan) {
				send_message(main_thread_id, "KK Counter: data conversion error: %d != %d", rv, nchan);
				goto error;
			}
			
			/* convert from kHz to Hz */
			for (int i = 0; i < nchan; i++)
				event.data[i] *= 1000.0;
			
			/* push data into the data queue */
			CmtWriteTSQData(dataQueue, &event, 1, TSQ_INFINITE_TIMEOUT, 0);
		
		} else if (strneq(resp, "7000", 4)) {
			/* ignore heart beat packet */
		} else if (strneq(resp, "7015", 4)) {
			/* ignore undocumented packet. it is sent by newer kk counters
			   for each data packet. it probably contains a sample count along
			   with some other information */
		} else if (strneq(resp, "7020", 4)) {
			/* undocumented packet. it probably reports the header for
			   subsequent data packets */
			send_message(main_thread_id, "KK Counter: packet header %s", resp + 7);
		} else if (strneq(resp, "7F51", 4)) {
			/* measurement interval synchronized */
			send_message(main_thread_id, "KK Counter: measurement interval synchronized");
		} else {
			/* send message to the main thread */
			send_message(main_thread_id, "KK Counter: %s", resp);
		}
	}
	
error:
	/* close serial port */
	FX_Close();
	/* free allocated resources */
	FX_Free();
	
	return 0;
}