refactoring step 2
This commit is contained in:
@ -63,7 +63,7 @@ class VNADashboard {
|
|||||||
*/
|
*/
|
||||||
async init() {
|
async init() {
|
||||||
try {
|
try {
|
||||||
console.log('🚀 Initializing VNA Dashboard...');
|
console.log('Initializing VNA Dashboard...');
|
||||||
|
|
||||||
// Initialize Lucide icons
|
// Initialize Lucide icons
|
||||||
if (typeof lucide !== 'undefined') {
|
if (typeof lucide !== 'undefined') {
|
||||||
@ -80,7 +80,7 @@ class VNADashboard {
|
|||||||
await this.websocket.connect();
|
await this.websocket.connect();
|
||||||
|
|
||||||
this.isInitialized = true;
|
this.isInitialized = true;
|
||||||
console.log('✅ VNA Dashboard initialized successfully');
|
console.log('VNA Dashboard initialized successfully');
|
||||||
|
|
||||||
// Show welcome notification
|
// Show welcome notification
|
||||||
this.notifications.show({
|
this.notifications.show({
|
||||||
@ -90,7 +90,7 @@ class VNADashboard {
|
|||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Failed to initialize VNA Dashboard:', error);
|
console.error('Failed to initialize VNA Dashboard:', error);
|
||||||
this.notifications.show({
|
this.notifications.show({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
title: 'Initialization Failed',
|
title: 'Initialization Failed',
|
||||||
@ -148,7 +148,7 @@ class VNADashboard {
|
|||||||
setupUIHandlers() {
|
setupUIHandlers() {
|
||||||
// Navigation
|
// Navigation
|
||||||
this.ui.onViewChange((view) => {
|
this.ui.onViewChange((view) => {
|
||||||
console.log(`📱 Switched to view: ${view}`);
|
console.log(` Switched to view: ${view}`);
|
||||||
if (view === 'settings') {
|
if (view === 'settings') {
|
||||||
this.settings.refresh();
|
this.settings.refresh();
|
||||||
}
|
}
|
||||||
@ -156,7 +156,7 @@ class VNADashboard {
|
|||||||
|
|
||||||
// Processor toggles (UI-only visibility)
|
// Processor toggles (UI-only visibility)
|
||||||
this.ui.onProcessorToggle((processor, enabled) => {
|
this.ui.onProcessorToggle((processor, enabled) => {
|
||||||
console.log(`🔧 Processor ${processor}: ${enabled ? 'enabled' : 'disabled'}`);
|
console.log(` Processor ${processor}: ${enabled ? 'enabled' : 'disabled'}`);
|
||||||
this.charts.toggleProcessor(processor, enabled);
|
this.charts.toggleProcessor(processor, enabled);
|
||||||
this.savePreferences();
|
this.savePreferences();
|
||||||
});
|
});
|
||||||
@ -171,7 +171,7 @@ class VNADashboard {
|
|||||||
if (data.type === 'system_status') {
|
if (data.type === 'system_status') {
|
||||||
this.ui.updateSystemStatus(data.data);
|
this.ui.updateSystemStatus(data.data);
|
||||||
} else if (data.type === 'processor_history') {
|
} else if (data.type === 'processor_history') {
|
||||||
console.log('📜 Received processor history:', data);
|
console.log('Received processor history:', data);
|
||||||
}
|
}
|
||||||
// Note: 'error' is handled by WebSocket manager directly
|
// Note: 'error' is handled by WebSocket manager directly
|
||||||
}
|
}
|
||||||
@ -247,7 +247,7 @@ class VNADashboard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('⚠️ Error applying preferences:', error);
|
console.warn('Error applying preferences:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,7 +263,7 @@ class VNADashboard {
|
|||||||
};
|
};
|
||||||
await this.storage.savePreferences(preferences);
|
await this.storage.savePreferences(preferences);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('⚠️ Failed to save preferences:', error);
|
console.warn('Failed to save preferences:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,7 +282,7 @@ class VNADashboard {
|
|||||||
destroy() {
|
destroy() {
|
||||||
if (!this.isInitialized) return;
|
if (!this.isInitialized) return;
|
||||||
|
|
||||||
console.log('🧹 Cleaning up VNA Dashboard...');
|
console.log('Cleaning up VNA Dashboard...');
|
||||||
|
|
||||||
document.removeEventListener('visibilitychange', this.handleVisibilityChange);
|
document.removeEventListener('visibilitychange', this.handleVisibilityChange);
|
||||||
window.removeEventListener('beforeunload', this.handleBeforeUnload);
|
window.removeEventListener('beforeunload', this.handleBeforeUnload);
|
||||||
@ -295,7 +295,7 @@ class VNADashboard {
|
|||||||
this.notifications.destroy();
|
this.notifications.destroy();
|
||||||
|
|
||||||
this.isInitialized = false;
|
this.isInitialized = false;
|
||||||
console.log('✅ VNA Dashboard cleanup complete');
|
console.log('VNA Dashboard cleanup complete');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,11 +312,11 @@ if (document.readyState === 'loading') {
|
|||||||
|
|
||||||
// Global error handling
|
// Global error handling
|
||||||
window.addEventListener('error', (event) => {
|
window.addEventListener('error', (event) => {
|
||||||
console.error('🚨 Global error:', event.error);
|
console.error('Global error:', event.error);
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('unhandledrejection', (event) => {
|
window.addEventListener('unhandledrejection', (event) => {
|
||||||
console.error('🚨 Unhandled promise rejection:', event.reason);
|
console.error('Unhandled promise rejection:', event.reason);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Development helpers
|
// Development helpers
|
||||||
|
|||||||
@ -63,14 +63,14 @@ export class AcquisitionManager {
|
|||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
this.notifications.show({type: 'success', message: result.message});
|
this.notifications.show({type: 'success', title: 'Acquisition Started', message: result.message});
|
||||||
await this.updateStatus();
|
await this.updateStatus();
|
||||||
} else {
|
} else {
|
||||||
this.notifications.show({type: 'error', message: result.error || 'Failed to start acquisition'});
|
this.notifications.show({type: 'error', title: 'Start Failed', message: result.error || 'Failed to start acquisition'});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error starting acquisition:', error);
|
console.error('Error starting acquisition:', error);
|
||||||
this.notifications.show({type: 'error', message: 'Failed to start acquisition'});
|
this.notifications.show({type: 'error', title: 'Start Failed', message: 'Failed to start acquisition'});
|
||||||
} finally {
|
} finally {
|
||||||
setButtonLoading(this.elements.startBtn, false, originalState);
|
setButtonLoading(this.elements.startBtn, false, originalState);
|
||||||
}
|
}
|
||||||
@ -88,14 +88,14 @@ export class AcquisitionManager {
|
|||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
this.notifications.show({type: 'success', message: result.message});
|
this.notifications.show({type: 'success', title: 'Acquisition Stopped', message: result.message});
|
||||||
await this.updateStatus();
|
await this.updateStatus();
|
||||||
} else {
|
} else {
|
||||||
this.notifications.show({type: 'error', message: result.error || 'Failed to stop acquisition'});
|
this.notifications.show({type: 'error', title: 'Stop Failed', message: result.error || 'Failed to stop acquisition'});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error stopping acquisition:', error);
|
console.error('Error stopping acquisition:', error);
|
||||||
this.notifications.show({type: 'error', message: 'Failed to stop acquisition'});
|
this.notifications.show({type: 'error', title: 'Stop Failed', message: 'Failed to stop acquisition'});
|
||||||
} finally {
|
} finally {
|
||||||
setButtonLoading(this.elements.stopBtn, false, originalState);
|
setButtonLoading(this.elements.stopBtn, false, originalState);
|
||||||
}
|
}
|
||||||
@ -113,14 +113,14 @@ export class AcquisitionManager {
|
|||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
this.notifications.show({type: 'success', message: result.message});
|
this.notifications.show({type: 'success', title: 'Single Sweep', message: result.message});
|
||||||
await this.updateStatus();
|
await this.updateStatus();
|
||||||
} else {
|
} else {
|
||||||
this.notifications.show({type: 'error', message: result.error || 'Failed to trigger single sweep'});
|
this.notifications.show({type: 'error', title: 'Single Sweep Failed', message: result.error || 'Failed to trigger single sweep'});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error triggering single sweep:', error);
|
console.error('Error triggering single sweep:', error);
|
||||||
this.notifications.show({type: 'error', message: 'Failed to trigger single sweep'});
|
this.notifications.show({type: 'error', title: 'Single Sweep Failed', message: 'Failed to trigger single sweep'});
|
||||||
} finally {
|
} finally {
|
||||||
setButtonLoading(this.elements.singleSweepBtn, false, originalState);
|
setButtonLoading(this.elements.singleSweepBtn, false, originalState);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -68,31 +68,31 @@ export class ChartManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
console.log('📊 Initializing Chart Manager...');
|
console.log('Initializing Chart Manager...');
|
||||||
this.chartsGrid = document.getElementById('chartsGrid');
|
this.chartsGrid = document.getElementById('chartsGrid');
|
||||||
this.emptyState = document.getElementById('emptyState');
|
this.emptyState = document.getElementById('emptyState');
|
||||||
if (!this.chartsGrid || !this.emptyState) throw new Error('Required DOM elements not found');
|
if (!this.chartsGrid || !this.emptyState) throw new Error('Required DOM elements not found');
|
||||||
if (typeof Plotly === 'undefined') throw new Error('Plotly.js not loaded');
|
if (typeof Plotly === 'undefined') throw new Error('Plotly.js not loaded');
|
||||||
console.log('✅ Chart Manager initialized');
|
console.log('Chart Manager initialized');
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Новый входной формат — прямо payload от WS: processor_result */
|
/** New input format - direct payload from WS: processor_result */
|
||||||
addResult(payload) {
|
addResult(payload) {
|
||||||
try {
|
try {
|
||||||
const { processor_id, timestamp, plotly_config, metadata, data } = payload;
|
const { processor_id, timestamp, plotly_config, metadata, data } = payload;
|
||||||
if (!processor_id) {
|
if (!processor_id) {
|
||||||
console.warn('⚠️ Invalid result - missing processor_id:', payload);
|
console.warn('Invalid result - missing processor_id:', payload);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.disabledProcessors.has(processor_id)) {
|
if (this.disabledProcessors.has(processor_id)) {
|
||||||
console.log(`⏸️ Skipping disabled processor: ${processor_id}`);
|
console.log(` Skipping disabled processor: ${processor_id}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store only the latest data (no history needed)
|
// Store only the latest data (no history needed)
|
||||||
this.chartData.set(processor_id, {
|
this.chartData.set(processor_id, {
|
||||||
timestamp: new Date((timestamp ?? Date.now()) * 1000), // если приходит epoch seconds
|
timestamp: new Date((timestamp ?? Date.now()) * 1000), // if timestamp is in epoch seconds
|
||||||
metadata: metadata || {},
|
metadata: metadata || {},
|
||||||
data: data || {},
|
data: data || {},
|
||||||
plotly_config: plotly_config || { data: [], layout: {} }
|
plotly_config: plotly_config || { data: [], layout: {} }
|
||||||
@ -103,7 +103,7 @@ export class ChartManager {
|
|||||||
this.updateChart(processor_id, plotly_config || { data: [], layout: {} });
|
this.updateChart(processor_id, plotly_config || { data: [], layout: {} });
|
||||||
this.hideEmptyState();
|
this.hideEmptyState();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('❌ Error adding chart result:', e);
|
console.error('Error adding chart result:', e);
|
||||||
this.notifications?.show?.({
|
this.notifications?.show?.({
|
||||||
type: 'error', title: 'Chart Error', message: `Failed to update chart`
|
type: 'error', title: 'Chart Error', message: `Failed to update chart`
|
||||||
});
|
});
|
||||||
@ -111,7 +111,7 @@ export class ChartManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createChart(processorId) {
|
createChart(processorId) {
|
||||||
console.log(`📊 Creating chart for processor: ${processorId}`);
|
console.log(` Creating chart for processor: ${processorId}`);
|
||||||
const card = this.createChartCard(processorId);
|
const card = this.createChartCard(processorId);
|
||||||
this.chartsGrid.appendChild(card);
|
this.chartsGrid.appendChild(card);
|
||||||
|
|
||||||
@ -144,7 +144,7 @@ export class ChartManager {
|
|||||||
if (this.isPaused) return;
|
if (this.isPaused) return;
|
||||||
|
|
||||||
const chart = this.charts.get(processorId);
|
const chart = this.charts.get(processorId);
|
||||||
if (!chart?.plotContainer) { console.warn(`⚠️ Chart not found for processor: ${processorId}`); return; }
|
if (!chart?.plotContainer) { console.warn(` Chart not found for processor: ${processorId}`); return; }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const start = performance.now();
|
const start = performance.now();
|
||||||
@ -172,7 +172,7 @@ export class ChartManager {
|
|||||||
this.updatePerformanceStats(dt);
|
this.updatePerformanceStats(dt);
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`❌ Error updating chart ${processorId}:`, e);
|
console.error(` Error updating chart ${processorId}:`, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,7 +187,7 @@ export class ChartManager {
|
|||||||
while (this.updateQueue.size > 0 && !this.isPaused) {
|
while (this.updateQueue.size > 0 && !this.isPaused) {
|
||||||
const [id, fn] = this.updateQueue.entries().next().value;
|
const [id, fn] = this.updateQueue.entries().next().value;
|
||||||
this.updateQueue.delete(id);
|
this.updateQueue.delete(id);
|
||||||
try { await fn(); } catch (e) { console.error(`❌ Error in queued update for ${id}:`, e); }
|
try { await fn(); } catch (e) { console.error(` Error in queued update for ${id}:`, e); }
|
||||||
await new Promise(r => setTimeout(r, 0));
|
await new Promise(r => setTimeout(r, 0));
|
||||||
}
|
}
|
||||||
this.isUpdating = false;
|
this.isUpdating = false;
|
||||||
@ -297,11 +297,11 @@ export class ChartManager {
|
|||||||
JSON.stringify(uiParameters) !== JSON.stringify(chart.lastUiParameters);
|
JSON.stringify(uiParameters) !== JSON.stringify(chart.lastUiParameters);
|
||||||
|
|
||||||
if (!parametersChanged) {
|
if (!parametersChanged) {
|
||||||
console.log(`⚪ No parameter changes for ${processorId}, skipping settings update`);
|
console.log(` No parameter changes for ${processorId}, skipping settings update`);
|
||||||
return; // No need to update if parameters haven't changed
|
return; // No need to update if parameters haven't changed
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`🔄 Updating settings for ${processorId}:`, {
|
console.log(` Updating settings for ${processorId}:`, {
|
||||||
old: chart.lastUiParameters,
|
old: chart.lastUiParameters,
|
||||||
new: uiParameters
|
new: uiParameters
|
||||||
});
|
});
|
||||||
@ -341,7 +341,7 @@ export class ChartManager {
|
|||||||
uiParameters.forEach(param => {
|
uiParameters.forEach(param => {
|
||||||
const settingKey = `${processorId}_${param.name}`;
|
const settingKey = `${processorId}_${param.name}`;
|
||||||
this.lastSettingValues[settingKey] = param.value;
|
this.lastSettingValues[settingKey] = param.value;
|
||||||
console.log(`💾 Initialized setting value: ${settingKey} = ${param.value}`);
|
console.log(` Initialized setting value: ${settingKey} = ${param.value}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,7 +396,7 @@ export class ChartManager {
|
|||||||
value = value === 'true';
|
value = value === 'true';
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`🔧 Chart setting changed: ${processorId}.${paramName} = ${value}`);
|
console.log(` Chart setting changed: ${processorId}.${paramName} = ${value}`);
|
||||||
|
|
||||||
// Store the current setting value to prevent loops
|
// Store the current setting value to prevent loops
|
||||||
if (!this.lastSettingValues) this.lastSettingValues = {};
|
if (!this.lastSettingValues) this.lastSettingValues = {};
|
||||||
@ -410,7 +410,7 @@ export class ChartManager {
|
|||||||
|
|
||||||
// Check if this is the same value we just set to prevent feedback loop
|
// Check if this is the same value we just set to prevent feedback loop
|
||||||
if (lastValue === value) {
|
if (lastValue === value) {
|
||||||
console.log(`🔄 Skipping duplicate setting: ${settingKey} = ${value} (was ${this.lastSettingValues[settingKey]})`);
|
console.log(` Skipping duplicate setting: ${settingKey} = ${value} (was ${this.lastSettingValues[settingKey]})`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -425,13 +425,13 @@ export class ChartManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.settingDebounceTimers[debounceKey] = setTimeout(() => {
|
this.settingDebounceTimers[debounceKey] = setTimeout(() => {
|
||||||
console.log(`📤 Sending setting update: ${processorId}.${paramName} = ${value}`);
|
console.log(` Sending setting update: ${processorId}.${paramName} = ${value}`);
|
||||||
// Send update via WebSocket
|
// Send update via WebSocket
|
||||||
const websocket = window.vnaDashboard?.websocket;
|
const websocket = window.vnaDashboard?.websocket;
|
||||||
if (websocket && websocket.recalculate) {
|
if (websocket && websocket.recalculate) {
|
||||||
websocket.recalculate(processorId, { [paramName]: value });
|
websocket.recalculate(processorId, { [paramName]: value });
|
||||||
} else {
|
} else {
|
||||||
console.warn('⚠️ WebSocket not available for settings update');
|
console.warn('WebSocket not available for settings update');
|
||||||
}
|
}
|
||||||
delete this.settingDebounceTimers[debounceKey];
|
delete this.settingDebounceTimers[debounceKey];
|
||||||
}, 300); // 300ms delay to prevent rapid updates
|
}, 300); // 300ms delay to prevent rapid updates
|
||||||
@ -445,17 +445,17 @@ export class ChartManager {
|
|||||||
const paramName = button.dataset.param;
|
const paramName = button.dataset.param;
|
||||||
|
|
||||||
if (!paramName) {
|
if (!paramName) {
|
||||||
console.warn('⚠️ Button missing param data:', button);
|
console.warn('Button missing param data:', button);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent multiple clicks while processing
|
// Prevent multiple clicks while processing
|
||||||
if (button.disabled) {
|
if (button.disabled) {
|
||||||
console.log('🔘 Button already processing, ignoring click');
|
console.log('Button already processing, ignoring click');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`🔘 Button clicked: ${processorId}.${paramName}`);
|
console.log(` Button clicked: ${processorId}.${paramName}`);
|
||||||
|
|
||||||
// Temporarily disable button and show feedback
|
// Temporarily disable button and show feedback
|
||||||
const originalText = button.textContent;
|
const originalText = button.textContent;
|
||||||
@ -465,10 +465,10 @@ export class ChartManager {
|
|||||||
// Send button action via WebSocket (set to true to trigger action) - only once
|
// Send button action via WebSocket (set to true to trigger action) - only once
|
||||||
const websocket = window.vnaDashboard?.websocket;
|
const websocket = window.vnaDashboard?.websocket;
|
||||||
if (websocket && websocket.recalculate) {
|
if (websocket && websocket.recalculate) {
|
||||||
console.log(`📤 Sending button action: ${processorId}.${paramName} = true`);
|
console.log(` Sending button action: ${processorId}.${paramName} = true`);
|
||||||
websocket.recalculate(processorId, { [paramName]: true });
|
websocket.recalculate(processorId, { [paramName]: true });
|
||||||
} else {
|
} else {
|
||||||
console.warn('⚠️ WebSocket not available for button action');
|
console.warn('WebSocket not available for button action');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-enable button after a short delay
|
// Re-enable button after a short delay
|
||||||
@ -543,7 +543,7 @@ export class ChartManager {
|
|||||||
message: `Downloaded ${formatProcessorName(id)} plot and data`
|
message: `Downloaded ${formatProcessorName(id)} plot and data`
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('❌ Chart download failed:', e);
|
console.error('Chart download failed:', e);
|
||||||
this.notifications?.show?.({
|
this.notifications?.show?.({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
title: 'Download Failed',
|
title: 'Download Failed',
|
||||||
@ -620,7 +620,7 @@ export class ChartManager {
|
|||||||
|
|
||||||
return rawData;
|
return rawData;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('⚠️ Error extracting processor raw data:', error);
|
console.warn('Error extracting processor raw data:', error);
|
||||||
return { error: 'Failed to extract raw data' };
|
return { error: 'Failed to extract raw data' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -681,8 +681,8 @@ export class ChartManager {
|
|||||||
this.performanceStats.avgUpdateTime = total / this.performanceStats.updatesProcessed;
|
this.performanceStats.avgUpdateTime = total / this.performanceStats.updatesProcessed;
|
||||||
}
|
}
|
||||||
|
|
||||||
pause() { this.isPaused = true; console.log('⏸️ Chart updates paused'); }
|
pause() { this.isPaused = true; console.log('Chart updates paused'); }
|
||||||
resume() { this.isPaused = false; console.log('▶️ Chart updates resumed'); if (this.updateQueue.size) this.processUpdateQueue(); }
|
resume() { this.isPaused = false; console.log('Chart updates resumed'); if (this.updateQueue.size) this.processUpdateQueue(); }
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -706,7 +706,7 @@ export class ChartManager {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(`⚠️ Could not extract Plotly data for ${processorId}:`, error);
|
console.warn(` Could not extract Plotly data for ${processorId}:`, error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -760,7 +760,7 @@ export class ChartManager {
|
|||||||
|
|
||||||
return { data, layout };
|
return { data, layout };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(`⚠️ Could not extract safe Plotly data for ${processorId}:`, error);
|
console.warn(` Could not extract safe Plotly data for ${processorId}:`, error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -790,11 +790,11 @@ export class ChartManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
console.log('🧹 Cleaning up Chart Manager...');
|
console.log('Cleaning up Chart Manager...');
|
||||||
this.clearAll();
|
this.clearAll();
|
||||||
this.updateQueue.clear();
|
this.updateQueue.clear();
|
||||||
this.isUpdating = false;
|
this.isUpdating = false;
|
||||||
this.isPaused = true;
|
this.isPaused = true;
|
||||||
console.log('✅ Chart Manager cleanup complete');
|
console.log('Chart Manager cleanup complete');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -59,7 +59,7 @@ export class NotificationManager {
|
|||||||
document.body.appendChild(this.container);
|
document.body.appendChild(this.container);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('📢 Notification Manager initialized');
|
console.log('Notification Manager initialized');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,7 +68,7 @@ export class NotificationManager {
|
|||||||
show(options) {
|
show(options) {
|
||||||
// Validate options
|
// Validate options
|
||||||
if (!options || typeof options !== 'object') {
|
if (!options || typeof options !== 'object') {
|
||||||
console.warn('⚠️ Invalid notification options:', options);
|
console.warn('Invalid notification options:', options);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ export class NotificationManager {
|
|||||||
|
|
||||||
// Don't show notification if both title and message are empty
|
// Don't show notification if both title and message are empty
|
||||||
if (!title && !message) {
|
if (!title && !message) {
|
||||||
console.warn('⚠️ Skipping notification with empty title and message');
|
console.warn('Skipping notification with empty title and message');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ export class NotificationManager {
|
|||||||
const lastShown = this.recentNotifications.get(notificationKey);
|
const lastShown = this.recentNotifications.get(notificationKey);
|
||||||
|
|
||||||
if (lastShown && (now - lastShown) < 2000) {
|
if (lastShown && (now - lastShown) < 2000) {
|
||||||
console.log('🔄 Skipping duplicate notification:', notificationKey);
|
console.log('Skipping duplicate notification:', notificationKey);
|
||||||
return null; // Skip duplicate notification
|
return null; // Skip duplicate notification
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,7 +169,7 @@ export class NotificationManager {
|
|||||||
lucide.createIcons({ attrs: { 'stroke-width': 1.5 } });
|
lucide.createIcons({ attrs: { 'stroke-width': 1.5 } });
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`📢 Showing ${notification.type} notification:`, notification.title);
|
console.log(` Showing ${notification.type} notification:`, notification.title);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -249,7 +249,7 @@ export class NotificationManager {
|
|||||||
try {
|
try {
|
||||||
actionConfig.handler(notification);
|
actionConfig.handler(notification);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Error in notification action handler:', error);
|
console.error('Error in notification action handler:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,7 +285,7 @@ export class NotificationManager {
|
|||||||
const notification = this.notifications.get(id);
|
const notification = this.notifications.get(id);
|
||||||
if (!notification) return;
|
if (!notification) return;
|
||||||
|
|
||||||
console.log(`📢 Dismissing notification: ${id}`);
|
console.log(` Dismissing notification: ${id}`);
|
||||||
|
|
||||||
// Clear timer
|
// Clear timer
|
||||||
if (notification.timer) {
|
if (notification.timer) {
|
||||||
@ -312,7 +312,7 @@ export class NotificationManager {
|
|||||||
* Dismiss all notifications
|
* Dismiss all notifications
|
||||||
*/
|
*/
|
||||||
dismissAll() {
|
dismissAll() {
|
||||||
console.log('📢 Dismissing all notifications');
|
console.log('Dismissing all notifications');
|
||||||
|
|
||||||
for (const id of this.notifications.keys()) {
|
for (const id of this.notifications.keys()) {
|
||||||
this.dismiss(id);
|
this.dismiss(id);
|
||||||
@ -323,7 +323,7 @@ export class NotificationManager {
|
|||||||
* Dismiss notifications by type
|
* Dismiss notifications by type
|
||||||
*/
|
*/
|
||||||
dismissByType(type) {
|
dismissByType(type) {
|
||||||
console.log(`📢 Dismissing all ${type} notifications`);
|
console.log(` Dismissing all ${type} notifications`);
|
||||||
|
|
||||||
for (const notification of this.notifications.values()) {
|
for (const notification of this.notifications.values()) {
|
||||||
if (notification.type === type) {
|
if (notification.type === type) {
|
||||||
@ -487,7 +487,7 @@ export class NotificationManager {
|
|||||||
* Cleanup
|
* Cleanup
|
||||||
*/
|
*/
|
||||||
destroy() {
|
destroy() {
|
||||||
console.log('🧹 Cleaning up Notification Manager...');
|
console.log('Cleaning up Notification Manager...');
|
||||||
|
|
||||||
this.dismissAll();
|
this.dismissAll();
|
||||||
|
|
||||||
@ -495,6 +495,6 @@ export class NotificationManager {
|
|||||||
this.container.parentNode.removeChild(this.container);
|
this.container.parentNode.removeChild(this.container);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('✅ Notification Manager cleanup complete');
|
console.log('Notification Manager cleanup complete');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,9 +71,9 @@ export class SettingsManager {
|
|||||||
await this._loadInitialData();
|
await this._loadInitialData();
|
||||||
|
|
||||||
this.isInitialized = true;
|
this.isInitialized = true;
|
||||||
console.log('✅ Settings Manager initialized');
|
console.log('Settings Manager initialized');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('❌ Settings Manager init failed:', err);
|
console.error('Settings Manager init failed:', err);
|
||||||
this._notify('error', 'Settings Error', 'Failed to initialize settings');
|
this._notify('error', 'Settings Error', 'Failed to initialize settings');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,7 +83,7 @@ export class SettingsManager {
|
|||||||
this._resetCalibrationCaptureState();
|
this._resetCalibrationCaptureState();
|
||||||
this._detachEvents();
|
this._detachEvents();
|
||||||
this.isInitialized = false;
|
this.isInitialized = false;
|
||||||
console.log('🧹 Settings Manager destroyed');
|
console.log('Settings Manager destroyed');
|
||||||
}
|
}
|
||||||
|
|
||||||
async refresh() {
|
async refresh() {
|
||||||
@ -287,7 +287,7 @@ export class SettingsManager {
|
|||||||
calibrations.forEach(c => {
|
calibrations.forEach(c => {
|
||||||
const opt = document.createElement('option');
|
const opt = document.createElement('option');
|
||||||
opt.value = c.name;
|
opt.value = c.name;
|
||||||
opt.textContent = `${c.name} ${c.is_complete ? '✓' : '⚠'}`;
|
opt.textContent = `${c.name} ${c.is_complete ? '✔' : '?'}`;
|
||||||
dd.appendChild(opt);
|
dd.appendChild(opt);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -392,7 +392,7 @@ export class SettingsManager {
|
|||||||
btn.title = 'Standard is currently being captured';
|
btn.title = 'Standard is currently being captured';
|
||||||
} else if (isCompleted) {
|
} else if (isCompleted) {
|
||||||
btn.classList.add('btn--success');
|
btn.classList.add('btn--success');
|
||||||
btn.innerHTML = `<i data-lucide="check"></i> ${std.toUpperCase()} ✓`;
|
btn.innerHTML = `<i data-lucide="check"></i> ${std.toUpperCase()}`;
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
btn.title = 'Click to recapture this standard';
|
btn.title = 'Click to recapture this standard';
|
||||||
} else if (isMissing) {
|
} else if (isMissing) {
|
||||||
@ -435,7 +435,7 @@ export class SettingsManager {
|
|||||||
// Reset reference state
|
// Reset reference state
|
||||||
this.availableReferences = [];
|
this.availableReferences = [];
|
||||||
this.currentReference = null;
|
this.currentReference = null;
|
||||||
console.log('🔄 Calibration and reference UI reset after preset change');
|
console.log('Calibration and reference UI reset after preset change');
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------- Event Handlers (UI) ----------------------------- */
|
/* ----------------------------- Event Handlers (UI) ----------------------------- */
|
||||||
@ -470,10 +470,10 @@ export class SettingsManager {
|
|||||||
|
|
||||||
this._notify('success', 'Preset Set', result.message);
|
this._notify('success', 'Preset Set', result.message);
|
||||||
|
|
||||||
// Сброс UI калибровки
|
// Reset calibration UI
|
||||||
this._resetCalibrationStateForPresetChange();
|
this._resetCalibrationStateForPresetChange();
|
||||||
|
|
||||||
// Обновить статус
|
// Update status
|
||||||
await this._loadStatus();
|
await this._loadStatus();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Set preset failed:', e);
|
console.error('Set preset failed:', e);
|
||||||
@ -521,7 +521,7 @@ export class SettingsManager {
|
|||||||
this.debouncer.debounce(key, () =>
|
this.debouncer.debounce(key, () =>
|
||||||
this.reqGuard.runExclusive(key, async () => {
|
this.reqGuard.runExclusive(key, async () => {
|
||||||
try {
|
try {
|
||||||
// Отметим стандарт как «занят»
|
// Mark standard as busy
|
||||||
this.disabledStandards.add(standard);
|
this.disabledStandards.add(standard);
|
||||||
|
|
||||||
const btn = document.querySelector(`[data-standard="${standard}"]`);
|
const btn = document.querySelector(`[data-standard="${standard}"]`);
|
||||||
@ -695,13 +695,13 @@ export class SettingsManager {
|
|||||||
const modal = this.elements.plotsModal;
|
const modal = this.elements.plotsModal;
|
||||||
if (!modal) return;
|
if (!modal) return;
|
||||||
|
|
||||||
// Запомним пакет
|
// Store data package
|
||||||
this.currentPlotsData = plotsData;
|
this.currentPlotsData = plotsData;
|
||||||
|
|
||||||
// Рендер карточек/графиков
|
// Render cards/plots
|
||||||
this._renderCalibrationPlots(plotsData.individual_plots, plotsData.preset);
|
this._renderCalibrationPlots(plotsData.individual_plots, plotsData.preset);
|
||||||
|
|
||||||
// Заголовок
|
// Header
|
||||||
const title = modal.querySelector('.modal__title');
|
const title = modal.querySelector('.modal__title');
|
||||||
if (title) {
|
if (title) {
|
||||||
title.innerHTML = `
|
title.innerHTML = `
|
||||||
@ -711,10 +711,10 @@ export class SettingsManager {
|
|||||||
if (typeof lucide !== 'undefined') lucide.createIcons();
|
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Кнопки закрытия/скачивания
|
// Close/download buttons
|
||||||
this._setupModalCloseHandlers(modal);
|
this._setupModalCloseHandlers(modal);
|
||||||
|
|
||||||
// Показ
|
// Display
|
||||||
modal.classList.add('modal--active');
|
modal.classList.add('modal--active');
|
||||||
document.body.style.overflow = 'hidden';
|
document.body.style.overflow = 'hidden';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ export class StorageManager {
|
|||||||
debug: 'debug_info'
|
debug: 'debug_info'
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(`💾 Storage Manager initialized (${this.isAvailable ? 'available' : 'not available'})`);
|
console.log(` Storage Manager initialized (${this.isAvailable ? 'available' : 'not available'})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -29,7 +29,7 @@ export class StorageManager {
|
|||||||
localStorage.removeItem(test);
|
localStorage.removeItem(test);
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('⚠️ localStorage not available:', e.message);
|
console.warn('localStorage not available:', e.message);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46,7 +46,7 @@ export class StorageManager {
|
|||||||
*/
|
*/
|
||||||
async save(key, data) {
|
async save(key, data) {
|
||||||
if (!this.isAvailable) {
|
if (!this.isAvailable) {
|
||||||
console.warn('⚠️ Storage not available, cannot save:', key);
|
console.warn('Storage not available, cannot save:', key);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,24 +58,24 @@ export class StorageManager {
|
|||||||
});
|
});
|
||||||
|
|
||||||
localStorage.setItem(this.getKey(key), serialized);
|
localStorage.setItem(this.getKey(key), serialized);
|
||||||
console.log(`💾 Saved to storage: ${key}`);
|
console.log(` Saved to storage: ${key}`);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Failed to save to storage:', error);
|
console.error('Failed to save to storage:', error);
|
||||||
|
|
||||||
// Handle quota exceeded error
|
// Handle quota exceeded error
|
||||||
if (error.name === 'QuotaExceededError') {
|
if (error.name === 'QuotaExceededError') {
|
||||||
console.log('🧹 Storage quota exceeded, cleaning up...');
|
console.log('Storage quota exceeded, cleaning up...');
|
||||||
this.cleanup();
|
this.cleanup();
|
||||||
|
|
||||||
// Try again after cleanup
|
// Try again after cleanup
|
||||||
try {
|
try {
|
||||||
localStorage.setItem(this.getKey(key), serialized);
|
localStorage.setItem(this.getKey(key), serialized);
|
||||||
console.log(`💾 Saved to storage after cleanup: ${key}`);
|
console.log(` Saved to storage after cleanup: ${key}`);
|
||||||
return true;
|
return true;
|
||||||
} catch (retryError) {
|
} catch (retryError) {
|
||||||
console.error('❌ Still failed after cleanup:', retryError);
|
console.error('Still failed after cleanup:', retryError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ export class StorageManager {
|
|||||||
*/
|
*/
|
||||||
async load(key) {
|
async load(key) {
|
||||||
if (!this.isAvailable) {
|
if (!this.isAvailable) {
|
||||||
console.warn('⚠️ Storage not available, cannot load:', key);
|
console.warn('Storage not available, cannot load:', key);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,16 +102,16 @@ export class StorageManager {
|
|||||||
|
|
||||||
// Validate structure
|
// Validate structure
|
||||||
if (!parsed.data || !parsed.timestamp) {
|
if (!parsed.data || !parsed.timestamp) {
|
||||||
console.warn('⚠️ Invalid storage data format:', key);
|
console.warn('Invalid storage data format:', key);
|
||||||
this.remove(key); // Clean up invalid data
|
this.remove(key); // Clean up invalid data
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`💾 Loaded from storage: ${key} (${new Date(parsed.timestamp).toLocaleString()})`);
|
console.log(` Loaded from storage: ${key} (${new Date(parsed.timestamp).toLocaleString()})`);
|
||||||
return parsed.data;
|
return parsed.data;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Failed to load from storage:', error);
|
console.error('Failed to load from storage:', error);
|
||||||
this.remove(key); // Clean up corrupted data
|
this.remove(key); // Clean up corrupted data
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -125,10 +125,10 @@ export class StorageManager {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
localStorage.removeItem(this.getKey(key));
|
localStorage.removeItem(this.getKey(key));
|
||||||
console.log(`🗑️ Removed from storage: ${key}`);
|
console.log(` Removed from storage: ${key}`);
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Failed to remove from storage:', error);
|
console.error('Failed to remove from storage:', error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -142,7 +142,7 @@ export class StorageManager {
|
|||||||
try {
|
try {
|
||||||
return localStorage.getItem(this.getKey(key)) !== null;
|
return localStorage.getItem(this.getKey(key)) !== null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Failed to check storage existence:', error);
|
console.error('Failed to check storage existence:', error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -215,7 +215,7 @@ export class StorageManager {
|
|||||||
|
|
||||||
if (data && data.expiresAt) {
|
if (data && data.expiresAt) {
|
||||||
if (Date.now() > data.expiresAt) {
|
if (Date.now() > data.expiresAt) {
|
||||||
console.log('🕒 Session data expired, removing...');
|
console.log('Session data expired, removing...');
|
||||||
await this.remove(this.keys.session);
|
await this.remove(this.keys.session);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -278,7 +278,7 @@ export class StorageManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Failed to calculate storage stats:', error);
|
console.error('Failed to calculate storage stats:', error);
|
||||||
return { available: false, error: error.message };
|
return { available: false, error: error.message };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -310,7 +310,7 @@ export class StorageManager {
|
|||||||
async cleanup() {
|
async cleanup() {
|
||||||
if (!this.isAvailable) return;
|
if (!this.isAvailable) return;
|
||||||
|
|
||||||
console.log('🧹 Cleaning up storage...');
|
console.log('Cleaning up storage...');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const keysToRemove = [];
|
const keysToRemove = [];
|
||||||
@ -339,13 +339,13 @@ export class StorageManager {
|
|||||||
// Remove identified keys
|
// Remove identified keys
|
||||||
keysToRemove.forEach(key => {
|
keysToRemove.forEach(key => {
|
||||||
localStorage.removeItem(key);
|
localStorage.removeItem(key);
|
||||||
console.log(`🗑️ Cleaned up: ${key}`);
|
console.log(` Cleaned up: ${key}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`🧹 Cleanup complete. Removed ${keysToRemove.length} items.`);
|
console.log(` Cleanup complete. Removed ${keysToRemove.length} items.`);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Cleanup failed:', error);
|
console.error('Cleanup failed:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -355,7 +355,7 @@ export class StorageManager {
|
|||||||
async clearAll() {
|
async clearAll() {
|
||||||
if (!this.isAvailable) return false;
|
if (!this.isAvailable) return false;
|
||||||
|
|
||||||
console.log('🗑️ Clearing all storage data...');
|
console.log('Clearing all storage data...');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const keysToRemove = [];
|
const keysToRemove = [];
|
||||||
@ -373,11 +373,11 @@ export class StorageManager {
|
|||||||
localStorage.removeItem(key);
|
localStorage.removeItem(key);
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`🗑️ Cleared ${keysToRemove.length} items from storage`);
|
console.log(` Cleared ${keysToRemove.length} items from storage`);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Failed to clear storage:', error);
|
console.error('Failed to clear storage:', error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -405,11 +405,11 @@ export class StorageManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('📤 Exported storage data');
|
console.log('Exported storage data');
|
||||||
return exportData;
|
return exportData;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Failed to export data:', error);
|
console.error('Failed to export data:', error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -431,11 +431,11 @@ export class StorageManager {
|
|||||||
localStorage.setItem(fullKey, JSON.stringify(value));
|
localStorage.setItem(fullKey, JSON.stringify(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`📥 Imported ${Object.keys(importData.data).length} items`);
|
console.log(` Imported ${Object.keys(importData.data).length} items`);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Failed to import data:', error);
|
console.error('Failed to import data:', error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -444,10 +444,10 @@ export class StorageManager {
|
|||||||
* Cleanup on destroy
|
* Cleanup on destroy
|
||||||
*/
|
*/
|
||||||
destroy() {
|
destroy() {
|
||||||
console.log('🧹 Storage Manager cleanup...');
|
console.log('Storage Manager cleanup...');
|
||||||
// Perform any necessary cleanup
|
// Perform any necessary cleanup
|
||||||
// For now, just run a cleanup to free up space
|
// For now, just run a cleanup to free up space
|
||||||
this.cleanup();
|
this.cleanup();
|
||||||
console.log('✅ Storage Manager cleanup complete');
|
console.log('Storage Manager cleanup complete');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -38,7 +38,7 @@ export class UIManager {
|
|||||||
* Initialize UI Manager
|
* Initialize UI Manager
|
||||||
*/
|
*/
|
||||||
async init() {
|
async init() {
|
||||||
console.log('🎨 Initializing UI Manager...');
|
console.log('Initializing UI Manager...');
|
||||||
|
|
||||||
// Get DOM elements
|
// Get DOM elements
|
||||||
this.findElements();
|
this.findElements();
|
||||||
@ -59,7 +59,7 @@ export class UIManager {
|
|||||||
this.websocket.on('processor_result', (payload) => this.onProcessorResult(payload));
|
this.websocket.on('processor_result', (payload) => this.onProcessorResult(payload));
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('✅ UI Manager initialized');
|
console.log('UI Manager initialized');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -263,12 +263,12 @@ export class UIManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleResize() {
|
handleResize() {
|
||||||
console.log('📱 Window resized');
|
console.log('Window resized');
|
||||||
// Charts handle their own resize
|
// Charts handle their own resize
|
||||||
}
|
}
|
||||||
|
|
||||||
// System status and theme methods simplified
|
// System status and theme methods simplified
|
||||||
updateSystemStatus(statusData) { console.log('📊 System status:', statusData); }
|
updateSystemStatus(statusData) { console.log('System status:', statusData); }
|
||||||
setTheme(theme) { document.documentElement.setAttribute('data-theme', theme); }
|
setTheme(theme) { document.documentElement.setAttribute('data-theme', theme); }
|
||||||
getCurrentTheme() { return document.documentElement.getAttribute('data-theme') || 'dark'; }
|
getCurrentTheme() { return document.documentElement.getAttribute('data-theme') || 'dark'; }
|
||||||
|
|
||||||
@ -280,7 +280,7 @@ export class UIManager {
|
|||||||
if (this.eventHandlers[eventType]) {
|
if (this.eventHandlers[eventType]) {
|
||||||
this.eventHandlers[eventType].forEach(handler => {
|
this.eventHandlers[eventType].forEach(handler => {
|
||||||
try { handler(...args); }
|
try { handler(...args); }
|
||||||
catch (error) { console.error(`❌ Error in ${eventType} handler:`, error); }
|
catch (error) { console.error(` Error in ${eventType} handler:`, error); }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -294,7 +294,7 @@ export class UIManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
console.log('🧹 Cleaning up UI Manager...');
|
console.log('Cleaning up UI Manager...');
|
||||||
|
|
||||||
// Clear event handlers
|
// Clear event handlers
|
||||||
Object.keys(this.eventHandlers).forEach(key => {
|
Object.keys(this.eventHandlers).forEach(key => {
|
||||||
@ -304,6 +304,6 @@ export class UIManager {
|
|||||||
// Clear processors
|
// Clear processors
|
||||||
this.processors.clear();
|
this.processors.clear();
|
||||||
|
|
||||||
console.log('✅ UI Manager cleanup complete');
|
console.log('UI Manager cleanup complete');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -120,47 +120,21 @@ export class ButtonState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set button to loading state with animation
|
* Set button to disabled state during request
|
||||||
* @param {HTMLElement} button - Button element
|
* @param {HTMLElement} button - Button element
|
||||||
* @param {boolean} loading - Whether to set loading state
|
* @param {boolean} loading - Whether to disable button
|
||||||
* @param {Object} originalState - Original button state to restore
|
* @param {Object} originalState - Original button state to restore
|
||||||
*/
|
*/
|
||||||
export function setButtonLoading(button, loading, originalState = {}) {
|
export function setButtonLoading(button, loading, originalState = {}) {
|
||||||
if (!button) return;
|
if (!button) return originalState;
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
// Save original state if not provided
|
if (!originalState.disabled) {
|
||||||
if (!originalState.text) {
|
originalState.disabled = button.disabled;
|
||||||
originalState.text = button.textContent;
|
|
||||||
originalState.icon = button.querySelector('i')?.getAttribute('data-lucide');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
button.disabled = true;
|
button.disabled = true;
|
||||||
button.classList.add('loading');
|
|
||||||
|
|
||||||
const icon = button.querySelector('i');
|
|
||||||
if (icon) {
|
|
||||||
icon.setAttribute('data-lucide', 'loader-2');
|
|
||||||
icon.style.animation = 'spin 1s linear infinite';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (window.lucide) {
|
|
||||||
window.lucide.createIcons();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
button.disabled = false;
|
button.disabled = originalState.disabled || false;
|
||||||
button.classList.remove('loading');
|
|
||||||
|
|
||||||
const icon = button.querySelector('i');
|
|
||||||
if (icon) {
|
|
||||||
icon.style.animation = '';
|
|
||||||
if (originalState.icon) {
|
|
||||||
icon.setAttribute('data-lucide', originalState.icon);
|
|
||||||
if (window.lucide) {
|
|
||||||
window.lucide.createIcons();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return originalState;
|
return originalState;
|
||||||
|
|||||||
@ -33,7 +33,7 @@ export class WebSocketManager {
|
|||||||
|
|
||||||
async connect() {
|
async connect() {
|
||||||
if (this.isConnected || this.isConnecting) {
|
if (this.isConnected || this.isConnecting) {
|
||||||
console.log('🔌 WebSocket already connected/connecting');
|
console.log('WebSocket already connected/connecting');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ export class WebSocketManager {
|
|||||||
this.isConnecting = true;
|
this.isConnecting = true;
|
||||||
this.emit('connecting');
|
this.emit('connecting');
|
||||||
|
|
||||||
console.log(`🔌 Connecting to WebSocket: ${this.config.url}`);
|
console.log(` Connecting to WebSocket: ${this.config.url}`);
|
||||||
|
|
||||||
this.ws = new WebSocket(this.config.url);
|
this.ws = new WebSocket(this.config.url);
|
||||||
this.setupWebSocketEvents();
|
this.setupWebSocketEvents();
|
||||||
@ -49,7 +49,7 @@ export class WebSocketManager {
|
|||||||
await this.waitForConnection(5000);
|
await this.waitForConnection(5000);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.isConnecting = false;
|
this.isConnecting = false;
|
||||||
console.error('❌ WebSocket connection failed:', error);
|
console.error('WebSocket connection failed:', error);
|
||||||
this.handleConnectionError(error);
|
this.handleConnectionError(error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
@ -85,7 +85,7 @@ export class WebSocketManager {
|
|||||||
if (!this.ws) return;
|
if (!this.ws) return;
|
||||||
|
|
||||||
this.ws.onopen = (event) => {
|
this.ws.onopen = (event) => {
|
||||||
console.log('✅ WebSocket connected');
|
console.log('WebSocket connected');
|
||||||
this.isConnected = true;
|
this.isConnected = true;
|
||||||
this.isConnecting = false;
|
this.isConnecting = false;
|
||||||
this.reconnectAttempts = 0;
|
this.reconnectAttempts = 0;
|
||||||
@ -106,7 +106,7 @@ export class WebSocketManager {
|
|||||||
try {
|
try {
|
||||||
this.handleMessage(event.data);
|
this.handleMessage(event.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Error processing WebSocket message:', error);
|
console.error('Error processing WebSocket message:', error);
|
||||||
this.notifications?.show?.({
|
this.notifications?.show?.({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
title: 'Message Error',
|
title: 'Message Error',
|
||||||
@ -116,12 +116,12 @@ export class WebSocketManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.ws.onerror = (error) => {
|
this.ws.onerror = (error) => {
|
||||||
console.error('❌ WebSocket error:', error);
|
console.error('WebSocket error:', error);
|
||||||
this.handleConnectionError(error);
|
this.handleConnectionError(error);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.ws.onclose = (event) => {
|
this.ws.onclose = (event) => {
|
||||||
console.log('🔌 WebSocket closed:', event.code, event.reason);
|
console.log('WebSocket closed:', event.code, event.reason);
|
||||||
this.handleDisconnection(event);
|
this.handleDisconnection(event);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -134,7 +134,7 @@ export class WebSocketManager {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (typeof data !== 'string') {
|
if (typeof data !== 'string') {
|
||||||
console.warn('⚠️ Received non-text message, ignoring');
|
console.warn('Received non-text message, ignoring');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data === 'ping') { this.handlePing(); return; }
|
if (data === 'ping') { this.handlePing(); return; }
|
||||||
@ -144,7 +144,7 @@ export class WebSocketManager {
|
|||||||
try {
|
try {
|
||||||
payload = JSON.parse(data);
|
payload = JSON.parse(data);
|
||||||
} catch (jsonError) {
|
} catch (jsonError) {
|
||||||
console.error('❌ Invalid JSON format:', data);
|
console.error('Invalid JSON format:', data);
|
||||||
console.error('JSON parse error:', jsonError);
|
console.error('JSON parse error:', jsonError);
|
||||||
this.notifications?.show?.({
|
this.notifications?.show?.({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
@ -154,7 +154,7 @@ export class WebSocketManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Публичное событие «data» — для всего, плюс более точечные:
|
// Public "data" event for everything, plus more specific ones:
|
||||||
this.emit('data', payload);
|
this.emit('data', payload);
|
||||||
|
|
||||||
switch (payload.type) {
|
switch (payload.type) {
|
||||||
@ -165,8 +165,8 @@ export class WebSocketManager {
|
|||||||
this.emit('processor_history', payload);
|
this.emit('processor_history', payload);
|
||||||
break;
|
break;
|
||||||
case 'error':
|
case 'error':
|
||||||
console.error('🔴 Server error:', payload);
|
console.error('Server error:', payload);
|
||||||
console.error('🔴 Error details:', {
|
console.error('Error details:', {
|
||||||
message: payload.message,
|
message: payload.message,
|
||||||
code: payload.code,
|
code: payload.code,
|
||||||
details: payload.details,
|
details: payload.details,
|
||||||
@ -180,11 +180,11 @@ export class WebSocketManager {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.warn('⚠️ Unknown payload type:', payload.type);
|
console.warn('Unknown payload type:', payload.type);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('❌ Failed to parse WebSocket JSON:', e);
|
console.error('Failed to parse WebSocket JSON:', e);
|
||||||
console.log('📝 Raw message:', data);
|
console.log('Raw message:', data);
|
||||||
this.notifications?.show?.({
|
this.notifications?.show?.({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
title: 'Message Processing Error',
|
title: 'Message Processing Error',
|
||||||
@ -205,7 +205,7 @@ export class WebSocketManager {
|
|||||||
}
|
}
|
||||||
handlePong() {
|
handlePong() {
|
||||||
if (this.lastPing) {
|
if (this.lastPing) {
|
||||||
console.log(`🏓 WebSocket latency: ${Date.now() - this.lastPing}ms`);
|
console.log(` WebSocket latency: ${Date.now() - this.lastPing}ms`);
|
||||||
this.lastPing = null;
|
this.lastPing = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -251,7 +251,7 @@ export class WebSocketManager {
|
|||||||
scheduleReconnect() {
|
scheduleReconnect() {
|
||||||
if (this.reconnectTimer) return;
|
if (this.reconnectTimer) return;
|
||||||
const delay = Math.min(this.config.reconnectInterval * Math.pow(2, this.reconnectAttempts), 30000);
|
const delay = Math.min(this.config.reconnectInterval * Math.pow(2, this.reconnectAttempts), 30000);
|
||||||
console.log(`🔄 Scheduling reconnect in ${delay}ms (attempt ${this.reconnectAttempts + 1})`);
|
console.log(` Scheduling reconnect in ${delay}ms (attempt ${this.reconnectAttempts + 1})`);
|
||||||
this.reconnectTimer = setTimeout(() => {
|
this.reconnectTimer = setTimeout(() => {
|
||||||
this.reconnectTimer = null;
|
this.reconnectTimer = null;
|
||||||
this.reconnect();
|
this.reconnect();
|
||||||
@ -260,7 +260,7 @@ export class WebSocketManager {
|
|||||||
|
|
||||||
async reconnect() {
|
async reconnect() {
|
||||||
if (this.reconnectAttempts >= this.config.maxReconnectAttempts) {
|
if (this.reconnectAttempts >= this.config.maxReconnectAttempts) {
|
||||||
console.error('❌ Max reconnection attempts reached');
|
console.error('Max reconnection attempts reached');
|
||||||
this.notifications?.show?.({
|
this.notifications?.show?.({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
title: 'Connection Failed',
|
title: 'Connection Failed',
|
||||||
@ -271,16 +271,16 @@ export class WebSocketManager {
|
|||||||
this.reconnectAttempts++;
|
this.reconnectAttempts++;
|
||||||
if (this.ws) { this.ws.close(); this.ws = null; }
|
if (this.ws) { this.ws.close(); this.ws = null; }
|
||||||
try { await this.connect(); }
|
try { await this.connect(); }
|
||||||
catch (error) { console.error(`❌ Reconnection attempt ${this.reconnectAttempts} failed:`, error); this.scheduleReconnect(); }
|
catch (error) { console.error(` Reconnection attempt ${this.reconnectAttempts} failed:`, error); this.scheduleReconnect(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
send(data) {
|
send(data) {
|
||||||
if (!this.isConnected || !this.ws) {
|
if (!this.isConnected || !this.ws) {
|
||||||
if (this.messageQueue.length < this.maxQueueSize) {
|
if (this.messageQueue.length < this.maxQueueSize) {
|
||||||
this.messageQueue.push(data);
|
this.messageQueue.push(data);
|
||||||
console.log('📤 Message queued (not connected)');
|
console.log('Message queued (not connected)');
|
||||||
} else {
|
} else {
|
||||||
console.warn('⚠️ Message queue full, dropping message');
|
console.warn('Message queue full, dropping message');
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -289,22 +289,22 @@ export class WebSocketManager {
|
|||||||
this.ws.send(msg);
|
this.ws.send(msg);
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('❌ Failed to send message:', e);
|
console.error('Failed to send message:', e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
processPendingMessages() {
|
processPendingMessages() {
|
||||||
if (this.messageQueue.length === 0) return;
|
if (this.messageQueue.length === 0) return;
|
||||||
console.log(`📤 Processing ${this.messageQueue.length} queued messages`);
|
console.log(` Processing ${this.messageQueue.length} queued messages`);
|
||||||
const messages = [...this.messageQueue];
|
const messages = [...this.messageQueue];
|
||||||
this.messageQueue = [];
|
this.messageQueue = [];
|
||||||
messages.forEach(m => this.send(m));
|
messages.forEach(m => this.send(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
// === ПУБЛИЧНОЕ API ДЛЯ СОВМЕСТИМОСТИ С БЭКЕНДОМ ===
|
// === PUBLIC API FOR BACKEND COMPATIBILITY ===
|
||||||
|
|
||||||
/** Запросить пересчёт с обновлением конфигурации (СУЩЕСТВУЕТ НА БЭКЕНДЕ) */
|
/** Request recalculation with config update (EXISTS ON BACKEND) */
|
||||||
recalculate(processorId, configUpdates = undefined) {
|
recalculate(processorId, configUpdates = undefined) {
|
||||||
return this.send({
|
return this.send({
|
||||||
type: 'recalculate',
|
type: 'recalculate',
|
||||||
@ -313,7 +313,7 @@ export class WebSocketManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Получить историю результатов процессора (СУЩЕСТВУЕТ НА БЭКЕНДЕ) */
|
/** Get processor results history (EXISTS ON BACKEND) */
|
||||||
getHistory(processorId, limit = 10) {
|
getHistory(processorId, limit = 10) {
|
||||||
return this.send({
|
return this.send({
|
||||||
type: 'get_history',
|
type: 'get_history',
|
||||||
@ -336,12 +336,12 @@ export class WebSocketManager {
|
|||||||
emit(event, data) {
|
emit(event, data) {
|
||||||
if (!this.eventListeners.has(event)) return;
|
if (!this.eventListeners.has(event)) return;
|
||||||
this.eventListeners.get(event).forEach(cb => {
|
this.eventListeners.get(event).forEach(cb => {
|
||||||
try { cb(data); } catch (e) { console.error(`❌ Error in event listener for ${event}:`, e); }
|
try { cb(data); } catch (e) { console.error(` Error in event listener for ${event}:`, e); }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnect() {
|
disconnect() {
|
||||||
console.log('🔌 Disconnecting WebSocket');
|
console.log('Disconnecting WebSocket');
|
||||||
if (this.reconnectTimer) { clearTimeout(this.reconnectTimer); this.reconnectTimer = null; }
|
if (this.reconnectTimer) { clearTimeout(this.reconnectTimer); this.reconnectTimer = null; }
|
||||||
if (this.pingTimer) { clearInterval(this.pingTimer); this.pingTimer = null; }
|
if (this.pingTimer) { clearInterval(this.pingTimer); this.pingTimer = null; }
|
||||||
if (this.ws) { this.ws.close(1000, 'Manual disconnect'); this.ws = null; }
|
if (this.ws) { this.ws.close(1000, 'Manual disconnect'); this.ws = null; }
|
||||||
|
|||||||
Reference in New Issue
Block a user