feat: Migrate IO and Recipe systems to use real data sources
- Enhanced GetIOList() to read from DIO library (inputs, outputs, interlocks) - Added interlock display to IOMonitorPage with 3-tab layout - Migrated GetRecipeList() to use PUB.mdm.dataSet.OPModel table - Migrated GetRecipe() to read from OPModel DataRow - Migrated SaveRecipe() to save to OPModel table with type conversion - Updated frontend to handle new structured IO format - All methods now use real hardware/database instead of mock data 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -7,10 +7,19 @@ interface IOMonitorPageProps {
|
||||
onToggle: (id: number, type: 'input' | 'output') => void;
|
||||
}
|
||||
|
||||
interface InterlockData {
|
||||
axisIndex: number;
|
||||
axisName: string;
|
||||
nonAxis: boolean;
|
||||
locks: { id: number; name: string; state: boolean }[];
|
||||
hexValue: string;
|
||||
}
|
||||
|
||||
export const IOMonitorPage: React.FC<IOMonitorPageProps> = ({ onToggle }) => {
|
||||
const [ioPoints, setIoPoints] = useState<IOPoint[]>([]);
|
||||
const [interlocks, setInterlocks] = useState<InterlockData[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [activeIOTab, setActiveIOTab] = useState<'in' | 'out'>('in');
|
||||
const [activeIOTab, setActiveIOTab] = useState<'in' | 'out' | 'interlock'>('in');
|
||||
|
||||
// Fetch initial IO list when page mounts
|
||||
useEffect(() => {
|
||||
@@ -18,8 +27,21 @@ export const IOMonitorPage: React.FC<IOMonitorPageProps> = ({ onToggle }) => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const ioStr = await comms.getIOList();
|
||||
const ioData: IOPoint[] = JSON.parse(ioStr);
|
||||
setIoPoints(ioData);
|
||||
const ioData = JSON.parse(ioStr);
|
||||
|
||||
// Handle new structured format: { inputs: [...], outputs: [...], interlocks: [...] }
|
||||
if (ioData.inputs && ioData.outputs) {
|
||||
// New format
|
||||
const flatIoList: IOPoint[] = [
|
||||
...ioData.outputs.map((io: any) => ({ id: io.id, name: io.name, type: 'output' as const, state: io.state })),
|
||||
...ioData.inputs.map((io: any) => ({ id: io.id, name: io.name, type: 'input' as const, state: io.state }))
|
||||
];
|
||||
setIoPoints(flatIoList);
|
||||
setInterlocks(ioData.interlocks || []);
|
||||
} else if (Array.isArray(ioData)) {
|
||||
// Old format - already flat array
|
||||
setIoPoints(ioData);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to fetch IO list:', e);
|
||||
}
|
||||
@@ -46,7 +68,9 @@ export const IOMonitorPage: React.FC<IOMonitorPageProps> = ({ onToggle }) => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
const points = ioPoints.filter(p => p.type === (activeIOTab === 'in' ? 'input' : 'output'));
|
||||
const points = activeIOTab === 'interlock'
|
||||
? []
|
||||
: ioPoints.filter(p => p.type === (activeIOTab === 'in' ? 'input' : 'output'));
|
||||
|
||||
return (
|
||||
<main className="relative w-full h-full px-6 pt-6 pb-20">
|
||||
@@ -60,7 +84,7 @@ export const IOMonitorPage: React.FC<IOMonitorPageProps> = ({ onToggle }) => {
|
||||
</h2>
|
||||
<div className="h-6 w-px bg-white/20"></div>
|
||||
<div className="text-sm font-mono text-neon-blue">
|
||||
TOTAL POINTS: {ioPoints.length}
|
||||
{activeIOTab === 'interlock' ? `TOTAL AXES: ${interlocks.length}` : `TOTAL POINTS: ${ioPoints.length}`}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -77,6 +101,12 @@ export const IOMonitorPage: React.FC<IOMonitorPageProps> = ({ onToggle }) => {
|
||||
>
|
||||
OUTPUTS ({ioPoints.filter(p => p.type === 'output').length})
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveIOTab('interlock')}
|
||||
className={`px-6 py-2 rounded-full font-tech font-bold text-sm transition-all ${activeIOTab === 'interlock' ? 'bg-neon-blue/20 text-neon-blue border border-neon-blue shadow-[0_0_15px_rgba(0,255,255,0.3)]' : 'text-slate-400 hover:text-white hover:bg-white/5'}`}
|
||||
>
|
||||
INTERLOCKS ({interlocks.length})
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -86,6 +116,35 @@ export const IOMonitorPage: React.FC<IOMonitorPageProps> = ({ onToggle }) => {
|
||||
<RotateCw className="w-16 h-16 text-neon-blue animate-spin" />
|
||||
<div className="text-xl font-tech text-neon-blue tracking-widest">LOADING IO POINTS...</div>
|
||||
</div>
|
||||
) : activeIOTab === 'interlock' ? (
|
||||
<div className="p-4 space-y-4">
|
||||
{interlocks.map(axis => (
|
||||
<div key={axis.axisIndex} className="bg-slate-900/60 border border-slate-700 rounded-lg p-4">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xl font-tech font-bold text-neon-blue">{axis.axisName}</span>
|
||||
<span className="text-sm font-mono text-slate-400">({axis.hexValue})</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
{axis.locks.map(lock => (
|
||||
<div
|
||||
key={lock.id}
|
||||
className={`
|
||||
flex items-center gap-2 px-3 py-2 transition-all border rounded
|
||||
${lock.state
|
||||
? 'bg-red-500/20 border-red-500 text-red-400 shadow-[0_0_10px_rgba(239,68,68,0.3)]'
|
||||
: 'bg-slate-800/40 border-slate-700 text-slate-500'}
|
||||
`}
|
||||
>
|
||||
<div className={`w-2 h-2 rounded-full shrink-0 ${lock.state ? 'bg-red-500 shadow-[0_0_6px_#ef4444]' : 'bg-slate-700'}`}></div>
|
||||
<span className="text-xs font-mono truncate">{lock.name}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-2 gap-x-8 gap-y-2 p-4">
|
||||
{points.map(p => (
|
||||
|
||||
Reference in New Issue
Block a user