feat: Add real-time IO/interlock updates, HW status display, and history page
- Implement real-time IO value updates via IOValueChanged event - Add interlock toggle and real-time interlock change events - Fix ToggleLight to check return value of DIO.SetRoomLight - Add HW status display in Footer matching WinForms HWState - Implement GetHWStatus API and 250ms broadcast interval - Create HistoryPage React component for work history viewing - Add GetHistoryData API for database queries - Add date range selection, search, filter, and CSV export - Add History button in Header navigation - Add PickerMoveDialog component for manage operations - Fix DataSet column names (idx, PRNATTACH, PRNVALID, qtymax) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -51,7 +51,8 @@ export const IOMonitorPage: React.FC<IOMonitorPageProps> = ({ onToggle }) => {
|
||||
|
||||
// Subscribe to real-time IO updates
|
||||
const unsubscribe = comms.subscribe((msg: any) => {
|
||||
if (msg?.type === 'STATUS_UPDATE' && msg.ioState) {
|
||||
// STATUS_UPDATE - 주기적인 상태 업데이트 (변경된 IO만 포함)
|
||||
if (msg?.type === 'STATUS_UPDATE' && msg.ioState && msg.ioState.length > 0) {
|
||||
setIoPoints(prev => {
|
||||
const newIO = [...prev];
|
||||
msg.ioState.forEach((update: { id: number, type: string, state: boolean }) => {
|
||||
@@ -61,6 +62,39 @@ export const IOMonitorPage: React.FC<IOMonitorPageProps> = ({ onToggle }) => {
|
||||
return newIO;
|
||||
});
|
||||
}
|
||||
|
||||
// IO_CHANGED - 개별 IO 값 변경 이벤트 (실시간)
|
||||
if (msg?.type === 'IO_CHANGED' && msg.data) {
|
||||
const { id, ioType, state } = msg.data;
|
||||
setIoPoints(prev => {
|
||||
const newIO = [...prev];
|
||||
const idx = newIO.findIndex(p => p.id === id && p.type === ioType);
|
||||
if (idx >= 0) {
|
||||
newIO[idx] = { ...newIO[idx], state: state };
|
||||
}
|
||||
return newIO;
|
||||
});
|
||||
}
|
||||
|
||||
// INTERLOCK_CHANGED - 인터락 값 변경 이벤트 (실시간)
|
||||
if (msg?.type === 'INTERLOCK_CHANGED' && msg.data) {
|
||||
const { axisIndex, lockIndex, state, hexValue } = msg.data;
|
||||
setInterlocks(prev => {
|
||||
const newInterlocks = [...prev];
|
||||
const axisIdx = newInterlocks.findIndex(a => a.axisIndex === axisIndex);
|
||||
if (axisIdx >= 0) {
|
||||
const lockIdx = newInterlocks[axisIdx].locks.findIndex(l => l.id === lockIndex);
|
||||
if (lockIdx >= 0) {
|
||||
newInterlocks[axisIdx].locks[lockIdx] = {
|
||||
...newInterlocks[axisIdx].locks[lockIdx],
|
||||
state: state
|
||||
};
|
||||
newInterlocks[axisIdx].hexValue = hexValue;
|
||||
}
|
||||
}
|
||||
return newInterlocks;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
@@ -72,6 +106,19 @@ export const IOMonitorPage: React.FC<IOMonitorPageProps> = ({ onToggle }) => {
|
||||
? []
|
||||
: ioPoints.filter(p => p.type === (activeIOTab === 'in' ? 'input' : 'output'));
|
||||
|
||||
// 인터락 토글 핸들러 (DIOMonitor.cs의 gvILXF_ItemClick과 동일)
|
||||
const handleInterlockToggle = async (axisIndex: number, lockIndex: number) => {
|
||||
try {
|
||||
const result = await comms.toggleInterlock(axisIndex, lockIndex);
|
||||
if (!result.success) {
|
||||
console.error('[IOMonitor] Interlock toggle failed:', result.message);
|
||||
}
|
||||
// 성공 시 INTERLOCK_CHANGED 이벤트로 UI가 자동 업데이트됨
|
||||
} catch (error) {
|
||||
console.error('[IOMonitor] Interlock toggle error:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<main className="relative w-full h-full px-6 pt-6 pb-20">
|
||||
<div className="glass-holo p-6 h-full flex flex-col gap-4">
|
||||
@@ -130,11 +177,13 @@ export const IOMonitorPage: React.FC<IOMonitorPageProps> = ({ onToggle }) => {
|
||||
{axis.locks.map(lock => (
|
||||
<div
|
||||
key={lock.id}
|
||||
onClick={() => handleInterlockToggle(axis.axisIndex, lock.id)}
|
||||
className={`
|
||||
flex items-center gap-2 px-3 py-2 transition-all border rounded
|
||||
flex items-center gap-2 px-3 py-2 transition-all border rounded cursor-pointer
|
||||
hover:translate-x-0.5 hover:brightness-110
|
||||
${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'}
|
||||
: 'bg-slate-800/40 border-slate-700 text-slate-500 hover:border-slate-600'}
|
||||
`}
|
||||
>
|
||||
<div className={`w-2 h-2 rounded-full shrink-0 ${lock.state ? 'bg-red-500 shadow-[0_0_6px_#ef4444]' : 'bg-slate-700'}`}></div>
|
||||
|
||||
Reference in New Issue
Block a user