Create drone model & First connect frontend + backend

This commit is contained in:
moxitech 2024-10-06 07:10:24 +07:00
parent 5eef7db6a0
commit 63f4e70896
77 changed files with 60209 additions and 553 deletions

2
.env
View File

@ -1,3 +1,5 @@
NONE_DEV_MODE=1
NGINX_PORT=80
SERVER_BASE_ADDRESS=0.0.0.0:8080

View File

@ -36,19 +36,19 @@ services:
- dns_net
restart: always
# nginx:
# image: nginx:alpine
# container_name: geotouchka-nginx-service
# ports:
# - "${NGINX_PORT}:80"
# volumes:
# # - ./frontend/geotouchka-web-application/build:/usr/share/nginx/html
# - ./nginx.conf:/etc/nginx/nginx.conf
# networks:
# - dns_net
# depends_on:
# - site
# restart: always
nginx:
image: nginx:alpine
container_name: dns-nginx
ports:
- "${NGINX_PORT}:80"
volumes:
# - ./frontend/geotouchka-web-application/build:/usr/share/nginx/html
- ./nginx.conf:/etc/nginx/nginx.conf
networks:
- dns_net
depends_on:
- front
restart: always
server:
container_name: dns-server

View File

@ -6,10 +6,10 @@ http {
server {
listen 80;
server_name localhost;
add_header 'Access-Control-Allow-Origin' 'http://site' always;
add_header 'Access-Control-Allow-Origin' 'http://front' always;
location / {
proxy_pass http://site:3000;
proxy_pass http://front:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

View File

@ -11,16 +11,13 @@
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"leaflet": "^1.9.4",
"leaflet-defaulticon-compatibility": "^0.1.2",
"bootstrap-icons": "^1.11.3",
"qrcode.react": "^4.0.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-leaflet": "^4.2.1",
"react-modal": "^3.16.1",
"react-router-dom": "^6.26.2",
"react-scripts": "5.0.1",
"socket.io": "^4.8.0",
"socket.io-client": "^4.8.0",
"three": "^0.169.0",
"web-vitals": "^2.1.4"
},
@ -3551,17 +3548,6 @@
}
}
},
"node_modules/@react-leaflet/core": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz",
"integrity": "sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==",
"license": "Hippocratic-2.1",
"peerDependencies": {
"leaflet": "^1.9.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
},
"node_modules/@remix-run/router": {
"version": "1.19.2",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.2.tgz",
@ -3686,12 +3672,6 @@
"@sinonjs/commons": "^1.7.0"
}
},
"node_modules/@socket.io/component-emitter": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
"license": "MIT"
},
"node_modules/@surma/rollup-plugin-off-main-thread": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",
@ -4346,21 +4326,6 @@
"@types/node": "*"
}
},
"node_modules/@types/cookie": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
"integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==",
"license": "MIT"
},
"node_modules/@types/cors": {
"version": "2.8.17",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz",
"integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/eslint": {
"version": "8.56.12",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz",
@ -6182,15 +6147,6 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"license": "MIT"
},
"node_modules/base64id": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
"license": "MIT",
"engines": {
"node": "^4.5.0 || >= 5.9"
}
},
"node_modules/batch": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
@ -6316,6 +6272,21 @@
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
"license": "ISC"
},
"node_modules/bootstrap-icons": {
"version": "1.11.3",
"resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.11.3.tgz",
"integrity": "sha512-+3lpHrCw/it2/7lBL15VR0HEumaBss0+f/Lb6ZvHISn1mlK83jjFpooTLsMWbIjJMDjDjOExMsTxnXSIT4k4ww==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/twbs"
},
{
"type": "opencollective",
"url": "https://opencollective.com/bootstrap"
}
]
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@ -6887,19 +6858,6 @@
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
"license": "MIT"
},
"node_modules/cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"license": "MIT",
"dependencies": {
"object-assign": "^4",
"vary": "^1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/cosmiconfig": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
@ -7856,100 +7814,6 @@
"node": ">= 0.8"
}
},
"node_modules/engine.io": {
"version": "6.6.1",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.1.tgz",
"integrity": "sha512-NEpDCw9hrvBW+hVEOK4T7v0jFJ++KgtPl4jKFwsZVfG1XhS0dCrSb3VMb9gPAd7VAdW52VT1EnaNiU2vM8C0og==",
"license": "MIT",
"dependencies": {
"@types/cookie": "^0.4.1",
"@types/cors": "^2.8.12",
"@types/node": ">=10.0.0",
"accepts": "~1.3.4",
"base64id": "2.0.0",
"cookie": "~0.4.1",
"cors": "~2.8.5",
"debug": "~4.3.1",
"engine.io-parser": "~5.2.1",
"ws": "~8.17.1"
},
"engines": {
"node": ">=10.2.0"
}
},
"node_modules/engine.io-client": {
"version": "6.6.1",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.1.tgz",
"integrity": "sha512-aYuoak7I+R83M/BBPIOs2to51BmFIpC1wZe6zZzMrT2llVsHy5cvcmdsJgP2Qz6smHu+sD9oexiSUAVd8OfBPw==",
"license": "MIT",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1",
"engine.io-parser": "~5.2.1",
"ws": "~8.17.1",
"xmlhttprequest-ssl": "~2.1.1"
}
},
"node_modules/engine.io-client/node_modules/ws": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/engine.io-parser": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/engine.io/node_modules/cookie": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
"integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/engine.io/node_modules/ws": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/enhanced-resolve": {
"version": "5.17.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
@ -8999,6 +8863,11 @@
"url": "https://github.com/sindresorhus/execa?sponsor=1"
}
},
"node_modules/exenv": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz",
"integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw=="
},
"node_modules/exit": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
@ -13497,18 +13366,6 @@
"shell-quote": "^1.8.1"
}
},
"node_modules/leaflet": {
"version": "1.9.4",
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz",
"integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==",
"license": "BSD-2-Clause"
},
"node_modules/leaflet-defaulticon-compatibility": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/leaflet-defaulticon-compatibility/-/leaflet-defaulticon-compatibility-0.1.2.tgz",
"integrity": "sha512-IrKagWxkTwzxUkFIumy/Zmo3ksjuAu3zEadtOuJcKzuXaD76Gwvg2Z1mLyx7y52ykOzM8rAH5ChBs4DnfdGa6Q==",
"license": "BSD-2-Clause"
},
"node_modules/leven": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
@ -16046,7 +15903,6 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-4.0.1.tgz",
"integrity": "sha512-Lpj0tPBn561WiQ3QQWXbkx8xTtB8BZkJeMZWLJIL8iaPBCoWzW1IpCeU3gY5MDqsb0+efCvEGkl9O0naP64crA==",
"license": "ISC",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
@ -16402,18 +16258,27 @@
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
"license": "MIT"
},
"node_modules/react-leaflet": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.2.1.tgz",
"integrity": "sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==",
"license": "Hippocratic-2.1",
"node_modules/react-lifecycles-compat": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
"node_modules/react-modal": {
"version": "3.16.1",
"resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.16.1.tgz",
"integrity": "sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==",
"dependencies": {
"@react-leaflet/core": "^2.1.0"
"exenv": "^1.2.0",
"prop-types": "^15.7.2",
"react-lifecycles-compat": "^3.0.0",
"warning": "^4.0.3"
},
"engines": {
"node": ">=8"
},
"peerDependencies": {
"leaflet": "^1.9.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
"react": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18",
"react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18"
}
},
"node_modules/react-refresh": {
@ -17518,83 +17383,6 @@
"node": ">=8"
}
},
"node_modules/socket.io": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.0.tgz",
"integrity": "sha512-8U6BEgGjQOfGz3HHTYaC/L1GaxDCJ/KM0XTkJly0EhZ5U/du9uNEZy4ZgYzEzIqlx2CMm25CrCqr1ck899eLNA==",
"license": "MIT",
"dependencies": {
"accepts": "~1.3.4",
"base64id": "~2.0.0",
"cors": "~2.8.5",
"debug": "~4.3.2",
"engine.io": "~6.6.0",
"socket.io-adapter": "~2.5.2",
"socket.io-parser": "~4.2.4"
},
"engines": {
"node": ">=10.2.0"
}
},
"node_modules/socket.io-adapter": {
"version": "2.5.5",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz",
"integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==",
"license": "MIT",
"dependencies": {
"debug": "~4.3.4",
"ws": "~8.17.1"
}
},
"node_modules/socket.io-adapter/node_modules/ws": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/socket.io-client": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.0.tgz",
"integrity": "sha512-C0jdhD5yQahMws9alf/yvtsMGTaIDBnZ8Rb5HU56svyq0l5LIrGzIDZZD5pHQlmzxLuU91Gz+VpQMKgCTNYtkw==",
"license": "MIT",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.2",
"engine.io-client": "~6.6.1",
"socket.io-parser": "~4.2.4"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io-parser": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
"license": "MIT",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/sockjs": {
"version": "0.3.24",
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
@ -19174,6 +18962,14 @@
"makeerror": "1.0.12"
}
},
"node_modules/warning": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/watchpack": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
@ -20095,14 +19891,6 @@
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
"license": "MIT"
},
"node_modules/xmlhttprequest-ssl": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.1.tgz",
"integrity": "sha512-ptjR8YSJIXoA3Mbv5po7RtSYHO6mZr8s7i5VGmEk7QY2pQWyT1o0N+W1gKbOyJPUCGXGnuw0wqe8f0L6Y0ny7g==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",

