diff --git a/.phpunit.cache/test-results b/.phpunit.cache/test-results index 230e444..09af03b 100644 --- a/.phpunit.cache/test-results +++ b/.phpunit.cache/test-results @@ -1 +1 @@ -{"version":2,"defects":{"tests\\flow\\FlowProcessorTest::testCreateProcessor":8,"tests\\flow\\FlowProcessorTest::testCreateProcessorWithConfig":8,"tests\\flow\\FlowProcessorTest::testUpdateConfig":8,"tests\\flow\\FlowProcessorTest::testBatchNoConsistency":8,"tests\\flow\\FlowProcessorTest::testBatchNoConsistencyInCompleteProcess":8,"tests\\flow\\FlowProcessorTest::testDatabaseOperationFlags":8,"tests\\flow\\FlowProcessorTest::testWebSocketNotifyFlag":8,"tests\\flow\\FlowProcessorTest::testVoiceGeneration":8,"tests\\flow\\FlowProcessorTest::testErrorVoiceGeneration":7,"tests\\flow\\FlowProcessorTest::testMultiHospitalConfig":8,"tests\\flow\\FlowProcessorTest::testSpecialHospitalConfig":8,"tests\\flow\\ProcessEngineTest::testTimeValidationFailure":8,"tests\\flow\\ProcessEngineTest::testWrongStep":7,"tests\\flow\\strategies\\TimeValidationStrategyTest::testCustomDuration":7,"tests\\flow\\ProcessEngineTest::testSkipStep":7,"tests\\flow\\BatchConsistencyTest::testBatchNoPreservedOnError":8,"tests\\flow\\BatchConsistencyTest::testBatchNoWithDifferentProcessTypes":8,"tests\\flow\\EdgeCaseTest::testBatchNoGenerationEdgeCases":7,"tests\\flow\\PerformanceTest::testContextCreationPerformance":7,"tests\\flow\\PerformanceTest::testStrategyExecutionPerformance":7,"tests\\flow\\PerformanceTest::testChainTraversalPerformance":8,"tests\\flow\\nodes\\MorningWashNodeTest::testCanHandleMorningWashReader":7,"tests\\flow\\nodes\\MorningWashNodeTest::testHandleProcess":7,"tests\\flow\\nodes\\MorningWashNodeTest::testGenerateBatchNo":8,"tests\\flow\\nodes\\MorningWashNodeTest::testCanHandleDisinfectReaderForMorningWash":8,"tests\\flow\\nodes\\MorningWashNodeTest::testCanHandleMachineWashReaderForMorningWash":8,"tests\\flow\\nodes\\MorningWashNodeTest::testHandleProcessWithDisinfect":8,"tests\\flow\\nodes\\MorningWashNodeTest::testHandleProcessWithMachineWash":8,"tests\\flow\\EdgeCaseTest::testConcurrentBatchNoGeneration":7,"tests\\flow\\ProcessEngineTest::testDisinfectAfterWash":7,"tests\\flow\\nodes\\DisinfectNodeTest::testCanHandleAfterWash":8,"tests\\flow\\nodes\\DryNodeTest::testCanHandleAfterDisinfect":8,"tests\\flow\\UsageExampleTest::testExample4CustomVoice":8,"tests\\flow\\UsageExampleTest::testExample8TimeValidation":8,"tests\\flow\\UsageExampleTest::testLoadCustomProcessFromConfig":8,"tests\\flow\\UsageExampleTest::testCreateNoMorningWashFromConfig":8,"tests\\flow\\UsageExampleTest::testCreateMachineWashFromConfig":8,"tests\\flow\\UsageExampleTest::testCreateYiwuModeFromConfig":8,"tests\\flow\\UsageExampleTest::testExample1StandardProcess":8,"tests\\flow\\UsageExampleTest::testExample2NoMorningWash":8,"tests\\flow\\UsageExampleTest::testExample3DynamicAdjust":8,"tests\\flow\\UsageExampleTest::testExample5MorningWashModes":8,"tests\\flow\\UsageExampleTest::testExample6MachineWash":8,"tests\\flow\\UsageExampleTest::testExample7SimpleProcess":8,"tests\\flow\\UsageExampleTest::testExample9FullProcess":8,"tests\\flow\\UsageExampleTest::testExample10MultiHospital":8,"tests\\flow\\UsageExampleTest::testFlowProcessorUsage":8,"tests\\flow\\UsageExampleTest::testFlowProcessorCompleteProcess":8,"tests\\flow\\UsageExampleTest::testFlowProcessorWithConfig":8,"tests\\flow\\BatchConsistencyTest::testSingleMachineBatchConsistency":8,"tests\\flow\\BatchConsistencyTest::testMultiEndoscopeBatchUniqueness":8,"tests\\flow\\BatchConsistencyTest::testBatchNoStability":8,"tests\\flow\\BatchConsistencyTest::testNewProcessGeneratesNewBatchNo":8,"tests\\flow\\BatchConsistencyTest::testDistributedBatchConsistency":8,"tests\\flow\\PerformanceTest::testSingleExecutionPerformance":8,"tests\\flow\\PerformanceTest::testCompleteProcessPerformance":8,"tests\\flow\\PerformanceTest::testEngineCreationPerformance":8,"tests\\flow\\PerformanceTest::testConcurrentProcessingSimulation":8,"tests\\flow\\ProcessEngineTest::testCreateStandardEngine":8,"tests\\flow\\ProcessEngineTest::testCreateNoMorningWashEngine":8,"tests\\flow\\ProcessEngineTest::testCreateSimpleEngine":8,"tests\\flow\\ProcessEngineTest::testCompleteWashProcess":8,"tests\\flow\\ProcessEngineTest::testStandardWashRinseDisinfectFlow":8,"tests\\flow\\ProcessEngineTest::testDisableNode":8,"tests\\flow\\ProcessEngineTest::testGetNodes":8,"tests\\flow\\ProcessEngineTest::testGetEnabledNodes":8,"tests\\flow\\ProcessEngineTest::testUpdateConfig":8,"tests\\flow\\ProcessEngineTest::testMachineWashProcess":8,"tests\\db\\DBC::testDBConnect":8,"tests\\db\\DBC::testGenTables":7,"tests\\db\\DBC::testModel":5,"tests\\flow\\nodes\\DisinfectNodeTest::testNodeIdentity":8,"tests\\flow\\nodes\\DisinfectNodeTest::testCanHandleAfterRinse":8,"tests\\flow\\nodes\\DisinfectNodeTest::testCanHandleAfterFinalRinse":8,"tests\\flow\\nodes\\DisinfectNodeTest::testCannotHandleNonDisinfectReader":8,"tests\\flow\\nodes\\DisinfectNodeTest::testCannotHandleAfterDisinfect":8,"tests\\flow\\nodes\\DisinfectNodeTest::testHandleProcess":8,"tests\\flow\\nodes\\DisinfectNodeTest::testDatabaseOperationFlags":7,"tests\\flow\\nodes\\DisinfectNodeTest::testWebSocketNotifyFlag":8,"tests\\flow\\nodes\\DryNodeTest::testNodeIdentity":8,"tests\\flow\\nodes\\DryNodeTest::testCanHandleAfterFinalRinse":7,"tests\\flow\\nodes\\DryNodeTest::testCannotHandleAfterRinse":8,"tests\\flow\\nodes\\DryNodeTest::testCannotHandleNonDryReader":8,"tests\\flow\\nodes\\DryNodeTest::testCannotHandleAfterDry":7,"tests\\flow\\nodes\\DryNodeTest::testHandleProcess":7,"tests\\flow\\nodes\\DryNodeTest::testDatabaseOperationFlags":7,"tests\\flow\\nodes\\DryNodeTest::testWebSocketNotifyFlag":7,"tests\\flow\\nodes\\DryNodeTest::testKeepExistingBatchNo":8,"tests\\flow\\nodes\\MorningWashNodeTest::testNodeIdentity":8,"tests\\flow\\nodes\\MorningWashNodeTest::testCannotHandleWhenNoNeed":8,"tests\\flow\\nodes\\MorningWashNodeTest::testCannotHandleNonDisinfectMachineReader":8,"tests\\flow\\nodes\\MorningWashNodeTest::testCannotHandleWhenAlreadyWashed":8,"tests\\flow\\nodes\\EndNodeTest::testNodeIdentity":8,"tests\\flow\\nodes\\EndNodeTest::testCanHandleAfterDry":8,"tests\\flow\\nodes\\EndNodeTest::testCanHandleAfterDisinfect":7,"tests\\flow\\nodes\\EndNodeTest::testCanHandleAfterFinalRinse":8,"tests\\flow\\nodes\\EndNodeTest::testCanHandleAfterMachineWash":8,"tests\\flow\\nodes\\EndNodeTest::testCannotHandleNonEndReader":8,"tests\\flow\\nodes\\EndNodeTest::testCannotHandleAfterWash":8,"tests\\flow\\nodes\\EndNodeTest::testCannotHandleAfterRinse":8,"tests\\flow\\nodes\\EndNodeTest::testCannotHandleWithEmptyStep":8,"tests\\flow\\nodes\\EndNodeTest::testHandleProcess":8,"tests\\flow\\nodes\\EndNodeTest::testDatabaseOperationIsUpdate":7,"tests\\flow\\nodes\\EndNodeTest::testWebSocketNotifyFlag":8,"tests\\flow\\nodes\\EndNodeTest::testActionEndTimeIsSet":8,"tests\\flow\\nodes\\FinalRinseNodeTest::testCanHandleAfterDisinfect":8,"tests\\flow\\nodes\\FinalRinseNodeTest::testCanHandleAfterMachineWashByDefault":8,"tests\\flow\\nodes\\FinalRinseNodeTest::testCannotHandleAfterMachineWashWhenDisabled":8,"tests\\flow\\nodes\\FinalRinseNodeTest::testCannotHandleNonFinalRinseReader":8,"tests\\flow\\nodes\\FinalRinseNodeTest::testHandleProcess":8,"tests\\flow\\nodes\\FinalRinseNodeTest::testCannotHandleAfterWash":8,"tests\\flow\\nodes\\FinalRinseNodeTest::testDatabaseOperationFlags":8,"tests\\flow\\nodes\\FinalRinseNodeTest::testWebSocketNotifyFlag":8,"tests\\flow\\nodes\\FinalRinseNodeTest::testNodeIdentity":8,"tests\\flow\\nodes\\MachineWashNodeTest::testNodeIdentity":8,"tests\\flow\\nodes\\MachineWashNodeTest::testCanHandleAfterWash":8,"tests\\flow\\nodes\\MachineWashNodeTest::testCanHandleAfterRinse":7,"tests\\flow\\nodes\\MachineWashNodeTest::testCanHandleAfterDisinfect":7,"tests\\flow\\nodes\\MachineWashNodeTest::testCanHandleWithEmptyStep":7,"tests\\flow\\nodes\\MachineWashNodeTest::testCanHandleAfterEnd":7,"tests\\flow\\nodes\\MachineWashNodeTest::testCanHandleAfterEndoscopeOut":8,"tests\\flow\\nodes\\MachineWashNodeTest::testCannotHandleNonMachineWashReader":8,"tests\\flow\\nodes\\MachineWashNodeTest::testCannotHandleAfterFinalRinse":8,"tests\\flow\\nodes\\MachineWashNodeTest::testCannotHandleAfterDry":8,"tests\\flow\\nodes\\MachineWashNodeTest::testHandleProcess":8,"tests\\flow\\nodes\\MachineWashNodeTest::testDatabaseOperationFlags":7,"tests\\flow\\nodes\\MachineWashNodeTest::testWebSocketNotifyFlag":8,"tests\\flow\\nodes\\RinseNodeTest::testNodeIdentity":8,"tests\\flow\\nodes\\RinseNodeTest::testCanHandleAfterWash":7,"tests\\flow\\nodes\\RinseNodeTest::testCannotHandleNonRinseReader":8,"tests\\flow\\nodes\\RinseNodeTest::testCannotHandleAfterDisinfect":7,"tests\\flow\\nodes\\RinseNodeTest::testCannotHandleWithEmptyStep":7,"tests\\flow\\nodes\\RinseNodeTest::testCannotHandleAfterRinse":7,"tests\\flow\\nodes\\RinseNodeTest::testHandleProcess":7,"tests\\flow\\nodes\\RinseNodeTest::testDatabaseOperationFlags":7,"tests\\flow\\nodes\\RinseNodeTest::testWebSocketNotifyFlag":7,"tests\\flow\\nodes\\RinseNodeTest::testDisabledNodeSkips":8,"tests\\flow\\nodes\\WashNodeTest::testNodeIdentity":8,"tests\\flow\\nodes\\WashNodeTest::testCanHandleWashReader":8,"tests\\flow\\nodes\\WashNodeTest::testCannotHandleNonWashReader":8,"tests\\flow\\nodes\\WashNodeTest::testCanStartNewWashProcess":8,"tests\\flow\\nodes\\WashNodeTest::testCannotWashWithoutMorningWash":8,"tests\\flow\\nodes\\WashNodeTest::testHandleProcess":8,"tests\\flow\\nodes\\WashNodeTest::testGenerateBatchNo":7,"tests\\flow\\nodes\\WashNodeTest::testKeepExistingBatchNo":8,"tests\\flow\\strategies\\TimeValidationStrategyTest::testTimeRequirementNotMet":7,"tests\\flow\\strategies\\TimeValidationStrategyTest::testSetStepDuration":7,"tests\\flow\\FlowProcessorTest::testSuccessfulFlowSavesToDatabase":8,"tests\\flow\\FlowProcessorTest::testEndOperationIsUpdate":8,"tests\\flow\\FlowProcessorTest::testGetActionTypeMapping":8,"tests\\flow\\EdgeCaseTest::testLongEndoscopeName":7,"tests\\flow\\EdgeCaseTest::testSpecialCharactersInName":7,"tests\\flow\\ProcessContextTest::testGetFullVoice":7,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testFullVoiceIncludesEndoscopeName":7,"tests\\flow\\FlowProcessorTest::testSuccessfulFlowSendsVoice":8,"tests\\flow\\FlowProcessorTest::testFailedFlowDoesNotSaveToDatabase":8,"tests\\flow\\FlowProcessorTest::testFailedFlowStillSendsVoice":8,"tests\\flow\\FlowProcessorTest::testWebSocketNotificationSentWhenNeeded":8,"tests\\flow\\FlowProcessorTest::testWebSocketNotificationNotSentWhenNotNeeded":8,"tests\\flow\\FlowProcessorTest::testStaticFactoryCreate":8,"tests\\flow\\EdgeCaseTest::testAllNodesDisabled":8,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testFinalRinseVoice":8,"tests\\flow\\ProcessSimulationTest::testDuplicateCheckNode":7,"tests\\flow\\FullProcessTest::testCompleteManualWashProcess":7,"tests\\flow\\FullProcessTest::testSwipeEndoscopeWithoutOperator":7,"tests\\flow\\cases\\BlockTest::testProcessChainExecution":5,"tests\\flow\\cases\\BlockTest::testDisabledNodeSkipped":5,"tests\\flow\\cases\\BlockTest::testTimeValidationBlockWithBlockModeOn":5,"tests\\flow\\cases\\BlockTest::testTimeValidationWithBlockModeOff":5,"tests\\flow\\cases\\BlockTest::testDuplicateSwipeBlock":5,"tests\\flow\\cases\\BlockTest::testTimeValidationPass":5,"tests\\flow\\cases\\ManualWashTest::testCompleteManualWashProcess":8},"times":{"tests\\flow\\FlowProcessorTest::testCreateProcessor":0.006,"tests\\flow\\FlowProcessorTest::testCreateProcessorWithConfig":0.006,"tests\\flow\\FlowProcessorTest::testStaticFactory":0.006,"tests\\flow\\FlowProcessorTest::testUpdateConfig":0.009,"tests\\flow\\FlowProcessorTest::testBatchNoConsistency":0.006,"tests\\flow\\FlowProcessorTest::testBatchNoConsistencyInCompleteProcess":0.008,"tests\\flow\\FlowProcessorTest::testDatabaseOperationFlags":0.006,"tests\\flow\\FlowProcessorTest::testWebSocketNotifyFlag":0.005,"tests\\flow\\FlowProcessorTest::testVoiceGeneration":0.007,"tests\\flow\\FlowProcessorTest::testErrorVoiceGeneration":0.006,"tests\\flow\\FlowProcessorTest::testMultiHospitalConfig":0.007,"tests\\flow\\FlowProcessorTest::testSpecialHospitalConfig":0.006,"tests\\flow\\ProcessContextTest::testCreateContext":0.005,"tests\\flow\\ProcessContextTest::testSetError":0.006,"tests\\flow\\ProcessContextTest::testSetVoice":0.006,"tests\\flow\\ProcessContextTest::testGetFullVoice":0.006,"tests\\flow\\ProcessContextTest::testStepTimeManagement":0.006,"tests\\flow\\ProcessContextTest::testStepDurationManagement":0.005,"tests\\flow\\ProcessContextTest::testDefaultStepDuration":0.007,"tests\\flow\\ProcessContextTest::testGenerateBatchNo":0.006,"tests\\flow\\ProcessContextTest::testBatchNoUniqueness":0.006,"tests\\flow\\ProcessContextTest::testCanStartNewProcess":0.006,"tests\\flow\\ProcessContextTest::testIsWashProcessCompleted":0.006,"tests\\flow\\ProcessContextTest::testChaining":0.005,"tests\\flow\\ProcessEngineTest::testCreateStandardEngine":0.006,"tests\\flow\\ProcessEngineTest::testCreateNoMorningWashEngine":0.006,"tests\\flow\\ProcessEngineTest::testCreateSimpleEngine":0.006,"tests\\flow\\ProcessEngineTest::testCompleteWashProcess":0.045,"tests\\flow\\ProcessEngineTest::testTimeValidationFailure":0.005,"tests\\flow\\ProcessEngineTest::testWrongStep":0.01,"tests\\flow\\ProcessEngineTest::testDisableNode":0.005,"tests\\flow\\ProcessEngineTest::testGetNodes":0.006,"tests\\flow\\ProcessEngineTest::testGetEnabledNodes":0.005,"tests\\flow\\ProcessEngineTest::testUpdateConfig":0.006,"tests\\flow\\ProcessEngineTest::testMachineWashProcess":0.033,"tests\\flow\\nodes\\FinalRinseNodeTest::testCanHandleAfterDisinfect":0.007,"tests\\flow\\nodes\\FinalRinseNodeTest::testCanHandleAfterMachineWashByDefault":0.005,"tests\\flow\\nodes\\FinalRinseNodeTest::testCannotHandleAfterMachineWashWhenDisabled":0.008,"tests\\flow\\nodes\\FinalRinseNodeTest::testCannotHandleNonFinalRinseReader":0.008,"tests\\flow\\nodes\\FinalRinseNodeTest::testHandleProcess":0.01,"tests\\flow\\nodes\\WashNodeTest::testNodeIdentity":0.006,"tests\\flow\\nodes\\WashNodeTest::testCanHandleWashReader":0.006,"tests\\flow\\nodes\\WashNodeTest::testCannotHandleNonWashReader":0.005,"tests\\flow\\nodes\\WashNodeTest::testCanStartNewWashProcess":0.005,"tests\\flow\\nodes\\WashNodeTest::testCannotWashWithoutMorningWash":0.006,"tests\\flow\\nodes\\WashNodeTest::testHandleProcess":0.009,"tests\\flow\\nodes\\WashNodeTest::testGenerateBatchNo":0.008,"tests\\flow\\nodes\\WashNodeTest::testKeepExistingBatchNo":0.006,"tests\\flow\\strategies\\MorningWashStrategyTest::testNoneMode":0.006,"tests\\flow\\strategies\\MorningWashStrategyTest::testAllMode":0.005,"tests\\flow\\strategies\\MorningWashStrategyTest::testDailyFirstModeWithNoRecords":0.005,"tests\\flow\\strategies\\MorningWashStrategyTest::testDailyFirstModeWithRecords":0.006,"tests\\flow\\strategies\\MorningWashStrategyTest::testSpecificTypesMode":0.005,"tests\\flow\\strategies\\MorningWashStrategyTest::testStorageTimeMode":0.006,"tests\\flow\\strategies\\MorningWashStrategyTest::testStrategyName":0.005,"tests\\flow\\strategies\\TimeValidationStrategyTest::testNoTimeLimitForFirstTime":0.005,"tests\\flow\\strategies\\TimeValidationStrategyTest::testTimeRequirementMet":0.006,"tests\\flow\\strategies\\TimeValidationStrategyTest::testTimeRequirementNotMet":0.005,"tests\\flow\\strategies\\TimeValidationStrategyTest::testCustomDuration":0.006,"tests\\flow\\strategies\\TimeValidationStrategyTest::testIsApplicable":0.006,"tests\\flow\\strategies\\TimeValidationStrategyTest::testStrategyName":0.006,"tests\\flow\\strategies\\TimeValidationStrategyTest::testStrategyPhase":0.005,"tests\\flow\\ProcessEngineTest::testSkipStep":0.012,"tests\\flow\\ProcessEngineTest::testDisinfectAfterWash":0.005,"tests\\flow\\BatchConsistencyTest::testSingleMachineBatchConsistency":0.046,"tests\\flow\\BatchConsistencyTest::testMultiEndoscopeBatchUniqueness":0.036,"tests\\flow\\BatchConsistencyTest::testBatchNoFormat":0.008,"tests\\flow\\BatchConsistencyTest::testBatchNoStability":0.02,"tests\\flow\\BatchConsistencyTest::testNewProcessGeneratesNewBatchNo":0.024,"tests\\flow\\BatchConsistencyTest::testDistributedBatchConsistency":0.057,"tests\\flow\\BatchConsistencyTest::testBatchNoPreservedOnError":0.024,"tests\\flow\\BatchConsistencyTest::testBatchNoWithDifferentProcessTypes":0.022,"tests\\flow\\EdgeCaseTest::testEmptyContext":0.005,"tests\\flow\\EdgeCaseTest::testLongEndoscopeName":0.006,"tests\\flow\\EdgeCaseTest::testSpecialCharactersInName":0.006,"tests\\flow\\EdgeCaseTest::testBatchNoGenerationEdgeCases":0.006,"tests\\flow\\EdgeCaseTest::testTimeBoundaryValues":0.006,"tests\\flow\\EdgeCaseTest::testEmptyChain":0.005,"tests\\flow\\EdgeCaseTest::testAllNodesDisabled":0.021,"tests\\flow\\EdgeCaseTest::testRepeatedStepTimeSetting":0.006,"tests\\flow\\EdgeCaseTest::testChainingEdgeCases":0.006,"tests\\flow\\EdgeCaseTest::testErrorStateOverwrite":0.006,"tests\\flow\\EdgeCaseTest::testRecoveryFromError":0.005,"tests\\flow\\EdgeCaseTest::testInvalidConfigItems":0.007,"tests\\flow\\EdgeCaseTest::testConcurrentBatchNoGeneration":0.011,"tests\\flow\\EdgeCaseTest::testMorningWashBoundaryTime":0.006,"tests\\flow\\EdgeCaseTest::testStorageTimeBoundary":0.007,"tests\\flow\\EdgeCaseTest::testStorageTimeOverThreshold":0.005,"tests\\flow\\PerformanceTest::testSingleExecutionPerformance":0.011,"tests\\flow\\PerformanceTest::testCompleteProcessPerformance":0.039,"tests\\flow\\PerformanceTest::testBatchNoGenerationPerformance":0.023,"tests\\flow\\PerformanceTest::testEngineCreationPerformance":0.094,"tests\\flow\\PerformanceTest::testConfigLoadingPerformance":0.023,"tests\\flow\\PerformanceTest::testContextCreationPerformance":0.258,"tests\\flow\\PerformanceTest::testStrategyExecutionPerformance":0.156,"tests\\flow\\PerformanceTest::testMemoryUsage":0.226,"tests\\flow\\PerformanceTest::testConcurrentProcessingSimulation":0.058,"tests\\flow\\PerformanceTest::testChainTraversalPerformance":5.796,"tests\\flow\\config\\ProcessConfigTest::testCreateDefaultConfig":0.007,"tests\\flow\\config\\ProcessConfigTest::testFromArray":0.006,"tests\\flow\\config\\ProcessConfigTest::testGetEnabledSteps":0.007,"tests\\flow\\config\\ProcessConfigTest::testAddStep":0.006,"tests\\flow\\config\\ProcessConfigTest::testRemoveStep":0.005,"tests\\flow\\config\\ProcessConfigTest::testSetNodeEnabled":0.008,"tests\\flow\\config\\ProcessConfigTest::testMorningWashConfig":0.006,"tests\\flow\\config\\ProcessConfigTest::testTimeValidationConfig":0.007,"tests\\flow\\config\\ProcessConfigTest::testVoiceTemplateConfig":0.005,"tests\\flow\\config\\ProcessConfigTest::testSetStepVoice":0.006,"tests\\flow\\config\\ProcessConfigTest::testToArray":0.007,"tests\\flow\\config\\ProcessConfigTest::testCreateStandard":0.006,"tests\\flow\\config\\ProcessConfigTest::testCreateNoMorningWash":0.007,"tests\\flow\\config\\ProcessConfigTest::testCreateSimple":0.006,"tests\\flow\\config\\ProcessConfigTest::testCreateMachineWash":0.005,"tests\\flow\\config\\ProcessConfigTest::testCreateNoDry":0.006,"tests\\flow\\config\\ProcessConfigTest::testCreateDryOnly":0.005,"tests\\flow\\config\\ProcessConfigTest::testChaining":0.006,"tests\\flow\\nodes\\DisinfectNodeTest::testNodeIdentity":0.005,"tests\\flow\\nodes\\DisinfectNodeTest::testCanHandleAfterRinse":0.005,"tests\\flow\\nodes\\DisinfectNodeTest::testCanHandleAfterFinalRinse":0.005,"tests\\flow\\nodes\\DisinfectNodeTest::testCanHandleAfterWash":0.005,"tests\\flow\\nodes\\DisinfectNodeTest::testCannotHandleNonDisinfectReader":0.005,"tests\\flow\\nodes\\DisinfectNodeTest::testCannotHandleAfterDisinfect":0.005,"tests\\flow\\nodes\\DisinfectNodeTest::testHandleProcess":0.009,"tests\\flow\\nodes\\DisinfectNodeTest::testDatabaseOperationFlags":0.008,"tests\\flow\\nodes\\DisinfectNodeTest::testWebSocketNotifyFlag":0.008,"tests\\flow\\nodes\\MorningWashNodeTest::testNodeIdentity":0.005,"tests\\flow\\nodes\\MorningWashNodeTest::testCanHandleMorningWashReader":0.021,"tests\\flow\\nodes\\MorningWashNodeTest::testCannotHandleWhenNoNeed":0.006,"tests\\flow\\nodes\\MorningWashNodeTest::testCannotHandleNonMorningWashReader":0.005,"tests\\flow\\nodes\\MorningWashNodeTest::testCannotHandleWhenAlreadyWashed":0.005,"tests\\flow\\nodes\\MorningWashNodeTest::testHandleProcess":0.008,"tests\\flow\\nodes\\MorningWashNodeTest::testGenerateBatchNo":0.009,"tests\\flow\\nodes\\MorningWashNodeTest::testCanHandleDisinfectReaderForMorningWash":0.006,"tests\\flow\\nodes\\MorningWashNodeTest::testCanHandleMachineWashReaderForMorningWash":0.005,"tests\\flow\\nodes\\MorningWashNodeTest::testCannotHandleNonDisinfectMachineReader":0.006,"tests\\flow\\nodes\\MorningWashNodeTest::testHandleProcessWithDisinfect":0.009,"tests\\flow\\nodes\\MorningWashNodeTest::testHandleProcessWithMachineWash":0.009,"tests\\flow\\nodes\\DryNodeTest::testNodeIdentity":0.006,"tests\\flow\\nodes\\DryNodeTest::testCanHandleAfterFinalRinse":0.007,"tests\\flow\\nodes\\DryNodeTest::testCanHandleAfterDisinfect":0.006,"tests\\flow\\nodes\\DryNodeTest::testCannotHandleAfterRinse":0.005,"tests\\flow\\nodes\\DryNodeTest::testCannotHandleNonDryReader":0.007,"tests\\flow\\nodes\\DryNodeTest::testCannotHandleAfterDry":0.006,"tests\\flow\\nodes\\DryNodeTest::testHandleProcess":0.008,"tests\\flow\\nodes\\DryNodeTest::testDatabaseOperationFlags":0.009,"tests\\flow\\nodes\\DryNodeTest::testWebSocketNotifyFlag":0.008,"tests\\flow\\nodes\\DryNodeTest::testKeepExistingBatchNo":0.01,"tests\\flow\\ProcessEngineTest::testStandardWashRinseDisinfectFlow":0.023,"tests\\flow\\UsageExampleTest::testExample1StandardProcess":0.022,"tests\\flow\\UsageExampleTest::testExample2NoMorningWash":0.025,"tests\\flow\\UsageExampleTest::testExample3DynamicAdjust":0.048,"tests\\flow\\UsageExampleTest::testExample4CustomVoice":0.022,"tests\\flow\\UsageExampleTest::testExample5MorningWashModes":0.033,"tests\\flow\\UsageExampleTest::testExample6MachineWash":0.022,"tests\\flow\\UsageExampleTest::testExample7SimpleProcess":0.021,"tests\\flow\\UsageExampleTest::testExample8TimeValidation":0.021,"tests\\flow\\UsageExampleTest::testExample9FullProcess":0.021,"tests\\flow\\UsageExampleTest::testExample10MultiHospital":0.046,"tests\\flow\\UsageExampleTest::testLoadCustomProcessFromConfig":0.007,"tests\\flow\\UsageExampleTest::testCreateNoMorningWashFromConfig":0.042,"tests\\flow\\UsageExampleTest::testCreateMachineWashFromConfig":0.036,"tests\\flow\\UsageExampleTest::testCreateYiwuModeFromConfig":0.015,"tests\\flow\\UsageExampleTest::testFlowProcessorUsage":0.045,"tests\\flow\\UsageExampleTest::testFlowProcessorCompleteProcess":0.021,"tests\\flow\\UsageExampleTest::testFlowProcessorWithConfig":0.037,"tests\\db\\DBC::testDBConnect":0.113,"tests\\db\\DBC::testGenTables":0.416,"tests\\db\\DBC::testModel":0.171,"tests\\flow\\nodes\\EndNodeTest::testNodeIdentity":0.006,"tests\\flow\\nodes\\EndNodeTest::testCanHandleAfterDry":0.006,"tests\\flow\\nodes\\EndNodeTest::testCanHandleAfterDisinfect":0.005,"tests\\flow\\nodes\\EndNodeTest::testCanHandleAfterFinalRinse":0.005,"tests\\flow\\nodes\\EndNodeTest::testCanHandleAfterMachineWash":0.005,"tests\\flow\\nodes\\EndNodeTest::testCannotHandleNonEndReader":0.005,"tests\\flow\\nodes\\EndNodeTest::testCannotHandleAfterWash":0.007,"tests\\flow\\nodes\\EndNodeTest::testCannotHandleAfterRinse":0.006,"tests\\flow\\nodes\\EndNodeTest::testCannotHandleWithEmptyStep":0.005,"tests\\flow\\nodes\\EndNodeTest::testHandleProcess":0.009,"tests\\flow\\nodes\\EndNodeTest::testDatabaseOperationIsUpdate":0.011,"tests\\flow\\nodes\\EndNodeTest::testWebSocketNotifyFlag":0.009,"tests\\flow\\nodes\\EndNodeTest::testActionEndTimeIsSet":0.009,"tests\\flow\\nodes\\FinalRinseNodeTest::testCannotHandleAfterWash":0.006,"tests\\flow\\nodes\\FinalRinseNodeTest::testDatabaseOperationFlags":0.008,"tests\\flow\\nodes\\FinalRinseNodeTest::testWebSocketNotifyFlag":0.009,"tests\\flow\\nodes\\FinalRinseNodeTest::testNodeIdentity":0.005,"tests\\flow\\nodes\\MachineWashNodeTest::testNodeIdentity":0.005,"tests\\flow\\nodes\\MachineWashNodeTest::testCanHandleAfterWash":0.005,"tests\\flow\\nodes\\MachineWashNodeTest::testCanHandleAfterRinse":0.005,"tests\\flow\\nodes\\MachineWashNodeTest::testCanHandleAfterDisinfect":0.005,"tests\\flow\\nodes\\MachineWashNodeTest::testCanHandleWithEmptyStep":0.006,"tests\\flow\\nodes\\MachineWashNodeTest::testCanHandleAfterEnd":0.006,"tests\\flow\\nodes\\MachineWashNodeTest::testCanHandleAfterEndoscopeOut":0.005,"tests\\flow\\nodes\\MachineWashNodeTest::testCannotHandleNonMachineWashReader":0.005,"tests\\flow\\nodes\\MachineWashNodeTest::testCannotHandleAfterFinalRinse":0.005,"tests\\flow\\nodes\\MachineWashNodeTest::testCannotHandleAfterDry":0.006,"tests\\flow\\nodes\\MachineWashNodeTest::testHandleProcess":0.008,"tests\\flow\\nodes\\MachineWashNodeTest::testDatabaseOperationFlags":0.01,"tests\\flow\\nodes\\MachineWashNodeTest::testWebSocketNotifyFlag":0.008,"tests\\flow\\nodes\\RinseNodeTest::testNodeIdentity":0.005,"tests\\flow\\nodes\\RinseNodeTest::testCanHandleAfterWash":0.007,"tests\\flow\\nodes\\RinseNodeTest::testCannotHandleNonRinseReader":0.007,"tests\\flow\\nodes\\RinseNodeTest::testCannotHandleAfterDisinfect":0.006,"tests\\flow\\nodes\\RinseNodeTest::testCannotHandleWithEmptyStep":0.006,"tests\\flow\\nodes\\RinseNodeTest::testCannotHandleAfterRinse":0.005,"tests\\flow\\nodes\\RinseNodeTest::testHandleProcess":0.01,"tests\\flow\\nodes\\RinseNodeTest::testDatabaseOperationFlags":0.009,"tests\\flow\\nodes\\RinseNodeTest::testWebSocketNotifyFlag":0.009,"tests\\flow\\nodes\\RinseNodeTest::testDisabledNodeSkips":0.009,"tests\\flow\\strategies\\TimeValidationStrategyTest::testNonDurationStepIsSkipped":0.008,"tests\\flow\\strategies\\TimeValidationStrategyTest::testSetStepDuration":0.006,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testStrategyName":0.007,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testStrategyPhaseIsAfter":0.006,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testIsAlwaysApplicable":0.007,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testNormalWashVoice":0.005,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testNormalRinseVoice":0.005,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testNormalDisinfectVoice":0.005,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testFinalRinseVoice":0.006,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testDryVoice":0.006,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testEndVoice":0.005,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testMachineWashVoice":0.007,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testMorningWashVoice":0.005,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testMachineMorningWashVoice":0.006,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testErrorStateGeneratesErrorVoice":0.006,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testFullVoiceIncludesEndoscopeName":0.006,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testLeakTestRemindAppended":0.006,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testStorageRemindAppended":0.005,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testCustomVoiceTemplate":0.005,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testSetStepVoice":0.005,"tests\\flow\\FlowProcessorTest::testSuccessfulFlowSavesToDatabase":0.011,"tests\\flow\\FlowProcessorTest::testSuccessfulFlowSendsVoice":0.008,"tests\\flow\\FlowProcessorTest::testFailedFlowDoesNotSaveToDatabase":0.007,"tests\\flow\\FlowProcessorTest::testFailedFlowStillSendsVoice":0.008,"tests\\flow\\FlowProcessorTest::testWebSocketNotificationSentWhenNeeded":0.01,"tests\\flow\\FlowProcessorTest::testWebSocketNotificationNotSentWhenNotNeeded":0.008,"tests\\flow\\FlowProcessorTest::testEndOperationIsUpdate":0.01,"tests\\flow\\FlowProcessorTest::testGetActionTypeMapping":0.107,"tests\\flow\\FlowProcessorTest::testStaticFactoryCreate":0.007,"tests\\flow\\ProcessSimulationTest::testBasicManualWashWithoutDatabase":0.287,"tests\\flow\\ProcessSimulationTest::testNodeChainConstruction":0.264,"tests\\flow\\ProcessSimulationTest::testMorningWashNodeConfiguration":0.278,"tests\\flow\\ProcessSimulationTest::testMachineWashNodeConfiguration":0.271,"tests\\flow\\ProcessSimulationTest::testStorageNodeConfiguration":0.273,"tests\\flow\\ProcessSimulationTest::testDuplicateCheckNode":0.522,"tests\\flow\\ProcessSimulationTest::testCloseNode":0.268,"tests\\flow\\ProcessSimulationTest::testNodeEnableDisable":0.268,"tests\\flow\\ProcessSimulationTest::testConfigUpdate":0.522,"tests\\flow\\ProcessSimulationTest::testStrategyInitialization":0.291,"tests\\flow\\FullProcessTest::testCompleteManualWashProcess":0.09,"tests\\flow\\FullProcessTest::testCompleteManualWashProcessUsingHelper":0.285,"tests\\flow\\FullProcessTest::testMorningWashRequired":0.262,"tests\\flow\\FullProcessTest::testManualWashAfterMorningWash":0.269,"tests\\flow\\FullProcessTest::testMachineWashProcess":0.281,"tests\\flow\\FullProcessTest::testMachineWashProcessUsingHelper":0.286,"tests\\flow\\FullProcessTest::testMorningWashThenMachineWash":0.275,"tests\\flow\\FullProcessTest::testSwipeEndoscopeWithoutOperator":0.02,"tests\\flow\\FullProcessTest::testWrongStepOrder":0.078,"tests\\flow\\FullProcessTest::testDuplicateSwipe":0.142,"tests\\flow\\FullProcessTest::testUnboundReader":0.273,"tests\\flow\\FullProcessTest::testUnboundEndoscope":0.323,"tests\\flow\\FullProcessTest::testDifferentOperatorContinueProcess":0.124,"tests\\flow\\FullProcessTest::testWashStepVoice":0.074,"tests\\flow\\FullProcessTest::testDisinfectStepVoice":0.182,"tests\\flow\\FullProcessTest::testOperatorCardVoice":0.02,"tests\\flow\\FullProcessTest::testErrorVoice":0.021,"tests\\flow\\FullProcessTest::testVoiceTemplateParams":0.077,"tests\\flow\\FullProcessTest::testProcessChainExecution":0.236,"tests\\flow\\FullProcessTest::testDisabledNodeSkipped":0.277,"tests\\flow\\FullProcessTest::testTimeValidationBlock":0.265,"tests\\flow\\FullProcessTest::testDuplicateSwipeBlock":0.201,"tests\\flow\\FullProcessTest::testNewProcessAfterComplete":0.331,"tests\\flow\\FullProcessTest::testConcurrentProcessIsolation":0.174,"tests\\flow\\FullProcessTest::testProcessorReset":0.081,"tests\\flow\\FullProcessTest::testVirtualContextBuilder":0.017,"tests\\flow\\FullProcessTest::testEnvironmentConfig":0.007,"tests\\flow\\Test::testExample":0.005,"tests\\flow\\cases\\BlockTest::testProcessChainExecution":0.239,"tests\\flow\\cases\\BlockTest::testDisabledNodeSkipped":0.288,"tests\\flow\\cases\\BlockTest::testTimeValidationBlockWithBlockModeOn":0.263,"tests\\flow\\cases\\BlockTest::testTimeValidationWithBlockModeOff":0.282,"tests\\flow\\cases\\BlockTest::testDuplicateSwipeBlock":0.193,"tests\\flow\\cases\\BlockTest::testTimeValidationPass":0.281,"tests\\flow\\cases\\ErrorFlowTest::testSwipeEndoscopeWithoutOperator":0.008,"tests\\flow\\cases\\ErrorFlowTest::testWrongStepOrder":0.069,"tests\\flow\\cases\\ErrorFlowTest::testDuplicateSwipe":0.109,"tests\\flow\\cases\\ErrorFlowTest::testUnboundReader":0.276,"tests\\flow\\cases\\ErrorFlowTest::testUnboundEndoscope":0.27,"tests\\flow\\cases\\HelperTest::testProcessorReset":0.063,"tests\\flow\\cases\\HelperTest::testVirtualContextBuilder":0.007,"tests\\flow\\cases\\HelperTest::testEnvironmentConfig":0.006,"tests\\flow\\cases\\MachineWashTest::testMachineWashProcess":0.06,"tests\\flow\\cases\\MachineWashTest::testMachineWashProcessUsingHelper":0.06,"tests\\flow\\cases\\MachineWashTest::testMorningWashThenMachineWash":0.059,"tests\\flow\\cases\\MachineWashTest::testMultipleEndoscopeMachineWash":0.112,"tests\\flow\\cases\\ManualWashTest::testCompleteManualWashProcess":0.432,"tests\\flow\\cases\\ManualWashTest::testCompleteManualWashProcessUsingHelper":0.436,"tests\\flow\\cases\\ManualWashTest::testMorningWashRequired":0.316,"tests\\flow\\cases\\ManualWashTest::testManualWashAfterMorningWash":0.403,"tests\\flow\\cases\\ManualWashTest::testDifferentOperatorContinueProcess":0.138,"tests\\flow\\cases\\ManualWashTest::testNewProcessAfterComplete":0.443,"tests\\flow\\cases\\ManualWashTest::testConcurrentProcessIsolation":0.201,"tests\\flow\\cases\\VoiceTest::testWashStepVoice":0.064,"tests\\flow\\cases\\VoiceTest::testDisinfectStepVoice":0.2,"tests\\flow\\cases\\VoiceTest::testOperatorCardVoice":0.008,"tests\\flow\\cases\\VoiceTest::testErrorVoice":0.008,"tests\\flow\\cases\\VoiceTest::testWashDurationInsufficientVoice":0.276}} \ No newline at end of file +{"version":2,"defects":{"tests\\flow\\FlowProcessorTest::testCreateProcessor":8,"tests\\flow\\FlowProcessorTest::testCreateProcessorWithConfig":8,"tests\\flow\\FlowProcessorTest::testUpdateConfig":8,"tests\\flow\\FlowProcessorTest::testBatchNoConsistency":8,"tests\\flow\\FlowProcessorTest::testBatchNoConsistencyInCompleteProcess":8,"tests\\flow\\FlowProcessorTest::testDatabaseOperationFlags":8,"tests\\flow\\FlowProcessorTest::testWebSocketNotifyFlag":8,"tests\\flow\\FlowProcessorTest::testVoiceGeneration":8,"tests\\flow\\FlowProcessorTest::testErrorVoiceGeneration":7,"tests\\flow\\FlowProcessorTest::testMultiHospitalConfig":8,"tests\\flow\\FlowProcessorTest::testSpecialHospitalConfig":8,"tests\\flow\\ProcessEngineTest::testTimeValidationFailure":8,"tests\\flow\\ProcessEngineTest::testWrongStep":7,"tests\\flow\\strategies\\TimeValidationStrategyTest::testCustomDuration":7,"tests\\flow\\ProcessEngineTest::testSkipStep":7,"tests\\flow\\BatchConsistencyTest::testBatchNoPreservedOnError":8,"tests\\flow\\BatchConsistencyTest::testBatchNoWithDifferentProcessTypes":8,"tests\\flow\\EdgeCaseTest::testBatchNoGenerationEdgeCases":7,"tests\\flow\\PerformanceTest::testContextCreationPerformance":7,"tests\\flow\\PerformanceTest::testStrategyExecutionPerformance":7,"tests\\flow\\PerformanceTest::testChainTraversalPerformance":8,"tests\\flow\\nodes\\MorningWashNodeTest::testCanHandleMorningWashReader":7,"tests\\flow\\nodes\\MorningWashNodeTest::testHandleProcess":7,"tests\\flow\\nodes\\MorningWashNodeTest::testGenerateBatchNo":8,"tests\\flow\\nodes\\MorningWashNodeTest::testCanHandleDisinfectReaderForMorningWash":8,"tests\\flow\\nodes\\MorningWashNodeTest::testCanHandleMachineWashReaderForMorningWash":8,"tests\\flow\\nodes\\MorningWashNodeTest::testHandleProcessWithDisinfect":8,"tests\\flow\\nodes\\MorningWashNodeTest::testHandleProcessWithMachineWash":8,"tests\\flow\\EdgeCaseTest::testConcurrentBatchNoGeneration":7,"tests\\flow\\ProcessEngineTest::testDisinfectAfterWash":7,"tests\\flow\\nodes\\DisinfectNodeTest::testCanHandleAfterWash":8,"tests\\flow\\nodes\\DryNodeTest::testCanHandleAfterDisinfect":8,"tests\\flow\\UsageExampleTest::testExample4CustomVoice":8,"tests\\flow\\UsageExampleTest::testExample8TimeValidation":8,"tests\\flow\\UsageExampleTest::testLoadCustomProcessFromConfig":8,"tests\\flow\\UsageExampleTest::testCreateNoMorningWashFromConfig":8,"tests\\flow\\UsageExampleTest::testCreateMachineWashFromConfig":8,"tests\\flow\\UsageExampleTest::testCreateYiwuModeFromConfig":8,"tests\\flow\\UsageExampleTest::testExample1StandardProcess":8,"tests\\flow\\UsageExampleTest::testExample2NoMorningWash":8,"tests\\flow\\UsageExampleTest::testExample3DynamicAdjust":8,"tests\\flow\\UsageExampleTest::testExample5MorningWashModes":8,"tests\\flow\\UsageExampleTest::testExample6MachineWash":8,"tests\\flow\\UsageExampleTest::testExample7SimpleProcess":8,"tests\\flow\\UsageExampleTest::testExample9FullProcess":8,"tests\\flow\\UsageExampleTest::testExample10MultiHospital":8,"tests\\flow\\UsageExampleTest::testFlowProcessorUsage":8,"tests\\flow\\UsageExampleTest::testFlowProcessorCompleteProcess":8,"tests\\flow\\UsageExampleTest::testFlowProcessorWithConfig":8,"tests\\flow\\BatchConsistencyTest::testSingleMachineBatchConsistency":8,"tests\\flow\\BatchConsistencyTest::testMultiEndoscopeBatchUniqueness":8,"tests\\flow\\BatchConsistencyTest::testBatchNoStability":8,"tests\\flow\\BatchConsistencyTest::testNewProcessGeneratesNewBatchNo":8,"tests\\flow\\BatchConsistencyTest::testDistributedBatchConsistency":8,"tests\\flow\\PerformanceTest::testSingleExecutionPerformance":8,"tests\\flow\\PerformanceTest::testCompleteProcessPerformance":8,"tests\\flow\\PerformanceTest::testEngineCreationPerformance":8,"tests\\flow\\PerformanceTest::testConcurrentProcessingSimulation":8,"tests\\flow\\ProcessEngineTest::testCreateStandardEngine":8,"tests\\flow\\ProcessEngineTest::testCreateNoMorningWashEngine":8,"tests\\flow\\ProcessEngineTest::testCreateSimpleEngine":8,"tests\\flow\\ProcessEngineTest::testCompleteWashProcess":8,"tests\\flow\\ProcessEngineTest::testStandardWashRinseDisinfectFlow":8,"tests\\flow\\ProcessEngineTest::testDisableNode":8,"tests\\flow\\ProcessEngineTest::testGetNodes":8,"tests\\flow\\ProcessEngineTest::testGetEnabledNodes":8,"tests\\flow\\ProcessEngineTest::testUpdateConfig":8,"tests\\flow\\ProcessEngineTest::testMachineWashProcess":8,"tests\\db\\DBC::testDBConnect":8,"tests\\db\\DBC::testGenTables":7,"tests\\db\\DBC::testModel":5,"tests\\flow\\nodes\\DisinfectNodeTest::testNodeIdentity":8,"tests\\flow\\nodes\\DisinfectNodeTest::testCanHandleAfterRinse":8,"tests\\flow\\nodes\\DisinfectNodeTest::testCanHandleAfterFinalRinse":8,"tests\\flow\\nodes\\DisinfectNodeTest::testCannotHandleNonDisinfectReader":8,"tests\\flow\\nodes\\DisinfectNodeTest::testCannotHandleAfterDisinfect":8,"tests\\flow\\nodes\\DisinfectNodeTest::testHandleProcess":8,"tests\\flow\\nodes\\DisinfectNodeTest::testDatabaseOperationFlags":7,"tests\\flow\\nodes\\DisinfectNodeTest::testWebSocketNotifyFlag":8,"tests\\flow\\nodes\\DryNodeTest::testNodeIdentity":8,"tests\\flow\\nodes\\DryNodeTest::testCanHandleAfterFinalRinse":7,"tests\\flow\\nodes\\DryNodeTest::testCannotHandleAfterRinse":8,"tests\\flow\\nodes\\DryNodeTest::testCannotHandleNonDryReader":8,"tests\\flow\\nodes\\DryNodeTest::testCannotHandleAfterDry":7,"tests\\flow\\nodes\\DryNodeTest::testHandleProcess":7,"tests\\flow\\nodes\\DryNodeTest::testDatabaseOperationFlags":7,"tests\\flow\\nodes\\DryNodeTest::testWebSocketNotifyFlag":7,"tests\\flow\\nodes\\DryNodeTest::testKeepExistingBatchNo":8,"tests\\flow\\nodes\\MorningWashNodeTest::testNodeIdentity":8,"tests\\flow\\nodes\\MorningWashNodeTest::testCannotHandleWhenNoNeed":8,"tests\\flow\\nodes\\MorningWashNodeTest::testCannotHandleNonDisinfectMachineReader":8,"tests\\flow\\nodes\\MorningWashNodeTest::testCannotHandleWhenAlreadyWashed":8,"tests\\flow\\nodes\\EndNodeTest::testNodeIdentity":8,"tests\\flow\\nodes\\EndNodeTest::testCanHandleAfterDry":8,"tests\\flow\\nodes\\EndNodeTest::testCanHandleAfterDisinfect":7,"tests\\flow\\nodes\\EndNodeTest::testCanHandleAfterFinalRinse":8,"tests\\flow\\nodes\\EndNodeTest::testCanHandleAfterMachineWash":8,"tests\\flow\\nodes\\EndNodeTest::testCannotHandleNonEndReader":8,"tests\\flow\\nodes\\EndNodeTest::testCannotHandleAfterWash":8,"tests\\flow\\nodes\\EndNodeTest::testCannotHandleAfterRinse":8,"tests\\flow\\nodes\\EndNodeTest::testCannotHandleWithEmptyStep":8,"tests\\flow\\nodes\\EndNodeTest::testHandleProcess":8,"tests\\flow\\nodes\\EndNodeTest::testDatabaseOperationIsUpdate":7,"tests\\flow\\nodes\\EndNodeTest::testWebSocketNotifyFlag":8,"tests\\flow\\nodes\\EndNodeTest::testActionEndTimeIsSet":8,"tests\\flow\\nodes\\FinalRinseNodeTest::testCanHandleAfterDisinfect":8,"tests\\flow\\nodes\\FinalRinseNodeTest::testCanHandleAfterMachineWashByDefault":8,"tests\\flow\\nodes\\FinalRinseNodeTest::testCannotHandleAfterMachineWashWhenDisabled":8,"tests\\flow\\nodes\\FinalRinseNodeTest::testCannotHandleNonFinalRinseReader":8,"tests\\flow\\nodes\\FinalRinseNodeTest::testHandleProcess":8,"tests\\flow\\nodes\\FinalRinseNodeTest::testCannotHandleAfterWash":8,"tests\\flow\\nodes\\FinalRinseNodeTest::testDatabaseOperationFlags":8,"tests\\flow\\nodes\\FinalRinseNodeTest::testWebSocketNotifyFlag":8,"tests\\flow\\nodes\\FinalRinseNodeTest::testNodeIdentity":8,"tests\\flow\\nodes\\MachineWashNodeTest::testNodeIdentity":8,"tests\\flow\\nodes\\MachineWashNodeTest::testCanHandleAfterWash":8,"tests\\flow\\nodes\\MachineWashNodeTest::testCanHandleAfterRinse":7,"tests\\flow\\nodes\\MachineWashNodeTest::testCanHandleAfterDisinfect":7,"tests\\flow\\nodes\\MachineWashNodeTest::testCanHandleWithEmptyStep":7,"tests\\flow\\nodes\\MachineWashNodeTest::testCanHandleAfterEnd":7,"tests\\flow\\nodes\\MachineWashNodeTest::testCanHandleAfterEndoscopeOut":8,"tests\\flow\\nodes\\MachineWashNodeTest::testCannotHandleNonMachineWashReader":8,"tests\\flow\\nodes\\MachineWashNodeTest::testCannotHandleAfterFinalRinse":8,"tests\\flow\\nodes\\MachineWashNodeTest::testCannotHandleAfterDry":8,"tests\\flow\\nodes\\MachineWashNodeTest::testHandleProcess":8,"tests\\flow\\nodes\\MachineWashNodeTest::testDatabaseOperationFlags":7,"tests\\flow\\nodes\\MachineWashNodeTest::testWebSocketNotifyFlag":8,"tests\\flow\\nodes\\RinseNodeTest::testNodeIdentity":8,"tests\\flow\\nodes\\RinseNodeTest::testCanHandleAfterWash":7,"tests\\flow\\nodes\\RinseNodeTest::testCannotHandleNonRinseReader":8,"tests\\flow\\nodes\\RinseNodeTest::testCannotHandleAfterDisinfect":7,"tests\\flow\\nodes\\RinseNodeTest::testCannotHandleWithEmptyStep":7,"tests\\flow\\nodes\\RinseNodeTest::testCannotHandleAfterRinse":7,"tests\\flow\\nodes\\RinseNodeTest::testHandleProcess":7,"tests\\flow\\nodes\\RinseNodeTest::testDatabaseOperationFlags":7,"tests\\flow\\nodes\\RinseNodeTest::testWebSocketNotifyFlag":7,"tests\\flow\\nodes\\RinseNodeTest::testDisabledNodeSkips":8,"tests\\flow\\nodes\\WashNodeTest::testNodeIdentity":8,"tests\\flow\\nodes\\WashNodeTest::testCanHandleWashReader":8,"tests\\flow\\nodes\\WashNodeTest::testCannotHandleNonWashReader":8,"tests\\flow\\nodes\\WashNodeTest::testCanStartNewWashProcess":8,"tests\\flow\\nodes\\WashNodeTest::testCannotWashWithoutMorningWash":8,"tests\\flow\\nodes\\WashNodeTest::testHandleProcess":8,"tests\\flow\\nodes\\WashNodeTest::testGenerateBatchNo":7,"tests\\flow\\nodes\\WashNodeTest::testKeepExistingBatchNo":8,"tests\\flow\\strategies\\TimeValidationStrategyTest::testTimeRequirementNotMet":7,"tests\\flow\\strategies\\TimeValidationStrategyTest::testSetStepDuration":7,"tests\\flow\\FlowProcessorTest::testSuccessfulFlowSavesToDatabase":8,"tests\\flow\\FlowProcessorTest::testEndOperationIsUpdate":8,"tests\\flow\\FlowProcessorTest::testGetActionTypeMapping":8,"tests\\flow\\EdgeCaseTest::testLongEndoscopeName":7,"tests\\flow\\EdgeCaseTest::testSpecialCharactersInName":7,"tests\\flow\\ProcessContextTest::testGetFullVoice":7,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testFullVoiceIncludesEndoscopeName":7,"tests\\flow\\FlowProcessorTest::testSuccessfulFlowSendsVoice":8,"tests\\flow\\FlowProcessorTest::testFailedFlowDoesNotSaveToDatabase":8,"tests\\flow\\FlowProcessorTest::testFailedFlowStillSendsVoice":8,"tests\\flow\\FlowProcessorTest::testWebSocketNotificationSentWhenNeeded":8,"tests\\flow\\FlowProcessorTest::testWebSocketNotificationNotSentWhenNotNeeded":8,"tests\\flow\\FlowProcessorTest::testStaticFactoryCreate":8,"tests\\flow\\EdgeCaseTest::testAllNodesDisabled":8,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testFinalRinseVoice":8,"tests\\flow\\ProcessSimulationTest::testDuplicateCheckNode":7,"tests\\flow\\FullProcessTest::testCompleteManualWashProcess":7,"tests\\flow\\FullProcessTest::testSwipeEndoscopeWithoutOperator":7,"tests\\flow\\cases\\BlockTest::testProcessChainExecution":5,"tests\\flow\\cases\\BlockTest::testDisabledNodeSkipped":5,"tests\\flow\\cases\\BlockTest::testTimeValidationBlockWithBlockModeOn":5,"tests\\flow\\cases\\BlockTest::testTimeValidationWithBlockModeOff":5,"tests\\flow\\cases\\BlockTest::testDuplicateSwipeBlock":5,"tests\\flow\\cases\\BlockTest::testTimeValidationPass":5,"tests\\flow\\cases\\ManualWashTest::testCompleteManualWashProcess":8},"times":{"tests\\flow\\FlowProcessorTest::testCreateProcessor":0.006,"tests\\flow\\FlowProcessorTest::testCreateProcessorWithConfig":0.006,"tests\\flow\\FlowProcessorTest::testStaticFactory":0.006,"tests\\flow\\FlowProcessorTest::testUpdateConfig":0.009,"tests\\flow\\FlowProcessorTest::testBatchNoConsistency":0.006,"tests\\flow\\FlowProcessorTest::testBatchNoConsistencyInCompleteProcess":0.008,"tests\\flow\\FlowProcessorTest::testDatabaseOperationFlags":0.006,"tests\\flow\\FlowProcessorTest::testWebSocketNotifyFlag":0.005,"tests\\flow\\FlowProcessorTest::testVoiceGeneration":0.007,"tests\\flow\\FlowProcessorTest::testErrorVoiceGeneration":0.006,"tests\\flow\\FlowProcessorTest::testMultiHospitalConfig":0.007,"tests\\flow\\FlowProcessorTest::testSpecialHospitalConfig":0.006,"tests\\flow\\ProcessContextTest::testCreateContext":0.005,"tests\\flow\\ProcessContextTest::testSetError":0.006,"tests\\flow\\ProcessContextTest::testSetVoice":0.006,"tests\\flow\\ProcessContextTest::testGetFullVoice":0.006,"tests\\flow\\ProcessContextTest::testStepTimeManagement":0.006,"tests\\flow\\ProcessContextTest::testStepDurationManagement":0.005,"tests\\flow\\ProcessContextTest::testDefaultStepDuration":0.007,"tests\\flow\\ProcessContextTest::testGenerateBatchNo":0.006,"tests\\flow\\ProcessContextTest::testBatchNoUniqueness":0.006,"tests\\flow\\ProcessContextTest::testCanStartNewProcess":0.006,"tests\\flow\\ProcessContextTest::testIsWashProcessCompleted":0.006,"tests\\flow\\ProcessContextTest::testChaining":0.005,"tests\\flow\\ProcessEngineTest::testCreateStandardEngine":0.006,"tests\\flow\\ProcessEngineTest::testCreateNoMorningWashEngine":0.006,"tests\\flow\\ProcessEngineTest::testCreateSimpleEngine":0.006,"tests\\flow\\ProcessEngineTest::testCompleteWashProcess":0.045,"tests\\flow\\ProcessEngineTest::testTimeValidationFailure":0.005,"tests\\flow\\ProcessEngineTest::testWrongStep":0.01,"tests\\flow\\ProcessEngineTest::testDisableNode":0.005,"tests\\flow\\ProcessEngineTest::testGetNodes":0.006,"tests\\flow\\ProcessEngineTest::testGetEnabledNodes":0.005,"tests\\flow\\ProcessEngineTest::testUpdateConfig":0.006,"tests\\flow\\ProcessEngineTest::testMachineWashProcess":0.033,"tests\\flow\\nodes\\FinalRinseNodeTest::testCanHandleAfterDisinfect":0.007,"tests\\flow\\nodes\\FinalRinseNodeTest::testCanHandleAfterMachineWashByDefault":0.005,"tests\\flow\\nodes\\FinalRinseNodeTest::testCannotHandleAfterMachineWashWhenDisabled":0.008,"tests\\flow\\nodes\\FinalRinseNodeTest::testCannotHandleNonFinalRinseReader":0.008,"tests\\flow\\nodes\\FinalRinseNodeTest::testHandleProcess":0.01,"tests\\flow\\nodes\\WashNodeTest::testNodeIdentity":0.006,"tests\\flow\\nodes\\WashNodeTest::testCanHandleWashReader":0.006,"tests\\flow\\nodes\\WashNodeTest::testCannotHandleNonWashReader":0.005,"tests\\flow\\nodes\\WashNodeTest::testCanStartNewWashProcess":0.005,"tests\\flow\\nodes\\WashNodeTest::testCannotWashWithoutMorningWash":0.006,"tests\\flow\\nodes\\WashNodeTest::testHandleProcess":0.009,"tests\\flow\\nodes\\WashNodeTest::testGenerateBatchNo":0.008,"tests\\flow\\nodes\\WashNodeTest::testKeepExistingBatchNo":0.006,"tests\\flow\\strategies\\MorningWashStrategyTest::testNoneMode":0.006,"tests\\flow\\strategies\\MorningWashStrategyTest::testAllMode":0.005,"tests\\flow\\strategies\\MorningWashStrategyTest::testDailyFirstModeWithNoRecords":0.005,"tests\\flow\\strategies\\MorningWashStrategyTest::testDailyFirstModeWithRecords":0.006,"tests\\flow\\strategies\\MorningWashStrategyTest::testSpecificTypesMode":0.005,"tests\\flow\\strategies\\MorningWashStrategyTest::testStorageTimeMode":0.006,"tests\\flow\\strategies\\MorningWashStrategyTest::testStrategyName":0.005,"tests\\flow\\strategies\\TimeValidationStrategyTest::testNoTimeLimitForFirstTime":0.005,"tests\\flow\\strategies\\TimeValidationStrategyTest::testTimeRequirementMet":0.006,"tests\\flow\\strategies\\TimeValidationStrategyTest::testTimeRequirementNotMet":0.005,"tests\\flow\\strategies\\TimeValidationStrategyTest::testCustomDuration":0.006,"tests\\flow\\strategies\\TimeValidationStrategyTest::testIsApplicable":0.006,"tests\\flow\\strategies\\TimeValidationStrategyTest::testStrategyName":0.006,"tests\\flow\\strategies\\TimeValidationStrategyTest::testStrategyPhase":0.005,"tests\\flow\\ProcessEngineTest::testSkipStep":0.012,"tests\\flow\\ProcessEngineTest::testDisinfectAfterWash":0.005,"tests\\flow\\BatchConsistencyTest::testSingleMachineBatchConsistency":0.046,"tests\\flow\\BatchConsistencyTest::testMultiEndoscopeBatchUniqueness":0.036,"tests\\flow\\BatchConsistencyTest::testBatchNoFormat":0.008,"tests\\flow\\BatchConsistencyTest::testBatchNoStability":0.02,"tests\\flow\\BatchConsistencyTest::testNewProcessGeneratesNewBatchNo":0.024,"tests\\flow\\BatchConsistencyTest::testDistributedBatchConsistency":0.057,"tests\\flow\\BatchConsistencyTest::testBatchNoPreservedOnError":0.024,"tests\\flow\\BatchConsistencyTest::testBatchNoWithDifferentProcessTypes":0.022,"tests\\flow\\EdgeCaseTest::testEmptyContext":0.005,"tests\\flow\\EdgeCaseTest::testLongEndoscopeName":0.006,"tests\\flow\\EdgeCaseTest::testSpecialCharactersInName":0.006,"tests\\flow\\EdgeCaseTest::testBatchNoGenerationEdgeCases":0.006,"tests\\flow\\EdgeCaseTest::testTimeBoundaryValues":0.006,"tests\\flow\\EdgeCaseTest::testEmptyChain":0.005,"tests\\flow\\EdgeCaseTest::testAllNodesDisabled":0.021,"tests\\flow\\EdgeCaseTest::testRepeatedStepTimeSetting":0.006,"tests\\flow\\EdgeCaseTest::testChainingEdgeCases":0.006,"tests\\flow\\EdgeCaseTest::testErrorStateOverwrite":0.006,"tests\\flow\\EdgeCaseTest::testRecoveryFromError":0.005,"tests\\flow\\EdgeCaseTest::testInvalidConfigItems":0.007,"tests\\flow\\EdgeCaseTest::testConcurrentBatchNoGeneration":0.011,"tests\\flow\\EdgeCaseTest::testMorningWashBoundaryTime":0.006,"tests\\flow\\EdgeCaseTest::testStorageTimeBoundary":0.007,"tests\\flow\\EdgeCaseTest::testStorageTimeOverThreshold":0.005,"tests\\flow\\PerformanceTest::testSingleExecutionPerformance":0.011,"tests\\flow\\PerformanceTest::testCompleteProcessPerformance":0.039,"tests\\flow\\PerformanceTest::testBatchNoGenerationPerformance":0.023,"tests\\flow\\PerformanceTest::testEngineCreationPerformance":0.094,"tests\\flow\\PerformanceTest::testConfigLoadingPerformance":0.023,"tests\\flow\\PerformanceTest::testContextCreationPerformance":0.258,"tests\\flow\\PerformanceTest::testStrategyExecutionPerformance":0.156,"tests\\flow\\PerformanceTest::testMemoryUsage":0.226,"tests\\flow\\PerformanceTest::testConcurrentProcessingSimulation":0.058,"tests\\flow\\PerformanceTest::testChainTraversalPerformance":5.796,"tests\\flow\\config\\ProcessConfigTest::testCreateDefaultConfig":0.007,"tests\\flow\\config\\ProcessConfigTest::testFromArray":0.006,"tests\\flow\\config\\ProcessConfigTest::testGetEnabledSteps":0.007,"tests\\flow\\config\\ProcessConfigTest::testAddStep":0.006,"tests\\flow\\config\\ProcessConfigTest::testRemoveStep":0.005,"tests\\flow\\config\\ProcessConfigTest::testSetNodeEnabled":0.008,"tests\\flow\\config\\ProcessConfigTest::testMorningWashConfig":0.006,"tests\\flow\\config\\ProcessConfigTest::testTimeValidationConfig":0.007,"tests\\flow\\config\\ProcessConfigTest::testVoiceTemplateConfig":0.005,"tests\\flow\\config\\ProcessConfigTest::testSetStepVoice":0.006,"tests\\flow\\config\\ProcessConfigTest::testToArray":0.007,"tests\\flow\\config\\ProcessConfigTest::testCreateStandard":0.006,"tests\\flow\\config\\ProcessConfigTest::testCreateNoMorningWash":0.007,"tests\\flow\\config\\ProcessConfigTest::testCreateSimple":0.006,"tests\\flow\\config\\ProcessConfigTest::testCreateMachineWash":0.005,"tests\\flow\\config\\ProcessConfigTest::testCreateNoDry":0.006,"tests\\flow\\config\\ProcessConfigTest::testCreateDryOnly":0.005,"tests\\flow\\config\\ProcessConfigTest::testChaining":0.006,"tests\\flow\\nodes\\DisinfectNodeTest::testNodeIdentity":0.005,"tests\\flow\\nodes\\DisinfectNodeTest::testCanHandleAfterRinse":0.005,"tests\\flow\\nodes\\DisinfectNodeTest::testCanHandleAfterFinalRinse":0.005,"tests\\flow\\nodes\\DisinfectNodeTest::testCanHandleAfterWash":0.005,"tests\\flow\\nodes\\DisinfectNodeTest::testCannotHandleNonDisinfectReader":0.005,"tests\\flow\\nodes\\DisinfectNodeTest::testCannotHandleAfterDisinfect":0.005,"tests\\flow\\nodes\\DisinfectNodeTest::testHandleProcess":0.009,"tests\\flow\\nodes\\DisinfectNodeTest::testDatabaseOperationFlags":0.008,"tests\\flow\\nodes\\DisinfectNodeTest::testWebSocketNotifyFlag":0.008,"tests\\flow\\nodes\\MorningWashNodeTest::testNodeIdentity":0.005,"tests\\flow\\nodes\\MorningWashNodeTest::testCanHandleMorningWashReader":0.021,"tests\\flow\\nodes\\MorningWashNodeTest::testCannotHandleWhenNoNeed":0.006,"tests\\flow\\nodes\\MorningWashNodeTest::testCannotHandleNonMorningWashReader":0.005,"tests\\flow\\nodes\\MorningWashNodeTest::testCannotHandleWhenAlreadyWashed":0.005,"tests\\flow\\nodes\\MorningWashNodeTest::testHandleProcess":0.008,"tests\\flow\\nodes\\MorningWashNodeTest::testGenerateBatchNo":0.009,"tests\\flow\\nodes\\MorningWashNodeTest::testCanHandleDisinfectReaderForMorningWash":0.006,"tests\\flow\\nodes\\MorningWashNodeTest::testCanHandleMachineWashReaderForMorningWash":0.005,"tests\\flow\\nodes\\MorningWashNodeTest::testCannotHandleNonDisinfectMachineReader":0.006,"tests\\flow\\nodes\\MorningWashNodeTest::testHandleProcessWithDisinfect":0.009,"tests\\flow\\nodes\\MorningWashNodeTest::testHandleProcessWithMachineWash":0.009,"tests\\flow\\nodes\\DryNodeTest::testNodeIdentity":0.006,"tests\\flow\\nodes\\DryNodeTest::testCanHandleAfterFinalRinse":0.007,"tests\\flow\\nodes\\DryNodeTest::testCanHandleAfterDisinfect":0.006,"tests\\flow\\nodes\\DryNodeTest::testCannotHandleAfterRinse":0.005,"tests\\flow\\nodes\\DryNodeTest::testCannotHandleNonDryReader":0.007,"tests\\flow\\nodes\\DryNodeTest::testCannotHandleAfterDry":0.006,"tests\\flow\\nodes\\DryNodeTest::testHandleProcess":0.008,"tests\\flow\\nodes\\DryNodeTest::testDatabaseOperationFlags":0.009,"tests\\flow\\nodes\\DryNodeTest::testWebSocketNotifyFlag":0.008,"tests\\flow\\nodes\\DryNodeTest::testKeepExistingBatchNo":0.01,"tests\\flow\\ProcessEngineTest::testStandardWashRinseDisinfectFlow":0.023,"tests\\flow\\UsageExampleTest::testExample1StandardProcess":0.022,"tests\\flow\\UsageExampleTest::testExample2NoMorningWash":0.025,"tests\\flow\\UsageExampleTest::testExample3DynamicAdjust":0.048,"tests\\flow\\UsageExampleTest::testExample4CustomVoice":0.022,"tests\\flow\\UsageExampleTest::testExample5MorningWashModes":0.033,"tests\\flow\\UsageExampleTest::testExample6MachineWash":0.022,"tests\\flow\\UsageExampleTest::testExample7SimpleProcess":0.021,"tests\\flow\\UsageExampleTest::testExample8TimeValidation":0.021,"tests\\flow\\UsageExampleTest::testExample9FullProcess":0.021,"tests\\flow\\UsageExampleTest::testExample10MultiHospital":0.046,"tests\\flow\\UsageExampleTest::testLoadCustomProcessFromConfig":0.007,"tests\\flow\\UsageExampleTest::testCreateNoMorningWashFromConfig":0.042,"tests\\flow\\UsageExampleTest::testCreateMachineWashFromConfig":0.036,"tests\\flow\\UsageExampleTest::testCreateYiwuModeFromConfig":0.015,"tests\\flow\\UsageExampleTest::testFlowProcessorUsage":0.045,"tests\\flow\\UsageExampleTest::testFlowProcessorCompleteProcess":0.021,"tests\\flow\\UsageExampleTest::testFlowProcessorWithConfig":0.037,"tests\\db\\DBC::testDBConnect":0.113,"tests\\db\\DBC::testGenTables":0.416,"tests\\db\\DBC::testModel":0.171,"tests\\flow\\nodes\\EndNodeTest::testNodeIdentity":0.006,"tests\\flow\\nodes\\EndNodeTest::testCanHandleAfterDry":0.006,"tests\\flow\\nodes\\EndNodeTest::testCanHandleAfterDisinfect":0.005,"tests\\flow\\nodes\\EndNodeTest::testCanHandleAfterFinalRinse":0.005,"tests\\flow\\nodes\\EndNodeTest::testCanHandleAfterMachineWash":0.005,"tests\\flow\\nodes\\EndNodeTest::testCannotHandleNonEndReader":0.005,"tests\\flow\\nodes\\EndNodeTest::testCannotHandleAfterWash":0.007,"tests\\flow\\nodes\\EndNodeTest::testCannotHandleAfterRinse":0.006,"tests\\flow\\nodes\\EndNodeTest::testCannotHandleWithEmptyStep":0.005,"tests\\flow\\nodes\\EndNodeTest::testHandleProcess":0.009,"tests\\flow\\nodes\\EndNodeTest::testDatabaseOperationIsUpdate":0.011,"tests\\flow\\nodes\\EndNodeTest::testWebSocketNotifyFlag":0.009,"tests\\flow\\nodes\\EndNodeTest::testActionEndTimeIsSet":0.009,"tests\\flow\\nodes\\FinalRinseNodeTest::testCannotHandleAfterWash":0.006,"tests\\flow\\nodes\\FinalRinseNodeTest::testDatabaseOperationFlags":0.008,"tests\\flow\\nodes\\FinalRinseNodeTest::testWebSocketNotifyFlag":0.009,"tests\\flow\\nodes\\FinalRinseNodeTest::testNodeIdentity":0.005,"tests\\flow\\nodes\\MachineWashNodeTest::testNodeIdentity":0.005,"tests\\flow\\nodes\\MachineWashNodeTest::testCanHandleAfterWash":0.005,"tests\\flow\\nodes\\MachineWashNodeTest::testCanHandleAfterRinse":0.005,"tests\\flow\\nodes\\MachineWashNodeTest::testCanHandleAfterDisinfect":0.005,"tests\\flow\\nodes\\MachineWashNodeTest::testCanHandleWithEmptyStep":0.006,"tests\\flow\\nodes\\MachineWashNodeTest::testCanHandleAfterEnd":0.006,"tests\\flow\\nodes\\MachineWashNodeTest::testCanHandleAfterEndoscopeOut":0.005,"tests\\flow\\nodes\\MachineWashNodeTest::testCannotHandleNonMachineWashReader":0.005,"tests\\flow\\nodes\\MachineWashNodeTest::testCannotHandleAfterFinalRinse":0.005,"tests\\flow\\nodes\\MachineWashNodeTest::testCannotHandleAfterDry":0.006,"tests\\flow\\nodes\\MachineWashNodeTest::testHandleProcess":0.008,"tests\\flow\\nodes\\MachineWashNodeTest::testDatabaseOperationFlags":0.01,"tests\\flow\\nodes\\MachineWashNodeTest::testWebSocketNotifyFlag":0.008,"tests\\flow\\nodes\\RinseNodeTest::testNodeIdentity":0.005,"tests\\flow\\nodes\\RinseNodeTest::testCanHandleAfterWash":0.007,"tests\\flow\\nodes\\RinseNodeTest::testCannotHandleNonRinseReader":0.007,"tests\\flow\\nodes\\RinseNodeTest::testCannotHandleAfterDisinfect":0.006,"tests\\flow\\nodes\\RinseNodeTest::testCannotHandleWithEmptyStep":0.006,"tests\\flow\\nodes\\RinseNodeTest::testCannotHandleAfterRinse":0.005,"tests\\flow\\nodes\\RinseNodeTest::testHandleProcess":0.01,"tests\\flow\\nodes\\RinseNodeTest::testDatabaseOperationFlags":0.009,"tests\\flow\\nodes\\RinseNodeTest::testWebSocketNotifyFlag":0.009,"tests\\flow\\nodes\\RinseNodeTest::testDisabledNodeSkips":0.009,"tests\\flow\\strategies\\TimeValidationStrategyTest::testNonDurationStepIsSkipped":0.008,"tests\\flow\\strategies\\TimeValidationStrategyTest::testSetStepDuration":0.006,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testStrategyName":0.007,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testStrategyPhaseIsAfter":0.006,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testIsAlwaysApplicable":0.007,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testNormalWashVoice":0.005,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testNormalRinseVoice":0.005,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testNormalDisinfectVoice":0.005,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testFinalRinseVoice":0.006,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testDryVoice":0.006,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testEndVoice":0.005,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testMachineWashVoice":0.007,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testMorningWashVoice":0.005,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testMachineMorningWashVoice":0.006,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testErrorStateGeneratesErrorVoice":0.006,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testFullVoiceIncludesEndoscopeName":0.006,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testLeakTestRemindAppended":0.006,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testStorageRemindAppended":0.005,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testCustomVoiceTemplate":0.005,"tests\\flow\\strategies\\VoiceGenerationStrategyTest::testSetStepVoice":0.005,"tests\\flow\\FlowProcessorTest::testSuccessfulFlowSavesToDatabase":0.011,"tests\\flow\\FlowProcessorTest::testSuccessfulFlowSendsVoice":0.008,"tests\\flow\\FlowProcessorTest::testFailedFlowDoesNotSaveToDatabase":0.007,"tests\\flow\\FlowProcessorTest::testFailedFlowStillSendsVoice":0.008,"tests\\flow\\FlowProcessorTest::testWebSocketNotificationSentWhenNeeded":0.01,"tests\\flow\\FlowProcessorTest::testWebSocketNotificationNotSentWhenNotNeeded":0.008,"tests\\flow\\FlowProcessorTest::testEndOperationIsUpdate":0.01,"tests\\flow\\FlowProcessorTest::testGetActionTypeMapping":0.107,"tests\\flow\\FlowProcessorTest::testStaticFactoryCreate":0.007,"tests\\flow\\ProcessSimulationTest::testBasicManualWashWithoutDatabase":0.287,"tests\\flow\\ProcessSimulationTest::testNodeChainConstruction":0.264,"tests\\flow\\ProcessSimulationTest::testMorningWashNodeConfiguration":0.278,"tests\\flow\\ProcessSimulationTest::testMachineWashNodeConfiguration":0.271,"tests\\flow\\ProcessSimulationTest::testStorageNodeConfiguration":0.273,"tests\\flow\\ProcessSimulationTest::testDuplicateCheckNode":0.522,"tests\\flow\\ProcessSimulationTest::testCloseNode":0.268,"tests\\flow\\ProcessSimulationTest::testNodeEnableDisable":0.268,"tests\\flow\\ProcessSimulationTest::testConfigUpdate":0.522,"tests\\flow\\ProcessSimulationTest::testStrategyInitialization":0.291,"tests\\flow\\FullProcessTest::testCompleteManualWashProcess":0.09,"tests\\flow\\FullProcessTest::testCompleteManualWashProcessUsingHelper":0.285,"tests\\flow\\FullProcessTest::testMorningWashRequired":0.262,"tests\\flow\\FullProcessTest::testManualWashAfterMorningWash":0.269,"tests\\flow\\FullProcessTest::testMachineWashProcess":0.281,"tests\\flow\\FullProcessTest::testMachineWashProcessUsingHelper":0.286,"tests\\flow\\FullProcessTest::testMorningWashThenMachineWash":0.275,"tests\\flow\\FullProcessTest::testSwipeEndoscopeWithoutOperator":0.02,"tests\\flow\\FullProcessTest::testWrongStepOrder":0.078,"tests\\flow\\FullProcessTest::testDuplicateSwipe":0.142,"tests\\flow\\FullProcessTest::testUnboundReader":0.273,"tests\\flow\\FullProcessTest::testUnboundEndoscope":0.323,"tests\\flow\\FullProcessTest::testDifferentOperatorContinueProcess":0.124,"tests\\flow\\FullProcessTest::testWashStepVoice":0.074,"tests\\flow\\FullProcessTest::testDisinfectStepVoice":0.182,"tests\\flow\\FullProcessTest::testOperatorCardVoice":0.02,"tests\\flow\\FullProcessTest::testErrorVoice":0.021,"tests\\flow\\FullProcessTest::testVoiceTemplateParams":0.077,"tests\\flow\\FullProcessTest::testProcessChainExecution":0.236,"tests\\flow\\FullProcessTest::testDisabledNodeSkipped":0.277,"tests\\flow\\FullProcessTest::testTimeValidationBlock":0.265,"tests\\flow\\FullProcessTest::testDuplicateSwipeBlock":0.201,"tests\\flow\\FullProcessTest::testNewProcessAfterComplete":0.331,"tests\\flow\\FullProcessTest::testConcurrentProcessIsolation":0.174,"tests\\flow\\FullProcessTest::testProcessorReset":0.081,"tests\\flow\\FullProcessTest::testVirtualContextBuilder":0.017,"tests\\flow\\FullProcessTest::testEnvironmentConfig":0.007,"tests\\flow\\Test::testExample":0.005,"tests\\flow\\cases\\BlockTest::testProcessChainExecution":0.239,"tests\\flow\\cases\\BlockTest::testDisabledNodeSkipped":0.288,"tests\\flow\\cases\\BlockTest::testTimeValidationBlockWithBlockModeOn":0.263,"tests\\flow\\cases\\BlockTest::testTimeValidationWithBlockModeOff":0.282,"tests\\flow\\cases\\BlockTest::testDuplicateSwipeBlock":0.193,"tests\\flow\\cases\\BlockTest::testTimeValidationPass":0.281,"tests\\flow\\cases\\ErrorFlowTest::testSwipeEndoscopeWithoutOperator":0.008,"tests\\flow\\cases\\ErrorFlowTest::testWrongStepOrder":0.069,"tests\\flow\\cases\\ErrorFlowTest::testDuplicateSwipe":0.109,"tests\\flow\\cases\\ErrorFlowTest::testUnboundReader":0.276,"tests\\flow\\cases\\ErrorFlowTest::testUnboundEndoscope":0.27,"tests\\flow\\cases\\HelperTest::testProcessorReset":0.063,"tests\\flow\\cases\\HelperTest::testVirtualContextBuilder":0.007,"tests\\flow\\cases\\HelperTest::testEnvironmentConfig":0.006,"tests\\flow\\cases\\MachineWashTest::testMachineWashProcess":0.06,"tests\\flow\\cases\\MachineWashTest::testMachineWashProcessUsingHelper":0.06,"tests\\flow\\cases\\MachineWashTest::testMorningWashThenMachineWash":0.059,"tests\\flow\\cases\\MachineWashTest::testMultipleEndoscopeMachineWash":0.112,"tests\\flow\\cases\\ManualWashTest::testCompleteManualWashProcess":15.531,"tests\\flow\\cases\\ManualWashTest::testCompleteManualWashProcessUsingHelper":0.436,"tests\\flow\\cases\\ManualWashTest::testMorningWashRequired":0.316,"tests\\flow\\cases\\ManualWashTest::testManualWashAfterMorningWash":0.403,"tests\\flow\\cases\\ManualWashTest::testDifferentOperatorContinueProcess":0.138,"tests\\flow\\cases\\ManualWashTest::testNewProcessAfterComplete":0.443,"tests\\flow\\cases\\ManualWashTest::testConcurrentProcessIsolation":0.201,"tests\\flow\\cases\\VoiceTest::testWashStepVoice":0.064,"tests\\flow\\cases\\VoiceTest::testDisinfectStepVoice":0.2,"tests\\flow\\cases\\VoiceTest::testOperatorCardVoice":0.008,"tests\\flow\\cases\\VoiceTest::testErrorVoice":0.008,"tests\\flow\\cases\\VoiceTest::testWashDurationInsufficientVoice":0.276}} \ No newline at end of file diff --git a/app/config/custom_process_config.php b/app/config/custom_process_config.php index 58436a4..c3ab501 100644 --- a/app/config/custom_process_config.php +++ b/app/config/custom_process_config.php @@ -1,60 +1,111 @@ [ + 'name' => '机洗单步骤流程', + 'description' => '只刷机洗后自动完成终末漂洗、干燥、结束', + 'override_steps' => true, + + // 晨洗配置:禁用 + 'morning_wash' => [ + 'mode' => 'none', // none | all | daily_first | storage_time | specific_types + ], + + // 步骤配置 + 'steps' => [ + ['code' => '晨洗', 'class' => 'MorningWashNode', 'enabled' => false], + ['code' => '清洗', 'class' => 'WashNode', 'enabled' => true, 'required' => ['结束']], + ['code' => '漂洗', 'class' => 'RinseNode', 'enabled' => true, 'required' => ['清洗']], + ['code' => '消毒', 'class' => 'DisinfectNode', 'enabled' => true, 'required' => ['漂洗']], + ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true, 'required' => ['清洗', '漂洗', '消毒']], + ['code' => '终末漂洗', 'class' => 'FinalRinseNode', 'enabled' => true, 'required' => ['机洗']], + ['code' => '干燥', 'class' => 'DryNode', 'enabled' => true, 'required' => ['终末漂洗']], + ['code' => '结束', 'class' => 'EndNode', 'enabled' => true, 'required' => ['干燥']], + ], + ], + + // ============================================ + // 2. 标准完整流程 + // ============================================ + // 场景:完整的七步洗消流程 + // 流程:晨洗 → 清洗 → 漂洗 → 消毒 → 终末漂洗 → 干燥 → 结束 + // 特点:符合 WS 507-2016 规范,包含所有必需步骤 // ============================================ 'standard' => [ 'name' => '标准完整流程', - 'description' => '包含所有步骤的完整清洗流程', - // 覆盖 steps - 'override_steps' => false, + 'description' => '包含晨洗在内的完整七步洗消流程', + 'override_steps' => true, + + // 晨洗配置:每天第一次需要晨洗 'morning_wash' => [ - 'mode' => 'daily_first', // 每天第一次需要晨洗 - 'storage_threshold' => 4, - 'morning_start_time' => '00:00:00', + 'mode' => 'daily_first', // 每天第一次洗消需要晨洗 + 'storage_threshold' => 4, // 存储时间阈值(小时) + 'morning_start_time' => '00:00:00', // 每日开始时间 ], + + // 步骤配置 'steps' => [ ['code' => '晨洗', 'class' => 'MorningWashNode', 'enabled' => true], - ['code' => '清洗', 'class' => 'WashNode', 'enabled' => true,'required' => ['结束','晨洗']], - ['code' => '漂洗', 'class' => 'RinseNode', 'enabled' => true,'required' => ['结束']], + ['code' => '清洗', 'class' => 'WashNode', 'enabled' => true], + ['code' => '漂洗', 'class' => 'RinseNode', 'enabled' => true], ['code' => '消毒', 'class' => 'DisinfectNode', 'enabled' => true], ['code' => '终末漂洗', 'class' => 'FinalRinseNode', 'enabled' => true], ['code' => '干燥', 'class' => 'DryNode', 'enabled' => true], ['code' => '结束', 'class' => 'EndNode', 'enabled' => true], ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true], ], + + // 默认时间验证配置(秒) 'time_validation' => [ - '手工洗' => [ - '清洗' => 120, - '漂洗' => 60, - '消毒' => 300, - '终末漂洗' => 120, - '干燥' => 30, - '机洗' => 360, - ] + 'durations' => [ + '清洗' => 120, // 2 分钟 + '漂洗' => 60, // 1 分钟 + '消毒' => 300, // 5 分钟 + '终末漂洗' => 120, // 2 分钟 + '干燥' => 30, // 30 秒 + '机洗' => 360, // 6 分钟 + ], ], ], - + // ============================================ - // 示例2: 无晨洗流程 - // 医院不需要晨洗功能 - // 清洗→漂洗→消毒→终末漂洗→干燥→结束 + // 3. 无晨洗流程 + // ============================================ + // 场景:不需要晨洗功能的医院 + // 流程:清洗 → 漂洗 → 消毒 → 终末漂洗 → 干燥 → 结束 + // 特点:完全禁用晨洗节点 // ============================================ 'no_morning_wash' => [ 'name' => '无晨洗流程', - 'description' => '医院不需要晨洗功能', + 'description' => '不包含晨洗步骤的标准洗消流程', + 'override_steps' => true, + + // 晨洗配置:完全禁用 'morning_wash' => [ - 'mode' => 'none', // 完全禁用晨洗 + 'mode' => 'none', ], + + // 步骤配置 'steps' => [ - ['code' => '晨洗', 'class' => 'MorningWashNode', 'enabled' => false], // 禁用晨洗节点 + ['code' => '晨洗', 'class' => 'MorningWashNode', 'enabled' => false], ['code' => '清洗', 'class' => 'WashNode', 'enabled' => true], ['code' => '漂洗', 'class' => 'RinseNode', 'enabled' => true], ['code' => '消毒', 'class' => 'DisinfectNode', 'enabled' => true], @@ -64,17 +115,48 @@ return [ ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true], ], ], - + // ============================================ - // 示例3: 部分镜子需要晨洗(义乌模式) - // 根据存储时间判断:超过4小时需要晨洗 + // 4. 晨洗配置(多种模式) // ============================================ - 'partial_morning_wash' => [ - 'name' => '部分镜子晨洗(义乌模式)', - 'description' => '普通镜柜超过4小时需要晨洗,无菌镜柜免晨消', + // 场景:根据不同医院政策配置晨洗规则 + // 子配置: + // - morning_wash_daily: 每天第一次 + // - morning_wash_storage: 存储超时 + // - morning_wash_all: 每次都晨洗 + // - morning_wash_specific: 特定类型镜子 + // ============================================ + + // 4.1 每天第一次晨洗(忠县模式) + 'morning_wash_daily' => [ + 'name' => '每天第一次晨洗', + 'description' => '每日首次洗消需要晨洗', + 'override_steps' => true, 'morning_wash' => [ - 'mode' => 'storage_time', // 根据存储时间判断 - 'storage_threshold' => 4, // 4小时阈值 + 'mode' => 'daily_first', + 'storage_threshold' => 4, + 'morning_start_time' => '00:00:00', + ], + 'steps' => [ + ['code' => '晨洗', 'class' => 'MorningWashNode', 'enabled' => true], + ['code' => '清洗', 'class' => 'WashNode', 'enabled' => true], + ['code' => '漂洗', 'class' => 'RinseNode', 'enabled' => true], + ['code' => '消毒', 'class' => 'DisinfectNode', 'enabled' => true], + ['code' => '终末漂洗', 'class' => 'FinalRinseNode', 'enabled' => true], + ['code' => '干燥', 'class' => 'DryNode', 'enabled' => true], + ['code' => '结束', 'class' => 'EndNode', 'enabled' => true], + ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true], + ], + ], + + // 4.2 存储超时晨洗(义乌模式) + 'morning_wash_storage' => [ + 'name' => '存储超时晨洗(义乌模式)', + 'description' => '普通镜柜超过阈值小时需要晨洗,无菌镜柜免晨消', + 'override_steps' => true, + 'morning_wash' => [ + 'mode' => 'storage_time', + 'storage_threshold' => 4, // 4 小时阈值 'morning_start_time' => '06:00:00', ], 'steps' => [ @@ -88,107 +170,14 @@ return [ ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true], ], ], - - // ============================================ - // 示例4: 无干燥流程 - // 医院不需要干燥步骤,消毒后直接结束 - // 清洗→漂洗→消毒→结束 - // ============================================ - 'no_dry' => [ - 'name' => '无干燥流程', - 'description' => '医院不需要干燥步骤', + + // 4.3 每次都晨洗(高感染风险区) + 'morning_wash_all' => [ + 'name' => '每次都晨洗', + 'description' => '每次洗消都需要先进行晨洗', + 'override_steps' => true, 'morning_wash' => [ - 'mode' => 'none', - ], - 'steps' => [ - ['code' => '晨洗', 'class' => 'MorningWashNode', 'enabled' => false], - ['code' => '清洗', 'class' => 'WashNode', 'enabled' => true], - ['code' => '漂洗', 'class' => 'RinseNode', 'enabled' => true], - ['code' => '消毒', 'class' => 'DisinfectNode', 'enabled' => true], - ['code' => '终末漂洗', 'class' => 'FinalRinseNode', 'enabled' => false], // 跳过终末漂洗 - ['code' => '干燥', 'class' => 'DryNode', 'enabled' => false], // 禁用干燥 - ['code' => '结束', 'class' => 'EndNode', 'enabled' => true], - ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true], - ], - ], - - // ============================================ - // 示例5: 仅干燥流程 - // 医院只有干燥步骤,没有完整清洗流程 - // 干燥→结束 - // ============================================ - 'dry_only' => [ - 'name' => '仅干燥流程', - 'description' => '医院只有干燥步骤', - 'morning_wash' => [ - 'mode' => 'none', - ], - 'steps' => [ - ['code' => '晨洗', 'class' => 'MorningWashNode', 'enabled' => false], - ['code' => '清洗', 'class' => 'WashNode', 'enabled' => false], - ['code' => '漂洗', 'class' => 'RinseNode', 'enabled' => false], - ['code' => '消毒', 'class' => 'DisinfectNode', 'enabled' => false], - ['code' => '终末漂洗', 'class' => 'FinalRinseNode', 'enabled' => false], - ['code' => '干燥', 'class' => 'DryNode', 'enabled' => true], - ['code' => '结束', 'class' => 'EndNode', 'enabled' => true], - ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => false], - ], - ], - - // ============================================ - // 示例6: 机洗流程 - // 清洗→机洗→终末漂洗→干燥→结束 - // ============================================ - 'machine_wash' => [ - 'name' => '机洗流程', - 'description' => '刷完机洗后接终末漂洗再干燥结束', - 'morning_wash' => [ - 'mode' => 'none', - ], - 'steps' => [ - ['code' => '晨洗', 'class' => 'MorningWashNode', 'enabled' => false], - ['code' => '清洗', 'class' => 'WashNode', 'enabled' => true], - ['code' => '漂洗', 'class' => 'RinseNode', 'enabled' => false], // 跳过硬洗漂洗 - ['code' => '消毒', 'class' => 'DisinfectNode', 'enabled' => false], // 跳过硬洗消毒 - ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true], - ['code' => '终末漂洗', 'class' => 'FinalRinseNode', 'enabled' => true], - ['code' => '干燥', 'class' => 'DryNode', 'enabled' => true], - ['code' => '结束', 'class' => 'EndNode', 'enabled' => true], - ], - ], - - // ============================================ - // 示例7: 简化流程 - // 只刷一个清洗就结束 - // 清洗→结束 - // ============================================ - 'simple' => [ - 'name' => '简化流程', - 'description' => '只刷一个清洗就结束', - 'morning_wash' => [ - 'mode' => 'none', - ], - 'steps' => [ - ['code' => '晨洗', 'class' => 'MorningWashNode', 'enabled' => false], - ['code' => '清洗', 'class' => 'WashNode', 'enabled' => true], - ['code' => '漂洗', 'class' => 'RinseNode', 'enabled' => false], - ['code' => '消毒', 'class' => 'DisinfectNode', 'enabled' => false], - ['code' => '终末漂洗', 'class' => 'FinalRinseNode', 'enabled' => false], - ['code' => '干燥', 'class' => 'DryNode', 'enabled' => false], - ['code' => '结束', 'class' => 'EndNode', 'enabled' => true], - ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => false], - ], - ], - - // ============================================ - // 示例8: 自定义语音流程 - // 针对某个流程特殊定制语音 - // ============================================ - 'custom_voice' => [ - 'name' => '自定义语音流程', - 'description' => '针对流程特殊定制语音', - 'morning_wash' => [ - 'mode' => 'daily_first', + 'mode' => 'all', ], 'steps' => [ ['code' => '晨洗', 'class' => 'MorningWashNode', 'enabled' => true], @@ -200,54 +189,118 @@ return [ ['code' => '结束', 'class' => 'EndNode', 'enabled' => true], ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true], ], - 'voice_templates' => [ - // 与 VoiceGenerationStrategy 中默认配置进行合并 - 'normal_wash' => [ - '清洗' => '第一步清洗开始,请认真清洗', - '漂洗' => '第二步漂洗开始', - '消毒' => '第三步消毒开始,请确保消毒时间', - '终末漂洗' => '第四步终末漂洗开始', - '干燥' => '最后一步干燥开始', - '结束' => '清洗流程全部完成,请妥善保管内镜', + ], + + // 4.4 特定类型镜子晨洗 + 'morning_wash_specific' => [ + 'name' => '特定类型镜子晨洗', + 'description' => '只有胃镜、十二指肠镜需要晨洗', + 'override_steps' => true, + 'morning_wash' => [ + 'mode' => 'specific_types', + 'specific_types' => ['胃镜', '十二指肠镜'], + ], + 'steps' => [ + ['code' => '晨洗', 'class' => 'MorningWashNode', 'enabled' => true], + ['code' => '清洗', 'class' => 'WashNode', 'enabled' => true], + ['code' => '漂洗', 'class' => 'RinseNode', 'enabled' => true], + ['code' => '消毒', 'class' => 'DisinfectNode', 'enabled' => true], + ['code' => '终末漂洗', 'class' => 'FinalRinseNode', 'enabled' => true], + ['code' => '干燥', 'class' => 'DryNode', 'enabled' => true], + ['code' => '结束', 'class' => 'EndNode', 'enabled' => true], + ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true], + ], + ], + + // ============================================ + // 5. 有干燥流程(默认) + // ============================================ + // 场景:包含干燥步骤的完整流程 + // 流程:晨洗 → 清洗 → 漂洗 → 消毒 → 终末漂洗 → 干燥 → 结束 + // 特点:干燥节点启用,符合规范要求 + // ============================================ + 'with_dry' => [ + 'name' => '有干燥流程', + 'description' => '包含干燥步骤的完整洗消流程', + 'override_steps' => true, + + // 晨洗配置:每天第一次 + 'morning_wash' => [ + 'mode' => 'daily_first', + ], + + // 步骤配置:干燥启用 + 'steps' => [ + ['code' => '晨洗', 'class' => 'MorningWashNode', 'enabled' => true], + ['code' => '清洗', 'class' => 'WashNode', 'enabled' => true], + ['code' => '漂洗', 'class' => 'RinseNode', 'enabled' => true], + ['code' => '消毒', 'class' => 'DisinfectNode', 'enabled' => true], + ['code' => '终末漂洗', 'class' => 'FinalRinseNode', 'enabled' => true], + ['code' => '干燥', 'class' => 'DryNode', 'enabled' => true], // ✓ 启用干燥 + ['code' => '结束', 'class' => 'EndNode', 'enabled' => true], + ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true], + ], + + // 时间验证 + 'time_validation' => [ + 'durations' => [ + '清洗' => 120, + '漂洗' => 60, + '消毒' => 300, + '终末漂洗' => 120, + '干燥' => 30, // 干燥至少 30 秒 + '机洗' => 360, ], ], ], - + // ============================================ - // 示例9: 特定类型镜子晨洗 - // 只有特定类型的镜子需要晨洗 + // 6. 无干燥流程 // ============================================ - 'specific_type_morning_wash' => [ - 'name' => '特定类型镜子晨洗', - 'description' => '只有胃镜需要晨洗,肠镜不需要', + // 场景:医院不需要干燥步骤 + // 流程:晨洗 → 清洗 → 漂洗 → 消毒 → 终末漂洗 → 结束 + // 特点:禁用干燥和终末漂洗 + // ============================================ + 'no_dry' => [ + 'name' => '无干燥流程', + 'description' => '不包含干燥步骤的洗消流程', + 'override_steps' => true, + + // 晨洗配置:禁用 'morning_wash' => [ - 'mode' => 'specific_types', - 'specific_types' => ['胃镜', '十二指肠镜'], // 只有这些类型需要晨洗 + 'mode' => 'none', ], + + // 步骤配置:禁用干燥和终末漂洗 'steps' => [ - ['code' => '晨洗', 'class' => 'MorningWashNode', 'enabled' => true], + ['code' => '晨洗', 'class' => 'MorningWashNode', 'enabled' => false], ['code' => '清洗', 'class' => 'WashNode', 'enabled' => true], ['code' => '漂洗', 'class' => 'RinseNode', 'enabled' => true], ['code' => '消毒', 'class' => 'DisinfectNode', 'enabled' => true], - ['code' => '终末漂洗', 'class' => 'FinalRinseNode', 'enabled' => true], - ['code' => '干燥', 'class' => 'DryNode', 'enabled' => true], + ['code' => '终末漂洗', 'class' => 'FinalRinseNode', 'enabled' => false], // ✗ 禁用 + ['code' => '干燥', 'class' => 'DryNode', 'enabled' => false], // ✗ 禁用干燥 ['code' => '结束', 'class' => 'EndNode', 'enabled' => true], ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true], ], ], - + // ============================================ - // 示例10: 自定义时间要求 - // 不同医院对步骤时间有不同要求 + // 7. 自定义语音流程 // ============================================ - 'custom_duration' => [ - 'name' => '自定义时间要求', - 'description' => '消毒时间要求10分钟', - // 覆盖 steps - 'override_steps' => false, + // 场景:需要特殊语音播报的医院 + // 特点:每个步骤都有自定义语音内容 + // ============================================ + 'custom_voice' => [ + 'name' => '自定义语音流程', + 'description' => '每个步骤都有特殊语音提示', + 'override_steps' => true, + + // 晨洗配置:每天第一次 'morning_wash' => [ 'mode' => 'daily_first', ], + + // 步骤配置:标准流程 'steps' => [ ['code' => '晨洗', 'class' => 'MorningWashNode', 'enabled' => true], ['code' => '清洗', 'class' => 'WashNode', 'enabled' => true], @@ -258,13 +311,44 @@ return [ ['code' => '结束', 'class' => 'EndNode', 'enabled' => true], ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true], ], + + // 自定义语音模板 + 'voice_templates' => [ + // 正常洗消流程语音 + 'normal_wash' => [ + '晨洗' => '晨洗开始,请进行每日首次清洗准备', + '清洗' => '第一步清洗开始,请认真清洗内镜外表面和管道', + '漂洗' => '第二步漂洗开始,用流动水冲洗残留酶液', + '消毒' => '第三步消毒开始,请确保消毒剂浓度和时间', + '终末漂洗' => '第四步终末漂洗开始,用无菌水彻底冲洗', + '干燥' => '最后一步干燥开始,用压力气枪吹干管道', + '结束' => '清洗流程全部完成,请妥善保管内镜', + '机洗' => '机洗开始,请将内镜放入清洗机', + ], + + // 晨洗流程语音 + 'morning_wash' => [ + '晨洗' => '晨洗模式启动,今日首次清洗需额外注意', + '清洗' => '晨洗后的第一次清洗,请按标准流程操作', + ], + + // 错误提示语音 + 'error_messages' => [ + 'NOT_ENOUGH_TIME' => '{step}时间不足,还需等待{time}秒', + 'PLEASE_SWIPE_OPERATOR' => '请先刷人员卡', + 'PLEASE_SWIPE_ENDOSCOPE' => '请刷内镜卡', + ], + ], + + // 时间验证配置 'time_validation' => [ 'durations' => [ - '清洗' => 300, // 5分钟 - '漂洗' => 120, // 2分钟 - '消毒' => 600, // 10分钟(自定义) - '终末漂洗' => 180, // 3分钟(自定义) - '干燥' => 300, // 5分钟(自定义) + '清洗' => 120, + '漂洗' => 60, + '消毒' => 300, + '终末漂洗' => 120, + '干燥' => 30, + '机洗' => 360, ], ], ], diff --git a/tests/flow/VirtualContextBuilder.php b/tests/flow/VirtualContextBuilder.php index 2201791..bf6b6fe 100644 --- a/tests/flow/VirtualContextBuilder.php +++ b/tests/flow/VirtualContextBuilder.php @@ -245,7 +245,7 @@ class VirtualContextBuilder /** * 设置操作时长(秒) */ - public function setDuration(int $seconds): self + public function setDuration(?int $seconds): self { $this->processStatus = new ProcessStatus( currentStep: $this->processStatus->currentStep, diff --git a/tests/flow/VirtualityFlowProcessor.php b/tests/flow/VirtualityFlowProcessor.php index 0165fa8..9862469 100644 --- a/tests/flow/VirtualityFlowProcessor.php +++ b/tests/flow/VirtualityFlowProcessor.php @@ -164,9 +164,12 @@ class VirtualityFlowProcessor // 继承之前的流程状态 if ($currentState !== null) { + $st = strtotime($currentState->getActionStartTime()); + $duration = time() - $st; + if (empty($st) || $duration <= 0) $duration = null; $builder->currentStep($currentState->getCurrentStep()) ->batchNo($currentState->getBatchNo()) - ->setDuration(time() - strtotime($currentState->getActionStartTime())) + ->setDuration($duration) ->processType($currentState->getProcessType()); // 设置 previousAction:上一步骤名称就是当前状态的 currentStep @@ -231,6 +234,7 @@ class VirtualityFlowProcessor Logger::info("当前步骤:{}", [$result->getCurrentStep()]); Logger::info("上一个步骤类型:{}", [$result->getPreviousAction()->action_type_name ?? "null"]); Logger::info("上一个步骤:{}", [$result->getPreviousAction()->process_name ?? "null"]); + Logger::info("上一个步骤开始时间:{}", [$result->getPreviousAction()->op_starttime ?? "null"]); Logger::info("时长:{}", [$result->getDuration() ?? "null"]); Logger::info("当前语音:{$result->getFullVoice()}\n"); return $result; diff --git a/tests/flow/node_flow/BlockTest.php b/tests/flow/node_flow/BlockTest.php index 62473c9..945a30e 100644 --- a/tests/flow/node_flow/BlockTest.php +++ b/tests/flow/node_flow/BlockTest.php @@ -86,9 +86,9 @@ class BlockTest extends TestCase ->reader('漂洗') ->operator('操作员1') ->currentStep('清洗') - ->previousAction('清洗') // 上一步也是清洗,表示重复刷同一步骤 + ->setPreviousAction('清洗') // 上一步也是清洗,表示重复刷同一步骤 ->processType('手工洗') // 必须设置流程类型,否则 hasStep 返回 false - ->duration(5) // 只有5秒,时间不足 + ->setDuration(5) // 只有5秒,时间不足 ->batchNo(date('Ymd') . '010001') ->build(); @@ -115,9 +115,9 @@ class BlockTest extends TestCase ->reader('漂洗') ->operator('操作员1') ->currentStep('清洗') - ->previousAction('清洗') // 设置 previousAction 使时间验证生效 + ->setPreviousAction('清洗') // 设置 previousAction 使时间验证生效 ->processType('手工洗') // 必须设置流程类型 - ->duration(5) // 只有5秒,时间不足 + ->setDuration(5) // 只有5秒,时间不足 ->batchNo(date('Ymd') . '010001') ->build(); @@ -166,9 +166,9 @@ class BlockTest extends TestCase ->reader('漂洗') ->operator('操作员1') ->currentStep('清洗') - ->previousAction('清洗') // 设置 previousAction 使时间验证生效 + ->setPreviousAction('清洗') // 设置 previousAction 使时间验证生效 ->processType('手工洗') // 必须设置流程类型 - ->duration(120) // 120秒,时间充足 + ->setDuration(120) // 120秒,时间充足 ->batchNo(date('Ymd') . '010001') ->build(); diff --git a/tests/flow/node_flow/VoiceTest.php b/tests/flow/node_flow/VoiceTest.php index 1bd6f6b..0916912 100644 --- a/tests/flow/node_flow/VoiceTest.php +++ b/tests/flow/node_flow/VoiceTest.php @@ -96,7 +96,7 @@ class VoiceTest extends TestCase ->reader('漂洗') ->operator('操作员1') ->currentStep('清洗') - ->duration(5) // 只有5秒,时间不足 + ->setDuration(5) // 只有5秒,时间不足 ->batchNo(date('Ymd') . '010001') ->build(); diff --git a/配置自定义节点.md b/配置自定义节点.md new file mode 100644 index 0000000..c558f58 --- /dev/null +++ b/配置自定义节点.md @@ -0,0 +1,443 @@ +# 配置自定义节点流程指南 + +## 概述 + +本项目支持通过配置文件自定义洗消流程,可以灵活控制每个节点的前置条件、启用状态等。 + +## 配置文件位置 + +- **文件路径**:`./custom_process_config.php` +- **配置键选择**:通过环境变量 `FLOW_PROCESS_CONFIG_KEY` 指定使用的配置 +- **配置键**:通过环境变量 `FLOW_USE_CUSTOM_PROCESS` 来开启允许使用自定义配置 + +### override_steps 参数详解 ⭐ + +**作用**:控制自定义步骤与默认步骤的合并策略。 + +#### 1. override_steps = true(完全覆盖模式) + +**行为**: +- ✅ 完全使用配置文件中的 `steps` 列表 +- ✅ **不会**创建配置中不存在的节点 +- ✅ 如果配置为空数组,则使用默认步骤列表 +- ✅ 适合:定义全新的流程、大幅简化流程 + +**示例**:机洗单步骤流程 +```php +'machine_wash' => [ + 'override_steps' => true, // 完全覆盖 + 'steps' => [ + // 只定义需要的节点,其他节点不会创建 + ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true], + ['code' => '终末漂洗', 'class' => 'FinalRinseNode', 'enabled' => true], + ['code' => '干燥', 'class' => 'DryNode', 'enabled' => true], + ['code' => '结束', 'class' => 'EndNode', 'enabled' => true], + ], +] +// 结果:流程中只有这 4 个节点,清洗/漂洗/消毒等节点不会被创建 +``` + +**注意事项**: +- ⚠️ 必须显式列出所有需要的节点 +- ⚠️ 漏掉的节点将**不存在**于流程中(不是禁用,是不存在) +- ⚠️ 如果某个节点没配置但后续逻辑需要,会导致错误 + +#### 2. override_steps = false(增量合并模式) + +**行为**: +- ✅ 使用默认步骤列表作为基础 +- ✅ 用自定义步骤**覆盖**相同 code 的默认步骤 +- ✅ 自定义步骤中不存在的节点**保持默认配置** +- ✅ 适合:微调特定节点的属性(如 enabled、required) + +**示例**:仅调整晨洗节点 +```php +'custom_morning' => [ + 'override_steps' => false, // 增量合并 + 'steps' => [ + // 只配置晨洗节点,其他节点使用默认值 + ['code' => '晨洗', 'class' => 'MorningWashNode', 'enabled' => true, 'required' => []], + ], +] +// 结果: +// - 晨洗节点:使用自定义配置(enabled=true, required=[]) +// - 清洗/漂洗/消毒等其他节点:使用默认配置 +``` + +**合并逻辑**(伪代码): +```php +// 1. 将默认步骤转为「code => StepConfig」的关联数组 +$defaultStepsMap = []; +foreach (self::defaultSteps() as $defaultStep) { + $defaultStepsMap[$defaultStep->code] = $defaultStep; +} + +// 2. 遍历自定义步骤,覆盖默认步骤中相同 code 的元素 +foreach ($customSteps as $customStep) { + $defaultStepsMap[$customStep->code] = $customStep; // 覆盖 +} + +// 3. 转回索引数组 +$this->steps = array_values($defaultStepsMap); +``` + +**注意事项**: +- ⚠️ 只能修改已存在于默认列表中的节点 +- ⚠️ 无法添加默认列表中不存在的新节点(会被忽略) +- ⚠️ 适合小范围调整,不适合大规模改动 + +#### 3. 对比总结 + +| 特性 | override_steps = true | override_steps = false | +|------|----------------------|------------------------| +| 基础列表 | 自定义 steps | 默认 steps | +| 未配置的节点 | 不存在 | 使用默认配置 | +| 相同 code 的节点 | 以自定义为准 | 以自定义为准 | +| 适用场景 | 全新流程、大幅简化 | 微调特定节点 | +| 风险 | 可能遗漏必需节点 | 无法添加新节点 | + +#### 4. 使用建议 + +**使用 override_steps = true 的场景**: +- ✅ 机洗单步骤流程(只需 4 个节点) +- ✅ 无干燥流程(去掉干燥和终末漂洗) +- ✅ 特殊医院定制的简化流程 + +**使用 override_steps = false 的场景**: +- ✅ 仅调整晨洗规则 +- ✅ 仅修改某个节点的 enabled 状态 +- ✅ 仅添加 required 约束 +- ✅ 临时测试某个节点的配置变更 + +**默认推荐**: +- 📌 大多数情况使用 `true`,明确列出所有需要的节点 +- 📌 只在确实只需要微调时使用 `false` + +--- + +## 配置结构说明 + +### 完整配置示例 + +```php +'custom_flow' => [ + 'name' => '自定义流程名称', + 'description' => '流程描述', + 'override_steps' => true, // 是否覆盖默认步骤 + + // 晨洗配置 + 'morning_wash' => [ + 'mode' => 'none', // none | daily_first | all | storage_time | specific_types + 'storage_threshold' => 4, // 存储时间阈值(小时)- storage_time 模式使用 + 'morning_start_time' => '00:00:00', // 每日开始时间 - daily_first 模式使用 + 'specific_types' => ['胃镜', '十二指肠镜'], // 特定类型 - specific_types 模式使用 + ], + + // 步骤配置(核心) + 'steps' => [ + [ + 'code' => '清洗', // 节点标识码(必须与 ProcessNode 的 getCode() 一致) + 'class' => 'WashNode', // 节点类名(不含命名空间) + 'enabled' => true, // 是否启用该节点 + 'required' => ['结束', '晨洗'], // 可选:指定前置节点列表 + ], + // ... 其他节点 + ], + + // 时间验证配置(秒) + 'time_validation' => [ + 'durations' => [ + '晨洗' => 60, // 晨洗至少 60 秒 + '清洗' => 120, // 清洗至少 120 秒 + '漂洗' => 60, // 漂洗至少 60 秒 + '消毒' => 300, // 消毒至少 300 秒 + '终末漂洗' => 120, // 终末漂洗至少 120 秒 + '干燥' => 30, // 干燥至少 30 秒 + '机洗' => 360, // 机洗至少 360 秒 + ], + 'enabled' => true, // 是否启用时间验证 + ], + + // 语音模板配置 + 'voice_templates' => [ + // 正常洗消流程语音 + 'normal_wash' => [ + '晨洗' => '晨洗开始,请进行每日首次清洗准备', + '清洗' => '第一步清洗开始,请认真清洗内镜外表面和管道', + '漂洗' => '第二步漂洗开始,用流动水冲洗残留酶液', + '消毒' => '第三步消毒开始,请确保消毒剂浓度和时间', + '终末漂洗' => '第四步终末漂洗开始,用无菌水彻底冲洗', + '干燥' => '最后一步干燥开始,用压力气枪吹干管道', + '结束' => '清洗流程全部完成,请妥善保管内镜', + '机洗' => '机洗开始,请将内镜放入清洗机', + ], + + // 晨洗流程语音 + 'morning_wash' => [ + '晨洗' => '晨洗模式启动,今日首次清洗需额外注意', + '清洗' => '晨洗后的第一次清洗,请按标准流程操作', + ], + + // 错误提示语音 + 'error_messages' => [ + 'NOT_ENOUGH_TIME' => '{step}时间不足,还需等待{time}秒', + 'PLEASE_SWIPE_OPERATOR' => '请先刷人员卡', + 'PLEASE_SWIPE_ENDOSCOPE' => '请刷内镜卡', + 'PLEASE_SWIPE_READER' => '请刷读卡器', + ], + + // 重复刷卡提示 + 'duplicate_check' => [ + 'operator' => '您已刷过人员卡,无需重复刷卡', + 'endoscope' => '您已刷过内镜卡,无需重复刷卡', + 'reader' => '您已刷过读卡器,无需重复刷卡', + ], + ], +], +``` + +## 核心参数详解 + +### 1. steps[].code(节点标识码) + +**作用**:唯一标识一个节点,必须与对应的 `ProcessNode` 子类的 `getCode()` 方法返回值一致。 + +**可用节点码**: +- `晨洗` - MorningWashNode +- `重复刷卡` - DuplicateCheckNode +- `清洗` - WashNode +- `漂洗` - RinseNode +- `消毒` - DisinfectNode +- `终末漂洗` - FinalRinseNode +- `干燥` - DryNode +- `结束` - EndNode +- `机洗` - MachineWashNode +- `虚拟清洗机` - VirtualWashMachineNode +- `存储` - StorageNode +- `内镜放入` - StorageInNode +- `内镜取出` - StorageOutNode +- `Close` - CloseNode + +### 2. steps[].class(节点类名) + +**作用**:指定处理该节点的类名(不含命名空间)。 + +**命名空间**:所有节点类都在 `app\flow\nodes\` 命名空间下。 + +**示例**: +```php +'class' => 'WashNode' // 对应 \app\flow\nodes\WashNode +``` + +### 3. steps[].enabled(启用状态) + +**作用**:控制节点是否启用。 + +**值**: +- `true` - 启用该节点 +- `false` - 禁用/跳过该节点 + +**示例**: +```php +// 机洗单步骤流程中,跳手工洗节点 +['code' => '清洗', 'class' => 'WashNode', 'enabled' => false], +['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true], +``` + +### 4. steps[].required(前置节点要求)⭐ + +**作用**:指定当前节点的前一个节点可以是哪些。用于实现特殊流程控制。 + +**类型**:字符串数组(允许的前置节点 code 列表) + +**工作原理**: +- 当流程执行到某个节点时,会检查上一个节点的 code 是否在 `required` 列表中 +- 如果不在列表中,流程会拒绝执行并提示错误 +- 如果不设置 `required`,则使用默认的配置 + +**使用场景**: + +#### 场景 1:强制流程顺序 +```php +'steps' => [ + ['code' => '清洗', 'class' => 'WashNode', 'enabled' => true, 'required' => ['结束', '晨洗']], + ['code' => '漂洗', 'class' => 'RinseNode', 'enabled' => true, 'required' => ['清洗']], + ['code' => '消毒', 'class' => 'DisinfectNode', 'enabled' => true, 'required' => ['漂洗']], + ['code' => '终末漂洗', 'class' => 'FinalRinseNode', 'enabled' => true, 'required' => ['消毒']], + ['code' => '干燥', 'class' => 'DryNode', 'enabled' => true, 'required' => ['终末漂洗']], + ['code' => '结束', 'class' => 'EndNode', 'enabled' => true, 'required' => ['干燥']], +] +``` +这样确保必须按顺序执行:清洗 → 漂洗 → 消毒 → 终末漂洗 → 干燥 → 结束 + +#### 场景 2:机洗介入 +```php +'steps' => [ + ['code' => '清洗', 'class' => 'WashNode', 'enabled' => true], + ['code' => '漂洗', 'class' => 'RinseNode', 'enabled' => true], + ['code' => '消毒', 'class' => 'DisinfectNode', 'enabled' => true], + // 机洗可以在手工洗的任意阶段介入 + ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true, 'required' => ['清洗', '漂洗', '消毒']], + ['code' => '终末漂洗', 'class' => 'FinalRinseNode', 'enabled' => true, 'required' => ['机洗']], + ['code' => '干燥', 'class' => 'DryNode', 'enabled' => true, 'required' => ['终末漂洗']], + ['code' => '结束', 'class' => 'EndNode', 'enabled' => true, 'required' => ['干燥', '机洗']], +] +``` + +#### 场景 3:多路径汇聚 +```php +'steps' => [ + ['code' => '干燥', 'class' => 'DryNode', 'enabled' => true], + ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true], + // 结束可以从干燥或机洗过来 + ['code' => '结束', 'class' => 'EndNode', 'enabled' => true, 'required' => ['干燥', '机洗']], +] +``` + +**注意事项**: +1. ✅ `required` 中的节点码必须在 `steps` 中存在 +2. ✅ 循环依赖可能会导致死锁(如 A 要求 B,B 要求 A) +3. ✅ 如果节点设置了 `required`,则只能从指定的前驱节点过来 + +## 常用流程配置示例 + +### 1. 标准完整流程(不设置 required) + +```php +'standard' => [ + 'name' => '标准完整流程', + 'override_steps' => true, + 'morning_wash' => ['mode' => 'daily_first'], + 'steps' => [ + ['code' => '晨洗', 'class' => 'MorningWashNode', 'enabled' => true], + ['code' => '清洗', 'class' => 'WashNode', 'enabled' => true], + ['code' => '漂洗', 'class' => 'RinseNode', 'enabled' => true], + ['code' => '消毒', 'class' => 'DisinfectNode', 'enabled' => true], + ['code' => '终末漂洗', 'class' => 'FinalRinseNode', 'enabled' => true], + ['code' => '干燥', 'class' => 'DryNode', 'enabled' => true], + ['code' => '结束', 'class' => 'EndNode', 'enabled' => true], + ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true], + ], +] +``` + +### 2. 机洗单步骤流程(设置 required) + +```php +'machine_wash' => [ + 'name' => '机洗单步骤流程', + 'override_steps' => true, + 'morning_wash' => ['mode' => 'none'], + 'steps' => [ + ['code' => '晨洗', 'class' => 'MorningWashNode', 'enabled' => false], + ['code' => '清洗', 'class' => 'WashNode', 'enabled' => true, 'required' => ['结束']], + ['code' => '漂洗', 'class' => 'RinseNode', 'enabled' => true, 'required' => ['清洗']], + ['code' => '消毒', 'class' => 'DisinfectNode', 'enabled' => true, 'required' => ['漂洗']], + ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true, 'required' => ['清洗', '漂洗', '消毒']], + ['code' => '终末漂洗', 'class' => 'FinalRinseNode', 'enabled' => true, 'required' => ['机洗']], + ['code' => '干燥', 'class' => 'DryNode', 'enabled' => true, 'required' => ['终末漂洗']], + ['code' => '结束', 'class' => 'EndNode', 'enabled' => true, 'required' => ['干燥']], + ], +] +``` + +### 3. 无干燥流程 + +```php +'no_dry' => [ + 'name' => '无干燥流程', + 'override_steps' => true, + 'morning_wash' => ['mode' => 'none'], + 'steps' => [ + ['code' => '晨洗', 'class' => 'MorningWashNode', 'enabled' => false], + ['code' => '清洗', 'class' => 'WashNode', 'enabled' => true], + ['code' => '漂洗', 'class' => 'RinseNode', 'enabled' => true], + ['code' => '消毒', 'class' => 'DisinfectNode', 'enabled' => true], + ['code' => '终末漂洗', 'class' => 'FinalRinseNode', 'enabled' => false], + ['code' => '干燥', 'class' => 'DryNode', 'enabled' => false], + ['code' => '结束', 'class' => 'EndNode', 'enabled' => true], + ], +] +``` + +## 调试技巧 + +### 1. 查看当前使用的配置 + +```bash +# 在 .env 文件中查看 +FLOW_PROCESS_CONFIG_KEY=standard +``` + +### 2. 日志输出 + +流程引擎会在日志中输出加载的配置信息: +``` +ProcessConfig 初始化中... +加载 [steps] 步骤配置,来源:自定义配置 +``` + +### 3. 测试配置有效性 + +运行单元测试验证配置: +```bash +phpunit tests/flow/node_flow/ +``` + +## 常见问题 + +### Q1: 配置后流程不生效? + +**检查清单**: +1. ✅ 确认 `.env` 中的 `FLOW_PROCESS_CONFIG_KEY` 正确 +2. ✅ 确认 `steps[].code` 与节点类的 `getCode()` 一致 +3. ✅ 确认 `steps[].class` 类名存在且正确 +4. ✅ 清除缓存(如果有) + +### Q2: required 设置后流程卡住? + +**可能原因**: +- 循环依赖:A 要求 B,B 要求 A +- 前置节点不存在于 steps 中 +- 前置节点被禁用(enabled=false) + +**解决方法**: +1. 检查 `required` 数组中的节点码是否拼写正确 +2. 确保前置节点在同一个配置的 `steps` 中且启用 +3. 暂时移除 `required` 测试基础流程 + +### Q3: 如何同时支持手工洗和机洗? + +**方案**:在 `steps` 中同时启用两种节点,通过 `required` 控制介入时机: + +```php +'steps' => [ + ['code' => '清洗', 'class' => 'WashNode', 'enabled' => true], + ['code' => '漂洗', 'class' => 'RinseNode', 'enabled' => true], + // 机洗可以在任意阶段介入 + ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true, 'required' => ['清洗', '漂洗', '消毒', '终末漂洗']], + ['code' => '消毒', 'class' => 'DisinfectNode', 'enabled' => true], + ['code' => '终末漂洗', 'class' => 'FinalRinseNode', 'enabled' => true], + // 结束可以从干燥或机洗过来 + ['code' => '结束', 'class' => 'EndNode', 'enabled' => true, 'required' => ['干燥', '机洗']], + ['code' => '干燥', 'class' => 'DryNode', 'enabled' => true], +] +``` + +## 相关文件 + +- **配置文件**:`app/config/custom_process_config.php` +- **配置加载类**:`app/flow/config/ProcessConfig.php` +- **步骤配置类**:`app/flow/config/StepConfig.php` +- **步骤集合类**:`app/flow/config/StepsConfig.php` +- **流程引擎**:`app/flow/ProcessEngine.php` +- **节点基类**:`app/flow/nodes/ProcessNodeInterface.php` +- **抽象节点**:`app/flow/nodes/AbstractProcessNode.php` + +## 开发建议 + +1. ✅ **标准流程保持简单**:`standard` 配置不设置 `required`,保持灵活性 +2. ✅ **特殊流程才用 required**:只在需要严格控制顺序时使用 +3. ✅ **充分测试**:修改配置后运行完整的流程测试 +4. ✅ **注释清晰**:为每个自定义配置添加详细的用途说明 +5. ✅ **版本管理**:配置变更要在 Git 中记录清楚 \ No newline at end of file