Compare commits

...

229 Commits

Author SHA1 Message Date
1ea1cdc3e6 set version to 1.5.7 after merge of pu/portrait 2025-02-19 12:08:17 +01:00
348fb15508
Ui: support portrait mode 2025-02-19 11:39:08 +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
68fc83ba67 Add DownloadDCFirmware and DownloadDCJsonFiles 2024-02-29 11:36:24 +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
88 changed files with 10163 additions and 4261 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
tags
*.tags
*.user

View File

@ -1,12 +1,15 @@
[REPOSITORY_URL]
repository-url="https://git.mimbach49.de/GerhardHoffmann"
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="libCAmaster.so"
plugin-name="libCAslave.so"
[FLAGS]
no-psa-hardware-update=false

3
ATBUpdateTool.pro Normal file
View File

@ -0,0 +1,3 @@
TEMPLATE = subdirs
CONFIG += ordered
SUBDIRS = DownloadDCFirmware DownloadDCJsonFiles UpdatePTUDevCtrl

View File

@ -0,0 +1,111 @@
QT += core gui
QT += widgets serialport network
TARGET = ATBDownloadDCFirmware
VERSION="0.1.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}"
!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\\\"
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
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 ) {
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 \
mainwindow.cpp \
../common/src/message_handler.cpp \
../UpdatePTUDevCtrl/commandline_parser.cpp \
update.cpp \
dc_download.cpp \
../common/src/System.cpp
HEADERS += \
mainwindow.h \
../common/include/message_handler.h \
../UpdatePTUDevCtrl/commandline_parser.h \
update.h \
dc_download.h \
../common/include/System.h
OTHER_FILES += \
ATBDownloadDCFirmware.ini \
../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

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

135
DownloadDCFirmware/main.cpp Normal file
View File

@ -0,0 +1,135 @@
#include <QtGlobal>
#include <QCoreApplication>
#include <QApplication>
#include <QByteArray>
#include <QProcess>
#include <QCommandLineParser>
#include <QStandardPaths>
#include <QSettings>
#include <QDir>
#include <QDebug>
#include <QThread>
#include "message_handler.h"
#include "commandline_parser.h"
#include "utils.h"
#include "update.h"
#include "mainwindow.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) {
QByteArray const value = qgetenv("LC_ALL");
if (value != "C") {
qputenv("LC_ALL", "C");
}
// qputenv("XDG_RUNTIME_DIR", "/var/run/user/0");
openlog("ATB-UPDATE-DC-FIRMWARE", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER);
QApplication a(argc, argv);
QApplication::setApplicationName("ATBDownloadDCFirmware");
QApplication::setApplicationVersion(APP_VERSION);
if (!messageHandlerInstalled()) { // change internal qt-QDebug-handling
atbInstallMessageHandler(atbDebugOutput);
setDebugLevel(LOG_NOTICE);
}
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 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 = 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;
if (showExtendedVersion) {
printf(APP_EXTENDED_VERSION"\n");
return 0;
}
QString const &customerRepo = QDir::cleanPath(workingDir + QDir::separator() + QString("customer_%1").arg(customerNr));
QStringList filesToUpdate;
QThread::currentThread()->setObjectName("main thread");
qInfo() << "Main thread" << QThread::currentThreadId();
MainWindow mw;
mw.setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
// mw.showFullScreen();
// qCritical() << "SHOW";
// mw.show();
return 0;
// return a.exec();
}

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,58 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTimer>
#include <QStatusBar>
#include <QWidget>
#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

@ -29,21 +29,7 @@
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="2">
<widget class="QPushButton" name="exit">
<property name="text">
<string>Exit</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QProgressBar" name="updateProgress">
<property name="value">
<number>1</number>
</property>
</widget>
</item>
<item row="0" column="0" rowspan="3" colspan="3">
<item row="0" column="0" rowspan="3" colspan="2">
<widget class="QTextEdit" name="updateStatus">
<property name="enabled">
<bool>true</bool>
@ -52,6 +38,7 @@
<font>
<family>Misc Fixed</family>
<pointsize>11</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>

View File

View File

View File

View File

View File

@ -0,0 +1,438 @@
#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>
#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(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) {
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();
}
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");
}
}
m_hw->dc_autoRequest(false);
qDebug() << "SET AUTO-REQUEST=FALSE";
QThread::sleep(1); // make sure the auto-request flag is acknowledged
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);
}

148
DownloadDCFirmware/update.h Normal file
View File

