update signal logic :: simulator init & correct parsing
This commit is contained in:
parent
1116ccfb36
commit
d3bcb58a35
10
DOCS.md
10
DOCS.md
@ -5,3 +5,13 @@ SOCKET MESSAGING:
|
||||
localhost:9091/storeEvent?client_id=1&message={"me": "update"}
|
||||
// CONNECTION:
|
||||
ws://localhost:9091/ws/1
|
||||
|
||||
DATABASE LOGS TYPE:
|
||||
1 - INFO
|
||||
2 - WARNING
|
||||
3 - ERROR
|
||||
4 - ADMIN_ACTION
|
||||
5 - USER_ACTION
|
||||
...
|
||||
10 - RESERV
|
||||
|
||||
|
16
Makefile
16
Makefile
@ -3,29 +3,29 @@
|
||||
|
||||
|
||||
build:
|
||||
docker compose build
|
||||
docker-compose build
|
||||
|
||||
dev: build
|
||||
docker compose up -d --force-recreate
|
||||
docker-compose up -d --force-recreate
|
||||
|
||||
devf: dev
|
||||
docker compose logs -f
|
||||
docker-compose logs -f
|
||||
|
||||
up:
|
||||
docker compose up -d --force-recreate
|
||||
docker-compose up -d --force-recreate
|
||||
|
||||
upf: up
|
||||
docker compose logs -f
|
||||
docker-compose logs -f
|
||||
|
||||
logs:
|
||||
docker compose logs -f
|
||||
docker-compose logs -f
|
||||
|
||||
|
||||
stop:
|
||||
echo "!!!!!!!!!!!!!!!!!EXTERMINATUS!!!!!!!!!!!!!!!!!"
|
||||
docker compose stop
|
||||
docker-compose stop
|
||||
start:
|
||||
docker compose start
|
||||
docker-compose start
|
||||
|
||||
drop:
|
||||
docker-compose down --volumes
|
4
ToDO.md.save
Normal file
4
ToDO.md.save
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
|
||||
!1 => Написать хук для воспроизведения симуляции
|
||||
!2 =>
|
316
src/frontend/package-lock.json
generated
316
src/frontend/package-lock.json
generated
@ -8,6 +8,7 @@
|
||||
"name": "dsn-application",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@mui/material": "^6.1.3",
|
||||
"@testing-library/jest-dom": "^5.17.0",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
@ -2445,6 +2446,60 @@
|
||||
"postcss-selector-parser": "^6.0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/cache": {
|
||||
"version": "11.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.1.tgz",
|
||||
"integrity": "sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==",
|
||||
"dependencies": {
|
||||
"@emotion/memoize": "^0.9.0",
|
||||
"@emotion/sheet": "^1.4.0",
|
||||
"@emotion/utils": "^1.4.0",
|
||||
"@emotion/weak-memoize": "^0.4.0",
|
||||
"stylis": "4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/hash": {
|
||||
"version": "0.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
|
||||
"integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g=="
|
||||
},
|
||||
"node_modules/@emotion/memoize": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
|
||||
"integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ=="
|
||||
},
|
||||
"node_modules/@emotion/serialize": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.2.tgz",
|
||||
"integrity": "sha512-grVnMvVPK9yUVE6rkKfAJlYZgo0cu3l9iMC77V7DW6E1DUIrU68pSEXRmFZFOFB1QFo57TncmOcvcbMDWsL4yA==",
|
||||
"dependencies": {
|
||||
"@emotion/hash": "^0.9.2",
|
||||
"@emotion/memoize": "^0.9.0",
|
||||
"@emotion/unitless": "^0.10.0",
|
||||
"@emotion/utils": "^1.4.1",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/sheet": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
|
||||
"integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg=="
|
||||
},
|
||||
"node_modules/@emotion/unitless": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
|
||||
"integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg=="
|
||||
},
|
||||
"node_modules/@emotion/utils": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.1.tgz",
|
||||
"integrity": "sha512-BymCXzCG3r72VKJxaYVwOXATqXIZ85cuvg0YOUDxMGNrKc1DJRZk8MgV5wyXRyEayIMd4FuXJIUgTBXvDNW5cA=="
|
||||
},
|
||||
"node_modules/@emotion/weak-memoize": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
|
||||
"integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg=="
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-utils": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
|
||||
@ -3425,6 +3480,213 @@
|
||||
"integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@mui/core-downloads-tracker": {
|
||||
"version": "6.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.3.tgz",
|
||||
"integrity": "sha512-ajMUgdfhTb++rwqj134Cq9f4SRN8oXUqMRnY72YBnXiXai3olJLLqETheRlq3MM8wCKrbq7g6j7iWL1VvP44VQ==",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/material": {
|
||||
"version": "6.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-6.1.3.tgz",
|
||||
"integrity": "sha512-loV5MBoMKLrK80JeWINmQ1A4eWoLv51O2dBPLJ260IAhupkB3Wol8lEQTEvvR2vO3o6xRHuXe1WaQEP6N3riqg==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.25.6",
|
||||
"@mui/core-downloads-tracker": "^6.1.3",
|
||||
"@mui/system": "^6.1.3",
|
||||
"@mui/types": "^7.2.18",
|
||||
"@mui/utils": "^6.1.3",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@types/react-transition-group": "^4.4.11",
|
||||
"clsx": "^2.1.1",
|
||||
"csstype": "^3.1.3",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-is": "^18.3.1",
|
||||
"react-transition-group": "^4.4.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.5.0",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"@mui/material-pigment-css": "^6.1.3",
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@emotion/styled": {
|
||||
"optional": true
|
||||
},
|
||||
"@mui/material-pigment-css": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/material/node_modules/react-is": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="
|
||||
},
|
||||
"node_modules/@mui/private-theming": {
|
||||
"version": "6.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.1.3.tgz",
|
||||
"integrity": "sha512-XK5OYCM0x7gxWb/WBEySstBmn+dE3YKX7U7jeBRLm6vHU5fGUd7GiJWRirpivHjOK9mRH6E1MPIVd+ze5vguKQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.25.6",
|
||||
"@mui/utils": "^6.1.3",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/styled-engine": {
|
||||
"version": "6.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.1.3.tgz",
|
||||
"integrity": "sha512-i4yh9m+eMZE3cNERpDhVr6Wn73Yz6C7MH0eE2zZvw8d7EFkIJlCQNZd1xxGZqarD2DDq2qWHcjIOucWGhxACtA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.25.6",
|
||||
"@emotion/cache": "^11.13.1",
|
||||
"@emotion/serialize": "^1.3.2",
|
||||
"@emotion/sheet": "^1.4.0",
|
||||
"csstype": "^3.1.3",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.4.1",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@emotion/styled": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/system": {
|
||||
"version": "6.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-6.1.3.tgz",
|
||||
"integrity": "sha512-ILaD9UsLTBLjMcep3OumJMXh1PYr7aqnkHm/L47bH46+YmSL1zWAX6tWG8swEQROzW2GvYluEMp5FreoxOOC6w==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.25.6",
|
||||
"@mui/private-theming": "^6.1.3",
|
||||
"@mui/styled-engine": "^6.1.3",
|
||||
"@mui/types": "^7.2.18",
|
||||
"@mui/utils": "^6.1.3",
|
||||
"clsx": "^2.1.1",
|
||||
"csstype": "^3.1.3",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.5.0",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@emotion/styled": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/types": {
|
||||
"version": "7.2.18",
|
||||
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.18.tgz",
|
||||
"integrity": "sha512-uvK9dWeyCJl/3ocVnTOS6nlji/Knj8/tVqVX03UVTpdmTJYu/s4jtDd9Kvv0nRGE0CUSNW1UYAci7PYypjealg==",
|
||||
"peerDependencies": {
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/utils": {
|
||||
"version": "6.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.1.3.tgz",
|
||||
"integrity": "sha512-4JBpLkjprlKjN10DGb1aiy/ii9TKbQ601uSHtAmYFAS879QZgAD7vRnv/YBE4iBbc7NXzFgbQMCOFrupXWekIA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.25.6",
|
||||
"@mui/types": "^7.2.18",
|
||||
"@types/prop-types": "^15.7.13",
|
||||
"clsx": "^2.1.1",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-is": "^18.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/utils/node_modules/react-is": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="
|
||||
},
|
||||
"node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
|
||||
"version": "5.1.1-v1",
|
||||
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
|
||||
@ -3549,6 +3811,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@popperjs/core": {
|
||||
"version": "2.11.8",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
}
|
||||
},
|
||||
"node_modules/@remix-run/router": {
|
||||
"version": "1.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.2.tgz",
|
||||
@ -4769,6 +5040,14 @@
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-transition-group": {
|
||||
"version": "4.4.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.11.tgz",
|
||||
"integrity": "sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==",
|
||||
"dependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/resolve": {
|
||||
"version": "1.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
|
||||
@ -6611,6 +6890,14 @@
|
||||
"wrap-ansi": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/clsx": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/co": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||
@ -7638,6 +7925,15 @@
|
||||
"utila": "~0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/dom-helpers": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
|
||||
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.8.7",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/dom-serializer": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
|
||||
@ -16404,6 +16700,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-transition-group": {
|
||||
"version": "4.4.5",
|
||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"dom-helpers": "^5.0.1",
|
||||
"loose-envify": "^1.4.0",
|
||||
"prop-types": "^15.6.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.6.0",
|
||||
"react-dom": ">=16.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/read-cache": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||
@ -17951,6 +18262,11 @@
|
||||
"postcss": "^8.2.15"
|
||||
}
|
||||
},
|
||||
"node_modules/stylis": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
|
||||
"integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
|
||||
},
|
||||
"node_modules/sucrase": {
|
||||
"version": "3.35.0",
|
||||
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
|
||||
|
@ -3,6 +3,7 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@mui/material": "^6.1.3",
|
||||
"@testing-library/jest-dom": "^5.17.0",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
|
@ -27,7 +27,7 @@ const useWebsocketConnection = (userToken, roomHash) => {
|
||||
return prevState;
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error parsing WebSocket message:', error);
|
||||
console.error('Error parsing WebSocket message:', error, event.data);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -14,11 +14,12 @@ import { ModalJson } from './Modal/MJson';
|
||||
import { ModalMap } from './Modal/MMap';
|
||||
import { CtxMenu } from './ContextMenu/CtxMenu';
|
||||
import { ProgramInterface } from './MainScreen/ProgramInterface';
|
||||
|
||||
import { SimulatorInterface } from './SimScreen/SimScreen';
|
||||
|
||||
const Dashboard = () => {
|
||||
const mountRef = useRef(null); // Указатель монтирования
|
||||
const sceneRef = useRef(null); // Указатель на сцену
|
||||
const cameraRef = useRef(null); // Указатель на камеру
|
||||
const {websocketStruct, sendMessage} = useWebsocketConnection(getCustomCookie("userToken"), 1);
|
||||
const [heightData, setHeightData] = useState([new HeightPoint()]); // Высоты
|
||||
const [formData, setFormData] = useState({ x: '', y: '', height: '' }); // Форма для ввода данных карты
|
||||
@ -29,7 +30,6 @@ const Dashboard = () => {
|
||||
const [selectedBaseStation, setSelectedBaseStation] = useState(null); // Состояние для выбранной базовой станции
|
||||
const [mapSettings, setMapSettings] = useState({maxHeight: 20, coordX: 0, coordY: 0, }); // Настройки карты
|
||||
const [contextMenu, setContextMenu] = useState({ visible: false, x: 0, y: 0 }); // контекстное меню сцены
|
||||
|
||||
const [objectType, setObjectType] = useState(1); // 1 = Drone, 2 = Base Station
|
||||
const [formObjectData, setFormObjectData] = useState({
|
||||
name: "",
|
||||
@ -52,6 +52,204 @@ const Dashboard = () => {
|
||||
meshName: "", // Название сети
|
||||
});
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [isSimulationRunning, setIsSimulationRunning] = useState(false);
|
||||
|
||||
// Рекурсивная функция для поиска объекта среди детей
|
||||
const containsObjectRecursively = (parent, target) => {
|
||||
if (parent === target) {
|
||||
return true;
|
||||
}
|
||||
for (let child of parent.children) {
|
||||
if (containsObjectRecursively(child, target)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Обработка websocket
|
||||
useEffect(() => {
|
||||
if (websocketStruct && websocketStruct.modulation) {
|
||||
const modulation = websocketStruct.modulation;
|
||||
// Парсинг карты
|
||||
const map = modulation.map;
|
||||
setMapSettings({
|
||||
name: map.name,
|
||||
minBound: map.map.MinBound,
|
||||
maxBound: map.map.MaxBound,
|
||||
heightData: map.map.HeightData,
|
||||
maxHeight: 20,
|
||||
});
|
||||
setHeightData(map.map.HeightData.flatMap((row, x) => row.map((height, y) => new HeightPoint(x, y, height))));
|
||||
|
||||
// Парсинг объектов
|
||||
const parsedDrones = [];
|
||||
const parsedBaseStations = [];
|
||||
modulation.objects.forEach((obj) => {
|
||||
if (obj.type === 1) {
|
||||
const drone = new Drone(obj.name);
|
||||
drone.setPosition(...obj.coords);
|
||||
parsedDrones.push(drone);
|
||||
} else if (obj.type === 2) {
|
||||
const base = new BaseStation(obj.name);
|
||||
base.setPosition(...obj.coords);
|
||||
parsedBaseStations.push(base);
|
||||
}
|
||||
});
|
||||
setDrones(parsedDrones);
|
||||
setBaseStation(parsedBaseStations);
|
||||
}
|
||||
}, [websocketStruct]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!mountRef.current) return;
|
||||
let width = mountRef.current.clientWidth;
|
||||
let height = mountRef.current.clientHeight;
|
||||
// Создаем объект сцены
|
||||
const scene = new THREE.Scene();
|
||||
scene.background = new THREE.Color(0xffffff);
|
||||
// Сохраняем ссылку на сцену
|
||||
sceneRef.current = scene;
|
||||
// Создаем камеру
|
||||
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 2000);
|
||||
camera.position.set(0, 80, 120);
|
||||
cameraRef.current = camera;
|
||||
|
||||
const renderer = new THREE.WebGLRenderer();
|
||||
renderer.setSize(width, height);
|
||||
mountRef.current.appendChild(renderer.domElement);
|
||||
|
||||
// Плоскость для высотной карты :: TODO :: W/H/Ws/Hs change
|
||||
const geometry = new THREE.PlaneGeometry(200, 200, 20, 20);
|
||||
const vertices = geometry.attributes.position.array;
|
||||
const colors = [];
|
||||
const color = new THREE.Color();
|
||||
|
||||
const minHeight = Math.min(...heightData.map((point) => point.height));
|
||||
const maxHeight = Math.max(mapSettings.maxHeight, ...heightData.map((point) => point.height));
|
||||
|
||||
for (let i = 0; i < vertices.length; i += 3) {
|
||||
const x = Math.floor(i / 3) % 5;
|
||||
const y = Math.floor(i / (3 * 5));
|
||||
|
||||
const heightPoint = heightData.find((point) => point.x === x && point.y === y);
|
||||
const heightValue = heightPoint ? heightPoint.height : 0;
|
||||
|
||||
vertices[i + 2] = heightValue;
|
||||
|
||||
const normalizedHeight = (heightValue - minHeight) / (maxHeight - minHeight);
|
||||
color.setHSL(0.7 * (1 - normalizedHeight), 1, 0.5);
|
||||
colors.push(color.r, color.g, color.b);
|
||||
}
|
||||
|
||||
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
|
||||
|
||||
const material = new THREE.MeshBasicMaterial({
|
||||
vertexColors: true,
|
||||
side: THREE.DoubleSide,
|
||||
wireframe: false,
|
||||
});
|
||||
|
||||
const mesh = new THREE.Mesh(geometry, material);
|
||||
mesh.rotation.x = -Math.PI / 2;
|
||||
scene.add(mesh);
|
||||
|
||||
const axesHelper = new THREE.AxesHelper(100);
|
||||
scene.add(axesHelper);
|
||||
|
||||
const controls = new OrbitControls(camera, renderer.domElement);
|
||||
controls.enableDamping = true;
|
||||
|
||||
const light = new THREE.DirectionalLight(0xffffff, 1);
|
||||
light.position.set(0, 50, 50).normalize();
|
||||
scene.add(light);
|
||||
|
||||
const handleMouseMove = (event) => {
|
||||
let x = event.x;
|
||||
let y = event.y;
|
||||
setMouseState({ x: x, y: y, z: 0 });
|
||||
}
|
||||
const animate = () => {
|
||||
requestAnimationFrame(animate);
|
||||
controls.update();
|
||||
renderer.render(scene, camera);
|
||||
};
|
||||
|
||||
renderer.domElement.addEventListener('click', handleClick);
|
||||
renderer.domElement.addEventListener('mousemove', handleMouseMove);
|
||||
|
||||
animate();
|
||||
drones.forEach(droneData => {
|
||||
scene.add(droneData.getObject());
|
||||
});
|
||||
baseStation.forEach(b => {
|
||||
scene.add(b.getObject());
|
||||
});
|
||||
|
||||
// Обработка нажатия ПКМ
|
||||
renderer.domElement.addEventListener('contextmenu', (event) => {
|
||||
event.preventDefault();
|
||||
setContextMenu({
|
||||
visible: true,
|
||||
x: event.clientX,
|
||||
y: event.clientY,
|
||||
});
|
||||
});
|
||||
return () => {
|
||||
if (mountRef.current) {
|
||||
mountRef.current.removeChild(renderer.domElement);
|
||||
}
|
||||
};
|
||||
}, [heightData, mapSettings]);
|
||||
|
||||
// Обработка кликов на сцене
|
||||
const handleClick = (event) => {
|
||||
// Рассчитываем координаты мыши в нормализованной системе координат и округляем до целого числа
|
||||
const mouse = new THREE.Vector2();
|
||||
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
|
||||
mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
|
||||
console.log('Mouse coordinates:', mouse);
|
||||
|
||||
// Создаем объект Raycaster для проверки пересечений
|
||||
const raycaster = new THREE.Raycaster();
|
||||
const rayHelper = new THREE.ArrowHelper(raycaster.ray.direction, raycaster.ray.origin, 100, 0xff4444);
|
||||
sceneRef.current.add(rayHelper);
|
||||
raycaster.setFromCamera(mouse, cameraRef.current);
|
||||
|
||||
// Проверяем пересечения с объектами дронов
|
||||
const intersects = raycaster.intersectObjects(drones.map(drone => drone.getObject()), true);
|
||||
console.log('Intersections:', intersects);
|
||||
console.log('Drones:', drones.map(drone => [drone.getObject(), drone.getObject().position, drone.name]));
|
||||
|
||||
if (intersects.length > 0) {
|
||||
const selected = intersects[0].object;
|
||||
console.log('Clicked on:', selected); // Лог для проверки объекта
|
||||
|
||||
// Поиск дрона рекурсивно
|
||||
const drone = drones.find(drone => containsObjectRecursively(drone.getObject(), selected));
|
||||
|
||||
if (drone) {
|
||||
// Сбрасываем цвет предыдущего выбранного дрона
|
||||
if (selectedDrone) {
|
||||
drone.getObject().visible = true;
|
||||
}
|
||||
// Устанавливаем цвет выбранного дрона
|
||||
drone.getObject().visible = false;
|
||||
console.log(drone);
|
||||
setSelectedDrone(drone);
|
||||
}
|
||||
} else {
|
||||
// Сбрасываем выбор, если ни один дрон не был выбран
|
||||
if (selectedDrone) {
|
||||
selectedDrone.getObject().visible = true;
|
||||
setSelectedDrone(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Остальной код компонента остается неизменным
|
||||
|
||||
|
||||
const handleSearch = () => {
|
||||
const foundDrone = drones.find(drone => drone.name.toLowerCase() === searchTerm.toLowerCase());
|
||||
const foundBaseStation = baseStation.find(base => base.name.toLowerCase() === searchTerm.toLowerCase());
|
||||
@ -122,204 +320,6 @@ const Dashboard = () => {
|
||||
isModalSearchOpen: false,
|
||||
});
|
||||
};
|
||||
// Обработка websocket
|
||||
useEffect(() => {
|
||||
if (websocketStruct && websocketStruct.modulation) {
|
||||
const modulation = websocketStruct.modulation;
|
||||
// Парсинг карты
|
||||
const map = modulation.map;
|
||||
setMapSettings({
|
||||
name: map.name,
|
||||
minBound: map.map.MinBound,
|
||||
maxBound: map.map.MaxBound,
|
||||
heightData: map.map.HeightData,
|
||||
maxHeight: 20,
|
||||
});
|
||||
setHeightData(map.map.HeightData.flatMap((row, x) => row.map((height, y) => new HeightPoint(x, y, height))));
|
||||
|
||||
// Парсинг объектов
|
||||
const parsedDrones = [];
|
||||
const parsedBaseStations = [];
|
||||
modulation.objects.forEach((obj) => {
|
||||
if (obj.type === 1) {
|
||||
const drone = new Drone(obj.name);
|
||||
drone.setPosition(...obj.coords);
|
||||
parsedDrones.push(drone);
|
||||
} else if (obj.type === 2) {
|
||||
const base = new BaseStation(obj.name);
|
||||
base.setPosition(...obj.coords);
|
||||
parsedBaseStations.push(base);
|
||||
}
|
||||
});
|
||||
setDrones(parsedDrones);
|
||||
setBaseStation(parsedBaseStations);
|
||||
}
|
||||
}, [websocketStruct]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
if (!mountRef.current) return;
|
||||
let width = mountRef.current.clientWidth;
|
||||
let height = mountRef.current.clientHeight;
|
||||
// Создаем объект сцены
|
||||
const scene = new THREE.Scene();
|
||||
scene.background = new THREE.Color(0xffffff);
|
||||
// Сохраняем ссылку на сцену
|
||||
sceneRef.current = scene;
|
||||
// Создаем камеру
|
||||
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 2000);
|
||||
camera.position.set(0, 80, 120);
|
||||
|
||||
const renderer = new THREE.WebGLRenderer();
|
||||
renderer.setSize(width, height);
|
||||
mountRef.current.appendChild(renderer.domElement);
|
||||
|
||||
// Плоскость для высотной карты :: TODO :: W/H/Ws/Hs change
|
||||
const geometry = new THREE.PlaneGeometry(200, 200, 20, 20);
|
||||
const vertices = geometry.attributes.position.array;
|
||||
const colors = [];
|
||||
const color = new THREE.Color();
|
||||
|
||||
const minHeight = Math.min(...heightData.map((point) => point.height));
|
||||
const maxHeight = Math.max(mapSettings.maxHeight, ...heightData.map((point) => point.height));
|
||||
|
||||
for (let i = 0; i < vertices.length; i += 3) {
|
||||
const x = Math.floor(i / 3) % 5;
|
||||
const y = Math.floor(i / (3 * 5));
|
||||
|
||||
const heightPoint = heightData.find((point) => point.x === x && point.y === y);
|
||||
const heightValue = heightPoint ? heightPoint.height : 0;
|
||||
|
||||
vertices[i + 2] = heightValue;
|
||||
|
||||
const normalizedHeight = (heightValue - minHeight) / (maxHeight - minHeight);
|
||||
color.setHSL(0.7 * (1 - normalizedHeight), 1, 0.5);
|
||||
colors.push(color.r, color.g, color.b);
|
||||
}
|
||||
|
||||
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
|
||||
|
||||
const material = new THREE.MeshBasicMaterial({
|
||||
vertexColors: true,
|
||||
side: THREE.DoubleSide,
|
||||
wireframe: false,
|
||||
});
|
||||
|
||||
const mesh = new THREE.Mesh(geometry, material);
|
||||
mesh.rotation.x = -Math.PI / 2;
|
||||
scene.add(mesh);
|
||||
|
||||
const axesHelper = new THREE.AxesHelper(100);
|
||||
scene.add(axesHelper);
|
||||
|
||||
const controls = new OrbitControls(camera, renderer.domElement);
|
||||
controls.enableDamping = true;
|
||||
|
||||
const light = new THREE.DirectionalLight(0xffffff, 1);
|
||||
light.position.set(0, 50, 50).normalize();
|
||||
scene.add(light);
|
||||
// Обработка кликов на сцене
|
||||
const handleClick = (event) => {
|
||||
// Рассчитываем координаты мыши в нормализованной системе координат и округляем до целого числа
|
||||
const mouse = new THREE.Vector2();
|
||||
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
|
||||
mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
|
||||
console.log('Mouse coordinates:', mouse);
|
||||
|
||||
// Создаем объект Raycaster для проверки пересечений
|
||||
const raycaster = new THREE.Raycaster();
|
||||
const rayHelper = new THREE.ArrowHelper(raycaster.ray.direction, raycaster.ray.origin, 100, 0xff4444);
|
||||
scene.add(rayHelper);
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
|
||||
// Проверяем пересечения с объектами дронов
|
||||
const intersects = raycaster.intersectObjects(drones.map(drone => drone.getObject()), true);
|
||||
console.log('Intersections:', intersects);
|
||||
console.log('Drones:', drones.map(drone => [drone.getObject(), drone.getObject().position, drone.name]));
|
||||
|
||||
if (intersects.length > 0) {
|
||||
const selected = intersects[0].object;
|
||||
console.log('Clicked on:', selected); // Лог для проверки объекта
|
||||
|
||||
// Поиск дрона рекурсивно
|
||||
const drone = drones.find(drone => containsObjectRecursively(drone.getObject(), selected));
|
||||
|
||||
if (drone) {
|
||||
// Сбрасываем цвет предыдущего выбранного дрона
|
||||
if (selectedDrone) {
|
||||
drone.getObject().visible = true;
|
||||
// selectedDrone.getObject().material.color.set(0xff0000);
|
||||
}
|
||||
// Устанавливаем цвет выбранного дрона
|
||||
// drone.getObject().material.color.set(0xff1111);
|
||||
drone.getObject().visible = false;
|
||||
console.log(drone);
|
||||
setSelectedDrone(drone);
|
||||
}
|
||||
} else {
|
||||
// Сбрасываем выбор, если ни один дрон не был выбран
|
||||
if (selectedDrone) {
|
||||
// selectedDrone.getObject().material.color.set(0xff0000);
|
||||
selectedDrone .getObject().visible = true;
|
||||
|
||||
setSelectedDrone(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Рекурсивная функция для поиска объекта среди детей
|
||||
const containsObjectRecursively = (parent, target) => {
|
||||
if (parent === target) {
|
||||
return true;
|
||||
}
|
||||
for (let child of parent.children) {
|
||||
if (containsObjectRecursively(child, target)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
|
||||
const handleMouseMove = (event) => {
|
||||
let x = event.x;
|
||||
let y = event.y;
|
||||
setMouseState({ x: x, y: y, z: 0 });
|
||||
}
|
||||
const animate = () => {
|
||||
requestAnimationFrame(animate);
|
||||
controls.update();
|
||||
renderer.render(scene, camera);
|
||||
};
|
||||
|
||||
renderer.domElement.addEventListener('click', handleClick);
|
||||
renderer.domElement.addEventListener('mousemove', handleMouseMove);
|
||||
|
||||
|
||||
animate();
|
||||
drones.forEach(droneData => {
|
||||
scene.add(droneData.getObject());
|
||||
});
|
||||
baseStation.forEach(b => {
|
||||
scene.add(b.getObject());
|
||||
});
|
||||
|
||||
// Обработка нажатия ПКМ
|
||||
renderer.domElement.addEventListener('contextmenu', (event) => {
|
||||
event.preventDefault();
|
||||
setContextMenu({
|
||||
visible: true,
|
||||
x: event.clientX,
|
||||
y: event.clientY,
|
||||
});
|
||||
});
|
||||
return () => {
|
||||
if (mountRef.current) {
|
||||
mountRef.current.removeChild(renderer.domElement);
|
||||
}
|
||||
};
|
||||
}, [heightData, mapSettings]);
|
||||
|
||||
// Добавление дрона в сцену
|
||||
const handleAddDrone = () => {
|
||||
@ -418,12 +418,8 @@ const containsObjectRecursively = (parent, target) => {
|
||||
};
|
||||
// Открытие контекстного меню
|
||||
const handleContextMenuClick = (action) => {
|
||||
if (action === 'add_base'){
|
||||
handleAddBaseStation();
|
||||
}
|
||||
else if (action === 'add') {
|
||||
handleAddDrone();
|
||||
} else if (action === 'save') {
|
||||
if (action === 'add_base'){ handleAddBaseStation();}
|
||||
else if (action === 'add') { handleAddDrone(); } else if (action === 'save') {
|
||||
console.log('Сохранить промежуточный результат');
|
||||
}
|
||||
setContextMenu({ ...contextMenu, visible: false });
|
||||
@ -471,16 +467,35 @@ const containsObjectRecursively = (parent, target) => {
|
||||
handleInputChange={handleInputChange}
|
||||
mapSettings={mapSettings}
|
||||
handleSettingsChange={handleSettingsChange}/>
|
||||
|
||||
<ul className="nav">
|
||||
<li className="nav-item">
|
||||
<a className="nav-link active"onClick={() => setIsSimulationRunning(true)} >Активная</a>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<a className="nav-link" onClick={() => setIsSimulationRunning(true)}>Симуляция</a>
|
||||
</li>
|
||||
</ul>
|
||||
{/* Окно программы */}
|
||||
<ProgramInterface
|
||||
mouseState={mouseState}
|
||||
mountRef={mountRef}
|
||||
openModal={openModal}
|
||||
sendMessage={sendMessage}
|
||||
RunSimulatorSignal={RunSimulatorSignal}
|
||||
getCustomCookie={getCustomCookie}
|
||||
/>
|
||||
{isSimulationRunning ? (
|
||||
// isSimulationRunning
|
||||
<SimulatorInterface
|
||||
mouseState={mouseState}
|
||||
mountRef={mountRef}
|
||||
openModal={openModal}
|
||||
sendMessage={sendMessage}
|
||||
RunSimulatorSignal={RunSimulatorSignal}
|
||||
getCustomCookie={getCustomCookie}
|
||||
/>
|
||||
) : (
|
||||
<ProgramInterface
|
||||
mouseState={mouseState}
|
||||
mountRef={mountRef}
|
||||
openModal={openModal}
|
||||
sendMessage={sendMessage}
|
||||
RunSimulatorSignal={RunSimulatorSignal}
|
||||
getCustomCookie={getCustomCookie}
|
||||
/>
|
||||
)}
|
||||
<CtxMenu
|
||||
contextMenu={contextMenu}
|
||||
handleContextMenuClick={handleContextMenuClick}
|
||||
|
@ -1,14 +1,23 @@
|
||||
import React from 'react';
|
||||
import ModalWindow from '../components/Modal/Modal';
|
||||
|
||||
|
||||
export const ModalDelete = ({isModalDeleteOpen, closeAllModals, handleDeleteSignal}) => {
|
||||
export const ModalDelete = ({ isModalDeleteOpen, closeAllModals, handleDeleteSignal }) => {
|
||||
return (
|
||||
<ModalWindow isOpen={isModalDeleteOpen} onClose={closeAllModals}>
|
||||
<h2>Удаление</h2>
|
||||
<h2>Вы точно хотите удалить объект X?</h2>
|
||||
<button onClick={closeAllModals}>Отмена</button>
|
||||
<button onClick={() => handleDeleteSignal(1) }>Удалить</button>
|
||||
</ModalWindow>
|
||||
)
|
||||
}
|
||||
<ModalWindow isOpen={isModalDeleteOpen} onClose={closeAllModals}>
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title">Удаление</h5>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<p>Вы точно хотите удалить объект X?</p>
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button type="button" className="btn btn-secondary" onClick={closeAllModals}>Отмена</button>
|
||||
<button type="button" className="btn btn-danger" onClick={() => handleDeleteSignal(1)}>Удалить</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ModalWindow>
|
||||
);
|
||||
};
|
@ -2,14 +2,25 @@ import React from 'react';
|
||||
import ModalWindow from '../components/Modal/Modal';
|
||||
|
||||
|
||||
export const ModalJson= ({isModalOpen, closeAllModals}) => {
|
||||
return (
|
||||
<ModalWindow isOpen={isModalOpen} onClose={closeAllModals}>
|
||||
<h2>Загрузка JSON</h2>
|
||||
<p>Выберите или переташите файл для загрузки</p>
|
||||
<input type='file'/>
|
||||
<button onClick={closeAllModals}>Отмена</button>
|
||||
<button onClick={() => alert('Загружено')}>Загрузить</button>
|
||||
export const ModalJson = ({ isModalOpen, closeAllModals }) => {
|
||||
return (
|
||||
<ModalWindow isOpen={isModalOpen} onClose={closeAllModals}>
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title">Загрузка JSON</h5>
|
||||
<button type="button" className="btn-close" aria-label="Close" onClick={closeAllModals}></button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<p>Выберите или переташите файл для загрузки</p>
|
||||
<input type="file" className="form-control" />
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button type="button" className="btn btn-secondary" onClick={closeAllModals}>Отмена</button>
|
||||
<button type="button" className="btn btn-success" onClick={() => alert('Загружено')}>Загрузить</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ModalWindow>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
@ -1,18 +1,21 @@
|
||||
import React from 'react';
|
||||
import ModalWindow from '../components/Modal/Modal';
|
||||
|
||||
|
||||
export const ModalMap = ({isModalMapOpen, closeAllModals, handleAddHeight, formData, handleInputChange, mapSettings, handleSettingsChange}) => {
|
||||
return (
|
||||
<ModalWindow isOpen={isModalMapOpen} onClose={closeAllModals}>
|
||||
<h2>Настройки карты</h2>
|
||||
<div className="columns">
|
||||
<div className='column'>
|
||||
<form onSubmit={handleAddHeight} style={{ marginTop: '20px' }}>
|
||||
<div>
|
||||
<label>Координата X:</label>
|
||||
export const ModalMap = ({ isModalMapOpen, closeAllModals, handleAddHeight, formData, handleInputChange, mapSettings, handleSettingsChange }) => {
|
||||
return (
|
||||
<ModalWindow isOpen={isModalMapOpen} onClose={closeAllModals}>
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title">Настройки карты</h5>
|
||||
<button type="button" className="btn-close" aria-label="Close" onClick={closeAllModals}></button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="row">
|
||||
<div className="col-md-6">
|
||||
<form onSubmit={handleAddHeight} className="mt-3">
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Координата X:</label>
|
||||
<input
|
||||
className="input is-primary"
|
||||
className="form-control"
|
||||
type="number"
|
||||
name="x"
|
||||
value={formData.x}
|
||||
@ -20,10 +23,10 @@ export const ModalMap = ({isModalMapOpen, closeAllModals, handleAddHeight, formD
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label>Координата Y:</label>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Координата Y:</label>
|
||||
<input
|
||||
className="input is-primary"
|
||||
className="form-control"
|
||||
type="number"
|
||||
name="y"
|
||||
value={formData.y}
|
||||
@ -31,10 +34,10 @@ export const ModalMap = ({isModalMapOpen, closeAllModals, handleAddHeight, formD
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label>Высота:</label>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Высота:</label>
|
||||
<input
|
||||
className="input is-primary"
|
||||
className="form-control"
|
||||
type="number"
|
||||
name="height"
|
||||
value={formData.height}
|
||||
@ -42,28 +45,31 @@ export const ModalMap = ({isModalMapOpen, closeAllModals, handleAddHeight, formD
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<button className='button' type="submit">Добавить точку высоты</button>
|
||||
<button className="btn btn-primary" type="submit">Добавить точку высоты</button>
|
||||
</form>
|
||||
</div>
|
||||
{/* Настройки карты */}
|
||||
<div className='column'>
|
||||
<div style={{ marginTop: '20px' }}>
|
||||
<h2>Настройки карты</h2>
|
||||
<div>
|
||||
<label>Максимальная высота:</label>
|
||||
<input
|
||||
className="input"
|
||||
type="number"
|
||||
name="maxHeight"
|
||||
value={mapSettings.maxHeight}
|
||||
onChange={handleSettingsChange}
|
||||
/>
|
||||
{/* Настройки карты */}
|
||||
<div className="col-md-6">
|
||||
<div className="mt-3">
|
||||
<h5>Настройки карты</h5>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Максимальная высота:</label>
|
||||
<input
|
||||
className="form-control"
|
||||
type="number"
|
||||
name="maxHeight"
|
||||
value={mapSettings.maxHeight}
|
||||
onChange={handleSettingsChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button onClick={closeAllModals}>Отмена</button>
|
||||
<button onClick={() => alert('Обновлена карта!')}>Готово</button>
|
||||
</ModalWindow>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button className="btn btn-secondary" onClick={closeAllModals}>Отмена</button>
|
||||
<button className="btn btn-success" onClick={() => alert('Обновлена карта!')}>Готово</button>
|
||||
</div>
|
||||
</ModalWindow>
|
||||
);
|
||||
};
|
@ -2,17 +2,28 @@ import React from 'react';
|
||||
import ModalWindow from '../components/Modal/Modal';
|
||||
|
||||
|
||||
export const ModalSearch = ({isModalSearchOpen, closeAllModals, handleSearch, searchTerm, setSearchTerm}) => {
|
||||
return (
|
||||
<ModalWindow isOpen={isModalSearchOpen} onClose={closeAllModals}>
|
||||
<h2>Поиск</h2>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Введите имя"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
<button onClick={handleSearch}>Найти</button>
|
||||
export const ModalSearch = ({ isModalSearchOpen, closeAllModals, handleSearch, searchTerm, setSearchTerm }) => {
|
||||
return (
|
||||
<ModalWindow isOpen={isModalSearchOpen} onClose={closeAllModals}>
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title">Поиск</h5>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="Введите имя"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button type="button" className="btn btn-primary" onClick={handleSearch}>Найти</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ModalWindow>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
@ -2,35 +2,42 @@ import React from 'react';
|
||||
import '../UserAccount.scss';
|
||||
|
||||
const PrevCalc = () => {
|
||||
|
||||
return (
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope='col'><abbr title="Уникальный идентификатор в базе данных">UID</abbr></th>
|
||||
<th scope='col'><abbr title="Название">Имя</abbr></th>
|
||||
<th scope='col'>Пользователь</th>
|
||||
<th scope='col'>Время начала</th>
|
||||
<th scope='col'>Время конца</th>
|
||||
<th scope='col'>Действия</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row">1</th>
|
||||
<td>Калькуляция полета до варшавы</td>
|
||||
<td>Гитлер</td>
|
||||
<td>1:00</td>
|
||||
<td>4:04</td>
|
||||
<td>
|
||||
<button className='btn btn-primary'>
|
||||
Загрузить
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
<div className="container mt-4">
|
||||
<h2 className="mb-4 text-center">История Калькуляций</h2>
|
||||
<table className="table table-hover table-striped table-bordered">
|
||||
<thead className="thead-dark">
|
||||
<tr>
|
||||
<th scope='col'><abbr title="Уникальный идентификатор в базе данных">UID</abbr></th>
|
||||
<th scope='col'><abbr title="Название">Имя</abbr></th>
|
||||
<th scope='col'>Пользователь</th>
|
||||
<th scope='col'>Время начала</th>
|
||||
<th scope='col'>Время конца</th>
|
||||
<th scope='col'>Действия</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row">1</th>
|
||||
<td>Калькуляция полета до Варшавы</td>
|
||||
<td>Гитлер</td>
|
||||
<td>1:00</td>
|
||||
<td>4:04</td>
|
||||
<td>
|
||||
<div className="btn-group" role="group">
|
||||
<button className='btn btn-primary'>
|
||||
Загрузить
|
||||
</button>
|
||||
<button className='btn btn-danger ml-2'>
|
||||
Удалить
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PrevCalc;
|
78
src/frontend/src/pages/SimScreen/SimScreen.js
Normal file
78
src/frontend/src/pages/SimScreen/SimScreen.js
Normal file
@ -0,0 +1,78 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
|
||||
export const SimulatorInterface = ({ mouseState, sendMessage, RunSimulatorSignal, getCustomCookie }) => {
|
||||
const [time, setTime] = useState(0);
|
||||
const [simulationState, setSimulationState] = useState({});
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
const mountRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (mountRef.current) {
|
||||
// Инициализация сцены происходит при монтировании компонента
|
||||
initializeScene(mountRef.current);
|
||||
}
|
||||
}, [mountRef.current]);
|
||||
|
||||
const initializeScene = (container) => {
|
||||
// Здесь должна быть логика создания и инициализации сцены внутри контейнера
|
||||
// Например, Three.js сцена
|
||||
console.log('Scene initialized inside container:', container);
|
||||
};
|
||||
|
||||
const handleSliderChange = (event) => {
|
||||
const newValue = event.target.value;
|
||||
setTime(newValue);
|
||||
// Обновляем состояние симуляции на основе времени
|
||||
const newSimulationState = getSimulationState(newValue);
|
||||
setSimulationState(newSimulationState);
|
||||
};
|
||||
|
||||
const handlePlayPause = () => {
|
||||
if (isPlaying) {
|
||||
setIsPlaying(false);
|
||||
} else {
|
||||
setIsPlaying(true);
|
||||
sendMessage(RunSimulatorSignal(getCustomCookie("userToken")));
|
||||
}
|
||||
};
|
||||
|
||||
const getSimulationState = (timeValue) => {
|
||||
// Здесь должна быть логика получения состояния симуляции на основе времени
|
||||
// Например, используйте временную шкалу и объекты, чтобы получить правильное состояние
|
||||
return {};
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='coler-border'>
|
||||
<div>
|
||||
<p> X: {mouseState.x} Y: {mouseState.y} Z: {mouseState.z}</p>
|
||||
</div>
|
||||
<div ref={mountRef} style={{
|
||||
minWidth: '100%',
|
||||
minHeight: '60vh',
|
||||
position: 'relative'
|
||||
}} />
|
||||
<div style={{ display: 'flex', alignItems: 'center', margin: '20px 0' }}>
|
||||
<button onClick={handlePlayPause} style={{
|
||||
backgroundColor: '#007bff',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
padding: '10px 20px',
|
||||
cursor: 'pointer',
|
||||
borderRadius: '4px'
|
||||
}}>
|
||||
{isPlaying ? 'Pause' : 'Play'}
|
||||
</button>
|
||||
<input
|
||||
type="range"
|
||||
value={time}
|
||||
onChange={handleSliderChange}
|
||||
min={0}
|
||||
max={100}
|
||||
step={1}
|
||||
style={{ flexGrow: 1, marginLeft: '20px', marginRight: '20px' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
10
src/server/entity/database/logs.go
Normal file
10
src/server/entity/database/logs.go
Normal file
@ -0,0 +1,10 @@
|
||||
package database
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
// Struct has represent a system log
|
||||
type Logs struct {
|
||||
gorm.Model
|
||||
Log string `json:"log"`
|
||||
LogType int `json:"type"`
|
||||
}
|
@ -23,10 +23,10 @@ type MapPartial struct {
|
||||
}
|
||||
|
||||
type SystemObject struct {
|
||||
Name string `json:"name"`
|
||||
Type int `json:"type"` // 0: drone, 1: base_station
|
||||
Coords [3]int `json:"coords"`
|
||||
Params *map[string]string `json:"params"`
|
||||
Name string `json:"name"`
|
||||
Type int `json:"type"` // 0: drone, 1: base_station
|
||||
Coords [3]int `json:"coords"`
|
||||
Params *map[string]interface{} `json:"params"`
|
||||
}
|
||||
|
||||
// DeleteObjectIfExists удаляет объект если он найден
|
||||
@ -63,11 +63,11 @@ func (o *Modulation) StartNewSimulation(timestep int, user_id string) {
|
||||
continue
|
||||
}
|
||||
|
||||
params := *v.Params
|
||||
params := v.Params
|
||||
if v.Type == 1 {
|
||||
// Парсинг параметров для дронов
|
||||
antennaDirection := [3]float64{1, 0, 0} // значение по умолчанию
|
||||
if dir, ok := params["antennaDirections"]; ok && dir != "" {
|
||||
if dir, ok := (*params)["$1"].(string); ok && dir != "" {
|
||||
directionValues := strings.Split(dir, ",")
|
||||
if len(directionValues) == 3 {
|
||||
antennaDirection[0], _ = strconv.ParseFloat(directionValues[0], 64)
|
||||
@ -77,12 +77,12 @@ func (o *Modulation) StartNewSimulation(timestep int, user_id string) {
|
||||
}
|
||||
|
||||
antennaRadius := 100.0 // значение по умолчанию
|
||||
if radius, ok := params["antennaRadius"]; ok && radius != "" {
|
||||
if radius, ok := (*params)["$1"].(string); ok && radius != "" {
|
||||
antennaRadius, _ = strconv.ParseFloat(radius, 64)
|
||||
}
|
||||
|
||||
waypoints := [][3]float64{}
|
||||
if wp, ok := params["wayPoints"]; ok && wp != "" {
|
||||
if wp, ok := (*params)["$1"].(string); ok && wp != "" {
|
||||
// Парсинг waypoints, предполагаем, что это строка вида "x1,y1,z1;x2,y2,z2"
|
||||
waypointStrings := strings.Split(wp, ";")
|
||||
for _, waypointStr := range waypointStrings {
|
||||
@ -97,12 +97,12 @@ func (o *Modulation) StartNewSimulation(timestep int, user_id string) {
|
||||
}
|
||||
|
||||
speed := 0.0
|
||||
if spd, ok := params["speed"]; ok && spd != "" {
|
||||
if spd, ok := (*params)["$1"].(string); ok && spd != "" {
|
||||
speed, _ = strconv.ParseFloat(spd, 64)
|
||||
}
|
||||
|
||||
meshName := ""
|
||||
if mesh, ok := params["meshName"]; ok {
|
||||
if mesh, ok := (*params)["$1"].(string); ok {
|
||||
meshName = mesh
|
||||
}
|
||||
|
||||
@ -124,7 +124,7 @@ func (o *Modulation) StartNewSimulation(timestep int, user_id string) {
|
||||
if v.Type == 2 {
|
||||
// Парсинг параметров для базовых станций
|
||||
antennaDirection := [3]float64{1, 0, 0} // значение по умолчанию
|
||||
if dir, ok := params["antennaDirections"]; ok && dir != "" {
|
||||
if dir, ok := (*params)["$1"].(string); ok && dir != "" {
|
||||
directionValues := strings.Split(dir, ",")
|
||||
if len(directionValues) == 3 {
|
||||
antennaDirection[0], _ = strconv.ParseFloat(directionValues[0], 64)
|
||||
@ -134,7 +134,7 @@ func (o *Modulation) StartNewSimulation(timestep int, user_id string) {
|
||||
}
|
||||
|
||||
antennaRadius := 200.0 // значение по умолчанию
|
||||
if radius, ok := params["antennaRadius"]; ok && radius != "" {
|
||||
if radius, ok := (*params)["$1"].(string); ok && radius != "" {
|
||||
antennaRadius, _ = strconv.ParseFloat(radius, 64)
|
||||
}
|
||||
|
||||
@ -173,10 +173,10 @@ func (o *Modulation) SpawnExampleData() bool {
|
||||
}
|
||||
|
||||
// AddObject добавляет объект станции или дрона
|
||||
func (o *Modulation) AddObject(name string, obj_type int, coords [3]int, params map[string]string) {
|
||||
func (o *Modulation) AddObject(name string, obj_type int, coords [3]int, params map[string]interface{}) error {
|
||||
if obj_type != 1 && obj_type != 2 {
|
||||
// Значит объект не станция или не дрон
|
||||
return
|
||||
return fmt.Errorf("ошибка добавления объекта: \n object type: %v", obj_type)
|
||||
}
|
||||
fmt.Println(name, obj_type, coords, params)
|
||||
if o.Objects == nil {
|
||||
@ -192,10 +192,10 @@ func (o *Modulation) AddObject(name string, obj_type int, coords [3]int, params
|
||||
Params: ¶ms,
|
||||
}
|
||||
o.Objects = append(o.Objects, obj)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateObject обновляет объект станции или дрона по имени
|
||||
func (o *Modulation) UpdateObject(name string, obj_type int, coords [3]int, params map[string]string) {
|
||||
func (o *Modulation) UpdateObject(name string, obj_type int, coords [3]int, params map[string]interface{}) {
|
||||
if obj_type != 1 && obj_type != 2 {
|
||||
// Значит объект не станция или не дрон
|
||||
return
|
||||
|
@ -47,6 +47,7 @@ func NewDBConnection() error {
|
||||
func (db *GormDbInstance) AutoMigrate() error {
|
||||
return DbDriver.DB.AutoMigrate(
|
||||
database.User{},
|
||||
database.Logs{},
|
||||
)
|
||||
}
|
||||
func (db *GormDbInstance) AutoMakeSuperUser() error {
|
||||
@ -68,6 +69,13 @@ func (db *GormDbInstance) AutoMakeSuperUser() error {
|
||||
}
|
||||
}
|
||||
|
||||
func (db *GormDbInstance) RegisterLog(type_log int, text_log string) error {
|
||||
if type_log > 0 && type_log < 10 {
|
||||
db.DB.Create(&database.Logs{LogType: type_log, Log: text_log})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *GormDbInstance) GetUserByName(name string) (*database.User, error) {
|
||||
// Find user by name
|
||||
var user database.User
|
||||
|
69
src/server/internal/server/handlers/logs/logs_handler.go
Normal file
69
src/server/internal/server/handlers/logs/logs_handler.go
Normal file
@ -0,0 +1,69 @@
|
||||
package logs_handler
|
||||
|
||||
import (
|
||||
database_entity "moxitech/dns/entity/database"
|
||||
"moxitech/dns/internal/database"
|
||||
"strconv"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func GetLogs(c *fiber.Ctx) error {
|
||||
var logs []database_entity.Logs
|
||||
|
||||
typeLogs := c.Query("type")
|
||||
if typeLogs == "" {
|
||||
typeLogs = "*"
|
||||
}
|
||||
|
||||
countLogs := c.Query("count")
|
||||
var logsCountParsed int
|
||||
var err error
|
||||
if countLogs != "" {
|
||||
logsCountParsed, err = strconv.Atoi(countLogs)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": "Invalid count parameter",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
db := database.DbDriver.DB // GORM DB instance
|
||||
query := db.Model(&database_entity.Logs{})
|
||||
|
||||
if typeLogs != "*" {
|
||||
query = query.Where("type = ?", typeLogs)
|
||||
}
|
||||
|
||||
if logsCountParsed > 0 {
|
||||
query = query.Limit(logsCountParsed)
|
||||
}
|
||||
|
||||
if err := query.Find(&logs).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
|
||||
"error": "No logs found",
|
||||
})
|
||||
}
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": "Failed to retrieve logs",
|
||||
})
|
||||
}
|
||||
|
||||
return c.JSON(logs)
|
||||
}
|
||||
|
||||
func DeleteAllLogs(c *fiber.Ctx) error {
|
||||
db := database.DbDriver.DB // GORM DB instance
|
||||
|
||||
if err := db.Exec("DELETE FROM logs").Error; err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": "Failed to delete logs",
|
||||
})
|
||||
}
|
||||
|
||||
return c.JSON(fiber.Map{
|
||||
"message": "All logs have been deleted successfully",
|
||||
})
|
||||
}
|
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"moxitech/dns/entity/dto"
|
||||
"moxitech/dns/internal/server/handlers/authorization"
|
||||
logs_handler "moxitech/dns/internal/server/handlers/logs"
|
||||
sim_queue "moxitech/dns/package/simulation"
|
||||
"os"
|
||||
"runtime"
|
||||
@ -47,6 +48,9 @@ func SpawnServer() error {
|
||||
// GET http://localhost:8080/simulations/from/history
|
||||
// app.Get("/simulations/from/history", authorization.AuthUser)
|
||||
|
||||
// GET http://localhost:8080/system/logs
|
||||
app.Get("/system/logs", logs_handler.GetLogs)
|
||||
|
||||
// GET http://localhost:8080/get/server/state
|
||||
app.Get("/get/server/state", GetServerState)
|
||||
// WebSocket маршрут
|
||||
@ -102,7 +106,7 @@ func CreateOrConnectSocket(c *websocket.Conn) {
|
||||
// Читаем сообщения от клиента
|
||||
messageType, msg, err := c.ReadMessage()
|
||||
if messageType != websocket.TextMessage {
|
||||
err = c.WriteMessage(websocket.BinaryMessage, []byte("PLS USE JSON TEXT STRUCTURE!"))
|
||||
err = c.WriteMessage(websocket.BinaryMessage, []byte("{\"error\":\"Use text instead\" }"))
|
||||
if err != nil {
|
||||
fmt.Printf("Error writing message: %v\n", err)
|
||||
break
|
||||
@ -116,7 +120,7 @@ func CreateOrConnectSocket(c *websocket.Conn) {
|
||||
fmt.Printf("Received message from user %s: %s\n", userToken, msg)
|
||||
err, ok = room.WebsocketAction(msg)
|
||||
if err != nil {
|
||||
err = c.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("%v", err)))
|
||||
err = c.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("{\"error\": \"%v\"}", err)))
|
||||
if err != nil {
|
||||
fmt.Printf("Error writing message: %v\n", err)
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"moxitech/dns/entity/websocket"
|
||||
"moxitech/dns/internal/database"
|
||||
"moxitech/dns/package/math/simulator"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -98,11 +99,12 @@ func (room *WebsocketRoom) DeleteMapObjectRequest(message websocket.SignalMessag
|
||||
// InsertObject вставляет базовые случайные данные в симуляцию
|
||||
func (room *WebsocketRoom) InsertObject(message websocket.SignalMessage) error {
|
||||
// Тип объекта (тип сигнала)
|
||||
type_obj := message.Signal
|
||||
typeObj := message.Signal
|
||||
|
||||
// Определяем структуру для данных в поле `data`
|
||||
var parsedData struct {
|
||||
Name string `json:"name"`
|
||||
Params map[string]string `json:"params"`
|
||||
Name string `json:"name"`
|
||||
Params map[string]interface{} `json:"params"`
|
||||
}
|
||||
|
||||
// Парсим `data` в структуру
|
||||
@ -111,43 +113,44 @@ func (room *WebsocketRoom) InsertObject(message websocket.SignalMessage) error {
|
||||
return fmt.Errorf("[insert] error parsing data: %v", err)
|
||||
}
|
||||
|
||||
fmt.Println("st 1")
|
||||
// Получаем значения name и params
|
||||
name := parsedData.Name
|
||||
params := parsedData.Params
|
||||
|
||||
// Проверяем наличие координат в параметрах
|
||||
if params["coords"] == "" {
|
||||
return fmt.Errorf("[insert] coords is empty")
|
||||
coordsValue, ok := params["coords"].(string)
|
||||
if !ok || coordsValue == "" {
|
||||
return fmt.Errorf("[insert] coords is missing or empty")
|
||||
}
|
||||
fmt.Println("st 2")
|
||||
|
||||
// Разделяем строку координат на отдельные элементы
|
||||
coordinateStrings := strings.Split(params["coords"], ",")
|
||||
coordinateStrings := strings.Split(coordsValue, ",")
|
||||
|
||||
// Инициализируем массив для хранения координат
|
||||
coordinates := make([]int, len(coordinateStrings))
|
||||
fmt.Println("st 3")
|
||||
|
||||
// Преобразуем каждую координату в целое число
|
||||
for i, coord := range coordinateStrings {
|
||||
parsedCoord, err := strconv.Atoi(strings.TrimSpace(coord))
|
||||
if err != nil {
|
||||
fmt.Println(fmt.Errorf("[insert] invalid coordinate: %s", coord))
|
||||
return fmt.Errorf("[insert] invalid coordinate: %s", coord)
|
||||
}
|
||||
coordinates[i] = parsedCoord
|
||||
}
|
||||
fmt.Println("st 4")
|
||||
|
||||
params["coords"] = ""
|
||||
// Проверяем, что количество координат соответствует ожидаемому (например, 3 координаты)
|
||||
if len(coordinates) != 3 {
|
||||
return fmt.Errorf("[insert] invalid number of coordinates: expected 3, got %d", len(coordinates))
|
||||
}
|
||||
|
||||
// Обновляем значение координат в параметрах
|
||||
params["coords"] = [3]int{coordinates[0], coordinates[1], coordinates[2]}
|
||||
|
||||
// Добавляем объект в комнату
|
||||
roomsMutex.Lock()
|
||||
defer roomsMutex.Unlock()
|
||||
fmt.Println("st 5")
|
||||
room.Modulation.AddObject(name, type_obj, [3]int{coordinates[0], coordinates[1], coordinates[2]}, params)
|
||||
|
||||
return nil
|
||||
return room.Modulation.AddObject(name, typeObj, [3]int{coordinates[0], coordinates[1], coordinates[2]}, params)
|
||||
}
|
||||
|
||||
// UpdateObjectRequest вставляет базовые случайные данные в симуляцию
|
||||
@ -156,8 +159,8 @@ func (room *WebsocketRoom) UpdateObjectRequest(message websocket.SignalMessage)
|
||||
type_obj := message.Signal - 20
|
||||
// Определяем структуру для данных в поле `data`
|
||||
var parsedData struct {
|
||||
Name string `json:"name"`
|
||||
Params map[string]string `json:"params"`
|
||||
Name string `json:"name"`
|
||||
Params map[string]interface{} `json:"params"`
|
||||
}
|
||||
|
||||
// Парсим `data` в структуру
|
||||
@ -169,14 +172,14 @@ func (room *WebsocketRoom) UpdateObjectRequest(message websocket.SignalMessage)
|
||||
// Получаем значения name и params
|
||||
name := parsedData.Name
|
||||
params := parsedData.Params
|
||||
|
||||
// Проверяем наличие координат в параметрах
|
||||
if params["coords"] == "" {
|
||||
return fmt.Errorf("[insert] coords is empty")
|
||||
coordsValue, ok := params["coords"].(string)
|
||||
if !ok || coordsValue == "" {
|
||||
return fmt.Errorf("[insert] coords is missing or empty")
|
||||
}
|
||||
|
||||
// Разделяем строку координат на отдельные элементы
|
||||
coordinateStrings := strings.Split(params["coords"], ",")
|
||||
coordinateStrings := strings.Split(coordsValue, ",")
|
||||
|
||||
// Инициализируем массив для хранения координат
|
||||
coordinates := make([]int, len(coordinateStrings))
|
||||
@ -189,7 +192,15 @@ func (room *WebsocketRoom) UpdateObjectRequest(message websocket.SignalMessage)
|
||||
}
|
||||
coordinates[i] = parsedCoord
|
||||
}
|
||||
params["coords"] = ""
|
||||
|
||||
// Проверяем, что количество координат соответствует ожидаемому (например, 3 координаты)
|
||||
if len(coordinates) != 3 {
|
||||
return fmt.Errorf("[insert] invalid number of coordinates: expected 3, got %d", len(coordinates))
|
||||
}
|
||||
|
||||
// Обновляем значение координат в параметрах
|
||||
params["coords"] = [3]int{coordinates[0], coordinates[1], coordinates[2]}
|
||||
|
||||
// Добавляем объект в комнату
|
||||
roomsMutex.Lock()
|
||||
defer roomsMutex.Unlock()
|
||||
@ -265,15 +276,15 @@ func (room *WebsocketRoom) WebsocketAction(message []byte) (error, bool) {
|
||||
fmt.Printf("[WebsocketAction] %v", err)
|
||||
return fmt.Errorf("error parsing occured: %v \n waiting for signal struct", err), false
|
||||
}
|
||||
fmt.Println("Run collector")
|
||||
room.UpdateGroupUptime()
|
||||
switch Signal.Signal {
|
||||
case 0:
|
||||
fmt.Println("Uptime updated")
|
||||
room.UpdateGroupUptime()
|
||||
return nil, false
|
||||
case 1:
|
||||
room.InsertObject(Signal)
|
||||
return nil, true
|
||||
fmt.Println("Начата вставка объекта")
|
||||
return room.InsertObject(Signal), true
|
||||
case 2:
|
||||
fmt.Println("Начата вставка объекта")
|
||||
room.InsertObject(Signal)
|
||||
@ -300,7 +311,9 @@ func (room *WebsocketRoom) WebsocketAction(message []byte) (error, bool) {
|
||||
// SYNC SIGNAL
|
||||
case 100:
|
||||
fmt.Println("Запуск симуляции : ", Signal)
|
||||
database.DbDriver.RegisterLog(1, "Симуляция инициирована пользователем")
|
||||
room.RunSimulation(Signal)
|
||||
|
||||
case 101:
|
||||
// room.StoreMongo(Signal)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user