Initial commit: Refactor AgvAutoRunControls
This commit is contained in:
142
components/AcsControls.tsx
Normal file
142
components/AcsControls.tsx
Normal file
@@ -0,0 +1,142 @@
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import { Navigation, Package, Zap, ChevronRight, Hash, Type } from 'lucide-react';
|
||||
import { SimulationMap } from '../types';
|
||||
|
||||
interface AcsControlsProps {
|
||||
onSend: (cmd: number, data?: number[]) => void;
|
||||
isConnected: boolean;
|
||||
mapData: SimulationMap;
|
||||
}
|
||||
|
||||
const AcsControls: React.FC<AcsControlsProps> = ({ onSend, isConnected, mapData }) => {
|
||||
const [tagId, setTagId] = useState('0001');
|
||||
const [alias, setAlias] = useState('charger1');
|
||||
|
||||
const PRESET_ALIASES = [
|
||||
'charger1', 'charger2',
|
||||
'loader', 'unloader', 'cleaner',
|
||||
'buffer1', 'buffer2', 'buffer3', 'buffer4', 'buffer5', 'buffer6'
|
||||
];
|
||||
|
||||
const availableTags = useMemo(() => {
|
||||
// Extract unique, non-empty RFID tags from map nodes
|
||||
const tags = mapData.nodes
|
||||
.filter(n => n.rfidId && n.rfidId.trim() !== '')
|
||||
.map(n => n.rfidId);
|
||||
return Array.from(new Set(tags)).sort();
|
||||
}, [mapData]);
|
||||
|
||||
const handleSend = (cmd: number, dataStr?: string, dataByte?: number) => {
|
||||
if (!isConnected) return;
|
||||
|
||||
let payload: number[] = [];
|
||||
|
||||
if (dataStr !== undefined) {
|
||||
// Convert string to ASCII bytes
|
||||
for (let i = 0; i < dataStr.length; i++) {
|
||||
payload.push(dataStr.charCodeAt(i));
|
||||
}
|
||||
} else if (dataByte !== undefined) {
|
||||
payload.push(dataByte);
|
||||
}
|
||||
|
||||
onSend(cmd, payload);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`bg-gray-800 p-4 border-t border-gray-700 select-none ${!isConnected ? 'opacity-50 pointer-events-none' : ''}`}>
|
||||
<h3 className="text-sm font-semibold mb-3 text-green-400 flex items-center justify-between">
|
||||
<span className="flex items-center gap-2"><Navigation size={16} /> ACS Control</span>
|
||||
<span className="text-[10px] bg-gray-900 px-1.5 py-0.5 rounded text-gray-500 border border-gray-700">EXT CMD</span>
|
||||
</h3>
|
||||
|
||||
{/* Navigation Commands */}
|
||||
<div className="space-y-2 mb-4">
|
||||
{/* Goto Tag */}
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="relative flex-1">
|
||||
<Hash size={12} className="absolute left-2 top-1/2 -translate-y-1/2 text-gray-500" />
|
||||
<input
|
||||
list="tag-options"
|
||||
type="text"
|
||||
value={tagId}
|
||||
onChange={(e) => setTagId(e.target.value)}
|
||||
className="w-full bg-gray-900 border border-gray-600 rounded py-1 pl-7 pr-2 text-xs text-white font-mono focus:border-green-500 outline-none"
|
||||
placeholder="Tag ID"
|
||||
/>
|
||||
<datalist id="tag-options">
|
||||
{availableTags.map(tag => (
|
||||
<option key={tag} value={tag} />
|
||||
))}
|
||||
</datalist>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => handleSend(0x81, tagId)}
|
||||
className="bg-gray-700 hover:bg-green-700 text-white text-xs px-3 py-1.5 rounded flex items-center gap-1 transition-colors border border-gray-600"
|
||||
>
|
||||
GO <ChevronRight size={10} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Goto Alias */}
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="relative flex-1">
|
||||
<Type size={12} className="absolute left-2 top-1/2 -translate-y-1/2 text-gray-500" />
|
||||
<input
|
||||
list="alias-options"
|
||||
type="text"
|
||||
value={alias}
|
||||
onChange={(e) => setAlias(e.target.value)}
|
||||
className="w-full bg-gray-900 border border-gray-600 rounded py-1 pl-7 pr-2 text-xs text-white font-mono focus:border-green-500 outline-none"
|
||||
placeholder="Alias Name"
|
||||
/>
|
||||
<datalist id="alias-options">
|
||||
{PRESET_ALIASES.map(item => (
|
||||
<option key={item} value={item} />
|
||||
))}
|
||||
</datalist>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => handleSend(0x82, alias)}
|
||||
className="bg-gray-700 hover:bg-green-700 text-white text-xs px-3 py-1.5 rounded flex items-center gap-1 transition-colors border border-gray-600"
|
||||
>
|
||||
GO <ChevronRight size={10} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="h-px bg-gray-700 my-3" />
|
||||
|
||||
{/* Action Buttons Grid */}
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<button
|
||||
onClick={() => handleSend(0x83, undefined, 1)}
|
||||
className="flex items-center justify-center gap-2 bg-blue-900/40 hover:bg-blue-800 border border-blue-800 text-blue-200 py-2 rounded text-xs transition-colors"
|
||||
>
|
||||
<Package size={14} /> Pick ON
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleSend(0x83, undefined, 0)}
|
||||
className="flex items-center justify-center gap-2 bg-gray-700 hover:bg-gray-600 border border-gray-600 text-gray-300 py-2 rounded text-xs transition-colors"
|
||||
>
|
||||
<Package size={14} /> Pick OFF
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => handleSend(0x84, undefined, 1)}
|
||||
className="flex items-center justify-center gap-2 bg-yellow-900/40 hover:bg-yellow-800 border border-yellow-800 text-yellow-200 py-2 rounded text-xs transition-colors"
|
||||
>
|
||||
<Zap size={14} /> Charge ON
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleSend(0x84, undefined, 0)}
|
||||
className="flex items-center justify-center gap-2 bg-gray-700 hover:bg-gray-600 border border-gray-600 text-gray-300 py-2 rounded text-xs transition-colors"
|
||||
>
|
||||
<Zap size={14} /> Charge OFF
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AcsControls;
|
||||
Reference in New Issue
Block a user