From 6da65eba7bd2fd748a609273d8eba9f38005f81e Mon Sep 17 00:00:00 2001
From: Jerald-Golden-B <jerald@hexrfactory.com>
Date: Tue, 20 May 2025 13:01:43 +0530
Subject: [PATCH] feat: implement conveyor and arm bot event managers for
 monitoring state changes

---
 .../eventManager/useConveyorEventManager.ts   | 65 +++++++++++++++++++
 .../conveyorInstance/conveyorInstance.tsx     | 48 ++++++--------
 .../eventManager/useArmBotEventManager.ts     | 65 +++++++++++++++++++
 .../armInstance/roboticArmInstance.tsx        | 30 +++------
 .../triggerHandler/useTriggerHandler.ts       | 55 ++++++++++++++--
 5 files changed, 212 insertions(+), 51 deletions(-)
 create mode 100644 app/src/modules/simulation/conveyor/eventManager/useConveyorEventManager.ts
 create mode 100644 app/src/modules/simulation/roboticArm/eventManager/useArmBotEventManager.ts

diff --git a/app/src/modules/simulation/conveyor/eventManager/useConveyorEventManager.ts b/app/src/modules/simulation/conveyor/eventManager/useConveyorEventManager.ts
new file mode 100644
index 0000000..bdc014c
--- /dev/null
+++ b/app/src/modules/simulation/conveyor/eventManager/useConveyorEventManager.ts
@@ -0,0 +1,65 @@
+import { useEffect, useRef } from 'react';
+import { useFrame } from '@react-three/fiber';
+import { useConveyorStore } from '../../../../store/simulation/useConveyorStore';
+
+type ConveyorCallback = {
+    conveyorId: string;
+    callback: () => void;
+};
+
+export function useConveyorEventManager() {
+    const { getConveyorById } = useConveyorStore();
+    const callbacksRef = useRef<ConveyorCallback[]>([]);
+    const isMonitoringRef = useRef(false);
+
+    // Add a new conveyor to monitor
+    const addConveyorToMonitor = (conveyorId: string, callback: () => void) => {
+        // Avoid duplicates
+        if (!callbacksRef.current.some((entry) => entry.conveyorId === conveyorId)) {
+            callbacksRef.current.push({ conveyorId, callback });
+        }
+
+        // Start monitoring if not already running
+        if (!isMonitoringRef.current) {
+            isMonitoringRef.current = true;
+        }
+    };
+
+    // Remove a conveyor from monitoring
+    const removeConveyorFromMonitor = (conveyorId: string) => {
+        callbacksRef.current = callbacksRef.current.filter(
+            (entry) => entry.conveyorId !== conveyorId
+        );
+
+        // Stop monitoring if no more conveyors to track
+        if (callbacksRef.current.length === 0) {
+            isMonitoringRef.current = false;
+        }
+    };
+
+    // Check conveyor states every frame
+    useFrame(() => {
+        if (!isMonitoringRef.current || callbacksRef.current.length === 0) return;
+
+        callbacksRef.current.forEach(({ conveyorId, callback }) => {
+            const conveyor = getConveyorById(conveyorId);
+            if (conveyor?.isPaused === false) {
+                callback();
+                removeConveyorFromMonitor(conveyorId); // Remove after triggering
+            }
+        });
+    });
+
+    // Cleanup on unmount
+    useEffect(() => {
+        return () => {
+            callbacksRef.current = [];
+            isMonitoringRef.current = false;
+        };
+    }, []);
+
+    return {
+        addConveyorToMonitor,
+        removeConveyorFromMonitor,
+    };
+}
\ No newline at end of file
diff --git a/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx b/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx
index fa1b33b..8cd6db9 100644
--- a/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx
+++ b/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx
@@ -2,43 +2,37 @@ import React, { useEffect } from 'react'
 import { useMaterialStore } from '../../../../../store/simulation/useMaterialStore';
 import { useConveyorStore } from '../../../../../store/simulation/useConveyorStore';
 import { useResetButtonStore } from '../../../../../store/usePlayButtonStore';
