warning system

This commit is contained in:
Siwat Sirichai 2025-06-21 13:29:01 +07:00
parent 5e029ff99c
commit 0c5c7bcb5f
10 changed files with 1074 additions and 54 deletions

View file

@ -1,36 +1,209 @@
import { NextResponse } from 'next/server';
import { SensorConfig } from '@/types/sensor';
// Sensor configuration that matches the API route
const SENSOR_CONFIG = [
// Head area
{ id: "head-1", x: 45, y: 15, zone: "head", label: "Head Left", pin: 2 },
{ id: "head-2", x: 55, y: 15, zone: "head", label: "Head Right", pin: 3 },
const SENSOR_CONFIG: SensorConfig[] = [
// Head area - Higher thresholds due to critical nature, faster escalation
{
id: "head-1",
x: 45,
y: 15,
zone: "head",
label: "Head Left",
pin: 2,
warningThreshold: 3000,
alarmThreshold: 3500,
warningDelayMs: 30000, // 30 seconds - fast escalation for head
baseNoise: 200
},
{
id: "head-2",
x: 55,
y: 15,
zone: "head",
label: "Head Right",
pin: 3,
warningThreshold: 3000,
alarmThreshold: 3500,
warningDelayMs: 30000, // 30 seconds
baseNoise: 150
},
// Shoulder area
{ id: "shoulder-1", x: 35, y: 25, zone: "shoulders", label: "Left Shoulder", pin: 4 },
{ id: "shoulder-2", x: 65, y: 25, zone: "shoulders", label: "Right Shoulder", pin: 5 },
// Shoulder area - Moderate thresholds, medium escalation time
{
id: "shoulder-1",
x: 35,
y: 25,
zone: "shoulders",
label: "Left Shoulder",
pin: 4,
warningThreshold: 2800,
alarmThreshold: 3200,
warningDelayMs: 45000, // 45 seconds
baseNoise: 250
},
{
id: "shoulder-2",
x: 65,
y: 25,
zone: "shoulders",
label: "Right Shoulder",
pin: 5,
warningThreshold: 2800,
alarmThreshold: 3200,
warningDelayMs: 45000, // 45 seconds
baseNoise: 220
},
// Upper back
{ id: "back-1", x: 40, y: 35, zone: "back", label: "Upper Back Left", pin: 6 },
{ id: "back-2", x: 50, y: 35, zone: "back", label: "Upper Back Center", pin: 7 },
{ id: "back-3", x: 60, y: 35, zone: "back", label: "Upper Back Right", pin: 8 },
// Upper back - Moderate thresholds, 1 minute escalation
{
id: "back-1",
x: 40,
y: 35,
zone: "back",
label: "Upper Back Left",
pin: 6,
warningThreshold: 2500,
alarmThreshold: 3000,
warningDelayMs: 60000, // 1 minute
baseNoise: 300
},
{
id: "back-2",
x: 50,
y: 35,
zone: "back",
label: "Upper Back Center",
pin: 7,
warningThreshold: 2500,
alarmThreshold: 3000,
warningDelayMs: 60000, // 1 minute
baseNoise: 350
},
{
id: "back-3",
x: 60,
y: 35,
zone: "back",
label: "Upper Back Right",
pin: 8,
warningThreshold: 2500,
alarmThreshold: 3000,
warningDelayMs: 60000, // 1 minute
baseNoise: 280
},
// Lower back/Hip area
{ id: "hip-1", x: 35, y: 50, zone: "hips", label: "Left Hip", pin: 9 },
{ id: "hip-2", x: 50, y: 50, zone: "hips", label: "Lower Back", pin: 10 },
{ id: "hip-3", x: 65, y: 50, zone: "hips", label: "Right Hip", pin: 11 },
// Lower back/Hip area - Lower thresholds, 90 second escalation
{
id: "hip-1",
x: 35,
y: 50,
zone: "hips",
label: "Left Hip",
pin: 9,
warningThreshold: 2200,
alarmThreshold: 2800,
warningDelayMs: 90000, // 90 seconds
baseNoise: 400
},
{
id: "hip-2",
x: 50,
y: 50,
zone: "hips",
label: "Lower Back",
pin: 10,
warningThreshold: 2200,
alarmThreshold: 2800,
warningDelayMs: 90000, // 90 seconds
baseNoise: 450
},
{
id: "hip-3",
x: 65,
y: 50,
zone: "hips",
label: "Right Hip",
pin: 11,
warningThreshold: 2200,
alarmThreshold: 2800,
warningDelayMs: 90000, // 90 seconds
baseNoise: 380
},
// Thigh area
{ id: "thigh-1", x: 40, y: 65, zone: "legs", label: "Left Thigh", pin: 12 },
{ id: "thigh-2", x: 60, y: 65, zone: "legs", label: "Right Thigh", pin: 13 },
// Thigh area - Lower thresholds, 2 minute escalation
{
id: "thigh-1",
x: 40,
y: 65,
zone: "legs",
label: "Left Thigh",
pin: 12,
warningThreshold: 2000,
alarmThreshold: 2500,
warningDelayMs: 120000, // 2 minutes
baseNoise: 320
},
{
id: "thigh-2",
x: 60,
y: 65,
zone: "legs",
label: "Right Thigh",
pin: 13,
warningThreshold: 2000,
alarmThreshold: 2500,
warningDelayMs: 120000, // 2 minutes
baseNoise: 300
},
// Calf area (mock data)
{ id: "calf-1", x: 40, y: 75, zone: "legs", label: "Left Calf" },
{ id: "calf-2", x: 60, y: 75, zone: "legs", label: "Right Calf" },
// Calf area (mock data) - Lower thresholds, 2.5 minute escalation
{
id: "calf-1",
x: 40,
y: 75,
zone: "legs",
label: "Left Calf",
warningThreshold: 1800,
alarmThreshold: 2200,
warningDelayMs: 150000, // 2.5 minutes
baseNoise: 200
},
{
id: "calf-2",
x: 60,
y: 75,
zone: "legs",
label: "Right Calf",
warningThreshold: 1800,
alarmThreshold: 2200,
warningDelayMs: 150000, // 2.5 minutes
baseNoise: 220
},
// Feet (mock data)
{ id: "feet-1", x: 45, y: 85, zone: "feet", label: "Left Foot" },
{ id: "feet-2", x: 55, y: 85, zone: "feet", label: "Right Foot" },
// Feet (mock data) - Lowest thresholds, 3 minute escalation
{
id: "feet-1",
x: 45,
y: 85,
zone: "feet",
label: "Left Foot",
warningThreshold: 1500,
alarmThreshold: 1800,
warningDelayMs: 180000, // 3 minutes
baseNoise: 150
},
{
id: "feet-2",
x: 55,
y: 85,
zone: "feet",
label: "Right Foot",
warningThreshold: 1500,
alarmThreshold: 1800,
warningDelayMs: 180000, // 3 minutes
baseNoise: 160
},
];
export async function GET() {

View file

@ -1,9 +1,10 @@
import { NextRequest, NextResponse } from 'next/server';
import { BedHardware, PinState, PinChange } from '@/services/BedHardware';
import { SensorDataStorage, SensorDataPoint } from '@/services/SensorDataStorage';
import { SensorConfig } from '@/types/sensor';
// Complete sensor configuration with positions, pin mappings, and thresholds
const SENSOR_CONFIG = [
const SENSOR_CONFIG: SensorConfig[] = [
// Head area
{ id: "head-1", x: 45, y: 15, zone: "head", label: "Head Left", pin: 2, baseNoise: 200, warningThreshold: 3000, alarmThreshold: 3500, warningDelayMs: 30000 },
{ id: "head-2", x: 55, y: 15, zone: "head", label: "Head Right", pin: 3, baseNoise: 150, warningThreshold: 3000, alarmThreshold: 3500, warningDelayMs: 30000 },
@ -235,6 +236,25 @@ function updateMockSensorData() {
const currentSensor = sensorData[sensor.id];
const variation = (Math.random() - 0.5) * 200; // Larger variation for analog values
const newValue = Math.max(0, Math.min(4095, currentSensor.value + variation));
const timestamp = Date.now();
// Determine status based on thresholds
let status = 'normal';
let warningStartTime = currentSensor.warningStartTime;
if (newValue >= sensor.alarmThreshold) {
status = 'alarm';
warningStartTime = undefined; // Clear warning timer for immediate alarm
} else if (newValue >= sensor.warningThreshold) {
status = 'warning';
if (!warningStartTime) {
warningStartTime = timestamp; // Start warning timer
} else if (timestamp - warningStartTime >= sensor.warningDelayMs) {
status = 'alarm'; // Escalate to alarm after delay
}
} else {
warningStartTime = undefined; // Clear warning timer
}
sensorData[sensor.id] = {
...currentSensor,
@ -248,8 +268,8 @@ function updateMockSensorData() {
value: newValue,
}
],
status: newValue >= sensor.alarmThreshold ? 'alarm' :
newValue >= sensor.warningThreshold ? 'warning' : 'normal'
status,
warningStartTime
};
}
});

View file

@ -0,0 +1,60 @@
import { NextResponse } from 'next/server';
import { ZONE_CONFIGS, validateSensorConfig } from '@/utils/sensorConfig';
export async function GET() {
try {
return NextResponse.json({
success: true,
zones: ZONE_CONFIGS,
description: 'Available sensor zones with default threshold configurations',
timestamp: new Date().toISOString()
});
} catch (error) {
console.error('Zone config API error:', error);
return NextResponse.json({
success: false,
error: 'Failed to get zone configurations',
zones: {},
timestamp: new Date().toISOString()
}, { status: 500 });
}
}
export async function POST(request: Request) {
try {
const sensorConfigs = await request.json();
if (!Array.isArray(sensorConfigs)) {
return NextResponse.json({
success: false,
error: 'Expected array of sensor configurations'
}, { status: 400 });
}
const validationResults = sensorConfigs.map(config => ({
sensorId: config.id,
...validateSensorConfig(config)
}));
const hasErrors = validationResults.some(result => !result.isValid);
return NextResponse.json({
success: !hasErrors,
validationResults,
summary: {
total: sensorConfigs.length,
valid: validationResults.filter(r => r.isValid).length,
withWarnings: validationResults.filter(r => r.warnings.length > 0).length,
withErrors: validationResults.filter(r => !r.isValid).length
},
timestamp: new Date().toISOString()
});
} catch (error) {
console.error('Sensor validation API error:', error);
return NextResponse.json({
success: false,
error: 'Failed to validate sensor configurations'
}, { status: 500 });
}
}

View file

@ -0,0 +1,197 @@
import { NextRequest, NextResponse } from 'next/server';
interface TestSensorData {
sensorId: string;
value: number;
status: string;
warningStartTime?: number;
}
// Test scenarios for the bed pressure monitoring system
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { scenario } = body;
let testData: TestSensorData[] = [];
switch (scenario) {
case 'normal':
testData = generateNormalScenario();
break;
case 'warning':
testData = generateWarningScenario();
break;
case 'alarm':
testData = generateAlarmScenario();
break;
case 'escalation':
testData = generateEscalationScenario();
break;
case 'mixed':
testData = generateMixedScenario();
break;
default:
return NextResponse.json({
success: false,
error: 'Invalid scenario. Use: normal, warning, alarm, escalation, or mixed'
}, { status: 400 });
}
return NextResponse.json({
success: true,
scenario,
message: `Generated ${scenario} test scenario`,
testData,
timestamp: new Date().toISOString()
});
} catch (error) {
console.error('Test scenario API error:', error);
return NextResponse.json({
success: false,
error: 'Failed to generate test scenario'
}, { status: 500 });
}
}
function generateNormalScenario() {
return [
{ sensorId: 'head-1', value: 1200, status: 'normal' },
{ sensorId: 'head-2', value: 1150, status: 'normal' },
{ sensorId: 'shoulder-1', value: 1800, status: 'normal' },
{ sensorId: 'shoulder-2', value: 1750, status: 'normal' },
{ sensorId: 'back-1', value: 2000, status: 'normal' },
{ sensorId: 'back-2', value: 2100, status: 'normal' },
{ sensorId: 'back-3', value: 1950, status: 'normal' },
{ sensorId: 'hip-1', value: 1900, status: 'normal' },
{ sensorId: 'hip-2', value: 2000, status: 'normal' },
{ sensorId: 'hip-3', value: 1850, status: 'normal' },
{ sensorId: 'thigh-1', value: 1600, status: 'normal' },
{ sensorId: 'thigh-2', value: 1550, status: 'normal' },
{ sensorId: 'calf-1', value: 1400, status: 'normal' },
{ sensorId: 'calf-2', value: 1350, status: 'normal' },
{ sensorId: 'feet-1', value: 1200, status: 'normal' },
{ sensorId: 'feet-2', value: 1150, status: 'normal' }
];
}
function generateWarningScenario() {
return [
{ sensorId: 'head-1', value: 3100, status: 'warning' }, // Above warning threshold
{ sensorId: 'head-2', value: 1150, status: 'normal' },
{ sensorId: 'shoulder-1', value: 2900, status: 'warning' }, // Above warning threshold
{ sensorId: 'shoulder-2', value: 1750, status: 'normal' },
{ sensorId: 'back-1', value: 2600, status: 'warning' }, // Above warning threshold
{ sensorId: 'back-2', value: 2100, status: 'normal' },
{ sensorId: 'back-3', value: 1950, status: 'normal' },
{ sensorId: 'hip-1', value: 1900, status: 'normal' },
{ sensorId: 'hip-2', value: 2300, status: 'warning' }, // Above warning threshold
{ sensorId: 'hip-3', value: 1850, status: 'normal' },
{ sensorId: 'thigh-1', value: 1600, status: 'normal' },
{ sensorId: 'thigh-2', value: 1550, status: 'normal' },
{ sensorId: 'calf-1', value: 1400, status: 'normal' },
{ sensorId: 'calf-2', value: 1350, status: 'normal' },
{ sensorId: 'feet-1', value: 1200, status: 'normal' },
{ sensorId: 'feet-2', value: 1150, status: 'normal' }
];
}
function generateAlarmScenario() {
return [
{ sensorId: 'head-1', value: 3600, status: 'alarm' }, // Above alarm threshold
{ sensorId: 'head-2', value: 3550, status: 'alarm' }, // Above alarm threshold
{ sensorId: 'shoulder-1', value: 3300, status: 'alarm' }, // Above alarm threshold
{ sensorId: 'shoulder-2', value: 1750, status: 'normal' },
{ sensorId: 'back-1', value: 3100, status: 'alarm' }, // Above alarm threshold
{ sensorId: 'back-2', value: 2100, status: 'normal' },
{ sensorId: 'back-3', value: 1950, status: 'normal' },
{ sensorId: 'hip-1', value: 2900, status: 'alarm' }, // Above alarm threshold
{ sensorId: 'hip-2', value: 2000, status: 'normal' },
{ sensorId: 'hip-3', value: 1850, status: 'normal' },
{ sensorId: 'thigh-1', value: 1600, status: 'normal' },
{ sensorId: 'thigh-2', value: 1550, status: 'normal' },
{ sensorId: 'calf-1', value: 1400, status: 'normal' },
{ sensorId: 'calf-2', value: 1350, status: 'normal' },
{ sensorId: 'feet-1', value: 1200, status: 'normal' },
{ sensorId: 'feet-2', value: 1150, status: 'normal' }
];
}
function generateEscalationScenario() {
// This scenario would simulate sensors that have been in warning state for a while
// and are about to escalate to alarm
const now = Date.now();
const warningStartTime = now - 25000; // Started warning 25 seconds ago (close to 30s threshold)
return [
{ sensorId: 'head-1', value: 3100, status: 'warning', warningStartTime },
{ sensorId: 'head-2', value: 1150, status: 'normal' },
{ sensorId: 'shoulder-1', value: 2900, status: 'warning', warningStartTime: now - 40000 }, // Close to 45s threshold
{ sensorId: 'shoulder-2', value: 1750, status: 'normal' },
{ sensorId: 'back-1', value: 2600, status: 'warning', warningStartTime: now - 55000 }, // Close to 60s threshold
{ sensorId: 'back-2', value: 2100, status: 'normal' },
{ sensorId: 'back-3', value: 1950, status: 'normal' },
{ sensorId: 'hip-1', value: 1900, status: 'normal' },
{ sensorId: 'hip-2', value: 2300, status: 'warning', warningStartTime: now - 85000 }, // Close to 90s threshold
{ sensorId: 'hip-3', value: 1850, status: 'normal' },
{ sensorId: 'thigh-1', value: 2100, status: 'warning', warningStartTime: now - 115000 }, // Close to 120s threshold
{ sensorId: 'thigh-2', value: 1550, status: 'normal' },
{ sensorId: 'calf-1', value: 1400, status: 'normal' },
{ sensorId: 'calf-2', value: 1350, status: 'normal' },
{ sensorId: 'feet-1', value: 1200, status: 'normal' },
{ sensorId: 'feet-2', value: 1150, status: 'normal' }
];
}
function generateMixedScenario() {
const now = Date.now();
return [
{ sensorId: 'head-1', value: 3600, status: 'alarm' }, // Immediate alarm
{ sensorId: 'head-2', value: 3100, status: 'warning', warningStartTime: now - 10000 }, // Recent warning
{ sensorId: 'shoulder-1', value: 2900, status: 'warning', warningStartTime: now - 40000 }, // Long warning
{ sensorId: 'shoulder-2', value: 1750, status: 'normal' },
{ sensorId: 'back-1', value: 3100, status: 'alarm' }, // Immediate alarm
{ sensorId: 'back-2', value: 2600, status: 'warning', warningStartTime: now - 30000 }, // Warning close to escalation
{ sensorId: 'back-3', value: 1950, status: 'normal' },
{ sensorId: 'hip-1', value: 1900, status: 'normal' },
{ sensorId: 'hip-2', value: 2900, status: 'alarm' }, // Immediate alarm
{ sensorId: 'hip-3', value: 2300, status: 'warning', warningStartTime: now - 60000 }, // Warning
{ sensorId: 'thigh-1', value: 1600, status: 'normal' },
{ sensorId: 'thigh-2', value: 2100, status: 'warning', warningStartTime: now - 90000 }, // Warning
{ sensorId: 'calf-1', value: 2300, status: 'alarm' }, // Immediate alarm
{ sensorId: 'calf-2', value: 1900, status: 'warning', warningStartTime: now - 120000 }, // Warning
{ sensorId: 'feet-1', value: 1200, status: 'normal' },
{ sensorId: 'feet-2', value: 1900, status: 'alarm' } // Immediate alarm
];
}
export async function GET() {
return NextResponse.json({
success: true,
availableScenarios: [
{
name: 'normal',
description: 'All sensors operating within normal ranges'
},
{
name: 'warning',
description: 'Several sensors in warning state (above warning threshold)'
},
{
name: 'alarm',
description: 'Multiple sensors in immediate alarm state (above alarm threshold)'
},
{
name: 'escalation',
description: 'Sensors in warning state close to escalating to alarm after delay'
},
{
name: 'mixed',
description: 'Mixed scenario with normal, warning, and alarm states'
}
],
usage: 'POST to /api/test-scenarios with body: { "scenario": "scenario_name" }'
});
}