damaris-backends/core/xml_result.cpp
2021-06-24 21:51:45 +02:00

627 lines
19 KiB
C++

#include "xml_result.h"
#include <cstdlib>
#include <xercesc/util/Base64.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/util/XercesDefs.hpp>
//#include <xercesc/dom/DOMWriter.hpp>
#include <xercesc/framework/MemBufFormatTarget.hpp>
/* ***************************************************************************************************
here starts the reader implementation
*****************************************************************************************************/
#ifndef SIZETPRINTFLETTER
# ifndef __LP64__
# define SIZETPRINTFLETTER "u"
# else
# define SIZETPRINTFLETTER "lu"
# endif
#endif
void start_element_stub(void* userdata,const XML_Char* name, const XML_Char** attrs );
void end_element_stub(void* userdata,const XML_Char* name);
void data_stub(void* userdata, const XML_Char* data, int len);
/**
table for translation base64 -> 0 to 63
*/
static char base64_table[] = {
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
};
class xml_result_handler {
/**
*/
int alloc_stepsize;
/**
maximum allocated size of buffer list
*/
size_t bufferno_max;
/**
actual or new sample buffer
*/
size_t bufferno;
/**
buffers
*/
short int** buffers;
/**
number of samples (2 values for one sample) in each buffer, if actual buffer, this is the expected number
*/
size_t* sampleno;
/**
*/
double* frequencies;
/**
maximum allocated size of last sample buffer in bytes
*/
size_t bufferlength;
/**
position of in actual sample buffer
*/
size_t bufferpos;
/**
base64 byte buffer
*/
char base64buffer[4];
/**
the length of content in base64buffer
*/
int base64buffer_length;
/**
1 if we are inside a adcdata section, 2 if we are saving a description
*/
int section_state;
/**
error messages, that are found
*/
std::string error_message;
/**
descripton sections
*/
std::string description_text;
public:
xml_result_handler(XML_Parser p) {
/* set event handlers */
alloc_stepsize=1<<10;
XML_SetElementHandler(p,start_element_stub,end_element_stub);
XML_SetCharacterDataHandler(p,data_stub);
XML_SetUserData(p,this);
/* allocate some buffers */
sampleno=(size_t*)malloc(alloc_stepsize*sizeof(size_t));
buffers=(short int**)malloc(alloc_stepsize*sizeof(short int*));
frequencies=(double*)malloc(alloc_stepsize*sizeof(double));
if (sampleno==NULL || buffers==NULL) {
if (sampleno!=NULL) free(sampleno);
if (buffers!=NULL) free(buffers);
if (frequencies!=NULL) free(frequencies);
/* todo throw something */
return;
}
bufferno=0;
buffers[0]=NULL;
bufferno_max=alloc_stepsize;
section_state=0;
}
void start_element(const XML_Char* name, const XML_Char** attrs) {
if (strcmp(name,"adcdata")==0) {
/* is there enough space in array? */
if (bufferno>=bufferno_max) {
bufferno_max+=alloc_stepsize;
buffers=(short int**)realloc((void*)buffers,bufferno_max*sizeof(short int*));
sampleno=(size_t*)realloc((void*)sampleno,bufferno_max*sizeof(size_t));
frequencies=(double*)realloc((void*)frequencies,bufferno_max*sizeof(double));
if (buffers==NULL || sampleno==NULL || frequencies==NULL) fprintf(stderr,"could not reallocate the buffer");
}
/* todo read sample no*/
const XML_Char** pos=attrs;
while (*pos!=NULL) {
if (strcmp(*pos,"samples")==0 || strcmp(*pos,"s")==0) {
sampleno[bufferno]=strtoul(*(pos+1),NULL,0);
}
else if (strcmp(*pos,"frequency")==0 || strcmp(*pos,"f")==0) {
frequencies[bufferno]=strtod(*(pos+1),NULL);
}
pos+=2;
}
if (sampleno[bufferno]!=0) {
bufferlength=sampleno[bufferno]*2*sizeof(short int);
/* fresh buffer */
buffers[bufferno]=(short int*)malloc(bufferlength);
}
else {
bufferlength=0;
buffers[bufferno]=NULL;
}
bufferpos=0;
/* todo check result */
base64buffer_length=0;
section_state=1;
}
if (strcmp(name,"description")) {
section_state=3;
}
if (strcmp(name,"error")==0) {
section_state=2;
}
if (section_state==3) {
description_text+="<";
description_text+=name;
const XML_Char** attr=attrs;
while(*attr!=NULL) {
description_text+=" ";
description_text+=*(attr++);
description_text+="='";
description_text+=*(attr++);
description_text+="'";
}
description_text+=">";
}
}
void end_element(const XML_Char* name) {
if (strcmp(name,"adcdata")==0) {
/* empty base64 buffer */
if (bufferpos+base64buffer_length-1>=bufferlength) {
bufferlength+=alloc_stepsize;
buffers[bufferno]=(short int*)realloc(buffers[bufferno],bufferlength);
/* todo reallocate check */
}
if (base64buffer_length>1) {
unsigned char s=base64buffer[0];
s<<=2;
s|=(base64buffer[1]>>4);
((char*)buffers[bufferno])[bufferpos]=s;
bufferpos++;
if (base64buffer_length>2) {
s=base64buffer[1]&15;
s<<=4;
s|=(base64buffer[2]>>2);
((char*)buffers[bufferno])[bufferpos]=s;
bufferpos++;
if (base64buffer_length>3) {
s=base64buffer[2]&3;
s<<=6;
s|=base64buffer[3];
((char*)buffers[bufferno])[bufferpos]=s;
bufferpos++;
}
}
}
/* check right sample number */
if (bufferpos!=sampleno[bufferno]*2*sizeof(short int)) {
sampleno[bufferno]=bufferpos/(2*sizeof(short int));
}
/* prepare for next buffer */
bufferlength=0;
bufferno++;
section_state=0;
}
if (strcmp(name,"error")==0) {
if (!error_message.empty() && error_message[error_message.size()-1]!='\n') {
error_message.push_back('\n');
}
section_state=0;
}
if (section_state==3) {
description_text+="<";
description_text+=name;
description_text+="/>";
}
if (strcmp(name,"description")) {
section_state=0;
}
}
void data(const XML_Char* the_data, int len) {
if (section_state==1) {
int pos=0;
while (pos<len) {
/* fill buffer */
while (base64buffer_length<4 && pos<len) {
if (((unsigned char)the_data[pos])<128) {
char value=base64_table[(unsigned char)the_data[pos]];
if (value!=-1) {
base64buffer[base64buffer_length]=value;
base64buffer_length++;
}
}
pos++;
}
/* interprete buffer, that has 4*6 bits=3 byte */
if (base64buffer_length==4) {
/* check the space */
if (bufferpos+3>=bufferlength) {
/* to do reallocate */
bufferlength+=alloc_stepsize;
buffers[bufferno]=(short int*)realloc(buffers[bufferno],bufferlength);
if (buffers[bufferno]==NULL) fprintf(stderr,"could not enlarge the sample buffer\n");
}
/* read three bytes from four letters */
unsigned char s=base64buffer[0];
s<<=2;
s|=(base64buffer[1]>>4);
((char*)buffers[bufferno])[bufferpos]=s;
bufferpos++;
s=base64buffer[1]&15;
s<<=4;
s|=(base64buffer[2]>>2);
((char*)buffers[bufferno])[bufferpos]=s;
bufferpos++;
s=base64buffer[2]&3;
s<<=6;
s|=base64buffer[3];
((char*)buffers[bufferno])[bufferpos]=s;
bufferpos++;
base64buffer_length=0;
}
}
}
else if (section_state==3) {
description_text+=the_data;
}
else if (section_state==2) {
error_message+=the_data;
}
}
~xml_result_handler() {
if (sampleno!=NULL && buffers!=NULL) {
size_t pos=0;
while (pos<=bufferno) {
if (buffers[pos]!=NULL) free(buffers[pos]);
pos++;
}
}
if (sampleno!=NULL) free(sampleno);
if (buffers!=NULL) free(buffers);
}
/**
print number of samples and frequency of the obtained data
*/
void print_info(FILE* f) {
if (buffers==NULL || sampleno==NULL) {
fprintf(f,"nothing saved\n");
return;
}
for (size_t i=0; i<bufferno; i++) {
fprintf(f,"%" SIZETPRINTFLETTER ": %" SIZETPRINTFLETTER " samples %f Hz frequency\n",i,sampleno[i],frequencies[i]);
if (buffers[i]!=NULL) {
for (size_t j=0; j<sampleno[i]; j++) {
short int a=buffers[i][j*2];
short int b=buffers[i][j*2+1];
fprintf(f,"%hd %hd\n",a,b);
}
}
}
}
};
void start_element_stub(void* userdata,const XML_Char* name, const XML_Char** attrs ) {
((xml_result_handler*)userdata)->start_element(name,attrs);
}
void end_element_stub(void* userdata,const XML_Char* name) {
((xml_result_handler*)userdata)->end_element(name);
}
void data_stub(void* userdata, const XML_Char* data, int len ) {
((xml_result_handler*)userdata)->data(data,len);
}
xml_result_reader::xml_result_reader() {
jobfilenamepattern="";
}
xml_result_reader::~xml_result_reader() {
}
xml_result_reader::xml_result_reader(const std::string& jobpattern) {
jobfilenamepattern=jobpattern;
}
/* ToDo error handling */
result* xml_result_reader::read(size_t no) {
if (jobfilenamepattern=="") return NULL;
char* tmp_name=(char*)malloc(jobfilenamepattern.size()+50);
if (tmp_name==NULL) return NULL;
snprintf(tmp_name,jobfilenamepattern.size()+50,jobfilenamepattern.c_str(),no);
result* new_result=NULL;
new_result=read(std::string(tmp_name));
free(tmp_name);
return new_result;
}
result* xml_result_reader::read(const std::string& jobfilename) {
/*open file*/
FILE* result_file=fopen(jobfilename.c_str(),"rb");
if (result_file==NULL) return NULL;
XML_Parser parser=XML_ParserCreate(NULL);
if (parser==NULL) {
fclose(result_file);
return NULL;
}
xml_result_handler h(parser);
for (;;) {
const size_t BUFF_SIZE=1<<10;
int bytes_read;
void *buff = XML_GetBuffer(parser, BUFF_SIZE);
if (buff == NULL) {
fprintf(stderr, "could not get the buffer from XML Parser\n");
fclose(result_file);
return NULL;
}
bytes_read = fread(buff, 1, BUFF_SIZE, result_file);
if (bytes_read < 0) {
/* handle error */
fprintf(stderr,"error while reading\n");
break;
}
if (! XML_ParseBuffer(parser, bytes_read, bytes_read == 0)) {
/* handle parse error */
fprintf(stderr,"error while parsing line %d: %s\n",static_cast<int>(XML_GetCurrentLineNumber(parser)),XML_ErrorString(XML_GetErrorCode(parser)));
break;
}
// peaceful end
if (bytes_read == 0) break;
}
fclose(result_file);
return NULL;
}
/* ************************************************************************************************
here starts the writer implementation
**************************************************************************************************/
xml_result_writer::xml_result_writer(data_save_mode_type how) {
resultfilename_pattern="";
data_save_mode=how;
}
xml_result_writer::xml_result_writer(const std::string& pattern, data_save_mode_type how) {
resultfilename_pattern=pattern;
data_save_mode=how;
}
int xml_result_writer::write_to_file(const std::string& filename, const result* res) const {
// find the way to write
const adc_result* adc_res=dynamic_cast<const adc_result*>(res);
if (adc_res!=NULL) {
write_adc_to_file(filename,adc_res);
return 0;
}
const adc_results* adc_ress=dynamic_cast<const adc_results*>(res);
if (adc_ress!=NULL) {
write_adcs_to_file(filename, adc_ress);
return 0;
}
const configuration_results* config_ress=dynamic_cast<const configuration_results*>(res);
if (config_ress!=NULL) {
write_configuration_results_to_file(filename, *config_ress);
return 0;
}
const error_result* err_res=dynamic_cast<const error_result*>(res);
if (err_res!=NULL) write_error_to_file(filename,err_res);
else
write_unknown_to_file(filename, res);
return 0;
}
int xml_result_writer::write_unknown_to_file(const std::string& filename, const result* res) const {
FILE* out=fopen(filename.c_str(),"w");
if (out==0) fprintf(stderr,"could not open file %s\n",filename.c_str());
fprintf(out,"<?xml version=\"1.0\"?>\n");
fprintf(out,"<result job=\"%" SIZETPRINTFLETTER "\">\n",res->job_no);
if (res==NULL)
fprintf(out,"<!-- got NULL pointer result... -->\n");
else
fprintf(out,"<!-- don't know how to print result... -->\n");
fprintf(out,"</result>\n");
fclose(out);
return 0;
}
int xml_result_writer::write_error_to_file(const std::string& filename, const error_result* res) const {
/* write an extra message to stderr */
fprintf(stderr,"job %" SIZETPRINTFLETTER ": %s\n",res->job_no,res->error_message.c_str());
FILE* out=fopen(filename.c_str(),"w");
if (out==0) {
fprintf(stderr,"could not open file %s\n",filename.c_str());
return 0;
}
fprintf(out,"<?xml version=\"1.0\"?>\n");
fprintf(out,"<result job=\"%" SIZETPRINTFLETTER "\">\n",res->job_no);
fprintf(out," <error>%s</error>\n",res->error_message.c_str());
fprintf(out,"</result>");
fclose(out);
return 0;
}
int xml_result_writer::write_configuration_results_to_file(const std::string& filename, const configuration_results& ress) const {
FILE* out=fopen(filename.c_str(),"w");
fprintf(out,"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
fprintf(out,"<result job=\"%" SIZETPRINTFLETTER "\">\n<configuration>\n",ress.job_no);
XMLCh tempStr[300];
xercesc::XMLString::transcode("LS", tempStr, 299);
xercesc::DOMImplementation *impl2 = xercesc::DOMImplementationRegistry::getDOMImplementation(tempStr);
xercesc::DOMLSSerializer *theSerializer=(static_cast<xercesc::DOMImplementationLS*>(impl2))->createLSSerializer();
xercesc::DOMLSOutput *theOutputDesc=(static_cast<xercesc::DOMImplementationLS*>(impl2))->createLSOutput();
for (configuration_results::const_iterator i=ress.begin(); i!=ress.end(); ++i)
if ((*i)->tag->getDocumentElement()!=NULL) {
xercesc::MemBufFormatTarget mem;
theOutputDesc->setByteStream(&mem);
xercesc::DOMNode *the_node = (*i)->tag->getDocumentElement();
theSerializer->write(the_node ,theOutputDesc);
//theSerializer->writeNode(&mem,*((*i)->tag->getDocumentElement()));
fwrite((char*)mem.getRawBuffer(),sizeof(char),mem.getLen(),out);
mem.reset();
}
theSerializer->release();
// todo
fprintf(out,"</configuration>\n</result>\n");
fclose(out);
return 0;
}
int xml_result_writer::write_adcs_to_file(const std::string& filename, const adc_results* ress) const {
// fall back to some default mode
data_save_mode_type how=data_save_mode;
if (how==defaultmode) how=ascii;
FILE* out=fopen(filename.c_str(),"w");
if (out==0) {
fprintf(stderr,"could not open file %s\n",filename.c_str());
return 0;
}
fprintf(out,"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
fprintf(out,"<result job=\"%" SIZETPRINTFLETTER "\">\n",ress->job_no);
fwrite(ress->description.c_str(),ress->description.size(),1,out);
fprintf(out,"\n");
int num_res=0;
for (adc_results::const_iterator res=ress->begin(); res!=ress->end(); ++res) {
switch (how) {
case separate_file:
{
// write separate data file
char result_filename[1<<10];
snprintf(result_filename,sizeof(result_filename),"adc.%09" SIZETPRINTFLETTER ".%d.bin",ress->job_no,num_res);
write_adcdata_separate(out, std::string(result_filename), *res);
}
break;
case base64:
write_adcdata_base64(out,*res);
break;
case csv:
write_adcdata_formated(out,"%d; %d\n",*res);
break;
case ascii:
write_adcdata_formated(out,"%d\t%d\n",*res);
break;
default:
/* forget data */
fprintf(stderr,"forgeting adc data\n");
}
num_res++;
}
fprintf(out,"</result>\n");
fclose(out);
return 0;
}
int xml_result_writer::write_adc_to_file(const std::string& filename, const adc_result* res) const {
// fall back to some default mode
data_save_mode_type how=data_save_mode;
if (how==defaultmode) how=ascii;
FILE* out=fopen(const_cast<char*>(filename.c_str()),"w");
if (out==0) {
fprintf(stderr,"write_adc_to_file: could not open file %s\n",filename.c_str());
return 0;
}
fprintf(out,"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
fprintf(out,"<result job=\"%" SIZETPRINTFLETTER "\">\n",res->job_no);
fwrite(res->description.c_str(),res->description.size(),1,out);
fprintf(out,"\n");
switch (how) {
case separate_file:
{
// write separate data file
write_adcdata_separate(out, filename+".bin", res);
}
break;
case base64:
write_adcdata_base64(out,res);
break;
case csv:
write_adcdata_formated(out,"%d; %d\n",res);
break;
case ascii:
write_adcdata_formated(out,"%d\t%d\n",res);
break;
default:
/* forget data */
fprintf(stderr,"forgetting adc data\n");
}
fprintf(out,"</result>\n");
fclose(out);
return 0;
}
int xml_result_writer::write_adcdata_separate(FILE* out, const std::string& datafilename, const adc_result* res) const {
/* todo: error handling */
FILE* binout=fopen(datafilename.c_str(),"wb");
if (binout==0) {
fprintf(stderr,"could not open file %s\n",datafilename.c_str());
return 0;
}
fwrite(res->data, sizeof(short int)*res->nchannels, res->samples, binout);
fclose(binout);
fprintf(out,
"<adcdatafile path=\"%s\" samples=\"%" SIZETPRINTFLETTER "\" rate=\"%g\" channels=\"%i\"/>\n",
datafilename.c_str(),
res->samples,
res->sampling_frequency,
res->nchannels);
return 0;
}
int xml_result_writer::write_adcdata_formated(FILE* out, const std::string& format, const adc_result* res) const {
fprintf(out,"<adcdata samples=\"%" SIZETPRINTFLETTER "\" rate=\"%g\" channels=\"%i\">\n",res->samples,res->sampling_frequency, res->nchannels);
for(size_t i=0;i<res->samples;++i) {
for(int j=0;j<res->nchannels;++j) {
fprintf(out, format.c_str(), res->data[i*2+ j]);
}
}
fprintf(out,"</adcdata>\n");
return 0;
}
int xml_result_writer::write_adcdata_base64(FILE* out, const adc_result* res) const {
xercesc::XMLPlatformUtils::Initialize();
fprintf(out,"<adcdata samples=\"%" SIZETPRINTFLETTER "\" rate=\"%g\" channels=\"%i\">\n",res->samples,res->sampling_frequency, res->nchannels);
unsigned int base64length=0;
XMLByte* base64buffer=xercesc::Base64::encode( reinterpret_cast<XMLByte*>(res->data),
res->samples*res->nchannels*sizeof(short int),
reinterpret_cast<XMLSize_t*>(&base64length),
xercesc::XMLPlatformUtils::fgMemoryManager );
fwrite(base64buffer,1,base64length,out);
fprintf(out,"</adcdata>\n");
xercesc::XMLPlatformUtils::fgMemoryManager->deallocate(base64buffer);
return 0;
}