-import { findConveyorInSequences } from '../../../simulator/functions/findConveyorInSequences';
-import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore';
-import { useProductStore } from '../../../../../store/simulation/useProductStore';
 
 function ConveyorInstance({ conveyor }: { conveyor: ConveyorStatus }) {
-    const { getProductById } = useProductStore();
-    const { selectedProduct } = useSelectedProduct();
     const { materials, getMaterialsByCurrentModelUuid } = useMaterialStore();
     const { isReset } = useResetButtonStore();
     const { setConveyorPaused } = useConveyorStore();
 
     useEffect(() => {
-        const product = getProductById(selectedProduct.productId);
-        if (!product) return;
-
-        const sequenceInfo = findConveyorInSequences(product, conveyor.modelUuid);
-        if (!sequenceInfo) return;
-
-        const { currentSubSequence } = sequenceInfo;
         const conveyorMaterials = getMaterialsByCurrentModelUuid(conveyor.modelUuid);
+        if (conveyorMaterials && conveyorMaterials?.length > 0) {
 
-        if (conveyorMaterials && conveyorMaterials.length > 0) {
-            const shouldPauseSubsequence = currentSubSequence.some(subConveyor => {
-                if (subConveyor.type !== 'transfer') return false;
-                const subMaterials = getMaterialsByCurrentModelUuid(subConveyor.modelUuid);
-                return subMaterials?.some(m => m.isPaused) ?? false;
-            });
+            const hasPausedMaterials = conveyorMaterials.some(material => material.isPaused);
 
-            currentSubSequence.forEach(subConveyor => {
-                if (subConveyor.type === 'transfer') {
-                    setConveyorPaused(subConveyor.modelUuid, shouldPauseSubsequence);
-                }
-            });
+            if (hasPausedMaterials) {
+                setConveyorPaused(conveyor.modelUuid, true);
+            } else {
+                setConveyorPaused(conveyor.modelUuid, false);
+            }
+        }else{
+            setConveyorPaused(conveyor.modelUuid, false);
         }
-    }, [materials, conveyor.modelUuid, getMaterialsByCurrentModelUuid, setConveyorPaused, isReset, selectedProduct.productId, getProductById]);
 
-    return null;
-}
+    }, [materials, conveyor.modelUuid, getMaterialsByCurrentModelUuid, setConveyorPaused, isReset]);
 
-export default React.memo(ConveyorInstance);
\ No newline at end of file
+    useEffect(() => {
+        // console.log('conveyor: ', conveyor);
+    }, [conveyor])
+
+    return (
+        <>
+        </>
+    )
+};
+
+export default ConveyorInstance
\ No newline at end of file
diff --git a/app/src/modules/simulation/roboticArm/eventManager/useArmBotEventManager.ts b/app/src/modules/simulation/roboticArm/eventManager/useArmBotEventManager.ts
new file mode 100644
index 0000000..97593b4
--- /dev/null
+++ b/app/src/modules/simulation/roboticArm/eventManager/useArmBotEventManager.ts
@@ -0,0 +1,65 @@
+import { useEffect, useRef } from 'react';
+import { useFrame } from '@react-three/fiber';
+import { useArmBotStore } from '../../../../store/simulation/useArmBotStore';
+
+type ArmBotCallback = {
+    armBotId: string;
+    callback: () => void;
+};
+
+export function useArmBotEventManager() {
+    const { getArmBotById } = useArmBotStore();
+    const callbacksRef = useRef<ArmBotCallback[]>([]);
+    const isMonitoringRef = useRef(false);
+
+    // Add a new armbot to monitor
+    const addArmBotToMonitor = (armBotId: string, callback: () => void) => {
+        // Avoid duplicates
+        if (!callbacksRef.current.some((entry) => entry.armBotId === armBotId)) {
+            callbacksRef.current.push({ armBotId, callback });
+        }
+
+        // Start monitoring if not already running
+        if (!isMonitoringRef.current) {
+            isMonitoringRef.current = true;
+        }
+    };
+
+    // Remove an armbot from monitoring (e.g., when unmounted)
+    const removeArmBotFromMonitor = (armBotId: string) => {
+        callbacksRef.current = callbacksRef.current.filter(
+            (entry) => entry.armBotId !== armBotId
+        );
+
+        // Stop monitoring if no more armbots to track
+        if (callbacksRef.current.length === 0) {
+            isMonitoringRef.current = false;
+        }
+    };
+
+    // Check armbot states every frame
+    useFrame(() => {
+        if (!isMonitoringRef.current || callbacksRef.current.length === 0) return;
+
+        callbacksRef.current.forEach(({ armBotId, callback }) => {
+            const armBot = getArmBotById(armBotId);
+            if (armBot?.isActive === false && armBot?.state === 'idle') {
+                callback();
+                removeArmBotFromMonitor(armBotId); // Remove after triggering
+            }
+        });
+    });
+
+    // Cleanup on unmount (optional, since useFrame auto-cleans)
+    useEffect(() => {
+        return () => {
+            callbacksRef.current = [];
+            isMonitoringRef.current = false;
+        };
+    }, []);
+
+    return {
+        addArmBotToMonitor,
+        removeArmBotFromMonitor,
+    };
+}
\ No newline at end of file
diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx
index 1c8c447..e07a4ab 100644
--- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx
+++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx
@@ -27,15 +27,14 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
     const pauseTimeRef = useRef<number | null>(null);
     const isPausedRef = useRef<boolean>(false);
     const isSpeedRef = useRef<any>(null);
