change claude proxy name and added ws support in adroid app

This commit is contained in:
2026-03-11 22:35:26 +01:00
parent e951fc712f
commit dd12a49aaf
116 changed files with 485 additions and 247 deletions
+66 -3
View File
@@ -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',
+64 -9
View File
@@ -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();