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.
500 lines
20 KiB
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;
|
|
}
|
|
|