Läuft mal mit kontinuierlicher Kuppelposition
This commit is contained in:
18
.vscode/launch.json
vendored
Normal file
18
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Launch Program",
|
||||||
|
"skipFiles": [
|
||||||
|
"<node_internals>/**"
|
||||||
|
],
|
||||||
|
"program": "${workspaceFolder}/JS/server.js"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
256
JS/package-lock.json
generated
256
JS/package-lock.json
generated
@@ -8,7 +8,32 @@
|
|||||||
"name": "observatory-sim",
|
"name": "observatory-sim",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^4.18.2"
|
"express": "^4.18.2",
|
||||||
|
"socket.io": "^4.8.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.2.tgz",
|
||||||
|
"integrity": "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~7.12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/accepts": {
|
"node_modules/accepts": {
|
||||||
@@ -30,6 +55,15 @@
|
|||||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
||||||
"license": "MIT"
|
"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/body-parser": {
|
"node_modules/body-parser": {
|
||||||
"version": "1.20.3",
|
"version": "1.20.3",
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
|
||||||
@@ -128,6 +162,19 @@
|
|||||||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
|
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/cors": {
|
||||||
|
"version": "2.8.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
||||||
|
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"object-assign": "^4",
|
||||||
|
"vary": "^1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "2.6.9",
|
"version": "2.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
@@ -185,6 +232,67 @@
|
|||||||
"node": ">= 0.8"
|
"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/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/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/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/es-define-property": {
|
"node_modules/es-define-property": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||||
@@ -521,6 +629,15 @@
|
|||||||
"node": ">= 0.6"
|
"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": {
|
"node_modules/object-inspect": {
|
||||||
"version": "1.13.4",
|
"version": "1.13.4",
|
||||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||||
@@ -770,6 +887,116 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"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/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/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-parser/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/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/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/statuses": {
|
"node_modules/statuses": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||||
@@ -801,6 +1028,12 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "7.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.12.0.tgz",
|
||||||
|
"integrity": "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/unpipe": {
|
"node_modules/unpipe": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||||
@@ -827,6 +1060,27 @@
|
|||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
"start": "node server.js"
|
"start": "node server.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^4.18.2"
|
"express": "^4.18.2",
|
||||||
|
"socket.io": "^4.8.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
142
JS/public/dome.js
Normal file
142
JS/public/dome.js
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
class Dome {
|
||||||
|
constructor(socketUrl) {
|
||||||
|
// Verhindere mehrfache Instanzen
|
||||||
|
if (window.activeDomeInstance) {
|
||||||
|
console.warn('Dome instance already exists! Stopping old one...');
|
||||||
|
window.activeDomeInstance.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.angle = 0; // Grad (Mitte des Spalts)
|
||||||
|
this.slotWidth = 20; // Spaltbreite in Grad
|
||||||
|
this.domeSpeed = 3.0; // Grad pro Frame (für smoothe animation)
|
||||||
|
this.telescopeAngle = 0;
|
||||||
|
|
||||||
|
// State Machine für diskrete Bewegung (wie Original)
|
||||||
|
this.state = "IDLE"; // "IDLE" oder "MOVING"
|
||||||
|
this.targetDomeAngle = this.angle;
|
||||||
|
|
||||||
|
this.socket = io(socketUrl);
|
||||||
|
|
||||||
|
// Registriere als aktive Instanz
|
||||||
|
window.activeDomeInstance = this;
|
||||||
|
|
||||||
|
this.setupSocketListeners();
|
||||||
|
this.startTracking();
|
||||||
|
}
|
||||||
|
|
||||||
|
setupSocketListeners() {
|
||||||
|
this.socket.on('connect', () => {
|
||||||
|
console.log('Dome connected to server');
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socket.on('telescope-position', (data) => {
|
||||||
|
this.updateTelescopePosition(data.angle);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
startTracking() {
|
||||||
|
this.isTracking = true;
|
||||||
|
|
||||||
|
// Tracking-Loop (30x pro Sekunde für smooth movement)
|
||||||
|
this.trackingInterval = setInterval(() => {
|
||||||
|
if (this.isTracking) {
|
||||||
|
this.updateDomePosition();
|
||||||
|
}
|
||||||
|
}, 1000 / 30); // 30 FPS für sehr smooth movement
|
||||||
|
|
||||||
|
// Position broadcasting (10x pro Sekunde für responsive Visualisierung)
|
||||||
|
this.broadcastInterval = setInterval(() => {
|
||||||
|
this.broadcastPosition();
|
||||||
|
}, 100); // 10x pro Sekunde
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTelescopePosition(telescopeAngle) {
|
||||||
|
this.telescopeAngle = telescopeAngle;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDomePosition() {
|
||||||
|
let diff = this.angleDiff(this.telescopeAngle, this.angle);
|
||||||
|
let halfSlot = this.slotWidth / 2;
|
||||||
|
let triggerPoint = halfSlot - 2.0; // Starte früher! (8° statt 10°)
|
||||||
|
let moveDir = "STOP";
|
||||||
|
|
||||||
|
if (this.state === "IDLE") {
|
||||||
|
// Prüfen, ob Teleskop aus Schlitz läuft - aber früher triggern!
|
||||||
|
if (diff > triggerPoint) {
|
||||||
|
// CW-Bewegung → Kuppel zentriert sich um das Teleskop
|
||||||
|
this.targetDomeAngle = (this.telescopeAngle + 360) % 360;
|
||||||
|
this.state = "MOVING";
|
||||||
|
console.log(`[Dome] START MOVING: Tel out of slot CW (${diff.toFixed(1)}° > ${triggerPoint.toFixed(1)}°), target: ${this.targetDomeAngle.toFixed(1)}° (centered on telescope)`);
|
||||||
|
} else if (diff < -triggerPoint) {
|
||||||
|
// CCW-Bewegung → Kuppel zentriert sich um das Teleskop
|
||||||
|
this.targetDomeAngle = (this.telescopeAngle + 360) % 360;
|
||||||
|
this.state = "MOVING";
|
||||||
|
console.log(`[Dome] START MOVING: Tel out of slot CCW (${diff.toFixed(1)}° < ${-triggerPoint.toFixed(1)}°), target: ${this.targetDomeAngle.toFixed(1)}° (centered on telescope)`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state === "MOVING") {
|
||||||
|
let delta = this.angleDiff(this.targetDomeAngle, this.angle);
|
||||||
|
if (Math.abs(delta) > this.domeSpeed) {
|
||||||
|
this.angle = (this.angle + Math.sign(delta) * this.domeSpeed + 360) % 360;
|
||||||
|
moveDir = Math.sign(delta) > 0 ? "CW" : "CCW";
|
||||||
|
} else {
|
||||||
|
// Ziel erreicht
|
||||||
|
this.angle = this.targetDomeAngle;
|
||||||
|
this.state = "IDLE";
|
||||||
|
console.log(`[Dome] REACHED TARGET: ${this.angle.toFixed(1)}°, back to IDLE`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug nur bei Aktivität
|
||||||
|
if (moveDir !== "STOP") {
|
||||||
|
console.log(`[Dome] ${this.state}: Tel=${this.telescopeAngle.toFixed(1)}°, Dome=${this.angle.toFixed(1)}° → ${this.targetDomeAngle.toFixed(1)}°, Dir=${moveDir}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Winkeldifferenz berechnen - kürzester Weg zwischen zwei Winkeln
|
||||||
|
angleDiff(target, source) {
|
||||||
|
// Standard-Formel für kürzesten Winkel
|
||||||
|
let diff = target - source;
|
||||||
|
|
||||||
|
// Normalisiere auf -180 bis +180
|
||||||
|
while (diff > 180) diff -= 360;
|
||||||
|
while (diff < -180) diff += 360;
|
||||||
|
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcastPosition() {
|
||||||
|
const data = {
|
||||||
|
angle: this.angle,
|
||||||
|
slotWidth: this.slotWidth,
|
||||||
|
telescopeAngle: this.telescopeAngle,
|
||||||
|
diff: this.angleDiff(this.telescopeAngle, this.angle),
|
||||||
|
timestamp: Date.now()
|
||||||
|
};
|
||||||
|
this.socket.emit('dome-position', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
this.isTracking = false;
|
||||||
|
if (this.trackingInterval) clearInterval(this.trackingInterval);
|
||||||
|
if (this.broadcastInterval) clearInterval(this.broadcastInterval);
|
||||||
|
|
||||||
|
// Entferne Referenz als aktive Instanz
|
||||||
|
if (window.activeDomeInstance === this) {
|
||||||
|
window.activeDomeInstance = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wenn wir in einem Browser sind, NICHT automatisch initialisieren
|
||||||
|
// Die Visualisierung wird die Kuppel kontrollieren
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
// Nur die Klasse verfügbar machen, aber noch nicht instanziieren
|
||||||
|
window.Dome = Dome;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node.js Export für separate Prozesse
|
||||||
|
if (typeof module !== 'undefined' && module.exports) {
|
||||||
|
module.exports = Dome;
|
||||||
|
}
|
||||||
@@ -2,13 +2,56 @@
|
|||||||
<html lang="de">
|
<html lang="de">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<title>Kuppel-Simulation (JS)</title>
|
<title>Observatory Simulation - Multi-Task System</title>
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="style.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h2>Kuppel-Simulation mit NFC-Tags (JavaScript)</h2>
|
<h2>Observatory Simulation - Distributed Tasks</h2>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="visualization">
|
||||||
<canvas id="simCanvas" width="700" height="700"></canvas>
|
<canvas id="simCanvas" width="700" height="700"></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="controls">
|
||||||
|
<div class="task-section">
|
||||||
|
<h3>🔭 Teleskop-Steuerung</h3>
|
||||||
|
<div class="button-group">
|
||||||
|
<button onclick="adjustTelescope(-45)">↺ 45° CCW</button>
|
||||||
|
<button onclick="adjustTelescope(-15)">↺ 15° CCW</button>
|
||||||
|
<button onclick="adjustTelescope(15)">↻ 15° CW</button>
|
||||||
|
<button onclick="adjustTelescope(45)">↻ 45° CW</button>
|
||||||
|
</div>
|
||||||
|
<div class="status" id="telescope-status">
|
||||||
|
Position: 0.0° | Speed: 1 U/min
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="task-section">
|
||||||
|
<h3>🏠 Kuppel-Tracking</h3>
|
||||||
|
<div class="status" id="dome-status">
|
||||||
|
Position: 0.0° | Tracking: Standby
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="task-section">
|
||||||
|
<h3>📊 System Status</h3>
|
||||||
|
<div class="status" id="system-status">
|
||||||
|
WebSocket: Disconnected
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="debug-info">
|
||||||
|
<h4>Debug Information</h4>
|
||||||
<pre id="debug"></pre>
|
<pre id="debug"></pre>
|
||||||
<script src="script.js"></script>
|
</div>
|
||||||
|
|
||||||
|
<!-- WebSocket & Task Scripts -->
|
||||||
|
<script src="/socket.io/socket.io.js"></script>
|
||||||
|
<script src="telescope.js"></script>
|
||||||
|
<script src="dome.js"></script>
|
||||||
|
<script src="visualization.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
126
JS/public/style.css
Normal file
126
JS/public/style.css
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
color: #e0e0e0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2, h3 {
|
||||||
|
color: #4a9eff;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
gap: 30px;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.visualization {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#simCanvas {
|
||||||
|
border: 2px solid #444;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: #2a2a2a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-section {
|
||||||
|
background-color: #2a2a2a;
|
||||||
|
border: 1px solid #444;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-group {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: #4a9eff;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 10px 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: #357abd;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:active {
|
||||||
|
transform: translateY(1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
font-family: 'Monaco', 'Menlo', monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #a0a0a0;
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border-left: 3px solid #4a9eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.debug-info {
|
||||||
|
margin-top: 30px;
|
||||||
|
background-color: #2a2a2a;
|
||||||
|
border: 1px solid #444;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#debug {
|
||||||
|
font-family: 'Monaco', 'Menlo', monospace;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 1.4;
|
||||||
|
color: #c0c0c0;
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: auto;
|
||||||
|
max-height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design */
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.container {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
max-width: 700px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
body {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#simCanvas {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-group {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
118
JS/public/telescope.js
Normal file
118
JS/public/telescope.js
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
class Telescope {
|
||||||
|
constructor(socketUrl) {
|
||||||
|
// Verhindere mehrfache Instanzen
|
||||||
|
if (window.activeTelescopeInstance) {
|
||||||
|
console.warn('Telescope instance already exists! Stopping old one...');
|
||||||
|
window.activeTelescopeInstance.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.angle = 0; // Grad
|
||||||
|
this.rotationSpeed = 6; // Grad pro Sekunde (1 Umdrehung = 360° pro Minute = 6°/sec)
|
||||||
|
this.socket = io(socketUrl);
|
||||||
|
this.isRunning = false;
|
||||||
|
|
||||||
|
// Registriere als aktive Instanz
|
||||||
|
window.activeTelescopeInstance = this;
|
||||||
|
|
||||||
|
this.setupSocketListeners();
|
||||||
|
this.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
setupSocketListeners() {
|
||||||
|
this.socket.on('connect', () => {
|
||||||
|
console.log('Telescope connected to server');
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socket.on('telescope-control', (data) => {
|
||||||
|
this.handleControl(data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
this.isRunning = true;
|
||||||
|
|
||||||
|
// Kontinuierliche Rotation (1 U/min = 6°/sec)
|
||||||
|
// Vernünftige Frequenz für gleichmäßige aber nicht chaotische Bewegung
|
||||||
|
this.rotationInterval = setInterval(() => {
|
||||||
|
if (this.isRunning) {
|
||||||
|
this.angle = (this.angle + this.rotationSpeed / 60) % 360; // Update 60x pro Sekunde
|
||||||
|
}
|
||||||
|
}, 1000 / 60); // 60 FPS - das ist völlig ausreichend
|
||||||
|
|
||||||
|
// Position senden (1x pro Sekunde - Visualisierung interpoliert lokal)
|
||||||
|
this.broadcastInterval = setInterval(() => {
|
||||||
|
if (this.isRunning) {
|
||||||
|
this.broadcastPosition();
|
||||||
|
}
|
||||||
|
}, 1000); // Zurück zu 1 Sekunde
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
this.isRunning = false;
|
||||||
|
if (this.rotationInterval) clearInterval(this.rotationInterval);
|
||||||
|
if (this.broadcastInterval) clearInterval(this.broadcastInterval);
|
||||||
|
|
||||||
|
// Entferne Referenz als aktive Instanz
|
||||||
|
if (window.activeTelescopeInstance === this) {
|
||||||
|
window.activeTelescopeInstance = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcastPosition() {
|
||||||
|
const data = {
|
||||||
|
angle: this.angle,
|
||||||
|
timestamp: Date.now()
|
||||||
|
};
|
||||||
|
this.socket.emit('telescope-position', data);
|
||||||
|
console.log(`[Telescope] Broadcasting position: ${this.angle.toFixed(1)}°`);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleControl(data) {
|
||||||
|
switch (data.command) {
|
||||||
|
case 'adjust':
|
||||||
|
this.adjustPosition(data.degrees);
|
||||||
|
break;
|
||||||
|
case 'stop':
|
||||||
|
this.stop();
|
||||||
|
break;
|
||||||
|
case 'start':
|
||||||
|
this.start();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
adjustPosition(degrees) {
|
||||||
|
this.angle = (this.angle + degrees + 360) % 360;
|
||||||
|
console.log(`Telescope adjusted by ${degrees}°, new position: ${this.angle.toFixed(1)}°`);
|
||||||
|
this.broadcastPosition(); // Sofort neue Position senden
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manuelle Steuerung für Testing
|
||||||
|
adjustBy15CW() {
|
||||||
|
this.adjustPosition(15);
|
||||||
|
}
|
||||||
|
|
||||||
|
adjustBy15CCW() {
|
||||||
|
this.adjustPosition(-15);
|
||||||
|
}
|
||||||
|
|
||||||
|
adjustBy45CW() {
|
||||||
|
this.adjustPosition(45);
|
||||||
|
}
|
||||||
|
|
||||||
|
adjustBy45CCW() {
|
||||||
|
this.adjustPosition(-45);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wenn wir in einem Browser sind, NICHT automatisch initialisieren
|
||||||
|
// Die Visualisierung wird das Teleskop kontrollieren
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
// Nur die Klasse verfügbar machen, aber noch nicht instanziieren
|
||||||
|
window.Telescope = Telescope;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node.js Export für separate Prozesse
|
||||||
|
if (typeof module !== 'undefined' && module.exports) {
|
||||||
|
module.exports = Telescope;
|
||||||
|
}
|
||||||
322
JS/public/visualization.js
Normal file
322
JS/public/visualization.js
Normal file
@@ -0,0 +1,322 @@
|
|||||||
|
class Visualization {
|
||||||
|
constructor() {
|
||||||
|
this.canvas = document.getElementById('simCanvas');
|
||||||
|
this.ctx = this.canvas.getContext('2d');
|
||||||
|
this.width = 700;
|
||||||
|
this.height = 700;
|
||||||
|
this.center = { x: this.width / 2, y: this.height / 2 };
|
||||||
|
this.radius = 260;
|
||||||
|
this.tagCount = 36;
|
||||||
|
|
||||||
|
// Aktuelle Positionen
|
||||||
|
this.telescopeAngle = 0;
|
||||||
|
this.domeAngle = 0;
|
||||||
|
this.slotWidth = 20;
|
||||||
|
this.telescopeData = null;
|
||||||
|
this.domeData = null;
|
||||||
|
|
||||||
|
// Lokale Teleskop-Interpolation für flüssige Bewegung
|
||||||
|
this.localTelescopeAngle = 0;
|
||||||
|
this.telescopeSpeed = 6; // 6°/sec = 1 U/min
|
||||||
|
this.lastUpdateTime = Date.now();
|
||||||
|
|
||||||
|
// Lokale Kuppel-Interpolation für flüssige Bewegung
|
||||||
|
this.localDomeAngle = 0;
|
||||||
|
this.domeSpeed = 720; // 720°/sec = 2 U/sec wie in der Kuppel-Logik
|
||||||
|
this.lastDomeUpdateTime = Date.now();
|
||||||
|
|
||||||
|
this.socket = io('http://localhost:3005');
|
||||||
|
this.setupSocketListeners();
|
||||||
|
this.startAnimation();
|
||||||
|
this.updateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
setupSocketListeners() {
|
||||||
|
this.socket.on('connect', () => {
|
||||||
|
console.log('Visualization connected to server');
|
||||||
|
this.updateSystemStatus('WebSocket: Connected');
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socket.on('disconnect', () => {
|
||||||
|
this.updateSystemStatus('WebSocket: Disconnected');
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socket.on('telescope-position', (data) => {
|
||||||
|
console.log(`[Visualization] Received telescope position: ${data.angle.toFixed(1)}°`);
|
||||||
|
this.telescopeData = data;
|
||||||
|
this.telescopeAngle = data.angle;
|
||||||
|
|
||||||
|
// Weiche Synchronisation: nur korrigieren wenn Differenz groß ist
|
||||||
|
const diff = Math.abs(data.angle - this.localTelescopeAngle);
|
||||||
|
const adjustedDiff = Math.min(diff, 360 - diff); // Berücksichtige Überlauf bei 360°
|
||||||
|
|
||||||
|
if (adjustedDiff > 3) {
|
||||||
|
// Große Abweichung (z.B. durch Button-Klick) -> sofort korrigieren
|
||||||
|
console.log(`[Visualization] Large sync correction: ${adjustedDiff.toFixed(1)}°`);
|
||||||
|
this.localTelescopeAngle = data.angle;
|
||||||
|
} else {
|
||||||
|
// Kleine Abweichung -> langsam angleichen um Blitzen zu vermeiden
|
||||||
|
const correctionFactor = 0.1; // 10% Korrektur pro Update
|
||||||
|
let targetAngle = data.angle;
|
||||||
|
|
||||||
|
// Handle 360° wrap-around
|
||||||
|
if (Math.abs(targetAngle - this.localTelescopeAngle) > 180) {
|
||||||
|
if (targetAngle > this.localTelescopeAngle) {
|
||||||
|
targetAngle -= 360;
|
||||||
|
} else {
|
||||||
|
targetAngle += 360;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.localTelescopeAngle += (targetAngle - this.localTelescopeAngle) * correctionFactor;
|
||||||
|
this.localTelescopeAngle = (this.localTelescopeAngle + 360) % 360;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastUpdateTime = Date.now();
|
||||||
|
this.updateTelescopeStatus();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socket.on('dome-position', (data) => {
|
||||||
|
this.domeData = data;
|
||||||
|
this.domeAngle = data.angle;
|
||||||
|
this.slotWidth = data.slotWidth;
|
||||||
|
|
||||||
|
console.log(`[Visualization] Received dome position: ${data.angle.toFixed(1)}°`);
|
||||||
|
|
||||||
|
// SOFORTIGE Synchronisation für die Kuppel (keine weiche Angleichung)
|
||||||
|
// Die Kuppel soll genau das anzeigen, was sie wirklich ist
|
||||||
|
this.localDomeAngle = data.angle;
|
||||||
|
this.lastDomeUpdateTime = Date.now();
|
||||||
|
|
||||||
|
this.updateDomeStatus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
startAnimation() {
|
||||||
|
// 60 FPS Animation
|
||||||
|
setInterval(() => {
|
||||||
|
this.draw();
|
||||||
|
}, 1000 / 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
// Lokale Teleskop-Interpolation aktualisieren (für flüssige Bewegung)
|
||||||
|
const now = Date.now();
|
||||||
|
const deltaTime = (now - this.lastUpdateTime) / 1000; // in Sekunden
|
||||||
|
this.localTelescopeAngle = (this.localTelescopeAngle + this.telescopeSpeed * deltaTime) % 360;
|
||||||
|
this.lastUpdateTime = now;
|
||||||
|
|
||||||
|
// KUPPEL: Keine lokale Interpolation! Zeige nur echte Position
|
||||||
|
// Die Kuppel bewegt sich nur diskret, wenn das Teleskop den Spalt verlässt
|
||||||
|
|
||||||
|
// Canvas löschen
|
||||||
|
this.ctx.fillStyle = '#2a2a2a';
|
||||||
|
this.ctx.fillRect(0, 0, this.width, this.height);
|
||||||
|
|
||||||
|
// NFC-Tags zeichnen
|
||||||
|
this.drawTags();
|
||||||
|
|
||||||
|
// Kuppel-Spalt zeichnen (verwendet echte Position, keine Interpolation)
|
||||||
|
this.drawDomeSlot();
|
||||||
|
|
||||||
|
// Teleskop zeichnen (verwendet lokale Interpolation)
|
||||||
|
this.drawTelescope();
|
||||||
|
|
||||||
|
// Zentrum
|
||||||
|
this.drawCenter();
|
||||||
|
|
||||||
|
// Debug-Info aktualisieren
|
||||||
|
this.updateDebugInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
drawTags() {
|
||||||
|
this.ctx.fillStyle = '#666';
|
||||||
|
for (let i = 0; i < this.tagCount; i++) {
|
||||||
|
const angle = 2 * Math.PI * i / this.tagCount; // Positive für Uhrzeigersinn
|
||||||
|
const x = this.center.x + Math.cos(angle) * this.radius;
|
||||||
|
const y = this.center.y + Math.sin(angle) * this.radius;
|
||||||
|
|
||||||
|
this.ctx.beginPath();
|
||||||
|
this.ctx.arc(x, y, 4, 0, 2 * Math.PI);
|
||||||
|
this.ctx.fill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drawDomeSlot() {
|
||||||
|
const midAngle = this.localDomeAngle * Math.PI / 180; // Verwendet sofortigen Sync ohne Interpolation
|
||||||
|
const halfSlot = this.slotWidth * Math.PI / 360;
|
||||||
|
|
||||||
|
this.ctx.strokeStyle = '#4a9eff';
|
||||||
|
this.ctx.lineWidth = 12;
|
||||||
|
this.ctx.beginPath();
|
||||||
|
this.ctx.arc(
|
||||||
|
this.center.x,
|
||||||
|
this.center.y,
|
||||||
|
this.radius,
|
||||||
|
midAngle - halfSlot,
|
||||||
|
midAngle + halfSlot
|
||||||
|
);
|
||||||
|
this.ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
drawTelescope() {
|
||||||
|
const angle = this.localTelescopeAngle * Math.PI / 180; // Verwende lokale Interpolation
|
||||||
|
const length = this.radius - 60;
|
||||||
|
|
||||||
|
const endX = this.center.x + Math.cos(angle) * length;
|
||||||
|
const endY = this.center.y + Math.sin(angle) * length;
|
||||||
|
|
||||||
|
// Linie
|
||||||
|
this.ctx.strokeStyle = '#ff4444';
|
||||||
|
this.ctx.lineWidth = 6;
|
||||||
|
this.ctx.beginPath();
|
||||||
|
this.ctx.moveTo(this.center.x, this.center.y);
|
||||||
|
this.ctx.lineTo(endX, endY);
|
||||||
|
this.ctx.stroke();
|
||||||
|
|
||||||
|
// Endpunkt
|
||||||
|
this.ctx.fillStyle = '#ff4444';
|
||||||
|
this.ctx.beginPath();
|
||||||
|
this.ctx.arc(endX, endY, 8, 0, 2 * Math.PI);
|
||||||
|
this.ctx.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
drawCenter() {
|
||||||
|
this.ctx.fillStyle = '#333';
|
||||||
|
this.ctx.beginPath();
|
||||||
|
this.ctx.arc(this.center.x, this.center.y, 6, 0, 2 * Math.PI);
|
||||||
|
this.ctx.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTelescopeStatus() {
|
||||||
|
if (this.telescopeData) {
|
||||||
|
const statusEl = document.getElementById('telescope-status');
|
||||||
|
statusEl.textContent = `Position: ${this.telescopeAngle.toFixed(1)}° | Speed: 1 U/min | Running`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDomeStatus() {
|
||||||
|
if (this.domeData) {
|
||||||
|
const statusEl = document.getElementById('dome-status');
|
||||||
|
const diff = this.domeData.diff || 0;
|
||||||
|
const isTracking = Math.abs(diff) > this.slotWidth / 2;
|
||||||
|
statusEl.textContent = `Position: ${this.domeAngle.toFixed(1)}° | ${isTracking ? 'Tracking' : 'Standby'} | Diff: ${diff.toFixed(1)}°`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSystemStatus(message) {
|
||||||
|
const statusEl = document.getElementById('system-status');
|
||||||
|
statusEl.textContent = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDebugInfo() {
|
||||||
|
const debugEl = document.getElementById('debug');
|
||||||
|
const now = new Date().toLocaleTimeString();
|
||||||
|
|
||||||
|
let info = `=== Observatory Debug Info [${now}] ===\n`;
|
||||||
|
|
||||||
|
if (this.telescopeData) {
|
||||||
|
info += `🔭 TELESCOPE:\n`;
|
||||||
|
info += ` Position: ${this.telescopeAngle.toFixed(1)}°\n`;
|
||||||
|
info += ` Last Update: ${new Date(this.telescopeData.timestamp).toLocaleTimeString()}\n\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.domeData) {
|
||||||
|
info += `🏠 DOME:\n`;
|
||||||
|
info += ` Position: ${this.domeAngle.toFixed(1)}°\n`;
|
||||||
|
info += ` Slot Width: ${this.slotWidth}°\n`;
|
||||||
|
info += ` Difference: ${(this.domeData.diff || 0).toFixed(1)}°\n`;
|
||||||
|
info += ` Status: ${Math.abs(this.domeData.diff || 0) > this.slotWidth / 2 ? 'TRACKING' : 'STANDBY'}\n`;
|
||||||
|
info += ` Last Update: ${new Date(this.domeData.timestamp).toLocaleTimeString()}\n\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
info += `📊 SYSTEM:\n`;
|
||||||
|
info += ` FPS: 60\n`;
|
||||||
|
info += ` Tags: ${this.tagCount}\n`;
|
||||||
|
info += ` WebSocket: ${this.socket.connected ? 'Connected' : 'Disconnected'}`;
|
||||||
|
|
||||||
|
debugEl.textContent = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateStatus() {
|
||||||
|
// Status alle 100ms aktualisieren
|
||||||
|
setInterval(() => {
|
||||||
|
this.updateDebugInfo();
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Telescope Control Funktionen für Buttons
|
||||||
|
function adjustTelescope(degrees) {
|
||||||
|
if (window.telescope && window.telescope.socket) {
|
||||||
|
window.telescope.socket.emit('telescope-control', {
|
||||||
|
command: 'adjust',
|
||||||
|
degrees: degrees
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keyboard Controls
|
||||||
|
document.addEventListener('keydown', (event) => {
|
||||||
|
switch(event.code) {
|
||||||
|
case 'ArrowLeft':
|
||||||
|
adjustTelescope(-15);
|
||||||
|
break;
|
||||||
|
case 'ArrowRight':
|
||||||
|
adjustTelescope(15);
|
||||||
|
break;
|
||||||
|
case 'KeyQ':
|
||||||
|
adjustTelescope(-45);
|
||||||
|
break;
|
||||||
|
case 'KeyE':
|
||||||
|
adjustTelescope(45);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Global flag to prevent multiple initializations
|
||||||
|
window.observatoryInitialized = window.observatoryInitialized || false;
|
||||||
|
|
||||||
|
// Initialize visualization when page loads
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
// Prevent multiple initializations
|
||||||
|
if (window.observatoryInitialized) {
|
||||||
|
console.log('=== Observatory System Already Running - Skipping ===');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('=== Observatory System Starting ===');
|
||||||
|
|
||||||
|
// Stoppe und entferne eventuelle bereits laufende Instanzen
|
||||||
|
if (window.telescope) {
|
||||||
|
console.log('Stopping existing telescope...');
|
||||||
|
window.telescope.stop();
|
||||||
|
delete window.telescope;
|
||||||
|
}
|
||||||
|
if (window.dome) {
|
||||||
|
console.log('Stopping existing dome...');
|
||||||
|
window.dome.stop();
|
||||||
|
delete window.dome;
|
||||||
|
}
|
||||||
|
if (window.visualization) {
|
||||||
|
console.log('Stopping existing visualization...');
|
||||||
|
delete window.visualization;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warte kurz, dann erstelle neue Instanzen
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log('Creating telescope...');
|
||||||
|
window.telescope = new Telescope('http://localhost:3005');
|
||||||
|
|
||||||
|
console.log('Creating dome...');
|
||||||
|
window.dome = new Dome('http://localhost:3005');
|
||||||
|
|
||||||
|
console.log('Creating visualization...');
|
||||||
|
window.visualization = new Visualization();
|
||||||
|
|
||||||
|
// Markiere als initialisiert
|
||||||
|
window.observatoryInitialized = true;
|
||||||
|
|
||||||
|
console.log('=== Observatory System Ready ===');
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
41
JS/server.js
41
JS/server.js
@@ -1,9 +1,46 @@
|
|||||||
const express = require("express");
|
const express = require("express");
|
||||||
|
const http = require("http");
|
||||||
|
const socketIo = require("socket.io");
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
const server = http.createServer(app);
|
||||||
|
const io = socketIo(server, {
|
||||||
|
cors: {
|
||||||
|
origin: "*",
|
||||||
|
methods: ["GET", "POST"]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const port = 3005;
|
const port = 3005;
|
||||||
|
|
||||||
app.use(express.static("public"));
|
app.use(express.static("public"));
|
||||||
|
|
||||||
app.listen(port, () => {
|
// WebSocket-Verbindungen verwalten
|
||||||
console.log(`Observatory simulation running at http://localhost:${port}`);
|
io.on("connection", (socket) => {
|
||||||
|
console.log("Client connected:", socket.id);
|
||||||
|
|
||||||
|
// Teleskop-Position broadcaasting
|
||||||
|
socket.on("telescope-position", (data) => {
|
||||||
|
// An alle anderen Clients weiterleiten (außer Sender)
|
||||||
|
socket.broadcast.emit("telescope-position", data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Kuppel-Position broadcasting
|
||||||
|
socket.on("dome-position", (data) => {
|
||||||
|
socket.broadcast.emit("dome-position", data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Teleskop-Steuerung
|
||||||
|
socket.on("telescope-control", (data) => {
|
||||||
|
io.emit("telescope-control", data);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("disconnect", () => {
|
||||||
|
console.log("Client disconnected:", socket.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(port, () => {
|
||||||
|
console.log(`Observatory simulation running at http://localhost:${port}`);
|
||||||
|
console.log("WebSocket server is ready for telescope and dome communication");
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user