-    const isIdleRef = useRef<boolean>(false);
     let startTime: number;
 
-    const { armBots, setArmBotActive, setArmBotState, removeCurrentAction, incrementActiveTime, incrementIdleTime } = useArmBotStore();
+    const { setArmBotActive, setArmBotState, removeCurrentAction, incrementActiveTime, incrementIdleTime } = useArmBotStore();
     const { decrementVehicleLoad, removeLastMaterial } = useVehicleStore();
     const { removeLastMaterial: removeLastStorageMaterial, updateCurrentLoad } = useStorageUnitStore();
     const { setIsVisible, setIsPaused, getMaterialById } = useMaterialStore();
     const { selectedProduct } = useSelectedProduct();
-    const { getActionByUuid, getEventByActionUuid, getEventByModelUuid, getProductById } = useProductStore();
+    const { getActionByUuid, getEventByActionUuid, getEventByModelUuid } = useProductStore();
     const { triggerPointActions } = useTriggerHandler();
     const { isPlaying } = usePlayButtonStore();
     const { isReset } = useResetButtonStore();
@@ -47,7 +46,6 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
 
     const animationFrameIdRef = useRef<number | null>(null);
     const previousTimeRef = useRef<number | null>(null);
-    const checkActiveRoboticArms = useCheckActiveRoboticArmsInSubsequence();
 
     const lastRemoved = useRef<{ type: string, materialId: string } | null>(null);
 
@@ -95,15 +93,6 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
                 if (!model) return;
                 if (model.type === 'transfer') {
                     setIsVisible(armBot.currentAction.materialId || '', true);
-
-                    const product = getProductById(selectedProduct.productId);
-                    if (product) {
-                        const result = checkActiveRoboticArms(product, armBot.modelUuid);
-                        // console.log('result: ', result);
-                        // if (result?.hasActiveRoboticArm) {
-                        //     lastRemoved.current = null;
-                        // }
-                    }
                 } else if (model.type === 'machine') {
                     //
                 } else if (model.type === 'vehicle') {
@@ -116,6 +105,14 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
                 triggerPointActions(action, armBot.currentAction.materialId)
                 removeCurrentAction(armBot.modelUuid)
             }
+
+            if (lastRemoved.current) {
+                if (lastRemoved.current.type === 'transfer') {
+                    setIsPaused(lastRemoved.current.materialId, true)
+                } else {
+                    setIsPaused(lastRemoved.current.materialId, false)
+                }
+            }
         }
     }
 
@@ -386,13 +383,6 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
             setCurrentPhase("rest");
             setPath([])
 
-            if (lastRemoved.current) {
-                if (lastRemoved.current.type === 'transfer') {
-                    setIsPaused(lastRemoved.current.materialId, true)
-                } else {
-                    setIsPaused(lastRemoved.current.materialId, false)
-                }
-            }
         }
     }
 
diff --git a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts
index 7de5d29..daf9b4f 100644
--- a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts
+++ b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts
@@ -7,12 +7,18 @@ import { useArmBotStore } from '../../../../store/simulation/useArmBotStore';
 import { useVehicleStore } from '../../../../store/simulation/useVehicleStore';
 import { useMachineStore } from '../../../../store/simulation/useMachineStore';
 import { useStorageUnitStore } from '../../../../store/simulation/useStorageUnitStore';
