giuliomoro@153: /*
giuliomoro@153:  * WriteFile.cpp
giuliomoro@153:  *
giuliomoro@153:  *  Created on: 5 Oct 2015
giuliomoro@153:  *      Author: giulio
giuliomoro@153:  */
giuliomoro@153: 
giuliomoro@153: #include "WriteFile.h"
giuliomoro@153: //initialise static members
giuliomoro@153: bool WriteFile::staticConstructed=false;
giuliomoro@153: AuxiliaryTask WriteFile::writeAllFilesTask=NULL;
giuliomoro@153: std::vector<WriteFile *> WriteFile::objAddrs(0);
giuliomoro@153: bool WriteFile::threadRunning;
giuliomoro@153: bool WriteFile::threadIsExiting;
giuliomoro@153: int WriteFile::sleepTimeMs;
giuliomoro@153: 
giuliomoro@153: void WriteFile::staticConstructor(){
giuliomoro@153: 	if(staticConstructed==true)
giuliomoro@153: 			return;
giuliomoro@153: 	staticConstructed=true;
giuliomoro@153: 	threadIsExiting=false;
giuliomoro@153: 	threadRunning=false;
giuliomoro@301: 	writeAllFilesTask = Bela_createAuxiliaryTask(WriteFile::run, 60, "writeAllFilesTask");
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@153: WriteFile::WriteFile(){
giuliomoro@153: 	buffer = NULL;
giuliomoro@153: 	format = NULL;
giuliomoro@153: 	header = NULL;
giuliomoro@153: 	footer = NULL;
giuliomoro@153: 	stringBuffer = NULL;
giuliomoro@153: };
giuliomoro@153: 
giuliomoro@153: void WriteFile::init(const char* filename){ //if you do not call this before using the object, results are undefined
giuliomoro@153: 	file = fopen(filename, "w");
giuliomoro@153: 	variableOpen = false;
giuliomoro@153: 	lineLength = 0;
giuliomoro@159: 	setEcho(false);
giuliomoro@153: 	bufferLength = 0;
giuliomoro@157: 	textReadPointer = 0;
giuliomoro@157: 	binaryReadPointer = 0;
giuliomoro@153: 	writePointer = 0;
giuliomoro@153: 	sleepTimeMs = 1;
giuliomoro@153: 	stringBufferLength = 1000;
giuliomoro@153: 	stringBuffer = (char*)malloc(sizeof(char) * (stringBufferLength));
giuliomoro@153: 	setHeader("variable=[\n");
giuliomoro@153: 	setFooter("];\n");
giuliomoro@301: 	staticConstructor(); //TODO: this line should be in the constructor, but cannot be because of a bug in Bela
giuliomoro@153: 	objAddrs.push_back(this);
giuliomoro@157: 	echoedLines = 0;
giuliomoro@157: 	echoPeriod = 1;
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@157: void WriteFile::setFileType(WriteFileType newFileType){
giuliomoro@157: 	fileType = newFileType;
giuliomoro@159: 	if(fileType == kBinary)
giuliomoro@159: 		setLineLength(1);
giuliomoro@157: }
giuliomoro@153: void WriteFile::setEcho(bool newEcho){
giuliomoro@153: 	echo=newEcho;
giuliomoro@153: }
giuliomoro@157: void WriteFile::setEchoInterval(int newEchoPeriod){
giuliomoro@157: 	echoPeriod = newEchoPeriod;
giuliomoro@157: 	if(echoPeriod != 0)
giuliomoro@157: 		echo = true;
giuliomoro@157: 	else
giuliomoro@157: 		echo = false;
giuliomoro@157: }
giuliomoro@153: void WriteFile::print(const char* string){
giuliomoro@153: 	if(echo == true){
giuliomoro@157: 		echoedLines++;
giuliomoro@157: 		if (echoedLines >= echoPeriod){
giuliomoro@157: 			echoedLines = 0;
giuliomoro@157: 			printf("%s", string);
giuliomoro@157: 		}
giuliomoro@153: 	}
giuliomoro@157: 	if(file != NULL && fileType != kBinary){
giuliomoro@153: 		fprintf(file, "%s", string);
giuliomoro@153: 	}
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@153: void WriteFile::writeLine(){
giuliomoro@157: 	if(echo == true || fileType != kBinary){
giuliomoro@157: 		int stringBufferPointer = 0;
giuliomoro@157: 		for(unsigned int n = 0; n < formatTokens.size(); n++){
giuliomoro@157: 			int numOfCharsWritten = snprintf( &stringBuffer[stringBufferPointer], stringBufferLength - stringBufferPointer,
giuliomoro@157: 								formatTokens[n], buffer[textReadPointer]);
giuliomoro@157: 			stringBufferPointer += numOfCharsWritten;
giuliomoro@157: 			textReadPointer++;
giuliomoro@159: 			if(textReadPointer >= bufferLength){
giuliomoro@157: 				textReadPointer -= bufferLength;
giuliomoro@157: 			}
giuliomoro@153: 		}
giuliomoro@157: 		print(stringBuffer);
giuliomoro@153: 	}
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@153: void WriteFile::setLineLength(int newLineLength){
giuliomoro@153: 	lineLength=newLineLength;
giuliomoro@153: 	free(buffer);
giuliomoro@159: 	bufferLength = lineLength * (int)1e7; // circular buffer of length 1e7 lineLenghts
giuliomoro@153: 	buffer = (float*)malloc(sizeof(float) * bufferLength);
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@153: void WriteFile::log(float value){
giuliomoro@157: 	if(fileType != kBinary && (format == NULL || buffer == NULL))
giuliomoro@153: 		return;
giuliomoro@153: 	buffer[writePointer] = value;
giuliomoro@153: 	writePointer++;
giuliomoro@153: 	if(writePointer == bufferLength){
giuliomoro@153: 		writePointer = 0;
giuliomoro@153: 	}
giuliomoro@157: 	if((fileType == kText && writePointer == textReadPointer - 1) ||
giuliomoro@157: 			(fileType == kBinary && writePointer == binaryReadPointer - 1)){
giuliomoro@157: 		fprintf(stderr, "%d %d WriteFile: pointers crossed, you should probably slow down your writing to disk\n", writePointer, binaryReadPointer);
giuliomoro@153: 	}
giuliomoro@153: 	if(threadRunning == false){
giuliomoro@153: 		startThread();
giuliomoro@153: 	}
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@395: void WriteFile::log(const float* array, int length){
giuliomoro@153: 	for(int n = 0; n < length; n++){
giuliomoro@153: 		log(array[n]);
giuliomoro@153: 	}
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@153: WriteFile::~WriteFile() {
giuliomoro@153: 	free(format);
giuliomoro@153: 	free(buffer);
giuliomoro@153: 	free(header);
giuliomoro@153: 	free(footer);
giuliomoro@153: 	free(stringBuffer);
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@153: void WriteFile::setFormat(const char* newFormat){
giuliomoro@153: 	allocateAndCopyString(newFormat, &format);
giuliomoro@153: 	for(unsigned int n = 0; n < formatTokens.size(); n++){
giuliomoro@153: 		free(formatTokens[n]);
giuliomoro@153: 	}
giuliomoro@153: 	formatTokens.clear();
giuliomoro@153: 	int tokenStart = 0;
giuliomoro@153: 	bool firstToken = true;
giuliomoro@153: 	for(unsigned int n = 0; n < strlen(format)+1; n++){
giuliomoro@153: 		if(format[n] == '%' && format[n + 1] == '%'){
giuliomoro@153: 			n++;
giuliomoro@153: 		} else if (format[n] == '%' || format[n] == 0){
giuliomoro@153: 			if(firstToken == true){
giuliomoro@153: 				firstToken = false;
giuliomoro@153: 				continue;
giuliomoro@153: 			}
giuliomoro@153: 			char* string;
giuliomoro@153: 			unsigned int tokenLength = n - tokenStart;
giuliomoro@153: 			if(tokenLength == 0)
giuliomoro@153: 				continue;
giuliomoro@153: 			string = (char*)malloc((1+tokenLength)*sizeof(char));
giuliomoro@153: 			for(unsigned int i = 0; i < tokenLength; i++){
giuliomoro@153: 				string[i] = format[tokenStart + i];
giuliomoro@153: 			}
giuliomoro@153: 			string[tokenLength] = 0;
giuliomoro@153: 			formatTokens.push_back(string);
giuliomoro@153: 			tokenStart = n;
giuliomoro@153: 		}
giuliomoro@153: 	}
giuliomoro@153: 	setLineLength(formatTokens.size());
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@153: int WriteFile::getNumInstances(){
giuliomoro@153: 	return objAddrs.size();
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@153: void WriteFile::startThread(){
giuliomoro@301: 	Bela_scheduleAuxiliaryTask(writeAllFilesTask);
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@153: void WriteFile::stopThread(){
giuliomoro@153: 	threadIsExiting=true;
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@153: bool WriteFile::threadShouldExit(){
giuliomoro@153: 	return(gShouldStop || threadIsExiting);
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@153: bool WriteFile::isThreadRunning(){
giuliomoro@153: 	return threadRunning;
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@153: float WriteFile::getBufferStatus(){
giuliomoro@153: 	return 1-getOffset()/(float)bufferLength;
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@157: int WriteFile::getOffsetFromPointer(int aReadPointer){
giuliomoro@157: 	int offset = writePointer - aReadPointer;
giuliomoro@157: 		if( offset < 0)
giuliomoro@157: 			offset += bufferLength;
giuliomoro@157: 		return offset;
giuliomoro@157: }
giuliomoro@153: int WriteFile::getOffset(){
giuliomoro@157: 	if(fileType == kBinary){
giuliomoro@157: 		return getOffsetFromPointer(binaryReadPointer);
giuliomoro@157: 	}
giuliomoro@157: 	else{
giuliomoro@157: 		return getOffsetFromPointer(textReadPointer);
giuliomoro@157: 	}
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@159: void WriteFile::writeOutput(bool flush){
giuliomoro@158: 	while((echo == true || fileType == kText) && getOffsetFromPointer(textReadPointer) >= lineLength){ //if there is less than one line worth of data to write, skip over.
giuliomoro@157: 							 	 // So we make sure we only write full lines
giuliomoro@153: 		writeLine();
giuliomoro@153: 	}
giuliomoro@157: 	if(fileType == kBinary){
giuliomoro@159: 		int numBinaryElementsToWriteAtOnce = 3*(int)1e5;
giuliomoro@157: 		while(getOffsetFromPointer(binaryReadPointer) > numBinaryElementsToWriteAtOnce){
giuliomoro@157: 			int elementsToEndOfBuffer = bufferLength - binaryReadPointer;
giuliomoro@157: 			int numberElementsToWrite = numBinaryElementsToWriteAtOnce < elementsToEndOfBuffer ?
giuliomoro@157: 					numBinaryElementsToWriteAtOnce : elementsToEndOfBuffer;
giuliomoro@157: 			numberElementsToWrite = fwrite(&(buffer[binaryReadPointer]), sizeof(float), numberElementsToWrite, file);
giuliomoro@157: 			binaryReadPointer += numberElementsToWrite;
giuliomoro@157: 			if(binaryReadPointer >= bufferLength){
giuliomoro@157: 				binaryReadPointer = 0;
giuliomoro@157: 			}
giuliomoro@157: 		}
giuliomoro@159: 		if(flush == true){ // flush all the buffer to the file
giuliomoro@157: 			while(getOffsetFromPointer(binaryReadPointer) != 0){
giuliomoro@157: 				binaryReadPointer += fwrite(&(buffer[binaryReadPointer]), sizeof(float), 1, file);
giuliomoro@157: 				if(binaryReadPointer >= bufferLength){
giuliomoro@157: 					binaryReadPointer = 0;
giuliomoro@157: 				}
giuliomoro@157: 			}
giuliomoro@157: 		}
giuliomoro@157: 	}
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@159: void WriteFile::writeAllOutputs(bool flush){
giuliomoro@153: 	for(unsigned int n = 0; n < objAddrs.size(); n++){
giuliomoro@159: 		objAddrs[n] -> writeOutput(flush);
giuliomoro@153: 	}
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@153: void WriteFile::writeAllHeaders(){
giuliomoro@153: 	for(unsigned int n = 0; n < objAddrs.size(); n++){
giuliomoro@153: 		objAddrs[n] -> writeHeader();
giuliomoro@153: 	}
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@153: void WriteFile::writeAllFooters(){
giuliomoro@153: 	for(unsigned int n = 0; n < objAddrs.size(); n++){
giuliomoro@153: 		objAddrs[n] -> writeFooter();
giuliomoro@153: 	}
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@153: void WriteFile::writeHeader(){
giuliomoro@153: 	print(header);
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@153: void WriteFile::writeFooter(){
giuliomoro@153: 	print(footer);
giuliomoro@153: 	fflush(file);
giuliomoro@153: 	fclose(file);
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@153: void WriteFile::setHeader(const char* newHeader){
giuliomoro@153: 	allocateAndCopyString(newHeader, &header);
giuliomoro@153: 	sanitizeString(header);
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@153: void WriteFile::setFooter(const char* newFooter){
giuliomoro@153: 	allocateAndCopyString(newFooter, &footer);
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@153: void WriteFile::sanitizeString(char* string){
giuliomoro@153: 	for(int unsigned n = 0; n < strlen(string); n++){ //purge %'s from the string
giuliomoro@153: 		if(string[n] == '%'){
giuliomoro@153: 			string[n] = ' ';
giuliomoro@153: 		}
giuliomoro@153: 	}
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@153: void WriteFile::run(){
giuliomoro@153: 	threadRunning = true;
giuliomoro@153: 	writeAllHeaders();
giuliomoro@153: 	while(threadShouldExit()==false){
giuliomoro@159: 		writeAllOutputs(false);
giuliomoro@153: 		usleep(sleepTimeMs*1000);
giuliomoro@153: 	}
giuliomoro@159: 	writeAllOutputs(true);
giuliomoro@153: 	writeAllFooters(); // when ctrl-c is pressed, the last line is closed and the file is closed
giuliomoro@153: 	threadRunning = false;
giuliomoro@153: }
giuliomoro@153: 
giuliomoro@153: void WriteFile::allocateAndCopyString(const char* source, char** destination){
giuliomoro@153: 	free(*destination);
giuliomoro@153: 	*destination = (char*)malloc(sizeof(char) * (strlen(source) + 1));
giuliomoro@153: 	strcpy(*destination, source);
giuliomoro@153: }