Update: Relocate AutoRun controls and cleanup

This commit is contained in:
2025-12-19 00:05:27 +09:00
parent c4089aeb20
commit 051138489b
3 changed files with 1227 additions and 1217 deletions

73
App.tsx
View File

@@ -8,6 +8,7 @@ import AgvControls from './components/AgvControls';
import BmsPanel from './components/BmsPanel';
import AcsControls from './components/AcsControls';
import AgvStatusPanel from './components/AgvStatusPanel';
import AgvAutoRunControls from './components/AgvAutoRunControls';
import SystemLogPanel from './components/SystemLogPanel';
import { SerialPortHandler } from './services/serialService';
@@ -184,12 +185,12 @@ const App: React.FC = () => {
}, [addLog]);
const handleAgvData = useCallback((data: Uint8Array) => {
for(let i=0; i<data.length; i++) {
for (let i = 0; i < data.length; i++) {
agvBufferRef.current.push(data[i]);
}
const buf = agvBufferRef.current;
while(true) {
while (true) {
const stxIdx = buf.indexOf(0x02);
if (stxIdx === -1) {
agvBufferRef.current = [];
@@ -232,7 +233,7 @@ const App: React.FC = () => {
setAgvState(s => {
const newBranch = (bunki === 'L' ? 'LEFT' : bunki === 'R' ? 'RIGHT' : bunki === 'S' ? 'STRAIGHT' : s.runConfig.branch);
const newSpeed = (['L','M','H'].includes(speed) ? speed : s.runConfig.speedLevel) as any;
const newSpeed = (['L', 'M', 'H'].includes(speed) ? speed : s.runConfig.speedLevel) as any;
// Treat '0' as placeholder for sensor if we want to preserve CBR logic,
// BUT allow manual toggle via UI to override later.
@@ -298,7 +299,7 @@ const App: React.FC = () => {
setAgvState(s => {
// Logic: Update if specific char provided, else keep existing
const newBranch = (bunki === 'L' ? 'LEFT' : bunki === 'R' ? 'RIGHT' : bunki === 'S' ? 'STRAIGHT' : s.runConfig.branch);
const newSpeed = (['L','M','H'].includes(speed) ? speed : s.runConfig.speedLevel) as any;
const newSpeed = (['L', 'M', 'H'].includes(speed) ? speed : s.runConfig.speedLevel) as any;
const newLidar = sensor !== '0';
@@ -603,7 +604,7 @@ const App: React.FC = () => {
const resp = new Uint8Array(34);
resp[0] = 0xDD; resp[1] = 0x03; resp[2] = 0x00; resp[3] = 0x1b; //arrary 3value 0->1b
const totalV = Math.floor(s.cellVoltages.reduce((a,b)=>a+b, 0) * 100);
const totalV = Math.floor(s.cellVoltages.reduce((a, b) => a + b, 0) * 100);
resp[4] = (totalV >> 8) & 0xFF; resp[5] = totalV & 0xFF;
const remainAh = Math.floor(s.maxCapacity * (s.batteryLevel / 100));
@@ -675,7 +676,7 @@ const App: React.FC = () => {
acsSerialRef.current.send(new Uint8Array(buffer));
const hex = buffer.map(b => b.toString(16).padStart(2,'0').toUpperCase()).join(' ');
const hex = buffer.map(b => b.toString(16).padStart(2, '0').toUpperCase()).join(' ');
addLog('ACS', 'TX', `CMD:${cmd.toString(16).toUpperCase()} [${hex}]`);
}, [addLog]);
@@ -735,7 +736,7 @@ const App: React.FC = () => {
// Default Little Endian. So index len-3 is Low, len-2 is High.
if (receivedCrc !== 0xFFFF && calcCrc !== receivedCrc) {
const hex = packetData.map(b => b.toString(16).padStart(2,'0').toUpperCase()).join(' ');
const hex = packetData.map(b => b.toString(16).padStart(2, '0').toUpperCase()).join(' ');
addLog('ACS', 'ERROR', `CRC Fail: ${hex}`);
acsParseErrorsRef.current++;
if (acsParseErrorsRef.current > 3) buf.length = 0; // Reset on too many errors
@@ -750,7 +751,7 @@ const App: React.FC = () => {
const cmd = packetData[3];
const payload = packetData.slice(4, totalPacketSize - 3);
const hexStr = packetData.map(b => b.toString(16).padStart(2,'0').toUpperCase()).join(' ');
const hexStr = packetData.map(b => b.toString(16).padStart(2, '0').toUpperCase()).join(' ');
addLog('ACS', 'RX', `[ID:${id} CMD:${cmd}] ${hexStr}`);
// Consume buffer
@@ -890,7 +891,7 @@ const App: React.FC = () => {
const [xStr, yStr] = l.Position.split(',').map(s => s.trim());
newNodes.push({
id: l.Id,
x: parseFloat(xStr)||0, y: parseFloat(yStr)||0,
x: parseFloat(xStr) || 0, y: parseFloat(yStr) || 0,
type: NodeType.Label,
name: "",
rfidId: "",
@@ -909,7 +910,7 @@ const App: React.FC = () => {
const [xStr, yStr] = img.Position.split(',').map(s => s.trim());
newNodes.push({
id: img.Id,
x: parseFloat(xStr)||0, y: parseFloat(yStr)||0,
x: parseFloat(xStr) || 0, y: parseFloat(yStr) || 0,
type: NodeType.Image,
name: img.Name,
rfidId: "",
@@ -1047,7 +1048,7 @@ const App: React.FC = () => {
NodeTextFontSize: n.fontSize || 7.0,
AliasName: "",
SpeedLimit: 0,
CanDocking: [1,2,3,4,5].includes(n.type),
CanDocking: [1, 2, 3, 4, 5].includes(n.type),
DockDirection: n.type === NodeType.Charging || n.type === NodeType.ChargerStation ? 1 : 0,
CanTurnLeft: true,
CanTurnRight: true,
@@ -1126,7 +1127,7 @@ const App: React.FC = () => {
};
// --- Real-time updates to Serial (AGV Events) ---
const prevSensors = useRef({ rfid: null as string|null, mark: false, line: false });
const prevSensors = useRef({ rfid: null as string | null, mark: false, line: false });
useEffect(() => {
if (!agvConnected || !agvSerialRef.current) return;
@@ -1164,8 +1165,38 @@ const App: React.FC = () => {
<div className="w-80 border-r border-gray-800 bg-gray-900 flex flex-col shrink-0">
<div className="flex-1 overflow-hidden">
<AgvStatusPanel agvState={agvState} />
</div>
{/* Auto Run Controls (Left Sidebar) */}
<div className="w-80 border-r border-gray-800 bg-gray-900 flex flex-col shrink-0">
<div className="flex-1 overflow-hidden">
<AgvAutoRunControls
agvState={agvState}
updateRunConfig={(key, value) => {
if (agvState.error) return;
setAgvState(s => ({ ...s, runConfig: { ...s.runConfig, [key]: value } }));
}}
toggleRun={() => {
if (agvState.error) return;
if (agvState.motionState === AgvMotionState.RUNNING || agvState.motionState === AgvMotionState.MARK_STOPPING) {
setAgvState(s => ({ ...s, motionState: AgvMotionState.IDLE }));
} else {
const isFwd = agvState.runConfig.direction === 'FWD';
const hasLine = isFwd ? agvState.sensorLineFront : agvState.sensorLineRear;
if (!hasLine) {
setAgvState(s => ({ ...s, error: 'LINE_OUT' }));
return;
}
setAgvState(s => ({ ...s, motionState: AgvMotionState.RUNNING }));
}
}}
isRunning={agvState.motionState === AgvMotionState.RUNNING || agvState.motionState === AgvMotionState.MARK_STOPPING}
isError={agvState.error !== null}
setLidar={(isOn) => setAgvState(s => ({ ...s, lidarEnabled: isOn, sensorStatus: isOn ? '1' : '0' }))}
/>
</div>
</div>
{/* Middle 2: ACS Panel (Fixed) */}
<div className="flex-shrink-0">
@@ -1242,20 +1273,20 @@ const App: React.FC = () => {
<div className="flex-1 overflow-y-auto">
<AgvControls
agvState={agvState}
setMotion={(m) => setAgvState(s => ({...s, motionState: m}))}
setLift={(h) => setAgvState(s => ({...s, liftHeight: h}))}
setRunConfig={(c) => setAgvState(s => ({...s, runConfig: c}))}
setError={(e) => setAgvState(s => ({...s, error: e}))}
setMotion={(m) => setAgvState(s => ({ ...s, motionState: m }))}
setLift={(h) => setAgvState(s => ({ ...s, liftHeight: h }))}
setRunConfig={(c) => setAgvState(s => ({ ...s, runConfig: c }))}
setError={(e) => setAgvState(s => ({ ...s, error: e }))}
onTurn180={handleTurn180}
setMagnet={(isOn) => setAgvState(s => ({...s, magnetOn: isOn}))}
setLiftStatus={(status) => setAgvState(s => ({...s, liftStatus: status}))}
setLidar={(isOn) => setAgvState(s => ({...s, lidarEnabled: isOn, sensorStatus: isOn ? '1' : '0'}))}
setMagnet={(isOn) => setAgvState(s => ({ ...s, magnetOn: isOn }))}
setLiftStatus={(status) => setAgvState(s => ({ ...s, liftStatus: status }))}
setLidar={(isOn) => setAgvState(s => ({ ...s, lidarEnabled: isOn, sensorStatus: isOn ? '1' : '0' }))}
/>
</div>
{/* Middle 1: BMS Panel (Fixed) */}
<div className="flex-shrink-0">
<BmsPanel state={agvState} setBatteryLevel={(l) => setAgvState(s => ({...s, batteryLevel: l}))} />
<BmsPanel state={agvState} setBatteryLevel={(l) => setAgvState(s => ({ ...s, batteryLevel: l }))} />
</div>
{/* Bottom: System Logs (Fixed Height) */}
@@ -1263,7 +1294,7 @@ const App: React.FC = () => {
<SystemLogPanel logs={logs.filter(l => l.source === 'SYSTEM')} onClear={() => clearLogs('SYSTEM')} />
</div>
</div>
</div>
</div >
);
};

1
commit_msg_2.txt Normal file
View File

@@ -0,0 +1 @@
Update: Relocate AutoRun controls and cleanup

View File

@@ -3,7 +3,7 @@ import React from 'react';
import { StopCircle, Play, Square, AlertTriangle, ChevronsUp, ChevronsDown, Magnet, Radar, ArrowLeft, ArrowRight } from 'lucide-react';
import { AgvState, AgvMotionState, AgvRunConfig } from '../types';
import AgvManualControls from './AgvManualControls';
import AgvAutoRunControls from './AgvAutoRunControls';
interface AgvControlsProps {
agvState: AgvState;
@@ -29,22 +29,7 @@ const AgvControls: React.FC<AgvControlsProps> = ({ agvState, setMotion, setLift,
});
};
const toggleRun = () => {
if (isError) return;
if (isRunning) {
setMotion(AgvMotionState.IDLE);
} else {
const isFwd = agvState.runConfig.direction === 'FWD';
const hasLine = isFwd ? agvState.sensorLineFront : agvState.sensorLineRear;
if (!hasLine) {
setError('LINE_OUT');
return;
}
setMotion(AgvMotionState.RUNNING);
}
};
const handleMarkStop = () => {
if (agvState.motionState === AgvMotionState.RUNNING) {
@@ -152,14 +137,7 @@ const AgvControls: React.FC<AgvControlsProps> = ({ agvState, setMotion, setLift,
{/* Auto Run Controls */}
{/* Auto Run Controls (분리된 콤포넌트) */}
<AgvAutoRunControls
agvState={agvState}
updateRunConfig={updateRunConfig}
toggleRun={toggleRun}
isRunning={isRunning}
isError={isError}
setLidar={setLidar}
/>
</div>
);
};