View File

@ -6,16 +6,13 @@
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"leaflet": "^1.9.4",
"leaflet-defaulticon-compatibility": "^0.1.2",
"bootstrap-icons": "^1.11.3",
"qrcode.react": "^4.0.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-leaflet": "^4.2.1",
"react-modal": "^3.16.1",
"react-router-dom": "^6.26.2",
"react-scripts": "5.0.1",
"socket.io": "^4.8.0",
"socket.io-client": "^4.8.0",
"three": "^0.169.0",
"web-vitals": "^2.1.4"
},

Binary file not shown.

View File

@ -0,0 +1 @@
{"accessors":[],"asset":{"generator":"Open CASCADE Technology 7.6 [dev.opencascade.org]","version":"2.0"},"bufferViews":[],"buffers":[{"byteLength":0,"uri":"drone_normalized_gltf.bin"}],"meshes":[],"nodes":[],"scene":0,"scenes":[{"nodes":[]}]}

View File

@ -1,7 +1,12 @@
.App {
text-align: center;
}
.coler {
background-color: #0d1117;
color: azure;
}
.App-logo {
height: 40vmin;
pointer-events: none;

View File

@ -9,7 +9,9 @@ import Login from './pages/Login'; // Импортируем страницу
import Connections from './pages/Connections'; // Импортируем страницу подключений
import PrevCalc from './pages/PrevCalc'; // Импортируем страницу подключений
import Docs from './pages/Docs'; // Импортируем страницу подключений
import "./css/bulma.min.css"
import './css/bootstrap-5.3.3-dist/css/bootstrap.min.css';
import './App.css';
import "bootstrap-icons/font/bootstrap-icons.css";
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
const App = () => {
@ -42,7 +44,7 @@ const App = () => {
activeTab={activeTab}
onLogout={handleLogout} // Передаем функцию handleLogout через пропс onLogout
/>
<div style={{ padding: '20px', flex: 1 }}>
<div className='coler' style={{ padding: '20px', flex: 1 }}>
{activeTab === 'main' && <Main />} {/* Подключаем компонент Dashboard */}
{activeTab === 'map' && <Dashboard />} {/* Подключаем компонент Dashboard */}
{activeTab === 'connection' && <Connections />} {/* Страница подключений */}

View File

@ -1,9 +0,0 @@
.main__imager {
height: 100%;
background-image: url('../public/blueprints.jpg');
}
.main__t_b {
color: black;
}

View File

@ -0,0 +1,6 @@
const InvokeStartModulation = () => {
}

View File

@ -5,7 +5,7 @@ export const giveMeServerAddress = () => {
if (process.env.SERVER_BASE_ADDRESS) {
return process.env.SERVER_BASE_ADDRESS
}
return "http://localhost:3000";
return "http://localhost:8080";
}
export const ServerRequest = async (req, type, data = null) => {
@ -23,7 +23,7 @@ export const ServerRequest = async (req, type, data = null) => {
}
try {
const response = await fetch(`${addr}/${req}`, options); // выполняем запрос
const response = await fetch(`${addr}${req}`, options); // выполняем запрос
const responseData = await response.json(); // парсим JSON ответ
return {
@ -38,3 +38,7 @@ export const ServerRequest = async (req, type, data = null) => {
};
}
};
export const ServerWebsocketRequest = async () => {
}

View File

@ -0,0 +1,13 @@
.main__imager {
height: 100%;
/* background-image: url('../../../public/blueprints.jpg'); */
}
.main__t_b {
color: black;
}
.title {
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,597 @@
/*!
* Bootstrap Reboot v5.3.3 (https://getbootstrap.com/)
* Copyright 2011-2024 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
:root,
[data-bs-theme=light] {
--bs-blue: #0d6efd;
--bs-indigo: #6610f2;
--bs-purple: #6f42c1;
--bs-pink: #d63384;
--bs-red: #dc3545;
--bs-orange: #fd7e14;
--bs-yellow: #ffc107;
--bs-green: #198754;
--bs-teal: #20c997;
--bs-cyan: #0dcaf0;
--bs-black: #000;
--bs-white: #fff;
--bs-gray: #6c757d;
--bs-gray-dark: #343a40;
--bs-gray-100: #f8f9fa;
--bs-gray-200: #e9ecef;
--bs-gray-300: #dee2e6;
--bs-gray-400: #ced4da;
--bs-gray-500: #adb5bd;
--bs-gray-600: #6c757d;
--bs-gray-700: #495057;
--bs-gray-800: #343a40;
--bs-gray-900: #212529;
--bs-primary: #0d6efd;
--bs-secondary: #6c757d;
--bs-success: #198754;
--bs-info: #0dcaf0;
--bs-warning: #ffc107;
--bs-danger: #dc3545;
--bs-light: #f8f9fa;
--bs-dark: #212529;
--bs-primary-rgb: 13, 110, 253;
--bs-secondary-rgb: 108, 117, 125;
--bs-success-rgb: 25, 135, 84;
--bs-info-rgb: 13, 202, 240;
--bs-warning-rgb: 255, 193, 7;
--bs-danger-rgb: 220, 53, 69;
--bs-light-rgb: 248, 249, 250;
--bs-dark-rgb: 33, 37, 41;
--bs-primary-text-emphasis: #052c65;
--bs-secondary-text-emphasis: #2b2f32;
--bs-success-text-emphasis: #0a3622;
--bs-info-text-emphasis: #055160;
--bs-warning-text-emphasis: #664d03;
--bs-danger-text-emphasis: #58151c;
--bs-light-text-emphasis: #495057;
--bs-dark-text-emphasis: #495057;
--bs-primary-bg-subtle: #cfe2ff;
--bs-secondary-bg-subtle: #e2e3e5;
--bs-success-bg-subtle: #d1e7dd;
--bs-info-bg-subtle: #cff4fc;
--bs-warning-bg-subtle: #fff3cd;
--bs-danger-bg-subtle: #f8d7da;
--bs-light-bg-subtle: #fcfcfd;
--bs-dark-bg-subtle: #ced4da;
--bs-primary-border-subtle: #9ec5fe;
--bs-secondary-border-subtle: #c4c8cb;
--bs-success-border-subtle: #a3cfbb;
--bs-info-border-subtle: #9eeaf9;
--bs-warning-border-subtle: #ffe69c;
--bs-danger-border-subtle: #f1aeb5;
--bs-light-border-subtle: #e9ecef;
--bs-dark-border-subtle: #adb5bd;
--bs-white-rgb: 255, 255, 255;
--bs-black-rgb: 0, 0, 0;
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
--bs-body-font-family: var(--bs-font-sans-serif);
--bs-body-font-size: 1rem;
--bs-body-font-weight: 400;
--bs-body-line-height: 1.5;
--bs-body-color: #212529;
--bs-body-color-rgb: 33, 37, 41;
--bs-body-bg: #fff;
--bs-body-bg-rgb: 255, 255, 255;
--bs-emphasis-color: #000;
--bs-emphasis-color-rgb: 0, 0, 0;
--bs-secondary-color: rgba(33, 37, 41, 0.75);
--bs-secondary-color-rgb: 33, 37, 41;
--bs-secondary-bg: #e9ecef;
--bs-secondary-bg-rgb: 233, 236, 239;
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
--bs-tertiary-color-rgb: 33, 37, 41;
--bs-tertiary-bg: #f8f9fa;
--bs-tertiary-bg-rgb: 248, 249, 250;
--bs-heading-color: inherit;
--bs-link-color: #0d6efd;
--bs-link-color-rgb: 13, 110, 253;
--bs-link-decoration: underline;
--bs-link-hover-color: #0a58ca;
--bs-link-hover-color-rgb: 10, 88, 202;
--bs-code-color: #d63384;
--bs-highlight-color: #212529;
--bs-highlight-bg: #fff3cd;
--bs-border-width: 1px;
--bs-border-style: solid;
--bs-border-color: #dee2e6;
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
--bs-border-radius: 0.375rem;
--bs-border-radius-sm: 0.25rem;
--bs-border-radius-lg: 0.5rem;
--bs-border-radius-xl: 1rem;
--bs-border-radius-xxl: 2rem;
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
--bs-border-radius-pill: 50rem;
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
--bs-focus-ring-width: 0.25rem;
--bs-focus-ring-opacity: 0.25;
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
--bs-form-valid-color: #198754;
--bs-form-valid-border-color: #198754;
--bs-form-invalid-color: #dc3545;
--bs-form-invalid-border-color: #dc3545;
}
[data-bs-theme=dark] {
color-scheme: dark;
--bs-body-color: #dee2e6;
--bs-body-color-rgb: 222, 226, 230;
--bs-body-bg: #212529;
--bs-body-bg-rgb: 33, 37, 41;
--bs-emphasis-color: #fff;
--bs-emphasis-color-rgb: 255, 255, 255;
--bs-secondary-color: rgba(222, 226, 230, 0.75);
--bs-secondary-color-rgb: 222, 226, 230;
--bs-secondary-bg: #343a40;
--bs-secondary-bg-rgb: 52, 58, 64;
--bs-tertiary-color: rgba(222, 226, 230, 0.5);
--bs-tertiary-color-rgb: 222, 226, 230;
--bs-tertiary-bg: #2b3035;
--bs-tertiary-bg-rgb: 43, 48, 53;
--bs-primary-text-emphasis: #6ea8fe;
--bs-secondary-text-emphasis: #a7acb1;
--bs-success-text-emphasis: #75b798;
--bs-info-text-emphasis: #6edff6;
--bs-warning-text-emphasis: #ffda6a;
--bs-danger-text-emphasis: #ea868f;
--bs-light-text-emphasis: #f8f9fa;
--bs-dark-text-emphasis: #dee2e6;
--bs-primary-bg-subtle: #031633;
--bs-secondary-bg-subtle: #161719;
--bs-success-bg-subtle: #051b11;
--bs-info-bg-subtle: #032830;
--bs-warning-bg-subtle: #332701;
--bs-danger-bg-subtle: #2c0b0e;
--bs-light-bg-subtle: #343a40;
--bs-dark-bg-subtle: #1a1d20;
--bs-primary-border-subtle: #084298;
--bs-secondary-border-subtle: #41464b;
--bs-success-border-subtle: #0f5132;
--bs-info-border-subtle: #087990;
--bs-warning-border-subtle: #997404;
--bs-danger-border-subtle: #842029;
--bs-light-border-subtle: #495057;
--bs-dark-border-subtle: #343a40;
--bs-heading-color: inherit;
--bs-link-color: #6ea8fe;
--bs-link-hover-color: #8bb9fe;
--bs-link-color-rgb: 110, 168, 254;
--bs-link-hover-color-rgb: 139, 185, 254;
--bs-code-color: #e685b5;
--bs-highlight-color: #dee2e6;
--bs-highlight-bg: #664d03;
--bs-border-color: #495057;
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
--bs-form-valid-color: #75b798;
--bs-form-valid-border-color: #75b798;
--bs-form-invalid-color: #ea868f;
--bs-form-invalid-border-color: #ea868f;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
@media (prefers-reduced-motion: no-preference) {
:root {
scroll-behavior: smooth;
}
}
body {
margin: 0;
font-family: var(--bs-body-font-family);
font-size: var(--bs-body-font-size);
font-weight: var(--bs-body-font-weight);
line-height: var(--bs-body-line-height);
color: var(--bs-body-color);
text-align: var(--bs-body-text-align);
background-color: var(--bs-body-bg);
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
hr {
margin: 1rem 0;
color: inherit;
border: 0;
border-top: var(--bs-border-width) solid;
opacity: 0.25;
}
h6, h5, h4, h3, h2, h1 {
margin-top: 0;
margin-bottom: 0.5rem;
font-weight: 500;
line-height: 1.2;
color: var(--bs-heading-color);
}
h1 {
font-size: calc(1.375rem + 1.5vw);
}
@media (min-width: 1200px) {
h1 {
font-size: 2.5rem;
}
}
h2 {
font-size: calc(1.325rem + 0.9vw);
}
@media (min-width: 1200px) {
h2 {
font-size: 2rem;
}
}
h3 {
font-size: calc(1.3rem + 0.6vw);
}
@media (min-width: 1200px) {
h3 {
font-size: 1.75rem;
}
}
h4 {
font-size: calc(1.275rem + 0.3vw);
}
@media (min-width: 1200px) {
h4 {
font-size: 1.5rem;
}
}
h5 {
font-size: 1.25rem;
}
h6 {
font-size: 1rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title] {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
-webkit-text-decoration-skip-ink: none;
text-decoration-skip-ink: none;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul {
padding-left: 2rem;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: 0.5rem;
margin-left: 0;
}
blockquote {
margin: 0 0 1rem;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 0.875em;
}
mark {
padding: 0.1875em;
color: var(--bs-highlight-color);
background-color: var(--bs-highlight-bg);
}
sub,
sup {
position: relative;
font-size: 0.75em;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
a {
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
text-decoration: underline;
}
a:hover {
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
}
a:not([href]):not([class]), a:not([href]):not([class]):hover {
color: inherit;
text-decoration: none;
}
pre,
code,
kbd,
samp {
font-family: var(--bs-font-monospace);
font-size: 1em;
}
pre {
display: block;
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
font-size: 0.875em;
}
pre code {
font-size: inherit;
color: inherit;
word-break: normal;
}
code {
font-size: 0.875em;
color: var(--bs-code-color);
word-wrap: break-word;
}
a > code {
color: inherit;
}
kbd {
padding: 0.1875rem 0.375rem;
font-size: 0.875em;
color: var(--bs-body-bg);
background-color: var(--bs-body-color);
border-radius: 0.25rem;
}
kbd kbd {
padding: 0;
font-size: 1em;
}
figure {
margin: 0 0 1rem;
}
img,
svg {
vertical-align: middle;
}
table {
caption-side: bottom;
border-collapse: collapse;
}
caption {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
color: var(--bs-secondary-color);
text-align: left;
}
th {
text-align: inherit;
text-align: -webkit-match-parent;
}
thead,
tbody,
tfoot,
tr,
td,
th {
border-color: inherit;
border-style: solid;
border-width: 0;
}
label {
display: inline-block;
}
button {
border-radius: 0;
}
button:focus:not(:focus-visible) {
outline: 0;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
select {
text-transform: none;
}
[role=button] {
cursor: pointer;
}
select {
word-wrap: normal;
}
select:disabled {
opacity: 1;
}
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
display: none !important;
}
button,
[type=button],
[type=reset],
[type=submit] {
-webkit-appearance: button;
}
button:not(:disabled),
[type=button]:not(:disabled),
[type=reset]:not(:disabled),
[type=submit]:not(:disabled) {
cursor: pointer;
}
::-moz-focus-inner {
padding: 0;
border-style: none;
}
textarea {
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
float: left;
width: 100%;
padding: 0;
margin-bottom: 0.5rem;
font-size: calc(1.275rem + 0.3vw);
line-height: inherit;
}
@media (min-width: 1200px) {
legend {
font-size: 1.5rem;
}
}
legend + * {
clear: left;
}
::-webkit-datetime-edit-fields-wrapper,
::-webkit-datetime-edit-text,
::-webkit-datetime-edit-minute,
::-webkit-datetime-edit-hour-field,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-year-field {
padding: 0;
}
::-webkit-inner-spin-button {
height: auto;
}
[type=search] {
-webkit-appearance: textfield;
outline-offset: -2px;
}
/* rtl:raw:
[type="tel"],
[type="url"],
[type="email"],
[type="number"] {
direction: ltr;
}
*/
::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-color-swatch-wrapper {
padding: 0;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
::file-selector-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
iframe {
border: 0;
}
summary {
display: list-item;
cursor: pointer;
}
progress {
vertical-align: baseline;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,594 @@
/*!
* Bootstrap Reboot v5.3.3 (https://getbootstrap.com/)
* Copyright 2011-2024 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
:root,
[data-bs-theme=light] {
--bs-blue: #0d6efd;
--bs-indigo: #6610f2;
--bs-purple: #6f42c1;
--bs-pink: #d63384;
--bs-red: #dc3545;
--bs-orange: #fd7e14;
--bs-yellow: #ffc107;
--bs-green: #198754;
--bs-teal: #20c997;
--bs-cyan: #0dcaf0;
--bs-black: #000;
--bs-white: #fff;
--bs-gray: #6c757d;
--bs-gray-dark: #343a40;
--bs-gray-100: #f8f9fa;
--bs-gray-200: #e9ecef;
--bs-gray-300: #dee2e6;
--bs-gray-400: #ced4da;
--bs-gray-500: #adb5bd;
--bs-gray-600: #6c757d;
--bs-gray-700: #495057;
--bs-gray-800: #343a40;
--bs-gray-900: #212529;
--bs-primary: #0d6efd;
--bs-secondary: #6c757d;
--bs-success: #198754;
--bs-info: #0dcaf0;
--bs-warning: #ffc107;
--bs-danger: #dc3545;
--bs-light: #f8f9fa;
--bs-dark: #212529;
--bs-primary-rgb: 13, 110, 253;
--bs-secondary-rgb: 108, 117, 125;
--bs-success-rgb: 25, 135, 84;
--bs-info-rgb: 13, 202, 240;
--bs-warning-rgb: 255, 193, 7;
--bs-danger-rgb: 220, 53, 69;
--bs-light-rgb: 248, 249, 250;
--bs-dark-rgb: 33, 37, 41;
--bs-primary-text-emphasis: #052c65;
--bs-secondary-text-emphasis: #2b2f32;
--bs-success-text-emphasis: #0a3622;
--bs-info-text-emphasis: #055160;
--bs-warning-text-emphasis: #664d03;
--bs-danger-text-emphasis: #58151c;
--bs-light-text-emphasis: #495057;
--bs-dark-text-emphasis: #495057;
--bs-primary-bg-subtle: #cfe2ff;
--bs-secondary-bg-subtle: #e2e3e5;
--bs-success-bg-subtle: #d1e7dd;
--bs-info-bg-subtle: #cff4fc;
--bs-warning-bg-subtle: #fff3cd;
--bs-danger-bg-subtle: #f8d7da;
--bs-light-bg-subtle: #fcfcfd;
--bs-dark-bg-subtle: #ced4da;
--bs-primary-border-subtle: #9ec5fe;
--bs-secondary-border-subtle: #c4c8cb;
--bs-success-border-subtle: #a3cfbb;
--bs-info-border-subtle: #9eeaf9;
--bs-warning-border-subtle: #ffe69c;
--bs-danger-border-subtle: #f1aeb5;
--bs-light-border-subtle: #e9ecef;
--bs-dark-border-subtle: #adb5bd;
--bs-white-rgb: 255, 255, 255;
--bs-black-rgb: 0, 0, 0;
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
--bs-body-font-family: var(--bs-font-sans-serif);
--bs-body-font-size: 1rem;
--bs-body-font-weight: 400;
--bs-body-line-height: 1.5;
--bs-body-color: #212529;
--bs-body-color-rgb: 33, 37, 41;
--bs-body-bg: #fff;
--bs-body-bg-rgb: 255, 255, 255;
--bs-emphasis-color: #000;
--bs-emphasis-color-rgb: 0, 0, 0;
--bs-secondary-color: rgba(33, 37, 41, 0.75);
--bs-secondary-color-rgb: 33, 37, 41;
--bs-secondary-bg: #e9ecef;
--bs-secondary-bg-rgb: 233, 236, 239;
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
--bs-tertiary-color-rgb: 33, 37, 41;
--bs-tertiary-bg: #f8f9fa;
--bs-tertiary-bg-rgb: 248, 249, 250;
--bs-heading-color: inherit;
--bs-link-color: #0d6efd;
--bs-link-color-rgb: 13, 110, 253;
--bs-link-decoration: underline;
--bs-link-hover-color: #0a58ca;
--bs-link-hover-color-rgb: 10, 88, 202;
--bs-code-color: #d63384;
--bs-highlight-color: #212529;
--bs-highlight-bg: #fff3cd;
--bs-border-width: 1px;
--bs-border-style: solid;
--bs-border-color: #dee2e6;
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
--bs-border-radius: 0.375rem;
--bs-border-radius-sm: 0.25rem;
--bs-border-radius-lg: 0.5rem;
--bs-border-radius-xl: 1rem;
--bs-border-radius-xxl: 2rem;
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
--bs-border-radius-pill: 50rem;
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
--bs-focus-ring-width: 0.25rem;
--bs-focus-ring-opacity: 0.25;
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
--bs-form-valid-color: #198754;
--bs-form-valid-border-color: #198754;
--bs-form-invalid-color: #dc3545;
--bs-form-invalid-border-color: #dc3545;
}
[data-bs-theme=dark] {
color-scheme: dark;
--bs-body-color: #dee2e6;
--bs-body-color-rgb: 222, 226, 230;
--bs-body-bg: #212529;
--bs-body-bg-rgb: 33, 37, 41;
--bs-emphasis-color: #fff;
--bs-emphasis-color-rgb: 255, 255, 255;
--bs-secondary-color: rgba(222, 226, 230, 0.75);
--bs-secondary-color-rgb: 222, 226, 230;
--bs-secondary-bg: #343a40;
--bs-secondary-bg-rgb: 52, 58, 64;
--bs-tertiary-color: rgba(222, 226, 230, 0.5);
--bs-tertiary-color-rgb: 222, 226, 230;
--bs-tertiary-bg: #2b3035;
--bs-tertiary-bg-rgb: 43, 48, 53;
--bs-primary-text-emphasis: #6ea8fe;
--bs-secondary-text-emphasis: #a7acb1;
--bs-success-text-emphasis: #75b798;
--bs-info-text-emphasis: #6edff6;
--bs-warning-text-emphasis: #ffda6a;
--bs-danger-text-emphasis: #ea868f;
--bs-light-text-emphasis: #f8f9fa;
--bs-dark-text-emphasis: #dee2e6;
--bs-primary-bg-subtle: #031633;
--bs-secondary-bg-subtle: #161719;
--bs-success-bg-subtle: #051b11;
--bs-info-bg-subtle: #032830;
--bs-warning-bg-subtle: #332701;
--bs-danger-bg-subtle: #2c0b0e;
--bs-light-bg-subtle: #343a40;
--bs-dark-bg-subtle: #1a1d20;
--bs-primary-border-subtle: #084298;
--bs-secondary-border-subtle: #41464b;
--bs-success-border-subtle: #0f5132;
--bs-info-border-subtle: #087990;
--bs-warning-border-subtle: #997404;
--bs-danger-border-subtle: #842029;
--bs-light-border-subtle: #495057;
--bs-dark-border-subtle: #343a40;
--bs-heading-color: inherit;
--bs-link-color: #6ea8fe;
--bs-link-hover-color: #8bb9fe;
--bs-link-color-rgb: 110, 168, 254;
--bs-link-hover-color-rgb: 139, 185, 254;
--bs-code-color: #e685b5;
--bs-highlight-color: #dee2e6;
--bs-highlight-bg: #664d03;
--bs-border-color: #495057;
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
--bs-form-valid-color: #75b798;
--bs-form-valid-border-color: #75b798;
--bs-form-invalid-color: #ea868f;
--bs-form-invalid-border-color: #ea868f;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
@media (prefers-reduced-motion: no-preference) {
:root {
scroll-behavior: smooth;
}
}
body {
margin: 0;
font-family: var(--bs-body-font-family);
font-size: var(--bs-body-font-size);
font-weight: var(--bs-body-font-weight);
line-height: var(--bs-body-line-height);
color: var(--bs-body-color);
text-align: var(--bs-body-text-align);
background-color: var(--bs-body-bg);
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
hr {
margin: 1rem 0;
color: inherit;
border: 0;
border-top: var(--bs-border-width) solid;
opacity: 0.25;
}
h6, h5, h4, h3, h2, h1 {
margin-top: 0;
margin-bottom: 0.5rem;
font-weight: 500;
line-height: 1.2;
color: var(--bs-heading-color);
}
h1 {
font-size: calc(1.375rem + 1.5vw);
}
@media (min-width: 1200px) {
h1 {
font-size: 2.5rem;
}
}
h2 {
font-size: calc(1.325rem + 0.9vw);
}
@media (min-width: 1200px) {
h2 {
font-size: 2rem;
}
}
h3 {
font-size: calc(1.3rem + 0.6vw);
}
@media (min-width: 1200px) {
h3 {
font-size: 1.75rem;
}
}
h4 {
font-size: calc(1.275rem + 0.3vw);
}
@media (min-width: 1200px) {
h4 {
font-size: 1.5rem;
}
}
h5 {
font-size: 1.25rem;
}
h6 {
font-size: 1rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title] {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
-webkit-text-decoration-skip-ink: none;
text-decoration-skip-ink: none;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul {
padding-right: 2rem;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: 0.5rem;
margin-right: 0;
}
blockquote {
margin: 0 0 1rem;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 0.875em;
}
mark {
padding: 0.1875em;
color: var(--bs-highlight-color);
background-color: var(--bs-highlight-bg);
}
sub,
sup {
position: relative;
font-size: 0.75em;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
a {
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
text-decoration: underline;
}
a:hover {
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
}
a:not([href]):not([class]), a:not([href]):not([class]):hover {
color: inherit;
text-decoration: none;
}
pre,
code,
kbd,
samp {
font-family: var(--bs-font-monospace);
font-size: 1em;
}
pre {
display: block;
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
font-size: 0.875em;
}
pre code {
font-size: inherit;
color: inherit;
word-break: normal;
}
code {
font-size: 0.875em;
color: var(--bs-code-color);
word-wrap: break-word;
}
a > code {
color: inherit;
}
kbd {
padding: 0.1875rem 0.375rem;
font-size: 0.875em;
color: var(--bs-body-bg);
background-color: var(--bs-body-color);
border-radius: 0.25rem;
}
kbd kbd {
padding: 0;
font-size: 1em;
}
figure {
margin: 0 0 1rem;
}
img,
svg {
vertical-align: middle;
}
table {
caption-side: bottom;
border-collapse: collapse;
}
caption {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
color: var(--bs-secondary-color);
text-align: right;
}
th {
text-align: inherit;
text-align: -webkit-match-parent;
}
thead,
tbody,
tfoot,
tr,
td,
th {
border-color: inherit;
border-style: solid;
border-width: 0;
}
label {
display: inline-block;
}
button {
border-radius: 0;
}
button:focus:not(:focus-visible) {
outline: 0;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
select {
text-transform: none;
}
[role=button] {
cursor: pointer;
}
select {
word-wrap: normal;
}
select:disabled {
opacity: 1;
}
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
display: none !important;
}
button,
[type=button],
[type=reset],
[type=submit] {
-webkit-appearance: button;
}
button:not(:disabled),
[type=button]:not(:disabled),
[type=reset]:not(:disabled),
[type=submit]:not(:disabled) {
cursor: pointer;
}
::-moz-focus-inner {
padding: 0;
border-style: none;
}
textarea {
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
float: right;
width: 100%;
padding: 0;
margin-bottom: 0.5rem;
font-size: calc(1.275rem + 0.3vw);
line-height: inherit;
}
@media (min-width: 1200px) {
legend {
font-size: 1.5rem;
}
}
legend + * {
clear: right;
}
::-webkit-datetime-edit-fields-wrapper,
::-webkit-datetime-edit-text,
::-webkit-datetime-edit-minute,
::-webkit-datetime-edit-hour-field,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-year-field {
padding: 0;
}
::-webkit-inner-spin-button {
height: auto;
}
[type=search] {
-webkit-appearance: textfield;
outline-offset: -2px;
}
[type="tel"],
[type="url"],
[type="email"],
[type="number"] {
direction: ltr;
}
::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-color-swatch-wrapper {
padding: 0;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
::file-selector-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
iframe {
border: 0;
}
summary {
display: list-item;
cursor: pointer;
}
progress {
vertical-align: baseline;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -21,44 +21,11 @@ const Connections = () => {
return (
<div>
<h2>Подключения</h2>
<h2>Настройки</h2>
{/* Webhook подключение */}
{/* <div className="section">
<h3>Подключения Webhooks</h3>
<form onSubmit={handleWebhookSubmit}>
<input type="text" name="webhookUrl" placeholder="URL Webhook" required />
<input type="text" name="groups" placeholder="Группы (через запятую)" required />
<button type="submit">Добавить Webhook</button>
</form>
<ul>
{webhooks.map((webhook, index) => (
<li key={index}>
Webhook: {webhook.url} (Группы: {webhook.groups.join(', ')})
</li>
))}
</ul>
</div> */}
{/* WebSocket подключение
<div className="section">
<h3>Подключения WebSockets</h3>
<form
onSubmit={(e) => {
e.preventDefault();
setWebsocketUrl(e.target.websocketUrl.value);
}}
>
<input type="text" name="websocketUrl" placeholder="URL WebSocket" required />
<button type="submit">Подключиться</button>
</form>
{websocketUrl && <p>Подключено по WebSocket: {websocketUrl}</p>}
</div> */}
{/* WebSocket подключение */}
<div className="section">
<h3>Настройки уведомлений</h3>
<h5>Уведомления</h5>
<form
onSubmit={(e) => {
e.preventDefault();

View File

@ -0,0 +1,29 @@
.background_color {
background: var(--bs-border-color);
}
.coler-border{
border: 5px solid #2c3e50;
background: #5b7996;
padding: 20px;
}
.context {
display: flex;
flex-direction: column;
flex-flow: column;
}
.context li {
color: aliceblue;
}
.context .dot {
width: 10px; /* Ширина точки */
height: 10px; /* Высота точки */
background-color: blue; /* Синий цвет */
border-radius: 50%; /* Скругляем углы до круга */
margin-bottom: 10px;
}
.context .btn {
background-color: rgba(91, 100, 185, 0.34);
}

View File

@ -1,65 +1,63 @@
import React, { useEffect, useRef, useState } from 'react';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { FontLoader } from 'three/examples/jsm/loaders/FontLoader';
import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry';
import Drone from './model/Drone';
import HeightPoint from './model/HeightPoint';
import { RandomHeightPoint } from './helpers/generateRandomHeightPoints';
// class HeightPoint {
// constructor(x, y, height) {
// this.x = x;
// this.y = y;
// this.height = height;
// }
// }
import { RandomHeightPoint } from './helpers/generateRandomHeightPoints'
import ModalWindow from './components/Modal/Modal';
import './Dashboard.css';
// import { NavBar } from './components/Navbar/Navbar';
const Dashboard = () => {
const mountRef = useRef(null);
const sceneRef = useRef(null);
const [state, setState] = useState({
heightData: RandomHeightPoint(),
drones: [],
selectedDrone: null,
baseStation: [],
selectedBaseStation: null,
formData: { x: '', y: '', height: '' },
mapSettings: { maxHeight: 20, coordX: 0, coordY: 0 },
contextMenu: { visible: false, x: 0, y: 0 },
mouseState: { x: 0, y: 0, z: 0 },
});
const mouse = new THREE.Vector2();
const mountRef = useRef(null); // Указатель монтирования
const sceneRef = useRef(null); // Указатель на сцену
const mouse = new THREE.Vector2(); // Мышка
const [heightData, setHeightData] = useState(RandomHeightPoint()); // Высоты
const [formData, setFormData] = useState({ x: '', y: '', height: '' }); // Форма для ввода данных карты
const [mouseState, setMouseState] = useState({ x: 0, y: 0, z: 0 }); // Форма для ввода данных карты
const [drones, setDrones] = useState([]) // Все дроны
const [selectedDrone, setSelectedDrone] = useState(null); // Состояние для выбранного дрона
const [baseStation, setBaseStation] = useState([]) // Все базовые станции
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 }); // контекстное меню сцены
useEffect(() => {
if (!mountRef.current) return;
let width = mountRef.current.clientWidth;
let height = mountRef.current.clientHeight;
// Создание сцены и камеры
// Создаем объект сцены
const scene = new THREE.Scene();
// Сохраняем ссылку на сцену
sceneRef.current = scene;
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
camera.position.set(0, 20, 30);
// Создаем камеру
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);
// Плоскость для высотной карты
const geometry = new THREE.PlaneGeometry(20, 20, 4, 4);
// Плоскость для высотной карты :: 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 { heightData, mapSettings } = state;
const minHeight = Math.min(...heightData.map(point => point.height));
const maxHeight = Math.max(mapSettings.maxHeight, ...heightData.map(point => point.height));
// Задание высот и цветов вершин
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 heightPoint = heightData.find((point) => point.x === x && point.y === y);
const heightValue = heightPoint ? heightPoint.height : 0;
vertices[i + 2] = heightValue;
@ -68,6 +66,7 @@ const Dashboard = () => {
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({
@ -80,146 +79,277 @@ const Dashboard = () => {
mesh.rotation.x = -Math.PI / 2;
scene.add(mesh);
const axesHelper = new THREE.AxesHelper(10);
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) => {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
console.log('Mouse coordinates:', mouse);
const raycaster = new THREE.Raycaster();
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 => drone.getObject().children[0] === selected);
if (drone) {
if (selectedDrone) {
selectedDrone.getObject().children[0].material.color.set(0xff0000);
}
drone.getObject().children[0].material.color.set(0xff1111);
setSelectedDrone(drone);
}
} else {
if (selectedDrone) {
selectedDrone.getObject().children[0].material.color.set(0xff0000);
setSelectedDrone(null);
}
}
};
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);
};
animate();
// События мыши
const handleClick = (event) => {
const { drones, selectedDrone } = state;
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(drones.map(drone => drone.getObject()), true);
if (intersects.length > 0) {
const selected = intersects[0].object;
const drone = drones.find(drone => drone.getObject().children[0] === selected);
if (drone) {
if (selectedDrone) selectedDrone.getObject().children[0].material.color.set(0xff0000);
drone.getObject().children[0].material.color.set(0xff1111);
setState(prevState => ({ ...prevState, selectedDrone: drone }));
}
} else if (selectedDrone) {
selectedDrone.getObject().children[0].material.color.set(0xff0000);
setState(prevState => ({ ...prevState, selectedDrone: null }));
}
};
renderer.domElement.addEventListener('click', handleClick);
renderer.domElement.addEventListener('mousemove', (event) => {
setState(prevState => ({
...prevState,
mouseState: { x: event.clientX, y: event.clientY, z: 0 }
}));
renderer.domElement.addEventListener('mousemove', handleMouseMove);
animate();
drones.forEach(droneData => {
scene.add(droneData.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);
}
};
}, [state.drones, state.heightData, state.mapSettings]);
}, [drones, heightData, mapSettings]);
// Добавление дрона в сцену
const handleAddDrone = () => {
const { drones } = state;
const drone = new Drone(Date.now() / 1000);
const droneMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
drone.getObject().children[0].material = droneMaterial;
drone.setPosition(Math.random() * 20 - 10, 3, Math.random() * 20 - 10);
setState(prevState => ({
...prevState,
drones: [...prevState.drones, drone]
}));
sceneRef.current.add(drone.getObject());
if (sceneRef.current) {
const current = Date.now();
const drone = new Drone(current / 1000);
// Создаем новый материал для каждого дрона
drone.setPosition(Math.random() * 20 - 10, 20, Math.random() * 20 - 10);
setDrones((prevDrones) => [...prevDrones, drone]);
console.log(drones.map(drone => drone.getObject().children[0]));
sceneRef.current.add(drone.getObject());
}
};
const handleInputChange = (event) => {
const { name, value } = event.target;
setState(prevState => ({
...prevState,
formData: { ...prevState.formData, [name]: value }
}));
};
// Обработчик ввода на форму
// const handleInputChange = (event) => {
// const { name, value } = event.target;
// setFormData({
// ...formData,
// [name]: value,
// });
// };
const handleAddHeight = (event) => {
event.preventDefault();
const { formData, heightData } = state;
const newPoint = new HeightPoint(parseInt(formData.x), parseInt(formData.y), parseFloat(formData.height));
setState(prevState => ({
...prevState,
heightData: [...prevState.heightData, newPoint],
formData: { x: '', y: '', height: '' }
}));
};
// Добавление элемента карты высот (высоты)
// const handleAddHeight = (event) => {
// event.preventDefault();
// const { x, y, height } = formData;
// const newPoint = new HeightPoint(parseInt(x), parseInt(y), parseFloat(height));
// setHeightData((prevData) => [...prevData, newPoint]);
// setFormData({ x: '', y: '', height: '' });
// };
const handleSettingsChange = (event) => {
const { name, value } = event.target;
setState(prevState => ({
...prevState,
mapSettings: { ...prevState.mapSettings, [name]: value }
}));
};
// Изменение настроек
// const handleSettingsChange = (event) => {
// const { name, value } = event.target;
// setMapSettings({
// ...mapSettings,
// [name]: value,
// });
// };
// Открытие контекстного меню
const handleContextMenuClick = (action) => {
if (action === 'add') handleAddDrone();
setState(prevState => ({
...prevState,
contextMenu: { ...prevState.contextMenu, visible: false }
}));
if (action === 'add') {
handleAddDrone();
console.log('Добавить объект');
} else if (action === 'save') {
console.log('Сохранить промежуточный результат');
}
setContextMenu({ ...contextMenu, visible: false });
};
// IS LOADING
// <div class="spinner-border text-primary" role="status">
// <span class="visually-hidden">Loading...</span>
// </div>
return (
<div style={{ flex: 1 }}>
<h1>Drone Network Simulator</h1>
<div>
<p>x: {state.mouseState.x} y: {state.mouseState.y} z: {state.mouseState.z}</p>
<div ref={mountRef} style={{ width: '100%', height: '500px', position: 'relative' }} />
<div className='col'>
{/* TODO: USERS BADGE */}
<h4><b>Drone</b> Network Simulator - окно разработки</h4>
</div>
{state.contextMenu.visible && (
<ul style={{ position: 'absolute', top: `${state.contextMenu.y}px`, left: `${state.contextMenu.x}px`, backgroundColor: 'white', padding: '10px', zIndex: 1000 }}>
<li className="button" onClick={() => handleContextMenuClick('add')}>Добавить объект</li>
<li className="button" onClick={() => handleContextMenuClick('save')}>Сохранить промежуточный результат</li>
<li className="button" onClick={() => handleContextMenuClick('cancel')}>Отмена</li>
{/* <NavBar/> */}
<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 class="btn-toolbar" role="toolbar" aria-label="Toolbar with button groups">
<div class="btn-group me-2" role="group" aria-label="First group">
<button type="button" class="btn btn-success">
<i class="bi-plus"/>Добавить
</button>
<button type="button" class="btn btn-success">
<i class="bi-trash"/>Удалить
</button>
<button type="button" class="btn btn-success">
<i class="bi-search"/>Выбрать
</button>
<button type="button" class="btn btn-success">
<i class="bi-save"/>Сохранить
</button>
</div>
<div class="btn-group me-2" role="group" aria-label="Second group">
<button type="button" class="btn btn-secondary">
<i class="bi-box-arrow-in-down"/>Загрузить данные
</button>
<button type="button" class="btn btn-secondary">
<i class="bi-filetype-json"/>Выгрузить
</button>
<button type="button" class="btn btn-secondary">
<i class="bi-map"/>Настройки карты
</button>
{/* <button type="button" class="btn btn-secondary">6</button> */}
</div>
<div class="btn-group" role="group" aria-label="Third group">
<button type="button" class="btn btn-info">
<i class="bi-play"/>
</button>
</div>
</div>
</div>
{/* Контекстное меню */}
{contextMenu.visible && (
<ul
style={{
position: 'absolute',
top: `${contextMenu.y}px`,
left: `${contextMenu.x}px`,
backgroundColor: 'transparent',
listStyle: 'none',
padding: '2px',
boxShadow: '0px 0px 5px rgba(0,0,0,0.3)',
zIndex: 1000,
}}
>
<div className='context'>
<div class="dot"></div>
<li className={"btn"} onClick={() => handleContextMenuClick('add')}>Добавить объект</li>
<li className={"btn"} onClick={() => handleContextMenuClick('save')}>Сохранить промежуточный результат</li>
<li className={"btn"} onClick={() => handleContextMenuClick('cancel')}>Отмена</li>
</div>
</ul>
)}
<div className="columns">
<div className='column'>
<form onSubmit={handleAddHeight} style={{ marginTop: '20px' }}>
<label>Координата X:</label>
<input className="input" type="number" name="x" value={state.formData.x} onChange={handleInputChange} />
<label>Координата Y:</label>
<input className="input" type="number" name="y" value={state.formData.y} onChange={handleInputChange} />
<label>Высота:</label>
<input className="input" type="number" name="height" value={state.formData.height} onChange={handleInputChange} />
<button type="submit" className="button">Добавить координаты</button>
</form>
</div>
<div className="column">
<form style={{ marginTop: '20px' }}>
<label>Максимальная высота</label>
<input className="input" type="number" name="maxHeight" value={state.mapSettings.maxHeight} onChange={handleSettingsChange} />
<label>Координата X</label>
<input className="input" type="number" name="coordX" value={state.mapSettings.coordX} onChange={handleSettingsChange} />
<label>Координата Y</label>
<input className="input" type="number" name="coordY" value={state.mapSettings.coordY} onChange={handleSettingsChange} />
<button type="submit" className="button">Применить</button>
</form>
</div>
{/* Форма для добавления новых высот */}
{/* <div className='column'>
<form onSubmit={handleAddHeight} style={{ marginTop: '20px' }}>
<div>
<label>Координата X:</label>
<input
className="input is-primary"
type="number"
name="x"
value={formData.x}
onChange={handleInputChange}
required
/>
</div>
<div>
<label>Координата Y:</label>
<input
className="input is-primary"
type="number"
name="y"
value={formData.y}
onChange={handleInputChange}
required
/>
</div>
<div>
<label>Высота:</label>
<input
className="input is-primary"
type="number"
name="height"
value={formData.height}
onChange={handleInputChange}
required
/>
</div>
<button className='button' 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>
</div>
</div> */}
</div>
<button className="button is-danger" onClick={handleAddDrone}>Добавить дрон</button>
</div>
);
};

View File

@ -50,7 +50,13 @@ const DeviceGroups = () => {
onChange={(e) => setNewGroupName(e.target.value)}
placeholder="Название шаблона"
/>
<button onClick={createGroup}>Создать шаблон</button>
<input
type="text"
value={newGroupName}
onChange={(e) => setNewGroupName(e.target.value)}
placeholder="JSON Template"
/>
<button className='btn btn-success' onClick={createGroup}>Создать шаблон</button>
</div>
<ul>

View File

@ -3,13 +3,13 @@ import React from 'react';
const Docs = () => {
return (
<div className="block">
<div class="block">
<div className="block">
Документация <strong>Drone Network Simulator</strong>.
</div>
<div class="block">
<div className="block">
created by <strong>@moxitech</strong>
</div>
<div class="block">
<div className="block">
Для начала работы необходимо войти в систему
</div>
</div>

View File

@ -1,34 +1,59 @@
import React from 'react';
import '../Main.css';
import { InfoBlock } from './components/DroneAssets/InfoBlock';
import React, { useEffect, useState } from 'react';
import '../css/Custom/Main.css';
import { ServerRequest } from '../Services/Server';
const Main = () => {
const [rooms, setRooms] = useState(null)
const makeSimulation = () => {
useEffect(() => {
connectSimulation()
}, [])
const createSimulation = () => {
// POST $BASE_ADDR/createSimulation?userToken=?
}
const connectSimulation = () => {
// POST $BASE_ADDR/connectSimulation?userToken=?
ServerRequest("/simulations/active", "GET").then( x => {
console.log(x.data, x.status)
if (x.status === 200) {
setRooms(x.data.rooms_ids)
} else {
setRooms(null)
}
})
}
return (
<div className='main__imager'>
<InfoBlock />
<div class="columns ">
<div class="column">
<h1 class="title">Создать новую симуляцию</h1>
<button class="button">Create</button>
</div>
<div class="column">
<h1 class="title">Присоединиться к моделированию</h1>
<div>
<p>Расчет полета до камчатки</p>
<button class="button">Connect</button>
<div className='main__imager' style={{marginTop: "2%"}}>
<div className="row align-items-start">
<div className="col">
<h1 className="title">Создать новую симуляцию</h1>
<hr/>
<button className="btn btn-primary">Create</button>
</div>
<div className="col">
<h1 className="title">Присоединиться к моделированию</h1>
<hr/>
{rooms && rooms.length > 0 ? (
rooms.map(room => (
<div className='row' key={room}>
<div className='col'>
<h3>{room.name} | UUID : {room.uuid}</h3>
</div>
<div className='col'>
<button className="btn btn-primary">
Connect
</button>
</div>
</div>
))
) : (
<p>Пока никто не работает 😒</p>
)}
</div>
</div>
</div>
</div>
</div>
)
};

View File

@ -4,20 +4,30 @@ import '../UserAccount.scss';
const PrevCalc = () => {
return (
<table class="table">
<table className="table">
<thead>
<th><abbr title="Уникальный идентификатор в базе данных">UID</abbr></th>
<th><abbr title="Название">Имя</abbr></th>
<th>Пользователь</th>
<th>Время начала</th>
<th>Время конца</th>
<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>
<th>1</th>
<td>Калькуляция полета до варшавы</td>
<td>Гитлер</td>
<td>1:00</td>
<td>4:04</td>
<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>
)

View File

@ -1,35 +0,0 @@
import React from "react"
export const InfoBlock = () => {
return(
<nav className="level">
<div className="level-item has-text-centered">
<div>
<p className="heading">Активных сессий</p>
<p className="title">0</p>
</div>
</div>
<div className="level-item has-text-centered">
<div>
<p className="heading">Горутин</p>
<p className="title">123</p>
</div>
</div>
<div className="level-item has-text-centered">
<div>
<p className="heading">Нагрузка сервера</p>
<p className="title">456K</p>
</div>
</div>
<div className="level-item has-text-centered">
<div>
<p className="heading">Объем хранилища</p>
<p className="title">789</p>
</div>
</div>
</nav>
)
}

View File

@ -0,0 +1 @@
{"accessors":[],"asset":{"generator":"Open CASCADE Technology 7.6 [dev.opencascade.org]","version":"2.0"},"bufferViews":[],"buffers":[{"byteLength":0,"uri":"drone_normalized_gltf.bin"}],"meshes":[],"nodes":[],"scene":0,"scenes":[{"nodes":[]}]}

View File

@ -0,0 +1,13 @@
.m_size_fixed {
color: antiquewhite;
width: 200px;
height: 200px;
}
.m_black_color {
color: black;
}
.m_font_scale_32 {
font-size: 32px;
}

View File

@ -0,0 +1,18 @@
import React, { useState } from 'react';
import Modal from 'react-modal';
import './Modal.css'
const ModalWindow = ({ isOpen, closeModal, openModal, title }) => {
return (
<div className=''>
<Modal className={"m_size_fixed"} isOpen={isOpen} onRequestClose={closeModal}>
<h2 className='m_black_color m_font_scale_32'>Это модальное окно!</h2>
<button className='button is-primary' onClick={closeModal}>Закрыть</button>
</Modal>
</div>
);
}
export default ModalWindow;

View File

@ -0,0 +1,10 @@
import React from "react"
export const NavBar = () => {
return(
<p></p>
)
}

View File

@ -2,8 +2,8 @@ import HeightPoint from '../model/HeightPoint';
export const RandomHeightPoint = () => {
let result = [];
for (let i = 0; i < 20; i++) {
for (let j = 0; j < 20; j++) {
for (let i = 0; i < 100; i++) {
for (let j = 0; j < 100; j++) {
result.push(new HeightPoint(i, j, Math.random() * 10 + 1));
}
}

View File

@ -1,14 +1,20 @@
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
class Drone {
constructor(name) {
constructor(name, scale = 0.5) {
this.object = new THREE.Object3D();
this.geometry = new THREE.BoxGeometry(1, 1, 1);
this.material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
this.name = name;
const cube = new THREE.Mesh(this.geometry, this.material);
// cube.material.M = new THREE.Color(0x000000); // Инициализируем с базовым эмиссивным цветом
this.object.add(cube);
// Инициализируем GLTFLoader
const loader = new GLTFLoader();
// Загружаем модель из файла GLTF
loader.load("/ImageToStl.com_drone.glb", (gltf) => {
this.object.add(gltf.scene); // Добавляем сцену из GLTF к объекту дрона
gltf.scene.scale.set(scale, scale, scale); // Применяем масштаб
}, undefined, (error) => {
console.error("Ошибка загрузки GLTF:", error);
});
}
setPosition(x, y, z) {
@ -18,8 +24,7 @@ class Drone {
getObject() {
return this.object;
}
}
export default Drone;

View File

@ -37,6 +37,78 @@ func (o *Modulation) DeleteObjectIfExists(name string) {
}
}
}
func (o *Modulation) MirrorPayloadToSimulationStruct() {
}
func (o *Modulation) RunSimulator() {
// // Пример карты высот, замените на настоящие данные
// heightData := [][]float64{
// {0, 10, 15, 20},
// {5, 15, 25, 30},
// {10, 20, 35, 40},
// {15, 25, 40, 50},
// }
// // Определяем карту высот
//
// mapObj := &simulator.Map{
// Name: "Example Map",
// MinBound: [3]float64{-1000, -1000, 0},
// MaxBound: [3]float64{1000, 1000, 500},
// HeightData: heightData,
// }
//
// // Определяем дронов
//
// drones := []*simulator.Drone{
// {
// ID: 1,
// Name: "Drone 1",
// Coords: [3]float64{100, 100, 50},
// Params: simulator.DroneParams{
// AntennaRadius: 500,
// AntennaDirection: [3]float64{1, 0, 0},
// Waypoints: [][3]float64{{200, 200, 50}},
// Speed: 10,
// MeshName: "MeshA",
// },
// },
// }
//
// // Определяем базовые станции
//
// baseStations := []*simulator.BaseStation{
// {
// ID: 1,
// Name: "BaseStation1",
// Coords: [3]float64{0, 0, 0},
// Params: simulator.BaseStationParams{
// AntennaRadius: 2000,
// AntennaDirection: [3]float64{1, 0, 0},
// },
// },
// }
//
// // Создаем симуляцию
//
// sim := &simulator.NetworkSimulation{
// Map: mapObj,
// Drones: drones,
// BaseStations: baseStations,
// TimeStep: 2,
// }
//
// // Запуск симуляции на 300 секунд
// result_sim := sim.Simulate(300)
// result := u_sorting.SortMap(result_sim)
//
// for time, state := range result {
// for localstate, f := range state {
// fmt.Printf("%v| %v sec :: %v\n", time, localstate, f)
// }
// }
}
// SpawnExampleData создает случайные данные и перезаписывает карту
func (o *Modulation) SpawnExampleData() bool {

View File

@ -11,6 +11,7 @@ import (
"time"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/websocket/v2"
)
@ -20,6 +21,10 @@ var (
func SpawnServer() error {
app := fiber.New()
app.Use(cors.New(cors.Config{
AllowHeaders: "*",
AllowOrigins: "*",
}))
go DispatchRooms()
Rooms = make(map[string]*WebsocketRoom)
app.Get("/", func(c *fiber.Ctx) error {

View File

@ -216,11 +216,12 @@ func (room *WebsocketRoom) InsertMapFromMongo(name string) {
}
// RunSimulation запускает симуляцию в горутине
func (room *WebsocketRoom) RunSimulation(name string) {
func (room *WebsocketRoom) RunSimulation(name string) (int, error) {
// TODO +NormalizeDataForSimulation()
roomsMutex.Lock()
defer roomsMutex.Unlock()
room.Modulation.DeleteObjectIfExists(name)
room.Modulation.MirrorPayloadToSimulationStruct()
return -1, nil
}
// NormalizeDataForSimulation - Преобразование данных для отправки в симуляцию

View File

@ -83,11 +83,12 @@ func (sim *NetworkSimulation) Simulate(totalTime int) map[int]map[string][]inter
// Проверка соединения с базовой станцией
if !IsDroneInNetwork(d, sim.BaseStations, sim.Map) {
// Если дрон имеет mesh-сеть, ищем другой дрон для ретрансляции
closestDrone := FindClosestDroneToBaseStation(d, sim.Drones, sim.BaseStations[0], sim.Map)
if closestDrone != nil {
// Дрон может передавать данные через другой дрон
// Логику передачи можно добавить сюда
}
FindClosestDroneToBaseStation(d, sim.Drones, sim.BaseStations[0], sim.Map)
// closestDrone :=
// if closestDrone != nil {
// Дрон может передавать данные через другой дрон
// Логику передачи можно добавить сюда
// }
}
// Добавляем данные о дроне в текущее состояние
@ -243,6 +244,7 @@ func CalculateDataRate(modulation string, bandwidth float64) float64 {
return spectralEfficiency * bandwidth // скорость передачи данных в Mbps
}
// MakeExampleMap создает статическую карту
func MakeExampleMap() Map {
heightData := [][]float64{
{0, 10, 15, 20},