From 970e3a837b1005065b65dba8d8feb8478ce70a9d Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Fri, 22 Aug 2025 13:51:35 +0530 Subject: [PATCH] human and vehicle bug fix --- .../actionHandler/useRetrieveHandler.ts | 30 ++--- .../instances/animator/materialAnimator.tsx | 4 +- .../instances/animator/pillarJibAnimator.tsx | 1 - .../instance/actions/operatorInstance.tsx | 2 +- .../instance/actions/workerInstance.tsx | 11 +- .../instances/animator/roboticArmAnimator.tsx | 6 +- .../armInstance/roboticArmInstance.tsx | 125 ++++++------------ .../triggerHandler/useTriggerHandler.ts | 17 +-- .../instances/animator/vehicleAnimator.tsx | 16 ++- app/src/store/simulation/useProductStore.ts | 53 ++++++++ 10 files changed, 135 insertions(+), 130 deletions(-) diff --git a/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts b/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts index ae907b4..f82d6de 100644 --- a/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts +++ b/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts @@ -38,7 +38,7 @@ export function useRetrieveHandler() { echo.info(`${materialUuid}, ${status}`); } - const createNewMaterial = useCallback((materialId: string, materialType: string, action: StorageAction) => { + const createNewMaterial = useCallback((materialId: string, materialType: string, action: StorageAction, visible: boolean = false) => { const modelUuid = getModelUuidByActionUuid(selectedProduct.productUuid, action.actionUuid); const pointUuid = getPointUuidByActionUuid(selectedProduct.productUuid, action.actionUuid); if (!modelUuid || !pointUuid) return null; @@ -51,7 +51,7 @@ export function useRetrieveHandler() { isActive: false, isVisible: false, isPaused: false, - isRendered: true, + isRendered: false, startTime: currentTime, previous: { modelUuid: modelUuid, @@ -168,10 +168,10 @@ export function useRetrieveHandler() { if (retrieval.action.triggers[0]?.triggeredAsset.triggeredAction?.actionUuid) { const action = getActionByUuid(selectedProduct.productUuid, retrieval.action.triggers[0]?.triggeredAsset.triggeredAction.actionUuid); + const storageAction = getActionByUuid(selectedProduct.productUuid, actionUuid); if (action && action.triggers.length > 0 && action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid) { const model = getEventByModelUuid(selectedProduct.productUuid, action?.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || ''); - const triggeredAction = getActionByUuid(selectedProduct.productUuid, action?.triggers[0]?.triggeredAsset?.triggeredAction?.actionUuid || ''); - if (model && triggeredAction) { + if (model && storageAction) { if (model.type === 'vehicle') { const vehicle = getVehicleById(model.modelUuid); if (vehicle && !vehicle.isActive && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) { @@ -179,7 +179,7 @@ export function useRetrieveHandler() { const material = createNewMaterial( lastMaterial.materialId, lastMaterial.materialType, - triggeredAction as StorageAction + storageAction as StorageAction ); if (material) { @@ -192,11 +192,11 @@ export function useRetrieveHandler() { retrieveLogStatus(material.materialName, `is being picked by ${armBot?.modelName}`); } } - } else if (triggeredAction) { + } else if (storageAction) { const material = createNewMaterial( lastMaterial.materialId, lastMaterial.materialType, - triggeredAction as StorageAction + storageAction as StorageAction ); if (material) { @@ -327,10 +327,7 @@ export function useRetrieveHandler() { ? getEventByModelUuid(selectedProduct.productUuid, action.triggers[0].triggeredAsset.triggeredModel.modelUuid) : null; - const triggeredAction = getActionByUuid( - selectedProduct.productUuid, - action.triggers[0]?.triggeredAsset?.triggeredAction?.actionUuid || '' - ); + const storageAction = getActionByUuid(selectedProduct.productUuid, actionUuid); if (triggeredModel?.type === 'vehicle') { const model = getVehicleById(triggeredModel.modelUuid); @@ -343,7 +340,7 @@ export function useRetrieveHandler() { const material = createNewMaterial( lastMaterial.materialId, lastMaterial.materialType, - triggeredAction as StorageAction + storageAction as StorageAction ); if (material) { removeLastMaterial(storageUnit.modelUuid); @@ -369,7 +366,7 @@ export function useRetrieveHandler() { const material = createNewMaterial( lastMaterial.materialId, lastMaterial.materialType, - triggeredAction as StorageAction + storageAction as StorageAction ); if (material) { removeLastMaterial(storageUnit.modelUuid); @@ -395,7 +392,7 @@ export function useRetrieveHandler() { const material = createNewMaterial( lastMaterial.materialId, lastMaterial.materialType, - triggeredAction as StorageAction + storageAction as StorageAction ); if (material) { removeLastMaterial(storageUnit.modelUuid); @@ -427,7 +424,7 @@ export function useRetrieveHandler() { const material = createNewMaterial( lastMaterial.materialId, lastMaterial.materialType, - triggeredAction as StorageAction + storageAction as StorageAction ); if (material) { removeLastMaterial(storageUnit.modelUuid); @@ -448,7 +445,7 @@ export function useRetrieveHandler() { const material = createNewMaterial( lastMaterial.materialId, lastMaterial.materialType, - triggeredAction as StorageAction + storageAction as StorageAction ); if (material) { removeLastMaterial(storageUnit.modelUuid); @@ -483,6 +480,7 @@ export function useRetrieveHandler() { addCurrentActionToCrane(crane.modelUuid, action.actionUuid, material.materialType, material.materialId); addCurrentMaterialToCrane(crane.modelUuid, material.materialType, material.materialId); cranePickupLockRef.current.set(crane.modelUuid, true); + monitoredHumansRef.current.delete(human.modelUuid); }, action.triggers[0].triggeredAsset.triggeredAction?.actionUuid) } monitoredHumansRef.current.add(human.modelUuid); diff --git a/app/src/modules/simulation/crane/instances/animator/materialAnimator.tsx b/app/src/modules/simulation/crane/instances/animator/materialAnimator.tsx index 796a897..20788fa 100644 --- a/app/src/modules/simulation/crane/instances/animator/materialAnimator.tsx +++ b/app/src/modules/simulation/crane/instances/animator/materialAnimator.tsx @@ -13,12 +13,12 @@ export default function MaterialAnimator({ crane }: Readonly(false); useEffect(() => { - if (crane.isCarrying) { + if (crane.isCarrying && (crane.currentPhase === 'pickup-drop' || crane.currentPhase === 'dropping')) { setIsRendered(true); } else { setIsRendered(false); } - }, [crane.isCarrying]); + }, [crane.isCarrying, crane.currentPhase]); useFrame(() => { const craneModel = scene.getObjectByProperty('uuid', crane.modelUuid); diff --git a/app/src/modules/simulation/crane/instances/animator/pillarJibAnimator.tsx b/app/src/modules/simulation/crane/instances/animator/pillarJibAnimator.tsx index 85e1448..2af3223 100644 --- a/app/src/modules/simulation/crane/instances/animator/pillarJibAnimator.tsx +++ b/app/src/modules/simulation/crane/instances/animator/pillarJibAnimator.tsx @@ -4,7 +4,6 @@ import { useFrame, useThree } from '@react-three/fiber'; import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore'; import { useSceneContext } from '../../../../scene/sceneContext'; import { useProductContext } from '../../../products/productContext'; -import { dragAction } from '@use-gesture/react'; function PillarJibAnimator({ crane, diff --git a/app/src/modules/simulation/human/instances/instance/actions/operatorInstance.tsx b/app/src/modules/simulation/human/instances/instance/actions/operatorInstance.tsx index fce077f..9b79d75 100644 --- a/app/src/modules/simulation/human/instances/instance/actions/operatorInstance.tsx +++ b/app/src/modules/simulation/human/instances/instance/actions/operatorInstance.tsx @@ -130,7 +130,7 @@ function OperatorInstance({ human }: { human: HumanStatus }) { setHumanState(human.modelUuid, 'running'); setHumanActive(human.modelUuid, true); setCurrentAnimation(human.modelUuid, 'working_standing', true, false, false); - }, 1) + }, 10) } } else { reset() diff --git a/app/src/modules/simulation/human/instances/instance/actions/workerInstance.tsx b/app/src/modules/simulation/human/instances/instance/actions/workerInstance.tsx index 8c75af2..8b49f49 100644 --- a/app/src/modules/simulation/human/instances/instance/actions/workerInstance.tsx +++ b/app/src/modules/simulation/human/instances/instance/actions/workerInstance.tsx @@ -129,10 +129,13 @@ function WorkerInstance({ human }: { human: HumanStatus }) { humanStatus(human.modelUuid, 'Started from pickup point, heading to drop point'); } } else if (human.currentMaterials.length > 0 && human.currentLoad > 0 && humanAsset?.animationState?.current !== 'pickup') { - if (human.currentMaterials[0]?.materialId) { - setIsVisible(human.currentMaterials[0]?.materialId, false); - } - setCurrentAnimation(human.modelUuid, 'pickup', true, false, false); + setTimeout(()=>{ + if (human.currentMaterials[0]?.materialId) { + setIsVisible(human.currentMaterials[0]?.materialId, false); + } + humanStatus(human.modelUuid, 'Started to pickup in pickup point'); + setCurrentAnimation(human.modelUuid, 'pickup', true, false, false); + },1) } } else if (!human.isActive && human.state === 'idle' && human.currentPhase === 'dropping' && human.currentLoad === 0) { if (action.pickUpPoint && action.dropPoint) { diff --git a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx index 8eb1af9..cecf51c 100644 --- a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx +++ b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx @@ -34,7 +34,7 @@ function RoboticArmAnimator({ HandleCallback, restPosition, ikSolver, targetBone const { armBotStore, productStore, materialStore } = useSceneContext(); const { getArmBotById } = armBotStore(); const { getMaterialById, getMaterialPosition } = materialStore(); - const { getEventByModelUuid, getActionByUuid, getPointByUuid } = productStore(); + const { getEventByModelUuid, getActionByUuid, getPointByUuid, getTriggeringModels } = productStore(); const { selectedProductStore } = useProductContext(); const { selectedProduct } = selectedProductStore(); const { scene } = useThree(); @@ -176,10 +176,12 @@ function RoboticArmAnimator({ HandleCallback, restPosition, ikSolver, targetBone if (armbotStatus && currentMaterial && currentAction && (currentPhase === 'rest-to-start' || currentPhase === 'start-to-end' || currentPhase === 'end-to-rest')) { const materialData = getMaterialById(currentMaterial); if (materialData) { - const prevModel = getEventByModelUuid(selectedProduct.productUuid, materialData.current.modelUuid); + const triggeringModel = getTriggeringModels(selectedProduct.productUuid, currentAction.actionUuid); + const prevModel = triggeringModel[0] || null; const nextModel = getEventByModelUuid(selectedProduct.productUuid, currentAction?.triggers[0]?.triggeredAsset?.triggeredModel?.modelUuid || ''); const nextPoint = getPointByUuid(selectedProduct.productUuid, currentAction?.triggers[0]?.triggeredAsset?.triggeredModel?.modelUuid || '', currentAction?.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid || ''); + console.log('prevModel: ', prevModel); if (prevModel && prevModel.type === 'transfer') { const material = scene.getObjectByProperty("uuid", currentMaterial); const armbotModel = scene.getObjectByProperty("uuid", armBot.modelUuid); diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx index e1595b6..ee196f5 100644 --- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx @@ -42,11 +42,6 @@ function RoboticArmInstance({ armBot }: { readonly armBot: ArmBotStatus }) { const lastRemoved = useRef<{ type: string, materialId: string, modelId: string } | null>(null); - function firstFrame() { - startTime = performance.now(); - step(); - } - const action = getActionByUuid(selectedProduct.productUuid, armBot.currentAction?.actionUuid || ''); const handlePickUpTrigger = () => { @@ -109,68 +104,6 @@ function RoboticArmInstance({ armBot }: { readonly armBot: ArmBotStatus }) { } } - function step() { - if (isPausedRef.current) { - if (!pauseTimeRef.current) { - pauseTimeRef.current = performance.now(); - } - requestAnimationFrame(() => step()); - return; - } - if (pauseTimeRef.current) { - const pauseDuration = performance.now() - pauseTimeRef.current; - startTime += pauseDuration; - pauseTimeRef.current = null; - } - const elapsedTime = performance.now() - startTime; - if (elapsedTime < 1000) { - // Wait until 1500ms has passed - requestAnimationFrame(step); - return; - } - if (currentPhase === "picking") { - setArmBotActive(armBot.modelUuid, true); - setArmBotState(armBot.modelUuid, "running"); - setCurrentPhase("start-to-end"); - startTime = 0 - if (!action) return; - const startPoint = (action as RoboticArmAction).process.startPoint; - const endPoint = (action as RoboticArmAction).process.endPoint; - if (startPoint && endPoint) { - let curve = createCurveBetweenTwoPoints( - new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2]), - new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2])); - if (curve) { - logStatus(armBot.modelUuid, "picking the object"); - setPath(curve.points.map(point => [point.x, point.y, point.z])) - - handlePickUpTrigger(); - - } - } - logStatus(armBot.modelUuid, "Moving armBot from start point to end position.") - } else if (currentPhase === "dropping") { - setArmBotActive(armBot.modelUuid, true); - setArmBotState(armBot.modelUuid, "running"); - setCurrentPhase("end-to-rest"); - startTime = 0; - if (!action) return; - const endPoint = (action as RoboticArmAction).process.endPoint; - if (endPoint) { - - let curve = createCurveBetweenTwoPoints(new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2]), restPosition); - if (curve) { - logStatus(armBot.modelUuid, "dropping the object"); - setPath(curve.points.map(point => [point.x, point.y, point.z])); - - handleDropTrigger(); - - } - } - logStatus(armBot.modelUuid, "Moving armBot from end point to rest position.") - } - } - useEffect(() => { isPausedRef.current = isPaused; }, [isPaused]); @@ -268,7 +201,6 @@ function RoboticArmInstance({ armBot }: { readonly armBot: ArmBotStatus }) { useEffect(() => { const targetBones = ikSolver?.mesh.skeleton.bones.find((b: any) => b.name === targetBone); if (!isReset && isPlaying) { - //Moving armBot from initial point to rest position. if (!armBot?.isActive && armBot?.state == "idle" && currentPhase == "init") { if (targetBones) { setArmBotActive(armBot.modelUuid, true) @@ -280,13 +212,9 @@ function RoboticArmInstance({ armBot }: { readonly armBot: ArmBotStatus }) { } } logStatus(armBot.modelUuid, "Moving armBot from initial point to rest position.") - } - //Waiting for trigger. - else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && !armBot.currentAction) { + } else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && !armBot.currentAction) { logStatus(armBot.modelUuid, "Waiting to trigger CurrentAction") - } - //Moving to pickup point - else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && armBot.currentAction) { + } else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && armBot.currentAction) { if (armBot.currentAction) { setArmBotActive(armBot.modelUuid, true); @@ -302,14 +230,45 @@ function RoboticArmInstance({ armBot }: { readonly armBot: ArmBotStatus }) { } } logStatus(armBot.modelUuid, "Moving armBot from rest point to start position.") - } - // Moving to Pick to Drop position - else if (armBot && !armBot.isActive && armBot.state === "running" && currentPhase === "picking" && armBot.currentAction) { - requestAnimationFrame(firstFrame); - } - //Moving to drop point to restPosition - else if (armBot && !armBot.isActive && armBot.state === "running" && currentPhase === "dropping" && armBot.currentAction) { - requestAnimationFrame(firstFrame); + } else if (armBot && !armBot.isActive && armBot.state === "running" && currentPhase === "picking" && armBot.currentAction) { + setTimeout(() => { + setArmBotActive(armBot.modelUuid, true); + setArmBotState(armBot.modelUuid, "running"); + setCurrentPhase("start-to-end"); + startTime = 0 + if (!action) return; + const startPoint = (action as RoboticArmAction).process.startPoint; + const endPoint = (action as RoboticArmAction).process.endPoint; + if (startPoint && endPoint) { + let curve = createCurveBetweenTwoPoints(new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2]), new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2])); + if (curve) { + logStatus(armBot.modelUuid, "picking the object"); + setPath(curve.points.map(point => [point.x, point.y, point.z])) + handlePickUpTrigger(); + } + } + logStatus(armBot.modelUuid, "Moving armBot from start point to end position.") + }, 100) + } else if (armBot && !armBot.isActive && armBot.state === "running" && currentPhase === "dropping" && armBot.currentAction) { + setTimeout(() => { + setArmBotActive(armBot.modelUuid, true); + setArmBotState(armBot.modelUuid, "running"); + setCurrentPhase("end-to-rest"); + startTime = 0; + if (!action) return; + const endPoint = (action as RoboticArmAction).process.endPoint; + if (endPoint) { + let curve = createCurveBetweenTwoPoints(new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2]), restPosition); + if (curve) { + logStatus(armBot.modelUuid, "dropping the object"); + setPath(curve.points.map(point => [point.x, point.y, point.z])); + + handleDropTrigger(); + + } + } + logStatus(armBot.modelUuid, "Moving armBot from end point to rest position.") + }, 100) } } else { logStatus(armBot.modelUuid, "Simulation Play Exited") @@ -348,7 +307,7 @@ function RoboticArmInstance({ armBot }: { readonly armBot: ArmBotStatus }) { setCurrentPhase("rest"); setPath([]) } - else if (armBot.isActive && armBot.state == "running" && currentPhase == "rest-to-start") { + else if (armBot.state == "running" && currentPhase == "rest-to-start") { logStatus(armBot.modelUuid, "Callback triggered: pick."); setArmBotActive(armBot.modelUuid, false) setArmBotState(armBot.modelUuid, "running") diff --git a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts index 020b1d3..d0b1619 100644 --- a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts +++ b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts @@ -528,24 +528,11 @@ export function useTriggerHandler() { if (action && armBot) { - if (armBot.isActive === false && armBot.state === 'idle') { - - // Handle current action from arm bot + addArmBotToMonitor(armBot.modelUuid, () => { setIsVisible(materialId, false); handleAction(action, materialId); - - } else { - - addArmBotToMonitor(armBot.modelUuid, - () => { - setIsVisible(materialId, false); - - handleAction(action, materialId); - } - ) - - } + }) } } } diff --git a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx index 62ddb21..c8463cf 100644 --- a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx +++ b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx @@ -147,6 +147,16 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai if (angle < 0.01) { object.quaternion.copy(targetQuaternion); setRestingRotation(false); + setTimeout(() => { + setRestingRotation(true); + progressRef.current = 0; + movingForward.current = !movingForward.current; + setCurrentPath([]); + handleCallBack(); + if (currentPhase === 'pickup-drop') { + requestAnimationFrame(startUnloadingProcess); + } + }, 0) } else { const step = rotationSpeed * delta * speed * agvDetail.speed; const angle = object.quaternion.angleTo(targetQuaternion); @@ -169,7 +179,6 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai handleCallBack(); if (currentPhase === 'pickup-drop') { requestAnimationFrame(startUnloadingProcess); - } } }); @@ -226,13 +235,8 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai ); } - - export default VehicleAnimator; - - - function DraggableSphere({ index, position, diff --git a/app/src/store/simulation/useProductStore.ts b/app/src/store/simulation/useProductStore.ts index f9533a1..8ff8ac8 100644 --- a/app/src/store/simulation/useProductStore.ts +++ b/app/src/store/simulation/useProductStore.ts @@ -73,6 +73,7 @@ type ProductsStore = { getPointUuidByActionUuid: (productUuid: string, actionUuid: string) => (string) | undefined; getTriggerByUuid: (productUuid: string, triggerUuid: string) => TriggerSchema | undefined; getTriggersByTriggeredPointUuid: (productUuid: string, triggeredPointUuid: string) => TriggerSchema[]; + getTriggeringModels: (productUuid: string, actionUUid: string) => EventsSchema[]; getIsEventInProduct: (productUuid: string, modelUuid: string) => boolean; }; @@ -898,6 +899,58 @@ export const createProductStore = () => { return undefined; }, + getTriggeringModels: (productUuid: string, actionUuid: string) => { + const product = get().products.find(p => p.productUuid === productUuid); + if (!product) return []; + + const triggeringModels: EventsSchema[] = []; + const targetModelUuid = get().getModelUuidByActionUuid(productUuid, actionUuid); + + if (!targetModelUuid) return []; + + for (const event of product.eventDatas) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action?.triggers) { + for (const trigger of point.action.triggers) { + if (trigger.triggeredAsset?.triggeredModel?.modelUuid === targetModelUuid && + trigger.triggeredAsset?.triggeredAction?.actionUuid === actionUuid) { + triggeringModels.push(event); + break; + } + } + } + } + } else if ('point' in event) { + const point = (event as any).point; + + if ('action' in point && point.action?.triggers) { + for (const trigger of point.action.triggers) { + if (trigger.triggeredAsset?.triggeredModel?.modelUuid === targetModelUuid && + trigger.triggeredAsset?.triggeredAction?.actionUuid === actionUuid) { + triggeringModels.push(event); + break; + } + } + } else if ('actions' in point) { + for (const action of point.actions) { + if (action.triggers) { + for (const trigger of action.triggers) { + if (trigger.triggeredAsset?.triggeredModel?.modelUuid === targetModelUuid && + trigger.triggeredAsset?.triggeredAction?.actionUuid === actionUuid) { + triggeringModels.push(event); + break; + } + } + } + } + } + } + } + + return triggeringModels; + }, + getTriggersByTriggeredPointUuid: (productUuid, triggeredPointUuid) => { const product = get().products.find(p => p.productUuid === productUuid); if (!product) return [];