You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
IAAT_Detector_Control/IAAT_UVDetectorControlMain.cpp

500 lines
20 KiB

#include <stdio.h>
#include <stdlib.h>
//#include <unistd.h>
#include <time.h>
//#include <sys/time.h>
#include <iostream>
#include <fstream>
#include <queue>
#include <pthread.h>
#include "ftd2xx.h"
#include <string>
#include <vector>
#include <IAAT_UVDetectorControlConstants.h>
/*/////////////////////////////////////////////////////////////////////////////
GENERAL STRUCTURE:
INCLUDED HEADERS:
All functions and different Threads are written in this one file.
There is one additional header (IAAT_UVDetectorControlConstants.h)
listing all the Commands to and from the detector. This includes the number
of data Bytes for every command.
Other crucial headers are from the standard C/C++ libraries:
- <iostream>(standard IO for C++)
- <fstream> (write/read from files)
- <queue> (FIFOs between different threads)
- <vector> (easy handling of data storage)
- <string> (used to replace char arrays)
Linux unique:
- <pthread.h> (multithreading)
FTDI D2XX driver library:
- "ftd2xx.h" (used for comunication to the FTDI-chip)
GLOBAL VARIABELS:
There are several global variables. Which is one of the reasons why the
whole code is written into one file.
THREADS: The program starts 3 differnet threads, using the pthread library.
1. int main():
- handles the user input via terminal and the Text file: CommandFile.txt
- starts all other threads
2. void* data_server(void* pArgs):
This thread handles all communication to the detector, which means the
FTDI library "ftd2xx.h" is only used here.
- Opens the detector
- continusly checks if data from the detector is available. If so writes
it to the Receiving FIFO.
- continusly checks the Transmitting FIFO if any commands for the detecor
are available. If so sends the Command to the detector. Does not check if
the command is valid.
3. void* package_filter(void* pArgs)
- continously checks the Receive FIFO if any data has been received. If so
it filters the out the packages and interprets them (science data, HK data,
Acknowledge)
FUNCTIONS: Right now there are two functions which are seperated from the
remaining code.
1. int read_txt_file(std::string command_string, char command_ID, std::vector<char>& command_data_bytes)
- reads the command and its data Bytes from the CommndFile.txt
- the command given as command_string is read from the File
- the command_ID is used to determine the number of data Bytes (can be found in IAAT_UVDetectorControlConstants.h)
- stores the command and the data Bytes into command_data_bytes
- command_data_bytes is given to main. Which then should push it into the transmit FIFO.
2. void process_package(char package[1000])
Used in package_filter thread.
- Interprets the package which is received from the detector and extracted via the package_filter.
- dependend on its content its saved to file (Science data, HK data) or given to main (Acknowlege)
Some features which are needed as well are not implemented yet:
1. Saving science data
2. Saving HK data
3. Sending Acknowledge to the main thread
4. Error handling
ADDITIONAL NOTES:
The main function does not have a mechanism to react to acknowledges, especially failed ones.
*//////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// GLOBAL VARIABLES! //////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
int exit_data_server = 0; // Controlling data_server thread. Not working when bool.
int exit_package_filter = 0; // Controlling package__server thread. Not working when bool.
std::queue<char> rec_queue; // Receive FIFO
std::queue<char> transmit_queue;// Transmit FIFO
int num_rec_data_bytes = 0; // Number of data Bytes for the received command (from detector)
char science_data_file_name[21];// buffer to store the name of the science data file, currently open
std::ofstream science_data_file;
std::ofstream HK_data_file;
pthread_mutex_t mutex_rec_queue = PTHREAD_MUTEX_INITIALIZER; //mutal excess for rec_queue
pthread_mutex_t mutex_transmit_queue = PTHREAD_MUTEX_INITIALIZER; //mutal excess for transmit_queue
///////////////////////////////////////////////////////////////////////////////
// Data Server Thread /////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Continiously checks if data from the detector is available or data needs to be send.
// Data from the detector is stored into a FIFO using <queue>. Data to the detector is read from a similar FIFO.
void* data_server(void *pArgs)
{
FT_HANDLE ftHandle0; // Handle for the Detector. FTDI Device FT232H, used in "USB to sync. FIFO style"
FT_STATUS ftStatus; // Status either: FT_OK or FT_Error
DWORD BytesReceived, BytesTransmitted; // Check communication
DWORD BytesInRxQueue, BytesInTxQueue; // used in FT function call, unfortunate names right now
DWORD EventMask; // Not used, but needed for FT function call
//////////////////////////////////////////////////////////////////////////////
// START UP! Open Detector ///////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
DWORD NumDevs;
char SerialNumber[9] = "FTVQPZC9"; // USB-Chip of Virtex 4 Board
ftStatus = FT_ListDevices(&NumDevs, NULL, FT_LIST_NUMBER_ONLY);
if (ftStatus == FT_OK && NumDevs != 0)
{
//Checking device (FTDI)
FT_DEVICE_LIST_INFO_NODE *DevInfo;
DevInfo = (FT_DEVICE_LIST_INFO_NODE*)malloc(sizeof(FT_DEVICE_LIST_INFO_NODE)*NumDevs);
ftStatus = FT_GetDeviceInfoList(DevInfo,&NumDevs);
for (int i = 0; i < NumDevs; i++)
{
printf(" Dev %d:\n",i);
printf(" Flags=0x%x\n",DevInfo[i].Flags);
printf(" Type=0x%x\n",DevInfo[i].Type);
printf(" ID=0x%x\n",DevInfo[i].ID);
printf(" LocId=0x%x\n",DevInfo[i].LocId);
printf(" SerialNumber=%s\n",DevInfo[i].SerialNumber);
printf(" Description=%s\n",DevInfo[i].Description);
}
ftStatus = FT_OpenEx(SerialNumber, FT_OPEN_BY_SERIAL_NUMBER, &ftHandle0);
if (ftStatus == FT_OK)
{
printf("FT Device: FTVQPZC9 open\n"); //Only for testing purposes
ftStatus = FT_SetTimeouts(ftHandle0, 5000, 1000); // copied from MainControl.cs:: InitialiseUSB()
ftStatus = FT_SetDataCharacteristics(ftHandle0, FT_BITS_8, FT_STOP_BITS_1, FT_PARITY_NONE); // copied from MainControl.cs:: InitialiseUSB()
ftStatus = FT_SetBitMode(ftHandle0, 0x0, 0x40); // copied from MainControl.cs:: InitialiseUSB()
}
else
{
printf("Can't find Detector.\n");
return NULL;
}
}
else
{
printf("Can't find any FTDI Device.\n");
return NULL;
}// end if(ftStatus == FT_OK && NumDevs != 0)
//START UP DONE //////////////////////////////////////////////////////////////
// Following while loop manages data tranfer to and from the detector. First reading out all data from the detector
// and afterwards sending new commands to the detector (in case there are any). Detector data is stored in rec_queue.
// Command data is read from transmit_queue.
while (exit_data_server != 1)
{
ftStatus = FT_GetStatus(ftHandle0, &BytesInRxQueue, &BytesInTxQueue, &EventMask);
if (BytesInRxQueue > 0)
{
char RxBuffer[1000]; //Buffer to store recieved data. Use malloc+free for good solution
ftStatus = FT_Read(ftHandle0, RxBuffer, BytesInRxQueue, &BytesReceived);
pthread_mutex_lock(&mutex_rec_queue); //lock rec_queue
for( int i= 0; i < BytesInRxQueue; i++)
{
rec_queue.push(RxBuffer[i]);
}
pthread_mutex_unlock(&mutex_rec_queue); //unlock rec_queue
}
if (!transmit_queue.empty())
{
pthread_mutex_lock(&mutex_transmit_queue); //lock transmit_queue
char TxBuffer[1002]; //1002 = 999(max. data bytes)+EOP/SOP+ID
int j = 0;
while(!transmit_queue.empty())
{
TxBuffer[j] = transmit_queue.front();
transmit_queue.pop();
j++;
}
pthread_mutex_unlock(&mutex_transmit_queue);//unlock transmit_queue
printf("Number of Bytes written from transmit_queue: %i\n", j);
ftStatus = FT_Write(ftHandle0, TxBuffer, j, &BytesTransmitted);
}
}//end while(exit_data_server != 1)
return NULL;
}
///////////////////////////////////////////////////////////////////////////////
// Process package function ///////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Interprets the package content and process it.
// The commands for the case statement are copied from IAAT_UVDetectorConstants.h.
// This function defines how to react on every Command (aka. how to process it).
// Not complete, and not used yet.
void process_package(std::vector<char> current_package, char num_rec_data_bytes)
{
char packet_id = current_package[1]; // get package ID
switch (packet_id) {
case REC_CMD_ID_NOP: // do nothing
case REC_CMD_ID_FLOW_NULL_ACK: // For maintenance (0 data bytes)
case REC_CMD_ID_FLOW_ACQUIRE_ACK: // Ack for acquisition start (0 data bytes)
time_t rawtime; // copied from http://www.cplusplus.com/reference/ctime/strftime/
struct tm* timeinfo; // "
time(&rawtime); // "
timeinfo = localtime(&rawtime); // "
strftime(science_data_file_name,21,"Science_%Y%m%d.dat",timeinfo); //write time and date into filename
//puts(science_data_file_name);
science_data_file.open(science_data_file_name);
printf("Flow Acquire acknowleged\n");
break;
case REC_CMD_ID_FLOW_STOP_ACK: // Ack for acquisition stop (0 data bytes)
printf("Flow Stop acknowleged\n");
science_data_file.close();
break;
case REC_CMD_ID_FLOW_RESET_ACK: // (0 data bytes)
case REC_CMD_ID_FLOW_SLEEP_ACK: // Ack for power saving mode (0 data bytes)
case REC_CMD_ID_FLOW_WAKEUP_ACK: // Ack for leaving power saving mode (0 data bytes)
case REC_CMD_ID_FPGA_SET_CONFIG_ACK:// Ack for set FPGA config register (0 data bytes)
printf("FPGA CONFIG acknowleged \n");
case REC_CMD_ID_STATUS_TRIGGER_RATE:
case REC_CMD_ID_DATA_UNPROC: // Unprocessed ADC data packet ((14+2)bit/chn)+NUMSAM+MaxX+MaxY (259 data bytes)
printf("Unproc data received \n");
for (int i = 0; i< current_package.size(); i++) // simple science_data_file << current_package, does not work
{
science_data_file << current_package[i];
}
break;
case REC_CMD_ID_DATA_MAXLOC: // Numbers of strips having maximum charge (3 data bytes)
printf("Maxloc data received \n");
for (int i = 0; i< current_package.size(); i++) // simple science_data_file << current_package, does not work
{
science_data_file << current_package[i];
}
break;
case REC_CMD_ID_DATA_EVENT: // Data packet in event format (999 data bytes = not yet defined)
printf("Event data received \n");
for (int i = 0; i< current_package.size(); i++) // simple science_data_file << current_package, does not work
{
science_data_file << current_package[i];
}
break;
default: break;
}
}
///////////////////////////////////////////////////////////////////////////////
// Package Filter Thread //////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Continiously checks the Rec_queue FIFO if data is available.
// Filters the Packages by checking the SOP/EOP MARKER.
// No error/exeption handling yet. But working correct 07.12.20 LH
void* package_filter(void * pArgs)
{
//std::ofstream science_data_file; // Science data file write only access
//std::ofstream HK_data_file; // House keeping (HK) data file, wirte only access
while (exit_package_filter != 1)
{
if(!rec_queue.empty())
{
char Current_Byte; //current byte from rec_queue
std::vector<char> current_package(2); //using <vector> for easy handling
Current_Byte = rec_queue.front();
if (Current_Byte == REC_CMD_ID_SOP) //looking for SOP marker
{
current_package[0] = Current_Byte; //storing SOP marker
rec_queue.pop();
current_package[1] = rec_queue.front(); //loading package ID
num_rec_data_bytes = REC_CMD_DATA_BYTES[Current_Byte]; //looking for number of data bytes coming in
current_package.resize(num_rec_data_bytes + 3);
rec_queue.pop();
if (num_rec_data_bytes > 0)
{
for (int n = 0; n < num_rec_data_bytes ;n++) //reads out data bytes (if any)
{
current_package[n+2] = rec_queue.front();
rec_queue.pop();
}
}
Current_Byte = 0;
while ((Current_Byte != SND_CMD_ID_EOP) /*&& (!rec_queue.empty())*/ ) //looking for EOP marker
{
Current_Byte = rec_queue.front();
rec_queue.pop();
if(Current_Byte == SND_CMD_ID_EOP)
{
current_package[num_rec_data_bytes + 2] = Current_Byte;
}
}
process_package(current_package, num_rec_data_bytes);
}//end if(Current_Byte == SND_CMD_ID_SOP)
else // if data != SOP marker discard it
{
rec_queue.pop();
}
} //end if(!rec_queue.empty())
}//end while(exit_package_filter != 1)
}
///////////////////////////////////////////////////////////////////////////////
// Function to read in the text file //////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// The data to be send to the detector is stored into vector<char>& command_data_bytes (passed as reference)
int read_txt_file(std::string command_string, char command_ID, std::vector<char>& command_data_bytes)
{
std::ifstream command_file; //file stream, read only
command_file.open("CommandFile.txt"); //open file
size_t line_pos; // position where the line starts
std::string line; // storing current line from txt file
bool command_found = false; // valid command marker
while(command_file.good())
{
getline(command_file, line); // search line for command
std::cout << line << std::endl; // test, print lines till found
line_pos = line.find(command_string); // search
if(line_pos != std::string::npos) // string::npos is returned if string is not found
{
command_found = true;
break;
}
}
if (command_found == true) // only read
{
//Getting command_ID from txt file
int pos_start = line_pos+1+command_string.length(); // set first start position, right behind the Command
int pos_comma = line.find(",", pos_start+1); // search for next comma. Comma is how the numbers are separated
int str_length = pos_comma - pos_start; // string length to be read, is calculated
std::string str_number = line.substr (pos_start,str_length);// read in that string (as string)
char current_byte = std::stoi(str_number); // convert string to integer
command_ID = current_byte; // save integer to Command ID
printf("command_ID = %i\n", command_ID); // test output
pos_start = pos_comma+1; // set next start position
int num_trans_data_bytes = SND_CMD_DATA_BYTES[command_ID]; // get numbers of data bytes from: IAAT_UVDetectorControlConstants.h
command_data_bytes.resize(num_trans_data_bytes+3); // make vector as big as needed (SOP, Command_ID, Data, EOP).
printf("NUM_BYTES= %i\n", num_trans_data_bytes); // test output
command_data_bytes[0] = SND_CMD_ID_SOP; // first command entry is always SOP marker
command_data_bytes[1] = command_ID; // second command entry is always commad ID
for( int n = 0; n < num_trans_data_bytes; n++)
{
std::cout << "pos_start: " << pos_start << std::endl;
int pos_comma = line.find(",", pos_start+1); // comma is how the numbers are separated
std::cout << "pos_comma: " << pos_comma << std::endl;
int str_length = pos_comma - pos_start; //determine length of number (in chars)
std::cout << "str_length: " << str_length << std::endl;
std::string str_number = line.substr (pos_start,str_length);
char current_byte = std::stoi(str_number);
printf("current_byte: 0x%x\n", current_byte);
command_data_bytes[n+2] = current_byte;
pos_start = pos_comma+1;
}//end for
command_data_bytes[num_trans_data_bytes+2] = SND_CMD_ID_EOP; // last entry is always EOP marker
command_file.close(); // close file
}
else
{
command_file.close(); // just close file, don't do anything
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// MAIN ///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
int main ()
{
//remove VCP driver, root access needed
system("sudo rmmod ftdi_sio");
system("sudo rmmod usbserial");
//////////////////////////////////////////////////////////////////////////////
// OPEN DETECTOR COMMUNICATION ///////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// Start other threads (data_server & package_filter)
pthread_t data_server_thread;
pthread_t package_filter_thread;
exit_data_server = 0;
pthread_create(&data_server_thread, NULL, &data_server, NULL);
pthread_create(&package_filter_thread, NULL, &package_filter, NULL);
//////////////////////////////////////////////////////////////////////////////
// USER INTERFACE ////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
bool exit_DetectorControl = false;
std::string command_str = "SND_CMD_ID_FLOW_STOP"; // set by default for tests
std::vector <char> command_data_bytes(3); // set to minimal size, will be resized later
while (exit_DetectorControl != true)
{
printf("\n\nManually enter a command for the UV-Detector.\n\n");
printf("0: Enter command manually\n"); //Test
printf("1: Read Text file\n");
printf("2: Send command to detector\n");
printf("3: End Programm\n");
printf("4: Write something to rec_queue (Test only) \n"); // Test
char rec_queue_test_data[3]; //Test only
//Initialize variables
char userIF_command;
int command_ID;
std::cin >> userIF_command; // get user Command
switch(userIF_command) {
case '0': printf("Enter Command manually\n");
std::cin >> command_str;
std::cout << "command entered: " << command_str << std::endl;
break;
case '1': printf("Read Command from Text file.\n");
read_txt_file(command_str, command_ID, command_data_bytes);
printf("Print first 3 Bytes of command: \n");
printf("Byte0_main: 0x%x\n", command_data_bytes[0]);
printf("Byte1_main: 0x%x\n", command_data_bytes[1]);
printf("Byte2_main: 0x%x\n", command_data_bytes[2]);
break;
case '2': printf("Send command to detector\n");
// push data into transmit_queue
pthread_mutex_lock(&mutex_transmit_queue);// unlock transmit_queue
for (int n = 0; n < 3; n++)
{
transmit_queue.push(command_data_bytes[n]);
printf("0x%x pushed into transmit_queue\n", transmit_queue.back());
}
pthread_mutex_unlock(&mutex_transmit_queue);//unlock transmit_queue
break;
case '3': printf("Program closed.\n");
exit_DetectorControl = true;
break;
case '4': printf("Write something to rec_queue.\n");
rec_queue_test_data[0] = 0xaa;
rec_queue_test_data[1] = 0x01;
rec_queue_test_data[2] = 0x55;
for (int i = 0; i < 3; i++)
{
rec_queue.push(rec_queue_test_data[i]);
}
break;
default: printf("Invalid Command.\n");
break;
}
}// end while
//////////////////////////////////////////////////////////////////////////////
// Close Detector Control ///////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
exit_data_server = 1;
exit_package_filter = 1;
//wait for other threads (data_server & packege_filter) to exit
pthread_join(data_server_thread, NULL);
pthread_join(package_filter_thread, NULL);
printf("Detecor Control closed.\n");
return 0;
}