Fotobot
Get data from your photovoltaic plant
database.cpp
Go to the documentation of this file.
1 
7 #include "database.h"
8 #include "msqlquery.h"
9 #include <QCryptographicHash>
10 #include <QDebug>
11 #include <QDir>
12 #include <QUuid>
13 #include <math.h>
14 
15 #ifndef ADMINPASSWORD
16 #define ADMINPASSWORD "e5a66b96415488a8df63d229d1a3048b29ef757f"
17 #endif
18 
19 #ifndef FOTOMONPASSWORD
20 #define FOTOMONPASSWORD "ff5bcc26bc440020b177abff256652991c3dd0ef"
21 #endif
22 
23 DATABASE::~DATABASE() {
24  m_db.close();
25  m_dbc.close();
26 }
27 
28 #ifdef ARM
29 #define COMMITINTERVAL 60000
30 #define SYNCHRONOUS "OFF"
31 #define DEFAULTDEVICE "/dev/ttyA"
32 #else
33 #define COMMITINTERVAL 5000
34 #define SYNCHRONOUS "FULL"
35 #define DEFAULTDEVICE "/dev/ttyS0"
36 #endif
37 
38 
39 DATABASE::DATABASE(QSettings *settings, QObject *parent) : QObject(parent) {
40  m_in_transaction = false;
41  m_timerCommit = new QTimer(this);
42  m_timerCommit->setInterval(settings->value("commitinterval", COMMITINTERVAL).toInt());
43  m_timerCommit->setSingleShot(false);
44  connect(m_timerCommit, SIGNAL(timeout()),
45  this, SLOT(commit()));
46  m_timerCommit->start();
47 
48  m_synchronous = settings->value("synchronous", SYNCHRONOUS).toString();
49  if (m_synchronous != "OFF" &&
50  m_synchronous != "NORMAL" &&
51  m_synchronous != "FULL" ) {
52  m_synchronous = SYNCHRONOUS;
53  }
54 
55  QString dbdata = settings->value("dbname","fotobot.sqlite3").toString();
56  QString dbcopy = settings->value("dbname-copy","fotobot-copy.sqlite3").toString();
57 
58  QFile filedbdata(dbdata);
59  QFile filedbcopy(dbcopy);
60  if (filedbcopy.exists() && !filedbdata.exists()) {
61  filedbcopy.copy(dbdata);
62  }
63 
64  m_db = open( settings->value("dbname","fotobot.sqlite3").toString() );
65  m_dbc = open( settings->value("dbname-copy","fotobot-copy.sqlite3").toString() );
66 }
67 
68 
72 QSqlDatabase DATABASE::open(const QString& name) {
73  QMutexLocker locker(&m_mutex);
74  qDebug() << QString("DATABASE::open(%1)").arg(name) << "current path" << QDir::currentPath();
75 
76  QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", QUuid::createUuid().toString().toAscii());
77  db.setDatabaseName( name );
78  db.open();
79 
80  QSqlQuery q(db);
81  q.exec(QString("pragma synchronous=%1;").arg(m_synchronous));
82  q.exec("select * from version;");
83  if (!q.next()) {
84  create(db);
85  }
86 
87  upgrade(db);
88  return db;
89 }
90 
91 
102  QMutexLocker locker(&m_mutex);
103  QSqlQuery q(m_db);
104  if (m_in_transaction) {
105  q.exec("commit;");
106  }
107  q.exec("begin;");
108  m_in_transaction = true;
109  m_timerCommit->start(); // restart timer
110 }
111 
112 
116 void DATABASE::create(QSqlDatabase& db) {
117  qWarning() << "DATABASE::create()";
118  QSqlQuery q(db);
119  q.exec("create table version (version integer);");
120  q.exec("insert into version (version) values (1);");
121  q.exec("create table lines ("
122  " line integer primary key, "
123  " description text, "
124  " device text, "
125  " speed integer, "
126  " retries integer not null default 5, "
127  " timeout integer not null default 3000, "
128  " sn_format text "
129  " );");
130  q.exec("create table invertors ("
131  // Primary key, description
132  " invertor integer primary key, "
133  " description text, "
134  // Address
135  " line integer, "
136  " address integer, "
137  // What to retrieve and save
138  " now_power integer, "
139  " now_ac_current integer, "
140  " now_ac_voltage integer, "
141  " now_ac_frequency integer, "
142  " now_dc_current integer, "
143  " now_dc_voltage integer, "
144  " day_energy integer, "
145  " day_power_maximum integer, "
146  " day_ac_voltage_maximum integer, "
147  " day_ac_voltage_minimum integer, "
148  " day_dc_voltage_maximum integer, "
149  " day_operating_hours integer, "
150  " total_energy integer, "
151  " total_power_maximum integer, "
152  " total_ac_voltage_maximum integer, "
153  " total_ac_voltage_minimum integer, "
154  " total_dc_voltage_maximum integer, "
155  " total_operating_hours integer, "
156  " unique (line, address) "
157  " );");
158  q.exec("create table data ("
159  " invertor integer "
160  " not null references invertors(invertor) "
161  " on update cascade on delete cascade, "
162  " date text, "
163  " error text, "
164  " now_power numeric, "
165  " now_ac_current numeric, "
166  " now_ac_voltage numeric, "
167  " now_ac_frequency numeric, "
168  " now_dc_current numeric, "
169  " now_dc_voltage numeric, "
170  " day_energy numeric, "
171  " day_power_maximum numeric, "
172  " day_ac_voltage_maximum numeric, "
173  " day_ac_voltage_minimum numeric, "
174  " day_dc_voltage_maximum numeric, "
175  " day_operating_hours numeric, "
176  " total_energy numeric, "
177  " total_power_maximum numeric, "
178  " total_ac_voltage_maximum numeric, "
179  " total_ac_voltage_minimum numeric, "
180  " total_dc_voltage_maximum numeric, "
181  " total_operating_hours numeric "
182  " );");
183  q.exec("create index data_idx_invertor_date on data(invertor,date);");
184 
185  q.exec("create table users ("
186  " user text primary key, "
187  " password text not null "
188  " );");
189  q.exec(QString("insert into users (user, password) values ('admin', '%1');").arg(ADMINPASSWORD));
190 
191  q.exec("create table maintenance ("
192  " days integer"
193  " );");
194  q.exec("insert into maintenance (days) values (5);");
195 
196  // this should go away in production state
197  // for developing purpose only
198  q.exec(QString(" insert into lines (line, device, description, retries, timeout, speed) values "
199  " (1, '%1', 'Line 1', 5, 3000, 9600);").arg(DEFAULTDEVICE));
200  q.exec(" begin;");
201  for (int i=1; i<=73; i++) {
202  q.exec(QString("insert into invertors (invertor, line, address, now_power) values (%1, 1, %1, 1);").arg(i));
203  }
204  q.exec(" commit;");
205 
206 }
207 
208 
209 void DATABASE::upgrade(QSqlDatabase& db) {
210  QSqlQuery q(db);
211  q.exec("select version from version;");
212  q.next();
213  int version = q.value(0).toInt();
214  if (version == 1) {
215  q.exec("create table interfaceboxes ("
216  " interfacebox integer primary key, "
217  " line integer not null references lines(line) on update cascade on delete cascade, "
218  " description text "
219  " );");
220  qDebug() << "DATABASE::upgrade() 1 -> 2";
221  version++;
222  }
223  if (version == 2) {
224  q.exec("create table geocoordinates ("
225  " longitude numeric not null, "
226  " latitude numeric not null "
227  " );");
228  qDebug() << "DATABASE::upgrade() 2 -> 3";
229  version++;
230  }
231  if (version == 3) {
232  q.exec("alter table users add column can_access_data integer;");
233  q.exec("alter table users add column can_change_settings integer;");
234  q.exec("delete from users;");
235  q.exec(QString("insert into users (user, password, can_access_data, can_change_settings) values ('admin', '%1', 1, 1); ").arg(ADMINPASSWORD));
236  q.exec(QString("insert into users (user, password, can_access_data, can_change_settings) values ('fotomon', '%1', 1, 0); ").arg(FOTOMONPASSWORD));
237  qDebug() << "DATABASE::upgrade() 3 -> 4";
238  version++;
239  }
240 
241  if (version == 4) {
242  q.exec("alter table lines add column type integer;");
243  q.exec("alter table lines add column hostname text;");
244  q.exec("alter table lines add column portnumber integer;");
245  q.exec(QString("update lines set type = %1;").arg(DBT_LINES::Fronius));
246  qDebug() << "DATABASE::upgrade() 4 -> 5";
247  version++;
248  }
249 
250  if (version == 5) {
251  q.exec("alter table invertors add column temperature_1 integer;");
252  q.exec("alter table invertors add column temperature_2 integer;");
253  q.exec("alter table invertors add column irradiance integer;");
254  q.exec("alter table data add column temperature_1 numeric;");
255  q.exec("alter table data add column temperature_2 numeric;");
256  q.exec("alter table data add column irradiance numeric;");
257  qDebug() << "DATABASE::upgrade() 4 -> 5";
258  version++;
259  }
260 
261  if (version == 6) {
262  q.exec("alter table data add column status text;");
263  qDebug() << "DATABASE::upgrade() 5 -> 6";
264  version++;
265  }
266 
267  q.exec(QString("update version set version=%1;").arg(version));
268 }
269 
270 
274 QList<DBT_USERS> DATABASE::users(const QString& user) {
275  QMutexLocker locker(&m_mutex);
276  QList<DBT_USERS> list;
277  MSqlQuery q(m_db);
278  if (user.isEmpty()) {
279  q.exec(QString("select user, password, can_access_data, can_change_settings from users;"));
280  } else {
281  q.exec(QString("select user, password, can_access_data, can_change_settings from users where user='%1';").arg(user));
282  }
283  while (q.next()) {
284  DBT_USERS p;
285  int i=0;
286  p.user = q.value(i++).toString();
287  p.password = q.value(i++).toString();
288  p.can_access_data = q.value(i++).toBool();
289  p.can_change_settings = q.value(i++).toBool();
290  list.append(p);
291  }
292  return list;
293 }
294 
295 
296 QString DATABASE::changePassword(DBT_USERS user) {
297  QString rc;
298  rc = changePassword(m_db, user);
299  rc = changePassword(m_dbc, user);
300  return rc;
301 }
302 
308 QString DATABASE::changePassword(QSqlDatabase& db, DBT_USERS user) {
309  QMutexLocker locker(&m_mutex);
310  MSqlQuery q(db);
311  if (user.password.isEmpty() || user.password == "") {
312  return user.user;
313  }
314  QString passsha1 = QCryptographicHash::hash(user.password.toAscii(), QCryptographicHash::Sha1).toHex();
315  q.exec(QString("update users set password='%1' where user='%2'")
316  .arg(passsha1)
317  .arg(user.user)
318  );
319  return user.user;
320 }
321 
322 
323 void DATABASE::saveUser(DBT_USERS user) {
324  saveUser(m_db, user);
325  saveUser(m_dbc, user);
326 }
327 
328 
332 void DATABASE::saveUser(QSqlDatabase& db, DBT_USERS user) {
333  QMutexLocker locker(&m_mutex);
334  MSqlQuery q(db);
335 
336  q.exec(QString("select * from users where user='%1'").arg(user.id));
337  if (q.next()) {
338  q.exec(QString("update users set user='%1', can_access_data=%2, can_change_settings=%3 where user='%4'")
339  .arg(user.user)
340  .arg(nullb(user.can_access_data))
341  .arg(nullb(user.can_change_settings))
342  .arg(user.id)
343  );
344  } else {
345  q.exec(QString("insert into users (user, password, can_access_data, can_change_settings) values ('%1', '!!', %2, %3);")
346  .arg(user.user)
347  .arg(user.can_access_data)
348  .arg(user.can_change_settings)
349  );
350  }
351 
352  locker.unlock();
353  changePassword(user);
354 }
355 
356 
357 void DATABASE::deleteUser(QString userid) {
358  deleteUser(m_db, userid);
359  deleteUser(m_dbc, userid);
360 }
361 
362 
363 void DATABASE::deleteUser(QSqlDatabase& db, QString userid) {
364  QMutexLocker locker(&m_mutex);
365  MSqlQuery q(db);
366  q.exec(QString("delete from users where user='%1'").arg(userid));
367 }
368 
369 
370 
380 QList<DBT_LINES> DATABASE::lines(int line) {
381  QMutexLocker locker(&m_mutex);
382  QList<DBT_LINES> x;
383  MSqlQuery q(m_db);
384  q.exec(QString("select line, device, description, speed, retries, timeout, sn_format, type, hostname, portnumber from lines "
385  " where %1 <= 0 or line = %1; ").arg(line));
386  while (q.next()) {
387  DBT_LINES p;
388  int i=0;
389  p.line = q.value(i++).toInt();
390  p.device = q.value(i++).toString();
391  p.description = q.value(i++).toString();
392  p.speed = q.value(i++).toInt();
393  p.retries = q.value(i++).toInt();
394  p.timeout = q.value(i++).toInt();
395  p.sn_format = q.value(i++).toString();
396  int type = q.value(i++).toInt();
397  p.type = (type == DBT_LINES::Fronius) ? DBT_LINES::Fronius
398  : (type == DBT_LINES::ModbusTCP) ? DBT_LINES::ModbusTCP
399  : (type == DBT_LINES::ModbusRTU) ? DBT_LINES::ModbusRTU
400  : DBT_LINES::Unknown;
401  p.hostname = q.value(i++).toString();
402  p.portnumber = q.value(i++).toInt();
403  x.append(p);
404  }
405  return x;
406 }
407 
411 QList<DBT_INVERTORS> DATABASE::invertors(int line) {
412  QMutexLocker locker(&m_mutex);
413  QList<DBT_INVERTORS> list;
414  MSqlQuery q(m_db);
415  q.exec(QString("select invertor, description, line, address, "
416  " now_power, now_ac_current, now_ac_voltage, now_ac_frequency, now_dc_current,"
417  " now_dc_voltage, day_energy, day_power_maximum, day_ac_voltage_maximum, "
418  " day_ac_voltage_minimum, day_dc_voltage_maximum, day_operating_hours, "
419  " total_energy, total_power_maximum, total_ac_voltage_maximum, "
420  " total_ac_voltage_minimum, total_dc_voltage_maximum, total_operating_hours, "
421  " temperature_1, temperature_2, irradiance "
422  " from invertors where 0 = %1 or line = %1 order by invertor; ")
423  .arg(line)
424  );
425  while (q.next()) {
426  DBT_INVERTORS x;
427  int i=0;
428  x.invertor = q.value(i++).toInt();
429  x.description = q.value(i++).toString();
430  x.line = q.value(i++).toInt();
431  x.address = q.value(i++).toInt();
432  x.now_power = q.value(i++).toBool();
433  x.now_ac_current = q.value(i++).toBool();
434  x.now_ac_voltage = q.value(i++).toBool();
435  x.now_ac_frequency = q.value(i++).toBool();
436  x.now_dc_current = q.value(i++).toBool();
437  x.now_dc_voltage = q.value(i++).toBool();
438  x.day_energy = q.value(i++).toBool();
439  x.day_power_maximum = q.value(i++).toBool();
440  x.day_ac_voltage_maximum = q.value(i++).toBool();
441  x.day_ac_voltage_minimum = q.value(i++).toBool();
442  x.day_dc_voltage_maximum = q.value(i++).toBool();
443  x.day_operating_hours = q.value(i++).toBool();
444  x.total_energy = q.value(i++).toBool();
445  x.total_power_maximum = q.value(i++).toBool();
446  x.total_ac_voltage_maximum = q.value(i++).toBool();
447  x.total_ac_voltage_minimum = q.value(i++).toBool();
448  x.total_dc_voltage_maximum = q.value(i++).toBool();
449  x.total_operating_hours = q.value(i++).toBool();
450  x.temperature_1 = q.value(i++).toBool();
451  x.temperature_2 = q.value(i++).toBool();
452  x.irradiance = q.value(i++).toBool();
453  list.append(x);
454  }
455  return list;
456 }
457 
458 
470 QList<DBT_INVERTORS> DATABASE::invertors(const QList<int>& invertors) {
471  QMutexLocker locker(&m_mutex);
472  QList<DBT_INVERTORS> list;
473  QString cond = "0=1 ";
474  for (int i=0; i<invertors.size(); i++) {
475  cond = cond + QString(" or invertor=%1").arg(invertors.at(i));
476  }
477  MSqlQuery q(m_db);
478  q.exec(QString("select invertor, description, line, address, "
479  " now_power, now_ac_current, now_ac_voltage, now_ac_frequency, now_dc_current,"
480  " now_dc_voltage, day_energy, day_power_maximum, day_ac_voltage_maximum, "
481  " day_ac_voltage_minimum, day_dc_voltage_maximum, day_operating_hours, "
482  " total_energy, total_power_maximum, total_ac_voltage_maximum, "
483  " total_ac_voltage_minimum, total_dc_voltage_maximum, total_operating_hours, "
484  " temperature_1, temperature_2, irradiance "
485  " from invertors where %1 order by invertor; ")
486  .arg(cond)
487  );
488  while (q.next()) {
489  DBT_INVERTORS x;
490  int i=0;
491  x.invertor = q.value(i++).toInt();
492  x.description = q.value(i++).toString();
493  x.line = q.value(i++).toInt();
494  x.address = q.value(i++).toInt();
495  x.now_power = q.value(i++).toBool();
496  x.now_ac_current = q.value(i++).toBool();
497  x.now_ac_voltage = q.value(i++).toBool();
498  x.now_ac_frequency = q.value(i++).toBool();
499  x.now_dc_current = q.value(i++).toBool();
500  x.now_dc_voltage = q.value(i++).toBool();
501  x.day_energy = q.value(i++).toBool();
502  x.day_power_maximum = q.value(i++).toBool();
503  x.day_ac_voltage_maximum = q.value(i++).toBool();
504  x.day_ac_voltage_minimum = q.value(i++).toBool();
505  x.day_dc_voltage_maximum = q.value(i++).toBool();
506  x.day_operating_hours = q.value(i++).toBool();
507  x.total_energy = q.value(i++).toBool();
508  x.total_power_maximum = q.value(i++).toBool();
509  x.total_ac_voltage_maximum = q.value(i++).toBool();
510  x.total_ac_voltage_minimum = q.value(i++).toBool();
511  x.total_dc_voltage_maximum = q.value(i++).toBool();
512  x.total_operating_hours = q.value(i++).toBool();
513  x.temperature_1 = q.value(i++).toBool();
514  x.temperature_2 = q.value(i++).toBool();
515  x.irradiance = q.value(i++).toBool();
516  list.append(x);
517  }
518  return list;
519 }
520 
524 QList<DBT_INTERFACEBOXES> DATABASE::interfaceboxes(int line) {
525  QMutexLocker locker(&m_mutex);
526  QList<DBT_INTERFACEBOXES> list;
527  MSqlQuery q(m_db);
528  q.exec(QString("select interfacebox, line, description "
529  " from interfaceboxes where 0 = %1 or line = %1 order by interfacebox; ")
530  .arg(line)
531  );
532  while (q.next()) {
534  int i=0;
535  x.interfacebox = q.value(i++).toInt();
536  x.line = q.value(i++).toInt();
537  x.description = q.value(i++).toString();
538  list.append(x);
539  }
540  return list;
541 }
542 
554 QList<DBT_INTERFACEBOXES> DATABASE::interfaceboxes(const QList<int>& interfaceboxes) {
555  QMutexLocker locker(&m_mutex);
556  QList<DBT_INTERFACEBOXES> list;
557  QString cond = "0=1 ";
558  for (int i=0; i<interfaceboxes.size(); i++) {
559  cond = cond + QString(" or interfacebox=%1").arg(interfaceboxes.at(i));
560  }
561  MSqlQuery q(m_db);
562  q.exec(QString("select interfacebox, line, description "
563  " from interfaceboxes where %1 order by interfacebox; ")
564  .arg(cond)
565  );
566  while (q.next()) {
568  int i=0;
569  x.interfacebox = q.value(i++).toInt();
570  x.line = q.value(i++).toInt();
571  x.description = q.value(i++).toString();
572  list.append(x);
573  }
574  return list;
575 }
576 
577 
582  QMutexLocker locker(&m_mutex);
583  MSqlQuery q(m_db);
584  q.exec(QString("select longitude, latitude "
585  " from geocoordinates; ")
586  );
587  q.next();
589  int i=0;
590  g.longitude = q.value(i++);
591  g.latitude = q.value(i++);
592  return g;
593 }
594 
611 QList<DBT_DATA_VW> DATABASE::data(const QDateTime& from, const QDateTime& to, bool limit) {
612  QMutexLocker locker(&m_mutex);
613  QList<DBT_DATA_VW> list;
614  MSqlQuery q(m_db);
615 
616  QString dotaz;
617  if (!from.isValid()) {
618  dotaz = QString("select i.line, i.address, "
619  "d.date, d.error, d.now_power, d.now_ac_current, d.now_ac_voltage, "
620  "d.now_ac_frequency, d.now_dc_current, d.now_dc_voltage, d.day_energy, "
621  "d.day_power_maximum, d.day_ac_voltage_maximum, d.day_ac_voltage_minimum, "
622  "d.day_dc_voltage_maximum, d.day_operating_hours, d.total_energy, "
623  "d.total_power_maximum, d.total_ac_voltage_maximum, d.total_ac_voltage_minimum, "
624  "d.total_dc_voltage_maximum, d.total_operating_hours, "
625  "d.temperature_1, d.temperature_2, d.irradiance, d.status, "
626  "l.sn_format "
627  "from data d, "
628  " (select invertor,max(date) as date from data group by invertor) x, "
629  " invertors i, "
630  " lines l "
631  "where d.invertor=x.invertor and d.date=x.date "
632  " and d.invertor=i.invertor "
633  " and l.line=i.line "
634  "order by i.line, i.address; ");
635  } else {
636  QDateTime lto = QDateTime::currentDateTime();
637  if (to.isValid() && !limit) {
638  lto = to;
639  }
640  if (to.isValid() && limit) {
641  lto = from.addSecs(10800);
642  if (lto > QDateTime::currentDateTime()) {
643  lto = QDateTime::currentDateTime();
644  }
645  if (lto > to) {
646  lto = to;
647  }
648  }
649  dotaz = QString("select i.line, i.address, "
650  "d.date, d.error, d.now_power, d.now_ac_current, d.now_ac_voltage, "
651  "d.now_ac_frequency, d.now_dc_current, d.now_dc_voltage, d.day_energy, "
652  "d.day_power_maximum, d.day_ac_voltage_maximum, d.day_ac_voltage_minimum, "
653  "d.day_dc_voltage_maximum, d.day_operating_hours, d.total_energy, "
654  "d.total_power_maximum, d.total_ac_voltage_maximum, d.total_ac_voltage_minimum, "
655  "d.total_dc_voltage_maximum, d.total_operating_hours, "
656  "d.temperature_1, d.temperature_2, d.irradiance, d.status, "
657  "l.sn_format "
658  "from data d, "
659  " invertors i, "
660  " lines l "
661  "where d.invertor=i.invertor "
662  " and l.line=i.line "
663  " and d.date >= '%1' and d.date <= '%2' "
664  "order by i.line, d.date, i.address ; ")
665  .arg(from.toString("yyyy-MM-dd hh:mm:ss"))
666  .arg( lto.toString("yyyy-MM-dd hh:mm:ss"))
667  ;
668  }
669 
670  qDebug() << dotaz;
671 
672  q.exec(dotaz);
673  while (q.next()) {
674  DBT_DATA_VW x;
675  int i=0;
676  x.line = q.value(i++).toInt();
677  x.address = q.value(i++).toInt();
678  x.date = QDateTime::fromString(q.value(i++).toString(), "yyyy-MM-dd hh:mm:ss");
679  x.error = q.value(i++).toString();
680  x.now_power = q.value(i++);
681  x.now_ac_current = q.value(i++);
682  x.now_ac_voltage = q.value(i++);
683  x.now_ac_frequency = q.value(i++);
684  x.now_dc_current = q.value(i++);
685  x.now_dc_voltage = q.value(i++);
686  x.day_energy = q.value(i++);
687  x.day_power_maximum = q.value(i++);
688  x.day_ac_voltage_maximum = q.value(i++);
689  x.day_ac_voltage_minimum = q.value(i++);
690  x.day_dc_voltage_maximum = q.value(i++);
691  x.day_operating_hours = q.value(i++);
692  x.total_energy = q.value(i++);
693  x.total_power_maximum = q.value(i++);
694  x.total_ac_voltage_maximum = q.value(i++);
695  x.total_ac_voltage_minimum = q.value(i++);
696  x.total_dc_voltage_maximum = q.value(i++);
697  x.total_operating_hours = q.value(i++);
698  x.temperature_1 = q.value(i++);
699  x.temperature_2 = q.value(i++);
700  x.irradiance = q.value(i++);
701  x.status = q.value(i++).toString();
702  x.serial_number = q.value(i++).toString();
703  if (x.serial_number.isEmpty() || x.serial_number=="") {
704  x.serial_number = QString("%1").arg(x.address);
705  } else {
706  x.serial_number = x.serial_number
707  .replace("$L",QString("%1").arg(x.line))
708  .replace("$A",QString("%1").arg(x.address))
709  .replace("$1A",QString("%1").arg(x.address,1,10,QChar('0')))
710  .replace("$2A",QString("%1").arg(x.address,2,10,QChar('0')))
711  .replace("$3A",QString("%1").arg(x.address,3,10,QChar('0')))
712  .replace("$4A",QString("%1").arg(x.address,4,10,QChar('0')))
713  .replace("$5A",QString("%1").arg(x.address,5,10,QChar('0')))
714  .replace("$6A",QString("%1").arg(x.address,6,10,QChar('0')))
715  .replace("$7A",QString("%1").arg(x.address,7,10,QChar('0')))
716  .replace("$8A",QString("%1").arg(x.address,8,10,QChar('0')))
717  .replace("$9A",QString("%1").arg(x.address,9,10,QChar('0')))
718  ;
719  }
720  list.append(x);
721  }
722 
723  return list;
724 }
725 
726 
735  QMutexLocker locker(&m_mutex);
736  if (x.invertor <= 0) return;
737  MSqlQuery q(m_db);
738 
739  q.exec(QString("select 1 from data where invertor=%1 and date=%2;")
740  .arg(x.invertor)
741  .arg(nulld(x.date))
742  );
743  if (q.next()) return;
744 
745  if (x.error.isEmpty()) {
746  q.exec(QString("insert into data ( "
747  "invertor, " // 1
748  "date, " // 2
749  "error, " // 3
750  "now_power, " // 4
751  "now_ac_current, " // 5
752  "now_ac_voltage, " // 6
753  "now_ac_frequency, " // 7
754  "now_dc_current, " // 8
755  "now_dc_voltage, " // 9
756  "day_energy, " // 10
757  "day_power_maximum, " // 11
758  "day_ac_voltage_maximum, " // 12
759  "day_ac_voltage_minimum, " // 13
760  "day_dc_voltage_maximum, " // 14
761  "day_operating_hours, " // 15
762  "total_energy, " // 16
763  "total_power_maximum, " // 17
764  "total_ac_voltage_maximum, " // 18
765  "total_ac_voltage_minimum, " // 19
766  "total_dc_voltage_maximum, " // 20
767  "total_operating_hours, " // 21
768  "temperature_1, " // 22
769  "temperature_2, " // 23
770  "irradiance," // 24
771  "status" // 25
772  ") values ("
773  "%1," // invertor
774  "%2," // date
775  "%3," // error
776  "%4," // now_power
777  "%5," // now_ac_current
778  "%6," // now_ac_voltage
779  "%7," // now_ac_frequency
780  "%8," // now_dc_current
781  "%9," // nod_dc_voltage
782  "%10," // day_energy
783  "%11," // day_power_maximum
784  "%12," // day_ac_voltage_maximum
785  "%13," // day_ac_voltage_minimum
786  "%14," // day_dc_voltage_maximum
787  "%15," // day_operating_hours
788  "%16," // total_energy
789  "%17," // total_power_maximum
790  "%18," // total_ac_voltage_maximum
791  "%19," // total_ac_voltage_minimum
792  "%20," // total_dc_voltage_maximum
793  "%21," // total_operating_hours
794  "%22," // temperature_1
795  "%23," // temperature_2
796  "%24," // irradiance
797  "%25" // status
798  ")" )
799  .arg( x.invertor) // 1
800  .arg(nulld(x.date)) // 2
801  .arg( "null") // 3
802  .arg(nulli(x.now_power)) // 4
803  .arg(nullf(x.now_ac_current)) // 5
804  .arg(nulli(x.now_ac_voltage)) // 6
805  .arg(nullf(x.now_ac_frequency)) // 7
806  .arg(nullf(x.now_dc_current)) // 8
807  .arg(nulli(x.now_dc_voltage)) // 9
808  .arg(nulli(x.day_energy)) // 10
809  .arg(nulli(x.day_power_maximum)) // 11
810  .arg(nulli(x.day_ac_voltage_maximum)) // 12
811  .arg(nulli(x.day_ac_voltage_minimum)) // 13
812  .arg(nulli(x.day_dc_voltage_maximum)) // 14
813  .arg(nullf(x.day_operating_hours)) // 15
814  .arg(nulli(x.total_energy)) // 16
815  .arg(nulli(x.total_power_maximum)) // 17
816  .arg(nulli(x.total_ac_voltage_maximum)) // 18
817  .arg(nulli(x.total_ac_voltage_minimum)) // 19
818  .arg(nulli(x.total_dc_voltage_maximum)) // 20
819  .arg(nullf(x.total_operating_hours)) // 21
820  .arg(nullf(x.temperature_1)) // 22
821  .arg(nullf(x.temperature_2)) // 23
822  .arg(nullf(x.irradiance)) // 24
823  .arg(nulls(x.status)) // 25
824  );
825  } else {
826  // Error, write only error messsage
827  q.exec(QString("insert into data ("
828  "invertor,"
829  "date,"
830  "error"
831  ") values ("
832  "%1,"
833  "%2,"
834  "%3"
835  ")" )
836  .arg( x.invertor)
837  .arg(nulld(x.date))
838  .arg(nulls(x.error))
839  );
840  }
841 
842 }
843 
845  int rc;
846  rc = saveLine(m_db, x);
847  rc = saveLine(m_dbc, x);
848  emit databaseChanged();
849  return rc;
850 }
851 
857 int DATABASE::saveLine(QSqlDatabase& db, DBT_LINES x) {
858  QMutexLocker locker(&m_mutex);
859  MSqlQuery q(db);
860  q.exec(QString("select line from lines where line=%1;").arg(x.line));
861  if (q.next()) {
862  // record exists
863  q.exec(QString("update lines set "
864  " device='%1', "
865  " description='%2', "
866  " speed=%3, "
867  " retries=%4, "
868  " timeout=%5, "
869  " sn_format='%6', "
870  " type = %7, "
871  " hostname = '%8', "
872  " portnumber = %9 "
873  " where line=%10;")
874  .arg(x.device)
875  .arg(x.description)
876  .arg(x.speed)
877  .arg(x.retries)
878  .arg(x.timeout)
879  .arg(x.sn_format)
880  .arg(x.type)
881  .arg(x.hostname)
882  .arg(x.portnumber)
883  .arg(x.line)
884  );
885  } else {
886  q.exec(QString("insert into lines "
887  "(device, description, speed, retries, timeout, sn_format, type, hostname, portnumber) "
888  "values "
889  "('%1', '%2', %3, %4, %5, '%6', %7, '%8', %9);")
890  .arg(x.device)
891  .arg(x.description)
892  .arg(x.speed)
893  .arg(x.retries)
894  .arg(x.timeout)
895  .arg(x.sn_format)
896  .arg(x.type)
897  .arg(x.hostname)
898  .arg(x.portnumber)
899  );
900  q.exec(QString("select last_insert_rowid();"));
901  q.next();
902  emit databaseChanged();
903  return q.value(0).toInt();
904  }
905  return x.line;
906 }
907 
908 
910  int rc;
911  rc = saveInvertor(m_db, x);
912  rc = saveInvertor(m_dbc, x);
913  emit databaseChanged();
914  return rc;
915 }
916 
922 int DATABASE::saveInvertor(QSqlDatabase& db, DBT_INVERTORS x) {
923  QMutexLocker locker(&m_mutex);
924  MSqlQuery q(db);
925  q.exec(QString("select line from invertors where invertor=%1;").arg(x.invertor));
926  if (q.next()) {
927  // record exists
928  q.exec(QString("update invertors set "
929  "description = %1, "
930  "line = %2, "
931  "address = %3, "
932  "now_power = %4, "
933  "now_ac_current = %5, "
934  "now_ac_voltage = %6, "
935  "now_ac_frequency = %7, "
936  "now_dc_current = %8, "
937  "now_dc_voltage = %9, "
938  "day_energy = %10, "
939  "day_power_maximum = %11, "
940  "day_ac_voltage_maximum = %12, "
941  "day_ac_voltage_minimum = %13, "
942  "day_dc_voltage_maximum = %14, "
943  "day_operating_hours = %15, "
944  "total_energy = %16, "
945  "total_power_maximum = %17, "
946  "total_ac_voltage_maximum = %18, "
947  "total_ac_voltage_minimum = %19, "
948  "total_dc_voltage_maximum = %20, "
949  "total_operating_hours = %21, "
950  "temperature_1 = %22, "
951  "temperature_2 = %23, "
952  "irradiance = %24 "
953  "where invertor = %25; ")
954  .arg(nulls(x.description))
955  .arg(nulli(x.line))
956  .arg(nulli(x.address))
957  .arg(nullb(x.now_power))
958  .arg(nullb(x.now_ac_current))
959  .arg(nullb(x.now_ac_voltage))
960  .arg(nullb(x.now_ac_frequency))
961  .arg(nullb(x.now_dc_current))
962  .arg(nullb(x.now_dc_voltage))
963  .arg(nullb(x.day_energy))
964  .arg(nullb(x.day_power_maximum))
965  .arg(nullb(x.day_ac_voltage_maximum))
966  .arg(nullb(x.day_ac_voltage_minimum))
967  .arg(nullb(x.day_dc_voltage_maximum))
968  .arg(nullb(x.day_operating_hours))
969  .arg(nullb(x.total_energy))
970  .arg(nullb(x.total_power_maximum))
971  .arg(nullb(x.total_ac_voltage_maximum))
972  .arg(nullb(x.total_ac_voltage_minimum))
973  .arg(nullb(x.total_dc_voltage_maximum))
974  .arg(nullb(x.total_operating_hours))
975  .arg(nulli(x.temperature_1))
976  .arg(nulli(x.temperature_2))
977  .arg(nulli(x.irradiance))
978  .arg(nulli(x.invertor))
979 
980  );
981  } else {
982  q.exec(QString("insert into invertors ("
983  "description, line, address, now_power, now_ac_current, now_ac_voltage, "
984  "now_ac_frequency, now_dc_current, now_dc_voltage, day_energy, "
985  "day_power_maximum, day_ac_voltage_maximum, day_ac_voltage_minimum, "
986  "day_dc_voltage_maximum, day_operating_hours, total_energy, "
987  "total_power_maximum, total_ac_voltage_maximum, total_ac_voltage_minimum, "
988  "total_dc_voltage_maximum, total_operating_hours, temperature_1, temperature_2, irradiance) values ("
989  "%1, %2, %3, %4, %5, %6, %7, %8, %9, %10, "
990  "%11, %12, %13, %14, %15, %16, %17, %18, %19, %20, %21, %22, %23, %24);")
991  .arg(nulls(x.description))
992  .arg(nulli(x.line))
993  .arg(nulli(x.address))
994  .arg(nullb(x.now_power))
995  .arg(nullb(x.now_ac_current))
996  .arg(nullb(x.now_ac_voltage))
997  .arg(nullb(x.now_ac_frequency))
998  .arg(nullb(x.now_dc_current))
999  .arg(nullb(x.now_dc_voltage))
1000  .arg(nullb(x.day_energy))
1001  .arg(nullb(x.day_power_maximum))
1002  .arg(nullb(x.day_ac_voltage_maximum))
1003  .arg(nullb(x.day_ac_voltage_minimum))
1004  .arg(nullb(x.day_dc_voltage_maximum))
1005  .arg(nullb(x.day_operating_hours))
1006  .arg(nullb(x.total_energy))
1007  .arg(nullb(x.total_power_maximum))
1008  .arg(nullb(x.total_ac_voltage_maximum))
1009  .arg(nullb(x.total_ac_voltage_minimum))
1010  .arg(nullb(x.total_dc_voltage_maximum))
1011  .arg(nullb(x.total_operating_hours))
1012  .arg(nulli(x.temperature_1))
1013  .arg(nulli(x.temperature_2))
1014  .arg(nulli(x.irradiance))
1015  );
1016  q.exec(QString("select last_insert_rowid();"));
1017  q.next();
1018  emit databaseChanged();
1019  return q.value(0).toInt();
1020  }
1021  return x.invertor;
1022 }
1023 
1024 
1026  int rc;
1027  rc = saveInterfacebox(m_db, x);
1028  rc = saveInterfacebox(m_dbc, x);
1029  emit databaseChanged();
1030  return rc;
1031 }
1032 
1039  QMutexLocker locker(&m_mutex);
1040  MSqlQuery q(db);
1041  q.exec(QString("select line from interfaceboxes where interfacebox=%1;").arg(x.interfacebox));
1042  if (q.next()) {
1043  // record exists
1044  q.exec(QString("update interfaceboxes set "
1045  "description = %1, "
1046  "line = %2 "
1047  "where interfacebox = %3; ")
1048  .arg(nulls(x.description))
1049  .arg(nulli(x.line))
1050  .arg(nulli(x.interfacebox))
1051  );
1052  } else {
1053  q.exec(QString("insert into interfaceboxes ("
1054  "description, line) values ("
1055  "%1, %2);")
1056  .arg(nulls(x.description))
1057  .arg(nulli(x.line))
1058  );
1059  q.exec(QString("select last_insert_rowid();"));
1060  q.next();
1061  emit databaseChanged();
1062  return q.value(0).toInt();
1063  }
1064  return x.interfacebox;
1065 }
1066 
1067 
1069  setGeocoordinates(m_db, x);
1070  setGeocoordinates(m_dbc, x);
1071  emit databaseChanged();
1072 }
1073 
1078  QMutexLocker locker(&m_mutex);
1079  MSqlQuery q(db);
1080  q.exec(QString("select longitude, latitude from geocoordinates;"));
1081  if (q.next()) {
1082  // record exists
1083  q.exec(QString("update geocoordinates set "
1084  "longitude = %1, "
1085  "latitude = %2 ")
1086  .arg(nullf(x.longitude))
1087  .arg(nullf(x.latitude))
1088  );
1089  } else {
1090  q.exec(QString("insert into geocoordinates ("
1091  "longitude, latitude) values ("
1092  "%1, %2);")
1093  .arg(nullf(x.longitude))
1094  .arg(nullf(x.latitude))
1095  );
1096  }
1097 }
1098 
1099 
1100 void DATABASE::deleteLine(int line) {
1101  deleteLine(m_db, line);
1102  deleteLine(m_dbc, line);
1103  emit databaseChanged();
1104 }
1105 
1109 void DATABASE::deleteLine(QSqlDatabase& db, int line) {
1110  QMutexLocker locker(&m_mutex);
1111  MSqlQuery q(db);
1112  q.exec(QString("begin;"));
1113  q.exec(QString("delete from invertors where line=%1;").arg(line));
1114  q.exec(QString("delete from lines where line=%1;").arg(line));
1115  q.exec(QString("commit;"));
1116 }
1117 
1118 
1119 void DATABASE::deleteInvertor(int invertor) {
1120  deleteInvertor(m_db, invertor);
1121  deleteInvertor(m_dbc, invertor);
1122  emit databaseChanged();
1123 }
1124 
1125 
1129 void DATABASE::deleteInvertor(QSqlDatabase& db, int invertor) {
1130  QMutexLocker locker(&m_mutex);
1131  MSqlQuery q(db);
1132  q.exec(QString("delete from invertors where invertor=%1;").arg(invertor));
1133 }
1134 
1135 
1136 void DATABASE::deleteInterfacebox(int interfacebox) {
1137  deleteInterfacebox(m_db, interfacebox);
1138  deleteInterfacebox(m_dbc, interfacebox);
1139  emit databaseChanged();
1140 }
1141 
1145 void DATABASE::deleteInterfacebox(QSqlDatabase& db, int interfacebox) {
1146  QMutexLocker locker(&m_mutex);
1147  MSqlQuery q(db);
1148  q.exec(QString("delete from interfaceboxes where interfacebox=%1;").arg(interfacebox));
1149 }
1150 
1151 
1159  QMutexLocker locker(&m_mutex);
1160  qWarning() << "DATABASE::execMaintenance();";
1161  MSqlQuery q(m_db);
1162  q.exec(QString("select days from maintenance;"));
1163  if (!q.next()) return;
1164  QDateTime date;
1165  date = QDateTime::currentDateTime();
1166  date = date.addDays(-q.value(0).toInt());
1167  q.exec(QString("delete from data where date < '%1';")
1168  .arg(date.toString("yyyy-MM-dd hh:mm:ss"))
1169  );
1170  q.exec(QString("vacuum full;"));
1171 }
1172 
1173 
1186 QList<DBT_LINES_STATUS> DATABASE::linesStatus() {
1187  QMutexLocker locker(&m_mutex);
1188  MSqlQuery q(m_db);
1189  QDateTime date = QDateTime::currentDateTime().addSecs(-600);
1190  QList<DBT_LINES_STATUS> list;
1191  QString dotaz = QString("select n.line, w.working, n.number "
1192  " from (select line, count(0) as number "
1193  " from invertors group by line) n "
1194  " left join (select line, coalesce(sum(jedna),0) as working "
1195  " from (select i.line, 1 as jedna "
1196  " from data d, invertors i "
1197  " where d.invertor=i.invertor "
1198  " and date>'%1' "
1199  " and error is null "
1200  " group by i.line, i.invertor "
1201  " having count(1) > 0) "
1202  " group by line) w using(line);")
1203  .arg(date.toString("yyyy-MM-dd hh:mm:ss")
1204  );
1205  q.exec(dotaz);
1206 // qDebug() << dotaz.replace(QRegExp("\\s+"), QString(" "));
1207  while (q.next()) {
1208  DBT_LINES_STATUS p;
1209  int i=0;
1210  p.line = q.value(i++).toInt();
1211  p.working = q.value(i++).toInt();
1212  p.invertors = q.value(i++).toInt();
1213  list.append(p);
1214  }
1215  return list;
1216 }
1217 
1218 
1222 QList<DBT_MAINTENANCE> DATABASE::maintenance() {
1223  QMutexLocker locker(&m_mutex);
1224  MSqlQuery q(m_db);
1225  QList<DBT_MAINTENANCE> list;
1226  q.exec(QString("select days from maintenance;"));
1227  while (q.next()) {
1228  DBT_MAINTENANCE p;
1229  int i=0;
1230  p.days = q.value(i++).toInt();
1231  list.append(p);
1232  }
1233  return list;
1234 }
1235 
1236 
1238  saveMaintenance(m_db, x);
1239  saveMaintenance(m_dbc, x);
1240 }
1241 
1242 
1246 void DATABASE::saveMaintenance(QSqlDatabase& db, DBT_MAINTENANCE x) {
1247  QMutexLocker locker(&m_mutex);
1248  MSqlQuery q(db);
1249  q.exec(QString("update maintenance set days=%1;")
1250  .arg(nulli(x.days))
1251  );
1252 }
1253 
1254 
1262 QString DATABASE::nulld(const QDateTime& x) {
1263  if (!x.isValid()) {
1264  return "null";
1265  }
1266  return QString("'%1'").arg(x.toString("yyyy-MM-dd hh:mm:ss"));
1267 }
1268 
1269 
1276 QString DATABASE::nulli(const QVariant& x) {
1277  if (!x.isValid()) {
1278  return "null";
1279  }
1280  int val = x.toInt();
1281  if (isnan(val)) {
1282  return "null";
1283  }
1284  return QString("%1").arg(val);
1285 }
1286 
1287 
1294 QString DATABASE::nullf(const QVariant& x) {
1295  if (!x.isValid()) {
1296  return "null";
1297  }
1298  double val = x.toInt();
1299  if (isnan(val)) {
1300  return "null";
1301  }
1302  return QString("%1").arg(val).replace(",",".");
1303 }
1304 
1305 
1312 QString DATABASE::nulls(const QString& x) {
1313  if (x.isEmpty()) {
1314  return "null";
1315  }
1316  QString xx = x;
1317  return QString("'%1'").arg(xx.replace('\'',"''"));
1318 }
1319 
1320 
1327 QString DATABASE::nullb(bool x) {
1328  return QString("%1").arg((x)?1:0);
1329 }
1330 
QList< DBT_USERS > users(const QString &user=QString())
Returs list of users or selected user.
Definition: database.cpp:274
QList< DBT_INTERFACEBOXES > interfaceboxes(int line=0)
Returns list of interfaceboxes filtered by line number.
Definition: database.cpp:524
QString changePassword(QSqlDatabase &, DBT_USERS)
Updates user's info (actually only password is changed)
Definition: database.cpp:308
Class describing database table GEOCOORDINATES.
QString nullb(bool x)
Formats bool for database store.
Definition: database.cpp:1327
Class describing database table MAINTENANCE.
QList< DBT_LINES > lines(int line=0)
Returns list of communications lines.
Definition: database.cpp:380
Extends class QSqlQuery - adds a diagnostics and logging.
Definition: msqlquery.h:22
int saveInvertor(QSqlDatabase &, DBT_INVERTORS)
Insert or update INVERTOR.
Definition: database.cpp:922
void commit()
Commit transactions.
Definition: database.cpp:101
QString nulls(const QString &x)
Formats QString for database store.
Definition: database.cpp:1312
Class describing database table DATA.
QList< DBT_DATA_VW > data(const QDateTime &from=QDateTime(), const QDateTime &to=QDateTime(), bool limit=true)
Returns data for all invertors.
Definition: database.cpp:611
QList< DBT_INVERTORS > invertors(int line=0)
Returns list of invertors filtered by line number.
Definition: database.cpp:411
void execMaintenance()
Database maintenance.
Definition: database.cpp:1158
QString nulli(const QVariant &x)
Formats QVariant - integer for database store.
Definition: database.cpp:1276
int saveInterfacebox(QSqlDatabase &, DBT_INTERFACEBOXES)
Insert or update Interfacebox.
Definition: database.cpp:1038
void deleteLine(QSqlDatabase &, int)
Delete line in database.
Definition: database.cpp:1109
void databaseChanged()
Signals when information about lines or invertors changed.
void saveUser(QSqlDatabase &, DBT_USERS)
Updates user's info.
Definition: database.cpp:332
void create(QSqlDatabase &db)
Creates new database structure in opened file.
Definition: database.cpp:116
QList< DBT_MAINTENANCE > maintenance()
Returns list of DBT_MAINTENANCE.
Definition: database.cpp:1222
void deleteInterfacebox(QSqlDatabase &, int)
Delete interfacebox in database.
Definition: database.cpp:1145
Class describing lines status.
QString nullf(const QVariant &x)
Formats QVariant - double for database store.
Definition: database.cpp:1294
Class describing database table USERS.
void insertData(DBT_DATA)
Writes data to database table DATA.
Definition: database.cpp:734
Class describing database table DATA + INVERTORS.
int saveLine(QSqlDatabase &, DBT_LINES)
Insert or update LINE.
Definition: database.cpp:857
QMutex m_mutex
Mutex for locking between threads.
Definition: database.h:114
void deleteInvertor(QSqlDatabase &, int)
Delete invertor in database.
Definition: database.cpp:1129
DBT_GEOCOORDINATES geocoordinates()
Returns geocoordinates.
Definition: database.cpp:581
void saveMaintenance(QSqlDatabase &, DBT_MAINTENANCE)
Updates maintenace record in database.
Definition: database.cpp:1246
void setGeocoordinates(QSqlDatabase &, DBT_GEOCOORDINATES)
update geocoordinates
Definition: database.cpp:1077
QList< DBT_LINES_STATUS > linesStatus()
Returns status of lines.
Definition: database.cpp:1186
Class describing database table LINES.
QString nulld(const QDateTime &x)
Formats QDateTime for database store.
Definition: database.cpp:1262
Class describing database table INVERTORS.
QSqlDatabase open(const QString &dbname)
Opens or creates new database file.
Definition: database.cpp:72