253 Commits

Author SHA1 Message Date
9b175d7789 Set version to 1.3.12 2023-10-18 11:45:54 +02:00
2d7f145a25 Replaced using gitFetch() with gitPull() 2023-10-18 11:40:04 +02:00
4589c4ca76 Removed gitFetchAndDiff() and original version of gitPull(). 2023-10-18 11:38:26 +02:00
a32258a59e Replaced "git fetch" with "git pull". 2023-10-18 11:37:46 +02:00
22f25e5251 Replaced 'git clone' with 'git clone --filter=blob:none' to speed
up cloning of customer-repository.
2023-10-18 11:36:13 +02:00
258d883a51 Replaced gitFetch() with gitPull() 2023-10-18 11:35:23 +02:00
504e242d42 Implemented getATBUpdateToolYoctoVersion() and getATBUpdateToolYoctoInstallationStatus() 2023-10-18 11:31:15 +02:00
731cdcbe09 Added getATBUpdateToolYoctoVersion() and getATBUpdateToolYoctoInstallationStatus() 2023-10-18 11:30:12 +02:00
b4e2d4c54a Minor: Move reading of machine_nr, customer_nr and zone_nr upwards. 2023-10-18 11:28:06 +02:00
42961dea40 Add command-parameters yoctoVersionOption (yocto-version of
ATBUpdateTool as reported by opkg), and yoctoInstallStatusOption (info
if ATBUpdateTool is installed as reported by opkg).
2023-10-18 11:26:30 +02:00
fd2f601f67 Add printAupdateStatusMsg(). 2023-10-18 11:21:32 +02:00
b45af505cd Minor: change output format. 2023-10-18 11:21:08 +02:00
2dfe80b654 Add printUpdateStatusMsg() 2023-10-18 11:20:31 +02:00
4b9dcc5e99 Set version to 1.3.11: integrate extended version (VERSION + last git
commit) in send-last-version-message.
2023-10-12 12:13:49 +02:00
53639b55c9 Integrate call parameter -V (= --extended-version) to show extended
version (including last git commit)
2023-10-12 12:12:16 +02:00
3a83efbd3f Integrate extended version of ATBUpdateTool into send-last-version command to ISMAS. 2023-10-12 12:11:48 +02:00
c9d6a8d245 Integrate extended version of ATBUpdateTool into send-last-version 2023-10-12 12:10:24 +02:00
91db59b9f3 Integrate version of ATBUpdateTool in send-last-version. 2023-10-12 12:09:19 +02:00
1d81e6b650 Minor: removed unused code. 2023-10-12 12:08:19 +02:00
6d57b45d1a Use getATBQTVersion() for dynamic value of atbapp. 2023-10-12 12:07:38 +02:00
4e7ce2cd70 set version to 1.3.10: remove timeout on opkg-commands. tested download of DC2C_cash.conf file. 2023-10-10 16:05:16 +02:00
47fac31223 remove timeout for process runtime for opkg-commands 2023-10-10 16:03:52 +02:00
0d353cfbcf Check if opkg_command failed, and if this is the case, stop the
update-process without showing a wrong UPDATE-SUCCESS.
2023-10-10 16:01:45 +02:00
bdcb073bf8 set version to 1.3.9 2023-10-10 14:26:20 +02:00
226553a8ab Set version to 1.3.9 after fix of gitShowReason(). 2023-10-09 15:57:11 +02:00
d4ee56559b Use getShowReason() with branchName. Use getOsVersion(0 and
getApismVersion().
2023-10-09 15:54:58 +02:00
355b28ba40 Use current branch name in gitShowReason(), not just master 2023-10-09 15:53:35 +02:00
edeff35d7e Activate download of json-configuration files. 2023-10-06 13:02:23 +02:00
09d5de1b0b rsync: check if source directories in customer repositories exist. 2023-10-06 13:00:25 +02:00
145fdab26e Minor: fixed output format (in text edit) 2023-10-06 13:00:00 +02:00
89d1ec5b21 Deactivate download of device controller. Will be added in coming
version.
2023-10-06 12:58:38 +02:00
f38c975dc6 Remove check for which parent has started atbupdatetool: it is always
systemd now.
2023-10-06 12:57:38 +02:00
fba007aa35 Activate download of json-configuration files. 2023-10-06 12:56:03 +02:00
8b6adb3ea7 Scrolldown text edit at end of whole update process to show
UPDATE_SUCCESS message.
2023-10-06 12:51:30 +02:00
30603317c6 Minor: Add debug messages or change debug message format. 2023-10-06 10:47:08 +02:00
7083f3b4f8 Minor: add debug message. 2023-10-06 10:44:55 +02:00
7ff866525e Fix: allow for json-files EC2C_conf/cash/device.json as well. 2023-10-06 10:43:55 +02:00
2164037123 Do not use opkg_commnands-file as located in the filesystem under
/etc/psa_update anymore.
2023-10-06 10:42:35 +02:00
2e7d33c4c8 Set version to 1.3.7: wait forever for git-commands to finish in QProcess. 2023-10-05 10:54:19 +02:00
2a2751f6f3 Add creation of BUILD_DATE and BUILD_TIME 2023-10-04 14:34:54 +02:00
2764ef4371 Output of current tool version at program startup 2023-10-04 14:33:52 +02:00
9783f343e1 Set version to 1.3.6: exec opkg-commands even for unchanged opkg-commnds-file 2023-10-04 13:23:06 +02:00
8c6f0dfcc7 Minor: use Q_UNUSED() for unused parameters 2023-10-04 13:21:49 +02:00
d688ad3d5c Minor: comments 2023-10-04 10:34:56 +02:00
7b3f652b0e Deactivated doUpdate 2023-10-04 10:20:52 +02:00
4cc42b2a65 Set version to 1.3.6. Removed DCPlugin related stuff. 2023-09-28 12:11:47 +02:00
d783fd7fb6 Minor: print info instead of critical message when git fetch is empty 2023-09-28 12:09:57 +02:00
e0a0ff54e1 Added comment for U00-ISMAS values. Deactivated finalResult() 2023-09-28 12:08:42 +02:00
863d052a21 Removed debug info in appendText()/replcaeLst(). 2023-09-28 12:06:28 +02:00
61b3d29e31 Disable text-edit (showing update-status) ehen update procedure
finished.
2023-09-28 12:05:07 +02:00
cac4f7249e Minor: Add progress parameters. 2023-09-28 12:01:55 +02:00
3223c430be Do not rsync opkg_commnds to local file systems: this sync is not needed. 2023-09-28 12:00:24 +02:00
c09682ea33 Always execute commands contained in opkg_commands, even if no
change in opkg_commands. WAIT button in ISMAS must still be activated.
2023-09-28 11:57:17 +02:00
fef1d43d5f Adapated information of update-progress-status. 2023-09-28 11:56:20 +02:00
705424727b Removed call to final-result: ISMAS will not show U0002 twice. 2023-09-28 11:53:59 +02:00
b96f0896e3 Add explicit message when update-process is starting. 2023-09-28 11:53:10 +02:00
a3967c76ac Use showFullScreen() instead of using a flag 2023-09-28 11:47:09 +02:00
1197598a3d Set version to 1.3.5 2023-09-21 16:52:42 +02:00
ec13e97226 Update interfaces.h to HWapi/4.6 2023-09-21 15:55:31 +02:00
a8dd9d7e24 Merge branch 'master' of git.mimbach49.de:GerhardHoffmann/UpdatePTUDevCtrl 2023-09-21 15:50:19 +02:00
82751eb1d4 Set version to 1.3.4. 2023-09-11 10:14:41 +02:00
17a4a69df2 Added some debug output for parent-process-name 2023-09-11 10:14:04 +02:00
a03261d04a Fixed getParentName() to work analogously to isATBQTRunning(). 2023-09-11 10:12:20 +02:00
7832ef5d8c Set version to 1.3.3. 2023-09-10 17:22:49 +02:00
9c213d0a97 Added some better debug output in the slots concerned with the text edit
of the main window ("update status").
Use insertPlainText() when adding to the text edit to simplify code.
2023-09-10 16:55:32 +02:00
18378afdc5 Did some testing with event filters on the main window. not used. 2023-09-10 16:54:54 +02:00
6dd8a8c6b3 did some testing with event filter. not used. 2023-09-10 16:54:29 +02:00
adfb358e12 Add some output to see if start of the update tool is configured correctly. 2023-09-10 16:51:36 +02:00
ff418b11a1 Use plauginLoader as a dedicated static object. 2023-09-10 16:51:07 +02:00
38e79f0354 Use pluginLoader as a dedicated static object. 2023-09-10 16:50:19 +02:00
103b3f3f9c isATBQTRunning():
Use std::fstream to read /proc/<pid>/status, as the lines
end on '\r', not on '\n'.
2023-09-10 16:46:59 +02:00
ff6a6e0e45 Use libCAslave.so as default library to use for downloading the device-controller-firmware. 2023-09-10 16:45:40 +02:00
afbce3b4ea Add new dc-lib 2023-09-09 15:10:53 +02:00
823e59a582 Set version to 1.3.2 2023-09-09 15:09:59 +02:00
01cfbddfb1 Add update_dc_event 2023-09-09 15:08:03 +02:00
838efd3945 Show status message in the status bar of the GUI. 2023-09-09 15:06:58 +02:00
9a65cb4456 Add some debug message when adding content to the text edit of the GUI. 2023-09-09 15:06:21 +02:00
df0951d671 Add the steps to prepare the bootloader for device-controller-firmware download
using cutom-events of type update-dc-event.
2023-09-09 15:04:41 +02:00
60cc752819 Add connect for showning a status message in status bar. 2023-09-09 15:03:52 +02:00
57b4716e2a Add emergency test function: if device stays in bootloader then use it to
make the device to leave the bootloader.
2023-09-09 15:02:45 +02:00
15f28e9ffd Add m_update-object and prepare for doing the bootloader setup for downloading
the device controller firmware.
2023-09-09 15:01:03 +02:00
a07893ddab doUpdate() and updateDC(): communicate with main window to enter bootloader
(i.e. to prepare device for download of device controller firmware), but do not
perform an actual download at the moment.
2023-09-09 14:59:05 +02:00
d0eb1d12d8 Add some debug output to updateBinary(). 2023-09-09 14:57:49 +02:00
cd59a39569 Add some more debug output to updateBinary(). 2023-09-09 14:57:04 +02:00
67c8b2f472 Extended comment: USING THE BOOTLOADER. 2023-09-09 14:55:48 +02:00
5158878ce2 Extend comment for: USING THE BOOTLOADER.
Bitte geben Sie eine Commit-Beschreibung für Ihre Änderungen ein. Zeilen,
2023-09-09 14:54:48 +02:00
b6971c1db5 resetDeviceController(): deprecated. 2023-09-09 14:54:12 +02:00
9df46a1c49 Add some debug output to openSerial(). 2023-09-09 14:53:36 +02:00
6765b12f0c startBootloader(): deprecated. 2023-09-09 14:52:40 +02:00
3e925756cf Add getDcSoftAndHardWareVersion() utility. 2023-09-09 14:43:46 +02:00
b2798b349e Fixed reg-exp for name of device controller firmare version. 2023-09-09 14:41:53 +02:00
64dce44fe1 Move update-object into main window.
Activate using ISMAS WAIT button.
2023-09-09 14:40:43 +02:00
7e96b65c1b Move m_update-object to main window.
Add signal for showing status messages at status bar of ATBUpdateTool gui.
2023-09-09 14:38:53 +02:00
276d65a9d8 Add utility isATBQTRunning(). 2023-09-09 14:33:13 +02:00
ba71728979 Move the dc-plugin (and the update-object) into the gui-thread instead of the
worker thread, so the worker-thread cannot block itself when inside a slot
and therefore not able to react to qt-signals.
2023-09-09 14:30:53 +02:00
e82742a609 Add helper class update_dc_event to dend customer messages from the worker-thread
to the gui-thread. The gui-thread will then perform bl_rebootDC, bl_startBL,
bl_checkBL(), bl_isUp() and bl_stopBL().
2023-09-09 14:27:50 +02:00
6773a7243a Save name of device-controller-plugin (either libCAmaster or libCAslave)
in the Update-object.
2023-09-06 09:12:25 +02:00
22c8997f1e Set autoRequest to false and pass a pointer to the device-controller-plugin
to the main window instead to the worker (thread).
2023-09-06 09:10:14 +02:00
9531a08b4a Add a pointer to the device-controller-plugin. The main window will always be
owned by the GUI thread, and the GUI thread is loading the plugin. Hence the
worker-thread does not block itself when inside a QT slot.
2023-09-06 09:07:45 +02:00
c065b57f0c Remove direct member m_hw, a pointer to the device-controller-plugin.
The worker shall not load the plugin, otherwise it would block itself inside
an QT slot.
2023-09-06 09:04:43 +02:00
cef05b7511 Set version to 1.3.1: extended reason in send-last-version 2023-09-04 11:48:15 +02:00
bb35e985ad Using IsmasClient::getReasonForLastSendVersion() 2023-09-04 11:46:37 +02:00
981a2ea13a Added method getReasonForSendLastVersion() 2023-09-04 11:45:30 +02:00
b14b296011 Added utility getParentName() (name of parent process) 2023-09-04 11:42:12 +02:00
1ef9853876 Set version of UpdateTool to 1.3.0.
1.3.0: main change: add fetching info for current apiism-version and send
if to ISMAS.
2023-08-30 11:47:56 +02:00
01d8312aa8 Removed rauc/opkg-members. Added m_apismVersion member. 2023-08-30 11:46:55 +02:00
507586f9dc Added fetching apism-version. Fixed calls to final_result(). 2023-08-30 11:46:00 +02:00
12ffa71455 Removed rauc/opkg-versions. Added fetching of apism-version. 2023-08-30 11:44:20 +02:00
a84f495d43 Removed output of rauc/opkg-versions, and added output of apism-version. 2023-08-30 11:42:05 +02:00
7e4b5006eb Fixed return string of final result(). 2023-08-30 11:41:23 +02:00
0a28f0d82c Add message for current APISM version 2023-08-30 11:39:37 +02:00
5427844977 For each customer repository change the file ChangeLog in branch master
as last step. The git commit for this file will be used as output, so this file
has always be the last to be checked in.
2023-08-30 11:36:28 +02:00
a45e552d90 set version to 1.2.0 2023-08-25 08:46:29 +02:00
be28570d23 Minor change for output in status bar. 2023-08-23 16:27:47 +02:00
1509e8619c Send message to ISMAS when rsyncing a traiff-file 2023-08-23 16:26:55 +02:00
a8df026a80 Added rsyncFile() method. 2023-08-23 16:26:11 +02:00
a803907449 Refined information shown in status bar. 2023-08-22 13:49:42 +02:00
afd31f1b27 Fill in opkg related info. 2023-08-22 13:49:09 +02:00
f8fef38009 Add last-commit for traiff and add info for opkg_commands. 2023-08-22 13:47:57 +02:00
cbe1fd387d After "rsync", compare etc/psa_tariff and /etc/psa_tariff, if they contain the
same traiff-files (as they should).
2023-08-22 12:31:15 +02:00
1620b73d01 Added sameFilesInDirs(): check for two different directories if the contain the
same files (comparison by name and by git-blob).
2023-08-22 12:29:52 +02:00
4ebdcf56a0 Clear message before showing a new one. 2023-08-22 12:21:17 +02:00
99b9419150 Made gitBlob() static and execute in /tmp as this command can be executed
for every file not only the files contained in a git repository.
2023-08-22 12:19:25 +02:00
4307fb96a6 Add status bar instead of using an message box for displaying error messages. 2023-08-22 09:27:59 +02:00
c35390b6d6 removed commented code plannned for future use -> did not work 2023-08-22 09:27:20 +02:00
fff6bd2b49 Make room for status bar 2023-08-22 09:25:02 +02:00
631ade1954 Show the executed opkg-commands in the text edit. 2023-08-18 11:53:32 +02:00
337bdd1bb0 Add some debug output when restarting APISM. APISM is give a delay of 20s when restarting. 2023-08-18 11:52:34 +02:00
978cc16304 added signal onReplaceLast for QStringList 2023-08-18 11:51:27 +02:00
bea8242d6f Add some changes to the message box (to be chenged anyways later). 2023-08-18 11:50:14 +02:00
56daa84a14 Streamlined the connects (without any whitespace) to silence clang. 2023-08-18 11:49:34 +02:00
17ddfd0ddd Added slot onReplaceLast() to handle adding a QStringList to the text edit. 2023-08-18 11:48:29 +02:00
2ac8c4cfc6 Added scrollDownTextEdit() -> text edit is supposed to scroll down autmatically
in case too much info has to be displayed.
2023-08-18 11:46:57 +02:00
0559ff64e2 Extended some debug output 2023-08-18 11:42:47 +02:00
385a7b7b00 Added utility rstrip() to remove whitespace at the right end of a string. 2023-08-18 11:41:16 +02:00
503b7c64f9 Added some comments to prepare for future change. 2023-08-18 11:39:00 +02:00
beec9c2f9d Added properties to text edit:
Qt::ScrollBarAsNeeded, Qt::ScrollBarAsNeeded, QAbstractScrollArea::AdjustToContents.
2023-08-18 11:37:24 +02:00
5263b7de0f Removed some DEBUG-output. 2023-08-16 12:42:10 +02:00
9b4d0494c8 Streamlined handling of UPDATE_STATUS. 2023-08-16 12:41:42 +02:00
0f2ee0349f Improved debug output. 2023-08-16 10:39:46 +02:00
e700a40875 Initialize psaInstalled.versionInfo to be sent to ISMAS in sendLastVersion(). 2023-08-16 10:38:36 +02:00
1eba5338e4 Removed obsolete sendCmdSendVersionToIsmas(). 2023-08-16 10:37:59 +02:00
f20be9ddcf Removed obsolete sendCmdSendVersionToIsmas(). 2023-08-16 10:37:31 +02:00
7631c05e22 Insert last commit-id, message and date of last commit into sendLastVersion-message to ISMAS. 2023-08-16 10:34:55 +02:00
ad93e536f0 Added gitShowReason(): get lastCommit, message and date of last commit to insert inti
sendLastVersion-message to ISMAS.
2023-08-16 10:33:08 +02:00
259da8200e Fixed known defaults for starting ATBUpdateTool. 2023-08-14 15:08:00 +02:00
8d528f0f55 SO_SNDTIMEO and SO_RCVTIMEO socket options have turned out not to be reliable.
Use select() for detecting timeout on socket (read and write).
2023-08-14 14:35:54 +02:00
66d0214720 Always look for {"error": "ISMAS is offline"} first.
Allow for empty update-trgger (try again)
rsync: mkdir -p the necessary directories.
2023-08-14 14:33:12 +02:00
86064979b4 Add memeber-variable for exitCode of executed process. 2023-08-14 14:28:23 +02:00
86c996d7ac Set final version to 1.1.1w 2023-08-11 12:24:51 +02:00
effe41bac9 Use finalResult() to sent a final message to ISMAS about the result of the update process. 2023-08-11 12:20:09 +02:00
4968942cc2 Added finalResult() -> send final message to ISMAS in any case. 2023-08-11 12:19:10 +02:00
769626581f Use head -n 1 in gitLastCommit(). 2023-08-11 11:12:59 +02:00
0e97ce7dc2 Take the pipe symbol into account in output of git getch command. 2023-08-11 11:12:09 +02:00
34334676fc Use print-utis from utils.h. 2023-08-11 11:11:14 +02:00
853787cd4b Added lastCommit to tariff-struct. Added jsonParseFailed(). 2023-08-11 11:09:51 +02:00
a932ed5471 Hide button on error message box. 2023-08-11 11:07:16 +02:00
c338884fc7 Use print-util in utils.h. 2023-08-11 11:06:38 +02:00
ed6df98653 Set progress bar timer to 500ms.
Yield thread to give worker thread its share of CPU time.
2023-08-11 11:05:33 +02:00
9eb458c4c5 Start Application after 1 second delay. 2023-08-11 11:05:02 +02:00
202e83268b Added utilities
void printCriticalErrorMsg(QString const &errorMsg);
    void printInfoMsg(QString const &infoMsg);
    void printLineEditInfo(QStringList const &lines);
    QString getTariffLoadTime(QString fileName);
2023-08-11 11:03:21 +02:00
8f26bfee0f Get last commit date and loadtime of tariff-file. 2023-08-11 11:02:15 +02:00
1af136e39d Set delay when trying to fetch value of update-trigger. 2023-08-11 11:01:26 +02:00
a550d55004 Get triggerValue directly from JSON. 2023-08-11 11:00:52 +02:00
a37a22d3f9 Show message boxes when running UpdateTool manually. 2023-08-11 11:00:02 +02:00
a8941f4ef4 Set progress values. 2023-08-11 10:59:26 +02:00
746565dbe0 Add messages sent to ISMAS. 2023-08-11 10:58:29 +02:00
79906df12e Add some qt debug aoutput. 2023-08-11 10:56:36 +02:00
edf1d105e7 Deactivate backendConnected(). 2023-08-11 10:55:53 +02:00
6c4b02cb56 Use print-utils to print some debug messages. 2023-08-11 10:52:31 +02:00
04d5061d79 Added some constants. 2023-08-11 10:49:34 +02:00
042e6dfa38 Try to establish a connection to backend 50x. 2023-08-09 16:17:28 +02:00
e523d3cc2c Added/chenged some debug output. 2023-08-09 16:16:36 +02:00
927197d0d1 Removed restart of APISM. 2023-08-09 16:14:59 +02:00
72cb738af5 Removed handling of pipe symbol (if available in git output). 2023-08-09 16:12:56 +02:00
0fb38013f7 Work with device controller file directly, not via link.
Add startProgress/stopProgress().
2023-08-09 15:09:44 +02:00
5f1376cf1e Removed buttons reserved for future use.
Start application automatically, not via Start-Button.
2023-08-09 15:08:22 +02:00
5db7b4224e Made start/stopProgress() public. 2023-08-09 15:06:20 +02:00
7c7adc94e6 Removed Start-button, because it is not needed.
Removed buttons reserved for future use.
2023-08-09 15:05:04 +02:00
89c2d3a8ae Added some debug output. 2023-08-09 15:03:14 +02:00
c2b52aa91d Only check for keyword RECORD to be more general. 2023-08-09 15:01:43 +02:00
329c770aa0 Use a message box with color red and a timer to click on ok after 5 secs. 2023-08-07 14:01:51 +02:00
cdb045b72b Fixed onAppendText() and onReplaceLast():
Watch out for special suffixes.
2023-08-07 13:59:44 +02:00
6d43cf4c9f Send custom events to the progress bar according to the state of the update process.
Changed the handling of messages for the text edit.
2023-08-07 13:56:51 +02:00
4caa0c0d83 Removed obsolete out-commented lines. 2023-08-07 13:55:52 +02:00
4d2d38e45c Default parameter for onReplaceLast() 2023-08-07 13:53:48 +02:00
9a55ce18e4 Chawnged font to Misc Fixed 2023-08-07 13:53:04 +02:00
223c7a8f8d Added errorGitClone(). 2023-08-07 13:51:47 +02:00
ce72d3d14d For the error EWOULDBLOCK try again 10 times until quitting. 2023-08-07 13:50:59 +02:00
8b66c47e49 Added errorGitClone() 2023-08-07 13:50:14 +02:00
4ff3b0efdf Advance the progress bar in the foreground when a long running task
in the background (e.g. git clone).
2023-08-06 20:44:26 +02:00
1fd2269753 onAppendText() only appends text.
onReplaceLast() replaces the last line in the text edit window.
2023-08-06 14:14:47 +02:00
a8994423f4 Change font size for full screen window. 2023-08-06 14:11:41 +02:00
4594c913e0 Using syslog for debugging. 2023-08-06 07:34:17 +02:00
cf9033e898 Send custom event from worker(-thread) to MainWindow in order to update
progress bar.
2023-08-05 18:50:50 +02:00
b09ccfd4f5 Add custom ProgressEvent class for future use. 2023-08-04 14:10:47 +02:00
6b4c486549 Add m_ismasClient.updateOfPSASucceeded() for a successful run. 2023-08-04 13:53:55 +02:00
9c44656104 Add parameter -vv fot the rsync command to see more debugoutput. 2023-08-04 13:52:57 +02:00
d57914957d Add text-edit entry if git pull is successful. 2023-08-04 13:50:19 +02:00
c4f12ce75a Added terminal-debug-output. 2023-08-04 13:49:32 +02:00
4ad370ea46 Extended displayed messages in text-edit. 2023-08-04 13:48:40 +02:00
44ad3caf2b Stop exit timer in onQuit.
Activate error message boxes.
2023-08-04 13:45:56 +02:00
427a272f8f Connect exit-button with clicked(). 2023-08-04 13:45:19 +02:00
96fb50e68d Moved init. of text-edit upwards.
Hide reserved buttons.
2023-08-04 13:44:16 +02:00
19274546c9 set main window fullscreen 2023-08-04 13:39:17 +02:00
d2d730589b changed return type of returnCode() ti 'int' 2023-08-04 13:38:33 +02:00
f88b0edb2a Fix for the case when several branches are edited: 'git fetch' will
display several lines then, not only one.
2023-08-04 13:35:42 +02:00
c62299aa72 Read from /mnt/system_data/ instead of /etc. 2023-08-04 13:33:25 +02:00
c054668eac Ask for the reurn code of the process, not only for process exit status. 2023-08-04 13:31:12 +02:00
82352713f1 Added 6 more buttons for future use and for the fullscreen layout. 2023-08-04 13:30:18 +02:00
48073ab1f0 update to next version 2023-08-03 10:14:23 +02:00
c6e98f50c2 call onQuit() directly whem timer runs out 2023-08-03 10:14:09 +02:00
ef88fdc9a4 Fix: config for deployment 2023-08-03 09:50:26 +02:00
8889aaca2a Fix: path for deployment 2023-08-03 09:44:08 +02:00
9b08420ac1 Use exit() instead of quit() to be able to add a returnCode in case of failure. 2023-08-03 09:06:50 +02:00
0ee92f0181 disbale exit-button as long as update process is running 2023-08-02 17:51:35 +02:00
a995cae000 set exit timer to 10 secs 2023-08-02 16:54:47 +02:00
0b7d504a7a Fixed missing git pull command.
Fixed missing update for text-edit when only clone the customer repository.
2023-08-02 16:53:19 +02:00
6a4a852fd6 Updated for new device controller lib (CAmaster) 2023-08-02 15:52:01 +02:00
81a9304438 Worker is the work-horse of the update process.
Using ismas-client it sends requests to APISM and gets results in a synchronous fashion.
Add previous emits to git client and apism client have been removed.
2023-08-02 15:50:04 +02:00
332d689b8c Add worker-object to be able to update for the worker. 2023-08-02 15:47:19 +02:00
b508c0517d Reset.
Add helper functions read1stLineOfFile() and zoneNr().
2023-08-02 15:45:20 +02:00
d2c0fdf820 Minor change: remove header 2023-08-02 15:44:45 +02:00
7293afd203 Minor change: include header 2023-08-02 15:44:16 +02:00
2fd1053bf9 Removed maintenance mode 2023-08-02 15:40:44 +02:00
e0a68a35f4 raised output-buffer length to 1024*8 2023-08-02 15:39:52 +02:00
4e277b4ca6 Added text edit 2023-08-02 15:39:08 +02:00
b3f4a4086b Populate text-edit with run-time info from update-process. 2023-08-02 15:29:10 +02:00
eb7d77692b Removed obsolete apism/tcp-client files. 2023-08-02 15:20:07 +02:00
ce1b1859df Removed apism-client and tcp-client. All done inside of ismas client now. 2023-08-02 15:18:39 +02:00
371cc1a187 Removed communication with ISMAS from git client. 2023-08-02 15:17:10 +02:00
a94606ca89 Added sendRequestReceiveResponse(): static member for communication with
APISM, on ports 7777 and 7778.
Each request is immediately handled in a synchronous fashion.
Added several helper functions to format messages to be sent to APISM/ISMAS.
2023-08-02 15:13:05 +02:00
5c9c9dc917 add mainwindow and utils 2023-07-31 16:56:16 +02:00
ab8acfc7d1 use gui-interface for ATBUpdateTool 2023-07-31 16:55:36 +02:00
1ddd0074b3 Set c++17 for PTU5-YOCTO 2023-07-20 09:06:07 +02:00
2b934f6baf Added signals
void sendCmdSendVersionToIsmas(QString);
    void sendCmdEventToIsmas(QString);
and associated slots:
    void onSendCmdSendVersionToIsmas(QString);
    void onSendCmdEventToIsmas(QString);
Implemented slots.
2023-07-19 16:53:45 +02:00
1bdae56342 Added debug messages.
Fixed gitBlob(): do not check for existing customer repository.
2023-07-19 16:50:54 +02:00
6caaae6d50 Add struct PSAInstalled.
Changed interface of updateOfPSASendVersion into
    QString updateOfPSASendVersion(PSAInstalled const &psa);

Re-implemented updateOfPSASendVersion().
2023-07-19 16:48:42 +02:00
4dc0c71d3f Add -g flag even for release build 2023-07-19 16:46:29 +02:00
a54f4f94c9 Minor change: remove not needed customerNrStr-variable 2023-07-19 16:45:43 +02:00
b8d6c909eb Minor change: reset output buffer-length to 1024 2023-07-19 16:44:26 +02:00
e35fb9dc19 Commented out debug-messages 2023-07-19 16:43:24 +02:00
0e0363f131 Additinal UPDATE_STATE constants.
Added struct UpdateStatus for printing debug messages.
Added member variables used for sending SENDCMD to ISMAS.A
Added helper functions
    getATBQTVersion(),
    getCPUSerial(),
    getRaucVersion(),
    getOpkgVersion(),
    getPluginVersion(),
    getDCVersion(),
    getFileSize().

Removed automatic restart of Apism.
Added emergency timer to end application after 10 mintes.
onHandleChangedFilenames():
    handling of opkg_commands
    handling of json/dc -> deactivated for the moment.
Re-implemented onSendCmdSendVersionToIsmas(): use only one parameter of type PSAInstalled.
Implemented operators<<() to print debug messages.
2023-07-19 16:42:18 +02:00
088d7c8aa0 Additinal UPDATE_STATE constants.
Added struct UpdateStatus for printing debug messages.
Added member variables used for sending SENDCMD to ISMAS.A
Added helper functions
    getATBQTVersion(),
    getCPUSerial(),
    getRaucVersion(),
    getOpkgVersion(),
    getPluginVersion(),
    getDCVersion(),
    getFileSize().
2023-07-19 16:35:48 +02:00
60084450e6 Removed 't (testMode)' and 'e (executeScript)' options. Adapted call to Worker-ctor. 2023-07-17 16:54:18 +02:00
9775792916 Minor change: extended debug-output. 2023-07-17 16:52:27 +02:00
93d6277386 Simplified interface. 2023-07-17 16:51:40 +02:00
37c5c7c4f6 Simplified interface.
When doing a clone of the repository, do not execute any other commands.
2023-07-17 16:49:44 +02:00
5eb33f3e31 Minor change: renamed function sendUpdateInfToIsmas() to
emulateUpdatesAvailable() for clarity.
2023-07-17 16:48:04 +02:00
43f5f3ecae Removed usage of any files. Removed any dependencies on git-hooks.
Removed handling of opkg-related things: they are done now inside the worker itself.
2023-07-17 16:45:11 +02:00
c503750e90 Simplified interface of ctor.
onHandleChangedFiles(): split handling of opkg_commands-file and
downloading of DC/JSON-Files.
rsync to file-system only once these operations were successful.
2023-07-17 16:43:05 +02:00
7054c18113 Extended UPDATE_STATUS enum.
Simplified interface.
Read machine_nr, cust_nr, zone_nr from file.
2023-07-17 16:38:53 +02:00
077eb803a1 plan fuer das update 2023-07-16 18:31:15 +02:00
30 changed files with 4658 additions and 2582 deletions

View File

@@ -1,48 +1,84 @@
# QT -= gui
QT += core
QT += core gui
QT += widgets serialport network
# QT += network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = ATBUpdateTool
VERSION=1.0.0
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
# 1.3.6 : Do not update device-controller/json files, but have the library
# (in a later step) do that.
# Fixed sending messages to ISMAS.
# Always execute contents of opkg_commands-file (even if there are no
# changes).
# 1.3.7 : Wait forever for git-commands to finish in QProcess executing such
# a command.
# 1.3.8 : Remove accessing opkg_commands under file-system-path /etc/psa_update.
# Activate download of json-configuration files.
# 1.3.9 : Fix sendLastVersion: use configured branch and not master branch in
# git show origin/master -s --format="c=%h m=%s d=%cI" ==>
# git show origin/zg1/zone1 -s --format="c=%h m=%s d=%cI"
# Use dynamic values for os-release and apism-version when sending
# last version info.
# 1.3.10: Fix premature killing opkg-commands: detected timeout of 100s was
# too small when updating apism.
# Fix display of UPDATE_SUCCESS when opkg_command fails. Detected when
# updating apsim failed.
# 1.3.11: Integrate version of ATBUpdateTool in SendLastVersion-ISMAS-message.
# 1.3.12: Add command parameters for output of yocto-infos about ATBUpdateTool.
# Use 'git pull' instead of 'git fetch'.
# Use 'git clone --filter=blob:none' instead of 'git clone' to speed
# up cloning of customer repository.
win32 {
BUILD_DATE=$$system("date /t")
BUILD_TIME=$$system("time /t")
} else {
BUILD_DATE=$$system("date +%d-%m-%y")
BUILD_TIME=$$system("date +%H:%M:%S")
}
GIT_COMMIT=$$system("git log -1 --format=oneline | cut -d' ' -f1")
VERSION="1.3.11"
EXTENDED_VERSION="$${VERSION}-$${GIT_COMMIT}"
INCLUDEPATH += plugins
CONFIG += c++17 console
CONFIG += c++17
# CONFIG -= app_bundle
DEFINES+=APP_VERSION=\\\"$$VERSION\\\"
DEFINES+=APP_BUILD_DATE=\\\"$$BUILD_DATE\\\"
DEFINES+=APP_BUILD_TIME=\\\"$$BUILD_TIME\\\"
DEFINES+=APP_EXTENDED_VERSION=\\\"$$EXTENDED_VERSION\\\"
QMAKE_CXXFLAGS += -g
QMAKE_CXXFLAGS += -Wno-deprecated-copy
# custom target for 'git subtree'
# subtree.target = subtree
# subtree.commands = git subtree add --prefix DCPlugin https://git.mimbach49.de/GerhardHoffmann/DCPlugin.git master --squash
# subtree.depends =
# QMAKE_EXTRA_UNIX_TARGETS += subtree
# ! exists(DCPlugin) {
# $$system("git subtree add --prefix DCPlugin https://git.mimbach49.de/GerhardHoffmann/DCPlugin.git master --squash")
# } else {
# $$system("git subtree pull --prefix DCPlugin https://git.mimbach49.de/GerhardHoffmann/DCPlugin.git master --squash")
# }
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
contains( CONFIG, PTU5 ) {
greaterThan(QT_MAJOR_VERSION, 4): QT += serialport
CONFIG += link_pkgconfig
lessThan(QT_MAJOR_VERSION, 5): PKGCONFIG += qextserialport
QMAKE_CXXFLAGS += -std=c++17 # for GCC >= 4.7
QMAKE_CXXFLAGS += -O2 -std=c++17 # for GCC >= 4.7
QMAKE_CXXFLAGS += -Wno-deprecated-copy
ARCH = PTU5
DEFINES+=PTU5
}
contains( CONFIG, PTU5_YOCTO ) {
greaterThan(QT_MAJOR_VERSION, 4): QT += serialport
QMAKE_CXXFLAGS += -std=c++17 # for GCC >= 4.7
QMAKE_CXXFLAGS += -Wno-deprecated-copy
PTU5BASEPATH = /opt/devel/ptu5
ARCH = PTU5
DEFINES+=PTU5
@@ -64,10 +100,12 @@ contains( CONFIG, DesktopLinux ) {
SOURCES += \
main.cpp \
progress_event.cpp \
update_dc_event.cpp \
mainwindow.cpp \
utils.cpp \
update.cpp \
git/git_client.cpp \
apism/apism_client.cpp \
apism/apism_tcp_client.cpp \
ismas/ismas_client.cpp \
process/command.cpp \
message_handler.cpp \
@@ -76,9 +114,11 @@ SOURCES += \
HEADERS += \
update.h \
progress_event.h \
update_dc_event.h \
utils.h \
mainwindow.h \
git/git_client.h \
apism/apism_client.h \
apism/apism_tcp_client.h \
apism/ismas_data.h \
ismas/ismas_client.h \
process/command.h \
@@ -87,13 +127,14 @@ HEADERS += \
worker_thread.h \
plugins/interfaces.h
OTHER_FILES += \
/opt/app/tools/atbupdate/update_log.csv \
main.cpp.bck \
main.cpp.bck2 \
main.cpp.bck3
FORMS += \
mainwindow.ui
# https://blog.developer.atlassian.com/the-power-of-git-subtree/?_ga=2-71978451-1385799339-1568044055-1068396449-1567112770
# git subtree add --prefix DCPlugin https://git.mimbach49.de/GerhardHoffmann/DCPlugin.git master --squash
# git subtree pull --prefix DCPlugin https://git.mimbach49.de/GerhardHoffmann/DCPlugin.git master --squash
# include(./DCPlugin/DCPlugin.pri)
##########################################################################################
# for running program on target through QtCreator
contains( CONFIG, PTU5 ) {
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/app/tools/atbupdate/
!isEmpty(target.path): INSTALLS += target
}

126
allgemein.txt Normal file
View File

@@ -0,0 +1,126 @@
0:
Zunaechst wir immer nachgesehen, ob das Repository bereits geklont wurde.
Wenn nein, dann wird geklont. Im Master-Branch befinden sich Dateinen, die
in allen anderen Branches gemeinsam genutzt werden. Das sollte wohl auf
jeden Fall fuer den DeviceController gelten.
1:
Anfrage bei ISMAS ob neue Daten da sind. Hier faengt die ganze Sache an.
Da man nicht weiss, on ISMAS ueberhaupt antworten wird braucht es einen
Timer. Man kann die Sache dann auch gleich mehrfach versuchen.
Die Anfrage geht also per Signal an den Apism-Client. Im Signal wird der
Zustand mitgegeben:
ISMAS_UPDATE_REQUEST_PENDING
und im Slot dann entsprechend uebernommen. Der Timer sollte nicht im Worker
stehen, sondern im Apism-Client. Der Worker sollte damit nicht belastet
sein. Der wird dann vom Apism-Client entsprechend benachrichtigt.
ISMAS_UPDATE_REQUEST_FAILURE
ISMAS_UPDATE_REQUEST_TIMEOUT
ISMAS_UPDATE_REQUEST_SUCCESS
Im 1./2.Fall wird dann ein Fehlerhandling durchgefuehrt. Insbesondere
wird Apism ueber den DB-Kanal darueber informiert (U0003). Am Ende wie
dann wie immer SendCmdVersion und beenden der Applikation.
Im Erfolgsfall muss in der Antwort der Branch enthalten sein, fuer den das
Update durchgefuhrt werden soll. Das ist auch bereits so (Location).
Ein Sonderfall waere der -m-Modus, da hier niemand auf der ISMAS-Seite
aktiv eingreift. Hier braucht es dann einen Ubergabeparameter an
ATBUpdateTool: --branch-name
Normalfall ist aber die Aktivierung via ISMAS. Dann muss der in der Antwort
enthaltene Branch zunaechst ausgecheckt werden.
2:
Dazu wird ein entsprechendes Signal an den GitClient gesandet.
GIT_CHECKOUT_BRANCH_REQUEST
Der GitClient sieht jetzt erstmal nach, ob es diesen Branch ueberhaupt
gibt.
Falls nein:
GIT_CHECKOUT_BRANCH_REQUEST_FAILURE
BRANCH_NOT_EXIST
Falls ja:
der GitClient versucht nun den Branch auszuchecken.
Geht das gut?
Falls nein:
GIT_CHECKOUT_BRANCH_REQUEST_FAILURE
BRANCH_CHECKOUT_ERROR
Falls ja:
GIT_CHECKOUT_BRANCH_REQUEST_SUCCESS
-> eventl. koennte man den Namen des Branches mitgeben
Signal an den Worker. Im entsprechenden Slot wird jetzt der eigentliche
UpdateProzess angeworfen.
3:
Mittels git fetch werden jetzt die Aenderungen ermittelt.
GIT_FETCH_UPDATES_REQUEST
GIT_FETCH_UPDATES_REQUEST_FAILURE
-> Worker informieren damit Fehlerbehandlung aktiviert wird
GIT_FETCH_UPDATES_REQUEST_SUCCESS
GIT_DIFF_UPDATES
Die Liste der geaenderten Dateien geht an den Worker. Der kann jetzt
noch Test machen, ob das Sinn macht. Evtl. eine Statusmeldung ausgeben
drueber was gleich passieren wird.
4:
Mittels git merge/pull werden nun die Dateien tatsaechlich im Branch
aktualisiert.
GIT_PULL_UPDATES_REQUEST
GIT_PULL_UPDATES_REQUEST_FAILURE
-> Worker informieren damit Fehlerbehandlung aktiviert wird
GIT_PULL_UPDATES_REQUEST_SUCCESS
-> Worker informieren
Sind die Daten vorhanden, dann werden sie mittels rsync ins Dateisystem
kopiert.
RSYNC_UPDATES
RSYNC_UPDATES_FAILURE
RSYNC_UPDATES_SUCCESS
Fuer jede kopierte Datei geht eine Nachricht raus ans ISMAS.
5:
Sind die Daten dann kopiert, so werden sie auf den DC kopiert (falls
notwendig): hier gehen dann wieder Nachrichten an ISMAS raus:
DC_UPDATE
DC_UPDATE_FAILURE
DC_UPDATE_SUCCESS
JSON_UPDATE
JSON_UPDATE_FAILURE
JSON_UPDATE_SUCCESS
Hier fuer jedes Json-File.
Schliesslich noch eine Zusammenfasung an ISMAS:
ISMAS_UPDATE_INFO_CONFIRM
ISMAS_UPDATE_INFO_CONFIRM_FAILURE
-> Worker informieren damit Fehlerbehandlung aktiviert wird
ISMAS_UPDATE_INFO_CONFIRM_SUCCESS
und schliesslich der abschliessende Status an ISMAS zurueckgemeldet:
ISMAS_CURRENT_PSA_STATUS_CONFIRM
ISMAS_CURRENT_PSA_STATUS_CONFIRM_FAILURE
ISMAS_CURRENT_PSA_STATUS_CONFIRM_SUCCESS

View File

@@ -1,811 +0,0 @@
#include "apism_client.h"
//#include "support/VendingData.h"
//#include "support/PersistentData.h"
//#include "support/utils.h"
//#include "ATBHMIconfig.h"
#include <QDebug>
#include <QByteArray>
#include <QHostAddress>
#include <QString>
#include <QList>
#include <QListIterator>
#include <QScopedPointer>
#include <QUuid>
#include <QProcess>
#include <QRegularExpression>
// Q_DECLARE_METATYPE(QAbstractSocket::SocketError)
ApismClient::ApismClient(QObject *eventReceiver, ATBHMIconfig *config, PersistentData *persistentData, QObject *parent)
: QObject(parent)
, healthEventReceiver(eventReceiver)
, m_config(config)
, persistentData(persistentData)
, lastError(0)
, lastErrorDescription("")
, currentRequestUid("")
, currentRequest(ISMAS::REQUEST::NO_REQUEST)
{
this->apismTcpSendClient = new ApismTcpClient("127.0.0.1", "7777", this);
this->apismTcpRequestResponseClient = new ApismTcpClient("127.0.0.1", "7778", this);
connect(apismTcpRequestResponseClient, &ApismTcpClient::receivedData,
this, &ApismClient::onReceivedResponse);
connect(apismTcpRequestResponseClient, &ApismTcpClient::responseTimeout,
this, &ApismClient::onRequestResponseClientResponseTimeout);
connect(apismTcpSendClient, &ApismTcpClient::responseTimeout,
this, &ApismClient::onSendClientResponseTimeout);
// not needed as APISM closes the socket after we send data, so readyRead()
// might not even fire
// connect(&m_socket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
// defined for Qt >= 5.15, we have 5.12
// qRegisterMetaType<QAbstractSocket::SocketError>();
// connect(&m_socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
// this, SLOT(onSocketError(&QAbstractSocket::SocketError)));
}
ApismClient::~ApismClient() {
}
void ApismClient::restartApism() {
QProcess::startDetached("/bin/systemctl", {"restart", "apism"});
}
//void ApismClient::onReadyRead() { // parse APISM response
// QByteArray data = m_socket.readAll();
// qCritical() << "APISM-RESPONSE = (" << endl << data << endl << ")";
//}
void ApismClient::sendTransaction(const VendingData *vendingData) {
#if 0
QScopedPointer<ISMAS::TransferData> transferData(new ISMAS::TransferData());
PAYMENT_VARIANTS::TYPE paymentType = vendingData->getParameter("PaymentType").value<PAYMENT_VARIANTS::TYPE>();
////////////////////// DEVICE //////////////////////////////////
bool deviceSet = false;
//QJsonValue tariffId("TariffInfo");
//transferData->device.insert("TARIFID", tariffId);
//QJsonValue group("group");
//transferData->device.insert("GROUP", group);
//QJsonValue zone("zone");
//transferData->device.insert("ZONE", zone);
if (deviceSet) {
transferData->insert("DEVICE", transferData->device);
}
////////////////////// TRANSACTION /////////////////////////////
bool transactionSet = false;
if (vendingData->hasParameter("TRANSACTION_STATE")) {
QVariant tstate = vendingData->getParameter("TRANSACTION_STATE");
if (tstate.isValid() && tstate.type() == QVariant::Int) {
transferData->transaction.state = tstate.toInt();
transferData->transaction.insert("STATE", transferData->transaction.state);
transactionSet = true;
}
}
if (vendingData->hasParameter("TRANSACTION_UID")) {
QVariant tuid = vendingData->getParameter("TRANSACTION_UID");
if (tuid.isValid() && tuid.type() == QVariant::String) {
transferData->transaction.uid = tuid.toString();
transferData->transaction.insert("UID", transferData->transaction.uid);
this->persistentData->setLastTransactionUID(tuid.toString());
transactionSet = true;
}
}
if (vendingData->hasParameter("TRANSACTION_TIMESTAMP")) {
QVariant tstamp = vendingData->getParameter("TRANSACTION_TIMESTAMP");
if (tstamp.isValid() && tstamp.type() == QVariant::String) {
transferData->transaction.timestamp = tstamp.toString();
transferData->transaction.insert("TIMESTAMP", transferData->transaction.timestamp);
transactionSet = true;
}
}
if (vendingData->hasParameter("TRANSACTION_TICKET_SEQUENCE_NUMBER")) {
QVariant tsn = vendingData->getParameter("TRANSACTION_TICKET_SEQUENCE_NUMBER");
if (tsn.isValid() && tsn.type() == QVariant::Int) {
transferData->transaction.seq_tick_number = tsn.toInt();
transferData->transaction.insert("TICKETNU", transferData->transaction.seq_tick_number);
transactionSet = true;
}
}
if (vendingData->hasParameter("LICENSEPLATE")) {
QVariant lp = vendingData->getParameter("LICENSEPLATE");
transferData->transaction.userText = QJsonValue::fromVariant(lp);
transferData->transaction.insert("USERTEXT", transferData->transaction.userText);
transferData->transaction.userTextType = "license plate";
transferData->transaction.insert("USERTEXTTYPE", transferData->transaction.userTextType);
transactionSet = true;
}
if (transactionSet) {
transferData->insert("TRANSACTION", transferData->transaction);
}
////////////////////// ITEM //////////////////////////////////
bool itemSet = false;
if (vendingData->hasParameter("PermitType")) {
QVariant idVariant = vendingData->getParameter("PermitType");
transferData->item.id = idVariant.toString();
transferData->item.insert("ID", transferData->item.id);
itemSet = true;
}
if (vendingData->hasParameter("Product")) {
QVariant nameVariant = vendingData->getParameter("Product");
transferData->item.name = nameVariant.toString();
transferData->item.insert("NAME", transferData->item.name);
itemSet = true;
}
if (vendingData->hasParameter("PRICE_INFO_GROSS")) {
int priceUint = vendingData->getUintParameter("PRICE_INFO_GROSS");
transferData->item.price = priceUint;
transferData->item.insert("PRICE", transferData->item.price);
itemSet = true;
}
if (vendingData->hasParameter("PERIOD_START")) {
QVariant startTimeVariant = vendingData->getParameter("PERIOD_START");
transferData->item.startTime = utils::getISODateTimeWithMsAndOffset(startTimeVariant);
transferData->item.insert("STARTTIME", transferData->item.startTime);
itemSet = true;
}
if (vendingData->hasParameter("PERIOD_END")) {
QVariant endTimeVariant = vendingData->getParameter("PERIOD_END");
transferData->item.endTime = utils::getISODateTimeWithMsAndOffset(endTimeVariant);
transferData->item.insert("ENDTIME", transferData->item.endTime);
itemSet = true;
}
if (vendingData->hasParameter("ITEM_PRINT_TEXT")) {
QVariant textVariant = vendingData->getParameter("ITEM_PRINT_TEXT");
transferData->item.printText = textVariant.toString();
transferData->item.insert("PRINTTEXT", transferData->item.printText);
itemSet = true;
}
// set static data:
// currency
if (itemSet) {
transferData->item.currency = this->m_config->getPaymentCurrencyISOCode();
transferData->item.insert("CURRENCY", transferData->item.currency);
}
if (itemSet) {
transferData->insert("ITEM", transferData->item);
}
////////////////////////////////////////////////////////////////////////
////////////////////// PAYMENT //////////////////////////////
bool paymentSet = false;
/////////////////////////// PAYMENT.CASH /////////////////////////////
if (paymentType == PAYMENT_VARIANTS::TYPE::CASH) {
bool cashSet = false;
if (vendingData->hasParameter("PaymentCashCoins")) {
QVariant coins = vendingData->getParameter("PaymentCashCoins");
transferData->payment.cash.coins = coins.toInt();
transferData->payment.cash.insert("COINS", transferData->payment.cash.coins);
cashSet = true;
}
if (vendingData->hasParameter("PaymentCashChange")) {
QVariant change = vendingData->getParameter("PaymentCashChange");
transferData->payment.cash.change = change.toInt();
transferData->payment.cash.insert("CHANGE", transferData->payment.cash.change);
cashSet = true;
}
if (vendingData->hasParameter("PaymentCashOverpaid")) {
QVariant overpaid = vendingData->getParameter("PaymentCashOverpaid");
transferData->payment.cash.overpaid = overpaid.toInt();
transferData->payment.cash.insert("OVERPAID", transferData->payment.cash.overpaid);
cashSet = true;
}
// currency
if (cashSet) {
transferData->payment.cash.currency = this->m_config->getPaymentCurrencyISOCode();
transferData->payment.cash.insert("CURRENCY", transferData->payment.cash.currency);
}
if (cashSet) {
transferData->payment.insert("CASH", transferData->payment.cash);
paymentSet = true;
}
}
/////////////////////////// PAYMENT.CARD /////////////////////////////
if (paymentType == PAYMENT_VARIANTS::TYPE::CARD) {
paymentSet = true;
bool cardSet = true;
transferData->payment.card.insert("CARDNU", "unknown");
transferData->payment.card.insert("VALUE", transferData->item.price);
transferData->payment.card.insert("CARDTYPE", "unknown");
transferData->payment.card.currency = this->m_config->getPaymentCurrencyISOCode();
transferData->payment.card.insert("CURRENCY", transferData->payment.card.currency);
// transferData->payment.card.insert("TERMINALID", tid);
//transferData->payment.card.insert("TERMINALRESULT", tresult);
if (cardSet) {
transferData->payment.insert("CARD", transferData->payment.card);
paymentSet = true;
}
}
if (paymentSet) {
transferData->insert("PAYMENT", transferData->payment);
}
////////////////////////////////////////////////////////////////////////
///////////////////////////// RESULT /////////////////////////////////
bool resultSet = false;
if (vendingData->hasParameter("RESULT_DELIVERY")) {
QVariant delVariant = vendingData->getParameter("RESULT_DELIVERY");
transferData->result.delivery = delVariant.toJsonValue();
transferData->result.insert("DELIVERY", transferData->result.delivery);
resultSet = true;
}
if (vendingData->hasParameter("RESULT_RESULT")) {
QVariant resVariant = vendingData->getParameter("RESULT_RESULT");
transferData->result.result = resVariant.toJsonValue();
transferData->result.insert("RESULT", transferData->result.result);
resultSet = true;
}
if (vendingData->hasParameter("RESULT_ERROR_CODE")) {
QVariant ecVariant = vendingData->getParameter("RESULT_ERROR_CODE");
transferData->result.errorCode = ecVariant.toJsonValue();
transferData->result.insert("ERRORCODE", transferData->result.errorCode);
resultSet = true;
}
if (vendingData->hasParameter("RESULT_ERROR_MESSAGE")) {
QVariant emsgVariant = vendingData->getParameter("RESULT_ERROR_MESSAGE");
transferData->result.errorMsg = emsgVariant.toJsonValue();
transferData->result.insert("ERRORMSG", transferData->result.errorMsg);
resultSet = true;
}
if (resultSet) {
transferData->insert("RESULT", transferData->result);
}
////////////////////////////////////////////////////////////////////////
QJsonDocument jsonDoc(*transferData);
QByteArray data = "#M=APISM#C=CMD_TRANSACTION#J=";
data += jsonDoc.toJson(QJsonDocument::Compact);
this->apismTcpSendClient->sendData(data);
}
void ApismClient::sendAccount(const QHash<QString, QVariant> & accountDataHash)
{
QScopedPointer<ISMAS::AccountData> accountData(new ISMAS::AccountData());
accountData->coinBox.UID = QUuid::createUuid().toString(QUuid::WithoutBraces); // .mid(0, 8)
accountData->insert("UID", accountData->coinBox.UID);
accountData->coinBox.ChangeNumber = QJsonValue(static_cast<qint32>(this->persistentData->getNewCoinboxChangeNumber()));
accountData->insert("COINBOX_CHANGE_NUMBER", accountData->coinBox.ChangeNumber);
accountData->coinBox.Process = "COINBOX_CHANGE";
accountData->insert("PROCESS", accountData->coinBox.Process);
accountData->insert("StartTime", utils::getISODateTimeWithMsAndOffset(persistentData->getAccountStartTime()));
accountData->insert("EndTime", utils::getCurrentISODateTimeWithMsAndOffset());
accountData->insert("StartHash", persistentData->getFirstTransactionUID());
accountData->insert("EndHash", persistentData->getLastTransactionUID());
// coins
int numberOfCoinVariants = accountDataHash["NumberOfCoinVariants"].toInt();
for (int i=0; i < numberOfCoinVariants;++i) {
accountData->coinBox.coin.value = accountDataHash["COIN_" + QString::number(i) + "_Value"].toInt();
accountData->coinBox.coin.numberOfCoins = accountDataHash["COIN_" + QString::number(i) + "_Quantity"].toInt();
accountData->coinBox.coin.insert("VALUE", accountData->coinBox.coin.value);
accountData->coinBox.coin.insert("QUANTITY", accountData->coinBox.coin.numberOfCoins);
if (accountDataHash.contains("COIN_" + QString::number(i) + "_Currency")) {
accountData->coinBox.coin.currency = accountDataHash["COIN_" + QString::number(i) + "_Currency"].toString();
accountData->coinBox.coin.insert("CURRENCY", accountData->coinBox.coin.numberOfCoins);
}
accountData->insert("COIN_" + QString::number(i), accountData->coinBox.coin);
}
QJsonDocument jsonDoc(*accountData);
QByteArray data = "#M=APISM#C=CMD_CashboxChange#J=";
data += jsonDoc.toJson(QJsonDocument::Compact);
this->apismTcpSendClient->sendData(data);
this->persistentData->clearForNewAccount();
persistentData->serializeToFile();
}
void ApismClient::sendEvent(const ATBMachineEvent* machineEvent)
{
QScopedPointer<ISMAS::EventData> eventData(new ISMAS::EventData());
eventData->machineEvent.eventID = machineEvent->eventId;
eventData->insert("EVENT_ID", eventData->machineEvent.eventID);
eventData->machineEvent.deviceName = machineEvent->deviceName;
eventData->insert("DeviceName", eventData->machineEvent.deviceName);
eventData->machineEvent.reason = ATBMachineEvent::getEventClassString(machineEvent->machineEventClass);
eventData->insert("Reason", eventData->machineEvent.reason);
eventData->machineEvent.event = machineEvent->eventName;
eventData->insert("Event", eventData->machineEvent.event);
eventData->machineEvent.eventState = machineEvent->eventState;
eventData->insert("EventState", eventData->machineEvent.eventState);
eventData->machineEvent.timeStamp = machineEvent->timestamString;
eventData->insert("Timestamp", eventData->machineEvent.timeStamp);
eventData->machineEvent.parameter = machineEvent->parameterString;
eventData->insert("Parameter", eventData->machineEvent.parameter);
eventData->machineEvent.secondLevelInfo = machineEvent->secondLevelInfoString;
eventData->insert("SecondLevelInfo", eventData->machineEvent.secondLevelInfo);
QJsonDocument jsonDoc(*eventData);
QByteArray data = "#M=APISM#C=CMD_EVENT#J=";
data += jsonDoc.toJson(QJsonDocument::Compact);
this->apismTcpSendClient->sendData(data);
#endif
}
void ApismClient::requestAvailableIsmasUpdates() {
QByteArray data = "#M=APISM #C=REQ_ISMASParameter #J={}";
this->currentRequest = ISMAS::REQUEST::ISMAS_PARAMETER;
this->apismTcpRequestResponseClient->sendData(data);
}
void ApismClient::sendCmdSendVersionToIsmas(QString const &msg) {
QJsonParseError parseError;
QJsonDocument document(QJsonDocument::fromJson(msg.toUtf8(), &parseError));
if (parseError.error != QJsonParseError::NoError) {
qCritical() << "INVALID JSON MSG: PARSING FAILED:"
<< parseError.error << parseError.errorString();
return;
}
if (!document.isObject()) {
qCritical() << "FILE IS NOT A JSON OBJECT!";
return;
}
QByteArray data = "#M=APISM#C=CMD_SENDVERSION#J=";
data += document.toJson(QJsonDocument::Compact);
printf("data=%s\n", QString(data).toStdString().c_str());
this->currentRequest = ISMAS::REQUEST::NO_REQUEST;
this->apismTcpSendClient->sendData(data);
}
void ApismClient::sendUpdateInfoToIsmas(QString const &msg) {
QJsonParseError parseError;
QJsonDocument document(QJsonDocument::fromJson(msg.toUtf8(), &parseError));
if (parseError.error != QJsonParseError::NoError) {
qCritical() << "INVALID JSON MSG: PARSING FAILED:"
<< parseError.error << parseError.errorString();
return;
}
if (!document.isObject()) {
qCritical() << "FILE IS NOT A JSON OBJECT!";
return;
}
QByteArray data = "#M=APISM#C=CMD_EVENT#J=";
data += document.toJson(QJsonDocument::Compact);
printf("data=%s\n", QString(data).toStdString().c_str());
this->currentRequest = ISMAS::REQUEST::NO_REQUEST;
this->apismTcpSendClient->sendData(data);
}
void ApismClient::sendState(const QString & state, const QString & msg)
{
qCritical() << "ApismClient::sendState(): ";
qCritical() << " state: " << state;
qCritical() << " msg: " << msg;
QJsonParseError parseError;
QJsonDocument document(QJsonDocument::fromJson(msg.toUtf8(), &parseError));
if (parseError.error != QJsonParseError::NoError) {
qCritical() << "ApismClient::sendState() invalid json msg: Parsing failed:" << parseError.error << parseError.errorString();
return;
}
if (!document.isObject()) {
qCritical() << "File is not JSON object!";
return;
}
QScopedPointer<ISMAS::StateData> stateData(new ISMAS::StateData());
QJsonObject stateObject = document.object();
QJsonArray statesArray;
statesArray.append(stateObject);
// stateData->insert("TIMESTAMP", utils::getCurrentISODateTimeWithMsAndOffset());
// stateData->insert("HW_States", statesArray);
QJsonDocument jsonDoc(*stateData);
QByteArray data = "#M=APISM#C=CMD_HW_STATUS#J=";
data += jsonDoc.toJson(QJsonDocument::Compact);
this->apismTcpSendClient->sendData(data);
}
#if 0
void ApismClient::sendMininformStartRequest(const VendingData* vendingData)
{
this->currentRequest = ISMAS::REQUEST::START;
struct MininFormTransferData : public QJsonObject {
struct : public QJsonObject {
QJsonValue uid; // MUST: uuid -> vendorId
QJsonValue posid; // terminal-id
QJsonValue authCode; // approval-code
QJsonValue stan; // MUST
QJsonValue lpn;
QJsonValue transactionTime; // MUST: Zeitstempel Verkauf
QJsonValue parkingStartTime; // MUST: Startzeit
QJsonValue preAuthAmount;
QJsonValue hourlyRate;
QJsonValue vehicleCategory;
QJsonValue langCode;
QJsonValue zoneCode;
} startAction;
};
QScopedPointer<MininFormTransferData> transferData(new MininFormTransferData());
transferData->startAction.uid = vendingData->getParameter("START_UID").toJsonValue();
this->currentRequestUid = vendingData->getParameter("START_UID").toString();
transferData->startAction.insert("UID", transferData->startAction.uid);
transferData->startAction.insert("POSID", vendingData->getParameter("START_POSID").toString());
transferData->startAction.insert("AUTHCODE", vendingData->getParameter("START_AuthCode").toString());
transferData->startAction.insert("STAN", vendingData->getParameter("START_STAN").toString());
transferData->startAction.insert("LPN", vendingData->getParameter("LICENSEPLATE").toString());
transferData->startAction.insert("TRANSACTIONTIME", utils::getISODateTimeWithMsAndOffset(vendingData->getParameter("START_TRANSACTIONTIME").toString()));
transferData->startAction.insert("STARTTIME", utils::getISODateTimeWithMsAndOffset(vendingData->getParameter("START_STARTTIME").toString()));
transferData->startAction.insert("AMOUNT", static_cast<int>(vendingData->getUintParameter("PRICE_INFO_GROSS")));
transferData->startAction.insert("RATE_H", static_cast<int>(vendingData->getUintParameter("PRICE_INFO_RATE_H")));
transferData->startAction.insert("VEHICLETYPE", "01"); // Fixed value
transferData->startAction.insert("LANGUAGE", "HUN"); // Fixed value
transferData->startAction.insert("ZONE", vendingData->getParameter("MININFORM_ZONE").toString());
transferData->insert("STARTACTION", transferData->startAction);
QJsonDocument jsonDoc(*transferData);
QByteArray data = "#M=APISM#C=REQ_START#J="; // REQ_... -> use port 7778
data += jsonDoc.toJson(QJsonDocument::Compact);
this->apismTcpRequestResponseClient->sendData(data);
}
void ApismClient::sendMininformStopRequest(const VendingData* vendingData)
{
this->currentRequest = ISMAS::REQUEST::STOP;
struct MininFormTransferData : public QJsonObject {
struct : public QJsonObject {
QJsonObject uid; // MUST: uuid
QJsonObject posid; // terminal-id
QJsonObject stan; // MUST
QJsonObject lpn; // MUST
QJsonObject stopTime; // MUST: Stop-Zeit
QJsonObject langCode;
QJsonObject deviceId;
} stopAction;
};
QScopedPointer<MininFormTransferData> transferData(new MininFormTransferData());
this->currentRequestUid = QUuid::createUuid().toString(QUuid::WithoutBraces).mid(0, 8);
transferData->stopAction.insert("UID", this->currentRequestUid);
transferData->stopAction.insert("POSID", vendingData->getParameter("STOP_POSID").toString());
transferData->stopAction.insert("STAN", vendingData->getParameter("STOP_STAN").toString());
transferData->stopAction.insert("LPN", vendingData->getParameter("LICENSEPLATE").toString());
transferData->stopAction.insert("STOPTIME", utils::getISODateTimeWithMsAndOffset(vendingData->getParameter("STOP_STOPTIME")));
transferData->stopAction.insert("LANGUAGE", "HUN"); // Fixed value
transferData->stopAction.insert("DEVICE_ID", this->m_config->getMachineNr());
transferData->insert("STOPACTION", transferData->stopAction);
QJsonDocument jsonDoc(*transferData);
QByteArray data = "#M=APISM#C=REQ_STOP#J="; // REQ_ -> use port 7778
data += jsonDoc.toJson(QJsonDocument::Compact);
this->apismTcpRequestResponseClient->sendData(data);
}
#endif
void ApismClient::sendSelfTest() {
qDebug() << "SENDING APISM-SELF-TEST";
this->currentRequest = ISMAS::REQUEST::SELF;
QByteArray data = "#M=APISM#C=REQ_SELF#J={}";
this->apismTcpRequestResponseClient->sendData(data);
}
#if 0
void ApismClient::sendMininformPingRequest()
{
this->currentRequest = ISMAS::REQUEST::PING;
QByteArray data = "#M=APISM#C=REQ_Ping#J={\"281\":\"PING\"}";
this->apismTcpRequestResponseClient->sendData(data);
}
#endif
void ApismClient::onReceivedResponse(QByteArray response) {
if (this->currentRequest == ISMAS::REQUEST::NO_REQUEST &&
response == "RECORD SAVED") { // sent by APISM to indicate that record
return; // has been saved in DB
}
// get the root object
QJsonParseError jsonParseError;
QJsonDocument responseDoc = QJsonDocument::fromJson(response, &jsonParseError);
if (jsonParseError.error != QJsonParseError::NoError) {
qCritical() << "ApismClient::onReceivedResponse() response is no json data:";
qCritical() << " Error: " << jsonParseError.errorString();
this->handleISMASResponseError();
return;
}
QJsonObject rootObject = responseDoc.object();
QStringList rootObjectKeys = rootObject.keys();
// DEBUG
qDebug() << "ApismClient::onReceivedResponse(): objects: " << rootObjectKeys;
// results to:
// ApismClient::onReceivedResponse(): objects: ("REQ_START#60044_Response", "Response")
if(rootObjectKeys.indexOf(QRegularExpression("^REQ_START.*")) >= 0) {
this->private_handleMininformStartResponse(rootObject["Response"].toObject());
}
else
if(rootObjectKeys.indexOf(QRegularExpression("^REQ_STOP.*")) >= 0) {
this->private_handleMininformStopResponse(rootObject["Response"].toObject());
}
else
if(rootObjectKeys.indexOf(QRegularExpression("^REQ_SELF.*")) >= 0) {
this->private_handleReqSelfResponse(rootObject["Response"].toObject());
}
else
if(rootObjectKeys.indexOf(QRegularExpression("^REQ_PING.*")) >= 0) {
this->private_handleReqPingResponse(rootObject["PING"].toObject());
} else
if(rootObjectKeys.indexOf(QRegularExpression("^REQ_ISMASPARAMETER.*")) >= 0) {
this->private_handleIsmasParameterResponse(rootObject);
}
else {
qCritical() << "ApismClient::onReceivedResponse() for unknown Request: ";
qCritical() << " currentRequestName: " << currentRequest;
qCritical() << " rootObject.keys(): " << rootObjectKeys;
this->handleISMASResponseError();
return;
}
this->currentRequest = ISMAS::REQUEST::NO_REQUEST;
}
void ApismClient::handleISMASResponseError()
{
switch (this->currentRequest) {
case ISMAS::REQUEST::NO_REQUEST:
qCritical() << "ApismClient::onReceivedResponse() for unknown Request: " << currentRequest;
break;
case ISMAS::REQUEST::START:
emit this->sendMininformStartResponse(nsApismInterface::RESULT_STATE::ERROR_BACKEND, QJsonObject());
break;
case ISMAS::REQUEST::STOP:
emit this->sendMininformStopResponse(nsApismInterface::RESULT_STATE::ERROR_BACKEND, QJsonObject());
break;
case ISMAS::REQUEST::PING:
emit this->sendMininformPingResponse(nsApismInterface::RESULT_STATE::ERROR_BACKEND, QJsonObject());
break;
case ISMAS::REQUEST::SELF:
emit this->sendReqSelfResponse(nsApismInterface::RESULT_STATE::ERROR_BACKEND, QJsonObject());
break;
case ISMAS::REQUEST::ISMAS_PARAMETER:
// TODO
// emit
break;
}
this->currentRequest = ISMAS::REQUEST::NO_REQUEST;
}
/*
{\r\n \"REQ_START#53365_Response\": {\r\n \"Aknoledge\": \"OK\"\r\n },
\r\n \"Response\": {\r\n \"Result\": \"ERR:Ung<6E>ltiger Datums-String: \"\r\n }\r\n}
*/
/*
: ISMAS received: "{\r\n \"REQ_PING#30844_Response\": {\r\n \"Aknoledge\": \"OK\"\r\n },
"PING": { "IsAviable": "TRUE" }
}"
*/
void ApismClient::onSendClientResponseTimeout()
{
}
void ApismClient::onRequestResponseClientResponseTimeout()
{
switch (this->currentRequest) {
case ISMAS::REQUEST::NO_REQUEST:
qCritical() << "ApismClient::onRequestResponseClientResponseTimeout() for unknown Request: " << currentRequest;
break;
case ISMAS::REQUEST::START:
emit this->sendMininformStartResponse(nsApismInterface::RESULT_STATE::ERROR_TIMEOUT, QJsonObject());
break;
case ISMAS::REQUEST::STOP:
emit this->sendMininformStopResponse(nsApismInterface::RESULT_STATE::ERROR_TIMEOUT, QJsonObject());
break;
case ISMAS::REQUEST::PING:
emit this->sendMininformPingResponse(nsApismInterface::RESULT_STATE::ERROR_TIMEOUT, QJsonObject());
break;
case ISMAS::REQUEST::SELF:
emit this->sendReqSelfResponse(nsApismInterface::RESULT_STATE::ERROR_TIMEOUT, QJsonObject());
break;
case ISMAS::REQUEST::ISMAS_PARAMETER:
break;
}
this->currentRequest = ISMAS::REQUEST::NO_REQUEST;
}
void ApismClient::private_handleMininformStartResponse(QJsonObject response)
{
emit this->sendMininformStartResponse(nsApismInterface::RESULT_STATE::SUCCESS, response);
}
void ApismClient::private_handleMininformStopResponse(QJsonObject response)
{
emit this->sendMininformStopResponse(nsApismInterface::RESULT_STATE::SUCCESS, response);
}
void ApismClient::private_handleReqSelfResponse(QJsonObject response)
{
emit this->sendReqSelfResponse(nsApismInterface::RESULT_STATE::SUCCESS, response);
}
void ApismClient::private_handleReqPingResponse(QJsonObject response)
{
emit this->sendMininformPingResponse(nsApismInterface::RESULT_STATE::SUCCESS, response);
}
void ApismClient::private_handleIsmasParameterResponse(QJsonObject response) {
emit this->ismasResponseAvailable(response);
}
/************************************************************************************************
* operators
*/
QDebug operator<< (QDebug debug, ISMAS::REQUEST request)
{
switch(request) {
case ISMAS::REQUEST::NO_REQUEST:
debug << QString("ISMAS::REQUEST::NO_REQUEST");
break;
case ISMAS::REQUEST::START:
debug << QString("ISMAS::REQUEST::START");
break;
case ISMAS::REQUEST::STOP:
debug << QString("ISMAS::REQUEST::STOP");
break;
case ISMAS::REQUEST::PING:
debug << QString("ISMAS::REQUEST::PING");
break;
case ISMAS::REQUEST::SELF:
debug << QString("ISMAS::REQUEST::SELF");
break;
case ISMAS::REQUEST::ISMAS_PARAMETER:
debug << QString("ISMAS::REQUEST::ISMASPARAMETER");
break;
}
return debug;
}
QString& operator<< (QString& str, ISMAS::REQUEST request)
{
switch(request) {
case ISMAS::REQUEST::NO_REQUEST:
str = QString("ISMAS::REQUEST::NO_REQUEST");
break;
case ISMAS::REQUEST::START:
str = QString("ISMAS::REQUEST::START");
break;
case ISMAS::REQUEST::STOP:
str = QString("ISMAS::REQUEST::STOP");
break;
case ISMAS::REQUEST::PING:
str = QString("ISMAS::REQUEST::PING");
break;
case ISMAS::REQUEST::SELF:
str = QString("ISMAS::REQUEST::SELF");
break;
case ISMAS::REQUEST::ISMAS_PARAMETER:
str = QString("ISMAS::REQUEST::ISMASPARAMETER");
break;
}
return str;
}

View File

@@ -1,122 +0,0 @@
#ifndef APISM_CLIENT_H_INCLUDED
#define APISM_CLIENT_H_INCLUDED
#include "ismas_data.h"
#include "apism_tcp_client.h"
#include <QObject>
#include <QAbstractSocket>
#include <QTcpSocket>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonParseError>
#include <QJsonValue>
QDebug operator<<(QDebug debug, ISMAS::REQUEST request);
QString& operator<<(QString& str, ISMAS::REQUEST request);
namespace nsApismInterface {
enum class RESULT_STATE : quint8 {
SUCCESS = 1,
ERROR_BACKEND = 2, // error from backand (e.g. backend replies with error)
ERROR_NETWORK = 3, // error from network (e.g. host not available)
ERROR_TIMEOUT = 4, // the operation timed out
ERROR_PROCESS = 5, // internal plugin error (e.g. bug in implementation)
ERROR_RETRY = 6, // retry operation
INFO = 7
};
}
class VendingData;
class ATBHMIconfig;
class PersistentData;
class ATBMachineEvent;
class ApismClient : public QObject {
Q_OBJECT
public:
explicit ApismClient(QObject *eventReceiver, ATBHMIconfig *config, PersistentData *persistentData, QObject *parent = 0);
~ApismClient();
quint32 getLastError();
const QString & getLastErrorDescription();
public slots:
void sendSelfTest();
void sendTransaction(const VendingData* vendingData);
// void sendAccount(const QHash<QString, QVariant> &accountDataHash);
//void sendEvent(const ATBMachineEvent* machineEvent);
void sendState(const QString & state, const QString & msg);
void sendUpdateInfoToIsmas(QString const &msg);
void sendCmdSendVersionToIsmas(QString const &msg);
void requestAvailableIsmasUpdates();
//void sendMininformStartRequest(const VendingData* vendingData);
//void sendMininformStopRequest(const VendingData* vendingData);
//void sendMininformPingRequest();
void restartApism();
signals:
// public signals:
void sendTransactionRespones(nsApismInterface::RESULT_STATE result);
void sendAccountResponse(nsApismInterface::RESULT_STATE result);
void sendMininformStartResponse(nsApismInterface::RESULT_STATE result, QJsonObject response);
void sendMininformStopResponse(nsApismInterface::RESULT_STATE result, QJsonObject response);
void sendMininformPingResponse(nsApismInterface::RESULT_STATE result, QJsonObject response);
void sendReqSelfResponse(nsApismInterface::RESULT_STATE result, QJsonObject response);
void ismasResponseAvailable(QJsonObject ismasResponse);
private slots:
// void onSocketError(QAbstractSocket::SocketError socketError);
void onReceivedResponse(QByteArray response);
void onSendClientResponseTimeout();
void onRequestResponseClientResponseTimeout();
private:
QObject *healthEventReceiver;
ATBHMIconfig *m_config;
PersistentData *persistentData;
ApismTcpClient* apismTcpSendClient;
ApismTcpClient* apismTcpRequestResponseClient;
quint32 lastError;
QString lastErrorDescription;
QString currentRequestUid;
ISMAS::REQUEST currentRequest;
void private_handleMininformStartResponse(QJsonObject response);
void private_handleMininformStopResponse(QJsonObject response);
void private_handlePingResponse(QJsonObject response);
void private_handleReqSelfResponse(QJsonObject response);
void private_handleReqPingResponse(QJsonObject response);
void private_handleIsmasParameterResponse(QJsonObject response);
void handleISMASResponseError();
};
// Q_DECLARE_METATYPE(QAbstractSocket::SocketError)
#endif // APISM_CLIENT_H_INCLUDED

View File

@@ -1,172 +0,0 @@
#include "apism_tcp_client.h"
#include <QHostAddress>
#include <QTimer>
#include <QCoreApplication>
ApismTcpClient::ApismTcpClient(const QString & hostname,
const QString & port,
QObject *parent)
: QObject(parent)
, hostname(hostname)
, port(port)
, responseTimerTimeoutCounter(0)
{
this->responseTimeoutTimer = new QTimer(this);
this->responseTimeoutTimer->setInterval(10000);
this->responseTimeoutTimer->setSingleShot(true);
connect(this->responseTimeoutTimer, SIGNAL(timeout()), this, SLOT(onResponseTimeoutTimerTimeout()));
socket = new QTcpSocket(this);
connect(socket, SIGNAL(connected()), this, SLOT(onSocketConnected()));
connect(socket, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected()));
connect(socket, SIGNAL(readyRead()), this, SLOT(onSocketReadyRead()));
connect(socket, SIGNAL(bytesWritten(qint64)), this, SLOT(onSocketBytesWritten(qint64)));
}
void ApismTcpClient::connectToHost() {
qCritical() << "ApismTcpClient::connectToHost(this->" << hostname << ", " << port << ")";
int portNumber = this->port.toInt();
this->socket->connectToHost(QHostAddress(this->hostname), portNumber);
if (!socket->waitForConnected(10000)) {
qCritical() << "ERROR IN WAIT FOR CONNECTED" << socket->errorString();
} else {
qDebug() << "connected to" << hostname << ", " << port << ")";
}
}
void ApismTcpClient::connectToHost(const QString & hostname, const QString & port)
{
qCritical() << "ApismTcpClient::connectToHost(" << hostname << ", " << port << ")";
int portNumber = port.toInt();
socket->connectToHost(hostname, portNumber);
}
void ApismTcpClient::closeConnection()
{
socket->close();
}
bool ApismTcpClient::isConnected()
{
bool result = false;
QAbstractSocket::SocketState socketState = socket->state();
switch (socketState) {
case QAbstractSocket::UnconnectedState:
/* FALLTHRU */
case QAbstractSocket::HostLookupState:
/* FALLTHRU */
case QAbstractSocket::ConnectingState:
result = false;
break;
case QAbstractSocket::ConnectedState:
/* FALLTHRU */
case QAbstractSocket::BoundState:
result = true;
break;
case QAbstractSocket::ClosingState:
/* FALLTHRU */
case QAbstractSocket::ListeningState:
result = false;
break;
}
return result;
}
void ApismTcpClient::sendData(const QByteArray & message) {
qDebug() << "ApismTcpClient::send: " << message;
this->sendQueue.enqueue(message);
if (this->isConnected()) {
qCritical() << "ApismTcpClient::send: connected, send" << message;
this->private_sendData();
} else {
qCritical() << "ApismTcpClient::send: not connected, connect";
this->connectToHost();
}
}
/**
* @brief ApismTcpClient::private_sendData
*
* Precondition is that queue is not empty.
*/
void ApismTcpClient::private_sendData()
{
// take message from queue
QByteArray ba = this->sendQueue.dequeue();
qDebug() << "ApismTcpClient::send: " << QString(ba);
socket->write(ba);
socket->flush();
// start timeoutTimer
this->responseTimeoutTimer->start();
}
void ApismTcpClient::onSocketConnected()
{
qInfo() << "ApismTcpClient: Connected!";
if (this->sendQueue.size() > 0) {
this->private_sendData();
}
}
void ApismTcpClient::onSocketDisconnected()
{
qDebug() << "ApismTcpClient: Disconnected!";
qDebug() << " -> SocketErrorString: " << socket->errorString();
if (this->sendQueue.size() > 0) {
this->connectToHost();
}
}
void ApismTcpClient::onSocketBytesWritten(qint64 bytes)
{
Q_UNUSED(bytes)
}
void ApismTcpClient::onSocketReadyRead()
{
QByteArray readData;
// stop timeoutTimer
this->responseTimeoutTimer->stop();
readData = socket->readAll();
qDebug() << "ISMAS received: " << QString(readData);
emit this->receivedData(readData);
//QCoreApplication::processEvents();
this->socket->close();
}
void ApismTcpClient::onResponseTimeoutTimerTimeout()
{
if (this->sendQueue.size() == 0) {
return;
}
emit this->responseTimeout();
qCritical() << "ApismTcpClient::onResponseTimeoutTimerTimeout() --> skip this message, send next command, if available.";
// Try next command
this->sendQueue.removeFirst();
if (this->sendQueue.size() > 0) this->private_sendData();
}

View File

@@ -1,62 +0,0 @@
#ifndef APISMTCPCLIENT_H
#define APISMTCPCLIENT_H
#include <QObject>
#include <QTcpSocket>
#include <QAbstractSocket>
#include <QByteArray>
#include <QQueue>
class QTimer;
class ApismTcpClient : public QObject
{
Q_OBJECT
public:
explicit ApismTcpClient(const QString & hostname, const QString & port, QObject *parent = nullptr);
bool isConnected();
void connectToHost();
void connectToHost(const QString & hostname, const QString & port);
// socket is implicitely closed by APISM
void closeConnection();
void sendData(const QByteArray & message);
public slots:
// socket interface
void onSocketConnected();
void onSocketDisconnected();
void onSocketBytesWritten(qint64 bytes);
void onSocketReadyRead();
signals:
void receivedData(QByteArray response);
void responseTimeout();
private:
QTcpSocket *socket;
QQueue<QByteArray> sendQueue;
QString hostname;
QString port;
QTimer *responseTimeoutTimer;
quint8 responseTimerTimeoutCounter;
void private_sendData();
public slots:
void onResponseTimeoutTimerTimeout();
};
#endif // APISMTCPCLIENT_H

View File

@@ -1,118 +1,107 @@
#include "git_client.h"
#include "update.h"
#include "worker.h"
#include "utils.h"
#include <QRegularExpression>
#include <QDebug>
#include <QDir>
GitClient::GitClient(QString const &repositoryPath,
QString const &customerId,
GitClient::GitClient(QString const &customerNrStr,
QString const &customerRepository,
QString const &workingDirectory,
QString const &branchName,
QObject *parent)
: QObject(parent)
, m_worker(qobject_cast<Worker *>(parent))
, m_repositoryPath(repositoryPath)
, m_customerId(customerId)
, m_repositoryPath(QString("https://git.mimbach49.de/GerhardHoffmann/%1.git").arg(customerNrStr))
, m_customerNr(customerNrStr)
, m_workingDirectory(workingDirectory)
, m_branchName(branchName)
, m_customerRepository(QDir::cleanPath(m_workingDirectory
+ QDir::separator()
+ m_customerId)) {
, m_customerRepository(customerRepository) {
if (!m_worker) {
qCritical() << "ERROR CASTING PARENT TO WORKER FAILED";
}
connect(this, SIGNAL(ismasUpdatesAvailable()),
this, SLOT(onIsmasUpdatesAvailable()), Qt::QueuedConnection);
}
void GitClient::onIsmasUpdatesAvailable() {
if (QDir(m_customerRepository).exists()) {
qInfo() << "FETCHING OF" << m_repositoryPath
<< "INTO" << m_customerRepository;
std::optional<QString> changes = gitFetch();
if (changes) {
std::optional<QStringList> changedFileNames = gitDiff(changes.value());
if (changedFileNames) {
for (int i=0;i<changedFileNames.value().size();++i) {
QString fname = changedFileNames.value().at(i);
QString lastCommit = gitLastCommit(fname);
qDebug() << "CCCC" << changedFileNames.value().at(i) << lastCommit;
}
if (gitPull()) {
emit m_worker->handleChangedFiles(changedFileNames.value());
} else {
qCritical() << "PULL FAILED FOR" << m_repositoryPath
<< "IN " << m_customerRepository;
emit m_worker->terminateUpdateProcess();
}
} else {
qCritical() << "NO CHANGES IN" << m_repositoryPath
<< "(" << m_customerRepository << ")";
emit m_worker->finishUpdateProcess(false);
}
} else {
qCritical() << "NO CHANGES IN" << m_repositoryPath
<< "(" << m_customerRepository << ")";
emit m_worker->finishUpdateProcess(false);
}
} else {
if (gitCloneAndCheckoutBranch()) {
qInfo() << "CLONED" << m_repositoryPath
<< "AND CHECKED OUT INTO" << m_customerRepository;
if (m_worker) {
qDebug() << "WORKER EXECUTE OPKG COMMANDS";
QStringList opkgCommands;
// To make sure that opkg upgrade does not break your system
// because of an unstable connection
// Add a line "option cache cachedir" to /etc/opkg/opkg.conf to
// avoid the --cache option on command line.
opkgCommands << "opkg update";
//opkgCommands << "opkg --cache cachedir --download-only upgrade";
//opkgCommands << "opkg --cache cachedir upgrade";
emit m_worker->executeOpkgCommands(opkgCommands);
}
} else {
qCritical() << "ERROR CLONING " << m_repositoryPath
<< "AND/OR CHECKING OUT INTO" << m_customerRepository;
emit m_worker->terminateUpdateProcess();
}
}
}
bool GitClient::gitCloneCustomerRepository() {
QString gitCommand("git clone ");
/* Blobless clone
==============
When using the --filter=blob:none option, the initial git clone will
download all reachable commits and trees, and only download the blobs
for commits when you do a git checkout. This includes the first checkout
inside the git clone operation.
The important thing to notice is that we have a copy of every blob at
HEAD but the blobs in the history are not present. If your repository
has a deep history full of large blobs, then this option can
significantly reduce your git clone times. The commit and tree data is
still present, so any subsequent git checkout only needs to download
the missing blobs. The Git client knows how to batch these requests to
ask the server only for the missing blobs.
Further, when running git fetch in a blobless clone, the server only
sends the new commits and trees. The new blobs are downloaded only
after a git checkout. Note that git pull runs git fetch and then git
merge, so it will download the necessary blobs during the git merge
command.
When using a blobless clone, you will trigger a blob download whenever
you need the contents of a file, but you will not need one if you only
need the OID (object-id) of a file. This means that git log can detect
which commits changed a given path without needing to download extra
data.
This means that blobless clones can perform commands like git
merge-base, git log, or even git log -- <path> with the same performance
as a full clone.
Commands like git diff or git blame <path> require the contents of the
paths to compute diffs, so these will trigger blob downloads the first
time they are run. However, the good news is that after that you will
have those blobs in your repository and do not need to download them a
second time. Most developers only need to run git blame on a small
number of files, so this tradeoff of a slightly slower git blame command
is worth the faster clone and fetch times.
Note: git v2.18 does not support treeless clones: --filter=tree:0.
*/
QString gitCommand("git clone --filter=blob:none ");
gitCommand += m_repositoryPath;
Command c(gitCommand);
qInfo() << "IN CURRENT WD" << m_workingDirectory
<< "CLONE" << m_repositoryPath << "...";
<< "CLONE VIA COMMAND" << gitCommand;
if (c.execute(m_workingDirectory)) { // execute the command in wd
QString result = c.getCommandResult();
QString const result = c.getCommandResult();
if (!result.isEmpty()) {
// Cloning into 'customer_281'...\n
static QRegularExpression re("(^\\s*Cloning\\s+into\\s+[']\\s*)(.*)(\\s*['].*$)");
QRegularExpressionMatch match = re.match(result);
if (match.hasMatch()) {
if (re.captureCount() == 3) { // start with full match (0), then the other 3 matches
if (match.captured(2).trimmed() == m_customerId) {
if (match.captured(2).trimmed() == m_customerNr) {
qInfo() << "CLONING" << m_repositoryPath << "OK";
return true;
}
}
}
qCritical() << "ERROR CLONE RESULT HAS WRONG FORMAT";
Utils::printCriticalErrorMsg(
QString("ERROR CLONE RESULT HAS WRONG FORMAT. rcc=%1 CLONE_RESULT=%2")
.arg(re.captureCount())
.arg(result));
return false;
}
Utils::printCriticalErrorMsg("ERROR CLONE RESULT IS EMPTY");
}
return false;
}
bool GitClient::copyGitConfigFromMaster() { // only allowed when called in
// master branch
// master branch (???)
if (QDir(m_customerRepository).exists()) {
QString const cp = QString("cp .gitconfig .git/config");
Command c("bash");
@@ -125,27 +114,100 @@ bool GitClient::copyGitConfigFromMaster() { // only allowed when called in
return false;
}
bool GitClient::gitCheckoutBranch() {
QStringList GitClient::gitBranchNames() {
// git config --global pager.branch false
QStringList bNames;
if (QDir(m_customerRepository).exists()) {
QString gitCommand("git branch -a");
Command c(gitCommand);
if (c.execute(m_customerRepository)) {
QString const result = c.getCommandResult();
return result.split('\n');
}
}
return bNames;
}
bool GitClient::gitCheckoutBranch() {
// TODO: nachsehen, ob der Branch ueberhaupt existiert
if (QDir(m_customerRepository).exists()) {
int zoneNr = Utils::read1stLineOfFile("/mnt/system_data/zone_nr");
m_branchName = (zoneNr != 0)
? QString("zg1/zone%1").arg(zoneNr) : "master";
QString gitCommand("git checkout ");
gitCommand += m_branchName;
Command c(gitCommand);
return c.execute(m_customerRepository); // execute command in customerRepo
}
qCritical() << "ERROR" << m_customerRepository << "DOES NOT EXIST";
Utils::printCriticalErrorMsg(QString("ERROR ") + m_customerRepository + " DOES NOT EXIST");
return false;
}
bool GitClient::gitCloneAndCheckoutBranch() {
qInfo() << "CLONE" << m_repositoryPath << "AND CHECKOUT" << m_branchName;
if (gitCloneCustomerRepository()) {
if (copyGitConfigFromMaster()) {
return gitCheckoutBranch();
//if (copyGitConfigFromMaster()) {
if (gitCheckoutBranch()) {
return true;
} else {
// TODO
}
//}
}
Utils::printCriticalErrorMsg(QString("CLONE ") + m_repositoryPath + " AND CHECKOUT FAILED");
return false;
}
QStringList GitClient::gitShowReason(QString branchName) {
QStringList lst;
if (QDir(m_customerRepository).exists()) {
// %h: commit (short form)
// %s: commit message
// %cI: commit date, strict ISO 8601 format
// Note: branch with branchName has to exist: format zg1/zone1
Command c(QString("git show origin/%1 -s --format=\"c=%h m=%s d=%cI\"").arg(branchName));
if (c.execute(m_customerRepository)) {
QString const s = c.getCommandResult().trimmed();
int const c = s.indexOf("c=");
int const m = s.indexOf("m=");
int const d = s.indexOf("d=");
QString msg = IsmasClient::getReasonForLastSendVersion();
QString commit{""}, date{""};
if (c != -1) {
int start = c + 2;
if (m >= start) {
int length = m - start;
commit = s.mid(start, length).trimmed();
start = m + 2;
if (d >= start) {
length = d - start;
msg += " (";
msg = s.mid(start, length).trimmed();
msg += ")";
start = d + 2;
date = s.mid(start);
}
}
if (!commit.isEmpty() && !msg.isEmpty() && !date.isEmpty()) {
lst << commit << msg << date;
}
}
}
} else {
qCritical() << "CUSTOMER_REPOSITORY" << m_customerRepository
<< "DOES NOT EXIST";
}
return lst;
}
/*
Zu beachten: wird eine datei neu hinzugefuegt (git add/commit) dann aber gleich
wieder geloscht, so wird sie im diff nicht angezeigt.
@@ -159,24 +221,37 @@ std::optional<QStringList> GitClient::gitDiff(QString const &commits) {
Command c(gitCommand);
if (c.execute(m_customerRepository)) { // execute command in local customerRepo
QString s = c.getCommandResult().trimmed();
Utils::printInfoMsg("GIT DIFF RESULT " + s);
QStringList lines = Update::split(s, '\n');
QStringList fileNames;
// each line has the format "etc/psa_config/DC2C_print01.json | 1 +
// or the format "etc/psa_config/DC2C_print01.json (new) | 1 +
// the filenames are relativ to the repository
for (int i = 0; i < lines.size(); ++i) {
// TODO: koennte auch (delete) kommen ?
int newIndex = lines.at(i).indexOf("(new)"); // for new files
// int goneIndex = lines.at(i).indexOf("(gone)"); // for removed files
QString const &line = lines.at(i);
int newIndex = line.indexOf("(new"); // for new files
int goneIndex = line.indexOf("(gone"); // for removed files
int modeIndex = line.indexOf("(mode");
int pipeIndex = line.indexOf('|');
if (newIndex != -1) {
QString fileName = lines.at(i).mid(0, newIndex).trimmed();
fileNames << fileName;
} else {
int pipeIndex = lines.at(i).indexOf('|');
QString file = line.left(newIndex).trimmed();
qInfo() << "FILE (NEW)" << file;
fileNames << file;
} else
if (modeIndex != -1) {
QString const file = line.left(modeIndex).trimmed();
qInfo() << "FILE (MODE)" << file;
fileNames << file;
} else
if (goneIndex != -1) {
QString const file = line.left(goneIndex).trimmed();
qCritical() << "FILE (GONE)" << file;
} else
if (pipeIndex != -1) {
QString fileName = lines.at(i).mid(0, pipeIndex).trimmed();
fileNames << fileName;
}
QString const file = line.left(pipeIndex).trimmed();
qInfo() << "FILE (PIPE)" << file;
fileNames << file;
}
}
if (!fileNames.isEmpty()) {
@@ -190,62 +265,68 @@ std::optional<QStringList> GitClient::gitDiff(QString const &commits) {
/*
Hat sich nichts geaendert, so werden auch keine Commits <>..<> angezeigt
*/
std::optional<QString> GitClient::gitFetch() {
QString const &customerRepository
= QDir::cleanPath(m_workingDirectory + QDir::separator() + m_customerId);
if (QDir(customerRepository).exists()) {
Command c("git fetch");
if (c.execute(customerRepository)) {
std::optional<QString> GitClient::gitPull() {
if (QDir(m_customerRepository).exists()) {
qInfo() << "BRANCH NAME" << m_branchName;
Command c("git pull");
if (c.execute(m_customerRepository)) {
QString const s = c.getCommandResult().trimmed();
if (!s.isEmpty()) {
QStringList lines = Update::split(s, '\n');
if (!lines.empty()) {
int zoneNr = Utils::read1stLineOfFile("/mnt/system_data/zone_nr");
m_branchName = (zoneNr != 0) ? QString("zg1/zone%1").arg(zoneNr) : "master";
// lines can look like this:
// From https://git.mimbach49.de/GerhardHoffmann/customer_281
// 41ec581..5d25ac3 master -> origin/master
// ff10f57..43530a1 zg1/zone1 -> origin/zg1/zone1
// 6ed893f..5d9882c zg1/zone2 -> origin/zg1/zone2
// 4384d17..77045d8 zg1/zone3 -> origin/zg1/zone3
// 89d2812..36a0d74 zg1/zone5 -> origin/zg1/zone5
bool found = false;
for (int i=0; i < lines.size(); ++i) {
if (lines.at(i).contains(m_branchName)) {
found = true;
// 409f198..6c22726 zg1/zone1 -> origin/zg1/zone1
static QRegularExpression re("(^\\s*)([0-9A-Fa-f]+..[0-9A-Fa-f]+)(.*$)");
QRegularExpressionMatch match = re.match(lines.last());
QRegularExpressionMatch match = re.match(lines.at(i));
if (match.hasMatch()) {
if (re.captureCount() == 3) { // start with full match (0), then the other 3 matches
return match.captured(2);
} else {
qCritical() << "ERROR WRONG CAPTURE COUNT FOR 'GIT FETCH'" << re.captureCount();
emit m_worker->showErrorMessage("git pull",
QString("(wrong cap-count (%1)").arg(re.captureCount()));
}
} else {
qCritical() << "ERROR NO MATCH OF COMMITS FOR 'GIT FETCH'";
emit m_worker->showErrorMessage("git pull",
"no regex-match for commits");
Utils::printCriticalErrorMsg("NO REGEX MATCH FOR COMMITS");
}
}
}
if (!found) {
emit m_worker->showErrorMessage("git pull",
QString("unkown branch name ") + m_branchName);
Utils::printCriticalErrorMsg("UNKNOWN BRANCH NAME " + m_branchName);
}
} else {
qCritical() << "ERROR WRONG FORMAT FOR RESULT FOR 'GIT FETCH'" << s;
emit m_worker->showErrorMessage("git pull",
QString("wrong format for result of 'git pull' ") + s);
Utils::printCriticalErrorMsg(QString("WRONG FORMAT FOR RESULT OF 'GIT FETCH' ") + s);
}
} else {
qCritical() << "ERROR EMPTY RESULT FROM 'GIT FETCH'";
// emit m_worker->showErrorMessage("git fetch", "empty result for 'git fetch'");
Utils::printInfoMsg("EMPTY RESULT FOR 'GIT PULL'");
}
}
} else {
qCritical() << "ERROR" << customerRepository << "DOES NOT EXIST";
emit m_worker->showErrorMessage("git pull", QString("repository ") + m_customerRepository + " does not exist");
Utils::printCriticalErrorMsg(QString("REPOSITORY ") + m_customerRepository + " DOES NOT EXIST");
}
return std::nullopt;
}
bool GitClient::gitFetchAndDiff() {
if (gitFetch()) {
QString gitCommand("git diff --compact-summary HEAD..FETCH_HEAD");
Command c(gitCommand);
return c.execute(m_workingDirectory);
}
return false;
}
bool GitClient::gitPull() {
if (QDir(m_customerRepository).exists()) {
Command c("git pull");
if (c.execute(m_customerRepository)) {
qInfo() << "PULLED INTO" << m_customerRepository;
return true;
}
qCritical() << "PULL INTO" << m_customerRepository << "FAILED";
}
return false;
}
std::optional<QStringList> GitClient::gitMerge() {
Command c("git merge");
if (c.execute(m_workingDirectory)) {
@@ -260,6 +341,7 @@ QString GitClient::gitLastCommit(QString fileName) {
if (QDir(m_customerRepository).exists()) {
QString const filePath
= QDir::cleanPath(m_customerRepository + QDir::separator() + fileName);
if (QFile(filePath).exists()) {
QString const gitCommand = QString("git log %1 | head -n 1").arg(fileName);
Command c("bash");
if (c.execute(m_customerRepository, QStringList() << "-c" << gitCommand)) {
@@ -270,22 +352,21 @@ QString GitClient::gitLastCommit(QString fileName) {
}
}
}
}
return "";
}
// get the blob of the file(name) passed as $1
// note: this can be used for any file in the filesystem
// fileName has to an absolute path
QString GitClient::gitBlob(QString fileName) {
if (QDir(m_customerRepository).exists()) {
QString const filePath
= QDir::cleanPath(m_customerRepository + QDir::separator() + fileName);
QFileInfo fi(fileName);
if (fi.exists()) {
QString const gitCommand = QString("git hash-object %1").arg(fileName);
Command c(gitCommand);
if (c.execute(m_customerRepository)) {
return c.getCommandResult();
if (c.execute("/tmp")) {
return c.getCommandResult().trimmed();
}
}
return "";
return "N/A";
}
QString GitClient::gitCommitForBlob(QString blob) {

View File

@@ -2,9 +2,11 @@
#define GIT_CLIENT_H_INCLUDED
#include <QObject>
#include <QStringList>
#include <optional>
#include "process/command.h"
#include "ismas/ismas_client.h"
class Worker;
class GitClient : public QObject {
@@ -12,22 +14,23 @@ class GitClient : public QObject {
Worker *m_worker;
QString const m_repositoryPath;
QString const m_customerId;
QString const m_customerNr;
QString const m_workingDirectory;
QString const m_branchName;
QString m_branchName;
QString const m_customerRepository;
bool copyGitConfigFromMaster();
public:
explicit GitClient(QString const &repositoryPath,
QString const &customerId,
explicit GitClient(QString const &customerNrStr,
QString const &repositoryPath,
QString const &workingDirectory = QCoreApplication::applicationDirPath(),
QString const &branchName = "master",
QObject *parent = 0);
bool gitCloneCustomerRepository();
bool gitCheckoutBranch();
QStringList gitBranchNames();
QString const workingDirectory() const { return m_workingDirectory; }
QString workingDirectory() { return m_workingDirectory; }
@@ -40,23 +43,15 @@ class GitClient : public QObject {
bool gitCloneAndCheckoutBranch();
std::optional<QString> gitFetch();
bool gitFetchAndDiff();
bool gitPull();
std::optional<QString> gitPull();
std::optional<QStringList> gitDiff(QString const &commit);
std::optional<QStringList> gitMerge();
QString gitLastCommit(QString fileName);
QString gitBlob(QString fileName);
QStringList gitShowReason(QString branchName);
static QString gitBlob(QString fileName);
QString gitCommitForBlob(QString blob);
bool gitIsFileTracked(QString file2name);
signals:
void ismasUpdatesAvailable();
public slots:
void onIsmasUpdatesAvailable();
};
#endif // GIT_CLIENT_H_INCLUDED

View File

@@ -1,9 +1,37 @@
#include "ismas/ismas_client.h"
#include "utils.h"
#include <cstring>
#include <cstdio>
#include <errno.h>
#include <arpa/inet.h> // inet_addr()
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <string.h>
#include <strings.h> // bzero()
#include <sys/socket.h>
#include <unistd.h> // read(), write(), close()
#include <fcntl.h>
#include <QThread>
#include <QJsonDocument>
#include <QJsonObject>
#if 0
########################
# Spec vom 27.10.2023:
# U0010 -> %-Werte
# U0001 -> 100%
# U0003 -> "FAIL"
# U0002 -> "" (OK -> WAIT state reset)
# ISMAS -> "WAIT"
########################
#
# $1: EVENT: U0001 update finished: 100%
# U0002 reset TRG
# U0003 error
@@ -24,6 +52,291 @@
#include <QDateTime>
#include <QDebug>
void IsmasClient::printDebugMessage(int port,
QString const &clientIP,
int clientPort,
QString const &message) {
qDebug().noquote()
<< "\n"
<< "SEND-REQUEST-RECEIVE-RESPONSE ..." << "\n"
<< "hostname ........" << "127.0.0.1" << "\n"
<< "port ............" << port << "\n"
<< "local address ..." << clientIP << "\n"
<< "local port ......" << clientPort << "\n"
<< message;
}
void IsmasClient::printInfoMessage(int port,
QString const &clientIP,
int clientPort,
QString const &message) {
qInfo().noquote()
<< "\n"
<< "SEND-REQUEST-RECEIVE-RESPONSE ..." << "\n"
<< "hostname ........" << "127.0.0.1" << "\n"
<< "port ............" << port << "\n"
<< "local address ..." << clientIP << "\n"
<< "local port ......" << clientPort << "\n"
<< message;
}
void IsmasClient::printErrorMessage(int port,
QString const &clientIP,
int clientPort,
QString const &message) {
qCritical().noquote()
<< "\n"
<< "SEND-REQUEST-RECEIVE-RESPONSE ..." << "\n"
<< "hostname ........" << "127.0.0.1" << "\n"
<< "port ............" << port << "\n"
<< "local address ..." << clientIP << "\n"
<< "local port ......" << clientPort << "\n"
<< message;
}
std::optional<QString>
IsmasClient::sendRequestReceiveResponse(int port, QString const &request) {
qInfo() << "REQUEST" << request;
int sockfd;
int r;
errno = 0;
// socket create and verification
if ((sockfd = ::socket(AF_INET, SOCK_STREAM, 0)) == -1) {
qCritical().noquote()
<< "\n"
<< "SEND-REQUEST-RECEIVE-RESPONSE ..." << "\n"
<< "SOCKET CREATION FAILED (" << strerror(errno) << ")";
return std::nullopt;
}
struct sockaddr_in servAddr;
bzero(&servAddr, sizeof(servAddr));
// assign IP, PORT
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
servAddr.sin_port = htons(port);
// connect the client socket to server socket
if ((r = ::connect(sockfd, (struct sockaddr *)(&servAddr), sizeof(servAddr))) != 0) {
qCritical().noquote()
<< "\n"
<< "SEND-REQUEST-RECEIVE-RESPONSE ..." << "\n"
<< "CONNECTION WITH SERVER FAILED (" << strerror(r) << ")";
::close(sockfd);
return std::nullopt;
}
struct sockaddr_in clientAddr;
bzero(&clientAddr, sizeof(clientAddr));
socklen_t sockLen = sizeof(clientAddr);
char clientIP[16];
bzero(&clientIP, sizeof(clientIP));
getsockname(sockfd, (struct sockaddr *)(&clientAddr), &sockLen);
inet_ntop(AF_INET, &clientAddr.sin_addr, clientIP, sizeof(clientIP));
unsigned int clientPort = ntohs(clientAddr.sin_port);
printDebugMessage(port, clientIP, clientPort, QString("CONNECTED TO SERVER"));
struct timeval tv;
tv.tv_sec = 10; /* 10 secs timeout for read and write */
struct linger so_linger;
so_linger.l_onoff = 1;
so_linger.l_linger = 0;
int maxfdp1;
fd_set rset;
fd_set wset;
setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger));
// no reliable, but does not harm, as we use select() as well
setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
int flag = 1;
setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
static char buf[1024*8];
bzero(buf, sizeof(buf));
int const bytesToWrite = strlen(request.toStdString().c_str());
strncpy(buf, request.toStdString().c_str(), sizeof(buf)-1);
int loop = 0;
int bytesWritten = 0;
while (bytesWritten < bytesToWrite) {
errno = 0;
FD_ZERO(&wset);
FD_SET(sockfd, &wset);
maxfdp1 = sockfd + 1;
tv.tv_sec = 60; /* 60 secs timeout for read and write -> APISM cuts the connection after 30s */
tv.tv_usec = 0;
int const w = select(maxfdp1, NULL, &wset, NULL, &tv);
if (w < 0) { // error
if (errno == EINTR) {
printErrorMessage(port, clientIP, clientPort,
QString("INTERRUPTED BY SIGNAL (1) (") + strerror(errno) + ")");
continue;
} else {
printErrorMessage(port, clientIP, clientPort,
QString("SELECT-ERROR (WRITE) %1(").arg(loop) + strerror(errno) + ")");
::close(sockfd);
return std::nullopt;
}
} else
if (w == 0) { // timeout
printErrorMessage(port, clientIP, clientPort,
QString("SELECT-TIMEOUT (WRITE) %1(").arg(loop) + strerror(errno) + ")");
if (++loop < 10) {
QThread::msleep(500);
continue;
}
::close(sockfd);
return std::nullopt;
} else
if (w > 0) {
int n = ::sendto(sockfd, buf+bytesWritten, bytesToWrite-bytesWritten, 0, NULL, 0);
if (n >= 0) {
bytesWritten += n;
} else {
if (errno == EWOULDBLOCK) {
if (++loop < 10) {
QThread::msleep(500);
continue;
}
printErrorMessage(port, clientIP, clientPort,
QString("WRITE TIMEOUT %1(").arg(loop) + strerror(errno) + ")");
::close(sockfd);
return std::nullopt;
} else
if (errno == EINTR) {
printErrorMessage(port, clientIP, clientPort,
QString("WRITE INTERRUPTED BY SIGNAL (1) (") + strerror(errno) + ")");
continue;
}
}
}
}
// DO NOT USE SHUTDOWN! APISM CAN NOT COPE WITH IT
// errno = 0;
// if (shutdown(sockfd, SHUT_WR) < 0) {
// printErrorMessage(port, clientIP, clientPort,
// QString("CANNOT CLOSE WRITING END (") + strerror(errno) + ")");
// }
printInfoMessage(port, clientIP, clientPort, QString("MESSAGE SENT <<<") + buf + ">>>");
loop = 0;
bzero(buf, sizeof(buf));
int bytesToRead = sizeof(buf)-1;
int bytesRead = 0;
while (bytesRead < bytesToRead) {
errno = 0;
FD_ZERO(&rset);
FD_SET(sockfd, &rset);
maxfdp1 = sockfd + 1;
tv.tv_sec = 60; /* 60 secs timeout for read and write */
tv.tv_usec = 0;
QString const selectStart = QDateTime::currentDateTime().toString(Qt::ISODateWithMs);
int const r = select(maxfdp1, &rset, NULL, NULL, &tv);
if (r < 0) { // error
if (errno == EINTR) {
printErrorMessage(port, clientIP, clientPort,
QString("INTERRUPTED BY SIGNAL (2) (") + strerror(errno) + ")");
continue;
} else {
printErrorMessage(port, clientIP, clientPort,
QString("SELECT-ERROR (READ) %1(").arg(loop) + strerror(errno) + ")");
::close(sockfd);
return std::nullopt;
}
} else
if (r == 0) { // timeout
printErrorMessage(port, clientIP, clientPort,
QString("SELECT-TIMEOUT (READ) %1(").arg(loop) + strerror(errno) + ")");
if (++loop < 10) {
QThread::msleep(500);
continue;
}
::close(sockfd);
return std::nullopt;
} else
if (r > 0) {
if (FD_ISSET(sockfd, &rset)) {
int n = ::recvfrom(sockfd, buf+bytesRead, bytesToRead-bytesRead,
0, NULL, NULL);
if (n > 0) { //
bytesRead += n;
} else
if (n == 0) {
// The return value will be 0 when the peer has performed an orderly shutdown.
printErrorMessage(port, clientIP, clientPort,
QString("PEER CLOSED CONNECTION (") + strerror(errno) + ") START AT" +
selectStart + " NOW " + QDateTime::currentDateTime().toString(Qt::ISODateWithMs));
::close(sockfd);
return std::nullopt;
} else
if (n < 0) {
if (errno == EWOULDBLOCK) { // check just in case
if (++loop < 10) {
QThread::msleep(500);
continue;
}
printErrorMessage(port, clientIP, clientPort,
QString("READ TIMEOUT %1(").arg(loop) + strerror(errno) + ")");
::close(sockfd);
return std::nullopt;
}
if (errno == EINTR) {
printErrorMessage(port, clientIP, clientPort,
QString("INTERRUPTED BY SIGNAL (2) (") + strerror(errno) + ")");
continue;
}
}
}
}
printInfoMessage(port, clientIP, clientPort, QString("MESSAGE RECEIVED ") + buf);
QString response(buf);
if (int idx = response.indexOf("{\"error\":\"ISMAS is offline\"}")) {
response = response.mid(0, idx);
} else
if (response.contains("RECORD")) { // RECORD SAVED or RECORD WRITE ABORTED
printInfoMessage(port, clientIP, clientPort, QString("IGNORED '") + response + "' RESPONSE");
::close(sockfd);
return std::nullopt;
}
QJsonParseError parseError;
QJsonDocument document(QJsonDocument::fromJson(response.toUtf8(), &parseError));
if (parseError.error == QJsonParseError::NoError) {
if (document.isObject()) { // done: received valid APISM response
printInfoMessage(port, clientIP, clientPort,
QString("VALID APISM RESPONSE .. \n") + response);
::close(sockfd);
return response;
} else {
printInfoMessage(port, clientIP, clientPort,
QString("CORRUPTED RESPONSE ") + response);
::close(sockfd);
return std::nullopt;
}
} else {
printDebugMessage(port, clientIP, clientPort,
QString("PARSE ERROR ") + response + " " + parseError.errorString());
::close(sockfd);
return std::nullopt;
}
}
return std::nullopt;
}
QString IsmasClient::updateNewsToIsmas(char const *event,
int percent,
int resultCode,
@@ -53,37 +366,82 @@ QString IsmasClient::updateNewsToIsmas(char const *event,
return buf;
}
QString IsmasClient::updateOfPSASendVersion(QString const &tariffVersion,
QString const &tariffProject,
int tariffZone,
QString const &tariffInfo,
QString const &tariffLoadTime,
QString const &linuxVersion,
QString const &cpuSerial,
QString const &deviceControllerVersion,
QString const &deviceControllerGitBlob,
QString const &deviceControllerGitLastCommit,
QString const &raucVersion,
QString const &opkgVersion,
QString const &atbQTVersion,
QString const &atbQTGitDescribe,
QString const &deviceControllerPluginVersion,
QString const &ingenicoISelfCCPluginVersion,
QString const &mobilisisCalculatePricePluginVersion,
QString const &mobilisisCalculatePriceConfigUiVersion,
QString const &prmCalculatePricePluginVersion,
QString const &prmCalculatePriceConfigUiPluginVersion,
QString const &tcpZVTPluginVersion) {
char buf[4096];
memset(buf, 0, sizeof(buf));
QString IsmasClient::errorBackendNotConnected(QString const &info,
QString const &version) {
return updateNewsToIsmas("U0003",
m_progressInPercent,
RESULT_CODE::INSTALL_ERROR,
"CHECK BACKEND CONNECTIVITY",
info.toStdString().c_str(),
version.toStdString().c_str());
}
QString const ts = QDateTime::currentDateTime().toString(Qt::ISODateWithMs);
QString sendVersionHash;
QString IsmasClient::errorGitClone(int percent, QString const &info, QString const &version) {
return updateNewsToIsmas("U0003",
percent,
RESULT_CODE::INSTALL_ERROR,
"CLONE CUSTOMER REPOSITORY FAILED",
info.toStdString().c_str(),
version.toStdString().c_str());
}
QString IsmasClient::backendConnected(QString const &info, QString const &version) {
return updateNewsToIsmas("U0010",
m_progressInPercent,
RESULT_CODE::SUCCESS,
"CHECK BACKEND CONNECTIVITY",
info.toStdString().c_str(),
version.toStdString().c_str());
}
QString IsmasClient::execOpkgCommand(QString const &info, QString const &version) {
return updateNewsToIsmas("U0010",
m_progressInPercent,
RESULT_CODE::SUCCESS,
"EXECUTE OPKG COMMAND",
info.toStdString().c_str(),
version.toStdString().c_str());
}
QString IsmasClient::rsyncFile(QString const &info, QString const &version) {
return updateNewsToIsmas("U0010",
m_progressInPercent,
RESULT_CODE::SUCCESS,
"RSYNC FILE",
info.toStdString().c_str(),
version.toStdString().c_str());
}
QString IsmasClient::updateTriggerSet(QString const &info, QString const &version) {
return updateNewsToIsmas("U0010",
m_progressInPercent,
RESULT_CODE::SUCCESS,
"CHECK UPDATE TRIGGER",
info.toStdString().c_str(),
version.toStdString().c_str());
}
QString IsmasClient::errorUpdateTrigger(QString const &info, QString const &version) {
return updateNewsToIsmas("U0003",
m_progressInPercent,
RESULT_CODE::INSTALL_ERROR,
"CHECK UPDATE TRIGGER",
info.toStdString().c_str(),
version.toStdString().c_str());
}
QString IsmasClient::updateOfPSASendVersion(PSAInstalled const &psa) {
//static int const constexpr SIZE = 4096*8;
static char buf[4096*2];
memset(buf, 0, sizeof(buf));
// local data="#M=APISM#C=CMD_SENDVERSION#J=
snprintf(buf, sizeof(buf)-1,
"{"
"\"VERSION_INFO\" : {"
"\"REASON\":\"%s\","
"\"CREATED\":\"%s\","
"\"HASH\":\"%s\""
"},"
@@ -92,8 +450,159 @@ QString IsmasClient::updateOfPSASendVersion(QString const &tariffVersion,
"\"PROJECT\" : \"%s\","
"\"ZONE\" : %d,"
"\"INFO\" : \"%s\","
"\"BLOB\" : \"%s\","
"\"LAST-COMMIT\" : \"%s\","
"\"SIZE\" : %d,"
"\"LOADED\" : \"%s\""
"},"
"\"OPKG_COMMANDS\" : {"
"\"BLOB\" : \"%s\","
"\"LAST-COMMIT\" : \"%s\","
"\"SIZE\" : %d,"
"\"LOADED\" : \"%s\""
"},"
"\"JSON\" : {"
"\"DC2C_CASH\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_CONF\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_DEVICE\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_01\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_02\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_03\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_04\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_05\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_06\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_07\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_08\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_09\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_10\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_11\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_12\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_13\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_14\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_15\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_16\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_17\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_18\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_19\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_20\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_21\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_22\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_23\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_24\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_25\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_26\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_27\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_28\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_29\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_30\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_31\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"},"
"\"DC2C_PRINT_32\" : {"
"\"BLOB\" : \"%s\","
"\"SIZE\" : %d"
"}"
"},"
"\"HARDWARE\" : {"
"\"DEVICES\" : [\"PTU5\", \"DC\", \"PRINTER\", \"BNA\"]"
"},"
@@ -105,7 +614,9 @@ QString IsmasClient::updateOfPSASendVersion(QString const &tariffVersion,
"\"CPU_SERIAL\" : \"%s\""
"},"
"\"DC\" : {"
"\"VERSION\" : \"%s\","
"\"HW-VERSION\" : \"%s\","
"\"SW-VERSION\" : \"%s\","
"\"SIZE\" : %d,"
"\"GITBLOB\" : \"%s\","
"\"GITLASTCOMMIT\" : \"%s\""
"},"
@@ -115,11 +626,14 @@ QString IsmasClient::updateOfPSASendVersion(QString const &tariffVersion,
"}"
"},"
"\"SOFTWARE\": {"
"\"RAUC\" : \"%s\","
"\"OPKG\" : \"%s\","
"\"APISM\" : {"
"\"VERSION\" : \"%s\""
"},"
"\"ATBQT\" : {"
"\"VERSION\" : \"%s\","
"\"GIT_DESCRIBE\" : \"%s\""
"\"VERSION\" : \"%s\""
"},"
"\"ATB-UPDATE-TOOL\" : {"
"\"VERSION\" : \"%s\""
"}"
"},"
"\"PLUGINS\" : {"
@@ -146,67 +660,266 @@ QString IsmasClient::updateOfPSASendVersion(QString const &tariffVersion,
"}"
"}"
"}",
ts.toStdString().c_str(),
sendVersionHash.toStdString().c_str(),
psa.versionInfo.reason.toStdString().c_str(),
psa.versionInfo.created.toStdString().c_str(),
psa.versionInfo.lastCommit.toStdString().c_str(),
tariffVersion.toStdString().c_str(),
tariffProject.toStdString().c_str(),
tariffZone,
tariffInfo.toStdString().c_str(),
tariffLoadTime.toStdString().c_str(),
psa.tariff.version.toStdString().c_str(),
psa.tariff.project.toStdString().c_str(),
psa.tariff.zone,
psa.tariff.info.toStdString().c_str(),
psa.tariff.blob.toStdString().c_str(),
psa.tariff.lastCommit.toStdString().c_str(),
psa.tariff.size,
psa.tariff.loadTime.toStdString().c_str(),
linuxVersion.toStdString().c_str(),
psa.opkg.blob.toStdString().c_str(),
psa.opkg.lastCommit.toStdString().c_str(),
psa.opkg.size,
psa.opkg.loadTime.toStdString().c_str(),
cpuSerial.toStdString().c_str(),
psa.cash.blob.toStdString().c_str(),
psa.cash.size,
psa.conf.blob.toStdString().c_str(),
psa.conf.size,
psa.device.blob.toStdString().c_str(),
psa.device.size,
deviceControllerVersion.toStdString().c_str(),
deviceControllerGitBlob.toStdString().c_str(),
deviceControllerGitLastCommit.toStdString().c_str(),
psa.print[0].blob.toStdString().c_str(),
psa.print[0].size,
psa.print[1].blob.toStdString().c_str(),
psa.print[1].size,
psa.print[2].blob.toStdString().c_str(),
psa.print[2].size,
psa.print[3].blob.toStdString().c_str(),
psa.print[3].size,
psa.print[4].blob.toStdString().c_str(),
psa.print[4].size,
psa.print[5].blob.toStdString().c_str(),
psa.print[5].size,
psa.print[6].blob.toStdString().c_str(),
psa.print[6].size,
psa.print[7].blob.toStdString().c_str(),
psa.print[7].size,
psa.print[8].blob.toStdString().c_str(),
psa.print[8].size,
psa.print[9].blob.toStdString().c_str(),
psa.print[9].size,
psa.print[10].blob.toStdString().c_str(),
psa.print[10].size,
psa.print[11].blob.toStdString().c_str(),
psa.print[11].size,
psa.print[12].blob.toStdString().c_str(),
psa.print[12].size,
psa.print[13].blob.toStdString().c_str(),
psa.print[13].size,
psa.print[14].blob.toStdString().c_str(),
psa.print[14].size,
psa.print[15].blob.toStdString().c_str(),
psa.print[15].size,
psa.print[16].blob.toStdString().c_str(),
psa.print[16].size,
psa.print[17].blob.toStdString().c_str(),
psa.print[17].size,
psa.print[18].blob.toStdString().c_str(),
psa.print[18].size,
psa.print[19].blob.toStdString().c_str(),
psa.print[19].size,
psa.print[20].blob.toStdString().c_str(),
psa.print[20].size,
psa.print[21].blob.toStdString().c_str(),
psa.print[21].size,
psa.print[22].blob.toStdString().c_str(),
psa.print[22].size,
psa.print[23].blob.toStdString().c_str(),
psa.print[23].size,
psa.print[24].blob.toStdString().c_str(),
psa.print[24].size,
psa.print[25].blob.toStdString().c_str(),
psa.print[25].size,
psa.print[26].blob.toStdString().c_str(),
psa.print[26].size,
psa.print[27].blob.toStdString().c_str(),
psa.print[27].size,
psa.print[28].blob.toStdString().c_str(),
psa.print[28].size,
psa.print[29].blob.toStdString().c_str(),
psa.print[29].size,
psa.print[30].blob.toStdString().c_str(),
psa.print[30].size,
psa.print[31].blob.toStdString().c_str(),
psa.print[31].size,
raucVersion.toStdString().c_str(),
opkgVersion.toStdString().c_str(),
atbQTVersion.toStdString().c_str(),
atbQTGitDescribe.toStdString().c_str(),
psa.hw.linuxVersion.toStdString().c_str(),
psa.hw.cpuSerial.toStdString().c_str(),
deviceControllerPluginVersion.toStdString().c_str(),
ingenicoISelfCCPluginVersion.toStdString().c_str(),
mobilisisCalculatePricePluginVersion.toStdString().c_str(),
mobilisisCalculatePriceConfigUiVersion.toStdString().c_str(),
prmCalculatePricePluginVersion.toStdString().c_str(),
prmCalculatePriceConfigUiPluginVersion.toStdString().c_str(),
tcpZVTPluginVersion.toStdString().c_str());
psa.dc.versionHW.toStdString().c_str(),
psa.dc.versionSW.toStdString().c_str(),
psa.dc.size,
psa.dc.gitBlob.toStdString().c_str(),
psa.dc.gitLastCommit.toStdString().c_str(),
psa.sw.apismVersion.toStdString().c_str(),
psa.sw.atbQTVersion.toStdString().c_str(),
psa.sw.atbUpdateToolVersion.toStdString().c_str(),
psa.pluginVersion.deviceController.toStdString().c_str(),
psa.pluginVersion.ingenicoISelfCC.toStdString().c_str(),
psa.pluginVersion.mobilisisCalculatePrice.toStdString().c_str(),
psa.pluginVersion.mobilisisCalculatePriceConfigUi.toStdString().c_str(),
psa.pluginVersion.prmCalculatePrice.toStdString().c_str(),
psa.pluginVersion.prmCalculatePriceConfigUi.toStdString().c_str(),
psa.pluginVersion.tcpZVT.toStdString().c_str());
qInfo() << buf;
return buf;
}
QString IsmasClient::updateOfPSAActivated() {
QString IsmasClient::updateOfPSAContinues(QString currentStage,
QString currentStageInfo,
QString const &version) {
return updateNewsToIsmas("U0010",
1,
0,
"activated",
"detected WAIT state",
"1.0.0");
m_progressInPercent,
RESULT_CODE::SUCCESS,
currentStage.toStdString().c_str(),
currentStageInfo.toStdString().c_str(),
version.toStdString().c_str());
}
QString IsmasClient::updateOfPSASucceeded() {
QString IsmasClient::updateOfPSAStarted(QString const &version) {
return updateNewsToIsmas("U0010",
m_progressInPercent,
RESULT_CODE::SUCCESS,
"START",
"detected WAIT state: start update process",
version.toStdString().c_str());
}
QString IsmasClient::checkoutBranch(QString const &info, QString const &version) {
return updateNewsToIsmas("U0010",
m_progressInPercent,
RESULT_CODE::SUCCESS,
"BRANCH-CHECKOUT",
info.toStdString().c_str(),
version.toStdString().c_str());
}
QString IsmasClient::cloneAndCheckoutCustomerRepository(QString const &info, QString const &version) { // clone and checkout customer repository
return updateNewsToIsmas("U0010",
m_progressInPercent,
RESULT_CODE::SUCCESS,
"CLONE-CHECKOUT",
info.toStdString().c_str(),
version.toStdString().c_str());
}
QString IsmasClient::gitFetch(QString const &info, QString const &version) {
return updateNewsToIsmas("U0010",
m_progressInPercent,
RESULT_CODE::SUCCESS,
"GIT-FETCH",
info.toStdString().c_str(),
version.toStdString().c_str());
}
QString IsmasClient::errorGitFetch(int resultCode, QString const &info, QString const &version) {
return updateNewsToIsmas("U0003",
m_progressInPercent,
resultCode,
"GIT-FETCH-FAILED",
info.toStdString().c_str(),
version.toStdString().c_str());
}
QString IsmasClient::updateOfPSAActivated(QString const &version) { // sent even after success
m_progressInPercent = 0;
return updateNewsToIsmas("U0002",
m_progressInPercent,
RESULT_CODE::SUCCESS,
"UPDATE ACTIVATED",
"reset WAIT state",
version.toStdString().c_str());
}
QString IsmasClient::updateOfPSASucceeded(QString const &version) {
m_progressInPercent = 100;
return updateNewsToIsmas("U0001",
100,
0,
"update_succeeded",
"",
"1.0.0");
m_progressInPercent,
RESULT_CODE::SUCCESS,
"UPDATE SUCCESS",
"update process succeeded",
version.toStdString().c_str());
}
QString IsmasClient::setUpdatesAvailable() {
return updateNewsToIsmas("U0099",
10,
0,
"set_updates_available",
"",
"");
QString IsmasClient::sanityCheckFailed(int resultCode, QString reason, QString const &version) {
return updateNewsToIsmas("U0003",
m_progressInPercent,
resultCode,
"SANITY-CHECK-FAILED",
reason.toStdString().c_str(),
version.toStdString().c_str());
}
QString IsmasClient::jsonParseFailed(int resultCode, QString reason, QString const &version) {
return updateNewsToIsmas("U0003",
m_progressInPercent,
resultCode,
"JSON-PARSE-ERROR",
reason.toStdString().c_str(),
version.toStdString().c_str());
}
std::optional<QString> IsmasClient::finalResult(int resultCode,
QString reason,
QString const &version) {
Q_UNUSED(resultCode);
Q_UNUSED(reason);
Q_UNUSED(version);
bool checkForAvailableUpdates();
/*
m_progressInPercent = 100;
if (resultCode == RESULT_CODE::SUCCESS) {
return updateNewsToIsmas("U0002",
m_progressInPercent,
resultCode,
"FINAL-UPDATE-RESULT",
reason.toStdString().c_str(),
version.toStdString().c_str());
}
if (resultCode == RESULT_CODE::INSTALL_ERROR) {
return updateNewsToIsmas("U0003",
m_progressInPercent,
resultCode,
"FINAL-UPDATE-RESULT",
reason.toStdString().c_str(),
version.toStdString().c_str());
}
*/
return std::nullopt;
}
QString IsmasClient::updateOfPSAFailed(int resultCode, QString step,
QString reason, QString const &version) {
return updateNewsToIsmas("U0003",
m_progressInPercent,
resultCode,
step.toStdString().c_str(),
reason.toStdString().c_str(),
version.toStdString().c_str());
}
char const *IsmasClient::reason[REASON::ENTRIES] = {
"TIME-TRIGGERED", "SERVICE", "DEV-TEST"
};
QString IsmasClient::getReasonForLastSendVersion() {
QString const &parentName = Utils::getParentName();
if (parentName == "ATBQT") {
return reason[REASON::SERVICE];
}
if (parentName == "systemd") {
return reason[REASON::TIME_TRIGGERED];
}
return reason[REASON::DEV_TEST];
}

View File

@@ -4,10 +4,168 @@
#include <QObject>
#include <QString>
#include <optional>
struct PSAInstalled {
struct VersionInfo {
QString created;
QString reason;
QString lastCommit;
} versionInfo;
struct Tariff {
QString name;
QString version;
QString project;
int zone;
int size;
QString blob;
QString lastCommit;
QString info;
QString loadTime;
} tariff;
struct HardWare {
QString linuxVersion;
QString cpuSerial;
} hw;
struct Opkg {
int size;
QString blob;
QString lastCommit;
QString loadTime;
} opkg;
struct DC {
QString versionHW;
QString versionSW;
QString gitBlob;
QString gitLastCommit;
int size;
} dc;
struct SoftWare {
QString apismVersion;
QString atbQTVersion;
QString atbUpdateToolVersion;
} sw;
struct PluginVersion {
QString deviceController;
QString ingenicoISelfCC;
QString mobilisisCalculatePrice;
QString mobilisisCalculatePriceConfigUi;
QString prmCalculatePrice;
QString prmCalculatePriceConfigUi;
QString tcpZVT;
} pluginVersion;
struct DC2C {
QString name;
QString blob;
int size;
};
DC2C cash;
DC2C conf;
DC2C device;
DC2C print[32];
explicit PSAInstalled() {
tariff.name = "N/A";
tariff.version = "N/A";
tariff.project = "N/A";
tariff.zone = -1;
tariff.size = -1;
tariff.blob = "N/A";
tariff.info = "N/A";
tariff.loadTime = "N/A";
hw.linuxVersion = "N/A";
hw.cpuSerial = "N/A";
opkg.size = -1;
opkg.blob = "N/A";
opkg.lastCommit = "N/A";
opkg.loadTime = "N/A";
dc.versionHW = "N/A";
dc.versionSW = "N/A";
dc.gitBlob = "N/A";
dc.gitLastCommit = "N/A";
dc.size = -1;
sw.apismVersion = "N/A";
sw.atbQTVersion = "N/A";
sw.atbUpdateToolVersion = "N/A";
pluginVersion.deviceController = "N/A";
pluginVersion.ingenicoISelfCC = "N/A";
pluginVersion.mobilisisCalculatePrice = "N/A";
pluginVersion.mobilisisCalculatePriceConfigUi = "N/A";
pluginVersion.prmCalculatePrice = "N/A";
pluginVersion.prmCalculatePriceConfigUi = "N/A";
pluginVersion.tcpZVT = "N/A";
cash.name = "N/A";
cash.blob = "N/A";
cash.size = -1;
conf.name = "N/A";
conf.blob = "N/A";
conf.size = -1;
device.size = -1;
device.blob = "N/A";
device.size = -1;
for (int i=0; i < 32; ++i) {
print[i].size = -1;
print[i].blob = "N/A";
print[i].size = -1;
}
}
};
class IsmasClient : public QObject {
Q_OBJECT
int m_progressInPercent;
public:
explicit IsmasClient() : m_progressInPercent(1) {}
enum APISM {
DB_PORT = 7777,
DIRECT_PORT = 7778
};
enum RESULT_CODE {
SUCCESS=0,
NO_UPDATE_NECESSARY=1,
BACKUP_FAILED=2,
WRONG_PACKAGE=3,
INSTALL_ERROR=4};
enum REASON {
TIME_TRIGGERED = 0,
SERVICE,
DEV_TEST,
ENTRIES
};
static char const *reason[REASON::ENTRIES];
static std::optional<QString>
sendRequestReceiveResponse(int port, QString const &request);
static QString getReasonForLastSendVersion();
int getProgressInPercent() const {return m_progressInPercent; }
void setProgressInPercent(int procent) { m_progressInPercent = procent; }
QString updateNewsToIsmas(char const *event,
int percent,
int resultCode,
@@ -15,32 +173,36 @@ public:
char const *step_result,
char const *version);
QString updateOfPSAActivated();
QString updateOfPSASucceeded();
QString updateOfPSASendVersion(QString const &tariffVersion,
QString const &tariffProject,
int tariffZone,
QString const &tariffInfo,
QString const &tariffLoadTime,
QString const &linuxVersion,
QString const &cpuSerial,
QString const &deviceControllerVersion,
QString const &deviceControllerGitBlob,
QString const &deviceControllerGitLastCommit,
QString const &raucVersion,
QString const &opkgVersion,
QString const &atbQTVersion,
QString const &atbQTGitDescribe,
QString const &deviceControllerPluginVersion,
QString const &ingenicoISelfCCPluginVersion,
QString const &mobilisisCalculatePricePluginVersion,
QString const &mobilisisCalculatePriceConfigUiVersion,
QString const &prmCalculatePricePluginVersion,
QString const &prmCalculatePriceConfigUiPluginVersion,
QString const &tcpZVTPluginVersion);
QString updateOfPSAStarted(QString const &version = QString()); // start of update process
QString cloneAndCheckoutCustomerRepository(QString const &info, QString const &version = QString()); // clone and checkout customer repository
QString checkoutBranch(QString const &info, QString const &version = QString()); // checkout branch
QString errorBackendNotConnected(QString const &info, QString const &version = QString()); // checkout branch
QString errorGitClone(int percent, QString const &info, QString const &version = QString());
QString backendConnected(QString const &info, QString const &version = QString());
QString updateTriggerSet(QString const &info, QString const &version = QString());
QString errorUpdateTrigger(QString const &info, QString const &version = QString());
QString gitFetch(QString const &info, QString const &version = QString());
QString execOpkgCommand(QString const &info, QString const &version = QString());
QString rsyncFile(QString const &info, QString const &version = QString());
QString errorGitFetch(int resultCode, QString const &info, QString const &version = QString());
QString updateOfPSAActivated(QString const &version = QString());
// and update accepted
QString updateOfPSASucceeded(QString const &version = QString()); // update process succeeded
QString updateOfPSAContinues(QString currentStage, QString currentStageInfo, QString const &version = QString());
QString updateOfPSAFailed(int resultCode, QString step, QString reason, QString const &version = QString());
QString sanityCheckFailed(int resultCode, QString reason, QString const &version = QString());
QString jsonParseFailed(int resultCode, QString reason, QString const &version = QString());
std::optional<QString> finalResult(int resultCode, QString reason, QString const &version = QString());
QString setUpdatesAvailable();
bool checkForAvailableUpdates();
QString updateOfPSASendVersion(PSAInstalled const &psa);
private:
static void printDebugMessage(int port, QString const &clientIP, int clientPort,
QString const &message);
static void printInfoMessage(int port, QString const &clientIP, int clientPort,
QString const &message);
static void printErrorMessage(int port, QString const &clientIP, int clientPort,
QString const &message);
};
#endif // ISMAS_CLIENT_H_INCLUDED

159
main.cpp
View File

@@ -20,15 +20,24 @@
#include <QProcess>
#include <QCommandLineParser>
#include <QStandardPaths>
#include <QMainWindow>
#include "update.h"
#include "git/git_client.h"
#include "ismas/ismas_client.h"
#include "apism/apism_client.h"
#include "worker_thread.h"
#include "worker.h"
#include "mainwindow.h"
#include "utils.h"
#include <QThread>
#include <QtWidgets>
#include <QScopedPointer>
#if defined (Q_OS_UNIX) || defined (Q_OS_LINUX)
#include <unistd.h>
#include <errno.h>
#endif
#ifdef PTU5
#define SERIAL_PORT "ttymxc2"
@@ -44,16 +53,35 @@ int main(int argc, char *argv[]) {
}
// qputenv("XDG_RUNTIME_DIR", "/run/user/0");
openlog("ATB-UPDATE", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER);
QApplication a(argc, argv);
QApplication::setApplicationName("ATBUpdateTool");
QApplication::setApplicationVersion(APP_VERSION);
if (!messageHandlerInstalled()) { // change internal qt-QDebug-handling
atbInstallMessageHandler(atbDebugOutput);
setDebugLevel(QtMsgType::QtInfoMsg);
//setDebugLevel(QtMsgType::QtDebugMsg);
setDebugLevel(LOG_NOTICE);
}
//#if defined (Q_OS_UNIX) || defined (Q_OS_LINUX)
//#ifdef _POSIX_THREAD_PROCESS_SHARED
// errno = 0;
// int res = 0;
// if ((res = sysconf(_SC_THREAD_PROCESS_SHARED)) < 0) {
// if (errno != 0) {
// qCritical() << "_POSIX_THREAD_PROCESS_SHARED NOT SUPPORTED"
// << strerror(errno);
// exit(-1);
// }
// } else {
// if (res == _POSIX_THREAD_PROCESS_SHARED) {
// Utils::printInfoMsg("_POSIX_THREAD_PROCESS_SHARED SUPPORTED");
// }
// }
//#endif
//#endif
QCommandLineParser parser;
parser.setApplicationDescription("Download tool for downloading device controller firmware, printer json-files and executing opkg-commands.");
parser.addHelpOption();
@@ -62,78 +90,89 @@ int main(int argc, char *argv[]) {
QCommandLineOption pluginDirectoryOption(QStringList() << "plugin-directory" << "plugin-directory",
QCoreApplication::translate("main", "Where to find dc-plugin."),
QCoreApplication::translate("main", "directory"));
QString const pluginDefault = "./plugins";
QString const pluginDefault = "/usr/lib";
pluginDirectoryOption.setDefaultValue(pluginDefault);
parser.addOption(pluginDirectoryOption);
QCommandLineOption pluginNameOption(QStringList() << "plugin-name" << "plugin-name",
QCoreApplication::translate("main", "Name of dc-plugin."),
QCoreApplication::translate("main", "directory"));
QString const pluginNameDefault = "libCAmaster.so";
QString const pluginNameDefault = "libCAslave.so";
pluginNameOption.setDefaultValue(pluginNameDefault);
parser.addOption(pluginNameOption);
QCommandLineOption workingDirectoryOption(QStringList() << "working-directory" << "working-directory",
QCoreApplication::translate("main", "working directory of update-script."),
QCoreApplication::translate("main", "directory"));
QString const workingDirectoryDefault = ".";
QString const workingDirectoryDefault = "/opt/app/tools/atbupdate";
workingDirectoryOption.setDefaultValue(workingDirectoryDefault);
parser.addOption(workingDirectoryOption);
QCommandLineOption maintenanceOption(QStringList() << "m" << "maintenance",
QCoreApplication::translate("main", "Maintenance mode for underlying script"));
parser.addOption(maintenanceOption);
// test-mode: edit the file update_log.csv and execute the commands
// contained in it. Do not call the update-script.
QCommandLineOption testOption(QStringList() << "t" << "test",
QCoreApplication::translate("main", "Test mode for ATBUpdateTool"));
parser.addOption(testOption);
QCommandLineOption execScriptOption(QStringList() << "e" << "execute-script-only",
QCoreApplication::translate("main", "ATBUpdateTool executes update-script only. No download of any files."));
parser.addOption(execScriptOption);
QCommandLineOption dryRunOption(QStringList() << "d" << "dry-run",
QCoreApplication::translate("main", "Start ATBUpdateTool in dry-run-mode. No actual actions."));
parser.addOption(dryRunOption);
// TODO:
// add some additional parameters
// --dry-run
// -d: only update device-controller firmware
// -j: only update json-files
// -o: only execute opkg-commnds
// -f: force. update_psa shall always perform a 'git pull'
QCommandLineOption extendedVersionOption(QStringList() << "V" << "extended-version",
QCoreApplication::translate("main", "Show extended version (including last git commit)."));
parser.addOption(extendedVersionOption);
QCommandLineOption yoctoVersionOption(QStringList() << "y" << "yocto-version",
QCoreApplication::translate("main", "Show yocto version of ATBUpdateTool."));
parser.addOption(yoctoVersionOption);
QCommandLineOption yoctoInstallStatusOption(QStringList() << "Y" << "yocto-install",
QCoreApplication::translate("main", "Show yocto install status of ATBUpdateTool."));
parser.addOption(yoctoInstallStatusOption);
// Process the actual command line arguments given by the user
parser.process(a);
QString plugInDir = parser.value(pluginDirectoryOption);
QString plugInName = parser.value(pluginNameOption);
QString workingDir = parser.value(workingDirectoryOption);
bool maintenanceMode = parser.isSet(maintenanceOption);
bool testMode = parser.isSet(testOption);
bool executeScriptOnly = parser.isSet(execScriptOption);
bool dryRun = parser.isSet(dryRunOption);
bool const dryRun = parser.isSet(dryRunOption);
bool const showYoctoVersion = parser.isSet(yoctoVersionOption);
bool const showYoctoInstallStatus = parser.isSet(yoctoInstallStatusOption);
bool const showExtendedVersion = parser.isSet(extendedVersionOption);
QString const rtPath = QCoreApplication::applicationDirPath();
if (plugInDir == pluginDefault) {
plugInDir = (rtPath + "/" + pluginDefault);
int machineNr = Utils::read1stLineOfFile("/etc/machine_nr");
int customerNr = Utils::read1stLineOfFile("/etc/cust_nr");
int zoneNr = Utils::read1stLineOfFile("/etc/zone_nr");
QString const branchName = (zoneNr != 0)
? QString("zg1/zone%1").arg(zoneNr) : "master";
QThread::currentThread()->setObjectName("main thread");
qInfo() << "Main thread" << QThread::currentThreadId();
if (showExtendedVersion) {
printf(APP_EXTENDED_VERSION"\n");
return 0;
}
if (showYoctoVersion) {
printf("%s\n", Worker::getATBUpdateToolYoctoVersion().toStdString().c_str());
return 0;
}
if (showYoctoInstallStatus) {
printf("%s\n", Worker::getATBUpdateToolYoctoInstallationStatus().toStdString().c_str());
return 0;
}
if (!QDir(plugInDir).exists()) {
qCritical() << plugInDir
<< "does not exists, but has to contain dc-library";
exit(-1);
}
qInfo() << "pwd ..............." << rtPath;
qInfo() << "plugInDir ........." << plugInDir;
qInfo() << "plugInName ........" << plugInName;
qInfo() << "workingDir ........" << workingDir;
qInfo() << "maintenanceMode ..." << maintenanceMode;
qInfo() << "testMode .........." << testMode;
qInfo() << "execScriptOnly ...." << executeScriptOnly;
qInfo() << "dryRun ............" << dryRun;
qInfo() << "pwd ......................" << rtPath;
qInfo() << "plugInDir ................" << plugInDir;
qInfo() << "plugInName ..............." << plugInName;
qInfo() << "workingDir ..............." << workingDir;
qInfo() << "dryRun ..................." << dryRun;
qInfo() << "extended-version ........." << APP_EXTENDED_VERSION;
//qInfo() << "yocto-version ............" << Worker::getATBUpdateToolYoctoVersion();
//qInfo() << "yocto-install-status ....." << Worker::getATBUpdateToolYoctoInstallationStatus();
// before loading the library, delete all possible shared memory segments
#if defined Q_OS_LINUX || defined Q_OS_UNIX
@@ -143,18 +182,38 @@ int main(int argc, char *argv[]) {
#endif
hwinf *hw = Update::loadDCPlugin(QDir(plugInDir), plugInName);
// hw->dc_autoRequest(false);
hw->dc_autoRequest(true);
// hw->dc_openSerial(5, "115200", "ttymxc2", 1);
QString const update_ctrl_file = "/opt/app/tools/atbupdate/update_log.csv";
Worker worker(hw, update_ctrl_file,
"https://git.mimbach49.de/GerhardHoffmann/customer_999.git",
"customer_999",
"zg1/zone1",
Worker worker(customerNr,
machineNr,
zoneNr,
branchName,
plugInName,
workingDir,
maintenanceMode,
testMode,
executeScriptOnly,
dryRun);
QString const customerNrStr(
QString("customer_") + QString::number(customerNr).rightJustified(3, '0'));
QScopedPointer<Update> update(
new Update(hw,
&worker,
QDir::cleanPath(workingDir + QDir::separator() + customerNrStr),
customerNrStr,
branchName,
plugInName,
workingDir,
dryRun,
nullptr,
SERIAL_PORT,
"115200"));
MainWindow mw(hw, &worker, update.get());
worker.setMainWindow(&mw);
mw.setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
mw.showFullScreen();
return a.exec();
}

450
mainwindow.cpp Normal file
View File

@@ -0,0 +1,450 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "worker.h"
#include "utils.h"
#include "progress_event.h"
#include "update_dc_event.h"
#include "plugins/interfaces.h"
#include <QDateTime>
#include <QMessageBox>
#include <QDebug>
#include <QScrollBar>
#include <QEvent>
#if EMERGENCY_LEAVE_BL==1
static int step = 0;
void MainWindow::emergencyLeaveBL() {
//
qCritical() << __func__ << step;
switch(step) {
case 0:
if (m_hw->dc_openSerial(5, "115200", "ttymxc2", 1)) {
qCritical() << __func__ << "open ok";
step++;
QThread::msleep(2000);
m_hw->dc_autoRequest(false);
emit leaveBL();
}
break;
case 1:
m_hw->bl_rebootDC();
QThread::msleep(1000);
qCritical() << __func__ << "reboot ok" << QDateTime::currentDateTime().toString(Qt::ISODateWithMs);
step++;
emit leaveBL();
break;
case 2:
case 3:
case 4:
case 5:
case 6:
m_hw->bl_startBL();
QThread::msleep(1000);
qCritical() << __func__ << "start" << QDateTime::currentDateTime().toString(Qt::ISODateWithMs);
step++;
emit leaveBL();
break;
case 7:
case 9:
case 11:
case 13:
case 15:
m_hw->bl_checkBL();
qCritical() << __func__ << "check" << QDateTime::currentDateTime().toString(Qt::ISODateWithMs);
QThread::msleep(1500);
++step;
emit leaveBL();
break;
case 8:
case 10:
case 12:
case 14:
case 16:
qCritical() << __func__ << "is Up..." << QDateTime::currentDateTime().toString(Qt::ISODateWithMs);
if (m_hw->bl_isUp()) {
qCritical() << __func__ << "is Up...OK" << step << QDateTime::currentDateTime().toString(Qt::ISODateWithMs);
QThread::msleep(5000);
step = 16;
} else {
qCritical() << __func__ << "is Up...NO" << step << QDateTime::currentDateTime().toString(Qt::ISODateWithMs);
}
++step;
emit leaveBL();
break;
case 17:
case 18:
case 19:
qCritical() << __func__ << "stop" << QDateTime::currentDateTime().toString(Qt::ISODateWithMs);
m_hw->bl_stopBL();
QThread::msleep(1000);
//m_hw->dc_closeSerial();
++step;
emit leaveBL();
break;
}
}
#endif
MainWindow::MainWindow(hwinf *hw, Worker *worker, Update *update, QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, m_hw(hw)
, m_worker(worker)
, m_width(70)
, m_progressRunning(false)
, m_progressValue(0)
, m_update(update)
, m_updateStep(UpdateDcEvent::UpdateStep::NONE) {
#if EMERGENCY_LEAVE_BL==1
QTimer *t = new QTimer(this);
connect(t, SIGNAL(timeout()), this, SLOT(emergencyLeaveBL()));
connect(this, SIGNAL(leaveBL()), this, SLOT(emergencyLeaveBL()), Qt::QueuedConnection);
t->setSingleShot(true);
t->start(1000);
return;
#endif
this->setStatusBar(new QStatusBar(this));
QFont f;
f.setStyleHint(QFont::Monospace);
f.setWeight(QFont::Bold);
f.setFamily("Misc Fixed");
f.setPixelSize(12);
this->statusBar()->setFont(f);
ui->setupUi(this);
ui->updateProgress->setRange(0, 100);
ui->updateProgress->reset();
QStringList lst;
QString start = QDateTime::currentDateTime().toString(Qt::ISODate);
lst << QString("Start: ") + start.leftJustified(m_width-10);
lst << QString("").leftJustified(m_width-3, '=');
lst << QString("Update tool version: %1 - %2 %3").arg(APP_VERSION).arg(APP_BUILD_DATE).arg(APP_BUILD_TIME).leftJustified(m_width-3);
lst << QString("Machine number : %1 ").arg(m_worker->machineNr()).leftJustified(m_width-3);
lst << QString("Customer number : %1 ").arg(m_worker->customerNr()).leftJustified(m_width-3);
lst << QString("Zone number : %1 (%2)").arg(m_worker->zoneNr()).arg(Utils::zoneName(m_worker->zoneNr())).leftJustified(m_width-3);
lst << QString("APISM version : %1").arg(m_worker->apismVersion()).leftJustified(m_width-3);
lst << QString("").leftJustified(m_width-3, '=');
ui->updateStatus->setText(lst.join('\n'));
ui->updateStatus->setEnabled(true);
// ui->updateStatus->installEventFilter(this);
m_startTimer = new QTimer(this);
connect(m_startTimer, SIGNAL(timeout()), m_worker, SLOT(update()));
m_startTimer->setSingleShot(true);
m_startTimer->start(1000);
m_exitTimer = new QTimer(this);
connect(m_exitTimer, SIGNAL(timeout()), ui->exit, SLOT(click()));
m_exitTimer->setSingleShot(true);
m_exitTimer->start(1800 * 1000);
connect(ui->exit, SIGNAL(clicked()),this,SLOT(onQuit()));
connect(m_worker, SIGNAL(disableExit()),this,SLOT(onDisableExit()));
connect(m_worker, SIGNAL(enableExit()),this,SLOT(onEnableExit()));
connect(m_worker, SIGNAL(stopStartTimer()),this,SLOT(onStopStartTimer()));
connect(m_worker, SIGNAL(restartExitTimer()),this,SLOT(onRestartExitTimer()));
connect(m_worker, SIGNAL(appendText(QString,QString)),this,SLOT(onAppendText(QString,QString)));
connect(m_worker, SIGNAL(showErrorMessage(QString,QString)),this, SLOT(onShowErrorMessage(QString,QString)));
connect(m_worker, SIGNAL(showStatusMessage(QString,QString)),this, SLOT(onShowStatusMessage(QString,QString)));
connect(m_worker, SIGNAL(replaceLast(QString,QString)),this,SLOT(onReplaceLast(QString,QString)));
connect(m_worker, SIGNAL(replaceLast(QStringList,QString)),this, SLOT(onReplaceLast(QStringList,QString)));
}
MainWindow::~MainWindow() {
delete m_startTimer;
delete m_exitTimer;
delete ui;
}
void MainWindow::customEvent(QEvent *event) {
if (event->type() == ProgressEvent::type()) {
ProgressEvent *pevent = (ProgressEvent *)event;
int const progress = pevent->progressPercent();
QObject const *sender = pevent->sender();
if (sender == this) {
switch(progress) {
case 0: {
ui->updateProgress->reset();
} break;
case START_PROGRESS_LOOP: {
m_progressRunning = true;
ui->updateProgress->reset();
m_progressValue = 10;
QApplication::postEvent(this, new ProgressEvent(this, m_progressValue));
} break;
case STOP_PROGRESS_LOOP: {
m_progressRunning = false;
m_progressValue -= 10;
m_worker->setProgress(m_progressValue/10);
} break;
default: {
if (m_progressRunning) {
m_progressValue = progress;
ui->updateProgress->setValue(progress/10);
QApplication::postEvent(this, new ProgressEvent(this, progress+10));
QThread::msleep(500);
}
}
}
} else
if (sender == m_worker) {
switch(progress) {
case 0: {
ui->updateProgress->reset();
} break;
case START_PROGRESS_LOOP: {
QApplication::postEvent(this, new ProgressEvent(this, START_PROGRESS_LOOP));
} break;
case STOP_PROGRESS_LOOP: {
QApplication::postEvent(this, new ProgressEvent(this, STOP_PROGRESS_LOOP));
} break;
default:{
ui->updateProgress->setValue(progress);
}}
} else {
qCritical() << "!!! UNKNOWN SENDER !!!";
}
} else
if (event->type() == UpdateDcEvent::type()) {
UpdateDcEvent *pevent = (UpdateDcEvent *)event;
UpdateDcEvent::UpdateStep const updateStep = pevent->updateStep();
QObject const *sender = pevent->sender();
if (sender == m_worker) {
QDateTime const &recv = QDateTime::currentDateTime();
QDateTime const &send = pevent->sendDateTime();
qint64 const delay = recv.toMSecsSinceEpoch() - send.toMSecsSinceEpoch();
switch(updateStep) {
case UpdateDcEvent::UpdateStep::NONE:
break;
case UpdateDcEvent::UpdateStep::DC_REBOOT: {
m_hw->bl_rebootDC();
QString msg = QDateTime::currentDateTime().toString(Qt::ISODateWithMs)
+ QString(": reset device controller (delay=%1ms").arg(delay);
emit m_worker->showStatusMessage("dc update", msg);
Utils::printInfoMsg(msg.toUpper());
m_updateStep = UpdateDcEvent::UpdateStep::DC_REBOOT;
} break;
case UpdateDcEvent::UpdateStep::BL_START: {
QString const &msg = recv.toString(Qt::ISODateWithMs)
+ QString(": start bootloader (%1, delay=%2ms)").arg(pevent->count()).arg(delay);
emit m_worker->showStatusMessage("dc update", msg);
Utils::printInfoMsg(msg.toUpper());
m_hw->bl_startBL();
if (pevent->count() == BL_START_COUNT) {
m_updateStep = UpdateDcEvent::UpdateStep::BL_START;
}
} break;
case UpdateDcEvent::UpdateStep::BL_CHECK: {
if (m_updateStep != UpdateDcEvent::UpdateStep::BL_IS_UP) {
QString const &msg = recv.toString(Qt::ISODateWithMs)
+ QString(": request bootloader version (%1, delay=%2ms)").arg(pevent->count()).arg(delay);
emit m_worker->showStatusMessage("dc update", msg);
Utils::printInfoMsg(msg.toUpper());
m_hw->bl_checkBL();
//m_updateStep = UpdateDcEvent::UpdateStep::BL_CHECK;
}
} break;
case UpdateDcEvent::UpdateStep::BL_IS_UP: {
QString msg = recv.toString(Qt::ISODateWithMs)
+ QString(": check running bootloader (%1, delay=%2ms)").arg(pevent->count()).arg(delay);
emit m_worker->showStatusMessage("dc update", msg);
Utils::printInfoMsg(msg.toUpper());
if (m_updateStep != UpdateDcEvent::UpdateStep::BL_IS_UP) {
if (m_hw->bl_isUp()) {
msg = recv.toString(Qt::ISODateWithMs)
+ QString(": bootloader running (%1, delay=%2ms)").arg(pevent->count()).arg(delay);
emit m_worker->showStatusMessage("dc update", msg);
Utils::printInfoMsg(msg.toUpper());
m_updateStep = UpdateDcEvent::UpdateStep::BL_IS_UP;
} else {
msg = recv.toString(Qt::ISODateWithMs)
+ QString(": bootloader stop requested (%1, delay=%2ms)").arg(pevent->count()).arg(delay);
emit m_worker->showStatusMessage("dc update", msg);
Utils::printInfoMsg(msg.toUpper());
if (m_updateStep == UpdateDcEvent::UpdateStep::BL_STOP) {
msg = QDateTime::currentDateTime().toString(Qt::ISODateWithMs)
+ QString(": bootloader down (%1, delay=%2ms)").arg(pevent->count()).arg(delay);
emit m_worker->showStatusMessage("dc update", msg);
Utils::printInfoMsg(msg.toUpper());
m_updateStep = UpdateDcEvent::UpdateStep::BL_IS_DOWN;
}
}
}
} break;
case UpdateDcEvent::UpdateStep::BL_STOP: {
QString const &msg = QDateTime::currentDateTime().toString(Qt::ISODateWithMs)
+ QString(": stop bootloader (%1, delay=%2ms)").arg(pevent->count()).arg(delay);
emit m_worker->showStatusMessage("dc update", msg);
Utils::printInfoMsg(msg.toUpper());
//if (m_bootLoaderIsUp) {
m_hw->bl_stopBL();
m_updateStep = UpdateDcEvent::UpdateStep::BL_STOP;
//}
} break;
default: ;
}
}
}
QThread::yieldCurrentThread();
}
void MainWindow::onStopStartTimer() {
m_startTimer->stop();
}
void MainWindow::onDisableExit() {
ui->exit->setEnabled(false);
}
void MainWindow::onEnableExit() {
ui->exit->setEnabled(true);
}
//bool MainWindow::eventFilter(QObject *obj, QEvent *ev) {
// if (obj == ui->updateStatus) {
// qCritical() << "REc. event for text edit" << ev->type();
// }
// return QMainWindow::eventFilter(obj, ev);
//}
void MainWindow::onRestartExitTimer() {
m_exitTimer->stop();
m_exitTimer->start(60 * 1000);
scrollDownTextEdit();
ui->updateStatus->setEnabled(false);
}
void MainWindow::onQuit() {
m_exitTimer->stop();
qCritical() << QString("ON QUIT: EXIT CODE %1").arg(m_worker->returnCode());
qApp->exit(m_worker->returnCode());
}
void MainWindow::scrollDownTextEdit() {
// Utils::printInfoMsg(QString("SCROLL-DOWN-TEXT_EDIT CALLED AT ")
// + QDateTime::currentDateTime().toString(Qt::ISODateWithMs));
ui->updateStatus->setEnabled(true);
QTextCursor tmpCursor = ui->updateStatus->textCursor();
tmpCursor.movePosition(QTextCursor::End);
ui->updateStatus->setTextCursor(tmpCursor);
ui->updateStatus->ensureCursorVisible();
}
void MainWindow::onAppendText(QString text, QString suffix) {
// Utils::printInfoMsg(QString("ON APPEND CALLED AT ")
// + QDateTime::currentDateTime().toString(Qt::ISODateWithMs));
QString editText = ui->updateStatus->toPlainText();
scrollDownTextEdit();
if (!suffix.isNull() && suffix.size() > 0) {
//qInfo() << "TEXT" << text << "SUFFIX" << suffix;
if (suffix == Worker::UPDATE_STEP_SUCCESS || suffix == Worker::UPDATE_STEP_FAIL) {
ui->updateStatus->insertPlainText(QString("\n").leftJustified(m_width-3, '=') + " ");
// editText += QString("\n").leftJustified(m_width-3, '=');
// editText += " ";
}
QString const &add = (QString("\n") + text).leftJustified(m_width - (2 + suffix.size())) + suffix;
ui->updateStatus->insertPlainText(add);
// editText += add;
} else {
QString const &add = text.leftJustified(m_width-9);
ui->updateStatus->insertPlainText(add);
//editText += add;
}
// debug
// QString editText = ui->updateStatus->toPlainText();
// Utils::printLineEditInfo(editText.split('\n', QString::SplitBehavior::SkipEmptyParts));
// ui->updateStatus->setText(editText.trimmed());
// scrollDownTextEdit();
}
void MainWindow::onReplaceLast(QStringList newTextLines, QString suffix) {
// Utils::printInfoMsg(QString("ON REPLACE LAST (LIST) CALLED AT ")
// + QDateTime::currentDateTime().toString(Qt::ISODateWithMs));
int const s = newTextLines.size();
if (s > 0) {
QString editText = ui->updateStatus->toPlainText();
QStringList lines = editText.split('\n', QString::SplitBehavior::SkipEmptyParts);
QString newText;
if (lines.size() >= s) {
for (int i = 0; i < s; ++i) {
lines.removeLast();
}
if (lines.size() > 0) {
newText = lines.join('\n');
newText += '\n';
}
QStringList newLines;
for (int i = 0; i < s; ++i) {
if (i == 0 && !suffix.isNull() && suffix.size() > 0 && suffix != "\n") {
newLines += Utils::rstrip(newTextLines.at(i).leftJustified(m_width-10) + suffix);
} else {
newLines += Utils::rstrip(newTextLines.at(i).leftJustified(m_width-10));
}
}
lines += newLines;
newText += newLines.join(' ');
}
ui->updateStatus->setText(newText);
Utils::printLineEditInfo(lines);
scrollDownTextEdit();
}
}
void MainWindow::onReplaceLast(QString text, QString suffix) {
// Utils::printInfoMsg(QString("ON REPLACE LAST (TEXT) CALLED AT ")
// + QDateTime::currentDateTime().toString(Qt::ISODateWithMs));
QString editText = ui->updateStatus->toPlainText();
QStringList lines = editText.split('\n', QString::SplitBehavior::SkipEmptyParts);
if (lines.size() > 0) {
lines.removeLast();
if (!suffix.isNull() && suffix.size() > 0 && suffix != "\n") {
QString const add = text.leftJustified(m_width-10) + suffix;
if (!add.isEmpty()) {
lines += text.leftJustified(m_width-10) + suffix;
}
} else {
QString const add = text.leftJustified(m_width-10);
if (!add.isEmpty()) {
lines += text.leftJustified(m_width-10);
}
}
}
Utils::printLineEditInfo(lines);
ui->updateStatus->setText(lines.join('\n').trimmed());
scrollDownTextEdit();
}
void MainWindow::onShowMessage(QString title, QString text) {
this->statusBar()->clearMessage();
this->statusBar()->showMessage( // timeout: 10000
QString(title + " " + text).leftJustified(80, ' '), 10000);
}
void MainWindow::onShowErrorMessage(QString title, QString text) {
onShowMessage(title, text);
}
void MainWindow::onShowStatusMessage(QString title, QString text) {
onShowMessage(title, text);
}

83
mainwindow.h Normal file
View File

@@ -0,0 +1,83 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTimer>
#include <QStatusBar>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
#include "worker.h"
#include "update.h"
#include "update_dc_event.h"
#define EMERGENCY_LEAVE_BL 0
class hwinf;
class MainWindow : public QMainWindow {
Q_OBJECT
protected:
void customEvent(QEvent *event) override;
// bool eventFilter(QObject *obj, QEvent *ev) override;
public:
MainWindow(hwinf *hw, Worker *worker, Update *update, QWidget *parent = nullptr);
~MainWindow();
static const int START_PROGRESS_LOOP = -1;
static const int STOP_PROGRESS_LOOP = -2;
static const int BL_START_COUNT = 5;
static const int BL_CHECK_COUNT = 5;
static const int BL_IS_UP_COUNT = 5;
static const int BL_STOP_COUNT = 5;
int progressValue() const { return m_progressValue; }
hwinf *getPlugin() { return m_hw; }
hwinf const *getPlugin() const { return m_hw; }
Update *getUpdate() { return m_update; }
Update const *getUpdate() const { return m_update; }
UpdateDcEvent::UpdateStep updateStep() const { return m_updateStep; }
void setUpdateStep(UpdateDcEvent::UpdateStep updateStep) { m_updateStep = updateStep; }
public slots:
void onAppendText(QString, QString suffix = "");
void onReplaceLast(QStringList, QString suffix = "");
void onReplaceLast(QString, QString suffix = "");
void onShowErrorMessage(QString, QString);
void onShowStatusMessage(QString, QString);
void onStopStartTimer();
void onRestartExitTimer();
void onEnableExit();
void onDisableExit();
#if EMERGENCY_LEAVE_BL==1
void emergencyLeaveBL();
#endif
signals:
#if EMERGENCY_LEAVE_BL==1
void leaveBL();
#endif
private slots:
void onQuit();
private:
void scrollDownTextEdit();
void onShowMessage(QString, QString);
Ui::MainWindow *ui;
hwinf *m_hw;
Worker *m_worker;
int const m_width;
QTimer *m_startTimer;
QTimer *m_exitTimer;
bool m_progressRunning;
int m_progressValue;
Update *m_update;
UpdateDcEvent::UpdateStep m_updateStep;
};
#endif // MAINWINDOW_H

75
mainwindow.ui Normal file
View File

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>480</height>
</rect>
</property>
<property name="font">
<font>
<family>Source Code Pro</family>
</font>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>781</width>
<height>441</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="2">
<widget class="QPushButton" name="exit">
<property name="text">
<string>Exit</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QProgressBar" name="updateProgress">
<property name="value">
<number>1</number>
</property>
</widget>
</item>
<item row="0" column="0" rowspan="3" colspan="3">
<widget class="QTextEdit" name="updateStatus">
<property name="enabled">
<bool>true</bool>
</property>
<property name="font">
<font>
<family>Misc Fixed</family>
<pointsize>11</pointsize>
<bold>true</bold>
</font>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAsNeeded</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAsNeeded</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -6,13 +6,13 @@
#include <QFileInfo>
#include <QMessageLogContext>
#define OUTPUT_LEN (512)
static char const *DBG_NAME[] = { "DBG ", "WARN ", "CRIT ", "FATAL", "INFO " };
static bool installedMsgHandler = false;
static QtMsgType debugLevel = QtInfoMsg;
static int debugLevel = LOG_NOTICE;
QtMsgType getDebugLevel() { return debugLevel; }
void setDebugLevel(QtMsgType newDebugLevel) {
int getDebugLevel() { return debugLevel; }
void setDebugLevel(int newDebugLevel) {
debugLevel = newDebugLevel;
}
@@ -45,75 +45,51 @@ QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler) {
///
#if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
void atbDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
// static constexpr const char *format = "dd.MM.yyyy hh:mm:ss";
QByteArray localMsg = msg.toLocal8Bit();
QString fileName(context.file ? context.file : "N/A");
QString function(context.function ? context.function : "N/A");
char buf[OUTPUT_LEN]{};
memset(buf, 0x00, sizeof(buf));
QString const datetime = QDateTime::currentDateTime().toString(Qt::ISODateWithMs);
switch (type) {
case QtDebugMsg: {
if (debugLevel == QtDebugMsg) {
snprintf(buf, sizeof(buf)-1, "%s DEBG [%s:%s:%04u] %s",
datetime.toStdString().c_str(),
function.toStdString().c_str(),
fileName.toStdString().c_str(),
context.line,
localMsg.constData());
fprintf(stderr, "%s\n", buf);
Q_UNUSED(context);
QString const localMsg = QString(DBG_NAME[type]) + msg.toLocal8Bit();
switch (debugLevel) {
case LOG_DEBUG: { // debug-level message
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
} break;
case LOG_INFO: { // informational message
if (type != QtDebugMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
case QtInfoMsg: {
if (debugLevel == QtInfoMsg || debugLevel == QtDebugMsg) {
snprintf(buf, sizeof(buf)-1, "%s DEBG [%s:%s:%04u] %s",
datetime.toStdString().c_str(),
function.toStdString().c_str(),
fileName.toStdString().c_str(),
context.line,
localMsg.constData());
fprintf(stderr, "%s\n", buf);
case LOG_NOTICE: { // normal, but significant, condition
if (type != QtDebugMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
case QtWarningMsg: {
if (debugLevel == QtInfoMsg || debugLevel == QtDebugMsg || debugLevel == QtWarningMsg) {
snprintf(buf, sizeof(buf)-1, "%s DEBG [%s:%s:%04u] %s",
datetime.toStdString().c_str(),
function.toStdString().c_str(),
fileName.toStdString().c_str(),
context.line,
localMsg.constData());
fprintf(stderr, "%s\n", buf);
case LOG_WARNING: { // warning conditions
if (type != QtInfoMsg && type != QtDebugMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
case QtCriticalMsg: {
if (debugLevel == QtInfoMsg || debugLevel == QtDebugMsg
|| debugLevel == QtWarningMsg || debugLevel == QtCriticalMsg) {
snprintf(buf, sizeof(buf)-1, "%s DEBG [%s:%s:%04u] %s",
datetime.toStdString().c_str(),
function.toStdString().c_str(),
fileName.toStdString().c_str(),
context.line,
localMsg.constData());
fprintf(stderr, "%s\n", buf);
case LOG_ERR: { // error conditions
if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
case QtFatalMsg: {
if (debugLevel == QtInfoMsg || debugLevel == QtDebugMsg
|| debugLevel == QtWarningMsg || debugLevel == QtCriticalMsg
|| debugLevel == QtFatalMsg) {
snprintf(buf, sizeof(buf)-1, "%s DEBG [%s:%s:%04u] %s",
datetime.toStdString().c_str(),
function.toStdString().c_str(),
fileName.toStdString().c_str(),
context.line,
localMsg.constData());
fprintf(stderr, "%s\n", buf);
case LOG_CRIT: { // critical conditions
if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
case LOG_ALERT: { // action must be taken immediately
if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
case LOG_EMERG: { // system is unusable
if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
default: {
fprintf(stderr, "%s No ErrorLevel defined! %s\n",
datetime.toStdString().c_str(), msg.toStdString().c_str());
//fprintf(stderr, "%s No ErrorLevel defined! %s\n",
// datetime.toStdString().c_str(), msg.toStdString().c_str());
}
}
}

View File

@@ -2,9 +2,12 @@
#define MESSAGE_HANDLER_H_INCLUDED
#include <QtGlobal>
#ifdef __linux__
#include <syslog.h>
#endif
QtMsgType getDebugLevel();
void setDebugLevel(QtMsgType newDebugLevel);
int getDebugLevel();
void setDebugLevel(int newDebugLevel);
bool messageHandlerInstalled();
QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler);

190
plugins/interfaces.h Normal file → Executable file
View File

@@ -3,7 +3,7 @@
#include <QtPlugin>
#define THIS_IS_CA_MASTER
struct T_emp
{
@@ -128,28 +128,24 @@ struct T_vaultRecord
uint32_t AbsReserve;
uint32_t AbsNrOfCuts;
//16
char label3buffer[4]; // mw >
// Verkauf, T<EFBFBD>r zu:
uint32_t VKcoinsInserted[16]; // nur f<EFBFBD>r Wechsler, soviel wurde eingeworfen
uint32_t VKcoinsReturned[6]; // nur f<EFBFBD>r Wechsler, Anzahl M<EFBFBD>nzen pro Typ, soviel wurde zur<EFBFBD>ckgegeben
//88
// Verkauf, Tuer zu:
uint32_t VKcoinsInserted[16]; // nur fuer Wechsler, soviel wurde eingeworfen
uint32_t VKcoinsReturned[6]; // nur fuer Wechsler, Anzahl Muenzen pro Typ, soviel wurde zurueckgegeben
// Service, T<EFBFBD>r offen:
uint16_t ServCoinsInserted[16]; // nur f<EFBFBD>r Wechsler, soviel wurde eingeworfen
uint16_t ServCoinsReturned[6]; // nur f<EFBFBD>r Wechsler, Anzahl M<EFBFBD>nzen pro Typ, soviel wurde zur<EFBFBD>ckgegeben
// Service, Tuer offen:
uint16_t ServCoinsInserted[16]; // nur fuer Wechsler, soviel wurde eingeworfen
uint16_t ServCoinsReturned[6]; // nur fuer Wechsler, Anzahl Muenzen pro Typ, soviel wurde zurueckgegeben
uint16_t resint3;
uint16_t resint4;
uint16_t currentTubeContent[6]; // nur f<EFBFBD>r Wechsler, aktueller F<EFBFBD>llstand
uint16_t currentTubeContent[6]; // nur fuer Wechsler, aktueller Fuellstand
uint16_t resint5;
uint16_t resint6;
// 56
char label4buffer[4]; // box>
uint16_t coinsInVault[16];
uint16_t billsInStacker[8];
// 48
char label5buffer[4]; // val>
// actually constant unless exchange rate is changed
@@ -159,11 +155,17 @@ struct T_vaultRecord
uint16_t exchangeRate;
uint16_t resint9;
// 64
// new from 1.8.23
uint32_t cutsSinceCBchange;
uint32_t CBcontent_cent;
uint32_t CBnrofCoins;
char endofblock[4]; // end>
char endofblock[4]; // end
// 332 bytes
// 316 byte Block im Speicher
uint16_t CRC16; // Xmodem16 from startbuffer[0] to endofblock[3]
uint16_t resint11;
char endofRecord[4]; // ----
};
@@ -191,7 +193,7 @@ struct T_moduleCondition
uint8_t coinChecker; // EMP, OMP or mei-cashflow
uint8_t coinEscrow;
uint8_t mifareReader;
uint8_t mifareReader; // 0: unknown 1=OK 200=no response 201=wrong response 202: Reader reports HW-error
uint8_t creditTerm;
uint8_t coinReject;
@@ -201,7 +203,7 @@ struct T_moduleCondition
uint8_t temper;
uint8_t poweronTest;
uint8_t doorState; // 1: alles zu 200: t?r offen + bit1(S) +bit2(CB) + bit3(CB)
uint8_t doorState; // 1: alles zu 200: tuer offen + bit1(S) +bit2(CB) + bit3(CB)
uint8_t doorWasOpened; // 1: all doors are closed 200: any door was just opened
uint8_t changer; // can only be tested by usage
@@ -315,9 +317,10 @@ struct T_devices
// set by master, used(1) or notused (0) or type 2....20
UCHAR kindOfPrinter; // 0:off 1:Gebe
UCHAR kindOfCoinChecker; // 0: without 1=EMP820 2=EMP900 3=currenza c<EFBFBD> (MW)
UCHAR kindOfCoinChecker; // 0: without 1=EMP820 2=EMP900 3=currenza Csquare (MW)
UCHAR kindOfMifareReader; // by now only stronglink SL025 =1
UCHAR suppressSleepMode; // 0:sleep allowed 1: no sleep
UCHAR solarPower; // 1:sleep allowed 0: no sleep
//UCHAR suppressSleepMode; // 0:sleep allowed 1: no sleep
UCHAR kindOfModem; // 0:off 1:Sunlink
UCHAR kindOfCreditcard; // 0:off 1:Feig NFC
@@ -336,6 +339,8 @@ struct T_devices
UINT VaultFullWarnLevel;
UINT VaultFullErrorLevel;
UINT BattEmptyWarnLevel;
UINT BattEmptyErrorLevel;
};
@@ -357,7 +362,7 @@ public:
// Furthermore the Cashagent-Library answers with status strings about sending and reading result
// $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
#ifdef THIS_IS_CA_MASTER
virtual bool dc_openSerial(int BaudNr, QString BaudStr, QString ComName, uint8_t connect) const =0;
// Command: open serial interface
// BaudNr: 0:1200 1:9600 2:19200 3:38400 4:57600 5:115200
@@ -368,7 +373,7 @@ public:
virtual void dc_closeSerial(void) const =0;
// Command: close serial interface in order to save power while power down
// or if another port must be used
#endif
virtual bool dc_isPortOpen(void) const =0;
// returns true if port open (don't send unless open. Sending to closed port will crash program)
@@ -394,7 +399,7 @@ public:
// get data back in "payLoad", max 64 byte, can be used for diagnosis
// retval = nr of bytes received. If host buffer too small then
// only plBufSiz bytes are copied to "payLoad"
// plBufSiz<EFBFBD>z=size of host buffer
// plBufSiz = size of host buffer
virtual void dc_setWakeFrequency(uint8_t period) const =0;
// RTC wakes DC2 (and PTU) by hardware signal every 32seconds
@@ -403,7 +408,7 @@ public:
virtual void dc_OrderToReset(void) const =0;
// want DC2 to reset (in order to start Bootloader)
#ifdef THIS_IS_CA_MASTER
virtual QString dc_getSerialState(void) const =0;
// get result of opening-command like "ttyS0 opened with 115200 8N1!
// or error messages like "comport not available..."
@@ -411,7 +416,7 @@ public:
virtual void dc_clrSerialStateText(void) const =0;
// clear above text to avoid multiple repetive displaying
#endif
virtual void bl_sendDataDirectly(uint8_t length, uint8_t *buf) const =0;
// send without protocol frame, needed for the DC bootloader
@@ -496,10 +501,10 @@ public:
// Analog values:
virtual uint32_t dc_getTemperature(void) const =0;
// in Sax-Format 0...400 (0=-50,0<EFBFBD>C 100=0,0<EFBFBD>C 141=20,5<EFBFBD>C 400=150,0<EFBFBD>C)
// in Sax-Format 0...400 (0=-50,0degC 100=0,0degC 141=20,5degC 400=150,0degC)
virtual QString dc_getTemperaturStr(void) const =0;
// as string like "-12,5<EFBFBD>C"
// as string like "-12,5degC"
virtual uint32_t dc_getVoltage(void) const =0;
// as value in mV, 0...65,535V
@@ -760,7 +765,7 @@ public:
uint8_t kindOfModem, uint8_t kindOfCredit ) const =0;
// enable hardware in device controller:
// kindOfPrinter: 0:off 1: GPT4672 (only this one implemented)
// kindOfCoinChecker: 0:off 1:EMP820 2:EMP900 3: C<EFBFBD>_changer
// kindOfCoinChecker: 0:off 1:EMP820 2:EMP900 3: Csquare_changer
// kindOfMifareReader: 0:off 1: SL025 (only this one implemented)
// suppressSleep: 0:sleep allowed 1: sleep surpressed for special reason
// kindOfModem: 0:off 1: ATB_Sunlink_LTE (not yet implemented)
@@ -813,7 +818,7 @@ public:
// --------------------------------------------- MIFARE -----------------------------------------------------
// ----------------------------------------------------------------------------------------------------------
// obsolete
virtual uint8_t mif_returnReaderStateAndCardType(uint8_t *buf, uint8_t maxBufferSize) const =0;
// retval 0=OK 1=error host buffer too small
/* data description, new fast version:
@@ -835,13 +840,16 @@ public:
virtual bool mif_readerIsOK(void) const =0;
virtual bool mif_cardAttached(void) const =0;
// not working! use mif_cardIsAttached() instead
virtual uint8_t mif_readResult(void) const =0;
// result: 0: unknown or still in progress
// 1: card read successful
// 2: reading error
// not working!
virtual QString mif_cardUID(void) const =0;
// not working
virtual uint8_t mif_getCardDataDec(uint8_t blkNr, uint8_t *buf, uint8_t maxBufferSize) const =0;
@@ -886,7 +894,7 @@ public:
// send 5 byte: byte 0,1: speed 5...250 mm/s
// byte2: density 0....(25)....50
// byte3: alignment 'l', 'c', 'r' = left, center, right
// byte4: orientation 0, 90, 180 = 0<EFBFBD>, 90<EFBFBD>, 180<EFBFBD> rotation (by now not supported!)
// byte4: orientation 0, 90, 180 = 0deg, 90deg, 180deg rotation (by now not supported!)
// not batched! don't use twice within 100ms
virtual void prn_movePaper(uint8_t wayInMm, uint8_t direction) const =0;
@@ -1018,7 +1026,7 @@ public:
// use for changer
#ifdef THIS_IS_CA_MASTER
virtual QString dc_getTxt4RsDiagWin(void) const =0;
virtual void dc_clrTxt4RsDiagWin(void) const =0;
virtual QString dc_get2ndTxt4RsDiagWin(void) const =0;
@@ -1033,7 +1041,6 @@ public:
virtual void dc_clrTxt4dataStateLine(void) const =0;
virtual QString dc_getdatifLine(void) const =0;
virtual void dc_clrTxt4datifLine(void) const =0;
#endif
@@ -1152,14 +1159,13 @@ public:
// to be forwarded to Ismas
virtual bool prn_printAccountReceipt(void) const =0;
// print all 8 backuped accounting receipts
// return true if sending to DC OK, false if cmd-stack is full
virtual bool prn_printTestTicket(void) const =0;
// return true if sending to DC OK, false if cmd-stack is full
virtual bool cash_startPayment(uint32_t amount) const =0;
// 17.4.23TS: extended to 32bit
@@ -1178,7 +1184,7 @@ public:
virtual uint16_t getLastInsertedCoin(void) const =0;
virtual bool getAllInsertedCoins(uint16_t *types, uint16_t *values) const =0;
// alle bei diesem Verkauf eingeworfenen M<EFBFBD>nzen sind gespeichert, max 64
// alle bei diesem Verkauf eingeworfenen Muenzen sind gespeichert, max 64
virtual bool cash_cancelPayment(void) const =0;
@@ -1232,6 +1238,8 @@ public:
virtual uint8_t prn_getPrintResult() const =0;
virtual uint8_t prn_getCurrentPrinterState() const =0;
// 0: printer OK
// bit0: near paper end bit1: no paper
@@ -1244,6 +1252,8 @@ public:
virtual void sys_sendDeviceParameter(struct T_devices *deviceSettings) const =0;
virtual void sys_restoreDeviceParameter(struct T_devices *deviceSettings) const =0;
// attention: only applies if function "sys_sendDeviceParameter()" was used to send this settings before
// cannot be used to see settings programmed by JsonFile
virtual bool sys_areDCdataValid(void) const =0;
@@ -1268,6 +1278,103 @@ public:
virtual bool dc_isAutoRequestOn(void) const =0;
virtual uint16_t log_getLatestAccountNumber(void) const=0;
// new function 27.6.2023
// latest = highest of the backup's
virtual uint8_t log_getAvailableVaultBlocks(void) const=0;
// return 0x0011 1111 if all 6 blocks are loaded (one bit per block)
virtual uint8_t log_getAnswerToLastSlaveRequest(void) const =0;
// use only for ONE request/command
// return: 0xFF: result unknown by now as sending is ongoing
// 0=OK
// 1= wrong length 2=wrong start sign 5= wrong crc
// 6= slave: master cmd was wrong 7: slave: could not write/read data
// 8=timeout, got no response from slave
// 0,8 work, 1..6 not yet tested. 8 comes immed. and stays 8 until reconnect
// use for important and extended commands (print several templates, print ticket...)
virtual void log_startSupervision(void) const =0;
virtual uint8_t log_getAnswerToLastCmdBatch(void) const =0;
// 0xFF: no command sent by now
// 0: started, in progress
// 1: done and OK
// 2: done and error
// not working properly, always 0
virtual bool log_getVaultData(uint8_t *data) const =0;
// get vault record in linear 8bit buffer with 384 byte
// new from 1.8.23
virtual bool prn_printOneAccountReceipt(uint16_t accountNr) const =0;
// print one out of eight stored last accounting receipts
// function log_getHoldAccountNumbers() gives a list of acc-Nr. of the stored receipts
virtual bool prn_printAllAvailAccountReceipts(void) const =0;
// same as: prn_printAccountReceipt() from line 1153
// return true if sending to DC OK, false if cmd-stack is full
virtual bool log_verifyVaultRecordByCrc(void) const =0;
// return true if CRC16 is correct, data are 100% OK. Security level 1:65536
// verification is strongly recommended before further processing
// in case of "false"-result please reload from DC
virtual uint16_t log_DC_getNextAccountNumber(void) const=0;
// the current cash box content will be backuped with this number on next cashbox-change
virtual void log_DC_setNextAccountNumber(uint16_t newAccountingNumber) const=0;
// the current cash box content will be backuped with this number on next cashbox-change
// use only in case of hardware replacements or errors which derailed the number
virtual void log_DC_deleteAllVaultrecordsInDc(void) const=0;
// use only in case of hardware replacements or errors which derailed the number
virtual void log_DC_deleteAllTotalCounters(void) const=0;
// use only in case of hardware replacements or errors which derailed the number
virtual void dc_setNewCustomerNumber(uint16_t newCustNr) const =0;
virtual void dc_setNewMachineNumber(uint16_t newMachNr) const =0;
virtual void dc_setNewBorough(uint16_t newBorough) const =0;
virtual void dc_setNewZone(uint16_t newZone) const =0;
// new functions from 8.9.23
virtual QString mif_getReaderType(void) const =0;
// return "SL025" if correct reader is connected
virtual void mif_getCardSize(uint8_t *cardSize, uint8_t *idLeng) const =0;
// cardSize=1k or 4kByte
// idLeng =4Byte or 7 byte
virtual char mif_getAtbCardData(uint8_t *buf, uint8_t maxBuffSiz) const =0;
// return complete buffer binary, just for test purpose
virtual bool mif_isValidAtbCard(void) const =0;
virtual uint32_t mif_getAtbCardCuNu(void) const =0;
virtual uint8_t mif_getAtbCardTyp(void) const =0;
// return 1=upper door card 1=lower door 3=printer-test 4=coin-test
// 0: not a valid atb2020 card
virtual QString mif_getAtbCardPerso(void) const =0;
// e.g. "PNsax001" used for personal number, name shortcode, card number
// free to use, can be set in AtbMcw23.exe tool
virtual void mif_getAtbCardExpire(uint8_t *year, uint8_t *month, uint8_t *day, uint8_t *hour, uint8_t *minute) const =0;
@@ -1326,6 +1433,10 @@ signals:
// 15.06.2023 V4.2 bring into same order as hwapi in order to set the THIS_IS_CA_MASTER correct
// 19.06.2023 V4.3 added some qCriticals to see emits
// 01.08.2023 V4.4 some new values at the end of struct T_vaultRecord
// two more values in struct T_devices
// 7 new functions at the end of the file
//#define HWINF_iid "Atb.Psa2020.software.HWapi/3.1"
//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/3.1"
@@ -1336,9 +1447,16 @@ signals:
//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.0"
//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.1"
//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.2"
#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.3"
//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.3"
//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.4"
// 8.9.2023 two new functions (end of file) for mifare test
//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.5"
// 18.9.2023 major improvements for DC data exchange
// verification of door and cash box signals
// intensive verification of Json-Programming Master-Slave (PTU to DC), 100% ok
#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.6"
// 20.9.2023: speeding up door and cash box signals
Q_DECLARE_INTERFACE(hwinf, HWINF_iid)

BIN
plugins/libCAmaster.so Normal file → Executable file

Binary file not shown.

View File

@@ -2,13 +2,15 @@
#include <QProcess>
#include <QDebug>
#include <QDir>
#include <QRegularExpression>
Command::Command(QString const &command, int start_timeout, int finish_timeout)
: m_command(command.trimmed())
, m_commandResult("")
, m_waitForStartTimeout(start_timeout)
, m_waitForFinishTimeout(finish_timeout) {
, m_waitForFinishTimeout(finish_timeout)
, m_exitCode(-1) {
}
QString Command::getCommandResult() const {
@@ -39,41 +41,71 @@ void Command::finished(int /*exitCode*/, QProcess::ExitStatus /*exitStatus*/) {
}
bool Command::execute(QString workingDirectory, QStringList args) {
if (!QDir::setCurrent(workingDirectory)) {
qCritical() << "SET WORKING_DIRECTORY" << workingDirectory
<< "FAILED FOR" << m_command;
return false;
}
QScopedPointer<QProcess> p(new QProcess(this));
p->setWorkingDirectory(workingDirectory);
p->setProcessChannelMode(QProcess::MergedChannels);
connect(&(*p), SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOutput()));
connect(&(*p), SIGNAL(readyReadStandardError()), this, SLOT(readyReadStandardError()));
qCritical() << "START COMMAND" << m_command << "IN" << workingDirectory;
p->setWorkingDirectory(workingDirectory);
if (!args.isEmpty()) {
qDebug() << "START COMMAND" << m_command << "WITH ARGS" << args
<< "IN" << p->workingDirectory();
p->start(m_command, args);
} else {
qDebug() << "START COMMAND" << m_command
<< "IN" << p->workingDirectory();
p->start(m_command);
}
if (p->waitForStarted(m_waitForStartTimeout)) {
qDebug() << "PROCESS" << m_command << "STARTED IN" << p->workingDirectory();
if (p->state() == QProcess::ProcessState::Running) {
if (p->waitForFinished(m_waitForFinishTimeout)) {
qDebug() << "PROCESS" << m_command << "RUNNING IN" << p->workingDirectory();
// wait forever for git/opkg-commands to finish
int wait = m_waitForFinishTimeout;
if (m_command.trimmed().startsWith("git", Qt::CaseInsensitive) ||
m_command.trimmed().startsWith("opkg", Qt::CaseInsensitive)) {
wait = -1;
}
bool const no_timeout = p->waitForFinished(wait);
if (no_timeout) {
qDebug() << "PROCESS" << m_command << "FINISHED IN" << p->workingDirectory();
if (p->exitStatus() == QProcess::NormalExit) {
qInfo() << "EXECUTED" << m_command
<< "with code" << p->exitCode();
// qInfo() << "RESULT" << m_commandResult;
if ((m_exitCode = p->exitCode()) == 0) {
qDebug() << "EXECUTED" << m_command
<< "with code" << m_exitCode
<< "IN" << p->workingDirectory();
return true;
} else {
qCritical() << "EXECUTED" << m_command
<< "with code" << m_exitCode
<< "IN" << p->workingDirectory();
}
} else {
qCritical() << "PROCESS" << m_command << "CRASHED with code"
<< p->exitCode();
<< p->exitCode()
<< "IN" << p->workingDirectory();
}
} else {
qCritical() << "PROCESS" << m_command << "DID NOT FINISH";
qCritical() << "PROCESS" << m_command
<< "DID NOT FINISH WITH" << wait
<< "MS IN" << p->workingDirectory();
}
} else {
qCritical() << "WRONG PROCESS STATE" << p->state();
qCritical() << "WRONG PROCESS STATE" << p->state()
<< "IN" << p->workingDirectory();
}
} else {
qCritical() << "PROCESS" << m_command << "TIMEOUT AT START";
qCritical() << "PROCESS" << m_command << "TIMEOUT AT START"
<< "IN" << p->workingDirectory();
}
return false;
}

View File

@@ -16,15 +16,17 @@ class Command : public QObject {
QString m_commandResult;
int m_waitForStartTimeout;
int m_waitForFinishTimeout;
int m_exitCode;
public:
explicit Command(QString const &command,
int start_timeout = 100000,
int finish_timeout = 100000);
QString getCommandResult() const;
QString command() const { return m_command; }
bool execute(QString workingDirectory, QStringList args = QStringList());
int exitCode() const { return m_exitCode; }
private slots:
void readyReadStandardOutput();

20
progress_event.cpp Normal file
View File

@@ -0,0 +1,20 @@
#include "progress_event.h"
QEvent::Type ProgressEvent::customEventType = QEvent::None;
ProgressEvent::ProgressEvent(QObject const *sender, int progressPercent)
: QEvent(ProgressEvent::type())
, m_sender(sender)
, m_progressPercent(progressPercent) {
}
ProgressEvent::~ProgressEvent() {
}
QEvent::Type ProgressEvent::type() {
if (customEventType == QEvent::None) {
int generatedType = QEvent::registerEventType();
customEventType = static_cast<QEvent::Type>(generatedType);
}
return customEventType;
}

26
progress_event.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef PROGRESS_EVENT_H_INCLUDED
#define PROGRESS_EVENT_H_INCLUDED
#include <QEvent>
class ProgressEvent : public QEvent {
QObject const *m_sender;
int m_progressPercent;
public:
explicit ProgressEvent(QObject const *sender, int progressPercent);
virtual ~ProgressEvent();
static QEvent::Type type();
QObject const *sender() { return m_sender; }
QObject const *sender() const { return m_sender; }
void setProgress(int progressPercent) { m_progressPercent = progressPercent; }
int progressPercent() { return m_progressPercent; }
int progressPercent() const { return m_progressPercent; }
private:
static QEvent::Type customEventType;
};
#endif // PROGRESS_EVENT_H_INCLUDED

View File

@@ -1,4 +1,8 @@
#include "update.h"
#include "worker.h"
#include "utils.h"
#include "update_dc_event.h"
#include "mainwindow.h"
#include <QCoreApplication>
#include <QApplication>
@@ -7,10 +11,12 @@
#include <QDebug>
#include <QTextStream>
#include <QRegularExpression>
#include <QRegExp>
#include <QApplication>
//#include <iostream>
//#include <fstream>
//#include <ctime>
#if defined (Q_OS_UNIX) || defined (Q_OS_LINUX)
#include "unistd.h"
#endif
#include "plugins/interfaces.h"
@@ -22,23 +28,16 @@
#include <QPluginLoader>
#include <QMap>
#define COLUMN_REQUEST (0)
#define COLUMN_NAME (1)
#define COLUMN_DATE_TIME (2)
#define COLUMN_RESULT (3)
#define UPDATE_OPKG (1)
#define UPDATE_DC (1)
#define UPDATE_PRINTER_TEMPLATES (1)
#define UPDATE_CASH_TEMPLATE (0)
#define UPDATE_CONF_TEMPLATE (0)
#define UPDATE_DEVICE_TEMPLATE (0)
#define UPDATE_DC (0)
static const QMap<QString, int> baudrateMap = {
{"1200" , 0}, {"9600" , 1}, {"19200" , 2}, {"38400" , 3},
{"57600" , 4}, {"115200" , 5}
};
QPluginLoader Update::pluginLoader;
hwinf *Update::loadDCPlugin(QDir const &plugInDir, QString const &fname) {
hwinf *hw = nullptr;
if (plugInDir.exists()) {
@@ -47,7 +46,8 @@ hwinf *Update::loadDCPlugin(QDir const &plugInDir, QString const &fname) {
QFileInfo info(pluginLibName);
if (info.exists()) {
pluginLibName = plugInDir.absoluteFilePath(pluginLibName);
static QPluginLoader pluginLoader(pluginLibName);
pluginLoader.setFileName(pluginLibName);
// static QPluginLoader pluginLoader(pluginLibName);
if (!pluginLoader.load()) {
qCritical() << "in directory" << plugInDir.absolutePath();
qCritical() << "cannot load plugin" << pluginLoader.fileName();
@@ -79,135 +79,54 @@ hwinf *Update::loadDCPlugin(QDir const &plugInDir, QString const &fname) {
return hw;
}
bool Update::unloadDCPlugin() {
if (pluginLoader.unload()) {
qCritical() << "unloaded plugin" << pluginLoader.fileName();
// Note: will re-instantiate the library !
// QObject *rootObject = pluginLoader.instance();
// if (rootObject) {
// qCritical() << "reloaded plugin: root object again available";
// return false;
// }
// qCritical()unloaded plugin: root object gone";
return true;
}
return false;
}
Update::Update(hwinf *hw,
QString update_ctrl_file,
QString repositoryPath,
QString customerId,
Worker *worker,
QString customerRepository,
QString customerNrStr,
QString branchName,
QString pluginName,
QString workingDir,
bool maintenanceMode,
bool testMode,
bool executeScriptOnly,
bool dryRun,
QObject *parent,
char const *serialInterface,
char const *baudrate)
: QObject(parent)
, m_hw(hw)
, m_worker(worker)
, m_serialInterface(serialInterface)
, m_baudrate(baudrate)
, m_update_ctrl_file(update_ctrl_file)
, m_update_ctrl_file_copy(update_ctrl_file + ".copy")
, m_repositoryPath(repositoryPath)
, m_customerId(customerId)
, m_customerRepository(customerRepository)
, m_customerNrStr(customerNrStr)
, m_branchName(branchName)
, m_pluginName(pluginName)
, m_workingDir(workingDir)
, m_maintenanceMode(maintenanceMode)
, m_testMode(testMode)
, m_executeScriptOnly(executeScriptOnly)
, m_dryRun(dryRun)
//, m_apismClient(nullptr, nullptr, nullptr)
, m_init(true) {
, m_dryRun(dryRun) {
// m_apismClient.sendSelfTest();
if (!m_testMode) {
// make sure the files are empty
if (m_update_ctrl_file.exists()) {
if (m_update_ctrl_file.open(QIODevice::ReadWrite |
QIODevice::Truncate |
QIODevice::Text)) {
m_update_ctrl_file.close();
}
} else {
qCritical() << "Update-file" << m_update_ctrl_file.fileName()
<< "does not exist";
m_init = false;
}
if (m_update_ctrl_file_copy.exists()) {
if (m_update_ctrl_file_copy.open(QIODevice::ReadWrite |
QIODevice::Truncate |
QIODevice::Text)) {
m_update_ctrl_file_copy.close();
}
} else {
qCritical() << "Update-file-copy" << m_update_ctrl_file_copy.fileName()
<< "does not exist";
m_init = false;
}
}
// execute update_psa-script
if (m_init) {
if (!m_testMode) {
if ((m_init = execUpdateScript()) == false) {
qCritical() << "UPDATE_SCRIPT FAILED";
}
}
if (m_init) {
if (!m_update_ctrl_file.open(QIODevice::ReadWrite | QIODevice::Text)) {
qCritical() << "CAN NOT OPEN" << m_update_ctrl_file.fileName();
m_init = false;
} else {
qDebug() << "OPENED" << m_update_ctrl_file.fileName();
}
if (!m_update_ctrl_file_copy.open(QIODevice::ReadWrite | QIODevice::Text)) {
qCritical() << "CAN NOT OPEN" << m_update_ctrl_file_copy.fileName();
m_init = false;
} else {
qDebug() << "OPENED" << m_update_ctrl_file_copy.fileName();
}
}
}
qInfo() << "UPDATE: m_serialInterface ..." << m_serialInterface;
qInfo() << "UPDATE: m_baudrate ..." << m_baudrate;
qInfo() << "UPDATE: m_customerRepository ..." << m_customerRepository;
qInfo() << "UPDATE: m_customerNr ..........." << m_customerNrStr;
qInfo() << "UPDATE: m_branchName ..........." << m_branchName;
qInfo() << "UPDATE: m_pluginName ..........." << m_pluginName;
qInfo() << "UPDATE: m_workingDirectory ....." << m_workingDir;
}
Update::~Update() {
// make sure the files are closed
m_update_ctrl_file.close();
m_update_ctrl_file_copy.close();
}
bool Update::execUpdateScript() {
// path of update-script 'update_psa'
QString update_psa("/opt/app/tools/atbupdate/update_psa ");
if (m_maintenanceMode) {
update_psa += " -m ";
}
update_psa += " --wdir ";
update_psa += m_workingDir;
qCritical() << "update_psa: " << update_psa;
QScopedPointer<QProcess> p(new QProcess(this));
p->setProcessChannelMode(QProcess::MergedChannels);
connect(&(*p), SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOutput()));
connect(&(*p), SIGNAL(readyReadStandardError()), this, SLOT(readyReadStandardError()));
p->start(update_psa);
if (p->waitForStarted(1000)) {
if (p->state() == QProcess::ProcessState::Running) {
// maintenance_mode: update_psa script enters an infinite loop
int const timeout = (m_maintenanceMode ? 200000: -1);
if (p->waitForFinished(timeout)) {
QString output = p->readAllStandardOutput().toStdString().c_str();
QStringList lst = output.split('\n');
for (int i = 0; i < lst.size(); ++i) {
qDebug() << lst[i];
}
if (p->exitStatus() == QProcess::NormalExit) {
qInfo() << "EXECUTED" << update_psa
<< "with code" << p->exitCode();
return (p->exitCode() == 0);
}
} else {
qCritical() << "update-script TIMEDOUT after"
<< timeout/1000 << "seconds";
}
}
}
return false;
}
Update::DownloadResult Update::sendStatus(int ret) const {
@@ -330,48 +249,99 @@ Update::DownloadResult Update::dc_downloadBinary(QByteArray const &b) const {
return res;
}
bool Update::startBootloader() const {
qDebug() << "starting bootloader...";
int nTry = 5;
while (--nTry >= 0) {
bool Update::startBootloader() const { // deprecated
return false;
#if 0
int nStartTry = 5;
while (--nStartTry >= 0) {
m_hw->bl_startBL();
QThread::msleep(5000);
QThread::msleep(500);
int nCheckTry = 10;
while (--nCheckTry >= 0) {
m_hw->bl_checkBL();
QThread::msleep(500);
if (m_hw->bl_isUp()) {
qInfo() << "starting bootloader...OK";
QThread::msleep(5000);
return true;
} else {
qCritical() << "bootloader not up (" << nTry << ")";
qCritical() << "bootloader not up ("
<< nStartTry << "," << nCheckTry << ")" << QThread::currentThread();
}
}
qCritical() << "starting bootloader...FAILED";
}
qCritical() << "starting bootloader...FAILED" << QThread::currentThread();
return false;
#endif
}
bool Update::stopBootloader() const {
qDebug() << "stopping bootloader...";
int nTry = 5;
while (--nTry >= 0) {
m_hw->bl_stopBL();
// stop bootloader: this MUST work -> otherwise the PSA has to be restarted
// manually
emit m_worker->showErrorMessage("dc update", "stopping bootloader...");
int nTryFinalize = 1; // could do this in an endless loop
do {
// in principle, any value except BL_STOP will do, as we want to detect
// change to BL_STOP
m_worker->mainWindow()->setUpdateStep(UpdateDcEvent::UpdateStep::BL_CHECK);
QApplication::postEvent(
m_worker->mainWindow(),
new UpdateDcEvent(m_worker, UpdateDcEvent::UpdateStep::BL_STOP, nTryFinalize));
QThread::sleep(1);
int const cntLimit = 20;
int cnt = 0;
while (++cnt < cntLimit &&
m_worker->mainWindow()->updateStep() != UpdateDcEvent::UpdateStep::BL_STOP) {
// wait until bl_stopBL() has been sent
QThread::msleep(500);
if (!m_hw->bl_isUp()) {
qInfo() << "stopping bootloader...OK";
return true;
}
QApplication::postEvent(
m_worker->mainWindow(),
new UpdateDcEvent(m_worker, UpdateDcEvent::UpdateStep::BL_CHECK, nTryFinalize));
QThread::sleep(1);
QApplication::postEvent(
m_worker->mainWindow(),
new UpdateDcEvent(m_worker, UpdateDcEvent::UpdateStep::BL_IS_UP, nTryFinalize));
QThread::sleep(1);
cnt = 0;
while (++cnt < cntLimit &&
m_worker->mainWindow()->updateStep() != UpdateDcEvent::UpdateStep::BL_IS_DOWN) {
// wait until done
QThread::msleep(200);
}
qCritical() << "stopping bootloader...FAILED";
return false;
} while (++nTryFinalize <= MainWindow::BL_STOP_COUNT &&
m_worker->mainWindow()->updateStep() != UpdateDcEvent::UpdateStep::BL_IS_DOWN);
return (m_worker->mainWindow()->updateStep() == UpdateDcEvent::UpdateStep::BL_IS_DOWN);
}
// br is a index into a table, used for historical reasons.
bool Update::openSerial(int br, QString baudrate, QString comPort) const {
qDebug() << "opening serial" << br << baudrate << comPort << "...";
if (m_hw->dc_openSerial(br, baudrate, comPort, 1)) { // 1 for connect
qInfo() << "opening serial" << br << baudrate << comPort << "...OK";
if (m_hw->dc_openSerial(br, baudrate, comPort, 1) == true) { // 1 for connect
Utils::printInfoMsg(
QString("OPENING SERIAL %1").arg(br)
+ " " + baudrate + " " + comPort + "...OK");
// m_hw->dc_autoRequest(true);
m_hw->dc_autoRequest(false);
QThread::sleep(1);
Utils::printInfoMsg(QString("IS PORT OPEN %1").arg(m_hw->dc_isPortOpen()));
return true;
}
qCritical() << "opening serial" << br << baudrate << comPort << "...FAILED";
Utils::printCriticalErrorMsg(
QString("OPENING SERIAL %1").arg(br)
+ " " + baudrate + " " + comPort + "...FAILED");
return false;
}
@@ -384,13 +354,15 @@ bool Update::isSerialOpen() const {
return m_hw->dc_isPortOpen();
}
bool Update::resetDeviceController() const {
bool Update::resetDeviceController() const { // deprecated
return false;
#if 0
qDebug() << "resetting device controller...";
m_hw->bl_rebootDC();
// wait maximally 3 seconds, before starting bootloader
QThread::sleep(1);
qInfo() << "resetting device controller...OK";
return true;
#endif
}
QByteArray Update::loadBinaryDCFile(QString filename) const {
@@ -428,17 +400,61 @@ bool Update::downloadBinaryToDC(QString const &bFile) const {
}
/*
Using the DC bootloader:
1 : bl_reboot() // send to application, want DC2 to reset (in order to start
// the bootloader)
2 : bl_startBL(): // send within 4s after DC poewer-on, otherwise bl is left
3 : bl_check(): // send command to verify if bl is up
4 : bl_isUp(): // returns true if bl is up and running
///////////////////////////////////////////////////////////////////////////////
//
// USING THE DC BOOTLOADER
//
///////////////////////////////////////////////////////////////////////////////
1 : bl_reboot() // send to application, want DC2 to reset (in order to
// start the bootloader)
//
// NOTE: this function is NOT reliable !!! Sometimes it
// simply does not work, in which case bl_startBL,
// bl_checkBL and bl_isUp do not work as well.
// Alas, there is no feedback if bl_reboot worked!
//
// NOTE: this function can be called only once per
// minute, because once called again, the controller
// performs some self-checks consuming some time.
//
// NOTE: after a successful bl_reboot(), the device is
// waiting about 4 seconds in the bootloader. To stay in
// the bootloader, we have to send the command
// bl_startBL(), which is kind of a misnomer, as it
// should be bl_doNotLeaveBL().
//
2 : bl_startBL(): // send within 4s after DC power-on, otherwise
// bootloader is left.
//
// NOTE: a running bootloader is a MUST for the download
// process of a device controller firmware as it does
// the actual writing of the memory (the bl_reboot()
// from above erases the available memory).
//
3 : bl_check(): // send command to verify if bl is up
//
// NOTE: this command is kind of a request that we want
// to check if the bootloader is up. The device
// (actually the bootloader) responds with its version.
//
4 : bl_isUp(): // returns true if bl is up and running
//
// NOTE: we know what the bootloader version actually is
// as the bootloader does not change. By comparing the
// string received in the previous step with this known
// version string we know if the bootloader is up.
//
// NOTE FOR ALL PREVIOUS STEPS: execute them in their
// own slots each to be sure to receive any possible
// responds from the device.
//
5 : bl_sendAddress(blockNumber)
// send start address, nr of 64-byte block, start with 0
// will be sent only for following block-numbers:
// 0, 1024, 2048, 3072 and 4096, so basically every 64kByte
// 0, 1024, 2048, 3072 and 4096, so basically every
// 64kByte.
// for other addresses nothing happens
6 : bl_wasSendingAddOK()
@@ -458,50 +474,119 @@ bool Update::downloadBinaryToDC(QString const &bFile) const {
// 10: OK
10 : bl_stopBL() // leave bl and start (the new) application
//
// NOTE: this function MUST work under all conditions.
// Alas, there is no direct result for this command, so
// the only way of knowing it was successful is to ask
// the device if the bootloader is still running.
// There is no problem to repeat this command until the
// bootloader is really not running anymore.
*/
bool Update::updateBinary(char const *fileToSendToDC) {
qInfo() << "updating device controller binary" << fileToSendToDC;
qInfo() << "UPDATING DEVICE CONTROLLER FIRMWARE BINARY" << fileToSendToDC;
QFile fn(fileToSendToDC);
bool r;
if ((r = fn.exists()) == true) {
QString const linkTarget = fn.symLinkTarget();
QFileInfo fi(linkTarget);
qInfo() << " updating binary (size=" << linkTarget << fi.size() << ")";
if ((r = updateDC(linkTarget)) == true) {
qInfo() << " updating binary (size=" << linkTarget << fi.size() << ") done";
QFileInfo fi(fn);
if ((r = updateDC(fileToSendToDC)) == true) {
Utils::printInfoMsg(
QString(" UPDATING BINARY ") + fi.fileName()
+ QString(" (size=%1").arg(fi.size()) + ") DONE");
} else {
qCritical() << "updating binary (size=" << linkTarget << fi.size() << ")... FAILED";
Utils::printCriticalErrorMsg(
QString(" UPDATING BINARY ") + fi.fileName()
+ QString(" (size=%1").arg(fi.size()) + ") FAILED");
}
} else {
qCritical() << "symlink" << fileToSendToDC
<< "does not exist -> NO UPDATE OF DC FIRMWARE";
Utils::printCriticalErrorMsg(
QString(fileToSendToDC) + " DOES NOT EXIST -> NO UPDATE OF DC FIRMWARE");
}
return r;
}
bool Update::updateDC(QString bFile) const {
qDebug() << "updating dc...";
qDebug() << "updating dc: file to send" << bFile;
if (!resetDeviceController()) {
return false;
qDebug() << "IN UPDATEDC: UPDATING DC: FILE TO SEND" << bFile;
m_worker->mainWindow()->setUpdateStep(UpdateDcEvent::UpdateStep::NONE);
QApplication::postEvent( // step 1: reset device controller
m_worker->mainWindow(),
new UpdateDcEvent(m_worker, UpdateDcEvent::UpdateStep::DC_REBOOT, 1));
QThread::sleep(1);
for (int i=1; i <= MainWindow::BL_START_COUNT; ++i) {
QApplication::postEvent( // step 2: start bootloader (5x)
m_worker->mainWindow(),
new UpdateDcEvent(m_worker, UpdateDcEvent::UpdateStep::BL_START, i));
QThread::sleep(1);
}
if (!startBootloader()) {
// even when start seems to fail, stopping the boot loader does not harm
stopBootloader();
int const cntLimit = 100; // wait until its for sure that bl_startBL()
int cnt = 0; // has been excuted
while (++cnt < cntLimit &&
m_worker->mainWindow()->updateStep() != UpdateDcEvent::UpdateStep::BL_START) {
// wait until all bl_startBL() are done
QThread::msleep(200);
}
if (cnt == cntLimit) {
// start events not received ???
Utils::printCriticalErrorMsg("BL_START EVENT NOT RECEIVED AFTER 20 SECS");
return false;
}
m_worker->mainWindow()->setUpdateStep(UpdateDcEvent::UpdateStep::BL_CHECK);
for (int i=1; i <= MainWindow::BL_IS_UP_COUNT; ++i) {
QApplication::postEvent(m_worker->mainWindow(), new UpdateDcEvent(m_worker, UpdateDcEvent::UpdateStep::BL_CHECK, i));
QThread::sleep(1);
QApplication::postEvent(m_worker->mainWindow(), new UpdateDcEvent(m_worker, UpdateDcEvent::UpdateStep::BL_IS_UP, i));
if (m_worker->mainWindow()->updateStep() == UpdateDcEvent::UpdateStep::BL_IS_UP) {
break;
}
QThread::sleep(1);
}
cnt = 0;
while (++cnt < cntLimit &&
m_worker->mainWindow()->updateStep() != UpdateDcEvent::UpdateStep::BL_IS_UP) {
// wait until all bl_startBL() are done
QThread::msleep(200);
}
if (cnt == cntLimit) {
// really not up
Utils::printCriticalErrorMsg("BL_IS_UP EVENT NOT RECEIVED AFTER 20 SECS");
stopBootloader(); // try to stop bootloader whichhas been already started
return false;
}
if (m_worker->mainWindow()->updateStep() == UpdateDcEvent::UpdateStep::BL_IS_UP) {
// bootloader MUST be running to download device-controller
#if 0
if (!downloadBinaryToDC(bFile)) {
stopBootloader();
qCritical() << "updating dc: " << bFile << "...FAILED";
Utils::printCriticalErrorMsg(
QString("UPDATING DC: ") + bFile + " ...DOWNLOAD FAILED");
}
#endif
} else {
Utils::printCriticalErrorMsg(
QString("UPDATING DC: ") + bFile + " BOOT LOADER NOT RUNNING -> NO DOWNLOAD ("
+ QThread::currentThread()->objectName() + ")");
return false;
}
qInfo() << "updating dc: " << bFile << "...OK";
stopBootloader();
//resetDeviceController();
// do this unconditionally, even if bootloader is not running at all ->
// the controller possibly tells us nonsense.
if (!stopBootloader()) {
Utils::printCriticalErrorMsg(
QString("UPDATING DC: ") + bFile + " BOOT LOADER STILL RUNNING ("
+ QThread::currentThread()->objectName() + ")");
return false;
}
QThread::sleep(3);
Utils::printInfoMsg(QString("UPDATING DC: ") + bFile + " ...OK");
return true;
}
@@ -521,9 +606,12 @@ bool Update::downloadJson(enum FileTypeJson type,
int templateIdx,
QString jsFileToSendToDC) const {
qDebug() << "updating json-file:" << jsFileToSendToDC << "...";
qDebug() << " template-index:" << templateIdx;
qDebug() << " json-type:" << jsonType(type);
Utils::printInfoMsg(
QString("UPDATING JSON-FILE=%1, TEMPLATE-INDEX=%2, JSON-TYPE=%3")
.arg(jsFileToSendToDC)
.arg(templateIdx)
.arg(jsonType(type)));
m_hw->dc_autoRequest(true); // downloading Json needs the AutoEmission flag
qDebug() << "SET AUTO-REQUEST=TRUE";
@@ -534,7 +622,7 @@ bool Update::downloadJson(enum FileTypeJson type,
while ((ready = m_hw->sys_ready4sending()) == false) {
QThread::msleep(200);
if (--nTry <= 0) {
qCritical() << "SYS NOT READY FOR SENDING AFTER 5 SECONDS";
Utils::printCriticalErrorMsg("SYS NOT READY FOR SENDING AFTER 5 SECONDS");
break;
}
}
@@ -547,22 +635,27 @@ bool Update::downloadJson(enum FileTypeJson type,
if (file.open(QIODevice::ReadOnly)) {
if (fi.size() <= 800) {
QByteArray ba = file.readAll();
// kindOfFile: 1=config, 2=device, 3=cash, 4=serial, 5=time, 6=printer
// nrOfTemplate=1...32 if kindOfFile==6
// content = content of the Json file, max 800byte ascii signs
if (m_hw->sys_sendJsonFileToDc((uint8_t)(type),
templateIdx,
(uint8_t *)ba.data())) {
QThread::sleep(1);
qDebug() << "SENT" << jsFileToSendToDC;
ret = true;
}
} else {
qCritical() << "SIZE OF" << jsFileToSendToDC
<< "TOO BIG (" << fi.size() << "BYTES)";
Utils::printCriticalErrorMsg(
QString("SIZE OF %1 TOO BIG (%2 BYTES)")
.arg(jsFileToSendToDC).arg(fi.size()));
}
} else {
qCritical() << "CANNOT OPEN" << jsFileToSendToDC << "FOR READING";
Utils::printCriticalErrorMsg(
QString("CAN NOT OPEN ") + jsFileToSendToDC + " FOR READING");
}
} else {
qCritical() << jsFileToSendToDC << "DOES NOT EXIST";
Utils::printCriticalErrorMsg(
QString(jsFileToSendToDC) + " DOES NOT EXIST");
}
}
@@ -589,22 +682,6 @@ bool Update::updateDeviceConf(QString jsFile) {
return downloadJson(FileTypeJson::DEVICE, 0, jsFile);
}
QStringList Update::getLinesToWorkOn() {
QStringList linesToWorkOn;
QTextStream in(&m_update_ctrl_file);
while (!in.atEnd()) {
QString line = in.readLine().trimmed();
if (line.startsWith("DONE")) {
m_update_ctrl_file_copy.write(line.toUtf8().constData());
m_update_ctrl_file_copy.write("\n");
} else {
linesToWorkOn << line;
}
}
return linesToWorkOn;
}
QStringList Update::split(QString line, QChar sep) {
QStringList lst;
QString next;
@@ -639,248 +716,146 @@ void Update::finished(int /*exitCode*/, QProcess::ExitStatus /*exitStatus*/) {
disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardError()));
}
// bool Update::executeProcess(QString const &cmd);
QStringList Update::getDcSoftAndHardWareVersion() {
m_hw->dc_autoRequest(true);
QThread::sleep(1); // make sure the timer-slots are active
bool Update::doUpdate() {
//
// ACHTUNG !!!
//
return true;
/*
The file referred to by 'update_data' has the following structure for
each line:
# ======================================================================
# REQUEST | NAME | DATE | RESULT
# ======================================================================
# where
#
# STATUS: DOWNLOAD, EXECUTE or DONE
# NAME : If starting with 'opkg' it is an opkg-command to be executed.
# Otherwise its the name of a file which has to be updated.
# DATE : 0000-00-00T00:00:00
# RESULT: SUCCESS or ERROR (possibly with description)
#
*/
if (!m_init) {
qCritical() << "DO UPDATE: INITIALIZATION OR UPDATE-SCRIPT FAILED";
return false;
}
if (m_executeScriptOnly) { // basically a test flag for executing only the
return true; // update script.
}
bool serialOpened = false;
bool serialOpen = false;
QStringList linesToWorkOn = getLinesToWorkOn();
if (linesToWorkOn.size() == 0) {
qCritical() << "No lines to handle in" << m_update_ctrl_file.fileName();
return true;
}
qDebug() << "open lines...";
for (int i=0; i< linesToWorkOn.size(); ++i) {
qDebug() << "line" << i << ":" << linesToWorkOn.at(i).trimmed();
}
QList<QString>::const_iterator it;
for (it = linesToWorkOn.cbegin(); it != linesToWorkOn.cend(); ++it) {
bool res = false;
QString line = (*it).trimmed();
if (line.size() == 0 || line.startsWith(QChar('#'))) {
continue;
}
QStringList lst = split(line.trimmed());
if (lst.size() != 4) {
qCritical() << "PARSING ERROR FOR LINE"
<< line << "IN" << m_update_ctrl_file.fileName();
continue;
}
QString const &request = lst[COLUMN_REQUEST];
QString const &name = lst[COLUMN_NAME];
QTextStream out(&m_update_ctrl_file_copy);
// QString const &datetime = lst[COLUMN_DATE_TIME];
// QString const &result = lst[COLUMN_RESULT];
qDebug() << "request=" << request << ", name=" << name;
if (request.trimmed() == "DOWNLOAD") {
if (!serialOpen) {
if (!isSerialOpen()) { // open serial only if not already open
if ((serialOpened = openSerial(baudrateMap.value(m_baudrate), m_baudrate, m_serialInterface)) == false) {
qCritical() << "CANNOT OPEN" << m_serialInterface << "(BAUDRATE="
<< m_baudrate << ")";
return false;
}
}
serialOpen = true;
qCritical() << "SERIAL OPEN" << m_serialInterface << "(BAUDRATE=" << m_baudrate << ")";
}
if (name.contains("dc2c", Qt::CaseInsensitive) &&
name.endsWith(".bin", Qt::CaseInsensitive)) {
qDebug() << "sending sw/hw-requests...";
for (int i=0; i < 3; ++i) { // send explicit reuests to get
// current SW/HW-versions
m_hw->request_DC2_SWversion();
m_hw->request_DC2_HWversion();
QThread::sleep(1);
}
QString const hwVersion = m_hw->dc_getHWversion().toLower();
QString const fwVersion = m_hw->dc_getSWversion().toLower();
qInfo() << "current dc-hardware-version" << hwVersion;
qInfo() << "current dc-firmware-version" << fwVersion;
QFile fn(name);
QFileInfo linkTarget(fn.symLinkTarget());
if (!linkTarget.exists()) { // check for broken link
QString const &hwVersion = m_hw->dc_getHWversion().toLower().trimmed();
QString const &swVersion = m_hw->dc_getSWversion().toLower().trimmed();
m_hw->dc_autoRequest(false);
QThread::sleep(1); // make sure the timer-slots are inactive
if (!hwVersion.isEmpty() && !swVersion.isEmpty()) {
return QStringList() << hwVersion << swVersion;
}
return QStringList() << "DC HW-version not available"
<< "DC SW-version not available";
}
bool Update::doUpdate(int &displayIndex, QStringList const &filesToWorkOn) {
// always assume that serial line is open
bool res = false;
QList<QString>::const_iterator it;
for (it = filesToWorkOn.cbegin(); it != filesToWorkOn.cend(); ++it) {
m_worker->startProgressLoop();
QString const &fToWorkOn = QDir::cleanPath(m_customerRepository + QDir::separator() + it->trimmed());
#if UPDATE_DC == 1
static const QRegularExpression version("^.*dc2c[.][0-9]{1,2}[.][0-9]{1,2}[.]bin.*$");
if (fToWorkOn.contains(version)) {
Utils::printInfoMsg("DO-UPDATE FILE-TO-WORK-ON " + fToWorkOn);
QFile fn(fToWorkOn);
QFileInfo finfo(fn);
if (!fn.exists()) { // check for broken link
Utils::printCriticalErrorMsg("DO-UPDATE FILE-TO-WORK-ON "
+ fToWorkOn + " DOES NOT EXIST");
res = false;
} else {
if (false) {
//if (fwVersion.startsWith(linkTarget.completeBaseName())) {
// qCritical() << "current dc-firmware-version" << fwVersion
// << "already installed";
// res = false;
} else {
res = true;
bool updateBinaryRes = true;
qCritical() << "downloading" << name.trimmed() << "->"
<< linkTarget.completeBaseName() << "to DC";
#if UPDATE_DC == 1
qInfo() << "DOWNLOADING" << finfo.completeBaseName() << "TO DC";
m_hw->dc_autoRequest(false);// default: turn auto-request setting off
QThread::sleep(1); // wait to be sure that there are no more
// commands sent to dc-hardware
qDebug() << "SET AUTO-REQUEST=FALSE";
qInfo() << "SET AUTO-REQUEST=FALSE";
if ((res = updateBinary(name.toStdString().c_str())) == true) {
qCritical() << "downloaded binary" << name;
if ((updateBinaryRes = updateBinary(fToWorkOn.toStdString().c_str())) == true) {
qCritical() << "downloaded binary" << fToWorkOn;
++displayIndex;
emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(2, ' ') + QString(")")
+ QString(" Update ") + QFileInfo(fToWorkOn).fileName(),
Worker::UPDATE_STEP_DONE);
}
m_hw->dc_autoRequest(true); // turn auto-request setting on
qDebug() << "SET AUTO-REQUEST=TRUE";
qDebug() << "WAIT 10 SECS TO RECEIVE RESPONSES...";
qInfo() << "SET AUTO-REQUEST=TRUE";
QThread::sleep(10); // wait to be sure that responses
// have been received
qCritical() << "updated dc-hardware-version" << m_hw->dc_getHWversion();
qCritical() << "updated dc-firmware-version" << m_hw->dc_getSWversion();
QStringList const &versions = Update::getDcSoftAndHardWareVersion();
if (versions.size() >= 2) {
if (updateBinaryRes == true) {
qInfo() << "dc-hardware-version (UPDATED)" << versions[0];
qInfo() << "dc-firmware-version (UPDATED)" << versions[1];
} else {
qInfo() << "dc-hardware-version (NOT UPDATED)" << versions[0];
qInfo() << "dc-firmware-version (NOT UPDATED)" << versions[1];
}
}
res = updateBinaryRes;
}
#endif
}
}
} else if (name.contains("DC2C_print", Qt::CaseInsensitive)
&& name.endsWith(".json", Qt::CaseInsensitive)) {
if (fToWorkOn.contains("DC2C_print", Qt::CaseInsensitive)
&& fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) {
res = true;
#if UPDATE_PRINTER_TEMPLATES == 1
int i = name.indexOf("DC2C_print", Qt::CaseInsensitive);
int const templateIdx = name.mid(i).midRef(10, 2).toInt();
int i = fToWorkOn.indexOf("DC2C_print", Qt::CaseInsensitive);
int const templateIdx = fToWorkOn.mid(i).midRef(10, 2).toInt();
if ((templateIdx < 1) || (templateIdx > 32)) {
qCritical() << "WRONG TEMPLATE INDEX" << templateIdx;
res = false;
} else {
if ((res = updatePrinterTemplate(templateIdx, name))) {
qInfo() << "downloaded printer template"<< name;
if ((res = updatePrinterTemplate(templateIdx, fToWorkOn))) {
Utils::printInfoMsg(
QString("DOWNLOADED PRINTER TEMPLATE %1 WITH INDEX=%2")
.arg(fToWorkOn)
.arg(templateIdx));
++displayIndex;
emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(3, ' ') + QString(")")
+ QString(" Update ") + QFileInfo(fToWorkOn).fileName(),
Worker::UPDATE_STEP_DONE);
}
}
#endif
} else if (name.contains("DC2C_cash", Qt::CaseInsensitive)
&& name.endsWith(".json", Qt::CaseInsensitive)) {
} else if (fToWorkOn.contains("DC2C_cash", Qt::CaseInsensitive)
&& fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) {
res = true;
#if UPDATE_CASH_TEMPLATE == 1
if ((res = updateCashConf(name))) {
qInfo() << "downloaded cash template"<< name;
if ((res = updateCashConf(fToWorkOn))) {
Utils::printInfoMsg(QString("DOWNLOADED CASH TEMPLATE %1").arg(fToWorkOn));
++displayIndex;
emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(3, ' ') + QString(")")
+ QString(" Update ") + QFileInfo(fToWorkOn).fileName(),
Worker::UPDATE_STEP_DONE);
}
#endif
} else if (name.contains("DC2C_conf", Qt::CaseInsensitive)
&& name.endsWith(".json", Qt::CaseInsensitive)) {
} else if (fToWorkOn.contains("DC2C_conf", Qt::CaseInsensitive)
&& fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) {
res = true;
#if UPDATE_CONF_TEMPLATE == 1
if ((res= updateConfig(name))) {
qInfo() << "downloaded config template"<< name;
if ((res= updateConfig(fToWorkOn))) {
Utils::printInfoMsg(QString("DOWNLOADED CONFIG TEMPLATE %1").arg(fToWorkOn));
++displayIndex;
emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(3, ' ') + QString(")")
+ QString(" Update ") + QFileInfo(fToWorkOn).fileName(),
Worker::UPDATE_STEP_DONE);
}
#endif
} else if (name.contains("DC2C_device", Qt::CaseInsensitive)
&& name.endsWith(".json", Qt::CaseInsensitive)) {
} else if (fToWorkOn.contains("DC2C_device", Qt::CaseInsensitive)
&& fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) {
res = true;
#if UPDATE_DEVICE_TEMPLATE == 1
if ((res = updateDeviceConf(name))) {
qInfo() << "downloaded device template"<< name;
if ((res = updateDeviceConf(fToWorkOn))) {
Utils::printInfoMsg(QString("DOWNLOADED DEVICE TEMPLATE %1").arg(fToWorkOn));
++displayIndex;
emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(3, ' ') + QString(")")
+ QString(" Update ") + QFileInfo(fToWorkOn).fileName(),
Worker::UPDATE_STEP_DONE);
}
#endif
} else {
qCritical() << "UNKNOWN JSON FILE NAME" << name;
qCritical() << "UNKNOWN JSON FILE NAME" << fToWorkOn;
res = false;
}
} else if (request == "EXECUTE" && name.contains("opkg")) {
qInfo() << "starting" << name.trimmed();
res = true;
#if UPDATE_OPKG == 1
QScopedPointer<QProcess> p(new QProcess(this));
p->setProcessChannelMode(QProcess::MergedChannels);
m_worker->stopProgressLoop();
m_worker->setProgress(100);
connect(&(*p), SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOutput()));
connect(&(*p), SIGNAL(readyReadStandardError()), this, SLOT(readyReadStandardError()));
p->start(name.trimmed());
if (p->waitForStarted(1000)) {
if (p->state() == QProcess::ProcessState::Running) {
if (p->waitForFinished(100000)) {
QString output = p->readAllStandardOutput();
QStringList outputLst = split(output, QChar('\n'));
for (int line=0; line < outputLst.size(); ++line) {
qDebug() << outputLst[line];
if (res == false) {
break;
}
if (p->exitStatus() == QProcess::NormalExit) {
qInfo() << "EXECUTED" << name
<< "with code" << p->exitCode();
res = true;
} else {
qCritical() << "PROCESS" << name << "CRASHED";
}
} else {
qCritical() << "PROCESS" << name << "DID NOT FINISH";
}
} else {
qCritical() << "WRONG PROCESS STATE" << p->state();
}
} else {
qCritical() << "PROCESS" << name << "TIMEOUT AT START";
}
#endif
} else {
// TODO
}
out << "DONE, " << name << ", "
<< QDateTime::currentDateTime().toString(Qt::ISODate) << ", "
<< ((res == true) ? "SUCCESS" : "ERROR") << "\n";
out.flush();
} // for (it = openLines.cbegin(); it != openLines.end(); ++it) {
m_hw->dc_autoRequest(true); // ALWAYS turn autoRequest ON
qDebug() << "SET AUTO-REQUEST=TRUE";
return true;
// return finishUpdate(linesToWorkOn.size() > 0);
}
bool Update::finishUpdate(bool swapCtrlFiles) {
if (swapCtrlFiles) {
m_update_ctrl_file.close();
m_update_ctrl_file_copy.close();
//std::ifstream source(m_update_ctrl_file_copy.fileName().toStdString().c_str(), std::ios::binary);
//std::ofstream dest(m_update_ctrl_file.fileName().toStdString().c_str(), std::ios::binary);
//dest << source.rdbuf();
//source.close();
//dest.close();
}
return true;
return res;
}

View File

@@ -7,9 +7,9 @@
#include <QDir>
#include <QByteArray>
#include <QProcess>
#include <QPluginLoader>
#include "plugins/interfaces.h"
#include "apism/apism_client.h"
#ifdef PTU5
#define SERIAL_PORT "ttymxc2"
@@ -17,68 +17,55 @@
#define SERIAL_PORT "ttyUSB0"
#endif
class Update;
// TODO: check hardware compatibility
// TODO: opkg commandos
class Worker;
class Update : public QObject {
Q_OBJECT
hwinf *m_hw;
Worker *m_worker;
char const *m_serialInterface;
char const *m_baudrate;
QFile m_update_ctrl_file;
QFile m_update_ctrl_file_copy;
QString m_repositoryPath;
QString m_customerId; // customer_281
QString m_customerRepository;
QString m_customerNrStr;
QString m_branchName;
QString m_pluginName;
QString m_workingDir;
bool m_maintenanceMode;
bool m_testMode;
bool m_executeScriptOnly;
bool m_dryRun;
//ApismClient m_apismClient;
bool m_init;
bool finishUpdate(bool finish);
QStringList getLinesToWorkOn();
bool execUpdateScript();
static QPluginLoader pluginLoader;
public:
enum class DownloadResult {OK, ERROR, TIMEOUT, NOP};
enum class FileTypeJson {CONFIG=1, DEVICE=2, CASH=3, SERIAL=4, TIME=5, PRINTER=6};
static hwinf *loadDCPlugin(QDir const &plugInDir, QString const &fn);
static bool unloadDCPlugin();
static QStringList split(QString line, QChar sep = ',');
explicit Update(hwinf *hw,
QString update_ctrl_file,
QString repositoryPath,
QString customerId,
Worker *worker,
QString customerRepository,
QString customerNrStr,
QString branchName,
QString workingDir = ".",
bool maintenanceMode = false,
bool testMode = false,
bool executeScriptOnly = false,
QString pluginName,
QString workingDir,
bool dryRun = false,
QObject *parent = nullptr,
char const *serialInterface = SERIAL_PORT,
char const *baudrate = "115200");
virtual ~Update() override;
bool doUpdate();
bool doUpdate(int &displayIndex, QStringList const &linesToWorkOn);
QString customerId() { return m_customerId; }
QString const customerId() const { return m_customerId; }
//QString customerId() { return m_customerId; }
//QString const customerId() const { return m_customerId; }
QString branchName() { return m_branchName; }
QString const branchName() const { return m_branchName; }
QString repositoryPath() { return m_repositoryPath; }
QString const repositoryPath() const { return m_repositoryPath; }
//QString repositoryPath() { return m_repositoryPath; }
//QString const repositoryPath() const { return m_repositoryPath; }
private:
static QString jsonType(enum FileTypeJson type);
@@ -104,8 +91,7 @@ private:
bool updateDeviceConf(QString jsFileToSendToDC);
bool downloadJson(enum FileTypeJson type, int templateIdx,
QString jsFileToSendToDC) const;
bool executeProcess(QString const &cmd);
QStringList getDcSoftAndHardWareVersion();
private slots:
void readyReadStandardOutput();

25
update_dc_event.cpp Normal file
View File

@@ -0,0 +1,25 @@
#include "update_dc_event.h"
QEvent::Type UpdateDcEvent::customEventType = QEvent::None;
UpdateDcEvent::UpdateDcEvent(QObject const *sender,
UpdateStep updateStep,
int count,
QDateTime const &sendDateTime)
: QEvent(UpdateDcEvent::type())
, m_sender(sender)
, m_updateStep(updateStep)
, m_count(count)
, m_sendDateTime(sendDateTime) {
}
UpdateDcEvent::~UpdateDcEvent() {
}
QEvent::Type UpdateDcEvent::type() {
if (customEventType == QEvent::None) {
int generatedType = QEvent::registerEventType();
customEventType = static_cast<QEvent::Type>(generatedType);
}
return customEventType;
}

40
update_dc_event.h Normal file
View File

@@ -0,0 +1,40 @@
#ifndef UPDATE_DC_EVENT_H_INCLUDED
#define UPDATE_DC_EVENT_H_INCLUDED
#include <QEvent>
#include <QDateTime>
class UpdateDcEvent : public QEvent {
public:
enum UpdateStep { NONE, DC_REBOOT, BL_START, BL_CHECK, BL_CHECK_AFTER_STOP, BL_IS_UP, BL_IS_DOWN, BL_STOP};
private:
QObject const *m_sender;
UpdateStep m_updateStep;
int m_count;
QDateTime m_sendDateTime;
public:
explicit UpdateDcEvent(QObject const *sender, UpdateStep updateStep,
int count,
QDateTime const &sendDateTime = QDateTime::currentDateTime());
virtual ~UpdateDcEvent();
static QEvent::Type type();
QObject const *sender() { return m_sender; }
QObject const *sender() const { return m_sender; }
void setUpdateStep(UpdateStep updateStep) { m_updateStep = updateStep; }
UpdateStep updateStep() { return m_updateStep; }
UpdateStep updateStep() const { return m_updateStep; }
int count() const { return m_count; }
void setCount(int count) { m_count = count; }
QDateTime &sendDateTime() { return m_sendDateTime; }
QDateTime const &sendDateTime() const { return m_sendDateTime; }
private:
static QEvent::Type customEventType;
};
#endif // PROGRESS_EVENT_H_INCLUDED

378
utils.cpp
View File

@@ -1,201 +1,237 @@
#include "utils.h"
#include "message_handler.h"
#include "git/git_client.h"
#if defined (Q_OS_UNIX) || defined (Q_OS_LINUX)
#include "unistd.h"
#endif
#include <QCoreApplication>
#include <QApplication>
#include <QFile>
#include <QTemporaryFile>
#include <QDebug>
#include <QTextStream>
#include <QDebug>
#include <QDir>
#include <QDirIterator>
#include <QRegularExpression>
#include "interfaces.h"
#include "DCPlugin/include/hwapi.h"
#include <fstream>
//#include <unistd.h>
#include <thread>
#include <memory>
#include <QSharedMemory>
#include <QScopedPointer>
#include <QProcess>
#define COLUMN_STATUS (0)
#define COLUMN_NAME (1)
#define COLUMN_DATE_TIME (2)
#define COLUMN_RESULT (3)
Utils::Utils(QString update_ctrl_file,
QObject *parent,
char const *serialInterface,
char const *baudrate)
: QObject(parent)
, m_hw(new hwapi())
, m_serialInterface(serialInterface)
, m_baudrate(baudrate)
, m_update_ctrl_file(update_ctrl_file)
, m_update_ctrl_file_copy(update_ctrl_file + ".copy")
, m_in(&m_update_ctrl_file)
, m_out(&m_update_ctrl_file_copy)
, m_init(true) {
if (!m_update_ctrl_file.exists()) {
qCritical() << "Update-file" << m_update_ctrl_file.fileName()
<< "does not exist";
m_init = false;
}
if (!m_update_ctrl_file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qCritical() << "can not open " << m_update_ctrl_file.fileName()
<< "for reading";
m_init = false;
}
if (!m_update_ctrl_file_copy.open(QIODevice::WriteOnly | QIODevice::Text)) {
qCritical() << "can not open " << m_update_ctrl_file_copy.fileName()
<< "for writing";
m_init = false;
int Utils::read1stLineOfFile(QString fileName) {
QFile f(fileName);
if (f.exists()) {
if (f.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&f);
in.setCodec("UTF-8");
while(!in.atEnd()) {
return in.readLine().toInt();
}
}
}
return -1;
}
Utils::~Utils() {
QString Utils::zoneName(quint8 i) {
static constexpr char const *zName[] = {
"",
"purple",
"blue",
"yellow",
"green",
"yellow (mars)",
"green (mars)"
};
if (i < (sizeof(zName)/sizeof(char const *))) {
return zName[i];
}
return "N/A";
}
void Utils::printCriticalErrorMsg(QString const &errorMsg) {
qCritical() << QString(80, 'E');
qCritical() << errorMsg;
qCritical() << QString(80, 'E');
}
void Utils::printUpdateStatusMsg(QString const &updateMsg) {
qCritical() << QString(80, 'U');
qCritical() << updateMsg;
qCritical() << QString(80, 'U');
}
void Utils::printInfoMsg(QString const &infoMsg) {
qCritical() << QString(80, 'I');
qCritical() << infoMsg;
qCritical() << QString(80, 'I');
}
void Utils::updateBinary(char const *fileToSendToDC) {
qDebug() << "file to send to DC ..." << fileToSendToDC;
qDebug() << "baudrate ............." << m_baudrate;
qDebug() << "serial interface ....." << m_serialInterface;
m_hw->dc_updateDC(fileToSendToDC, m_baudrate, m_serialInterface);
std::this_thread::sleep_for(std::chrono::milliseconds(3000));
QCoreApplication::quit();
void Utils::printLineEditInfo(QStringList const &lines) {
if (getDebugLevel() == LOG_DEBUG) {
for (int i=0; i<lines.size(); ++i) {
qInfo() << lines.at(i);
} qInfo() << ""; qInfo() << "";
}
}
void Utils::updatePrinterConf(int nrOfTemplate, char const *fileToSendToDC) {
QVector<int> printTemplates{ nrOfTemplate };
QVector<QString> filesToSend{ fileToSendToDC };
m_hw->dc_updatePrinterTemplate(hwapi::FileTypeJson::PRINTER,
printTemplates, filesToSend,
QString(m_baudrate),
QString(m_serialInterface));
std::this_thread::sleep_for(std::chrono::milliseconds(3000));
QCoreApplication::quit();
}
QStringList Utils::getOpenLines() {
QStringList openLines;
while (!m_in.atEnd()) {
QString line = m_in.readLine().trimmed();
// QString.split() is defined >= 5.14
if (!line.startsWith("OPEN")) {
m_out << line;
QString Utils::getTariffLoadTime(QString fileName) {
QFileInfo fInfo(fileName);
if (fInfo.exists()) {
QDateTime lastModifiedTime = fInfo.lastModified();
if (lastModifiedTime.isValid()) {
return lastModifiedTime.toString(Qt::ISODateWithMs);
} else {
openLines << line;
}
}
return openLines;
}
bool Utils::doUpdate() {
/*
The file referred to by 'update_data' has the following structure for
each line:
# ======================================================================
# STATUS | NAME | DATE | RESULT
# ======================================================================
# where
#
# STATUS: OPEN or CLOSED
# NAME : If starting with 'opkg' it is an opkg-command to be executed.
# Otherwise its the name of a file which has to be updated.
# DATE : 0000-00-00T00:00:00
# RESULT: SUCCESS or ERROR (possibly with description)
#
*/
if (!m_init) {
return false;
}
QStringList openLines = getOpenLines();
bool res = false;
QList<QString>::const_iterator it;
for (it = openLines.cbegin(); it != openLines.cend(); ++it) {
int start = 0, end;
int column = 0;
QString status, name, datetime, result;
QString line = *it;
while ((end = line.indexOf(QChar(','), start)) != -1) {
QString next = line.mid(start, end).trimmed();
switch (column) {
case COLUMN_STATUS:
status = next;
break;
case COLUMN_NAME:
name = next;
break;
case COLUMN_DATE_TIME:
datetime = next;
break;
case COLUMN_RESULT:
result = next;
break;
}
++column;
start = end + 1;
}
if (!status.contains("OPEN")) {
qCritical() << "Parsing error for" << m_update_ctrl_file.fileName();
return false;
}
if (name.contains("dc2c") && name.endsWith(".bin")) {
updateBinary(name.toStdString().c_str());
res = true;
} else
if (name.contains("DC2C_print") && name.endsWith(".json")) {
int i = name.indexOf("DC2C_print");
int templateIdx = name.mid(i).midRef(10, 2).toInt();
updatePrinterConf(templateIdx, name.toStdString().c_str());
res = true;
} else
if (name.contains("opkg")) {
int i = name.indexOf("opkg ");
QString rest = name.mid(i).trimmed();
QScopedPointer<QProcess> p(new QProcess(this));
p->setProcessChannelMode(QProcess::MergedChannels);
p->start("opkg", QStringList() << rest);
if (p->waitForStarted(1000)) {
if (p->state() == QProcess::ProcessState::Running) {
if (p->waitForFinished(10000)) {
QByteArray output = p->readAllStandardOutput();
qCritical() << output;
res = true;
}
printCriticalErrorMsg(fileName + " HAS INVALID MODIFIED-TIME");
QDateTime birthTime = fInfo.birthTime();
if (birthTime.isValid()) {
return birthTime.toString(Qt::ISODateWithMs);
} else {
printCriticalErrorMsg(fileName + " HAS INVALID BIRTH-TIME");
}
}
} else {
// TODO
printCriticalErrorMsg(fileName + " DOES NOT EXIST");
}
QString resultLine = "CLOSED";
resultLine += ", " + name;
resultLine += ", " + QDateTime::currentDateTime().toString(Qt::ISODate);
resultLine += ", " + (res == true) ? "SUCCESS" : "ERROR";
m_out << resultLine;
} // for (it = openLines.cbegin(); it != openLines.end(); ++it) {
return finishUpdate(openLines.size() > 0);
return "N/A";
}
bool Utils::finishUpdate(bool replaceCtrlFile) {
if (replaceCtrlFile) {
if (!m_update_ctrl_file_copy.exists()) {
QString Utils::rstrip(QString const &str) {
int n = str.size() - 1;
for (; n >= 0; --n) {
if (!str.at(n).isSpace()) {
return str.left(n + 1);
}
}
return "";
}
bool Utils::sameFilesInDirs(QDir const &dir1, QDir const &dir2,
QStringList const &nameFilters) {
if (!dir1.exists()) {
printCriticalErrorMsg(dir1.dirName() + " DOES NOT EXIST");
return false;
}
if (!m_update_ctrl_file.remove()) {
if (!dir2.exists()) {
printCriticalErrorMsg(dir2.dirName() + " DOES NOT EXIST");
return false;
}
if (!m_update_ctrl_file_copy.rename(m_update_ctrl_file.fileName())) {
if (dir1.absolutePath() == dir2.absolutePath()) {
printCriticalErrorMsg(dir1.dirName() + " AND "+ dir2.dirName() + " HAVE SAME PATH");
return false;
}
// files, sorted by name
QFileInfoList const &lst1 = dir1.entryInfoList(nameFilters, QDir::Files, QDir::Name);
QFileInfoList const &lst2 = dir2.entryInfoList(nameFilters, QDir::Files, QDir::Name);
QStringList fileNameLst1{};
QStringList fileNameLst2{};
QListIterator<QFileInfo> i1(lst1);
while (i1.hasNext()) {
fileNameLst1 << i1.next().fileName();
}
QListIterator<QFileInfo> i2(lst2);
while (i2.hasNext()) {
fileNameLst2 << i2.next().fileName();
}
if (fileNameLst1.isEmpty()) {
qCritical() << "DIR1" << dir1.dirName() << " DOES NOT CONTAIN EXPECTED FILES";
return false;
}
if (fileNameLst2.isEmpty()) {
qCritical() << "DIR1" << dir2.dirName() << " DOES NOT CONTAIN EXPECTED FILES";
return false;
}
if (fileNameLst1 != fileNameLst2) {
printCriticalErrorMsg(dir1.dirName() + " AND " + dir2.dirName()
+ " DIFFER: [" + fileNameLst1.join(',') + "],["
+ fileNameLst2.join(',') + "]");
return false;
} else {
printInfoMsg(dir1.dirName() + " AND " + dir2.dirName()
+ " ARE EQUAL: [" + fileNameLst1.join(',') + "]");
}
QStringList gitBlobLst1{};
QStringList gitBlobLst2{};
QListIterator<QFileInfo> i3(lst1);
while (i3.hasNext()) {
gitBlobLst1 << GitClient::gitBlob(i3.next().fileName());
}
QListIterator<QFileInfo> i4(lst2);
while (i4.hasNext()) {
gitBlobLst2 << GitClient::gitBlob(i4.next().fileName());
}
if (gitBlobLst1.isEmpty()) {
qCritical() << "DIR1" << dir1.dirName() << " DOES NOT CONTAIN EXPECTED FILES";
return false;
}
if (gitBlobLst2.isEmpty()) {
qCritical() << "DIR1" << dir2.dirName() << " DOES NOT CONTAIN EXPECTED FILES";
return false;
}
if (gitBlobLst1 != gitBlobLst2) {
printCriticalErrorMsg(dir1.dirName() + " AND " + dir2.dirName()
+ " DIFFER: [" + gitBlobLst1.join(',') + "],["
+ gitBlobLst2.join(',') + "]");
return false;
} else {
printInfoMsg(dir1.dirName() + " AND " + dir2.dirName()
+ " CONTAIN SAME GIT-BLOBS FOR FILES: [" + fileNameLst1.join(',') + "]");
}
return true;
}
QString Utils::getParentName() { // get name of parent process
QString ppid = QString("/proc/%1/status").arg(getppid());
std::ifstream f(ppid.toStdString().c_str());
if (f.is_open()) {
std::string next;
while (std::getline(f, next)) {
QString line = QString(next.c_str()).simplified();
if (line.startsWith("Name")) {
int const idx = line.indexOf(QChar(':'));
if (idx != -1) {
return line.mid(idx+1).trimmed();
}
}
}
}
return "";
}
bool Utils::isATBQTRunning() {
QDirIterator it("/proc",
QStringList() << "status",
QDir::Files,
QDirIterator::Subdirectories);
while (it.hasNext()) {
QString const &nextStatusFile = it.next();
static const QRegularExpression re("^/proc/[0-9]{1,}/status");
QRegularExpressionMatch match = re.match(nextStatusFile);
if (match.hasMatch()) {
std::ifstream f(nextStatusFile.toStdString().c_str());
if (f.is_open()) {
std::string next;
while (std::getline(f, next)) {
QString line = QString(next.c_str()).simplified();
if (line.startsWith("Name")) {
int const idx = line.indexOf(QChar(':'));
if (idx != -1) {
QString const binary = line.mid(idx+1).trimmed();
if (binary == "ATBQT") {
return true;
}
}
}
}
}
}
}
return false;
}

53
utils.h
View File

@@ -3,44 +3,27 @@
#include <QObject>
#include <QString>
#include <QStringList>
#include <QFile>
#include <QFileInfo>
#include <QDateTime>
#include <QDir>
#include <memory>
namespace Utils {
int read1stLineOfFile(QString fileName);
QString zoneName(quint8 i);
void printCriticalErrorMsg(QString const &errorMsg);
void printInfoMsg(QString const &infoMsg);
void printUpdateStatusMsg(QString const &updateMsg);
void printLineEditInfo(QStringList const &lines);
QString getTariffLoadTime(QString fileName);
QString rstrip(QString const &str);
bool sameFilesInDirs(QDir const &dir1, QDir const &dir2,
QStringList const &nameFilters = {"*.json"});
#include "interfaces.h"
#include "DCPlugin/include/hwapi.h"
#ifdef PTU5
#define SERIAL_PORT "ttymxc2"
#else
#define SERIAL_PORT "ttyUSB0"
#endif
QString getParentName();
bool isATBQTRunning();
}
class Utils : public QObject {
Q_OBJECT
std::unique_ptr<hwinf> m_hw;
char const *m_serialInterface;
char const *m_baudrate;
QFile m_update_ctrl_file;
QFile m_update_ctrl_file_copy;
QTextStream m_in;
QTextStream m_out;
bool m_init;
void updateBinary(char const *fileToSendToDC);
void updatePrinterConf(int nrOfTemplate, char const *fileToSendToDC);
bool finishUpdate(bool finish);
QStringList getOpenLines();
static constexpr QChar SEPARATOR = QChar(',');
public:
explicit Utils(QString update_ctrl_file,
QObject *parent = nullptr,
char const *serialInterface = SERIAL_PORT,
char const *baudrate = "115200");
virtual ~Utils() override;
bool doUpdate();
};
#endif // UTILS_H_INCLUDED

1607
worker.cpp

File diff suppressed because it is too large Load Diff

203
worker.h
View File

@@ -9,11 +9,12 @@
#include <QJsonObject>
#include <QHash>
#include <optional>
#include "worker_thread.h"
#include "update.h"
#include "git/git_client.h"
#include "ismas/ismas_client.h"
#include "apism/apism_client.h"
#ifdef PTU5
#define SERIAL_PORT "ttymxc2"
@@ -22,79 +23,191 @@
#endif
enum class UPDATE_STATUS : quint8 {
NOT_DEFINED,
ISMAS_WAIT_STATE_CHECK_PENDING,
ISMAS_WAIT_STATE_CHECK_FAILURE,
ISMAS_RESPONSE_RECEIVED,
BACKEND_CHECK,
BACKEND_CHECK_FAILURE,
BACKEND_NOT_CONNECTED,
ISMAS_UPDATE_TRIGGER_SET,
ISMAS_UPDATE_TRIGGER_NOT_SET_OR_WRONG,
GIT_CLONE_AND_CHECKOUT_SUCCESS,
GIT_CLONE_AND_CHECKOUT_FAILURE,
GIT_CHECKOUT_BRANCH,
GIT_CHECKOUT_BRANCH_FAILURE,
GIT_FETCH_UPDATES,
GIT_FETCH_UPDATES_REQUEST_FAILURE,
EXEC_OPKG_COMMAND,
EXEC_OPKG_COMMANDS,
RSYNC_UPDATES_FAILURE,
RSYNC_UPDATES_SUCCESS,
RSYNC_FILE_SUCCESS,
JSON_PARSE_FAILURE,
UPDATE_PROCESS_SUCCESS,
UPDATE_PROCESS_FAILURE,
ISMAS_SANITY_CHECK_OK,
ISMAS_UPDATE_TRIGGER_SET_FAILURE,
PSA_UPDATE_FILES_FAILED,
GIT_CHECK_FILES_TO_UPDATE_SUCCESS,
ISMAS_SEND_LAST_VERSION_FAILED,
SAVE_LOG_FILES_FAILED
};
struct UpdateStatus {
UPDATE_STATUS m_updateStatus;
QString m_statusDescription;
explicit UpdateStatus(UPDATE_STATUS s = UPDATE_STATUS::NOT_DEFINED,
QString const &d = QString(""))
: m_updateStatus(s), m_statusDescription(d) {}
};
QDebug operator<<(QDebug debug, UpdateStatus status);
QString& operator<<(QString &str, UpdateStatus status);
#define ISMAS_UPDATE_REQUESTS (10)
class MainWindow;
class hwinf;
class Worker : public QObject {
Q_OBJECT
WorkerThread m_workerThread;
QTimer m_startUpdateProcess;
Update *m_update;
ApismClient m_apismClient;
GitClient m_gc;
QString const m_customerId;
int const m_customerNr;
QString const m_customerNrStr;
int const m_machineNr;
int const m_zoneNr;
QString const m_pluginName;
QString const m_workingDirectory;
QString const m_branchName;
QString const m_customerRepositoryPath;
QString const m_customerRepository;
bool m_maintenanceMode;
bool const m_dryRun;
QObject *m_parent;
QString const m_serialInterface;
QString const m_baudrate;
IsmasClient m_ismasClient;
GitClient m_gc;
QString const m_osVersion;
QString const m_atbqtVersion;
QString const m_atbUpdateToolVersion;
QString const m_cpuSerial;
QString const m_pluginVersionATBDeciceController;
QString const m_pluginVersionIngenicoISelf;
QString const m_pluginVersionMobilisisCalc;
QString const m_pluginVersionMobilisisCalcConfig;
QString const m_pluginVersionPrmCalc;
QString const m_pluginVersionPrmCalcConfig;
QString const m_pluginVersionTcpZvt;
int m_ismasUpdateRequests;
QTimer m_waitForNewUpdates;
IsmasClient m_ismasClient;
int m_machineNr; // setzen
int m_customerNr;
UpdateStatus m_updateStatus;
QStringList m_filesToUpdate;
bool m_updateProcessRunning;
int m_displayIndex;
int m_returnCode;
enum { ISMAS_UPDATE_REQUESTS = 6 };
MainWindow *m_mainWindow;
int m_progressValue;
bool m_withoutIsmasDirectPort;
QString m_apismVersion;
enum class UPDATE_STATUS : quint8 {
STARTED,
STOPPED,
ERROR_BACKEND
} m_updateStatus;
bool executeOpkgCommand(QString opkgCommand);
QString getOsVersion() const;
QString getATBQTVersion() const;
QString getATBUpdateToolVersion() const;
QString getCPUSerial() const;
QString getRaucVersion() const;
QString getOpkgVersion() const;
QString getPluginVersion(QString const &pluginFileName) const;
QStringList getDCVersion() const;
qint64 getFileSize(QString const &fileName) const;
public:
explicit Worker(hwinf *hw,
QString update_ctrl_file,
QString repositoryPath,
QString customerId,
static QString getATBUpdateToolYoctoVersion();
static QString getATBUpdateToolYoctoInstallationStatus();
static const QString UPDATE_STEP_OK;
static const QString UPDATE_STEP_DONE;
static const QString UPDATE_STEP_FAIL;
static const QString UPDATE_STEP_SUCCESS;
explicit Worker(int customerNr, // 281
int machineNr,
int zoneNr,
QString branchName,
QString pluginName,
QString workingDir = ".",
bool maintenanceMode = false,
bool testMode = false,
bool executeScriptOnly = false,
bool dryRun = false,
QObject *parent = nullptr,
char const *serialInterface = SERIAL_PORT,
char const *baudrate = "115200");
~Worker();
void quit() { return m_workerThread.quit(); }
void setMainWindow(MainWindow *mainWindow) { m_mainWindow = mainWindow; }
hwinf *getPlugin();
hwinf const *getPlugin() const;
void setProgress(int progress);
void startProgressLoop();
void stopProgressLoop();
IsmasClient &getIsmasClient() { return m_ismasClient; }
IsmasClient const &getIsmasClient() const { return m_ismasClient; }
bool updateProcessRunning() const { return m_updateProcessRunning; }
int returnCode() const { return m_returnCode; }
int machineNr() const { return m_machineNr; }
int customerNr() const { return m_customerNr; }
int zoneNr() const { return m_zoneNr; }
QString apismVersion() const { return m_apismVersion; }
QString atbUpdateToolVersion() const { return m_atbUpdateToolVersion; }
MainWindow *mainWindow() { return m_mainWindow; }
MainWindow const *mainWindow() const { return m_mainWindow; }
//friend QDebug operator<<(QDebug debug, Worker const &w) {
// Q_UNUSED(w);
// return debug;
//}
//friend QString& operator<<(QString &str, Worker const &w) {
// Q_UNUSED(w);
// return str;
//}
signals:
void executeOpkgCommands(QStringList);
void executeOpkgCommand(QString);
void handleChangedFiles(QStringList);
void summarizeUpload(QStringList);
void summarizeRepositoryStatus();
void sendCmdSendVersionToIsmas();
void finishUpdateProcess(bool changes);
void terminateUpdateProcess();
void appendText(QString, QString suffix = "");
void replaceLast(QString, QString);
void replaceLast(QStringList, QString);
void showErrorMessage(QString title, QString description);
void showStatusMessage(QString title, QString description);
void stopStartTimer();
void restartExitTimer();
void enableExit();
void disableExit();
public slots:
void onIsmasResponseReceived(QJsonObject ismasResponse);
void onExecuteOpkgCommands(QStringList opkgCommands);
void onExecuteOpkgCommand(QString opkgCommand);
void update();
private slots:
void askIsmasForNewData();
void onSendCmdSendVersionToIsmas();
void onSummarizeRepositoryStatus();
void onFinishUpdateProcess(bool changes);
void onTerminateUpdateProcess();
void onSummarizeUpload(QStringList);
void onHandleChangedFiles(QStringList);
bool updateTriggerSet(int progress);
bool customerEnvironment(int progress);
bool filesToUpdate();
bool updateFiles(quint8 percent);
bool syncCustomerRepositoryAndFS();
bool sendIsmasLastVersionNotification();
bool saveLogFile();
private:
PSAInstalled getPSAInstalled();
void privateUpdate();
std::optional<QString> getApismVersion();
};
//Q_DECLARE_METATYPE((QHash<QString, QString>))
//Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE(QHash)
#endif // WORKER_H_INCLUDED