173 lines
4.6 KiB
TypeScript
173 lines
4.6 KiB
TypeScript
import type { IActionConfig } from './actions/base-action.ts';
|
|
import { NETWORK } from './constants.ts';
|
|
import type { IUpsStatus as IProtocolUpsStatus } from './snmp/types.ts';
|
|
import { createInitialUpsStatus, type IUpsIdentity, type IUpsStatus } from './ups-status.ts';
|
|
|
|
export interface ISuccessfulUpsPollSnapshot {
|
|
updatedStatus: IUpsStatus;
|
|
transition: 'none' | 'recovered' | 'powerStatusChange';
|
|
previousStatus?: IUpsStatus;
|
|
downtimeSeconds?: number;
|
|
}
|
|
|
|
export interface IFailedUpsPollSnapshot {
|
|
updatedStatus: IUpsStatus;
|
|
transition: 'none' | 'unreachable';
|
|
failures: number;
|
|
previousStatus?: IUpsStatus;
|
|
}
|
|
|
|
export function ensureUpsStatus(
|
|
currentStatus: IUpsStatus | undefined,
|
|
ups: IUpsIdentity,
|
|
now: number = Date.now(),
|
|
): IUpsStatus {
|
|
return currentStatus || createInitialUpsStatus(ups, now);
|
|
}
|
|
|
|
export function buildSuccessfulUpsPollSnapshot(
|
|
ups: IUpsIdentity,
|
|
polledStatus: IProtocolUpsStatus,
|
|
currentStatus: IUpsStatus | undefined,
|
|
currentTime: number,
|
|
): ISuccessfulUpsPollSnapshot {
|
|
const previousStatus = ensureUpsStatus(currentStatus, ups, currentTime);
|
|
const updatedStatus: IUpsStatus = {
|
|
id: ups.id,
|
|
name: ups.name,
|
|
powerStatus: polledStatus.powerStatus,
|
|
batteryCapacity: polledStatus.batteryCapacity,
|
|
batteryRuntime: polledStatus.batteryRuntime,
|
|
outputLoad: polledStatus.outputLoad,
|
|
outputPower: polledStatus.outputPower,
|
|
outputVoltage: polledStatus.outputVoltage,
|
|
outputCurrent: polledStatus.outputCurrent,
|
|
lastCheckTime: currentTime,
|
|
lastStatusChange: previousStatus.lastStatusChange || currentTime,
|
|
consecutiveFailures: 0,
|
|
unreachableSince: 0,
|
|
};
|
|
|
|
if (previousStatus.powerStatus === 'unreachable') {
|
|
updatedStatus.lastStatusChange = currentTime;
|
|
return {
|
|
updatedStatus,
|
|
transition: 'recovered',
|
|
previousStatus,
|
|
downtimeSeconds: Math.round((currentTime - previousStatus.unreachableSince) / 1000),
|
|
};
|
|
}
|
|
|
|
if (previousStatus.powerStatus !== polledStatus.powerStatus) {
|
|
updatedStatus.lastStatusChange = currentTime;
|
|
return {
|
|
updatedStatus,
|
|
transition: 'powerStatusChange',
|
|
previousStatus,
|
|
};
|
|
}
|
|
|
|
return {
|
|
updatedStatus,
|
|
transition: 'none',
|
|
previousStatus: currentStatus,
|
|
};
|
|
}
|
|
|
|
export function buildFailedUpsPollSnapshot(
|
|
ups: IUpsIdentity,
|
|
currentStatus: IUpsStatus | undefined,
|
|
currentTime: number,
|
|
): IFailedUpsPollSnapshot {
|
|
const previousStatus = ensureUpsStatus(currentStatus, ups, currentTime);
|
|
const failures = Math.min(
|
|
previousStatus.consecutiveFailures + 1,
|
|
NETWORK.MAX_CONSECUTIVE_FAILURES,
|
|
);
|
|
|
|
if (
|
|
failures >= NETWORK.CONSECUTIVE_FAILURE_THRESHOLD &&
|
|
previousStatus.powerStatus !== 'unreachable'
|
|
) {
|
|
return {
|
|
updatedStatus: {
|
|
...previousStatus,
|
|
consecutiveFailures: failures,
|
|
powerStatus: 'unreachable',
|
|
unreachableSince: currentTime,
|
|
lastStatusChange: currentTime,
|
|
},
|
|
transition: 'unreachable',
|
|
failures,
|
|
previousStatus,
|
|
};
|
|
}
|
|
|
|
return {
|
|
updatedStatus: {
|
|
...previousStatus,
|
|
consecutiveFailures: failures,
|
|
},
|
|
transition: 'none',
|
|
failures,
|
|
previousStatus: currentStatus,
|
|
};
|
|
}
|
|
|
|
export function hasThresholdViolation(
|
|
powerStatus: IProtocolUpsStatus['powerStatus'],
|
|
batteryCapacity: number,
|
|
batteryRuntime: number,
|
|
actions: IActionConfig[] | undefined,
|
|
): boolean {
|
|
return getActionThresholdStates(powerStatus, batteryCapacity, batteryRuntime, actions).some(
|
|
Boolean,
|
|
);
|
|
}
|
|
|
|
export function isActionThresholdExceeded(
|
|
actionConfig: IActionConfig,
|
|
powerStatus: IProtocolUpsStatus['powerStatus'],
|
|
batteryCapacity: number,
|
|
batteryRuntime: number,
|
|
): boolean {
|
|
if (powerStatus !== 'onBattery' || !actionConfig.thresholds) {
|
|
return false;
|
|
}
|
|
|
|
return (
|
|
batteryCapacity < actionConfig.thresholds.battery ||
|
|
batteryRuntime < actionConfig.thresholds.runtime
|
|
);
|
|
}
|
|
|
|
export function getActionThresholdStates(
|
|
powerStatus: IProtocolUpsStatus['powerStatus'],
|
|
batteryCapacity: number,
|
|
batteryRuntime: number,
|
|
actions: IActionConfig[] | undefined,
|
|
): boolean[] {
|
|
if (!actions || actions.length === 0) {
|
|
return [];
|
|
}
|
|
|
|
return actions.map((actionConfig) =>
|
|
isActionThresholdExceeded(actionConfig, powerStatus, batteryCapacity, batteryRuntime)
|
|
);
|
|
}
|
|
|
|
export function getEnteredThresholdIndexes(
|
|
previousStates: boolean[] | undefined,
|
|
currentStates: boolean[],
|
|
): number[] {
|
|
const enteredIndexes: number[] = [];
|
|
|
|
for (let index = 0; index < currentStates.length; index++) {
|
|
if (currentStates[index] && !previousStates?.[index]) {
|
|
enteredIndexes.push(index);
|
|
}
|
|
}
|
|
|
|
return enteredIndexes;
|
|
}
|