@ -0,0 +1,148 @@
#ifndef UPDATE_H_INCLUDED
#define UPDATE_H_INCLUDED
#include <QObject>
#include <QString>
#include <QFile>
#include <QDir>
#include <QByteArray>
#include <QProcess>
#include <QPluginLoader>
#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;
public:
enum class DownloadResult {OK, ERROR, TIMEOUT, NOP};
enum class FileTypeJson {CONFIG=1, DEVICE=2, CASH=3, SERIAL=4, TIME=5, PRINTER=6};
static hwinf *loadDCPlugin(QDir const &plugInDir, QString const &fn);
static bool unloadDCPlugin();
static QStringList split(QString line, QChar sep = ',');
explicit Update(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 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

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

@ -0,0 +1,103 @@
QT += core
QT += serialport network
TARGET = ATBDownloadDCJsonFiles
VERSION="0.1.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}"
!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\\\"
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
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 ) {
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 \
../UpdatePTUDevCtrl/message_handler.cpp \
../UpdatePTUDevCtrl/commandline_parser.cpp \
update.cpp \
../common/src/System.cpp
HEADERS += \
../UpdatePTUDevCtrl/message_handler.h \
../UpdatePTUDevCtrl/commandline_parser.h \
update.h \
../common/include/System.h
OTHER_FILES += \
ATBDownloadDCJsonFiles.ini \
../ATBUpdateTool.ini
##########################################################################################
# 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,445 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 5.0.2, 2024-02-28T16:01:22. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{6a494cc5-6dea-4681-86fc-d47b9761a1f4}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="int">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="int" 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">GenericLinuxOsType</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Yocto i.MX6-ATB-PTU5</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Yocto i.MX6-ATB-PTU5</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{804f60e1-6e88-41af-b072-9f5c6a606099}</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" 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-DownloadDCJsonFiles-Yocto_i_MX6_ATB_PTU5-Debug</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/opt/ptu5/opt/build-DownloadDCJsonFiles-Yocto_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="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments">CONFIG+=PTU5</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>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand">/usr/bin/make</value>
</valuemap>
<value type="int" 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>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand">/usr/bin/make</value>
</valuemap>
<value type="int" 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"/>
<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-DownloadDCJsonFiles-Yocto_i_MX6_ATB_PTU5-Release</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/opt/ptu5/opt/build-DownloadDCJsonFiles-Yocto_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="int" 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="int" 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"/>
<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-DownloadDCJsonFiles-Yocto_i_MX6_ATB_PTU5-Profile</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/opt/ptu5/opt/build-DownloadDCJsonFiles-Yocto_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="int" 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="int" 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"/>
<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="int" 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>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</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="int" 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>
<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.DisplayName">DownloadDCJsonFiles</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">RemoteLinuxRunConfiguration:/opt/ptu5/opt/DownloadDCJsonFiles/DownloadDCJsonFiles.pro</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/opt/ptu5/opt/DownloadDCJsonFiles/DownloadDCJsonFiles.pro</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="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.1</variable>
<valuemap type="QVariantMap">
<value type="QString" key="DeviceType">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop Qt 5.12.12 GCC 64bit</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop Qt 5.12.12 GCC 64bit</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">qt.qt5.51212.gcc_64_kit</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" 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-DownloadDCJsonFiles-Desktop_Qt_5_12_12_GCC_64bit-Debug</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/opt/ptu5/opt/build-DownloadDCJsonFiles-Desktop_Qt_5_12_12_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="int" 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="int" 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"/>
<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-DownloadDCJsonFiles-Desktop_Qt_5_12_12_GCC_64bit-Release</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/opt/ptu5/opt/build-DownloadDCJsonFiles-Desktop_Qt_5_12_12_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="int" 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="int" 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"/>
<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-DownloadDCJsonFiles-Desktop_Qt_5_12_12_GCC_64bit-Profile</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/opt/ptu5/opt/build-DownloadDCJsonFiles-Desktop_Qt_5_12_12_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="int" 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="int" 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"/>
<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="int" key="ProjectExplorer.Target.BuildConfigurationCount">3</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="int" 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="int" 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>
<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>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">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

View File

@ -0,0 +1,158 @@
#include <QtGlobal>
#include <QCoreApplication>
#include <QByteArray>
#include <QProcess>
#include <QCommandLineParser>
#include <QStandardPaths>
#include <QSettings>
#include <QDir>
#include <QDebug>
#include "message_handler.h"
#include "commandline_parser.h"
#include "utils.h"
#include "update.h"
#include "System.h"
#include <DeviceController/interfaces.h>
//#include <unistd.h>
//#include <errno.h>
#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) {
QByteArray const value = qgetenv("LC_ALL");
if (value != "C") {
qputenv("LC_ALL", "C");
}
// qputenv("XDG_RUNTIME_DIR", "/var/run/user/0");
openlog("ATB-DL-JSON", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER);
QCoreApplication a(argc, argv);
QCoreApplication::setApplicationName("ATBDownloadDCJsonFiles");
QCoreApplication::setApplicationVersion(APP_VERSION);
if (!messageHandlerInstalled()) { // change internal qt-QDebug-handling
atbInstallMessageHandler(atbDebugOutput);
setDebugLevel(LOG_NOTICE);
}
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 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 = 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;
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 {
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() << "JSON FILES TO UPDATE" << filesToUpdate;
Update update(customerRepo,
QString::number(customerNr),
branchName,
plugInDir,
plugInName,
workingDir);
update.doUpdate(filesToUpdate, mountPath.has_value());
update.checkJsonVersions();
//update.checkJsonVersions(filesToUpdate);
if (mountPath.has_value()) {
System::umountUSBStick();
}
return 0;
}

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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,36 @@
#ifndef COMMAND_H_INCLUDED
#define COMMAND_H_INCLUDED
#include <QObject>
#include <QCoreApplication>
#include <QString>
#include <QStringList>
#include <QProcess>
class Command : public QObject {
Q_OBJECT
QString m_command;
QString m_commandResult;
int m_waitForStartTimeout;
int m_waitForFinishTimeout;
int m_exitCode;
public:
explicit Command(QString const &command,
int start_timeout = 100000,
int finish_timeout = 100000);
QString getCommandResult() const;
QString command() const { return m_command; }
bool execute(QString workingDirectory, QStringList args = QStringList());
int exitCode() const { return m_exitCode; }
private slots:
void readyReadStandardOutput();
void readyReadStandardError();
void finished(int exitCode, QProcess::ExitStatus exitStatus);
};
#endif // COMMAND_H_INCLUDED

View File

@ -0,0 +1,434 @@
#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>
#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(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) {
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();
}
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,148 @@
#ifndef UPDATE_H_INCLUDED
#define UPDATE_H_INCLUDED
#include <QObject>
#include <QString>
#include <QFile>
#include <QDir>
#include <QByteArray>
#include <QProcess>
#include <QPluginLoader>
#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;
public:
enum class DownloadResult {OK, ERROR, TIMEOUT, NOP};
enum class FileTypeJson {CONFIG=1, DEVICE=2, CASH=3, SERIAL=4, TIME=5, PRINTER=6};
static hwinf *loadDCPlugin(QDir const &plugInDir, QString const &fn);
static bool unloadDCPlugin();
static QStringList split(QString line, QChar sep = ',');
explicit Update(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 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

View File

@ -91,8 +91,21 @@ DEFINES += QT_DEPRECATED_WARNINGS
# 1.3._24_: Special version for szeged using a old dc-controller (4.42):
# Changes:
# (1) the ini-File now uses the libCAmaster.so.
VERSION="1.3._24_"
# 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
@ -106,7 +119,6 @@ VERSION="1.3._24_"
# ISMAS eine entsprechende Meldung anzeigen als Teil von SEND-LAST-VERSION.
# Wenn der WAIT-button aktiv ist, dann werden zumindest die opkg-commands
# ausgefuehrt.
# 4: rsync: immer alle Dateien soiegeln (bis auf opkg-commands)
# 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.
@ -122,9 +134,6 @@ VERSION="1.3._24_"
# 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.
# 10: Bei einer Neuinstallation (Neuhauser) immer JSON files runterladen,
# Tariff-Files syncen (d.h. nur wenn noch kein Repo vorhanden ist), und
# zwar auch ohne WAIT-Button.
# 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
@ -178,7 +187,7 @@ DEFINES+=APP_EXTENDED_VERSION=\\\"$$EXTENDED_VERSION\\\"
# keep comments, as /* fall through */
QMAKE_CXXFLAGS += -C
QMAKE_CXXFLAGS += -g
QMAKE_CXXFLAGS += -Wno-deprecated-copy
QMAKE_CXXFLAGS += -Wno-deprecated-copy -O
contains( CONFIG, PTU5 ) {
greaterThan(QT_MAJOR_VERSION, 4): QT += serialport
@ -238,6 +247,7 @@ HEADERS += \
process/command.h \
message_handler.h \
worker.h \
interfaces.h \
commandline_parser.h \
plugins/interfaces.h

View File

@ -0,0 +1,314 @@
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.
# 1.5.7 : Add support for dynamic portrait / landscape.
VERSION="1.5.7"
# 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
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 \
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 \
commandline_parser.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

@ -60,6 +60,16 @@ CommandLineParser::CommandLineParser()
QStringList() << "working-directory" << "working-directory",
QCoreApplication::translate("main", "working directory of update-script."),
QCoreApplication::translate("main", "directory")))
, m_psaConfigDirectoryOption(
QCommandLineOption(
QStringList() << "psa-config-directory" << "psa-config-directory",
QCoreApplication::translate("main", "config directory of json-files sent to dc."),
QCoreApplication::translate("main", "directory")))
, m_psaTariffDirectoryOption(
QCommandLineOption(
QStringList() << "psa-tariff-directory" << "psa-tariff-directory",
QCoreApplication::translate("main", "tariff directory of tariff-json-files."),
QCoreApplication::translate("main", "directory")))
, m_dryRunOption(
QCommandLineOption(
QStringList() << "d" << "dry-run",
@ -111,6 +121,12 @@ void CommandLineParser::configure() {
m_workingDirectoryOption.setDefaultValue("/opt/app/tools/atbupdate/");
m_parser.addOption(m_workingDirectoryOption);
m_psaConfigDirectoryOption.setDefaultValue("etc/psa_config/");
m_parser.addOption(m_psaConfigDirectoryOption);
m_psaTariffDirectoryOption.setDefaultValue("etc/psa_tariff/");
m_parser.addOption(m_psaTariffDirectoryOption);
m_dryRunOption.setDefaultValue("false");
m_parser.addOption(m_dryRunOption);
@ -128,12 +144,20 @@ void CommandLineParser::readSettings() {
QString const iniFileDir = m_parser.value(m_iniFileDirectoryOption);
QString const iniFileName = m_parser.value(m_iniFileNameOption);
m_iniFileName = QDir::cleanPath(iniFileDir + QDir::separator() + iniFileName);
qCritical() << __PRETTY_FUNCTION__ << " iniFileDir" << iniFileDir;
qCritical() << __PRETTY_FUNCTION__ << "iniFileName" << m_iniFileName;
if (!m_iniFileName.isEmpty()) {
if (QFile(m_iniFileName).exists()) {
QSettings settings(m_iniFileName, QSettings::IniFormat);
QStringList keys = settings.allKeys();
for (QString const &key: keys) {
QVariant v = settings.value(key);
qCritical() << __PRETTY_FUNCTION__
<< key << " -> " << v.toString();
if (key.contains("repository-url")) {
m_repositoryUrl = v.toString();
} else
@ -143,6 +167,12 @@ void CommandLineParser::readSettings() {
if (key.contains("working-directory")) {
m_workingDir = v.toString();
} else
if (key.contains("psa-config-directory")) {
m_psaConfigDir = v.toString();
} else
if (key.contains("psa-tariff-directory")) {
m_psaTariffDir = v.toString();
} else
if (key.contains("dry-run")) {
m_dryRun = (v.toBool() ? "true" : "false");
} else
@ -166,8 +196,14 @@ void CommandLineParser::readSettings() {
} else
if (key.contains("plugin-name")) {
m_plugInName = v.toString();
} else {
qCritical() << __PRETTY_FUNCTION__
<< key << " -> (UNKNOWN) " << v.toString();
}
}
} else {
qCritical() << __PRETTY_FUNCTION__ << "iniFileName" << m_iniFileName
<< "DOES NOT EXIST";
}
}
}
@ -193,6 +229,20 @@ QString CommandLineParser::plugInName() {
return m_plugInName;
}
QString CommandLineParser::psaConfigDir() {
if (m_parser.isSet(m_psaConfigDirectoryOption)) {
m_psaConfigDir = m_parser.value(m_psaConfigDirectoryOption);
}
return m_psaConfigDir;
}
QString CommandLineParser::psaTariffDir() {
if (m_parser.isSet(m_psaTariffDirectoryOption)) {
m_psaTariffDir = m_parser.value(m_psaTariffDirectoryOption);
}
return m_psaTariffDir;
}
QString CommandLineParser::workingDir() {
if (m_parser.isSet(m_workingDirectoryOption)) {
m_workingDir = m_parser.value(m_workingDirectoryOption);
@ -238,7 +288,9 @@ bool CommandLineParser::extendedVersion() {
bool CommandLineParser::alwaysDownloadConfig() {
if (m_parser.isSet(m_alwaysDownloadConfigOption)) {
m_alwaysDownloadConfig = m_parser.value(m_alwaysDownloadConfigOption);
qCritical() << "m_alwaysDownloadConfigOption IS SET" << m_alwaysDownloadConfig;
}
qCritical() << "m_alwaysDownloadConfig" << m_alwaysDownloadConfig;
return m_alwaysDownloadConfig == "false" ? false : true;
}

View File

@ -11,6 +11,8 @@ class CommandLineParser : public QCommandLineParser {
QString m_plugInDir;
QString m_plugInName;
QString m_workingDir;
QString m_psaConfigDir;
QString m_psaTariffDir;
QString m_dryRun;
QString m_noUpdatePsaHardware;
QString m_showYoctoVersion;
@ -29,6 +31,8 @@ class CommandLineParser : public QCommandLineParser {
QCommandLineOption m_alwaysDownloadConfigOption;
QCommandLineOption m_alwaysDownloadDCOption;
QCommandLineOption m_workingDirectoryOption;
QCommandLineOption m_psaConfigDirectoryOption;
QCommandLineOption m_psaTariffDirectoryOption;
QCommandLineOption m_dryRunOption;
QCommandLineOption m_extendedVersionOption;
QCommandLineOption m_yoctoVersionOption;
@ -52,6 +56,8 @@ public:
QString plugInDir();
QString plugInName();
QString workingDir();
QString psaConfigDir();
QString psaTariffDir();
bool dryRun();
bool noUpdatePsaHardware();
bool yoctoVersion();

Binary file not shown.

View File

@ -6,16 +6,18 @@
#include <QRegularExpression>
#include <QDebug>
#include <QDir>
#include <QStringList>
GitClient::GitClient(QString const &customerNrStr,
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(QString("https://git.mimbach49.de/GerhardHoffmann/%1.git").arg(customerNrStr))
, m_repositoryPath(customerRepositoryPath)
, m_customerNr(customerNrStr)
, m_workingDirectory(workingDirectory)
, m_branchName(branchName)
@ -192,7 +194,7 @@ QStringList GitClient::gitShowReason(QString branchName) {
int const m = s.indexOf("m=");
int const d = s.indexOf("d=");
QString msg = IsmasClient::getReasonForLastSendVersion();
QString msg{""}; // IsmasClient::getReasonForLastSendVersion();
QString commit{""}, date{""};
if (c != -1) {
@ -204,9 +206,9 @@ QStringList GitClient::gitShowReason(QString branchName) {
start = m + 2;
if (d >= start) {
length = d - start;
msg += " (";
// msg = " (";
msg = s.mid(start, length).trimmed();
msg += ")";
// msg += ")";
start = d + 2;
date = s.mid(start);
@ -290,6 +292,100 @@ bool GitClient::gitFsck() {
}
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
*/
@ -297,6 +393,45 @@ 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();
@ -316,6 +451,31 @@ std::optional<QString> GitClient::gitPull() {
// 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)) {

View File

@ -22,7 +22,8 @@ class GitClient : public QObject {
bool copyGitConfigFromMaster();
public:
explicit GitClient(QString const &customerNrStr,
explicit GitClient(QString const &customerRepositoryUrl,
QString const &customerNrStr,
QString const &repositoryPath,
QString const &workingDirectory = QCoreApplication::applicationDirPath(),
QString const &branchName = "master",
@ -57,6 +58,10 @@ class GitClient : public QObject {
static QString gitBlob(QString fileName);
QString gitCommitForBlob(QString blob);
bool gitIsFileTracked(QString file2name);
bool branchExistsRemotely();
bool branchExistsLocally();
bool gitPullNewBranches();
};
#endif // GIT_CLIENT_H_INCLUDED

View File

@ -342,8 +342,11 @@ IsmasClient::sendRequestReceiveResponse(int port, QString const &request) {
return std::nullopt;
}
} else {
printDebugMessage(port, clientIP, clientPort,
QString("PARSE ERROR ") + response + " " + parseError.errorString());
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;
}
@ -360,7 +363,19 @@ QString IsmasClient::updateNewsToIsmas(char const *event,
char buf[1024];
memset(buf, 0, sizeof(buf));
QString const ts = QDateTime::currentDateTime().toString(Qt::ISODateWithMs);
QDateTime const local(QDateTime::currentDateTime());
QDateTime utc(local);
utc.setTimeSpec(Qt::UTC);
int const diff = (int)local.secsTo(utc); // diff between UTC and local time
QTime t(0, 0, 0);
t = t.addSecs((uint)diff);
QString const st(QString("%1%2").arg(diff < 0 ? "-" : "+").arg(t.toString("hh:mm")));
QString const ts(local.toString(Qt::ISODateWithMs) + st);
snprintf(buf, sizeof(buf)-1,
"{"
"\"REASON\":\"SW_UP\","
@ -384,7 +399,7 @@ QString IsmasClient::errorBackendNotConnected(QString const &info,
QString const &version) {
return updateNewsToIsmas("U0003",
m_progressInPercent,
RESULT_CODE::INSTALL_ERROR,
RESULT_CODE::ISMAS_NO_CONNECTION_ERROR,
"CHECK BACKEND CONNECTIVITY",
info.toStdString().c_str(),
version.toStdString().c_str());
@ -394,7 +409,7 @@ QString IsmasClient::errorGitClone(QString const &info,
QString const &version) {
return updateNewsToIsmas("U0003",
m_progressInPercent,
RESULT_CODE::INSTALL_ERROR,
RESULT_CODE::GIT_CLONE_ERROR,
"CLONE CUSTOMER REPOSITORY FAILED",
info.toStdString().c_str(),
version.toStdString().c_str());
@ -442,7 +457,7 @@ QString IsmasClient::updateTriggerSet(QString const &info, QString const &versio
QString IsmasClient::errorUpdateTrigger(QString const &info, QString const &version) {
return updateNewsToIsmas("U0003",
m_progressInPercent,
RESULT_CODE::INSTALL_ERROR,
RESULT_CODE::ISMAS_TRIGGER_ERROR,
"CHECK UPDATE TRIGGER",
info.toStdString().c_str(),
version.toStdString().c_str());
@ -675,7 +690,8 @@ QString IsmasClient::updateOfPSASendVersion(PSAInstalled const &psa) {
"\"libTCP_ZVT_CCPlugin.so\" : {"
"\"VERSION\" : \"%s\""
"}"
"}"
"},"
"\"PTU-PACKAGE-VERSIONS\" : %s"
"}",
psa.versionInfo.reason.toStdString().c_str(),
psa.versionInfo.created.toStdString().c_str(),
@ -786,9 +802,11 @@ QString IsmasClient::updateOfPSASendVersion(PSAInstalled const &psa) {
psa.pluginVersion.mobilisisCalculatePriceConfigUi.toStdString().c_str(),
psa.pluginVersion.prmCalculatePrice.toStdString().c_str(),
psa.pluginVersion.prmCalculatePriceConfigUi.toStdString().c_str(),
psa.pluginVersion.tcpZVT.toStdString().c_str());
psa.pluginVersion.tcpZVT.toStdString().c_str(),
qInfo() << buf;
psa.ptuPackageVersion.toStdString().c_str());
qInfo() << buf;
return buf;
}

View File

@ -77,6 +77,8 @@ struct PSAInstalled {
DC2C print[32];
QString ptuPackageVersion;
explicit PSAInstalled() {
tariff.name = "N/A";
tariff.version = "N/A";
@ -145,13 +147,47 @@ public:
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,
BACKUP_FAILED=2,
WRONG_PACKAGE=3,
INSTALL_ERROR=4};
// 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,

View File

@ -9,7 +9,7 @@
#endif
#include "message_handler.h"
#include "plugins/interfaces.h"
#include <DeviceController/interfaces.h>
#include "commandline_parser.h"
#include <unistd.h>
@ -50,9 +50,11 @@
// argv[1]: file to send to dc
int main(int argc, char *argv[]) {
QByteArray const value = qgetenv("LC_ALL");
if (value != "C") {
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);
@ -62,7 +64,8 @@ int main(int argc, char *argv[]) {
QApplication::setApplicationVersion(APP_VERSION);
if (!messageHandlerInstalled()) { // change internal qt-QDebug-handling
atbInstallMessageHandler(atbDebugOutput);
atbInstallMessageHandler(nullptr);
//atbInstallMessageHandler(atbDebugOutput);
setDebugLevel(LOG_NOTICE);
}
@ -71,6 +74,38 @@ int main(int argc, char *argv[]) {
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()) {
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();
@ -93,6 +128,9 @@ int main(int argc, char *argv[]) {
qInfo() << "pwd ......................" << rtPath;
qInfo() << "repositoryUrl ............" << repositoryUrl;
if (!gitSSHCommand.isEmpty()) {
qInfo() << "GIT_SSH_COMMAND .........." << gitSSHCommand;
}
qInfo() << "plugInDir ................" << plugInDir;
qInfo() << "plugInName ..............." << plugInName;
qInfo() << "workingDir ..............." << workingDir;
@ -130,7 +168,9 @@ int main(int argc, char *argv[]) {
if (!QDir(plugInDir).exists()) {
qCritical() << plugInDir
<< "does not exists, but has to contain dc-library";
exit(-1);
if (noUpdatePsaHardware == false) {
exit(-1);
}
}
// before loading the library, delete all possible shared memory segments

View File

@ -4,7 +4,7 @@
#include "utils.h"
#include "progress_event.h"
#include "update_dc_event.h"
#include "plugins/interfaces.h"
#include <DeviceController/interfaces.h>
#include <QDateTime>
#include <QMessageBox>
@ -12,6 +12,9 @@
#include <QScrollBar>
#include <QEvent>
#include <QScreen>
MainWindow::MainWindow(Worker *worker, QWidget *parent)
: QMainWindow(parent)
@ -30,6 +33,7 @@ MainWindow::MainWindow(Worker *worker, QWidget *parent)
this->statusBar()->setFont(f);
ui->setupUi(this);
checkOrientation();
ui->updateProgress->setRange(0, 100);
ui->updateProgress->reset();
@ -59,6 +63,24 @@ MainWindow::MainWindow(Worker *worker, QWidget *parent)
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(ui->exit, SIGNAL(clicked()),this,SLOT(onQuit()));
connect(m_worker, SIGNAL(disableExit()),this,SLOT(onDisableExit()));
connect(m_worker, SIGNAL(enableExit()),this,SLOT(onEnableExit()));
@ -76,9 +98,60 @@ MainWindow::MainWindow(Worker *worker, QWidget *parent)
MainWindow::~MainWindow() {
delete m_startTimer;
delete m_exitTimer;
delete m_statusTimer;
delete ui;
}
// ----------------------------- Ui::LAYOUT setting -------------------------------------
void MainWindow::checkOrientation()
{
QScreen *screen = QGuiApplication::primaryScreen();
Qt::ScreenOrientation orientation = screen->orientation();
switch (orientation) {
case Qt::PrimaryOrientation:
this->setLandscapeLayout();
break;
case Qt::LandscapeOrientation:
this->setLandscapeLayout();
break;
case Qt::PortraitOrientation:
this->setPortraitLayout();
break;
case Qt::InvertedLandscapeOrientation:
this->setLandscapeLayout();
break;
case Qt::InvertedPortraitOrientation:
this->setPortraitLayout();
break;
}
this->currentOrientation = orientation;
}
void MainWindow::setPortraitLayout()
{
// Adjust layout for portrait mode (480x800)
this->setFixedSize(480, 800);
ui->centralwidget->setFixedSize(480, 800);
}
void MainWindow::setLandscapeLayout()
{
// Adjust layout for landscape mode (800x480)
this->setFixedSize(800, 480);
ui->centralwidget->setFixedSize(800, 480);
}
void MainWindow::customEvent(QEvent *event) {
if (event->type() == ProgressEvent::type()) {
ProgressEvent *pevent = (ProgressEvent *)event;

View File

@ -66,6 +66,12 @@ private:
void onShowMessage(QString, QString);
Ui::MainWindow *ui;
void checkOrientation();
void setPortraitLayout();
void setLandscapeLayout();
Qt::ScreenOrientation currentOrientation;
Worker *m_worker;
int const m_width;
QTimer *m_startTimer;
@ -73,5 +79,6 @@ private:
bool m_progressRunning;
//int m_progressValue;
UpdateDcEvent::UpdateStep m_updateStep;
QTimer *m_statusTimer;
};
#endif // MAINWINDOW_H

View File

@ -0,0 +1,73 @@
<?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="minimumSize">
<size>
<width>480</width>
<height>480</height>
</size>
</property>
<property name="font">
<font>
<family>Source Code Pro</family>
</font>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="2">
<widget class="QPushButton" name="exit">
<property name="text">
<string>Exit</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QProgressBar" name="updateProgress">
<property name="value">
<number>1</number>
</property>
</widget>
</item>
<item row="0" column="0" rowspan="3" colspan="3">
<widget class="QTextEdit" name="updateStatus">
<property name="enabled">
<bool>true</bool>
</property>
<property name="font">
<font>
<family>Misc Fixed</family>
<pointsize>11</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAsNeeded</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAsNeeded</enum>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</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,124 @@
#include "command.h"
#include <QProcess>
#include <QDebug>
#include <QDir>
#include <QRegularExpression>
#include <QDateTime>
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) {
}
QString Command::getCommandResult() const {
return m_commandResult;
}
void Command::readyReadStandardOutput() {
QProcess *p = (QProcess *)sender();
m_commandResult += p->readAllStandardOutput();
// qCritical() << m_commandResult;
}
void Command::readyReadStandardError() {
QProcess *p = (QProcess *)sender();
QByteArray buf = p->readAllStandardError();
qCritical() << buf;
}
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()) {
m_commandResult += d;
}
disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardOutput()));
disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardError()));
}
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;
}

1023
UpdatePTUDevCtrl/update.cpp Normal file

File diff suppressed because it is too large Load Diff

143
UpdatePTUDevCtrl/update.h Normal file
View File

@ -0,0 +1,143 @@
#ifndef UPDATE_H_INCLUDED
#define UPDATE_H_INCLUDED
#include <QObject>
#include <QString>
#include <QFile>
#include <QDir>
#include <QByteArray>
#include <QProcess>
#include <QPluginLoader>
#include <QMap>
#include <DeviceController/interfaces.h>
#ifdef PTU5
#define SERIAL_PORT "ttymxc2"
#else
#define SERIAL_PORT "ttyUSB0"
#endif
class Worker;
class Update : public QObject {
Q_OBJECT
hwinf *m_hw = nullptr;
Worker *m_worker = 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;
public:
enum class DownloadResult {OK, ERROR, TIMEOUT, NOP};
enum class FileTypeJson {CONFIG=1, DEVICE=2, CASH=3, SERIAL=4, TIME=5, PRINTER=6};
static hwinf *loadDCPlugin(QDir const &plugInDir, QString const &fn);
static bool unloadDCPlugin();
static QStringList split(QString line, QChar sep = ',');
explicit Update(Worker *worker,
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(int &displayIndex, QStringList const &linesToWorkOn);
bool checkDownloadedJsonVersions(QStringList const& jsonFileNames);
QMap<QString, QString>
getInstalledJsonVersions(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")})));
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 updatePrinterTemplate(int templateIdx, QString fname) const;
bool updateBinary(QString const &fileToSendToDC);
bool updateConfig(QString jsFileToSendToDC);
bool updateCashConf(QString jsFileToSendToDC);
bool updateDeviceConf(QString jsFileToSendToDC);
bool downloadJson(enum FileTypeJson type, int templateIdx,
QString jsFileToSendToDC) const;
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

@ -30,20 +30,99 @@ int Utils::read1stLineOfFile(QString fileName) {
return -1;
}
QString Utils::zoneName(quint8 i) {
static constexpr char const *zName[] = {
"",
"purple",
"blue",
"yellow",
"green",
"yellow (mars)",
"green (mars)"
};
if (i < (sizeof(zName)/sizeof(char const *))) {
return zName[i];
QString Utils::getLocation(QString fileName) {
QString location("N/A");
QFile f(fileName);
if (f.exists()) {
if (f.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&f);
in.setCodec("UTF-8");
while(!in.atEnd()) {
QString const &line = in.readLine();
if (line.indexOf("Project", Qt::CaseInsensitive) != -1) {
int const c = line.indexOf(":");
if (c != -1) {
location = line.mid(c+1);
if (!location.isEmpty()) {
location = location.replace(QChar(','), QString(""));
return location.replace(QChar('"'), QString("")).trimmed();
}
}
}
}
}
}
return "N/A";
return location;
}
QString Utils::getTariffVersion(QString fileName) {
QString version("N/A");
QFile f(fileName);
if (f.exists()) {
if (f.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&f);
in.setCodec("UTF-8");
while(!in.atEnd()) {
QString const &line = in.readLine();
if (line.indexOf("Version", Qt::CaseInsensitive) != -1) {
int const c = line.indexOf(":");
if (c != -1) {
version = line.mid(c+1);
if (!version.isEmpty()) {
version = version.replace(QChar(','), QString(""));
return version.replace(QChar('"'), QString("")).trimmed();
}
}
}
}
}
}
return version;
}
QString Utils::getTariffInfo(QString fileName) {
QString info("N/A");
QFile f(fileName);
if (f.exists()) {
if (f.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&f);
in.setCodec("UTF-8");
while(!in.atEnd()) {
QString const &line = in.readLine();
if (line.indexOf("Info", Qt::CaseInsensitive) != -1) {
int const c = line.indexOf(":");
if (c != -1) {
info = line.mid(c+1);
if (!info.isEmpty()) {
info = info.replace(QChar(','), QString(""));
return info.replace(QChar('"'), QString("")).trimmed();
}
}
}
}
}
}
return info;
}
QString Utils::zoneName(quint8 i) {
//static constexpr char const *zName[] = {
// "",
// "purple",
// "blue",
// "yellow",
// "green",
// "yellow (mars)",
// "green (mars)"
//};
//if (i < (sizeof(zName)/sizeof(char const *))) {
// return zName[i];
//}
return "---";
}
void Utils::printCriticalErrorMsg(QString const &errorMsg, bool upper, bool lower) {
@ -201,21 +280,24 @@ bool Utils::sameFilesInDirs(QDir const &dir1, QDir const &dir2,
fileNameLst2 << i2.next().fileName();
}
QString dirPath1 = dir1.absolutePath();
QString dirPath2 = dir2.absolutePath();
if (fileNameLst1.isEmpty()) {
qCritical() << "DIR1" << dir1.dirName() << " DOES NOT CONTAIN EXPECTED FILES";
qCritical() << "DIR1" << dirPath1 << " DOES NOT CONTAIN EXPECTED FILES";
return false;
}
if (fileNameLst2.isEmpty()) {
qCritical() << "DIR1" << dir2.dirName() << " DOES NOT CONTAIN EXPECTED FILES";
qCritical() << "DIR1" << dirPath2 << " DOES NOT CONTAIN EXPECTED FILES";
return false;
}
if (fileNameLst1 != fileNameLst2) {
printCriticalErrorMsg(dir1.dirName() + " AND " + dir2.dirName()
+ " DIFFER: [" + fileNameLst1.join(',') + "],["
printCriticalErrorMsg(dirPath1 + " AND " + dirPath2
+ " HAVE DIFFERENT FILES: [" + fileNameLst1.join(',') + "],["
+ fileNameLst2.join(',') + "]");
return false;
} else {
printInfoMsg(dir1.dirName() + " AND " + dir2.dirName()
printInfoMsg(dirPath1 + " AND " + dirPath2
+ " ARE EQUAL: [" + fileNameLst1.join(',') + "]");
}
@ -231,21 +313,21 @@ bool Utils::sameFilesInDirs(QDir const &dir1, QDir const &dir2,
}
if (gitBlobLst1.isEmpty()) {
qCritical() << "DIR1" << dir1.dirName() << " DOES NOT CONTAIN EXPECTED FILES";
qCritical() << "DIR1" << dirPath1 << " DOES NOT CONTAIN EXPECTED FILES";
return false;
}
if (gitBlobLst2.isEmpty()) {
qCritical() << "DIR1" << dir2.dirName() << " DOES NOT CONTAIN EXPECTED FILES";
qCritical() << "DIR1" << dirPath2 << " DOES NOT CONTAIN EXPECTED FILES";
return false;
}
if (gitBlobLst1 != gitBlobLst2) {
printCriticalErrorMsg(dir1.dirName() + " AND " + dir2.dirName()
+ " DIFFER: [" + gitBlobLst1.join(',') + "],["
printCriticalErrorMsg(dirPath1 + " AND " + dirPath2
+ " HAVE DIFFERENT BLOBS: [" + gitBlobLst1.join(',') + "],["
+ gitBlobLst2.join(',') + "]");
return false;
} else {
printInfoMsg(dir1.dirName() + " AND " + dir2.dirName()
printInfoMsg(dirPath1 + " AND " + dirPath2
+ " CONTAIN SAME GIT-BLOBS FOR FILES: [" + fileNameLst1.join(',') + "]");
}

View File

@ -12,6 +12,9 @@
namespace Utils {
int read1stLineOfFile(QString fileName);
QString getLocation(QString fileName);
QString getTariffVersion(QString fileName);
QString getTariffInfo(QString fileName);
QString zoneName(quint8 i);
void printCriticalErrorMsg(QString const &errorMsg, bool upper=false, bool lower=false);
void printCriticalErrorMsg(QStringList const &errorMsg);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

60
common/include/System.h Normal file
View File

@ -0,0 +1,60 @@
#ifndef SYSTEM_H
#define SYSTEM_H
#include <QObject>
#include <QStringList>
#include <optional>
#include <initializer_list>
class System : public QObject
{
Q_OBJECT
private:
explicit System(QObject *parent = 0);
static QString errorMsg;
public:
static const QStringList allowedJsonFiles;
static bool checkForSDCard();
static bool umountSDCard();
static std::optional<QString> checkForUSBStick(QString const &dirPathUnderMountPath = ".");
static QString getUSBMountPath(QString const &dirPathUnderMountPath = ".");
//static QString getUSBDeviceName();
static bool umountUSBStick();
static bool test_mount(const QString& dev, const QString &mountpoint);
static bool test_dir(const QString& dir);
static quint32 getFreeDiskSpace(const QString& dev);
static quint32 getFreeDiskSpaceSDCard();
static bool isSDCardWritable();
static quint32 getFreeDiskSpaceUSBStick();
static bool isUSBStickWritable();
static quint8 createLogFileBackup(const QString & targzfile, const QString &filelistfile = "");
static QString getSDCardMountPath();
static QString getSDCardDeviceName();
static QString getUniqueDeviceId();
static QString getErrorMessage();
static QString readStringFromFile(const QString & filename);
static int readIntFromFile(const QString & filename);
static QString getPTU4SerialNumber();
static QString getPTU4MACAddress();
static QStringList getJsonFilesOnUsbStick(QString const &mountPath);
};
#endif // SYSTEM_H

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

711
common/src/System.cpp Normal file
View File

@ -0,0 +1,711 @@
#include "System.h"
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QTextStream>
#include <QProcess>
#include <QRegExp>
#include <QStringList>
#include <QDebug>
#include <QDirIterator>
//#include "version.h"
const QStringList System::allowedJsonFiles =
std::initializer_list<QString>{
QString("DC2C_conf.json"),
QString("DC2C_cash.json"),
QString("DC2C_device.json"),
QString("DC2C_print01.json"),
QString("DC2C_print02.json"),
QString("DC2C_print03.json"),
QString("DC2C_print04.json"),
QString("DC2C_print05.json"),
QString("DC2C_print06.json"),
QString("DC2C_print07.json"),
QString("DC2C_print08.json"),
QString("DC2C_print09.json"),
QString("DC2C_print10.json"),
QString("DC2C_print11.json"),
QString("DC2C_print12.json"),
QString("DC2C_print13.json"),
QString("DC2C_print14.json"),
QString("DC2C_print15.json"),
QString("DC2C_print16.json"),
QString("DC2C_print17.json"),
QString("DC2C_print18.json"),
QString("DC2C_print19.json"),
QString("DC2C_print20.json"),
QString("DC2C_print21.json"),
QString("DC2C_print22.json"),
QString("DC2C_print23.json"),
QString("DC2C_print24.json"),
QString("DC2C_print25.json"),
QString("DC2C_print26.json"),
QString("DC2C_print27.json"),
QString("DC2C_print28.json"),
QString("DC2C_print29.json"),
QString("DC2C_print30.json"),
QString("DC2C_print31.json"),
QString("DC2C_print32.json")
};
QString System::errorMsg = "";
System::System(QObject *parent) :
QObject(parent)
{
}
/********************************************************************************
* static function to return error message.
*
*/
QString System::getErrorMessage()
{
return errorMsg;
}
/********************************************************************************
* static function to check if a writable SD-card is in the cardslot.
*
* This function only checks a certain path or a mount entry. It does not mount
* a SD-card, this must be provided by the OS (automounter).
*
* returns 1 if a SD-card is available, 0 otherwise.
*/
bool System::checkForSDCard()
{
#if defined (ARCH_DesktopLinux)
// DEBUG / TEST:
return true;
#endif
if (!test_mount(getSDCardDeviceName(), getSDCardMountPath())) {
qDebug() << "check for SD-card failed: \n"
<< " device name is: " << getSDCardDeviceName()
<< " mount path is: " << getSDCardMountPath();
return false;
}
return true;
}
/********************************************************************************
* static function for umount sd-card.
*
*/
bool System::umountSDCard()
{
#if defined (ARCH_DesktopLinux)
// DEBUG / TEST:
return true;
#endif
QProcess process;
process.setProcessChannelMode(QProcess::MergedChannels);
QString commandString = "umount " + getSDCardMountPath();
process.start(commandString);
if (!process.waitForStarted()) {
errorMsg = "System::umountSDCard(): ERROR: waitForStarted()";
return false;
}
if (!process.waitForFinished(600000)) {
errorMsg = "System::umountSDCard(): ERROR: " + process.errorString();
qDebug() << errorMsg;
return false;
}
else {
QByteArray bytes = process.readAll();
QStringList lines = QString(bytes).split("\n");
foreach (QString line, lines) {
qDebug() << "System::umountSDCard() line: " << line;
}
}
return true;
}
/********************************************************************************
* static function for umount usb-stick.
*
*/
bool System::umountUSBStick()
{
#if defined (ARCH_DesktopLinux)
// DEBUG / TEST:
return true;
#endif
QProcess process;
process.setProcessChannelMode(QProcess::MergedChannels);
QString commandString = "umount " + getUSBMountPath();
process.start(commandString);
if (!process.waitForStarted()) {
errorMsg = "System::umountUSBStick(): ERROR: waitForStarted()";
return false;
}
if (!process.waitForFinished(600000)) {
errorMsg = "System::umountUSBStick(): ERROR: " + process.errorString();
qDebug() << errorMsg;
return false;
}
else {
QByteArray bytes = process.readAll();
QStringList lines = QString(bytes).split("\n");
foreach (QString line, lines) {
qDebug() << "System::umountUSBStick() line: " << line;
}
}
return true;
}
/*****************************************************************************
* test, if a certain device is already mounted
*
* e.g. a mounted SD-card on PTU4:
* - device is '/dev/mmcblk0p1'
* - mount path is '/media/mmcblk0p1'
*
* e.g. a mounted USB-Stick on PTU4:
* - device is '/dev/sda'
* - mount path is '/media/sda'
*/
bool System::test_mount(const QString& dev, const QString& mountpoint)
{
bool mounted = false;
QProcess process;
process.setProcessChannelMode(QProcess::MergedChannels);
qDebug() << "System::test_mount() dev = " << dev;
qDebug() << "System::test_mount() mountpoint = " << mountpoint;
//normalize mountpoint: (mountpoint is without trailing '/')
QString tmp_mountpoint = QString(mountpoint).remove(QRegExp("/*$"));
QString commandString = "mount";
process.start(commandString);
if (!process.waitForStarted()) {
errorMsg = "System::test_mount(): ERROR: waitForStarted()";
return false;
}
if (!process.waitForFinished(600000)) {
errorMsg = "System::test_mount(): ERROR: " + process.errorString();
qDebug() << errorMsg;
return false;
}
else {
QByteArray bytes = process.readAll();
QStringList lines = QString(bytes).split("\n");
foreach (QString line, lines) {
//qDebug() << "System::test_mount() line: " << line;
//if (line.contains(dev)) qDebug() << "System::test_mount() line contains dev";
//if (line.contains(tmp_mountpoint)) qDebug() << "System::test_mount() line contains mountpoint";
if (line.contains(dev) && line.contains(tmp_mountpoint)) {
qDebug() << "System::test_mount(): " << dev << " is mounted on " << tmp_mountpoint;
mounted = true;
}
}
}
return mounted;
}
/*****************************************************************************
* test, if a certain directory is existing and readable
*/
bool System::test_dir(const QString& dir)
{
QFileInfo fileInfo(dir);
if (! fileInfo.isDir() ) { return false; }
return true;
}
std::optional<QString> System::checkForUSBStick(QString const &dirPathUnderMountPath) {
#if defined (ARCH_DesktopLinux)
// DEBUG / TEST:
if (QFileInfo(getUSBMountPath()).isDir())
return true;
else
return false;
#endif
QString const &mountPath = getUSBMountPath(dirPathUnderMountPath);
// qCritical() << "MOUNT-PATH" << mountPath;
return mountPath.isEmpty() ? std::nullopt : std::optional<QString>(mountPath);
}
/**
* @brief SupportSystem::getUSBMountPath
* @return path where a USB storage device is mounted.
*
* Note, do not return an empty string ("") here because a calling method (which could not be
* identified till now) is relying on this!
*/
QString System::getUSBMountPath(QString const &dirPathUnderMountPath) {
#if defined (ARCH_DesktopLinux)
// DEBUG / TEST:
return QDir::homePath().append("/APconfigTest/USB");
#endif
QProcess process;
process.setProcessChannelMode(QProcess::MergedChannels);
QStringList mountLine;
qDebug() << "System::getUSBMountPath()";
QRegExp devRegExp = QRegExp("dev/sd*", Qt::CaseSensitive, QRegExp::WildcardUnix);
QRegExp mountRegExp = QRegExp("media/sd*", Qt::CaseSensitive, QRegExp::WildcardUnix);
QString commandString = "mount";
process.start(commandString);
if (!process.waitForStarted()) {
errorMsg = "System::getUSBMountPath(): ERROR: waitForStarted()";
return "";
}
if (!process.waitForFinished(600000)) {
errorMsg = "System::getUSBMountPath(): ERROR: " + process.errorString();
qDebug() << errorMsg;
return "";
}
else {
QByteArray bytes = process.readAll();
QStringList lines = QString(bytes).split("\n");
foreach (QString line, lines) {
qDebug() << "System::getUSBMountPath() line: " << line;
if (line.contains(devRegExp) && line.contains(mountRegExp)) {
qDebug() << " -> this line is a usb storage device mount" << line;
mountLine = line.split(' ');
if (mountLine.size() > 3) {
qCritical() << "System::getUSBMountPath(): " << mountLine.at(0) << " is mounted on " << mountLine.at(2);
QDir d(QDir::cleanPath(mountLine.at(2) + QDir::separator() + dirPathUnderMountPath));
if (d.exists()) {
return mountLine.at(2);
} else {
qCritical() << "directory" << d.absolutePath() << "does not exist";
}
}
}
}
}
qDebug() << "System::getUSBMountPath() no mounted usb device found!";
return "";
}
QStringList System::getJsonFilesOnUsbStick(QString const &mountPath) {
QStringList jsonFiles;
// /media/sda2/etc/psa_config
QString const &dirPath = QDir::cleanPath(mountPath + QDir::separator() + "etc" + QDir::separator() + "psa_config");
QDir d(dirPath);
if (d.exists()) {
QDirIterator it(dirPath, QStringList() << "*.json", QDir::Files, QDirIterator::NoIteratorFlags);
while (it.hasNext()) {
QString const &jsonFile = it.next();
QFileInfo fi(jsonFile);
if (System::allowedJsonFiles.contains(fi.fileName())) {
jsonFiles << jsonFile;
} else {
qCritical() << "Warning:" << fi.fileName() << "unknown";
}
}
}
return jsonFiles;
}
/********************************************************************************
* static function to check if a mounted sd-card is writable.
*
* Note: the caller must ensure (e.g. by calling checkForSDCard()) that a
* sd-card is mounted.
*
*/
bool System::isSDCardWritable()
{
QFileInfo fi(getSDCardMountPath());
if(fi.isDir() && fi.isWritable()) {
return true;
}
else
{
return false;
}
}
/********************************************************************************
* static function to check if a mounted usb-stick is writable.
*
* Note: the caller must ensure (e.g. by calling checkForUSBStick()) that a
* usb-stick is mounted.
*
*/
bool System::isUSBStickWritable()
{
QFileInfo fi(getUSBMountPath());
if(fi.isDir() && fi.isWritable()) {
return true;
}
else
{
return false;
}
}
/********************************************************************************
* wrapper function for getting free space on a sd-card on ptu
*
*/
quint32 System::getFreeDiskSpaceSDCard()
{
#if defined (ARCH_DesktopLinux)
return getFreeDiskSpace("/home/siegert/server1home");
#endif
return getFreeDiskSpace(getSDCardMountPath());
}
/********************************************************************************
* wrapper function for getting free space on a usb storage device on ptu
*
*/
quint32 System::getFreeDiskSpaceUSBStick()
{
#if defined (ARCH_DesktopLinux)
return getFreeDiskSpace("/home/siegert/server1home");
#endif
return getFreeDiskSpace(getUSBMountPath());
}
/********************************************************************************
* static function to read free disk space
*
* example output of 'df' command on ptu:
* # df
* Filesystem 1K-blocks Used Available Use% Mounted on
* ubi0:rootfs 111704 78940 32764 71% /
* devtmpfs 62072 0 62072 0% /dev
* tmpfs 62332 0 62332 0% /dev/shm
* tmpfs 62332 416 61916 1% /tmp
* tmpfs 62332 24 62308 1% /run
* ubi0:data 222260 5892 216368 3% /opt
* tmpfs 62332 0 62332 0% /media
* /dev/mmcblk0p1 3878912 87360 3791552 3% /media/mmcblk0p1
*
* in this case, if we want to get the free space on the sd-card the methode should
* be called:
* getFreeDiskSpace("/dev/mmcblk0p1")
* the result should be:
* 3791552
*
*/
quint32 System::getFreeDiskSpace(const QString& dev)
{
quint32 availableSpace = 0;
QProcess process;
process.setProcessChannelMode(QProcess::MergedChannels);
//qDebug() << "System::getFreeDiskSpace() dev = " << dev;
QString commandString = "df";
process.start(commandString);
if (!process.waitForStarted()) {
errorMsg = "System::getFreeDiskSpace(): ERROR: waitForStarted()";
return availableSpace;
}
if (!process.waitForFinished(600000)) {
errorMsg = "System::getFreeDiskSpace(): ERROR: " + process.errorString();
qDebug() << errorMsg;
return availableSpace;
}
else {
QByteArray bytes = process.readAll();
QStringList lines = QString(bytes).split("\n");
foreach (QString line, lines) {
//qDebug() << "System::getFreeDiskSpace() line: " << line;
if (line.contains(dev)) {
//qDebug() << "System::getFreeDiskSpace() line contains dev";
QStringList processResultLine = line.split(' ', QString::SkipEmptyParts);
if (processResultLine.size() >= 4) {
bool ok;
availableSpace = processResultLine.at(3).toLong(&ok);
if (!ok) {
return 0;
}
qDebug() << "System::getFreeDiskSpace() availableSpace = " << availableSpace;
}
else {
return availableSpace;
}
}
}
}
return availableSpace;
}
/********************************************************************************
* static function to create a backup of logfiles
*
* 'targzfile' is an absolute filename for ar tar.gz file wich will be created
* 'filelistfile' is an absolute filename of a file containing all names of files
* which should be backed up.
*
* returns:
* 1 on success
* 0 on failure
*
*/
quint8 System::createLogFileBackup(const QString & targzfile, const QString &filelistfile)
{
QProcess process;
process.setProcessChannelMode(QProcess::MergedChannels);
qDebug() << "System::createLogFileBackup() targzfile = " << targzfile;
qDebug() << "System::createLogFileBackup() filelistfile = " << filelistfile;
QString commandString = "tar hczvf " + targzfile + " -T " + filelistfile;
qDebug() << "System::createLogFileBackup() tar command:" << commandString;
process.start(commandString);
if (!process.waitForStarted()) {
errorMsg = "System::createLogFileBackup(): ERROR: waitForStarted()";
return 1;
}
if (!process.waitForFinished(600000)) {
errorMsg = "System::createLogFileBackup(): ERROR: " + process.errorString();
qDebug() << errorMsg;
return 1;
}
else {
// DEBUG
//QByteArray bytes = process.readAll();
//QStringList lines = QString(bytes).split("\n");
//foreach (QString line, lines) {
// qDebug() << "System::createLogFileBackup() line: " << line;
//}
}
QString resultMessage;
QProcess::ExitStatus exitStatus = process.exitStatus();
int exitCode = process.exitCode();
if (exitStatus == QProcess::CrashExit) {
resultMessage = "local backup program crashed";
qCritical() << resultMessage;
return 1;
}
else
/**
* note for exit codes of process tar:
* see: http://www.gnu.org/software/tar/manual/html_section/tar_19.html#Synopsis
*
* tar returns exitCode 2 if some files in logFileList could not be read (e.g. if they are not existing)
* however, tar-archive is created although!
* This means success for our needs!
*/
if ( (exitCode != 0) && (exitCode != 2) && (exitStatus == QProcess::NormalExit) ) {
resultMessage = "Local Backup file \"" + targzfile + "\" with exit code = " + QString::number(exitCode);
qCritical() << resultMessage;
return 1;
}
else {
resultMessage = "Local Backup file \"" + targzfile + "\" was created successfully.";
qCritical() << resultMessage;
return 0;
}
return 0;
}
/*****************************************************************************
* get SD-card mount path.
*/
QString System::getSDCardMountPath()
{
#if defined (ARCH_PTU4)
return "/media/mmcblk0p1";
#elif defined (ARCH_PTU5)
return "/media/mmcblk0p1";
#elif defined (ARCH_DesktopLinux)
// DEBUG / TEST:
return "/home/siegert/APconfigTest";
#endif
return "";
}
/*****************************************************************************
* get SD-card device name.
*/
QString System::getSDCardDeviceName()
{
#if defined (ARCH_PTU4)
return "/dev/mmcblk0p1";
#elif defined (ARCH_PTU5)
return "/dev/mmcblk0p1";
#elif defined (ARCH_DesktopLinux)
return "/dev/sdf1";
#endif
return "";
}
/********************************************************************************
* static function to build and return a unique device id.
* This id consists currently only of the mac-address of network device eth0.
*/
QString System::getUniqueDeviceId()
{
QString filename = "/sys/class/net/eth0/address";
QString result;
QFile file(filename);
if(!file.open(QIODevice::ReadOnly)) {
qCritical() << "System::getUniqueDeviceId() could not open file: " << filename;
result = "0";
}
else {
QTextStream in(&file);
result = in.readLine(20);
}
file.close();
result.remove(':');
return result;
}
QString System::readStringFromFile(const QString & filename)
{
#if defined (ARCH_PTU4)
QFileInfo fileinfo(filename);
#else
QFileInfo fileinfo(QDir::homePath() + filename);
#endif
if (! fileinfo.isReadable() ) {
qDebug() << "System::readStringFromFile(): \"" << filename << "\" is not readable";
return "";
}
#if defined (ARCH_PTU4)
QFile file(filename);
#else
QFile file(QDir::homePath() + filename);
#endif
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qDebug() << "System::readStringFromFile() cannot open file: " << filename;
return "";
}
QTextStream in(&file);
QString stringValue = in.readLine(100);
qDebug() << "System::readStringFromFile() stringValue = " << stringValue;
file.close();
return stringValue;
}
int System::readIntFromFile(const QString & filename)
{
bool ok;
int result = (int)readStringFromFile(filename).toInt(&ok);
if (!ok) {
result = 0;
}
return result;
}
/****************************************************************************
* read ptu internal data
*
*/
QString System::getPTU4SerialNumber()
{
return readStringFromFile("/sys/bus/i2c/devices/0-0050/SNO");
}
QString System::getPTU4MACAddress()
{
return readStringFromFile("/sys/bus/i2c/devices/0-0050/MAC");
}

97
common/src/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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

View File

@ -1,709 +0,0 @@
#include "update.h"
#include "worker.h"
#include "utils.h"
#include "update_dc_event.h"
#include "mainwindow.h"
#include <QCoreApplication>
#include <QApplication>
#include <QFile>
#include <QTemporaryFile>
#include <QDebug>
#include <QTextStream>
#include <QRegularExpression>
#include <QRegExp>
#include <QApplication>
#if defined (Q_OS_UNIX) || defined (Q_OS_LINUX)
#include "unistd.h"
#endif
#include "plugins/interfaces.h"
#include <QSharedMemory>
#include <QScopedPointer>
#include <QDir>
#include <QThread>
#include <QDateTime>
#include <QPluginLoader>
#include <QMap>
#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(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();
exit(-1);
}
qCritical() << "loadDCPlugin() plugin directory:" << plugInDir.absolutePath();
qCritical() << "loadDCPlugin() plugin file name:" << pluginLoader.fileName();
if (!pluginLoader.isLoaded()) {
qCritical() << pluginLoader.errorString();
exit(-2);
}
QObject *plugin = pluginLoader.instance();
if (!plugin) {
qCritical() << "cannot start instance";
exit(-3);
}
if (! (hw = qobject_cast<hwinf *>(plugin))) {
qCritical() << "cannot cast plugin" << plugin << "to hwinf";
exit(-4);
}
} else {
qCritical() << pluginLibName << "does not exist";
exit(-5);
}
} else {
qCritical() << "plugins directory" << plugInDir.absolutePath()
<< "does not exist";
exit(-6);
}
return hw;
}
bool Update::unloadDCPlugin() {
if (pluginLoader.unload()) {
qCritical() << "unloaded plugin" << pluginLoader.fileName();
// Note: will re-instantiate the library !
// QObject *rootObject = pluginLoader.instance();
// if (rootObject) {
// qCritical() << "reloaded plugin: root object again available";
// return false;
// }
// qCritical()unloaded plugin: root object gone";
return true;
}
return false;
}
Update::Update(Worker *worker,
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_worker(worker)
, 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) {
//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() << "UPDATE: m_sys_areDCDataValid ..." << m_sys_areDCdataValid;
//qInfo() << "UPDATE: m_serialInterface ..." << m_serialInterface;
//qInfo() << "UPDATE: m_baudrate ..." << m_baudrate;
//qInfo() << "UPDATE: m_customerRepository ..." << m_customerRepository;
//qInfo() << "UPDATE: m_customerNr ..........." << m_customerNrStr;
//qInfo() << "UPDATE: m_branchName ..........." << m_branchName;
//qInfo() << "UPDATE: m_pluginName ..........." << m_pluginName;
//qInfo() << "UPDATE: m_workingDirectory ....." << m_workingDir;
}
Update::~Update() {
}
// br is a index into a table, used for historical reasons.
bool Update::openSerial(int br, QString baudrate, QString comPort) const {
qDebug() << "opening serial" << br << baudrate << comPort << "...";
if (m_hw->dc_openSerial(br, baudrate, comPort, 1) == true) { // 1 for connect
Utils::printInfoMsg(
QString("OPENING SERIAL %1").arg(br)
+ " " + baudrate + " " + comPort + "...OK");
// m_hw->dc_autoRequest(true);
m_hw->dc_autoRequest(false);
QThread::sleep(1);
Utils::printInfoMsg(QString("IS PORT OPEN %1").arg(m_hw->dc_isPortOpen()));
return true;
}
Utils::printCriticalErrorMsg(
QString("OPENING SERIAL %1").arg(br)
+ " " + baudrate + " " + comPort + "...FAILED");
return false;
}
void Update::closeSerial() const {
qInfo() << "CLOSED SERIAL" << m_baudrate << m_serialInterface;
m_hw->dc_closeSerial();
}
bool Update::isSerialOpen() const {
return m_hw->dc_isPortOpen();
}
/*
///////////////////////////////////////////////////////////////////////////////
//
// 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.
*/
bool Update::updateBinary(QString const &fileToSendToDC) {
qInfo() << "UPDATING DEVICE CONTROLLER FIRMWARE BINARY" << fileToSendToDC;
return false;
#if 0
QFile fn(fileToSendToDC);
if (!fn.exists()) {
// output via CONSOLE() etc
return false;
}
bool bl_isUp = false;
if (m_hw->bl_completeStart()) {
int cnt = 5;
while (--cnt > 0) {
if (m_hw->bl_isUp()) {
bl_isUp = true;
break;
}
}
}
if (!bl_isUp) {
return false;
}
if (!m_hw->bl_storeFirmware(fileToSendToDC)) {
m_hw->bl_stopBL();
return false;
}
uint16_t const nrOfFirmwareBlocks = m_hw->bl_getNrOfFirmwareBlocks();
for (uint16_t blockNr = 0; blockNr <= nrOfFirmwareBlocks; ++blockNr) {
m_hw->bl_blockAutoLoad(blockNr);
int sleepTime = 0;
while (1) {
if (sleepTime > 1500) {
m_hw->bl_stopBL();
return false;
}
int8_t const r = m_hw->bl_blockAutoResponse();
// after every "bl_blockAutoLoad()" call this until response
// retval 0: wait 1: OK, blk was sent 2: OK, transfer complete
// 3: error despite repeating, cancel. probably bin file corrupted
// Max duration: 3x no response from BL = 900ms
switch(r) {
case 1:
/* fall through */
case 2:
sleepTime = 0;
break;
case 0: {
QThread::msleep(100);
sleepTime += 100;
} break;
case 3:
m_hw->bl_stopBL();
return false;
default:
m_hw->bl_stopBL();
return false; // unknown error code
}
}
m_hw->bl_stopBL();
}
return true;
#endif
}
QString Update::jsonType(enum FileTypeJson type) {
switch (type) {
case FileTypeJson::CASH: return "CASH";
case FileTypeJson::CONFIG: return "CONFIG";
case FileTypeJson::PRINTER: return "PRINTER";
case FileTypeJson::SERIAL: return "SERIAL";
case FileTypeJson::DEVICE: return "DEVICE";
case FileTypeJson::TIME: return "TIME";
}
return "N/A";
}
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
bool ready = false;
int nTry = 25;
while ((ready = m_hw->sys_ready4sending()) == false) {
QThread::msleep(200);
if (--nTry <= 0) {
Utils::printCriticalErrorMsg("SYS NOT READY FOR SENDING AFTER 5 SECONDS");
break;
}
}
bool ret = false;
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() <= 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 {
Utils::printCriticalErrorMsg(
QString("SIZE OF %1 TOO BIG (%2 BYTES)")
.arg(jsFileToSendToDC).arg(fi.size()));
}
} else {
Utils::printCriticalErrorMsg(
QString("CAN NOT OPEN ") + jsFileToSendToDC + " FOR READING");
}
} else {
Utils::printCriticalErrorMsg(
QString(jsFileToSendToDC) + " DOES NOT EXIST");
}
}
m_hw->dc_autoRequest(false);
qDebug() << "SET AUTO-REQUEST=FALSE";
QThread::sleep(1); // make sure the auto-request flag is acknowledged
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);
}
QStringList Update::split(QString line, QChar sep) {
QStringList lst;
QString next;
int start = 0, end;
while ((end = line.indexOf(sep, start)) != -1) {
next = line.mid(start, end - start).trimmed();
lst << next;
start = end + 1;
}
next = line.mid(start, end - start).trimmed();
lst << next;
return lst;
}
void Update::readyReadStandardOutput() {
QProcess *p = (QProcess *)sender();
QByteArray buf = p->readAllStandardOutput();
qCritical() << buf;
}
void Update::readyReadStandardError() {
QProcess *p = (QProcess *)sender();
QByteArray buf = p->readAllStandardError();
qCritical() << buf;
}
void Update::finished(int /*exitCode*/, QProcess::ExitStatus /*exitStatus*/) {
QProcess *p = (QProcess *)sender();
disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardOutput()));
disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardError()));
}
QStringList Update::getDcSoftAndHardWareVersion() {
m_hw->dc_autoRequest(true);
QThread::sleep(1); // make sure the timer-slots are active
for (int i=0; i < 3; ++i) { // send explicit reuests to get
// current SW/HW-versions
m_hw->request_DC2_SWversion();
m_hw->request_DC2_HWversion();
QThread::sleep(1);
}
QString const &hwVersion = m_hw->dc_getHWversion().toLower().trimmed();
QString const &swVersion = m_hw->dc_getSWversion().toLower().trimmed();
m_hw->dc_autoRequest(false);
QThread::sleep(1); // make sure the timer-slots are inactive
if (!hwVersion.isEmpty() && !swVersion.isEmpty()) {
return QStringList() << hwVersion << swVersion;
}
return QStringList() << "DC HW-version not available"
<< "DC SW-version not available";
}
QString Update::getFileVersion(QString const& jsonFileName) {
// "version":"15.10.2023 14:55 02.00.06",
static const QRegularExpression re("^.*(\\\"version\\\":)(.*)$");
QString fileVersion;
QFile inputFile(jsonFileName);
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) {
fileVersion = match.captured(match.lastCapturedIndex());
break;
}
}
inputFile.close();
}
return fileVersion;
}
bool Update::checkDownloadedJsonVersions(QStringList const& jsonFileNames) {
for (QStringList::size_type i=0; i < jsonFileNames.size(); ++i) {
uint8_t jsonNr = 0;
QFileInfo fInfo(jsonFileNames[i]);
if (fInfo.fileName().endsWith("conf.json")) {
jsonNr = 1;
} else
if (fInfo.fileName().endsWith("device.json")) {
jsonNr = 2;
} else
if (fInfo.fileName().endsWith("cash.json")) {
jsonNr = 3;
} else {
QRegularExpressionMatch match;
static const QRegularExpression re("^(.*print)([0-3][0-9])\\.json\\s*$");
int idx = fInfo.fileName().indexOf(re, 0, &match);
if (idx != -1) {
QString captured = match.captured(match.lastCapturedIndex());
bool ok = false;
int n = captured.toInt(&ok);
if (ok) {
jsonNr = n + 4;
}
}
}
if (jsonNr != 0) {
#if 0
m_hw->sys_requestJsonVersions(jsonNr);
QThread::msleep(500);
char buf[64];
memset(buf, 0x00, sizeof(buf));
m_hw->sys_getJsonVersions(jsonNr, buf);
buf[sizeof(buf)-1] = '\0';
QString const installedVersion(buf);
QString const fileVersion = getFileVersion(jsonFileNames[i]);
qCritical() << "installed version:" << installedVersion;
qCritical() << " file version:" << fileVersion;
if (installedVersion == fileVersion) {
}
#endif
} else {
qCritical() << "CANNOT FIND JSON-NR FOR" << jsonFileNames[i];
}
}
return false;
}
bool Update::doUpdate(int &displayIndex, QStringList const &filesToWorkOn) {
//if (m_sys_areDCdataValid == false) {
// qCritical() << "ERROR!!! DC DATA NOT VALID -> CA-MASTER-PLUGIN NOT CONNECTED";
// return false;
//}
bool res = false;
QList<QString>::const_iterator it;
for (it = filesToWorkOn.cbegin(); it != filesToWorkOn.cend(); ++it) {
m_worker->startProgressLoop();
QString const &fToWorkOn = QDir::cleanPath(m_customerRepository + QDir::separator() + it->trimmed());
if (fToWorkOn.contains("dc2c.bin")) {
bool updateBinaryRes = true;
// CONSOLE()
m_hw->dc_autoRequest(false);// default: turn auto-request setting off
QThread::sleep(1); // wait to be sure that there are no more
// commands sent to dc-hardware
if ((updateBinaryRes = updateBinary(fToWorkOn)) == true) {
// qCritical() << "downloaded binary" << fToWorkOn;
++displayIndex;
emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(2, ' ') + QString(")")
+ QString(" Update ") + QFileInfo(fToWorkOn).fileName(),
Worker::UPDATE_STEP_DONE);
}
m_hw->dc_autoRequest(true); // turn auto-request setting on
// qInfo() << "SET AUTO-REQUEST=TRUE";
QStringList const &versions = Update::getDcSoftAndHardWareVersion();
if (versions.size() >= 2) {
if (updateBinaryRes == true) {
qInfo() << "dc-hardware-version (UPDATED)" << versions[0];
qInfo() << "dc-firmware-version (UPDATED)" << versions[1];
} else {
qInfo() << "dc-hardware-version (NOT UPDATED)" << versions[0];
qInfo() << "dc-firmware-version (NOT UPDATED)" << versions[1];
}
}
res = updateBinaryRes;
} else {
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))) {
Utils::printInfoMsg(
QString("DOWNLOADED PRINTER TEMPLATE %1 WITH INDEX=%2")
.arg(fToWorkOn)
.arg(templateIdx));
++displayIndex;
emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(3, ' ') + QString(")")
+ QString(" Update ") + QFileInfo(fToWorkOn).fileName(),
Worker::UPDATE_STEP_DONE);
}
}
} else if (fToWorkOn.contains("DC2C_cash", Qt::CaseInsensitive)
&& fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) {
res = true;
if ((res = updateCashConf(fToWorkOn))) {
Utils::printInfoMsg(QString("DOWNLOADED CASH TEMPLATE %1").arg(fToWorkOn));
++displayIndex;
emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(3, ' ') + QString(")")
+ QString(" Update ") + QFileInfo(fToWorkOn).fileName(),
Worker::UPDATE_STEP_DONE);
}
} else if (fToWorkOn.contains("DC2C_conf", Qt::CaseInsensitive)
&& fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) {
res = true;
if ((res= updateConfig(fToWorkOn))) {
Utils::printInfoMsg(QString("DOWNLOADED CONFIG TEMPLATE %1").arg(fToWorkOn));
++displayIndex;
emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(3, ' ') + QString(")")
+ QString(" Update ") + QFileInfo(fToWorkOn).fileName(),
Worker::UPDATE_STEP_DONE);
}
} else if (fToWorkOn.contains("DC2C_device", Qt::CaseInsensitive)
&& fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) {
res = true;
if ((res = updateDeviceConf(fToWorkOn))) {
Utils::printInfoMsg(QString("DOWNLOADED DEVICE TEMPLATE %1").arg(fToWorkOn));
++displayIndex;
emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(3, ' ') + QString(")")
+ QString(" Update ") + QFileInfo(fToWorkOn).fileName(),
Worker::UPDATE_STEP_DONE);
}
} else {
qCritical() << "UNKNOWN JSON FILE NAME" << fToWorkOn;
res = false;
}
}
if (res == false) {
break;
}
} // for (it = openLines.cbegin(); it != openLines.end(); ++it) {
m_hw->dc_autoRequest(true); // ALWAYS turn autoRequest ON
qDebug() << "SET AUTO-REQUEST=TRUE";
return res;
}

View File

@ -1,99 +0,0 @@
#ifndef UPDATE_H_INCLUDED
#define UPDATE_H_INCLUDED
#include <QObject>
#include <QString>
#include <QFile>
#include <QDir>
#include <QByteArray>
#include <QProcess>
#include <QPluginLoader>
#include "plugins/interfaces.h"
#ifdef PTU5
#define SERIAL_PORT "ttymxc2"
#else
#define SERIAL_PORT "ttyUSB0"
#endif
class Worker;
class Update : public QObject {
Q_OBJECT
hwinf *m_hw = nullptr;
Worker *m_worker = 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;
public:
enum class DownloadResult {OK, ERROR, TIMEOUT, NOP};
enum class FileTypeJson {CONFIG=1, DEVICE=2, CASH=3, SERIAL=4, TIME=5, PRINTER=6};
static hwinf *loadDCPlugin(QDir const &plugInDir, QString const &fn);
static bool unloadDCPlugin();
static QStringList split(QString line, QChar sep = ',');
explicit Update(Worker *worker,
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(int &displayIndex, QStringList const &linesToWorkOn);
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 updatePrinterTemplate(int templateIdx, QString fname) const;
bool updateBinary(QString const &fileToSendToDC);
bool updateConfig(QString jsFileToSendToDC);
bool updateCashConf(QString jsFileToSendToDC);
bool updateDeviceConf(QString jsFileToSendToDC);
bool downloadJson(enum FileTypeJson type, int templateIdx,
QString jsFileToSendToDC) const;
QStringList getDcSoftAndHardWareVersion();
QString getFileVersion(QString const& jsonFile);
private slots:
void readyReadStandardOutput();
void readyReadStandardError();
void finished(int exitCode, QProcess::ExitStatus exitStatus);
};
#endif // UPDATE_H_INCLUDED