/******************************************************************************
 * $HeadURL: https://svnpub.iter.org/codac/iter/codac/dev/units/m-codac-unit-templates/tags/CODAC-CORE-6.0.0/templates/cpp-sdn/main/c++/prog/prog.cpp.template $
 * $Id: prog.cpp.template 83098 2018-01-08 13:23:38Z cesnikt $
 *
 * Project	: CODAC Core System
 *
 * Description	: GyrotronDAN program
 *
 * Author        : codac-dev
 *
 * Copyright (c) : 2010-2018 ITER Organization,
 *				  CS 90 046
 *				  13067 St. Paul-lez-Durance Cedex
 *				  France
 *
 * This file is part of ITER CODAC software.
 * For the terms and conditions of redistribution or use of this software
 * refer to the file ITER-LICENSE.TXT located in the top level directory
 * of the distribution package.
 ******************************************************************************/

/* header files */
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <error.h>
//#include <xseries-lib.h>
#define float32_t float32_t1
#include <xseries-lib.h>
#undef float32_t

#include <iostream>
#include <dan.h>
#include <time.h>
#include <ca-if.h>
#include <math.h>
#include <tcn.h>

#include <log.h>

// global variables
static bool __terminate = false;
char pxie6368_0_ai_fd[] = "/dev/pxie-6368.1.ai";   // ai segment
char pxie6368_0_device[] = "/dev/pxie-6368.1";    // device descriptor

typedef struct {
	int daq_smpl_rate;
	int daq_mode;
	double daq_smpl_st_dly;
	double daq_len;
	double daq_pub_dly;
} daq_parameters;

void signal_handler(int signal) {
	log_info("Received signal '%d' to terminate", signal);
	__terminate = true;
};

