Compare commits

..

912 Commits

Author SHA1 Message Date
0fe9ebef66 Make sure that if everything fails, to check /etc/dc for thedc-binary. 2025-10-02 15:36:09 +02:00
d15593c7aa Add doxygen documentation for dcCanditateDc(). 2025-10-02 15:35:05 +02:00
6f60265c74 Only use DEFAULT_INSTALL_DIR, not DEFAULT_INI_DIR. 2025-10-02 15:34:23 +02:00
1c10d9bc14 Mior: add comment line 2025-10-02 15:32:18 +02:00
0bca22f995 make sure dcDir can be overwritten on commandline. however, not if usb/sd was detected. 2025-10-02 15:31:36 +02:00
67eeade609 add command-line parameter "debug" 2025-10-01 14:36:28 +02:00
559298331b dcCandidateToInstall(): pass mount path as second parameter to prevent code duplication. 2025-09-29 13:06:14 +02:00
4f1efcf5cf Check if USB/SD is mounted and get dc-controller from etc/dc. 2025-09-29 13:04:47 +02:00
3047df8130 fix wrong path for command line parser 2025-09-29 13:01:52 +02:00
c783b7a967 remove run()-methode as not a thread anymore. 2025-09-29 11:27:45 +02:00
e4be41e670 Minor: add doxygen comments 2025-09-18 13:54:30 +02:00
b2ecd1b628 Minor: define structure for doxygen-documentation 2025-09-18 13:53:12 +02:00
fb4da1f208 Add Doxyfile for generating documentation 2025-09-18 13:52:10 +02:00
ec4e327899 ignore documentation directory 2025-09-18 13:50:04 +02:00
8621c1dd17 save prepared tools 2025-09-18 13:49:40 +02:00
0ddfc1d7e3 use as build dir for docu. 2025-09-18 12:02:42 +02:00
e95f034538 service calling on_update_failure.sh 2025-09-18 12:00:44 +02:00
72fbd9089a Minor: add doxygen comment 2025-09-16 13:26:35 +02:00
3bdc117c01 Define worker as usual qobject. Thread functionality not necessary anymore:
remove corresponding run() method, i.e. privateTupdate().
2025-09-16 13:08:33 +02:00
12eafc2878 start to add doxygen-comments 2025-09-16 13:07:27 +02:00
9abe718010 send update ACTIVATED command 3x to ISMAS -> reordering with 100% should be impossible now 2025-09-15 13:24:35 +02:00
3b6f22e93b move commndline_parser to common/ 2025-09-15 13:23:29 +02:00
34cc077937 move commandline_parser to ../common/ 2025-09-15 13:12:57 +02:00
ba1b2263d2 IMPORTANT: Use gitea repository url 2025-08-26 14:37:30 +02:00
1f99618ba5 Add helper functions -> QCommandLineParser reported some warnings 2025-08-26 14:36:39 +02:00
df4e384d9d Minor: define constants for case of clone 2025-08-26 14:34:50 +02:00
ede8a9519f Minor: show file in qtcreator 2025-08-26 14:33:28 +02:00
903977bdd1 set correct working directory for git commands except 'clone' 2025-08-26 14:32:49 +02:00
775e9b7b8a Use custom command line parser.
Checkout branch after cloning.
2025-08-26 14:31:26 +02:00
a8fdd29666 use custom command line parser 2025-08-26 14:23:14 +02:00
70d0ffb38d activate opkg with --noaction 2025-08-25 14:00:56 +02:00
f623b36421 after dc-download, check if device-controller reboots successfully 2025-08-25 13:55:49 +02:00
f415406672 test without ppid -> access etc/ in customer-repo 2025-08-25 13:54:44 +02:00
2c1afa463f download jsons after sync 2025-08-21 13:27:06 +02:00
927b488582 Mior: removed debug output 2025-08-21 13:26:11 +02:00
7712803ad6 Set pid into news to ISMAS (used by Christian to reorder events correctly) 2025-08-21 11:27:22 +02:00
bc88a9b0be Use ATBUpdateTool.ini for all child-processes (ATBUpdateDC etc.) 2025-08-21 11:03:51 +02:00
8b3f0991f7 Minor: add helpers lastFailedUpdateStep() and setLastFailedUpdateStep(). 2025-08-21 10:56:50 +02:00
8123526e11 Minor: add helper size() 2025-08-21 10:56:13 +02:00
57a093e9ae No timeout for running processes. 2025-08-21 10:55:33 +02:00
10536c81a5 Send to UPDATE SUCCEEDED to ISMAS when nothing else to do. 2025-08-21 10:54:18 +02:00
659dc69831 Replace TEST_DC_DOWNLOAD with m_debug. 2025-08-21 10:51:16 +02:00
a47dd60e9e Replace TEST_DC_DOWNLOAD with m_debug. 2025-08-20 11:15:09 +02:00
575a740692 Minor: init. m_debug to false. 2025-08-20 11:12:56 +02:00
e22d78cba8 read ATBUpdateDCsettings in *.ini file 2025-08-20 11:11:02 +02:00
ad8b9f26c0 add sections for child-applications 2025-08-20 11:10:17 +02:00
5b5a4504fa use ca-slave-plugin to download jsonfiles to DC 2025-08-20 10:03:11 +02:00
9ed8603dfb DCUpdate runs before download jsons and before sync. with ptu-filesystem. Show this in main-window. 2025-08-19 10:57:19 +02:00
7d367f7fa9 renamed target in *.pro 2025-08-19 10:30:39 +02:00
fc306a45f7 Renamed target ATBDownloadDCJsonFiles -> ATBUpdateJsonFiles (consistency). 2025-08-19 10:29:29 +02:00
df6f33a259 Add define for TEST_DC_DOWNLOAD and add corresponding code
to test the download without actual dc-controller on the PTU.
2025-08-19 10:14:12 +02:00
716543ec8c "after dc_download restart autorequest or else E255" 2025-08-18 13:06:25 +02:00
0f355c0c50 "shortened time to sendnextblock." 2025-08-18 13:04:50 +02:00
36a4d53df6 "increased BL_start Timer (500 -> 1000) " 2025-08-18 08:55:24 +02:00
b9e9688269 startBootloader():
set wait time to 1000ms.
    set try counter to 10.
    a message to ask for general bl installation (in hardware)
2025-08-13 11:16:15 +02:00
f5611cf826 turn dc-download on 2025-08-12 11:59:07 +02:00
8d5831286d testing... 2025-07-07 13:00:34 +02:00
2ab485454c make sure to delete QDebug before termination 2025-04-11 12:32:30 +02:00
cbb81b7260 Add update initial 2025-04-11 12:31:51 +02:00
487bbb8160 minor: add another constant 2025-04-11 12:30:46 +02:00
d36c7c1f72 provide for update logging using a file watcher 2025-04-11 12:30:08 +02:00
cf3e1d61dd provide for update logging using a file watcher 2025-04-11 12:29:38 +02:00
e12181dc5a start to implement progress bar 2025-04-11 12:28:53 +02:00
d1b3b81972 define file for update log 2025-04-11 12:27:35 +02:00
ac03f4ae91 define line for log-entry 2025-04-11 12:26:29 +02:00
0c0debb790 include headers 2025-04-11 12:25:57 +02:00
6fc62863c1 --ismas-connected: try to reach ismas 10x. Use "Broker" and "ISMAS" keys
in json-data. Timeout between tries: 5secs.
2025-03-07 13:50:40 +01:00
0dd731ff50 added new constants 2025-03-07 13:49:12 +01:00
19445ec4f5 minor 2025-03-07 13:48:47 +01:00
739010ba59 readyReadStandardOutput():
parse outout of ATBUpdateDC.
2025-03-07 12:20:40 +01:00
7ace073d12 finished():
start summary timer when binary  has finished.
2025-03-07 12:19:54 +01:00
e36ff70dd9 minor: rename name of binaries 2025-03-07 12:18:32 +01:00
57e65e2234 change for testing 2025-03-07 12:16:35 +01:00
79af890be6 set version to 2.0.0 2025-03-07 12:15:52 +01:00
24b83840cd handling of summary after installation 2025-03-07 12:15:16 +01:00
53946457ab minor: stepLabel format: rich text. 2025-03-07 12:11:41 +01:00
c3947e49ab cleaning up source code. move summary-handling to main-window. 2025-03-07 12:10:54 +01:00
7ecd45b547 cleaning up source. rename doUndate() to run(). 2025-03-07 12:09:07 +01:00
e7d8365c62 read ini-file and determine device controller to install 2025-03-07 12:06:03 +01:00
c304d1a78b readSettings()
read ini-file for binary (e.g. ATBUpdateTool, ATBUpdateDC etc).
	1: using optional path
	2: under /etc...
	3: under /opt...
2025-03-07 11:50:58 +01:00
a8d4ae3aee Minor: renamed binary to ATBUpdateShow 2025-03-07 11:44:48 +01:00
0fe318b291 preparing dc-update 2025-02-28 14:22:51 +01:00
ec08e04f2c return negative error codes 2025-02-26 16:58:31 +01:00
b26b65ee76 take over some code from mainwindow 2025-02-26 16:57:57 +01:00
674b572da5 to be removed 2025-02-26 16:57:01 +01:00
9a6ac85f99 use qcoreapplication -> no window or widgets 2025-02-26 16:56:33 +01:00
ae582b75d5 remove main-window (not needed) 2025-02-26 16:55:40 +01:00
3069c3bd65 Add and use m_updateSteps. 2025-02-24 16:16:00 +01:00
b5067ce044 Minor: add some constants 2025-02-24 16:14:23 +01:00
3573279dfa first implementation for sync-tool 2025-02-24 16:13:43 +01:00
39b5551e90 continued... 2025-02-21 13:39:46 +01:00
05974fd8ee started documentation 2025-02-21 13:10:36 +01:00
2189684cad Minor: use constants in internal:: 2025-02-21 12:43:15 +01:00
f4d785ea9d Minor: use namespace internal:: 2025-02-21 12:42:13 +01:00
5bae9fcdf1 Minor: save result in m_commandResult. 2025-02-21 12:41:26 +01:00
eddf67739a Minor: renamed constants with prefix E_ 2025-02-21 12:39:53 +01:00
cb28bd2a1f Minor: added new constants 2025-02-21 12:39:23 +01:00
618430bcc5 Minor: added aux function 2025-02-21 12:38:54 +01:00
037b91b889 Update customer repository instead of Check... 2025-02-21 12:38:28 +01:00
96e3b606ef Use defined codes and strings. 2025-02-21 12:37:00 +01:00
5d3849dcf3 Update not necessary between 0:00 - 4:00. 2025-02-21 12:33:39 +01:00
8c66e03ea3 start docu 2025-02-21 12:32:51 +01:00
43f60251ef finished(): send messages to ISMAS 2025-02-20 16:02:21 +01:00
226dbbd050 Removed direct call to dcUpdate(). 2025-02-20 16:00:37 +01:00
6cd14d6559 Minor: Use macro for directory of .pro file. 2025-02-20 15:57:35 +01:00
4c8c3ed3c0 Minor: changed executable names used internally 2025-02-20 15:57:05 +01:00
7258976528 Add ismas-client object for update 2025-02-20 15:56:06 +01:00
d7a4d98a29 Add new constant for progress in percent. 2025-02-20 15:54:06 +01:00
c339941585 Minor: removed some comments 2025-02-20 14:35:03 +01:00
744ad52c89 Start ATBUpdateSync 2025-02-18 15:25:03 +01:00
87ce6a7d54 Start ATBUpdateSync 2025-02-18 15:24:43 +01:00
b25b66395f Support for ATBUpdateOpkg 2025-02-18 14:56:13 +01:00
de9182bdc9 Call ATBUpdateOpkg 2025-02-18 14:55:17 +01:00
af83c11f73 Changes for ATBUpdateOpkg 2025-02-18 14:54:41 +01:00
7cdefc9b49 add verbose parameter 2025-02-18 14:53:54 +01:00
dd0d7790e0 Add some more constants 2025-02-18 14:53:07 +01:00
b23314a1f3 add verbose parameter 2025-02-18 14:52:41 +01:00
9f1f41e4b6 add signal declarations for opkg 2025-02-18 14:52:16 +01:00
522f86aaac add helper variable for updating steplabel 2025-02-18 14:51:30 +01:00
13259ba86e add new signals for opkg 2025-02-18 14:50:50 +01:00
63a959315b add files for ATBUpdateOpkg 2025-02-18 14:49:52 +01:00
c05db3b323 Set up ATBUpdateOpkg 2025-02-18 14:49:01 +01:00
bc21ede1a1 set up ATBUpdateOpkg 2025-02-18 14:47:43 +01:00
8db818f6cd checkin for saving current state 2025-02-14 13:20:42 +01:00
ef9cc23093 Start with git-directory 2025-02-07 13:44:02 +01:00
3a259f8a22 First version of ATBUpdateCheck 2025-02-07 13:32:15 +01:00
b270a9f30e Update main-window 2025-02-06 16:26:44 +01:00
238f2498b7 provide (dummies) for receiving responses from binaries 2025-02-06 16:25:50 +01:00
44585f2c59 start ATBUpdateCheck 2025-02-06 16:25:09 +01:00
c98b01d002 fix typo 2025-02-05 16:30:48 +01:00
31aa7c0584 add directory for check binary 2025-02-05 16:28:14 +01:00
f750f76dbb renamed 2025-02-05 16:27:30 +01:00
aa88a040ba list of commands to execute 2025-02-05 16:27:09 +01:00
3358e1a0d4 start to refactor update-procedure 2025-02-05 16:26:16 +01:00
ea1d858f15 add headers/sources 2025-02-05 16:25:47 +01:00
7b4bee524f adding commnds to execute 2025-02-05 16:25:01 +01:00
b6f0615e9c directory for binary calling rsync on repository 2025-02-05 16:24:22 +01:00
fbedaab3fe directory for binary displaying status of ptu 2025-02-05 16:23:44 +01:00
8a482e328c directory for binary calling opkg-commands 2025-02-05 16:23:14 +01:00
f67b4bfa6f save for weekend 2025-01-10 14:43:14 +01:00
f1aad81a0c save for weekend 2025-01-10 14:19:43 +01:00
8f822c5383 save files with comment 2025-01-09 15:22:10 +01:00
7bfb7f5a3b save for christmas 2024-12-20 13:01:34 +01:00
2ec7b61682 Remove any reference to device controller as downloading jsons/dc-firmware
will be done by ATBDownloadDCJsonFiles and ATBDownloadDCFirmware binaries.
2024-12-06 12:30:54 +01:00
75b0b83174 Update::updateBinary():
begin with starting "/opt/app/tools/atbupdate/ATBDownloadDCFirmware".
2024-12-06 11:51:06 +01:00
79e2405738 Minor: remove unused code. 2024-12-06 11:50:25 +01:00
0ebf8b095a getCommandResult():
reset result if necessary (for instance for showing current result
	in GUI.
2024-12-06 11:48:21 +01:00
11524b8389 read dc-verion directly from binary file 2024-11-28 12:53:44 +01:00
6663866743 Mior: remove debugs 2024-11-28 12:52:50 +01:00
12aa9a734a init some vars to defaults 2024-11-28 12:52:14 +01:00
9b6f5db8fe start with downloading dc: parsing command arguments. started to implement the acrual download 2024-11-27 15:54:42 +01:00
061c57ef51 set verion to 1.5.6 2024-11-22 12:59:13 +01:00
e82417dde7 MainWindow::MainWindow():
Add status timer, to show proceeding update, so user does not exit
	application / restart machine.
2024-11-22 12:55:27 +01:00
1e271201c5 Set version to 1.5.5. 2024-11-21 09:15:33 +01:00
da66d75a45 getPSAInstalled():
call ptuPackagesVersion. Convert returned JSON-array into JsonObject
	to be appended to CMD_SENDVERSION.
2024-11-21 09:14:05 +01:00
7accabfa53 Add variables for handling ptu-package-versions and append corresponding data
to CMD_SENDVERSION.
2024-11-21 09:12:35 +01:00
201a1cbab9 privateUpdate():
Make sure opkg-commands re executed aunder certain error conditions:

	Failure of customerEnvironment(), filesToUpdate() or
	syncCustomerRepositoryAndFS().
2024-10-23 13:23:13 +02:00
9a9cce126a Minor: extended comment 2024-10-23 13:22:42 +02:00
da0fa54d28 gitPull():
Add important comment for the case that an remote host key change:
	'git pull' handles the issue itself, and continues to pull the repository
	using the ssh-key provided in the .keys/ directory.
2024-10-23 12:58:48 +02:00
1433c107b3 Remove right-justification in customer_id-name. Version: 1.5.3 2024-10-15 16:48:40 +02:00
8e0732cf95 Set version to 1.5.2. 2024-10-08 15:04:08 +02:00
8f6e1fdae7 clean up: remove .gz and .ipk files in /var/cache/opkg 2024-10-08 15:03:43 +02:00
0d00faf493 Remove hard coded (szeged) zone names 2024-07-23 09:28:54 +02:00
5beb235d92 Chop a trailing / of the repository url -> no clean path necessary 2024-07-17 14:50:32 +02:00
a4bb993217 Set version to 1.5.1 2024-07-17 14:06:18 +02:00
50357997c2 Do not use cleanPath() on an url-path 2024-07-17 14:04:53 +02:00
c02f0b08ed Set version to 1.5.0. 2024-06-28 13:01:24 +02:00
5da1eff41a Use repository-url="gitea@ptu-config.atb-comm.de:ATB/" 2024-06-28 12:58:13 +02:00
af89c9fbc1 Add SSH_GIT_COMMAND environment variable for pyu-config.atb-comm.de repository server. 2024-06-28 12:52:48 +02:00
fa7d1ba879 Allow empty lines in opkg_commands. 2024-06-28 12:50:30 +02:00
1dc7578645 Clean repository path from multiple slashes. 2024-06-28 12:49:53 +02:00
cd30cc91f0 Set version to 1.4.11. 2024-06-28 10:06:35 +02:00
6683b925aa Fix: remove hard-coded path to gitea repository (mimbach). 2024-06-28 10:03:10 +02:00
336d208906 Fix: never set the autoRequest-flag to false. 2024-06-26 13:27:39 +02:00
2da3c34d84 Set version to 1.4.10. 2024-06-26 13:26:55 +02:00
8f0a9112d6 Fix: do not set the autoRequest flag back to false. 2024-06-26 13:15:48 +02:00
25bf905a79 Use now new git-server: https://ptu-config.atb-comm.de/ATB 2024-06-14 13:36:34 +02:00
5d141b7234 Merge branch 'master' of git.mimbach49.de:GerhardHoffmann/ATBUpdateTool 2024-06-05 17:33:54 +02:00
ebb170123e Provide proto-binary 2024-06-05 10:10:21 +02:00
96bb70cfe9 Merge branch 'release_1.4.9' 2024-05-23 12:31:27 +02:00
8c1da92ff4 Make compile with yocto/bitbake 2024-05-23 12:29:07 +02:00
3e1a4ab09c Add comment for M0100 2024-05-17 12:32:53 +02:00
ee203e7672 doUpdate()/downloadJson(): send messages to ISMAS of the download
process of JSON files (DC2C_cash.json etc).
2024-05-17 12:29:02 +02:00
55c82eb509 Fix: send output to CONSOLE if directories are actually not equal. 2024-05-17 12:26:36 +02:00
f60cd9e4ef Again: improved ISMAS messages. 2024-05-17 12:26:03 +02:00
a9fb7a3630 Improve messages sent to ISMAS during update process. 2024-05-17 12:25:26 +02:00
db82f2af0d Fix: do not send a message to ISMAS about a check of the ISMAS
trigger (WAIT-button). For an automatic update this meant that
two messages have been visible in ISMAS which confused many users.
2024-05-17 12:22:38 +02:00
8b2e2d2086 For automatic updates send M0100, for user-triggered updates send U0002 = OK = Update aktivated. 2024-05-17 12:20:46 +02:00
5870f61395 Send UPDATE_SUCCEEDED (U0001 = 100%) only for user-triggered updates,
not for automatic ones.
2024-05-17 12:19:22 +02:00
aaa0f1e76c Minor: use correct string tor ISMAS message. 2024-05-17 12:18:11 +02:00
0d9a224977 Add M0100 message define. To be sent for automatic updates if not necessary. 2024-05-17 12:17:12 +02:00
d191020caa Configure new update-step values.
INSERT_ELEMENT(UPDATE_STEP::DOWNLOAD_CASH_FILE),
INSERT_ELEMENT(UPDATE_STEP::DOWNLOAD_CASH_FILE_FAILURE),
INSERT_ELEMENT(UPDATE_STEP::DOWNLOAD_CASH_FILE_SUCCESS),
INSERT_ELEMENT(UPDATE_STEP::DOWNLOAD_DEVICE_FILE),
INSERT_ELEMENT(UPDATE_STEP::DOWNLOAD_DEVICE_FILE_FAILURE),
INSERT_ELEMENT(UPDATE_STEP::DOWNLOAD_DEVICE_FILE_SUCCESS),
INSERT_ELEMENT(UPDATE_STEP::DOWNLOAD_JSON_FILE),
INSERT_ELEMENT(UPDATE_STEP::DOWNLOAD_JSON_FILE_FAILURE),
INSERT_ELEMENT(UPDATE_STEP::DOWNLOAD_JSON_FILE_SUCCESS),
2024-05-17 08:47:10 +02:00
2b68242d04 Add new macro-values to improve output sent to ISMAS:
DOWNLOAD_CASH_FILE
DOWNLOAD_CASH_FILE_FAILURE
DOWNLOAD_CASH_FILE_SUCCESS
DOWNLOAD_DEVICE_FILE
DOWNLOAD_DEVICE_FILE_FAILURE
DOWNLOAD_DEVICE_FILE_SUCCESS
DOWNLOAD_JSON_FILE
DOWNLOAD_JSON_FILE_FAILURE
DOWNLOAD_JSON_FILE_SUCCESS

Adapt percent-counters to show progress of update process.
2024-05-17 08:43:37 +02:00
bd785e7df1 Minor: changes some debug text 2024-05-16 16:44:35 +02:00
f2534c7763 Fix: updateNewsToIsmas(): format TIMESTAMP with UTC-Offset suffix. 2024-05-16 10:36:58 +02:00
d2a6ae239d Set version to 1.4.9. 2024-05-15 15:06:54 +02:00
19273daa55 Do not install an own message-handler to prevent double-debug-output in
linux-journals.
2024-05-15 15:05:49 +02:00
ca67d1d159 Ignore RECORD-messages sent by APISM which do not have JSON format. 2024-05-15 15:04:51 +02:00
7c3e4ea497 Minor: remove some debug output. Error messages still intact. 2024-05-15 15:03:47 +02:00
f8897c2950 Implement Update::getInstalledJsonVersions(). 2024-05-15 15:02:53 +02:00
94a658ce8a Add some IMPORTANT comments about the format of JSON files to be downloaded. 2024-05-15 15:02:10 +02:00
9d9865e3c7 downloadJson(): continue update-process even when downloading of some JSON file fails. 2024-05-15 15:01:16 +02:00
a7d5a8f5cd Declaration of getInstalledJsonVersions() with default parameter. 2024-05-15 14:59:08 +02:00
cafd16a744 Read versions of JSON files installed on PSA (DC!).
If version is available, use as blob-value to be visible
in ISMAS.
2024-05-15 14:57:15 +02:00
5876c50a32 Make sure that SEND-LAST-VERSION is executed exactly once. 2024-05-15 14:56:41 +02:00
3b2200c0bf Make sure that SEND-LAST-VERSION is executed exactly once. 2024-05-15 14:55:55 +02:00
e8070a9e16 set port name 2024-05-14 16:39:09 +02:00
da10d6c2f6 make compile 2024-05-14 16:38:47 +02:00
c52b276722 first draft 2024-05-14 16:38:19 +02:00
e9cb421ac5 Add first draft 2024-05-14 16:37:45 +02:00
a3aef7594e Minor: add comment 2024-05-14 12:52:09 +02:00
afbcccf785 Minor: add comment about index shift: 4 -> 5 2024-05-14 12:51:32 +02:00
90e4e21391 System::getJsonFilesOnUsbStick(): return list of JSON files for configured
directory under mount path.
2024-05-14 12:49:28 +02:00
4536284f34 getUSBMountPath(): check for directory 'dirPathUnderMountPath' under mount path. 2024-05-14 12:48:19 +02:00
a3a4c09c7f checkForUSBStick(): look for etc/psa_config in mountpath-directory. 2024-05-14 12:46:38 +02:00
a5dd9af29a Add System::allowedJsonFiles to name default JSON file names. 2024-05-14 12:45:43 +02:00
5124e1cca6 Use return value of type std::optional<QString> for
checkForUSBStick() and getUSBMountPath().
Add getJsonFilesOnUsbStick().
2024-05-14 12:43:46 +02:00
fc75585de3 Parse psa-config-directory and psa-tariff-directory. 2024-05-14 12:40:32 +02:00
1964b08349 doUpdate(): use parameter 'usbStickDetected' to use the correct JSON
filenames.
2024-05-14 12:38:31 +02:00
c0d014644c Update (conf) JSON-Files using USB stick. When plugged in, look for
JSON-files in directory psa-config-directory and load them to the PSA.
Note: these files are overwritten during a usual ISMAS-update if not
checked in into the customer repository.
2024-05-14 12:35:18 +02:00
b24c50c572 Parse psa-config-directory and psa-tariff-directory settings. 2024-05-14 12:33:40 +02:00
bbdadc9063 Include psa_config_directory and psa_tariff_directory settings. 2024-05-14 12:32:26 +02:00
dc7f839d3f Include ATBUpdateTool.ini 2024-05-14 12:31:19 +02:00
0a33d078be Set version to 1.4.8 2024-05-08 16:20:54 +02:00
1d64769094 Fix: shift jsonNr by 1 in checkDownloadedJsonVersions(); print-templates start now with 6 instead of 5. 2024-05-08 16:17:28 +02:00
2e3af64c02 getFileVersion(): use only 16 byte of buffer filled by DC with version-info
of JSON-file.
2024-05-08 16:16:13 +02:00
f8472a3b87 call update.checkJsonVersions(filesToUpdate) after doUpdate(). 2024-05-08 16:10:20 +02:00
24addad11e Implement getFileversion(). 2024-05-08 16:09:18 +02:00
5a8298a078 Implemented checkJsonVersions(). 2024-05-08 16:08:53 +02:00
52af7894db Minor: remove useless assignments. 2024-05-08 16:08:01 +02:00
a6592ffd3e Check m_hw for nullprt. 2024-05-08 16:07:27 +02:00
696db7de39 Don't call exit(), but return nullptr. 2024-05-08 16:06:14 +02:00
b31588bfab checkJsonVersions(): provide default parameters 2024-05-08 16:04:53 +02:00
6651920f4a Merge branch 'release_1.4.6' 2024-05-06 11:29:29 +02:00
ef800366e1 Make compile with yocto/bitbake 2024-05-06 10:59:31 +02:00
1acec4d876 revert to original version of the file (tag: 1.4.7) 2024-05-06 10:28:43 +02:00
46c481b862 Add worker_thread.h/.cpp 2024-05-06 06:25:03 +02:00
8945ead5d6 set setting for opening serial device 2024-05-06 06:24:25 +02:00
bc064c38c6 use only one thread 2024-05-06 06:23:52 +02:00
f741fb96f9 start main sources 2024-05-05 17:26:34 +02:00
8d1c719626 form fir mainwindow 2024-05-05 17:25:07 +02:00
b5389a599e add main window 2024-05-05 17:24:28 +02:00
ea2f42beed Add gui-form and sneder/receiver-thread source. 2024-05-05 17:23:08 +02:00
22e751e197 Minor: add comment 2024-05-03 13:35:33 +02:00
05c7d11ef8 Only create m_update-object if it is configured to update hardware (JSONs and DC). 2024-05-03 13:32:39 +02:00
63dc70dda9 Check if it has to be possible to update hardware (JSONs, DC). If yes, then exit(). 2024-05-03 13:31:04 +02:00
e5ab9b98cd Set Version to 1.4.7 2024-05-03 13:14:57 +02:00
4477647acf Check m_hw before its use 2024-05-03 13:11:33 +02:00
d64b029a2d loadDCPlugin(): never call exit() inside a library. 2024-05-03 13:08:25 +02:00
e24022a612 Set version to 1.4.6. Fix: do not send all jsons down to DC unconditionally 2024-04-29 15:05:10 +02:00
60d78790ba Minor: remove trailing ")" in msg 2024-04-29 15:03:43 +02:00
543e712c62 Minor: Replace trailing "," 2024-04-29 15:02:37 +02:00
8402fde6b5 Reformat m_versionInfo in case a version is included in the commit-message. 2024-04-29 15:00:52 +02:00
f15cc636c6 FIX: set m_pulledNewBranch = false in case no new branch has to be fetched from remote repository. 2024-04-29 14:59:10 +02:00
adaa12395e Trigger CHECK_ISMAS_TRIGGER_SUCCESS-message to ISMAS. 2024-04-29 14:58:29 +02:00
853c29e448 Set result code NO_UPDATE_NECESSARY instead of just SUCCESS. 2024-04-29 14:54:27 +02:00
78c214ceb4 CHECK_ISMAS_TRIGGER_SUCCESS: send message to ISMAS if WAIT-button as been set to "WAIT". 2024-04-29 14:52:50 +02:00
88bec387c5 Minor: formatted code for readability. 2024-04-29 14:50:43 +02:00
05b68f21c2 Add System.h/System.cpp -> to be adapted later 2024-04-26 13:05:19 +02:00
9ca394db46 Begin with fetching versions of downloaded json-files. 2024-04-26 13:03:10 +02:00
0d268a1d9d set INCLUDEPATH to use System.h/.cpp 2024-04-26 13:02:12 +02:00
da102b5ed0 Set version to 1.4.6 2024-04-26 12:53:30 +02:00
7031c0e349 Replace INSTALL_ERROR with proper error codes. 2024-04-26 12:49:06 +02:00
c528ce377d Add some new messages sent to ISMAS. 2024-04-26 12:47:44 +02:00
5e7c848fca Replaced INSTALL_ERROR by proper error codes. 2024-04-26 12:47:13 +02:00
3165d77f49 Replaced U0010 by its macro _ISMAS_CONTINUE 2024-04-26 12:46:28 +02:00
f0d2d5f958 Replaced U0010 by its macro _ISMAS_CONTINUE 2024-04-26 12:45:08 +02:00
1da4c3b224 Add _ISMAS_TEST_TRIGGER 2024-04-26 12:43:21 +02:00
f1cedbf1d5 Define new error codes (and actually use them) -> ISMAS not affected. 2024-04-26 12:41:21 +02:00
0933274c82 Add update functionality (again using ATBUpdateTool as template) 2024-04-23 16:25:36 +02:00
2aa676f18c Implement json-download tool with ATBUpdateTool as template. 2024-04-23 16:23:58 +02:00
6c0d49b90c Start with project file for json-download tool. 2024-04-23 16:22:56 +02:00
18d04d15cf Set version to 1.4.5. 2024-04-11 16:31:44 +02:00
b81120f8dc Implemented:
bool branchExistsRemotely();
	Check if branch exists in remote customer-repository.

    bool branchExistsLocally();
	Check if branch exists locally in customer-repository.

    bool gitPullNewBranches();
	In case the remote branch exists, but not the corresponding local one,
	then fetch the remote-branch.
2024-04-11 16:25:36 +02:00
8b2fcb25db Added declarations for:
bool branchExistsRemotely();
    bool branchExistsLocally();
    bool gitPullNewBranches();
2024-04-11 16:24:33 +02:00
698cf74516 doUpdate(): re-check again if there are valid data form the device-controller. 2024-04-11 16:23:08 +02:00
20681b0d6c If we have pull a new remote branch, the behave as if the customer-repo
did not exist before: especially download all json-files inside
the new local branch.
2024-04-11 16:20:46 +02:00
763647c145 If customer-repo is already existent (i.e. not cloned),
then check for the existence of remote branch (zg/zone<zone-number of psa>).
If it exists remotely, but not locally, then execute 'git pull'
(could also be 'git pull origin zg1/zone<zone-number>').
A follwing 'git checkout zg1/zone<zone-number>' will switch to the
now local branch.
2024-04-11 16:16:34 +02:00
4aad14b181 Added PULL_NEW_BRANCH, PULL_NEW_BRANCH_FAILED, PULL_NEW_BRANCH_SUCCESS and corresponding output messages. 2024-04-11 16:15:49 +02:00
0a03228dea Added PULL_NEW_BRANCH, PULL_NEW_BRANCH_FAILED, PULL_NEW_BRANCH_SUCCESS and corresponding output messages. 2024-04-11 16:14:45 +02:00
23551066c1 Set version to 1.4.4 2024-04-09 15:02:59 +02:00
24f2ba7c44 Move rsyncing the customer-repository after filesToUpdate(), as
the repository is pulled in filesToUpdate().
2024-04-09 14:56:25 +02:00
2ed6768953 Minor: remove ismasTriggerValue-(debug)-message, as it was too confusing. 2024-04-09 14:55:45 +02:00
a1327388bc execute(): Enhance debug output. 2024-04-09 14:53:50 +02:00
8d18ae10fe downLoadJson(): add more debug-output (visible in ISMAS as well)
to see what actuelly went wrong when downloading json-Files.
2024-04-09 14:51:54 +02:00
31bc2d0fa2 Minor: update comment 2024-04-09 14:49:42 +02:00
175b8fd3a1 If the update-trigger (aka WAIT-button) is empty and we have an automatic
update (i.e. time is 0:00 - 4:00 am), then do not inform ISMAS with an U0003
error signal, but send an U0002 (setting the trigger-value to "OK").
2024-03-19 12:41:42 +01:00
a57fa6c31e Add path to global device-controller library 2024-03-19 09:30:20 +01:00
c22c924e38 Add path to global device-controller library 2024-03-19 09:29:51 +01:00
bdc64920a2 Minor: remove superfluous include 2024-03-19 09:29:17 +01:00
7bc9fa0c04 Minor: fix typo 2024-03-19 09:28:37 +01:00
334da1fe4a Merge branch 'master' of https://git.mimbach49.de/GerhardHoffmann/ATBUpdateTool 2024-03-19 09:23:54 +01:00
e5054582c2 set version to 1.4.3 2024-03-19 09:20:31 +01:00
bbd287c92b Minor: comment out a line 2024-03-19 09:19:52 +01:00
5c152c9dc2 Minor: remove plugins/interfaces.h 2024-03-19 09:13:27 +01:00
367ffaa3a1 Minor: adapt header-include 2024-03-19 09:12:46 +01:00
2fd004f249 Add PATH for device-controller-library as well. 2024-03-19 09:12:07 +01:00
721a156fce Remove functionality for downloading firmware-device-controller 2024-03-15 12:59:59 +01:00
b7576d04e1 Add *.user 2024-03-15 12:58:32 +01:00
753954ecd7 Use <DeviceController/interfaces.h> 2024-03-15 12:07:15 +01:00
df7cabe2c1 Remove references to interfaces.h. Add refs to device-controller 2024-03-15 12:05:50 +01:00
cfca8d2ef4 remove interfaces.h plus CA-libs 2024-03-15 11:55:21 +01:00
829932f29d Load libCAslave.so not libCAmaster.so 2024-03-15 11:26:57 +01:00
8968667c23 Add comment. Set version to 1.4.2. 2024-03-08 08:47:54 +01:00
a5e0154757 Do not check if etc/psa_tariff and /etc/psa_tariff are equal after the rsync:
reason is that this might be not the case if the customer-number has been changed.
2024-03-08 08:44:39 +01:00
f346390631 Add UpdatePTUDevCtrl subdirectory 2024-02-29 15:02:12 +01:00
c77fa097c1 Use UpdatePTUDevCtrl.pro 2024-02-29 15:01:40 +01:00
074e60d8dc Merge remote-tracking branch 'UpdatePTUDevCtrl/dc-download' 2024-02-29 14:48:59 +01:00
8eff6e5d10 Move UpdatePTUDevCtrl files into subdir UpdatePTUDevCtrl 2024-02-29 14:41:45 +01:00
4233ca8637 Merge remote-tracking branch 'UpdatePTUDevCtrl/master' 2024-02-29 14:38:14 +01:00
052028afe8 Check if download/reporting thread are running 2024-02-29 11:30:24 +01:00
a240711946 (R)sync files of customer repository (under ./etc in the repository)
with file-susyem /etc as very forst step of the update process, even
before executing the opkg commands.
2024-02-27 13:56:38 +01:00
75fdca95d9 set version to 1.4.1 2024-02-27 13:56:24 +01:00
9b087f62f9 Check if json-file to be downloaded to device-controller is empty before
starting a download.
2024-02-17 08:04:06 +01:00
7d38cc1269 Set version to 2.0.0 2024-02-09 13:07:22 +01:00
f5baad16d5 Prepend last-commit with pid of ATBUpdateTool (to be used in ISMAS). 2024-02-09 13:02:35 +01:00
b35e6812aa getDCVersionPreparedForDownload(): implemented. 2024-02-09 13:02:11 +01:00
d7e709d997 Minor: make a 2-sec nap before starting reporting threadw 2024-02-09 13:01:15 +01:00
4d93aa1392 getDCVersionPreparedForDownload(): added for future use. 2024-02-09 12:59:25 +01:00
b0f1e0a493 Minor: reformatting code.w 2024-02-06 11:28:01 +01:00
0dffc1d1c2 Create m_update-object inside of constructor of worker-class:
this way the object belongs to the GUI-thread, and can be addressed via emit
by the reporting thread.
2024-02-06 11:25:46 +01:00
3581dd4b1d doUpdate(): start reorting thread to report download-progress to GUI. 2024-02-05 16:31:24 +01:00
8d2d7bbb15 Connect slots
void onReportDCDownloadStatus(QString const &status);
    void onReportDCDownloadSuccess(QString const &msg);
    void onReportDCDownloadFailure(QString const &errorMsg);
2024-02-05 16:30:41 +01:00
e2e7c0772d Implemented slots:
void onReportDCDownloadStatus(QString const &status);
    void onReportDCDownloadSuccess(QString const &msg);
    void onReportDCDownloadFailure(QString const &errorMsg);
2024-02-05 16:30:01 +01:00
807cea1042 Added slots:
void onReportDCDownloadStatus(QString const &status);
    void onReportDCDownloadSuccess(QString const &msg);
    void onReportDCDownloadFailure(QString const &errorMsg);
2024-02-05 16:28:34 +01:00
80712964ef Use of pointer m_update. 2024-02-05 16:26:53 +01:00
978e98b2ee removed dependency to interface.h 2024-02-05 14:53:56 +01:00
44c2820130 Make sure to lookup only "apism", not the new "apism-tools" 2024-02-02 08:18:13 +01:00
123f22cd21 add .gitignore file 2024-01-29 09:03:16 +01:00
b60af1a4fc Flowchart for ATBUpdateTool 1.4.0 2024-01-26 14:19:50 +01:00
bb3ecc4b76 Use same interfaces.h as DCLibraries (master branch) 2024-01-26 11:39:21 +01:00
fa30fe045b Minor: added interfaces.h 2024-01-26 11:38:43 +01:00
bae014822d Fix: set version info several times to reflect real current git commit 2024-01-26 11:17:06 +01:00
6c472b542e Merge remote-tracking branch 'origin/korneuburg-improvements' 2024-01-26 08:55:50 +01:00
7578746d2f make it explicit that no device-controller is loaded down with this version 2024-01-25 15:46:46 +01:00
0b1ed62df1 Optimization: if customer repository does not exist, do noy check the
ISMAS trigger, but proceed with the update procedure.
2024-01-25 15:14:42 +01:00
76ec41c291 Minor: add some comment. 2024-01-25 15:13:56 +01:00
630cd36f13 Minor: removed typo 2024-01-25 11:54:13 +01:00
cbe8bb7aeb Minor: add comment 2024-01-25 09:01:04 +01:00
e04636e3f7 Minor: add debug output 2024-01-24 13:07:39 +01:00
728185ddb9 Set
m_lastFailedUpdateStep = UPDATE_STEP::NONE
as last step in case of a successful update.
2024-01-24 08:31:22 +01:00
aaa485e2fc Send messages to ISMAS for checking/repairing the customer repository 2024-01-23 16:09:15 +01:00
9a9480035b Improve output, esecially to ISMAS 2024-01-23 15:31:12 +01:00
0a43654f9b Improve output, esecially to ISMAS 2024-01-23 15:30:31 +01:00
4dba36a420 Add and use UPDATE_STEP::UPDATE_FINALIZE in debug output. 2024-01-23 15:29:19 +01:00
f9ce6a6c1b Minor: remove obsolete line. 2024-01-23 12:22:48 +01:00
c2c6bc3f8a Minor: removed typo. 2024-01-23 12:18:10 +01:00
8c02ebcf15 Minor: debug output to GUI. 2024-01-23 12:07:42 +01:00
100bfd63ab Do not write to GUO when running in loop to check ISMAS trigger button 2024-01-23 12:03:46 +01:00
23ff4977d9 Disable exit button after checking update trigger button. 2024-01-23 12:02:18 +01:00
8da6443833 Send additional messages to ISMAS about recovering the customer-repository 2024-01-19 13:58:08 +01:00
6b3ebde2b5 Check for automatic update and add a hint in SEND-LAST-MESSAGE. 2024-01-19 13:40:24 +01:00
41392a98e3 Minor: added comment. 2024-01-19 13:11:12 +01:00
1c9bb11f0b Moved final processing into destructor of subclsee UpdateProcessRunning. 2024-01-19 13:06:58 +01:00
ec0e687c21 Adapt step order: check of ISMAS trigger is done with percent value 2. 2024-01-19 13:06:11 +01:00
e040e784fc Add members
bool m_updateNotNecessary = false;
    QStringList m_ismasTriggerStatusMessage;
2024-01-19 13:05:46 +01:00
d2300b87c6 Move final processing to subclass UpdateProcessRunning. 2024-01-19 13:04:12 +01:00
0dff2ece75 Check ISMAS update trigger as very first thing.
Save the result in m_ismasTriggerActive for later use.
2024-01-19 13:02:59 +01:00
db24853062 Initialize new member m_ismasTriggerStatusMessage. 2024-01-19 13:01:46 +01:00
530ea33460 Minor: debug output. 2024-01-19 08:29:03 +01:00
005e4d249f Set m_lastFailedUpdateStep (future use for downloading the firmware-device-controller). 2024-01-19 08:27:07 +01:00
1c0786e28c Minor: Add NONE constant (for UPDATE_NECESSARY). 2024-01-19 08:24:54 +01:00
0aa8d9ba5a Check if update-process was really necessary, i.e. NOT activated
by an automatic nightly update.
2024-01-19 08:22:03 +01:00
1ecb844b64 Added for future use when updating device-controller-frimware:
hwinf *m_hw = nullptr;
    UPDATE_STEP m_lastFailedUpdateStep = UPDATE_STEP::NONE;
2024-01-19 08:20:21 +01:00
9a687e6628 Minor: add some debug output 2024-01-19 08:16:26 +01:00
979afa37d3 Minor: add comment 2024-01-17 15:38:05 +01:00
1e1820724d Set version to v1.4.0. Set compile-option -O (FORTIFY_SOURCE). 2024-01-17 15:32:26 +01:00
d90954c6eb Add location, version and info to send-last-version object. 2024-01-17 15:30:34 +01:00
cf77d0ff76 Initialize m_versionInfo and send its contents to ISMAS in the update-process. 2024-01-17 15:28:49 +01:00
73d02d214a Send contant of m_versionInfo(0) (git commit of repository) to ISMAS. 2024-01-17 15:26:53 +01:00
6a67d8e9b0 Minor: update percent numbers in some cases. 2024-01-17 15:26:14 +01:00
30d8cc3684 Add m_version_info as memeber. 2024-01-17 15:25:35 +01:00
bc9ebb7d68 Implemented helpers:
QString getLocation(QString fileName);
    QString getTariffVersion(QString fileName);
    QString getTariffInfo(QString fileName);

Read project location, tariff location and tariff-info
from tariff[].json file.
2024-01-17 15:18:03 +01:00
e6f6d43bf2 Adden helpers:
QString getLocation(QString fileName);
    QString getTariffVersion(QString fileName);
    QString getTariffInfo(QString fileName);
2024-01-17 15:17:16 +01:00
854c8b9706 Minor: add some additional debug output 2024-01-10 09:58:37 +01:00
d521fd977a Using interface.h, verion 4.4, from 20230802. set version to v.1.3._25_ 2023-12-21 13:02:12 +01:00
ebbdc2f864 Use interface file from 20230802, version 4.4 2023-12-21 13:00:00 +01:00
62496c5d95 Prepare special version for szeged. set version to 1.3._24_ 2023-12-20 12:27:29 +01:00
edd606fe78 Change for szeged (dc-version: 4.42): do not check if dc is alive.
Comment out some functions which are not available in old interface.h
2023-12-20 12:20:06 +01:00
1748c35c45 Add interfaces.h as given for 26.Sep 2023 (szeged) 2023-12-20 12:18:02 +01:00
4c46932a3c Specail version for szeged (dc-version: 4.42): use master lib. 2023-12-20 12:16:07 +01:00
686e113c3a Fix: add break when checking dc alive status. set version to 1.3.24. 2023-12-19 14:30:58 +01:00
6ddfbbfd9e add a break to prevent possible endless loop 2023-12-19 14:25:59 +01:00
7bfbdc0f07 Update flowchart for UpdateTool. 2023-12-19 13:05:10 +01:00
dd591fdd23 Amde some preparations for sendLastVersion 2023-12-17 16:25:31 +01:00
18c7b656c3 Add some new points. 2023-12-17 15:23:00 +01:00
5e9b05e887 Set version to 1.3.23. 2023-12-15 09:48:14 +01:00
37aae73f21 Fix: Make sure the path for the json-files and the decive-controller is correct. 2023-12-15 09:39:46 +01:00
0b4eed9dc0 doUpdate(): add another level in if-case when downloading json-files. 2023-12-15 09:38:01 +01:00
12f48ad1bb Remove only half-working check if download of json-conf-file worked. 2023-12-15 09:33:57 +01:00
5d7d1a2870 Implement helpers getFileVersions() and checkDownloadedJsonVersions(). 2023-12-15 09:32:54 +01:00
5d7f13a254 Future: add getFileVersion() and checkDownloadedJsonVersions() to
check if download of Json-File worked and to ask what Json-Version
is actually installed
2023-12-15 09:30:02 +01:00
d332a990d5 Turn on automatic download of json-files. 2023-12-15 09:27:49 +01:00
bfa39eb3df Set version to 1.3.22.
After git clone: execute full update process (modulo settings in ATBUpdateTool.ini).
2023-12-13 14:00:21 +01:00
d7fcfa3d0f Click the Exit button after 5 seconds timeout (old timeout: 60s). 2023-12-13 13:59:00 +01:00
f1e449c108 Change known filename of device controller: dc2c.bin. 2023-12-13 13:57:46 +01:00
32346c2665 If the repository is cloned (or repaired and cloned) and the settings
always-download-config and always-download-dc are set in the ini-file,
then download the json-files and dc-file, even without an activated
WAIT-button. The tariff-files are always synced for a clone.
2023-12-13 13:49:22 +01:00
38e7bf4985 Minor: call rsync with -v instead of -vvv. 2023-12-13 13:48:23 +01:00
6df73e1082 Minor: change some debug output. 2023-12-13 13:47:02 +01:00
19250a0a2f If the update process is activated without a valid ISMAS trigger, then
wait for a valid trigger value 15x (=90s) instaed of 100x.
2023-12-13 13:44:55 +01:00
6f5c8103e4 When checking the sanity of the customer repository, check also for existence
of etc-directory inside repository.
2023-12-13 13:42:12 +01:00
07dcf0ba30 Minor: add some GUI debug output when checking ISMAS trigger. 2023-12-13 13:41:06 +01:00
1e379cf086 Fix: set directory of application for directory of the ini-file. 2023-12-13 13:38:34 +01:00
e87456f26b set version to 1.3.21 2023-12-13 10:06:04 +01:00
0c9a7bc7b5 let ISMAS know if some opkg commands fail 2023-12-13 10:04:38 +01:00
a35c6afcb8 Minor: add debug output if some opkg commands fail 2023-12-13 10:03:37 +01:00
b06f4f46bb Update interfaces.h (DeviceController) 2023-12-08 13:02:23 +01:00
33d45eab2e Merge branch 'master' of git.mimbach49.de:GerhardHoffmann/UpdatePTUDevCtrl 2023-12-08 13:01:03 +01:00
d2e11d8f07 Replace the last line of the text edit when the new line is
the last line followed by a suffix.
2023-11-29 14:18:01 +01:00
fbffdc923f Minor: When appendig text, scroll down the text edit. 2023-11-29 14:17:28 +01:00
8faf5af2bf Typo: replace m_files_to_update with m_files_to_download. 2023-11-29 12:07:25 +01:00
1d532c13a4 Minor: show files to update with a direct qCritical() 2023-11-29 12:06:17 +01:00
d2d3afc28e Show debug output in text-edit only when at least one file to update. 2023-11-29 12:05:09 +01:00
f4bb201633 Minor: Add debug message to console 2023-11-29 11:37:08 +01:00
b68cf1fd1d Set version to 1.3.20. 2023-11-29 11:30:50 +01:00
0888c1b525 Read machine-nr, customer-nr and zone-nr from /mnt/system_data/
rather then the deprecated /etc-directory.
2023-11-29 11:29:52 +01:00
a6faf9ce21 Init triggerValue with "NOT CHECKED YET" otherwise one might be confused. 2023-11-29 11:28:35 +01:00
5577cc5d6d Add m_sys_areDCdataValid plus its handling 2023-11-24 14:08:54 +01:00
9967d5cc45 Add comment 2023-11-24 14:08:19 +01:00
e20ed57bd5 Set version to 1.3.19 2023-11-21 16:18:17 +01:00
6986007b16 Set all parameters to default values other than "" 2023-11-21 16:17:15 +01:00
9ecdf73bc0 Minor: output all command parameters 2023-11-21 16:16:28 +01:00
59d6c49cb2 Set version to 1.3.18 2023-11-21 15:27:34 +01:00
f6a8059e2d Activated downloading of json to device 2023-11-21 15:25:20 +01:00
4372cb578b Added additional output to CONSOLE() and ISMAS(). 2023-11-21 15:23:28 +01:00
d683a8fc32 Added some comments. 2023-11-21 10:09:52 +01:00
4e92522578 Activate downloading device controller. 2023-11-21 10:09:04 +01:00
b45f3a04b4 Add debug output (for testing, and commented out) in downloadJson(). 2023-11-21 10:08:31 +01:00
0db39746db Aupdate updateBinary() to download device controller using new library fucntions. 2023-11-21 10:07:50 +01:00
f2844aa4d9 Remove obsolete functions: sendStatus(), sendNextAddress(), sendNextDataBlock(),
dc_downloadBinary(), startBootloader(), stopBootloader().
2023-11-21 10:01:11 +01:00
d4043bd7d2 Remove obsolete functions:
DownloadResult sendStatus(int ret) const;
    DownloadResult sendNextAddress(int bNum) const;
    DownloadResult sendNextDataBlock(QByteArray const &b, int bNum) const;
    DownloadResult dc_downloadBinary(QByteArray const &binary) const;

    bool startBootloader() const;
    bool stopBootloader() const;

and updateDC().
2023-11-21 09:56:36 +01:00
89b639c0ed Add handling of alwaysDownloadConfig and alwaysDownloadDC.
Not tested and commnetd out.
2023-11-21 09:54:57 +01:00
1ad13d9a8a Add:
bool const m_alwaysDownloadConfig;
    bool const m_alwaysDownloadDC;
2023-11-21 09:53:28 +01:00
6f2cbb0a26 Set default values for boolean flags. 2023-11-21 09:50:40 +01:00
c15cebf503 Add parsing for alwaysDownloadConfig and alwaysDownloadDC. 2023-11-21 09:49:47 +01:00
5ee1308c9d Add parsing for alwaysDownloadConfig and alwaysDownloadDC. 2023-11-21 09:48:23 +01:00
746d96ca7c Add parsing for flags: alwaysDownLoadDC and alwaysDownloadConfig. 2023-11-21 09:46:11 +01:00
f387eaedea Added flags: always-download-config and always-download-dc.
Download DC and Json files even without any change in customer-repository.
Not tested by now.
2023-11-21 09:44:15 +01:00
0fd977c399 Fix copy-paste-error: set working-directory 2023-11-20 13:49:48 +01:00
d53d72c536 Minor: added comment. 2023-11-17 13:14:02 +01:00
f4be1f3f51 Check if setting was given on the command line. If so, then overwrite setting given in ini-file. 2023-11-17 13:13:04 +01:00
75136e41f4 Set version to 1.3.17 2023-11-16 14:19:19 +01:00
4007bc585e Add command line parser and ATBUpdateTool.ini 2023-11-16 14:17:20 +01:00
8fff5026df Use own command line parser. 2023-11-16 14:16:31 +01:00
792ff33482 Add repository url parameter 2023-11-16 14:15:05 +01:00
1d8b4ce191 Add repository url parameter 2023-11-16 14:14:48 +01:00
bc864c7e8b Add command line parsing. 2023-11-16 14:13:49 +01:00
8bc86c6a94 Add command-line parameters. 2023-11-16 14:13:12 +01:00
35bd2743b2 Add ini-file for future use 2023-11-16 10:17:23 +01:00
f45017e9f3 update interfaces.h 2023-11-16 10:13:26 +01:00
4d47a5f95c Remove unused files. There is an own repo: DCLibraries. 2023-11-16 10:10:56 +01:00
1ab625ca05 delete obsolete file 2023-11-16 10:08:38 +01:00
e1a26ef966 update libCAmaster.so 2023-11-16 10:06:23 +01:00
da31e1eda6 add ca-slave-lib 2023-11-16 10:04:40 +01:00
a206ba8c83 Add interfaces.h (needed for libCA*.so) 2023-11-16 10:03:27 +01:00
1c5ab2aa7d Minor: add comment 2023-11-10 13:20:20 +01:00
39deef760b Use default dummies so we can load hwapi 2023-11-10 13:20:00 +01:00
6079d9143f Set version to 1.3.16 2023-11-07 11:44:22 +01:00
c1cfca79d2 Fixed lost updating of progressbar of GUI 2023-11-07 11:43:26 +01:00
903d0206a5 add flow-chart for ATBUpdateTool 2023-11-07 11:40:30 +01:00
e163b9561e Added comment. Set tag for version 1.3.15. 2023-11-07 11:06:49 +01:00
990d257b09 Make sure the dc-data are vaild: set again the auto-request flag inside the CA-plugin.w 2023-11-07 09:33:42 +01:00
dfbad69ab1 Add some comments. Set version to 1.3.15. Removed worker_thread.h/.cpp. 2023-11-07 09:24:32 +01:00
5f0c86ba19 Minor: removed obsolete test-code. 2023-11-07 09:23:43 +01:00
3588b25e65 Removed any references to CA-plugin from MainWindow. 2023-11-07 09:18:04 +01:00
1f8b88b2b6 Update-object now proper memeber of worker-thread.w 2023-11-07 09:14:49 +01:00
0050ea35d0 Update to new interfaces.h 2023-11-07 09:13:15 +01:00
7e4c138d1b Fixed getDCVersion() as part of turning worker-object into its own
thread.
2023-11-07 09:12:17 +01:00
685568d4f6 Call execOpkgCommands(0 and downloadTpPSAHardware() helpers. 2023-11-07 09:11:42 +01:00
14b4c035da Add computeFilesToUpdate() and downloadFilesToPSA() helper functions. 2023-11-07 09:10:45 +01:00
90de2f415e Add new members to worker-class. 2023-11-07 09:10:01 +01:00
3cc71cb69b Minor: remove obsolete code. 2023-11-07 09:09:26 +01:00
fef7533d00 Minor: replace APPLY_... with DOWNLOAD... enum-variables. 2023-11-07 09:08:33 +01:00
e93058cc6b Turn worker-object into a thread. Don't do a moveToThread() anymore. 2023-11-07 09:07:08 +01:00
e65387aa60 Turned worker-object into a thread 2023-11-07 09:04:05 +01:00
904fa0374b Minor: removed empty line 2023-11-07 09:00:25 +01:00
4bf1bbe81f Removed references to update and ca-plugin.w 2023-11-06 16:23:43 +01:00
3ccdcbae51 Removed generation of upodate-object. This will be donw inside
worker(-thread), and only when it is really needed, i.e. when there
are json-files to be updated (or a deveice-controller).
2023-11-06 16:20:27 +01:00
8c50e6cf59 Added command-line option no-psa-hardware-update (future use for
multipass).
2023-11-06 16:19:36 +01:00
7e69846169 Minor: removed obsolete code (commented out). 2023-11-06 16:15:31 +01:00
34c55c576c Use new masterlib. NOTE: furture versions have to use the slave lib. 2023-11-03 13:50:22 +01:00
7c2c4d4b80 Use new interface file. 2023-11-03 13:49:56 +01:00
f9f698fd15 Save for the weekend: set version to 1.3.15. 2023-11-03 13:49:28 +01:00
b4457d8815 Fix debugging output. 2023-11-03 13:46:23 +01:00
3621777827 Do not disable the exit button. 2023-11-03 13:45:54 +01:00
003bd0bf77 Check if repository is corrupted: do not check for etc/ or
opt/-directories, as they may be not existent inside of the repository.
2023-11-03 13:44:15 +01:00
721c5dd7a5 MAke sure removeDuplicates() is called for list containing file to be
updated.
2023-10-31 09:16:24 +01:00
a24eb9fd8c Minor: removed unused, obsolete code (commented out). 2023-10-31 09:14:02 +01:00
24351b8342 Minor: add runtime information in debug output. 2023-10-31 09:12:43 +01:00
4b3a39b0e6 Set version to 1.3.14.
Add compile flag -C (so it is possible to use /*fall through*/ without
having the proprocessor to remove this comment.
2023-10-30 15:35:38 +01:00
a44b780d93 Minor: Add header for using RAII in sending SEND-LAST-VERSION to ISAMS. 2023-10-30 15:29:23 +01:00
19dfae9b56 Use output functions ISMAS(), GUI() and CONSOLE(), which point to friend
operators<< in Worker-class.
2023-10-30 15:28:25 +01:00
bef0d4fe12 Refactor the update process: streamline code. When WAIT button is not
active, the a clone or a fix of a defunct repository is possible, but
not more.
If WAIT button is active, the at least the opkg_commnds are executed. If
there are chenged files, they are handkled as wll.
2023-10-30 15:25:49 +01:00
72a2fc781c Minor: reove obsolete m_returnCode. 2023-10-30 15:24:33 +01:00
fc264689b1 Use and implement getAPISMYoctoVersion() and
getAPISMYoctoInstallationSTatus().
2023-10-30 15:22:59 +01:00
fc587456d5 Filling the QMap Worker:smap with data. 2023-10-30 15:21:26 +01:00
a2b933ab71 Minor: Added UPDATE_STEP_WRONG. 2023-10-30 15:20:15 +01:00
7d0fdf4d6d Small fixes:
1: fixed wrong parameters of a connect().
2: Minor: improved onQuit().
2023-10-30 15:17:59 +01:00
c2ce44c79b Adding three friend operator<< functions to output status information of
the update process: the first on outputs to CONSOLE(), the second one
outputs to ISMAS() and the last one to GUI(), i.e. the text edit of the
main window.
2023-10-30 15:15:30 +01:00
16a9556863 bool customerEnvironment();
bool filesToUpdate();
bool updateFiles();
bool syncCustomerRepositoryAndFS();
bool saveLogFile(): remove progress parameter.
2023-10-30 15:14:19 +01:00
48896f97ec Adding UPDATE_STEP_WRONG: helper variable to be used in text edit of
main window.
2023-10-30 15:13:04 +01:00
4486317cb2 dd declaration for getAPISMYoctoVersion() and
getAPISMYoctoInstallationStatus(): the first one returning the
yocto-version
of APIS, the second returning the installation status of APIS as display
in 'opkg info'.
2023-10-30 15:11:06 +01:00
6b9b88ea19 Adding member variables to check if customer repository was a fresh
clone, or if it was already existent.
2023-10-30 15:09:33 +01:00
99a99d95a1 Adding UPDATE_STEP enum plus associated interger defines.
Adding QMap: Worker::smap, which contains names of UPDATE_STEP-members.
2023-10-30 15:07:28 +01:00
d4ddbbee21 Add class UpdateProcessRunning: use for sending SEND-LAST-VERSION as
part of its destruktor: RAII-pattern.
2023-10-30 15:05:30 +01:00
34e5189945 comment out some debug output -> too much output. 2023-10-30 15:03:35 +01:00
c44c805238 Use some special output methods using ISMAS(), GUI() nad CONSOLE(). 2023-10-30 15:00:57 +01:00
196f1a730e gitPull(): don't use regex anumore, but just plain
string-matching/searching.
Regex seems to be quite error-prone.
2023-10-30 14:59:58 +01:00
7dc04c4422 Add helper function worker(), returning the worker-object. 2023-10-30 14:56:19 +01:00
5efac2619b sicherung der aenderungen 2023-10-23 16:16:11 +02:00
9b0f741b9b to be continued on monday 2023-10-22 19:44:45 +02:00
81c5f8ee7e Save for the weekend. 2023-10-20 13:55:18 +02:00
29e6a25e72 Add comment for version 1.3.13 2023-10-19 13:45:52 +02:00
5abc057bda Minor: add possible debug messages. 2023-10-19 13:44:51 +02:00
8aeb7ecfea Don't check opkg_commands-file in the system-filesystem to remove
confusing error-messages.
2023-10-19 13:43:53 +02:00
4bb8e241b6 Update process: check sanity of customer repository: are etc/ and
opt/directories contained? If not, remove the repository and clone it
again. This is done without checking the ISMAS-WAIT-button.
2023-10-19 13:41:44 +02:00
4469a23f9c Implemented helpers:
bool isRepositoryCorrupted();
    bool repairCorruptedRepository();

    int sendCloneAndCheckoutSuccess();
    int sendCloneAndCheckoutFailure();
    int sendIsmasTriggerFailure();
2023-10-19 13:39:48 +02:00
d1f795e2db Added helpers:
bool isRepositoryCorrupted();
    bool repairCorruptedRepository();

    int sendCloneAndCheckoutSuccess();
    int sendCloneAndCheckoutFailure();
    int sendIsmasTriggerFailure();

and static variables

    static constexpr const int CLONE_AND_CHECKOUT_SUCCESS = 0;
    static constexpr const int CLONE_AND_CHECKOUT_FAILURE = -3;
    static constexpr const int ISMAS_TRIGGER_FAILURE = -5;
2023-10-19 13:38:16 +02:00
6865056f4b Replace regex in gitCloneCustomerRepository() with straight-forward
steing-handling: regex sometimes returned error.
2023-10-19 13:35:07 +02:00
37bd5c90d3 Add printUpdateStatus() helper for QString 2023-10-19 13:34:07 +02:00
fcba120dfa Minor: commented out unnecessary debug/info output 2023-10-18 16:21:44 +02:00
1d4f50fb9f Add printing-utils that also take string-lists 2023-10-18 16:20:49 +02:00
a78040a037 Minor: add error-debug-message in ase customer-nr is wrong 2023-10-18 16:19:37 +02:00
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
f963b61ebc Added helper functions. Update will be used heavily by the Worker-class in a later stage. 2023-07-14 13:34:48 +02:00
9ed51d60e4 Added helper functions. 2023-07-14 13:33:46 +02:00
f5198efab3 Added worker/worker-thread-pair.
Worker uses event-loop of worker-thread.
Worker itself is used as work-horse for the update-process.
2023-07-14 13:32:00 +02:00
d6446f90fe Removed C++-thread-handling, replaced it with Qt-versions. Using explicit Worker
instance instead.
2023-07-14 13:29:52 +02:00
58bceb5d27 Started ISMASClient providing the data to be sent to ISMAS.
It does not send to ISMAS itself.
2023-07-14 13:28:41 +02:00
92084bed99 Added call to waitForConnected() when doing a connectToHost().
Replaced some qCritical() with qDebug()-calls.
2023-07-14 13:27:14 +02:00
cd1c92a7db Copied from ATBQT app.
Added slots requestAvailableIsmasUpdates(), sendCmdSendVersionToIsmas() and
sendUpdateInfoToIsmas().
2023-07-14 13:24:14 +02:00
9ca758ecd3 Added ISMAS_PARAMETER for ISMAS::REQUEST 2023-07-14 13:21:02 +02:00
3c54d8de6d Made GitClient a Qt-object. Impreoved several utility functions handling git commands.
Added slot onIsmasUpdatesAvailable().
Added helpers getLastCommit(), gitBlob(), gitCommitForBlob(), gitIsFileTracked().
2023-07-14 13:17:45 +02:00
d91d4261d9 Add another utility function and added signal/slot ismasUpdatesAvailable/onIsmasUpdatesAvailable(). 2023-07-14 13:13:03 +02:00
00f5216a9f Added utility functions. 2023-07-14 13:12:37 +02:00
aeae9002fe Made GitClient a Qt-Object. 2023-07-14 13:10:55 +02:00
9df425f5f8 Use Qt to format debug-info. Note that the full info is visible only in debug-mode. 2023-07-14 12:58:23 +02:00
5149a67d4b Provide the possibility to pass parameters to a command using a string-list.
Has to be improved later.
2023-07-14 12:56:33 +02:00
1309c27f7c Added ismas_client.h/.cpp. Added c++17-flag. 2023-07-11 16:59:49 +02:00
26db620465 use of worker/worker-thread so we can work without using buttons (as the cannot be triggered by an automatic update process) 2023-07-11 16:58:49 +02:00
bd213b8f8c start 2023-07-11 11:24:23 +02:00
31b7bd1314 Made split() a static member function. 2023-07-10 16:00:34 +02:00
0bdbd39632 Added directories for apism/command/process. 2023-07-10 15:59:28 +02:00
b979fb5b2a command-class as abstraction for executing git-commands. 2023-07-10 15:57:59 +02:00
cbefccd2d3 Add first commands for using AUBUpdateTool as git-client 2023-07-10 15:57:17 +02:00
0ebe274e9a Add files copied from ATBQT application 2023-07-10 15:56:20 +02:00
c4d09eb2ea Add -e (execute-script-only) and -d (dry-run) options 2023-07-06 16:54:01 +02:00
3039fcc553 remove superfluous \n 2023-07-06 16:52:32 +02:00
a67e587769 Activate download of printer templates 2023-07-06 15:14:52 +02:00
d89520d58e Activate updating device-controller-firmware. 2023-07-06 14:32:57 +02:00
06a9eba177 Turn auto-request ON in any case. 2023-07-06 14:32:20 +02:00
a41dc5403f Open serial port only if not already opened. 2023-07-06 14:31:32 +02:00
85b2c1f08e Write update_log.csv.copy in different format. 2023-07-06 14:30:48 +02:00
ba8dd7d083 updateDC(): stop boot loader even when not ven able to start it. 2023-07-06 14:28:46 +02:00
fa04e32266 When sending dc-data, fill the last block with 0xFF to stream-line the source code. 2023-07-06 14:27:44 +02:00
7d6b433373 Removed obsolete code. 2023-07-06 14:26:58 +02:00
faae8b8d9a Add aux. function isSerialOpen(). 2023-07-06 14:25:36 +02:00
a0d0de19c5 Use QThread::sleep() instead of std-C++-code. 2023-07-06 14:24:19 +02:00
17eaa7858f Add handling of test-mode. 2023-07-06 14:23:04 +02:00
f2556412d8 Comment some lines. 2023-07-06 14:21:29 +02:00
26557542f1 Added test-mode. Added aux. function isSerialOpen(). 2023-07-06 14:13:33 +02:00
6bb46e165c Add handling of VERSION. Add test-mode. 2023-07-06 14:12:41 +02:00
027529161d Add VERSION to be used inside application 2023-07-06 14:11:28 +02:00
61c847102d Open/empty update_log.csv/.csv.copy at start. Added some debug-output 2023-06-29 12:42:16 +02:00
3034f49c96 Minor change: add comment 2023-06-29 12:40:47 +02:00
528b74549a Aopen serial port only when necessary, i.e. when downloading device controller
or json-files.
2023-06-27 17:32:13 +02:00
c7acc2a99b Add comment for the procedure of downloading device controller. 2023-06-27 17:31:38 +02:00
61afdfc325 resetDeviceController(): use bl_rebootDC(). 2023-06-27 17:30:56 +02:00
c34944af8b Add slots for catching output of underlying update_psa_script.
iSetting timeout to 200000 when in maintenance-mode.
2023-06-27 17:29:10 +02:00
58684cf3c4 Add new slots readyReadStandardOutput/Error and finished to get messages
from included update_psa-script.
2023-06-27 17:25:23 +02:00
08122cf703 Update interfaces.h 2023-06-22 18:17:46 +02:00
fdd32cea92 Remove interfaces.h for update interfaces.h 2023-06-22 18:16:41 +02:00
7c17090a2b Remove deployment rules (conflict on yocto) 2023-06-22 17:54:11 +02:00
3de46ea099 Add Config PTU5_YOCTO 2023-06-22 17:46:09 +02:00
f46ac4075a activate only handling of opkg-commands 2023-06-22 15:43:21 +02:00
0668ab65be Added helper-function downloadJson() to implement downloadinf print/conf/cash/device.json. Added some compile time flags used for testing. 2023-06-22 15:36:36 +02:00
f66ae498ce Adding methods for downloading DC2C_cash/conf/device.json 2023-06-22 15:20:21 +02:00
e420f95eb0 Fix: do not print an empty buffer ending with \n 2023-06-22 15:19:17 +02:00
809440ba95 Added comment 2023-06-22 15:18:20 +02:00
c6ea94e249 Added some commnts. 2023-06-21 16:25:53 +02:00
4cee0e37da executeUpdateScript: set timeout to 200 secs. Open serial port only once for the whole loop handling the lines in update_log.csv. Sending of device controller in Debug-setting worked. Execution of opkg-commnds worked. Sending of JSON files needs further testing: sys_readyForSending failes sometimes after 5 seconds. 2023-06-21 16:25:21 +02:00
9c343f3eeb Added planned methods updateConf() and updateCashConf(). Changed interface of updateDC() and updatePrinterTemplate() 2023-06-21 16:19:45 +02:00
e79d6c6fef Added maintenance-mode parameter. Activated handling of opkg-commands. 2023-06-20 16:14:13 +02:00
88013e82f0 Added maintenance-mode parameter 2023-06-20 16:13:11 +02:00
079a6910dd Added flag for maintenance-mode 2023-06-20 16:08:42 +02:00
bbf97dc58d network-setting not necessary. Make console-setting explicit. 2023-06-20 16:08:02 +02:00
810b603d70 Added updateDC() and updatePrinterTemplate(). Prepared for live testing. 2023-06-19 16:01:29 +02:00
8281303a55 Added updateDC() and updatePrinterTemplate() 2023-06-19 16:00:22 +02:00
a4afeeb396 Added Qt-commandline parser 2023-06-19 15:59:17 +02:00
bc3a801b8d Renamed ATBUpdateDC to ATBUpdateTool 2023-06-19 15:58:15 +02:00
f52dec9124 Just for saving 2023-06-16 16:54:21 +02:00
07942153c3 Add newest dc-library 2023-06-16 16:53:02 +02:00
bac848f6be update interface.h to fit newest version of dc-controller-software 2023-06-16 16:52:22 +02:00
ccc1bf2a99 Add functionality from hwapi to download device controller to dc-hardware 2023-06-16 16:51:30 +02:00
c8487a7541 fixed handling of debug levels 2023-06-16 16:49:20 +02:00
dc90705f24 re-implement to use dc-plugin as shsred onject 2023-06-16 16:48:58 +02:00
c4c503080e Do not use DCPlugin subtree anymore 2023-06-16 16:47:56 +02:00
e2d9c5aa90 Comit helper-classes for now 2023-06-16 16:47:13 +02:00
60f845aa8d generated from tex file 2023-06-16 16:45:03 +02:00
fa0c31d9e4 save for the weekend 2023-06-02 14:17:48 +02:00
57b82094c8 removed handling of tariff, as this is not downloaded to dc 2023-05-30 16:49:48 +02:00
af337874a6 removed handling of tariff, as this is not downloaded to dc 2023-05-30 16:47:00 +02:00
f016e9777e Added default parameter to split() 2023-05-30 16:44:55 +02:00
85bec3364f deleted obsolete file 2023-05-26 13:04:43 +02:00
35d40e3b3c Add handling of update-script 2023-05-26 13:03:38 +02:00
71a6b82d58 Add env-var XDG_RUNTIME_DIR 2023-05-26 13:02:45 +02:00
de75ef32d9 start docu for update_ptu 2023-05-26 13:01:44 +02:00
3bac92c711 Add subtree add command to make sure DCPlugin is included 2023-05-25 10:21:42 +02:00
2b5fca73a2 removed c++-headers. 2023-05-22 16:06:52 +02:00
6d4c247de7 Using update.h/.cpp and thread pool 2023-05-22 16:04:50 +02:00
892fae92ad Using Utils-class and QRunnable.w 2023-05-19 15:33:40 +02:00
3938051c32 Add Utils-class to provide most of the previous functionality. 2023-05-19 15:32:52 +02:00
18e4811d57 Added some debug output.
Send only one DC_print-template.
Tested again downloading dc-firmware and printer-templates.
2023-05-18 14:26:23 +02:00
8d325c20eb Merge commit '6099d07650acb28df1a5c9659479351daa7c2d27' 2023-05-17 16:08:57 +02:00
6099d07650 Squashed 'DCPlugin/' changes from d992ee3..e32142c
e32142c Implement cash input interface
f611e07 Implement printing ticket
6478eda TS: pr_printTemplate(): send 'longFDcmd_set()'
1663d09 Merge with TS 21.04.23
080c00e Set serialPort name from config
bbce2b0 Test printing ticket
8ff17a2 DeviceControllerInterface: erroCode is a string
c657428 Project: including DCPlugin.pri globally
f0f0493 hwapi: remove unused dependency to QWidget
9bf99c5 Fix: make plugin compile
8ff8faf Fix: warning
09a8049 Merge branch 'master' into pu/integration
21fb07b Merge branch 'master' of git.mimbach49.de:GerhardHoffmann/DCPlugin
01f8c1e First compiling version for high level vending interface
3029b8d Rename TARGET name
2143801 Configure project for PTU5-YOCTO
6f6d3b7 Add atb/qt gitignore
4cfb8f1 Add auto version generation script

git-subtree-dir: DCPlugin
git-subtree-split: e32142cd6277cf647d27a9c0a2e9e4d7c6ca4269
2023-05-17 16:08:57 +02:00
0c14002f7f remove update printer template 2023-05-03 09:00:02 +02:00
6765bb13e5 Fixed printing od ticket: dc_printTemplate(). First send dynamic data, then use prn_sendDynamicPrnValues() 2023-05-03 08:59:17 +02:00
ceedf72b34 iAdd some test data. 2023-04-19 14:22:28 +02:00
8a216c43c0 Squashed 'DCPlugin/' changes from 01140c5..d992ee3
d992ee3 Added dc_getStatus() to send info to ISMAS
b7d8fab Added dc_getStatus() to send info to ISMAS
7129805 Added dc_getStatus() to send info to ISMAS

git-subtree-dir: DCPlugin
git-subtree-split: d992ee3fad831f8bc159b87e687390a233b41afb
2023-04-18 15:07:55 +02:00
9620d58373 Merge commit '8a216c43c05db28340cf5e4272bf2e66c6a868e9' 2023-04-18 15:07:55 +02:00
2c6fa31cff Squashed 'DCPlugin/' changes from 1eb2ac3..01140c5
01140c5 Using std::min from algorithm-header
c99d073 Added additional variables into shared memory. Cleaned source.
81c610c Included additional variables into shared memory
6ae7c06 Changed to const-pointer

git-subtree-dir: DCPlugin
git-subtree-split: 01140c523b600912e83302c988059c1cfd0a5488
2023-04-18 14:44:24 +02:00
073482d1a9 Merge commit '2c6fa31cff30e193dc542183b8a5aec022100d79' 2023-04-18 14:44:24 +02:00
ee902898c4 Squashed 'DCPlugin/' changes from f3adba2..1eb2ac3
1eb2ac3 Use const-pointer

git-subtree-dir: DCPlugin
git-subtree-split: 1eb2ac3a1df01a7af3244f6fe495a8b143d946bf
2023-04-18 13:56:04 +02:00
4c21c439a2 Merge commit 'ee902898c4bed3a7ecdc4954094e535cf1af1e67' 2023-04-18 13:56:04 +02:00
acde999b9d Squashed 'DCPlugin/' changes from 652ad9b..f3adba2
f3adba2 Change parameter type from uint8_t to bool

git-subtree-dir: DCPlugin
git-subtree-split: f3adba2f0ff7a574039c23b44cee9872d0f25d34
2023-04-18 13:53:23 +02:00
928e7de9ac Merge commit 'acde999b9db601dcb93e86639da5127cfc10ac79'. 2023-04-18 13:53:23 +02:00
8b301a91dc Squashed 'DCPlugin/' changes from 3e3e1ef..652ad9b
652ad9b Included changes of TS for version 3.5.
c85b090 Added ulong2uchar (version 3.5).
87a6ed0 Include changes of Thomas for version 3.5.
99c88c7 Included changes of Thomas for version 3.5. prn_getPrintResult(),
712ea0f Included changes of Thomas for version 3.5 of library: datif_templatePrintFinished_OK(), datif_templatePrintFinished_Err(), datif_gotNewCoin().
86311de Add use of shared memory. Add changes for version 3.4. of library.
cb44127 Added check4freeFDshortCmd.
a95e174 Included some changes of Thomas.
c724b5b Shift several device controller related variables to shared memory.
ef79321 Massive change: using shared memory containing most device controller realted variables.
a7aa75a Massive change: using shared memory containing devcie controller variables.
dc00c69 Added check4freeFDlongCmd.
eeb3519 Adding shared memory containing all variables necessary to check device controller related variables.
30338e2 Changed struct T_moduleCondition. Changed struct T_dynamicCondition. Added struct T_extTime. Added log_chkIfVaultRecordAvailable(). Added sys_getDeviceConditions(). set version to 3.4.
58fdea4 Added m_sharedMem. Added cash_getAmountInVault. Added cash_getNrCoinsInVault. Added sys_getDynMachineConditions() with new interface. Added sys_getDeviceConditions() with new interface. Added log_chkIfVaultRecordAvailable().
df760f1 Added shared_mem_buffer.h/.cpp.

git-subtree-dir: DCPlugin
git-subtree-split: 652ad9b43d24bddbc3c2636c4ff4b5d74e081e46
2023-04-18 13:43:37 +02:00
981d9706e2 Merge commit '8b301a91dc0d4ae7a4ddd5f470016b4cd68b85c4'. 2023-04-18 13:43:37 +02:00
ff5e3f15a4 Squashed 'DCPlugin/' changes from e5a8cfd..3e3e1ef
3e3e1ef Make sure the printer functions can be called without explicitly openeing the serial device.

git-subtree-dir: DCPlugin
git-subtree-split: 3e3e1efe7d77c71f0101f35c8b38419e877f5636
2023-04-14 09:06:56 +02:00
04dfc407c0 Merge commit 'ff5e3f15a47f165005e691a88cbe1fa44a84fe0d'
Use a defualt parameter for printer functions to decide if serial device has to be
opened or not.
2023-04-14 09:06:56 +02:00
39f5e2af10 Use PTU5 define. 2023-04-14 09:06:45 +02:00
b15ad441f8 Merge commit 'a9e69cd0c4ad3477a99becaf87b84e0d9b23538f' 2023-04-13 16:28:43 +02:00
a9e69cd0c4 Squashed 'DCPlugin/' changes from 60c4858..e5a8cfd
e5a8cfd copy of include/interfaces.h
5e6e601 Minor change
f3d9c69 Added dc_printTemplate. Fixed implementation of dc_updatePrinterTemplate
3f10469 Added dc_printTemplate. Changed interface for updating printer templates.
d92bdbf Added dc_printTemplate. Changed interface for updating printer templates.

git-subtree-dir: DCPlugin
git-subtree-split: e5a8cfd1cd6a0ece3db295669c801b8e2ea9332b
2023-04-13 16:24:43 +02:00
ead91eaba0 test implementation 2023-04-13 16:22:46 +02:00
84729f90fc Squashed 'DCPlugin/' changes from ef48301..60c4858
60c4858 Fixed template number: cannot be 0

git-subtree-dir: DCPlugin
git-subtree-split: 60c48588b09dd680d6d92ab6944904dc6a28ca90
2023-04-13 14:03:52 +02:00
f114570a8c Merge commit '84729f90fc46ef46dc26eeab2843dbc6d5266787'
Merged in fix for template number.
2023-04-13 14:03:52 +02:00
649f8ce0d0 Added draft version for updating json-files 2023-04-13 14:03:25 +02:00
1f0f7da128 Squashed 'DCPlugin/' changes from 37f0012..ef48301
ef48301 Added dc_updatePrinterTemplate
5f7d34e implemented dc_updatePrinterTemplate
863e4b2 Added dc_updatePrinterTemplate

git-subtree-dir: DCPlugin
git-subtree-split: ef48301dad80ebce3db4764613bb7f183ef69485
2023-04-13 13:39:17 +02:00
eaa6df0da7 Merge commit '1f0f7da128687daf09d065e60e254fbd24f2545d'
Added dc_updatePrinterTemplate.
2023-04-13 13:39:17 +02:00
db8415cd3e Squashed 'DCPlugin/' changes from 82accc4..37f0012
37f0012 converted to utf-8

git-subtree-dir: DCPlugin
git-subtree-split: 37f0012ce9092b4152481264747500d91f5c160e
2023-04-13 11:35:31 +02:00
0139e3dde7 Merge commit 'db8415cd3edd8b7484f2842cd0d8bffd2f759597'
Use interface.h converted to UTF-8.
2023-04-13 11:35:31 +02:00
91c907ba50 Squashed 'DCPlugin/' changes from c1b9452..82accc4
82accc4 Added file types for JSON as enum

git-subtree-dir: DCPlugin
git-subtree-split: 82accc4b1a46c29b5d93a897a09fcd208cfb6cc2
2023-04-13 11:28:18 +02:00
622237afeb Merge commit '91c907ba506fc03e4f9e2a02bca17c312e184462'
Synchronize with DCPlugin.
2023-04-13 11:28:18 +02:00
b741d89ceb Use interfaces from DCPlugin 2023-04-13 11:28:00 +02:00
b62f71b49b Use proper interfaces.h 2023-04-13 11:27:20 +02:00
073506d244 Add printer update 2023-04-13 11:22:32 +02:00
f214e0ff43 Squashed 'DCPlugin/' changes from debade9..c1b9452
c1b9452 Added/merged from sources of Thomas (version CashAgentLib 3.3, 12.04.2023)

git-subtree-dir: DCPlugin
git-subtree-split: c1b945225bbdc9f254fff82ed69cf0194b7bc40b
2023-04-13 10:58:17 +02:00
1b5eaef37a Merge commit 'f214e0ff43594ebf48a00f8debb96613c307c309'
fetch sources for CashAgentLib 3.3.
2023-04-13 10:58:17 +02:00
dc2584e007 Remove inclusion of unnecessary headers. 2023-04-11 15:39:48 +02:00
750e7a13f7 remove obsolete files 2023-04-11 15:39:08 +02:00
c000971f14 No need to load plugin, as DCPlugin directly included 2023-04-11 15:38:04 +02:00
8cf2141916 configure for PTU5/Desktop. Remove obsolete files. 2023-04-11 15:37:01 +02:00
0a5816454e Include DCPlugin as subtree 2023-04-11 14:06:41 +02:00
3c7aec979a Squashed 'DCPlugin/' content from commit debade9
git-subtree-dir: DCPlugin
git-subtree-split: debade99427801efa46a708cd726648c7f83a257
2023-04-11 14:04:38 +02:00
d4ccde61c1 Merge commit '3c7aec979af86ff5ba513f8b867f54dc278592df' as 'DCPlugin' 2023-04-11 14:04:38 +02:00
7512942a8e use DEBUG instead of INFO 2023-04-11 14:04:33 +02:00
153678523f using ttymxc2 instead of ttyUSB0 (bound to modem on PTU5) 2023-04-11 14:03:54 +02:00
3d3ec297c0 no need for gui 2023-04-11 14:02:51 +02:00
6043d15494 dos2unix 2023-04-05 14:45:19 +02:00
c1dac2212a add files for example 2023-04-05 14:43:27 +02:00
134 changed files with 24863 additions and 33 deletions

2
.gitignore vendored
View File

@@ -1,2 +1,4 @@
tags
*.tags
*.user
documentation/

39
ATBUpdateTool.ini Normal file
View File

@@ -0,0 +1,39 @@
[REPOSITORY_URL]
repository-url="gitea@ptu-config.atb-comm.de:ATB"
[DIRECTORIES]
plugin-directory="/usr/lib/"
working-directory="/opt/app/tools/atbupdate/"
psa-config-directory="etc/psa_config/"
psa-tariff-directory="etc/psa_tariff/"
[PLUGINS]
plugin-name="libCAslave.so"
[FLAGS]
no-psa-hardware-update=false
dry-run=false
extended-version=false
yocto-version=false
yocto-install=false
always-download-config=true
always-download-dc=false
[ATBUpdateCheck]
[ATBUpdateDC]
debug=true
workingDir=/tmp
libca=/usr/lib/libCAslave.so
[ATBUpdateGit]
[ATBUpdateJsonFiles]
[ATBUpdateOpkg]
[ATBUpdateShow]
[ATBUpdateSync]

View File

@@ -1,4 +1,3 @@
TEMPLATE = subdirs
CONFIG += ordered
SUBDIRS = DownloadDCFirmware DownloadDCJsonFiles
SUBDIRS = Check Show DownloadDCFirmware DownloadDCJsonFiles Git Opkg Sync UpdatePTUDevCtrl

82
Check/Check.pro Normal file
View File

@@ -0,0 +1,82 @@
QT += core
TARGET = ATBUpdateCheck
VERSION="1.0.0"
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")
EXTENDED_VERSION="$${VERSION}-$${GIT_COMMIT}"
INCLUDEPATH += \
plugins \
$${INCLUDEINTERFACES} \
$${_PRO_FILE_PWD_}/../common/include
CONFIG += c++17
DEFINES+=APP_VERSION=\\\"$$VERSION\\\"
DEFINES+=APP_BUILD_DATE=\\\"$$BUILD_DATE\\\"
DEFINES+=APP_BUILD_TIME=\\\"$$BUILD_TIME\\\"
DEFINES+=APP_EXTENDED_VERSION=\\\"$$EXTENDED_VERSION\\\"
# keep comments, as /* fall through */
QMAKE_CXXFLAGS += -C
QMAKE_CXXFLAGS += -g
QMAKE_CXXFLAGS += -Wno-deprecated-copy -O
contains( CONFIG, PTU5 ) {
CONFIG += link_pkgconfig
QMAKE_CXXFLAGS += -O2 -std=c++17 # for GCC >= 4.7
# QMAKE_CXXFLAGS += -Wno-deprecated-copy
PTU5BASEPATH = /opt/devel/ptu5
INCLUDEPATH += $$PTU5BASEPATH/qt/libs/devicecontroller/include
LIBS += -L$$PTU5BASEPATH/qt/libs/devicecontroller/library
ARCH = PTU5
DEFINES+=PTU5
}
contains( CONFIG, PTU5_YOCTO ) {
QMAKE_CXXFLAGS += -std=c++17 # for GCC >= 4.7
# QMAKE_CXXFLAGS += -Wno-deprecated-copy
PTU5BASEPATH = /opt/devel/ptu5
ARCH = PTU5
DEFINES+=PTU5
}
contains( CONFIG, DesktopLinux ) {
# QMAKE_CC = ccache $$QMAKE_CC
# QMAKE_CXX = ccache $$QMAKE_CXX
QMAKE_CXXFLAGS += -std=c++17
# QMAKE_CXXFLAGS += -Wno-deprecated-copy
linux-clang { QMAKE_CXXFLAGS += -Qunused-arguments }
ARCH = DesktopLinux
DEFINES+=DesktopLinux
}
SOURCES += \
main.cpp \
ismas_client.cpp \
message_handler.cpp \
../common/src/utils_internal.cpp
HEADERS += \
ismas_client.h \
message_handler.h \
../common/include/utils_internal.h
##########################################################################################
# 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
}

378
Check/ismas_client.cpp Normal file
View File

@@ -0,0 +1,378 @@
#ifdef __WIN32__
#error "WIN32 NOT SUPPORTED"
#else
#include "ismas_client.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
# U0010 for update process
# $2: PERCENT : "only for ISMAS: 0-100%",
# $3: RESULTCODE : "only for ISMAS",
# 0: Success
# 1: no Update nessesary
# 2: Backup failed
# 3: Package error/ Wrong package
# 4: Install Error
# $4: STEP : "running step (only for us): update_psa...",
# $5: STEP_RESULT : "error and result text",
# $6: VERSION : "opkg and conf info; what will be updated"
#
#endif
#include <QDateTime>
#include <QDebug>
void IsmasClient::printDebugMessage(int port,
QString const &clientIP,
int clientPort,
QString const &message) {
#if 0
Q_UNUSED(port);
Q_UNUSED(clientIP);
Q_UNUSED(clientPort);
Q_UNUSED(message);
#else
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;
#endif
}
void IsmasClient::printInfoMessage(int port,
QString const &clientIP,
int clientPort,
QString const &message) {
#if 0
Q_UNUSED(port);
Q_UNUSED(clientIP);
Q_UNUSED(clientPort);
Q_UNUSED(message);
#else
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;
#endif
}
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, bool verbose) {
if (verbose) {
qInfo() << "REQUEST" << request;
}
int sockfd;
int r;
errno = 0;
// socket create and verification
if ((sockfd = ::socket(AF_INET, SOCK_STREAM, 0)) == -1) {
if (verbose) {
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) {
if (verbose) {
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);
if (verbose) {
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) + ")");
// }
if (verbose) {
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
if (verbose) {
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
if (verbose) {
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 {
if (!response.contains("RECORD")) {
// maybe APISM does not send valid JSON: "RECORD SAVED" etc.
printDebugMessage(port, clientIP, clientPort,
QString("PARSE ERROR ") + response + " " + parseError.errorString());
}
::close(sockfd);
return std::nullopt;
}
}
return std::nullopt;
}
char const *IsmasClient::reason[REASON::ENTRIES] = {
"TIME-TRIGGERED", "SERVICE", "DEV-TEST"
};
#endif // __WIN32__

51
Check/ismas_client.h Normal file
View File

@@ -0,0 +1,51 @@
#ifndef ISMAS_CLIENT_H_INCLUDED
#define ISMAS_CLIENT_H_INCLUDED
#include <QObject>
#include <QString>
#include <optional>
class IsmasClient : public QObject {
Q_OBJECT
public:
explicit IsmasClient() = default;
enum APISM {
DB_PORT = 7777,
DIRECT_PORT = 7778
};
enum RESULT_CODE {
SUCCESS=0,
// if between 00:00 - 04:00 Wait-button state not WAIT, then we assume
// that's an automatic nightly (not-necessary) update
NO_UPDATE_NECESSARY=1,
// if APISM reports the ISMAS is not available (15x, 6s delay each)
ISMAS_NO_CONNECTION_ERROR=2,
// if not within 00:00-04:00 and WAIT-button was not in state WAIT
ISMAS_TRIGGER_ERROR=3,
};
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, bool verbose=false);
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

180
Check/main.cpp Normal file
View File

@@ -0,0 +1,180 @@
#include <QtGlobal>
#include <QCoreApplication>
#include <QByteArray>
#include <QProcess>
#include <QCommandLineParser>
#include <QStandardPaths>
#include <QSettings>
#include <QDir>
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QRegularExpression>
#include <QFile>
#include <QTextStream>
#include <QDateTime>
#include <QThread>
#include <optional>
#include "message_handler.h"
#include "utils_internal.h"
#include "ismas_client.h"
int main(int argc, char **argv) {
QByteArray const value = qgetenv("LC_ALL");
if (value.isEmpty() || value != "C") {
qputenv("LC_ALL", "C");
}
openlog("ATB-UPDATE_CHECK", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER);
QCoreApplication a(argc, argv);
QCoreApplication::setApplicationName("ATBUpdateCheck");
QCoreApplication::setApplicationVersion(APP_VERSION);
if (!messageHandlerInstalled()) { // change internal qt-QDebug-handling
atbInstallMessageHandler(nullptr);
//atbInstallMessageHandler(atbDebugOutput);
setDebugLevel(LOG_NOTICE);
}
int exitCode = 0;
QCommandLineParser parser;
QCommandLineOption ismasConnectOption("ismas-connected");
QCommandLineOption updateRequestedOption("update-requested");
QCommandLineOption verboseOption("verbose");
parser.addOption(ismasConnectOption);
parser.addOption(updateRequestedOption);
parser.addOption(verboseOption);
parser.process(a);
QString connectionStatus{internal::ISMAS_NOT_CONNECTED};
QString updateRequestStatus{internal::UPDATE_NOT_REQUESTED};
{
QDebug debug = qCritical();
int tries = 10;
while (--tries > 0) {
if (std::optional<QString> result
= IsmasClient::sendRequestReceiveResponse(
IsmasClient::APISM::DIRECT_PORT,
"#M=APISM#C=REQ_SELF#J={}")) {
QJsonDocument d = QJsonDocument::fromJson(result.value().toUtf8());
for (QString const &k : d.object().keys()) {
if (k.contains("CMD_GET_APISMSTATUS_RESPONSE")) {
QJsonObject o = d.object()[k].toObject();
QJsonObject::const_iterator bit = o.find("Broker");
QJsonObject::const_iterator ismit = o.find("ISMAS");
if (bit != o.constEnd() && ismit != o.constEnd()) {
// value for "Broker"
QString const &v = bit->toString();
bool ismas = ismit->toBool();
if (ismas && v.contains(internal::BROKER_CONNECTED, Qt::CaseInsensitive)) {
connectionStatus = internal::ISMAS_CONNECTED;
break;
} else
if (v.contains(internal::BROKER_NOT_CONNECTED, Qt::CaseInsensitive)) {
connectionStatus = internal::ISMAS_NOT_CONNECTED;
} else
if (v.contains(internal::BROKER_DISCONNECTED, Qt::CaseInsensitive)) {
connectionStatus = internal::ISMAS_NOT_CONNECTED;
} else
if (v.contains(internal::BROKER_CONNECTION_IN_PROGRESS, Qt::CaseInsensitive)) {
connectionStatus = internal::ISMAS_NOT_CONNECTED;
} else
if (v.contains(internal::BROKER_DISCONNECTING, Qt::CaseInsensitive)) {
connectionStatus = internal::ISMAS_NOT_CONNECTED;
}
break;
}
}
}
}
if (connectionStatus == internal::ISMAS_CONNECTED) {
break;
}
QThread::sleep(5);
}
if (connectionStatus != internal::ISMAS_CONNECTED) {
if (internal::customerRepoExists() == false) {
debug.noquote() << internal::NO_CUSTOMER_REPOSITORY;
}
}
if (parser.isSet(updateRequestedOption)) {
if (internal::customerRepoExists() == false) {
// if the customer repository does not exists, it does not matter is
// ISMAS is connected or how the setting for the WAIT-button is.
updateRequestStatus = internal::UPDATE_INITIAL;
} else {
if (connectionStatus == internal::ISMAS_CONNECTED) {
if (std::optional<QString> result
= IsmasClient::sendRequestReceiveResponse(
IsmasClient::APISM::DIRECT_PORT,
"#M=APISM#C=REQ_ISMASPARAMETER#J={}")) {
QJsonDocument d = QJsonDocument::fromJson(result.value().toUtf8());
for (QString const &k : d.object().keys()) {
if (k.contains("REQ_ISMASPARAMETER")) {
QJsonObject o = d.object()[k].toObject();
QJsonObject::const_iterator it = o.find("Aknoledge");
if (it == o.constEnd()) continue;
QString const &v = it->toString();
if (v != "OK") break;
for (QString const &m : d.object().keys()) { // request ack
if (!m.contains("FileUpload", Qt::CaseInsensitive)) continue;
QJsonObject o2 = d.object()[m].toObject();
QJsonObject::const_iterator it2 = o2.find("TRG");
if (it2 == o2.constEnd()) break;
QString const &v2 = it2->toString();
if (v2 == "WAIT") {
updateRequestStatus = internal::UPDATE_REQUESTED;
} else {
// the customer-repository does exist, and the ISMAS-trigger is
// *NOT* "WAIT", but from 00:00:00 - 00:03:59 this counts as an
// automatic update
QDateTime const &current = QDateTime::currentDateTime();
if (current.time().hour() < 4) {
updateRequestStatus = internal::UPDATE_NOT_NECESSARY;
} else {
updateRequestStatus = internal::UPDATE_NOT_REQUESTED;
exitCode = -2;
}
}
break;
}
break;
}
}
}
} else {
// not connected (so its unknown if update has been requested),
// and customer repository exists. Assume 'not requested'.
updateRequestStatus = internal::UPDATE_NOT_REQUESTED;
exitCode = -1;
}
}
debug.noquote() << updateRequestStatus;
} else
if (parser.isSet(ismasConnectOption)) {
debug.noquote() << connectionStatus;
}
}
return exitCode;
}

97
Check/message_handler.cpp Executable file
View File

@@ -0,0 +1,97 @@
#include "message_handler.h"
#include <QDateTime>
#include <cstring>
#include <QString>
#include <QFileInfo>
#include <QMessageLogContext>
static char const *DBG_NAME[] = { "DBG ", "WARN ", "CRIT ", "FATAL", "INFO " };
static bool installedMsgHandler = false;
static int debugLevel = LOG_NOTICE;
int getDebugLevel() { return debugLevel; }
void setDebugLevel(int newDebugLevel) {
debugLevel = newDebugLevel;
}
bool messageHandlerInstalled() {
return installedMsgHandler;
}
QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler) {
installedMsgHandler = (handler != 0);
static QtMessageHandler prevHandler = nullptr;
if (handler) {
prevHandler = qInstallMessageHandler(handler);
return prevHandler;
} else {
return qInstallMessageHandler(prevHandler);
}
}
///
/// \brief Print message according to given debug level.
///
/// \note Install this function using qInstallMsgHandler().
///
/// int main(int argc, char **argv) {
/// installMsgHandler(atbDebugOutput);
/// QApplication app(argc, argv);
/// ...
/// return app.exec();
/// }
///
#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) {
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 LOG_NOTICE: { // normal, but significant, condition
if (type != QtDebugMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
case LOG_WARNING: { // warning conditions
if (type != QtInfoMsg && type != QtDebugMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
case LOG_ERR: { // error conditions
if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
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());
}
}
}
#endif

23
Check/message_handler.h Executable file
View File

@@ -0,0 +1,23 @@
#ifndef MESSAGE_HANDLER_H_INCLUDED
#define MESSAGE_HANDLER_H_INCLUDED
#include <QtGlobal>
#ifdef __linux__
#include <syslog.h>
#endif
int getDebugLevel();
void setDebugLevel(int newDebugLevel);
bool messageHandlerInstalled();
QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler);
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
// typedef void (*QtMessageHandler)(QtMsgType, const char *);
void atbDebugOutput(QtMsgType type, const char *msg);
#elif QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
// typedef void (*QtMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &);
void atbDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg);
#endif
#endif // MESSAGE_HANDLER_H_INCLUDED

View File

@@ -1,7 +1,6 @@
QT += core
QT += serialport network
QT += core serialport
TARGET = ATBDownloadDCFirmware
TARGET = ATBUpdateDC
VERSION="0.1.0"
win32 {
@@ -16,6 +15,16 @@ GIT_COMMIT=$$system("git log -1 --format=oneline | cut -d' ' -f1")
EXTENDED_VERSION="$${VERSION}-$${GIT_COMMIT}"
!contains(CONFIG, INCLUDEINTERFACES) {
INCLUDEINTERFACES=/opt/ptu5/opt/DCLibraries/include
}
INCLUDEPATH += \
plugins \
$${INCLUDEINTERFACES} \
$${_PRO_FILE_PWD_}/../UpdatePTUDevCtrl \
$${_PRO_FILE_PWD_}/../common/include
CONFIG += c++17
DEFINES+=APP_VERSION=\\\"$$VERSION\\\"
@@ -34,6 +43,11 @@ contains( CONFIG, PTU5 ) {
lessThan(QT_MAJOR_VERSION, 5): PKGCONFIG += qextserialport
QMAKE_CXXFLAGS += -O2 -std=c++17 # for GCC >= 4.7
# QMAKE_CXXFLAGS += -Wno-deprecated-copy
PTU5BASEPATH = /opt/devel/ptu5
INCLUDEPATH += $$PTU5BASEPATH/qt/libs/devicecontroller/include
LIBS += -L$$PTU5BASEPATH/qt/libs/devicecontroller/library
ARCH = PTU5
DEFINES+=PTU5
}
@@ -61,14 +75,32 @@ contains( CONFIG, DesktopLinux ) {
}
SOURCES += \
main.cpp
main.cpp \
../common/src/message_handler.cpp \
../common/src/commandline_parser.cpp \
update.cpp \
../common/src/System.cpp \
../common/src/utils_internal.cpp \
../common/src/command.cpp
HEADERS += \
../common/include/message_handler.h \
../common/include/commandline_parser.h \
update.h \
../common/include/System.h \
../common/include/utils_internal.h \
../common/include/command.h
# HEADERS +=
OTHER_FILES += \
ATBDownloadDCFirmware.ini
ATBDownloadDCFirmware.ini \
../ATBUpdateTool.ini
FORMS += \
mainwindow.ui
##########################################################################################
# for running program on target through QtCreator
contains( CONFIG, PTU5 ) {

View File

@@ -0,0 +1,444 @@
#include "dc_download.h"
#include <DeviceController/interfaces.h>
#include <QFile>
#include <QDebug>
#include <QDateTime>
#include <QThread>
DcDownload::DcDownload(hwinf *hw)
: m_hw(hw)
, m_fileToDownload(m_hw->dcDownloadFileName()) {
// connect(this, &QThread::finished,
// dynamic_cast<QObject const *>(m_hw), &QThread::deleteLater);
}
DcDownload::~DcDownload() {
}
/*
///////////////////////////////////////////////////////////////////////////////
//
// 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.
// for other addresses nothing happens
6 : bl_wasSendingAddOK()
// return val: 0: no response by now
// 1: error
// 10: OK
7 : bl_sendDataBlock()
// send 64 byte from bin file
8 : bl_sendLastBlock()
// send this command after all data are transferred
9 : bl_wasSendingDataOK()
// return val: 0: no response by now
// 1: error
// 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.
*/
void DcDownload::doDownload() {
#if 0
void DcDownload::run() {
// download thread running in ca-master sends the dc-file down to firmware
// TODO: send the json files as well
m_hw->dcDownloadRequestAck();
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< "DcDownload::run(): DOWNLOAD THREAD STARTED:";
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< " DcDownload::run(): Filename:" << m_hw->dcDownloadFileName();
QDateTime const start = QDateTime::currentDateTime();
#if 1
QFile fn(m_hw->dcDownloadFileName());
if (!fn.exists()) {
// output via CONSOLE() etc
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< " DcDownload::run(): Filename:" << m_hw->dcDownloadFileName() << "DOES NOT EXIST";;
} else {
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< "DcDownload::run(): DC-CONTROLLER SW-VERSION BEFORE"
<< m_hw->dc_getSWversion();
// load binary device controller file into memory
QByteArray ba = loadBinaryDCFile(m_hw->dcDownloadFileName());
if (ba.size() > 0) {
uint16_t const totalBlocks = (((ba.size())%64)==0) ? (ba.size()/64) : (ba.size()/64)+1;
m_hw->dcDownloadSetTotalBlockNumber(totalBlocks);
// fill last block of data to be sent with 0xFF
ba = ba.leftJustified(totalBlocks*64, (char)(0xFF));
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< "DcDownload::run(): TOTAL NUMBER OF BYTES TO SEND TO DC" << ba.size();
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< "DcDownload::run(): TOTAL NUMBER OF BLOCKS" << totalBlocks;
m_hw->dc_autoRequest(true); // turn auto-request setting on
m_hw->request_DC2_HWversion();
m_hw->request_DC2_SWversion();
QThread::sleep(1);
// m_hw->dc_autoRequest(false); // turn auto-request setting on
resetDeviceController();
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< "DcDownload::run(): RESET DEVICE-CONTROLLER";
if (startBootloader()) {
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< "DcDownload::run(): STARTED BOOT-LOADER";
m_hw->dc_autoRequest(false);// turn auto-request setting off for
// download of binary dc
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< "DcDownload::run(): TOTAL NUMBER OF FIRMWARE BLOCKS" << totalBlocks;
int currentBlock = 0; // download of binary dc
DownloadResult res = DownloadResult::OK;
while (res != DownloadResult::ERROR && currentBlock < totalBlocks) {
if ((res = sendNextAddress(currentBlock)) != DownloadResult::ERROR) {
if ((res = sendNextDataBlock(ba, currentBlock)) != DownloadResult::ERROR) {
m_hw->dcDownloadSetCurrentBlockNumber(currentBlock);
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< "DcDownload::run(): currentBlockNumber ..." << currentBlock;
currentBlock += 1;
}
}
}
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< QString("DcDownload::run(): last 64-byte block %1").arg(currentBlock);
int const rest = ba.size() % 64;
int const offset = ba.size() - rest;
char const *startAddress = ba.constData() + offset;
if (rest > 0) {
// SHOULD NEVER HAPPEN !!!
uint8_t local[66];
memset(local, 0xFF, sizeof(local));
memcpy(local, startAddress, rest);
qCritical() << "DcDownload::run(): ERROR SEND REMAINING" << rest << "BYTES";
m_hw->bl_sendDataBlock(64, local);
} else {
m_hw->bl_sendLastBlock();
m_hw->dcDownloadSetCurrentBlockNumber(currentBlock);
qCritical() << "DcDownload::run(): currentBlockNumber" << currentBlock;
// QThread::msleep(250);
}
qCritical() << "DcDownload::run(): last result" << (int)sendStatus(m_hw->bl_wasSendingDataOK());
stopBootloader(); // stop bootloader several times: if it
QThread::sleep(1); // is not stopped, then the PSA has to be
}
// restarted manually (!!!)
stopBootloader();
QThread::sleep(1);
}
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< "DcDownload::run(): STOPPED BOOT-LOADER";
stopBootloader(); // there is no harm in stopping the bootloader even
// if it was not started at all
m_hw->dc_autoRequest(true);
}
#else // test
// load binary device controller file into memory
QByteArray ba = loadBinaryDCFile(m_hw->dcDownloadFileName());
if (ba.size() > 0) {
uint16_t const totalBlocks = (((ba.size())%64)==0) ? (ba.size()/64) : (ba.size()/64)+1;
m_hw->dcDownloadSetTotalBlockNumber(totalBlocks);
// fill last block of data to be sent with 0xFF
ba = ba.leftJustified(totalBlocks*64, (char)(0xFF));
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< "DcDownload::run(): TOTAL NUMBER OF BYTES TO SEND TO DC" << ba.size();
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< "DcDownload::run(): TOTAL NUMBER OF BLOCKS" << totalBlocks;
m_hw->dc_autoRequest(true); // turn auto-request setting on
m_hw->request_DC2_HWversion();
m_hw->request_DC2_SWversion();
QThread::sleep(1);
resetDeviceController();
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< "DcDownload::run(): RESET DEVICE-CONTROLLER";
QThread::sleep(1);
if (startBootloader()) {
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< "DcDownload::run(): STARTED BOOT-LOADER";
m_hw->dc_autoRequest(false);// turn auto-request setting off for
// download of binary dc
for (uint16_t currentBlock = 0; currentBlock <= totalBlocks; ++currentBlock) {
m_hw->dcDownloadSetCurrentBlockNumber(currentBlock);
qCritical() << "DcDownload::run(): currentBlockNumber" << currentBlock;
QThread::msleep(250);
}
m_hw->dc_autoRequest(true); // turn auto-request setting on again
}
stopBootloader(); // there is no harm in stopping the bootloader even
// if it was not started at all
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< "DcDownload::run(): STOPPED BOOT-LOADER";
}
#endif
m_hw->dcDownloadSetRunning(false);
m_hw->dcDownloadSetFinished(true);
QDateTime const end = QDateTime::currentDateTime();
quint64 secs = start.secsTo(end);
QString runtime;
if (secs % 60) {
runtime = QString("%1min %2s").arg(secs / 60).arg(secs % 60);
} else {
runtime = QString("%1min").arg((secs / 60) + 1);
}
qCritical() << end.time().toString(Qt::ISODateWithMs)
<< QString("DOWNLOAD THREAD FINISHED (RUNTIME %1)")
.arg(runtime);
// the object deletes itself ! This is the last line in run().
// Never touch the object after this statement
// m_hw->dcDcDownloadFinalize(this);
#endif
}
DcDownload::DownloadResult DcDownload::sendStatus(int ret) const {
switch (ret) { // return values of dc are:
case 0: // 0: no answer by now
return DownloadResult::NOP; // 1: error
case 10: // 10: success
return DownloadResult::OK;
default:;
}
return DownloadResult::ERROR;
}
DcDownload::DownloadResult
DcDownload::sendNextAddress(int bNum) const {
// sends address only if blockNumber is one of 0, 1024, 2048, 3072, 4096
int noAnswerCount = 0;
int errorCount = 0;
if ( bNum==0 || bNum==1024 || bNum==2048 || bNum==3072 || bNum==4096 ) {
while (noAnswerCount <= 250) {
m_hw->bl_sendAddress(bNum);
QThread::msleep(100);
DownloadResult const res = sendStatus(m_hw->bl_wasSendingAddOK());
if (res != DownloadResult::NOP) {
if (res == DownloadResult::ERROR) {
if (++errorCount >= 10) {
qCritical() << "addr-block" << bNum << "...FAILED";
return res;
}
} else { // res == DownloadResult::OK
// qInfo() << "addr-block" << bNum << "...OK";
// TODO: hier ins shared-mem schreiben
return res;
}
} else {
noAnswerCount += 1; // no answer by now
}
}
// wait max. about 3 seconds
return DownloadResult::TIMEOUT;
}
// blockNumber is not one of 0, 1024, 2048, 3072, 4096 -> do nothing
return DownloadResult::NOP;
}
DcDownload::DownloadResult
DcDownload::sendNextDataBlock(QByteArray const &binary, int bNum) const {
uint8_t local[66];
int const bAddr = bNum * 64;
int noAnswerCount = 0;
int errorCount = 0;
memcpy(local, binary.constData() + bAddr, 64);
local[64] = local[65] = 0x00;
while (noAnswerCount <= 250) {
m_hw->bl_sendDataBlock(64, local);
QThread::msleep(10);
DownloadResult const res = sendStatus(m_hw->bl_wasSendingDataOK());
if (res != DownloadResult::NOP) {
if (res == DownloadResult::ERROR) {
if (++errorCount >= 10) {
qCritical() << "data for block" << bNum << "...FAILED";
return res;
}
} else {
// qInfo() << "data for block" << bNum << "OK";
// TODO: hier ins shared mem schreiben
return res;
}
} else {
noAnswerCount += 1; // no answer by now
}
}
// wait max. about 3 seconds
return DownloadResult::TIMEOUT;
}
bool DcDownload::startBootloader() const {
qDebug() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< "starting bootloader...";
int nTry = 5;
while (--nTry >= 0) {
m_hw->bl_startBL();
qDebug() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) << "bl_startBL() ..." << nTry;
QThread::msleep(500);
m_hw->bl_checkBL();
qDebug() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) << "bl_checkBL() ..." << nTry;
QThread::msleep(500);
if (m_hw->bl_isUp()) {
qInfo() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) << "bootloader... isUP" << nTry;
return true;
} else {
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< "bootloader not up (" << nTry << ")";
}
}
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< "starting bootloader FAILED";
return false;
}
bool DcDownload::stopBootloader() const {
qDebug() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< "stopping bootloader...";
int nTry = 5;
while (--nTry >= 0) {
m_hw->bl_stopBL();
qDebug() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< "bl_stopBL() ...";
QThread::msleep(500);
m_hw->bl_checkBL();
qDebug() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) << "bl_checkBL() ..." << nTry;
if (!m_hw->bl_isUp()) {
qInfo() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< "stopping bootloader OK";
return true;
}
}
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< "stopping bootloader FAILED";
return false;
}
bool DcDownload::resetDeviceController() const {
qDebug() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< "resetting device controller...";
m_hw->bl_rebootDC();
// wait maximally 3 seconds, before starting bootloader
QThread::sleep(1);
qInfo() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< "resetting device controller...OK";
return true;
}
QByteArray DcDownload::loadBinaryDCFile(QString filename) const {
qInfo() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
<< "loading dc binary" << filename << "...";
QFile file(filename); // closed in destructor call
if (!file.exists()) {
qCritical() << file.fileName() << "does not exist";
return QByteArray();
}
if (!file.open(QIODevice::ReadOnly)) {
qCritical() << "cannot open file" << file.fileName();
return QByteArray();
}
qInfo() << "loading dc binary" << filename << "...OK";
return file.readAll();
}

View File

@@ -0,0 +1,32 @@
#ifndef DC_DOWNLOAD_H_INCLUDED
#define DC_DOWNLOAD_H_INCLUDED
#include <QString>
#include <QByteArray>
class hwinf;
class DcDownload {
public:
enum class DownloadResult {OK, ERROR, TIMEOUT, NOP};
DcDownload(hwinf *hw);
~DcDownload();
void doDownload();
private:
DownloadResult sendStatus(int ret) const;
DownloadResult sendNextAddress(int bNum) const;
DownloadResult sendNextDataBlock(QByteArray const &binary, int bNum) const;
bool startBootloader() const;
bool stopBootloader() const;
QByteArray loadBinaryDCFile(QString dcFileName) const;
bool resetDeviceController() const;
DownloadResult dcDownloadBinary(QByteArray const &b) const;
hwinf *m_hw;
QString m_fileToDownload;
};
#endif // DOWNLOAD_THREAD_H_INCLUDED

View File

@@ -1,8 +1,230 @@
#include <QtGlobal>
#include <QCoreApplication>
#include <QByteArray>
#include <QProcess>
#include <QCommandLineParser>
#include <QStandardPaths>
#include <QSettings>
#include <QDir>
#include <QDebug>
#include <QThread>
#include <QRegularExpression>
#include "message_handler.h"
#include "commandline_parser.h"
#include "utils.h"
#include "utils_internal.h"
#include "update.h"
#include "System.h"
#include <DeviceController/interfaces.h>
#ifdef __linux__
#include <sys/sysinfo.h>
#endif
#ifdef PTU5
#define SERIAL_PORT "ttymxc2"
#else
#define SERIAL_PORT "ttyUSB0"
#endif
int 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;
}
int main(int argc, char **argv) {
Q_UNUSED(argc);
Q_UNUSED(argv);
QByteArray const value = qgetenv("LC_ALL");
if (value != "C") {
qputenv("LC_ALL", "C");
}
// qputenv("XDG_RUNTIME_DIR", "/var/run/user/0");
openlog("DC", LOG_PERROR | LOG_CONS, LOG_USER);
QCoreApplication a(argc, argv);
QCoreApplication::setOrganizationName("ATB Automatentechnik Baumann GmBH");
QCoreApplication::setApplicationName("ATBUpdateDC");
QCoreApplication::setApplicationVersion(APP_VERSION);
if (!messageHandlerInstalled()) { // change internal qt-QDebug-handling
atbInstallMessageHandler(atbDebugOutput);
setDebugLevel(LOG_NOTICE);
}
//return 0;
CommandLineParser parser;
parser.process(a);
parser.readSettings();
/*
QString repositoryUrl = parser.repositoryUrl();
QString plugInDir = parser.plugInDir();
QString plugInName = parser.plugInName();
QString workingDir = parser.workingDir();
QString psaConfigDir = parser.psaConfigDir();
QString psaTariffDir = parser.psaTariffDir();
QString psaDcDir = parser.dcDir();
QString iniFileName = parser.iniFileName();
bool debug = parser.debug();
bool const dryRun = parser.dryRun();
bool const noUpdatePsaHardware = parser.noUpdatePsaHardware();
bool const showYoctoVersion = parser.yoctoVersion();
bool const showYoctoInstallStatus = parser.yoctoInstallStatus();
bool const showExtendedVersion = parser.extendedVersion();
bool const alwaysDownloadConfig = parser.alwaysDownloadConfig();
bool const alwaysDownloadDC = parser.alwaysDownloadDC();
bool const readDCVersion = parser.readDCVersion();
QString const rtPath = QCoreApplication::applicationDirPath();
int const machineNr = read1stLineOfFile("/mnt/system_data/machine_nr");
int const customerNr = read1stLineOfFile("/mnt/system_data/cust_nr");
int const zoneNr = read1stLineOfFile("/mnt/system_data/zone_nr");
QString const branchName = (zoneNr != 0)
? QString("zg1/zone%1").arg(zoneNr) : "master";
qInfo() << "pwd ......................" << rtPath;
qInfo() << "repositoryUrl ............" << repositoryUrl;
qInfo() << "plugInDir ................" << plugInDir;
qInfo() << "plugInName ..............." << plugInName;
qInfo() << "workingDir ..............." << workingDir;
qInfo() << "psaConfigDir ............." << psaConfigDir;
qInfo() << "psaTariffDir ............." << psaTariffDir;
qInfo() << "dryRun ..................." << dryRun;
qInfo() << "noUpdatePsaHardware ......" << noUpdatePsaHardware;
qInfo() << "alwaysDownloadConfig ....." << alwaysDownloadConfig;
qInfo() << "alwaysDownloadDC ........." << alwaysDownloadDC;
qInfo() << "showYoctoVersion ........." << showYoctoVersion;
qInfo() << "showYoctoInstallStatus ..." << showYoctoInstallStatus;
qInfo() << "showExtendedVersion ......" << showExtendedVersion;
qInfo() << "iniFileName .............." << iniFileName;
qInfo() << "extended-version ........." << APP_EXTENDED_VERSION;
qInfo() << "machineNr ................" << machineNr;
qInfo() << "customerNr ..............." << customerNr;
qInfo() << "zoneNr ..................." << zoneNr;
qInfo() << "readDCVersion ............" << readDCVersion;
qInfo() << "dcDir ...................." << psaDcDir;
if (!QDir(plugInDir).exists()) {
qCritical() << plugInDir
<< "does not exists, but has to contain dc-library";
exit(-1);
}
if (showExtendedVersion) {
printf(APP_EXTENDED_VERSION"\n");
return 0;
}
QString const &customerRepo
= QDir::cleanPath(workingDir + QDir::separator() + QString("customer_%1").arg(customerNr));
*/
QString psaDcDir = internal::customerRepoDcDir();
QString psaRepoRootDir = internal::customerRepoRoot();
QString psaRepoDir = internal::customerRepoDir();
QString branchName = internal::branchName();
QString rootDir = "/etc/dc/";
// bool usbDetected = false;
// bool sdCardDetected = false;
bool debug = false;
bool noaction = true;
QString workingDir;
QString libca;
std::unique_ptr<QSettings> settings = internal::readSettings();
if (settings) {
settings->beginGroup("ATBUpdateDC");
debug = settings->value("debug", false).toBool();
workingDir = settings->value("workingdir", "/tmp").toString();
libca = settings->value("libca", "/usr/lib/libCAslave.so").toString();
settings->endGroup();
} else {
qCritical() << "count not read ATBUpdateTool.ini";
return -1;
}
QString dcDir = parser.dcDir();
if (!dcDir.isEmpty()) {
psaDcDir = dcDir;
}
debug = parser.debug();
if (debug) {
qInfo() << " using dc directory: " << psaDcDir;
qInfo() << "using psa repo root directory: " << psaRepoRootDir;
qInfo() << " using customer repository: " << psaRepoDir;
qInfo() << " using branch: " << branchName;
}
// etc/dc: located under mount-path
std::optional<QString> mountPath = System::checkForUSBStick(psaDcDir);
QFileInfo fi;
if (mountPath.has_value()) {
// usbDetected = true;
// mountPath/etc/dc must exists !
// cannot override with commandline parameter --dc-directory
if (debug) {
qInfo() << "using mount path (USB): " << mountPath.value();
}
} else
if ((mountPath = System::checkForSDCard(psaDcDir)).has_value()) {
// sdCardDetected = true;
// mountPath/etc/dc must exists !
// cannot override with commandline parameter --dc-directory
if (debug) {
qInfo() << "using mount path (SD): " << mountPath.value();
}
} else {
rootDir = "";
mountPath = QDir::cleanPath(psaRepoRootDir + QDir::separator() + psaDcDir);
if (debug) {
qInfo() << "using as dc-directory" << mountPath.value();
}
}
if (debug) {
qInfo() << "using as mount path: " << mountPath.value();
}
std::unique_ptr<QString> c = internal::dcCandidateToInstall(rootDir, mountPath.value());
if (c) {
fi.setFile(*c);
if (fi.exists() == false) {
qCritical() << "dc2c.bin candidate" << *c << "does not exist. STOP.";
return -1;
}
qInfo() << "dc2c.bin canditate" << fi.absoluteFilePath();
}
if (debug) {
qInfo() << "downloading dc-firmware" << fi.absoluteFilePath();
qInfo() << "dc-firmware size (bytes)" << fi.size();
qInfo() << "dc-version" << Update::dcVersion(fi.absoluteFilePath());
}
// only for testing
// return 0;
Update u(fi.absoluteFilePath(), libca, debug, noaction);
u.run();
qInfo() << "<DC-UPDATE-FINISH>";
return 0;
}

View File

@@ -0,0 +1,352 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDateTime>
#include <QMessageBox>
#include <QDebug>
#include <QScrollBar>
#include <QEvent>
#ifdef PTU5
#define SERIAL_PORT "ttymxc2"
#else
#define SERIAL_PORT "ttyUSB0"
#endif
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, m_serial(new QSerialPort(this))
, m_width(70) {
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);
openSerialPort();
//ui->updateProgress->setRange(0, 100);
//ui->updateProgress->reset();
#if 0
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(start()));
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(showErrorMessage(QStringList)),this, SLOT(onShowErrorMessage(QStringList)));
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)));
#endif
}
MainWindow::~MainWindow() {
//delete m_startTimer;
//delete m_exitTimer;
closeSerialPort();
delete ui;
}
bool MainWindow::openSerialPort() {
// const SettingsDialog::Settings p = m_settings->settings();
m_serial->setPortName(SERIAL_PORT);
m_serial->setBaudRate(QSerialPort::Baud115200);
m_serial->setDataBits(QSerialPort::DataBits::Data8);
m_serial->setParity(QSerialPort::Parity::NoParity);
m_serial->setStopBits(QSerialPort::StopBits::OneStop);
m_serial->setFlowControl(QSerialPort::FlowControl::NoFlowControl);
if (m_serial->open(QIODevice::ReadWrite)) {
//m_console->setEnabled(true);
//m_console->setLocalEchoEnabled(p.localEchoEnabled);
//m_ui->actionConnect->setEnabled(false);
//m_ui->actionDisconnect->setEnabled(true);
//m_ui->actionConfigure->setEnabled(false);
//showStatusMessage(tr("Connected to %1 : %2, %3, %4, %5, %6")
// .arg(p.name, p.stringBaudRate, p.stringDataBits,
// p.stringParity, p.stringStopBits, p.stringFlowControl));
return true;
} else {
//QMessageBox::critical(this, tr("Error"), m_serial->errorString());
//showStatusMessage(tr("Open error"));
}
return false;
}
bool MainWindow::closeSerialPort() {
if (m_serial->isOpen()) {
m_serial->close();
return true;
}
return false;
//m_console->setEnabled(false);
//m_ui->actionConnect->setEnabled(true);
//m_ui->actionDisconnect->setEnabled(false);
//m_ui->actionConfigure->setEnabled(true);
//showStatusMessage(tr("Disconnected"));
}
#if 0
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, 1));
} 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);
// ueberpruefen: hauptfenster schickt sich selber ein event
// QApplication::postEvent(this, new ProgressEvent(this, progress));
// 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 !!!";
}
}
QThread::yieldCurrentThread();
}
void MainWindow::onStopStartTimer() {
m_startTimer->stop();
}
void MainWindow::onDisableExit() {
ui->exit->setEnabled(false);
}
void MainWindow::onEnableExit() {
ui->exit->setEnabled(true);
}
void MainWindow::onRestartExitTimer() {
m_exitTimer->stop();
m_exitTimer->start(5 * 1000);
scrollDownTextEdit();
ui->updateStatus->setEnabled(false);
}
void MainWindow::onQuit() {
m_exitTimer->stop();
int errorCode = 0;
qCritical()
<< QString("ON QUIT: CURRENT STEP %1")
.arg(m_worker->getSmap()[m_worker->currentStep()]);
// TODO: replace SEND_LAST_VERSION with UPDATE_SUCCEEDED
if (m_worker->currentStep() != Worker::UPDATE_STEP::SEND_LAST_VERSION) {
errorCode = -1;
}
qCritical() << QString("ON QUIT: EXIT CODE %1").arg(errorCode);
qApp->exit(errorCode);
}
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) {
// removing the last line is really meant for refreshing the last line
// with a string very similar than the original one, typically only
// followed by a suffix.
if (lines.last().contains(text)) {
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);
}
void MainWindow::onShowErrorMessage(QStringList lst) {
if (lst.size() >= 2) {
onShowMessage(lst.at(0), lst.at(1));
}
if (lst.size() == 1) {
onShowMessage(lst.at(0), "");
}
}
void MainWindow::onShowStatusMessage(QStringList lst) {
if (lst.size() >= 2) {
onShowMessage(lst.at(0), lst.at(1));
}
if (lst.size() == 1) {
onShowMessage(lst.at(0), "");
}
}
#endif

View File

@@ -0,0 +1,55 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QTimer>
#include <QSerialPort>
#include <QSerialPortInfo>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
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 onShowErrorMessage(QStringList);
//void onShowStatusMessage(QStringList);
//void onStopStartTimer();
//void onRestartExitTimer();
//void onEnableExit();
//void onDisableExit();
signals:
private slots:
// void onQuit();
private:
// void scrollDownTextEdit();
// void onShowMessage(QString, QString);
bool openSerialPort();
bool closeSerialPort();
Ui::MainWindow *ui;
QSerialPort *m_serial;
// Worker *m_worker;
int const m_width;
// QTimer *m_startTimer;
// QTimer *m_exitTimer;
// bool m_progressRunning;
//int m_progressValue;
// UpdateDcEvent::UpdateStep m_updateStep;
};
#endif // MAINWINDOW_H

View File

@@ -0,0 +1,62 @@
<?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="0" column="0" rowspan="3" colspan="2">
<widget class="QTextEdit" name="updateStatus">
<property name="enabled">
<bool>true</bool>
</property>
<property name="font">
<font>
<family>Misc Fixed</family>
<pointsize>11</pointsize>
<weight>75</weight>
<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

View File

View File

View File

View File

@@ -0,0 +1,477 @@
#include "update.h"
#include "process/command.h"
#include <QCoreApplication>
#include <QFile>
#include <QTemporaryFile>
#include <QDebug>
#include <QTextStream>
#include <QRegularExpression>
#include <QRegExp>
#if defined (Q_OS_UNIX) || defined (Q_OS_LINUX)
#include "unistd.h"
#endif
#include <DeviceController/interfaces.h>
#include <QSharedMemory>
#include <QScopedPointer>
#include <QDir>
#include <QThread>
#include <QDateTime>
#include <QPluginLoader>
#include <QMap>
#include <QStringList>
#include <QString>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QSettings>
#define UPDATE_OPKG (1)
#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(QString const &libCA /* absolute file path */) {
hwinf *hw = nullptr;
QFileInfo libCAInfo(libCA);
if (libCAInfo.exists()) {
pluginLoader.setFileName(libCA);
// static QPluginLoader pluginLoader(pluginLibName);
if (!pluginLoader.load()) {
qCritical() << "cannot load plugin" << pluginLoader.fileName();
qCritical() << pluginLoader.errorString();
return nullptr;
}
// qCritical() << "loadDCPlugin() plugin file name:" << pluginLoader.fileName();
if (!pluginLoader.isLoaded()) {
qCritical() << pluginLoader.errorString();
return nullptr;
}
QObject *plugin = pluginLoader.instance();
if (!plugin) {
qCritical() << "cannot start instance";
return nullptr;
}
if (! (hw = qobject_cast<hwinf *>(plugin))) {
qCritical() << "cannot cast plugin" << plugin << "to hwinf";
return nullptr;
}
} else {
qCritical() << libCAInfo.absoluteFilePath() << "does not exist";
return nullptr;
}
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;
}
QString Update::dcVersion(QString const &dcBinFile) {
QProcess p;
QStringList params;
params << "-c" << QString(R"(strings %1 | grep DC2c.\[0-9\] | uniq)").arg(dcBinFile);
p.start("bash", params);
p.waitForFinished();
return QString(p.readAllStandardOutput()).trimmed().split(QRegularExpression("\\s")).first();
}
class hwapi;
Update::Update(QString const &dcFileName, QString const &libCA, bool debug, bool noaction)
: m_dcFileName(dcFileName)
, m_hw(loadDCPlugin(libCA))
, m_sys_areDCdataValid(false)
, m_debug(debug)
, m_noaction(noaction) {
}
Update::~Update() {
unloadDCPlugin();
}
Update::DownloadResult Update::sendStatus(int ret) const {
switch (ret) { // return values of dc are:
case 0: // 0: no answer by now
return DownloadResult::NOP; // 1: error
case 10: // 10: success
return DownloadResult::OK;
default:;
}
return DownloadResult::ERROR;
}
Update::DownloadResult
Update::sendNextAddress(int bNum) const {
// sends address only if blockNumber is one of 0, 1024, 2048, 3072, 4096
int noAnswerCount = 0;
int errorCount = 0;
if ( bNum==0 || bNum==1024 || bNum==2048 || bNum==3072 || bNum==4096 ) {
// qDebug() << "addr-block" << bNum << "...";
while (noAnswerCount <= 250) {
DownloadResult res = DownloadResult::OK;
if (!m_debug) {
m_hw->bl_sendAddress(bNum);
QThread::msleep(10); //from 100ms to 20ms
//###################################################################################
res = sendStatus(m_hw->bl_wasSendingAddOK());
}
if (res != DownloadResult::NOP) {
if (res == DownloadResult::ERROR) {
if (++errorCount >= 10) {
qCritical() << "addr-block" << bNum << "...FAILED";
return res;
}
} else { // res == DownloadResult::OK
qInfo() << nextTimePoint().toUtf8().constData() << "addr-block" << bNum << "...done";
return res;
}
} else {
noAnswerCount += 1; // no answer by now
}
} // while
// wait max. about 3 seconds
return DownloadResult::TIMEOUT;
}
// blockNumber is not one of 0, 1024, 2048, 3072, 4096 -> do nothing
return DownloadResult::NOP;
}
Update::DownloadResult
Update::sendNextDataBlock(QByteArray const &binary, int bNum) const {
uint8_t local[66];
int const bAddr = bNum * 64;
int noAnswerCount = 0;
int errorCount = 0;
memcpy(local, binary.constData() + bAddr, 64);
local[64] = local[65] = 0x00;
QString s = nextTimePoint();
s += " sending block ";
s += QString("%1/%2 ...done <DC-PROGRESS>").arg(bNum).arg(m_totalBlocks);
s += QString::number(ceil(((bNum * 100.0) / (double)m_totalBlocks)));
qInfo() << s.toUtf8().constData();
QThread::msleep(20); //reduce from 200 to 50 ms
//############################################################################
QByteArray b((const char *)(&local[0]), 64);
qCritical() << "SNDB" << bNum << b.size() << b.toHex();
while (noAnswerCount <= 250) {
DownloadResult res = DownloadResult::OK;
if (!m_debug) {
m_hw->bl_sendDataBlock(64, local);
res = sendStatus(m_hw->bl_wasSendingDataOK());
}
if (res != DownloadResult::NOP) {
if (res == DownloadResult::ERROR) {
if (++errorCount >= 10) {
qCritical() << "data for block" << bNum << "...FAILED";
return res;
}
} else {
qInfo() << nextTimePoint().toUtf8().constData() << "data for block"
<< QString("%1/%2").arg(bNum).arg(m_totalBlocks) << "done";
return res;
}
} else {
noAnswerCount += 1; // no answer by now
}
}
// wait max. about 3 seconds
return DownloadResult::TIMEOUT;
}
bool Update::startBootloader() const {
qDebug() << "starting bootloader...";
if (!m_debug) {
int nTry = 10;
while (--nTry >= 0) {
m_hw->bl_startBL();
QThread::msleep(1000);
m_hw->bl_checkBL();
if (m_hw->bl_isUp()) {
qInfo() << "starting bootloader...OK";
QThread::msleep(5000);
return true;
} else {
qCritical() << "bootloader not up (" << nTry << ")";
qCritical() << "IS BOOTLOADER INSTALLED ???";
}
}
qCritical() << "starting bootloader...FAILED";
return false;
} else {
QThread::msleep(1000);
qInfo() << "starting bootloader...OK";
}
return true;
}
bool Update::stopBootloader() const {
qDebug() << "stopping bootloader...";
if (!m_debug) {
int nTry = 5;
while (--nTry >= 0) {
m_hw->bl_stopBL();
QThread::msleep(1000);
if (!m_hw->bl_isUp()) {
qInfo() << "stopping bootloader...OK";
return true;
}
}
qCritical() << "stopping bootloader...FAILED";
return false;
} else {
QThread::msleep(1000);
qInfo() << "stopping bootloader...OK";
}
return true;
}
bool Update::resetDeviceController() const {
qInfo() << nextTimePoint().toUtf8().constData() << "resetting device controller";
if (!m_debug) {
m_hw->bl_rebootDC();
}
// wait maximally 3 seconds, before starting bootloader
QThread::sleep(1);
qInfo() << nextTimePoint().toUtf8().constData()
<< "resetting device controller ...done";
return true;
}
QByteArray Update::loadBinaryDCFile(QString const &filename) const {
QFile file(filename); // closed in destructor call
if (!file.exists()) {
qCritical() << "(" << __func__ << ":" << __LINE__ << ")"
<< file.fileName() << "does not exist";
return QByteArray{};
}
if (!file.open(QIODevice::ReadOnly)) {
qCritical() << "(" << __func__ << ":" << __LINE__ << ")"
<< "cannot open file" << file.fileName();
return QByteArray{};
}
qInfo() << nextTimePoint().toUtf8().constData()
<< "loading dc binary to memory" << Update::dcVersion(filename) << "...done";
return file.readAll();
}
/*
///////////////////////////////////////////////////////////////////////////////
//
// 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.
// for other addresses nothing happens
6 : bl_wasSendingAddOK()
// return val: 0: no response by now
// 1: error
// 10: OK
7 : bl_sendDataBlock()
// send 64 byte from bin file
8 : bl_sendLastBlock()
// send this command after all data are transferred
9 : bl_wasSendingDataOK()
// return val: 0: no response by now
// 1: error
// 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.
*/
int Update::run() {
if (!m_hw) {
qCritical() << "(" << __func__ << ":" << __LINE__ << ") m_hw == nullptr -> ca-slave plugin loaded ???";
return -(int)Result::PLUGIN_LOAD_ERROR;
}
m_start = QDateTime::currentDateTime();
if (m_debug) {
qInfo() << "start dc-update for" << m_dcFileName << "at" << m_start.toString(Qt::ISODate);
qInfo() << "<DC-VERSION>" << Update::dcVersion(m_dcFileName);
}
if (!m_debug) {
m_hw->dc_autoRequest(false);
}
qInfo() << "DC auto request OFF";
qCritical() << "start dc-update for" << m_dcFileName << "at" << m_start.toString(Qt::ISODate);
QByteArray ba = loadBinaryDCFile(m_dcFileName);
if (ba.size() > 0) {
m_totalBlocks = (((ba.size())%64)==0) ? (ba.size()/64) : (ba.size()/64)+1;
qInfo() << nextTimePoint().toUtf8().constData() << "blocks to send" << m_totalBlocks;
// fill last block of data to be sent with 0xFF
ba = ba.leftJustified(m_totalBlocks*64, (char)(0xFF));
resetDeviceController();
if (startBootloader()) {
int currentBlock = 0;
DownloadResult res = DownloadResult::OK;
qInfo() << nextTimePoint().toUtf8().constData() << "64-byte block" << currentBlock;
while (res != DownloadResult::ERROR && currentBlock <= m_totalBlocks) {
if ((res = sendNextAddress(currentBlock)) != DownloadResult::ERROR) {
if ((res = sendNextDataBlock(ba, currentBlock)) != DownloadResult::ERROR) {
currentBlock += 1;
} else break;
}
}
#if 0
qCritical() << "DownloadThread::run(): last 64-byte block %04d" << currentBlock;
int const rest = ba.size() % 64;
int const offset = ba.size() - rest;
char const *startAddress = ba.constData() + offset;
if (rest > 0) {
// SHOULD NEVER HAPPEN !!!
uint8_t local[66];
memset(local, 0xFF, sizeof(local));
memcpy(local, startAddress, rest);
qCritical() << "DownloadThread::run(): ERROR SEND REMAINING" << rest << "BYTES";
m_hw->bl_sendDataBlock(64, local);
} else {
m_hw->bl_sendLastBlock();
m_hw->dcDownloadSetCurrentBlockNumber(currentBlock);
}
qCritical() << "DownloadThread::run(): last result" << (int)sendStatus(m_hw->bl_wasSendingDataOK());
#endif
}
stopBootloader(); // there is no harm in stopping the bootloader even
// if starting the bootloader failed
// check if update was successful
if (!m_debug) {
m_hw->dc_autoRequest(true); //restart dc_autoRequest after download else E255!
}
for (int i = 0; i < 3; ++i) {
qInfo() << "waiting for device controller restart...(" << i << ")";
QThread::sleep(20);
resetDeviceController();
if (startBootloader()) {
qInfo() << nextTimePoint().toUtf8().constData() << "<DC-UPDATE-SUCCESS>";
stopBootloader();
return -(int)Result::SUCCESS;
}
}
}
qInfo() << nextTimePoint().toUtf8().constData() << "<DC-UPDATE-FAILURE>";
//To Do Error handling if Dc doesnt start after download
return false;
}

View File

@@ -0,0 +1,92 @@
#ifndef UPDATE_H_INCLUDED
#define UPDATE_H_INCLUDED
#include <QObject>
#include <QString>
#include <QFile>
#include <QDir>
#include <QByteArray>
#include <QProcess>
#include <QPluginLoader>
#include <QDateTime>
#include <cmath>
#include <initializer_list>
#include <DeviceController/interfaces.h>
#ifdef PTU5
#define SERIAL_PORT "ttymxc2"
#define BAUDRATE 115200
#else
#define SERIAL_PORT "ttyUSB0"
#define BAUDRATE 115200
#endif
class QSerialPort;
class Update : public QObject {
Q_OBJECT
QString m_dcFileName{};
hwinf *m_hw = nullptr;
bool m_sys_areDCdataValid{};
bool m_debug{false};
bool m_noaction;
static QPluginLoader pluginLoader;
QDateTime m_start;
QString nextTimePoint() const {
float const secs = m_start.msecsTo(QDateTime::currentDateTime()) / 1000.0;
return QStringLiteral("+%1s").arg(secs, 7, 'f', 2, QChar('0'));
}
public:
enum class DownloadResult {OK, ERROR, TIMEOUT, NOP};
enum class Result {SUCCESS=0, PLUGIN_LOAD_ERROR};
static hwinf *loadDCPlugin(QString const &libCA = "/usr/lib/libCAslave.so");
static bool unloadDCPlugin();
static QStringList split(QString line, QChar sep = ',');
explicit Update(QString const &dcBinFile, QString const &libCA, bool debug, bool noaction);
virtual ~Update() override;
int run();
static QString dcVersion(QString const &dcBinFile);
private:
DownloadResult sendStatus(int ret) const;
DownloadResult sendNextAddress(int bNum) const;
DownloadResult sendNextDataBlock(QByteArray const &binary, int bNum) const;
bool startBootloader() const;
bool stopBootloader() const;
QByteArray loadBinaryDCFile(QString const &dcFilename) const;
bool resetDeviceController() const;
DownloadResult dcDownloadBinary(QByteArray const &b) const;
QString m_fileToDownload;
uint16_t m_totalBlocks = 0;
/*
private:
static QString jsonType(enum FileTypeJson type);
bool openSerial(int br, QString baudrate, QString comPort) const;
void closeSerial() const;
bool isSerialOpen() const;
bool resetDeviceController() const;
QByteArray loadBinaryDCFile(QString filename) const;
bool downloadBinaryToDC(QString const &bFile) const;
bool updateBinary(QString const &fileToSendToDC);
QStringList getDcSoftAndHardWareVersion();
QString getFileVersion(QString const& jsonFile);
private slots:
void readyReadStandardOutput();
void readyReadStandardError();
void finished(int exitCode, QProcess::ExitStatus exitStatus);
void onReportDCDownloadStatus(QString const &status);
void onReportDCDownloadSuccess(QString const &msg);
void onReportDCDownloadFailure(QString const &errorMsg);
*/
};
#endif // UPDATE_H_INCLUDED

View File

@@ -0,0 +1,13 @@
#include "worker_thread.h"
WorkerThread::WorkerThread(QObject *parent)
: QThread(parent) {
}
WorkerThread::~WorkerThread() {
}
void WorkerThread::run() {
}

View File

@@ -0,0 +1,20 @@
#ifndef WORKER_THREAD_H_INCLUDED
#define WORKER_THREAD_H_INCLUDED
#include <QThread>
class WorkerThread : public QThread {
Q_OBJECT
public:
explicit WorkerThread(QObject *parent = nullptr);
~WorkerThread();
private:
void run() override;
bool m_quit = false;
};
#endif // WORKER_THREAD_H_INCLUDED

View File

@@ -1,7 +1,7 @@
QT += core
QT += serialport network
TARGET = ATBDownloadDCJsonFiles
TARGET = ATBUpdateJsonFiles
VERSION="0.1.0"
win32 {
@@ -19,7 +19,12 @@ EXTENDED_VERSION="$${VERSION}-$${GIT_COMMIT}"
!contains(CONFIG, INCLUDEINTERFACES) {
INCLUDEINTERFACES=/opt/ptu5/opt/DCLibraries/include
}
INCLUDEPATH += plugins $${INCLUDEINTERFACES}
INCLUDEPATH += \
plugins \
$${INCLUDEINTERFACES} \
$${_PRO_FILE_PWD_}/../UpdatePTUDevCtrl \
$${_PRO_FILE_PWD_}/../common/include
CONFIG += c++17
@@ -39,6 +44,12 @@ contains( CONFIG, PTU5 ) {
lessThan(QT_MAJOR_VERSION, 5): PKGCONFIG += qextserialport
QMAKE_CXXFLAGS += -O2 -std=c++17 # for GCC >= 4.7
# QMAKE_CXXFLAGS += -Wno-deprecated-copy
PTU5BASEPATH = /opt/devel/ptu5
INCLUDEPATH += $$PTU5BASEPATH/qt/libs/devicecontroller/include
LIBS += -L$$PTU5BASEPATH/qt/libs/devicecontroller/library
ARCH = PTU5
DEFINES+=PTU5
}
@@ -67,16 +78,20 @@ contains( CONFIG, DesktopLinux ) {
SOURCES += \
main.cpp \
message_handler.cpp \
process/command.cpp
../UpdatePTUDevCtrl/message_handler.cpp \
../common/src/commandline_parser.cpp \
update.cpp \
../common/src/System.cpp
HEADERS += \
message_handler.h \
plugins/interfaces.h \
process/command.h
../UpdatePTUDevCtrl/message_handler.h \
../common/include/commandline_parser.h \
update.h \
../common/include/System.h
OTHER_FILES += \
ATBDownloadDCJsonFiles.ini
ATBDownloadDCJsonFiles.ini \
../ATBUpdateTool.ini
##########################################################################################

View File

@@ -3,8 +3,21 @@
#include <QCoreApplication>
#include <QByteArray>
#include <QProcess>
#include <QCommandLineParser>
#include <QStandardPaths>
#include <QSettings>
#include <QDir>
#include <QDebug>
#include "message_handler.h"
#include "plugins/interfaces.h"
#include "commandline_parser.h"
#include "utils.h"
#include "update.h"
#include "System.h"
#include <DeviceController/interfaces.h>
//#include <unistd.h>
@@ -16,6 +29,20 @@
#define SERIAL_PORT "ttyUSB0"
#endif
int 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;
}
int main(int argc, char **argv) {
QByteArray const value = qgetenv("LC_ALL");
if (value != "C") {
@@ -34,23 +61,110 @@ int main(int argc, char **argv) {
setDebugLevel(LOG_NOTICE);
}
int r = std::system("ls -l /proc/[0-9]*/fd/* 2>/dev/null | grep /dev/ttymxc2 > /tmp/slave-or-master.txt");
// lrwx------ 1 root root 64 Oct 31 14:55 /proc/884/fd/18 -> /dev/ttymxc2
CommandLineParser parser;
parser.process(a);
parser.readSettings();
//std::ifstream ifs("/tmp/slave-or-master.txt");
QString repositoryUrl = parser.repositoryUrl();
QString plugInDir = parser.plugInDir();
QString plugInName = parser.plugInName();
QString workingDir = parser.workingDir();
QString psaConfigDir = parser.psaConfigDir();
QString psaTariffDir = parser.psaTariffDir();
QString iniFileName = parser.iniFileName();
bool const dryRun = parser.dryRun();
bool const noUpdatePsaHardware = parser.noUpdatePsaHardware();
bool const showYoctoVersion = parser.yoctoVersion();
bool const showYoctoInstallStatus = parser.yoctoInstallStatus();
bool const showExtendedVersion = parser.extendedVersion();
bool const alwaysDownloadConfig = parser.alwaysDownloadConfig();
bool const alwaysDownloadDC = parser.alwaysDownloadDC();
Update::setPPid(parser.ppid());
//if (ifs.is_open()) {
// std::string line;
// if (std::getline(ifs, line)) {
// if (line.size() > 0) {
// std::cout << "SLAVE " << line << std::endl;
// }
// } else {
// }
QString const rtPath = QCoreApplication::applicationDirPath();
// ifs.close();
int const machineNr = read1stLineOfFile("/mnt/system_data/machine_nr");
int const customerNr = read1stLineOfFile("/mnt/system_data/cust_nr");
int const zoneNr = read1stLineOfFile("/mnt/system_data/zone_nr");
QString const branchName = (zoneNr != 0)
? QString("zg1/zone%1").arg(zoneNr) : "master";
//}
if (Update::ppid() == -1) {
qInfo() << "pwd ......................" << rtPath;
qInfo() << "repositoryUrl ............" << repositoryUrl;
qInfo() << "plugInDir ................" << plugInDir;
qInfo() << "plugInName ..............." << plugInName;
qInfo() << "workingDir ..............." << workingDir;
qInfo() << "psaConfigDir ............." << psaConfigDir;
qInfo() << "psaTariffDir ............." << psaTariffDir;
qInfo() << "dryRun ..................." << dryRun;
qInfo() << "noUpdatePsaHardware ......" << noUpdatePsaHardware;
qInfo() << "alwaysDownloadConfig ....." << alwaysDownloadConfig;
qInfo() << "alwaysDownloadDC ........." << alwaysDownloadDC;
qInfo() << "showYoctoVersion ........." << showYoctoVersion;
qInfo() << "showYoctoInstallStatus ..." << showYoctoInstallStatus;
qInfo() << "showExtendedVersion ......" << showExtendedVersion;
qInfo() << "iniFileName .............." << iniFileName;
qInfo() << "extended-version ........." << APP_EXTENDED_VERSION;
qInfo() << "machineNr ................" << machineNr;
qInfo() << "customerNr ..............." << customerNr;
qInfo() << "zoneNr ..................." << zoneNr;
qInfo() << "parent pid ..............." << Update::ppid();
return a.exec();
if (showExtendedVersion) {
printf(APP_EXTENDED_VERSION"\n");
return 0;
}
}
QString const &customerRepo = QDir::cleanPath(workingDir + QDir::separator() + QString("customer_%1").arg(customerNr));
QStringList filesToUpdate;
// etc/psa_config: located under mount-path
std::optional<QString> mountPath = System::checkForUSBStick(psaConfigDir);
if (mountPath.has_value()) {
filesToUpdate = System::getJsonFilesOnUsbStick(mountPath.value());
} else {
if (Update::ppid() == -1) {
qCritical() << "Using customer repository" << customerRepo;
}
QDir dir(QDir::cleanPath(customerRepo + QDir::separator() + "etc/psa_config"));
if (dir.exists()) {
QStringList jsons = dir.entryList(QStringList() << "DC2C*.json", QDir::Files);
if (!jsons.isEmpty()) {
for (QStringList::size_type i=0; i<jsons.size(); ++i) {
filesToUpdate << QDir::cleanPath(QString("etc/psa_config/") + jsons.at(i));
}
}
} else {
qCritical() << "DIRECTORY" << dir << "DOES NOT EXIST";
return -1;
}
}
qCritical() << __LINE__ << "JSON FILES TO UPDATE" << filesToUpdate;
Update update(customerRepo,
QString::number(customerNr),
branchName,
plugInDir,
plugInName,
workingDir);
if (!filesToUpdate.empty()) {
update.doUpdate(filesToUpdate, mountPath.has_value());
}
// update.checkJsonVersions();
//update.checkJsonVersions(filesToUpdate);
if (mountPath.has_value()) {
System::umountUSBStick();
}
qInfo() << "<JS-UPDATE-FINISH>";
return 0;
}

View File

@@ -0,0 +1,477 @@
#include "update.h"
#include <QCoreApplication>
#include <QFile>
#include <QTemporaryFile>
#include <QDebug>
#include <QTextStream>
#include <QRegularExpression>
#include <QRegExp>
#if defined (Q_OS_UNIX) || defined (Q_OS_LINUX)
#include "unistd.h"
#endif
#include <DeviceController/interfaces.h>
#include <QSharedMemory>
#include <QScopedPointer>
#include <QDir>
#include <QThread>
#include <QDateTime>
#include <QPluginLoader>
#include <QMap>
#include <cmath>
#define UPDATE_OPKG (1)
#define UPDATE_DC (0)
qint64 Update::c_ppid = -1;
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()) {
QString pluginLibName(fname);
pluginLibName = plugInDir.absoluteFilePath(pluginLibName);
QFileInfo info(pluginLibName);
if (info.exists()) {
pluginLibName = plugInDir.absoluteFilePath(pluginLibName);
pluginLoader.setFileName(pluginLibName);
// static QPluginLoader pluginLoader(pluginLibName);
if (!pluginLoader.load()) {
qCritical() << "in directory" << plugInDir.absolutePath();
qCritical() << "cannot load plugin" << pluginLoader.fileName();
qCritical() << pluginLoader.errorString();
return nullptr;
}
qCritical() << "loadDCPlugin() plugin directory:" << plugInDir.absolutePath();
qCritical() << "loadDCPlugin() plugin file name:" << pluginLoader.fileName();
if (!pluginLoader.isLoaded()) {
qCritical() << pluginLoader.errorString();
return nullptr;
}
QObject *plugin = pluginLoader.instance();
if (!plugin) {
qCritical() << "cannot start instance";
return nullptr;
}
if (! (hw = qobject_cast<hwinf *>(plugin))) {
qCritical() << "cannot cast plugin" << plugin << "to hwinf";
return nullptr;
}
} else {
qCritical() << pluginLibName << "does not exist";
return nullptr;
}
} else {
qCritical() << "plugins directory" << plugInDir.absolutePath()
<< "does not exist";
return nullptr;
}
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;
}
class hwapi;
Update::Update(QString customerRepository,
QString customerNrStr,
QString branchName,
QString plugInDir,
QString pluginName,
QString workingDir,
bool dryRun,
QObject *parent,
char const *serialInterface,
char const *baudrate)
: QObject(parent)
, m_hw(loadDCPlugin(QDir(plugInDir), pluginName))
, m_serialInterface(serialInterface)
, m_baudrate(baudrate)
, m_customerRepository(customerRepository)
, m_customerNrStr(customerNrStr)
, m_branchName(branchName)
, m_pluginName(pluginName)
, m_workingDir(workingDir)
, m_dryRun(dryRun)
, m_sys_areDCdataValid(false) {
m_start = QDateTime::currentDateTime();
if (Update::ppid() == -1) {
if (!m_hw) {
qCritical() << "(" << __func__ << ":" << __LINE__ << ") m_hw == nullptr -> ca-slave plugin loaded ???";
} else {
int tries = 20;
while ((m_sys_areDCdataValid = m_hw->sys_areDCdataValid()) == false) {
// must deliver 'true', only then are all data from hwapi valid
if (--tries < 0) {
qCritical() << "ERROR!!! DC DATA NOT VALID -> CA-MASTER-PLUGIN NOT CONNECTED";
break;
}
m_hw->dc_autoRequest(true);
QThread::msleep(500);
}
qCritical() << "(" << __func__ << ":" << __LINE__ << ") m_sys_areDCDataValid ..."
<< m_sys_areDCdataValid;
}
}
}
Update::~Update() {
// unloadDCPlugin();
}
#if 0
bool Update::doUpdate() { // test function
int numberOfFiles = 3;
QString s = nextTimePoint();
s += " sending ";
s += QString("%1 ...done <JS-PROGRESS>").arg("DC2C_cash.json");
s += QString::number(ceil(((1 * 100.0) / (double)numberOfFiles)));
qInfo() << s.toUtf8().constData();
QThread::msleep(2000);
s = nextTimePoint();
s += " sending ";
s += QString("%1 ...done <JS-PROGRESS>").arg("DC2C_device.json");
s += QString::number(ceil(((2 * 100.0) / (double)numberOfFiles)));
qInfo() << s.toUtf8().constData();
QThread::msleep(2000);
s = nextTimePoint();
s += " sending ";
s += QString("%1 ...done <JS-PROGRESS>").arg("DC2C_print01.json");
s += QString::number(ceil(((3 * 100.0) / (double)numberOfFiles)));
qInfo() << s.toUtf8().constData();
QThread::msleep(2000);
return true;
}
#endif
bool Update::doUpdate(QStringList const &filesToWorkOn, bool usbStickDetected) {
if (!m_hw) {
qCritical() << "(" << __func__ << ":" << __LINE__ << "):"
<< "ERROR!!! m_hw == nullptr";
return false;
}
int tries = 20;
while ((m_sys_areDCdataValid = m_hw->sys_areDCdataValid()) == false) {
// must deliver 'true', only then are all data from hwapi valid
if (--tries < 0) {
qCritical() << "(" << __func__ << ":" << __LINE__ << "):"
<< "ERROR!!! DC DATA NOT VALID -> CA-SLAVE-PLUGIN NOT CONNECTED";
return false;
}
qCritical() << "(" << __func__ << ":" << __LINE__ << "):"
<< "ERROR!!! DC DATA NOT VALID -> CA-SLAVE-PLUGIN NOT CONNECTED (" << tries << ")";
m_hw->dc_autoRequest(true);
QThread::msleep(500);
}
bool res = false;
QList<QString>::const_iterator it;
for (it = filesToWorkOn.cbegin(); it != filesToWorkOn.cend(); ++it) {
QString const &fToWorkOn = usbStickDetected ? QDir::cleanPath(it->trimmed())
: QDir::cleanPath(m_customerRepository + QDir::separator() + it->trimmed());
if (fToWorkOn.contains("DC2C_print", Qt::CaseInsensitive)
&& fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) {
res = true;
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, fToWorkOn))) {
qCritical() <<
QString("DOWNLOADED PRINTER TEMPLATE %1 WITH INDEX=%2")
.arg(fToWorkOn)
.arg(templateIdx);
}
}
} else if (fToWorkOn.contains("DC2C_cash", Qt::CaseInsensitive)
&& fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) {
if ((res = updateCashConf(fToWorkOn))) {
qCritical() << QString("DOWNLOADED CASH TEMPLATE %1").arg(fToWorkOn);
}
} else if (fToWorkOn.contains("DC2C_conf", Qt::CaseInsensitive)
&& fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) {
if ((res = updateConfig(fToWorkOn))) {
qCritical() << QString("DOWNLOADED CONFIG TEMPLATE %1").arg(fToWorkOn);
}
} else if (fToWorkOn.contains("DC2C_device", Qt::CaseInsensitive)
&& fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) {
if ((res = updateDeviceConf(fToWorkOn))) {
qCritical() << QString("DOWNLOADED DEVICE TEMPLATE %1").arg(fToWorkOn);
}
} else {
qCritical() << "UNKNOWN JSON FILE NAME" << fToWorkOn;
res = false;
}
}
return res;
}
bool Update::checkJsonVersions(QStringList const& jsonFileNames) {
if (!m_hw) {
qCritical() << "(" << __func__ << ":" << __LINE__ << "):"
<< "ERROR!!! m_hw == nullptr";
return false;
}
int tries = 20;
while ((m_sys_areDCdataValid = m_hw->sys_areDCdataValid()) == false) {
// must deliver 'true', only then are all data from hwapi valid
if (--tries < 0) {
qCritical() << "(" << __func__ << ":" << __LINE__ << "):"
<< "ERROR!!! DC DATA NOT VALID -> CA-SLAVE-PLUGIN NOT CONNECTED";
return false;
}
qCritical() << "(" << __func__ << ":" << __LINE__ << "):"
<< "ERROR!!! DC DATA NOT VALID -> CA-SLAVE-PLUGIN NOT CONNECTED (" << tries << ")";
m_hw->dc_autoRequest(true);
QThread::msleep(500);
}
for (QStringList::size_type i=0; i < jsonFileNames.size(); ++i) {
uint8_t jsonNr = 0;
QString const &fName = jsonFileNames[i];
// send one request for every single version
// jsonNr=1...36, 1=config file (cust.Nr) 2=devices 3=cash 4=res.
// 6=printer template 1 ..... 36= template 32
if (fName.endsWith("conf.json")) {
jsonNr = 1;
} else
if (fName.endsWith("device.json")) {
jsonNr = 2;
} else
if (fName.endsWith("cash.json")) {
jsonNr = 3;
} else {
QRegularExpressionMatch match;
static const QRegularExpression re("^(.*print)([0-3][0-9])\\.json\\s*$");
int idx = fName.indexOf(re, 0, &match);
if (idx != -1) {
QString captured = match.captured(match.lastCapturedIndex());
bool ok = false;
int n = captured.toInt(&ok);
if (ok) {
// note: use 5 (instead of 4 -> index has been shifted)
jsonNr = n + 5;
}
}
}
if (jsonNr != 0) {
// send one request for every single version
// jsonNr=1...36, 1=config file (cust.Nr) 2=devices 3=cash 4=res.
// 5=printer template 1 ..... 36= template 32
m_hw->sys_requestJsonVersions(jsonNr);
QThread::msleep(500);
char buf[64];
memset(buf, 0x00, sizeof(buf));
m_hw->sys_getJsonVersions(jsonNr, buf);
buf[16] = '\0'; // the DC only handles 16 bytes
static const QByteArray cb(16, (char)0xff);
QString const installedVersion(QString::fromStdString(buf));
QString const fileVersion = getFileVersion(jsonFileNames[i]);
QFileInfo fi(jsonFileNames[i]);
qCritical() << endl;
qCritical() << " json request nr:" << jsonNr;
if (installedVersion == fileVersion) {
qCritical() << " json file:" << fi.fileName();
qCritical() << " installed version in DC:" << installedVersion;
} else
if (cb == QByteArray(buf) && fileVersion == "") {
qCritical() << "unknown json file (repo and DC):" << fi.fileName();
} else {
qCritical() << " json file:" << fi.fileName();
qCritical() << " installed version in DC:" << installedVersion;
qCritical() << " file version in repository:" << fileVersion;
}
} else {
qCritical() << "CANNOT FIND JSON-NR FOR" << fName;
}
}
return false;
}
QString Update::getFileVersion(QString const& jsonFileName) {
// "version":"15.10.2023 14:55 02.00.06",
static const QRegularExpression re("^.*(\\\"[Vv]ersion\\\":)([\\s\\\"]{0,})([^,\\\"]{0,}).*$");
QString fileVersion("");
QFile inputFile(QDir::cleanPath(m_customerRepository + QDir::separator() + jsonFileName));
if (inputFile.exists()) {
if (inputFile.open(QIODevice::ReadOnly)) {
QTextStream in(&inputFile);
while (!in.atEnd()) {
QString line = in.readLine();
QRegularExpressionMatch match;
int idx = line.indexOf(re, 0, &match);
if (idx != -1) {
int const lastCaptured = match.lastCapturedIndex();
// the dc only sends 16 Byte
fileVersion = match.captured(lastCaptured);
fileVersion.truncate(16);
break;
}
}
inputFile.close();
}
} else {
// qCritical() << "ERROR" << inputFile.fileName() << "does not exist";
}
return fileVersion;
}
bool Update::downloadJson(enum FileTypeJson type,
int templateIdx,
QString jsFileToSendToDC) const {
m_hw->dc_autoRequest(true); // downloading Json needs the AutoEmission flag
qDebug() << "SET AUTO-REQUEST=TRUE";
QThread::sleep(1); // make sure the auto-request flag is acknowledged
QStringList lst;
bool ready = false;
int nTry = 25;
while ((ready = m_hw->sys_ready4sending()) == false) {
QThread::msleep(200);
if (--nTry <= 0) {
qCritical() << "SYS NOT READY FOR SENDING AFTER 5 SECONDS";
break;
}
}
bool ret = false;
QString msg;
lst.clear();
if (ready) {
QFile file(jsFileToSendToDC);
QFileInfo fi(jsFileToSendToDC); // max. size of template file is 800 bytes
if (file.exists()) {
if (file.open(QIODevice::ReadOnly)) {
if (fi.size() > 0 && 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())) {
/*
* Note: the machine id is contained in DC2C_conf.json.
* The idea was to use this to check if the download of
* the json-file was correct. It did not work, as the
* update of the PSA (to reflect a change in the
* machine id) did not happen immediately.
*
m_hw->dc_autoRequest(true);
QThread::msleep(500);
// testing
m_hw->request_ReadbackMachineID();
QThread::msleep(500);
uint8_t data[64];
memset(data, 0x00, sizeof(data));
uint8_t length = 0;
m_hw->readback_machineIDdata(&length, data);
QThread::msleep(500);
QByteArray ba((const char*)data, length);
qCritical() << length << "MACHINE ID =" << ba.toHex(':');
*/
ret = true;
} else {
qCritical() << QString("ERROR SEND JSON-FILE %1 TO DC").arg(file.fileName());
}
} else {
qCritical() << QString("SIZE OF %1 TOO BIG (%2 BYTES)").arg(jsFileToSendToDC).arg(fi.size());
}
} else {
qCritical() << QString("CAN NOT OPEN ") + jsFileToSendToDC + " FOR READING";
}
} else {
qCritical() << (QString(jsFileToSendToDC) + " DOES NOT EXIST");
}
}
return ret;
}
bool Update::updatePrinterTemplate(int templateIdx, QString jsFile) const {
return downloadJson(FileTypeJson::PRINTER, templateIdx, jsFile);
}
bool Update::updateConfig(QString jsFile) {
return downloadJson(FileTypeJson::CONFIG, 0, jsFile);
}
bool Update::updateCashConf(QString jsFile) {
return downloadJson(FileTypeJson::CASH, 0, jsFile);
}
bool Update::updateDeviceConf(QString jsFile) {
return downloadJson(FileTypeJson::DEVICE, 0, jsFile);
}

View File

@@ -0,0 +1,160 @@
#ifndef UPDATE_H_INCLUDED
#define UPDATE_H_INCLUDED
#include <QObject>
#include <QString>
#include <QFile>
#include <QDir>
#include <QByteArray>
#include <QProcess>
#include <QPluginLoader>
#include <QDateTime>
#include <initializer_list>
#include <DeviceController/interfaces.h>
#ifdef PTU5
#define SERIAL_PORT "ttymxc2"
#else
#define SERIAL_PORT "ttyUSB0"
#endif
class Update : public QObject {
Q_OBJECT
hwinf *m_hw = nullptr;
char const *m_serialInterface;
char const *m_baudrate;
QString m_customerRepository;
QString m_customerNrStr;
QString m_branchName;
QString m_pluginName;
QString m_workingDir;
bool m_maintenanceMode;
bool m_dryRun;
bool m_sys_areDCdataValid;
static QPluginLoader pluginLoader;
QDateTime m_start;
QString nextTimePoint() const {
float const secs = m_start.msecsTo(QDateTime::currentDateTime()) / 1000.0;
return QStringLiteral("+%1s").arg(secs, 7, 'f', 2, QChar('0'));
}
static qint64 c_ppid;
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 = ',');
static qint64 ppid() { return c_ppid; }
static void setPPid(qint64 ppid) { c_ppid = ppid; }
explicit Update(QString customerRepository,
QString customerNrStr,
QString branchName,
QString plugInDir,
QString pluginName,
QString workingDir,
bool dryRun = false,
QObject *parent = nullptr,
char const *serialInterface = SERIAL_PORT,
char const *baudrate = "115200");
virtual ~Update() override;
bool doUpdate(QStringList const &jsonFilesToDownload, bool usbStickDetected = false);
bool doUpdate();
bool updatePrinterTemplate(int templateIdx, QString fname) const;
bool updateConfig(QString jsFileToSendToDC);
bool updateCashConf(QString jsFileToSendToDC);
bool updateDeviceConf(QString jsFileToSendToDC);
bool downloadJson(enum FileTypeJson type, int templateIdx,
QString jsFileToSendToDC) const;
QString getFileVersion(QString const& jsonFileName);
bool checkJsonVersions(QStringList const& jsonFileNames =
QStringList(
QList(
std::initializer_list<QString>{
QString("etc/psa_config/DC2C_conf.json"),
QString("etc/psa_config/DC2C_cash.json"),
QString("etc/psa_config/DC2C_device.json"),
QString("etc/psa_config/DC2C_print01.json"),
QString("etc/psa_config/DC2C_print02.json"),
QString("etc/psa_config/DC2C_print03.json"),
QString("etc/psa_config/DC2C_print04.json"),
QString("etc/psa_config/DC2C_print05.json"),
QString("etc/psa_config/DC2C_print06.json"),
QString("etc/psa_config/DC2C_print07.json"),
QString("etc/psa_config/DC2C_print08.json"),
QString("etc/psa_config/DC2C_print09.json"),
QString("etc/psa_config/DC2C_print10.json"),
QString("etc/psa_config/DC2C_print11.json"),
QString("etc/psa_config/DC2C_print12.json"),
QString("etc/psa_config/DC2C_print13.json"),
QString("etc/psa_config/DC2C_print14.json"),
QString("etc/psa_config/DC2C_print15.json"),
QString("etc/psa_config/DC2C_print16.json"),
QString("etc/psa_config/DC2C_print17.json"),
QString("etc/psa_config/DC2C_print18.json"),
QString("etc/psa_config/DC2C_print19.json"),
QString("etc/psa_config/DC2C_print20.json"),
QString("etc/psa_config/DC2C_print21.json"),
QString("etc/psa_config/DC2C_print22.json"),
QString("etc/psa_config/DC2C_print23.json"),
QString("etc/psa_config/DC2C_print24.json"),
QString("etc/psa_config/DC2C_print25.json"),
QString("etc/psa_config/DC2C_print26.json"),
QString("etc/psa_config/DC2C_print27.json"),
QString("etc/psa_config/DC2C_print28.json"),
QString("etc/psa_config/DC2C_print29.json"),
QString("etc/psa_config/DC2C_print30.json"),
QString("etc/psa_config/DC2C_print31.json"),
QString("etc/psa_config/DC2C_print32.json")})));
/*
bool checkDownloadedJsonVersions(QStringList const& jsonFileNames);
hwinf *hw() { return m_hw; }
hwinf const *hw() const { return m_hw; }
//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; }
private:
static QString jsonType(enum FileTypeJson type);
bool openSerial(int br, QString baudrate, QString comPort) const;
void closeSerial() const;
bool isSerialOpen() const;
bool resetDeviceController() const;
QByteArray loadBinaryDCFile(QString filename) const;
bool downloadBinaryToDC(QString const &bFile) const;
bool updateBinary(QString const &fileToSendToDC);
QStringList getDcSoftAndHardWareVersion();
QString getFileVersion(QString const& jsonFile);
private slots:
void readyReadStandardOutput();
void readyReadStandardError();
void finished(int exitCode, QProcess::ExitStatus exitStatus);
void onReportDCDownloadStatus(QString const &status);
void onReportDCDownloadSuccess(QString const &msg);
void onReportDCDownloadFailure(QString const &errorMsg);
*/
};
#endif // UPDATE_H_INCLUDED

2970
Doxyfile Normal file

File diff suppressed because it is too large Load Diff

87
Git/Git.pro Normal file
View File

@@ -0,0 +1,87 @@
QT += core
TARGET = ATBUpdateGit
VERSION="1.0.0"
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")
EXTENDED_VERSION="$${VERSION}-$${GIT_COMMIT}"
INCLUDEPATH += plugins \
$${INCLUDEINTERFACES}/ \
$${_PRO_FILE_PWD_}/../common/ \
$${_PRO_FILE_PWD_}/../common/include
CONFIG += c++17
DEFINES+=APP_VERSION=\\\"$$VERSION\\\"
DEFINES+=APP_BUILD_DATE=\\\"$$BUILD_DATE\\\"
DEFINES+=APP_BUILD_TIME=\\\"$$BUILD_TIME\\\"
DEFINES+=APP_EXTENDED_VERSION=\\\"$$EXTENDED_VERSION\\\"
# keep comments, as /* fall through */
QMAKE_CXXFLAGS += -C
QMAKE_CXXFLAGS += -g
QMAKE_CXXFLAGS += -Wno-deprecated-copy -O
contains( CONFIG, PTU5 ) {
CONFIG += link_pkgconfig
QMAKE_CXXFLAGS += -O2 -std=c++17 # for GCC >= 4.7
# QMAKE_CXXFLAGS += -Wno-deprecated-copy
PTU5BASEPATH = /opt/devel/ptu5
INCLUDEPATH += $$PTU5BASEPATH/qt/libs/devicecontroller/include
LIBS += -L$$PTU5BASEPATH/qt/libs/devicecontroller/library
ARCH = PTU5
DEFINES+=PTU5
}
contains( CONFIG, PTU5_YOCTO ) {
QMAKE_CXXFLAGS += -std=c++17 # for GCC >= 4.7
# QMAKE_CXXFLAGS += -Wno-deprecated-copy
PTU5BASEPATH = /opt/devel/ptu5
ARCH = PTU5
DEFINES+=PTU5
}
contains( CONFIG, DesktopLinux ) {
# QMAKE_CC = ccache $$QMAKE_CC
# QMAKE_CXX = ccache $$QMAKE_CXX
QMAKE_CXXFLAGS += -std=c++17
# QMAKE_CXXFLAGS += -Wno-deprecated-copy
linux-clang { QMAKE_CXXFLAGS += -Qunused-arguments }
ARCH = DesktopLinux
DEFINES+=DesktopLinux
}
SOURCES += \
main.cpp \
message_handler.cpp \
../common/src/utils_internal.cpp \
../common/src/command.cpp \
../common/src/commandline_parser.cpp \
git_command.cpp
HEADERS += \
message_handler.h \
../common/include/utils_internal.h \
../common/include/command.h \
../common/include/commandline_parser.h \
git_command.h
##########################################################################################
# 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
}

105
Git/git_command.cpp Normal file
View File

@@ -0,0 +1,105 @@
#include "git_command.h"
#include "command.h"
#include "utils_internal.h"
using namespace internal;
#include <QProcess>
#include <QByteArray>
#include <QFileInfo>
#include <QDebug>
#include <QDir>
bool GitCommand::initEnv = false;
static bool initEnv() {
QString gitSSHCommand{""};
QByteArray const v = qgetenv("GIT_SSH_COMMAND");
if (v.isEmpty()) {
QString sshKeyFile("/opt/app/tools/atbupdate/.keys/id_ed25519_ptuConfig");
if (QFileInfo(sshKeyFile).exists()) {
if (qgetenv("GIT_SSH_COMMAND").isNull()) {
gitSSHCommand = "ssh -i /opt/app/tools/atbupdate/.keys/id_ed25519_ptuConfig";
if (!qputenv("GIT_SSH_COMMAND", QByteArray(gitSSHCommand.toStdString().c_str()))) {
qCritical() << "ERROR: GIT_SSH_COMMAND not put into env. Exiting...";
return false;
}
}
} else {
qCritical() << "ERROR ssh-key-file" << sshKeyFile << "does not exists. Exiting...";
return false;
}
} else {
gitSSHCommand = QString(v.toStdString().c_str());
qCritical() << "WARNING GIT_SSH_COMMAND already set in enviroment:"
<< gitSSHCommand;
if (gitSSHCommand != "ssh -i /opt/app/tools/atbupdate/.keys/id_ed25519_ptuConfig") {
qCritical() << "ERROR" << gitSSHCommand << "wrong. Exiting...";
return false;
}
}
// qCritical() << __func__ << ":" << __LINE__ << gitSSHCommand;
return true;
}
GitCommand::GitCommand()
: m_workingDirectory(customerRepoDir()) {
// qCritical() << __func__ << ":" << __LINE__ << m_workingDirectory;
if (!GitCommand::initEnv) {
GitCommand::initEnv = ::initEnv();
}
}
bool GitCommand::exec(QStringList const &options, int start_timeout, int finish_timeout) {
bool ret = false;
if (GitCommand::initEnv) {
Command cmd(QString("git"), options, m_workingDirectory,
start_timeout, finish_timeout);
ret = cmd.exec();
//qCritical() << __func__ << ":" << __LINE__ << cmd.command()
// << "," << cmd.args()
// << ", result" << cmd.commandResult();
m_commandResult = cmd.commandResult();
}
return ret;
}
bool GitCommand::check(int start_timeout, int finish_timeout) {
m_workingDirectory = customerRepoDir();
QStringList const lst{"fsck"};
return exec(lst, start_timeout, finish_timeout);
}
bool GitCommand::checkout(int start_timeout, int finish_timeout) {
m_workingDirectory = customerRepoDir();
int const zoneNr = read1stLineOfFile("/mnt/system_data/zone_nr");
if (zoneNr != -1) {
QStringList const lst{"checkout", QString("zg1/zone%1").arg(zoneNr)};
// qCritical() << __func__ << __LINE__ << lst;
return exec(lst, start_timeout, finish_timeout);
}
return false;
}
bool GitCommand::clone(int start_timeout, int finish_timeout) {
m_workingDirectory = customerRepoRoot(); // /opt/app/tools/atbupdate !
QDir const repoDir{customerRepoDir()};
if (repoDir.exists() && repoDir.entryInfoList(QDir::NoDotAndDotDot|QDir::AllEntries).count() != 0) {
qCritical() << "clone error:" << repoDir << "exists and is not empty";
return false;
}
// git clone "gitea@ptu-config.atb-comm.de:ATB/customer_999"
QStringList const lst{"clone", repositoryUrl() + customerRepoDirName()};
return exec(lst, start_timeout, finish_timeout);
}
bool GitCommand::pull(int start_timeout, int finish_timeout) {
m_workingDirectory = customerRepoDir();
QStringList const lst{"pull"};
return exec(lst, start_timeout, finish_timeout);
}
bool GitCommand::status(int start_timeout, int finish_timeout) {
m_workingDirectory = customerRepoDir();
QStringList const lst{"status"};
return exec(lst, start_timeout, finish_timeout);
}

30
Git/git_command.h Normal file
View File

@@ -0,0 +1,30 @@
#ifndef GIT_COMMAND_H_INCLUDED
#define GIT_COMMAND_H_INCLUDED
#include <QStringList>
#include <QScopedPointer>
class QProcess;
class GitCommand {
static bool initEnv;
QString m_workingDirectory;
bool exec(QStringList const &options, int start_timeout = 100000,
int finish_timeout = 100000);
QString m_commandResult{};
public:
GitCommand();
void resetCommandResult() { m_commandResult.clear(); }
QString const &commandResult() const { return m_commandResult; }
bool status(int start_timeout = 100000, int finish_timeout = 100000);
bool check(int start_timeout = 100000, int finish_timeout = 100000);
bool checkout(int start_timeout = 100000, int finish_timeout = 100000);
bool clone(int start_timeout = 100000, int finish_timeout = 100000);
bool pull(int start_timeout = 100000, int finish_timeout = 100000);
};
#endif // GIT_COMMAND_H_INCLUDED

170
Git/main.cpp Normal file
View File

@@ -0,0 +1,170 @@
#include <QtGlobal>
#include <QCoreApplication>
#include <QByteArray>
#include <QProcess>
#include <QCommandLineParser>
#include <QStandardPaths>
#include <QSettings>
#include <QDir>
#include <QDebug>
#include <QDateTime>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QRegularExpression>
#include <QFile>
#include <QTextStream>
#include <QRegularExpression>
#include <optional>
#include "message_handler.h"
#include "utils_internal.h"
#include "git_command.h"
#include "commandline_parser.h"
int main(int argc, char **argv) {
QByteArray const value = qgetenv("LC_ALL");
if (value.isEmpty() || value != "C") {
qputenv("LC_ALL", "C");
}
openlog("ATB-UPDATE-GIT", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER);
QCoreApplication a(argc, argv);
QCoreApplication::setApplicationName("ATBUpdateGit");
QCoreApplication::setApplicationVersion(APP_VERSION);
if (!messageHandlerInstalled()) { // change internal qt-QDebug-handling
atbInstallMessageHandler(nullptr);
//atbInstallMessageHandler(atbDebugOutput);
setDebugLevel(LOG_NOTICE);
}
CommandLineParser parser;
parser.setApplicationDescription("git-commands for the update-system");
QCommandLineOption const checkCustomerRepositoryOption{"check"};
QCommandLineOption const cloneCustomerRepositoryOption{"clone"};
QCommandLineOption const pullBranchOption{"pull"};
QCommandLineOption const checkoutBranchOption("checkout");
parser.addOption(checkCustomerRepositoryOption);
parser.addOption(cloneCustomerRepositoryOption);
parser.addOption(pullBranchOption);
parser.addOption(checkoutBranchOption);
QCommandLineOption verboseOption{parser.addVersionOption()};
parser.process(a);
parser.readSettings();
#if 0
// note: also used in initEnv().
QString repositoryUrl = parser.repositoryUrl();
if (repositoryUrl.endsWith('/')) {
repositoryUrl.chop(1);
}
if (!repositoryUrl.isEmpty()) {
qInfo() << "customer repository url" << repositoryUrl;
} else {
qCritical() << "ERROR customer repository url empty. git commands might fail.";
}
if (repositoryUrl.contains("ptu-config.atb-comm.de")) {
QString gitSSHCommand("");
QByteArray const v = qgetenv("GIT_SSH_COMMAND");
if (v.isEmpty()) {
QString sshKeyFile("/opt/app/tools/atbupdate/.keys/id_ed25519_ptuConfig");
if (QFileInfo(sshKeyFile).exists()) {
if (qgetenv("GIT_SSH_COMMAND").isNull()) {
gitSSHCommand = "ssh -i /opt/app/tools/atbupdate/.keys/id_ed25519_ptuConfig";
if (!qputenv("GIT_SSH_COMMAND", QByteArray(gitSSHCommand.toStdString().c_str()))) {
qCritical() << "ERROR: GIT_SSH_COMMAND not put into env. Exiting...";
return -1;
}
}
} else {
qCritical() << "ERROR ssh-key-file" << sshKeyFile << "does not exists. Exiting...";
return -1;
}
} else {
gitSSHCommand = QString(v.toStdString().c_str());
qInfo() << "GIT_SSH_COMMAND already set in enviroment:" << gitSSHCommand;
if (gitSSHCommand != "ssh -i /opt/app/tools/atbupdate/.keys/id_ed25519_ptuConfig") {
qCritical() << "ERROR" << gitSSHCommand << "wrong. Exiting...";
return -1;
}
}
if (!gitSSHCommand.isEmpty()) {
qInfo() << "GIT_SSH_COMMAND .........." << gitSSHCommand;
}
}
#endif
if (parser.isSet(verboseOption)) {
parser.showVersion();
return 0;
}
GitCommand gitCmd;
if (parser.isSet(checkCustomerRepositoryOption)) {
if (!gitCmd.check()) {
return -1;
}
} else
if (parser.isSet(checkoutBranchOption)) {
if (!gitCmd.checkout()) {
return -2;
}
} else
if (parser.isSet(cloneCustomerRepositoryOption)) {
if (!gitCmd.clone()) {
return -3;
}
} else
if (parser.isSet(pullBranchOption)) {
if (!gitCmd.pull()) {
return -4;
}
} else {
if (internal::customerRepoExists()) {
if (!gitCmd.checkout()) {
return -2;
}
if (!gitCmd.pull()) {
return -4;
}
QString const result = gitCmd.commandResult().trimmed();
if (result.contains("Already", Qt::CaseInsensitive)
&& result.contains("up", Qt::CaseInsensitive)
&& result.contains("to", Qt::CaseInsensitive)
&& result.contains("date", Qt::CaseInsensitive)) {
qCritical() << internal::GIT_CUSTOMER_REPO_NO_UPDATE_NECESSARY;
return internal::GIT_NOT_NECESSARY_CODE;
} else
if (result.contains(QRegularExpression("[Uu]pdating\\s+[a-z0-9]{6,}\\.\\.[a-z0-9]{6,}"))) {
// Updating 49a97f5..13a0321
qCritical() << internal::GIT_CUSTOMER_REPO_UPDATED;
return internal::GIT_UPDATED_CODE;
}
} else {
if (!gitCmd.clone()) {
return -3;
}
if (!gitCmd.checkout()) {
return -2;
}
qCritical() << internal::GIT_CUSTOMER_REPO_CLONED;
return internal::GIT_CLONED_CODE;
}
}
return 0;
}

97
Git/message_handler.cpp Executable file
View File

@@ -0,0 +1,97 @@
#include "message_handler.h"
#include <QDateTime>
#include <cstring>
#include <QString>
#include <QFileInfo>
#include <QMessageLogContext>
static char const *DBG_NAME[] = { "DBG ", "WARN ", "CRIT ", "FATAL", "INFO " };
static bool installedMsgHandler = false;
static int debugLevel = LOG_NOTICE;
int getDebugLevel() { return debugLevel; }
void setDebugLevel(int newDebugLevel) {
debugLevel = newDebugLevel;
}
bool messageHandlerInstalled() {
return installedMsgHandler;
}
QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler) {
installedMsgHandler = (handler != 0);
static QtMessageHandler prevHandler = nullptr;
if (handler) {
prevHandler = qInstallMessageHandler(handler);
return prevHandler;
} else {
return qInstallMessageHandler(prevHandler);
}
}
///
/// \brief Print message according to given debug level.
///
/// \note Install this function using qInstallMsgHandler().
///
/// int main(int argc, char **argv) {
/// installMsgHandler(atbDebugOutput);
/// QApplication app(argc, argv);
/// ...
/// return app.exec();
/// }
///
#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) {
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 LOG_NOTICE: { // normal, but significant, condition
if (type != QtDebugMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
case LOG_WARNING: { // warning conditions
if (type != QtInfoMsg && type != QtDebugMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
case LOG_ERR: { // error conditions
if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
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());
}
}
}
#endif

23
Git/message_handler.h Executable file
View File

@@ -0,0 +1,23 @@
#ifndef MESSAGE_HANDLER_H_INCLUDED
#define MESSAGE_HANDLER_H_INCLUDED
#include <QtGlobal>
#ifdef __linux__
#include <syslog.h>
#endif
int getDebugLevel();
void setDebugLevel(int newDebugLevel);
bool messageHandlerInstalled();
QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler);
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
// typedef void (*QtMessageHandler)(QtMsgType, const char *);
void atbDebugOutput(QtMsgType type, const char *msg);
#elif QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
// typedef void (*QtMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &);
void atbDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg);
#endif
#endif // MESSAGE_HANDLER_H_INCLUDED

85
Opkg/Opkg.pro Normal file
View File

@@ -0,0 +1,85 @@
QT += core
TARGET = ATBUpdateOpkg
VERSION="1.0.0"
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")
EXTENDED_VERSION="$${VERSION}-$${GIT_COMMIT}"
INCLUDEPATH += \
plugins \
$${INCLUDEINTERFACES} \
$${_PRO_FILE_PWD_}/../UpdatePTUDevCtrl \
$${_PRO_FILE_PWD_}/../common/include
CONFIG += c++17
DEFINES+=APP_VERSION=\\\"$$VERSION\\\"
DEFINES+=APP_BUILD_DATE=\\\"$$BUILD_DATE\\\"
DEFINES+=APP_BUILD_TIME=\\\"$$BUILD_TIME\\\"
DEFINES+=APP_EXTENDED_VERSION=\\\"$$EXTENDED_VERSION\\\"
# keep comments, as /* fall through */
QMAKE_CXXFLAGS += -C
QMAKE_CXXFLAGS += -g
QMAKE_CXXFLAGS += -Wno-deprecated-copy -O
contains( CONFIG, PTU5 ) {
CONFIG += link_pkgconfig
QMAKE_CXXFLAGS += -O2 -std=c++17 # for GCC >= 4.7
# QMAKE_CXXFLAGS += -Wno-deprecated-copy
PTU5BASEPATH = /opt/devel/ptu5
INCLUDEPATH += $$PTU5BASEPATH/qt/libs/devicecontroller/include
LIBS += -L$$PTU5BASEPATH/qt/libs/devicecontroller/library
ARCH = PTU5
DEFINES+=PTU5
}
contains( CONFIG, PTU5_YOCTO ) {
QMAKE_CXXFLAGS += -std=c++17 # for GCC >= 4.7
# QMAKE_CXXFLAGS += -Wno-deprecated-copy
PTU5BASEPATH = /opt/devel/ptu5
ARCH = PTU5
DEFINES+=PTU5
}
contains( CONFIG, DesktopLinux ) {
# QMAKE_CC = ccache $$QMAKE_CC
# QMAKE_CXX = ccache $$QMAKE_CXX
QMAKE_CXXFLAGS += -std=c++17
# QMAKE_CXXFLAGS += -Wno-deprecated-copy
linux-clang { QMAKE_CXXFLAGS += -Qunused-arguments }
ARCH = DesktopLinux
DEFINES+=DesktopLinux
}
SOURCES += \
main.cpp \
message_handler.cpp \
../common/src/utils_internal.cpp \
../common/src/command.cpp \
opkg_command.cpp
HEADERS += \
message_handler.h \
../common/include/utils_internal.h \
../common/include/command.h \
opkg_command.h
##########################################################################################
# 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
}

69
Opkg/main.cpp Normal file
View File

@@ -0,0 +1,69 @@
#include <QtGlobal>
#include <QCoreApplication>
#include <QByteArray>
#include <QProcess>
#include <QCommandLineParser>
#include <QCommandLineOption>
#include <QStandardPaths>
#include <QSettings>
#include <QDir>
#include <QDebug>
#include "message_handler.h"
#include "utils_internal.h"
#include "opkg_command.h"
int main(int argc, char **argv) {
QByteArray const value = qgetenv("LC_ALL");
if (value.isEmpty() || value != "C") {
qputenv("LC_ALL", "C");
}
openlog("ATB-UPDATE-OPKG", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER);
QCoreApplication a(argc, argv);
QCoreApplication::setApplicationName("ATBUpdateOpkg");
QCoreApplication::setApplicationVersion(APP_VERSION);
if (!messageHandlerInstalled()) { // change internal qt-QDebug-handling
atbInstallMessageHandler(nullptr);
//atbInstallMessageHandler(atbDebugOutput);
setDebugLevel(LOG_NOTICE);
}
#if 0
QString s = "<OPKG>\n\naaa<OPKG>bbb<OPKG>ccc<OPKG>\n";
QString m_standardOutput{};
if (!s.isEmpty()) {
m_standardOutput += s.replace(QChar('\n'), "");
qCritical() << m_standardOutput;
int startIndex, endIndex{};
while (((startIndex = m_standardOutput.indexOf("<OPKG>")) == 0) &&
((endIndex = m_standardOutput.indexOf("<OPKG>", 1)) != -1)) {
QString str = m_standardOutput.mid(0, endIndex);
qCritical() << "str" << str << str.mid(6);
m_standardOutput = m_standardOutput.mid(endIndex);
// qCritical() << "m" << m_standardOutput;
}
qCritical() << "m" << m_standardOutput;
}
return 0;
#endif
QCommandLineParser parser;
QCommandLineOption noactionOption("noaction");
QCommandLineOption verboseOption("verbose");
parser.addOption(noactionOption);
parser.addOption(verboseOption);
parser.process(a);
bool noaction = parser.isSet(noactionOption);
OpkgCommand opkgCmd(noaction);
return 0;
}

97
Opkg/message_handler.cpp Executable file
View File

@@ -0,0 +1,97 @@
#include "message_handler.h"
#include <QDateTime>
#include <cstring>
#include <QString>
#include <QFileInfo>
#include <QMessageLogContext>
static char const *DBG_NAME[] = { "DBG ", "WARN ", "CRIT ", "FATAL", "INFO " };
static bool installedMsgHandler = false;
static int debugLevel = LOG_NOTICE;
int getDebugLevel() { return debugLevel; }
void setDebugLevel(int newDebugLevel) {
debugLevel = newDebugLevel;
}
bool messageHandlerInstalled() {
return installedMsgHandler;
}
QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler) {
installedMsgHandler = (handler != 0);
static QtMessageHandler prevHandler = nullptr;
if (handler) {
prevHandler = qInstallMessageHandler(handler);
return prevHandler;
} else {
return qInstallMessageHandler(prevHandler);
}
}
///
/// \brief Print message according to given debug level.
///
/// \note Install this function using qInstallMsgHandler().
///
/// int main(int argc, char **argv) {
/// installMsgHandler(atbDebugOutput);
/// QApplication app(argc, argv);
/// ...
/// return app.exec();
/// }
///
#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) {
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 LOG_NOTICE: { // normal, but significant, condition
if (type != QtDebugMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
case LOG_WARNING: { // warning conditions
if (type != QtInfoMsg && type != QtDebugMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
case LOG_ERR: { // error conditions
if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
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());
}
}
}
#endif

23
Opkg/message_handler.h Executable file
View File

@@ -0,0 +1,23 @@
#ifndef MESSAGE_HANDLER_H_INCLUDED
#define MESSAGE_HANDLER_H_INCLUDED
#include <QtGlobal>
#ifdef __linux__
#include <syslog.h>
#endif
int getDebugLevel();
void setDebugLevel(int newDebugLevel);
bool messageHandlerInstalled();
QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler);
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
// typedef void (*QtMessageHandler)(QtMsgType, const char *);
void atbDebugOutput(QtMsgType type, const char *msg);
#elif QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
// typedef void (*QtMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &);
void atbDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg);
#endif
#endif // MESSAGE_HANDLER_H_INCLUDED

176
Opkg/opkg_command.cpp Normal file
View File

@@ -0,0 +1,176 @@
#include "opkg_command.h"
#include "command.h"
#include "utils_internal.h"
using namespace internal;
#include <QDir>
#include <QDebug>
#include <QFlags>
#include <QRegularExpression>
OpkgCommand::OpkgCommand(bool noaction, QString const &opkg_commands_filename)
: m_noaction(noaction)
, m_opkg_commands_filename(opkg_commands_filename) {
cleanUpOpkgCache();
execCommandsInternal();
}
bool OpkgCommand::readCommands() {
QFile opkgFile(QDir::cleanPath(m_opkg_commands_dir + QDir::separator() + m_opkg_commands_filename));
if (!opkgFile.exists()) {
qCritical() << __func__ << ":" << __LINE__
<< opkgFile.fileName() << "does not exists";
return false;
}
m_commands.clear();
if (opkgFile.open(QIODevice::ReadOnly)) {
QTextStream in(&opkgFile);
while (!in.atEnd()) {
QString line = in.readLine();
// TODO: "^\\s*[#]{0,}$" : empty line or comment line starting with #
static const QRegularExpression comment("^\\s*[#].*$");
static const QRegularExpression emptyLine("^\\s*$");
if (line.indexOf(emptyLine, 0) == -1 &&
line.indexOf(comment, 0) == -1) {
QString const &commandLine = line.trimmed();
if (!commandLine.isEmpty()) {
m_commands << commandLine;
}
}
}
} else {
qCritical() << __func__ << ":" << __LINE__
<< "error opening" << opkgFile.fileName();
}
return m_commands.size() > 0;
}
bool OpkgCommand::execCommandsInternal() {
if (readCommands()) {
// command lines are located between markers: <OPKG>...<OPKG>
// they are later removed when used by the update-tool.
qCritical().noquote() << "<OPKG>";
QListIterator<QString> it(m_commands);
while (it.hasNext()) {
QString command = it.next();
QStringList cmdAndOptions = command.split(u' ', QString::SkipEmptyParts);
if (cmdAndOptions.size() > 0) {
QString const &cmd = cmdAndOptions.takeFirst();
if (m_noaction) {
if (cmd.contains("opkg")) {
cmdAndOptions.prepend("--noaction");
} else continue; // only opkg has the --noaction option
}
QStringList const &options = cmdAndOptions;
if (exec(cmd, options)) {
qCritical().noquote() << cmd << options.join(" ") << "ok" << "<OPKG>";
} else {
qCritical().noquote() << cmd << options.join(" ") << "FAIL" << "<OPKG>";
}
}
}
return true;
}
return false;
}
bool OpkgCommand::execCommands() {
if (readCommands()) {
QListIterator<QString> it(m_commands);
while (it.hasNext()) {
QString command = it.next();
QStringList cmdAndOptions = command.split(u' ', QString::SkipEmptyParts);
if (cmdAndOptions.size() > 0) {
QString const &cmd = cmdAndOptions.takeFirst();
if (m_noaction) {
cmdAndOptions.prepend("--noaction");
}
QStringList const &options = cmdAndOptions;
if (exec(cmd, options)) {
qCritical().noquote() << cmd << options.join(" ");
} else {
qCritical().noquote() << cmd << options.join(" ") << "FAIL";
return false;
}
}
}
return true;
}
return false;
#if 0
QFile opkgFile(QDir::cleanPath(m_opkg_commands_dir + QDir::separator() + m_opkg_commands_filename));
if (!opkgFile.exists()) {
qCritical() << __func__ << ":" << __LINE__
<< opkgFile.fileName() << "does not exists";
return false;
}
if (opkgFile.open(QIODevice::ReadOnly)) {
QTextStream in(&opkgFile);
while (!in.atEnd()) {
QString line = in.readLine();
// TODO: "^\\s*[#]{0,}$" : empty line or comment line starting with #
static const QRegularExpression comment("^\\s*[#].*$");
static const QRegularExpression emptyLine("^\\s*$");
if (line.indexOf(emptyLine, 0) == -1 &&
line.indexOf(comment, 0) == -1) {
QString const &commandLine = line.trimmed();
if (!commandLine.isEmpty()) {
QStringList cmdAndOptions = commandLine.split(u' ', QString::SkipEmptyParts);
if (cmdAndOptions.size() > 0) {
QString const &cmd = cmdAndOptions.takeFirst();
if (m_noaction) {
cmdAndOptions.prepend("--noaction");
}
QStringList const &options = cmdAndOptions;
if (exec(cmd, options)) {
qCritical().noquote() << cmd << options.join(" ") << "ok";
} else {
qCritical().noquote() << cmd << options.join(" ") << "FAIL";
}
} else {
continue;
}
}
}
}
return true;
} else {
qCritical() << __func__ << ":" << __LINE__
<< "error opening" << opkgFile.fileName();
}
return false;
#endif
}
bool OpkgCommand::exec(QString const &cmd, QStringList const &options,
int start_timeout, int finish_timeout) {
bool const verbose = false;
return Command(cmd, options, "/tmp", verbose, start_timeout, finish_timeout).exec();
}
bool OpkgCommand::cleanUpOpkgCache() {
bool removedFiles = true;
QDir dir("/var/cache/opkg/");
if (dir.exists()) {
dir.setNameFilters(QStringList() << ".gz" << ".ipk");
dir.setFilter(QDir::Files);
foreach(QString dirFile, dir.entryList()) {
removedFiles &= dir.remove(dirFile);
}
}
if (removedFiles == false) {
qCritical() << "some errors while cleaning up opkg-cache";
}
return removedFiles;
}

29
Opkg/opkg_command.h Normal file
View File

@@ -0,0 +1,29 @@
#ifndef OPKG_COMMND_H_INCLUDED
#define OPKG_COMMND_H_INCLUDED
#include <QStringList>
class OpkgCommand {
QString const m_opkg_commands_dir{"/etc/psa_update/"};
bool m_noaction;
QString m_opkg_commands_filename;
QStringList m_commands;
bool execCommands();
bool execCommandsInternal();
bool cleanUpOpkgCache();
public:
OpkgCommand(bool noaction = false,
QString const &opkg_commands_file_name="opkg_commands");
bool exec(QString const &cmd, QStringList const &options,
int start_timeout = 100000, int finish_timeout = 100000);
QStringList commands() { return m_commands; }
QStringList const &commands() const { return m_commands; }
bool readCommands();
};
#endif // OPKG_COMMND_H_INCLUDED

68
Show/Show.pro Normal file
View File

@@ -0,0 +1,68 @@
QT += core
TARGET = ATBUpdateShow
VERSION="1.0.0"
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")
EXTENDED_VERSION="$${VERSION}-$${GIT_COMMIT}"
INCLUDEPATH += \
plugins \
$${INCLUDEINTERFACES} \
$${_PRO_FILE_PWD_}/../common/include
CONFIG += c++17
DEFINES+=APP_VERSION=\\\"$$VERSION\\\"
DEFINES+=APP_BUILD_DATE=\\\"$$BUILD_DATE\\\"
DEFINES+=APP_BUILD_TIME=\\\"$$BUILD_TIME\\\"
DEFINES+=APP_EXTENDED_VERSION=\\\"$$EXTENDED_VERSION\\\"
# keep comments, as /* fall through */
QMAKE_CXXFLAGS += -C
QMAKE_CXXFLAGS += -g
QMAKE_CXXFLAGS += -Wno-deprecated-copy -O
contains( CONFIG, PTU5 ) {
CONFIG += link_pkgconfig
QMAKE_CXXFLAGS += -O2 -std=c++17 # for GCC >= 4.7
# QMAKE_CXXFLAGS += -Wno-deprecated-copy
PTU5BASEPATH = /opt/devel/ptu5
INCLUDEPATH += $$PTU5BASEPATH/qt/libs/devicecontroller/include
LIBS += -L$$PTU5BASEPATH/qt/libs/devicecontroller/library
ARCH = PTU5
DEFINES+=PTU5
}
contains( CONFIG, PTU5_YOCTO ) {
QMAKE_CXXFLAGS += -std=c++17 # for GCC >= 4.7
# QMAKE_CXXFLAGS += -Wno-deprecated-copy
PTU5BASEPATH = /opt/devel/ptu5
ARCH = PTU5
DEFINES+=PTU5
}
contains( CONFIG, DesktopLinux ) {
# QMAKE_CC = ccache $$QMAKE_CC
# QMAKE_CXX = ccache $$QMAKE_CXX
QMAKE_CXXFLAGS += -std=c++17
# QMAKE_CXXFLAGS += -Wno-deprecated-copy
linux-clang { QMAKE_CXXFLAGS += -Qunused-arguments }
ARCH = DesktopLinux
DEFINES+=DesktopLinux
}
SOURCES += \
main.cpp
# HEADERS += \

14
Show/main.cpp Normal file
View File

@@ -0,0 +1,14 @@
#include <QtGlobal>
#include <QCoreApplication>
#include <QByteArray>
#include <QProcess>
#include <QCommandLineParser>
#include <QStandardPaths>
#include <QSettings>
#include <QDir>
#include <QDebug>
int main(int argc, char **argv) {
return 0;
}

87
Sync/Sync.pro Normal file
View File

@@ -0,0 +1,87 @@
QT += core
TARGET = ATBUpdateSync
VERSION="1.0.0"
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")
EXTENDED_VERSION="$${VERSION}-$${GIT_COMMIT}"
INCLUDEPATH += \
plugins \
$${INCLUDEINTERFACES} \
$${_PRO_FILE_PWD_}/../UpdatePTUDevCtrl \
$${_PRO_FILE_PWD_}/../common/include
CONFIG += c++17
DEFINES+=APP_VERSION=\\\"$$VERSION\\\"
DEFINES+=APP_BUILD_DATE=\\\"$$BUILD_DATE\\\"
DEFINES+=APP_BUILD_TIME=\\\"$$BUILD_TIME\\\"
DEFINES+=APP_EXTENDED_VERSION=\\\"$$EXTENDED_VERSION\\\"
# keep comments, as /* fall through */
QMAKE_CXXFLAGS += -C
QMAKE_CXXFLAGS += -g
QMAKE_CXXFLAGS += -Wno-deprecated-copy -O
contains( CONFIG, PTU5 ) {
CONFIG += link_pkgconfig
QMAKE_CXXFLAGS += -O2 -std=c++17 # for GCC >= 4.7
# QMAKE_CXXFLAGS += -Wno-deprecated-copy
PTU5BASEPATH = /opt/devel/ptu5
INCLUDEPATH += $$PTU5BASEPATH/qt/libs/devicecontroller/include
LIBS += -L$$PTU5BASEPATH/qt/libs/devicecontroller/library
ARCH = PTU5
DEFINES+=PTU5
}
contains( CONFIG, PTU5_YOCTO ) {
QMAKE_CXXFLAGS += -std=c++17 # for GCC >= 4.7
# QMAKE_CXXFLAGS += -Wno-deprecated-copy
PTU5BASEPATH = /opt/devel/ptu5
ARCH = PTU5
DEFINES+=PTU5
}
contains( CONFIG, DesktopLinux ) {
# QMAKE_CC = ccache $$QMAKE_CC
# QMAKE_CXX = ccache $$QMAKE_CXX
QMAKE_CXXFLAGS += -std=c++17
# QMAKE_CXXFLAGS += -Wno-deprecated-copy
linux-clang { QMAKE_CXXFLAGS += -Qunused-arguments }
ARCH = DesktopLinux
DEFINES+=DesktopLinux
}
SOURCES += \
main.cpp \
message_handler.cpp \
../common/src/utils_internal.cpp \
../common/src/command.cpp \
sync_command.cpp
HEADERS += \
message_handler.h \
../common/include/utils_internal.h \
../common/include/command.h \
sync_command.h
##########################################################################################
# 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
}

94
Sync/main.cpp Normal file
View File

@@ -0,0 +1,94 @@
#include <QtGlobal>
#include <QCoreApplication>
#include <QByteArray>
#include <QProcess>
#include <QCommandLineParser>
#include <QStandardPaths>
#include <QSettings>
#include <QDir>
#include <QDebug>
#include "message_handler.h"
#include "utils_internal.h"
#include "sync_command.h"
int main(int argc, char **argv) {
QByteArray const value = qgetenv("LC_ALL");
if (value.isEmpty() || value != "C") {
qputenv("LC_ALL", "C");
}
openlog("ATB-UPDATE-SYNC", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER);
QCoreApplication a(argc, argv);
QCoreApplication::setApplicationName("ATBUpdateSync");
QCoreApplication::setApplicationVersion(APP_VERSION);
QDebug debug = qCritical();
if (!messageHandlerInstalled()) { // change internal qt-QDebug-handling
atbInstallMessageHandler(nullptr);
//atbInstallMessageHandler(atbDebugOutput);
setDebugLevel(LOG_NOTICE);
}
if (internal::customerRepoExists() == false) {
qCritical().noquote() << internal::NO_CUSTOMER_REPOSITORY;
return internal::NO_CUSTOMER_REPOSITORY_CODE;
}
QString const crd = internal::customerRepoDir();
QString const etcInRepo = QDir::cleanPath(crd + QDir::separator() + "etc/");
QString const optInRepo = QDir::cleanPath(crd + QDir::separator() + "opt/");
if (!QDir(etcInRepo).exists()) {
qCritical().noquote() << internal::NO_ETC_CUSTOMER_REPOSITORY;
return internal::NO_ETC_CUSTOMER_REPOSITORY_CODE;
}
if (!QDir(optInRepo).exists()) {
qCritical().noquote() << internal::NO_OPT_CUSTOMER_REPOSITORY;
return internal::NO_OPT_CUSTOMER_REPOSITORY_CODE;
}
#if 0
error codes for rsync:
https://stackoverflow.com/questions/20737204/comprehensive-list-of-rsync-error-codes
0 Success
1 Syntax or usage error
2 Protocol incompatibility
3 Errors selecting input/output files, dirs
4 Requested action not supported: an attempt was made to manipulate 64-bit
files on a platform that cannot support them; or an option was specified
that is supported by the client and not by the server.
5 Error starting client-server protocol
6 Daemon unable to append to log-file
10 Error in socket I/O
11 Error in file I/O
12 Error in rsync protocol data stream
13 Errors with program diagnostics
14 Error in IPC code
20 Received SIGUSR1 or SIGINT
21 Some error returned by waitpid()
22 Error allocating core memory buffers
23 Partial transfer due to error
24 Partial transfer due to vanished source files
25 The --max-delete limit stopped deletions
30 Timeout in data send/receive
35 Timeout waiting for daemon connection
#endif
QStringList options({"-v", "--recursive", "--progress", "--checksum",
"--exclude=.*", "--include=*.bin", "--include=*.json",
"--include=*.ini"});
int ret = SyncCommand().exec("rsync", options << etcInRepo << "/etc");
if (ret == 0) {
ret = SyncCommand().exec("rsync", options << optInRepo << "/opt");
}
return (ret > 0) ? -ret : ret;
}

97
Sync/message_handler.cpp Executable file
View File

@@ -0,0 +1,97 @@
#include "message_handler.h"
#include <QDateTime>
#include <cstring>
#include <QString>
#include <QFileInfo>
#include <QMessageLogContext>
static char const *DBG_NAME[] = { "DBG ", "WARN ", "CRIT ", "FATAL", "INFO " };
static bool installedMsgHandler = false;
static int debugLevel = LOG_NOTICE;
int getDebugLevel() { return debugLevel; }
void setDebugLevel(int newDebugLevel) {
debugLevel = newDebugLevel;
}
bool messageHandlerInstalled() {
return installedMsgHandler;
}
QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler) {
installedMsgHandler = (handler != 0);
static QtMessageHandler prevHandler = nullptr;
if (handler) {
prevHandler = qInstallMessageHandler(handler);
return prevHandler;
} else {
return qInstallMessageHandler(prevHandler);
}
}
///
/// \brief Print message according to given debug level.
///
/// \note Install this function using qInstallMsgHandler().
///
/// int main(int argc, char **argv) {
/// installMsgHandler(atbDebugOutput);
/// QApplication app(argc, argv);
/// ...
/// return app.exec();
/// }
///
#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) {
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 LOG_NOTICE: { // normal, but significant, condition
if (type != QtDebugMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
case LOG_WARNING: { // warning conditions
if (type != QtInfoMsg && type != QtDebugMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
case LOG_ERR: { // error conditions
if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
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());
}
}
}
#endif

23
Sync/message_handler.h Executable file
View File

@@ -0,0 +1,23 @@
#ifndef MESSAGE_HANDLER_H_INCLUDED
#define MESSAGE_HANDLER_H_INCLUDED
#include <QtGlobal>
#ifdef __linux__
#include <syslog.h>
#endif
int getDebugLevel();
void setDebugLevel(int newDebugLevel);
bool messageHandlerInstalled();
QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler);
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
// typedef void (*QtMessageHandler)(QtMsgType, const char *);
void atbDebugOutput(QtMsgType type, const char *msg);
#elif QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
// typedef void (*QtMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &);
void atbDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg);
#endif
#endif // MESSAGE_HANDLER_H_INCLUDED

21
Sync/sync_command.cpp Normal file
View File

@@ -0,0 +1,21 @@
#include "sync_command.h"
#include "command.h"
#include "utils_internal.h"
using namespace internal;
#include <QDir>
#include <QDebug>
#include <QFlags>
#include <QRegularExpression>
SyncCommand::SyncCommand() {
}
int SyncCommand::exec(QString const &cmd, QStringList const &options,
int start_timeout, int finish_timeout) {
Command c(cmd, options, internal::customerRepoDir(),
start_timeout, finish_timeout);
c.exec();
return c.exitCode();
}

14
Sync/sync_command.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef SYNC_COMMAND_H_INCLUDED
#define SYNC_COMMAND_H_INCLUDED
#include <QStringList>
class SyncCommand {
public:
SyncCommand();
int exec(QString const &cmd, QStringList const &options,
int start_timeout = 100000, int finish_timeout = 100000);
};
#endif // SYNC_COMMAND_H_INCLUDED

View File

@@ -0,0 +1,36 @@
{
"title":"DC2C_pri01",
"project":"DBM Szeged park_coins",
"issued":"13.04.2023 01.01",
"styl":"size 01",
"text":"PARKOLÓJEGY",
"styl":"size 11",
"feed":"nl",
"text":"Nyugta",
"styl":"size 00",
"feed":"nl",
"feed":"nl",
"text":"mögé jól láthatóan",
"feed":"nl",
"text":"elhelyezni!",
"feed":"nl",
"text":"A PARKOLÓ NEM ŐRZÖTT!",
"feed":"nl",
"styl":"size 10",
"text":"Rendszám: ",
"styl":"size 00",
"feed":"nl",
"vari":"Dynamic01",
"feed":"nl",
"text":"Parkolási idõ vége:",
"vari":"Dynamic02",
"vari":"Dynamic03",
"feed":"nl",
"text":"Parkolás helye:",
"vari":"location",
"text":"Összeg: ",
"vari":"Dynamic04",
"feed":"nl",
}

View File

@@ -0,0 +1,30 @@
{
"title":"DC2C_pri02",
"project":"DBM Szeged park_coins",
"issued":"13.04.2023 01.01",
"text":"Kiadás időpontja:",
"vari":"date us",
"vari":"time long",
"text":"Automata száma: ",
"vari":"manu",
"feed":"nl",
"text":"Park.jegy sorsz.: ",
"vari":"TickCtr",
"feed":"nl",
"text":"Szegedi Közlekedési Kft",
"feed":"nl",
"text":"6720 Szeged ",
"feed":"nl",
"text":"Zrínyi utca 4-8",
"feed":"nl",
"text":"Adósz: 11092612-2-06",
"feed":"nl",
"text":"A jegy ára az ÁFA-t",
"feed":"nl",
"text":" tartalmazza!",
"feed":"nl",
"text":"Üf. szolg nyitvatartás:",
"feed":"nl",
}

View File

@@ -0,0 +1,20 @@
{
"title":"DC2C_pri03",
"project":"DBM Szeged park_coins",
"issued":"13.04.2023 01.01",
"text":"H - P: 7:45 - 17:00",
"feed":"nl",
"text":"Tel: (80) 820-500",
"feed":"nl",
"text":"Parkoljon egy érintéssel. ",
"feed":"nl",
"text":"Használja bankkártyáját ",
"feed":"nl",
"text":" automatáinknál is!",
"feed":"nl",
"feed":"nl",
"feed":"nl",
"feed":"eject",
}

View File

@@ -0,0 +1,267 @@
QT += core gui
QT += widgets serialport network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = ATBUpdateTool
# 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.
# 1.3.13: Fix: if the customer repository is corrupted, remove it and re-clone
# the repository (without checking the ISMAS-trigger (WAIT-)button.
# 1.3.14: Add additional check for sanity of customer repository using
# "git fsck".
# Stream-lined code of update process: massive refactoring.
# Added functionality: If WAIT button is not active, then an existing
# customer repository will be repaired, or a not existing repository
# will be cloned. The process stops then.
# However, if the WAIT button is active, the at least the commands in
# opkg_commands will be executed. Changed files in the customer
# repository will be worked on: tariff-files will be synced with the
# local filesystem, json-files will be downloaded to firmware.
# The device-controller firmware will be handled in a later version.
# 1.3.15: Bug fixes found during testing.
# Do not disable Exit-button during update-process.
# Removed worker-thread with an own event-loop: only the GUI thread
# has an event loop. Tested JSON-downloads several times successfully
# (using the slave lib where the CA helper tool was active as master).
# Turned previous worker-object into its own thread, but without any
# own event-loop (so it cannot block anything inside the CA-plugin).
# 1.3.16: Bug fixes found during testing.
# 1.3.17: Add ATBUpdateTool.ini and custom command line parser. Settings
# given in ATBUpdateTool.ini can be overwritten on the command-line.
# 1.3.18: Bug fixes found during testing.
# 1.3.19: Bug fixes found during testing.
# 1.3.20: Bug fixes found during testing.
# 1.3.21: Bug fixes found during testing:
# Fix directory of ATBUpdateTool.ini to be the working directory of
# the application rather than just ".".
# Check existance of etc-directory inside customer repository.
# Check for valid ISMAS trigger (button) 15x (=90s).
# NOTE: if the customer repository is cloned (or repaired and cloned
# again), and if the settings always-download-config=true and
# always-download-dc=true in the ATBUpdateTool.ini file, the download
# the printer-json files and the device controller file, even without
# an activated ISMAS trigger (button). The tariff-files are rsynced to
# the local filesystem for such clone.
# Set new filename for device controller: dc2c.bin.
# 1.3.22: Bug fixes found during testing:
# Fix the path-names of the json-files and the device-controller.
# Set automatic download of json-file in ATBUpdateTool.ini file for
# a fresh clone of the repository.
# 1.3.23: Added a 'break' to prevent a possible endless loop when checking if
# the device is alive.
#
# NOTE: The versioning info has to be shifted up by one version, i.e. what
# happened for 1.3.23 was actually done in 1.3.24.
# 1.3.24
#
# 1.3._24_: Special version for szeged using a old dc-controller (4.42):
# Changes:
# (1) the ini-File now uses the libCAmaster.so.
# 1.3._25_: Again special version for szeged, using interface.h, version 4.4.
# 1.4.0 : Start with version at 1.4.0 (mainly to see a difference with Szeged)
# Set hash-value in EVENT-objects. Set location (project), version
# and info in send-last-version.
# If the customer repository does not exist, then do not check the
# ISMAS trigger, but proceed with the update procedure. Otherwise,
# check the ISMAS update-trigger as first step.
# If the current time is between 0.00 - 4.00 o'clock, then a wrong
# trigger-value will result in an UPDATE_STEP_NOT_NECESSARY.
# Move final processing to subclass UpdateProcessRunning.
# Disable EXIT-button for the whole update-process, except for the
# checking of the ISMAS-trigger-button (aka WAIT-button).
# 1.4.1 : Sync files in the customer repository (under ./etc) as the very
# first step
VERSION="1.4.1"
# PLANNED TODOS:
# 1: Das Repository wird repariert bwz. neu geklont. Unabhaengig vom WAIT.
# 2: Wenn der WAIT-Button aktiv ist, dann wird ein Repository repariert (neu
# geklont), aber zusaetzlich werden alle verfuegbaren Dateien als neu
# angesehen und die entsprechenden Aktionen durchgefuehrt: tariff-files
# spiegeln, json-files laden und dc laden. Also VORSICHT: das repository
# muss in diesem fall wirklich in ordnung sein.
# 3: Wurde keine Datei geaendert, kein initiales Clone und der WAIT-button
# nicht aktiv, so (passiert natuerlich nichts) kann man davon ausgehen,
# dass es sich um ein automatisches Update handelt. Dann koennte man im
# ISMAS eine entsprechende Meldung anzeigen als Teil von SEND-LAST-VERSION.
# Wenn der WAIT-button aktiv ist, dann werden zumindest die opkg-commands
# ausgefuehrt.
# 5: Falls das Tool mal abstuerzt, dann einen Signal-Handler (fuer TERM)
# installieren, sodass zumnidest SEND-LAST-VERSION mit rausgeht.
# 6: rsync: explizites Binary, nicht das in busybox enthaltene.
# 7: Versionen der Json-Files lassen sich auslesen.
# Problem: Einstellungen in den Json-Files lassen sich auch mittels
# Funktionen in der CD-Library ueberschreiben. Damit ist dann wieder nicht
# mehr so klar, was jetzt eigentlich aktiv ist.
# 8: m_alwaysDownloadConfig und m_alwaysDownloadDC: vorbereitet: man koennte
# es so arrangieren, dass der DC plus die Json-files im Repository immer
# runtergeladen werden, obwohl sich im Repository gar nicts veraendert
# hat. Eeventuell nuetzlich beim initialen Setuo eines PSA.
# 9: Das Kunden-Repository sollte immer gezogen werden, unabhaengig von der
# Stellung des WAIT-Button. Grund: es koennte sein, dass andernfalls ein
# PSA weit hiter anderen steht, und dann ploetzlich einmal alle vorher-
# gehenden Aenderungen anzieht, die gar nicht fuer ihn gemeint waren.
# 11: Das Edit-Fenster teilen um die Anzeige zu verbessern.
# 12: Bei einem Update muss immer ersichtlich sein, warum es ueberhaupt
# angestossen wurde. Steht kein "WAIT" im ISMAS-Trigger, dann kann man
# davon ausgehen, dass es sich um ein automatisches Update handelt.
# In jedem Fall wird bei einem automatischen Update, bei dem der WAIT-
# Button nicht gesetzt war, ein "OK" gesendet, falls sonst nichts weiter
# zu tun ist. Beachte aber: wir haben auch noch den Fall, dass eine SD-
# Karte gesteckt wird. In diesem Fall wird ein komplettes Update gefahren,
# und zwar explizit auch ohne WAIT-Button.
# Am Ende eines Updates steht im ISMAS entweder ein "OK" oder ein "FAIL".
# 13: SendLastVersion: fuer jedes erfolgreich installierte Paket eine
# Send-Last-Version-Nachricht an ISMAS. Dadurch entsteht im ISMAS eine
# History (Christian darueber informieren).
# 14: Installiert werden nur Dateien, die neu sind oder geaendert wurden.
# Nicht etwas Dateien, die geloescht wurden: sicherstellen, dass man hier
# immer direkt im repository arbeitet, nicht auf dem Filesystem.
# Ferner: der DeviceController heisst dc2c.bin, auch fuer die Jsons
# sind Dtandard-Namen vergeben. Alternativ: alle vorhandenen Jsons
# werden runtergeladen: Thomas ist eh fuer deren Inhalte verantworlich.
# WICHTIG: immer ueberpruefen, ob die Dateien im Customer-Repository
# wirklich die richtigen Dateien sind.
# 15: Der WAIT-Button laesst sich auf WAIT zuruecksetzen (etwa wenn git
# selber Probleme hatte).
# 16: Der Download-Thread sollte sowohl die auto-Variable auf false setzen
# als auch den cycle-Timer stoppen, damit sichergestellt ist, dass der
# Download des DC nicht gestoert wird.
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")
EXTENDED_VERSION="$${VERSION}-$${GIT_COMMIT}"
INCLUDEPATH += plugins
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\\\"
# keep comments, as /* fall through */
QMAKE_CXXFLAGS += -C
QMAKE_CXXFLAGS += -g
QMAKE_CXXFLAGS += -Wno-deprecated-copy -O
contains( CONFIG, PTU5 ) {
greaterThan(QT_MAJOR_VERSION, 4): QT += serialport
CONFIG += link_pkgconfig
lessThan(QT_MAJOR_VERSION, 5): PKGCONFIG += qextserialport
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
# add qmqtt lib
#LIBS += -lQt5Qmqtt
}
contains( CONFIG, DesktopLinux ) {
greaterThan(QT_MAJOR_VERSION, 4): QT += serialport
lessThan(QT_MAJOR_VERSION, 5): CONFIG += extserialport
# QMAKE_CC = ccache $$QMAKE_CC
# QMAKE_CXX = ccache $$QMAKE_CXX
QMAKE_CXXFLAGS += -std=c++17
# QMAKE_CXXFLAGS += -Wno-deprecated-copy
linux-clang { QMAKE_CXXFLAGS += -Qunused-arguments }
ARCH = DesktopLinux
DEFINES+=DesktopLinux
}
SOURCES += \
main.cpp \
progress_event.cpp \
update_dc_event.cpp \
mainwindow.cpp \
utils.cpp \
update.cpp \
git/git_client.cpp \
ismas/ismas_client.cpp \
process/command.cpp \
message_handler.cpp \
worker.cpp \
commandline_parser.cpp
HEADERS += \
update.h \
progress_event.h \
update_dc_event.h \
utils.h \
mainwindow.h \
git/git_client.h \
apism/ismas_data.h \
ismas/ismas_client.h \
process/command.h \
message_handler.h \
worker.h \
interfaces.h \
commandline_parser.h \
plugins/interfaces.h
OTHER_FILES += \
ATBUpdateTool.ini
FORMS += \
mainwindow.ui
##########################################################################################
# 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
}

View File

@@ -0,0 +1,349 @@
QT += core gui
QT += widgets serialport network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = ATBUpdateTool
# 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.
# 1.3.13: Fix: if the customer repository is corrupted, remove it and re-clone
# the repository (without checking the ISMAS-trigger (WAIT-)button.
# 1.3.14: Add additional check for sanity of customer repository using
# "git fsck".
# Stream-lined code of update process: massive refactoring.
# Added functionality: If WAIT button is not active, then an existing
# customer repository will be repaired, or a not existing repository
# will be cloned. The process stops then.
# However, if the WAIT button is active, the at least the commands in
# opkg_commands will be executed. Changed files in the customer
# repository will be worked on: tariff-files will be synced with the
# local filesystem, json-files will be downloaded to firmware.
# The device-controller firmware will be handled in a later version.
# 1.3.15: Bug fixes found during testing.
# Do not disable Exit-button during update-process.
# Removed worker-thread with an own event-loop: only the GUI thread
# has an event loop. Tested JSON-downloads several times successfully
# (using the slave lib where the CA helper tool was active as master).
# Turned previous worker-object into its own thread, but without any
# own event-loop (so it cannot block anything inside the CA-plugin).
# 1.3.16: Bug fixes found during testing.
# 1.3.17: Add ATBUpdateTool.ini and custom command line parser. Settings
# given in ATBUpdateTool.ini can be overwritten on the command-line.
# 1.3.18: Bug fixes found during testing.
# 1.3.19: Bug fixes found during testing.
# 1.3.20: Bug fixes found during testing.
# 1.3.21: Bug fixes found during testing:
# Fix directory of ATBUpdateTool.ini to be the working directory of
# the application rather than just ".".
# Check existance of etc-directory inside customer repository.
# Check for valid ISMAS trigger (button) 15x (=90s).
# NOTE: if the customer repository is cloned (or repaired and cloned
# again), and if the settings always-download-config=true and
# always-download-dc=true in the ATBUpdateTool.ini file, the download
# the printer-json files and the device controller file, even without
# an activated ISMAS trigger (button). The tariff-files are rsynced to
# the local filesystem for such clone.
# Set new filename for device controller: dc2c.bin.
# 1.3.22: Bug fixes found during testing:
# Fix the path-names of the json-files and the device-controller.
# Set automatic download of json-file in ATBUpdateTool.ini file for
# a fresh clone of the repository.
# 1.3.23: Added a 'break' to prevent a possible endless loop when checking if
# the device is alive.
#
# NOTE: The versioning info has to be shifted up by one version, i.e. what
# happened for 1.3.23 was actually done in 1.3.24.
# 1.3.24
#
# 1.3._24_: Special version for szeged using a old dc-controller (4.42):
# Changes:
# (1) the ini-File now uses the libCAmaster.so.
# 1.3._25_: Again special version for szeged, using interface.h, version 4.4.
# 1.4.0 : Start with version at 1.4.0 (mainly to see a difference with Szeged)
# Set hash-value in EVENT-objects. Set location (project), version
# and info in send-last-version.
# If the customer repository does not exist, then do not check the
# ISMAS trigger, but proceed with the update procedure. Otherwise,
# check the ISMAS update-trigger as first step.
# If the current time is between 0.00 - 4.00 o'clock, then a wrong
# trigger-value will result in an UPDATE_STEP_NOT_NECESSARY.
# Move final processing to subclass UpdateProcessRunning.
# Disable EXIT-button for the whole update-process, except for the
# checking of the ISMAS-trigger-button (aka WAIT-button).
# 1.4.1 : Sync files in the customer repository (under ./etc) as the very
# first step
# 1.4.2 : Do not check if <repo_dir>/etc/psa_tariff and /etc/psa_tariff are
# the same after an rsync. They might be noy after a change of the
# customer-number.
# 1.4.3 : Use global directory for device-controller interfaces.h-file.
# 1.4.4 : Add additional debug messages when downloading json-files.
# Move rsyncing of the customer-repository after the actual fetching
# of the repository. Otherwise, the update of, for instance
# tariff-files, would always be a step behind.
# 1.4.5 : In case a new branch has been created in a remote
# customer-repository (e.g. origin/zg1/zone101), then fetch/pull
# this branch before switching to this now locally existen branch.
# : Improve output of GUI/Console and messages sent to ISMAS.
# : Fix: do not send the json-files to dc in any case. Bug introduced
# when pulling new branch, but branch already existed locally.
# 1.4.6 : No exit()-call if loading CA-plugin fails.
# Check m_hw (pointer to CA-(Slave)Plugin) before its use.
# Only exit() if firmware is configured to be possibly updated, but
# loading the CA-plugin failed.
# 1.4.7 : Read Json-file versions of Json-files actually loaded into DC.
# 1.4.8 : Improve reading Json-file versions of Json-files actually loaded
# in DC. If available, show version info instead of blob-info of
# Json file.
# 1.4.9 : Improve ISMAS output: add messages for JSON downloads.
# Do not send CHECK_ISMAS_TRIGGER_SUCCESS to ISMAS for an automatic
# update.
# In case there is an automatic update (00:00-04:00) use M0100 instead
# of U0002.
# 1.4.10 : Fix: for the time being, never set the autoRequest-flag to false.
# 1.4.11 : Fix: remove hard-coded path to git-repos of gitea in mimbach. Read
# from ATBUpdateTool.ini the url for the gitrepositories.
# 1.5.0 : Use ptu-config.atb-comm.de (given in ini-file) as new server for
# customer repositories.
# Fix: allow empty lines in opkg_commands.
# 1.5.1 : Fix: do not use cleanPath() on a url-address.
# 1.5.2 : Remove .ipk and .gz files in /var/cache/opkg.
# 1.5.3 : Build customer_(id) name without right justification.
# 1.5.4 : Try to run opkg-commands even under some error conditions (failure
# of customerEnvironment(), filesToUpdate() or
# syncCustomerRepositoryAndFS().
# 1.5.5 : Call into binary ptuPackageVersion to get installed package
# versions.
# 1.5.6 : Show additional update progress info in status bar.
# 2.0.0 : Rewrite of UpdateTool.
VERSION="2.0.0"
# PLANNED TODOS:
# 1: Das Repository wird repariert bwz. neu geklont. Unabhaengig vom WAIT.
# 2: Wenn der WAIT-Button aktiv ist, dann wird ein Repository repariert (neu
# geklont), aber zusaetzlich werden alle verfuegbaren Dateien als neu
# angesehen und die entsprechenden Aktionen durchgefuehrt: tariff-files
# spiegeln, json-files laden und dc laden. Also VORSICHT: das repository
# muss in diesem fall wirklich in ordnung sein.
# 3: Wurde keine Datei geaendert, kein initiales Clone und der WAIT-button
# nicht aktiv, so (passiert natuerlich nichts) kann man davon ausgehen,
# dass es sich um ein automatisches Update handelt. Dann koennte man im
# ISMAS eine entsprechende Meldung anzeigen als Teil von SEND-LAST-VERSION.
# Wenn der WAIT-button aktiv ist, dann werden zumindest die opkg-commands
# ausgefuehrt.
# 5: Falls das Tool mal abstuerzt, dann einen Signal-Handler (fuer TERM)
# installieren, sodass zumnidest SEND-LAST-VERSION mit rausgeht.
# 6: rsync: explizites Binary, nicht das in busybox enthaltene.
# 7: Versionen der Json-Files lassen sich auslesen.
# Problem: Einstellungen in den Json-Files lassen sich auch mittels
# Funktionen in der CD-Library ueberschreiben. Damit ist dann wieder nicht
# mehr so klar, was jetzt eigentlich aktiv ist.
# 8: m_alwaysDownloadConfig und m_alwaysDownloadDC: vorbereitet: man koennte
# es so arrangieren, dass der DC plus die Json-files im Repository immer
# runtergeladen werden, obwohl sich im Repository gar nicts veraendert
# hat. Eeventuell nuetzlich beim initialen Setuo eines PSA.
# 9: Das Kunden-Repository sollte immer gezogen werden, unabhaengig von der
# Stellung des WAIT-Button. Grund: es koennte sein, dass andernfalls ein
# PSA weit hiter anderen steht, und dann ploetzlich einmal alle vorher-
# gehenden Aenderungen anzieht, die gar nicht fuer ihn gemeint waren.
# 11: Das Edit-Fenster teilen um die Anzeige zu verbessern.
# 12: Bei einem Update muss immer ersichtlich sein, warum es ueberhaupt
# angestossen wurde. Steht kein "WAIT" im ISMAS-Trigger, dann kann man
# davon ausgehen, dass es sich um ein automatisches Update handelt.
# In jedem Fall wird bei einem automatischen Update, bei dem der WAIT-
# Button nicht gesetzt war, ein "OK" gesendet, falls sonst nichts weiter
# zu tun ist. Beachte aber: wir haben auch noch den Fall, dass eine SD-
# Karte gesteckt wird. In diesem Fall wird ein komplettes Update gefahren,
# und zwar explizit auch ohne WAIT-Button.
# Am Ende eines Updates steht im ISMAS entweder ein "OK" oder ein "FAIL".
# 13: SendLastVersion: fuer jedes erfolgreich installierte Paket eine
# Send-Last-Version-Nachricht an ISMAS. Dadurch entsteht im ISMAS eine
# History (Christian darueber informieren).
# 14: Installiert werden nur Dateien, die neu sind oder geaendert wurden.
# Nicht etwas Dateien, die geloescht wurden: sicherstellen, dass man hier
# immer direkt im repository arbeitet, nicht auf dem Filesystem.
# Ferner: der DeviceController heisst dc2c.bin, auch fuer die Jsons
# sind Dtandard-Namen vergeben. Alternativ: alle vorhandenen Jsons
# werden runtergeladen: Thomas ist eh fuer deren Inhalte verantworlich.
# WICHTIG: immer ueberpruefen, ob die Dateien im Customer-Repository
# wirklich die richtigen Dateien sind.
# 15: Der WAIT-Button laesst sich auf WAIT zuruecksetzen (etwa wenn git
# selber Probleme hatte).
# 16: Der Download-Thread sollte sowohl die auto-Variable auf false setzen
# als auch den cycle-Timer stoppen, damit sichergestellt ist, dass der
# Download des DC nicht gestoert wird.
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")
EXTENDED_VERSION="$${VERSION}-$${GIT_COMMIT}"
# INCLUDEPATH += plugins
INCLUDEPATH += plugins \
$${_PRO_FILE_PWD_}/../common/ \
$${_PRO_FILE_PWD_}/../common/include
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\\\"
# keep comments, as /* fall through */
QMAKE_CXXFLAGS += -C
QMAKE_CXXFLAGS += -g
QMAKE_CXXFLAGS += -Wno-deprecated-copy -O
contains( CONFIG, PTU5 ) {
greaterThan(QT_MAJOR_VERSION, 4): QT += serialport
CONFIG += link_pkgconfig
lessThan(QT_MAJOR_VERSION, 5): PKGCONFIG += qextserialport
QMAKE_CXXFLAGS += -O2 -std=c++17 # for GCC >= 4.7
PTU5BASEPATH = /opt/devel/ptu5
INCLUDEPATH += $$PTU5BASEPATH/qt/libs/devicecontroller/include
LIBS += -L$$PTU5BASEPATH/qt/libs/devicecontroller/library
# 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
LIBS += -lCAslave
LIBS += -lCAmaster
}
contains( CONFIG, DesktopLinux ) {
greaterThan(QT_MAJOR_VERSION, 4): QT += serialport
lessThan(QT_MAJOR_VERSION, 5): CONFIG += extserialport
# QMAKE_CC = ccache $$QMAKE_CC
# QMAKE_CXX = ccache $$QMAKE_CXX
QMAKE_CXXFLAGS += -std=c++17
# QMAKE_CXXFLAGS += -Wno-deprecated-copy
linux-clang { QMAKE_CXXFLAGS += -Qunused-arguments }
ARCH = DesktopLinux
DEFINES+=DesktopLinux
}
SOURCES += \
main.cpp \
progress_event.cpp \
update_dc_event.cpp \
mainwindow.cpp \
utils.cpp \
update.cpp \
git/git_client.cpp \
ismas/ismas_client.cpp \
process/command.cpp \
process/update_command.cpp \
process/update_json_command.cpp \
process/update_filesystem_command.cpp \
process/update_dc_command.cpp \
process/check_ismas_connectivity_command.cpp \
process/check_update_activation_command.cpp \
process/check_and_fetch_customer_repository_command.cpp \
process/exec_opkg_command.cpp \
process/show_software_status_command.cpp \
message_handler.cpp \
worker.cpp \
work_process_list.cpp \
$${_PRO_FILE_PWD_}/../common/src/utils_internal.cpp \
$${_PRO_FILE_PWD_}/../common/src/commandline_parser.cpp \
$${_PRO_FILE_PWD_}/../common/ismas/ApismClient.cpp \
$${_PRO_FILE_PWD_}/../common/ismas/ApismTcpClient.cpp \
$${_PRO_FILE_PWD_}/../common/ismas/ApismClientForUpdate.cpp
HEADERS += \
update.h \
progress_event.h \
update_dc_event.h \
utils.h \
mainwindow.h \
git/git_client.h \
apism/ismas_data.h \
ismas/ismas_client.h \
process/command.h \
process/update_command.h \
process/update_json_command.h \
process/update_filesystem_command.h \
process/update_dc_command.h \
process/check_ismas_connectivity_command.h \
process/check_update_activation_command.h \
process/check_and_fetch_customer_repository_command.h \
process/exec_opkg_command.h \
process/show_software_status_command.h \
message_handler.h \
worker.h \
work_process_list.h \
$${_PRO_FILE_PWD_}/../common/include/utils_internal.h \
$${_PRO_FILE_PWD_}/../common/include/commandline_parser.h \
$${_PRO_FILE_PWD_}/../common/include/log_line_entry.h \
$${_PRO_FILE_PWD_}/../common/ismas/ApismClient.h \
$${_PRO_FILE_PWD_}/../common/ismas/ApismTcpClient.h \
$${_PRO_FILE_PWD_}/../common/ismas/ISMASData.h \
$${_PRO_FILE_PWD_}/../common/ismas/ApismClientForUpdate.h
OTHER_FILES += \
ATBUpdateTool.ini
FORMS += \
mainwindow.ui
##########################################################################################
# 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
}

View File

@@ -0,0 +1,477 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 7.0.1, 2023-04-05T10:01:18. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{f1e3048b-d6d5-4c03-ba3e-d60a08550dca}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="qlonglong">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="qlonglong" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
<value type="bool" key="AutoTest.Framework.Boost">true</value>
<value type="bool" key="AutoTest.Framework.CTest">false</value>
<value type="bool" key="AutoTest.Framework.Catch">true</value>
<value type="bool" key="AutoTest.Framework.GTest">true</value>
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
</valuemap>
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
<value type="int" key="AutoTest.RunAfterBuild">0</value>
<value type="bool" key="AutoTest.UseGlobal">true</value>
<valuemap type="QVariantMap" key="ClangTools">
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
<value type="int" key="ClangTools.ParallelJobs">3</value>
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="DeviceType">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop Qt 5.12.5 GCC 64bit</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop Qt 5.12.5 GCC 64bit</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">qt.qt5.5125.gcc_64_kit</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="int" key="EnableQmlDebugging">0</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/opt/ptu5/opt/build-UpdatePTUDevCtrl-Desktop_Qt_5_12_5_GCC_64bit-Debug</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/opt/ptu5/opt/build-UpdatePTUDevCtrl-Desktop_Qt_5_12_5_GCC_64bit-Debug</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<valuelist type="QVariantList" key="QtProjectManager.QMakeBuildStep.SelectedAbis"/>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Debug</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">2</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/opt/ptu5/opt/build-UpdatePTUDevCtrl-Desktop_Qt_5_12_5_GCC_64bit-Release</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/opt/ptu5/opt/build-UpdatePTUDevCtrl-Desktop_Qt_5_12_5_GCC_64bit-Release</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<valuelist type="QVariantList" key="QtProjectManager.QMakeBuildStep.SelectedAbis"/>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Release</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
<value type="int" key="QtQuickCompiler">0</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.2">
<value type="int" key="EnableQmlDebugging">0</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/opt/ptu5/opt/build-UpdatePTUDevCtrl-Desktop_Qt_5_12_5_GCC_64bit-Profile</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/opt/ptu5/opt/build-UpdatePTUDevCtrl-Desktop_Qt_5_12_5_GCC_64bit-Profile</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<valuelist type="QVariantList" key="QtProjectManager.QMakeBuildStep.SelectedAbis"/>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Profile</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
<value type="int" key="QtQuickCompiler">0</value>
<value type="int" key="SeparateDebugInfo">0</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">3</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">/usr/bin/valgrind</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:/opt/ptu5/opt/UpdatePTUDevCtrl/UpdatePTUDevCtrl.pro</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/opt/ptu5/opt/UpdatePTUDevCtrl/UpdatePTUDevCtrl.pro</value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/opt/ptu5/opt/build-UpdatePTUDevCtrl-Desktop_Qt_5_12_5_GCC_64bit-Debug</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.1</variable>
<valuemap type="QVariantMap">
<value type="QString" key="DeviceType">GenericLinuxOsType</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">i.MX6-ATB-PTU5</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">i.MX6-ATB-PTU5</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{cce48fb5-7479-4381-9a7f-7b1adfbd1ea1}</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="int" key="EnableQmlDebugging">0</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/opt/ptu5/opt/build-UpdatePTUDevCtrl-i_MX6_ATB_PTU5-Debug</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/opt/ptu5/opt/build-UpdatePTUDevCtrl-i_MX6_ATB_PTU5-Debug</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<valuelist type="QVariantList" key="QtProjectManager.QMakeBuildStep.SelectedAbis"/>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Debug</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">2</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/opt/ptu5/opt/build-UpdatePTUDevCtrl-i_MX6_ATB_PTU5-Release</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/opt/ptu5/opt/build-UpdatePTUDevCtrl-i_MX6_ATB_PTU5-Release</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<valuelist type="QVariantList" key="QtProjectManager.QMakeBuildStep.SelectedAbis"/>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Release</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
<value type="int" key="QtQuickCompiler">0</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.2">
<value type="int" key="EnableQmlDebugging">0</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/opt/ptu5/opt/build-UpdatePTUDevCtrl-i_MX6_ATB_PTU5-Profile</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/opt/ptu5/opt/build-UpdatePTUDevCtrl-i_MX6_ATB_PTU5-Profile</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<valuelist type="QVariantList" key="QtProjectManager.QMakeBuildStep.SelectedAbis"/>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Profile</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
<value type="int" key="QtQuickCompiler">0</value>
<value type="int" key="SeparateDebugInfo">0</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">3</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">RemoteLinux.CheckForFreeDiskSpaceStep</value>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedFiles"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedHosts"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedRemotePaths"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedSysroots"/>
<value type="QString" key="RemoteLinux.CheckForFreeDiskSpaceStep.PathToCheck">/</value>
<value type="qlonglong" key="RemoteLinux.CheckForFreeDiskSpaceStep.RequiredSpace">5242880</value>
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedLocalTimes"/>
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedRemoteTimes"/>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">RemoteLinux.KillAppStep</value>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedFiles"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedHosts"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedRemotePaths"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedSysroots"/>
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedLocalTimes"/>
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedRemoteTimes"/>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.2">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">RemoteLinux.RsyncDeployStep</value>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedFiles"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedHosts"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedRemotePaths"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedSysroots"/>
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedLocalTimes"/>
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedRemoteTimes"/>
<value type="QString" key="RemoteLinux.RsyncDeployStep.Flags">-av</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">3</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">DeployToGenericLinux</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">/usr/bin/valgrind</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.1">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">/usr/bin/valgrind</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">1</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">RemoteLinux.CustomRunConfig</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
<value type="int" key="RemoteLinux.EnvironmentAspect.Version">1</value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.X11Forwarding">:0</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">2</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="qlonglong">2</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>

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

@@ -0,0 +1,137 @@
#ifndef ISMASDATA_H
#define ISMASDATA_H
#include <QJsonObject>
#include <QJsonArray>
namespace ISMAS {
struct TransferData : public QJsonObject {
struct : public QJsonObject {
QJsonValue tariffId;
QJsonValue group;
QJsonValue zone;
} device;
struct : public QJsonObject {
QJsonValue state;
QJsonValue uid;
QJsonValue seq_tick_number;
QJsonValue timestamp;
QJsonValue userText;
QJsonValue userTextType;
} transaction;
struct : public QJsonObject {
// TODO: check what is really used at the moment
QJsonValue id; // unique article id
QJsonValue name; // name
QJsonValue price; // price in cent
QJsonValue currency; //
QJsonValue startTime; // start time
QJsonValue endTime; // end time
QJsonValue userText; // additional info
QJsonValue parkingTime;
QJsonValue printText;
// QJsonValue discount;
} item;
struct : public QJsonObject {
struct : public QJsonObject {
QJsonValue coins; // total amount of coins value
// QJsonValue notes; // total amount of notes value
QJsonValue overpaid; // in cent
QJsonValue currency;
QJsonValue change;
} cash;
struct : public QJsonObject {
QJsonValue cardNumber;
QJsonValue value; // buchungsbetrag
QJsonValue cardType;
QJsonValue currency;
QJsonValue tid;
QJsonValue tresult;
} card;
struct : public QJsonObject {
QJsonValue cardNumber;
QJsonValue cardType;
QJsonValue value;
QJsonValue valueOld;
QJsonValue valueNew;
QJsonValue time;
QJsonValue timeOld;
QJsonValue timeNew;
} prePaidCard;
} payment;
struct : public QJsonObject {
QJsonValue delivery; // PRINT, OnlineTicket
QJsonValue result; // SUCCESS, ERROR
QJsonValue errorCode; // 0=OK, 1=...
QJsonValue errorMsg;
} result;
};
struct AccountData : public QJsonObject {
struct : public QJsonObject {
QJsonValue UID;
QJsonValue ChangeNumber;
QJsonValue Process; // Vorgang
QJsonValue startDateTime;
QJsonValue endDateTime;
QJsonValue startHash;
QJsonValue endHash;
struct : public QJsonObject {
QJsonValue value; // coin value
QJsonValue numberOfCoins; // number of coins
QJsonValue currency;
} coin;
} coinBox; // Münzkasse
};
struct EventData : public QJsonObject {
struct : public QJsonObject {
QJsonValue eventID;
QJsonValue deviceName;
QJsonValue reason;
QJsonValue event;
QJsonValue eventState;
QJsonValue timeStamp;
QJsonValue parameter;
QJsonValue secondLevelInfo;
} machineEvent; //
};
struct StateData : public QJsonObject {
QJsonValue Timestamp;
QJsonArray HW_States;
struct : public QJsonObject {
QJsonValue name;
QJsonValue value;
QJsonValue unit;
} machineState; //
};
enum class REQUEST : quint8 {
NO_REQUEST,
START,
STOP,
PING,
SELF,
ISMAS_PARAMETER
};
}
#endif // ISMASDATA_H

BIN
UpdatePTUDevCtrl/dc2c4.bin Executable file

Binary file not shown.

View File

@@ -0,0 +1,230 @@
<mxfile host="www.draw.io" modified="2023-11-07T10:37:35.350Z" agent="Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/118.0" etag="ldmgaDoAxcOIqmOptaKH" version="22.0.8" type="device">
<diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">
<mxGraphModel dx="1042" dy="633" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="WIyWlLk6GJQsqaUBKTNV-0" />
<mxCell id="WIyWlLk6GJQsqaUBKTNV-1" parent="WIyWlLk6GJQsqaUBKTNV-0" />
<mxCell id="WIyWlLk6GJQsqaUBKTNV-2" value="" style="rounded=0;html=1;jettySize=auto;orthogonalLoop=1;fontSize=11;endArrow=block;endFill=0;endSize=8;strokeWidth=1;shadow=0;labelBackgroundColor=none;edgeStyle=orthogonalEdgeStyle;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="WIyWlLk6GJQsqaUBKTNV-3" target="WIyWlLk6GJQsqaUBKTNV-6" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="WIyWlLk6GJQsqaUBKTNV-3" value="START" style="rounded=1;whiteSpace=wrap;html=1;fontSize=12;glass=0;strokeWidth=1;shadow=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="350" y="10" width="120" height="40" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-82" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="WIyWlLk6GJQsqaUBKTNV-6">
<mxGeometry relative="1" as="geometry">
<mxPoint x="220" y="180" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="WIyWlLk6GJQsqaUBKTNV-6" value="&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Existent customer repository&lt;/div&gt;&lt;div&gt;?&lt;/div&gt;" style="rhombus;whiteSpace=wrap;html=1;shadow=0;fontFamily=Helvetica;fontSize=12;align=center;strokeWidth=1;spacing=6;spacingTop=-4;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="170" y="60" width="100" height="90" as="geometry" />
</mxCell>
<object label="" id="KwNStDcgQ-FNAQaH3l0E-18">
<mxCell style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="WIyWlLk6GJQsqaUBKTNV-7" target="KwNStDcgQ-FNAQaH3l0E-9">
<mxGeometry relative="1" as="geometry" />
</mxCell>
</object>
<mxCell id="WIyWlLk6GJQsqaUBKTNV-7" value="Clone customer repository" style="rounded=1;whiteSpace=wrap;html=1;fontSize=12;glass=0;strokeWidth=1;shadow=0;points=[[0,0,0,0,0],[0,0.25,0,0,0],[0,0.5,0,0,0],[0,0.75,0,0,0],[0,1,0,0,0],[0.25,0,0,0,0],[0.25,1,0,0,0],[0.5,0,0,0,0],[0.5,1,0,0,0],[0.75,0,0,0,0],[0.75,1,0,0,0],[1,0,0,0,0],[1,0.25,0,0,0],[1,0.5,0,0,0],[1,0.75,0,0,0],[1,1,0,0,0]];" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="310" y="130" width="120" height="40" as="geometry" />
</mxCell>
<mxCell id="WIyWlLk6GJQsqaUBKTNV-10" value="&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Repository corrupted&lt;/div&gt;&lt;div&gt;?&lt;/div&gt;" style="rhombus;whiteSpace=wrap;html=1;shadow=0;fontFamily=Helvetica;fontSize=12;align=center;strokeWidth=1;spacing=6;spacingTop=-4;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="170" y="180" width="100" height="80" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-19" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.15;entryY=0.55;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="KwNStDcgQ-FNAQaH3l0E-9" target="KwNStDcgQ-FNAQaH3l0E-110">
<mxGeometry relative="1" as="geometry">
<mxPoint x="600" y="150" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-9" value="&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Repository corrupted&lt;/div&gt;&lt;div&gt;?&lt;/div&gt;" style="rhombus;whiteSpace=wrap;html=1;shadow=0;fontFamily=Helvetica;fontSize=12;align=center;strokeWidth=1;spacing=6;spacingTop=-4;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="480" y="150" width="90" height="80" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-11" value="FAIL" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.terminator;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="585" y="10" width="100" height="40" as="geometry" />
</mxCell>
<UserObject label="Yes" placeholders="1" name="Variable" id="KwNStDcgQ-FNAQaH3l0E-20">
<mxCell style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;overflow=hidden;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="560" y="160" width="80" height="20" as="geometry" />
</mxCell>
</UserObject>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-23" value="&lt;div&gt;No&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="430" y="280" width="60" height="20" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-25" value="Delete corrupted repository" style="rounded=1;whiteSpace=wrap;html=1;fontSize=12;glass=0;strokeWidth=1;shadow=0;points=[[0,0,0,0,0],[0,0.25,0,0,0],[0,0.5,0,0,0],[0,0.75,0,0,0],[0,1,0,0,0],[0.25,0,0,0,0],[0.25,1,0,0,0],[0.5,0,0,0,0],[0.5,1,0,0,0],[0.75,0,0,0,0],[0.75,1,0,0,0],[1,0,0,0,0],[1,0.25,0,0,0],[1,0.5,0,0,0],[1,0.75,0,0,0],[1,1,0,0,0]];" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="310" y="200" width="120" height="40" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-26" value="" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" target="KwNStDcgQ-FNAQaH3l0E-25">
<mxGeometry width="80" relative="1" as="geometry">
<mxPoint x="270" y="220" as="sourcePoint" />
<mxPoint x="310" y="220" as="targetPoint" />
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-27" value="" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="KwNStDcgQ-FNAQaH3l0E-25" target="WIyWlLk6GJQsqaUBKTNV-7">
<mxGeometry width="80" relative="1" as="geometry">
<mxPoint x="540" y="240" as="sourcePoint" />
<mxPoint x="620" y="240" as="targetPoint" />
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-93" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="KwNStDcgQ-FNAQaH3l0E-28" target="KwNStDcgQ-FNAQaH3l0E-92">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-28" value="&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;ISMAS trigger&lt;/div&gt;&lt;div&gt;&lt;b&gt;WAIT&lt;/b&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;?&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;/div&gt;" style="rhombus;whiteSpace=wrap;html=1;shadow=0;fontFamily=Helvetica;fontSize=12;align=center;strokeWidth=1;spacing=6;spacingTop=-4;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="300" y="380" width="140" height="90" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-29" value="" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="KwNStDcgQ-FNAQaH3l0E-9" target="KwNStDcgQ-FNAQaH3l0E-78">
<mxGeometry width="80" relative="1" as="geometry">
<mxPoint x="540" y="240" as="sourcePoint" />
<mxPoint x="420" y="300" as="targetPoint" />
<Array as="points">
<mxPoint x="525" y="300" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-34" value="" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;entryX=1;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" target="KwNStDcgQ-FNAQaH3l0E-37">
<mxGeometry width="80" relative="1" as="geometry">
<mxPoint x="220" y="260" as="sourcePoint" />
<mxPoint x="320" y="300" as="targetPoint" />
<Array as="points">
<mxPoint x="220" y="300" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-37" value="&lt;div&gt;No&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="250" y="280" width="120" height="20" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-42" value="" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="KwNStDcgQ-FNAQaH3l0E-28" target="KwNStDcgQ-FNAQaH3l0E-72">
<mxGeometry width="80" relative="1" as="geometry">
<mxPoint x="260" y="430" as="sourcePoint" />
<mxPoint x="99" y="560" as="targetPoint" />
<Array as="points">
<mxPoint x="40" y="425" />
<mxPoint x="40" y="570" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-43" value="&lt;div&gt;Yes&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="240" y="405" width="60" height="20" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-65" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" target="KwNStDcgQ-FNAQaH3l0E-45">
<mxGeometry relative="1" as="geometry">
<mxPoint x="120" y="585" as="sourcePoint" />
</mxGeometry>
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-66" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" target="KwNStDcgQ-FNAQaH3l0E-46">
<mxGeometry relative="1" as="geometry">
<mxPoint x="120" y="670" as="sourcePoint" />
</mxGeometry>
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-63" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" target="KwNStDcgQ-FNAQaH3l0E-44">
<mxGeometry relative="1" as="geometry">
<mxPoint x="120" y="510" as="sourcePoint" />
</mxGeometry>
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-76" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.994;exitY=0.491;exitDx=0;exitDy=0;exitPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="KwNStDcgQ-FNAQaH3l0E-75" target="KwNStDcgQ-FNAQaH3l0E-107">
<mxGeometry relative="1" as="geometry">
<mxPoint x="760" y="445" as="targetPoint" />
<mxPoint x="730" y="580" as="sourcePoint" />
</mxGeometry>
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-79" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="KwNStDcgQ-FNAQaH3l0E-78" target="KwNStDcgQ-FNAQaH3l0E-28">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-78" value="" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];fillColor=none;resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="360" y="290" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-81" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="WIyWlLk6GJQsqaUBKTNV-6" target="WIyWlLk6GJQsqaUBKTNV-7">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-83" value="&lt;div&gt;No&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="290" y="90" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-84" value="&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Yes&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="170" y="140" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-86" value="opkg_commands always counts as a file to update" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;fontColor=#000000;darkOpacity=0.05;fillColor=#FFF9B2;strokeColor=none;fillStyle=solid;direction=west;gradientDirection=north;gradientColor=#FFF2A1;shadow=1;size=20;pointerEvents=1;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="240" y="610" width="120" height="100" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-87" value="in case someone has changed the zone (which means changed the branch)" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;fontColor=#000000;darkOpacity=0.05;fillColor=#FFF9B2;strokeColor=none;fillStyle=solid;direction=west;gradientDirection=north;gradientColor=#FFF2A1;shadow=1;size=20;pointerEvents=1;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="90" y="610" width="140" height="100" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-88" value="Update Json-configuration files) and the device controller firmware." style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;fontColor=#000000;darkOpacity=0.05;fillColor=#FFF9B2;strokeColor=none;fillStyle=solid;direction=west;gradientDirection=north;gradientColor=#FFF2A1;shadow=1;size=20;pointerEvents=1;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="370" y="610" width="180" height="100" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-89" value="Sync tariff-files and ini-files with local filesystem." style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;fontColor=#000000;darkOpacity=0.05;fillColor=#FFF9B2;strokeColor=none;fillStyle=solid;direction=west;gradientDirection=north;gradientColor=#FFF2A1;shadow=1;size=20;pointerEvents=1;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="560" y="610" width="160" height="100" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-90" value="Make sure the customer repository is in place and useable." style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;fontColor=#000000;darkOpacity=0.05;fillColor=#FFF9B2;strokeColor=none;fillStyle=solid;direction=west;gradientDirection=north;gradientColor=#FFF2A1;shadow=1;size=20;pointerEvents=1;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="20" y="110" width="140" height="95" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-91" value="&lt;b&gt;NEVER&lt;/b&gt; do any real update-steps without an active ISMAS trigger." style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;fontColor=#000000;darkOpacity=0.05;fillColor=#FFF9B2;strokeColor=none;fillStyle=solid;direction=west;gradientDirection=north;gradientColor=#FFF2A1;shadow=1;size=20;pointerEvents=1;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="20" y="320" width="140" height="80" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-92" value="&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Initial/repaired&lt;/div&gt;&lt;div&gt;clone&lt;/div&gt;&lt;div&gt;?&lt;br&gt;&lt;/div&gt;" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="585" y="375" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-96" value="SUCCESS" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.terminator;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="710" y="10" width="100" height="40" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-132" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="KwNStDcgQ-FNAQaH3l0E-97" target="KwNStDcgQ-FNAQaH3l0E-11">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-97" value="&lt;div&gt;Send Last Version&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;fontSize=12;glass=0;strokeWidth=1;shadow=0;points=[[0,0,0,0,0],[0,0.25,0,0,0],[0,0.5,0,0,0],[0,0.75,0,0,0],[0,1,0,0,0],[0.25,0,0,0,0],[0.25,1,0,0,0],[0.5,0,0,0,0],[0.5,1,0,0,0],[0.75,0,0,0,0],[0.75,1,0,0,0],[1,0,0,0,0],[1,0.25,0,0,0],[1,0.5,0,0,0],[1,0.75,0,0,0],[1,1,0,0,0]];" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="580" y="100" width="110" height="30" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-99" value="No" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="450" y="400" width="60" height="20" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-103" value="No" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="580" y="330" width="60" height="20" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-104" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.15;entryY=0.4;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="KwNStDcgQ-FNAQaH3l0E-92" target="KwNStDcgQ-FNAQaH3l0E-107">
<mxGeometry relative="1" as="geometry">
<mxPoint x="700" y="425" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-106" value="" style="group" vertex="1" connectable="0" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="50" y="540" width="770" height="57" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-72" value="&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp; Checkout branch&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;" style="shape=step;perimeter=stepPerimeter;fixedSize=1;points=[];whiteSpace=wrap;html=1;" vertex="1" parent="KwNStDcgQ-FNAQaH3l0E-106">
<mxGeometry width="130" height="57" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-73" value="&lt;div&gt;Update files&lt;/div&gt;&lt;div&gt;(Json, DeviceController)&lt;br&gt;&lt;/div&gt;" style="shape=step;perimeter=stepPerimeter;fixedSize=1;points=[];whiteSpace=wrap;html=1;" vertex="1" parent="KwNStDcgQ-FNAQaH3l0E-106">
<mxGeometry x="350" width="194" height="57" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-74" value="&lt;div&gt;Compute files &lt;br&gt;&lt;/div&gt;&lt;div&gt;to update&lt;br&gt;&lt;/div&gt;" style="shape=step;perimeter=stepPerimeter;fixedSize=1;points=[];whiteSpace=wrap;html=1;" vertex="1" parent="KwNStDcgQ-FNAQaH3l0E-106">
<mxGeometry x="110" width="140" height="57" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-75" value="&lt;div&gt;(R)Sync with local&lt;/div&gt;&lt;div&gt;filesystem&lt;br&gt;&lt;/div&gt;" style="shape=step;perimeter=stepPerimeter;fixedSize=1;points=[];whiteSpace=wrap;html=1;" vertex="1" parent="KwNStDcgQ-FNAQaH3l0E-106">
<mxGeometry x="520" width="170" height="57" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-105" value="&lt;div&gt;Exec&lt;/div&gt;&lt;div&gt;opkg-commands&lt;/div&gt;" style="shape=step;perimeter=stepPerimeter;fixedSize=1;points=[];whiteSpace=wrap;html=1;" vertex="1" parent="KwNStDcgQ-FNAQaH3l0E-106">
<mxGeometry x="230" width="140" height="57" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-107" value="" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];fillColor=none;resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="750" y="415" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-109" value="&lt;div&gt;Yes&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="690" y="415" width="60" height="20" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-120" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="KwNStDcgQ-FNAQaH3l0E-110" target="KwNStDcgQ-FNAQaH3l0E-97">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-110" value="" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];fillColor=none;resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="625" y="180" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-129" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.95;entryY=0.95;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="KwNStDcgQ-FNAQaH3l0E-92" target="KwNStDcgQ-FNAQaH3l0E-110">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-135" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="KwNStDcgQ-FNAQaH3l0E-130" target="KwNStDcgQ-FNAQaH3l0E-96">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-130" value="&lt;div&gt;Send Last Version&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;fontSize=12;glass=0;strokeWidth=1;shadow=0;points=[[0,0,0,0,0],[0,0.25,0,0,0],[0,0.5,0,0,0],[0,0.75,0,0,0],[0,1,0,0,0],[0.25,0,0,0,0],[0.25,1,0,0,0],[0.5,0,0,0,0],[0.5,1,0,0,0],[0.75,0,0,0,0],[0.75,1,0,0,0],[1,0,0,0,0],[1,0.25,0,0,0],[1,0.5,0,0,0],[1,0.75,0,0,0],[1,1,0,0,0]];" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="705" y="100" width="110" height="30" as="geometry" />
</mxCell>
<mxCell id="KwNStDcgQ-FNAQaH3l0E-131" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="KwNStDcgQ-FNAQaH3l0E-107" target="KwNStDcgQ-FNAQaH3l0E-130">
<mxGeometry relative="1" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,172 @@
\documentclass[12pt]{article}
\usepackage{euler}
\usepackage[english]{babel}
\usepackage{lipsum}
\usepackage{multirow}
\usepackage[colorlinks=true, urlcolor=blue, linkcolor=red]{hyperref}
\newcounter{Chapcounter}
\newcommand\showmycounter{\addtocounter{Chapcounter}{1}\themycounter}
\newcommand{\chapter}[1]
{{\centering
\addtocounter{Chapcounter}{1} \Large \underline{\textbf{ \color{blue} Chapter \theChapcounter: ~#1}} }
\addcontentsline{toc}{section}{ \color{blue} Chapter:~\theChapcounter~~ #1}
}
\hypersetup{colorlinks=true}
\hypersetup{linkcolor=black}
\title{Update PTU}
\author{Gerhard Hoffmann}
\date{\today}
\begin{document}
\maketitle
% \chapter{Introduction}
\section{Motivation}
The two main components of a PSA are
\begin{itemize}
\item PTU software.
\item Device controller (DC) firmware.
\end{itemize}
While the DC firmware is basically the same for each PSA (even for different
customers), the PTU software is highly dependent on customer requirements.\par
Hence, each customer is assigned an own git-repository, which will be loaded
("cloned") on the PSA when configuring the machine for the first time.\par
Two special tools, the {\bf UpdateController} (a \href{https://doc.qt.io/qt-5/}{Qt}
binary [{\bf \nameref{UpdateTool}}]) and the {\bf UpdateScript}
(a \href{https://www.gnu.org/software/bash/manual/bash.html}{bash}
script [{\bf \nameref{UpdateScript}}]), work together to finish a PSA installation.\par
\section{PSA: Initial configuration}
For the initial configuration, a PSA loads a customer-specific git-repository,
which structure is detailed below [{\bf \nameref{repostructure}}].\par
The "git clone" for the repository is done by the UpdateScript [{\bf \nameref{UpdateScript}}].
It updates the file
\begin{center}
\fbox{
/opt/app/tools/atbupdate/update\_log.csv
}
\end{center}
which will be interpreted by the UpdateController [{\bf \nameref{UpdateTool}}].
The structure of [{\bf \nameref{updatelogcsv}}] is detailed below.\par
Each line of update\_log.csv represents a command for the UpdateController,
which will either download certain files to the DC or execute some
\href{https://openwrt.org/docs/guide-user/additional-software/opkg}{opkg}
commands [{\bf \nameref{opkg}}].
\section{PSA: Update}
The update of a PSA is basically managed by \href{https://git-scm.com/}{git}, a
free and open source distributed version control system. Like many other Version
Control Systems, git has a way to fire off custom scripts when certain important
actions occur, so-called \href{https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks}{hooks}.
The hooks used for a PSA update are called {\bf post-checkout} and {\bf post-merge}.\par
The post-checkout hook is called when checking out some branch of the customer-repository
(for instance when cloning the customer repository for the first time). In contrast,
the post-merge hook is called when a \href{https://www.git-scm.com/docs/git-pull}{git pull}
operation has been issued (more precisely, a \href {https://www.git-scm.com/docs/git-fetch}{git-fetch}
followed by a \href{https://www.git-scm.com/docs/git-merge}{git-merge}).\par
Both hooks update the file [{\bf \nameref{updatelogcsv}}], which is interpreted
by the UpdateController in a second step.
% \chapter{Update-Tool "up\_dev\_ctrl"}
\section{up\_dev\_ctrl}
\label{UpdateTool}
The update-tool is a Qt binary ("up\_dev\_ctrl") and called by the
system-controller application. It is installed under
\begin{center}
\fbox{
/opt/app/tools/atbupdate/up\_dev\_ctrl
}
\end{center}
and has two responsibilities:
\begin{itemize}
\item Call update-script "update\_psa".
\item Update the device controller firmware.
\end{itemize}
\subsection{Calling the update-script "update\_psa"}
The update-script "update\_psa" is about executing all git-commands
necessary to clone and pull a customer repository.
\newpage
% \chapter{Update-Script "update\_psa"}
\section{update\_psa}
\label{UpdateScript}
Inside of such a
repository, there are at least the following directories:
\begin{itemize}
\item {\bf etc}
\item {\bf etc/dc}\newline
Contains the device controller firmware as binary file.
\item {\bf etc/psa\_config}\newline
Contains the printer template files (JSON).
\item {\bf etc/psa\_tariff}\newline
Contains the tariff files (JSON).
\item {\bf etc/psa\_update}\newline
Contains a single file for opkg-commands.
\end{itemize}
\newpage
%\chapter{Annex}
\section{Structure of a customer git-repository}
\label{repostructure}
\subsection{The post-checkout hook}
TODO: checkout\_history
\subsection{The post-merge hook}
They are both located under the {\bf .githooks}-directory
\section{update\_log.csv}
\label{updatelogcsv}
\begin{table}[h!]
\begin{center}
\caption{Initial state of update\_log.csv}
\label{tab:initial_state}
\begin{tabular}{llll}
\textbf{Request} & \textbf{Name} & \textbf{Date} & \textbf{Status}\\
\hline
\multicolumn{4}{c}{}\\
DOWNLOAD & /etc/dc/dc2c4.21.bin & 2023-05-01T12:00:00 & N/A\\
DOWNLOAD & /etc/psa\_config/DC2C\_print01.json & 2023-05-01T12:00:00 & N/A\\
DOWNLOAD & /etc/psa\_config/DC2C\_print02.json & 2023-05-01T12:00:00 & N/A\\
DOWNLOAD & /etc/psa\_config/DC2C\_print03.json & 2023-05-01T12:00:00 & N/A\\
DOWNLOAD & /etc/psa\_config/DC2C\_print04.json & 2023-05-01T12:00:00 & N/A\\
DOWNLOAD & /etc/psa\_config/DC2C\_print29.json & 2023-05-01T12:00:00 & N/A\\
DOWNLOAD & /etc/psa\_config/DC2C\_print32.json & 2023-05-01T12:00:00 & N/A\\
EXECUTE & opkg update & 2023-05-01T12:00:00 & N/A\\
\end{tabular}
\end{center}
\end{table}
\begin{table}[h!]
\begin{center}
\caption{State of update\_log.csv after updating}
\label{tab:initial_state}
\begin{tabular}{llll}
\textbf{Request} & \textbf{Name} & \textbf{Date} & \textbf{Status}\\
\hline
\multicolumn{4}{c}{}\\
DONE & /etc/dc/dc2c4.21.bin & 2023-05-01T12:00:00 & N/A\\
DONE & /etc/psa\_config/DC2C\_print01.json & 2023-05-01T12:00:00 & N/A\\
DONE & /etc/psa\_config/DC2C\_print02.json & 2023-05-01T12:00:00 & N/A\\
DONE & /etc/psa\_config/DC2C\_print03.json & 2023-05-01T12:00:00 & N/A\\
DONE & /etc/psa\_config/DC2C\_print04.json & 2023-05-01T12:00:00 & N/A\\
DONE & /etc/psa\_config/DC2C\_print29.json & 2023-05-01T12:00:00 & N/A\\
DONE & /etc/psa\_config/DC2C\_print32.json & 2023-05-01T12:00:00 & N/A\\
DONE & opkg update & 2023-05-01T12:00:00 & N/A\\
\end{tabular}
\end{center}
\end{table}
\newpage
\section{The package manager "opkg"}
\label{opkg}
\section{Known problems}
\end{document}

View File

@@ -0,0 +1,595 @@
#include "git_client.h"
#include "update.h"
#include "worker.h"
#include "utils.h"
#include "process/command.h"
#include <QRegularExpression>
#include <QDebug>
#include <QDir>
#include <QStringList>
GitClient::GitClient(QString const &customerRepositoryPath,
QString const &customerNrStr,
QString const &customerRepository,
QString const &workingDirectory,
QString const &branchName,
QObject *parent)
: QObject(parent)
, m_worker(qobject_cast<Worker *>(parent))
, m_repositoryPath(customerRepositoryPath)
, m_customerNr(customerNrStr)
, m_workingDirectory(workingDirectory)
, m_branchName(branchName)
, m_customerRepository(customerRepository) {
if (!m_worker) {
qCritical() << "ERROR CASTING PARENT TO WORKER FAILED";
}
}
bool GitClient::gitCloneCustomerRepository() {
/* 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.
*/
// Note: for some reason it is necessary to pass "--progress ---v",
// otherwise QProcess returns an error of 128 = 0x80 for the command.
QString gitCommand("git clone --progress -vvv --filter=blob:none ");
gitCommand += m_repositoryPath;
Command c(gitCommand);
qInfo() << "IN CURRENT WD" << m_workingDirectory
<< "CLONE VIA COMMAND" << gitCommand;
if (c.execute(m_workingDirectory)) { // execute the command in wd
QString const result = c.getCommandResult();
if (!result.isEmpty()) {
// Cloning into 'customer_281'...\n
int customer = -1;
int cloning = result.indexOf("Cloning", 0, Qt::CaseInsensitive);
if (cloning != -1) {
customer = result.indexOf("customer_", cloning, Qt::CaseInsensitive);
if (customer != -1) {
QString customerNr = result.mid(customer);
static constexpr char const ch = '\'';
int i = customerNr.indexOf(QChar(ch));
if (i != -1) {
if ((customerNr = customerNr.mid(0, i)) == m_customerNr) {
qInfo() << "CLONING" << m_repositoryPath << "OK";
return true;
}
Utils::printCriticalErrorMsg(
QString("ERROR CLONE RESULT HAS WRONG CUSTOMER-NR. (%1 != %2) CLONE_RESULT=%3")
.arg(customerNr)
.arg(m_customerNr)
.arg(result));
return false;
}
}
}
Utils::printCriticalErrorMsg(
QString("ERROR CLONE RESULT HAS WRONG FORMAT. CLONING=%1 CUSTOMER=%2 CLONE_RESULT=%3")
.arg(cloning)
.arg(customer)
.arg(result));
return false;
}
Utils::printCriticalErrorMsg("ERROR CLONE RESULT IS EMPTY");
}
return false;
}
bool GitClient::copyGitConfigFromMaster() { // only allowed when called in
// master branch (???)
if (QDir(m_customerRepository).exists()) {
QString const cp = QString("cp .gitconfig .git/config");
Command c("bash");
if (c.execute(m_customerRepository, QStringList() << "-c" << cp)) {
qInfo() << "cp .gitconfig .git/config OK";
return true;
}
qCritical() << "ERROR cp .gitconfig .git/config";
}
return false;
}
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
}
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()) {
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.
*/
std::optional<QStringList> GitClient::gitDiff(QString const &commits) {
if (QDir(m_customerRepository).exists()) {
// 409f198..6c22726
QString gitCommand("git diff --compact-summary ");
gitCommand += 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) {
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 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 const file = line.left(pipeIndex).trimmed();
qInfo() << "FILE (PIPE)" << file;
fileNames << file;
}
}
if (!fileNames.isEmpty()) {
return fileNames;
}
}
}
return std::nullopt;
}
bool GitClient::gitFsck() {
bool r = false;
if (QDir(m_customerRepository).exists()) {
Command c("git fsck");
if ((r = c.execute(m_customerRepository)) == false) {
QString const &s = c.getCommandResult().trimmed();
Utils::printCriticalErrorMsg(QString("GIT FSCK FAILED: %1").arg(s));
}
}
return r;
}
bool GitClient::branchExistsRemotely() {
bool remoteBranchExists = false;
if (QDir(m_customerRepository).exists()) {
qInfo() << "BRANCH NAME" << m_branchName;
QString const cmd = QString("git ls-remote --exit-code --heads origin %1").arg(m_branchName);
Command c(cmd);
if (c.execute(m_customerRepository)) {
// expected result: c16c833c8778c1b3691a74afee5a469177e4e69b refs/heads/zg1/zone1000
QString const s = c.getCommandResult().trimmed();
if (!s.isEmpty()) {
// the result is only one line
if ((remoteBranchExists = s.contains(m_branchName)) == true) {
qCritical() << "(" << __func__ << ":" << __LINE__ << ") branch"
<< m_branchName << "EXISTS REMOTELY. (" << s << ")";
}
} else {
Utils::printCriticalErrorMsg(QString("EMPTY RESULT FOR CMD %1").arg(cmd));
}
} else {
Utils::printCriticalErrorMsg(QString("FAILED TO EXEC '%1'").arg(cmd));
}
}
return remoteBranchExists;
}
bool GitClient::branchExistsLocally() {
Command c("git branch -l");
if (c.execute(m_customerRepository)) {
QString const s = c.getCommandResult().trimmed();
if (!s.isEmpty()) {
QStringList lines = Update::split(s, '\n');
if (!lines.empty()) {
for (int i=0; i < lines.size(); ++i) {
QString line = lines.at(i);
// expected: * [new branch] zg1/zone12 -> origin/zg1/zone12"
if (line.contains(m_branchName)) {
if (m_worker) {
QStringList lst(QString("BRANCH-NAME %1 CONTAINED IN RESULT %2").arg(m_branchName).arg(s));
m_worker->CONSOLE(lst) << Worker::UPDATE_STEP::PULL_NEW_BRANCH;
}
return true;
}
}
if (m_worker) {
QStringList lst(QString("BRANCH-NAME %1 NOT CONTAINED IN RESULT %2").arg(m_branchName).arg(s));
m_worker->CONSOLE(lst) << Worker::UPDATE_STEP::PULL_NEW_BRANCH_FAILURE;
}
} else {
if (m_worker) {
QStringList lst(QString("'git branch -l' RETURNED NO LINES"));
m_worker->CONSOLE(lst) << Worker::UPDATE_STEP::PULL_NEW_BRANCH_FAILURE;
}
}
} else {
if (m_worker) {
QStringList lst(QString("'git branch -l' RETURNED EMPTY RESULT"));
m_worker->CONSOLE(lst) << Worker::UPDATE_STEP::PULL_NEW_BRANCH_FAILURE;
}
}
} else {
if (m_worker) {
QStringList lst(QString("FAILED TO EXEC 'git branch -l'"));
m_worker->CONSOLE(lst) << Worker::UPDATE_STEP::PULL_NEW_BRANCH_FAILURE;
}
}
return false;
}
bool GitClient::gitPullNewBranches() {
if (QDir(m_customerRepository).exists()) {
Command c("git pull");
if (c.execute(m_customerRepository)) {
QString const s = c.getCommandResult().trimmed();
// expected: Already up-to-date.
if (!s.isEmpty()) {
QStringList lst;
QString msg(QString("GIT-PULL-NEW-BRANCH. RESULT=%1").arg(s));
if (m_worker) {
m_worker->CONSOLE(lst) << Worker::UPDATE_STEP::PULL_NEW_BRANCH;
}
return true;
}
}
}
return false;
}
/*
Hat sich nichts geaendert, so werden auch keine Commits <>..<> angezeigt
*/
std::optional<QString> GitClient::gitPull() {
if (QDir(m_customerRepository).exists()) {
qInfo() << "BRANCH NAME" << m_branchName;
#if 0
IMPORTANT COMMENT:
If remote host keys are changed, then
export GIT_SSH_COMMAND="ssh -i /opt/app/tools/atbupdate/.keys/id_ed25519_ptuConfig"
git pull
leads to the following warning/error message:
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ECDSA key sent by the remote host is
SHA256:vOD5jF2hglGktqLhK9ABxfEjwEgIK68/v9erdT05NDQ.
Please contact your system administrator.
Add correct host key in /home/root/.ssh/known_hosts to get rid of this message.
Offending ECDSA key in /home/root/.ssh/known_hosts:1
Password authentication is disabled to avoid man-in-the-middle attacks.
Keyboard-interactive authentication is disabled to avoid man-in-the-middle attacks.
Agent forwarding is disabled to avoid man-in-the-middle attacks.
X11 forwarding is disabled to avoid man-in-the-middle attacks.
Already up to date.
This first part is from ssh itself. Only the last line is the git message.
Here an output of running ATBUpdateTool with a corrupted known-hosts-file:
...
Oct 23 14:18:18 ATB_PTU5 ATBUpdateTool[2696]: ( branchExistsRemotely : 310 ) branch "zg1/zone1" EXISTS REMOTELY. ( "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r\n@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @\r\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r\nIT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!\r\nSomeone could be eavesdropping on you right now (man-in-the-middle attack)!\r\nIt is also possible that a host key has just been changed.\r\nThe fingerprint for the ECDSA key sent by the remote host is\nSHA256:vOD5jF2hglGktqLhK9ABxfEjwEgIK68/v9erdT05NDQ.\r\nPlease contact your system administrator.\r\nAdd correct host key in /home/root/.ssh/known_hosts to get rid of this message.\r\nOffending ECDSA key in /home/root/.ssh/known_hosts:1\r\nPassword authentication is disabled to avoid man-in-the-middle attacks.\r\nKeyboard-interactive authentication is disabled to avoid man-in-the-middle attacks.\r\nAgent forwarding is disabled to avoid man-in-the-middle attacks.\r\nX11 forwarding is disabled to avoid man-in-the-middle attacks.\r\nbd4e8da4780b1a7d6be3d3ce8419f43ccf7e706f\trefs/heads/zg1/zone1" )
Oct 23 14:18:18 ATB_PTU5 ATBUpdateTool[2696]: EXECUTED "git branch -l" "(runtime 16ms)" with code 0 IN "/opt/app/tools/atbupdate/customer_336"
Oct 23 14:18:18 ATB_PTU5 ATBUpdateTool[2696]: "UPDATE_STEP::PULL_NEW_BRANCH"
Oct 23 14:18:18 ATB_PTU5 ATBUpdateTool[2696]: "BRANCH-NAME zg1/zone1 CONTAINED IN RESULT master\n* zg1/zone1" .....
The download continues.
#endif
Command c("git pull");
if (c.execute(m_customerRepository)) {
QString const s = c.getCommandResult().trimmed();
if (!s.isEmpty()) {
QStringList lines = Update::split(s, '\n');
worker()->CONSOLE(lines) << Worker::UPDATE_STEP::UPDATE_REPOSITORY;
if (!lines.empty()) {
static const QRegularExpression alreadyUpToDate("^\\s*Already\\s+up\\s+to\\s+date.*$");
if (std::none_of(lines.cbegin(), lines.cend(),
[](QString const &s) { return s.contains(alreadyUpToDate); })) {
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
//
// More exactly:
// remote: Counting objects: 382, done.
// remote: Compressing objects: 100% (203/203), done.
// remote: Total 278 (delta 177), reused 103 (delta 59)
// Receiving objects: 100% (278/278), 4.89 MiB | 539 KiB/s, done.
// Resolving deltas: 100% (177/177), completed with 40 local objects.
// From ssh://longair@pacific.mpi-cbg.de/srv/git/fiji
// 3036acc..9eb5e40 debian-release-20081030 -> origin/debian-release-20081030
// * [new branch] debian-release-20081112 -> origin/debian-release-20081112
// * [new branch] debian-release-20081112.1 -> origin/debian-release-20081112.1
// 3d619e7..6260626 master -> origin/master
//
// The most important bits here are the lines like these:
//
// 3036acc..9eb5e40 debian-release-20081030 -> origin/debian-release-20081030
// * [new branch] debian-release-20081112 -> origin/debian-release-20081112
//
// The first line of these two shows that your remote-tracking branch
// origin/debian-release-20081030 has been advanced from the commit 3036acc to 9eb5e40.
// The bit before the arrow is the name of the branch in the remote repository.
// The second line similarly show that since we last did this, a new remote tracking
// branch has been created. (git fetch may also fetch new tags if they have appeared
// in the remote repository.)
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.at(i));
if (match.hasMatch()) {
if (re.captureCount() == 3) { // start with full match (0), then the other 3 matches
QString const matchCaptured = match.captured(2);
worker()->CONSOLE(QStringList(matchCaptured)) << Worker::UPDATE_STEP::UPDATE_REPOSITORY;
return matchCaptured;
} else {
QStringList lst(QString("(wrong capture count (%1)").arg(re.captureCount()));
worker()->GUI(lst) << (worker()->CONSOLE(lst) << Worker::UPDATE_STEP::UPDATE_REPOSITORY_FAILURE);
}
} else {
QStringList lst("no regex-match for commits");
worker()->GUI(lst) << (worker()->CONSOLE(lst) << Worker::UPDATE_STEP::UPDATE_REPOSITORY_FAILURE);
}
}
}
if (!found) {
QStringList lst(QString("unknown branch name ") + m_branchName);
worker()->GUI(lst) << (worker()->CONSOLE(lst) << Worker::UPDATE_STEP::UPDATE_REPOSITORY_FAILURE);
}
} else {
return "Already up to date";
}
} else {
QStringList lst(QString("WRONG FORMAT FOR RESULT OF 'GIT PULL' ") + s);
worker()->GUI(lst) << (worker()->CONSOLE(lst) << Worker::UPDATE_STEP::UPDATE_REPOSITORY_FAILURE);
}
} else {
QStringList lst("EMPTY RESULT FOR 'GIT PULL'");
worker()->GUI(lst) << (worker()->CONSOLE(lst) << Worker::UPDATE_STEP::UPDATE_REPOSITORY_FAILURE);
}
}
} else {
QStringList lst(QString("REPOSITORY ") + m_customerRepository + " DOES NOT EXIST");
worker()->GUI(lst) << (worker()->CONSOLE(lst) << Worker::UPDATE_STEP::UPDATE_REPOSITORY_FAILURE);
}
return std::nullopt;
}
std::optional<QStringList> GitClient::gitMerge() {
Command c("git merge");
if (c.execute(m_workingDirectory)) {
QString s = c.getCommandResult();
QStringList lst = Update::split(s, '\n');
return lst;
}
return std::nullopt;
}
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)) {
QString const r = c.getCommandResult();
int const idx = r.indexOf("commit ");
if (idx != -1) {
return r.mid(idx + 8).trimmed();
}
}
}
}
return "";
}
// fileName has to an absolute path
QString GitClient::gitBlob(QString fileName) {
QFileInfo fi(fileName);
if (fi.exists()) {
QString const gitCommand = QString("git hash-object %1").arg(fileName);
Command c(gitCommand);
if (c.execute("/tmp")) {
return c.getCommandResult().trimmed();
}
}
return "N/A";
}
QString GitClient::gitCommitForBlob(QString blob) {
if (QDir(m_customerRepository).exists()) {
QString const gitCommand
= QString("git whatchanged --all --find-object=%1 | head -n 1").arg(blob);
Command c(gitCommand);
if (c.execute(m_customerRepository)) {
return c.getCommandResult();
}
}
return "";
}
bool GitClient::gitIsFileTracked(QString fName) {
if (QDir(m_customerRepository).exists()) {
QString const gitCommand
= QString("git ls-files --error-unmatch %1").arg(fName);
Command c(gitCommand);
return c.execute(m_customerRepository);
}
return false;
}
//get_commit_for_blob () {
// # search for the blob in all commits for the file(name) $1
// echo $(git log --all --pretty=format:%H -- $2 |
// xargs -I{} bash -c "git ls-tree {} -- $2 |
// grep -q $1 && echo -n {} && head -n 1")
//}

View File

@@ -0,0 +1,67 @@
#ifndef GIT_CLIENT_H_INCLUDED
#define GIT_CLIENT_H_INCLUDED
#include <QObject>
#include <QStringList>
#include <optional>
#include <QCoreApplication>
#include "ismas/ismas_client.h"
class Worker;
class GitClient : public QObject {
Q_OBJECT
Worker *m_worker;
QString const m_repositoryPath;
QString const m_customerNr;
QString const m_workingDirectory;
QString m_branchName;
QString const m_customerRepository;
bool copyGitConfigFromMaster();
public:
explicit GitClient(QString const &customerRepositoryUrl,
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; }
QString const branchName() const { return m_branchName; }
QString branchName() { return m_branchName; }
QString repositoryPath() { return m_repositoryPath; }
QString const repositoryPath() const { return m_repositoryPath; }
bool gitCloneAndCheckoutBranch();
Worker *worker() { return m_worker; }
Worker const *worker() const { return m_worker; }
std::optional<QString> gitPull();
std::optional<QStringList> gitDiff(QString const &commit);
std::optional<QStringList> gitMerge();
bool gitFsck();
QString gitLastCommit(QString fileName);
QStringList gitShowReason(QString branchName);
static QString gitBlob(QString fileName);
QString gitCommitForBlob(QString blob);
bool gitIsFileTracked(QString file2name);
bool branchExistsRemotely();
bool branchExistsLocally();
bool gitPullNewBranches();
};
#endif // GIT_CLIENT_H_INCLUDED

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,308 @@
#ifndef ISMAS_CLIENT_H_INCLUDED
#define ISMAS_CLIENT_H_INCLUDED
#include <QObject>
#include <QString>
#include <optional>
struct PSAInstalled {
struct VersionInfo {
QString created;
QString reason;
QString lastCommit;
} versionInfo;
struct Tariff {
// VersionInfo versionInfo;
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 {
// VersionInfo versionInfo;
int size;
QString blob;
QString lastCommit;
QString loadTime;
} opkg;
struct DC {
// VersionInfo versionInfo;
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 {
// VersionInfo versionInfo;
QString name;
QString blob;
int size;
};
DC2C cash;
DC2C conf;
DC2C device;
DC2C print[32];
QString ptuPackageVersion;
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,
// if between 00:00 - 04:00 Wait-button state not WAIT, then we assume
// that's an automatic nightly (not-necessary) update
NO_UPDATE_NECESSARY=1,
// if APISM reports the ISMAS is not available (15x, 6s delay each)
ISMAS_NO_CONNECTION_ERROR=2,
// if not within 00:00-04:00 and WAIT-button was not in state WAIT
ISMAS_TRIGGER_ERROR=3,
// cloning git repo. not possible
GIT_CLONE_ERROR=4,
// pulling from remote git server not possible
GIT_PULL_ERROR=5,
// fetching from remote git server not possible
GIT_FETCH_ERROR=6,
// merging fetched data not possible
GIT_MERGE_ERROR=7,
// check sanity of local customer-repository failed
GIT_CHECK_REPOSITORY_ERROR=8,
// switch/checkout of branch (i.e. zone) on error
GIT_SWITCH_BRANCH_ERROR=9,
// fetch/pull of new branch failed. the new branch was not available
// when installing via SD-card followed by intial clone during the
// update process.
GIT_FETCH_NEW_BRANCH_ERROR=10,
// error computing git-blob hash-value
GIT_HASH_ERROR=11,
// update for general json files failed.
JSON_FILES_UPDATE_ERROR=12,
// error downloading config-json-files to device controller
JSON_FILES_DOWNLOAD_ERROR=13,
// error downloading device-controller
DC_DOWNLOAD_ERROR=14,
// error rsyncing json/ini-files to local filesystem
RSYN_ERROR=15,
// HASH_VALUE_ERROR=14,
// HW_COMPATIBILITY_ERROR=15,
OPKG_COMMANDS_ERROR=16,
// CLEANUP_ERROR=18,
UPDATE_IN_ERROR_STATE=99
};
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,
char const *step,
char const *step_result,
char const *version);
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(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());
// legacy
QString updateOfPSASendVersion(PSAInstalled const &psa);
#if 0
enum class UPDATE_COMPONENT {
TARIFF,
SOFTWARE_ATBQT,
SOFTWARE_APISM,
SOFTWARE_ATB_UPDATE_TOOL,
CONFIG_PTU5_CPU_SERIAL,
CONFIG_DEVICE_CONTROLLER,
CONFIG_PRINTER,
CONFIG_BNA,
PLUGIN_ATB_DEVICE_CONTROLLER,
PLUGIN_INGENICO_CC,
PLUGIN_MOBILISIS_CALC_PRICE,
PLUGIN_MOBILISIS_CALC_PRICE_UI,
PLUGIN_PRM_CALC_PRICE,
PLUGIN_PRM_CALC_PRICE_UI,
PLUGIN_TCP_ZVT_CC,
OPKG_COMMANDS,
HARDWARE_DEVICES,
OS,
DC2C_CASH_JSON,
DC2C_CONF_JSON,
DC2C_DEVICE_JSON,
DC2C_PRINT01_JSON,
DC2C_PRINT02_JSON,
DC2C_PRINT03_JSON,
DC2C_PRINT04_JSON,
DC2C_PRINT05_JSON,
DC2C_PRINT06_JSON,
DC2C_PRINT07_JSON,
DC2C_PRINT08_JSON,
DC2C_PRINT09_JSON,
DC2C_PRINT10_JSON,
DC2C_PRINT11_JSON,
DC2C_PRINT12_JSON,
DC2C_PRINT13_JSON,
DC2C_PRINT14_JSON,
DC2C_PRINT15_JSON,
DC2C_PRINT16_JSON,
DC2C_PRINT17_JSON,
DC2C_PRINT18_JSON,
DC2C_PRINT19_JSON,
DC2C_PRINT20_JSON,
DC2C_PRINT21_JSON,
DC2C_PRINT22_JSON,
DC2C_PRINT23_JSON,
DC2C_PRINT24_JSON,
DC2C_PRINT25_JSON,
DC2C_PRINT26_JSON,
DC2C_PRINT27_JSON,
DC2C_PRINT28_JSON,
DC2C_PRINT29_JSON,
DC2C_PRINT30_JSON,
DC2C_PRINT31_JSON,
DC2C_PRINT32_JSON,
};
QString sendLastVersion(UPDATE_COMPONENT comp, PSAInstalled const &psa);
#endif
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

242
UpdatePTUDevCtrl/main.cpp Normal file
View File

@@ -0,0 +1,242 @@
#include <QCoreApplication>
#include <QApplication>
#include <QDebug>
#include <QTimer>
#include <QFileInfo>
#ifdef __linux__
#include <stdlib.h> // system()
#endif
#include "message_handler.h"
#include <DeviceController/interfaces.h>
#include "commandline_parser.h"
#include <unistd.h>
#include <memory>
#include <QSharedMemory>
#include <QRunnable>
#include <QThreadPool>
#include <QDir>
#include <QProcess>
#include <QCommandLineParser>
#include <QStandardPaths>
#include <QMainWindow>
#include <QSettings>
#include "update.h"
#include "git/git_client.h"
#include "ismas/ismas_client.h"
#include "worker_thread.h"
#include "worker.h"
#include "mainwindow.h"
#include "utils.h"
// #include "process/command.h"
#include <QThread>
#include <QtWidgets>
#include <QScopedPointer>
#include <QScreen>
#if defined (Q_OS_UNIX) || defined (Q_OS_LINUX)
#include <unistd.h>
#include <errno.h>
#endif
#ifdef PTU5
#define SERIAL_PORT "ttymxc2"
#else
#define SERIAL_PORT "ttyUSB0"
#endif
/**
* @mainpage ATB-Update-Tool Documentation
* \section intro_sec Introduction
* The ATB update tool consistes of several binaries, which are executed in
* order:
* * \ref ATBUpdateTool
* * \ref ATBUpdateCheck
* * \ref ATBUpdateGit
* * \ref ATBUpdateSync
* * \ref ATBUpdateOpkg
* * \ref ATBUpdateDC
* * \ref ATBUpdateJsonFiles
* * \ref ATBUpdateShow
*
* The binaries are started synchonously as child processes of \ref ATBUpdateTool:
* each binary will finish before the next one is started.
* The output for the child process is captured in \ref ATBUpdateTool, and
* optionally shown in the GUI presemted to the user.
*
* - \page ATBUpdateTool
* - \page ATBUpdateCheck
* - \page ATBUpdateGit
* - \page ATBUpdateSync
* - \page ATBUpdateOpkg
* - \page ATBUpdateDC
* Typical call: ./ATBUpdateDC --dc-directory /etc/dc/ --debug true
* - \page ATBUpdateJsonFiles
* - \page ATBUpdateShow
*/
// argv[1]: file to send to dc
int main(int argc, char *argv[]) {
QByteArray const value = qgetenv("LC_ALL");
if (value.isEmpty() || value != "C") {
qputenv("LC_ALL", "C");
}
// qputenv("XDG_RUNTIME_DIR", "/var/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(nullptr);
//atbInstallMessageHandler(atbDebugOutput);
setDebugLevel(LOG_NOTICE);
}
CommandLineParser parser;
parser.process(a);
parser.readSettings();
QString repositoryUrl = parser.repositoryUrl();
if (repositoryUrl.endsWith('/')) {
repositoryUrl.chop(1);
}
QString gitSSHCommand("");
if (repositoryUrl.contains("ptu-config.atb-comm.de")) {
QByteArray const v = qgetenv("GIT_SSH_COMMAND");
if (v.isEmpty()) {
QString sshKeyFile("/opt/app/tools/atbupdate/.keys/id_ed25519_ptuConfig");
if (QFileInfo(sshKeyFile).exists()) {
if (qgetenv("GIT_SSH_COMMAND").isNull()) {
gitSSHCommand = "ssh -i /opt/app/tools/atbupdate/.keys/id_ed25519_ptuConfig";
if (!qputenv("GIT_SSH_COMMAND", QByteArray(gitSSHCommand.toStdString().c_str()))) {
qCritical() << "ERROR: GIT_SSH_COMMAND not put into env. Exiting...";
return -1;
}
}
} else {
qCritical() << "ERROR ssh-key-file" << sshKeyFile << "does not exists. Exiting...";
return -1;
}
} else {
gitSSHCommand = QString(v.toStdString().c_str());
qCritical() << "WARNING GIT_SSH_COMMAND already set in enviroment:"
<< gitSSHCommand;
if (gitSSHCommand != "ssh -i /opt/app/tools/atbupdate/.keys/id_ed25519_ptuConfig") {
qCritical() << "ERROR" << gitSSHCommand << "wrong. Exiting...";
return -1;
}
}
}
QString plugInDir = parser.plugInDir();
QString plugInName = parser.plugInName();
QString workingDir = parser.workingDir();
QString iniFileName = parser.iniFileName();
bool const dryRun = parser.dryRun();
bool const noUpdatePsaHardware = parser.noUpdatePsaHardware();
bool const showYoctoVersion = parser.yoctoVersion();
bool const showYoctoInstallStatus = parser.yoctoInstallStatus();
bool const showExtendedVersion = parser.extendedVersion();
bool const alwaysDownloadConfig = parser.alwaysDownloadConfig();
bool const alwaysDownloadDC = parser.alwaysDownloadDC();
QString const rtPath = QCoreApplication::applicationDirPath();
int const machineNr = Utils::read1stLineOfFile("/mnt/system_data/machine_nr");
int const customerNr = Utils::read1stLineOfFile("/mnt/system_data/cust_nr");
int const zoneNr = Utils::read1stLineOfFile("/mnt/system_data/zone_nr");
QString const branchName = (zoneNr != 0)
? QString("zg1/zone%1").arg(zoneNr) : "master";
qInfo() << "pwd ......................" << rtPath;
qInfo() << "repositoryUrl ............" << repositoryUrl;
if (!gitSSHCommand.isEmpty()) {
qInfo() << "GIT_SSH_COMMAND .........." << gitSSHCommand;
}
qInfo() << "plugInDir ................" << plugInDir;
qInfo() << "plugInName ..............." << plugInName;
qInfo() << "workingDir ..............." << workingDir;
qInfo() << "dryRun ..................." << dryRun;
qInfo() << "noUpdatePsaHardware ......" << noUpdatePsaHardware;
qInfo() << "alwaysDownloadConfig ....." << alwaysDownloadConfig;
qInfo() << "alwaysDownloadDC ........." << alwaysDownloadDC;
qInfo() << "showYoctoVersion ........." << showYoctoVersion;
qInfo() << "showYoctoInstallStatus ..." << showYoctoInstallStatus;
qInfo() << "showExtendedVersion ......" << showExtendedVersion;
qInfo() << "iniFileName .............." << iniFileName;
qInfo() << "extended-version ........." << APP_EXTENDED_VERSION;
qInfo() << "machineNr ................" << machineNr;
qInfo() << "customerNr ..............." << customerNr;
qInfo() << "zoneNr ..................." << zoneNr;
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;
}
QThread::currentThread()->setObjectName("main thread");
qInfo() << "Main thread" << QThread::currentThreadId();
if (!QDir(plugInDir).exists()) {
qCritical() << plugInDir
<< "does not exists, but has to contain dc-library";
if (noUpdatePsaHardware == false) {
exit(-1);
}
}
// before loading the library, delete all possible shared memory segments
#if defined Q_OS_LINUX || defined Q_OS_UNIX
// system("rm -rf /tmp/qipc*");
#else
#error "Only tested under UNIX/LINUX"
#endif
Worker worker(customerNr,
machineNr,
zoneNr,
repositoryUrl,
branchName,
plugInDir,
plugInName,
workingDir,
noUpdatePsaHardware,
alwaysDownloadConfig,
alwaysDownloadDC,
dryRun);
MainWindow mw(&worker);
worker.setMainWindow(&mw);
mw.setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
mw.showFullScreen();
// test
//worker.jsUpdate();
worker.workList().exec();
// worker.summary();
return a.exec();
}

View File

@@ -0,0 +1,81 @@
#include <QCoreApplication>
#include <QApplication>
#include <QDebug>
#include <QTimer>
#include <QFileInfo>
#include "message_handler.h"
#include "plugins/interfaces.h"
#include "DCPlugin/include/hwapi.h"
#include <unistd.h>
#include <thread>
#include <memory>
static void updateBinary(std::unique_ptr<hwinf> hw, // update d2dc*.bin
char const *fileToSendToDC,
char const *baudrate,
char const *serialInterface) {
for (int i=0; i < 1;++i) {
hw->dc_updateDC(fileToSendToDC, baudrate, serialInterface);
std::this_thread::sleep_for(std::chrono::milliseconds(3000));
}
QCoreApplication::quit();
}
static void updatePrinterConf(std::unique_ptr<hwinf> hw, // update printer-file
char const *fileToSendToDC,
char const *baudrate,
char const *serialInterface) {
for (int i=0; i < 1;++i) {
hw->dc_updateDC(fileToSendToDC, baudrate, serialInterface);
std::this_thread::sleep_for(std::chrono::milliseconds(3000));
}
QCoreApplication::quit();
}
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
if (!messageHandlerInstalled()) { // change internal qt-QDebug-handling
atbInstallMessageHandler(atbDebugOutput);
setDebugLevel(QtMsgType::QtDebugMsg);
//setDebugLevel(QtMsgType::QtDebugMsg);
}
if (argc > 2) {
qCritical() << "Usage: " << argv[0] << "<file to send to dc>";
return -1;
}
std::unique_ptr<hwinf> hw(new hwapi());
QFileInfo fileInfo(argv[1]);
QString fname(fileInfo.fileName());
int ret = 0;
if (fname.startsWith("dc") && fname.endsWith(".bin")) {
#ifdef PTU5
std::thread t(updateBinary, std::move(hw),
fname.toStdString().c_str(), "115200", "ttymxc2");
#else
std::thread t(updateBinary, std::move(hw),
fname.toStdString().c_str(), "115200", "ttyUSB0");
#endif
ret = a.exec();
t.join();
} else
if (fname.startsWith("DC") && fname.endsWith(".json")) {
#ifdef PTU5
std::thread t(updatePrinterConf, std::move(hw),
fname.toStdString().c_str(), "115200", "ttymxc2");
#else
std::thread t(updatePrinterConf, std::move(hw),
fname.toStdString().c_str(), "115200", "ttyUSB0");
#endif
ret = a.exec();
t.join();
}
return ret;
}

View File

@@ -0,0 +1,54 @@
#include <QCoreApplication>
#include <QApplication>
#include <QDebug>
#include <QTimer>
#include <QFileInfo>
#include "message_handler.h"
#include "interfaces.h"
#include "DCPlugin/include/hwapi.h"
#include <unistd.h>
#include <thread>
#include <memory>
#include <QSharedMemory>
#include <QRunnable>
#include <QThreadPool>
#include <QDir>
#include "update.h"
#include "worker_thread.h"
#include "worker.h"
#ifdef PTU5
#define SERIAL_PORT "ttymxc2"
#else
#define SERIAL_PORT "ttyUSB0"
#endif
// argv[1]: file to send to dc
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
if (!messageHandlerInstalled()) { // change internal qt-QDebug-handling
atbInstallMessageHandler(atbDebugOutput);
setDebugLevel(QtMsgType::QtDebugMsg);
//setDebugLevel(QtMsgType::QtDebugMsg);
}
QByteArray const value = qgetenv("XDG_RUNTIME_DIR");
if (value.size() == 0) {
qputenv("XDG_RUNTIME_DIR", "/run/user/0");
}
QString const update_ctrl_file = "/opt/app/tools/atbupdate/update_log.csv";
QString const workingDir = (argc == 2) ? argv[1] : ".";
Worker worker(update_ctrl_file, workingDir);
qCritical() << "starting main event loop";
int ret = a.exec();
qCritical() << "stopping main event loop" << ret;
return ret;
}

View File

@@ -0,0 +1,62 @@
#include <QCoreApplication>
#include <QApplication>
#include <QDebug>
#include <QTimer>
#include <QFileInfo>
#include "message_handler.h"
#include "plugins/interfaces.h"
#include <unistd.h>
#include <thread>
#include <memory>
#include <QSharedMemory>
#include <QRunnable>
#include <QThreadPool>
#include <QDir>
#include "update.h"
#include "worker_thread.h"
#include "worker.h"
#include <thread>
#ifdef PTU5
#define SERIAL_PORT "ttymxc2"
#else
#define SERIAL_PORT "ttyUSB0"
#endif
static void doWork(QString update_ctrl_file, QString workingDir) {
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
Update update(update_ctrl_file, workingDir);
update.doUpdate();
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
QCoreApplication::quit();
}
// argv[1]: file to send to dc
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
if (!messageHandlerInstalled()) { // change internal qt-QDebug-handling
atbInstallMessageHandler(atbDebugOutput);
setDebugLevel(QtMsgType::QtDebugMsg);
//setDebugLevel(QtMsgType::QtDebugMsg);
}
QByteArray const value = qgetenv("XDG_RUNTIME_DIR");
if (value.size() == 0) {
qputenv("XDG_RUNTIME_DIR", "/run/user/0");
}
// QString const update_ctrl_file = "/opt/app/tools/atbupdate/update_log.csv";
// QString const workingDir = (argc == 2) ? argv[1] : ".";
// std::thread t(doWork, update_ctrl_file, workingDir);
int ret = a.exec();
// t.join();
return ret;
}

View File

@@ -0,0 +1,887 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "worker.h"
#include "utils.h"
#include "utils_internal.h"
#include "log_line_entry.h"
#include "progress_event.h"
#include "update_dc_event.h"
#include "process/update_command.h"
#include <DeviceController/interfaces.h>
#include <QDateTime>
#include <QMessageBox>
#include <QDebug>
#include <QScrollBar>
#include <QEvent>
#include <QColor>
#include <QColorDialog>
#define CHECK_BACKEND_CONNECTION 0
#define CHECK_UPDATE_REQUEST 1
#define UPDATE_CUSTOMER_REPOSITORY 2
#define INSTALL_SW_PACKETS_DRY_RUN 3
#define INSTALL_SW_PACKETS 4
#define UPDATE_DC 5
#define SYNCHRONIZE_REPOSITORY 6
#define INSTALL_DC_CONFIGURATION 7
#define CHECK_ISMAS_CONNECT_PERCENT_START ( 1)
#define CHECK_ISMAS_CONNECT_PERCENT_END (10)
#define CHECK_UPDATE_REQUEST_PERCENT_START (10)
#define CHECK_UPDATE_REQUEST_PERCENT_END (20)
#define UPDATE_GIT_PERCENT_START (20)
#define UPDATE_GIT_PERCENT_END (21)
#define UPDATE_OPKG_NOACTION_PERCENT_START (21)
#define UPDATE_OPKG_NOACTION_PERCENT_END (25)
#define UPDATE_OPKG_PERCENT_START (25)
#define UPDATE_OPKG_PERCENT_END (40)
#define UPDATE_DOWNLOAD_DC_START (41)
#define UPDATE_DOWNLOAD_DC_END (80)
#define UPDATE_SYNC_START (81)
#define UPDATE_SYNC_END (90)
#define UPDATE_DOWNLOAD_JSON_START (91)
#define UPDATE_DOWNLOAD_JSON_END (100)
void MainWindow::onFileChanged(QString const& /*f*/) {
static int i = 30;
ui->updateProgress->setValue(++i);
// TODO: daten an ISMAS senden
}
QProgressBar *MainWindow::progressBar() {
return ui ? ui->updateProgress : nullptr;
}
MainWindow::MainWindow(Worker *worker, QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, m_worker(worker)
, m_width(60)
, m_progressRunning(false)
, m_updateStep(UpdateDcEvent::UpdateStep::NONE)
, m_updateLog("/opt/app/tools/atbupdate/update.log") { // TODO: in ini-file eintragen
ui->setupUi(this);
if (!m_updateLog.open(QIODevice::ReadWrite | QIODevice::Unbuffered)) {
qCritical() << "ERROR can not open" << m_updateLog.fileName();
}
m_updateSteps.resize(8);
m_updateSteps[CHECK_BACKEND_CONNECTION] = "Check backend connection (ISMAS) ";
m_updateSteps[CHECK_UPDATE_REQUEST] = "Check update request ";
m_updateSteps[UPDATE_CUSTOMER_REPOSITORY] = "Update customer repository ";
m_updateSteps[INSTALL_SW_PACKETS_DRY_RUN] = "Install SW packets (dry run) ";
m_updateSteps[INSTALL_SW_PACKETS] = "Install SW packets ";
m_updateSteps[UPDATE_DC] = "Update DC ";
m_updateSteps[SYNCHRONIZE_REPOSITORY] = "Synchronize repository/filesystem ";
m_updateSteps[INSTALL_DC_CONFIGURATION] = "Install DC configuration ";
QString text{};
for (int i = 0; i < m_updateSteps.size(); ++i) {
QString &tmp = m_updateSteps[i];
int len = m_showLineLength - tmp.length();
while (--len > 0) {
tmp += "&nbsp;";
}
m_updateSteps[i] = tmp;
text += m_updateSteps[i];
}
ui->stepLabel->setTextFormat(Qt::RichText);
ui->stepLabel->setText(text);
this->setStatusBar(new QStatusBar(this));
QFont f;
f.setStyleHint(QFont::Monospace);
f.setWeight(QFont::Bold);
f.setFamily("Misc Fixed");
f.setPointSize(11);
this->statusBar()->setFont(f);
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 ").arg(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->updateLabel->setText(lst.join('\n'));
ui->updateLabel->setEnabled(true);
// ui->updateStatus->installEventFilter(this);
m_startTimer = new QTimer(this);
connect(m_startTimer, SIGNAL(timeout()), m_worker, SLOT(start()));
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);
m_statusTimer = new QTimer(this);
if (m_statusTimer) {
connect(m_statusTimer, &QTimer::timeout, [this]() {
static QString p(".");
QTime const &t = QDateTime::currentDateTime().time();
QString s = t.toString(Qt::ISODate);
s += ": Update might take several minutes " + p;
if (p.length() >= 5) {
p = ".";
} else {
p += ".";
}
this->statusBar()->showMessage(s);
});
m_statusTimer->setSingleShot(false);
m_statusTimer->start(1000);
}
connect(m_worker->summaryTimer(), SIGNAL(timeout()), this, SLOT(onSummary()));
m_worker->summaryTimer()->setSingleShot(true);
m_worker->summaryTimer()->setInterval(5000);
connect(ui->exit, SIGNAL(clicked()),this,SLOT(onQuit()));
connect(m_worker, SIGNAL(summary()),this,SLOT(onSummary()));
connect(m_worker, SIGNAL(showSummary(QString)),this,SLOT(onShowSummary(QString)));
connect(m_worker, SIGNAL(disableExit()),this,SLOT(onDisableExit()));
connect(m_worker, SIGNAL(showDcDownload(QString)),this,SLOT(onShowDcDownload(QString)));
connect(m_worker, SIGNAL(showJsonDownload(QString)),this,SLOT(onShowJsonDownload(QString)));
connect(m_worker, SIGNAL(showTariffUpdate(QString)),this,SLOT(onShowTariffUpdate(QString)));
// deprecated
connect(m_worker, SIGNAL(showISMASChecks(QString)),this,SLOT(onShowISMASChecks(QString)));
connect(m_worker, SIGNAL(showISMASConnectivity(QString)),this,SLOT(onShowISMASConnectivity(QString)));
connect(m_worker, SIGNAL(showUpdateRequest(QString)),this,SLOT(onShowUpdateRequest(QString)));
connect(m_worker, SIGNAL(showCustRepoStatus(QString)),this,SLOT(onShowCustRepoStatus(QString)));
connect(m_worker, SIGNAL(showExecOpkgStatus(QString)),this,SLOT(onShowExecOpkgStatus(QString)));
connect(m_worker, SIGNAL(showExecOpkgCommand(QString)),this,SLOT(onShowExecOpkgCommand(QString)));
connect(m_worker, SIGNAL(showExecOpkgOverallResult(QString, bool)),this,SLOT(onShowExecOpkgOverallResult(QString,bool)));
connect(m_worker, SIGNAL(showDownloadDCJsonFilesStatus(QString)),this,SLOT(onShowDownloadDCJsonFilesStatus(QString)));
connect(m_worker, SIGNAL(showSyncCustRepoStatus(QString)),this,SLOT(onShowSyncCustRepoStatus(QString)));
connect(m_worker, SIGNAL(showUpdateDCFirmware(QString)),this,SLOT(onShowUpdateDCFirmware(QString)));
connect(m_worker, SIGNAL(setDcDownloadProgress(int)),this,SLOT(onSetDcDownloadProgress(int)));
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(insertText(QString)),this,SLOT(onInsertText(QString)), Qt::DirectConnection);
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(showErrorMessage(QStringList)),this, SLOT(onShowErrorMessage(QStringList)));
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)));
}
void MainWindow::onSummary() {
QThread::sleep(3);
QString summary, first, second, line, tmp;
QVector<QPair<QString, QString>> vec = Utils::installedPackages();
if (m_worker) {
vec.append(Utils::installedTariffFiles(m_worker, m_worker->customerRepository()));
vec.append(Utils::installedJsonFiles(m_worker, m_worker->customerRepository()));
}
int max_first = 0, max_second = 0;
for (int i = 0; i < vec.size(); ++i) {
max_first = std::max(max_first, vec[i].first.length());
max_second = std::max(max_second, vec[i].second.length());
}
max_first += 5;
summary = "UPDATE SUMMARY\n\n";
if (m_worker) {
first = QString("%1").arg("start", max_first, QChar(' '));
tmp = QString("%1").arg(m_worker->start().toString(Qt::ISODate));
second = QString("%1").arg(tmp, -max_second, QChar(' '));
line = first + ": " + second;
summary += line + "\n";
}
first = QString("%1").arg("update tool version", max_first, QChar(' '));
tmp = QString("%1 - %2 %3").arg(APP_VERSION).arg(APP_BUILD_DATE).arg(APP_BUILD_TIME);
second = QString("%1").arg(tmp, -max_second, QChar(' '));
line = first + ": " + second;
summary += line + "\n";
if (m_worker) {
first = QString("%1").arg("machine number", max_first, QChar(' '));
tmp = QString("%1").arg(m_worker->machineNr());
second = QString("%1").arg(tmp, -max_second, QChar(' '));
line = first + ": " + second;
summary += line + "\n";
first = QString("%1").arg("customer number", max_first, QChar(' '));
tmp = QString("%1").arg(m_worker->customerNr());
second = QString("%1").arg(tmp, -max_second, QChar(' '));
line = first + ": " + second;
summary += line + "\n";
first = QString("%1").arg("zone number", max_first, QChar(' '));
tmp = QString("%1").arg(m_worker->zoneNr());
second = QString("%1").arg(tmp, -max_second, QChar(' '));
line = first + ": " + second;
summary += line + "\n";
}
tmp = targetDcVersion();
if (!tmp.isEmpty()) {
first = QString("%1").arg("target device controller", max_first, QChar(' '));
second = QString("%1").arg(tmp, -max_second, QChar(' '));
line = first + ": " + second;
summary += line + "\n";
}
if (m_worker) {
first = QString("%1").arg("apism", max_first, QChar(' '));
tmp = QString("%1").arg(m_worker->apismVersion());
second = QString("%1").arg(tmp, -max_second, QChar(' '));
line = first + ": " + second;
summary += line + "\n";
}
for (int i = 0; i < vec.size(); ++i) {
first = QString("%1").arg(vec[i].first, max_first, QChar(' '));
second = QString("%1").arg(vec[i].second, -max_second, QChar(' '));
line = first + ": " + second;
summary += line + "\n";
}
if (m_worker) {
emit m_worker->showSummary(summary);
emit m_worker->enableExit();
}
}
void MainWindow::onShowSummary(QString text) {
// QString s = ui->updateLabel->text();
QString s("\n");
ui->updateLabel->setText(s);
ui->updateLabel->hide();
ui->stepLabel->hide();
ui->updateProgress->hide();
s += text;
ui->updateStatus->setText(s);
}
void MainWindow::onSetDcDownloadProgress(int v) {
ui->updateProgress->setValue(v);
}
void MainWindow::onShowTariffUpdate(QString) {
return;
ui->exit->setEnabled(false);
QString s = ui->stepLabel->text();
QString tmp("Install tariff files ");
int len = m_showLineLength - tmp.length();
while (--len > 0) {
tmp += "&nbsp;";
}
s += QString("%1 <font color='Green'>[OK ]</font><br />").arg(tmp);
ui->stepLabel->setText(s);
}
void MainWindow::onShowISMASConnectivity(QString status) {
// qCritical() << __func__ << ":" << __LINE__ << "status" << status;
QString stepResult;
QString s = m_updateSteps[CHECK_BACKEND_CONNECTION].trimmed();
bool const custRepoExists = internal::customerRepoExists();
if (status.contains(UpdateCommand::ISMAS_CONNECTED, Qt::CaseInsensitive)) {
if (custRepoExists) {
s += " <font color='Green'>connected</font>";
stepResult = "ISMAS connected";
} else {
s += " <font color='Green'>connected&nbsp;(initial configuration)</font>";
stepResult = "ISMAS connected (initial configuration)";
}
} else
if (status.contains(UpdateCommand::NO_CUSTOMER_REPOSITORY, Qt::CaseInsensitive)) {
s += " <font color='Blue'>NOT CONNECTED</font>";
stepResult = "ISMAS not connected";
} else
if (status.contains(UpdateCommand::ISMAS_CONNECTION_IN_PROGRESS, Qt::CaseInsensitive)) {
s += " <font color='Green'>connecting</font>";
stepResult = "connecting ISMAS";
} else
if (status.contains(UpdateCommand::ISMAS_NOT_CONNECTED, Qt::CaseInsensitive)) {
if (custRepoExists) {
s += " <font color='Red'>NOT CONNECTED. STOP</font>";
stepResult = "ISMAS not connected";
} else {
s += " <font color='Red'>not connected.&nbsp;(initial configuration)</font>";
stepResult = "ISMAS not connected (initial configuration)";
}
} else {
s += " <font color='Red'>UNKNOWN STATUS</font>";
stepResult = "unknown status";
}
struct LogLineEntry logLine =
initLogLineEntry(1, // receiver
QDateTime::currentDateTime().toString(Qt::ISODate).toUtf8().constData(),
_ISMAS_CONTINUE,
0, // eventState
CHECK_ISMAS_CONNECT_PERCENT_START,
ISMAS::RESULT_CODE::E_SUCCESS,
"show ISMAS connectivity", // step
stepResult.toUtf8().constData()); // stepResult
int w = 0;
if ((w = m_updateLog.write((char *)(&logLine), sizeof(logLine))) != sizeof(logLine)) {
qCritical() << __func__ << ":" << __LINE__ << "ERROR only" << w << "bytes written";
}
m_updateSteps[CHECK_BACKEND_CONNECTION] = s;
s.clear();
for (int i = 0; i < m_updateSteps.size(); ++i) {
s += m_updateSteps[i] + "<br />";
}
ui->stepLabel->setText(s);
logLine =
initLogLineEntry(1, // receiver
QDateTime::currentDateTime().toString(Qt::ISODate).toUtf8().constData(),
_ISMAS_CONTINUE,
0, // eventState
CHECK_ISMAS_CONNECT_PERCENT_END,
ISMAS::RESULT_CODE::E_SUCCESS,
"show ISMAS connectivity", // step
stepResult.toUtf8().constData()); // stepResult
if ((w = m_updateLog.write((char *)(&logLine), sizeof(logLine))) != sizeof(logLine)) {
qCritical() << __func__ << ":" << __LINE__ << "ERROR only" << w << "bytes written";
}
}
void MainWindow::onShowCustRepoStatus(QString status) {
// qCritical() << __func__ << ":" << __LINE__ << "status" << status;
QString s = m_updateSteps[UPDATE_CUSTOMER_REPOSITORY].trimmed();
if (status.contains(internal::GIT_CUSTOMER_REPO_UP_TO_DATE, Qt::CaseInsensitive)) {
s += QString(" <font color='Green'>%1</font>").arg(internal::GIT_CUSTOMER_REPO_UP_TO_DATE);
} else
if (status.contains(internal::GIT_CUSTOMER_REPO_NOT_NECESSARY, Qt::CaseInsensitive)) {
s += QString(" <font color='Green'>%1</font>").arg(internal::GIT_CUSTOMER_REPO_NOT_NECESSARY);
} else {
s += " <font color='Red'>UNKNOWN STATUS</font>";
}
m_updateSteps[UPDATE_CUSTOMER_REPOSITORY] = s;
s.clear();
for (int i = 0; i < m_updateSteps.size(); ++i) {
s += m_updateSteps[i] + "<br />";
}
ui->stepLabel->setText(s);
}
void MainWindow::onShowExecOpkgStatus(QString status) {
qCritical() << __func__ << ":" << __LINE__ << "status" << status;
return;
QString s = ui->stepLabel->text();
QString tmp = "execute opkg commands ";
int len = m_showLineLength - tmp.length();
while (--len > 0) {
tmp += "&nbsp;";
}
if (status.contains(UpdateCommand::EXEC_OPKG_COMMANDS_SUCCESS, Qt::CaseInsensitive)) {
s += QString("%1 <font color='Green'>success</font><br />").arg(tmp);
} else {
s += QString( "%1 <font color='Red'>UNKNOWN STATUS</font><br />").arg(tmp);
}
ui->stepLabel->setText(s);
}
void MainWindow::onShowExecOpkgCommand(QString cmd) {
qCritical() << __func__ << ":" << __LINE__ << "cmd" << cmd;
return;
if (cmd.back() != QChar('\n')) {
cmd += "\n";
}
onInsertText(cmd);
}
void MainWindow::onShowExecOpkgOverallResult(QString status, bool noaction) {
//qCritical() << __func__ << ":" << __LINE__ << "status" << status;
QString s = noaction ?
m_updateSteps[INSTALL_SW_PACKETS_DRY_RUN].trimmed() :
m_updateSteps[INSTALL_SW_PACKETS].trimmed();
if (status.contains(internal::EXEC_OPKG_COMMANDS_SUCCESS, Qt::CaseInsensitive)) {
s += " <font color='Green'>success</font>";
} else
if (status.contains(internal::EXEC_OPKG_COMMANDS_FAIL, Qt::CaseInsensitive)) {
s += QString(" <font color='Red'>%1</font>").arg(internal::EXEC_OPKG_COMMANDS_FAIL);
} else {
s += " <font color='Red'>UNKNOWN STATUS</font>";
}
if (noaction) {
m_updateSteps[INSTALL_SW_PACKETS_DRY_RUN] = s;
} else {
m_updateSteps[INSTALL_SW_PACKETS] = s;
}
s.clear();
for (int i = 0; i < m_updateSteps.size(); ++i) {
s += m_updateSteps[i] + "<br />";
}
ui->stepLabel->setText(s);
}
void MainWindow::onShowDownloadDCJsonFilesStatus(QString status) {
//qCritical() << __func__ << ":" << __LINE__ << "status" << status;
QString s = m_updateSteps[INSTALL_DC_CONFIGURATION].trimmed();
if (status.contains(UpdateCommand::UPDATE_DC_JSON_FILES_SUCCESS, Qt::CaseInsensitive)) {
s += " <font color='Green'>success</font>";
} else {
s += " <font color='Red'>UNKNOWN STATUS</font>";
}
m_updateSteps[INSTALL_DC_CONFIGURATION] = s;
s.clear();
for (int i = 0; i < m_updateSteps.size(); ++i) {
s += m_updateSteps[i] + "<br />";
}
ui->stepLabel->setText(s);
}
void MainWindow::onShowSyncCustRepoStatus(QString status) {
//qCritical() << __func__ << ":" << __LINE__ << "status" << status;
QString s = m_updateSteps[SYNCHRONIZE_REPOSITORY].trimmed();
if (status.contains(UpdateCommand::SYNC_CUSTOMER_REPO_FILES_SUCCESS, Qt::CaseInsensitive)) {
s += " <font color='Green'>success</font>";
} else {
s += " <font color='Red'>UNKNOWN STATUS</font>";
}
m_updateSteps[SYNCHRONIZE_REPOSITORY] = s;
s.clear();
for (int i = 0; i < m_updateSteps.size(); ++i) {
s += m_updateSteps[i] + "<br />";
}
ui->stepLabel->setText(s);
}
void MainWindow::onShowUpdateDCFirmware(QString status) {
// qCritical() << __func__ << ":" << __LINE__ << "status" << status;
QString s = m_updateSteps[UPDATE_DC].trimmed();
if (status.contains(internal::UPDATE_DC_FIRMARE_SUCCESS, Qt::CaseInsensitive)) {
s += " <font color='Green'>success</font>";
} else {
s += " <font color='Red'>UNKNOWN STATUS</font>";
}
s.clear();
for (int i = 0; i < m_updateSteps.size(); ++i) {
if (i != UPDATE_DC) {
s += m_updateSteps[i] + "<br />";
} else {
s += m_updateSteps[i];
}
}
ui->stepLabel->setText(s);
}
void MainWindow::onShowUpdateRequest(QString status) {
// qCritical() << __func__ << ":" << __LINE__ << "status" << status;
//ui->updateProgress->setValue(CHECK_UPDATE_REQUEST_PERCENT_START);
QString s = m_updateSteps[CHECK_UPDATE_REQUEST].trimmed();
bool const custRepoExists = internal::customerRepoExists();
if (status.contains(UpdateCommand::UPDATE_NOT_REQUESTED, Qt::CaseInsensitive)) {
if (custRepoExists) {
s += " <font color='Red'>NOT REQUESTED. STOP.</font>";
} else {
s += " <font color='Blue'>not requested&nbsp;(initial configuration)</font>";
}
} else
if (status.contains(internal::UPDATE_REQUESTED, Qt::CaseInsensitive)) {
if (custRepoExists) {
s += " <font color='Green'>requested</font>";
} else {
s += " <font color='Blue'>requested&nbsp;(initial configuration)</font>";
}
} else
if (status.contains(internal::UPDATE_INITIAL, Qt::CaseInsensitive)) {
s += " <font color='Green'>requested&nbsp;(initial configuration)</font>";
} else
if (status.contains(internal::UPDATE_NOT_NECESSARY, Qt::CaseInsensitive)) {
s += " <font color='Green'>not necessary</font>";
} else
if (status.contains(internal::NO_CUSTOMER_REPOSITORY, Qt::CaseInsensitive)) {
s += " <font color='Blue'>UNKNOWN (ISMAS not connected)</font>";
} else {
s += " <font color='Red'>UNKNOWN</font>";
}
m_updateSteps[CHECK_UPDATE_REQUEST] = s;
s.clear();
for (int i = 0; i < m_updateSteps.size(); ++i) {
s += m_updateSteps[i] + "<br />";
}
ui->stepLabel->setText(s);
//ui->updateProgress->setValue(CHECK_UPDATE_REQUEST_PERCENT_END);
}
void MainWindow::onShowISMASChecks(QString) {
// deprecated
QString s = ui->stepLabel->text();
return;
QString tmp("Check ISMAS connectivity ");
int len = m_showLineLength - tmp.length();
while (--len > 0) {
tmp += "&nbsp;";
}
s += QString("%1 <font color='Green'>[OK ]</font><br />").arg(tmp);
tmp = "Check update activation ";
len = m_showLineLength - tmp.length();
while (--len > 0) {
tmp += "&nbsp;";
}
s += QString("%1 <font color='Green'>[OK ]</font><br />").arg(tmp);
ui->stepLabel->setText(s);
}
void MainWindow::onShowJsonDownload(QString) {
ui->exit->setEnabled(false);
QString s = ui->stepLabel->text();
QString tmp("Send json files to dc-hardware ");
int len = m_showLineLength - tmp.length();
while (--len > 0) {
tmp += "&nbsp;";
}
s += QString("%1 <font color='Green'>[OK ]</font><br />").arg(tmp);
ui->stepLabel->setText(s);
}
void MainWindow::onShowDcDownload(QString version) {
return;
m_targetDcVersion = version;
ui->exit->setEnabled(false);
// test
// onShowISMASChecks("");
onShowISMASConnectivity("connected");
onShowUpdateRequest("activated");
onShowTariffUpdate("");
onShowJsonDownload("");
QString s = ui->stepLabel->text();
QString tmp("Send dc-firmware to dc-hardware ");
int len = m_showLineLength - tmp.length();
while (--len > 0) {
tmp += "&nbsp;";
}
s += QString("%1 <font color='#33FF50'>[OK ]</font> (%2)").arg(tmp).arg(version);
ui->stepLabel->setText(s);
}
MainWindow::~MainWindow() {
delete m_startTimer;
delete m_exitTimer;
delete m_statusTimer;
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, 1));
} 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);
// ueberpruefen: hauptfenster schickt sich selber ein event
// QApplication::postEvent(this, new ProgressEvent(this, progress));
// 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 !!!";
}
}
QThread::yieldCurrentThread();
}
void MainWindow::onStopStartTimer() {
m_startTimer->stop();
}
void MainWindow::onDisableExit() {
ui->exit->setEnabled(false);
}
void MainWindow::onEnableExit() {
ui->exit->setEnabled(true);
}
void MainWindow::onRestartExitTimer() {
m_exitTimer->stop();
m_exitTimer->start(5 * 1000);
scrollDownTextEdit();
ui->updateStatus->setEnabled(false);
}
void MainWindow::onQuit() {
m_exitTimer->stop();
int errorCode = 0;
qCritical()
<< QString("ON QUIT: CURRENT STEP %1")
.arg(m_worker->getSmap()[m_worker->currentStep()]);
// TODO: replace SEND_LAST_VERSION with UPDATE_SUCCEEDED
if (m_worker->currentStep() != Worker::UPDATE_STEP::SEND_LAST_VERSION) {
errorCode = -1;
}
qCritical() << QString("ON QUIT: EXIT CODE %1").arg(errorCode);
qApp->exit(errorCode);
}
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::onInsertText(QString text) {
scrollDownTextEdit();
ui->updateStatus->textCursor().insertText(text);
}
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) {
// removing the last line is really meant for refreshing the last line
// with a string very similar than the original one, typically only
// followed by a suffix.
if (lines.last().contains(text)) {
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);
}
void MainWindow::onShowErrorMessage(QStringList lst) {
if (lst.size() >= 2) {
onShowMessage(lst.at(0), lst.at(1));
}
if (lst.size() == 1) {
onShowMessage(lst.at(0), "");
}
}
void MainWindow::onShowStatusMessage(QStringList lst) {
if (lst.size() >= 2) {
onShowMessage(lst.at(0), lst.at(1));
}
if (lst.size() == 1) {
onShowMessage(lst.at(0), "");
}
}

View File

@@ -0,0 +1,111 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTimer>
#include <QStatusBar>
#include <QVector>
#include <QString>
#include <QProgressBar>
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;
public:
MainWindow(Worker *worker, 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;
UpdateDcEvent::UpdateStep updateStep() const { return m_updateStep; }
void setUpdateStep(UpdateDcEvent::UpdateStep updateStep) { m_updateStep = updateStep; }
QString targetDcVersion() {return m_targetDcVersion; }
QProgressBar *progressBar();
public slots:
void onAppendText(QString, QString suffix = "");
void onInsertText(QString);
void onReplaceLast(QStringList, QString suffix = "");
void onReplaceLast(QString, QString suffix = "");
void onShowErrorMessage(QString, QString);
void onShowStatusMessage(QString, QString);
void onShowErrorMessage(QStringList);
void onShowStatusMessage(QStringList);
void onStopStartTimer();
void onRestartExitTimer();
void onEnableExit();
void onDisableExit();
void onShowDcDownload(QString);
void onShowJsonDownload(QString);
void onShowTariffUpdate(QString);
void onShowISMASChecks(QString);
void onShowISMASConnectivity(QString);
void onShowUpdateRequest(QString);
void onShowCustRepoStatus(QString);
void onShowExecOpkgStatus(QString);
void onShowExecOpkgCommand(QString);
void onShowExecOpkgOverallResult(QString,bool);
void onShowDownloadDCJsonFilesStatus(QString);
void onShowSyncCustRepoStatus(QString);
void onShowUpdateDCFirmware(QString);
void onSetDcDownloadProgress(int);
void onShowSummary(QString);
void onSummary();
void onFileChanged(QString const&);
#if EMERGENCY_LEAVE_BL==1
void emergencyLeaveBL();
#endif
signals:
#if EMERGENCY_LEAVE_BL==1
void leaveBL();
#endif
private slots:
void onQuit();
private:
int const m_showLineLength = 37;
void scrollDownTextEdit();
void onShowMessage(QString, QString);
Ui::MainWindow *ui;
Worker *m_worker;
int const m_width;
QTimer *m_startTimer;
QTimer *m_exitTimer;
bool m_progressRunning;
//int m_progressValue;
UpdateDcEvent::UpdateStep m_updateStep;
QTimer *m_statusTimer;
QString m_targetDcVersion;
int m_stepLabelChopCount{};
QVector<QString> m_updateSteps{};
QFile m_updateLog;
};
#endif // MAINWINDOW_H

View File

@@ -0,0 +1,133 @@
<?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>Misc Fixed</family>
<pointsize>11</pointsize>
<weight>75</weight>
<bold>true</bold>
</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="2" column="0" colspan="3">
<widget class="QLabel" name="stepLabel">
<property name="minimumSize">
<size>
<width>0</width>
<height>18</height>
</size>
</property>
<property name="font">
<font>
<family>Misc Fixed</family>
<pointsize>11</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
</widget>
</item>
<item row="3" column="0" 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>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="frameShape">
<enum>QFrame::WinPanel</enum>
</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>
<property name="undoRedoEnabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="0" rowspan="2" colspan="3">
<widget class="QLabel" name="updateLabel">
<property name="minimumSize">
<size>
<width>0</width>
<height>100</height>
</size>
</property>
<property name="font">
<font>
<family>Misc Fixed</family>
<pointsize>11</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QProgressBar" name="updateProgress">
<property name="value">
<number>1</number>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QPushButton" name="exit">
<property name="text">
<string>Exit</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,97 @@
#include "message_handler.h"
#include <QDateTime>
#include <cstring>
#include <QString>
#include <QFileInfo>
#include <QMessageLogContext>
static char const *DBG_NAME[] = { "DBG ", "WARN ", "CRIT ", "FATAL", "INFO " };
static bool installedMsgHandler = false;
static int debugLevel = LOG_NOTICE;
int getDebugLevel() { return debugLevel; }
void setDebugLevel(int newDebugLevel) {
debugLevel = newDebugLevel;
}
bool messageHandlerInstalled() {
return installedMsgHandler;
}
QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler) {
installedMsgHandler = (handler != 0);
static QtMessageHandler prevHandler = nullptr;
if (handler) {
prevHandler = qInstallMessageHandler(handler);
return prevHandler;
} else {
return qInstallMessageHandler(prevHandler);
}
}
///
/// \brief Print message according to given debug level.
///
/// \note Install this function using qInstallMsgHandler().
///
/// int main(int argc, char **argv) {
/// installMsgHandler(atbDebugOutput);
/// QApplication app(argc, argv);
/// ...
/// return app.exec();
/// }
///
#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) {
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 LOG_NOTICE: { // normal, but significant, condition
if (type != QtDebugMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
case LOG_WARNING: { // warning conditions
if (type != QtInfoMsg && type != QtDebugMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
case LOG_ERR: { // error conditions
if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
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());
}
}
}
#endif

View File

@@ -0,0 +1,23 @@
#ifndef MESSAGE_HANDLER_H_INCLUDED
#define MESSAGE_HANDLER_H_INCLUDED
#include <QtGlobal>
#ifdef __linux__
#include <syslog.h>
#endif
int getDebugLevel();
void setDebugLevel(int newDebugLevel);
bool messageHandlerInstalled();
QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler);
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
// typedef void (*QtMessageHandler)(QtMsgType, const char *);
void atbDebugOutput(QtMsgType type, const char *msg);
#elif QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
// typedef void (*QtMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &);
void atbDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg);
#endif
#endif // MESSAGE_HANDLER_H_INCLUDED

View File

@@ -0,0 +1,63 @@
#include "process/check_and_fetch_customer_repository_command.h"
#include "worker.h"
#include "utils_internal.h"
CheckAndFetchCustomerRepositoryCommand::CheckAndFetchCustomerRepositoryCommand(
QString const &command, Worker *worker, int nextCommandIndex,
int start_timeout, int finish_timeout)
: UpdateCommand(command, worker, nextCommandIndex, start_timeout, finish_timeout) {
}
void CheckAndFetchCustomerRepositoryCommand::finished(int exitCode, QProcess::ExitStatus exitStatus) {
qCritical() << __func__ << ":" << __LINE__ << command() << exitCode << exitStatus;
Worker *w = worker();
if (w) {
switch (exitCode) {
case internal::GIT_CHECKOUT_ERROR_CODE:
emit w->showCustRepoStatus(internal::GIT_CUSTOMER_REPO_CHECKOUT_ERROR);
break;
case internal::GIT_PULL_ERROR_CODE:
emit w->showCustRepoStatus(internal::GIT_CUSTOMER_REPO_PULL_ERROR);
break;
case internal::GIT_NOT_NECESSARY_CODE:
emit w->showCustRepoStatus(internal::GIT_CUSTOMER_REPO_NOT_NECESSARY);
exitCode = 0;
break;
case internal::GIT_UPDATED_CODE:
emit w->showCustRepoStatus(internal::GIT_CUSTOMER_REPO_UPDATED);
exitCode = 0;
break;
case 0:
emit w->showCustRepoStatus(internal::GIT_CUSTOMER_REPO_UP_TO_DATE);
default:;
}
}
return UpdateCommand::finished(exitCode, exitStatus);
}
void CheckAndFetchCustomerRepositoryCommand::readyReadStandardOutput() {
QProcess *p = (QProcess *)sender();
if (p) {
Worker *w = worker();
if (w) {
QString s = p->readAllStandardOutput().trimmed();
m_commandResult += s;
if (m_commandResult.contains(internal::GIT_CUSTOMER_REPO_NO_UPDATE_NECESSARY)) {
//emit w->showCustRepoStatus(internal::GIT_CUSTOMER_REPO_NOT_NECESSARY);
m_commandResult.clear();
} else
if (m_commandResult.contains(internal::GIT_CUSTOMER_REPO_UPDATED)) {
//emit w->showCustRepoStatus(internal::GIT_CUSTOMER_REPO_UPDATED);
} else
if (m_commandResult.contains(internal::GIT_CUSTOMER_REPO_CLONED)) {
//emit w->showCustRepoStatus(internal::GIT_CUSTOMER_REPO_CLONED);
}
}
}
// static constexpr const char *GIT_CUSTOMER_REPO_UP_TO_DATE{"up to date"};
// emit w->showCustRepoStatus(UpdateCommand::GIT_CUSTOMER_REPO_UP_TO_DATE);
// }
//}
}

View File

@@ -0,0 +1,19 @@
#ifndef CHECK_AND_FETCH_CUSTOMER_REPOSITORY_COMMAND_H_INCLUDED
#define CHECK_AND_FETCH_CUSTOMER_REPOSITORY_COMMAND_H_INCLUDED
#include "update_command.h"
class CheckAndFetchCustomerRepositoryCommand : public UpdateCommand {
public:
explicit CheckAndFetchCustomerRepositoryCommand(QString const &command,
Worker *worker,
int nextCommandIndex,
int start_timeout = 100000,
int finish_timeout = 100000);
QString m_commandResult{};
public slots:
virtual void readyReadStandardOutput() override;
virtual void finished(int exitCode, QProcess::ExitStatus exitStatus) override;
};
#endif // CHECK_AND_FETCH_CUSTOMER_REPOSITORY_COMMAND_H_INCLUDED

View File

@@ -0,0 +1,38 @@
#include "process/check_ismas_connectivity_command.h"
#include "worker.h"
#include <QDebug>
CheckIsmasConnectivityCommand::CheckIsmasConnectivityCommand(QString const &command,
Worker *worker,
int nextCommandIndex,
int start_timeout,
int finish_timeout)
: UpdateCommand(command, worker, nextCommandIndex, start_timeout, finish_timeout) {
}
void CheckIsmasConnectivityCommand::finished(int exitCode, QProcess::ExitStatus exitStatus) {
return UpdateCommand::finished(exitCode, exitStatus);
}
void CheckIsmasConnectivityCommand::readyReadStandardOutput() {
QProcess *p = (QProcess *)sender();
if (p) {
Worker *w = worker();
if (w) {
QString s = p->readAllStandardOutput().trimmed();
if (s == UpdateCommand::ISMAS_CONNECTED) {
emit w->showISMASConnectivity(UpdateCommand::ISMAS_CONNECTED);
} else
if (s == UpdateCommand::NO_CUSTOMER_REPOSITORY) {
emit w->showISMASConnectivity(UpdateCommand::NO_CUSTOMER_REPOSITORY);
} else
if (s == UpdateCommand::ISMAS_NOT_CONNECTED) {
emit w->showISMASConnectivity(UpdateCommand::ISMAS_NOT_CONNECTED);
} else
if (s == UpdateCommand::ISMAS_CONNECTION_IN_PROGRESS) {
emit w->showISMASConnectivity(UpdateCommand::ISMAS_CONNECTION_IN_PROGRESS);
}
}
}
}

View File

@@ -0,0 +1,18 @@
#ifndef CHECK_ISMAS_CONNECTIVITY_COMMAND_H_INCLUDED
#define CHECK_ISMAS_CONNECTIVITY_COMMAND_H_INCLUDED
#include "update_command.h"
class CheckIsmasConnectivityCommand : public UpdateCommand {
public:
explicit CheckIsmasConnectivityCommand(QString const &command,
Worker *worker,
int nextCommandIndex,
int start_timeout = 100000,
int finish_timeout = 100000);
public slots:
virtual void readyReadStandardOutput() override;
virtual void finished(int exitCode, QProcess::ExitStatus exitStatus) override;
};
#endif // CHECK_ISMAS_CONNECTIVITY_COMMAND_H_INCLUDED

View File

@@ -0,0 +1,43 @@
#include "process/check_update_activation_command.h"
#include "worker.h"
#include "utils_internal.h"
#include <QDebug>
CheckUpdateActivationCommand::CheckUpdateActivationCommand(QString const &command,
Worker *worker,
int nextCommandIndex,
int start_timeout,
int finish_timeout)
: UpdateCommand(command, worker, nextCommandIndex, start_timeout, finish_timeout) {
}
void CheckUpdateActivationCommand::finished(int exitCode, QProcess::ExitStatus exitStatus) {
qCritical() << __func__ << ":" << __LINE__ << command() << exitCode << exitStatus;
return UpdateCommand::finished(exitCode, exitStatus);
}
void CheckUpdateActivationCommand::readyReadStandardOutput() {
QProcess *p = (QProcess *)sender();
if (p) {
Worker *w = worker();
if (w) {
QString s = p->readAllStandardOutput().trimmed();
if (s == internal::UPDATE_REQUESTED) {
emit w->showUpdateRequest(internal::UPDATE_REQUESTED);
} else
if (s == internal::UPDATE_NOT_NECESSARY) {
emit w->showUpdateRequest(internal::UPDATE_NOT_NECESSARY);
} else
if (s == internal::UPDATE_NOT_REQUESTED) {
emit w->showUpdateRequest(internal::UPDATE_NOT_REQUESTED);
} else
if (s == internal::NO_CUSTOMER_REPOSITORY) {
emit w->showUpdateRequest(internal::NO_CUSTOMER_REPOSITORY);
} else
if (s == internal::UPDATE_INITIAL) {
emit w->showUpdateRequest(internal::UPDATE_INITIAL);
}
}
}
}

View File

@@ -0,0 +1,18 @@
#ifndef CHECK_UPDATE_ACTIVATION_COMMAND_H_INCLUDED
#define CHECK_UPDATE_ACTIVATION_COMMAND_H_INCLUDED
#include "update_command.h"
class CheckUpdateActivationCommand : public UpdateCommand {
public:
explicit CheckUpdateActivationCommand(QString const &command,
Worker *worker,
int nextCommandIndex,
int start_timeout = 100000,
int finish_timeout = 100000);
public slots:
virtual void readyReadStandardOutput() override;
virtual void finished(int exitCode, QProcess::ExitStatus exitStatus) override;
};
#endif // CHECK_UPDATE_ACTIVATION_COMMAND_H_INCLUDED

View File

@@ -0,0 +1,226 @@
#include "command.h"
#include "worker.h"
#include <QProcess>
#include <QDebug>
#include <QDir>
#include <QRegularExpression>
#include <QDateTime>
#include <QMutexLocker>
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_exitCode(-1)
, m_p(nullptr)
, m_worker(nullptr) {
}
QString Command::getCommandResult(bool reset) const {
QMutexLocker locker(&m_mtx);
if (reset == false) {
return m_commandResult;
}
QString commandResult = m_commandResult;
m_commandResult.clear();
return commandResult;
}
void Command::readyReadStandardOutput() {
QMutexLocker locker(&m_mtx);
QProcess *p = (QProcess *)sender();
if (p) {
QString s = p->readAllStandardOutput();
// qCritical() << __func__ << ":" << __LINE__ << s;
if (m_worker) {
int i = -1;
if ((i = s.indexOf("<DC-VERSION>")) != -1) {
s = s.mid(i+12).trimmed();
if ((i = s.indexOf("\"")) != -1) {
s = s.mid(i+1);
if ((i = s.indexOf("\"")) != -1) {
s = s.mid(0, i).trimmed();
}
}
// emit m_worker->showDcDownload(s);
} else
if ((i = s.indexOf("<DC-PROGRESS>")) != -1) {
bool ok;
int v = s.mid(i+13).trimmed().toInt(&ok);
if (ok) {
emit m_worker->setDcDownloadProgress(v);
emit m_worker->insertText(s.mid(0,i) + "\n");
}
} else
if ((i = s.indexOf("<DC-UPDATE-FINISH>")) != -1) {
emit m_worker->summary();
// qApp->exit(0);
} else
if ((i = s.indexOf("<DC-UPDATE-SUCCESS>")) != -1) {
// TODO
emit m_worker->summary();
} else
if ((i = s.indexOf("<DC-UPDATE-FAILURE>")) != -1) {
emit m_worker->summary();
//qApp->exit(-1);
} else
if ((i = s.indexOf("<JS-PROGRESS>")) != -1) {
bool ok;
int v = s.mid(i+13).trimmed().toInt(&ok);
if (ok) {
emit m_worker->setDcDownloadProgress(v);
emit m_worker->insertText(s.mid(0,i) + "\n");
}
} else {
emit m_worker->insertText(s);
}
}
m_commandResult += s;
}
}
void Command::readyReadStandardError() {
QProcess *p = (QProcess *)sender();
if (p) {
QByteArray buf = p->readAllStandardError();
qCritical() << buf;
}
}
// TODO: nach UpdateCommands ziehen
void Command::finished(int /*exitCode*/, QProcess::ExitStatus /*exitStatus*/) {
QProcess *p = (QProcess *)sender();
// read all remaining data sent to the process, just in case
QString d = p->readAllStandardOutput();
if (!d.isEmpty()) {
QMutexLocker locker(&m_mtx);
m_commandResult += d;
}
disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardOutput()));
disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardError()));
disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(finished(int,QProcess::ExitStatus)));
//if (m_command.contains("ATBDownloadDCJsonFiles")) {
// m_worker->dcUpdate();
//}
}
// TODO: nach UpdateCommand ziehen
bool Command::start(QString workingDirectory, QStringList args) {
if (!QDir::setCurrent(workingDirectory)) {
qCritical() << "SET WORKING_DIRECTORY" << workingDirectory
<< "FAILED FOR" << m_command;
return false;
}
if (m_p != nullptr) {
delete m_p;
}
qCritical() << "COMMAND" << m_command << workingDirectory << args;
m_p = new QProcess(this);
m_p->setWorkingDirectory(workingDirectory);
m_p->setProcessChannelMode(QProcess::MergedChannels);
connect(m_p, SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOutput()));
connect(m_p, SIGNAL(readyReadStandardError()), this, SLOT(readyReadStandardError()));
connect(m_p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(finished(int,QProcess::ExitStatus)));
if (!args.isEmpty()) {
m_p->start(m_command, args);
} else {
m_p->start(m_command);
}
return m_p->waitForStarted(m_waitForStartTimeout);
}
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()));
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);
}
qint64 const start = QDateTime::currentDateTime().toMSecsSinceEpoch();
if (p->waitForStarted(m_waitForStartTimeout)) {
// qDebug() << "PROCESS" << m_command << "STARTED IN" << p->workingDirectory();
if (p->state() == QProcess::ProcessState::Running) {
// 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) {
if ((m_exitCode = p->exitCode()) == 0) {
qint64 const end = QDateTime::currentDateTime().toMSecsSinceEpoch();
qDebug() << "EXECUTED" << m_command
<< QString("(runtime %1ms)").arg(end-start)
<< "with code" << m_exitCode
<< "IN" << p->workingDirectory();
return true;
} else {
qint64 const end = QDateTime::currentDateTime().toMSecsSinceEpoch();
qCritical() << "EXECUTED" << m_command
<< QString("(runtime %1ms)").arg(end-start)
<< "with code" << m_exitCode
<< "IN" << p->workingDirectory();
}
} else {
qint64 const end = QDateTime::currentDateTime().toMSecsSinceEpoch();
qCritical() << "PROCESS" << m_command << "CRASHED with code"
<< p->exitCode()
<< QString("(after %1ms)").arg(end-start)
<< "IN" << p->workingDirectory();
}
} else {
qint64 const end = QDateTime::currentDateTime().toMSecsSinceEpoch();
qCritical() << "PROCESS" << m_command
<< "DID NOT FINISH WITH" << wait
<< "MS IN" << p->workingDirectory()
<< QString("(runtime %1ms)").arg(end-start);
}
} else {
qCritical() << "WRONG PROCESS STATE" << p->state()
<< "IN" << p->workingDirectory();
}
} else {
qint64 const end = QDateTime::currentDateTime().toMSecsSinceEpoch();
qCritical() << "PROCESS" << m_command << "TIMEOUT AT START"
<< QString("(runtime %1ms)").arg(end-start)
<< "IN" << p->workingDirectory();
}
return false;
}

View File

@@ -0,0 +1,55 @@
#ifndef COMMAND_H_INCLUDED
#define COMMAND_H_INCLUDED
#include <QObject>
#include <QCoreApplication>
#include <QString>
#include <QStringList>
#include <QProcess>
#include <QMutex>
class Worker;
class Command : public QObject {
Q_OBJECT
protected:
QString m_command;
mutable QString m_commandResult;
private:
int m_waitForStartTimeout;
int m_waitForFinishTimeout;
int m_exitCode;
protected:
mutable QMutex m_mtx;
private:
QProcess *m_p;
protected:
Worker *m_worker;
public:
explicit Command(QString const &command,
int start_timeout = 100000,
int finish_timeout = 100000);
QString getCommandResult(bool reset = false) const;
QString command() const { return m_command; }
bool execute(QString workingDirectory, QStringList args = QStringList());
bool start(QString workingDirectory, QStringList args = QStringList());
int exitCode() const { return m_exitCode; }
void setWorker(Worker *worker) {m_worker = worker; }
Worker const *worker() const { return m_worker; }
Worker *worker() { return m_worker; }
protected slots:
virtual void readyReadStandardOutput();
virtual void readyReadStandardError();
virtual void finished(int exitCode, QProcess::ExitStatus exitStatus);
};
#endif // COMMAND_H_INCLUDED

View File

@@ -0,0 +1,85 @@
#include "process/exec_opkg_command.h"
#include "worker.h"
#include "utils_internal.h"
#include <QStringList>
ExecOpkgCommand::ExecOpkgCommand(QString const &command,
Worker *worker,
int nextCommandIndex,
bool noaction,
int start_timeout,
int finish_timeout)
: UpdateCommand(command, worker, nextCommandIndex, start_timeout, finish_timeout)
, m_noaction(noaction)
, m_ok_count{0}
, m_fail_count{0} {
}
void ExecOpkgCommand::finished(int exitCode, QProcess::ExitStatus exitStatus) {
QProcess *p = (QProcess *)sender();
if (p) {
Worker *w = worker();
if (w) {
if (m_fail_count == 0 && m_ok_count > 0) {
emit w->showExecOpkgOverallResult(internal::EXEC_OPKG_COMMANDS_SUCCESS, m_noaction);
} else
if (m_ok_count == 0 && m_fail_count > 0) {
emit w->showExecOpkgOverallResult(internal::EXEC_OPKG_COMMANDS_FAIL, m_noaction);
} else {
// TODO
emit w->showExecOpkgOverallResult(internal::EXEC_OPKG_COMMANDS_SUCCESS, m_noaction);
}
}
}
return UpdateCommand::finished(exitCode, exitStatus);
}
void ExecOpkgCommand::readyReadStandardOutput() {
QProcess *p = (QProcess *)sender();
if (p) {
Worker *w = worker();
if (w) {
QString s = p->readAllStandardOutput().trimmed();
if (!s.isEmpty()) {
m_standardOutput += s.replace(QChar('\n'), "");
// the command lines in etc/psa_update/opkg_commands are
// separated by "<OPKG>" markers. Note that the file opkg_commands
// itself is *not* changed, of course.
int startIndex, endIndex{};
while (((startIndex = m_standardOutput.indexOf(internal::OPKG_MARKER)) == 0) &&
((endIndex = m_standardOutput.indexOf(internal::OPKG_MARKER, 1)) != -1)) {
QString result = m_standardOutput.mid(0, endIndex).mid(6);
m_standardOutput = m_standardOutput.mid(endIndex);
if (!s.isEmpty()) {
QStringList const lst = result.split(u' ', QString::SkipEmptyParts);
if (lst.size() >= 2) {
if (lst.last() == "ok") {
m_ok_count += 1;
if (lst.contains("noaction")) {
emit w->showExecOpkgCommand(result);
} else {
emit w->showExecOpkgCommand(result);
}
} else {
m_fail_count += 1;
if (lst.contains("noaction")) {
emit w->showExecOpkgCommand(result);
} else {
emit w->showExecOpkgCommand(result);
}
}
} else {
emit w->showExecOpkgCommand(result);
}
}
}
}
}
}
}

View File

@@ -0,0 +1,24 @@
#ifndef EXEC_OPKG_COMMAND_H_INCLUDED
#define EXEC_OPKG_COMMAND_H_INCLUDED
#include "update_command.h"
class ExecOpkgCommand : public UpdateCommand {
bool m_noaction{false};
QString m_standardOutput{};
int m_ok_count{};
int m_fail_count{};
public:
explicit ExecOpkgCommand(QString const &command,
Worker *worker,
int nextCommandIndex,
bool noaction,
int start_timeout = 100000,
int finish_timeout = 100000);
public slots:
virtual void readyReadStandardOutput() override;
virtual void finished(int exitCode, QProcess::ExitStatus exitStatus) override;
};
#endif // EXEC_OPKG_COMMAND_H_INCLUDED

View File

@@ -0,0 +1,29 @@
#include "process/show_software_status_command.h"
#include "worker.h"
ShowSoftwareStatusCommand::ShowSoftwareStatusCommand(QString const &command,
Worker *worker,
int nextCommandIndex,
int start_timeout,
int finish_timeout)
: UpdateCommand(command, worker, nextCommandIndex, start_timeout, finish_timeout) {
}
void ShowSoftwareStatusCommand::finished(int exitCode, QProcess::ExitStatus exitStatus) {
return UpdateCommand::finished(exitCode, exitStatus);
}
void ShowSoftwareStatusCommand::readyReadStandardOutput() {
//QProcess *p = (QProcess *)sender();
//if (p) {
// QString s = p->readAllStandardOutput();
//
// TODO
// Worker *w = worker();
//if (w) {
// static constexpr const char *EXEC_OPKG_COMMANDS_SUCCESS{"success"};
// emit w->showExecOpkgStatus(UpdateCommand::EXEC_OPKG_COMMANDS_SUCCESS);
// }
//}
}

View File

@@ -0,0 +1,17 @@
#ifndef SHOW_SOFTWARE_STATUS_COMMAND_H_INCLUDED
#define SHOW_SOFTWARE_STATUS_COMMAND_H_INCLUDED
#include "update_command.h"
class ShowSoftwareStatusCommand : public UpdateCommand {
public:
explicit ShowSoftwareStatusCommand(QString const &command,
Worker *worker,
int nextCommandIndex,
int start_timeout = 100000,
int finish_timeout = 100000);
public slots:
virtual void readyReadStandardOutput() override;
virtual void finished(int exitCode, QProcess::ExitStatus exitStatus) override;
};
#endif // SHOW_SOFTWARE_STATUS_COMMAND_H_INCLUDED

View File

@@ -0,0 +1,141 @@
#include "update_command.h"
#include "worker.h"
#include "utils_internal.h"
#include <QDebug>
#include <QProcess>
UpdateCommand::UpdateCommand(QString const &command,
Worker *worker,
int nextCommandIndex,
int start_timeout,
int finish_timeout)
: Command(command, start_timeout, finish_timeout)
, m_nextCommandIndex(nextCommandIndex) {
setWorker(worker);
}
bool UpdateCommand::stopUpdateOnFailure() {
return true;
}
void UpdateCommand::finished(int exitCode, QProcess::ExitStatus exitStatus) {
QProcess *p = qobject_cast<QProcess *>(sender());
if (p) {
// read all remaining data sent to the process, just in case
QString s = p->readAllStandardOutput().trimmed();
if (!s.isEmpty()) {
m_commandResult += s;
}
disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardOutput()));
disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardError()));
disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(finished(int,QProcess::ExitStatus)));
}
if (!m_worker->workList().empty()) {
//static constexpr const int PERCENT_CHECK_ISMAS_CONNECIVITY{10};
//static constexpr const int PERCENT_CHECK_UPDATE_REQUEST{20};
//static constexpr const int PERCENT_CHECK_CUSTOMER_REPOSITORY{30};
//static constexpr const int PERCENT_INSTALL_SW_PACKETS_NOACTION{40};
//static constexpr const int PERCENT_INSTALL_SW_PACKETS{50};
//static constexpr const int PERCENT_INSTALL_DC_CONFIGURATION{60};
//static constexpr const int PERCENT_SYNCHRONIZE_REPO_AND_FILESYS{70};
//static constexpr const int PERCENT_UPDATE_DC{80};
//static constexpr const int PERCENT_SHOW_FINAL_STATUS{90};
if (exitCode == 0 && exitStatus == QProcess::ExitStatus::NormalExit) {
if (m_command.contains("ATBUpdateCheck")) {
// TODO: resultCodes richtig eintragen
if (m_command.contains("--ismas-connected")) {
ISMAS::EventData e(ISMAS::CONTINUE,
internal::PERCENT_CHECK_ISMAS_CONNECIVITY,
0,
"ATBUpdateCheck --ismas-connected",
"ISMAS connected");
m_worker->clientForUpdate().sendCmdEvent(e);
} else
if (m_command.contains("--update-requested")) {
ISMAS::EventData e(ISMAS::CONTINUE,
internal::PERCENT_CHECK_UPDATE_REQUEST,
0,
"ATBUpdateCheck --update-requested",
"update not necessary");
m_worker->clientForUpdate().sendCmdEvent(e);
}
} else
if (m_command.contains("ATBUpdateGit")) {
ISMAS::EventData e(ISMAS::CONTINUE,
internal::PERCENT_CHECK_CUSTOMER_REPOSITORY,
0,
"ATBUpdateGit",
"customer repository clean and up-to-date");
m_worker->clientForUpdate().sendCmdEvent(e);
} else
if (m_command.contains("ATBUpdateOpkg")) {
if (m_command.contains("--noaction")) {
ISMAS::EventData e(ISMAS::CONTINUE,
internal::PERCENT_INSTALL_SW_PACKETS_NOACTION,
0,
"ATBUpdateOpkg --noaction",
"execute opkg-commands (dry-run)");
m_worker->clientForUpdate().sendCmdEvent(e);
} else {
ISMAS::EventData e(ISMAS::CONTINUE,
internal::PERCENT_INSTALL_SW_PACKETS,
0,
"ATBUpdateOpkg",
"execute opkg-commands");
m_worker->clientForUpdate().sendCmdEvent(e);
}
} else
if (m_command.contains("ATBDownloadDCJsonFiles")) {
ISMAS::EventData e(ISMAS::CONTINUE,
internal::PERCENT_INSTALL_DC_CONFIGURATION,
0,
"ATBDownloadDCJsonFiles",
"download json-files to device controller");
m_worker->clientForUpdate().sendCmdEvent(e);
} else
if (m_command.contains("ATBUpdateSync")) {
ISMAS::EventData e(ISMAS::CONTINUE,
internal::PERCENT_SYNCHRONIZE_REPO_AND_FILESYS,
0,
"ATBUpdateSync",
"sync customer repository with /etc");
m_worker->clientForUpdate().sendCmdEvent(e);
} else
if (m_command.contains("ATBUpdateDC")) {
ISMAS::EventData e(ISMAS::CONTINUE,
internal::PERCENT_UPDATE_DC,
0,
"ATBUpdateDC",
"downloaded new dc-firmware. about to reboot...");
m_worker->clientForUpdate().sendCmdEvent(e);
} else
if (m_command.contains("ATBUpdateShow")) {
// TODO
}
if (m_worker->workList().nextExec()) {
m_worker->workList().exec();
} else {
// testing
qCritical() << __func__ << ":" << __LINE__ << "TEST: SEND UPDATE SUCCEEDED TO ISMAS";
m_worker->setLastFailedUpdateStep(Worker::UPDATE_STEP::NONE);
Worker::UpdateProcessRunning _(m_worker);
}
} else {
bool execShowStatus = true;
m_worker->workList().exec(execShowStatus);
}
} else {
Q_ASSERT_X(false,
QString("%1:%2").arg(__func__).arg(__LINE__).toUtf8().constData(),
"empty worker list");
}
}

View File

@@ -0,0 +1,38 @@
#ifndef UPDATE_COMMAND_H_INCLUDED
#define UPDATE_COMMAND_H_INCLUDED
#include "process/command.h"
class Worker;
class UpdateCommand : public Command {
int m_nextCommandIndex{0};
public:
static constexpr const char *UPDATE_NOT_NECESSARY{"not necessary"};
static constexpr const char *UPDATE_NOT_REQUESTED{"not requested"};
static constexpr const char *UPDATE_REQUESTED{"requested"};
static constexpr const char *NO_CUSTOMER_REPOSITORY{"no customer repository"};
static constexpr const char *ISMAS_CONNECTED{"connected"};
static constexpr const char *ISMAS_NOT_CONNECTED{"not connected"};
static constexpr const char *ISMAS_CONNECTION_IN_PROGRESS{"connecting"};
static constexpr const char *GIT_CUSTOMER_REPO_UP_TO_DATE{"up to date"};
static constexpr const char *EXEC_OPKG_COMMANDS_SUCCESS{"success"};
static constexpr const char *UPDATE_DC_JSON_FILES_SUCCESS{"success"};
static constexpr const char *SYNC_CUSTOMER_REPO_FILES_SUCCESS{"success"};
static constexpr const char *UPDATE_DC_FIRMARE_SUCCESS{"success"};
explicit UpdateCommand(QString const &command,
Worker *worker,
int nextCommandIndex,
int start_timeout = 100000,
int finish_timeout = 100000);
int nextCommandIndex() { return m_nextCommandIndex; }
virtual bool stopUpdateOnFailure();
public slots:
virtual void finished(int exitCode, QProcess::ExitStatus exitStatus) override;
};
#endif // UPDATE_COMMAND_H_INCLUDED

View File

@@ -0,0 +1,65 @@
#include "process/update_dc_command.h"
#include "worker.h"
#include "utils_internal.h"
UpdateDCCommand::UpdateDCCommand(QString const &command,
Worker *worker,
int nextCommandIndex,
int start_timeout,
int finish_timeout)
: UpdateCommand(command, worker, nextCommandIndex, start_timeout, finish_timeout) {
}
void UpdateDCCommand::finished(int exitCode, QProcess::ExitStatus exitStatus) {
Worker *w = worker();
if (w) {
w->summaryTimer()->start();
}
return UpdateCommand::finished(exitCode, exitStatus);
}
void UpdateDCCommand::readyReadStandardOutput() {
QProcess *p = (QProcess *)sender();
if (p) {
Worker *w = worker();
if (w) {
QString s = p->readAllStandardOutput();
int i = -1;
if ((i = s.indexOf("<DC-VERSION>")) != -1) {
s = s.mid(i+12).trimmed();
if ((i = s.indexOf("\"")) != -1) {
s = s.mid(i+1);
if ((i = s.indexOf("\"")) != -1) {
s = s.mid(0, i).trimmed();
}
}
// emit w->showDcDownload(s);
} else
if ((i = s.indexOf("<DC-PROGRESS>")) != -1) {
bool ok;
int v = s.mid(i+13).trimmed().toInt(&ok);
if (ok) {
emit w->setDcDownloadProgress(v);
emit w->insertText(s.mid(0,i) + "\n");
}
} else
if ((i = s.indexOf("<DC-UPDATE-FINISH>")) != -1) {
emit w->showUpdateDCFirmware(internal::UPDATE_DC_FIRMARE_SUCCESS);
// QThread::sleep(3);
// emit w->summary();
// qApp->exit(0);
} else
if ((i = s.indexOf("<DC-UPDATE-SUCCESS>")) != -1) {
// TODO
emit w->summary();
} else
if ((i = s.indexOf("<DC-UPDATE-FAILURE>")) != -1) {
emit w->summary();
//qApp->exit(-1);
} else {
emit w->insertText(s);
}
m_commandResult += s;
}
}
}

View File

@@ -0,0 +1,18 @@
#ifndef UPDATE_DC_COMMAND_H_INCLUDED
#define UPDATE_DC_COMMAND_H_INCLUDED
#include "update_command.h"
class UpdateDCCommand : public UpdateCommand {
public:
explicit UpdateDCCommand(QString const &command,
Worker *worker,
int nextCommandIndex,
int start_timeout = 100000,
int finish_timeout = -1);
public slots:
virtual void readyReadStandardOutput() override;
virtual void finished(int exitCode, QProcess::ExitStatus exitStatus) override;
};
#endif // UPDATE_DC_COMMAND_H_INCLUDED

View File

@@ -0,0 +1,28 @@
#include "process/update_filesystem_command.h"
#include "worker.h"
UpdateFileSystemCommand::UpdateFileSystemCommand(QString const &command,
Worker *worker,
int nextCommandIndex,
int start_timeout,
int finish_timeout)
: UpdateCommand(command, worker, nextCommandIndex, start_timeout, finish_timeout) {
}
void UpdateFileSystemCommand::finished(int exitCode, QProcess::ExitStatus exitStatus) {
return UpdateCommand::finished(exitCode, exitStatus);
}
void UpdateFileSystemCommand::readyReadStandardOutput() {
QProcess *p = (QProcess *)sender();
if (p) {
QString s = p->readAllStandardOutput();
// TODO
Worker *w = worker();
if (w) {
// static constexpr const char *SYNC_CUSTOMER_REPO_FILES_SUCCESS{"success"};
emit w->showSyncCustRepoStatus(UpdateCommand::SYNC_CUSTOMER_REPO_FILES_SUCCESS);
}
}
}

View File

@@ -0,0 +1,18 @@
#ifndef UPDATE_FS_COMMAND_H_INCLUDED
#define UPDATE_FS_COMMAND_H_INCLUDED
#include "update_command.h"
class UpdateFileSystemCommand : public UpdateCommand {
public:
explicit UpdateFileSystemCommand(QString const &command,
Worker *worker,
int nextCommandIndex,
int start_timeout = 100000,
int finish_timeout = 100000);
public slots:
virtual void readyReadStandardOutput() override;
virtual void finished(int exitCode, QProcess::ExitStatus exitStatus) override;
};
#endif // UPDATE_FS_COMMAND_H_INCLUDED

View File

@@ -0,0 +1,29 @@
#include "process/update_json_command.h"
#include "worker.h"
#include <QDebug>
UpdateJsonCommand::UpdateJsonCommand(QString const &command,
Worker *worker,
int nextCommandIndex,
int start_timeout,
int finish_timeout)
: UpdateCommand(command, worker, nextCommandIndex, start_timeout, finish_timeout) {
}
void UpdateJsonCommand::finished(int exitCode, QProcess::ExitStatus exitStatus) {
return UpdateCommand::finished(exitCode, exitStatus);
}
void UpdateJsonCommand::readyReadStandardOutput() {
QProcess *p = (QProcess *)sender();
if (p) {
QString s = p->readAllStandardOutput();
// qCritical() << __func__ << ":" << __LINE__ << s;
// static constexpr const char *UPDATE_DC_JSON_FILES_SUCCESS{"success"};
emit m_worker->showDownloadDCJsonFilesStatus(UpdateCommand::UPDATE_DC_JSON_FILES_SUCCESS);
}
}

View File

@@ -0,0 +1,18 @@
#ifndef UPDATE_JSON_COMMAND_H_INCLUDED
#define UPDATE_JSON_COMMAND_H_INCLUDED
#include "process/update_command.h"
class UpdateJsonCommand : public UpdateCommand {
public:
explicit UpdateJsonCommand(QString const &command,
Worker *worker,
int nextCommandIndex,
int start_timeout = 100000,
int finish_timeout = 100000);
public slots:
virtual void readyReadStandardOutput() override;
virtual void finished(int exitCode, QProcess::ExitStatus exitStatus) override;
};
#endif // UPDATE_JSON_HANDLER_H_INCLUDED

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

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

Some files were not shown because too many files have changed in this diff Show More