change claude proxy name and added ws support in adroid app
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
* QR-Scanner fuer Pairing, Moduswahl, GPS-Toggle, Log-Viewer.
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
Alert,
|
||||
Platform,
|
||||
} from 'react-native';
|
||||
import rvs, { ConnectionState, RVSMessage, ConnectionConfig } from '../services/rvs';
|
||||
import rvs, { ConnectionState, RVSMessage, ConnectionConfig, ConnectionLogEntry } from '../services/rvs';
|
||||
import ModeSelector from '../components/ModeSelector';
|
||||
import QRScanner from '../components/QRScanner';
|
||||
|
||||
@@ -61,14 +61,29 @@ const SettingsScreen: React.FC = () => {
|
||||
const [logTab, setLogTab] = useState<LogTab>('live');
|
||||
const [logs, setLogs] = useState<LogEntry[]>([]);
|
||||
const [events, setEvents] = useState<EventEntry[]>([]);
|
||||
const [connLog, setConnLog] = useState<ConnectionLogEntry[]>(rvs.getConnectionLog());
|
||||
|
||||
let logIdCounter = 0;
|
||||
|
||||
// RVS-Nachrichten abonnieren (Logs und Events)
|
||||
// Gespeicherte Config in die Felder laden
|
||||
useEffect(() => {
|
||||
const config = rvs.getConfig();
|
||||
if (config) {
|
||||
setManualHost(config.host);
|
||||
setManualPort(String(config.port));
|
||||
setManualToken(config.token);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// RVS-Nachrichten und Verbindungslog abonnieren
|
||||
useEffect(() => {
|
||||
const unsubState = rvs.onStateChange(setConnectionState);
|
||||
setConnectionState(rvs.getState());
|
||||
|
||||
const unsubLog = rvs.onLog((entry) => {
|
||||
setConnLog(prev => [...prev.slice(-99), entry]);
|
||||
});
|
||||
|
||||
const unsubMessage = rvs.onMessage((message: RVSMessage) => {
|
||||
if (message.type === 'log') {
|
||||
const entry: LogEntry = {
|
||||
@@ -101,6 +116,7 @@ const SettingsScreen: React.FC = () => {
|
||||
return () => {
|
||||
unsubState();
|
||||
unsubMessage();
|
||||
unsubLog();
|
||||
};
|
||||
}, []);
|
||||
|
||||
@@ -250,6 +266,47 @@ const SettingsScreen: React.FC = () => {
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* === Verbindungslog === */}
|
||||
<Text style={styles.sectionTitle}>Verbindungslog</Text>
|
||||
<View style={styles.card}>
|
||||
<ScrollView
|
||||
style={styles.connLogScroll}
|
||||
nestedScrollEnabled={true}
|
||||
ref={(ref) => {
|
||||
// Auto-Scroll nach unten bei neuen Eintraegen
|
||||
if (ref && connLog.length > 0) {
|
||||
setTimeout(() => ref.scrollToEnd({ animated: false }), 50);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{connLog.length > 0 ? (
|
||||
connLog.slice(-50).map((entry, idx) => (
|
||||
<View key={`cl_${idx}`} style={styles.logEntry}>
|
||||
<Text style={styles.logTime}>{formatTime(entry.timestamp)}</Text>
|
||||
<Text
|
||||
style={[
|
||||
styles.logMessage,
|
||||
entry.level === 'error' && styles.logError,
|
||||
entry.level === 'warn' && styles.logWarn,
|
||||
]}
|
||||
numberOfLines={3}
|
||||
>
|
||||
{entry.message}
|
||||
</Text>
|
||||
</View>
|
||||
))
|
||||
) : (
|
||||
<Text style={styles.emptyLog}>Noch keine Verbindungsversuche</Text>
|
||||
)}
|
||||
</ScrollView>
|
||||
<TouchableOpacity
|
||||
style={styles.clearButton}
|
||||
onPress={() => setConnLog([])}
|
||||
>
|
||||
<Text style={styles.clearButtonText}>Log l{'\u00F6'}schen</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* === Modus === */}
|
||||
<Text style={styles.sectionTitle}>Betriebsmodus</Text>
|
||||
<View style={styles.card}>
|
||||
@@ -526,6 +583,12 @@ const styles = StyleSheet.create({
|
||||
tabTextActive: {
|
||||
color: '#FFFFFF',
|
||||
},
|
||||
connLogScroll: {
|
||||
maxHeight: 200,
|
||||
backgroundColor: '#0A0A18',
|
||||
borderRadius: 8,
|
||||
padding: 10,
|
||||
},
|
||||
logContainer: {
|
||||
maxHeight: 300,
|
||||
backgroundColor: '#0A0A18',
|
||||
|
||||
@@ -30,12 +30,22 @@ export interface ConnectionConfig {
|
||||
type MessageCallback = (message: RVSMessage) => void;
|
||||
type StateCallback = (state: ConnectionState) => void;
|
||||
|
||||
/** Einzelner Eintrag im Verbindungslog */
|
||||
export interface ConnectionLogEntry {
|
||||
timestamp: number;
|
||||
level: 'info' | 'warn' | 'error';
|
||||
message: string;
|
||||
}
|
||||
|
||||
type LogCallback = (entry: ConnectionLogEntry) => void;
|
||||
|
||||
// --- Konstanten ---
|
||||
|
||||
const HEARTBEAT_INTERVAL_MS = 25_000;
|
||||
const INITIAL_RECONNECT_DELAY_MS = 1_000;
|
||||
const MAX_RECONNECT_DELAY_MS = 30_000;
|
||||
const RECONNECT_BACKOFF_FACTOR = 2;
|
||||
const MAX_LOG_ENTRIES = 100;
|
||||
|
||||
// --- RVS-Klasse ---
|
||||
|
||||
@@ -51,6 +61,9 @@ class RVSConnection {
|
||||
|
||||
private messageListeners: MessageCallback[] = [];
|
||||
private stateListeners: StateCallback[] = [];
|
||||
private logListeners: LogCallback[] = [];
|
||||
private connectionLog: ConnectionLogEntry[] = [];
|
||||
private usingTLSFallback: boolean = false;
|
||||
|
||||
// --- Konfiguration ---
|
||||
|
||||
@@ -73,17 +86,19 @@ class RVSConnection {
|
||||
/** Verbindung zum RVS aufbauen */
|
||||
connect(): void {
|
||||
if (!this.config) {
|
||||
console.warn('[RVS] Keine Verbindungskonfiguration vorhanden');
|
||||
this.log('warn', 'Keine Verbindungskonfiguration vorhanden');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.ws?.readyState === WebSocket.OPEN) {
|
||||
console.log('[RVS] Bereits verbunden');
|
||||
this.log('info', 'Bereits verbunden');
|
||||
return;
|
||||
}
|
||||
|
||||
this.shouldReconnect = true;
|
||||
this.reconnectDelay = INITIAL_RECONNECT_DELAY_MS;
|
||||
this.usingTLSFallback = false;
|
||||
this.log('info', `Verbindungsaufbau zu ${this.config.host}:${this.config.port} (TLS: ${this.config.useTLS ? 'ja' : 'nein'})`);
|
||||
this.establishConnection();
|
||||
}
|
||||
|
||||
@@ -97,6 +112,7 @@ class RVSConnection {
|
||||
this.ws = null;
|
||||
}
|
||||
|
||||
this.log('info', 'Verbindung getrennt (manuell)');
|
||||
this.setState('disconnected');
|
||||
}
|
||||
|
||||
@@ -135,21 +151,47 @@ class RVSConnection {
|
||||
};
|
||||
}
|
||||
|
||||
/** Callback fuer Verbindungslog-Eintraege registrieren */
|
||||
onLog(callback: LogCallback): () => void {
|
||||
this.logListeners.push(callback);
|
||||
return () => {
|
||||
this.logListeners = this.logListeners.filter(cb => cb !== callback);
|
||||
};
|
||||
}
|
||||
|
||||
/** Gesamten Verbindungslog abrufen */
|
||||
getConnectionLog(): ConnectionLogEntry[] {
|
||||
return [...this.connectionLog];
|
||||
}
|
||||
|
||||
// --- Interne Methoden ---
|
||||
|
||||
/** Eintrag ins Verbindungslog schreiben */
|
||||
private log(level: ConnectionLogEntry['level'], message: string): void {
|
||||
const entry: ConnectionLogEntry = { timestamp: Date.now(), level, message };
|
||||
this.connectionLog = [...this.connectionLog.slice(-(MAX_LOG_ENTRIES - 1)), entry];
|
||||
this.logListeners.forEach(cb => cb(entry));
|
||||
const prefix = level === 'error' ? 'ERROR' : level === 'warn' ? 'WARN' : 'INFO';
|
||||
console.log(`[RVS] [${prefix}] ${message}`);
|
||||
}
|
||||
|
||||
private establishConnection(): void {
|
||||
if (!this.config) return;
|
||||
|
||||
this.setState('connecting');
|
||||
|
||||
const protocol = this.config.useTLS ? 'wss' : 'ws';
|
||||
const useTLS = this.config.useTLS && !this.usingTLSFallback;
|
||||
const protocol = useTLS ? 'wss' : 'ws';
|
||||
const url = `${protocol}://${this.config.host}:${this.config.port}?token=${this.config.token}`;
|
||||
|
||||
this.log('info', `Verbinde: ${protocol}://${this.config.host}:${this.config.port}`);
|
||||
|
||||
try {
|
||||
this.ws = new WebSocket(url);
|
||||
|
||||
this.ws.onopen = () => {
|
||||
console.log('[RVS] Verbunden');
|
||||
const tlsInfo = this.usingTLSFallback ? ' (TLS-Fallback: ws://)' : '';
|
||||
this.log('info', `Verbunden${tlsInfo}`);
|
||||
this.setState('connected');
|
||||
this.reconnectDelay = INITIAL_RECONNECT_DELAY_MS;
|
||||
this.startHeartbeat();
|
||||
@@ -160,12 +202,12 @@ class RVSConnection {
|
||||
const message: RVSMessage = JSON.parse(event.data as string);
|
||||
this.notifyMessageListeners(message);
|
||||
} catch (err) {
|
||||
console.error('[RVS] Fehler beim Parsen der Nachricht:', err);
|
||||
this.log('error', `Nachricht parsen fehlgeschlagen: ${err}`);
|
||||
}
|
||||
};
|
||||
|
||||
this.ws.onclose = (event) => {
|
||||
console.log(`[RVS] Verbindung geschlossen (Code: ${event.code})`);
|
||||
this.log('info', `Verbindung geschlossen (Code: ${event.code}, Reason: ${event.reason || '-'})`);
|
||||
this.clearTimers();
|
||||
this.ws = null;
|
||||
this.setState('disconnected');
|
||||
@@ -176,10 +218,23 @@ class RVSConnection {
|
||||
};
|
||||
|
||||
this.ws.onerror = (error) => {
|
||||
console.error('[RVS] WebSocket-Fehler:', error);
|
||||
const errorMsg = (error as any)?.message || 'Unbekannter Fehler';
|
||||
this.log('error', `WebSocket-Fehler: ${errorMsg}`);
|
||||
|
||||
// TLS-Fallback: Wenn wss:// fehlschlaegt, auf ws:// wechseln
|
||||
if (this.config?.useTLS && !this.usingTLSFallback) {
|
||||
this.usingTLSFallback = true;
|
||||
this.log('warn', 'TLS fehlgeschlagen — Fallback auf ws:// (ohne TLS)');
|
||||
this.clearTimers();
|
||||
this.ws?.close();
|
||||
this.ws = null;
|
||||
this.reconnectDelay = INITIAL_RECONNECT_DELAY_MS;
|
||||
this.establishConnection();
|
||||
return;
|
||||
}
|
||||
};
|
||||
} catch (err) {
|
||||
console.error('[RVS] Verbindungsfehler:', err);
|
||||
this.log('error', `Verbindungsfehler: ${err}`);
|
||||
this.setState('disconnected');
|
||||
|
||||
if (this.shouldReconnect) {
|
||||
@@ -190,7 +245,7 @@ class RVSConnection {
|
||||
|
||||
/** Reconnect mit exponentiellem Backoff planen */
|
||||
private scheduleReconnect(): void {
|
||||
console.log(`[RVS] Reconnect in ${this.reconnectDelay / 1000}s...`);
|
||||
this.log('info', `Reconnect in ${this.reconnectDelay / 1000}s...`);
|
||||
|
||||
this.reconnectTimer = setTimeout(() => {
|
||||
this.establishConnection();
|
||||
|
||||
Reference in New Issue
Block a user