/** * useMachinePolling Hook * * Implements dynamic polling for machine status based on machine state. * Uses adaptive polling intervals and conditional progress polling during sewing. * * Polling intervals: * - 500ms for active states (SEWING, MASK_TRACING, SEWING_DATA_RECEIVE) * - 1000ms for waiting states (COLOR_CHANGE_WAIT, MASK_TRACE_LOCK_WAIT, SEWING_WAIT) * - 2000ms for idle/other states * * Additionally polls service count every 10 seconds. * * @param options - Configuration options * @param options.machineStatus - Current machine status to determine polling interval * @param options.patternInfo - Current pattern info for resumable pattern check * @param options.onStatusRefresh - Callback to refresh machine status * @param options.onProgressRefresh - Callback to refresh sewing progress * @param options.onServiceCountRefresh - Callback to refresh service count * @param options.onPatternInfoRefresh - Callback to refresh pattern info * @param options.shouldCheckResumablePattern - Function to check if resumable pattern exists * @returns Object containing start/stop functions and polling state * * @example * ```tsx * const { startPolling, stopPolling, isPolling } = useMachinePolling({ * machineStatus, * patternInfo, * onStatusRefresh: async () => { ... }, * onProgressRefresh: async () => { ... }, * onServiceCountRefresh: async () => { ... }, * onPatternInfoRefresh: async () => { ... }, * shouldCheckResumablePattern: () => resumeAvailable * }); * * useEffect(() => { * startPolling(); * return () => stopPolling(); * }, []); * ``` */ import { useState, useCallback, useRef, useEffect } from "react"; import { MachineStatus } from "../../types/machine"; import type { PatternInfo } from "../../types/machine"; export interface UseMachinePollingOptions { machineStatus: MachineStatus; patternInfo: PatternInfo | null; onStatusRefresh: () => Promise; onProgressRefresh: () => Promise; onServiceCountRefresh: () => Promise; onPatternInfoRefresh: () => Promise; shouldCheckResumablePattern: () => boolean; } export interface UseMachinePollingReturn { startPolling: () => void; stopPolling: () => void; isPolling: boolean; } export function useMachinePolling( options: UseMachinePollingOptions, ): UseMachinePollingReturn { const { machineStatus, patternInfo, onStatusRefresh, onProgressRefresh, onServiceCountRefresh, onPatternInfoRefresh, shouldCheckResumablePattern, } = options; const [isPolling, setIsPolling] = useState(false); const pollTimeoutRef = useRef(null); const serviceCountIntervalRef = useRef(null); const pollFunctionRef = useRef<(() => Promise) | undefined>(undefined); // Store callbacks in refs to avoid unnecessary re-renders const callbacksRef = useRef({ onStatusRefresh, onProgressRefresh, onServiceCountRefresh, onPatternInfoRefresh, shouldCheckResumablePattern, }); // Update refs when callbacks change useEffect(() => { callbacksRef.current = { onStatusRefresh, onProgressRefresh, onServiceCountRefresh, onPatternInfoRefresh, shouldCheckResumablePattern, }; }); // Function to determine polling interval based on machine status const getPollInterval = useCallback((status: MachineStatus) => { // Fast polling for active states if ( status === MachineStatus.SEWING || status === MachineStatus.MASK_TRACING || status === MachineStatus.SEWING_DATA_RECEIVE ) { return 500; } else if ( status === MachineStatus.COLOR_CHANGE_WAIT || status === MachineStatus.MASK_TRACE_LOCK_WAIT || status === MachineStatus.SEWING_WAIT ) { return 1000; } return 2000; // Default for idle states }, []); // Main polling function const poll = useCallback(async () => { await callbacksRef.current.onStatusRefresh(); // Refresh progress during sewing if (machineStatus === MachineStatus.SEWING) { await callbacksRef.current.onProgressRefresh(); } // Check if we have a cached pattern and pattern info needs refreshing // This follows the app's logic for resumable patterns if ( callbacksRef.current.shouldCheckResumablePattern() && patternInfo?.totalStitches === 0 ) { await callbacksRef.current.onPatternInfoRefresh(); } // Schedule next poll with updated interval const newInterval = getPollInterval(machineStatus); if (pollFunctionRef.current) { pollTimeoutRef.current = setTimeout(pollFunctionRef.current, newInterval); } }, [machineStatus, patternInfo, getPollInterval]); // Store poll function in ref for recursive setTimeout useEffect(() => { pollFunctionRef.current = poll; }, [poll]); const stopPolling = useCallback(() => { if (pollTimeoutRef.current) { clearTimeout(pollTimeoutRef.current); pollTimeoutRef.current = null; } if (serviceCountIntervalRef.current) { clearInterval(serviceCountIntervalRef.current); serviceCountIntervalRef.current = null; } setIsPolling(false); }, []); const startPolling = useCallback(() => { // Stop any existing polling stopPolling(); // Start main polling const initialInterval = getPollInterval(machineStatus); pollTimeoutRef.current = setTimeout(poll, initialInterval); // Start service count polling (every 10 seconds) serviceCountIntervalRef.current = setInterval( callbacksRef.current.onServiceCountRefresh, 10000, ); setIsPolling(true); }, [machineStatus, poll, stopPolling, getPollInterval]); return { startPolling, stopPolling, isPolling, }; }