Add copyright/license stuff.
[toast/barcode2cash.git] / qtapp / barcode2cash / mainwindow.cpp
1 /*
2  * Copyright (C) 2017 Philipp Spitzer, gregor herrmann
3  *
4  * This file is part of barcode2cash.
5  *
6  * ConfClerk is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation, either version 2 of the License, or (at your option)
9  * any later version.
10  *
11  * ConfClerk is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License along with
17  * ConfClerk.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <cstddef>
21 #include <zbar.h>
22 #include "mainwindow.h"
23 #include "ui_mainwindow.h"
24 #include <QDir>
25
26
27 /// converts a string like "123,45" (comma as decimal point) to 12345.
28 bool cashstr_to_currencycent(QString cash, CurrencyCent& cent) {
29     bool ok;
30     cent = cash.replace(",", "").toInt(&ok);
31     return ok;
32 }
33
34
35 /// returns true if the decoding was successful
36 bool decode_rksv(const QString& rksv_string, RksvParts& rksv_parts) {
37     // https://github.com/a-sit-plus/at-registrierkassen-mustercode/releases/download/1.2-DOK/2016-09-05-Detailfragen-RKSV-V1.2.pdf
38     auto parts = rksv_string.split("_");
39     if (parts.size() != 14) return false; // QR code found but not RKSV
40     if (!parts.takeFirst().isEmpty()) return false;
41     rksv_parts.rlat = parts.takeFirst();
42     if (!rksv_parts.rlat.startsWith("R1-")) return false; // e.g. "R1-AT1"
43     rksv_parts.device_id = parts.takeFirst(); // e.g. "603002101"
44     rksv_parts.receipt_id = parts.takeFirst(); // e.g. "1/603002101/691/10340"
45     rksv_parts.date_time = QDateTime::fromString(parts.takeFirst(), "yyyy-MM-ddThh:mm:ss");
46     if (!rksv_parts.date_time.isValid()) return false;
47     for (int i = 0; i != RksvParts::cash_count; ++i) {
48         if (!cashstr_to_currencycent(parts.takeFirst(), rksv_parts.cash[i])) return false;
49     }
50     rksv_parts.counter = parts.takeFirst(); // e.g. "9IY7hV1mTqhVd4bii4gIhw=="
51     rksv_parts.cert_serial = parts.takeFirst();  // e.g. "5660C34F"
52     rksv_parts.sig_prev = parts.takeFirst(); // e.g. "I8/Wa2TZWgw="
53     rksv_parts.sig = parts.takeFirst(); // e.g."eSmlrKimgzbtYBJXq0MzwmgI3fBDL+NfSjzfFCCdcZuoHC+LET8BGvmVGWAwmNSZnDD2tCewJ7AooQc0Bt0HcQ=="
54     return true;
55 }
56
57
58 bool checkCameraAvailability()
59 {
60     if (QCameraInfo::availableCameras().count() > 0)
61         return true;
62     else
63         return false;
64 }
65
66 MainWindow::MainWindow(QWidget *parent) :
67     QMainWindow(parent),
68     ui(new Ui::MainWindow)
69 {
70     ui->setupUi(this);
71
72     QCameraInfo info = QCameraInfo::defaultCamera();
73     if (!info.isNull()) {
74         camera = new QCamera(info);
75         camera->setCaptureMode(QCamera::CaptureStillImage);
76         camera->setViewfinder(ui->viewFinder);
77         imageCapture = new QCameraImageCapture(camera);
78         connect(imageCapture, SIGNAL(readyForCaptureChanged(bool)), SLOT(updateCameraInfo()));
79         connect(imageCapture, SIGNAL(imageSaved(int,QString)), SLOT(showCaptureSuccess(int,QString)));
80         connect(imageCapture, SIGNAL(error(int,QCameraImageCapture::Error,QString)), SLOT(showCaptureError(int,QCameraImageCapture::Error,QString)));
81         updateCameraInfo();
82         connect(ui->pbCapture, SIGNAL(clicked()), SLOT(capture()));
83         QImageEncoderSettings imageSettings;
84         imageSettings.setCodec("image/jpeg");
85         imageCapture->setEncodingSettings(imageSettings);
86         camera->start();
87     }
88
89
90     ui->statusBar->showMessage(tr("There are %1 cameras").arg(checkCameraAvailability()));
91
92 }
93
94 MainWindow::~MainWindow() {
95     delete imageCapture;
96     delete camera;
97     delete ui;
98 }
99
100
101 void MainWindow::updateRksv(const RksvParts& rksv) {
102     ui->le_rlat->setText(rksv.rlat);
103     ui->le_device_id->setText(rksv.device_id);
104     ui->le_receipt_id->setText(rksv.receipt_id);
105     ui->dte_date_time->setDateTime(rksv.date_time);
106     ui->le_counter->setText(rksv.counter);
107     ui->le_cert_serial->setText(rksv.cert_serial);
108     ui->le_sig_prev->setText(rksv.sig_prev);
109     ui->le_sig->setText(rksv.sig);
110     for (int i = 0; i != RksvParts::cash_count; ++i) {
111         QString text = tr("%1").arg(double(rksv.cash[i]) / 100);
112         QTableWidgetItem* item = new QTableWidgetItem(text);
113         ui->twCash->setItem(i, 0, item);
114     }
115 }
116
117
118 void MainWindow::updateCameraInfo() {
119     ui->labCameraReady->setText(imageCapture->isReadyForCapture() ? "ready" : "not ready");
120 }
121
122
123 void MainWindow::capture() {
124     camera->searchAndLock();
125     imageCapture->capture(QDir::homePath() + "/test.jpg"); // default: saves to $HOME under Linux, to %USERPROFILE%\Pictures under Windows, so let's unify
126     camera->unlock();
127 }
128
129
130 void MainWindow::showCaptureSuccess(int, QString fileName) {
131     ui->statusBar->showMessage(tr("Image captured and saved as '%1'.").arg(fileName));
132 }
133
134
135 void MainWindow::showCaptureError(int, QCameraImageCapture::Error error, QString errorString) {
136     ui->statusBar->showMessage(tr("Error when capturing image (error id %2): %1.").arg(errorString).arg(error));
137 }
138
139
140 void MainWindow::analyze() {
141     QImage qimage(QDir::homePath() + "/test.jpg"); // default: read from $HOME under Linux, from whereever under Windows, so let's be explicit
142     if (qimage.isNull()) {
143         ui->labAnalyzeImage->setText("Can't read image");
144         return;
145     }
146     qimage = qimage.convertToFormat(QImage::Format_Grayscale8);
147     auto zbimage = zbar::Image(qimage.width(), qimage.height(), "GREY", qimage.constBits(), qimage.byteCount());
148     auto scanner = zbar::ImageScanner();
149     auto scan_results = scanner.scan(zbimage);
150     if (scan_results > 0) {
151         auto results = scanner.get_results();
152         for (auto resit = results.symbol_begin(); resit != results.symbol_end(); ++ resit) {
153             if (resit->get_type() == zbar::ZBAR_QRCODE) {
154                 QString qrcode_data = QString::fromStdString(resit->get_data());
155                 RksvParts rksv_parts;
156                 if (decode_rksv(qrcode_data, rksv_parts)) {
157                     updateRksv(rksv_parts);
158                     break;
159                 }
160             }
161         }
162     }
163
164     if (qimage.width() > 500) qimage = qimage.scaledToWidth(500);
165     ui->labAnalyzeImage->setPixmap(QPixmap::fromImage(qimage));
166 }