/** @file temperature_scan.c

@brief Scan mode with RTD PT1000 on module 0.


@par LICENCE
@verbatim
Copyright (C) 2004,2005  ADDI-DATA GmbH for the source code of this module.

ADDI-DATA GmbH
Dieselstrasse 3
D-77833 Ottersweier
Tel: +19(0)7223/9493-0
Fax: +49(0)7223/9493-92
http://www.addi-data-com
info@addi-data.com

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

You shoud also find the complete GPL in the COPYING file
accompanying this source code.
@endverbatim
*/


#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/sysmacros.h> /* major() minor() */
#define NDEBUG
#include <assert.h>
#include <apci3200.h>
#include <apci3200-events.h>
#include "convert.h"
#include "findboards.h"
#include "input.h"
#include "errorlib.h"
#include "errormsg.h"
#include "libapci3200sample.h"


static int stop = 0;
static int verbose = 0;

//--------------------------------------------------------------------------------

/* Enables to stop the acquisition */
void quit_sample(int sig)
{
	printf("Quit the sample\n");
	stop = 1;
}

//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------

int call_CMD_APCI3200_ReadChannelCalibrationValue(int fd, int ch, struct acquisition_t *acquisition)
{
	struct ReadCalibrationValue_t read;

	read.b_Channel = ch;
	read.w_ConnectionType = acquisition->ChannelInit.b_DiffSingSelected;
	read.w_Gain = acquisition->ChannelInit.w_GainSelected;

	// Following variables are used to get driver values
	read.dw_CJCCurrentSource = 0;
	read.dw_ChannelCurrentSource = 0;
	read.dw_ChannelGainFactor = 0;

	if(verbose) printf("=> CMD_APCI3200_ReadChannelCalibrationValue on channel %i\n", read.b_Channel);
	{
		if ( call_ioctl (fd,CMD_APCI3200_ReadChannelCalibrationValue, &read))
			return -1;

		printf("Calibration values on channel %i\n", read.b_Channel);
		printf("CJC current source = %lu\n", read.dw_CJCCurrentSource);
		printf("Channel current source = %lu\n", read.dw_ChannelCurrentSource);
		printf("Channel gain factor = %lu\n", read.dw_ChannelGainFactor);

		acquisition->dw_CJCCurrentSource = read.dw_CJCCurrentSource;	// CJC Is the same for all channels
		acquisition->dw_ChannelGainFactor = read.dw_ChannelGainFactor;
		acquisition->dw_ChannelCurrentSource = read.dw_ChannelCurrentSource;
	}
	return 0;
}

//--------------------------------------------------------------------------------

int call_CMD_APCI3200_ReadReferenceVoltage(int fd, int ch, struct acquisition_t *acquisition)
{
	struct ReadReferenceVoltage_t read;

	read.b_Channel = ch;

	if(verbose) printf("=> CMD_APCI3200_ReadReferenceVoltage on input %i\n", read.b_Channel);
	{
		if ( call_ioctl (fd,CMD_APCI3200_ReadReferenceVoltage, &read))
			return -1;

		printf("Analog input %i value has reference voltage %u %s\n", read.b_Channel, read.dw_ReferenceVoltage, apci3200_unit2symb(read.Unit));
		acquisition->dw_ReferenceVoltage = read.dw_ReferenceVoltage;
	}
	return 0;
}

//--------------------------------------------------------------------------------

