251 lines
8.8 KiB
C++
251 lines
8.8 KiB
C++
|
#include "tempcont.h"
|
||
|
#include <cmath>
|
||
|
#include "errno.h"
|
||
|
#include <xercesc/util/XMLString.hpp>
|
||
|
#include <xercesc/dom/DOMElement.hpp>
|
||
|
|
||
|
void temp_history::print_xml(FILE* f) const {
|
||
|
if (step==0) {fprintf(f,"<temperature/>\n");}
|
||
|
char timebuffer[30];
|
||
|
ctime_r(&latest,timebuffer);
|
||
|
timebuffer[strlen(timebuffer)-1]=0;
|
||
|
fprintf(f,"<temperature time=\"%s\" step=\"%d\">",timebuffer,step);
|
||
|
if (!empty()) {
|
||
|
fprintf(f,"\n");
|
||
|
for (const_iterator i=begin(); i!=end();++i) fprintf(f,"%g ",*i);
|
||
|
fprintf(f,"\n");
|
||
|
}
|
||
|
fprintf(f,"</temperature>\n");
|
||
|
}
|
||
|
|
||
|
configuration_result* temp_history::as_result() const {
|
||
|
configuration_result* res=new configuration_result(0);
|
||
|
XMLCh* myname=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("temphistory");
|
||
|
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* newelement=res->tag->createElement(myname);
|
||
|
res->tag->insertBefore(newelement,NULL);
|
||
|
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&myname);
|
||
|
|
||
|
// print step [s]
|
||
|
XMLCh* step_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("step");
|
||
|
char step_value_char_buf[100];
|
||
|
snprintf(step_value_char_buf,sizeof(step_value_char_buf),"%d",step);
|
||
|
XMLCh* step_value=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(step_value_char_buf);
|
||
|
newelement->setAttribute(step_name,step_value);
|
||
|
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&step_name);
|
||
|
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&step_value);
|
||
|
// print latest timestamp
|
||
|
XMLCh* latest_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("latest");
|
||
|
char latest_value_char_buf[200];
|
||
|
struct tm latest_broken_timeinfo;
|
||
|
localtime_r(&latest, &latest_broken_timeinfo);
|
||
|
strftime(latest_value_char_buf,sizeof(latest_value_char_buf),"%Y%m%d %H:%M:%S",&latest_broken_timeinfo);
|
||
|
XMLCh* latest_value=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(latest_value_char_buf);
|
||
|
newelement->setAttribute(latest_name,latest_value);
|
||
|
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&latest_name);
|
||
|
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&latest_value);
|
||
|
|
||
|
// append values to buffer
|
||
|
if (!empty()) {
|
||
|
for (const_iterator i=begin(); i!=end(); ++i){
|
||
|
char temp_char_buf[256];
|
||
|
snprintf(temp_char_buf,sizeof(temp_char_buf),"%g\n",*i);
|
||
|
XMLCh* temp_line=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(temp_char_buf);
|
||
|
XERCES_CPP_NAMESPACE_QUALIFIER DOMText* textnode=res->tag->createTextNode(temp_line);
|
||
|
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&temp_line);
|
||
|
newelement->insertBefore(textnode,NULL);
|
||
|
}
|
||
|
}
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
void* history_maintainance_thread(void* temperature_sensor) {
|
||
|
tempcont* ts=(tempcont*)temperature_sensor;
|
||
|
// wait for completely initialised structure
|
||
|
ts->maintain_history();
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
temp_history* tempcont::get_history(size_t seconds_back) const {
|
||
|
temp_history* result_history=new temp_history();
|
||
|
if (result_history==NULL) throw tempcont_error("failed to create new temp_history object");
|
||
|
if (history_step!=0) {
|
||
|
pthread_mutex_lock((pthread_mutex_t*)&history_lock);
|
||
|
for (size_t i=0; ((seconds_back==0 || i*history_step<seconds_back) && i<history_used); ++i)
|
||
|
result_history->push_back(history_buffer[(history_latest_index+history_length-i)%history_length]);
|
||
|
pthread_mutex_unlock((pthread_mutex_t*)&history_lock);
|
||
|
result_history->step=history_step;
|
||
|
result_history->latest=history_latest_time;
|
||
|
}
|
||
|
return result_history;
|
||
|
}
|
||
|
|
||
|
void tempcont::set_history_stepsize(size_t step) {
|
||
|
if (history_step==step) return;
|
||
|
pthread_mutex_lock((pthread_mutex_t*)&history_lock);
|
||
|
history_step=step;
|
||
|
if (step==0) {
|
||
|
// reinitalise history
|
||
|
history_used=0;
|
||
|
}
|
||
|
else {
|
||
|
history_latest_index=0;
|
||
|
history_used=1;
|
||
|
history_buffer[0]=get_temperature();
|
||
|
history_latest_time=time(NULL);
|
||
|
}
|
||
|
pthread_mutex_unlock((pthread_mutex_t*)&history_lock);
|
||
|
}
|
||
|
|
||
|
double tempcont::wait_setpoint_reached(double delta, size_t timeout) const {
|
||
|
// poll temperature every second
|
||
|
time_t end_polling=time(NULL)+timeout;
|
||
|
double setpoint=get_setpoint();
|
||
|
double temperature=get_temperature();
|
||
|
while (fabs(temperature-setpoint)>delta && (timeout==0 || end_polling>=time(NULL))) {
|
||
|
sleep(1);
|
||
|
temperature=get_temperature();
|
||
|
}
|
||
|
return temperature;
|
||
|
}
|
||
|
|
||
|
void tempcont::maintain_history() {
|
||
|
// wait for initialisation of derived class or failure
|
||
|
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
|
||
|
// sleep 0.2 seconds, that assures a check every second
|
||
|
timespec sleeptime;
|
||
|
sleeptime.tv_sec=0;
|
||
|
sleeptime.tv_nsec=200*1000*1000;
|
||
|
while (1) {
|
||
|
// do not maintain history until step is set to a reasonable value
|
||
|
if (history_step!=0) {
|
||
|
pthread_mutex_lock(&history_lock);
|
||
|
time_t now=time(NULL);
|
||
|
if (history_step<=difftime(now, history_latest_time)) {
|
||
|
double new_temp;
|
||
|
try {
|
||
|
new_temp=get_temperature();
|
||
|
}
|
||
|
catch (tempcont_error e) {
|
||
|
pthread_mutex_unlock(&history_lock);
|
||
|
fprintf(stderr,"history maintainance thread caught exception: %s, terminating\n",e.what());
|
||
|
device_failed=1;
|
||
|
pthread_exit(NULL);
|
||
|
}
|
||
|
// append the new temperature value
|
||
|
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
|
||
|
history_latest_time=now;
|
||
|
if (history_used==0) {
|
||
|
history_latest_index=0;
|
||
|
history_buffer[0]=new_temp;
|
||
|
history_used=1;
|
||
|
}
|
||
|
else {
|
||
|
history_latest_index++;
|
||
|
if (history_latest_index==history_length) history_latest_index=0;
|
||
|
history_buffer[history_latest_index]=new_temp;
|
||
|
if (history_used<history_length) history_used++;
|
||
|
}
|
||
|
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
|
||
|
pthread_mutex_unlock(&history_lock);
|
||
|
// continue directly with next check
|
||
|
continue;
|
||
|
}
|
||
|
else {
|
||
|
pthread_mutex_unlock(&history_lock);
|
||
|
}
|
||
|
}
|
||
|
nanosleep(&sleeptime,NULL);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
configuration_result* tempcont::configure(const configuration_device_section& conf, int run) {
|
||
|
|
||
|
std::map<const std::string,std::string>::const_iterator get=conf.attributes.find("get");
|
||
|
if (get!=conf.attributes.end()) {
|
||
|
temp_history* h=get_history(0);
|
||
|
configuration_result* res=h->as_result();
|
||
|
delete h;
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
std::map<const std::string,std::string>::const_iterator set=conf.attributes.find("set");
|
||
|
if (set!=conf.attributes.end()) {
|
||
|
// read new temperature setpoint
|
||
|
const char* temp_str=set->second.c_str();
|
||
|
char* temp_str_end=NULL;
|
||
|
double new_temperature=strtod(temp_str,&temp_str_end);
|
||
|
if (temp_str_end!=set->second.size()+temp_str) {
|
||
|
throw tempcont_error("could not read new temperature setpoint from attributes");
|
||
|
}
|
||
|
// set the temperature
|
||
|
double new_temp_return=set_setpoint(new_temperature);
|
||
|
if (new_temp_return!=new_temperature) {
|
||
|
char error_message[256];
|
||
|
snprintf(error_message, sizeof(error_message), "could not set new temperature setpoint %f!=%f",
|
||
|
new_temp_return,
|
||
|
new_temperature);
|
||
|
throw tempcont_error(error_message);
|
||
|
}
|
||
|
|
||
|
// report success
|
||
|
configuration_result* res=new configuration_result(0);
|
||
|
XMLCh* myname=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(conf.name.c_str());
|
||
|
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* newelement=res->tag->createElement(myname);
|
||
|
res->tag->insertBefore(newelement,NULL);
|
||
|
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&myname);
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
tempcont::tempcont() {
|
||
|
// initialise history
|
||
|
history_step=0;
|
||
|
// one day history (1s steps)
|
||
|
history_length=24*60*60;
|
||
|
history_used=0;
|
||
|
history_buffer=(double*)malloc(sizeof(double)*history_length);
|
||
|
if (history_buffer==NULL) throw tempcont_error("failed to allocate history");
|
||
|
// no failures detected
|
||
|
device_failed=0;
|
||
|
// create history lock
|
||
|
pthread_mutexattr_t history_lock_attr;
|
||
|
pthread_mutexattr_init(&history_lock_attr);
|
||
|
pthread_mutex_init(&history_lock,&history_lock_attr);
|
||
|
pthread_mutexattr_destroy(&history_lock_attr);
|
||
|
// create device lock
|
||
|
pthread_mutexattr_t device_lock_attr;
|
||
|
pthread_mutexattr_init(&device_lock_attr);
|
||
|
pthread_mutex_init(&device_lock,&device_lock_attr);
|
||
|
pthread_mutexattr_destroy(&device_lock_attr);
|
||
|
// create history update thread
|
||
|
pthread_attr_t history_thread_attr;
|
||
|
pthread_attr_init(&history_thread_attr);
|
||
|
int result=pthread_create(&history_thread,&history_thread_attr,&history_maintainance_thread,this);
|
||
|
pthread_attr_destroy(&history_thread_attr);
|
||
|
// clean up, it this operation failed
|
||
|
if (result!=0) {
|
||
|
free(history_buffer);
|
||
|
pthread_mutex_destroy(&device_lock);
|
||
|
pthread_mutex_destroy(&history_lock);
|
||
|
throw tempcont_error("could not create history maintainance thread");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
tempcont::~tempcont() {
|
||
|
// send cancelation
|
||
|
if (0!=pthread_cancel(history_thread) && errno!=EIO) {
|
||
|
fprintf(stderr,"thread cancelation failed\n");
|
||
|
}
|
||
|
// wait for terminated thread
|
||
|
if (pthread_join(history_thread,NULL)!=0){
|
||
|
fprintf(stderr,"thread joining failed\n");
|
||
|
};
|
||
|
if (history_buffer!=NULL) free(history_buffer);
|
||
|
pthread_mutex_destroy(&device_lock);
|
||
|
pthread_mutex_destroy(&history_lock);
|
||
|
}
|