Files

229 lines
8.3 KiB
JavaScript

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) - immer auf NFC-Tag-Position!
this.slotWidth = 20; // Spaltbreite in Grad
this.domeSpeed = 0.5; // Grad pro Frame (für sehr smoothe animation)
this.telescopeAngle = 0;
// NFC-Tag System (wie in Original)
this.tagCount = 36; // 36 Tags = alle 10°
this.tagStep = 360 / this.tagCount; // 10° pro Tag
this.currentTagIndex = 0; // Aktueller Tag (0-35)
this.targetTagIndex = 0; // Ziel-Tag während Bewegung
// Setze initiale Position auf Tag 0
this.angle = this.tagToAngle(this.currentTagIndex); // 5° (Mitte von Tag 0)
this.targetDomeAngle = this.angle;
// 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
}
// NFC-Tag Hilfsfunktionen (wie im Original script.js)
getTagIndex(angle) {
return Math.floor(((angle % 360) + 360) % 360 / this.tagStep);
}
tagToAngle(tagIndex) {
return (tagIndex * this.tagStep + this.tagStep / 2) % 360;
}
findOptimalTag(telescopeAngle) {
// Finde den Tag, der das Teleskop am besten zentriert
const telTagIndex = this.getTagIndex(telescopeAngle);
// Teste den Tag mit dem Teleskop und die Nachbar-Tags
const candidates = [
(telTagIndex - 1 + this.tagCount) % this.tagCount,
telTagIndex,
(telTagIndex + 1) % this.tagCount
];
let bestTag = telTagIndex;
let bestDiff = Infinity;
for (let tagIndex of candidates) {
const tagAngle = this.tagToAngle(tagIndex);
const diff = this.angleDiff(telescopeAngle, tagAngle);
const absDiff = Math.abs(diff);
if (absDiff < bestDiff) {
bestDiff = absDiff;
bestTag = tagIndex;
}
}
return bestTag;
}
updateTelescopePosition(telescopeAngle) {
this.telescopeAngle = telescopeAngle;
}
updateDomePosition() {
let diff = this.angleDiff(this.telescopeAngle, this.angle);
let halfSlot = this.slotWidth / 2;
let triggerPoint = halfSlot - 4.0; // Noch früher triggern! (6° statt 8°)
let moveDir = "STOP";
if (this.state === "IDLE") {
// Prüfen, ob Teleskop aus Schlitz läuft
if (Math.abs(diff) > triggerPoint) {
// Finde den optimalen Tag für das Teleskop
let optimalTag = this.findOptimalTag(this.telescopeAngle);
this.targetTagIndex = optimalTag;
// Debug für große Sprünge (45° Bewegungen)
const absDiff = Math.abs(diff);
if (absDiff > 20) {
console.log(`[Dome] LARGE JUMP DEBUG: Tel=${this.telescopeAngle.toFixed(1)}°, Dome=${this.angle.toFixed(1)}°, Diff=${diff.toFixed(1)}°`);
console.log(`[Dome] Current Tag=${this.currentTagIndex}, Optimal Tag=${optimalTag}, Direction=${diff > 0 ? 'CW' : 'CCW'}`);
// Zeige auch die findOptimalTag Ergebnisse
console.log(`[Dome] Optimal Tag ${optimalTag} = ${this.tagToAngle(optimalTag).toFixed(1)}°`);
}
// Bei großen Sprüngen (>20°) erweiterten Puffer verwenden
// WICHTIG: Puffer immer in CW-Richtung (Teleskop-Rotationsrichtung!)
if (absDiff > 20) {
// Bei großen Sprüngen: 1 Tag CW-Puffer (Teleskop dreht sich ja CW weiter!)
this.targetTagIndex = (optimalTag + 1) % this.tagCount;
console.log(`[Dome] Large jump: Adding CW buffer, Tag ${optimalTag}${this.targetTagIndex}`);
} else if (absDiff > 8.0) { // Normale große Abweichungen
if (diff > 0) {
// Normale CW-Bewegung → 1 Tag weiter CW
this.targetTagIndex = (optimalTag + 1) % this.tagCount;
} else {
// Normale CCW-Bewegung → kein Puffer (Teleskop kommt ja zurück)
this.targetTagIndex = optimalTag;
}
if (diff > 0) {
this.targetTagIndex = (optimalTag + 1) % this.tagCount;
} else {
this.targetTagIndex = (optimalTag - 1 + this.tagCount) % this.tagCount;
}
}
const targetAngle = this.tagToAngle(this.targetTagIndex);
if (this.targetTagIndex !== this.currentTagIndex) {
this.targetDomeAngle = targetAngle; // Physisches Ziel
this.state = "MOVING";
const bufferInfo = this.targetTagIndex !== optimalTag ? " [+buffer]" : "";
console.log(`[Dome] START MOVING: Tel out of slot (${diff.toFixed(1)}° > ${triggerPoint.toFixed(1)}°)`);
console.log(`[Dome] Tag ${this.currentTagIndex} (${this.angle.toFixed(1)}°) → Tag ${this.targetTagIndex} (${targetAngle.toFixed(1)}°)${bufferInfo}`);
}
}
} else if (this.state === "MOVING") {
// Während der Bewegung: Höhere Trigger-Schwelle, damit keine neuen Bewegungen ausgelöst werden
if (Math.abs(diff) > halfSlot + 2.0) { // 12° statt 6° während Bewegung
console.log(`[Dome] WARNING: Large deviation during movement! Tel=${this.telescopeAngle.toFixed(1)}°, Dome=${this.angle.toFixed(1)}°, Diff=${diff.toFixed(1)}°`);
console.log(`[Dome] Continuing current movement to ${this.targetDomeAngle.toFixed(1)}°`);
}
}
if (this.state === "MOVING") {
// Smoothe physische Bewegung zum Ziel-Tag
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";
console.log(`[Dome] MOVING: ${this.angle.toFixed(1)}° → ${this.targetDomeAngle.toFixed(1)}°, Dir=${moveDir}`);
} else {
// Ziel erreicht - Kuppel "erkennt" jetzt den neuen Tag
this.angle = this.targetDomeAngle;
this.currentTagIndex = this.targetTagIndex; // Tag-Position aktualisiert!
this.state = "IDLE";
console.log(`[Dome] REACHED TARGET: Tag ${this.currentTagIndex} (${this.angle.toFixed(1)}°), back to IDLE`);
}
}
}
// 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;
}