feat: Implement vision menu, processed data panel, and UI improvements

- Add VisionMenu component with Camera (QRCode) and Barcode (Keyence) submenus
- Remove old CameraPanel component and replace with dropdown menu structure
- Add ProcessedDataPanel to display processed data in bottom dock (5 rows visible)
- Create SystemStatusPanel component with horizontal button layout (START/STOP/RESET)
- Create EventLogPanel component for better code organization
- Add device initialization feature with 7-axis progress tracking
- Add GetProcessedData and GetInitializeStatus backend methods
- Update Header menu layout to vertical (icon on top, text below) for more space
- Update HomePage layout with bottom-docked ProcessedDataPanel
- Refactor HomePage to use new modular components

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-25 22:18:55 +09:00
parent e46886cabc
commit 6219c4c60e
14 changed files with 1273 additions and 555 deletions

View File

@@ -1,14 +1,15 @@
import React, { useState, useEffect } from 'react';
import { Play, Square, RotateCw, AlertTriangle, Siren, Terminal } from 'lucide-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 { CameraPanel } from '../components/CameraPanel';
import { CyberPanel } from '../components/common/CyberPanel';
import { TechButton } from '../components/common/TechButton';
import { ModelInfoPanel } from '../components/ModelInfoPanel';
import { ProcessedDataPanel } from '../components/ProcessedDataPanel';
import { SystemStatusPanel } from '../components/SystemStatusPanel';
import { EventLogPanel } from '../components/EventLogPanel';
import { SystemState, Recipe, IOPoint, LogEntry, RobotTarget, ConfigItem } from '../types';
interface HomePageProps {
@@ -46,14 +47,8 @@ export const HomePage: React.FC<HomePageProps> = ({
onCloseTab,
videoRef
}) => {
useEffect(() => {
if (activeTab === 'camera' && navigator.mediaDevices?.getUserMedia) {
navigator.mediaDevices.getUserMedia({ video: true }).then(s => { if (videoRef.current) videoRef.current.srcObject = s });
}
}, [activeTab, videoRef]);
return (
<main className="relative w-full h-full flex gap-6 px-6">
<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} />
@@ -78,11 +73,10 @@ export const HomePage: React.FC<HomePageProps> = ({
</div>
{/* Floating Panel (Left) */}
{activeTab && activeTab !== 'setting' && activeTab !== 'recipe' && (
<div className="w-[450px] z-20 animate-in slide-in-from-left-20 duration-500 fade-in">
{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">
{activeTab === 'motion' && <MotionPanel robotTarget={robotTarget} onMove={onMove} />}
{activeTab === 'camera' && <CameraPanel videoRef={videoRef} />}
<MotionPanel robotTarget={robotTarget} onMove={onMove} />
</CyberPanel>
</div>
)}
@@ -109,54 +103,15 @@ export const HomePage: React.FC<HomePageProps> = ({
/>
{/* Right Sidebar (Dashboard) */}
<div className="w-80 ml-auto z-20 flex flex-col gap-4">
<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>
<CyberPanel className="flex-none">
<div className="mb-2 text-xs text-neon-blue font-bold tracking-widest uppercase">System Status</div>
<div className={`text-3xl font-tech font-bold mb-4 ${systemState === SystemState.RUNNING ? 'text-neon-green text-shadow-glow-green' : 'text-slate-400'}`}>
{systemState}
</div>
<div className="space-y-3">
<TechButton
variant="green"
className="w-full flex items-center justify-center gap-2"
active={systemState === SystemState.RUNNING}
onClick={() => onControl('start')}
>
<Play className="w-4 h-4" /> START AUTO
</TechButton>
<TechButton
variant="amber"
className="w-full flex items-center justify-center gap-2"
active={systemState === SystemState.PAUSED}
onClick={() => onControl('stop')}
>
<Square className="w-4 h-4 fill-current" /> STOP / PAUSE
</TechButton>
<TechButton
className="w-full flex items-center justify-center gap-2"
onClick={() => onControl('reset')}
>
<RotateCw className="w-4 h-4" /> SYSTEM RESET
</TechButton>
</div>
</CyberPanel>
<CyberPanel className="flex-1 flex flex-col min-h-0">
<div className="mb-2 flex items-center justify-between text-xs text-neon-blue font-bold tracking-widest uppercase border-b border-white/10 pb-2">
<span>Event Log</span>
<Terminal className="w-3 h-3" />
</div>
<div className="flex-1 overflow-y-auto font-mono text-[10px] space-y-1 pr-1 custom-scrollbar">
{logs.map(log => (
<div key={log.id} className={`flex gap-2 ${log.type === 'error' ? 'text-red-500' : log.type === 'warning' ? 'text-amber-400' : 'text-slate-400'}`}>
<span className="opacity-50">[{log.timestamp}]</span>
<span>{log.message}</span>
</div>
))}
</div>
</CyberPanel>
{/* Bottom Docked - Processed Data Panel */}
<div className="absolute left-6 right-6 bottom-10 h-48 z-20">
<ProcessedDataPanel />
</div>
</main>
);