From 7914e6b7e5f711f2deffc2ca09d1f5a20a6ec37f Mon Sep 17 00:00:00 2001 From: Maxim Zaugolnikov Date: Wed, 1 Oct 2025 11:48:38 +0700 Subject: [PATCH 01/34] rename --- {YOUR_TEAM_NAME => Yadershiki}/standart.beacons | 0 {YOUR_TEAM_NAME => Yadershiki}/standart.path | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {YOUR_TEAM_NAME => Yadershiki}/standart.beacons (100%) rename {YOUR_TEAM_NAME => Yadershiki}/standart.path (100%) diff --git a/YOUR_TEAM_NAME/standart.beacons b/Yadershiki/standart.beacons similarity index 100% rename from YOUR_TEAM_NAME/standart.beacons rename to Yadershiki/standart.beacons diff --git a/YOUR_TEAM_NAME/standart.path b/Yadershiki/standart.path similarity index 100% rename from YOUR_TEAM_NAME/standart.path rename to Yadershiki/standart.path From 422043a0d35438efc08727c96a083f1dbb8bcd8c Mon Sep 17 00:00:00 2001 From: Matvey Solovev Date: Wed, 1 Oct 2025 13:24:21 +0700 Subject: [PATCH 02/34] Initial commit --- Yadershiki/README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 Yadershiki/README.md diff --git a/Yadershiki/README.md b/Yadershiki/README.md new file mode 100644 index 0000000..d5419c9 --- /dev/null +++ b/Yadershiki/README.md @@ -0,0 +1 @@ +# Indoor navigation system From c2a7a89e7c6e100f469816b86f412aab1e329ba4 Mon Sep 17 00:00:00 2001 From: Klim Sadov Date: Wed, 1 Oct 2025 13:51:09 +0700 Subject: [PATCH 03/34] add plate.py --- Yadershiki/src/esp32/plate.py | 36 +++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 Yadershiki/src/esp32/plate.py diff --git a/Yadershiki/src/esp32/plate.py b/Yadershiki/src/esp32/plate.py new file mode 100644 index 0000000..b99070b --- /dev/null +++ b/Yadershiki/src/esp32/plate.py @@ -0,0 +1,36 @@ +import network +import time + +wlan = network.WLAN(network.STA_IF) +wlan.active(True) + +if not wlan.isconnected(): + print("Пытаемся подключиться") + wlan.connect('vaidy31', '88888888') + + for i in range(10): + if wlan.isconnected(): + break + print('Ожидание...') + time.sleep(1) + +if wlan.isconnected(): + print("Мы смогли. Ура") + print('Сетевые настройки:', wlan.ifconfig()) + + try: + import urequests + print("urequests доступен") + + ip_address = "192.168.3.26:8080" + response = urequests.get(f"http://{ip_address}") + print("Status:", response.status_code) + print("Response:", response.text) + response.close() + + except ImportError: + print("urequests не установлен") + +else: + print("Не удалось подключиться") + From d9d42d1f7f164960173cdd0ee9fcc4f1706553db Mon Sep 17 00:00:00 2001 From: Matvey Solovev Date: Wed, 1 Oct 2025 13:55:27 +0700 Subject: [PATCH 04/34] Implement server template --- Yadershiki/src/server/.gitignore | 16 + Yadershiki/src/server/index.html | 11 + Yadershiki/src/server/package-lock.json | 895 ++++++++++++++++++++++++ Yadershiki/src/server/package.json | 7 + Yadershiki/src/server/server.js | 14 + 5 files changed, 943 insertions(+) create mode 100644 Yadershiki/src/server/.gitignore create mode 100644 Yadershiki/src/server/index.html create mode 100644 Yadershiki/src/server/package-lock.json create mode 100644 Yadershiki/src/server/package.json create mode 100644 Yadershiki/src/server/server.js diff --git a/Yadershiki/src/server/.gitignore b/Yadershiki/src/server/.gitignore new file mode 100644 index 0000000..6d2c041 --- /dev/null +++ b/Yadershiki/src/server/.gitignore @@ -0,0 +1,16 @@ +# Dependencies +node_modules/ + +# Environment variables +.env + +# Logs +*.log +logs/ + +# OS files +.DS_Store + +# Build outputs +dist/ +build/ diff --git a/Yadershiki/src/server/index.html b/Yadershiki/src/server/index.html new file mode 100644 index 0000000..75ebbf2 --- /dev/null +++ b/Yadershiki/src/server/index.html @@ -0,0 +1,11 @@ + + + + + + Yaderщики + + +

Hello World!

