Compare commits
556 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2388576a9 | ||
|
|
7f21049d0f | ||
|
|
49ca27d994 | ||
| 3cb4a46a16 | |||
|
|
13677f21d9 | ||
|
|
a1941a375b | ||
|
|
9dc8ecd873 | ||
|
|
7c6c103f17 | ||
|
|
b113788e54 | ||
|
|
599edde008 | ||
|
|
cc0b685c66 | ||
|
|
29b0a4f966 | ||
|
|
c651b18e72 | ||
|
|
6fd180b4d4 | ||
|
|
282f9cf4eb | ||
|
|
1cfea7fd6c | ||
|
|
251e302e59 | ||
|
|
873e920add | ||
|
|
fd7c6ada6b | ||
|
|
c148bddfc9 | ||
|
|
f0857b7c46 | ||
| 9b1c6f61e6 | |||
|
|
3f72c52cdc | ||
|
|
ef757c52ea | ||
|
|
4110a835c8 | ||
|
|
e78ce544e3 | ||
|
|
c10d54e79a | ||
|
|
2796558040 | ||
|
|
920a808729 | ||
|
|
8e24ac4b71 | ||
|
|
3eb2736edb | ||
|
|
8c3eba9224 | ||
|
|
6288aa565e | ||
|
|
0fa7ec91c4 | ||
|
|
fdb4b7060a | ||
| 3f47b0f008 | |||
|
|
cb431b5af1 | ||
|
|
153428b24f | ||
|
|
d92544f7c4 | ||
|
|
3f1ae1886a | ||
|
|
af4863af65 | ||
|
|
4b7c1259a7 | ||
|
|
de1496389e | ||
| 79003cd0f4 | |||
|
|
c3443fcc91 | ||
|
|
5105e77823 | ||
|
|
eb068b76a4 | ||
| 9f11f7ec11 | |||
| 2012221b73 | |||
|
|
9ab5d42439 | ||
| 7abcaefeb1 | |||
|
|
f4df52dd1c | ||
| db115bb27d | |||
|
|
68d96e67aa | ||
| 3f94012faa | |||
| 41c557118c | |||
| 6596a572d6 | |||
| 139c7b0651 | |||
|
|
ab236cd34f | ||
|
|
786bd5d660 | ||
|
|
21d2c2b7a7 | ||
|
|
48fa4c2390 | ||
|
|
033330b005 | ||
|
|
f81123c3f7 | ||
|
|
5539cb2887 | ||
|
|
1df9c8d703 | ||
|
|
1cbed2a620 | ||
| 89667367ea | |||
| 92b95dd86d | |||
|
|
6b7e38fef6 | ||
|
|
f10debe2f2 | ||
|
|
9d15351fba | ||
|
|
2339a006ec | ||
|
|
7fd904ab79 | ||
|
|
61f149b562 | ||
|
|
57ee3a4d43 | ||
| 563eb80b65 | |||
|
|
dae10378dd | ||
|
|
40cb153656 | ||
|
|
af4f000b13 | ||
|
|
f922ee97aa | ||
|
|
321ec97130 | ||
|
|
0dd3502942 | ||
|
|
26c5e933f5 | ||
|
|
00ba09faae | ||
|
|
40e39d651b | ||
|
|
02c8164b7e | ||
|
|
8c598aec1e | ||
|
|
2c59defdc2 | ||
|
|
5642bf2b31 | ||
|
|
ba76df66b0 | ||
|
|
57c419eb70 | ||
|
|
98f4ecef6c | ||
|
|
20e07712cb | ||
|
|
ac1d98efdc | ||
|
|
5d161acfad | ||
|
|
1534327f6f | ||
|
|
04ada8740a | ||
|
|
98cca582f6 | ||
|
|
a2de4b80a7 | ||
|
|
c7d8fc3168 | ||
|
|
071c6e3d64 | ||
| b4878d4a25 | |||
|
|
bb22857fc9 | ||
|
|
b0a4458c56 | ||
| 6357cde72b | |||
| dddffe43cb | |||
|
|
51fdf6bf59 | ||
|
|
b53ef274cf | ||
|
|
90d618b66f | ||
| e81413eaa9 | |||
| 33cefdd0b1 | |||
| 80a886a5eb | |||
| 0a85b433ba | |||
|
|
66786200bd | ||
| 15e93b6734 | |||
|
|
6843497908 | ||
|
|
cd486f419f | ||
|
|
7394762e28 | ||
|
|
2f75fe062b | ||
|
|
d4e09a09cf | ||
|
|
d58452012d | ||
|
|
014ac7931e | ||
| 3d4fb44b3a | |||
|
|
1abba4a528 | ||
|
|
d06fa8476c | ||
|
|
20cb78eb06 | ||
|
|
f3e9cb7171 | ||
|
|
6f5746861f | ||
|
|
dc34bd9b44 | ||
|
|
2dcf90d6c7 | ||
|
|
ac1c5fd43e | ||
|
|
dc0244d81d | ||
| 449de8bbc5 | |||
| 8caaf95427 | |||
| 81df650d09 | |||
|
|
69503c7ca9 | ||
| e42121ba4c | |||
|
|
35e52e0722 | ||
|
|
80c383a746 | ||
|
|
84f9e61e57 | ||
|
|
d18e84159f | ||
|
|
1ea09cc52c | ||
|
|
f0540d4c92 | ||
|
|
04299007d0 | ||
|
|
947ee4f771 | ||
|
|
c3c46b5257 | ||
|
|
213d940ff1 | ||
|
|
d220ee0187 | ||
|
|
f56c1086b9 | ||
|
|
4532f2242b | ||
|
|
9deaea9e81 | ||
|
|
44738f2878 | ||
|
|
3f12758d1b | ||
|
|
fd7ffbe87e | ||
|
|
12edb57581 | ||
|
|
a163150fec | ||
|
|
46e8811e59 | ||
| 90eb90554b | |||
|
|
694f12bc29 | ||
| 2121a293cb | |||
|
|
6c6bed4b46 | ||
|
|
7f2a61ba21 | ||
|
|
ecafa996a3 | ||
| e11107eb8f | |||
| c414f1a42a | |||
|
|
7c18d0038a | ||
|
|
b09438e29d | ||
|
|
6ef908ff60 | ||
|
|
7461801657 | ||
|
|
efe92e28b1 | ||
|
|
e3f682212e | ||
|
|
a7f3925a2f | ||
|
|
4ecec5e6ef | ||
| da3373c710 | |||
|
|
8963b20dd3 | ||
| 0977a77eed | |||
|
|
3951b71fff | ||
|
|
52b99c2669 | ||
|
|
760db06120 | ||
|
|
7e66b67cde | ||
|
|
5422340dd3 | ||
|
|
d5e550a8f4 | ||
|
|
4128b21da8 | ||
|
|
6ae9037a47 | ||
|
|
d0f6cc46ad | ||
|
|
d5f22c4147 | ||
|
|
cd41320032 | ||
|
|
415fc0c129 | ||
| d6108967ce | |||
| b074ba29fc | |||
|
|
d020890639 | ||
|
|
7b9954f1fe | ||
|
|
b4bd2e6d1d | ||
|
|
ddf6da0855 | ||
| cd7ae5d06c | |||
|
|
e1872d030a | ||
|
|
c9bf604a33 | ||
|
|
257d0b3af8 | ||
| a25febc245 | |||
| db992ec790 | |||
|
|
c9fb9db0c0 | ||
| a35ee521b6 | |||
| c91b3bdc51 | |||
|
|
ac31267eb9 | ||
|
|
50d626f563 | ||
|
|
c1d2160335 | ||
| 9ad02d45f4 | |||
| f26e4f0e11 | |||
| 541a707570 | |||
| 13fd6f11ae | |||
| fe5040c7af | |||
|
|
09d2911c4c | ||
|
|
f1777f8bf1 | ||
|
|
10729c44fc | ||
| 1a0227620f | |||
| c73e062109 | |||
| df83f65328 | |||
|
|
c16c1f8e1d | ||
| f520234e55 | |||
| e7598137ae | |||
| 7078a5efe5 | |||
| 1c741d6406 | |||
|
|
b10700d976 | ||
| 71083101ae | |||
| ca1b1661d1 | |||
|
|
a27eb7e462 | ||
|
|
f68d5da31e | ||
|
|
2d889b07f7 | ||
|
|
417a53a835 | ||
|
|
04a667531b | ||
| 9ecfe4f59b | |||
|
|
0d80e33d13 | ||
|
|
a2f7b1b9de | ||
| ee96b8e56b | |||
|
|
a0f6846f53 | ||
|
|
6c88c54982 | ||
|
|
a177d7991c | ||
| b2ff27b15c | |||
|
|
f8fb45cf6f | ||
| 35a47b2fa3 | |||
|
|
4c0816a829 | ||
|
|
1eb64fadae | ||
|
|
3ceb73d601 | ||
|
|
9d701ad55e | ||
| 8453b1da95 | |||
|
|
ba2fec5ac0 | ||
| 46a2e2068b | |||
| 7336a0aaa8 | |||
|
|
910069a463 | ||
| 56477157aa | |||
|
|
a67c5de69d | ||
|
|
95d5396759 | ||
| 858fc9aa05 | |||
| a6b0509179 | |||
|
|
55793dcecd | ||
| c9ac33864a | |||
| 6e4bbfc383 | |||
|
|
cb4d8d502a | ||
| 067f2235a7 | |||
| b42ede2853 | |||
| 9a4ef6abb5 | |||
|
|
af2b080ff5 | ||
|
|
0e056a7de1 | ||
|
|
b5e5286411 | ||
|
|
d01c0e14c0 | ||
|
|
0cf8bbc3d5 | ||
|
|
3a239b5123 | ||
|
|
0eabe977cb | ||
|
|
f1f86ea854 | ||
|
|
e048c92ad1 | ||
|
|
65f46a22f5 | ||
| 5d4cce365e | |||
|
|
b4c9bd12c9 | ||
|
|
8a11a9e5b1 | ||
|
|
b2738e1726 | ||
|
|
5d16d6c494 | ||
|
|
2693015dab | ||
|
|
3ecffc99b8 | ||
| 9c02a7cb8e | |||
|
|
a4edf8b3f6 | ||
| 8328d6c06e | |||
|
|
2ac33e8cec | ||
|
|
bbb911c917 | ||
|
|
bef9723255 | ||
|
|
47e3e5218d | ||
|
|
72800e1e44 | ||
| f6547b02a4 | |||
|
|
8fb22017d3 | ||
|
|
6f8070c85f | ||
| c09a0f4b85 | |||
| ba22f2b796 | |||
|
|
f62aa1f7f7 | ||
|
|
f19a467300 | ||
|
|
f4550b9c76 | ||
|
|
a5791ef76b | ||
|
|
dff4b556eb | ||
|
|
c013cd2eac | ||
| dc366ae1c3 | |||
| a9d2ba8dde | |||
|
|
de138328e9 | ||
|
|
757501efa8 | ||
|
|
73028c70d2 | ||
|
|
bdb3efd81d | ||
| 20423028f7 | |||
|
|
6442219087 | ||
|
|
a0148badaf | ||
| 8745f8bbbe | |||
|
|
277b1710fa | ||
| 1115520106 | |||
|
|
c8b63a7a7a | ||
|
|
1ed27ed29f | ||
|
|
072a2b6bfc | ||
|
|
b2bb7c3901 | ||
|
|
ad2752197a | ||
| 424a81ac53 | |||
|
|
38332616fa | ||
| 118068bafe | |||
| 8103b45a61 | |||
|
|
ef4c4a0a17 | ||
|
|
31a2311698 | ||
|
|
9937c3cb5f | ||
| 482d7f6ce5 | |||
|
|
5905c0b438 | ||
| 594ac7d4d4 | |||
| 9d2aad81e4 | |||
| 6b1303d250 | |||
| c45800f6d5 | |||
|
|
74cdfaf882 | ||
|
|
34ff162db1 | ||
|
|
bd6af21b99 | ||
| f0566a5969 | |||
| 76a571921a | |||
| 512ed2a542 | |||
|
|
c2c5c9668e | ||
|
|
a0b0023def | ||
| a11189d85c | |||
| ed54f79983 | |||
|
|
f03622ac5b | ||
|
|
bf3fe9450f | ||
| b4e2727c58 | |||
| 977ab4bf1b | |||
| d26504c841 | |||
| 418e17a427 | |||
| 4e0e50a4f8 | |||
| e05403bfa7 | |||
| b1a2ccd018 | |||
| 10f39b44a2 | |||
| 9eca074ffb | |||
| 9820d0a915 | |||
|
|
525de0c24c | ||
|
|
744c72dea2 | ||
|
|
b4114f19de | ||
|
|
b0dde94314 | ||
|
|
da7015fd89 | ||
|
|
9058c9081b | ||
|
|
34f317780e | ||
|
|
708b40880e | ||
|
|
bf661b5c1c | ||
|
|
57b08a99c6 | ||
| 8bc8a97e83 | |||
|
|
53548aec32 | ||
|
|
1380af6d50 | ||
|
|
21dcd87be2 | ||
|
|
fb97e421bb | ||
|
|
4f2ae176fa | ||
|
|
06e21711f2 | ||
|
|
95c564595f | ||
|
|
e725cd8a60 | ||
|
|
8507ad8378 | ||
|
|
cb8495fb0d | ||
|
|
8f8d3d38cc | ||
|
|
7eb80121a1 | ||
|
|
3736082096 | ||
|
|
2f303e63a3 | ||
|
|
ab2e5c346e | ||
|
|
ebb45238b0 | ||
|
|
6763721043 | ||
|
|
e235c3ef66 | ||
|
|
40b0e535cb | ||
|
|
9438301ebd | ||
|
|
bfed1b3e93 | ||
|
|
a9515f42b3 | ||
|
|
bbb62586f9 | ||
|
|
6aef21797d | ||
|
|
b7a90ebeb4 | ||
|
|
67665d3762 | ||
|
|
41252af4e9 | ||
|
|
2869f66154 | ||
|
|
2bdaabac6e | ||
|
|
4b41e2edd1 | ||
|
|
5fa09bb41b | ||
|
|
c96cadc5d1 | ||
|
|
b471a05181 | ||
|
|
e42a2c8309 | ||
| ea585a65ff | |||
|
|
59ba5c0a0c | ||
|
|
99d43041cb | ||
|
|
aa5f57a856 | ||
|
|
9fc0fbcbcf | ||
|
|
434dc676ce | ||
| ea83963d33 | |||
|
|
257df0a511 | ||
| 496cff6c11 | |||
|
|
484f9c9f4c | ||
|
|
19b69eed0e | ||
|
|
b165365203 | ||
|
|
a42dcd1d4b | ||
|
|
be3df5c497 | ||
|
|
7b951fa4b6 | ||
|
|
89a7b5b8f6 | ||
|
|
102aa504e6 | ||
| d1cbfcedcc | |||
| 116563f13e | |||
|
|
55bf90d0af | ||
|
|
7ac01b0f5e | ||
| 1a28d25026 | |||
|
|
92b0640f9c | ||
|
|
4a2fb595d0 | ||
| 56dd19b32e | |||
|
|
8460574f10 | ||
| 6f890daad6 | |||
|
|
6727dee61e | ||
|
|
3fcad5ff76 | ||
| 1fe7866970 | |||
|
|
b96d0cdeb1 | ||
| 0c30603f31 | |||
|
|
64bdba9fa2 | ||
|
|
c7a45b5500 | ||
| 42701db2d5 | |||
|
|
6c34c4a968 | ||
|
|
186d701dcb | ||
|
|
9b1b21dad5 | ||
|
|
00bda55ddd | ||
|
|
593b7e9d7b | ||
| f842886c00 | |||
|
|
af58306d96 | ||
|
|
09972e0764 | ||
|
|
7ba71562d8 | ||
|
|
71ba7fa139 | ||
|
|
48408859b1 | ||
|
|
fef01b692a | ||
|
|
55208ecf69 | ||
|
|
9a7d841d9c | ||
|
|
4ef5c982de | ||
|
|
22028fe7eb | ||
|
|
6ff3d7e336 | ||
| fe7af8a632 | |||
|
|
c67a2c8323 | ||
|
|
ce217b8b86 | ||
|
|
ebc52eb8ba | ||
|
|
0e1fb0e254 | ||
|
|
4d61cfc490 | ||
|
|
d0491b4850 | ||
|
|
610a3c1d13 | ||
|
|
ac0a9dd777 | ||
|
|
f294cb315f | ||
|
|
051e7fe14a | ||
|
|
38cbb22eff | ||
|
|
423fa581b4 | ||
|
|
279a8a6e83 | ||
|
|
a108b295d1 | ||
|
|
04bd8a7db3 | ||
|
|
d084950090 | ||
|
|
ff53c91279 | ||
|
|
751dc56568 | ||
| 7f42a21536 | |||
|
|
8ca804283a | ||
|
|
c43c5b4342 | ||
|
|
03ec551f71 | ||
|
|
0ddf98e1af | ||
|
|
1c014a75a7 | ||
|
|
c80ae2f95f | ||
|
|
d633b0bd35 | ||
|
|
81645ae1ba | ||
|
|
708a4e6fbd | ||
|
|
c60d41af10 | ||
|
|
91e2e9e7fe | ||
|
|
d1101cd157 | ||
|
|
7594657021 | ||
|
|
9bef1331f2 | ||
|
|
77e2a73345 | ||
|
|
1b518e508e | ||
|
|
1c12187d93 | ||
|
|
67b6f822c3 | ||
|
|
337797b0ed | ||
|
|
1075a72375 | ||
|
|
9fcf4e39a4 | ||
|
|
189ffad11d | ||
|
|
efccaae69e | ||
|
|
34d4321290 | ||
|
|
6cca428d62 | ||
|
|
653c556867 | ||
|
|
8c4ce5d8af | ||
|
|
2261a786a4 | ||
|
|
1f5916e898 | ||
|
|
fe9a81a3a3 | ||
|
|
c0466ed189 | ||
|
|
bc37e47b43 | ||
|
|
6968bf437d | ||
|
|
08be6c15fb | ||
|
|
1d7440b4f7 | ||
|
|
e0e1b6ea9e | ||
|
|
646e33d93a | ||
|
|
8419450427 | ||
|
|
ae94e35607 | ||
|
|
6fb6733727 | ||
|
|
273dd18405 | ||
|
|
8e78ae4e4c | ||
|
|
dda18320f8 | ||
|
|
993ac5f019 | ||
|
|
98dd7894b8 | ||
|
|
f34a996705 | ||
|
|
bc20fc0705 | ||
| 5d606673a6 | |||
|
|
a4f5034c7f | ||
|
|
6e6a6512c9 | ||
|
|
c7ac87c930 | ||
|
|
06b13fdfe1 | ||
|
|
a2ac240150 | ||
|
|
19e99fda9e | ||
|
|
bf94ed37d9 | ||
|
|
5ee33fabdf | ||
|
|
d65b68bbf4 | ||
|
|
6fb57f8422 | ||
|
|
cbbf2db4eb | ||
| 837ea9e369 | |||
|
|
496c7a7908 | ||
|
|
758b2a9033 | ||
|
|
913a86dcc7 | ||
|
|
84879b8d18 | ||
|
|
79b89e7e8b | ||
|
|
cb06b77aef | ||
|
|
b746a6445f | ||
|
|
c062e2a47f | ||
|
|
5828a52329 | ||
|
|
bb9a0f586f | ||
| 8cef3c8358 | |||
|
|
0c012152bb | ||
|
|
76ec26dca0 | ||
|
|
a5472781de | ||
|
|
ff7fbf92e6 | ||
| 158422860d | |||
| 422645bcfd | |||
|
|
dd96bc4171 | ||
|
|
2c950b3ac5 | ||
|
|
25e975fbc8 | ||
|
|
a006c0df33 | ||
|
|
b5cee74731 | ||
|
|
801e17e274 | ||
|
|
93d649162d | ||
| cc529b7e91 | |||
|
|
8f719e5a73 | ||
|
|
8f8788b43c | ||
|
|
04135f7044 | ||
|
|
aca953e827 |
28
.claude/settings.local.json
Normal file
28
.claude/settings.local.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(dir:*)",
|
||||
"Bash(ls:*)",
|
||||
"Bash(find:*)",
|
||||
"Bash(mvn clean:*)",
|
||||
"Bash(rm:*)",
|
||||
"Bash(grep:*)",
|
||||
"Bash(mkdir:*)",
|
||||
"Read(/F:\\gitea\\fusionForce\\CN_Gather\\tools\\wave-comtrade\\src\\main\\java\\com\\njcn\\gather\\tools\\comtrade\\comparewave/**)",
|
||||
"Read(/F:\\gitea\\fusionForce\\CN_Gather\\tools\\wave-comtrade\\src\\main\\java\\com\\njcn\\gather\\tools\\comtrade\\comparewave/**)",
|
||||
"Read(/F:\\gitea\\fusionForce\\CN_Gather\\tools\\wave-comtrade\\src\\main\\java\\com\\njcn\\gather\\tools\\comtrade\\comparewave\\service\\impl/**)",
|
||||
"mcp__exa__web_search_exa",
|
||||
"WebSearch",
|
||||
"Bash(mvn compile:*)",
|
||||
"Bash(git checkout:*)",
|
||||
"Bash(mvn install:*)",
|
||||
"WebFetch(domain:officeopenxml.com)",
|
||||
"Bash(systeminfo)",
|
||||
"Bash(findstr:*)",
|
||||
"Bash(ver)",
|
||||
"Bash(git add:*)"
|
||||
],
|
||||
"deny": []
|
||||
},
|
||||
"outputStyle": "engineer-professional"
|
||||
}
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -48,3 +48,8 @@ rebel.xml
|
||||
/.fastRequest/collections/Root/Default Group/directory.json
|
||||
/.fastRequest/collections/Root/directory.json
|
||||
/.fastRequest/config/fastRequestCurrentProjectConfig.json
|
||||
|
||||
# 个人工作文档,不与团队共享
|
||||
CLAUDE.md
|
||||
docs/
|
||||
data/
|
||||
4
.vscode/settings.json
vendored
Normal file
4
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"java.compile.nullAnalysis.mode": "automatic",
|
||||
"java.configuration.updateBuildConfiguration": "automatic"
|
||||
}
|
||||
1675
CN_Gather_Detection_Netty架构详细分析文档.md
Normal file
1675
CN_Gather_Detection_Netty架构详细分析文档.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -74,6 +74,77 @@
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>4.1.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/org.docx4j/docx4j -->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.docx4j</groupId>-->
|
||||
<!-- <artifactId>docx4j-core</artifactId>-->
|
||||
<!-- <version>8.3.8</version>-->
|
||||
<!-- </dependency>-->
|
||||
<dependency>
|
||||
<groupId>jakarta.xml.bind</groupId>
|
||||
<artifactId>jakarta.xml.bind-api</artifactId>
|
||||
<version>2.3.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jaxb</groupId>
|
||||
<artifactId>jaxb-runtime</artifactId>
|
||||
<version>2.3.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.docx4j</groupId>
|
||||
<artifactId>docx4j</artifactId>
|
||||
<version>6.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.12.0</version> <!-- 您可以根据需要选择其他版本 -->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>2.12.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
<version>2.12.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>RestTemplate-plugin</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-net</groupId>
|
||||
<artifactId>commons-net</artifactId>
|
||||
<version>3.10.0</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!--波形工具模块-->
|
||||
<dependency>
|
||||
<groupId>com.njcn.gather</groupId>
|
||||
<artifactId>wave-comtrade</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!--报告工具模块-->
|
||||
<dependency>
|
||||
<groupId>com.njcn.gather</groupId>
|
||||
<artifactId>report-generator</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<!--激活工具-->
|
||||
<dependency>
|
||||
<groupId>com.njcn.gather</groupId>
|
||||
<artifactId>activate-tool</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
package com.njcn.gather.detection.controller;
|
||||
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.constant.OperateType;
|
||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.common.utils.LogUtil;
|
||||
import com.njcn.gather.detection.pojo.param.ContrastDetectionParam;
|
||||
import com.njcn.gather.detection.pojo.param.PreDetectionParam;
|
||||
import com.njcn.gather.detection.pojo.param.SimulateDetectionParam;
|
||||
import com.njcn.gather.detection.service.PreDetectionService;
|
||||
import com.njcn.gather.detection.service.impl.DetectionServiceImpl;
|
||||
import com.njcn.gather.detection.util.socket.MsgUtil;
|
||||
import com.njcn.gather.device.pojo.vo.PreDetection;
|
||||
import com.njcn.gather.device.service.IPqDevService;
|
||||
import com.njcn.gather.script.pojo.param.PqScriptIssueParam;
|
||||
import com.njcn.gather.script.pojo.po.SourceIssue;
|
||||
import com.njcn.gather.script.service.IPqScriptDtlsService;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import com.njcn.web.utils.HttpResultUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
@@ -22,11 +20,10 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
/**
|
||||
* @author chendaofei
|
||||
*/
|
||||
@Slf4j
|
||||
@Api(tags = "预检测")
|
||||
@RestController
|
||||
@@ -36,30 +33,65 @@ public class PreDetectionController extends BaseController {
|
||||
|
||||
private final PreDetectionService preDetectionService;
|
||||
|
||||
private final DetectionServiceImpl detectionServiceImpl;
|
||||
|
||||
private final IPqScriptDtlsService pqScriptDtlsService;
|
||||
|
||||
private final IPqDevService iPqDevService;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 开始检测
|
||||
* 开始检测通用入口
|
||||
*
|
||||
* @param param 实体参数
|
||||
*/
|
||||
@PostMapping("/startPreTest")
|
||||
@OperateInfo
|
||||
@ApiOperation("开始检测")
|
||||
@ApiImplicitParam(name = "param", value = "查询参数", required = true)
|
||||
public HttpResult<String> startPreTest(@RequestBody @Validated PreDetectionParam param){
|
||||
public HttpResult<String> startPreTest(@RequestBody @Validated PreDetectionParam param) {
|
||||
String methodDescribe = getMethodDescribe("startPreTest");
|
||||
preDetectionService.sourceCommunicationCheck(param);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 程控源-源通讯校验
|
||||
*
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/ytxCheckSimulate")
|
||||
@OperateInfo
|
||||
@ApiOperation("源通讯校验")
|
||||
@ApiImplicitParam(name = "param", value = "查询参数", required = true)
|
||||
public HttpResult<String> ytxCheckSimulate(@RequestBody @Validated SimulateDetectionParam param) {
|
||||
String methodDescribe = getMethodDescribe("ytxCheckSimulate");
|
||||
preDetectionService.ytxCheckSimulate(param);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
|
||||
/**
|
||||
* 程控源检测
|
||||
*/
|
||||
@PostMapping("/startSimulateTest")
|
||||
@OperateInfo
|
||||
@ApiOperation("启动")
|
||||
@ApiImplicitParam(name = "param", value = "查询参数", required = true)
|
||||
public HttpResult<String> startTestSimulate(@RequestBody @Validated SimulateDetectionParam param) {
|
||||
String methodDescribe = getMethodDescribe("startTestSimulate");
|
||||
preDetectionService.sendScript(param);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭检测-程控源
|
||||
*/
|
||||
@PostMapping("/closeSimulateTest")
|
||||
@OperateInfo
|
||||
@ApiOperation("停止")
|
||||
@ApiImplicitParam(name = "param", value = "查询参数", required = true)
|
||||
public HttpResult<String> closeSimulateTest(@RequestBody @Validated SimulateDetectionParam param) {
|
||||
String methodDescribe = getMethodDescribe("closeSimulateTest");
|
||||
preDetectionService.closeTestSimulate(param);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 系数校验
|
||||
*/
|
||||
@@ -67,7 +99,7 @@ public class PreDetectionController extends BaseController {
|
||||
@OperateInfo
|
||||
@ApiOperation("系数校验")
|
||||
@ApiImplicitParam(name = "param", value = "查询参数", required = true)
|
||||
public HttpResult<String> coefficientCheck(@RequestBody PreDetectionParam param){
|
||||
public HttpResult<String> coefficientCheck(@RequestBody PreDetectionParam param) {
|
||||
String methodDescribe = getMethodDescribe("coefficientCheck");
|
||||
preDetectionService.coefficientCheck(param);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
@@ -81,7 +113,7 @@ public class PreDetectionController extends BaseController {
|
||||
@OperateInfo
|
||||
@ApiOperation("暂停检测")
|
||||
@ApiImplicitParam(name = "param", value = "参数", required = true)
|
||||
public HttpResult<String> temStopTest(){
|
||||
public HttpResult<String> temStopTest() {
|
||||
String methodDescribe = getMethodDescribe("temStopTest");
|
||||
preDetectionService.temStopTest();
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
@@ -94,126 +126,54 @@ public class PreDetectionController extends BaseController {
|
||||
@OperateInfo
|
||||
@ApiOperation("重新开始检测")
|
||||
@ApiImplicitParam(name = "param", value = "参数", required = true)
|
||||
public HttpResult<String> restartTemTest(@RequestBody PreDetectionParam param){
|
||||
public HttpResult<String> restartTemTest(@RequestBody PreDetectionParam param) {
|
||||
String methodDescribe = getMethodDescribe("restartTemTest");
|
||||
preDetectionService.restartTemTest(param);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 测试
|
||||
* 开始比对检测(包括预检测、正式检测)通用入口
|
||||
*/
|
||||
@PostMapping("/startTest")
|
||||
@PostMapping("/startContrastTest")
|
||||
@OperateInfo
|
||||
@ApiOperation("测试")
|
||||
@ApiOperation("开始比对检测")
|
||||
@ApiImplicitParam(name = "param", value = "查询参数", required = true)
|
||||
public HttpResult<String> startTest(@RequestBody PreDetectionParam param){
|
||||
String methodDescribe = getMethodDescribe("startTest");
|
||||
//preDetectionService.startTest(param);
|
||||
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:49:50.815\",\"ID\":\"172.17.102.204_2\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:49:50.815\",\"ID\":\"172.17.102.204_3\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001100\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:49:50.815\",\"ID\":\"172.17.102.204_4\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001100\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:49:53.815\",\"ID\":\"172.17.102.204_1\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:49:53.815\",\"ID\":\"172.17.102.204_2\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:49:53.815\",\"ID\":\"172.17.102.204_3\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:49:53.815\",\"ID\":\"172.17.102.204_4\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:49:56.815\",\"ID\":\"172.17.102.204_1\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:49:56.815\",\"ID\":\"172.17.102.204_2\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:49:56.815\",\"ID\":\"172.17.102.204_3\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:49:56.815\",\"ID\":\"172.17.102.204_4\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:49:59.815\",\"ID\":\"172.17.102.204_1\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001100\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:49:59.815\",\"ID\":\"172.17.102.204_2\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:49:59.815\",\"ID\":\"172.17.102.204_3\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:49:59.815\",\"ID\":\"172.17.102.204_4\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:02.8\",\"ID\":\"172.17.102.204_1\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:02.8\",\"ID\":\"172.17.102.204_2\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:02.8\",\"ID\":\"172.17.102.204_3\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:02.8\",\"ID\":\"172.17.102.204_4\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:03\",\"ID\":\"172.17.102.204_1\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:03\",\"ID\":\"172.17.102.204_2\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:03\",\"ID\":\"172.17.102.204_3\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:03\",\"ID\":\"172.17.102.204_4\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:06\",\"ID\":\"172.17.102.204_1\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001100\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:06\",\"ID\":\"172.17.102.204_2\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001100\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:06\",\"ID\":\"172.17.102.204_3\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:06\",\"ID\":\"172.17.102.204_4\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001100\"}}],\"SqlDataHarm\":[]}");
|
||||
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:09\",\"ID\":\"172.17.102.204_1\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:09\",\"ID\":\"172.17.102.204_2\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:09\",\"ID\":\"172.17.102.204_3\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:09\",\"ID\":\"172.17.102.204_4\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:12\",\"ID\":\"172.17.102.204_1\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:12\",\"ID\":\"172.17.102.204_2\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:12\",\"ID\":\"172.17.102.204_3\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:12\",\"ID\":\"172.17.102.204_4\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:15\",\"ID\":\"172.17.102.204_1\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001100\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:15\",\"ID\":\"172.17.102.204_2\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001100\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:15\",\"ID\":\"172.17.102.204_3\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:15\",\"ID\":\"172.17.102.204_4\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001100\"}}],\"SqlDataHarm\":[]}");
|
||||
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:18\",\"ID\":\"172.17.102.204_1\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:18\",\"ID\":\"172.17.102.204_2\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:18\",\"ID\":\"172.17.102.204_3\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:18\",\"ID\":\"172.17.102.204_4\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:21\",\"ID\":\"172.17.102.204_1\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:21\",\"ID\":\"172.17.102.204_2\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:21\",\"ID\":\"172.17.102.204_3\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:21\",\"ID\":\"172.17.102.204_4\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:24\",\"ID\":\"172.17.102.204_1\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:24\",\"ID\":\"172.17.102.204_2\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:24\",\"ID\":\"172.17.102.204_3\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001100\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:24\",\"ID\":\"172.17.102.204_4\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:27\",\"ID\":\"172.17.102.204_1\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:27\",\"ID\":\"172.17.102.204_2\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:27\",\"ID\":\"172.17.102.204_3\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:27\",\"ID\":\"172.17.102.204_4\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:30\",\"ID\":\"172.17.102.204_1\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:30\",\"ID\":\"172.17.102.204_2\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:30\",\"ID\":\"172.17.102.204_3\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:30\",\"ID\":\"172.17.102.204_4\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:33\",\"ID\":\"172.17.102.204_1\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:33\",\"ID\":\"172.17.102.204_2\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:33\",\"ID\":\"172.17.102.204_3\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:33\",\"ID\":\"172.17.102.204_4\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:36\",\"ID\":\"172.17.102.204_1\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:36\",\"ID\":\"172.17.102.204_2\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:36\",\"ID\":\"172.17.102.204_3\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:36\",\"ID\":\"172.17.102.204_4\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:39\",\"ID\":\"172.17.102.204_1\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:39\",\"ID\":\"172.17.102.204_2\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:39\",\"ID\":\"172.17.102.204_3\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:39\",\"ID\":\"172.17.102.204_4\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:42.001\",\"ID\":\"172.17.102.204_1\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:42.001\",\"ID\":\"172.17.102.204_2\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:42.001\",\"ID\":\"172.17.102.204_3\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:42.001\",\"ID\":\"172.17.102.204_4\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:45.001\",\"ID\":\"172.17.102.204_1\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:45.001\",\"ID\":\"172.17.102.204_2\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:45.001\",\"ID\":\"172.17.102.204_3\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001000\"}}],\"SqlDataHarm\":[]}");
|
||||
MsgUtil.socketDataMsg("{\"Time\":\"2025-01-15T15:50:45.001\",\"ID\":\"172.17.102.204_4\",\"result\":false,\"SqlData\":[{\"type\":\"real\",\"desc\":\"FREQ\",\"list\":{\"A\":null,\"B\":null,\"C\":null,\"T\":\"50.001100\"}}],\"SqlDataHarm\":[]}");
|
||||
|
||||
|
||||
PqScriptIssueParam issueParam = new PqScriptIssueParam();
|
||||
issueParam.setPlanId(param.getPlanId());
|
||||
issueParam.setSourceId(param.getSourceId());
|
||||
issueParam.setDevIds(param.getDevIds());
|
||||
issueParam.setScriptId("a303b2224845fcc6f60198b8ca23dca9");
|
||||
//issueParam.setIsPhaseSequence(false);
|
||||
SourceIssue sourceIssues = pqScriptDtlsService.listSourceIssue(issueParam).get(0);
|
||||
|
||||
List<PreDetection> pqDevList = iPqDevService.getDevInfo(param.getDevIds());
|
||||
Map<String, String> devIdMapComm = pqDevList.stream().collect(Collectors.toMap(PreDetection::getDevIP, PreDetection::getDevId));
|
||||
|
||||
//detectionServiceImpl.processing(devDataList,param,devIdMapComm,sourceIssues, DictDataEnum.AT_WILL_VALUE);
|
||||
public HttpResult<String> startContrastTest(@RequestBody @Validated ContrastDetectionParam param) {
|
||||
String methodDescribe = getMethodDescribe("startContrastTest");
|
||||
preDetectionService.startContrastTest(param);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
@OperateInfo(info = LogEnum.SYSTEM_COMMON, operateType = OperateType.DOWNLOAD)
|
||||
@PostMapping("/exportAlignData")
|
||||
@ApiOperation("实时对齐数据导出为csv文件")
|
||||
public void exportAlignData() {
|
||||
String methodDescribe = getMethodDescribe("exportAlignData");
|
||||
LogUtil.njcnDebug(log, "{}", methodDescribe);
|
||||
preDetectionService.exportAlignData();
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
|
||||
@GetMapping("/canCoefficient")
|
||||
@ApiOperation("比对模式是否能够进行系数校验")
|
||||
public HttpResult<Boolean> canCoefficient() {
|
||||
String methodDescribe = getMethodDescribe("canCoefficient");
|
||||
LogUtil.njcnDebug(log, "{}", methodDescribe);
|
||||
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, preDetectionService.getCanCoefficient(), methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
|
||||
@GetMapping("/startCoefficient")
|
||||
@ApiOperation("比对模式开启系数校验")
|
||||
public HttpResult<String> startCoefficient() {
|
||||
String methodDescribe = getMethodDescribe("startCoefficient");
|
||||
LogUtil.njcnDebug(log, "{}", methodDescribe);
|
||||
|
||||
preDetectionService.startCoefficient();
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
package com.njcn.gather.detection.controller;
|
||||
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import com.njcn.web.utils.HttpResultUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author wr
|
||||
* @description
|
||||
* @date 2024/12/10 14:25
|
||||
*/
|
||||
@Slf4j
|
||||
@Api(tags = "守时检测")
|
||||
@RestController
|
||||
@RequestMapping("/punctuality")
|
||||
@RequiredArgsConstructor
|
||||
public class PunctualityController extends BaseController {
|
||||
|
||||
// private final PunctualityService punctualityService;
|
||||
|
||||
@OperateInfo
|
||||
@PostMapping("/deliveryTime")
|
||||
@ApiOperation("下发守时检测")
|
||||
@ApiImplicitParam(name = "queryParam", value = "查询参数", required = true)
|
||||
public HttpResult<?> list(@RequestBody Object param) {
|
||||
String methodDescribe = getMethodDescribe("list");
|
||||
// punctualityService.triggerTimeMark();
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@ package com.njcn.gather.detection.handler;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.njcn.gather.detection.pojo.enums.SourceOperateCodeEnum;
|
||||
import com.njcn.gather.detection.pojo.enums.SourceResponseCodeEnum;
|
||||
import com.njcn.gather.detection.pojo.param.DevPhaseSequenceParam;
|
||||
@@ -10,91 +11,319 @@ import com.njcn.gather.detection.pojo.vo.DevLineTestResult;
|
||||
import com.njcn.gather.detection.pojo.vo.SocketDataMsg;
|
||||
import com.njcn.gather.detection.pojo.vo.SocketMsg;
|
||||
import com.njcn.gather.detection.pojo.vo.WebSocketVO;
|
||||
import com.njcn.gather.detection.util.socket.CnSocketUtil;
|
||||
import com.njcn.gather.detection.util.socket.MsgUtil;
|
||||
import com.njcn.gather.detection.util.socket.SocketManager;
|
||||
import com.njcn.gather.detection.util.socket.WebServiceManager;
|
||||
import com.njcn.gather.detection.util.socket.cilent.NettyClient;
|
||||
import com.njcn.gather.detection.util.socket.cilent.NettyDevClientHandler;
|
||||
import com.njcn.gather.detection.util.socket.*;
|
||||
import com.njcn.gather.detection.util.socket.websocket.WebServiceManager;
|
||||
import com.njcn.gather.device.pojo.vo.PreDetection;
|
||||
import com.njcn.gather.device.service.IPqDevService;
|
||||
import com.njcn.gather.plan.pojo.po.AdPlanSource;
|
||||
import com.njcn.gather.plan.service.IAdPlanSourceService;
|
||||
import com.njcn.gather.result.pojo.enums.ResultUnitEnum;
|
||||
import com.njcn.gather.script.pojo.po.SourceIssue;
|
||||
import com.njcn.gather.source.pojo.po.SourceInitialize;
|
||||
import com.njcn.gather.source.service.IPqSourceService;
|
||||
import com.njcn.gather.system.pojo.enums.DicDataEnum;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 程控源Socket响应处理服务
|
||||
* <p>
|
||||
* 该服务类负责处理来自程控源设备的Socket消息响应,包括:
|
||||
* - 源初始化响应处理
|
||||
* - 检测流程控制和状态管理
|
||||
* - 相序检测、系数校验等特定检测类型的响应处理
|
||||
* - WebSocket消息推送给前端用户
|
||||
* - 错误处理和连接管理
|
||||
* </p>
|
||||
* <p>
|
||||
* 主要处理的操作类型:
|
||||
* - YJC_YTXJY: 源通信校验/源初始化
|
||||
* - YJC_XUJY: 相序检测
|
||||
* - FORMAL_REAL: 正式检测
|
||||
* - Coefficient_Check: 系数校验
|
||||
* - QUITE_SOURCE: 退出源连接
|
||||
* </p>
|
||||
*
|
||||
* @author CN_Gather Detection Team
|
||||
* @version 1.0
|
||||
* @since 2023
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class SocketSourceResponseService {
|
||||
|
||||
/**
|
||||
* 向webSocket客户端发送消息
|
||||
* 设备信息服务,提供设备基础信息查询功能
|
||||
*/
|
||||
|
||||
private final SocketDevResponseService socketDevResponseService;
|
||||
private final IPqDevService iPqDevService;
|
||||
private final IAdPlanSourceService adPlanSourceService;
|
||||
private final IPqSourceService pqSourceService;
|
||||
|
||||
private final String DEV = "_Dev";
|
||||
private final String source = "_Source";
|
||||
/**
|
||||
* Socket连接管理器,负责管理设备和源的Socket连接
|
||||
*/
|
||||
private final SocketManager socketManager;
|
||||
|
||||
private final String stepTag = "&&";
|
||||
private final String stepBegin = "_Start";
|
||||
private final String stepEnd = "_End";
|
||||
/**
|
||||
* 发送WebSocket消息到指定用户页面
|
||||
* <p>
|
||||
* 将数据对象转换为JSON字符串并通过WebSocket推送给前端用户,
|
||||
* 用于实时通知用户检测进度、状态变化或错误信息。
|
||||
* </p>
|
||||
*
|
||||
* @param userPageId 用户页面ID,用于标识消息接收方
|
||||
* @param data 要发送的数据对象,将被转换为JSON格式
|
||||
*/
|
||||
private void sendWebSocketMessage(String userPageId, Object data) {
|
||||
WebServiceManager.sendMsg(userPageId, JSON.toJSONString(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送错误消息并退出源连接
|
||||
* <p>
|
||||
* 当检测过程中发生错误时,执行以下操作:
|
||||
* 1. 主动断开与程控源的连接
|
||||
* 2. 构造包含错误信息的Socket消息
|
||||
* 3. 通过WebSocket将错误信息推送给前端用户
|
||||
* </p>
|
||||
*
|
||||
* @param param 检测参数,包含用户页面ID等信息
|
||||
* @param socketDataMsg 原始Socket消息,用于构造响应消息
|
||||
* @param errorMessage 具体的错误描述信息
|
||||
*/
|
||||
private void sendErrorAndQuit(PreDetectionParam param, SocketDataMsg socketDataMsg, String errorMessage) {
|
||||
CnSocketUtil.quitSendSource(param);
|
||||
SocketMsg<String> socketMsg = new SocketMsg<>();
|
||||
socketMsg.setRequestId(socketDataMsg.getRequestId());
|
||||
socketMsg.setOperateCode(socketDataMsg.getOperateCode());
|
||||
socketMsg.setData(errorMessage);
|
||||
sendWebSocketMessage(param.getUserPageId(), socketMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送错误消息并退出源连接(使用枚举消息)
|
||||
* <p>
|
||||
* 重载方法,使用预定义的错误码枚举来获取标准化的错误消息。
|
||||
* 确保错误信息的一致性和规范性。
|
||||
* </p>
|
||||
*
|
||||
* @param param 检测参数
|
||||
* @param socketDataMsg 原始Socket消息
|
||||
* @param errorCode 错误码枚举,包含标准化的错误描述
|
||||
*/
|
||||
private void sendErrorAndQuit(PreDetectionParam param, SocketDataMsg socketDataMsg, SourceResponseCodeEnum errorCode) {
|
||||
sendErrorAndQuit(param, socketDataMsg, errorCode.getMessage());
|
||||
}
|
||||
|
||||
|
||||
@Value("${socket.device.ip}")
|
||||
private String ip;
|
||||
|
||||
@Value("${socket.device.port}")
|
||||
private Integer port;
|
||||
|
||||
/**
|
||||
* 当前检测会话中的设备列表
|
||||
* <p>
|
||||
* 存储正在进行检测的设备信息,包含设备基本信息和监测点配置。
|
||||
* 注意:该字段存在线程安全问题,建议后续重构为线程安全的设计。
|
||||
* </p>
|
||||
*/
|
||||
private List<PreDetection> devList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 当前检测会话中的监测点ID列表
|
||||
* <p>
|
||||
* 从设备列表中提取的所有监测点ID集合,用于向设备发送数据请求时指定监测范围。
|
||||
* 与devList字段保持同步更新。
|
||||
* </p>
|
||||
*/
|
||||
private List<String> monitorIdList = new ArrayList<>();
|
||||
|
||||
|
||||
/**
|
||||
* 程控源响应消息处理主入口
|
||||
* <p>
|
||||
* 根据消息中的操作码,分发到相应的处理方法:
|
||||
* - 解析Socket消息,提取操作码
|
||||
* - 根据操作码类型调用对应的处理方法
|
||||
* - 支持检测计划模式和模拟测试模式的区分处理
|
||||
* </p>
|
||||
* <p>
|
||||
* 支持的操作类型:
|
||||
* - YJC_YTXJY: 源通信校验/源初始化
|
||||
* - YJC_XUJY: 相序检测
|
||||
* - FORMAL_REAL: 正式检测
|
||||
* - Coefficient_Check: 系数校验
|
||||
* - QUITE_SOURCE: 退出源连接
|
||||
* </p>
|
||||
*
|
||||
* @param param 检测参数,包含用户ID、设备ID、计划ID等关键信息
|
||||
* @param msg 从程控源接收的原始Socket消息
|
||||
* @throws Exception 当消息解析失败或处理过程中发生异常时抛出
|
||||
*/
|
||||
public void deal(PreDetectionParam param, String msg) throws Exception {
|
||||
// 解析接收到的Socket消息
|
||||
SocketDataMsg socketDataMsg = MsgUtil.socketDataMsg(msg);
|
||||
String[] tem = socketDataMsg.getRequestId().split(stepTag);
|
||||
|
||||
// 从requestId中提取操作码,requestId格式为:操作码_步骤标识
|
||||
String[] tem = socketDataMsg.getRequestId().split(CnSocketUtil.STEP_TAG);
|
||||
SourceOperateCodeEnum enumByCode = SourceOperateCodeEnum.getDictDataEnumByCode(tem[0]);
|
||||
|
||||
if (ObjectUtil.isNotNull(enumByCode)) {
|
||||
switch (enumByCode) {
|
||||
//源初始化
|
||||
// 源初始化处理:根据是否有计划ID判断是正式检测还是模拟检测
|
||||
case YJC_YTXJY:
|
||||
detectionDev(param, socketDataMsg);
|
||||
if (ObjectUtil.isNotNull(param.getPlanId())) {
|
||||
// 有计划ID:正式检测模式,源初始化成功后启动设备检测
|
||||
detectionDev(param, socketDataMsg);
|
||||
} else {
|
||||
// 无计划ID:模拟检测模式,仅进行源通信校验
|
||||
handleYtxjySimulate(param, socketDataMsg);
|
||||
}
|
||||
break;
|
||||
//相序检测
|
||||
|
||||
// 相序检测:检测设备的相序是否正确
|
||||
case YJC_XUJY:
|
||||
phaseSequenceDev(param, socketDataMsg);
|
||||
break;
|
||||
//正式检测
|
||||
|
||||
// 正式检测:根据是否有计划ID选择不同的处理方式
|
||||
case FORMAL_REAL:
|
||||
senParamToDev(param, socketDataMsg);
|
||||
if (ObjectUtil.isNotNull(param.getPlanId())) {
|
||||
// 有计划ID:向设备发送检测参数
|
||||
senParamToDev(param, socketDataMsg);
|
||||
} else {
|
||||
// 无计划ID:模拟测试模式
|
||||
handleSimulateTest(param, socketDataMsg);
|
||||
}
|
||||
break;
|
||||
//系数校验
|
||||
|
||||
// 系数校验:验证设备的计量系数是否准确
|
||||
case Coefficient_Check:
|
||||
coefficient(param, socketDataMsg);
|
||||
break;
|
||||
|
||||
// 退出源连接:清理资源并关闭连接
|
||||
case QUITE_SOURCE:
|
||||
quitDeal(socketDataMsg, param);
|
||||
break;
|
||||
|
||||
// YXT操作:暂未实现具体功能
|
||||
case YXT:
|
||||
// TODO: 实现YXT操作的具体逻辑
|
||||
break;
|
||||
|
||||
default:
|
||||
// TODO: 记录未知操作码到日志,并向前端发送友好提示
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
System.out.println("fggggggggggggggggggggg" + enumByCode);
|
||||
// TODO: 向前端发送错误提示
|
||||
log.error("程控源响应消息操作码解析失败,原始消息: {}, 解析结果: {}", msg, enumByCode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理模拟检测中的源通信校验响应
|
||||
* <p>
|
||||
* 在模拟检测模式下(非计划检测),处理程控源的通信校验响应:
|
||||
* - 成功时:根据参数决定是否向前端发送WebSocket消息
|
||||
* - 业务未处理:直接转发消息给前端
|
||||
* - 各种错误情况:统一处理为退出源连接并通知前端
|
||||
* </p>
|
||||
* <p>
|
||||
* 支持的错误类型包括:
|
||||
* - 源连接错误、程控源错误、测试项解析错误
|
||||
* - 源控制错误、目标源错误、未初始化错误
|
||||
* - 未知错误、无法响应错误
|
||||
* </p>
|
||||
*
|
||||
* @param param 检测参数,包含用户信息和WebSocket消息发送控制
|
||||
* @param socketDataMsg 程控源返回的响应消息
|
||||
*/
|
||||
private void handleYtxjySimulate(PreDetectionParam param, SocketDataMsg socketDataMsg) {
|
||||
|
||||
SourceResponseCodeEnum dictDataEnumByCode = SourceResponseCodeEnum.getDictDataEnumByCode(socketDataMsg.getCode());
|
||||
if (ObjectUtil.isNotNull(dictDataEnumByCode)) {
|
||||
switch (dictDataEnumByCode) {
|
||||
case SUCCESS:
|
||||
// 源初始化成功:根据参数控制是否发送WebSocket消息
|
||||
if (param.getSendWebMsg()) {
|
||||
sendWebSocketMessage(param.getUserPageId(), socketDataMsg);
|
||||
}
|
||||
log.info("模拟检测源初始化成功,用户: {}, WebSocket发送: {}",
|
||||
param.getUserPageId(), param.getSendWebMsg());
|
||||
break;
|
||||
case UNPROCESSED_BUSINESS:
|
||||
// 业务暂未处理:直接转发消息给前端等待处理
|
||||
sendWebSocketMessage(param.getUserPageId(), socketDataMsg);
|
||||
break;
|
||||
// 各种错误情况:源连接错误、程控源控制错误、测试项解析错误等
|
||||
case SOURCE_CONNECTION_ERROR:
|
||||
case CONTROLLED_SOURCE_ERROR:
|
||||
case TEST_ITEM_PARSING_ERROR:
|
||||
case SOURCE_CONTROL_ERROR:
|
||||
case TARGET_SOURCE_ERROR:
|
||||
case NOT_INITIALIZED:
|
||||
case UNKNOWN_ERROR:
|
||||
case UNABLE_TO_RESPOND:
|
||||
// 所有错误情况统一处理:退出源连接并通知前端
|
||||
CnSocketUtil.quitSendSource(param);
|
||||
sendWebSocketMessage(param.getUserPageId(), socketDataMsg);
|
||||
break;
|
||||
default:
|
||||
// 未识别的响应码:发送通用错误消息
|
||||
WebServiceManager.sendUnknownErrorMessage(param.getUserPageId());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理模拟检测时,通信模块返回报文
|
||||
*
|
||||
* @param param
|
||||
* @param socketDataMsg
|
||||
*/
|
||||
private void handleSimulateTest(PreDetectionParam param, SocketDataMsg socketDataMsg) {
|
||||
SourceResponseCodeEnum dictDataEnumByCode = SourceResponseCodeEnum.getDictDataEnumByCode(socketDataMsg.getCode());
|
||||
if (ObjectUtil.isNotNull(dictDataEnumByCode)) {
|
||||
switch (dictDataEnumByCode) {
|
||||
case SUCCESS:
|
||||
sendWebSocketMessage(param.getUserPageId(), socketDataMsg);
|
||||
log.info("模拟检测源成功执行脚本,用户: {}, 响应消息: {}",
|
||||
param.getUserPageId(), JSON.toJSONString(socketDataMsg));
|
||||
break;
|
||||
case UNPROCESSED_BUSINESS:
|
||||
sendWebSocketMessage(param.getUserPageId(), socketDataMsg);
|
||||
break;
|
||||
default:
|
||||
sendErrorAndQuit(param, socketDataMsg, dictDataEnumByCode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 系数校验源数据返回处理
|
||||
* <p>
|
||||
* 处理系数校验阶段程控源的响应消息:
|
||||
* 1. 成功时:向前端推送响应信息,然后向设备发送数据请求
|
||||
* 2. 构造设备数据请求参数,包含监测点列表和数据类型
|
||||
* 3. 设置固定的读取参数:读取3次数据,忽略前4次
|
||||
* 4. 请求的数据类型:实时电压有效值(real$VRMS)和实时电流有效值(real$IRMS)
|
||||
* </p>
|
||||
* <p>
|
||||
* 数据请求配置:
|
||||
* - 监测点:使用当前会话的monitorIdList
|
||||
* - 数据类型:["real$VRMS", "real$IRMS"]
|
||||
* - 读取次数:3次
|
||||
* - 忽略次数:4次(预热数据)
|
||||
* </p>
|
||||
*
|
||||
* @param param 检测参数
|
||||
* @param socketDataMsg 程控源响应消息
|
||||
*/
|
||||
private void coefficient(PreDetectionParam param, SocketDataMsg socketDataMsg) {
|
||||
SourceResponseCodeEnum dictDataEnumByCode = SourceResponseCodeEnum.getDictDataEnumByCode(socketDataMsg.getCode());
|
||||
@@ -103,30 +332,28 @@ public class SocketSourceResponseService {
|
||||
switch (dictDataEnumByCode) {
|
||||
case SUCCESS:
|
||||
//向前端推送信息
|
||||
WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg));
|
||||
sendWebSocketMessage(param.getUserPageId(), socketDataMsg);
|
||||
|
||||
String s = param.getUserPageId() + DEV;
|
||||
String s = param.getUserPageId() + CnSocketUtil.DEV_TAG;
|
||||
socketMsg.setRequestId(SourceOperateCodeEnum.Coefficient_Check.getValue());
|
||||
socketMsg.setOperateCode(SourceOperateCodeEnum.DEV_DATA_REQUEST_02.getValue());
|
||||
DevPhaseSequenceParam phaseSequenceParam = new DevPhaseSequenceParam();
|
||||
phaseSequenceParam.setMoniterIdList(monitorIdList);
|
||||
// 系数校验固定检测项:实时电压有效值和实时电流有效值
|
||||
phaseSequenceParam.setDataType(Arrays.asList("real$VRMS", "real$IRMS"));
|
||||
phaseSequenceParam.setReadCount(10);
|
||||
phaseSequenceParam.setIgnoreCount(7);
|
||||
// 读取3次数据用于系数计算
|
||||
phaseSequenceParam.setReadCount(7); //3
|
||||
// 忽略前4次数据,等待测量稳定
|
||||
phaseSequenceParam.setIgnoreCount(3); //4
|
||||
socketMsg.setData(JSON.toJSONString(phaseSequenceParam));
|
||||
SocketManager.sendMsg(s, JSON.toJSONString(socketMsg));
|
||||
|
||||
|
||||
break;
|
||||
case UNPROCESSED_BUSINESS:
|
||||
WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg));
|
||||
sendWebSocketMessage(param.getUserPageId(), socketDataMsg);
|
||||
break;
|
||||
default:
|
||||
CnSocketUtil.quitSendSource(param);
|
||||
socketMsg.setRequestId(socketDataMsg.getRequestId());
|
||||
socketMsg.setOperateCode(socketDataMsg.getOperateCode());
|
||||
socketMsg.setData(dictDataEnumByCode.getMessage());
|
||||
WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketMsg));
|
||||
sendErrorAndQuit(param, socketDataMsg, dictDataEnumByCode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -135,69 +362,63 @@ public class SocketSourceResponseService {
|
||||
/**
|
||||
* 装置检测(当源初始化成功后,直接向装置通道向装置服务器发送,装置检测)
|
||||
*
|
||||
* @param param
|
||||
* @param socketDataMsg
|
||||
* @param param 参数
|
||||
* @param socketDataMsg 消息
|
||||
*/
|
||||
private void detectionDev(PreDetectionParam param, SocketDataMsg socketDataMsg) {
|
||||
SourceResponseCodeEnum dictDataEnumByCode = SourceResponseCodeEnum.getDictDataEnumByCode(socketDataMsg.getCode());
|
||||
if (ObjectUtil.isNotNull(dictDataEnumByCode)) {
|
||||
SocketMsg<String> socketMsg = new SocketMsg<>();
|
||||
|
||||
switch (dictDataEnumByCode) {
|
||||
case SUCCESS:
|
||||
//todo 前端推送收到的消息暂未处理好
|
||||
WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg));
|
||||
String s = param.getUserPageId() + DEV;
|
||||
//开始设备通讯检测(发送设备初始化)
|
||||
List<PreDetection> devList = iPqDevService.getDevInfo(param.getDevIds());
|
||||
Map<String, List<PreDetection>> map = new HashMap<>(1);
|
||||
map.put("deviceList", devList);
|
||||
String jsonString = JSON.toJSONString(map);
|
||||
socketMsg.setRequestId(SourceOperateCodeEnum.YJC_SBTXJY.getValue());
|
||||
socketMsg.setOperateCode(SourceOperateCodeEnum.DEV_INIT_GATHER_01.getValue());
|
||||
socketMsg.setData(jsonString);
|
||||
String json = JSON.toJSONString(socketMsg);
|
||||
// SocketManager.sendMsg(s,json);
|
||||
NettyClient.socketClient(ip, port, param, json, new NettyDevClientHandler(param, socketDevResponseService));
|
||||
if (FormalTestManager.unknownError) {
|
||||
FormalTestManager.unknownError = false;
|
||||
|
||||
//重新下发脚本
|
||||
String type = FormalTestManager.currentIssue.getType();
|
||||
if (ResultUnitEnum.P.getCode().equals(type)) {
|
||||
FormalTestManager.currentIssue.setType(ResultUnitEnum.V_ABSOLUTELY.getCode());
|
||||
}
|
||||
socketMsg.setOperateCode(SourceOperateCodeEnum.OPER_GATHER.getValue());
|
||||
socketMsg.setData(JSON.toJSONString(FormalTestManager.currentIssue));
|
||||
socketMsg.setRequestId(SourceOperateCodeEnum.FORMAL_REAL.getValue() + CnSocketUtil.STEP_TAG + type);
|
||||
SocketManager.sendMsg(param.getUserPageId() + CnSocketUtil.SOURCE_TAG, JSON.toJSONString(socketMsg));
|
||||
} else {
|
||||
//todo 前端推送收到的消息暂未处理好
|
||||
sendWebSocketMessage(param.getUserPageId(), socketDataMsg);
|
||||
//开始设备通讯检测(发送设备初始化)
|
||||
Map<String, List<PreDetection>> map = new HashMap<>(1);
|
||||
map.put("deviceList", FormalTestManager.devList);
|
||||
String jsonString = JSON.toJSONString(map);
|
||||
socketMsg.setRequestId(SourceOperateCodeEnum.YJC_SBTXJY.getValue());
|
||||
socketMsg.setOperateCode(SourceOperateCodeEnum.DEV_INIT_GATHER_01.getValue());
|
||||
socketMsg.setData(jsonString);
|
||||
String json = JSON.toJSONString(socketMsg);
|
||||
// 使用智能发送工具类,自动管理设备连接
|
||||
socketManager.smartSendToDevice(param, json);
|
||||
}
|
||||
break;
|
||||
case UNPROCESSED_BUSINESS:
|
||||
WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg));
|
||||
sendWebSocketMessage(param.getUserPageId(), socketDataMsg);
|
||||
break;
|
||||
case SOURCE_CONNECTION_ERROR:
|
||||
CnSocketUtil.quitSendSource(param);
|
||||
WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg));
|
||||
case CONTROLLED_SOURCE_ERROR:
|
||||
CnSocketUtil.quitSendSource(param);
|
||||
WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg));
|
||||
break;
|
||||
case TEST_ITEM_PARSING_ERROR:
|
||||
CnSocketUtil.quitSendSource(param);
|
||||
WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg));
|
||||
break;
|
||||
case SOURCE_CONTROL_ERROR:
|
||||
CnSocketUtil.quitSendSource(param);
|
||||
WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg));
|
||||
break;
|
||||
case TARGET_SOURCE_ERROR:
|
||||
CnSocketUtil.quitSendSource(param);
|
||||
WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg));
|
||||
break;
|
||||
case NOT_INITIALIZED:
|
||||
CnSocketUtil.quitSendSource(param);
|
||||
WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg));
|
||||
break;
|
||||
case UNKNOWN_ERROR:
|
||||
CnSocketUtil.quitSendSource(param);
|
||||
WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg));
|
||||
break;
|
||||
case UNABLE_TO_RESPOND:
|
||||
CnSocketUtil.quitSendSource(param);
|
||||
WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg));
|
||||
sendWebSocketMessage(param.getUserPageId(), socketDataMsg);
|
||||
break;
|
||||
default:
|
||||
CnSocketUtil.sendUnSocket(param.getUserPageId());
|
||||
// todo... 这种情况是报文的状态码不一致,需要记录到日志表,以便问题追踪
|
||||
WebServiceManager.sendUnknownErrorMessage(param.getUserPageId());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// todo... 这种情况是报文的状态码不一致,需要记录到日志表,以便问题追踪
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,9 +435,9 @@ public class SocketSourceResponseService {
|
||||
switch (dictDataEnumByCode) {
|
||||
case SUCCESS:
|
||||
//向前端推送信息
|
||||
WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg));
|
||||
sendWebSocketMessage(param.getUserPageId(), socketDataMsg);
|
||||
|
||||
String s = param.getUserPageId() + DEV;
|
||||
String s = param.getUserPageId() + CnSocketUtil.DEV_TAG;
|
||||
socketMsg.setRequestId(SourceOperateCodeEnum.YJC_XUJY.getValue());
|
||||
socketMsg.setOperateCode(SourceOperateCodeEnum.DEV_DATA_REQUEST_02.getValue());
|
||||
List<PreDetection> pqDevList = iPqDevService.getDevInfo(param.getDevIds());
|
||||
@@ -226,24 +447,23 @@ public class SocketSourceResponseService {
|
||||
|
||||
DevPhaseSequenceParam phaseSequenceParam = new DevPhaseSequenceParam();
|
||||
phaseSequenceParam.setMoniterIdList(moniterIdList);
|
||||
// 相序检测项:电压有效值、电压角度、电流有效值、电流角度
|
||||
phaseSequenceParam.setDataType(Arrays.asList("real$VRMS", "real$VA", "real$IRMS", "real$IA"));
|
||||
// 相序检测只需要读取1次数据
|
||||
phaseSequenceParam.setReadCount(1);
|
||||
// 忽略前10次数据,确保相序稳定
|
||||
phaseSequenceParam.setIgnoreCount(10);
|
||||
socketMsg.setData(JSON.toJSONString(phaseSequenceParam));
|
||||
SocketManager.sendMsg(s, JSON.toJSONString(socketMsg));
|
||||
break;
|
||||
case UNPROCESSED_BUSINESS:
|
||||
WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg));
|
||||
sendWebSocketMessage(param.getUserPageId(), socketDataMsg);
|
||||
break;
|
||||
case MESSAGE_PARSING_ERROR:
|
||||
CnSocketUtil.quitSendSource(param);
|
||||
break;
|
||||
default:
|
||||
CnSocketUtil.quitSendSource(param);
|
||||
socketMsg.setRequestId(socketDataMsg.getRequestId());
|
||||
socketMsg.setOperateCode(socketDataMsg.getOperateCode());
|
||||
socketMsg.setData(dictDataEnumByCode.getMessage());
|
||||
WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketMsg));
|
||||
sendErrorAndQuit(param, socketDataMsg, dictDataEnumByCode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -251,10 +471,28 @@ public class SocketSourceResponseService {
|
||||
|
||||
|
||||
/**
|
||||
* 组装和装置要数据
|
||||
* 正式检测时向设备发送参数请求
|
||||
* <p>
|
||||
* 当程控源成功执行脚本后,根据检测项目类型向设备发送相应的数据请求:
|
||||
* 1. 获取源脚本信息,确定检测类型和数据类型
|
||||
* 2. 根据检测类型设置不同的读取参数:
|
||||
* - 闪变(F):忽略1次,读取2次,使用DEV_DATA_REQUEST_01
|
||||
* - 暂态(VOLTAGE):忽略5次,读取1次,使用DEV_DATA_REQUEST_03
|
||||
* - 其他类型:忽略5次,读取5次,根据数据类型选择操作码
|
||||
* 3. 构造设备数据请求并发送
|
||||
* 4. 向前端推送检测开始信息
|
||||
* </p>
|
||||
* <p>
|
||||
* 检测参数配置:
|
||||
* - 闪变检测:ignoreCount=1, readCount=2
|
||||
* - 暂态检测:ignoreCount=5, readCount=1
|
||||
* - 常规检测:ignoreCount=5, readCount=5
|
||||
* - 实时数据:使用DEV_DATA_REQUEST_02
|
||||
* - 分钟数据:使用DEV_DATA_REQUEST_01
|
||||
* </p>
|
||||
*
|
||||
* @param param
|
||||
* @param socketDataMsg
|
||||
* @param param 检测参数,包含用户和设备信息
|
||||
* @param socketDataMsg 程控源成功响应消息
|
||||
*/
|
||||
private void senParamToDev(PreDetectionParam param, SocketDataMsg socketDataMsg) {
|
||||
SourceResponseCodeEnum dictDataEnumByCode = SourceResponseCodeEnum.getDictDataEnumByCode(socketDataMsg.getCode());
|
||||
@@ -265,40 +503,74 @@ public class SocketSourceResponseService {
|
||||
//向前端推送信息
|
||||
// webSocketHandler.sendMsgToUser(param.getUserPageId(), JSON.toJSONString(socketDataMsg));
|
||||
|
||||
String s = param.getUserPageId() + DEV;
|
||||
// 构造设备通道标识:用户ID + 设备标签
|
||||
String s = param.getUserPageId() + CnSocketUtil.DEV_TAG;
|
||||
|
||||
// 获取当前检测的源脚本信息,包含检测类型和数据要求
|
||||
SourceIssue sourceIssue = SocketManager.getSourceList().get(0);
|
||||
// 数据类型列表,格式:real$VRMS、real$IRMS
|
||||
List<String> comm = sourceIssue.getDevValueTypeList();
|
||||
System.out.println("向装置下发的参数>>>>>>>>" + comm);
|
||||
socketMsg.setRequestId(SourceOperateCodeEnum.FORMAL_REAL.getValue() + stepTag + sourceIssue.getType());
|
||||
log.debug("向设备下发检测参数,用户: {}, 数据类型: {}", param.getUserPageId(), comm);
|
||||
|
||||
// 设置请求ID:正式检测操作码 + 步骤标识 + 检测类型
|
||||
socketMsg.setRequestId(socketDataMsg.getRequestId());
|
||||
|
||||
// 根据检测类型设置不同的读取参数和操作码
|
||||
int ignoreCount;
|
||||
int readData;
|
||||
if (sourceIssue.getType().equals(DicDataEnum.F.getCode())) {
|
||||
|
||||
if (DicDataEnum.F.getCode().equals(sourceIssue.getType())) {
|
||||
// 闪变检测:数据变化较慢,只需少量预热和读取
|
||||
// 闪变测量稳定性好,预热1次即可
|
||||
ignoreCount = 1;
|
||||
// 读取2次数据计算闪变值
|
||||
readData = 2;
|
||||
socketMsg.setOperateCode(SourceOperateCodeEnum.DEV_DATA_REQUEST_01.getValue());
|
||||
} else if (sourceIssue.getType().equals(DicDataEnum.HP.getCode())) {
|
||||
ignoreCount = 2;
|
||||
readData = 5;
|
||||
socketMsg.setOperateCode(SourceOperateCodeEnum.DEV_DATA_REQUEST_01.getValue());
|
||||
} else if (DicDataEnum.VOLTAGE.getCode().equals(sourceIssue.getType())) {
|
||||
// 暂态电压检测:需要更多预热时间,但只读取一次快照
|
||||
// 暂态事件需要5次预热确保触发稳定
|
||||
ignoreCount = 5;
|
||||
// 暂态检测只需要捕获一次事件
|
||||
readData = 1;
|
||||
socketMsg.setOperateCode(SourceOperateCodeEnum.DEV_DATA_REQUEST_03.getValue());
|
||||
} else {
|
||||
// 常规检测(谐波、不平衡度等):需要多次采样以提高精度
|
||||
// 常规检测预热5次等待稳定
|
||||
ignoreCount = 5;
|
||||
// 读取5次数据进行统计分析
|
||||
readData = 5;
|
||||
socketMsg.setOperateCode(SourceOperateCodeEnum.DEV_DATA_REQUEST_02.getValue());
|
||||
}
|
||||
|
||||
List<String> moniterIdList = devList.stream().flatMap(x -> x.getMonitorList().stream()).map(PreDetection.MonitorListDTO::getLineId).collect(Collectors.toList());
|
||||
// 根据数据类型选择相应的请求操作码
|
||||
if ("real".equals(sourceIssue.getDataType())) {
|
||||
// 实时数据:瞬时值或有效值
|
||||
socketMsg.setOperateCode(SourceOperateCodeEnum.DEV_DATA_REQUEST_02.getValue());
|
||||
} else {
|
||||
// 分钟数据:统计周期内的平均值或累计值
|
||||
socketMsg.setOperateCode(SourceOperateCodeEnum.DEV_DATA_REQUEST_01.getValue());
|
||||
}
|
||||
}
|
||||
log.debug("检测设备列表状态检查,用户: {}, 设备列表为空: {}",
|
||||
param.getUserPageId(), CollectionUtils.isEmpty(devList));
|
||||
|
||||
// 构造设备数据请求参数
|
||||
DevPhaseSequenceParam phaseSequenceParam = new DevPhaseSequenceParam();
|
||||
phaseSequenceParam.setMoniterIdList(moniterIdList);
|
||||
// 设置监测点ID列表
|
||||
phaseSequenceParam.setMoniterIdList(monitorIdList);
|
||||
if (socketDataMsg.getRequestId().equals(SourceOperateCodeEnum.FORMAL_REAL.getValue() + CnSocketUtil.STEP_TAG + "P")) {
|
||||
comm.add("real$PF");
|
||||
}
|
||||
// 设置数据类型列表
|
||||
phaseSequenceParam.setDataType(comm);
|
||||
// 设置读取次数
|
||||
phaseSequenceParam.setReadCount(readData);
|
||||
// 设置忽略次数
|
||||
phaseSequenceParam.setIgnoreCount(ignoreCount);
|
||||
socketMsg.setData(JSON.toJSONString(phaseSequenceParam));
|
||||
|
||||
// 向设备发送数据请求
|
||||
SocketManager.sendMsg(s, JSON.toJSONString(socketMsg));
|
||||
|
||||
// 构造前端显示的设备列表,只包含设备ID和名称
|
||||
List<DevLineTestResult> devListRes = new ArrayList<>();
|
||||
devList.forEach(item -> {
|
||||
DevLineTestResult devLineTestResult = new DevLineTestResult();
|
||||
@@ -307,21 +579,25 @@ public class SocketSourceResponseService {
|
||||
devListRes.add(devLineTestResult);
|
||||
});
|
||||
|
||||
// 构造WebSocket消息并推送给前端,通知检测开始
|
||||
WebSocketVO<Object> webSocketVO = new WebSocketVO<>();
|
||||
webSocketVO.setRequestId(socketDataMsg.getRequestId().split(stepTag)[1] + stepBegin);
|
||||
// 设置请求ID:检测类型 + 开始标识
|
||||
webSocketVO.setRequestId(socketDataMsg.getRequestId().split(CnSocketUtil.STEP_TAG)[1] + CnSocketUtil.START_TAG);
|
||||
// 检测描述信息
|
||||
webSocketVO.setDesc(SocketManager.getSourceList().get(0).getDesc());
|
||||
// 参与检测的设备列表
|
||||
webSocketVO.setData(devListRes);
|
||||
WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(webSocketVO));
|
||||
sendWebSocketMessage(param.getUserPageId(), webSocketVO);
|
||||
break;
|
||||
case UNPROCESSED_BUSINESS:
|
||||
WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg));
|
||||
sendWebSocketMessage(param.getUserPageId(), socketDataMsg);
|
||||
break;
|
||||
case UNKNOWN_ERROR: //-1源未知异常
|
||||
CnSocketUtil.quitSendSource(param);
|
||||
FormalTestManager.unknownError = true;
|
||||
break;
|
||||
default:
|
||||
CnSocketUtil.quitSendSource(param);
|
||||
socketMsg.setRequestId(socketDataMsg.getRequestId());
|
||||
socketMsg.setOperateCode(socketDataMsg.getOperateCode());
|
||||
socketMsg.setData(dictDataEnumByCode.getMessage());
|
||||
WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketMsg));
|
||||
sendErrorAndQuit(param, socketDataMsg, dictDataEnumByCode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -329,22 +605,53 @@ public class SocketSourceResponseService {
|
||||
|
||||
|
||||
/**
|
||||
* 退出检测返回
|
||||
* 处理退出检测的响应
|
||||
* <p>
|
||||
* 当用户主动退出检测或系统需要终止检测时,处理程控源的退出响应:
|
||||
* - 成功退出:移除Socket连接管理中的用户信息,向前端发送成功消息
|
||||
* - 业务未处理:不做特殊处理
|
||||
* - 消息解析错误/无法响应:移除用户连接信息
|
||||
* - 其他错误:调用退出源连接方法
|
||||
* </p>
|
||||
* <p>
|
||||
* 退出流程:
|
||||
* 1. 解析响应状态码
|
||||
* 2. 根据状态码执行相应的清理操作
|
||||
* 3. 确保Socket连接资源得到正确释放
|
||||
* </p>
|
||||
*
|
||||
* @param socketDataMsg 程控源退出响应消息
|
||||
* @param param 检测参数,包含用户信息
|
||||
*/
|
||||
private void quitDeal(SocketDataMsg socketDataMsg, PreDetectionParam param) {
|
||||
SourceResponseCodeEnum dictDataEnumByCode = SourceResponseCodeEnum.getDictDataEnumByCode(socketDataMsg.getCode());
|
||||
switch (Objects.requireNonNull(dictDataEnumByCode)) {
|
||||
case SUCCESS:
|
||||
//通讯校验成功
|
||||
SocketManager.removeUser(param.getUserPageId() + source);
|
||||
if (FormalTestManager.unknownError) {
|
||||
//获取源初始化参数
|
||||
AdPlanSource planSource = adPlanSourceService.getOne(new LambdaQueryWrapper<AdPlanSource>().eq(AdPlanSource::getPlanId, param.getPlanId()));
|
||||
SourceInitialize sourceParam = pqSourceService.getSourceInitializeParam(planSource.getSourceId());
|
||||
if (ObjectUtil.isNotNull(sourceParam)) {
|
||||
SocketMsg<String> socketMsg1 = new SocketMsg<>();
|
||||
socketMsg1.setRequestId(SourceOperateCodeEnum.YJC_YTXJY.getValue());
|
||||
socketMsg1.setOperateCode(SourceOperateCodeEnum.INIT_GATHER.getValue());
|
||||
socketMsg1.setData(JSON.toJSONString(sourceParam));
|
||||
//使用智能发送工具类,自动管理与源控程序的socket连接
|
||||
socketManager.smartSendToSource(param, JSON.toJSONString(socketMsg1));
|
||||
}
|
||||
} else {
|
||||
//通讯校验成功
|
||||
SocketManager.removeUser(param.getUserPageId() + CnSocketUtil.SOURCE_TAG);
|
||||
sendWebSocketMessage(param.getUserPageId(), socketDataMsg);
|
||||
}
|
||||
break;
|
||||
case UNPROCESSED_BUSINESS:
|
||||
break;
|
||||
case MESSAGE_PARSING_ERROR:
|
||||
SocketManager.removeUser(param.getUserPageId() + source);
|
||||
SocketManager.removeUser(param.getUserPageId() + CnSocketUtil.SOURCE_TAG);
|
||||
break;
|
||||
case UNABLE_TO_RESPOND:
|
||||
SocketManager.removeUser(param.getUserPageId() + source);
|
||||
SocketManager.removeUser(param.getUserPageId() + CnSocketUtil.SOURCE_TAG);
|
||||
break;
|
||||
default:
|
||||
CnSocketUtil.quitSendSource(param);
|
||||
@@ -354,13 +661,40 @@ public class SocketSourceResponseService {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 初始化检测设备和监测点列表
|
||||
* <p>
|
||||
* 在开始检测前,根据检测参数初始化当前会话的设备信息:
|
||||
* 1. 清空之前的设备列表和监测点列表
|
||||
* 2. 根据设备ID列表查询设备详细信息
|
||||
* 3. 从设备信息中提取所有监测点的线路ID
|
||||
* 4. 同步更新XiNumberManager中的设备列表
|
||||
* </p>
|
||||
* <p>
|
||||
* 该方法通常在检测开始前调用,确保后续的检测流程能够获取到正确的
|
||||
* 设备配置和监测点信息。
|
||||
* </p>
|
||||
*
|
||||
* @param param 检测参数,包含要检测的设备ID列表
|
||||
*/
|
||||
public void initList(PreDetectionParam param) {
|
||||
// 清空现有列表,为新的检测会话做准备
|
||||
devList.clear();
|
||||
monitorIdList.clear();
|
||||
|
||||
// 查询设备详细信息,包含监测点配置
|
||||
this.devList = iPqDevService.getDevInfo(param.getDevIds());
|
||||
this.monitorIdList = devList.stream().flatMap(x -> x.getMonitorList().stream())
|
||||
|
||||
// 提取所有设备的监测点线路ID
|
||||
this.monitorIdList = devList.stream()
|
||||
.flatMap(x -> x.getMonitorList().stream())
|
||||
.map(PreDetection.MonitorListDTO::getLineId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 同步更新系数管理器中的设备列表
|
||||
XiNumberManager.xiDevList = devList;
|
||||
FormalTestManager.unknownError = false;
|
||||
FormalTestManager.currentIssue = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.njcn.gather.detection.mapper;
|
||||
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import com.njcn.gather.detection.pojo.po.AdPair;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2025-08-18
|
||||
*/
|
||||
public interface AdPairMapper extends MPJBaseMapper<AdPair> {
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.njcn.gather.detection.pojo.constant;
|
||||
|
||||
/**
|
||||
* 通信相关的常量
|
||||
*
|
||||
* @author hongawen
|
||||
* @version 1.0
|
||||
* @data 2025/4/15 14:11
|
||||
*/
|
||||
public interface DetectionCommunicateConstant {
|
||||
|
||||
String SOURCE_CHANNEL_NAME = "AUTO_DETECTION_SOURCE";
|
||||
|
||||
String DEVICE_CHANNEL_NAME = "AUTO_DETECTION_DEV";
|
||||
|
||||
String DEV = "_Dev";
|
||||
|
||||
String SOURCE = "_Source";
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.njcn.gather.detection.pojo.dto;
|
||||
|
||||
import com.njcn.gather.err.pojo.po.PqErrSysDtls;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2025-08-12
|
||||
*/
|
||||
@Data
|
||||
public class ConditionDataDTO {
|
||||
|
||||
/**
|
||||
* 某一相别且某一个误差条件范围内的被检色设备数据
|
||||
*/
|
||||
private Double devData;
|
||||
|
||||
/**
|
||||
* 某一相别且某一个误差条件范围内的标准设备数据
|
||||
*/
|
||||
private Double stdDevData;
|
||||
|
||||
|
||||
/**
|
||||
* 与上面数据所对应的误差体系详情
|
||||
*/
|
||||
private PqErrSysDtls pqErrSysDtls;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.njcn.gather.detection.pojo.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2025-08-12
|
||||
*/
|
||||
@Data
|
||||
public class HarmonicConditionDataDTO extends ConditionDataDTO {
|
||||
|
||||
/**
|
||||
* (间谐波)谐波次数
|
||||
*/
|
||||
private Double harmonicNum;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.njcn.gather.detection.pojo.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2025-09-01
|
||||
*/
|
||||
@Data
|
||||
public class WaveCommandDTO {
|
||||
private String ip;
|
||||
|
||||
private Integer port;
|
||||
|
||||
private String oper;
|
||||
|
||||
private Integer line;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.njcn.gather.detection.pojo.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2025-09-01
|
||||
*/
|
||||
@Data
|
||||
public class WaveResultDTO {
|
||||
private String id;
|
||||
|
||||
private String path;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.njcn.gather.detection.pojo.enums;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @Description:
|
||||
* @Author: wr
|
||||
* @Date: 2025/2/12 10:15
|
||||
*/
|
||||
@Getter
|
||||
public enum DetectionCodeEnum {
|
||||
|
||||
FREQ("FREQ", "频率"),
|
||||
VRMS("VRMS", "相电压有效值"),
|
||||
PVRMS("PVRMS", "线电压有效值"),
|
||||
DELTA_V("DELTA_V", "电压偏差"),
|
||||
VA("VA", "电压相角"),
|
||||
U1A("U1A", "相电压基波有效值角度值"),
|
||||
PU1A("PU1A", "线电压基波有效值角度值"),
|
||||
U1("U1", "基波电压"),
|
||||
PU1("PU1", "线电压基波电压"),
|
||||
V2_50("V2-50", "谐波电压"),
|
||||
PV2_50("PV2-50", "线电压谐波电压"),
|
||||
I2_50("I2-50", "谐波电流"),
|
||||
P2_50("P2-50", "谐波有功功率"),
|
||||
SV_1_49("SV_1-49", "间谐波电压"),
|
||||
PSV_1_49("PSV_1-49", "线电压间谐波电压"),
|
||||
SI_1_49("SI_1-49", "间谐波电流"),
|
||||
MAG("MAG", "电压幅值"),
|
||||
DUR("DUR", "持续时间"),
|
||||
IRMS("IRMS", "电流有效值"),
|
||||
IA("IA", "电流相角"),
|
||||
I1A("I1A", "电流基波角度值"),
|
||||
V_UNBAN("V_UNBAN", "三相电压负序不平衡度"),
|
||||
I_UNBAN("I_UNBAN", "三相电流负序不平衡度"),
|
||||
PST("PST", "短时间闪变"),
|
||||
W("W", "有功功率"),
|
||||
VARW("VARW", "无功功率"),
|
||||
VAW("VAW", "视在功率"),
|
||||
// PF("PF", "功率因数"),
|
||||
// P_FUND("P_FUND", "基波有功功率"),
|
||||
// P_HVAR("P_HVAR", "基波无功功率"),
|
||||
// P_HVA("P_HVA", "基波视在功率"),
|
||||
I1("I1", "基波电流"),
|
||||
UNKNOWN_ERROR("-1", "未知异常"),
|
||||
|
||||
|
||||
STAR("Star", "星型接线"),
|
||||
DELTA("Delta", "角型接线"),
|
||||
|
||||
REAL_PREFIX("real$", "实时数据前缀"),
|
||||
CP_95_PREFIX("cp95$", "分钟统计-CP95值数据前缀"),
|
||||
MAX_PREFIX("max$", "分钟统计-最大值数据前缀"),
|
||||
MIN_PREFIX("min$", "分钟统计-最小值数据前缀"),
|
||||
AVG_PREFIX("avg$", "分钟统计-平均值数据前缀");
|
||||
|
||||
private final String code;
|
||||
private final String message;
|
||||
|
||||
DetectionCodeEnum(String code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public static DetectionCodeEnum getDetectionCodeByCode(String code) {
|
||||
for (DetectionCodeEnum detectionCodeEnum : DetectionCodeEnum.values()) {
|
||||
if (ObjectUtil.equals(code, detectionCodeEnum.getCode())) {
|
||||
return detectionCodeEnum;
|
||||
}
|
||||
}
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
@@ -9,11 +9,17 @@ import lombok.Getter;
|
||||
@Getter
|
||||
public enum DetectionResponseEnum {
|
||||
PLAN_PATTERN_NOT("A020001", "计划模式查询为空"),
|
||||
PLAN_NOT_EXIST("A020001", "计划信息缺失"),
|
||||
SCRIPT_PATTERN_NOT("A020001", "检测脚本查询为空"),
|
||||
SOURCE_INFO_NOT("A020002", "源表信息不存在"),
|
||||
PLAN_AND_SOURCE_NOT("A020003", "计划和源关系不存在"),
|
||||
ITEM_TEST_NOT("A020004", "检测项为空"),
|
||||
PLAN_DEV_IP_HAS("A20005","当前计划检测装置ip重复")
|
||||
;
|
||||
PLAN_DEV_IP_HAS("A20005","当前计划检测装置ip重复"),
|
||||
SOURCE_NOT_CONNECT("A020006", "源未连接"),
|
||||
|
||||
|
||||
SCRIPT_CHECK_DATA_NOT_EXIST("A020040","测试脚本项暂无配置" ),
|
||||
EXCEED_MAX_TIME("A020041","检测次数超出最大限制!" );
|
||||
|
||||
private final String code;
|
||||
private final String message;
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.njcn.gather.detection.pojo.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2025-08-13
|
||||
*/
|
||||
@Getter
|
||||
public enum ResultEnum {
|
||||
QUALIFIED(1, "符合"),
|
||||
NOT_QUALIFIED(2, "不符合"),
|
||||
NETWORK_TIMEOUT(3, "网络超时"),
|
||||
NO_ERROR_SYS(4, "不在误差条件范围内"),
|
||||
NO_COMPARE_ERROR_SYS(5, "不参与误差比较");
|
||||
|
||||
private int value;
|
||||
private String msg;
|
||||
|
||||
ResultEnum(int value, String msg) {
|
||||
this.value = value;
|
||||
this.msg = msg;
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,8 @@ public enum SourceOperateCodeEnum {
|
||||
DATA_CHNFACTOR$01("DATA_CHNFACTOR$01","校验系数获取"),
|
||||
DATA_CHNFACTOR$02("DATA_CHNFACTOR$02","校验系数下发"),
|
||||
|
||||
VERIFY_MAPPING$01("VERIFY_MAPPING$01","脚本与icd校验"),
|
||||
|
||||
|
||||
/**
|
||||
* 请求操作类型对应实体中 requestId
|
||||
@@ -46,17 +48,26 @@ public enum SourceOperateCodeEnum {
|
||||
YJC_SBTXJY("yjc_sbtxjy", "设备通讯检测"),
|
||||
YJC_XYJY("yjc_xyjy", "协议校验"),
|
||||
YJC_XUJY("YJC_xujy", "相序校验"),
|
||||
YJC_ALIGN("yjc_align","实时数据对齐校验"),
|
||||
YJC_MXYZXJY("yjc_mxyzxjy", "模型一致性校验"),
|
||||
FORMAL_REAL("formal_real","正式检测"),
|
||||
RECORD_WAVE_STEP1("record_wave_step1","启动录波_step1"),
|
||||
// RECORD_WAVE_STEP2("record_wave_step2","启动录波_step2"),
|
||||
// SIMULATE_REAL("simulate_real","模拟检测"),
|
||||
Coefficient_Check("Coefficient_Check","系数校验"),
|
||||
QUITE("quit","关闭设备通讯初始化"),
|
||||
QUITE_SOURCE("close_source","关闭源通讯"),
|
||||
ERROR_FLOW_END("error_flow_end","当前流程存在异常结束"),
|
||||
YXT("yxt","心跳"),
|
||||
REPORT_CAT("FTP_SEND$01","处理报告"),
|
||||
|
||||
UNKNOWN_OPERATE("unknown_operate","未知的操作返回,请联系管理员排查"),
|
||||
SOCKET_TIMEOUT("socket_timeout","与源或者装置通讯等待超时"),
|
||||
STOP_TIMEOUT("stop_timeout","暂停时间超过十分钟"),
|
||||
SERVER_ERROR("server_error","服务端主动关闭连接,请稍后再试"),
|
||||
DEVICE_ERROR("device_error","设备主动关闭连接,请稍后再试"),
|
||||
|
||||
FLICKER_DATA_CHECK("flicker_data_check","闪变数据校验"),
|
||||
|
||||
|
||||
|
||||
@@ -65,16 +76,17 @@ public enum SourceOperateCodeEnum {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
COEFFICIENT_TEST("0","系数校验"),
|
||||
PRE_TEST("1","预检测"),
|
||||
FORMAL_TEST("2","正式检测"),
|
||||
TIME_TEST("3","守时检测"),
|
||||
PHASE_TEST("4","相序检测"),
|
||||
TEST_TEM_STOP("5","临时停止"),
|
||||
TEST_TEM_START("6","继续检测"),
|
||||
TEST_STOP("7","停止检测"),
|
||||
RE_ERROR_TEST("8","不合格项复检"),
|
||||
|
||||
// COEFFICIENT_TEST("0","系数校验"),
|
||||
// PRE_TEST("1","预检测"),
|
||||
// FORMAL_TEST("2","正式检测"),
|
||||
// TIME_TEST("3","守时检测"),
|
||||
// PHASE_TEST("4","相序检测"),
|
||||
// TEST_TEM_STOP("5","临时停止"),
|
||||
ALL_TEST("1","全部检测"),
|
||||
RE_ERROR_TEST("2","不合格项复检"),
|
||||
SIMULATE_TEST("4","模拟检测"),
|
||||
//TEST_STOP("7","停止检测"),
|
||||
//FAST_TEST("10","一键检测"),
|
||||
|
||||
/**
|
||||
* 系数校验步骤
|
||||
@@ -89,8 +101,11 @@ public enum SourceOperateCodeEnum {
|
||||
small_comp_end("small_comp_end","小电压校准结束"),
|
||||
|
||||
|
||||
|
||||
;
|
||||
/**
|
||||
* ftp文件传送指令
|
||||
*/
|
||||
FTP_SEND_01("FTP_SEND$01", "发送文件"),
|
||||
RDRE$01("RDRE$01", "启动录波");
|
||||
|
||||
private final String value;
|
||||
private final String msg;
|
||||
|
||||
@@ -13,6 +13,8 @@ public enum SourceResponseCodeEnum {
|
||||
SUCCESS(10200, "请求成功"),
|
||||
UNPROCESSED_BUSINESS(10201, "立即响应,业务还未处理,类似肯定应答"),
|
||||
NORMAL_RESPONSE(10202, "正常响应中间状态码"),
|
||||
ICD_NOT_FOUND(10500, "未找到对应ICD"),
|
||||
RECORD_WAVE_FAILED(10501, "录波失败"),
|
||||
MESSAGE_PARSING_ERROR(10520, "报文解析有误"),
|
||||
CONTROLLED_SOURCE_ERROR(10521, "程控源参数有误"),
|
||||
TEST_ITEM_PARSING_ERROR(10522, "测试项解析有误"),
|
||||
@@ -36,10 +38,13 @@ public enum SourceResponseCodeEnum {
|
||||
|
||||
//自定义前端展示消息
|
||||
SOCKET_ERROR(25000,"服务端连接失败"),
|
||||
DEV_COMM_ALL_SUCCESS(25001,"校验成功"),
|
||||
DEV_COMM_TEST_FAIL(25002,"设备通讯校验失败"),
|
||||
PHASE_CHECK_FAIL(25003,"相序校验未通过"),
|
||||
|
||||
ALL_SUCCESS(25001,"校验成功"),
|
||||
FAIL(25002,"失败"),
|
||||
ALL_FAIL(25003,"校验失败"),
|
||||
RECEIVE_DATA_TIME_OUT(25004,"接收数据超时"),
|
||||
REAL_DATA_CHECK_FAIL(25005,"实时数据不符合"),
|
||||
STATISTICS_DATA_CHECK_FAIL(25006,"统计数据不符合"),
|
||||
FLICKER_DATA_START(25007,"开始接收闪变数据")
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.njcn.gather.detection.pojo.param;
|
||||
|
||||
import com.njcn.gather.pojo.constant.DetectionValidMessage;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2025-07-18
|
||||
*/
|
||||
@Data
|
||||
public class ContrastDetectionParam {
|
||||
|
||||
@ApiModelProperty("检测计划ID")
|
||||
@NotBlank(message = DetectionValidMessage.PLAN_ID_NOT_BLANK)
|
||||
private String planId;
|
||||
|
||||
@ApiModelProperty("用户ID,唯一标识")
|
||||
private String loginName;
|
||||
|
||||
@ApiModelProperty("被检设备ID列表")
|
||||
@NotEmpty(message = DetectionValidMessage.DEV_IDS_NOT_EMPTY)
|
||||
private List<String> devIds;
|
||||
|
||||
@ApiModelProperty("标准设备ID列表")
|
||||
@NotEmpty(message = DetectionValidMessage.STANDARD_DEV_IDS_NOT_EMPTY)
|
||||
private List<String> standardDevIds;
|
||||
|
||||
/**
|
||||
* key为被检设备ID_检测点序号、value为 标准设备ID_检测点序号
|
||||
*/
|
||||
@ApiModelProperty("配对关系")
|
||||
@NotEmpty(message = DetectionValidMessage.PAIRS_NOT_EMPTY)
|
||||
private Map<String, String> pairs;
|
||||
/**
|
||||
* 检测项列表。第一个元素为预检测、第二个元素为系数校准、第三个元素为正式检测
|
||||
*/
|
||||
private List<Boolean> testItemList;
|
||||
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 是否进行相许校验
|
||||
*/
|
||||
private Integer phaseCheck;
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import lombok.Data;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author wr
|
||||
@@ -14,10 +15,8 @@ import java.util.List;
|
||||
@Data
|
||||
public class PreDetectionParam {
|
||||
|
||||
/**
|
||||
* 操作类型 0.预检测 1.正式检测 2.系数校验
|
||||
*/
|
||||
private String operateType = "0";
|
||||
// "1"-"全部检测" , "2"-"不合格项复检"
|
||||
private String reCheckType;
|
||||
|
||||
/**
|
||||
* 检测计划id
|
||||
@@ -25,15 +24,6 @@ public class PreDetectionParam {
|
||||
@NotBlank(message = "计划id不可为空")
|
||||
private String planId;
|
||||
|
||||
/**
|
||||
* 检测计划code
|
||||
*/
|
||||
private String planCode;
|
||||
|
||||
/**
|
||||
* 数字、模拟、比对
|
||||
*/
|
||||
private String pattern;
|
||||
|
||||
/**
|
||||
* 用户功能组成唯一标识 zhangsan_test
|
||||
@@ -56,6 +46,11 @@ public class PreDetectionParam {
|
||||
*/
|
||||
private String sourceId;
|
||||
|
||||
/**
|
||||
* 源名称
|
||||
*/
|
||||
private String sourceName;
|
||||
|
||||
/**
|
||||
* 所属误差体系
|
||||
*/
|
||||
@@ -65,4 +60,23 @@ public class PreDetectionParam {
|
||||
* 自动生成,用于生成数据表后缀
|
||||
*/
|
||||
private String code;
|
||||
|
||||
private Boolean sendWebMsg;
|
||||
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 温度
|
||||
*/
|
||||
private Float temperature;
|
||||
|
||||
/**
|
||||
* 相对湿度
|
||||
*/
|
||||
private Float humidity;
|
||||
|
||||
/**
|
||||
* 检测项列表。第一个元素为预检测、第二个元素为系数校准、第三个元素为正式检测
|
||||
*/
|
||||
private List<Boolean> testItemList;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.njcn.gather.detection.pojo.param;
|
||||
|
||||
import com.njcn.gather.pojo.constant.DetectionValidMessage;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2025-03-06
|
||||
*/
|
||||
@Data
|
||||
public class SimulateDetectionParam {
|
||||
|
||||
/**
|
||||
* 操作类型 9.模拟检测
|
||||
*/
|
||||
private String operateType = "9";
|
||||
|
||||
|
||||
/**
|
||||
* 用户功能组成唯一标识 zhangsan_test
|
||||
*/
|
||||
private String userPageId;
|
||||
|
||||
/**
|
||||
* 检测脚本Id
|
||||
*/
|
||||
private String scriptId;
|
||||
|
||||
@NotNull(message = DetectionValidMessage.INDEX_NOT_NULL)
|
||||
private Integer scriptIndex;
|
||||
|
||||
/**
|
||||
* 源id
|
||||
*/
|
||||
@NotBlank(message = DetectionValidMessage.SOURCE_ID_NOT_BLANK)
|
||||
private String sourceId;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.njcn.gather.detection.pojo.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2025-08-18
|
||||
*/
|
||||
@Data
|
||||
@TableName("ad_pair")
|
||||
public class AdPair implements Serializable {
|
||||
private String id;
|
||||
|
||||
private String planId;
|
||||
|
||||
private Integer num;
|
||||
|
||||
private String devMonitorId;
|
||||
|
||||
private String stdDevMonitorId;
|
||||
}
|
||||
@@ -18,6 +18,9 @@ public class DevData {
|
||||
@JSONField(name = "Time", ordinal = 1)
|
||||
private String time;
|
||||
|
||||
/**
|
||||
* 设备ip_通道号 这种形式
|
||||
*/
|
||||
@JSONField(name = "ID", ordinal = 2)
|
||||
private String id;
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.njcn.gather.detection.pojo.po;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2025-04-10
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public class IcdCheckData {
|
||||
|
||||
private String icdType;
|
||||
|
||||
private List<ResultData> resultData;
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class ResultData {
|
||||
private String type;
|
||||
|
||||
private String desc;
|
||||
|
||||
private DevData.SqlDataDTO.ListDTO phaseResult;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.njcn.gather.detection.pojo.vo;
|
||||
|
||||
import cn.afterturn.easypoi.excel.annotation.Excel;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2025-08-07
|
||||
*/
|
||||
@Data
|
||||
public class AlignDataExcel {
|
||||
|
||||
|
||||
@Excel(name = "时间", orderNum = "1", width = 40, groupName = "标准设备")
|
||||
private String timeStdDev;
|
||||
|
||||
@Excel(name = "Ua", orderNum = "2", groupName = "标准设备")
|
||||
private Double uaStdDev;
|
||||
|
||||
@Excel(name = "Ub", orderNum = "3", groupName = "标准设备")
|
||||
private Double ubStdDev;
|
||||
|
||||
@Excel(name = "Uc", orderNum = "4", groupName = "标准设备")
|
||||
private Double ucStdDev;
|
||||
|
||||
@Excel(name = "时间", orderNum = "5", width = 40, groupName = "被检设备")
|
||||
private String timeDev;
|
||||
|
||||
@Excel(name = "Ua", orderNum = "6", groupName = "被检设备")
|
||||
private Double uaDev;
|
||||
|
||||
@Excel(name = "Ub", orderNum = "7", groupName = "被检设备")
|
||||
private Double ubDev;
|
||||
|
||||
@Excel(name = "Uc", orderNum = "8", groupName = "被检设备")
|
||||
private Double ucDev;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.njcn.gather.detection.pojo.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2025-08-06
|
||||
*/
|
||||
@Data
|
||||
public class AlignDataVO {
|
||||
|
||||
private String stdDevName;
|
||||
|
||||
private List<ChannelData> channelDataList;
|
||||
|
||||
|
||||
// 通道数据
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class ChannelData {
|
||||
|
||||
// 标准设备通道号
|
||||
private String stdDevNum;
|
||||
|
||||
// 与之对应的被检设备名称_通道号
|
||||
private String devInfo;
|
||||
|
||||
// 数据
|
||||
private List<RawData> dataList;
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class RawData {
|
||||
private String timeDev;
|
||||
|
||||
private Double uaDev;
|
||||
|
||||
private Double ubDev;
|
||||
|
||||
private Double ucDev;
|
||||
|
||||
private Double utDev;
|
||||
|
||||
private String timeStdDev;
|
||||
|
||||
private Double uaStdDev;
|
||||
|
||||
private Double ubStdDev;
|
||||
|
||||
private Double ucStdDev;
|
||||
|
||||
private Double utStdDev;
|
||||
|
||||
private String unit;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package com.njcn.gather.detection.pojo.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* @author wr
|
||||
* @description
|
||||
@@ -18,7 +20,7 @@ public class DetectionData {
|
||||
private Double num;
|
||||
|
||||
/**
|
||||
* 是否是符合数据
|
||||
* 1.合格 2.不合格 3.网络超时 4.无法处理 5.不参与误差比较
|
||||
*/
|
||||
private Integer isData;
|
||||
|
||||
@@ -37,4 +39,23 @@ public class DetectionData {
|
||||
*/
|
||||
private String radius;
|
||||
|
||||
/**
|
||||
* 误差值
|
||||
*/
|
||||
private BigDecimal errorData;
|
||||
|
||||
/**
|
||||
* 单位
|
||||
*/
|
||||
private String unit;
|
||||
|
||||
/**
|
||||
* 误差体系详情ID(比对式使用)
|
||||
*/
|
||||
private String errorDtlId;
|
||||
|
||||
/**
|
||||
* 有效组数
|
||||
*/
|
||||
private Integer validGroupNum;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
package com.njcn.gather.detection.pojo.vo;
|
||||
|
||||
import io.swagger.models.auth.In;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author: cdf
|
||||
* @CreateTime: 2024-12-26
|
||||
@@ -13,6 +10,11 @@ import java.util.List;
|
||||
@Data
|
||||
public class DevLineTestResult {
|
||||
|
||||
/**
|
||||
* 检测项code
|
||||
*/
|
||||
private String scriptName;
|
||||
|
||||
private String deviceId;
|
||||
|
||||
private String deviceName;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.njcn.gather.detection.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.njcn.gather.detection.pojo.po.AdPair;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2025-08-18
|
||||
*/
|
||||
public interface IAdPariService extends IService<AdPair> {
|
||||
|
||||
/**
|
||||
* 获取最大的检测次数
|
||||
*
|
||||
* @param devMonitorId
|
||||
* @return
|
||||
*/
|
||||
Integer getMaxNum(String devMonitorId);
|
||||
|
||||
/**
|
||||
* 根据设备id查询配对关系
|
||||
*
|
||||
* @param devIds
|
||||
* @return
|
||||
*/
|
||||
List<AdPair> listByDevIds(List<String> devIds);
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
package com.njcn.gather.detection.service;
|
||||
|
||||
import com.njcn.gather.detection.pojo.param.ContrastDetectionParam;
|
||||
import com.njcn.gather.detection.pojo.param.PreDetectionParam;
|
||||
import com.njcn.gather.detection.pojo.param.SimulateDetectionParam;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
@@ -13,7 +17,8 @@ public interface PreDetectionService {
|
||||
|
||||
/**
|
||||
* 源通讯校验socket入参拼接
|
||||
* @param param
|
||||
*
|
||||
* @param param
|
||||
* @Author: wr
|
||||
* @Date: 2024/12/11 13:26
|
||||
*/
|
||||
@@ -23,14 +28,44 @@ public interface PreDetectionService {
|
||||
void coefficientCheck(PreDetectionParam param);
|
||||
|
||||
|
||||
boolean startTest(PreDetectionParam param);
|
||||
|
||||
|
||||
boolean temStopTest();
|
||||
|
||||
|
||||
boolean restartTemTest(PreDetectionParam param);
|
||||
|
||||
/**
|
||||
* 模拟测试-源通讯校验
|
||||
*
|
||||
* @param param
|
||||
*/
|
||||
void ytxCheckSimulate(SimulateDetectionParam param);
|
||||
|
||||
/**
|
||||
* 模拟测试-向源发送脚本
|
||||
*
|
||||
* @param param
|
||||
*/
|
||||
void sendScript(SimulateDetectionParam param);
|
||||
|
||||
/**
|
||||
* 模拟测试-停止
|
||||
*
|
||||
* @param param
|
||||
*/
|
||||
void closeTestSimulate(SimulateDetectionParam param);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param param
|
||||
*/
|
||||
void startContrastTest(ContrastDetectionParam param);
|
||||
|
||||
/**
|
||||
* 导出实时数据对齐过程中的数据
|
||||
*/
|
||||
void exportAlignData();
|
||||
|
||||
boolean getCanCoefficient();
|
||||
|
||||
void startCoefficient();
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
package com.njcn.gather.detection.service;
|
||||
|
||||
/**
|
||||
* @Description: 守时检测流程
|
||||
* @Author: wr
|
||||
* @Date: 2024/12/10 14:23
|
||||
*/
|
||||
public interface PunctualityService {
|
||||
|
||||
/**
|
||||
* 触发时间标识
|
||||
*/
|
||||
void triggerTimeMark();
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.njcn.gather.detection.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.njcn.gather.detection.mapper.AdPairMapper;
|
||||
import com.njcn.gather.detection.pojo.po.AdPair;
|
||||
import com.njcn.gather.detection.service.IAdPariService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2025-08-18
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AdPairServiceImpl extends ServiceImpl<AdPairMapper, AdPair> implements IAdPariService {
|
||||
|
||||
@Override
|
||||
public Integer getMaxNum(String devMonitorId) {
|
||||
List<AdPair> adPairList = this.lambdaQuery().select(AdPair::getNum)
|
||||
.eq(AdPair::getDevMonitorId, devMonitorId)
|
||||
.orderByDesc(AdPair::getNum)
|
||||
.last("LIMIT 1").list();
|
||||
if (CollUtil.isNotEmpty(adPairList)) {
|
||||
return adPairList.get(0).getNum();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AdPair> listByDevIds(List<String> devIds) {
|
||||
if (CollUtil.isNotEmpty(devIds)) {
|
||||
QueryWrapper<AdPair> wrapper = new QueryWrapper<>();
|
||||
devIds.forEach(devId -> wrapper.likeRight("ad_pair.Dev_Monitor_Id", devId));
|
||||
return this.list(wrapper);
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,51 +6,68 @@ import cn.hutool.core.util.ObjectUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.gather.detection.handler.SocketContrastResponseService;
|
||||
import com.njcn.gather.detection.handler.SocketDevResponseService;
|
||||
import com.njcn.gather.detection.handler.SocketSourceResponseService;
|
||||
import com.njcn.gather.detection.pojo.constant.DetectionCommunicateConstant;
|
||||
import com.njcn.gather.detection.pojo.enums.DetectionResponseEnum;
|
||||
import com.njcn.gather.detection.pojo.enums.SourceOperateCodeEnum;
|
||||
import com.njcn.gather.detection.pojo.param.ContrastDetectionParam;
|
||||
import com.njcn.gather.detection.pojo.param.PreDetectionParam;
|
||||
import com.njcn.gather.detection.pojo.param.SimulateDetectionParam;
|
||||
import com.njcn.gather.detection.pojo.vo.SocketMsg;
|
||||
import com.njcn.gather.detection.service.PreDetectionService;
|
||||
|
||||
import com.njcn.gather.detection.util.business.DetectionCommunicateUtil;
|
||||
import com.njcn.gather.detection.util.socket.CnSocketUtil;
|
||||
import com.njcn.gather.detection.util.socket.FormalTestManager;
|
||||
import com.njcn.gather.detection.util.socket.SocketManager;
|
||||
import com.njcn.gather.detection.util.socket.cilent.NettyClient;
|
||||
import com.njcn.gather.detection.util.socket.cilent.NettySourceClientHandler;
|
||||
import com.njcn.gather.device.pojo.enums.DevResponseEnum;
|
||||
import com.njcn.gather.detection.util.socket.XiNumberManager;
|
||||
import com.njcn.gather.detection.util.socket.cilent.NettyContrastClientHandler;
|
||||
import com.njcn.gather.detection.util.socket.websocket.WebServiceManager;
|
||||
import com.njcn.gather.device.pojo.po.PqDev;
|
||||
import com.njcn.gather.device.pojo.vo.PreDetection;
|
||||
import com.njcn.gather.device.service.IPqDevService;
|
||||
import com.njcn.gather.plan.pojo.enums.DataSourceEnum;
|
||||
import com.njcn.gather.plan.pojo.po.AdPlan;
|
||||
import com.njcn.gather.plan.pojo.po.AdPlanSource;
|
||||
import com.njcn.gather.plan.service.IAdPlanService;
|
||||
import com.njcn.gather.plan.service.IAdPlanSourceService;
|
||||
import com.njcn.gather.result.pojo.enums.ResultUnitEnum;
|
||||
import com.njcn.gather.script.pojo.param.PqScriptCheckDataParam;
|
||||
import com.njcn.gather.script.pojo.param.PqScriptIssueParam;
|
||||
import com.njcn.gather.script.pojo.po.SourceIssue;
|
||||
import com.njcn.gather.script.service.IPqScriptCheckDataService;
|
||||
import com.njcn.gather.script.service.IPqScriptDtlsService;
|
||||
import com.njcn.gather.source.pojo.po.SourceInitialize;
|
||||
import com.njcn.gather.source.service.IPqSourceService;
|
||||
import com.njcn.gather.plan.pojo.po.AdPlan;
|
||||
import com.njcn.gather.plan.pojo.po.AdPlanSource;
|
||||
import com.njcn.gather.plan.service.IAdPlanService;
|
||||
import com.njcn.gather.plan.service.IAdPlanSourceService;
|
||||
import com.njcn.gather.system.cfg.service.ISysTestConfigService;
|
||||
import com.njcn.gather.system.config.PathConfig;
|
||||
import com.njcn.gather.system.dictionary.pojo.enums.DictDataEnum;
|
||||
import com.njcn.gather.system.dictionary.service.IDictDataService;
|
||||
import com.njcn.web.utils.HttpServletUtil;
|
||||
import com.njcn.web.utils.RequestUtil;
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class PreDetectionServiceImpl implements PreDetectionService {
|
||||
|
||||
private final String source = "_Source";
|
||||
private final String dev = "_Dev";
|
||||
|
||||
private final IPqDevService iPqDevService;
|
||||
private final IDictDataService dictDataService;
|
||||
private final IAdPlanService iAdPlanService;
|
||||
@@ -60,52 +77,68 @@ public class PreDetectionServiceImpl implements PreDetectionService {
|
||||
|
||||
private final SocketDevResponseService socketDevResponseService;
|
||||
private final SocketSourceResponseService socketSourceResponseService;
|
||||
private final SocketContrastResponseService socketContrastResponseService;
|
||||
private final IPqScriptCheckDataService iPqScriptCheckDataService;
|
||||
private final SocketManager socketManager;
|
||||
private final ISysTestConfigService sysTestConfigService;
|
||||
|
||||
@Value("${socket.source.ip:192.168.1.136}")
|
||||
private String ip;
|
||||
|
||||
@Value("${socket.source.port:10086}")
|
||||
private Integer port;
|
||||
|
||||
private final SocketSourceResponseService sourceResponseService;
|
||||
// @Value("${report.reportDir}")
|
||||
// private String alignDataFilePath;
|
||||
private final PathConfig pathConfig;
|
||||
|
||||
|
||||
@Override
|
||||
public void sourceCommunicationCheck(PreDetectionParam param) {
|
||||
commCheck(param);
|
||||
specialDeal(param);
|
||||
// 参数校验,目前仅检查IP是否重复,后续可在里面扩展
|
||||
checkDevIp(param.getDevIds());
|
||||
//用于处理异常导致的socket通道未关闭,socket交互异常
|
||||
DetectionCommunicateUtil.checkCommunicateChannel(param);
|
||||
/*
|
||||
先组装源通讯协议
|
||||
查询计划什么模式的(除了对比式,其他都是一个计划对应一个源)
|
||||
*/
|
||||
* 先组装源通讯协议
|
||||
* 查询计划什么模式的(除了对比式,其他都是一个计划对应一个源)
|
||||
*/
|
||||
AdPlan plan = iAdPlanService.getById(param.getPlanId());
|
||||
param.setScriptId(plan.getScriptId());
|
||||
param.setErrorSysId(plan.getErrorSysId());
|
||||
param.setCode(String.valueOf(plan.getCode()));
|
||||
if (ObjectUtil.isNotNull(plan)) {
|
||||
String code = dictDataService.getDictDataById(plan.getPattern()).getCode();
|
||||
DictDataEnum dictDataEnumByCode = DictDataEnum.getDictDataEnumByCode(code);
|
||||
switch (dictDataEnumByCode) {
|
||||
case DIGITAL:
|
||||
case SIMULATE:
|
||||
sendYtxSocket(param);
|
||||
break;
|
||||
case CONTRAST:
|
||||
break;
|
||||
default:
|
||||
throw new BusinessException(DetectionResponseEnum.PLAN_PATTERN_NOT);
|
||||
if (Objects.nonNull(dictDataEnumByCode)) {
|
||||
switch (dictDataEnumByCode) {
|
||||
case DIGITAL:
|
||||
case SIMULATE:
|
||||
sendYtxSocket(param);
|
||||
break;
|
||||
case CONTRAST:
|
||||
break;
|
||||
default:
|
||||
throw new BusinessException(DetectionResponseEnum.PLAN_PATTERN_NOT);
|
||||
}
|
||||
} else {
|
||||
throw new BusinessException(DetectionResponseEnum.PLAN_PATTERN_NOT);
|
||||
}
|
||||
} else {
|
||||
throw new BusinessException(DetectionResponseEnum.PLAN_NOT_EXIST);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 原本系数校准单独写的,现在合并了,该方法过期了,没有调用了
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
public void coefficientCheck(PreDetectionParam param) {
|
||||
specialDeal(param);
|
||||
// 检测是否存在连接的通道,后期需要做成动态,如果组合中不是第一位,则不需要关闭,也不用初始化 todo....
|
||||
DetectionCommunicateUtil.checkCommunicateChannel(param);
|
||||
AdPlanSource planSource = adPlanSourceService.getOne(new LambdaQueryWrapper<AdPlanSource>()
|
||||
.eq(AdPlanSource::getPlanId, param.getPlanId())
|
||||
);
|
||||
if (ObjectUtil.isNotNull(planSource)) {
|
||||
SourceInitialize sourceParam = pqSourceService.getSourceInitializeParam(planSource.getSourceId());
|
||||
param.setSourceName(sourceParam.getSourceId());
|
||||
if (ObjectUtil.isNotNull(sourceParam)) {
|
||||
//开始组装socket报文请求头
|
||||
socketDevResponseService.initList(param);
|
||||
@@ -115,7 +148,8 @@ public class PreDetectionServiceImpl implements PreDetectionService {
|
||||
msg.setOperateCode(SourceOperateCodeEnum.INIT_GATHER.getValue());
|
||||
msg.setData(JSON.toJSONString(sourceParam));
|
||||
param.setSourceId(sourceParam.getSourceId());
|
||||
NettyClient.socketClient(ip, port, param, JSON.toJSONString(msg), new NettySourceClientHandler(param, sourceResponseService));
|
||||
// 使用智能发送工具类,自动管理连接
|
||||
socketManager.smartSendToSource(param, JSON.toJSONString(msg));
|
||||
} else {
|
||||
throw new BusinessException(DetectionResponseEnum.SOURCE_INFO_NOT);
|
||||
}
|
||||
@@ -125,25 +159,43 @@ public class PreDetectionServiceImpl implements PreDetectionService {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 发送源通信校验Socket连接(数字式和模拟式检测模式)
|
||||
*
|
||||
* <p>该方法用于建立与程控源设备的Socket连接,进行源通信校验。主要流程:</p>
|
||||
* <ul>
|
||||
* <li>1. 存储检测参数到全局管理器</li>
|
||||
* <li>2. 根据计划ID获取计划源信息</li>
|
||||
* <li>3. 获取源设备初始化参数</li>
|
||||
* <li>4. 初始化设备和源响应服务列表</li>
|
||||
* <li>5. 组装Socket请求报文</li>
|
||||
* <li>6. 建立Netty客户端连接</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param param 预检测参数,包含计划ID、用户ID等信息
|
||||
* @throws BusinessException 当计划源信息不存在或源初始化参数为空时抛出
|
||||
* @see SourceOperateCodeEnum#YJC_YTXJY 源通信校验操作码
|
||||
* @see SourceOperateCodeEnum#INIT_GATHER 初始化采集操作码
|
||||
*/
|
||||
private void sendYtxSocket(PreDetectionParam param) {
|
||||
AdPlan plan = iAdPlanService.getById(param.getPlanId());
|
||||
AdPlanSource planSource = adPlanSourceService.getOne(new LambdaQueryWrapper<AdPlanSource>().eq(AdPlanSource::getPlanId, param.getPlanId()));
|
||||
param.setSourceId(planSource.getSourceId());
|
||||
param.setScriptId(plan.getScriptId());
|
||||
param.setErrorSysId(plan.getErrorSysId());
|
||||
param.setCode(String.valueOf(plan.getCode()));
|
||||
String loginName = RequestUtil.getLoginNameByToken();
|
||||
WebServiceManager.addPreDetectionParam(loginName, param);
|
||||
if (ObjectUtil.isNotNull(planSource)) {
|
||||
//获取源初始化参数
|
||||
SourceInitialize sourceParam = pqSourceService.getSourceInitializeParam(planSource.getSourceId());
|
||||
param.setSourceName(sourceParam.getSourceId());
|
||||
if (ObjectUtil.isNotNull(sourceParam)) {
|
||||
//开始组装socket报文请求头
|
||||
socketDevResponseService.initList(param);
|
||||
socketSourceResponseService.initList(param);
|
||||
SocketMsg<String> msg = new SocketMsg<>();
|
||||
msg.setRequestId(SourceOperateCodeEnum.YJC_YTXJY.getValue());
|
||||
msg.setOperateCode(SourceOperateCodeEnum.INIT_GATHER.getValue());
|
||||
msg.setData(JSON.toJSONString(sourceParam));
|
||||
|
||||
NettyClient.socketClient(ip, port, param, JSON.toJSONString(msg), new NettySourceClientHandler(param, sourceResponseService));
|
||||
SocketMsg<String> socketMsg = new SocketMsg<>();
|
||||
socketMsg.setRequestId(SourceOperateCodeEnum.YJC_YTXJY.getValue());
|
||||
socketMsg.setOperateCode(SourceOperateCodeEnum.INIT_GATHER.getValue());
|
||||
socketMsg.setData(JSON.toJSONString(sourceParam));
|
||||
//使用智能发送工具类,自动管理与源控程序的socket连接
|
||||
socketManager.smartSendToSource(param, JSON.toJSONString(socketMsg));
|
||||
} else {
|
||||
throw new BusinessException(DetectionResponseEnum.SOURCE_INFO_NOT);
|
||||
}
|
||||
@@ -152,13 +204,37 @@ public class PreDetectionServiceImpl implements PreDetectionService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean startTest(PreDetectionParam param) {
|
||||
socketDevResponseService.initList(param);
|
||||
NettyClient.socketClient(ip, port, param, "start\n", new NettySourceClientHandler(param, sourceResponseService));
|
||||
|
||||
return true;
|
||||
/**
|
||||
* 发送源通信校验Socket连接(仿真模式)
|
||||
*
|
||||
* <p>该方法专门用于仿真检测模式下的源通信校验。与普通模式的区别:</p>
|
||||
* <ul>
|
||||
* <li>直接使用传入的sourceId获取源初始化参数</li>
|
||||
* <li>不需要通过计划ID查询计划源信息</li>
|
||||
* <li>适用于独立的源设备通信测试</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param param 预检测参数,必须包含sourceId和userPageId
|
||||
* @throws BusinessException 当源初始化参数为空时抛出
|
||||
* @see #sendYtxSocket(PreDetectionParam) 普通检测模式的源通信校验
|
||||
* @see SourceOperateCodeEnum#YJC_YTXJY 源通信校验操作码
|
||||
* @see SourceOperateCodeEnum#INIT_GATHER 初始化采集操作码
|
||||
*/
|
||||
private void sendYtxSocketSimulate(PreDetectionParam param) {
|
||||
SourceInitialize sourceParam = pqSourceService.getSourceInitializeParam(param.getSourceId());
|
||||
param.setSourceId(sourceParam.getSourceId());
|
||||
String loginName = RequestUtil.getLoginNameByToken();
|
||||
WebServiceManager.addPreDetectionParam(loginName, param);
|
||||
if (ObjectUtil.isNotNull(sourceParam)) {
|
||||
SocketMsg<String> socketMsg = new SocketMsg<>();
|
||||
socketMsg.setRequestId(SourceOperateCodeEnum.YJC_YTXJY.getValue());
|
||||
socketMsg.setOperateCode(SourceOperateCodeEnum.INIT_GATHER.getValue());
|
||||
socketMsg.setData(JSON.toJSONString(sourceParam));
|
||||
// 使用智能发送工具类,自动管理连接
|
||||
socketManager.smartSendToSource(param, JSON.toJSONString(socketMsg));
|
||||
} else {
|
||||
throw new BusinessException(DetectionResponseEnum.SOURCE_INFO_NOT);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -170,63 +246,215 @@ public class PreDetectionServiceImpl implements PreDetectionService {
|
||||
@Override
|
||||
public boolean restartTemTest(PreDetectionParam param) {
|
||||
FormalTestManager.stopFlag = false;
|
||||
socketDevResponseService.initRestart();
|
||||
List<SourceIssue> sourceIssueList = SocketManager.getSourceList();
|
||||
SourceInitialize sourceInitialize = pqSourceService.getSourceInitializeParam(param.getSourceId());
|
||||
param.setSourceName(sourceInitialize.getSourceId());
|
||||
if (CollUtil.isNotEmpty(sourceIssueList)) {
|
||||
SourceIssue sourceIssues = SocketManager.getSourceList().get(0);
|
||||
SocketMsg<String> xuMsg = new SocketMsg<>();
|
||||
xuMsg.setOperateCode(SourceOperateCodeEnum.OPER_GATHER.getValue());
|
||||
xuMsg.setData(JSON.toJSONString(sourceIssues));
|
||||
xuMsg.setRequestId(SourceOperateCodeEnum.FORMAL_REAL.getValue() + "&&" + sourceIssues.getType());
|
||||
SocketManager.sendMsg(param.getUserPageId() + source, JSON.toJSONString(xuMsg));
|
||||
SocketManager.sendMsg(param.getUserPageId() + DetectionCommunicateConstant.SOURCE, JSON.toJSONString(xuMsg));
|
||||
// Resume_Success
|
||||
} else {
|
||||
//TODO 是否最终检测完成需要推送给用户
|
||||
PqScriptCheckDataParam checkDataParam=new PqScriptCheckDataParam();
|
||||
//TODO 是否最终检测完成需要推送给用户 检测完成
|
||||
PqScriptCheckDataParam checkDataParam = new PqScriptCheckDataParam();
|
||||
checkDataParam.setScriptId(param.getScriptId());
|
||||
checkDataParam.setIsValueTypeName(false);
|
||||
List<String> valueType = iPqScriptCheckDataService.getValueType(checkDataParam);
|
||||
List<String> adType = iPqScriptCheckDataService.getValueType(checkDataParam);
|
||||
|
||||
iPqDevService.updateResult(param.getDevIds(), valueType,param.getCode());
|
||||
iPqDevService.updateResult(param.getDevIds(), adType, param.getCode(), param.getUserId(), param.getTemperature(), param.getHumidity(), true);
|
||||
CnSocketUtil.quitSend(param);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ytxCheckSimulate(SimulateDetectionParam param) {
|
||||
PreDetectionParam preDetectionParam = new PreDetectionParam();
|
||||
preDetectionParam.setSourceId(param.getSourceId());
|
||||
preDetectionParam.setUserPageId(param.getUserPageId());
|
||||
preDetectionParam.setSendWebMsg(true);
|
||||
|
||||
/**
|
||||
* 对重复发起或者异常发起的检测进行关闭源操作
|
||||
* @param param
|
||||
*/
|
||||
private void specialDeal(PreDetectionParam param) {
|
||||
Channel channel = SocketManager.getChannelByUserId(param.getUserPageId() + source);
|
||||
if (Objects.nonNull(channel) && channel.isActive()) {
|
||||
System.out.println("进入关闭源。。//////");
|
||||
CnSocketUtil.quitSendSource(param);
|
||||
DetectionCommunicateUtil.checkCommunicateChannel(preDetectionParam);
|
||||
sendYtxSocketSimulate(preDetectionParam);
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
SocketManager.removeUser(param.getUserPageId() + source);
|
||||
SocketManager.removeUser(param.getUserPageId() + dev);
|
||||
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
@Override
|
||||
public void sendScript(SimulateDetectionParam param) {
|
||||
Channel channel = SocketManager.getChannelByUserId(param.getUserPageId() + DetectionCommunicateConstant.SOURCE);
|
||||
if (Objects.isNull(channel) || !channel.isActive()) {
|
||||
// 进行源通信连接
|
||||
PreDetectionParam preDetectionParam = new PreDetectionParam();
|
||||
preDetectionParam.setSourceId(param.getSourceId());
|
||||
preDetectionParam.setUserPageId(param.getUserPageId());
|
||||
preDetectionParam.setSendWebMsg(false);
|
||||
this.sendYtxSocketSimulate(preDetectionParam);
|
||||
}
|
||||
//组装源控制脚本
|
||||
PqScriptIssueParam issueParam = new PqScriptIssueParam();
|
||||
SourceInitialize sourceInitialize = pqSourceService.getSourceInitializeParam(param.getSourceId());
|
||||
issueParam.setSourceId(sourceInitialize.getSourceId());
|
||||
issueParam.setScriptId(param.getScriptId());
|
||||
issueParam.setType(1);
|
||||
issueParam.setIsPhaseSequence(SourceOperateCodeEnum.SIMULATE_TEST.getValue());
|
||||
|
||||
List<SourceIssue> sourceIssues = pqScriptDtlsService.listSourceIssue(issueParam);
|
||||
sourceIssues = sourceIssues.stream()
|
||||
.filter(s -> s.getIndex().equals(param.getScriptIndex()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
SourceIssue sourceIssue = sourceIssues.get(0);
|
||||
String type = sourceIssue.getType();
|
||||
if (sourceIssue.getIsP()) {
|
||||
sourceIssue.setType(ResultUnitEnum.V_ABSOLUTELY.getCode());
|
||||
type = ResultUnitEnum.P.getCode();
|
||||
}
|
||||
|
||||
List<String> comm = sourceIssue.getDevValueTypeList();
|
||||
System.out.println("向装置下发的参数ddd>>>>>>>>" + comm);
|
||||
|
||||
SocketMsg<String> socketMsg = new SocketMsg<>();
|
||||
socketMsg.setOperateCode(SourceOperateCodeEnum.OPER_GATHER.getValue());
|
||||
socketMsg.setRequestId(SourceOperateCodeEnum.FORMAL_REAL.getValue() + CnSocketUtil.STEP_TAG + type);
|
||||
socketMsg.setData(JSON.toJSONString(sourceIssues.get(0)));
|
||||
SocketManager.sendMsg(param.getUserPageId() + CnSocketUtil.SOURCE_TAG, JSON.toJSONString(socketMsg));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeTestSimulate(SimulateDetectionParam param) {
|
||||
Channel channel = SocketManager.getChannelByUserId(param.getUserPageId() + DetectionCommunicateConstant.SOURCE);
|
||||
if (Objects.isNull(channel) || !channel.isActive()) {
|
||||
throw new BusinessException(DetectionResponseEnum.SOURCE_NOT_CONNECT);
|
||||
}
|
||||
SourceInitialize sourceParam = pqSourceService.getSourceInitializeParam(param.getSourceId());
|
||||
PreDetectionParam preDetectionParam = new PreDetectionParam();
|
||||
preDetectionParam.setSourceId(sourceParam.getSourceId());
|
||||
preDetectionParam.setUserPageId(param.getUserPageId());
|
||||
CnSocketUtil.quitSendSource(preDetectionParam);
|
||||
WebServiceManager.removePreDetectionParam();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startContrastTest(ContrastDetectionParam param) {
|
||||
// 参数校验,目前仅检查IP是否重复,后续可在里面扩展
|
||||
checkDevIp(param.getDevIds());
|
||||
socketContrastResponseService.init(param);
|
||||
|
||||
// 和通信模块进行连接
|
||||
this.sendContrastSocket(param);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportAlignData() {
|
||||
String fileName = "对齐数据.xlsx";
|
||||
HttpServletResponse response = HttpServletUtil.getResponse();
|
||||
response.reset();
|
||||
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
|
||||
response.setContentType("application/octet-stream;charset=UTF-8");
|
||||
try {
|
||||
InputStream inputStream = new FileInputStream(pathConfig.getDataPath() + File.separator + fileName);
|
||||
byte[] buffer = new byte[1024];
|
||||
int len = 0;
|
||||
ServletOutputStream outputStream = response.getOutputStream();
|
||||
while ((len = inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, len);
|
||||
}
|
||||
outputStream.flush();
|
||||
outputStream.close();
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getCanCoefficient() {
|
||||
return CollUtil.isNotEmpty(DetectionServiceImpl.vAndIResultMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startCoefficient() {
|
||||
if (CollUtil.isNotEmpty(DetectionServiceImpl.vAndIResultMap)) {
|
||||
// 是否进行系数校准
|
||||
//万一录波了这里就进不来了,但是在误差处理的时候DetectionServiceImpl.vAndIResultMap该集合会保留不符合的结果
|
||||
|
||||
XiNumberManager.xiDevList.clear();
|
||||
XiNumberManager.devXiNumDataMap.clear();
|
||||
List<String> stdIpList = DetectionServiceImpl.vAndIResultMap.values().stream().map(DetectionServiceImpl.VAndIResult::getIp).collect(Collectors.toList());
|
||||
XiNumberManager.xiDevList = FormalTestManager.standardDevList.stream().filter(d -> stdIpList.contains(d.getDevIP())).collect(Collectors.toList());
|
||||
// 逐一设备向通讯模块要原始系数
|
||||
SocketMsg<String> socketMsg = new SocketMsg<>();
|
||||
socketMsg.setRequestId(SourceOperateCodeEnum.Coefficient_Check.getValue());
|
||||
socketMsg.setOperateCode(SourceOperateCodeEnum.DATA_CHNFACTOR$01.getValue());
|
||||
PreDetection preDetection = XiNumberManager.xiDevList.get(0);
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("devIP", preDetection.getDevIP());
|
||||
map.put("chnNum", preDetection.getDevChns());
|
||||
socketMsg.setData(JSON.toJSONString(map));
|
||||
SocketManager.sendMsg(NettyContrastClientHandler.param.getUserPageId() + CnSocketUtil.CONTRAST_DEV_TAG, JSON.toJSONString(socketMsg));
|
||||
|
||||
FormalTestManager.currentStep = SourceOperateCodeEnum.Coefficient_Check;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验
|
||||
* 比对式-与通信模块进行连接
|
||||
*
|
||||
* @param param
|
||||
*/
|
||||
private void commCheck(PreDetectionParam param){
|
||||
List<PqDev> pqDevList = iPqDevService.listByIds(param.getDevIds());
|
||||
private void sendContrastSocket(ContrastDetectionParam param) {
|
||||
Map<String, List<PreDetection>> map = new HashMap<>(1);
|
||||
List<PreDetection> preDetections = new ArrayList<>();
|
||||
preDetections.addAll(FormalTestManager.devList);
|
||||
preDetections.addAll(FormalTestManager.standardDevList);
|
||||
|
||||
preDetections.forEach(x -> {
|
||||
x.setDevType(x.getIcdType());
|
||||
x.getMonitorList().forEach(y -> {
|
||||
|
||||
String ptStr = y.getPtStr();
|
||||
int i = ptStr.indexOf(":");
|
||||
y.setPt(BigDecimal.valueOf(Double.parseDouble(ptStr.substring(0, i))).divide(BigDecimal.valueOf(Double.parseDouble(ptStr.substring(i + 1))), 5, BigDecimal.ROUND_HALF_UP).doubleValue());
|
||||
|
||||
String ctStr = y.getCtStr();
|
||||
i = ctStr.indexOf(":");
|
||||
y.setCt(BigDecimal.valueOf(Double.parseDouble(ctStr.substring(0, i))).divide(BigDecimal.valueOf(Double.parseDouble(ctStr.substring(i + 1))), 5, BigDecimal.ROUND_HALF_UP).doubleValue());
|
||||
});
|
||||
});
|
||||
map.put("deviceList", preDetections);
|
||||
String jsonString = JSON.toJSONString(map);
|
||||
SocketMsg<String> socketMsg = new SocketMsg<>();
|
||||
socketMsg.setRequestId(SourceOperateCodeEnum.YJC_SBTXJY.getValue());
|
||||
AdPlan currentTestPlan = FormalTestManager.currentTestPlan;
|
||||
String[] datasource = currentTestPlan.getDatasourceId().split(",");
|
||||
if (Arrays.stream(datasource).anyMatch(x -> x.equals(DataSourceEnum.REAL_DATA.getValue()))) {
|
||||
socketMsg.setOperateCode(SourceOperateCodeEnum.DEV_INIT_GATHER_02.getValue());
|
||||
} else if (Arrays.stream(datasource).anyMatch(x -> x.equals(DataSourceEnum.MINUTE_STATISTICS_AVG.getValue()) || x.equals(DataSourceEnum.MINUTE_STATISTICS_MAX.getValue()) || x.equals(DataSourceEnum.MINUTE_STATISTICS_MIN.getValue()) || x.equals(DataSourceEnum.MINUTE_STATISTICS_CP95.getValue()))) {
|
||||
socketMsg.setOperateCode(SourceOperateCodeEnum.DEV_INIT_GATHER_01.getValue());
|
||||
} else if (Arrays.stream(datasource).anyMatch(x -> x.equals(DataSourceEnum.WAVE_DATA.getValue()))) {
|
||||
socketMsg.setOperateCode(SourceOperateCodeEnum.DEV_INIT_GATHER_02.getValue());
|
||||
}
|
||||
socketMsg.setData(jsonString);
|
||||
PreDetectionParam preDetectionParam = new PreDetectionParam();
|
||||
preDetectionParam.setUserPageId(param.getLoginName());
|
||||
preDetectionParam.setTestItemList(param.getTestItemList());
|
||||
preDetectionParam.setDevIds(param.getDevIds());
|
||||
preDetectionParam.setUserId(param.getUserId());
|
||||
WebServiceManager.addPreDetectionParam(param.getLoginName(), preDetectionParam);
|
||||
socketManager.smartSendToContrast(param, JSON.toJSONString(socketMsg));
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验被检设备中是否存在IP重复的
|
||||
*/
|
||||
private void checkDevIp(List<String> devIds) {
|
||||
List<PqDev> pqDevList = iPqDevService.listByIds(devIds);
|
||||
List<String> ipList = pqDevList.stream().map(PqDev::getIp).distinct().collect(Collectors.toList());
|
||||
if(ipList.size()!=param.getDevIds().size()){
|
||||
if (ipList.size() != devIds.size()) {
|
||||
throw new BusinessException(DetectionResponseEnum.PLAN_DEV_IP_HAS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,531 @@
|
||||
package com.njcn.gather.detection.util;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.njcn.gather.detection.pojo.po.DevData;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.File;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 检测工具类
|
||||
* <p>
|
||||
* 提供电能质量检测相关的数据处理、时间转换、统计计算等工具方法。
|
||||
* 主要功能包括:
|
||||
* <ul>
|
||||
* <li>相角矫正和标准化</li>
|
||||
* <li>设备数据时间对齐判断</li>
|
||||
* <li>时间格式转换和毫秒数计算</li>
|
||||
* <li>数值零值判断</li>
|
||||
* <li>统计数据处理(CP95分位数、部分值、平均值等)</li>
|
||||
* <li>数据排序和索引计算</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author caozehui
|
||||
* @version 1.0
|
||||
* @since 2025-07-28
|
||||
*/
|
||||
@Slf4j
|
||||
public class DetectionUtil {
|
||||
/**
|
||||
* ISO 8601日期时间格式化器
|
||||
* 用于解析和格式化符合ISO 8601标准的日期时间字符串
|
||||
*/
|
||||
public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_DATE_TIME;
|
||||
|
||||
|
||||
/**
|
||||
* 毫秒转秒的转换因子
|
||||
*/
|
||||
private static final long MILLIS_TO_SECONDS = 1000L;
|
||||
|
||||
/**
|
||||
* 毫秒转分钟的转换因子
|
||||
*/
|
||||
private static final long MILLIS_TO_MINUTES = 1000 * 60L;
|
||||
|
||||
/**
|
||||
* 时间对齐判断的容差毫秒数
|
||||
* 当两个时间戳差值小于此值时,认为时间是对齐的
|
||||
*/
|
||||
private static final long TIME_ALIGNMENT_TOLERANCE_MS = 100L;
|
||||
|
||||
/**
|
||||
* 角度相关常量
|
||||
*/
|
||||
private static final double ANGLE_180 = 180.0;
|
||||
private static final double ANGLE_360 = 360.0;
|
||||
private static final double ANGLE_MINUS_180 = -180.0;
|
||||
|
||||
/**
|
||||
* CP95算法相关常量
|
||||
*/
|
||||
private static final int CP95_DATA_SIZE_THRESHOLD = 21;
|
||||
private static final double CP95_PERCENTILE = 0.05;
|
||||
private static final int CP95_SMALL_DATA_INDEX = 1;
|
||||
|
||||
/**
|
||||
* 数据处理相关常量
|
||||
*/
|
||||
private static final int MIN_DATA_SIZE_FOR_SECTION_VALUE = 2;
|
||||
|
||||
|
||||
/**
|
||||
* 相角矫正到统一区间[-180°, 180°]
|
||||
* <p>
|
||||
* 将任意角度值标准化到[-180°, 180°]范围内,便于相角比较和计算。
|
||||
* 使用模运算处理任意大小的角度值,能正确处理超出多个360°范围的情况。
|
||||
* <p>
|
||||
* 示例:
|
||||
* <ul>
|
||||
* <li>-1080° → 0°</li>
|
||||
* <li>-900° → 180°</li>
|
||||
* <li>720° → 0°</li>
|
||||
* <li>450° → 90°</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param phase 待矫正的相角值(单位:度)
|
||||
* @return 矫正后的相角值,范围在[-180°, 180°]内
|
||||
*/
|
||||
public static Double adjustPhase(Double phase) {
|
||||
if (phase == null) {
|
||||
return null;
|
||||
}
|
||||
// 使用模运算将角度标准化到[-180, 180]范围
|
||||
double normalizedPhase = phase % ANGLE_360;
|
||||
// 处理超出[-180, 180]范围的情况
|
||||
if (normalizedPhase > ANGLE_180) {
|
||||
normalizedPhase -= ANGLE_360;
|
||||
} else if (normalizedPhase < ANGLE_MINUS_180) {
|
||||
normalizedPhase += ANGLE_360;
|
||||
}
|
||||
|
||||
return normalizedPhase;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断被检设备数据与标准设备数据的时间是否对齐
|
||||
* <p>
|
||||
* 数据对齐的判断标准(满足任一条件即可):
|
||||
* 1. 将时间戳四舍五入到秒级精度后完全相等(处理跨秒边界情况)
|
||||
* 2. 两个时间戳的毫秒差值小于容差值(处理同秒内的精确对齐)
|
||||
* <p>
|
||||
* 示例:499ms vs 509ms → 四舍五入后不同秒,但差值仅10ms < 100ms → 对齐
|
||||
*
|
||||
* @param devData 被检设备数据,包含时间戳信息
|
||||
* @param standardDevData 标准设备数据,包含时间戳信息
|
||||
* @return true表示数据时间对齐,false表示不对齐
|
||||
*/
|
||||
public static boolean isAlignData(DevData devData, DevData standardDevData, int type) {
|
||||
if (ObjectUtil.isNotNull(devData) && ObjectUtil.isNotNull(standardDevData)) {
|
||||
// 获取两个设备数据的时间戳(毫秒)
|
||||
long devMillis = getMillis(devData.getTime());
|
||||
long standardMillis = getMillis(standardDevData.getTime());
|
||||
if (type == 1) { // 实时数据
|
||||
// 方式1:将时间戳转换为秒级精度进行比较(处理跨秒边界情况)
|
||||
BigDecimal devSeconds = BigDecimal.valueOf(devMillis).divide(BigDecimal.valueOf(MILLIS_TO_SECONDS), 0, RoundingMode.HALF_UP);
|
||||
BigDecimal standardSeconds = BigDecimal.valueOf(standardMillis).divide(BigDecimal.valueOf(MILLIS_TO_SECONDS), 0, RoundingMode.HALF_UP);
|
||||
if (devSeconds.compareTo(standardSeconds) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 方式2:毫秒级时间差小于容差值也认为是对齐的(处理精确对齐)
|
||||
if (Math.abs(devMillis - standardMillis) < TIME_ALIGNMENT_TOLERANCE_MS) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (type == 2) { //统计数据
|
||||
BigDecimal devSeconds = BigDecimal.valueOf(devMillis).divide(BigDecimal.valueOf(MILLIS_TO_MINUTES), 0, RoundingMode.HALF_UP);
|
||||
BigDecimal standardSeconds = BigDecimal.valueOf(standardMillis).divide(BigDecimal.valueOf(MILLIS_TO_MINUTES), 0, RoundingMode.HALF_UP);
|
||||
if (devSeconds.compareTo(standardSeconds) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串日期时间转换为LocalDateTime对象
|
||||
* <p>
|
||||
* 使用指定的格式解析时间字符串,支持带时区的ISO 8601格式。
|
||||
* 解析时会将时间统一转换为UTC时区的LocalDateTime。
|
||||
*
|
||||
* @param dateTimeStr 日期时间字符串,应符合指定格式
|
||||
* @param formatter 时间格式化器,用于解析字符串
|
||||
* @return 解析成功返回LocalDateTime对象,解析失败返回null
|
||||
*/
|
||||
public static LocalDateTime timeFormat(String dateTimeStr, DateTimeFormatter formatter) {
|
||||
try {
|
||||
// 使用UTC时区解析时间字符串
|
||||
ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateTimeStr, formatter.withZone(ZoneId.of("UTC")));
|
||||
// 转换为LocalDateTime对象
|
||||
return zonedDateTime.toLocalDateTime();
|
||||
} catch (DateTimeParseException e) {
|
||||
log.error("日期时间字符串格式错误: {}", e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字符串日期时间对应的UTC毫秒时间戳
|
||||
* <p>
|
||||
* 使用默认的ISO_DATE_TIME格式解析时间字符串,
|
||||
* 并转换为UTC时区的毫秒时间戳。
|
||||
*
|
||||
* @param dateTimeStr ISO 8601格式的日期时间字符串
|
||||
* @return UTC时区的毫秒时间戳
|
||||
*/
|
||||
public static long getMillis(String dateTimeStr) {
|
||||
if (dateTimeStr == null || dateTimeStr.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("日期时间字符串不能为空");
|
||||
}
|
||||
LocalDateTime localDateTime = timeFormat(dateTimeStr, FORMATTER);
|
||||
if (localDateTime == null) {
|
||||
throw new IllegalArgumentException("无法解析日期时间字符串: " + dateTimeStr);
|
||||
}
|
||||
return getMillis(localDateTime);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取LocalDateTime对应的UTC毫秒时间戳
|
||||
* <p>
|
||||
* 将LocalDateTime对象转换为UTC时区的毫秒时间戳。
|
||||
*
|
||||
* @param localDateTime 本地日期时间对象
|
||||
* @return UTC时区的毫秒时间戳
|
||||
*/
|
||||
public static long getMillis(LocalDateTime localDateTime) {
|
||||
if (localDateTime == null) {
|
||||
throw new IllegalArgumentException("LocalDateTime参数不能为null");
|
||||
}
|
||||
return localDateTime.atZone(ZoneId.of("UTC")).toInstant().toEpochMilli();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断数值是否为零(在容差范围内)
|
||||
* <p>
|
||||
* 使用BigDecimal进行精确计算,避免浮点数精度问题。
|
||||
* 当数值的绝对值小于预设阈值(0.01)时,认为该数值为零。
|
||||
* 主要用于电流等物理量的零值判断。
|
||||
*
|
||||
* @param value 待判断的数值,null值被认为是零
|
||||
* @param ratedCurrent 额定电流,用于计算阈值
|
||||
* @return true表示数值为零(在容差范围内),false表示非零
|
||||
*/
|
||||
public static boolean isZero(Double value, Double ratedCurrent) {
|
||||
if (value == null) {
|
||||
return true;
|
||||
}
|
||||
double threshold = 0.01 * ratedCurrent;
|
||||
BigDecimal bd = BigDecimal.valueOf(value);
|
||||
return bd.subtract(BigDecimal.ZERO).abs().compareTo(BigDecimal.valueOf(threshold)) < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取CP95分位数值
|
||||
* <p>
|
||||
* CP95表示95%分位数,即有95%的数据小于等于此值。
|
||||
* 算法逻辑:
|
||||
* <ul>
|
||||
* <li>数据量=1时:返回该数据</li>
|
||||
* <li>数据量<21时:返回第2个数据(索引1)</li>
|
||||
* <li>数据量≥21时:计算5%位置的数据(适用于从大到小排序的数据)</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param t 已排序的数据列表(从大到小排序)
|
||||
* @return CP95分位数值列表,包含单个元素
|
||||
*/
|
||||
public static List<Double> getCP95Doubles(List<Double> t) {
|
||||
if (CollUtil.isEmpty(t)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 单个数据直接返回
|
||||
if (t.size() == 1) {
|
||||
return new ArrayList<>(t);
|
||||
}
|
||||
|
||||
// 数据量较少时,取第2个数据作为CP95值
|
||||
if (t.size() < CP95_DATA_SIZE_THRESHOLD) {
|
||||
return t.subList(CP95_SMALL_DATA_INDEX, CP95_SMALL_DATA_INDEX + 1);
|
||||
}
|
||||
|
||||
// 数据量充足时,计算真正的95%分位数
|
||||
// 由于数据已从大到小排序,95%分位数位于5%位置
|
||||
int cp95Index = (int) Math.ceil(t.size() * CP95_PERCENTILE) - 1;
|
||||
return t.subList(cp95Index, cp95Index + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取CP95分位数值在列表中的索引位置
|
||||
* <p>
|
||||
* 计算CP95分位数在已排序列表中的索引位置。
|
||||
* 索引计算规则与getCP95Doubles方法保持一致。
|
||||
*
|
||||
* @param t 已排序的数据列表(从大到小排序)
|
||||
* @return CP95分位数的索引位置,列表为空时返回-1
|
||||
*/
|
||||
public static int getCP95Idx(List<Double> t) {
|
||||
if (CollUtil.isEmpty(t)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 单个数据返回索引0
|
||||
if (t.size() == 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 数据量较少时,返回索引1
|
||||
if (t.size() < CP95_DATA_SIZE_THRESHOLD) {
|
||||
return CP95_SMALL_DATA_INDEX;
|
||||
}
|
||||
|
||||
// 数据量充足时,计算95%分位数的索引位置
|
||||
// 由于数据已从大到小排序,95%分位数索引为5%位置
|
||||
return (int) Math.ceil(t.size() * CP95_PERCENTILE) - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部分值(去除最大最小值后的数据)
|
||||
* <p>
|
||||
* 用于数据预处理,去除可能的异常值。
|
||||
* 算法逻辑:
|
||||
* <ul>
|
||||
* <li>数据量≤2时:返回原数据副本</li>
|
||||
* <li>数据量>2时:移除一个最大值和一个最小值后返回剩余数据</li>
|
||||
* </ul>
|
||||
* 注意:该方法不会修改原始列表,而是返回新的列表。
|
||||
*
|
||||
* @param t 原始数据列表
|
||||
* @return 去除最大最小值后的数据列表副本
|
||||
*/
|
||||
public static List<Double> getSectionValueDoubles(List<Double> t) {
|
||||
if (CollUtil.isEmpty(t) || t.size() <= MIN_DATA_SIZE_FOR_SECTION_VALUE) {
|
||||
return new ArrayList<>(t);
|
||||
}
|
||||
|
||||
// 创建副本避免修改原始列表
|
||||
List<Double> result = new ArrayList<>(t);
|
||||
Double max = Collections.max(result);
|
||||
Double min = Collections.min(result);
|
||||
result.remove(max);
|
||||
result.remove(min);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算数据列表的算术平均值
|
||||
* <p>
|
||||
* 对输入的数值列表计算算术平均值,并以单元素列表形式返回。
|
||||
* 空列表会返回空列表。
|
||||
*
|
||||
* @param t 数值列表
|
||||
* @return 包含平均值的单元素列表,输入为空时返回空列表
|
||||
*/
|
||||
public static List<Double> getAvgDoubles(List<Double> t) {
|
||||
if (CollUtil.isEmpty(t)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 计算列表中所有数值的算术平均值
|
||||
double average = t.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
|
||||
// 将平均值包装为单元素列表返回
|
||||
return Collections.singletonList(average);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对数据列表进行排序并返回原始索引序列
|
||||
* <p>
|
||||
* 使用选择排序算法对列表进行排序,同时跟踪每个元素的原始索引位置。
|
||||
* 这样可以在数据排序后仍然知道每个数据在原始列表中的位置。
|
||||
*
|
||||
* <b>注意:</b>该方法会直接修改输入的列表。
|
||||
*
|
||||
* @param list 待排序的数据列表(会被直接修改)
|
||||
* @param isAsc 排序方式,true为升序,false为降序
|
||||
* @return 排序后各元素在原始列表中的索引位置
|
||||
*/
|
||||
public static List<Integer> sort(List<Double> list, Boolean isAsc) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
if (isAsc == null) {
|
||||
throw new IllegalArgumentException("排序方式参数不能为null");
|
||||
}
|
||||
// 创建索引列表,记录每个元素的原始位置
|
||||
List<Integer> indexList = Stream.iterate(0, i -> i + 1).limit(list.size()).collect(Collectors.toList());
|
||||
// 使用选择排序算法,同时维护索引映射
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
// 当前轮次要放置的目标位置
|
||||
int targetIdx = i;
|
||||
// 在未排序部分寻找最值
|
||||
for (int j = i + 1; j < list.size(); j++) {
|
||||
if (isAsc) {
|
||||
// 升序:寻找最小值
|
||||
if (list.get(j) < list.get(targetIdx)) {
|
||||
targetIdx = j;
|
||||
}
|
||||
} else {
|
||||
// 降序:寻找最大值
|
||||
if (list.get(j) > list.get(targetIdx)) {
|
||||
targetIdx = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 交换数据值和对应的索引
|
||||
if (targetIdx != i) {
|
||||
// 交换数据值
|
||||
double temp = list.get(i);
|
||||
list.set(i, list.get(targetIdx));
|
||||
list.set(targetIdx, temp);
|
||||
|
||||
// 交换对应的原始索引
|
||||
int tempIdx = indexList.get(i);
|
||||
indexList.set(i, indexList.get(targetIdx));
|
||||
indexList.set(targetIdx, tempIdx);
|
||||
}
|
||||
}
|
||||
return indexList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据idxList索引列表从oldList中获取新数组
|
||||
*
|
||||
* @param oldList
|
||||
* @param idxList
|
||||
* @return
|
||||
*/
|
||||
public static List<Double> getNewArray(List<Double> oldList, List<Integer> idxList) {
|
||||
if (CollUtil.isNotEmpty(oldList) && CollUtil.isNotEmpty(idxList)) {
|
||||
if (CollUtil.max(idxList) > oldList.size() - 1 || CollUtil.min(idxList) < 0) {
|
||||
return null;
|
||||
}
|
||||
List<Double> newList = new ArrayList<>();
|
||||
for (int i = 0; i < idxList.size(); i++) {
|
||||
newList.add(oldList.get(idxList.get(i)));
|
||||
}
|
||||
return newList;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查文件是否存在
|
||||
*/
|
||||
public static void checkFileExists(String filePath, String description) {
|
||||
File file = new File(filePath);
|
||||
if (!file.exists()) {
|
||||
System.err.println("警告: " + description + " 不存在: " + filePath);
|
||||
System.err.println("请确保文件路径正确,或修改测试中的文件路径");
|
||||
} else {
|
||||
System.out.println(description + " 存在: " + filePath);
|
||||
System.out.println(" 文件大小: " + file.length() + " bytes");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取皮尔逊系数
|
||||
*
|
||||
* <p>相关系数绝对值|r|所代表的相关强度</p>
|
||||
* <ul>
|
||||
* <li>0.8 - 1.0:极强相关</li>
|
||||
* <li>0.6 - 0.8:强相关</li>
|
||||
* <li>0.4 - 0.6:中等程度相关</li>
|
||||
* <li>0.2 - 0.4:弱相关</li>
|
||||
* <li>0.0 - 0.2:极弱相关或无相关</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static BigDecimal getPearsonCorrelationCoefficient(List<Double> x, List<Double> y) {
|
||||
Double xAvg = x.parallelStream().mapToDouble(Double::doubleValue).average().orElse(0.0);
|
||||
Double yAvg = y.parallelStream().mapToDouble(Double::doubleValue).average().orElse(0.0);
|
||||
|
||||
// 协方差求和
|
||||
BigDecimal covarianceSum = BigDecimal.ZERO;
|
||||
// x的方差
|
||||
BigDecimal varianceX = BigDecimal.ZERO;
|
||||
// y的方差
|
||||
BigDecimal varianceY = BigDecimal.ZERO;
|
||||
for (int i = 0; i < x.size(); i++) {
|
||||
covarianceSum = covarianceSum.add(BigDecimal.valueOf((x.get(i) - xAvg) * (y.get(i) - yAvg)));
|
||||
varianceX = varianceX.add(BigDecimal.valueOf(Math.pow(x.get(i) - xAvg, 2)));
|
||||
varianceY = varianceY.add(BigDecimal.valueOf(Math.pow(y.get(i) - yAvg, 2)));
|
||||
}
|
||||
BigDecimal fm = BigDecimal.valueOf(Math.sqrt(varianceX.doubleValue()) * Math.sqrt(varianceY.doubleValue()));
|
||||
if (NumberUtil.equals(fm.doubleValue(), BigDecimal.ZERO.doubleValue())) {
|
||||
if (NumberUtil.equals(covarianceSum, BigDecimal.ZERO)) {
|
||||
return BigDecimal.valueOf(1.0);
|
||||
} else {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
} else {
|
||||
return BigDecimal.valueOf(Math.abs(covarianceSum.divide(fm, 8, BigDecimal.ROUND_HALF_UP).doubleValue())); //相关系数
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 比值一致性,获取CV系数
|
||||
*
|
||||
* <p>一致性等级</p>
|
||||
* <ul>
|
||||
* <li>CV<1%:非常优秀</li>
|
||||
* <li>1%≤CV<5%:优良</li>
|
||||
* <li>5%≤CV<10%:良好</li>
|
||||
* <li>10%≤CV<20%:中等</li>
|
||||
* <li>20%≤CV:较差</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param x
|
||||
* @param y
|
||||
* @return
|
||||
*/
|
||||
public static BigDecimal getCvCoefficient(List<Double> x, List<Double> y) {
|
||||
List<BigDecimal> ratioList = new ArrayList<>();
|
||||
for (int i = 0; i < x.size(); i++) {
|
||||
ratioList.add(BigDecimal.valueOf(x.get(i) / y.get(i)));
|
||||
}
|
||||
|
||||
BigDecimal avg = BigDecimal.valueOf(ratioList.parallelStream().mapToDouble(BigDecimal::doubleValue).average().orElse(0.0));
|
||||
BigDecimal variance = BigDecimal.valueOf(
|
||||
ratioList.parallelStream().mapToDouble(BigDecimal::doubleValue)
|
||||
.map(v -> Math.pow(v - avg.doubleValue(), 2)).average().orElse(0.0));
|
||||
|
||||
return BigDecimal.valueOf(Math.sqrt(variance.doubleValue()) / avg.doubleValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算新的系数
|
||||
*
|
||||
* @param devList
|
||||
* @param stdDevList
|
||||
* @return
|
||||
*/
|
||||
public static Double getNewXi(List<Double> devList, List<Double> stdDevList) {
|
||||
ArrayList<Double> xiList = new ArrayList<>();
|
||||
for (int i = 0; i < stdDevList.size(); i++) {
|
||||
if (!stdDevList.get(i).equals(0)) {
|
||||
BigDecimal bigDecimalDev = BigDecimal.valueOf(devList.get(i));
|
||||
BigDecimal bigDecimalStdDev = BigDecimal.valueOf(stdDevList.get(i));
|
||||
xiList.add((bigDecimalDev.subtract(bigDecimalStdDev)).divide(BigDecimal.valueOf(2.0).multiply(bigDecimalDev), 8, BigDecimal.ROUND_HALF_UP).doubleValue() + 1.0);
|
||||
}
|
||||
}
|
||||
return xiList.stream().mapToDouble(Double::doubleValue).average().orElse(1.0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.njcn.gather.detection.util.business;
|
||||
|
||||
import com.njcn.gather.detection.pojo.constant.DetectionCommunicateConstant;
|
||||
import com.njcn.gather.detection.pojo.param.PreDetectionParam;
|
||||
import com.njcn.gather.detection.util.socket.CnSocketUtil;
|
||||
import com.njcn.gather.detection.util.socket.SocketManager;
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 检测通讯工具类
|
||||
*
|
||||
* @author hongawen
|
||||
* @version 1.0
|
||||
* @data 2025/4/15 15:24
|
||||
*/
|
||||
@Slf4j
|
||||
public class DetectionCommunicateUtil {
|
||||
|
||||
|
||||
/**
|
||||
* 检测是否存在连接的源、设备通讯的模块通道
|
||||
* 有则强行关闭
|
||||
*/
|
||||
public static void checkCommunicateChannel(PreDetectionParam param) {
|
||||
Channel channelSource = SocketManager.getChannelByUserId(param.getUserPageId() + DetectionCommunicateConstant.SOURCE);
|
||||
Channel channelDev = SocketManager.getChannelByUserId(param.getUserPageId() + DetectionCommunicateConstant.DEV);
|
||||
|
||||
boolean channelSourceActive = channelSource != null && channelSource.isActive();
|
||||
boolean channelDevActive = channelDev != null && channelDev.isActive();
|
||||
if(channelSourceActive || channelDevActive){
|
||||
if(channelSourceActive){
|
||||
System.out.println("发送关闭源指令。。。。。。。。");
|
||||
CnSocketUtil.quitSendSource(param);
|
||||
}
|
||||
|
||||
if(channelDevActive){
|
||||
System.out.println("发送关闭设备通讯指令。。。。。。。。");
|
||||
CnSocketUtil.quitSend(param);
|
||||
}
|
||||
// 休眠4秒
|
||||
try {
|
||||
Thread.sleep(4000);
|
||||
} catch (InterruptedException e) {
|
||||
log.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// SocketManager.removeUser(param.getUserPageId() + DetectionCommunicateConstant.SOURCE);
|
||||
// SocketManager.removeUser(param.getUserPageId() + DetectionCommunicateConstant.DEV);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 比对式-检测是否存在已有的Socket通道,有则强行关闭
|
||||
*
|
||||
* @param loginName
|
||||
*/
|
||||
// public static void checkContrastCommunicateChannel(String loginName) {
|
||||
// Channel channel = SocketManager.getChannelByUserId(loginName + CnSocketUtil.CONTRAST_DEV_TAG);
|
||||
//
|
||||
// if (Objects.nonNull(channel) && channel.isActive()) {
|
||||
// System.out.println("存在已有的Socket通道,强行关闭。。。。。。。。");
|
||||
// CnSocketUtil.contrastSendquit(loginName);
|
||||
// SocketManager.removeUser(loginName + CnSocketUtil.CONTRAST_DEV_TAG);
|
||||
// try {
|
||||
// Thread.sleep(4000);
|
||||
// } catch (InterruptedException e) {
|
||||
// log.error(e.getMessage());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import com.alibaba.fastjson.JSONObject;
|
||||
import com.njcn.gather.detection.pojo.enums.SourceOperateCodeEnum;
|
||||
import com.njcn.gather.detection.pojo.param.PreDetectionParam;
|
||||
import com.njcn.gather.detection.pojo.vo.SocketMsg;
|
||||
import com.njcn.gather.detection.pojo.vo.WebSocketVO;
|
||||
import com.njcn.gather.detection.util.socket.websocket.WebServiceManager;
|
||||
|
||||
/**
|
||||
* @Author: cdf
|
||||
@@ -14,8 +14,19 @@ import com.njcn.gather.detection.pojo.vo.WebSocketVO;
|
||||
*/
|
||||
public class CnSocketUtil {
|
||||
|
||||
private final static String devTag = "_Dev";
|
||||
private final static String sourceTag = "_Source";
|
||||
public final static String DEV_TAG = "_Dev";
|
||||
|
||||
public final static String CONTRAST_DEV_TAG = "_Contrast_Dev";
|
||||
|
||||
public final static String SOURCE_TAG = "_Source";
|
||||
|
||||
public final static String START_TAG = "_Start";
|
||||
|
||||
public final static String END_TAG = "_End";
|
||||
|
||||
public final static String STEP_TAG = "&&";
|
||||
|
||||
public final static String SPLIT_TAG = "_";
|
||||
|
||||
/**
|
||||
* 退出检测
|
||||
@@ -24,7 +35,8 @@ public class CnSocketUtil {
|
||||
SocketMsg<String> socketMsg = new SocketMsg<>();
|
||||
socketMsg.setRequestId(SourceOperateCodeEnum.QUITE.getValue());
|
||||
socketMsg.setOperateCode(SourceOperateCodeEnum.QUIT_INIT_03.getValue());
|
||||
SocketManager.sendMsg(param.getUserPageId() + devTag, JSON.toJSONString(socketMsg));
|
||||
SocketManager.sendMsg(param.getUserPageId() + DEV_TAG, JSON.toJSONString(socketMsg));
|
||||
WebServiceManager.removePreDetectionParam(param.getUserPageId());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -35,32 +47,24 @@ public class CnSocketUtil {
|
||||
socketMsg.setRequestId(SourceOperateCodeEnum.QUITE_SOURCE.getValue());
|
||||
socketMsg.setOperateCode(SourceOperateCodeEnum.CLOSE_GATHER.getValue());
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("sourceId", param.getSourceId());
|
||||
jsonObject.put("sourceId", param.getSourceName());
|
||||
socketMsg.setData(jsonObject.toJSONString());
|
||||
SocketManager.sendMsg(param.getUserPageId() + sourceTag, JSON.toJSONString(socketMsg));
|
||||
}
|
||||
|
||||
/**
|
||||
* 推送webSocket数据
|
||||
*/
|
||||
public static void sendToWebSocket(String userId, String requestId, String operatorType, Object data, String desc){
|
||||
WebSocketVO<Object> webSocketVO = new WebSocketVO<>();
|
||||
webSocketVO.setRequestId(requestId);
|
||||
webSocketVO.setOperateCode(operatorType);
|
||||
webSocketVO.setData(data);
|
||||
webSocketVO.setDesc(desc);
|
||||
WebServiceManager.sendMessage(userId,webSocketVO);
|
||||
SocketManager.sendMsg(param.getUserPageId() + SOURCE_TAG, JSON.toJSONString(socketMsg));
|
||||
WebServiceManager.removePreDetectionParam(param.getUserPageId());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 推送未知异常的webSocket数据
|
||||
* 比对式-退出检测
|
||||
*/
|
||||
public static void sendUnSocket(String userId){
|
||||
WebSocketVO<Object> webSocketVO = new WebSocketVO<>();
|
||||
webSocketVO.setRequestId(SourceOperateCodeEnum.UNKNOWN_OPERATE.getValue());
|
||||
webSocketVO.setData(SourceOperateCodeEnum.UNKNOWN_OPERATE.getMsg());
|
||||
webSocketVO.setOperateCode(SourceOperateCodeEnum.UNKNOWN_OPERATE.getMsg());
|
||||
WebServiceManager.sendMessage(userId,webSocketVO);
|
||||
public static void contrastSendquit(String loginName, SourceOperateCodeEnum operateCode, boolean isRemoveSocket) {
|
||||
System.out.println("比对式-发送" + operateCode.getMsg() + "指令。。。。。。。。");
|
||||
SocketMsg<String> socketMsg = new SocketMsg<>();
|
||||
socketMsg.setRequestId(SourceOperateCodeEnum.QUITE.getValue());
|
||||
socketMsg.setOperateCode(operateCode.getValue());
|
||||
SocketManager.sendMsg(loginName + CONTRAST_DEV_TAG, JSON.toJSONString(socketMsg));
|
||||
// WebServiceManager.removePreDetectionParam();
|
||||
FormalTestManager.isRemoveSocket = isRemoveSocket;
|
||||
FormalTestManager.currentStep = SourceOperateCodeEnum.QUITE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,67 +1,211 @@
|
||||
package com.njcn.gather.detection.util.socket;
|
||||
|
||||
import com.google.common.collect.HashBiMap;
|
||||
import com.njcn.gather.detection.pojo.dto.WaveResultDTO;
|
||||
import com.njcn.gather.detection.pojo.enums.SourceOperateCodeEnum;
|
||||
import com.njcn.gather.detection.pojo.po.DevData;
|
||||
import com.njcn.gather.detection.pojo.vo.DevLineTestResult;
|
||||
import com.njcn.gather.device.pojo.enums.PatternEnum;
|
||||
import com.njcn.gather.device.pojo.vo.PreDetection;
|
||||
import com.njcn.gather.plan.pojo.enums.DataSourceEnum;
|
||||
import com.njcn.gather.plan.pojo.po.AdPlan;
|
||||
import com.njcn.gather.plan.pojo.po.AdPlanTestConfig;
|
||||
import com.njcn.gather.script.pojo.po.SourceIssue;
|
||||
import com.njcn.gather.system.dictionary.pojo.enums.DictDataEnum;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
|
||||
/**
|
||||
* @Author: cdf
|
||||
* @CreateTime: 2025-01-08
|
||||
* @Description: 正式检测对象管理
|
||||
* @Description: 正式(预)检测对象管理
|
||||
*/
|
||||
public class FormalTestManager {
|
||||
|
||||
// 当前步骤
|
||||
public static SourceOperateCodeEnum currentStep;
|
||||
|
||||
/**
|
||||
* key:设备ip,value:当前设备下面的通道序号
|
||||
* key:设备ip,value:当前设备下面的监测点ID(ip_通道号)
|
||||
*/
|
||||
public static Map<String,List<String>> devMapMonitorNum = new ConcurrentHashMap<>();
|
||||
public static Map<String, List<String>> devMapMonitorNum = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 所有参与检测的装置
|
||||
* 所有参与检测的被检设备
|
||||
*/
|
||||
public static List<PreDetection> devList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 所有参与检测的标准设备
|
||||
*/
|
||||
public static List<PreDetection> standardDevList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 所有参与检测的监测点
|
||||
*/
|
||||
public static List<String> monitorIdListComm = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 所有参与检测的监测点。key:监测点(ip_通道号),value:检测点实体
|
||||
*/
|
||||
public static Map<String, PreDetection.MonitorListDTO> monitorMap = new HashMap<>();
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 用于存储所有测点的实时数据
|
||||
*/
|
||||
public static List<DevData> realDataXiList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* key:设备ip,value:装置名称
|
||||
*/
|
||||
public static Map<String, String> devNameMapComm = new HashMap<>();
|
||||
|
||||
/**
|
||||
* key:设备ip,value:装置id
|
||||
*/
|
||||
public static Map<String, String> devIdMapComm = new HashMap<>();
|
||||
|
||||
public static HashBiMap<String, String> devIdMapComm = HashBiMap.create();
|
||||
|
||||
/**
|
||||
* 停止触发标识
|
||||
*/
|
||||
public static Boolean stopFlag = false;
|
||||
|
||||
|
||||
/**
|
||||
* 已经暂停后的标识
|
||||
*/
|
||||
public static Boolean hasStopFlag = false;
|
||||
|
||||
|
||||
/**
|
||||
* 暂停后的超时计时时间
|
||||
*/
|
||||
public static Integer stopTime = 0;
|
||||
|
||||
/**
|
||||
* 当前正在检测的计划
|
||||
*/
|
||||
public static AdPlan currentTestPlan;
|
||||
|
||||
/**
|
||||
* 非录波数据源枚举
|
||||
*/
|
||||
public static DataSourceEnum nonWaveDataSourceEnum;
|
||||
|
||||
|
||||
/**
|
||||
* 统计数据最大超时时间
|
||||
*/
|
||||
public static Long maxTime;
|
||||
|
||||
public static AdPlanTestConfig curretntTestPlanConfig;
|
||||
|
||||
/**
|
||||
* 当前正在检测的模式
|
||||
*/
|
||||
public static PatternEnum patternEnum;
|
||||
|
||||
/**
|
||||
* 比对式检测-检测项。key为检测项code,value为检测项id
|
||||
*/
|
||||
public static Map<String, String> testItemMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 数据处理原则
|
||||
*/
|
||||
public static DictDataEnum dataRule;
|
||||
|
||||
/**
|
||||
* 所有参与比对式检测的被检设备、标准设备配对关系。key:被检设备ip_通道号,value:标准设备ip_通道号
|
||||
*/
|
||||
public static HashBiMap<String, String> pairsIpMap = HashBiMap.create();
|
||||
|
||||
/**
|
||||
* 所有参与比对式检测的被检设备、标准设备配对关系。key:被检设备id_通道号,value:标准设备id_通道号
|
||||
*/
|
||||
public static HashBiMap<String, String> pairsIdMap = HashBiMap.create();
|
||||
|
||||
/**
|
||||
* 被检设备的数据。key:设备ip_通道号,value:DevData数据集合
|
||||
*/
|
||||
public static Map<String, List<DevData>> devDataMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 标准设备的数据。key:设备ip_通道号,value:DevData数据集合
|
||||
*/
|
||||
public static Map<String, List<DevData>> standardDevDataMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 是否要移除和通信模块的socket连接
|
||||
*/
|
||||
public static Boolean isRemoveSocket;
|
||||
|
||||
/**
|
||||
* 录波功能校验
|
||||
*/
|
||||
public static Boolean waveCheckFlag;
|
||||
|
||||
/**
|
||||
* 第几次监测 key为设备监测点id,value为第几次监测
|
||||
*/
|
||||
public static Map<String, Integer> numMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 存放录波相关数据。key:设备ip_通道号,value:WaveResultDTO数据
|
||||
*/
|
||||
public static Map<String, WaveResultDTO> waveResultDTOMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 录波组数
|
||||
*/
|
||||
public static Integer waveNum;
|
||||
|
||||
/**
|
||||
* 每次录波检测结果
|
||||
*/
|
||||
public static List<DevLineTestResult> preNumTestResultList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 定时器
|
||||
*/
|
||||
public static ScheduledExecutorService scheduler;
|
||||
|
||||
/**
|
||||
* 定时器任务
|
||||
*/
|
||||
public static ScheduledFuture<?> scheduledFuture;
|
||||
|
||||
|
||||
public static boolean isWaveCheck;
|
||||
|
||||
public static List<String> pstDataType;
|
||||
|
||||
public static boolean isPstData;
|
||||
|
||||
/**
|
||||
* 是否在检测中
|
||||
*/
|
||||
public static boolean isTesting;
|
||||
|
||||
public static boolean realProtocol;
|
||||
|
||||
public static boolean statisticsProtocol;
|
||||
|
||||
public static boolean voltageProtocol;
|
||||
|
||||
public static boolean unknownError;
|
||||
|
||||
/**
|
||||
* 当前下发的脚本
|
||||
*/
|
||||
public static SourceIssue currentIssue;
|
||||
|
||||
/**
|
||||
* 是否进行相序校验
|
||||
*/
|
||||
public static boolean isXu;
|
||||
}
|
||||
|
||||
@@ -3,10 +3,7 @@ package com.njcn.gather.detection.util.socket;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.serializer.SerializerFeature;
|
||||
import com.njcn.gather.detection.pojo.enums.SourceOperateCodeEnum;
|
||||
import com.njcn.gather.detection.pojo.param.PreDetectionParam;
|
||||
import com.njcn.gather.detection.pojo.vo.SocketDataMsg;
|
||||
import com.njcn.gather.detection.pojo.vo.SocketMsg;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -19,9 +16,8 @@ import java.util.Map;
|
||||
public class MsgUtil {
|
||||
|
||||
|
||||
|
||||
public static SocketDataMsg socketDataMsg(String textMsg){
|
||||
return JSON.parseObject(textMsg,SocketDataMsg.class);
|
||||
public static SocketDataMsg socketDataMsg(String textMsg) {
|
||||
return JSON.parseObject(textMsg, SocketDataMsg.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,13 +42,12 @@ public class MsgUtil {
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param socketDataMsg
|
||||
* @param devMap
|
||||
* @param type 0.装置 1.监测点
|
||||
* @param type 0.装置 1.监测点
|
||||
* @return
|
||||
*/
|
||||
public static String msgToWebData(SocketDataMsg socketDataMsg, Map<String,String> devMap,Integer type){
|
||||
public static String msgToWebData(SocketDataMsg socketDataMsg, Map<String, String> devMap, Integer type) {
|
||||
String data = socketDataMsg.getData();
|
||||
if (StrUtil.isNotBlank(data)) {
|
||||
String[] parts = data.split("_");
|
||||
@@ -60,10 +55,10 @@ public class MsgUtil {
|
||||
String key = parts[0];
|
||||
String newValue = devMap.get(key);
|
||||
if (newValue != null) {
|
||||
if(type == 0) {
|
||||
if (type == 0) {
|
||||
socketDataMsg.setData(newValue);
|
||||
}else {
|
||||
socketDataMsg.setData(newValue+"_"+parts[1]+"路");
|
||||
} else {
|
||||
socketDataMsg.setData(newValue + "_" + parts[1] + "路");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -71,6 +66,43 @@ public class MsgUtil {
|
||||
return JSON.toJSONString(socketDataMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一组监测点配对的字符串
|
||||
*
|
||||
* @param devMonitorId
|
||||
* @param standardDevId
|
||||
* @param devMap key为设备ip,value为设备名称
|
||||
* @return
|
||||
*/
|
||||
public static String getPairStr(String devMonitorId, String standardDevId, Map<String, String> devMap) {
|
||||
if (StrUtil.isBlank(devMonitorId) || StrUtil.isBlank(standardDevId)) {
|
||||
return "";
|
||||
} else {
|
||||
String[] split1 = devMonitorId.split("_");
|
||||
String[] split2 = standardDevId.split("_");
|
||||
return "被检设备\"" + devMap.get(split1[0]) + CnSocketUtil.SPLIT_TAG + split1[1] + "路\"" + " -> 标准设备\"" + devMap.get(split2[0]) + CnSocketUtil.SPLIT_TAG + split2[1] + "路\"";
|
||||
}
|
||||
}
|
||||
|
||||
public static String getMonitorInfo(String monitorId, Map<String, String> devMap) {
|
||||
if (StrUtil.isBlank(monitorId)) {
|
||||
return "";
|
||||
} else {
|
||||
String[] split1 = monitorId.split("_");
|
||||
return devMap.get(split1[0]) + CnSocketUtil.SPLIT_TAG + split1[1] + "路\"";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取消息
|
||||
*
|
||||
* @param monitorId 监测点id
|
||||
* @param devMap key为设备ip,value为设备名称
|
||||
* @param appendMsg 附加的消息
|
||||
* @return
|
||||
*/
|
||||
public static String getMsg(String monitorId, Map<String, String> devMap, String appendMsg) {
|
||||
String[] split1 = monitorId.split("_");
|
||||
return "\"" + devMap.get(split1[0]) + CnSocketUtil.SPLIT_TAG + "第" + split1[1] + "路\"" + appendMsg;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,58 @@
|
||||
package com.njcn.gather.detection.util.socket;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.njcn.gather.device.pojo.vo.PreDetection;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.njcn.gather.detection.pojo.param.ContrastDetectionParam;
|
||||
import com.njcn.gather.detection.pojo.param.PreDetectionParam;
|
||||
import com.njcn.gather.detection.util.socket.cilent.NettyClient;
|
||||
import com.njcn.gather.detection.util.socket.cilent.NettyContrastClientHandler;
|
||||
import com.njcn.gather.detection.util.socket.config.SocketConnectionConfig;
|
||||
import com.njcn.gather.plan.pojo.enums.DataSourceEnum;
|
||||
import com.njcn.gather.script.pojo.po.SourceIssue;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Socket连接管理器
|
||||
* 提供Socket连接的生命周期管理、消息发送、检测任务管理等功能
|
||||
* <p>
|
||||
* 包含以下主要功能:
|
||||
* 1. 基础连接管理:addUser, removeUser, sendMsg等
|
||||
* 2. 智能消息发送:smartSendToSource, smartSendToDevice等(新增)
|
||||
* 3. 检测任务管理:targetMap, sourceIssueList等管理
|
||||
* 4. 连接状态监控:getConnectionStatus等(新增)
|
||||
*
|
||||
* @Description: webSocket存储的通道
|
||||
* @Author: wr
|
||||
* @Author: wr, hongawen
|
||||
* @Date: 2024/12/11 13:04
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class SocketManager {
|
||||
|
||||
@Resource
|
||||
private SocketConnectionConfig socketConnectionConfig;
|
||||
|
||||
/**
|
||||
* key为userId(xxx_Source、xxx_Dev),value为channel
|
||||
*/
|
||||
private static final Map<String, Channel> socketSessions = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* key为userId(xxx_Source、xxx_Dev),value为group
|
||||
*/
|
||||
private static final Map<String, NioEventLoopGroup> socketGroup = new ConcurrentHashMap<>();
|
||||
|
||||
public static void addUser(String userId, Channel channel) {
|
||||
@@ -27,24 +60,25 @@ public class SocketManager {
|
||||
}
|
||||
|
||||
public static void addGroup(String userId, NioEventLoopGroup group) {
|
||||
socketGroup.put(userId, group);
|
||||
socketGroup.put(userId, group);
|
||||
}
|
||||
|
||||
public static void removeUser(String userId) {
|
||||
Channel channel = socketSessions.get(userId);
|
||||
if(ObjectUtil.isNotNull(channel)){
|
||||
if (ObjectUtil.isNotNull(channel)) {
|
||||
try {
|
||||
channel.close().sync();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
NioEventLoopGroup eventExecutors = socketGroup.get(userId);
|
||||
if(ObjectUtil.isNotNull(channel)){
|
||||
if (ObjectUtil.isNotNull(eventExecutors)) {
|
||||
eventExecutors.shutdownGracefully();
|
||||
System.out.println(userId+"__"+channel.id()+"关闭了客户端");
|
||||
System.out.println(userId + "__" + channel.id() + "关闭了客户端");
|
||||
}
|
||||
}
|
||||
socketSessions.remove(userId);
|
||||
socketGroup.remove(userId);
|
||||
}
|
||||
|
||||
public static Channel getChannelByUserId(String userId) {
|
||||
@@ -55,15 +89,170 @@ public class SocketManager {
|
||||
return socketGroup.get(userId);
|
||||
}
|
||||
|
||||
public static void sendMsg(String userId,String msg) {
|
||||
public static void sendMsg(String userId, String msg) {
|
||||
Channel channel = socketSessions.get(userId);
|
||||
if(ObjectUtil.isNotNull(channel)){
|
||||
channel.writeAndFlush(msg+'\n');
|
||||
System.out.println(userId+"__"+channel.id()+"往"+channel.remoteAddress()+"发送数据:"+msg);
|
||||
}else{
|
||||
System.out.println(userId+"__发送数据:失败通道不存在"+msg);
|
||||
if (ObjectUtil.isNotNull(channel)) {
|
||||
channel.writeAndFlush(msg + '\n');
|
||||
log.info("{}__{}往{}发送数据:{}", userId, channel.id(), channel.remoteAddress(), msg);
|
||||
} else {
|
||||
log.warn("{}__发送数据:失败通道不存在{}", userId, msg);
|
||||
}
|
||||
}
|
||||
|
||||
// =================== 智能发送功能 ===================
|
||||
|
||||
/**
|
||||
* 智能发送消息到程控源设备
|
||||
* 自动从配置文件读取IP和PORT,开发者无需关心网络配置
|
||||
* 如果连接不存在且requestId需要建立连接,会自动建立连接后发送
|
||||
*
|
||||
* @param param 检测参数,包含用户页面ID等信息
|
||||
* @param msg 要发送的消息内容(JSON格式,包含requestId字段)
|
||||
*/
|
||||
public void smartSendToSource(PreDetectionParam param, String msg) {
|
||||
String requestId = extractRequestId(msg);
|
||||
String userId = param.getUserPageId() + CnSocketUtil.SOURCE_TAG;
|
||||
// 检查是否需要建立连接
|
||||
if (SocketConnectionConfig.needsSourceConnection(requestId)) {
|
||||
String ip = socketConnectionConfig.getSource().getIp();
|
||||
Integer port = socketConnectionConfig.getSource().getPort();
|
||||
// 检查连接是否存在且活跃
|
||||
if (!isChannelActive(userId)) {
|
||||
log.info("程控源连接不存在,自动建立连接: userId={}, requestId={}", userId, requestId);
|
||||
// 异步建立程控源连接并发送消息
|
||||
CompletableFuture.runAsync(() -> {
|
||||
NettyClient.connectToSourceStatic(ip, port, param, msg);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 连接已存在或不需要建立连接,直接发送消息
|
||||
log.info("直接发送消息到程控源: userId={}, requestId={}", userId, requestId);
|
||||
sendMsg(userId, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 智能发送消息到被检设备
|
||||
* 自动从配置文件读取IP和PORT,开发者无需关心网络配置
|
||||
* 如果连接不存在且requestId需要建立连接,会自动建立连接后发送
|
||||
*
|
||||
* @param param 检测参数,包含用户页面ID等信息
|
||||
* @param msg 要发送的消息内容(JSON格式,包含requestId字段)
|
||||
*/
|
||||
public void smartSendToDevice(PreDetectionParam param, String msg) {
|
||||
String requestId = extractRequestId(msg);
|
||||
String userId = param.getUserPageId() + CnSocketUtil.DEV_TAG;
|
||||
// 检查是否需要建立连接
|
||||
if (SocketConnectionConfig.needsDeviceConnection(requestId)) {
|
||||
String ip = socketConnectionConfig.getDevice().getIp();
|
||||
Integer port = socketConnectionConfig.getDevice().getPort();
|
||||
// 检查连接是否存在且活跃
|
||||
if (!isChannelActive(userId)) {
|
||||
log.info("被检设备连接不存在,自动建立连接: userId={}, requestId={}", userId, requestId);
|
||||
// 异步建立被检设备连接并发送消息
|
||||
CompletableFuture.runAsync(() -> {
|
||||
NettyClient.connectToDeviceStatic(ip, port, param, msg);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 连接已存在或不需要建立连接,直接发送消息
|
||||
log.info("直接发送消息到被检设备: userId={}, requestId={}", userId, requestId);
|
||||
sendMsg(userId, msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 比对智能发送消息到被检设备
|
||||
* 自动从配置文件读取IP和PORT,开发者无需关心网络配置
|
||||
* 如果连接不存在且requestId需要建立连接,会自动建立连接后发送
|
||||
*
|
||||
* @param param 检测参数,包含用户页面ID等信息
|
||||
* @param msg 要发送的消息内容(JSON格式,包含requestId字段)
|
||||
*/
|
||||
public void smartSendToContrast(ContrastDetectionParam param, String msg) {
|
||||
String requestId = extractRequestId(msg);
|
||||
String userId = param.getLoginName() + CnSocketUtil.CONTRAST_DEV_TAG;
|
||||
// 检查是否需要建立连接
|
||||
if (SocketConnectionConfig.needsDeviceConnection(requestId)) {
|
||||
String ip = socketConnectionConfig.getDevice().getIp();
|
||||
Integer port = socketConnectionConfig.getDevice().getPort();
|
||||
// 检查连接是否存在且活跃
|
||||
if (!isChannelActive(userId)) {
|
||||
log.info("比对被检设备连接不存在,自动建立连接: userId={}, requestId={}", userId, requestId);
|
||||
// 异步建立比对被检设备连接并发送消息
|
||||
CompletableFuture.runAsync(() -> {
|
||||
NettyClient.connectToContrastDeviceStatic(ip, port, param, msg);
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
PreDetectionParam preDetectionParam = new PreDetectionParam();
|
||||
preDetectionParam.setUserPageId(param.getLoginName());
|
||||
preDetectionParam.setTestItemList(param.getTestItemList());
|
||||
preDetectionParam.setDevIds(param.getDevIds());
|
||||
preDetectionParam.setUserId(param.getUserId());
|
||||
NettyContrastClientHandler.param = preDetectionParam;
|
||||
}
|
||||
}
|
||||
|
||||
// 连接已存在或不需要建立连接,直接发送消息
|
||||
log.info("直接发送消息到比对被检设备: userId={}, requestId={}", userId, requestId);
|
||||
sendMsg(userId, msg);
|
||||
}
|
||||
|
||||
|
||||
// =================== 私有工具方法 ===================
|
||||
|
||||
/**
|
||||
* 从消息中提取requestId
|
||||
* 支持JSON格式的消息解析
|
||||
*
|
||||
* @param msg 消息内容
|
||||
* @return String requestId,如果解析失败返回"unknown"
|
||||
*/
|
||||
private static String extractRequestId(String msg) {
|
||||
try {
|
||||
if (StrUtil.isNotBlank(msg)) {
|
||||
// 尝试解析JSON格式消息
|
||||
JSONObject jsonObject = JSON.parseObject(msg);
|
||||
String requestId = jsonObject.getString("requestId");
|
||||
if (StrUtil.isNotBlank(requestId)) {
|
||||
return requestId;
|
||||
}
|
||||
|
||||
// 如果没有requestId字段,尝试解析request_id字段
|
||||
requestId = jsonObject.getString("request_id");
|
||||
if (StrUtil.isNotBlank(requestId)) {
|
||||
return requestId;
|
||||
}
|
||||
|
||||
// 如果没有JSON字段,尝试从普通字符串中匹配
|
||||
if (msg.contains("requestId=")) {
|
||||
String[] parts = msg.split("requestId=");
|
||||
if (parts.length > 1) {
|
||||
String idPart = parts[1].split("[,\\s&]")[0];
|
||||
return idPart.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("解析消息中的requestId失败: msg={}, error={}", msg, e.getMessage());
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查指定用户的Channel是否活跃
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return boolean true:连接活跃, false:连接不存在或不活跃
|
||||
*/
|
||||
public static boolean isChannelActive(String userId) {
|
||||
Channel channel = getChannelByUserId(userId);
|
||||
return ObjectUtil.isNotNull(channel) && channel.isActive();
|
||||
}
|
||||
|
||||
|
||||
@@ -86,9 +275,12 @@ public class SocketManager {
|
||||
/**
|
||||
* 用于存储每个测试小项超时时长key key:检测项 value:时间秒
|
||||
*/
|
||||
public static volatile Map<Integer,Long> clockMap = new ConcurrentHashMap<>();
|
||||
|
||||
public static volatile Map<Integer, Long> clockMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 用于存储比对式测试时间。
|
||||
*/
|
||||
public static volatile Map<DataSourceEnum, Long> contrastClockMap = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
public static void addSourceList(List<SourceIssue> sList) {
|
||||
@@ -113,8 +305,8 @@ public class SocketManager {
|
||||
targetMap = map;
|
||||
}
|
||||
|
||||
public static void addTargetMap(String scriptType,Long count) {
|
||||
targetMap.put(scriptType,count);
|
||||
public static void addTargetMap(String scriptType, Long count) {
|
||||
targetMap.put(scriptType, count);
|
||||
}
|
||||
|
||||
public static Long getSourceTarget(String scriptType) {
|
||||
@@ -122,10 +314,5 @@ public class SocketManager {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
package com.njcn.gather.detection.util.socket;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.njcn.gather.detection.pojo.vo.WebSocketVO;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.poi.ss.formula.functions.T;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @Description: webSocket存储的通道
|
||||
* @Author: wr
|
||||
* @Date: 2024/12/11 13:04
|
||||
*/
|
||||
@Slf4j
|
||||
public class WebServiceManager {
|
||||
|
||||
//key:页面 value:channel
|
||||
private static final Map<String, Channel> userSessions = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
public static void addUser(String userId, Channel channel) {
|
||||
userSessions.put(userId, channel);
|
||||
}
|
||||
|
||||
|
||||
public static void removeChannel(String channelId) {
|
||||
// 遍历并删除
|
||||
Iterator<Map.Entry<String, Channel>> iterator = userSessions.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<String, Channel> entry = iterator.next();
|
||||
if (entry.getValue().id().equals(channelId)) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Channel getChannelByUserId(String userId) {
|
||||
return userSessions.get(userId);
|
||||
}
|
||||
|
||||
public static void sendMsg(String userId,String msg) {
|
||||
Channel channel = userSessions.get(userId);
|
||||
if(Objects.nonNull(channel) && channel.isActive()){
|
||||
TextWebSocketFrame wd = new TextWebSocketFrame(msg);
|
||||
channel.writeAndFlush(wd);
|
||||
}else {
|
||||
log.error("{}-websocket推送消息失败;当前用户-{}-客户端已经断开连接", LocalDateTime.now(),userId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void sendMessage(String userId, WebSocketVO<Object> webSocketVO) {
|
||||
Channel channel = userSessions.get(userId);
|
||||
if(Objects.nonNull(channel) && channel.isActive()){
|
||||
TextWebSocketFrame wd = new TextWebSocketFrame(JSON.toJSONString(webSocketVO));
|
||||
channel.writeAndFlush(wd);
|
||||
}else {
|
||||
log.error("{}-websocket推送消息失败;当前用户-{}-客户端已经断开连接", LocalDateTime.now(),userId);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.njcn.gather.detection.util.socket;
|
||||
|
||||
import com.njcn.gather.detection.pojo.dto.DevXiNumData;
|
||||
import com.njcn.gather.detection.pojo.vo.CoefficientVO;
|
||||
import com.njcn.gather.device.pojo.vo.PreDetection;
|
||||
import com.njcn.gather.script.pojo.po.SourceIssue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -27,7 +28,7 @@ public class XiNumberManager {
|
||||
public static List<String> devXiList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 存储所有测点的大电压系数
|
||||
* 最开始存储装置通道原始系数,后续存储大电压计算出来的系数。key为装置ip
|
||||
*/
|
||||
public static Map<String, DevXiNumData> devXiNumDataMap = new ConcurrentHashMap<>();
|
||||
|
||||
@@ -42,10 +43,19 @@ public class XiNumberManager {
|
||||
public static Integer stepNumber = 0;
|
||||
|
||||
|
||||
// 存放大电压、小电压、大电流、小电流的数值
|
||||
public static List<CoefficientVO.DevParameter> devParameterList = new ArrayList<>();
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 因为只支持单台装置获取系数,用于记录未获取到系数的装置,获取到一个删除一个
|
||||
*/
|
||||
public static List<PreDetection> xiDevList = new ArrayList<>();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,313 @@
|
||||
package com.njcn.gather.detection.util.socket.cilent;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.njcn.gather.detection.pojo.enums.SourceOperateCodeEnum;
|
||||
import com.njcn.gather.detection.pojo.param.PreDetectionParam;
|
||||
import com.njcn.gather.detection.pojo.vo.SocketDataMsg;
|
||||
import com.njcn.gather.detection.pojo.vo.SocketMsg;
|
||||
import com.njcn.gather.detection.util.socket.CnSocketUtil;
|
||||
import com.njcn.gather.detection.util.socket.FormalTestManager;
|
||||
import com.njcn.gather.detection.util.socket.MsgUtil;
|
||||
import com.njcn.gather.detection.util.socket.SocketManager;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* Netty心跳处理器
|
||||
* <p>
|
||||
* 负责维护Socket长连接的心跳检测机制,通过定期发送心跳包来检测连接状态,
|
||||
* 当连续多次未收到心跳响应时自动断开连接并清理相关资源。
|
||||
* </p>
|
||||
*
|
||||
* <h3>核心功能:</h3>
|
||||
* <ul>
|
||||
* <li>定时发送心跳包 (默认10秒间隔,3秒后开始)</li>
|
||||
* <li>监听心跳响应,重置失败计数器</li>
|
||||
* <li>连续失败超过阈值时触发断开逻辑 (默认3次)</li>
|
||||
* <li>异步处理断开操作,避免阻塞心跳线程</li>
|
||||
* <li>优雅关闭资源,防止内存泄漏</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>心跳机制流程:</h3>
|
||||
* <pre>
|
||||
* 连接建立 → 启动心跳定时任务(3秒后开始,每10秒执行)
|
||||
* ↓
|
||||
* 发送心跳包 → 等待响应 → 收到响应(重置计数器) / 未收到响应(递增计数器)
|
||||
* ↓
|
||||
* 连续3次失败 → 异步执行断开逻辑 → 发送退出指令 → 延迟清理连接
|
||||
* ↓
|
||||
* 连接断开 → 优雅关闭定时任务和线程池
|
||||
* </pre>
|
||||
*
|
||||
* <h3>线程安全设计:</h3>
|
||||
* <p>
|
||||
* 使用单线程的ScheduledExecutorService处理心跳发送,避免并发问题。
|
||||
* 超时处理使用CompletableFuture异步执行,不阻塞心跳发送线程。
|
||||
* Future引用使用volatile修饰,确保多线程环境下的可见性。
|
||||
* </p>
|
||||
*
|
||||
* <h3>设备类型支持:</h3>
|
||||
* <ul>
|
||||
* <li>程控源设备 (CnSocketUtil.SOURCE_TAG): "_Source"</li>
|
||||
* <li>被检设备 (CnSocketUtil.DEV_TAG): "_Dev"</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>使用示例:</h3>
|
||||
* <pre>{@code
|
||||
* // 创建心跳处理器
|
||||
* HeartbeatHandler handler = new HeartbeatHandler(param, CnSocketUtil.SOURCE_TAG);
|
||||
*
|
||||
* // 添加到Netty管道中
|
||||
* pipeline.addLast(handler);
|
||||
* }</pre>
|
||||
*
|
||||
* @author cdf
|
||||
* @version 1.2
|
||||
* @since 2025-02-11
|
||||
*/
|
||||
@Slf4j
|
||||
public class HeartbeatHandler extends SimpleChannelInboundHandler<String> {
|
||||
|
||||
/**
|
||||
* 心跳定时任务执行器,使用单线程池避免并发问题
|
||||
*/
|
||||
private final ScheduledExecutorService heartbeatExecutor = Executors.newScheduledThreadPool(1);
|
||||
|
||||
/**
|
||||
* 检测参数,包含用户页面ID等信息
|
||||
*/
|
||||
private final PreDetectionParam param;
|
||||
|
||||
/**
|
||||
* 处理器类型标识("_Source" 或 "_Dev")
|
||||
*/
|
||||
private final String handlerType;
|
||||
|
||||
/**
|
||||
* 保存定时任务的Future引用,便于取消和管理
|
||||
*/
|
||||
private ScheduledFuture<?> heartbeatFuture;
|
||||
|
||||
/**
|
||||
* 允许连续未收到心跳响应的最大次数
|
||||
*/
|
||||
private static final int MAX_HEARTBEAT_MISSES = 3;
|
||||
|
||||
/**
|
||||
* 连续未收到心跳响应的次数
|
||||
*/
|
||||
private int consecutiveHeartbeatMisses = 0;
|
||||
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param param 检测参数,包含用户页面ID等信息
|
||||
* @param type 处理器类型(CnSocketUtil.SOURCE_TAG 或 CnSocketUtil.DEV_TAG)
|
||||
*/
|
||||
public HeartbeatHandler(PreDetectionParam param, String type) {
|
||||
this.param = param;
|
||||
this.handlerType = type;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通道激活时的回调方法
|
||||
* 在Socket连接建立成功后被调用,启动心跳机制
|
||||
*
|
||||
* @param ctx Netty的通道上下文对象
|
||||
*/
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) {
|
||||
log.info("心跳处理器启动 - 设备类型: {}", handlerType);
|
||||
// 启动心跳定时任务
|
||||
scheduleHeartbeat(ctx);
|
||||
// 传播事件给管道中的后续处理器
|
||||
ctx.fireChannelActive();
|
||||
}
|
||||
|
||||
/**
|
||||
* 通道断开时的回调方法
|
||||
* 在Socket连接断开时被调用,负责清理相关资源
|
||||
*
|
||||
* @param ctx Netty的通道上下文对象
|
||||
* @throws Exception 异常情况
|
||||
*/
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
log.info("心跳处理器开始清理资源 - 设备类型: {}", handlerType);
|
||||
shutdownExecutorGracefully();
|
||||
super.channelInactive(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动心跳定时任务
|
||||
* 每10秒发送一次心跳包,3秒后开始执行
|
||||
*
|
||||
* @param ctx Netty的通道上下文对象
|
||||
*/
|
||||
private void scheduleHeartbeat(ChannelHandlerContext ctx) {
|
||||
heartbeatFuture = heartbeatExecutor.scheduleAtFixedRate(() -> {
|
||||
if (ctx.channel().isActive()) {
|
||||
// 发送心跳包
|
||||
SocketMsg<String> msg = new SocketMsg<>();
|
||||
msg.setRequestId("yxt");
|
||||
msg.setOperateCode(SourceOperateCodeEnum.HEARTBEAT.getValue());
|
||||
msg.setData("");
|
||||
ctx.channel().writeAndFlush(JSON.toJSONString(msg) + "\n");
|
||||
|
||||
log.debug("心跳发送 - 设备类型: {}, 时间: {}", handlerType, LocalDateTime.now());
|
||||
consecutiveHeartbeatMisses++;
|
||||
if (consecutiveHeartbeatMisses >= MAX_HEARTBEAT_MISSES) {
|
||||
// 连续三次未收到心跳响应,异步处理断开逻辑,避免阻塞心跳线程
|
||||
log.warn("心跳响应超时 - 设备类型: {}, 连续失败次数: {}/{}, 执行断开连接",
|
||||
handlerType, consecutiveHeartbeatMisses, MAX_HEARTBEAT_MISSES);
|
||||
handleHeartbeatTimeoutAsync();
|
||||
consecutiveHeartbeatMisses = 0; // 重置连续心跳丢失次数
|
||||
}
|
||||
}
|
||||
}, 3, 10, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步处理心跳超时断开逻辑
|
||||
* <p>
|
||||
* 使用CompletableFuture避免阻塞心跳发送线程,确保心跳机制不受影响。
|
||||
* 处理流程:
|
||||
* 1. 异步发送退出指令
|
||||
* 2. 延迟3秒后清理Socket连接
|
||||
* 3. 记录处理过程和异常
|
||||
* </p>
|
||||
*/
|
||||
private void handleHeartbeatTimeoutAsync() {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
log.info("开始执行心跳超时断开处理 - 设备类型: {}", handlerType);
|
||||
// 根据设备类型发送对应的退出指令
|
||||
if (CnSocketUtil.DEV_TAG.equals(handlerType)) {
|
||||
CnSocketUtil.quitSend(param);
|
||||
} else if (CnSocketUtil.SOURCE_TAG.equals(handlerType)) {
|
||||
CnSocketUtil.quitSendSource(param);
|
||||
} else {
|
||||
if (FormalTestManager.currentStep == SourceOperateCodeEnum.RECORD_WAVE_STEP1) {
|
||||
CnSocketUtil.contrastSendquit(param.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_03, true);
|
||||
} else {
|
||||
CnSocketUtil.contrastSendquit(param.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_02, true);
|
||||
}
|
||||
}
|
||||
log.debug("退出指令已发送,等待3秒后清理连接 - 设备类型: {}", handlerType);
|
||||
} catch (Exception e) {
|
||||
log.error("心跳超时处理发送退出指令异常 - 设备类型: {}", handlerType, e);
|
||||
}
|
||||
}).thenRunAsync(() -> {
|
||||
try {
|
||||
// 延迟3秒后清理连接,给退出指令留出处理时间
|
||||
Thread.sleep(3000);
|
||||
// 构建连接Key并从SocketManager中移除
|
||||
String key = CnSocketUtil.DEV_TAG.equals(handlerType) ?
|
||||
param.getUserPageId() + CnSocketUtil.DEV_TAG :
|
||||
param.getUserPageId() + CnSocketUtil.SOURCE_TAG;
|
||||
SocketManager.removeUser(key);
|
||||
log.info("心跳超时断开处理完成 - 设备类型: {}, 连接已清理", handlerType);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.error("心跳超时处理等待过程中被中断 - 设备类型: {}", handlerType);
|
||||
} catch (Exception e) {
|
||||
log.error("心跳超时处理清理连接异常 - 设备类型: {}", handlerType, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 优雅关闭线程池执行器
|
||||
* <p>
|
||||
* 确保资源的完全清理,避免内存泄漏:
|
||||
* 1. 取消当前的心跳定时任务
|
||||
* 2. 关闭线程池并等待正在执行的任务完成
|
||||
* 3. 如果等待超时则强制关闭
|
||||
* 4. 处理中断异常并恢复中断状态
|
||||
* </p>
|
||||
*/
|
||||
private void shutdownExecutorGracefully() {
|
||||
try {
|
||||
// 1. 取消心跳定时任务
|
||||
if (heartbeatFuture != null && !heartbeatFuture.isCancelled()) {
|
||||
boolean cancelled = heartbeatFuture.cancel(false);
|
||||
log.debug("心跳定时任务取消结果: {} - 设备类型: {}", cancelled, handlerType);
|
||||
}
|
||||
// 2. 关闭线程池,不再接收新任务
|
||||
heartbeatExecutor.shutdown();
|
||||
// 3. 等待已提交的任务完成,最多等待5秒
|
||||
if (!heartbeatExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
|
||||
log.warn("心跳线程池未能在5秒内正常关闭,执行强制关闭 - 设备类型: {}", handlerType);
|
||||
heartbeatExecutor.shutdownNow();
|
||||
// 再次等待强制关闭完成,最多等待2秒
|
||||
if (!heartbeatExecutor.awaitTermination(2, TimeUnit.SECONDS)) {
|
||||
log.error("心跳线程池强制关闭失败 - 设备类型: {}", handlerType);
|
||||
}
|
||||
} else {
|
||||
log.debug("心跳线程池已优雅关闭 - 设备类型: {}", handlerType);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// 如果等待过程中被中断,立即强制关闭
|
||||
log.warn("心跳线程池关闭过程中被中断,执行强制关闭 - 设备类型: {}", handlerType);
|
||||
heartbeatExecutor.shutdownNow();
|
||||
// 恢复中断状态,遵循Java并发编程最佳实践
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息接收处理方法
|
||||
* <p>
|
||||
* 负责处理从服务端接收到的消息:
|
||||
* 1. 过滤心跳响应包,重置失败计数器
|
||||
* 2. 业务消息传递给后续处理器
|
||||
* </p>
|
||||
*
|
||||
* @param ctx Netty的通道上下文对象
|
||||
* @param msg 接收到的消息内容
|
||||
* @throws Exception 异常情况
|
||||
*/
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
|
||||
// 过滤心跳包,避免进入业务逻辑
|
||||
if (isHeartbeatPacket(msg)) {
|
||||
log.debug("心跳响应 - 设备类型: {}, 时间: {}", handlerType, LocalDateTime.now());
|
||||
// 重置连续失败计数器,表示连接正常
|
||||
consecutiveHeartbeatMisses = 0;
|
||||
return;
|
||||
}
|
||||
// 业务消息传递给管道中的后续处理器
|
||||
ctx.fireChannelRead(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为心跳数据包
|
||||
* <p>
|
||||
* 通过解析消息的operateCode字段来判断是否为心跳响应。
|
||||
* 心跳包的操作码为SourceOperateCodeEnum.HEARTBEAT。
|
||||
* </p>
|
||||
*
|
||||
* @param msg 需要判断的消息内容
|
||||
* @return true:心跳包, false:业务消息
|
||||
*/
|
||||
private boolean isHeartbeatPacket(String msg) {
|
||||
try {
|
||||
// 解析消息为SocketDataMsg对象
|
||||
SocketDataMsg socketDataMsg = MsgUtil.socketDataMsg(msg);
|
||||
// 检查操作码是否为心跳类型
|
||||
return socketDataMsg != null &&
|
||||
socketDataMsg.getOperateCode() != null &&
|
||||
socketDataMsg.getOperateCode().equals(SourceOperateCodeEnum.HEARTBEAT.getValue());
|
||||
} catch (Exception e) {
|
||||
// 消息解析失败,可能不是标准格式的心跳包
|
||||
log.debug("消息解析失败,可能不是心跳包: {}", msg, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,17 @@
|
||||
package com.njcn.gather.detection.util.socket.cilent;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.njcn.gather.detection.handler.SocketContrastResponseService;
|
||||
import com.njcn.gather.detection.handler.SocketDevResponseService;
|
||||
import com.njcn.gather.detection.handler.SocketSourceResponseService;
|
||||
import com.njcn.gather.detection.pojo.enums.SourceResponseCodeEnum;
|
||||
import com.njcn.gather.detection.pojo.param.ContrastDetectionParam;
|
||||
import com.njcn.gather.detection.pojo.param.PreDetectionParam;
|
||||
import com.njcn.gather.detection.pojo.vo.SocketDataMsg;
|
||||
import com.njcn.gather.detection.util.socket.CnSocketUtil;
|
||||
import com.njcn.gather.detection.util.socket.SocketManager;
|
||||
import com.njcn.gather.detection.util.socket.WebServiceManager;
|
||||
import com.njcn.gather.detection.util.socket.websocket.WebServiceManager;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
@@ -18,125 +21,486 @@ import io.netty.handler.codec.string.StringDecoder;
|
||||
import io.netty.handler.codec.string.StringEncoder;
|
||||
import io.netty.handler.timeout.IdleStateHandler;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Netty客户端工具类
|
||||
* 用于建立与检测设备和程控源设备的Socket通信连接
|
||||
* 支持心跳检测、断线重连和异常处理
|
||||
*
|
||||
* @Description: 心跳检测服务端 对应的服务端在netty-server 包下的NettyClient
|
||||
* @Author: wr
|
||||
* @Date: 2024/12/10 14:16
|
||||
*/
|
||||
|
||||
@Getter
|
||||
@Slf4j
|
||||
@Component
|
||||
public class NettyClient {
|
||||
|
||||
public static void socketClient(String ip, Integer port, PreDetectionParam param, String msg, SimpleChannelInboundHandler<String> handler) {
|
||||
NioEventLoopGroup group = new NioEventLoopGroup();
|
||||
Bootstrap bootstrap = new Bootstrap();
|
||||
try {
|
||||
bootstrap.group(group)
|
||||
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
|
||||
.channel(NioSocketChannel.class)
|
||||
.handler(new ChannelInitializer<NioSocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(NioSocketChannel ch) {
|
||||
if (handler instanceof NettySourceClientHandler) {
|
||||
ch.pipeline()
|
||||
//空闲状态的handler
|
||||
// 添加LineBasedFrameDecoder来按行分割数据
|
||||
.addLast(new LineBasedFrameDecoder(10240))
|
||||
.addLast(new IdleStateHandler(0, 10, 0, TimeUnit.SECONDS))
|
||||
.addLast(new StringDecoder(CharsetUtil.UTF_8))
|
||||
.addLast(new StringEncoder(CharsetUtil.UTF_8))
|
||||
.addLast(handler);
|
||||
} else {
|
||||
ch.pipeline()
|
||||
//空闲状态的handler
|
||||
// 添加LineBasedFrameDecoder来按行分割数据
|
||||
.addLast(new LineBasedFrameDecoder(10240))
|
||||
.addLast(new IdleStateHandler(60, 0, 0, TimeUnit.SECONDS))
|
||||
.addLast(new StringDecoder(CharsetUtil.UTF_8))
|
||||
.addLast(new StringEncoder(CharsetUtil.UTF_8))
|
||||
.addLast(handler);
|
||||
}
|
||||
@Resource
|
||||
private SocketSourceResponseService socketSourceResponseService;
|
||||
|
||||
}
|
||||
});
|
||||
ChannelFuture channelFuture = bootstrap.connect(ip, port).sync();
|
||||
channelFuture.addListener((ChannelFutureListener) ch -> {
|
||||
if (!ch.isSuccess()) {
|
||||
System.out.println("链接服务端失败...");
|
||||
} else {
|
||||
System.out.println("链接服务端成功...");
|
||||
System.out.println("客户端向服务端发送消息:"+msg);
|
||||
channelFuture.channel().writeAndFlush(msg+"\n");
|
||||
}
|
||||
});
|
||||
NioEventLoopGroup groupByUserId = SocketManager.getGroupByUserId(param.getUserPageId() + "_Dev");
|
||||
if(ObjectUtil.isNotNull(groupByUserId)){
|
||||
groupByUserId.shutdownGracefully();
|
||||
}else{
|
||||
if (handler instanceof NettySourceClientHandler) {
|
||||
SocketManager.addGroup(param.getUserPageId()+"_Source",group);
|
||||
}else{
|
||||
SocketManager.addGroup(param.getUserPageId()+"_Dev",group);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("连接socket服务端发送异常............" + e.getMessage());
|
||||
group.shutdownGracefully();
|
||||
//TODO 通知页面
|
||||
SocketDataMsg socketDataMsg = new SocketDataMsg();
|
||||
socketDataMsg.setType("aaa");
|
||||
socketDataMsg.setCode(SourceResponseCodeEnum.SOCKET_ERROR.getCode());
|
||||
socketDataMsg.setData(SourceResponseCodeEnum.SOCKET_ERROR.getMessage());
|
||||
socketDataMsg.setRequestId("connect");
|
||||
if (handler instanceof NettySourceClientHandler) {
|
||||
socketDataMsg.setOperateCode("Source");
|
||||
}else{
|
||||
CnSocketUtil.quitSendSource(param);
|
||||
socketDataMsg.setOperateCode("Dev");
|
||||
}
|
||||
WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg));
|
||||
} finally {
|
||||
// System.out.println("进入clientSocket最后步骤---------------------");
|
||||
@Resource
|
||||
private SocketDevResponseService socketDevResponseService;
|
||||
|
||||
@Resource
|
||||
private SocketContrastResponseService socketContrastResponseService;
|
||||
|
||||
/**
|
||||
* 静态实例,用于保持向后兼容
|
||||
*/
|
||||
private static NettyClient instance;
|
||||
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
instance = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建程控源Handler实例(Spring管理方式)
|
||||
* 自动注入SocketSourceResponseService,统一使用CnSocketUtil.SOURCE_TAG
|
||||
*
|
||||
* @param param 检测参数
|
||||
* @return NettySourceClientHandler 程控源处理器实例
|
||||
*/
|
||||
public NettySourceClientHandler createSourceHandler(PreDetectionParam param) {
|
||||
return new NettySourceClientHandler(param, socketSourceResponseService);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建被检设备Handler实例(Spring管理方式)
|
||||
* 自动注入SocketDevResponseService
|
||||
*
|
||||
* @param param 检测参数
|
||||
* @return NettyDevClientHandler 被检设备处理器实例
|
||||
*/
|
||||
public NettyDevClientHandler createDeviceHandler(PreDetectionParam param) {
|
||||
return new NettyDevClientHandler(param, socketDevResponseService);
|
||||
}
|
||||
|
||||
/**
|
||||
* 智能连接程控源设备(新增方法)
|
||||
* 自动创建Handler并建立连接
|
||||
*
|
||||
* @param ip 程控源IP地址
|
||||
* @param port 程控源端口
|
||||
* @param param 检测参数
|
||||
* @param msg 初始消息
|
||||
*/
|
||||
public void connectToSource(String ip, Integer port, PreDetectionParam param, String msg) {
|
||||
NettySourceClientHandler handler = createSourceHandler(param);
|
||||
executeSocketConnection(ip, port, param, msg, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 智能连接被检设备(新增方法)
|
||||
* 自动创建Handler并建立连接
|
||||
*
|
||||
* @param ip 被检设备IP地址
|
||||
* @param port 被检设备端口
|
||||
* @param param 检测参数
|
||||
* @param msg 初始消息
|
||||
*/
|
||||
public void connectToDevice(String ip, Integer port, PreDetectionParam param, String msg) {
|
||||
NettyDevClientHandler handler = createDeviceHandler(param);
|
||||
executeSocketConnection(ip, port, param, msg, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 智能连接比对被检设备(新增方法)
|
||||
* 自动创建Handler并建立连接
|
||||
*
|
||||
* @param ip 被检设备IP地址
|
||||
* @param port 被检设备端口
|
||||
* @param param 检测参数
|
||||
* @param msg 初始消息
|
||||
* 静态方法:智能连接程控源设备(兼容性包装)
|
||||
*/
|
||||
private void connectToContrast(String ip, Integer port, ContrastDetectionParam param, String msg) {
|
||||
PreDetectionParam preDetectionParam = new PreDetectionParam();
|
||||
preDetectionParam.setUserPageId(param.getLoginName());
|
||||
preDetectionParam.setTestItemList(param.getTestItemList());
|
||||
preDetectionParam.setDevIds(param.getDevIds());
|
||||
preDetectionParam.setUserId(param.getUserId());
|
||||
NettyContrastClientHandler handler = new NettyContrastClientHandler();
|
||||
handler.param = preDetectionParam;
|
||||
handler.socketContrastResponseService = socketContrastResponseService;
|
||||
executeSocketConnection(ip, port, preDetectionParam, msg, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态方法:智能连接程控源设备(兼容性包装)
|
||||
*/
|
||||
public static void connectToContrastDeviceStatic(String ip, Integer port, ContrastDetectionParam param, String msg) {
|
||||
if (instance != null) {
|
||||
instance.connectToContrast(ip, port, param, msg);
|
||||
} else {
|
||||
log.error("NettyClient未初始化,无法创建比对设备通讯连接");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 静态方法:智能连接程控源设备(兼容性包装)
|
||||
*/
|
||||
public static void connectToSourceStatic(String ip, Integer port, PreDetectionParam param, String msg) {
|
||||
if (instance != null) {
|
||||
instance.connectToSource(ip, port, param, msg);
|
||||
} else {
|
||||
log.error("NettyClient未初始化,无法创建程控源连接");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重连方法
|
||||
* 静态方法:智能连接被检设备(兼容性包装)
|
||||
*/
|
||||
public static void connect(Bootstrap bootstrap, String msg) {
|
||||
try {
|
||||
bootstrap.connect("127.0.0.1", 8787).sync()
|
||||
.addListener((ChannelFutureListener) ch -> {
|
||||
if (!ch.isSuccess()) {
|
||||
ch.channel().close();
|
||||
final EventLoop loop = ch.channel().eventLoop();
|
||||
loop.schedule(() -> {
|
||||
System.err.println("服务端链接不上,开始重连操作...");
|
||||
//重连
|
||||
connect(bootstrap, msg);
|
||||
}, 3L, TimeUnit.SECONDS);
|
||||
} else {
|
||||
if (StrUtil.isNotBlank(msg)) {
|
||||
ch.channel().writeAndFlush(msg);
|
||||
}
|
||||
System.out.println("服务端链接成功...");
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
System.out.println(e.getMessage());
|
||||
try {
|
||||
Thread.sleep(3000L);
|
||||
} catch (InterruptedException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
//再重连
|
||||
connect(bootstrap, msg);
|
||||
public static void connectToDeviceStatic(String ip, Integer port, PreDetectionParam param, String msg) {
|
||||
if (instance != null) {
|
||||
instance.connectToDevice(ip, port, param, msg);
|
||||
} else {
|
||||
log.error("NettyClient未初始化,无法创建被检设备连接");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 内部重构后的实现 - 拆分职责但不暴露给外部
|
||||
* 执行完整的Socket连接建立流程:
|
||||
* 1. 创建事件循环组
|
||||
* 2. 配置Bootstrap启动器
|
||||
* 3. 设置管道处理链
|
||||
* 4. 建立连接并处理结果
|
||||
*
|
||||
* @param ip 目标服务器IP地址
|
||||
* @param port 目标服务器端口号
|
||||
* @param param 检测参数对象
|
||||
* @param msg 连接成功后发送的初始消息
|
||||
* @param handler 业务处理器(区分程控源和被检设备)
|
||||
*/
|
||||
private static void executeSocketConnection(String ip, Integer port,
|
||||
PreDetectionParam param, String msg, SimpleChannelInboundHandler<String> handler) {
|
||||
// 创建NIO事件循环组,用于处理网络I/O事件
|
||||
NioEventLoopGroup group = createEventLoopGroup();
|
||||
|
||||
try {
|
||||
// 配置客户端启动器
|
||||
Bootstrap bootstrap = configureBootstrap(group);
|
||||
// 创建管道初始化器,配置编解码和业务处理链
|
||||
ChannelInitializer<NioSocketChannel> initializer = createChannelInitializer(param, handler);
|
||||
bootstrap.handler(initializer);
|
||||
// 同步连接到目标服务器
|
||||
ChannelFuture channelFuture = bootstrap.connect(ip, port).sync();
|
||||
// 处理连接结果(成功或失败)
|
||||
handleConnectionResult(channelFuture, param, handler, group, msg);
|
||||
} catch (Exception e) {
|
||||
// 处理连接过程中的异常
|
||||
handleConnectionException(e, param, handler, group);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建NIO事件循环组
|
||||
* 用于管理网络I/O操作的线程池,处理连接、读写等异步事件
|
||||
*
|
||||
* @return NioEventLoopGroup 事件循环组实例
|
||||
*/
|
||||
private static NioEventLoopGroup createEventLoopGroup() {
|
||||
return new NioEventLoopGroup();
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置Bootstrap客户端启动器
|
||||
* 设置连接超时、通道类型等基础参数
|
||||
*
|
||||
* @param group 事件循环组
|
||||
* @return Bootstrap 配置好的启动器
|
||||
*/
|
||||
private static Bootstrap configureBootstrap(NioEventLoopGroup group) {
|
||||
return new Bootstrap()
|
||||
// 绑定事件循环组
|
||||
.group(group)
|
||||
// 连接超时5秒
|
||||
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
|
||||
// 使用NIO Socket通道
|
||||
.channel(NioSocketChannel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建通道初始化器
|
||||
* 当新连接建立时,初始化该连接的处理管道
|
||||
*
|
||||
* @param param 检测参数,用于配置心跳处理器
|
||||
* @param handler 业务处理器
|
||||
* @return ChannelInitializer 通道初始化器
|
||||
*/
|
||||
private static ChannelInitializer<NioSocketChannel> createChannelInitializer(
|
||||
PreDetectionParam param, SimpleChannelInboundHandler<String> handler) {
|
||||
return new ChannelInitializer<NioSocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(NioSocketChannel ch) {
|
||||
setupPipeline(ch.pipeline(), param, handler);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置管道处理链
|
||||
* 按顺序添加各种处理器,构成完整的数据处理流水线:
|
||||
* 1. LineBasedFrameDecoder:按行分割数据,解决TCP粘包拆包问题
|
||||
* 2. StringDecoder/StringEncoder:字符串编解码器
|
||||
* 3. HeartbeatHandler:心跳处理器,维持连接活跃
|
||||
* 4. IdleStateHandler:空闲检测器(仅被检设备需要)
|
||||
* 5. 业务处理器:具体的业务逻辑处理
|
||||
*
|
||||
* @param pipeline 管道对象
|
||||
* @param param 检测参数
|
||||
* @param handler 业务处理器
|
||||
*/
|
||||
private static void setupPipeline(ChannelPipeline pipeline,
|
||||
PreDetectionParam param, SimpleChannelInboundHandler<String> handler) {
|
||||
// 基础编解码器:处理数据格式转换和粘包拆包
|
||||
// 按行分割,最大20KB
|
||||
pipeline.addLast(new LineBasedFrameDecoder(10240 * 2))
|
||||
// 字节转字符串
|
||||
.addLast(new StringDecoder(CharsetUtil.UTF_8))
|
||||
// 字符串转字节
|
||||
.addLast(new StringEncoder(CharsetUtil.UTF_8));
|
||||
|
||||
// 心跳处理器:根据设备类型选择不同的标签
|
||||
String tag = getDeviceTag(handler);
|
||||
pipeline.addLast(new HeartbeatHandler(param, tag));
|
||||
// 空闲检测器:仅被检设备和比对被检设备需要,60秒无读操作触发空闲事件
|
||||
if (!isSourceHandler(handler)) {
|
||||
pipeline.addLast(new IdleStateHandler(60, 0, 0, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
// 业务处理器:处理具体的检测业务逻辑
|
||||
pipeline.addLast(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为程控源处理器
|
||||
* 程控源设备和被检设备使用不同的处理器和配置
|
||||
*
|
||||
* @param handler 业务处理器
|
||||
* @return boolean true:程控源处理器, false:被检设备处理器
|
||||
*/
|
||||
private static boolean isSourceHandler(SimpleChannelInboundHandler<String> handler) {
|
||||
return handler instanceof NettySourceClientHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备标签
|
||||
* 用于在SocketManager中区分不同类型的设备连接
|
||||
*
|
||||
* @param handler 业务处理器
|
||||
* @return String 设备标签("_Source" 或 "_Dev")
|
||||
*/
|
||||
private static String getDeviceTag(SimpleChannelInboundHandler<String> handler) {
|
||||
String tag;
|
||||
if (handler instanceof NettySourceClientHandler) {
|
||||
tag = CnSocketUtil.SOURCE_TAG;
|
||||
} else if (handler instanceof NettyDevClientHandler) {
|
||||
tag = CnSocketUtil.DEV_TAG;
|
||||
} else {
|
||||
tag = CnSocketUtil.CONTRAST_DEV_TAG;
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取名称
|
||||
* 用于在SocketManager中区分不同类型的设备连接
|
||||
*
|
||||
* @param handler 业务处理器
|
||||
* @return String 设备标签("程控源设备" 或 "被检设备")
|
||||
*/
|
||||
private static String getDeviceType(SimpleChannelInboundHandler<String> handler) {
|
||||
String deviceType;
|
||||
if (handler instanceof NettySourceClientHandler) {
|
||||
deviceType = "程控源设备";
|
||||
} else if (handler instanceof NettyDevClientHandler) {
|
||||
deviceType = "被检设备";
|
||||
} else {
|
||||
deviceType = "比对被检设备";
|
||||
}
|
||||
return deviceType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理连接结果
|
||||
* 为连接Future添加监听器,异步处理连接成功或失败的情况
|
||||
*
|
||||
* @param channelFuture 连接Future对象
|
||||
* @param param 检测参数
|
||||
* @param handler 业务处理器
|
||||
* @param group 事件循环组
|
||||
* @param msg 初始消息
|
||||
*/
|
||||
private static void handleConnectionResult(ChannelFuture channelFuture,
|
||||
PreDetectionParam param, SimpleChannelInboundHandler<String> handler,
|
||||
NioEventLoopGroup group, String msg) {
|
||||
channelFuture.addListener((ChannelFutureListener) ch -> {
|
||||
if (!ch.isSuccess()) {
|
||||
onConnectionFailure(handler, group);
|
||||
} else {
|
||||
onConnectionSuccess(channelFuture, param, handler, group, msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接失败处理
|
||||
* 输出失败信息并优雅关闭事件循环组
|
||||
*
|
||||
* @param handler 业务处理器,用于区分设备类型
|
||||
* @param group 事件循环组
|
||||
*/
|
||||
private static void onConnectionFailure(SimpleChannelInboundHandler<String> handler, NioEventLoopGroup group) {
|
||||
String deviceType = getDeviceType(handler);
|
||||
log.info("连接{}服务端失败...", deviceType);
|
||||
group.shutdownGracefully();
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接成功处理
|
||||
* 执行连接成功后的初始化操作:
|
||||
* 1. 管理Socket连接会话(注册EventLoopGroup到SocketManager)
|
||||
* 2. 注册Channel到SocketManager,实现统一的连接管理
|
||||
* 3. 通过SocketManager发送初始消息,统一消息发送入口
|
||||
*
|
||||
* @param channelFuture 连接Future对象
|
||||
* @param param 检测参数
|
||||
* @param handler 业务处理器
|
||||
* @param group 事件循环组
|
||||
* @param msg 初始消息
|
||||
*/
|
||||
private static void onConnectionSuccess(ChannelFuture channelFuture,
|
||||
PreDetectionParam param, SimpleChannelInboundHandler<String> handler,
|
||||
NioEventLoopGroup group, String msg) {
|
||||
String deviceType = getDeviceType(handler);
|
||||
log.info("连接{}服务端成功...", deviceType);
|
||||
// 管理连接会话,将EventLoopGroup注册到SocketManager
|
||||
manageSocketConnection(param, handler, group);
|
||||
|
||||
// 将Channel也注册到SocketManager,便于统一消息发送
|
||||
String userId = param.getUserPageId() + getDeviceTag(handler);
|
||||
SocketManager.addUser(userId, channelFuture.channel());
|
||||
|
||||
// 通过SocketManager发送初始消息,统一消息发送入口
|
||||
SocketManager.sendMsg(userId, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理Socket连接会话
|
||||
* 将新建立的连接注册到SocketManager中进行统一管理:
|
||||
* 1. 检查并关闭同用户同设备类型的旧连接,避免资源泄露
|
||||
* 2. 将新连接注册到SocketManager,便于后续管理和查找
|
||||
* <p>
|
||||
* 连接Key格式:{userPageId}_{deviceTag}
|
||||
* 例如:zhangsan_test_Source(程控源) / zhangsan_test_Dev(被检设备)
|
||||
*
|
||||
* @param param 检测参数,包含用户页面ID
|
||||
* @param handler 业务处理器,用于区分设备类型
|
||||
* @param group 事件循环组,表示具体的连接资源
|
||||
*/
|
||||
private static void manageSocketConnection(PreDetectionParam param,
|
||||
SimpleChannelInboundHandler<String> handler, NioEventLoopGroup group) {
|
||||
// 构建连接标识:用户ID + 设备标签
|
||||
String key = param.getUserPageId() + getDeviceTag(handler);
|
||||
|
||||
// 关闭旧连接:同一用户同一设备类型只能有一个活跃连接
|
||||
NioEventLoopGroup existingGroup = SocketManager.getGroupByUserId(key);
|
||||
if (ObjectUtil.isNotNull(existingGroup)) {
|
||||
try {
|
||||
existingGroup.shutdownGracefully().sync();
|
||||
} catch (InterruptedException e) {
|
||||
// 恢复中断状态
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
// 注册新连接到SocketManager
|
||||
SocketManager.addGroup(key, group);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理连接异常
|
||||
* 当连接建立过程中发生异常时的统一处理流程:
|
||||
* 1. 关闭相关资源,防止资源泄露
|
||||
* 2. 执行设备相关的退出操作
|
||||
* 3. 通过WebSocket向前端通知错误信息
|
||||
*
|
||||
* @param e 异常对象
|
||||
* @param param 检测参数
|
||||
* @param handler 业务处理器
|
||||
* @param group 事件循环组
|
||||
*/
|
||||
private static void handleConnectionException(Exception e, PreDetectionParam param,
|
||||
SimpleChannelInboundHandler<String> handler, NioEventLoopGroup group) {
|
||||
log.info("连接socket服务端发送异常: {}", e.getMessage());
|
||||
|
||||
// 关闭事件循环组资源
|
||||
group.shutdownGracefully();
|
||||
|
||||
// 执行设备相关的退出操作
|
||||
executeQuitOperations(param, handler);
|
||||
|
||||
// 通过WebSocket通知前端页面
|
||||
notifyFrontendError(param, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行退出操作
|
||||
* 根据不同的设备处理器类型执行相应的退出指令:
|
||||
* - NettyDevClientHandler:被检设备处理器,需要发送程控源退出指令
|
||||
* - 其他非程控源处理器:发送通用退出指令
|
||||
* - NettySourceClientHandler:程控源处理器,无需额外退出操作
|
||||
*
|
||||
* @param param 检测参数
|
||||
* @param handler 业务处理器
|
||||
*/
|
||||
private static void executeQuitOperations(PreDetectionParam param,
|
||||
SimpleChannelInboundHandler<String> handler) {
|
||||
if (handler instanceof NettyDevClientHandler) {
|
||||
// 被检设备异常时,发送程控源退出指令
|
||||
CnSocketUtil.quitSendSource(param);
|
||||
}
|
||||
// 程控源处理器异常时无需额外操作
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知前端错误信息
|
||||
* 构建错误消息对象并通过WebSocket发送给前端页面
|
||||
* 前端可以根据操作码(Source/Dev)显示相应的错误提示
|
||||
*
|
||||
* @param param 检测参数,包含用户页面ID
|
||||
* @param handler 业务处理器,用于确定操作码
|
||||
*/
|
||||
private static void notifyFrontendError(PreDetectionParam param,
|
||||
SimpleChannelInboundHandler<String> handler) {
|
||||
// 构建错误消息对象
|
||||
SocketDataMsg socketDataMsg = new SocketDataMsg();
|
||||
// 消息类型
|
||||
socketDataMsg.setType("aaa");
|
||||
// 错误码
|
||||
socketDataMsg.setCode(SourceResponseCodeEnum.SOCKET_ERROR.getCode());
|
||||
// 错误消息
|
||||
socketDataMsg.setData(SourceResponseCodeEnum.SOCKET_ERROR.getMessage());
|
||||
// 请求ID标识
|
||||
socketDataMsg.setRequestId("connect");
|
||||
// 设置操作码:程控源为"Source",被检设备为"Dev"
|
||||
String devTag = getDeviceTag(handler).substring(1);
|
||||
socketDataMsg.setOperateCode(devTag);
|
||||
// 通过WebSocket发送错误信息到前端页面
|
||||
WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
package com.njcn.gather.detection.util.socket.cilent;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.njcn.gather.detection.handler.SocketContrastResponseService;
|
||||
import com.njcn.gather.detection.pojo.enums.SourceOperateCodeEnum;
|
||||
import com.njcn.gather.detection.pojo.enums.SourceResponseCodeEnum;
|
||||
import com.njcn.gather.detection.pojo.param.PreDetectionParam;
|
||||
import com.njcn.gather.detection.pojo.vo.SocketDataMsg;
|
||||
import com.njcn.gather.detection.util.socket.CnSocketUtil;
|
||||
import com.njcn.gather.detection.util.socket.FormalTestManager;
|
||||
import com.njcn.gather.detection.util.socket.MsgUtil;
|
||||
import com.njcn.gather.detection.util.socket.SocketManager;
|
||||
import com.njcn.gather.detection.util.socket.websocket.WebServiceManager;
|
||||
import com.njcn.gather.plan.pojo.enums.DataSourceEnum;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.timeout.IdleState;
|
||||
import io.netty.handler.timeout.IdleStateEvent;
|
||||
import io.netty.handler.timeout.TimeoutException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.ProtocolException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2025-07-25
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class NettyContrastClientHandler extends SimpleChannelInboundHandler<String> {
|
||||
|
||||
|
||||
public static PreDetectionParam param;
|
||||
public static SocketContrastResponseService socketContrastResponseService;
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
System.out.println("客户端通道已建立" + ctx.channel().id());
|
||||
Channel channel = SocketManager.getChannelByUserId(param.getUserPageId() + CnSocketUtil.CONTRAST_DEV_TAG);
|
||||
if (Objects.nonNull(channel)) {
|
||||
try {
|
||||
channel.close().sync();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
SocketManager.addUser(param.getUserPageId() + CnSocketUtil.CONTRAST_DEV_TAG, ctx.channel());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws InterruptedException {
|
||||
log.info("contrastdevhandler接收server端数据: {}", msg);
|
||||
try {
|
||||
socketContrastResponseService.deal(param, msg);
|
||||
} catch (Exception e) {
|
||||
log.error("处理服务端消息异常", e);
|
||||
// if (FormalTestManager.currentStep == SourceOperateCodeEnum.RECORD_WAVE_STEP1) {
|
||||
// CnSocketUtil.contrastSendquit(param.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_03, true);
|
||||
// } else {
|
||||
// CnSocketUtil.contrastSendquit(param.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_02, true);
|
||||
// }
|
||||
CnSocketUtil.contrastSendquit(param.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_01, false);
|
||||
CnSocketUtil.contrastSendquit(param.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_02, false);
|
||||
CnSocketUtil.contrastSendquit(param.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_03, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
System.out.println("与通信模块端断线");
|
||||
ctx.close();
|
||||
SocketManager.removeUser(param.getUserPageId() + CnSocketUtil.CONTRAST_DEV_TAG);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户事件的回调方法(自定义事件用于心跳机制)
|
||||
*
|
||||
* @param ctx
|
||||
* @param evt
|
||||
*/
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
|
||||
if (evt instanceof IdleStateEvent) { //IdleState.在一段时间内没有收到任何消息时,会触发该事件
|
||||
if (((IdleStateEvent) evt).state() == IdleState.READER_IDLE) {
|
||||
if (FormalTestManager.isTesting) {
|
||||
System.out.println(LocalDateTime.now() + "contrastClientHandler触发读超时函数**************************************");
|
||||
|
||||
if (!FormalTestManager.isRemoveSocket && ObjectUtil.isNotNull(FormalTestManager.nonWaveDataSourceEnum)) {
|
||||
long time = SocketManager.contrastClockMap.get(FormalTestManager.nonWaveDataSourceEnum) + 60L;
|
||||
SocketManager.contrastClockMap.put(FormalTestManager.nonWaveDataSourceEnum, time);
|
||||
|
||||
if (FormalTestManager.isPstData) {
|
||||
if (time > 60 * 10) {
|
||||
if (DataSourceEnum.REAL_DATA == FormalTestManager.nonWaveDataSourceEnum) {
|
||||
CnSocketUtil.contrastSendquit(param.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_02, true);
|
||||
}
|
||||
if (FormalTestManager.isWaveCheck) {
|
||||
CnSocketUtil.contrastSendquit(param.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_03, true);
|
||||
}
|
||||
CnSocketUtil.contrastSendquit(param.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_01, true);
|
||||
timeoutSend(SourceOperateCodeEnum.QUIT_INIT_01);
|
||||
}
|
||||
} else if (DataSourceEnum.REAL_DATA == FormalTestManager.nonWaveDataSourceEnum) {
|
||||
if (time >= 60) {
|
||||
CnSocketUtil.contrastSendquit(param.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_02, true);
|
||||
timeoutSend(SourceOperateCodeEnum.QUIT_INIT_02);
|
||||
}
|
||||
} else if (time >= FormalTestManager.maxTime) {
|
||||
CnSocketUtil.contrastSendquit(param.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_01, true);
|
||||
timeoutSend(SourceOperateCodeEnum.QUIT_INIT_01);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlerAdded(ChannelHandlerContext ctx) {
|
||||
System.out.println("有通道准备接入" + ctx.channel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
System.out.println("捕获到通信模块服务异常。。。。。。。");
|
||||
// 处理异常,例如记录日志、关闭连接等
|
||||
cause.printStackTrace();
|
||||
// 根据异常类型进行不同的处理
|
||||
if (cause instanceof ConnectException) {
|
||||
System.out.println("连接socket服务端异常");
|
||||
} else if (cause instanceof IOException) {
|
||||
System.out.println("IOException caught: There was an I/O error.");
|
||||
WebServiceManager.sendDetectionErrorMessage(param.getUserPageId(), SourceOperateCodeEnum.DEVICE_ERROR);
|
||||
} else if (cause instanceof TimeoutException) {
|
||||
System.out.println("TimeoutException caught: Operation timed out.");
|
||||
} else if (cause instanceof ProtocolException) {
|
||||
System.out.println("ProtocolException caught: Invalid protocol message.");
|
||||
} else {
|
||||
// 处理其他类型的异常
|
||||
System.out.println("Unknown exception caught: " + cause.getMessage());
|
||||
WebServiceManager.sendDetectionErrorMessage(param.getUserPageId(), SourceOperateCodeEnum.DEVICE_ERROR);
|
||||
}
|
||||
CnSocketUtil.contrastSendquit(param.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_01, false);
|
||||
CnSocketUtil.contrastSendquit(param.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_02, false);
|
||||
CnSocketUtil.contrastSendquit(param.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_03, true);
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 接收数据超时处理
|
||||
*/
|
||||
private void timeoutSend(SourceOperateCodeEnum sourceOperateCodeEnum) {
|
||||
System.out.println("超时处理-----》" + "统计数据已超时----------------关闭");
|
||||
// 向前端推送超时消息
|
||||
SocketDataMsg webSend = new SocketDataMsg();
|
||||
webSend.setRequestId(sourceOperateCodeEnum.getValue());
|
||||
webSend.setData(sourceOperateCodeEnum.getMsg() + SourceResponseCodeEnum.RECEIVE_DATA_TIME_OUT.getMessage());
|
||||
webSend.setCode(SourceResponseCodeEnum.RECEIVE_DATA_TIME_OUT.getCode());
|
||||
WebServiceManager.sendMsg(param.getUserPageId(), MsgUtil.msgToWebData(webSend, FormalTestManager.devNameMapComm, 0));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,11 +3,15 @@ package com.njcn.gather.detection.util.socket.cilent;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.njcn.gather.detection.handler.SocketDevResponseService;
|
||||
import com.njcn.gather.detection.pojo.enums.ResultEnum;
|
||||
import com.njcn.gather.detection.pojo.enums.SourceOperateCodeEnum;
|
||||
import com.njcn.gather.detection.pojo.param.PreDetectionParam;
|
||||
import com.njcn.gather.detection.pojo.vo.DevLineTestResult;
|
||||
import com.njcn.gather.detection.pojo.vo.WebSocketVO;
|
||||
import com.njcn.gather.detection.util.socket.*;
|
||||
import com.njcn.gather.detection.util.socket.CnSocketUtil;
|
||||
import com.njcn.gather.detection.util.socket.FormalTestManager;
|
||||
import com.njcn.gather.detection.util.socket.SocketManager;
|
||||
import com.njcn.gather.detection.util.socket.websocket.WebServiceManager;
|
||||
import com.njcn.gather.device.pojo.vo.PreDetection;
|
||||
import com.njcn.gather.script.pojo.po.SourceIssue;
|
||||
import com.njcn.gather.system.pojo.enums.DicDataEnum;
|
||||
@@ -18,25 +22,55 @@ import io.netty.handler.timeout.IdleState;
|
||||
import io.netty.handler.timeout.IdleStateEvent;
|
||||
import io.netty.handler.timeout.TimeoutException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.ProtocolException;
|
||||
import java.util.ArrayList;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
/**
|
||||
* @Description: 源客户端业务处理(示例)
|
||||
* Netty设备客户端处理器
|
||||
* <p>负责处理与被检测设备的Socket通信,包括:</p>
|
||||
* <ul>
|
||||
* <li>通道生命周期管理(建立、断开)</li>
|
||||
* <li>消息接收和处理</li>
|
||||
* <li>心跳超时处理</li>
|
||||
* <li>异常处理和恢复</li>
|
||||
* </ul>
|
||||
*
|
||||
* @Description: 设备客户端业务处理器
|
||||
* @Author: wr
|
||||
* @Date: 2024/12/10 14:16
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class NettyDevClientHandler extends SimpleChannelInboundHandler<String> {
|
||||
|
||||
private final String dev = "_Dev";
|
||||
/**
|
||||
* 闪变检测超时时间:20分钟(1300秒)
|
||||
*/
|
||||
private static final long FLICKER_TIMEOUT = 1300L;
|
||||
|
||||
/**
|
||||
* 统计数据检测超时时间:3分钟(180秒)
|
||||
*/
|
||||
private static final long STATISTICS_TIMEOUT = 180L;
|
||||
|
||||
/**
|
||||
* 实时数据检测超时时间:1分钟(60秒)
|
||||
*/
|
||||
private static final long REALTIME_TIMEOUT = 60L;
|
||||
|
||||
/**
|
||||
* 暂停操作超时时间:10分钟(600秒)
|
||||
*/
|
||||
private static final long STOP_TIMEOUT = 600L;
|
||||
|
||||
private final PreDetectionParam param;
|
||||
|
||||
@@ -44,187 +78,269 @@ public class NettyDevClientHandler extends SimpleChannelInboundHandler<String> {
|
||||
|
||||
|
||||
/**
|
||||
* 当通道进行连接时推送消息
|
||||
* 当通道连接建立时的处理逻辑
|
||||
* <p>将关闭原有连接,并将新连接注册到SocketManager中</p>
|
||||
*
|
||||
* @param ctx
|
||||
* @param ctx 通道上下文
|
||||
* @throws Exception 连接异常
|
||||
*/
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
System.out.println("客户端通道已建立" + ctx.channel().id());
|
||||
Channel channel = SocketManager.getChannelByUserId(param.getUserPageId() + dev);
|
||||
log.info("客户端通道已建立: {}", ctx.channel().id());
|
||||
|
||||
// 检查是否存在同一用户的老连接
|
||||
Channel channel = SocketManager.getChannelByUserId(param.getUserPageId() + CnSocketUtil.DEV_TAG);
|
||||
if (Objects.nonNull(channel)) {
|
||||
try {
|
||||
// 关闭老连接避免连接泄漏
|
||||
channel.close().sync();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
log.error("关闭通道异常", e);
|
||||
}
|
||||
}
|
||||
SocketManager.addUser(param.getUserPageId() + dev, ctx.channel());
|
||||
// 注册新的连接到用户管理器
|
||||
SocketManager.addUser(param.getUserPageId() + CnSocketUtil.DEV_TAG, ctx.channel());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理服务端消息消息信息
|
||||
*/
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws InterruptedException {
|
||||
System.out.println("devhandler接收server端数据>>>>>>" + msg);
|
||||
try {
|
||||
socketResponseService.deal(param, msg);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
CnSocketUtil.quitSend(param);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 当通道断线时,支持重连
|
||||
* 当通道断开时的清理工作
|
||||
* <p>关闭连接,清理用户映射,退出源设备发送</p>
|
||||
*
|
||||
* @param ctx
|
||||
* @param ctx 通道上下文
|
||||
* @throws Exception 关闭异常
|
||||
*/
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
System.out.println("设备通讯客户端断线");
|
||||
log.warn("设备通讯客户端断线");
|
||||
ctx.close();
|
||||
SocketManager.removeUser(param.getUserPageId() + dev);
|
||||
SocketManager.removeUser(param.getUserPageId() + CnSocketUtil.DEV_TAG);
|
||||
CnSocketUtil.quitSendSource(param);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户事件的回调方法(自定义事件用于心跳机制)
|
||||
* 处理从服务端接收到的消息
|
||||
* <p>将消息交给SocketDevResponseService进行具体处理</p>
|
||||
*
|
||||
* @param ctx
|
||||
* @param evt
|
||||
* @param ctx 通道上下文
|
||||
* @param msg 接收到的消息
|
||||
*/
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
|
||||
Boolean fly = false;
|
||||
if (evt instanceof IdleStateEvent) {
|
||||
if (((IdleStateEvent) evt).state() == IdleState.READER_IDLE) {
|
||||
if(!FormalTestManager.hasStopFlag) {
|
||||
if (CollUtil.isNotEmpty(SocketManager.getSourceList())) {
|
||||
SourceIssue sourceIssue = SocketManager.getSourceList().get(0);
|
||||
if (SocketManager.clockMap.containsKey(sourceIssue.getIndex())) {
|
||||
SocketManager.clockMap.put(sourceIssue.getIndex(), SocketManager.clockMap.get(sourceIssue.getIndex()) + 60L);
|
||||
} else {
|
||||
SocketManager.clockMap.put(sourceIssue.getIndex(), 60L);
|
||||
}
|
||||
|
||||
if (sourceIssue.getType().equals(DicDataEnum.F.getCode())) {
|
||||
//闪变,正常抛一轮最大等待20分钟超时
|
||||
if (SocketManager.clockMap.get(sourceIssue.getIndex()) > 1300) {
|
||||
fly=true;
|
||||
System.out.println("超时处理-----》" + sourceIssue.getType() + "已超时----------------关闭");
|
||||
CnSocketUtil.quitSend(param);
|
||||
timeoutSend(sourceIssue);
|
||||
}
|
||||
} else if (sourceIssue.getType().equals(DicDataEnum.VOLTAGE.getCode()) || sourceIssue.getType().equals(DicDataEnum.HP.getCode())) {
|
||||
//统计数据项,正常抛一轮数据,超时
|
||||
if (SocketManager.clockMap.get(sourceIssue.getIndex()) > 180) {
|
||||
fly=true;
|
||||
CnSocketUtil.quitSend(param);
|
||||
System.out.println("超时处理-----》" + sourceIssue.getType() + "已超时----------------关闭");
|
||||
timeoutSend(sourceIssue);
|
||||
}
|
||||
|
||||
} else {
|
||||
//实时数据
|
||||
if (SocketManager.clockMap.get(sourceIssue.getIndex()) > 60) {
|
||||
fly=true;
|
||||
CnSocketUtil.quitSend(param);
|
||||
System.out.println("超时处理-----》" + sourceIssue.getType() + "已超时----------------关闭");
|
||||
timeoutSend(sourceIssue);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fly=true;
|
||||
//为空则认为是常规步骤,设定一分钟超时
|
||||
CnSocketUtil.quitSend(param);
|
||||
}
|
||||
if(fly){
|
||||
socketResponseService.backCheckState(param);
|
||||
}
|
||||
System.out.println("已经等了一分钟了。。。。。。。。。。。。");
|
||||
}else {
|
||||
//如果是暂停操作后
|
||||
FormalTestManager.stopTime+=60;
|
||||
System.out.println("当前进入暂停操作超时函数-----------------"+FormalTestManager.stopTime);
|
||||
if(FormalTestManager.stopTime > 600){
|
||||
CnSocketUtil.quitSend(param);
|
||||
CnSocketUtil.sendToWebSocket(param.getUserPageId(),SourceOperateCodeEnum.FORMAL_REAL.getValue(),SourceOperateCodeEnum.STOP_TIMEOUT.getValue(),SourceOperateCodeEnum.STOP_TIMEOUT.getMsg(),null);
|
||||
}
|
||||
}
|
||||
}
|
||||
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
|
||||
log.info("devhandler接收server端数据: {}", msg);
|
||||
try {
|
||||
socketResponseService.deal(param, msg);
|
||||
} catch (Exception e) {
|
||||
log.error("处理服务端消息异常", e);
|
||||
CnSocketUtil.quitSend(param);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void handlerAdded(ChannelHandlerContext ctx) {
|
||||
System.out.println("有通道准备接入" + ctx.channel());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
System.out.println("捕获到设备服务异常。。。。。。。");
|
||||
// 处理异常,例如记录日志、关闭连接等
|
||||
cause.printStackTrace();
|
||||
// 根据异常类型进行不同的处理
|
||||
if (cause instanceof ConnectException) {
|
||||
// 处理连接异常,例如重试连接或记录特定的连接错误信息
|
||||
System.out.println("连接socket服务端异常");
|
||||
|
||||
} else if (cause instanceof IOException) {
|
||||
// 处理I/O异常,例如读写错误
|
||||
System.out.println("IOException caught: There was an I/O error.");
|
||||
CnSocketUtil.sendToWebSocket(param.getUserPageId(),SourceOperateCodeEnum.SERVER_ERROR.getValue(),SourceOperateCodeEnum.SERVER_ERROR.getValue(),SourceOperateCodeEnum.SERVER_ERROR.getMsg(),null );
|
||||
|
||||
// 例如,可以记录更详细的I/O错误信息
|
||||
} else if (cause instanceof TimeoutException) {
|
||||
// 处理超时异常
|
||||
System.out.println("TimeoutException caught: Operation timed out.");
|
||||
// 可以根据业务逻辑决定是否重试或记录超时信息
|
||||
} else if (cause instanceof ProtocolException) {
|
||||
// 处理协议异常,例如消息格式不正确
|
||||
System.out.println("ProtocolException caught: Invalid protocol message.");
|
||||
// 可以记录协议错误信息或向客户端发送错误响应
|
||||
} else {
|
||||
// 处理其他类型的异常
|
||||
System.out.println("Unknown exception caught: " + cause.getMessage());
|
||||
// 可以记录未知异常信息
|
||||
}
|
||||
CnSocketUtil.quitSend(param);
|
||||
socketResponseService.backCheckState(param);
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 用户事件回调方法,主要用于处理心跳超时
|
||||
* <p>当触发READER_IDLE事件时,根据当前状态进行超时处理</p>
|
||||
*
|
||||
* @param ctx 通道上下文
|
||||
* @param evt 用户事件对象
|
||||
*/
|
||||
private void timeoutSend(SourceIssue sourceIssue){
|
||||
List<DevLineTestResult> devListRes = new ArrayList<>();
|
||||
FormalTestManager.devList.forEach(dev -> {
|
||||
DevLineTestResult devLineTestResult = new DevLineTestResult();
|
||||
devLineTestResult.setDeviceId(dev.getDevId());
|
||||
devLineTestResult.setDeviceName(dev.getDevName());
|
||||
List<Integer> resultFlagList = new ArrayList<>();
|
||||
List<PreDetection.MonitorListDTO> monitorListDTOList = dev.getMonitorList();
|
||||
monitorListDTOList.forEach(i -> resultFlagList.add(3));
|
||||
devLineTestResult.setChnResult(resultFlagList.toArray(new Integer[monitorListDTOList.size()]));
|
||||
devListRes.add(devLineTestResult);
|
||||
});
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
|
||||
// 检查是否为读取空闲事件(心跳超时)
|
||||
if (evt instanceof IdleStateEvent && ((IdleStateEvent) evt).state() == IdleState.READER_IDLE) {
|
||||
log.warn("devHandler触发读超时函数: {}", LocalDateTime.now());
|
||||
// 根据是否有停止标志采取不同的超时处理策略
|
||||
if (!FormalTestManager.hasStopFlag) {
|
||||
// 正常检测中的超时处理
|
||||
handleReadTimeout();
|
||||
} else {
|
||||
// 暂停状态下的超时处理
|
||||
handleStopTimeout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理器被添加到管道时的回调
|
||||
*
|
||||
* @param ctx 通道上下文
|
||||
*/
|
||||
@Override
|
||||
public void handlerAdded(ChannelHandlerContext ctx) {
|
||||
log.info("有通道准备接入: {}", ctx.channel());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 异常捕获处理
|
||||
* <p>捕获并处理各种类型的异常,执行清理工作</p>
|
||||
*
|
||||
* @param ctx 通道上下文
|
||||
* @param cause 异常原因
|
||||
*/
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
log.error("捕获到设备服务异常", cause);
|
||||
handleSpecificException(cause);
|
||||
// 统一清理工作
|
||||
CnSocketUtil.quitSend(param);
|
||||
CnSocketUtil.quitSendSource(param);
|
||||
socketResponseService.backCheckState(param);
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理特定类型的异常
|
||||
* <p>根据异常类型进行相应的日志记录和错误通知</p>
|
||||
*
|
||||
* @param cause 异常对象
|
||||
*/
|
||||
private void handleSpecificException(Throwable cause) {
|
||||
if (cause instanceof ConnectException) {
|
||||
log.error("连接socket服务端异常");
|
||||
} else if (cause instanceof IOException) {
|
||||
log.error("IO异常: {}", cause.getMessage());
|
||||
WebServiceManager.sendDetectionErrorMessage(param.getUserPageId(), SourceOperateCodeEnum.DEVICE_ERROR);
|
||||
} else if (cause instanceof TimeoutException) {
|
||||
log.error("操作超时: {}", cause.getMessage());
|
||||
} else if (cause instanceof ProtocolException) {
|
||||
log.error("协议异常: {}", cause.getMessage());
|
||||
} else {
|
||||
log.error("未知异常: {}", cause.getMessage());
|
||||
WebServiceManager.sendDetectionErrorMessage(param.getUserPageId(), SourceOperateCodeEnum.DEVICE_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理读取超时事件
|
||||
* <p>检查源列表,更新超时计数器,判断是否超时并处理</p>
|
||||
*/
|
||||
private void handleReadTimeout() {
|
||||
if (CollUtil.isNotEmpty(SocketManager.getSourceList())) {
|
||||
// 获取当前正在检测的源问题(取第一个)
|
||||
SourceIssue sourceIssue = SocketManager.getSourceList().get(0);
|
||||
// 更新该源问题的超时计数器
|
||||
updateTimeoutCounter(sourceIssue);
|
||||
// 根据检测类型判断是否已超时
|
||||
if (isTimeout(sourceIssue)) {
|
||||
handleTimeout(sourceIssue);
|
||||
}
|
||||
} else {
|
||||
// 源列表为空,认为是常规步骤的超时,默认一分钟超时
|
||||
CnSocketUtil.quitSend(param);
|
||||
WebServiceManager.sendDetectionErrorMessage(param.getUserPageId(), SourceOperateCodeEnum.SOCKET_TIMEOUT);
|
||||
socketResponseService.backCheckState(param);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理暂停操作的超时事件
|
||||
* <p>当检测被暂停时,统计暂停时间,超过限制后发送超时通知</p>
|
||||
*/
|
||||
private void handleStopTimeout() {
|
||||
FormalTestManager.stopTime += 60;
|
||||
log.warn("当前进入暂停操作超时函数,停止时间: {}", FormalTestManager.stopTime);
|
||||
if (FormalTestManager.stopTime > STOP_TIMEOUT) {
|
||||
CnSocketUtil.quitSend(param);
|
||||
WebServiceManager.sendDetectionErrorMessage(param.getUserPageId(), SourceOperateCodeEnum.FORMAL_REAL.getValue(), SourceOperateCodeEnum.STOP_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新指定源问题的超时计数器
|
||||
* <p>每次调用时增加60秒,用于统计累计超时时间</p>
|
||||
*
|
||||
* @param sourceIssue 源问题对象
|
||||
*/
|
||||
private void updateTimeoutCounter(SourceIssue sourceIssue) {
|
||||
Integer index = sourceIssue.getIndex();
|
||||
if (SocketManager.clockMap.containsKey(index)) {
|
||||
SocketManager.clockMap.put(index, SocketManager.clockMap.get(index) + 60L);
|
||||
} else {
|
||||
SocketManager.clockMap.put(index, 60L);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据检测类型判断是否已超时
|
||||
* <p>不同检测类型有不同的超时阈值:</p>
|
||||
* <ul>
|
||||
* <li>闪变检测:20分钟</li>
|
||||
* <li>统计数据:3分钟</li>
|
||||
* <li>实时数据:1分钟</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param sourceIssue 源问题对象
|
||||
* @return true 如果已超时,false 否则
|
||||
*/
|
||||
private boolean isTimeout(SourceIssue sourceIssue) {
|
||||
long currentTime = SocketManager.clockMap.get(sourceIssue.getIndex());
|
||||
String type = sourceIssue.getType();
|
||||
|
||||
// 根据不同检测类型使用不同的超时阈值
|
||||
if (DicDataEnum.F.getCode().equals(type)) {
|
||||
// 闪变检测:需要更长时间,20分钟超时
|
||||
return currentTime >= FLICKER_TIMEOUT;
|
||||
} else if (DicDataEnum.VOLTAGE.getCode().equals(type) || DicDataEnum.HP.getCode().equals(type)) {
|
||||
// 统计数据类型(电压、谐波):中等时间,3分钟超时
|
||||
return currentTime >= STATISTICS_TIMEOUT;
|
||||
} else {
|
||||
// 实时数据类型:短时间,1分钟超时
|
||||
return currentTime >= REALTIME_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行超时处理操作
|
||||
* <p>记录超时日志,退出发送,发送超时结果,恢复检测状态</p>
|
||||
*
|
||||
* @param sourceIssue 源问题对象
|
||||
*/
|
||||
private void handleTimeout(SourceIssue sourceIssue) {
|
||||
log.warn("超时处理 - {} 已超时,关闭连接", sourceIssue.getType());
|
||||
CnSocketUtil.quitSend(param);
|
||||
timeoutSend(sourceIssue);
|
||||
socketResponseService.backCheckState(param);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送超时结果
|
||||
* <p>为所有设备创建超时的检测结果,并通过WebSocket发送给客户端</p>
|
||||
*
|
||||
* @param sourceIssue 源问题对象
|
||||
*/
|
||||
private void timeoutSend(SourceIssue sourceIssue) {
|
||||
List<DevLineTestResult> devListRes = FormalTestManager.devList.stream()
|
||||
.map(this::createTimeoutResult)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
WebSocketVO<List<DevLineTestResult>> socketVO = new WebSocketVO<>();
|
||||
socketVO.setRequestId(sourceIssue.getType()+"_End");
|
||||
socketVO.setRequestId(sourceIssue.getType() + CnSocketUtil.END_TAG);
|
||||
socketVO.setOperateCode(sourceIssue.getType());
|
||||
socketVO.setData(devListRes);
|
||||
|
||||
WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketVO));
|
||||
}
|
||||
|
||||
/**
|
||||
* 为指定设备创建超时的检测结果
|
||||
* <p>将所有监测点的结果设置为超时标志值</p>
|
||||
*
|
||||
* @param dev 设备对象
|
||||
* @return 设备检测结果
|
||||
*/
|
||||
private DevLineTestResult createTimeoutResult(PreDetection dev) {
|
||||
DevLineTestResult devLineTestResult = new DevLineTestResult();
|
||||
devLineTestResult.setDeviceId(dev.getDevId());
|
||||
devLineTestResult.setDeviceName(dev.getDevName());
|
||||
|
||||
Integer[] resultFlags = dev.getMonitorList().stream()
|
||||
.map(monitor -> ResultEnum.NETWORK_TIMEOUT.getValue())
|
||||
.toArray(Integer[]::new);
|
||||
devLineTestResult.setChnResult(resultFlags);
|
||||
|
||||
return devLineTestResult;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,145 +1,140 @@
|
||||
package com.njcn.gather.detection.util.socket.cilent;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.njcn.gather.detection.handler.SocketSourceResponseService;
|
||||
import com.njcn.gather.detection.pojo.enums.SourceOperateCodeEnum;
|
||||
import com.njcn.gather.detection.pojo.param.PreDetectionParam;
|
||||
import com.njcn.gather.detection.pojo.vo.SocketDataMsg;
|
||||
import com.njcn.gather.detection.pojo.vo.SocketMsg;
|
||||
import com.njcn.gather.detection.pojo.vo.WebSocketVO;
|
||||
import com.njcn.gather.detection.util.socket.CnSocketUtil;
|
||||
import com.njcn.gather.detection.util.socket.MsgUtil;
|
||||
import com.njcn.gather.detection.util.socket.SocketManager;
|
||||
import io.netty.channel.Channel;
|
||||
import com.njcn.gather.detection.util.socket.websocket.WebServiceManager;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.timeout.IdleState;
|
||||
import io.netty.handler.timeout.IdleStateEvent;
|
||||
import io.netty.handler.timeout.TimeoutException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.ProtocolException;
|
||||
import java.util.Objects;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
|
||||
/**
|
||||
* @Description: 源客户端业务处理(示例)
|
||||
* @Author: wr
|
||||
* @Date: 2024/12/10 14:16
|
||||
* 源设备Netty客户端通道处理器
|
||||
* 负责处理程控源设备的Socket通信
|
||||
*
|
||||
* @author wr
|
||||
* @since 2024/12/10
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class NettySourceClientHandler extends SimpleChannelInboundHandler<String> {
|
||||
|
||||
/** 检测参数对象,包含用户页面ID等信息 */
|
||||
private final PreDetectionParam webUser;
|
||||
private final String sourceTag = "_Source";
|
||||
/** 源设备响应处理服务 */
|
||||
private final SocketSourceResponseService sourceResponseService;
|
||||
|
||||
/**
|
||||
* 当通道进行连接时推送消息
|
||||
*
|
||||
* @param ctx
|
||||
* 通道激活回调,将通道注册到SocketManager
|
||||
*/
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
System.out.println("客户端通道已建立" + ctx.channel().id());
|
||||
|
||||
SocketManager.addUser(webUser.getUserPageId() + sourceTag, ctx.channel());
|
||||
// 验证webUser参数有效性
|
||||
if (webUser == null) {
|
||||
log.warn("源设备客户端通道已建立但webUser为空, channelId: {}", ctx.channel().id());
|
||||
return;
|
||||
}
|
||||
|
||||
String userId = webUser.getUserPageId();
|
||||
log.info("源设备客户端通道已建立, channelId: {}, userId: {}", ctx.channel().id(), userId);
|
||||
|
||||
// 将通道注册到Socket管理器,便于后续消息推送
|
||||
if (StrUtil.isNotBlank(userId)) {
|
||||
SocketManager.addUser(userId + CnSocketUtil.SOURCE_TAG, ctx.channel());
|
||||
} else {
|
||||
log.warn("源设备userId为空或空白,跳过通道注册, channelId: {}", ctx.channel().id());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理服务端消息信息
|
||||
* 通道断开回调,清理资源
|
||||
*/
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
String userId = webUser != null ? webUser.getUserPageId() : "unknown";
|
||||
log.warn("源通讯客户端断线, channelId: {}, userId: {}", ctx.channel().id(), userId);
|
||||
// 关闭通道连接
|
||||
ctx.close();
|
||||
// 从Socket管理器中移除用户通道映射
|
||||
if (webUser != null && StrUtil.isNotBlank(userId)) {
|
||||
SocketManager.removeUser(userId + CnSocketUtil.SOURCE_TAG);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理源设备响应消息
|
||||
*/
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws InterruptedException {
|
||||
System.out.println("source接收server端数据>>>>>>" + msg);
|
||||
// 验证用户参数
|
||||
if (webUser == null) {
|
||||
log.warn("源设备消息处理失败: webUser为空, message: {}", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
String userId = webUser.getUserPageId();
|
||||
log.debug("源设备接收服务端数据, userId: {}, message: {}", userId, msg);
|
||||
|
||||
try {
|
||||
// 委托给专门的响应处理服务处理业务逻辑
|
||||
sourceResponseService.deal(webUser, msg);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.error("源设备消息处理异常, userId: {}, message: {}", userId, msg, e);
|
||||
// 发生异常时退出发送,避免后续问题
|
||||
CnSocketUtil.quitSend(webUser);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 当通道断线时,支持重连
|
||||
*
|
||||
* @param ctx
|
||||
*/
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception{
|
||||
System.out.println("源通讯客户端断线");
|
||||
ctx.close();
|
||||
SocketManager.removeUser(webUser.getUserPageId()+sourceTag);
|
||||
// System.out.println("断线了......" + ctx.channel());
|
||||
// ctx.channel().eventLoop().schedule(() -> {
|
||||
// System.out.println("断线重连......");
|
||||
// //重连
|
||||
// NettyClient.connect();
|
||||
// }, 3L, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户事件的回调方法(自定义事件用于心跳机制)
|
||||
*
|
||||
* @param ctx
|
||||
* @param evt
|
||||
*/
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
|
||||
//如果是空闲状态事件
|
||||
if (evt instanceof IdleStateEvent) {
|
||||
if (((IdleStateEvent) evt).state() == IdleState.WRITER_IDLE) {
|
||||
//发送ping 保持心跳链接
|
||||
SocketMsg msg = new SocketMsg();
|
||||
msg.setRequestId("yxt");
|
||||
msg.setOperateCode(SourceOperateCodeEnum.HEARTBEAT.getValue());
|
||||
msg.setData("");
|
||||
ctx.writeAndFlush(JSON.toJSONString(msg)+"\n");
|
||||
}
|
||||
} else {
|
||||
//防止堆栈溢出
|
||||
//userEventTriggered(ctx, evt);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlerAdded(ChannelHandlerContext ctx) {
|
||||
System.out.println("有通道准备接入" + ctx.channel().id());
|
||||
// 记录处理器添加事件,用于调试
|
||||
String userId = webUser != null ? webUser.getUserPageId() : "unknown";
|
||||
log.debug("源设备通道准备接入, channelId: {}, userId: {}", ctx.channel().id(), userId);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 处理异常,例如记录日志、关闭连接等
|
||||
System.out.println("捕获到源异常。。。。。。。");
|
||||
cause.printStackTrace();
|
||||
// 根据异常类型进行不同的处理
|
||||
String userId = webUser != null ? webUser.getUserPageId() : "unknown";
|
||||
String channelId = ctx.channel().id().toString();
|
||||
|
||||
// 根据异常类型进行分类处理和日志记录
|
||||
if (cause instanceof ConnectException) {
|
||||
// 处理连接异常,例如重试连接或记录特定的连接错误信息
|
||||
System.out.println("连接socket服务端异常");
|
||||
|
||||
// 连接异常:网络连接失败
|
||||
log.error("连接源设备Socket服务端异常, channelId: {}, userId: {}", channelId, userId, cause);
|
||||
} else if (cause instanceof IOException) {
|
||||
// 处理I/O异常,例如读写错误
|
||||
CnSocketUtil.sendToWebSocket(webUser.getUserPageId(),SourceOperateCodeEnum.SERVER_ERROR.getValue(),SourceOperateCodeEnum.SERVER_ERROR.getValue(),SourceOperateCodeEnum.SERVER_ERROR.getMsg(),null );
|
||||
// 例如,可以记录更详细的I/O错误信息
|
||||
// IO异常:数据传输错误,需通知前端
|
||||
log.error("源设备IO异常, channelId: {}, userId: {}", channelId, userId, cause);
|
||||
// 向前端发送服务器错误消息
|
||||
if (StrUtil.isNotBlank(userId) && !"unknown".equals(userId)) {
|
||||
WebServiceManager.sendDetectionErrorMessage(userId, SourceOperateCodeEnum.SERVER_ERROR);
|
||||
}
|
||||
} else if (cause instanceof TimeoutException) {
|
||||
// 处理超时异常
|
||||
System.out.println("TimeoutException caught: Operation timed out.");
|
||||
// 可以根据业务逻辑决定是否重试或记录超时信息
|
||||
// 超时异常:通信响应超时
|
||||
log.warn("源设备通信超时, channelId: {}, userId: {}", channelId, userId, cause);
|
||||
} else if (cause instanceof ProtocolException) {
|
||||
// 处理协议异常,例如消息格式不正确
|
||||
System.out.println("ProtocolException caught: Invalid protocol message.");
|
||||
// 可以记录协议错误信息或向客户端发送错误响应
|
||||
// 协议异常:数据格式不符合协议规范
|
||||
log.error("源设备协议异常, channelId: {}, userId: {}", channelId, userId, cause);
|
||||
} else {
|
||||
// 处理其他类型的异常
|
||||
System.out.println("Unknown exception caught: " + cause.getMessage());
|
||||
// 可以记录未知异常信息
|
||||
// 其他未知异常
|
||||
log.error("源设备未知异常, channelId: {}, userId: {}, message: {}", channelId, userId, cause.getMessage(), cause);
|
||||
}
|
||||
|
||||
// 发生异常时关闭通道
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
package com.njcn.gather.detection.util.socket.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Socket连接配置管理类
|
||||
* 定义哪些requestId需要建立通道连接,以及IP/PORT配置
|
||||
*
|
||||
* @Author: hongawen
|
||||
* @Date: 2024/12/10
|
||||
*/
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "socket")
|
||||
public class SocketConnectionConfig {
|
||||
|
||||
/**
|
||||
* 程控源设备配置
|
||||
*/
|
||||
private SourceConfig source = new SourceConfig();
|
||||
|
||||
/**
|
||||
* 被检设备配置
|
||||
*/
|
||||
private DeviceConfig device = new DeviceConfig();
|
||||
|
||||
@Data
|
||||
public static class SourceConfig {
|
||||
/**
|
||||
* 程控源IP地址
|
||||
*/
|
||||
private String ip;
|
||||
|
||||
/**
|
||||
* 程控源端口号
|
||||
*/
|
||||
private Integer port;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class DeviceConfig {
|
||||
/**
|
||||
* 被检设备IP地址
|
||||
*/
|
||||
private String ip;
|
||||
|
||||
/**
|
||||
* 被检设备端口号
|
||||
*/
|
||||
private Integer port;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取程控源配置
|
||||
*/
|
||||
public SourceConfig getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取被检设备配置
|
||||
*/
|
||||
public DeviceConfig getDevice() {
|
||||
return device;
|
||||
}
|
||||
|
||||
/**
|
||||
* 需要建立程控源通道的requestId集合
|
||||
* 这些requestId在发送消息时,如果程控源通道不存在,会自动建立连接
|
||||
*/
|
||||
private static final Set<String> SOURCE_CONNECTION_REQUEST_IDS = new HashSet<>(Arrays.asList(
|
||||
// 源通讯检测
|
||||
"yjc_ytxjy"
|
||||
// 可以根据实际业务需求添加更多requestId
|
||||
));
|
||||
|
||||
/**
|
||||
* 需要建立被检设备通道的requestId集合
|
||||
* 这些requestId在发送消息时,如果被检设备通道不存在,会自动建立连接
|
||||
*/
|
||||
private static final Set<String> DEVICE_CONNECTION_REQUEST_IDS = new HashSet<>(Arrays.asList(
|
||||
// 连接建立
|
||||
"yjc_sbtxjy",
|
||||
// ftp文件传送指令
|
||||
"FTP_SEND$01"
|
||||
// 可以根据实际业务需求添加更多requestId
|
||||
));
|
||||
|
||||
/**
|
||||
* 检查指定的requestId是否需要建立程控源连接
|
||||
*
|
||||
* @param requestId 请求ID
|
||||
* @return boolean true:需要建立连接, false:不需要建立连接
|
||||
*/
|
||||
public static boolean needsSourceConnection(String requestId) {
|
||||
return SOURCE_CONNECTION_REQUEST_IDS.contains(requestId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查指定的requestId是否需要建立被检设备连接
|
||||
*
|
||||
* @param requestId 请求ID
|
||||
* @return boolean true:需要建立连接, false:不需要建立连接
|
||||
*/
|
||||
public static boolean needsDeviceConnection(String requestId) {
|
||||
return DEVICE_CONNECTION_REQUEST_IDS.contains(requestId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加需要建立程控源连接的requestId
|
||||
* 支持运行时动态添加
|
||||
*
|
||||
* @param requestId 请求ID
|
||||
*/
|
||||
public static void addSourceConnectionRequestId(String requestId) {
|
||||
SOURCE_CONNECTION_REQUEST_IDS.add(requestId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加需要建立被检设备连接的requestId
|
||||
* 支持运行时动态添加
|
||||
*
|
||||
* @param requestId 请求ID
|
||||
*/
|
||||
public static void addDeviceConnectionRequestId(String requestId) {
|
||||
DEVICE_CONNECTION_REQUEST_IDS.add(requestId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除程控源连接requestId
|
||||
*
|
||||
* @param requestId 请求ID
|
||||
*/
|
||||
public static void removeSourceConnectionRequestId(String requestId) {
|
||||
SOURCE_CONNECTION_REQUEST_IDS.remove(requestId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除被检设备连接requestId
|
||||
*
|
||||
* @param requestId 请求ID
|
||||
*/
|
||||
public static void removeDeviceConnectionRequestId(String requestId) {
|
||||
DEVICE_CONNECTION_REQUEST_IDS.remove(requestId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有需要建立程控源连接的requestId集合(只读)
|
||||
*
|
||||
* @return Set<String> requestId集合
|
||||
*/
|
||||
public static Set<String> getSourceConnectionRequestIds() {
|
||||
return new HashSet<>(SOURCE_CONNECTION_REQUEST_IDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有需要建立被检设备连接的requestId集合(只读)
|
||||
*
|
||||
* @return Set<String> requestId集合
|
||||
*/
|
||||
public static Set<String> getDeviceConnectionRequestIds() {
|
||||
return new HashSet<>(DEVICE_CONNECTION_REQUEST_IDS);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package com.njcn.gather.detection.util.socket.service;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.njcn.gather.detection.pojo.enums.SourceOperateCodeEnum;
|
||||
import com.njcn.gather.detection.pojo.vo.SocketDataMsg;
|
||||
import com.njcn.gather.detection.util.socket.MsgUtil;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @Description: 客户端业务处理
|
||||
* @Author: wr
|
||||
* @Date: 2024/12/10 14:18
|
||||
*/
|
||||
|
||||
public class DevNettyServerHandler extends SimpleChannelInboundHandler<String> {
|
||||
|
||||
private final String DEV = "_Dev";
|
||||
private final String source = "_Source";
|
||||
|
||||
private final String stepTag = "&&";
|
||||
private final String stepBegin = "_Start";
|
||||
private final String stepEnd = "_End";
|
||||
|
||||
|
||||
public static final DevNettyServerHandler INSTANCE = new DevNettyServerHandler();
|
||||
|
||||
/**
|
||||
* @Description: 当通道进行连接时推送消息
|
||||
* @Author: wr
|
||||
* @Date: 2024/12/10 14:19
|
||||
*/
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
System.out.println("服务端监听到" + ctx.channel().id() + "连接");
|
||||
super.channelActive(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理消息信息
|
||||
*/
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, String msg) {
|
||||
System.out.println(ctx.channel().id() + "NettyServer服务端接收到客户端消息:" + msg);
|
||||
Channel channel = ctx.channel();
|
||||
SocketDataMsg socketDataMsg = MsgUtil.socketDataMsg(msg);
|
||||
String[] tem = socketDataMsg.getRequestId().split(stepTag);
|
||||
SourceOperateCodeEnum sourceOperateCodeEnum = SourceOperateCodeEnum.getDictDataEnumByCode(tem[0]);
|
||||
|
||||
switch (Objects.requireNonNull(sourceOperateCodeEnum)) {
|
||||
//设备通讯校验
|
||||
case YJC_SBTXJY:
|
||||
String aa = "{\"requestId\":\"yjc_sbtxjy\",\"operateCode\":\"INIT_GATHER$01\",\"data\":null,\"code\":10201}";
|
||||
String bb = "{\"requestId\":\"yjc_sbtxjy\",\"operateCode\":\"INIT_GATHER$01\",\"data\":null,\"code\":10201}";
|
||||
String cc = "{\"requestId\":\"yjc_sbtxjy\",\"operateCode\":\"INIT_GATHER$01\",\"data\":\"192.168.1.237_1\",\"code\":10200}";
|
||||
channel.writeAndFlush(aa+"\n");
|
||||
channel.writeAndFlush(bb+"\n");
|
||||
channel.writeAndFlush(cc+"\n");
|
||||
break;
|
||||
//协议校验
|
||||
case YJC_XYJY:
|
||||
String dd="{\"requestId\":\"yjc_xyjy\",\"operateCode\":\"INIT_GATHER$02\",\"data\":null,\"code\":10201}";
|
||||
String ee = "{\"requestId\":\"yjc_xyjy\",\"operateCode\":\"INIT_GATHER$02\",\"data\":\"192.168.1.237_1\",\"code\":10200}";
|
||||
channel.writeAndFlush(dd+"\n");
|
||||
channel.writeAndFlush(ee+"\n");
|
||||
|
||||
String ff = "{\"requestId\":\"yjc_xyjy\",\"operateCode\":\"INIT_GATHER$03\",\"data\":null,\"code\":10201}";
|
||||
String gg ="{\"requestId\":\"yjc_xyjy\",\"operateCode\":\"INIT_GATHER$03\",\"data\":\"192.168.1.237_1\",\"code\":10200}";
|
||||
channel.writeAndFlush(ff+"\n");
|
||||
channel.writeAndFlush(gg+"\n");
|
||||
break;
|
||||
//相序校验
|
||||
case YJC_XUJY:
|
||||
String hh = "{\"requestId\":\"YJC_xujy\",\"operateCode\":\"DATA_REQUEST$02\",\"data\":null,\"code\":10201}";
|
||||
String ii = "{\"requestId\":\"YJC_xujy\",\"operateCode\":\"DATA_REQUEST$02\",\"data\":\"{\\\"Time\\\":\\\"2025-02-14T15:09:06.004\\\",\\\"ID\\\":\\\"192.168.1.237_1\\\",\\\"result\\\":false,\\\"SqlData\\\":[{\\\"type\\\":\\\"real\\\",\\\"desc\\\":\\\"VRMS\\\",\\\"list\\\":{\\\"A\\\":\\\"59.969920\\\",\\\"B\\\":\\\"49.963360\\\",\\\"C\\\":\\\"39.971470\\\",\\\"T\\\":null}},{\\\"type\\\":\\\"real\\\",\\\"desc\\\":\\\"VA\\\",\\\"list\\\":{\\\"A\\\":\\\"0.000000\\\",\\\"B\\\":\\\"-119.864000\\\",\\\"C\\\":\\\"120.052000\\\",\\\"T\\\":null}},{\\\"type\\\":\\\"real\\\",\\\"desc\\\":\\\"IRMS\\\",\\\"list\\\":{\\\"A\\\":\\\"1.000979\\\",\\\"B\\\":\\\"1.998939\\\",\\\"C\\\":\\\"3.000807\\\",\\\"T\\\":null}},{\\\"type\\\":\\\"real\\\",\\\"desc\\\":\\\"IA\\\",\\\"list\\\":{\\\"A\\\":\\\"-0.038000\\\",\\\"B\\\":\\\"-120.052000\\\",\\\"C\\\":\\\"119.941000\\\",\\\"T\\\":null}}],\\\"SqlDataHarm\\\":[]}\",\"code\":10200}\n";
|
||||
channel.writeAndFlush(hh+"\n");
|
||||
channel.writeAndFlush(ii+"\n");
|
||||
break;
|
||||
//正式检测
|
||||
case FORMAL_REAL:
|
||||
|
||||
break;
|
||||
//系数校验
|
||||
case Coefficient_Check:
|
||||
String a = "{\"requestId\":\"Coefficient_Check\",\"operateCode\":\"DATA_CHNFACTOR$01\",\"data\":null,\"code\":10201}";
|
||||
String b = "{\"requestId\":\"Coefficient_Check\",\"operateCode\":\"DATA_CHNFACTOR$01\",\"data\":\"[{\\\"uMonitorPoint\\\":0,\\\"f\\\":{\\\"Ua_gain\\\":9999,\\\"Ub_gain\\\":10008,\\\"Uc_gain\\\":10000,\\\"U0_gain\\\":10000,\\\"Ia_gain\\\":9997,\\\"Ib_gain\\\":9997,\\\"Ic_gain\\\":10001,\\\"I0_gain\\\":10000,\\\"Uab_gain\\\":9999,\\\"Ubc_gain\\\":10008,\\\"Uca_gain\\\":10000}}]\",\"code\":10200}";
|
||||
channel.writeAndFlush(a+"\n");
|
||||
channel.writeAndFlush(b+"\n");
|
||||
|
||||
switch (Objects.requireNonNull(sourceOperateCodeEnum)) {
|
||||
case DATA_CHNFACTOR$01:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
//退出关闭
|
||||
case QUITE:
|
||||
// quitDeal(socketDataMsg, param, msg);
|
||||
break;
|
||||
case YXT:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlerAdded(ChannelHandlerContext ctx) {
|
||||
System.out.println("有新连接加入了++++......" + ctx.channel());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
cause.printStackTrace();
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
// Client disconnected, close the server-side channel too
|
||||
System.out.println("NettyServer服务端监听到" + ctx.channel().id() + "关闭连接");
|
||||
ctx.close(); // This will trigger the close event on the server side
|
||||
super.channelInactive(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户事件的回调方法(自定义事件用于心跳机制)
|
||||
*/
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
|
||||
//空闲状态的事件
|
||||
// if (evt instanceof IdleStateEvent) {
|
||||
// IdleStateEvent event = (IdleStateEvent) evt;
|
||||
// System.out.println(event.state() + ">>>" + ctx.channel().id());
|
||||
// //已经10秒钟没有读时间了
|
||||
// if (event.state().equals(IdleState.READER_IDLE)){
|
||||
// // 心跳包丢失,10秒没有收到客户端心跳 (断开连接)
|
||||
// ctx.channel().close().sync();
|
||||
// System.out.println("已与 "+ctx.channel().remoteAddress()+" 断开连接");
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,6 +6,11 @@ import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.ServerSocketChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.codec.LineBasedFrameDecoder;
|
||||
import io.netty.handler.codec.string.StringDecoder;
|
||||
import io.netty.handler.codec.string.StringEncoder;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
|
||||
/**
|
||||
@@ -25,11 +30,17 @@ public class NettyServer {
|
||||
|
||||
public static void main(String[] args) {
|
||||
NettyServer nettyServer = new NettyServer();
|
||||
nettyServer.run();
|
||||
nettyServer.runSource();
|
||||
}
|
||||
|
||||
|
||||
private void run() {
|
||||
public static void sourceMain(String[] args) {
|
||||
NettyServer nettyServer = new NettyServer();
|
||||
nettyServer.runDev();
|
||||
}
|
||||
|
||||
|
||||
private void runSource() {
|
||||
NioEventLoopGroup boss = new NioEventLoopGroup(1);
|
||||
NioEventLoopGroup work = new NioEventLoopGroup();
|
||||
try {
|
||||
@@ -39,19 +50,30 @@ public class NettyServer {
|
||||
.handler(new ChannelInitializer<ServerSocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(ServerSocketChannel ch) {
|
||||
System.out.println("服务正在启动中......");
|
||||
System.out.println("源通讯服务正在启动中......");
|
||||
}
|
||||
})
|
||||
//业务处理
|
||||
.childHandler(NettyServerChannelInitializer.INSTANCE);
|
||||
.childHandler(new ChannelInitializer<NioSocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(NioSocketChannel ch) {
|
||||
ch.pipeline()
|
||||
//空闲状态的handler
|
||||
// 添加LineBasedFrameDecoder来按行分割数据
|
||||
.addLast(new LineBasedFrameDecoder(10240*2))
|
||||
.addLast(new StringDecoder(CharsetUtil.UTF_8))
|
||||
.addLast(new StringEncoder(CharsetUtil.UTF_8))
|
||||
.addLast(new DevNettyServerHandler());
|
||||
}
|
||||
});
|
||||
|
||||
ChannelFuture future = bootstrap.bind(port).sync();
|
||||
|
||||
future.addListener(f -> {
|
||||
if (future.isSuccess()) {
|
||||
System.out.println("服务启动成功");
|
||||
System.out.println("源通讯服务启动成功");
|
||||
} else {
|
||||
System.out.println("服务启动失败");
|
||||
System.out.println("源通讯服务启动失败");
|
||||
}
|
||||
});
|
||||
future.channel().closeFuture().sync();
|
||||
@@ -63,4 +85,52 @@ public class NettyServer {
|
||||
work.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void runDev() {
|
||||
NioEventLoopGroup boss = new NioEventLoopGroup(1);
|
||||
NioEventLoopGroup work = new NioEventLoopGroup();
|
||||
try {
|
||||
ServerBootstrap bootstrap = new ServerBootstrap().group(boss, work);
|
||||
bootstrap.channel(NioServerSocketChannel.class)
|
||||
//这个处理器可以不写
|
||||
.handler(new ChannelInitializer<ServerSocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(ServerSocketChannel ch) {
|
||||
System.out.println("设备通讯服务正在启动中......");
|
||||
}
|
||||
})
|
||||
//业务处理
|
||||
.childHandler(new ChannelInitializer<NioSocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(NioSocketChannel ch) {
|
||||
ch.pipeline()
|
||||
//空闲状态的handler
|
||||
// 添加LineBasedFrameDecoder来按行分割数据
|
||||
.addLast(new LineBasedFrameDecoder(10240*2))
|
||||
.addLast(new StringDecoder(CharsetUtil.UTF_8))
|
||||
.addLast(new StringEncoder(CharsetUtil.UTF_8))
|
||||
.addLast(new SourceNettyServerHandler());
|
||||
}
|
||||
});
|
||||
|
||||
ChannelFuture future = bootstrap.bind(port).sync();
|
||||
|
||||
future.addListener(f -> {
|
||||
if (future.isSuccess()) {
|
||||
System.out.println("设备通讯服务启动成功");
|
||||
} else {
|
||||
System.out.println("设备通讯服务启动失败");
|
||||
}
|
||||
});
|
||||
future.channel().closeFuture().sync();
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
boss.shutdownGracefully();
|
||||
work.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ public class NettyServerChannelInitializer extends ChannelInitializer<NioSocketC
|
||||
// .addLast(new IdleStateHandler(10, 0, 0, TimeUnit.SECONDS))
|
||||
.addLast(new StringDecoder(CharsetUtil.UTF_8))
|
||||
.addLast(new StringEncoder(CharsetUtil.UTF_8))
|
||||
.addLast(new NettyServerHandler());
|
||||
.addLast(new DevNettyServerHandler());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
package com.njcn.gather.detection.util.socket.service;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
|
||||
/**
|
||||
* @Description: 客户端业务处理
|
||||
* @Author: wr
|
||||
* @Date: 2024/12/10 14:18
|
||||
*/
|
||||
|
||||
public class NettyServerHandler extends SimpleChannelInboundHandler<String> {
|
||||
|
||||
public static final NettyServerHandler INSTANCE = new NettyServerHandler();
|
||||
|
||||
/**
|
||||
* @Description: 当通道进行连接时推送消息
|
||||
* @Author: wr
|
||||
* @Date: 2024/12/10 14:19
|
||||
*/
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
System.out.println("服务端监听到"+ctx.channel().id()+"连接");
|
||||
super.channelActive(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理消息信息
|
||||
*/
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, String msg) {
|
||||
System.out.println(ctx.channel().id()+"NettyServer服务端接收到客户端消息:"+msg);
|
||||
Channel channel = ctx.channel();
|
||||
/*
|
||||
if(msg.endsWith("结束")) {
|
||||
channel.writeAndFlush("socket指令结果:"+msg);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
channel.writeAndFlush("socket指令结果:"+msg);
|
||||
}
|
||||
}else{
|
||||
channel.writeAndFlush("socket指令结果:成功指令");
|
||||
}*/
|
||||
if(!msg.contains("HeartBeat")){
|
||||
if(msg.contains("start")){
|
||||
JSONObject jsonObject1 = new JSONObject();
|
||||
jsonObject1.put("requestId","yjc_ytxjy");
|
||||
jsonObject1.put("operateCode","INIT_GATHER");
|
||||
jsonObject1.put("code","10201");
|
||||
channel.writeAndFlush(jsonObject1.toJSONString()+'\n');
|
||||
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("requestId","yjc_ytxjy");
|
||||
jsonObject.put("operateCode","INIT_GATHER");
|
||||
jsonObject.put("code","10200");
|
||||
channel.writeAndFlush(jsonObject.toJSONString()+'\n');
|
||||
}else if(msg.contains("YJC_xujy")){
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("requestId","YJC_xujy");
|
||||
jsonObject.put("operateCode","OPER_GATHER");
|
||||
jsonObject.put("code","10201");
|
||||
channel.writeAndFlush(jsonObject.toJSONString()+'\n');
|
||||
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
JSONObject jsonObject1 = new JSONObject();
|
||||
jsonObject1.put("requestId","YJC_xujy");
|
||||
jsonObject1.put("operateCode","OPER_GATHER");
|
||||
jsonObject1.put("code","10200");
|
||||
channel.writeAndFlush(jsonObject1.toJSONString()+'\n');
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlerAdded(ChannelHandlerContext ctx) {
|
||||
System.out.println("有新连接加入了++++......" + ctx.channel());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
cause.printStackTrace();
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
// Client disconnected, close the server-side channel too
|
||||
System.out.println("NettyServer服务端监听到"+ctx.channel().id()+"关闭连接");
|
||||
ctx.close(); // This will trigger the close event on the server side
|
||||
super.channelInactive(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户事件的回调方法(自定义事件用于心跳机制)
|
||||
*/
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
|
||||
//空闲状态的事件
|
||||
// if (evt instanceof IdleStateEvent) {
|
||||
// IdleStateEvent event = (IdleStateEvent) evt;
|
||||
// System.out.println(event.state() + ">>>" + ctx.channel().id());
|
||||
// //已经10秒钟没有读时间了
|
||||
// if (event.state().equals(IdleState.READER_IDLE)){
|
||||
// // 心跳包丢失,10秒没有收到客户端心跳 (断开连接)
|
||||
// ctx.channel().close().sync();
|
||||
// System.out.println("已与 "+ctx.channel().remoteAddress()+" 断开连接");
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package com.njcn.gather.detection.util.socket.service;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.njcn.gather.detection.pojo.enums.SourceOperateCodeEnum;
|
||||
import com.njcn.gather.detection.pojo.vo.SocketDataMsg;
|
||||
import com.njcn.gather.detection.util.socket.MsgUtil;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
|
||||
/**
|
||||
* @Description: 客户端业务处理
|
||||
* @Author: wr
|
||||
* @Date: 2024/12/10 14:18
|
||||
*/
|
||||
|
||||
public class SourceNettyServerHandler extends SimpleChannelInboundHandler<String> {
|
||||
|
||||
private final String DEV = "_Dev";
|
||||
private final String source = "_Source";
|
||||
|
||||
private final String stepTag = "&&";
|
||||
private final String stepBegin = "_Start";
|
||||
private final String stepEnd = "_End";
|
||||
|
||||
public static final SourceNettyServerHandler INSTANCE = new SourceNettyServerHandler();
|
||||
|
||||
/**
|
||||
* @Description: 当通道进行连接时推送消息
|
||||
* @Author: wr
|
||||
* @Date: 2024/12/10 14:19
|
||||
*/
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
System.out.println("服务端监听到" + ctx.channel().id() + "连接");
|
||||
super.channelActive(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理消息信息
|
||||
*/
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, String msg) {
|
||||
System.out.println(ctx.channel().id() + "NettyServer服务端接收到客户端消息:" + msg);
|
||||
Channel channel = ctx.channel();
|
||||
SocketDataMsg socketDataMsg = MsgUtil.socketDataMsg(msg);
|
||||
String[] tem = socketDataMsg.getRequestId().split(stepTag);
|
||||
SourceOperateCodeEnum enumByCode = SourceOperateCodeEnum.getDictDataEnumByCode(tem[0]);
|
||||
JSONObject jsonObject1 = new JSONObject();
|
||||
if (ObjectUtil.isNotNull(enumByCode)) {
|
||||
switch (enumByCode) {
|
||||
//源初始化
|
||||
case YJC_YTXJY:
|
||||
String aa = "{\"requestId\":\"yjc_ytxjy\",\"operateCode\":\"INIT_GATHER\",\"data\":null,\"code\":10201}";
|
||||
String bb = "{\"requestId\":\"yjc_ytxjy\",\"operateCode\":\"INIT_GATHER\",\"data\":null,\"code\":10200}";
|
||||
channel.writeAndFlush(aa+"/n");
|
||||
channel.writeAndFlush(bb+"/n");
|
||||
break;
|
||||
//相序检测
|
||||
case YJC_XUJY:
|
||||
String hh = "{\"requestId\":\"YJC_xujy\",\"operateCode\":\"OPER_GATHER\",\"data\":null,\"code\":10201}";
|
||||
String ii = "{\"requestId\":\"YJC_xujy\",\"operateCode\":\"OPER_GATHER\",\"data\":null,\"code\":10200}";
|
||||
channel.writeAndFlush(hh+"\n");
|
||||
channel.writeAndFlush(ii+"\n");
|
||||
break;
|
||||
//正式检测
|
||||
case FORMAL_REAL:
|
||||
|
||||
break;
|
||||
//系数校验
|
||||
case Coefficient_Check:
|
||||
String a = "{\"requestId\":\"Coefficient_Check\",\"operateCode\":\"OPER_GATHER\",\"data\":null,\"code\":10201}";
|
||||
String b= "{\"requestId\":\"Coefficient_Check\",\"operateCode\":\"OPER_GATHER\",\"data\":null,\"code\":10200}";
|
||||
|
||||
break;
|
||||
case QUITE_SOURCE:
|
||||
|
||||
break;
|
||||
case YXT:
|
||||
jsonObject1.put("requestId", "yjc_ytxjy");
|
||||
jsonObject1.put("operateCode", "INIT_GATHER");
|
||||
jsonObject1.put("code", "10201");
|
||||
channel.writeAndFlush(jsonObject1.toJSONString() + '\n');
|
||||
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
System.out.println("未知异常" + enumByCode);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlerAdded(ChannelHandlerContext ctx) {
|
||||
System.out.println("有新连接加入了++++......" + ctx.channel());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
cause.printStackTrace();
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
// Client disconnected, close the server-side channel too
|
||||
System.out.println("NettyServer服务端监听到" + ctx.channel().id() + "关闭连接");
|
||||
ctx.close(); // This will trigger the close event on the server side
|
||||
super.channelInactive(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户事件的回调方法(自定义事件用于心跳机制)
|
||||
*/
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
|
||||
//空闲状态的事件
|
||||
// if (evt instanceof IdleStateEvent) {
|
||||
// IdleStateEvent event = (IdleStateEvent) evt;
|
||||
// System.out.println(event.state() + ">>>" + ctx.channel().id());
|
||||
// //已经10秒钟没有读时间了
|
||||
// if (event.state().equals(IdleState.READER_IDLE)){
|
||||
// // 心跳包丢失,10秒没有收到客户端心跳 (断开连接)
|
||||
// ctx.channel().close().sync();
|
||||
// System.out.println("已与 "+ctx.channel().remoteAddress()+" 断开连接");
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
package com.njcn.gather.detection.util.socket.web;
|
||||
|
||||
import com.njcn.gather.detection.pojo.param.PreDetectionParam;
|
||||
import com.njcn.gather.detection.util.socket.CnSocketUtil;
|
||||
import com.njcn.gather.detection.util.socket.SocketManager;
|
||||
import com.njcn.gather.detection.util.socket.WebServiceManager;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
||||
import io.netty.handler.timeout.IdleStateEvent;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
||||
/**
|
||||
* @Description: 泛型 代表的是处理数据的单位
|
||||
* TextWebSocketFrame : 文本信息帧
|
||||
* @Author: wr
|
||||
* @Date: 2024/12/10 13:56
|
||||
*/
|
||||
|
||||
|
||||
@Slf4j
|
||||
public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
|
||||
|
||||
private int times;
|
||||
|
||||
private final static String QUESTION_MARK = "?";
|
||||
private final static String EQUAL_TO = "=";
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
System.out.println("webSocket服务端通道已建立" + ctx.channel().id());
|
||||
super.channelActive(ctx);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
//首次连接是FullHttpRequest,把用户id和对应的channel对象存储起来
|
||||
if (null != msg && msg instanceof FullHttpRequest) {
|
||||
FullHttpRequest request = (FullHttpRequest) msg;
|
||||
String uri = request.uri();
|
||||
String userId = getUrlParams(uri);
|
||||
WebServiceManager.addUser(userId, ctx.channel());
|
||||
log.info("登录的用户id是:{}", userId);
|
||||
//如果url包含参数,需要处理
|
||||
if (uri.contains(QUESTION_MARK)) {
|
||||
String newUri = uri.substring(0, uri.indexOf(QUESTION_MARK));
|
||||
request.setUri(newUri);
|
||||
}
|
||||
|
||||
} else if (msg instanceof TextWebSocketFrame) {
|
||||
//正常的TEXT消息类型
|
||||
TextWebSocketFrame frame = (TextWebSocketFrame) msg;
|
||||
log.info("webSocket服务器收到客户端心跳信息:{}", frame.text());
|
||||
/*if ("alive".equals(frame.text())) {
|
||||
TextWebSocketFrame wd = new TextWebSocketFrame("over");
|
||||
ctx.writeAndFlush(wd);
|
||||
}*/
|
||||
}
|
||||
super.channelRead(ctx, msg);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户地址获取用户名 ws://127.0.0.1:7777/hello?name=aa
|
||||
* @param url
|
||||
* @return
|
||||
*/
|
||||
private static String getUrlParams(String url) {
|
||||
if (!url.contains(EQUAL_TO)) {
|
||||
return null;
|
||||
}
|
||||
String userId = url.substring(url.indexOf(EQUAL_TO) + 1);
|
||||
return userId;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) {
|
||||
/* System.out.println("服务端消息 == " + msg.text());
|
||||
if(msg.text().equals("下发指令")){
|
||||
*//**
|
||||
* 处理对应消息
|
||||
* 1.先下发所要操作的流程信息
|
||||
* 2.组装对应的入参信息
|
||||
* 3.再用socket信息返回结束
|
||||
*//*
|
||||
//NettyClient.socketClient(msg.text(),new NettySourceClientHandler());
|
||||
|
||||
}*/
|
||||
|
||||
//可以直接调用text 拿到文本信息帧中的信息
|
||||
/* Channel channel = ctx.channel();
|
||||
TextWebSocketFrame resp = new TextWebSocketFrame(msg.text());
|
||||
channel.writeAndFlush(resp);*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlerAdded(ChannelHandlerContext ctx) {
|
||||
//WebServiceManager.addUser(userId, ctx.channel());
|
||||
System.out.println("webSocket有新的连接接入:" + ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlerRemoved(ChannelHandlerContext ctx){
|
||||
// 假设用户 ID 是从某个地方获取的,这里简单示例为 "userId"
|
||||
System.out.println("weoSocket客户端退出: " + ctx.channel().id());
|
||||
WebServiceManager.removeChannel(ctx.channel().id().toString());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) {
|
||||
System.out.println("weoSocket断线" + ctx.channel().id());
|
||||
ctx.close();
|
||||
|
||||
PreDetectionParam param = new PreDetectionParam();
|
||||
param.setUserPageId("cdf");
|
||||
CnSocketUtil.quitSend(param);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
|
||||
IdleStateEvent event = (IdleStateEvent) evt;
|
||||
String eventDesc = null;
|
||||
switch (event.state()) {
|
||||
case READER_IDLE:
|
||||
eventDesc = "读空闲";
|
||||
System.out.println(ctx.channel().remoteAddress() + "发生超时事件--" + eventDesc);
|
||||
times++;
|
||||
if (times > 3) {
|
||||
System.out.println("空闲次数超过三次 关闭连接");
|
||||
ctx.channel().close();
|
||||
}
|
||||
break;
|
||||
case WRITER_IDLE:
|
||||
eventDesc = "写空闲";
|
||||
break;
|
||||
case ALL_IDLE:
|
||||
eventDesc = "读写空闲";
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
//super.userEventTriggered(ctx, evt);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 推送数据至前台
|
||||
*/
|
||||
public void sendMsgToUser(String userId, String msg) {
|
||||
WebServiceManager.sendMsg(userId, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
cause.printStackTrace();
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
package com.njcn.gather.detection.util.socket.web;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.HttpResponseDecoder;
|
||||
import io.netty.handler.codec.http.HttpServerCodec;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
|
||||
import io.netty.handler.stream.ChunkedWriteHandler;
|
||||
import io.netty.handler.timeout.IdleStateHandler;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @Description: webSocket服务端自定义配置
|
||||
* @Author: wr
|
||||
* @Date: 2024/12/10 14:20
|
||||
*/
|
||||
|
||||
public class WebSocketInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void initChannel(SocketChannel ch) throws Exception {
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
//设置心跳机制
|
||||
// ch.pipeline().addLast(new IdleStateHandler(5, 0, 0));
|
||||
//增加编解码器 的另一种方式
|
||||
pipeline.addLast(new HttpServerCodec());
|
||||
pipeline.addLast(new HttpResponseDecoder());
|
||||
//块方式写的处理器 适合处理大数据
|
||||
pipeline.addLast(new ChunkedWriteHandler());
|
||||
//聚合
|
||||
pipeline.addLast(new HttpObjectAggregator(512 * 1024));
|
||||
/*
|
||||
* 这个时候 我们需要声明我们使用的是 websocket 协议
|
||||
* netty为websocket也准备了对应处理器 设置的是访问路径
|
||||
* 这个时候我们只需要访问 ws://127.0.0.1:7777/hello 就可以了
|
||||
* 这个handler是将http协议升级为websocket 并且使用 101 作为响应码
|
||||
* */
|
||||
pipeline.addLast(new IdleStateHandler(13, 0, 0, TimeUnit.SECONDS));
|
||||
pipeline.addLast(new WebSocketHandler());
|
||||
pipeline.addLast(new WebSocketServerProtocolHandler("/hello"));
|
||||
|
||||
pipeline.addLast(new ChannelInboundHandlerAdapter() {
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 处理异常,例如记录日志、关闭连接等
|
||||
System.out.println("进入异常++++++++++++++++++");
|
||||
cause.printStackTrace();
|
||||
ctx.close();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
package com.njcn.gather.detection.util.socket.web;
|
||||
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
|
||||
/**
|
||||
* @Description: websocket服务端
|
||||
* @Author: wr
|
||||
* @Date: 2024/12/10 13:59
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class WebSocketService {
|
||||
|
||||
|
||||
/**
|
||||
* 端口号
|
||||
*/
|
||||
@Value("${webSocket.port:7777}")
|
||||
int port;
|
||||
|
||||
|
||||
EventLoopGroup bossGroup;
|
||||
EventLoopGroup workerGroup;
|
||||
|
||||
|
||||
|
||||
@PostConstruct
|
||||
public void start() {
|
||||
new Thread(() -> {
|
||||
//可以自定义线程的数量
|
||||
bossGroup = new NioEventLoopGroup();
|
||||
// 默认创建的线程数量 = CPU 处理器数量 *2
|
||||
workerGroup = new NioEventLoopGroup();
|
||||
try {
|
||||
ServerBootstrap serverBootstrap = new ServerBootstrap();
|
||||
serverBootstrap.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.handler(new LoggingHandler())
|
||||
//当前连接被阻塞的时候,BACKLOG代表的事 阻塞队列的长度
|
||||
.option(ChannelOption.SO_BACKLOG, 128)
|
||||
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
|
||||
|
||||
//设置连接保持为活动状态
|
||||
.childOption(ChannelOption.SO_KEEPALIVE, true)
|
||||
.childHandler(new WebSocketInitializer());
|
||||
ChannelFuture future = serverBootstrap.bind(port).sync();
|
||||
future.addListener(f -> {
|
||||
if (future.isSuccess()) {
|
||||
System.out.println("webSocket服务启动成功");
|
||||
} else {
|
||||
System.out.println("webSocket服务启动失败");
|
||||
}
|
||||
});
|
||||
future.channel().closeFuture().sync();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 释放资源
|
||||
*/
|
||||
@PreDestroy
|
||||
public void destroy() throws InterruptedException {
|
||||
if (bossGroup != null) {
|
||||
bossGroup.shutdownGracefully().sync();
|
||||
}
|
||||
if (workerGroup != null) {
|
||||
workerGroup.shutdownGracefully().sync();
|
||||
}
|
||||
|
||||
System.out.println("webSocket销毁---------------");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
package com.njcn.gather.detection.util.socket.websocket;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.njcn.gather.detection.pojo.param.PreDetectionParam;
|
||||
import com.njcn.gather.detection.pojo.vo.WebSocketVO;
|
||||
import com.njcn.gather.detection.pojo.enums.SourceOperateCodeEnum;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* WebSocket会话管理器
|
||||
*
|
||||
* <p>负责管理电能质量检测系统中的WebSocket连接会话和检测参数,主要功能包括:</p>
|
||||
* <ul>
|
||||
* <li>WebSocket连接会话的添加、删除和管理</li>
|
||||
* <li>向指定用户推送实时消息(文本消息和结构化消息)</li>
|
||||
* <li>全局检测参数的存储和管理</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p><b>线程安全性:</b></p>
|
||||
* 使用ConcurrentHashMap确保在高并发环境下的线程安全。
|
||||
*
|
||||
* <p><b>使用场景:</b></p>
|
||||
* <ul>
|
||||
* <li>检测进度实时推送</li>
|
||||
* <li>检测结果数据推送</li>
|
||||
* <li>设备状态变更通知</li>
|
||||
* <li>异常信息推送</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p><b>消息推送方式:</b></p>
|
||||
* <ul>
|
||||
* <li>{@link #sendMsg(String, String)} - 发送纯文本消息</li>
|
||||
* <li>{@link #sendMessage(String, WebSocketVO)} - 发送结构化JSON消息</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author wr
|
||||
* @version 1.0
|
||||
* @date 2024/12/11 13:04
|
||||
* @see com.njcn.gather.detection.util.socket.websocket.WebSocketHandler WebSocket处理器
|
||||
* @see com.njcn.gather.detection.pojo.vo.WebSocketVO WebSocket消息对象
|
||||
* @since 检测系统 v2.3.12
|
||||
*/
|
||||
@Slf4j
|
||||
public class WebServiceManager {
|
||||
|
||||
/**
|
||||
* WebSocket用户会话存储
|
||||
* key: 用户ID, value: WebSocket连接通道
|
||||
*/
|
||||
private static final Map<String, Channel> userSessions = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 检测参数存储
|
||||
* key: 用户ID(userPageId), value: 检测参数对象
|
||||
* 支持多用户并发检测,每个用户的检测参数独立存储
|
||||
*/
|
||||
private static final Map<String, PreDetectionParam> preDetectionParamMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 添加用户WebSocket会话
|
||||
*
|
||||
* @param userId 用户ID,不能为null
|
||||
* @param channel WebSocket连接通道,不能为null
|
||||
*/
|
||||
public static void addUser(String userId, Channel channel) {
|
||||
userSessions.put(userId, channel);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据用户ID移除会话(推荐使用)
|
||||
* 时间复杂度:O(1)
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 被移除的Channel,如果不存在则返回null
|
||||
*/
|
||||
public static Channel removeByUserId(String userId) {
|
||||
return userSessions.remove(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据channelId移除会话(兼容老版本)
|
||||
* 时间复杂度:O(n),建议使用removeByUserId替代
|
||||
*
|
||||
* @param channelId 通道ID
|
||||
* @deprecated 建议使用 {@link #removeByUserId(String)} 替代
|
||||
*/
|
||||
@Deprecated
|
||||
public static void removeChannel(String channelId) {
|
||||
// 遍历并删除
|
||||
Iterator<Map.Entry<String, Channel>> iterator = userSessions.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<String, Channel> entry = iterator.next();
|
||||
if (entry.getValue().id().toString().equals(channelId)) {
|
||||
iterator.remove();
|
||||
break; // 找到后立即退出,避免继续遍历
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 发送纯文本消息给指定用户
|
||||
*
|
||||
* @param userId 目标用户ID
|
||||
* @param msg 要发送的文本消息
|
||||
*/
|
||||
public static void sendMsg(String userId, String msg) {
|
||||
Channel channel = userSessions.get(userId);
|
||||
if (Objects.nonNull(channel) && channel.isActive()) {
|
||||
TextWebSocketFrame frame = new TextWebSocketFrame(msg);
|
||||
channel.writeAndFlush(frame);
|
||||
} else {
|
||||
log.error("WebSocket推送消息失败,用户连接已断开,时间: {}, userId: {}", LocalDateTime.now(), userId);
|
||||
WebSocketHandler.cleanupSocketResources(userId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送结构化消息给指定用户
|
||||
* 消息会被序列化为JSON格式后发送
|
||||
*
|
||||
* @param userId 目标用户ID
|
||||
* @param webSocketVO 要发送的结构化消息对象
|
||||
*/
|
||||
public static void sendMessage(String userId, WebSocketVO<Object> webSocketVO) {
|
||||
Channel channel = userSessions.get(userId);
|
||||
if (Objects.nonNull(channel) && channel.isActive()) {
|
||||
TextWebSocketFrame frame = new TextWebSocketFrame(JSON.toJSONString(webSocketVO));
|
||||
channel.writeAndFlush(frame);
|
||||
} else {
|
||||
log.error("WebSocket推送结构化消息失败,用户连接已断开,时间: {}, userId: {}", LocalDateTime.now(), userId);
|
||||
WebSocketHandler.cleanupSocketResources(userId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 存储检测参数(基于用户ID)
|
||||
* 支持多用户并发检测,每个用户的检测参数独立存储
|
||||
*
|
||||
* @param userId 用户ID(登录名)
|
||||
* @param preDetectionParam 检测参数对象
|
||||
* @throws IllegalArgumentException 当userId或检测参数为空时抛出
|
||||
*/
|
||||
public static void addPreDetectionParam(String userId, PreDetectionParam preDetectionParam) {
|
||||
if (userId == null || userId.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("用户ID不能为空");
|
||||
}
|
||||
if (preDetectionParam == null) {
|
||||
throw new IllegalArgumentException("检测参数不能为空");
|
||||
}
|
||||
preDetectionParamMap.put(userId, preDetectionParam);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取指定用户的检测参数
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 检测参数对象,如果不存在则返回null
|
||||
*/
|
||||
public static PreDetectionParam getPreDetectionParam(String userId) {
|
||||
return preDetectionParamMap.get(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前检测参数(兼容老版本)
|
||||
* 注意:该方法已废弃,建议使用 {@link #getPreDetectionParam(String)}
|
||||
*
|
||||
* @return 检测参数对象,如果不存在则返回null
|
||||
* @deprecated 多用户并发场景下该方法不安全,请使用 {@link #getPreDetectionParam(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static PreDetectionParam getPreDetectionParam() {
|
||||
if (preDetectionParamMap.size() == 1) {
|
||||
return preDetectionParamMap.values().iterator().next();
|
||||
}
|
||||
log.warn("存在多个检测参数,无法确定返回哪个,当前参数数量: {}", preDetectionParamMap.size());
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除指定用户的检测参数
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 被移除的检测参数,如果不存在则返回null
|
||||
*/
|
||||
public static PreDetectionParam removePreDetectionParam(String userId) {
|
||||
return preDetectionParamMap.remove(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有检测参数
|
||||
*/
|
||||
public static void removeAllPreDetectionParam() {
|
||||
preDetectionParamMap.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有检测参数(兼容老版本)
|
||||
*
|
||||
* @deprecated 建议使用 {@link #removeAllPreDetectionParam()} 或 {@link #removePreDetectionParam(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static void removePreDetectionParam() {
|
||||
removeAllPreDetectionParam();
|
||||
}
|
||||
|
||||
// ================================ 实用功能方法 ================================
|
||||
|
||||
/**
|
||||
* 获取当前在线用户数量
|
||||
*
|
||||
* @return 在线用户数量
|
||||
*/
|
||||
public static int getOnlineUserCount() {
|
||||
return userSessions.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查指定用户是否在线
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return true如果用户在线且连接活跃,否则返回false
|
||||
*/
|
||||
public static boolean isUserOnline(String userId) {
|
||||
Channel channel = userSessions.get(userId);
|
||||
return Objects.nonNull(channel) && channel.isActive();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有在线用户ID集合
|
||||
*
|
||||
* @return 在线用户ID集合的快照
|
||||
*/
|
||||
public static java.util.Set<String> getOnlineUserIds() {
|
||||
return new java.util.HashSet<>(userSessions.keySet());
|
||||
}
|
||||
|
||||
// ================================ 检测消息推送方法 ================================
|
||||
|
||||
/**
|
||||
* 发送检测相关消息给指定用户
|
||||
* <p>用于推送检测状态、进度、结果等结构化消息</p>
|
||||
*
|
||||
* @param userId 目标用户ID
|
||||
* @param requestId 请求ID,用于标识消息类型和流程
|
||||
* @param operateCode 操作代码,标识具体的操作类型
|
||||
* @param data 数据载荷,可以是任意类型的数据
|
||||
* @param desc 描述信息
|
||||
* @since v2.3.12 重构版本
|
||||
*/
|
||||
public static void sendDetectionMessage(String userId, String requestId, String operateCode, Object data, String desc) {
|
||||
WebSocketVO<Object> webSocketVO = new WebSocketVO<>();
|
||||
webSocketVO.setRequestId(requestId);
|
||||
webSocketVO.setOperateCode(operateCode);
|
||||
webSocketVO.setData(data);
|
||||
webSocketVO.setDesc(desc);
|
||||
sendMessage(userId, webSocketVO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送未知错误消息给指定用户
|
||||
* <p>用于处理系统无法识别的操作或未知异常情况</p>
|
||||
*
|
||||
* @param userId 目标用户ID
|
||||
* @since v2.3.12 重构版本
|
||||
*/
|
||||
public static void sendUnknownErrorMessage(String userId) {
|
||||
WebSocketVO<Object> webSocketVO = new WebSocketVO<>();
|
||||
webSocketVO.setRequestId(SourceOperateCodeEnum.UNKNOWN_OPERATE.getValue());
|
||||
webSocketVO.setData(SourceOperateCodeEnum.UNKNOWN_OPERATE.getMsg());
|
||||
webSocketVO.setOperateCode(SourceOperateCodeEnum.UNKNOWN_OPERATE.getMsg());
|
||||
sendMessage(userId, webSocketVO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送检测错误消息给指定用户
|
||||
* <p>用于推送特定类型的检测错误信息</p>
|
||||
*
|
||||
* @param userId 目标用户ID
|
||||
* @param errorType 错误类型枚举
|
||||
* @since v2.3.12 重构版本
|
||||
*/
|
||||
public static void sendDetectionErrorMessage(String userId, SourceOperateCodeEnum errorType) {
|
||||
WebSocketVO<Object> webSocketVO = new WebSocketVO<>();
|
||||
webSocketVO.setRequestId(errorType.getValue());
|
||||
webSocketVO.setData(errorType.getMsg());
|
||||
webSocketVO.setOperateCode(errorType.getValue());
|
||||
sendMessage(userId, webSocketVO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送检测错误消息给指定用户
|
||||
* <p>用于推送特定类型的检测错误信息</p>
|
||||
*
|
||||
* @param userId 目标用户ID
|
||||
* @param requestId 请求ID
|
||||
* @param errorType 错误类型枚举
|
||||
* @since v2.3.12 重构版本
|
||||
*/
|
||||
public static void sendDetectionErrorMessage(String userId, String requestId, SourceOperateCodeEnum errorType) {
|
||||
WebSocketVO<Object> webSocketVO = new WebSocketVO<>();
|
||||
webSocketVO.setRequestId(requestId);
|
||||
webSocketVO.setData(errorType.getMsg());
|
||||
webSocketVO.setOperateCode(errorType.getValue());
|
||||
sendMessage(userId, webSocketVO);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.njcn.gather.detection.util.socket.websocket;
|
||||
|
||||
/**
|
||||
* WebSocket常量管理类
|
||||
*
|
||||
* @author wr
|
||||
* @date 2024/12/10
|
||||
*/
|
||||
public final class WebSocketConstants {
|
||||
|
||||
/**
|
||||
* URL参数分隔符
|
||||
*/
|
||||
public static final String QUESTION_MARK = "?";
|
||||
|
||||
/**
|
||||
* URL参数等号分隔符
|
||||
*/
|
||||
public static final String EQUAL_TO = "=";
|
||||
|
||||
/**
|
||||
* 客户端心跳消息
|
||||
*/
|
||||
public static final String HEARTBEAT_PING = "alive";
|
||||
|
||||
/**
|
||||
* 服务端心跳响应
|
||||
*/
|
||||
public static final String HEARTBEAT_PONG = "over";
|
||||
|
||||
/**
|
||||
* 心跳超时最大次数
|
||||
*/
|
||||
public static final int MAX_HEARTBEAT_MISS_COUNT = 3;
|
||||
|
||||
/**
|
||||
* WebSocket握手失败状态码
|
||||
*/
|
||||
public static final int HANDSHAKE_FAILED_STATUS = 4000;
|
||||
|
||||
/**
|
||||
* WebSocket握手失败原因
|
||||
*/
|
||||
public static final String HANDSHAKE_FAILED_REASON = "Missing required userId parameter";
|
||||
|
||||
private WebSocketConstants() {
|
||||
// 私有构造函数,防止实例化
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
package com.njcn.gather.detection.util.socket.websocket;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.njcn.gather.detection.pojo.enums.SourceOperateCodeEnum;
|
||||
import com.njcn.gather.detection.pojo.param.PreDetectionParam;
|
||||
import com.njcn.gather.detection.util.socket.CnSocketUtil;
|
||||
import com.njcn.gather.detection.util.socket.FormalTestManager;
|
||||
import com.njcn.gather.device.pojo.enums.PatternEnum;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.CorruptedFrameException;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketHandshakeException;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
|
||||
import io.netty.handler.timeout.IdleStateEvent;
|
||||
import io.netty.util.AttributeKey;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static com.njcn.gather.detection.util.socket.websocket.WebSocketConstants.*;
|
||||
|
||||
|
||||
/**
|
||||
* WebSocket消息处理器
|
||||
*
|
||||
* <p>负责处理电能质量检测系统中的WebSocket连接和消息通信,主要功能包括:</p>
|
||||
* <ul>
|
||||
* <li>WebSocket连接的建立、维护和断开</li>
|
||||
* <li>用户身份验证和会话管理</li>
|
||||
* <li>心跳检测和连接保活</li>
|
||||
* <li>检测状态和结果的实时推送</li>
|
||||
* <li>异常处理和资源清理</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p><b>通信协议:</b></p>
|
||||
* <pre>
|
||||
* 连接URL: ws://host:port/path?name=userId
|
||||
* 心跳消息: "alive" -> "over"
|
||||
* 业务消息: JSON格式的检测数据和状态信息
|
||||
* </pre>
|
||||
*
|
||||
* <p><b>安全策略:</b></p>
|
||||
* <ul>
|
||||
* <li>连接时必须提供有效的userId参数,否则拒绝连接</li>
|
||||
* <li>支持心跳超时检测,超时3次自动断开连接</li>
|
||||
* <li>连接断开时自动清理相关Socket资源</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p><b>使用场景:</b></p>
|
||||
* 主要用于前端实时接收检测进度、检测结果、设备状态等信息的推送,
|
||||
* 配合detection模块的ResponseService类实现完整的实时通信链路。
|
||||
*
|
||||
* @author wr
|
||||
* @version 1.0
|
||||
* @date 2024/12/10 13:56
|
||||
* @see WebServiceManager 会话管理器
|
||||
* @see WebSocketConstants 常量定义
|
||||
* @see com.njcn.gather.detection.handler.SocketDevResponseService 设备响应处理
|
||||
* @see com.njcn.gather.detection.handler.SocketSourceResponseService 源响应处理
|
||||
* @since 检测系统 v2.3.12
|
||||
*/
|
||||
|
||||
|
||||
@Slf4j
|
||||
public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
|
||||
|
||||
// ================================ 字段定义 ================================
|
||||
|
||||
/**
|
||||
* 心跳超时计数器
|
||||
*/
|
||||
private int times;
|
||||
|
||||
/**
|
||||
* 当前WebSocket连接对应的用户ID
|
||||
* 在首次HTTP握手时从URL参数中提取并存储
|
||||
* 用于后续的Socket连接管理和资源清理
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 心跳响应内容常量
|
||||
* 注意:不能预创建TextWebSocketFrame对象,因为ByteBuf状态会改变
|
||||
*/
|
||||
private static final String HEARTBEAT_RESPONSE_TEXT = HEARTBEAT_PONG;
|
||||
|
||||
// ================================ Netty生命周期方法 ================================
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
log.info("webSocket服务端通道已建立,channelId: {}", ctx.channel().id());
|
||||
super.channelActive(ctx);
|
||||
}
|
||||
|
||||
// HTTP握手处理已移至WebSocketPreprocessor,这里只处理WebSocket帧
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) {
|
||||
handleWebSocketMessage(ctx, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlerAdded(ChannelHandlerContext ctx) {
|
||||
log.info("webSocket有新的连接接入,channelId: {}", ctx.channel().id());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlerRemoved(ChannelHandlerContext ctx) {
|
||||
log.info("webSocket客户端退出,channelId: {}, userId: {}", ctx.channel().id(), this.userId);
|
||||
if (this.userId != null) {
|
||||
WebServiceManager.removeByUserId(this.userId);
|
||||
} else {
|
||||
// 备用方案:如果userId为空,使用传统方法
|
||||
WebServiceManager.removeChannel(ctx.channel().id().toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) {
|
||||
log.info("webSocket连接断线,channelId: {}, userId: {}", ctx.channel().id(), this.userId);
|
||||
|
||||
// 确保通道关闭
|
||||
if (ctx.channel() != null && ctx.channel().isActive()) {
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
// 使用Handler实例中保存的userId进行资源清理
|
||||
cleanupSocketResources(this.userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
|
||||
// 处理WebSocket握手完成事件
|
||||
if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) {
|
||||
WebSocketServerProtocolHandler.HandshakeComplete handshakeComplete =
|
||||
(WebSocketServerProtocolHandler.HandshakeComplete) evt;
|
||||
|
||||
// 从Channel属性获取userId(由WebSocketPreprocessor设置)
|
||||
this.userId = ctx.channel().attr(AttributeKey.<String>valueOf("userId")).get();
|
||||
|
||||
log.info("WebSocket协议升级完成,userId: {}, channelId: {}, requestUri: {}",
|
||||
this.userId, ctx.channel().id(), handshakeComplete.requestUri());
|
||||
|
||||
// 握手完成后建立用户会话
|
||||
if (this.userId != null) {
|
||||
WebServiceManager.addUser(this.userId, ctx.channel());
|
||||
log.info("WebSocket用户会话已建立,userId: {}, channelId: {}", this.userId, ctx.channel().id());
|
||||
}
|
||||
|
||||
// 发送连接成功消息给前端
|
||||
sendConnectionSuccessMessage(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理心跳超时事件
|
||||
if (evt instanceof IdleStateEvent) {
|
||||
IdleStateEvent event = (IdleStateEvent) evt;
|
||||
String eventDesc;
|
||||
switch (event.state()) {
|
||||
case READER_IDLE:
|
||||
eventDesc = "读空闲";
|
||||
log.warn("客户端心跳检测发生超时事件: {},channelId: {}", eventDesc, ctx.channel().id());
|
||||
times++;
|
||||
if (times > MAX_HEARTBEAT_MISS_COUNT) {
|
||||
log.error("客户端心跳检测空闲次数超过{}次,关闭连接,channelId: {}, userId: {}", MAX_HEARTBEAT_MISS_COUNT, ctx.channel().id(), this.userId);
|
||||
ctx.channel().close();
|
||||
if (this.userId != null) {
|
||||
WebServiceManager.removeByUserId(this.userId);
|
||||
} else {
|
||||
WebServiceManager.removeChannel(ctx.channel().id().toString());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WRITER_IDLE:
|
||||
log.debug("webSocket写空闲事件,channelId: {}", ctx.channel().id());
|
||||
break;
|
||||
case ALL_IDLE:
|
||||
log.debug("webSocket读写空闲事件,channelId: {}", ctx.channel().id());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 其他事件传递给父类处理
|
||||
super.userEventTriggered(ctx, evt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
String channelId = ctx.channel().id().toString();
|
||||
try {
|
||||
// 1. 异常分类记录
|
||||
logExceptionByType(channelId, cause);
|
||||
// 2. 业务清理
|
||||
cleanupOnException(ctx, cause);
|
||||
// 3. 连接处理决策
|
||||
handleConnectionByExceptionType(ctx, cause);
|
||||
} catch (Exception e) {
|
||||
// 防止异常处理本身出错
|
||||
log.error("异常处理过程中发生错误,强制关闭连接,channelId: {}", channelId, e);
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
|
||||
// ================================ HTTP握手处理已移至WebSocketPreprocessor ================================
|
||||
|
||||
// ================================ WebSocket消息处理 ================================
|
||||
|
||||
/**
|
||||
* 发送连接成功消息给前端
|
||||
* WebSocket握手完成后立即调用,通知前端连接建立成功
|
||||
*
|
||||
* @param ctx Netty通道上下文
|
||||
*/
|
||||
private void sendConnectionSuccessMessage(ChannelHandlerContext ctx) {
|
||||
if (ctx == null || ctx.channel() == null || !ctx.channel().isActive()) {
|
||||
log.warn("无法发送连接成功消息:通道不可用, userId: {}", this.userId);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 构建连接成功消息
|
||||
String welcomeMessage = String.format("{\"type\":\"connection\",\"status\":\"success\",\"message\":\"WebSocket连接建立成功\",\"userId\":\"%s\",\"timestamp\":%d}",
|
||||
this.userId, System.currentTimeMillis());
|
||||
|
||||
TextWebSocketFrame frame = new TextWebSocketFrame(welcomeMessage);
|
||||
ctx.channel().writeAndFlush(frame);
|
||||
|
||||
log.info("已发送连接成功消息给前端, userId: {}, channelId: {}", this.userId, ctx.channel().id());
|
||||
} catch (Exception e) {
|
||||
log.error("发送连接成功消息失败, userId: {}, channelId: {}", this.userId, ctx.channel().id(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理WebSocket文本消息
|
||||
* 这里是所有WebSocket文本消息的统一处理入口
|
||||
*
|
||||
* @param ctx Netty通道上下文
|
||||
* @param frame WebSocket文本帧
|
||||
*/
|
||||
private void handleWebSocketMessage(ChannelHandlerContext ctx, TextWebSocketFrame frame) {
|
||||
String messageText = frame.text();
|
||||
// 处理心跳消息
|
||||
if (HEARTBEAT_PING.equals(messageText)) {
|
||||
handleHeartbeat(ctx);
|
||||
} else {
|
||||
// 处理业务消息
|
||||
handleBusinessMessage(ctx, frame, messageText);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理心跳消息
|
||||
* 重置超时计数器并回复心跳响应
|
||||
*
|
||||
* @param ctx Netty通道上下文
|
||||
*/
|
||||
private void handleHeartbeat(ChannelHandlerContext ctx) {
|
||||
if (ctx == null || ctx.channel() == null || !ctx.channel().isActive()) {
|
||||
log.warn("心跳处理失败:通道不可用,userId: {}", this.userId);
|
||||
return;
|
||||
}
|
||||
log.debug("收到心跳消息,userId: {}, channelId: {}", this.userId, ctx.channel().id());
|
||||
// 重置心跳超时计数器
|
||||
times = 0;
|
||||
// 每次创建新的心跳响应帧,确保内容正确
|
||||
TextWebSocketFrame heartbeatFrame = new TextWebSocketFrame(HEARTBEAT_RESPONSE_TEXT);
|
||||
ctx.channel().writeAndFlush(heartbeatFrame);
|
||||
log.debug("发送心跳响应,userId: {}, channelId: {}", this.userId, ctx.channel().id());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理业务消息
|
||||
* 可以在这里扩展具体的业务逻辑处理
|
||||
*
|
||||
* @param ctx Netty通道上下文
|
||||
* @param frame WebSocket文本帧
|
||||
* @param messageText 消息文本内容
|
||||
*/
|
||||
private void handleBusinessMessage(ChannelHandlerContext ctx, TextWebSocketFrame frame, String messageText) {
|
||||
log.debug("收到WebSocket业务消息,userId: {}, channelId: {}, message: {}",
|
||||
this.userId, ctx.channel().id(), messageText);
|
||||
// TODO: 根据业务需要扩展消息处理逻辑
|
||||
// 例如:
|
||||
// - 解析JSON消息
|
||||
// - 根据消息类型分发到不同的处理器
|
||||
// - 调用业务服务处理具体逻辑
|
||||
}
|
||||
|
||||
// ================================ 异常处理 ================================
|
||||
|
||||
/**
|
||||
* 根据异常类型记录不同级别的日志
|
||||
*/
|
||||
private void logExceptionByType(String channelId, Throwable cause) {
|
||||
if (cause instanceof IOException) {
|
||||
log.info("webSocket网络异常,客户端可能异常断开,channelId: {}, 异常: {}", channelId, cause.getMessage());
|
||||
} else if (cause instanceof WebSocketHandshakeException) {
|
||||
log.warn("webSocket握手异常,channelId: {}, 异常: {}", channelId, cause.getMessage());
|
||||
} else if (cause instanceof DecoderException || cause instanceof CorruptedFrameException) {
|
||||
log.error("webSocket协议解码异常,可能是恶意请求,channelId: {}, 异常: {}", channelId, cause.getMessage(), cause);
|
||||
} else if (cause instanceof IllegalArgumentException) {
|
||||
log.warn("webSocket参数异常,channelId: {}, 异常: {}", channelId, cause.getMessage());
|
||||
} else {
|
||||
log.error("webSocket未分类异常,channelId: {}, 类型: {}, 异常: {}",
|
||||
channelId, cause.getClass().getSimpleName(), cause.getMessage(), cause);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 异常发生时的业务清理工作
|
||||
*/
|
||||
private void cleanupOnException(ChannelHandlerContext ctx, Throwable cause) {
|
||||
if (ctx == null || ctx.channel() == null) {
|
||||
log.warn("异常处理:通道上下文为空,无法进行清理");
|
||||
return;
|
||||
}
|
||||
|
||||
String channelId = ctx.channel().id().toString();
|
||||
|
||||
// 清理会话
|
||||
if (this.userId != null) {
|
||||
WebServiceManager.removeByUserId(this.userId);
|
||||
log.debug("已清理WebSocket会话,userId: {}, channelId: {}", this.userId, channelId);
|
||||
} else {
|
||||
WebServiceManager.removeChannel(channelId);
|
||||
log.debug("已清理WebSocket会话(使用channelId),channelId: {}", channelId);
|
||||
}
|
||||
|
||||
// 清理检测相关资源
|
||||
cleanupSocketResources(this.userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据异常类型决定连接处理策略
|
||||
*/
|
||||
private void handleConnectionByExceptionType(ChannelHandlerContext ctx, Throwable cause) {
|
||||
String channelId = ctx.channel().id().toString();
|
||||
// URL参数异常但连接本身可能正常,尝试保持连接
|
||||
if (cause instanceof IllegalArgumentException &&
|
||||
cause.getMessage() != null && cause.getMessage().contains("URL")) {
|
||||
log.info("URL参数异常,尝试保持连接,channelId: {}", channelId);
|
||||
return;
|
||||
}
|
||||
// 其他情况都关闭连接
|
||||
log.debug("关闭WebSocket连接,channelId: {}", channelId);
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
// ================================ 资源清理 ================================
|
||||
|
||||
/**
|
||||
* 清理Socket相关资源
|
||||
*
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
public static void cleanupSocketResources(String userId) {
|
||||
if (userId == null || userId.trim().isEmpty()) {
|
||||
log.warn("userId为空,无法进行Socket连接清理");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
PreDetectionParam preDetectionParam = WebServiceManager.getPreDetectionParam(userId);
|
||||
if (ObjectUtil.isNotNull(preDetectionParam)) {
|
||||
// 使用该用户的检测参数关闭Socket连接
|
||||
log.info("使用用户检测参数关闭Socket连接,userId: {}", userId);
|
||||
if (FormalTestManager.patternEnum.equals(PatternEnum.CONTRAST)) {
|
||||
CnSocketUtil.contrastSendquit(preDetectionParam.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_01, false);
|
||||
CnSocketUtil.contrastSendquit(preDetectionParam.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_02, false);
|
||||
CnSocketUtil.contrastSendquit(preDetectionParam.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_03, false);
|
||||
// if (FormalTestManager.isRemoveSocket) {
|
||||
// boolean channelActive = SocketManager.isChannelActive(preDetectionParam.getUserPageId() + CnSocketUtil.CONTRAST_DEV_TAG);
|
||||
// if (channelActive) {
|
||||
// SocketManager.removeUser(preDetectionParam.getUserPageId() + CnSocketUtil.CONTRAST_DEV_TAG);
|
||||
// }
|
||||
// } else {
|
||||
// if (FormalTestManager.currentStep == SourceOperateCodeEnum.RECORD_WAVE_STEP1) {
|
||||
// CnSocketUtil.contrastSendquit(preDetectionParam.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_03, false);
|
||||
// } else if (FormalTestManager.currentStep != SourceOperateCodeEnum.QUITE) {
|
||||
// if (ObjectUtil.isNotNull(FormalTestManager.nonWaveDataSourceEnum)) {
|
||||
// if (FormalTestManager.nonWaveDataSourceEnum == DataSourceEnum.REAL_DATA) {
|
||||
// CnSocketUtil.contrastSendquit(preDetectionParam.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_02, false);
|
||||
// if (FormalTestManager.statisticsProtocol) {
|
||||
// CnSocketUtil.contrastSendquit(preDetectionParam.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_01, false);
|
||||
// }
|
||||
// } else {
|
||||
// CnSocketUtil.contrastSendquit(preDetectionParam.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_01, false);
|
||||
// }
|
||||
// if (FormalTestManager.isWaveCheck) {
|
||||
// CnSocketUtil.contrastSendquit(preDetectionParam.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_03, false);
|
||||
// }
|
||||
// } else {
|
||||
// if (FormalTestManager.statisticsProtocol) {
|
||||
// CnSocketUtil.contrastSendquit(preDetectionParam.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_01, false);
|
||||
// }
|
||||
// if (FormalTestManager.isWaveCheck) {
|
||||
// CnSocketUtil.contrastSendquit(preDetectionParam.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_02, false);
|
||||
// CnSocketUtil.contrastSendquit(preDetectionParam.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_03, false);
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// SocketManager.removeUser(preDetectionParam.getUserPageId() + CnSocketUtil.CONTRAST_DEV_TAG);
|
||||
// }
|
||||
// }
|
||||
} else {
|
||||
CnSocketUtil.quitSendSource(preDetectionParam);
|
||||
CnSocketUtil.quitSend(preDetectionParam);
|
||||
}
|
||||
// 清理完成后移除该用户的检测参数
|
||||
WebServiceManager.removePreDetectionParam(userId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("清理Socket连接时发生异常,userId: {}", userId, e);
|
||||
}
|
||||
}
|
||||
|
||||
// ================================ URL解析工具已移至WebSocketPreprocessor ================================
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
package com.njcn.gather.detection.util.socket.websocket;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.http.*;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
|
||||
import io.netty.handler.stream.ChunkedWriteHandler;
|
||||
import io.netty.handler.timeout.IdleStateHandler;
|
||||
import io.netty.util.AttributeKey;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* WebSocket服务端管道初始化器
|
||||
*
|
||||
* 职责:
|
||||
* 1. 为每个新的WebSocket连接配置处理器链(Pipeline)
|
||||
* 2. 按正确顺序添加各种Handler,确保数据流正确处理
|
||||
* 3. 配置HTTP到WebSocket的协议升级
|
||||
* 4. 设置心跳检测和异常处理机制
|
||||
*
|
||||
* 处理流程:
|
||||
* HTTP请求 → HTTP编解码 → 分块处理 → 消息聚合 → 协议升级 → 心跳检测 → 业务处理 → 异常处理
|
||||
*
|
||||
* @Description: webSocket服务端自定义配置
|
||||
* @Author: wr
|
||||
* @Date: 2024/12/10 14:20
|
||||
*/
|
||||
@Slf4j
|
||||
public class WebSocketInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
/**
|
||||
* WebSocket访问路径
|
||||
*/
|
||||
private static final String WEBSOCKET_PATH = "/hello";
|
||||
|
||||
/**
|
||||
* HTTP消息最大聚合大小:512KB
|
||||
* 用于WebSocket握手和消息传输
|
||||
*/
|
||||
private static final int MAX_CONTENT_LENGTH = 512 * 1024;
|
||||
|
||||
/**
|
||||
* 心跳检测间隔:13秒
|
||||
* 13秒内没有收到客户端消息则触发空闲事件
|
||||
*/
|
||||
private static final int READER_IDLE_TIME_SECONDS = 13;
|
||||
|
||||
/**
|
||||
* 为每个新连接初始化处理器管道
|
||||
* 注意:Handler的添加顺序非常重要,决定了数据的处理流向
|
||||
*
|
||||
* @param ch 新建立的Socket通道
|
||||
* @throws Exception 初始化过程中的异常
|
||||
*/
|
||||
@Override
|
||||
protected void initChannel(SocketChannel ch) throws Exception {
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
|
||||
// 1. HTTP协议处理器
|
||||
// HttpServerCodec = HttpRequestDecoder + HttpResponseEncoder
|
||||
// 负责HTTP请求解码和HTTP响应编码
|
||||
pipeline.addLast("http-codec", new HttpServerCodec());
|
||||
|
||||
// 2. 分块写入处理器
|
||||
// 用于处理大文件的分块传输,防止内存溢出
|
||||
// 支持ChunkedInput,如ChunkedFile、ChunkedNioFile等
|
||||
pipeline.addLast("chunked-write", new ChunkedWriteHandler());
|
||||
|
||||
// 3. HTTP消息聚合器
|
||||
// 将分片的HTTP消息重新组装成完整的FullHttpRequest或FullHttpResponse
|
||||
// WebSocket握手需要完整的HTTP请求,所以这个Handler必须添加
|
||||
pipeline.addLast("http-aggregator", new HttpObjectAggregator(MAX_CONTENT_LENGTH));
|
||||
|
||||
// 4. WebSocket URL预处理器
|
||||
// 在WebSocket握手之前处理URL参数,验证用户ID
|
||||
pipeline.addLast("websocket-preprocessor", new WebSocketPreprocessor());
|
||||
|
||||
// 5. WebSocket协议升级处理器
|
||||
// 处理WebSocket握手,将HTTP协议升级为WebSocket协议
|
||||
// 只有访问指定路径(WEBSOCKET_PATH)的请求才会被升级
|
||||
// 升级后会移除HTTP相关的Handler,添加WebSocket相关的Handler
|
||||
pipeline.addLast("websocket-protocol", new WebSocketServerProtocolHandler(WEBSOCKET_PATH));
|
||||
|
||||
// 6. 空闲状态检测器
|
||||
// 检测连接的空闲状态,用于心跳机制
|
||||
// readerIdleTime: 读空闲时间,writerIdleTime: 写空闲时间,allIdleTime: 读写空闲时间
|
||||
pipeline.addLast("idle-state", new IdleStateHandler(READER_IDLE_TIME_SECONDS, 0, 0, TimeUnit.SECONDS));
|
||||
|
||||
// 7. 自定义WebSocket业务处理器
|
||||
// 处理WebSocket帧,实现具体的业务逻辑
|
||||
// 包括心跳处理、消息路由、连接管理等
|
||||
pipeline.addLast("websocket-handler", new WebSocketHandler());
|
||||
|
||||
// 7. 全局异常处理器
|
||||
// 处理整个管道中未被捕获的异常,作为最后的异常处理兜底
|
||||
pipeline.addLast("exception-handler", new GlobalExceptionHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* WebSocket预处理器
|
||||
* 在WebSocket握手之前验证URL参数并清理URL
|
||||
*/
|
||||
private static class WebSocketPreprocessor extends ChannelInboundHandlerAdapter {
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
if (msg instanceof FullHttpRequest) {
|
||||
FullHttpRequest request = (FullHttpRequest) msg;
|
||||
String uri = request.uri();
|
||||
|
||||
log.debug("WebSocket预处理器收到HTTP请求:{}", uri);
|
||||
|
||||
// 验证并提取userId
|
||||
String userId = extractUserId(uri);
|
||||
if (userId == null || userId.trim().isEmpty()) {
|
||||
log.warn("WebSocket连接被拒绝:缺少userId参数, uri: {}", uri);
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(
|
||||
HttpVersion.HTTP_1_1,
|
||||
HttpResponseStatus.BAD_REQUEST
|
||||
);
|
||||
ctx.writeAndFlush(response).addListener(f -> ctx.close());
|
||||
return;
|
||||
}
|
||||
|
||||
// 将userId存储到Channel属性中
|
||||
ctx.channel().attr(AttributeKey.<String>valueOf("userId")).set(userId);
|
||||
|
||||
// 清理URL参数
|
||||
if (uri.contains("?")) {
|
||||
String cleanUri = uri.substring(0, uri.indexOf("?"));
|
||||
request.setUri(cleanUri);
|
||||
log.debug("URL已清理,原始: {}, 清理后: {}, userId: {}", uri, cleanUri, userId);
|
||||
}
|
||||
}
|
||||
|
||||
// 继续传递给下一个Handler
|
||||
super.channelRead(ctx, msg);
|
||||
}
|
||||
|
||||
private String extractUserId(String uri) {
|
||||
if (!uri.contains("name=")) {
|
||||
return null;
|
||||
}
|
||||
int start = uri.indexOf("name=") + 5;
|
||||
int end = uri.indexOf("&", start);
|
||||
if (end == -1) {
|
||||
return uri.substring(start);
|
||||
} else {
|
||||
return uri.substring(start, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 全局异常处理器
|
||||
* 作为管道中的最后一个Handler,捕获所有未处理的异常
|
||||
*/
|
||||
private static class GlobalExceptionHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 记录异常详情,便于问题排查
|
||||
log.error("WebSocket连接发生未处理异常,远程地址:{},异常信息:{}",
|
||||
ctx.channel().remoteAddress(), cause.getMessage(), cause);
|
||||
|
||||
// 优雅关闭连接
|
||||
if (ctx.channel().isActive()) {
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
log.debug("WebSocket连接断开,远程地址:{}", ctx.channel().remoteAddress());
|
||||
super.channelInactive(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
package com.njcn.gather.detection.util.socket.websocket;
|
||||
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
||||
/**
|
||||
* WebSocket服务端核心类
|
||||
*
|
||||
* 职责:
|
||||
* 1. 启动基于Netty的WebSocket服务器
|
||||
* 2. 管理服务器生命周期(启动/关闭)
|
||||
* 3. 提供高性能的WebSocket通信支持
|
||||
*
|
||||
* 特性:
|
||||
* - 使用ApplicationRunner确保在Spring容器完全启动后再启动WebSocket服务
|
||||
* - 使用CompletableFuture异步启动,避免阻塞Spring Boot主线程
|
||||
* - 支持优雅关闭,确保资源正确释放
|
||||
* - 完善的异常处理和日志记录
|
||||
*
|
||||
* @Description: websocket服务端
|
||||
* @Author: wr
|
||||
* @Date: 2024/12/10 13:59
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class WebSocketService implements ApplicationRunner {
|
||||
|
||||
/**
|
||||
* WebSocket服务器监听端口
|
||||
* 默认7777端口,可通过配置文件webSocket.port自定义
|
||||
* 客户端连接地址:ws://host:port/hello?name=userId
|
||||
*/
|
||||
@Value("${webSocket.port:7777}")
|
||||
int port;
|
||||
|
||||
/**
|
||||
* Netty Boss线程组
|
||||
* 专门负责接受新的客户端连接请求
|
||||
* 通常配置1个线程即可,因为接受连接的操作相对简单
|
||||
*/
|
||||
EventLoopGroup bossGroup;
|
||||
|
||||
/**
|
||||
* Netty Worker线程组
|
||||
* 专门负责处理已建立连接的I/O操作和业务逻辑
|
||||
* 默认线程数 = CPU核心数 * 2,用于并发处理多个客户端
|
||||
*/
|
||||
EventLoopGroup workerGroup;
|
||||
|
||||
/**
|
||||
* 服务器通道引用
|
||||
* 保存绑定端口后的Channel,用于服务器关闭时释放资源
|
||||
*/
|
||||
private Channel serverChannel;
|
||||
|
||||
/**
|
||||
* 异步启动任务的Future对象
|
||||
* 用于管理WebSocket服务器的异步启动过程
|
||||
* 可以用来取消启动任务或检查启动状态
|
||||
*/
|
||||
private CompletableFuture<Void> serverFuture;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Spring Boot应用启动完成后自动调用此方法
|
||||
* 使用ApplicationRunner确保在所有Bean初始化完成后再启动WebSocket服务
|
||||
*/
|
||||
@Override
|
||||
public void run(ApplicationArguments args){
|
||||
// 使用CompletableFuture异步启动WebSocket服务,避免阻塞Spring Boot主线程
|
||||
// 这样可以让应用快速启动完成,WebSocket服务在后台异步启动
|
||||
serverFuture = CompletableFuture.runAsync(this::startWebSocketServer)
|
||||
.exceptionally(throwable -> {
|
||||
// 如果启动过程中发生异常,记录日志但不影响应用启动
|
||||
log.error("WebSocket服务启动异常", throwable);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动WebSocket服务器的核心方法
|
||||
* 此方法会一直阻塞直到服务器关闭,所以需要在异步线程中执行
|
||||
*/
|
||||
private void startWebSocketServer() {
|
||||
try {
|
||||
// 1. 创建线程组
|
||||
// bossGroup: 专门负责接受新的客户端连接请求
|
||||
// 可以自定义线程的数量,这里使用默认值(通常为1个线程)
|
||||
bossGroup = new NioEventLoopGroup(1);
|
||||
|
||||
// workerGroup: 专门负责处理已建立连接的I/O操作
|
||||
// 默认创建的线程数量 = CPU 处理器数量 * 2,用于处理业务逻辑
|
||||
workerGroup = new NioEventLoopGroup();
|
||||
|
||||
// 2. 配置服务器启动参数
|
||||
ServerBootstrap serverBootstrap = new ServerBootstrap();
|
||||
serverBootstrap.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.handler(new LoggingHandler())
|
||||
// 网络配置参数
|
||||
.option(ChannelOption.SO_BACKLOG, 128)
|
||||
// TCP连接建立超时时间5秒
|
||||
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
|
||||
// 子通道配置(针对每个客户端连接)
|
||||
// 启用TCP keepalive机制,检测死连接
|
||||
.childOption(ChannelOption.SO_KEEPALIVE, true)
|
||||
.childHandler(new WebSocketInitializer());
|
||||
|
||||
// 3. 绑定端口并启动服务器
|
||||
ChannelFuture future = serverBootstrap.bind(port).sync();
|
||||
// 保存服务器通道引用,用于后续关闭操作
|
||||
serverChannel = future.channel();
|
||||
// 4. 监听绑定结果并记录日志
|
||||
future.addListener(f -> {
|
||||
if (future.isSuccess()) {
|
||||
log.info("webSocket服务启动成功,端口:{}", port);
|
||||
} else {
|
||||
log.error("webSocket服务启动失败,端口:{}", port);
|
||||
}
|
||||
});
|
||||
|
||||
// 5. 等待服务器关闭
|
||||
// 这里会一直阻塞,直到serverChannel被外部关闭
|
||||
// 这就是为什么需要在异步线程中执行此方法的原因
|
||||
future.channel().closeFuture().sync();
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
// 如果线程被中断(比如应用关闭),记录日志并恢复中断状态
|
||||
log.error("WebSocket服务启动过程中被中断", e);
|
||||
Thread.currentThread().interrupt(); // 恢复中断状态
|
||||
} catch (Exception e) {
|
||||
// 捕获其他所有异常,记录日志并抛出运行时异常
|
||||
log.error("WebSocket服务启动失败", e);
|
||||
throw new RuntimeException("WebSocket服务启动失败", e);
|
||||
} finally {
|
||||
// 无论成功还是失败,都要清理资源
|
||||
shutdownGracefully();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 优雅关闭Netty线程组资源
|
||||
* 私有方法,用于在服务器启动异常时清理资源
|
||||
*/
|
||||
private void shutdownGracefully() {
|
||||
// 优雅关闭接收连接的线程组
|
||||
if (bossGroup != null) {
|
||||
bossGroup.shutdownGracefully();
|
||||
}
|
||||
// 优雅关闭处理I/O的线程组
|
||||
if (workerGroup != null) {
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spring容器销毁时自动调用此方法释放资源
|
||||
* 使用@PreDestroy确保在应用关闭时优雅地关闭WebSocket服务
|
||||
*/
|
||||
@PreDestroy
|
||||
public void destroy() throws InterruptedException {
|
||||
log.info("正在关闭WebSocket服务...");
|
||||
|
||||
// 步骤1: 首先关闭服务器通道,停止接受新的连接请求
|
||||
// 这样可以确保不会有新的客户端连接进来
|
||||
if (serverChannel != null) {
|
||||
try {
|
||||
// 等待最多5秒让服务器通道关闭
|
||||
serverChannel.close().awaitUninterruptibly(5, TimeUnit.SECONDS);
|
||||
log.debug("服务器通道已关闭");
|
||||
} catch (Exception e) {
|
||||
log.warn("关闭服务器通道时发生异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 步骤2: 关闭bossGroup线程组
|
||||
// bossGroup负责接受连接,现在可以安全关闭了
|
||||
if (bossGroup != null) {
|
||||
try {
|
||||
// 优雅关闭:静默期0秒,超时时间5秒
|
||||
// 静默期0秒意味着立即开始关闭,超时5秒后强制关闭
|
||||
bossGroup.shutdownGracefully(0, 5, TimeUnit.SECONDS).sync();
|
||||
log.debug("bossGroup线程组已关闭");
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("关闭bossGroup时被中断", e);
|
||||
Thread.currentThread().interrupt(); // 恢复中断状态
|
||||
}
|
||||
}
|
||||
|
||||
// 步骤3: 关闭workerGroup线程组
|
||||
// workerGroup负责处理I/O,需要等待现有连接处理完成
|
||||
if (workerGroup != null) {
|
||||
try {
|
||||
// 等待现有任务完成,但最多等待5秒
|
||||
workerGroup.shutdownGracefully(0, 5, TimeUnit.SECONDS).sync();
|
||||
log.debug("workerGroup线程组已关闭");
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("关闭workerGroup时被中断", e);
|
||||
Thread.currentThread().interrupt(); // 恢复中断状态
|
||||
}
|
||||
}
|
||||
|
||||
// 步骤4: 取消异步启动任务(如果还在运行)
|
||||
// 这可以避免在应用关闭后还有线程在后台运行
|
||||
if (serverFuture != null && !serverFuture.isDone()) {
|
||||
// true表示允许中断正在执行的任务
|
||||
boolean cancelled = serverFuture.cancel(true);
|
||||
if (cancelled) {
|
||||
log.debug("异步启动任务已取消");
|
||||
}
|
||||
}
|
||||
|
||||
log.info("webSocket服务已销毁");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +1,6 @@
|
||||
package com.njcn.gather.device.controller;
|
||||
|
||||
import cn.afterturn.easypoi.excel.ExcelImportUtil;
|
||||
import cn.afterturn.easypoi.excel.entity.ImportParams;
|
||||
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.constant.OperateType;
|
||||
@@ -15,37 +9,24 @@ import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.common.utils.LogUtil;
|
||||
import com.njcn.gather.device.pojo.enums.DevResponseEnum;
|
||||
import com.njcn.gather.device.pojo.enums.PatternEnum;
|
||||
import com.njcn.gather.device.pojo.param.PqDevParam;
|
||||
import com.njcn.gather.device.pojo.po.PqDev;
|
||||
import com.njcn.gather.device.pojo.vo.CNDevExcel;
|
||||
import com.njcn.gather.device.pojo.vo.PqDevExcel;
|
||||
import com.njcn.gather.device.pojo.vo.PqDevVO;
|
||||
import com.njcn.gather.device.pojo.vo.PreDetection;
|
||||
import com.njcn.gather.device.service.IPqDevService;
|
||||
import com.njcn.gather.system.dictionary.pojo.po.DictData;
|
||||
import com.njcn.gather.system.dictionary.service.IDictDataService;
|
||||
import com.njcn.gather.type.pojo.po.DevType;
|
||||
import com.njcn.gather.type.service.IDevTypeService;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import com.njcn.web.utils.ExcelUtil;
|
||||
import com.njcn.web.utils.FileUtil;
|
||||
import com.njcn.web.utils.HttpResultUtil;
|
||||
import com.njcn.web.utils.PoiUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -62,24 +43,8 @@ import java.util.Map;
|
||||
public class PqDevController extends BaseController {
|
||||
|
||||
private final IPqDevService pqDevService;
|
||||
private final IDictDataService dictDataService;
|
||||
private final IDevTypeService devTypeService;
|
||||
|
||||
|
||||
@OperateInfo
|
||||
@GetMapping("/aaa")
|
||||
@ApiOperation("分页查询被检设备")
|
||||
@ApiImplicitParam(name = "queryParam", value = "查询参数", required = true)
|
||||
public HttpResult<List<PreDetection>> aaa() {
|
||||
String methodDescribe = getMethodDescribe("list");
|
||||
List<PreDetection> devInfo = pqDevService.getDevInfo(Arrays.asList("578c142b7e4e4978a35bd6225aa62a23", "393504f55f1f79bce255bfc195cfdb56"));
|
||||
Map<String, List<PreDetection>> map = new HashMap();
|
||||
map.put("deviceList", devInfo);
|
||||
String jsonString = JSON.toJSONString(map);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, devInfo, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
@OperateInfo
|
||||
@PostMapping("/list")
|
||||
@ApiOperation("分页查询被检设备")
|
||||
@@ -106,14 +71,14 @@ public class PqDevController extends BaseController {
|
||||
@PostMapping("/add")
|
||||
@ApiOperation("新增被检设备")
|
||||
@ApiImplicitParam(name = "pqDevParam", value = "被检设备", required = true)
|
||||
public HttpResult<Object> add(@RequestBody @Validated PqDevParam pqDevParam) {
|
||||
public HttpResult<Boolean> add(@RequestBody @Validated PqDevParam pqDevParam) {
|
||||
String methodDescribe = getMethodDescribe("add");
|
||||
LogUtil.njcnDebug(log, "{},新增数据为:{}", methodDescribe, pqDevParam);
|
||||
boolean result = pqDevService.addPqDev(pqDevParam);
|
||||
if (result) {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe);
|
||||
} else {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, null, methodDescribe);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, false, methodDescribe);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,157 +86,67 @@ public class PqDevController extends BaseController {
|
||||
@PostMapping("/update")
|
||||
@ApiOperation("修改被检设备")
|
||||
@ApiImplicitParam(name = "updateParam", value = "被检设备", required = true)
|
||||
public HttpResult<Object> update(@RequestBody @Validated PqDevParam.UpdateParam updateParam) {
|
||||
public HttpResult<Boolean> update(@RequestBody @Validated PqDevParam.UpdateParam updateParam) {
|
||||
String methodDescribe = getMethodDescribe("update");
|
||||
LogUtil.njcnDebug(log, "{},修改数据为:{}", methodDescribe, updateParam);
|
||||
boolean result = pqDevService.updatePqDev(updateParam);
|
||||
if (result) {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe);
|
||||
} else {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, null, methodDescribe);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, false, methodDescribe);
|
||||
}
|
||||
}
|
||||
|
||||
@OperateInfo(operateType = OperateType.DELETE)
|
||||
@PostMapping("/delete")
|
||||
@ApiOperation("删除被检设备")
|
||||
@ApiImplicitParam(name = "ids", value = "被检设备id", required = true)
|
||||
public HttpResult<Object> delete(@RequestBody @Validated PqDevParam.DeleteParam param) {
|
||||
@ApiImplicitParam(name = "param", value = "删除参数", required = true)
|
||||
public HttpResult<Boolean> delete(@RequestBody @Validated PqDevParam.DeleteParam param) {
|
||||
String methodDescribe = getMethodDescribe("delete");
|
||||
LogUtil.njcnDebug(log, "{},删除ID数据为:{}", methodDescribe, String.join(StrUtil.COMMA, param.getIds()));
|
||||
boolean result = pqDevService.deletePqDev(param);
|
||||
if (result) {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe);
|
||||
} else {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, null, methodDescribe);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, false, methodDescribe);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.DOWNLOAD)
|
||||
@PostMapping("/export")
|
||||
@ApiOperation("导出被检设备数据")
|
||||
@ApiOperation("批量导出被检设备")
|
||||
@ApiImplicitParam(name = "queryParam", value = "查询参数", required = true)
|
||||
public void export(@RequestBody @Validated PqDevParam.QueryParam queryParam) {
|
||||
String methodDescribe = getMethodDescribe("export");
|
||||
public void exportDev(@RequestBody @Validated PqDevParam.QueryParam queryParam) {
|
||||
String methodDescribe = getMethodDescribe("exportDev");
|
||||
LogUtil.njcnDebug(log, "{},查询数据为:{}", methodDescribe, queryParam);
|
||||
DictData dictData = dictDataService.getDictDataById(queryParam.getPattern());
|
||||
if (ObjectUtil.isNotNull(dictData)) {
|
||||
if (PatternEnum.CONTRAST.getValue().equals(dictData.getCode())) {
|
||||
List<PqDevExcel.ContrastExportData> data = pqDevService.getContrastExportData(queryParam);
|
||||
ExcelUtil.exportExcel("被检设备导出数据.xlsx", PqDevExcel.ContrastExportData.class, data);
|
||||
} else {
|
||||
List<PqDevExcel.SimulateOrDigitalExportData> data = pqDevService.getSimulateOrDigitExportData(queryParam);
|
||||
ExcelUtil.exportExcel("被检设备导出数据.xlsx", PqDevExcel.SimulateOrDigitalExportData.class, data);
|
||||
}
|
||||
}
|
||||
pqDevService.exportDev(queryParam);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.DOWNLOAD)
|
||||
@PostMapping("/downloadTemplate")
|
||||
@ApiOperation("下载被检设备导入文件模板")
|
||||
public void downloadTemplate() {
|
||||
pqDevService.downloadTemplate();
|
||||
public void downloadTemplate(@RequestBody PqDevParam pqDevParam) {
|
||||
pqDevService.downloadTemplate(pqDevParam.getPattern());
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPLOAD)
|
||||
@PostMapping(value = "/importContrast")
|
||||
@ApiOperation("批量导入被检设备数据")
|
||||
@ApiImplicitParam(name = "file", value = "被检设备数据文件", required = true)
|
||||
public HttpResult<Object> importContrastData(@RequestParam("file") MultipartFile file, HttpServletResponse response) {
|
||||
String methodDescribe = getMethodDescribe("importContrastData");
|
||||
@PostMapping(value = "/import")
|
||||
@ApiOperation("批量导入被检设备")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "file", value = "被检设备数据文件", required = true),
|
||||
@ApiImplicitParam(name = "patternId", value = "模式id", required = true)
|
||||
})
|
||||
public HttpResult importDev(@RequestParam("file") MultipartFile file, @RequestParam("patternId") String patternId, @RequestParam("planId") String planId, @RequestParam(value = "cover", defaultValue = "0") Integer cover, HttpServletResponse response) {
|
||||
String methodDescribe = getMethodDescribe("importDev");
|
||||
LogUtil.njcnDebug(log, "{},上传文件为:{}", methodDescribe, file.getOriginalFilename());
|
||||
ImportParams params = new ImportParams();
|
||||
params.setHeadRows(2);
|
||||
params.setNeedVerify(true);
|
||||
params.setStartSheetIndex(0);
|
||||
params.setSheetNum(1);
|
||||
try {
|
||||
ExcelImportResult<PqDevExcel.ContrastImportData> excelImportResult = ExcelImportUtil.importExcelMore(file.getInputStream(), PqDevExcel.ContrastImportData.class, params);
|
||||
//如果存在非法数据,将不合格的数据导出
|
||||
if (excelImportResult.isVerifyFail()) {
|
||||
// 此处前端要做特殊处理,具体可以参考技术监督的数据导入
|
||||
Workbook failWorkbook = excelImportResult.getFailWorkbook();
|
||||
PoiUtil.exportFileByWorkbook(failWorkbook, "非法被检设备数据.xlsx", response);
|
||||
} else {
|
||||
//批量录入数据
|
||||
List<PqDevExcel.ContrastImportData> list = excelImportResult.getList();
|
||||
pqDevService.importContrastData(list);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new BusinessException(DevResponseEnum.IMPORT_DATA_FAIL);
|
||||
}
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出灿能二楼设备
|
||||
*
|
||||
* @param queryParam
|
||||
*/
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.DOWNLOAD)
|
||||
@PostMapping("/exportCNDev")
|
||||
@ApiOperation("导出被检设备数据")
|
||||
@ApiImplicitParam(name = "queryParam", value = "查询参数", required = true)
|
||||
public void exportCNDev(@RequestBody @Validated PqDevParam.QueryParam queryParam) {
|
||||
String methodDescribe = getMethodDescribe("exportCNDev");
|
||||
LogUtil.njcnDebug(log, "{},查询数据为:{}", methodDescribe, queryParam);
|
||||
|
||||
pqDevService.exportCNDev(queryParam);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载灿能二楼设备导入文件模板
|
||||
*/
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.DOWNLOAD)
|
||||
@PostMapping("/downloadCNDevTemplate")
|
||||
@ApiOperation("下载被检设备导入文件模板")
|
||||
public void downloadCNDevTemplate() {
|
||||
pqDevService.downloadCNDevTemplate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 导入灿能二楼设备
|
||||
*/
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPLOAD)
|
||||
@PostMapping(value = "/importCNDev")
|
||||
@ApiOperation("批量导入设备数据")
|
||||
@ApiImplicitParam(name = "file", value = "被检设备数据文件", required = true)
|
||||
public HttpResult<String> importCNDev(@RequestParam("file") MultipartFile file, String patternId, HttpServletResponse response) {
|
||||
String methodDescribe = getMethodDescribe("importCNDev");
|
||||
LogUtil.njcnDebug(log, "{},上传文件为:{}", methodDescribe, file.getOriginalFilename());
|
||||
|
||||
boolean fileType = FileUtil.judgeFileIsExcel(file.getOriginalFilename());
|
||||
if (!fileType) {
|
||||
throw new BusinessException(CommonResponseEnum.FILE_XLSX_ERROR);
|
||||
}
|
||||
|
||||
ImportParams params = new ImportParams();
|
||||
params.setStartSheetIndex(0);
|
||||
params.setSheetNum(1);
|
||||
params.setHeadRows(1);
|
||||
params.setNeedVerify(true);
|
||||
|
||||
List<CNDevExcel> cnDevExcelList;
|
||||
try {
|
||||
ExcelImportResult<CNDevExcel> excelImportResult = ExcelImportUtil.importExcelMore(file.getInputStream(), CNDevExcel.class, params);
|
||||
if (excelImportResult.isVerifyFail()) {
|
||||
// 此处前端要做特殊处理,具体可以参考技术监督的数据导入
|
||||
Workbook failWorkbook = excelImportResult.getFailWorkbook();
|
||||
PoiUtil.exportFileByWorkbook(failWorkbook, "非法被检设备数据.xlsx", response);
|
||||
// throw new BusinessException(DevResponseEnum.IMPORT_DATA_FORMAT_FAIL);
|
||||
return null;
|
||||
} else {
|
||||
cnDevExcelList = excelImportResult.getList();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new BusinessException(DevResponseEnum.IMPORT_DATA_FAIL);
|
||||
if ("null".equals(planId)) {
|
||||
planId = null;
|
||||
}
|
||||
if (ObjectUtil.isNotEmpty(cnDevExcelList)) {
|
||||
pqDevService.importCNDev(cnDevExcelList, patternId);
|
||||
}
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
return pqDevService.importDev(file, patternId, planId, response, cover);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@@ -285,54 +160,33 @@ public class PqDevController extends BaseController {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@PostMapping("/listByPlanId")
|
||||
@ApiOperation("查询出所有已绑定的设备")
|
||||
@ApiImplicitParam(name = "planId", value = "计划id", required = true)
|
||||
public HttpResult<List<PqDevVO>> listByPlanId(@RequestBody @Validated PqDevParam.QueryParam param) {
|
||||
String methodDescribe = getMethodDescribe("listByPlanId");
|
||||
LogUtil.njcnDebug(log, "{},查询数据为:{}", methodDescribe, param);
|
||||
List<PqDev> pqDevList = pqDevService.listByPlanId(param);
|
||||
|
||||
List<PqDevVO> result = BeanUtil.copyToList(pqDevList, PqDevVO.class);
|
||||
result.forEach(pqDevVO -> {
|
||||
DevType devType = devTypeService.getById(pqDevVO.getDevType());
|
||||
if (ObjectUtil.isNotNull(devType)) {
|
||||
pqDevVO.setDevChns(devType.getDevChns());
|
||||
pqDevVO.setDevVolt(devType.getDevVolt());
|
||||
pqDevVO.setDevCurr(devType.getDevCurr());
|
||||
}
|
||||
});
|
||||
@OperateInfo
|
||||
@GetMapping("/getSelectOptions")
|
||||
@ApiOperation("从历史数据中查询下拉框选项")
|
||||
@ApiImplicitParam(name = "pattern", value = "模式id", required = true)
|
||||
public HttpResult<Map<String, List<String>>> getSelectOptions(@RequestParam("pattern") String pattern) {
|
||||
String methodDescribe = getMethodDescribe("getSelectOptions");
|
||||
Map<String, List<String>> result = pqDevService.listSelectOptions(pattern);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE)
|
||||
@PostMapping("/documented")
|
||||
@ApiOperation("设备归档")
|
||||
@ApiImplicitParam(name = "id", value = "设备id", required = true)
|
||||
public HttpResult<List<PqDev>> documented(@RequestBody List<String> ids) {
|
||||
String methodDescribe = getMethodDescribe("documented");
|
||||
LogUtil.njcnDebug(log, "{},设备id为:{}", methodDescribe, ids);
|
||||
boolean result = pqDevService.documented(ids);
|
||||
if (result) {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
} else {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, null, methodDescribe);
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPLOAD)
|
||||
@PostMapping(value = "/ttt")
|
||||
@ApiOperation("批量导入被检设备")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "file", value = "被检设备数据文件", required = true),
|
||||
@ApiImplicitParam(name = "patternId", value = "模式id", required = true)
|
||||
})
|
||||
public HttpResult ttt(@RequestParam("file") MultipartFile file, @RequestParam("patternId") String patternId, @RequestParam("planId") String planId, @RequestParam(value = "cover", defaultValue = "0") Integer cover, HttpServletResponse response) {
|
||||
String methodDescribe = getMethodDescribe("ttt");
|
||||
LogUtil.njcnDebug(log, "{},上传文件为:{}", methodDescribe, file.getOriginalFilename());
|
||||
boolean fileType = FileUtil.judgeFileIsExcel(file.getOriginalFilename());
|
||||
if (!fileType) {
|
||||
throw new BusinessException(CommonResponseEnum.FILE_XLSX_ERROR);
|
||||
}
|
||||
if ("null".equals(planId)) {
|
||||
planId = null;
|
||||
}
|
||||
return pqDevService.importDev(file, patternId, planId, response, cover);
|
||||
}
|
||||
|
||||
// @OperateInfo(operateType = OperateType.UPDATE)
|
||||
// @PostMapping("/bindDev")
|
||||
// @ApiOperation("检测计划绑定设备")
|
||||
// @ApiImplicitParam(name = "bindPlanParam", value = "绑定参数", required = true)
|
||||
// public HttpResult<Object> bindDev(@RequestBody @Validated PqDevParam.BindPlanParam bindPlanParam) {
|
||||
// String methodDescribe = getMethodDescribe("bindDev");
|
||||
// LogUtil.njcnDebug(log, "{},绑定计划数据为:planId={}, pqDevIds={}", methodDescribe, bindPlanParam.getPlanId(), String.join(StrUtil.COMMA, bindPlanParam.getPqDevIds()));
|
||||
// boolean result = pqDevService.bind(bindPlanParam.getPlanId(), bindPlanParam.getPqDevIds());
|
||||
// if (result) {
|
||||
// return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
// } else {
|
||||
// return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, null, methodDescribe);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
package com.njcn.gather.device.controller;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.constant.OperateType;
|
||||
import com.njcn.common.pojo.enums.common.DataStateEnum;
|
||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.common.utils.LogUtil;
|
||||
import com.njcn.gather.device.pojo.param.PqStandardDevParam;
|
||||
import com.njcn.gather.device.pojo.po.PqStandardDev;
|
||||
import com.njcn.gather.device.pojo.vo.PqStandardDevVO;
|
||||
import com.njcn.gather.device.service.IPqStandardDevService;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import com.njcn.web.utils.FileUtil;
|
||||
import com.njcn.web.utils.HttpResultUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @date 2025-07-02
|
||||
*/
|
||||
@Slf4j
|
||||
@Api(tags = "标准设备管理")
|
||||
@RestController
|
||||
@RequestMapping("/pqStandardDev")
|
||||
@RequiredArgsConstructor
|
||||
public class PqStandardDevController extends BaseController {
|
||||
private final IPqStandardDevService pqStandardDevService;
|
||||
|
||||
@OperateInfo
|
||||
@PostMapping("/list")
|
||||
@ApiOperation("分页查询标准设备")
|
||||
@ApiImplicitParam(name = "queryParam", value = "查询参数", required = true)
|
||||
public HttpResult<Page<PqStandardDev>> list(@RequestBody PqStandardDevParam.QueryParam queryParam) {
|
||||
String methodDescribe = getMethodDescribe("list");
|
||||
LogUtil.njcnDebug(log, "{},查询数据为:{}", methodDescribe, queryParam);
|
||||
Page<PqStandardDev> result = pqStandardDevService.listPqStandardDevs(queryParam);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo
|
||||
@GetMapping("/getById")
|
||||
@ApiOperation("根据id查询标准设备")
|
||||
@ApiImplicitParam(name = "id", value = "标准设备id", required = true)
|
||||
public HttpResult<PqStandardDevVO> getById(@RequestParam("id") String id) {
|
||||
String methodDescribe = getMethodDescribe("getById");
|
||||
LogUtil.njcnDebug(log, "{},查询ID为:{}", methodDescribe, id);
|
||||
PqStandardDevVO result = pqStandardDevService.getPqStandardDevById(id);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(operateType = OperateType.ADD)
|
||||
@PostMapping("/add")
|
||||
@ApiOperation("新增标准设备")
|
||||
@ApiImplicitParam(name = "param", value = "标准设备", required = true)
|
||||
public HttpResult<Boolean> add(@RequestBody @Validated PqStandardDevParam param) {
|
||||
String methodDescribe = getMethodDescribe("add");
|
||||
LogUtil.njcnDebug(log, "{},新增数据为:{}", methodDescribe, param);
|
||||
boolean result = pqStandardDevService.addPqStandardDev(param);
|
||||
if (result) {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe);
|
||||
} else {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, false, methodDescribe);
|
||||
}
|
||||
}
|
||||
|
||||
@OperateInfo(operateType = OperateType.UPDATE)
|
||||
@PostMapping("/update")
|
||||
@ApiOperation("修改标准设备")
|
||||
@ApiImplicitParam(name = "param", value = "标准设备", required = true)
|
||||
public HttpResult<Boolean> update(@RequestBody @Validated PqStandardDevParam.UpdateParam param) {
|
||||
String methodDescribe = getMethodDescribe("update");
|
||||
LogUtil.njcnDebug(log, "{},修改数据为:{}", methodDescribe, param);
|
||||
boolean result = pqStandardDevService.updatePqStandardDev(param);
|
||||
if (result) {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe);
|
||||
} else {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, false, methodDescribe);
|
||||
}
|
||||
}
|
||||
|
||||
@OperateInfo(operateType = OperateType.DELETE)
|
||||
@PostMapping("/delete")
|
||||
@ApiOperation("删除标准设备")
|
||||
@ApiImplicitParam(name = "ids", value = "标准设备id", required = true)
|
||||
public HttpResult<Boolean> delete(@RequestBody List<String> ids) {
|
||||
String methodDescribe = getMethodDescribe("delete");
|
||||
LogUtil.njcnDebug(log, "{},删除ID数据为:{}", methodDescribe, String.join(StrUtil.COMMA, ids));
|
||||
boolean result = pqStandardDevService.deletePqStandardDevs(ids);
|
||||
if (result) {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe);
|
||||
} else {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, false, methodDescribe);
|
||||
}
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.DOWNLOAD)
|
||||
@PostMapping("/export")
|
||||
@ApiOperation("批量导出标准设备")
|
||||
@ApiImplicitParam(name = "queryParam", value = "查询参数", required = true)
|
||||
public void export(@RequestBody PqStandardDevParam.QueryParam queryParam) {
|
||||
String methodDescribe = getMethodDescribe("export");
|
||||
LogUtil.njcnDebug(log, "{},查询数据为:{}", methodDescribe, queryParam);
|
||||
pqStandardDevService.export(queryParam);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.DOWNLOAD)
|
||||
@PostMapping("/downloadTemplate")
|
||||
@ApiOperation("下载标准设备导入文件模板")
|
||||
public void downloadTemplate() {
|
||||
pqStandardDevService.downloadTemplate();
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPLOAD)
|
||||
@PostMapping("/import")
|
||||
@ApiOperation("导入标准设备")
|
||||
@ApiImplicitParam(name = "file", value = "导入文件", required = true)
|
||||
public HttpResult<Object> importData(@RequestParam("file") MultipartFile file, HttpServletResponse response) {
|
||||
String methodDescribe = getMethodDescribe("importData");
|
||||
LogUtil.njcnDebug(log, "{},导入文件为:{}", methodDescribe, file.getOriginalFilename());
|
||||
boolean fileType = FileUtil.judgeFileIsExcel(file.getOriginalFilename());
|
||||
if (!fileType) {
|
||||
throw new BusinessException(CommonResponseEnum.FILE_XLSX_ERROR);
|
||||
}
|
||||
pqStandardDevService.importData(file, response);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo
|
||||
@GetMapping("/getAll")
|
||||
@ApiOperation("获取所有标准设备")
|
||||
public HttpResult<List<PqStandardDev>> getAll() {
|
||||
String methodDescribe = getMethodDescribe("getAll");
|
||||
LogUtil.njcnDebug(log, "{},查询所有数据", methodDescribe);
|
||||
List<PqStandardDev> result = pqStandardDevService.lambdaQuery().eq(PqStandardDev::getState, DataStateEnum.ENABLE.getCode()).list();
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
// @OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
// @PostMapping("/listByPlanId")
|
||||
// @ApiOperation("查询出指定计划已关联的标准设备")
|
||||
// @ApiImplicitParam(name = "planId", value = "计划id", required = true)
|
||||
// public HttpResult<List<PqStandardDev>> listByPlanId(@RequestParam("planId") String planId) {
|
||||
// String methodDescribe = getMethodDescribe("listByPlanId");
|
||||
// LogUtil.njcnDebug(log, "{},查询数据为:{}", methodDescribe, planId);
|
||||
// List<PqStandardDev> pqDevVOList = pqStandardDevService.listByPlanId(planId);
|
||||
//
|
||||
// return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, pqDevVOList, methodDescribe);
|
||||
// }
|
||||
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@GetMapping("/canBindingList")
|
||||
@ApiOperation("查询可绑定的标准设备")
|
||||
public HttpResult<List<PqStandardDev>> canBindingList() {
|
||||
String methodDescribe = getMethodDescribe("canBindingList");
|
||||
LogUtil.njcnDebug(log, "{},查询可绑定的标准设备", methodDescribe);
|
||||
List<PqStandardDev> result = pqStandardDevService.canBindingList();
|
||||
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.njcn.gather.device.mapper;
|
||||
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import com.njcn.gather.device.pojo.param.PqDevParam;
|
||||
import com.njcn.gather.device.pojo.po.PqDev;
|
||||
import com.njcn.gather.device.pojo.vo.PqDevVO;
|
||||
import com.njcn.gather.device.pojo.vo.PreDetection;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
@@ -26,8 +28,33 @@ public interface PqDevMapper extends MPJBaseMapper<PqDev> {
|
||||
|
||||
void updateReportState(@Param("id")String id);
|
||||
|
||||
/**
|
||||
* 修改检测计划的检测结果
|
||||
*
|
||||
* @param planId
|
||||
* @param checkResult
|
||||
*/
|
||||
void updatePlanCheckResult(@Param("planId")String planId, @Param("checkResult")Integer checkResult);
|
||||
|
||||
/**
|
||||
* 修改检测计划的检测状态
|
||||
*
|
||||
* @param planId
|
||||
* @param testState
|
||||
*/
|
||||
void updatePlanTestState(@Param("planId")String planId, @Param("testState")Integer testState);
|
||||
|
||||
/**
|
||||
* 根据计划id获取脚本id
|
||||
*
|
||||
* @param planId
|
||||
*/
|
||||
String getScriptIdByPlanId(@Param("planId")String planId);
|
||||
|
||||
List<PqDevVO> selectByQueryParam(PqDevParam.QueryParam param);
|
||||
|
||||
PqDevVO selectByDevId(@Param("devId") String devId);
|
||||
|
||||
List<PqDevVO> listByDevIds(@Param("devIds") List<String> devIds);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.njcn.gather.device.mapper;
|
||||
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import com.njcn.gather.device.pojo.po.PqDevSub;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @date 2025-07-02
|
||||
*/
|
||||
public interface PqDevSubMapper extends MPJBaseMapper<PqDevSub> {
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.njcn.gather.device.mapper;
|
||||
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import com.njcn.gather.device.pojo.po.PqStandardDevGain;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @date 2026-01-12
|
||||
*/
|
||||
public interface PqStandardDevGainMapper extends MPJBaseMapper<PqStandardDevGain> {
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.njcn.gather.device.mapper;
|
||||
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import com.njcn.gather.device.pojo.po.PqStandardDevGainRecord;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @date 2026-01-15
|
||||
*/
|
||||
public interface PqStandardDevGainRecordMapper extends MPJBaseMapper<PqStandardDevGainRecord> {
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.njcn.gather.device.mapper;
|
||||
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import com.njcn.gather.device.pojo.po.PqStandardDev;
|
||||
import com.njcn.gather.device.pojo.vo.PreDetection;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author makejava
|
||||
* @date 2025-05-09
|
||||
*/
|
||||
public interface PqStandardDevMapper extends MPJBaseMapper<PqStandardDev> {
|
||||
|
||||
List<PreDetection> listStandardDevPreDetection(@Param("ids") List<String> ids);
|
||||
}
|
||||
|
||||
@@ -4,17 +4,20 @@
|
||||
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="DevResultMap" type="com.njcn.gather.device.pojo.vo.PreDetection">
|
||||
<id column="Id" property="devId" />
|
||||
<id column="Name" property="devName" />
|
||||
<id column="IP" property="devIP" />
|
||||
<result column="Port" property="port" />
|
||||
<result column="Dev_Type" property="devType" />
|
||||
<result column="Series" property="devCode" />
|
||||
<result column="Dev_Key" property="devKey" />
|
||||
<result column="icdType" property="icdType" />
|
||||
<result column="Dev_Chns" property="devChns" />
|
||||
<result column="Dev_Volt" property="devVolt" />
|
||||
<result column="Dev_Curr" property="devCurr" />
|
||||
<id column="Id" property="devId"/>
|
||||
<id column="Name" property="devName"/>
|
||||
<id column="IP" property="devIP"/>
|
||||
<result column="Port" property="port"/>
|
||||
<result column="Dev_Type" property="devType"/>
|
||||
<result column="Series" property="devCode"/>
|
||||
<result column="Dev_Key" property="devKey"/>
|
||||
<result column="icdType" property="icdType"/>
|
||||
<result column="Dev_Chns" property="devChns"/>
|
||||
<result column="Dev_Volt" property="devVolt"/>
|
||||
<result column="Dev_Curr" property="devCurr"/>
|
||||
<result column="Angle" property="angle"/>
|
||||
<result column="Use_Phase_Index" property="usePhaseIndex"/>
|
||||
<result column="Wave_Cmd" property="waveCmd"/>
|
||||
|
||||
<collection
|
||||
property="monitorList"
|
||||
@@ -35,7 +38,10 @@
|
||||
p.name as icdType,
|
||||
t.Dev_Chns,
|
||||
t.Dev_Volt,
|
||||
t.Dev_Curr
|
||||
t.Dev_Curr,
|
||||
p.Angle,
|
||||
p.Use_Phase_Index,
|
||||
t.Wave_Cmd
|
||||
FROM
|
||||
pq_dev d
|
||||
inner join pq_dev_type t on d.Dev_Type = t.id
|
||||
@@ -50,22 +56,85 @@
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<update id="finishPlan" >
|
||||
update ad_plan set Test_State = 2 where id = #{planId}
|
||||
<update id="finishPlan">
|
||||
update ad_plan
|
||||
set Test_State = 2
|
||||
where id = #{planId}
|
||||
</update>
|
||||
|
||||
<update id="updateReportState" >
|
||||
update pq_dev set Report_State = 1 where id = #{id}
|
||||
<update id="updateReportState">
|
||||
update pq_dev
|
||||
set Report_State = 1
|
||||
where id = #{id}
|
||||
</update>
|
||||
|
||||
<update id="updatePlanCheckResult">
|
||||
update ad_plan set Result = #{checkResult} where id = #{planId}
|
||||
update ad_plan
|
||||
set Result = #{checkResult}
|
||||
where id = #{planId}
|
||||
</update>
|
||||
|
||||
<update id="updatePlanTestState">
|
||||
update ad_plan set Test_State = #{testState} where id = #{planId}
|
||||
update ad_plan
|
||||
set Test_State = #{testState}
|
||||
where id = #{planId}
|
||||
</update>
|
||||
|
||||
<select id="getScriptIdByPlanId" resultType="java.lang.String">
|
||||
SELECT Script_Id
|
||||
FROM ad_plan
|
||||
WHERE id = #{planId}
|
||||
</select>
|
||||
|
||||
<select id="selectByQueryParam" resultType="com.njcn.gather.device.pojo.vo.PqDevVO">
|
||||
SELECT dev.*,dev_sub.* FROM pq_dev dev
|
||||
left JOIN pq_dev_sub dev_sub ON dev.Id = dev_sub.Dev_Id
|
||||
<where>
|
||||
dev.state = 1
|
||||
<if test="planIdList!= null and planIdList.size!=0">
|
||||
AND dev.Plan_Id in
|
||||
<foreach collection="planIdList" open="(" close=")" item="item" separator=",">
|
||||
#{item}
|
||||
</foreach>
|
||||
</if>
|
||||
<if test="name!= null and name!= ''">
|
||||
AND dev.Name LIKE CONCAT('%',#{name},'%')
|
||||
</if>
|
||||
<if test="checkStateList!= null and checkStateList.size()!= 0 ">
|
||||
AND dev_sub.Check_State in
|
||||
<foreach collection="checkStateList" open="(" close=")" item="item" separator=",">
|
||||
#{item}
|
||||
</foreach>
|
||||
</if>
|
||||
<if test="checkResult!= null">
|
||||
AND dev_sub.Check_Result = #{checkResult}
|
||||
</if>
|
||||
<if test="reportState!= null">
|
||||
AND dev_sub.Report_State = #{reportState}
|
||||
</if>
|
||||
</where>
|
||||
ORDER BY dev.Create_Time DESC,dev.Name
|
||||
</select>
|
||||
|
||||
<select id="selectByDevId" resultType="com.njcn.gather.device.pojo.vo.PqDevVO">
|
||||
SELECT dev.*, dev_sub.*
|
||||
FROM pq_dev dev
|
||||
left JOIN pq_dev_sub dev_sub ON dev.Id = dev_sub.Dev_Id
|
||||
WHERE dev.Id = #{devId}
|
||||
</select>
|
||||
|
||||
<select id="listByDevIds" resultType="com.njcn.gather.device.pojo.vo.PqDevVO">
|
||||
SELECT dev.*, dev_sub.*
|
||||
FROM pq_dev dev
|
||||
left JOIN pq_dev_sub dev_sub ON dev.Id = dev_sub.Dev_Id
|
||||
WHERE dev.state = 1
|
||||
<if test="devIds!= null and devIds.size() != 0">
|
||||
AND dev.Id in
|
||||
<foreach collection="devIds" open="(" close=")" item="item" separator=",">
|
||||
#{item}
|
||||
</foreach>
|
||||
</if>
|
||||
order by dev.Create_Time DESC, dev.Name ASC
|
||||
</select>
|
||||
</mapper>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.njcn.gather.storage.mapper.AdNonHarmonicMapper">
|
||||
|
||||
<mapper namespace="com.njcn.gather.device.mapper.PqDevSubMapper">
|
||||
|
||||
|
||||
</mapper>
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.njcn.gather.system.config.mapper.SysTestConfigMapper">
|
||||
<mapper namespace="com.njcn.gather.device.mapper.PqStandardDevGainMapper">
|
||||
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.njcn.gather.device.mapper.PqStandardDevGainRecordMapper">
|
||||
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.njcn.gather.device.mapper.PqStandardDevMapper">
|
||||
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="standardDevResultMap" type="com.njcn.gather.device.pojo.vo.PreDetection">
|
||||
<id column="Id" property="devId"/>
|
||||
<id column="Name" property="devName"/>
|
||||
<id column="IP" property="devIP"/>
|
||||
<result column="Port" property="port"/>
|
||||
<result column="Dev_Type" property="devType"/>
|
||||
<result column="Series" property="devCode"/>
|
||||
<result column="Dev_Key" property="devKey"/>
|
||||
<result column="icdType" property="icdType"/>
|
||||
<result column="Dev_Chns" property="devChns"/>
|
||||
<result column="Dev_Volt" property="devVolt"/>
|
||||
<result column="Dev_Curr" property="devCurr"/>
|
||||
<result column="Angle" property="angle"/>
|
||||
<result column="Use_Phase_Index" property="usePhaseIndex"/>
|
||||
<result column="Wave_Cmd" property="waveCmd"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="listStandardDevPreDetection" resultMap="standardDevResultMap">
|
||||
select
|
||||
standard_dev.Id,
|
||||
standard_dev.Name,
|
||||
standard_dev.IP,
|
||||
standard_dev.Port,
|
||||
dev_type.name as Dev_Type,
|
||||
standard_dev.Series,
|
||||
standard_dev.Dev_Key,
|
||||
icd_path.Name as icdType,
|
||||
dev_type.Dev_Chns,
|
||||
dev_type.Dev_Volt,
|
||||
dev_type.Dev_Curr,
|
||||
icd_path.Angle,
|
||||
icd_path.Use_Phase_Index,
|
||||
dev_type.Wave_Cmd
|
||||
from pq_standard_dev standard_dev
|
||||
inner join pq_dev_type dev_type on standard_dev.Dev_Type = dev_type.id
|
||||
inner join pq_icd_path icd_path on dev_type.icd = icd_path.id
|
||||
where standard_dev.Id in
|
||||
<foreach collection="ids" item="id" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</select>
|
||||
</mapper>
|
||||
|
||||
@@ -10,6 +10,7 @@ import lombok.Getter;
|
||||
public enum DevReportStateEnum {
|
||||
NOT_GENERATED("未生成", 0),
|
||||
GENERATED("已生成", 1),
|
||||
GENERATED_UPLOADED("已生成且已上传", 3),
|
||||
UNCHECKED("未检", 2);
|
||||
|
||||
private final Integer value;
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
package com.njcn.gather.device.pojo.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum DevResponseEnum {
|
||||
//NAME_REPEAT("A001001", "名称重复"),
|
||||
|
||||
IMPORT_DATA_FAIL("A001002", "导入数据失败"),
|
||||
SERIES_AND_DEVKEY_NOT_BLANK("A001003", "当为加密设备时,加密设备的序列号和设备密钥不能为空"),
|
||||
PQ_SOURCE_GEN_NAME_ERROR("A001004", "检测源生成名称出错"),
|
||||
ERR_SOURCE_GEN_NAME_ERROR("A001005", "误差体系生成名称出错"),
|
||||
PQ_ERRSYS_GEN_NAME_ERROR("A001005", "误差体系生成名称出错"),
|
||||
PLAN_HAS_DEVICE_BIND("A001006", "检测计划下已绑定设备,请先解绑设备"),
|
||||
PQ_DEV_REPEAT("A001007", "重复的被检设备"),
|
||||
PQ_DEV_HAS_MONITOR("A001008", "该设备下关联有监测点,请先移除监测点"),
|
||||
HAS_NOT_UNCHECKED_DEVICE("A001009", "设备在检测中或已被检测过,请勿解除绑定"),
|
||||
IMPORT_PLAN_DATA_FAIL("A001010", "导入的检测计划为空"),
|
||||
IMPORT_DATA_FORMAT_FAIL("A001011", "导入数据格式错误"),
|
||||
IMPORT_SOURCE_ERROR("A001012","当前模式下一个检测计划只能有一个检测源" ),
|
||||
IMPORT_DATASOURCE_ERROR("A001013","当前模式下一个检测计划只能有一个数据源" ),
|
||||
DEV_UN_CHECKED("A001013","装置还未检测完成!" ),
|
||||
DEV_UN_REPORT("A001013","装置报告未生成!" ),
|
||||
DEVICE_DIS_ERROR("A001014","装置配置异常" ),
|
||||
DEVICE_DELETE("A001015","设备无法删除,已绑定计划!" )
|
||||
|
||||
;
|
||||
|
||||
private final String message;
|
||||
private final String code;
|
||||
|
||||
|
||||
DevResponseEnum(String code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
@@ -19,4 +19,13 @@ public enum PatternEnum {
|
||||
this.value = value;
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public static PatternEnum getEnum(String value) {
|
||||
for (PatternEnum patternEnum : PatternEnum.values()) {
|
||||
if (patternEnum.getValue().equals(value)) {
|
||||
return patternEnum;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package com.njcn.gather.device.pojo.param;
|
||||
|
||||
import com.njcn.common.pojo.constant.PatternRegex;
|
||||
import com.njcn.gather.monitor.pojo.param.PqMonitorParam;
|
||||
import com.njcn.gather.device.pojo.constant.DevValidMessage;
|
||||
import com.njcn.gather.pojo.constant.DetectionValidMessage;
|
||||
import com.njcn.web.pojo.annotation.DateTimeStrValid;
|
||||
import com.njcn.web.pojo.param.BaseParam;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
@@ -22,61 +22,55 @@ import java.util.List;
|
||||
public class PqDevParam {
|
||||
|
||||
@ApiModelProperty(value = "名称", required = true)
|
||||
//@NotBlank(message = DevValidMessage.NAME_NOT_BLANK)
|
||||
//@Pattern(regexp = PatternRegex.DEV_NAME_REGEX, message = DevValidMessage.NAME_FORMAT_ERROR)
|
||||
@NotBlank(message = DetectionValidMessage.NAME_NOT_BLANK)
|
||||
private String name;
|
||||
|
||||
@ApiModelProperty(value = "设备模式,字典表(数字、模拟、比对)", required = true)
|
||||
@NotBlank(message = DevValidMessage.PATTERN_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.SYSTEM_ID, message = DevValidMessage.PATTERN_FORMAT_ERROR)
|
||||
@NotBlank(message = DetectionValidMessage.PATTERN_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.SYSTEM_ID, message = DetectionValidMessage.PATTERN_FORMAT_ERROR)
|
||||
private String pattern;
|
||||
|
||||
@ApiModelProperty(value = "设备类型,字典表", required = true)
|
||||
@NotBlank(message = DevValidMessage.DEV_TYPE_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.SYSTEM_ID, message = DevValidMessage.DEV_TYPE_FORMAT_ERROR)
|
||||
@ApiModelProperty(value = "设备类型", required = true)
|
||||
@NotBlank(message = DetectionValidMessage.DEV_TYPE_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.SYSTEM_ID, message = DetectionValidMessage.DEV_TYPE_FORMAT_ERROR)
|
||||
private String devType;
|
||||
|
||||
@ApiModelProperty(value = "设备厂家,字典表", required = true)
|
||||
//@NotBlank(message = DevValidMessage.MANUFACTURER_NOT_BLANK)
|
||||
//@Pattern(regexp = PatternRegex.SYSTEM_ID, message = DevValidMessage.MANUFACTURER_FORMAT_ERROR)
|
||||
private String manufacturer;
|
||||
|
||||
@ApiModelProperty(value = "出厂日期", required = true)
|
||||
//@NotBlank(message = DevValidMessage.CREATEDATETIME_NOT_NULL)
|
||||
//@DateTimeStrValid(format = "yyyy-MM-dd", message = DevValidMessage.CREATEDATETIME_FORMAT_ERROR)
|
||||
private String createDate;
|
||||
|
||||
@ApiModelProperty(value = "设备序列号", required = true)
|
||||
@NotBlank(message = DevValidMessage.FACTORYNO_NOT_BLANK)
|
||||
@ApiModelProperty(value = "装置编号", required = true)
|
||||
@NotBlank(message = DetectionValidMessage.FACTORYNO_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.DEV_CREATE_ID_REGEX, message = DetectionValidMessage.DEV_CREATE_ID_FORMAT_ERROR)
|
||||
private String createId;
|
||||
|
||||
@ApiModelProperty(value = "固件版本", required = true)
|
||||
//@NotBlank(message = DevValidMessage.FIRMWARE_NOT_BLANK)
|
||||
private String hardwareVersion;
|
||||
|
||||
@ApiModelProperty(value = "软件版本", required = true)
|
||||
//@NotBlank(message = DevValidMessage.SOFTWARE_NOT_BLANK)
|
||||
private String softwareVersion;
|
||||
|
||||
@ApiModelProperty(value = "通讯协议", required = true)
|
||||
@NotBlank(message = DevValidMessage.PROTOCOL_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.SYSTEM_ID, message = DevValidMessage.PROTOCOL_FORMAT_ERROR)
|
||||
@NotBlank(message = DetectionValidMessage.PROTOCOL_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.SYSTEM_ID, message = DetectionValidMessage.PROTOCOL_FORMAT_ERROR)
|
||||
private String protocol;
|
||||
|
||||
@ApiModelProperty(value = "IP地址", required = true)
|
||||
@NotBlank(message = DevValidMessage.IP_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.IP_REGEX, message = DevValidMessage.IP_FORMAT_ERROR)
|
||||
@NotBlank(message = DetectionValidMessage.IP_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.IP_REGEX, message = DetectionValidMessage.IP_FORMAT_ERROR)
|
||||
private String ip;
|
||||
|
||||
@ApiModelProperty(value = "端口号", required = true)
|
||||
@NotNull(message = DevValidMessage.PORT_NOT_NULL)
|
||||
@Range(min = 1, max = 65535, message = DevValidMessage.PORT_RANGE_ERROR)
|
||||
@NotNull(message = DetectionValidMessage.PORT_NOT_NULL)
|
||||
@Range(min = 1, max = 65535, message = DetectionValidMessage.PORT_RANGE_ERROR)
|
||||
private Integer port;
|
||||
|
||||
@ApiModelProperty(value = "装置是否为加密版本", required = true)
|
||||
@NotNull(message = DevValidMessage.ENCRYPTION_NOT_NULL)
|
||||
@Min(value = 0, message = DevValidMessage.ENCRYPTION_FLAG_FORMAT_ERROR)
|
||||
@Max(value = 1, message = DevValidMessage.ENCRYPTION_FLAG_FORMAT_ERROR)
|
||||
@NotNull(message = DetectionValidMessage.ENCRYPTION_NOT_NULL)
|
||||
@Min(value = 0, message = DetectionValidMessage.ENCRYPTION_FLAG_FORMAT_ERROR)
|
||||
@Max(value = 1, message = DetectionValidMessage.ENCRYPTION_FLAG_FORMAT_ERROR)
|
||||
private Integer encryptionFlag;
|
||||
|
||||
@ApiModelProperty("装置识别码(3ds加密)")
|
||||
@@ -89,7 +83,7 @@ public class PqDevParam {
|
||||
private String sampleId;
|
||||
|
||||
@ApiModelProperty(value = "送样日期")
|
||||
@DateTimeStrValid(message = DevValidMessage.ARRIVE_DATE_FORMAT_ERROR)
|
||||
@DateTimeStrValid(message = DetectionValidMessage.ARRIVE_DATE_FORMAT_ERROR)
|
||||
private String arrivedDate;
|
||||
|
||||
@ApiModelProperty("所属地市名称")
|
||||
@@ -104,18 +98,16 @@ public class PqDevParam {
|
||||
@ApiModelProperty("报告路径")
|
||||
private String reportPath;
|
||||
|
||||
@ApiModelProperty("设备关键信息二维码")
|
||||
private String qrCode;
|
||||
// @ApiModelProperty("设备关键信息二维码")
|
||||
// private String qrCode;
|
||||
|
||||
@ApiModelProperty(value = "检测次数,默认为0", required = true)
|
||||
@NotNull(message = DevValidMessage.RECHECK_NUM_NOT_NULL)
|
||||
@Min(value = 0, message = DevValidMessage.RECHECK_NUM_FORMAT_ERROR)
|
||||
private Integer reCheckNum;
|
||||
// @ApiModelProperty(value = "检测次数,默认为0", required = true)
|
||||
// @NotNull(message = DetectionValidMessage.RECHECK_NUM_NOT_NULL)
|
||||
// @Min(value = 0, message = DetectionValidMessage.RECHECK_NUM_FORMAT_ERROR)
|
||||
// private Integer reCheckNum;
|
||||
|
||||
@ApiModelProperty("是否支持系数校准")
|
||||
@Min(value = 0, message = DevValidMessage.FACTOR_FLAG_FORMAT_ERROR)
|
||||
@Max(value = 1, message = DevValidMessage.FACTOR_FLAG_FORMAT_ERROR)
|
||||
private String factorFlag;
|
||||
private Integer factorFlag;
|
||||
|
||||
@ApiModelProperty("监测点台账列表")
|
||||
@Valid
|
||||
@@ -123,13 +115,27 @@ public class PqDevParam {
|
||||
|
||||
@ApiModelProperty("icdId")
|
||||
private String icdId;
|
||||
//
|
||||
// @ApiModelProperty("power")
|
||||
// private String power;
|
||||
|
||||
@ApiModelProperty("预投计划")
|
||||
private String preinvestmentPlan;
|
||||
|
||||
@ApiModelProperty("委托方")
|
||||
private String delegate;
|
||||
|
||||
@ApiModelProperty("被检通道")
|
||||
private String inspectChannel;
|
||||
|
||||
@ApiModelProperty("投运日期")
|
||||
@DateTimeStrValid(message = DetectionValidMessage.OPERATION_DATE_FORMAT_ERROR)
|
||||
private String operationDate;
|
||||
|
||||
@ApiModelProperty("定验日期")
|
||||
@DateTimeStrValid(message = DetectionValidMessage.INSPECT_DATE_FORMAT_ERROR)
|
||||
private String inspectDate;
|
||||
|
||||
@ApiModelProperty("是否为导入设备")
|
||||
private Integer importFlag;
|
||||
|
||||
/**
|
||||
* 更新操作实体
|
||||
*/
|
||||
@@ -138,29 +144,9 @@ public class PqDevParam {
|
||||
public static class UpdateParam extends PqDevParam {
|
||||
|
||||
@ApiModelProperty(value = "id", required = true)
|
||||
@NotBlank(message = DevValidMessage.ID_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.SYSTEM_ID, message = DevValidMessage.ID_FORMAT_ERROR)
|
||||
@NotBlank(message = DetectionValidMessage.ID_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.SYSTEM_ID, message = DetectionValidMessage.ID_FORMAT_ERROR)
|
||||
private String id;
|
||||
|
||||
// @ApiModelProperty("检测状态")
|
||||
// @Min(value = 0, message = DevValidMessage.CHECK_STATE_FORMAT_ERROR)
|
||||
// @Max(value = 3, message = DevValidMessage.CHECK_STATE_FORMAT_ERROR)
|
||||
// private Integer checkState;
|
||||
//
|
||||
// @ApiModelProperty("检测结果")
|
||||
// @Min(value = 0, message = DevValidMessage.CHECK_RESULT_FORMAT_ERROR)
|
||||
// @Max(value = 2, message = DevValidMessage.CHECK_RESULT_FORMAT_ERROR)
|
||||
// private Integer checkResult;
|
||||
//
|
||||
// @ApiModelProperty("报告状态")
|
||||
// @Min(value = 0, message = DevValidMessage.REPORT_STATE_FORMAT_ERROR)
|
||||
// @Max(value = 2, message = DevValidMessage.REPORT_STATE_FORMAT_ERROR)
|
||||
// private Integer reportState;
|
||||
//
|
||||
// @ApiModelProperty("归档状态")
|
||||
// @Min(value = 0, message = DevValidMessage.DOCUMENT_STATE_FORMAT_ERROR)
|
||||
// @Max(value = 1, message = DevValidMessage.DOCUMENT_STATE_FORMAT_ERROR)
|
||||
// private Integer documentState;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,7 +156,6 @@ public class PqDevParam {
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class QueryParam extends BaseParam {
|
||||
@ApiModelProperty("名称")
|
||||
// @Pattern(regexp = PatternRegex.DEV_NAME_REGEX, message = DevValidMessage.NAME_FORMAT_ERROR)
|
||||
private String name;
|
||||
|
||||
@ApiModelProperty(value = "设备模式,字典表(数字、模拟、比对)")
|
||||
@@ -180,46 +165,68 @@ public class PqDevParam {
|
||||
private String manufacturer;
|
||||
|
||||
@ApiModelProperty("检测计划ID")
|
||||
//@Pattern(regexp = PatternRegex.SYSTEM_ID, message = DevValidMessage.PLAN_ID_FORMAT_ERROR)
|
||||
private String planId;
|
||||
private List<String> planIdList;
|
||||
|
||||
@ApiModelProperty("检测状态列表")
|
||||
private List<
|
||||
@Min(value = 0, message = DevValidMessage.CHECK_STATE_FORMAT_ERROR)
|
||||
@Max(value = 3, message = DevValidMessage.CHECK_STATE_FORMAT_ERROR) Integer> checkStateList;
|
||||
@Min(value = 0, message = DetectionValidMessage.CHECK_STATE_FORMAT_ERROR)
|
||||
@Max(value = 3, message = DetectionValidMessage.CHECK_STATE_FORMAT_ERROR) Integer> checkStateList;
|
||||
|
||||
@ApiModelProperty("检测结果")
|
||||
@Min(value = 0, message = DevValidMessage.CHECK_RESULT_FORMAT_ERROR)
|
||||
@Max(value = 2, message = DevValidMessage.CHECK_RESULT_FORMAT_ERROR)
|
||||
@Min(value = 0, message = DetectionValidMessage.CHECK_RESULT_FORMAT_ERROR)
|
||||
@Max(value = 2, message = DetectionValidMessage.CHECK_RESULT_FORMAT_ERROR)
|
||||
private Integer checkResult;
|
||||
|
||||
@ApiModelProperty("报告状态")
|
||||
@Min(value = 0, message = DevValidMessage.REPORT_STATE_FORMAT_ERROR)
|
||||
@Max(value = 2, message = DevValidMessage.REPORT_STATE_FORMAT_ERROR)
|
||||
@Min(value = 0, message = DetectionValidMessage.REPORT_STATE_FORMAT_ERROR)
|
||||
@Max(value = 2, message = DetectionValidMessage.REPORT_STATE_FORMAT_ERROR)
|
||||
private Integer reportState;
|
||||
|
||||
@ApiModelProperty("所属地市名称")
|
||||
private String cityName;
|
||||
|
||||
@ApiModelProperty("所属供电公司名称")
|
||||
private String gdName;
|
||||
|
||||
@ApiModelProperty("所属电站名称")
|
||||
private String subName;
|
||||
|
||||
@ApiModelProperty("地市")
|
||||
private String region;
|
||||
|
||||
@ApiModelProperty("是否分配")
|
||||
private Integer assign;
|
||||
@ApiModelProperty("关键词")
|
||||
private String keywords;
|
||||
@ApiModelProperty("主计划ID")
|
||||
private String planId;
|
||||
@ApiModelProperty("是否分配子计划")
|
||||
private Integer assignSub;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class DeleteParam {
|
||||
@ApiModelProperty(value = "ids")
|
||||
private List<@Pattern(regexp = PatternRegex.SYSTEM_ID, message = DevValidMessage.ID_FORMAT_ERROR) String> ids;
|
||||
private List<@Pattern(regexp = PatternRegex.SYSTEM_ID, message = DetectionValidMessage.ID_FORMAT_ERROR) String> ids;
|
||||
|
||||
@ApiModelProperty(value = "设备模式,字典表(数字、模拟、比对)", required = true)
|
||||
@NotBlank(message = DevValidMessage.PATTERN_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.SYSTEM_ID, message = DevValidMessage.PATTERN_FORMAT_ERROR)
|
||||
@NotBlank(message = DetectionValidMessage.PATTERN_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.SYSTEM_ID, message = DetectionValidMessage.PATTERN_FORMAT_ERROR)
|
||||
private String pattern;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class BindPlanParam {
|
||||
@ApiModelProperty("检测计划ID")
|
||||
@NotNull(message = DevValidMessage.PLAN_ID_NOT_NULL)
|
||||
@Pattern(regexp = PatternRegex.SYSTEM_ID, message = DevValidMessage.PLAN_ID_FORMAT_ERROR)
|
||||
@NotBlank(message = DetectionValidMessage.PLAN_ID_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.SYSTEM_ID, message = DetectionValidMessage.PLAN_ID_FORMAT_ERROR)
|
||||
private String planId;
|
||||
|
||||
@ApiModelProperty("被检设备ID列表")
|
||||
@NotNull(message = DevValidMessage.PQ_DEV_IDS_NOT_NULL)
|
||||
private List<String> pqDevIds;
|
||||
@NotNull(message = DetectionValidMessage.PQ_DEV_IDS_NOT_NULL)
|
||||
private List<String> devIds;
|
||||
|
||||
@ApiModelProperty("0-解绑、1-绑定")
|
||||
private Integer bindFlag;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.njcn.gather.device.pojo.param;
|
||||
|
||||
import com.njcn.common.pojo.constant.PatternRegex;
|
||||
import com.njcn.gather.pojo.constant.DetectionValidMessage;
|
||||
import com.njcn.web.pojo.param.BaseParam;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.hibernate.validator.constraints.Range;
|
||||
|
||||
import javax.validation.constraints.*;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2025-05-09
|
||||
*/
|
||||
@Data
|
||||
public class PqStandardDevParam {
|
||||
@ApiModelProperty(value = "名称", required = true)
|
||||
@NotBlank(message = DetectionValidMessage.NAME_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.DEV_NAME_REGEX, message = DetectionValidMessage.NAME_FORMAT_ERROR)
|
||||
private String name;
|
||||
|
||||
@ApiModelProperty(value = "设备类型", required = true)
|
||||
@NotBlank(message = DetectionValidMessage.DEV_TYPE_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.SYSTEM_ID, message = DetectionValidMessage.DEV_TYPE_FORMAT_ERROR)
|
||||
private String devType;
|
||||
|
||||
@ApiModelProperty(value = "设备厂家", required = true)
|
||||
@NotBlank(message = DetectionValidMessage.MANUFACTURER_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.SYSTEM_ID, message = DetectionValidMessage.MANUFACTURER_FORMAT_ERROR)
|
||||
private String manufacturer;
|
||||
|
||||
@ApiModelProperty(value = "通信协议", required = true)
|
||||
@NotBlank(message = DetectionValidMessage.PROTOCOL_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.SYSTEM_ID, message = DetectionValidMessage.PROTOCOL_FORMAT_ERROR)
|
||||
private String protocol;
|
||||
|
||||
@ApiModelProperty(value = "ip地址", required = true)
|
||||
@NotBlank(message = DetectionValidMessage.IP_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.IP_REGEX, message = DetectionValidMessage.IP_FORMAT_ERROR)
|
||||
private String ip;
|
||||
|
||||
@ApiModelProperty(value = "端口", required = true)
|
||||
@NotNull(message = DetectionValidMessage.PORT_NOT_NULL)
|
||||
@Range(min = 1, max = 65535, message = DetectionValidMessage.PORT_RANGE_ERROR)
|
||||
private Integer port;
|
||||
|
||||
@ApiModelProperty(value = "装置是否为加密版本", required = true)
|
||||
@NotNull(message = DetectionValidMessage.ENCRYPTION_NOT_NULL)
|
||||
@Min(value = 0, message = DetectionValidMessage.ENCRYPTION_FLAG_FORMAT_ERROR)
|
||||
@Max(value = 1, message = DetectionValidMessage.ENCRYPTION_FLAG_FORMAT_ERROR)
|
||||
private Integer encryptionFlag;
|
||||
|
||||
@ApiModelProperty("识别码(3ds加密)")
|
||||
private String series;
|
||||
|
||||
@ApiModelProperty("秘钥(3ds加密)")
|
||||
private String devKey;
|
||||
|
||||
@ApiModelProperty(value = "可检通道", required = true)
|
||||
private String inspectChannel;
|
||||
|
||||
/**
|
||||
* 分页查询实体
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class QueryParam extends BaseParam {
|
||||
@ApiModelProperty(value = "名称", required = true)
|
||||
private String name;
|
||||
|
||||
@ApiModelProperty(value = "设备厂家", required = true)
|
||||
private String manufacturer;
|
||||
|
||||
@ApiModelProperty(value = "设备类型", required = true)
|
||||
private String devType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新操作实体
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class UpdateParam extends PqStandardDevParam {
|
||||
@ApiModelProperty(value = "标准设备id", required = true)
|
||||
@NotBlank(message = DetectionValidMessage.ID_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.SYSTEM_ID, message = DetectionValidMessage.ID_FORMAT_ERROR)
|
||||
private String id;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.njcn.gather.device.pojo.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
@@ -10,12 +9,13 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
|
||||
import com.njcn.db.mybatisplus.bo.BaseEntity;
|
||||
import com.njcn.gather.monitor.pojo.po.PqMonitor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
@@ -115,7 +115,6 @@ public class PqDev extends BaseEntity implements Serializable {
|
||||
/**
|
||||
* 送样日期
|
||||
*/
|
||||
@TableField(updateStrategy = FieldStrategy.IGNORED)
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@JsonDeserialize(using = LocalDateDeserializer.class)
|
||||
@JsonSerialize(using = LocalDateSerializer.class)
|
||||
@@ -136,67 +135,61 @@ public class PqDev extends BaseEntity implements Serializable {
|
||||
*/
|
||||
private String subName;
|
||||
|
||||
/**
|
||||
* 检测状态
|
||||
*/
|
||||
private Integer checkState;
|
||||
|
||||
/**
|
||||
* 检测结果
|
||||
*/
|
||||
private Integer checkResult;
|
||||
|
||||
/**
|
||||
* 报告状态
|
||||
*/
|
||||
private Integer reportState;
|
||||
|
||||
/**
|
||||
* 报告路径
|
||||
*/
|
||||
private String reportPath;
|
||||
|
||||
/**
|
||||
* 设备关键信息二维码
|
||||
*/
|
||||
private String qrCode;
|
||||
|
||||
/**
|
||||
* 检测次数,默认为0
|
||||
*/
|
||||
@TableField(value = "ReCheck_Num")
|
||||
private Integer reCheckNum;
|
||||
|
||||
/**
|
||||
* 状态:0-删除 1-正常
|
||||
*/
|
||||
private Integer state;
|
||||
|
||||
/**
|
||||
* 检测计划id
|
||||
*/
|
||||
private String planId;
|
||||
|
||||
/**
|
||||
* 守时检测结果(0:不合格, 1:合格)
|
||||
*/
|
||||
private Integer timeCheckResult;
|
||||
|
||||
/**
|
||||
* 是否支持系数校准(0:不支持,1:支持)
|
||||
*/
|
||||
private Integer factorFlag;
|
||||
|
||||
/**
|
||||
* 系数校准结果(0:不合格,1:合格)
|
||||
* 预投计划
|
||||
*/
|
||||
private Integer factorCheckResult;
|
||||
|
||||
@TableField("Check_Time")
|
||||
private LocalDateTime checkTime;
|
||||
|
||||
@TableField("Preinvestment_Plan")
|
||||
private String preinvestmentPlan;
|
||||
|
||||
/**
|
||||
* 委托方
|
||||
*/
|
||||
private String delegate;
|
||||
|
||||
/**
|
||||
* 可检通道
|
||||
*/
|
||||
private String inspectChannel;
|
||||
|
||||
/**
|
||||
* 定检日期
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@JsonDeserialize(using = LocalDateDeserializer.class)
|
||||
@JsonSerialize(using = LocalDateSerializer.class)
|
||||
private LocalDate inspectDate;
|
||||
|
||||
/**
|
||||
* 谐波系统设备id
|
||||
*/
|
||||
private String harmSysId;
|
||||
|
||||
/**
|
||||
* 是否为导入设备(比对式使用) 0-否 1-是
|
||||
*/
|
||||
private Integer importFlag;
|
||||
|
||||
/**
|
||||
* 状态:0-删除 1-正常
|
||||
*/
|
||||
private Integer state;
|
||||
|
||||
@TableField(exist = false)
|
||||
private List<PqMonitor> monitorList;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.njcn.gather.device.pojo.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @date 2025-07-04
|
||||
*/
|
||||
@Data
|
||||
@TableName("pq_dev_sub")
|
||||
public class PqDevSub {
|
||||
private static final long serialVersionUID = -93146063424890267L;
|
||||
/**
|
||||
* 关联pq_dev表的id字段
|
||||
*/
|
||||
private String devId;
|
||||
|
||||
/**
|
||||
* 检测状态 0-未检、1检测中、2检测完成、3归档
|
||||
*/
|
||||
private Integer checkState;
|
||||
|
||||
/**
|
||||
* 检测结果 0不符合、1符合、2未检
|
||||
*/
|
||||
private Integer checkResult;
|
||||
|
||||
/**
|
||||
* 报告生成状态 0未生成、1已生成、2未检
|
||||
*/
|
||||
private Integer reportState;
|
||||
|
||||
/**
|
||||
* 检测次数
|
||||
*/
|
||||
@TableField(value = "ReCheck_Num")
|
||||
private Integer recheckNum;
|
||||
|
||||
/**
|
||||
* 守时检测结果 0:不合格, 1:合格,2:/表示没有做守时检测
|
||||
*/
|
||||
private Integer timeCheckResult;
|
||||
|
||||
/**
|
||||
* 系数校准结果 0:不合格,1:合格,2:/表示没有做系数校准
|
||||
*/
|
||||
private Integer factorCheckResult;
|
||||
|
||||
/**
|
||||
* 检测人
|
||||
*/
|
||||
private String checkBy;
|
||||
|
||||
/**
|
||||
* 检测时间
|
||||
*/
|
||||
@TableField("Check_Time")
|
||||
private LocalDateTime checkTime;
|
||||
|
||||
/**
|
||||
* 预检测耗时
|
||||
*/
|
||||
private Integer preDetectTime;
|
||||
|
||||
/**
|
||||
* 系数校准耗时
|
||||
*/
|
||||
private Integer coefficientTime;
|
||||
|
||||
/**
|
||||
* 正式检测耗时
|
||||
*/
|
||||
private Integer formalCheckTime;
|
||||
|
||||
/**
|
||||
* 温度
|
||||
*/
|
||||
private Float temperature;
|
||||
|
||||
/**
|
||||
* 相对湿度
|
||||
*/
|
||||
private Float humidity;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.njcn.gather.device.pojo.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.njcn.db.mybatisplus.bo.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author makejava
|
||||
* @date 2025-05-09
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("pq_standard_dev")
|
||||
public class PqStandardDev extends BaseEntity implements Serializable {
|
||||
private static final long serialVersionUID = 932459726326242984L;
|
||||
/**
|
||||
* 标准设备id
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 标准设备-名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 标准设备-设备类型
|
||||
*/
|
||||
private String devType;
|
||||
|
||||
/**
|
||||
* 标准设备-设备厂家
|
||||
*/
|
||||
private String manufacturer;
|
||||
|
||||
/**
|
||||
* 可检通道(可多选,中间使用英文逗号隔开)
|
||||
*/
|
||||
private String inspectChannel;
|
||||
|
||||
/**
|
||||
* 通信协议
|
||||
*/
|
||||
private String protocol;
|
||||
|
||||
/**
|
||||
* IP地址
|
||||
*/
|
||||
private String ip;
|
||||
|
||||
/**
|
||||
* 端口号
|
||||
*/
|
||||
private Integer port;
|
||||
|
||||
/**
|
||||
* 是否加密(0否、1是)
|
||||
*/
|
||||
private Integer encryptionFlag;
|
||||
|
||||
/**
|
||||
* 识别码
|
||||
*/
|
||||
private String series;
|
||||
|
||||
/**
|
||||
* 密钥
|
||||
*/
|
||||
private String devKey;
|
||||
|
||||
private Integer state;
|
||||
|
||||
@TableField(exist = false)
|
||||
private boolean disabled;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.njcn.gather.device.pojo.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.njcn.db.mybatisplus.bo.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @date 2026-01-12
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("pq_standard_dev_gain")
|
||||
public class PqStandardDevGain extends BaseEntity implements Serializable {
|
||||
private static final long serialVersionUID = -46280767885558804L;
|
||||
|
||||
|
||||
/**
|
||||
* 标准设备Id_通道号
|
||||
*/
|
||||
@TableId
|
||||
private String stdDevMonitorId;
|
||||
|
||||
private Integer uaGain;
|
||||
|
||||
private Integer ubGain;
|
||||
|
||||
|
||||
private Integer ucGain;
|
||||
|
||||
|
||||
private Integer u0Gain;
|
||||
|
||||
|
||||
private Integer iaGain;
|
||||
|
||||
|
||||
private Integer ibGain;
|
||||
|
||||
|
||||
private Integer icGain;
|
||||
|
||||
|
||||
private Integer i0Gain;
|
||||
|
||||
|
||||
private Integer uabGain;
|
||||
|
||||
|
||||
private Integer ubcGain;
|
||||
|
||||
|
||||
private Integer ucaGain;
|
||||
|
||||
private Integer state;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.njcn.gather.device.pojo.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.njcn.db.mybatisplus.bo.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @date 2026-01-15
|
||||
*/
|
||||
@Data
|
||||
@TableName("pq_standard_dev_gain_record")
|
||||
public class PqStandardDevGainRecord implements Serializable {
|
||||
private static final long serialVersionUID = 477276215572686991L;
|
||||
|
||||
/**
|
||||
* 标准设备监测点id,由设备id_通道号组成
|
||||
*/
|
||||
private String stdDevMonitorId;
|
||||
|
||||
/**
|
||||
* 被检设备监测点id,由设备id_通道号组成
|
||||
*/
|
||||
private String devMonitorId;
|
||||
|
||||
/**
|
||||
* 系数下发次数
|
||||
*/
|
||||
private Integer num;
|
||||
|
||||
}
|
||||
|
||||
@@ -2,9 +2,8 @@ package com.njcn.gather.device.pojo.vo;
|
||||
|
||||
import cn.afterturn.easypoi.excel.annotation.Excel;
|
||||
import com.njcn.common.pojo.constant.PatternRegex;
|
||||
import com.njcn.gather.device.pojo.constant.DevValidMessage;
|
||||
import com.njcn.gather.pojo.constant.DetectionValidMessage;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.hibernate.validator.constraints.Range;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
@@ -19,41 +18,42 @@ import javax.validation.constraints.Pattern;
|
||||
public class CNDevExcel {
|
||||
|
||||
@Excel(name = "预投计划*", width = 20, orderNum = "1")
|
||||
@NotBlank(message = DetectionValidMessage.PREINVESTMENT_PLAN_NOT_BLANK)
|
||||
private String preinvestmentPlan;
|
||||
|
||||
@Excel(name = "设备编号(开始编号-结束编号,编号为数字)*", width = 50, orderNum = "2")
|
||||
@NotBlank(message = DevValidMessage.NAME_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.CN_DEV_NAME_REGEX, message = DevValidMessage.NAME_FORMAT_ERROR)
|
||||
@Excel(name = "装置编号(开始编号-结束编号,编号为数字)*", width = 50, orderNum = "2")
|
||||
@NotBlank(message = DetectionValidMessage.NAME_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.CN_DEV_NAME_REGEX_IMPORT, message = DetectionValidMessage.CN_DEV_NAME_FORMAT_ERROR)
|
||||
private String name;
|
||||
|
||||
@Excel(name = "设备类型*", width = 20, orderNum = "3")
|
||||
@NotBlank(message = DevValidMessage.DEV_TYPE_NOT_BLANK)
|
||||
@NotBlank(message = DetectionValidMessage.DEV_TYPE_NOT_BLANK)
|
||||
private String devType;
|
||||
|
||||
@Excel(name = "通讯协议*", width = 15, orderNum = "4")
|
||||
@NotBlank(message = DevValidMessage.PROTOCOL_NOT_BLANK)
|
||||
@NotBlank(message = DetectionValidMessage.PROTOCOL_NOT_BLANK)
|
||||
private String protocol;
|
||||
|
||||
@Excel(name = "是否加密*", width = 20, replace = {"否_0", "是_1"}, orderNum = "5")
|
||||
@NotNull(message = DevValidMessage.ENCRYPTION_NOT_NULL)
|
||||
@NotNull(message = DetectionValidMessage.ENCRYPTION_NOT_NULL)
|
||||
private Integer encryptionFlag;
|
||||
|
||||
@Excel(name = "识别码", width = 30, orderNum = "6")
|
||||
@Excel(name = "识别码(当加密时必填)", width = 30, orderNum = "6")
|
||||
private String series;
|
||||
|
||||
@Excel(name = "秘钥", width = 30, orderNum = "7")
|
||||
@Excel(name = "秘钥(当加密时必填)", width = 30, orderNum = "7")
|
||||
private String devKey;
|
||||
|
||||
@Excel(name = "是否支持系数校准*", width = 25, replace = {"否_0", "是_1"}, orderNum = "8")
|
||||
private Integer factorFlag;
|
||||
|
||||
@Excel(name = "IP地址*", width = 20, replace = {"否_0", "是_1"}, orderNum = "9")
|
||||
@NotBlank(message = DevValidMessage.IP_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.IP_REGEX, message = DevValidMessage.IP_FORMAT_ERROR)
|
||||
@Excel(name = "IP地址*", width = 20, orderNum = "9")
|
||||
@NotBlank(message = DetectionValidMessage.IP_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.IP_REGEX, message = DetectionValidMessage.IP_FORMAT_ERROR)
|
||||
private String ip;
|
||||
|
||||
@Excel(name = "端口号*", width = 15, orderNum = "10")
|
||||
@NotNull(message = DevValidMessage.PORT_NOT_NULL)
|
||||
@Range(min = 1, max = 65535, message = DevValidMessage.PORT_RANGE_ERROR)
|
||||
@NotNull(message = DetectionValidMessage.PORT_NOT_NULL)
|
||||
@Range(min = 1, max = 65535, message = DetectionValidMessage.PORT_RANGE_ERROR)
|
||||
private Integer port;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.njcn.gather.device.pojo.vo;
|
||||
|
||||
import cn.afterturn.easypoi.excel.annotation.Excel;
|
||||
import cn.afterturn.easypoi.excel.annotation.ExcelCollection;
|
||||
import com.njcn.common.pojo.constant.PatternRegex;
|
||||
import com.njcn.gather.monitor.pojo.vo.PqMonitorExcel;
|
||||
import com.njcn.gather.pojo.constant.DetectionValidMessage;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Range;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2025-05-07
|
||||
*/
|
||||
@Data
|
||||
public class ContrastDevExcel implements Serializable {
|
||||
|
||||
private String id;
|
||||
|
||||
@Excel(name = "所属地市*", width = 30, needMerge = true, orderNum = "1")
|
||||
private String cityName;
|
||||
|
||||
@Excel(name = "所属供电公司*", width = 30, needMerge = true, orderNum = "2")
|
||||
private String gdName;
|
||||
|
||||
@Excel(name = "所属电站*", width = 30, needMerge = true, orderNum = "3")
|
||||
private String subName;
|
||||
|
||||
@Excel(name = "设备名称*", width = 20, needMerge = true, orderNum = "4")
|
||||
@NotBlank(message = DetectionValidMessage.NAME_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.DEV_NAME_REGEX, message = DetectionValidMessage.NAME_FORMAT_ERROR)
|
||||
private String name;
|
||||
|
||||
@Excel(name = "设备序列号", width = 20, needMerge = true, orderNum = "5")
|
||||
// @NotBlank(message = DetectionValidMessage.FACTORYNO_NOT_BLANK)
|
||||
private String createId;
|
||||
|
||||
@Excel(name = "设备类型*", width = 20, needMerge = true, orderNum = "6")
|
||||
@NotBlank(message = DetectionValidMessage.DEV_TYPE_NOT_BLANK)
|
||||
private String devType;
|
||||
|
||||
@Excel(name = "设备厂家*", width = 20, needMerge = true, orderNum = "7")
|
||||
@NotBlank(message = DetectionValidMessage.MANUFACTURER_NOT_BLANK)
|
||||
private String manufacturer;
|
||||
|
||||
// @Excel(name = "被检通道", width = 20, needMerge = true, orderNum = "8")
|
||||
// private String inspectChannel;
|
||||
|
||||
@Excel(name = "固件版本", width = 15, needMerge = true, orderNum = "8")
|
||||
private String hardwareVersion;
|
||||
|
||||
@Excel(name = "软件版本", width = 15, needMerge = true, orderNum = "9")
|
||||
private String softwareVersion;
|
||||
|
||||
@Excel(name = "通讯协议*", width = 15, needMerge = true, orderNum = "10")
|
||||
@NotBlank(message = DetectionValidMessage.PROTOCOL_NOT_BLANK)
|
||||
private String protocol;
|
||||
|
||||
@Excel(name = "是否加密*", width = 20, needMerge = true, replace = {"否_0", "是_1"}, orderNum = "11")
|
||||
@NotNull(message = DetectionValidMessage.ENCRYPTION_NOT_NULL)
|
||||
private Integer encryptionFlag;
|
||||
|
||||
@Excel(name = "识别码(当加密时必填)", width = 30, needMerge = true, orderNum = "12")
|
||||
private String series;
|
||||
|
||||
@Excel(name = "秘钥(当加密时必填)", width = 30, needMerge = true, orderNum = "13")
|
||||
private String devKey;
|
||||
|
||||
@Excel(name = "IP地址*", width = 20, needMerge = true, orderNum = "14")
|
||||
@NotBlank(message = DetectionValidMessage.IP_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.IP_REGEX, message = DetectionValidMessage.IP_FORMAT_ERROR)
|
||||
private String ip;
|
||||
|
||||
@Excel(name = "端口号*", width = 15, needMerge = true, orderNum = "15")
|
||||
@NotNull(message = DetectionValidMessage.PORT_NOT_NULL)
|
||||
@Range(min = 1, max = 65535, message = DetectionValidMessage.PORT_RANGE_ERROR)
|
||||
private Integer port;
|
||||
|
||||
@Excel(name = "投运日期(yyyy-MM-dd)*", width = 30, needMerge = true, orderNum = "16", format = "yyyy-MM-dd")
|
||||
@NotNull(message = DetectionValidMessage.CREATE_DATE_NOT_NULL)
|
||||
private LocalDate createDate;
|
||||
|
||||
@Excel(name = "定检日期(yyyy-MM-dd)*", width = 30, needMerge = true, orderNum = "17", format = "yyyy-MM-dd")
|
||||
@NotNull(message = DetectionValidMessage.INSPECT_DATE_NOT_NULL)
|
||||
private LocalDate inspectDate;
|
||||
|
||||
@Excel(name = "谐波系统设备id", width = 30, needMerge = true, orderNum = "18")
|
||||
// @NotBlank(message = DetectionValidMessage.HARM_SYS_ID_NOT_BLANK)
|
||||
private String harmSysId;
|
||||
|
||||
@ExcelCollection(name = "监测点信息", orderNum = "19")
|
||||
private List<PqMonitorExcel> pqMonitorExcelList;
|
||||
}
|
||||
@@ -1,211 +0,0 @@
|
||||
package com.njcn.gather.device.pojo.vo;
|
||||
|
||||
import cn.afterturn.easypoi.excel.annotation.Excel;
|
||||
import cn.afterturn.easypoi.excel.annotation.ExcelCollection;
|
||||
import com.njcn.common.pojo.constant.PatternRegex;
|
||||
import com.njcn.gather.monitor.pojo.vo.PqMonitorExcel;
|
||||
import com.njcn.gather.device.pojo.constant.DevValidMessage;
|
||||
import com.njcn.web.pojo.annotation.DateTimeStrValid;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.hibernate.validator.constraints.Range;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2024/11/7
|
||||
*/
|
||||
@Data
|
||||
public class PqDevExcel implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Excel(name = "名称", width = 20, needMerge = true)
|
||||
@NotBlank(message = DevValidMessage.NAME_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.DEV_NAME_REGEX, message = DevValidMessage.NAME_FORMAT_ERROR)
|
||||
private String name;
|
||||
|
||||
@Excel(name = "设备类型", width = 20, orderNum = "2", needMerge = true)
|
||||
@NotBlank(message = DevValidMessage.DEV_TYPE_NOT_BLANK)
|
||||
private String devType;
|
||||
|
||||
@Excel(name = "设备通道数", width = 20, orderNum = "3", needMerge = true)
|
||||
@NotNull(message = DevValidMessage.DEV_CHNS_NOT_NULL)
|
||||
private Integer devChns;
|
||||
|
||||
@Excel(name = "额定电压(V)", width = 15, orderNum = "4", needMerge = true)
|
||||
@NotNull(message = DevValidMessage.DEV_VOLT_NOT_NULL)
|
||||
private Float devVolt;
|
||||
|
||||
@Excel(name = "额定电流(A)", width = 15, orderNum = "5", needMerge = true)
|
||||
@NotNull(message = DevValidMessage.DEV_CURR_NOT_NULL)
|
||||
private Float devCurr;
|
||||
|
||||
@Excel(name = "设备厂家", width = 20, orderNum = "6", needMerge = true)
|
||||
@NotBlank(message = DevValidMessage.MANUFACTURER_NOT_BLANK)
|
||||
private String manufacturer;
|
||||
|
||||
@Excel(name = "设备序列号", width = 40, orderNum = "8", needMerge = true)
|
||||
@NotBlank(message = DevValidMessage.FACTORYNO_NOT_BLANK)
|
||||
private String createId;
|
||||
|
||||
@Excel(name = "固件版本", width = 15, orderNum = "9", needMerge = true)
|
||||
@NotBlank(message = DevValidMessage.FIRMWARE_NOT_BLANK)
|
||||
private String hardwareVersion;
|
||||
|
||||
@Excel(name = "软件版本", width = 15, orderNum = "10", needMerge = true)
|
||||
@NotBlank(message = DevValidMessage.SOFTWARE_NOT_BLANK)
|
||||
private String softwareVersion;
|
||||
|
||||
@Excel(name = "通讯协议", width = 15, orderNum = "11", needMerge = true)
|
||||
@NotBlank(message = DevValidMessage.PROTOCOL_NOT_BLANK)
|
||||
private String protocol;
|
||||
|
||||
@Excel(name = "IP地址", width = 20, orderNum = "12", needMerge = true)
|
||||
@NotBlank(message = DevValidMessage.IP_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.IP_REGEX, message = DevValidMessage.IP_FORMAT_ERROR)
|
||||
private String ip;
|
||||
|
||||
@Excel(name = "端口号", orderNum = "13", needMerge = true)
|
||||
@NotNull(message = DevValidMessage.PORT_NOT_NULL)
|
||||
@Range(min = 1, max = 65535, message = DevValidMessage.PORT_RANGE_ERROR)
|
||||
private Integer port;
|
||||
|
||||
@Excel(name = "是否为加密版本(否\\是)", width = 20, replace = {"否_0", "是_1"}, orderNum = "14", needMerge = true)
|
||||
@NotNull(message = DevValidMessage.ENCRYPTION_NOT_NULL)
|
||||
private Integer encryptionFlag;
|
||||
|
||||
@Excel(name = "识别码(当为加密版本时必填)", width = 30, orderNum = "15", needMerge = true)
|
||||
private String series;
|
||||
|
||||
@Excel(name = "秘钥(当为加密版本时必填)", width = 30, orderNum = "16", needMerge = true)
|
||||
private String devKey;
|
||||
|
||||
@Excel(name = "所属地市名称", width = 20, orderNum = "19", needMerge = true)
|
||||
private String cityName;
|
||||
|
||||
@Excel(name = "所属供电公司名称", width = 20, orderNum = "20", needMerge = true)
|
||||
private String gdName;
|
||||
|
||||
@Excel(name = "所属电站名称", width = 20, orderNum = "21", needMerge = true)
|
||||
private String subName;
|
||||
|
||||
@Excel(name = "关键信息二维码", width = 20, orderNum = "30", needMerge = true)
|
||||
private String qrCode;
|
||||
|
||||
@Excel(name = "检测次数", width = 15, orderNum = "31", needMerge = true)
|
||||
@NotNull(message = DevValidMessage.RECHECK_NUM_NOT_NULL)
|
||||
private Integer reCheckNum;
|
||||
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class ExportData extends PqDevExcel {
|
||||
|
||||
@Excel(name = "设备模式", width = 20, orderNum = "1", needMerge = true)
|
||||
@NotBlank(message = DevValidMessage.PATTERN_NOT_BLANK)
|
||||
private String pattern;
|
||||
|
||||
@Excel(name = "出厂日期(yyyy-MM-dd)", width = 25, format = "yyyy-MM-dd", orderNum = "7", needMerge = true)
|
||||
@NotNull(message = DevValidMessage.CREATEDATETIME_NOT_NULL)
|
||||
private LocalDate createDate;
|
||||
|
||||
@Excel(name = "是否支持系数校准(否\\是)", width = 15, replace = {"否_0", "是_1"}, orderNum = "22", needMerge = true)
|
||||
private Integer factorFlag;
|
||||
|
||||
@Excel(name = "守时检测结果(不合格\\合格\\/)", replace = {"不合格_0", "合格_1", "/_2"}, width = 15, orderNum = "23", needMerge = true)
|
||||
private Integer timeCheckResult;
|
||||
|
||||
@Excel(name = "系数校准结果(不合格\\合格\\/)", width = 15, replace = {"不合格_0", "合格_1", "/_2"}, orderNum = "24", needMerge = true)
|
||||
private Integer factorCheckResult;
|
||||
|
||||
@Excel(name = "检测状态(未检\\检测中\\检测完成\\归档)", width = 15, replace = {"未检_0", "检测中_1", "检测完成_2", "归档_3"}, orderNum = "25", needMerge = true)
|
||||
private Integer checkState;
|
||||
|
||||
@Excel(name = "检测结果(不符合\\符合\\未检)", width = 15, replace = {"不符合_0", "符合_1", "未检_2"}, orderNum = "26", needMerge = true)
|
||||
private Integer checkResult;
|
||||
|
||||
@Excel(name = "报告状态(未生成\\已生成\\未检)", width = 15, replace = {"未生成_0", "已生成_1", "未检_2"}, orderNum = "27", needMerge = true)
|
||||
private Integer reportState;
|
||||
|
||||
// @Excel(name = "归档状态(未归档\\归档)", width = 15, replace = {"未归档_0", "归档_1"}, orderNum = "28", needMerge = true)
|
||||
// private Integer documentState;
|
||||
|
||||
@Excel(name = "报告路径", width = 20, orderNum = "29", needMerge = true)
|
||||
private String reportPath;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟式和比对式设备导出数据
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class SimulateOrDigitalExportData extends ExportData {
|
||||
@Excel(name = "样品编号", width = 40, orderNum = "17", needMerge = true)
|
||||
private String sampleId;
|
||||
|
||||
@Excel(name = "送样日期(yyyy-MM-dd)", width = 25, format = "yyyy-MM-dd", orderNum = "18", needMerge = true)
|
||||
private LocalDate arrivedDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 比对式设备导出数据
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class ContrastExportData extends ExportData {
|
||||
@ExcelCollection(name = "检测点台账", orderNum = "32")
|
||||
List<PqMonitorExcel.ExportData> monitorList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 被检设备导入数据
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class ImportData extends PqDevExcel {
|
||||
|
||||
@Excel(name = "出厂日期(yyyy-MM-dd)", width = 25, format = "yyyy-MM-dd", orderNum = "7", needMerge = true)
|
||||
@NotNull(message = DevValidMessage.CREATEDATETIME_NOT_NULL)
|
||||
@DateTimeStrValid(message = DevValidMessage.CREATEDATETIME_FORMAT_ERROR)
|
||||
private String createDate;
|
||||
|
||||
|
||||
@Excel(name = "是否支持系数校准(否\\是)", width = 15, replace = {"否_0", "是_1"}, orderNum = "22", needMerge = true)
|
||||
@NotBlank(message = DevValidMessage.FACTOR_FLAG_NOT_BLANK)
|
||||
private String factorFlag;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟式和比对式设备导入数据
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class SimulateOrDigitalImportData extends ImportData {
|
||||
@Excel(name = "样品编号", width = 40, orderNum = "17", needMerge = true)
|
||||
private String sampleId;
|
||||
|
||||
@Excel(name = "送样日期(yyyy-MM-dd)", width = 25, format = "yyyy-MM-dd", orderNum = "18", needMerge = true)
|
||||
@DateTimeStrValid(message = DevValidMessage.ARRIVE_DATE_FORMAT_ERROR)
|
||||
private String arrivedDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对比式设备导入数据
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class ContrastImportData extends ImportData {
|
||||
@ExcelCollection(name = "检测点台账", orderNum = "32")
|
||||
List<PqMonitorExcel.ImportData> monitorList;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,15 +1,9 @@
|
||||
package com.njcn.gather.device.pojo.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
|
||||
import com.njcn.gather.device.pojo.po.PqDev;
|
||||
import com.njcn.gather.monitor.pojo.po.PqMonitor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@@ -27,4 +21,99 @@ public class PqDevVO extends PqDev {
|
||||
private Double devCurr;
|
||||
|
||||
private List<PqMonitor> monitorList;
|
||||
|
||||
/**
|
||||
* 检测状态 0-未检、1检测中、2检测完成、3归档
|
||||
*/
|
||||
private Integer checkState;
|
||||
|
||||
/**
|
||||
* 检测结果 0不符合、1符合、2未检
|
||||
*/
|
||||
private Integer checkResult;
|
||||
|
||||
/**
|
||||
* 报告生成状态 0未生成、1已生成、2未检
|
||||
*/
|
||||
private Integer reportState;
|
||||
|
||||
/**
|
||||
* 检测次数
|
||||
*/
|
||||
private Integer recheckNum;
|
||||
|
||||
/**
|
||||
* 守时检测结果 0:不合格, 1:合格,2:/表示没有做守时检测
|
||||
*/
|
||||
private Integer timeCheckResult;
|
||||
|
||||
/**
|
||||
* 系数校准结果 0:不合格,1:合格,2:/表示没有做系数校准
|
||||
*/
|
||||
private Integer factorCheckResult;
|
||||
|
||||
/**
|
||||
* 实时数据结果 0:不合格,1:合格,2:未检
|
||||
*/
|
||||
private Integer realtimeResult;
|
||||
|
||||
/**
|
||||
* 统计数据结果 0:不合格,1:合格,2:未检
|
||||
*/
|
||||
private Integer statisticsResult;
|
||||
|
||||
/**
|
||||
* 录波数据结果 0:不合格,1:合格,2:未检
|
||||
*/
|
||||
private Integer recordedResult;
|
||||
|
||||
/**
|
||||
* 检测人
|
||||
*/
|
||||
private String checkBy;
|
||||
|
||||
/**
|
||||
* 检测时间
|
||||
*/
|
||||
private LocalDateTime checkTime;
|
||||
|
||||
/**
|
||||
* 预检测耗时
|
||||
*/
|
||||
private Integer preDetectTime;
|
||||
|
||||
/**
|
||||
* 系数校准耗时
|
||||
*/
|
||||
private Integer coefficientTime;
|
||||
|
||||
/**
|
||||
* 正式检测耗时
|
||||
*/
|
||||
private Integer formalCheckTime;
|
||||
|
||||
/**
|
||||
* 温度
|
||||
*/
|
||||
private Float temperature;
|
||||
|
||||
/**
|
||||
* 相对湿度
|
||||
*/
|
||||
private Float humidity;
|
||||
|
||||
/**
|
||||
* 绑定的计划名称
|
||||
*/
|
||||
private String boundPlanName;
|
||||
|
||||
/**
|
||||
* 是否已经分配。0-未分配、1-已分配、2-所有
|
||||
*/
|
||||
private Integer assign;
|
||||
|
||||
/**
|
||||
* 检测点结果
|
||||
*/
|
||||
private List<Integer> monitorResults;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.njcn.gather.device.pojo.vo;
|
||||
|
||||
import cn.afterturn.easypoi.excel.annotation.Excel;
|
||||
import com.njcn.common.pojo.constant.PatternRegex;
|
||||
import com.njcn.gather.pojo.constant.DetectionValidMessage;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Range;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2025-07-01
|
||||
*/
|
||||
@Data
|
||||
public class PqStandardDevExcel {
|
||||
|
||||
@Excel(name = "名称*", width = 20, orderNum = "1")
|
||||
@NotBlank(message = DetectionValidMessage.NAME_NOT_BLANK)
|
||||
private String name;
|
||||
|
||||
@Excel(name = "设备类型*", width = 25, orderNum = "2")
|
||||
@NotBlank(message = DetectionValidMessage.DEV_TYPE_NOT_BLANK)
|
||||
private String devType;
|
||||
|
||||
@Excel(name = "设备厂家*", width = 25, orderNum = "3")
|
||||
@NotBlank(message = DetectionValidMessage.MANUFACTURER_NOT_BLANK)
|
||||
private String manufacturer;
|
||||
|
||||
@Excel(name = "可检通道*", width = 25, orderNum = "4")
|
||||
@NotBlank(message = DetectionValidMessage.INSPECT_CHANNEL_NOT_BLANK)
|
||||
private String inspectChannel;
|
||||
|
||||
@Excel(name = "通信协议*", width = 25, orderNum = "5")
|
||||
@NotBlank(message = DetectionValidMessage.PROTOCOL_NOT_BLANK)
|
||||
private String protocol;
|
||||
|
||||
@Excel(name = "IP地址*", width = 25, orderNum = "6")
|
||||
@NotBlank(message = DetectionValidMessage.IP_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.IP_REGEX, message = DetectionValidMessage.IP_FORMAT_ERROR)
|
||||
private String ip;
|
||||
|
||||
@Excel(name = "端口号*", width = 15, orderNum = "7")
|
||||
@NotNull(message = DetectionValidMessage.PORT_NOT_NULL)
|
||||
@Range(min = 1, max = 65535, message = DetectionValidMessage.PORT_RANGE_ERROR)
|
||||
private Integer port;
|
||||
|
||||
@Excel(name = "是否加密*", width = 20, replace = {"否_0", "是_1"}, orderNum = "8")
|
||||
@NotNull(message = DetectionValidMessage.ENCRYPTION_NOT_NULL)
|
||||
private Integer encryptionFlag;
|
||||
|
||||
@Excel(name = "识别码(当加密时必填)", width = 30, orderNum = "9")
|
||||
private String series;
|
||||
|
||||
@Excel(name = "秘钥(当加密时必填)", width = 30, orderNum = "10")
|
||||
private String devKey;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.njcn.gather.device.pojo.vo;
|
||||
|
||||
import com.njcn.gather.device.pojo.po.PqStandardDev;
|
||||
import com.njcn.gather.device.pojo.po.PqStandardDevGain;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2026-01-19
|
||||
*/
|
||||
@Data
|
||||
public class PqStandardDevVO extends PqStandardDev {
|
||||
|
||||
/**
|
||||
* 通道系数
|
||||
*/
|
||||
List<PqStandardDevGain> gainList;
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import java.util.List;
|
||||
public class PreDetection {
|
||||
|
||||
/**
|
||||
* 装置ip
|
||||
* 装置id
|
||||
*/
|
||||
@JSONField(serialize = false)
|
||||
private String devId;
|
||||
@@ -54,6 +54,18 @@ public class PreDetection {
|
||||
@JSONField(name = "icdType")
|
||||
private String icdType;
|
||||
|
||||
/**
|
||||
* 是否支持相角。0:不支持,1:支持
|
||||
*/
|
||||
@JSONField(serialize = false)
|
||||
private Integer angle;
|
||||
|
||||
/**
|
||||
* 角型接线时是否使用相别的指标来进行检测,0表示否,1表示是
|
||||
*/
|
||||
@JSONField(serialize = false)
|
||||
private Integer usePhaseIndex;
|
||||
|
||||
/**
|
||||
* 装置识别码(3ds加密)
|
||||
*/
|
||||
@@ -72,6 +84,9 @@ public class PreDetection {
|
||||
private Double devVolt;
|
||||
|
||||
private Double devCurr;
|
||||
|
||||
@JSONField(serialize = false)
|
||||
private String waveCmd;
|
||||
/**
|
||||
* 监测点信息
|
||||
*/
|
||||
@@ -95,16 +110,40 @@ public class PreDetection {
|
||||
private Integer line;
|
||||
|
||||
/**
|
||||
* 监测点线路号
|
||||
* pt
|
||||
*/
|
||||
@JSONField(name = "pt")
|
||||
private Integer pt;
|
||||
@JSONField(serialize = false)
|
||||
private String ptStr;
|
||||
|
||||
/**
|
||||
* 监测点线路号
|
||||
* ct
|
||||
*/
|
||||
@JSONField(serialize = false)
|
||||
private String ctStr;
|
||||
|
||||
/**
|
||||
* pt
|
||||
*/
|
||||
@JSONField(name = "pt")
|
||||
private Integer ct;
|
||||
private Double pt;
|
||||
|
||||
/**
|
||||
* ct
|
||||
*/
|
||||
@JSONField(name = "ct")
|
||||
private Double ct;
|
||||
|
||||
/**
|
||||
* 统计间隔
|
||||
*/
|
||||
@JSONField(serialize = false)
|
||||
private Integer statInterval;
|
||||
|
||||
/**
|
||||
* 接线方式
|
||||
*/
|
||||
@JSONField(serialize = false)
|
||||
private String connection;
|
||||
}
|
||||
|
||||
public String getDevKey() {
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.njcn.gather.device.pojo.vo;
|
||||
|
||||
import cn.afterturn.easypoi.excel.annotation.Excel;
|
||||
import com.njcn.common.pojo.constant.PatternRegex;
|
||||
import com.njcn.gather.pojo.constant.DetectionValidMessage;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Range;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2024/11/7
|
||||
* @description 省级平台设备Excel导入导出实体类
|
||||
*/
|
||||
@Data
|
||||
public class ProvinceDevExcel implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Excel(name = "装置编号*", width = 20)
|
||||
@NotBlank(message = DetectionValidMessage.FACTORYNO_NOT_BLANK)
|
||||
private String createId;
|
||||
|
||||
@Excel(name = "设备名称*", width = 20, orderNum = "2")
|
||||
@NotBlank(message = DetectionValidMessage.NAME_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.DEV_NAME_REGEX, message = DetectionValidMessage.NAME_FORMAT_ERROR)
|
||||
private String name;
|
||||
|
||||
@Excel(name = "设备类型*", width = 20, orderNum = "3")
|
||||
@NotBlank(message = DetectionValidMessage.DEV_TYPE_NOT_BLANK)
|
||||
private String devType;
|
||||
|
||||
@Excel(name = "设备厂家*", width = 20, orderNum = "7")
|
||||
@NotBlank(message = DetectionValidMessage.MANUFACTURER_NOT_BLANK)
|
||||
private String manufacturer;
|
||||
|
||||
@Excel(name = "出厂日期(yyyy-MM-dd)*", width = 25, format = "yyyy-MM-dd", orderNum = "8")
|
||||
@NotNull(message = DetectionValidMessage.CREATEDATETIME_NOT_NULL)
|
||||
private LocalDate createDate;
|
||||
|
||||
@Excel(name = "固件版本", width = 15, orderNum = "9")
|
||||
private String hardwareVersion;
|
||||
|
||||
@Excel(name = "软件版本", width = 15, orderNum = "10")
|
||||
private String softwareVersion;
|
||||
|
||||
@Excel(name = "通讯协议*", width = 15, orderNum = "11")
|
||||
@NotBlank(message = DetectionValidMessage.PROTOCOL_NOT_BLANK)
|
||||
private String protocol;
|
||||
|
||||
@Excel(name = "是否加密*", width = 20, replace = {"否_0", "是_1"}, orderNum = "12")
|
||||
@NotNull(message = DetectionValidMessage.ENCRYPTION_NOT_NULL)
|
||||
private Integer encryptionFlag;
|
||||
|
||||
@Excel(name = "识别码(当加密时必填)", width = 30, orderNum = "13")
|
||||
private String series;
|
||||
|
||||
@Excel(name = "秘钥(当加密时必填)", width = 30, orderNum = "14")
|
||||
private String devKey;
|
||||
|
||||
// @Excel(name = "是否支持系数校准*", width = 25, replace = {"否_0", "是_1"}, orderNum = "15")
|
||||
// private Integer factorFlag;
|
||||
|
||||
@Excel(name = "IP地址*", width = 20, orderNum = "15")
|
||||
@NotBlank(message = DetectionValidMessage.IP_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.IP_REGEX, message = DetectionValidMessage.IP_FORMAT_ERROR)
|
||||
private String ip;
|
||||
|
||||
@Excel(name = "端口号*", width = 15, orderNum = "16")
|
||||
@NotNull(message = DetectionValidMessage.PORT_NOT_NULL)
|
||||
@Range(min = 1, max = 65535, message = DetectionValidMessage.PORT_RANGE_ERROR)
|
||||
private Integer port;
|
||||
|
||||
@Excel(name = "样品编号", width = 20, orderNum = "17")
|
||||
private String sampleId;
|
||||
|
||||
@Excel(name = "送样日期(yyyy-MM-dd)", width = 25, format = "yyyy-MM-dd", orderNum = "18")
|
||||
private LocalDate arrivedDate;
|
||||
|
||||
@Excel(name="委托方", width = 40, orderNum = "19")
|
||||
private String delegate;
|
||||
}
|
||||
@@ -2,15 +2,16 @@ package com.njcn.gather.device.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.njcn.common.pojo.poi.PullDown;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.gather.device.pojo.enums.TimeCheckResultEnum;
|
||||
import com.njcn.gather.device.pojo.param.PqDevParam;
|
||||
import com.njcn.gather.device.pojo.po.PqDev;
|
||||
import com.njcn.gather.device.pojo.vo.CNDevExcel;
|
||||
import com.njcn.gather.device.pojo.vo.PqDevExcel;
|
||||
import com.njcn.gather.device.pojo.vo.PqDevVO;
|
||||
import com.njcn.gather.device.pojo.vo.PreDetection;
|
||||
import com.njcn.gather.device.pojo.vo.*;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -61,41 +62,6 @@ public interface IPqDevService extends IService<PqDev> {
|
||||
*/
|
||||
boolean updatePqDevTimeCheckResult(List<String> ids, TimeCheckResultEnum result);
|
||||
|
||||
/**
|
||||
* 获取模拟式||数字式设备导出时所需的数据
|
||||
*
|
||||
* @param queryParam 查询参数
|
||||
* @return
|
||||
*/
|
||||
List<PqDevExcel.SimulateOrDigitalExportData> getSimulateOrDigitExportData(PqDevParam.QueryParam queryParam);
|
||||
|
||||
/**
|
||||
* 获取比对式设备导出时所需的数据
|
||||
*
|
||||
* @param queryParam 查询参数
|
||||
* @return 比对式设备导出时所需的数据
|
||||
*/
|
||||
List<PqDevExcel.ContrastExportData> getContrastExportData(PqDevParam.QueryParam queryParam);
|
||||
|
||||
/**
|
||||
* 下载模板文件
|
||||
*/
|
||||
void downloadTemplate();
|
||||
|
||||
/**
|
||||
* 批量导入被检设备信息
|
||||
*
|
||||
* @param sgEventExcels 批量导入的数据
|
||||
*/
|
||||
void importContrastData(List<PqDevExcel.ContrastImportData> sgEventExcels);
|
||||
|
||||
/**
|
||||
* 批量导入被检设备信息
|
||||
*
|
||||
* @param sgEventExcels 批量导入的数据
|
||||
*/
|
||||
void importSimulateAndDigitalData(List<PqDevExcel.SimulateOrDigitalImportData> sgEventExcels);
|
||||
|
||||
/**
|
||||
* 获取所有未绑定的设备
|
||||
*
|
||||
@@ -110,7 +76,7 @@ public interface IPqDevService extends IService<PqDev> {
|
||||
* @param param 计划id
|
||||
* @return 绑定的设备列表
|
||||
*/
|
||||
List<PqDev> listByPlanId(PqDevParam.QueryParam param);
|
||||
List<PqDevVO> listByPlanId(PqDevParam.QueryParam param);
|
||||
|
||||
/**
|
||||
* 绑定计划
|
||||
@@ -121,14 +87,6 @@ public interface IPqDevService extends IService<PqDev> {
|
||||
*/
|
||||
Integer bind(String planId, List<String> devIds);
|
||||
|
||||
/**
|
||||
* 获取饼图数据
|
||||
*
|
||||
* @param planId 检测计划id
|
||||
* @return 饼图数据
|
||||
*/
|
||||
//List<List<Map<String, Object>>> getPieData(String planId);
|
||||
|
||||
/**
|
||||
* 根据id获取被检设备信息
|
||||
*
|
||||
@@ -137,25 +95,6 @@ public interface IPqDevService extends IService<PqDev> {
|
||||
*/
|
||||
PqDevVO getPqDevById(String id);
|
||||
|
||||
/**
|
||||
* 获取所有非未检测状态的设备
|
||||
*
|
||||
* @return 所有非未检测状态的设备列表
|
||||
*/
|
||||
List<PqDev> listUnchecked();
|
||||
|
||||
/**
|
||||
* 可视化,各种id回显字典值,解码等操作
|
||||
*
|
||||
* @param sourceList
|
||||
*/
|
||||
void visualize(List<PqDev> sourceList);
|
||||
|
||||
/**
|
||||
* 逆向可视化
|
||||
*/
|
||||
void reverseVisualize(List<PqDev> sourceList);
|
||||
|
||||
/**
|
||||
* 获取装置信息和装置下监测点信息
|
||||
*
|
||||
@@ -166,46 +105,188 @@ public interface IPqDevService extends IService<PqDev> {
|
||||
*/
|
||||
List<PreDetection> getDevInfo(@Param("devIds") List<String> devIds);
|
||||
|
||||
/**
|
||||
* 设备归档操作
|
||||
*
|
||||
* @param id 设备id
|
||||
* @return 归档成功返回true,否则返回false
|
||||
*/
|
||||
boolean documented(List<String> id);
|
||||
|
||||
|
||||
/**
|
||||
* 正式监测完成,修改中断状态
|
||||
*
|
||||
* @param ids
|
||||
* @param valueType
|
||||
* @param adType
|
||||
* @param code
|
||||
* @param userId
|
||||
* @param temperature
|
||||
* @param humidity
|
||||
* @param updateCheckNum 是否更新检测次数
|
||||
* @return
|
||||
*/
|
||||
boolean updateResult(List<String> ids, List<String> valueType, String code);
|
||||
boolean updateResult(List<String> ids, List<String> adType, String code, String userId, Float temperature, Float humidity, boolean updateCheckNum);
|
||||
|
||||
/**
|
||||
* 比对式-修改设备状态
|
||||
*
|
||||
* @param devId
|
||||
* @param userId
|
||||
*/
|
||||
void updateResult(String devId, String userId);
|
||||
|
||||
void updatePqDevReportState(String devId, int i);
|
||||
|
||||
int countUnReportDev(String planId);
|
||||
long countUnReportDev(String planId);
|
||||
|
||||
/**
|
||||
* 导出灿能二楼设备数据
|
||||
* 根据计划id列表获取设备列表
|
||||
*
|
||||
* @param planIds
|
||||
* @return
|
||||
*/
|
||||
List<PqDev> listByPlanIds(List<String> planIds);
|
||||
|
||||
/**
|
||||
* 导出设备数据
|
||||
*
|
||||
* @param queryParam
|
||||
*/
|
||||
void exportCNDev(PqDevParam.QueryParam queryParam);
|
||||
void exportDev(PqDevParam.QueryParam queryParam);
|
||||
|
||||
/**
|
||||
* 下载灿能二楼设备模板文件
|
||||
* 下载模板文件
|
||||
*
|
||||
* @param patternId 模式Id
|
||||
*/
|
||||
void downloadCNDevTemplate();
|
||||
void downloadTemplate(String patternId);
|
||||
|
||||
/**
|
||||
* 导入设备数据
|
||||
*
|
||||
* @param file 上传的文件
|
||||
* @param patternId 模式Id
|
||||
* @param planId 计划Id
|
||||
* @param response 响应
|
||||
*/
|
||||
HttpResult importDev(MultipartFile file, String patternId, String planId, HttpServletResponse response, Integer cover);
|
||||
|
||||
/**
|
||||
* 导入灿能二楼设备数据
|
||||
*
|
||||
* @param cnDevExcelList 灿能二楼设备数据列表
|
||||
* @param patternId 模式Id
|
||||
* @param file 上传的文件
|
||||
* @param patternId 模式Id
|
||||
* @param planId 计划Id
|
||||
* @param response 响应
|
||||
*/
|
||||
void importCNDev(List<CNDevExcel> cnDevExcelList, String patternId);
|
||||
boolean importCNDev(MultipartFile file, String patternId, String planId, HttpServletResponse response);
|
||||
|
||||
/**
|
||||
* 导入灿能二楼设备数据
|
||||
*
|
||||
* @param cnDevExcelList
|
||||
* @param patternId
|
||||
* @param planId
|
||||
*/
|
||||
boolean importCNDev(List<CNDevExcel> cnDevExcelList, String patternId, String planId);
|
||||
|
||||
/**
|
||||
* 可视化-灿能二楼设备
|
||||
*
|
||||
* @param pqDevs
|
||||
*/
|
||||
void visualizeCNDev(List<PqDevVO> pqDevs);
|
||||
|
||||
/**
|
||||
* 逆向可视化-灿能二楼设备
|
||||
*
|
||||
* @param pqDevs 设备列表
|
||||
* @param patternId 模式Id
|
||||
*/
|
||||
void reverseVisualizeCNDev(List<PqDev> pqDevs, String patternId);
|
||||
|
||||
/**
|
||||
* 导入省级平台设备数据
|
||||
*
|
||||
* @param file 上传的文件
|
||||
* @param patternId 模式Id
|
||||
* @param planId 计划Id
|
||||
* @param response 响应
|
||||
*/
|
||||
boolean importProvinceDev(MultipartFile file, String patternId, String planId, HttpServletResponse response);
|
||||
|
||||
/**
|
||||
* 导入省级平台设备数据
|
||||
*
|
||||
* @param proviceDevExcelList
|
||||
* @param patternId
|
||||
* @param planId
|
||||
*/
|
||||
boolean importProvinceDev(List<ProvinceDevExcel> proviceDevExcelList, String patternId, String planId);
|
||||
|
||||
/**
|
||||
* 可视化-省级平台设备
|
||||
*
|
||||
* @param pqDevs
|
||||
*/
|
||||
void visualizeProvinceDev(List<PqDevVO> pqDevs);
|
||||
|
||||
/**
|
||||
* 逆向可视化-省级平台设备
|
||||
*
|
||||
* @param pqDevs 设备列表
|
||||
* @param patternId 模式Id
|
||||
*/
|
||||
void reverseVisualizeProvinceDev(List<PqDev> pqDevs, String patternId);
|
||||
|
||||
/**
|
||||
* 获取省级平台设备导出、导出文件模板的下拉列表
|
||||
*
|
||||
* @param startCol 开始列
|
||||
* @return
|
||||
*/
|
||||
List<PullDown> getProvinceDevPullDownList(int startCol);
|
||||
|
||||
/**
|
||||
* 获取灿能二楼设备导出、导出文件模板的下拉列表
|
||||
*
|
||||
* @param startCol 开始列
|
||||
* @return
|
||||
*/
|
||||
List<PullDown> getCNDevPullDownList(int startCol);
|
||||
|
||||
/**
|
||||
* 导入比对式设备数据
|
||||
*
|
||||
* @param file 上传的文件
|
||||
* @param patternId 模式Id
|
||||
* @param planId 计划Id
|
||||
* @param response 响应
|
||||
*/
|
||||
HttpResult importContrastDev(MultipartFile file, String patternId, String planId, HttpServletResponse response, Integer cover);
|
||||
|
||||
/**
|
||||
* 导入比对式设备数据
|
||||
*
|
||||
* @param contrastDevExcelList
|
||||
* @param patternId
|
||||
*/
|
||||
HttpResult importContrastDev(List<ContrastDevExcel> contrastDevExcelList, String patternId, String planId, Integer cover);
|
||||
|
||||
/**
|
||||
* 获取比对式设备导出、导出文件模板的下拉列表
|
||||
*
|
||||
* @param startCol
|
||||
* @return
|
||||
*/
|
||||
List<PullDown> getContrastDevPullDownList(int startCol);
|
||||
|
||||
/**
|
||||
* 根据历史记录来获取下拉框内容
|
||||
*
|
||||
* @param pattern
|
||||
* @return
|
||||
*/
|
||||
Map<String, List<String>> listSelectOptions(String pattern);
|
||||
|
||||
/**
|
||||
* 获取比对式设备导出数据
|
||||
*
|
||||
* @param pqDevVOList 设备id列表
|
||||
* @return
|
||||
*/
|
||||
List<ContrastDevExcel> getExportContrastDevData(List<PqDevVO> pqDevVOList);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.njcn.gather.device.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.njcn.gather.device.pojo.po.PqDevSub;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @date 2025-07-04
|
||||
*/
|
||||
public interface IPqDevSubService extends IService<PqDevSub> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.njcn.gather.device.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.njcn.gather.device.pojo.po.PqStandardDevGainRecord;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @date 2026-01-15
|
||||
*/
|
||||
public interface IPqStandardDevGainRecordService extends IService<PqStandardDevGainRecord> {
|
||||
|
||||
/**
|
||||
* 获取最大下发系数的次数
|
||||
*
|
||||
* @param stdDevMonitorId
|
||||
* @param devMonitorId
|
||||
* @return
|
||||
*/
|
||||
int getMaxNum(String stdDevMonitorId, String devMonitorId);
|
||||
|
||||
/**
|
||||
* 批量新增或更新记录
|
||||
* @param recordList
|
||||
* @return
|
||||
*/
|
||||
boolean addOrUpdate(List<PqStandardDevGainRecord> recordList);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.njcn.gather.device.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.njcn.gather.device.pojo.po.PqStandardDevGain;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @date 2026-01-12
|
||||
*/
|
||||
public interface IPqStandardDevGainService extends IService<PqStandardDevGain> {
|
||||
|
||||
boolean add(List<PqStandardDevGain> pqStandardDevGainList);
|
||||
|
||||
PqStandardDevGain get(String stdDevMonitorId);
|
||||
|
||||
List<PqStandardDevGain> list(String stdDevId);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package com.njcn.gather.device.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.njcn.gather.device.pojo.param.PqStandardDevParam;
|
||||
import com.njcn.gather.device.pojo.po.PqStandardDev;
|
||||
import com.njcn.gather.device.pojo.vo.PqStandardDevVO;
|
||||
import com.njcn.gather.device.pojo.vo.PreDetection;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @date 2025-07-02
|
||||
*/
|
||||
public interface IPqStandardDevService extends IService<PqStandardDev> {
|
||||
|
||||
/**
|
||||
* 分页查询标准设备列表
|
||||
*
|
||||
* @param queryParam 分页查询参数
|
||||
* @return
|
||||
*/
|
||||
Page<PqStandardDev> listPqStandardDevs(PqStandardDevParam.QueryParam queryParam);
|
||||
|
||||
/**
|
||||
* 根据id查询标准设备
|
||||
*
|
||||
* @param id 设备id
|
||||
* @return 设备对象
|
||||
*/
|
||||
PqStandardDevVO getPqStandardDevById(String id);
|
||||
|
||||
/**
|
||||
* 新增标准设备
|
||||
*
|
||||
* @param param 新增参数
|
||||
* @return 新增成功返回true,失败返回false
|
||||
*/
|
||||
boolean addPqStandardDev(PqStandardDevParam param);
|
||||
|
||||
/**
|
||||
* 修改标准设备
|
||||
*
|
||||
* @param param 修改参数
|
||||
* @return 修改成功返回true,失败返回false
|
||||
*/
|
||||
boolean updatePqStandardDev(PqStandardDevParam.UpdateParam param);
|
||||
|
||||
/**
|
||||
* 批量删除标准设备
|
||||
*
|
||||
* @param ids
|
||||
* @return
|
||||
*/
|
||||
boolean deletePqStandardDevs(List<String> ids);
|
||||
|
||||
/**
|
||||
* 导出标准设备
|
||||
*
|
||||
* @param queryParam
|
||||
*/
|
||||
void export(PqStandardDevParam.QueryParam queryParam);
|
||||
|
||||
/**
|
||||
* 下装标准设备导入模板
|
||||
*/
|
||||
void downloadTemplate();
|
||||
|
||||
/**
|
||||
* 导入标准设备数据
|
||||
*
|
||||
* @param file
|
||||
* @return
|
||||
*/
|
||||
void importData(MultipartFile file, HttpServletResponse response);
|
||||
|
||||
/**
|
||||
* 查询出指定计划以关联的标准设备列表
|
||||
*
|
||||
* @param planId
|
||||
* @return
|
||||
*/
|
||||
// List<PqStandardDev> listByPlanId(String planId);
|
||||
|
||||
/**
|
||||
* 查询出标准设备所需的检测信息
|
||||
*
|
||||
* @param ids
|
||||
* @return
|
||||
*/
|
||||
List<PreDetection> listStandardDevPreDetection(List<String> ids);
|
||||
|
||||
|
||||
/**
|
||||
* 查询可绑定的标准设备列表
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
List<PqStandardDev> canBindingList();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user