Files
WebUITest-RealProjecT/FrontEnd/pages/HomePage.tsx
arDTDev 86fe466b55 feat: Add VisionData panel, HW error display, and reel handler 3D model
- Add hardware error banner with priority system (motion > i/o > emergency)
- Add DIO status to HW status display with backend integration
- Remove status text from HW status, keep only LED indicators
- Add VisionDataPanel showing real-time recognized data for L/C/R ports
- Add GetVisionData API in MachineBridge with batch field support
- Add BroadcastVisionData function (250ms interval)
- Replace 3D model with detailed reel handler equipment
- Use OrthographicCamera with front view for distortion-free display
- Fix ProcessedDataPanel layout to avoid right sidebar overlap
- Show log viewer filename in error message when file not found

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-27 23:22:56 +09:00

137 lines
5.8 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { AlertTriangle, Siren } from 'lucide-react';
import { Machine3D } from '../components/Machine3D';
import { SettingsModal } from '../components/SettingsModal';
import { InitializeModal } from '../components/InitializeModal';
import { RecipePanel } from '../components/RecipePanel';
import { MotionPanel } from '../components/MotionPanel';
import { CyberPanel } from '../components/common/CyberPanel';
import { ModelInfoPanel } from '../components/ModelInfoPanel';
import { ProcessedDataPanel } from '../components/ProcessedDataPanel';
import { VisionDataPanel } from '../components/VisionDataPanel';
import { SystemStatusPanel } from '../components/SystemStatusPanel';
import { EventLogPanel } from '../components/EventLogPanel';
import { SystemState, Recipe, IOPoint, LogEntry, RobotTarget, ConfigItem } from '../types';
interface HomePageProps {
systemState: SystemState;
currentRecipe: Recipe;
robotTarget: RobotTarget;
logs: LogEntry[];
ioPoints: IOPoint[];
doorStates: { front: boolean; right: boolean; left: boolean; back: boolean };
isLowPressure: boolean;
isEmergencyStop: boolean;
isHostConnected: boolean;
activeTab: 'recipe' | 'motion' | 'camera' | 'setting' | 'initialize' | null;
onSelectRecipe: (r: Recipe) => void;
onMove: (axis: 'X' | 'Y' | 'Z', val: number) => void;
onControl: (action: 'start' | 'stop' | 'reset') => void;
onSaveConfig: (config: ConfigItem[]) => void;
onCloseTab: () => void;
videoRef: React.RefObject<HTMLVideoElement>;
}
export const HomePage: React.FC<HomePageProps> = ({
systemState,
currentRecipe,
robotTarget,
logs,
ioPoints,
doorStates,
isLowPressure,
isEmergencyStop,
isHostConnected,
activeTab,
onSelectRecipe,
onMove,
onControl,
onSaveConfig,
onCloseTab,
videoRef
}) => {
return (
<main className="relative w-full h-full">
{/* 3D Canvas (Background Layer) */}
<div className="absolute inset-0 z-0">
<Machine3D target={robotTarget} ioState={ioPoints} doorStates={doorStates} />
</div>
{/* Center Alarms */}
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-50 pointer-events-none flex flex-col items-center gap-4">
{!isHostConnected && !isEmergencyStop && (
<div className="bg-red-600/90 text-white p-8 border-4 border-red-500 shadow-glow-red flex items-center gap-6 animate-pulse">
<AlertTriangle className="w-16 h-16" />
<div>
<h1 className="text-4xl font-tech font-bold tracking-widest">HOST DISCONNECTED</h1>
<p className="text-center font-mono text-base">WAITING FOR CONNECTION...</p>
<p className="text-center font-mono text-sm mt-2 text-red-200">PLEASE CHECK THE HANDLER PROGRAM</p>
</div>
</div>
)}
{isEmergencyStop && (
<div className="bg-red-600/90 text-white p-8 border-4 border-red-500 shadow-glow-red flex items-center gap-6 animate-pulse">
<Siren className="w-16 h-16 animate-spin" />
<div>
<h1 className="text-5xl font-tech font-bold tracking-widest">EMERGENCY STOP</h1>
<p className="text-center font-mono text-lg">SYSTEM HALTED - RELEASE TO RESET</p>
</div>
</div>
)}
{isLowPressure && !isEmergencyStop && isHostConnected && (
<div className="bg-amber-500/80 text-black px-8 py-4 rounded font-bold text-2xl tracking-widest flex items-center gap-4 shadow-glow-red animate-bounce">
<AlertTriangle className="w-8 h-8" /> LOW AIR PRESSURE WARNING
</div>
)}
</div>
{/* Floating Panel (Left) */}
{activeTab === 'motion' && (
<div className="absolute left-6 top-0 bottom-52 w-[450px] z-20 animate-in slide-in-from-left-20 duration-500 fade-in">
<CyberPanel className="h-full flex flex-col">
<MotionPanel robotTarget={robotTarget} onMove={onMove} />
</CyberPanel>
</div>
)}
{/* Recipe Selection Modal */}
<RecipePanel
isOpen={activeTab === 'recipe'}
currentRecipe={currentRecipe}
onSelectRecipe={onSelectRecipe}
onClose={onCloseTab}
/>
{/* Settings Modal */}
<SettingsModal
isOpen={activeTab === 'setting'}
onClose={onCloseTab}
onSave={onSaveConfig}
/>
{/* Initialize Modal */}
<InitializeModal
isOpen={activeTab === 'initialize'}
onClose={onCloseTab}
/>
{/* Right Sidebar (Dashboard) */}
<div className="absolute right-6 top-0 bottom-52 w-80 z-20 flex flex-col gap-4">
<ModelInfoPanel currentRecipe={currentRecipe} />
<SystemStatusPanel systemState={systemState} onControl={onControl} />
<EventLogPanel logs={logs} />
</div>
{/* Left - Vision Data Panel (Recognized Data) - 세로 공간 최대 사용 */}
<div className="absolute left-6 top-0 bottom-52 w-[680px] z-20">
<VisionDataPanel />
</div>
{/* Bottom Docked - Processed Data Panel (우측 사이드바 피함) */}
<div className="absolute left-6 right-[356px] bottom-10 h-40 z-20">
<ProcessedDataPanel />
</div>
</main>
);
};