+ + diff --git a/Yadershiki/src/server/package-lock.json b/Yadershiki/src/server/package-lock.json new file mode 100644 index 0000000..77727c8 --- /dev/null +++ b/Yadershiki/src/server/package-lock.json @@ -0,0 +1,895 @@ +{ + "name": "server", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "dotenv": "^17.2.3", + "express": "^5.1.0", + "path": "^0.12.7" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/body-parser/node_modules/raw-body": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", + "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.7.0", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/body-parser/node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", + "license": "MIT", + "dependencies": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/router/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/router/node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "license": "MIT", + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "license": "ISC" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + } + } +} diff --git a/Yadershiki/src/server/package.json b/Yadershiki/src/server/package.json new file mode 100644 index 0000000..0b852fc --- /dev/null +++ b/Yadershiki/src/server/package.json @@ -0,0 +1,7 @@ +{ + "dependencies": { + "dotenv": "^17.2.3", + "express": "^5.1.0", + "path": "^0.12.7" + } +} diff --git a/Yadershiki/src/server/server.js b/Yadershiki/src/server/server.js new file mode 100644 index 0000000..086662b --- /dev/null +++ b/Yadershiki/src/server/server.js @@ -0,0 +1,14 @@ +require('dotenv').config(); +const port = process.env.PORT; + +const path = require('path'); +const express = require('express'); +const app = express(); + +app.get('/', (req, res) => { + res.sendFile(path.join(__dirname, 'index.html')); +}); + +app.listen(port, () => { + console.log(`Starting server on ${port}`); +}); From 697190109d5c67cab1ef062f6a954a10994d21f5 Mon Sep 17 00:00:00 2001 From: vaidy31 <656mpx@gmail.com> Date: Wed, 1 Oct 2025 15:29:56 +0700 Subject: [PATCH 05/34] added dockerfile --- Yadershiki/src/server/Dockerfile | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Yadershiki/src/server/Dockerfile diff --git a/Yadershiki/src/server/Dockerfile b/Yadershiki/src/server/Dockerfile new file mode 100644 index 0000000..a4f6b42 --- /dev/null +++ b/Yadershiki/src/server/Dockerfile @@ -0,0 +1,20 @@ +# Используем официальный образ Node.js +FROM node:18-alpine + +# Устанавливаем рабочую директорию +WORKDIR /app + +# Копируем package.json и package-lock.json для установки зависимостей +COPY package*.json ./ + +# Устанавливаем зависимости +RUN npm install --production + +# Копируем весь исходный код в контейнер +COPY . . + +# Указываем порт, который будет использовать сервер +EXPOSE 8080 + +# Запускаем сервер +CMD ["node", "server.js"] From e14b979eb99898451058b3b29fcbf9898223ddb8 Mon Sep 17 00:00:00 2001 From: vaidy31 <656mpx@gmail.com> Date: Wed, 1 Oct 2025 15:31:02 +0700 Subject: [PATCH 06/34] added dockerfile --- Yadershiki/src/server/package-lock.json | 7 ++++++- Yadershiki/src/server/package.json | 13 ++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Yadershiki/src/server/package-lock.json b/Yadershiki/src/server/package-lock.json index 77727c8..f97861b 100644 --- a/Yadershiki/src/server/package-lock.json +++ b/Yadershiki/src/server/package-lock.json @@ -1,14 +1,19 @@ { "name": "server", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { + "name": "server", + "version": "1.0.0", + "license": "ISC", "dependencies": { "dotenv": "^17.2.3", "express": "^5.1.0", "path": "^0.12.7" - } + }, + "devDependencies": {} }, "node_modules/accepts": { "version": "2.0.0", diff --git a/Yadershiki/src/server/package.json b/Yadershiki/src/server/package.json index 0b852fc..00d9961 100644 --- a/Yadershiki/src/server/package.json +++ b/Yadershiki/src/server/package.json @@ -3,5 +3,16 @@ "dotenv": "^17.2.3", "express": "^5.1.0", "path": "^0.12.7" - } + }, + "name": "server", + "version": "1.0.0", + "description": "", + "main": "server.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node server.js" + }, + "keywords": [], + "author": "", + "license": "ISC" } From 30830b399ca3511a1b0330eb207f546cb9da5cda Mon Sep 17 00:00:00 2001 From: vaidy31 <656mpx@gmail.com> Date: Wed, 1 Oct 2025 16:22:59 +0700 Subject: [PATCH 07/34] adding docker-compose for container EMQX --- Yadershiki/src/server/Dockerfile | 12 +++---- Yadershiki/src/server/docker-compose.yml | 40 ++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 Yadershiki/src/server/docker-compose.yml diff --git a/Yadershiki/src/server/Dockerfile b/Yadershiki/src/server/Dockerfile index a4f6b42..336ad75 100644 --- a/Yadershiki/src/server/Dockerfile +++ b/Yadershiki/src/server/Dockerfile @@ -1,7 +1,7 @@ -# Используем официальный образ Node.js +# образ node.js FROM node:18-alpine -# Устанавливаем рабочую директорию +# рабочая директория WORKDIR /app # Копируем package.json и package-lock.json для установки зависимостей @@ -10,11 +10,11 @@ COPY package*.json ./ # Устанавливаем зависимости RUN npm install --production -# Копируем весь исходный код в контейнер +# копируем весь исходный код в контейнер COPY . . -# Указываем порт, который будет использовать сервер +# Порт где будет сервер EXPOSE 8080 -# Запускаем сервер -CMD ["node", "server.js"] +# запуск сервера +CMD ["node", "server.js"] \ No newline at end of file diff --git a/Yadershiki/src/server/docker-compose.yml b/Yadershiki/src/server/docker-compose.yml new file mode 100644 index 0000000..bd600da --- /dev/null +++ b/Yadershiki/src/server/docker-compose.yml @@ -0,0 +1,40 @@ +version: '3.8' + +services: + #Express.js сервер + app: + build: + context: . # путь к Dockerfile + dockerfile: Dockerfile # Dockerfile с Node.js + container_name: my-app + ports: + - "8080:8080" # сервер на порту 8080 + environment: + - NODE_ENV=production + - MQTT_HOST=emqx # используем имя сервиса для подключения к EMQX + - MQTT_PORT=1883 + depends_on: + - emqx # гарантируем, что EMQX запустится первым + restart: unless-stopped + volumes: + - ./:/app # для разработки: монтируем код для hot-reload + - /app/node_modules # исключаем node_modules из монтирования + + # MQTT брокер EMQX + emqx: + image: emqx/emqx:5.4 + container_name: emqx + ports: + - "1883:1883" # MQTT порт + - "8083:8083" # MQTT over WebSocket + - "8084:8084" # MQTT over WebSocket SSL + - "8883:8883" # MQTT SSL + - "8081:18083" # Web Dashboard + environment: + - EMQX_NODE_NAME=emqx@localhost + restart: unless-stopped + +# Сеть для общения между контейнерами +networks: + default: + name: app-network \ No newline at end of file From 1861a5c1106d5a325bb245e82beaab396505b857 Mon Sep 17 00:00:00 2001 From: Matvey Solovev Date: Wed, 1 Oct 2025 17:02:34 +0700 Subject: [PATCH 08/34] Implement SSE --- Yadershiki/src/server/package-lock.json | 91 ++++++++++++++++++++++++- Yadershiki/src/server/package.json | 4 ++ Yadershiki/src/server/server.js | 62 +++++++++++++++-- 3 files changed, 151 insertions(+), 6 deletions(-) diff --git a/Yadershiki/src/server/package-lock.json b/Yadershiki/src/server/package-lock.json index f97861b..3999cea 100644 --- a/Yadershiki/src/server/package-lock.json +++ b/Yadershiki/src/server/package-lock.json @@ -9,11 +9,13 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "body-parser": "^2.2.0", + "compression": "^1.8.1", + "cors": "^2.8.5", "dotenv": "^17.2.3", "express": "^5.1.0", "path": "^0.12.7" - }, - "devDependencies": {} + } }, "node_modules/accepts": { "version": "2.0.0", @@ -138,6 +140,51 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/content-disposition": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", @@ -177,6 +224,19 @@ "node": ">=6.6.0" } }, + "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/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -542,6 +602,24 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -566,6 +644,15 @@ "node": ">= 0.8" } }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", diff --git a/Yadershiki/src/server/package.json b/Yadershiki/src/server/package.json index 00d9961..64cb60c 100644 --- a/Yadershiki/src/server/package.json +++ b/Yadershiki/src/server/package.json @@ -1,10 +1,14 @@ { "dependencies": { + "body-parser": "^2.2.0", + "compression": "^1.8.1", + "cors": "^2.8.5", "dotenv": "^17.2.3", "express": "^5.1.0", "path": "^0.12.7" }, "name": "server", + "type": "module", "version": "1.0.0", "description": "", "main": "server.js", diff --git a/Yadershiki/src/server/server.js b/Yadershiki/src/server/server.js index 086662b..989162d 100644 --- a/Yadershiki/src/server/server.js +++ b/Yadershiki/src/server/server.js @@ -1,14 +1,68 @@ -require('dotenv').config(); +import dotenv from 'dotenv'; +import express from 'express'; +import path from 'path'; +import compression from 'compression'; +import bodyParser from 'body-parser'; +import cors from 'cors'; + +dotenv.config(); const port = process.env.PORT; -const path = require('path'); -const express = require('express'); const app = express(); +app.use(compression()); +app.use(cors()); +app.use(bodyParser.json()); + +let clients = []; +let state = []; app.get('/', (req, res) => { - res.sendFile(path.join(__dirname, 'index.html')); + res.sendFile(path.join(import.meta.dirname, 'index.html')); }); +app.get('/beacons', (req, res) => { + const headers = { + 'Content-Type': 'text/event-stream', + 'Access-Control-Allow-Origin': '*', + 'Connection': 'keep-alive', + 'Cache-Control': 'no-cache' + }; + res.writeHead(200, headers); + + const sendData = `data: ${JSON.stringify(state)}\n\n`; + + res.write(sendData); + res.flush(); + + const clientId = genUniqueId(); + const newClient = { + id: clientId, + res, + }; + + clients.push(newClient); + + console.log(`${clientId} - Connection opened`); + + req.on('close', () => { + console.log(`${clientId} - Connection closed`); + clients = clients.filter(client => client.id !== clientId); + }); +}); + +function genUniqueId(){ + return Date.now() + '-' + Math.floor(Math.random() * 1000000000); +} + +function notifyClients() { + const sendData = `data: ${JSON.stringify(state)}\n\n`; + + for (let i = 0; i < clients.length; i++) { + clients[i].res.write(sendData); + clients[i].res.flush(); + } +} + app.listen(port, () => { console.log(`Starting server on ${port}`); }); From 670c763092a224283d6f31ad2f428277665b356f Mon Sep 17 00:00:00 2001 From: vaidy31 <656mpx@gmail.com> Date: Wed, 1 Oct 2025 17:20:17 +0700 Subject: [PATCH 09/34] fix __dirname --- Yadershiki/src/server/server.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Yadershiki/src/server/server.js b/Yadershiki/src/server/server.js index 989162d..197f64a 100644 --- a/Yadershiki/src/server/server.js +++ b/Yadershiki/src/server/server.js @@ -4,9 +4,12 @@ import path from 'path'; import compression from 'compression'; import bodyParser from 'body-parser'; import cors from 'cors'; +import { fileURLToPath } from 'url'; dotenv.config(); const port = process.env.PORT; +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); const app = express(); app.use(compression()); @@ -17,7 +20,7 @@ let clients = []; let state = []; app.get('/', (req, res) => { - res.sendFile(path.join(import.meta.dirname, 'index.html')); + res.sendFile(path.join(__dirname, 'index.html')); }); app.get('/beacons', (req, res) => { From 79a15d4063a09a755e3b941c2482a811ea75929d Mon Sep 17 00:00:00 2001 From: Klim Sadov Date: Wed, 1 Oct 2025 18:58:28 +0700 Subject: [PATCH 10/34] add file to connect beacons --- Yadershiki/src/esp32/beacon_connection.py | 54 +++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 Yadershiki/src/esp32/beacon_connection.py diff --git a/Yadershiki/src/esp32/beacon_connection.py b/Yadershiki/src/esp32/beacon_connection.py new file mode 100644 index 0000000..10b61b4 --- /dev/null +++ b/Yadershiki/src/esp32/beacon_connection.py @@ -0,0 +1,54 @@ +import bluetooth +from micropython import const +import time + +_IRQ_SCAN_RESULT = const(5) +_IRQ_SCAN_DONE = const(6) +_ADV_TYPE_NAME = const(0x09) # Complete Local Name + +ble = bluetooth.BLE() +ble.active(True) + +# словарь {имя: rssi} +beacons = {} +scan_done = False + +def decode_name(adv_data): + adv_bytes = bytes(adv_data) + i = 0 + while i + 1 < len(adv_bytes): + length = adv_bytes[i] + if length == 0: + break + type = adv_bytes[i + 1] + if type == _ADV_TYPE_NAME: + return adv_bytes[i + 2 : i + 1 + length].decode("utf-8") + i += 1 + length + return None + +def bt_irq(event, data): + global beacons, scan_done + if event == _IRQ_SCAN_RESULT: + addr_type, addr, adv_type, rssi, adv_data = data + name = decode_name(adv_data) + if name and name.startswith("beacon_"): + if name not in beacons or rssi > beacons[name]: + beacons[name] = rssi + elif event == _IRQ_SCAN_DONE: + scan_done = True + +def scan_once(duration_ms=5000): + """Запускает сканирование и возвращает список [имя, rssi], отсортированный по близости""" + global beacons, scan_done + beacons = {} + scan_done = False + ble.irq(bt_irq) + ble.gap_scan(duration_ms, 30000, 30000) + + # ждём окончания + while not scan_done: + time.sleep_ms(100) + + beacons_sorted = sorted(beacons.items(), key=lambda x: x[1], reverse=True) + result = [[name, rssi] for name, rssi in beacons_sorted] + return result From 0903dc51f4d35b1889869e004b9e82b89f3f7ffb Mon Sep 17 00:00:00 2001 From: Klim Sadov Date: Wed, 1 Oct 2025 19:58:25 +0700 Subject: [PATCH 11/34] some changes in beacon_connection.py --- Yadershiki/src/esp32/beacon_connection.py | 75 ++++++++++++++++++++--- 1 file changed, 68 insertions(+), 7 deletions(-) diff --git a/Yadershiki/src/esp32/beacon_connection.py b/Yadershiki/src/esp32/beacon_connection.py index 10b61b4..36c149c 100644 --- a/Yadershiki/src/esp32/beacon_connection.py +++ b/Yadershiki/src/esp32/beacon_connection.py @@ -1,15 +1,23 @@ import bluetooth from micropython import const import time +import network +from umqtt.simple import MQTTClient _IRQ_SCAN_RESULT = const(5) _IRQ_SCAN_DONE = const(6) -_ADV_TYPE_NAME = const(0x09) # Complete Local Name +_ADV_TYPE_NAME = const(0x09) + +_SCAN_FREQ = 100 + +MQTT_BROKER = "192.168.3.26" +MQTT_PORT = 1883 +MQTT_TOPIC = "beacons/rssi" +CLIENT_ID = "beacon_publisher_01" ble = bluetooth.BLE() ble.active(True) -# словарь {имя: rssi} beacons = {} scan_done = False @@ -38,17 +46,70 @@ def bt_irq(event, data): scan_done = True def scan_once(duration_ms=5000): - """Запускает сканирование и возвращает список [имя, rssi], отсортированный по близости""" global beacons, scan_done beacons = {} scan_done = False ble.irq(bt_irq) ble.gap_scan(duration_ms, 30000, 30000) - # ждём окончания while not scan_done: - time.sleep_ms(100) + time.sleep_ms(_SCAN_FREQ) beacons_sorted = sorted(beacons.items(), key=lambda x: x[1], reverse=True) - result = [[name, rssi] for name, rssi in beacons_sorted] - return result + return [[name, rssi] for name, rssi in beacons_sorted] + +def publish_beacons_data(beacons_data): + try: + client = MQTTClient(CLIENT_ID, MQTT_BROKER, port=MQTT_PORT) + client.connect() + + if not beacons_data: + data_str = "NO_BEACONS_FOUND" + print("Маяки не найдены, отправляем сообщение об отсутствии") + else: + data_str = str(beacons_data).replace("'", '"') + print(f"Найдено маяков: {len(beacons_data)}") + + client.publish(MQTT_TOPIC, data_str) + print(f"Опубликовано в топик {MQTT_TOPIC}: {data_str}") + + client.disconnect() + return True + + except Exception as e: + print(f"Ошибка публикации: {e}") + return False + +wlan = network.WLAN(network.STA_IF) +wlan.active(True) + +if not wlan.isconnected(): + print("Подключаемся к Wi-Fi...") + wlan.connect('vaidy31', '88888888') + + for i in range(10): + if wlan.isconnected(): + break + print('Ожидание...') + time.sleep(1) + +if wlan.isconnected(): + print("Успешно подключено к Wi-Fi") + print('IP адрес:', wlan.ifconfig()[0]) + + while True: + try: + # Сканируем маяки + beacons_data = scan_once(duration_ms=5000) + + # Публикуем данные (функция сама определит, есть маяки или нет) + publish_beacons_data(beacons_data) + + # Пауза между сканированиями + time.sleep(2) + + except Exception as e: + print(f"Ошибка: {e}") + time.sleep(5) +else: + print("Не удалось подключиться к Wi-Fi") \ No newline at end of file From 1154909414f6b0d1533125144cec8c34ffe822d8 Mon Sep 17 00:00:00 2001 From: vaidy31 <656mpx@gmail.com> Date: Thu, 2 Oct 2025 09:08:41 +0700 Subject: [PATCH 12/34] very unstable version but it should work --- Yadershiki/src/server/docker-compose.yml | 53 +- Yadershiki/src/server/package-lock.json | 757 +++++++++++++++++++- Yadershiki/src/server/package.json | 4 +- Yadershiki/src/server/server.js | 203 +++++- Yadershiki/src/server/skynet-mqtt-sender.js | 147 ++++ 5 files changed, 1117 insertions(+), 47 deletions(-) create mode 100644 Yadershiki/src/server/skynet-mqtt-sender.js diff --git a/Yadershiki/src/server/docker-compose.yml b/Yadershiki/src/server/docker-compose.yml index bd600da..5b4dddd 100644 --- a/Yadershiki/src/server/docker-compose.yml +++ b/Yadershiki/src/server/docker-compose.yml @@ -1,40 +1,59 @@ version: '3.8' services: - #Express.js сервер + # Основной сервер (получатель MQTT) app: build: - context: . # путь к Dockerfile - dockerfile: Dockerfile # Dockerfile с Node.js + context: . + dockerfile: Dockerfile container_name: my-app ports: - - "8080:8080" # сервер на порту 8080 + - "8080:8080" environment: - NODE_ENV=production - - MQTT_HOST=emqx # используем имя сервиса для подключения к EMQX + - MQTT_HOST=emqx # ← используем имя сервиса - MQTT_PORT=1883 + command: node server.js # ← явно указываем запуск основного сервера depends_on: - - emqx # гарантируем, что EMQX запустится первым + - emqx restart: unless-stopped - volumes: - - ./:/app # для разработки: монтируем код для hot-reload - - /app/node_modules # исключаем node_modules из монтирования + networks: + - app-network + + # MQTT отправитель (используем тот же образ) + mqtt-sender: + build: + context: . + dockerfile: Dockerfile + container_name: mqtt-sender + ports: + - "8085:8085" + environment: + - MQTT_HOST=emqx # ← используем имя сервиса + - MQTT_PORT=1883 + command: node skynet-mqtt-sender.js # ← запускаем отправитель + depends_on: + - emqx + restart: unless-stopped + networks: + - app-network # MQTT брокер EMQX emqx: image: emqx/emqx:5.4 container_name: emqx ports: - - "1883:1883" # MQTT порт - - "8083:8083" # MQTT over WebSocket - - "8084:8084" # MQTT over WebSocket SSL - - "8883:8883" # MQTT SSL + - "1883:1883" + - "8083:8083" + - "8084:8084" + - "8883:8883" - "8081:18083" # Web Dashboard environment: - - EMQX_NODE_NAME=emqx@localhost + - EMQX_NODE_NAME=emqx@node restart: unless-stopped + networks: + - app-network -# Сеть для общения между контейнерами networks: - default: - name: app-network \ No newline at end of file + app-network: + driver: bridge \ No newline at end of file diff --git a/Yadershiki/src/server/package-lock.json b/Yadershiki/src/server/package-lock.json index 3999cea..06aeb0a 100644 --- a/Yadershiki/src/server/package-lock.json +++ b/Yadershiki/src/server/package-lock.json @@ -14,7 +14,72 @@ "cors": "^2.8.5", "dotenv": "^17.2.3", "express": "^5.1.0", - "path": "^0.12.7" + "mqtt": "^5.14.1", + "path": "^0.12.7", + "socket.io": "^4.8.1" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.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/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "24.6.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.6.1.tgz", + "integrity": "sha512-ljvjjs3DNXummeIaooB4cLBKg2U6SPI6Hjra/9rRIy7CpM0HpLtG9HptkMKAb4HYWy5S7HUvJEuWgr/y0U8SHw==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.13.0" + } + }, + "node_modules/@types/readable-stream": { + "version": "4.0.21", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.21.tgz", + "integrity": "sha512-19eKVv9tugr03IgfXlA9UVUVRbW6IuqRO5B92Dl4a6pT7K8uaGrNS0GkxiZD0BOk6PLuXl5FhWl//eX/pzYdTQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" } }, "node_modules/accepts": { @@ -39,6 +104,47 @@ "node": ">= 0.6" } }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "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/bl": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.1.3.tgz", + "integrity": "sha512-nHB8B5roHlGX5TFsWeiQJijdddZIOHuv1eL2cM2kHnG3qR91CYLsysGe+CvxQfEd23EKD0eJf4lto0frTbddKA==", + "license": "MIT", + "dependencies": { + "@types/readable-stream": "^4.0.0", + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^4.2.0" + } + }, "node_modules/body-parser": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", @@ -102,6 +208,48 @@ "url": "https://opencollective.com/express" } }, + "node_modules/broker-factory": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/broker-factory/-/broker-factory-3.1.10.tgz", + "integrity": "sha512-BzqK5GYFhvVFvO13uzPN0SCiOsOQuhMUbsGvTXDJMA2/N4GvIlFdxEuueE+60Zk841bBU5G3+fl2cqYEo0wgGg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "fast-unique-numbers": "^9.0.24", + "tslib": "^2.8.1", + "worker-factory": "^7.0.46" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -140,6 +288,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/commist": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", + "integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==", + "license": "MIT" + }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -185,6 +339,35 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/content-disposition": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", @@ -295,6 +478,116 @@ "node": ">= 0.8" } }, + "node_modules/engine.io": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", + "license": "MIT", + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "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-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/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "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/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -340,6 +633,24 @@ "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/express": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", @@ -382,6 +693,19 @@ "url": "https://opencollective.com/express" } }, + "node_modules/fast-unique-numbers": { + "version": "9.0.24", + "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-9.0.24.tgz", + "integrity": "sha512-Dv0BYn4waOWse94j16rsZ5w/0zoaCa74O3q6IZjMqaXbtT92Q+Sb6pPk+phGzD8Xh+nueQmSRI3tSCaHKidzKw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18.2.0" + } + }, "node_modules/finalhandler": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", @@ -499,6 +823,12 @@ "node": ">= 0.4" } }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", + "license": "MIT" + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -533,18 +863,63 @@ "node": ">= 0.8" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/ip-address": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, + "node_modules/js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -596,6 +971,58 @@ "node": ">= 0.6" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mqtt": { + "version": "5.14.1", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.14.1.tgz", + "integrity": "sha512-NxkPxE70Uq3Ph7goefQa7ggSsVzHrayCD0OyxlJgITN/EbzlZN+JEPmaAZdxP1LsIT5FamDyILoQTF72W7Nnbw==", + "license": "MIT", + "dependencies": { + "@types/readable-stream": "^4.0.21", + "@types/ws": "^8.18.1", + "commist": "^3.2.0", + "concat-stream": "^2.0.0", + "debug": "^4.4.1", + "help-me": "^5.0.0", + "lru-cache": "^10.4.3", + "minimist": "^1.2.8", + "mqtt-packet": "^9.0.2", + "number-allocator": "^1.0.14", + "readable-stream": "^4.7.0", + "rfdc": "^1.4.1", + "socks": "^2.8.6", + "split2": "^4.2.0", + "worker-timers": "^8.0.23", + "ws": "^8.18.3" + }, + "bin": { + "mqtt": "build/bin/mqtt.js", + "mqtt_pub": "build/bin/pub.js", + "mqtt_sub": "build/bin/sub.js" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/mqtt-packet": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.2.tgz", + "integrity": "sha512-MvIY0B8/qjq7bKxdN1eD+nrljoeaai+qjLJgfRn3TiMuz0pamsIWY2bFODPZMSNmabsLANXsLl4EMoWvlaTZWA==", + "license": "MIT", + "dependencies": { + "bl": "^6.0.8", + "debug": "^4.3.4", + "process-nextick-args": "^2.0.1" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -611,6 +1038,16 @@ "node": ">= 0.6" } }, + "node_modules/number-allocator": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz", + "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.1", + "js-sdsl": "4.3.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -690,6 +1127,12 @@ "node": ">= 0.6.0" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -736,6 +1179,28 @@ "node": ">= 0.6" } }, + "node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, "node_modules/router": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", @@ -912,6 +1377,195 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "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/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "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-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/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -921,6 +1575,15 @@ "node": ">= 0.8" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -930,6 +1593,12 @@ "node": ">=0.6" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/type-is": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", @@ -944,6 +1613,18 @@ "node": ">= 0.6" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.13.0.tgz", + "integrity": "sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ==", + "license": "MIT" + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -962,6 +1643,12 @@ "inherits": "2.0.3" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/util/node_modules/inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", @@ -977,11 +1664,79 @@ "node": ">= 0.8" } }, + "node_modules/worker-factory": { + "version": "7.0.46", + "resolved": "https://registry.npmjs.org/worker-factory/-/worker-factory-7.0.46.tgz", + "integrity": "sha512-Sr1hq2FMgNa04UVhYQacsw+i58BtMimzDb4+CqYphZ97OfefRpURu0UZ+JxMr/H36VVJBfuVkxTK7MytsanC3w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "fast-unique-numbers": "^9.0.24", + "tslib": "^2.8.1" + } + }, + "node_modules/worker-timers": { + "version": "8.0.25", + "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-8.0.25.tgz", + "integrity": "sha512-X7Z5dmM6PlrEnaadtFQOyXHGD/IysPA3HZzaC2koqsU1VI+RvyGmjiiLiUBQixK8PH5R7ilkOzZupWskNRaXmA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "tslib": "^2.8.1", + "worker-timers-broker": "^8.0.11", + "worker-timers-worker": "^9.0.11" + } + }, + "node_modules/worker-timers-broker": { + "version": "8.0.11", + "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-8.0.11.tgz", + "integrity": "sha512-uwhxKru8BI9m2tsogxr2fB6POZ8LB2xH+Pu3R0mvQnAZLPgLD6K3IX4LNKPTEgTJ/j5VsuQPB+gLI1NBNKkPlg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "broker-factory": "^3.1.10", + "fast-unique-numbers": "^9.0.24", + "tslib": "^2.8.1", + "worker-timers-worker": "^9.0.11" + } + }, + "node_modules/worker-timers-worker": { + "version": "9.0.11", + "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-9.0.11.tgz", + "integrity": "sha512-pArb5xtgHWImYpXhjg1OFv7JFG0ubmccb73TFoXHXjG830fFj+16N57q9YeBnZX52dn+itRrMoJZ9HaZBVzDaA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "tslib": "^2.8.1", + "worker-factory": "^7.0.46" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "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 + } + } } } } diff --git a/Yadershiki/src/server/package.json b/Yadershiki/src/server/package.json index 64cb60c..8d74551 100644 --- a/Yadershiki/src/server/package.json +++ b/Yadershiki/src/server/package.json @@ -5,7 +5,9 @@ "cors": "^2.8.5", "dotenv": "^17.2.3", "express": "^5.1.0", - "path": "^0.12.7" + "mqtt": "^5.14.1", + "path": "^0.12.7", + "socket.io": "^4.8.1" }, "name": "server", "type": "module", diff --git a/Yadershiki/src/server/server.js b/Yadershiki/src/server/server.js index 197f64a..412df87 100644 --- a/Yadershiki/src/server/server.js +++ b/Yadershiki/src/server/server.js @@ -5,12 +5,14 @@ import compression from 'compression'; import bodyParser from 'body-parser'; import cors from 'cors'; import { fileURLToPath } from 'url'; +import mqtt from 'mqtt'; dotenv.config(); const port = process.env.PORT; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); + const app = express(); app.use(compression()); app.use(cors()); @@ -19,38 +21,96 @@ app.use(bodyParser.json()); let clients = []; let state = []; +console.log('=== Server starting ==='); +console.log('Endpoints registered: /, /beacons, /api/state, /api/status'); + +// Вместо localhost используем переменную окружения +const mqttHost = process.env.MQTT_HOST || 'localhost'; +const mqttPort = process.env.MQTT_PORT || 1883; + +const mqttClient = mqtt.connect(`mqtt://${mqttHost}:${mqttPort}`, { + reconnectPeriod: 5000, + connectTimeout: 10000 +}); + +mqttClient.on('connect', () => { + console.log('✅ Подключились к MQTT'); + + // Подписываемся на все топики skynet + mqttClient.subscribe('skynet/#', (err) => { + if (err) { + console.log('❌ Ошибка подписки:', err); + } else { + console.log('✅ Подписались на skynet/#'); + } + }); +}); + +// Убедитесь что этот обработчик есть и работает: +mqttClient.on('message', (topic, message) => { + console.log(`🔔 MQTT ПОЛУЧЕНО: [${topic}]`, message.toString()); + + try { + const data = JSON.parse(message.toString()); + console.log('📊 Данные из MQTT:', data); + + // ВАЖНО: обновляем state данными из MQTT + state = data.data || data; // берем либо data.data, либо весь объект + console.log('🔄 State обновлен:', state); + + // Уведомляем SSE клиентов + notifyClients(); + console.log('📢 Клиенты уведомлены'); + + } catch (e) { + console.log('❌ Ошибка парсинга:', e.message); + // Если ошибка парсинга, сохраняем как текст + state = [{ error: 'parse_error', message: message.toString() }]; + notifyClients(); + } +}); + +mqttClient.on('error', (err) => { + console.log('❌ MQTT ошибка:', err.message); +}); + app.get('/', (req, res) => { res.sendFile(path.join(__dirname, 'index.html')); }); app.get('/beacons', (req, res) => { - const headers = { - 'Content-Type': 'text/event-stream', - 'Access-Control-Allow-Origin': '*', - 'Connection': 'keep-alive', - 'Cache-Control': 'no-cache' - }; - res.writeHead(200, headers); + const headers = { + 'Content-Type': 'text/event-stream', + 'Access-Control-Allow-Origin': '*', + 'Connection': 'keep-alive', + 'Cache-Control': 'no-cache' + }; + res.writeHead(200, headers); - const sendData = `data: ${JSON.stringify(state)}\n\n`; + const clientId = genUniqueId(); + const newClient = { + id: clientId, + res, + }; - res.write(sendData); - res.flush(); + clients.push(newClient); - const clientId = genUniqueId(); - const newClient = { - id: clientId, - res, - }; + console.log(`${clientId} - Connection opened. Total clients: ${clients.length}`); - clients.push(newClient); + // Отправляем текущее состояние сразу при подключении + const initialData = `data: ${JSON.stringify(state)}\n\n`; + res.write(initialData); + res.flush(); - console.log(`${clientId} - Connection opened`); + req.on('close', () => { + console.log(`${clientId} - Connection closed. Total clients: ${clients.length}`); + clients = clients.filter(client => client.id !== clientId); + }); - req.on('close', () => { - console.log(`${clientId} - Connection closed`); - clients = clients.filter(client => client.id !== clientId); - }); + req.on('error', (err) => { + console.log(`${clientId} - Connection error:`, err.message); + clients = clients.filter(client => client.id !== clientId); + }); }); function genUniqueId(){ @@ -58,14 +118,101 @@ function genUniqueId(){ } function notifyClients() { - const sendData = `data: ${JSON.stringify(state)}\n\n`; + const sendData = `data: ${JSON.stringify(state)}\n\n`; + const disconnectedClients = []; - for (let i = 0; i < clients.length; i++) { - clients[i].res.write(sendData); - clients[i].res.flush(); - } + clients.forEach((client, index) => { + try { + client.res.write(sendData); + client.res.flush(); + } catch (err) { + console.log(`❌ Ошибка отправки клиенту ${client.id}:`, err.message); + disconnectedClients.push(index); + } + }); + + // Удаляем отключившихся клиентов + if (disconnectedClients.length > 0) { + clients = clients.filter((_, index) => !disconnectedClients.includes(index)); + console.log(`🗑️ Удалено ${disconnectedClients.length} отключившихся клиентов`); + } } -app.listen(port, () => { - console.log(`Starting server on ${port}`); + +// Получить текущее состояние (разово) +app.get('/api/state', (req, res) => { + res.json({ + state: state, + clientsCount: clients.length, + timestamp: new Date().toISOString() + }); }); + +// Получить последние MQTT сообщения +app.get('/api/messages', (req, res) => { + res.json({ + state: state, + message: 'Это текущее состояние из MQTT', + timestamp: new Date().toISOString() + }); +}); + +// Получить статус сервера +app.get('/api/status', (req, res) => { + res.json({ + service: 'Main Server', + status: 'running', + clients: clients.length, + mqtt_connected: mqttClient ? mqttClient.connected : false, + timestamp: new Date().toISOString() + }); +}); + +// Получить информацию о MQTT +app.get('/api/mqtt-info', (req, res) => { + res.json({ + connected: mqttClient ? mqttClient.connected : false, + topics: ['skynet/data', 'skynet/events', 'skynet/test'], + timestamp: new Date().toISOString() + }); +}); + +// Тестовый endpoint для проверки работы +app.post('/api/test-mqtt', (req, res) => { + const testData = req.body; + + console.log('🧪 Тестовые данные:', testData); + + // Принудительно обновляем state + state = testData; + notifyClients(); + + res.json({ + success: true, + message: 'Данные принудительно обновлены', + state: state + }); +}); + +app.post('/api/simulate-mqtt', (req, res) => { + const testData = req.body; + + // Имитируем получение MQTT сообщения + const mockMessage = JSON.stringify(testData); + mqttClient.emit('message', 'skynet/test', mockMessage); + + res.json({ + success: true, + message: 'MQTT сообщение сымитировано', + data: testData + }); +}); + +// Простой ping +app.get('/api/ping', (req, res) => { + res.json({ status: 'ok', time: new Date().toISOString() }); +}); + +app.listen(port, () => { + console.log(`Starting server on ${port}`); +}); \ No newline at end of file diff --git a/Yadershiki/src/server/skynet-mqtt-sender.js b/Yadershiki/src/server/skynet-mqtt-sender.js new file mode 100644 index 0000000..8e6f3ec --- /dev/null +++ b/Yadershiki/src/server/skynet-mqtt-sender.js @@ -0,0 +1,147 @@ +import express from 'express'; +import mqtt from 'mqtt'; + +const app = express(); +const PORT = 8085; + +app.use(express.json()); + +// Подключаемся к EMQX (который в Docker) +// В MQTT болванке добавьте таймауты и реконнект +// Тоже используем переменные окружения +const mqttHost = process.env.MQTT_HOST || 'localhost'; +const mqttPort = process.env.MQTT_PORT || 1883; + +const mqttClient = mqtt.connect(`mqtt://${mqttHost}:${mqttPort}`, { + reconnectPeriod: 5000, + connectTimeout: 10000 +}); + +let mqttReady = false; + +mqttClient.on('connect', () => { + console.log('✅ MQTT болванка подключена к брокеру'); + mqttReady = true; +}); + +mqttClient.on('error', (err) => { + console.log('❌ Ошибка MQTT:', err.message); + mqttReady = false; +}); + +mqttClient.on('close', () => { + console.log('🔌 MQTT соединение закрыто'); + mqttReady = false; +}); + + +// Главная страница +app.get('/', (req, res) => { + res.json({ + service: 'MQTT Sender (Node.js)', + status: 'running', + mqtt_connected: mqttClient.connected, + endpoints: [ + 'POST /send-data', + 'POST /send-event', + 'GET /status' + ] + }); +}); + +// Статус подключения +app.get('/status', (req, res) => { + res.json({ + mqtt_connected: mqttClient.connected, + timestamp: new Date().toISOString() + }); +}); + +// Отправка данных +app.post('/send-data', (req, res) => { + const data = req.body; + + // Отправляем в формате который ожидает основной сервер + const message = data; // отправляем как есть, без обертки + + mqttClient.publish('skynet/data', JSON.stringify(message)); + console.log('📤 Отправлены RAW данные:', message); + + res.json({ success: true, message: 'Данные отправлены' }); +}); + +// Отправка событий +app.post('/send-event', (req, res) => { + if (!mqttClient.connected) { + return res.status(500).json({ error: 'MQTT не подключен' }); + } + + const { event, details } = req.body; + + const message = { + type: 'event', + event: event || 'unknown', + details: details || {}, + timestamp: new Date().toISOString(), + from: 'mqtt-sender' + }; + + mqttClient.publish('skynet/events', JSON.stringify(message)); + console.log('📤 Отправлено событие:', event); + + res.json({ + success: true, + message: 'Событие отправлено через MQTT', + sent: message + }); +}); + +// Автоматическая отправка тестовых данных +app.post('/test', (req, res) => { + if (!mqttClient.connected) { + return res.status(500).json({ error: 'MQTT не подключен' }); + } + + const testData = { + type: 'test', + value: Math.random() * 100, + timestamp: new Date().toISOString(), + from: 'mqtt-sender' + }; + + mqttClient.publish('skynet/test', JSON.stringify(testData)); + console.log('📤 Отправлен тест:', testData.value); + + res.json({ + success: true, + message: 'Тестовое сообщение отправлено', + sent: testData + }); +}); +process.on('SIGINT', () => { + console.log('🛑 Получен SIGINT. Завершаем работу...'); + + // Закрываем MQTT соединение + if (mqttClient) { + mqttClient.end(); + } + + // Закрываем HTTP сервер + process.exit(0); +}); + +process.on('SIGTERM', () => { + console.log('🛑 Получен SIGTERM. Завершаем работу...'); + + if (mqttClient) { + mqttClient.end(); + } + + process.exit(0); +}); +// Запуск сервера +app.listen(PORT, () => { + console.log(`🚀 MQTT болванка запущена на http://localhost:${PORT}`); + console.log(`📡 Подключается к MQTT брокеру: localhost:1883`); +}); + From d814edfe8953ac8073845d46d433905381ae2012 Mon Sep 17 00:00:00 2001 From: vaidy31 <656mpx@gmail.com> Date: Thu, 2 Oct 2025 09:09:28 +0700 Subject: [PATCH 13/34] very unstable version but it should work --- Yadershiki/src/server/server.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Yadershiki/src/server/server.js b/Yadershiki/src/server/server.js index 412df87..93c8d4a 100644 --- a/Yadershiki/src/server/server.js +++ b/Yadershiki/src/server/server.js @@ -21,7 +21,7 @@ app.use(bodyParser.json()); let clients = []; let state = []; -console.log('=== Server starting ==='); +console.log('Server starting'); console.log('Endpoints registered: /, /beacons, /api/state, /api/status'); // Вместо localhost используем переменную окружения @@ -34,36 +34,36 @@ const mqttClient = mqtt.connect(`mqtt://${mqttHost}:${mqttPort}`, { }); mqttClient.on('connect', () => { - console.log('✅ Подключились к MQTT'); + console.log('Подключились к MQTT'); // Подписываемся на все топики skynet mqttClient.subscribe('skynet/#', (err) => { if (err) { - console.log('❌ Ошибка подписки:', err); + console.log('Ошибка подписки:', err); } else { - console.log('✅ Подписались на skynet/#'); + console.log('Подписались на skynet/#'); } }); }); // Убедитесь что этот обработчик есть и работает: mqttClient.on('message', (topic, message) => { - console.log(`🔔 MQTT ПОЛУЧЕНО: [${topic}]`, message.toString()); + console.log(`MQTT ПОЛУЧЕНО: [${topic}]`, message.toString()); try { const data = JSON.parse(message.toString()); - console.log('📊 Данные из MQTT:', data); + console.log('Данные из MQTT:', data); // ВАЖНО: обновляем state данными из MQTT state = data.data || data; // берем либо data.data, либо весь объект - console.log('🔄 State обновлен:', state); + console.log('State обновлен:', state); // Уведомляем SSE клиентов notifyClients(); - console.log('📢 Клиенты уведомлены'); + console.log('Клиенты уведомлены'); } catch (e) { - console.log('❌ Ошибка парсинга:', e.message); + console.log('Ошибка парсинга:', e.message); // Если ошибка парсинга, сохраняем как текст state = [{ error: 'parse_error', message: message.toString() }]; notifyClients(); @@ -71,7 +71,7 @@ mqttClient.on('message', (topic, message) => { }); mqttClient.on('error', (err) => { - console.log('❌ MQTT ошибка:', err.message); + console.log('MQTT ошибка:', err.message); }); app.get('/', (req, res) => { @@ -181,7 +181,7 @@ app.get('/api/mqtt-info', (req, res) => { app.post('/api/test-mqtt', (req, res) => { const testData = req.body; - console.log('🧪 Тестовые данные:', testData); + console.log('Тестовые данные:', testData); // Принудительно обновляем state state = testData; From 59558630a3f1bcf471e66cd717d606a7d3dc5d51 Mon Sep 17 00:00:00 2001 From: Matvey Solovev Date: Thu, 2 Oct 2025 10:43:44 +0700 Subject: [PATCH 14/34] add points drawing --- Yadershiki/src/client/.gitignore | 24 + Yadershiki/src/client/index.html | 14 + Yadershiki/src/client/package-lock.json | 1102 +++++++++++++++++++++++ Yadershiki/src/client/package.json | 19 + Yadershiki/src/client/src/main.js | 29 + Yadershiki/src/client/src/points.js | 40 + Yadershiki/src/client/src/style.css | 12 + 7 files changed, 1240 insertions(+) create mode 100644 Yadershiki/src/client/.gitignore create mode 100644 Yadershiki/src/client/index.html create mode 100644 Yadershiki/src/client/package-lock.json create mode 100644 Yadershiki/src/client/package.json create mode 100644 Yadershiki/src/client/src/main.js create mode 100644 Yadershiki/src/client/src/points.js create mode 100644 Yadershiki/src/client/src/style.css diff --git a/Yadershiki/src/client/.gitignore b/Yadershiki/src/client/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/Yadershiki/src/client/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/Yadershiki/src/client/index.html b/Yadershiki/src/client/index.html new file mode 100644 index 0000000..8f1668b --- /dev/null +++ b/Yadershiki/src/client/index.html @@ -0,0 +1,14 @@ + + + + + + Yaderщики + + +
+
+
+ + + diff --git a/Yadershiki/src/client/package-lock.json b/Yadershiki/src/client/package-lock.json new file mode 100644 index 0000000..9bcb73d --- /dev/null +++ b/Yadershiki/src/client/package-lock.json @@ -0,0 +1,1102 @@ +{ + "name": "client", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "client", + "version": "0.0.0", + "dependencies": { + "dotenv": "^17.2.3", + "konva": "^10.0.2", + "normalize.css": "^8.0.1" + }, + "devDependencies": { + "vite": "^7.1.7" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", + "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", + "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", + "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", + "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", + "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", + "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", + "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", + "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", + "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", + "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", + "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", + "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", + "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", + "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", + "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", + "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", + "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", + "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", + "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", + "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", + "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", + "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", + "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", + "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", + "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", + "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.3.tgz", + "integrity": "sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.3.tgz", + "integrity": "sha512-wd+u7SLT/u6knklV/ifG7gr5Qy4GUbH2hMWcDauPFJzmCZUAJ8L2bTkVXC2niOIxp8lk3iH/QX8kSrUxVZrOVw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.3.tgz", + "integrity": "sha512-lj9ViATR1SsqycwFkJCtYfQTheBdvlWJqzqxwc9f2qrcVrQaF/gCuBRTiTolkRWS6KvNxSk4KHZWG7tDktLgjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.3.tgz", + "integrity": "sha512-+Dyo7O1KUmIsbzx1l+4V4tvEVnVQqMOIYtrxK7ncLSknl1xnMHLgn7gddJVrYPNZfEB8CIi3hK8gq8bDhb3h5A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.3.tgz", + "integrity": "sha512-u9Xg2FavYbD30g3DSfNhxgNrxhi6xVG4Y6i9Ur1C7xUuGDW3banRbXj+qgnIrwRN4KeJ396jchwy9bCIzbyBEQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.3.tgz", + "integrity": "sha512-5M8kyi/OX96wtD5qJR89a/3x5x8x5inXBZO04JWhkQb2JWavOWfjgkdvUqibGJeNNaz1/Z1PPza5/tAPXICI6A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.3.tgz", + "integrity": "sha512-IoerZJ4l1wRMopEHRKOO16e04iXRDyZFZnNZKrWeNquh5d6bucjezgd+OxG03mOMTnS1x7hilzb3uURPkJ0OfA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.3.tgz", + "integrity": "sha512-ZYdtqgHTDfvrJHSh3W22TvjWxwOgc3ThK/XjgcNGP2DIwFIPeAPNsQxrJO5XqleSlgDux2VAoWQ5iJrtaC1TbA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.3.tgz", + "integrity": "sha512-NcViG7A0YtuFDA6xWSgmFb6iPFzHlf5vcqb2p0lGEbT+gjrEEz8nC/EeDHvx6mnGXnGCC1SeVV+8u+smj0CeGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.3.tgz", + "integrity": "sha512-d3pY7LWno6SYNXRm6Ebsq0DJGoiLXTb83AIPCXl9fmtIQs/rXoS8SJxxUNtFbJ5MiOvs+7y34np77+9l4nfFMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.3.tgz", + "integrity": "sha512-3y5GA0JkBuirLqmjwAKwB0keDlI6JfGYduMlJD/Rl7fvb4Ni8iKdQs1eiunMZJhwDWdCvrcqXRY++VEBbvk6Eg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.3.tgz", + "integrity": "sha512-AUUH65a0p3Q0Yfm5oD2KVgzTKgwPyp9DSXc3UA7DtxhEb/WSPfbG4wqXeSN62OG5gSo18em4xv6dbfcUGXcagw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.3.tgz", + "integrity": "sha512-1makPhFFVBqZE+XFg3Dkq+IkQ7JvmUrwwqaYBL2CE+ZpxPaqkGaiWFEWVGyvTwZace6WLJHwjVh/+CXbKDGPmg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.3.tgz", + "integrity": "sha512-OOFJa28dxfl8kLOPMUOQBCO6z3X2SAfzIE276fwT52uXDWUS178KWq0pL7d6p1kz7pkzA0yQwtqL0dEPoVcRWg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.3.tgz", + "integrity": "sha512-jMdsML2VI5l+V7cKfZx3ak+SLlJ8fKvLJ0Eoa4b9/vCUrzXKgoKxvHqvJ/mkWhFiyp88nCkM5S2v6nIwRtPcgg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.3.tgz", + "integrity": "sha512-tPgGd6bY2M2LJTA1uGq8fkSPK8ZLYjDjY+ZLK9WHncCnfIz29LIXIqUgzCR0hIefzy6Hpbe8Th5WOSwTM8E7LA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.3.tgz", + "integrity": "sha512-BCFkJjgk+WFzP+tcSMXq77ymAPIxsX9lFJWs+2JzuZTLtksJ2o5hvgTdIcZ5+oKzUDMwI0PfWzRBYAydAHF2Mw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.3.tgz", + "integrity": "sha512-KTD/EqjZF3yvRaWUJdD1cW+IQBk4fbQaHYJUmP8N4XoKFZilVL8cobFSTDnjTtxWJQ3JYaMgF4nObY/+nYkumA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.3.tgz", + "integrity": "sha512-+zteHZdoUYLkyYKObGHieibUFLbttX2r+58l27XZauq0tcWYYuKUwY2wjeCN9oK1Um2YgH2ibd6cnX/wFD7DuA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.3.tgz", + "integrity": "sha512-of1iHkTQSo3kr6dTIRX6t81uj/c/b15HXVsPcEElN5sS859qHrOepM5p9G41Hah+CTqSh2r8Bm56dL2z9UQQ7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.3.tgz", + "integrity": "sha512-s0hybmlHb56mWVZQj8ra9048/WZTPLILKxcvcq+8awSZmyiSUZjjem1AhU3Tf4ZKpYhK4mg36HtHDOe8QJS5PQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.3.tgz", + "integrity": "sha512-zGIbEVVXVtauFgl3MRwGWEN36P5ZGenHRMgNw88X5wEhEBpq0XrMEZwOn07+ICrwM17XO5xfMZqh0OldCH5VTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/esbuild": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", + "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.10", + "@esbuild/android-arm": "0.25.10", + "@esbuild/android-arm64": "0.25.10", + "@esbuild/android-x64": "0.25.10", + "@esbuild/darwin-arm64": "0.25.10", + "@esbuild/darwin-x64": "0.25.10", + "@esbuild/freebsd-arm64": "0.25.10", + "@esbuild/freebsd-x64": "0.25.10", + "@esbuild/linux-arm": "0.25.10", + "@esbuild/linux-arm64": "0.25.10", + "@esbuild/linux-ia32": "0.25.10", + "@esbuild/linux-loong64": "0.25.10", + "@esbuild/linux-mips64el": "0.25.10", + "@esbuild/linux-ppc64": "0.25.10", + "@esbuild/linux-riscv64": "0.25.10", + "@esbuild/linux-s390x": "0.25.10", + "@esbuild/linux-x64": "0.25.10", + "@esbuild/netbsd-arm64": "0.25.10", + "@esbuild/netbsd-x64": "0.25.10", + "@esbuild/openbsd-arm64": "0.25.10", + "@esbuild/openbsd-x64": "0.25.10", + "@esbuild/openharmony-arm64": "0.25.10", + "@esbuild/sunos-x64": "0.25.10", + "@esbuild/win32-arm64": "0.25.10", + "@esbuild/win32-ia32": "0.25.10", + "@esbuild/win32-x64": "0.25.10" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/konva": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/konva/-/konva-10.0.2.tgz", + "integrity": "sha512-NrZED6YG5BX5h3Xu8EZgLqhQ/+ZhxANYXmlIhMOfpBf+0ToExcdwE+Y46LyJOO/JR7FVeR3YTqon3eirnuo44A==", + "funding": [ + { + "type": "patreon", + "url": "https://www.patreon.com/lavrton" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/konva" + }, + { + "type": "github", + "url": "https://github.com/sponsors/lavrton" + } + ], + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/normalize.css": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz", + "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.3.tgz", + "integrity": "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.52.3", + "@rollup/rollup-android-arm64": "4.52.3", + "@rollup/rollup-darwin-arm64": "4.52.3", + "@rollup/rollup-darwin-x64": "4.52.3", + "@rollup/rollup-freebsd-arm64": "4.52.3", + "@rollup/rollup-freebsd-x64": "4.52.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.3", + "@rollup/rollup-linux-arm-musleabihf": "4.52.3", + "@rollup/rollup-linux-arm64-gnu": "4.52.3", + "@rollup/rollup-linux-arm64-musl": "4.52.3", + "@rollup/rollup-linux-loong64-gnu": "4.52.3", + "@rollup/rollup-linux-ppc64-gnu": "4.52.3", + "@rollup/rollup-linux-riscv64-gnu": "4.52.3", + "@rollup/rollup-linux-riscv64-musl": "4.52.3", + "@rollup/rollup-linux-s390x-gnu": "4.52.3", + "@rollup/rollup-linux-x64-gnu": "4.52.3", + "@rollup/rollup-linux-x64-musl": "4.52.3", + "@rollup/rollup-openharmony-arm64": "4.52.3", + "@rollup/rollup-win32-arm64-msvc": "4.52.3", + "@rollup/rollup-win32-ia32-msvc": "4.52.3", + "@rollup/rollup-win32-x64-gnu": "4.52.3", + "@rollup/rollup-win32-x64-msvc": "4.52.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/vite": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.7.tgz", + "integrity": "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + } + } +} diff --git a/Yadershiki/src/client/package.json b/Yadershiki/src/client/package.json new file mode 100644 index 0000000..991170f --- /dev/null +++ b/Yadershiki/src/client/package.json @@ -0,0 +1,19 @@ +{ + "name": "client", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "devDependencies": { + "vite": "^7.1.7" + }, + "dependencies": { + "dotenv": "^17.2.3", + "konva": "^10.0.2", + "normalize.css": "^8.0.1" + } +} diff --git a/Yadershiki/src/client/src/main.js b/Yadershiki/src/client/src/main.js new file mode 100644 index 0000000..7daa5bd --- /dev/null +++ b/Yadershiki/src/client/src/main.js @@ -0,0 +1,29 @@ +import Konva from 'konva'; +import './style.css'; +import 'normalize.css'; +import drawPoints from './points'; + +Konva.hitOnDragEnabled = true; + +let stage = new Konva.Stage({ + container: 'convas', + width: window.innerWidth, + height: window.innerHeight, + draggable: true, +}); + +const markers = [ + {x: 0, y: 0, name: 'Start'}, + {x: 12, y: 45, fill: 'red'}, + {x: -7, y: 19, fill: 'red'}, + {x: 5, y: -10, fill: 'red'} +]; + +const points = drawPoints( + {x: 0, y: 0}, + {x: stage.width() / 2, y: stage.height() / 2}, + 10, + markers +); + +stage.add(points); diff --git a/Yadershiki/src/client/src/points.js b/Yadershiki/src/client/src/points.js new file mode 100644 index 0000000..2fc25fd --- /dev/null +++ b/Yadershiki/src/client/src/points.js @@ -0,0 +1,40 @@ +import Konva from "konva"; + +export default function drawPoints(origin, startsAt, step, points) { + const layer = new Konva.Layer(); + + points.forEach((point) => { + const x = startsAt.x + step * (point.x - origin.x); + const y = startsAt.y + step * (point.y - origin.y); + + layer.add(drawPoint(x, y, point.fill, point.name)); + }); + + return layer; +} + +function drawPoint(x, y, fill, name) { + const point = new Konva.Group({x, y}); + const circle = new Konva.Circle({ + radius: 10, + fill: fill || 'gray', + stroke: 'black', + strokeWidth: 1, + }); + + point.add(circle); + + if (name !== undefined) { + const caption = new Konva.Text({ + text: name, + fontSize: 16, + }); + + caption.offsetX(caption.width() / 2); + caption.offsetY(-caption.height()); + + point.add(caption); + } + + return point; +} diff --git a/Yadershiki/src/client/src/style.css b/Yadershiki/src/client/src/style.css new file mode 100644 index 0000000..dcd00aa --- /dev/null +++ b/Yadershiki/src/client/src/style.css @@ -0,0 +1,12 @@ +body { + display: flex; + flex: 1 1 auto; +} + +#app { + margin: 0 auto; +} + +canvas { + border: 1px black solid; +} From 856fa0befe9c1eb857a7f3ebe8aa46ce8c08c078 Mon Sep 17 00:00:00 2001 From: vaidy31 <656mpx@gmail.com> Date: Thu, 2 Oct 2025 12:29:08 +0700 Subject: [PATCH 15/34] fix connection with topics --- Yadershiki/src/server/Dockerfile | 19 +++++------ Yadershiki/src/server/server.js | 57 +++++++++++++------------------- 2 files changed, 31 insertions(+), 45 deletions(-) diff --git a/Yadershiki/src/server/Dockerfile b/Yadershiki/src/server/Dockerfile index 336ad75..2b1155a 100644 --- a/Yadershiki/src/server/Dockerfile +++ b/Yadershiki/src/server/Dockerfile @@ -1,20 +1,17 @@ -# образ node.js FROM node:18-alpine -# рабочая директория WORKDIR /app -# Копируем package.json и package-lock.json для установки зависимостей +# Копируем package.json отдельно для кэширования COPY package*.json ./ +RUN npm install -# Устанавливаем зависимости -RUN npm install --production - -# копируем весь исходный код в контейнер +# Копируем исходный код COPY . . -# Порт где будет сервер -EXPOSE 8080 +# Устанавливаем переменные окружения для логирования +ENV NODE_ENV=production +ENV LOG_LEVEL=info -# запуск сервера -CMD ["node", "server.js"] \ No newline at end of file +# Используем stdout/stderr для логов (Docker best practice) +CMD ["node", "--enable-source-maps", "server.js"] \ No newline at end of file diff --git a/Yadershiki/src/server/server.js b/Yadershiki/src/server/server.js index 93c8d4a..24b5ef9 100644 --- a/Yadershiki/src/server/server.js +++ b/Yadershiki/src/server/server.js @@ -33,52 +33,51 @@ const mqttClient = mqtt.connect(`mqtt://${mqttHost}:${mqttPort}`, { connectTimeout: 10000 }); +const mqttTopics = ['skynet/#', 'beacons/rssi']; + mqttClient.on('connect', () => { - console.log('Подключились к MQTT'); + console.log('Connected to MQTT'); - // Подписываемся на все топики skynet - mqttClient.subscribe('skynet/#', (err) => { - if (err) { - console.log('Ошибка подписки:', err); - } else { - console.log('Подписались на skynet/#'); - } + mqttTopics.forEach((topic) => { + mqttClient.subscribe(topic, (err) => { + if (err) { + console.log(`Could not subscribe ${topic}`); + } else { + console.log(`Subscribed on ${topic}`); + } + }) }); }); -// Убедитесь что этот обработчик есть и работает: mqttClient.on('message', (topic, message) => { - console.log(`MQTT ПОЛУЧЕНО: [${topic}]`, message.toString()); + console.log(`MQTT recieved: [${topic}]`, message.toString()); try { const data = JSON.parse(message.toString()); - console.log('Данные из MQTT:', data); + console.log('Got data:', data); - // ВАЖНО: обновляем state данными из MQTT - state = data.data || data; // берем либо data.data, либо весь объект - console.log('State обновлен:', state); + state = data.data || data; + console.log('Updated state:', state); - // Уведомляем SSE клиентов notifyClients(); - console.log('Клиенты уведомлены'); + console.log('Clients notified'); } catch (e) { - console.log('Ошибка парсинга:', e.message); - // Если ошибка парсинга, сохраняем как текст + console.log('Parsing error:', e.message); state = [{ error: 'parse_error', message: message.toString() }]; notifyClients(); } }); mqttClient.on('error', (err) => { - console.log('MQTT ошибка:', err.message); + console.log('MQTT error:', err.message); }); app.get('/', (req, res) => { res.sendFile(path.join(__dirname, 'index.html')); }); -app.get('/beacons', (req, res) => { +app.get('/api/position', (req, res) => { const headers = { 'Content-Type': 'text/event-stream', 'Access-Control-Allow-Origin': '*', @@ -97,7 +96,6 @@ app.get('/beacons', (req, res) => { console.log(`${clientId} - Connection opened. Total clients: ${clients.length}`); - // Отправляем текущее состояние сразу при подключении const initialData = `data: ${JSON.stringify(state)}\n\n`; res.write(initialData); res.flush(); @@ -114,7 +112,7 @@ app.get('/beacons', (req, res) => { }); function genUniqueId(){ - return Date.now() + '-' + Math.floor(Math.random() * 1000000000); + return Date.now() + '-' + Math.floor(Math.random() * 1_000_000_000); } function notifyClients() { @@ -126,20 +124,18 @@ function notifyClients() { client.res.write(sendData); client.res.flush(); } catch (err) { - console.log(`❌ Ошибка отправки клиенту ${client.id}:`, err.message); + console.log(`Error sendig to ${client.id}:`, err.message); disconnectedClients.push(index); } }); - // Удаляем отключившихся клиентов if (disconnectedClients.length > 0) { clients = clients.filter((_, index) => !disconnectedClients.includes(index)); - console.log(`🗑️ Удалено ${disconnectedClients.length} отключившихся клиентов`); + console.log(`Disconnected ${disconnectedClients.length} clients`); } } -// Получить текущее состояние (разово) app.get('/api/state', (req, res) => { res.json({ state: state, @@ -148,16 +144,14 @@ app.get('/api/state', (req, res) => { }); }); -// Получить последние MQTT сообщения app.get('/api/messages', (req, res) => { res.json({ state: state, - message: 'Это текущее состояние из MQTT', + message: 'Current MQTT state', timestamp: new Date().toISOString() }); }); -// Получить статус сервера app.get('/api/status', (req, res) => { res.json({ service: 'Main Server', @@ -168,7 +162,6 @@ app.get('/api/status', (req, res) => { }); }); -// Получить информацию о MQTT app.get('/api/mqtt-info', (req, res) => { res.json({ connected: mqttClient ? mqttClient.connected : false, @@ -177,13 +170,11 @@ app.get('/api/mqtt-info', (req, res) => { }); }); -// Тестовый endpoint для проверки работы app.post('/api/test-mqtt', (req, res) => { const testData = req.body; console.log('Тестовые данные:', testData); - // Принудительно обновляем state state = testData; notifyClients(); @@ -197,7 +188,6 @@ app.post('/api/test-mqtt', (req, res) => { app.post('/api/simulate-mqtt', (req, res) => { const testData = req.body; - // Имитируем получение MQTT сообщения const mockMessage = JSON.stringify(testData); mqttClient.emit('message', 'skynet/test', mockMessage); @@ -208,7 +198,6 @@ app.post('/api/simulate-mqtt', (req, res) => { }); }); -// Простой ping app.get('/api/ping', (req, res) => { res.json({ status: 'ok', time: new Date().toISOString() }); }); From ade39f4ed09d7f23828a1bb5fa9b617d9667e5e8 Mon Sep 17 00:00:00 2001 From: Klim Sadov Date: Thu, 2 Oct 2025 12:32:30 +0700 Subject: [PATCH 16/34] replace previous versions of files and add esp32.py && config_esp32.py --- Yadershiki/src/esp32/config_esp32.py | 17 +++++ .../esp32/{beacon_connection.py => esp32.py} | 63 +++++++++---------- Yadershiki/src/esp32/plate.py | 36 ----------- 3 files changed, 46 insertions(+), 70 deletions(-) create mode 100644 Yadershiki/src/esp32/config_esp32.py rename Yadershiki/src/esp32/{beacon_connection.py => esp32.py} (66%) delete mode 100644 Yadershiki/src/esp32/plate.py diff --git a/Yadershiki/src/esp32/config_esp32.py b/Yadershiki/src/esp32/config_esp32.py new file mode 100644 index 0000000..67a1163 --- /dev/null +++ b/Yadershiki/src/esp32/config_esp32.py @@ -0,0 +1,17 @@ +# MQTT settings +MQTT_BROKER = "192.168.3.26" +MQTT_PORT = 1883 +MQTT_TOPIC = "beacons/rssi" +CLIENT_ID = "beacon_publisher_01" + +# Wi-Fi settings +WIFI_SSID = "vaidy31" +WIFI_PASSWORD = "88888888" + +# BLE scan settings +SCAN_DURATION_MS = 1000 +SCAN_INTERVAL = 1 +SCAN_FREQ = 100 + +# Beacon settings +BEACON_PREFIX = "beacon_" diff --git a/Yadershiki/src/esp32/beacon_connection.py b/Yadershiki/src/esp32/esp32.py similarity index 66% rename from Yadershiki/src/esp32/beacon_connection.py rename to Yadershiki/src/esp32/esp32.py index 36c149c..bbb68bb 100644 --- a/Yadershiki/src/esp32/beacon_connection.py +++ b/Yadershiki/src/esp32/esp32.py @@ -3,18 +3,12 @@ import time import network from umqtt.simple import MQTTClient +import config_esp32 _IRQ_SCAN_RESULT = const(5) -_IRQ_SCAN_DONE = const(6) +_IRQ_SCAN_DONE = const(6) _ADV_TYPE_NAME = const(0x09) -_SCAN_FREQ = 100 - -MQTT_BROKER = "192.168.3.26" -MQTT_PORT = 1883 -MQTT_TOPIC = "beacons/rssi" -CLIENT_ID = "beacon_publisher_01" - ble = bluetooth.BLE() ble.active(True) @@ -39,13 +33,13 @@ def bt_irq(event, data): if event == _IRQ_SCAN_RESULT: addr_type, addr, adv_type, rssi, adv_data = data name = decode_name(adv_data) - if name and name.startswith("beacon_"): + if name and name.startswith(config_esp32.BEACON_PREFIX): if name not in beacons or rssi > beacons[name]: beacons[name] = rssi elif event == _IRQ_SCAN_DONE: scan_done = True -def scan_once(duration_ms=5000): +def scan_once(duration_ms=config_esp32.SCAN_DURATION_MS): global beacons, scan_done beacons = {} scan_done = False @@ -53,16 +47,16 @@ def scan_once(duration_ms=5000): ble.gap_scan(duration_ms, 30000, 30000) while not scan_done: - time.sleep_ms(_SCAN_FREQ) + time.sleep_ms(config_esp32.SCAN_FREQ) beacons_sorted = sorted(beacons.items(), key=lambda x: x[1], reverse=True) return [[name, rssi] for name, rssi in beacons_sorted] def publish_beacons_data(beacons_data): try: - client = MQTTClient(CLIENT_ID, MQTT_BROKER, port=MQTT_PORT) + client = MQTTClient(config_esp32.CLIENT_ID, config_esp32.MQTT_BROKER, port=config_esp32.MQTT_PORT) client.connect() - + beacons_data = [["maxim", -34], ["matvey", -45]] if not beacons_data: data_str = "NO_BEACONS_FOUND" print("Маяки не найдены, отправляем сообщение об отсутствии") @@ -70,8 +64,8 @@ def publish_beacons_data(beacons_data): data_str = str(beacons_data).replace("'", '"') print(f"Найдено маяков: {len(beacons_data)}") - client.publish(MQTT_TOPIC, data_str) - print(f"Опубликовано в топик {MQTT_TOPIC}: {data_str}") + client.publish(config_esp32.MQTT_TOPIC, data_str) + print(f"Опубликовано в топик {config_esp32.MQTT_TOPIC}: {data_str}") client.disconnect() return True @@ -80,18 +74,24 @@ def publish_beacons_data(beacons_data): print(f"Ошибка публикации: {e}") return False -wlan = network.WLAN(network.STA_IF) -wlan.active(True) - -if not wlan.isconnected(): - print("Подключаемся к Wi-Fi...") - wlan.connect('vaidy31', '88888888') +def connect_wifi(): + wlan = network.WLAN(network.STA_IF) + wlan.active(True) - for i in range(10): - if wlan.isconnected(): - break - print('Ожидание...') - time.sleep(1) + if not wlan.isconnected(): + print(f"Подключаемся к Wi-Fi {config_esp32.WIFI_SSID}...") + wlan.connect(config_esp32.WIFI_SSID, config_esp32.WIFI_PASSWORD) + + for i in range(10): + if wlan.isconnected(): + break + print('Ожидание...') + time.sleep(1) + + return wlan + + +wlan = connect_wifi() if wlan.isconnected(): print("Успешно подключено к Wi-Fi") @@ -99,17 +99,12 @@ def publish_beacons_data(beacons_data): while True: try: - # Сканируем маяки - beacons_data = scan_once(duration_ms=5000) - - # Публикуем данные (функция сама определит, есть маяки или нет) + beacons_data = scan_once(duration_ms=config_esp32.SCAN_DURATION_MS) publish_beacons_data(beacons_data) - - # Пауза между сканированиями - time.sleep(2) + time.sleep(config_esp32.SCAN_INTERVAL) except Exception as e: print(f"Ошибка: {e}") time.sleep(5) else: - print("Не удалось подключиться к Wi-Fi") \ No newline at end of file + print("Не удалось подключиться к Wi-Fi") diff --git a/Yadershiki/src/esp32/plate.py b/Yadershiki/src/esp32/plate.py deleted file mode 100644 index b99070b..0000000 --- a/Yadershiki/src/esp32/plate.py +++ /dev/null @@ -1,36 +0,0 @@ -import network -import time - -wlan = network.WLAN(network.STA_IF) -wlan.active(True) - -if not wlan.isconnected(): - print("Пытаемся подключиться") - wlan.connect('vaidy31', '88888888') - - for i in range(10): - if wlan.isconnected(): - break - print('Ожидание...') - time.sleep(1) - -if wlan.isconnected(): - print("Мы смогли. Ура") - print('Сетевые настройки:', wlan.ifconfig()) - - try: - import urequests - print("urequests доступен") - - ip_address = "192.168.3.26:8080" - response = urequests.get(f"http://{ip_address}") - print("Status:", response.status_code) - print("Response:", response.text) - response.close() - - except ImportError: - print("urequests не установлен") - -else: - print("Не удалось подключиться") - From f63852f05ab261fa5f8b8af3ce210f51728b2537 Mon Sep 17 00:00:00 2001 From: Matvey Solovev Date: Thu, 2 Oct 2025 13:16:21 +0700 Subject: [PATCH 17/34] add beacons config parsing --- Yadershiki/src/server/beacons.js | 20 ++++++++++++++++++++ Yadershiki/src/server/config.beacons | 9 +++++++++ 2 files changed, 29 insertions(+) create mode 100644 Yadershiki/src/server/beacons.js create mode 100644 Yadershiki/src/server/config.beacons diff --git a/Yadershiki/src/server/beacons.js b/Yadershiki/src/server/beacons.js new file mode 100644 index 0000000..6431277 --- /dev/null +++ b/Yadershiki/src/server/beacons.js @@ -0,0 +1,20 @@ +import fs from 'fs'; +import path from 'path'; +import csv from 'csv-parser'; + +export default async function getBeacons(dirname) { + const configPath = path.join(dirname, 'config.beacons'); + return new Promise((resolve, rejects) => { + const results = []; + + fs.createReadStream(configPath) + .pipe(csv({separator: ';'})) + .on('data', (data) => results.push({ + name: data.Name, + x: Number(data.X), + y: Number(data.Y), + })) + .on('end', () => resolve(results)) + .on('error', () => rejects(results)); + }); +} diff --git a/Yadershiki/src/server/config.beacons b/Yadershiki/src/server/config.beacons new file mode 100644 index 0000000..bcb97e0 --- /dev/null +++ b/Yadershiki/src/server/config.beacons @@ -0,0 +1,9 @@ +Name;X;Y +beacon_1;3.0;-2.4 +beacon_2;-2.4;-0.6 +beacon_3;1.8;9 +beacon_4;4.8;18.6 +beacon_5;-1.8;26.4 +beacon_6;-1.8;34.2 +beacon_7;7.8;34.2 +beacon_8;-1.8;40.8 From fa7f938d5ac0cedbdd78ce70f2dd4297bad7223d Mon Sep 17 00:00:00 2001 From: Matvey Solovev Date: Thu, 2 Oct 2025 14:51:00 +0700 Subject: [PATCH 18/34] add beacons end point --- Yadershiki/src/server/server.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Yadershiki/src/server/server.js b/Yadershiki/src/server/server.js index 24b5ef9..6303a98 100644 --- a/Yadershiki/src/server/server.js +++ b/Yadershiki/src/server/server.js @@ -6,6 +6,7 @@ import bodyParser from 'body-parser'; import cors from 'cors'; import { fileURLToPath } from 'url'; import mqtt from 'mqtt'; +import getBeacons from './beacons.js'; dotenv.config(); const port = process.env.PORT; @@ -24,7 +25,6 @@ let state = []; console.log('Server starting'); console.log('Endpoints registered: /, /beacons, /api/state, /api/status'); -// Вместо localhost используем переменную окружения const mqttHost = process.env.MQTT_HOST || 'localhost'; const mqttPort = process.env.MQTT_PORT || 1883; @@ -135,6 +135,10 @@ function notifyClients() { } } +app.get('/api/beacons', async (req, res) => { + const beacons = await getBeacons(__dirname); + res.json(beacons); +}); app.get('/api/state', (req, res) => { res.json({ @@ -204,4 +208,4 @@ app.get('/api/ping', (req, res) => { app.listen(port, () => { console.log(`Starting server on ${port}`); -}); \ No newline at end of file +}); From edd6472556ebdbce4ca0b3229cf6cb2c5898a9a2 Mon Sep 17 00:00:00 2001 From: Maxim Zaugolnikov Date: Thu, 2 Oct 2025 15:48:06 +0700 Subject: [PATCH 19/34] added trilateration --- Yadershiki/src/server/locationcals.js | 144 ++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 Yadershiki/src/server/locationcals.js diff --git a/Yadershiki/src/server/locationcals.js b/Yadershiki/src/server/locationcals.js new file mode 100644 index 0000000..c177f80 --- /dev/null +++ b/Yadershiki/src/server/locationcals.js @@ -0,0 +1,144 @@ +import getBeacons from './beacons.js'; + +function addRSSIData(beaconCoordinates, rssiDataList) { + rssiDataList.forEach(rssiData => { + if (Array.isArray(rssiData) && rssiData.length === 2) { + const beaconName = rssiData[0]; + const rssiValue = rssiData[1]; + + const beacon = beaconCoordinates.find(b => b.name === beaconName); + if (beacon) { + beacon.RSSI = rssiValue; + } + } + }); + + return beaconCoordinates; +} + +function sortBeaconsByRSSI(beaconCoordinates) { + return beaconCoordinates.sort((b, a) => { + return a.RSSI - b.RSSI; + }); +} + +function getTopThreeBeacons(sortedBeacons) { + return sortedBeacons.slice(0, 3); +} + +function rssiToDistance(rssi, measuredPower = -59, environmentalFactor = 4) { + return Math.pow(10, (measuredPower - rssi) / (10 * environmentalFactor)); +} + +function calculateBeaconDistances(topThreeBeacons) { + return topThreeBeacons.map(beacon => { + return { + ...beacon, + distance: rssiToDistance(beacon.RSSI) + }; + }); +} + +function trilaterateThreeCircles(circles) { + if (circles.length < 3) { + console.log("There are not enough circles for trilateration"); + return null; + } + + const [c1, c2, c3] = circles.slice(0, 3); + + const A = c2.x - c1.x; + const B = c2.y - c1.y; + const C = c3.x - c1.x; + const D = c3.y - c1.y; + + const r1_sq = c1.distance * c1.distance; + const r2_sq = c2.distance * c2.distance; + const r3_sq = c3.distance * c3.distance; + + const right1 = (r1_sq - r2_sq + c2.x*c2.x - c1.x*c1.x + c2.y*c2.y - c1.y*c1.y) / 2; + const right2 = (r1_sq - r3_sq + c3.x*c3.x - c1.x*c1.x + c3.y*c3.y - c1.y*c1.y) / 2; + + const determinant = A * D - B * C; + + if (Math.abs(determinant) < 1e-10) { + console.log("The circles are degenerate or parallel, and it is impossible to find a point of intersection"); + return null; + } + + const x = (right1 * D - B * right2) / determinant; + const y = (A * right2 - right1 * C) / determinant; + + const maxX = Math.max(c1.x, c2.x, c3.x) + Math.max(c1.distance, c2.distance, c3.distance); + const minX = Math.min(c1.x, c2.x, c3.x) - Math.max(c1.distance, c2.distance, c3.distance); + const maxY = Math.max(c1.y, c2.y, c3.y) + Math.max(c1.distance, c2.distance, c3.distance); + const minY = Math.min(c1.y, c2.y, c3.y) - Math.max(c1.distance, c2.distance, c3.distance); + + if (x < minX || x > maxX || y < minY || y > maxY) { + console.log("The found point goes beyond reasonable limits"); + return null; + } + + return { x: x, y: y }; +} + + +async function trilaterate(dirname, rssiData) { + const beacons = await getBeacons(dirname); + addRSSIData(beacons, rssiData); + const sortedBeacons = sortBeaconsByRSSI(beacons); + const topThreeBeacons = getTopThreeBeacons(sortedBeacons); + + const beaconsWithDistances = calculateBeaconDistances(topThreeBeacons); + + console.log("\nCircles for trilateration:"); + beaconsWithDistances.forEach((beacon, index) => { + console.log(`Circle ${index + 1} (${beacon.name}):`); + console.log(` Center: (${beacon.x}, ${beacon.y})`); + console.log(` Raduis: ${beacon.distance.toFixed(2)} м`); + }); + + const estimatedPosition = trilaterateThreeCircles(beaconsWithDistances); + + if (estimatedPosition) { + console.log(`\n Estimated location: (${estimatedPosition.x.toFixed(2)}, ${estimatedPosition.y.toFixed(2)})`); + } + + console.log("\nAll the beacons:"); + sortedBeacons.forEach(beacon => { + console.log(`${beacon.name}: X=${beacon.x}, Y=${beacon.y}, RSSI=${beacon.RSSI}`); + }); + + return { + allBeacons: sortedBeacons, + topThreeBeacons: topThreeBeacons, + circles: beaconsWithDistances, + estimatedPosition: estimatedPosition, + }; +} + +// 1. Поставьте приемник на 1 метр от маячка +// 2. Измерьте RSSI 10-20 раз +// 3. Возьмите среднее + +// function calibrateBeacon(beaconMac) { +// const measurements = [-58, -59, -57, -60, -58, -59]; +// const averageRSSI = measurements.reduce((a, b) => a + b) / measurements.length; + +// beaconConfigs[beaconMac] = { +// measuredPower: Math.round(averageRSSI), // ≈ -59 +// txPower: -4, +// type: 'ESP32-BEACON' +// }; + +// return averageRSSI; +// } + +const rssi = [ + ['beacon_1', -23], + ['beacon_2', -45], + ['beacon_3', -6] +] + + +trilaterate(".", rssi); \ No newline at end of file From 98a5be7c42da9f73695d270d23b117f4489be16f Mon Sep 17 00:00:00 2001 From: Maxim Zaugolnikov Date: Thu, 2 Oct 2025 15:55:14 +0700 Subject: [PATCH 20/34] fixed --- Yadershiki/src/server/locationcals.js | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/Yadershiki/src/server/locationcals.js b/Yadershiki/src/server/locationcals.js index c177f80..cd458a1 100644 --- a/Yadershiki/src/server/locationcals.js +++ b/Yadershiki/src/server/locationcals.js @@ -91,28 +91,11 @@ async function trilaterate(dirname, rssiData) { const beaconsWithDistances = calculateBeaconDistances(topThreeBeacons); - console.log("\nCircles for trilateration:"); - beaconsWithDistances.forEach((beacon, index) => { - console.log(`Circle ${index + 1} (${beacon.name}):`); - console.log(` Center: (${beacon.x}, ${beacon.y})`); - console.log(` Raduis: ${beacon.distance.toFixed(2)} м`); - }); - const estimatedPosition = trilaterateThreeCircles(beaconsWithDistances); - if (estimatedPosition) { - console.log(`\n Estimated location: (${estimatedPosition.x.toFixed(2)}, ${estimatedPosition.y.toFixed(2)})`); - } - console.log("\nAll the beacons:"); - sortedBeacons.forEach(beacon => { - console.log(`${beacon.name}: X=${beacon.x}, Y=${beacon.y}, RSSI=${beacon.RSSI}`); - }); return { - allBeacons: sortedBeacons, - topThreeBeacons: topThreeBeacons, - circles: beaconsWithDistances, estimatedPosition: estimatedPosition, }; } From 39145c1560db648f9b65ba147629e3f5016330a6 Mon Sep 17 00:00:00 2001 From: Maxim Zaugolnikov Date: Thu, 2 Oct 2025 15:57:43 +0700 Subject: [PATCH 21/34] fixed again. delete hardcode --- Yadershiki/src/server/locationcals.js | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/Yadershiki/src/server/locationcals.js b/Yadershiki/src/server/locationcals.js index cd458a1..f85b4b9 100644 --- a/Yadershiki/src/server/locationcals.js +++ b/Yadershiki/src/server/locationcals.js @@ -93,11 +93,7 @@ async function trilaterate(dirname, rssiData) { const estimatedPosition = trilaterateThreeCircles(beaconsWithDistances); - - - return { - estimatedPosition: estimatedPosition, - }; + return estimatedPosition; } // 1. Поставьте приемник на 1 метр от маячка @@ -117,11 +113,5 @@ async function trilaterate(dirname, rssiData) { // return averageRSSI; // } -const rssi = [ - ['beacon_1', -23], - ['beacon_2', -45], - ['beacon_3', -6] -] - trilaterate(".", rssi); \ No newline at end of file From d59f74db2a41e243b7dc6d682b2f58e3b8ec4b37 Mon Sep 17 00:00:00 2001 From: Maxim Zaugolnikov Date: Thu, 2 Oct 2025 18:26:52 +0700 Subject: [PATCH 22/34] added endpoint for reciving data --- Yadershiki/src/server/locationcals.js | 14 +++++++------- Yadershiki/src/server/server.js | 6 ++++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Yadershiki/src/server/locationcals.js b/Yadershiki/src/server/locationcals.js index f85b4b9..c028e69 100644 --- a/Yadershiki/src/server/locationcals.js +++ b/Yadershiki/src/server/locationcals.js @@ -17,7 +17,7 @@ function addRSSIData(beaconCoordinates, rssiDataList) { } function sortBeaconsByRSSI(beaconCoordinates) { - return beaconCoordinates.sort((b, a) => { + return beaconCoordinates.filter((beacon) => beacon.RSSI !== undefined).sort((b, a) => { return a.RSSI - b.RSSI; }); } @@ -74,16 +74,16 @@ function trilaterateThreeCircles(circles) { const maxY = Math.max(c1.y, c2.y, c3.y) + Math.max(c1.distance, c2.distance, c3.distance); const minY = Math.min(c1.y, c2.y, c3.y) - Math.max(c1.distance, c2.distance, c3.distance); - if (x < minX || x > maxX || y < minY || y > maxY) { - console.log("The found point goes beyond reasonable limits"); - return null; - } + // if (x < minX || x > maxX || y < minY || y > maxY) { + // console.log("The found point goes beyond reasonable limits"); + // return null; + // } return { x: x, y: y }; } -async function trilaterate(dirname, rssiData) { +export default async function trilaterate(dirname, rssiData) { const beacons = await getBeacons(dirname); addRSSIData(beacons, rssiData); const sortedBeacons = sortBeaconsByRSSI(beacons); @@ -114,4 +114,4 @@ async function trilaterate(dirname, rssiData) { // } -trilaterate(".", rssi); \ No newline at end of file +// trilaterate(".", rssi); \ No newline at end of file diff --git a/Yadershiki/src/server/server.js b/Yadershiki/src/server/server.js index 6303a98..ad01249 100644 --- a/Yadershiki/src/server/server.js +++ b/Yadershiki/src/server/server.js @@ -7,6 +7,7 @@ import cors from 'cors'; import { fileURLToPath } from 'url'; import mqtt from 'mqtt'; import getBeacons from './beacons.js'; +import trilaterate from './locationcals.js'; dotenv.config(); const port = process.env.PORT; @@ -49,14 +50,15 @@ mqttClient.on('connect', () => { }); }); -mqttClient.on('message', (topic, message) => { +mqttClient.on('message', async (topic, message) => { console.log(`MQTT recieved: [${topic}]`, message.toString()); try { const data = JSON.parse(message.toString()); console.log('Got data:', data); - state = data.data || data; + const rssi = data.data || data; + state = await trilaterate(__dirname, rssi); console.log('Updated state:', state); notifyClients(); From e928c19e4d939c06934c9704c9cd94d726e5cf18 Mon Sep 17 00:00:00 2001 From: Matvey Solovev Date: Thu, 2 Oct 2025 18:51:27 +0700 Subject: [PATCH 23/34] add user drawing --- Yadershiki/src/client/src/main.js | 48 +++++++++++++++++++++-------- Yadershiki/src/client/src/points.js | 22 ++++++++----- 2 files changed, 50 insertions(+), 20 deletions(-) diff --git a/Yadershiki/src/client/src/main.js b/Yadershiki/src/client/src/main.js index 7daa5bd..3160483 100644 --- a/Yadershiki/src/client/src/main.js +++ b/Yadershiki/src/client/src/main.js @@ -1,9 +1,10 @@ import Konva from 'konva'; import './style.css'; import 'normalize.css'; -import drawPoints from './points'; +import drawPoints, { drawPoint, mapCoordinates } from './points'; Konva.hitOnDragEnabled = true; +const API_URL = import.meta.env.VITE_API_URL; let stage = new Konva.Stage({ container: 'convas', @@ -11,19 +12,42 @@ let stage = new Konva.Stage({ height: window.innerHeight, draggable: true, }); +const origin = {x: 0, y: 0}; +const center = {x: stage.width() / 2, y: stage.height() / 2}; +const step = 10; -const markers = [ - {x: 0, y: 0, name: 'Start'}, - {x: 12, y: 45, fill: 'red'}, - {x: -7, y: 19, fill: 'red'}, - {x: 5, y: -10, fill: 'red'} -]; +const data = await fetch(`${API_URL}/api/beacons`) + .then((response) => { + return response.json(); + }); -const points = drawPoints( - {x: 0, y: 0}, - {x: stage.width() / 2, y: stage.height() / 2}, - 10, - markers +const user = drawPoint( + origin, center, step, + {x: 0, y: 0, name: 'You', fill: 'green'} ); +const start = {x: 0, y: 0, name: 'Start', fill: 'red'}; +const points = drawPoints( + origin, center, step, + [start, ...data] +); + +points.add(user); stage.add(points); + + +const positionSource = new EventSource(`${API_URL}/api/position`); +positionSource.onmessage = async (event) => { + try { + const data = await JSON.parse(event.data); + const {x, y} = mapCoordinates(origin, center, step, data); + + user.x(x); + user.y(y); + + points.batchDraw(); + } catch (err) { + console.error(`Error: ${err}`); + } +}; + diff --git a/Yadershiki/src/client/src/points.js b/Yadershiki/src/client/src/points.js index 2fc25fd..97127c1 100644 --- a/Yadershiki/src/client/src/points.js +++ b/Yadershiki/src/client/src/points.js @@ -4,29 +4,28 @@ export default function drawPoints(origin, startsAt, step, points) { const layer = new Konva.Layer(); points.forEach((point) => { - const x = startsAt.x + step * (point.x - origin.x); - const y = startsAt.y + step * (point.y - origin.y); - - layer.add(drawPoint(x, y, point.fill, point.name)); + layer.add(drawPoint(origin, startsAt, step, point)); }); return layer; } -function drawPoint(x, y, fill, name) { +export function drawPoint(origin, startsAt, step, pointData) { + const {x, y} = mapCoordinates(origin, startsAt, step, pointData); + const point = new Konva.Group({x, y}); const circle = new Konva.Circle({ radius: 10, - fill: fill || 'gray', + fill: pointData.fill || 'gray', stroke: 'black', strokeWidth: 1, }); point.add(circle); - if (name !== undefined) { + if (pointData.name !== undefined) { const caption = new Konva.Text({ - text: name, + text: pointData.name, fontSize: 16, }); @@ -38,3 +37,10 @@ function drawPoint(x, y, fill, name) { return point; } + +export function mapCoordinates(origin, startsAt, step, coords) { + return { + x: startsAt.x + step * (coords.x - origin.x), + y: startsAt.y - step * (coords.y - origin.y), + }; +} From 6a244e20b90e3332e3c1b8f1e127e26e106b49e1 Mon Sep 17 00:00:00 2001 From: Matvey Solovev Date: Thu, 2 Oct 2025 20:04:11 +0700 Subject: [PATCH 24/34] add path recording --- Yadershiki/src/client/index.html | 4 ++ Yadershiki/src/client/src/main.js | 6 ++- Yadershiki/src/client/src/pathRecord.js | 63 +++++++++++++++++++++++++ Yadershiki/src/client/src/style.css | 8 +++- 4 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 Yadershiki/src/client/src/pathRecord.js diff --git a/Yadershiki/src/client/index.html b/Yadershiki/src/client/index.html index 8f1668b..45bab64 100644 --- a/Yadershiki/src/client/index.html +++ b/Yadershiki/src/client/index.html @@ -7,6 +7,10 @@
+
+ + +
diff --git a/Yadershiki/src/client/src/main.js b/Yadershiki/src/client/src/main.js index 3160483..d5c722a 100644 --- a/Yadershiki/src/client/src/main.js +++ b/Yadershiki/src/client/src/main.js @@ -2,11 +2,14 @@ import Konva from 'konva'; import './style.css'; import 'normalize.css'; import drawPoints, { drawPoint, mapCoordinates } from './points'; +import PathRecord from './pathRecord'; Konva.hitOnDragEnabled = true; const API_URL = import.meta.env.VITE_API_URL; -let stage = new Konva.Stage({ +const record = new PathRecord(); + +const stage = new Konva.Stage({ container: 'convas', width: window.innerWidth, height: window.innerHeight, @@ -40,6 +43,7 @@ const positionSource = new EventSource(`${API_URL}/api/position`); positionSource.onmessage = async (event) => { try { const data = await JSON.parse(event.data); + record.add(data.x, data.y); const {x, y} = mapCoordinates(origin, center, step, data); user.x(x); diff --git a/Yadershiki/src/client/src/pathRecord.js b/Yadershiki/src/client/src/pathRecord.js new file mode 100644 index 0000000..28a67f7 --- /dev/null +++ b/Yadershiki/src/client/src/pathRecord.js @@ -0,0 +1,63 @@ +export default class PathRecord { + #data = []; + #isRecording = false; + + constructor() { + const recordButton = document.getElementById('record'); + const downloadButton = document.getElementById('download'); + + recordButton.onclick = () => { + if (!this.isRecording()) { + this.start(); + downloadButton.disabled = true; + recordButton.innerHTML = 'Stop Recording'; + } else { + this.stop(); + downloadButton.disabled = false; + recordButton.innerHTML = 'Start Recording'; + } + } + + downloadButton.onclick = () => { + const blob = this.toCsv(';'); + const link = document.createElement('a'); + link.href = URL.createObjectURL(blob); + link.download = 'recorded.path' + + document.body.appendChild(link); + link.click(); + + URL.revokeObjectURL(link.href); + document.body.removeChild(link); + } + + downloadButton.disabled = true; + } + + start() { + this.#data = [['X', 'Y']]; + this.#isRecording = true; + } + + add(x, y) { + if (this.#isRecording) { + this.#data.push([x, y]); + } + } + + stop() { + this.#isRecording = false; + } + + toCsv(separator) { + const csvContent = this.#data.map((row) => row.join(separator)).join('\n'); + const blob = new Blob([csvContent], {type: 'text/csv,charset=utf-8'}); + + return blob; + } + + isRecording() { + return this.#isRecording; + } +} + diff --git a/Yadershiki/src/client/src/style.css b/Yadershiki/src/client/src/style.css index dcd00aa..9c2bc4f 100644 --- a/Yadershiki/src/client/src/style.css +++ b/Yadershiki/src/client/src/style.css @@ -7,6 +7,10 @@ body { margin: 0 auto; } -canvas { - border: 1px black solid; +#controls { + position: fixed; + top: 15px; + left: 20px; + z-index: 100; } + From fdd3629247666763a03e3ccabc6985c6f44493c7 Mon Sep 17 00:00:00 2001 From: Matvey Solovev Date: Fri, 3 Oct 2025 08:32:55 +0700 Subject: [PATCH 25/34] splitted logic into modules --- Yadershiki/src/client/index.html | 2 +- Yadershiki/src/client/src/canvas.js | 38 ++++++++++++++++++++++ Yadershiki/src/client/src/main.js | 47 +++++----------------------- Yadershiki/src/client/src/network.js | 15 +++++++++ 4 files changed, 62 insertions(+), 40 deletions(-) create mode 100644 Yadershiki/src/client/src/canvas.js create mode 100644 Yadershiki/src/client/src/network.js diff --git a/Yadershiki/src/client/index.html b/Yadershiki/src/client/index.html index 45bab64..bd0acbd 100644 --- a/Yadershiki/src/client/index.html +++ b/Yadershiki/src/client/index.html @@ -11,7 +11,7 @@ -
+
diff --git a/Yadershiki/src/client/src/canvas.js b/Yadershiki/src/client/src/canvas.js new file mode 100644 index 0000000..3f919c0 --- /dev/null +++ b/Yadershiki/src/client/src/canvas.js @@ -0,0 +1,38 @@ +import Konva from "konva"; +import drawPoints, { drawPoint } from "./points"; +import { fetchBeacons } from "./network"; + +export async function setUpStage(containerId) { + Konva.hitOnDragEnabled = true; + + const stage = new Konva.Stage({ + container: containerId, + width: window.innerWidth, + height: window.innerHeight, + draggable: true, + }); + + const origin = {x: 0, y: 0}; + const center = {x: stage.width() / 2, y: stage.height() / 2}; + const step = 10; + + const getGridParams = () => ({ origin, center, step }); + + const user = drawPoint( + origin, center, step, + {x: 0, y: 0, name: 'You', fill: 'green'} + ); + const start = {x: 0, y: 0, name: 'Start', fill: 'red'}; + + const data = await fetchBeacons(); + const points = drawPoints( + origin, center, step, + [start, ...data] + ); + + points.add(user); + stage.add(points); + + return { stage, user, getGridParams}; +} + diff --git a/Yadershiki/src/client/src/main.js b/Yadershiki/src/client/src/main.js index d5c722a..841bdfc 100644 --- a/Yadershiki/src/client/src/main.js +++ b/Yadershiki/src/client/src/main.js @@ -1,57 +1,26 @@ -import Konva from 'konva'; import './style.css'; import 'normalize.css'; -import drawPoints, { drawPoint, mapCoordinates } from './points'; +import { mapCoordinates } from './points'; +import { openPositionSource } from './network'; +import { setUpStage } from './canvas'; import PathRecord from './pathRecord'; -Konva.hitOnDragEnabled = true; -const API_URL = import.meta.env.VITE_API_URL; - const record = new PathRecord(); +const { stage, user, getGridParams } = await setUpStage('canvas'); -const stage = new Konva.Stage({ - container: 'convas', - width: window.innerWidth, - height: window.innerHeight, - draggable: true, -}); -const origin = {x: 0, y: 0}; -const center = {x: stage.width() / 2, y: stage.height() / 2}; -const step = 10; - -const data = await fetch(`${API_URL}/api/beacons`) - .then((response) => { - return response.json(); - }); - -const user = drawPoint( - origin, center, step, - {x: 0, y: 0, name: 'You', fill: 'green'} -); -const start = {x: 0, y: 0, name: 'Start', fill: 'red'}; - -const points = drawPoints( - origin, center, step, - [start, ...data] -); - -points.add(user); -stage.add(points); - - -const positionSource = new EventSource(`${API_URL}/api/position`); -positionSource.onmessage = async (event) => { +const positionSource = openPositionSource(async (event) => { try { const data = await JSON.parse(event.data); record.add(data.x, data.y); + const { origin, center, step } = getGridParams(); const {x, y} = mapCoordinates(origin, center, step, data); user.x(x); user.y(y); - points.batchDraw(); + stage.batchDraw(); } catch (err) { console.error(`Error: ${err}`); } -}; +}); diff --git a/Yadershiki/src/client/src/network.js b/Yadershiki/src/client/src/network.js new file mode 100644 index 0000000..f693456 --- /dev/null +++ b/Yadershiki/src/client/src/network.js @@ -0,0 +1,15 @@ +const API_URL = import.meta.env.VITE_API_URL; + +export async function fetchBeacons() { + return await fetch(`${API_URL}/api/beacons`) + .then((response) => { + return response.json(); + }); +} + +export function openPositionSource(onmessage) { + const source = new EventSource(`${API_URL}/api/position`); + source.onmessage = onmessage; + + return source; +} From 8f5e132b250fe84fa6fba58c36df4577441373e8 Mon Sep 17 00:00:00 2001 From: Matvey Solovev Date: Fri, 3 Oct 2025 09:35:34 +0700 Subject: [PATCH 26/34] add path drawing --- Yadershiki/src/client/src/canvas.js | 19 +++++++++++++++++++ Yadershiki/src/client/src/main.js | 10 +++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/Yadershiki/src/client/src/canvas.js b/Yadershiki/src/client/src/canvas.js index 3f919c0..d186d35 100644 --- a/Yadershiki/src/client/src/canvas.js +++ b/Yadershiki/src/client/src/canvas.js @@ -36,3 +36,22 @@ export async function setUpStage(containerId) { return { stage, user, getGridParams}; } +export function drawPath(stage, layer, path) { + let line; + if (layer === undefined || layer.find('Line').length === 0) { + layer = new Konva.Layer(); + line = new Konva.Line({ + points: [], + stroke: 'black', + strokeWidth: 2, + }) + layer.add(line); + stage.add(layer); + } else { + line = layer.find('Line')[0]; + } + + line.points(path); + layer.batchDraw(); + return layer; +} diff --git a/Yadershiki/src/client/src/main.js b/Yadershiki/src/client/src/main.js index 841bdfc..984f9bb 100644 --- a/Yadershiki/src/client/src/main.js +++ b/Yadershiki/src/client/src/main.js @@ -2,10 +2,11 @@ import './style.css'; import 'normalize.css'; import { mapCoordinates } from './points'; import { openPositionSource } from './network'; -import { setUpStage } from './canvas'; +import { drawPath, setUpStage } from './canvas'; import PathRecord from './pathRecord'; const record = new PathRecord(); +let layer, path = []; const { stage, user, getGridParams } = await setUpStage('canvas'); const positionSource = openPositionSource(async (event) => { @@ -18,6 +19,13 @@ const positionSource = openPositionSource(async (event) => { user.x(x); user.y(y); + if (record.isRecording()) { + path.push(x, y); + } else { + path = []; + } + layer = drawPath(stage, layer, path); + stage.batchDraw(); } catch (err) { console.error(`Error: ${err}`); From bf70e44070c3f8f6df24818bcf8e0b5adf3416ff Mon Sep 17 00:00:00 2001 From: Matvey Solovev Date: Fri, 3 Oct 2025 10:40:57 +0700 Subject: [PATCH 27/34] remove start marker --- Yadershiki/src/client/src/canvas.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Yadershiki/src/client/src/canvas.js b/Yadershiki/src/client/src/canvas.js index d186d35..92bc4a0 100644 --- a/Yadershiki/src/client/src/canvas.js +++ b/Yadershiki/src/client/src/canvas.js @@ -14,7 +14,7 @@ export async function setUpStage(containerId) { const origin = {x: 0, y: 0}; const center = {x: stage.width() / 2, y: stage.height() / 2}; - const step = 10; + const step = 25; const getGridParams = () => ({ origin, center, step }); @@ -22,12 +22,11 @@ export async function setUpStage(containerId) { origin, center, step, {x: 0, y: 0, name: 'You', fill: 'green'} ); - const start = {x: 0, y: 0, name: 'Start', fill: 'red'}; const data = await fetchBeacons(); const points = drawPoints( origin, center, step, - [start, ...data] + data ); points.add(user); From 93ded87f95a00e77a53421690cfc655e94aea84b Mon Sep 17 00:00:00 2001 From: Matvey Solovev Date: Fri, 3 Oct 2025 11:28:06 +0700 Subject: [PATCH 28/34] add port configuration --- Yadershiki/src/client/src/network.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Yadershiki/src/client/src/network.js b/Yadershiki/src/client/src/network.js index f693456..c458ca3 100644 --- a/Yadershiki/src/client/src/network.js +++ b/Yadershiki/src/client/src/network.js @@ -1,4 +1,7 @@ -const API_URL = import.meta.env.VITE_API_URL; +const API_PORT = import.meta.env.VITE_API_PORT || 8080; +const API_URL = `${import.meta.env.VITE_API_URL}:${API_PORT}`; + + export async function fetchBeacons() { return await fetch(`${API_URL}/api/beacons`) From 0e3095259f638b31a2e3500762fcf417d9cfc386 Mon Sep 17 00:00:00 2001 From: Matvey Solovev Date: Fri, 3 Oct 2025 11:30:33 +0700 Subject: [PATCH 29/34] add client set up guide --- Yadershiki/set_up_client.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 Yadershiki/set_up_client.md diff --git a/Yadershiki/set_up_client.md b/Yadershiki/set_up_client.md new file mode 100644 index 0000000..69ebf60 --- /dev/null +++ b/Yadershiki/set_up_client.md @@ -0,0 +1,36 @@ +# Настройка клиента +Клиент представляет собой веб-приложение. Для сборки требуется установленный NodeJS 20.19 и выше + +## 1. Подготовка окружения +Перед сборкой требуется создать .env файл корне папки клиента(в репозитории это src/client) и прописать в нём конфигурацию + +``` +VITE_API_URL= # укажите здесь URL API-сервера +VITE_API_PORT= # укажите здесь порт, по умолчанию используется 8080 +``` +**ВАЖНО**: VITE_API_URL не должен иметь в конце знак '/', так как в приложении он конкатенируется с портом + +Пример файла: +``` +VITE_API_URL=http://myapi.best +VITE_API_PORT= +``` + +Приложение использует систему сборки Vite, поэтому вы можете дополнительно сконфигурировать используя официальную документацию + +## 2. Сборка приложения +Сборка происходит в 2 этапа: +1. Установка зависимостей +2. Сборка + +Вы можете использовать следующие команды +```bash +npm i +npm run build +``` + +Готовый билд должен находиться в папке dist/ + +## 3. Публикация +Полученную папку dist необходимо целиком перенести в папку сервера + From 9702e777c0d95b6227fb55e1fa1acfff25f701a1 Mon Sep 17 00:00:00 2001 From: Matvey Solovev Date: Fri, 3 Oct 2025 11:39:39 +0700 Subject: [PATCH 30/34] updated README --- Yadershiki/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Yadershiki/README.md b/Yadershiki/README.md index d5419c9..0066ed2 100644 --- a/Yadershiki/README.md +++ b/Yadershiki/README.md @@ -1 +1,9 @@ # Indoor navigation system +Система представляет из себя решение для определения положения в пространстве при помощи BLE маяков. +Для развёртки см. файл SETUP.md + +## Работа с клиентом +Клиент представляет из сайт, где отображается карта и кнопки для управления +На карте отображены маяки(серые точки) и пользователь(зелёная точка) + +Для начала записи пути нажмите на кноку "Start Recording". При этом на карте начнёт отображаться записываемый маршрут. Чтобы завершить запись нажмите на "Stop Recording". После этого станет доступна кнопка "Download Path", при нажатии на которую загружается csv файл с маршрутом From b95ec4c158e2447cc51d502d435d121bf0dbbf59 Mon Sep 17 00:00:00 2001 From: Klim Sadov Date: Fri, 3 Oct 2025 11:42:17 +0700 Subject: [PATCH 31/34] Add files via upload --- .../ESP32 Beacon Scanner + MQTT Publisher.md | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 Yadershiki/ESP32 Beacon Scanner + MQTT Publisher.md diff --git a/Yadershiki/ESP32 Beacon Scanner + MQTT Publisher.md b/Yadershiki/ESP32 Beacon Scanner + MQTT Publisher.md new file mode 100644 index 0000000..edcadf5 --- /dev/null +++ b/Yadershiki/ESP32 Beacon Scanner + MQTT Publisher.md @@ -0,0 +1,93 @@ +Этот проект позволяет использовать **ESP32** как считыватель BLE-маяков и отправлять данные о сигнале (**RSSI**) на сервер через **MQTT**. + +--- +### Требования +- ESP32 (с поддержкой Wi-Fi и Bluetooth) +- Thonny IDE (Python IDE с поддержкой MicroPython) +- Прошивка **MicroPython** на ESP32 (официальный сайт) +- MQTT-брокер (например, Mosquitto, EMQX или встроенный в Home Assistant) +--- + +## Установка прошивки на ESP32 + +1. Скачайте **MicroPython прошивку** для ESP32: + [MicroPython - Python for microcontrollers](https://micropython.org/download/ESP32_GENERIC/) +2. Установите утилиту `esptool.py` (если ещё не установлена): + `pip install esptool` +3. Подключите ESP32 к компьютеру и прошейте: + `esptool.py --chip esp32 --port COM7 erase_flash esptool.py --chip esp32 --port COM7 write_flash -z 0x1000 esp32-[версия].bin` + + ⚠️ Замените `COM7` на ваш порт и `esp32-[версия].bin` на скачанный файл. + + +--- + +## Загрузка и запуск кода в Thonny + +1. Установите **Thonny**: thonny.org +2. Подключите ESP32 и выберите его порт: + - В Thonny: `Инструменты → Настройки → Интерпретатор → MicroPython (ESP32)` +3. Скопируйте файлы на плату: + - `esp32.py` + - `config_esp32.py` +4. Нажмите **Run **для запуска `esp32.py`. + + +--- + +## Настройка `config_esp32.py` + +Откройте файл `config_esp32.py` и укажите свои параметры: + +MQTT_BROKER = "192.168.3.26" # IP брокера +MQTT_PORT = 1883 +MQTT_TOPIC = "beacons/rssi" +CLIENT_ID = "beacon_publisher_01" + +WIFI_SSID = "ваш_вайфай" +WIFI_PASSWORD = "ваш_пароль" + +SCAN_DURATION_MS = 1000 # время одного скана (мс) +SCAN_INTERVAL = 1 # пауза между циклами (сек) +SCAN_FREQ = 100 # частота проверки статуса (мс) + +BEACON_PREFIX = "beacon_" # фильтрация по имени маяка + +--- + +## Как работает `esp32.py` + +- Подключается к Wi-Fi (данные берутся из `config_esp32.py`). +- Запускает Bluetooth-сканер и ищет устройства, начинающиеся с `BEACON_PREFIX`. +- Собирает **RSSI** для найденных маяков. +- Отправляет результат в MQTT-брокер в JSON-формате. + +Пример данных, отправляемых в топик: + +`[["beacon_1", -45], ["beacon_2", -67]]` + +Если маяки не найдены: + +`"NO_BEACONS_FOUND"` + +--- + +## Проверка работы + +1. Запустите MQTT-брокер (например, Mosquitto). +2. Подпишитесь на топик: + `mosquitto_sub -h 192.168.3.26 -t beacons/rssi` +3. Должны приходить данные в реальном времени: + `[["beacon_maxim", -34], ["beacon_matvey", -45]]` +--- + +## Возможные ошибки + +- **"Не удалось подключиться к Wi-Fi"** → Проверьте SSID и пароль. + +- **"Ошибка публикации"** → Проверьте IP MQTT-брокера и его доступность. + +- **Нет маяков** → Убедитесь, что у маяков имя начинается с `BEACON_PREFIX`. + + +--- From baebb0619d9e88b261a00630b40719c5c80e8de4 Mon Sep 17 00:00:00 2001 From: Maxim Zaugolnikov Date: Fri, 3 Oct 2025 11:51:46 +0700 Subject: [PATCH 32/34] add setup --- Yadershiki/src/server/Dockerfile | 4 - Yadershiki/src/server/config.beacons | 16 +- Yadershiki/src/server/docker-compose.yml | 30 +- Yadershiki/src/server/index.html | 11 - Yadershiki/src/server/locationcals.js | 294 +++++++++++++------- Yadershiki/src/server/package.json | 1 + Yadershiki/src/server/server.js | 112 ++++++-- Yadershiki/src/server/server.md | 54 ++++ Yadershiki/src/server/skynet-mqtt-sender.js | 147 ---------- 9 files changed, 356 insertions(+), 313 deletions(-) delete mode 100644 Yadershiki/src/server/index.html create mode 100644 Yadershiki/src/server/server.md delete mode 100644 Yadershiki/src/server/skynet-mqtt-sender.js diff --git a/Yadershiki/src/server/Dockerfile b/Yadershiki/src/server/Dockerfile index 2b1155a..e5a42a6 100644 --- a/Yadershiki/src/server/Dockerfile +++ b/Yadershiki/src/server/Dockerfile @@ -2,16 +2,12 @@ FROM node:18-alpine WORKDIR /app -# Копируем package.json отдельно для кэширования COPY package*.json ./ RUN npm install -# Копируем исходный код COPY . . -# Устанавливаем переменные окружения для логирования ENV NODE_ENV=production ENV LOG_LEVEL=info -# Используем stdout/stderr для логов (Docker best practice) CMD ["node", "--enable-source-maps", "server.js"] \ No newline at end of file diff --git a/Yadershiki/src/server/config.beacons b/Yadershiki/src/server/config.beacons index bcb97e0..e75c267 100644 --- a/Yadershiki/src/server/config.beacons +++ b/Yadershiki/src/server/config.beacons @@ -1,9 +1,9 @@ Name;X;Y -beacon_1;3.0;-2.4 -beacon_2;-2.4;-0.6 -beacon_3;1.8;9 -beacon_4;4.8;18.6 -beacon_5;-1.8;26.4 -beacon_6;-1.8;34.2 -beacon_7;7.8;34.2 -beacon_8;-1.8;40.8 +beacon_1;-7.2;-4.8 +beacon_2;3;-4.8 +beacon_3;9.6;-0.6 +beacon_4;-13.8;-1.8 +beacon_5;-9;4.2 +beacon_6;6;9 +beacon_7;-13.2;10.8 +beacon_8;-3;2.4 \ No newline at end of file diff --git a/Yadershiki/src/server/docker-compose.yml b/Yadershiki/src/server/docker-compose.yml index 5b4dddd..2faedf2 100644 --- a/Yadershiki/src/server/docker-compose.yml +++ b/Yadershiki/src/server/docker-compose.yml @@ -1,7 +1,5 @@ -version: '3.8' - services: - # Основной сервер (получатель MQTT) + # Основной сервер app: build: context: . @@ -11,27 +9,9 @@ services: - "8080:8080" environment: - NODE_ENV=production - - MQTT_HOST=emqx # ← используем имя сервиса - - MQTT_PORT=1883 - command: node server.js # ← явно указываем запуск основного сервера - depends_on: - - emqx - restart: unless-stopped - networks: - - app-network - - # MQTT отправитель (используем тот же образ) - mqtt-sender: - build: - context: . - dockerfile: Dockerfile - container_name: mqtt-sender - ports: - - "8085:8085" - environment: - - MQTT_HOST=emqx # ← используем имя сервиса + - MQTT_HOST=emqx - MQTT_PORT=1883 - command: node skynet-mqtt-sender.js # ← запускаем отправитель + command: node server.js depends_on: - emqx restart: unless-stopped @@ -43,11 +23,11 @@ services: image: emqx/emqx:5.4 container_name: emqx ports: - - "1883:1883" + - "1883:1883" # порт - "8083:8083" - "8084:8084" - "8883:8883" - - "8081:18083" # Web Dashboard + - "8081:18083" environment: - EMQX_NODE_NAME=emqx@node restart: unless-stopped diff --git a/Yadershiki/src/server/index.html b/Yadershiki/src/server/index.html deleted file mode 100644 index 75ebbf2..0000000 --- a/Yadershiki/src/server/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Yaderщики - - -

Hello World!

- - diff --git a/Yadershiki/src/server/locationcals.js b/Yadershiki/src/server/locationcals.js index c028e69..8c0a0b6 100644 --- a/Yadershiki/src/server/locationcals.js +++ b/Yadershiki/src/server/locationcals.js @@ -1,117 +1,213 @@ +import { EventEmitter } from 'events'; import getBeacons from './beacons.js'; +import * as math from 'mathjs'; -function addRSSIData(beaconCoordinates, rssiDataList) { - rssiDataList.forEach(rssiData => { - if (Array.isArray(rssiData) && rssiData.length === 2) { - const beaconName = rssiData[0]; - const rssiValue = rssiData[1]; - - const beacon = beaconCoordinates.find(b => b.name === beaconName); - if (beacon) { - beacon.RSSI = rssiValue; - } - } - }); - - return beaconCoordinates; + +class DistanceEstimator { + constructor(bufferSize = 20, measuredPower = -59, envFactor = 4) { + this.bufferSize = bufferSize; + this.measuredPower = measuredPower; + this.envFactor = envFactor; + this.buffers = { + logModel: [], + fspl: [], + linear: [] + }; + } + + rssiToDistanceLog(rssi) { + return Math.pow(10, (this.measuredPower - rssi) / (10 * this.envFactor)); + } + + rssiToDistanceFSPL(rssi, freqMHz = 2400) { + return Math.pow(10, (27.55 - (20 * Math.log10(freqMHz)) + Math.abs(rssi)) / 20.0); + } + + rssiToDistanceLinear(rssi) { + return Math.max(0.1, (this.measuredPower - rssi) * 0.1); + } + + addRSSI(rssi) { + const d1 = this.rssiToDistanceLog(rssi); + const d2 = this.rssiToDistanceFSPL(rssi); + const d3 = this.rssiToDistanceLinear(rssi); + + this._addToBuffer(this.buffers.logModel, d1); + this._addToBuffer(this.buffers.fspl, d2); + this._addToBuffer(this.buffers.linear, d3); + } + + _addToBuffer(buffer, value) { + if (buffer.length >= this.bufferSize) buffer.shift(); + buffer.push(value); + } + + getDistance() { + const averages = []; + for (const buf of Object.values(this.buffers)) { + if (buf.length > 0) { + const avg = buf.reduce((a, b) => a + b, 0) / buf.length; + averages.push(avg); + } + } + if (averages.length > 0) { + return averages.reduce((a, b) => a + b, 0) / averages.length; + } + return null; + } } -function sortBeaconsByRSSI(beaconCoordinates) { - return beaconCoordinates.filter((beacon) => beacon.RSSI !== undefined).sort((b, a) => { - return a.RSSI - b.RSSI; + +class BeaconTracker extends EventEmitter { + constructor(beacons, options = {}) { + super(); + this.beacons = this.normalizeBeacons(beacons); + this.bufferSize = options.bufferSize || 50; + this.rssiBuffers = {}; + this.estimators = {}; + this.updateInterval = options.updateInterval || 1000; + + setInterval(() => this.processBuffers(), this.updateInterval); + } + + normalizeBeacons(beaconsArray) { + const beaconsObj = {}; + beaconsArray.forEach(beacon => { + beaconsObj[beacon.name] = { + x: beacon.x, + y: beacon.y, + measuredPower: beacon.measuredPower || -59, + environmentalFactor: beacon.environmentalFactor || 3.0 + }; }); -} + return beaconsObj; + } -function getTopThreeBeacons(sortedBeacons) { - return sortedBeacons.slice(0, 3); -} + addRssi(beaconName, rssi) { + if (!this.rssiBuffers[beaconName]) this.rssiBuffers[beaconName] = []; + this.rssiBuffers[beaconName].push(rssi); + if (this.rssiBuffers[beaconName].length > this.bufferSize) this.rssiBuffers[beaconName].shift(); -function rssiToDistance(rssi, measuredPower = -59, environmentalFactor = 4) { - return Math.pow(10, (measuredPower - rssi) / (10 * environmentalFactor)); -} + if (!this.estimators[beaconName]) { + const beacon = this.beacons[beaconName]; + this.estimators[beaconName] = new DistanceEstimator( + this.bufferSize, + beacon ? beacon.measuredPower : -59, + beacon ? beacon.environmentalFactor : 2.0 + ); + } + this.estimators[beaconName].addRSSI(rssi); + } -function calculateBeaconDistances(topThreeBeacons) { - return topThreeBeacons.map(beacon => { - return { - ...beacon, - distance: rssiToDistance(beacon.RSSI) - }; + addRSSIData(rssiDataList) { + rssiDataList.forEach(rssiData => { + if (Array.isArray(rssiData) && rssiData.length === 2) { + const beaconName = rssiData[0]; + const rssiValue = rssiData[1]; + this.addRssi(beaconName, rssiValue); + } }); -} + } + + processBuffers() { + const distances = {}; + const usedBeacons = []; + + for (const [beaconName, samples] of Object.entries(this.rssiBuffers)) { + const beacon = this.beacons[beaconName]; + if (!beacon) continue; + + const estimator = this.estimators[beaconName]; + const distance = estimator ? estimator.getDistance() : null; + if (distance == null) continue; + + distances[beaconName] = distance; + usedBeacons.push({ + name: beaconName, + rssi: samples[samples.length - 1], + distance: distance, + x: beacon.x, + y: beacon.y + }); + } + + + if (Object.keys(distances).length >= 3) { + const pos = this.trilaterate(distances); + if (pos) this.emit('position', { position: pos, usedBeacons, timestamp: new Date().toISOString() }); + else console.log('Trilateration failed'); + } else { + console.log(`Not enough beacons for trilateration: ${Object.keys(distances).length}/3`); + } + } + + trilaterate(distances) { + const keys = Object.keys(distances); + if (keys.length < 3) return null; + + const bestBeacons = keys + .map(key => ({ key, distance: distances[key] })) + .sort((a, b) => a.distance - b.distance) + .slice(0, 3) + .map(item => item.key); + + const A = []; + const b = []; + const refKey = bestBeacons[0]; + const refBeacon = this.beacons[refKey]; + const refDist = distances[refKey]; + + for (let i = 1; i < bestBeacons.length; i++) { + const key = bestBeacons[i]; + const beacon = this.beacons[key]; + const dist = distances[key]; + + const dx = beacon.x - refBeacon.x; + const dy = beacon.y - refBeacon.y; + + A.push([2 * dx, 2 * dy]); + b.push( + Math.pow(refDist, 2) - Math.pow(dist, 2) - + Math.pow(refBeacon.x, 2) + Math.pow(beacon.x, 2) - + Math.pow(refBeacon.y, 2) + Math.pow(beacon.y, 2) + ); + } + + try { + const AT = math.transpose(A); + const ATA = math.multiply(AT, A); + const ATb = math.multiply(AT, b); + const sol = math.lusolve(ATA, ATb); -function trilaterateThreeCircles(circles) { - if (circles.length < 3) { - console.log("There are not enough circles for trilateration"); - return null; + const x = sol[0][0] + refBeacon.x; + const y = sol[1][0] + refBeacon.y; + return { x, y }; + } catch (error) { + console.log('Trilateration error:', error.message); + return null; } + } - const [c1, c2, c3] = circles.slice(0, 3); - - const A = c2.x - c1.x; - const B = c2.y - c1.y; - const C = c3.x - c1.x; - const D = c3.y - c1.y; - - const r1_sq = c1.distance * c1.distance; - const r2_sq = c2.distance * c2.distance; - const r3_sq = c3.distance * c3.distance; - - const right1 = (r1_sq - r2_sq + c2.x*c2.x - c1.x*c1.x + c2.y*c2.y - c1.y*c1.y) / 2; - const right2 = (r1_sq - r3_sq + c3.x*c3.x - c1.x*c1.x + c3.y*c3.y - c1.y*c1.y) / 2; - - const determinant = A * D - B * C; - - if (Math.abs(determinant) < 1e-10) { - console.log("The circles are degenerate or parallel, and it is impossible to find a point of intersection"); - return null; + getBufferStatus() { + const status = {}; + for (const [beaconName, buffer] of Object.entries(this.rssiBuffers)) { + const estimator = this.estimators[beaconName]; + status[beaconName] = { + samples: buffer.length, + lastRSSI: buffer.length > 0 ? buffer[buffer.length - 1] : null, + estimatedDistance: estimator ? estimator.getDistance() : null + }; } - - const x = (right1 * D - B * right2) / determinant; - const y = (A * right2 - right1 * C) / determinant; - - const maxX = Math.max(c1.x, c2.x, c3.x) + Math.max(c1.distance, c2.distance, c3.distance); - const minX = Math.min(c1.x, c2.x, c3.x) - Math.max(c1.distance, c2.distance, c3.distance); - const maxY = Math.max(c1.y, c2.y, c3.y) + Math.max(c1.distance, c2.distance, c3.distance); - const minY = Math.min(c1.y, c2.y, c3.y) - Math.max(c1.distance, c2.distance, c3.distance); - - // if (x < minX || x > maxX || y < minY || y > maxY) { - // console.log("The found point goes beyond reasonable limits"); - // return null; - // } - - return { x: x, y: y }; + return status; + } } +// === Фабрика трекера === +async function createTracker(dirname, options = {}) { + const beaconsArray = await getBeacons(dirname); + const tracker = new BeaconTracker(beaconsArray, options); -export default async function trilaterate(dirname, rssiData) { - const beacons = await getBeacons(dirname); - addRSSIData(beacons, rssiData); - const sortedBeacons = sortBeaconsByRSSI(beacons); - const topThreeBeacons = getTopThreeBeacons(sortedBeacons); - - const beaconsWithDistances = calculateBeaconDistances(topThreeBeacons); - - const estimatedPosition = trilaterateThreeCircles(beaconsWithDistances); - - return estimatedPosition; + return tracker; } -// 1. Поставьте приемник на 1 метр от маячка -// 2. Измерьте RSSI 10-20 раз -// 3. Возьмите среднее - -// function calibrateBeacon(beaconMac) { -// const measurements = [-58, -59, -57, -60, -58, -59]; -// const averageRSSI = measurements.reduce((a, b) => a + b) / measurements.length; - -// beaconConfigs[beaconMac] = { -// measuredPower: Math.round(averageRSSI), // ≈ -59 -// txPower: -4, -// type: 'ESP32-BEACON' -// }; - -// return averageRSSI; -// } - - -// trilaterate(".", rssi); \ No newline at end of file +export { BeaconTracker, createTracker }; diff --git a/Yadershiki/src/server/package.json b/Yadershiki/src/server/package.json index 8d74551..d8b7ce0 100644 --- a/Yadershiki/src/server/package.json +++ b/Yadershiki/src/server/package.json @@ -6,6 +6,7 @@ "dotenv": "^17.2.3", "express": "^5.1.0", "mqtt": "^5.14.1", + "mathjs": "^11.8.0", "path": "^0.12.7", "socket.io": "^4.8.1" }, diff --git a/Yadershiki/src/server/server.js b/Yadershiki/src/server/server.js index ad01249..f131b1d 100644 --- a/Yadershiki/src/server/server.js +++ b/Yadershiki/src/server/server.js @@ -7,14 +7,18 @@ import cors from 'cors'; import { fileURLToPath } from 'url'; import mqtt from 'mqtt'; import getBeacons from './beacons.js'; -import trilaterate from './locationcals.js'; +import { createTracker } from './locationcals.js'; // исправлен импорт +// Добавить эту функцию (или удалить её вызовы) +async function improvedTrilaterate(dirname, rssi) { + console.log('Legacy trilateration called with:', rssi); + return { x: 0, y: 0, error: 'legacy_method' }; +} dotenv.config(); const port = process.env.PORT; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); - const app = express(); app.use(compression()); app.use(cors()); @@ -22,6 +26,7 @@ app.use(bodyParser.json()); let clients = []; let state = []; +let tracker; console.log('Server starting'); console.log('Endpoints registered: /, /beacons, /api/state, /api/status'); @@ -36,6 +41,27 @@ const mqttClient = mqtt.connect(`mqtt://${mqttHost}:${mqttPort}`, { const mqttTopics = ['skynet/#', 'beacons/rssi']; + +async function initializeTracker() { + try { + tracker = await createTracker("."); + console.log('Beacon tracker initialized'); + + tracker.on('position', ({ position, usedBeacons, timestamp }) => { + console.log('New position:', position); + state = position; + notifyClients(); + }); + + tracker.on('error', (error) => { + console.error('Tracker error:', error); + }); + + } catch (error) { + console.error('Failed to initialize tracker:', error); + } +} + mqttClient.on('connect', () => { console.log('Connected to MQTT'); @@ -46,27 +72,38 @@ mqttClient.on('connect', () => { } else { console.log(`Subscribed on ${topic}`); } - }) + }); }); }); mqttClient.on('message', async (topic, message) => { - console.log(`MQTT recieved: [${topic}]`, message.toString()); + console.log(`MQTT received: [${topic}]`, message.toString()); try { const data = JSON.parse(message.toString()); console.log('Got data:', data); - const rssi = data.data || data; - state = await trilaterate(__dirname, rssi); - console.log('Updated state:', state); + if (topic === 'beacons/rssi') { + if (tracker) { + tracker.addRSSIData(data); + console.log('Data sent to tracker'); + } else { + console.log('Tracker not initialized yet, using legacy method'); + const rssi = data.data || data; + state = await improvedTrilaterate(__dirname, rssi); + notifyClients(); + } + } else { + const rssi = data.data || data; + state = await improvedTrilaterate(__dirname, rssi); + notifyClients(); + } - notifyClients(); - console.log('Clients notified'); + console.log('Updated state:', state); } catch (e) { console.log('Parsing error:', e.message); - state = [{ error: 'parse_error', message: message.toString() }]; + state = { error: 'parse_error', message: message.toString() }; notifyClients(); } }); @@ -76,7 +113,7 @@ mqttClient.on('error', (err) => { }); app.get('/', (req, res) => { - res.sendFile(path.join(__dirname, 'index.html')); + res.sendFile(path.join(__dirname, 'dist', 'index.html')); }); app.get('/api/position', (req, res) => { @@ -114,7 +151,7 @@ app.get('/api/position', (req, res) => { }); function genUniqueId(){ - return Date.now() + '-' + Math.floor(Math.random() * 1_000_000_000); + return Date.now() + '-' + Math.floor(Math.random() * 1_000_000_000); } function notifyClients() { @@ -126,7 +163,7 @@ function notifyClients() { client.res.write(sendData); client.res.flush(); } catch (err) { - console.log(`Error sendig to ${client.id}:`, err.message); + console.log(`Error sending to ${client.id}:`, err.message); disconnectedClients.push(index); } }); @@ -138,8 +175,8 @@ function notifyClients() { } app.get('/api/beacons', async (req, res) => { - const beacons = await getBeacons(__dirname); - res.json(beacons); + const beacons = await getBeacons(__dirname); + res.json(beacons); }); app.get('/api/state', (req, res) => { @@ -150,6 +187,17 @@ app.get('/api/state', (req, res) => { }); }); +app.get('/api/tracker-status', (req, res) => { + const bufferStatus = tracker ? tracker.getBufferStatus() : {}; + + res.json({ + trackerInitialized: !!tracker, + bufferStatus: bufferStatus, + clientsCount: clients.length, + timestamp: new Date().toISOString() + }); +}); + app.get('/api/messages', (req, res) => { res.json({ state: state, @@ -164,6 +212,7 @@ app.get('/api/status', (req, res) => { status: 'running', clients: clients.length, mqtt_connected: mqttClient ? mqttClient.connected : false, + tracker_initialized: !!tracker, timestamp: new Date().toISOString() }); }); @@ -171,7 +220,7 @@ app.get('/api/status', (req, res) => { app.get('/api/mqtt-info', (req, res) => { res.json({ connected: mqttClient ? mqttClient.connected : false, - topics: ['skynet/data', 'skynet/events', 'skynet/test'], + topics: mqttTopics, timestamp: new Date().toISOString() }); }); @@ -191,6 +240,24 @@ app.post('/api/test-mqtt', (req, res) => { }); }); +app.post('/api/simulate-beacons', (req, res) => { + const testData = req.body; + + if (tracker) { + tracker.addRSSIData(testData); + res.json({ + success: true, + message: 'Данные маяков отправлены в трекер', + data: testData + }); + } else { + res.status(500).json({ + success: false, + message: 'Трекер не инициализирован' + }); + } +}); + app.post('/api/simulate-mqtt', (req, res) => { const testData = req.body; @@ -208,6 +275,13 @@ app.get('/api/ping', (req, res) => { res.json({ status: 'ok', time: new Date().toISOString() }); }); -app.listen(port, () => { - console.log(`Starting server on ${port}`); -}); +// Запускаем сервер после инициализации +async function startServer() { + await initializeTracker(); + + app.listen(port, () => { + console.log(`Server started on port ${port}`); + }); +} + +startServer().catch(console.error); \ No newline at end of file diff --git a/Yadershiki/src/server/server.md b/Yadershiki/src/server/server.md new file mode 100644 index 0000000..85f6aac --- /dev/null +++ b/Yadershiki/src/server/server.md @@ -0,0 +1,54 @@ +## Подготовка окружения + +Перед запуском убедитесь, что установлены: +- **Docker** +- **Docker Compose** +- ESP32 устройства с прошитым кодом для сканирования BLE-маяков + + +Сборка окружения + +Запуск сервисов: + +docker-compose up + +Проверка работы контейнеров: + +docker-compose ps + + +Управление сервисами + +# Просмотр логов приложения +docker-compose logs app -f + +# Просмотр логов MQTT брокера +docker-compose logs emqx -f + +# Общие логи всех сервисов +docker-compose logs -f + +# Остановка всех сервисов +docker-compose down + +# Полная пересборка и запуск +docker-compose down +docker-compose build --no-cache +docker-compose up -d + + +Тестирование MQTT + +# Установка MQTT клиента +npm install -g mqtt + +# Подписка на топик для мониторинга данных +mqtt sub -t 'beacons/#' -h localhost -p 1883 -v + + +Проверка состояния API +# Проверка здоровья приложения +curl http://localhost:8080/health + +# Получение статуса буферов +curl http://localhost:8080/status \ No newline at end of file diff --git a/Yadershiki/src/server/skynet-mqtt-sender.js b/Yadershiki/src/server/skynet-mqtt-sender.js deleted file mode 100644 index 8e6f3ec..0000000 --- a/Yadershiki/src/server/skynet-mqtt-sender.js +++ /dev/null @@ -1,147 +0,0 @@ -import express from 'express'; -import mqtt from 'mqtt'; - -const app = express(); -const PORT = 8085; - -app.use(express.json()); - -// Подключаемся к EMQX (который в Docker) -// В MQTT болванке добавьте таймауты и реконнект -// Тоже используем переменные окружения -const mqttHost = process.env.MQTT_HOST || 'localhost'; -const mqttPort = process.env.MQTT_PORT || 1883; - -const mqttClient = mqtt.connect(`mqtt://${mqttHost}:${mqttPort}`, { - reconnectPeriod: 5000, - connectTimeout: 10000 -}); - -let mqttReady = false; - -mqttClient.on('connect', () => { - console.log('✅ MQTT болванка подключена к брокеру'); - mqttReady = true; -}); - -mqttClient.on('error', (err) => { - console.log('❌ Ошибка MQTT:', err.message); - mqttReady = false; -}); - -mqttClient.on('close', () => { - console.log('🔌 MQTT соединение закрыто'); - mqttReady = false; -}); - - -// Главная страница -app.get('/', (req, res) => { - res.json({ - service: 'MQTT Sender (Node.js)', - status: 'running', - mqtt_connected: mqttClient.connected, - endpoints: [ - 'POST /send-data', - 'POST /send-event', - 'GET /status' - ] - }); -}); - -// Статус подключения -app.get('/status', (req, res) => { - res.json({ - mqtt_connected: mqttClient.connected, - timestamp: new Date().toISOString() - }); -}); - -// Отправка данных -app.post('/send-data', (req, res) => { - const data = req.body; - - // Отправляем в формате который ожидает основной сервер - const message = data; // отправляем как есть, без обертки - - mqttClient.publish('skynet/data', JSON.stringify(message)); - console.log('📤 Отправлены RAW данные:', message); - - res.json({ success: true, message: 'Данные отправлены' }); -}); - -// Отправка событий -app.post('/send-event', (req, res) => { - if (!mqttClient.connected) { - return res.status(500).json({ error: 'MQTT не подключен' }); - } - - const { event, details } = req.body; - - const message = { - type: 'event', - event: event || 'unknown', - details: details || {}, - timestamp: new Date().toISOString(), - from: 'mqtt-sender' - }; - - mqttClient.publish('skynet/events', JSON.stringify(message)); - console.log('📤 Отправлено событие:', event); - - res.json({ - success: true, - message: 'Событие отправлено через MQTT', - sent: message - }); -}); - -// Автоматическая отправка тестовых данных -app.post('/test', (req, res) => { - if (!mqttClient.connected) { - return res.status(500).json({ error: 'MQTT не подключен' }); - } - - const testData = { - type: 'test', - value: Math.random() * 100, - timestamp: new Date().toISOString(), - from: 'mqtt-sender' - }; - - mqttClient.publish('skynet/test', JSON.stringify(testData)); - console.log('📤 Отправлен тест:', testData.value); - - res.json({ - success: true, - message: 'Тестовое сообщение отправлено', - sent: testData - }); -}); -process.on('SIGINT', () => { - console.log('🛑 Получен SIGINT. Завершаем работу...'); - - // Закрываем MQTT соединение - if (mqttClient) { - mqttClient.end(); - } - - // Закрываем HTTP сервер - process.exit(0); -}); - -process.on('SIGTERM', () => { - console.log('🛑 Получен SIGTERM. Завершаем работу...'); - - if (mqttClient) { - mqttClient.end(); - } - - process.exit(0); -}); -// Запуск сервера -app.listen(PORT, () => { - console.log(`🚀 MQTT болванка запущена на http://localhost:${PORT}`); - console.log(`📡 Подключается к MQTT брокеру: localhost:1883`); -}); - From 42249d226d37daeec3ba03b85a731821e62929c6 Mon Sep 17 00:00:00 2001 From: Matvey Solovev Date: Fri, 3 Oct 2025 11:56:43 +0700 Subject: [PATCH 33/34] add setup guide --- Yadershiki/SETUP.md | 5 +++++ Yadershiki/{set_up_client.md => SETUP_CLIENT.md} | 0 ...P32 Beacon Scanner + MQTT Publisher.md => SETUP_ESP32.md} | 0 Yadershiki/{src/server/server.md => SETUP_SERVER.md} | 0 4 files changed, 5 insertions(+) create mode 100644 Yadershiki/SETUP.md rename Yadershiki/{set_up_client.md => SETUP_CLIENT.md} (100%) rename Yadershiki/{ESP32 Beacon Scanner + MQTT Publisher.md => SETUP_ESP32.md} (100%) rename Yadershiki/{src/server/server.md => SETUP_SERVER.md} (100%) diff --git a/Yadershiki/SETUP.md b/Yadershiki/SETUP.md new file mode 100644 index 0000000..fd4d3cc --- /dev/null +++ b/Yadershiki/SETUP.md @@ -0,0 +1,5 @@ +# Разрвёртывание окружения +Чтобы развернуть окружение, следуйте инструкция в следующем порядке +1. Подготовка платы(см. SETUP_ESP32.md) +2. Подготовка клиента(см. SETUP_CLIENT.md) +3. Подготовка сервера(см. SETUP_SERVER.md) diff --git a/Yadershiki/set_up_client.md b/Yadershiki/SETUP_CLIENT.md similarity index 100% rename from Yadershiki/set_up_client.md rename to Yadershiki/SETUP_CLIENT.md diff --git a/Yadershiki/ESP32 Beacon Scanner + MQTT Publisher.md b/Yadershiki/SETUP_ESP32.md similarity index 100% rename from Yadershiki/ESP32 Beacon Scanner + MQTT Publisher.md rename to Yadershiki/SETUP_ESP32.md diff --git a/Yadershiki/src/server/server.md b/Yadershiki/SETUP_SERVER.md similarity index 100% rename from Yadershiki/src/server/server.md rename to Yadershiki/SETUP_SERVER.md From ef03781dfec41d8566363fdb5af97f67657ef1b0 Mon Sep 17 00:00:00 2001 From: Klim Sadov Date: Fri, 3 Oct 2025 12:16:14 +0700 Subject: [PATCH 34/34] Add files via upload --- Yadershiki/src/esp32/esp32.py | 273 ++++++++++++++++++++-------------- 1 file changed, 163 insertions(+), 110 deletions(-) diff --git a/Yadershiki/src/esp32/esp32.py b/Yadershiki/src/esp32/esp32.py index bbb68bb..d752b7f 100644 --- a/Yadershiki/src/esp32/esp32.py +++ b/Yadershiki/src/esp32/esp32.py @@ -1,110 +1,163 @@ -import bluetooth -from micropython import const -import time -import network -from umqtt.simple import MQTTClient -import config_esp32 - -_IRQ_SCAN_RESULT = const(5) -_IRQ_SCAN_DONE = const(6) -_ADV_TYPE_NAME = const(0x09) - -ble = bluetooth.BLE() -ble.active(True) - -beacons = {} -scan_done = False - -def decode_name(adv_data): - adv_bytes = bytes(adv_data) - i = 0 - while i + 1 < len(adv_bytes): - length = adv_bytes[i] - if length == 0: - break - type = adv_bytes[i + 1] - if type == _ADV_TYPE_NAME: - return adv_bytes[i + 2 : i + 1 + length].decode("utf-8") - i += 1 + length - return None - -def bt_irq(event, data): - global beacons, scan_done - if event == _IRQ_SCAN_RESULT: - addr_type, addr, adv_type, rssi, adv_data = data - name = decode_name(adv_data) - if name and name.startswith(config_esp32.BEACON_PREFIX): - if name not in beacons or rssi > beacons[name]: - beacons[name] = rssi - elif event == _IRQ_SCAN_DONE: - scan_done = True - -def scan_once(duration_ms=config_esp32.SCAN_DURATION_MS): - global beacons, scan_done - beacons = {} - scan_done = False - ble.irq(bt_irq) - ble.gap_scan(duration_ms, 30000, 30000) - - while not scan_done: - time.sleep_ms(config_esp32.SCAN_FREQ) - - beacons_sorted = sorted(beacons.items(), key=lambda x: x[1], reverse=True) - return [[name, rssi] for name, rssi in beacons_sorted] - -def publish_beacons_data(beacons_data): - try: - client = MQTTClient(config_esp32.CLIENT_ID, config_esp32.MQTT_BROKER, port=config_esp32.MQTT_PORT) - client.connect() - beacons_data = [["maxim", -34], ["matvey", -45]] - if not beacons_data: - data_str = "NO_BEACONS_FOUND" - print("Маяки не найдены, отправляем сообщение об отсутствии") - else: - data_str = str(beacons_data).replace("'", '"') - print(f"Найдено маяков: {len(beacons_data)}") - - client.publish(config_esp32.MQTT_TOPIC, data_str) - print(f"Опубликовано в топик {config_esp32.MQTT_TOPIC}: {data_str}") - - client.disconnect() - return True - - except Exception as e: - print(f"Ошибка публикации: {e}") - return False - -def connect_wifi(): - wlan = network.WLAN(network.STA_IF) - wlan.active(True) - - if not wlan.isconnected(): - print(f"Подключаемся к Wi-Fi {config_esp32.WIFI_SSID}...") - wlan.connect(config_esp32.WIFI_SSID, config_esp32.WIFI_PASSWORD) - - for i in range(10): - if wlan.isconnected(): - break - print('Ожидание...') - time.sleep(1) - - return wlan - - -wlan = connect_wifi() - -if wlan.isconnected(): - print("Успешно подключено к Wi-Fi") - print('IP адрес:', wlan.ifconfig()[0]) - - while True: - try: - beacons_data = scan_once(duration_ms=config_esp32.SCAN_DURATION_MS) - publish_beacons_data(beacons_data) - time.sleep(config_esp32.SCAN_INTERVAL) - - except Exception as e: - print(f"Ошибка: {e}") - time.sleep(5) -else: - print("Не удалось подключиться к Wi-Fi") + +import bluetooth +from micropython import const +import time +import network +from umqtt.simple import MQTTClient +import config_esp32 + +_IRQ_SCAN_RESULT = const(5) +_IRQ_SCAN_DONE = const(6) +_ADV_TYPE_NAME = const(0x09) + +ble = bluetooth.BLE() +ble.active(True) + +beacons_history = {} +HISTORY_SIZE = 6 + +scan_done = False + +def decode_name(adv_data): + adv_bytes = bytes(adv_data) + i = 0 + while i + 1 < len(adv_bytes): + length = adv_bytes[i] + if length == 0: + break + type = adv_bytes[i + 1] + if type == _ADV_TYPE_NAME: + return adv_bytes[i + 2 : i + 1 + length].decode("utf-8") + i += 1 + length + return None + +def bt_irq(event, data): + global beacons_history, scan_done + if event == _IRQ_SCAN_RESULT: + addr_type, addr, adv_type, rssi, adv_data = data + name = decode_name(adv_data) + if name and name.startswith(config_esp32.BEACON_PREFIX): + if name not in beacons_history: + beacons_history[name] = [] + + beacons_history[name].append(rssi) + + if len(beacons_history[name]) > HISTORY_SIZE: + beacons_history[name].pop(0) + + elif event == _IRQ_SCAN_DONE: + scan_done = True + +def calculate_median(values): + """Вычисляет медиану из списка значений""" + if not values: + return None + + sorted_values = sorted(values) + n = len(sorted_values) + + if n % 2 == 1: + return sorted_values[n // 2] + else: + return (sorted_values[n // 2 - 1] + sorted_values[n // 2]) // 2 + +def scan_once(duration_ms=config_esp32.SCAN_DURATION_MS): + global beacons_history, scan_done + scan_done = False + ble.irq(bt_irq) + ble.gap_scan(duration_ms, 30000, 30000) + + while not scan_done: + time.sleep_ms(config_esp32.SCAN_FREQ) + + beacons_processed = {} + + all_beacons = {} + for name, rssi_list in beacons_history.items(): + if rssi_list: + all_beacons[name] = rssi_list[-1] + + + beacons_sorted = sorted(all_beacons.items(), key=lambda x: x[1], reverse=True) + + + top_3_names = [name for name, _ in beacons_sorted[:3]] + for name in top_3_names: + rssi_list = beacons_history[name] + if len(rssi_list) >= 3: + median_rssi = calculate_median(rssi_list) + beacons_processed[name] = median_rssi + else: + beacons_processed[name] = rssi_list[-1] if rssi_list else 0 + + for name, last_rssi in beacons_sorted[3:]: + beacons_processed[name] = last_rssi + + beacons_final_sorted = sorted(beacons_processed.items(), key=lambda x: x[1], reverse=True) + return [[name, rssi] for name, rssi in beacons_final_sorted] + +def publish_beacons_data(beacons_data): + try: + client = MQTTClient(config_esp32.CLIENT_ID, config_esp32.MQTT_BROKER, port=config_esp32.MQTT_PORT) + client.connect() + if not beacons_data: + data_str = "NO_BEACONS_FOUND" + print("Маяки не найдены, отправляем сообщение об отсутствии") + else: + data_str = str(beacons_data).replace("'", '"') + print(f"Найдено маяков: {len(beacons_data)}") + + for i, (name, rssi) in enumerate(beacons_data): + history_len = len(beacons_history.get(name, [])) + if i < 3: + marker = "TOP 3" + method = "медиана" + else: + marker = "LAST" + method = "последнее значение" + print(f" {marker} {name}: RSSI={rssi} dBm ({history_len} изм., {method})") + + client.publish(config_esp32.MQTT_TOPIC, data_str) + print(f"Опубликовано в топик {config_esp32.MQTT_TOPIC}") + + client.disconnect() + return True + + except Exception as e: + print(f"Ошибка публикации: {e}") + return False + +def connect_wifi(): + wlan = network.WLAN(network.STA_IF) + wlan.active(True) + + if not wlan.isconnected(): + print(f"Подключаемся к Wi-Fi {config_esp32.WIFI_SSID}...") + wlan.connect(config_esp32.WIFI_SSID, config_esp32.WIFI_PASSWORD) + + for i in range(10): + if wlan.isconnected(): + break + print('Ожидание...') + time.sleep(1) + + return wlan + +wlan = connect_wifi() + +if wlan.isconnected(): + print("Успешно подключено к Wi-Fi") + print('IP адрес:', wlan.ifconfig()[0]) + + + while True: + try: + beacons_data = scan_once(duration_ms=config_esp32.SCAN_DURATION_MS) + publish_beacons_data(beacons_data) + time.sleep(config_esp32.SCAN_INTERVAL) + + except Exception as e: + print(f"Ошибка: {e}") + time.sleep(5) +else: + print("Не удалось подключиться к Wi-Fi") \ No newline at end of file