+import { useArmBotEventManager } from '../../roboticArm/eventManager/useArmBotEventManager';
+import { useConveyorStore } from '../../../../store/simulation/useConveyorStore';
+import { useConveyorEventManager } from '../../conveyor/eventManager/useConveyorEventManager';
 
 export function useTriggerHandler() {
     const { handleAction } = useActionHandler();
     const { selectedProduct } = useSelectedProduct();
     const { getEventByTriggerUuid, getEventByModelUuid, getActionByUuid, getModelUuidByActionUuid } = useProductStore();
     const { getArmBotById } = useArmBotStore();
+    const { getConveyorById } = useConveyorStore();
+    const { addArmBotToMonitor } = useArmBotEventManager();
+    const { addConveyorToMonitor } = useConveyorEventManager();
     const { getVehicleById } = useVehicleStore();
     const { getMachineById } = useMachineStore();
     const { getStorageUnitById } = useStorageUnitStore();
@@ -144,6 +150,10 @@ export function useTriggerHandler() {
                                     } else {
 
                                         // Event Manager Needed
+                                        setIsPaused(materialId, true);
+                                        addArmBotToMonitor(armBot.modelUuid,
+                                            () => handleAction(action, materialId)
+                                        );
 
                                     }
                                 }
@@ -320,12 +330,42 @@ export function useTriggerHandler() {
                             if (armBot.isActive === false && armBot.state === 'idle') {
 
                                 // Handle current action from arm bot
-                                handleAction(action, materialId);
+                                const model = getEventByModelUuid(selectedProduct.productId, action.triggers[0].triggeredAsset?.triggeredModel.modelUuid || '');
+                                if (model?.type === 'transfer') {
+                                    const conveyor = getConveyorById(action.triggers[0].triggeredAsset?.triggeredModel.modelUuid || '');
+                                    if (conveyor) {
+                                        addConveyorToMonitor(conveyor.modelUuid,
+                                            () => {
+                                                handleAction(action, materialId)
+                                            }
+                                        )
+                                        // handleAction(action, materialId)
+                                    }
+                                } else {
+                                    handleAction(action, materialId)
+                                }
 
                             } else {
 
                                 // Event Manager Needed
-
+                                addArmBotToMonitor(armBot.modelUuid,
+                                    () => {
+                                        const model = getEventByModelUuid(selectedProduct.productId, action.triggers[0].triggeredAsset?.triggeredModel.modelUuid || '');
+                                        if (model?.type === 'transfer') {
+                                            const conveyor = getConveyorById(action.triggers[0].triggeredAsset?.triggeredModel.modelUuid || '');
+                                            if (conveyor) {
+                                                addConveyorToMonitor(conveyor.modelUuid,
+                                                    () => {
+                                                        handleAction(action, materialId)
+                                                    }
+                                                )
+                                                // handleAction(action, materialId)
+                                            }
+                                        } else {
+                                            handleAction(action, materialId)
+                                        }
+                                    }
+                                );
                             }
                         }
                     }
@@ -342,8 +382,6 @@ export function useTriggerHandler() {
                     const material = getMaterialById(materialId);
                     if (material) {
 
-                        // setIsPaused(material.materialId, false);
-
                         setPreviousLocation(material.materialId, {
                             modelUuid: material.current.modelUuid,
                             pointUuid: material.current.pointUuid,
@@ -410,6 +448,15 @@ export function useTriggerHandler() {
                                     }
                                 }
 
+                            } else if (model?.type === 'transfer') {
+                                const conveyor = getConveyorById(action.triggers[0].triggeredAsset?.triggeredModel.modelUuid);
+                                if (conveyor) {
+                                    setNextLocation(material.materialId, {
+                                        modelUuid: action.triggers[0].triggeredAsset?.triggeredModel.modelUuid,
+                                        pointUuid: action.triggers[0].triggeredAsset?.triggeredPoint?.pointUuid,
+                                    })
+                                    handleAction(action, material.materialId);
+                                }
                             } else {
                                 setNextLocation(material.materialId, {
                                     modelUuid: action.triggers[0].triggeredAsset?.triggeredModel.modelUuid,