int apci3200_scan(int fd, int module)
{
	struct acquisition_t acquisition;
	unsigned long dw_ChannelGainFactor[2];
	unsigned long dw_ChannelCurrentSource[2];
	int ch = 0;

	acquisition.SensorClass = ADDIDATA_RTD;
	acquisition.RTDValue = 1000;
	acquisition.SensorType = ADDIDATA_RTD_TYPE_PT;
	acquisition.ChannelInit.b_DiffSingSelected = APCI3200_CONNECTION_TYPE_DIFFERENTIAL;
	acquisition.ChannelInit.w_GainSelected = APCI3200_GAIN_1;
	acquisition.ChannelInit.b_PolaritySelected = APCI3200_POLARITY_BIPOLAR;

	{
		struct InitTemperatureModule_t initTemperatureModule;

		initTemperatureModule.l_DiffSingSelected = acquisition.ChannelInit.b_DiffSingSelected;
		initTemperatureModule.l_Module = module;
		initTemperatureModule.l_SensorType = acquisition.SensorClass;
		initTemperatureModule.pl_SensorValue[0] = acquisition.SensorType;
		initTemperatureModule.pl_SensorValue[1] = acquisition.SensorType;
		initTemperatureModule.pl_SensorValue[2] = acquisition.SensorType;
		initTemperatureModule.pl_SensorValue[3] = acquisition.SensorType;

		if (call_ioctl (fd, CMD_APCI3200_InitScanModuleAcquisitionSensor, &initTemperatureModule))
			goto error;
	}

	{
		DWORD args[7];

		args[0] = module;					// Module
		args[1] = 20;						// Conversion time
		args[2] = APCI3200_DELAY_NOT_USED;	// Scan time mode
		args[3] = 0;						// Scan time
		args[4] = 0;						// Scan time unit
		args[5] = APCI3200_CONTINUOUS_SCAN;	// Scan mode
		args[6] = APCI3200_DISABLE;			// Extern trigger

		if (call_ioctl (fd, CMD_APCI3200_InitScanModuleAcquisition, args))
			goto error;
	}

	/* Stop running acquisitions */
	{
		int ret = ioctl (fd, CMD_APCI3200_StopScanModuleAcquisition, &module);

		printf("CMD_APCI3200_StopScanModuleAcquisition error: %d \n", ret);
	}

	/* We are using Ch0 and Ch1 on module 0 (only two channels because the diff. connection is selected) */
	for (ch=0; ch<2; ch++)
	{
		if(call_CMD_APCI3200_ReadChannelCalibrationValue(fd, ch, &acquisition))
			goto error;

		dw_ChannelGainFactor[ch] = acquisition.dw_ChannelGainFactor;
		dw_ChannelCurrentSource[ch] = acquisition.dw_ChannelCurrentSource;
	}

	/* The voltage is the same for the whole board that's why we pass 0 as channel number */
	if( call_CMD_APCI3200_ReadReferenceVoltage(fd, 0, &acquisition) )
		goto error;

	{
		WORD arg = module;

		if (call_ioctl (fd, CMD_APCI3200_StartScanModuleAcquisition, &arg))
			goto error;
	}

	/* Set the callback function to stop the sample with Ctrl + c */
	signal (SIGINT, quit_sample);

	/* Empty the event buffer */
	printf("Empty the event buffer \n");
	{
		if ( ioctl (fd,CMD_APCI3200_EventEmpty,NULL) != 0 )
		{
			printf("Error CMD_APCI3200_EventEmpty \n");
			goto error;
		}
	}

	{
		int i = 0;

		/* Endless while, only interruptible by Ctrl + c */
		while (!stop)
		{
			apci3200_event_t evt;

			/*
			 * Values delivered by the EventWait function are:
			 * mask:	Bits 0-3 are the module who generates the interrupt.
			 * 			Bits 4-7 are the functionality who generates the interrupt (0x10 stands for scan).
			 * 			Bit 31: When FIFO was full and has been overwritten.
			 *
			 * size:	This is the number of useful values in the values array.
			 * 			The size depends on the sensor class (RTD or thermocouple).
			 *
			 * values:	With a thermocouple:
			 * 			values[0]	Channel 0
			 * 			values[1]	Channel 1
			 * 			values[2]	Channel 2
			 * 			values[3]	Channel 3
			 * 			values[4]	Channel CJC
			 * 			values[5]	Channel Calibration offset
			 * 			values[6]	Channel Calibration gain
			 *
			 *			With a RTD:
			 * 			values[0]	Channel 0
			 * 			values[1]	Channel 1
			 * 			values[2]	Channel CJC (not used with RTD)
			 * 			values[3]	Channel Calibration offset
			 * 			values[4]	Channel Calibration gain
			 */
			if ( ioctl (fd,CMD_APCI3200_EventWait, &evt) != 0 )
			{
				printf("Error CMD_APCI3200_EventWait \n");
				goto error;
			}

			printf("Interrupt type %i on module: %d fifo %s\n", (evt.mask & 0xF0), (evt.mask & 0xF), ((evt.mask & 0x80000000)==0x80000000)?"full":"not full");

			if ((evt.mask & 0x80000000)==0x80000000)
				goto error;

			// Convert in temperature value
			{
				double d_RealValue = 0.0;
				int i_ReturnValue = 0;
				unsigned long pdw_DigitalValue [6];

				for (i=0; i<2; i++)
				{
					if(acquisition.SensorClass == ADDIDATA_RTD)
					{
						pdw_DigitalValue [0] = evt.values[i];
						pdw_DigitalValue [1] = evt.values[3]; // Calibration offset
						pdw_DigitalValue [2] = evt.values[4]; // Calibration gain
					}

					/* Sample function to convert values */
					i_ReturnValue = i_ADDIDATA_ConvertDigitalToRealTemperatureValueWithCorrectionParameters (
													APCI3200_OTHER, /* acquisition type */
													acquisition.SensorClass,
													acquisition.SensorType,
													acquisition.RTDValue,
													acquisition.ChannelInit.b_PolaritySelected,
													acquisition.ChannelInit.w_GainSelected,
													( (double) dw_ChannelGainFactor[i] / 1000000.0),
													( (double) dw_ChannelCurrentSource[i] / 1000000000.0),
													( (double) acquisition.dw_CJCCurrentSource / 1000000000.0),
													pdw_DigitalValue,
													&d_RealValue,
													( (float) acquisition.dw_ReferenceVoltage/1000.0)
					);

					if (i_ReturnValue != 0)
					{
						printf ("i_ADDIDATA_ConvertDigitalToRealTemperatureValueWithCorrectionParameters error =%i\n", i_ReturnValue);
						goto error;
					}

					printf ("ch%i Temperature value = %f\n", i, d_RealValue);
				}
			}
		}
	}

error:
	{
		int ret = ioctl (fd, CMD_APCI3200_StopScanModuleAcquisition, &module);
		printf("CMD_APCI3200_StopScanModuleAcquisition error: %d \n", ret);
	}

	close(fd);

	return 0;
}

//--------------------------------------------------------------------------------
int run_test(int fd)
{
	int module;

	printf("Module number>");
	module = get_integer(0,3);

	return apci3200_scan(fd, module);
}
//--------------------------------------------------------------------------------
int main(int argc, char** argv)
{
	int * apci3200_card_fd;

	unsigned int apci3200_card_number = 0;
	{
		apci3200_card_number = apci3200_find_cards(&apci3200_card_fd);

		printf("total: %d cards\n",apci3200_card_number);
	}

	{
		int index;
		switch(apci3200_card_number)
		{
		case 1:
			index = 0;
			break;
		case 0:
			exit(0);
		default:
			printf("board>");
			index = get_integer(0,apci3200_card_number-1);
		}
		return run_test(apci3200_card_fd[index]);
	}
	return 0;
}
//--------------------------------------------------------------------------------
