fixed realtime processor settings managment
This commit is contained in:
@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"open_air": true,
|
"open_air": false,
|
||||||
"axis": "abs",
|
"axis": "real",
|
||||||
"data_limitation": "ph_only_1",
|
"data_limitation": "ph_only_1",
|
||||||
"cut": 2.0,
|
"cut": 0.816,
|
||||||
"max": 5.0,
|
"max": 3.8,
|
||||||
"gain": 0.0,
|
"gain": 0.0,
|
||||||
"start_freq": 100.0,
|
"start_freq": 100.0,
|
||||||
"stop_freq": 8800.0,
|
"stop_freq": 8800.0,
|
||||||
|
|||||||
@ -229,10 +229,10 @@ class ProcessorWebSocketHandler:
|
|||||||
- Schedule a coroutine on the main event loop to broadcast to clients.
|
- Schedule a coroutine on the main event loop to broadcast to clients.
|
||||||
"""
|
"""
|
||||||
# Best-effort persistence
|
# Best-effort persistence
|
||||||
try:
|
# try:
|
||||||
self.data_storage.store_result(processor_id, result)
|
# self.data_storage.store_result(processor_id, result)
|
||||||
except Exception: # noqa: BLE001
|
# except Exception: # noqa: BLE001
|
||||||
logger.error("Failed to store processor result")
|
# logger.error("Failed to store processor result")
|
||||||
|
|
||||||
# Broadcast to clients
|
# Broadcast to clients
|
||||||
self._broadcast_result_sync(processor_id, result)
|
self._broadcast_result_sync(processor_id, result)
|
||||||
|
|||||||
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"name": "ыфвф",
|
||||||
|
"timestamp": "2025-09-30T19:05:27.995476",
|
||||||
|
"preset_filename": "s11_start100_stop8800_points1000_bw1khz.bin",
|
||||||
|
"description": "фывфф",
|
||||||
|
"metadata": {}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@ -11,6 +11,9 @@ export class ChartSettingsManager {
|
|||||||
this.lastSettingValues = {};
|
this.lastSettingValues = {};
|
||||||
this.settingDebounceTimers = {};
|
this.settingDebounceTimers = {};
|
||||||
this.lastUiParameters = new Map();
|
this.lastUiParameters = new Map();
|
||||||
|
this.lastUserInteractionTime = new Map();
|
||||||
|
this.parameterChangeTimestamps = {};
|
||||||
|
this.containerListeners = new Map(); // Store listeners per container
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSettings(processorId, settingsContainer, latestData) {
|
updateSettings(processorId, settingsContainer, latestData) {
|
||||||
@ -26,6 +29,13 @@ export class ChartSettingsManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if user recently interacted with controls
|
||||||
|
const lastInteraction = this.lastUserInteractionTime.get(processorId);
|
||||||
|
if (lastInteraction && Date.now() - lastInteraction < 1000) {
|
||||||
|
console.log(`Skipping update for ${processorId} - user is actively editing`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`Updating settings for ${processorId}`);
|
console.log(`Updating settings for ${processorId}`);
|
||||||
this.lastUiParameters.set(processorId, uiParameters ? JSON.parse(JSON.stringify(uiParameters)) : null);
|
this.lastUiParameters.set(processorId, uiParameters ? JSON.parse(JSON.stringify(uiParameters)) : null);
|
||||||
|
|
||||||
@ -47,13 +57,18 @@ export class ChartSettingsManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate settings HTML
|
// Smart update: only update changed parameters, skip focused elements
|
||||||
const settingsHtml = uiParameters.map(param =>
|
const hasExistingControls = settingsContainer.children.length > 0;
|
||||||
createParameterControl(param, processorId, 'chart')
|
if (hasExistingControls) {
|
||||||
).join('');
|
this.updateParametersSelectively(processorId, settingsContainer, uiParameters);
|
||||||
|
} else {
|
||||||
settingsContainer.innerHTML = settingsHtml;
|
// Initial render: full rebuild
|
||||||
this.setupEvents(settingsContainer, processorId);
|
const settingsHtml = uiParameters.map(param =>
|
||||||
|
createParameterControl(param, processorId, 'chart')
|
||||||
|
).join('');
|
||||||
|
settingsContainer.innerHTML = settingsHtml;
|
||||||
|
this.setupEvents(settingsContainer, processorId);
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize last values
|
// Initialize last values
|
||||||
if (uiParameters) {
|
if (uiParameters) {
|
||||||
@ -66,6 +81,73 @@ export class ChartSettingsManager {
|
|||||||
renderIcons(settingsContainer);
|
renderIcons(settingsContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateParametersSelectively(processorId, settingsContainer, newParameters) {
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
// Temporarily remove event listeners to prevent programmatic changes from triggering events
|
||||||
|
this.removeEventListeners(settingsContainer);
|
||||||
|
|
||||||
|
for (const param of newParameters) {
|
||||||
|
const settingElement = settingsContainer.querySelector(`[data-param="${param.name}"]`);
|
||||||
|
if (!settingElement) continue;
|
||||||
|
|
||||||
|
const settingKey = `${processorId}_${param.name}`;
|
||||||
|
const currentValue = this.lastSettingValues[settingKey];
|
||||||
|
|
||||||
|
console.log(`[UPDATE CHECK] ${param.name}: currentValue=${currentValue}, newValue=${param.value}`);
|
||||||
|
|
||||||
|
// ALWAYS sync lastSettingValues with backend value
|
||||||
|
const valueChanged = currentValue !== param.value;
|
||||||
|
if (valueChanged) {
|
||||||
|
this.lastSettingValues[settingKey] = param.value;
|
||||||
|
console.log(`[SYNCED] lastSettingValues["${settingKey}"] = ${param.value}`);
|
||||||
|
} else {
|
||||||
|
console.log(`[SKIP] ${param.name}: value unchanged`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip UI update if element has focus
|
||||||
|
if (settingElement.contains(document.activeElement)) {
|
||||||
|
console.log(`Skipping UI update for ${param.name} - has focus`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip UI update if user recently changed this parameter
|
||||||
|
const lastChange = this.parameterChangeTimestamps[settingKey];
|
||||||
|
if (lastChange && now - lastChange < 2000) {
|
||||||
|
console.log(`Skipping UI update for ${param.name} - user changed it ${now - lastChange}ms ago`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update UI control based on type (without triggering events)
|
||||||
|
if (param.type === 'toggle') {
|
||||||
|
const checkbox = settingElement.querySelector('input[type="checkbox"]');
|
||||||
|
if (checkbox && checkbox.checked !== param.value) {
|
||||||
|
console.log(`[UPDATE] Toggle ${param.name}: ${checkbox.checked} -> ${param.value}`);
|
||||||
|
checkbox.checked = param.value;
|
||||||
|
}
|
||||||
|
} else if (param.type === 'slider') {
|
||||||
|
const slider = settingElement.querySelector('input[type="range"]');
|
||||||
|
const valueDisplay = settingElement.querySelector('.chart-setting__value');
|
||||||
|
if (slider && parseFloat(slider.value) !== param.value) {
|
||||||
|
slider.value = param.value;
|
||||||
|
if (valueDisplay) {
|
||||||
|
valueDisplay.textContent = param.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (param.type === 'select') {
|
||||||
|
const select = settingElement.querySelector('select');
|
||||||
|
if (select && select.value !== String(param.value)) {
|
||||||
|
console.log(`[UPDATE] Select ${param.name}: ${select.value} -> ${param.value}`);
|
||||||
|
select.value = String(param.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-add event listeners after all updates are complete
|
||||||
|
this.restoreEventListeners(settingsContainer, processorId);
|
||||||
|
}
|
||||||
|
|
||||||
setupEvents(settingsContainer, processorId) {
|
setupEvents(settingsContainer, processorId) {
|
||||||
const onParamChange = (e) => {
|
const onParamChange = (e) => {
|
||||||
if (!e.target.closest('.chart-setting')) return;
|
if (!e.target.closest('.chart-setting')) return;
|
||||||
@ -77,16 +159,52 @@ export class ChartSettingsManager {
|
|||||||
this.handleButtonClick(e, processorId);
|
this.handleButtonClick(e, processorId);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Store listeners for later removal/restoration
|
||||||
|
this.containerListeners.set(settingsContainer, {
|
||||||
|
input: onParamChange,
|
||||||
|
change: onParamChange,
|
||||||
|
click: onButtonClick
|
||||||
|
});
|
||||||
|
|
||||||
settingsContainer.addEventListener('input', onParamChange);
|
settingsContainer.addEventListener('input', onParamChange);
|
||||||
settingsContainer.addEventListener('change', onParamChange);
|
settingsContainer.addEventListener('change', onParamChange);
|
||||||
settingsContainer.addEventListener('click', onButtonClick);
|
settingsContainer.addEventListener('click', onButtonClick);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeEventListeners(settingsContainer) {
|
||||||
|
const listeners = this.containerListeners.get(settingsContainer);
|
||||||
|
if (!listeners) return;
|
||||||
|
|
||||||
|
settingsContainer.removeEventListener('input', listeners.input);
|
||||||
|
settingsContainer.removeEventListener('change', listeners.change);
|
||||||
|
settingsContainer.removeEventListener('click', listeners.click);
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreEventListeners(settingsContainer, processorId) {
|
||||||
|
const listeners = this.containerListeners.get(settingsContainer);
|
||||||
|
if (!listeners) {
|
||||||
|
// If listeners don't exist, set them up
|
||||||
|
this.setupEvents(settingsContainer, processorId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
settingsContainer.addEventListener('input', listeners.input);
|
||||||
|
settingsContainer.addEventListener('change', listeners.change);
|
||||||
|
settingsContainer.addEventListener('click', listeners.click);
|
||||||
|
}
|
||||||
|
|
||||||
handleSettingChange(event, processorId) {
|
handleSettingChange(event, processorId) {
|
||||||
const settingElement = event.target.closest('.chart-setting');
|
const settingElement = event.target.closest('.chart-setting');
|
||||||
if (!settingElement) return;
|
if (!settingElement) return;
|
||||||
|
|
||||||
|
const now = Date.now();
|
||||||
const paramName = settingElement.dataset.param;
|
const paramName = settingElement.dataset.param;
|
||||||
|
const settingKey = `${processorId}_${paramName}`;
|
||||||
|
|
||||||
|
// Mark user interaction time for both processor and specific parameter
|
||||||
|
this.lastUserInteractionTime.set(processorId, now);
|
||||||
|
this.parameterChangeTimestamps[settingKey] = now;
|
||||||
|
|
||||||
const input = event.target;
|
const input = event.target;
|
||||||
|
|
||||||
let value;
|
let value;
|
||||||
@ -105,18 +223,21 @@ export class ChartSettingsManager {
|
|||||||
value = value === 'true';
|
value = value === 'true';
|
||||||
}
|
}
|
||||||
|
|
||||||
const settingKey = `${processorId}_${paramName}`;
|
|
||||||
let lastValue = this.lastSettingValues[settingKey];
|
let lastValue = this.lastSettingValues[settingKey];
|
||||||
if (typeof lastValue === 'string' && (lastValue === 'true' || lastValue === 'false')) {
|
if (typeof lastValue === 'string' && (lastValue === 'true' || lastValue === 'false')) {
|
||||||
lastValue = lastValue === 'true';
|
lastValue = lastValue === 'true';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`[CHANGE] ${processorId}.${paramName}: ${lastValue} -> ${value} (type: ${typeof value})`);
|
||||||
|
|
||||||
// Check for duplicate
|
// Check for duplicate
|
||||||
if (lastValue === value) {
|
if (lastValue === value) {
|
||||||
|
console.log(`Skipping duplicate value for ${processorId}.${paramName}: ${value}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.lastSettingValues[settingKey] = value;
|
this.lastSettingValues[settingKey] = value;
|
||||||
|
console.log(`[SAVED] lastSettingValues["${settingKey}"] = ${value}`);
|
||||||
|
|
||||||
// Debounce updates
|
// Debounce updates
|
||||||
const debounceKey = `${processorId}_${paramName}`;
|
const debounceKey = `${processorId}_${paramName}`;
|
||||||
|
|||||||
@ -67,7 +67,8 @@ export class ReferenceManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (previousPreset !== newPreset) {
|
// Clear current reference only if preset actually changed (not on initial load)
|
||||||
|
if (previousPreset !== null && previousPreset !== newPreset) {
|
||||||
await this.clearServerCurrentReference();
|
await this.clearServerCurrentReference();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user