Files
go-service/cmd/server/web/js/state.ts

193 lines
5.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// state.ts - Peak Holder алгоритм с таймером
interface AppState {
maxPoints: number;
signalHistory: number[];
lastBufferSize: number;
// Peak Holder
peakValue: number; // Текущее удерживаемое пиковое значение амплитуды
currentValue: number; // Текущее реальное значение амплитуды
peakTimer: number | null; // Таймер для сброса пика амплитуды
peakHoldTime: number; // Время удержания пика в миллисекундах
// Peak Holder для уровня сигнала
peakSignalLevel: number;
currentSignalLevel: number;
signalPeakTimer: number | null;
signalPeakHoldTime: number;
hasPeak: boolean;
}
type PeakHolderListener = () => void;
export const state: AppState = {
maxPoints: 1000,
signalHistory: [],
lastBufferSize: 0,
peakValue: 0,
currentValue: 0,
peakTimer: null,
peakHoldTime: 3000, // 3 секунды
peakSignalLevel: 0,
currentSignalLevel: 0,
signalPeakTimer: null,
signalPeakHoldTime: 3000,
hasPeak: false,
};
let peakHolderListener: PeakHolderListener | null = null;
/** Обновление UI при сбросе peak holder по таймеру (между опросами API). */
export function setPeakHolderListener(listener: PeakHolderListener | null): void {
peakHolderListener = listener;
}
function notifyPeakHolderChange(): void {
if (peakHolderListener) {
peakHolderListener();
}
}
function scheduleAmplitudePeakRelease(): void {
// Сбрасываем старый таймер
if (state.peakTimer !== null) {
clearTimeout(state.peakTimer);
state.peakTimer = null;
}
// Запускаем новый таймер на 3 секунды
state.peakTimer = window.setTimeout(() => {
state.peakValue = state.currentValue;
state.peakTimer = null;
state.hasPeak = false;
console.log(`[Peak] Сброс пика до: ${state.peakValue}`);
notifyPeakHolderChange();
}, state.peakHoldTime);
}
function scheduleSignalPeakRelease(): void {
// Сбрасываем старый таймер
if (state.signalPeakTimer !== null) {
clearTimeout(state.signalPeakTimer);
state.signalPeakTimer = null;
}
// Запускаем новый таймер на 3 секунды
state.signalPeakTimer = window.setTimeout(() => {
// Сбрасываем пик к текущему уровню
state.peakSignalLevel = state.currentSignalLevel;
state.signalPeakTimer = null;
state.hasPeak = false;
console.log(`[SignalPeak] Сброс пика до: ${state.peakSignalLevel}`);
notifyPeakHolderChange();
}, state.signalPeakHoldTime);
}
// ========== ФУНКЦИИ ДЛЯ АМПЛИТУДЫ ==========
export function updatePeakHolder(target: number): number {
// Обновляем текущее значение
state.currentValue = target;
// Если новое значение больше текущего пика
if (target > state.peakValue) {
// Устанавливаем новый пик
state.peakValue = target;
state.hasPeak = true;
scheduleAmplitudePeakRelease();
} else if (target < state.peakValue && state.peakTimer === null) {
// Пик выше текущего значения, но таймер пропал — перезапускаем удержание
scheduleAmplitudePeakRelease();
} // Возвращаем текущий пик
return state.peakValue;
}
// ========== ФУНКЦИИ ДЛЯ УРОВНЯ СИГНАЛА ==========
export function updateSignalPeakHolder(level: number): number {
// Обновляем текущее значение
state.currentSignalLevel = level;
// Если новый уровень больше пика — устанавливаем новый пик
if (level > state.peakSignalLevel) {
state.peakSignalLevel = level;
state.hasPeak = true;
scheduleSignalPeakRelease();
} else if (level < state.peakSignalLevel && state.signalPeakTimer === null) {
scheduleSignalPeakRelease();
}
// Возвращаем текущий пик
return state.peakSignalLevel;
}
// ========== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ==========
export function forceResetSignalPeak(): void {
if (state.signalPeakTimer !== null) {
clearTimeout(state.signalPeakTimer);
state.signalPeakTimer = null;
}
state.peakSignalLevel = 0;
state.currentSignalLevel = 0;
state.hasPeak = false;
}
export function setSignalPeakHoldTime(ms: number): void {
state.signalPeakHoldTime = ms;
}
export function getSignalPeakInfo(): { peak: number; current: number; timerActive: boolean } {
return {
peak: state.peakSignalLevel,
current: state.currentSignalLevel,
timerActive: state.signalPeakTimer !== null,
};
}
export function forceResetPeak(): void {
if (state.peakTimer !== null) {
clearTimeout(state.peakTimer);
state.peakTimer = null;
}
state.peakValue = 0;
state.currentValue = 0;
state.hasPeak = false;
forceResetSignalPeak();
}
export function addSignal(value: number): void {
state.signalHistory.push(value);
if (state.signalHistory.length > state.maxPoints) {
state.signalHistory = state.signalHistory.slice(-state.maxPoints);
}
}
export function addSignals(values: number[], bufferSize: number): void {
if (!Array.isArray(values) || values.length === 0) return;
const newBytes = bufferSize - state.lastBufferSize;
if (newBytes > 0 && newBytes <= values.length) {
const fresh = values.slice(-newBytes);
const decoded = fresh.map(b => b & 0x3F);
state.signalHistory.push(...decoded);
}
state.lastBufferSize = bufferSize;
if (state.signalHistory.length > state.maxPoints) {
state.signalHistory = state.signalHistory.slice(-state.maxPoints);
}
}
export function resetBufferTracking(): void {
state.lastBufferSize = 0;
forceResetPeak();
}