// DAN parameter reload function ------------------------------------------------
int reload_daq_conf(int *aifd, int *ai_chan_fd, daq_parameters *daq_p) {
	std::cout << "reload_daq_conf was called \n";

	// Common variables
	int retval;
	unsigned long number_of_samples;
	int chan_num = 10;                     // Channel number to add.
	uint32_t sample_period_divisor;
	uint32_t base_clock = 100000000;       // TB3:100MHz, TB2:100 kHz
	uint32_t pre_samples = 1000;
	uint32_t post_samples = 1000;
	char str[40];
	xseries_ai_conf_t conf;               // ai segment configuraton data

	// calculate sampling period divisor
	if (daq_p->daq_smpl_rate != 0) {
		sample_period_divisor = base_clock / daq_p->daq_smpl_rate;
	} else {
		return 0;
	}

	// Stop ai segment----------------------------------------------------------------------
	retval = xseries_stop_ai(*aifd);
	if (retval) {
		log_error("ai segment stop failed.\n");
	} else {
		log_info("ai segment stopped.\n");
	}

	// reset ai segment---------------------------------------------------------------------
	retval = xseries_reset_ai(*aifd);
	if (retval) {
		log_error("ai segment reset failed.\n");
		return -1;
	}

	//    Configure AI segment mode ------------------------------------------------------------
	switch (daq_p->daq_mode) {
	case 0:
		conf = xseries_software_timed_ai();
		log_info("software_timed mode.\n");
		break;
	case 1:
		conf = xseries_finite_ai(number_of_samples);
		log_info("finite mode.\n");
		break;
	case 2:
		conf = xseries_retriggerable_finite_ai(number_of_samples);
		log_info("retriggerable_finite mode.\n");
		break;
	case 3:
		conf = xseries_continuous_ai();
		log_info("continuous mode.\n");
		break;
	case 4:
		conf = xseries_reference_ai(pre_samples, post_samples);
		log_info("reference mode.\n");
		break;
	}

	// disable external gate
	retval = xseries_set_ai_external_gate(&conf,
			XSERIES_AI_EXTERNAL_GATE_DISABLED,  // No external pause signal
			XSERIES_AI_POLARITY_ACTIVE_LOW_OR_FALLING_EDGE); // Don't care
	if (retval) {
		log_error("Cannot set gate trigger.\n");
		return -1;
	}

	// select start trigger (START1 signal)
	retval = xseries_set_ai_start_trigger(&conf,
			XSERIES_AI_START_TRIGGER_SW_PULSE, // Set the line to software-driven
			XSERIES_AI_POLARITY_ACTIVE_HIGH_OR_RISING_EDGE,  // Make line active on rising...
			1);                                                                                            // ... edge (not high level)
	if (retval) {
		log_error("Cannot set start trigger.\n");
		return -1;
	}

	// set sampling clock source----------------------------------------------------------------------------------------
	retval = xseries_set_ai_sample_clock(&conf,
			XSERIES_AI_SAMPLE_CONVERT_CLOCK_INTERNALTIMING,
			XSERIES_AI_POLARITY_ACTIVE_HIGH_OR_RISING_EDGE, 1);
	if (retval) {
		log_error("Cannot configure sampling clock.\n");
		return -1;
	}

	// set scan interval -------------------------------------------------------------------------------------------------------------------------
	retval = xseries_set_ai_scan_interval_counter(&conf,
			XSERIES_SCAN_INTERVAL_COUNTER_TB3,                                     // TB3 : 100MHz base clock, TB2 : 100kHz base clock
			XSERIES_SCAN_INTERVAL_COUNTER_POLARITY_RISING_EDGE,
			sample_period_divisor, 2);                                                                  // Wait 2*100MHz sec after sampling trig.
	if (retval) {
		log_error("Cannot configure scan counter.\n");
		return -1;
	}

	// Set DMA buffer size-----------------------------------------------------------------------------------------------------------------------
	retval = xseries_set_ai_attribute(&conf, XSERIES_AI_DMA_BUFFER_SIZE, 1000);
	if (retval) {
		log_error("DMA configuration was failed.\n");
		return -1;
	}

	// Add AI channels ---------------------------------------------------------------------------------------------------------------------------
	for (int i = 0; i < chan_num; i++) {
		retval = xseries_add_ai_channel(&conf, i, XSERIES_INPUT_RANGE_10V,
				XSERIES_AI_CHANNEL_TYPE_DIFFERENTIAL, 0);
		if (retval) {
			log_error("Add AI channel %d was failed.\n", i);
			return -1;
		}
	}
	sleep(3);

	// Load configuration.-----------------------------------------------------------------------------------------------------------------------
	retval = xseries_load_ai_conf(*aifd, conf);
	if (retval) {
		log_error("xseries_load_ai_conf was failed. error code: %d \n", retval);
		exit (EXIT_FAILURE);
	}
	sleep(3);

	// Open channels--------------------------------------------------------------------------------------------------------------------------------
	for (int i = 0; i < chan_num; i++) {
		sprintf(str, "%s.ai.%u", pxie6368_0_device, i);
		//ai_chan_fd[i] = open(str, O_RDWR | O_NONBLOCK);
		ai_chan_fd[i] = open(str, O_RDWR);
		if (ai_chan_fd[i] < 0) {
			log_error("Cannot open ai channel %d .\n",i);
			sleep(1);
			exit (EXIT_FAILURE);
		}
	}

	// start ai segment task-----------------------------------------------------------------------------------------------------------------------
	retval = xseries_start_ai(*aifd);
	if (retval) {
		log_error("ERROR, starting AI segment failed: %s\n", strerror(retval));
		exit (EXIT_FAILURE);
	}

	log_info("PXIe-6368 configuration was finished.\n");
	return 0;
};



