Compare commits
560 Commits
20230510-f
...
mp-2025_02
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6174233731 | ||
|
|
216a13e90d | ||
|
|
44c5eb65bd | ||
|
|
edb6c90f46 | ||
|
|
58bc39c2fb | ||
|
|
fecaaaf341 | ||
|
|
263ce00a14 | ||
|
|
9cca3af402 | ||
|
|
541b5ee2ca | ||
|
|
dc4a02a8ff | ||
|
|
5011e7e123 | ||
|
|
cf9408b594 | ||
|
|
3e4e3ffe2d | ||
|
|
9c1188b4d1 | ||
|
|
ea564ba101 | ||
|
|
ca2103f7b3 | ||
|
|
58c6d12e36 | ||
|
|
2f468e4f78 | ||
|
|
daba490c09 | ||
|
|
3638378344 | ||
|
|
f26effc8b3 | ||
|
|
8b22e7d22a | ||
|
|
b413b172e5 | ||
|
|
4e85b59ec9 | ||
|
|
c7551512bc | ||
|
|
6b61763d6f | ||
|
|
106f47d465 | ||
|
|
0cfec70025 | ||
|
|
72e32847c7 | ||
|
|
c6f4759530 | ||
|
|
3e9aa7969b | ||
|
|
e308e07b87 | ||
|
|
f2d527cd2f | ||
|
|
d887747b3c | ||
|
|
bebc4c1b0d | ||
|
|
8968f14122 | ||
|
|
f63079af11 | ||
|
|
cf8edbbd5f | ||
|
|
f1f24168e5 | ||
|
|
d5d6217c5e | ||
|
|
b60de3994c | ||
|
|
6e25647c0a | ||
|
|
a7990db831 | ||
|
|
bf61be029e | ||
|
|
c5ab06b6d0 | ||
|
|
bcc7194254 | ||
|
|
2a776ca895 | ||
|
|
35f2c2bd7e | ||
|
|
393d4b4f56 | ||
|
|
4339a8e80b | ||
|
|
351ab57d62 | ||
|
|
0790ac0dea | ||
|
|
358ceaaa7d | ||
|
|
50ba9ee3a1 | ||
|
|
703f8042f9 | ||
|
|
601559ca6e | ||
|
|
a36639ed8c | ||
|
|
224a5f336a | ||
|
|
9719063a21 | ||
|
|
dbe23f7e73 | ||
|
|
702acb3304 | ||
|
|
cc8dd6e22f | ||
|
|
bb73225b86 | ||
|
|
31ca7ae529 | ||
|
|
64e781f82f | ||
|
|
2d259ae9be | ||
|
|
b38d864612 | ||
|
|
52bbfcfb15 | ||
|
|
a96bd7fc07 | ||
|
|
7962ff6213 | ||
|
|
e840bfd9e6 | ||
|
|
779b37f195 | ||
|
|
dfbc10149c | ||
|
|
22a5402141 | ||
|
|
19af9daea7 | ||
|
|
0a10d136d5 | ||
|
|
0d7aca0060 | ||
|
|
7d33e451cd | ||
|
|
d631c5465f | ||
|
|
ecb2d85ea2 | ||
|
|
c390b1059c | ||
|
|
ba279ae2bb | ||
|
|
30a2743f19 | ||
|
|
46d939fa31 | ||
|
|
f2aff6c235 | ||
|
|
e97a5c0720 | ||
|
|
6dd9a3ba8f | ||
|
|
1775fb7785 | ||
|
|
a639316cdf | ||
|
|
87706e1265 | ||
|
|
fff64ae41e | ||
|
|
3633bb03fc | ||
|
|
172998a5c4 | ||
|
|
acbe4505b9 | ||
|
|
b2193afa46 | ||
|
|
28b130ecd4 | ||
|
|
2ac1182879 | ||
|
|
f2d18fceee | ||
|
|
43c23d754c | ||
|
|
8eee81c682 | ||
|
|
34d395bb0b | ||
|
|
277af02f68 | ||
|
|
68ef24ea9b | ||
|
|
a3d5b33105 | ||
|
|
a7adf15bf6 | ||
|
|
a435b995a8 | ||
|
|
9d71ff27a7 | ||
|
|
090e178ed1 | ||
|
|
a78e354c28 | ||
|
|
9c87c3e5f3 | ||
|
|
01133cc35b | ||
|
|
4dc6031d03 | ||
|
|
c3fd458769 | ||
|
|
433720525d | ||
|
|
375ab592ff | ||
|
|
d2694df67c | ||
|
|
2270163837 | ||
|
|
9282ac3bb2 | ||
|
|
6f858e3909 | ||
|
|
cf26a01bb8 | ||
|
|
2e3705946e | ||
|
|
42f76f9d09 | ||
|
|
bc7a549513 | ||
|
|
f002587aee | ||
|
|
a520b37089 | ||
|
|
4f628a16c6 | ||
|
|
630dc2cecb | ||
|
|
51a585740d | ||
|
|
dab4980a7a | ||
|
|
e847130f0c | ||
|
|
4c88c15874 | ||
|
|
132ec3ce95 | ||
|
|
9178a6fca5 | ||
|
|
eb68e66746 | ||
|
|
b72d474a7f | ||
|
|
2b68bfd3af | ||
|
|
e4a0e8557a | ||
|
|
57bbefdf38 | ||
|
|
7707cb0a82 | ||
|
|
dfad168875 | ||
|
|
7fde61f849 | ||
|
|
8904d33789 | ||
|
|
d28e20b179 | ||
|
|
c5915b5583 | ||
|
|
2626c4365c | ||
|
|
9c35e7a006 | ||
|
|
061438b7c8 | ||
|
|
88035efdf9 | ||
|
|
3546c93d23 | ||
|
|
e232b4adbf | ||
|
|
89019f1e60 | ||
|
|
efc91241d9 | ||
|
|
c90c33ec11 | ||
|
|
aca24183e4 | ||
|
|
1626526714 | ||
|
|
7ad64cd654 | ||
|
|
cc6ed1198b | ||
|
|
fae5b55e82 | ||
|
|
a6af10a32d | ||
|
|
97138bcbe0 | ||
|
|
2b8cabd391 | ||
|
|
85d0992ce9 | ||
|
|
b3199681de | ||
|
|
b78fc0999b | ||
|
|
3cba4a1f7c | ||
|
|
d7705590fe | ||
|
|
adcb037976 | ||
|
|
072ed88102 | ||
|
|
cc1dcc4d7f | ||
|
|
25efeeb244 | ||
|
|
0926ba2381 | ||
|
|
1d08945ae5 | ||
|
|
d2ac801223 | ||
|
|
8f6291ef02 | ||
|
|
21d47b8b7f | ||
|
|
6f5da8ee6c | ||
|
|
9c2001285b | ||
|
|
8199f7c3b0 | ||
|
|
03f9178dd2 | ||
|
|
0566483575 | ||
|
|
3a9bb3d5ec | ||
|
|
2d7459394e | ||
|
|
bb77c6acd1 | ||
|
|
10564ec78f | ||
|
|
ee73bb0f69 | ||
|
|
8aa4143d8a | ||
|
|
c22edcbbec | ||
|
|
8dd9e69bdf | ||
|
|
2014304c73 | ||
|
|
c2d7564e0c | ||
|
|
d70c673b8a | ||
|
|
23489dd5bf | ||
|
|
5767c1307d | ||
|
|
b93ead5e5f | ||
|
|
6fc7f5fc7a | ||
|
|
60c86c7598 | ||
|
|
07f99b7050 | ||
|
|
6676024cb0 | ||
|
|
cf9118e41d | ||
|
|
49d037c040 | ||
|
|
b0b6efb1c3 | ||
|
|
be053b035f | ||
|
|
a839d97140 | ||
|
|
bdcbaa2b46 | ||
|
|
a624331166 | ||
|
|
7e4977f472 | ||
|
|
9adb95d532 | ||
|
|
f729766e6b | ||
|
|
4548b3c225 | ||
|
|
caa7138738 | ||
|
|
91752156f2 | ||
|
|
a1a64fec42 | ||
|
|
9e80636acb | ||
|
|
741dd4fe12 | ||
|
|
5ca1fd4275 | ||
|
|
7590aa3717 | ||
|
|
08399ecf87 | ||
|
|
0e83988904 | ||
|
|
3bec999ae0 | ||
|
|
5b3c813981 | ||
|
|
b490646799 | ||
|
|
f912c537d4 | ||
|
|
3af514d946 | ||
|
|
9a2a1764d9 | ||
|
|
4687a8726b | ||
|
|
7a5900be25 | ||
|
|
2d09e22ec6 | ||
|
|
cdcb4e2b3e | ||
|
|
6ff68b848c | ||
|
|
0f98ed87a4 | ||
|
|
ee94d8a583 | ||
|
|
4ba0e01c9f | ||
|
|
c908eb4840 | ||
|
|
892dc65898 | ||
|
|
0c819bcd19 | ||
|
|
caf04e88f3 | ||
|
|
79f4018341 | ||
|
|
f1fe99673b | ||
|
|
79afc70b92 | ||
|
|
ec033cfd10 | ||
|
|
0107330c32 | ||
|
|
cb43378a2f | ||
|
|
0fafb36100 | ||
|
|
88aea30da1 | ||
|
|
4a20933397 | ||
|
|
a10cd8293b | ||
|
|
c055ee6cc2 | ||
|
|
7fbe9744ea | ||
|
|
0fabc6d613 | ||
|
|
39933a957b | ||
|
|
6308207548 | ||
|
|
2de89ecc96 | ||
|
|
85d7ccf0f2 | ||
|
|
d14363b8f0 | ||
|
|
a298e6845a | ||
|
|
d8985183f7 | ||
|
|
ad34bc8ec3 | ||
|
|
0bc37343a5 | ||
|
|
73f749b5bb | ||
|
|
28882010a9 | ||
|
|
b0afa98387 | ||
|
|
6ba1a18de1 | ||
|
|
c79037a1e1 | ||
|
|
743b33664f | ||
|
|
fd43a89bcd | ||
|
|
9bd376464a | ||
|
|
e70c294d9b | ||
|
|
a456211438 | ||
|
|
1c4d94c5ce | ||
|
|
50546799dc | ||
|
|
9bc837249e | ||
|
|
2215d68544 | ||
|
|
fc6130f4cc | ||
|
|
cf1983d264 | ||
|
|
ed21f4bbfc | ||
|
|
d99787b787 | ||
|
|
5f2da242c5 | ||
|
|
9b724d5a5f | ||
|
|
2fa3e9d4ab | ||
|
|
5e4ca45443 | ||
|
|
090917ea09 | ||
|
|
7e3523ec42 | ||
|
|
12a2cc1b8e | ||
|
|
6b0972d76e | ||
|
|
984cccc25b | ||
|
|
dbad1ba8a1 | ||
|
|
b70e0e0bc2 | ||
|
|
595a140428 | ||
|
|
b94105bf78 | ||
|
|
eec544d1b8 | ||
|
|
cb6e21715a | ||
|
|
026d4a57fb | ||
|
|
7745accfae | ||
|
|
cea3137b5a | ||
|
|
c73fede935 | ||
|
|
b56947dcba | ||
|
|
a23997db39 | ||
|
|
51ba17d43f | ||
|
|
575b0285fe | ||
|
|
277a27516c | ||
|
|
817ff958b9 | ||
|
|
ea2cd6e58e | ||
|
|
1f2f8b574e | ||
|
|
cd6413e7a6 | ||
|
|
425c26cfbb | ||
|
|
f2ab0d8b9f | ||
|
|
e0a6ddd89f | ||
|
|
c02f371350 | ||
|
|
c2a6d33ff2 | ||
|
|
8aeb488e2e | ||
|
|
45cae14f6a | ||
|
|
892d9f5c5a | ||
|
|
65e60c2dec | ||
|
|
6aac577365 | ||
|
|
85a93065a4 | ||
|
|
319cdbce18 | ||
|
|
c800e99dc8 | ||
|
|
2da873bbb9 | ||
|
|
6abac9b5a1 | ||
|
|
ed1f96fdbc | ||
|
|
37a290ce1f | ||
|
|
a3bf403ac0 | ||
|
|
dde9106a5d | ||
|
|
334d0e8096 | ||
|
|
abada7b89b | ||
|
|
69893640a3 | ||
|
|
9f7f5ab0db | ||
|
|
6828438780 | ||
|
|
05fb3d3b0a | ||
|
|
66c583f866 | ||
|
|
2363535fe0 | ||
|
|
9d0037b83d | ||
|
|
f8f4380038 | ||
|
|
ead34f0309 | ||
|
|
1d51ef0259 | ||
|
|
b888524cc3 | ||
|
|
5ec9827f92 | ||
|
|
7ce34b0500 | ||
|
|
516ac4e34e | ||
|
|
36e9909060 | ||
|
|
403392a72e | ||
|
|
2c584bbff9 | ||
|
|
b6e4a5265a | ||
|
|
b473d62cdc | ||
|
|
3e5bff90d1 | ||
|
|
d5b1dbd5ee | ||
|
|
29eb910881 | ||
|
|
bcc7629b1e | ||
|
|
f400104bbc | ||
|
|
a3d0fad984 | ||
|
|
075fbc1cb5 | ||
|
|
c9d82cc88e | ||
|
|
2342dfbe4a | ||
|
|
dbc8fdf35d | ||
|
|
89dd230b8f | ||
|
|
243754c15d | ||
|
|
d17274c6d6 | ||
|
|
9b2b9dccea | ||
|
|
83106327fa | ||
|
|
87114cecea | ||
|
|
f3c68a8bba | ||
|
|
f083fb1c00 | ||
|
|
88d049d68a | ||
|
|
7ee7edffec | ||
|
|
e486a7e69d | ||
|
|
2443fbca9f | ||
|
|
716c1c58df | ||
|
|
7ea260031e | ||
|
|
02c256ffa2 | ||
|
|
02f02b1ad1 | ||
|
|
f4902d5717 | ||
|
|
8bb60fdba7 | ||
|
|
1050ee1c75 | ||
|
|
ec816bddcf | ||
|
|
9468911451 | ||
|
|
61ce363a8a | ||
|
|
563e5f0eff | ||
|
|
ec5aeb9a05 | ||
|
|
9a4eb3c608 | ||
|
|
dd133c7368 | ||
|
|
a7267c061a | ||
|
|
a94ebeca29 | ||
|
|
1b1d2f6c9e | ||
|
|
83cdc8abe3 | ||
|
|
2653b16939 | ||
|
|
ee19014644 | ||
|
|
a479538743 | ||
|
|
2ac4887f01 | ||
|
|
8a7a4c7a64 | ||
|
|
c62165cd92 | ||
|
|
5305aa7fe3 | ||
|
|
e29a32f24b | ||
|
|
50bdefcb4a | ||
|
|
02d12b4209 | ||
|
|
6f9e20095a | ||
|
|
e0476924c1 | ||
|
|
2787bb9b79 | ||
|
|
d92ae455db | ||
|
|
f9a5119d1f | ||
|
|
e162d9564b | ||
|
|
6889d5d851 | ||
|
|
87d22b1e16 | ||
|
|
30522df662 | ||
|
|
ed4fc1852a | ||
|
|
d6494d07d5 | ||
|
|
2c8e57ecff | ||
|
|
d3a6256c8c | ||
|
|
656c9cf66f | ||
|
|
db19019202 | ||
|
|
5712b041fe | ||
|
|
5925bc58b1 | ||
|
|
2e29790be7 | ||
|
|
0545a802de | ||
|
|
a3efa18a61 | ||
|
|
605d78a2b7 | ||
|
|
e7bfa36cab | ||
|
|
dce1b8698a | ||
|
|
e4c753cefd | ||
|
|
9e49e60aa8 | ||
|
|
4c44890d3c | ||
|
|
ef22bd65ea | ||
|
|
1ba263fb13 | ||
|
|
0cf3976fc7 | ||
|
|
eeffe225ec | ||
|
|
de1a975586 | ||
|
|
13d161d119 | ||
|
|
793d12693b | ||
|
|
5888bc9068 | ||
|
|
b6fe1775bd | ||
|
|
90d2467fde | ||
|
|
3c91a83177 | ||
|
|
c7ee9dc18c | ||
|
|
62abfd56e9 | ||
|
|
b818065b9b | ||
|
|
c9b88f2cea | ||
|
|
f0cfbfccc4 | ||
|
|
a3f866f69c | ||
|
|
16e08d623d | ||
|
|
077b367299 | ||
|
|
d1f7a6b730 | ||
|
|
f26c78a5e9 | ||
|
|
0fc18c1da1 | ||
|
|
ddded51f9e | ||
|
|
7b89df6153 | ||
|
|
161dee1667 | ||
|
|
e98afa80d9 | ||
|
|
38ae2d3d1d | ||
|
|
79c71563d5 | ||
|
|
fa12be880a | ||
|
|
dcdb512995 | ||
|
|
7fb4e8b577 | ||
|
|
7f1c66cbe6 | ||
|
|
c90e8c899a | ||
|
|
ab6480ebca | ||
|
|
8613fbdad7 | ||
|
|
b66f3d2ef4 | ||
|
|
f1753eeea7 | ||
|
|
bfed937950 | ||
|
|
4730943931 | ||
|
|
afc0994c38 | ||
|
|
831c94f898 | ||
|
|
0740378ad8 | ||
|
|
f56b25d06f | ||
|
|
eadfead77c | ||
|
|
17d4ce5125 | ||
|
|
253b3862da | ||
|
|
043541f936 | ||
|
|
45da38b64a | ||
|
|
c57472d86e | ||
|
|
1e27223dfa | ||
|
|
0f896c1729 | ||
|
|
66073737a4 | ||
|
|
bf89562d51 | ||
|
|
2adefc4b79 | ||
|
|
f03c078606 | ||
|
|
d6ae79cf81 | ||
|
|
0814cf4af1 | ||
|
|
74789b802f | ||
|
|
5f6581d126 | ||
|
|
8c60c3c8e4 | ||
|
|
cf005fa60c | ||
|
|
9b7d043682 | ||
|
|
161b979e84 | ||
|
|
11798a39d6 | ||
|
|
07abc76a7a | ||
|
|
e1639a9d13 | ||
|
|
71f5ce8c7e | ||
|
|
518a3a53f9 | ||
|
|
2b733a52ca | ||
|
|
3bfb39966f | ||
|
|
f9ae85b9ad | ||
|
|
5fdb33c192 | ||
|
|
51a13f286f | ||
|
|
290967a7c5 | ||
|
|
64938b9cb0 | ||
|
|
590eccf8d9 | ||
|
|
a4c0f2e6fd | ||
|
|
bcd3e3325c | ||
|
|
c1353c056a | ||
|
|
9602471a9b | ||
|
|
f5878f43ff | ||
|
|
edcac1f2b9 | ||
|
|
e25a391fde | ||
|
|
978d3f6f7a | ||
|
|
c5171714b2 | ||
|
|
96c2b9a649 | ||
|
|
487e506a01 | ||
|
|
ec745f5cc9 | ||
|
|
17889fd30b | ||
|
|
23c9e286b1 | ||
|
|
a2c79aa6db | ||
|
|
f716ebd338 | ||
|
|
9b0122e34c | ||
|
|
b9a54b8ffb | ||
|
|
b5916acf79 | ||
|
|
4701a71986 | ||
|
|
fc2c18b489 | ||
|
|
0cc498d830 | ||
|
|
28bc8efac8 | ||
|
|
aafecfa704 | ||
|
|
3378908c93 | ||
|
|
e4135a7bbd | ||
|
|
1efcd09f0c | ||
|
|
6269431467 | ||
|
|
792f167a71 | ||
|
|
897fdffcf9 | ||
|
|
53e1fbae56 | ||
|
|
16ce958964 | ||
|
|
db5d6cb980 | ||
|
|
06b5ab26c8 | ||
|
|
02797ff092 | ||
|
|
3eb0a9afa9 | ||
|
|
5852ad9a1e | ||
|
|
8ee0602f7a | ||
|
|
f8c325e747 | ||
|
|
893ae6867b | ||
|
|
3efb83ecfd | ||
|
|
f30c4895fa | ||
|
|
0fd58567fe | ||
|
|
7a4edb6854 | ||
|
|
c7f232dd98 | ||
|
|
4489314b47 | ||
|
|
08c3875a26 | ||
|
|
39987b31c7 | ||
|
|
43b23b2636 | ||
|
|
2d393630d8 | ||
|
|
5105c0c7f7 | ||
|
|
6f5a26b0cf | ||
|
|
d766a3635a | ||
|
|
b4175f4a89 | ||
|
|
f0917064af | ||
|
|
1751170940 | ||
|
|
3e85dc9bd5 | ||
|
|
54301fed44 | ||
|
|
efcab6f38d | ||
|
|
9f32f206b3 | ||
|
|
6a22fb9956 | ||
|
|
a29a314976 | ||
|
|
226922d3e4 | ||
|
|
84403d07f6 |
20
.gitignore
vendored
20
.gitignore
vendored
@@ -4,3 +4,23 @@ build/
|
||||
aqhome.db
|
||||
aqhome.log
|
||||
aqhomed.vg
|
||||
aqhome-mqttlog.vg
|
||||
aqhome-storage.vg
|
||||
aqhome-storage.pid
|
||||
aqhome-data.vg
|
||||
aqhome-data.pid
|
||||
aqhome-nodes.db
|
||||
aqhome-nodes.log
|
||||
aqhome-nodes.pid
|
||||
aqhome-nodes.vg
|
||||
aqhome-tool.vg
|
||||
aqhome-mqtt.pid
|
||||
aqhome-mqtt.devices
|
||||
aqhome-react.pid
|
||||
|
||||
core.*
|
||||
core
|
||||
aqhome.geany
|
||||
aqhome-nodes.db
|
||||
aqhome-nodes.db.*
|
||||
|
||||
|
||||
78
0BUILD
78
0BUILD
@@ -2,7 +2,7 @@
|
||||
|
||||
<gwbuild>
|
||||
|
||||
<project name="aqhome" version="0.0.1" so_current="0" so_age="0" so_revision="0" write_config_h="TRUE">
|
||||
<project name="aqhome" version="0.0.4" so_current="0" so_age="0" so_revision="4" write_config_h="TRUE">
|
||||
<setVar name="package">$(project_name)</setVar>
|
||||
<setVar name="version">
|
||||
$(project_vmajor).$(project_vminor).$(project_vpatchlevel)
|
||||
@@ -44,23 +44,81 @@
|
||||
<setVar name="includedir">$(option_prefix)/include</setVar>
|
||||
<setVar name="datarootdir">$(option_prefix)/share</setVar>
|
||||
<setVar name="datadir">$(option_prefix)/share</setVar>
|
||||
<setVar name="rtdatadir">$(option_prefix)/var/lib</setVar>
|
||||
<setVar name="localedir">$(option_prefix)/share/locale</setVar>
|
||||
|
||||
<setVar name="pkglibdir">$(libdir)/$(package)</setVar>
|
||||
<setVar name="pkgincludedir">$(includedir)/aqhome/$(package)</setVar>
|
||||
<setVar name="pkgdatadir">$(datadir)/$(package)</setVar>
|
||||
|
||||
<option id="enable_testcode" type="string">
|
||||
<default>TRUE</default>
|
||||
<choices>TRUE FALSE</choices>
|
||||
</option>
|
||||
<ifVarMatches name="option_enable_testcode" value="TRUE" >
|
||||
<define name="AQHOME_ENABLE_TESTCODE" value="1" />
|
||||
</ifVarMatches>
|
||||
|
||||
|
||||
<option id="local_install" type="string">
|
||||
<default>FALSE</default>
|
||||
<choices>TRUE FALSE</choices>
|
||||
</option>
|
||||
<ifVarMatches name="option_local_install" value="TRUE" >
|
||||
<define name="ENABLE_LOCAL_INSTALL" value="1" />
|
||||
</ifVarMatches>
|
||||
|
||||
|
||||
<option id="debug" type="string">
|
||||
<default>TRUE</default>
|
||||
<choices>TRUE FALSE</choices>
|
||||
</option>
|
||||
<ifVarMatches name="option_debug" value="TRUE" >
|
||||
<setVar name="CFLAGS">-ggdb -Wall -O0</setVar>
|
||||
<setVar name="CXXFLAGS">-ggdb -Wall -O0</setVar>
|
||||
<setVar name="CFLAGS">
|
||||
-ggdb -Wall -Og -W
|
||||
-Wbad-function-cast -Wmissing-parameter-type
|
||||
-Wnested-externs -Wstringop-overflow=2
|
||||
-Wdeclaration-after-statement -Wpointer-sign
|
||||
-Wunused-parameter
|
||||
</setVar>
|
||||
<setVar name="CXXFLAGS">-ggdb -Wall -Og</setVar>
|
||||
</ifVarMatches>
|
||||
|
||||
|
||||
|
||||
<!-- paths -->
|
||||
<ifVarMatches name="GWBUILD_SYSTEM" value="windows" > <!-- long version of IF statement with THEN and ELSE -->
|
||||
<then>
|
||||
<define name="OS_WIN32" value="1" />
|
||||
<setVar name="aqhome_sys_is_windows">1</setVar>
|
||||
<setVar name="aqhome_cfg_searchdir">etc</setVar>
|
||||
<setVar name="aqhome_locale_searchdir">share/locale</setVar>
|
||||
<setVar name="aqhome_data_searchdir">share"</setVar>
|
||||
<setVar name="aqhome_rtdata_searchdir">var/lib</setVar>
|
||||
</then>
|
||||
<else>
|
||||
<define name="OS_POSIX" value="1" />
|
||||
<setVar name="aqhome_sys_is_windows">0</setVar>
|
||||
<ifVarMatches name="option_local_install" value="TRUE" >
|
||||
<then>
|
||||
<setVar name="aqhome_cfg_searchdir">etc</setVar>
|
||||
<setVar name="aqhome_locale_searchdir">share/locale</setVar>
|
||||
<setVar name="aqhome_data_searchdir">share</setVar>
|
||||
<setVar name="aqhome_rtdata_searchdir">var/lib</setVar>
|
||||
</then>
|
||||
<else>
|
||||
<setVar name="aqhome_cfg_searchdir">$(sysconfdir)</setVar>
|
||||
<setVar name="aqhome_locale_searchdir">$(datadir)/locale</setVar>
|
||||
<setVar name="aqhome_data_searchdir">$(datadir)</setVar>
|
||||
<setVar name="aqhome_rtdata_searchdir">$(rtdatadir)</setVar>
|
||||
</else>
|
||||
</ifVarMatches>
|
||||
</else>
|
||||
</ifVarMatches>
|
||||
<define name="AQHOME_SYS_IS_WINDOWS" value="$(aqhome_sys_is_windows)" />
|
||||
|
||||
|
||||
|
||||
<!-- use "-Owith_avr=TRUE" or "FALSE" to enable or disable assembling AVR code -->
|
||||
<option id="with_avr" type="string">
|
||||
<default>TRUE</default>
|
||||
@@ -88,9 +146,13 @@
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dep id="gwenhywfar" name="gwenhywfar" minversion="5.8.2.1" required="TRUE" >
|
||||
<dep id="gwenhywfar" name="gwenhywfar" minversion="5.11.2" required="TRUE" >
|
||||
<variables>plugindir gwengui-fox16</variables>
|
||||
</dep>
|
||||
<!-- <dep id="aqdatabase" name="aqdatabase" minversion="1.99.1" required="TRUE" >
|
||||
<variables>aqdatabase_typemakerdir</variables>
|
||||
</dep>
|
||||
-->
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -110,6 +172,9 @@
|
||||
<subdirs>
|
||||
aqhome
|
||||
apps
|
||||
devices
|
||||
etc
|
||||
scripts
|
||||
</subdirs>
|
||||
|
||||
<ifVarMatches name="option_with_avr" value="TRUE" >
|
||||
@@ -131,7 +196,10 @@
|
||||
|
||||
<output>
|
||||
</output>
|
||||
|
||||
<!-- see http://eleccelerator.com/fusecalc/fusecalc.php?chip=attiny84 -->
|
||||
<!-- disable divide by 8: -U lfuse:w:0xEA:m default (divide by 8): -U lfuse:w:0x6A:m -->
|
||||
<!-- changes to hfuse: enable EESAVE -->
|
||||
<!-- changes to efuse: enable SELFPRGEN -->
|
||||
<cmd tool="$(avrdude)" checkDates="FALSE" >
|
||||
-p t84 -c stk500 -P /dev/ttyACM0 -B16 -U hfuse:w:0xD7:m -U efuse:w:0xFE:m -U flash:w:avr/att84_temp1.hex
|
||||
</cmd>
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
<gwbuild>
|
||||
|
||||
<subdirs>
|
||||
aqhomed
|
||||
aqhome-tool
|
||||
aqhome-mqttlog
|
||||
aqhome-data
|
||||
aqhome-nodes
|
||||
aqhome-react
|
||||
</subdirs>
|
||||
|
||||
</gwbuild>
|
||||
|
||||
1
apps/aqhome-data/.gitignore
vendored
Normal file
1
apps/aqhome-data/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
test/
|
||||
88
apps/aqhome-data/0BUILD
Normal file
88
apps/aqhome-data/0BUILD
Normal file
@@ -0,0 +1,88 @@
|
||||
<?xml?>
|
||||
|
||||
<gwbuild>
|
||||
|
||||
<target type="Program" name="aqhome-data" install="$(sbindir)" >
|
||||
|
||||
<includes type="c" >
|
||||
$(gwenhywfar_cflags)
|
||||
-I$(topsrcdir)
|
||||
-I$(topbuilddir)
|
||||
-I$(builddir)
|
||||
-I$(srcdir)
|
||||
</includes>
|
||||
|
||||
<includes type="tm2" >
|
||||
--include=$(builddir)
|
||||
--include=$(srcdir)
|
||||
</includes>
|
||||
|
||||
<setVar name="local/cflags">$(visibility_cflags)</setVar>
|
||||
|
||||
<setVar name="tm2flags" >
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/typefiles" >
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_sources" >
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_headers_pub">
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_headers_priv" >
|
||||
</setVar>
|
||||
|
||||
<headers dist="true" >
|
||||
server.h
|
||||
server_p.h
|
||||
s_connect.h
|
||||
s_getdevices.h
|
||||
s_getvalues.h
|
||||
s_addvalue.h
|
||||
s_annvalue.h
|
||||
s_updatedata.h
|
||||
s_setdata.h
|
||||
s_getdatapoints.h
|
||||
s_moddevice.h
|
||||
</headers>
|
||||
|
||||
<sources>
|
||||
$(local/typefiles)
|
||||
|
||||
server.c
|
||||
s_connect.c
|
||||
s_getdevices.c
|
||||
s_getvalues.c
|
||||
s_addvalue.c
|
||||
s_annvalue.c
|
||||
s_updatedata.c
|
||||
s_setdata.c
|
||||
s_getdatapoints.c
|
||||
s_moddevice.c
|
||||
main.c
|
||||
</sources>
|
||||
|
||||
<useTargets>
|
||||
aqhome
|
||||
</useTargets>
|
||||
|
||||
<libraries>
|
||||
$(gwenhywfar_libs)
|
||||
</libraries>
|
||||
|
||||
<subdirs>
|
||||
</subdirs>
|
||||
|
||||
|
||||
<extradist>
|
||||
</extradist>
|
||||
|
||||
|
||||
</target>
|
||||
|
||||
|
||||
</gwbuild>
|
||||
|
||||
|
||||
267
apps/aqhome-data/main.c
Normal file
267
apps/aqhome-data/main.c
Normal file
@@ -0,0 +1,267 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <aqhome/api.h>
|
||||
#include <aqhome/aqhome.h>
|
||||
|
||||
#include "./server.h"
|
||||
|
||||
#include <gwenhywfar/gwenhywfar.h>
|
||||
#include <gwenhywfar/logger.h>
|
||||
#include <gwenhywfar/cgui.h>
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
# include <signal.h>
|
||||
#endif
|
||||
|
||||
|
||||
#define DISABLE_DEBUGLOG
|
||||
|
||||
|
||||
//#define WRITE_INTERVAL_IN_SECS (5*60)
|
||||
|
||||
#define WRITE_INTERVAL_IN_SECS 60
|
||||
#define PING_INTERVAL 120
|
||||
#define CONNCLEAN_INTERVAL_IN_SECS 10
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* defines
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
static int _setSignalHandlers(void);
|
||||
static int _setupSigAction(struct sigaction *sa, int sig);
|
||||
static void _signalHandler(int s);
|
||||
#endif
|
||||
|
||||
static void _runService(AQH_OBJECT *aqh, AQH_EVENT_LOOP *eventLoop);
|
||||
static void _writeCurrentState(AQH_OBJECT *aqh);
|
||||
static int _diffInSeconds(time_t t1, time_t t0);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* static vars
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
static struct sigaction saINT,saTERM, saHUP, saTSTP, saCONT, saPIPE;
|
||||
#endif
|
||||
|
||||
static int stopService=0;
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* implementations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int rv;
|
||||
AQH_EVENT_LOOP *eventLoop;
|
||||
AQH_OBJECT *aqh;
|
||||
GWEN_GUI *gui;
|
||||
|
||||
rv=GWEN_Init();
|
||||
if (rv) {
|
||||
fprintf(stderr, "ERROR: Unable to init Gwen.\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
GWEN_Logger_Open(0, "aqhome-data", 0, GWEN_LoggerType_Console, GWEN_LoggerFacility_User);
|
||||
GWEN_Logger_SetLevel(0, GWEN_LoggerLevel_Warning);
|
||||
|
||||
rv=_setSignalHandlers();
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "here (%d)", rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv=AQH_Init();
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "here (%d)", rv);
|
||||
return 2;
|
||||
}
|
||||
|
||||
gui=GWEN_Gui_CGui_new();
|
||||
GWEN_Gui_SetGui(gui);
|
||||
|
||||
eventLoop=AQH_EventLoop_new();
|
||||
aqh=AqHomeDataServer_new(eventLoop);
|
||||
rv=AqHomeDataServer_Init(aqh, argc, argv);
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "here (%d)", rv);
|
||||
return 2;
|
||||
}
|
||||
|
||||
_runService(aqh, eventLoop);
|
||||
|
||||
//AqHomeData_Fini(aqh);
|
||||
AQH_Object_free(aqh);
|
||||
|
||||
GWEN_Gui_SetGui(NULL);
|
||||
GWEN_Gui_free(gui);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _runService(AQH_OBJECT *aqh, AQH_EVENT_LOOP *eventLoop)
|
||||
{
|
||||
time_t timeStart;
|
||||
time_t timeLastWrite;
|
||||
time_t timeLastConnectionCleanup;
|
||||
int timeout;
|
||||
|
||||
timeout=AqHomeDataServer_GetTimeout(aqh);
|
||||
timeStart=time(NULL);
|
||||
timeLastWrite=time(NULL);
|
||||
timeLastConnectionCleanup=time(NULL);
|
||||
|
||||
while(!stopService) {
|
||||
time_t now;
|
||||
|
||||
DBG_INFO(NULL, "Next loop (%d clients)", AqHomeDataServer_GetClientNum(aqh));
|
||||
AQH_EventLoop_Run(eventLoop, 2000);
|
||||
AqHomeDataServer_HandleClientMsgs(aqh);
|
||||
|
||||
now=time(NULL);
|
||||
|
||||
if (_diffInSeconds(now, timeLastConnectionCleanup)>CONNCLEAN_INTERVAL_IN_SECS) {
|
||||
DBG_INFO(NULL, "Cleanup connections");
|
||||
AqHomeDataServer_CleanupClients(aqh);
|
||||
timeLastConnectionCleanup=now;
|
||||
}
|
||||
|
||||
if (_diffInSeconds(now, timeLastWrite)>WRITE_INTERVAL_IN_SECS) {
|
||||
DBG_INFO(NULL, "Write time");
|
||||
_writeCurrentState(aqh);
|
||||
timeLastWrite=now;
|
||||
}
|
||||
|
||||
if (timeout && (_diffInSeconds(now, timeStart)>timeout)) {
|
||||
DBG_ERROR(NULL, "Timeout");
|
||||
_writeCurrentState(aqh);
|
||||
break;
|
||||
}
|
||||
} /* while */
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _diffInSeconds(time_t t1, time_t t0)
|
||||
{
|
||||
return t1-t0;
|
||||
}
|
||||
|
||||
|
||||
void _writeCurrentState(AQH_OBJECT *aqh)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv=AqHomeDataServer_WriteStorageIfChanged(aqh);
|
||||
if (rv<0) {
|
||||
DBG_ERROR(NULL, "ATTENTION: Could not write storage statefile (%d)", rv);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int _setSignalHandlers(void)
|
||||
{
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
int rv;
|
||||
|
||||
rv=_setupSigAction(&saINT, SIGINT);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
rv=_setupSigAction(&saTERM, SIGTERM);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
rv=_setupSigAction(&saHUP, SIGHUP);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
rv=_setupSigAction(&saPIPE, SIGPIPE);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
# ifdef SIGTSTP
|
||||
rv=_setupSigAction(&saTSTP, SIGTSTP);
|
||||
if (rv)
|
||||
return rv;
|
||||
# endif
|
||||
|
||||
# ifdef SIGCONT
|
||||
rv=_setupSigAction(&saCONT, SIGCONT);
|
||||
if (rv)
|
||||
return rv;
|
||||
# endif
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _setupSigAction(struct sigaction *sa, int sig)
|
||||
{
|
||||
sa->sa_handler=_signalHandler;
|
||||
sigemptyset(&sa->sa_mask);
|
||||
sa->sa_flags=0;
|
||||
if (sigaction(sig, sa, 0)) {
|
||||
DBG_ERROR(NULL, "Could not setup signal handler for signal %d", sig);
|
||||
return GWEN_ERROR_IO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _signalHandler(int s)
|
||||
{
|
||||
switch(s) {
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
case SIGHUP:
|
||||
DBG_WARN(0, "Received signal %d, stopping service in next loop.",s);
|
||||
stopService=1;
|
||||
break;
|
||||
case SIGPIPE:
|
||||
DBG_WARN(0, "Received PIPE signal");
|
||||
break;
|
||||
default:
|
||||
DBG_WARN(0, "Unknown signal %d",s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
67
apps/aqhome-data/s_addvalue.c
Normal file
67
apps/aqhome-data/s_addvalue.c
Normal file
@@ -0,0 +1,67 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "./s_addvalue.h"
|
||||
#include "./server_p.h"
|
||||
#include "aqhome/ipc2/endpoint.h"
|
||||
#include "aqhome/msg/ipc/m_ipc.h"
|
||||
#include "aqhome/msg/ipc/data/m_ipcd.h"
|
||||
#include "aqhome/msg/ipc/data/m_ipcd_values.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_result.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_tag16.h"
|
||||
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* code
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void AqHomeDataServer_HandleAddValue(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList)
|
||||
{
|
||||
if (tagList) {
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
xo=AqHomeDataServer_GetServerData(o);
|
||||
if (xo) {
|
||||
AQH_MESSAGE *outMsg;
|
||||
int resultCode=AQH_MSGDATA_RESULT_SUCCESS;
|
||||
AQH_VALUE *recvdValue;
|
||||
|
||||
recvdValue=AQH_IpcdMessageValues_ReadFirstValue(tagList);
|
||||
if (recvdValue) {
|
||||
AQH_VALUE *value;
|
||||
|
||||
value=AqHomeDataServer_GetOrCreateValueForDriverWithTemplate(o, ep, recvdValue);
|
||||
if (value==NULL)
|
||||
resultCode=AQH_MSGDATA_RESULT_ERROR_PERMS;
|
||||
AQH_Value_free(recvdValue);
|
||||
}
|
||||
else
|
||||
resultCode=AQH_MSGDATA_RESULT_ERROR_BADDATA;
|
||||
|
||||
outMsg=AQH_IpcMessageResult_new(AQH_IPC_PROTOCOL_DATA_ID,
|
||||
AQH_IPC_PROTOCOL_DATA_VERSION,
|
||||
AQH_MSGTYPE_IPC_DATA_RESULT,
|
||||
AQH_Endpoint_GetNextMessageId(ep),
|
||||
AQH_IpcMessage_GetMsgId(msg),
|
||||
resultCode, NULL);
|
||||
AQH_Endpoint_AddMsgOut(ep, outMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
27
apps/aqhome-data/s_addvalue.h
Normal file
27
apps/aqhome-data/s_addvalue.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_DATA_S_ADDVALUE_H
|
||||
#define AQHOME_DATA_S_ADDVALUE_H
|
||||
|
||||
|
||||
#include "./server.h"
|
||||
|
||||
#include <gwenhywfar/tag16.h>
|
||||
|
||||
|
||||
void AqHomeDataServer_HandleAddValue(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
50
apps/aqhome-data/s_annvalue.c
Normal file
50
apps/aqhome-data/s_annvalue.c
Normal file
@@ -0,0 +1,50 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "./s_annvalue.h"
|
||||
#include "./server_p.h"
|
||||
#include "aqhome/ipc2/endpoint.h"
|
||||
#include "aqhome/msg/ipc/m_ipc.h"
|
||||
#include "aqhome/msg/ipc/data/m_ipcd.h"
|
||||
#include "aqhome/msg/ipc/data/m_ipcd_values.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_result.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_tag16.h"
|
||||
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* code
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void AqHomeDataServer_HandleAnnounceValue(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList)
|
||||
{
|
||||
if (tagList) {
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
xo=AqHomeDataServer_GetServerData(o);
|
||||
if (xo) {
|
||||
AQH_VALUE *recvdValue;
|
||||
|
||||
recvdValue=AQH_IpcdMessageValues_ReadFirstValue(tagList);
|
||||
if (recvdValue) {
|
||||
AqHomeDataServer_GetOrCreateValueForDriverWithTemplate(o, ep, recvdValue);
|
||||
AQH_Value_free(recvdValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
27
apps/aqhome-data/s_annvalue.h
Normal file
27
apps/aqhome-data/s_annvalue.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_DATA_S_ANNVALUE_H
|
||||
#define AQHOME_DATA_S_ANNVALUE_H
|
||||
|
||||
|
||||
#include "./server.h"
|
||||
|
||||
#include <gwenhywfar/tag16.h>
|
||||
|
||||
|
||||
void AqHomeDataServer_HandleAnnounceValue(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
82
apps/aqhome-data/s_connect.c
Normal file
82
apps/aqhome-data/s_connect.c
Normal file
@@ -0,0 +1,82 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "./s_connect.h"
|
||||
#include "./server_p.h"
|
||||
#include "aqhome/ipc2/endpoint.h"
|
||||
#include "aqhome/msg/ipc/m_ipc.h"
|
||||
#include "aqhome/msg/ipc/data/m_ipcd.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_connect.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_result.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_tag16.h"
|
||||
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* code
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void AqHomeDataServer_HandleConnect(GWEN_UNUSED AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList)
|
||||
{
|
||||
if (tagList) {
|
||||
AQH_MESSAGE *outMsg;
|
||||
int resultCode=AQH_MSGDATA_RESULT_SUCCESS;
|
||||
char *clientId=NULL;
|
||||
char *userId=NULL;
|
||||
char *passw=NULL;
|
||||
uint32_t flags;
|
||||
|
||||
clientId=AQH_Tag16_GetTagDataAsNewString(tagList, AQH_MSG_CONNECT_TAGS_CLIENTID, NULL);
|
||||
userId=AQH_Tag16_GetTagDataAsNewString(tagList, AQH_MSG_CONNECT_TAGS_USERID, NULL);
|
||||
flags=AQH_Tag16_GetTagDataAsUint32(tagList, AQH_MSG_CONNECT_TAGS_FLAGS, 0);
|
||||
passw=AQH_Tag16_GetTagDataAsNewString(tagList, AQH_MSG_CONNECT_TAGS_PASSWORD, NULL);
|
||||
|
||||
if (clientId)
|
||||
AQH_Endpoint_SetServiceName(ep, clientId);
|
||||
if (userId)
|
||||
AQH_Endpoint_SetUserName(ep, userId);
|
||||
|
||||
if (flags & AQH_MSG_CONNECT_FLAGS_WANTUPDATES)
|
||||
AQH_Endpoint_AddFlags(ep, AQH_ENDPOINT_FLAGS_WANTUPDATES);
|
||||
|
||||
/* TODO: add user management, for now we allow all */
|
||||
AQH_Endpoint_SetPermissions(ep,
|
||||
AQH_ENDPOINT_PERMS_LISTVALUES |
|
||||
AQH_ENDPOINT_PERMS_READVALUE |
|
||||
AQH_ENDPOINT_PERMS_ADDVALUE |
|
||||
AQH_ENDPOINT_PERMS_LISTDATA |
|
||||
AQH_ENDPOINT_PERMS_READDATA |
|
||||
AQH_ENDPOINT_PERMS_ADDDATA |
|
||||
AQH_ENDPOINT_PERMS_LISTDEVICES |
|
||||
AQH_ENDPOINT_PERMS_READDEVICE |
|
||||
AQH_ENDPOINT_PERMS_ADDDEVICE |
|
||||
AQH_ENDPOINT_PERMS_MODDEVICE);
|
||||
free(passw);
|
||||
free(userId);
|
||||
free(clientId);
|
||||
|
||||
outMsg=AQH_IpcMessageResult_new(AQH_IPC_PROTOCOL_DATA_ID,
|
||||
AQH_IPC_PROTOCOL_DATA_VERSION,
|
||||
AQH_MSGTYPE_IPC_DATA_RESULT,
|
||||
AQH_Endpoint_GetNextMessageId(ep),
|
||||
AQH_IpcMessage_GetMsgId(msg),
|
||||
resultCode, NULL);
|
||||
AQH_Endpoint_AddMsgOut(ep, outMsg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
27
apps/aqhome-data/s_connect.h
Normal file
27
apps/aqhome-data/s_connect.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_DATA_S_CONNECT_H
|
||||
#define AQHOME_DATA_S_CONNECT_H
|
||||
|
||||
|
||||
#include "./server.h"
|
||||
|
||||
#include <gwenhywfar/tag16.h>
|
||||
|
||||
|
||||
void AqHomeDataServer_HandleConnect(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
228
apps/aqhome-data/s_getdatapoints.c
Normal file
228
apps/aqhome-data/s_getdatapoints.c
Normal file
@@ -0,0 +1,228 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "./s_getdatapoints.h"
|
||||
|
||||
#include "./server_p.h"
|
||||
#include "aqhome/aqhome.h"
|
||||
#include "aqhome/ipc2/endpoint.h"
|
||||
#include "aqhome/msg/ipc/m_ipc.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_result.h"
|
||||
#include "aqhome/msg/ipc/data/m_ipcd.h"
|
||||
#include "aqhome/msg/ipc/data/m_ipcd_getdata.h"
|
||||
#include "aqhome/msg/ipc/data/m_ipcd_multidata.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_result.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_tag16.h"
|
||||
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* defines
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define AQHOMEDATA_HANDLEGETDATAPOINTS_MAXTABLEENTRIES 2048
|
||||
#define AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS 1024
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static int _getAndSendDataPoints(AQH_STORAGE *storage, AQH_OBJECT *ep,
|
||||
const AQH_VALUE *value,
|
||||
uint64_t tsBegin, uint64_t tsEnd, uint64_t num, uint32_t refMsgId);
|
||||
static int _getAndSendDataPointsNoNum(AQH_STORAGE *storage, AQH_OBJECT *ep, const AQH_VALUE *value, uint64_t tsBegin, uint64_t tsEnd,
|
||||
uint32_t refMsgId);
|
||||
static int _getAndSendDataPointsWithNum(AQH_STORAGE *storage, AQH_OBJECT *ep, const AQH_VALUE *value, uint64_t num, uint32_t refMsgId);
|
||||
static void _sendDataPointsResponse(AQH_OBJECT *ep, const AQH_VALUE *value, const uint64_t *tablePtr,
|
||||
uint32_t refMsgId);
|
||||
static void _getAndSendLastDatapoint(AQH_STORAGE *storage, AQH_OBJECT *ep, const AQH_VALUE *value, uint32_t refMsgId);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* implementations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
void AqHomeDataServer_HandleGetDataPoints(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *recvdMsg, const GWEN_TAG16_LIST *tagList)
|
||||
{
|
||||
if (tagList) {
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
xo=AqHomeDataServer_GetServerData(o);
|
||||
if (xo) {
|
||||
uint32_t refMsgId;
|
||||
|
||||
refMsgId=AQH_IpcMessage_GetMsgId(recvdMsg);
|
||||
if (AQH_Endpoint_GetPermissions(ep) & AQH_ENDPOINT_PERMS_READDATA) {
|
||||
char *valueName;
|
||||
|
||||
valueName=AQH_Tag16_GetTagDataAsNewString(tagList, AQH_MSGDATA_GETDATA_TAGS_NAME, NULL);
|
||||
if (valueName && *valueName) {
|
||||
AQH_VALUE *value;
|
||||
uint64_t tsBegin;
|
||||
uint64_t tsEnd;
|
||||
uint64_t numRequested;
|
||||
|
||||
tsBegin=AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_BEGIN, 0);
|
||||
tsEnd=AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_END, 0);
|
||||
numRequested=AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_NUM, 0);
|
||||
|
||||
value=AQH_Storage_GetValueByNameForSystem(xo->storage, valueName);
|
||||
if (value) {
|
||||
int resultCode;
|
||||
|
||||
resultCode=_getAndSendDataPoints(xo->storage, ep, value, tsBegin, tsEnd, numRequested, refMsgId);
|
||||
AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, resultCode);
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "Value \"%s\" does not exist", valueName);
|
||||
AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, AQH_MSGDATA_RESULT_ERROR_NOTFOUND);
|
||||
}
|
||||
free(valueName);
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "Missing value name");
|
||||
AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, AQH_MSGDATA_RESULT_ERROR_BADDATA);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(AQH_LOGDOMAIN, "No permissions to read data");
|
||||
AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, AQH_MSGDATA_RESULT_ERROR_PERMS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _getAndSendDataPoints(AQH_STORAGE *storage, AQH_OBJECT *ep,
|
||||
const AQH_VALUE *value,
|
||||
uint64_t tsBegin, uint64_t tsEnd, uint64_t num,
|
||||
uint32_t refMsgId)
|
||||
{
|
||||
if (num==0)
|
||||
return _getAndSendDataPointsNoNum(storage, ep, value, tsBegin, tsEnd, refMsgId);
|
||||
else if (num==1) {
|
||||
_getAndSendLastDatapoint(storage, ep, value, refMsgId);
|
||||
return AQH_MSGDATA_RESULT_SUCCESS;
|
||||
}
|
||||
else
|
||||
return _getAndSendDataPointsWithNum(storage, ep, value, num, refMsgId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _getAndSendDataPointsNoNum(AQH_STORAGE *storage, AQH_OBJECT *ep,
|
||||
const AQH_VALUE *value, uint64_t tsBegin, uint64_t tsEnd,
|
||||
uint32_t refMsgId)
|
||||
{
|
||||
uint64_t valueId;
|
||||
uint64_t *tablePtr;
|
||||
|
||||
valueId=AQH_Value_GetId(value);
|
||||
tablePtr=AQH_Storage_GetDataPoints(storage, valueId, tsBegin, tsEnd, AQHOMEDATA_HANDLEGETDATAPOINTS_MAXTABLEENTRIES);
|
||||
if (tablePtr) {
|
||||
_sendDataPointsResponse(ep, value, tablePtr, refMsgId);
|
||||
free(tablePtr);
|
||||
return AQH_MSGDATA_RESULT_SUCCESS;
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "No matching datapoints for value \"%s\"", AQH_Value_GetNameForSystem(value));
|
||||
return AQH_MSGDATA_RESULT_ERROR_NODATA;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _getAndSendDataPointsWithNum(AQH_STORAGE *storage, AQH_OBJECT *ep,
|
||||
const AQH_VALUE *value, uint64_t num,
|
||||
uint32_t refMsgId)
|
||||
{
|
||||
uint64_t valueId;
|
||||
uint64_t *tablePtr;
|
||||
|
||||
if (num>AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS)
|
||||
num=AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS;
|
||||
valueId=AQH_Value_GetId(value);
|
||||
tablePtr=AQH_Storage_GetLastNDataPoints(storage, valueId, num);
|
||||
if (tablePtr) {
|
||||
_sendDataPointsResponse(ep, value, tablePtr, refMsgId);
|
||||
free(tablePtr);
|
||||
return AQH_MSGDATA_RESULT_SUCCESS;
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "No matching datapoints for value \"%s\"", AQH_Value_GetNameForSystem(value));
|
||||
return AQH_MSGDATA_RESULT_ERROR_NODATA;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _sendDataPointsResponse(AQH_OBJECT *ep,
|
||||
const AQH_VALUE *value, const uint64_t *tablePtr,
|
||||
uint32_t refMsgId)
|
||||
{
|
||||
int numTableEntries;
|
||||
int numDataPoints;
|
||||
AQH_MESSAGE *outMsg;
|
||||
|
||||
numTableEntries=(int)(tablePtr[0]);
|
||||
numDataPoints=numTableEntries/2;
|
||||
outMsg=AQH_IpcdMessageMultiData_new(AQH_MSGTYPE_IPC_DATA_GETDATA_RSP,
|
||||
AQH_Endpoint_GetNextMessageId(ep), refMsgId,
|
||||
value, &(tablePtr[1]), numDataPoints);
|
||||
AQH_Endpoint_AddMsgOut(ep, outMsg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _getAndSendLastDatapoint(AQH_STORAGE *storage, AQH_OBJECT *ep,
|
||||
const AQH_VALUE *value, uint32_t refMsgId)
|
||||
{
|
||||
int rv;
|
||||
uint64_t timestamp=0;
|
||||
double data=0.0;
|
||||
|
||||
rv=AQH_Storage_GetLastDataPoint(storage, AQH_Value_GetId(value), ×tamp, &data);
|
||||
if (rv<0) {
|
||||
int resultCode;
|
||||
|
||||
switch(rv) {
|
||||
case GWEN_ERROR_INVALID: resultCode=AQH_MSGDATA_RESULT_ERROR_INVALID; break;
|
||||
case GWEN_ERROR_NO_DATA: resultCode=AQH_MSGDATA_RESULT_ERROR_NODATA; break;
|
||||
default: resultCode=AQH_MSGDATA_RESULT_ERROR_GENERIC; break;
|
||||
}
|
||||
AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, resultCode);
|
||||
}
|
||||
else {
|
||||
AQH_MESSAGE *outMsg;
|
||||
|
||||
outMsg=AQH_IpcdMessageMultiData_newForOne(AQH_MSGTYPE_IPC_DATA_GETDATA_RSP,
|
||||
AQH_Endpoint_GetNextMessageId(ep), refMsgId,
|
||||
value, timestamp, data);
|
||||
AQH_Endpoint_AddMsgOut(ep, outMsg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
27
apps/aqhome-data/s_getdatapoints.h
Normal file
27
apps/aqhome-data/s_getdatapoints.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_DATA_S_GETDATAPOINTS_H
|
||||
#define AQHOME_DATA_S_GETDATAPOINTS_H
|
||||
|
||||
|
||||
#include "./server.h"
|
||||
|
||||
#include <gwenhywfar/tag16.h>
|
||||
|
||||
|
||||
void AqHomeDataServer_HandleGetDataPoints(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *recvdMsg, const GWEN_TAG16_LIST *tagList);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
118
apps/aqhome-data/s_getdevices.c
Normal file
118
apps/aqhome-data/s_getdevices.c
Normal file
@@ -0,0 +1,118 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "./s_getdevices.h"
|
||||
#include "./server_p.h"
|
||||
#include "aqhome/ipc2/endpoint.h"
|
||||
#include "aqhome/msg/ipc/m_ipc.h"
|
||||
#include "aqhome/msg/ipc/data/m_ipcd.h"
|
||||
#include "aqhome/msg/ipc/data/m_ipcd_devices.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_result.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_tag16.h"
|
||||
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* defines
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define AQHOMEDATA_DEVICESPERMSG 10
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static void _sendDeviceList(AQH_OBJECT *ep, const AQH_DEVICE_LIST *vl, uint32_t flags, uint32_t refMsgId);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* implementations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void AqHomeDataServer_HandleGetDevices(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList)
|
||||
{
|
||||
if (tagList) {
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
xo=AqHomeDataServer_GetServerData(o);
|
||||
if (xo) {
|
||||
const AQH_DEVICE_LIST *origDeviceList;
|
||||
uint32_t refMsgId;
|
||||
|
||||
refMsgId=AQH_IpcMessage_GetMsgId(msg);
|
||||
|
||||
DBG_ERROR(NULL, "HandleGetDevices");
|
||||
origDeviceList=AQH_Storage_GetDeviceList(xo->storage);
|
||||
if (origDeviceList) {
|
||||
DBG_ERROR(NULL, "Have a list of %d devices", AQH_Device_List_GetCount(origDeviceList));
|
||||
if (AQH_Device_List_GetCount(origDeviceList)<AQHOMEDATA_DEVICESPERMSG) {
|
||||
DBG_ERROR(NULL, "Sending all entries in one message");
|
||||
_sendDeviceList(ep, origDeviceList, AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, refMsgId);
|
||||
}
|
||||
else {
|
||||
AQH_DEVICE_LIST *tmpDeviceList;
|
||||
const AQH_DEVICE *v;
|
||||
|
||||
DBG_INFO(NULL, "Sending entries in multiple messages");
|
||||
tmpDeviceList=AQH_Device_List_new();
|
||||
v=AQH_Device_List_First(origDeviceList);
|
||||
while(v) {
|
||||
const AQH_DEVICE *next;
|
||||
AQH_DEVICE *copyOfDevice;
|
||||
|
||||
next=AQH_Device_List_Next(v);
|
||||
copyOfDevice=AQH_Device_dup(v);
|
||||
AQH_Device_List_Add(copyOfDevice, tmpDeviceList);
|
||||
if (AQH_Device_List_GetCount(tmpDeviceList)>=AQHOMEDATA_DEVICESPERMSG) {
|
||||
DBG_ERROR(NULL, "Sending %d devices", AQH_Device_List_GetCount(tmpDeviceList));
|
||||
_sendDeviceList(ep, tmpDeviceList, next?0:AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, refMsgId);
|
||||
AQH_Device_List_Clear(tmpDeviceList);
|
||||
}
|
||||
v=next;
|
||||
}
|
||||
if (AQH_Device_List_GetCount(tmpDeviceList)) {
|
||||
DBG_ERROR(NULL, "Sending %d devices", AQH_Device_List_GetCount(tmpDeviceList));
|
||||
_sendDeviceList(ep, tmpDeviceList, AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, refMsgId); /* send remaining */
|
||||
}
|
||||
AQH_Device_List_free(tmpDeviceList);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* empty list */
|
||||
_sendDeviceList(ep, NULL, AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, refMsgId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _sendDeviceList(AQH_OBJECT *ep, const AQH_DEVICE_LIST *vl, uint32_t flags, uint32_t refMsgId)
|
||||
{
|
||||
AQH_MESSAGE *msg;
|
||||
|
||||
DBG_ERROR(NULL, "Sending msg (refMsgId=%d)", refMsgId);
|
||||
msg=AQH_IpcdMessageDevices_new(AQH_MSGTYPE_IPC_DATA_GETDEVICES_RSP, AQH_Endpoint_GetNextMessageId(ep), refMsgId, flags, vl);
|
||||
AQH_Endpoint_AddMsgOut(ep, msg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
27
apps/aqhome-data/s_getdevices.h
Normal file
27
apps/aqhome-data/s_getdevices.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_DATA_S_GETDEVICES_H
|
||||
#define AQHOME_DATA_S_GETDEVICES_H
|
||||
|
||||
|
||||
#include "./server.h"
|
||||
|
||||
#include <gwenhywfar/tag16.h>
|
||||
|
||||
|
||||
void AqHomeDataServer_HandleGetDevices(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
119
apps/aqhome-data/s_getvalues.c
Normal file
119
apps/aqhome-data/s_getvalues.c
Normal file
@@ -0,0 +1,119 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "./s_getvalues.h"
|
||||
#include "./server_p.h"
|
||||
#include "aqhome/ipc2/endpoint.h"
|
||||
#include "aqhome/msg/ipc/m_ipc.h"
|
||||
#include "aqhome/msg/ipc/data/m_ipcd.h"
|
||||
#include "aqhome/msg/ipc/data/m_ipcd_values.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_result.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_tag16.h"
|
||||
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* defines
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define AQHOMEDATA_VALUESPERMSG 10
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static void _sendValueList(AQH_OBJECT *ep, const AQH_VALUE_LIST *vl, uint32_t flags, uint32_t refMsgId);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* implementations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void AqHomeDataServer_HandleGetValues(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList)
|
||||
{
|
||||
if (tagList) {
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
xo=AqHomeDataServer_GetServerData(o);
|
||||
if (xo) {
|
||||
const AQH_VALUE_LIST *origValueList;
|
||||
|
||||
uint32_t refMsgId;
|
||||
|
||||
refMsgId=AQH_IpcMessage_GetMsgId(msg);
|
||||
|
||||
DBG_INFO(NULL, "HandleGetValues");
|
||||
origValueList=AQH_Storage_GetValueList(xo->storage);
|
||||
if (origValueList) {
|
||||
DBG_INFO(NULL, "Have a list of %d values", AQH_Value_List_GetCount(origValueList));
|
||||
if (AQH_Value_List_GetCount(origValueList)<AQHOMEDATA_VALUESPERMSG) {
|
||||
DBG_INFO(NULL, "Sending all entries in one message");
|
||||
_sendValueList(ep, origValueList, AQH_MSGDATA_VALUES_FLAGS_LASTMSG, refMsgId);
|
||||
}
|
||||
else {
|
||||
AQH_VALUE_LIST *tmpValueList;
|
||||
const AQH_VALUE *v;
|
||||
|
||||
DBG_INFO(NULL, "Sending entries in multiple messages");
|
||||
tmpValueList=AQH_Value_List_new();
|
||||
v=AQH_Value_List_First(origValueList);
|
||||
while(v) {
|
||||
const AQH_VALUE *next;
|
||||
AQH_VALUE *copyOfValue;
|
||||
|
||||
next=AQH_Value_List_Next(v);
|
||||
copyOfValue=AQH_Value_dup(v);
|
||||
AQH_Value_List_Add(copyOfValue, tmpValueList);
|
||||
if (AQH_Value_List_GetCount(tmpValueList)>=AQHOMEDATA_VALUESPERMSG) {
|
||||
DBG_INFO(NULL, "Sending %d values", AQH_Value_List_GetCount(tmpValueList));
|
||||
_sendValueList(ep, tmpValueList, next?0:AQH_MSGDATA_VALUES_FLAGS_LASTMSG, refMsgId);
|
||||
AQH_Value_List_Clear(tmpValueList);
|
||||
}
|
||||
v=next;
|
||||
}
|
||||
if (AQH_Value_List_GetCount(tmpValueList)) {
|
||||
DBG_INFO(NULL, "Sending %d values", AQH_Value_List_GetCount(tmpValueList));
|
||||
_sendValueList(ep, tmpValueList, AQH_MSGDATA_VALUES_FLAGS_LASTMSG, refMsgId); /* send remaining */
|
||||
}
|
||||
AQH_Value_List_free(tmpValueList);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* empty list */
|
||||
_sendValueList(ep, NULL, AQH_MSGDATA_VALUES_FLAGS_LASTMSG, refMsgId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _sendValueList(AQH_OBJECT *ep, const AQH_VALUE_LIST *vl, uint32_t flags, uint32_t refMsgId)
|
||||
{
|
||||
AQH_MESSAGE *msg;
|
||||
|
||||
DBG_ERROR(NULL, "Sending msg (refMsgId=%d)", refMsgId);
|
||||
msg=AQH_IpcdMessageValues_new(AQH_MSGTYPE_IPC_DATA_GETVALUES_RSP, AQH_Endpoint_GetNextMessageId(ep), refMsgId, flags, vl);
|
||||
AQH_Endpoint_AddMsgOut(ep, msg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
27
apps/aqhome-data/s_getvalues.h
Normal file
27
apps/aqhome-data/s_getvalues.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_DATA_S_GETVALUES_H
|
||||
#define AQHOME_DATA_S_GETVALUES_H
|
||||
|
||||
|
||||
#include "./server.h"
|
||||
|
||||
#include <gwenhywfar/tag16.h>
|
||||
|
||||
|
||||
void AqHomeDataServer_HandleGetValues(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
105
apps/aqhome-data/s_moddevice.c
Normal file
105
apps/aqhome-data/s_moddevice.c
Normal file
@@ -0,0 +1,105 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "./s_moddevice.h"
|
||||
|
||||
#include "./server_p.h"
|
||||
#include "aqhome/aqhome.h"
|
||||
#include "aqhome/ipc2/endpoint.h"
|
||||
#include "aqhome/msg/ipc/m_ipc.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_result.h"
|
||||
#include "aqhome/msg/ipc/data/m_ipcd.h"
|
||||
#include "aqhome/msg/ipc/data/m_ipcd_devices.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_result.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_tag16.h"
|
||||
|
||||
#include <gwenhywfar/debug.h>
|
||||
#include <gwenhywfar/text.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* code
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
void AqHomeDataServer_HandleModDevice(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *recvdMsg, const GWEN_TAG16_LIST *tagList)
|
||||
{
|
||||
if (tagList) {
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
xo=AqHomeDataServer_GetServerData(o);
|
||||
if (xo) {
|
||||
int resultCode=AQH_MSGDATA_RESULT_SUCCESS;
|
||||
|
||||
if (AQH_Endpoint_GetPermissions(ep) & AQH_ENDPOINT_PERMS_MODDEVICE) {
|
||||
AQH_DEVICE *device;
|
||||
|
||||
device=AQH_IpcdMessageDevices_ReadFirstDevice(tagList);
|
||||
if (device) {
|
||||
const char *deviceNameForSystem;
|
||||
|
||||
deviceNameForSystem=AQH_Device_GetNameForSystem(device);
|
||||
if (deviceNameForSystem && *deviceNameForSystem) {
|
||||
AQH_DEVICE *storedDevice;
|
||||
|
||||
storedDevice=AQH_Storage_GetDeviceByNameForSystem(xo->storage, deviceNameForSystem);
|
||||
if (storedDevice) {
|
||||
const char *s;
|
||||
|
||||
s=AQH_Device_GetNameForGui(device);
|
||||
if (s && *s)
|
||||
AQH_Device_SetNameForGui(storedDevice, s);
|
||||
|
||||
s=AQH_Device_GetRoomName(device);
|
||||
if (s && *s)
|
||||
AQH_Device_SetRoomName(storedDevice, s);
|
||||
|
||||
s=AQH_Device_GetLocation(device);
|
||||
if (s && *s)
|
||||
AQH_Device_SetLocation(storedDevice, s);
|
||||
|
||||
s=AQH_Device_GetDescription(device);
|
||||
if (s && *s)
|
||||
AQH_Device_SetDescription(storedDevice, s);
|
||||
|
||||
AQH_Storage_AddRuntimeFlags(xo->storage, AQH_STORAGE_RTFLAGS_MODIFIED);
|
||||
resultCode=AQH_MSGDATA_RESULT_SUCCESS;
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "Device \"%s\" not found", deviceNameForSystem);
|
||||
resultCode=AQH_MSGDATA_RESULT_ERROR_NOTFOUND;
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "No name for value");
|
||||
resultCode=AQH_MSGDATA_RESULT_ERROR_NOTFOUND;
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "No device info in message");
|
||||
resultCode=AQH_MSGDATA_RESULT_ERROR_INVALID;
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "No permissions to read data");
|
||||
resultCode=AQH_MSGDATA_RESULT_ERROR_PERMS;
|
||||
}
|
||||
AqHomeDataServer_SendResponseResultToEndpoint(ep, AQH_IpcMessage_GetMsgId(recvdMsg), resultCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
27
apps/aqhome-data/s_moddevice.h
Normal file
27
apps/aqhome-data/s_moddevice.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_DATA_S_MODDEVICE_H
|
||||
#define AQHOME_DATA_S_MODDEVICE_H
|
||||
|
||||
|
||||
#include "./server.h"
|
||||
|
||||
#include <gwenhywfar/tag16.h>
|
||||
|
||||
|
||||
void AqHomeDataServer_HandleModDevice(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *recvdMsg, const GWEN_TAG16_LIST *tagList);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
282
apps/aqhome-data/s_setdata.c
Normal file
282
apps/aqhome-data/s_setdata.c
Normal file
@@ -0,0 +1,282 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "./s_setdata.h"
|
||||
|
||||
#include "./server_p.h"
|
||||
#include "aqhome/aqhome.h"
|
||||
#include "aqhome/ipc2/endpoint.h"
|
||||
#include "aqhome/msg/ipc/m_ipc.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_result.h"
|
||||
#include "aqhome/msg/ipc/data/m_ipcd.h"
|
||||
#include "aqhome/msg/ipc/data/m_ipcd_setdata.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_result.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_tag16.h"
|
||||
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* defines
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define R_SETDATA_REQUEST_EXPIRE_SECS 20
|
||||
#define R_SETDATA_SUBREQUEST_EXPIRE_SECS 10
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static AQH_MSG_REQUEST *_mkRequest_SetData(AQH_OBJECT *o,
|
||||
AQH_OBJECT *epSrc, uint32_t requestMsgId,
|
||||
AQH_OBJECT *epDriver,
|
||||
const AQH_VALUE *v, const char *data);
|
||||
static void _rqSubRequestFinished(AQH_MSG_REQUEST *rq, AQH_MSG_REQUEST *subRq, int reason);
|
||||
static void _rqAbort(AQH_MSG_REQUEST *rq, int reason);
|
||||
|
||||
static AQH_MSG_REQUEST *_mkSubRequest_SetData(AQH_OBJECT *o, AQH_OBJECT *epDriver, const AQH_VALUE *v, const char *data);
|
||||
static int _subRqHandleResponse(AQH_MSG_REQUEST *rq, const AQH_MESSAGE *msg);
|
||||
static void _subRqAbort(AQH_MSG_REQUEST *rq, int reason);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* implementations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void AqHomeDataServer_HandleSetData(AQH_OBJECT *o, AQH_OBJECT *epSrc, const AQH_MESSAGE *recvdMsg, const GWEN_TAG16_LIST *tagList)
|
||||
{
|
||||
if (tagList) {
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
xo=AqHomeDataServer_GetServerData(o);
|
||||
if (xo) {
|
||||
uint32_t msgId;
|
||||
AQH_VALUE *recvdValue;
|
||||
|
||||
msgId=AQH_IpcMessage_GetMsgId(recvdMsg);
|
||||
DBG_INFO(NULL, "Received IPC SetDataRequest message (msgId=%d)", msgId);
|
||||
|
||||
recvdValue=AQH_IpcdMessageSetData_ReadValue(tagList);
|
||||
if (recvdValue) {
|
||||
const char *valueName;
|
||||
char *valueDataFreeable;
|
||||
AQH_VALUE *systemValue;
|
||||
|
||||
valueName=AQH_Value_GetNameForSystem(recvdValue);
|
||||
valueDataFreeable=AQH_IpcdMessageSetData_ReadData(tagList);
|
||||
|
||||
systemValue=AQH_Storage_GetValueByNameForSystem(xo->storage, valueName);
|
||||
if (systemValue) {
|
||||
if (AQH_Value_GetValueType(systemValue)==AQH_ValueType_Actor) {
|
||||
const char *driverName;
|
||||
|
||||
driverName=AQH_Value_GetDriver(systemValue);
|
||||
if (driverName && *driverName) {
|
||||
AQH_OBJECT *epDriver;
|
||||
|
||||
epDriver=AqHomeDataServer_GetIpcEndpointByServiceName(o, driverName);
|
||||
if (epDriver) {
|
||||
AQH_MSG_REQUEST *rq;
|
||||
|
||||
DBG_ERROR(NULL, "Creating SETDATA request for driver endpoint (%s)", AQH_Endpoint_GetServiceName(epDriver));
|
||||
rq=_mkRequest_SetData(o, epSrc, msgId, epDriver, systemValue, valueDataFreeable);
|
||||
AqHomeDataServer_AddRequestToTree(o, rq);
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Driver \"%s\" not available", driverName);
|
||||
AqHomeDataServer_SendResponseResultToEndpoint(epSrc, msgId, AQH_MSGDATA_RESULT_ERROR_GENERIC);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "No driver name");
|
||||
AqHomeDataServer_SendResponseResultToEndpoint(epSrc, msgId, AQH_MSGDATA_RESULT_ERROR_GENERIC);
|
||||
}
|
||||
} /* if actor */
|
||||
else {
|
||||
DBG_ERROR(NULL, "Value \"%s\" is not an actor", valueName);
|
||||
AqHomeDataServer_SendResponseResultToEndpoint(epSrc, msgId, AQH_MSGDATA_RESULT_ERROR_INVALID);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Unknown value \"%s\"", valueName);
|
||||
AqHomeDataServer_SendResponseResultToEndpoint(epSrc, msgId, AQH_MSGDATA_RESULT_ERROR_NOTFOUND);
|
||||
}
|
||||
AQH_Value_free(recvdValue);
|
||||
free(valueDataFreeable);
|
||||
} /* if recvdValue */
|
||||
else {
|
||||
DBG_ERROR(NULL, "No value in message");
|
||||
AqHomeDataServer_SendResponseResultToEndpoint(epSrc, msgId, AQH_MSGDATA_RESULT_ERROR_BADDATA);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* IPC Request SETDATA
|
||||
*/
|
||||
|
||||
AQH_MSG_REQUEST *_mkRequest_SetData(AQH_OBJECT *o,
|
||||
AQH_OBJECT *epSrc, uint32_t requestMsgId,
|
||||
AQH_OBJECT *epDriver,
|
||||
const AQH_VALUE *v, const char *data)
|
||||
{
|
||||
AQH_MSG_REQUEST *rq;
|
||||
AQH_MSG_REQUEST *subRq;
|
||||
|
||||
rq=AQH_MsgRequest_new();
|
||||
AQH_MsgRequest_SetPrivateData(rq, o);
|
||||
AQH_MsgRequest_SetEndpoint(rq, epSrc);
|
||||
AQH_MsgRequest_SetRequestMsgId(rq, requestMsgId);
|
||||
AQH_MsgRequest_SetSubRequestFinishedFn(rq, _rqSubRequestFinished);
|
||||
AQH_MsgRequest_SetAbortFn(rq, _rqAbort);
|
||||
AQH_MsgRequest_SetTimestamps(rq, R_SETDATA_REQUEST_EXPIRE_SECS);
|
||||
|
||||
subRq=_mkSubRequest_SetData(o, epDriver, v, data);
|
||||
AQH_MsgRequest_Tree2_AddChild(rq, subRq);
|
||||
|
||||
return rq;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _rqSubRequestFinished(AQH_MSG_REQUEST *rq, AQH_MSG_REQUEST *subRq, int reason)
|
||||
{
|
||||
AQH_OBJECT *ep;
|
||||
uint32_t refMsgId;
|
||||
int result;
|
||||
|
||||
DBG_DEBUG(NULL, "SubRequest finished (reason: %d)", reason);
|
||||
refMsgId=AQH_MsgRequest_GetRequestMsgId(rq);
|
||||
ep=AQH_MsgRequest_GetEndpoint(rq);
|
||||
result=AQH_MsgRequest_GetResult(subRq);
|
||||
|
||||
if (reason==AQH_MSG_REQUEST_REASON_ABORTED)
|
||||
AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, AQH_MSGDATA_RESULT_ERROR_GENERIC);
|
||||
else
|
||||
AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, result);
|
||||
|
||||
AQH_MsgRequest_SetResult(rq, result);
|
||||
AQH_MsgRequest_SetState(rq, AQH_MSG_REQUEST_STATE_DONE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _rqAbort(AQH_MSG_REQUEST *rq, int reason)
|
||||
{
|
||||
AQH_OBJECT *ep;
|
||||
uint32_t refMsgId;
|
||||
AQH_MSG_REQUEST *rqParent;
|
||||
|
||||
DBG_INFO(NULL, "Aborting request");
|
||||
refMsgId=AQH_MsgRequest_GetRequestMsgId(rq);
|
||||
ep=AQH_MsgRequest_GetEndpoint(rq);
|
||||
AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, AQH_MSGDATA_RESULT_ERROR_GENERIC);
|
||||
AQH_MsgRequest_SetState(rq, AQH_MSG_REQUEST_STATE_DONE);
|
||||
|
||||
rqParent=AQH_MsgRequest_Tree2_GetParent(rq);
|
||||
if (rqParent)
|
||||
AQH_MsgRequest_SubRequestFinished(rqParent, rq, reason);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Driver Request SETDATA
|
||||
*/
|
||||
|
||||
|
||||
AQH_MSG_REQUEST *_mkSubRequest_SetData(AQH_OBJECT *o, AQH_OBJECT *epDriver, const AQH_VALUE *v, const char *data)
|
||||
{
|
||||
AQH_MSG_REQUEST *rq;
|
||||
uint16_t msgId;
|
||||
AQH_MESSAGE *driverMsg;
|
||||
|
||||
rq=AQH_MsgRequest_new();
|
||||
AQH_MsgRequest_SetPrivateData(rq, o);
|
||||
AQH_MsgRequest_SetEndpoint(rq, epDriver);
|
||||
|
||||
AQH_MsgRequest_SetHandleResponseFn(rq, _subRqHandleResponse);
|
||||
AQH_MsgRequest_SetAbortFn(rq, _subRqAbort);
|
||||
|
||||
msgId=AQH_Endpoint_GetNextMessageId(epDriver);
|
||||
AQH_MsgRequest_SetRequestMsgId(rq, msgId);
|
||||
AQH_MsgRequest_SetTimestamps(rq, R_SETDATA_SUBREQUEST_EXPIRE_SECS);
|
||||
|
||||
driverMsg=AQH_IpcdMessageSetData_new(AQH_MSGTYPE_IPC_DATA_SETDATA, msgId, 0, v, data);
|
||||
AQH_Endpoint_AddMsgOut(epDriver, driverMsg);
|
||||
|
||||
return rq;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _subRqHandleResponse(AQH_MSG_REQUEST *rq, const AQH_MESSAGE *msg)
|
||||
{
|
||||
DBG_DEBUG(NULL, "Checking message from driver");
|
||||
if (AQH_IpcMessage_GetCode(msg)==AQH_MSGTYPE_IPC_DATA_RESULT) {
|
||||
GWEN_TAG16_LIST *tagList;
|
||||
|
||||
tagList=AQH_IpcMessageTag16_ParsePayload(msg, 0);
|
||||
if (tagList) {
|
||||
uint32_t result;
|
||||
AQH_MSG_REQUEST *rqParent;
|
||||
|
||||
result=AQH_IpcMessageResult_GetResult(tagList);
|
||||
DBG_INFO(NULL, "Received result for request: %d", result);
|
||||
AQH_MsgRequest_SetResult(rq, result);
|
||||
AQH_MsgRequest_SetState(rq, AQH_MSG_REQUEST_STATE_DONE);
|
||||
rqParent=AQH_MsgRequest_Tree2_GetParent(rq);
|
||||
if (rqParent)
|
||||
AQH_MsgRequest_SubRequestFinished(rqParent, rq, AQH_MSG_REQUEST_REASON_DONE);
|
||||
|
||||
GWEN_Tag16_List_free(tagList);
|
||||
return AQH_MSG_REQUEST_RESULT_HANDLED;
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Bad message %d (no TAG16 data)", AQH_IpcMessage_GetCode(msg));
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Unexpected response message %d", AQH_IpcMessage_GetCode(msg));
|
||||
}
|
||||
|
||||
return AQH_MSG_REQUEST_RESULT_NOT_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _subRqAbort(AQH_MSG_REQUEST *rq, int reason)
|
||||
{
|
||||
AQH_MSG_REQUEST *rqParent;
|
||||
|
||||
DBG_INFO(NULL, "Aborting request");
|
||||
|
||||
AQH_MsgRequest_SetResult(rq, AQH_MSGDATA_RESULT_ERROR_GENERIC);
|
||||
AQH_MsgRequest_SetState(rq, AQH_MSG_REQUEST_STATE_DONE);
|
||||
|
||||
rqParent=AQH_MsgRequest_Tree2_GetParent(rq);
|
||||
if (rqParent)
|
||||
AQH_MsgRequest_SubRequestFinished(rqParent, rq, reason);
|
||||
}
|
||||
|
||||
|
||||
27
apps/aqhome-data/s_setdata.h
Normal file
27
apps/aqhome-data/s_setdata.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_DATA_S_SETDATA_H
|
||||
#define AQHOME_DATA_S_SETDATA_H
|
||||
|
||||
|
||||
#include "./server.h"
|
||||
|
||||
#include <gwenhywfar/tag16.h>
|
||||
|
||||
|
||||
void AqHomeDataServer_HandleSetData(AQH_OBJECT *o, AQH_OBJECT *epSrc, const AQH_MESSAGE *recvdMsg, const GWEN_TAG16_LIST *tagList);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
171
apps/aqhome-data/s_updatedata.c
Normal file
171
apps/aqhome-data/s_updatedata.c
Normal file
@@ -0,0 +1,171 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "./s_updatedata.h"
|
||||
|
||||
#include "./server_p.h"
|
||||
#include <aqhome/data/value.h>
|
||||
#include "aqhome/ipc2/endpoint.h"
|
||||
#include "aqhome/msg/ipc/m_ipc.h"
|
||||
#include "aqhome/msg/ipc/data/m_ipcd.h"
|
||||
#include "aqhome/msg/ipc/data/m_ipcd_multidata.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_result.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_tag16.h"
|
||||
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* defines
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define DISABLE_DEBUGLOG
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static int _storeDataPoints(AQHOME_SERVER *xo, const AQH_VALUE *v, const uint64_t *dataPoints, unsigned int numValues);
|
||||
static void _sendDataChangedMsgToAllClients(AQHOME_SERVER *xo, AQH_OBJECT *epSrc,
|
||||
const AQH_VALUE *v, const uint64_t *dataPoints, int numValues);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* implementations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void AqHomeDataServer_HandleUpdateData(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList)
|
||||
{
|
||||
if (tagList) {
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
xo=AqHomeDataServer_GetServerData(o);
|
||||
if (xo) {
|
||||
AQH_MESSAGE *outMsg;
|
||||
int resultCode=AQH_MSGDATA_RESULT_SUCCESS;
|
||||
AQH_VALUE *recvdValue;
|
||||
|
||||
recvdValue=AQH_IpcdMessageMultiData_ReadValue(tagList);
|
||||
if (recvdValue) {
|
||||
const char *valueName;
|
||||
const uint64_t *dataPoints=NULL;
|
||||
uint64_t numberOfPoints=0;
|
||||
|
||||
valueName=recvdValue?AQH_Value_GetName(recvdValue):NULL;
|
||||
AQH_IpcdMessageMultiData_ReadDatapoints(tagList, &dataPoints, &numberOfPoints);
|
||||
if (numberOfPoints>0) {
|
||||
AQH_VALUE *value;
|
||||
|
||||
value=AqHomeDataServer_GetOrCreateValueForDriverWithTemplate(o, ep, recvdValue);
|
||||
if (value) {
|
||||
if (AQH_Endpoint_GetPermissions(ep) & AQH_ENDPOINT_PERMS_ADDDATA) {
|
||||
resultCode=_storeDataPoints(xo, value, dataPoints, numberOfPoints);
|
||||
if (resultCode==AQH_MSGDATA_RESULT_SUCCESS)
|
||||
_sendDataChangedMsgToAllClients(xo, ep, value, dataPoints, numberOfPoints);
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "No permissions to add data to value \"%s\"", valueName);
|
||||
resultCode=AQH_MSGDATA_RESULT_ERROR_PERMS;
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "No permissions to add/create value \"%s\"", valueName);
|
||||
resultCode=AQH_MSGDATA_RESULT_ERROR_PERMS;
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "No datapoints");
|
||||
resultCode=AQH_MSGDATA_RESULT_ERROR_INVALID;
|
||||
}
|
||||
AQH_Value_free(recvdValue);
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "No value");
|
||||
resultCode=AQH_MSGDATA_RESULT_ERROR_INVALID;
|
||||
}
|
||||
|
||||
outMsg=AQH_IpcMessageResult_new(AQH_IPC_PROTOCOL_DATA_ID,
|
||||
AQH_IPC_PROTOCOL_DATA_VERSION,
|
||||
AQH_MSGTYPE_IPC_DATA_RESULT,
|
||||
AQH_Endpoint_GetNextMessageId(ep),
|
||||
AQH_IpcMessage_GetMsgId(msg),
|
||||
resultCode, NULL);
|
||||
AQH_Endpoint_AddMsgOut(ep, outMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _storeDataPoints(AQHOME_SERVER *xo, const AQH_VALUE *v, const uint64_t *dataPoints, unsigned int numValues)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for(i=0; i<numValues; i++) {
|
||||
uint64_t timestamp;
|
||||
union {double f; uint64_t i;} u;
|
||||
int rv;
|
||||
|
||||
timestamp=*(dataPoints++);
|
||||
u.i=*(dataPoints++);
|
||||
rv=AQH_Storage_AddDatapoint(xo->storage, AQH_Value_GetId(v), timestamp, u.f);
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "here (%d)", rv);
|
||||
return AQH_MSGDATA_RESULT_ERROR_GENERIC;
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "Datapoint added for value \"%s\"", AQH_Value_GetNameForSystem(v));
|
||||
}
|
||||
} /* for */
|
||||
|
||||
return AQH_MSGDATA_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _sendDataChangedMsgToAllClients(AQHOME_SERVER *xo, AQH_OBJECT *epSrc, const AQH_VALUE *v, const uint64_t *dataPoints, int numValues)
|
||||
{
|
||||
AQH_OBJECT *ep;
|
||||
|
||||
ep=AQH_Object_List_First(xo->tcpClientList);
|
||||
while(ep) {
|
||||
if (ep!=epSrc) {
|
||||
if (AQH_Endpoint_GetFlags(ep) & AQH_ENDPOINT_FLAGS_WANTUPDATES) {
|
||||
AQH_MESSAGE *msg;
|
||||
|
||||
DBG_DEBUG(AQH_LOGDOMAIN, "Sending update msg to endpoint");
|
||||
msg=AQH_IpcdMessageMultiData_new(AQH_MSGTYPE_IPC_DATA_DATACHANGED,
|
||||
AQH_Endpoint_GetNextMessageId(ep), 0,
|
||||
v, dataPoints, numValues);
|
||||
AQH_Endpoint_AddMsgOut(ep, msg);
|
||||
}
|
||||
else {
|
||||
DBG_DEBUG(AQH_LOGDOMAIN, "Endpoint doesn't want updates");
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_DEBUG(AQH_LOGDOMAIN, "Not sending update msg to source of updates");
|
||||
}
|
||||
ep=AQH_Object_List_Next(ep);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
27
apps/aqhome-data/s_updatedata.h
Normal file
27
apps/aqhome-data/s_updatedata.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_DATA_S_UPDATEDATA_H
|
||||
#define AQHOME_DATA_S_UPDATEDATA_H
|
||||
|
||||
|
||||
#include "./server.h"
|
||||
|
||||
#include <gwenhywfar/tag16.h>
|
||||
|
||||
|
||||
void AqHomeDataServer_HandleUpdateData(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
857
apps/aqhome-data/server.c
Normal file
857
apps/aqhome-data/server.c
Normal file
@@ -0,0 +1,857 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "./server_p.h"
|
||||
#include "./s_connect.h"
|
||||
#include "./s_getdevices.h"
|
||||
#include "./s_getvalues.h"
|
||||
#include "./s_addvalue.h"
|
||||
#include "./s_annvalue.h"
|
||||
#include "./s_updatedata.h"
|
||||
#include "./s_setdata.h"
|
||||
#include "./s_getdatapoints.h"
|
||||
#include "./s_moddevice.h"
|
||||
|
||||
#include <aqhome/aqhome.h>
|
||||
#include <aqhome/ipc2/ipc_server.h>
|
||||
#include <aqhome/ipc2/tcpd_object.h>
|
||||
#include <aqhome/msg/ipc/m_ipc.h>
|
||||
#include <aqhome/msg/ipc/m_ipc_result.h>
|
||||
#include <aqhome/msg/ipc/m_ipc_tag16.h>
|
||||
#include <aqhome/msg/ipc/data/m_ipcd.h>
|
||||
|
||||
#include <gwenhywfar/args.h>
|
||||
#include <gwenhywfar/misc.h>
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* defines
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define I18N(msg) msg
|
||||
#define I18S(msg) msg
|
||||
|
||||
|
||||
|
||||
enum {
|
||||
AQH_AQHOME_SERVER_SLOT_NEWCLIENT=1,
|
||||
AQH_AQHOME_SERVER_SLOT_CLOSED
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* global vars
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
GWEN_INHERIT(AQH_OBJECT, AQHOME_SERVER)
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static void GWENHYWFAR_CB _freeData(void *bp, void *p);
|
||||
static int _setupStorage(AQHOME_SERVER *xo, GWEN_DB_NODE *dbArgs);
|
||||
static int _setupIpc(AQH_OBJECT *o, AQHOME_SERVER *xo, GWEN_DB_NODE *dbArgs);
|
||||
static int _handleSignal(AQH_OBJECT *o, uint32_t slotId, AQH_OBJECT *senderObject, int param1, void *param2);
|
||||
static int _handleNewClient(AQH_OBJECT *o, AQH_OBJECT *clientEndpoint);
|
||||
static int _handleClientDown(AQH_OBJECT *o, AQH_OBJECT *clientEndpoint);
|
||||
static void _handleMsgsFromClient(AQH_OBJECT *o, AQHOME_SERVER *xo, AQH_OBJECT *ep);
|
||||
static void _handleMsgFromClient(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg);
|
||||
static AQH_DEVICE *_getOrCreateDeviceForDriver(AQHOME_SERVER *xo, AQH_OBJECT *epDriver, const char *deviceName);
|
||||
static int _createPidFile(const char *pidFilename);
|
||||
static int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* implementation
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
AQH_OBJECT *AqHomeDataServer_new(AQH_EVENT_LOOP *eventLoop)
|
||||
{
|
||||
AQH_OBJECT *o;
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
o=AQH_Object_new(eventLoop);
|
||||
GWEN_NEW_OBJECT(AQHOME_SERVER, xo);
|
||||
GWEN_INHERIT_SETDATA(AQH_OBJECT, AQHOME_SERVER, o, xo, _freeData);
|
||||
xo->storageMutex=GWEN_Mutex_new();
|
||||
xo->tcpClientList=AQH_Object_List_new();
|
||||
xo->requestTree=AQH_MsgRequest_new();
|
||||
|
||||
AQH_Object_SetSignalHandlerFn(o, _handleSignal);
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GWENHYWFAR_CB _freeData(GWEN_UNUSED void *bp, void *p)
|
||||
{
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
xo=(AQHOME_SERVER*) p;
|
||||
GWEN_Mutex_free(xo->storageMutex);
|
||||
if (xo->ipcServer) {
|
||||
AQH_Object_Disable(xo->ipcServer);
|
||||
AQH_Object_free(xo->ipcServer);
|
||||
xo->ipcServer=NULL;
|
||||
}
|
||||
if (xo->tcpClientList) {
|
||||
AQH_Object_List_free(xo->tcpClientList);
|
||||
xo->tcpClientList=NULL;
|
||||
}
|
||||
GWEN_DB_Group_free(xo->dbArgs);
|
||||
AQH_Storage_free(xo->storage);
|
||||
free(xo->pidFile);
|
||||
|
||||
GWEN_FREE_OBJECT(xo);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
* getters, setters
|
||||
* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
*/
|
||||
|
||||
AQHOME_SERVER *AqHomeDataServer_GetServerData(const AQH_OBJECT *o)
|
||||
{
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
|
||||
return xo;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int AqHomeDataServer_GetTimeout(const AQH_OBJECT *o)
|
||||
{
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
|
||||
if (xo)
|
||||
return xo->timeout;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int AqHomeDataServer_GetClientNum(const AQH_OBJECT *o)
|
||||
{
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
|
||||
if (xo)
|
||||
return AQH_Object_List_GetCount(xo->tcpClientList);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
* init
|
||||
* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
*/
|
||||
|
||||
int AqHomeDataServer_Init(AQH_OBJECT *o, int argc, char **argv)
|
||||
{
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
|
||||
if (xo) {
|
||||
GWEN_DB_NODE *dbArgs;
|
||||
int rv;
|
||||
const char *s;
|
||||
|
||||
dbArgs=GWEN_DB_Group_new("args");
|
||||
rv=_readArgs(argc, argv, dbArgs);
|
||||
if (rv<0) {
|
||||
DBG_ERROR(NULL, "Error reading args (%d)", rv);
|
||||
return rv;
|
||||
}
|
||||
AQH_MergeConfigFileIntoConfig(dbArgs, "ConfigFile");
|
||||
|
||||
xo->dbArgs=dbArgs;
|
||||
|
||||
s=GWEN_DB_GetCharValue(dbArgs, "loglevel", 0, NULL);
|
||||
if (s && *s) {
|
||||
GWEN_LOGGER_LEVEL ll;
|
||||
|
||||
ll=GWEN_Logger_Name2Level(s);
|
||||
GWEN_Logger_SetLevel(NULL, ll);
|
||||
}
|
||||
|
||||
xo->timeout=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 0);
|
||||
|
||||
s=GWEN_DB_GetCharValue(dbArgs, "pidfile", 0, AQHOME_DATA_DEFAULT_PIDFILE);
|
||||
if (s && *s) {
|
||||
free(xo->pidFile);
|
||||
xo->pidFile=strdup(s);
|
||||
rv=_createPidFile(s);
|
||||
if (rv<0) {
|
||||
DBG_ERROR(NULL, "Error creating PID file (%d)", rv);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
rv=_setupStorage(xo, dbArgs);
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "here (%d)", rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv=_setupIpc(o, xo, dbArgs);
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "here (%d)", rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Not of type AQHOME_SERVER object");
|
||||
return GWEN_ERROR_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _setupStorage(AQHOME_SERVER *xo, GWEN_DB_NODE *dbArgs)
|
||||
{
|
||||
const char *dataFolder;
|
||||
GWEN_BUFFER *nameBuf;
|
||||
AQH_STORAGE *sto;
|
||||
int rv;
|
||||
|
||||
dataFolder=GWEN_DB_GetCharValue(dbArgs, "dataFolder", 0, AQHOME_DATA_DEFAULT_DATADIR);
|
||||
nameBuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||
GWEN_Buffer_AppendArgs(nameBuf, "%s%s%s", dataFolder, GWEN_DIR_SEPARATOR_S, AQHOME_DATA_STATEFILENAME);
|
||||
|
||||
sto=AQH_Storage_new();
|
||||
AQH_Storage_SetStateFile(sto, GWEN_Buffer_GetStart(nameBuf));
|
||||
AQH_Storage_SetDataFileFolder(sto, dataFolder);
|
||||
GWEN_Buffer_free(nameBuf);
|
||||
|
||||
rv=AQH_Storage_Init(sto);
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "here (%d)", rv);
|
||||
AQH_Storage_free(sto);
|
||||
return rv;
|
||||
}
|
||||
xo->storage=sto;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _setupIpc(AQH_OBJECT *o, AQHOME_SERVER *xo, GWEN_DB_NODE *dbArgs)
|
||||
{
|
||||
const char *tcpAddress;
|
||||
int tcpPort;
|
||||
|
||||
tcpAddress=GWEN_DB_GetCharValue(dbArgs, "tcpAddress", 0, NULL);
|
||||
if (!(tcpAddress && *tcpAddress))
|
||||
tcpAddress=GWEN_DB_GetCharValue(dbArgs, "ConfigFile/brokerAddress", 0, NULL);
|
||||
|
||||
tcpPort=GWEN_DB_GetIntValue(dbArgs, "tcpPort", 0, -1);
|
||||
if (tcpPort<0)
|
||||
tcpPort=GWEN_DB_GetIntValue(dbArgs, "ConfigFile/brokerPort", 0, AQHOME_DATA_DEFAULT_IPC_PORT);
|
||||
|
||||
if (tcpAddress && *tcpAddress && tcpPort>0) {
|
||||
int fd;
|
||||
|
||||
DBG_ERROR(NULL, "Starting TCP service on \"%s\":%d", tcpAddress, tcpPort);
|
||||
fd=AQH_TcpdObject_CreateListeningSocket(tcpAddress, tcpPort);
|
||||
if (fd<0) {
|
||||
DBG_INFO(NULL, "here");
|
||||
return GWEN_ERROR_IO;
|
||||
}
|
||||
|
||||
xo->ipcServer=AQH_IpcServerObject_new(AQH_Object_GetEventLoop(o), fd);
|
||||
AQH_Object_AddLink(xo->ipcServer, AQH_IPC_SERVER_SIGNAL_NEWCLIENT, AQH_AQHOME_SERVER_SLOT_NEWCLIENT, o);
|
||||
AQH_Object_Enable(xo->ipcServer);
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Missing server address");
|
||||
return GWEN_ERROR_GENERIC;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
* signal handler
|
||||
* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
*/
|
||||
|
||||
int _handleSignal(AQH_OBJECT *o, uint32_t slotId, AQH_OBJECT *senderObject, GWEN_UNUSED int param1, void *param2)
|
||||
{
|
||||
switch(slotId) {
|
||||
case AQH_AQHOME_SERVER_SLOT_NEWCLIENT: return _handleNewClient(o, (AQH_OBJECT*) param2);
|
||||
case AQH_AQHOME_SERVER_SLOT_CLOSED: return _handleClientDown(o, senderObject);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0; /* not handled */
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _handleNewClient(AQH_OBJECT *o, AQH_OBJECT *clientEndpoint)
|
||||
{
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
|
||||
if (xo) {
|
||||
DBG_ERROR(NULL, "New IPC client");
|
||||
AQH_Object_AddLink(clientEndpoint, AQH_ENDPOINT_SIGNAL_CLOSED, AQH_AQHOME_SERVER_SLOT_CLOSED, o);
|
||||
AQH_Object_List_Add(clientEndpoint, xo->tcpClientList);
|
||||
return 1; /* handled */
|
||||
}
|
||||
|
||||
return 0; /* not handled */
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _handleClientDown(AQH_OBJECT *o, AQH_OBJECT *clientEndpoint)
|
||||
{
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
|
||||
if (xo) {
|
||||
DBG_ERROR(NULL, "IPC client down");
|
||||
AQH_Object_AddFlags(clientEndpoint, AQH_OBJECT_FLAGS_DELETE);
|
||||
return 1; /* handled */
|
||||
}
|
||||
|
||||
return 0; /* not handled */
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
* client management functions
|
||||
* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
*/
|
||||
|
||||
void AqHomeDataServer_CleanupClients(AQH_OBJECT *o)
|
||||
{
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
|
||||
if (xo) {
|
||||
AQH_OBJECT *ep;
|
||||
|
||||
ep=AQH_Object_List_First(xo->tcpClientList);
|
||||
while(ep) {
|
||||
AQH_OBJECT *epNext;
|
||||
|
||||
epNext=AQH_Object_List_Next(ep);
|
||||
if (AQH_Object_GetFlags(ep) & AQH_OBJECT_FLAGS_DELETE) {
|
||||
AQH_Object_List_Del(ep);
|
||||
AQH_Object_free(ep);
|
||||
}
|
||||
ep=epNext;
|
||||
} /* while */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AqHomeDataServer_HandleClientMsgs(AQH_OBJECT *o)
|
||||
{
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
|
||||
if (xo) {
|
||||
AQH_OBJECT *ep;
|
||||
|
||||
ep=AQH_Object_List_First(xo->tcpClientList);
|
||||
while(ep) {
|
||||
AQH_OBJECT *epNext;
|
||||
|
||||
epNext=AQH_Object_List_Next(ep);
|
||||
_handleMsgsFromClient(o, xo, ep);
|
||||
ep=epNext;
|
||||
} /* while */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _handleMsgsFromClient(AQH_OBJECT *o, AQHOME_SERVER *xo, AQH_OBJECT *ep)
|
||||
{
|
||||
AQH_MESSAGE *msg;
|
||||
|
||||
while( (msg=AQH_Endpoint_GetNextMsgIn(ep)) ) {
|
||||
AQH_Message_SetObject(msg, ep);
|
||||
if (AQH_Request_Tree2_HandleIpcMsg(xo->requestTree, ep, msg)!=AQH_MSG_REQUEST_RESULT_HANDLED)
|
||||
_handleMsgFromClient(o, ep, msg);
|
||||
AQH_Message_free(msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _handleMsgFromClient(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg)
|
||||
{
|
||||
GWEN_TAG16_LIST *tagList;
|
||||
uint16_t code;
|
||||
uint8_t protoId;
|
||||
|
||||
tagList=AQH_IpcMessageTag16_ParsePayload(msg, 0);
|
||||
|
||||
code=AQH_IpcMessage_GetCode(msg);
|
||||
protoId=AQH_IpcMessage_GetProtoId(msg);
|
||||
if (protoId==AQH_IPC_PROTOCOL_DATA_ID) {
|
||||
DBG_INFO(NULL, "Received IPC packet %d (%x)", (int) code, code);
|
||||
switch(code) {
|
||||
case AQH_MSGTYPE_IPC_DATA_CONNECT_REQ: AqHomeDataServer_HandleConnect(o, ep, msg, tagList); break;
|
||||
case AQH_MSGTYPE_IPC_DATA_UPDATEDATA: AqHomeDataServer_HandleUpdateData(o, ep, msg, tagList); break;
|
||||
case AQH_MSGTYPE_IPC_DATA_GETVALUES_REQ: AqHomeDataServer_HandleGetValues(o, ep, msg, tagList); break;
|
||||
case AQH_MSGTYPE_IPC_DATA_GETDATA_REQ: AqHomeDataServer_HandleGetDataPoints(o, ep, msg, tagList); break;
|
||||
case AQH_MSGTYPE_IPC_DATA_SETDATA: AqHomeDataServer_HandleSetData(o, ep, msg, tagList); break;
|
||||
case AQH_MSGTYPE_IPC_DATA_ADDVALUE: AqHomeDataServer_HandleAddValue(o, ep, msg, tagList); break;
|
||||
case AQH_MSGTYPE_IPC_DATA_ANNOUNCEVALUE: AqHomeDataServer_HandleAnnounceValue(o, ep, msg, tagList); break;
|
||||
case AQH_MSGTYPE_IPC_DATA_GETDEVICES_REQ: AqHomeDataServer_HandleGetDevices(o, ep, msg, tagList); break;
|
||||
case AQH_MSGTYPE_IPC_DATA_MODDEVICE_REQ: AqHomeDataServer_HandleModDevice(o, ep, msg, tagList); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Invalid IPC protocol %d (%02x)", protoId, protoId);
|
||||
}
|
||||
GWEN_Tag16_List_free(tagList);
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQH_OBJECT *AqHomeDataServer_GetIpcEndpointByServiceName(const AQH_OBJECT *o, const char *serviceName)
|
||||
{
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
|
||||
if (xo) {
|
||||
AQH_OBJECT *ep;
|
||||
|
||||
ep=AQH_Object_List_First(xo->tcpClientList);
|
||||
while(ep) {
|
||||
const char *s;
|
||||
|
||||
s=AQH_Endpoint_GetServiceName(ep);
|
||||
if (s && *s && strcasecmp(s, serviceName)==0)
|
||||
return ep;
|
||||
ep=AQH_Object_List_Next(ep);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AqHomeDataServer_SendResponseResultToEndpoint(AQH_OBJECT *ep, uint32_t refMsgId, int result)
|
||||
{
|
||||
AQH_MESSAGE *msg;
|
||||
|
||||
msg=AQH_IpcMessageResult_new(AQH_IPC_PROTOCOL_DATA_ID, AQH_IPC_PROTOCOL_DATA_VERSION, AQH_MSGTYPE_IPC_DATA_RESULT,
|
||||
AQH_Endpoint_GetNextMessageId(ep), refMsgId, result, NULL);
|
||||
AQH_Endpoint_AddMsgOut(ep, msg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
* request management functions
|
||||
* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
*/
|
||||
|
||||
AQH_MSG_REQUEST *AqHomeDataServer_GetRequestTree(const AQH_OBJECT *o)
|
||||
{
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
|
||||
if (xo)
|
||||
return xo->requestTree;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AqHomeDataServer_AddRequestToTree(AQH_OBJECT *o, AQH_MSG_REQUEST *rq)
|
||||
{
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
|
||||
if (xo && rq)
|
||||
AQH_MsgRequest_Tree2_AddChild(xo->requestTree, rq);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AqHomeDataServer_CleanupRequests(AQH_OBJECT *o)
|
||||
{
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
|
||||
if (xo) {
|
||||
AQH_Request_Tree2_CheckTimeouts(xo->requestTree);
|
||||
AQH_Request_Tree2_Cleanup(xo->requestTree);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
* storage management functions
|
||||
* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
*/
|
||||
|
||||
int AqHomeDataServer_LockStorage(AQH_OBJECT *o)
|
||||
{
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
|
||||
if (xo) {
|
||||
int rv;
|
||||
|
||||
rv=GWEN_Mutex_Lock(xo->storageMutex);
|
||||
if (rv<0) {
|
||||
DBG_ERROR(AQH_LOGDOMAIN, "Error obtaining lock on storage mutex");
|
||||
return rv;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
return GWEN_ERROR_INVALID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int AqHomeDataServer_UnlockStorage(AQH_OBJECT *o)
|
||||
{
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
|
||||
if (xo) {
|
||||
int rv;
|
||||
|
||||
rv=GWEN_Mutex_Unlock(xo->storageMutex);
|
||||
if (rv<0) {
|
||||
DBG_ERROR(AQH_LOGDOMAIN, "Error releasing lock on storage mutex");
|
||||
return rv;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
return GWEN_ERROR_INVALID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int AqHomeDataServer_WriteStorageIfChanged(AQH_OBJECT *o)
|
||||
{
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
|
||||
if (xo) {
|
||||
if (AQH_Storage_GetRuntimeFlags(xo->storage) & AQH_STORAGE_RTFLAGS_MODIFIED) {
|
||||
int rv;
|
||||
|
||||
DBG_INFO(NULL, "Storage modified, writing statefile");
|
||||
rv=AqHomeDataServer_LockStorage(o);
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "Error locking storage (%d)", rv);
|
||||
return rv;
|
||||
}
|
||||
rv=AQH_Storage_WriteState(xo->storage);
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "Error writing state file (%d)", rv);
|
||||
AqHomeDataServer_UnlockStorage(o);
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv=AqHomeDataServer_UnlockStorage(o);
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "Error unlocking storage (%d)", rv);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
return GWEN_ERROR_INVALID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQH_VALUE *AqHomeDataServer_GetOrCreateValueForDriverWithTemplate(AQH_OBJECT *o, AQH_OBJECT *epDriver, const AQH_VALUE *valueTemplate)
|
||||
{
|
||||
AQHOME_SERVER *xo;
|
||||
|
||||
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
|
||||
if (xo) {
|
||||
const char *serviceName;
|
||||
AQH_VALUE *v;
|
||||
GWEN_BUFFER *buf;
|
||||
const char *valueName;
|
||||
const char *deviceName;
|
||||
|
||||
serviceName=AQH_Endpoint_GetServiceName(epDriver);
|
||||
valueName=AQH_Value_GetName(valueTemplate);
|
||||
deviceName=AQH_Value_GetDeviceName(valueTemplate);
|
||||
|
||||
buf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||
if (deviceName && *deviceName)
|
||||
GWEN_Buffer_AppendArgs(buf, "%s/%s/%s", (serviceName && *serviceName)?serviceName:"unknown", deviceName, valueName);
|
||||
else
|
||||
GWEN_Buffer_AppendArgs(buf, "%s/%s", (serviceName && *serviceName)?serviceName:"unknown", valueName);
|
||||
|
||||
v=AQH_Storage_GetValueByNameForSystem(xo->storage, GWEN_Buffer_GetStart(buf));
|
||||
if (v==NULL) {
|
||||
if (AQH_Endpoint_GetPermissions(epDriver) & AQH_ENDPOINT_PERMS_ADDVALUE) {
|
||||
AQH_DEVICE *device;
|
||||
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Creating value \"%s\"", GWEN_Buffer_GetStart(buf));
|
||||
device=(deviceName && *deviceName)?_getOrCreateDeviceForDriver(xo, epDriver, deviceName):NULL;
|
||||
|
||||
v=AQH_Value_new();
|
||||
AQH_Value_SetDriver(v, serviceName);
|
||||
AQH_Value_SetName(v, AQH_Value_GetName(valueTemplate));
|
||||
AQH_Value_SetNameForSystem(v, GWEN_Buffer_GetStart(buf));
|
||||
AQH_Value_SetValueUnits(v, AQH_Value_GetValueUnits(valueTemplate));
|
||||
AQH_Value_SetValueType(v, AQH_Value_GetValueType(valueTemplate));
|
||||
AQH_Value_SetModality(v, AQH_Value_GetModality(valueTemplate));
|
||||
AQH_Value_SetTimestampCreation(v, (uint64_t) time(NULL));
|
||||
if (device) {
|
||||
AQH_Value_SetDeviceNameForSystem(v, AQH_Device_GetNameForSystem(device));
|
||||
AQH_Value_SetDeviceName(v, AQH_Device_GetName(device));
|
||||
}
|
||||
AQH_Storage_AddValue(xo->storage, v);
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(AQH_LOGDOMAIN, "No permissions to create value \"%s\"", GWEN_Buffer_GetStart(buf));
|
||||
GWEN_Buffer_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
GWEN_Buffer_free(buf);
|
||||
return v;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQH_DEVICE *_getOrCreateDeviceForDriver(AQHOME_SERVER *xo, AQH_OBJECT *epDriver, const char *deviceName)
|
||||
{
|
||||
const char *serviceName;
|
||||
AQH_DEVICE *device;
|
||||
GWEN_BUFFER *buf;
|
||||
|
||||
serviceName=AQH_Endpoint_GetServiceName(epDriver);
|
||||
|
||||
buf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||
GWEN_Buffer_AppendArgs(buf, "%s/%s", (serviceName && *serviceName)?serviceName:"unknown", deviceName);
|
||||
|
||||
device=AQH_Storage_GetDeviceByNameForSystem(xo->storage, GWEN_Buffer_GetStart(buf));
|
||||
if (device==NULL) {
|
||||
if (AQH_Endpoint_GetPermissions(epDriver) & AQH_ENDPOINT_PERMS_ADDDEVICE) {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Creating device \"%s\"", GWEN_Buffer_GetStart(buf));
|
||||
device=AQH_Device_new();
|
||||
AQH_Device_SetDriver(device, serviceName);
|
||||
AQH_Device_SetName(device, deviceName);
|
||||
AQH_Device_SetNameForSystem(device, GWEN_Buffer_GetStart(buf));
|
||||
AQH_Device_SetTimestampCreation(device, (uint64_t) time(NULL));
|
||||
AQH_Storage_AddDevice(xo->storage, device);
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(AQH_LOGDOMAIN, "No permissions to create device \"%s\"", GWEN_Buffer_GetStart(buf));
|
||||
GWEN_Buffer_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
GWEN_Buffer_free(buf);
|
||||
return device;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
* helper functions
|
||||
* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
*/
|
||||
|
||||
int _createPidFile(const char *pidFilename)
|
||||
{
|
||||
FILE *f;
|
||||
int pidfd;
|
||||
|
||||
if (remove(pidFilename)==0) {
|
||||
DBG_ERROR(0, "Old PID file existed, removed. (Unclean shutdown?)");
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
pidfd = open(pidFilename, O_EXCL|O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
||||
if (pidfd < 0) {
|
||||
DBG_ERROR(NULL, "Could not create PID file \"%s\" (%s), aborting.", pidFilename, strerror(errno));
|
||||
return GWEN_ERROR_IO;
|
||||
}
|
||||
|
||||
f = fdopen(pidfd, "w");
|
||||
#else /* HAVE_STAT_H */
|
||||
f=fopen(pidFilename,"w+");
|
||||
#endif /* HAVE_STAT_H */
|
||||
|
||||
/* write pid */
|
||||
#ifdef HAVE_GETPID
|
||||
fprintf(f,"%d\n",getpid());
|
||||
#else
|
||||
fprintf(f,"-1\n");
|
||||
#endif
|
||||
if (fclose(f)) {
|
||||
DBG_ERROR(0, "Could not close PID file \"%s\" (%s), aborting.", pidFilename, strerror(errno));
|
||||
return GWEN_ERROR_IO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs)
|
||||
{
|
||||
int rv;
|
||||
const GWEN_ARGS args[]= {
|
||||
{
|
||||
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
|
||||
GWEN_ArgsType_Char, /* type */
|
||||
"loglevel", /* name */
|
||||
0, /* minnum */
|
||||
1, /* maxnum */
|
||||
"L", /* short option */
|
||||
"loglevel", /* long option */
|
||||
I18S("Specify loglevel"), /* short description */
|
||||
I18S("Specify loglevel") /* long description */
|
||||
},
|
||||
{
|
||||
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
|
||||
GWEN_ArgsType_Char, /* type */
|
||||
"tcpAddress", /* name */
|
||||
0, /* minnum */
|
||||
1, /* maxnum */
|
||||
"t", /* short option */
|
||||
"tcpaddress", /* long option */
|
||||
I18S("Specify the TCP address to listen on (disabled if missing)"),
|
||||
I18S("Specify the TCP address to listen on (disabled if missing)")
|
||||
},
|
||||
{
|
||||
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
|
||||
GWEN_ArgsType_Int, /* type */
|
||||
"tcpPort", /* name */
|
||||
0, /* minnum */
|
||||
1, /* maxnum */
|
||||
"P", /* short option */
|
||||
"tcpport", /* long option */
|
||||
I18S("Specify the TCP port to listen on"),
|
||||
I18S("Specify the TCP port to listen on")
|
||||
},
|
||||
{
|
||||
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
|
||||
GWEN_ArgsType_Char, /* type */
|
||||
"datafolder", /* name */
|
||||
0, /* minnum */
|
||||
1, /* maxnum */
|
||||
NULL, /* short option */
|
||||
"datafolder", /* long option */
|
||||
I18S("Folder where data files are stored"),
|
||||
I18S("Folder where data files are stored")
|
||||
},
|
||||
{
|
||||
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
|
||||
GWEN_ArgsType_Char, /* type */
|
||||
"pidfile", /* name */
|
||||
0, /* minnum */
|
||||
1, /* maxnum */
|
||||
"p", /* short option */
|
||||
"pidfile", /* long option */
|
||||
I18S("Specify the PID file"),
|
||||
I18S("Specify the PID file")
|
||||
},
|
||||
{
|
||||
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
|
||||
GWEN_ArgsType_Int, /* type */
|
||||
"timeout", /* name */
|
||||
0, /* minnum */
|
||||
1, /* maxnum */
|
||||
"T", /* short option */
|
||||
"timeout", /* long option */
|
||||
I18S("Specify timeout in second (default: no timeout)"),
|
||||
I18S("Specify timeout in second (default: no timeout)")
|
||||
},
|
||||
{
|
||||
GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST, /* flags */
|
||||
GWEN_ArgsType_Int, /* type */
|
||||
"help", /* name */
|
||||
0, /* minnum */
|
||||
0, /* maxnum */
|
||||
"h", /* short option */
|
||||
"help",
|
||||
I18S("Show this help screen."),
|
||||
I18S("Show this help screen.")
|
||||
}
|
||||
};
|
||||
|
||||
rv=GWEN_Args_Check(argc, argv, 1, 0, args, dbArgs);
|
||||
if (rv==GWEN_ARGS_RESULT_ERROR) {
|
||||
fprintf(stderr, "ERROR: Could not parse arguments main\n");
|
||||
return GWEN_ERROR_INVALID;
|
||||
}
|
||||
else if (rv==GWEN_ARGS_RESULT_HELP) {
|
||||
GWEN_BUFFER *ubuf;
|
||||
|
||||
ubuf=GWEN_Buffer_new(0, 1024, 0, 1);
|
||||
GWEN_Buffer_AppendArgs(ubuf,
|
||||
I18N("This is version %s.\nUsage: %s [OPTIONS]\n\nOptions:\n"),
|
||||
AQHOME_VERSION_STRING,
|
||||
argv[0]);
|
||||
if (GWEN_Args_Usage(args, ubuf, GWEN_ArgsOutType_Txt)) {
|
||||
fprintf(stderr, "ERROR: Could not create help string\n");
|
||||
return 1;
|
||||
}
|
||||
GWEN_Buffer_AppendString(ubuf, "\n");
|
||||
|
||||
fprintf(stdout, "%s\n", GWEN_Buffer_GetStart(ubuf));
|
||||
GWEN_Buffer_free(ubuf);
|
||||
return GWEN_ERROR_CLOSE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
59
apps/aqhome-data/server.h
Normal file
59
apps/aqhome-data/server.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_DATA_SERVER_H
|
||||
#define AQHOME_DATA_SERVER_H
|
||||
|
||||
|
||||
#include <aqhome/data/storage.h>
|
||||
#include <aqhome/events2/object.h>
|
||||
#include <aqhome/ipc2/msgrequest.h>
|
||||
|
||||
|
||||
#define AQH_ENDPOINT_PERMS_LISTVALUES 0x0001
|
||||
#define AQH_ENDPOINT_PERMS_READVALUE 0x0002
|
||||
#define AQH_ENDPOINT_PERMS_ADDVALUE 0x0004
|
||||
|
||||
#define AQH_ENDPOINT_PERMS_LISTDATA 0x0010
|
||||
#define AQH_ENDPOINT_PERMS_READDATA 0x0020
|
||||
#define AQH_ENDPOINT_PERMS_ADDDATA 0x0040
|
||||
#define AQH_ENDPOINT_PERMS_SETDATA 0x0080
|
||||
|
||||
#define AQH_ENDPOINT_PERMS_LISTDEVICES 0x0100
|
||||
#define AQH_ENDPOINT_PERMS_READDEVICE 0x0200
|
||||
#define AQH_ENDPOINT_PERMS_ADDDEVICE 0x0400
|
||||
#define AQH_ENDPOINT_PERMS_MODDEVICE 0x0800
|
||||
|
||||
|
||||
|
||||
AQH_OBJECT *AqHomeDataServer_new(AQH_EVENT_LOOP *eventLoop);
|
||||
int AqHomeDataServer_Init(AQH_OBJECT *o, int argc, char **argv);
|
||||
|
||||
int AqHomeDataServer_GetTimeout(const AQH_OBJECT *o);
|
||||
int AqHomeDataServer_GetClientNum(const AQH_OBJECT *o);
|
||||
|
||||
void AqHomeDataServer_CleanupClients(AQH_OBJECT *o);
|
||||
void AqHomeDataServer_HandleClientMsgs(AQH_OBJECT *o);
|
||||
AQH_OBJECT *AqHomeDataServer_GetIpcEndpointByServiceName(const AQH_OBJECT *o, const char *serviceName);
|
||||
void AqHomeDataServer_SendResponseResultToEndpoint(AQH_OBJECT *ep, uint32_t refMsgId, int result);
|
||||
|
||||
AQH_MSG_REQUEST *AqHomeDataServer_GetRequestTree(const AQH_OBJECT *o);
|
||||
void AqHomeDataServer_AddRequestToTree(AQH_OBJECT *o, AQH_MSG_REQUEST *rq);
|
||||
void AqHomeDataServer_CleanupRequests(AQH_OBJECT *o);
|
||||
|
||||
int AqHomeDataServer_LockStorage(AQH_OBJECT *o);
|
||||
int AqHomeDataServer_UnlockStorage(AQH_OBJECT *o);
|
||||
int AqHomeDataServer_WriteStorageIfChanged(AQH_OBJECT *o);
|
||||
AQH_VALUE *AqHomeDataServer_GetOrCreateValueForDriverWithTemplate(AQH_OBJECT *o, AQH_OBJECT *epDriver, const AQH_VALUE *valueTemplate);
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
49
apps/aqhome-data/server_p.h
Normal file
49
apps/aqhome-data/server_p.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_DATA_SERVER_P_H
|
||||
#define AQHOME_DATA_SERVER_P_H
|
||||
|
||||
|
||||
#include "./server.h"
|
||||
|
||||
#include <aqhome/events2/object.h>
|
||||
|
||||
#include <gwenhywfar/mutex.h>
|
||||
|
||||
|
||||
#define AQHOME_DATA_DEFAULT_PIDFILE "/var/run/aqhome-data.pid"
|
||||
#define AQHOME_DATA_DEFAULT_DATADIR "/var/lib/aqhome-data/data"
|
||||
#define AQHOME_DATA_DEFAULT_IPC_PORT 45456
|
||||
|
||||
#define AQHOME_DATA_STATEFILENAME "statefile"
|
||||
|
||||
|
||||
|
||||
typedef struct AQHOME_SERVER AQHOME_SERVER;
|
||||
struct AQHOME_SERVER {
|
||||
AQH_OBJECT *ipcServer;
|
||||
AQH_OBJECT_LIST *tcpClientList;
|
||||
|
||||
AQH_MSG_REQUEST *requestTree;
|
||||
|
||||
GWEN_DB_NODE *dbArgs;
|
||||
AQH_STORAGE *storage;
|
||||
char *pidFile;
|
||||
int timeout; /* timeout for run e.g. inside valgrind */
|
||||
GWEN_MUTEX *storageMutex;
|
||||
};
|
||||
|
||||
|
||||
AQHOME_SERVER *AqHomeDataServer_GetServerData(const AQH_OBJECT *o);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
81
apps/aqhome-mqttlog/0BUILD
Normal file
81
apps/aqhome-mqttlog/0BUILD
Normal file
@@ -0,0 +1,81 @@
|
||||
<?xml?>
|
||||
|
||||
<gwbuild>
|
||||
|
||||
<target type="Program" name="aqhome-mqttlog" install="$(sbindir)" >
|
||||
|
||||
<includes type="c" >
|
||||
$(gwenhywfar_cflags)
|
||||
-I$(topsrcdir)
|
||||
-I$(topbuilddir)
|
||||
-I$(topsrcdir)/apps
|
||||
-I$(topbuilddir)/apps
|
||||
-I$(builddir)
|
||||
-I$(srcdir)
|
||||
</includes>
|
||||
|
||||
<includes type="tm2" >
|
||||
--include=$(builddir)
|
||||
--include=$(srcdir)
|
||||
</includes>
|
||||
|
||||
<setVar name="local/cflags">$(visibility_cflags)</setVar>
|
||||
|
||||
<setVar name="tm2flags" >
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/typefiles" >
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_sources" >
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_headers_pub">
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_headers_priv" >
|
||||
</setVar>
|
||||
|
||||
<headers dist="true" >
|
||||
xmlread.h
|
||||
xmlwrite.h
|
||||
server.h
|
||||
server_p.h
|
||||
s_publish.h
|
||||
s_setdata.h
|
||||
</headers>
|
||||
|
||||
<sources>
|
||||
$(local/typefiles)
|
||||
main.c
|
||||
xmlread.c
|
||||
xmlwrite.c
|
||||
server.c
|
||||
s_publish.c
|
||||
s_setdata.c
|
||||
</sources>
|
||||
|
||||
<useTargets>
|
||||
aqhome
|
||||
aqhmqtt_types
|
||||
</useTargets>
|
||||
|
||||
<libraries>
|
||||
$(gwenhywfar_libs)
|
||||
</libraries>
|
||||
|
||||
<subdirs>
|
||||
types
|
||||
</subdirs>
|
||||
|
||||
|
||||
<extradist>
|
||||
</extradist>
|
||||
|
||||
|
||||
</target>
|
||||
|
||||
|
||||
</gwbuild>
|
||||
|
||||
|
||||
282
apps/aqhome-mqttlog/main.c
Normal file
282
apps/aqhome-mqttlog/main.c
Normal file
@@ -0,0 +1,282 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "./server.h"
|
||||
|
||||
#include "aqhome/aqhome.h"
|
||||
|
||||
#include <gwenhywfar/gwenhywfar.h>
|
||||
#include <gwenhywfar/args.h>
|
||||
#include <gwenhywfar/logger.h>
|
||||
#include <gwenhywfar/db.h>
|
||||
#include <gwenhywfar/debug.h>
|
||||
#include <gwenhywfar/cgui.h>
|
||||
#include <gwenhywfar/text.h>
|
||||
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
# include <signal.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* defines
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define I18N(msg) msg
|
||||
#define I18S(msg) msg
|
||||
|
||||
#define CONNCHECK_INTERVAL_IN_SECS 10
|
||||
#define PING_INTERVAL_IN_SECS 120
|
||||
#define SAVE_INTERVAL_IN_SECS 60
|
||||
|
||||
#define FULL_DEBUG
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static void _runService(AQH_OBJECT *aqh, AQH_EVENT_LOOP *eventLoop);
|
||||
static int _diffInSeconds(time_t t1, time_t t0);
|
||||
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
static int _setSignalHandlers(void);
|
||||
static int _setupSigAction(struct sigaction *sa, int sig);
|
||||
static void _signalHandler(int s);
|
||||
static struct sigaction saINT,saTERM, saHUP, saTSTP, saCONT, saPIPE;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* static vars
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static int stopService=0;
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* implementations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
AQH_EVENT_LOOP *eventLoop;
|
||||
AQH_OBJECT *aqh;
|
||||
int rv;
|
||||
GWEN_GUI *gui;
|
||||
|
||||
rv=GWEN_Init();
|
||||
if (rv) {
|
||||
fprintf(stderr, "ERROR: Unable to init Gwen.\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
GWEN_Logger_Open(0, "aqhome-mqttlog", 0, GWEN_LoggerType_Console, GWEN_LoggerFacility_User);
|
||||
//GWEN_Logger_SetLevel(0, GWEN_LoggerLevel_Warning);
|
||||
GWEN_Logger_SetLevel(0, GWEN_LoggerLevel_Notice);
|
||||
|
||||
rv=_setSignalHandlers();
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "here (%d)", rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv=AQH_Init();
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "here (%d)", rv);
|
||||
return 2;
|
||||
}
|
||||
|
||||
gui=GWEN_Gui_CGui_new();
|
||||
GWEN_Gui_SetGui(gui);
|
||||
|
||||
eventLoop=AQH_EventLoop_new();
|
||||
|
||||
aqh=AQH_MqttLogServer_new(eventLoop);
|
||||
rv=AQH_MqttLogServer_Init(aqh, argc, argv);
|
||||
if (rv<0) {
|
||||
if (rv==GWEN_ERROR_CLOSE)
|
||||
return 1;
|
||||
DBG_INFO(NULL, "here (%d)", rv);
|
||||
return 2;
|
||||
}
|
||||
|
||||
_runService(aqh, eventLoop);
|
||||
|
||||
|
||||
AQH_MqttLogServer_Fini(aqh);
|
||||
AQH_Object_free(aqh);
|
||||
|
||||
GWEN_Gui_SetGui(NULL);
|
||||
GWEN_Gui_free(gui);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _runService(AQH_OBJECT *aqh, AQH_EVENT_LOOP *eventLoop)
|
||||
{
|
||||
time_t timeStart;
|
||||
int timeout;
|
||||
time_t timeLastConnCheck;
|
||||
time_t timeLastSave;
|
||||
time_t timeLastPingSend;
|
||||
int rv;
|
||||
|
||||
timeout=AQH_MqttLogServer_GetTimeout(aqh);
|
||||
timeStart=time(NULL);
|
||||
timeLastConnCheck=time(NULL);
|
||||
timeLastSave=time(NULL);
|
||||
timeLastPingSend=time(NULL);
|
||||
|
||||
while(!stopService) {
|
||||
time_t now;
|
||||
|
||||
AQH_EventLoop_Run(eventLoop, 2000);
|
||||
AQH_MqttLogServer_HandleMqttMsgs(aqh);
|
||||
AQH_MqttLogServer_HandleBrokerMsgs(aqh);
|
||||
|
||||
now=time(NULL);
|
||||
|
||||
if (_diffInSeconds(now, timeLastConnCheck)>CONNCHECK_INTERVAL_IN_SECS) {
|
||||
DBG_INFO(NULL, "Check connections");
|
||||
AQH_MqttLogServer_CheckBrokerConnection(aqh);
|
||||
AQH_MqttLogServer_CheckMqttConnection(aqh);
|
||||
timeLastConnCheck=now;
|
||||
}
|
||||
|
||||
if (_diffInSeconds(now, timeLastPingSend)>PING_INTERVAL_IN_SECS) {
|
||||
rv=AQH_MqttLogServer_SendPing(aqh);
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "Error sending PING");
|
||||
}
|
||||
timeLastPingSend=time(NULL);
|
||||
}
|
||||
|
||||
if (_diffInSeconds(now, timeLastSave)>SAVE_INTERVAL_IN_SECS) {
|
||||
DBG_INFO(NULL, "Writing device files");
|
||||
rv=AQH_MqttLogServer_SaveRuntimeDeviceFiles(aqh);
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "Error writing device file");
|
||||
}
|
||||
timeLastSave=time(NULL);
|
||||
}
|
||||
|
||||
if (timeout && (_diffInSeconds(now, timeStart)>timeout)) {
|
||||
DBG_INFO(NULL, "Timeout");
|
||||
break;
|
||||
}
|
||||
} /* while */
|
||||
DBG_ERROR(NULL, "Leaving server");
|
||||
|
||||
rv=AQH_MqttLogServer_SaveRuntimeDeviceFiles(aqh);
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "Error writing runtime data");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _setSignalHandlers(void)
|
||||
{
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
int rv;
|
||||
|
||||
rv=_setupSigAction(&saINT, SIGINT);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
rv=_setupSigAction(&saTERM, SIGTERM);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
rv=_setupSigAction(&saHUP, SIGHUP);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
rv=_setupSigAction(&saPIPE, SIGPIPE);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
# ifdef SIGTSTP
|
||||
rv=_setupSigAction(&saTSTP, SIGTSTP);
|
||||
if (rv)
|
||||
return rv;
|
||||
# endif
|
||||
|
||||
# ifdef SIGCONT
|
||||
rv=_setupSigAction(&saCONT, SIGCONT);
|
||||
if (rv)
|
||||
return rv;
|
||||
# endif
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _setupSigAction(struct sigaction *sa, int sig)
|
||||
{
|
||||
sa->sa_handler=_signalHandler;
|
||||
sigemptyset(&sa->sa_mask);
|
||||
sa->sa_flags=0;
|
||||
if (sigaction(sig, sa, 0)) {
|
||||
DBG_ERROR(NULL, "Could not setup signal handler for signal %d", sig);
|
||||
return GWEN_ERROR_IO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _signalHandler(int s)
|
||||
{
|
||||
switch(s) {
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
case SIGHUP:
|
||||
DBG_WARN(0, "Received signal %d, stopping service in next loop.",s);
|
||||
stopService=1;
|
||||
break;
|
||||
case SIGPIPE:
|
||||
DBG_WARN(0, "Received PIPE signal");
|
||||
break;
|
||||
default:
|
||||
DBG_WARN(0, "Unknown signal %d",s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _diffInSeconds(time_t t1, time_t t0)
|
||||
{
|
||||
return t1-t0;
|
||||
}
|
||||
|
||||
449
apps/aqhome-mqttlog/s_publish.c
Normal file
449
apps/aqhome-mqttlog/s_publish.c
Normal file
@@ -0,0 +1,449 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "./s_publish.h"
|
||||
#include "./server_p.h"
|
||||
|
||||
#include <aqhome/aqhome.h>
|
||||
#include <aqhome/msg/ipc/data/m_ipcd.h>
|
||||
#include <aqhome/msg/ipc/data/m_ipcd_multidata.h>
|
||||
#include <aqhome/msg/ipc/data/m_ipcd_values.h>
|
||||
#include <aqhome/ipc2/endpoint.h>
|
||||
#include <aqhome/msg/mqtt/m_mqtt_publish.h>
|
||||
|
||||
#include <gwenhywfar/json_read.h>
|
||||
#include <gwenhywfar/text.h>
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static int _handlePublish(AQH_OBJECT *o, const char *rcvdTopic, const char *rcvdValue);
|
||||
static void _handleNumTopic(AQH_MQTTLOG_SERVER *xo, AQHMQTT_DEVICE *device,
|
||||
AQHMQTT_TOPIC *topic, const char *rcvdValue);
|
||||
static void _handleJsonTopic(AQH_MQTTLOG_SERVER *xo, AQHMQTT_DEVICE *device,
|
||||
AQHMQTT_TOPIC *topic, const char *rcvdValue);
|
||||
static void _sendMessage(AQH_MQTTLOG_SERVER *xo, const AQHMQTT_DEVICE *device, const AQHMQTT_VALUE *value, const char *rcvdValue);
|
||||
static void _announceDeviceToBroker(AQH_MQTTLOG_SERVER *xo, const AQHMQTT_DEVICE *device);
|
||||
static void _sendAnnounceValueMessage(AQH_MQTTLOG_SERVER *xo, const AQHMQTT_DEVICE *device, const AQHMQTT_VALUE *value);
|
||||
static AQH_VALUE *_mkMessageValue(const AQHMQTT_DEVICE *device, const AQHMQTT_VALUE *value);
|
||||
static int _mqttValueTypeMessageValueType(int t);
|
||||
static int _registerNewDeviceForTopic(AQH_MQTTLOG_SERVER *xo, const char *rcvdTopic);
|
||||
static AQHMQTT_TOPIC *_findMaskMatchingTopic(AQHMQTT_TOPIC_LIST *topicList, const char *rcvdTopic, int dir);
|
||||
static AQHMQTT_TOPIC *_findTopicMatchingTopic(AQHMQTT_TOPIC_LIST *topicList, const char *rcvdTopic, int dir);
|
||||
static GWEN_BUFFER *_extractDeviceId(const AQHMQTT_TOPIC *topic, const char *rcvdTopic);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* code
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void AQH_MqttLogServer_HandlePublishMsg(AQH_OBJECT *o, GWEN_UNUSED AQH_OBJECT *ep, const AQH_MESSAGE *msg)
|
||||
{
|
||||
if (o && msg) {
|
||||
AQH_MQTTLOG_SERVER *xo;
|
||||
|
||||
xo=AQH_MqttLogServer_GetServerData(o);
|
||||
if (xo && xo->registeredDeviceList) {
|
||||
char *topic;
|
||||
char *value;
|
||||
|
||||
topic=AQH_MqttMessagePublish_ExtractTopic(msg);
|
||||
value=AQH_MqttMessagePublish_ExtractValue(msg);
|
||||
|
||||
if (topic && value) {
|
||||
int rv;
|
||||
|
||||
rv=_handlePublish(o, topic, value);
|
||||
if (rv!=1) {
|
||||
DBG_INFO(NULL, "New topic \"%s\", trying to register", topic);
|
||||
rv=_registerNewDeviceForTopic(xo, topic);
|
||||
if (rv==1) {
|
||||
rv=_handlePublish(o, topic, value);
|
||||
if (rv!=1) {
|
||||
DBG_ERROR(NULL, "Topic \"%s\" still not handled, SNH!", topic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Either topic or value missing in PUBLISH msg");
|
||||
}
|
||||
free(value);
|
||||
free(topic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _handlePublish(AQH_OBJECT *o, const char *rcvdTopic, const char *rcvdValue)
|
||||
{
|
||||
if (o && rcvdTopic && *rcvdTopic) {
|
||||
AQH_MQTTLOG_SERVER *xo;
|
||||
|
||||
xo=AQH_MqttLogServer_GetServerData(o);
|
||||
if (xo && xo->registeredDeviceList) {
|
||||
AQHMQTT_DEVICE *device;
|
||||
|
||||
device=AQHMQTT_Device_List_First(xo->registeredDeviceList);
|
||||
while(device) {
|
||||
AQHMQTT_TOPIC_LIST *topicList;
|
||||
const char *sDeviceName;
|
||||
const char *sDeviceId;
|
||||
|
||||
sDeviceName=AQHMQTT_Device_GetName(device);
|
||||
sDeviceId=AQHMQTT_Device_GetId(device);
|
||||
|
||||
topicList=AQHMQTT_Device_GetTopicList(device);
|
||||
if (topicList) {
|
||||
AQHMQTT_TOPIC *topic;
|
||||
|
||||
topic=_findTopicMatchingTopic(topicList, rcvdTopic, AQHMQTT_TopicDir_In);
|
||||
#if 0
|
||||
if (topic==NULL) {
|
||||
topic=_findMaskMatchingTopic(topicList, rcvdTopic, AQHMQTT_TopicDir_In);
|
||||
if (topic)
|
||||
AQHMQTT_Topic_SetTopic(topic, rcvdTopic);
|
||||
}
|
||||
#endif
|
||||
if (topic) {
|
||||
DBG_INFO(AQH_LOGDOMAIN,
|
||||
"Handling topic \"%s\" for device type %s (id: %s)",
|
||||
rcvdTopic,
|
||||
sDeviceName, sDeviceId?sDeviceId:"<no id>");
|
||||
if (AQHMQTT_Topic_GetTopicType(topic)==AQHMQTT_TopicType_Json)
|
||||
_handleJsonTopic(xo, device, topic, rcvdValue);
|
||||
else
|
||||
_handleNumTopic(xo, device, topic, rcvdValue);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
device=AQHMQTT_Device_List_Next(device);
|
||||
}
|
||||
DBG_INFO(AQH_LOGDOMAIN, "ignoring topic \"%s\"", rcvdTopic);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _handleNumTopic(AQH_MQTTLOG_SERVER *xo, AQHMQTT_DEVICE *device,
|
||||
AQHMQTT_TOPIC *topic, const char *rcvdValue)
|
||||
{
|
||||
AQHMQTT_VALUE_LIST *valueList;
|
||||
|
||||
valueList=AQHMQTT_Topic_GetValueList(topic);
|
||||
if (valueList)
|
||||
_sendMessage(xo, device, AQHMQTT_Value_List_First(valueList), rcvdValue);
|
||||
else {
|
||||
DBG_INFO(NULL, "No value list in device \"%s\"", AQHMQTT_Device_GetId(device));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _handleJsonTopic(AQH_MQTTLOG_SERVER *xo, AQHMQTT_DEVICE *device,
|
||||
AQHMQTT_TOPIC *topic, const char *rcvdValue)
|
||||
{
|
||||
GWEN_JSON_ELEM *jeRoot;
|
||||
|
||||
jeRoot=GWEN_JsonElement_fromString(rcvdValue);
|
||||
if (jeRoot==NULL) {
|
||||
DBG_INFO(NULL, "Could not parse JSON value: %s", rcvdValue);
|
||||
}
|
||||
else {
|
||||
AQHMQTT_VALUE_LIST *valueList;
|
||||
|
||||
valueList=AQHMQTT_Topic_GetValueList(topic);
|
||||
if (valueList) {
|
||||
AQHMQTT_VALUE *value;
|
||||
|
||||
value=AQHMQTT_Value_List_First(valueList);
|
||||
while(value) {
|
||||
const char *path;
|
||||
|
||||
path=AQHMQTT_Value_GetPath(value);
|
||||
if (path && *path) {
|
||||
GWEN_JSON_ELEM *je;
|
||||
|
||||
je=GWEN_JsonElement_GetElementByPath(jeRoot, path, 0);
|
||||
if (je) {
|
||||
const char *s;
|
||||
|
||||
s=GWEN_JsonElement_GetData(je);
|
||||
if (s && *s)
|
||||
_sendMessage(xo, device, value, s);
|
||||
}
|
||||
}
|
||||
value=AQHMQTT_Value_List_Next(value);
|
||||
} /* while */
|
||||
}
|
||||
GWEN_JsonElement_free(jeRoot);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _sendMessage(AQH_MQTTLOG_SERVER *xo, const AQHMQTT_DEVICE *device, const AQHMQTT_VALUE *value, const char *rcvdValue)
|
||||
{
|
||||
int rv;
|
||||
double f;
|
||||
const char *deviceName;
|
||||
|
||||
deviceName=AQHMQTT_Device_GetId(device);
|
||||
rv=GWEN_Text_StringToDouble(rcvdValue, &f);
|
||||
if (rv<0) {
|
||||
DBG_ERROR(NULL, "Invalid value received from MQTT server (%s)", rcvdValue?rcvdValue:"<empty>");
|
||||
}
|
||||
else {
|
||||
uint64_t now;
|
||||
AQH_VALUE *msgValue;
|
||||
|
||||
now=(uint64_t) time(NULL);
|
||||
msgValue=_mkMessageValue(device, value);
|
||||
if (xo->brokerEndpoint) {
|
||||
AQH_MESSAGE *pubMsg;
|
||||
|
||||
pubMsg=AQH_IpcdMessageMultiData_newForOne(AQH_MSGTYPE_IPC_DATA_UPDATEDATA,
|
||||
AQH_Endpoint_GetNextMessageId(xo->brokerEndpoint), 0,
|
||||
msgValue, now, f);
|
||||
DBG_INFO(AQH_LOGDOMAIN, "BROKER UPDATE_DATA %s/%s: %f",
|
||||
deviceName?deviceName:"<no device name>",
|
||||
AQH_Value_GetName(msgValue), f);
|
||||
AQH_Endpoint_AddMsgOut(xo->brokerEndpoint, pubMsg);
|
||||
}
|
||||
else {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Skipping BROKER UPDATE_DATA %s/%s: %f",
|
||||
deviceName?deviceName:"<no device name>",
|
||||
AQH_Value_GetName(msgValue), f);
|
||||
}
|
||||
AQH_Value_free(msgValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _announceDeviceToBroker(AQH_MQTTLOG_SERVER *xo, const AQHMQTT_DEVICE *device)
|
||||
{
|
||||
AQHMQTT_TOPIC_LIST *topicList;
|
||||
|
||||
topicList=AQHMQTT_Device_GetTopicList(device);
|
||||
if (topicList) {
|
||||
AQHMQTT_TOPIC *topic;
|
||||
|
||||
topic=AQHMQTT_Topic_List_First(topicList);
|
||||
while(topic) {
|
||||
AQHMQTT_VALUE_LIST *valueList;
|
||||
|
||||
valueList=AQHMQTT_Topic_GetValueList(topic);
|
||||
if (valueList) {
|
||||
const AQHMQTT_VALUE *value;
|
||||
|
||||
value=AQHMQTT_Value_List_First(valueList);
|
||||
while(value) {
|
||||
_sendAnnounceValueMessage(xo, device, value);
|
||||
value=AQHMQTT_Value_List_Next(value);
|
||||
}
|
||||
}
|
||||
|
||||
topic=AQHMQTT_Topic_List_Next(topic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _sendAnnounceValueMessage(AQH_MQTTLOG_SERVER *xo, const AQHMQTT_DEVICE *device, const AQHMQTT_VALUE *value)
|
||||
{
|
||||
AQH_MESSAGE *pubMsg;
|
||||
AQH_VALUE *msgValue;
|
||||
|
||||
msgValue=_mkMessageValue(device, value);
|
||||
if (xo->brokerEndpoint) {
|
||||
pubMsg=AQH_IpcdMessageValues_newForOne(AQH_MSGTYPE_IPC_DATA_ANNOUNCEVALUE,
|
||||
AQH_Endpoint_GetNextMessageId(xo->brokerEndpoint), 0,
|
||||
0, msgValue);
|
||||
if (pubMsg) {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "BROKER ANNOUNCE_VALUE %s", AQH_Value_GetName(msgValue));
|
||||
AQH_Endpoint_AddMsgOut(xo->brokerEndpoint, pubMsg);
|
||||
}
|
||||
AQH_Value_free(msgValue);
|
||||
}
|
||||
else {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Ignoring BROKER ANNOUNCE_VALUE %s", AQH_Value_GetName(msgValue));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQH_VALUE *_mkMessageValue(const AQHMQTT_DEVICE *device, const AQHMQTT_VALUE *value)
|
||||
{
|
||||
AQH_VALUE *msgValue;
|
||||
|
||||
msgValue=AQH_Value_new();
|
||||
AQH_Value_SetDeviceName(msgValue, AQHMQTT_Device_GetId(device));
|
||||
AQH_Value_SetName(msgValue, AQHMQTT_Value_GetName(value));
|
||||
AQH_Value_SetValueUnits(msgValue, AQHMQTT_Value_GetValueUnits(value));
|
||||
AQH_Value_SetValueType(msgValue, _mqttValueTypeMessageValueType(AQHMQTT_Value_GetValueType(value)));
|
||||
return msgValue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _mqttValueTypeMessageValueType(int t)
|
||||
{
|
||||
switch(t){
|
||||
case AQHMQTT_ValueType_Sensor: return AQH_ValueType_Sensor;
|
||||
case AQHMQTT_ValueType_Actor: return AQH_ValueType_Actor;
|
||||
default: break;
|
||||
}
|
||||
DBG_ERROR(AQH_LOGDOMAIN, "Invalid mqtt value type %d", t);
|
||||
return AQH_ValueType_Sensor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _registerNewDeviceForTopic(AQH_MQTTLOG_SERVER *xo, const char *rcvdTopic)
|
||||
{
|
||||
if (rcvdTopic && *rcvdTopic) {
|
||||
if (xo->availableDeviceList) {
|
||||
AQHMQTT_DEVICE *device;
|
||||
|
||||
device=AQHMQTT_Device_List_First(xo->availableDeviceList);
|
||||
while(device) {
|
||||
AQHMQTT_TOPIC_LIST *topicList;
|
||||
|
||||
topicList=AQHMQTT_Device_GetTopicList(device);
|
||||
if (topicList) {
|
||||
AQHMQTT_TOPIC *topic;
|
||||
|
||||
topic=_findMaskMatchingTopic(topicList, rcvdTopic, AQHMQTT_TopicDir_In);
|
||||
if (topic) {
|
||||
GWEN_BUFFER *buf;
|
||||
|
||||
buf=_extractDeviceId(topic, rcvdTopic);
|
||||
if (buf) {
|
||||
AQHMQTT_DEVICE *newDevice;
|
||||
|
||||
newDevice=AQHMQTT_Device_dup(device);
|
||||
AQHMQTT_Device_SetId(newDevice, GWEN_Buffer_GetStart(buf));
|
||||
topic=_findMaskMatchingTopic(AQHMQTT_Device_GetTopicList(newDevice), rcvdTopic, AQHMQTT_TopicDir_In);
|
||||
AQHMQTT_Topic_SetTopic(topic, rcvdTopic);
|
||||
if (xo->registeredDeviceList==NULL)
|
||||
xo->registeredDeviceList=AQHMQTT_Device_List_new();
|
||||
DBG_ERROR(NULL, "Registered device \"%s\" (%s)", AQHMQTT_Device_GetId(newDevice), AQHMQTT_Device_GetName(newDevice));
|
||||
AQHMQTT_Device_List_Add(newDevice, xo->registeredDeviceList);
|
||||
_announceDeviceToBroker(xo, newDevice);
|
||||
GWEN_Buffer_free(buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
device=AQHMQTT_Device_List_Next(device);
|
||||
}
|
||||
}
|
||||
DBG_INFO(AQH_LOGDOMAIN, "ignoring topic \"%s\"", rcvdTopic);
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHMQTT_TOPIC *_findMaskMatchingTopic(AQHMQTT_TOPIC_LIST *topicList, const char *rcvdTopic, int dir)
|
||||
{
|
||||
if (topicList) {
|
||||
AQHMQTT_TOPIC *topic;
|
||||
|
||||
topic=AQHMQTT_Topic_List_First(topicList);
|
||||
while(topic) {
|
||||
if (AQHMQTT_Topic_GetDirection(topic)==dir) {
|
||||
const char *sMask;
|
||||
|
||||
sMask=AQHMQTT_Topic_GetMask(topic);
|
||||
if (sMask && *sMask && GWEN_Text_ComparePattern(rcvdTopic, sMask, 0)!=-1) {
|
||||
/* found a matching topic */
|
||||
return topic;
|
||||
}
|
||||
}
|
||||
|
||||
topic=AQHMQTT_Topic_List_Next(topic);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHMQTT_TOPIC *_findTopicMatchingTopic(AQHMQTT_TOPIC_LIST *topicList, const char *rcvdTopic, int dir)
|
||||
{
|
||||
if (topicList) {
|
||||
AQHMQTT_TOPIC *topic;
|
||||
|
||||
topic=AQHMQTT_Topic_List_First(topicList);
|
||||
while(topic) {
|
||||
if (AQHMQTT_Topic_GetDirection(topic)==dir) {
|
||||
const char *sTopic;
|
||||
|
||||
sTopic=AQHMQTT_Topic_GetTopic(topic);
|
||||
if (sTopic && *sTopic && strcasecmp(rcvdTopic, sTopic)==0) {
|
||||
return topic;
|
||||
}
|
||||
}
|
||||
topic=AQHMQTT_Topic_List_Next(topic);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
GWEN_BUFFER *_extractDeviceId(const AQHMQTT_TOPIC *topic, const char *rcvdTopic)
|
||||
{
|
||||
const char *sBefore;
|
||||
const char *sAfter;
|
||||
|
||||
sBefore=AQHMQTT_Topic_GetBeforeId(topic);
|
||||
sAfter=AQHMQTT_Topic_GetAfterId(topic);
|
||||
|
||||
if (sBefore && *sBefore && sAfter && *sAfter) {
|
||||
GWEN_BUFFER *buf;
|
||||
int rv;
|
||||
|
||||
buf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||
GWEN_Buffer_AppendString(buf, rcvdTopic);
|
||||
rv=GWEN_Buffer_KeepTextBetweenStrings(buf, sBefore, sAfter, 1);
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "Could not extract id from [%s] (beforeId=%s, afterId=%s)", rcvdTopic, sBefore, sAfter);
|
||||
GWEN_Buffer_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
25
apps/aqhome-mqttlog/s_publish.h
Normal file
25
apps/aqhome-mqttlog/s_publish.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOMEMQTT_S_PUBLISH_H
|
||||
#define AQHOMEMQTT_S_PUBLISH_H
|
||||
|
||||
|
||||
#include "./aqhome_mqtt.h"
|
||||
|
||||
#include <aqhome/events2/object.h>
|
||||
#include <aqhome/ipc2/message.h>
|
||||
|
||||
|
||||
|
||||
void AQH_MqttLogServer_HandlePublishMsg(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
180
apps/aqhome-mqttlog/s_setdata.c
Normal file
180
apps/aqhome-mqttlog/s_setdata.c
Normal file
@@ -0,0 +1,180 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "./s_setdata.h"
|
||||
#include "./server_p.h"
|
||||
#include "aqhome/data/value.h"
|
||||
#include "aqhome/msg/ipc/data/m_ipcd_setdata.h"
|
||||
#include "aqhome/msg/mqtt/m_mqtt_publish.h"
|
||||
#include "aqhome/ipc2/endpoint.h"
|
||||
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
|
||||
#define DEBUG_DRY_RUN 1 /* don't actually set value if "1" */
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static void _sendDataForDevice(AQH_MQTTLOG_SERVER *xo, const AQHMQTT_DEVICE *device,
|
||||
const char *valueName, const char *valueData);
|
||||
static void _sendValueToMqtt(AQH_MQTTLOG_SERVER *xo, const char *deviceId,
|
||||
const AQHMQTT_TOPIC *topic, const char *valueData);
|
||||
static GWEN_BUFFER *_createBufferForTopic(const char *deviceId, const AQHMQTT_TOPIC *topic);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* implementations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void AQH_MqttLogServer_HandleSetData(AQH_OBJECT *o,
|
||||
const AQH_MESSAGE *msg,
|
||||
const GWEN_TAG16_LIST *tagList)
|
||||
{
|
||||
if (o && msg) {
|
||||
AQH_MQTTLOG_SERVER *xo;
|
||||
|
||||
xo=AQH_MqttLogServer_GetServerData(o);
|
||||
if (xo) {
|
||||
AQH_VALUE *recvdValue;
|
||||
|
||||
DBG_ERROR(NULL, "Received SETDATA request");
|
||||
recvdValue=AQH_IpcdMessageSetData_ReadValue(tagList);
|
||||
if (recvdValue) {
|
||||
const char *valueName;
|
||||
const char *deviceName;
|
||||
|
||||
valueName=recvdValue?AQH_Value_GetName(recvdValue):NULL;
|
||||
deviceName=recvdValue?AQH_Value_GetDeviceName(recvdValue):NULL;
|
||||
if (valueName && deviceName) {
|
||||
AQHMQTT_DEVICE *device;
|
||||
|
||||
device=AQH_MqttLogServer_FindRegisteredDevice(o, deviceName);
|
||||
if (device) {
|
||||
char *valueDataFreeable;
|
||||
|
||||
DBG_ERROR(NULL, "Sending data to value \"%s\" of device \"%s\"", valueName, deviceName);
|
||||
valueDataFreeable=AQH_IpcdMessageSetData_ReadData(tagList);
|
||||
_sendDataForDevice(xo, device, valueName, valueDataFreeable);
|
||||
free(valueDataFreeable);
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Device \"%s\" not found", deviceName);
|
||||
AQH_MqttLogServer_DumpRegisteredDevices(o);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Either value name or device name missing in request");
|
||||
}
|
||||
AQH_Value_free(recvdValue);
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Request does not contain a value object");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _sendDataForDevice(AQH_MQTTLOG_SERVER *xo,
|
||||
const AQHMQTT_DEVICE *device,
|
||||
const char *valueName, const char *valueData)
|
||||
{
|
||||
const char *deviceId;
|
||||
|
||||
deviceId=AQHMQTT_Device_GetId(device);
|
||||
if (deviceId && *deviceId) {
|
||||
AQHMQTT_TOPIC_LIST *topicList;
|
||||
|
||||
topicList=AQHMQTT_Device_GetTopicList(device);
|
||||
if (topicList) {
|
||||
AQHMQTT_TOPIC *topic;
|
||||
|
||||
topic=AQHMQTT_Topic_List_First(topicList);
|
||||
while(topic) {
|
||||
if (AQHMQTT_Topic_GetDirection(topic)==AQHMQTT_TopicDir_Out) {
|
||||
AQHMQTT_VALUE_LIST *valueList;
|
||||
AQHMQTT_VALUE *value;
|
||||
|
||||
valueList=AQHMQTT_Topic_GetValueList(topic);
|
||||
value=valueList?AQHMQTT_Value_List_GetByName(valueList, valueName):NULL;
|
||||
if (value) {
|
||||
/* found value, create publish msg, send */
|
||||
DBG_ERROR(NULL, "Topic \"%s\" contains value \"%s\"", AQHMQTT_Topic_GetName(topic), valueName);
|
||||
_sendValueToMqtt(xo, deviceId, topic, valueData);
|
||||
}
|
||||
} /* if out */
|
||||
topic=AQHMQTT_Topic_List_Next(topic);
|
||||
} /* while topic */
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Device has no id");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _sendValueToMqtt(AQH_MQTTLOG_SERVER *xo, const char *deviceId, const AQHMQTT_TOPIC *topic, const char *valueData)
|
||||
{
|
||||
GWEN_BUFFER *buf;
|
||||
#if !DEBUG_DRY_RUN
|
||||
AQH_MESSAGE *msgOut;
|
||||
#endif
|
||||
|
||||
buf=_createBufferForTopic(deviceId, topic);
|
||||
#if !DEBUG_DRY_RUN
|
||||
DBG_ERROR(NULL, "MQTT PUBLISH: %s = %s", GWEN_Buffer_GetStart(buf), valueData?valueData:"<empty>");
|
||||
msgOut=AQH_MqttMessagePublish_new(0, 0, GWEN_Buffer_GetStart(buf),
|
||||
(const uint8_t*) (valueData?valueData:NULL),
|
||||
valueData?strlen(valueData):0);
|
||||
if (msgOut)
|
||||
AQH_Endpoint_AddMsgOut(xo->mqttEndpoint, msgOut);
|
||||
else {
|
||||
DBG_ERROR(NULL, "Error creating message");
|
||||
}
|
||||
#else
|
||||
DBG_ERROR(NULL, "Would MQTT PUBLISH: %s = %s", GWEN_Buffer_GetStart(buf), valueData?valueData:"<empty>");
|
||||
#endif
|
||||
GWEN_Buffer_free(buf);
|
||||
}
|
||||
|
||||
|
||||
|
||||
GWEN_BUFFER *_createBufferForTopic(const char *deviceId, const AQHMQTT_TOPIC *topic)
|
||||
{
|
||||
GWEN_BUFFER *buf;
|
||||
const char *s;
|
||||
|
||||
buf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||
s=AQHMQTT_Topic_GetBeforeId(topic);
|
||||
if (s && *s)
|
||||
GWEN_Buffer_AppendString(buf, s);
|
||||
GWEN_Buffer_AppendString(buf, deviceId);
|
||||
s=AQHMQTT_Topic_GetAfterId(topic);
|
||||
if (s && *s)
|
||||
GWEN_Buffer_AppendString(buf, s);
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
27
apps/aqhome-mqttlog/s_setdata.h
Normal file
27
apps/aqhome-mqttlog/s_setdata.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOMEMQTT_S_SETDATA_H
|
||||
#define AQHOMEMQTT_S_SETDATA_H
|
||||
|
||||
|
||||
#include "./aqhome_mqtt.h"
|
||||
|
||||
#include <aqhome/events2/object.h>
|
||||
#include <aqhome/ipc2/message.h>
|
||||
|
||||
#include <gwenhywfar/tag16.h>
|
||||
|
||||
|
||||
void AQH_MqttLogServer_HandleSetData(AQH_OBJECT *o, const AQH_MESSAGE *recvdMsg, const GWEN_TAG16_LIST *tagList);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
1143
apps/aqhome-mqttlog/server.c
Normal file
1143
apps/aqhome-mqttlog/server.c
Normal file
File diff suppressed because it is too large
Load Diff
75
apps/aqhome-mqttlog/server.h
Normal file
75
apps/aqhome-mqttlog/server.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef SERVER_H
|
||||
#define SERVER_H
|
||||
|
||||
|
||||
|
||||
#include "aqhome-mqttlog/types/device.h"
|
||||
|
||||
#include "aqhome/events2/object.h"
|
||||
|
||||
|
||||
|
||||
#define AQH_ENDPOINT_PERMS_LISTVALUES 0x0001
|
||||
#define AQH_ENDPOINT_PERMS_READVALUE 0x0002
|
||||
#define AQH_ENDPOINT_PERMS_ADDVALUE 0x0004
|
||||
|
||||
#define AQH_ENDPOINT_PERMS_LISTDATA 0x0010
|
||||
#define AQH_ENDPOINT_PERMS_READDATA 0x0020
|
||||
#define AQH_ENDPOINT_PERMS_ADDDATA 0x0040
|
||||
#define AQH_ENDPOINT_PERMS_SETDATA 0x0080
|
||||
|
||||
#define AQH_ENDPOINT_PERMS_LISTDEVICES 0x0100
|
||||
#define AQH_ENDPOINT_PERMS_READDEVICE 0x0200
|
||||
#define AQH_ENDPOINT_PERMS_ADDDEVICE 0x0400
|
||||
#define AQH_ENDPOINT_PERMS_MODDEVICE 0x0800
|
||||
|
||||
|
||||
|
||||
AQH_OBJECT *AQH_MqttLogServer_new(AQH_EVENT_LOOP *eventLoop);
|
||||
int AQH_MqttLogServer_Init(AQH_OBJECT *o, int argc, char **argv);
|
||||
void AQH_MqttLogServer_Fini(AQH_OBJECT *o);
|
||||
|
||||
void AQH_MqttLogServer_ReloadDeviceFiles(AQH_OBJECT *o);
|
||||
void AQH_MqttLogServer_LoadRuntimeDeviceFiles(AQH_OBJECT *o);
|
||||
int AQH_MqttLogServer_SaveRuntimeDeviceFiles(AQH_OBJECT *o);
|
||||
|
||||
|
||||
/* loop functions */
|
||||
void AQH_MqttLogServer_HandleBrokerMsgs(AQH_OBJECT *o);
|
||||
void AQH_MqttLogServer_HandleMqttMsgs(AQH_OBJECT *o);
|
||||
void AQH_MqttLogServer_CheckBrokerConnection(AQH_OBJECT *o);
|
||||
void AQH_MqttLogServer_CheckMqttConnection(AQH_OBJECT *o);
|
||||
int AQH_MqttLogServer_SendPing(AQH_OBJECT *o);
|
||||
|
||||
|
||||
/* getters and setters */
|
||||
int AQH_MqttLogServer_GetTimeout(const AQH_OBJECT *o);
|
||||
void AQH_MqttLogServer_SetPidFile(AQH_OBJECT *o, const char *s);
|
||||
void AQH_MqttLogServer_SetDeviceFile(AQH_OBJECT *o, const char *s);
|
||||
|
||||
|
||||
/* device management */
|
||||
AQHMQTT_DEVICE_LIST *AQH_MqttLogServer_GetAvailableDeviceList(const AQH_OBJECT *o);
|
||||
void AQH_MqttLogServer_SetAvailableDeviceList(AQH_OBJECT *o, AQHMQTT_DEVICE_LIST *dl);
|
||||
void AQH_MqttLogServer_SetRegisteredDeviceList(AQH_OBJECT *o, AQHMQTT_DEVICE_LIST *dl);
|
||||
AQHMQTT_DEVICE *AQH_MqttLogServer_FindRegisteredDevice(AQH_OBJECT *o, const char *wantedDeviceId);
|
||||
void AQH_MqttLogServer_DumpRegisteredDevices(const AQH_OBJECT *o);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
67
apps/aqhome-mqttlog/server_p.h
Normal file
67
apps/aqhome-mqttlog/server_p.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef SERVER_P_H
|
||||
#define SERVER_P_H
|
||||
|
||||
|
||||
#include "./server.h"
|
||||
#include "aqhome-nodes/types/device.h"
|
||||
|
||||
#include "aqhome/ipc2/msgrequest.h"
|
||||
|
||||
#include <termios.h>
|
||||
|
||||
|
||||
/* default values */
|
||||
#define AQHOME_MQTT_DEFAULT_PIDFILE "/var/run/aqhome-mqtt.pid"
|
||||
#define AQHOME_MQTT_DEFAULT_DATADIR "/var/lib/aqhome-mqtt"
|
||||
#define AQHOME_MQTT_DEFAULT_DEVICEFILE "mqttlog/registereddevices.xml"
|
||||
|
||||
#define AQHOME_MQTT_DEFAULT_BROKER_PORT 1899
|
||||
#define AQHOME_MQTT_DEFAULT_BROKER_CLIENTID "mqtt"
|
||||
|
||||
|
||||
|
||||
typedef struct AQH_MQTTLOG_SERVER AQH_MQTTLOG_SERVER;
|
||||
struct AQH_MQTTLOG_SERVER {
|
||||
AQH_OBJECT *mqttEndpoint;
|
||||
AQH_OBJECT *brokerEndpoint;
|
||||
|
||||
GWEN_DB_NODE *dbArgs;
|
||||
|
||||
AQHMQTT_DEVICE_LIST *availableDeviceList;
|
||||
AQHMQTT_DEVICE_LIST *registeredDeviceList;
|
||||
|
||||
char *deviceFile;
|
||||
char *pidFile;
|
||||
|
||||
int timeout; /* timeout for run e.g. inside valgrind */
|
||||
|
||||
char *mqttAddress;
|
||||
int mqttPort;
|
||||
char *mqttClientId;
|
||||
int mqttKeepAlive;
|
||||
|
||||
char *brokerAddress;
|
||||
int brokerPort;
|
||||
char *brokerClientId;
|
||||
|
||||
time_t timestampMqttDown;
|
||||
time_t timestampBrokerDown;
|
||||
|
||||
int timeoutInSeconds;
|
||||
|
||||
};
|
||||
|
||||
|
||||
AQH_MQTTLOG_SERVER *AQH_MqttLogServer_GetServerData(const AQH_OBJECT *o);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
81
apps/aqhome-mqttlog/types/0BUILD
Normal file
81
apps/aqhome-mqttlog/types/0BUILD
Normal file
@@ -0,0 +1,81 @@
|
||||
<?xml?>
|
||||
|
||||
<gwbuild>
|
||||
|
||||
<target type="ConvenienceLibrary" name="aqhmqtt_types" >
|
||||
|
||||
<includes type="c" >
|
||||
$(gwenhywfar_cflags)
|
||||
-I$(topsrcdir)
|
||||
-I$(topbuilddir)
|
||||
-I$(topsrcdir)/apps
|
||||
-I$(topbuilddir)/apps
|
||||
-I$(builddir)
|
||||
-I$(srcdir)
|
||||
</includes>
|
||||
|
||||
<includes type="tm2" >
|
||||
--include=$(builddir)
|
||||
--include=$(srcdir)
|
||||
</includes>
|
||||
|
||||
<setVar name="local/cflags">$(visibility_cflags)</setVar>
|
||||
|
||||
<setVar name="tm2flags" >
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/typefiles" >
|
||||
device.t2d
|
||||
value.t2d
|
||||
topic.t2d
|
||||
translation.t2d
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_sources" >
|
||||
device.c
|
||||
value.c
|
||||
topic.c
|
||||
translation.c
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_headers_pub">
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_headers_priv" >
|
||||
device.h
|
||||
device_p.h
|
||||
value.h
|
||||
value_p.h
|
||||
topic.h
|
||||
topic_p.h
|
||||
translation.h
|
||||
translation_p.h
|
||||
</setVar>
|
||||
|
||||
<headers dist="true" >
|
||||
</headers>
|
||||
|
||||
<sources>
|
||||
$(local/typefiles)
|
||||
</sources>
|
||||
|
||||
<useTargets>
|
||||
</useTargets>
|
||||
|
||||
<libraries>
|
||||
</libraries>
|
||||
|
||||
<subdirs>
|
||||
</subdirs>
|
||||
|
||||
|
||||
<extradist>
|
||||
</extradist>
|
||||
|
||||
|
||||
</target>
|
||||
|
||||
|
||||
</gwbuild>
|
||||
|
||||
|
||||
75
apps/aqhome-mqttlog/types/device.t2d
Normal file
75
apps/aqhome-mqttlog/types/device.t2d
Normal file
@@ -0,0 +1,75 @@
|
||||
<?xml?>
|
||||
|
||||
<tm2>
|
||||
<type id="AQHMQTT_DEVICE" type="pointer">
|
||||
<descr>
|
||||
This object and its objects are used to store registered devices and definitions for possible new devices.
|
||||
</descr>
|
||||
<lang id="c">
|
||||
<identifier>AQHMQTT_DEVICE</identifier>
|
||||
<prefix>AQHMQTT_Device</prefix>
|
||||
<baseFileName>device</baseFileName>
|
||||
|
||||
<flags>
|
||||
with_list1
|
||||
with_list2
|
||||
</flags>
|
||||
|
||||
<headers>
|
||||
<header type="sys" loc="pre">aqhome/api.h</header>
|
||||
<header type="sys" loc="pre">aqhome-mqttlog/types/topic.h</header>
|
||||
</headers>
|
||||
|
||||
<inlines>
|
||||
</inlines>
|
||||
|
||||
|
||||
|
||||
</lang>
|
||||
|
||||
|
||||
<enums>
|
||||
</enums>
|
||||
|
||||
|
||||
<members>
|
||||
|
||||
<member name="id" type="char_ptr" maxlen="128">
|
||||
<descr>Only set for registered devices</descr>
|
||||
<default>NULL</default>
|
||||
<preset>NULL</preset>
|
||||
<access>public</access>
|
||||
<flags>own with_getbymember</flags>
|
||||
</member>
|
||||
|
||||
<member name="name" type="char_ptr" maxlen="128">
|
||||
<default>NULL</default>
|
||||
<preset>NULL</preset>
|
||||
<access>public</access>
|
||||
<flags>own</flags>
|
||||
</member>
|
||||
|
||||
<member name="driver" type="char_ptr" maxlen="64">
|
||||
<default>NULL</default>
|
||||
<preset>NULL</preset>
|
||||
<access>public</access>
|
||||
<flags>own</flags>
|
||||
</member>
|
||||
|
||||
|
||||
<member name="topicList" type="AQHMQTT_TOPIC_LIST" >
|
||||
<default>NULL</default>
|
||||
<preset>NULL</preset>
|
||||
<access>public</access>
|
||||
<flags>own noconst</flags>
|
||||
<getflags>none</getflags>
|
||||
<setflags>none</setflags>
|
||||
</member>
|
||||
|
||||
|
||||
</members>
|
||||
|
||||
</type>
|
||||
|
||||
</tm2>
|
||||
|
||||
114
apps/aqhome-mqttlog/types/topic.t2d
Normal file
114
apps/aqhome-mqttlog/types/topic.t2d
Normal file
@@ -0,0 +1,114 @@
|
||||
<?xml?>
|
||||
|
||||
<tm2>
|
||||
<type id="AQHMQTT_TOPIC" type="pointer">
|
||||
<descr>
|
||||
</descr>
|
||||
<lang id="c">
|
||||
<identifier>AQHMQTT_TOPIC</identifier>
|
||||
<prefix>AQHMQTT_Topic</prefix>
|
||||
<baseFileName>topic</baseFileName>
|
||||
|
||||
<flags>
|
||||
with_list1
|
||||
</flags>
|
||||
|
||||
<headers>
|
||||
<header type="sys" loc="pre">aqhome/api.h</header>
|
||||
<header type="sys" loc="pre">aqhome-mqttlog/types/value.h</header>
|
||||
</headers>
|
||||
|
||||
<inlines>
|
||||
</inlines>
|
||||
|
||||
|
||||
|
||||
</lang>
|
||||
|
||||
|
||||
<enums>
|
||||
|
||||
<enum id="AQHMQTT_TOPIC_TYPE" prefix="AQHMQTT_TopicType_">
|
||||
<item name="num" value="0">
|
||||
<descr>numeric type</descr>
|
||||
</item>
|
||||
<item name="json" >
|
||||
<descr>JSON type</descr>
|
||||
</item>
|
||||
</enum>
|
||||
|
||||
<enum id="AQHMQTT_TOPIC_DIR" prefix="AQHMQTT_TopicDir_">
|
||||
<item name="in" value="0"/>
|
||||
<item name="out" />
|
||||
</enum>
|
||||
|
||||
</enums>
|
||||
|
||||
|
||||
<members>
|
||||
|
||||
<member name="topic" type="char_ptr" maxlen="128">
|
||||
<descr>Only set for registered devices</descr>
|
||||
<default>NULL</default>
|
||||
<preset>NULL</preset>
|
||||
<access>public</access>
|
||||
<flags>own</flags>
|
||||
</member>
|
||||
|
||||
<member name="topicType" type="int" maxlen="8">
|
||||
<default>0</default>
|
||||
<preset>0</preset>
|
||||
<access>public</access>
|
||||
<flags></flags>
|
||||
</member>
|
||||
|
||||
<member name="direction" type="int" maxlen="8">
|
||||
<default>0</default>
|
||||
<preset>0</preset>
|
||||
<access>public</access>
|
||||
<flags></flags>
|
||||
</member>
|
||||
|
||||
<member name="name" type="char_ptr" maxlen="128">
|
||||
<default>NULL</default>
|
||||
<preset>NULL</preset>
|
||||
<access>public</access>
|
||||
<flags>own</flags>
|
||||
</member>
|
||||
|
||||
<member name="mask" type="char_ptr" maxlen="128">
|
||||
<default>NULL</default>
|
||||
<preset>NULL</preset>
|
||||
<access>public</access>
|
||||
<flags>own</flags>
|
||||
</member>
|
||||
|
||||
<member name="beforeId" type="char_ptr" maxlen="128">
|
||||
<default>NULL</default>
|
||||
<preset>NULL</preset>
|
||||
<access>public</access>
|
||||
<flags>own</flags>
|
||||
</member>
|
||||
|
||||
<member name="afterId" type="char_ptr" maxlen="128">
|
||||
<default>NULL</default>
|
||||
<preset>NULL</preset>
|
||||
<access>public</access>
|
||||
<flags>own</flags>
|
||||
</member>
|
||||
|
||||
<member name="valueList" type="AQHMQTT_VALUE_LIST" >
|
||||
<default>NULL</default>
|
||||
<preset>NULL</preset>
|
||||
<access>public</access>
|
||||
<flags>own</flags>
|
||||
<getflags>none</getflags>
|
||||
<setflags>none</setflags>
|
||||
</member>
|
||||
|
||||
</members>
|
||||
|
||||
</type>
|
||||
|
||||
</tm2>
|
||||
|
||||
55
apps/aqhome-mqttlog/types/translation.t2d
Normal file
55
apps/aqhome-mqttlog/types/translation.t2d
Normal file
@@ -0,0 +1,55 @@
|
||||
<?xml?>
|
||||
|
||||
<tm2>
|
||||
<type id="AQHMQTT_TRANSLATION" type="pointer">
|
||||
<descr>
|
||||
</descr>
|
||||
<lang id="c">
|
||||
<identifier>AQHMQTT_TRANSLATION</identifier>
|
||||
<prefix>AQHMQTT_Translation</prefix>
|
||||
<baseFileName>translation</baseFileName>
|
||||
|
||||
<flags>
|
||||
with_db
|
||||
with_list1
|
||||
</flags>
|
||||
|
||||
<headers>
|
||||
<header type="sys" loc="pre">aqhome/api.h</header>
|
||||
</headers>
|
||||
|
||||
<inlines>
|
||||
</inlines>
|
||||
|
||||
|
||||
|
||||
</lang>
|
||||
|
||||
|
||||
<enums>
|
||||
</enums>
|
||||
|
||||
|
||||
<members>
|
||||
|
||||
<member name="aqhValue" type="char_ptr" maxlen="128">
|
||||
<default>0</default>
|
||||
<preset>0</preset>
|
||||
<access>public</access>
|
||||
<flags>own</flags>
|
||||
</member>
|
||||
|
||||
<member name="driverValue" type="char_ptr" maxlen="128">
|
||||
<default>0</default>
|
||||
<preset>0</preset>
|
||||
<access>public</access>
|
||||
<flags>own</flags>
|
||||
</member>
|
||||
|
||||
|
||||
</members>
|
||||
|
||||
</type>
|
||||
|
||||
</tm2>
|
||||
|
||||
92
apps/aqhome-mqttlog/types/value.t2d
Normal file
92
apps/aqhome-mqttlog/types/value.t2d
Normal file
@@ -0,0 +1,92 @@
|
||||
<?xml?>
|
||||
|
||||
<tm2>
|
||||
<type id="AQHMQTT_VALUE" type="pointer">
|
||||
<descr>
|
||||
</descr>
|
||||
<lang id="c">
|
||||
<identifier>AQHMQTT_VALUE</identifier>
|
||||
<prefix>AQHMQTT_Value</prefix>
|
||||
<baseFileName>value</baseFileName>
|
||||
|
||||
<flags>
|
||||
with_list1
|
||||
with_list2
|
||||
</flags>
|
||||
|
||||
<headers>
|
||||
<header type="sys" loc="pre">aqhome/api.h</header>
|
||||
<header type="sys" loc="pre">aqhome-mqttlog/types/translation.h</header>
|
||||
</headers>
|
||||
|
||||
<inlines>
|
||||
</inlines>
|
||||
|
||||
|
||||
|
||||
</lang>
|
||||
|
||||
|
||||
<enums>
|
||||
|
||||
<enum id="AQHMQTT_VALUE_TYPE" prefix="AQHMQTT_ValueType_">
|
||||
<item name="sensor" value="0">
|
||||
<descr>sensor</descr>
|
||||
</item>
|
||||
<item name="actor" >
|
||||
<descr>actor</descr>
|
||||
</item>
|
||||
|
||||
</enum>
|
||||
|
||||
</enums>
|
||||
|
||||
|
||||
<members>
|
||||
|
||||
<member name="name" type="char_ptr" maxlen="128">
|
||||
<default>0</default>
|
||||
<preset>0</preset>
|
||||
<access>public</access>
|
||||
<flags>own with_getbymember</flags>
|
||||
</member>
|
||||
|
||||
<member name="valueType" type="int" maxlen="8">
|
||||
<default>0</default>
|
||||
<preset>0</preset>
|
||||
<access>public</access>
|
||||
<flags></flags>
|
||||
</member>
|
||||
|
||||
<member name="valueUnits" type="char_ptr" maxlen="32">
|
||||
<default>0</default>
|
||||
<preset>0</preset>
|
||||
<access>public</access>
|
||||
<flags>own</flags>
|
||||
</member>
|
||||
|
||||
|
||||
<member name="path" type="char_ptr" maxlen="256">
|
||||
<default>0</default>
|
||||
<preset>0</preset>
|
||||
<access>public</access>
|
||||
<flags>own</flags>
|
||||
</member>
|
||||
|
||||
|
||||
<member name="translationList" type="AQHMQTT_TRANSLATION_LIST" >
|
||||
<default>NULL</default>
|
||||
<preset>NULL</preset>
|
||||
<access>public</access>
|
||||
<flags>own</flags>
|
||||
<getflags>none</getflags>
|
||||
<setflags>none</setflags>
|
||||
</member>
|
||||
|
||||
|
||||
</members>
|
||||
|
||||
</type>
|
||||
|
||||
</tm2>
|
||||
|
||||
482
apps/aqhome-mqttlog/xmlread.c
Normal file
482
apps/aqhome-mqttlog/xmlread.c
Normal file
@@ -0,0 +1,482 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "./xmlread.h"
|
||||
#include "./server_p.h"
|
||||
#include "aqhome-mqttlog/types/topic.h"
|
||||
#include "aqhome-mqttlog/types/value.h"
|
||||
#include "aqhome-mqttlog/types/translation.h"
|
||||
|
||||
#include <aqhome/api.h>
|
||||
#include <aqhome/aqhome.h>
|
||||
|
||||
#include <gwenhywfar/endpoint_multilayer.h>
|
||||
#include <gwenhywfar/text.h>
|
||||
#include <gwenhywfar/xml.h>
|
||||
#include <gwenhywfar/directory.h>
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* defines
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static AQHMQTT_DEVICE_LIST *_readDeviceFiles(const GWEN_STRINGLIST *sl);
|
||||
static int _readDeviceFileToList(const char *sFilename, AQHMQTT_DEVICE_LIST *deviceList);
|
||||
static int _readXmlDevices(GWEN_XMLNODE *deviceListNode, AQHMQTT_DEVICE_LIST *deviceList);
|
||||
static AQHMQTT_DEVICE *_readXmlDevice(GWEN_XMLNODE *deviceNode);
|
||||
static AQHMQTT_TOPIC_LIST *_readXmlTopicList(GWEN_XMLNODE *parentNode);
|
||||
static AQHMQTT_TOPIC *_readXmlTopic(GWEN_XMLNODE *topicNode);
|
||||
static AQHMQTT_VALUE_LIST *_readXmlValueList(GWEN_XMLNODE *parentNode);
|
||||
static AQHMQTT_VALUE *_readXmlValue(GWEN_XMLNODE *valueNode);
|
||||
static AQHMQTT_TRANSLATION_LIST *_readXmlTranslationList(GWEN_XMLNODE *parentNode);
|
||||
static AQHMQTT_TRANSLATION *_readXmlTranslation(GWEN_XMLNODE *translationNode);
|
||||
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* implementations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
AQHMQTT_DEVICE_LIST *AQH_MqttLogServer_ReadDeviceFile(AQH_OBJECT *o, const char *sFilename)
|
||||
{
|
||||
if (o) {
|
||||
AQH_MQTTLOG_SERVER *xo;
|
||||
|
||||
xo=AQH_MqttLogServer_GetServerData(o);
|
||||
if (xo) {
|
||||
int rv;
|
||||
|
||||
rv=GWEN_Directory_GetPath(sFilename, GWEN_PATH_FLAGS_CHECKROOT | GWEN_PATH_FLAGS_PATHMUSTEXIST | GWEN_PATH_FLAGS_VARIABLE);
|
||||
if (rv<0) {
|
||||
DBG_ERROR(NULL, "File \"%s\" does not exists, writing later", sFilename);
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
AQHMQTT_DEVICE_LIST *deviceList;
|
||||
|
||||
deviceList=AQHMQTT_Device_List_new();
|
||||
rv=_readDeviceFileToList(sFilename, deviceList);
|
||||
if (rv<0) {
|
||||
DBG_ERROR(NULL, "File \"%s\" not found", sFilename);
|
||||
AQHMQTT_Device_List_free(deviceList);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (AQHMQTT_Device_List_GetCount(deviceList)<1) {
|
||||
AQHMQTT_Device_List_free(deviceList);
|
||||
return NULL;
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHMQTT_DEVICE_LIST *AQH_MqttLogServer_ReadDataDeviceFiles(AQH_OBJECT *o)
|
||||
{
|
||||
if (o) {
|
||||
AQH_MQTTLOG_SERVER *xo;
|
||||
|
||||
xo=AQH_MqttLogServer_GetServerData(o);
|
||||
if (xo) {
|
||||
GWEN_STRINGLIST *sl;
|
||||
|
||||
sl=AQH_GetListOfMatchingDataFiles("aqhome/devices/mqtt", "*.xml");
|
||||
if (sl) {
|
||||
AQHMQTT_DEVICE_LIST *deviceList;
|
||||
|
||||
deviceList=_readDeviceFiles(sl);
|
||||
GWEN_StringList_free(sl);
|
||||
if (deviceList==NULL) {
|
||||
DBG_INFO(NULL, "Error reading data device files");
|
||||
return NULL;
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "No data device files");
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHMQTT_DEVICE_LIST *_readDeviceFiles(const GWEN_STRINGLIST *sl)
|
||||
{
|
||||
GWEN_STRINGLISTENTRY *se;
|
||||
AQHMQTT_DEVICE_LIST *deviceList;
|
||||
|
||||
deviceList=AQHMQTT_Device_List_new();
|
||||
se=GWEN_StringList_FirstEntry(sl);
|
||||
while(se) {
|
||||
const char *s;
|
||||
|
||||
s=GWEN_StringListEntry_Data(se);
|
||||
if (s && *s) {
|
||||
int rv;
|
||||
|
||||
DBG_INFO(NULL, "Reading device file \"%s\"", s);
|
||||
rv=_readDeviceFileToList(s, deviceList);
|
||||
if (rv<0 && rv!=GWEN_ERROR_NO_DATA) {
|
||||
DBG_WARN(NULL, "Error reading device file \"%s\" (%d), ignoring", s, rv);
|
||||
}
|
||||
}
|
||||
se=GWEN_StringListEntry_Next(se);
|
||||
}
|
||||
|
||||
if (AQHMQTT_Device_List_GetCount(deviceList)<1) {
|
||||
AQHMQTT_Device_List_free(deviceList);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _readDeviceFileToList(const char *sFilename, AQHMQTT_DEVICE_LIST *deviceList)
|
||||
{
|
||||
GWEN_XMLNODE *rootNode;
|
||||
GWEN_XMLNODE *deviceListNode;
|
||||
int rv;
|
||||
|
||||
rootNode=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, NULL);
|
||||
rv=GWEN_XML_ReadFile(rootNode, sFilename, GWEN_XML_FLAGS_DEFAULT);
|
||||
if (rv<0) {
|
||||
DBG_ERROR(NULL, "Error reading XML file \"%s\": %d", sFilename, rv);
|
||||
GWEN_XMLNode_free(rootNode);
|
||||
return rv;
|
||||
}
|
||||
|
||||
deviceListNode=GWEN_XMLNode_FindFirstTag(rootNode, "devices", NULL, NULL);
|
||||
if (deviceListNode==NULL)
|
||||
deviceListNode=rootNode;
|
||||
|
||||
rv=_readXmlDevices(deviceListNode, deviceList);
|
||||
if (rv<0 && rv!=GWEN_ERROR_NO_DATA) {
|
||||
DBG_ERROR(AQH_LOGDOMAIN, "Error reading devices from file \"%s\" (%d)", sFilename, rv);
|
||||
GWEN_XMLNode_free(rootNode);
|
||||
return rv;
|
||||
}
|
||||
|
||||
GWEN_XMLNode_free(rootNode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int _readXmlDevices(GWEN_XMLNODE *deviceListNode, AQHMQTT_DEVICE_LIST *deviceList)
|
||||
{
|
||||
GWEN_XMLNODE *deviceNode;
|
||||
|
||||
deviceNode=GWEN_XMLNode_FindFirstTag(deviceListNode, "device", NULL, NULL);
|
||||
if (deviceNode) {
|
||||
while(deviceNode) {
|
||||
const char *driverName;
|
||||
|
||||
driverName=GWEN_XMLNode_GetProperty(deviceNode, "driver", NULL);
|
||||
if (driverName && *driverName && strcasecmp(driverName, "mqtt")==0) {
|
||||
AQHMQTT_DEVICE *device;
|
||||
|
||||
device=_readXmlDevice(deviceNode);
|
||||
if (device==NULL) {
|
||||
DBG_INFO(NULL, "Error reading device from XML");
|
||||
return GWEN_ERROR_BAD_DATA;
|
||||
}
|
||||
else {
|
||||
const char *sDeviceId;
|
||||
const char *sDeviceName;
|
||||
|
||||
sDeviceId=AQHMQTT_Device_GetId(device);
|
||||
sDeviceName=AQHMQTT_Device_GetName(device);
|
||||
if (sDeviceId && *sDeviceId) {
|
||||
DBG_ERROR(NULL, "Adding device \"%s\" (%s) to list", sDeviceId, sDeviceName?sDeviceName:"<no type name>");
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Adding device type \"%s\" to list", sDeviceName?sDeviceName:"<no type name>");
|
||||
}
|
||||
AQHMQTT_Device_List_Add(device, deviceList);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "Device is not an MQTT device, ignoring");
|
||||
}
|
||||
deviceNode=GWEN_XMLNode_FindNextTag(deviceNode, "device", NULL, NULL);
|
||||
} /* while */
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "No <device> element found");
|
||||
return GWEN_ERROR_NO_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHMQTT_DEVICE *_readXmlDevice(GWEN_XMLNODE *deviceNode)
|
||||
{
|
||||
AQHMQTT_DEVICE *device;
|
||||
GWEN_XMLNODE *topicsNode;
|
||||
|
||||
device=AQHMQTT_Device_new();
|
||||
AQHMQTT_Device_SetId(device, GWEN_XMLNode_GetProperty(deviceNode, "id", NULL));
|
||||
AQHMQTT_Device_SetName(device, GWEN_XMLNode_GetProperty(deviceNode, "name", NULL));
|
||||
AQHMQTT_Device_SetDriver(device, GWEN_XMLNode_GetProperty(deviceNode, "driver", NULL));
|
||||
|
||||
topicsNode=GWEN_XMLNode_FindFirstTag(deviceNode, "mqtttopics", NULL, NULL);
|
||||
if (topicsNode) {
|
||||
AQHMQTT_TOPIC_LIST *topicList;
|
||||
|
||||
topicList=_readXmlTopicList(topicsNode);
|
||||
if (topicList)
|
||||
AQHMQTT_Device_SetTopicList(device, topicList);
|
||||
else {
|
||||
DBG_INFO(NULL, "No mqtt topics read");
|
||||
AQHMQTT_Device_free(device);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "No <mqtttopics> element");
|
||||
AQHMQTT_Device_free(device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHMQTT_TOPIC_LIST *_readXmlTopicList(GWEN_XMLNODE *parentNode)
|
||||
{
|
||||
AQHMQTT_TOPIC_LIST *topicList;
|
||||
GWEN_XMLNODE *topicNode;
|
||||
|
||||
topicList=AQHMQTT_Topic_List_new();
|
||||
topicNode=GWEN_XMLNode_FindFirstTag(parentNode, "mqtttopic", NULL, NULL);
|
||||
while(topicNode) {
|
||||
AQHMQTT_TOPIC *topic=_readXmlTopic(topicNode);
|
||||
if (topic)
|
||||
AQHMQTT_Topic_List_Add(topic, topicList);
|
||||
else {
|
||||
DBG_INFO(NULL, "Error reading <mqtttopic> element");
|
||||
AQHMQTT_Topic_List_free(topicList);
|
||||
return NULL;
|
||||
}
|
||||
topicNode=GWEN_XMLNode_FindNextTag(topicNode, "mqtttopic", NULL, NULL);
|
||||
}
|
||||
if (AQHMQTT_Topic_List_GetCount(topicList)<1) {
|
||||
AQHMQTT_Topic_List_free(topicList);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return topicList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHMQTT_TOPIC *_readXmlTopic(GWEN_XMLNODE *topicNode)
|
||||
{
|
||||
AQHMQTT_TOPIC *topic;
|
||||
GWEN_XMLNODE *valuesNode;
|
||||
int i;
|
||||
const char *s;
|
||||
|
||||
topic=AQHMQTT_Topic_new();
|
||||
s=GWEN_XMLNode_GetProperty(topicNode, "type", NULL);
|
||||
i=AQHMQTT_TopicType_fromString(s);
|
||||
if (i==AQHMQTT_TopicType_Unknown) {
|
||||
DBG_ERROR(NULL, "Invalid topic type \"%s\"", s?s:"<empty>");
|
||||
AQHMQTT_Topic_free(topic);
|
||||
return NULL;
|
||||
}
|
||||
AQHMQTT_Topic_SetTopicType(topic, i);
|
||||
|
||||
s=GWEN_XMLNode_GetProperty(topicNode, "direction", NULL);
|
||||
i=AQHMQTT_TopicDir_fromString(s);
|
||||
if (i==AQHMQTT_TopicDir_Unknown) {
|
||||
DBG_ERROR(NULL, "Invalid topic direction \"%s\"", s?s:"<empty>");
|
||||
AQHMQTT_Topic_free(topic);
|
||||
return NULL;
|
||||
}
|
||||
AQHMQTT_Topic_SetDirection(topic, i);
|
||||
|
||||
AQHMQTT_Topic_SetName(topic, GWEN_XMLNode_GetProperty(topicNode, "name", NULL));
|
||||
AQHMQTT_Topic_SetTopic(topic, GWEN_XMLNode_GetCharValue(topicNode, "topic", NULL));
|
||||
AQHMQTT_Topic_SetMask(topic, GWEN_XMLNode_GetCharValue(topicNode, "mask", NULL));
|
||||
AQHMQTT_Topic_SetBeforeId(topic, GWEN_XMLNode_GetCharValue(topicNode, "beforeId", NULL));
|
||||
AQHMQTT_Topic_SetAfterId(topic, GWEN_XMLNode_GetCharValue(topicNode, "afterId", NULL));
|
||||
|
||||
valuesNode=GWEN_XMLNode_FindFirstTag(topicNode, "values", NULL, NULL);
|
||||
if (valuesNode) {
|
||||
AQHMQTT_VALUE_LIST *valueList;
|
||||
|
||||
valueList=_readXmlValueList(valuesNode);
|
||||
if (valueList)
|
||||
AQHMQTT_Topic_SetValueList(topic, valueList);
|
||||
else {
|
||||
DBG_INFO(NULL, "No values read");
|
||||
AQHMQTT_Topic_free(topic);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "No <values> element");
|
||||
AQHMQTT_Topic_free(topic);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return topic;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHMQTT_VALUE_LIST *_readXmlValueList(GWEN_XMLNODE *parentNode)
|
||||
{
|
||||
AQHMQTT_VALUE_LIST *valueList;
|
||||
GWEN_XMLNODE *valueNode;
|
||||
|
||||
valueList=AQHMQTT_Value_List_new();
|
||||
valueNode=GWEN_XMLNode_FindFirstTag(parentNode, "value", NULL, NULL);
|
||||
while(valueNode) {
|
||||
AQHMQTT_VALUE *value=_readXmlValue(valueNode);
|
||||
if (value)
|
||||
AQHMQTT_Value_List_Add(value, valueList);
|
||||
else {
|
||||
DBG_INFO(NULL, "Error reading <value> element");
|
||||
AQHMQTT_Value_List_free(valueList);
|
||||
return NULL;
|
||||
}
|
||||
valueNode=GWEN_XMLNode_FindNextTag(valueNode, "value", NULL, NULL);
|
||||
}
|
||||
if (AQHMQTT_Value_List_GetCount(valueList)<1) {
|
||||
AQHMQTT_Value_List_free(valueList);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return valueList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHMQTT_VALUE *_readXmlValue(GWEN_XMLNODE *valueNode)
|
||||
{
|
||||
AQHMQTT_VALUE *value;
|
||||
GWEN_XMLNODE *translationNode;
|
||||
const char *s;
|
||||
int i;
|
||||
|
||||
value=AQHMQTT_Value_new();
|
||||
AQHMQTT_Value_SetName(value, GWEN_XMLNode_GetProperty(valueNode, "name", NULL));
|
||||
AQHMQTT_Value_SetValueUnits(value, GWEN_XMLNode_GetProperty(valueNode, "units", NULL));
|
||||
AQHMQTT_Value_SetPath(value, GWEN_XMLNode_GetProperty(valueNode, "path", NULL));
|
||||
|
||||
s=GWEN_XMLNode_GetProperty(valueNode, "type", NULL);
|
||||
i=AQHMQTT_ValueType_fromString(s);
|
||||
if (i==AQHMQTT_ValueType_Unknown) {
|
||||
DBG_ERROR(NULL, "Invalid value type \"%s\"", s?s:"<empty>");
|
||||
AQHMQTT_Value_free(value);
|
||||
return NULL;
|
||||
}
|
||||
AQHMQTT_Value_SetValueType(value, i);
|
||||
|
||||
translationNode=GWEN_XMLNode_FindFirstTag(valueNode, "translations", NULL, NULL);
|
||||
if (translationNode) {
|
||||
AQHMQTT_TRANSLATION_LIST *translationList;
|
||||
|
||||
translationList=_readXmlTranslationList(translationNode);
|
||||
if (translationList) {
|
||||
DBG_INFO(NULL, "Translations read");
|
||||
AQHMQTT_Value_SetTranslationList(value, translationList);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "No <translations> element");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHMQTT_TRANSLATION_LIST *_readXmlTranslationList(GWEN_XMLNODE *parentNode)
|
||||
{
|
||||
AQHMQTT_TRANSLATION_LIST *translationList;
|
||||
GWEN_XMLNODE *translationNode;
|
||||
|
||||
translationList=AQHMQTT_Translation_List_new();
|
||||
translationNode=GWEN_XMLNode_FindFirstTag(parentNode, "translation", NULL, NULL);
|
||||
while(translationNode) {
|
||||
AQHMQTT_TRANSLATION *translation=_readXmlTranslation(translationNode);
|
||||
if (translation)
|
||||
AQHMQTT_Translation_List_Add(translation, translationList);
|
||||
else {
|
||||
DBG_INFO(NULL, "Error reading <translation> element");
|
||||
AQHMQTT_Translation_List_free(translationList);
|
||||
return NULL;
|
||||
}
|
||||
translationNode=GWEN_XMLNode_FindNextTag(translationNode, "translation", NULL, NULL);
|
||||
}
|
||||
if (AQHMQTT_Translation_List_GetCount(translationList)<1) {
|
||||
AQHMQTT_Translation_List_free(translationList);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return translationList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHMQTT_TRANSLATION *_readXmlTranslation(GWEN_XMLNODE *translationNode)
|
||||
{
|
||||
const char *sAqhValue;
|
||||
const char *sDriverValue;
|
||||
|
||||
sAqhValue=GWEN_XMLNode_GetProperty(translationNode, "aqhValue", NULL);
|
||||
sDriverValue=GWEN_XMLNode_GetProperty(translationNode, "driverValue", NULL);
|
||||
if (sAqhValue && *sAqhValue && sDriverValue && *sDriverValue) {
|
||||
AQHMQTT_TRANSLATION *translation;
|
||||
|
||||
translation=AQHMQTT_Translation_new();
|
||||
AQHMQTT_Translation_SetAqhValue(translation, sAqhValue);
|
||||
AQHMQTT_Translation_SetDriverValue(translation, sDriverValue);
|
||||
return translation;
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(AQH_LOGDOMAIN, "Either AqhValue or DriverValue missing in device description file");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
26
apps/aqhome-mqttlog/xmlread.h
Normal file
26
apps/aqhome-mqttlog/xmlread.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_MQTTLOG_XMLREAD_H
|
||||
#define AQHOME_MQTTLOG_XMLREAD_H
|
||||
|
||||
#include "aqhome-mqttlog/server.h"
|
||||
#include "aqhome-mqttlog/aqhome_mqtt.h"
|
||||
#include "aqhome-mqttlog/types/device.h"
|
||||
|
||||
|
||||
|
||||
AQHMQTT_DEVICE_LIST *AQH_MqttLogServer_ReadDataDeviceFiles(AQH_OBJECT *o);
|
||||
AQHMQTT_DEVICE_LIST *AQH_MqttLogServer_ReadDeviceFile(AQH_OBJECT *o, const char *sFilename);
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
249
apps/aqhome-mqttlog/xmlwrite.c
Normal file
249
apps/aqhome-mqttlog/xmlwrite.c
Normal file
@@ -0,0 +1,249 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "./xmlwrite.h"
|
||||
#include "./aqhome_mqtt_p.h"
|
||||
#include "aqhome-mqttlog/types/topic.h"
|
||||
#include "aqhome-mqttlog/types/value.h"
|
||||
#include "aqhome-mqttlog/types/translation.h"
|
||||
|
||||
#include <aqhome/api.h>
|
||||
#include <aqhome/aqhome.h>
|
||||
|
||||
#include <gwenhywfar/endpoint_multilayer.h>
|
||||
#include <gwenhywfar/text.h>
|
||||
#include <gwenhywfar/xml.h>
|
||||
#include <gwenhywfar/directory.h>
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static void _writeDevicesToXml(const AQHMQTT_DEVICE_LIST *deviceList, GWEN_XMLNODE *node);
|
||||
static void _writeDeviceToXml(const AQHMQTT_DEVICE *device, GWEN_XMLNODE *node);
|
||||
static void _writeTopicToXml(const AQHMQTT_TOPIC *topic, GWEN_XMLNODE *node);
|
||||
static void _writeValueToXml(const AQHMQTT_VALUE *value, GWEN_XMLNODE *node);
|
||||
static void _writeTranslationToXml(const AQHMQTT_TRANSLATION *t, GWEN_XMLNODE *nTranslation);
|
||||
static void _setXmlPropertyIfNotNull(GWEN_XMLNODE *n, const char *name, const char *value);
|
||||
static void _setXmlCharValueIfNotNull(GWEN_XMLNODE *n, const char *name, const char *value);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* implementations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
|
||||
int AQH_MqttLogServer_WriteDevicesFile(const AQHMQTT_DEVICE_LIST *deviceList, const char *sFilename)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv=GWEN_Directory_GetPath(sFilename, GWEN_PATH_FLAGS_CHECKROOT | GWEN_PATH_FLAGS_VARIABLE);
|
||||
if (rv<0) {
|
||||
DBG_ERROR(NULL, "Could not access file \"%s\"", sFilename);
|
||||
return GWEN_ERROR_GENERIC;
|
||||
}
|
||||
else {
|
||||
GWEN_XMLNODE *rootNode;
|
||||
GWEN_BUFFER *nbuf;
|
||||
|
||||
rootNode=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, "root");
|
||||
_writeDevicesToXml(deviceList, rootNode);
|
||||
|
||||
nbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||
GWEN_Buffer_AppendString(nbuf, sFilename);
|
||||
GWEN_Buffer_AppendString(nbuf, ".tmp");
|
||||
unlink(GWEN_Buffer_GetStart(nbuf));
|
||||
rv=GWEN_XMLNode_WriteFile(rootNode,
|
||||
GWEN_Buffer_GetStart(nbuf),
|
||||
GWEN_XML_FLAGS_SIMPLE | GWEN_XML_FLAGS_HANDLE_HEADERS | GWEN_XML_FLAGS_INDENT);
|
||||
if (rv<0) {
|
||||
DBG_ERROR(AQH_LOGDOMAIN, "Error writing XML file \"%s\": %d", GWEN_Buffer_GetStart(nbuf), rv);
|
||||
GWEN_Buffer_free(nbuf);
|
||||
GWEN_XMLNode_free(rootNode);
|
||||
return rv;
|
||||
}
|
||||
if (rename(GWEN_Buffer_GetStart(nbuf), sFilename)<0) {
|
||||
DBG_ERROR(AQH_LOGDOMAIN, "Error renaming \"%s\"->\"%s\": %d (%s)",
|
||||
GWEN_Buffer_GetStart(nbuf), sFilename, errno, strerror(errno));
|
||||
GWEN_Buffer_free(nbuf);
|
||||
GWEN_XMLNode_free(rootNode);
|
||||
return rv;
|
||||
}
|
||||
GWEN_Buffer_free(nbuf);
|
||||
GWEN_XMLNode_free(rootNode);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _writeDevicesToXml(const AQHMQTT_DEVICE_LIST *deviceList, GWEN_XMLNODE *node)
|
||||
{
|
||||
if (deviceList && AQHMQTT_Device_List_GetCount(deviceList)) {
|
||||
GWEN_XMLNODE *nDevices;
|
||||
const AQHMQTT_DEVICE *device;
|
||||
|
||||
nDevices=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, "devices");
|
||||
device=AQHMQTT_Device_List_First(deviceList);
|
||||
while(device) {
|
||||
GWEN_XMLNODE *nDevice;
|
||||
|
||||
nDevice=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, "device");
|
||||
_writeDeviceToXml(device, nDevice);
|
||||
GWEN_XMLNode_AddChild(nDevices, nDevice);
|
||||
|
||||
device=AQHMQTT_Device_List_Next(device);
|
||||
}
|
||||
|
||||
GWEN_XMLNode_AddChild(node, nDevices);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void _writeDeviceToXml(const AQHMQTT_DEVICE *device, GWEN_XMLNODE *node)
|
||||
{
|
||||
const AQHMQTT_TOPIC_LIST *topicList;
|
||||
|
||||
_setXmlPropertyIfNotNull(node, "id", AQHMQTT_Device_GetId(device));
|
||||
_setXmlPropertyIfNotNull(node, "name", AQHMQTT_Device_GetName(device));
|
||||
_setXmlPropertyIfNotNull(node, "driver", AQHMQTT_Device_GetDriver(device));
|
||||
|
||||
topicList=AQHMQTT_Device_GetTopicList(device);
|
||||
if (topicList && AQHMQTT_Topic_List_GetCount(topicList)) {
|
||||
GWEN_XMLNODE *nTopics;
|
||||
const AQHMQTT_TOPIC *topic;
|
||||
|
||||
nTopics=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, "mqtttopics");
|
||||
topic=AQHMQTT_Topic_List_First(topicList);
|
||||
while(topic) {
|
||||
GWEN_XMLNODE *nTopic;
|
||||
|
||||
nTopic=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, "mqtttopic");
|
||||
_writeTopicToXml(topic, nTopic);
|
||||
GWEN_XMLNode_AddChild(nTopics, nTopic);
|
||||
|
||||
topic=AQHMQTT_Topic_List_Next(topic);
|
||||
}
|
||||
|
||||
GWEN_XMLNode_AddChild(node, nTopics);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void _writeTopicToXml(const AQHMQTT_TOPIC *topic, GWEN_XMLNODE *node)
|
||||
{
|
||||
const AQHMQTT_VALUE_LIST *valueList;
|
||||
|
||||
GWEN_XMLNode_SetProperty(node, "type", AQHMQTT_TopicType_toString(AQHMQTT_Topic_GetTopicType(topic)));
|
||||
GWEN_XMLNode_SetProperty(node, "direction", AQHMQTT_TopicDir_toString(AQHMQTT_Topic_GetDirection(topic)));
|
||||
_setXmlPropertyIfNotNull(node, "name", AQHMQTT_Topic_GetName(topic));
|
||||
_setXmlCharValueIfNotNull(node, "topic", AQHMQTT_Topic_GetTopic(topic));
|
||||
_setXmlCharValueIfNotNull(node, "mask", AQHMQTT_Topic_GetMask(topic));
|
||||
_setXmlCharValueIfNotNull(node, "beforeId", AQHMQTT_Topic_GetBeforeId(topic));
|
||||
_setXmlCharValueIfNotNull(node, "afterId", AQHMQTT_Topic_GetAfterId(topic));
|
||||
|
||||
valueList=AQHMQTT_Topic_GetValueList(topic);
|
||||
if (valueList && AQHMQTT_Value_List_GetCount(valueList)) {
|
||||
GWEN_XMLNODE *nValues;
|
||||
const AQHMQTT_VALUE *value;
|
||||
|
||||
nValues=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, "values");
|
||||
value=AQHMQTT_Value_List_First(valueList);
|
||||
while(value) {
|
||||
GWEN_XMLNODE *nValue;
|
||||
|
||||
nValue=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, "value");
|
||||
_writeValueToXml(value, nValue);
|
||||
GWEN_XMLNode_AddChild(nValues, nValue);
|
||||
|
||||
value=AQHMQTT_Value_List_Next(value);
|
||||
}
|
||||
|
||||
GWEN_XMLNode_AddChild(node, nValues);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void _writeValueToXml(const AQHMQTT_VALUE *value, GWEN_XMLNODE *node)
|
||||
{
|
||||
const AQHMQTT_TRANSLATION_LIST *translationList;
|
||||
|
||||
_setXmlPropertyIfNotNull(node, "name", AQHMQTT_Value_GetName(value));
|
||||
_setXmlPropertyIfNotNull(node, "units", AQHMQTT_Value_GetValueUnits(value));
|
||||
_setXmlPropertyIfNotNull(node, "path", AQHMQTT_Value_GetPath(value));
|
||||
GWEN_XMLNode_SetProperty(node, "type", AQHMQTT_ValueType_toString(AQHMQTT_Value_GetValueType(value)));
|
||||
|
||||
translationList=AQHMQTT_Value_GetTranslationList(value);
|
||||
if (translationList && AQHMQTT_Translation_List_GetCount(translationList)) {
|
||||
GWEN_XMLNODE *nTranslations;
|
||||
const AQHMQTT_TRANSLATION *translation;
|
||||
|
||||
nTranslations=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, "translations");
|
||||
translation=AQHMQTT_Translation_List_First(translationList);
|
||||
while(translation) {
|
||||
GWEN_XMLNODE *nTranslation;
|
||||
|
||||
nTranslation=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, "transaction");
|
||||
_writeTranslationToXml(translation, nTranslation);
|
||||
GWEN_XMLNode_AddChild(nTranslations, nTranslation);
|
||||
|
||||
translation=AQHMQTT_Translation_List_Next(translation);
|
||||
}
|
||||
|
||||
GWEN_XMLNode_AddChild(node, nTranslations);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _writeTranslationToXml(const AQHMQTT_TRANSLATION *t, GWEN_XMLNODE *nTranslation)
|
||||
{
|
||||
_setXmlPropertyIfNotNull(nTranslation, "aqhValue", AQHMQTT_Translation_GetAqhValue(t));
|
||||
_setXmlPropertyIfNotNull(nTranslation, "driverValue", AQHMQTT_Translation_GetDriverValue(t));
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _setXmlPropertyIfNotNull(GWEN_XMLNODE *n, const char *name, const char *value)
|
||||
{
|
||||
if (value && *value)
|
||||
GWEN_XMLNode_SetProperty(n, name, value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _setXmlCharValueIfNotNull(GWEN_XMLNODE *n, const char *name, const char *value)
|
||||
{
|
||||
if (value && *value)
|
||||
GWEN_XMLNode_SetCharValue(n, name, value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
24
apps/aqhome-mqttlog/xmlwrite.h
Normal file
24
apps/aqhome-mqttlog/xmlwrite.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_MQTTLOG_XMLWRITE_H
|
||||
#define AQHOME_MQTTLOG_XMLWRITE_H
|
||||
|
||||
#include "aqhome-mqttlog/server.h"
|
||||
#include "aqhome-mqttlog/aqhome_mqtt.h"
|
||||
#include "aqhome-mqttlog/types/device.h"
|
||||
|
||||
|
||||
|
||||
int AQH_MqttLogServer_WriteDevicesFile(const AQHMQTT_DEVICE_LIST *deviceList, const char *sFilename);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
84
apps/aqhome-nodes/0BUILD
Normal file
84
apps/aqhome-nodes/0BUILD
Normal file
@@ -0,0 +1,84 @@
|
||||
<?xml?>
|
||||
|
||||
<gwbuild>
|
||||
|
||||
<target type="Program" name="aqhome-nodes" install="$(sbindir)" >
|
||||
|
||||
<includes type="c" >
|
||||
$(gwenhywfar_cflags)
|
||||
-I$(topsrcdir)
|
||||
-I$(topbuilddir)
|
||||
-I$(topsrcdir)/apps
|
||||
-I$(topbuilddir)/apps
|
||||
</includes>
|
||||
|
||||
<includes type="tm2" >
|
||||
--include=$(builddir)
|
||||
--include=$(srcdir)
|
||||
</includes>
|
||||
|
||||
<setVar name="local/cflags">$(visibility_cflags)</setVar>
|
||||
|
||||
<setVar name="tm2flags" >
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/typefiles" >
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_sources" >
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_headers_pub">
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_headers_priv" >
|
||||
</setVar>
|
||||
|
||||
<headers dist="true" >
|
||||
db.h
|
||||
tty_log.h
|
||||
devicesread.h
|
||||
devicesdump.h
|
||||
r_setdata.h
|
||||
r_connect.h
|
||||
r_forward.h
|
||||
r_setaccmsggrps.h
|
||||
r_getnodes.h
|
||||
</headers>
|
||||
|
||||
<sources>
|
||||
$(local/typefiles)
|
||||
server.c
|
||||
db.c
|
||||
devicesread.c
|
||||
devicesdump.c
|
||||
r_setdata.c
|
||||
r_connect.c
|
||||
r_forward.c
|
||||
r_setaccmsggrps.c
|
||||
r_getnodes.c
|
||||
main.c
|
||||
</sources>
|
||||
|
||||
<useTargets>
|
||||
aqhnodes_types
|
||||
aqhome
|
||||
</useTargets>
|
||||
|
||||
<libraries>
|
||||
$(gwenhywfar_libs)
|
||||
</libraries>
|
||||
|
||||
<subdirs>
|
||||
types
|
||||
</subdirs>
|
||||
|
||||
|
||||
<extradist>
|
||||
</extradist>
|
||||
|
||||
|
||||
</target>
|
||||
|
||||
|
||||
</gwbuild>
|
||||
390
apps/aqhome-nodes/db.c
Normal file
390
apps/aqhome-nodes/db.c
Normal file
@@ -0,0 +1,390 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "./db.h"
|
||||
#include "./server_p.h"
|
||||
|
||||
#include "aqhome/aqhome.h"
|
||||
#include "aqhome/msg/node/m_node.h"
|
||||
#include "aqhome/msg/node/m_sendstats.h"
|
||||
#include "aqhome/msg/node/m_recvstats.h"
|
||||
#include "aqhome/msg/node/m_value.h"
|
||||
#include "aqhome/msg/node/m_addr.h"
|
||||
#include "aqhome/msg/node/m_device.h"
|
||||
#include "aqhome/msg/node/m_flashready.h"
|
||||
|
||||
#include "aqhome/data/value.h"
|
||||
#include "aqhome/msg/ipc/data/m_ipcd.h"
|
||||
#include "aqhome/msg/ipc/data/m_ipcd_values.h"
|
||||
#include "aqhome/ipc2/endpoint.h"
|
||||
|
||||
#include <gwenhywfar/gwenhywfar.h>
|
||||
#include <gwenhywfar/args.h>
|
||||
#include <gwenhywfar/debug.h>
|
||||
#include <gwenhywfar/timestamp.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static void _handleMsgValue(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg);
|
||||
static void _handleAddressMsg(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg);
|
||||
static void _handleMsgComSendStat(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg);
|
||||
static void _handleMsgComRecvStat(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg);
|
||||
static void _handleMsgDevice(AQH_OBJECT *o, AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg);
|
||||
static void _handleMsgFlashReady(AQH_OBJECT *o, AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg);
|
||||
|
||||
static AQH_NODE_INFO *_getOrCreateNodeAndUpdateUidAddr(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg, uint32_t uid);
|
||||
static void _updateTimestampLastChange(AQH_NODE_INFO *ni);
|
||||
static void _assignDeviceId(AQH_OBJECT *o, AQH_NODE_INFO *ni, uint32_t uid);
|
||||
|
||||
static void _announceNodeValues(AQH_OBJECT *o, AQH_NODE_SERVER *xo, const AQH_NODE_INFO *ni);
|
||||
static void _setDeviceName(AQH_VALUE *value, uint32_t uid);
|
||||
static void _announceValue(AQH_NODE_SERVER *xo, uint32_t uid, const AQHNODE_VALUE *v);
|
||||
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* implementations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void AQH_NodeServer_NodeMsgToDb(AQH_OBJECT *o, const AQH_MESSAGE *msg)
|
||||
{
|
||||
AQH_NODE_SERVER *xo;
|
||||
|
||||
xo=AQH_NodeServer_GetServerData(o);
|
||||
if (xo) {
|
||||
uint8_t msgType;
|
||||
|
||||
msgType=AQH_NodeMessage_GetMsgType(msg);
|
||||
|
||||
switch(msgType) {
|
||||
case AQH_MSG_TYPE_COMSENDSTATS: _handleMsgComSendStat(xo, msg); break;
|
||||
case AQH_MSG_TYPE_COMRECVSTATS: _handleMsgComRecvStat(xo, msg); break;
|
||||
case AQH_MSG_TYPE_VALUE_REPORT: _handleMsgValue(xo, msg); break;
|
||||
case AQH_MSG_TYPE_NEED_ADDRESS: _handleAddressMsg(xo, msg); break;
|
||||
case AQH_MSG_TYPE_CLAIM_ADDRESS: _handleAddressMsg(xo, msg); break;
|
||||
case AQH_MSG_TYPE_HAVE_ADDRESS: _handleAddressMsg(xo, msg); break;
|
||||
case AQH_MSG_TYPE_DEVICE: _handleMsgDevice(o, xo, msg); break;
|
||||
case AQH_MSG_TYPE_FLASH_READY: _handleMsgFlashReady(o, xo, msg); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQH_NodeServer_WriteNodeDb(AQH_OBJECT *o)
|
||||
{
|
||||
AQH_NODE_SERVER *xo;
|
||||
|
||||
xo=AQH_NodeServer_GetServerData(o);
|
||||
if (xo && xo->dbFile) {
|
||||
GWEN_DB_NODE *dbNodeDb;
|
||||
|
||||
AQH_NodeDb_ClearModified(xo->nodeDb);
|
||||
dbNodeDb=GWEN_DB_Group_new("nodeDb");
|
||||
AQH_NodeDb_toDb(xo->nodeDb, dbNodeDb);
|
||||
GWEN_DB_WriteFile(dbNodeDb, xo->dbFile, GWEN_DB_FLAGS_DEFAULT);
|
||||
GWEN_DB_Group_free(dbNodeDb);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _handleMsgValue(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg)
|
||||
{
|
||||
AQH_NODE_INFO *ni;
|
||||
uint32_t uid;
|
||||
|
||||
uid=AQH_ValueMessage_GetUid(msg);
|
||||
ni=_getOrCreateNodeAndUpdateUidAddr(xo, msg, uid);
|
||||
if (ni==NULL) {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _handleAddressMsg(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg)
|
||||
{
|
||||
AQH_NODE_INFO *ni;
|
||||
uint32_t uid;
|
||||
|
||||
uid=AQH_AddrMessage_GetUid(msg);
|
||||
ni=_getOrCreateNodeAndUpdateUidAddr(xo, msg, uid);
|
||||
if (ni==NULL) {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _handleMsgComSendStat(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg)
|
||||
{
|
||||
AQH_NODE_INFO *ni;
|
||||
uint32_t uid;
|
||||
|
||||
uid=AQH_SendStatsMessage_GetUid(msg);
|
||||
ni=_getOrCreateNodeAndUpdateUidAddr(xo, msg, uid);
|
||||
if (ni==NULL) {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
|
||||
}
|
||||
AQH_NodeInfo_SetStatsPacketsOut(ni, AQH_SendStatsMessage_GetPacketsOut(msg));
|
||||
AQH_NodeInfo_SetStatsCollisions(ni, AQH_SendStatsMessage_GetCollisions(msg));
|
||||
AQH_NodeInfo_SetStatsBusy(ni, AQH_SendStatsMessage_GetBusyErrors(msg));
|
||||
AQH_NodeDb_SetModified(xo->nodeDb);
|
||||
_updateTimestampLastChange(ni);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _handleMsgComRecvStat(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg)
|
||||
{
|
||||
AQH_NODE_INFO *ni;
|
||||
uint32_t uid;
|
||||
|
||||
uid=AQH_RecvStatsMessage_GetUid(msg);
|
||||
ni=_getOrCreateNodeAndUpdateUidAddr(xo, msg, uid);
|
||||
if (ni==NULL) {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
|
||||
}
|
||||
AQH_NodeInfo_SetStatsPacketsIn(ni, AQH_RecvStatsMessage_GetPacketsIn(msg));
|
||||
AQH_NodeInfo_SetStatsCrcErrors(ni, AQH_RecvStatsMessage_GetCrcErrors(msg));
|
||||
AQH_NodeInfo_SetStatsIoErrors(ni, AQH_RecvStatsMessage_GetIoErrors(msg));
|
||||
AQH_NodeDb_SetModified(xo->nodeDb);
|
||||
_updateTimestampLastChange(ni);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _handleMsgDevice(AQH_OBJECT *o, AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg)
|
||||
{
|
||||
AQH_NODE_INFO *ni;
|
||||
uint32_t uid;
|
||||
|
||||
uid=AQH_DeviceMessage_GetUid(msg);
|
||||
ni=_getOrCreateNodeAndUpdateUidAddr(xo, msg, uid);
|
||||
if (ni) {
|
||||
const char *s;
|
||||
|
||||
AQH_NodeInfo_SetManufacturer(ni, AQH_DeviceMessage_GetManufacturer(msg));
|
||||
AQH_NodeInfo_SetDeviceType(ni, AQH_DeviceMessage_GetDeviceType(msg));
|
||||
AQH_NodeInfo_SetDeviceVersion(ni, (AQH_DeviceMessage_GetDeviceVersion(msg)<<8)+AQH_DeviceMessage_GetDeviceRevision(msg));
|
||||
AQH_NodeInfo_SetFirmwareVersion(ni,
|
||||
(AQH_DeviceMessage_GetFirmwareVariant(msg)<<24) |
|
||||
(AQH_DeviceMessage_GetFirmwareVersionMajor(msg)<<16) |
|
||||
(AQH_DeviceMessage_GetFirmwareVersionMinor(msg)<<8) |
|
||||
AQH_DeviceMessage_GetFirmwareVersionPatchlevel(msg));
|
||||
s=AQH_NodeInfo_GetDeviceId(ni);
|
||||
if (!(s && *s))
|
||||
_assignDeviceId(o, ni, uid);
|
||||
_updateTimestampLastChange(ni);
|
||||
AQH_NodeDb_SetModified(xo->nodeDb);
|
||||
if (uid!=0x00000000L && uid!=0xffffffff)
|
||||
_announceNodeValues(o, xo, ni);
|
||||
}
|
||||
else {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _announceNodeValues(AQH_OBJECT *o, AQH_NODE_SERVER *xo, const AQH_NODE_INFO *ni)
|
||||
{
|
||||
const char *devName;
|
||||
|
||||
devName=AQH_NodeInfo_GetDeviceId(ni);
|
||||
if (devName) {
|
||||
const AQHNODE_DEVICE *devInfo;
|
||||
|
||||
devInfo=AQH_NodeServer_GetDeviceDefByName(o, devName);
|
||||
if (devInfo) {
|
||||
const AQHNODE_VALUE_LIST *valueList;
|
||||
|
||||
valueList=AQHNODE_Device_GetValueList(devInfo);
|
||||
if (valueList) {
|
||||
const AQHNODE_VALUE *v;
|
||||
|
||||
v=AQHNODE_Value_List_First(valueList);
|
||||
while(v) {
|
||||
DBG_INFO(NULL, "Announcing value \"%08x/%s\" (%d=%s)",
|
||||
AQH_NodeInfo_GetUid(ni), AQHNODE_Value_GetName(v),
|
||||
AQHNODE_Value_GetModality(v),
|
||||
AQH_ValueModality_toString(AQHNODE_Value_GetModality(v)));
|
||||
_announceValue(xo, AQH_NodeInfo_GetUid(ni), v);
|
||||
v=AQHNODE_Value_List_Next(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "Node type \"%s\" not in database", devName);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "Node type not in database");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _setDeviceName(AQH_VALUE *value, uint32_t uid)
|
||||
{
|
||||
GWEN_BUFFER *buf;
|
||||
|
||||
buf=GWEN_Buffer_new(0, 64, 0, 1);
|
||||
GWEN_Buffer_AppendArgs(buf, "%08x", uid);
|
||||
AQH_Value_SetDeviceName(value, GWEN_Buffer_GetStart(buf));
|
||||
GWEN_Buffer_free(buf);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _announceValue(AQH_NODE_SERVER *xo, uint32_t uid, const AQHNODE_VALUE *v)
|
||||
{
|
||||
AQH_VALUE *value;
|
||||
AQH_MESSAGE *msg;
|
||||
|
||||
value=AQH_Value_new();
|
||||
_setDeviceName(value, uid);
|
||||
AQH_Value_SetDriver(value, "nodes");
|
||||
AQH_Value_SetName(value, AQHNODE_Value_GetName(v));
|
||||
AQH_Value_SetValueUnits(value, AQHNODE_Value_GetValueUnits(v));
|
||||
AQH_Value_SetValueType(value, AQHNODE_Value_GetValueType(v));
|
||||
AQH_Value_SetModality(value, AQHNODE_Value_GetModality(v));
|
||||
|
||||
msg=AQH_IpcdMessageValues_newForOne(AQH_MSGTYPE_IPC_DATA_ANNOUNCEVALUE,
|
||||
AQH_Endpoint_GetNextMessageId(xo->brokerEndpoint), 0,
|
||||
0, value);
|
||||
AQH_Endpoint_AddMsgOut(xo->brokerEndpoint, msg);
|
||||
AQH_Value_free(value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _handleMsgFlashReady(AQH_OBJECT *o, AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg)
|
||||
{
|
||||
AQH_NODE_INFO *ni;
|
||||
uint32_t uid;
|
||||
|
||||
uid=AQH_FlashReadyMessage_GetUid(msg);
|
||||
ni=_getOrCreateNodeAndUpdateUidAddr(xo, msg, uid);
|
||||
if (ni) {
|
||||
const char *s;
|
||||
|
||||
AQH_NodeInfo_SetManufacturer(ni, AQH_FlashReadyMessage_GetManufacturer(msg));
|
||||
AQH_NodeInfo_SetDeviceType(ni, AQH_FlashReadyMessage_GetDeviceType(msg));
|
||||
AQH_NodeInfo_SetDeviceVersion(ni, (AQH_FlashReadyMessage_GetDeviceVersion(msg)<<8)+AQH_FlashReadyMessage_GetDeviceRevision(msg));
|
||||
AQH_NodeInfo_SetFirmwareVersion(ni,
|
||||
(AQH_FlashReadyMessage_GetFirmwareVariant(msg)<<24) |
|
||||
(AQH_FlashReadyMessage_GetFirmwareVersionMajor(msg)<<16) |
|
||||
(AQH_FlashReadyMessage_GetFirmwareVersionMinor(msg)<<8) |
|
||||
AQH_FlashReadyMessage_GetFirmwareVersionPatchlevel(msg));
|
||||
s=AQH_NodeInfo_GetDeviceId(ni);
|
||||
if (!(s && *s))
|
||||
_assignDeviceId(o, ni, uid);
|
||||
_updateTimestampLastChange(ni);
|
||||
AQH_NodeDb_SetModified(xo->nodeDb);
|
||||
if (uid!=0x00000000L && uid!=0xffffffff)
|
||||
_announceNodeValues(o, xo, ni);
|
||||
}
|
||||
else {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQH_NODE_INFO *_getOrCreateNodeAndUpdateUidAddr(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg, uint32_t uid)
|
||||
{
|
||||
uint8_t busAddr;
|
||||
AQH_NODE_INFO *ni;
|
||||
|
||||
busAddr=AQH_NodeMessage_GetSourceAddress(msg);
|
||||
ni=AQH_NodeDb_GetNodeInfoByUid(xo->nodeDb, uid);
|
||||
if (ni) {
|
||||
uint8_t storedBusAddr;
|
||||
|
||||
storedBusAddr=AQH_NodeInfo_GetBusAddress(ni);
|
||||
if (busAddr!=0 && storedBusAddr!=busAddr) {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Changed busaddr for %08x from %02x to %02x", uid, storedBusAddr, busAddr);
|
||||
AQH_NodeInfo_SetBusAddress(ni, busAddr);
|
||||
_updateTimestampLastChange(ni);
|
||||
AQH_NodeDb_SetModified(xo->nodeDb);
|
||||
}
|
||||
}
|
||||
else {
|
||||
int rv;
|
||||
|
||||
ni=AQH_NodeInfo_new();
|
||||
AQH_NodeInfo_SetBusAddress(ni, busAddr);
|
||||
AQH_NodeInfo_SetUid(ni, uid);
|
||||
_updateTimestampLastChange(ni);
|
||||
rv=AQH_NodeDb_AddNodeInfo(xo->nodeDb, ni);
|
||||
if (rv<0) {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
|
||||
AQH_NodeInfo_free(ni);
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Added node %08x (%02x)", uid, busAddr);
|
||||
}
|
||||
}
|
||||
return ni;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _assignDeviceId(AQH_OBJECT *o, AQH_NODE_INFO *ni, uint32_t uid)
|
||||
{
|
||||
const AQHNODE_DEVICE *dev;
|
||||
|
||||
dev=AQH_NodeServer_FindDeviceDef(o,
|
||||
AQH_NodeInfo_GetManufacturer(ni),
|
||||
AQH_NodeInfo_GetDeviceType(ni),
|
||||
AQH_NodeInfo_GetDeviceVersion(ni));
|
||||
if (dev==NULL) {
|
||||
DBG_ERROR(NULL,
|
||||
"Unknown NODE device encountered (%08x, %04x, %04x)",
|
||||
AQH_NodeInfo_GetManufacturer(ni),
|
||||
AQH_NodeInfo_GetDeviceType(ni),
|
||||
AQH_NodeInfo_GetDeviceVersion(ni));
|
||||
}
|
||||
else {
|
||||
const char *s;
|
||||
|
||||
s=AQHNODE_Device_GetName(dev);
|
||||
DBG_ERROR(NULL, "Found device \"%s\" (%08x)", s?s:"<no name>", uid);
|
||||
AQH_NodeInfo_SetDeviceId(ni, s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _updateTimestampLastChange(AQH_NODE_INFO *ni)
|
||||
{
|
||||
GWEN_TIMESTAMP *t;
|
||||
|
||||
t=GWEN_Timestamp_NowInLocalTime();
|
||||
AQH_NodeInfo_SetTimestampLastChange(ni, t);
|
||||
GWEN_Timestamp_free(t);
|
||||
}
|
||||
|
||||
|
||||
|
||||
27
apps/aqhome-nodes/db.h
Normal file
27
apps/aqhome-nodes/db.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOMED_DB_H
|
||||
#define AQHOMED_DB_H
|
||||
|
||||
|
||||
#include "./server.h"
|
||||
|
||||
#include "aqhome/events2/object.h"
|
||||
#include "aqhome/ipc2/message.h"
|
||||
|
||||
|
||||
void AQH_NodeServer_NodeMsgToDb(AQH_OBJECT *o, const AQH_MESSAGE *msg);
|
||||
|
||||
void AQH_NodeServer_WriteNodeDb(AQH_OBJECT *o);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
128
apps/aqhome-nodes/devicesdump.c
Normal file
128
apps/aqhome-nodes/devicesdump.c
Normal file
@@ -0,0 +1,128 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2024 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "./devicesdump.h"
|
||||
#include "./server_p.h"
|
||||
#include "aqhome/aqhome.h"
|
||||
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static void _dumpDevice(const AQHNODE_DEVICE *dev, GWEN_BUFFER *dbuf, int indent);
|
||||
static void _dumpValue(const AQHNODE_VALUE *value, GWEN_BUFFER *dbuf, int indent);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* implementations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void AQH_NodeServer_DumpDevices(const AQHNODE_DEVICE_LIST *devList, GWEN_BUFFER *dbuf)
|
||||
{
|
||||
|
||||
if (devList && AQHNODE_Device_List_GetCount(devList)) {
|
||||
const AQHNODE_DEVICE *dev;
|
||||
|
||||
GWEN_Buffer_AppendString(dbuf, "Devices:\n");
|
||||
dev=AQHNODE_Device_List_First(devList);
|
||||
while(dev) {
|
||||
_dumpDevice(dev, dbuf, 2);
|
||||
dev=AQHNODE_Device_List_Next(dev);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _dumpDevice(const AQHNODE_DEVICE *dev, GWEN_BUFFER *dbuf, int indent)
|
||||
{
|
||||
const char *name;
|
||||
const char *driver;
|
||||
uint32_t manufacturer;
|
||||
uint16_t deviceType;
|
||||
uint16_t deviceVersion;
|
||||
const AQHNODE_VALUE_LIST *valueList;
|
||||
|
||||
name=AQHNODE_Device_GetName(dev);
|
||||
driver=AQHNODE_Device_GetDriver(dev);
|
||||
manufacturer=AQHNODE_Device_GetManufacturer(dev);
|
||||
deviceType=AQHNODE_Device_GetDeviceType(dev);
|
||||
deviceVersion=AQHNODE_Device_GetDeviceVersion(dev);
|
||||
|
||||
GWEN_Buffer_FillWithBytes(dbuf, ' ', indent);
|
||||
GWEN_Buffer_AppendArgs(dbuf, "Device: %s (%s, %08x, %04x, %04x)\n",
|
||||
name?name:"<empty name>",
|
||||
driver?driver:"<empty driver>",
|
||||
manufacturer,
|
||||
deviceType,
|
||||
deviceVersion);
|
||||
valueList=AQHNODE_Device_GetValueList(dev);
|
||||
if (valueList && AQHNODE_Value_List_GetCount(valueList)) {
|
||||
const AQHNODE_VALUE *value;
|
||||
|
||||
value=AQHNODE_Value_List_First(valueList);
|
||||
while(value) {
|
||||
_dumpValue(value, dbuf, indent+2);
|
||||
value=AQHNODE_Value_List_Next(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _dumpValue(const AQHNODE_VALUE *value, GWEN_BUFFER *dbuf, int indent)
|
||||
{
|
||||
int id;
|
||||
const char *name;
|
||||
const char *descr;
|
||||
int valueType;
|
||||
int dataType;
|
||||
int modality;
|
||||
const char *units;
|
||||
int denom;
|
||||
|
||||
id=AQHNODE_Value_GetId(value);
|
||||
name=AQHNODE_Value_GetName(value);
|
||||
descr=AQHNODE_Value_GetDescription(value);
|
||||
valueType=AQHNODE_Value_GetValueType(value);
|
||||
dataType=AQHNODE_Value_GetDataType(value);
|
||||
modality=AQHNODE_Value_GetModality(value);
|
||||
units=AQHNODE_Value_GetValueUnits(value);
|
||||
denom=AQHNODE_Value_GetDenom(value);
|
||||
|
||||
GWEN_Buffer_FillWithBytes(dbuf, ' ', indent);
|
||||
GWEN_Buffer_AppendArgs(dbuf, "Value: %d[%02x] (%s, %s, %s, %s, %s, %s, %d)\n",
|
||||
id, id,
|
||||
name?name:"<empty name>",
|
||||
AQH_ValueType_toString(valueType),
|
||||
AQH_ValueDataType_toString(dataType),
|
||||
AQH_ValueModality_toString(modality),
|
||||
units?units:"<empty units>",
|
||||
descr?descr:"<empty descr>",
|
||||
denom);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
24
apps/aqhome-nodes/devicesdump.h
Normal file
24
apps/aqhome-nodes/devicesdump.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2024 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_NODES_DEVICESDUMP_H
|
||||
#define AQHOME_NODES_DEVICESDUMP_H
|
||||
|
||||
|
||||
#include "./server.h"
|
||||
#include "aqhome-nodes/types/device.h"
|
||||
|
||||
|
||||
|
||||
void AQH_NodeServer_DumpDevices(const AQHNODE_DEVICE_LIST *devList, GWEN_BUFFER *dbuf);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
397
apps/aqhome-nodes/devicesread.c
Normal file
397
apps/aqhome-nodes/devicesread.c
Normal file
@@ -0,0 +1,397 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2024 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "./devicesread.h"
|
||||
#include "./server_p.h"
|
||||
#include "aqhome/aqhome.h"
|
||||
|
||||
#include <gwenhywfar/directory.h>
|
||||
#include <gwenhywfar/debug.h>
|
||||
#include <gwenhywfar/stringlist.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* defines
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static int _readDeviceFileToList(const char *sFilename, AQHNODE_DEVICE_LIST *deviceList);
|
||||
static AQHNODE_DEVICE_LIST *_readDeviceFiles(const GWEN_STRINGLIST *sl);
|
||||
static int _readXmlDevices(GWEN_XMLNODE *deviceListNode, AQHNODE_DEVICE_LIST *deviceList);
|
||||
static AQHNODE_DEVICE *_readXmlDevice(GWEN_XMLNODE *deviceNode);
|
||||
static AQHNODE_VALUE_LIST *_readXmlValueList(GWEN_XMLNODE *valuesNode);
|
||||
static AQHNODE_VALUE *_readXmlValue(GWEN_XMLNODE *valueNode);
|
||||
static int _readManufacturer(AQHNODE_DEVICE *device, GWEN_XMLNODE *deviceNode);
|
||||
static int _readDeviceType(AQHNODE_DEVICE *device, GWEN_XMLNODE *deviceNode);
|
||||
static int _readDeviceVersion(AQHNODE_DEVICE *device, GWEN_XMLNODE *deviceNode);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* implementations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
AQHNODE_DEVICE_LIST *AQH_NodeServer_ReadDeviceFile(const char *sFilename)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv=GWEN_Directory_GetPath(sFilename, GWEN_PATH_FLAGS_CHECKROOT | GWEN_PATH_FLAGS_PATHMUSTEXIST | GWEN_PATH_FLAGS_VARIABLE);
|
||||
if (rv<0) {
|
||||
DBG_ERROR(NULL, "File \"%s\" does not exists, writing later", sFilename);
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
AQHNODE_DEVICE_LIST *deviceList;
|
||||
|
||||
deviceList=AQHNODE_Device_List_new();
|
||||
rv=_readDeviceFileToList(sFilename, deviceList);
|
||||
if (rv<0) {
|
||||
DBG_ERROR(NULL, "File \"%s\" not found", sFilename);
|
||||
AQHNODE_Device_List_free(deviceList);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (AQHNODE_Device_List_GetCount(deviceList)<1) {
|
||||
AQHNODE_Device_List_free(deviceList);
|
||||
return NULL;
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHNODE_DEVICE_LIST *AQH_NodeServer_ReadDataDeviceFiles()
|
||||
{
|
||||
GWEN_STRINGLIST *sl;
|
||||
|
||||
sl=AQH_GetListOfMatchingDataFiles("aqhome/devices/nodes", "*.xml");
|
||||
if (sl) {
|
||||
AQHNODE_DEVICE_LIST *deviceList;
|
||||
|
||||
deviceList=_readDeviceFiles(sl);
|
||||
GWEN_StringList_free(sl);
|
||||
if (deviceList==NULL) {
|
||||
DBG_INFO(NULL, "Error reading data device files");
|
||||
return NULL;
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "No data device files");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHNODE_DEVICE_LIST *_readDeviceFiles(const GWEN_STRINGLIST *sl)
|
||||
{
|
||||
GWEN_STRINGLISTENTRY *se;
|
||||
AQHNODE_DEVICE_LIST *deviceList;
|
||||
|
||||
deviceList=AQHNODE_Device_List_new();
|
||||
se=GWEN_StringList_FirstEntry(sl);
|
||||
while(se) {
|
||||
const char *s;
|
||||
|
||||
s=GWEN_StringListEntry_Data(se);
|
||||
if (s && *s) {
|
||||
int rv;
|
||||
|
||||
DBG_INFO(NULL, "Reading device file \"%s\"", s);
|
||||
rv=_readDeviceFileToList(s, deviceList);
|
||||
if (rv<0 && rv!=GWEN_ERROR_NO_DATA) {
|
||||
DBG_WARN(NULL, "Error reading device file \"%s\" (%d), ignoring", s, rv);
|
||||
}
|
||||
}
|
||||
se=GWEN_StringListEntry_Next(se);
|
||||
}
|
||||
|
||||
if (AQHNODE_Device_List_GetCount(deviceList)<1) {
|
||||
AQHNODE_Device_List_free(deviceList);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _readDeviceFileToList(const char *sFilename, AQHNODE_DEVICE_LIST *deviceList)
|
||||
{
|
||||
GWEN_XMLNODE *rootNode;
|
||||
GWEN_XMLNODE *deviceListNode;
|
||||
int rv;
|
||||
|
||||
rootNode=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, NULL);
|
||||
rv=GWEN_XML_ReadFile(rootNode, sFilename, GWEN_XML_FLAGS_DEFAULT);
|
||||
if (rv<0) {
|
||||
DBG_ERROR(NULL, "Error reading XML file \"%s\": %d", sFilename, rv);
|
||||
GWEN_XMLNode_free(rootNode);
|
||||
return rv;
|
||||
}
|
||||
|
||||
deviceListNode=GWEN_XMLNode_FindFirstTag(rootNode, "devices", NULL, NULL);
|
||||
if (deviceListNode==NULL)
|
||||
deviceListNode=rootNode;
|
||||
|
||||
rv=_readXmlDevices(deviceListNode, deviceList);
|
||||
if (rv<0 && rv!=GWEN_ERROR_NO_DATA) {
|
||||
DBG_ERROR(AQH_LOGDOMAIN, "Error reading devices from file \"%s\" (%d)", sFilename, rv);
|
||||
GWEN_XMLNode_free(rootNode);
|
||||
return rv;
|
||||
}
|
||||
|
||||
GWEN_XMLNode_free(rootNode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _readXmlDevices(GWEN_XMLNODE *deviceListNode, AQHNODE_DEVICE_LIST *deviceList)
|
||||
{
|
||||
GWEN_XMLNODE *deviceNode;
|
||||
|
||||
deviceNode=GWEN_XMLNode_FindFirstTag(deviceListNode, "device", NULL, NULL);
|
||||
if (deviceNode) {
|
||||
while(deviceNode) {
|
||||
const char *driverName;
|
||||
|
||||
driverName=GWEN_XMLNode_GetProperty(deviceNode, "driver", NULL);
|
||||
if (driverName && *driverName && strcasecmp(driverName, "nodes")==0) {
|
||||
AQHNODE_DEVICE *device;
|
||||
|
||||
device=_readXmlDevice(deviceNode);
|
||||
if (device==NULL) {
|
||||
DBG_INFO(NULL, "Error reading device from XML");
|
||||
return GWEN_ERROR_BAD_DATA;
|
||||
}
|
||||
else {
|
||||
const char *sDeviceName;
|
||||
|
||||
sDeviceName=AQHNODE_Device_GetName(device);
|
||||
if (sDeviceName && *sDeviceName) {
|
||||
DBG_ERROR(NULL, "Adding device type \"%s\" to list", sDeviceName?sDeviceName:"<no type name>");
|
||||
AQHNODE_Device_List_Add(device, deviceList);
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Device with no name, not adding");
|
||||
AQHNODE_Device_free(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "Device is not an NODES device, ignoring");
|
||||
}
|
||||
deviceNode=GWEN_XMLNode_FindNextTag(deviceNode, "device", NULL, NULL);
|
||||
} /* while */
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "No <device> element found");
|
||||
return GWEN_ERROR_NO_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHNODE_DEVICE *_readXmlDevice(GWEN_XMLNODE *deviceNode)
|
||||
{
|
||||
AQHNODE_DEVICE *device;
|
||||
GWEN_XMLNODE *valuesNode;
|
||||
int rv;
|
||||
|
||||
device=AQHNODE_Device_new();
|
||||
AQHNODE_Device_SetName(device, GWEN_XMLNode_GetProperty(deviceNode, "name", NULL));
|
||||
AQHNODE_Device_SetDriver(device, GWEN_XMLNode_GetProperty(deviceNode, "driver", NULL));
|
||||
rv=_readManufacturer(device, deviceNode);
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "here (%d)", rv);
|
||||
AQHNODE_Device_free(device);
|
||||
return NULL;
|
||||
}
|
||||
rv=_readDeviceType(device, deviceNode);
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "here (%d)", rv);
|
||||
AQHNODE_Device_free(device);
|
||||
return NULL;
|
||||
}
|
||||
rv=_readDeviceVersion(device, deviceNode);
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "here (%d)", rv);
|
||||
AQHNODE_Device_free(device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* read values */
|
||||
valuesNode=GWEN_XMLNode_FindFirstTag(deviceNode, "values", NULL, NULL);
|
||||
if (valuesNode) {
|
||||
AQHNODE_VALUE_LIST *valueList;
|
||||
|
||||
valueList=_readXmlValueList(valuesNode);
|
||||
if (valueList==NULL) {
|
||||
DBG_INFO(NULL, "here");
|
||||
AQHNODE_Device_free(device);
|
||||
return NULL;
|
||||
}
|
||||
AQHNODE_Device_SetValueList(device, valueList);
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "No <values> element");
|
||||
AQHNODE_Device_free(device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHNODE_VALUE_LIST *_readXmlValueList(GWEN_XMLNODE *valuesNode)
|
||||
{
|
||||
AQHNODE_VALUE_LIST *valueList;
|
||||
GWEN_XMLNODE *node;
|
||||
|
||||
valueList=AQHNODE_Value_List_new();
|
||||
node=GWEN_XMLNode_FindFirstTag(valuesNode, "value", NULL, NULL);
|
||||
while(node) {
|
||||
AQHNODE_VALUE *value;
|
||||
|
||||
value=_readXmlValue(node);
|
||||
if (value)
|
||||
AQHNODE_Value_List_Add(value, valueList);
|
||||
else {
|
||||
DBG_INFO(NULL, "Error reading <value> element");
|
||||
AQHNODE_Value_List_free(valueList);
|
||||
return NULL;
|
||||
}
|
||||
node=GWEN_XMLNode_FindNextTag(node, "value", NULL, NULL);
|
||||
}
|
||||
if (AQHNODE_Value_List_GetCount(valueList)<1) {
|
||||
DBG_INFO(NULL, "No <value> element in <values> element");
|
||||
AQHNODE_Value_List_free(valueList);
|
||||
return NULL;
|
||||
}
|
||||
return valueList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHNODE_VALUE *_readXmlValue(GWEN_XMLNODE *valueNode)
|
||||
{
|
||||
AQHNODE_VALUE *value;
|
||||
|
||||
value=AQHNODE_Value_new();
|
||||
AQHNODE_Value_SetName(value, GWEN_XMLNode_GetProperty(valueNode, "name", NULL));
|
||||
AQHNODE_Value_SetId(value, GWEN_XMLNode_GetIntProperty(valueNode, "id", 0));
|
||||
AQHNODE_Value_SetValueUnits(value, GWEN_XMLNode_GetProperty(valueNode, "units", NULL));
|
||||
AQHNODE_Value_SetValueType(value, AQH_ValueType_fromString(GWEN_XMLNode_GetProperty(valueNode, "type", NULL)));
|
||||
AQHNODE_Value_SetDataType(value, AQH_ValueDataType_fromString(GWEN_XMLNode_GetProperty(valueNode, "dataType", NULL)));
|
||||
AQHNODE_Value_SetModality(value, AQH_ValueModality_fromString(GWEN_XMLNode_GetProperty(valueNode, "modality", NULL)));
|
||||
AQHNODE_Value_SetDenom(value, GWEN_XMLNode_GetIntProperty(valueNode, "denom", 1));
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _readManufacturer(AQHNODE_DEVICE *device, GWEN_XMLNODE *deviceNode)
|
||||
{
|
||||
const char *s;
|
||||
uint32_t v;
|
||||
|
||||
s=GWEN_XMLNode_GetCharValue(deviceNode, "manufacturer", NULL);
|
||||
if (!(s && *s)) {
|
||||
DBG_ERROR(NULL, "Missing manufacturer");
|
||||
return GWEN_ERROR_BAD_DATA;
|
||||
}
|
||||
|
||||
if (strlen(s)>4) {
|
||||
DBG_ERROR(NULL, "Bad manufacturer string (too long): \"%s\"", s);
|
||||
return GWEN_ERROR_BAD_DATA;
|
||||
}
|
||||
|
||||
v=0;
|
||||
while(*s) {
|
||||
v<<=8;
|
||||
v|=*s & 0xff;
|
||||
s++;
|
||||
}
|
||||
v=((v>>24) & 0x000000ffL) |
|
||||
((v>>8) & 0x0000ff00L) |
|
||||
((v<<8) & 0x00ff0000L) |
|
||||
((v<<24) & 0xff000000L);
|
||||
|
||||
AQHNODE_Device_SetManufacturer(device, v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _readDeviceType(AQHNODE_DEVICE *device, GWEN_XMLNODE *deviceNode)
|
||||
{
|
||||
const char *s;
|
||||
uint16_t v;
|
||||
|
||||
s=GWEN_XMLNode_GetCharValue(deviceNode, "devicetype", NULL);
|
||||
if (!(s && *s)) {
|
||||
DBG_ERROR(NULL, "Missing device type");
|
||||
return GWEN_ERROR_BAD_DATA;
|
||||
}
|
||||
|
||||
if (strlen(s)>2) {
|
||||
DBG_ERROR(NULL, "Bad devicetype string (too long): \"%s\"", s);
|
||||
return GWEN_ERROR_BAD_DATA;
|
||||
}
|
||||
|
||||
v=0;
|
||||
while(*s) {
|
||||
v<<=8;
|
||||
v|=*s & 0xff;
|
||||
s++;
|
||||
}
|
||||
|
||||
AQHNODE_Device_SetDeviceType(device, v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _readDeviceVersion(AQHNODE_DEVICE *device, GWEN_XMLNODE *deviceNode)
|
||||
{
|
||||
int v;
|
||||
|
||||
v=GWEN_XMLNode_GetIntValue(deviceNode, "deviceversion", -1);
|
||||
if (v<0) {
|
||||
DBG_ERROR(NULL, "Missing device version");
|
||||
return GWEN_ERROR_BAD_DATA;
|
||||
}
|
||||
|
||||
AQHNODE_Device_SetDeviceVersion(device, v<<8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
25
apps/aqhome-nodes/devicesread.h
Normal file
25
apps/aqhome-nodes/devicesread.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_NODES_DEVICESREAD_H
|
||||
#define AQHOME_NODES_DEVICESREAD_H
|
||||
|
||||
|
||||
#include "./server.h"
|
||||
#include "aqhome-nodes/types/device.h"
|
||||
|
||||
|
||||
|
||||
AQHNODE_DEVICE_LIST *AQH_NodeServer_ReadDeviceFile(const char *sFilename);
|
||||
AQHNODE_DEVICE_LIST *AQH_NodeServer_ReadDataDeviceFiles(void);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
251
apps/aqhome-nodes/main.c
Normal file
251
apps/aqhome-nodes/main.c
Normal file
@@ -0,0 +1,251 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <aqhome/api.h>
|
||||
#include <aqhome/aqhome.h>
|
||||
|
||||
#include "./server.h"
|
||||
|
||||
#include <gwenhywfar/gwenhywfar.h>
|
||||
#include <gwenhywfar/logger.h>
|
||||
#include <gwenhywfar/cgui.h>
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
# include <signal.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* defines
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define I18N(msg) msg
|
||||
#define I18S(msg) msg
|
||||
|
||||
#define CONNCLEAN_INTERVAL_IN_SECS 2
|
||||
#define CONNCHECK_INTERVAL_IN_SECS 10
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static void _runService(AQH_OBJECT *aqh, AQH_EVENT_LOOP *eventLoop);
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
static int _setSignalHandlers(void);
|
||||
static int _setupSigAction(struct sigaction *sa, int sig);
|
||||
static void _signalHandler(int s);
|
||||
#endif
|
||||
static int _diffInSeconds(time_t t1, time_t t0);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* static vars
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
static struct sigaction saINT,saTERM, saHUP, saTSTP, saCONT, saPIPE;
|
||||
#endif
|
||||
|
||||
static int stopService=0;
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* implementations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int rv;
|
||||
AQH_EVENT_LOOP *eventLoop;
|
||||
AQH_OBJECT *aqh;
|
||||
GWEN_GUI *gui;
|
||||
|
||||
rv=GWEN_Init();
|
||||
if (rv) {
|
||||
fprintf(stderr, "ERROR: Unable to init Gwen.\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
GWEN_Logger_Open(0, "aqhome-nodes", 0, GWEN_LoggerType_Console, GWEN_LoggerFacility_User);
|
||||
GWEN_Logger_SetLevel(0, GWEN_LoggerLevel_Warning);
|
||||
|
||||
rv=_setSignalHandlers();
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "here (%d)", rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv=AQH_Init();
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "here (%d)", rv);
|
||||
return 2;
|
||||
}
|
||||
|
||||
gui=GWEN_Gui_CGui_new();
|
||||
GWEN_Gui_SetGui(gui);
|
||||
|
||||
eventLoop=AQH_EventLoop_new();
|
||||
aqh=AQH_NodeServer_new(eventLoop);
|
||||
rv=AQH_NodeServer_Init(aqh, argc, argv);
|
||||
if (rv<0) {
|
||||
if (rv==GWEN_ERROR_CLOSE)
|
||||
return 1;
|
||||
DBG_INFO(NULL, "here (%d)", rv);
|
||||
return 2;
|
||||
}
|
||||
|
||||
_runService(aqh, eventLoop);
|
||||
|
||||
//AQH_NodeServer_Fini(aqh);
|
||||
AQH_Object_free(aqh);
|
||||
|
||||
GWEN_Gui_SetGui(NULL);
|
||||
GWEN_Gui_free(gui);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _runService(AQH_OBJECT *aqh, AQH_EVENT_LOOP *eventLoop)
|
||||
{
|
||||
time_t timeStart;
|
||||
int timeout;
|
||||
time_t timeLastConnectionCleanup;
|
||||
time_t timeLastConnCheck;
|
||||
|
||||
timeout=AQH_NodeServer_GetTimeout(aqh);
|
||||
timeStart=time(NULL);
|
||||
timeLastConnectionCleanup=time(NULL);
|
||||
timeLastConnCheck=time(NULL);
|
||||
|
||||
while(!stopService) {
|
||||
time_t now;
|
||||
|
||||
AQH_EventLoop_Run(eventLoop, 2000);
|
||||
AQH_NodeServer_HandleTtyMsgs(aqh);
|
||||
AQH_NodeServer_HandleClientMsgs(aqh);
|
||||
AQH_NodeServer_HandleBrokerMsgs(aqh);
|
||||
|
||||
now=time(NULL);
|
||||
|
||||
if (_diffInSeconds(now, timeLastConnectionCleanup)>CONNCLEAN_INTERVAL_IN_SECS) {
|
||||
DBG_ERROR(NULL, "Cleanup connections");
|
||||
AQH_NodeServer_CleanupClients(aqh);
|
||||
timeLastConnectionCleanup=now;
|
||||
}
|
||||
|
||||
if (_diffInSeconds(now, timeLastConnCheck)>CONNCHECK_INTERVAL_IN_SECS) {
|
||||
DBG_ERROR(NULL, "Check connections");
|
||||
AQH_NodeServer_CheckBrokerConnection(aqh);
|
||||
AQH_NodeServer_CheckTtyConnection(aqh);
|
||||
timeLastConnCheck=now;
|
||||
}
|
||||
|
||||
if (timeout && (_diffInSeconds(now, timeStart)>timeout)) {
|
||||
DBG_INFO(NULL, "Timeout");
|
||||
break;
|
||||
}
|
||||
} /* while */
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _setSignalHandlers(void)
|
||||
{
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
int rv;
|
||||
|
||||
rv=_setupSigAction(&saINT, SIGINT);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
rv=_setupSigAction(&saTERM, SIGTERM);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
rv=_setupSigAction(&saHUP, SIGHUP);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
rv=_setupSigAction(&saPIPE, SIGPIPE);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
# ifdef SIGTSTP
|
||||
rv=_setupSigAction(&saTSTP, SIGTSTP);
|
||||
if (rv)
|
||||
return rv;
|
||||
# endif
|
||||
|
||||
# ifdef SIGCONT
|
||||
rv=_setupSigAction(&saCONT, SIGCONT);
|
||||
if (rv)
|
||||
return rv;
|
||||
# endif
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _setupSigAction(struct sigaction *sa, int sig)
|
||||
{
|
||||
sa->sa_handler=_signalHandler;
|
||||
sigemptyset(&sa->sa_mask);
|
||||
sa->sa_flags=0;
|
||||
if (sigaction(sig, sa, 0)) {
|
||||
DBG_ERROR(NULL, "Could not setup signal handler for signal %d", sig);
|
||||
return GWEN_ERROR_IO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _signalHandler(int s)
|
||||
{
|
||||
switch(s) {
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
case SIGHUP:
|
||||
DBG_WARN(0, "Received signal %d, stopping service in next loop.",s);
|
||||
stopService=1;
|
||||
break;
|
||||
case SIGPIPE:
|
||||
DBG_WARN(0, "Received PIPE signal");
|
||||
break;
|
||||
default:
|
||||
DBG_WARN(0, "Unknown signal %d",s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _diffInSeconds(time_t t1, time_t t0)
|
||||
{
|
||||
return t1-t0;
|
||||
}
|
||||
|
||||
|
||||
82
apps/aqhome-nodes/r_connect.c
Normal file
82
apps/aqhome-nodes/r_connect.c
Normal file
@@ -0,0 +1,82 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "./r_connect.h"
|
||||
#include "./server_p.h"
|
||||
#include "aqhome/ipc2/endpoint.h"
|
||||
#include "aqhome/msg/ipc/m_ipc.h"
|
||||
#include "aqhome/msg/ipc/nodes/m_ipcn.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_connect.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_result.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_tag16.h"
|
||||
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* code
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void AQH_NodeServer_HandleConnect(GWEN_UNUSED AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList)
|
||||
{
|
||||
if (tagList) {
|
||||
AQH_MESSAGE *outMsg;
|
||||
int resultCode=AQH_MSGDATA_RESULT_SUCCESS;
|
||||
char *clientId=NULL;
|
||||
char *userId=NULL;
|
||||
char *passw=NULL;
|
||||
uint32_t flags;
|
||||
|
||||
clientId=AQH_Tag16_GetTagDataAsNewString(tagList, AQH_MSG_CONNECT_TAGS_CLIENTID, NULL);
|
||||
userId=AQH_Tag16_GetTagDataAsNewString(tagList, AQH_MSG_CONNECT_TAGS_USERID, NULL);
|
||||
flags=AQH_Tag16_GetTagDataAsUint32(tagList, AQH_MSG_CONNECT_TAGS_FLAGS, 0);
|
||||
passw=AQH_Tag16_GetTagDataAsNewString(tagList, AQH_MSG_CONNECT_TAGS_PASSWORD, NULL);
|
||||
|
||||
if (clientId)
|
||||
AQH_Endpoint_SetServiceName(ep, clientId);
|
||||
if (userId)
|
||||
AQH_Endpoint_SetUserName(ep, userId);
|
||||
|
||||
if (flags & AQH_MSG_CONNECT_FLAGS_WANTUPDATES)
|
||||
AQH_Endpoint_AddFlags(ep, AQH_ENDPOINT_FLAGS_WANTUPDATES);
|
||||
|
||||
/* TODO: add user management, for now we allow all */
|
||||
AQH_Endpoint_SetPermissions(ep,
|
||||
AQH_ENDPOINT_PERMS_LISTVALUES |
|
||||
AQH_ENDPOINT_PERMS_READVALUE |
|
||||
AQH_ENDPOINT_PERMS_ADDVALUE |
|
||||
AQH_ENDPOINT_PERMS_LISTDATA |
|
||||
AQH_ENDPOINT_PERMS_READDATA |
|
||||
AQH_ENDPOINT_PERMS_ADDDATA |
|
||||
AQH_ENDPOINT_PERMS_LISTDEVICES |
|
||||
AQH_ENDPOINT_PERMS_READDEVICE |
|
||||
AQH_ENDPOINT_PERMS_ADDDEVICE |
|
||||
AQH_ENDPOINT_PERMS_MODDEVICE);
|
||||
free(passw);
|
||||
free(userId);
|
||||
free(clientId);
|
||||
|
||||
outMsg=AQH_IpcMessageResult_new(AQH_IPC_PROTOCOL_NODES_ID,
|
||||
AQH_IPC_PROTOCOL_NODES_VERSION,
|
||||
AQH_MSGTYPE_IPC_NODES_RESULT,
|
||||
AQH_Endpoint_GetNextMessageId(ep),
|
||||
AQH_IpcMessage_GetMsgId(msg),
|
||||
resultCode, NULL);
|
||||
AQH_Endpoint_AddMsgOut(ep, outMsg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
27
apps/aqhome-nodes/r_connect.h
Normal file
27
apps/aqhome-nodes/r_connect.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOMED_R_CONNECT_H
|
||||
#define AQHOMED_R_CONNECT_H
|
||||
|
||||
|
||||
#include "./server.h"
|
||||
|
||||
#include <gwenhywfar/tag16.h>
|
||||
|
||||
|
||||
|
||||
void AQH_NodeServer_HandleConnect(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *recvdMsg, const GWEN_TAG16_LIST *tagList);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
79
apps/aqhome-nodes/r_forward.c
Normal file
79
apps/aqhome-nodes/r_forward.c
Normal file
@@ -0,0 +1,79 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "./r_forward.h"
|
||||
#include "./server_p.h"
|
||||
#include "aqhome/ipc2/endpoint.h"
|
||||
#include "aqhome/msg/ipc/m_ipc.h"
|
||||
#include "aqhome/msg/ipc/nodes/m_ipcn.h"
|
||||
#include "aqhome/msg/ipc/nodes/m_ipcn_forward.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_connect.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_result.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_tag16.h"
|
||||
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* code
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void AQH_NodeServer_HandleForward(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList)
|
||||
{
|
||||
AQH_NODE_SERVER *xo;
|
||||
|
||||
xo=AQH_NodeServer_GetServerData(o);
|
||||
if (xo) {
|
||||
AQH_MESSAGE *outMsg;
|
||||
int resultCode=AQH_MSGDATA_RESULT_SUCCESS;
|
||||
const GWEN_TAG16 *tag;
|
||||
const uint8_t *ptr;
|
||||
uint32_t len;
|
||||
|
||||
tag=tagList?GWEN_Tag16_List_FindFirstByTagType(tagList, AQH_MSGNODE_FORWARD_TAGS_MSG):NULL;
|
||||
ptr=tag?GWEN_Tag16_GetTagData(tag):NULL;
|
||||
len=tag?GWEN_Tag16_GetTagLength(tag):0;
|
||||
|
||||
if (ptr && len) {
|
||||
if (xo->ttyEndpoint) {
|
||||
AQH_MESSAGE *nodeMsg;
|
||||
|
||||
nodeMsg=AQH_Message_new();
|
||||
AQH_Message_SetData(nodeMsg, ptr, len);
|
||||
AQH_Message_SetUsedSize(nodeMsg, len);
|
||||
AQH_Endpoint_AddMsgOut(xo->ttyEndpoint, nodeMsg);
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "TTY endpoint currently not connected");
|
||||
resultCode=AQH_MSGDATA_RESULT_ERROR_TRYAGAIN;
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Empty message to forward");
|
||||
resultCode=AQH_MSGDATA_RESULT_ERROR_BADDATA;
|
||||
}
|
||||
|
||||
outMsg=AQH_IpcMessageResult_new(AQH_IPC_PROTOCOL_NODES_ID,
|
||||
AQH_IPC_PROTOCOL_NODES_VERSION,
|
||||
AQH_MSGTYPE_IPC_NODES_RESULT,
|
||||
AQH_Endpoint_GetNextMessageId(ep),
|
||||
AQH_IpcMessage_GetMsgId(msg),
|
||||
resultCode, NULL);
|
||||
AQH_Endpoint_AddMsgOut(ep, outMsg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
27
apps/aqhome-nodes/r_forward.h
Normal file
27
apps/aqhome-nodes/r_forward.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2024 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOMED_R_FORWARD_H
|
||||
#define AQHOMED_R_FORWARD_H
|
||||
|
||||
|
||||
#include "./server.h"
|
||||
|
||||
#include <gwenhywfar/tag16.h>
|
||||
|
||||
|
||||
void AQH_NodeServer_HandleForward(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
77
apps/aqhome-nodes/r_getnodes.c
Normal file
77
apps/aqhome-nodes/r_getnodes.c
Normal file
@@ -0,0 +1,77 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "./r_getnodes.h"
|
||||
#include "./server_p.h"
|
||||
#include "aqhome/ipc2/endpoint.h"
|
||||
#include "aqhome/msg/ipc/m_ipc.h"
|
||||
#include "aqhome/msg/ipc/nodes/m_ipcn.h"
|
||||
#include "aqhome/msg/ipc/nodes/m_ipcn_setaccmsggrps.h"
|
||||
#include "aqhome/msg/ipc/nodes/m_ipcn_getdevices_rsp.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_connect.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_result.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_tag16.h"
|
||||
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* code
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void AQH_NodeServer_HandleGetNodes(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg)
|
||||
{
|
||||
AQH_NODE_SERVER *xo;
|
||||
|
||||
xo=AQH_NodeServer_GetServerData(o);
|
||||
if (xo) {
|
||||
AQH_NODE_INFO_LIST *nodeInfoList;
|
||||
|
||||
nodeInfoList=AQH_NodeDb_GetAllNodeInfos(xo->nodeDb);
|
||||
if (nodeInfoList && AQH_NodeInfo_List_GetCount(nodeInfoList)) {
|
||||
const AQH_NODE_INFO *ni;
|
||||
|
||||
ni=AQH_NodeInfo_List_First(nodeInfoList);
|
||||
while(ni) {
|
||||
const AQH_NODE_INFO *niNext;
|
||||
AQH_MESSAGE *outMsg;
|
||||
|
||||
niNext=AQH_NodeInfo_List_Next(ni);
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Sending response for node %02x (%08x)", AQH_NodeInfo_GetBusAddress(ni), AQH_NodeInfo_GetUid(ni));
|
||||
outMsg=AQH_IpcnMessageGetDevicesRsp_new(AQH_MSGTYPE_IPC_NODES_GETDEVICES_RSP,
|
||||
AQH_Endpoint_GetNextMessageId(ep), AQH_IpcMessage_GetMsgId(msg),
|
||||
niNext?0:AQH_MSGNODE_GETDEVICES_RSP_FLAGS_LASTMSG, ni);
|
||||
AQH_Endpoint_AddMsgOut(ep, outMsg);
|
||||
DBG_ERROR(NULL, "Messages in clients out queue: %d", AQH_Message_List_GetCount(AQH_Endpoint_GetMsgOutList(ep)));
|
||||
ni=niNext;
|
||||
}
|
||||
}
|
||||
else {
|
||||
AQH_MESSAGE *outMsg;
|
||||
|
||||
DBG_INFO(AQH_LOGDOMAIN, "No nodes");
|
||||
outMsg=AQH_IpcMessageResult_new(AQH_IPC_PROTOCOL_NODES_ID,
|
||||
AQH_IPC_PROTOCOL_NODES_VERSION,
|
||||
AQH_MSGTYPE_IPC_NODES_RESULT,
|
||||
AQH_Endpoint_GetNextMessageId(ep),
|
||||
AQH_IpcMessage_GetMsgId(msg),
|
||||
AQH_MSGDATA_RESULT_ERROR_NODATA, NULL);
|
||||
AQH_Endpoint_AddMsgOut(ep, outMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
27
apps/aqhome-nodes/r_getnodes.h
Normal file
27
apps/aqhome-nodes/r_getnodes.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOMED_R_GETNODES_H
|
||||
#define AQHOMED_R_GETNODES_H
|
||||
|
||||
|
||||
#include "./server.h"
|
||||
|
||||
#include <gwenhywfar/tag16.h>
|
||||
|
||||
|
||||
void AQH_NodeServer_HandleGetNodes(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
56
apps/aqhome-nodes/r_setaccmsggrps.c
Normal file
56
apps/aqhome-nodes/r_setaccmsggrps.c
Normal file
@@ -0,0 +1,56 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "./r_setaccmsggrps.h"
|
||||
#include "./server_p.h"
|
||||
#include "aqhome/ipc2/endpoint.h"
|
||||
#include "aqhome/msg/ipc/m_ipc.h"
|
||||
#include "aqhome/msg/ipc/nodes/m_ipcn.h"
|
||||
#include "aqhome/msg/ipc/nodes/m_ipcn_setaccmsggrps.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_connect.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_result.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_tag16.h"
|
||||
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* code
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void AQH_NodeServer_HandleSetAccMsgGrps(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList)
|
||||
{
|
||||
AQH_NODE_SERVER *xo;
|
||||
|
||||
xo=AQH_NodeServer_GetServerData(o);
|
||||
if (xo) {
|
||||
AQH_MESSAGE *outMsg;
|
||||
int resultCode=AQH_MSGDATA_RESULT_SUCCESS;
|
||||
uint32_t msgGrps=0;
|
||||
|
||||
msgGrps=AQH_IpcnMessageSetAcceptedMsgGroups_GetGroups(tagList);
|
||||
AQH_Endpoint_SetAcceptedMsgGroups(ep, msgGrps);
|
||||
outMsg=AQH_IpcMessageResult_new(AQH_IPC_PROTOCOL_NODES_ID,
|
||||
AQH_IPC_PROTOCOL_NODES_VERSION,
|
||||
AQH_MSGTYPE_IPC_NODES_RESULT,
|
||||
AQH_Endpoint_GetNextMessageId(ep),
|
||||
AQH_IpcMessage_GetMsgId(msg),
|
||||
resultCode, NULL);
|
||||
AQH_Endpoint_AddMsgOut(ep, outMsg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
27
apps/aqhome-nodes/r_setaccmsggrps.h
Normal file
27
apps/aqhome-nodes/r_setaccmsggrps.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2024 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOMED_R_SETACCMSGGRPS_H
|
||||
#define AQHOMED_R_SETACCMSGGRPS_H
|
||||
|
||||
|
||||
#include "./server.h"
|
||||
|
||||
#include <gwenhywfar/tag16.h>
|
||||
|
||||
|
||||
void AQH_NodeServer_HandleSetAccMsgGrps(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
382
apps/aqhome-nodes/r_setdata.c
Normal file
382
apps/aqhome-nodes/r_setdata.c
Normal file
@@ -0,0 +1,382 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "./r_setdata.h"
|
||||
#include "./server_p.h"
|
||||
|
||||
#include "aqhome/aqhome.h"
|
||||
#include "aqhome/data/value.h"
|
||||
#include "aqhome/msg/ipc/m_ipc.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_result.h"
|
||||
#include "aqhome/msg/ipc/m_ipc_tag16.h"
|
||||
#include "aqhome/msg/ipc/data/m_ipcd.h"
|
||||
#include "aqhome/msg/ipc/data/m_ipcd_setdata.h"
|
||||
#include "aqhome/msg/node/m_node.h"
|
||||
#include "aqhome/msg/node/m_value.h"
|
||||
#include "aqhome/ipc2/endpoint.h"
|
||||
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* definitions
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#define R_SETDATA_REQUEST_EXPIRE_SECS 20
|
||||
#define R_SETDATA_SUBREQUEST_EXPIRE_SECS 10
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static AQH_NODE_INFO *_getNodeInfoFromValue(AQH_NODE_SERVER *xo, const AQH_VALUE *value);
|
||||
|
||||
static AQH_MSG_REQUEST *_mkRequest_SetData(AQH_OBJECT *o, AQH_NODE_SERVER *xo,
|
||||
AQH_OBJECT *ep, uint32_t requestMsgId,
|
||||
int destAddr, int valueId, uint16_t dataVal, uint16_t dataDenom);
|
||||
|
||||
static void _rqSubRequestFinished(AQH_MSG_REQUEST *rq, AQH_MSG_REQUEST *subRq, int reason);
|
||||
static void _rqAbort(AQH_MSG_REQUEST *rq, int reason);
|
||||
|
||||
static AQH_MSG_REQUEST *_mkSubRequest_SetData(AQH_OBJECT *o, AQH_NODE_SERVER *xo,
|
||||
int destAddr, int valueId, uint16_t dataVal, uint16_t dataDenom);
|
||||
static int _subRqHandleResponse(AQH_MSG_REQUEST *rq, const AQH_MESSAGE *msg);
|
||||
static void _subRqAbort(AQH_MSG_REQUEST *rq, int reason);
|
||||
static void _sendResponseResultToBroker(AQH_OBJECT *ep, uint32_t refMsgId, int result);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* implementations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
void AQH_NodeServer_HandleSetData(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *recvdMsg)
|
||||
{
|
||||
if (o) {
|
||||
AQH_NODE_SERVER *xo;
|
||||
|
||||
xo=AQH_NodeServer_GetServerData(o);
|
||||
if (xo) {
|
||||
uint32_t msgId;
|
||||
|
||||
DBG_INFO(NULL, "Received IPC SetDataRequest message");
|
||||
msgId=AQH_IpcMessage_GetMsgId(recvdMsg);
|
||||
|
||||
if (xo->ttyEndpoint) {
|
||||
GWEN_TAG16_LIST *tagList;
|
||||
|
||||
tagList=AQH_IpcMessageTag16_ParsePayload(recvdMsg, 0);
|
||||
if (tagList) {
|
||||
AQH_VALUE *value;
|
||||
|
||||
value=AQH_IpcdMessageSetData_ReadValue(tagList);
|
||||
if (value) {
|
||||
const char *varName;
|
||||
|
||||
varName=AQH_Value_GetName(value);
|
||||
if (varName) {
|
||||
char *data;
|
||||
|
||||
data=AQH_IpcdMessageSetData_ReadData(tagList);
|
||||
if (data) {
|
||||
AQH_NODE_INFO *nodeInfo;
|
||||
|
||||
nodeInfo=_getNodeInfoFromValue(xo, value);
|
||||
if (nodeInfo) {
|
||||
const char *devName;
|
||||
|
||||
devName=AQH_NodeInfo_GetDeviceId(nodeInfo);
|
||||
if (devName) {
|
||||
const AQHNODE_DEVICE *devInfo;
|
||||
|
||||
devInfo=AQH_NodeServer_GetDeviceDefByName(o, devName);
|
||||
if (devInfo) {
|
||||
const AQHNODE_VALUE *devValue;
|
||||
|
||||
devValue=AQHNODE_Value_List_GetByName(AQHNODE_Device_GetValueList(devInfo), varName);
|
||||
if (devValue) {
|
||||
uint16_t dataVal=0;
|
||||
uint16_t dataDenom=0;
|
||||
|
||||
if (AQH_ReadDataFromString(AQHNODE_Value_GetDataType(devValue), data, &dataVal, &dataDenom)==0) {
|
||||
AQH_MSG_REQUEST *rq;
|
||||
int destAddr;
|
||||
|
||||
destAddr=AQH_NodeInfo_GetBusAddress(nodeInfo);
|
||||
DBG_DEBUG(NULL, "Creating SETDATA request");
|
||||
|
||||
rq=_mkRequest_SetData(o, xo, ep, msgId, destAddr, AQHNODE_Value_GetId(devValue), dataVal, dataDenom);
|
||||
AQH_NodeServer_AddRequestToTree(o, rq);
|
||||
/* done */
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Bad data \"%s\"", data);
|
||||
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_BADDATA);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Invalid value name \"%s\"", varName);
|
||||
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_INVALID);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Unknown node \"%s\"", devName);
|
||||
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_INVALID);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Node not yet fully identified, come back later");
|
||||
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_TRYAGAIN);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "No matching nodeinfo");
|
||||
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_INVALID);
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "No data");
|
||||
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_NODATA);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "No var name");
|
||||
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_NODATA);
|
||||
}
|
||||
AQH_Value_free(value);
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Could not read value from message");
|
||||
}
|
||||
GWEN_Tag16_List_free(tagList);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "TTY endpoint not connected");
|
||||
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_IO);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* IPC Request SETDATA
|
||||
*/
|
||||
|
||||
AQH_MSG_REQUEST *_mkRequest_SetData(AQH_OBJECT *o, AQH_NODE_SERVER *xo,
|
||||
AQH_OBJECT *ep, uint32_t requestMsgId,
|
||||
int destAddr, int valueId, uint16_t dataVal, uint16_t dataDenom)
|
||||
{
|
||||
AQH_MSG_REQUEST *rq;
|
||||
AQH_MSG_REQUEST *subRq;
|
||||
|
||||
rq=AQH_MsgRequest_new();
|
||||
AQH_MsgRequest_SetPrivateData(rq, o);
|
||||
AQH_MsgRequest_SetEndpoint(rq, ep);
|
||||
AQH_MsgRequest_SetRequestMsgId(rq, requestMsgId);
|
||||
AQH_MsgRequest_SetSubRequestFinishedFn(rq, _rqSubRequestFinished);
|
||||
AQH_MsgRequest_SetAbortFn(rq, _rqAbort);
|
||||
AQH_MsgRequest_SetTimestamps(rq, R_SETDATA_REQUEST_EXPIRE_SECS);
|
||||
|
||||
subRq=_mkSubRequest_SetData(o, xo, destAddr, valueId, dataVal, dataDenom);
|
||||
AQH_MsgRequest_Tree2_AddChild(rq, subRq);
|
||||
|
||||
return rq;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _rqSubRequestFinished(AQH_MSG_REQUEST *rq, AQH_MSG_REQUEST *subRq, int reason)
|
||||
{
|
||||
AQH_OBJECT *ep;
|
||||
uint32_t refMsgId;
|
||||
int result;
|
||||
|
||||
DBG_INFO(NULL, "SubRequest finished (reason: %d)", reason);
|
||||
refMsgId=AQH_MsgRequest_GetRequestMsgId(rq);
|
||||
ep=AQH_MsgRequest_GetEndpoint(rq);
|
||||
result=AQH_MsgRequest_GetResult(subRq);
|
||||
|
||||
if (reason==AQH_MSG_REQUEST_REASON_ABORTED)
|
||||
_sendResponseResultToBroker(ep, refMsgId, AQH_MSGDATA_RESULT_ERROR_GENERIC);
|
||||
else
|
||||
_sendResponseResultToBroker(ep, refMsgId, result);
|
||||
|
||||
AQH_MsgRequest_SetResult(rq, result);
|
||||
AQH_MsgRequest_SetState(rq, AQH_MSG_REQUEST_STATE_DONE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _rqAbort(AQH_MSG_REQUEST *rq, int reason)
|
||||
{
|
||||
AQH_OBJECT *ep;
|
||||
uint32_t refMsgId;
|
||||
AQH_MSG_REQUEST *rqParent;
|
||||
|
||||
DBG_INFO(NULL, "Aborting request");
|
||||
refMsgId=AQH_MsgRequest_GetRequestMsgId(rq);
|
||||
ep=AQH_MsgRequest_GetEndpoint(rq);
|
||||
_sendResponseResultToBroker(ep, refMsgId, AQH_MSGDATA_RESULT_ERROR_GENERIC);
|
||||
AQH_MsgRequest_SetState(rq, AQH_MSG_REQUEST_STATE_DONE);
|
||||
|
||||
rqParent=AQH_MsgRequest_Tree2_GetParent(rq);
|
||||
if (rqParent)
|
||||
AQH_MsgRequest_SubRequestFinished(rqParent, rq, reason);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* TTY Request SETDATA
|
||||
*/
|
||||
|
||||
|
||||
AQH_MSG_REQUEST *_mkSubRequest_SetData(AQH_OBJECT *o, AQH_NODE_SERVER *xo,
|
||||
int destAddr, int valueId, uint16_t dataVal, uint16_t dataDenom)
|
||||
{
|
||||
AQH_MSG_REQUEST *rq;
|
||||
uint16_t msgId;
|
||||
AQH_MESSAGE *msgOut;
|
||||
|
||||
rq=AQH_MsgRequest_new();
|
||||
AQH_MsgRequest_SetPrivateData(rq, o);
|
||||
AQH_MsgRequest_SetEndpoint(rq, xo->ttyEndpoint);
|
||||
|
||||
AQH_MsgRequest_SetHandleResponseFn(rq, _subRqHandleResponse);
|
||||
AQH_MsgRequest_SetAbortFn(rq, _subRqAbort);
|
||||
|
||||
msgId=AQH_Endpoint_GetNextMessageId(xo->ttyEndpoint) & 0xffff;
|
||||
AQH_MsgRequest_SetRequestMsgId(rq, msgId);
|
||||
AQH_MsgRequest_SetTimestamps(rq, R_SETDATA_SUBREQUEST_EXPIRE_SECS);
|
||||
|
||||
msgOut=AQH_ValueMessage_new(xo->nodeAddress, destAddr, AQH_MSG_TYPE_VALUE_SET, msgId, valueId, dataVal, dataDenom);
|
||||
AQH_Endpoint_AddMsgOut(xo->ttyEndpoint, msgOut);
|
||||
|
||||
return rq;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _subRqHandleResponse(AQH_MSG_REQUEST *rq, const AQH_MESSAGE *msg)
|
||||
{
|
||||
AQH_OBJECT *o;
|
||||
|
||||
DBG_DEBUG(NULL, "Checking message from %02x", AQH_NodeMessage_GetSourceAddress(msg));
|
||||
o=(AQH_OBJECT*)AQH_MsgRequest_GetPrivateData(rq);
|
||||
if (o) {
|
||||
AQH_NODE_SERVER *xo;
|
||||
|
||||
xo=AQH_NodeServer_GetServerData(o);
|
||||
if (xo) {
|
||||
uint8_t destAddr;
|
||||
|
||||
destAddr=AQH_NodeMessage_GetDestAddress(msg);
|
||||
if (destAddr==0xff || destAddr==xo->nodeAddress) {
|
||||
uint8_t msgCode;
|
||||
|
||||
msgCode=AQH_NodeMessage_GetMsgType(msg);
|
||||
if (msgCode==AQH_MSG_TYPE_VALUE_SET_ACK || msgCode==AQH_MSG_TYPE_VALUE_SET_NACK) {
|
||||
uint16_t msgId;
|
||||
|
||||
msgId=AQH_ValueMessage_GetMsgId(msg);
|
||||
if (msgId==AQH_MsgRequest_GetRequestMsgId(rq)) {
|
||||
AQH_MSG_REQUEST *rqParent;
|
||||
|
||||
DBG_INFO(NULL,
|
||||
"Received response (%02x) for msg id %04x from %02x",
|
||||
msgCode, msgId, AQH_NodeMessage_GetSourceAddress(msg));
|
||||
AQH_MsgRequest_SetResult(rq,
|
||||
(msgCode==AQH_MSG_TYPE_VALUE_SET_ACK)?AQH_MSGDATA_RESULT_SUCCESS:AQH_MSGDATA_RESULT_ERROR_GENERIC);
|
||||
AQH_MsgRequest_SetState(rq, AQH_MSG_REQUEST_STATE_DONE);
|
||||
rqParent=AQH_MsgRequest_Tree2_GetParent(rq);
|
||||
if (rqParent)
|
||||
AQH_MsgRequest_SubRequestFinished(rqParent, rq, AQH_MSG_REQUEST_REASON_DONE);
|
||||
return AQH_MSG_REQUEST_RESULT_HANDLED;
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, " Non-matching message id");
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, " Non-matching message code");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return AQH_MSG_REQUEST_RESULT_NOT_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _subRqAbort(AQH_MSG_REQUEST *rq, int reason)
|
||||
{
|
||||
AQH_MSG_REQUEST *rqParent;
|
||||
|
||||
DBG_INFO(NULL, "Aborting request");
|
||||
|
||||
AQH_MsgRequest_SetResult(rq, AQH_MSGDATA_RESULT_ERROR_GENERIC);
|
||||
AQH_MsgRequest_SetState(rq, AQH_MSG_REQUEST_STATE_DONE);
|
||||
|
||||
rqParent=AQH_MsgRequest_Tree2_GetParent(rq);
|
||||
if (rqParent)
|
||||
AQH_MsgRequest_SubRequestFinished(rqParent, rq, reason);
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQH_NODE_INFO *_getNodeInfoFromValue(AQH_NODE_SERVER *xo, const AQH_VALUE *value)
|
||||
{
|
||||
const char *s;
|
||||
unsigned long int uid;
|
||||
|
||||
s=AQH_Value_GetDeviceName(value);
|
||||
if (s && *s && 1==sscanf(s, "%lx", &uid)) {
|
||||
AQH_NODE_INFO *ni;
|
||||
|
||||
ni=AQH_NodeDb_GetNodeInfoByUid(xo->nodeDb, uid);
|
||||
if (ni==NULL) {
|
||||
DBG_ERROR(NULL, "Node \"%08lx\" not found", uid);
|
||||
return NULL;
|
||||
}
|
||||
return ni;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _sendResponseResultToBroker(AQH_OBJECT *ep, uint32_t refMsgId, int result)
|
||||
{
|
||||
AQH_MESSAGE *msg;
|
||||
|
||||
msg=AQH_IpcMessageResult_new(AQH_IPC_PROTOCOL_DATA_ID, AQH_IPC_PROTOCOL_DATA_VERSION, AQH_MSGTYPE_IPC_DATA_RESULT,
|
||||
AQH_Endpoint_GetNextMessageId(ep), refMsgId, result, NULL);
|
||||
AQH_Endpoint_AddMsgOut(ep, msg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
27
apps/aqhome-nodes/r_setdata.h
Normal file
27
apps/aqhome-nodes/r_setdata.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2024 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOMED_R_SETDATA_H
|
||||
#define AQHOMED_R_SETDATA_H
|
||||
|
||||
|
||||
#include "./server.h"
|
||||
|
||||
#include <gwenhywfar/request.h>
|
||||
|
||||
|
||||
|
||||
void AQH_NodeServer_HandleSetData(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *recvdMsg);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
1443
apps/aqhome-nodes/server.c
Normal file
1443
apps/aqhome-nodes/server.c
Normal file
File diff suppressed because it is too large
Load Diff
82
apps/aqhome-nodes/server.h
Normal file
82
apps/aqhome-nodes/server.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef SERVER_H
|
||||
#define SERVER_H
|
||||
|
||||
|
||||
|
||||
#include "aqhome-nodes/types/device.h"
|
||||
|
||||
#include "aqhome/events2/object.h"
|
||||
#include <aqhome/ipc2/msgrequest.h>
|
||||
|
||||
|
||||
|
||||
#define AQH_ENDPOINT_PERMS_LISTVALUES 0x0001
|
||||
#define AQH_ENDPOINT_PERMS_READVALUE 0x0002
|
||||
#define AQH_ENDPOINT_PERMS_ADDVALUE 0x0004
|
||||
|
||||
#define AQH_ENDPOINT_PERMS_LISTDATA 0x0010
|
||||
#define AQH_ENDPOINT_PERMS_READDATA 0x0020
|
||||
#define AQH_ENDPOINT_PERMS_ADDDATA 0x0040
|
||||
#define AQH_ENDPOINT_PERMS_SETDATA 0x0080
|
||||
|
||||
#define AQH_ENDPOINT_PERMS_LISTDEVICES 0x0100
|
||||
#define AQH_ENDPOINT_PERMS_READDEVICE 0x0200
|
||||
#define AQH_ENDPOINT_PERMS_ADDDEVICE 0x0400
|
||||
#define AQH_ENDPOINT_PERMS_MODDEVICE 0x0800
|
||||
|
||||
|
||||
|
||||
AQH_OBJECT *AQH_NodeServer_new(AQH_EVENT_LOOP *eventLoop);
|
||||
int AQH_NodeServer_Init(AQH_OBJECT *o, int argc, char **argv);
|
||||
void AQH_NodeServer_Fini(AQH_OBJECT *o);
|
||||
|
||||
/* loop functions */
|
||||
void AQH_NodeServer_CleanupClients(AQH_OBJECT *o);
|
||||
void AQH_NodeServer_HandleTtyMsgs(AQH_OBJECT *o);
|
||||
void AQH_NodeServer_HandleClientMsgs(AQH_OBJECT *o);
|
||||
void AQH_NodeServer_HandleBrokerMsgs(AQH_OBJECT *o);
|
||||
void AQH_NodeServer_CheckBrokerConnection(AQH_OBJECT *o);
|
||||
void AQH_NodeServer_CheckTtyConnection(AQH_OBJECT *o);
|
||||
|
||||
|
||||
/* getters and setters */
|
||||
int AQH_NodeServer_GetTimeout(const AQH_OBJECT *o);
|
||||
void AQH_NodeServer_SetLogFile(AQH_OBJECT *o, const char *s);
|
||||
void AQH_NodeServer_SetDbFile(AQH_OBJECT *o, const char *s);
|
||||
void AQH_NodeServer_SetPidFile(AQH_OBJECT *o, const char *s);
|
||||
|
||||
void AQH_NodeServer_SetDevicePath(AQH_OBJECT *o, const char *s);
|
||||
void AQH_NodeServer_SetTpcAddress(AQH_OBJECT *o, const char *s);
|
||||
void AQH_NodeServer_SetTcpPort(AQH_OBJECT *o, int i);
|
||||
void AQH_NodeServer_SetBrokerAddress(AQH_OBJECT *o, const char *s);
|
||||
void AQH_NodeServer_SetBrokerPort(AQH_OBJECT *o, int i);
|
||||
void AQH_NodeServer_SetBrokerClientId(AQH_OBJECT *o, const char *s);
|
||||
|
||||
|
||||
/* device management */
|
||||
const AQHNODE_DEVICE_LIST *AQH_NodeServer_GetDeviceDefList(const AQH_OBJECT *o);
|
||||
const AQHNODE_DEVICE *AQH_NodeServer_FindDeviceDef(const AQH_OBJECT *o,
|
||||
uint32_t manufacturer,
|
||||
uint16_t deviceType, uint16_t deviceVersion);
|
||||
const AQHNODE_DEVICE *AQH_NodeServer_GetDeviceDefByName(const AQH_OBJECT *o, const char *name);
|
||||
|
||||
|
||||
/* request management */
|
||||
AQH_MSG_REQUEST *AQH_NodeServer_GetRequestTree(const AQH_OBJECT *o);
|
||||
void AQH_NodeServer_AddRequestToTree(AQH_OBJECT *o, AQH_MSG_REQUEST *rq);
|
||||
void AQH_NodeServer_CleanupRequests(AQH_OBJECT *o);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
80
apps/aqhome-nodes/server_p.h
Normal file
80
apps/aqhome-nodes/server_p.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef SERVER_P_H
|
||||
#define SERVER_P_H
|
||||
|
||||
|
||||
#include "./server.h"
|
||||
#include "aqhome-nodes/types/device.h"
|
||||
|
||||
#include "aqhome/nodes/nodedb.h"
|
||||
#include "aqhome/ipc2/msgrequest.h"
|
||||
|
||||
#include <termios.h>
|
||||
|
||||
|
||||
/* default values */
|
||||
#define AQHOMED_DEFAULT_NODEADDR 240
|
||||
#define AQHOMED_DEFAULT_PIDFILE "/var/run/aqhomed-node.pid"
|
||||
#define AQHOMED_DEFAULT_DEVICE "/dev/ttyUSB0"
|
||||
#define AQHOMED_DEFAULT_IPC_PORT 45454
|
||||
#define AQHOMED_DEFAULT_BROKER_PORT 1899
|
||||
#define AQHOMED_DEFAULT_BROKER_CLIENTID "nodes"
|
||||
|
||||
|
||||
|
||||
typedef struct AQH_NODE_SERVER AQH_NODE_SERVER;
|
||||
struct AQH_NODE_SERVER {
|
||||
AQH_OBJECT *ttyEndpoint;
|
||||
AQH_OBJECT *brokerEndpoint;
|
||||
|
||||
AQH_OBJECT *ipcEndpoint;
|
||||
AQH_OBJECT_LIST *ipcClientList;
|
||||
|
||||
AQH_NODE_DB *nodeDb;
|
||||
AQHNODE_DEVICE_LIST *deviceDefList;
|
||||
|
||||
AQH_MSG_REQUEST *requestTree;
|
||||
|
||||
GWEN_DB_NODE *dbArgs;
|
||||
|
||||
char *dbFile;
|
||||
char *logFile;
|
||||
char *pidFile;
|
||||
|
||||
int timeout; /* timeout for run e.g. inside valgrind */
|
||||
char *devicePath;
|
||||
|
||||
char *tcpAddress;
|
||||
int tcpPort;
|
||||
|
||||
char *brokerAddress;
|
||||
int brokerPort;
|
||||
char *brokerClientId;
|
||||
|
||||
time_t timestampTtyDown;
|
||||
time_t timestampBrokerDown;
|
||||
|
||||
int nodeAddress;
|
||||
|
||||
struct termios initialTermiosState;
|
||||
|
||||
int timeoutInSeconds;
|
||||
|
||||
uint8_t protoId;
|
||||
uint8_t protoVer;
|
||||
|
||||
};
|
||||
|
||||
|
||||
AQH_NODE_SERVER *AQH_NodeServer_GetServerData(const AQH_OBJECT *o);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
136
apps/aqhome-nodes/tty_log.c
Normal file
136
apps/aqhome-nodes/tty_log.c
Normal file
@@ -0,0 +1,136 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "./tty_log.h"
|
||||
#include "./aqhomed_p.h"
|
||||
|
||||
#include "aqhome/msg/msg_value.h"
|
||||
#include "aqhome/msg/msg_value2.h"
|
||||
#include "aqhome/msg/msg_value3.h"
|
||||
#include "aqhome/msg/msg_sendstats.h"
|
||||
#include "aqhome/msg/msg_recvstats.h"
|
||||
#include "aqhome/msg/msg_memstats.h"
|
||||
#include "aqhome/msg/msg_sysstats.h"
|
||||
#include "aqhome/msg/msg_ping.h"
|
||||
#include "aqhome/msg/msg_pong.h"
|
||||
#include "aqhome/msg/msg_needaddr.h"
|
||||
#include "aqhome/msg/msg_claimaddr.h"
|
||||
#include "aqhome/msg/msg_haveaddr.h"
|
||||
#include "aqhome/msg/msg_denyaddr.h"
|
||||
#include "aqhome/msg/msg_device.h"
|
||||
#include "aqhome/msg/msg_flashready.h"
|
||||
#include "aqhome/msg/msg_flashstart.h"
|
||||
#include "aqhome/msg/msg_flashresponse.h"
|
||||
#include "aqhome/msg/msg_flashend.h"
|
||||
#include "aqhome/msg/msg_flashdata.h"
|
||||
#include "aqhome/msg/msg_reboot.h"
|
||||
|
||||
#include <gwenhywfar/list.h>
|
||||
#include <gwenhywfar/inherit.h>
|
||||
#include <gwenhywfar/debug.h>
|
||||
#include <gwenhywfar/gwentime.h>
|
||||
#include <gwenhywfar/text.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static void _writeToLogFile(const char *filename, const char *txt);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* implementations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void AqHomed_LogTtyMsg(AQHOMED *aqh, const GWEN_MSG *msg)
|
||||
{
|
||||
if (aqh && aqh->logFile) {
|
||||
uint8_t msgType;
|
||||
int msgIsValid;
|
||||
GWEN_BUFFER *dbuf;
|
||||
GWEN_TIME *ti;
|
||||
|
||||
dbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||
ti=GWEN_CurrentTime();
|
||||
GWEN_Time_toString(ti, "YYYY-MM-DD hh:mm:ss ", dbuf);
|
||||
GWEN_Time_free(ti);
|
||||
ti=NULL;
|
||||
|
||||
msgIsValid=(AQH_NodeMsg_IsChecksumValid(msg) && AQH_NodeMsg_IsMsgComplete(msg));
|
||||
msgType=AQH_NodeMsg_GetMsgType(msg);
|
||||
|
||||
if (msgIsValid) {
|
||||
switch(msgType) {
|
||||
case AQH_MSG_TYPE_PING: AQH_PingMsg_DumpToBuffer(msg, dbuf, "received"); break;
|
||||
case AQH_MSG_TYPE_PONG: AQH_PongMsg_DumpToBuffer(msg, dbuf, "received"); break;
|
||||
case AQH_MSG_TYPE_COMSENDSTATS: AQH_SendStatsMsg_DumpToBuffer(msg, dbuf, "received"); break;
|
||||
case AQH_MSG_TYPE_COMRECVSTATS: AQH_RecvStatsMsg_DumpToBuffer(msg, dbuf, "received"); break;
|
||||
case AQH_MSG_TYPE_TWIBUSMEMBER: AQH_NodeMsg_DumpToBuffer(msg, dbuf, "received"); break;
|
||||
case AQH_MSG_TYPE_DEBUG: AQH_NodeMsg_DumpToBuffer(msg, dbuf, "received"); break;
|
||||
case AQH_MSG_TYPE_VALUE: AQH_ValueMsg_DumpToBuffer(msg, dbuf, "received"); break;
|
||||
case AQH_MSG_TYPE_VALUE2: AQH_Value2Msg_DumpToBuffer(msg, dbuf, "received"); break;
|
||||
case AQH_MSG_TYPE_NEED_ADDRESS: AQH_NeedAddrMsg_DumpToBuffer(msg, dbuf, "received"); break;
|
||||
case AQH_MSG_TYPE_CLAIM_ADDRESS: AQH_ClaimAddrMsg_DumpToBuffer(msg, dbuf, "received"); break;
|
||||
case AQH_MSG_TYPE_HAVE_ADDRESS: AQH_HaveAddrMsg_DumpToBuffer(msg, dbuf, "received"); break;
|
||||
case AQH_MSG_TYPE_DENY_ADDRESS: AQH_DenyAddrMsg_DumpToBuffer(msg, dbuf, "received"); break;
|
||||
case AQH_MSG_TYPE_DEVICE: AQH_DeviceMsg_DumpToBuffer(msg, dbuf, "received"); break;
|
||||
case AQH_MSG_TYPE_MEMSTATS: AQH_MemStatsMsg_DumpToBuffer(msg, dbuf, "received"); break;
|
||||
case AQH_MSG_TYPE_SYSSTATS: AQH_SysStatsMsg_DumpToBuffer(msg, dbuf, "received"); break;
|
||||
case AQH_MSG_TYPE_FLASH_READY: AQH_FlashReadyMsg_DumpToBuffer(msg, dbuf, "received"); break;
|
||||
case AQH_MSG_TYPE_FLASH_START: AQH_FlashStartMsg_DumpToBuffer(msg, dbuf, "received"); break;
|
||||
case AQH_MSG_TYPE_FLASH_RSP: AQH_FlashResponseMsg_DumpToBuffer(msg, dbuf, "received"); break;
|
||||
case AQH_MSG_TYPE_FLASH_END: AQH_FlashEndMsg_DumpToBuffer(msg, dbuf, "received"); break;
|
||||
case AQH_MSG_TYPE_FLASH_DATA: AQH_FlashDataMsg_DumpToBuffer(msg, dbuf, "received"); break;
|
||||
case AQH_MSG_TYPE_REBOOT_REQ: AQH_RebootRequestMsg_DumpToBuffer(msg, dbuf, "received"); break;
|
||||
case AQH_MSG_TYPE_REBOOT_RSP: AQH_RebootResponseMsg_DumpToBuffer(msg, dbuf, "received"); break;
|
||||
case AQH_MSG_TYPE_VALUE_REPORT:
|
||||
case AQH_MSG_TYPE_VALUE_SET:
|
||||
case AQH_MSG_TYPE_VALUE_SET_ACK:
|
||||
case AQH_MSG_TYPE_VALUE_SET_NACK: AQH_Value3Msg_DumpToBuffer(msg, dbuf, "received"); break;
|
||||
default: AQH_NodeMsg_DumpToBuffer(msg, dbuf, "received"); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
AQH_NodeMsg_DumpToBuffer(msg, dbuf, "(invalid) received");
|
||||
}
|
||||
_writeToLogFile(aqh->logFile, GWEN_Buffer_GetStart(dbuf));
|
||||
GWEN_Buffer_free(dbuf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _writeToLogFile(const char *filename, const char *txt)
|
||||
{
|
||||
if (txt && *txt) {
|
||||
FILE *f;
|
||||
|
||||
f=fopen(filename, "a+");
|
||||
if (f) {
|
||||
if (1!=fwrite(txt, strlen(txt), 1, f)) {
|
||||
DBG_ERROR(AQH_LOGDOMAIN, "Error logging.");
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
23
apps/aqhome-nodes/tty_log.h
Normal file
23
apps/aqhome-nodes/tty_log.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOMED_TTY_LOG_H
|
||||
#define AQHOMED_TTY_LOG_H
|
||||
|
||||
|
||||
#include "./aqhomed.h"
|
||||
|
||||
|
||||
|
||||
void AqHomed_LogTtyMsg(AQHOMED *aqh, const GWEN_MSG *msg);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
73
apps/aqhome-nodes/types/0BUILD
Normal file
73
apps/aqhome-nodes/types/0BUILD
Normal file
@@ -0,0 +1,73 @@
|
||||
<?xml?>
|
||||
|
||||
<gwbuild>
|
||||
|
||||
<target type="ConvenienceLibrary" name="aqhnodes_types" >
|
||||
|
||||
<includes type="c" >
|
||||
$(gwenhywfar_cflags)
|
||||
-I$(topsrcdir)
|
||||
-I$(topbuilddir)
|
||||
-I$(topsrcdir)/apps
|
||||
-I$(topbuilddir)/apps
|
||||
-I$(builddir)
|
||||
-I$(srcdir)
|
||||
</includes>
|
||||
|
||||
<includes type="tm2" >
|
||||
--include=$(builddir)
|
||||
--include=$(srcdir)
|
||||
</includes>
|
||||
|
||||
<setVar name="local/cflags">$(visibility_cflags)</setVar>
|
||||
|
||||
<setVar name="tm2flags" >
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/typefiles" >
|
||||
device.t2d
|
||||
value.t2d
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_sources" >
|
||||
device.c
|
||||
value.c
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_headers_pub">
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_headers_priv" >
|
||||
device.h
|
||||
device_p.h
|
||||
value.h
|
||||
value_p.h
|
||||
</setVar>
|
||||
|
||||
<headers dist="true" >
|
||||
</headers>
|
||||
|
||||
<sources>
|
||||
$(local/typefiles)
|
||||
</sources>
|
||||
|
||||
<useTargets>
|
||||
</useTargets>
|
||||
|
||||
<libraries>
|
||||
</libraries>
|
||||
|
||||
<subdirs>
|
||||
</subdirs>
|
||||
|
||||
|
||||
<extradist>
|
||||
</extradist>
|
||||
|
||||
|
||||
</target>
|
||||
|
||||
|
||||
</gwbuild>
|
||||
|
||||
|
||||
87
apps/aqhome-nodes/types/device.t2d
Normal file
87
apps/aqhome-nodes/types/device.t2d
Normal file
@@ -0,0 +1,87 @@
|
||||
<?xml?>
|
||||
|
||||
<tm2>
|
||||
<type id="AQHNODE_DEVICE" type="pointer">
|
||||
<descr>
|
||||
This object and its objects are used to store registered devices and definitions for possible new devices.
|
||||
</descr>
|
||||
<lang id="c">
|
||||
<identifier>AQHNODE_DEVICE</identifier>
|
||||
<prefix>AQHNODE_Device</prefix>
|
||||
<baseFileName>device</baseFileName>
|
||||
|
||||
<flags>
|
||||
with_list1
|
||||
with_list2
|
||||
</flags>
|
||||
|
||||
<headers>
|
||||
<header type="sys" loc="pre">aqhome/api.h</header>
|
||||
<header type="sys" loc="pre">aqhome-nodes/types/value.h</header>
|
||||
</headers>
|
||||
|
||||
<inlines>
|
||||
</inlines>
|
||||
|
||||
|
||||
|
||||
</lang>
|
||||
|
||||
|
||||
<enums>
|
||||
</enums>
|
||||
|
||||
|
||||
<members>
|
||||
|
||||
<member name="name" type="char_ptr" maxlen="128">
|
||||
<default>NULL</default>
|
||||
<preset>NULL</preset>
|
||||
<access>public</access>
|
||||
<flags>own with_getbymember</flags>
|
||||
</member>
|
||||
|
||||
<member name="driver" type="char_ptr" maxlen="64">
|
||||
<default>NULL</default>
|
||||
<preset>NULL</preset>
|
||||
<access>public</access>
|
||||
<flags>own</flags>
|
||||
</member>
|
||||
|
||||
<member name="manufacturer" type="uint32_t" maxlen="8">
|
||||
<default>0</default>
|
||||
<preset>0</preset>
|
||||
<access>public</access>
|
||||
<flags></flags>
|
||||
</member>
|
||||
|
||||
<member name="deviceType" type="uint16_t" maxlen="8">
|
||||
<default>0</default>
|
||||
<preset>0</preset>
|
||||
<access>public</access>
|
||||
<flags></flags>
|
||||
</member>
|
||||
|
||||
<member name="deviceVersion" type="uint16_t" maxlen="8">
|
||||
<default>0</default>
|
||||
<preset>0</preset>
|
||||
<access>public</access>
|
||||
<flags></flags>
|
||||
</member>
|
||||
|
||||
|
||||
<member name="valueList" type="AQHNODE_VALUE_LIST" >
|
||||
<default>NULL</default>
|
||||
<preset>NULL</preset>
|
||||
<access>public</access>
|
||||
<flags>own</flags>
|
||||
<getflags>none</getflags>
|
||||
<setflags>none</setflags>
|
||||
</member>
|
||||
|
||||
</members>
|
||||
|
||||
</type>
|
||||
|
||||
</tm2>
|
||||
|
||||
94
apps/aqhome-nodes/types/value.t2d
Normal file
94
apps/aqhome-nodes/types/value.t2d
Normal file
@@ -0,0 +1,94 @@
|
||||
<?xml?>
|
||||
|
||||
<tm2>
|
||||
<type id="AQHNODE_VALUE" type="pointer">
|
||||
<descr>
|
||||
</descr>
|
||||
<lang id="c">
|
||||
<identifier>AQHNODE_VALUE</identifier>
|
||||
<prefix>AQHNODE_Value</prefix>
|
||||
<baseFileName>value</baseFileName>
|
||||
|
||||
<flags>
|
||||
with_xml
|
||||
with_db
|
||||
with_list1
|
||||
with_list2
|
||||
</flags>
|
||||
|
||||
<headers>
|
||||
<header type="sys" loc="pre">aqhome/api.h</header>
|
||||
</headers>
|
||||
|
||||
<inlines>
|
||||
</inlines>
|
||||
|
||||
|
||||
|
||||
</lang>
|
||||
|
||||
|
||||
<members>
|
||||
|
||||
<member name="id" type="int" maxlen="8">
|
||||
<default>0</default>
|
||||
<preset>0</preset>
|
||||
<access>public</access>
|
||||
<flags>with_getbymember</flags>
|
||||
</member>
|
||||
|
||||
<member name="name" type="char_ptr" maxlen="32">
|
||||
<default>0</default>
|
||||
<preset>0</preset>
|
||||
<access>public</access>
|
||||
<flags>own with_getbymember</flags>
|
||||
</member>
|
||||
|
||||
<member name="description" type="char_ptr" maxlen="256">
|
||||
<default>0</default>
|
||||
<preset>0</preset>
|
||||
<access>public</access>
|
||||
<flags>own</flags>
|
||||
</member>
|
||||
|
||||
<member name="valueType" type="int" maxlen="8">
|
||||
<default>0</default>
|
||||
<preset>0</preset>
|
||||
<access>public</access>
|
||||
<flags></flags>
|
||||
</member>
|
||||
|
||||
<member name="dataType" type="int" maxlen="8">
|
||||
<default>0</default>
|
||||
<preset>0</preset>
|
||||
<access>public</access>
|
||||
<flags></flags>
|
||||
</member>
|
||||
|
||||
<member name="modality" type="int" maxlen="8">
|
||||
<default>0</default>
|
||||
<preset>0</preset>
|
||||
<access>public</access>
|
||||
<flags></flags>
|
||||
</member>
|
||||
|
||||
<member name="valueUnits" type="char_ptr" maxlen="32">
|
||||
<default>0</default>
|
||||
<preset>0</preset>
|
||||
<access>public</access>
|
||||
<flags>own</flags>
|
||||
</member>
|
||||
|
||||
<member name="denom" type="int" maxlen="8">
|
||||
<default>0</default>
|
||||
<preset>0</preset>
|
||||
<access>public</access>
|
||||
<flags></flags>
|
||||
</member>
|
||||
|
||||
</members>
|
||||
|
||||
</type>
|
||||
|
||||
</tm2>
|
||||
|
||||
145
apps/aqhome-react/0BUILD
Normal file
145
apps/aqhome-react/0BUILD
Normal file
@@ -0,0 +1,145 @@
|
||||
<?xml?>
|
||||
|
||||
<gwbuild>
|
||||
|
||||
|
||||
<target type="Program" name="aqhome-react" install="$(sbindir)" >
|
||||
|
||||
<includes type="c" >
|
||||
$(gwenhywfar_cflags)
|
||||
-I$(topsrcdir)
|
||||
-I$(topbuilddir)
|
||||
-I$(topsrcdir)/apps
|
||||
-I$(topbuilddir)/apps
|
||||
-I$(builddir)
|
||||
-I$(srcdir)
|
||||
</includes>
|
||||
|
||||
<includes type="tm2" >
|
||||
--include=$(builddir)
|
||||
--include=$(srcdir)
|
||||
</includes>
|
||||
|
||||
<setVar name="local/cflags">$(visibility_cflags)</setVar>
|
||||
|
||||
<setVar name="tm2flags" >
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/typefiles" >
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_sources" >
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_headers_pub">
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_headers_priv" >
|
||||
</setVar>
|
||||
|
||||
<headers dist="true" >
|
||||
</headers>
|
||||
|
||||
<sources>
|
||||
$(local/typefiles)
|
||||
main.c
|
||||
</sources>
|
||||
|
||||
<useTargets>
|
||||
aqhomereact
|
||||
aqhome
|
||||
</useTargets>
|
||||
|
||||
<libraries>
|
||||
$(gwenhywfar_libs)
|
||||
-lm
|
||||
</libraries>
|
||||
|
||||
<subdirs>
|
||||
</subdirs>
|
||||
|
||||
|
||||
<extradist>
|
||||
</extradist>
|
||||
|
||||
|
||||
</target>
|
||||
|
||||
|
||||
|
||||
<target type="ConvenienceLibrary" name="aqhomereact" >
|
||||
|
||||
<includes type="c" >
|
||||
$(gwenhywfar_cflags)
|
||||
-I$(topsrcdir)
|
||||
-I$(topbuilddir)
|
||||
-I$(topsrcdir)/apps
|
||||
-I$(topbuilddir)/apps
|
||||
-I$(builddir)
|
||||
-I$(srcdir)
|
||||
</includes>
|
||||
|
||||
<includes type="tm2" >
|
||||
--include=$(builddir)
|
||||
--include=$(srcdir)
|
||||
</includes>
|
||||
|
||||
<setVar name="local/cflags">$(visibility_cflags)</setVar>
|
||||
|
||||
<setVar name="tm2flags" >
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/typefiles" >
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_sources" >
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_headers_pub">
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_headers_priv" >
|
||||
</setVar>
|
||||
|
||||
<headers dist="true" >
|
||||
server.h
|
||||
net_read.h
|
||||
suntimes.h
|
||||
</headers>
|
||||
|
||||
<sources>
|
||||
$(local/typefiles)
|
||||
server.c
|
||||
net_read.c
|
||||
suntimes.c
|
||||
</sources>
|
||||
|
||||
<useTargets>
|
||||
aqhome
|
||||
aqhreact_types
|
||||
aqhreact_units
|
||||
</useTargets>
|
||||
|
||||
<libraries>
|
||||
$(gwenhywfar_libs)
|
||||
-lm
|
||||
</libraries>
|
||||
|
||||
<subdirs>
|
||||
types
|
||||
units
|
||||
networks
|
||||
examples
|
||||
</subdirs>
|
||||
|
||||
|
||||
<extradist>
|
||||
</extradist>
|
||||
|
||||
|
||||
</target>
|
||||
|
||||
|
||||
</gwbuild>
|
||||
|
||||
|
||||
55
apps/aqhome-react/examples/001-tvlight.xml
Normal file
55
apps/aqhome-react/examples/001-tvlight.xml
Normal file
@@ -0,0 +1,55 @@
|
||||
|
||||
|
||||
<network id="tvlight1">
|
||||
<units>
|
||||
<unit id="valueFilterInPlug" type="valueFilter">
|
||||
<params>
|
||||
<param name="valueName">mqtt/plug02/powerusage</param>
|
||||
</params>
|
||||
</unit>
|
||||
|
||||
<unit id="suntime1" type="suntime" invert="TRUE">
|
||||
<params>
|
||||
<param name="latitude">52.619425</param>
|
||||
<param name="longitude">10.087891</param>
|
||||
<param name="offsetMinsForSunrise">0.0</param>
|
||||
<param name="offsetMinsForSunset">-30.0</param>
|
||||
</params>
|
||||
</unit>
|
||||
|
||||
<unit id="delayedOff1" type="delayedOff">
|
||||
<params>
|
||||
<param name="delayTime">300</param>
|
||||
<param name="threshold">70</param>
|
||||
</params>
|
||||
</unit>
|
||||
|
||||
<unit id="and1" type="and" />
|
||||
|
||||
<unit id="zeroPosNegString1" type="zeroPosNegString">
|
||||
<params>
|
||||
<param name="valueIfNegative">OFF</param>
|
||||
<param name="valueIfZero">OFF</param>
|
||||
<param name="valueIfPositive">ON</param>
|
||||
</params>
|
||||
</unit>
|
||||
|
||||
|
||||
<unit id="valueSetOutPlug1" type="valueSet">
|
||||
<params>
|
||||
<param name="valueName">mqtt/109C2F/power</param>
|
||||
</params>
|
||||
</unit>
|
||||
</units>
|
||||
|
||||
<links>
|
||||
<!-- sourceUnit, sourcePort, targetUnit, targetPort -->
|
||||
<link sourceUnit=".updatedValue" sourcePort="doubleOutput" targetUnit="valueFilterInPlug" targetPort="input" />
|
||||
<link sourceUnit="valueFilterInPlug" sourcePort="output" targetUnit="delayedOff1" targetPort="input" />
|
||||
<link sourceUnit=".timer" sourcePort="output" targetUnit="suntime1" targetPort="timer" />
|
||||
<link sourceUnit="suntime1" sourcePort="output" targetUnit="and1" targetPort="input" />
|
||||
<link sourceUnit="delayedOff1" sourcePort="output" targetUnit="and1" targetPort="input" />
|
||||
<link sourceUnit="and1" sourcePort="output" targetUnit="zeroPosNegString1" targetPort="input" />
|
||||
<link sourceUnit="zeroPosNegString1" sourcePort="output" targetUnit="valueSetOutPlug1" targetPort="input" />
|
||||
</links>
|
||||
</network>
|
||||
8
apps/aqhome-react/examples/0BUILD
Normal file
8
apps/aqhome-react/examples/0BUILD
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml?>
|
||||
|
||||
<gwbuild>
|
||||
<data dist="true">
|
||||
001-tvlight.xml
|
||||
</data>
|
||||
|
||||
</gwbuild>
|
||||
356
apps/aqhome-react/main.c
Normal file
356
apps/aqhome-react/main.c
Normal file
@@ -0,0 +1,356 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "./server.h"
|
||||
#include "./net_read.h"
|
||||
#include "aqhome-react/units/u_timer.h"
|
||||
#include "aqhome-react/types/prgrule.h"
|
||||
//#include "aqhome-react/types/prgrule-t.h"
|
||||
|
||||
#include "aqhome/aqhome.h"
|
||||
#include "aqhome/data/path-t.h"
|
||||
#include "aqhome/data/vars-t.h"
|
||||
//#include "aqhome/data/vars_dbread-t.h"
|
||||
//#include "aqhome/data/vars_dbwrite-t.h"
|
||||
|
||||
#include <gwenhywfar/gwenhywfar.h>
|
||||
#include <gwenhywfar/args.h>
|
||||
#include <gwenhywfar/logger.h>
|
||||
#include <gwenhywfar/db.h>
|
||||
#include <gwenhywfar/debug.h>
|
||||
#include <gwenhywfar/cgui.h>
|
||||
#include <gwenhywfar/text.h>
|
||||
#include <gwenhywfar/testframework.h>
|
||||
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
# include <signal.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* defines
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define I18N(msg) msg
|
||||
#define I18S(msg) msg
|
||||
|
||||
|
||||
#define FULL_DEBUG
|
||||
|
||||
#define AQHOME_REACT_READNET_INTERVAL 300
|
||||
#define AQHOME_REACT_SAVE_INTERVAL 300
|
||||
|
||||
#define CONNCLEAN_INTERVAL_IN_SECS 2
|
||||
#define CONNCHECK_INTERVAL_IN_SECS 10
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static void _runService(AQH_OBJECT *aqh, AQH_EVENT_LOOP *eventLoop);
|
||||
static int _diffInSeconds(time_t t1, time_t t0);
|
||||
//static int _testModules(int argc, char **argv);
|
||||
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
static int _setSignalHandlers(void);
|
||||
static int _setupSigAction(struct sigaction *sa, int sig);
|
||||
static void _signalHandler(int s);
|
||||
static struct sigaction saINT,saTERM, saHUP, saTSTP, saCONT, saPIPE;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* static vars
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static int stopService=0;
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* implementations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int rv;
|
||||
AQH_EVENT_LOOP *eventLoop;
|
||||
AQH_OBJECT *aqh;
|
||||
GWEN_GUI *gui;
|
||||
|
||||
rv=GWEN_Init();
|
||||
if (rv) {
|
||||
fprintf(stderr, "ERROR: Unable to init Gwen.\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
GWEN_Logger_Open(0, "aqhome-react", 0, GWEN_LoggerType_Console, GWEN_LoggerFacility_User);
|
||||
GWEN_Logger_SetLevel(0, GWEN_LoggerLevel_Warning);
|
||||
//GWEN_Logger_SetLevel(0, GWEN_LoggerLevel_Info);
|
||||
|
||||
rv=_setSignalHandlers();
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "here (%d)", rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv=AQH_Init();
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "here (%d)", rv);
|
||||
return 2;
|
||||
}
|
||||
|
||||
gui=GWEN_Gui_CGui_new();
|
||||
GWEN_Gui_SetGui(gui);
|
||||
|
||||
eventLoop=AQH_EventLoop_new();
|
||||
aqh=AQH_ReactServer_new(eventLoop);
|
||||
rv=AQH_ReactServer_Init(aqh, argc, argv);
|
||||
if (rv<0) {
|
||||
if (rv==GWEN_ERROR_CLOSE)
|
||||
return 1;
|
||||
DBG_INFO(NULL, "here (%d)", rv);
|
||||
return 2;
|
||||
}
|
||||
|
||||
_runService(aqh, eventLoop);
|
||||
|
||||
AQH_ReactServer_Fini(aqh);
|
||||
AQH_Object_free(aqh);
|
||||
|
||||
GWEN_Gui_SetGui(NULL);
|
||||
GWEN_Gui_free(gui);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _runService(AQH_OBJECT *aqh, AQH_EVENT_LOOP *eventLoop)
|
||||
{
|
||||
time_t timeStart;
|
||||
int timeout;
|
||||
time_t timeLastConnCheck;
|
||||
time_t lastFileScanTime;
|
||||
time_t lastSaveTime;
|
||||
|
||||
timeout=AQH_ReactServer_GetTimeout(aqh);
|
||||
timeStart=time(NULL);
|
||||
timeLastConnCheck=time(NULL);
|
||||
lastFileScanTime=timeStart;
|
||||
lastSaveTime=timeStart;
|
||||
|
||||
AQH_ReactServer_SetLatestNetworkFileTime(aqh, AQH_ReactServer_GetNewestUnitNetFiletime());
|
||||
|
||||
while(!stopService) {
|
||||
time_t now;
|
||||
int rv;
|
||||
|
||||
AQH_EventLoop_Run(eventLoop, 2000);
|
||||
AQH_ReactServer_HandleBrokerMsgs(aqh);
|
||||
|
||||
now=time(NULL);
|
||||
|
||||
if (_diffInSeconds(now, timeLastConnCheck)>CONNCHECK_INTERVAL_IN_SECS) {
|
||||
DBG_INFO(NULL, "Check connections");
|
||||
AQH_ReactServer_CheckBrokerConnection(aqh);
|
||||
timeLastConnCheck=now;
|
||||
}
|
||||
|
||||
AQH_ReactServer_ProcessAllUnits(aqh);
|
||||
|
||||
if (_diffInSeconds(now, lastFileScanTime)>AQHOME_REACT_READNET_INTERVAL) {
|
||||
time_t tNew;
|
||||
time_t t;
|
||||
|
||||
DBG_INFO(NULL, "Checking network files");
|
||||
tNew=AQH_ReactServer_GetNewestUnitNetFiletime();
|
||||
t=AQH_ReactServer_GetLatestNetworkFileTime(aqh);
|
||||
if (tNew && tNew>t) {
|
||||
DBG_INFO(NULL, "Reloading network files");
|
||||
AQH_ReactServer_ReloadUnitNets(aqh);
|
||||
AQH_ReactServer_SetLatestNetworkFileTime(aqh, tNew);
|
||||
}
|
||||
lastFileScanTime=now;
|
||||
}
|
||||
|
||||
if (_diffInSeconds(now, lastSaveTime)>AQHOME_REACT_SAVE_INTERVAL) {
|
||||
DBG_ERROR(NULL, "Writing var file");
|
||||
rv=AQH_ReactServer_WriteVarsFile(aqh);
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "Error writing runtime data");
|
||||
}
|
||||
lastSaveTime=time(NULL);
|
||||
}
|
||||
|
||||
if (timeout && (_diffInSeconds(now, timeStart)>timeout)) {
|
||||
DBG_INFO(NULL, "Timeout");
|
||||
break;
|
||||
}
|
||||
} /* while */
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _setSignalHandlers(void)
|
||||
{
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
int rv;
|
||||
|
||||
rv=_setupSigAction(&saINT, SIGINT);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
rv=_setupSigAction(&saTERM, SIGTERM);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
rv=_setupSigAction(&saHUP, SIGHUP);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
rv=_setupSigAction(&saPIPE, SIGPIPE);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
# ifdef SIGTSTP
|
||||
rv=_setupSigAction(&saTSTP, SIGTSTP);
|
||||
if (rv)
|
||||
return rv;
|
||||
# endif
|
||||
|
||||
# ifdef SIGCONT
|
||||
rv=_setupSigAction(&saCONT, SIGCONT);
|
||||
if (rv)
|
||||
return rv;
|
||||
# endif
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _setupSigAction(struct sigaction *sa, int sig)
|
||||
{
|
||||
sa->sa_handler=_signalHandler;
|
||||
sigemptyset(&sa->sa_mask);
|
||||
sa->sa_flags=0;
|
||||
if (sigaction(sig, sa, 0)) {
|
||||
DBG_ERROR(NULL, "Could not setup signal handler for signal %d", sig);
|
||||
return GWEN_ERROR_IO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _signalHandler(int s)
|
||||
{
|
||||
switch(s) {
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
case SIGHUP:
|
||||
DBG_WARN(0, "Received signal %d, stopping service in next loop.",s);
|
||||
stopService=1;
|
||||
break;
|
||||
case SIGPIPE:
|
||||
DBG_WARN(0, "Received PIPE signal");
|
||||
break;
|
||||
default:
|
||||
DBG_WARN(0, "Unknown signal %d",s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
int _testModules(int argc, char **argv)
|
||||
{
|
||||
GWEN_GUI *gui;
|
||||
GWEN_TEST_FRAMEWORK *tf;
|
||||
int rv;
|
||||
|
||||
gui=GWEN_Gui_CGui_new();
|
||||
GWEN_Gui_SetGui(gui);
|
||||
|
||||
tf=TestFramework_new();
|
||||
|
||||
rv=AQHREACT_PrgRule_AddTests(TestFramework_GetModulesRoot(tf));
|
||||
if (rv<0) {
|
||||
fprintf(stderr, "Adding module \"PrgRule\" failed.\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
rv=AQH_Path_AddTests(TestFramework_GetModulesRoot(tf));
|
||||
if (rv<0) {
|
||||
fprintf(stderr, "Adding module \"path\" failed.\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
rv=AQH_Vars_AddTests(TestFramework_GetModulesRoot(tf));
|
||||
if (rv<0) {
|
||||
fprintf(stderr, "Adding module \"vars\" failed.\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
rv=AQH_Vars_DbRead_AddTests(TestFramework_GetModulesRoot(tf));
|
||||
if (rv<0) {
|
||||
fprintf(stderr, "Adding module \"vars_dbread\" failed.\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
rv=AQH_Vars_DbWrite_AddTests(TestFramework_GetModulesRoot(tf));
|
||||
if (rv<0) {
|
||||
fprintf(stderr, "Adding module \"vars_dbwrite\" failed.\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
argc--;
|
||||
argv++;
|
||||
rv=TestFramework_Run(tf, argc, argv);
|
||||
if (rv) {
|
||||
fprintf(stderr, "SomeError in tests failed.\n");
|
||||
GWEN_Gui_SetGui(NULL);
|
||||
GWEN_Gui_free(gui);
|
||||
return 2;
|
||||
}
|
||||
TestFramework_free(tf);
|
||||
|
||||
GWEN_Gui_SetGui(NULL);
|
||||
GWEN_Gui_free(gui);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
int _diffInSeconds(time_t t1, time_t t0)
|
||||
{
|
||||
return t1-t0;
|
||||
}
|
||||
|
||||
255
apps/aqhome-react/net_read.c
Normal file
255
apps/aqhome-react/net_read.c
Normal file
@@ -0,0 +1,255 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "./net_read.h"
|
||||
#include "./server_p.h"
|
||||
#include "aqhome-react/aqhome_react_p.h"
|
||||
#include "aqhome-react/types/unit.h"
|
||||
#include "aqhome-react/units/u_module.h"
|
||||
|
||||
#include "aqhome/aqhome.h"
|
||||
|
||||
#include <gwenhywfar/xml.h>
|
||||
#include <gwenhywfar/text.h>
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static time_t _getNewestFiletimeFromFileList(const GWEN_STRINGLIST *sl);
|
||||
static AQHREACT_UNIT *_readNetworkFromFile(AQH_OBJECT *o, const char *filename);
|
||||
static GWEN_XMLNODE *_readUnitNetFileToXml(const char *sFilename);
|
||||
static int _readUnitNetFilesIntoList(AQH_OBJECT *o, const GWEN_STRINGLIST *sl, AQHREACT_UNIT_LIST *unitList);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* implementations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
time_t AQH_ReactServer_GetNewestUnitNetFiletime(void)
|
||||
{
|
||||
GWEN_STRINGLIST *sl;
|
||||
time_t t1;
|
||||
time_t t2;
|
||||
|
||||
sl=AQH_GetListOfMatchingDataFiles("aqhome/react/networks", "*.xml");
|
||||
t1=_getNewestFiletimeFromFileList(sl);
|
||||
GWEN_StringList_free(sl);
|
||||
|
||||
sl=AQH_GetListOfMatchingSysconfFiles("aqhome/react/networks", "*.xml");
|
||||
t2=_getNewestFiletimeFromFileList(sl);
|
||||
GWEN_StringList_free(sl);
|
||||
|
||||
return (t1>t2)?t1:t2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int AQH_ReactServer_ReadUnitNetFiles(AQH_OBJECT *aqh)
|
||||
{
|
||||
AQH_REACT_SERVER *xo;
|
||||
|
||||
xo=AQH_ReactServer_GetServerData(aqh);
|
||||
if (xo) {
|
||||
GWEN_STRINGLIST *sl;
|
||||
|
||||
sl=AQH_GetListOfMatchingSysconfFiles("aqhome/react/networks", "*.xml");
|
||||
if (sl) {
|
||||
int rv;
|
||||
|
||||
GWEN_StringList_Sort(sl, 1, GWEN_StringList_SortModeNoCase);
|
||||
rv=_readUnitNetFilesIntoList(aqh, sl, xo->unitList);
|
||||
GWEN_StringList_free(sl);
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "Error reading unit network files (%d)", rv);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "No unit network files");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return GWEN_ERROR_INVALID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHREACT_UNIT *AQH_ReactServer_FindAndReadDataDirNetwork(AQH_OBJECT *o, const char *networkName)
|
||||
{
|
||||
if (o && networkName) {
|
||||
AQH_REACT_SERVER *xo;
|
||||
|
||||
xo=AQH_ReactServer_GetServerData(o);
|
||||
if (xo) {
|
||||
AQHREACT_UNIT *unit;
|
||||
GWEN_BUFFER *bufFilename;
|
||||
GWEN_BUFFER *bufPath;
|
||||
const char *s;
|
||||
|
||||
bufFilename=GWEN_Buffer_new(0, 256, 0, 1);
|
||||
GWEN_Buffer_AppendString(bufFilename, "aqhome/react/networks/");
|
||||
s=networkName;
|
||||
while(*s)
|
||||
GWEN_Buffer_AppendByte(bufFilename, tolower((unsigned char) *(s++)));
|
||||
GWEN_Buffer_AppendString(bufFilename, ".xml");
|
||||
|
||||
bufPath=AQH_FindPathOfDataFile(GWEN_Buffer_GetStart(bufFilename));
|
||||
if (bufPath==NULL) {
|
||||
DBG_ERROR(NULL, "Network file \"%s\" not found in data folders", GWEN_Buffer_GetStart(bufFilename));
|
||||
GWEN_Buffer_free(bufFilename);
|
||||
return NULL;
|
||||
}
|
||||
GWEN_Buffer_free(bufFilename);
|
||||
|
||||
unit=_readNetworkFromFile(o, GWEN_Buffer_GetStart(bufPath));
|
||||
if (unit==NULL) {
|
||||
DBG_ERROR(NULL, "Error reading network from file \"%s\"", GWEN_Buffer_GetStart(bufPath));
|
||||
GWEN_Buffer_free(bufPath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GWEN_Buffer_free(bufPath);
|
||||
return unit;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
time_t _getNewestFiletimeFromFileList(const GWEN_STRINGLIST *sl)
|
||||
{
|
||||
time_t resultTime=0;
|
||||
|
||||
if (sl) {
|
||||
GWEN_STRINGLISTENTRY *se;
|
||||
|
||||
se=GWEN_StringList_FirstEntry(sl);
|
||||
while(se) {
|
||||
const char *s;
|
||||
|
||||
s=GWEN_StringListEntry_Data(se);
|
||||
if (s && *s) {
|
||||
struct stat sb;
|
||||
|
||||
if (stat(s, &sb)==0) {
|
||||
time_t t;
|
||||
|
||||
t=sb.st_mtim.tv_sec;
|
||||
if (t>resultTime)
|
||||
resultTime=t;
|
||||
}
|
||||
else {
|
||||
DBG_WARN(NULL, "Error on stat(%s): %s (%d)", s, strerror(errno), errno);
|
||||
}
|
||||
}
|
||||
se=GWEN_StringListEntry_Next(se);
|
||||
}
|
||||
}
|
||||
return resultTime;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHREACT_UNIT *_readNetworkFromFile(AQH_OBJECT *o, const char *filename)
|
||||
{
|
||||
GWEN_XMLNODE *n;
|
||||
AQHREACT_UNIT *unit;
|
||||
|
||||
n=_readUnitNetFileToXml(filename);
|
||||
if (n==NULL) {
|
||||
DBG_ERROR(NULL, "Error reading network file \"%s\"", filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unit=AqHomeReact_UnitModule_fromXml(o, n);
|
||||
if (unit==NULL) {
|
||||
DBG_ERROR(NULL, "Error reading network from file \"%s\"", filename);
|
||||
GWEN_XMLNode_free(n);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GWEN_XMLNode_free(n);
|
||||
return unit;
|
||||
}
|
||||
|
||||
|
||||
|
||||
GWEN_XMLNODE *_readUnitNetFileToXml(const char *sFilename)
|
||||
{
|
||||
GWEN_XMLNODE *rootNode;
|
||||
GWEN_XMLNODE *netNode;
|
||||
int rv;
|
||||
|
||||
rootNode=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, NULL);
|
||||
rv=GWEN_XML_ReadFile(rootNode, sFilename, GWEN_XML_FLAGS_DEFAULT);
|
||||
if (rv<0) {
|
||||
DBG_ERROR(NULL, "Error reading XML file \"%s\": %d", sFilename, rv);
|
||||
GWEN_XMLNode_free(rootNode);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
netNode=GWEN_XMLNode_FindFirstTag(rootNode, "network", NULL, NULL);
|
||||
if (netNode) {
|
||||
GWEN_XMLNode_UnlinkChild(rootNode, netNode);
|
||||
GWEN_XMLNode_free(rootNode);
|
||||
return netNode;
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "No \"network\" element in network file \"%s\"", sFilename);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _readUnitNetFilesIntoList(AQH_OBJECT *o, const GWEN_STRINGLIST *sl, AQHREACT_UNIT_LIST *unitList)
|
||||
{
|
||||
GWEN_STRINGLISTENTRY *se;
|
||||
|
||||
se=GWEN_StringList_FirstEntry(sl);
|
||||
while(se) {
|
||||
const char *s;
|
||||
|
||||
s=GWEN_StringListEntry_Data(se);
|
||||
if (s && *s) {
|
||||
AQHREACT_UNIT *unit;
|
||||
|
||||
DBG_INFO(NULL, "Reading unit network file \"%s\"", s);
|
||||
unit=_readNetworkFromFile(o, s);
|
||||
if (unit==NULL) {
|
||||
DBG_WARN(NULL, "No network read from network file \"%s\"", s);
|
||||
return GWEN_ERROR_GENERIC;
|
||||
}
|
||||
AQHREACT_Unit_List_Add(unit, unitList);
|
||||
}
|
||||
se=GWEN_StringListEntry_Next(se);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
24
apps/aqhome-react/net_read.h
Normal file
24
apps/aqhome-react/net_read.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOMEREACT_NET_READ_H
|
||||
#define AQHOMEREACT_NET_READ_H
|
||||
|
||||
|
||||
#include "./aqhome_react.h"
|
||||
|
||||
|
||||
int AQH_ReactServer_ReadUnitNetFiles(AQH_OBJECT *aqh);
|
||||
AQHREACT_UNIT *AQH_ReactServer_FindAndReadDataDirNetwork(AQH_OBJECT *aqh, const char *networkName);
|
||||
|
||||
time_t AQH_ReactServer_GetNewestUnitNetFiletime(void);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
8
apps/aqhome-react/networks/0BUILD
Normal file
8
apps/aqhome-react/networks/0BUILD
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml?>
|
||||
|
||||
<gwbuild>
|
||||
<data dist="true" install="$(datadir)/aqhome/react/networks">
|
||||
delayedoff.xml
|
||||
</data>
|
||||
|
||||
</gwbuild>
|
||||
41
apps/aqhome-react/networks/delayedoff.xml
Normal file
41
apps/aqhome-react/networks/delayedoff.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
|
||||
|
||||
<network type="delayedOff">
|
||||
|
||||
<paramdefs>
|
||||
<param name="delayTime" targetModule="stabilize1" targetParam="holdTimeLow">30</param>
|
||||
<param name="threshold" targetModule="highPass1" targetParam="threshold">70</param>
|
||||
</paramdefs>
|
||||
|
||||
<inputPorts>
|
||||
<inputPort name="input" dataType="double" />
|
||||
</inputPorts>
|
||||
|
||||
<outputPorts>
|
||||
<outputPort name="output" dataType="string" />
|
||||
</outputPorts>
|
||||
|
||||
|
||||
|
||||
<units>
|
||||
<unit id="highPass1" type="highPass" />
|
||||
|
||||
<unit id="stabilize1" type="stabilize">
|
||||
<params>
|
||||
<param name="holdTimeHigh">0.0</param>
|
||||
</params>
|
||||
</unit>
|
||||
|
||||
</units>
|
||||
|
||||
|
||||
<links>
|
||||
<!-- (sourceNet), sourceUnit, sourcePort, (targetNet), targetUnit, targetPort -->
|
||||
<link sourceUnit=".timer" sourcePort="output" targetUnit="stabilize1" targetPort="timer" />
|
||||
<link sourceUnit="." sourcePort="input" targetUnit="highPass1" targetPort="input" />
|
||||
|
||||
<link sourceUnit="highPass1" sourcePort="output" targetUnit="stabilize1" targetPort="input" />
|
||||
<link sourceUnit="stabilize1" sourcePort="output" targetUnit="." targetPort="output" />
|
||||
</links>
|
||||
</network>
|
||||
|
||||
1207
apps/aqhome-react/server.c
Normal file
1207
apps/aqhome-react/server.c
Normal file
File diff suppressed because it is too large
Load Diff
57
apps/aqhome-react/server.h
Normal file
57
apps/aqhome-react/server.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_REACT_SERVER_H
|
||||
#define AQHOME_REACT_SERVER_H
|
||||
|
||||
#include <aqhome/events2/object.h>
|
||||
|
||||
#include "aqhome-react/types/unit.h"
|
||||
#include "aqhome/data/vars.h"
|
||||
|
||||
|
||||
|
||||
AQH_OBJECT *AQH_ReactServer_new(AQH_EVENT_LOOP *eventLoop);
|
||||
int AQH_ReactServer_Init(AQH_OBJECT *o, int argc, char **argv);
|
||||
void AQH_ReactServer_Fini(AQH_OBJECT *o);
|
||||
|
||||
int AQH_ReactServer_GetTimeout(const AQH_OBJECT *o);
|
||||
void AQH_ReactServer_SetVarsFile(AQH_OBJECT *o, const char *s);
|
||||
time_t AQH_ReactServer_GetLatestNetworkFileTime(const AQH_OBJECT *aqh);
|
||||
void AQH_ReactServer_SetLatestNetworkFileTime(AQH_OBJECT *aqh, time_t t);
|
||||
|
||||
AQHREACT_UNIT *AQH_ReactServer_GetTimerUnit(const AQH_OBJECT *aqh);
|
||||
AQHREACT_UNIT *AQH_ReactServer_GetServerVarChangeUnit(const AQH_OBJECT *aqh);
|
||||
AQH_OBJECT *AQH_ReactServer_GetBrokerEndpoint(const AQH_OBJECT *o);
|
||||
|
||||
void AQH_ReactServer_HandleBrokerMsgs(AQH_OBJECT *o);
|
||||
void AQH_ReactServer_CheckBrokerConnection(AQH_OBJECT *o);
|
||||
void AQH_ReactServer_ProcessAllUnits(AQH_OBJECT *o);
|
||||
|
||||
AQHREACT_UNIT *AQH_ReactServer_CreateUnitByName(AQH_OBJECT *aqh, const char *unitType);
|
||||
int AQH_ReactServer_ReloadUnitNets(AQH_OBJECT *o);
|
||||
AQHREACT_UNIT *AQH_ReactServer_FindUnitByUnitId(const AQH_OBJECT *aqh, const char *unitId);
|
||||
void AQH_ReactServer_AddUnit(AQH_OBJECT *aqh, AQHREACT_UNIT *unit);
|
||||
|
||||
|
||||
|
||||
int AQH_ReactServer_SetCharValue(AQH_OBJECT *aqh, const char *path, const char *value);
|
||||
const char *AQH_ReactServer_GetCharValue(AQH_OBJECT *aqh, const char *path, int idx, const char *defaultValue);
|
||||
int AQH_ReactServer_SetDoubleValue(AQH_OBJECT *aqh, const char *path, double value);
|
||||
double AQH_ReactServer_GetDoubleValue(AQH_OBJECT *aqh, const char *path, int idx, double defaultValue);
|
||||
int AQH_ReactServer_SetIntValue(AQH_OBJECT *aqh, const char *path, int value);
|
||||
int AQH_ReactServer_GetIntValue(AQH_OBJECT *aqh, const char *path, int idx, int defaultValue);
|
||||
int AQH_ReactServer_IncIntValue(AQH_OBJECT *aqh, const char *path, int startValue, int defaultValue);
|
||||
int AQH_ReactServer_DecIntValue(AQH_OBJECT *aqh, const char *path, int startValue, int defaultValue);
|
||||
|
||||
int AQH_ReactServer_WriteVarsFile(AQH_OBJECT *aqh);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
64
apps/aqhome-react/server_p.h
Normal file
64
apps/aqhome-react/server_p.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_REACT_SERVER_P_H
|
||||
#define AQHOME_REACT_SERVER_P_H
|
||||
|
||||
|
||||
#include "./server.h"
|
||||
|
||||
#include <gwenhywfar/db.h>
|
||||
|
||||
|
||||
|
||||
/* default values */
|
||||
#define AQHOME_REACT_DEFAULT_PIDFILE "/var/run/aqhome-react.pid"
|
||||
#define AQHOME_REACT_DEFAULT_DATADIR "/var/lib/aqhome-react"
|
||||
#define AQHOME_REACT_DEFAULT_VARSFILE "vars.conf"
|
||||
|
||||
#define AQHOME_REACT_DEFAULT_BROKER_PORT 1899
|
||||
#define AQHOME_REACT_DEFAULT_BROKER_CLIENTID "react"
|
||||
|
||||
|
||||
|
||||
typedef struct AQH_REACT_SERVER AQH_REACT_SERVER;
|
||||
struct AQH_REACT_SERVER {
|
||||
AQH_OBJECT *brokerEndpoint;
|
||||
|
||||
GWEN_DB_NODE *dbArgs;
|
||||
char *pidFile;
|
||||
char *varsFile;
|
||||
|
||||
int timeout; /* timeout for run e.g. inside valgrind */
|
||||
|
||||
char *brokerAddress;
|
||||
int brokerPort;
|
||||
char *brokerClientId;
|
||||
|
||||
AQHREACT_UNIT *timerUnit;
|
||||
AQHREACT_UNIT *serverVarChangeUnit;
|
||||
AQHREACT_UNIT *localVarChangeUnit;
|
||||
AQHREACT_UNIT_LIST *unitList;
|
||||
|
||||
AQH_VARS *localVars;
|
||||
|
||||
time_t latestNetworkFileTime;
|
||||
|
||||
time_t timestampBrokerDown;
|
||||
|
||||
int timeoutInSeconds;
|
||||
|
||||
};
|
||||
|
||||
|
||||
AQH_REACT_SERVER *AQH_ReactServer_GetServerData(const AQH_OBJECT *o);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
203
apps/aqhome-react/suntimes.c
Normal file
203
apps/aqhome-react/suntimes.c
Normal file
@@ -0,0 +1,203 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2024 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "./suntimes.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static GWEN_TIME *_getSunTime(const GWEN_DATE *date, double lat, double lng, int calcSunrise);
|
||||
static double _calcSunTime(const GWEN_DATE *date, double lat, double lng, int calcSunrise);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* implementations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
GWEN_TIME *AQHomeReact_GetSunriseTimeForDateAndLoc(const GWEN_DATE *date, double lat, double lng)
|
||||
{
|
||||
return _getSunTime(date, lat, lng, 1);
|
||||
}
|
||||
|
||||
|
||||
GWEN_TIME *AQHomeReact_GetSunsetTimeForDateAndLoc(const GWEN_DATE *date, double lat, double lng)
|
||||
{
|
||||
return _getSunTime(date, lat, lng, 0);
|
||||
}
|
||||
|
||||
|
||||
GWEN_TIME *_getSunTime(const GWEN_DATE *date, double lat, double lng, int calcSunrise)
|
||||
{
|
||||
double timeInFloat=fmod(24+_calcSunTime(date, lat, lng, calcSunrise), 24);
|
||||
double hours;
|
||||
double minutes = modf(timeInFloat, &hours)*60;
|
||||
GWEN_TIME *ti=GWEN_Time_new(GWEN_Date_GetYear(date), GWEN_Date_GetMonth(date)-1, GWEN_Date_GetDay(date),
|
||||
hours, minutes, 0, 1);
|
||||
return ti;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
int _test(void) {
|
||||
double lat = 52.619425; // Latitude of location.
|
||||
double lng = 10.087891; // Longitude of location.
|
||||
int y=2024;
|
||||
int m=4;
|
||||
int d;
|
||||
|
||||
for (d=19; d<=24; d++) {
|
||||
GWEN_DATE *date;
|
||||
GWEN_TIME *sunrise;
|
||||
GWEN_TIME *sunset;
|
||||
int sunsetHours, sunsetMinutes, sunsetSecs;
|
||||
int sunriseHours, sunriseMinutes, sunriseSecs;
|
||||
|
||||
date=GWEN_Date_fromGregorian(y, m, d);
|
||||
sunrise=AQHomeReact_GetSunriseTimeForDateAndLoc(date, lat, lng);
|
||||
sunset=AQHomeReact_GetSunsetTimeForDateAndLoc(date, lat, lng);
|
||||
|
||||
GWEN_Time_GetBrokenDownTime(sunset, &sunsetHours, &sunsetMinutes, &sunsetSecs);
|
||||
GWEN_Time_GetBrokenDownTime(sunrise, &sunriseHours, &sunriseMinutes, &sunriseSecs);
|
||||
|
||||
fprintf(stdout, "date=%04d-%02d-%02d: sunrise=%02d:%02d sunset=%02d:%02d\n",
|
||||
GWEN_Date_GetYear(date), GWEN_Date_GetMonth(date), GWEN_Date_GetDay(date),
|
||||
sunriseHours, sunriseMinutes,
|
||||
sunsetHours, sunsetMinutes);
|
||||
GWEN_Time_free(sunset);
|
||||
GWEN_Time_free(sunrise);
|
||||
GWEN_Date_free(date);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Calculate time of sunset or sunrise at a given position and date according to an algorithm presented
|
||||
* on this site:
|
||||
* http://edwilliams.org/sunrise_sunset_algorithm.htm
|
||||
*
|
||||
* Original notes follow.
|
||||
*
|
||||
* -----------------------------
|
||||
* Source:
|
||||
* Almanac for Computers, 1990
|
||||
* published by Nautical Almanac Office
|
||||
* United States Naval Observatory
|
||||
* Washington, DC 20392
|
||||
*
|
||||
* Inputs:
|
||||
* date : date for which to calculate sun time
|
||||
* lat, lng: latitude and longitude of the location to calculate for (west and north are positive values)
|
||||
* ZENITH : Sun's zenith for sunrise/sunset
|
||||
* official = 90 degrees 50'
|
||||
* civil = 96 degrees
|
||||
* nautical = 102 degrees
|
||||
* astronomical = 108 degrees
|
||||
*
|
||||
* NOTE: longitude is positive for East and negative for West
|
||||
* NOTE: the algorithm assumes the use of a calculator with the
|
||||
* trig functions in "degree" (rather than "radian") mode. Most
|
||||
* programming languages assume radian arguments, requiring back
|
||||
* and forth convertions. The factor is 180/pi. So, for instance,
|
||||
* the equation RA = atan(0.91764 * tan(L)) would be coded as RA
|
||||
* = (180/pi)*atan(0.91764 * tan((pi/180)*L)) to give a degree
|
||||
* answer with a degree input for L.
|
||||
*/
|
||||
double _calcSunTime(const GWEN_DATE *date, double lat, double lng, int calcSunrise)
|
||||
{
|
||||
const double PI=3.1415926535897932384626433832795;
|
||||
const double ZENITH=-.83; /* accounts for atmospheric refraction (assuming height of 0m above horizon) */
|
||||
double PiBy180=PI/180.0;
|
||||
double N;
|
||||
double lngHour;
|
||||
double t;
|
||||
double M;
|
||||
double L;
|
||||
double RA;
|
||||
double Lquadrant;
|
||||
double RAquadrant;
|
||||
double sinDec;
|
||||
double cosDec;
|
||||
double cosH;
|
||||
double H;
|
||||
double T;
|
||||
double UT;
|
||||
|
||||
/* 1. first calculate the day of the year */
|
||||
N=GWEN_Date_DaysInYear(date);
|
||||
|
||||
/* 2. convert the longitude to hour value and calculate an approximate time */
|
||||
lngHour = lng/15.0;
|
||||
if (calcSunrise)
|
||||
t = N+((6-lngHour)/24);
|
||||
else
|
||||
t = N+((18-lngHour)/24);
|
||||
|
||||
/* 3. calculate the Sun's mean anomaly */
|
||||
M = (0.9856*t)-3.289;
|
||||
|
||||
/* 4. calculate the Sun's true longitude */
|
||||
L = fmod(M+(1.916*sin(PiBy180*M)) + (0.020 * sin(2*PiBy180*M)) + 282.634, 360.0);
|
||||
|
||||
/* 5a. calculate the Sun's right ascension */
|
||||
RA = fmod(180/PI*atan(0.91764*tan(PiBy180*L)), 360.0);
|
||||
|
||||
/* 5b. right ascension value needs to be in the same quadrant as L */
|
||||
Lquadrant = floor(L/90)*90;
|
||||
RAquadrant = floor(RA/90)*90;
|
||||
RA = RA + (Lquadrant-RAquadrant);
|
||||
|
||||
/* 5c. right ascension value needs to be converted into hours */
|
||||
RA = RA / 15;
|
||||
|
||||
/* 6. calculate the Sun's declination */
|
||||
sinDec = 0.39782*sin(PiBy180*L);
|
||||
cosDec = cos(asin(sinDec));
|
||||
|
||||
/* 7a. calculate the Sun's local hour angle */
|
||||
cosH = (sin(PiBy180*ZENITH)-(sinDec*sin(PiBy180*lat))) / (cosDec*cos(PiBy180*lat));
|
||||
/* cosH >1 : sun never rises on this location and date
|
||||
* cosH <-1: sun never sets on this location and date
|
||||
*/
|
||||
|
||||
/* 7b. finish calculating H and convert into hours */
|
||||
if (calcSunrise)
|
||||
H = 360-(180/PI)*acos(cosH);
|
||||
else
|
||||
H = (180/PI)*acos(cosH);
|
||||
H = H/15;
|
||||
|
||||
/* 8. calculate local mean time of rising/setting */
|
||||
T = H+RA-(0.06571*t)-6.622;
|
||||
|
||||
/* 9. adjust back to UTC */
|
||||
UT = fmod(T-lngHour, 24.0);
|
||||
|
||||
/* we don't do step 10. here which would be to convert UT into local time as we want UTC */
|
||||
return UT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
47
apps/aqhome-react/suntimes.h
Normal file
47
apps/aqhome-react/suntimes.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2024 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOMEREACT_SUNTIMES_H
|
||||
#define AQHOMEREACT_SUNTIMES_H
|
||||
|
||||
|
||||
#include <gwenhywfar/gwendate.h>
|
||||
#include <gwenhywfar/gwentime.h>
|
||||
|
||||
|
||||
/**
|
||||
* Returns the sunrise time on the provided day and location.
|
||||
*
|
||||
* You can get the location by just clicking on a position on your chosen map website (e.g. "G'maps").
|
||||
*
|
||||
* @return time of sunrise for the given date and location
|
||||
* @param date date for which to calculate
|
||||
* @param lat latitude of the location to calculate for (positive for east)
|
||||
* @param lng longitude of the location to calculate for (positive for north)
|
||||
*/
|
||||
GWEN_TIME *AQHomeReact_GetSunriseTimeForDateAndLoc(const GWEN_DATE *date, double lat, double lng);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the sunset time on the provided day and location.
|
||||
*
|
||||
* You can get the location by just clicking on a position on your chosen map website (e.g. "G'maps").
|
||||
*
|
||||
* @return time of sunset for the given date and location
|
||||
* @param date date for which to calculate
|
||||
* @param lat latitude of the location to calculate for (positive for east)
|
||||
* @param lng longitude of the location to calculate for (positive for north)
|
||||
*/
|
||||
GWEN_TIME *AQHomeReact_GetSunsetTimeForDateAndLoc(const GWEN_DATE *date, double lat, double lng);
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
85
apps/aqhome-react/types/0BUILD
Normal file
85
apps/aqhome-react/types/0BUILD
Normal file
@@ -0,0 +1,85 @@
|
||||
<?xml?>
|
||||
|
||||
<gwbuild>
|
||||
|
||||
<target type="ConvenienceLibrary" name="aqhreact_types" >
|
||||
|
||||
<includes type="c" >
|
||||
$(gwenhywfar_cflags)
|
||||
-I$(topsrcdir)
|
||||
-I$(topbuilddir)
|
||||
-I$(topsrcdir)/apps
|
||||
-I$(topbuilddir)/apps
|
||||
-I$(builddir)
|
||||
-I$(srcdir)
|
||||
</includes>
|
||||
|
||||
<includes type="tm2" >
|
||||
--include=$(builddir)
|
||||
--include=$(srcdir)
|
||||
</includes>
|
||||
|
||||
<setVar name="local/cflags">$(visibility_cflags)</setVar>
|
||||
|
||||
<setVar name="tm2flags" >
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/typefiles" >
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_sources" >
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_headers_pub">
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_headers_priv" >
|
||||
</setVar>
|
||||
|
||||
<headers dist="true" >
|
||||
dataobject.h
|
||||
dataobject_p.h
|
||||
port.h
|
||||
port_p.h
|
||||
link.h
|
||||
link_p.h
|
||||
param.h
|
||||
param_p.h
|
||||
unit.h
|
||||
unit_p.h
|
||||
prgrule.h
|
||||
prgrule_p.h
|
||||
</headers>
|
||||
|
||||
<sources>
|
||||
$(local/typefiles)
|
||||
dataobject.c
|
||||
port.c
|
||||
link.c
|
||||
param.c
|
||||
unit.c
|
||||
prgrule.c
|
||||
</sources>
|
||||
|
||||
<useTargets>
|
||||
</useTargets>
|
||||
|
||||
<libraries>
|
||||
</libraries>
|
||||
|
||||
<subdirs>
|
||||
</subdirs>
|
||||
|
||||
|
||||
<extradist>
|
||||
prgrule-t.h
|
||||
prgrule-t.c
|
||||
</extradist>
|
||||
|
||||
|
||||
</target>
|
||||
|
||||
|
||||
</gwbuild>
|
||||
|
||||
|
||||
264
apps/aqhome-react/types/dataobject.c
Normal file
264
apps/aqhome-react/types/dataobject.c
Normal file
@@ -0,0 +1,264 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2024 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "./dataobject_p.h"
|
||||
|
||||
#include <gwenhywfar/list.h>
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
|
||||
|
||||
GWEN_LIST_FUNCTIONS(AQHREACT_DATAOBJECT, AQHREACT_DataObject)
|
||||
|
||||
|
||||
|
||||
|
||||
AQHREACT_DATAOBJECT *AQHREACT_DataObject_new()
|
||||
{
|
||||
AQHREACT_DATAOBJECT *dataObject;
|
||||
|
||||
GWEN_NEW_OBJECT(AQHREACT_DATAOBJECT, dataObject);
|
||||
dataObject->_refCount=1;
|
||||
GWEN_LIST_INIT(AQHREACT_DATAOBJECT, dataObject);
|
||||
return dataObject;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_DataObject_free(AQHREACT_DATAOBJECT *dataObject)
|
||||
{
|
||||
if (dataObject) {
|
||||
if (dataObject->_refCount==1) {
|
||||
GWEN_LIST_FINI(AQHREACT_DATAOBJECT, dataObject);
|
||||
free(dataObject->systemValueId);
|
||||
free(dataObject->valueUnits);
|
||||
free(dataObject->stringData);
|
||||
dataObject->_refCount=0;
|
||||
GWEN_FREE_OBJECT(dataObject);
|
||||
}
|
||||
else if (dataObject->_refCount<1) {
|
||||
DBG_ERROR(NULL, "dataobject already freed!");
|
||||
}
|
||||
else
|
||||
dataObject->_refCount--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHREACT_DATAOBJECT *AQHREACT_DataObject_dup(const AQHREACT_DATAOBJECT *origObject)
|
||||
{
|
||||
if (origObject) {
|
||||
AQHREACT_DATAOBJECT *dataObject;
|
||||
|
||||
dataObject=AQHREACT_DataObject_new();
|
||||
dataObject->valueId=origObject->valueId;
|
||||
AQHREACT_DataObject_SetSystemValueId(dataObject, origObject->systemValueId);
|
||||
AQHREACT_DataObject_SetValueUnits(dataObject, origObject->valueUnits);
|
||||
dataObject->timestamp=origObject->timestamp;
|
||||
dataObject->dataType=origObject->dataType;
|
||||
dataObject->doubleData=origObject->doubleData;
|
||||
dataObject->intData=origObject->intData;
|
||||
AQHREACT_DataObject_SetStringData(dataObject, origObject->stringData);
|
||||
return dataObject;
|
||||
}
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint64_t AQHREACT_DataObject_GetValueId(const AQHREACT_DATAOBJECT *dataObject)
|
||||
{
|
||||
return dataObject?(dataObject->valueId):0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_DataObject_SetValueId(AQHREACT_DATAOBJECT *dataObject, uint64_t i)
|
||||
{
|
||||
if (dataObject)
|
||||
dataObject->valueId=i;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const char *AQHREACT_DataObject_GetSystemValueId(const AQHREACT_DATAOBJECT *dataObject)
|
||||
{
|
||||
return dataObject?(dataObject->systemValueId):NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_DataObject_SetSystemValueId(AQHREACT_DATAOBJECT *dataObject, const char *s)
|
||||
{
|
||||
if (dataObject) {
|
||||
free(dataObject->systemValueId);
|
||||
dataObject->systemValueId=(s && *s)?strdup(s):NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const char *AQHREACT_DataObject_GetValueUnits(const AQHREACT_DATAOBJECT *dataObject)
|
||||
{
|
||||
return dataObject?(dataObject->valueUnits):NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_DataObject_SetValueUnits(AQHREACT_DATAOBJECT *dataObject, const char *s)
|
||||
{
|
||||
if (dataObject) {
|
||||
free(dataObject->valueUnits);
|
||||
dataObject->valueUnits=(s && *s)?strdup(s):NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint64_t AQHREACT_DataObject_GetTimestamp(const AQHREACT_DATAOBJECT *dataObject)
|
||||
{
|
||||
return dataObject?(dataObject->timestamp):0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_DataObject_SetTimestamp(AQHREACT_DATAOBJECT *dataObject, uint64_t i)
|
||||
{
|
||||
if (dataObject)
|
||||
dataObject->timestamp=i;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int AQHREACT_DataObject_GetDataType(const AQHREACT_DATAOBJECT *dataObject)
|
||||
{
|
||||
return dataObject?(dataObject->dataType):AQHREACT_DATAOBJECTTYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_DataObject_SetDataType(AQHREACT_DATAOBJECT *dataObject, int i)
|
||||
{
|
||||
if (dataObject)
|
||||
dataObject->dataType=i;
|
||||
}
|
||||
|
||||
|
||||
|
||||
double AQHREACT_DataObject_GetDoubleData(const AQHREACT_DATAOBJECT *dataObject)
|
||||
{
|
||||
return dataObject?(dataObject->doubleData):0.0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_DataObject_SetDoubleData(AQHREACT_DATAOBJECT *dataObject, double i)
|
||||
{
|
||||
if (dataObject)
|
||||
dataObject->doubleData=i;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const char *AQHREACT_DataObject_GetStringData(const AQHREACT_DATAOBJECT *dataObject)
|
||||
{
|
||||
return dataObject?(dataObject->stringData):NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_DataObject_SetStringData(AQHREACT_DATAOBJECT *dataObject, const char *s)
|
||||
{
|
||||
if (dataObject) {
|
||||
free(dataObject->stringData);
|
||||
dataObject->stringData=(s && *s)?strdup(s):NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int AQHREACT_DataObject_GetIntData(const AQHREACT_DATAOBJECT *dataObject)
|
||||
{
|
||||
return dataObject?(dataObject->intData):0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_DataObject_SetIntData(AQHREACT_DATAOBJECT *dataObject, int i)
|
||||
{
|
||||
if (dataObject)
|
||||
dataObject->intData=i;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const char *AQHREACT_DataObjectType_toString(int t)
|
||||
{
|
||||
switch(t) {
|
||||
case AQHREACT_DATAOBJECTTYPE_DOUBLE: return "double";
|
||||
case AQHREACT_DATAOBJECTTYPE_STRING: return "string";
|
||||
case AQHREACT_DATAOBJECTTYPE_INT: return "int";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int AQHREACT_DataObjectType_fromString(const char *s)
|
||||
{
|
||||
if (s && *s) {
|
||||
if (strcasecmp(s, "double")==0)
|
||||
return AQHREACT_DATAOBJECTTYPE_DOUBLE;
|
||||
else if (strcasecmp(s, "string")==0)
|
||||
return AQHREACT_DATAOBJECTTYPE_STRING;
|
||||
else if (strcasecmp(s, "int")==0)
|
||||
return AQHREACT_DATAOBJECTTYPE_INT;
|
||||
}
|
||||
|
||||
return AQHREACT_DATAOBJECTTYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_DataObject_Dump(const AQHREACT_DATAOBJECT *dataObject, GWEN_BUFFER *buf, int indent)
|
||||
{
|
||||
if (dataObject && buf) {
|
||||
const char *sVarSystemId;
|
||||
|
||||
sVarSystemId=AQHREACT_DataObject_GetSystemValueId(dataObject);
|
||||
|
||||
if (indent)
|
||||
GWEN_Buffer_FillWithBytes(buf, ' ', indent);
|
||||
|
||||
switch(dataObject->dataType) {
|
||||
case AQHREACT_DATAOBJECTTYPE_DOUBLE:
|
||||
GWEN_Buffer_AppendArgs(buf, "%f (DOUBLE) [%s]\n", AQHREACT_DataObject_GetDoubleData(dataObject), sVarSystemId?sVarSystemId:"");
|
||||
break;
|
||||
case AQHREACT_DATAOBJECTTYPE_STRING:
|
||||
GWEN_Buffer_AppendArgs(buf, "%s (STRING) [%s]\n", AQHREACT_DataObject_GetStringData(dataObject), sVarSystemId?sVarSystemId:"");
|
||||
break;
|
||||
case AQHREACT_DATAOBJECTTYPE_INT:
|
||||
GWEN_Buffer_AppendArgs(buf, "%d (INT) [%s]\n", AQHREACT_DataObject_GetIntData(dataObject), sVarSystemId?sVarSystemId:"");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
70
apps/aqhome-react/types/dataobject.h
Normal file
70
apps/aqhome-react/types/dataobject.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_REACT_DATAOBJECT_H
|
||||
#define AQHOME_REACT_DATAOBJECT_H
|
||||
|
||||
|
||||
#include <gwenhywfar/list.h>
|
||||
|
||||
|
||||
typedef struct AQHREACT_DATAOBJECT AQHREACT_DATAOBJECT;
|
||||
GWEN_LIST_FUNCTION_DEFS(AQHREACT_DATAOBJECT, AQHREACT_DataObject)
|
||||
|
||||
|
||||
enum {
|
||||
AQHREACT_DATAOBJECTTYPE_UNKNOWN=0,
|
||||
AQHREACT_DATAOBJECTTYPE_DOUBLE,
|
||||
AQHREACT_DATAOBJECTTYPE_STRING,
|
||||
AQHREACT_DATAOBJECTTYPE_INT
|
||||
};
|
||||
|
||||
|
||||
#include "aqhome-react/aqhome_react.h"
|
||||
|
||||
|
||||
|
||||
AQHREACT_DATAOBJECT *AQHREACT_DataObject_new();
|
||||
void AQHREACT_DataObject_free(AQHREACT_DATAOBJECT *dataObject);
|
||||
AQHREACT_DATAOBJECT *AQHREACT_DataObject_dup(const AQHREACT_DATAOBJECT *origObject);
|
||||
|
||||
|
||||
uint64_t AQHREACT_DataObject_GetValueId(const AQHREACT_DATAOBJECT *dataObject);
|
||||
void AQHREACT_DataObject_SetValueId(AQHREACT_DATAOBJECT *dataObject, uint64_t i);
|
||||
|
||||
const char *AQHREACT_DataObject_GetSystemValueId(const AQHREACT_DATAOBJECT *dataObject);
|
||||
void AQHREACT_DataObject_SetSystemValueId(AQHREACT_DATAOBJECT *dataObject, const char *s);
|
||||
|
||||
const char *AQHREACT_DataObject_GetValueUnits(const AQHREACT_DATAOBJECT *dataObject);
|
||||
void AQHREACT_DataObject_SetValueUnits(AQHREACT_DATAOBJECT *dataObject, const char *s);
|
||||
|
||||
uint64_t AQHREACT_DataObject_GetTimestamp(const AQHREACT_DATAOBJECT *dataObject);
|
||||
void AQHREACT_DataObject_SetTimestamp(AQHREACT_DATAOBJECT *dataObject, uint64_t i);
|
||||
|
||||
int AQHREACT_DataObject_GetDataType(const AQHREACT_DATAOBJECT *dataObject);
|
||||
void AQHREACT_DataObject_SetDataType(AQHREACT_DATAOBJECT *dataObject, int i);
|
||||
|
||||
|
||||
double AQHREACT_DataObject_GetDoubleData(const AQHREACT_DATAOBJECT *dataObject);
|
||||
void AQHREACT_DataObject_SetDoubleData(AQHREACT_DATAOBJECT *dataObject, double i);
|
||||
|
||||
const char *AQHREACT_DataObject_GetStringData(const AQHREACT_DATAOBJECT *dataObject);
|
||||
void AQHREACT_DataObject_SetStringData(AQHREACT_DATAOBJECT *dataObject, const char *s);
|
||||
|
||||
int AQHREACT_DataObject_GetIntData(const AQHREACT_DATAOBJECT *dataObject);
|
||||
void AQHREACT_DataObject_SetIntData(AQHREACT_DATAOBJECT *dataObject, int i);
|
||||
|
||||
void AQHREACT_DataObject_Dump(const AQHREACT_DATAOBJECT *dataObject, GWEN_BUFFER *buf, int indent);
|
||||
|
||||
|
||||
const char *AQHREACT_DataObjectType_toString(int t);
|
||||
int AQHREACT_DataObjectType_fromString(const char *s);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
32
apps/aqhome-react/types/dataobject_p.h
Normal file
32
apps/aqhome-react/types/dataobject_p.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_REACT_DATAOBJECT_P_H
|
||||
#define AQHOME_REACT_DATAOBJECT_P_H
|
||||
|
||||
|
||||
#include "aqhome-react/types/dataobject.h"
|
||||
|
||||
|
||||
struct AQHREACT_DATAOBJECT {
|
||||
GWEN_LIST_ELEMENT(AQHREACT_DATAOBJECT)
|
||||
|
||||
int _refCount;
|
||||
uint64_t valueId;
|
||||
char *systemValueId;
|
||||
char *valueUnits;
|
||||
uint64_t timestamp;
|
||||
int dataType;
|
||||
double doubleData;
|
||||
int intData;
|
||||
char *stringData;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
117
apps/aqhome-react/types/link.c
Normal file
117
apps/aqhome-react/types/link.c
Normal file
@@ -0,0 +1,117 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2024 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "./link_p.h"
|
||||
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
|
||||
GWEN_LIST_FUNCTIONS(AQHREACT_LINK, AQHREACT_Link)
|
||||
|
||||
|
||||
AQHREACT_LINK *AQHREACT_Link_new()
|
||||
{
|
||||
AQHREACT_LINK *lnk;
|
||||
|
||||
GWEN_NEW_OBJECT(AQHREACT_LINK, lnk);
|
||||
GWEN_LIST_INIT(AQHREACT_LINK, lnk);
|
||||
|
||||
return lnk;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Link_free(AQHREACT_LINK *lnk)
|
||||
{
|
||||
if (lnk) {
|
||||
GWEN_LIST_FINI(AQHREACT_LINK, lnk);
|
||||
GWEN_FREE_OBJECT(lnk);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHREACT_UNIT *AQHREACT_Link_GetTargetUnit(const AQHREACT_LINK *lnk)
|
||||
{
|
||||
return lnk?lnk->targetUnit:NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Link_SetTargetUnit(AQHREACT_LINK *lnk, AQHREACT_UNIT *unit)
|
||||
{
|
||||
if (lnk)
|
||||
lnk->targetUnit=unit;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHREACT_PORT *AQHREACT_Link_GetTargetPort(const AQHREACT_LINK *lnk)
|
||||
{
|
||||
return lnk?lnk->targetPort:NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Link_SetTargetPort(AQHREACT_LINK *lnk, AQHREACT_PORT *port)
|
||||
{
|
||||
if (lnk)
|
||||
lnk->targetPort=port;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Link_Dump(const AQHREACT_LINK *lnk, GWEN_BUFFER *buf, int indent)
|
||||
{
|
||||
if (lnk && buf) {
|
||||
const char *unitName;
|
||||
const char *unitId;
|
||||
const char *portName;
|
||||
|
||||
unitName=(lnk->targetUnit)?AQHREACT_Unit_GetTypeName(lnk->targetUnit):NULL;
|
||||
unitId=(lnk->targetUnit)?AQHREACT_Unit_GetId(lnk->targetUnit):NULL;
|
||||
portName=(lnk->targetPort)?AQHREACT_Port_GetName(lnk->targetPort):NULL;
|
||||
if (indent)
|
||||
GWEN_Buffer_FillWithBytes(buf, ' ', indent);
|
||||
GWEN_Buffer_AppendArgs(buf, "- target unit id: %s (%s), target slot name: %s [%d]\n",
|
||||
unitId?unitId:"<no id>",
|
||||
unitName?unitName:"<no name>",
|
||||
portName?portName:"<no port>",
|
||||
(lnk->targetPort)?AQHREACT_Port_GetIdForUnit(lnk->targetPort):0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Link_List_Dump(const AQHREACT_LINK_LIST *lnkList, GWEN_BUFFER *buf, int indent, const char *title)
|
||||
{
|
||||
if (lnkList && buf) {
|
||||
const AQHREACT_LINK *lnk;
|
||||
|
||||
if (indent)
|
||||
GWEN_Buffer_FillWithBytes(buf, ' ', indent);
|
||||
GWEN_Buffer_AppendArgs(buf, "%s\n", (title && *title)?title:"Link List:\n");
|
||||
|
||||
lnk=AQHREACT_Link_List_First(lnkList);
|
||||
while(lnk) {
|
||||
AQHREACT_Link_Dump(lnk, buf, indent+2);
|
||||
lnk=AQHREACT_Link_List_Next(lnk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
39
apps/aqhome-react/types/link.h
Normal file
39
apps/aqhome-react/types/link.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2024 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_REACT_LINK_H
|
||||
#define AQHOME_REACT_LINK_H
|
||||
|
||||
|
||||
#include <gwenhywfar/list.h>
|
||||
|
||||
|
||||
typedef struct AQHREACT_LINK AQHREACT_LINK;
|
||||
GWEN_LIST_FUNCTION_DEFS(AQHREACT_LINK, AQHREACT_Link)
|
||||
|
||||
|
||||
#include "aqhome-react/types/unit.h"
|
||||
#include "aqhome-react/types/port.h"
|
||||
|
||||
|
||||
AQHREACT_LINK *AQHREACT_Link_new();
|
||||
void AQHREACT_Link_free(AQHREACT_LINK *lnk);
|
||||
|
||||
|
||||
AQHREACT_UNIT *AQHREACT_Link_GetTargetUnit(const AQHREACT_LINK *lnk);
|
||||
void AQHREACT_Link_SetTargetUnit(AQHREACT_LINK *lnk, AQHREACT_UNIT *unit);
|
||||
|
||||
AQHREACT_PORT *AQHREACT_Link_GetTargetPort(const AQHREACT_LINK *lnk);
|
||||
void AQHREACT_Link_SetTargetPort(AQHREACT_LINK *lnk, AQHREACT_PORT *port);
|
||||
|
||||
void AQHREACT_Link_Dump(const AQHREACT_LINK *lnk, GWEN_BUFFER *buf, int indent);
|
||||
void AQHREACT_Link_List_Dump(const AQHREACT_LINK_LIST *lnkList, GWEN_BUFFER *buf, int indent, const char *title);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
25
apps/aqhome-react/types/link_p.h
Normal file
25
apps/aqhome-react/types/link_p.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2024 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_REACT_LINK_P_H
|
||||
#define AQHOME_REACT_LINK_P_H
|
||||
|
||||
|
||||
#include "aqhome-react/types/link.h"
|
||||
|
||||
|
||||
struct AQHREACT_LINK {
|
||||
GWEN_LIST_ELEMENT(AQHREACT_LINK)
|
||||
|
||||
AQHREACT_UNIT *targetUnit;
|
||||
AQHREACT_PORT *targetPort;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
294
apps/aqhome-react/types/param.c
Normal file
294
apps/aqhome-react/types/param.c
Normal file
@@ -0,0 +1,294 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2024 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "./param_p.h"
|
||||
#include "./dataobject.h"
|
||||
|
||||
#include <gwenhywfar/debug.h>
|
||||
#include <gwenhywfar/text.h>
|
||||
|
||||
|
||||
|
||||
GWEN_LIST_FUNCTIONS(AQHREACT_PARAM, AQHREACT_Param)
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static void _setDbValueFromParamString(const AQHREACT_PARAM *param, GWEN_DB_NODE *db, uint32_t dbFlags);
|
||||
static void _setDbValueFromParamDouble(const AQHREACT_PARAM *param, GWEN_DB_NODE *db, uint32_t dbFlags);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* implementations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
AQHREACT_PARAM *AQHREACT_Param_new()
|
||||
{
|
||||
AQHREACT_PARAM *param;
|
||||
|
||||
GWEN_NEW_OBJECT(AQHREACT_PARAM, param);
|
||||
GWEN_LIST_INIT(AQHREACT_PARAM, param);
|
||||
|
||||
return param;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Param_free(AQHREACT_PARAM *param)
|
||||
{
|
||||
if (param) {
|
||||
GWEN_LIST_FINI(AQHREACT_PARAM, param);
|
||||
free(param->name);
|
||||
free(param->description);
|
||||
free(param->stringValue);
|
||||
GWEN_FREE_OBJECT(param);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const char *AQHREACT_Param_GetName(const AQHREACT_PARAM *param)
|
||||
{
|
||||
return param?param->name:NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Param_SetName(AQHREACT_PARAM *param, const char *s)
|
||||
{
|
||||
if (param) {
|
||||
free(param->name);
|
||||
param->name=s?strdup(s):NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const char *AQHREACT_Param_GetDescription(const AQHREACT_PARAM *param)
|
||||
{
|
||||
return param?param->description:NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Param_SetDescription(AQHREACT_PARAM *param, const char *s)
|
||||
{
|
||||
if (param) {
|
||||
free(param->description);
|
||||
param->description=s?strdup(s):NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int AQHREACT_Param_GetDataType(const AQHREACT_PARAM *param)
|
||||
{
|
||||
return param?param->dataType:AQHREACT_DATAOBJECTTYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Param_SetDataType(AQHREACT_PARAM *param, int i)
|
||||
{
|
||||
if (param)
|
||||
param->dataType=i;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const char *AQHREACT_Param_GetStringValue(const AQHREACT_PARAM *param)
|
||||
{
|
||||
return param?param->stringValue:NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Param_SetStringValue(AQHREACT_PARAM *param, const char *s)
|
||||
{
|
||||
if (param) {
|
||||
free(param->stringValue);
|
||||
param->stringValue=s?strdup(s):NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
double AQHREACT_Param_GetDoubleValue(const AQHREACT_PARAM *param)
|
||||
{
|
||||
return param?param->doubleValue:0.0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Param_SetDoubleValue(AQHREACT_PARAM *param, double d)
|
||||
{
|
||||
if (param)
|
||||
param->doubleValue=d;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint32_t AQHREACT_Param_GetFlags(const AQHREACT_PARAM *param)
|
||||
{
|
||||
return param?param->flags:0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Param_SetFlags(AQHREACT_PARAM *param, uint32_t f)
|
||||
{
|
||||
if (param)
|
||||
param->flags=f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Param_AddFlags(AQHREACT_PARAM *param, uint32_t f)
|
||||
{
|
||||
if (param)
|
||||
param->flags|=f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Param_SubFlags(AQHREACT_PARAM *param, uint32_t f)
|
||||
{
|
||||
if (param)
|
||||
param->flags&=~f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHREACT_PARAM *AQHREACT_Param_List_GetParamByName(const AQHREACT_PARAM_LIST *paramList, const char *paramName)
|
||||
{
|
||||
if (paramList && paramName && *paramName) {
|
||||
AQHREACT_PARAM *param;
|
||||
|
||||
param=AQHREACT_Param_List_First(paramList);
|
||||
while(param) {
|
||||
const char *s;
|
||||
|
||||
s=AQHREACT_Param_GetName(param);
|
||||
if (s && *s && strcasecmp(paramName, s)==0)
|
||||
return param;
|
||||
param=AQHREACT_Param_List_Next(param);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int AQHREACT_Param_List_WriteIntoDb(const AQHREACT_PARAM_LIST *paramList, GWEN_DB_NODE *db, uint32_t dbFlags)
|
||||
{
|
||||
if (paramList && db) {
|
||||
const AQHREACT_PARAM *param;
|
||||
|
||||
param=AQHREACT_Param_List_First(paramList);
|
||||
while(param) {
|
||||
if (param->name) {
|
||||
switch(param->dataType) {
|
||||
case AQHREACT_DATAOBJECTTYPE_STRING:
|
||||
_setDbValueFromParamString(param, db, dbFlags);
|
||||
break;
|
||||
case AQHREACT_DATAOBJECTTYPE_DOUBLE:
|
||||
_setDbValueFromParamDouble(param, db, dbFlags);
|
||||
break;
|
||||
default:
|
||||
DBG_ERROR(NULL, "Unhandled param type %d", param->dataType);
|
||||
return GWEN_ERROR_GENERIC;
|
||||
}
|
||||
}
|
||||
|
||||
param=AQHREACT_Param_List_Next(param);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _setDbValueFromParamString(const AQHREACT_PARAM *param, GWEN_DB_NODE *db, uint32_t dbFlags)
|
||||
{
|
||||
if (param->stringValue)
|
||||
GWEN_DB_SetCharValue(db, dbFlags, param->name, param->stringValue);
|
||||
else
|
||||
GWEN_DB_DeleteVar(db, param->name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _setDbValueFromParamDouble(const AQHREACT_PARAM *param, GWEN_DB_NODE *db, uint32_t dbFlags)
|
||||
{
|
||||
GWEN_BUFFER *buf;
|
||||
int rv;
|
||||
|
||||
buf=GWEN_Buffer_new(0, 32, 0, 1);
|
||||
rv=GWEN_Text_DoubleToBuffer(param->doubleValue, buf);
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "here (%d)", rv);
|
||||
}
|
||||
else
|
||||
GWEN_DB_SetCharValue(db, dbFlags, param->name, GWEN_Buffer_GetStart(buf));
|
||||
GWEN_Buffer_free(buf);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Param_Dump(const AQHREACT_PARAM *param, GWEN_BUFFER *buf, int indent)
|
||||
{
|
||||
if (param && buf) {
|
||||
if (indent)
|
||||
GWEN_Buffer_FillWithBytes(buf, ' ', indent);
|
||||
|
||||
switch(param->dataType) {
|
||||
case AQHREACT_DATAOBJECTTYPE_DOUBLE:
|
||||
GWEN_Buffer_AppendArgs(buf, "- %s = %f [DOUBLE]\n", (param->name)?(param->name):"<unnamed>", param->doubleValue);
|
||||
break;
|
||||
case AQHREACT_DATAOBJECTTYPE_STRING:
|
||||
GWEN_Buffer_AppendArgs(buf, "- %s = %s [STRING]\n", (param->name)?(param->name):"<unnamed>", param->stringValue);
|
||||
break;
|
||||
default:
|
||||
DBG_ERROR(NULL, "Unhandled data type %d", param->dataType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Param_List_Dump(const AQHREACT_PARAM_LIST *paramList, GWEN_BUFFER *buf, int indent, const char *title)
|
||||
{
|
||||
if (paramList && buf) {
|
||||
const AQHREACT_PARAM *param;
|
||||
|
||||
if (indent)
|
||||
GWEN_Buffer_FillWithBytes(buf, ' ', indent);
|
||||
GWEN_Buffer_AppendArgs(buf, "%s\n", (title && *title)?title:"Parameter List:\n");
|
||||
|
||||
param=AQHREACT_Param_List_First(paramList);
|
||||
while(param) {
|
||||
AQHREACT_Param_Dump(param, buf, indent+2);
|
||||
param=AQHREACT_Param_List_Next(param);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
55
apps/aqhome-react/types/param.h
Normal file
55
apps/aqhome-react/types/param.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2024 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_REACT_PARAM_H
|
||||
#define AQHOME_REACT_PARAM_H
|
||||
|
||||
#include <gwenhywfar/list.h>
|
||||
#include <gwenhywfar/db.h>
|
||||
|
||||
|
||||
typedef struct AQHREACT_PARAM AQHREACT_PARAM;
|
||||
GWEN_LIST_FUNCTION_DEFS(AQHREACT_PARAM, AQHREACT_Param)
|
||||
|
||||
#include "aqhome-react/aqhome_react.h"
|
||||
|
||||
|
||||
|
||||
AQHREACT_PARAM *AQHREACT_Param_new();
|
||||
void AQHREACT_Param_free(AQHREACT_PARAM *param);
|
||||
|
||||
const char *AQHREACT_Param_GetName(const AQHREACT_PARAM *param);
|
||||
void AQHREACT_Param_SetName(AQHREACT_PARAM *param, const char *s);
|
||||
|
||||
const char *AQHREACT_Param_GetDescription(const AQHREACT_PARAM *param);
|
||||
void AQHREACT_Param_SetDescription(AQHREACT_PARAM *param, const char *s);
|
||||
|
||||
int AQHREACT_Param_GetDataType(const AQHREACT_PARAM *param);
|
||||
void AQHREACT_Param_SetDataType(AQHREACT_PARAM *param, int i);
|
||||
|
||||
const char *AQHREACT_Param_GetStringValue(const AQHREACT_PARAM *param);
|
||||
void AQHREACT_Param_SetStringValue(AQHREACT_PARAM *param, const char *s);
|
||||
|
||||
double AQHREACT_Param_GetDoubleValue(const AQHREACT_PARAM *param);
|
||||
void AQHREACT_Param_SetDoubleValue(AQHREACT_PARAM *param, double d);
|
||||
|
||||
uint32_t AQHREACT_Param_GetFlags(const AQHREACT_PARAM *param);
|
||||
void AQHREACT_Param_SetFlags(AQHREACT_PARAM *param, uint32_t f);
|
||||
void AQHREACT_Param_AddFlags(AQHREACT_PARAM *param, uint32_t f);
|
||||
void AQHREACT_Param_SubFlags(AQHREACT_PARAM *param, uint32_t f);
|
||||
|
||||
AQHREACT_PARAM *AQHREACT_Param_List_GetParamByName(const AQHREACT_PARAM_LIST *paramList, const char *paramName);
|
||||
|
||||
int AQHREACT_Param_List_WriteIntoDb(const AQHREACT_PARAM_LIST *paramList, GWEN_DB_NODE *db, uint32_t dbFlags);
|
||||
|
||||
void AQHREACT_Param_Dump(const AQHREACT_PARAM *param, GWEN_BUFFER *buf, int indent);
|
||||
void AQHREACT_Param_List_Dump(const AQHREACT_PARAM_LIST *paramList, GWEN_BUFFER *buf, int indent, const char *title);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
30
apps/aqhome-react/types/param_p.h
Normal file
30
apps/aqhome-react/types/param_p.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2024 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_REACT_PARAM_P_H
|
||||
#define AQHOME_REACT_PARAM_P_H
|
||||
|
||||
|
||||
#include "aqhome-react/types/param.h"
|
||||
|
||||
|
||||
struct AQHREACT_PARAM {
|
||||
GWEN_LIST_ELEMENT(AQHREACT_PARAM)
|
||||
|
||||
char *name;
|
||||
char *description;
|
||||
int dataType;
|
||||
char *stringValue;
|
||||
double doubleValue;
|
||||
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
303
apps/aqhome-react/types/port.c
Normal file
303
apps/aqhome-react/types/port.c
Normal file
@@ -0,0 +1,303 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2024 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "./port_p.h"
|
||||
#include "aqhome-react/types/dataobject.h"
|
||||
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
|
||||
|
||||
GWEN_LIST_FUNCTIONS(AQHREACT_PORT, AQHREACT_Port)
|
||||
|
||||
|
||||
AQHREACT_PORT *AQHREACT_Port_new()
|
||||
{
|
||||
AQHREACT_PORT *port;
|
||||
|
||||
GWEN_NEW_OBJECT(AQHREACT_PORT, port);
|
||||
GWEN_LIST_INIT(AQHREACT_PORT, port);
|
||||
port->linkList=AQHREACT_Link_List_new();
|
||||
return port;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHREACT_PORT *AQHREACT_Port_dup(const AQHREACT_PORT *srcPort)
|
||||
{
|
||||
if (srcPort) {
|
||||
AQHREACT_PORT *newPort;
|
||||
|
||||
newPort=AQHREACT_Port_new();
|
||||
AQHREACT_Port_SetName(newPort, srcPort->name);
|
||||
newPort->idForUnit=srcPort->idForUnit;
|
||||
AQHREACT_Port_SetDescription(newPort, srcPort->description);
|
||||
newPort->flags=srcPort->flags;
|
||||
newPort->dataType=srcPort->dataType;
|
||||
/* don't copy current dataObject */
|
||||
/* don't copy current links */
|
||||
return newPort;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Port_free(AQHREACT_PORT *port)
|
||||
{
|
||||
if (port) {
|
||||
GWEN_LIST_FINI(AQHREACT_PORT, port);
|
||||
AQHREACT_DataObject_free(port->currentDataObject);
|
||||
AQHREACT_Link_List_free(port->linkList);
|
||||
free(port->name);
|
||||
free(port->description);
|
||||
GWEN_FREE_OBJECT(port);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const char *AQHREACT_Port_GetName(const AQHREACT_PORT *port)
|
||||
{
|
||||
return port?port->name:NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Port_SetName(AQHREACT_PORT *port, const char *s)
|
||||
{
|
||||
if (port) {
|
||||
free(port->name);
|
||||
port->name=s?strdup(s):NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int AQHREACT_Port_GetIdForUnit(const AQHREACT_PORT *port)
|
||||
{
|
||||
return port?port->idForUnit:0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Port_SetIdForUnit(AQHREACT_PORT *port, int i)
|
||||
{
|
||||
if (port)
|
||||
port->idForUnit=i;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const char *AQHREACT_Port_GetDescription(const AQHREACT_PORT *port)
|
||||
{
|
||||
return port?port->description:NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Port_SetDescription(AQHREACT_PORT *port, const char *s)
|
||||
{
|
||||
if (port) {
|
||||
free(port->description);
|
||||
port->description=s?strdup(s):NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint32_t AQHREACT_Port_GetFlags(const AQHREACT_PORT *port)
|
||||
{
|
||||
return port?port->flags:0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Port_SetFlags(AQHREACT_PORT *port, uint32_t f)
|
||||
{
|
||||
if (port)
|
||||
port->flags=f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Port_AddFlags(AQHREACT_PORT *port, uint32_t f)
|
||||
{
|
||||
if (port)
|
||||
port->flags|=f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Port_SubFlags(AQHREACT_PORT *port, uint32_t f)
|
||||
{
|
||||
if (port)
|
||||
port->flags&=~f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int AQHREACT_Port_GetDataType(const AQHREACT_PORT *port)
|
||||
{
|
||||
return port?port->dataType:AQHREACT_DATAOBJECTTYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Port_SetDataType(AQHREACT_PORT *port, int i)
|
||||
{
|
||||
if (port)
|
||||
port->dataType=i;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHREACT_DATAOBJECT *AQHREACT_Port_GetCurrentDataObject(const AQHREACT_PORT *port)
|
||||
{
|
||||
return port?port->currentDataObject:NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Port_SetCurrentDataObject(AQHREACT_PORT *port, AQHREACT_DATAOBJECT *dataObject)
|
||||
{
|
||||
if (port){
|
||||
AQHREACT_DataObject_free(port->currentDataObject);
|
||||
port->currentDataObject=dataObject;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHREACT_LINK_LIST *AQHREACT_Port_GetLinkList(const AQHREACT_PORT *port)
|
||||
{
|
||||
return port?port->linkList:NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Port_AddLink(AQHREACT_PORT *port, AQHREACT_LINK *lnk)
|
||||
{
|
||||
if (port)
|
||||
AQHREACT_Link_List_Add(lnk, port->linkList);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Port_SendData(const AQHREACT_PORT *port, const AQHREACT_DATAOBJECT *dataObject)
|
||||
{
|
||||
AQHREACT_LINK_LIST *linkList;
|
||||
|
||||
linkList=AQHREACT_Port_GetLinkList(port);
|
||||
if (linkList) {
|
||||
AQHREACT_LINK *lnk;
|
||||
|
||||
lnk=AQHREACT_Link_List_First(linkList);
|
||||
while(lnk) {
|
||||
AQHREACT_UNIT *targetUnit;
|
||||
AQHREACT_PORT *targetPort;
|
||||
|
||||
targetUnit=AQHREACT_Link_GetTargetUnit(lnk);
|
||||
targetPort=AQHREACT_Link_GetTargetPort(lnk);
|
||||
if (targetUnit && targetPort) {
|
||||
DBG_DEBUG(NULL, "%s: Sending data to %s:%s",
|
||||
AQHREACT_Port_GetName(port), AQHREACT_Unit_GetId(targetUnit), AQHREACT_Port_GetName(targetPort));
|
||||
AQHREACT_Unit_InputData(targetUnit, targetPort, dataObject);
|
||||
}
|
||||
lnk=AQHREACT_Link_List_Next(lnk);
|
||||
} /* while */
|
||||
} /* if linkList */
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHREACT_PORT *AQHREACT_Port_List_GetByName(const AQHREACT_PORT_LIST *portList, const char *name)
|
||||
{
|
||||
if (portList && name) {
|
||||
AQHREACT_PORT *port;
|
||||
|
||||
port=AQHREACT_Port_List_First(portList);
|
||||
while(port) {
|
||||
const char *s;
|
||||
|
||||
s=AQHREACT_Port_GetName(port);
|
||||
if (s && strcasecmp(s, name)==0)
|
||||
return port;
|
||||
port=AQHREACT_Port_List_Next(port);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHREACT_PORT *AQHREACT_Port_List_GetByIdForUnit(const AQHREACT_PORT_LIST *portList, int id)
|
||||
{
|
||||
if (portList) {
|
||||
AQHREACT_PORT *port;
|
||||
|
||||
port=AQHREACT_Port_List_First(portList);
|
||||
while(port) {
|
||||
if (id==AQHREACT_Port_GetIdForUnit(port))
|
||||
return port;
|
||||
port=AQHREACT_Port_List_Next(port);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Port_Dump(const AQHREACT_PORT *port, GWEN_BUFFER *buf, int indent)
|
||||
{
|
||||
if (port && buf) {
|
||||
if (indent)
|
||||
GWEN_Buffer_FillWithBytes(buf, ' ', indent);
|
||||
GWEN_Buffer_AppendArgs(buf, "- \"%s\"[%d] (%s, %08x)\n",
|
||||
(port->name)?(port->name):"<no name>",
|
||||
port->idForUnit,
|
||||
AQHREACT_DataObjectType_toString(port->dataType),
|
||||
port->flags);
|
||||
if (port->linkList && AQHREACT_Link_List_GetCount(port->linkList))
|
||||
AQHREACT_Link_List_Dump(port->linkList, buf, indent+2, "Link List:");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_Port_List_Dump(const AQHREACT_PORT_LIST *portList, GWEN_BUFFER *buf, int indent, const char *title)
|
||||
{
|
||||
if (portList && buf) {
|
||||
const AQHREACT_PORT *port;
|
||||
|
||||
if (indent)
|
||||
GWEN_Buffer_FillWithBytes(buf, ' ', indent);
|
||||
GWEN_Buffer_AppendArgs(buf, "%s\n", (title && *title)?title:"Port List:\n");
|
||||
|
||||
port=AQHREACT_Port_List_First(portList);
|
||||
while(port) {
|
||||
AQHREACT_Port_Dump(port, buf, indent+2);
|
||||
port=AQHREACT_Port_List_Next(port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
67
apps/aqhome-react/types/port.h
Normal file
67
apps/aqhome-react/types/port.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2024 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_REACT_PORT_H
|
||||
#define AQHOME_REACT_PORT_H
|
||||
|
||||
#include <gwenhywfar/list.h>
|
||||
|
||||
|
||||
typedef struct AQHREACT_PORT AQHREACT_PORT;
|
||||
GWEN_LIST_FUNCTION_DEFS(AQHREACT_PORT, AQHREACT_Port)
|
||||
|
||||
|
||||
#include "aqhome-react/aqhome_react.h"
|
||||
#include "aqhome-react/types/link.h"
|
||||
#include "aqhome-react/types/dataobject.h"
|
||||
|
||||
|
||||
|
||||
AQHREACT_PORT *AQHREACT_Port_new();
|
||||
AQHREACT_PORT *AQHREACT_Port_dup(const AQHREACT_PORT *srcPort);
|
||||
void AQHREACT_Port_free(AQHREACT_PORT *port);
|
||||
|
||||
const char *AQHREACT_Port_GetName(const AQHREACT_PORT *port);
|
||||
void AQHREACT_Port_SetName(AQHREACT_PORT *port, const char *s);
|
||||
|
||||
int AQHREACT_Port_GetIdForUnit(const AQHREACT_PORT *port);
|
||||
void AQHREACT_Port_SetIdForUnit(AQHREACT_PORT *port, int i);
|
||||
|
||||
const char *AQHREACT_Port_GetDescription(const AQHREACT_PORT *port);
|
||||
void AQHREACT_Port_SetDescription(AQHREACT_PORT *port, const char *s);
|
||||
|
||||
uint32_t AQHREACT_Port_GetFlags(const AQHREACT_PORT *port);
|
||||
void AQHREACT_Port_SetFlags(AQHREACT_PORT *port, uint32_t f);
|
||||
void AQHREACT_Port_AddFlags(AQHREACT_PORT *port, uint32_t f);
|
||||
void AQHREACT_Port_SubFlags(AQHREACT_PORT *port, uint32_t f);
|
||||
|
||||
int AQHREACT_Port_GetDataType(const AQHREACT_PORT *port);
|
||||
void AQHREACT_Port_SetDataType(AQHREACT_PORT *port, int i);
|
||||
|
||||
AQHREACT_DATAOBJECT *AQHREACT_Port_GetCurrentDataObject(const AQHREACT_PORT *port);
|
||||
void AQHREACT_Port_SetCurrentDataObject(AQHREACT_PORT *port, AQHREACT_DATAOBJECT *dataObject);
|
||||
|
||||
void AQHREACT_Port_SendData(const AQHREACT_PORT *port, const AQHREACT_DATAOBJECT *dataObject);
|
||||
|
||||
|
||||
AQHREACT_LINK_LIST *AQHREACT_Port_GetLinkList(const AQHREACT_PORT *port);
|
||||
void AQHREACT_Port_AddLink(AQHREACT_PORT *port, AQHREACT_LINK *lnk);
|
||||
|
||||
|
||||
AQHREACT_PORT *AQHREACT_Port_List_GetByName(const AQHREACT_PORT_LIST *portList, const char *name);
|
||||
AQHREACT_PORT *AQHREACT_Port_List_GetByIdForUnit(const AQHREACT_PORT_LIST *portList, int id);
|
||||
|
||||
|
||||
void AQHREACT_Port_Dump(const AQHREACT_PORT *port, GWEN_BUFFER *buf, int indent);
|
||||
void AQHREACT_Port_List_Dump(const AQHREACT_PORT_LIST *portList, GWEN_BUFFER *buf, int indent, const char *title);
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
31
apps/aqhome-react/types/port_p.h
Normal file
31
apps/aqhome-react/types/port_p.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2024 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_REACT_PORT_P_H
|
||||
#define AQHOME_REACT_PORT_P_H
|
||||
|
||||
|
||||
#include "aqhome-react/types/port.h"
|
||||
|
||||
|
||||
struct AQHREACT_PORT {
|
||||
GWEN_LIST_ELEMENT(AQHREACT_PORT)
|
||||
|
||||
char *name;
|
||||
char *description;
|
||||
int idForUnit;
|
||||
uint32_t flags;
|
||||
int dataType;
|
||||
|
||||
AQHREACT_DATAOBJECT *currentDataObject;
|
||||
AQHREACT_LINK_LIST *linkList;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
182
apps/aqhome-react/types/prgrule-t.c
Normal file
182
apps/aqhome-react/types/prgrule-t.c
Normal file
@@ -0,0 +1,182 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2024 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
/* This file is included by "prgrule.c" */
|
||||
|
||||
|
||||
#include <gwenhywfar/testframework.h>
|
||||
#include "prgrule-t.h"
|
||||
|
||||
|
||||
#ifdef AQHOME_ENABLE_TESTCODE
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static int GWENHYWFAR_CB test1(GWEN_TEST_MODULE *mod);
|
||||
static int GWENHYWFAR_CB test2(GWEN_TEST_MODULE *mod);
|
||||
static int GWENHYWFAR_CB test3(GWEN_TEST_MODULE *mod);
|
||||
|
||||
static int testAgainstValues(const char *ruleText, const uint64_t *ptrExpectedFields, double expectedValue);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* implementations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
int AQHREACT_PrgRule_AddTests(GWEN_TEST_MODULE *mod)
|
||||
{
|
||||
GWEN_TEST_MODULE *newMod;
|
||||
|
||||
newMod=GWEN_Test_Module_AddModule(mod, "AQHREACT_PrgRule", NULL);
|
||||
|
||||
GWEN_Test_Module_AddTest(newMod, "parse rules 1", test1, NULL);
|
||||
GWEN_Test_Module_AddTest(newMod, "parse rules 2", test2, NULL);
|
||||
GWEN_Test_Module_AddTest(newMod, "match rules 1", test3, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int test1(GWEN_UNUSED GWEN_TEST_MODULE *mod)
|
||||
{
|
||||
static char ruleText[]="* * * * * 10.0";
|
||||
uint64_t expectedFields[]={
|
||||
0b111111111111111111111111111111111111111111111111111111111111L, /* minute */
|
||||
0b111111111111111111111111L, /* hour */
|
||||
0b11111111111111111111111111111110L, /* day of month */
|
||||
0b1111111111110L, /* month */
|
||||
0b1111111L /* day of week (0=sunday) */
|
||||
};
|
||||
|
||||
return testAgainstValues(ruleText, expectedFields, 10.0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int test2(GWEN_UNUSED GWEN_TEST_MODULE *mod)
|
||||
{
|
||||
static char ruleText[]="/2 4-7 1-5,8,15,23-25 * 0,3,5 12.3";
|
||||
uint64_t expectedFields[]={
|
||||
0b010101010101010101010101010101010101010101010101010101010101L, /* minute */
|
||||
0b000000000000000011110000L, /* hour */
|
||||
0b00000011100000001000000100111110L, /* day of month */
|
||||
0b1111111111110L, /* month */
|
||||
0b0101001L /* day of week (0=sunday) */
|
||||
};
|
||||
|
||||
return testAgainstValues(ruleText, expectedFields, 12.3);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int test3(GWEN_UNUSED GWEN_TEST_MODULE *mod)
|
||||
{
|
||||
AQHREACT_PRGRULE_LIST *prgRuleList;
|
||||
AQHREACT_PRGRULE *prgRule;
|
||||
static char ruleText[]="/2 4-7 1-5,8,15,23-25 * 0,1,3,5 12.3";
|
||||
|
||||
prgRuleList=AQHREACT_PrgRule_ReadRules(ruleText);
|
||||
if (prgRuleList==NULL) {
|
||||
DBG_ERROR(NULL, "Error reading rules list");
|
||||
return GWEN_ERROR_GENERIC;
|
||||
}
|
||||
|
||||
prgRule=AQHREACT_PrgRule_List_First(prgRuleList);
|
||||
|
||||
if (!AQHREACT_PrgRule_Matches(prgRule, 2, 5, 8, 4, 1)) {
|
||||
DBG_ERROR(NULL, "Error matching against rule");
|
||||
return GWEN_ERROR_GENERIC;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int testAgainstValues(const char *ruleText, const uint64_t *ptrExpectedFields, double expectedValue)
|
||||
{
|
||||
AQHREACT_PRGRULE_LIST *prgRuleList;
|
||||
AQHREACT_PRGRULE *prgRule;
|
||||
|
||||
prgRuleList=AQHREACT_PrgRule_ReadRules(ruleText);
|
||||
if (prgRuleList==NULL) {
|
||||
DBG_ERROR(NULL, "Error reading rules list");
|
||||
return GWEN_ERROR_GENERIC;
|
||||
}
|
||||
|
||||
prgRule=AQHREACT_PrgRule_List_First(prgRuleList);
|
||||
if (AQHREACT_PrgRule_List_Next(prgRule)) {
|
||||
DBG_ERROR(NULL, "More than one rule read when only one given");
|
||||
return GWEN_ERROR_GENERIC;
|
||||
}
|
||||
|
||||
if (prgRule->bitfieldMinute!=ptrExpectedFields[0]) {
|
||||
DBG_ERROR(NULL, "Unexpected value for MINUTE: %016lX [%016lX]", prgRule->bitfieldMinute, ptrExpectedFields[0]);
|
||||
AQHREACT_PrgRule_List_free(prgRuleList);
|
||||
return GWEN_ERROR_GENERIC;
|
||||
}
|
||||
|
||||
if (prgRule->bitfieldHour!=ptrExpectedFields[1]) {
|
||||
DBG_ERROR(NULL, "Unexpected value for HOUR: %016X [%016lX]", prgRule->bitfieldHour, ptrExpectedFields[1]);
|
||||
AQHREACT_PrgRule_List_free(prgRuleList);
|
||||
return GWEN_ERROR_GENERIC;
|
||||
}
|
||||
|
||||
if (prgRule->bitfieldDayOfMonth!=ptrExpectedFields[2]) {
|
||||
DBG_ERROR(NULL, "Unexpected value for DAYOFMONTH: %016X [%016lX]", prgRule->bitfieldDayOfMonth, ptrExpectedFields[2]);
|
||||
AQHREACT_PrgRule_List_free(prgRuleList);
|
||||
return GWEN_ERROR_GENERIC;
|
||||
}
|
||||
|
||||
if (prgRule->bitfieldMonth!=ptrExpectedFields[3]) {
|
||||
DBG_ERROR(NULL, "Unexpected value for MONTH: %016X [%016lX]", prgRule->bitfieldMonth, ptrExpectedFields[3]);
|
||||
AQHREACT_PrgRule_List_free(prgRuleList);
|
||||
return GWEN_ERROR_GENERIC;
|
||||
}
|
||||
|
||||
if (prgRule->bitfieldDayOfWeek!=ptrExpectedFields[4]) {
|
||||
DBG_ERROR(NULL, "Unexpected value for DAYOFWEEK: %08X [%016lX]", prgRule->bitfieldDayOfWeek, ptrExpectedFields[4]);
|
||||
AQHREACT_PrgRule_List_free(prgRuleList);
|
||||
return GWEN_ERROR_GENERIC;
|
||||
}
|
||||
|
||||
if (prgRule->value!=expectedValue) {
|
||||
DBG_ERROR(NULL, "Unexpected value : %f", prgRule->value);
|
||||
AQHREACT_PrgRule_List_free(prgRuleList);
|
||||
return GWEN_ERROR_GENERIC;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#else
|
||||
|
||||
int AQHREACT_PrgRule_AddTests(GWEN_TEST_MODULE *mod)
|
||||
{
|
||||
DBG_ERROR(GWEN_LOGDOMAIN, "AqHome was compiled without test code enabled.");
|
||||
return GWEN_ERROR_GENERIC;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
26
apps/aqhome-react/types/prgrule-t.h
Normal file
26
apps/aqhome-react/types/prgrule-t.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2024 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQHOME_REACT_PRGRULE_T_H
|
||||
#define AQHOME_REACT_PRGRULE_T_H
|
||||
|
||||
|
||||
|
||||
#include <gwenhywfar/gwenhywfarapi.h>
|
||||
#include <gwenhywfar/testframework.h>
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Internal tests for AQHREACT_PrgRule.
|
||||
*/
|
||||
int AQHREACT_PrgRule_AddTests(GWEN_TEST_MODULE *mod);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
425
apps/aqhome-react/types/prgrule.c
Normal file
425
apps/aqhome-react/types/prgrule.c
Normal file
@@ -0,0 +1,425 @@
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2024 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "./prgrule_p.h"
|
||||
|
||||
#include <gwenhywfar/debug.h>
|
||||
#include <gwenhywfar/text.h>
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
GWEN_LIST_FUNCTIONS(AQHREACT_PRGRULE, AQHREACT_PrgRule)
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const char *_readRule(AQHREACT_PRGRULE *prgRule, const char *s);
|
||||
|
||||
const char *_readField(const char *s, uint64_t *ptrBitField, int minValue, int maxValue);
|
||||
const char *_readRangeOpenBegin(const char *s, uint64_t *ptrBitField, int minValue, int maxValue);
|
||||
const char *_readInterval(const char *s, uint64_t *ptrBitField, int minValue, int maxValue);
|
||||
const char *_readValueAndProbablyRange(const char *s, uint64_t *ptrBitField, int minValue, int maxValue);
|
||||
const char *_readDouble(const char *s, double *ptrDouble);
|
||||
|
||||
int _valueOkay(int v, int minValue, int maxValue);
|
||||
int _rangeOkay(int startValue, int endValue, int minValue, int maxValue);
|
||||
void _setBitField(uint64_t *ptrBitField, int startValue, int endValue);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* implementations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
AQHREACT_PRGRULE *AQHREACT_PrgRule_new(void)
|
||||
{
|
||||
AQHREACT_PRGRULE *prgRule;
|
||||
|
||||
GWEN_NEW_OBJECT(AQHREACT_PRGRULE, prgRule);
|
||||
GWEN_LIST_INIT(AQHREACT_PRGRULE, prgRule);
|
||||
|
||||
return prgRule;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQHREACT_PrgRule_free(AQHREACT_PRGRULE *prgRule)
|
||||
{
|
||||
if (prgRule) {
|
||||
GWEN_LIST_FINI(AQHREACT_PRGRULE, prgRule);
|
||||
GWEN_FREE_OBJECT(prgRule);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
double AQHREACT_PrgRule_GetValue(const AQHREACT_PRGRULE *prgRule)
|
||||
{
|
||||
return prgRule?prgRule->value:0.0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int AQHREACT_PrgRule_Matches(const AQHREACT_PRGRULE *prgRule, int min, int hour, int dayOfMonth, int month, int dayOfWeek)
|
||||
{
|
||||
if ((prgRule->bitfieldMonth & (1L<<month)) &&
|
||||
(prgRule->bitfieldDayOfMonth & (1L<<dayOfMonth)) &&
|
||||
(prgRule->bitfieldDayOfWeek & (1L<<dayOfWeek)) &&
|
||||
(prgRule->bitfieldHour & (1L<<hour)) &&
|
||||
(prgRule->bitfieldMinute & (1L<<min)))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHREACT_PRGRULE *AQHREACT_PrgRule_List_FindMatchingRule(AQHREACT_PRGRULE_LIST *prgRuleList,
|
||||
int min, int hour, int dayOfMonth, int month, int dayOfWeek)
|
||||
{
|
||||
AQHREACT_PRGRULE *prgRule;
|
||||
|
||||
prgRule=AQHREACT_PrgRule_List_First(prgRuleList);
|
||||
while(prgRule) {
|
||||
if (AQHREACT_PrgRule_Matches(prgRule, min, hour, dayOfMonth, month, dayOfWeek))
|
||||
return prgRule;
|
||||
prgRule=AQHREACT_PrgRule_List_Next(prgRule);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQHREACT_PRGRULE_LIST *AQHREACT_PrgRule_ReadRules(const char *s)
|
||||
{
|
||||
AQHREACT_PRGRULE_LIST *prgRuleList;
|
||||
|
||||
prgRuleList=AQHREACT_PrgRule_List_new();
|
||||
|
||||
while(*s) {
|
||||
AQHREACT_PRGRULE *prgRule;
|
||||
const char *r;
|
||||
|
||||
prgRule=AQHREACT_PrgRule_new();
|
||||
r=_readRule(prgRule, s);
|
||||
if (r==NULL){
|
||||
DBG_INFO(NULL, "Error parsing \"%s\"", s);
|
||||
AQHREACT_PrgRule_free(prgRule);
|
||||
AQHREACT_PrgRule_List_free(prgRuleList);
|
||||
return NULL;
|
||||
}
|
||||
AQHREACT_PrgRule_List_Add(prgRule, prgRuleList);
|
||||
s=r;
|
||||
|
||||
while(*s && isspace(*s))
|
||||
s++;
|
||||
if (*s==';')
|
||||
s++;
|
||||
}
|
||||
|
||||
if (AQHREACT_PrgRule_List_GetCount(prgRuleList)<1) {
|
||||
AQHREACT_PrgRule_List_free(prgRuleList);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return prgRuleList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const char *_readRule(AQHREACT_PRGRULE *prgRule, const char *s)
|
||||
{
|
||||
uint64_t v;
|
||||
const char *r;
|
||||
|
||||
v=0;
|
||||
r=_readField(s, &v, 0, 59);
|
||||
if (r==NULL) {
|
||||
DBG_ERROR(NULL, "Error reading MIN field at \"%s\"", s);
|
||||
return NULL;
|
||||
}
|
||||
prgRule->bitfieldMinute=v;
|
||||
|
||||
s=r;
|
||||
v=0;
|
||||
r=_readField(s, &v, 0, 23);
|
||||
if (r==NULL) {
|
||||
DBG_ERROR(NULL, "Error reading HOUR field at \"%s\"", s);
|
||||
return NULL;
|
||||
}
|
||||
prgRule->bitfieldHour=(uint32_t)(v & 0xffffffffl);
|
||||
|
||||
s=r;
|
||||
v=0;
|
||||
r=_readField(s, &v, 1, 31);
|
||||
if (r==NULL) {
|
||||
DBG_ERROR(NULL, "Error reading DAYOFMONTH field at \"%s\"", s);
|
||||
return NULL;
|
||||
}
|
||||
prgRule->bitfieldDayOfMonth=(uint32_t)(v & 0xffffffffl);
|
||||
|
||||
s=r;
|
||||
v=0;
|
||||
r=_readField(s, &v, 1, 12);
|
||||
if (r==NULL) {
|
||||
DBG_ERROR(NULL, "Error reading MONTH field at \"%s\"", s);
|
||||
return NULL;
|
||||
}
|
||||
prgRule->bitfieldMonth=(uint16_t)(v & 0xffffl);
|
||||
|
||||
s=r;
|
||||
v=0;
|
||||
r=_readField(s, &v, 0, 6);
|
||||
if (r==NULL) {
|
||||
DBG_ERROR(NULL, "Error reading DAYOFWEEK field at \"%s\"", s);
|
||||
return NULL;
|
||||
}
|
||||
prgRule->bitfieldDayOfWeek=(uint8_t)(v & 0xffl);
|
||||
|
||||
s=r;
|
||||
r=_readDouble(s, &(prgRule->value));
|
||||
if (r==NULL) {
|
||||
DBG_ERROR(NULL, "Error reading DOUBLE field at \"%s\"", s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const char *_readField(const char *s, uint64_t *ptrBitField, int minValue, int maxValue)
|
||||
{
|
||||
while(*s && isspace(*s))
|
||||
s++;
|
||||
|
||||
if (*s==0) {
|
||||
DBG_INFO(NULL, "Premature field end");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while(s && *s) {
|
||||
if (*s=='*') {
|
||||
_setBitField(ptrBitField, minValue, maxValue);
|
||||
s++;
|
||||
}
|
||||
else if (*s=='-') {
|
||||
s=_readRangeOpenBegin(++s, ptrBitField, minValue, maxValue);
|
||||
if (s==NULL) {
|
||||
DBG_INFO(NULL, "here");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else if (*s=='/') {
|
||||
s=_readInterval(++s, ptrBitField, minValue, maxValue);
|
||||
if (s==NULL) {
|
||||
DBG_INFO(NULL, "here");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else if (isdigit(*s)) {
|
||||
s=_readValueAndProbablyRange(s, ptrBitField, minValue, maxValue);
|
||||
if (s==NULL) {
|
||||
DBG_INFO(NULL, "here");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Unexpected character at \"%s\"", s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (isspace(*s)){
|
||||
/* field done */
|
||||
return s;
|
||||
}
|
||||
else if (*s==',') {
|
||||
s++;
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Unexpected character at \"%s\"", s);
|
||||
return NULL;
|
||||
}
|
||||
} /* while */
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const char *_readRangeOpenBegin(const char *s, uint64_t *ptrBitField, int minValue, int maxValue)
|
||||
{
|
||||
int v=0;
|
||||
|
||||
if (!isdigit(*s)) {
|
||||
DBG_ERROR(NULL, "Need at least start or end value for range");
|
||||
return NULL;
|
||||
}
|
||||
while(isdigit(*s)) {
|
||||
v*=10;
|
||||
v+=(*s)-'0';
|
||||
s++;
|
||||
}
|
||||
if (!_rangeOkay(minValue, v, minValue, maxValue)) {
|
||||
DBG_INFO(NULL, "here");
|
||||
return NULL;
|
||||
}
|
||||
_setBitField(ptrBitField, minValue, v);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const char *_readInterval(const char *s, uint64_t *ptrBitField, int minValue, int maxValue)
|
||||
{
|
||||
int v=0;
|
||||
int i;
|
||||
|
||||
while(isdigit(*s)) {
|
||||
v*=10;
|
||||
v+=(*s)-'0';
|
||||
s++;
|
||||
}
|
||||
if (v==0) {
|
||||
DBG_ERROR(NULL, "Bad value 0 for interval (/0)");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i=minValue; i<maxValue; i+=v) {
|
||||
_setBitField(ptrBitField, i, i);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const char *_readValueAndProbablyRange(const char *s, uint64_t *ptrBitField, int minValue, int maxValue)
|
||||
{
|
||||
int v=0;
|
||||
|
||||
while(isdigit(*s)) {
|
||||
v*=10;
|
||||
v+=(*s)-'0';
|
||||
s++;
|
||||
}
|
||||
if (!_valueOkay(v, minValue, maxValue)) {
|
||||
DBG_INFO(NULL, "here");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (*s=='-') {
|
||||
s=_readRangeOpenBegin(++s, ptrBitField, v, maxValue);
|
||||
if (s==NULL) {
|
||||
DBG_INFO(NULL, "here");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!_valueOkay(v, minValue, maxValue)) {
|
||||
DBG_INFO(NULL, "here");
|
||||
return NULL;
|
||||
}
|
||||
_setBitField(ptrBitField, v, v);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const char *_readDouble(const char *s, double *ptrDouble)
|
||||
{
|
||||
const char *sStart;
|
||||
int rv;
|
||||
|
||||
while(*s && isspace(*s))
|
||||
s++;
|
||||
sStart=s;
|
||||
while(*s && (isdigit(*s) || *s=='.'))
|
||||
s++;
|
||||
|
||||
rv=GWEN_Text_StringToDouble(sStart, ptrDouble);
|
||||
if (rv<0) {
|
||||
DBG_ERROR(NULL, "Error reading double: %d", rv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _valueOkay(int v, int minValue, int maxValue)
|
||||
{
|
||||
if (v<minValue) {
|
||||
DBG_ERROR(NULL, "Value below minimum value (%d < %d)", v, minValue);
|
||||
return 0;
|
||||
}
|
||||
if (v>maxValue) {
|
||||
DBG_ERROR(NULL, "Value above maxmum value (%d > %d)", v, maxValue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _rangeOkay(int startValue, int endValue, int minValue, int maxValue)
|
||||
{
|
||||
if (startValue>endValue) {
|
||||
DBG_ERROR(NULL, "Startvalue (%d) > endvalue (%d)", startValue, endValue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (startValue<minValue || startValue>maxValue) {
|
||||
DBG_ERROR(NULL, "Startvalue (%d) out of range (%d-%d)", startValue, minValue, maxValue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (endValue>maxValue) {
|
||||
DBG_ERROR(NULL, "Endvalue (%d) out of range (%d-%d)", endValue, minValue, maxValue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _setBitField(uint64_t *ptrBitField, int startValue, int endValue)
|
||||
{
|
||||
int i;
|
||||
uint64_t m;
|
||||
uint64_t v=0;
|
||||
|
||||
m=1l<<startValue;
|
||||
for (i=startValue; i<=endValue; i++) {
|
||||
v|=m;
|
||||
m<<=1;
|
||||
}
|
||||
*ptrBitField|=v;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//#include "prgrule-t.c"
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user