Compare commits

...

194 Commits

Author SHA1 Message Date
60ef5724cd Merge pull request 'sergeev_evgenii_lab_2 is done!' (#128) from sergeev_evgenii_lab_2 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/128
2024-01-18 16:04:59 +04:00
6827a64c4d Merge pull request 'kutygin_andrey_lab_3_ready' (#120) from kutygin_andrey_lab_3 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/120
2024-01-18 16:03:23 +04:00
7de577eadc Merge pull request 'podkorytova_yulia_lab_4 is ready' (#134) from podkorytova_yulia_lab_4 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/134
2024-01-17 17:07:53 +04:00
2690438508 Merge pull request 'romanova_adelina_lab_4_ready' (#129) from romanova_adelina_lab_4 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/129
2024-01-17 13:12:17 +04:00
e3677ed302 Merge pull request 'kutygin_andrey_lab_2_ready' (#121) from kutygin_andrey_lab_2 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/121
2024-01-17 11:50:07 +04:00
yulia
f5bc94c4ec podkorytova_yulia_lab_4 is ready 2024-01-17 04:12:23 +04:00
f4ec46b14d romanova_adelina_lab_4_ready 2024-01-16 17:30:43 +04:00
ac085099f4 Merge pull request 'kutygin_lab_1_ready' (#122) from kutygin_andrey_lab_1 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/122
2024-01-16 17:29:29 +04:00
sergeevevgen
46f2a8da94 + 2024-01-16 17:12:52 +04:00
Евгений Сергеев
84bd5277a9 done! 2024-01-16 16:57:20 +04:00
Евгений Сергеев
bb78e6823b done! 2024-01-16 16:05:42 +04:00
sergeevevgen
ea990fd848 + 2024-01-16 14:52:17 +04:00
a284e473a9 kutygin_andrey_lab_3_ready 2024-01-16 12:10:49 +04:00
c7ccb94de9 kutygin_andrey_lab_2_ready 2024-01-16 12:09:03 +04:00
0a6ce933c7 kutygin_lab_1_ready 2024-01-16 11:35:19 +04:00
d867909883 Merge pull request 'sergeev_evgenii_lab_1 is done!' (#119) from sergeev_evgenii_lab_1 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/119
2024-01-16 09:15:07 +04:00
sergeevevgen
b9d3eb249b лабораторная номер 1 готова! 2024-01-14 01:35:29 +04:00
sergeevevgen
934dd837ac + 2024-01-13 20:31:25 +04:00
Евгений Сергеев
36c429cb4f init 2024-01-13 19:17:58 +04:00
Евгений Сергеев
61c94a9155 init 2024-01-13 19:15:55 +04:00
8496ba5b3e Merge pull request 'podkorytova_yulia_lab_8 is ready' (#110) from podkorytova_yulia_lab_8 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/110
2024-01-13 09:40:47 +04:00
4e177372a9 Merge pull request 'podkorytova_yulia_lab_6 is ready' (#108) from podkorytova_yulia_lab_6 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/108
2024-01-13 09:40:42 +04:00
a311037d8d Merge pull request 'podkorytova_yulia_lab_7 is ready' (#109) from podkorytova_yulia_lab_7 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/109
2024-01-13 09:40:39 +04:00
6fea07c73d Merge pull request 'belyaeva lab8 ready' (#118) from belyaeva_ekaterina_lab_8 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/118
2024-01-13 09:39:50 +04:00
3f6234209a Merge pull request 'belyaeva lab7 ready' (#117) from belyaeva_ekaterina_lab_7 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/117
2024-01-13 09:39:41 +04:00
b3c9d6a471 Merge pull request 'belyaeva lab6 ready' (#116) from belyaeva_ekaterina_lab_6 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/116
2024-01-13 09:39:33 +04:00
080efc8a4c Merge pull request 'belyaeva_ekaterina_lab_5' (#115) from belyaeva_ekaterina_lab_5 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/115
2024-01-13 09:39:24 +04:00
6e86bdcc1e Merge pull request 'belyaeva lab4 ready' (#114) from belyaeva_ekaterina_lab_4 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/114
2024-01-13 09:39:15 +04:00
51c5bfa80a Merge pull request 'belyaeva lab3 ready' (#113) from belyaeva_ekaterina_lab_3 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/113
2024-01-13 09:39:08 +04:00
a901a2b306 Merge pull request 'belyaeva lab2 ready' (#112) from belyaeva_ekaterina_lab_2 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/112
2024-01-13 09:39:00 +04:00
ffdf6c9ab9 Merge pull request 'lab1 belyaeva ready' (#111) from belyaeva_ekaterina_lab_1 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/111
2024-01-13 09:38:51 +04:00
a44eb5cb56 belyaeva lab8 ready 2024-01-11 20:29:50 +04:00
2270fa67e4 belyaeva lab7 ready 2024-01-11 20:27:39 +04:00
8ed344819f belyaeva lab6 ready 2024-01-11 20:25:40 +04:00
426dcca1ec belyaeva lab5 ready 2024-01-11 20:23:47 +04:00
e7ba5e4e23 belyaeva lab5 ready 2024-01-11 20:21:38 +04:00
5bca0d2e6f belyaeva lab4 ready 2024-01-11 20:16:53 +04:00
992635f9e5 belyaeva lab3 ready 2024-01-11 20:12:15 +04:00
ec6f254f98 belyaeva lab2 ready 2024-01-11 20:08:33 +04:00
e1be77f193 lab1 ready 2024-01-11 20:02:42 +04:00
yulia
a8df36581d podkorytova_yulia_lab_8 is ready 2024-01-11 05:00:33 +04:00
yulia
e3972e8932 podkorytova_yulia_lab_7 is ready 2024-01-11 03:24:59 +04:00
yulia
e617f9ebbb podkorytova_yulia_lab_6 is ready 2024-01-10 16:37:46 +04:00
5e2305d3ac Merge pull request 'mashkova_margarita_lab_8' (#104) from mashkova_margarita_lab_8 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/104
2024-01-10 11:17:11 +04:00
89b987e416 Merge pull request 'mashkova_margarita_lab_7 ready' (#101) from mashkova_margarita_lab_7 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/101
2024-01-10 11:16:40 +04:00
2de04f60a9 Merge pull request 'mashkova_margarita_lab_6 ready' (#106) from mashkova_margarita_lab_6 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/106
2024-01-10 11:09:53 +04:00
a2cd0cf527 Merge pull request 'mashkova_margarita_lab_5 ready' (#105) from mashkova_margarita_lab_5 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/105
2024-01-10 11:08:46 +04:00
219ce2acfe Merge pull request 'mashkova_margarita_lab_4 ready' (#107) from mashkova_margarita_lab_4 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/107
2024-01-10 11:08:15 +04:00
cf88b559cc Merge pull request 'podkorytova_yulia_lab_5 is ready' (#100) from podkorytova_yulia_lab_5 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/100
2024-01-10 11:05:31 +04:00
8f7c563a25 Merge pull request 'podkorytova_yulia_lab_3 is ready' (#99) from podkorytova_yulia_lab_3 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/99
2024-01-10 11:01:51 +04:00
259bcd07f0 mashkova_margarita_lab_4 ready 2024-01-10 08:39:13 +04:00
e68c51b313 mashkova_margarita_lab_6 ready 2024-01-10 06:03:14 +04:00
12523aabaf mashkova_margarita_lab_5 some fix 2024-01-10 05:43:46 +04:00
b0d3c4cc7a mashkova_margarita_lab_5 fix readme 2024-01-10 05:28:38 +04:00
7205c6f8f0 mashkova_margarita_lab_5 ready 2024-01-10 05:21:50 +04:00
82cab3fc1b mashkova_margarita_lab_8 ready 2024-01-10 04:32:02 +04:00
a0e60b3699 mashkova_margarita_lab_8 ready 2024-01-10 04:24:31 +04:00
993786b8ae mashkova_margarita_lab_7 ready 2024-01-10 04:12:45 +04:00
yulia
66525b2d4b podkorytova_yulia_lab_5 is ready 2024-01-10 03:52:28 +04:00
yulia
7328dcb134 podkorytova_yulia_lab_3 is ready 2024-01-10 00:13:23 +04:00
08e44c25c3 Merge pull request 'almukhammetov_bulat_lab_8' (#95) from almukhammetov_bulat_lab_8 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/95
2024-01-09 11:33:51 +04:00
2c61f815fa Merge pull request 'almukhammetov_bulat_lab_7' (#94) from almukhammetov_bulat_lab_7 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/94
2024-01-09 11:33:41 +04:00
2bdf735b95 Merge pull request 'almulkammetov_bulat_lab_6' (#97) from almukhammetov_bulat_lab_6 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/97
2024-01-09 11:33:25 +04:00
2ef1f65ed6 Merge pull request 'almukhammetov_bulat_lab_5' (#96) from almukhammetov_bulat_lab_5 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/96
2024-01-09 11:33:12 +04:00
7dcce0138f Merge pull request 'almukhammetov_bulat_lab_4' (#93) from almukhammetov_bulat_lab_4 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/93
2024-01-09 11:32:43 +04:00
427173d554 Merge pull request 'almukhammetov_bulat_lab_3' (#92) from almukhammetov_bulat_lab_3 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/92
2024-01-09 11:32:27 +04:00
5104f74803 Merge pull request 'almukhammetov_bulat_lab_2' (#91) from almukhammetov_bulat_lab_2 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/91
2024-01-09 11:32:12 +04:00
5341271f9e Merge pull request 'degtyarev_mikhail_lab_2 is ready' (#98) from degtyarev_mikhail_lab_2 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/98
2024-01-09 11:31:43 +04:00
e582711076 degtyarev_mikhail_lab_2 is ready 2024-01-08 23:30:25 +04:00
BulatReznik
0a5d8d6e02 almulkammetob_lab_6 2024-01-07 13:36:54 +04:00
BulatReznik
4498245823 almukhammetov_bulat_lab_5 2024-01-07 11:16:43 +04:00
BulatReznik
b06fb8f5c5 almukhammetov_bulat_lab_8 2024-01-06 19:20:17 +04:00
BulatReznik
1ffed2075a + 2024-01-06 18:57:32 +04:00
BulatReznik
f7b9e00012 almukhammetov_bulat_lab_4 2024-01-06 18:37:17 +04:00
BulatReznik
6afc3b032f + 2024-01-06 16:59:55 +04:00
BulatReznik
a07d49560a + 2024-01-06 16:52:19 +04:00
BulatReznik
1cd0264117 almukhammetov_bulat_lab_2 2024-01-02 13:36:35 +04:00
a346187851 Merge pull request 'antonov_dmitry_lab_8_ready' (#38) from antonov_dmitry_lab_8 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/38
2023-12-28 11:20:49 +04:00
97f2f1e018 Merge pull request 'tepechin_kirill_lab_8' (#52) from tepechin_kirill_lab_8 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/52
2023-12-28 11:20:36 +04:00
314d96b716 Merge pull request 'basharin_sevastyan_lab_8 is ready' (#58) from basharin_sevastyan_lab_8 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/58
2023-12-28 11:20:25 +04:00
d45d516d4a Merge pull request 'gusev_vladislav_lab_8 is ready' (#66) from gusev_vladislav_lab_8 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/66
2023-12-28 11:20:06 +04:00
7c877e803d Merge pull request 'martysheva_tamara_lab_8 is ready' (#70) from martysheva_tamara_lab_8 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/70
2023-12-28 11:19:57 +04:00
e07dde810d Merge pull request 'alexandrov_dmitrii_lab_8 is ready' (#74) from alexandrov_dmitrii_lab_8 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/74
2023-12-28 11:19:46 +04:00
46ae5b2736 Merge pull request 'tepechin_kirill_lab_7' (#51) from tepechin_kirill_lab_7 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/51
2023-12-28 11:16:45 +04:00
55d384cf1b Merge pull request 'senkin_alexander_lab_7 is ready' (#56) from senkin_alexander_lab_7 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/56
2023-12-28 11:16:34 +04:00
3cc0bb20fc Merge pull request 'basharin_sevastyan_lab_7' (#57) from basharin_sevastyan_lab_7 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/57
2023-12-28 11:12:13 +04:00
d36865cc2b Merge pull request 'antonov_dmitry_lab_7_ready' (#62) from antonov_dmitry_lab_7 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/62
2023-12-28 11:11:48 +04:00
34ecd9c33e Merge pull request 'gusev_vladislav_lab_7 is ready' (#65) from gusev_vladislav_lab_7 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/65
2023-12-28 11:11:36 +04:00
9e7b0fcc03 Merge pull request 'martysheva_tamara_lab_7 is ready' (#69) from martysheva_tamara_lab_7 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/69
2023-12-28 11:10:13 +04:00
a3d495ad1a Merge pull request 'alexandrov_dmitrii_lab_7 is ready' (#83) from alexandrov_dmitrii_lab_7 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/83
2023-12-28 11:09:17 +04:00
0c34abeb21 Merge pull request 'martysheva_tamara_lab_5 is ready' (#67) from martysheva_tamara_lab_5 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/67
2023-12-28 11:08:38 +04:00
86d0303d8d Merge pull request 'tepechin_kirill_lab_6' (#49) from tepechin_kirill_lab_6 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/49
2023-12-28 11:08:22 +04:00
a5f9016bb5 Merge pull request 'gusev_vladislav_lab_6 is ready' (#64) from gusev_vladislav_lab_6 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/64
2023-12-28 11:07:34 +04:00
38717b004c Merge pull request 'martysheva_tamara_lab_6 is ready' (#68) from martysheva_tamara_lab_6 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/68
2023-12-28 11:07:04 +04:00
d613c85a20 Merge pull request 'alexandrov_dmitrii_lab_6 is ready' (#72) from alexandrov_dmitrii_lab_6 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/72
2023-12-28 11:06:51 +04:00
db6e3e1ba6 Merge pull request 'shadaev_anton_lab_6' (#82) from shadaev_anton_lab_6 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/82
2023-12-28 11:05:29 +04:00
4c7875241f Merge pull request 'alexandrov_dmitrii_lab_5 lab 5 is ready' (#71) from alexandrov_dmitrii_lab_5 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/71
2023-12-28 11:00:22 +04:00
6cf3fb6eda Merge pull request 'tepechin_kirill_lab_5' (#48) from tepechin_kirill_lab_5 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/48
2023-12-28 10:58:06 +04:00
073265a64b Merge pull request 'martysheva_tamara_lab_4 is ready' (#63) from martysheva_tamara_lab_4 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/63
2023-12-28 10:57:30 +04:00
176dd95462 Merge pull request 'gusev_vladislav_lab_4' (#46) from gusev_vladislav_lab_4 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/46
2023-12-28 10:57:06 +04:00
5f1bb5e2bd Merge pull request 'tepechin_kirill_lab_4' (#45) from tepechin_kirill_lab_4 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/45
2023-12-28 10:55:19 +04:00
5a1e7cb698 Merge pull request 'martysheva_tamara_lab_2 is ready' (#50) from martysheva_tamara_lab_2 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/50
2023-12-28 10:55:03 +04:00
0a2777cf55 Merge pull request 'alexandrov_dmitrii_lab_4 is ready' (#77) from alexandrov_dmitrii_lab_4 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/77
2023-12-28 10:46:50 +04:00
aeee4e3f7e Merge pull request 'shadaev_anton_lab_4' (#84) from shadaev_anton_lab_4 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/84
2023-12-28 10:43:58 +04:00
927816dc42 Merge pull request 'lab 3 is ready' (#87) from volkov_rafael_lab_3 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/87
2023-12-28 10:42:49 +04:00
660bc2681c Merge pull request 'kamyshov_danila_lab_3 is ready' (#90) from kamyshov_danila_lab_3 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/90
2023-12-28 10:42:22 +04:00
f03ae3e342 Merge pull request 'lab 2 is ready' (#86) from volkov_rafael_lab_2 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/86
2023-12-28 10:39:13 +04:00
59e524f061 Merge pull request 'kamyshov_danila_lab_2 is ready' (#89) from kamyshov_danila_lab_2 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/89
2023-12-28 10:39:02 +04:00
dc26a1f48c Merge pull request 'lab 1 is ready' (#85) from volkov_rafael_lab_1 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/85
2023-12-28 10:38:31 +04:00
a8f49f7572 Merge pull request 'kamyshov_danila_lab_1 is ready' (#88) from kamyshov_danila_lab_1 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/88
2023-12-28 10:38:10 +04:00
Danila Kamyshov
722b354fab kamyshov_danila_lab_3 is ready 2023-12-25 21:53:03 +04:00
Danila Kamyshov
256e916485 kamyshov_danila_lab_2 is ready 2023-12-25 21:52:38 +04:00
Danila Kamyshov
9160de5e51 kamyshov_danila_lab_1 is ready 2023-12-25 21:52:12 +04:00
VolkAuf
3593a55226 lab 3 is ready 2023-12-25 21:49:32 +04:00
VolkAuf
89f2a30f71 lab 2 is ready 2023-12-25 21:49:10 +04:00
VolkAuf
7d25138f9d lab 1 is ready 2023-12-25 21:48:33 +04:00
44925143c2 Merge pull request 'romanova_adelina_lab_3 is ready' (#43) from romanova_adelina_lab_3 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/43
2023-12-25 16:27:55 +04:00
c4c4873091 Merge pull request 'tepechin_kirill_lab_3' (#41) from tepechin_kirill_lab_3 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/41
2023-12-25 16:27:39 +04:00
42b2bd7a78 Merge pull request 'mashkova_margarita_lab_3 ready' (#55) from mashkova_margarita_lab_3 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/55
2023-12-25 14:10:55 +04:00
1555cb5642 Merge pull request 'martysheva_tamara_lab_3 is ready' (#60) from martysheva_tamara_lab_3 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/60
2023-12-25 14:10:23 +04:00
de4b5c8584 Merge pull request 'alexandrov_dmitrii_lab_3 is ready' (#73) from alexandrov_dmitrii_lab_3 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/73
2023-12-25 14:01:38 +04:00
27bde41368 Merge pull request 'shadaev_anton_lab_3' (#76) from shadaev_anton_lab_3 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/76
2023-12-25 13:59:18 +04:00
8926cf25c0 Merge pull request 'tepechin_kirill_lab_2' (#40) from tepechin_kirill_lab_2 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/40
2023-12-25 13:58:07 +04:00
fb2cd5836b Merge pull request 'shadaev_anton_lab_2' (#53) from shadaev_anton_lab_2 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/53
2023-12-25 12:19:08 +04:00
48b78907cb Merge pull request 'podkorytova_yulia_lab_2 is ready' (#61) from podkorytova_yulia_lab_2 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/61
2023-12-25 12:18:45 +04:00
33c9790fef Merge pull request 'mashkova_margarita_lab_2 ready' (#54) from mashkova_margarita_lab_2 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/54
2023-12-25 12:18:29 +04:00
f6b74ebd83 Merge pull request 'tepechin_kirill_lab_1' (#39) from tepechin_kirill_lab_1 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/39
2023-12-25 11:51:50 +04:00
60c5417187 Merge pull request 'mashkova_margarita_lab_1 ready' (#42) from mashkova_margarita_lab_1 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/42
2023-12-25 11:51:36 +04:00
ac825f136b Merge pull request 'shadaev_anton_lab_1' (#44) from shadaev_anton_lab_1 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/44
2023-12-25 11:23:55 +04:00
fa99584756 Merge pull request 'martysheva_tamara_lab_1' (#47) from martysheva_tamara_lab_1 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/47
2023-12-25 11:23:35 +04:00
da4a529412 Merge pull request 'podkorytova_yulia_lab_1 is ready' (#59) from podkorytova_yulia_lab_1 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/59
2023-12-25 11:21:27 +04:00
3a1de5ead0 Add lab4 2023-12-23 20:50:41 +04:00
3ca3be38d3 Лабораторная 7 2023-12-23 20:42:02 +04:00
c80433f4ca Update 'shadaev_anton_lab_6/README.md' 2023-12-23 20:07:54 +04:00
1d7145da66 Add lab8 2023-12-23 20:03:38 +04:00
985bf1ba01 Add lab7 2023-12-23 19:59:59 +04:00
880d359158 Add lab6 2023-12-23 19:56:19 +04:00
7ae3de0e65 Add lab5 2023-12-23 19:40:32 +04:00
2690abc1d6 Изменил(а) на 'alexandrov_dmitrii_lab_4/readme.md' 2023-12-23 19:15:05 +04:00
4ccd7b91ff Update 'shadaev_anton_lab_3/README.md' 2023-12-23 19:13:13 +04:00
fda332b761 Update 'shadaev_anton_lab_3/README.md' 2023-12-23 19:12:09 +04:00
d3154cdae0 4 лабораторная 2023-12-23 19:09:22 +04:00
cd51734f82 Add lab3 2023-12-23 18:39:38 +04:00
be23ab0a6e Лабораторная 8 2023-12-23 02:46:51 +04:00
cfb17604eb Лабораторная 3 2023-12-23 01:50:54 +04:00
13d5139b19 Лабораторная 6 2023-12-22 22:22:14 +04:00
c3c07e870e Изменил(а) на 'alexandrov_dmitrii_lab_5/readme.md' 2023-12-22 22:17:57 +04:00
0cf1b56fd2 Изменил(а) на 'alexandrov_dmitrii_lab_5/readme.md' 2023-12-22 22:12:18 +04:00
f17187207a Изменил(а) на 'alexandrov_dmitrii_lab_5/readme.md' 2023-12-22 22:10:41 +04:00
b7187c8f60 Изменил(а) на 'alexandrov_dmitrii_lab_5/readme.md' 2023-12-22 22:07:13 +04:00
81f8116770 Пятая лабораторная 2023-12-22 16:48:46 +04:00
06de5e8246 Пятая лабораторная 2023-12-22 16:08:59 +04:00
c99644f2d4 martysheva_tamara_lab_8 is ready 2023-12-21 20:57:28 +04:00
d0d2bcf2db martysheva_tamara_lab_7 is ready 2023-12-21 20:54:48 +04:00
301f7ad941 martysheva_tamara_lab_6 is ready 2023-12-21 20:50:48 +04:00
711b7d275a martysheva_tamara_lab_5 is ready 2023-12-21 20:43:51 +04:00
vladg
60be16bdbc gusev_vladislav_lab_8 is ready 2023-12-21 11:57:40 +04:00
vladg
46001bf28b gusev_vladislav_lab_7 is ready 2023-12-21 11:44:00 +04:00
vladg
fadbd75718 gusev_vladislav_lab_6 is ready 2023-12-21 11:24:57 +04:00
38854734b5 martysheva_tamara_lab_4 is ready 2023-12-17 01:31:56 +04:00
DmitriyAntonov
3f98128517 antonov_dmitry_lab_7_ready 2023-12-16 12:28:21 +04:00
yulia
9b69bb7feb podkorytova_yulia_lab_2 is ready 2023-12-15 16:38:14 +04:00
yulia
7459415f70 podkorytova_yulia_lab_2 is ready 2023-12-15 16:29:47 +04:00
yulia
6ddd513f75 podkorytova_yulia_lab_2 is ready 2023-12-15 16:29:17 +04:00
ad6a4552db martysheva_tamara_lab_3 is ready 2023-12-15 12:05:54 +04:00
yulia
7bdeec66ba podkorytova_yulia_lab_1 is ready 2023-12-14 23:35:32 +04:00
e35c9da7e0 basharin_sevastyan_lab_8 is ready 2023-12-14 17:24:26 +04:00
c83f26573c basharin_sevastyan_lab_7 is ready 2023-12-14 16:58:50 +04:00
01a4946295 basharin_sevastyan_lab_7 is ready 2023-12-14 16:57:58 +04:00
ad12c8f3ac senkin_alexander_lab_7 is ready 2023-12-14 15:16:02 +04:00
45b0dc6f0a mashkova_margarita_lab_3 ready 2023-12-14 09:39:03 +04:00
71bc735b26 mashkova_margarita_lab_2 ready 2023-12-14 01:57:37 +04:00
a1f814a584 Add lab2 2023-12-13 23:36:59 +04:00
5044e52972 tepechin_kirill_lab_8 2023-12-13 22:50:36 +04:00
d39df3e247 tepechin_kirill_lab_7 2023-12-13 22:04:05 +04:00
df28992456 martysheva_tamara_lab_2 is ready 2023-12-13 21:44:10 +04:00
f67d0375f9 tepechin_kirill_lab_6 2023-12-13 19:48:21 +04:00
977bdd56b2 martysheva_tamara_lab_1 is ready 2023-12-13 15:15:47 +04:00
063e631fc0 martysheva_tamara_lab_1 is ready 2023-12-13 15:13:47 +04:00
vladg
775bd41749 gusev_vladislav_lab_4 is ready 2023-12-13 15:02:01 +04:00
vladg
b75a601ab9 gusev_vladislav_lab_4 is ready 2023-12-13 15:01:25 +04:00
9022f7ad44 tepechin_kirill_lab_5 2023-12-13 12:23:00 +04:00
0eb56e61da tepechin_kirill_lab_5 2023-12-13 12:22:31 +04:00
04fc1cf183 fix README.md 2023-12-12 23:02:45 +04:00
170d671dff tepechin_kirill_lab_4 2023-12-12 23:01:02 +04:00
eaa28196ec Add lab1 2023-12-12 22:14:05 +04:00
f2adafe2e1 Update 'README.md' 2023-12-12 21:33:03 +04:00
5f3397e313 Update 'labs/lab_7.md' 2023-12-12 21:28:37 +04:00
b0c2b37252 romanova_adelina_lab_3 is ready 2023-12-12 21:24:00 +04:00
aac23548d7 mashkova_margarita_lab_1 ready 2023-12-12 05:28:44 +04:00
79c62bdf6a tepechin_kirill_lab_2 2023-12-11 03:01:38 +04:00
49a6a252e3 tepechin_kirill_lab_1 2023-12-11 01:03:05 +04:00
DmitriyAntonov
5120eec932 antonov_dmitry_lab_8_ready 2023-12-09 21:40:16 +04:00
076820725d test 2023-11-09 06:59:07 +04:00
1770 changed files with 54133 additions and 2 deletions

View File

@ -20,5 +20,5 @@
4. [Работа с брокером сообщений](http://student.git.athene.tech/Alexey/DAS_2023_1/src/branch/main/labs/lab_4.md)
5. [Параллельное умножение матриц](http://student.git.athene.tech/Alexey/DAS_2023_1/src/branch/main/labs/lab_5.md)
6. [Параллельный поиск значения детерминанта матрицы](http://student.git.athene.tech/Alexey/DAS_2023_1/src/branch/main/labs/lab_6.md)
7. TBD
7. [Балансировка нагрузки в распределённых системах при помощи открытых технологий на примерах](http://student.git.athene.tech/Alexey/DAS_2023_1/src/branch/main/labs/lab_7.md).
8. [Про устройство распределенных систем](http://student.git.athene.tech/Alexey/DAS_2023_1/src/branch/main/labs/lab_8.md).

View File

@ -0,0 +1,27 @@
version: '3.8'
services:
usr_service:
build:
context: /user_service
dockerfile: Dockerfile
depends_on:
- msg_service
expose:
- 8082
msg_service:
build:
context: /message_service
dockerfile: Dockerfile
expose:
- 8081
nginx:
image: nginx
ports:
- 8086:8086
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- msg_service
- usr_service

View File

@ -0,0 +1,11 @@
FROM python:3.9
EXPOSE 8081
RUN pip install Flask requests
WORKDIR /work
COPY msg_service.py ./
CMD ["python", "msg_service.py"]

View File

@ -0,0 +1,50 @@
from flask import Flask, jsonify, request, Response
from datetime import datetime
app = Flask(__name__)
msgs = {0: {'id': 0, 'from': 0, 'to': 1, 'body': 'qq', 'dt': datetime(2023, 12, 22, 8, 0)},
1: {'id': 1, 'from': 1, 'to': 0, 'body': 'qq', 'dt': datetime(2023, 12, 22, 8, 5)},
2: {'id': 2, 'from': 0, 'to': 1, 'body': 'bye', 'dt': datetime(2023, 12, 22, 8, 10)},
3: {'id': 3, 'from': 1, 'to': 1, 'body': 'bye', 'dt': datetime(2023, 12, 22, 8, 15)}}
@app.route('/', methods=['GET', 'POST'])
def get_all():
if request.method == 'POST':
dto = request.get_json()['dto']
new_id = max(msgs.keys()) + 1
msgs[new_id] = {
'id': new_id,
'from': dto['from'],
'to': dto['to'],
'body': dto['body'],
'dt': datetime.now()
}
return jsonify(msgs[new_id])
return jsonify(msgs)
@app.route('/<int:msg_id>', methods=['GET', 'PUT', 'DELETE'])
def get_by_id(msg_id):
if msg_id not in msgs.keys():
return Response(status=404)
if request.method == 'PUT':
dto = request.get_json()['dto']
msgs[msg_id] = {
'from': dto['from'],
'to': dto['to'],
'body': dto['body'],
'dt': datetime.now()
}
return msgs[msg_id]
elif request.method == 'DELETE':
msgs.pop(msg_id)
return Response(status=200)
return jsonify(msgs[msg_id])
if __name__ == '__main__':
app.run(host='0.0.0.0', use_reloader=False, port=8081)

View File

@ -0,0 +1,27 @@
events {
worker_connections 1024;
}
http {
server {
listen 8086;
listen [::]:8086;
server_name localhost;
location /msg_service/ {
proxy_pass http://msg_service:8081/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Prefix $scheme;
}
location /usr_service/ {
proxy_pass http://usr_service:8082/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Prefix $scheme;
}
}
}

View File

@ -0,0 +1,32 @@
## Задание
1. Создать 2 микросервиса, реализующих CRUD на связанных сущностях.
2. Реализовать механизм синхронного обмена сообщениями между микросервисами.
3. Реализовать шлюз на основе прозрачного прокси-сервера nginx.
Вариант: сообщения и пользователи
## Выполнение
Были написаны два сервиса на языке python с технологией flask:
* Сервис msg_service, хранящий данные о сообщениях и реализующий CRUD операции с ними через HTTP запросы.
* Сервис usr_service, хранящий данные о пользователях и реализующий CRUD операции с ними через HTTP запросы.
Сервисы синхронно сообщены - сервис пользователей запрашивает данные у сервиса сообщений для получения сообщений пользователя.
Для сервисов прописаны файлы Dockerfile, описывающие создание контейнеров:
* Для обоих контейнеров выбирается Python 3.9.
* Оба контейнера проявляют порты, на которых работает приложение: 8081 для сообщений и 8082 для пользователей.
* В оба контейнера устанавливаются пакеты Flask и requests.
* Выбирается рабочая директория /work и туда копируются файлы скриптов.
* Командой запускаются сами скрипты.
Общий yaml-файл развёртки был настроен следующим образом:
* блок services, где перечислены разворачиваемые сервисы.
* для каждого сервиса прописан build, где объявляется его папка и докерфайл создания и зависимости.
* для сервиса nginx прописан порт для отображения вовне.
## Результат
Демонстрация работы в видео.
## Ссылка на видео
https://drive.google.com/file/d/1gmZsbzMmC34Uidi4u_D3nFyPAG0MuPAf/view?usp=drive_link

View File

@ -0,0 +1,11 @@
FROM python:3.9
EXPOSE 8082
RUN pip install Flask requests
WORKDIR /work
COPY usr_service.py ./
CMD ["python", "usr_service.py"]

View File

@ -0,0 +1,56 @@
import requests
from flask import Flask, jsonify, request, Response
app = Flask(__name__)
usrs = {0: {'id': 0, 'name': 'anton', 'role': 'admin', 'online': False, 'msgs_sent': [0, 2], 'msgs_got': [1, 3]},
1: {'id': 1, 'name': 'lioha', 'role': 'user', 'online': False, 'msgs_sent': [1, 3], 'msgs_got': [0, 2]}}
@app.route('/', methods=['GET', 'POST'])
def get_all():
if request.method == 'POST':
dto = request.get_json()['dto']
new_id = max(usrs.keys()) + 1
usrs[new_id] = {
'id': new_id,
'name': dto['name'],
'role': dto['role'],
'online': dto['online']
}
return jsonify(usrs[new_id])
return jsonify(usrs)
@app.route('/<int:usr_id>', methods=['GET', 'PUT', 'DELETE'])
def get_by_id(usr_id):
if usr_id not in usrs.keys():
return Response(status=404)
if request.method == 'PUT':
dto = request.get_json()['dto']
usrs[usr_id] = {
'name': dto['name'],
'role': dto['role'],
'online': dto['online']
}
return usrs[usr_id]
elif request.method == 'DELETE':
usrs.pop(usr_id)
return Response(status=200)
usr = usrs[usr_id]
msgs = []
for msg_id in usr['msgs_sent']:
msgs.append(requests.get("http://msg_service:8081/"+str(msg_id)).json())
usr['msgs_sent'] = msgs
msgs.clear()
for msg_id in usr['msgs_got']:
msgs.append(requests.get("http://msg_service:8081/"+str(msg_id)).json())
usr['msgs_got'] = msgs
return jsonify(usr)
if __name__ == '__main__':
app.run(host='0.0.0.0', use_reloader=False, port=8082)

View File

@ -0,0 +1,53 @@
#ifndef _CLIENT_H
#define _CLIENT_H
#include <QList>
#include <QMap>
#include <QObject>
#include <QString>
#include <QVariant>
#include "options/clientoption.h"
#include "options/consumeoption.h"
#include "options/exchangeoption.h"
using namespace std;
#define HEARBEATS "@@__Control__@@"
class Sender;
class Receiver;
class Client : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
QList<Sender *> *_senders;
QList<Receiver *> *_receivers;
public:
Client();
Client(ClientOption option);
virtual ~Client();
Client& operator=(Client client);
QString getVersion() const;
ClientOption getClientOption() const { return clientOption; }
Sender *createSender(ExchangeOption& option);
void removeSender(Sender *sender);
Receiver *createReceiver(ConsumeOption& option);
void removeReceiver(Receiver *receiver);
signals:
void onStop();
};
Q_DECLARE_METATYPE(string)
#endif // _CLIENT_H

View File

@ -0,0 +1,124 @@
#include "_client_.h"
#include "sender.h"
#include "receiver.h"
// Конструктор для связи с локальным RabbitMQ
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Client::Client() : QObject(nullptr)
{
{
static const int idMsg = qRegisterMetaType<ProduceMessage>();
static const int idMsgPtr = qRegisterMetaType<PtrProduceMessage>();
static const int idString = qRegisterMetaType<string>();
Q_UNUSED(idMsg)
Q_UNUSED(idMsgPtr)
Q_UNUSED(idString)
}
_senders = new QList<Sender *>();
_senders->clear();
_receivers = new QList<Receiver *>();
_receivers->clear();
}
Client::Client(ClientOption option) : Client()
{
clientOption = option;
}
Client::~Client()
{
for (auto &sender : *_senders)
delete sender;
delete _senders;
for (auto &receiver : *_receivers)
delete receiver;
delete _receivers;
}
Client& Client::operator=(Client client)
{
if (this != &client) {
this->clientOption = client.clientOption;
this->_senders = new QList<Sender *>();
this->_senders->clear();
this->_receivers = new QList<Receiver *>();
this->_receivers->clear();
}
return *this;
}
int majorVersion = 1;
int minorVersion = 1;
int releaseVersion = 1;
QString Client::getVersion() const
{
return QString::number(majorVersion) +
"." + QString::number(minorVersion) +
"." + QString::number(releaseVersion);
}
// Создание публикатора (издателя) сообщений
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sender *Client::createSender(ExchangeOption& option)
{
Sender *sender = new Sender(clientOption, option);
connect(this, &Client::onStop, sender, &Sender::slotStop);
connect(this, &Client::onStop, sender, &Sender::deleteLater);
_senders->append(sender);
return sender;
}
void Client::removeSender(Sender *sender)
{
if ( !_senders->contains(sender))
return;
sender->slotStop();
_senders->removeOne(sender);
}
// Создание потребителя сообщений
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Receiver *Client::createReceiver(ConsumeOption& option)
{
Receiver *receiver = new Receiver(clientOption, option);
connect(this, &Client::onStop, receiver, &Receiver::slotStop);
connect(this, &Client::onStop, receiver, &Receiver::deleteLater);
_receivers->append(receiver);
return receiver;
}
void Client::removeReceiver(Receiver *receiver)
{
if ( !_receivers->contains(receiver))
return;
receiver->slotStop();
_receivers->removeOne(receiver);
}

View File

@ -0,0 +1,22 @@
#ifndef CLIENT_CPP_H
#define CLIENT_CPP_H
#include "clientrbcpp_global.h"
#include "clientrbcpp.h"
#include "_client_.h"
#include "producemessage.h"
#include "properties.h"
#include "cworker.h"
#include "headers.h"
#include "pworker.h"
#include "receiver.h"
#include "sender.h"
#include "validator.h"
#include "vworker.h"
#include "options/clientoption.h"
#include "options/consumeoption.h"
#include "options/exchangeoption.h"
#include "options/queueoption.h"
#endif // CLIENT_CPP_H

View File

@ -0,0 +1,6 @@
#include "clientrbcpp.h"
ClientRBcpp::ClientRBcpp()
{
}

View File

@ -0,0 +1,13 @@
#ifndef CLIENTRBCPP_H
#define CLIENTRBCPP_H
#include "clientrbcpp_global.h"
class CLIENTRBCPPSHARED_EXPORT ClientRBcpp
{
public:
ClientRBcpp();
};
#endif // CLIENTRBCPP_H

View File

@ -0,0 +1,12 @@
#ifndef CLIENTRBCPP_GLOBAL_H
#define CLIENTRBCPP_GLOBAL_H
#include <QtCore/qglobal.h>
#if defined(CLIENTRBCPP_LIBRARY)
# define CLIENTRBCPPSHARED_EXPORT Q_DECL_EXPORT
#else
# define CLIENTRBCPPSHARED_EXPORT Q_DECL_IMPORT
#endif
#endif // CLIENTRBCPP_GLOBAL_H

View File

@ -0,0 +1,276 @@
#include <QCoreApplication>
#include "client_cpp.h"
#include <QDebug>
CWorker::CWorker(ClientOption& clientOption, ConsumeOption& consumeOption)
: QObject(nullptr), markStop(false)
{
this->clientOption = clientOption;
this->consumeOption = consumeOption;
this->consumeOption.receivingExchange = consumeOption.receivingExchange;
connection = nullptr;
channel = nullptr;
}
CWorker::~CWorker()
{
}
// Здесь реализуется основная деятельность потока
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void CWorker::doWork()
{
string host = clientOption.host.toStdString();
string port = QString::number(clientOption.port).toStdString();
string user = clientOption.user.toStdString();
string password = clientOption.password.toStdString();
string vhost = clientOption.vhost.toStdString();
string address = "amqp://" + user + ":" + password + "@" + host + ":" + port + vhost;
auto evbase = event_base_new();
AMQP::LibEventHandler handler(evbase);
// AMQP::TcpConnection connection(&handler, AMQP::Address(address));
// AMQP::TcpChannel channel(&connection);
connection = new AMQP::TcpConnection(&handler, AMQP::Address(address));
channel = new AMQP::TcpChannel(connection);
channel->setQos(1);
channel->onError([&](const char *message) {
Q_UNUSED(message)
emit onErrorConsume("Channel error!!!");
});
QTimer tm;
tm.stop();
tm.setInterval(30000);
connect(&tm, &QTimer::timeout, this, [&]() {
tm.stop();
connection->heartbeat();
tm.start();
});
tm.start();
// Обработка принятого сообщения
auto messageCb = [&](const AMQP::Message& message, uint64_t deliveryTag, bool redelivered)
{
Q_UNUSED(redelivered)
// Формируем принятое сообщениев формате ReceivedMessage
ProduceMessage *rMsg = new ProduceMessage;
rMsg->setBodyMsg(message.body(), message.bodySize());
// Формируем таблицу Properties свойств сообщения
Properties p;
if (message.hasContentType())
p.setContentType(QString::fromStdString(message.contentType()));
if (message.hasContentEncoding())
p.setContentEncoding(QString::fromStdString(message.contentEncoding()));
if (message.hasMessageID())
p.setMessageID(QString::fromStdString(message.messageID()));
if (message.hasCorrelationID())
p.setCorrelationID(QString::fromStdString(message.correlationID()));
if (message.timestamp())
p.setTimestamp(message.timestamp());
if (message.hasExpiration())
p.setExpiration(QString::fromStdString(message.expiration()));
if (message.hasDeliveryMode())
p.setDeliveryMode(message.deliveryMode());
if (message.hasAppID())
p.setAppID(QString::fromStdString(message.appID()));
if (message.hasUserID())
p.setUserID(QString::fromStdString(message.userID()));
if (message.hasTypeName())
p.setTypeName(QString::fromStdString(message.typeName()));
if (message.hasReplyTo())
p.setReplyTo(QString::fromStdString(message.replyTo()));
if (message.hasPriority())
p.setPriority(message.priority());
rMsg->setProperties(p);
// Работа со свойствами Headers
Headers h;
AMQP::Table table = message.headers();
vector<string> keys = table.keys();
string name;
for(uint i = 0; i < keys.size(); i++) {
name = keys[i];
if (table.get(name).isInteger()) {
int value = table.get(name);
h.set(QString::fromStdString(name), value);
}
else if (table.get(name).isString()) {
QString str = QString::fromStdString(table.get(name));
h.set(QString::fromStdString(name), str);
}
else if (table.get(name).isBoolean()) {
AMQP::Field &b = const_cast<AMQP::Field &>(table.get(name));
AMQP::BooleanSet &b1 = dynamic_cast<AMQP::BooleanSet &>(b);
bool value = b1.get(0);
h.set(QString::fromStdString(name), value);
}
}
rMsg->setHeaders(h);
emit onResultReady(rMsg, deliveryTag);
channel->ack(deliveryTag);
};
// объявление точки обмена
if (!consumeOption.receivingExchange.name.isEmpty()) {
string exchange = consumeOption.receivingExchange.name.toStdString();
string type = consumeOption.receivingExchange.type.toStdString();
// преобразование типа точки обмена в формат AMQP
AMQP::ExchangeType typeEx;
if (type == "" || type == "direct")
typeEx = AMQP::direct;
else if (type == "topic")
typeEx = AMQP::topic;
else if (type == "headers")
typeEx = AMQP::headers;
else
typeEx = AMQP::fanout;
// предобразование флагов точки обмена в формат AMQP
int flagsExchange = 0;
if (consumeOption.receivingExchange.durable)
flagsExchange |= AMQP::durable;
if (consumeOption.receivingExchange.auto_delete)
flagsExchange |= AMQP::autodelete;
if (consumeOption.receivingExchange.internal)
flagsExchange |= AMQP::internal;
AMQP::Table tableExch;
QString alt_e_name = "alternate-exchange";
QString alt_e_value = "";
if (consumeOption.receivingExchange.arguments.contains(alt_e_name)) {
alt_e_value = consumeOption.receivingExchange.arguments[alt_e_name].value<QString>();
tableExch.set(alt_e_name.toStdString(), alt_e_value.toStdString());
}
// Для предопределенных точек обмена их обьявление не производим
if ( exchange != "" && exchange != "amq.fanout" && exchange != "amq.direct" &&
exchange != "amq.topic" && exchange != "amq.headers") {
channel->declareExchange(exchange, typeEx, flagsExchange, tableExch)
.onError([&](const char *description) {
qDebug() << description;
});
}
QMultiMap<QString, QString>::iterator it = consumeOption.bindingArgs.begin();
for(; it != consumeOption.bindingArgs.end(); ++it) {
channel->bindExchange(it.key().toStdString(), exchange, it.value().toStdString()).onError([&](const char *description) {
qDebug() << description;
});
}
}
// объявление очереди
QueueOption option = consumeOption.queueOption;
string exchange = consumeOption.exchange.toStdString();
string queue = option.name.toStdString();
// Подготовка флагов для объявления очереди
int flagsQueue = 0;
if (option.durable)
flagsQueue |= AMQP::durable;
if (option.auto_delete)
flagsQueue |= AMQP::autodelete;
if (option.exclusive)
flagsQueue |= AMQP::exclusive;
channel->declareQueue(queue, flagsQueue)
.onSuccess( [&](const string &name, uint32_t messageCount, uint32_t consumerCount) {
Q_UNUSED(messageCount)
Q_UNUSED(consumerCount)
queue = name;
if (exchange != "")
for (QString rk : consumeOption.bindingKeys) {
channel->bindQueue(exchange, queue, rk.toStdString())
.onError( [&](const char *description) {
qDebug() << description;
});
}
});
// Подготовка флагов потребления сообщений
int flagsConsume = 0;
if (consumeOption.nolocal)
flagsConsume |= AMQP::nolocal;
if (consumeOption.noack)
flagsConsume |= AMQP::noack;
if (consumeOption.exclusive)
flagsConsume |= AMQP::exclusive;
channel->consume(queue, flagsConsume).onReceived(messageCb)
.onSuccess( [&](const string& tag) {
nextTag = tag;
})
.onError( [&](const char *description) {
emit onErrorConsume(description);
markStop = true; // Останов потока
});
//Цикл обработки событий
while(!markStop) {
event_base_loop(evbase, EVLOOP_ONCE);
QCoreApplication::processEvents();
}
// Закроем канал и соединение
channel->close();
connection->close();
event_base_loopbreak(evbase);
event_base_loopexit(evbase, 0);
event_base_free(evbase);
delete channel;
delete connection;
emit onWorkFinished(); // Посылаем сигнал о завершении работы
}
void CWorker::slotStop()
{
markStop = true;
channel->cancel(nextTag); // Отменить потребление
channel->close();
connection->close();
}
void CWorker::bind(QString exchange, QString key, bool ex)
{
if (ex) channel->bindExchange(exchange.toStdString(), consumeOption.exchange.toStdString(), key.toStdString());
else channel->bindQueue(exchange.toStdString(), consumeOption.queueOption.name.toStdString(), key.toStdString());
}
void CWorker::unbind(QString exchange, QString key, bool ex)
{
if (ex) channel->unbindExchange(exchange.toStdString(), consumeOption.exchange.toStdString(), key.toStdString());
else channel->unbindQueue(exchange.toStdString(), consumeOption.queueOption.name.toStdString(), key.toStdString());
}

View File

@ -0,0 +1,54 @@
#ifndef CWORKER_H
#define CWORKER_H
#include <QMap>
#include <QMutex>
#include <QObject>
#include <QString>
#include <QThread>
#include <QTimer>
#include <QVariant>
#include <iostream>
#include <amqpcpp.h>
#include <amqpcpp/libevent.h>
#include "client_cpp.h"
using namespace std;
class CWorker : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
ConsumeOption consumeOption;
bool markStop;
AMQP::TcpConnection *connection;
AMQP::TcpChannel *channel;
string nextTag;
public:
CWorker(ClientOption& clientOption, ConsumeOption& consumeOption);
virtual ~CWorker();
public slots:
void doWork();
void slotStop();
void bind(QString exchange, QString key, bool ex);
void unbind(QString exchange, QString key, bool ex);
signals:
void onResultReady(PtrProduceMessage msg, uint64_t deliveryTag);
void onErrorConsume(const char *description);
void onWorkFinished();
};
#endif // CWORKER_H

View File

@ -0,0 +1,40 @@
#include "headers.h"
Headers::Headers()
{
_headers.clear();
}
Headers::Headers(const Headers& h)
{
this->_headers = h._headers;
}
void Headers::operator=(const Headers& h)
{
this->_headers = h._headers;
}
QMap<QString,QVariant> Headers::getHeaders() const { return _headers; }
QList<QString> Headers::keys() const { return _headers.keys(); }
QList<QVariant> Headers::values() const { return _headers.values(); }
int Headers::size() const { return _headers.size(); }
bool Headers::contains(const QString name) const { return _headers.contains(name); }
void Headers::set(const QString name, bool value) { _headers.insert(name, value); }
void Headers::set(const QString name, int value) { _headers.insert(name, value); }
void Headers::set(const QString name, QString str) { _headers.insert(name, str); }
bool Headers::isBool(const QString name) const { return _headers[name].type() == QVariant::Bool; }
bool Headers::isInteger(const QString name) const { return _headers[name].type() == QVariant::Int; }
bool Headers::isString(const QString name) const { return _headers[name].type() == QVariant::String; }
bool Headers::getBool(const QString name) const { return _headers[name].value<bool>(); }
int Headers::getInteger(const QString name) const { return _headers[name].value<int>(); }
QString Headers::getString(const QString name) const { return _headers[name].value<QString>(); }

View File

@ -0,0 +1,45 @@
#ifndef HEADERS_H
#define HEADERS_H
#include <QList>
#include <QMap>
#include <QString>
#include <QVariant>
class Headers
{
private:
QMap<QString,QVariant> _headers;
public:
Headers();
Headers(const Headers& h); // Конструктор копирования
~Headers() {}
void operator=(const Headers& h);
QMap<QString,QVariant> getHeaders() const;
QList<QString> keys() const;
QList<QVariant> values() const;
int size() const;
bool contains(const QString name) const;
void set(const QString name, bool value);
void set(const QString name, int value);
void set(const QString name, QString str);
bool isBool(const QString name) const;
bool isInteger(const QString name) const;
bool isString(const QString name) const;
bool getBool(const QString name) const;
int getInteger(const QString name) const;
QString getString(const QString name) const;
};
#endif // HEADERS_H

View File

@ -0,0 +1,45 @@
#ifndef CLIENTOPTION_H
#define CLIENTOPTION_H
#include <QString>
// Значения по умолчанию
const QString DEFAULT_CPP_HOST = "localhost";
const int DEFAULT_CPP_PORT = 5672;
const QString DEFAULT_CPP_USER = "guest";
const QString DEFAULT_CPP_PASSWORD = "guest";
const QString DEFAULT_CPP_VHOST = "/";
struct ClientOption {
QString host;
int port;
QString user;
QString password;
QString vhost;
ClientOption() {
host = DEFAULT_CPP_HOST;
port = DEFAULT_CPP_PORT;
user = DEFAULT_CPP_USER;
password = DEFAULT_CPP_PASSWORD;
vhost = DEFAULT_CPP_VHOST;
}
~ClientOption() {}
ClientOption(const ClientOption& src) = default; // Конструктор копирования
ClientOption(ClientOption&& src) = default; // Конструктор перемещения
ClientOption& operator=(const ClientOption rhs) // Оператор присваивания
{
host = rhs.host;
port = rhs.port;
user = rhs.user;
password = rhs.password;
vhost = rhs.vhost;
return *this;
}
};
#endif // CLIENTOPTION_H

View File

@ -0,0 +1,52 @@
#ifndef CONSUMEOPTION_H
#define CONSUMEOPTION_H
#include <QObject>
#include <QString>
#include <QList>
#include "queueoption.h"
#include "exchangeoption.h"
struct ConsumeOption
{
QString exchange; // Имя точки обмена для связывания
QStringList bindingKeys; // ключи связи точки с очередью
bool nolocal;
bool noack;
bool exclusive;
QueueOption queueOption; // Параметры очереди
ExchangeOption receivingExchange; // Параметры новой принимающей очереди (по умолчанию новой не создаётся)
QMultiMap<QString, QString> bindingArgs; // список связей для точки обмена (если создаётся новая точка)
ConsumeOption() {
exchange = "";
receivingExchange.name = "";
nolocal = false;
noack = false;
exclusive = false;
}
~ConsumeOption() {}
ConsumeOption(const ConsumeOption& src) = default; // Конструктор копирования
ConsumeOption(ConsumeOption&& src) = default; // Конструктор перемещения
ConsumeOption& operator=(const ConsumeOption rhs) // Оператор присваивания
{
exchange = rhs.exchange;
bindingKeys = rhs.bindingKeys;
nolocal = rhs.nolocal;
noack = rhs.noack;
exclusive = rhs.exclusive;
queueOption = rhs.queueOption;
bindingArgs = rhs.bindingArgs;
return *this;
}
};
#endif // CONSUMEOPTION_H

View File

@ -0,0 +1,55 @@
#ifndef EXCHANGEOPTION_H
#define EXCHANGEOPTION_H
#include <QString>
#include <QVariantMap>
#include <QList>
struct ExchangeOption
{
QString name; // уникальное имя точки обмена
QString type; // тип точки обмена (direct, topic,
// fanout или headers)
bool auto_delete; // автоматически удаляемая точка обмена
bool durable; // долгоживущая точка обмена
bool passive; // требуется информация о точке обмена
bool internal; // нельзя вести публикацию из приложения
QVariantMap arguments; // необязательные аргументы
QMap<QString, QString> bindingArgs; // список связей для точки обмена
bool ifunused; // можно удалять, только если точка обмена
// не используется (не имеет потребителей)
ExchangeOption() {
name = "";
type = "";
auto_delete = false;
durable = false;
passive = false;
internal = false;
arguments.clear();
ifunused = false;
}
~ExchangeOption() {}
ExchangeOption(const ExchangeOption& src) = default; // Конструктор копирования
ExchangeOption(ExchangeOption&& src) = default; // Конструктор перемещения
ExchangeOption& operator=(const ExchangeOption rhs) // Оператор присваивания
{
name = rhs.name;
type = rhs.type;
auto_delete = rhs.auto_delete;
durable = rhs.durable;
passive = rhs.passive;
internal = rhs.internal;
arguments = rhs.arguments;
ifunused = rhs.ifunused;
return *this;
}
};
#endif // EXCHANGEOPTION_H

View File

@ -0,0 +1,63 @@
#ifndef QUEUEOPTION_H
#define QUEUEOPTION_H
#include <QObject>
#include <QString>
#include <QVariantMap>
struct QueueOption
{
QString name; // Уникальное имя очереди
bool auto_delete; // Автоматически удаляемая очередь
bool durable; // Долгоживущая очередь
bool passive; // Требуется информация об очереди
bool exclusive; // Исключительная очередь
QVariantMap arguments; // Необязательные аргументы очереди
bool ifunused; // Удалять, только если не используется
bool ifempty; // Удалять, только если очередь пуста
int messageCount; // Число сообщений в очереди
int consumerCount; // Число потребителей очереди
QueueOption() {
name = "";
auto_delete = false;
durable = false;
passive = false;
exclusive = false;
arguments.clear();
ifunused = false;
ifempty = false;
messageCount = 0;
consumerCount = 0;
}
~QueueOption() {}
QueueOption(const QueueOption& src) = default; // Конструктор копирования
QueueOption(QueueOption&& src) = default; // Конструктор перемещения
QueueOption& operator=(const QueueOption rhs) // Оператор присваивания
{
name = rhs.name;
auto_delete = rhs.auto_delete;
passive = rhs.passive;
exclusive = rhs.exclusive;
arguments = rhs.arguments;
ifunused = rhs.ifunused;
ifempty = rhs.ifempty;
messageCount = rhs.messageCount;
consumerCount = rhs.consumerCount;
return *this;
}
};
#endif // QUEUEOPTION_H

View File

@ -0,0 +1,37 @@
#include "producemessage.h"
// Конструктор по умолчанию
ProduceMessage::ProduceMessage()
{
_body.clear();
}
// Конструктор копирования
ProduceMessage::ProduceMessage(const ProduceMessage& msg)
{
this->_body = msg._body;
this->_headers = msg._headers;
this->_properties = msg._properties;
}
ProduceMessage& ProduceMessage::operator=(const ProduceMessage& msg)
{
if (this != &msg) {
this->_body = msg._body;
this->_headers = msg._headers;
this->_properties = msg._properties;
}
return *this;
}
QByteArray ProduceMessage::getBodyMsg() const { return _body; }
void ProduceMessage::setBodyMsg(const QByteArray &ba) { _body = ba; }
void ProduceMessage::setBodyMsg(const char *body, int size) { _body = QByteArray(body, size); }
Headers ProduceMessage::getHeaders() const { return _headers; }
void ProduceMessage::setHeaders(const Headers &headers) { _headers = headers; }
Properties ProduceMessage::getProperties() const { return _properties; }
void ProduceMessage::setProperties(const Properties &properties) { _properties = properties; }

View File

@ -0,0 +1,43 @@
#ifndef PRODUCEMESSAGE_H
#define PRODUCEMESSAGE_H
#include <QByteArray>
#include "properties.h"
#include "headers.h"
class ProduceMessage
{
public:
ProduceMessage();
ProduceMessage(const ProduceMessage& msg);
~ProduceMessage() {}
ProduceMessage& operator=(const ProduceMessage& msg);
QByteArray getBodyMsg() const;
void setBodyMsg(const QByteArray &ba);
void setBodyMsg(const char *body, int size);
Headers getHeaders() const;
void setHeaders(const Headers &headers);
Properties getProperties() const;
void setProperties(const Properties &properties);
private:
QByteArray _body;
protected:
Properties _properties;
Headers _headers;
};
using PtrProduceMessage = ProduceMessage*;
Q_DECLARE_METATYPE(ProduceMessage)
Q_DECLARE_METATYPE(PtrProduceMessage)
#endif // PRODUCEMESSAGE_H

View File

@ -0,0 +1,63 @@
#include "properties.h"
Properties::Properties()
{
_properties.clear();
setDeliveryMode(1); // не оставлять сообщения
}
// Конструктор копирования
Properties::Properties(const Properties& p)
{
this->_properties = p._properties;
}
void Properties::operator=(const Properties& p)
{
this->_properties = p._properties;
}
const QMap<QString,QVariant> &Properties::getProperties() { return _properties; }
bool Properties::contains(const QString name) const { return _properties.contains(name); }
bool Properties::isContentType() const { return _properties.contains("content-type"); }
bool Properties::isContentEncoding() const { return _properties.contains("content-encoding"); }
bool Properties::isMessageID() const { return _properties.contains("message-id"); }
bool Properties::isCorrelationID() const { return _properties.contains("correlation-id"); }
bool Properties::isTimestamp() const { return _properties.contains("timestamp"); }
bool Properties::isExpiration() const { return _properties.contains("expiration"); }
bool Properties::isDeliveryMode() const { return _properties.contains("delivery-mode"); }
bool Properties::isAppID() const { return _properties.contains("app-id"); }
bool Properties::isUserID() const { return _properties.contains("user-id"); }
bool Properties::isTypeName() const { return _properties.contains("type"); }
bool Properties::isReplyTo() const { return _properties.contains("reply-to"); }
bool Properties::isPriority() const { return _properties.contains("priority"); }
void Properties::setContentType(const QString &str) { _properties.insert("content-type", str); }
void Properties::setContentEncoding(const QString &str) { _properties.insert("content-encoding", str); }
void Properties::setMessageID(const QString &str) { _properties.insert("message-id", str); }
void Properties::setCorrelationID(const QString &str) { _properties.insert("correlation-id", str); }
void Properties::setTimestamp(const quint64 val) { _properties.insert("timestamp", val); }
void Properties::setExpiration(const QString &str) { _properties.insert("expiration", str); }
void Properties::setDeliveryMode(const quint8 val) { _properties.insert("delivery-mode", val); }
void Properties::setAppID(const QString &str) { _properties.insert("app-id", str); }
void Properties::setUserID(const QString &str) { _properties.insert("user-id", str); }
void Properties::setTypeName(const QString &str) { _properties.insert("type", str); }
void Properties::setReplyTo(const QString &str) { _properties.insert("reply-to", str); }
void Properties::setPriority(const quint8 val) { _properties.insert("priority", val); }
QString Properties::getContentType() const { return _properties["content-type"].value<QString>(); }
QString Properties::getContentEncoding() const { return _properties["content-encoding"].value<QString>(); }
QString Properties::getMessageID() const { return _properties["message-id"].value<QString>(); }
QString Properties::getCorrelationID() const { return _properties["correlation-id"].value<QString>(); }
quint64 Properties::getTimestamp() const { return _properties["timestamp"].value<quint64>(); }
QString Properties::getExpiration() const { return _properties["expiration"].value<QString>(); }
quint8 Properties::getDeliveryMode() const { return _properties["delivery-mode"].value<quint8>(); }
QString Properties::getAppID() const { return _properties["app-id"].value<QString>(); }
QString Properties::getUserID() const { return _properties["user-id"].value<QString>(); }
QString Properties::getTypeName() const { return _properties["type"].value<QString>(); }
QString Properties::getReplyTo() const { return _properties["reply-to"].value<QString>(); }
quint8 Properties::getPriority() const { return _properties["priority"].value<quint8>(); }

View File

@ -0,0 +1,73 @@
#ifndef PROPERTIES_H
#define PROPERTIES_H
#include <QtGlobal>
#include <QMap>
#include <QString>
#include <QVariant>
class Properties
{
private:
QMap<QString,QVariant> _properties;
public:
Properties();
Properties(const Properties& p);
~Properties() {}
void operator=(const Properties& p);
int size() {return _properties.size(); }
const QMap<QString,QVariant> &getProperties();
bool contains(const QString name) const;
bool isContentType() const;
bool isContentEncoding() const;
bool isMessageID() const;
bool isCorrelationID() const;
bool isTimestamp() const;
bool isExpiration() const;
bool isDeliveryMode() const;
bool isAppID() const;
bool isUserID() const;
bool isTypeName() const;
bool isReplyTo() const;
bool isPriority() const;
void setContentType(const QString &str);
void setContentEncoding(const QString &str);
void setMessageID(const QString &str);
void setCorrelationID(const QString &str);
void setTimestamp(const quint64 val);
void setExpiration(const QString &str);
void setDeliveryMode(const quint8 val);
void setAppID(const QString &str);
void setUserID(const QString &str);
void setTypeName(const QString &str);
void setReplyTo(const QString &str);
void setPriority(const quint8 val);
QString getContentType() const;
QString getContentEncoding() const;
QString getMessageID() const;
QString getCorrelationID() const;
quint64 getTimestamp() const;
QString getExpiration() const;
quint8 getDeliveryMode() const;
QString getAppID() const;
QString getUserID() const;
QString getTypeName() const;
QString getReplyTo() const;
quint8 getPriority() const;
};
#endif // PROPERTIES_H

View File

@ -0,0 +1,330 @@
/**************************************************************************
* PWorker - Publish Worker, - рабочий поток публикации сообщений *
* редакция от 08.06.2022 *
* Принадлежность: библиотека clientRBcpp *
**************************************************************************/
#include <QCoreApplication>
#include "_client_.h"
#include "sender.h"
#include "pworker.h"
#include <QDebug>
PWorker::PWorker(ClientOption& clientOption, ExchangeOption& exchangeOption)
: QObject(nullptr), markStop(false)
{
this->clientOption = clientOption;
this->exchangeOption = exchangeOption;
qu.clear();
// static const int idE2E = qRegisterMetaType<E2EStruct>();
// Q_UNUSED(idE2E)
}
PWorker::~PWorker()
{
// Освободим очередь сообщений
mutex.lock();
while (! qu.isEmpty()) {
PublishPacket *packet = qu.dequeue();
AMQP::Envelope *envelope = packet->envelope;
delete envelope->body();
delete envelope;
delete packet;
}
mutex.unlock();
}
// Здесь реализуется основная деятельность потока
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void PWorker::doWork()
{
string host = clientOption.host.toStdString();
string port = QString::number(clientOption.port).toStdString();
string user = clientOption.user.toStdString();
string password = clientOption.password.toStdString();
string vhost = clientOption.vhost.toStdString();
string address = "amqp://" + user + ":" + password + "@" + host + ":" + port + vhost;
// Обрабатываем аргументы на предмет альтернативной точки обмена
AMQP::Table table;
QString alt_e_name = "alternate-exchange";
QString alt_e_value = "";
if (exchangeOption.arguments.contains(alt_e_name)) {
alt_e_value = exchangeOption.arguments[alt_e_name].value<QString>();
table.set(alt_e_name.toStdString(), alt_e_value.toStdString());
}
string alt_exchange = alt_e_value.toStdString(); // Имя альтернативной точки обмена
AMQP::ExchangeType typeAltEx = AMQP::fanout; // Тип альтернативной точки обмена - всегда fanout
int flagsAltEx = (AMQP::durable | AMQP::internal); // Точка долгоживущая и внутренняя
auto evbase = event_base_new();
AMQP::LibEventHandler handler(evbase);
AMQP::TcpConnection connection(&handler, AMQP::Address(address));
AMQP::TcpChannel channel(&connection);
channel.setQos(1);
channel.confirmSelect()
.onAck([&](uint64_t deliveryTag, bool multiple) {
emit onReceivedAckNack(deliveryTag, true, multiple);
})
.onNack([&](uint64_t deliveryTag, bool multiple, bool requeue) {
Q_UNUSED(requeue)
emit onReceivedAckNack(deliveryTag, false, multiple);
});
// Объявляем альтернативную точку обмена
//--------------------------------------
if (alt_e_value != "") {
channel.declareExchange(alt_exchange, typeAltEx, flagsAltEx)
.onError( [&](const char *message) {
string msg(message);
emit onError(msg);
});
}
// Обработка основной точки обмена
//----------------------------------
string exchange = exchangeOption.name.toStdString();
string type = exchangeOption.type.toStdString();
// преобразование типа точки обмена в формат AMQP
AMQP::ExchangeType typeEx;
if (type == "" || type == "direct")
typeEx = AMQP::direct;
else if (type == "topic")
typeEx = AMQP::topic;
else if (type == "headers")
typeEx = AMQP::headers;
else
typeEx = AMQP::fanout;
// предобразование флагов точки обмена в формат AMQP
int flagsExchange = 0;
if (exchangeOption.durable)
flagsExchange |= AMQP::durable;
if (exchangeOption.auto_delete)
flagsExchange |= AMQP::autodelete;
if (exchangeOption.internal)
flagsExchange |= AMQP::internal;
// Для предопределенных точек обмена их обьявление не производим
if ( exchange != "" && exchange != "amq.fanout" && exchange != "amq.direct" &&
exchange != "amq.topic" && exchange != "amq.headers") {
channel.declareExchange(exchange, typeEx, flagsExchange, table)
.onError( [&](const char *message) {
string msg(message);
emit onError(msg);
});
}
// обработка mandatory
auto messageCb = [&](const AMQP::Message& message, int16_t code, const std::string &description)
{
Q_UNUSED(code)
Q_UNUSED(description)
// Формируем принятое сообщениев формате ReceivedMessage
ProduceMessage *rMsg = new ProduceMessage;
rMsg->setBodyMsg(message.body(), message.bodySize());
// Формируем таблицу Properties свойств сообщения
Properties p;
if (message.hasContentType())
p.setContentType(QString::fromStdString(message.contentType()));
if (message.hasContentEncoding())
p.setContentEncoding(QString::fromStdString(message.contentEncoding()));
if (message.hasMessageID())
p.setMessageID(QString::fromStdString(message.messageID()));
if (message.hasCorrelationID())
p.setCorrelationID(QString::fromStdString(message.correlationID()));
if (message.timestamp())
p.setTimestamp(message.timestamp());
if (message.hasExpiration())
p.setExpiration(QString::fromStdString(message.expiration()));
if (message.hasDeliveryMode())
p.setDeliveryMode(message.deliveryMode());
if (message.hasAppID())
p.setAppID(QString::fromStdString(message.appID()));
if (message.hasUserID())
p.setUserID(QString::fromStdString(message.userID()));
if (message.hasTypeName())
p.setTypeName(QString::fromStdString(message.typeName()));
if (message.hasReplyTo())
p.setReplyTo(QString::fromStdString(message.replyTo()));
if (message.hasPriority())
p.setPriority(message.priority());
rMsg->setProperties(p);
// Работа со свойствами Headers
Headers h;
AMQP::Table table = message.headers();
vector<string> keys = table.keys();
string name;
for(uint i = 0; i < keys.size(); i++) {
name = keys[i];
if (table.get(name).isInteger()) {
int value = table.get(name);
h.set(QString::fromStdString(name), value);
}
else if (table.get(name).isString()) {
QString str = QString::fromStdString(table.get(name));
h.set(QString::fromStdString(name), str);
}
else if (table.get(name).isBoolean()) {
AMQP::Field &b = const_cast<AMQP::Field &>(table.get(name));
AMQP::BooleanSet &b1 = dynamic_cast<AMQP::BooleanSet &>(b);
bool value = b1.get(0);
h.set(QString::fromStdString(name), value);
}
}
rMsg->setHeaders(h);
emit onMessageBounced(rMsg);
};
channel.recall().onReceived(messageCb);
// Цикл событий (event loop)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
while (! markStop) {
// Обрабатываем очередь сообщений на передачу
if (! qu.isEmpty()) {
mutex.lock();
PublishPacket *packet = qu.dequeue();
mutex.unlock();
AMQP::Envelope *envelope = packet->envelope;
int publishFlags = packet->publishFlags;
string routingKey = packet->routingKey;
if (envelope->hasAppID() && envelope->appID() == HEARBEATS)
connection.heartbeat();
else
channel.publish(exchange, routingKey, *envelope, publishFlags);
delete envelope->body();
delete envelope;
delete packet;
}
event_base_loop(evbase, EVLOOP_NONBLOCK);
QCoreApplication::processEvents();
QThread::msleep(0);
} //while
// Закроем канал и соединение
channel.close();
connection.close();
event_base_loopbreak(evbase);
event_base_loopexit(evbase, 0);
event_base_free(evbase);
emit onWorkFinished(); // Посылаем сигнал о завершении работы
}
// Прием данных, предразование в формат передачи
// и постановка в очередь на выдачу
void PWorker::sending(ProduceMessage msg, QString routingKey, bool mandatory)
{
mutex.lock();
uint64_t size = msg.getBodyMsg().size();
char *body = new char[size];
memcpy(body, msg.getBodyMsg().data(), size);
AMQP::Envelope *env = new AMQP::Envelope(body, size);
// Готовим сообщение для отправки
Properties p = msg.getProperties();
if (p.contains("content-type"))
env->setContentType(p.getContentType().toStdString().c_str());
if (p.contains("content-encoding"))
env->setContentEncoding(p.getContentEncoding().toStdString().c_str());
if (p.contains("message-id"))
env->setMessageID(p.getMessageID().toStdString().c_str());
if (p.contains("correlation-id"))
env->setCorrelationID(p.getCorrelationID().toStdString().c_str());
if (p.contains("timestamp"))
env->setTimestamp(p.getTimestamp());
if (p.contains("expiration"))
env->setExpiration(p.getExpiration().toStdString().c_str());
if (p.contains("delivery-mode"))
env->setDeliveryMode(p.getDeliveryMode());
if (p.contains("app-id"))
env->setAppID(p.getAppID().toStdString().c_str());
if (p.contains("user-id"))
env->setUserID(p.getUserID().toStdString().c_str());
if (p.contains("type"))
env->setTypeName(p.getTypeName().toStdString().c_str());
if (p.contains("reply-to"))
env->setReplyTo(p.getReplyTo().toStdString().c_str());
if (p.contains("priority"))
env->setPriority(p.getPriority());
AMQP::Table table;
Headers p2 = msg.getHeaders();
QList<QString> k = p2.keys();
QList<QVariant> v = p2.values();
for (int i=0; i < p2.size(); i++) {
QString name = k[i];
QVariant val = v[i];
if (val.type() == QVariant::Int) {
AMQP::Long numb = val.value<int>();
table.set(name.toStdString(), numb.value());
}
else if (val.type() == QVariant::String) {
QString str = val.value<QString>();
AMQP::ShortString s(str.toStdString());
table.set(name.toStdString(), s.value());
}
else if (val.type() == QVariant::Bool) {
bool numb = val.value<bool>();
table.set(name.toStdString(), numb);
}
}
env->setHeaders(table);
int flags = 0; // флаги - в формат AMQP
if (mandatory)
flags |= AMQP::mandatory;
string routing = routingKey.toStdString();
// формируем пакет для постановки в очередь
PublishPacket *pp = new PublishPacket;
pp->envelope = env;
pp->publishFlags = flags;
pp->routingKey = routing;
qu.enqueue(pp);
mutex.unlock();
}
void PWorker::stop()
{
markStop = true; // завершить цикл обработки сообщений
}

View File

@ -0,0 +1,58 @@
#ifndef PWORKER_H
#define PWORKER_H
#include <QMutex>
#include <QObject>
#include <QQueue>
#include <QString>
#include <QVariant>
#include <iostream>
#include <amqpcpp.h>
#include <amqpcpp/libevent.h>
#include "client_cpp.h"
using namespace std;
struct PublishPacket {
AMQP::Envelope *envelope;
string routingKey;
int publishFlags;
};
class PWorker : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
ExchangeOption exchangeOption;
bool markStop;
QMutex mutex;
QQueue<PublishPacket *> qu;
public:
PWorker(ClientOption& clientOption, ExchangeOption& exchangeOption);
virtual ~PWorker();
public slots:
void doWork();
void sending(ProduceMessage msg, QString routingKey, bool mandatory=false);
void stop();
signals:
void onMessageBounced(PtrProduceMessage msg);
void onError(string msg);
void onReceivedAckNack(uint64_t deliveryTag, bool ack, bool multiple);
void onWorkFinished();
};
#endif // PWORKER_H

View File

@ -0,0 +1,65 @@
#include <QCoreApplication>
#include "client_cpp.h"
Receiver::Receiver(ClientOption &clientOption, ConsumeOption &consumeOption)
: QObject(nullptr)
{
this->clientOption = clientOption;
this->consumeOption = consumeOption;
this->consumeOption.receivingExchange = consumeOption.receivingExchange;
this->consumeOption.bindingArgs = consumeOption.bindingArgs;
CWorker *worker = new CWorker(this->clientOption, this->consumeOption);
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::started, worker, &CWorker::doWork);
connect(&workerThread, &QThread::finished, worker, &CWorker::slotStop);
// Автоматическое удаление объектов PWorker и QThread по окончании работы
connect(worker, &CWorker::onWorkFinished, worker, &CWorker::deleteLater);
// Посылаемые потоку сигналы
connect(this, &Receiver::onStop, worker, &CWorker::slotStop, Qt::DirectConnection);
connect(this, &Receiver::doBind, worker, &CWorker::bind, Qt::DirectConnection);
connect(this, &Receiver::doUnbind, worker, &CWorker::unbind, Qt::DirectConnection);
// Сигналы, принимаемые от потока
connect(worker, &CWorker::onWorkFinished, &workerThread, &QThread::quit, Qt::QueuedConnection);
connect(worker, &CWorker::onResultReady, this, &Receiver::slotMsgReady, Qt::QueuedConnection);
connect(worker, &CWorker::onErrorConsume, this, &Receiver::slotErrorMsg, Qt::QueuedConnection);
workerThread.start();
}
Receiver::~Receiver()
{
workerThread.quit();
workerThread.wait();
}
// При получении от потока сигнала о приеме сообщения
// выпускаем сигнал дальше
void Receiver::slotMsgReady(PtrProduceMessage msg, uint64_t deliveryTag)
{
ProduceMessage message = *msg;
delete msg;
emit onMessage(message, deliveryTag);
}
// При получении сигнала об ошибке транслируем его
void Receiver::slotErrorMsg(const char *description)
{
QString str(description);
emit onError(str);
}
void Receiver::slotStop()
{
emit onStop();
}

View File

@ -0,0 +1,47 @@
#ifndef RECEIVER_H
#define RECEIVER_H
#include <QObject>
#include <QList>
#include <QMap>
#include <QString>
#include <QTimer>
#include <QThread>
#include <QVariant>
#include "_client_.h"
#include "cworker.h"
using namespace std;
class Receiver : public QObject
{
Q_OBJECT
private:
bool stop;
ClientOption clientOption;
ConsumeOption consumeOption;
QThread workerThread;
public:
Receiver(ClientOption& clientOption, ConsumeOption& consumeOption);
virtual ~Receiver();
public slots:
void slotMsgReady(PtrProduceMessage msg, uint64_t deliveryTag);
void slotErrorMsg(const char *description);
void slotStop();
signals:
void onMessage(ProduceMessage msg, uint64_t deliveryTag);
void onError(QString description);
void onStop();
void doBind(QString exchange, QString key, bool ex);
void doUnbind(QString exchange, QString key, bool ex);
};
#endif // RECEIVER_H

View File

@ -0,0 +1,106 @@
#include <QCoreApplication>
#include "client_cpp.h"
Sender::Sender(ClientOption& clientOption, ExchangeOption& exchangeOption)
: QObject(nullptr)
{
this->clientOption = clientOption;
this->exchangeOption = exchangeOption;
PWorker *worker = new PWorker(this->clientOption, this->exchangeOption);
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::started, worker, &PWorker::doWork);
// Автоматическое удаление объектов PWorker и QThread по окончании работы
connect(worker, &PWorker::onWorkFinished, worker, &PWorker::deleteLater);
// Посылаемые потоку сигналы
connect(this, &Sender::onSend, worker, &PWorker::sending, Qt::QueuedConnection);
connect(this, &Sender::onStop, worker, &PWorker::stop, Qt::DirectConnection);
// Сигналы, принимаемые от потока
connect(worker, &PWorker::onWorkFinished, &workerThread, &QThread::quit, Qt::QueuedConnection);
connect(worker, &PWorker::onReceivedAckNack, this, &Sender::slotAckNack, Qt::QueuedConnection);
connect(worker, &PWorker::onError, this, &Sender::slotError, Qt::QueuedConnection);
connect(worker, &PWorker::onMessageBounced, this, &Sender::slotMsgBounced, Qt::QueuedConnection);
workerThread.start();
// Запуск таймера для механизма сердцебиения
tm.stop();
tm.setInterval(30000); // 0,5 мин
connect(&tm, &QTimer::timeout, this, &Sender::onTimer);
tm.start();
}
Sender::~Sender()
{
tm.stop();
workerThread.quit();
workerThread.wait();
}
// Периодическое подключение по таймеру (1 мин)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Sender::onTimer()
{
tm.stop();
// Формируем и отправляем служебное сообщение
// на проверку соединения с экземпляром RabbitMQ
string str = "@@"; // содержимое не играет роли
ProduceMessage msg;
msg.setBodyMsg(str.c_str(), str.size());
Properties p;
QString hearbeats(HEARBEATS);
p.setAppID(hearbeats); // маркер служебного сообщения сердцебиения
msg.setProperties(p);
QString routingKey = "";
emit onSend(msg, routingKey); // сообщение передаем в поток для передачи серверу
tm.start();
}
void Sender::slotMsgBounced(PtrProduceMessage msg)
{
ProduceMessage message = *msg;
delete msg;
emit onMsgBounced(message);
}
// Передаем сообщение в поток для выдачи
void Sender::send(ProduceMessage msg, QString routingKey, bool mandatory)
{
emit onSend(msg, routingKey, mandatory);
}
// Прием подтверждения от потока о выдаче (или невыдаче) сообщения
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-------~~~~~~~~~~~~~~------------
void Sender::slotAckNack(uint64_t deliveryTag, bool ack, bool multiple)
{
emit onReceivedAckNack(deliveryTag, ack, multiple);
}
void Sender::slotError(string msg)
{
QString message = QString::fromStdString(msg);
emit onError(message);
}
void Sender::slotStop()
{
emit onStop();
}

View File

@ -0,0 +1,58 @@
#ifndef SENDER_H
#define SENDER_H
#include <QMap>
#include <QMetaType>
#include <QObject>
#include <QString>
#include <QThread>
#include <QTimer>
#include <QVariant>
#include <iostream>
#include "client_cpp.h"
using namespace std;
class Client;
class PWorker;
class Sender : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
ExchangeOption exchangeOption;
QThread workerThread;
QTimer tm;
public:
Sender(ClientOption& clientOption, ExchangeOption& exchangeOption);
virtual ~Sender();
void send(ProduceMessage msg, QString routingKey, bool mandatory=false);
private slots:
void onTimer();
public slots:
void slotMsgBounced(PtrProduceMessage msg);
void slotStop();
void slotAckNack(uint64_t deliveryTag, bool ack, bool multiple);
void slotError(string msg);
signals:
void onMsgBounced(ProduceMessage msg);
void onReceivedAckNack(uint64_t deliveryTag, bool ack, bool multiple);
void onSend(ProduceMessage msg, QString routingKey, bool mandatory=false); // Отправка сообщения потоку
void onError(QString &msg);
void onStop();
};
#endif // SENDER_H

View File

@ -0,0 +1,54 @@
#include "validator.h"
#include <QDebug>
Validator::Validator(ClientOption& clientOption, QList<ExchangeOption>& exchangeOptions)
: QObject(nullptr)
{
// класс запускает поток с валидатором, который в течение 5 секунд должен проверить инфраструктуру.
// любая ошибка приведёт к завершению работы программы. если ошибок не было перехвачено, через 5 секунд процесс завершается.
this->clientOption = clientOption;
this->exchangeOptions = exchangeOptions;
VWorker *worker = new VWorker(this->clientOption, this->exchangeOptions);
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::started, worker, &VWorker::doWork);
// Автоматическое удаление объектов VWorker и QThread по окончании работы
connect(worker, &VWorker::onWorkFinished, worker, &VWorker::deleteLater);
// Сигналы, принимаемые от потока
connect(worker, &VWorker::onWorkFinished, &workerThread, &QThread::quit, Qt::QueuedConnection);
connect(worker, &VWorker::onError, this, &Validator::slotError, Qt::QueuedConnection);
workerThread.start();
// Запуск таймера для механизма сердцебиения
tm.stop();
tm.setInterval(5000); // 5 сек
connect(&tm, &QTimer::timeout, worker, &VWorker::slotStop);
connect(&tm, &QTimer::timeout, this, &Validator::onTimer);
tm.start();
}
Validator::~Validator()
{
tm.stop();
workerThread.quit();
}
void Validator::onTimer()
{
tm.stop();
workerThread.quit();
}
void Validator::slotError(string msg)
{
QString message = QString::fromStdString(msg);
emit onError(message);
}

View File

@ -0,0 +1,41 @@
#ifndef VALIDATOR_H
#define VALIDATOR_H
#include <QMap>
#include <QMetaType>
#include <QObject>
#include <QString>
#include <QThread>
#include <QTimer>
#include <QVariant>
#include <iostream>
#include "client_cpp.h"
class Validator : public QObject
{
Q_OBJECT
public:
Validator(ClientOption& clientOption, QList<ExchangeOption>& exchangeOptions);
virtual ~Validator();
private:
ClientOption clientOption;
QList<ExchangeOption> exchangeOptions;
QThread workerThread;
QTimer tm;
private slots:
void onTimer();
public slots:
void slotError(string msg);
signals:
void onError(QString &msg);
void onStop();
};
#endif // VALIDATOR_H

View File

@ -0,0 +1,123 @@
#include <QCoreApplication>
#include "_client_.h"
#include "sender.h"
#include "vworker.h"
#include <QDebug>
VWorker::VWorker(ClientOption &clientOption, QList<ExchangeOption>& exchangeOptions)
: QObject(nullptr), markStop(false)
{
this->clientOption = clientOption;
this->exchangeOptions = exchangeOptions;
}
VWorker::~VWorker()
{
}
void VWorker::doWork()
{
string host = clientOption.host.toStdString();
string port = QString::number(clientOption.port).toStdString();
string user = clientOption.user.toStdString();
string password = clientOption.password.toStdString();
string vhost = clientOption.vhost.toStdString();
string address = "amqp://" + user + ":" + password + "@" + host + ":" + port + vhost;
// Создаём подключение
auto evbase = event_base_new();
AMQP::LibEventHandler handler(evbase);
AMQP::TcpConnection connection(&handler, AMQP::Address(address));
AMQP::TcpChannel channel(&connection);
channel.setQos(1);
channel.confirmSelect()
.onError([&](const char *message) {
qDebug() << "validator - connecting error: " << message;
});
// Обрабатываем список точек обмена
string exch;
string exType;
AMQP::ExchangeType typeExch;
int flagsExch = 0;
QString ex_alt_e_name = "alternate-exchange";
QString ex_alt_e_value = "";
for(ExchangeOption exOpt : exchangeOptions) {
AMQP::Table exTable;
exch = exOpt.name.toStdString();
exType = exOpt.type.toStdString();
// преобразование типа точки обмена в формат AMQP
if (exType == "" || exType == "direct")
typeExch = AMQP::direct;
else if (exType == "topic")
typeExch = AMQP::topic;
else if (exType == "headers")
typeExch = AMQP::headers;
else
typeExch = AMQP::fanout;
// предобразование флагов точки обмена в формат AMQP
if (exOpt.durable)
flagsExch |= AMQP::durable;
if (exOpt.auto_delete)
flagsExch |= AMQP::autodelete;
if (exOpt.internal)
flagsExch |= AMQP::internal;
if (exOpt.arguments.contains(ex_alt_e_name)) {
ex_alt_e_value = exOpt.arguments[ex_alt_e_name].value<QString>();
exTable.set(ex_alt_e_name.toStdString(), ex_alt_e_value.toStdString());
}
//Для предопределенных точек обмена их обьявление не производим
if ( exch != "" && exch != "amq.fanout" && exch != "amq.direct" &&
exch != "amq.topic" && exch != "amq.headers") {
channel.declareExchange(exch, typeExch, flagsExch, exTable)
.onError( [&](const char *message) {
qDebug() << "validator - declaring error: " << message;
emit onError(message);
});
}
QMap<QString, QString>::iterator it = exOpt.bindingArgs.begin();
for (; it != exOpt.bindingArgs.end(); ++it) {
channel.bindExchange(exch, it.key().toStdString(), it.value().toStdString())
.onError( [&](const char *message) {
qDebug() << "validator - binding error: " << message;
emit onError(message);
});
}
}
while (! markStop) {
event_base_loop(evbase, EVLOOP_NONBLOCK);
QCoreApplication::processEvents();
QThread::msleep(0);
}
// Закроем канал и соединение
channel.close();
connection.close();
event_base_loopbreak(evbase);
event_base_loopexit(evbase, 0);
event_base_free(evbase);
}
void VWorker::slotStop()
{
markStop = true;
}

View File

@ -0,0 +1,43 @@
#ifndef VWORKER_H
#define VWORKER_H
#include <QObject>
#include <QQueue>
#include <QString>
#include <QVariant>
#include <iostream>
#include <amqpcpp.h>
#include <amqpcpp/libevent.h>
#include "client_cpp.h"
using namespace std;
class VWorker : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
QList<ExchangeOption> exchangeOptions;
bool markStop;
QMutex mutex;
public:
VWorker(ClientOption& clientOption, QList<ExchangeOption>& exchangeOptions);
virtual ~VWorker();
public slots:
void doWork();
void slotStop();
signals:
void onError(string msg);
void onWorkFinished();
};
#endif // VWORKER_H

View File

@ -0,0 +1,41 @@
QT += core gui widgets
LIBS += -L/usr/lib -lamqpcpp -L/usr/lib/x86_64-linux-gnu/ -levent -lpthread -ldl
SOURCES += \
clientRBcpp/client.cpp \
clientRBcpp/clientrbcpp.cpp \
clientRBcpp/cworker.cpp \
clientRBcpp/headers.cpp \
clientRBcpp/producemessage.cpp \
clientRBcpp/properties.cpp \
clientRBcpp/pworker.cpp \
clientRBcpp/receiver.cpp \
clientRBcpp/sender.cpp \
clientRBcpp/validator.cpp \
clientRBcpp/vworker.cpp \
main.cpp \
mainwindow.cpp
HEADERS += \
clientRBcpp/_client_.h \
clientRBcpp/client_cpp.h \
clientRBcpp/clientrbcpp.h \
clientRBcpp/clientrbcpp_global.h \
clientRBcpp/cworker.h \
clientRBcpp/headers.h \
clientRBcpp/options/clientoption.h \
clientRBcpp/options/consumeoption.h \
clientRBcpp/options/exchangeoption.h \
clientRBcpp/options/queueoption.h \
clientRBcpp/producemessage.h \
clientRBcpp/properties.h \
clientRBcpp/pworker.h \
clientRBcpp/receiver.h \
clientRBcpp/sender.h \
clientRBcpp/validator.h \
clientRBcpp/vworker.h \
mainwindow.h
FORMS += \
mainwindow.ui

View File

@ -0,0 +1,338 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.12.3, 2023-12-23T19:03:36. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{77607214-f3f8-45c8-bf65-1a310ea854a8}</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.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="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="bool" key="EditorConfiguration.inEntireDocument">false</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuelist type="QVariantList" key="ClangCodeModel.CustomCommandLineKey"/>
<value type="bool" key="ClangCodeModel.UseGlobalConfig">true</value>
<value type="QString" key="ClangCodeModel.WarningConfigId">Builtin.Questionable</value>
<valuemap type="QVariantMap" key="ClangTools">
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
<value type="int" key="ClangTools.ParallelJobs">0</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="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{fa463890-d98c-43fb-aee8-64b3da65bdfc}</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="bool">true</value>
<value type="int" key="EnableQmlDebugging">0</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_fast-Desktop-Debug</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_fast-Desktop-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"></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>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Сборка</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>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Очистка</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.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Отладка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">2</value>
<value type="int" key="QtQuickCompiler">2</value>
<value type="int" key="SeparateDebugInfo">2</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
<value type="bool">true</value>
<value type="int" key="EnableQmlDebugging">2</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_fast-Desktop-Release</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_fast-Desktop-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="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></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>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Сборка</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>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Очистка</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.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Выпуск</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">2</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.2">
<value type="bool">true</value>
<value type="int" key="EnableQmlDebugging">0</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_fast-Desktop-Profile</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_fast-Desktop-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="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></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>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Сборка</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>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Очистка</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.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Профилирование</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">Развёртывание</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Развёртывание</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.PluginSettings"/>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="QString" key="Analyzer.Perf.CallgraphMode">dwarf</value>
<valuelist type="QVariantList" key="Analyzer.Perf.Events">
<value type="QString">cpu-cycles</value>
</valuelist>
<valuelist type="QVariantList" key="Analyzer.Perf.ExtraArguments"/>
<value type="int" key="Analyzer.Perf.Frequency">250</value>
<valuelist type="QVariantList" key="Analyzer.Perf.RecordArguments">
<value type="QString">-e</value>
<value type="QString">cpu-cycles</value>
<value type="QString">--call-graph</value>
<value type="QString">dwarf,4096</value>
<value type="QString">-F</value>
<value type="QString">250</value>
</valuelist>
<value type="QString" key="Analyzer.Perf.SampleMode">-F</value>
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Perf.StackSize">4096</value>
<value type="bool" key="Analyzer.QmlProfiler.AggregateTraces">false</value>
<value type="bool" key="Analyzer.QmlProfiler.FlushEnabled">false</value>
<value type="uint" key="Analyzer.QmlProfiler.FlushInterval">1000</value>
<value type="QString" key="Analyzer.QmlProfiler.LastTraceFile"></value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
<value type="QString" key="Analyzer.Valgrind.KCachegrindExecutable">kcachegrind</value>
<value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
<value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
<value type="int">0</value>
<value type="int">1</value>
<value type="int">2</value>
<value type="int">3</value>
<value type="int">4</value>
<value type="int">5</value>
<value type="int">6</value>
<value type="int">7</value>
<value type="int">8</value>
<value type="int">9</value>
<value type="int">10</value>
<value type="int">11</value>
<value type="int">12</value>
<value type="int">13</value>
<value type="int">14</value>
</valuelist>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/consumer_fast/consumer_fast.pro</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/consumer_fast/consumer_fast.pro</value>
<value type="QString" key="RunConfiguration.Arguments"></value>
<value type="bool" key="RunConfiguration.Arguments.multi">false</value>
<value type="QString" key="RunConfiguration.OverrideDebuggerStartup"></value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
<value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory"></value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_fast-Desktop-Debug</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>

View File

@ -0,0 +1,11 @@
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}

View File

@ -0,0 +1,31 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ClientOption clOpt;
ConsumeOption conOpt;
conOpt.exchange = "publisher";
conOpt.bindingKeys << "all";
conOpt.queueOption.name = "queue_fast";
conOpt.queueOption.auto_delete = true;
receiver = new Receiver(clOpt, conOpt);
QObject::connect(receiver, &Receiver::onMessage, this, [&](ProduceMessage msg, uint64_t consumeTag) {
Q_UNUSED(consumeTag)
QString msg_body = "got " + QString::fromLocal8Bit(msg.getBodyMsg());
ui->listWidget->addItem(msg_body);
});
}
MainWindow::~MainWindow()
{
delete ui;
}

View File

@ -0,0 +1,26 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "clientRBcpp/client_cpp.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
Receiver *receiver;
int counter;
};
#endif // MAINWINDOW_H

View File

@ -0,0 +1,37 @@
<?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>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QListWidget" name="listWidget"/>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>30</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

Binary file not shown.

View File

@ -0,0 +1,53 @@
#ifndef _CLIENT_H
#define _CLIENT_H
#include <QList>
#include <QMap>
#include <QObject>
#include <QString>
#include <QVariant>
#include "options/clientoption.h"
#include "options/consumeoption.h"
#include "options/exchangeoption.h"
using namespace std;
#define HEARBEATS "@@__Control__@@"
class Sender;
class Receiver;
class Client : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
QList<Sender *> *_senders;
QList<Receiver *> *_receivers;
public:
Client();
Client(ClientOption option);
virtual ~Client();
Client& operator=(Client client);
QString getVersion() const;
ClientOption getClientOption() const { return clientOption; }
Sender *createSender(ExchangeOption& option);
void removeSender(Sender *sender);
Receiver *createReceiver(ConsumeOption& option);
void removeReceiver(Receiver *receiver);
signals:
void onStop();
};
Q_DECLARE_METATYPE(string)
#endif // _CLIENT_H

View File

@ -0,0 +1,124 @@
#include "_client_.h"
#include "sender.h"
#include "receiver.h"
// Конструктор для связи с локальным RabbitMQ
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Client::Client() : QObject(nullptr)
{
{
static const int idMsg = qRegisterMetaType<ProduceMessage>();
static const int idMsgPtr = qRegisterMetaType<PtrProduceMessage>();
static const int idString = qRegisterMetaType<string>();
Q_UNUSED(idMsg)
Q_UNUSED(idMsgPtr)
Q_UNUSED(idString)
}
_senders = new QList<Sender *>();
_senders->clear();
_receivers = new QList<Receiver *>();
_receivers->clear();
}
Client::Client(ClientOption option) : Client()
{
clientOption = option;
}
Client::~Client()
{
for (auto &sender : *_senders)
delete sender;
delete _senders;
for (auto &receiver : *_receivers)
delete receiver;
delete _receivers;
}
Client& Client::operator=(Client client)
{
if (this != &client) {
this->clientOption = client.clientOption;
this->_senders = new QList<Sender *>();
this->_senders->clear();
this->_receivers = new QList<Receiver *>();
this->_receivers->clear();
}
return *this;
}
int majorVersion = 1;
int minorVersion = 1;
int releaseVersion = 1;
QString Client::getVersion() const
{
return QString::number(majorVersion) +
"." + QString::number(minorVersion) +
"." + QString::number(releaseVersion);
}
// Создание публикатора (издателя) сообщений
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sender *Client::createSender(ExchangeOption& option)
{
Sender *sender = new Sender(clientOption, option);
connect(this, &Client::onStop, sender, &Sender::slotStop);
connect(this, &Client::onStop, sender, &Sender::deleteLater);
_senders->append(sender);
return sender;
}
void Client::removeSender(Sender *sender)
{
if ( !_senders->contains(sender))
return;
sender->slotStop();
_senders->removeOne(sender);
}
// Создание потребителя сообщений
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Receiver *Client::createReceiver(ConsumeOption& option)
{
Receiver *receiver = new Receiver(clientOption, option);
connect(this, &Client::onStop, receiver, &Receiver::slotStop);
connect(this, &Client::onStop, receiver, &Receiver::deleteLater);
_receivers->append(receiver);
return receiver;
}
void Client::removeReceiver(Receiver *receiver)
{
if ( !_receivers->contains(receiver))
return;
receiver->slotStop();
_receivers->removeOne(receiver);
}

View File

@ -0,0 +1,22 @@
#ifndef CLIENT_CPP_H
#define CLIENT_CPP_H
#include "clientrbcpp_global.h"
#include "clientrbcpp.h"
#include "_client_.h"
#include "producemessage.h"
#include "properties.h"
#include "cworker.h"
#include "headers.h"
#include "pworker.h"
#include "receiver.h"
#include "sender.h"
#include "validator.h"
#include "vworker.h"
#include "options/clientoption.h"
#include "options/consumeoption.h"
#include "options/exchangeoption.h"
#include "options/queueoption.h"
#endif // CLIENT_CPP_H

View File

@ -0,0 +1,6 @@
#include "clientrbcpp.h"
ClientRBcpp::ClientRBcpp()
{
}

View File

@ -0,0 +1,13 @@
#ifndef CLIENTRBCPP_H
#define CLIENTRBCPP_H
#include "clientrbcpp_global.h"
class CLIENTRBCPPSHARED_EXPORT ClientRBcpp
{
public:
ClientRBcpp();
};
#endif // CLIENTRBCPP_H

View File

@ -0,0 +1,12 @@
#ifndef CLIENTRBCPP_GLOBAL_H
#define CLIENTRBCPP_GLOBAL_H
#include <QtCore/qglobal.h>
#if defined(CLIENTRBCPP_LIBRARY)
# define CLIENTRBCPPSHARED_EXPORT Q_DECL_EXPORT
#else
# define CLIENTRBCPPSHARED_EXPORT Q_DECL_IMPORT
#endif
#endif // CLIENTRBCPP_GLOBAL_H

View File

@ -0,0 +1,278 @@
#include <QCoreApplication>
#include "client_cpp.h"
#include <QDebug>
CWorker::CWorker(ClientOption& clientOption, ConsumeOption& consumeOption)
: QObject(nullptr), markStop(false)
{
this->clientOption = clientOption;
this->consumeOption = consumeOption;
this->consumeOption.receivingExchange = consumeOption.receivingExchange;
connection = nullptr;
channel = nullptr;
}
CWorker::~CWorker()
{
}
// Здесь реализуется основная деятельность потока
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void CWorker::doWork()
{
string host = clientOption.host.toStdString();
string port = QString::number(clientOption.port).toStdString();
string user = clientOption.user.toStdString();
string password = clientOption.password.toStdString();
string vhost = clientOption.vhost.toStdString();
string address = "amqp://" + user + ":" + password + "@" + host + ":" + port + vhost;
auto evbase = event_base_new();
AMQP::LibEventHandler handler(evbase);
// AMQP::TcpConnection connection(&handler, AMQP::Address(address));
// AMQP::TcpChannel channel(&connection);
connection = new AMQP::TcpConnection(&handler, AMQP::Address(address));
channel = new AMQP::TcpChannel(connection);
channel->setQos(1);
channel->onError([&](const char *message) {
Q_UNUSED(message)
emit onErrorConsume("Channel error!!!");
});
QTimer tm;
tm.stop();
tm.setInterval(30000);
connect(&tm, &QTimer::timeout, this, [&]() {
tm.stop();
connection->heartbeat();
tm.start();
});
tm.start();
// Обработка принятого сообщения
auto messageCb = [&](const AMQP::Message& message, uint64_t deliveryTag, bool redelivered)
{
Q_UNUSED(redelivered)
// Формируем принятое сообщениев формате ReceivedMessage
ProduceMessage *rMsg = new ProduceMessage;
rMsg->setBodyMsg(message.body(), message.bodySize());
// Формируем таблицу Properties свойств сообщения
Properties p;
if (message.hasContentType())
p.setContentType(QString::fromStdString(message.contentType()));
if (message.hasContentEncoding())
p.setContentEncoding(QString::fromStdString(message.contentEncoding()));
if (message.hasMessageID())
p.setMessageID(QString::fromStdString(message.messageID()));
if (message.hasCorrelationID())
p.setCorrelationID(QString::fromStdString(message.correlationID()));
if (message.timestamp())
p.setTimestamp(message.timestamp());
if (message.hasExpiration())
p.setExpiration(QString::fromStdString(message.expiration()));
if (message.hasDeliveryMode())
p.setDeliveryMode(message.deliveryMode());
if (message.hasAppID())
p.setAppID(QString::fromStdString(message.appID()));
if (message.hasUserID())
p.setUserID(QString::fromStdString(message.userID()));
if (message.hasTypeName())
p.setTypeName(QString::fromStdString(message.typeName()));
if (message.hasReplyTo())
p.setReplyTo(QString::fromStdString(message.replyTo()));
if (message.hasPriority())
p.setPriority(message.priority());
rMsg->setProperties(p);
// Работа со свойствами Headers
Headers h;
AMQP::Table table = message.headers();
vector<string> keys = table.keys();
string name;
for(uint i = 0; i < keys.size(); i++) {
name = keys[i];
if (table.get(name).isInteger()) {
int value = table.get(name);
h.set(QString::fromStdString(name), value);
}
else if (table.get(name).isString()) {
QString str = QString::fromStdString(table.get(name));
h.set(QString::fromStdString(name), str);
}
else if (table.get(name).isBoolean()) {
AMQP::Field &b = const_cast<AMQP::Field &>(table.get(name));
AMQP::BooleanSet &b1 = dynamic_cast<AMQP::BooleanSet &>(b);
bool value = b1.get(0);
h.set(QString::fromStdString(name), value);
}
}
rMsg->setHeaders(h);
QThread::sleep(3);
emit onResultReady(rMsg, deliveryTag);
channel->ack(deliveryTag);
};
// объявление точки обмена
if (!consumeOption.receivingExchange.name.isEmpty()) {
string exchange = consumeOption.receivingExchange.name.toStdString();
string type = consumeOption.receivingExchange.type.toStdString();
// преобразование типа точки обмена в формат AMQP
AMQP::ExchangeType typeEx;
if (type == "" || type == "direct")
typeEx = AMQP::direct;
else if (type == "topic")
typeEx = AMQP::topic;
else if (type == "headers")
typeEx = AMQP::headers;
else
typeEx = AMQP::fanout;
// предобразование флагов точки обмена в формат AMQP
int flagsExchange = 0;
if (consumeOption.receivingExchange.durable)
flagsExchange |= AMQP::durable;
if (consumeOption.receivingExchange.auto_delete)
flagsExchange |= AMQP::autodelete;
if (consumeOption.receivingExchange.internal)
flagsExchange |= AMQP::internal;
AMQP::Table tableExch;
QString alt_e_name = "alternate-exchange";
QString alt_e_value = "";
if (consumeOption.receivingExchange.arguments.contains(alt_e_name)) {
alt_e_value = consumeOption.receivingExchange.arguments[alt_e_name].value<QString>();
tableExch.set(alt_e_name.toStdString(), alt_e_value.toStdString());
}
// Для предопределенных точек обмена их обьявление не производим
if ( exchange != "" && exchange != "amq.fanout" && exchange != "amq.direct" &&
exchange != "amq.topic" && exchange != "amq.headers") {
channel->declareExchange(exchange, typeEx, flagsExchange, tableExch)
.onError([&](const char *description) {
qDebug() << description;
});
}
QMultiMap<QString, QString>::iterator it = consumeOption.bindingArgs.begin();
for(; it != consumeOption.bindingArgs.end(); ++it) {
channel->bindExchange(it.key().toStdString(), exchange, it.value().toStdString()).onError([&](const char *description) {
qDebug() << description;
});
}
}
// объявление очереди
QueueOption option = consumeOption.queueOption;
string exchange = consumeOption.exchange.toStdString();
string queue = option.name.toStdString();
// Подготовка флагов для объявления очереди
int flagsQueue = 0;
if (option.durable)
flagsQueue |= AMQP::durable;
if (option.auto_delete)
flagsQueue |= AMQP::autodelete;
if (option.exclusive)
flagsQueue |= AMQP::exclusive;
channel->declareQueue(queue, flagsQueue)
.onSuccess( [&](const string &name, uint32_t messageCount, uint32_t consumerCount) {
Q_UNUSED(messageCount)
Q_UNUSED(consumerCount)
queue = name;
if (exchange != "")
for (QString rk : consumeOption.bindingKeys) {
channel->bindQueue(exchange, queue, rk.toStdString())
.onError( [&](const char *description) {
qDebug() << description;
});
}
});
// Подготовка флагов потребления сообщений
int flagsConsume = 0;
if (consumeOption.nolocal)
flagsConsume |= AMQP::nolocal;
if (consumeOption.noack)
flagsConsume |= AMQP::noack;
if (consumeOption.exclusive)
flagsConsume |= AMQP::exclusive;
channel->consume(queue, flagsConsume).onReceived(messageCb)
.onSuccess( [&](const string& tag) {
nextTag = tag;
})
.onError( [&](const char *description) {
emit onErrorConsume(description);
markStop = true; // Останов потока
});
//Цикл обработки событий
while(!markStop) {
event_base_loop(evbase, EVLOOP_ONCE);
QCoreApplication::processEvents();
}
// Закроем канал и соединение
channel->close();
connection->close();
event_base_loopbreak(evbase);
event_base_loopexit(evbase, 0);
event_base_free(evbase);
delete channel;
delete connection;
emit onWorkFinished(); // Посылаем сигнал о завершении работы
}
void CWorker::slotStop()
{
markStop = true;
channel->cancel(nextTag); // Отменить потребление
channel->close();
connection->close();
}
void CWorker::bind(QString exchange, QString key, bool ex)
{
if (ex) channel->bindExchange(exchange.toStdString(), consumeOption.exchange.toStdString(), key.toStdString());
else channel->bindQueue(exchange.toStdString(), consumeOption.queueOption.name.toStdString(), key.toStdString());
}
void CWorker::unbind(QString exchange, QString key, bool ex)
{
if (ex) channel->unbindExchange(exchange.toStdString(), consumeOption.exchange.toStdString(), key.toStdString());
else channel->unbindQueue(exchange.toStdString(), consumeOption.queueOption.name.toStdString(), key.toStdString());
}

View File

@ -0,0 +1,54 @@
#ifndef CWORKER_H
#define CWORKER_H
#include <QMap>
#include <QMutex>
#include <QObject>
#include <QString>
#include <QThread>
#include <QTimer>
#include <QVariant>
#include <iostream>
#include <amqpcpp.h>
#include <amqpcpp/libevent.h>
#include "client_cpp.h"
using namespace std;
class CWorker : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
ConsumeOption consumeOption;
bool markStop;
AMQP::TcpConnection *connection;
AMQP::TcpChannel *channel;
string nextTag;
public:
CWorker(ClientOption& clientOption, ConsumeOption& consumeOption);
virtual ~CWorker();
public slots:
void doWork();
void slotStop();
void bind(QString exchange, QString key, bool ex);
void unbind(QString exchange, QString key, bool ex);
signals:
void onResultReady(PtrProduceMessage msg, uint64_t deliveryTag);
void onErrorConsume(const char *description);
void onWorkFinished();
};
#endif // CWORKER_H

View File

@ -0,0 +1,40 @@
#include "headers.h"
Headers::Headers()
{
_headers.clear();
}
Headers::Headers(const Headers& h)
{
this->_headers = h._headers;
}
void Headers::operator=(const Headers& h)
{
this->_headers = h._headers;
}
QMap<QString,QVariant> Headers::getHeaders() const { return _headers; }
QList<QString> Headers::keys() const { return _headers.keys(); }
QList<QVariant> Headers::values() const { return _headers.values(); }
int Headers::size() const { return _headers.size(); }
bool Headers::contains(const QString name) const { return _headers.contains(name); }
void Headers::set(const QString name, bool value) { _headers.insert(name, value); }
void Headers::set(const QString name, int value) { _headers.insert(name, value); }
void Headers::set(const QString name, QString str) { _headers.insert(name, str); }
bool Headers::isBool(const QString name) const { return _headers[name].type() == QVariant::Bool; }
bool Headers::isInteger(const QString name) const { return _headers[name].type() == QVariant::Int; }
bool Headers::isString(const QString name) const { return _headers[name].type() == QVariant::String; }
bool Headers::getBool(const QString name) const { return _headers[name].value<bool>(); }
int Headers::getInteger(const QString name) const { return _headers[name].value<int>(); }
QString Headers::getString(const QString name) const { return _headers[name].value<QString>(); }

View File

@ -0,0 +1,45 @@
#ifndef HEADERS_H
#define HEADERS_H
#include <QList>
#include <QMap>
#include <QString>
#include <QVariant>
class Headers
{
private:
QMap<QString,QVariant> _headers;
public:
Headers();
Headers(const Headers& h); // Конструктор копирования
~Headers() {}
void operator=(const Headers& h);
QMap<QString,QVariant> getHeaders() const;
QList<QString> keys() const;
QList<QVariant> values() const;
int size() const;
bool contains(const QString name) const;
void set(const QString name, bool value);
void set(const QString name, int value);
void set(const QString name, QString str);
bool isBool(const QString name) const;
bool isInteger(const QString name) const;
bool isString(const QString name) const;
bool getBool(const QString name) const;
int getInteger(const QString name) const;
QString getString(const QString name) const;
};
#endif // HEADERS_H

View File

@ -0,0 +1,45 @@
#ifndef CLIENTOPTION_H
#define CLIENTOPTION_H
#include <QString>
// Значения по умолчанию
const QString DEFAULT_CPP_HOST = "localhost";
const int DEFAULT_CPP_PORT = 5672;
const QString DEFAULT_CPP_USER = "guest";
const QString DEFAULT_CPP_PASSWORD = "guest";
const QString DEFAULT_CPP_VHOST = "/";
struct ClientOption {
QString host;
int port;
QString user;
QString password;
QString vhost;
ClientOption() {
host = DEFAULT_CPP_HOST;
port = DEFAULT_CPP_PORT;
user = DEFAULT_CPP_USER;
password = DEFAULT_CPP_PASSWORD;
vhost = DEFAULT_CPP_VHOST;
}
~ClientOption() {}
ClientOption(const ClientOption& src) = default; // Конструктор копирования
ClientOption(ClientOption&& src) = default; // Конструктор перемещения
ClientOption& operator=(const ClientOption rhs) // Оператор присваивания
{
host = rhs.host;
port = rhs.port;
user = rhs.user;
password = rhs.password;
vhost = rhs.vhost;
return *this;
}
};
#endif // CLIENTOPTION_H

View File

@ -0,0 +1,52 @@
#ifndef CONSUMEOPTION_H
#define CONSUMEOPTION_H
#include <QObject>
#include <QString>
#include <QList>
#include "queueoption.h"
#include "exchangeoption.h"
struct ConsumeOption
{
QString exchange; // Имя точки обмена для связывания
QStringList bindingKeys; // ключи связи точки с очередью
bool nolocal;
bool noack;
bool exclusive;
QueueOption queueOption; // Параметры очереди
ExchangeOption receivingExchange; // Параметры новой принимающей очереди (по умолчанию новой не создаётся)
QMultiMap<QString, QString> bindingArgs; // список связей для точки обмена (если создаётся новая точка)
ConsumeOption() {
exchange = "";
receivingExchange.name = "";
nolocal = false;
noack = false;
exclusive = false;
}
~ConsumeOption() {}
ConsumeOption(const ConsumeOption& src) = default; // Конструктор копирования
ConsumeOption(ConsumeOption&& src) = default; // Конструктор перемещения
ConsumeOption& operator=(const ConsumeOption rhs) // Оператор присваивания
{
exchange = rhs.exchange;
bindingKeys = rhs.bindingKeys;
nolocal = rhs.nolocal;
noack = rhs.noack;
exclusive = rhs.exclusive;
queueOption = rhs.queueOption;
bindingArgs = rhs.bindingArgs;
return *this;
}
};
#endif // CONSUMEOPTION_H

View File

@ -0,0 +1,55 @@
#ifndef EXCHANGEOPTION_H
#define EXCHANGEOPTION_H
#include <QString>
#include <QVariantMap>
#include <QList>
struct ExchangeOption
{
QString name; // уникальное имя точки обмена
QString type; // тип точки обмена (direct, topic,
// fanout или headers)
bool auto_delete; // автоматически удаляемая точка обмена
bool durable; // долгоживущая точка обмена
bool passive; // требуется информация о точке обмена
bool internal; // нельзя вести публикацию из приложения
QVariantMap arguments; // необязательные аргументы
QMap<QString, QString> bindingArgs; // список связей для точки обмена
bool ifunused; // можно удалять, только если точка обмена
// не используется (не имеет потребителей)
ExchangeOption() {
name = "";
type = "";
auto_delete = false;
durable = false;
passive = false;
internal = false;
arguments.clear();
ifunused = false;
}
~ExchangeOption() {}
ExchangeOption(const ExchangeOption& src) = default; // Конструктор копирования
ExchangeOption(ExchangeOption&& src) = default; // Конструктор перемещения
ExchangeOption& operator=(const ExchangeOption rhs) // Оператор присваивания
{
name = rhs.name;
type = rhs.type;
auto_delete = rhs.auto_delete;
durable = rhs.durable;
passive = rhs.passive;
internal = rhs.internal;
arguments = rhs.arguments;
ifunused = rhs.ifunused;
return *this;
}
};
#endif // EXCHANGEOPTION_H

View File

@ -0,0 +1,63 @@
#ifndef QUEUEOPTION_H
#define QUEUEOPTION_H
#include <QObject>
#include <QString>
#include <QVariantMap>
struct QueueOption
{
QString name; // Уникальное имя очереди
bool auto_delete; // Автоматически удаляемая очередь
bool durable; // Долгоживущая очередь
bool passive; // Требуется информация об очереди
bool exclusive; // Исключительная очередь
QVariantMap arguments; // Необязательные аргументы очереди
bool ifunused; // Удалять, только если не используется
bool ifempty; // Удалять, только если очередь пуста
int messageCount; // Число сообщений в очереди
int consumerCount; // Число потребителей очереди
QueueOption() {
name = "";
auto_delete = false;
durable = false;
passive = false;
exclusive = false;
arguments.clear();
ifunused = false;
ifempty = false;
messageCount = 0;
consumerCount = 0;
}
~QueueOption() {}
QueueOption(const QueueOption& src) = default; // Конструктор копирования
QueueOption(QueueOption&& src) = default; // Конструктор перемещения
QueueOption& operator=(const QueueOption rhs) // Оператор присваивания
{
name = rhs.name;
auto_delete = rhs.auto_delete;
passive = rhs.passive;
exclusive = rhs.exclusive;
arguments = rhs.arguments;
ifunused = rhs.ifunused;
ifempty = rhs.ifempty;
messageCount = rhs.messageCount;
consumerCount = rhs.consumerCount;
return *this;
}
};
#endif // QUEUEOPTION_H

View File

@ -0,0 +1,37 @@
#include "producemessage.h"
// Конструктор по умолчанию
ProduceMessage::ProduceMessage()
{
_body.clear();
}
// Конструктор копирования
ProduceMessage::ProduceMessage(const ProduceMessage& msg)
{
this->_body = msg._body;
this->_headers = msg._headers;
this->_properties = msg._properties;
}
ProduceMessage& ProduceMessage::operator=(const ProduceMessage& msg)
{
if (this != &msg) {
this->_body = msg._body;
this->_headers = msg._headers;
this->_properties = msg._properties;
}
return *this;
}
QByteArray ProduceMessage::getBodyMsg() const { return _body; }
void ProduceMessage::setBodyMsg(const QByteArray &ba) { _body = ba; }
void ProduceMessage::setBodyMsg(const char *body, int size) { _body = QByteArray(body, size); }
Headers ProduceMessage::getHeaders() const { return _headers; }
void ProduceMessage::setHeaders(const Headers &headers) { _headers = headers; }
Properties ProduceMessage::getProperties() const { return _properties; }
void ProduceMessage::setProperties(const Properties &properties) { _properties = properties; }

View File

@ -0,0 +1,43 @@
#ifndef PRODUCEMESSAGE_H
#define PRODUCEMESSAGE_H
#include <QByteArray>
#include "properties.h"
#include "headers.h"
class ProduceMessage
{
public:
ProduceMessage();
ProduceMessage(const ProduceMessage& msg);
~ProduceMessage() {}
ProduceMessage& operator=(const ProduceMessage& msg);
QByteArray getBodyMsg() const;
void setBodyMsg(const QByteArray &ba);
void setBodyMsg(const char *body, int size);
Headers getHeaders() const;
void setHeaders(const Headers &headers);
Properties getProperties() const;
void setProperties(const Properties &properties);
private:
QByteArray _body;
protected:
Properties _properties;
Headers _headers;
};
using PtrProduceMessage = ProduceMessage*;
Q_DECLARE_METATYPE(ProduceMessage)
Q_DECLARE_METATYPE(PtrProduceMessage)
#endif // PRODUCEMESSAGE_H

View File

@ -0,0 +1,63 @@
#include "properties.h"
Properties::Properties()
{
_properties.clear();
setDeliveryMode(1); // не оставлять сообщения
}
// Конструктор копирования
Properties::Properties(const Properties& p)
{
this->_properties = p._properties;
}
void Properties::operator=(const Properties& p)
{
this->_properties = p._properties;
}
const QMap<QString,QVariant> &Properties::getProperties() { return _properties; }
bool Properties::contains(const QString name) const { return _properties.contains(name); }
bool Properties::isContentType() const { return _properties.contains("content-type"); }
bool Properties::isContentEncoding() const { return _properties.contains("content-encoding"); }
bool Properties::isMessageID() const { return _properties.contains("message-id"); }
bool Properties::isCorrelationID() const { return _properties.contains("correlation-id"); }
bool Properties::isTimestamp() const { return _properties.contains("timestamp"); }
bool Properties::isExpiration() const { return _properties.contains("expiration"); }
bool Properties::isDeliveryMode() const { return _properties.contains("delivery-mode"); }
bool Properties::isAppID() const { return _properties.contains("app-id"); }
bool Properties::isUserID() const { return _properties.contains("user-id"); }
bool Properties::isTypeName() const { return _properties.contains("type"); }
bool Properties::isReplyTo() const { return _properties.contains("reply-to"); }
bool Properties::isPriority() const { return _properties.contains("priority"); }
void Properties::setContentType(const QString &str) { _properties.insert("content-type", str); }
void Properties::setContentEncoding(const QString &str) { _properties.insert("content-encoding", str); }
void Properties::setMessageID(const QString &str) { _properties.insert("message-id", str); }
void Properties::setCorrelationID(const QString &str) { _properties.insert("correlation-id", str); }
void Properties::setTimestamp(const quint64 val) { _properties.insert("timestamp", val); }
void Properties::setExpiration(const QString &str) { _properties.insert("expiration", str); }
void Properties::setDeliveryMode(const quint8 val) { _properties.insert("delivery-mode", val); }
void Properties::setAppID(const QString &str) { _properties.insert("app-id", str); }
void Properties::setUserID(const QString &str) { _properties.insert("user-id", str); }
void Properties::setTypeName(const QString &str) { _properties.insert("type", str); }
void Properties::setReplyTo(const QString &str) { _properties.insert("reply-to", str); }
void Properties::setPriority(const quint8 val) { _properties.insert("priority", val); }
QString Properties::getContentType() const { return _properties["content-type"].value<QString>(); }
QString Properties::getContentEncoding() const { return _properties["content-encoding"].value<QString>(); }
QString Properties::getMessageID() const { return _properties["message-id"].value<QString>(); }
QString Properties::getCorrelationID() const { return _properties["correlation-id"].value<QString>(); }
quint64 Properties::getTimestamp() const { return _properties["timestamp"].value<quint64>(); }
QString Properties::getExpiration() const { return _properties["expiration"].value<QString>(); }
quint8 Properties::getDeliveryMode() const { return _properties["delivery-mode"].value<quint8>(); }
QString Properties::getAppID() const { return _properties["app-id"].value<QString>(); }
QString Properties::getUserID() const { return _properties["user-id"].value<QString>(); }
QString Properties::getTypeName() const { return _properties["type"].value<QString>(); }
QString Properties::getReplyTo() const { return _properties["reply-to"].value<QString>(); }
quint8 Properties::getPriority() const { return _properties["priority"].value<quint8>(); }

View File

@ -0,0 +1,73 @@
#ifndef PROPERTIES_H
#define PROPERTIES_H
#include <QtGlobal>
#include <QMap>
#include <QString>
#include <QVariant>
class Properties
{
private:
QMap<QString,QVariant> _properties;
public:
Properties();
Properties(const Properties& p);
~Properties() {}
void operator=(const Properties& p);
int size() {return _properties.size(); }
const QMap<QString,QVariant> &getProperties();
bool contains(const QString name) const;
bool isContentType() const;
bool isContentEncoding() const;
bool isMessageID() const;
bool isCorrelationID() const;
bool isTimestamp() const;
bool isExpiration() const;
bool isDeliveryMode() const;
bool isAppID() const;
bool isUserID() const;
bool isTypeName() const;
bool isReplyTo() const;
bool isPriority() const;
void setContentType(const QString &str);
void setContentEncoding(const QString &str);
void setMessageID(const QString &str);
void setCorrelationID(const QString &str);
void setTimestamp(const quint64 val);
void setExpiration(const QString &str);
void setDeliveryMode(const quint8 val);
void setAppID(const QString &str);
void setUserID(const QString &str);
void setTypeName(const QString &str);
void setReplyTo(const QString &str);
void setPriority(const quint8 val);
QString getContentType() const;
QString getContentEncoding() const;
QString getMessageID() const;
QString getCorrelationID() const;
quint64 getTimestamp() const;
QString getExpiration() const;
quint8 getDeliveryMode() const;
QString getAppID() const;
QString getUserID() const;
QString getTypeName() const;
QString getReplyTo() const;
quint8 getPriority() const;
};
#endif // PROPERTIES_H

View File

@ -0,0 +1,330 @@
/**************************************************************************
* PWorker - Publish Worker, - рабочий поток публикации сообщений *
* редакция от 08.06.2022 *
* Принадлежность: библиотека clientRBcpp *
**************************************************************************/
#include <QCoreApplication>
#include "_client_.h"
#include "sender.h"
#include "pworker.h"
#include <QDebug>
PWorker::PWorker(ClientOption& clientOption, ExchangeOption& exchangeOption)
: QObject(nullptr), markStop(false)
{
this->clientOption = clientOption;
this->exchangeOption = exchangeOption;
qu.clear();
// static const int idE2E = qRegisterMetaType<E2EStruct>();
// Q_UNUSED(idE2E)
}
PWorker::~PWorker()
{
// Освободим очередь сообщений
mutex.lock();
while (! qu.isEmpty()) {
PublishPacket *packet = qu.dequeue();
AMQP::Envelope *envelope = packet->envelope;
delete envelope->body();
delete envelope;
delete packet;
}
mutex.unlock();
}
// Здесь реализуется основная деятельность потока
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void PWorker::doWork()
{
string host = clientOption.host.toStdString();
string port = QString::number(clientOption.port).toStdString();
string user = clientOption.user.toStdString();
string password = clientOption.password.toStdString();
string vhost = clientOption.vhost.toStdString();
string address = "amqp://" + user + ":" + password + "@" + host + ":" + port + vhost;
// Обрабатываем аргументы на предмет альтернативной точки обмена
AMQP::Table table;
QString alt_e_name = "alternate-exchange";
QString alt_e_value = "";
if (exchangeOption.arguments.contains(alt_e_name)) {
alt_e_value = exchangeOption.arguments[alt_e_name].value<QString>();
table.set(alt_e_name.toStdString(), alt_e_value.toStdString());
}
string alt_exchange = alt_e_value.toStdString(); // Имя альтернативной точки обмена
AMQP::ExchangeType typeAltEx = AMQP::fanout; // Тип альтернативной точки обмена - всегда fanout
int flagsAltEx = (AMQP::durable | AMQP::internal); // Точка долгоживущая и внутренняя
auto evbase = event_base_new();
AMQP::LibEventHandler handler(evbase);
AMQP::TcpConnection connection(&handler, AMQP::Address(address));
AMQP::TcpChannel channel(&connection);
channel.setQos(1);
channel.confirmSelect()
.onAck([&](uint64_t deliveryTag, bool multiple) {
emit onReceivedAckNack(deliveryTag, true, multiple);
})
.onNack([&](uint64_t deliveryTag, bool multiple, bool requeue) {
Q_UNUSED(requeue)
emit onReceivedAckNack(deliveryTag, false, multiple);
});
// Объявляем альтернативную точку обмена
//--------------------------------------
if (alt_e_value != "") {
channel.declareExchange(alt_exchange, typeAltEx, flagsAltEx)
.onError( [&](const char *message) {
string msg(message);
emit onError(msg);
});
}
// Обработка основной точки обмена
//----------------------------------
string exchange = exchangeOption.name.toStdString();
string type = exchangeOption.type.toStdString();
// преобразование типа точки обмена в формат AMQP
AMQP::ExchangeType typeEx;
if (type == "" || type == "direct")
typeEx = AMQP::direct;
else if (type == "topic")
typeEx = AMQP::topic;
else if (type == "headers")
typeEx = AMQP::headers;
else
typeEx = AMQP::fanout;
// предобразование флагов точки обмена в формат AMQP
int flagsExchange = 0;
if (exchangeOption.durable)
flagsExchange |= AMQP::durable;
if (exchangeOption.auto_delete)
flagsExchange |= AMQP::autodelete;
if (exchangeOption.internal)
flagsExchange |= AMQP::internal;
// Для предопределенных точек обмена их обьявление не производим
if ( exchange != "" && exchange != "amq.fanout" && exchange != "amq.direct" &&
exchange != "amq.topic" && exchange != "amq.headers") {
channel.declareExchange(exchange, typeEx, flagsExchange, table)
.onError( [&](const char *message) {
string msg(message);
emit onError(msg);
});
}
// обработка mandatory
auto messageCb = [&](const AMQP::Message& message, int16_t code, const std::string &description)
{
Q_UNUSED(code)
Q_UNUSED(description)
// Формируем принятое сообщениев формате ReceivedMessage
ProduceMessage *rMsg = new ProduceMessage;
rMsg->setBodyMsg(message.body(), message.bodySize());
// Формируем таблицу Properties свойств сообщения
Properties p;
if (message.hasContentType())
p.setContentType(QString::fromStdString(message.contentType()));
if (message.hasContentEncoding())
p.setContentEncoding(QString::fromStdString(message.contentEncoding()));
if (message.hasMessageID())
p.setMessageID(QString::fromStdString(message.messageID()));
if (message.hasCorrelationID())
p.setCorrelationID(QString::fromStdString(message.correlationID()));
if (message.timestamp())
p.setTimestamp(message.timestamp());
if (message.hasExpiration())
p.setExpiration(QString::fromStdString(message.expiration()));
if (message.hasDeliveryMode())
p.setDeliveryMode(message.deliveryMode());
if (message.hasAppID())
p.setAppID(QString::fromStdString(message.appID()));
if (message.hasUserID())
p.setUserID(QString::fromStdString(message.userID()));
if (message.hasTypeName())
p.setTypeName(QString::fromStdString(message.typeName()));
if (message.hasReplyTo())
p.setReplyTo(QString::fromStdString(message.replyTo()));
if (message.hasPriority())
p.setPriority(message.priority());
rMsg->setProperties(p);
// Работа со свойствами Headers
Headers h;
AMQP::Table table = message.headers();
vector<string> keys = table.keys();
string name;
for(uint i = 0; i < keys.size(); i++) {
name = keys[i];
if (table.get(name).isInteger()) {
int value = table.get(name);
h.set(QString::fromStdString(name), value);
}
else if (table.get(name).isString()) {
QString str = QString::fromStdString(table.get(name));
h.set(QString::fromStdString(name), str);
}
else if (table.get(name).isBoolean()) {
AMQP::Field &b = const_cast<AMQP::Field &>(table.get(name));
AMQP::BooleanSet &b1 = dynamic_cast<AMQP::BooleanSet &>(b);
bool value = b1.get(0);
h.set(QString::fromStdString(name), value);
}
}
rMsg->setHeaders(h);
emit onMessageBounced(rMsg);
};
channel.recall().onReceived(messageCb);
// Цикл событий (event loop)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
while (! markStop) {
// Обрабатываем очередь сообщений на передачу
if (! qu.isEmpty()) {
mutex.lock();
PublishPacket *packet = qu.dequeue();
mutex.unlock();
AMQP::Envelope *envelope = packet->envelope;
int publishFlags = packet->publishFlags;
string routingKey = packet->routingKey;
if (envelope->hasAppID() && envelope->appID() == HEARBEATS)
connection.heartbeat();
else
channel.publish(exchange, routingKey, *envelope, publishFlags);
delete envelope->body();
delete envelope;
delete packet;
}
event_base_loop(evbase, EVLOOP_NONBLOCK);
QCoreApplication::processEvents();
QThread::msleep(0);
} //while
// Закроем канал и соединение
channel.close();
connection.close();
event_base_loopbreak(evbase);
event_base_loopexit(evbase, 0);
event_base_free(evbase);
emit onWorkFinished(); // Посылаем сигнал о завершении работы
}
// Прием данных, предразование в формат передачи
// и постановка в очередь на выдачу
void PWorker::sending(ProduceMessage msg, QString routingKey, bool mandatory)
{
mutex.lock();
uint64_t size = msg.getBodyMsg().size();
char *body = new char[size];
memcpy(body, msg.getBodyMsg().data(), size);
AMQP::Envelope *env = new AMQP::Envelope(body, size);
// Готовим сообщение для отправки
Properties p = msg.getProperties();
if (p.contains("content-type"))
env->setContentType(p.getContentType().toStdString().c_str());
if (p.contains("content-encoding"))
env->setContentEncoding(p.getContentEncoding().toStdString().c_str());
if (p.contains("message-id"))
env->setMessageID(p.getMessageID().toStdString().c_str());
if (p.contains("correlation-id"))
env->setCorrelationID(p.getCorrelationID().toStdString().c_str());
if (p.contains("timestamp"))
env->setTimestamp(p.getTimestamp());
if (p.contains("expiration"))
env->setExpiration(p.getExpiration().toStdString().c_str());
if (p.contains("delivery-mode"))
env->setDeliveryMode(p.getDeliveryMode());
if (p.contains("app-id"))
env->setAppID(p.getAppID().toStdString().c_str());
if (p.contains("user-id"))
env->setUserID(p.getUserID().toStdString().c_str());
if (p.contains("type"))
env->setTypeName(p.getTypeName().toStdString().c_str());
if (p.contains("reply-to"))
env->setReplyTo(p.getReplyTo().toStdString().c_str());
if (p.contains("priority"))
env->setPriority(p.getPriority());
AMQP::Table table;
Headers p2 = msg.getHeaders();
QList<QString> k = p2.keys();
QList<QVariant> v = p2.values();
for (int i=0; i < p2.size(); i++) {
QString name = k[i];
QVariant val = v[i];
if (val.type() == QVariant::Int) {
AMQP::Long numb = val.value<int>();
table.set(name.toStdString(), numb.value());
}
else if (val.type() == QVariant::String) {
QString str = val.value<QString>();
AMQP::ShortString s(str.toStdString());
table.set(name.toStdString(), s.value());
}
else if (val.type() == QVariant::Bool) {
bool numb = val.value<bool>();
table.set(name.toStdString(), numb);
}
}
env->setHeaders(table);
int flags = 0; // флаги - в формат AMQP
if (mandatory)
flags |= AMQP::mandatory;
string routing = routingKey.toStdString();
// формируем пакет для постановки в очередь
PublishPacket *pp = new PublishPacket;
pp->envelope = env;
pp->publishFlags = flags;
pp->routingKey = routing;
qu.enqueue(pp);
mutex.unlock();
}
void PWorker::stop()
{
markStop = true; // завершить цикл обработки сообщений
}

View File

@ -0,0 +1,58 @@
#ifndef PWORKER_H
#define PWORKER_H
#include <QMutex>
#include <QObject>
#include <QQueue>
#include <QString>
#include <QVariant>
#include <iostream>
#include <amqpcpp.h>
#include <amqpcpp/libevent.h>
#include "client_cpp.h"
using namespace std;
struct PublishPacket {
AMQP::Envelope *envelope;
string routingKey;
int publishFlags;
};
class PWorker : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
ExchangeOption exchangeOption;
bool markStop;
QMutex mutex;
QQueue<PublishPacket *> qu;
public:
PWorker(ClientOption& clientOption, ExchangeOption& exchangeOption);
virtual ~PWorker();
public slots:
void doWork();
void sending(ProduceMessage msg, QString routingKey, bool mandatory=false);
void stop();
signals:
void onMessageBounced(PtrProduceMessage msg);
void onError(string msg);
void onReceivedAckNack(uint64_t deliveryTag, bool ack, bool multiple);
void onWorkFinished();
};
#endif // PWORKER_H

View File

@ -0,0 +1,65 @@
#include <QCoreApplication>
#include "client_cpp.h"
Receiver::Receiver(ClientOption &clientOption, ConsumeOption &consumeOption)
: QObject(nullptr)
{
this->clientOption = clientOption;
this->consumeOption = consumeOption;
this->consumeOption.receivingExchange = consumeOption.receivingExchange;
this->consumeOption.bindingArgs = consumeOption.bindingArgs;
CWorker *worker = new CWorker(this->clientOption, this->consumeOption);
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::started, worker, &CWorker::doWork);
connect(&workerThread, &QThread::finished, worker, &CWorker::slotStop);
// Автоматическое удаление объектов PWorker и QThread по окончании работы
connect(worker, &CWorker::onWorkFinished, worker, &CWorker::deleteLater);
// Посылаемые потоку сигналы
connect(this, &Receiver::onStop, worker, &CWorker::slotStop, Qt::DirectConnection);
connect(this, &Receiver::doBind, worker, &CWorker::bind, Qt::DirectConnection);
connect(this, &Receiver::doUnbind, worker, &CWorker::unbind, Qt::DirectConnection);
// Сигналы, принимаемые от потока
connect(worker, &CWorker::onWorkFinished, &workerThread, &QThread::quit, Qt::QueuedConnection);
connect(worker, &CWorker::onResultReady, this, &Receiver::slotMsgReady, Qt::QueuedConnection);
connect(worker, &CWorker::onErrorConsume, this, &Receiver::slotErrorMsg, Qt::QueuedConnection);
workerThread.start();
}
Receiver::~Receiver()
{
workerThread.quit();
workerThread.wait();
}
// При получении от потока сигнала о приеме сообщения
// выпускаем сигнал дальше
void Receiver::slotMsgReady(PtrProduceMessage msg, uint64_t deliveryTag)
{
ProduceMessage message = *msg;
delete msg;
emit onMessage(message, deliveryTag);
}
// При получении сигнала об ошибке транслируем его
void Receiver::slotErrorMsg(const char *description)
{
QString str(description);
emit onError(str);
}
void Receiver::slotStop()
{
emit onStop();
}

View File

@ -0,0 +1,47 @@
#ifndef RECEIVER_H
#define RECEIVER_H
#include <QObject>
#include <QList>
#include <QMap>
#include <QString>
#include <QTimer>
#include <QThread>
#include <QVariant>
#include "_client_.h"
#include "cworker.h"
using namespace std;
class Receiver : public QObject
{
Q_OBJECT
private:
bool stop;
ClientOption clientOption;
ConsumeOption consumeOption;
QThread workerThread;
public:
Receiver(ClientOption& clientOption, ConsumeOption& consumeOption);
virtual ~Receiver();
public slots:
void slotMsgReady(PtrProduceMessage msg, uint64_t deliveryTag);
void slotErrorMsg(const char *description);
void slotStop();
signals:
void onMessage(ProduceMessage msg, uint64_t deliveryTag);
void onError(QString description);
void onStop();
void doBind(QString exchange, QString key, bool ex);
void doUnbind(QString exchange, QString key, bool ex);
};
#endif // RECEIVER_H

View File

@ -0,0 +1,106 @@
#include <QCoreApplication>
#include "client_cpp.h"
Sender::Sender(ClientOption& clientOption, ExchangeOption& exchangeOption)
: QObject(nullptr)
{
this->clientOption = clientOption;
this->exchangeOption = exchangeOption;
PWorker *worker = new PWorker(this->clientOption, this->exchangeOption);
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::started, worker, &PWorker::doWork);
// Автоматическое удаление объектов PWorker и QThread по окончании работы
connect(worker, &PWorker::onWorkFinished, worker, &PWorker::deleteLater);
// Посылаемые потоку сигналы
connect(this, &Sender::onSend, worker, &PWorker::sending, Qt::QueuedConnection);
connect(this, &Sender::onStop, worker, &PWorker::stop, Qt::DirectConnection);
// Сигналы, принимаемые от потока
connect(worker, &PWorker::onWorkFinished, &workerThread, &QThread::quit, Qt::QueuedConnection);
connect(worker, &PWorker::onReceivedAckNack, this, &Sender::slotAckNack, Qt::QueuedConnection);
connect(worker, &PWorker::onError, this, &Sender::slotError, Qt::QueuedConnection);
connect(worker, &PWorker::onMessageBounced, this, &Sender::slotMsgBounced, Qt::QueuedConnection);
workerThread.start();
// Запуск таймера для механизма сердцебиения
tm.stop();
tm.setInterval(30000); // 0,5 мин
connect(&tm, &QTimer::timeout, this, &Sender::onTimer);
tm.start();
}
Sender::~Sender()
{
tm.stop();
workerThread.quit();
workerThread.wait();
}
// Периодическое подключение по таймеру (1 мин)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Sender::onTimer()
{
tm.stop();
// Формируем и отправляем служебное сообщение
// на проверку соединения с экземпляром RabbitMQ
string str = "@@"; // содержимое не играет роли
ProduceMessage msg;
msg.setBodyMsg(str.c_str(), str.size());
Properties p;
QString hearbeats(HEARBEATS);
p.setAppID(hearbeats); // маркер служебного сообщения сердцебиения
msg.setProperties(p);
QString routingKey = "";
emit onSend(msg, routingKey); // сообщение передаем в поток для передачи серверу
tm.start();
}
void Sender::slotMsgBounced(PtrProduceMessage msg)
{
ProduceMessage message = *msg;
delete msg;
emit onMsgBounced(message);
}
// Передаем сообщение в поток для выдачи
void Sender::send(ProduceMessage msg, QString routingKey, bool mandatory)
{
emit onSend(msg, routingKey, mandatory);
}
// Прием подтверждения от потока о выдаче (или невыдаче) сообщения
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-------~~~~~~~~~~~~~~------------
void Sender::slotAckNack(uint64_t deliveryTag, bool ack, bool multiple)
{
emit onReceivedAckNack(deliveryTag, ack, multiple);
}
void Sender::slotError(string msg)
{
QString message = QString::fromStdString(msg);
emit onError(message);
}
void Sender::slotStop()
{
emit onStop();
}

View File

@ -0,0 +1,58 @@
#ifndef SENDER_H
#define SENDER_H
#include <QMap>
#include <QMetaType>
#include <QObject>
#include <QString>
#include <QThread>
#include <QTimer>
#include <QVariant>
#include <iostream>
#include "client_cpp.h"
using namespace std;
class Client;
class PWorker;
class Sender : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
ExchangeOption exchangeOption;
QThread workerThread;
QTimer tm;
public:
Sender(ClientOption& clientOption, ExchangeOption& exchangeOption);
virtual ~Sender();
void send(ProduceMessage msg, QString routingKey, bool mandatory=false);
private slots:
void onTimer();
public slots:
void slotMsgBounced(PtrProduceMessage msg);
void slotStop();
void slotAckNack(uint64_t deliveryTag, bool ack, bool multiple);
void slotError(string msg);
signals:
void onMsgBounced(ProduceMessage msg);
void onReceivedAckNack(uint64_t deliveryTag, bool ack, bool multiple);
void onSend(ProduceMessage msg, QString routingKey, bool mandatory=false); // Отправка сообщения потоку
void onError(QString &msg);
void onStop();
};
#endif // SENDER_H

View File

@ -0,0 +1,54 @@
#include "validator.h"
#include <QDebug>
Validator::Validator(ClientOption& clientOption, QList<ExchangeOption>& exchangeOptions)
: QObject(nullptr)
{
// класс запускает поток с валидатором, который в течение 5 секунд должен проверить инфраструктуру.
// любая ошибка приведёт к завершению работы программы. если ошибок не было перехвачено, через 5 секунд процесс завершается.
this->clientOption = clientOption;
this->exchangeOptions = exchangeOptions;
VWorker *worker = new VWorker(this->clientOption, this->exchangeOptions);
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::started, worker, &VWorker::doWork);
// Автоматическое удаление объектов VWorker и QThread по окончании работы
connect(worker, &VWorker::onWorkFinished, worker, &VWorker::deleteLater);
// Сигналы, принимаемые от потока
connect(worker, &VWorker::onWorkFinished, &workerThread, &QThread::quit, Qt::QueuedConnection);
connect(worker, &VWorker::onError, this, &Validator::slotError, Qt::QueuedConnection);
workerThread.start();
// Запуск таймера для механизма сердцебиения
tm.stop();
tm.setInterval(5000); // 5 сек
connect(&tm, &QTimer::timeout, worker, &VWorker::slotStop);
connect(&tm, &QTimer::timeout, this, &Validator::onTimer);
tm.start();
}
Validator::~Validator()
{
tm.stop();
workerThread.quit();
}
void Validator::onTimer()
{
tm.stop();
workerThread.quit();
}
void Validator::slotError(string msg)
{
QString message = QString::fromStdString(msg);
emit onError(message);
}

View File

@ -0,0 +1,41 @@
#ifndef VALIDATOR_H
#define VALIDATOR_H
#include <QMap>
#include <QMetaType>
#include <QObject>
#include <QString>
#include <QThread>
#include <QTimer>
#include <QVariant>
#include <iostream>
#include "client_cpp.h"
class Validator : public QObject
{
Q_OBJECT
public:
Validator(ClientOption& clientOption, QList<ExchangeOption>& exchangeOptions);
virtual ~Validator();
private:
ClientOption clientOption;
QList<ExchangeOption> exchangeOptions;
QThread workerThread;
QTimer tm;
private slots:
void onTimer();
public slots:
void slotError(string msg);
signals:
void onError(QString &msg);
void onStop();
};
#endif // VALIDATOR_H

View File

@ -0,0 +1,123 @@
#include <QCoreApplication>
#include "_client_.h"
#include "sender.h"
#include "vworker.h"
#include <QDebug>
VWorker::VWorker(ClientOption &clientOption, QList<ExchangeOption>& exchangeOptions)
: QObject(nullptr), markStop(false)
{
this->clientOption = clientOption;
this->exchangeOptions = exchangeOptions;
}
VWorker::~VWorker()
{
}
void VWorker::doWork()
{
string host = clientOption.host.toStdString();
string port = QString::number(clientOption.port).toStdString();
string user = clientOption.user.toStdString();
string password = clientOption.password.toStdString();
string vhost = clientOption.vhost.toStdString();
string address = "amqp://" + user + ":" + password + "@" + host + ":" + port + vhost;
// Создаём подключение
auto evbase = event_base_new();
AMQP::LibEventHandler handler(evbase);
AMQP::TcpConnection connection(&handler, AMQP::Address(address));
AMQP::TcpChannel channel(&connection);
channel.setQos(1);
channel.confirmSelect()
.onError([&](const char *message) {
qDebug() << "validator - connecting error: " << message;
});
// Обрабатываем список точек обмена
string exch;
string exType;
AMQP::ExchangeType typeExch;
int flagsExch = 0;
QString ex_alt_e_name = "alternate-exchange";
QString ex_alt_e_value = "";
for(ExchangeOption exOpt : exchangeOptions) {
AMQP::Table exTable;
exch = exOpt.name.toStdString();
exType = exOpt.type.toStdString();
// преобразование типа точки обмена в формат AMQP
if (exType == "" || exType == "direct")
typeExch = AMQP::direct;
else if (exType == "topic")
typeExch = AMQP::topic;
else if (exType == "headers")
typeExch = AMQP::headers;
else
typeExch = AMQP::fanout;
// предобразование флагов точки обмена в формат AMQP
if (exOpt.durable)
flagsExch |= AMQP::durable;
if (exOpt.auto_delete)
flagsExch |= AMQP::autodelete;
if (exOpt.internal)
flagsExch |= AMQP::internal;
if (exOpt.arguments.contains(ex_alt_e_name)) {
ex_alt_e_value = exOpt.arguments[ex_alt_e_name].value<QString>();
exTable.set(ex_alt_e_name.toStdString(), ex_alt_e_value.toStdString());
}
//Для предопределенных точек обмена их обьявление не производим
if ( exch != "" && exch != "amq.fanout" && exch != "amq.direct" &&
exch != "amq.topic" && exch != "amq.headers") {
channel.declareExchange(exch, typeExch, flagsExch, exTable)
.onError( [&](const char *message) {
qDebug() << "validator - declaring error: " << message;
emit onError(message);
});
}
QMap<QString, QString>::iterator it = exOpt.bindingArgs.begin();
for (; it != exOpt.bindingArgs.end(); ++it) {
channel.bindExchange(exch, it.key().toStdString(), it.value().toStdString())
.onError( [&](const char *message) {
qDebug() << "validator - binding error: " << message;
emit onError(message);
});
}
}
while (! markStop) {
event_base_loop(evbase, EVLOOP_NONBLOCK);
QCoreApplication::processEvents();
QThread::msleep(0);
}
// Закроем канал и соединение
channel.close();
connection.close();
event_base_loopbreak(evbase);
event_base_loopexit(evbase, 0);
event_base_free(evbase);
}
void VWorker::slotStop()
{
markStop = true;
}

View File

@ -0,0 +1,43 @@
#ifndef VWORKER_H
#define VWORKER_H
#include <QObject>
#include <QQueue>
#include <QString>
#include <QVariant>
#include <iostream>
#include <amqpcpp.h>
#include <amqpcpp/libevent.h>
#include "client_cpp.h"
using namespace std;
class VWorker : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
QList<ExchangeOption> exchangeOptions;
bool markStop;
QMutex mutex;
public:
VWorker(ClientOption& clientOption, QList<ExchangeOption>& exchangeOptions);
virtual ~VWorker();
public slots:
void doWork();
void slotStop();
signals:
void onError(string msg);
void onWorkFinished();
};
#endif // VWORKER_H

View File

@ -0,0 +1,41 @@
QT += core gui widgets
LIBS += -L/usr/lib -lamqpcpp -L/usr/lib/x86_64-linux-gnu/ -levent -lpthread -ldl
SOURCES += \
clientRBcpp/client.cpp \
clientRBcpp/clientrbcpp.cpp \
clientRBcpp/cworker.cpp \
clientRBcpp/headers.cpp \
clientRBcpp/producemessage.cpp \
clientRBcpp/properties.cpp \
clientRBcpp/pworker.cpp \
clientRBcpp/receiver.cpp \
clientRBcpp/sender.cpp \
clientRBcpp/validator.cpp \
clientRBcpp/vworker.cpp \
main.cpp \
mainwindow.cpp
HEADERS += \
clientRBcpp/_client_.h \
clientRBcpp/client_cpp.h \
clientRBcpp/clientrbcpp.h \
clientRBcpp/clientrbcpp_global.h \
clientRBcpp/cworker.h \
clientRBcpp/headers.h \
clientRBcpp/options/clientoption.h \
clientRBcpp/options/consumeoption.h \
clientRBcpp/options/exchangeoption.h \
clientRBcpp/options/queueoption.h \
clientRBcpp/producemessage.h \
clientRBcpp/properties.h \
clientRBcpp/pworker.h \
clientRBcpp/receiver.h \
clientRBcpp/sender.h \
clientRBcpp/validator.h \
clientRBcpp/vworker.h \
mainwindow.h
FORMS += \
mainwindow.ui

View File

@ -0,0 +1,338 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.12.3, 2023-12-23T19:03:36. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{77607214-f3f8-45c8-bf65-1a310ea854a8}</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.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="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="bool" key="EditorConfiguration.inEntireDocument">false</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuelist type="QVariantList" key="ClangCodeModel.CustomCommandLineKey"/>
<value type="bool" key="ClangCodeModel.UseGlobalConfig">true</value>
<value type="QString" key="ClangCodeModel.WarningConfigId">Builtin.Questionable</value>
<valuemap type="QVariantMap" key="ClangTools">
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
<value type="int" key="ClangTools.ParallelJobs">0</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="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{fa463890-d98c-43fb-aee8-64b3da65bdfc}</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="bool">true</value>
<value type="int" key="EnableQmlDebugging">0</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_slow-Desktop-Debug</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_slow-Desktop-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"></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>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Сборка</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>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Очистка</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.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Отладка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">2</value>
<value type="int" key="QtQuickCompiler">2</value>
<value type="int" key="SeparateDebugInfo">2</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
<value type="bool">true</value>
<value type="int" key="EnableQmlDebugging">2</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_slow-Desktop-Release</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_slow-Desktop-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="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></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>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Сборка</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>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Очистка</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.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Выпуск</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">2</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.2">
<value type="bool">true</value>
<value type="int" key="EnableQmlDebugging">0</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_slow-Desktop-Profile</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_slow-Desktop-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="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></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>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Сборка</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>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Очистка</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.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Профилирование</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">Развёртывание</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Развёртывание</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.PluginSettings"/>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="QString" key="Analyzer.Perf.CallgraphMode">dwarf</value>
<valuelist type="QVariantList" key="Analyzer.Perf.Events">
<value type="QString">cpu-cycles</value>
</valuelist>
<valuelist type="QVariantList" key="Analyzer.Perf.ExtraArguments"/>
<value type="int" key="Analyzer.Perf.Frequency">250</value>
<valuelist type="QVariantList" key="Analyzer.Perf.RecordArguments">
<value type="QString">-e</value>
<value type="QString">cpu-cycles</value>
<value type="QString">--call-graph</value>
<value type="QString">dwarf,4096</value>
<value type="QString">-F</value>
<value type="QString">250</value>
</valuelist>
<value type="QString" key="Analyzer.Perf.SampleMode">-F</value>
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Perf.StackSize">4096</value>
<value type="bool" key="Analyzer.QmlProfiler.AggregateTraces">false</value>
<value type="bool" key="Analyzer.QmlProfiler.FlushEnabled">false</value>
<value type="uint" key="Analyzer.QmlProfiler.FlushInterval">1000</value>
<value type="QString" key="Analyzer.QmlProfiler.LastTraceFile"></value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
<value type="QString" key="Analyzer.Valgrind.KCachegrindExecutable">kcachegrind</value>
<value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
<value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
<value type="int">0</value>
<value type="int">1</value>
<value type="int">2</value>
<value type="int">3</value>
<value type="int">4</value>
<value type="int">5</value>
<value type="int">6</value>
<value type="int">7</value>
<value type="int">8</value>
<value type="int">9</value>
<value type="int">10</value>
<value type="int">11</value>
<value type="int">12</value>
<value type="int">13</value>
<value type="int">14</value>
</valuelist>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/consumer_slow/consumer_slow.pro</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/consumer_slow/consumer_slow.pro</value>
<value type="QString" key="RunConfiguration.Arguments"></value>
<value type="bool" key="RunConfiguration.Arguments.multi">false</value>
<value type="QString" key="RunConfiguration.OverrideDebuggerStartup"></value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
<value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory"></value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_slow-Desktop-Debug</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>

View File

@ -0,0 +1,11 @@
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}

View File

@ -0,0 +1,33 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QUuid>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ClientOption clOpt;
ConsumeOption conOpt;
conOpt.exchange = "publisher";
conOpt.bindingKeys << "all";
conOpt.queueOption.name = "queue_slow";
conOpt.queueOption.auto_delete = true;
receiver = new Receiver(clOpt, conOpt);
QObject::connect(receiver, &Receiver::onMessage, this, [&](ProduceMessage msg, uint64_t consumeTag) {
Q_UNUSED(consumeTag)
QString msg_body = "got " + QString::fromLocal8Bit(msg.getBodyMsg());
ui->listWidget->addItem(msg_body);
});
}
MainWindow::~MainWindow()
{
delete ui;
}

View File

@ -0,0 +1,25 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "clientRBcpp/client_cpp.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
Receiver *receiver;
int counter;
};
#endif // MAINWINDOW_H

View File

@ -0,0 +1,37 @@
<?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>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QListWidget" name="listWidget"/>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>30</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

Binary file not shown.

View File

@ -0,0 +1,53 @@
#ifndef _CLIENT_H
#define _CLIENT_H
#include <QList>
#include <QMap>
#include <QObject>
#include <QString>
#include <QVariant>
#include "options/clientoption.h"
#include "options/consumeoption.h"
#include "options/exchangeoption.h"
using namespace std;
#define HEARBEATS "@@__Control__@@"
class Sender;
class Receiver;
class Client : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
QList<Sender *> *_senders;
QList<Receiver *> *_receivers;
public:
Client();
Client(ClientOption option);
virtual ~Client();
Client& operator=(Client client);
QString getVersion() const;
ClientOption getClientOption() const { return clientOption; }
Sender *createSender(ExchangeOption& option);
void removeSender(Sender *sender);
Receiver *createReceiver(ConsumeOption& option);
void removeReceiver(Receiver *receiver);
signals:
void onStop();
};
Q_DECLARE_METATYPE(string)
#endif // _CLIENT_H

View File

@ -0,0 +1,124 @@
#include "_client_.h"
#include "sender.h"
#include "receiver.h"
// Конструктор для связи с локальным RabbitMQ
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Client::Client() : QObject(nullptr)
{
{
static const int idMsg = qRegisterMetaType<ProduceMessage>();
static const int idMsgPtr = qRegisterMetaType<PtrProduceMessage>();
static const int idString = qRegisterMetaType<string>();
Q_UNUSED(idMsg)
Q_UNUSED(idMsgPtr)
Q_UNUSED(idString)
}
_senders = new QList<Sender *>();
_senders->clear();
_receivers = new QList<Receiver *>();
_receivers->clear();
}
Client::Client(ClientOption option) : Client()
{
clientOption = option;
}
Client::~Client()
{
for (auto &sender : *_senders)
delete sender;
delete _senders;
for (auto &receiver : *_receivers)
delete receiver;
delete _receivers;
}
Client& Client::operator=(Client client)
{
if (this != &client) {
this->clientOption = client.clientOption;
this->_senders = new QList<Sender *>();
this->_senders->clear();
this->_receivers = new QList<Receiver *>();
this->_receivers->clear();
}
return *this;
}
int majorVersion = 1;
int minorVersion = 1;
int releaseVersion = 1;
QString Client::getVersion() const
{
return QString::number(majorVersion) +
"." + QString::number(minorVersion) +
"." + QString::number(releaseVersion);
}
// Создание публикатора (издателя) сообщений
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sender *Client::createSender(ExchangeOption& option)
{
Sender *sender = new Sender(clientOption, option);
connect(this, &Client::onStop, sender, &Sender::slotStop);
connect(this, &Client::onStop, sender, &Sender::deleteLater);
_senders->append(sender);
return sender;
}
void Client::removeSender(Sender *sender)
{
if ( !_senders->contains(sender))
return;
sender->slotStop();
_senders->removeOne(sender);
}
// Создание потребителя сообщений
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Receiver *Client::createReceiver(ConsumeOption& option)
{
Receiver *receiver = new Receiver(clientOption, option);
connect(this, &Client::onStop, receiver, &Receiver::slotStop);
connect(this, &Client::onStop, receiver, &Receiver::deleteLater);
_receivers->append(receiver);
return receiver;
}
void Client::removeReceiver(Receiver *receiver)
{
if ( !_receivers->contains(receiver))
return;
receiver->slotStop();
_receivers->removeOne(receiver);
}

View File

@ -0,0 +1,22 @@
#ifndef CLIENT_CPP_H
#define CLIENT_CPP_H
#include "clientrbcpp_global.h"
#include "clientrbcpp.h"
#include "_client_.h"
#include "producemessage.h"
#include "properties.h"
#include "cworker.h"
#include "headers.h"
#include "pworker.h"
#include "receiver.h"
#include "sender.h"
#include "validator.h"
#include "vworker.h"
#include "options/clientoption.h"
#include "options/consumeoption.h"
#include "options/exchangeoption.h"
#include "options/queueoption.h"
#endif // CLIENT_CPP_H

View File

@ -0,0 +1,6 @@
#include "clientrbcpp.h"
ClientRBcpp::ClientRBcpp()
{
}

View File

@ -0,0 +1,13 @@
#ifndef CLIENTRBCPP_H
#define CLIENTRBCPP_H
#include "clientrbcpp_global.h"
class CLIENTRBCPPSHARED_EXPORT ClientRBcpp
{
public:
ClientRBcpp();
};
#endif // CLIENTRBCPP_H

View File

@ -0,0 +1,12 @@
#ifndef CLIENTRBCPP_GLOBAL_H
#define CLIENTRBCPP_GLOBAL_H
#include <QtCore/qglobal.h>
#if defined(CLIENTRBCPP_LIBRARY)
# define CLIENTRBCPPSHARED_EXPORT Q_DECL_EXPORT
#else
# define CLIENTRBCPPSHARED_EXPORT Q_DECL_IMPORT
#endif
#endif // CLIENTRBCPP_GLOBAL_H

View File

@ -0,0 +1,276 @@
#include <QCoreApplication>
#include "client_cpp.h"
#include <QDebug>
CWorker::CWorker(ClientOption& clientOption, ConsumeOption& consumeOption)
: QObject(nullptr), markStop(false)
{
this->clientOption = clientOption;
this->consumeOption = consumeOption;
this->consumeOption.receivingExchange = consumeOption.receivingExchange;
connection = nullptr;
channel = nullptr;
}
CWorker::~CWorker()
{
}
// Здесь реализуется основная деятельность потока
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void CWorker::doWork()
{
string host = clientOption.host.toStdString();
string port = QString::number(clientOption.port).toStdString();
string user = clientOption.user.toStdString();
string password = clientOption.password.toStdString();
string vhost = clientOption.vhost.toStdString();
string address = "amqp://" + user + ":" + password + "@" + host + ":" + port + vhost;
auto evbase = event_base_new();
AMQP::LibEventHandler handler(evbase);
// AMQP::TcpConnection connection(&handler, AMQP::Address(address));
// AMQP::TcpChannel channel(&connection);
connection = new AMQP::TcpConnection(&handler, AMQP::Address(address));
channel = new AMQP::TcpChannel(connection);
channel->setQos(1);
channel->onError([&](const char *message) {
Q_UNUSED(message)
emit onErrorConsume("Channel error!!!");
});
QTimer tm;
tm.stop();
tm.setInterval(30000);
connect(&tm, &QTimer::timeout, this, [&]() {
tm.stop();
connection->heartbeat();
tm.start();
});
tm.start();
// Обработка принятого сообщения
auto messageCb = [&](const AMQP::Message& message, uint64_t deliveryTag, bool redelivered)
{
Q_UNUSED(redelivered)
// Формируем принятое сообщениев формате ReceivedMessage
ProduceMessage *rMsg = new ProduceMessage;
rMsg->setBodyMsg(message.body(), message.bodySize());
// Формируем таблицу Properties свойств сообщения
Properties p;
if (message.hasContentType())
p.setContentType(QString::fromStdString(message.contentType()));
if (message.hasContentEncoding())
p.setContentEncoding(QString::fromStdString(message.contentEncoding()));
if (message.hasMessageID())
p.setMessageID(QString::fromStdString(message.messageID()));
if (message.hasCorrelationID())
p.setCorrelationID(QString::fromStdString(message.correlationID()));
if (message.timestamp())
p.setTimestamp(message.timestamp());
if (message.hasExpiration())
p.setExpiration(QString::fromStdString(message.expiration()));
if (message.hasDeliveryMode())
p.setDeliveryMode(message.deliveryMode());
if (message.hasAppID())
p.setAppID(QString::fromStdString(message.appID()));
if (message.hasUserID())
p.setUserID(QString::fromStdString(message.userID()));
if (message.hasTypeName())
p.setTypeName(QString::fromStdString(message.typeName()));
if (message.hasReplyTo())
p.setReplyTo(QString::fromStdString(message.replyTo()));
if (message.hasPriority())
p.setPriority(message.priority());
rMsg->setProperties(p);
// Работа со свойствами Headers
Headers h;
AMQP::Table table = message.headers();
vector<string> keys = table.keys();
string name;
for(uint i = 0; i < keys.size(); i++) {
name = keys[i];
if (table.get(name).isInteger()) {
int value = table.get(name);
h.set(QString::fromStdString(name), value);
}
else if (table.get(name).isString()) {
QString str = QString::fromStdString(table.get(name));
h.set(QString::fromStdString(name), str);
}
else if (table.get(name).isBoolean()) {
AMQP::Field &b = const_cast<AMQP::Field &>(table.get(name));
AMQP::BooleanSet &b1 = dynamic_cast<AMQP::BooleanSet &>(b);
bool value = b1.get(0);
h.set(QString::fromStdString(name), value);
}
}
rMsg->setHeaders(h);
emit onResultReady(rMsg, deliveryTag);
channel->ack(deliveryTag);
};
// объявление точки обмена
if (!consumeOption.receivingExchange.name.isEmpty()) {
string exchange = consumeOption.receivingExchange.name.toStdString();
string type = consumeOption.receivingExchange.type.toStdString();
// преобразование типа точки обмена в формат AMQP
AMQP::ExchangeType typeEx;
if (type == "" || type == "direct")
typeEx = AMQP::direct;
else if (type == "topic")
typeEx = AMQP::topic;
else if (type == "headers")
typeEx = AMQP::headers;
else
typeEx = AMQP::fanout;
// предобразование флагов точки обмена в формат AMQP
int flagsExchange = 0;
if (consumeOption.receivingExchange.durable)
flagsExchange |= AMQP::durable;
if (consumeOption.receivingExchange.auto_delete)
flagsExchange |= AMQP::autodelete;
if (consumeOption.receivingExchange.internal)
flagsExchange |= AMQP::internal;
AMQP::Table tableExch;
QString alt_e_name = "alternate-exchange";
QString alt_e_value = "";
if (consumeOption.receivingExchange.arguments.contains(alt_e_name)) {
alt_e_value = consumeOption.receivingExchange.arguments[alt_e_name].value<QString>();
tableExch.set(alt_e_name.toStdString(), alt_e_value.toStdString());
}
// Для предопределенных точек обмена их обьявление не производим
if ( exchange != "" && exchange != "amq.fanout" && exchange != "amq.direct" &&
exchange != "amq.topic" && exchange != "amq.headers") {
channel->declareExchange(exchange, typeEx, flagsExchange, tableExch)
.onError([&](const char *description) {
qDebug() << description;
});
}
QMultiMap<QString, QString>::iterator it = consumeOption.bindingArgs.begin();
for(; it != consumeOption.bindingArgs.end(); ++it) {
channel->bindExchange(it.key().toStdString(), exchange, it.value().toStdString()).onError([&](const char *description) {
qDebug() << description;
});
}
}
// объявление очереди
QueueOption option = consumeOption.queueOption;
string exchange = consumeOption.exchange.toStdString();
string queue = option.name.toStdString();
// Подготовка флагов для объявления очереди
int flagsQueue = 0;
if (option.durable)
flagsQueue |= AMQP::durable;
if (option.auto_delete)
flagsQueue |= AMQP::autodelete;
if (option.exclusive)
flagsQueue |= AMQP::exclusive;
channel->declareQueue(queue, flagsQueue)
.onSuccess( [&](const string &name, uint32_t messageCount, uint32_t consumerCount) {
Q_UNUSED(messageCount)
Q_UNUSED(consumerCount)
queue = name;
if (exchange != "")
for (QString rk : consumeOption.bindingKeys) {
channel->bindQueue(exchange, queue, rk.toStdString())
.onError( [&](const char *description) {
qDebug() << description;
});
}
});
// Подготовка флагов потребления сообщений
int flagsConsume = 0;
if (consumeOption.nolocal)
flagsConsume |= AMQP::nolocal;
if (consumeOption.noack)
flagsConsume |= AMQP::noack;
if (consumeOption.exclusive)
flagsConsume |= AMQP::exclusive;
channel->consume(queue, flagsConsume).onReceived(messageCb)
.onSuccess( [&](const string& tag) {
nextTag = tag;
})
.onError( [&](const char *description) {
emit onErrorConsume(description);
markStop = true; // Останов потока
});
//Цикл обработки событий
while(!markStop) {
event_base_loop(evbase, EVLOOP_ONCE);
QCoreApplication::processEvents();
}
// Закроем канал и соединение
channel->close();
connection->close();
event_base_loopbreak(evbase);
event_base_loopexit(evbase, 0);
event_base_free(evbase);
delete channel;
delete connection;
emit onWorkFinished(); // Посылаем сигнал о завершении работы
}
void CWorker::slotStop()
{
markStop = true;
channel->cancel(nextTag); // Отменить потребление
channel->close();
connection->close();
}
void CWorker::bind(QString exchange, QString key, bool ex)
{
if (ex) channel->bindExchange(exchange.toStdString(), consumeOption.exchange.toStdString(), key.toStdString());
else channel->bindQueue(exchange.toStdString(), consumeOption.queueOption.name.toStdString(), key.toStdString());
}
void CWorker::unbind(QString exchange, QString key, bool ex)
{
if (ex) channel->unbindExchange(exchange.toStdString(), consumeOption.exchange.toStdString(), key.toStdString());
else channel->unbindQueue(exchange.toStdString(), consumeOption.queueOption.name.toStdString(), key.toStdString());
}

View File

@ -0,0 +1,54 @@
#ifndef CWORKER_H
#define CWORKER_H
#include <QMap>
#include <QMutex>
#include <QObject>
#include <QString>
#include <QThread>
#include <QTimer>
#include <QVariant>
#include <iostream>
#include <amqpcpp.h>
#include <amqpcpp/libevent.h>
#include "client_cpp.h"
using namespace std;
class CWorker : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
ConsumeOption consumeOption;
bool markStop;
AMQP::TcpConnection *connection;
AMQP::TcpChannel *channel;
string nextTag;
public:
CWorker(ClientOption& clientOption, ConsumeOption& consumeOption);
virtual ~CWorker();
public slots:
void doWork();
void slotStop();
void bind(QString exchange, QString key, bool ex);
void unbind(QString exchange, QString key, bool ex);
signals:
void onResultReady(PtrProduceMessage msg, uint64_t deliveryTag);
void onErrorConsume(const char *description);
void onWorkFinished();
};
#endif // CWORKER_H

View File

@ -0,0 +1,40 @@
#include "headers.h"
Headers::Headers()
{
_headers.clear();
}
Headers::Headers(const Headers& h)
{
this->_headers = h._headers;
}
void Headers::operator=(const Headers& h)
{
this->_headers = h._headers;
}
QMap<QString,QVariant> Headers::getHeaders() const { return _headers; }
QList<QString> Headers::keys() const { return _headers.keys(); }
QList<QVariant> Headers::values() const { return _headers.values(); }
int Headers::size() const { return _headers.size(); }
bool Headers::contains(const QString name) const { return _headers.contains(name); }
void Headers::set(const QString name, bool value) { _headers.insert(name, value); }
void Headers::set(const QString name, int value) { _headers.insert(name, value); }
void Headers::set(const QString name, QString str) { _headers.insert(name, str); }
bool Headers::isBool(const QString name) const { return _headers[name].type() == QVariant::Bool; }
bool Headers::isInteger(const QString name) const { return _headers[name].type() == QVariant::Int; }
bool Headers::isString(const QString name) const { return _headers[name].type() == QVariant::String; }
bool Headers::getBool(const QString name) const { return _headers[name].value<bool>(); }
int Headers::getInteger(const QString name) const { return _headers[name].value<int>(); }
QString Headers::getString(const QString name) const { return _headers[name].value<QString>(); }

View File

@ -0,0 +1,45 @@
#ifndef HEADERS_H
#define HEADERS_H
#include <QList>
#include <QMap>
#include <QString>
#include <QVariant>
class Headers
{
private:
QMap<QString,QVariant> _headers;
public:
Headers();
Headers(const Headers& h); // Конструктор копирования
~Headers() {}
void operator=(const Headers& h);
QMap<QString,QVariant> getHeaders() const;
QList<QString> keys() const;
QList<QVariant> values() const;
int size() const;
bool contains(const QString name) const;
void set(const QString name, bool value);
void set(const QString name, int value);
void set(const QString name, QString str);
bool isBool(const QString name) const;
bool isInteger(const QString name) const;
bool isString(const QString name) const;
bool getBool(const QString name) const;
int getInteger(const QString name) const;
QString getString(const QString name) const;
};
#endif // HEADERS_H

View File

@ -0,0 +1,45 @@
#ifndef CLIENTOPTION_H
#define CLIENTOPTION_H
#include <QString>
// Значения по умолчанию
const QString DEFAULT_CPP_HOST = "localhost";
const int DEFAULT_CPP_PORT = 5672;
const QString DEFAULT_CPP_USER = "guest";
const QString DEFAULT_CPP_PASSWORD = "guest";
const QString DEFAULT_CPP_VHOST = "/";
struct ClientOption {
QString host;
int port;
QString user;
QString password;
QString vhost;
ClientOption() {
host = DEFAULT_CPP_HOST;
port = DEFAULT_CPP_PORT;
user = DEFAULT_CPP_USER;
password = DEFAULT_CPP_PASSWORD;
vhost = DEFAULT_CPP_VHOST;
}
~ClientOption() {}
ClientOption(const ClientOption& src) = default; // Конструктор копирования
ClientOption(ClientOption&& src) = default; // Конструктор перемещения
ClientOption& operator=(const ClientOption rhs) // Оператор присваивания
{
host = rhs.host;
port = rhs.port;
user = rhs.user;
password = rhs.password;
vhost = rhs.vhost;
return *this;
}
};
#endif // CLIENTOPTION_H

View File

@ -0,0 +1,52 @@
#ifndef CONSUMEOPTION_H
#define CONSUMEOPTION_H
#include <QObject>
#include <QString>
#include <QList>
#include "queueoption.h"
#include "exchangeoption.h"
struct ConsumeOption
{
QString exchange; // Имя точки обмена для связывания
QStringList bindingKeys; // ключи связи точки с очередью
bool nolocal;
bool noack;
bool exclusive;
QueueOption queueOption; // Параметры очереди
ExchangeOption receivingExchange; // Параметры новой принимающей очереди (по умолчанию новой не создаётся)
QMultiMap<QString, QString> bindingArgs; // список связей для точки обмена (если создаётся новая точка)
ConsumeOption() {
exchange = "";
receivingExchange.name = "";
nolocal = false;
noack = false;
exclusive = false;
}
~ConsumeOption() {}
ConsumeOption(const ConsumeOption& src) = default; // Конструктор копирования
ConsumeOption(ConsumeOption&& src) = default; // Конструктор перемещения
ConsumeOption& operator=(const ConsumeOption rhs) // Оператор присваивания
{
exchange = rhs.exchange;
bindingKeys = rhs.bindingKeys;
nolocal = rhs.nolocal;
noack = rhs.noack;
exclusive = rhs.exclusive;
queueOption = rhs.queueOption;
bindingArgs = rhs.bindingArgs;
return *this;
}
};
#endif // CONSUMEOPTION_H

View File

@ -0,0 +1,55 @@
#ifndef EXCHANGEOPTION_H
#define EXCHANGEOPTION_H
#include <QString>
#include <QVariantMap>
#include <QList>
struct ExchangeOption
{
QString name; // уникальное имя точки обмена
QString type; // тип точки обмена (direct, topic,
// fanout или headers)
bool auto_delete; // автоматически удаляемая точка обмена
bool durable; // долгоживущая точка обмена
bool passive; // требуется информация о точке обмена
bool internal; // нельзя вести публикацию из приложения
QVariantMap arguments; // необязательные аргументы
QMap<QString, QString> bindingArgs; // список связей для точки обмена
bool ifunused; // можно удалять, только если точка обмена
// не используется (не имеет потребителей)
ExchangeOption() {
name = "";
type = "";
auto_delete = false;
durable = false;
passive = false;
internal = false;
arguments.clear();
ifunused = false;
}
~ExchangeOption() {}
ExchangeOption(const ExchangeOption& src) = default; // Конструктор копирования
ExchangeOption(ExchangeOption&& src) = default; // Конструктор перемещения
ExchangeOption& operator=(const ExchangeOption rhs) // Оператор присваивания
{
name = rhs.name;
type = rhs.type;
auto_delete = rhs.auto_delete;
durable = rhs.durable;
passive = rhs.passive;
internal = rhs.internal;
arguments = rhs.arguments;
ifunused = rhs.ifunused;
return *this;
}
};
#endif // EXCHANGEOPTION_H

View File

@ -0,0 +1,63 @@
#ifndef QUEUEOPTION_H
#define QUEUEOPTION_H
#include <QObject>
#include <QString>
#include <QVariantMap>
struct QueueOption
{
QString name; // Уникальное имя очереди
bool auto_delete; // Автоматически удаляемая очередь
bool durable; // Долгоживущая очередь
bool passive; // Требуется информация об очереди
bool exclusive; // Исключительная очередь
QVariantMap arguments; // Необязательные аргументы очереди
bool ifunused; // Удалять, только если не используется
bool ifempty; // Удалять, только если очередь пуста
int messageCount; // Число сообщений в очереди
int consumerCount; // Число потребителей очереди
QueueOption() {
name = "";
auto_delete = false;
durable = false;
passive = false;
exclusive = false;
arguments.clear();
ifunused = false;
ifempty = false;
messageCount = 0;
consumerCount = 0;
}
~QueueOption() {}
QueueOption(const QueueOption& src) = default; // Конструктор копирования
QueueOption(QueueOption&& src) = default; // Конструктор перемещения
QueueOption& operator=(const QueueOption rhs) // Оператор присваивания
{
name = rhs.name;
auto_delete = rhs.auto_delete;
passive = rhs.passive;
exclusive = rhs.exclusive;
arguments = rhs.arguments;
ifunused = rhs.ifunused;
ifempty = rhs.ifempty;
messageCount = rhs.messageCount;
consumerCount = rhs.consumerCount;
return *this;
}
};
#endif // QUEUEOPTION_H

View File

@ -0,0 +1,37 @@
#include "producemessage.h"
// Конструктор по умолчанию
ProduceMessage::ProduceMessage()
{
_body.clear();
}
// Конструктор копирования
ProduceMessage::ProduceMessage(const ProduceMessage& msg)
{
this->_body = msg._body;
this->_headers = msg._headers;
this->_properties = msg._properties;
}
ProduceMessage& ProduceMessage::operator=(const ProduceMessage& msg)
{
if (this != &msg) {
this->_body = msg._body;
this->_headers = msg._headers;
this->_properties = msg._properties;
}
return *this;
}
QByteArray ProduceMessage::getBodyMsg() const { return _body; }
void ProduceMessage::setBodyMsg(const QByteArray &ba) { _body = ba; }
void ProduceMessage::setBodyMsg(const char *body, int size) { _body = QByteArray(body, size); }
Headers ProduceMessage::getHeaders() const { return _headers; }
void ProduceMessage::setHeaders(const Headers &headers) { _headers = headers; }
Properties ProduceMessage::getProperties() const { return _properties; }
void ProduceMessage::setProperties(const Properties &properties) { _properties = properties; }

View File

@ -0,0 +1,43 @@
#ifndef PRODUCEMESSAGE_H
#define PRODUCEMESSAGE_H
#include <QByteArray>
#include "properties.h"
#include "headers.h"
class ProduceMessage
{
public:
ProduceMessage();
ProduceMessage(const ProduceMessage& msg);
~ProduceMessage() {}
ProduceMessage& operator=(const ProduceMessage& msg);
QByteArray getBodyMsg() const;
void setBodyMsg(const QByteArray &ba);
void setBodyMsg(const char *body, int size);
Headers getHeaders() const;
void setHeaders(const Headers &headers);
Properties getProperties() const;
void setProperties(const Properties &properties);
private:
QByteArray _body;
protected:
Properties _properties;
Headers _headers;
};
using PtrProduceMessage = ProduceMessage*;
Q_DECLARE_METATYPE(ProduceMessage)
Q_DECLARE_METATYPE(PtrProduceMessage)
#endif // PRODUCEMESSAGE_H

View File

@ -0,0 +1,63 @@
#include "properties.h"
Properties::Properties()
{
_properties.clear();
setDeliveryMode(1); // не оставлять сообщения
}
// Конструктор копирования
Properties::Properties(const Properties& p)
{
this->_properties = p._properties;
}
void Properties::operator=(const Properties& p)
{
this->_properties = p._properties;
}
const QMap<QString,QVariant> &Properties::getProperties() { return _properties; }
bool Properties::contains(const QString name) const { return _properties.contains(name); }
bool Properties::isContentType() const { return _properties.contains("content-type"); }
bool Properties::isContentEncoding() const { return _properties.contains("content-encoding"); }
bool Properties::isMessageID() const { return _properties.contains("message-id"); }
bool Properties::isCorrelationID() const { return _properties.contains("correlation-id"); }
bool Properties::isTimestamp() const { return _properties.contains("timestamp"); }
bool Properties::isExpiration() const { return _properties.contains("expiration"); }
bool Properties::isDeliveryMode() const { return _properties.contains("delivery-mode"); }
bool Properties::isAppID() const { return _properties.contains("app-id"); }
bool Properties::isUserID() const { return _properties.contains("user-id"); }
bool Properties::isTypeName() const { return _properties.contains("type"); }
bool Properties::isReplyTo() const { return _properties.contains("reply-to"); }
bool Properties::isPriority() const { return _properties.contains("priority"); }
void Properties::setContentType(const QString &str) { _properties.insert("content-type", str); }
void Properties::setContentEncoding(const QString &str) { _properties.insert("content-encoding", str); }
void Properties::setMessageID(const QString &str) { _properties.insert("message-id", str); }
void Properties::setCorrelationID(const QString &str) { _properties.insert("correlation-id", str); }
void Properties::setTimestamp(const quint64 val) { _properties.insert("timestamp", val); }
void Properties::setExpiration(const QString &str) { _properties.insert("expiration", str); }
void Properties::setDeliveryMode(const quint8 val) { _properties.insert("delivery-mode", val); }
void Properties::setAppID(const QString &str) { _properties.insert("app-id", str); }
void Properties::setUserID(const QString &str) { _properties.insert("user-id", str); }
void Properties::setTypeName(const QString &str) { _properties.insert("type", str); }
void Properties::setReplyTo(const QString &str) { _properties.insert("reply-to", str); }
void Properties::setPriority(const quint8 val) { _properties.insert("priority", val); }
QString Properties::getContentType() const { return _properties["content-type"].value<QString>(); }
QString Properties::getContentEncoding() const { return _properties["content-encoding"].value<QString>(); }
QString Properties::getMessageID() const { return _properties["message-id"].value<QString>(); }
QString Properties::getCorrelationID() const { return _properties["correlation-id"].value<QString>(); }
quint64 Properties::getTimestamp() const { return _properties["timestamp"].value<quint64>(); }
QString Properties::getExpiration() const { return _properties["expiration"].value<QString>(); }
quint8 Properties::getDeliveryMode() const { return _properties["delivery-mode"].value<quint8>(); }
QString Properties::getAppID() const { return _properties["app-id"].value<QString>(); }
QString Properties::getUserID() const { return _properties["user-id"].value<QString>(); }
QString Properties::getTypeName() const { return _properties["type"].value<QString>(); }
QString Properties::getReplyTo() const { return _properties["reply-to"].value<QString>(); }
quint8 Properties::getPriority() const { return _properties["priority"].value<quint8>(); }

View File

@ -0,0 +1,73 @@
#ifndef PROPERTIES_H
#define PROPERTIES_H
#include <QtGlobal>
#include <QMap>
#include <QString>
#include <QVariant>
class Properties
{
private:
QMap<QString,QVariant> _properties;
public:
Properties();
Properties(const Properties& p);
~Properties() {}
void operator=(const Properties& p);
int size() {return _properties.size(); }
const QMap<QString,QVariant> &getProperties();
bool contains(const QString name) const;
bool isContentType() const;
bool isContentEncoding() const;
bool isMessageID() const;
bool isCorrelationID() const;
bool isTimestamp() const;
bool isExpiration() const;
bool isDeliveryMode() const;
bool isAppID() const;
bool isUserID() const;
bool isTypeName() const;
bool isReplyTo() const;
bool isPriority() const;
void setContentType(const QString &str);
void setContentEncoding(const QString &str);
void setMessageID(const QString &str);
void setCorrelationID(const QString &str);
void setTimestamp(const quint64 val);
void setExpiration(const QString &str);
void setDeliveryMode(const quint8 val);
void setAppID(const QString &str);
void setUserID(const QString &str);
void setTypeName(const QString &str);
void setReplyTo(const QString &str);
void setPriority(const quint8 val);
QString getContentType() const;
QString getContentEncoding() const;
QString getMessageID() const;
QString getCorrelationID() const;
quint64 getTimestamp() const;
QString getExpiration() const;
quint8 getDeliveryMode() const;
QString getAppID() const;
QString getUserID() const;
QString getTypeName() const;
QString getReplyTo() const;
quint8 getPriority() const;
};
#endif // PROPERTIES_H

View File

@ -0,0 +1,330 @@
/**************************************************************************
* PWorker - Publish Worker, - рабочий поток публикации сообщений *
* редакция от 08.06.2022 *
* Принадлежность: библиотека clientRBcpp *
**************************************************************************/
#include <QCoreApplication>
#include "_client_.h"
#include "sender.h"
#include "pworker.h"
#include <QDebug>
PWorker::PWorker(ClientOption& clientOption, ExchangeOption& exchangeOption)
: QObject(nullptr), markStop(false)
{
this->clientOption = clientOption;
this->exchangeOption = exchangeOption;
qu.clear();
// static const int idE2E = qRegisterMetaType<E2EStruct>();
// Q_UNUSED(idE2E)
}
PWorker::~PWorker()
{
// Освободим очередь сообщений
mutex.lock();
while (! qu.isEmpty()) {
PublishPacket *packet = qu.dequeue();
AMQP::Envelope *envelope = packet->envelope;
delete envelope->body();
delete envelope;
delete packet;
}
mutex.unlock();
}
// Здесь реализуется основная деятельность потока
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void PWorker::doWork()
{
string host = clientOption.host.toStdString();
string port = QString::number(clientOption.port).toStdString();
string user = clientOption.user.toStdString();
string password = clientOption.password.toStdString();
string vhost = clientOption.vhost.toStdString();
string address = "amqp://" + user + ":" + password + "@" + host + ":" + port + vhost;
// Обрабатываем аргументы на предмет альтернативной точки обмена
AMQP::Table table;
QString alt_e_name = "alternate-exchange";
QString alt_e_value = "";
if (exchangeOption.arguments.contains(alt_e_name)) {
alt_e_value = exchangeOption.arguments[alt_e_name].value<QString>();
table.set(alt_e_name.toStdString(), alt_e_value.toStdString());
}
string alt_exchange = alt_e_value.toStdString(); // Имя альтернативной точки обмена
AMQP::ExchangeType typeAltEx = AMQP::fanout; // Тип альтернативной точки обмена - всегда fanout
int flagsAltEx = (AMQP::durable | AMQP::internal); // Точка долгоживущая и внутренняя
auto evbase = event_base_new();
AMQP::LibEventHandler handler(evbase);
AMQP::TcpConnection connection(&handler, AMQP::Address(address));
AMQP::TcpChannel channel(&connection);
channel.setQos(1);
channel.confirmSelect()
.onAck([&](uint64_t deliveryTag, bool multiple) {
emit onReceivedAckNack(deliveryTag, true, multiple);
})
.onNack([&](uint64_t deliveryTag, bool multiple, bool requeue) {
Q_UNUSED(requeue)
emit onReceivedAckNack(deliveryTag, false, multiple);
});
// Объявляем альтернативную точку обмена
//--------------------------------------
if (alt_e_value != "") {
channel.declareExchange(alt_exchange, typeAltEx, flagsAltEx)
.onError( [&](const char *message) {
string msg(message);
emit onError(msg);
});
}
// Обработка основной точки обмена
//----------------------------------
string exchange = exchangeOption.name.toStdString();
string type = exchangeOption.type.toStdString();
// преобразование типа точки обмена в формат AMQP
AMQP::ExchangeType typeEx;
if (type == "" || type == "direct")
typeEx = AMQP::direct;
else if (type == "topic")
typeEx = AMQP::topic;
else if (type == "headers")
typeEx = AMQP::headers;
else
typeEx = AMQP::fanout;
// предобразование флагов точки обмена в формат AMQP
int flagsExchange = 0;
if (exchangeOption.durable)
flagsExchange |= AMQP::durable;
if (exchangeOption.auto_delete)
flagsExchange |= AMQP::autodelete;
if (exchangeOption.internal)
flagsExchange |= AMQP::internal;
// Для предопределенных точек обмена их обьявление не производим
if ( exchange != "" && exchange != "amq.fanout" && exchange != "amq.direct" &&
exchange != "amq.topic" && exchange != "amq.headers") {
channel.declareExchange(exchange, typeEx, flagsExchange, table)
.onError( [&](const char *message) {
string msg(message);
emit onError(msg);
});
}
// обработка mandatory
auto messageCb = [&](const AMQP::Message& message, int16_t code, const std::string &description)
{
Q_UNUSED(code)
Q_UNUSED(description)
// Формируем принятое сообщениев формате ReceivedMessage
ProduceMessage *rMsg = new ProduceMessage;
rMsg->setBodyMsg(message.body(), message.bodySize());
// Формируем таблицу Properties свойств сообщения
Properties p;
if (message.hasContentType())
p.setContentType(QString::fromStdString(message.contentType()));
if (message.hasContentEncoding())
p.setContentEncoding(QString::fromStdString(message.contentEncoding()));
if (message.hasMessageID())
p.setMessageID(QString::fromStdString(message.messageID()));
if (message.hasCorrelationID())
p.setCorrelationID(QString::fromStdString(message.correlationID()));
if (message.timestamp())
p.setTimestamp(message.timestamp());
if (message.hasExpiration())
p.setExpiration(QString::fromStdString(message.expiration()));
if (message.hasDeliveryMode())
p.setDeliveryMode(message.deliveryMode());
if (message.hasAppID())
p.setAppID(QString::fromStdString(message.appID()));
if (message.hasUserID())
p.setUserID(QString::fromStdString(message.userID()));
if (message.hasTypeName())
p.setTypeName(QString::fromStdString(message.typeName()));
if (message.hasReplyTo())
p.setReplyTo(QString::fromStdString(message.replyTo()));
if (message.hasPriority())
p.setPriority(message.priority());
rMsg->setProperties(p);
// Работа со свойствами Headers
Headers h;
AMQP::Table table = message.headers();
vector<string> keys = table.keys();
string name;
for(uint i = 0; i < keys.size(); i++) {
name = keys[i];
if (table.get(name).isInteger()) {
int value = table.get(name);
h.set(QString::fromStdString(name), value);
}
else if (table.get(name).isString()) {
QString str = QString::fromStdString(table.get(name));
h.set(QString::fromStdString(name), str);
}
else if (table.get(name).isBoolean()) {
AMQP::Field &b = const_cast<AMQP::Field &>(table.get(name));
AMQP::BooleanSet &b1 = dynamic_cast<AMQP::BooleanSet &>(b);
bool value = b1.get(0);
h.set(QString::fromStdString(name), value);
}
}
rMsg->setHeaders(h);
emit onMessageBounced(rMsg);
};
channel.recall().onReceived(messageCb);
// Цикл событий (event loop)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
while (! markStop) {
// Обрабатываем очередь сообщений на передачу
if (! qu.isEmpty()) {
mutex.lock();
PublishPacket *packet = qu.dequeue();
mutex.unlock();
AMQP::Envelope *envelope = packet->envelope;
int publishFlags = packet->publishFlags;
string routingKey = packet->routingKey;
if (envelope->hasAppID() && envelope->appID() == HEARBEATS)
connection.heartbeat();
else
channel.publish(exchange, routingKey, *envelope, publishFlags);
delete envelope->body();
delete envelope;
delete packet;
}
event_base_loop(evbase, EVLOOP_NONBLOCK);
QCoreApplication::processEvents();
QThread::msleep(0);
} //while
// Закроем канал и соединение
channel.close();
connection.close();
event_base_loopbreak(evbase);
event_base_loopexit(evbase, 0);
event_base_free(evbase);
emit onWorkFinished(); // Посылаем сигнал о завершении работы
}
// Прием данных, предразование в формат передачи
// и постановка в очередь на выдачу
void PWorker::sending(ProduceMessage msg, QString routingKey, bool mandatory)
{
mutex.lock();
uint64_t size = msg.getBodyMsg().size();
char *body = new char[size];
memcpy(body, msg.getBodyMsg().data(), size);
AMQP::Envelope *env = new AMQP::Envelope(body, size);
// Готовим сообщение для отправки
Properties p = msg.getProperties();
if (p.contains("content-type"))
env->setContentType(p.getContentType().toStdString().c_str());
if (p.contains("content-encoding"))
env->setContentEncoding(p.getContentEncoding().toStdString().c_str());
if (p.contains("message-id"))
env->setMessageID(p.getMessageID().toStdString().c_str());
if (p.contains("correlation-id"))
env->setCorrelationID(p.getCorrelationID().toStdString().c_str());
if (p.contains("timestamp"))
env->setTimestamp(p.getTimestamp());
if (p.contains("expiration"))
env->setExpiration(p.getExpiration().toStdString().c_str());
if (p.contains("delivery-mode"))
env->setDeliveryMode(p.getDeliveryMode());
if (p.contains("app-id"))
env->setAppID(p.getAppID().toStdString().c_str());
if (p.contains("user-id"))
env->setUserID(p.getUserID().toStdString().c_str());
if (p.contains("type"))
env->setTypeName(p.getTypeName().toStdString().c_str());
if (p.contains("reply-to"))
env->setReplyTo(p.getReplyTo().toStdString().c_str());
if (p.contains("priority"))
env->setPriority(p.getPriority());
AMQP::Table table;
Headers p2 = msg.getHeaders();
QList<QString> k = p2.keys();
QList<QVariant> v = p2.values();
for (int i=0; i < p2.size(); i++) {
QString name = k[i];
QVariant val = v[i];
if (val.type() == QVariant::Int) {
AMQP::Long numb = val.value<int>();
table.set(name.toStdString(), numb.value());
}
else if (val.type() == QVariant::String) {
QString str = val.value<QString>();
AMQP::ShortString s(str.toStdString());
table.set(name.toStdString(), s.value());
}
else if (val.type() == QVariant::Bool) {
bool numb = val.value<bool>();
table.set(name.toStdString(), numb);
}
}
env->setHeaders(table);
int flags = 0; // флаги - в формат AMQP
if (mandatory)
flags |= AMQP::mandatory;
string routing = routingKey.toStdString();
// формируем пакет для постановки в очередь
PublishPacket *pp = new PublishPacket;
pp->envelope = env;
pp->publishFlags = flags;
pp->routingKey = routing;
qu.enqueue(pp);
mutex.unlock();
}
void PWorker::stop()
{
markStop = true; // завершить цикл обработки сообщений
}

View File

@ -0,0 +1,58 @@
#ifndef PWORKER_H
#define PWORKER_H
#include <QMutex>
#include <QObject>
#include <QQueue>
#include <QString>
#include <QVariant>
#include <iostream>
#include <amqpcpp.h>
#include <amqpcpp/libevent.h>
#include "client_cpp.h"
using namespace std;
struct PublishPacket {
AMQP::Envelope *envelope;
string routingKey;
int publishFlags;
};
class PWorker : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
ExchangeOption exchangeOption;
bool markStop;
QMutex mutex;
QQueue<PublishPacket *> qu;
public:
PWorker(ClientOption& clientOption, ExchangeOption& exchangeOption);
virtual ~PWorker();
public slots:
void doWork();
void sending(ProduceMessage msg, QString routingKey, bool mandatory=false);
void stop();
signals:
void onMessageBounced(PtrProduceMessage msg);
void onError(string msg);
void onReceivedAckNack(uint64_t deliveryTag, bool ack, bool multiple);
void onWorkFinished();
};
#endif // PWORKER_H

View File

@ -0,0 +1,65 @@
#include <QCoreApplication>
#include "client_cpp.h"
Receiver::Receiver(ClientOption &clientOption, ConsumeOption &consumeOption)
: QObject(nullptr)
{
this->clientOption = clientOption;
this->consumeOption = consumeOption;
this->consumeOption.receivingExchange = consumeOption.receivingExchange;
this->consumeOption.bindingArgs = consumeOption.bindingArgs;
CWorker *worker = new CWorker(this->clientOption, this->consumeOption);
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::started, worker, &CWorker::doWork);
connect(&workerThread, &QThread::finished, worker, &CWorker::slotStop);
// Автоматическое удаление объектов PWorker и QThread по окончании работы
connect(worker, &CWorker::onWorkFinished, worker, &CWorker::deleteLater);
// Посылаемые потоку сигналы
connect(this, &Receiver::onStop, worker, &CWorker::slotStop, Qt::DirectConnection);
connect(this, &Receiver::doBind, worker, &CWorker::bind, Qt::DirectConnection);
connect(this, &Receiver::doUnbind, worker, &CWorker::unbind, Qt::DirectConnection);
// Сигналы, принимаемые от потока
connect(worker, &CWorker::onWorkFinished, &workerThread, &QThread::quit, Qt::QueuedConnection);
connect(worker, &CWorker::onResultReady, this, &Receiver::slotMsgReady, Qt::QueuedConnection);
connect(worker, &CWorker::onErrorConsume, this, &Receiver::slotErrorMsg, Qt::QueuedConnection);
workerThread.start();
}
Receiver::~Receiver()
{
workerThread.quit();
workerThread.wait();
}
// При получении от потока сигнала о приеме сообщения
// выпускаем сигнал дальше
void Receiver::slotMsgReady(PtrProduceMessage msg, uint64_t deliveryTag)
{
ProduceMessage message = *msg;
delete msg;
emit onMessage(message, deliveryTag);
}
// При получении сигнала об ошибке транслируем его
void Receiver::slotErrorMsg(const char *description)
{
QString str(description);
emit onError(str);
}
void Receiver::slotStop()
{
emit onStop();
}

View File

@ -0,0 +1,47 @@
#ifndef RECEIVER_H
#define RECEIVER_H
#include <QObject>
#include <QList>
#include <QMap>
#include <QString>
#include <QTimer>
#include <QThread>
#include <QVariant>
#include "_client_.h"
#include "cworker.h"
using namespace std;
class Receiver : public QObject
{
Q_OBJECT
private:
bool stop;
ClientOption clientOption;
ConsumeOption consumeOption;
QThread workerThread;
public:
Receiver(ClientOption& clientOption, ConsumeOption& consumeOption);
virtual ~Receiver();
public slots:
void slotMsgReady(PtrProduceMessage msg, uint64_t deliveryTag);
void slotErrorMsg(const char *description);
void slotStop();
signals:
void onMessage(ProduceMessage msg, uint64_t deliveryTag);
void onError(QString description);
void onStop();
void doBind(QString exchange, QString key, bool ex);
void doUnbind(QString exchange, QString key, bool ex);
};
#endif // RECEIVER_H

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