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);
|
||
}
|