int main(int argc, char **argv) {
	using namespace std;
	unsigned long n_sample_quotient = 0;

	log_initialize(NULL);

	while (!__terminate) {
		// Install signal handler to support graceful termination
		sigset(SIGTERM, signal_handler);
		sigset(SIGINT, signal_handler);
		sigset(SIGHUP, signal_handler);

		// ----------------------------------------------------------------------------------------------------------------
		// Running parameter declaration
		// ----------------------------------------------------------------------------------------------------------------
		// pxie6368 hardware parameters
		int pxie6368_0_device_number = 1;            // device discriptor number
		int aifd = 0;                                                   // ai segment file discriptor
		xseries_ai_conf_t conf;                                 // ai segment config
		xseries_ai_attribute_t aiatrb;                        // ai segment attributes
		char str[40];                                                  // fd tmp var
		int ai_chan_fd[16];                                        // ai channels file discriptor
		int pxie6368_sampling_rate = 1000;            // sampling rate Hz
		uint32_t sample_period_divisor = 100000000 / pxie6368_sampling_rate; // TB3 100MHz, TB2:100kHz, TB1:20MHz
		int sampleSize = 2;                                      // Sample size in bytes <---> uInt16
		int n_channels;                                             // Number of opened channels
		struct xseries_dma *dma;                            // Ptr to dma struct
		int chan_num = 10;                                      // Channel number to add.

		// DAN parameters
		char dan_source_name[] = "GYBDanSource";            // DAN data source (ds) name
		long offset_ai = 0;                                              // offset size in published data
		int dma_mode = 1;                                            // 0:All, 1:AI only, 2:AO only
		dan_DataCore dc = NULL;                                 // Datacore reference declaration
		dan_Source ds = NULL;                                     // DAN source declaration
		long samplesCounter = 0;                                 // Total acquired sample size
		size_t n_samples;                                              // prepared sample size to publish

		// TCN parameters
		uint64_t tBase_old = 0;
		uint64_t tBase = 0;                                            // TCN publishing base time
		uint64_t nanos;                                                 // time length for sampling
		hpn_timestamp_t delay = 1e7;                         // wait for publishing (10ms)
		char buf[ISO_8601_LEN];
		uint64_t tBase0 = 0;

		// Operation tmp parameter
		int result;                                      // For result values.
		int exValue;
		bool toterminate = false;
		int retval;
		bool daq_start = false;
		int testcounter = 10000;

		// Connected EPCIS PV

		// DAQ config PVs
		chid daq_smpl_rate_id;
		chtype daq_smpl_rate_type;
		int daq_smpl_rate;
		chid daq_smpl_rate_rb_id;
		chtype daq_smpl_rate_rb_type;
		int daq_smpl_rate_rb;
		chid daq_mode_id;
		chtype daq_mode_type;
		int daq_mode;
		chid daq_mode_rb_id;
		chtype daq_mode_rb_type;
		int daq_mode_rb;
		chid daq_sw_trig_id;
		chtype daq_sw_trig_type;
		int daq_sw_trig;
		chid daq_smpl_st_dly_id;
		chtype daq_smpl_st_dly_type;
		double daq_smpl_st_dly;
		chid daq_smpl_st_dly_rb_id;
		chtype daq_smpl_st_dly_rb_type;
		double daq_smpl_st_dly_rb;
		chid daq_len_id;
		chtype daq_len_type;
		double daq_len;
		chid daq_len_rb_id;
		chtype daq_len_rb_type;
		double daq_len_rb;
		chid daq_reconf_id;
		chtype daq_reconf_type;
		int daq_reconf;
		chid daq_pub_dly_id;
		chtype daq_pub_dly_type;
		double daq_pub_dly;
		chid daq_pub_dly_rb_id;
		chtype daq_pub_dly_rb_type;
		double daq_pub_dly_rb;

		chid daq_stat_id;
		chtype daq_stat_type;
		int daq_stat;
		chid daq_conf_stat_id;
		chtype daq_conf_stat_type;
		int daq_conf_stat;

		daq_parameters daq_p = { 0 }; // = {daq_smpl_rate, daq_smpl_rate_rb, daq_mode, daq_mode_rb};

		// ----------------------------------------------------------------------------------------------------------------------------------------------
		// EPICS PV connection
		// ----------------------------------------------------------------------------------------------------------------------------------------------
		//Initialize ChannelAccess interface
		CAInterface_Initialize();

		// Connect to EPICS PVs
		CAInterface_ConnectVariable("EC-GN-P01-GPF:STAT-DAQ-SMPL-RATE", daq_smpl_rate_id);
		daq_smpl_rate_type = DBR_LONG;

		CAInterface_ConnectVariable("EC-GN-P01-GPF:STAT-DAQ-SMPL-RATE-RB", daq_smpl_rate_rb_id);
		daq_smpl_rate_rb_type = DBR_LONG;

		CAInterface_ConnectVariable("EC-GN-P01-GPF:STAT-DAQ-MODE", daq_mode_id);
		daq_mode_type = DBR_ENUM;

		CAInterface_ConnectVariable("EC-GN-P01-GPF:STAT-DAQ-MODE-RB", daq_mode_rb_id);
		daq_mode_rb_type = DBR_ENUM;

		CAInterface_ConnectVariable("EC-GN-P01-GPF:STAT-DAQ-SW-TRIG", daq_sw_trig_id);
		daq_sw_trig_type = DBR_ENUM;

		CAInterface_ConnectVariable("EC-GN-P01-GPF:STAT-SMPL-ST-DLY", daq_smpl_st_dly_id);
		daq_smpl_st_dly_type = DBR_DOUBLE;

		CAInterface_ConnectVariable("EC-GN-P01-GPF:STAT-SMPL-ST-DLY-RB", daq_smpl_st_dly_rb_id);
		daq_smpl_st_dly_rb_type = DBR_DOUBLE;

		CAInterface_ConnectVariable("EC-GN-P01-GPF:STAT-DAQ-LEN", daq_len_id);
		daq_len_type = DBR_DOUBLE;

		CAInterface_ConnectVariable("EC-GN-P01-GPF:STAT-DAQ-LEN-RB", daq_len_rb_id);
		daq_len_rb_type = DBR_DOUBLE;

		CAInterface_ConnectVariable("EC-GN-P01-GPF:STAT-DAQ-RECONF", daq_reconf_id);
		daq_reconf_type = DBR_ENUM;

		CAInterface_ConnectVariable("EC-GN-P01-GPF:STAT-DAQ-PUB-DLY", daq_pub_dly_id);
		daq_pub_dly_type = DBR_DOUBLE;

		CAInterface_ConnectVariable("EC-GN-P01-GPF:STAT-DAQ-PUB-DLY-RB", daq_pub_dly_rb_id);
		daq_pub_dly_rb_type = DBR_DOUBLE;

		CAInterface_ConnectVariable("EC-GN-P01-GPF:STAT-DAQ-STAT", daq_stat_id);
		daq_stat_type = DBR_ENUM;

		CAInterface_ConnectVariable("EC-GN-P01-GPF:STAT-DAQ-CONF-STAT", daq_conf_stat_id);
		daq_conf_stat_type = DBR_ENUM;

		tcn_sleep(3e9);// Wait for a while.

		// ----------------------------------------------------------------------------------------------------------------
		// Initialize tcn library
		// ----------------------------------------------------------------------------------------------------------------
		int tcn_err;
		tcn_err = tcn_init();
		if (tcn_err != TCN_SUCCESS) {
			log_error("TCN Initialization failed.\n");
			exValue = 1;
			goto END;
		}

		// ----------------------------------------------------------------------------------------------------------------
		// pxie-6368 settings
		// ----------------------------------------------------------------------------------------------------------------
		// Open ai segment----------------------------------------------------------------------
		aifd = open(pxie6368_0_ai_fd, O_RDWR);
		if (aifd <= 0) {
			log_error("Open PXIe6368 AI fd failed.\n");
			exValue = -1;
			goto END;
		}

		// wait ai segment setup start
		do {
			CAInterface_ReadVariable(daq_reconf_id, daq_reconf_type, (void*) &daq_reconf);
		} while (!daq_reconf and !__terminate);

		// read parameter
		result = CAInterface_ReadVariable(daq_smpl_rate_id, daq_smpl_rate_type, (void *) &daq_p.daq_smpl_rate);
		if (!result) {
			log_error("CA_READ failed.\n");
		}
		// read parameter
		result = CAInterface_ReadVariable(daq_mode_id, daq_mode_type, (void *) &daq_p.daq_mode);
		if (!result) {
			log_error("CA_READ failed.\n");
		}
		// read parameter
		result = CAInterface_ReadVariable(daq_smpl_st_dly_id, daq_smpl_st_dly_type, (void *) &daq_p.daq_smpl_st_dly);
		if (!result) {
			log_error("CA_READ failed.\n");
		}
		// read parameter
		result = CAInterface_ReadVariable(daq_len_id, daq_len_type, (void *) &daq_p.daq_len);
		if (!result) {
			log_error("CA_READ failed.\n");
		}
		// read parameter
		result = CAInterface_ReadVariable(daq_pub_dly_id, daq_pub_dly_type, (void *) &daq_p.daq_pub_dly);
		if (!result) {
			log_error("CA_READ failed.\n");
		}

		std::cout << "SAMPLE RATE:" << daq_p.daq_smpl_rate << endl;
		std::cout << "MODE            :" << daq_p.daq_mode << endl;
		std::cout << "START DELAY:" <<daq_p.daq_smpl_st_dly << endl;
		std::cout << "DAQ LENGTH:" << daq_p.daq_len << endl;
		std::cout << "PUB DELAY    :" << daq_p.daq_pub_dly << endl;

		// Configure ai segment and dan parameter by using EPICS PV.
		result = reload_daq_conf(&aifd, ai_chan_fd, &daq_p);
		if (result < 0) {
			log_error("Load DAQ CONF failed.\n");
			daq_stat = 3;                                                                                           // 0:Not ready, 1:Waiting trigger, 2:Aquiring, 3:Error
			CAInterface_WriteVariable(daq_stat_id, daq_stat_type, &daq_stat);
			daq_conf_stat = 0;                                                                                  // 0:Not Ready, 1: Ready
			CAInterface_WriteVariable(daq_conf_stat_id, daq_conf_stat_type, &daq_conf_stat);
			goto END;
		} else {
			// All parameters were configured correctly, update readback PV
			CAInterface_WriteVariable(daq_smpl_rate_rb_id, daq_smpl_rate_rb_type, &daq_p.daq_smpl_rate);
			CAInterface_WriteVariable(daq_mode_rb_id, daq_mode_rb_type, &daq_p.daq_mode);
			CAInterface_WriteVariable(daq_smpl_st_dly_rb_id, daq_smpl_st_dly_rb_type, &daq_p.daq_smpl_st_dly);
			CAInterface_WriteVariable(daq_len_rb_id, daq_len_rb_type, &daq_p.daq_len);
			CAInterface_WriteVariable(daq_pub_dly_rb_id, daq_pub_dly_rb_type, &daq_p.daq_pub_dly);
			daq_conf_stat = 1;
			CAInterface_WriteVariable(daq_conf_stat_id, daq_conf_stat_type, &daq_conf_stat);
		}

		// ------------------------------------------------------------------------------------------------------------------
		// ----- DAN Publisher.
		// ------------------------------------------------------------------------------------------------------------------
		// Initialize DAN DataCore
		dc = dan_initLibrary();
		if (dc == NULL) {
			log_error("dan_init failed.\n");
			exValue = 1;
			goto END;
		}

		// ----- DMA initialization
		dma = xseries_dma_init(pxie6368_0_device_number, 1); // (devid, dma_mode)  mode 0:mapping all dma mem, 1:AI only.
		if (dma == NULL) {
			cout << "Failed to connect to samples buffer.\n go to END.\n";
			exValue = -1;
			goto END;
		}

		// Push a new data block into source.
		ds = dan_publisher_publishSource(dc, dan_source_name, pxie6368_0_device,
				                                                 DAN_DAQ_MMAP, dma->ai.count * sampleSize, 4096);

		//dan_DataCore, ds_name, devName, enum_daq_type refType, long refSize, long refOffset
		if (ds == NULL) {
			log_error("Error while publishing DAN source %s ", dan_source_name);
			exValue = 1;
			goto END;
		}

		n_channels = dan_publisher_getNumberOfChannels(ds);

		// ------------------------------------------------------------------------------------------------------------------
		// ----- DAN Streamer.
		// ------------------------------------------------------------------------------------------------------------------
		// ----- Open a new stream ---------------------------------------------------------------------
		dan_publisher_openStream(ds, daq_p.daq_smpl_rate, 0);
		sleep(1);

		// ------------------------------------------------------------------------------------------------------------------
		// ----- Main DAN Loop.
		// ------------------------------------------------------------------------------------------------------------------
		while (!__terminate) {
			// Wait for the master trigger.----------------------------------------------------------------------------
			daq_stat = 1;                                                                                            //0:Not Ready, 1: Waiting trigger, 2:Acquiring 3:Error
			CAInterface_WriteVariable(daq_stat_id, daq_stat_type, &daq_stat);
			daq_conf_stat = 1;                                                                                    //0:Not ready, 1: Ready
			CAInterface_WriteVariable(daq_conf_stat_id, daq_conf_stat_type, &daq_conf_stat);

			// Wait DAQ Start Software Trigger.
			do {
				// check daq_sw_trigger to start DAQ sequence.
				CAInterface_ReadVariable(daq_sw_trig_id, daq_sw_trig_type, (void*) &daq_sw_trig);
				tcn_sleep(1e7); // wait 0.01 s
				if (daq_sw_trig) {
					daq_start = true;
				}
			} while (daq_start != true and !__terminate);

			daq_start = false;                                          //reset daq_start flag.
			delay = (long) (1e6 * daq_p.daq_pub_dly);  //check publish period.

			// Wait a time to start DAQ. ----------------------------------------------------------------------------------------------------------------------
			tcn_sleep(daq_p.daq_smpl_st_dly * 1e6);   // in ms

			// After the wating, trigger DAQ  ------------------------------------------------------------------------------------------------------------------
			retval = xseries_pulse_ai(aifd, XSERIES_START_TRIGGER);
			daq_stat = 2;
			CAInterface_WriteVariable(daq_stat_id, daq_stat_type, &daq_stat);
			if (retval) {
				std::cout << "Cannot generate start trigger!\n";
				goto END;
			} else {
				tcn_get_time(&tBase);  // get DAQ start time.
				tBase0 = tBase;        // save DAQ start time to tBase0;
				tBase_old = tBase;
			}

			// DAQ Publish Loop. ----------------------------------------------------------------------------------------------------------------------------
			// DAN data publish is executed every delay time interval.
			while (!__terminate && (tcn_wait_until_hr((tBase + delay), &tBase, 0) == TCN_SUCCESS)) {
				// Get the number of available samples in PXIe6368 buffer.
				n_samples = xsereis_ai_dma_samples_in_buffer(dma);  // samples

				if (n_samples > dma->ai.count) {
					//log_error("DMA Buffer overflow: Number of new samples in buffer = %ld, AI buffer size = %ld,\n", n_samples, dma->ai.count);
				} else if (n_samples > 0) {
					//log_info("Current Time:\n%s\n", tcn_strftime(tBase, buf, ISO_8601_LEN));
					if (tBase == 0) {
						// "tBase was reset.------------------------------------------------------\n";
						tcn_get_time(&tBase);
						tBase -= n_samples * 100000000 / pxie6368_sampling_rate;
						nanos = 0;
					} else {
						// update time offset value.
						nanos = n_samples * 100000000 / daq_p.daq_smpl_rate;
					}
					n_sample_quotient = floor(n_samples / chan_num);
					n_samples = n_sample_quotient * chan_num;

					// Publish a new data block --------------------------------------------------------------------------------------------------------------------------
					result = dan_publisher_putBlockReference(ds,
							                                                              tBase_old + nanos,             // timestamp in epoch nanoseconds
							                                                              n_samples * sampleSize,   // datablock size in bytes
							                                                              offset_ai * sampleSize,      // Offset in DAQ buffer
							                                                              NULL);                                // No datablock header....
					// Detected overflow in pass datablock reference queue
					if (result == -1) {
						log_info("%s, Queue overflow with policy %d\n", dan_source_name,  dan_publisher_getCheckPolicy(ds));
					} else if (result == -2) {
						log_info("%s, DAQ buffer overflow with policy %d\n", dan_source_name,  dan_publisher_getCheckPolicy(ds));
					}
				}

				samplesCounter += n_samples;                                   // Increment total of published sampes
				offset_ai = (offset_ai + n_samples) % dma->ai.count; // Update offset of memory
				dma->ai.last_transfer_count = samplesCounter;          // Update dma last_transfer_count

				// Check exit condition.
				if ((daq_p.daq_len * 1e6 <= (tBase - tBase0))) {
					retval = xseries_stop_ai(aifd);
					if (retval) {
						std::cout << "Stop AI segment failed.\n";
						exit (EXIT_FAILURE);
					}

					//restart ai segment.
					tcn_sleep(1e9);  // wait
					retval = xseries_start_ai(aifd);
					if (retval) {
						std::cout << "Start AI segment failed.\n";
						exit (EXIT_FAILURE);
					}
					//debug
					tcn_sleep(1e8);
					n_samples = xsereis_ai_dma_samples_in_buffer(dma);
					cout << "n_samples check : " << n_samples << endl;
					break;
				}
				tBase_old = tBase;
			} // End of DAQ publish loop.
			break;
		} // End of DAN main loop.

		// ------------------------------------------------------------------------------------------------------------------
		// ----- Closing program.
		// ------------------------------------------------------------------------------------------------------------------
		log_info("DAN end of acquisition, closing all resources");

		//Closing stream
		dan_publisher_closeStream(ds);
		exValue = 0;

		// Close all channels
		for (int channel = 0; channel < chan_num; channel++) {
			close(ai_chan_fd[channel]);
		}
		goto END;

		END: daq_conf_stat = 0;
		CAInterface_WriteVariable(daq_conf_stat_id, daq_conf_stat_type, &daq_conf_stat);
		daq_stat = 0;
		CAInterface_WriteVariable(daq_stat_id, daq_stat_type, &daq_stat);
		// Destroy CA context
		CAInterface_Finalize();
		// Close pxie6368
		if (dma != NULL) {
			xseries_dma_close(dma);
			cout << "pxie-6368 dma was clesed.\n";
		}

		close(aifd);

		// Unpublishing source from DAN API
		if (ds != NULL) {
			dan_publisher_unpublishSource(dc, ds);
		}

		// Closing DAN API library
		if (dc != NULL) {
			dan_closeLibrary(dc);
		}

		// Closing TCN library
		result = tcn_finalize();
		if (result != TCN_SUCCESS) {
			//log_error("Error finalizing TCN lib.\n");
		}

	} 	// loop end from initialization
	return (0);

}
