Implemented qt port + e2e

This commit is contained in:
Martino Ferrari
2026-06-26 09:11:10 +02:00
parent 0d7d8f396b
commit 4702d0a217
146 changed files with 57272 additions and 128 deletions
@@ -1,3 +1,138 @@
../../../Build/x86-linux/Applications/StreamHub/BinaryRecorderGTest.o: BinaryRecorderGTest.cpp \
../../../Source/Applications/StreamHub/BinaryRecorder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Architecture/x86_gcc/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TypeDescriptor.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BasicType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/FractionalInteger.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TypeCharacteristics.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TemplateParametersVerificator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitRange.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitBoolean.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/FastPollingMutexSem.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Atomic.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TypeCharacteristics.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Architecture/x86_gcc/AtomicA.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorInformation.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ErrorType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitBoolean.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitRange.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StreamI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeoutType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeStamp.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Architecture/x86_gcc/HighResolutionTimerA.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/HighResolutionTimerCalibrator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeStamp.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Sleep.h \
../../../Common/UDP/UDPSProtocol.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-internal.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-port.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-message.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-string.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-filepath.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-type-util.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-death-test.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-death-test-internal.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-param-test.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-param-util.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-linked_ptr.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-printers.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-param-util-generated.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest_prod.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-test-part.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-typed-test.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest_pred_impl.h
../../../Build/x86-linux/Applications/StreamHub/BinaryRecorderSrc.o: BinaryRecorderSrc.cpp \
../../../Source/Applications/StreamHub/BinaryRecorder.cpp \
../../../Source/Applications/StreamHub/BinaryRecorder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Architecture/x86_gcc/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TypeDescriptor.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BasicType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/FractionalInteger.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TypeCharacteristics.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TemplateParametersVerificator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitRange.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitBoolean.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/FastPollingMutexSem.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Atomic.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TypeCharacteristics.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Architecture/x86_gcc/AtomicA.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorInformation.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ErrorType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitBoolean.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitRange.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StreamI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeoutType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeStamp.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Architecture/x86_gcc/HighResolutionTimerA.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/HighResolutionTimerCalibrator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeStamp.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Sleep.h \
../../../Common/UDP/UDPSProtocol.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/AdvancedErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassProperties.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/FractionalInteger.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/StreamMemoryReference.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/BufferedStreamI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeoutType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/AnyType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassProperties.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassRegistryDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GlobalObjectsDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GlobalObjectI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/StandardHeap.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../HeapI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../Generic/StandardHeap_Generic.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassRegistryItem.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CString.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ZeroTerminatedArray.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/Introspection.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/IntrospectionEntry.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ZeroTerminatedArray.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListable.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListHolderT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/IteratorT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Iterator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListable.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListHolder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/SortFilter.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/SearchFilter.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/LoadableLibrary.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ObjectBuilder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StaticList.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StaticListHolder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Matrix.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Vector.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapManager.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/MemoryOperationsHelper.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/FormatDescriptor.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/IOBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/CharBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StreamI.h
../../../Build/x86-linux/Applications/StreamHub/LTTBGTest.o: LTTBGTest.cpp ../../../Source/Applications/StreamHub/LTTB.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Architecture/x86_gcc/CompilerTypes.h \
@@ -1,3 +1,138 @@
BinaryRecorderGTest.o: BinaryRecorderGTest.cpp \
../../../Source/Applications/StreamHub/BinaryRecorder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Architecture/x86_gcc/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TypeDescriptor.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BasicType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/FractionalInteger.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TypeCharacteristics.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TemplateParametersVerificator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitRange.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitBoolean.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/FastPollingMutexSem.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Atomic.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TypeCharacteristics.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Architecture/x86_gcc/AtomicA.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorInformation.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ErrorType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitBoolean.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitRange.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StreamI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeoutType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeStamp.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Architecture/x86_gcc/HighResolutionTimerA.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/HighResolutionTimerCalibrator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeStamp.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Sleep.h \
../../../Common/UDP/UDPSProtocol.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-internal.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-port.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-message.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-string.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-filepath.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-type-util.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-death-test.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-death-test-internal.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-param-test.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-param-util.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-linked_ptr.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-printers.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-param-util-generated.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest_prod.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-test-part.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-typed-test.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest_pred_impl.h
BinaryRecorderSrc.o: BinaryRecorderSrc.cpp \
../../../Source/Applications/StreamHub/BinaryRecorder.cpp \
../../../Source/Applications/StreamHub/BinaryRecorder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Architecture/x86_gcc/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TypeDescriptor.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BasicType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/FractionalInteger.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TypeCharacteristics.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TemplateParametersVerificator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitRange.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitBoolean.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/FastPollingMutexSem.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Atomic.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TypeCharacteristics.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Architecture/x86_gcc/AtomicA.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorInformation.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ErrorType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitBoolean.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitRange.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StreamI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeoutType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeStamp.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Architecture/x86_gcc/HighResolutionTimerA.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/HighResolutionTimerCalibrator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeStamp.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Sleep.h \
../../../Common/UDP/UDPSProtocol.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/AdvancedErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassProperties.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/FractionalInteger.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/StreamMemoryReference.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/BufferedStreamI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeoutType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/AnyType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassProperties.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassRegistryDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GlobalObjectsDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GlobalObjectI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/StandardHeap.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../HeapI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../Generic/StandardHeap_Generic.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassRegistryItem.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CString.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ZeroTerminatedArray.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/Introspection.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/IntrospectionEntry.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ZeroTerminatedArray.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListable.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListHolderT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/IteratorT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Iterator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListable.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListHolder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/SortFilter.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/SearchFilter.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/LoadableLibrary.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ObjectBuilder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StaticList.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StaticListHolder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Matrix.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Vector.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapManager.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/MemoryOperationsHelper.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/FormatDescriptor.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/IOBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/CharBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StreamI.h
LTTBGTest.o: LTTBGTest.cpp ../../../Source/Applications/StreamHub/LTTB.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Architecture/x86_gcc/CompilerTypes.h \
@@ -18,3 +18,182 @@
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-typed-test.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest_pred_impl.h \
UDPStreamerTest.h
../../../../Build/x86-linux/Components/DataSources/UDPStreamer/UDPStreamerTest.o: UDPStreamerTest.cpp \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/AdvancedErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassProperties.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TypeCharacteristics.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Architecture/x86_gcc/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/FractionalInteger.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TypeCharacteristics.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TemplateParametersVerificator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorInformation.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ErrorType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitBoolean.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitRange.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StreamI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeoutType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeStamp.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Architecture/x86_gcc/HighResolutionTimerA.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/HighResolutionTimerCalibrator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeStamp.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/StreamMemoryReference.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/BufferedStreamI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeoutType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/AnyType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassProperties.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassRegistryDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GlobalObjectsDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GlobalObjectI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/StandardHeap.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../HeapI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../Generic/StandardHeap_Generic.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/FastPollingMutexSem.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Atomic.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Architecture/x86_gcc/AtomicA.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Sleep.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassRegistryItem.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CString.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ZeroTerminatedArray.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/Introspection.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/IntrospectionEntry.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TypeDescriptor.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BasicType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/FractionalInteger.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitRange.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitBoolean.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ZeroTerminatedArray.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListable.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListHolderT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/IteratorT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Iterator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListable.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListHolder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/SortFilter.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/SearchFilter.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/LoadableLibrary.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ObjectBuilder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StaticList.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StaticListHolder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Matrix.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Vector.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapManager.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/MemoryOperationsHelper.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/FormatDescriptor.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/IOBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/CharBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StreamI.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/BasicTCPSocket.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/BasicSocket.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/InternetHost.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/StreamString.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StringHelper.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/StreamStringIOBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/Environment/Linux/InternetHostCore.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/Environment/Linux/InternetMulticastCore.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HandleI.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/Environment/Linux/SocketCore.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/BasicUDPSocket.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Sleep.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/ConfigurationDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/AnyObject.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/Object.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/StructuredDataI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/AnyType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/CLASSREGISTER.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassRegistryItemT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ObjectBuilderT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/ConfigurationDatabaseNode.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Fnv1aHashFunction.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/HashFunction.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceContainer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListHolder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/Object.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/Reference.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceContainerFilter.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceContainerNode.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/UnorderedMap.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapManager.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StringHelper.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceContainerFilterObjectName.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceContainer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/StructuredDataI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/TypeConversion.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Vector.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/GAM.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/DataSourceI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/StatefulI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/ExecutableI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L5GAMs/GAMScheduler.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L1Portability/EventSem.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/GAMSchedulerI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/GAM.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ProcessorType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitSet.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/TimingDataSource.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/GAMDataSource.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/MemoryArea.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Messages/Message.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/MultiThreadService.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedServiceI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedServiceMethodBinderI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/ExecutionInfo.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L1Portability/Threads.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ThreadsB.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L1Portability/ExceptionHandler.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedServiceMethodBinderT.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedThreadI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/SingleThreadService.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedThread.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/RealTimeApplication.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/CLASSMETHODREGISTER.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassMethodInterfaceMapper.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassMethodCaller.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassMethodCallerT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/GAMSchedulerI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Messages/MessageI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Messages/Message.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Messages/MessageFilterPool.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Messages/MessageFilter.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/RealTimeApplicationConfigurationBuilder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/RealTimeApplication.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Messages/RegisteredMethodsMessageFilter.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ObjectRegistryDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/StandardParser.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/ConfigurationParserI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/AnyTypeCreator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StaticListHolder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/ConfigurationDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/LexicalAnalyzer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/GrammarInfo.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/Token.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/TokenInfo.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/ParserI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/RuntimeEvaluator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/IteratorT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StaticStack.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StaticList.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/VariableInformation.h \
../../../../Source/Components/DataSources/UDPStreamer/UDPStreamer.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedServiceMethodBinderI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/MemoryDataSourceI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/SingleThreadService.h \
../../../../Source/Components/Interfaces/UDPStream/UDPSServer.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/InternetHost.h \
../../../../Common/UDP/UDPSProtocol.h UDPStreamerTest.h
@@ -18,3 +18,182 @@ UDPStreamerGTest.o: UDPStreamerGTest.cpp \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-typed-test.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest_pred_impl.h \
UDPStreamerTest.h
UDPStreamerTest.o: UDPStreamerTest.cpp \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/AdvancedErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassProperties.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TypeCharacteristics.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Architecture/x86_gcc/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/FractionalInteger.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TypeCharacteristics.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TemplateParametersVerificator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorInformation.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ErrorType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitBoolean.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitRange.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StreamI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeoutType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeStamp.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Architecture/x86_gcc/HighResolutionTimerA.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/HighResolutionTimerCalibrator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeStamp.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/StreamMemoryReference.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/BufferedStreamI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeoutType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/AnyType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassProperties.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassRegistryDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GlobalObjectsDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GlobalObjectI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/StandardHeap.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../HeapI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../Generic/StandardHeap_Generic.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/FastPollingMutexSem.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Atomic.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Architecture/x86_gcc/AtomicA.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Sleep.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassRegistryItem.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CString.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ZeroTerminatedArray.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/Introspection.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/IntrospectionEntry.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TypeDescriptor.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BasicType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/FractionalInteger.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitRange.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitBoolean.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ZeroTerminatedArray.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListable.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListHolderT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/IteratorT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Iterator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListable.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListHolder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/SortFilter.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/SearchFilter.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/LoadableLibrary.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ObjectBuilder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StaticList.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StaticListHolder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Matrix.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Vector.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapManager.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/MemoryOperationsHelper.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/FormatDescriptor.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/IOBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/CharBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StreamI.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/BasicTCPSocket.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/BasicSocket.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/InternetHost.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/StreamString.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StringHelper.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/StreamStringIOBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/Environment/Linux/InternetHostCore.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/Environment/Linux/InternetMulticastCore.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HandleI.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/Environment/Linux/SocketCore.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/BasicUDPSocket.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Sleep.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/ConfigurationDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/AnyObject.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/Object.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/StructuredDataI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/AnyType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/CLASSREGISTER.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassRegistryItemT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ObjectBuilderT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/ConfigurationDatabaseNode.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Fnv1aHashFunction.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/HashFunction.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceContainer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListHolder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/Object.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/Reference.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceContainerFilter.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceContainerNode.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/UnorderedMap.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapManager.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StringHelper.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceContainerFilterObjectName.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceContainer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/StructuredDataI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/TypeConversion.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Vector.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/GAM.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/DataSourceI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/StatefulI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/ExecutableI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L5GAMs/GAMScheduler.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L1Portability/EventSem.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/GAMSchedulerI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/GAM.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ProcessorType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitSet.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/TimingDataSource.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/GAMDataSource.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/MemoryArea.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Messages/Message.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/MultiThreadService.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedServiceI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedServiceMethodBinderI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/ExecutionInfo.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L1Portability/Threads.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ThreadsB.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L1Portability/ExceptionHandler.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedServiceMethodBinderT.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedThreadI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/SingleThreadService.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedThread.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/RealTimeApplication.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/CLASSMETHODREGISTER.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassMethodInterfaceMapper.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassMethodCaller.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassMethodCallerT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/GAMSchedulerI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Messages/MessageI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Messages/Message.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Messages/MessageFilterPool.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Messages/MessageFilter.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/RealTimeApplicationConfigurationBuilder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/RealTimeApplication.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Messages/RegisteredMethodsMessageFilter.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ObjectRegistryDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/StandardParser.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/ConfigurationParserI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/AnyTypeCreator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StaticListHolder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/ConfigurationDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/LexicalAnalyzer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/GrammarInfo.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/Token.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/TokenInfo.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/ParserI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/RuntimeEvaluator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/IteratorT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StaticStack.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StaticList.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/VariableInformation.h \
../../../../Source/Components/DataSources/UDPStreamer/UDPStreamer.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedServiceMethodBinderI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/MemoryDataSourceI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/SingleThreadService.h \
../../../../Source/Components/Interfaces/UDPStream/UDPSServer.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/InternetHost.h \
../../../../Common/UDP/UDPSProtocol.h UDPStreamerTest.h
+18 -8
View File
@@ -7,11 +7,14 @@
* Sine1 (1 Hz, 5 V, phase 0)
* Sine2 (0.3 Hz, 3 V, phase 60 deg)
*
* Thread2 1 kHz — 2 array sines @ 1 Msps (1000 elem × 1 kHz)
* Thread2 1 kHz — 2 array sines @ 1 Msps (1000 elem × 1 kHz, Strict)
* Ch1 (1 kHz, 1 V, phase 0)
* Ch2 (5 kHz, 0.5 V, phase 90 deg)
* Strict publishing: every cycle's 1 ms array is sent so the
* reconstructed waveform is contiguous (Decimate would drop
* whole cycles, leaving periodic gaps in the array trace).
*
* Thread3 5 kHz — 2 array sines @ 5 Msps (1000 elem × 5 kHz, Ratio=5 → 1 kHz pub)
* Thread3 5 kHz — 2 array sines @ 5 Msps (1000 elem × 5 kHz, Strict)
* Ch3 (10 kHz, 2 V, phase 0)
* Ch4 (50 kHz, 1.5 V, phase 45 deg)
*/
@@ -437,9 +440,13 @@ $App = {
+Streamer2 = {
Class = UDPStreamer
Port = 44501
MaxPayloadSize = 1400
PublishingMode = "Decimate"
Ratio = 10
// One full cycle = TimeArray1(8000) + Ch1(4000) + Ch2(4000) = 16000 B.
// Sizing the payload above a whole cycle sends each cycle as a single
// datagram (totalFragments = 1): at Strict 1 kHz / 5 kHz this avoids
// saturating the receiver's fragment-reassembly pool, which otherwise
// evicts in-flight cycles and reintroduces gaps in the array traces.
MaxPayloadSize = 18000
PublishingMode = "Strict"
Signals = {
TimeArray1 = {
Type = uint64
@@ -469,9 +476,12 @@ $App = {
+Streamer3 = {
Class = UDPStreamer
Port = 44502
MaxPayloadSize = 1400
PublishingMode = "Decimate"
Ratio = 5
// One full cycle = TimeArray2(8000) + Ch3(4000) + Ch4(4000) = 16000 B.
// Single-datagram cycles (see Streamer2) — essential here because this
// source runs Strict at 5 kHz, where per-cycle fragmentation would flood
// the reassembly pool.
MaxPayloadSize = 18000
PublishingMode = "Strict"
Signals = {
TimeArray2 = {
Type = uint64
+332
View File
@@ -0,0 +1,332 @@
// E2E_Report.typ — Streaming-chain E2E report.
//
// Renders report_data.json (produced by report_build.py) into a single PDF:
// headline KPIs, per-field progression/regression vs the previous run, unit-test
// suites, code coverage, performance (CPU/peak-RSS/throughput), per-scenario
// waveform fidelity, trend plots, and embedded per-scenario waveform images.
//
// Compile from the directory holding report_data.json + the *.png artifacts:
// typst compile E2E_Report.typ E2E_Report.pdf
// Missing inputs degrade to "n/a" / skipped sections so a partial run still
// renders.
#let data = json("report_data.json")
#let meta = data.meta
#let e2e = data.e2e
#let ut = data.unit_tests
#let cov = data.coverage
#let reg = data.regression
#let hl = data.headline
// ── helpers ──────────────────────────────────────────────────────────────────
#let fmt(v, suffix: "") = if v == none { "n/a" } else { str(v) + suffix }
#let fnum(v, digits: 3, suffix: "") = {
if v == none { "n/a" } else {
[#calc.round(v, digits: digits)] + suffix
}
}
#let ok_color = rgb("#1a7f37")
#let bad_color = rgb("#cf222e")
#let neutral = rgb("#57606a")
#let warn_color = rgb("#9a6700") // XFAIL — expected/known failure
#let xpass_color = rgb("#8250df") // XPASS — stale marker, needs attention
#let status_badge(s) = {
let c = if s == "PASS" { ok_color }
else if s == "FAIL" { bad_color }
else if s == "XFAIL" { warn_color }
else if s == "XPASS" { xpass_color }
else { neutral }
box(fill: c, inset: (x: 6pt, y: 2pt), radius: 3pt, text(fill: white, weight: "bold", size: 8pt)[#s])
}
#let yesno(b) = {
if b == true { text(fill: ok_color)[] }
else if b == false { text(fill: bad_color)[] }
else { text(fill: neutral)[] }
}
// progression arrow for a regression row
#let trend_arrow(r) = {
if r.delta == none or r.delta == 0 { text(fill: neutral)[] }
else {
let up = r.delta > 0
let good = r.better == true
let c = if good { ok_color } else { bad_color }
let arrow = if up { "▲" } else { "▼" }
text(fill: c)[#arrow #calc.round(r.delta, digits: 4)]
}
}
// ── page setup ───────────────────────────────────────────────────────────────
#set page(
paper: "a4", margin: (x: 1.8cm, y: 1.8cm),
header: align(right, text(size: 8pt, fill: neutral)[Streaming-chain E2E · #meta.git_sha]),
footer: context align(center, text(size: 8pt, fill: neutral)[#counter(page).display("1 / 1", both: true)]),
)
#set text(font: "DejaVu Sans", size: 9.5pt)
#set heading(numbering: "1.1")
#show heading.where(level: 1): it => block(above: 14pt, below: 8pt, text(size: 14pt, weight: "bold", it))
#show heading.where(level: 2): it => block(above: 10pt, below: 6pt, text(size: 11pt, weight: "bold", it))
// ── title ────────────────────────────────────────────────────────────────────
#align(center)[
#text(size: 20pt, weight: "bold")[MARTe2 Streaming-Chain E2E Report] \
#v(2pt)
#text(size: 10pt, fill: neutral)[
#meta.timestamp · commit #raw(meta.git_sha) · target #meta.target ·
run \##data.history_len
]
]
#v(6pt)
#align(center, status_badge(e2e.overall) + h(8pt) + text(size: 11pt)[
E2E #hl.e2e_pass/#hl.e2e_total · Units #hl.unit_pass/#hl.unit_total
#if hl.at("e2e_xfail", default: 0) > 0 [ · #hl.e2e_xfail xfail]
#if hl.at("e2e_xpass", default: 0) > 0 [ · #text(fill: xpass_color)[#hl.e2e_xpass xpass!]]
])
// ── headline KPIs ────────────────────────────────────────────────────────────
#v(10pt)
#let kpi(label, value) = box(width: 100%, inset: 8pt, radius: 4pt, fill: rgb("#f6f8fa"))[
#align(center)[
#text(size: 14pt, weight: "bold")[#value] \
#text(size: 8pt, fill: neutral)[#label]
]
]
#grid(columns: 4, gutter: 6pt,
kpi("E2E passed", [#hl.e2e_pass/#hl.e2e_total]),
kpi("Unit passed", [#hl.unit_pass/#hl.unit_total]),
kpi("Mean sine corr", fnum(hl.mean_corr, digits: 4)),
kpi("Throughput", fnum(hl.mean_throughput_sps, digits: 0, suffix: " sp/s")),
kpi("Python cov", fmt(hl.cov_python, suffix: "%")),
kpi("Go cov", fmt(hl.cov_go, suffix: "%")),
kpi("Mean peak RSS", fnum(hl.mean_peak_rss_mb, digits: 1, suffix: " MB")),
kpi("Mean CPU", fnum(hl.mean_cpu_s, digits: 2, suffix: " s")),
)
// ── progression / regression ─────────────────────────────────────────────────
= Progression / regression
#if data.is_first_run [
_First recorded run baseline established; no previous run to compare against._
] else [
Comparison against the previous run (#text(fill: ok_color)[▲] better,
#text(fill: bad_color)[] worse, unchanged/unavailable).
#v(4pt)
#table(
columns: (1.6fr, 1fr, 1fr, 1.2fr),
align: (left, right, right, right),
stroke: 0.4pt + rgb("#d0d7de"),
inset: 5pt,
table.header([*Metric*], [*Current*], [*Previous*], [*Δ (trend)*]),
..reg.map(r => (
[#r.name],
fnum(r.current, digits: 4),
fnum(r.previous, digits: 4),
trend_arrow(r),
)).flatten()
)
]
// ── unit tests ───────────────────────────────────────────────────────────────
= Unit tests
#table(
columns: (2fr, 0.8fr, 0.8fr, 0.8fr, 0.8fr, 0.8fr, 0.8fr),
align: (left, center, right, right, right, right, right),
stroke: 0.4pt + rgb("#d0d7de"),
inset: 5pt,
table.header([*Suite*], [*Status*], [*Total*], [*Pass*], [*Fail*], [*Skip*], [*Time (s)*]),
..ut.suites.map(s => (
[#s.name],
if not s.avail { status_badge("SKIP") } else if s.ok { status_badge("PASS") } else { status_badge("FAIL") },
[#s.total], [#s.passed], [#s.failed], [#s.skipped], fnum(s.time_s, digits: 2),
)).flatten(),
table.cell(colspan: 2)[*Totals*],
[#ut.totals.total], [#ut.totals.passed], [#ut.totals.failed], [#ut.totals.skipped], [],
)
// ── coverage ─────────────────────────────────────────────────────────────────
= Code coverage
#table(
columns: (1fr, 1fr, 2fr),
align: (left, right, left),
stroke: 0.4pt + rgb("#d0d7de"),
inset: 5pt,
table.header([*Language*], [*Coverage*], [*Source*]),
..cov.languages.map(l => (
[#l.name],
if l.pct == none { text(fill: neutral)[n/a] } else { [#l.pct%] },
text(size: 8pt, fill: neutral)[#l.note],
)).flatten()
)
// per-file C++ detail — the streaming chain's most critical code, so the report
// shows line coverage for every project C++ source (worst-covered first).
#let cpp = cov.languages.find(l => l.name == "C++")
#if cpp != none and cpp.at("files", default: ()).len() > 0 [
== C++ source detail
#let cf = cpp.files
#text(size: 8pt, fill: neutral)[
#cpp.at("lines_hit", default: 0) of #cpp.at("lines_found", default: 0)
lines covered across #cf.len() project files (worst-covered first;
green ≥75%, amber 5075%, red under 50%).
]
#v(4pt)
#let covcell(p) = {
let c = if p == none { neutral }
else if p >= 75 { ok_color }
else if p >= 50 { warn_color }
else { bad_color }
text(fill: c, weight: "bold")[#if p == none { "n/a" } else [#p%]]
}
#table(
columns: (3.2fr, 1fr, 1fr, 1.1fr),
align: (left, right, right, right),
stroke: 0.4pt + rgb("#d0d7de"),
inset: 4pt,
table.header([*Source file*], [*Lines*], [*Hit*], [*Coverage*]),
..cf.map(f => (
text(size: 8pt)[#f.path],
[#f.lines_found],
[#f.lines_hit],
covcell(f.pct),
)).flatten()
)
]
// ── performance ──────────────────────────────────────────────────────────────
= Performance
Per-scenario CPU time and peak resident memory (VmHWM) of the StreamHub and
MARTe2 processes, plus sustained client throughput (recorded samples ÷ duration).
#v(4pt)
#table(
columns: (1.8fr, 1fr, 1fr, 1fr, 1fr, 1.1fr),
align: (left, right, right, right, right, right),
stroke: 0.4pt + rgb("#d0d7de"),
inset: 5pt,
table.header([*Scenario*], [*Hub CPU (s)*], [*Hub RSS (MB)*],
[*MARTe CPU (s)*], [*MARTe RSS (MB)*], [*Throughput (sp/s)*]),
..e2e.scenarios.map(sc => {
let h = sc.perf.at("hub", default: (:))
let m = sc.perf.at("marte", default: (:))
(
[#sc.id],
fnum(h.at("cpu_s", default: none), digits: 2),
fnum(h.at("peak_rss_mb", default: none), digits: 1),
fnum(m.at("cpu_s", default: none), digits: 2),
fnum(m.at("peak_rss_mb", default: none), digits: 1),
fnum(sc.throughput_sps, digits: 0),
)
}).flatten()
)
// ── per-scenario waveform fidelity ───────────────────────────────────────────
= Scenarios
#for sc in e2e.scenarios [
== #sc.id #h(6pt) #status_badge(sc.status)
#if sc.at("desc", default: none) != none [
#v(1pt)
#text(size: 9pt, fill: neutral, style: "italic")[#sc.desc]
#v(2pt)
]
#if sc.at("known_issue", default: none) != none [
#v(2pt)
#box(fill: rgb("#fff8c5"), inset: 5pt, radius: 3pt, width: 100%,
text(size: 8pt, fill: rgb("#7d4e00"))[*Known issue:* #sc.known_issue])
]
#let rb = sc.rollup
Client checks:
live #yesno(rb.at("live_ok", default: none)) ·
zoom #yesno(rb.at("zoom_ok", default: none)) ·
window #yesno(rb.at("window_ok", default: none)) ·
trigger #yesno(rb.at("trigger_ok", default: none))
#if sc.live_frames != none [ · #sc.live_frames live frames]
#v(3pt)
#table(
columns: (1.6fr, 0.6fr, 0.8fr, 0.8fr, 1fr, 0.9fr, 0.9fr, 0.8fr, 0.8fr),
align: (left, center, left, left, right, right, right, center, center),
stroke: 0.4pt + rgb("#d0d7de"),
inset: 4pt,
table.header([*Signal*], [*Pass*], [*Type*], [*Quant*], [*Max abs err*],
[*Corr*], [*nRMSE*], [*Fidelity*], [*Shape*]),
..sc.signals.map(g => (
raw(g.key),
yesno(g.pass),
text(size: 8pt)[#g.type],
text(size: 8pt)[#g.quant],
fnum(g.max_abs_err, digits: 6),
fnum(g.corr, digits: 4),
fnum(g.nrmse, digits: 4),
yesno(g.fidelity_ok),
yesno(g.shape_ok),
)).flatten()
)
// zoom / window / trigger behavioural detail (real WS-driven results)
#let zooms = sc.at("zoom", default: ())
#let win = sc.at("window", default: (:))
#let trigs = sc.at("trigger", default: ())
#if zooms.len() > 0 or win.len() > 0 [
#v(3pt)
#text(size: 8.5pt, weight: "bold")[Zoom / window queries]
#table(
columns: (1fr, 1.2fr, 1fr, 0.8fr, 0.8fr),
align: (left, left, right, right, center),
stroke: 0.4pt + rgb("#d0d7de"),
inset: 4pt,
table.header([*Query*], [*Signal*], [*Span (s)*], [*Pts*], [*OK*]),
..zooms.enumerate().map(((i, z)) => {
let r = z.at("range", default: (0, 0))
(
[zoom #str(i + 1)],
raw(z.at("key", default: "")),
fnum(r.at(1) - r.at(0), digits: 4),
[#z.at("returned", default: 0)],
yesno(z.at("inrange", default: none)),
)
}).flatten(),
..(if win.len() > 0 and win.at("returned", default: 0) > 0 {(
[window #fnum(win.at("windowSec", default: 0), digits: 2)],
raw(win.at("key", default: "")),
fnum(win.at("span", default: 0), digits: 4),
[#win.at("returned", default: 0)],
yesno(win.at("ok", default: none)),
)} else {()}),
)
]
#if trigs.len() > 0 [
#v(3pt)
#text(size: 8.5pt, weight: "bold")[Trigger captures] #h(4pt)
#text(size: 8pt, fill: neutral)[(signal #raw(trigs.at(0).at("key", default: "")))]
#table(
columns: (1fr, 1fr, 0.8fr, 1fr, 1fr, 0.8fr, 0.8fr),
align: (left, left, center, right, right, center, center),
stroke: 0.4pt + rgb("#d0d7de"),
inset: 4pt,
table.header([*Edge*], [*Mode*], [*Fired*], [*Pre/Post (s)*],
[*Capture pts*], [*Win OK*], [*Edge OK*]),
..trigs.map(t => (
text(size: 8pt)[#t.at("edge", default: "")],
text(size: 8pt)[#t.at("mode", default: "")],
yesno(t.at("fired", default: none)),
text(size: 8pt)[#fnum(t.at("preSec", default: 0), digits: 3) / #fnum(t.at("postSec", default: 0), digits: 3)],
[#t.at("capturePts", default: 0)],
yesno(t.at("windowOk", default: none)),
yesno(t.at("edgeOk", default: none)),
)).flatten()
)
]
// embed the waveform overview if report_build.py found one in the artifacts
#if sc.at("wave_img", default: none) != none [
#v(3pt)
#align(center, image(sc.wave_img, width: 92%))
]
#v(6pt)
]
// ── trend plots ──────────────────────────────────────────────────────────────
#if data.trend_plots.len() > 0 [
= Trends over runs
#grid(columns: 2, gutter: 8pt,
..data.trend_plots.map(p => image(p, width: 100%))
)
]
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+177 -10
View File
@@ -44,6 +44,9 @@ var (
durSec = flag.Float64("dur", 4.0, "live recording duration (s)")
timeout = flag.Duration("timeout", 90*time.Second, "overall timeout")
verbose = flag.Bool("v", false, "log every event")
mode = flag.String("mode", "checks", "checks | stress")
reqrate = flag.Float64("reqrate", 0, "stress: sustained zoom requests/sec (0 = liveness only)")
clientID = flag.Int("clientid", 0, "stress: parallel client index (output suffix)")
)
// ── wire types ────────────────────────────────────────────────────────────
@@ -173,13 +176,14 @@ type client struct {
ws *websocket.Conn
deadline time.Time
mu sync.Mutex
sources []sourceInfo
configs map[string][]signalInfo
pushes []*pushFrame
zooms map[uint32]map[string]points
trigSt []string
captures []*captureFrame
mu sync.Mutex
sources []sourceInfo
configs map[string][]signalInfo
pushes []*pushFrame
zooms map[uint32]map[string]points
zoomArrival map[uint32]time.Time
trigSt []string
captures []*captureFrame
readErr error
done chan struct{}
@@ -266,6 +270,7 @@ func (c *client) handle(mt int, data []byte) {
if json.Unmarshal(data, &body) == nil {
c.mu.Lock()
c.zooms[ev.ReqID] = body.Signals
c.zoomArrival[ev.ReqID] = time.Now()
c.mu.Unlock()
}
case "triggerState":
@@ -491,9 +496,10 @@ func main() {
c := &client{
ws: ws, deadline: time.Now().Add(*timeout),
configs: map[string][]signalInfo{},
zooms: map[uint32]map[string]points{},
done: make(chan struct{}),
configs: map[string][]signalInfo{},
zooms: map[uint32]map[string]points{},
zoomArrival: map[uint32]time.Time{},
done: make(chan struct{}),
}
go c.reader()
out := checksOut{Scenario: *scenario}
@@ -522,6 +528,13 @@ func main() {
return true
})
// stress mode: record liveness + sustained zoom latency, then exit. The
// correctness checks below are skipped (a separate gate framework).
if *mode == "stress" {
c.runStress(*scenario, *clientID, *durSec, *reqrate, *outDir)
return
}
// 2. live recording — the reader goroutine accumulates pushes in the
// background, so we simply wait out the recording window (or an early
// reader death).
@@ -647,6 +660,160 @@ func main() {
fmt.Printf("OK chain-client %s: %s + %s\n", *scenario, filepath.Base(recvPath), filepath.Base(cj))
}
// ── stress mode ──────────────────────────────────────────────────────────────
type stressOut struct {
Scenario string `json:"scenario"`
ClientID int `json:"clientId"`
Frames int `json:"frames"`
Signals int `json:"signals"`
Monotonic bool `json:"monotonic"`
WallClock bool `json:"wallclock"`
DurationS float64 `json:"duration"`
ReqRate float64 `json:"reqRate"`
ZoomCount int `json:"zoomCount"`
ZoomFail int `json:"zoomFail"`
ZoomP50ms float64 `json:"zoomP50ms"`
ZoomP95ms float64 `json:"zoomP95ms"`
ZoomMaxms float64 `json:"zoomMaxms"`
Key string `json:"key"`
}
// busiestKey returns the full "src:sig" key carrying the most samples so far.
func (c *client) busiestKey() string {
m := c.merged()
var busy string
bn := 0
for k, p := range m {
if len(p.T) > bn {
bn, busy = len(p.T), k
}
}
return busy
}
// maxTimeFull returns the latest timestamp seen for a full "src:sig" key.
func (c *client) maxTimeFull(full string) (float64, bool) {
c.mu.Lock()
defer c.mu.Unlock()
var mx float64
found := false
for _, f := range c.pushes {
if !strings.HasPrefix(full, f.sourceID+":") {
continue
}
name := full[len(f.sourceID)+1:]
p, ok := f.signals[name]
if !ok {
continue
}
for _, t := range p.T {
if !found || t > mx {
mx, found = t, true
}
}
}
return mx, found
}
// runStress records liveness for the duration while (optionally) issuing zoom
// queries at a sustained rate, measuring round-trip latency. One request is in
// flight at a time, so latency reflects the hub's serialised zoom service time
// under whatever concurrent live/zoom load the matrix imposes.
func (c *client) runStress(scenario string, clientID int, dur, reqrate float64, outDir string) {
if !c.waitFor(20*time.Second, func() bool { return len(c.pushes) > 0 }) {
fatal("stress: no live push within timeout")
}
// brief warmup so the busiest signal and a usable time window exist.
c.waitFor(500*time.Millisecond, func() bool { return false })
key := c.busiestKey()
start := time.Now()
end := start.Add(time.Duration(dur * float64(time.Second)))
var lat []float64
zoomFail := 0
var reqID uint32 = 5000
var interval time.Duration
if reqrate > 0 {
interval = time.Duration(float64(time.Second) / reqrate)
}
for reqrate > 0 && key != "" && time.Now().Before(end) {
tick := time.Now()
t1, ok := c.maxTimeFull(key)
if !ok {
c.waitFor(50*time.Millisecond, func() bool { return false })
continue
}
reqID++
c.send(map[string]interface{}{
"type": "zoom", "reqId": reqID, "t0": t1 - 0.5, "t1": t1,
"n": 300, "signals": key,
})
sendT := time.Now()
got := c.waitFor(3*time.Second, func() bool {
_, ok := c.zoomArrival[reqID]
return ok
})
if got {
c.mu.Lock()
at := c.zoomArrival[reqID]
c.mu.Unlock()
lat = append(lat, at.Sub(sendT).Seconds()*1000.0)
} else {
zoomFail++
}
if rem := interval - time.Since(tick); rem > 0 {
c.waitFor(rem, func() bool { return false })
}
}
// if no reqrate, simply wait out the remaining liveness window.
if reqrate <= 0 {
c.waitFor(time.Until(end), func() bool { return false })
}
m := c.merged()
now := float64(time.Now().UnixNano()) / 1e9
mono, wall := true, true
for _, p := range m {
for i, t := range p.T {
if math.Abs(t-now) > 60.0 {
wall = false
}
if i > 0 && t < p.T[i-1]-1e-9 {
mono = false
}
}
}
sort.Float64s(lat)
pct := func(q float64) float64 {
if len(lat) == 0 {
return 0
}
idx := int(q * float64(len(lat)-1))
return lat[idx]
}
so := stressOut{
Scenario: scenario, ClientID: clientID,
Frames: c.nPushes(), Signals: len(m), Monotonic: mono,
WallClock: wall, DurationS: dur, ReqRate: reqrate, Key: key,
ZoomCount: len(lat), ZoomFail: zoomFail,
ZoomP50ms: pct(0.50), ZoomP95ms: pct(0.95),
}
if len(lat) > 0 {
so.ZoomMaxms = lat[len(lat)-1]
}
path := filepath.Join(outDir,
fmt.Sprintf("stress_%s_c%d.json", scenario, clientID))
b, _ := json.MarshalIndent(so, "", " ")
if err := os.WriteFile(path, b, 0o644); err != nil {
fatal("write stress: %v", err)
}
log.Printf("stress: frames=%d signals=%d zoom n=%d fail=%d p50=%.1fms p95=%.1fms max=%.1fms",
so.Frames, so.Signals, so.ZoomCount, so.ZoomFail, so.ZoomP50ms, so.ZoomP95ms, so.ZoomMaxms)
fmt.Printf("OK stress %s c%d: %s\n", scenario, clientID, filepath.Base(path))
}
// runTrigger configures one edge/mode trigger, arms it, and records the result.
func (c *client) runTrigger(key, edge, mode string, thr float64) trigCheck {
tc := trigCheck{Edge: edge, Mode: mode, Key: key}
+153
View File
@@ -0,0 +1,153 @@
package main
import (
"encoding/binary"
"math"
"testing"
)
// ── frame builders (mirror the StreamHub wire format) ───────────────────────
func putf(b []byte, v float64) []byte {
var t [8]byte
binary.LittleEndian.PutUint64(t[:], math.Float64bits(v))
return append(b, t[:]...)
}
func putSignals(b []byte, sigs map[string]points) []byte {
var n [4]byte
binary.LittleEndian.PutUint32(n[:], uint32(len(sigs)))
b = append(b, n[:]...)
for k, p := range sigs {
var kl [2]byte
binary.LittleEndian.PutUint16(kl[:], uint16(len(k)))
b = append(b, kl[:]...)
b = append(b, []byte(k)...)
var cnt [4]byte
binary.LittleEndian.PutUint32(cnt[:], uint32(len(p.T)))
b = append(b, cnt[:]...)
for _, t := range p.T {
b = putf(b, t)
}
for _, v := range p.V {
b = putf(b, v)
}
}
return b
}
func buildPush(srcID string, sigs map[string]points) []byte {
b := []byte{1, byte(len(srcID))}
b = append(b, []byte(srcID)...)
return putSignals(b, sigs)
}
func buildCapture(trigTime, preSec, postSec float64, sigs map[string]points) []byte {
b := []byte{2}
b = putf(b, trigTime)
b = putf(b, preSec)
b = putf(b, postSec)
return putSignals(b, sigs)
}
// ── tests ───────────────────────────────────────────────────────────────────
func TestParsePushRoundTrip(t *testing.T) {
in := map[string]points{"Sine": {T: []float64{0, 0.1, 0.2}, V: []float64{1, 2, 3}}}
f, err := parsePush(buildPush("src", in))
if err != nil {
t.Fatalf("parsePush: %v", err)
}
if f.sourceID != "src" {
t.Errorf("sourceID = %q, want src", f.sourceID)
}
got := f.signals["Sine"]
if len(got.T) != 3 || got.T[2] != 0.2 || got.V[1] != 2 {
t.Errorf("bad payload: %+v", got)
}
}
func TestParsePushRejectsWrongVersion(t *testing.T) {
if _, err := parsePush([]byte{2, 0, 0}); err == nil {
t.Error("expected error on non-v1 frame")
}
}
func TestParsePushTruncated(t *testing.T) {
full := buildPush("src", map[string]points{"A": {T: []float64{0}, V: []float64{1}}})
if _, err := parsePush(full[:len(full)-4]); err == nil {
t.Error("expected truncation error")
}
}
func TestParseCaptureRoundTrip(t *testing.T) {
in := map[string]points{"Sig": {T: []float64{1, 2}, V: []float64{9, 8}}}
f, err := parseCapture(buildCapture(100.5, 0.02, 0.08, in))
if err != nil {
t.Fatalf("parseCapture: %v", err)
}
if f.trigTime != 100.5 || f.preSec != 0.02 || f.postSec != 0.08 {
t.Errorf("header = %v/%v/%v", f.trigTime, f.preSec, f.postSec)
}
if g := f.signals["Sig"]; len(g.T) != 2 || g.V[0] != 9 {
t.Errorf("bad capture payload: %+v", g)
}
}
func TestParseCaptureRejectsWrongVersion(t *testing.T) {
if _, err := parseCapture([]byte{1, 0, 0, 0}); err == nil {
t.Error("expected error on non-v2 frame")
}
}
func TestMergedSortsAndDedups(t *testing.T) {
c := &client{
configs: map[string][]signalInfo{},
zooms: map[uint32]map[string]points{},
}
// two pushes, out of order, with one duplicate timestamp
c.pushes = []*pushFrame{
{sourceID: "s", signals: map[string]points{"A": {T: []float64{0.2, 0.1}, V: []float64{2, 1}}}},
{sourceID: "s", signals: map[string]points{"A": {T: []float64{0.1, 0.3}, V: []float64{1, 3}}}},
}
m := c.merged()
got := m["s:A"]
want := []float64{0.1, 0.2, 0.3}
if len(got.T) != len(want) {
t.Fatalf("len = %d, want %d (%+v)", len(got.T), len(want), got)
}
for i := range want {
if got.T[i] != want[i] {
t.Errorf("t[%d] = %v, want %v", i, got.T[i], want[i])
}
}
}
func TestEdgeCrosses(t *testing.T) {
rising := points{T: []float64{0, 1, 2}, V: []float64{-1, 1, 2}}
if !edgeCrosses(rising, 0.5, "rising", 0.0) {
t.Error("rising cross not detected")
}
if edgeCrosses(rising, 0.5, "falling", 0.0) {
t.Error("false falling detection on rising data")
}
falling := points{T: []float64{0, 1}, V: []float64{1, -1}}
if !edgeCrosses(falling, 0.5, "falling", 0.0) {
t.Error("falling cross not detected")
}
if !edgeCrosses(falling, 0.5, "both", 0.0) {
t.Error("both should match a falling edge")
}
}
func TestHas(t *testing.T) {
if !has("live,zoom,trigger", "zoom") {
t.Error("has should find zoom")
}
if has("live, window", "trigger") {
t.Error("has should not find trigger")
}
if !has("live, window", "window") {
t.Error("has should trim spaces")
}
}
+287
View File
@@ -0,0 +1,287 @@
#!/usr/bin/env python3
"""
collect.py — Run the unit-test suites and gather coverage for the E2E report.
Produces two JSON artifacts in --out:
* ``unit_tests.json`` — per-suite {total, passed, failed, skipped, time_s, ok}
for the C++ GTest binary, the Go chain-client tests, and the Python
framework tests, plus grand totals.
* ``coverage.json`` — per-language {pct, avail, note} for Python (coverage.py),
Go (``go test -cover``) and C++ (lcov, best-effort: only when the build was
instrumented with .gcno files; otherwise reported unavailable).
Each suite is isolated: a missing toolchain or a failing suite is recorded, never
fatal, so the report always renders. Requires the MARTe env (LD_LIBRARY_PATH) to
already be exported for the GTest binary — the orchestrator does this.
"""
import argparse
import json
import os
import re
import subprocess
import sys
import xml.etree.ElementTree as ET
def _run(cmd, cwd=None, env=None, timeout=600):
try:
p = subprocess.run(cmd, cwd=cwd, env=env, timeout=timeout,
capture_output=True, text=True)
return p.returncode, p.stdout, p.stderr
except (subprocess.TimeoutExpired, FileNotFoundError, OSError) as e:
return 127, "", str(e)
# ── C++ GTest ─────────────────────────────────────────────────────────────────
def gtest_suite(gtest_bin, work):
s = {"name": "C++ GTest", "lang": "cpp", "total": 0, "passed": 0,
"failed": 0, "skipped": 0, "time_s": 0.0, "ok": False, "avail": False}
if not gtest_bin or not os.path.exists(gtest_bin):
s["detail"] = "GTest binary not found"
return s
xml_p = os.path.join(work, "gtest.xml")
rc, out, err = _run([gtest_bin, f"--gtest_output=xml:{xml_p}"], timeout=900)
s["avail"] = True
if os.path.exists(xml_p):
try:
root = ET.parse(xml_p).getroot()
s["total"] = int(root.get("tests", 0))
s["failed"] = int(root.get("failures", 0)) + int(root.get("errors", 0))
s["skipped"] = int(root.get("disabled", 0)) + int(root.get("skipped", 0))
s["time_s"] = float(root.get("time", 0.0))
s["passed"] = s["total"] - s["failed"] - s["skipped"]
s["ok"] = s["failed"] == 0 and s["total"] > 0
except (ET.ParseError, ValueError) as e:
s["detail"] = f"xml parse: {e}"
else:
s["detail"] = (err or out or "no xml produced")[-200:]
return s
# ── Go ────────────────────────────────────────────────────────────────────────
def go_suite(client_dir, work):
s = {"name": "Go (chain-client)", "lang": "go", "total": 0, "passed": 0,
"failed": 0, "skipped": 0, "time_s": 0.0, "ok": False, "avail": False}
cov_p = os.path.join(work, "go_cover.out")
rc, out, err = _run(["go", "test", "-json", f"-coverprofile={cov_p}", "./..."],
cwd=client_dir)
if rc == 127:
s["detail"] = "go toolchain not found"
return s
s["avail"] = True
cov_pct = None
for line in out.splitlines():
try:
ev = json.loads(line)
except json.JSONDecodeError:
continue
act = ev.get("Action")
if act == "pass" and ev.get("Test"):
s["passed"] += 1
s["total"] += 1
elif act == "fail" and ev.get("Test"):
s["failed"] += 1
s["total"] += 1
elif act == "skip" and ev.get("Test"):
s["skipped"] += 1
s["total"] += 1
elif act == "output":
m = re.search(r"coverage:\s+([\d.]+)%", ev.get("Output", ""))
if m:
cov_pct = float(m.group(1))
s["ok"] = s["failed"] == 0 and s["total"] > 0
s["cov_pct"] = cov_pct
return s
# ── Python ──────────────────────────────────────────────────────────────────
def py_suite(chain_dir, work):
s = {"name": "Python (framework)", "lang": "python", "total": 0, "passed": 0,
"failed": 0, "skipped": 0, "time_s": 0.0, "ok": False, "avail": True}
cov_json = os.path.join(work, "py_cover.json")
env = dict(os.environ)
env["COVERAGE_FILE"] = os.path.join(work, ".coverage")
have_cov = _run(["coverage", "--version"])[0] == 0
if have_cov:
cmd = ["coverage", "run", f"--source={chain_dir}", "-m", "unittest",
"tests_py", "-v"]
else:
cmd = [sys.executable, "-m", "unittest", "tests_py", "-v"]
rc, out, err = _run(cmd, cwd=chain_dir, env=env)
text = out + "\n" + err
m = re.search(r"Ran (\d+) tests? in ([\d.]+)s", text)
if m:
s["total"] = int(m.group(1))
s["time_s"] = float(m.group(2))
fm = re.search(r"failures=(\d+)", text)
em = re.search(r"errors=(\d+)", text)
sm = re.search(r"skipped=(\d+)", text)
s["failed"] = (int(fm.group(1)) if fm else 0) + (int(em.group(1)) if em else 0)
s["skipped"] = int(sm.group(1)) if sm else 0
s["passed"] = s["total"] - s["failed"] - s["skipped"]
s["ok"] = s["failed"] == 0 and s["total"] > 0
pct = None
if have_cov:
_run(["coverage", "json", "-o", cov_json], cwd=chain_dir, env=env)
if os.path.exists(cov_json):
try:
cj = json.load(open(cov_json))
pct = round(cj["totals"]["percent_covered"], 1)
except (KeyError, ValueError):
pass
s["cov_pct"] = pct
return s
# ── C++ coverage (best-effort) ────────────────────────────────────────────────
def _parse_lcov_info(path, repo):
"""Parse an LCOV tracefile into per-file line/function coverage.
Returns (files, totals) where ``files`` is a list of
{path (repo-relative), lines_found, lines_hit, pct, funcs_found,
funcs_hit} sorted worst-covered first, and ``totals`` aggregates the
same line counts across all files. Only the line counters (DA/LF/LH)
and function counters (FNF/FNH) are read; branch data is ignored.
"""
files = []
cur = None
repo_abs = os.path.abspath(repo) + os.sep
try:
fh = open(path)
except OSError:
return [], {"lines_found": 0, "lines_hit": 0, "pct": None}
with fh:
for line in fh:
line = line.rstrip("\n")
if line.startswith("SF:"):
src = line[3:]
rel = src[len(repo_abs):] if src.startswith(repo_abs) else src
cur = {"path": rel, "lines_found": 0, "lines_hit": 0,
"funcs_found": 0, "funcs_hit": 0}
elif cur is None:
continue
elif line.startswith("LF:"):
cur["lines_found"] = int(line[3:] or 0)
elif line.startswith("LH:"):
cur["lines_hit"] = int(line[3:] or 0)
elif line.startswith("FNF:"):
cur["funcs_found"] = int(line[4:] or 0)
elif line.startswith("FNH:"):
cur["funcs_hit"] = int(line[4:] or 0)
elif line == "end_of_record":
lf = cur["lines_found"]
cur["pct"] = round(100.0 * cur["lines_hit"] / lf, 1) if lf else None
files.append(cur)
cur = None
tot_f = sum(f["lines_found"] for f in files)
tot_h = sum(f["lines_hit"] for f in files)
totals = {"lines_found": tot_f, "lines_hit": tot_h,
"pct": round(100.0 * tot_h / tot_f, 1) if tot_f else None}
files.sort(key=lambda f: (f["pct"] if f["pct"] is not None else 101.0,
f["path"]))
return files, totals
def cpp_coverage(repo, target):
cov = {"name": "C++", "avail": False, "pct": None, "files": [],
"note": "not instrumented (rebuild with --cpp-coverage)"}
build = os.path.join(repo, "Build", target)
gcno = []
for root, _, files in os.walk(build):
for fn in files:
if fn.endswith(".gcno"):
gcno.append(os.path.join(root, fn))
if not gcno:
return cov
# lcov 2.x is strict by default; tolerate the benign mismatches that arise
# from mixing instrumented project objects with non-instrumented MARTe2/STL
# headers, and never let a single bad file abort the whole capture.
ign = ["--ignore-errors",
"mismatch,source,gcov,unused,empty,negative,unsupported,inconsistent"]
raw = os.path.join(build, "coverage_raw.info")
rc, out, err = _run(["lcov", "--capture", "--directory", build,
"--output-file", raw, "--quiet"] + ign, timeout=900)
if rc != 0 or not os.path.exists(raw):
cov["note"] = "lcov capture failed: " + (err or out or "")[-160:]
return cov
# Keep only this repo's own sources so the number reflects project code,
# not the MARTe2 framework headers dragged in by templates/inlines.
info = os.path.join(build, "coverage.info")
rc2, _, e2 = _run(["lcov", "--extract", raw,
os.path.join(repo, "Source", "*"),
os.path.join(repo, "Test", "*"),
"--output-file", info, "--quiet"] + ign, timeout=300)
summ_file = info if (rc2 == 0 and os.path.exists(info)) else raw
# Parse the tracefile directly for per-file detail; this also yields the
# aggregate so the headline number and the per-file table are consistent.
fdetail, totals = _parse_lcov_info(summ_file, repo)
if totals["pct"] is not None:
cov.update(avail=True, pct=totals["pct"], files=fdetail,
lines_found=totals["lines_found"],
lines_hit=totals["lines_hit"],
note="lcov (project sources)" if summ_file == info else "lcov")
return cov
# Fall back to lcov --summary if the tracefile had no parseable line data.
rc3, summ, _ = _run(["lcov", "--summary", summ_file] + ign)
m = re.search(r"lines[.\s]+:\s+([\d.]+)%", summ)
if m:
cov.update(avail=True, pct=float(m.group(1)),
note="lcov (project sources)" if summ_file == info else "lcov")
else:
cov["note"] = "lcov summary unparsed"
return cov
def main():
p = argparse.ArgumentParser(description="Collect unit tests + coverage")
p.add_argument("--repo", required=True)
p.add_argument("--target", default="x86-linux")
p.add_argument("--out", required=True)
p.add_argument("--work", default=None,
help="scratch dir for *.xml/cover files (default: --out)")
p.add_argument("--cpp-coverage", action="store_true")
args = p.parse_args()
os.makedirs(args.out, exist_ok=True)
work = args.work or args.out
os.makedirs(work, exist_ok=True)
chain_dir = os.path.dirname(os.path.abspath(__file__))
client_dir = os.path.join(chain_dir, "client")
gtest_bin = os.path.join(args.repo, "Build", args.target, "GTest", "MainGTest.ex")
suites = [gtest_suite(gtest_bin, work),
go_suite(client_dir, work),
py_suite(chain_dir, work)]
totals = {k: sum(s.get(k, 0) for s in suites)
for k in ("total", "passed", "failed", "skipped")}
ut = {"suites": suites, "totals": totals,
"ok": all(s["ok"] for s in suites if s["avail"])}
with open(os.path.join(args.out, "unit_tests.json"), "w") as f:
json.dump(ut, f, indent=2)
langs = []
py = next(s for s in suites if s["lang"] == "python")
go = next(s for s in suites if s["lang"] == "go")
langs.append({"name": "Python", "avail": py.get("cov_pct") is not None,
"pct": py.get("cov_pct"), "note": "coverage.py"})
langs.append({"name": "Go", "avail": go.get("cov_pct") is not None,
"pct": go.get("cov_pct"), "note": "go test -cover"})
cpp = cpp_coverage(args.repo, args.target) if args.cpp_coverage else \
{"name": "C++", "avail": False, "pct": None, "note": "skipped (use --cpp-coverage)"}
langs.append(cpp)
with open(os.path.join(args.out, "coverage.json"), "w") as f:
json.dump({"languages": langs}, f, indent=2)
print(f"unit_tests: {totals['passed']}/{totals['total']} pass "
f"({totals['failed']} fail, {totals['skipped']} skip)")
print("coverage: " + ", ".join(
f"{l['name']}={l['pct']}%" if l["pct"] is not None else f"{l['name']}=n/a"
for l in langs))
if __name__ == "__main__":
main()
+3 -1
View File
@@ -90,6 +90,8 @@ def write_marte_cfg(scenario, path, input_bin, tap_bin=None):
srcs = scenario["sources"]
want_tap = scenario["oracle"] in ("fed", "both") and tap_bin is not None
_row_dt, _num_rows, producer_hz, _loop_hz = S.geometry(scenario)
gams = [] # +Functions entries
datas = [] # +Data entries
thread_funcs = ["TimerGAM"]
@@ -98,7 +100,7 @@ def write_marte_cfg(scenario, path, input_bin, tap_bin=None):
gams.append(
" +TimerGAM = { Class = IOGAM "
"InputSignals = { Counter = { DataSource = ReaderTimer Type = uint32 } "
f"Time = {{ Frequency = {PRODUCER_HZ} DataSource = ReaderTimer Type = uint32 }} }} "
f"Time = {{ Frequency = {producer_hz} DataSource = ReaderTimer Type = uint32 }} }} "
"OutputSignals = { Counter = { DataSource = DDB Type = uint32 } "
"Time = { DataSource = DDB Type = uint32 } } }")
+17 -15
View File
@@ -51,19 +51,19 @@ NUM_ROWS = S.NUM_ROWS # producer cycles written to the FileReader input
ROW_DT = S.ROW_DT # seconds per producer cycle (row); 1 kHz producer
def _sample_dt(sig):
def _sample_dt(sig, row_dt=ROW_DT):
"""Per-element time spacing (s) for a signal."""
if sig["sampling_rate"]:
return 1.0 / sig["sampling_rate"]
e = sig["elements"]
return ROW_DT / e if e > 1 else ROW_DT
return row_dt / e if e > 1 else row_dt
def _sample_times(sig):
"""Flattened intended sample times (s): NUM_ROWS*elements values."""
def _sample_times(sig, row_dt=ROW_DT, num_rows=NUM_ROWS):
"""Flattened intended sample times (s): num_rows*elements values."""
e = sig["elements"]
sdt = _sample_dt(sig)
rows = np.arange(NUM_ROWS, dtype=np.float64).reshape(-1, 1) * ROW_DT
sdt = _sample_dt(sig, row_dt)
rows = np.arange(num_rows, dtype=np.float64).reshape(-1, 1) * row_dt
cols = np.arange(e, dtype=np.float64).reshape(1, -1) * sdt
return (rows + cols).reshape(-1)
@@ -99,15 +99,16 @@ def _native(sig, vals):
def build_ground_truth(scenario):
"""Return {"<src>:<sig>": gt_dict} for every signal in the scenario."""
row_dt, num_rows, _ph, _lh = S.geometry(scenario)
gt = {}
for src in scenario["sources"]:
for sig in src["signals"]:
t = _sample_times(sig)
t = _sample_times(sig, row_dt, num_rows)
v = _native(sig, _values(sig, t))
gt[f"{src['id']}:{sig['name']}"] = {
"t": t, "v": v, "dt": _sample_dt(sig),
"t": t, "v": v, "dt": _sample_dt(sig, row_dt),
"formula": sig["formula"], "freq": sig["freq"],
"elements": sig["elements"], "rows": NUM_ROWS,
"elements": sig["elements"], "rows": num_rows,
"type": sig["type"], "quant": sig["quant"],
"range_min": sig["range_min"], "range_max": sig["range_max"],
"is_time": sig["is_time"],
@@ -126,25 +127,26 @@ def write_input(scenario, path):
"""
os.makedirs(os.path.dirname(os.path.abspath(path)), exist_ok=True)
gt = build_ground_truth(scenario)
row_dt, num_rows, _ph, _lh = S.geometry(scenario)
# write each source to its own file: <path> for src[0], <path>.<srcid> else.
written = {}
for i, src in enumerate(scenario["sources"]):
p = path if i == 0 else f"{path}.{src['id']}"
_write_source_bin(src, p)
_write_source_bin(src, p, row_dt, num_rows)
written[src["id"]] = p
gt["_files"] = written
return gt
def _write_source_bin(src, path):
def _write_source_bin(src, path, row_dt=ROW_DT, num_rows=NUM_ROWS):
sigs = src["signals"]
# per-signal native 2D arrays [NUM_ROWS, elements]
# per-signal native 2D arrays [num_rows, elements]
cols = {}
for sig in sigs:
t = _sample_times(sig)
t = _sample_times(sig, row_dt, num_rows)
v = _native(sig, _values(sig, t))
cols[sig["name"]] = v.reshape(NUM_ROWS, sig["elements"])
cols[sig["name"]] = v.reshape(num_rows, sig["elements"])
with open(path, "wb") as f:
f.write(struct.pack("<I", len(sigs)))
@@ -153,7 +155,7 @@ def _write_source_bin(src, path):
name = (sig["name"] + "\0").encode()
f.write((name + b"\0" * 32)[:32])
f.write(struct.pack("<I", sig["elements"]))
for r in range(NUM_ROWS):
for r in range(num_rows):
for sig in sigs:
f.write(cols[sig["name"]][r].tobytes())
+76
View File
@@ -0,0 +1,76 @@
#!/usr/bin/env python3
"""
proc_perf.py — Snapshot a live process's CPU time and peak memory from /proc.
Usage: proc_perf.py <pid> <label> <out.json>
Reads /proc/<pid>/stat (utime+stime, in clock ticks) and /proc/<pid>/status
(VmHWM = peak resident set, VmRSS = current) *while the process is alive* — peak
RSS is only legible before the process exits, so the orchestrator calls this just
before tearing the stack down. Emits a small JSON record:
{"label": ..., "avail": true, "cpu_s": float, "peak_rss_kb": int,
"rss_kb": int, "threads": int}
If the process is already gone, writes {"label": ..., "avail": false}.
"""
import json
import os
import sys
def _clk_tck():
try:
return os.sysconf("SC_CLK_TCK") or 100
except (ValueError, OSError):
return 100
def snapshot(pid):
stat_p = f"/proc/{pid}/stat"
status_p = f"/proc/{pid}/status"
if not os.path.exists(stat_p):
return {"avail": False}
try:
with open(stat_p) as f:
raw = f.read()
# The comm field is parenthesised and may contain spaces/parens, so split
# on the final ')': everything after it is space-separated, with state as
# the first token. utime/stime are overall fields 14/15 → indices 11/12
# of the post-')' tokens (pid + comm consumed before the split).
after = raw[raw.rindex(")") + 1:].split()
utime = int(after[11])
stime = int(after[12])
cpu_s = (utime + stime) / float(_clk_tck())
rec: dict = {"avail": True, "cpu_s": cpu_s}
with open(status_p) as f:
for line in f:
if line.startswith("VmHWM:"):
rec["peak_rss_kb"] = int(line.split()[1])
elif line.startswith("VmRSS:"):
rec["rss_kb"] = int(line.split()[1])
elif line.startswith("Threads:"):
rec["threads"] = int(line.split()[1])
return rec
except (OSError, ValueError, IndexError):
return {"avail": False}
def main():
if len(sys.argv) != 4:
print("usage: proc_perf.py <pid> <label> <out.json>", file=sys.stderr)
sys.exit(2)
pid, label, out = sys.argv[1], sys.argv[2], sys.argv[3]
rec = snapshot(pid)
rec["label"] = label
with open(out, "w") as f:
json.dump(rec, f)
if rec.get("avail"):
print(f"perf {label}: cpu={rec.get('cpu_s', 0):.2f}s "
f"peakRSS={rec.get('peak_rss_kb', 0)/1024:.1f}MB")
else:
print(f"perf {label}: unavailable (process gone)")
if __name__ == "__main__":
main()
+306
View File
@@ -0,0 +1,306 @@
#!/usr/bin/env python3
"""
report_build.py — Consolidate the E2E run into report_data.json (+ trend plots).
Inputs (paths via flags):
* results.json — per-scenario status + waveform metrics (orchestrator)
* perf_<id>_*.json — per-scenario CPU/peak-RSS snapshots (proc_perf.py)
* unit_tests.json — GTest/Go/Python suite results (collect.py)
* coverage.json — per-language coverage (collect.py)
Outputs (into --out):
* report_data.json — everything the Typst template renders, including a
``regression`` block that diffs this run's headline metrics against the
previous entry in history.jsonl (progression ▲ / regression ▼).
* history.jsonl — appended one line of headline metrics per run.
* trend_*.png — pass-rate / coverage / fidelity / memory over runs.
Throughput is derived as recorded-samples / recording-duration. Memory is the
peak resident set (VmHWM). All inputs are optional: a missing artifact degrades
to nulls so a partial run still produces a report.
"""
import argparse
import datetime
import json
import os
import subprocess
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt # noqa: E402
REC_DUR_S = 4.0 # client -dur; samples/sec denominator
def _load(path, default=None):
if path and os.path.exists(path):
try:
return json.load(open(path))
except (ValueError, OSError):
return default
return default
def _git_sha(repo):
try:
return subprocess.run(["git", "rev-parse", "--short", "HEAD"], cwd=repo,
capture_output=True, text=True, timeout=10).stdout.strip()
except (subprocess.SubprocessError, OSError):
return "unknown"
def _scenario_perf(work, sid):
out = {}
for role in ("hub", "marte"):
rec = _load(os.path.join(work, f"perf_{sid}_{role}.json"), {})
if rec and rec.get("avail"):
out[role] = {"cpu_s": round(rec.get("cpu_s", 0.0), 3),
"peak_rss_mb": round(rec.get("peak_rss_kb", 0) / 1024.0, 1),
"threads": rec.get("threads")}
return out
def _scenario_descs():
"""id -> human description, imported from the scenario matrix (best effort)."""
try:
from scenarios import SCENARIOS
return {s["id"]: s.get("desc") for s in SCENARIOS}
except Exception:
return {}
def build_e2e(results, work):
descs = _scenario_descs()
scen = []
corrs, rss_vals, cpu_vals, tput_vals = [], [], [], []
for r in results.get("scenarios", []):
sid = r["id"]
metrics = r.get("metrics", {})
sigs = []
nrecv_total = 0
for key, m in (metrics.get("signals", {}) or {}).items():
nrecv_total += int(m.get("n_recv", 0) or 0)
if "corr" in m:
corrs.append(m["corr"])
sigs.append({
"key": key, "pass": m.get("pass"),
"type": m.get("type"), "quant": m.get("quant"),
"max_abs_err": m.get("max_abs_err"),
"corr": m.get("corr"), "nrmse": m.get("nrmse"),
"fidelity_ok": m.get("fidelity_ok"), "shape_ok": m.get("shape_ok"),
"n_recv": m.get("n_recv"),
})
perf = _scenario_perf(work, sid)
for role in perf.values():
if role.get("peak_rss_mb"):
rss_vals.append(role["peak_rss_mb"])
if role.get("cpu_s"):
cpu_vals.append(role["cpu_s"])
tput = round(nrecv_total / REC_DUR_S, 1) if nrecv_total else 0.0
if tput:
tput_vals.append(tput)
client = metrics.get("client", {}) or {}
# waveform overview image (plots.py writes it into --work); record the
# basename only when present so the Typst template can embed it without
# tripping over a missing file (Typst read() throws on absence).
wave_img = f"wave_{sid}.png"
has_wave = os.path.exists(os.path.join(work, wave_img))
scen.append({
"id": sid, "status": r.get("status"),
"desc": descs.get(sid),
"known_issue": r.get("known_issue"),
"signals": sigs, "perf": perf, "throughput_sps": tput,
"live_frames": (client.get("live", {}) or {}).get("frames"),
"rollup": client.get("_rollup", {}),
# detailed client behavioural checks (chain-client checks_<id>.json),
# surfaced so the report can show real zoom ranges + trigger captures
# rather than only the pass/fail rollup booleans.
"zoom": client.get("zoom", []) or [],
"window": client.get("window", {}) or {},
"trigger": client.get("trigger", []) or [],
"wave_img": wave_img if has_wave else None,
})
agg = {
"mean_corr": round(sum(corrs) / len(corrs), 4) if corrs else None,
"mean_peak_rss_mb": round(sum(rss_vals) / len(rss_vals), 1) if rss_vals else None,
"mean_cpu_s": round(sum(cpu_vals) / len(cpu_vals), 3) if cpu_vals else None,
"mean_throughput_sps": round(sum(tput_vals) / len(tput_vals), 1) if tput_vals else None,
}
npass = sum(1 for s in scen if s["status"] == "PASS")
nfail = sum(1 for s in scen if s["status"] == "FAIL")
nskip = sum(1 for s in scen if s["status"] == "SKIP")
nxfail = sum(1 for s in scen if s["status"] == "XFAIL")
nxpass = sum(1 for s in scen if s["status"] == "XPASS")
return {
"overall": results.get("overall", "FAIL"),
"n_pass": npass, "n_fail": nfail, "n_skip": nskip,
"n_xfail": nxfail, "n_xpass": nxpass,
"scenarios": scen, "agg": agg,
}
def headline(e2e, ut, cov):
cov_by = {c["name"]: c.get("pct") for c in cov.get("languages", [])}
t = ut.get("totals", {})
return {
"e2e_pass": e2e["n_pass"], "e2e_fail": e2e["n_fail"],
"e2e_xfail": e2e.get("n_xfail", 0), "e2e_xpass": e2e.get("n_xpass", 0),
"e2e_total": (e2e["n_pass"] + e2e["n_fail"] + e2e["n_skip"]
+ e2e.get("n_xfail", 0) + e2e.get("n_xpass", 0)),
"unit_pass": t.get("passed", 0), "unit_fail": t.get("failed", 0),
"unit_total": t.get("total", 0),
"cov_python": cov_by.get("Python"), "cov_go": cov_by.get("Go"),
"cov_cpp": cov_by.get("C++"),
"mean_corr": e2e["agg"]["mean_corr"],
"mean_peak_rss_mb": e2e["agg"]["mean_peak_rss_mb"],
"mean_cpu_s": e2e["agg"]["mean_cpu_s"],
"mean_throughput_sps": e2e["agg"]["mean_throughput_sps"],
}
# field → "higher is better" (True), "lower is better" (False)
_DIRECTION = {
"e2e_pass": True, "e2e_fail": False, "unit_pass": True, "unit_fail": False,
"cov_python": True, "cov_go": True, "cov_cpp": True, "mean_corr": True,
"mean_peak_rss_mb": False, "mean_cpu_s": False, "mean_throughput_sps": True,
}
_LABELS = {
"e2e_pass": "E2E scenarios passed", "e2e_fail": "E2E scenarios failed",
"unit_pass": "Unit tests passed", "unit_fail": "Unit tests failed",
"cov_python": "Python coverage %", "cov_go": "Go coverage %",
"cov_cpp": "C++ coverage %", "mean_corr": "Mean sine corr",
"mean_peak_rss_mb": "Mean peak RSS (MB)", "mean_cpu_s": "Mean CPU (s)",
"mean_throughput_sps": "Mean throughput (samp/s)",
}
def regression(curr, prev):
rows = []
for k, label in _LABELS.items():
c = curr.get(k)
p = prev.get(k) if prev else None
better = None
delta = None
if isinstance(c, (int, float)) and isinstance(p, (int, float)):
delta = round(c - p, 4)
if delta == 0:
better = None
else:
better = (delta > 0) == _DIRECTION[k]
rows.append({"name": label, "key": k, "current": c, "previous": p,
"delta": delta, "better": better,
"higher_better": _DIRECTION[k]})
return rows
def trend_plots(history, out):
if not history:
return []
xs = list(range(len(history)))
labels = [h.get("ts_short", str(i)) for i, h in enumerate(history)]
made = []
def _plot(fname, series, title, ylabel):
ys = [[h.get(k) for h in history] for _, k in series]
if all(all(v is None for v in y) for y in ys):
return
fig, ax = plt.subplots(figsize=(7, 3))
for (lbl, _), y in zip(series, ys):
xp = [x for x, v in zip(xs, y) if v is not None]
yp = [v for v in y if v is not None]
if yp:
ax.plot(xp, yp, "o-", label=lbl)
ax.set_title(title)
ax.set_ylabel(ylabel)
ax.set_xticks(xs)
ax.set_xticklabels(labels, rotation=45, ha="right", fontsize=7)
ax.grid(alpha=0.3)
ax.legend(fontsize=8)
fig.tight_layout()
p = os.path.join(out, fname)
fig.savefig(p, dpi=110)
plt.close(fig)
made.append(p)
_plot("trend_tests.png",
[("E2E pass", "e2e_pass"), ("Unit pass", "unit_pass")],
"Passing tests over runs", "count")
_plot("trend_coverage.png",
[("Python", "cov_python"), ("Go", "cov_go"), ("C++", "cov_cpp")],
"Code coverage over runs", "% covered")
_plot("trend_fidelity.png", [("Mean sine corr", "mean_corr")],
"Waveform fidelity over runs", "correlation")
_plot("trend_perf.png",
[("Peak RSS (MB)", "mean_peak_rss_mb"), ("CPU (s)", "mean_cpu_s")],
"Resource use over runs", "value")
return made
def main():
ap = argparse.ArgumentParser(description="Build E2E report_data.json")
ap.add_argument("--repo", required=True)
ap.add_argument("--results", required=True)
ap.add_argument("--work", required=True)
ap.add_argument("--out", required=True)
args = ap.parse_args()
os.makedirs(args.out, exist_ok=True)
results = _load(args.results, {"overall": "FAIL", "scenarios": []})
ut = _load(os.path.join(args.out, "unit_tests.json"), {"suites": [], "totals": {}})
cov = _load(os.path.join(args.out, "coverage.json"), {"languages": []})
e2e = build_e2e(results, args.work)
now = datetime.datetime.now()
meta = {"timestamp": now.isoformat(timespec="seconds"),
"ts_short": now.strftime("%m-%d %H:%M"),
"git_sha": _git_sha(args.repo), "target": "x86-linux"}
hl = headline(e2e, ut, cov)
# history: read previous, then append current
hist_path = os.path.join(args.out, "history.jsonl")
history = []
if os.path.exists(hist_path):
for line in open(hist_path):
line = line.strip()
if line:
try:
history.append(json.loads(line))
except ValueError:
pass
prev = history[-1] if history else None
reg = regression(hl, prev)
entry = dict(hl)
entry["timestamp"] = meta["timestamp"]
entry["ts_short"] = meta["ts_short"]
entry["git_sha"] = meta["git_sha"]
entry["overall"] = e2e["overall"]
with open(hist_path, "a") as f:
f.write(json.dumps(entry) + "\n")
history.append(entry)
plots = [os.path.basename(p) for p in trend_plots(history, args.out)]
doc = {
"meta": meta, "e2e": e2e, "unit_tests": ut,
"coverage": cov, "regression": reg, "headline": hl,
"trend_plots": plots, "history_len": len(history),
"is_first_run": prev is None,
}
with open(os.path.join(args.out, "report_data.json"), "w") as f:
json.dump(doc, f, indent=2)
print(f"report_data.json: e2e {e2e['n_pass']}/{e2e['n_pass']+e2e['n_fail']+e2e['n_skip']}"
f" pass, units {hl['unit_pass']}/{hl['unit_total']}, "
f"cov py={hl['cov_python']} go={hl['cov_go']} cpp={hl['cov_cpp']}")
if prev:
ups = sum(1 for r in reg if r["better"] is True)
downs = sum(1 for r in reg if r["better"] is False)
print(f"regression vs previous run: {ups} improved, {downs} regressed")
else:
print("regression: first run (baseline established)")
if __name__ == "__main__":
main()
+87 -11
View File
@@ -24,12 +24,14 @@ mkdir -p "${OUT_DIR}" "${WORK}"
SKIP_BUILD=0
ONLY=""
PDF_ONLY=0
CPP_COV=0
while [ $# -gt 0 ]; do
case "$1" in
--skip-build) SKIP_BUILD=1 ;;
--only) shift; ONLY="$1" ;;
--pdf-only) PDF_ONLY=1 ;;
--help|-h) echo "Usage: $0 [--skip-build] [--only <id>] [--pdf-only]"; exit 0 ;;
--cpp-coverage) CPP_COV=1 ;;
--help|-h) echo "Usage: $0 [--skip-build] [--only <id>] [--pdf-only] [--cpp-coverage]"; exit 0 ;;
*) echo "unknown arg $1" >&2; exit 2 ;;
esac
shift
@@ -156,6 +158,9 @@ while IFS='|' read -r ID WSPORT UDPPORT NET ORACLE TRIG CHECKS; do
timeout 120 "${MARTE_APP}" -l RealTimeLoader -f "${MCFG}" -s Running > "${APP_LOG}" 2>&1 &
APP_PID=$!
sleep 1
# APP_PID is the `timeout` wrapper; perf must target the real MARTeApp child.
APP_PERF_PID="$(pgrep -P "${APP_PID}" 2>/dev/null | head -1)"
[ -z "${APP_PERF_PID}" ] && APP_PERF_PID="${APP_PID}"
TRIGARG=""
[ -n "${TRIG}" ] && TRIGARG="-trigsig ${TRIG}"
@@ -166,6 +171,11 @@ while IFS='|' read -r ID WSPORT UDPPORT NET ORACLE TRIG CHECKS; do
echo " client FAILED (see client_${ID}.log)"
tail -3 "${OUT_DIR}/client_${ID}.log" | sed 's/^/ /'
fi
# Snapshot CPU/peak-RSS while the stack is still alive (peak RSS is only
# legible before exit), then tear it down.
[ -n "${HUB_PID}" ] && ${PY} "${SCRIPT_DIR}/proc_perf.py" "${HUB_PID}" streamhub "${WORK}/perf_${ID}_hub.json" || true
[ -n "${APP_PERF_PID}" ] && ${PY} "${SCRIPT_DIR}/proc_perf.py" "${APP_PERF_PID}" marte "${WORK}/perf_${ID}_marte.json" || true
cleanup
# validate + plot
@@ -183,33 +193,99 @@ trap - EXIT
cleanup
# ── Aggregate results.json ───────────────────────────────────────────────────
WORK="${WORK}" OUT_DIR="${OUT_DIR}" SCEN_IDS="${SCEN_IDS}" ${PY} - <<'PY'
import json, os
# Scenarios carrying a `known_issue` marker exercise a documented, not-yet-fixed
# chain gap: a raw FAIL is reclassified XFAIL (expected failure — does not break
# the green baseline) and a raw PASS becomes XPASS (the bug was unexpectedly
# fixed; the marker should be dropped). Overall is PASS when nothing FAILs and
# nothing unexpectedly XPASSes.
WORK="${WORK}" OUT_DIR="${OUT_DIR}" SCEN_IDS="${SCEN_IDS}" \
SCRIPT_DIR="${SCRIPT_DIR}" ${PY} - <<'PY'
import json, os, sys
work = os.environ["WORK"]; out = os.environ["OUT_DIR"]
ids = os.environ["SCEN_IDS"].split()
sys.path.insert(0, os.environ["SCRIPT_DIR"])
try:
from scenarios import SCENARIOS
known = {s["id"]: s.get("known_issue") for s in SCENARIOS}
except Exception:
known = {}
results = []
for sid in ids:
rec = {"id": sid}
st = os.path.join(work, f"status_{sid}.txt")
rec["status"] = open(st).read().strip() if os.path.exists(st) else "UNKNOWN"
raw = open(st).read().strip() if os.path.exists(st) else "UNKNOWN"
ki = known.get(sid)
if ki:
rec["known_issue"] = ki
if raw == "FAIL":
raw = "XFAIL" # expected failure — documented chain gap
elif raw == "PASS":
raw = "XPASS" # unexpectedly fixed — drop the marker
rec["status"] = raw
mp = os.path.join(work, f"metrics_{sid}.json")
if os.path.exists(mp):
rec["metrics"] = json.load(open(mp))
results.append(rec)
overall = all(r["status"] in ("PASS", "SKIP") for r in results) and bool(results)
# Green when no hard FAIL and no XPASS (an XPASS means a known_issue marker is
# now stale and must be removed — surfaced as a failure to force the cleanup).
overall = (bool(results)
and all(r["status"] in ("PASS", "SKIP", "XFAIL") for r in results))
doc = {"overall": "PASS" if overall else "FAIL", "scenarios": results}
with open(os.path.join(out, "results.json"), "w") as f:
json.dump(doc, f, indent=2)
print(f"\nresults.json: {sum(r['status']=='PASS' for r in results)} pass, "
f"{sum(r['status']=='FAIL' for r in results)} fail, "
f"{sum(r['status']=='SKIP' for r in results)} skip → {doc['overall']}")
c = lambda st: sum(r["status"] == st for r in results)
print(f"\nresults.json: {c('PASS')} pass, {c('FAIL')} fail, {c('SKIP')} skip, "
f"{c('XFAIL')} xfail, {c('XPASS')} xpass → {doc['overall']}")
PY
# ── Optional PDF ─────────────────────────────────────────────────────────────
# ── Unit tests + coverage ────────────────────────────────────────────────────
echo ""
echo "── Unit tests + coverage ──"
# Optional C++ line coverage: rebuild the project's libraries + GTest with gcov
# instrumentation in place (OPTIM/LFLAGS are the MARTe2-sanctioned override
# hooks), let collect.py run the instrumented GTest (emits .gcda) and lcov, then
# restore a clean build so later --skip-build runs aren't left instrumented.
CPP_COV_FLAG=""
if [ "${CPP_COV}" -eq 1 ]; then
echo " building instrumented (gcov) libraries + GTest ..."
COV_O="--coverage"
COV_L="-Wl,--no-as-needed -fPIC --coverage"
make -C "${REPO_ROOT}" -f Makefile.gcc clean >/dev/null 2>&1 || true
make -C "${REPO_ROOT}" -f Makefile.gcc core TARGET="${TARGET}" \
OPTIM="${COV_O}" LFLAGS="${COV_L}" 2>&1 | tail -1
for d in Test/Components/DataSources/UDPStreamer Test/Applications/StreamHub Test/GTest; do
make -C "${REPO_ROOT}/${d}" -f Makefile.gcc TARGET="${TARGET}" \
OPTIM="${COV_O}" LFLAGS="${COV_L}" 2>&1 | tail -1
done
CPP_COV_FLAG="--cpp-coverage"
fi
${PY} "${SCRIPT_DIR}/collect.py" --repo "${REPO_ROOT}" --target "${TARGET}" \
--out "${OUT_DIR}" --work "${WORK}" ${CPP_COV_FLAG} || true
if [ "${CPP_COV}" -eq 1 ]; then
echo " restoring non-instrumented build ..."
make -C "${REPO_ROOT}" -f Makefile.gcc clean >/dev/null 2>&1 || true
make -C "${REPO_ROOT}" -f Makefile.gcc core apps TARGET="${TARGET}" 2>&1 | tail -1
(cd "${SCRIPT_DIR}/client" && go build -o chain-client . >/dev/null 2>&1) || true
fi
# ── Consolidated report data (+ history/regression + trend plots) ────────────
${PY} "${SCRIPT_DIR}/report_build.py" --repo "${REPO_ROOT}" \
--results "${OUT_DIR}/results.json" --work "${WORK}" --out "${OUT_DIR}" || true
# ── PDF ──────────────────────────────────────────────────────────────────────
if command -v typst >/dev/null 2>&1 && [ -f "${SCRIPT_DIR}/E2E_Report.typ" ]; then
cp "${SCRIPT_DIR}/E2E_Report.typ" "${OUT_DIR}/" 2>/dev/null || true
(cd "${OUT_DIR}" && typst compile E2E_Report.typ E2E_Report.pdf 2>/dev/null) \
&& echo "PDF: ${OUT_DIR}/E2E_Report.pdf"
# Embedded waveform overviews live in WORK; the template references them by
# bare name, so stage them next to report_data.json before compiling.
cp "${WORK}"/wave_*.png "${OUT_DIR}/" 2>/dev/null || true
if (cd "${OUT_DIR}" && typst compile E2E_Report.typ E2E_Report.pdf); then
echo "PDF: ${OUT_DIR}/E2E_Report.pdf"
else
echo "typst compile failed (report_data.json present at ${OUT_DIR})"
fi
fi
echo "Done — artifacts in ${OUT_DIR} and ${WORK}"
+85
View File
@@ -0,0 +1,85 @@
#!/usr/bin/env bash
# run_stress.sh — Capacity/stress harness for the streaming chain.
#
# MARTe2 app (FileReader -> IOGAM -> UDPStreamer)
# -> UDPS -> StreamHub(s) -> chain-client(s) (stress: liveness + zoom latency)
# -> proc_perf (cpu/peakRSS) -> per-case gate -> stress_results.json
#
# It sweeps one load axis at a time (signal size/count, subscriber fan-out, source
# count, WS-client count, zoom request rate — see stress.py) and gates each case on
# survival + liveness (hard) and RSS + zoom-p95 latency (soft). This is the
# capacity sibling of run_chain_e2e.sh (which gates waveform correctness).
#
# Usage: ./run_stress.sh [--skip-build] [--only <id>] [--axis <axis>]
set -u
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
TARGET=x86-linux
BUILD_DIR="${REPO_ROOT}/Build/${TARGET}"
OUT_DIR="${BUILD_DIR}/E2E/chain/stress"
WORK="/tmp/chain_stress"
mkdir -p "${OUT_DIR}" "${WORK}"
SKIP_BUILD=0
ONLY=""
AXIS=""
while [ $# -gt 0 ]; do
case "$1" in
--skip-build) SKIP_BUILD=1 ;;
--only) shift; ONLY="$1" ;;
--axis) shift; AXIS="$1" ;;
--help|-h) echo "Usage: $0 [--skip-build] [--only <id>] [--axis <axis>]"; exit 0 ;;
*) echo "unknown arg $1" >&2; exit 2 ;;
esac
shift
done
ENV_SCRIPT="${REPO_ROOT}/env.sh"
[ -f "${ENV_SCRIPT}" ] || { echo "ERROR: ${ENV_SCRIPT} not found" >&2; exit 1; }
: "${LD_LIBRARY_PATH:=}"
source "${ENV_SCRIPT}"
COMP="${MARTe2_Components_DIR}/Build/${TARGET}/Components"
export LD_LIBRARY_PATH="\
${BUILD_DIR}/Components/DataSources/UDPStreamer:\
${BUILD_DIR}/Components/Interfaces/UDPStream:\
${MARTe2_DIR}/Build/${TARGET}/Core:\
${COMP}/DataSources/LinuxTimer:\
${COMP}/DataSources/FileDataSource:\
${COMP}/GAMs/IOGAM:\
${LD_LIBRARY_PATH:-}"
MARTE_APP="${MARTe2_DIR}/Build/${TARGET}/App/MARTeApp.ex"
STREAMHUB_EX="${BUILD_DIR}/StreamHub/StreamHub.ex"
CLIENT="${SCRIPT_DIR}/client/chain-client"
PY="python3"
# ── Build ────────────────────────────────────────────────────────────────────
if [ "${SKIP_BUILD}" -eq 0 ]; then
echo "── Building components ──"
make -C "${REPO_ROOT}/Source/Components/Interfaces/UDPStream" -f Makefile.gcc TARGET="${TARGET}" 2>&1 | tail -1
make -C "${REPO_ROOT}/Source/Components/DataSources/UDPStreamer" -f Makefile.gcc TARGET="${TARGET}" 2>&1 | tail -1
make -C "${REPO_ROOT}/Source/Applications/StreamHub" -f Makefile.gcc TARGET="${TARGET}" 2>&1 | tail -1
fi
if [ ! -x "${CLIENT}" ] || [ "${SKIP_BUILD}" -eq 0 ]; then
echo "── Building chain-client ──"
(cd "${SCRIPT_DIR}/client" && go build -o chain-client .) || { echo "client build failed"; exit 1; }
fi
[ -x "${MARTE_APP}" ] || { echo "ERROR: MARTeApp.ex not found at ${MARTE_APP}" >&2; exit 1; }
[ -x "${STREAMHUB_EX}" ] || { echo "ERROR: StreamHub.ex not found" >&2; exit 1; }
# ── Validate the matrix, then run ────────────────────────────────────────────
${PY} "${SCRIPT_DIR}/stress.py" >/dev/null || { echo "stress matrix invalid"; exit 1; }
ARGS=(--marte "${MARTE_APP}" --hub "${STREAMHUB_EX}" --client "${CLIENT}"
--work "${WORK}" --out "${OUT_DIR}")
[ -n "${ONLY}" ] && ARGS+=(--only "${ONLY}")
[ -n "${AXIS}" ] && ARGS+=(--axis "${AXIS}")
${PY} "${SCRIPT_DIR}/stress_run.py" "${ARGS[@]}"
RC=$?
echo ""
echo "Done — results in ${OUT_DIR}/stress_results.json, logs in ${OUT_DIR}"
exit ${RC}
+370 -5
View File
@@ -49,6 +49,13 @@ Scenario dict schema
"oracle": "analytic" | "fed" | "both"
"client_checks": subset of ["live","zoom","window","trigger"]
"trig_signal": "src:sig" | None # which signal to trigger on
"known_issue": str | None # if set, the scenario exercises a
# documented, not-yet-fixed chain gap:
# a FAIL is reclassified XFAIL (does not
# break the baseline) and a PASS becomes
# XPASS (the bug is unexpectedly fixed —
# time to drop the marker). The string is
# the human-readable reason.
}
"""
@@ -75,6 +82,14 @@ NP_DTYPE = {
"float32": "<f4", "float64": "<f8",
}
# Wire constraints shared with the C++ UDPS layer. The UDPStreamer fragments the
# serialised packet into chunks of at most MaxPayloadSize *payload* bytes and
# prepends a UDPS_HEADER_SIZE-byte header, so the datagram it hands to sendto() is
# MaxPayloadSize+UDPS_HEADER_SIZE bytes. That must not exceed the maximum UDP
# payload or sendto() fails with EMSGSIZE.
UDPS_HEADER_SIZE = 17 # bytes; mirrors Common/UDP/UDPSProtocol.h
MAX_UDP_PAYLOAD = 65507 # 65535 - 20 (IP) - 8 (UDP)
FLOAT_TYPES = {"float32", "float64"}
QUANT_TYPES = {"none", "uint8", "int8", "uint16", "int16"}
QUANT_LEVELS = {"uint8": 255, "int8": 254, "uint16": 65535, "int16": 65534}
@@ -91,6 +106,20 @@ ROW_DT = 1.0e-3 # seconds per producer cycle (row); 1 kHz producer
LOOP_HZ = 1.0 / (NUM_ROWS * ROW_DT) # 5.0 Hz buffer fundamental
def geometry(s):
"""Resolve a scenario's producer geometry (per-scenario override or global).
Returns (row_dt, num_rows, producer_hz, loop_hz). Every data/config/validator
consumer reads geometry through here so the override cannot drift apart from
the seamless-loop fundamental used by the sine constraint.
"""
row_dt = s.get("row_dt") or ROW_DT
num_rows = s.get("num_rows") or NUM_ROWS
producer_hz = s.get("producer_hz") or int(round(1.0 / row_dt))
loop_hz = 1.0 / (num_rows * row_dt)
return row_dt, num_rows, producer_hz, loop_hz
def _sig(name, type, elements=1, time_mode="PacketTime", time_signal=None,
sampling_rate=None, quant="none", range_min=None, range_max=None,
unit=None, formula="sine", freq=1.0, is_time=False):
@@ -106,6 +135,7 @@ def _sig(name, type, elements=1, time_mode="PacketTime", time_signal=None,
def validate_scenario(s):
"""Return a list of validity error strings (empty == valid)."""
errs = []
row_dt, num_rows, _producer_hz, loop_hz = geometry(s)
if s.get("network") not in ("unicast", "multicast"):
errs.append("network must be unicast|multicast")
if s.get("publishing") not in ("Strict", "Accumulate", "Decimate"):
@@ -117,6 +147,12 @@ def validate_scenario(s):
errs.append("Accumulate needs min_refresh_hz>0")
if not s.get("sources"):
errs.append("no sources")
max_payload = s.get("max_payload")
if max_payload is not None and max_payload + UDPS_HEADER_SIZE > MAX_UDP_PAYLOAD:
errs.append(
f"max_payload {max_payload} + {UDPS_HEADER_SIZE}B header exceeds the "
f"{MAX_UDP_PAYLOAD}B UDP datagram limit (sendto EMSGSIZE); "
f"cap at {MAX_UDP_PAYLOAD - UDPS_HEADER_SIZE}")
for src in s.get("sources", []):
if s["network"] == "multicast":
if not src.get("multicast_group") or not src.get("data_port"):
@@ -145,16 +181,77 @@ def validate_scenario(s):
if sig["time_mode"] in ("FirstSample", "LastSample"):
if not sig["sampling_rate"] or sig["sampling_rate"] <= 0:
errs.append(f"{sig['name']}: First/LastSample needs sampling_rate>0")
else:
# The per-packet sample window must fit inside one producer
# cycle, or successive packets' expanded timestamps overlap
# and the hub's binary-search ring (which assumes a sorted
# time axis) is corrupted — the s17/s18 failure class.
window = (sig["elements"] - 1) / sig["sampling_rate"]
if window > row_dt + 1e-12:
errs.append(
f"{sig['name']}: {sig['time_mode']} window "
f"{window * 1e3:.4f} ms > row period {row_dt * 1e3:.4f} ms "
f"(non-monotonic ring); raise producer rate or "
f"sampling_rate, or lower elements")
if sig["formula"] == "sine" and sig.get("freq"):
ratio = sig["freq"] / LOOP_HZ
ratio = sig["freq"] / loop_hz
if abs(ratio - round(ratio)) > 1e-9 or round(ratio) < 1:
errs.append(f"{sig['name']}: sine freq {sig['freq']} must be a "
f"positive multiple of LOOP_HZ={LOOP_HZ} (seamless loop)")
f"positive multiple of LOOP_HZ={loop_hz} (seamless loop)")
return errs
# ── Starter set (expanded to the full covering matrix in Task 8) ──────────────
SCENARIOS = [
# ── Curated covering matrix ───────────────────────────────────────────────────
# The first three scenarios are referenced positionally by tests_py.py
# (SCENARIOS[0..2]) and are kept verbatim. The remainder is built with the helpers
# below, which auto-allocate unique WS/UDP/DATA ports so the matrix can grow
# without manual bookkeeping. Coverage goal: every UDPStreamer option *value*
# (all 10 types, scalar+array shapes, all four TimeModes, all five quant kinds,
# the three publishing modes, unicast+multicast, fragmentation via small payload,
# multi-source) appears at least once, plus deliberately chosen high-risk
# interactions (decimate+quant+array, accumulate+fullarray, multicast+decimate,
# fragmentation+decimate). Non-sine formulas (counter/ramp) are preferred for
# pure type/shape coverage because only the fidelity oracle gates them; sine is
# used where the shape metric should be tracked. MCAST_GROUP must have a route on
# the test host or those scenarios report SKIP (the orchestrator probes it).
import itertools
MCAST_GROUP = "239.0.7.7"
_ws = itertools.count(8104)
_udp = itertools.count(44616, 2)
_data = itertools.count(45616, 2)
def _src(sid, signals, multicast=False):
return {
"id": sid, "udp_port": next(_udp),
"data_port": next(_data) if multicast else None,
"multicast_group": MCAST_GROUP if multicast else None,
"signals": signals,
}
def mk(sid, desc, sources, network="unicast", publishing="Strict",
ratio=None, min_refresh_hz=None, max_payload=1400,
oracle="analytic", checks=("live", "zoom"), trig=None,
known_issue=None, row_dt=None, num_rows=None, producer_hz=None):
# row_dt / num_rows / producer_hz override the global producer geometry for
# this scenario only (default None == use the suite-wide NUM_ROWS / ROW_DT /
# 1 kHz). They are kept together so the per-cycle wall gap (1/producer_hz),
# the encoded time-step (row_dt) and the seamless-loop fundamental
# (1/(num_rows*row_dt)) stay mutually consistent.
return {
"id": sid, "desc": desc, "network": network, "publishing": publishing,
"ratio": ratio, "min_refresh_hz": min_refresh_hz,
"max_payload": max_payload, "ws_port": next(_ws),
"sources": sources, "oracle": oracle,
"client_checks": list(checks), "trig_signal": trig,
"known_issue": known_issue,
"row_dt": row_dt, "num_rows": num_rows, "producer_hz": producer_hz,
}
_STARTERS = [
{
"id": "s01_scalar_uint32",
"desc": "Single uint32 scalar counter, Strict unicast (type fidelity)",
@@ -177,7 +274,8 @@ SCENARIOS = [
"id": "s02_array_float32_fullarray",
"desc": "100-elem float32 array, FullArray time mode, uint64 ns time array",
"network": "unicast", "publishing": "Strict",
"ratio": None, "min_refresh_hz": None, "max_payload": 65507,
"ratio": None, "min_refresh_hz": None,
"max_payload": MAX_UDP_PAYLOAD - UDPS_HEADER_SIZE,
"ws_port": 8102,
"sources": [{
"id": "src", "udp_port": 44612, "data_port": None,
@@ -214,11 +312,278 @@ SCENARIOS = [
},
]
# ── Type fidelity: one scalar per remaining MARTe type (fidelity-only) ────────
_TYPES = [
mk("s04_int8_scalar", "int8 scalar counter, type fidelity",
[_src("src", [_sig("Cnt", "int8", 1, formula="counter")])]),
mk("s05_uint8_scalar", "uint8 scalar counter, type fidelity",
[_src("src", [_sig("Cnt", "uint8", 1, formula="counter")])]),
mk("s06_int16_scalar", "int16 scalar ramp, type fidelity",
[_src("src", [_sig("Ramp", "int16", 1, formula="ramp")])]),
mk("s07_uint16_scalar", "uint16 scalar ramp, type fidelity",
[_src("src", [_sig("Ramp", "uint16", 1, formula="ramp")])]),
mk("s08_int32_scalar", "int32 scalar counter, type fidelity",
[_src("src", [_sig("Cnt", "int32", 1, formula="counter")])]),
mk("s09_int64_scalar", "int64 scalar counter, type fidelity",
[_src("src", [_sig("Cnt", "int64", 1, formula="counter")])]),
mk("s10_uint64_scalar", "uint64 scalar counter, type fidelity",
[_src("src", [_sig("Cnt", "uint64", 1, formula="counter")])]),
mk("s11_float64_scalar", "float64 scalar sine 5 Hz (double-precision path)",
[_src("src", [_sig("Sine", "float64", 1, formula="sine", freq=5.0,
unit="V")])],
checks=("live", "zoom", "trigger"), trig="src:Sine"),
]
# ── Array shapes (NumberOfElements) ──────────────────────────────────────────
_ARRAYS = [
mk("s12_f32_arr8", "float32 8-elem array sine 5 Hz",
[_src("src", [_sig("Wave", "float32", 8, formula="sine", freq=5.0)])]),
mk("s13_f32_arr32", "float32 32-elem array sine 10 Hz",
[_src("src", [_sig("Wave", "float32", 32, formula="sine", freq=10.0)])]),
mk("s14_f64_arr64", "float64 64-elem array ramp",
[_src("src", [_sig("Ramp", "float64", 64, formula="ramp")])]),
mk("s15_i16_arr16", "int16 16-elem array counter",
[_src("src", [_sig("Cnt", "int16", 16, formula="counter")])]),
mk("s16_f32_arr256", "float32 256-elem array sine 5 Hz (large frame)",
[_src("src", [_sig("Wave", "float32", 256, formula="sine", freq=5.0)])],
max_payload=MAX_UDP_PAYLOAD - UDPS_HEADER_SIZE),
mk("s39_uint8_arr32", "uint8 32-elem array counter (wrap fidelity)",
[_src("src", [_sig("Cnt", "uint8", 32, formula="counter")])]),
mk("s40_int8_arr16", "int8 16-elem array counter (wrap fidelity)",
[_src("src", [_sig("Cnt", "int8", 16, formula="counter")])]),
]
# ── Time modes (each non-PacketTime mode needs a TimeSignal in the source) ────
_TIMEMODES = [
# sampling_rate = elements/ROW_DT (8/0.001 = 8000) so the per-cycle window
# of 8 samples fills exactly one 1 kHz producer cycle. A smaller rate makes
# each cycle's window wider than the inter-cycle gap, so successive windows
# overlap and the published timestamps stop being monotonic — which corrupts
# the hub's binary-search range/zoom queries (they assume a sorted ring).
mk("s17_lastsample", "float32 8-elem LastSample, uint64 ns scalar anchor",
[_src("src", [
_sig("Tns", "uint64", 1, unit="ns", formula="time_ns", is_time=True),
_sig("Data", "float32", 8, time_mode="LastSample",
time_signal="Tns", sampling_rate=8000.0, formula="counter"),
])]),
mk("s18_firstsample", "float32 8-elem FirstSample, uint32 us scalar anchor",
[_src("src", [
_sig("Tus", "uint32", 1, unit="us", formula="time_us", is_time=True),
_sig("Data", "float32", 8, time_mode="FirstSample",
time_signal="Tus", sampling_rate=8000.0, formula="counter"),
])]),
mk("s19_fullarray_f64", "float64 50-elem FullArray sine 5 Hz, uint64 ns time",
[_src("src", [
_sig("TimeArr", "uint64", 50, unit="ns", formula="time_ns",
is_time=True),
_sig("Wave", "float64", 50, time_mode="FullArray",
time_signal="TimeArr", formula="sine", freq=5.0, unit="V"),
])],
oracle="both"),
mk("s43_fullarray_quant", "float32 16-elem FullArray quant uint16 sine 5 Hz",
[_src("src", [
_sig("TimeArr", "uint64", 16, unit="ns", formula="time_ns",
is_time=True),
_sig("Wave", "float32", 16, time_mode="FullArray",
time_signal="TimeArr", quant="uint16", range_min=-1.0,
range_max=1.0, formula="sine", freq=5.0, unit="V"),
])]),
]
# ── Quantization (each QuantizedType, on float signals) ──────────────────────
_QUANT = [
mk("s20_quant_uint8", "float32 scalar quant uint8 [-1,1] sine 5 Hz",
[_src("src", [_sig("Sine", "float32", 1, quant="uint8", range_min=-1.0,
range_max=1.0, formula="sine", freq=5.0)])]),
mk("s21_quant_int8", "float32 scalar quant int8 [-10,10] sine 5 Hz",
[_src("src", [_sig("Sine", "float32", 1, quant="int8", range_min=-10.0,
range_max=10.0, formula="sine", freq=5.0)])]),
mk("s22_quant_int16", "float32 scalar quant int16 [-100,100] ramp",
[_src("src", [_sig("Ramp", "float32", 1, quant="int16", range_min=-100.0,
range_max=100.0, formula="ramp")])]),
mk("s23_quant_f64_arr", "float64 16-elem quant uint16 [-2,2] sine 5 Hz",
[_src("src", [_sig("Wave", "float64", 16, quant="uint16", range_min=-2.0,
range_max=2.0, formula="sine", freq=5.0)])]),
]
# ── Publishing modes (Strict covered; Accumulate + Decimate here) ────────────
_PUBLISH = [
mk("s24_accumulate", "float32 scalar sine 5 Hz, Accumulate @50 Hz refresh",
[_src("src", [_sig("Sine", "float32", 1, formula="sine", freq=5.0)])],
publishing="Accumulate", min_refresh_hz=50.0),
mk("s25_decimate4", "float32 scalar sine 5 Hz, Decimate ratio 4",
[_src("src", [_sig("Sine", "float32", 1, formula="sine", freq=5.0)])],
publishing="Decimate", ratio=4),
mk("s26_decimate10_arr", "float32 8-elem counter, Decimate ratio 10",
[_src("src", [_sig("Cnt", "float32", 8, formula="counter")])],
publishing="Decimate", ratio=10),
mk("s46_accumulate_arr",
"Accumulate @200 Hz: accumulated scalar sine + 16-elem array passenger",
[_src("src", [_sig("Sine", "float32", 1, formula="sine", freq=5.0),
_sig("Wave", "float32", 16, formula="sine", freq=5.0)])],
publishing="Accumulate", min_refresh_hz=200.0),
mk("s45_decimate_multisig", "Decimate ratio 2 over a 2-signal source",
[_src("src", [_sig("Sine", "float32", 1, formula="sine", freq=5.0),
_sig("Cnt", "uint32", 1, formula="counter")])],
publishing="Decimate", ratio=2),
]
# ── Payload size / fragmentation (small MaxPayloadSize vs large array) ───────
_PAYLOAD = [
mk("s27_frag_f64_128", "float64 128-elem ramp, MaxPayload 512 (fragmented)",
[_src("src", [_sig("Ramp", "float64", 128, formula="ramp")])],
max_payload=512),
mk("s28_frag_f32_100", "float32 100-elem sine 5 Hz, MaxPayload 256 (fragmented)",
[_src("src", [_sig("Wave", "float32", 100, formula="sine", freq=5.0)])],
max_payload=256),
mk("s48_f64_arr_big_payload", "float64 100-elem ramp, MaxPayload 65490 (single frame)",
[_src("src", [_sig("Ramp", "float64", 100, formula="ramp")])],
max_payload=MAX_UDP_PAYLOAD - UDPS_HEADER_SIZE),
]
# ── Multicast ────────────────────────────────────────────────────────────────
_MCAST = [
mk("s29_mcast_scalar", "multicast float32 scalar sine 5 Hz",
[_src("src", [_sig("Sine", "float32", 1, formula="sine", freq=5.0)],
multicast=True)],
network="multicast"),
mk("s30_mcast_arr_fullarray", "multicast float32 32-elem FullArray sine 5 Hz",
[_src("src", [
_sig("TimeArr", "uint64", 32, unit="ns", formula="time_ns",
is_time=True),
_sig("Wave", "float32", 32, time_mode="FullArray",
time_signal="TimeArr", formula="sine", freq=5.0),
], multicast=True)],
network="multicast"),
mk("s47_mcast_multisrc", "multicast, two sources (scalar each)",
[_src("a", [_sig("Sine", "float32", 1, formula="sine", freq=5.0)],
multicast=True),
_src("b", [_sig("Cnt", "uint32", 1, formula="counter")],
multicast=True)],
network="multicast"),
]
# ── Multiple sources (independent UDPStreamer blocks, one hub) ───────────────
_MULTISRC = [
mk("s31_two_src", "two unicast sources: float32 sine + uint32 counter",
[_src("a", [_sig("Sine", "float32", 1, formula="sine", freq=5.0)]),
_src("b", [_sig("Cnt", "uint32", 1, formula="counter")])]),
mk("s32_three_src", "three unicast sources: int16 ramp / float64 sine / uint8 counter",
[_src("a", [_sig("Ramp", "int16", 1, formula="ramp")]),
_src("b", [_sig("Sine", "float64", 1, formula="sine", freq=5.0)]),
_src("c", [_sig("Cnt", "uint8", 1, formula="counter")])]),
]
# ── High-risk interactions ───────────────────────────────────────────────────
_INTERACT = [
mk("s33_dec_arr_quant", "Decimate 2 + 16-elem quant uint16 sine 5 Hz",
[_src("src", [_sig("Wave", "float32", 16, quant="uint16", range_min=-1.0,
range_max=1.0, formula="sine", freq=5.0)])],
publishing="Decimate", ratio=2),
mk("s34_acc_fullarray",
"Accumulate @100 Hz: accumulated scalar + 32-elem FullArray sine passenger",
[_src("src", [
_sig("Sine", "float32", 1, formula="sine", freq=5.0),
_sig("TimeArr", "uint64", 32, unit="ns", formula="time_ns",
is_time=True),
_sig("Wave", "float32", 32, time_mode="FullArray",
time_signal="TimeArr", formula="sine", freq=5.0),
])],
publishing="Accumulate", min_refresh_hz=100.0),
mk("s35_mcast_decimate", "multicast + Decimate ratio 5, float32 scalar sine 5 Hz",
[_src("src", [_sig("Sine", "float32", 1, formula="sine", freq=5.0)],
multicast=True)],
network="multicast", publishing="Decimate", ratio=5),
mk("s36_big_frag_dec", "float64 64-elem ramp, MaxPayload 256 + Decimate 4",
[_src("src", [_sig("Ramp", "float64", 64, formula="ramp")])],
max_payload=256, publishing="Decimate", ratio=4),
mk("s49_mixed_quant_raw", "one source: quant uint8 sine + raw float32 sine",
[_src("src", [
_sig("Q", "float32", 1, quant="uint8", range_min=-1.0, range_max=1.0,
formula="sine", freq=5.0),
_sig("Raw", "float32", 1, formula="sine", freq=5.0),
])]),
]
# ── Trigger + client-check coverage (rising/falling/both swept by the client) ─
_TRIGGER = [
mk("s37_trig_ramp_i32", "trigger on int32 ramp scalar",
[_src("src", [_sig("Ramp", "int32", 1, formula="ramp")])],
checks=("live", "trigger"), trig="src:Ramp"),
mk("s38_trig_f64_sine", "trigger on float64 sine 5 Hz scalar",
[_src("src", [_sig("Sine", "float64", 1, formula="sine", freq=5.0)])],
checks=("live", "zoom", "trigger"), trig="src:Sine"),
mk("s44_window_check", "float32 sine 5 Hz scalar, window time-range check",
[_src("src", [_sig("Sine", "float32", 1, formula="sine", freq=5.0)])],
checks=("live", "zoom", "window")),
mk("s50_trig_quant", "trigger on quantised uint16 sine 10 Hz",
[_src("src", [_sig("Sine", "float32", 1, quant="uint16", range_min=-5.0,
range_max=5.0, formula="sine", freq=10.0)])],
checks=("live", "trigger"), trig="src:Sine"),
]
# ── Misc scalar coverage (unit annotation, counter on float) ─────────────────
_MISC = [
mk("s41_f32_unit", "float32 scalar ramp with Unit=V",
[_src("src", [_sig("Ramp", "float32", 1, formula="ramp", unit="V")])]),
mk("s42_f64_counter", "float64 scalar counter (large integer values)",
[_src("src", [_sig("Cnt", "float64", 1, formula="counter")])]),
]
# ── High sample-rate / high-throughput stress ────────────────────────────────
# Eight signals, each a 1000-element float32 array carrying 1 ms worth of 1 MSps
# data, published once per 1 kHz producer cycle. Aggregate wire rate is
# 8 x 1 MSps x 4 B = 32 MB/s. A single scalar uint64 ns anchor (FirstSample) is
# shared by all eight data signals; the per-packet sample window
# (1000 / 1 MHz = 1 ms) equals the 1 kHz inter-packet gap so the expanded
# per-sample timestamps stay monotonic for the hub's binary-search ring. Ramp
# formula keeps the gate fidelity-only (robust to the UDP loss expected at rate).
#
# Packet sizing — DATA payload is 8 (ns anchor) + 8 x 1000 x 4 = 32008 B, which
# is deliberately kept under the 64 KB single-datagram ceiling. That ceiling is
# the UDPSClient reassembly buffer (UDPSReassemblySlot::payload[65535]) plus its
# offset overflow guard: a packet whose payload exceeds ~64 KB can never be
# reassembled (fragment 1 lands past the buffer and is dropped), so high-rate
# streaming must use single-datagram packets. max_payload is set to the maximum
# valid value (UDP limit - 17 B header) so the 32 KB packet is sent unfragmented.
_HIGHRATE = [
mk("s51_8x1msps_100hz",
"8x float32 10k-elem arrays @1 MSps, FirstSample, 100 Hz packets (~32 MB/s, "
"320 KB/cycle fragmented)",
[_src("src", [
_sig("Tns", "uint64", 1, unit="ns", formula="time_ns", is_time=True),
] + [
_sig(f"S{i}", "float32", 10000, time_mode="FirstSample",
time_signal="Tns", sampling_rate=1.0e6, formula="ramp")
for i in range(8)
])],
max_payload=40000, row_dt=0.01, num_rows=50, producer_hz=100,
checks=("live", "zoom")),
]
SCENARIOS = (_STARTERS + _TYPES + _ARRAYS + _TIMEMODES + _QUANT + _PUBLISH +
_PAYLOAD + _MCAST + _MULTISRC + _INTERACT + _TRIGGER + _MISC +
_HIGHRATE)
if __name__ == "__main__":
import sys
ok = True
seen_ids, seen_ws, seen_udp = set(), set(), set()
for s in SCENARIOS:
errs = validate_scenario(s)
# uniqueness invariants the orchestrator relies on
if s["id"] in seen_ids:
errs.append("duplicate id")
seen_ids.add(s["id"])
if s["ws_port"] in seen_ws:
errs.append(f"duplicate ws_port {s['ws_port']}")
seen_ws.add(s["ws_port"])
for src in s["sources"]:
if src["udp_port"] in seen_udp:
errs.append(f"duplicate udp_port {src['udp_port']}")
seen_udp.add(src["udp_port"])
print(f"{s['id']:32s} {'OK' if not errs else errs}")
ok = ok and not errs
print(f"\n{len(SCENARIOS)} scenarios, {'ALL VALID' if ok else 'INVALID PRESENT'}")
sys.exit(0 if ok else 1)
+237
View File
@@ -0,0 +1,237 @@
#!/usr/bin/env python3
"""
stress.py — Declarative stress matrix for the streaming chain.
Where scenarios.py exercises *correctness* (every option value, exact waveform
fidelity), this module exercises *capacity*: it pushes one axis of load at a time
and records how the two server processes behave. The two components under stress:
* UDPStreamer (datasource) — serialises + sends UDPS packets each RT cycle.
Stress axes: signal size (bytes/packet), signal count, subscriber count
(UDPStreamer fans a unicast source out to up to 16 clients).
* StreamHub (hub) — ring storage, LTTB decimation, WS fan-out, zoom.
Stress axes: signal size, source count (independent UDPStreamer feeds),
WS client count, and client request (zoom) rate.
What is measured (not waveform fidelity — at these rates UDP loss is expected and
the hub decimates to PushRate anyway, so end-to-end sample loss is not the gate):
* survival — neither server crashed/hung for the whole run (hard gate).
* liveness — every WS client kept receiving monotonic, wall-clock-stamped
pushes under the load (hard gate).
* cpu / peakRSS — of marte and hub, captured by proc_perf.py (scaling curves;
soft gate against generous ceilings).
* zoom latency — p50/p95 round-trip of zoom queries issued under load (soft
gate; the headline responsiveness metric for the hub).
Stress-case dict schema (a superset of the scenarios.py scenario dict, so the
gen_data.py / gen_cfg.py generators consume it unchanged):
{ ...all scenario keys (id, network, publishing, max_payload, ws_port,
sources, oracle, client_checks, trig_signal, row_dt/num_rows/...),
"shape": "hub" | "ds_fanout",
"stress": {
"axis": str, # which load axis this case varies (for the report)
"level": number, # the axis value (x for the scaling curve)
"clients": int, # parallel WS clients (hub shape)
"hubs": int, # parallel subscriber hubs (ds_fanout shape)
"reqrate": float, # zoom requests/sec/client (0 = one-shot checks)
"dur": float, # live-load duration (s)
"gate": { "min_frames": int, "max_marte_rss_mb": float,
"max_hub_rss_mb": float, "max_zoom_p95_ms": float },
} }
The matrix keeps every datagram a single UDP fragment (payload < 64 KB): the
UDPSClient reassembly buffer caps a deliverable packet at ~64 KB, so sustained
high-rate streaming must stay below it (see scenarios.py s51). Cases therefore
sweep *count* and *rate*, not oversized single packets.
"""
import itertools
import scenarios as S
# Dedicated port ranges, disjoint from scenarios.py (ws 8101-8151, udp 44610-).
_ws = itertools.count(8300)
_udp = itertools.count(45000, 2)
_data = itertools.count(46000, 2)
# Producer geometry knobs shared across the matrix. 1 kHz producer (default),
# float32 arrays sized so one cycle's array is a single sub-64 KB datagram.
F32 = "float32"
def _f32_arr(name, elements, sampling_rate=1.0e6):
"""A float32 array signal timestamped FirstSample off the shared ns anchor.
sampling_rate defaults to elements*producer_hz so the per-cycle window equals
one 1 kHz producer cycle (keeps the hub ring's time axis monotonic, the same
constraint scenarios.py enforces for First/LastSample)."""
return S._sig(name, F32, elements, time_mode="FirstSample",
time_signal="Tns", sampling_rate=sampling_rate, formula="ramp")
def _source(sid, n_signals, elements, multicast=False):
"""One UDPStreamer source: a uint64 ns anchor + n_signals float32 arrays.
sampling_rate = elements / row_dt(1 ms) = elements * 1000 keeps each array's
1 ms window aligned to the 1 kHz cycle regardless of `elements`."""
rate = elements * 1000.0
sigs = [S._sig("Tns", "uint64", 1, unit="ns", formula="time_ns",
is_time=True)]
sigs += [_f32_arr(f"S{i}", elements, rate) for i in range(n_signals)]
return {
"id": sid, "udp_port": next(_udp),
"data_port": next(_data) if multicast else None,
"multicast_group": S.MCAST_GROUP if multicast else None,
"signals": sigs,
}
def _packet_bytes(n_signals, elements):
"""Approx DATA payload bytes/packet: ns anchor + n_signals*elements*4."""
return 8 + n_signals * elements * 4
def mk_stress(sid, axis, level, sources, shape="hub", clients=1, hubs=1,
reqrate=0.0, dur=6.0, network="unicast", publishing="Strict",
ratio=None, min_refresh_hz=None, gate=None):
case = {
"id": sid, "desc": f"stress {axis}={level}",
"network": network, "publishing": publishing,
"ratio": ratio, "min_refresh_hz": min_refresh_hz,
"max_payload": S.MAX_UDP_PAYLOAD - S.UDPS_HEADER_SIZE,
"ws_port": next(_ws), "sources": sources,
"oracle": "analytic", "client_checks": ["live", "zoom"],
"trig_signal": None, "known_issue": None,
"row_dt": None, "num_rows": None, "producer_hz": None,
"shape": shape,
"stress": {
"axis": axis, "level": level, "clients": clients, "hubs": hubs,
"reqrate": reqrate, "dur": dur,
"gate": gate or {},
},
}
return case
# Default gate: at least a handful of frames per client, generous RSS ceilings,
# and a 1 s p95 zoom round-trip cap. Stress cases tune these per axis.
def _gate(min_frames=5, marte_rss=512.0, hub_rss=1024.0, zoom_p95=1000.0):
return {"min_frames": min_frames, "max_marte_rss_mb": marte_rss,
"max_hub_rss_mb": hub_rss, "max_zoom_p95_ms": zoom_p95}
# ── UDPStreamer (datasource) stress ───────────────────────────────────────────
# Single source + single hub; the load lands on the UDPStreamer serialise/send
# path and is read back through one WS client (proc_perf measures marte).
# size: one float32 array, growing element count → bigger single-datagram packet.
_DS_SIZE = [
mk_stress(f"ds_size_{e}", "ds_signal_elements", e,
[_source("src", 1, e)], gate=_gate())
for e in (1000, 4000, 8000, 15000) # 4 KB → 60 KB packets (sub-64 KB cap)
]
# count: many 1000-element float32 arrays in one source → wider packet + more
# per-cycle serialise work.
_DS_COUNT = [
mk_stress(f"ds_count_{n}", "ds_signal_count", n,
[_source("src", n, 1000)], gate=_gate())
for n in (1, 4, 8, 15) # 15*4 KB ≈ 60 KB packet (sub-64 KB cap)
]
# clients (fan-out): one source, M subscriber hubs (ds_fanout shape). The
# UDPStreamer copies every packet to each unicast client (max 16); CPU should
# scale with M. Each hub gets its own WS port + one client.
_DS_CLIENTS = [
mk_stress(f"ds_clients_{m}", "ds_subscriber_hubs", m,
[_source("src", 4, 2000)], shape="ds_fanout", hubs=m,
gate=_gate(hub_rss=1024.0))
for m in (1, 2, 4, 8)
]
# ── StreamHub (hub) stress ────────────────────────────────────────────────────
# All measured against a single hub; proc_perf measures the hub process.
# size: hub ring/decimation cost vs per-signal width.
_HUB_SIZE = [
mk_stress(f"hub_size_{e}", "hub_signal_elements", e,
[_source("src", 1, e)], gate=_gate())
for e in (1000, 4000, 8000, 15000)
]
# sources: N independent UDPStreamer feeds into one hub (each its own udp_port).
_HUB_SOURCES = [
mk_stress(f"hub_sources_{n}", "hub_source_count", n,
[_source(f"s{i}", 2, 1000) for i in range(n)],
gate=_gate(marte_rss=1024.0))
for n in (1, 2, 4, 8)
]
# clients: one source, C parallel WS clients all recording live + zooming.
_HUB_CLIENTS = [
mk_stress(f"hub_clients_{c}", "hub_ws_clients", c,
[_source("src", 2, 2000)], clients=c, gate=_gate())
for c in (1, 4, 8, 16)
]
# request rate: one source, a few clients each issuing zoom queries at a sustained
# rate; the headline gate is zoom p95 latency under that query load.
_HUB_REQRATE = [
mk_stress(f"hub_reqrate_{r}", "hub_zoom_reqrate_hz", r,
[_source("src", 2, 2000)], clients=4, reqrate=r, dur=8.0,
gate=_gate(zoom_p95=1500.0))
for r in (5, 20, 50)
]
STRESS_CASES = (_DS_SIZE + _DS_COUNT + _DS_CLIENTS +
_HUB_SIZE + _HUB_SOURCES + _HUB_CLIENTS + _HUB_REQRATE)
def validate_case(c):
"""Stress-case validity = scenario validity + stress-specific bounds."""
errs = S.validate_scenario(c)
st = c.get("stress", {})
if c["shape"] == "ds_fanout":
if st["hubs"] < 1 or st["hubs"] > 16:
errs.append(f"{c['id']}: ds_fanout hubs must be 1..16 "
f"(UDPStreamer max unicast clients)")
if c["shape"] == "hub" and (st["clients"] < 1):
errs.append(f"{c['id']}: hub clients must be >= 1")
# Single-datagram ceiling: keep each source's packet < 64 KB so it never
# needs reassembly (the deliverable-packet cap).
for src in c["sources"]:
n_data = sum(1 for s in src["signals"] if not s["is_time"])
elem = max((s["elements"] for s in src["signals"]
if not s["is_time"]), default=0)
pb = _packet_bytes(n_data, elem)
if pb >= 65536:
errs.append(f"{c['id']}: source {src['id']} packet {pb} B exceeds "
f"the 64 KB single-datagram cap")
return errs
if __name__ == "__main__":
import sys
ok = True
seen_ids, seen_ws, seen_udp = set(), set(), set()
by_axis = {}
for c in STRESS_CASES:
errs = validate_case(c)
if c["id"] in seen_ids:
errs.append("duplicate id")
seen_ids.add(c["id"])
if c["ws_port"] in seen_ws:
errs.append(f"duplicate ws_port {c['ws_port']}")
seen_ws.add(c["ws_port"])
for src in c["sources"]:
if src["udp_port"] in seen_udp:
errs.append(f"duplicate udp_port {src['udp_port']}")
seen_udp.add(src["udp_port"])
by_axis.setdefault(c["stress"]["axis"], []).append(c["id"])
print(f"{c['id']:20s} {c['shape']:9s} "
f"{'OK' if not errs else errs}")
ok = ok and not errs
print(f"\n{len(STRESS_CASES)} stress cases across {len(by_axis)} axes, "
f"{'ALL VALID' if ok else 'INVALID PRESENT'}")
sys.exit(0 if ok else 1)
+298
View File
@@ -0,0 +1,298 @@
#!/usr/bin/env python3
"""
stress_run.py — Orchestrator for the streaming-chain stress matrix (stress.py).
Where run_chain_e2e.sh drives scenarios.py for *correctness*, this drives
STRESS_CASES for *capacity*. Per case it:
1. generates the FileReader input + MARTe app cfg + 1..M StreamHub cfgs
(reusing gen_data / gen_cfg unchanged — a stress case is a scenario superset),
2. launches the server stack:
* "hub" shape: 1 MARTe UDPStreamer feed + 1 StreamHub, then N
parallel chain-clients (stress mode) on that one hub,
* "ds_fanout" shape: 1 MARTe feed + M independent StreamHubs all
subscribing to the same UDPStreamer (unicast fan-out), 1 client each,
3. drives the clients in stress mode (liveness + sustained zoom-latency),
4. snapshots CPU / peak-RSS of marte and every hub *while alive* (proc_perf),
5. evaluates the per-case gate (survival + liveness hard gates; RSS + zoom-p95
soft gates against the case's ceilings),
and writes stress_results.json (one record per case, carrying the axis/level for
later scaling-curve plots). Server binaries + LD_LIBRARY_PATH come from the
caller (run_stress.sh sources env.sh); this module only orchestrates.
"""
import argparse
import copy
import json
import os
import signal
import socket
import subprocess
import sys
import time
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import gen_cfg # noqa: E402
import gen_data # noqa: E402
import proc_perf # noqa: E402
import stress as ST # noqa: E402
# WS port probing bases. StreamHub does not set SO_REUSEADDR, so a leftover
# listener (e.g. an orphaned hub from an aborted run) keeps a fixed port wedged;
# we therefore probe for genuinely-free ports at launch rather than trusting a
# static assignment. ds_fanout hubs probe from one base, single hubs from another.
FANOUT_WS_BASE = 9000
SINGLE_WS_BASE = 9100
def _popen(cmd, log_path):
"""Launch cmd in its own session, stdout+stderr → log_path."""
f = open(log_path, "w")
return subprocess.Popen(cmd, stdout=f, stderr=subprocess.STDOUT,
start_new_session=True), f
def _free_ports(n, base):
"""Find n free TCP ports on 127.0.0.1 at/above base.
Probed ports are closed and handed straight to the hub launch; the race
window is negligible for this sequential localhost harness, and probing is
what makes the suite robust to leftover wedged listeners."""
found = []
cand = base
while len(found) < n and cand < base + 1000:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.bind(("127.0.0.1", cand))
found.append(cand)
except OSError:
pass
finally:
s.close()
cand += 1
if len(found) < n:
raise RuntimeError(f"could not find {n} free ports from {base}")
return found
def _hub_ports(case):
st = case["stress"]
if case["shape"] == "ds_fanout":
return _free_ports(st["hubs"], FANOUT_WS_BASE)
return _free_ports(1, SINGLE_WS_BASE)
def _teardown(procs_files):
for p, _f in procs_files:
if p.poll() is None:
try:
os.killpg(os.getpgid(p.pid), signal.SIGTERM)
except (ProcessLookupError, PermissionError):
pass
deadline = time.time() + 5
for p, _f in procs_files:
try:
p.wait(timeout=max(0.1, deadline - time.time()))
except subprocess.TimeoutExpired:
try:
os.killpg(os.getpgid(p.pid), signal.SIGKILL)
except (ProcessLookupError, PermissionError):
pass
for _p, f in procs_files:
try:
f.close()
except OSError:
pass
def run_case(case, bins, work, out):
sid = case["id"]
st = case["stress"]
input_bin = os.path.join(work, f"sinput_{sid}.bin")
marte_cfg = os.path.join(work, f"sm_{sid}.cfg")
gen_data.write_input(case, input_bin)
gen_cfg.write_marte_cfg(case, marte_cfg, input_bin)
ports = _hub_ports(case)
hub_pf = [] # (proc, file)
for i, p in enumerate(ports):
hc = copy.deepcopy(case)
hc["ws_port"] = p
hcfg = os.path.join(work, f"sh_{sid}_{i}.cfg")
gen_cfg.write_hub_cfg(hc, hcfg)
proc, f = _popen([bins["hub"], "-cfg", hcfg],
os.path.join(out, f"shub_{sid}_{i}.log"))
hub_pf.append((proc, f))
time.sleep(1.0)
marte_proc, marte_f = _popen(
[bins["marte"], "-l", "RealTimeLoader", "-f", marte_cfg, "-s", "Running"],
os.path.join(out, f"smarte_{sid}.log"))
time.sleep(1.5)
dur, reqrate = st["dur"], st["reqrate"]
# clean stale client outputs so a missing file means "client failed".
for i in range(max(st["clients"], st["hubs"])):
sp = os.path.join(work, f"stress_{sid}_c{i}.json")
if os.path.exists(sp):
os.remove(sp)
client_pf = []
if case["shape"] == "ds_fanout":
targets = [(i, ports[i]) for i in range(len(ports))]
else:
targets = [(i, ports[0]) for i in range(st["clients"])]
for cid, wsport in targets:
cmd = [bins["client"], "-mode", "stress",
"-hub", f"127.0.0.1:{wsport}", "-scenario", sid,
"-clientid", str(cid), "-dur", str(dur),
"-reqrate", str(reqrate), "-out", work,
"-timeout", f"{int(dur) + 90}s"]
proc, f = _popen(cmd, os.path.join(out, f"sclient_{sid}_c{cid}.log"))
client_pf.append((proc, f))
deadline = time.time() + dur + 60
for proc, _f in client_pf:
try:
proc.wait(timeout=max(1.0, deadline - time.time()))
except subprocess.TimeoutExpired:
proc.kill()
for _p, f in client_pf:
f.close()
# survival + perf must be read while the stack is still alive.
survival = (marte_proc.poll() is None
and all(h.poll() is None for h, _ in hub_pf))
perf_marte = proc_perf.snapshot(marte_proc.pid)
perf_hubs = [proc_perf.snapshot(h.pid) for h, _ in hub_pf]
_teardown([(marte_proc, marte_f)] + hub_pf)
clients = []
for cid, _ in targets:
jp = os.path.join(work, f"stress_{sid}_c{cid}.json")
if os.path.exists(jp):
with open(jp) as f:
clients.append(json.load(f))
return _evaluate(case, survival, perf_marte, perf_hubs, clients)
def _rss_mb(rec):
return rec.get("peak_rss_kb", 0) / 1024.0 if rec.get("avail") else 0.0
def _evaluate(case, survival, perf_marte, perf_hubs, clients):
g = case["stress"].get("gate", {})
fails = []
if not survival:
fails.append("server process died during run")
if not clients:
fails.append("no client results recorded")
min_frames = g.get("min_frames", 1)
for c in clients:
cid = c.get("clientId")
if c.get("frames", 0) < min_frames:
fails.append(f"c{cid} frames {c.get('frames')} < {min_frames}")
if not c.get("monotonic", False):
fails.append(f"c{cid} non-monotonic time axis")
if not c.get("wallclock", False):
fails.append(f"c{cid} non-wallclock timestamps")
marte_rss = _rss_mb(perf_marte)
if "max_marte_rss_mb" in g and marte_rss > g["max_marte_rss_mb"]:
fails.append(f"marte peakRSS {marte_rss:.0f}MB > {g['max_marte_rss_mb']}MB")
hub_rss_max = max((_rss_mb(h) for h in perf_hubs), default=0.0)
if "max_hub_rss_mb" in g and hub_rss_max > g["max_hub_rss_mb"]:
fails.append(f"hub peakRSS {hub_rss_max:.0f}MB > {g['max_hub_rss_mb']}MB")
p95s = [c["zoomP95ms"] for c in clients if c.get("zoomCount", 0) > 0]
zoom_p95 = max(p95s) if p95s else 0.0
p50s = [c["zoomP50ms"] for c in clients if c.get("zoomCount", 0) > 0]
zoom_p50 = max(p50s) if p50s else 0.0
if "max_zoom_p95_ms" in g and zoom_p95 > g["max_zoom_p95_ms"]:
fails.append(f"zoom p95 {zoom_p95:.0f}ms > {g['max_zoom_p95_ms']}ms")
st = case["stress"]
return {
"id": case["id"], "shape": case["shape"],
"axis": st["axis"], "level": st["level"],
"status": "PASS" if not fails else "FAIL",
"survival": survival,
"clients": len(clients),
"min_frames": min(((c.get("frames", 0)) for c in clients), default=0),
"marte_cpu_s": perf_marte.get("cpu_s", 0.0),
"marte_rss_mb": round(marte_rss, 1),
"hub_cpu_s": round(sum(h.get("cpu_s", 0.0) for h in perf_hubs), 2),
"hub_rss_mb": round(hub_rss_max, 1),
"zoom_count": sum(c.get("zoomCount", 0) for c in clients),
"zoom_fail": sum(c.get("zoomFail", 0) for c in clients),
"zoom_p50_ms": round(zoom_p50, 1),
"zoom_p95_ms": round(zoom_p95, 1),
"fails": fails,
}
def main():
p = argparse.ArgumentParser(description="Run the streaming-chain stress matrix")
p.add_argument("--marte", required=True, help="MARTeApp.ex path")
p.add_argument("--hub", required=True, help="StreamHub.ex path")
p.add_argument("--client", required=True, help="chain-client path")
p.add_argument("--work", required=True, help="scratch dir")
p.add_argument("--out", required=True, help="report/log dir")
p.add_argument("--only", default="", help="run a single case id")
p.add_argument("--axis", default="", help="run only cases on this axis")
args = p.parse_args()
os.makedirs(args.work, exist_ok=True)
os.makedirs(args.out, exist_ok=True)
bins = {"marte": args.marte, "hub": args.hub, "client": args.client}
cases = ST.STRESS_CASES
if args.only:
cases = [c for c in cases if c["id"] == args.only]
if args.axis:
cases = [c for c in cases if c["stress"]["axis"] == args.axis]
if not cases:
print("no stress cases selected", file=sys.stderr)
sys.exit(1)
results = []
for c in cases:
errs = ST.validate_case(c)
if errs:
print(f"══ {c['id']}: INVALID {errs} ══")
results.append({"id": c["id"], "axis": c["stress"]["axis"],
"level": c["stress"]["level"], "status": "FAIL",
"fails": errs})
continue
print(f"\n══ stress {c['id']} ({c['shape']} {c['stress']['axis']}="
f"{c['stress']['level']}) ══")
rec = run_case(c, bins, args.work, args.out)
results.append(rec)
tag = rec["status"]
print(f" {tag} frames>={rec.get('min_frames')} "
f"marteCPU={rec.get('marte_cpu_s', 0):.1f}s "
f"marteRSS={rec.get('marte_rss_mb', 0):.0f}MB "
f"hubRSS={rec.get('hub_rss_mb', 0):.0f}MB "
f"zoom p50/p95={rec.get('zoom_p50_ms', 0):.0f}/"
f"{rec.get('zoom_p95_ms', 0):.0f}ms")
if rec.get("fails"):
for fmsg in rec["fails"]:
print(f" - {fmsg}")
overall = bool(results) and all(r["status"] == "PASS" for r in results)
doc = {"overall": "PASS" if overall else "FAIL", "cases": results}
rp = os.path.join(args.out, "stress_results.json")
with open(rp, "w") as f:
json.dump(doc, f, indent=2)
npass = sum(r["status"] == "PASS" for r in results)
print(f"\nstress_results.json: {npass}/{len(results)} pass → {doc['overall']}")
sys.exit(0 if overall else 1)
if __name__ == "__main__":
main()
+188
View File
@@ -0,0 +1,188 @@
#!/usr/bin/env python3
"""
tests_py.py — Unit tests for the streaming-chain E2E Python framework.
Run directly (``python3 -m unittest tests_py``) or, for coverage, via collect.py
which wraps it in ``coverage run``. These exercise the pure logic of the
generators, the waveform oracle, the config emitter, the RCV1 reader and the
/proc perf sampler — i.e. everything the orchestrator depends on, without needing
a live MARTe/StreamHub stack.
"""
import os
import struct
import sys
import tempfile
import unittest
import numpy as np
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import scenarios as S
import gen_data as G
import gen_cfg as C
import validate_waveform as V
import proc_perf as P
class TestScenarios(unittest.TestCase):
def test_all_starter_scenarios_valid(self):
for s in S.SCENARIOS:
self.assertEqual(S.validate_scenario(s), [], f"{s['id']} invalid")
def test_seamless_loop_constraint(self):
s = {
"id": "x", "network": "unicast", "publishing": "Strict",
"ratio": None, "min_refresh_hz": None, "max_payload": 1400,
"ws_port": 9000,
"sources": [{"id": "s", "udp_port": 1, "data_port": None,
"multicast_group": None,
"signals": [S._sig("Bad", "float32", freq=3.0)]}],
"oracle": "analytic", "client_checks": ["live"], "trig_signal": None,
}
errs = S.validate_scenario(s)
self.assertTrue(any("multiple of LOOP_HZ" in e for e in errs), errs)
s["sources"][0]["signals"][0]["freq"] = S.LOOP_HZ * 2
self.assertEqual(S.validate_scenario(s), [])
def test_bad_quant_rejected(self):
s = {
"id": "x", "network": "unicast", "publishing": "Strict",
"ratio": None, "min_refresh_hz": None, "max_payload": 1400,
"ws_port": 9000,
"sources": [{"id": "s", "udp_port": 1, "data_port": None,
"multicast_group": None,
"signals": [S._sig("Q", "int32", quant="uint8",
range_min=0, range_max=1, formula="ramp")]}],
"oracle": "analytic", "client_checks": ["live"], "trig_signal": None,
}
errs = S.validate_scenario(s)
self.assertTrue(any("quant only on float" in e for e in errs), errs)
class TestGenData(unittest.TestCase):
def test_ground_truth_shapes(self):
gt = G.build_ground_truth(S.SCENARIOS[1]) # s02: TimeArr+Wave, 100 elem
wave = gt["src:Wave"]
self.assertEqual(wave["elements"], 100)
self.assertEqual(wave["v"].size, S.NUM_ROWS * 100)
self.assertEqual(wave["t"].size, S.NUM_ROWS * 100)
def test_binary_roundtrip(self):
sc = S.SCENARIOS[0] # s01: Counter(uint32)+Sine(float32)
with tempfile.TemporaryDirectory() as d:
p = os.path.join(d, "in.bin")
gt = G.write_input(sc, p)
cols = V.read_marte_binary(p)
self.assertIn("Counter", cols)
self.assertIn("Sine", cols)
# counter is row index → first/last match ground truth
self.assertEqual(cols["Counter"].reshape(-1)[0], gt["src:Counter"]["v"][0])
self.assertEqual(int(cols["Counter"].reshape(-1)[5]), 5)
class TestOracle(unittest.TestCase):
def _gt(self, **kw):
t = np.linspace(0, 1, 200)
base = {"v": np.sin(2 * np.pi * 5 * t), "type": "float32", "quant": "none",
"formula": "sine", "freq": 5.0, "range_min": -1.0, "range_max": 1.0,
"elements": 1, "rows": 200, "is_time": False, "t": t,
"dt": t[1] - t[0]}
base.update(kw)
return base, t
def test_quant_tol_is_one_level(self):
gt, _ = self._gt(quant="uint16", range_min=-5.0, range_max=5.0)
tol, step = V._tol(gt)
self.assertAlmostEqual(step, 10.0 / 65535, places=9)
self.assertGreaterEqual(tol, step) # one full level, not half
def test_good_passes_bad_fails(self):
gt, t = self._gt(quant="uint16")
_, step = V._tol(gt)
good = gt["v"] + (np.random.rand(200) - 0.5) * step
bad = gt["v"] + (np.random.rand(200) - 0.5) * step * 50
self.assertTrue(V.compare_signal(gt, t, good)["pass"])
self.assertFalse(V.compare_signal(gt, t, bad)["pass"])
def test_wrong_frequency_fails_shape(self):
gt, t = self._gt()
wrong = np.sin(2 * np.pi * 50 * t) # 10x frequency
m = V.compare_signal(gt, t, wrong)
self.assertFalse(m["shape_ok"], m)
def test_integer_offgrid_fails(self):
gt, t = self._gt(type="uint32", quant="none",
v=np.arange(200, dtype=np.float64), formula="counter")
self.assertTrue(V.compare_signal(gt, t, np.arange(50, 150, dtype=float))["pass"])
self.assertFalse(V.compare_signal(gt, t, np.array([1.5, 999.0]))["pass"])
class TestRCV1(unittest.TestCase):
def test_read_received(self):
with tempfile.TemporaryDirectory() as d:
p = os.path.join(d, "r.bin")
t = [0.0, 0.1, 0.2]
v = [1.0, 2.0, 3.0]
buf = bytearray(b"RCV1")
buf += struct.pack("<I", 1)
key = b"src:Sig"
buf += struct.pack("<H", len(key)) + key + struct.pack("<I", len(t))
for x in t:
buf += struct.pack("<d", x)
for x in v:
buf += struct.pack("<d", x)
open(p, "wb").write(buf)
r = V.read_received(p)
self.assertIn("src:Sig", r)
tt, vv = r["src:Sig"]
self.assertEqual(list(vv), v)
class TestGenCfg(unittest.TestCase):
def test_tap_routes_through_ddb(self):
sc = S.SCENARIOS[1] # s02, oracle=both
with tempfile.TemporaryDirectory() as d:
mc = os.path.join(d, "m.cfg")
C.write_marte_cfg(sc, mc, os.path.join(d, "in.bin"),
tap_bin=os.path.join(d, "tap.bin"))
cfg = open(mc).read()
self.assertIn("StreamGAM_src", cfg)
self.assertIn("TapGAM", cfg)
# TapGAM must read from the DDB (prefixed name), not the FileReader
self.assertIn("src_Wave", cfg)
tap_block = cfg[cfg.index("+TapGAM"):]
tap_in = tap_block[tap_block.index("InputSignals"):tap_block.index("OutputSignals")]
self.assertNotIn("FileReaderDS", tap_in)
def test_no_tap_direct(self):
sc = S.SCENARIOS[0] # s01, oracle=analytic
with tempfile.TemporaryDirectory() as d:
mc = os.path.join(d, "m.cfg")
C.write_marte_cfg(sc, mc, os.path.join(d, "in.bin"))
cfg = open(mc).read()
self.assertNotIn("TapGAM", cfg)
self.assertIn("ReaderGAM_src", cfg)
def test_hub_cfg_has_ports(self):
sc = S.SCENARIOS[0]
with tempfile.TemporaryDirectory() as d:
hc = os.path.join(d, "h.cfg")
C.write_hub_cfg(sc, hc)
cfg = open(hc).read()
self.assertIn(f"WSPort = {sc['ws_port']}", cfg)
self.assertIn(f"Port = {sc['sources'][0]['udp_port']}", cfg)
class TestProcPerf(unittest.TestCase):
def test_snapshot_self(self):
rec = P.snapshot(os.getpid())
self.assertTrue(rec["avail"])
self.assertIn("cpu_s", rec)
self.assertGreaterEqual(rec["cpu_s"], 0.0)
def test_snapshot_missing(self):
self.assertFalse(P.snapshot(2 ** 30).get("avail", False))
if __name__ == "__main__":
unittest.main(verbosity=2)