Fotobot
Get data from your photovoltaic plant
fronius.cpp
Go to the documentation of this file.
1 
7 #include "fronius.h"
8 #include "fronius_request.h"
9 #include <QCoreApplication>
10 #include <QDebug>
11 #include <QEventLoop>
12 #include "string.h"
13 
14 
15 FRONIUS::~FRONIUS() {
16 }
17 
25  m_port = NULL;
26 }
27 
28 
37  m_timer = new QTimer(this);
38  m_timer->setInterval(10);
39  m_timer->setSingleShot(true);
40  connect(m_timer, SIGNAL(timeout()),
41  this, SLOT(loop()));
42 
43  m_number_of_err=0;
44  m_number_of_ok=0;
46 
47  open();
48 }
49 
50 
51 void FRONIUS::slotQuit() {
52  if (m_port) {
53  m_port->close();
54  delete m_port;
55  }
56 }
57 
66 void FRONIUS::open() {
67  BaudRateType speed = BAUD9600;
68  switch (m_line.speed) {
69  case 110: speed = BAUD110; break;
70  case 300: speed = BAUD300; break;
71  case 600: speed = BAUD600; break;
72  case 1200: speed = BAUD1200; break;
73  case 2400: speed = BAUD2400; break;
74  case 4800: speed = BAUD4800; break;
75  case 9600: speed = BAUD9600; break;
76  case 19200: speed = BAUD19200; break;
77  case 38400: speed = BAUD38400; break;
78  case 57600: speed = BAUD57600; break;
79  case 115200: speed = BAUD115200; break;
80  }
81  PortSettings spset;
82  spset.BaudRate = speed;
83  spset.DataBits = DATA_8;
84  spset.Parity = PAR_NONE;
85  spset.StopBits = STOP_1;
86  spset.FlowControl = FLOW_OFF;
87  spset.Timeout_Millisec = m_line.timeout;
88 
89  m_port = new QextSerialPort(m_line.device, spset, QextSerialPort::Polling);
90  m_port->open(QIODevice::ReadWrite);
91  if (m_port == NULL || !m_port->isOpen()) {
92  // Send the signal data() for every invertor with error message and return
93  DBT_DATA err;
94  err.error = tr("Could not open %1: %2").arg(m_line.device).arg(m_port->errorString());
95  for (int i=0; i<m_invertors.size(); i++) {
96  err.invertor = m_invertors.at(i).invertor;
97  emit data(err);
98  }
99  setStatus(
100  0,
101  0,
102  tr("Open"),
103  tr("Could not open serial port %1").arg(m_line.device)
104  );
105  QTimer::singleShot(3333, this, SLOT(open()));
106  return;
107  }
108 
109  m_port->flush();
110  m_timer->start();
111  m_retries = m_line.retries;
112 }
113 
114 
128  if (m_invertors.size() <= 0) return;
129 
130  readInvertor();
131 
133  if (m_invertors.size() <= m_current_invertor_index) {
134  int secs = 3;
136  setStatus(
137  0,
138  0,
139  tr("No command"),
140  tr("Sleeping %1 secs to begin new reading cycle").arg(secs)
141  );
142  m_timer->setInterval(secs*1000); // wait a few seconds after whole cycle
143  emit loopFinished(m_number_of_ok, m_number_of_err);
144  m_number_of_err=0;
145  m_number_of_ok=0;
146  } else {
147  setStatus(
149  m_retries-m_nr+1,
151  tr("Going to read next invertor")
152  );
153  m_timer->setInterval(10);
154  }
155 
156  m_timer->start();
157 }
158 
159 
167  int n;
168 
169  // Read header of the datagram
170  setStatus(
172  m_retries-m_nr+1,
174  tr("Reading header from serial port")
175  );
176  n = m_port->read((char *)&response, FRONIUS_response::HEADER_LENGTH );
177 
178  // Check header length
179  if (n != FRONIUS_response::HEADER_LENGTH) {
180  throw tr("Header invalid: %1 bytes read, should be %2.")
181  .arg(n)
182  .arg(FRONIUS_response::HEADER_LENGTH)
183  ;
184  }
185 
186  // Check header structure
187  if (!response.isHeaderValid()) {
188  throw tr("Header invalid.");
189  }
190 
191  // Read rest of the datagram
192  setStatus(
194  m_retries-m_nr+1,
196  tr("Reading rest of datagram from serial port")
197  );
198  n = m_port->read((char *)response.buffer.buffer, response.length+1);
199 
200  // Is the checksum ok?
201  if (!response.isChecksumValid()) {
202  throw tr("Response checksum error");
203  }
204 
205  // Did the right invertor answered?
206  int address = m_invertors.at(m_current_invertor_index).address;
207  if (address >= 1000) {
208  address = address - 1000;
209  }
210  if (response.device_number != address) {
211  throw tr("Wrong invertor address in response: should be: %1, read: %2")
212  .arg(m_invertors.at(m_current_invertor_index).address)
213  .arg(response.device_number)
214  ;
215  }
216 
217  // Is there an error?
218  if (response.command == FRONIUS_ERROR) {
219  QString errmsg = decodeError((FRONIUS::Error )response.buffer.Error.errorInformation);
220  throw tr("Error: %1").arg(errmsg);
221  }
222 }
223 
224 
238  // Store current command
239  m_command = command;
240 
241  // Create FRONIUS_request datagram and write it to the serial port and flush all data
242  setStatus(
244  m_retries-m_nr+1,
246  tr("Writing command to serial port")
247  );
248  int address = m_invertors.at(m_current_invertor_index).address;
249  if (address >= 1000) {
250  address = address - 1000;
251  }
252 
253 // m_port->read(32); // Přečíst případné nesmysly
254 
255  FRONIUS_request request( address, command );
256  m_port->write((char *)&request, sizeof(request));
257  m_port->flush();
258 
259  QEventLoop eventLoop;
260  QTimer::singleShot(300, &eventLoop, SLOT(quit()));
261  eventLoop.exec();
262 
263  // Read response
264  FRONIUS_response response;
265  readResponse (response);
266 
267  // Decode resonse and decode values
268  return decodeResponse(response, command);
269 }
270 
271 
277  if (command != response.command) {
278  QString reqcmd = decodeCommand((FRONIUS::Command)m_command);
279  QString reccmd = decodeCommand(command);
280  throw tr("Bad response: requested command: %1 response: %2 ").arg(reqcmd).arg(reccmd);
281  }
282 
283  QVariant x;
284  try {
285  switch (command) {
286  case FRONIUS_CMD_GET_VERSION:
287  x = QString("%1 %2.%3.%4")
288  .arg(response.buffer.Version.ifcType)
289  .arg(response.buffer.Version.versionMajor)
290  .arg(response.buffer.Version.versionMinor)
291  .arg(response.buffer.Version.versionRelease)
292  ;
293  break;
294  case FRONIUS_CMD_GET_DEVICE_TYPE:
295  x = response.buffer.DeviceType.typeIdentification;
296  break;
297  case FRONIUS_CMD_GET_DATE_TIME:
298  x = response.value().toDouble() / 60.0;
299  break;
300  case FRONIUS_CMD_GET_ACTIVE_INVERTOR_NUMBER:
301  case FRONIUS_CMD_GET_POWER_NOW:
302  case FRONIUS_CMD_GET_AC_CURRENT_NOW:
303  case FRONIUS_CMD_GET_AC_VOLTAGE_NOW:
304  case FRONIUS_CMD_GET_AC_FREQUENCY_NOW:
305  case FRONIUS_CMD_GET_DC_CURRENT_NOW:
306  case FRONIUS_CMD_GET_DC_VOLTAGE_NOW:
307  case FRONIUS_CMD_GET_ENERGY_DAY:
308  case FRONIUS_CMD_GET_MAXIMUM_POWER_DAY:
309  case FRONIUS_CMD_GET_MAXIMUM_AC_VOLTAGE_DAY:
310  case FRONIUS_CMD_GET_MINIMUM_AC_VOLTAGE_DAY:
311  case FRONIUS_CMD_GET_MAXIMUM_DC_VOLTAGE_DAY:
312  case FRONIUS_CMD_GET_ENERGY_TOTAL:
313  case FRONIUS_CMD_GET_MAXIMUM_POWER_TOTAL:
314  case FRONIUS_CMD_GET_MAXIMUM_AC_VOLTAGE_TOTAL:
315  case FRONIUS_CMD_GET_MINIMUM_AC_VOLTAGE_TOTAL:
316  case FRONIUS_CMD_GET_MAXIMUM_DC_VOLTAGE_TOTAL:
317  case FRONIUS_CMD_GET_TEMPERATURE_1:
318  case FRONIUS_CMD_GET_TEMPERATURE_2:
319  case FRONIUS_CMD_GET_IRRADIANCE:
320  x = response.value();
321  break;
322  case FRONIUS_CMD_GET_OPERATING_HOURS_DAY:
323  case FRONIUS_CMD_GET_OPERATING_HOURS_TOTAL:
324  x = response.value().toDouble() / 60.0;
325  break;
326  case FRONIUS_CMD_GET_ENERGY_YEAR:
327  case FRONIUS_CMD_GET_YIELD_YEAR:
328  case FRONIUS_CMD_GET_YIELD_DAY:
329  break;
330  case FRONIUS_CMD_GET_INVERTOR_STATUS:
331  // x = decodeStatus((FRONIUS::Status)response.buffer.Status.status);
332  // x = response.buffer.Status.status;
333  x = decodeStatus((FRONIUS::Status)response.value().toInt());
334  break;
335  };
336  return x;
337  }
338  catch (QString e) {
339  throw tr("Value cannot be recognized: %1")
340  .arg(e)
341  ;
342  }
343 }
344 
345 
360  const DBT_INVERTORS& invertor = m_invertors.at(m_current_invertor_index);
361 
362  DBT_DATA x;
363  x.invertor = invertor.invertor;
364 
365  for (m_nr=m_retries; m_nr>0; m_nr--) {
366  try {
367  if (invertor.now_power) x.now_power = readValue(FRONIUS_CMD_GET_POWER_NOW).toDouble();
368  if (invertor.now_ac_current) x.now_ac_current = readValue(FRONIUS_CMD_GET_AC_CURRENT_NOW).toDouble();
369  if (invertor.now_ac_voltage) x.now_ac_voltage = readValue(FRONIUS_CMD_GET_AC_VOLTAGE_NOW).toDouble();
370  if (invertor.now_ac_frequency) x.now_ac_frequency = readValue(FRONIUS_CMD_GET_AC_FREQUENCY_NOW).toDouble();
371  if (invertor.now_dc_current) x.now_dc_current = readValue(FRONIUS_CMD_GET_DC_CURRENT_NOW).toDouble();
372  if (invertor.now_dc_voltage) x.now_dc_voltage = readValue(FRONIUS_CMD_GET_DC_VOLTAGE_NOW).toDouble();
373  if (invertor.day_energy) x.day_energy = readValue(FRONIUS_CMD_GET_ENERGY_DAY).toDouble();
374  if (invertor.day_power_maximum) x.day_power_maximum = readValue(FRONIUS_CMD_GET_MAXIMUM_POWER_DAY).toDouble();
375  if (invertor.day_ac_voltage_maximum) x.day_ac_voltage_maximum = readValue(FRONIUS_CMD_GET_MAXIMUM_AC_VOLTAGE_DAY).toDouble();
376  if (invertor.day_ac_voltage_minimum) x.day_ac_voltage_minimum = readValue(FRONIUS_CMD_GET_MINIMUM_AC_VOLTAGE_DAY).toDouble();
377  if (invertor.day_dc_voltage_maximum) x.day_dc_voltage_maximum = readValue(FRONIUS_CMD_GET_MAXIMUM_DC_VOLTAGE_DAY).toDouble();
378  if (invertor.day_operating_hours) x.day_operating_hours = readValue(FRONIUS_CMD_GET_OPERATING_HOURS_DAY).toDouble();
379  if (invertor.total_energy) x.total_energy = readValue(FRONIUS_CMD_GET_ENERGY_TOTAL).toDouble();
380  if (invertor.total_power_maximum) x.total_power_maximum = readValue(FRONIUS_CMD_GET_MAXIMUM_POWER_TOTAL).toDouble();
381  if (invertor.total_ac_voltage_maximum) x.total_ac_voltage_maximum = readValue(FRONIUS_CMD_GET_MAXIMUM_AC_VOLTAGE_TOTAL).toDouble();
382  if (invertor.total_ac_voltage_minimum) x.total_ac_voltage_minimum = readValue(FRONIUS_CMD_GET_MINIMUM_AC_VOLTAGE_TOTAL).toDouble();
383  if (invertor.total_dc_voltage_maximum) x.total_dc_voltage_maximum = readValue(FRONIUS_CMD_GET_MAXIMUM_DC_VOLTAGE_TOTAL).toDouble();
384  if (invertor.total_operating_hours) x.total_operating_hours = readValue(FRONIUS_CMD_GET_OPERATING_HOURS_TOTAL).toDouble();
385  if (invertor.temperature_1) x.temperature_1 = readValue(FRONIUS_CMD_GET_TEMPERATURE_1).toDouble();
386  if (invertor.temperature_2) x.temperature_2 = readValue(FRONIUS_CMD_GET_TEMPERATURE_2).toDouble();
387  if (invertor.irradiance) x.irradiance = readValue(FRONIUS_CMD_GET_IRRADIANCE).toDouble();
388  try {
389  x.status = readValue(FRONIUS_CMD_GET_INVERTOR_STATUS).toString();
390  } catch (QString e) {
391  x.status = "UNKNOWN";
392  }
393  checkData(x);
394  emit data(x);
395  m_number_of_ok++;
396  break;
397  }
398  catch (QString e) {
399  qDebug() << "Repeated invertor reading:" << x.invertor << "try: " << m_nr;
400  if (m_nr>1) {
401  setStatus(
403  m_retries-m_nr+1,
405  tr("Error: %1").arg(e)
406  );
407  msleep(1000);
408  continue;
409  }
410  qDebug() << "Repeated invertor reading:" << x.invertor << "try: " << m_nr << "gave up";
411  QString cmdmsg = decodeCommand((FRONIUS::Command)m_command);
412  x.error = QString("%1 Command: %2").arg(e).arg(cmdmsg);
413  emit data(x);
414  m_number_of_err++;
415  break;
416  }
417  }
418 }
419 
423 void FRONIUS::checkData(const DBT_DATA& x) {
424  if (x.now_power.toDouble() < 0) throw tr("Now power < 0");
425  if (x.now_power.toDouble() > 15000) throw tr("Now power > 15000");
426 }
427 
428 
435  QString commandString;
436  switch (command) {
437  case FRONIUS_CMD_GET_VERSION:
438  commandString = tr("getVersion");
439  break;
440  case FRONIUS_CMD_GET_DEVICE_TYPE:
441  commandString = tr("getDeviceType");
442  break;
443  case FRONIUS_CMD_GET_DATE_TIME:
444  commandString = tr("getDateTime");
445  break;
446  case FRONIUS_CMD_GET_ACTIVE_INVERTOR_NUMBER:
447  commandString = tr("getActiveInverterNumber");
448  break;
449  case FRONIUS_CMD_GET_POWER_NOW:
450  commandString = tr("getPowerNow");
451  break;
452  case FRONIUS_CMD_GET_ENERGY_TOTAL:
453  commandString = tr("getEnergyTotal");
454  break;
455  case FRONIUS_CMD_GET_ENERGY_DAY:
456  commandString = tr("getEnergyDay");
457  break;
458  case FRONIUS_CMD_GET_ENERGY_YEAR:
459  commandString = tr("getEnergyYear");
460  break;
461  case FRONIUS_CMD_GET_AC_CURRENT_NOW:
462  commandString = tr("getACCurrentNow");
463  break;
464  case FRONIUS_CMD_GET_AC_VOLTAGE_NOW:
465  commandString = tr("getACVoltageNow");
466  break;
467  case FRONIUS_CMD_GET_AC_FREQUENCY_NOW:
468  commandString = tr("getACFrequencyNow");
469  break;
470  case FRONIUS_CMD_GET_DC_CURRENT_NOW:
471  commandString = tr("getDCCurrentNow");
472  break;
473  case FRONIUS_CMD_GET_DC_VOLTAGE_NOW:
474  commandString = tr("getDCVoltageNow");
475  break;
476  case FRONIUS_CMD_GET_YIELD_DAY:
477  commandString = tr("getYieldDay");
478  break;
479  case FRONIUS_CMD_GET_MAXIMUM_POWER_DAY:
480  commandString = tr("getMaximumPowerDay");
481  break;
482  case FRONIUS_CMD_GET_MAXIMUM_AC_VOLTAGE_DAY:
483  commandString = tr("getMaximumACVoltageDay");
484  break;
485  case FRONIUS_CMD_GET_MINIMUM_AC_VOLTAGE_DAY:
486  commandString = tr("getMinimumACVoltageDay");
487  break;
488  case FRONIUS_CMD_GET_MAXIMUM_DC_VOLTAGE_DAY:
489  commandString = tr("getMaximumDCVoltageDay");
490  break;
491  case FRONIUS_CMD_GET_OPERATING_HOURS_DAY:
492  commandString = tr("getOperatingHoursDay");
493  break;
494  case FRONIUS_CMD_GET_YIELD_YEAR:
495  commandString = tr("getYieldYear");
496  break;
497  case FRONIUS_CMD_GET_MAXIMUM_POWER_TOTAL:
498  commandString = tr("getMaximumPowerTotal");
499  break;
500  case FRONIUS_CMD_GET_MAXIMUM_AC_VOLTAGE_TOTAL:
501  commandString = tr("getMaximumACVoltageTotal");
502  break;
503  case FRONIUS_CMD_GET_MINIMUM_AC_VOLTAGE_TOTAL:
504  commandString = tr("getMinimumACVoltageTotal");
505  break;
506  case FRONIUS_CMD_GET_MAXIMUM_DC_VOLTAGE_TOTAL:
507  commandString = tr("getMaximumDCVoltageTotal");
508  break;
509  case FRONIUS_CMD_GET_OPERATING_HOURS_TOTAL:
510  commandString = tr("getOperatingHoursTotal");
511  break;
512  case FRONIUS_CMD_GET_TEMPERATURE_1:
513  commandString = tr("getTemperature1");
514  break;
515  case FRONIUS_CMD_GET_TEMPERATURE_2:
516  commandString = tr("getTemperature2");
517  break;
518  case FRONIUS_CMD_GET_IRRADIANCE:
519  commandString = tr("getIrradiance");
520  break;
521  default:
522  commandString = tr("Unknown command");
523  break;
524  }
525  return commandString;
526 }
527 
528 
534 QString FRONIUS::decodeError(FRONIUS::Error errorInformation) {
535  QString errorString;
536  switch (errorInformation) {
537  case FRONIUS_PROTOCOL_ERROR_UNKNOWN_COMMAND:
538  errorString = tr("unknown command");
539  break;
540  case FRONIUS_PROTOCOL_ERROR_TIMEOUT:
541  errorString = tr("timeout");
542  break;
543  case FRONIUS_PROTOCOL_ERROR_INCORRECT_DATA_STRUCTURE:
544  errorString = tr("incorrect data structure");
545  break;
546  case FRONIUS_PROTOCOL_ERROR_COMMAND_QUEUE_FULL:
547  errorString = tr("command queue full");
548  break;
549  case FRONIUS_PROTOCOL_ERROR_DEVICE_OR_OPTION_NA:
550  errorString = tr("device or option not available");
551  break;
552  case FRONIUS_PROTOCOL_ERROR_NO_RESPONSE:
553  errorString = tr("no response");
554  break;
555  case FRONIUS_PROTOCOL_ERROR_SENSOR_ERROR:
556  errorString = tr("sensor error");
557  break;
558  case FRONIUS_PROTOCOL_ERROR_SENSOR_NOT_ACTIVE:
559  errorString = tr("sensor not active");
560  break;
561  case FRONIUS_PROTOCOL_ERROR_INCORRECT_COMMAND:
562  errorString = tr("incorrect command");
563  break;
564  case FRONIUS_PROTOCOL_ERROR_DEVICE_ID_COLLISION:
565  errorString = tr("device id collision");
566  break;
567  default:
568  errorString = tr("unknown error information");
569  break;
570  }
571  return errorString;
572 }
573 
574 
580 QString FRONIUS::decodeStatus(FRONIUS::Status statusInformation) {
581  QString statusString;
582  switch (statusInformation) {
583  case FRONIUS_STATUS_STARTUP:
584  statusString = "STARTING";
585  break;
586  case FRONIUS_STATUS_OPERATION:
587  statusString = "MPPT";
588  break;
589  case FRONIUS_STATUS_MANUAL_STANDBY:
590  statusString = "STANDBY";
591  break;
592  case FRONIUS_STATUS_FAILURE:
593  statusString = "FAULT";
594  break;
595  default:
596  statusString = "UNKNOWN";
597  break;
598  }
599  return statusString;
600 }
601 
602 
void quit()
Quits the running thread.
Definition: invertor.cpp:69
void loop()
Loops the invertors's list.
Definition: fronius.cpp:127
QString decodeError(FRONIUS::Error)
Converts fronius error code to text description.
Definition: fronius.cpp:534
void readInvertor()
Reads values from current invertor.
Definition: fronius.cpp:359
QVariant value()
Decodes and returns value stored in response.
Response from fronius invertor.
void open()
Opens serial port.
Definition: fronius.cpp:66
void data(DBT_DATA)
Signal to send retrieved data to other objects.
void checkData(const DBT_DATA &)
Basic data check.
Definition: fronius.cpp:423
Error
List of all fronius error codes.
Definition: fronius.h:76
FRONIUS()
Constructor. The very basic initializations.
Definition: fronius.cpp:24
int m_current_invertor_index
Current index in INVERTOR::m_invertors list.
Definition: fronius.h:116
Class describing database table DATA.
QVariant readValue(FRONIUS::Command)
Reads one value from invertor.
Definition: fronius.cpp:237
DBT_LINES m_line
Stores information about line.
Definition: invertor.h:122
QList< DBT_INVERTORS > m_invertors
Stores information about all invertors connected to the line.
Definition: invertor.h:127
QTimer * m_timer
Timer for main loop.
Definition: fronius.h:118
bool isChecksumValid()
Check header validity.
Structure of fronius request sent to invertor over serial line.
QextSerialPort * m_port
Serial port device.
Definition: fronius.h:117
void readResponse(FRONIUS_response &)
Reads and checks response read from invertor.
Definition: fronius.cpp:166
QVariant decodeResponse(FRONIUS_response &, FRONIUS::Command)
Decode response from invertor and returnsi retrieved value.
Definition: fronius.cpp:276
QString decodeStatus(FRONIUS::Status)
Converts fronius error code to text description.
Definition: fronius.cpp:580
Virtual class for invertor communication.
Definition: invertor.h:28
Command m_command
Current command processed.
Definition: fronius.h:115
bool isHeaderValid()
Check header validity.
QString decodeCommand(FRONIUS::Command)
Converts fronius command code to text description.
Definition: fronius.cpp:434
void loopFinished(int number_of_ok, int number_of_err)
Signal is sent when reading cycle was finished and all invertors were read.
void slotInit()
Function called within running thread to initialize all needed child objects.
Definition: fronius.cpp:36
void setStatus(int address, int retries, const QString &command, const QString &status)
Set status of line.
Definition: invertor.cpp:54
Class describing database table INVERTORS.
Command
List of all fronius commands.
Definition: fronius.h:39