303 lines
9.4 KiB
C++
303 lines
9.4 KiB
C++
|
/* **************************************************************************
|
|||
|
|
|||
|
Author: Holger Stork, Achim Gaedke
|
|||
|
Created: January 2005
|
|||
|
|
|||
|
****************************************************************************/
|
|||
|
|
|||
|
#include "Eurotherm-2000Series.h"
|
|||
|
#include <cstdio>
|
|||
|
#include <cmath>
|
|||
|
#include <unistd.h>
|
|||
|
#include <fcntl.h>
|
|||
|
#include <termios.h>
|
|||
|
#include <errno.h>
|
|||
|
#include <locale.h>
|
|||
|
#include <string>
|
|||
|
|
|||
|
Eurotherm2000Series::Eurotherm2000Series(const std::string& dev_name, int dev_address, int failure_status_mask): tempcont() {
|
|||
|
/*
|
|||
|
initialization of serial device
|
|||
|
*/
|
|||
|
device_name=dev_name;
|
|||
|
failure_mask=failure_status_mask;
|
|||
|
serial_dev=open(dev_name.c_str(),O_RDWR|O_NONBLOCK);
|
|||
|
if (serial_dev<0)
|
|||
|
throw Eurotherm2000Series_error("could not open serial device");
|
|||
|
// set attributes now
|
|||
|
termios the_attributes;
|
|||
|
the_attributes.c_iflag=0;
|
|||
|
the_attributes.c_oflag=0;
|
|||
|
// 7 bit per byte, even parity, reciever enabled
|
|||
|
the_attributes.c_cflag=CS7|PARENB|CREAD;
|
|||
|
the_attributes.c_lflag=0;
|
|||
|
// baud rate
|
|||
|
cfsetospeed(&the_attributes,B19200);
|
|||
|
cfsetispeed(&the_attributes,B19200);
|
|||
|
if(tcsetattr(serial_dev,TCSANOW,&the_attributes)!=0)
|
|||
|
throw Eurotherm2000Series_error("could not configure serial line");
|
|||
|
address=dev_address;
|
|||
|
(void)setlocale(LC_NUMERIC,"C");
|
|||
|
fp_format="%05.1f";
|
|||
|
int_format="%05d.";
|
|||
|
hex_format=">%04x";
|
|||
|
std::map<std::string,std::string> config;
|
|||
|
// configure for <20>K output
|
|||
|
config["Q1"]="2.";
|
|||
|
// configure for nnn.n format output/precision
|
|||
|
config["QD"]="1.";
|
|||
|
configure(config);
|
|||
|
set_history_stepsize(1);
|
|||
|
device_failed=0;
|
|||
|
}
|
|||
|
|
|||
|
void Eurotherm2000Series::configure(const std::map<std::string,std::string>& config) {
|
|||
|
std::string error_message;
|
|||
|
try {
|
|||
|
std::string mode;
|
|||
|
const char config_mode[]="0002.";
|
|||
|
read_value("IM",mode);
|
|||
|
for (std::map<std::string,std::string>::const_iterator i=config.begin();i!=config.end();++i) {
|
|||
|
try {
|
|||
|
std::string value;
|
|||
|
read_value(i->first, value);
|
|||
|
fprintf(stderr, "%s: %s (%s)", i->first.c_str(), value.c_str(), i->second.c_str());
|
|||
|
if (value!=i->second) {
|
|||
|
if (mode!=config_mode) set_value("IM",config_mode);
|
|||
|
set_value(i->first,i->second);
|
|||
|
mode=config_mode;
|
|||
|
}
|
|||
|
}
|
|||
|
catch (Eurotherm2000Series_error e){error_message+=i->first+"->"+i->second+", ";}
|
|||
|
}
|
|||
|
if (mode==config_mode) {
|
|||
|
set_value("IM","0000.");
|
|||
|
// read superficial zero byte
|
|||
|
char buffer[1];
|
|||
|
while (read(serial_dev,buffer,1)<=0) sleep(1);
|
|||
|
// wait for restarted communication
|
|||
|
int timeout=10;
|
|||
|
while (1) {
|
|||
|
if (timeout<0) throw Eurotherm2000Series_error("could not establish connection after configuring");
|
|||
|
try {get_summary_status();} catch(Eurotherm2000Series_error){--timeout;sleep(1); continue;}
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
catch (const Eurotherm2000Series_error& e) {
|
|||
|
throw Eurotherm2000Series_error("got error while configuration: "+std::string(e.what()));
|
|||
|
}
|
|||
|
if (error_message.size()!=0) {
|
|||
|
throw Eurotherm2000Series_error("Could not set "+error_message.substr(0,error_message.size()-1));
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
void Eurotherm2000Series::reset() {
|
|||
|
set_value("IM","0000.");
|
|||
|
// read superficial zero byte
|
|||
|
char buffer[1];
|
|||
|
while (read(serial_dev,buffer,1)<=0) sleep(1);
|
|||
|
// wait for restarted communication
|
|||
|
int timeout=10;
|
|||
|
while (1) {
|
|||
|
if (timeout<0) throw Eurotherm2000Series_error("could not establish connection after reset");
|
|||
|
try {get_summary_status();} catch(Eurotherm2000Series_error){--timeout; sleep(1); continue;}
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Eurotherm2000Series::~Eurotherm2000Series() {
|
|||
|
/**
|
|||
|
close serial device, clean up
|
|||
|
*/
|
|||
|
close(serial_dev);
|
|||
|
}
|
|||
|
|
|||
|
void Eurotherm2000Series::read_value(const std::string& param_name, std::string& return_value) const{
|
|||
|
// assemble message without channel
|
|||
|
std::string message;
|
|||
|
message+=4;
|
|||
|
message+='0'+(address/10);
|
|||
|
message+='0'+(address/10);
|
|||
|
message+='0'+(address%10);
|
|||
|
message+='0'+(address%10);
|
|||
|
message+=param_name;
|
|||
|
message+=5;
|
|||
|
// write message to device
|
|||
|
pthread_mutex_lock((pthread_mutex_t*)&device_lock);
|
|||
|
write(serial_dev,message.c_str(),message.size());
|
|||
|
// read answer
|
|||
|
unsigned char got_byte=0;
|
|||
|
return_value.clear();
|
|||
|
// sleep minimum latency time for parameter reading
|
|||
|
timespec read_latency;
|
|||
|
read_latency.tv_sec=0; read_latency.tv_nsec=2000000;
|
|||
|
nanosleep(&read_latency,NULL);
|
|||
|
// now use shorter steps
|
|||
|
int timeout=100;
|
|||
|
read_latency.tv_nsec=1000000;
|
|||
|
while (return_value.size()<2 || return_value[return_value.size()-2]!=3) {
|
|||
|
int result=read(serial_dev,&got_byte,1);
|
|||
|
if (result<=0) {
|
|||
|
if (result<0 && errno!=EAGAIN) {
|
|||
|
return_value.clear();
|
|||
|
pthread_mutex_unlock((pthread_mutex_t*)&device_lock);
|
|||
|
throw Eurotherm2000Series_error("read_value: error while reading");
|
|||
|
}
|
|||
|
if (timeout<0) {
|
|||
|
return_value.clear();
|
|||
|
pthread_mutex_unlock((pthread_mutex_t*)&device_lock);
|
|||
|
throw Eurotherm2000Series_error("read_value: timeout while reading");
|
|||
|
}
|
|||
|
// go on sleeping for a while
|
|||
|
nanosleep(&read_latency,NULL);
|
|||
|
timeout--;
|
|||
|
continue;
|
|||
|
}
|
|||
|
if (return_value.size()==0 && got_byte==4) throw Eurotherm2000Series_error("read_value: invalid register");
|
|||
|
return_value+=got_byte;
|
|||
|
}
|
|||
|
pthread_mutex_unlock((pthread_mutex_t*)&device_lock);
|
|||
|
|
|||
|
if (0) {
|
|||
|
for (std::string::const_iterator i=return_value.begin();i!=return_value.end();++i)
|
|||
|
fprintf(stderr,"%02x",*i);
|
|||
|
fprintf(stderr,"\n");
|
|||
|
}
|
|||
|
// test bcc
|
|||
|
unsigned char bcc=return_value[1];
|
|||
|
for (size_t i=2;i<return_value.size()-1;++i) bcc^=return_value[i];
|
|||
|
if (bcc!=got_byte) {
|
|||
|
return_value.clear();
|
|||
|
throw Eurotherm2000Series_error("read_value: bcc missmatch");
|
|||
|
}
|
|||
|
|
|||
|
// compare C1 and C2
|
|||
|
if (return_value.substr(1,param_name.size())!=param_name) {
|
|||
|
return_value.clear();
|
|||
|
throw Eurotherm2000Series_error("read_value: register name echo mismatch");
|
|||
|
}
|
|||
|
// cut protocol bytes
|
|||
|
return_value.erase(0,3);
|
|||
|
return_value.resize(return_value.size()-2);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
int Eurotherm2000Series::set_value(const std::string& param_name, const std::string& value) {
|
|||
|
// comunicate with device
|
|||
|
std::string message;
|
|||
|
message+=4; //EOT
|
|||
|
message+='0'+(address/10);
|
|||
|
message+='0'+(address/10);
|
|||
|
message+='0'+(address%10);
|
|||
|
message+='0'+(address%10);
|
|||
|
message+=2; //STX
|
|||
|
message+=param_name.substr(0,2);
|
|||
|
message+=value;
|
|||
|
message+=3; //ETX
|
|||
|
|
|||
|
// calculate bcc byte
|
|||
|
unsigned char bcc=message[6];
|
|||
|
for (size_t i=7;i<message.size();++i) bcc^=message[i];
|
|||
|
message+=(unsigned char)bcc; //BCC
|
|||
|
|
|||
|
#if 0
|
|||
|
printf("param: %s, value: %s: ",param_name.c_str(),value.c_str());
|
|||
|
for (size_t i=0; i<message.size();i++)
|
|||
|
printf("%02x ",(unsigned char)message[i]);
|
|||
|
printf("\n");
|
|||
|
#endif
|
|||
|
|
|||
|
pthread_mutex_lock(&device_lock);
|
|||
|
write(serial_dev,message.c_str(),message.size());
|
|||
|
// sleep minimum latency time for value setting
|
|||
|
timespec write_latency;
|
|||
|
write_latency.tv_sec=0; write_latency.tv_nsec=5000000;
|
|||
|
nanosleep(&write_latency,NULL);
|
|||
|
// wait shorter while polling
|
|||
|
write_latency.tv_nsec=1000000;
|
|||
|
int timeout=1000;
|
|||
|
unsigned char got_byte;
|
|||
|
int result;
|
|||
|
while ((result=read(serial_dev,&got_byte,1))<=0) {
|
|||
|
if (result<0 && errno!=EAGAIN) {
|
|||
|
pthread_mutex_unlock(&device_lock);
|
|||
|
throw Eurotherm2000Series_error("set_value: read error");
|
|||
|
}
|
|||
|
if (timeout<0) {
|
|||
|
pthread_mutex_unlock(&device_lock);
|
|||
|
throw Eurotherm2000Series_error("set_value: timeout occurred");
|
|||
|
}
|
|||
|
nanosleep(&write_latency,NULL);
|
|||
|
timeout--;
|
|||
|
}
|
|||
|
pthread_mutex_unlock(&device_lock);
|
|||
|
if (got_byte==0x15) {
|
|||
|
char message_buffer[256];
|
|||
|
std::string ee_answer;
|
|||
|
read_value("EE",ee_answer);
|
|||
|
snprintf(message_buffer, 256, "set_value: negative acknoledge (ee=0x%02x)",ee_answer.c_str()[0]);
|
|||
|
throw Eurotherm2000Series_error(message_buffer);
|
|||
|
}
|
|||
|
if (got_byte!=6) {
|
|||
|
char message_buffer[256];
|
|||
|
snprintf(message_buffer,256,"(0x%02x)",got_byte);
|
|||
|
throw Eurotherm2000Series_error(std::string("set_value: no acknoledge ")+message_buffer);
|
|||
|
}
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
double Eurotherm2000Series::get_temperature() const {
|
|||
|
int summary_status=get_summary_status();
|
|||
|
if (failure_mask&summary_status) {
|
|||
|
char message_buffer[10];
|
|||
|
snprintf(message_buffer,10,"0x%04x",summary_status);
|
|||
|
throw Eurotherm2000Series_error(std::string("sensor/heater/temperature range fault: ")+message_buffer);
|
|||
|
}
|
|||
|
std::string answer;
|
|||
|
read_value("PV",answer);
|
|||
|
return strtod(answer.c_str(),NULL);
|
|||
|
}
|
|||
|
|
|||
|
double Eurotherm2000Series::set_setpoint(double ct) {
|
|||
|
char buffer[8];
|
|||
|
snprintf(buffer,8,fp_format.c_str(),ct);
|
|||
|
set_value("SL",buffer);
|
|||
|
// extra wait, until the get_setpoint function returns the same value
|
|||
|
timespec write_latency;
|
|||
|
write_latency.tv_sec=0; write_latency.tv_nsec=50000000;
|
|||
|
nanosleep(&write_latency,NULL);
|
|||
|
write_latency.tv_nsec=100000000;
|
|||
|
int i=100;
|
|||
|
while(fabs(get_setpoint()-ct)>1e-4) {
|
|||
|
if (i<0) throw Eurotherm2000Series_error("could not confirm setpoint value settings, timeout");
|
|||
|
nanosleep(&write_latency,NULL);
|
|||
|
--i;
|
|||
|
}
|
|||
|
return ct;
|
|||
|
}
|
|||
|
|
|||
|
double Eurotherm2000Series::get_setpoint() const {
|
|||
|
std::string answer;
|
|||
|
read_value("SL",answer);
|
|||
|
return strtod(answer.c_str(),NULL);
|
|||
|
}
|
|||
|
|
|||
|
int Eurotherm2000Series::get_summary_status() const {
|
|||
|
std::string answer;
|
|||
|
read_value("SO",answer);
|
|||
|
if (answer[0]!='>') throw Eurotherm2000Series_error("could not handle status data format \'"+answer+"\'");
|
|||
|
answer.erase(0,1);
|
|||
|
return strtol(answer.c_str(),NULL,16);
|
|||
|
}
|
|||
|
|
|||
|
void Eurotherm2000Series::get_setpoint_limits(double& min, double& max) const {
|
|||
|
std::string answer;
|
|||
|
read_value("LS",answer);
|
|||
|
min=strtod(answer.c_str(),NULL);
|
|||
|
read_value("HS",answer);
|
|||
|
max=strtod(answer.c_str(),NULL);
|
|||
|
}
|