diff --git a/.gitignore b/.gitignore
index f09498d5..a6f4ab69 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,12 @@ main.exe
dist/
__pycache__/
+# .NET/C# build outputs
+bin/
+obj/
+*.dll
+*.exe
+
# Generated code directories
generated/
gen/
diff --git a/src/struct_frame/__init__.py b/src/struct_frame/__init__.py
index ad7e287d..dc80c5ad 100644
--- a/src/struct_frame/__init__.py
+++ b/src/struct_frame/__init__.py
@@ -6,12 +6,14 @@
from .py_gen import FilePyGen
from .gql_gen import FileGqlGen
from .cpp_gen import FileCppGen
+from .csharp_gen import FileCSharpGen
from .frame_format import FrameFormat, FrameFormatCollection, parse_frame_formats
from .frame_parser_c_gen import generate_c_frame_parsers, FrameParserCGen
from .frame_parser_py_gen import generate_py_frame_parsers, FrameParserPyGen
from .frame_parser_ts_gen import generate_ts_frame_parsers, generate_js_frame_parsers, FrameParserTsGen, FrameParserJsGen
from .frame_parser_cpp_gen import generate_cpp_frame_parsers, FrameParserCppGen
+from .frame_parser_csharp_gen import generate_csharp_frame_parsers, FrameParserCSharpGen
from .generate import main
from .generate_boilerplate import (
@@ -21,12 +23,12 @@
get_boilerplate_dir
)
-__all__ = ["main", "FileCGen", "FileTsGen", "FileJsGen", "FilePyGen", "FileGqlGen", "FileCppGen", "version",
+__all__ = ["main", "FileCGen", "FileTsGen", "FileJsGen", "FilePyGen", "FileGqlGen", "FileCppGen", "FileCSharpGen", "version",
"NamingStyleC", "CamelToSnakeCase", "pascalCase",
"FrameFormat", "FrameFormatCollection", "parse_frame_formats",
"generate_c_frame_parsers", "generate_py_frame_parsers",
"generate_ts_frame_parsers", "generate_js_frame_parsers",
- "generate_cpp_frame_parsers",
- "FrameParserCGen", "FrameParserPyGen", "FrameParserTsGen", "FrameParserJsGen", "FrameParserCppGen",
+ "generate_cpp_frame_parsers", "generate_csharp_frame_parsers",
+ "FrameParserCGen", "FrameParserPyGen", "FrameParserTsGen", "FrameParserJsGen", "FrameParserCppGen", "FrameParserCSharpGen",
"generate_boilerplate_to_paths", "update_src_boilerplate",
"get_default_frame_formats_path", "get_boilerplate_dir"]
diff --git a/src/struct_frame/boilerplate/c/basic_default.h b/src/struct_frame/boilerplate/c/basic_default.h
index b9be7a02..15d05c08 100644
--- a/src/struct_frame/boilerplate/c/basic_default.h
+++ b/src/struct_frame/boilerplate/c/basic_default.h
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/c/basic_extended.h b/src/struct_frame/boilerplate/c/basic_extended.h
index f48a8182..a388f8a8 100644
--- a/src/struct_frame/boilerplate/c/basic_extended.h
+++ b/src/struct_frame/boilerplate/c/basic_extended.h
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/c/basic_extended_length.h b/src/struct_frame/boilerplate/c/basic_extended_length.h
index 3fe8d42e..cfb71fd7 100644
--- a/src/struct_frame/boilerplate/c/basic_extended_length.h
+++ b/src/struct_frame/boilerplate/c/basic_extended_length.h
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/c/basic_extended_msg_ids.h b/src/struct_frame/boilerplate/c/basic_extended_msg_ids.h
index b51fcc5b..8a11a7a2 100644
--- a/src/struct_frame/boilerplate/c/basic_extended_msg_ids.h
+++ b/src/struct_frame/boilerplate/c/basic_extended_msg_ids.h
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/c/basic_extended_multi_system_stream.h b/src/struct_frame/boilerplate/c/basic_extended_multi_system_stream.h
index d6ccf9db..6ca612e7 100644
--- a/src/struct_frame/boilerplate/c/basic_extended_multi_system_stream.h
+++ b/src/struct_frame/boilerplate/c/basic_extended_multi_system_stream.h
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/c/basic_minimal.h b/src/struct_frame/boilerplate/c/basic_minimal.h
index dae08c80..a8964ae7 100644
--- a/src/struct_frame/boilerplate/c/basic_minimal.h
+++ b/src/struct_frame/boilerplate/c/basic_minimal.h
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/c/basic_multi_system_stream.h b/src/struct_frame/boilerplate/c/basic_multi_system_stream.h
index 4a99c861..9df9fbc7 100644
--- a/src/struct_frame/boilerplate/c/basic_multi_system_stream.h
+++ b/src/struct_frame/boilerplate/c/basic_multi_system_stream.h
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/c/basic_seq.h b/src/struct_frame/boilerplate/c/basic_seq.h
index bd1d55f3..9dcb2e31 100644
--- a/src/struct_frame/boilerplate/c/basic_seq.h
+++ b/src/struct_frame/boilerplate/c/basic_seq.h
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/c/basic_sys_comp.h b/src/struct_frame/boilerplate/c/basic_sys_comp.h
index d2559d82..897255dd 100644
--- a/src/struct_frame/boilerplate/c/basic_sys_comp.h
+++ b/src/struct_frame/boilerplate/c/basic_sys_comp.h
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/c/frame_base.h b/src/struct_frame/boilerplate/c/frame_base.h
index 729d618c..39787730 100644
--- a/src/struct_frame/boilerplate/c/frame_base.h
+++ b/src/struct_frame/boilerplate/c/frame_base.h
@@ -1,5 +1,5 @@
/* Automatically generated frame parser base utilities */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/c/frame_format_config.h b/src/struct_frame/boilerplate/c/frame_format_config.h
index 63dae554..5ef639de 100644
--- a/src/struct_frame/boilerplate/c/frame_format_config.h
+++ b/src/struct_frame/boilerplate/c/frame_format_config.h
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/c/frame_parsers.h b/src/struct_frame/boilerplate/c/frame_parsers.h
index 0469ffd6..3f2b4cc1 100644
--- a/src/struct_frame/boilerplate/c/frame_parsers.h
+++ b/src/struct_frame/boilerplate/c/frame_parsers.h
@@ -1,5 +1,5 @@
/* Automatically generated frame parser main header */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/c/mavlink_v1_frame.h b/src/struct_frame/boilerplate/c/mavlink_v1_frame.h
index a9d36fad..0acaa100 100644
--- a/src/struct_frame/boilerplate/c/mavlink_v1_frame.h
+++ b/src/struct_frame/boilerplate/c/mavlink_v1_frame.h
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/c/mavlink_v2_frame.h b/src/struct_frame/boilerplate/c/mavlink_v2_frame.h
index e4732c45..6e4858a0 100644
--- a/src/struct_frame/boilerplate/c/mavlink_v2_frame.h
+++ b/src/struct_frame/boilerplate/c/mavlink_v2_frame.h
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/c/tiny_default.h b/src/struct_frame/boilerplate/c/tiny_default.h
index 9ab9f0b8..02946fa7 100644
--- a/src/struct_frame/boilerplate/c/tiny_default.h
+++ b/src/struct_frame/boilerplate/c/tiny_default.h
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/c/tiny_extended.h b/src/struct_frame/boilerplate/c/tiny_extended.h
index 7284c2fb..9765442b 100644
--- a/src/struct_frame/boilerplate/c/tiny_extended.h
+++ b/src/struct_frame/boilerplate/c/tiny_extended.h
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/c/tiny_extended_length.h b/src/struct_frame/boilerplate/c/tiny_extended_length.h
index b2fa1893..0838b73d 100644
--- a/src/struct_frame/boilerplate/c/tiny_extended_length.h
+++ b/src/struct_frame/boilerplate/c/tiny_extended_length.h
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/c/tiny_extended_msg_ids.h b/src/struct_frame/boilerplate/c/tiny_extended_msg_ids.h
index c05f2814..4b3b79a7 100644
--- a/src/struct_frame/boilerplate/c/tiny_extended_msg_ids.h
+++ b/src/struct_frame/boilerplate/c/tiny_extended_msg_ids.h
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/c/tiny_extended_multi_system_stream.h b/src/struct_frame/boilerplate/c/tiny_extended_multi_system_stream.h
index 9ce03404..c69f4919 100644
--- a/src/struct_frame/boilerplate/c/tiny_extended_multi_system_stream.h
+++ b/src/struct_frame/boilerplate/c/tiny_extended_multi_system_stream.h
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/c/tiny_minimal.h b/src/struct_frame/boilerplate/c/tiny_minimal.h
index 85b0b1ce..e5b5b298 100644
--- a/src/struct_frame/boilerplate/c/tiny_minimal.h
+++ b/src/struct_frame/boilerplate/c/tiny_minimal.h
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/c/tiny_multi_system_stream.h b/src/struct_frame/boilerplate/c/tiny_multi_system_stream.h
index 6787665c..37c8ada1 100644
--- a/src/struct_frame/boilerplate/c/tiny_multi_system_stream.h
+++ b/src/struct_frame/boilerplate/c/tiny_multi_system_stream.h
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/c/tiny_seq.h b/src/struct_frame/boilerplate/c/tiny_seq.h
index cd71b2b1..5e5237ec 100644
--- a/src/struct_frame/boilerplate/c/tiny_seq.h
+++ b/src/struct_frame/boilerplate/c/tiny_seq.h
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/c/tiny_sys_comp.h b/src/struct_frame/boilerplate/c/tiny_sys_comp.h
index 76d55cb6..1ac54415 100644
--- a/src/struct_frame/boilerplate/c/tiny_sys_comp.h
+++ b/src/struct_frame/boilerplate/c/tiny_sys_comp.h
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/c/ubx_frame.h b/src/struct_frame/boilerplate/c/ubx_frame.h
index a11b7e1d..5869d9ab 100644
--- a/src/struct_frame/boilerplate/c/ubx_frame.h
+++ b/src/struct_frame/boilerplate/c/ubx_frame.h
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/cpp/BasicDefault.hpp b/src/struct_frame/boilerplate/cpp/BasicDefault.hpp
index 5efc0ba6..99c65ea8 100644
--- a/src/struct_frame/boilerplate/cpp/BasicDefault.hpp
+++ b/src/struct_frame/boilerplate/cpp/BasicDefault.hpp
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/cpp/BasicExtended.hpp b/src/struct_frame/boilerplate/cpp/BasicExtended.hpp
index 86996959..0e4dfcf8 100644
--- a/src/struct_frame/boilerplate/cpp/BasicExtended.hpp
+++ b/src/struct_frame/boilerplate/cpp/BasicExtended.hpp
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/cpp/BasicExtendedLength.hpp b/src/struct_frame/boilerplate/cpp/BasicExtendedLength.hpp
index 71571bd2..3f4f570c 100644
--- a/src/struct_frame/boilerplate/cpp/BasicExtendedLength.hpp
+++ b/src/struct_frame/boilerplate/cpp/BasicExtendedLength.hpp
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/cpp/BasicExtendedMsgIds.hpp b/src/struct_frame/boilerplate/cpp/BasicExtendedMsgIds.hpp
index f60787d9..7c4c2f30 100644
--- a/src/struct_frame/boilerplate/cpp/BasicExtendedMsgIds.hpp
+++ b/src/struct_frame/boilerplate/cpp/BasicExtendedMsgIds.hpp
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/cpp/BasicExtendedMultiSystemStream.hpp b/src/struct_frame/boilerplate/cpp/BasicExtendedMultiSystemStream.hpp
index dec5924f..b93c3781 100644
--- a/src/struct_frame/boilerplate/cpp/BasicExtendedMultiSystemStream.hpp
+++ b/src/struct_frame/boilerplate/cpp/BasicExtendedMultiSystemStream.hpp
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/cpp/BasicMinimal.hpp b/src/struct_frame/boilerplate/cpp/BasicMinimal.hpp
index fca39b89..3b9e95ae 100644
--- a/src/struct_frame/boilerplate/cpp/BasicMinimal.hpp
+++ b/src/struct_frame/boilerplate/cpp/BasicMinimal.hpp
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/cpp/BasicMultiSystemStream.hpp b/src/struct_frame/boilerplate/cpp/BasicMultiSystemStream.hpp
index 5104d52d..76975be8 100644
--- a/src/struct_frame/boilerplate/cpp/BasicMultiSystemStream.hpp
+++ b/src/struct_frame/boilerplate/cpp/BasicMultiSystemStream.hpp
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/cpp/BasicSeq.hpp b/src/struct_frame/boilerplate/cpp/BasicSeq.hpp
index 8c5d9842..86e89172 100644
--- a/src/struct_frame/boilerplate/cpp/BasicSeq.hpp
+++ b/src/struct_frame/boilerplate/cpp/BasicSeq.hpp
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/cpp/BasicSysComp.hpp b/src/struct_frame/boilerplate/cpp/BasicSysComp.hpp
index 7009478c..1652e57a 100644
--- a/src/struct_frame/boilerplate/cpp/BasicSysComp.hpp
+++ b/src/struct_frame/boilerplate/cpp/BasicSysComp.hpp
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/cpp/FrameFormatConfig.hpp b/src/struct_frame/boilerplate/cpp/FrameFormatConfig.hpp
index 43190d44..00d08603 100644
--- a/src/struct_frame/boilerplate/cpp/FrameFormatConfig.hpp
+++ b/src/struct_frame/boilerplate/cpp/FrameFormatConfig.hpp
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/cpp/MavlinkV1Frame.hpp b/src/struct_frame/boilerplate/cpp/MavlinkV1Frame.hpp
index 32563371..f8523265 100644
--- a/src/struct_frame/boilerplate/cpp/MavlinkV1Frame.hpp
+++ b/src/struct_frame/boilerplate/cpp/MavlinkV1Frame.hpp
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/cpp/MavlinkV2Frame.hpp b/src/struct_frame/boilerplate/cpp/MavlinkV2Frame.hpp
index b36003a3..ef95875a 100644
--- a/src/struct_frame/boilerplate/cpp/MavlinkV2Frame.hpp
+++ b/src/struct_frame/boilerplate/cpp/MavlinkV2Frame.hpp
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/cpp/TinyDefault.hpp b/src/struct_frame/boilerplate/cpp/TinyDefault.hpp
index 2fb609e6..c2527742 100644
--- a/src/struct_frame/boilerplate/cpp/TinyDefault.hpp
+++ b/src/struct_frame/boilerplate/cpp/TinyDefault.hpp
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/cpp/TinyExtended.hpp b/src/struct_frame/boilerplate/cpp/TinyExtended.hpp
index 6d70497f..85bbefc6 100644
--- a/src/struct_frame/boilerplate/cpp/TinyExtended.hpp
+++ b/src/struct_frame/boilerplate/cpp/TinyExtended.hpp
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/cpp/TinyExtendedLength.hpp b/src/struct_frame/boilerplate/cpp/TinyExtendedLength.hpp
index 37d5acc9..d60a4c1a 100644
--- a/src/struct_frame/boilerplate/cpp/TinyExtendedLength.hpp
+++ b/src/struct_frame/boilerplate/cpp/TinyExtendedLength.hpp
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/cpp/TinyExtendedMsgIds.hpp b/src/struct_frame/boilerplate/cpp/TinyExtendedMsgIds.hpp
index 3d32b6f8..50498540 100644
--- a/src/struct_frame/boilerplate/cpp/TinyExtendedMsgIds.hpp
+++ b/src/struct_frame/boilerplate/cpp/TinyExtendedMsgIds.hpp
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/cpp/TinyExtendedMultiSystemStream.hpp b/src/struct_frame/boilerplate/cpp/TinyExtendedMultiSystemStream.hpp
index df4b81a1..c89d6ee7 100644
--- a/src/struct_frame/boilerplate/cpp/TinyExtendedMultiSystemStream.hpp
+++ b/src/struct_frame/boilerplate/cpp/TinyExtendedMultiSystemStream.hpp
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/cpp/TinyMinimal.hpp b/src/struct_frame/boilerplate/cpp/TinyMinimal.hpp
index e2cd4c5b..015787bd 100644
--- a/src/struct_frame/boilerplate/cpp/TinyMinimal.hpp
+++ b/src/struct_frame/boilerplate/cpp/TinyMinimal.hpp
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/cpp/TinyMultiSystemStream.hpp b/src/struct_frame/boilerplate/cpp/TinyMultiSystemStream.hpp
index 90f85688..a6a01e04 100644
--- a/src/struct_frame/boilerplate/cpp/TinyMultiSystemStream.hpp
+++ b/src/struct_frame/boilerplate/cpp/TinyMultiSystemStream.hpp
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/cpp/TinySeq.hpp b/src/struct_frame/boilerplate/cpp/TinySeq.hpp
index 8d4c6085..41663edb 100644
--- a/src/struct_frame/boilerplate/cpp/TinySeq.hpp
+++ b/src/struct_frame/boilerplate/cpp/TinySeq.hpp
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/cpp/TinySysComp.hpp b/src/struct_frame/boilerplate/cpp/TinySysComp.hpp
index 5b6d54a7..bb245598 100644
--- a/src/struct_frame/boilerplate/cpp/TinySysComp.hpp
+++ b/src/struct_frame/boilerplate/cpp/TinySysComp.hpp
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/cpp/UbxFrame.hpp b/src/struct_frame/boilerplate/cpp/UbxFrame.hpp
index cf7a580d..efe1c410 100644
--- a/src/struct_frame/boilerplate/cpp/UbxFrame.hpp
+++ b/src/struct_frame/boilerplate/cpp/UbxFrame.hpp
@@ -1,5 +1,5 @@
/* Automatically generated frame parser */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/cpp/frame_base.hpp b/src/struct_frame/boilerplate/cpp/frame_base.hpp
index 7c2b25a7..1c6d4e1e 100644
--- a/src/struct_frame/boilerplate/cpp/frame_base.hpp
+++ b/src/struct_frame/boilerplate/cpp/frame_base.hpp
@@ -1,5 +1,5 @@
/* Automatically generated frame parser base utilities */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/cpp/frame_parsers.hpp b/src/struct_frame/boilerplate/cpp/frame_parsers.hpp
index 38253a37..9032efe9 100644
--- a/src/struct_frame/boilerplate/cpp/frame_parsers.hpp
+++ b/src/struct_frame/boilerplate/cpp/frame_parsers.hpp
@@ -1,5 +1,5 @@
/* Automatically generated frame parser main header */
-/* Generated by 0.0.1 at Thu Dec 4 22:44:11 2025. */
+/* Generated by 0.0.1 at Fri Dec 5 01:14:49 2025. */
#pragma once
diff --git a/src/struct_frame/boilerplate/csharp/BasicDefault.cs b/src/struct_frame/boilerplate/csharp/BasicDefault.cs
new file mode 100644
index 00000000..c3fb8431
--- /dev/null
+++ b/src/struct_frame/boilerplate/csharp/BasicDefault.cs
@@ -0,0 +1,196 @@
+// Automatically generated frame parser for C#
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
+
+using System;
+using System.Collections.Generic;
+
+namespace StructFrame
+{
+ ///
+ /// BasicDefault - Frame format parser and encoder
+ ///
+ public class BasicDefault : FrameFormatBase
+ {
+ public enum ParserState
+ {
+ LookingForStart1 = 0,
+ LookingForStart2 = 1,
+ GettingMsgId = 2,
+ GettingLength = 3,
+ GettingPayload = 4
+ }
+
+ public const byte StartByte1 = 0x90;
+ public const byte StartByte2 = 0x71;
+ public const int HeaderSize = 4;
+ public const int FooterSize = 2;
+ public const int Overhead = 6;
+ public const int LengthBytes = 1;
+
+ private ParserState _state;
+ private List _buffer;
+ private int _packetSize;
+ private int _msgId;
+ private int _msgLength;
+ private Func _getMsgLength;
+
+ ///
+ /// Initialize the BasicDefault parser
+ ///
+ /// Callback function to get message length from msg_id
+ public BasicDefault(Func getMsgLength = null)
+ {
+ _getMsgLength = getMsgLength;
+ _buffer = new List();
+ Reset();
+ }
+
+ ///
+ /// Reset parser state
+ ///
+ public override void Reset()
+ {
+ _state = ParserState.LookingForStart1;
+ _buffer.Clear();
+ _packetSize = 0;
+ _msgId = 0;
+ _msgLength = 0;
+ }
+
+ ///
+ /// Parse a single byte
+ ///
+ /// The byte to parse
+ /// FrameParseResult with Valid=true when a complete valid message is received
+ public override FrameParseResult ParseByte(byte b)
+ {
+ var result = new FrameParseResult();
+
+ switch (_state)
+ {
+ case ParserState.LookingForStart1:
+ if (b == StartByte1)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.LookingForStart2;
+ }
+ break;
+
+ case ParserState.LookingForStart2:
+ if (b == StartByte2)
+ {
+ _buffer.Add(b);
+ _state = ParserState.GettingMsgId;
+ }
+ else if (b == StartByte1)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.LookingForStart2;
+ }
+ else
+ {
+ _state = ParserState.LookingForStart1;
+ }
+ break;
+
+ case ParserState.GettingMsgId:
+ _buffer.Add(b);
+ _msgId = b;
+ _state = ParserState.GettingLength;
+ break;
+
+ case ParserState.GettingLength:
+ _buffer.Add(b);
+ _msgLength = b;
+ _packetSize = Overhead + _msgLength;
+ _state = ParserState.GettingPayload;
+ break;
+
+ case ParserState.GettingPayload:
+ _buffer.Add(b);
+ if (_buffer.Count >= _packetSize)
+ {
+ result = ValidatePacket(_buffer.ToArray(), _buffer.Count);
+ _state = ParserState.LookingForStart1;
+ }
+ break;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Encode a message with BasicDefault format
+ ///
+ /// Message ID
+ /// Message data bytes
+ /// Encoded frame as byte array
+ public override byte[] Encode(int msgId, byte[] msgData)
+ {
+ var output = new List();
+ output.Add(StartByte1);
+ output.Add(StartByte2);
+ output.Add((byte)(msgData.Length & 0xFF));
+ output.Add((byte)msgId);
+ output.AddRange(msgData);
+
+ // Calculate Fletcher checksum
+ var ck = FletcherChecksum(output.ToArray(), 2, output.Count);
+ output.Add(ck.Item1);
+ output.Add(ck.Item2);
+ return output.ToArray();
+ }
+
+ ///
+ /// Validate a complete BasicDefault packet in a buffer
+ ///
+ /// Buffer containing the complete packet
+ /// Length of the data in the buffer
+ /// FrameParseResult with Valid=true if packet is valid
+ public override FrameParseResult ValidatePacket(byte[] data, int length)
+ {
+ var result = new FrameParseResult();
+
+ if (length < Overhead)
+ return result;
+
+ if (data[0] != StartByte1)
+ return result;
+ if (data[1] != StartByte2)
+ return result;
+
+ int msgLength = length - Overhead;
+ int crcDataLen = msgLength + 1 + LengthBytes;
+ var ck = FletcherChecksum(data, 2, 2 + crcDataLen);
+
+ if (ck.Item1 == data[length - 2] && ck.Item2 == data[length - 1])
+ {
+ result.Valid = true;
+ result.MsgId = data[HeaderSize - 1];
+ result.MsgSize = msgLength;
+ result.MsgData = new byte[msgLength];
+ Array.Copy(data, HeaderSize, result.MsgData, 0, msgLength);
+ }
+ return result;
+ }
+
+ ///
+ /// Calculate Fletcher-16 checksum
+ ///
+ private static (byte, byte) FletcherChecksum(byte[] buffer, int start, int end)
+ {
+ byte byte1 = 0;
+ byte byte2 = 0;
+
+ for (int i = start; i < end; i++)
+ {
+ byte1 = (byte)((byte1 + buffer[i]) % 256);
+ byte2 = (byte)((byte2 + byte1) % 256);
+ }
+
+ return (byte1, byte2);
+ }
+ }
+}
diff --git a/src/struct_frame/boilerplate/csharp/BasicExtended.cs b/src/struct_frame/boilerplate/csharp/BasicExtended.cs
new file mode 100644
index 00000000..a6f8cf80
--- /dev/null
+++ b/src/struct_frame/boilerplate/csharp/BasicExtended.cs
@@ -0,0 +1,206 @@
+// Automatically generated frame parser for C#
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
+
+using System;
+using System.Collections.Generic;
+
+namespace StructFrame
+{
+ ///
+ /// BasicExtended - Frame format parser and encoder
+ ///
+ public class BasicExtended : FrameFormatBase
+ {
+ public enum ParserState
+ {
+ LookingForStart1 = 0,
+ LookingForStart2 = 1,
+ GettingMsgId = 2,
+ GettingLength = 3,
+ GettingPayload = 4
+ }
+
+ public const byte StartByte1 = 0x90;
+ public const byte StartByte2 = 0x74;
+ public const int HeaderSize = 6;
+ public const int FooterSize = 2;
+ public const int Overhead = 8;
+ public const int LengthBytes = 2;
+
+ private ParserState _state;
+ private List _buffer;
+ private int _packetSize;
+ private int _msgId;
+ private int _msgLength;
+ private int _lengthLo;
+ private Func _getMsgLength;
+
+ ///
+ /// Initialize the BasicExtended parser
+ ///
+ /// Callback function to get message length from msg_id
+ public BasicExtended(Func getMsgLength = null)
+ {
+ _getMsgLength = getMsgLength;
+ _buffer = new List();
+ Reset();
+ }
+
+ ///
+ /// Reset parser state
+ ///
+ public override void Reset()
+ {
+ _state = ParserState.LookingForStart1;
+ _buffer.Clear();
+ _packetSize = 0;
+ _msgId = 0;
+ _msgLength = 0;
+ _lengthLo = 0;
+ }
+
+ ///
+ /// Parse a single byte
+ ///
+ /// The byte to parse
+ /// FrameParseResult with Valid=true when a complete valid message is received
+ public override FrameParseResult ParseByte(byte b)
+ {
+ var result = new FrameParseResult();
+
+ switch (_state)
+ {
+ case ParserState.LookingForStart1:
+ if (b == StartByte1)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.LookingForStart2;
+ }
+ break;
+
+ case ParserState.LookingForStart2:
+ if (b == StartByte2)
+ {
+ _buffer.Add(b);
+ _state = ParserState.GettingMsgId;
+ }
+ else if (b == StartByte1)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.LookingForStart2;
+ }
+ else
+ {
+ _state = ParserState.LookingForStart1;
+ }
+ break;
+
+ case ParserState.GettingMsgId:
+ _buffer.Add(b);
+ _msgId = b;
+ _state = ParserState.GettingLength;
+ break;
+
+ case ParserState.GettingLength:
+ _buffer.Add(b);
+ if (_buffer.Count == 4)
+ {
+ _lengthLo = b;
+ }
+ else
+ {
+ _msgLength = _lengthLo | (b << 8);
+ _packetSize = Overhead + _msgLength;
+ _state = ParserState.GettingPayload;
+ }
+ break;
+
+ case ParserState.GettingPayload:
+ _buffer.Add(b);
+ if (_buffer.Count >= _packetSize)
+ {
+ result = ValidatePacket(_buffer.ToArray(), _buffer.Count);
+ _state = ParserState.LookingForStart1;
+ }
+ break;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Encode a message with BasicExtended format
+ ///
+ /// Message ID
+ /// Message data bytes
+ /// Encoded frame as byte array
+ public override byte[] Encode(int msgId, byte[] msgData)
+ {
+ var output = new List();
+ output.Add(StartByte1);
+ output.Add(StartByte2);
+ output.Add((byte)(msgData.Length & 0xFF));
+ output.Add((byte)((msgData.Length >> 8) & 0xFF));
+ output.Add((byte)msgId);
+ output.AddRange(msgData);
+
+ // Calculate Fletcher checksum
+ var ck = FletcherChecksum(output.ToArray(), 2, output.Count);
+ output.Add(ck.Item1);
+ output.Add(ck.Item2);
+ return output.ToArray();
+ }
+
+ ///
+ /// Validate a complete BasicExtended packet in a buffer
+ ///
+ /// Buffer containing the complete packet
+ /// Length of the data in the buffer
+ /// FrameParseResult with Valid=true if packet is valid
+ public override FrameParseResult ValidatePacket(byte[] data, int length)
+ {
+ var result = new FrameParseResult();
+
+ if (length < Overhead)
+ return result;
+
+ if (data[0] != StartByte1)
+ return result;
+ if (data[1] != StartByte2)
+ return result;
+
+ int msgLength = length - Overhead;
+ int crcDataLen = msgLength + 1 + LengthBytes;
+ var ck = FletcherChecksum(data, 2, 2 + crcDataLen);
+
+ if (ck.Item1 == data[length - 2] && ck.Item2 == data[length - 1])
+ {
+ result.Valid = true;
+ result.MsgId = data[HeaderSize - 1];
+ result.MsgSize = msgLength;
+ result.MsgData = new byte[msgLength];
+ Array.Copy(data, HeaderSize, result.MsgData, 0, msgLength);
+ }
+ return result;
+ }
+
+ ///
+ /// Calculate Fletcher-16 checksum
+ ///
+ private static (byte, byte) FletcherChecksum(byte[] buffer, int start, int end)
+ {
+ byte byte1 = 0;
+ byte byte2 = 0;
+
+ for (int i = start; i < end; i++)
+ {
+ byte1 = (byte)((byte1 + buffer[i]) % 256);
+ byte2 = (byte)((byte2 + byte1) % 256);
+ }
+
+ return (byte1, byte2);
+ }
+ }
+}
diff --git a/src/struct_frame/boilerplate/csharp/BasicExtendedLength.cs b/src/struct_frame/boilerplate/csharp/BasicExtendedLength.cs
new file mode 100644
index 00000000..5332769b
--- /dev/null
+++ b/src/struct_frame/boilerplate/csharp/BasicExtendedLength.cs
@@ -0,0 +1,206 @@
+// Automatically generated frame parser for C#
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
+
+using System;
+using System.Collections.Generic;
+
+namespace StructFrame
+{
+ ///
+ /// BasicExtendedLength - Frame format parser and encoder
+ ///
+ public class BasicExtendedLength : FrameFormatBase
+ {
+ public enum ParserState
+ {
+ LookingForStart1 = 0,
+ LookingForStart2 = 1,
+ GettingMsgId = 2,
+ GettingLength = 3,
+ GettingPayload = 4
+ }
+
+ public const byte StartByte1 = 0x90;
+ public const byte StartByte2 = 0x73;
+ public const int HeaderSize = 5;
+ public const int FooterSize = 2;
+ public const int Overhead = 7;
+ public const int LengthBytes = 2;
+
+ private ParserState _state;
+ private List _buffer;
+ private int _packetSize;
+ private int _msgId;
+ private int _msgLength;
+ private int _lengthLo;
+ private Func _getMsgLength;
+
+ ///
+ /// Initialize the BasicExtendedLength parser
+ ///
+ /// Callback function to get message length from msg_id
+ public BasicExtendedLength(Func getMsgLength = null)
+ {
+ _getMsgLength = getMsgLength;
+ _buffer = new List();
+ Reset();
+ }
+
+ ///
+ /// Reset parser state
+ ///
+ public override void Reset()
+ {
+ _state = ParserState.LookingForStart1;
+ _buffer.Clear();
+ _packetSize = 0;
+ _msgId = 0;
+ _msgLength = 0;
+ _lengthLo = 0;
+ }
+
+ ///
+ /// Parse a single byte
+ ///
+ /// The byte to parse
+ /// FrameParseResult with Valid=true when a complete valid message is received
+ public override FrameParseResult ParseByte(byte b)
+ {
+ var result = new FrameParseResult();
+
+ switch (_state)
+ {
+ case ParserState.LookingForStart1:
+ if (b == StartByte1)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.LookingForStart2;
+ }
+ break;
+
+ case ParserState.LookingForStart2:
+ if (b == StartByte2)
+ {
+ _buffer.Add(b);
+ _state = ParserState.GettingMsgId;
+ }
+ else if (b == StartByte1)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.LookingForStart2;
+ }
+ else
+ {
+ _state = ParserState.LookingForStart1;
+ }
+ break;
+
+ case ParserState.GettingMsgId:
+ _buffer.Add(b);
+ _msgId = b;
+ _state = ParserState.GettingLength;
+ break;
+
+ case ParserState.GettingLength:
+ _buffer.Add(b);
+ if (_buffer.Count == 4)
+ {
+ _lengthLo = b;
+ }
+ else
+ {
+ _msgLength = _lengthLo | (b << 8);
+ _packetSize = Overhead + _msgLength;
+ _state = ParserState.GettingPayload;
+ }
+ break;
+
+ case ParserState.GettingPayload:
+ _buffer.Add(b);
+ if (_buffer.Count >= _packetSize)
+ {
+ result = ValidatePacket(_buffer.ToArray(), _buffer.Count);
+ _state = ParserState.LookingForStart1;
+ }
+ break;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Encode a message with BasicExtendedLength format
+ ///
+ /// Message ID
+ /// Message data bytes
+ /// Encoded frame as byte array
+ public override byte[] Encode(int msgId, byte[] msgData)
+ {
+ var output = new List();
+ output.Add(StartByte1);
+ output.Add(StartByte2);
+ output.Add((byte)(msgData.Length & 0xFF));
+ output.Add((byte)((msgData.Length >> 8) & 0xFF));
+ output.Add((byte)msgId);
+ output.AddRange(msgData);
+
+ // Calculate Fletcher checksum
+ var ck = FletcherChecksum(output.ToArray(), 2, output.Count);
+ output.Add(ck.Item1);
+ output.Add(ck.Item2);
+ return output.ToArray();
+ }
+
+ ///
+ /// Validate a complete BasicExtendedLength packet in a buffer
+ ///
+ /// Buffer containing the complete packet
+ /// Length of the data in the buffer
+ /// FrameParseResult with Valid=true if packet is valid
+ public override FrameParseResult ValidatePacket(byte[] data, int length)
+ {
+ var result = new FrameParseResult();
+
+ if (length < Overhead)
+ return result;
+
+ if (data[0] != StartByte1)
+ return result;
+ if (data[1] != StartByte2)
+ return result;
+
+ int msgLength = length - Overhead;
+ int crcDataLen = msgLength + 1 + LengthBytes;
+ var ck = FletcherChecksum(data, 2, 2 + crcDataLen);
+
+ if (ck.Item1 == data[length - 2] && ck.Item2 == data[length - 1])
+ {
+ result.Valid = true;
+ result.MsgId = data[HeaderSize - 1];
+ result.MsgSize = msgLength;
+ result.MsgData = new byte[msgLength];
+ Array.Copy(data, HeaderSize, result.MsgData, 0, msgLength);
+ }
+ return result;
+ }
+
+ ///
+ /// Calculate Fletcher-16 checksum
+ ///
+ private static (byte, byte) FletcherChecksum(byte[] buffer, int start, int end)
+ {
+ byte byte1 = 0;
+ byte byte2 = 0;
+
+ for (int i = start; i < end; i++)
+ {
+ byte1 = (byte)((byte1 + buffer[i]) % 256);
+ byte2 = (byte)((byte2 + byte1) % 256);
+ }
+
+ return (byte1, byte2);
+ }
+ }
+}
diff --git a/src/struct_frame/boilerplate/csharp/BasicExtendedMsgIds.cs b/src/struct_frame/boilerplate/csharp/BasicExtendedMsgIds.cs
new file mode 100644
index 00000000..39c1018f
--- /dev/null
+++ b/src/struct_frame/boilerplate/csharp/BasicExtendedMsgIds.cs
@@ -0,0 +1,196 @@
+// Automatically generated frame parser for C#
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
+
+using System;
+using System.Collections.Generic;
+
+namespace StructFrame
+{
+ ///
+ /// BasicExtendedMsgIds - Frame format parser and encoder
+ ///
+ public class BasicExtendedMsgIds : FrameFormatBase
+ {
+ public enum ParserState
+ {
+ LookingForStart1 = 0,
+ LookingForStart2 = 1,
+ GettingMsgId = 2,
+ GettingLength = 3,
+ GettingPayload = 4
+ }
+
+ public const byte StartByte1 = 0x90;
+ public const byte StartByte2 = 0x72;
+ public const int HeaderSize = 5;
+ public const int FooterSize = 2;
+ public const int Overhead = 7;
+ public const int LengthBytes = 1;
+
+ private ParserState _state;
+ private List _buffer;
+ private int _packetSize;
+ private int _msgId;
+ private int _msgLength;
+ private Func _getMsgLength;
+
+ ///
+ /// Initialize the BasicExtendedMsgIds parser
+ ///
+ /// Callback function to get message length from msg_id
+ public BasicExtendedMsgIds(Func getMsgLength = null)
+ {
+ _getMsgLength = getMsgLength;
+ _buffer = new List();
+ Reset();
+ }
+
+ ///
+ /// Reset parser state
+ ///
+ public override void Reset()
+ {
+ _state = ParserState.LookingForStart1;
+ _buffer.Clear();
+ _packetSize = 0;
+ _msgId = 0;
+ _msgLength = 0;
+ }
+
+ ///
+ /// Parse a single byte
+ ///
+ /// The byte to parse
+ /// FrameParseResult with Valid=true when a complete valid message is received
+ public override FrameParseResult ParseByte(byte b)
+ {
+ var result = new FrameParseResult();
+
+ switch (_state)
+ {
+ case ParserState.LookingForStart1:
+ if (b == StartByte1)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.LookingForStart2;
+ }
+ break;
+
+ case ParserState.LookingForStart2:
+ if (b == StartByte2)
+ {
+ _buffer.Add(b);
+ _state = ParserState.GettingMsgId;
+ }
+ else if (b == StartByte1)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.LookingForStart2;
+ }
+ else
+ {
+ _state = ParserState.LookingForStart1;
+ }
+ break;
+
+ case ParserState.GettingMsgId:
+ _buffer.Add(b);
+ _msgId = b;
+ _state = ParserState.GettingLength;
+ break;
+
+ case ParserState.GettingLength:
+ _buffer.Add(b);
+ _msgLength = b;
+ _packetSize = Overhead + _msgLength;
+ _state = ParserState.GettingPayload;
+ break;
+
+ case ParserState.GettingPayload:
+ _buffer.Add(b);
+ if (_buffer.Count >= _packetSize)
+ {
+ result = ValidatePacket(_buffer.ToArray(), _buffer.Count);
+ _state = ParserState.LookingForStart1;
+ }
+ break;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Encode a message with BasicExtendedMsgIds format
+ ///
+ /// Message ID
+ /// Message data bytes
+ /// Encoded frame as byte array
+ public override byte[] Encode(int msgId, byte[] msgData)
+ {
+ var output = new List();
+ output.Add(StartByte1);
+ output.Add(StartByte2);
+ output.Add((byte)(msgData.Length & 0xFF));
+ output.Add((byte)msgId);
+ output.AddRange(msgData);
+
+ // Calculate Fletcher checksum
+ var ck = FletcherChecksum(output.ToArray(), 2, output.Count);
+ output.Add(ck.Item1);
+ output.Add(ck.Item2);
+ return output.ToArray();
+ }
+
+ ///
+ /// Validate a complete BasicExtendedMsgIds packet in a buffer
+ ///
+ /// Buffer containing the complete packet
+ /// Length of the data in the buffer
+ /// FrameParseResult with Valid=true if packet is valid
+ public override FrameParseResult ValidatePacket(byte[] data, int length)
+ {
+ var result = new FrameParseResult();
+
+ if (length < Overhead)
+ return result;
+
+ if (data[0] != StartByte1)
+ return result;
+ if (data[1] != StartByte2)
+ return result;
+
+ int msgLength = length - Overhead;
+ int crcDataLen = msgLength + 1 + LengthBytes;
+ var ck = FletcherChecksum(data, 2, 2 + crcDataLen);
+
+ if (ck.Item1 == data[length - 2] && ck.Item2 == data[length - 1])
+ {
+ result.Valid = true;
+ result.MsgId = data[HeaderSize - 1];
+ result.MsgSize = msgLength;
+ result.MsgData = new byte[msgLength];
+ Array.Copy(data, HeaderSize, result.MsgData, 0, msgLength);
+ }
+ return result;
+ }
+
+ ///
+ /// Calculate Fletcher-16 checksum
+ ///
+ private static (byte, byte) FletcherChecksum(byte[] buffer, int start, int end)
+ {
+ byte byte1 = 0;
+ byte byte2 = 0;
+
+ for (int i = start; i < end; i++)
+ {
+ byte1 = (byte)((byte1 + buffer[i]) % 256);
+ byte2 = (byte)((byte2 + byte1) % 256);
+ }
+
+ return (byte1, byte2);
+ }
+ }
+}
diff --git a/src/struct_frame/boilerplate/csharp/BasicExtendedMultiSystemStream.cs b/src/struct_frame/boilerplate/csharp/BasicExtendedMultiSystemStream.cs
new file mode 100644
index 00000000..e2310887
--- /dev/null
+++ b/src/struct_frame/boilerplate/csharp/BasicExtendedMultiSystemStream.cs
@@ -0,0 +1,206 @@
+// Automatically generated frame parser for C#
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
+
+using System;
+using System.Collections.Generic;
+
+namespace StructFrame
+{
+ ///
+ /// BasicExtendedMultiSystemStream - Frame format parser and encoder
+ ///
+ public class BasicExtendedMultiSystemStream : FrameFormatBase
+ {
+ public enum ParserState
+ {
+ LookingForStart1 = 0,
+ LookingForStart2 = 1,
+ GettingMsgId = 2,
+ GettingLength = 3,
+ GettingPayload = 4
+ }
+
+ public const byte StartByte1 = 0x90;
+ public const byte StartByte2 = 0x78;
+ public const int HeaderSize = 9;
+ public const int FooterSize = 2;
+ public const int Overhead = 11;
+ public const int LengthBytes = 2;
+
+ private ParserState _state;
+ private List _buffer;
+ private int _packetSize;
+ private int _msgId;
+ private int _msgLength;
+ private int _lengthLo;
+ private Func _getMsgLength;
+
+ ///
+ /// Initialize the BasicExtendedMultiSystemStream parser
+ ///
+ /// Callback function to get message length from msg_id
+ public BasicExtendedMultiSystemStream(Func getMsgLength = null)
+ {
+ _getMsgLength = getMsgLength;
+ _buffer = new List();
+ Reset();
+ }
+
+ ///
+ /// Reset parser state
+ ///
+ public override void Reset()
+ {
+ _state = ParserState.LookingForStart1;
+ _buffer.Clear();
+ _packetSize = 0;
+ _msgId = 0;
+ _msgLength = 0;
+ _lengthLo = 0;
+ }
+
+ ///
+ /// Parse a single byte
+ ///
+ /// The byte to parse
+ /// FrameParseResult with Valid=true when a complete valid message is received
+ public override FrameParseResult ParseByte(byte b)
+ {
+ var result = new FrameParseResult();
+
+ switch (_state)
+ {
+ case ParserState.LookingForStart1:
+ if (b == StartByte1)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.LookingForStart2;
+ }
+ break;
+
+ case ParserState.LookingForStart2:
+ if (b == StartByte2)
+ {
+ _buffer.Add(b);
+ _state = ParserState.GettingMsgId;
+ }
+ else if (b == StartByte1)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.LookingForStart2;
+ }
+ else
+ {
+ _state = ParserState.LookingForStart1;
+ }
+ break;
+
+ case ParserState.GettingMsgId:
+ _buffer.Add(b);
+ _msgId = b;
+ _state = ParserState.GettingLength;
+ break;
+
+ case ParserState.GettingLength:
+ _buffer.Add(b);
+ if (_buffer.Count == 4)
+ {
+ _lengthLo = b;
+ }
+ else
+ {
+ _msgLength = _lengthLo | (b << 8);
+ _packetSize = Overhead + _msgLength;
+ _state = ParserState.GettingPayload;
+ }
+ break;
+
+ case ParserState.GettingPayload:
+ _buffer.Add(b);
+ if (_buffer.Count >= _packetSize)
+ {
+ result = ValidatePacket(_buffer.ToArray(), _buffer.Count);
+ _state = ParserState.LookingForStart1;
+ }
+ break;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Encode a message with BasicExtendedMultiSystemStream format
+ ///
+ /// Message ID
+ /// Message data bytes
+ /// Encoded frame as byte array
+ public override byte[] Encode(int msgId, byte[] msgData)
+ {
+ var output = new List();
+ output.Add(StartByte1);
+ output.Add(StartByte2);
+ output.Add((byte)(msgData.Length & 0xFF));
+ output.Add((byte)((msgData.Length >> 8) & 0xFF));
+ output.Add((byte)msgId);
+ output.AddRange(msgData);
+
+ // Calculate Fletcher checksum
+ var ck = FletcherChecksum(output.ToArray(), 2, output.Count);
+ output.Add(ck.Item1);
+ output.Add(ck.Item2);
+ return output.ToArray();
+ }
+
+ ///
+ /// Validate a complete BasicExtendedMultiSystemStream packet in a buffer
+ ///
+ /// Buffer containing the complete packet
+ /// Length of the data in the buffer
+ /// FrameParseResult with Valid=true if packet is valid
+ public override FrameParseResult ValidatePacket(byte[] data, int length)
+ {
+ var result = new FrameParseResult();
+
+ if (length < Overhead)
+ return result;
+
+ if (data[0] != StartByte1)
+ return result;
+ if (data[1] != StartByte2)
+ return result;
+
+ int msgLength = length - Overhead;
+ int crcDataLen = msgLength + 1 + LengthBytes;
+ var ck = FletcherChecksum(data, 2, 2 + crcDataLen);
+
+ if (ck.Item1 == data[length - 2] && ck.Item2 == data[length - 1])
+ {
+ result.Valid = true;
+ result.MsgId = data[HeaderSize - 1];
+ result.MsgSize = msgLength;
+ result.MsgData = new byte[msgLength];
+ Array.Copy(data, HeaderSize, result.MsgData, 0, msgLength);
+ }
+ return result;
+ }
+
+ ///
+ /// Calculate Fletcher-16 checksum
+ ///
+ private static (byte, byte) FletcherChecksum(byte[] buffer, int start, int end)
+ {
+ byte byte1 = 0;
+ byte byte2 = 0;
+
+ for (int i = start; i < end; i++)
+ {
+ byte1 = (byte)((byte1 + buffer[i]) % 256);
+ byte2 = (byte)((byte2 + byte1) % 256);
+ }
+
+ return (byte1, byte2);
+ }
+ }
+}
diff --git a/src/struct_frame/boilerplate/csharp/BasicMinimal.cs b/src/struct_frame/boilerplate/csharp/BasicMinimal.cs
new file mode 100644
index 00000000..b8e0dd12
--- /dev/null
+++ b/src/struct_frame/boilerplate/csharp/BasicMinimal.cs
@@ -0,0 +1,188 @@
+// Automatically generated frame parser for C#
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
+
+using System;
+using System.Collections.Generic;
+
+namespace StructFrame
+{
+ ///
+ /// BasicMinimal - Frame format parser and encoder
+ ///
+ public class BasicMinimal : FrameFormatBase
+ {
+ public enum ParserState
+ {
+ LookingForStart1 = 0,
+ LookingForStart2 = 1,
+ GettingMsgId = 2,
+ GettingPayload = 3
+ }
+
+ public const byte StartByte1 = 0x90;
+ public const byte StartByte2 = 0x70;
+ public const int HeaderSize = 3;
+ public const int FooterSize = 0;
+ public const int Overhead = 3;
+
+ private ParserState _state;
+ private List _buffer;
+ private int _packetSize;
+ private int _msgId;
+ private Func _getMsgLength;
+
+ ///
+ /// Initialize the BasicMinimal parser
+ ///
+ /// Callback function to get message length from msg_id
+ public BasicMinimal(Func getMsgLength = null)
+ {
+ _getMsgLength = getMsgLength;
+ _buffer = new List();
+ Reset();
+ }
+
+ ///
+ /// Reset parser state
+ ///
+ public override void Reset()
+ {
+ _state = ParserState.LookingForStart1;
+ _buffer.Clear();
+ _packetSize = 0;
+ _msgId = 0;
+ }
+
+ ///
+ /// Parse a single byte
+ ///
+ /// The byte to parse
+ /// FrameParseResult with Valid=true when a complete valid message is received
+ public override FrameParseResult ParseByte(byte b)
+ {
+ var result = new FrameParseResult();
+
+ switch (_state)
+ {
+ case ParserState.LookingForStart1:
+ if (b == StartByte1)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.LookingForStart2;
+ }
+ break;
+
+ case ParserState.LookingForStart2:
+ if (b == StartByte2)
+ {
+ _buffer.Add(b);
+ _state = ParserState.GettingMsgId;
+ }
+ else if (b == StartByte1)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.LookingForStart2;
+ }
+ else
+ {
+ _state = ParserState.LookingForStart1;
+ }
+ break;
+
+ case ParserState.GettingMsgId:
+ _buffer.Add(b);
+ _msgId = b;
+ if (_getMsgLength != null)
+ {
+ int? msgLength = _getMsgLength(b);
+ if (msgLength.HasValue)
+ {
+ _packetSize = Overhead + msgLength.Value;
+ _state = ParserState.GettingPayload;
+ }
+ else
+ {
+ _state = ParserState.LookingForStart1;
+ }
+ }
+ else
+ {
+ _state = ParserState.LookingForStart1;
+ }
+ break;
+
+ case ParserState.GettingPayload:
+ _buffer.Add(b);
+ if (_buffer.Count >= _packetSize)
+ {
+ result = ValidatePacket(_buffer.ToArray(), _buffer.Count);
+ _state = ParserState.LookingForStart1;
+ }
+ break;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Encode a message with BasicMinimal format
+ ///
+ /// Message ID
+ /// Message data bytes
+ /// Encoded frame as byte array
+ public override byte[] Encode(int msgId, byte[] msgData)
+ {
+ var output = new List();
+ output.Add(StartByte1);
+ output.Add(StartByte2);
+ output.Add((byte)msgId);
+ output.AddRange(msgData);
+ return output.ToArray();
+ }
+
+ ///
+ /// Validate a complete BasicMinimal packet in a buffer
+ ///
+ /// Buffer containing the complete packet
+ /// Length of the data in the buffer
+ /// FrameParseResult with Valid=true if packet is valid
+ public override FrameParseResult ValidatePacket(byte[] data, int length)
+ {
+ var result = new FrameParseResult();
+
+ if (length < Overhead)
+ return result;
+
+ if (data[0] != StartByte1)
+ return result;
+ if (data[1] != StartByte2)
+ return result;
+
+ result.Valid = true;
+ result.MsgId = data[HeaderSize - 1];
+ result.MsgSize = length - HeaderSize;
+ result.MsgData = new byte[result.MsgSize];
+ Array.Copy(data, HeaderSize, result.MsgData, 0, result.MsgSize);
+ return result;
+ }
+
+ ///
+ /// Calculate Fletcher-16 checksum
+ ///
+ private static (byte, byte) FletcherChecksum(byte[] buffer, int start, int end)
+ {
+ byte byte1 = 0;
+ byte byte2 = 0;
+
+ for (int i = start; i < end; i++)
+ {
+ byte1 = (byte)((byte1 + buffer[i]) % 256);
+ byte2 = (byte)((byte2 + byte1) % 256);
+ }
+
+ return (byte1, byte2);
+ }
+ }
+}
diff --git a/src/struct_frame/boilerplate/csharp/BasicMultiSystemStream.cs b/src/struct_frame/boilerplate/csharp/BasicMultiSystemStream.cs
new file mode 100644
index 00000000..20516901
--- /dev/null
+++ b/src/struct_frame/boilerplate/csharp/BasicMultiSystemStream.cs
@@ -0,0 +1,196 @@
+// Automatically generated frame parser for C#
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
+
+using System;
+using System.Collections.Generic;
+
+namespace StructFrame
+{
+ ///
+ /// BasicMultiSystemStream - Frame format parser and encoder
+ ///
+ public class BasicMultiSystemStream : FrameFormatBase
+ {
+ public enum ParserState
+ {
+ LookingForStart1 = 0,
+ LookingForStart2 = 1,
+ GettingMsgId = 2,
+ GettingLength = 3,
+ GettingPayload = 4
+ }
+
+ public const byte StartByte1 = 0x90;
+ public const byte StartByte2 = 0x77;
+ public const int HeaderSize = 7;
+ public const int FooterSize = 2;
+ public const int Overhead = 9;
+ public const int LengthBytes = 1;
+
+ private ParserState _state;
+ private List _buffer;
+ private int _packetSize;
+ private int _msgId;
+ private int _msgLength;
+ private Func _getMsgLength;
+
+ ///
+ /// Initialize the BasicMultiSystemStream parser
+ ///
+ /// Callback function to get message length from msg_id
+ public BasicMultiSystemStream(Func getMsgLength = null)
+ {
+ _getMsgLength = getMsgLength;
+ _buffer = new List();
+ Reset();
+ }
+
+ ///
+ /// Reset parser state
+ ///
+ public override void Reset()
+ {
+ _state = ParserState.LookingForStart1;
+ _buffer.Clear();
+ _packetSize = 0;
+ _msgId = 0;
+ _msgLength = 0;
+ }
+
+ ///
+ /// Parse a single byte
+ ///
+ /// The byte to parse
+ /// FrameParseResult with Valid=true when a complete valid message is received
+ public override FrameParseResult ParseByte(byte b)
+ {
+ var result = new FrameParseResult();
+
+ switch (_state)
+ {
+ case ParserState.LookingForStart1:
+ if (b == StartByte1)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.LookingForStart2;
+ }
+ break;
+
+ case ParserState.LookingForStart2:
+ if (b == StartByte2)
+ {
+ _buffer.Add(b);
+ _state = ParserState.GettingMsgId;
+ }
+ else if (b == StartByte1)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.LookingForStart2;
+ }
+ else
+ {
+ _state = ParserState.LookingForStart1;
+ }
+ break;
+
+ case ParserState.GettingMsgId:
+ _buffer.Add(b);
+ _msgId = b;
+ _state = ParserState.GettingLength;
+ break;
+
+ case ParserState.GettingLength:
+ _buffer.Add(b);
+ _msgLength = b;
+ _packetSize = Overhead + _msgLength;
+ _state = ParserState.GettingPayload;
+ break;
+
+ case ParserState.GettingPayload:
+ _buffer.Add(b);
+ if (_buffer.Count >= _packetSize)
+ {
+ result = ValidatePacket(_buffer.ToArray(), _buffer.Count);
+ _state = ParserState.LookingForStart1;
+ }
+ break;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Encode a message with BasicMultiSystemStream format
+ ///
+ /// Message ID
+ /// Message data bytes
+ /// Encoded frame as byte array
+ public override byte[] Encode(int msgId, byte[] msgData)
+ {
+ var output = new List();
+ output.Add(StartByte1);
+ output.Add(StartByte2);
+ output.Add((byte)(msgData.Length & 0xFF));
+ output.Add((byte)msgId);
+ output.AddRange(msgData);
+
+ // Calculate Fletcher checksum
+ var ck = FletcherChecksum(output.ToArray(), 2, output.Count);
+ output.Add(ck.Item1);
+ output.Add(ck.Item2);
+ return output.ToArray();
+ }
+
+ ///
+ /// Validate a complete BasicMultiSystemStream packet in a buffer
+ ///
+ /// Buffer containing the complete packet
+ /// Length of the data in the buffer
+ /// FrameParseResult with Valid=true if packet is valid
+ public override FrameParseResult ValidatePacket(byte[] data, int length)
+ {
+ var result = new FrameParseResult();
+
+ if (length < Overhead)
+ return result;
+
+ if (data[0] != StartByte1)
+ return result;
+ if (data[1] != StartByte2)
+ return result;
+
+ int msgLength = length - Overhead;
+ int crcDataLen = msgLength + 1 + LengthBytes;
+ var ck = FletcherChecksum(data, 2, 2 + crcDataLen);
+
+ if (ck.Item1 == data[length - 2] && ck.Item2 == data[length - 1])
+ {
+ result.Valid = true;
+ result.MsgId = data[HeaderSize - 1];
+ result.MsgSize = msgLength;
+ result.MsgData = new byte[msgLength];
+ Array.Copy(data, HeaderSize, result.MsgData, 0, msgLength);
+ }
+ return result;
+ }
+
+ ///
+ /// Calculate Fletcher-16 checksum
+ ///
+ private static (byte, byte) FletcherChecksum(byte[] buffer, int start, int end)
+ {
+ byte byte1 = 0;
+ byte byte2 = 0;
+
+ for (int i = start; i < end; i++)
+ {
+ byte1 = (byte)((byte1 + buffer[i]) % 256);
+ byte2 = (byte)((byte2 + byte1) % 256);
+ }
+
+ return (byte1, byte2);
+ }
+ }
+}
diff --git a/src/struct_frame/boilerplate/csharp/BasicSeq.cs b/src/struct_frame/boilerplate/csharp/BasicSeq.cs
new file mode 100644
index 00000000..5b0afb2b
--- /dev/null
+++ b/src/struct_frame/boilerplate/csharp/BasicSeq.cs
@@ -0,0 +1,196 @@
+// Automatically generated frame parser for C#
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
+
+using System;
+using System.Collections.Generic;
+
+namespace StructFrame
+{
+ ///
+ /// BasicSeq - Frame format parser and encoder
+ ///
+ public class BasicSeq : FrameFormatBase
+ {
+ public enum ParserState
+ {
+ LookingForStart1 = 0,
+ LookingForStart2 = 1,
+ GettingMsgId = 2,
+ GettingLength = 3,
+ GettingPayload = 4
+ }
+
+ public const byte StartByte1 = 0x90;
+ public const byte StartByte2 = 0x76;
+ public const int HeaderSize = 5;
+ public const int FooterSize = 2;
+ public const int Overhead = 7;
+ public const int LengthBytes = 1;
+
+ private ParserState _state;
+ private List _buffer;
+ private int _packetSize;
+ private int _msgId;
+ private int _msgLength;
+ private Func _getMsgLength;
+
+ ///
+ /// Initialize the BasicSeq parser
+ ///
+ /// Callback function to get message length from msg_id
+ public BasicSeq(Func getMsgLength = null)
+ {
+ _getMsgLength = getMsgLength;
+ _buffer = new List();
+ Reset();
+ }
+
+ ///
+ /// Reset parser state
+ ///
+ public override void Reset()
+ {
+ _state = ParserState.LookingForStart1;
+ _buffer.Clear();
+ _packetSize = 0;
+ _msgId = 0;
+ _msgLength = 0;
+ }
+
+ ///
+ /// Parse a single byte
+ ///
+ /// The byte to parse
+ /// FrameParseResult with Valid=true when a complete valid message is received
+ public override FrameParseResult ParseByte(byte b)
+ {
+ var result = new FrameParseResult();
+
+ switch (_state)
+ {
+ case ParserState.LookingForStart1:
+ if (b == StartByte1)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.LookingForStart2;
+ }
+ break;
+
+ case ParserState.LookingForStart2:
+ if (b == StartByte2)
+ {
+ _buffer.Add(b);
+ _state = ParserState.GettingMsgId;
+ }
+ else if (b == StartByte1)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.LookingForStart2;
+ }
+ else
+ {
+ _state = ParserState.LookingForStart1;
+ }
+ break;
+
+ case ParserState.GettingMsgId:
+ _buffer.Add(b);
+ _msgId = b;
+ _state = ParserState.GettingLength;
+ break;
+
+ case ParserState.GettingLength:
+ _buffer.Add(b);
+ _msgLength = b;
+ _packetSize = Overhead + _msgLength;
+ _state = ParserState.GettingPayload;
+ break;
+
+ case ParserState.GettingPayload:
+ _buffer.Add(b);
+ if (_buffer.Count >= _packetSize)
+ {
+ result = ValidatePacket(_buffer.ToArray(), _buffer.Count);
+ _state = ParserState.LookingForStart1;
+ }
+ break;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Encode a message with BasicSeq format
+ ///
+ /// Message ID
+ /// Message data bytes
+ /// Encoded frame as byte array
+ public override byte[] Encode(int msgId, byte[] msgData)
+ {
+ var output = new List();
+ output.Add(StartByte1);
+ output.Add(StartByte2);
+ output.Add((byte)(msgData.Length & 0xFF));
+ output.Add((byte)msgId);
+ output.AddRange(msgData);
+
+ // Calculate Fletcher checksum
+ var ck = FletcherChecksum(output.ToArray(), 2, output.Count);
+ output.Add(ck.Item1);
+ output.Add(ck.Item2);
+ return output.ToArray();
+ }
+
+ ///
+ /// Validate a complete BasicSeq packet in a buffer
+ ///
+ /// Buffer containing the complete packet
+ /// Length of the data in the buffer
+ /// FrameParseResult with Valid=true if packet is valid
+ public override FrameParseResult ValidatePacket(byte[] data, int length)
+ {
+ var result = new FrameParseResult();
+
+ if (length < Overhead)
+ return result;
+
+ if (data[0] != StartByte1)
+ return result;
+ if (data[1] != StartByte2)
+ return result;
+
+ int msgLength = length - Overhead;
+ int crcDataLen = msgLength + 1 + LengthBytes;
+ var ck = FletcherChecksum(data, 2, 2 + crcDataLen);
+
+ if (ck.Item1 == data[length - 2] && ck.Item2 == data[length - 1])
+ {
+ result.Valid = true;
+ result.MsgId = data[HeaderSize - 1];
+ result.MsgSize = msgLength;
+ result.MsgData = new byte[msgLength];
+ Array.Copy(data, HeaderSize, result.MsgData, 0, msgLength);
+ }
+ return result;
+ }
+
+ ///
+ /// Calculate Fletcher-16 checksum
+ ///
+ private static (byte, byte) FletcherChecksum(byte[] buffer, int start, int end)
+ {
+ byte byte1 = 0;
+ byte byte2 = 0;
+
+ for (int i = start; i < end; i++)
+ {
+ byte1 = (byte)((byte1 + buffer[i]) % 256);
+ byte2 = (byte)((byte2 + byte1) % 256);
+ }
+
+ return (byte1, byte2);
+ }
+ }
+}
diff --git a/src/struct_frame/boilerplate/csharp/BasicSysComp.cs b/src/struct_frame/boilerplate/csharp/BasicSysComp.cs
new file mode 100644
index 00000000..75b3f70a
--- /dev/null
+++ b/src/struct_frame/boilerplate/csharp/BasicSysComp.cs
@@ -0,0 +1,196 @@
+// Automatically generated frame parser for C#
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
+
+using System;
+using System.Collections.Generic;
+
+namespace StructFrame
+{
+ ///
+ /// BasicSysComp - Frame format parser and encoder
+ ///
+ public class BasicSysComp : FrameFormatBase
+ {
+ public enum ParserState
+ {
+ LookingForStart1 = 0,
+ LookingForStart2 = 1,
+ GettingMsgId = 2,
+ GettingLength = 3,
+ GettingPayload = 4
+ }
+
+ public const byte StartByte1 = 0x90;
+ public const byte StartByte2 = 0x75;
+ public const int HeaderSize = 6;
+ public const int FooterSize = 2;
+ public const int Overhead = 8;
+ public const int LengthBytes = 1;
+
+ private ParserState _state;
+ private List _buffer;
+ private int _packetSize;
+ private int _msgId;
+ private int _msgLength;
+ private Func _getMsgLength;
+
+ ///
+ /// Initialize the BasicSysComp parser
+ ///
+ /// Callback function to get message length from msg_id
+ public BasicSysComp(Func getMsgLength = null)
+ {
+ _getMsgLength = getMsgLength;
+ _buffer = new List();
+ Reset();
+ }
+
+ ///
+ /// Reset parser state
+ ///
+ public override void Reset()
+ {
+ _state = ParserState.LookingForStart1;
+ _buffer.Clear();
+ _packetSize = 0;
+ _msgId = 0;
+ _msgLength = 0;
+ }
+
+ ///
+ /// Parse a single byte
+ ///
+ /// The byte to parse
+ /// FrameParseResult with Valid=true when a complete valid message is received
+ public override FrameParseResult ParseByte(byte b)
+ {
+ var result = new FrameParseResult();
+
+ switch (_state)
+ {
+ case ParserState.LookingForStart1:
+ if (b == StartByte1)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.LookingForStart2;
+ }
+ break;
+
+ case ParserState.LookingForStart2:
+ if (b == StartByte2)
+ {
+ _buffer.Add(b);
+ _state = ParserState.GettingMsgId;
+ }
+ else if (b == StartByte1)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.LookingForStart2;
+ }
+ else
+ {
+ _state = ParserState.LookingForStart1;
+ }
+ break;
+
+ case ParserState.GettingMsgId:
+ _buffer.Add(b);
+ _msgId = b;
+ _state = ParserState.GettingLength;
+ break;
+
+ case ParserState.GettingLength:
+ _buffer.Add(b);
+ _msgLength = b;
+ _packetSize = Overhead + _msgLength;
+ _state = ParserState.GettingPayload;
+ break;
+
+ case ParserState.GettingPayload:
+ _buffer.Add(b);
+ if (_buffer.Count >= _packetSize)
+ {
+ result = ValidatePacket(_buffer.ToArray(), _buffer.Count);
+ _state = ParserState.LookingForStart1;
+ }
+ break;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Encode a message with BasicSysComp format
+ ///
+ /// Message ID
+ /// Message data bytes
+ /// Encoded frame as byte array
+ public override byte[] Encode(int msgId, byte[] msgData)
+ {
+ var output = new List();
+ output.Add(StartByte1);
+ output.Add(StartByte2);
+ output.Add((byte)(msgData.Length & 0xFF));
+ output.Add((byte)msgId);
+ output.AddRange(msgData);
+
+ // Calculate Fletcher checksum
+ var ck = FletcherChecksum(output.ToArray(), 2, output.Count);
+ output.Add(ck.Item1);
+ output.Add(ck.Item2);
+ return output.ToArray();
+ }
+
+ ///
+ /// Validate a complete BasicSysComp packet in a buffer
+ ///
+ /// Buffer containing the complete packet
+ /// Length of the data in the buffer
+ /// FrameParseResult with Valid=true if packet is valid
+ public override FrameParseResult ValidatePacket(byte[] data, int length)
+ {
+ var result = new FrameParseResult();
+
+ if (length < Overhead)
+ return result;
+
+ if (data[0] != StartByte1)
+ return result;
+ if (data[1] != StartByte2)
+ return result;
+
+ int msgLength = length - Overhead;
+ int crcDataLen = msgLength + 1 + LengthBytes;
+ var ck = FletcherChecksum(data, 2, 2 + crcDataLen);
+
+ if (ck.Item1 == data[length - 2] && ck.Item2 == data[length - 1])
+ {
+ result.Valid = true;
+ result.MsgId = data[HeaderSize - 1];
+ result.MsgSize = msgLength;
+ result.MsgData = new byte[msgLength];
+ Array.Copy(data, HeaderSize, result.MsgData, 0, msgLength);
+ }
+ return result;
+ }
+
+ ///
+ /// Calculate Fletcher-16 checksum
+ ///
+ private static (byte, byte) FletcherChecksum(byte[] buffer, int start, int end)
+ {
+ byte byte1 = 0;
+ byte byte2 = 0;
+
+ for (int i = start; i < end; i++)
+ {
+ byte1 = (byte)((byte1 + buffer[i]) % 256);
+ byte2 = (byte)((byte2 + byte1) % 256);
+ }
+
+ return (byte1, byte2);
+ }
+ }
+}
diff --git a/src/struct_frame/boilerplate/csharp/FrameBase.cs b/src/struct_frame/boilerplate/csharp/FrameBase.cs
new file mode 100644
index 00000000..d04a8974
--- /dev/null
+++ b/src/struct_frame/boilerplate/csharp/FrameBase.cs
@@ -0,0 +1,98 @@
+// Automatically generated frame base for C#
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
+// This file provides base utilities for struct frame parsing and encoding
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace StructFrame
+{
+ ///
+ /// Represents the result of parsing a frame
+ ///
+ public struct FrameParseResult
+ {
+ public bool Valid;
+ public int MsgId;
+ public byte[] MsgData;
+ public int MsgSize;
+ }
+
+ ///
+ /// Base class for frame format implementations
+ ///
+ public abstract class FrameFormatBase
+ {
+ ///
+ /// Encodes a message into a byte array
+ ///
+ public abstract byte[] Encode(int msgId, byte[] msgData);
+
+ ///
+ /// Validates and parses a packet
+ ///
+ public abstract FrameParseResult ValidatePacket(byte[] data, int length);
+
+ ///
+ /// Parses a single byte (for streaming parsers)
+ ///
+ public abstract FrameParseResult ParseByte(byte b);
+
+ ///
+ /// Resets the parser state
+ ///
+ public abstract void Reset();
+ }
+
+ ///
+ /// Helper methods for struct serialization
+ ///
+ public static class StructHelper
+ {
+ ///
+ /// Converts a struct to a byte array
+ ///
+ public static byte[] StructToBytes(T structure) where T : struct
+ {
+ int size = Marshal.SizeOf(typeof(T));
+ byte[] buffer = new byte[size];
+ IntPtr ptr = Marshal.AllocHGlobal(size);
+ try
+ {
+ Marshal.StructureToPtr(structure, ptr, false);
+ Marshal.Copy(ptr, buffer, 0, size);
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(ptr);
+ }
+ return buffer;
+ }
+
+ ///
+ /// Converts a byte array to a struct
+ ///
+ public static T BytesToStruct(byte[] buffer) where T : struct
+ {
+ return BytesToStruct(buffer, 0);
+ }
+
+ ///
+ /// Converts a byte array to a struct starting at an offset
+ ///
+ public static T BytesToStruct(byte[] buffer, int offset) where T : struct
+ {
+ int size = Marshal.SizeOf(typeof(T));
+ IntPtr ptr = Marshal.AllocHGlobal(size);
+ try
+ {
+ Marshal.Copy(buffer, offset, ptr, size);
+ return Marshal.PtrToStructure(ptr);
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(ptr);
+ }
+ }
+ }
+}
diff --git a/src/struct_frame/boilerplate/csharp/FrameFormatConfig.cs b/src/struct_frame/boilerplate/csharp/FrameFormatConfig.cs
new file mode 100644
index 00000000..24ce5454
--- /dev/null
+++ b/src/struct_frame/boilerplate/csharp/FrameFormatConfig.cs
@@ -0,0 +1,198 @@
+// Automatically generated frame parser for C#
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
+
+using System;
+using System.Collections.Generic;
+
+namespace StructFrame
+{
+ ///
+ /// FrameFormatConfig - Frame format parser and encoder
+ ///
+ public class FrameFormatConfig : FrameFormatBase
+ {
+ public enum ParserState
+ {
+ LookingForStart1 = 0,
+ LookingForStart2 = 1,
+ GettingMsgId = 2,
+ GettingPayload = 3
+ }
+
+ public const byte StartByte1 = 0x90;
+ public const byte StartByte2 = 0x71;
+ public const int HeaderSize = 2;
+ public const int FooterSize = 1;
+ public const int Overhead = 3;
+
+ private ParserState _state;
+ private List _buffer;
+ private int _packetSize;
+ private int _msgId;
+ private Func _getMsgLength;
+
+ ///
+ /// Initialize the FrameFormatConfig parser
+ ///
+ /// Callback function to get message length from msg_id
+ public FrameFormatConfig(Func getMsgLength = null)
+ {
+ _getMsgLength = getMsgLength;
+ _buffer = new List();
+ Reset();
+ }
+
+ ///
+ /// Reset parser state
+ ///
+ public override void Reset()
+ {
+ _state = ParserState.LookingForStart1;
+ _buffer.Clear();
+ _packetSize = 0;
+ _msgId = 0;
+ }
+
+ ///
+ /// Parse a single byte
+ ///
+ /// The byte to parse
+ /// FrameParseResult with Valid=true when a complete valid message is received
+ public override FrameParseResult ParseByte(byte b)
+ {
+ var result = new FrameParseResult();
+
+ switch (_state)
+ {
+ case ParserState.LookingForStart1:
+ if (b == StartByte1)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.LookingForStart2;
+ }
+ break;
+
+ case ParserState.LookingForStart2:
+ if (b == StartByte2)
+ {
+ _buffer.Add(b);
+ _state = ParserState.GettingMsgId;
+ }
+ else if (b == StartByte1)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.LookingForStart2;
+ }
+ else
+ {
+ _state = ParserState.LookingForStart1;
+ }
+ break;
+
+ case ParserState.GettingMsgId:
+ _buffer.Add(b);
+ _msgId = b;
+ if (_getMsgLength != null)
+ {
+ int? msgLength = _getMsgLength(b);
+ if (msgLength.HasValue)
+ {
+ _packetSize = Overhead + msgLength.Value;
+ _state = ParserState.GettingPayload;
+ }
+ else
+ {
+ _state = ParserState.LookingForStart1;
+ }
+ }
+ else
+ {
+ _state = ParserState.LookingForStart1;
+ }
+ break;
+
+ case ParserState.GettingPayload:
+ _buffer.Add(b);
+ if (_buffer.Count >= _packetSize)
+ {
+ result = ValidatePacket(_buffer.ToArray(), _buffer.Count);
+ _state = ParserState.LookingForStart1;
+ }
+ break;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Encode a message with FrameFormatConfig format
+ ///
+ /// Message ID
+ /// Message data bytes
+ /// Encoded frame as byte array
+ public override byte[] Encode(int msgId, byte[] msgData)
+ {
+ var output = new List();
+ output.Add(StartByte1);
+ output.Add(StartByte2);
+ output.Add((byte)msgId);
+ output.AddRange(msgData);
+ var ck = FletcherChecksum(output.ToArray(), 2, output.Count);
+ output.Add(ck.Item1);
+ output.Add(ck.Item2);
+ return output.ToArray();
+ }
+
+ ///
+ /// Validate a complete FrameFormatConfig packet in a buffer
+ ///
+ /// Buffer containing the complete packet
+ /// Length of the data in the buffer
+ /// FrameParseResult with Valid=true if packet is valid
+ public override FrameParseResult ValidatePacket(byte[] data, int length)
+ {
+ var result = new FrameParseResult();
+
+ if (length < Overhead)
+ return result;
+
+ if (data[0] != StartByte1)
+ return result;
+ if (data[1] != StartByte2)
+ return result;
+
+ int msgLength = length - Overhead;
+ int crcDataLen = msgLength + 1;
+ var ck = FletcherChecksum(data, 2, 2 + crcDataLen);
+
+ if (ck.Item1 == data[length - 2] && ck.Item2 == data[length - 1])
+ {
+ result.Valid = true;
+ result.MsgId = data[HeaderSize - 1];
+ result.MsgSize = msgLength;
+ result.MsgData = new byte[msgLength];
+ Array.Copy(data, HeaderSize, result.MsgData, 0, msgLength);
+ }
+ return result;
+ }
+
+ ///
+ /// Calculate Fletcher-16 checksum
+ ///
+ private static (byte, byte) FletcherChecksum(byte[] buffer, int start, int end)
+ {
+ byte byte1 = 0;
+ byte byte2 = 0;
+
+ for (int i = start; i < end; i++)
+ {
+ byte1 = (byte)((byte1 + buffer[i]) % 256);
+ byte2 = (byte)((byte2 + byte1) % 256);
+ }
+
+ return (byte1, byte2);
+ }
+ }
+}
diff --git a/src/struct_frame/boilerplate/csharp/FrameParsers.cs b/src/struct_frame/boilerplate/csharp/FrameParsers.cs
new file mode 100644
index 00000000..67a086b0
--- /dev/null
+++ b/src/struct_frame/boilerplate/csharp/FrameParsers.cs
@@ -0,0 +1,34 @@
+// Automatically generated frame parsers for C#
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
+
+namespace StructFrame
+{
+ ///
+ /// Frame format type enumeration
+ ///
+ public enum FrameFormatType
+ {
+ TINY_MINIMAL = 0,
+ TINY_DEFAULT = 1,
+ TINY_EXTENDED_MSG_IDS = 2,
+ TINY_EXTENDED_LENGTH = 3,
+ TINY_EXTENDED = 4,
+ TINY_SYS_COMP = 5,
+ TINY_SEQ = 6,
+ TINY_MULTI_SYSTEM_STREAM = 7,
+ TINY_EXTENDED_MULTI_SYSTEM_STREAM = 8,
+ BASIC_MINIMAL = 9,
+ BASIC_DEFAULT = 10,
+ BASIC_EXTENDED_MSG_IDS = 11,
+ BASIC_EXTENDED_LENGTH = 12,
+ BASIC_EXTENDED = 13,
+ BASIC_SYS_COMP = 14,
+ BASIC_SEQ = 15,
+ BASIC_MULTI_SYSTEM_STREAM = 16,
+ BASIC_EXTENDED_MULTI_SYSTEM_STREAM = 17,
+ UBX_FRAME = 18,
+ MAVLINK_V1_FRAME = 19,
+ MAVLINK_V2_FRAME = 20,
+ FRAME_FORMAT_CONFIG = 21
+ }
+}
diff --git a/src/struct_frame/boilerplate/csharp/MavlinkV1Frame.cs b/src/struct_frame/boilerplate/csharp/MavlinkV1Frame.cs
new file mode 100644
index 00000000..9a7e7be2
--- /dev/null
+++ b/src/struct_frame/boilerplate/csharp/MavlinkV1Frame.cs
@@ -0,0 +1,173 @@
+// Automatically generated frame parser for C#
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
+
+using System;
+using System.Collections.Generic;
+
+namespace StructFrame
+{
+ ///
+ /// MavlinkV1Frame - Frame format parser and encoder
+ ///
+ public class MavlinkV1Frame : FrameFormatBase
+ {
+ public enum ParserState
+ {
+ LookingForStart = 0,
+ GettingMsgId = 1,
+ GettingLength = 2,
+ GettingPayload = 3
+ }
+
+ public const byte StartByte = 0xFE;
+ public const int HeaderSize = 6;
+ public const int FooterSize = 2;
+ public const int Overhead = 8;
+ public const int LengthBytes = 1;
+
+ private ParserState _state;
+ private List _buffer;
+ private int _packetSize;
+ private int _msgId;
+ private int _msgLength;
+ private Func _getMsgLength;
+
+ ///
+ /// Initialize the MavlinkV1Frame parser
+ ///
+ /// Callback function to get message length from msg_id
+ public MavlinkV1Frame(Func getMsgLength = null)
+ {
+ _getMsgLength = getMsgLength;
+ _buffer = new List();
+ Reset();
+ }
+
+ ///
+ /// Reset parser state
+ ///
+ public override void Reset()
+ {
+ _state = ParserState.LookingForStart;
+ _buffer.Clear();
+ _packetSize = 0;
+ _msgId = 0;
+ _msgLength = 0;
+ }
+
+ ///
+ /// Parse a single byte
+ ///
+ /// The byte to parse
+ /// FrameParseResult with Valid=true when a complete valid message is received
+ public override FrameParseResult ParseByte(byte b)
+ {
+ var result = new FrameParseResult();
+
+ switch (_state)
+ {
+ case ParserState.LookingForStart:
+ if (b == StartByte)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.GettingMsgId;
+ }
+ break;
+
+ case ParserState.GettingMsgId:
+ _buffer.Add(b);
+ _msgId = b;
+ _state = ParserState.GettingLength;
+ break;
+
+ case ParserState.GettingLength:
+ _buffer.Add(b);
+ _msgLength = b;
+ _packetSize = Overhead + _msgLength;
+ _state = ParserState.GettingPayload;
+ break;
+
+ case ParserState.GettingPayload:
+ _buffer.Add(b);
+ if (_buffer.Count >= _packetSize)
+ {
+ result = ValidatePacket(_buffer.ToArray(), _buffer.Count);
+ _state = ParserState.LookingForStart;
+ }
+ break;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Encode a message with MavlinkV1Frame format
+ ///
+ /// Message ID
+ /// Message data bytes
+ /// Encoded frame as byte array
+ public override byte[] Encode(int msgId, byte[] msgData)
+ {
+ var output = new List();
+ output.Add(StartByte);
+ output.Add((byte)(msgData.Length & 0xFF));
+ output.Add((byte)msgId);
+ output.AddRange(msgData);
+
+ // Calculate Fletcher checksum
+ var ck = FletcherChecksum(output.ToArray(), 1, output.Count);
+ output.Add(ck.Item1);
+ output.Add(ck.Item2);
+ return output.ToArray();
+ }
+
+ ///
+ /// Validate a complete MavlinkV1Frame packet in a buffer
+ ///
+ /// Buffer containing the complete packet
+ /// Length of the data in the buffer
+ /// FrameParseResult with Valid=true if packet is valid
+ public override FrameParseResult ValidatePacket(byte[] data, int length)
+ {
+ var result = new FrameParseResult();
+
+ if (length < Overhead)
+ return result;
+
+ if (data[0] != StartByte)
+ return result;
+
+ int msgLength = length - Overhead;
+ int crcDataLen = msgLength + 1 + LengthBytes;
+ var ck = FletcherChecksum(data, 1, 1 + crcDataLen);
+
+ if (ck.Item1 == data[length - 2] && ck.Item2 == data[length - 1])
+ {
+ result.Valid = true;
+ result.MsgId = data[HeaderSize - 1];
+ result.MsgSize = msgLength;
+ result.MsgData = new byte[msgLength];
+ Array.Copy(data, HeaderSize, result.MsgData, 0, msgLength);
+ }
+ return result;
+ }
+
+ ///
+ /// Calculate Fletcher-16 checksum
+ ///
+ private static (byte, byte) FletcherChecksum(byte[] buffer, int start, int end)
+ {
+ byte byte1 = 0;
+ byte byte2 = 0;
+
+ for (int i = start; i < end; i++)
+ {
+ byte1 = (byte)((byte1 + buffer[i]) % 256);
+ byte2 = (byte)((byte2 + byte1) % 256);
+ }
+
+ return (byte1, byte2);
+ }
+ }
+}
diff --git a/src/struct_frame/boilerplate/csharp/MavlinkV2Frame.cs b/src/struct_frame/boilerplate/csharp/MavlinkV2Frame.cs
new file mode 100644
index 00000000..18daa1bc
--- /dev/null
+++ b/src/struct_frame/boilerplate/csharp/MavlinkV2Frame.cs
@@ -0,0 +1,173 @@
+// Automatically generated frame parser for C#
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
+
+using System;
+using System.Collections.Generic;
+
+namespace StructFrame
+{
+ ///
+ /// MavlinkV2Frame - Frame format parser and encoder
+ ///
+ public class MavlinkV2Frame : FrameFormatBase
+ {
+ public enum ParserState
+ {
+ LookingForStart = 0,
+ GettingMsgId = 1,
+ GettingLength = 2,
+ GettingPayload = 3
+ }
+
+ public const byte StartByte = 0xFD;
+ public const int HeaderSize = 8;
+ public const int FooterSize = 2;
+ public const int Overhead = 10;
+ public const int LengthBytes = 1;
+
+ private ParserState _state;
+ private List _buffer;
+ private int _packetSize;
+ private int _msgId;
+ private int _msgLength;
+ private Func _getMsgLength;
+
+ ///
+ /// Initialize the MavlinkV2Frame parser
+ ///
+ /// Callback function to get message length from msg_id
+ public MavlinkV2Frame(Func getMsgLength = null)
+ {
+ _getMsgLength = getMsgLength;
+ _buffer = new List();
+ Reset();
+ }
+
+ ///
+ /// Reset parser state
+ ///
+ public override void Reset()
+ {
+ _state = ParserState.LookingForStart;
+ _buffer.Clear();
+ _packetSize = 0;
+ _msgId = 0;
+ _msgLength = 0;
+ }
+
+ ///
+ /// Parse a single byte
+ ///
+ /// The byte to parse
+ /// FrameParseResult with Valid=true when a complete valid message is received
+ public override FrameParseResult ParseByte(byte b)
+ {
+ var result = new FrameParseResult();
+
+ switch (_state)
+ {
+ case ParserState.LookingForStart:
+ if (b == StartByte)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.GettingMsgId;
+ }
+ break;
+
+ case ParserState.GettingMsgId:
+ _buffer.Add(b);
+ _msgId = b;
+ _state = ParserState.GettingLength;
+ break;
+
+ case ParserState.GettingLength:
+ _buffer.Add(b);
+ _msgLength = b;
+ _packetSize = Overhead + _msgLength;
+ _state = ParserState.GettingPayload;
+ break;
+
+ case ParserState.GettingPayload:
+ _buffer.Add(b);
+ if (_buffer.Count >= _packetSize)
+ {
+ result = ValidatePacket(_buffer.ToArray(), _buffer.Count);
+ _state = ParserState.LookingForStart;
+ }
+ break;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Encode a message with MavlinkV2Frame format
+ ///
+ /// Message ID
+ /// Message data bytes
+ /// Encoded frame as byte array
+ public override byte[] Encode(int msgId, byte[] msgData)
+ {
+ var output = new List();
+ output.Add(StartByte);
+ output.Add((byte)(msgData.Length & 0xFF));
+ output.Add((byte)msgId);
+ output.AddRange(msgData);
+
+ // Calculate Fletcher checksum
+ var ck = FletcherChecksum(output.ToArray(), 1, output.Count);
+ output.Add(ck.Item1);
+ output.Add(ck.Item2);
+ return output.ToArray();
+ }
+
+ ///
+ /// Validate a complete MavlinkV2Frame packet in a buffer
+ ///
+ /// Buffer containing the complete packet
+ /// Length of the data in the buffer
+ /// FrameParseResult with Valid=true if packet is valid
+ public override FrameParseResult ValidatePacket(byte[] data, int length)
+ {
+ var result = new FrameParseResult();
+
+ if (length < Overhead)
+ return result;
+
+ if (data[0] != StartByte)
+ return result;
+
+ int msgLength = length - Overhead;
+ int crcDataLen = msgLength + 1 + LengthBytes;
+ var ck = FletcherChecksum(data, 1, 1 + crcDataLen);
+
+ if (ck.Item1 == data[length - 2] && ck.Item2 == data[length - 1])
+ {
+ result.Valid = true;
+ result.MsgId = data[HeaderSize - 1];
+ result.MsgSize = msgLength;
+ result.MsgData = new byte[msgLength];
+ Array.Copy(data, HeaderSize, result.MsgData, 0, msgLength);
+ }
+ return result;
+ }
+
+ ///
+ /// Calculate Fletcher-16 checksum
+ ///
+ private static (byte, byte) FletcherChecksum(byte[] buffer, int start, int end)
+ {
+ byte byte1 = 0;
+ byte byte2 = 0;
+
+ for (int i = start; i < end; i++)
+ {
+ byte1 = (byte)((byte1 + buffer[i]) % 256);
+ byte2 = (byte)((byte2 + byte1) % 256);
+ }
+
+ return (byte1, byte2);
+ }
+ }
+}
diff --git a/src/struct_frame/boilerplate/csharp/TinyDefault.cs b/src/struct_frame/boilerplate/csharp/TinyDefault.cs
new file mode 100644
index 00000000..e4512786
--- /dev/null
+++ b/src/struct_frame/boilerplate/csharp/TinyDefault.cs
@@ -0,0 +1,173 @@
+// Automatically generated frame parser for C#
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
+
+using System;
+using System.Collections.Generic;
+
+namespace StructFrame
+{
+ ///
+ /// TinyDefault - Frame format parser and encoder
+ ///
+ public class TinyDefault : FrameFormatBase
+ {
+ public enum ParserState
+ {
+ LookingForStart = 0,
+ GettingMsgId = 1,
+ GettingLength = 2,
+ GettingPayload = 3
+ }
+
+ public const byte StartByte = 0x71;
+ public const int HeaderSize = 3;
+ public const int FooterSize = 2;
+ public const int Overhead = 5;
+ public const int LengthBytes = 1;
+
+ private ParserState _state;
+ private List _buffer;
+ private int _packetSize;
+ private int _msgId;
+ private int _msgLength;
+ private Func _getMsgLength;
+
+ ///
+ /// Initialize the TinyDefault parser
+ ///
+ /// Callback function to get message length from msg_id
+ public TinyDefault(Func getMsgLength = null)
+ {
+ _getMsgLength = getMsgLength;
+ _buffer = new List();
+ Reset();
+ }
+
+ ///
+ /// Reset parser state
+ ///
+ public override void Reset()
+ {
+ _state = ParserState.LookingForStart;
+ _buffer.Clear();
+ _packetSize = 0;
+ _msgId = 0;
+ _msgLength = 0;
+ }
+
+ ///
+ /// Parse a single byte
+ ///
+ /// The byte to parse
+ /// FrameParseResult with Valid=true when a complete valid message is received
+ public override FrameParseResult ParseByte(byte b)
+ {
+ var result = new FrameParseResult();
+
+ switch (_state)
+ {
+ case ParserState.LookingForStart:
+ if (b == StartByte)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.GettingMsgId;
+ }
+ break;
+
+ case ParserState.GettingMsgId:
+ _buffer.Add(b);
+ _msgId = b;
+ _state = ParserState.GettingLength;
+ break;
+
+ case ParserState.GettingLength:
+ _buffer.Add(b);
+ _msgLength = b;
+ _packetSize = Overhead + _msgLength;
+ _state = ParserState.GettingPayload;
+ break;
+
+ case ParserState.GettingPayload:
+ _buffer.Add(b);
+ if (_buffer.Count >= _packetSize)
+ {
+ result = ValidatePacket(_buffer.ToArray(), _buffer.Count);
+ _state = ParserState.LookingForStart;
+ }
+ break;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Encode a message with TinyDefault format
+ ///
+ /// Message ID
+ /// Message data bytes
+ /// Encoded frame as byte array
+ public override byte[] Encode(int msgId, byte[] msgData)
+ {
+ var output = new List();
+ output.Add(StartByte);
+ output.Add((byte)(msgData.Length & 0xFF));
+ output.Add((byte)msgId);
+ output.AddRange(msgData);
+
+ // Calculate Fletcher checksum
+ var ck = FletcherChecksum(output.ToArray(), 1, output.Count);
+ output.Add(ck.Item1);
+ output.Add(ck.Item2);
+ return output.ToArray();
+ }
+
+ ///
+ /// Validate a complete TinyDefault packet in a buffer
+ ///
+ /// Buffer containing the complete packet
+ /// Length of the data in the buffer
+ /// FrameParseResult with Valid=true if packet is valid
+ public override FrameParseResult ValidatePacket(byte[] data, int length)
+ {
+ var result = new FrameParseResult();
+
+ if (length < Overhead)
+ return result;
+
+ if (data[0] != StartByte)
+ return result;
+
+ int msgLength = length - Overhead;
+ int crcDataLen = msgLength + 1 + LengthBytes;
+ var ck = FletcherChecksum(data, 1, 1 + crcDataLen);
+
+ if (ck.Item1 == data[length - 2] && ck.Item2 == data[length - 1])
+ {
+ result.Valid = true;
+ result.MsgId = data[HeaderSize - 1];
+ result.MsgSize = msgLength;
+ result.MsgData = new byte[msgLength];
+ Array.Copy(data, HeaderSize, result.MsgData, 0, msgLength);
+ }
+ return result;
+ }
+
+ ///
+ /// Calculate Fletcher-16 checksum
+ ///
+ private static (byte, byte) FletcherChecksum(byte[] buffer, int start, int end)
+ {
+ byte byte1 = 0;
+ byte byte2 = 0;
+
+ for (int i = start; i < end; i++)
+ {
+ byte1 = (byte)((byte1 + buffer[i]) % 256);
+ byte2 = (byte)((byte2 + byte1) % 256);
+ }
+
+ return (byte1, byte2);
+ }
+ }
+}
diff --git a/src/struct_frame/boilerplate/csharp/TinyExtended.cs b/src/struct_frame/boilerplate/csharp/TinyExtended.cs
new file mode 100644
index 00000000..8feab0ba
--- /dev/null
+++ b/src/struct_frame/boilerplate/csharp/TinyExtended.cs
@@ -0,0 +1,183 @@
+// Automatically generated frame parser for C#
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
+
+using System;
+using System.Collections.Generic;
+
+namespace StructFrame
+{
+ ///
+ /// TinyExtended - Frame format parser and encoder
+ ///
+ public class TinyExtended : FrameFormatBase
+ {
+ public enum ParserState
+ {
+ LookingForStart = 0,
+ GettingMsgId = 1,
+ GettingLength = 2,
+ GettingPayload = 3
+ }
+
+ public const byte StartByte = 0x74;
+ public const int HeaderSize = 5;
+ public const int FooterSize = 2;
+ public const int Overhead = 7;
+ public const int LengthBytes = 2;
+
+ private ParserState _state;
+ private List _buffer;
+ private int _packetSize;
+ private int _msgId;
+ private int _msgLength;
+ private int _lengthLo;
+ private Func _getMsgLength;
+
+ ///
+ /// Initialize the TinyExtended parser
+ ///
+ /// Callback function to get message length from msg_id
+ public TinyExtended(Func getMsgLength = null)
+ {
+ _getMsgLength = getMsgLength;
+ _buffer = new List();
+ Reset();
+ }
+
+ ///
+ /// Reset parser state
+ ///
+ public override void Reset()
+ {
+ _state = ParserState.LookingForStart;
+ _buffer.Clear();
+ _packetSize = 0;
+ _msgId = 0;
+ _msgLength = 0;
+ _lengthLo = 0;
+ }
+
+ ///
+ /// Parse a single byte
+ ///
+ /// The byte to parse
+ /// FrameParseResult with Valid=true when a complete valid message is received
+ public override FrameParseResult ParseByte(byte b)
+ {
+ var result = new FrameParseResult();
+
+ switch (_state)
+ {
+ case ParserState.LookingForStart:
+ if (b == StartByte)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.GettingMsgId;
+ }
+ break;
+
+ case ParserState.GettingMsgId:
+ _buffer.Add(b);
+ _msgId = b;
+ _state = ParserState.GettingLength;
+ break;
+
+ case ParserState.GettingLength:
+ _buffer.Add(b);
+ if (_buffer.Count == 3)
+ {
+ _lengthLo = b;
+ }
+ else
+ {
+ _msgLength = _lengthLo | (b << 8);
+ _packetSize = Overhead + _msgLength;
+ _state = ParserState.GettingPayload;
+ }
+ break;
+
+ case ParserState.GettingPayload:
+ _buffer.Add(b);
+ if (_buffer.Count >= _packetSize)
+ {
+ result = ValidatePacket(_buffer.ToArray(), _buffer.Count);
+ _state = ParserState.LookingForStart;
+ }
+ break;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Encode a message with TinyExtended format
+ ///
+ /// Message ID
+ /// Message data bytes
+ /// Encoded frame as byte array
+ public override byte[] Encode(int msgId, byte[] msgData)
+ {
+ var output = new List();
+ output.Add(StartByte);
+ output.Add((byte)(msgData.Length & 0xFF));
+ output.Add((byte)((msgData.Length >> 8) & 0xFF));
+ output.Add((byte)msgId);
+ output.AddRange(msgData);
+
+ // Calculate Fletcher checksum
+ var ck = FletcherChecksum(output.ToArray(), 1, output.Count);
+ output.Add(ck.Item1);
+ output.Add(ck.Item2);
+ return output.ToArray();
+ }
+
+ ///
+ /// Validate a complete TinyExtended packet in a buffer
+ ///
+ /// Buffer containing the complete packet
+ /// Length of the data in the buffer
+ /// FrameParseResult with Valid=true if packet is valid
+ public override FrameParseResult ValidatePacket(byte[] data, int length)
+ {
+ var result = new FrameParseResult();
+
+ if (length < Overhead)
+ return result;
+
+ if (data[0] != StartByte)
+ return result;
+
+ int msgLength = length - Overhead;
+ int crcDataLen = msgLength + 1 + LengthBytes;
+ var ck = FletcherChecksum(data, 1, 1 + crcDataLen);
+
+ if (ck.Item1 == data[length - 2] && ck.Item2 == data[length - 1])
+ {
+ result.Valid = true;
+ result.MsgId = data[HeaderSize - 1];
+ result.MsgSize = msgLength;
+ result.MsgData = new byte[msgLength];
+ Array.Copy(data, HeaderSize, result.MsgData, 0, msgLength);
+ }
+ return result;
+ }
+
+ ///
+ /// Calculate Fletcher-16 checksum
+ ///
+ private static (byte, byte) FletcherChecksum(byte[] buffer, int start, int end)
+ {
+ byte byte1 = 0;
+ byte byte2 = 0;
+
+ for (int i = start; i < end; i++)
+ {
+ byte1 = (byte)((byte1 + buffer[i]) % 256);
+ byte2 = (byte)((byte2 + byte1) % 256);
+ }
+
+ return (byte1, byte2);
+ }
+ }
+}
diff --git a/src/struct_frame/boilerplate/csharp/TinyExtendedLength.cs b/src/struct_frame/boilerplate/csharp/TinyExtendedLength.cs
new file mode 100644
index 00000000..5af47301
--- /dev/null
+++ b/src/struct_frame/boilerplate/csharp/TinyExtendedLength.cs
@@ -0,0 +1,183 @@
+// Automatically generated frame parser for C#
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
+
+using System;
+using System.Collections.Generic;
+
+namespace StructFrame
+{
+ ///
+ /// TinyExtendedLength - Frame format parser and encoder
+ ///
+ public class TinyExtendedLength : FrameFormatBase
+ {
+ public enum ParserState
+ {
+ LookingForStart = 0,
+ GettingMsgId = 1,
+ GettingLength = 2,
+ GettingPayload = 3
+ }
+
+ public const byte StartByte = 0x73;
+ public const int HeaderSize = 4;
+ public const int FooterSize = 2;
+ public const int Overhead = 6;
+ public const int LengthBytes = 2;
+
+ private ParserState _state;
+ private List _buffer;
+ private int _packetSize;
+ private int _msgId;
+ private int _msgLength;
+ private int _lengthLo;
+ private Func _getMsgLength;
+
+ ///
+ /// Initialize the TinyExtendedLength parser
+ ///
+ /// Callback function to get message length from msg_id
+ public TinyExtendedLength(Func getMsgLength = null)
+ {
+ _getMsgLength = getMsgLength;
+ _buffer = new List();
+ Reset();
+ }
+
+ ///
+ /// Reset parser state
+ ///
+ public override void Reset()
+ {
+ _state = ParserState.LookingForStart;
+ _buffer.Clear();
+ _packetSize = 0;
+ _msgId = 0;
+ _msgLength = 0;
+ _lengthLo = 0;
+ }
+
+ ///
+ /// Parse a single byte
+ ///
+ /// The byte to parse
+ /// FrameParseResult with Valid=true when a complete valid message is received
+ public override FrameParseResult ParseByte(byte b)
+ {
+ var result = new FrameParseResult();
+
+ switch (_state)
+ {
+ case ParserState.LookingForStart:
+ if (b == StartByte)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.GettingMsgId;
+ }
+ break;
+
+ case ParserState.GettingMsgId:
+ _buffer.Add(b);
+ _msgId = b;
+ _state = ParserState.GettingLength;
+ break;
+
+ case ParserState.GettingLength:
+ _buffer.Add(b);
+ if (_buffer.Count == 3)
+ {
+ _lengthLo = b;
+ }
+ else
+ {
+ _msgLength = _lengthLo | (b << 8);
+ _packetSize = Overhead + _msgLength;
+ _state = ParserState.GettingPayload;
+ }
+ break;
+
+ case ParserState.GettingPayload:
+ _buffer.Add(b);
+ if (_buffer.Count >= _packetSize)
+ {
+ result = ValidatePacket(_buffer.ToArray(), _buffer.Count);
+ _state = ParserState.LookingForStart;
+ }
+ break;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Encode a message with TinyExtendedLength format
+ ///
+ /// Message ID
+ /// Message data bytes
+ /// Encoded frame as byte array
+ public override byte[] Encode(int msgId, byte[] msgData)
+ {
+ var output = new List();
+ output.Add(StartByte);
+ output.Add((byte)(msgData.Length & 0xFF));
+ output.Add((byte)((msgData.Length >> 8) & 0xFF));
+ output.Add((byte)msgId);
+ output.AddRange(msgData);
+
+ // Calculate Fletcher checksum
+ var ck = FletcherChecksum(output.ToArray(), 1, output.Count);
+ output.Add(ck.Item1);
+ output.Add(ck.Item2);
+ return output.ToArray();
+ }
+
+ ///
+ /// Validate a complete TinyExtendedLength packet in a buffer
+ ///
+ /// Buffer containing the complete packet
+ /// Length of the data in the buffer
+ /// FrameParseResult with Valid=true if packet is valid
+ public override FrameParseResult ValidatePacket(byte[] data, int length)
+ {
+ var result = new FrameParseResult();
+
+ if (length < Overhead)
+ return result;
+
+ if (data[0] != StartByte)
+ return result;
+
+ int msgLength = length - Overhead;
+ int crcDataLen = msgLength + 1 + LengthBytes;
+ var ck = FletcherChecksum(data, 1, 1 + crcDataLen);
+
+ if (ck.Item1 == data[length - 2] && ck.Item2 == data[length - 1])
+ {
+ result.Valid = true;
+ result.MsgId = data[HeaderSize - 1];
+ result.MsgSize = msgLength;
+ result.MsgData = new byte[msgLength];
+ Array.Copy(data, HeaderSize, result.MsgData, 0, msgLength);
+ }
+ return result;
+ }
+
+ ///
+ /// Calculate Fletcher-16 checksum
+ ///
+ private static (byte, byte) FletcherChecksum(byte[] buffer, int start, int end)
+ {
+ byte byte1 = 0;
+ byte byte2 = 0;
+
+ for (int i = start; i < end; i++)
+ {
+ byte1 = (byte)((byte1 + buffer[i]) % 256);
+ byte2 = (byte)((byte2 + byte1) % 256);
+ }
+
+ return (byte1, byte2);
+ }
+ }
+}
diff --git a/src/struct_frame/boilerplate/csharp/TinyExtendedMsgIds.cs b/src/struct_frame/boilerplate/csharp/TinyExtendedMsgIds.cs
new file mode 100644
index 00000000..5f2d8ac4
--- /dev/null
+++ b/src/struct_frame/boilerplate/csharp/TinyExtendedMsgIds.cs
@@ -0,0 +1,173 @@
+// Automatically generated frame parser for C#
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
+
+using System;
+using System.Collections.Generic;
+
+namespace StructFrame
+{
+ ///
+ /// TinyExtendedMsgIds - Frame format parser and encoder
+ ///
+ public class TinyExtendedMsgIds : FrameFormatBase
+ {
+ public enum ParserState
+ {
+ LookingForStart = 0,
+ GettingMsgId = 1,
+ GettingLength = 2,
+ GettingPayload = 3
+ }
+
+ public const byte StartByte = 0x72;
+ public const int HeaderSize = 4;
+ public const int FooterSize = 2;
+ public const int Overhead = 6;
+ public const int LengthBytes = 1;
+
+ private ParserState _state;
+ private List _buffer;
+ private int _packetSize;
+ private int _msgId;
+ private int _msgLength;
+ private Func _getMsgLength;
+
+ ///
+ /// Initialize the TinyExtendedMsgIds parser
+ ///
+ /// Callback function to get message length from msg_id
+ public TinyExtendedMsgIds(Func getMsgLength = null)
+ {
+ _getMsgLength = getMsgLength;
+ _buffer = new List();
+ Reset();
+ }
+
+ ///
+ /// Reset parser state
+ ///
+ public override void Reset()
+ {
+ _state = ParserState.LookingForStart;
+ _buffer.Clear();
+ _packetSize = 0;
+ _msgId = 0;
+ _msgLength = 0;
+ }
+
+ ///
+ /// Parse a single byte
+ ///
+ /// The byte to parse
+ /// FrameParseResult with Valid=true when a complete valid message is received
+ public override FrameParseResult ParseByte(byte b)
+ {
+ var result = new FrameParseResult();
+
+ switch (_state)
+ {
+ case ParserState.LookingForStart:
+ if (b == StartByte)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.GettingMsgId;
+ }
+ break;
+
+ case ParserState.GettingMsgId:
+ _buffer.Add(b);
+ _msgId = b;
+ _state = ParserState.GettingLength;
+ break;
+
+ case ParserState.GettingLength:
+ _buffer.Add(b);
+ _msgLength = b;
+ _packetSize = Overhead + _msgLength;
+ _state = ParserState.GettingPayload;
+ break;
+
+ case ParserState.GettingPayload:
+ _buffer.Add(b);
+ if (_buffer.Count >= _packetSize)
+ {
+ result = ValidatePacket(_buffer.ToArray(), _buffer.Count);
+ _state = ParserState.LookingForStart;
+ }
+ break;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Encode a message with TinyExtendedMsgIds format
+ ///
+ /// Message ID
+ /// Message data bytes
+ /// Encoded frame as byte array
+ public override byte[] Encode(int msgId, byte[] msgData)
+ {
+ var output = new List();
+ output.Add(StartByte);
+ output.Add((byte)(msgData.Length & 0xFF));
+ output.Add((byte)msgId);
+ output.AddRange(msgData);
+
+ // Calculate Fletcher checksum
+ var ck = FletcherChecksum(output.ToArray(), 1, output.Count);
+ output.Add(ck.Item1);
+ output.Add(ck.Item2);
+ return output.ToArray();
+ }
+
+ ///
+ /// Validate a complete TinyExtendedMsgIds packet in a buffer
+ ///
+ /// Buffer containing the complete packet
+ /// Length of the data in the buffer
+ /// FrameParseResult with Valid=true if packet is valid
+ public override FrameParseResult ValidatePacket(byte[] data, int length)
+ {
+ var result = new FrameParseResult();
+
+ if (length < Overhead)
+ return result;
+
+ if (data[0] != StartByte)
+ return result;
+
+ int msgLength = length - Overhead;
+ int crcDataLen = msgLength + 1 + LengthBytes;
+ var ck = FletcherChecksum(data, 1, 1 + crcDataLen);
+
+ if (ck.Item1 == data[length - 2] && ck.Item2 == data[length - 1])
+ {
+ result.Valid = true;
+ result.MsgId = data[HeaderSize - 1];
+ result.MsgSize = msgLength;
+ result.MsgData = new byte[msgLength];
+ Array.Copy(data, HeaderSize, result.MsgData, 0, msgLength);
+ }
+ return result;
+ }
+
+ ///
+ /// Calculate Fletcher-16 checksum
+ ///
+ private static (byte, byte) FletcherChecksum(byte[] buffer, int start, int end)
+ {
+ byte byte1 = 0;
+ byte byte2 = 0;
+
+ for (int i = start; i < end; i++)
+ {
+ byte1 = (byte)((byte1 + buffer[i]) % 256);
+ byte2 = (byte)((byte2 + byte1) % 256);
+ }
+
+ return (byte1, byte2);
+ }
+ }
+}
diff --git a/src/struct_frame/boilerplate/csharp/TinyExtendedMultiSystemStream.cs b/src/struct_frame/boilerplate/csharp/TinyExtendedMultiSystemStream.cs
new file mode 100644
index 00000000..5be2c5a6
--- /dev/null
+++ b/src/struct_frame/boilerplate/csharp/TinyExtendedMultiSystemStream.cs
@@ -0,0 +1,183 @@
+// Automatically generated frame parser for C#
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
+
+using System;
+using System.Collections.Generic;
+
+namespace StructFrame
+{
+ ///
+ /// TinyExtendedMultiSystemStream - Frame format parser and encoder
+ ///
+ public class TinyExtendedMultiSystemStream : FrameFormatBase
+ {
+ public enum ParserState
+ {
+ LookingForStart = 0,
+ GettingMsgId = 1,
+ GettingLength = 2,
+ GettingPayload = 3
+ }
+
+ public const byte StartByte = 0x78;
+ public const int HeaderSize = 8;
+ public const int FooterSize = 2;
+ public const int Overhead = 10;
+ public const int LengthBytes = 2;
+
+ private ParserState _state;
+ private List _buffer;
+ private int _packetSize;
+ private int _msgId;
+ private int _msgLength;
+ private int _lengthLo;
+ private Func _getMsgLength;
+
+ ///
+ /// Initialize the TinyExtendedMultiSystemStream parser
+ ///
+ /// Callback function to get message length from msg_id
+ public TinyExtendedMultiSystemStream(Func getMsgLength = null)
+ {
+ _getMsgLength = getMsgLength;
+ _buffer = new List();
+ Reset();
+ }
+
+ ///
+ /// Reset parser state
+ ///
+ public override void Reset()
+ {
+ _state = ParserState.LookingForStart;
+ _buffer.Clear();
+ _packetSize = 0;
+ _msgId = 0;
+ _msgLength = 0;
+ _lengthLo = 0;
+ }
+
+ ///
+ /// Parse a single byte
+ ///
+ /// The byte to parse
+ /// FrameParseResult with Valid=true when a complete valid message is received
+ public override FrameParseResult ParseByte(byte b)
+ {
+ var result = new FrameParseResult();
+
+ switch (_state)
+ {
+ case ParserState.LookingForStart:
+ if (b == StartByte)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.GettingMsgId;
+ }
+ break;
+
+ case ParserState.GettingMsgId:
+ _buffer.Add(b);
+ _msgId = b;
+ _state = ParserState.GettingLength;
+ break;
+
+ case ParserState.GettingLength:
+ _buffer.Add(b);
+ if (_buffer.Count == 3)
+ {
+ _lengthLo = b;
+ }
+ else
+ {
+ _msgLength = _lengthLo | (b << 8);
+ _packetSize = Overhead + _msgLength;
+ _state = ParserState.GettingPayload;
+ }
+ break;
+
+ case ParserState.GettingPayload:
+ _buffer.Add(b);
+ if (_buffer.Count >= _packetSize)
+ {
+ result = ValidatePacket(_buffer.ToArray(), _buffer.Count);
+ _state = ParserState.LookingForStart;
+ }
+ break;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Encode a message with TinyExtendedMultiSystemStream format
+ ///
+ /// Message ID
+ /// Message data bytes
+ /// Encoded frame as byte array
+ public override byte[] Encode(int msgId, byte[] msgData)
+ {
+ var output = new List();
+ output.Add(StartByte);
+ output.Add((byte)(msgData.Length & 0xFF));
+ output.Add((byte)((msgData.Length >> 8) & 0xFF));
+ output.Add((byte)msgId);
+ output.AddRange(msgData);
+
+ // Calculate Fletcher checksum
+ var ck = FletcherChecksum(output.ToArray(), 1, output.Count);
+ output.Add(ck.Item1);
+ output.Add(ck.Item2);
+ return output.ToArray();
+ }
+
+ ///
+ /// Validate a complete TinyExtendedMultiSystemStream packet in a buffer
+ ///
+ /// Buffer containing the complete packet
+ /// Length of the data in the buffer
+ /// FrameParseResult with Valid=true if packet is valid
+ public override FrameParseResult ValidatePacket(byte[] data, int length)
+ {
+ var result = new FrameParseResult();
+
+ if (length < Overhead)
+ return result;
+
+ if (data[0] != StartByte)
+ return result;
+
+ int msgLength = length - Overhead;
+ int crcDataLen = msgLength + 1 + LengthBytes;
+ var ck = FletcherChecksum(data, 1, 1 + crcDataLen);
+
+ if (ck.Item1 == data[length - 2] && ck.Item2 == data[length - 1])
+ {
+ result.Valid = true;
+ result.MsgId = data[HeaderSize - 1];
+ result.MsgSize = msgLength;
+ result.MsgData = new byte[msgLength];
+ Array.Copy(data, HeaderSize, result.MsgData, 0, msgLength);
+ }
+ return result;
+ }
+
+ ///
+ /// Calculate Fletcher-16 checksum
+ ///
+ private static (byte, byte) FletcherChecksum(byte[] buffer, int start, int end)
+ {
+ byte byte1 = 0;
+ byte byte2 = 0;
+
+ for (int i = start; i < end; i++)
+ {
+ byte1 = (byte)((byte1 + buffer[i]) % 256);
+ byte2 = (byte)((byte2 + byte1) % 256);
+ }
+
+ return (byte1, byte2);
+ }
+ }
+}
diff --git a/src/struct_frame/boilerplate/csharp/TinyMinimal.cs b/src/struct_frame/boilerplate/csharp/TinyMinimal.cs
new file mode 100644
index 00000000..167185e4
--- /dev/null
+++ b/src/struct_frame/boilerplate/csharp/TinyMinimal.cs
@@ -0,0 +1,165 @@
+// Automatically generated frame parser for C#
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
+
+using System;
+using System.Collections.Generic;
+
+namespace StructFrame
+{
+ ///
+ /// TinyMinimal - Frame format parser and encoder
+ ///
+ public class TinyMinimal : FrameFormatBase
+ {
+ public enum ParserState
+ {
+ LookingForStart = 0,
+ GettingMsgId = 1,
+ GettingPayload = 2
+ }
+
+ public const byte StartByte = 0x70;
+ public const int HeaderSize = 2;
+ public const int FooterSize = 0;
+ public const int Overhead = 2;
+
+ private ParserState _state;
+ private List _buffer;
+ private int _packetSize;
+ private int _msgId;
+ private Func _getMsgLength;
+
+ ///
+ /// Initialize the TinyMinimal parser
+ ///
+ /// Callback function to get message length from msg_id
+ public TinyMinimal(Func getMsgLength = null)
+ {
+ _getMsgLength = getMsgLength;
+ _buffer = new List();
+ Reset();
+ }
+
+ ///
+ /// Reset parser state
+ ///
+ public override void Reset()
+ {
+ _state = ParserState.LookingForStart;
+ _buffer.Clear();
+ _packetSize = 0;
+ _msgId = 0;
+ }
+
+ ///
+ /// Parse a single byte
+ ///
+ /// The byte to parse
+ /// FrameParseResult with Valid=true when a complete valid message is received
+ public override FrameParseResult ParseByte(byte b)
+ {
+ var result = new FrameParseResult();
+
+ switch (_state)
+ {
+ case ParserState.LookingForStart:
+ if (b == StartByte)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.GettingMsgId;
+ }
+ break;
+
+ case ParserState.GettingMsgId:
+ _buffer.Add(b);
+ _msgId = b;
+ if (_getMsgLength != null)
+ {
+ int? msgLength = _getMsgLength(b);
+ if (msgLength.HasValue)
+ {
+ _packetSize = Overhead + msgLength.Value;
+ _state = ParserState.GettingPayload;
+ }
+ else
+ {
+ _state = ParserState.LookingForStart;
+ }
+ }
+ else
+ {
+ _state = ParserState.LookingForStart;
+ }
+ break;
+
+ case ParserState.GettingPayload:
+ _buffer.Add(b);
+ if (_buffer.Count >= _packetSize)
+ {
+ result = ValidatePacket(_buffer.ToArray(), _buffer.Count);
+ _state = ParserState.LookingForStart;
+ }
+ break;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Encode a message with TinyMinimal format
+ ///
+ /// Message ID
+ /// Message data bytes
+ /// Encoded frame as byte array
+ public override byte[] Encode(int msgId, byte[] msgData)
+ {
+ var output = new List();
+ output.Add(StartByte);
+ output.Add((byte)msgId);
+ output.AddRange(msgData);
+ return output.ToArray();
+ }
+
+ ///
+ /// Validate a complete TinyMinimal packet in a buffer
+ ///
+ /// Buffer containing the complete packet
+ /// Length of the data in the buffer
+ /// FrameParseResult with Valid=true if packet is valid
+ public override FrameParseResult ValidatePacket(byte[] data, int length)
+ {
+ var result = new FrameParseResult();
+
+ if (length < Overhead)
+ return result;
+
+ if (data[0] != StartByte)
+ return result;
+
+ result.Valid = true;
+ result.MsgId = data[HeaderSize - 1];
+ result.MsgSize = length - HeaderSize;
+ result.MsgData = new byte[result.MsgSize];
+ Array.Copy(data, HeaderSize, result.MsgData, 0, result.MsgSize);
+ return result;
+ }
+
+ ///
+ /// Calculate Fletcher-16 checksum
+ ///
+ private static (byte, byte) FletcherChecksum(byte[] buffer, int start, int end)
+ {
+ byte byte1 = 0;
+ byte byte2 = 0;
+
+ for (int i = start; i < end; i++)
+ {
+ byte1 = (byte)((byte1 + buffer[i]) % 256);
+ byte2 = (byte)((byte2 + byte1) % 256);
+ }
+
+ return (byte1, byte2);
+ }
+ }
+}
diff --git a/src/struct_frame/boilerplate/csharp/TinyMultiSystemStream.cs b/src/struct_frame/boilerplate/csharp/TinyMultiSystemStream.cs
new file mode 100644
index 00000000..bb9056d6
--- /dev/null
+++ b/src/struct_frame/boilerplate/csharp/TinyMultiSystemStream.cs
@@ -0,0 +1,173 @@
+// Automatically generated frame parser for C#
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
+
+using System;
+using System.Collections.Generic;
+
+namespace StructFrame
+{
+ ///
+ /// TinyMultiSystemStream - Frame format parser and encoder
+ ///
+ public class TinyMultiSystemStream : FrameFormatBase
+ {
+ public enum ParserState
+ {
+ LookingForStart = 0,
+ GettingMsgId = 1,
+ GettingLength = 2,
+ GettingPayload = 3
+ }
+
+ public const byte StartByte = 0x77;
+ public const int HeaderSize = 6;
+ public const int FooterSize = 2;
+ public const int Overhead = 8;
+ public const int LengthBytes = 1;
+
+ private ParserState _state;
+ private List _buffer;
+ private int _packetSize;
+ private int _msgId;
+ private int _msgLength;
+ private Func _getMsgLength;
+
+ ///
+ /// Initialize the TinyMultiSystemStream parser
+ ///
+ /// Callback function to get message length from msg_id
+ public TinyMultiSystemStream(Func getMsgLength = null)
+ {
+ _getMsgLength = getMsgLength;
+ _buffer = new List();
+ Reset();
+ }
+
+ ///
+ /// Reset parser state
+ ///
+ public override void Reset()
+ {
+ _state = ParserState.LookingForStart;
+ _buffer.Clear();
+ _packetSize = 0;
+ _msgId = 0;
+ _msgLength = 0;
+ }
+
+ ///
+ /// Parse a single byte
+ ///
+ /// The byte to parse
+ /// FrameParseResult with Valid=true when a complete valid message is received
+ public override FrameParseResult ParseByte(byte b)
+ {
+ var result = new FrameParseResult();
+
+ switch (_state)
+ {
+ case ParserState.LookingForStart:
+ if (b == StartByte)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.GettingMsgId;
+ }
+ break;
+
+ case ParserState.GettingMsgId:
+ _buffer.Add(b);
+ _msgId = b;
+ _state = ParserState.GettingLength;
+ break;
+
+ case ParserState.GettingLength:
+ _buffer.Add(b);
+ _msgLength = b;
+ _packetSize = Overhead + _msgLength;
+ _state = ParserState.GettingPayload;
+ break;
+
+ case ParserState.GettingPayload:
+ _buffer.Add(b);
+ if (_buffer.Count >= _packetSize)
+ {
+ result = ValidatePacket(_buffer.ToArray(), _buffer.Count);
+ _state = ParserState.LookingForStart;
+ }
+ break;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Encode a message with TinyMultiSystemStream format
+ ///
+ /// Message ID
+ /// Message data bytes
+ /// Encoded frame as byte array
+ public override byte[] Encode(int msgId, byte[] msgData)
+ {
+ var output = new List();
+ output.Add(StartByte);
+ output.Add((byte)(msgData.Length & 0xFF));
+ output.Add((byte)msgId);
+ output.AddRange(msgData);
+
+ // Calculate Fletcher checksum
+ var ck = FletcherChecksum(output.ToArray(), 1, output.Count);
+ output.Add(ck.Item1);
+ output.Add(ck.Item2);
+ return output.ToArray();
+ }
+
+ ///
+ /// Validate a complete TinyMultiSystemStream packet in a buffer
+ ///
+ /// Buffer containing the complete packet
+ /// Length of the data in the buffer
+ /// FrameParseResult with Valid=true if packet is valid
+ public override FrameParseResult ValidatePacket(byte[] data, int length)
+ {
+ var result = new FrameParseResult();
+
+ if (length < Overhead)
+ return result;
+
+ if (data[0] != StartByte)
+ return result;
+
+ int msgLength = length - Overhead;
+ int crcDataLen = msgLength + 1 + LengthBytes;
+ var ck = FletcherChecksum(data, 1, 1 + crcDataLen);
+
+ if (ck.Item1 == data[length - 2] && ck.Item2 == data[length - 1])
+ {
+ result.Valid = true;
+ result.MsgId = data[HeaderSize - 1];
+ result.MsgSize = msgLength;
+ result.MsgData = new byte[msgLength];
+ Array.Copy(data, HeaderSize, result.MsgData, 0, msgLength);
+ }
+ return result;
+ }
+
+ ///
+ /// Calculate Fletcher-16 checksum
+ ///
+ private static (byte, byte) FletcherChecksum(byte[] buffer, int start, int end)
+ {
+ byte byte1 = 0;
+ byte byte2 = 0;
+
+ for (int i = start; i < end; i++)
+ {
+ byte1 = (byte)((byte1 + buffer[i]) % 256);
+ byte2 = (byte)((byte2 + byte1) % 256);
+ }
+
+ return (byte1, byte2);
+ }
+ }
+}
diff --git a/src/struct_frame/boilerplate/csharp/TinySeq.cs b/src/struct_frame/boilerplate/csharp/TinySeq.cs
new file mode 100644
index 00000000..6df13902
--- /dev/null
+++ b/src/struct_frame/boilerplate/csharp/TinySeq.cs
@@ -0,0 +1,173 @@
+// Automatically generated frame parser for C#
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
+
+using System;
+using System.Collections.Generic;
+
+namespace StructFrame
+{
+ ///
+ /// TinySeq - Frame format parser and encoder
+ ///
+ public class TinySeq : FrameFormatBase
+ {
+ public enum ParserState
+ {
+ LookingForStart = 0,
+ GettingMsgId = 1,
+ GettingLength = 2,
+ GettingPayload = 3
+ }
+
+ public const byte StartByte = 0x76;
+ public const int HeaderSize = 4;
+ public const int FooterSize = 2;
+ public const int Overhead = 6;
+ public const int LengthBytes = 1;
+
+ private ParserState _state;
+ private List _buffer;
+ private int _packetSize;
+ private int _msgId;
+ private int _msgLength;
+ private Func _getMsgLength;
+
+ ///
+ /// Initialize the TinySeq parser
+ ///
+ /// Callback function to get message length from msg_id
+ public TinySeq(Func getMsgLength = null)
+ {
+ _getMsgLength = getMsgLength;
+ _buffer = new List();
+ Reset();
+ }
+
+ ///
+ /// Reset parser state
+ ///
+ public override void Reset()
+ {
+ _state = ParserState.LookingForStart;
+ _buffer.Clear();
+ _packetSize = 0;
+ _msgId = 0;
+ _msgLength = 0;
+ }
+
+ ///
+ /// Parse a single byte
+ ///
+ /// The byte to parse
+ /// FrameParseResult with Valid=true when a complete valid message is received
+ public override FrameParseResult ParseByte(byte b)
+ {
+ var result = new FrameParseResult();
+
+ switch (_state)
+ {
+ case ParserState.LookingForStart:
+ if (b == StartByte)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.GettingMsgId;
+ }
+ break;
+
+ case ParserState.GettingMsgId:
+ _buffer.Add(b);
+ _msgId = b;
+ _state = ParserState.GettingLength;
+ break;
+
+ case ParserState.GettingLength:
+ _buffer.Add(b);
+ _msgLength = b;
+ _packetSize = Overhead + _msgLength;
+ _state = ParserState.GettingPayload;
+ break;
+
+ case ParserState.GettingPayload:
+ _buffer.Add(b);
+ if (_buffer.Count >= _packetSize)
+ {
+ result = ValidatePacket(_buffer.ToArray(), _buffer.Count);
+ _state = ParserState.LookingForStart;
+ }
+ break;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Encode a message with TinySeq format
+ ///
+ /// Message ID
+ /// Message data bytes
+ /// Encoded frame as byte array
+ public override byte[] Encode(int msgId, byte[] msgData)
+ {
+ var output = new List();
+ output.Add(StartByte);
+ output.Add((byte)(msgData.Length & 0xFF));
+ output.Add((byte)msgId);
+ output.AddRange(msgData);
+
+ // Calculate Fletcher checksum
+ var ck = FletcherChecksum(output.ToArray(), 1, output.Count);
+ output.Add(ck.Item1);
+ output.Add(ck.Item2);
+ return output.ToArray();
+ }
+
+ ///
+ /// Validate a complete TinySeq packet in a buffer
+ ///
+ /// Buffer containing the complete packet
+ /// Length of the data in the buffer
+ /// FrameParseResult with Valid=true if packet is valid
+ public override FrameParseResult ValidatePacket(byte[] data, int length)
+ {
+ var result = new FrameParseResult();
+
+ if (length < Overhead)
+ return result;
+
+ if (data[0] != StartByte)
+ return result;
+
+ int msgLength = length - Overhead;
+ int crcDataLen = msgLength + 1 + LengthBytes;
+ var ck = FletcherChecksum(data, 1, 1 + crcDataLen);
+
+ if (ck.Item1 == data[length - 2] && ck.Item2 == data[length - 1])
+ {
+ result.Valid = true;
+ result.MsgId = data[HeaderSize - 1];
+ result.MsgSize = msgLength;
+ result.MsgData = new byte[msgLength];
+ Array.Copy(data, HeaderSize, result.MsgData, 0, msgLength);
+ }
+ return result;
+ }
+
+ ///
+ /// Calculate Fletcher-16 checksum
+ ///
+ private static (byte, byte) FletcherChecksum(byte[] buffer, int start, int end)
+ {
+ byte byte1 = 0;
+ byte byte2 = 0;
+
+ for (int i = start; i < end; i++)
+ {
+ byte1 = (byte)((byte1 + buffer[i]) % 256);
+ byte2 = (byte)((byte2 + byte1) % 256);
+ }
+
+ return (byte1, byte2);
+ }
+ }
+}
diff --git a/src/struct_frame/boilerplate/csharp/TinySysComp.cs b/src/struct_frame/boilerplate/csharp/TinySysComp.cs
new file mode 100644
index 00000000..65deafc2
--- /dev/null
+++ b/src/struct_frame/boilerplate/csharp/TinySysComp.cs
@@ -0,0 +1,173 @@
+// Automatically generated frame parser for C#
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
+
+using System;
+using System.Collections.Generic;
+
+namespace StructFrame
+{
+ ///
+ /// TinySysComp - Frame format parser and encoder
+ ///
+ public class TinySysComp : FrameFormatBase
+ {
+ public enum ParserState
+ {
+ LookingForStart = 0,
+ GettingMsgId = 1,
+ GettingLength = 2,
+ GettingPayload = 3
+ }
+
+ public const byte StartByte = 0x75;
+ public const int HeaderSize = 5;
+ public const int FooterSize = 2;
+ public const int Overhead = 7;
+ public const int LengthBytes = 1;
+
+ private ParserState _state;
+ private List _buffer;
+ private int _packetSize;
+ private int _msgId;
+ private int _msgLength;
+ private Func _getMsgLength;
+
+ ///
+ /// Initialize the TinySysComp parser
+ ///
+ /// Callback function to get message length from msg_id
+ public TinySysComp(Func getMsgLength = null)
+ {
+ _getMsgLength = getMsgLength;
+ _buffer = new List();
+ Reset();
+ }
+
+ ///
+ /// Reset parser state
+ ///
+ public override void Reset()
+ {
+ _state = ParserState.LookingForStart;
+ _buffer.Clear();
+ _packetSize = 0;
+ _msgId = 0;
+ _msgLength = 0;
+ }
+
+ ///
+ /// Parse a single byte
+ ///
+ /// The byte to parse
+ /// FrameParseResult with Valid=true when a complete valid message is received
+ public override FrameParseResult ParseByte(byte b)
+ {
+ var result = new FrameParseResult();
+
+ switch (_state)
+ {
+ case ParserState.LookingForStart:
+ if (b == StartByte)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.GettingMsgId;
+ }
+ break;
+
+ case ParserState.GettingMsgId:
+ _buffer.Add(b);
+ _msgId = b;
+ _state = ParserState.GettingLength;
+ break;
+
+ case ParserState.GettingLength:
+ _buffer.Add(b);
+ _msgLength = b;
+ _packetSize = Overhead + _msgLength;
+ _state = ParserState.GettingPayload;
+ break;
+
+ case ParserState.GettingPayload:
+ _buffer.Add(b);
+ if (_buffer.Count >= _packetSize)
+ {
+ result = ValidatePacket(_buffer.ToArray(), _buffer.Count);
+ _state = ParserState.LookingForStart;
+ }
+ break;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Encode a message with TinySysComp format
+ ///
+ /// Message ID
+ /// Message data bytes
+ /// Encoded frame as byte array
+ public override byte[] Encode(int msgId, byte[] msgData)
+ {
+ var output = new List();
+ output.Add(StartByte);
+ output.Add((byte)(msgData.Length & 0xFF));
+ output.Add((byte)msgId);
+ output.AddRange(msgData);
+
+ // Calculate Fletcher checksum
+ var ck = FletcherChecksum(output.ToArray(), 1, output.Count);
+ output.Add(ck.Item1);
+ output.Add(ck.Item2);
+ return output.ToArray();
+ }
+
+ ///
+ /// Validate a complete TinySysComp packet in a buffer
+ ///
+ /// Buffer containing the complete packet
+ /// Length of the data in the buffer
+ /// FrameParseResult with Valid=true if packet is valid
+ public override FrameParseResult ValidatePacket(byte[] data, int length)
+ {
+ var result = new FrameParseResult();
+
+ if (length < Overhead)
+ return result;
+
+ if (data[0] != StartByte)
+ return result;
+
+ int msgLength = length - Overhead;
+ int crcDataLen = msgLength + 1 + LengthBytes;
+ var ck = FletcherChecksum(data, 1, 1 + crcDataLen);
+
+ if (ck.Item1 == data[length - 2] && ck.Item2 == data[length - 1])
+ {
+ result.Valid = true;
+ result.MsgId = data[HeaderSize - 1];
+ result.MsgSize = msgLength;
+ result.MsgData = new byte[msgLength];
+ Array.Copy(data, HeaderSize, result.MsgData, 0, msgLength);
+ }
+ return result;
+ }
+
+ ///
+ /// Calculate Fletcher-16 checksum
+ ///
+ private static (byte, byte) FletcherChecksum(byte[] buffer, int start, int end)
+ {
+ byte byte1 = 0;
+ byte byte2 = 0;
+
+ for (int i = start; i < end; i++)
+ {
+ byte1 = (byte)((byte1 + buffer[i]) % 256);
+ byte2 = (byte)((byte2 + byte1) % 256);
+ }
+
+ return (byte1, byte2);
+ }
+ }
+}
diff --git a/src/struct_frame/boilerplate/csharp/UbxFrame.cs b/src/struct_frame/boilerplate/csharp/UbxFrame.cs
new file mode 100644
index 00000000..dc615b4b
--- /dev/null
+++ b/src/struct_frame/boilerplate/csharp/UbxFrame.cs
@@ -0,0 +1,196 @@
+// Automatically generated frame parser for C#
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
+
+using System;
+using System.Collections.Generic;
+
+namespace StructFrame
+{
+ ///
+ /// UbxFrame - Frame format parser and encoder
+ ///
+ public class UbxFrame : FrameFormatBase
+ {
+ public enum ParserState
+ {
+ LookingForStart1 = 0,
+ LookingForStart2 = 1,
+ GettingMsgId = 2,
+ GettingLength = 3,
+ GettingPayload = 4
+ }
+
+ public const byte StartByte1 = 0xB5;
+ public const byte StartByte2 = 0x62;
+ public const int HeaderSize = 5;
+ public const int FooterSize = 2;
+ public const int Overhead = 7;
+ public const int LengthBytes = 1;
+
+ private ParserState _state;
+ private List _buffer;
+ private int _packetSize;
+ private int _msgId;
+ private int _msgLength;
+ private Func _getMsgLength;
+
+ ///
+ /// Initialize the UbxFrame parser
+ ///
+ /// Callback function to get message length from msg_id
+ public UbxFrame(Func getMsgLength = null)
+ {
+ _getMsgLength = getMsgLength;
+ _buffer = new List();
+ Reset();
+ }
+
+ ///
+ /// Reset parser state
+ ///
+ public override void Reset()
+ {
+ _state = ParserState.LookingForStart1;
+ _buffer.Clear();
+ _packetSize = 0;
+ _msgId = 0;
+ _msgLength = 0;
+ }
+
+ ///
+ /// Parse a single byte
+ ///
+ /// The byte to parse
+ /// FrameParseResult with Valid=true when a complete valid message is received
+ public override FrameParseResult ParseByte(byte b)
+ {
+ var result = new FrameParseResult();
+
+ switch (_state)
+ {
+ case ParserState.LookingForStart1:
+ if (b == StartByte1)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.LookingForStart2;
+ }
+ break;
+
+ case ParserState.LookingForStart2:
+ if (b == StartByte2)
+ {
+ _buffer.Add(b);
+ _state = ParserState.GettingMsgId;
+ }
+ else if (b == StartByte1)
+ {
+ _buffer.Clear();
+ _buffer.Add(b);
+ _state = ParserState.LookingForStart2;
+ }
+ else
+ {
+ _state = ParserState.LookingForStart1;
+ }
+ break;
+
+ case ParserState.GettingMsgId:
+ _buffer.Add(b);
+ _msgId = b;
+ _state = ParserState.GettingLength;
+ break;
+
+ case ParserState.GettingLength:
+ _buffer.Add(b);
+ _msgLength = b;
+ _packetSize = Overhead + _msgLength;
+ _state = ParserState.GettingPayload;
+ break;
+
+ case ParserState.GettingPayload:
+ _buffer.Add(b);
+ if (_buffer.Count >= _packetSize)
+ {
+ result = ValidatePacket(_buffer.ToArray(), _buffer.Count);
+ _state = ParserState.LookingForStart1;
+ }
+ break;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Encode a message with UbxFrame format
+ ///
+ /// Message ID
+ /// Message data bytes
+ /// Encoded frame as byte array
+ public override byte[] Encode(int msgId, byte[] msgData)
+ {
+ var output = new List();
+ output.Add(StartByte1);
+ output.Add(StartByte2);
+ output.Add((byte)(msgData.Length & 0xFF));
+ output.Add((byte)msgId);
+ output.AddRange(msgData);
+
+ // Calculate Fletcher checksum
+ var ck = FletcherChecksum(output.ToArray(), 2, output.Count);
+ output.Add(ck.Item1);
+ output.Add(ck.Item2);
+ return output.ToArray();
+ }
+
+ ///
+ /// Validate a complete UbxFrame packet in a buffer
+ ///
+ /// Buffer containing the complete packet
+ /// Length of the data in the buffer
+ /// FrameParseResult with Valid=true if packet is valid
+ public override FrameParseResult ValidatePacket(byte[] data, int length)
+ {
+ var result = new FrameParseResult();
+
+ if (length < Overhead)
+ return result;
+
+ if (data[0] != StartByte1)
+ return result;
+ if (data[1] != StartByte2)
+ return result;
+
+ int msgLength = length - Overhead;
+ int crcDataLen = msgLength + 1 + LengthBytes;
+ var ck = FletcherChecksum(data, 2, 2 + crcDataLen);
+
+ if (ck.Item1 == data[length - 2] && ck.Item2 == data[length - 1])
+ {
+ result.Valid = true;
+ result.MsgId = data[HeaderSize - 1];
+ result.MsgSize = msgLength;
+ result.MsgData = new byte[msgLength];
+ Array.Copy(data, HeaderSize, result.MsgData, 0, msgLength);
+ }
+ return result;
+ }
+
+ ///
+ /// Calculate Fletcher-16 checksum
+ ///
+ private static (byte, byte) FletcherChecksum(byte[] buffer, int start, int end)
+ {
+ byte byte1 = 0;
+ byte byte2 = 0;
+
+ for (int i = start; i < end; i++)
+ {
+ byte1 = (byte)((byte1 + buffer[i]) % 256);
+ byte2 = (byte)((byte2 + byte1) % 256);
+ }
+
+ return (byte1, byte2);
+ }
+ }
+}
diff --git a/src/struct_frame/boilerplate/js/BasicDefault.js b/src/struct_frame/boilerplate/js/BasicDefault.js
index ed27c71a..e0a1048e 100644
--- a/src/struct_frame/boilerplate/js/BasicDefault.js
+++ b/src/struct_frame/boilerplate/js/BasicDefault.js
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
const {
createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/js/BasicExtended.js b/src/struct_frame/boilerplate/js/BasicExtended.js
index 0a5f62c8..46ec6c20 100644
--- a/src/struct_frame/boilerplate/js/BasicExtended.js
+++ b/src/struct_frame/boilerplate/js/BasicExtended.js
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
const {
createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/js/BasicExtendedLength.js b/src/struct_frame/boilerplate/js/BasicExtendedLength.js
index 6b85964b..0bcc57bc 100644
--- a/src/struct_frame/boilerplate/js/BasicExtendedLength.js
+++ b/src/struct_frame/boilerplate/js/BasicExtendedLength.js
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
const {
createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/js/BasicExtendedMsgIds.js b/src/struct_frame/boilerplate/js/BasicExtendedMsgIds.js
index a390d7b6..2abfeb1b 100644
--- a/src/struct_frame/boilerplate/js/BasicExtendedMsgIds.js
+++ b/src/struct_frame/boilerplate/js/BasicExtendedMsgIds.js
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
const {
createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/js/BasicExtendedMultiSystemStream.js b/src/struct_frame/boilerplate/js/BasicExtendedMultiSystemStream.js
index 5a7d88b6..31adfffd 100644
--- a/src/struct_frame/boilerplate/js/BasicExtendedMultiSystemStream.js
+++ b/src/struct_frame/boilerplate/js/BasicExtendedMultiSystemStream.js
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
const {
createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/js/BasicMinimal.js b/src/struct_frame/boilerplate/js/BasicMinimal.js
index de7765aa..b1824fcf 100644
--- a/src/struct_frame/boilerplate/js/BasicMinimal.js
+++ b/src/struct_frame/boilerplate/js/BasicMinimal.js
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
const {
createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/js/BasicMultiSystemStream.js b/src/struct_frame/boilerplate/js/BasicMultiSystemStream.js
index f4709a42..41004d20 100644
--- a/src/struct_frame/boilerplate/js/BasicMultiSystemStream.js
+++ b/src/struct_frame/boilerplate/js/BasicMultiSystemStream.js
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
const {
createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/js/BasicSeq.js b/src/struct_frame/boilerplate/js/BasicSeq.js
index e7981273..6919a7c1 100644
--- a/src/struct_frame/boilerplate/js/BasicSeq.js
+++ b/src/struct_frame/boilerplate/js/BasicSeq.js
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
const {
createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/js/BasicSysComp.js b/src/struct_frame/boilerplate/js/BasicSysComp.js
index d5239a4d..8088ea94 100644
--- a/src/struct_frame/boilerplate/js/BasicSysComp.js
+++ b/src/struct_frame/boilerplate/js/BasicSysComp.js
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
const {
createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/js/FrameFormatConfig.js b/src/struct_frame/boilerplate/js/FrameFormatConfig.js
index a327cfe4..c1008716 100644
--- a/src/struct_frame/boilerplate/js/FrameFormatConfig.js
+++ b/src/struct_frame/boilerplate/js/FrameFormatConfig.js
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
const {
createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/js/MavlinkV1Frame.js b/src/struct_frame/boilerplate/js/MavlinkV1Frame.js
index 5db888ea..e954e3bf 100644
--- a/src/struct_frame/boilerplate/js/MavlinkV1Frame.js
+++ b/src/struct_frame/boilerplate/js/MavlinkV1Frame.js
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
const {
createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/js/MavlinkV2Frame.js b/src/struct_frame/boilerplate/js/MavlinkV2Frame.js
index c76d73c1..f434a3b0 100644
--- a/src/struct_frame/boilerplate/js/MavlinkV2Frame.js
+++ b/src/struct_frame/boilerplate/js/MavlinkV2Frame.js
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
const {
createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/js/TinyDefault.js b/src/struct_frame/boilerplate/js/TinyDefault.js
index 0e89ac08..b43fbb74 100644
--- a/src/struct_frame/boilerplate/js/TinyDefault.js
+++ b/src/struct_frame/boilerplate/js/TinyDefault.js
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
const {
createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/js/TinyExtended.js b/src/struct_frame/boilerplate/js/TinyExtended.js
index 04209afb..ca872415 100644
--- a/src/struct_frame/boilerplate/js/TinyExtended.js
+++ b/src/struct_frame/boilerplate/js/TinyExtended.js
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
const {
createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/js/TinyExtendedLength.js b/src/struct_frame/boilerplate/js/TinyExtendedLength.js
index f62bac8d..e18572f1 100644
--- a/src/struct_frame/boilerplate/js/TinyExtendedLength.js
+++ b/src/struct_frame/boilerplate/js/TinyExtendedLength.js
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
const {
createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/js/TinyExtendedMsgIds.js b/src/struct_frame/boilerplate/js/TinyExtendedMsgIds.js
index d9b3820d..122318ce 100644
--- a/src/struct_frame/boilerplate/js/TinyExtendedMsgIds.js
+++ b/src/struct_frame/boilerplate/js/TinyExtendedMsgIds.js
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
const {
createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/js/TinyExtendedMultiSystemStream.js b/src/struct_frame/boilerplate/js/TinyExtendedMultiSystemStream.js
index 0400df4b..5858249b 100644
--- a/src/struct_frame/boilerplate/js/TinyExtendedMultiSystemStream.js
+++ b/src/struct_frame/boilerplate/js/TinyExtendedMultiSystemStream.js
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
const {
createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/js/TinyMinimal.js b/src/struct_frame/boilerplate/js/TinyMinimal.js
index b5b51f42..2c297f28 100644
--- a/src/struct_frame/boilerplate/js/TinyMinimal.js
+++ b/src/struct_frame/boilerplate/js/TinyMinimal.js
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
const {
createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/js/TinyMultiSystemStream.js b/src/struct_frame/boilerplate/js/TinyMultiSystemStream.js
index 045563c8..66f3ceb4 100644
--- a/src/struct_frame/boilerplate/js/TinyMultiSystemStream.js
+++ b/src/struct_frame/boilerplate/js/TinyMultiSystemStream.js
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
const {
createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/js/TinySeq.js b/src/struct_frame/boilerplate/js/TinySeq.js
index b3cad8bc..dabc1771 100644
--- a/src/struct_frame/boilerplate/js/TinySeq.js
+++ b/src/struct_frame/boilerplate/js/TinySeq.js
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
const {
createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/js/TinySysComp.js b/src/struct_frame/boilerplate/js/TinySysComp.js
index 6cebbe1f..0d260d26 100644
--- a/src/struct_frame/boilerplate/js/TinySysComp.js
+++ b/src/struct_frame/boilerplate/js/TinySysComp.js
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
const {
createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/js/UbxFrame.js b/src/struct_frame/boilerplate/js/UbxFrame.js
index cf67599f..d5be22d2 100644
--- a/src/struct_frame/boilerplate/js/UbxFrame.js
+++ b/src/struct_frame/boilerplate/js/UbxFrame.js
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
const {
createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/js/frame_base.js b/src/struct_frame/boilerplate/js/frame_base.js
index 331d15e5..9567d584 100644
--- a/src/struct_frame/boilerplate/js/frame_base.js
+++ b/src/struct_frame/boilerplate/js/frame_base.js
@@ -1,5 +1,5 @@
// Automatically generated frame parser base utilities
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
// Frame format type enumeration
const FrameFormatType = {
diff --git a/src/struct_frame/boilerplate/js/index.js b/src/struct_frame/boilerplate/js/index.js
index 7828c776..b55cf3c3 100644
--- a/src/struct_frame/boilerplate/js/index.js
+++ b/src/struct_frame/boilerplate/js/index.js
@@ -1,5 +1,5 @@
// Automatically generated frame parser package
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
// Base utilities
const frameBase = require('./frame_base');
diff --git a/src/struct_frame/boilerplate/py/__init__.py b/src/struct_frame/boilerplate/py/__init__.py
index d2abe5e1..5476ff92 100644
--- a/src/struct_frame/boilerplate/py/__init__.py
+++ b/src/struct_frame/boilerplate/py/__init__.py
@@ -1,5 +1,5 @@
# Automatically generated frame parser package
-# Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+# Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
# Base utilities and generic parser infrastructure
from .frame_base import (
diff --git a/src/struct_frame/boilerplate/py/basic_default.py b/src/struct_frame/boilerplate/py/basic_default.py
index 4df8b5b5..dcc3a89c 100644
--- a/src/struct_frame/boilerplate/py/basic_default.py
+++ b/src/struct_frame/boilerplate/py/basic_default.py
@@ -1,5 +1,5 @@
# Automatically generated frame parser
-# Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+# Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
from .frame_base import (
FrameParserConfig, create_frame_parser_class, GenericParserState,
diff --git a/src/struct_frame/boilerplate/py/basic_extended.py b/src/struct_frame/boilerplate/py/basic_extended.py
index a692d09f..aeee5c88 100644
--- a/src/struct_frame/boilerplate/py/basic_extended.py
+++ b/src/struct_frame/boilerplate/py/basic_extended.py
@@ -1,5 +1,5 @@
# Automatically generated frame parser
-# Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+# Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
from .frame_base import (
FrameParserConfig, create_frame_parser_class, GenericParserState,
diff --git a/src/struct_frame/boilerplate/py/basic_extended_length.py b/src/struct_frame/boilerplate/py/basic_extended_length.py
index fc0a8ccd..5280255f 100644
--- a/src/struct_frame/boilerplate/py/basic_extended_length.py
+++ b/src/struct_frame/boilerplate/py/basic_extended_length.py
@@ -1,5 +1,5 @@
# Automatically generated frame parser
-# Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+# Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
from .frame_base import (
FrameParserConfig, create_frame_parser_class, GenericParserState,
diff --git a/src/struct_frame/boilerplate/py/basic_extended_msg_ids.py b/src/struct_frame/boilerplate/py/basic_extended_msg_ids.py
index 5ca34d0c..77d0b377 100644
--- a/src/struct_frame/boilerplate/py/basic_extended_msg_ids.py
+++ b/src/struct_frame/boilerplate/py/basic_extended_msg_ids.py
@@ -1,5 +1,5 @@
# Automatically generated frame parser
-# Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+# Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
from .frame_base import (
FrameParserConfig, create_frame_parser_class, GenericParserState,
diff --git a/src/struct_frame/boilerplate/py/basic_extended_multi_system_stream.py b/src/struct_frame/boilerplate/py/basic_extended_multi_system_stream.py
index 800e93b1..02c8fe55 100644
--- a/src/struct_frame/boilerplate/py/basic_extended_multi_system_stream.py
+++ b/src/struct_frame/boilerplate/py/basic_extended_multi_system_stream.py
@@ -1,5 +1,5 @@
# Automatically generated frame parser
-# Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+# Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
from .frame_base import (
FrameParserConfig, create_frame_parser_class, GenericParserState,
diff --git a/src/struct_frame/boilerplate/py/basic_minimal.py b/src/struct_frame/boilerplate/py/basic_minimal.py
index 5fed738b..f4572d06 100644
--- a/src/struct_frame/boilerplate/py/basic_minimal.py
+++ b/src/struct_frame/boilerplate/py/basic_minimal.py
@@ -1,5 +1,5 @@
# Automatically generated frame parser
-# Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+# Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
from .frame_base import (
FrameParserConfig, create_frame_parser_class, GenericParserState,
diff --git a/src/struct_frame/boilerplate/py/basic_multi_system_stream.py b/src/struct_frame/boilerplate/py/basic_multi_system_stream.py
index 99ecf897..61eb0bf1 100644
--- a/src/struct_frame/boilerplate/py/basic_multi_system_stream.py
+++ b/src/struct_frame/boilerplate/py/basic_multi_system_stream.py
@@ -1,5 +1,5 @@
# Automatically generated frame parser
-# Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+# Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
from .frame_base import (
FrameParserConfig, create_frame_parser_class, GenericParserState,
diff --git a/src/struct_frame/boilerplate/py/basic_seq.py b/src/struct_frame/boilerplate/py/basic_seq.py
index ab02dfa8..58aec598 100644
--- a/src/struct_frame/boilerplate/py/basic_seq.py
+++ b/src/struct_frame/boilerplate/py/basic_seq.py
@@ -1,5 +1,5 @@
# Automatically generated frame parser
-# Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+# Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
from .frame_base import (
FrameParserConfig, create_frame_parser_class, GenericParserState,
diff --git a/src/struct_frame/boilerplate/py/basic_sys_comp.py b/src/struct_frame/boilerplate/py/basic_sys_comp.py
index ea6b6f7d..1285f982 100644
--- a/src/struct_frame/boilerplate/py/basic_sys_comp.py
+++ b/src/struct_frame/boilerplate/py/basic_sys_comp.py
@@ -1,5 +1,5 @@
# Automatically generated frame parser
-# Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+# Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
from .frame_base import (
FrameParserConfig, create_frame_parser_class, GenericParserState,
diff --git a/src/struct_frame/boilerplate/py/frame_base.py b/src/struct_frame/boilerplate/py/frame_base.py
index 92370160..313e0607 100644
--- a/src/struct_frame/boilerplate/py/frame_base.py
+++ b/src/struct_frame/boilerplate/py/frame_base.py
@@ -1,5 +1,5 @@
# Automatically generated frame parser base utilities
-# Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+# Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
from enum import Enum
from typing import Callable, List, Tuple, Union
diff --git a/src/struct_frame/boilerplate/py/frame_format_config.py b/src/struct_frame/boilerplate/py/frame_format_config.py
index 27328a5d..6b9e80fe 100644
--- a/src/struct_frame/boilerplate/py/frame_format_config.py
+++ b/src/struct_frame/boilerplate/py/frame_format_config.py
@@ -1,5 +1,5 @@
# Automatically generated frame parser
-# Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+# Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
from .frame_base import (
FrameParserConfig, create_frame_parser_class, GenericParserState,
diff --git a/src/struct_frame/boilerplate/py/mavlink_v1_frame.py b/src/struct_frame/boilerplate/py/mavlink_v1_frame.py
index ae8abc2f..862391a7 100644
--- a/src/struct_frame/boilerplate/py/mavlink_v1_frame.py
+++ b/src/struct_frame/boilerplate/py/mavlink_v1_frame.py
@@ -1,5 +1,5 @@
# Automatically generated frame parser
-# Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+# Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
from .frame_base import (
FrameParserConfig, create_frame_parser_class, GenericParserState,
diff --git a/src/struct_frame/boilerplate/py/mavlink_v2_frame.py b/src/struct_frame/boilerplate/py/mavlink_v2_frame.py
index 88ff273b..8fa234b8 100644
--- a/src/struct_frame/boilerplate/py/mavlink_v2_frame.py
+++ b/src/struct_frame/boilerplate/py/mavlink_v2_frame.py
@@ -1,5 +1,5 @@
# Automatically generated frame parser
-# Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+# Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
from .frame_base import (
FrameParserConfig, create_frame_parser_class, GenericParserState,
diff --git a/src/struct_frame/boilerplate/py/tiny_default.py b/src/struct_frame/boilerplate/py/tiny_default.py
index 9c491053..f1bea2c5 100644
--- a/src/struct_frame/boilerplate/py/tiny_default.py
+++ b/src/struct_frame/boilerplate/py/tiny_default.py
@@ -1,5 +1,5 @@
# Automatically generated frame parser
-# Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+# Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
from .frame_base import (
FrameParserConfig, create_frame_parser_class, GenericParserState,
diff --git a/src/struct_frame/boilerplate/py/tiny_extended.py b/src/struct_frame/boilerplate/py/tiny_extended.py
index de14acd4..1556bfab 100644
--- a/src/struct_frame/boilerplate/py/tiny_extended.py
+++ b/src/struct_frame/boilerplate/py/tiny_extended.py
@@ -1,5 +1,5 @@
# Automatically generated frame parser
-# Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+# Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
from .frame_base import (
FrameParserConfig, create_frame_parser_class, GenericParserState,
diff --git a/src/struct_frame/boilerplate/py/tiny_extended_length.py b/src/struct_frame/boilerplate/py/tiny_extended_length.py
index 5b231512..c7470a3d 100644
--- a/src/struct_frame/boilerplate/py/tiny_extended_length.py
+++ b/src/struct_frame/boilerplate/py/tiny_extended_length.py
@@ -1,5 +1,5 @@
# Automatically generated frame parser
-# Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+# Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
from .frame_base import (
FrameParserConfig, create_frame_parser_class, GenericParserState,
diff --git a/src/struct_frame/boilerplate/py/tiny_extended_msg_ids.py b/src/struct_frame/boilerplate/py/tiny_extended_msg_ids.py
index f0d4cf52..2d8bc4a4 100644
--- a/src/struct_frame/boilerplate/py/tiny_extended_msg_ids.py
+++ b/src/struct_frame/boilerplate/py/tiny_extended_msg_ids.py
@@ -1,5 +1,5 @@
# Automatically generated frame parser
-# Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+# Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
from .frame_base import (
FrameParserConfig, create_frame_parser_class, GenericParserState,
diff --git a/src/struct_frame/boilerplate/py/tiny_extended_multi_system_stream.py b/src/struct_frame/boilerplate/py/tiny_extended_multi_system_stream.py
index 06ce0dd9..c9c4503d 100644
--- a/src/struct_frame/boilerplate/py/tiny_extended_multi_system_stream.py
+++ b/src/struct_frame/boilerplate/py/tiny_extended_multi_system_stream.py
@@ -1,5 +1,5 @@
# Automatically generated frame parser
-# Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+# Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
from .frame_base import (
FrameParserConfig, create_frame_parser_class, GenericParserState,
diff --git a/src/struct_frame/boilerplate/py/tiny_minimal.py b/src/struct_frame/boilerplate/py/tiny_minimal.py
index d3f6ca81..aaafdb3a 100644
--- a/src/struct_frame/boilerplate/py/tiny_minimal.py
+++ b/src/struct_frame/boilerplate/py/tiny_minimal.py
@@ -1,5 +1,5 @@
# Automatically generated frame parser
-# Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+# Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
from .frame_base import (
FrameParserConfig, create_frame_parser_class, GenericParserState,
diff --git a/src/struct_frame/boilerplate/py/tiny_multi_system_stream.py b/src/struct_frame/boilerplate/py/tiny_multi_system_stream.py
index 2cc94208..c59a8ad3 100644
--- a/src/struct_frame/boilerplate/py/tiny_multi_system_stream.py
+++ b/src/struct_frame/boilerplate/py/tiny_multi_system_stream.py
@@ -1,5 +1,5 @@
# Automatically generated frame parser
-# Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+# Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
from .frame_base import (
FrameParserConfig, create_frame_parser_class, GenericParserState,
diff --git a/src/struct_frame/boilerplate/py/tiny_seq.py b/src/struct_frame/boilerplate/py/tiny_seq.py
index c4800520..abccc4cb 100644
--- a/src/struct_frame/boilerplate/py/tiny_seq.py
+++ b/src/struct_frame/boilerplate/py/tiny_seq.py
@@ -1,5 +1,5 @@
# Automatically generated frame parser
-# Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+# Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
from .frame_base import (
FrameParserConfig, create_frame_parser_class, GenericParserState,
diff --git a/src/struct_frame/boilerplate/py/tiny_sys_comp.py b/src/struct_frame/boilerplate/py/tiny_sys_comp.py
index 2735a2f8..fafd0255 100644
--- a/src/struct_frame/boilerplate/py/tiny_sys_comp.py
+++ b/src/struct_frame/boilerplate/py/tiny_sys_comp.py
@@ -1,5 +1,5 @@
# Automatically generated frame parser
-# Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+# Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
from .frame_base import (
FrameParserConfig, create_frame_parser_class, GenericParserState,
diff --git a/src/struct_frame/boilerplate/py/ubx_frame.py b/src/struct_frame/boilerplate/py/ubx_frame.py
index ceeb3e48..4cdf93e4 100644
--- a/src/struct_frame/boilerplate/py/ubx_frame.py
+++ b/src/struct_frame/boilerplate/py/ubx_frame.py
@@ -1,5 +1,5 @@
# Automatically generated frame parser
-# Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+# Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
from .frame_base import (
FrameParserConfig, create_frame_parser_class, GenericParserState,
diff --git a/src/struct_frame/boilerplate/ts/BasicDefault.ts b/src/struct_frame/boilerplate/ts/BasicDefault.ts
index c43b84a1..b9c7522e 100644
--- a/src/struct_frame/boilerplate/ts/BasicDefault.ts
+++ b/src/struct_frame/boilerplate/ts/BasicDefault.ts
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
import {
FrameParserConfig, createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/ts/BasicExtended.ts b/src/struct_frame/boilerplate/ts/BasicExtended.ts
index 3c58f8d8..f26a5b9a 100644
--- a/src/struct_frame/boilerplate/ts/BasicExtended.ts
+++ b/src/struct_frame/boilerplate/ts/BasicExtended.ts
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
import {
FrameParserConfig, createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/ts/BasicExtendedLength.ts b/src/struct_frame/boilerplate/ts/BasicExtendedLength.ts
index fff6a52f..47108cd3 100644
--- a/src/struct_frame/boilerplate/ts/BasicExtendedLength.ts
+++ b/src/struct_frame/boilerplate/ts/BasicExtendedLength.ts
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
import {
FrameParserConfig, createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/ts/BasicExtendedMsgIds.ts b/src/struct_frame/boilerplate/ts/BasicExtendedMsgIds.ts
index 0c7f8994..989bff4f 100644
--- a/src/struct_frame/boilerplate/ts/BasicExtendedMsgIds.ts
+++ b/src/struct_frame/boilerplate/ts/BasicExtendedMsgIds.ts
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
import {
FrameParserConfig, createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/ts/BasicExtendedMultiSystemStream.ts b/src/struct_frame/boilerplate/ts/BasicExtendedMultiSystemStream.ts
index 73df40f8..0c4ae0ad 100644
--- a/src/struct_frame/boilerplate/ts/BasicExtendedMultiSystemStream.ts
+++ b/src/struct_frame/boilerplate/ts/BasicExtendedMultiSystemStream.ts
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
import {
FrameParserConfig, createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/ts/BasicMinimal.ts b/src/struct_frame/boilerplate/ts/BasicMinimal.ts
index ecd3e859..75fa548a 100644
--- a/src/struct_frame/boilerplate/ts/BasicMinimal.ts
+++ b/src/struct_frame/boilerplate/ts/BasicMinimal.ts
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
import {
FrameParserConfig, createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/ts/BasicMultiSystemStream.ts b/src/struct_frame/boilerplate/ts/BasicMultiSystemStream.ts
index b41e01fe..f5c6a81e 100644
--- a/src/struct_frame/boilerplate/ts/BasicMultiSystemStream.ts
+++ b/src/struct_frame/boilerplate/ts/BasicMultiSystemStream.ts
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
import {
FrameParserConfig, createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/ts/BasicSeq.ts b/src/struct_frame/boilerplate/ts/BasicSeq.ts
index d7af1043..a5c1d3af 100644
--- a/src/struct_frame/boilerplate/ts/BasicSeq.ts
+++ b/src/struct_frame/boilerplate/ts/BasicSeq.ts
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
import {
FrameParserConfig, createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/ts/BasicSysComp.ts b/src/struct_frame/boilerplate/ts/BasicSysComp.ts
index d1c1f67f..0359c3cf 100644
--- a/src/struct_frame/boilerplate/ts/BasicSysComp.ts
+++ b/src/struct_frame/boilerplate/ts/BasicSysComp.ts
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
import {
FrameParserConfig, createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/ts/FrameFormatConfig.ts b/src/struct_frame/boilerplate/ts/FrameFormatConfig.ts
index d0016a1b..43952caf 100644
--- a/src/struct_frame/boilerplate/ts/FrameFormatConfig.ts
+++ b/src/struct_frame/boilerplate/ts/FrameFormatConfig.ts
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
import {
FrameParserConfig, createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/ts/MavlinkV1Frame.ts b/src/struct_frame/boilerplate/ts/MavlinkV1Frame.ts
index d71b7a8f..33330f84 100644
--- a/src/struct_frame/boilerplate/ts/MavlinkV1Frame.ts
+++ b/src/struct_frame/boilerplate/ts/MavlinkV1Frame.ts
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
import {
FrameParserConfig, createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/ts/MavlinkV2Frame.ts b/src/struct_frame/boilerplate/ts/MavlinkV2Frame.ts
index 265839e4..83caeab5 100644
--- a/src/struct_frame/boilerplate/ts/MavlinkV2Frame.ts
+++ b/src/struct_frame/boilerplate/ts/MavlinkV2Frame.ts
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
import {
FrameParserConfig, createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/ts/TinyDefault.ts b/src/struct_frame/boilerplate/ts/TinyDefault.ts
index 6b312e2e..45d8705a 100644
--- a/src/struct_frame/boilerplate/ts/TinyDefault.ts
+++ b/src/struct_frame/boilerplate/ts/TinyDefault.ts
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
import {
FrameParserConfig, createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/ts/TinyExtended.ts b/src/struct_frame/boilerplate/ts/TinyExtended.ts
index 7d504a31..4d602b54 100644
--- a/src/struct_frame/boilerplate/ts/TinyExtended.ts
+++ b/src/struct_frame/boilerplate/ts/TinyExtended.ts
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
import {
FrameParserConfig, createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/ts/TinyExtendedLength.ts b/src/struct_frame/boilerplate/ts/TinyExtendedLength.ts
index 9326a099..17e32e19 100644
--- a/src/struct_frame/boilerplate/ts/TinyExtendedLength.ts
+++ b/src/struct_frame/boilerplate/ts/TinyExtendedLength.ts
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
import {
FrameParserConfig, createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/ts/TinyExtendedMsgIds.ts b/src/struct_frame/boilerplate/ts/TinyExtendedMsgIds.ts
index e938eb1d..5b7c907d 100644
--- a/src/struct_frame/boilerplate/ts/TinyExtendedMsgIds.ts
+++ b/src/struct_frame/boilerplate/ts/TinyExtendedMsgIds.ts
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
import {
FrameParserConfig, createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/ts/TinyExtendedMultiSystemStream.ts b/src/struct_frame/boilerplate/ts/TinyExtendedMultiSystemStream.ts
index 35a79adb..099e5e2f 100644
--- a/src/struct_frame/boilerplate/ts/TinyExtendedMultiSystemStream.ts
+++ b/src/struct_frame/boilerplate/ts/TinyExtendedMultiSystemStream.ts
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
import {
FrameParserConfig, createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/ts/TinyMinimal.ts b/src/struct_frame/boilerplate/ts/TinyMinimal.ts
index 51c5a920..ce4ebaa3 100644
--- a/src/struct_frame/boilerplate/ts/TinyMinimal.ts
+++ b/src/struct_frame/boilerplate/ts/TinyMinimal.ts
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
import {
FrameParserConfig, createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/ts/TinyMultiSystemStream.ts b/src/struct_frame/boilerplate/ts/TinyMultiSystemStream.ts
index d4fe9a3d..047eef37 100644
--- a/src/struct_frame/boilerplate/ts/TinyMultiSystemStream.ts
+++ b/src/struct_frame/boilerplate/ts/TinyMultiSystemStream.ts
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
import {
FrameParserConfig, createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/ts/TinySeq.ts b/src/struct_frame/boilerplate/ts/TinySeq.ts
index b78dd9f1..b70ae976 100644
--- a/src/struct_frame/boilerplate/ts/TinySeq.ts
+++ b/src/struct_frame/boilerplate/ts/TinySeq.ts
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
import {
FrameParserConfig, createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/ts/TinySysComp.ts b/src/struct_frame/boilerplate/ts/TinySysComp.ts
index 1f55713e..63b4c18b 100644
--- a/src/struct_frame/boilerplate/ts/TinySysComp.ts
+++ b/src/struct_frame/boilerplate/ts/TinySysComp.ts
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
import {
FrameParserConfig, createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/ts/UbxFrame.ts b/src/struct_frame/boilerplate/ts/UbxFrame.ts
index 173bf7a4..97101c5a 100644
--- a/src/struct_frame/boilerplate/ts/UbxFrame.ts
+++ b/src/struct_frame/boilerplate/ts/UbxFrame.ts
@@ -1,5 +1,5 @@
// Automatically generated frame parser
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
import {
FrameParserConfig, createFrameParserClass, GenericParserState,
diff --git a/src/struct_frame/boilerplate/ts/frame_base.ts b/src/struct_frame/boilerplate/ts/frame_base.ts
index 7c369898..08bac215 100644
--- a/src/struct_frame/boilerplate/ts/frame_base.ts
+++ b/src/struct_frame/boilerplate/ts/frame_base.ts
@@ -1,5 +1,5 @@
// Automatically generated frame parser base utilities
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
// Frame format type enumeration
export enum FrameFormatType {
diff --git a/src/struct_frame/boilerplate/ts/index.ts b/src/struct_frame/boilerplate/ts/index.ts
index d88c692f..74849bd7 100644
--- a/src/struct_frame/boilerplate/ts/index.ts
+++ b/src/struct_frame/boilerplate/ts/index.ts
@@ -1,5 +1,5 @@
// Automatically generated frame parser package
-// Generated by 0.0.1 at Thu Dec 4 22:44:11 2025.
+// Generated by 0.0.1 at Fri Dec 5 01:14:49 2025.
// Base utilities and generic parser infrastructure
export {
diff --git a/src/struct_frame/csharp_gen.py b/src/struct_frame/csharp_gen.py
new file mode 100644
index 00000000..1386bcf3
--- /dev/null
+++ b/src/struct_frame/csharp_gen.py
@@ -0,0 +1,245 @@
+#!/usr/bin/env python3
+# kate: replace-tabs on; indent-width 4;
+"""
+C# code generator for struct-frame.
+
+This module generates C# code for struct serialization using
+StructLayout and MarshalAs attributes for binary compatibility.
+"""
+
+from struct_frame import version, NamingStyleC, CamelToSnakeCase, pascalCase
+import time
+
+StyleC = NamingStyleC()
+
+# Mapping from proto types to C# types
+csharp_types = {
+ "uint8": "byte",
+ "int8": "sbyte",
+ "uint16": "ushort",
+ "int16": "short",
+ "uint32": "uint",
+ "int32": "int",
+ "bool": "bool",
+ "float": "float",
+ "double": "double",
+ "uint64": "ulong",
+ "int64": "long",
+ "string": "byte", # Strings are byte arrays in C#
+}
+
+
+class EnumCSharpGen():
+ @staticmethod
+ def generate(field):
+ leading_comment = field.comments
+
+ result = ''
+ if leading_comment:
+ for c in leading_comment:
+ result += ' /// \n'
+ result += ' /// %s\n' % c.strip('/')
+ result += ' /// \n'
+
+ enumName = '%s%s' % (pascalCase(field.package), field.name)
+ result += ' public enum %s : byte\n' % enumName
+ result += ' {\n'
+
+ enum_length = len(field.data)
+ enum_values = []
+ for index, (d) in enumerate(field.data):
+ leading_comment = field.data[d][1]
+
+ if leading_comment:
+ for c in leading_comment:
+ enum_values.append(' /// ')
+ enum_values.append(' /// %s' % c.strip('/'))
+ enum_values.append(' /// ')
+
+ comma = ","
+ if index == enum_length - 1:
+ comma = ""
+
+ enum_value = " %s = %d%s" % (
+ StyleC.enum_entry(d), field.data[d][0], comma)
+ enum_values.append(enum_value)
+
+ result += '\n'.join(enum_values)
+ result += '\n }\n'
+
+ return result
+
+
+class FieldCSharpGen():
+ @staticmethod
+ def generate(field, field_offset):
+ """Generate C# field definition with FieldOffset attribute"""
+ result = ''
+ var_name = pascalCase(field.name)
+ type_name = field.fieldType
+
+ # Add leading comments
+ leading_comment = field.comments
+ if leading_comment:
+ for c in leading_comment:
+ result += ' /// \n'
+ result += ' /// %s\n' % c.strip('/')
+ result += ' /// \n'
+
+ # Handle basic type resolution
+ if type_name in csharp_types:
+ base_type = csharp_types[type_name]
+ else:
+ if field.isEnum:
+ base_type = '%s%s' % (pascalCase(field.package), type_name)
+ else:
+ base_type = '%s%s' % (pascalCase(field.package), type_name)
+
+ # Handle arrays
+ if field.is_array:
+ if field.fieldType == "string":
+ # String arrays need both array size and individual string size
+ if field.size_option is not None:
+ # Fixed string array: size_option strings, each element_size bytes
+ total_size = field.size_option * field.element_size
+ result += f' [FieldOffset({field_offset})]\n'
+ result += f' [MarshalAs(UnmanagedType.ByValArray, SizeConst = {total_size})]\n'
+ result += f' public byte[] {var_name}; // Fixed string array: {field.size_option} strings, each max {field.element_size} chars\n'
+ elif field.max_size is not None:
+ # Variable string array: count byte + max_size strings of element_size bytes each
+ result += f' [FieldOffset({field_offset})]\n'
+ result += f' public byte {var_name}Count;\n'
+ total_size = field.max_size * field.element_size
+ result += f' [FieldOffset({field_offset + 1})]\n'
+ result += f' [MarshalAs(UnmanagedType.ByValArray, SizeConst = {total_size})]\n'
+ result += f' public byte[] {var_name}Data; // Variable string array: up to {field.max_size} strings, each max {field.element_size} chars\n'
+ else:
+ # Non-string arrays
+ if field.size_option is not None:
+ # Fixed array
+ result += f' [FieldOffset({field_offset})]\n'
+ if field.isEnum:
+ result += f' [MarshalAs(UnmanagedType.ByValArray, SizeConst = {field.size_option})]\n'
+ result += f' public byte[] {var_name}; // Fixed array of {base_type}: {field.size_option} elements\n'
+ else:
+ result += f' [MarshalAs(UnmanagedType.ByValArray, SizeConst = {field.size_option})]\n'
+ result += f' public {base_type}[] {var_name}; // Fixed array: {field.size_option} elements\n'
+ elif field.max_size is not None:
+ # Variable array: count byte + max elements
+ result += f' [FieldOffset({field_offset})]\n'
+ result += f' public byte {var_name}Count;\n'
+ result += f' [FieldOffset({field_offset + 1})]\n'
+ if field.isEnum:
+ result += f' [MarshalAs(UnmanagedType.ByValArray, SizeConst = {field.max_size})]\n'
+ result += f' public byte[] {var_name}Data; // Variable array of {base_type}: up to {field.max_size} elements\n'
+ else:
+ result += f' [MarshalAs(UnmanagedType.ByValArray, SizeConst = {field.max_size})]\n'
+ result += f' public {base_type}[] {var_name}Data; // Variable array: up to {field.max_size} elements\n'
+
+ # Handle regular strings
+ elif field.fieldType == "string":
+ if field.size_option is not None:
+ # Fixed string: exactly size_option characters
+ result += f' [FieldOffset({field_offset})]\n'
+ result += f' [MarshalAs(UnmanagedType.ByValArray, SizeConst = {field.size_option})]\n'
+ result += f' public byte[] {var_name}; // Fixed string: exactly {field.size_option} chars\n'
+ elif field.max_size is not None:
+ # Variable string: length byte + max characters
+ result += f' [FieldOffset({field_offset})]\n'
+ result += f' public byte {var_name}Length;\n'
+ result += f' [FieldOffset({field_offset + 1})]\n'
+ result += f' [MarshalAs(UnmanagedType.ByValArray, SizeConst = {field.max_size})]\n'
+ result += f' public byte[] {var_name}Data; // Variable string: up to {field.max_size} chars\n'
+
+ # Handle regular fields
+ else:
+ result += f' [FieldOffset({field_offset})]\n'
+ result += f' public {base_type} {var_name};\n'
+
+ return result
+
+
+class MessageCSharpGen():
+ @staticmethod
+ def generate(msg):
+ leading_comment = msg.comments
+
+ result = ''
+ if leading_comment:
+ for c in msg.comments:
+ result += ' /// \n'
+ result += ' /// %s\n' % c.strip('/')
+ result += ' /// \n'
+
+ structName = '%s%s' % (pascalCase(msg.package), msg.name)
+ defineName = '%s_%s' % (CamelToSnakeCase(
+ msg.package).upper(), CamelToSnakeCase(msg.name).upper())
+
+ result += ' [StructLayout(LayoutKind.Explicit, Pack = 1, Size = %d)]\n' % msg.size
+ result += ' public struct %s\n' % structName
+ result += ' {\n'
+
+ result += ' public const int MaxSize = %d;\n' % msg.size
+ if msg.id:
+ result += ' public const int MsgId = %d;\n' % msg.id
+ result += '\n'
+
+ if not msg.fields:
+ # Empty structs need a dummy field
+ result += ' [FieldOffset(0)]\n'
+ result += ' public byte DummyField;\n'
+ else:
+ # Calculate field offsets
+ offset = 0
+ for key, f in msg.fields.items():
+ result += FieldCSharpGen.generate(f, offset)
+ offset += f.size
+
+ result += ' }\n'
+
+ return result + '\n'
+
+
+class FileCSharpGen():
+ @staticmethod
+ def generate(package):
+ yield '// Automatically generated struct frame code for C#\n'
+ yield '// Generated by %s at %s.\n\n' % (version, time.asctime())
+
+ yield 'using System;\n'
+ yield 'using System.Runtime.InteropServices;\n\n'
+
+ namespace_name = pascalCase(package.name)
+ yield 'namespace StructFrame.%s\n' % namespace_name
+ yield '{\n'
+
+ if package.enums:
+ yield ' // Enum definitions\n'
+ for key, enum in package.enums.items():
+ yield EnumCSharpGen.generate(enum) + '\n'
+
+ if package.messages:
+ yield ' // Struct definitions\n'
+ # Need to sort messages to make sure dependencies are properly met
+ for key, msg in package.sortedMessages().items():
+ yield MessageCSharpGen.generate(msg)
+ yield '\n'
+
+ # Generate helper class with message definitions
+ if package.messages:
+ yield ' public static class MessageDefinitions\n'
+ yield ' {\n'
+ yield ' public static bool GetMessageLength(int msgId, out int size)\n'
+ yield ' {\n'
+ yield ' switch (msgId)\n'
+ yield ' {\n'
+ for key, msg in package.sortedMessages().items():
+ if msg.id:
+ structName = '%s%s' % (pascalCase(msg.package), msg.name)
+ yield ' case %s.MsgId: size = %s.MaxSize; return true;\n' % (structName, structName)
+ yield ' default: size = 0; return false;\n'
+ yield ' }\n'
+ yield ' }\n'
+ yield ' }\n'
+
+ yield '}\n'
diff --git a/src/struct_frame/frame_parser_csharp_gen.py b/src/struct_frame/frame_parser_csharp_gen.py
new file mode 100644
index 00000000..ff3159f7
--- /dev/null
+++ b/src/struct_frame/frame_parser_csharp_gen.py
@@ -0,0 +1,536 @@
+#!/usr/bin/env python3
+# kate: replace-tabs on; indent-width 4;
+
+"""
+C# Frame Parser Generator
+
+Generates C# code for frame parsers based on frame format definitions.
+"""
+
+from struct_frame import version
+import time
+
+
+def camel_to_snake(name):
+ """Convert CamelCase to snake_case"""
+ import re
+ name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
+ name = re.sub('([a-z0-9])([A-Z])', r'\1_\2', name)
+ return name.lower()
+
+
+def camel_to_upper_snake(name):
+ """Convert CamelCase to UPPER_SNAKE_CASE"""
+ return camel_to_snake(name).upper()
+
+
+class FrameParserCSharpGen:
+ """Generates C# code for frame parsers"""
+
+ @staticmethod
+ def generate(formats):
+ """Generate the complete frame parser C# file"""
+ yield '// Automatically generated frame parser for C#\n'
+ yield f'// Generated by {version} at {time.asctime()}.\n\n'
+ yield 'using System;\n'
+ yield 'using System.Collections.Generic;\n\n'
+ yield 'namespace StructFrame\n'
+ yield '{\n'
+
+ # Generate frame format enum
+ yield ' /// \n'
+ yield ' /// Frame format type enumeration\n'
+ yield ' /// \n'
+ yield ' public enum FrameFormatType\n'
+ yield ' {\n'
+ for i, fmt in enumerate(formats):
+ enum_name = camel_to_upper_snake(fmt.name)
+ comma = ',' if i < len(formats) - 1 else ''
+ yield f' {enum_name} = {i}{comma}\n'
+ yield ' }\n\n'
+
+ # Generate individual frame format classes
+ for fmt in formats:
+ yield from FrameParserCSharpGen.generate_format(fmt)
+ yield '\n'
+
+ yield '}\n'
+
+ @staticmethod
+ def generate_format(fmt):
+ """Generate C# code for a single frame format"""
+ name = fmt.name
+ class_name = name
+
+ yield f' /// \n'
+ yield f' /// {name} - Frame format parser and encoder\n'
+ yield f' /// \n'
+ yield f' public class {class_name} : FrameFormatBase\n'
+ yield f' {{\n'
+
+ # Generate parser state enum
+ yield f' public enum ParserState\n'
+ yield f' {{\n'
+ state_idx = 0
+ if fmt.start_bytes:
+ for i in range(len(fmt.start_bytes)):
+ state_name = f'LookingForStart{i + 1}' if len(fmt.start_bytes) > 1 else 'LookingForStart'
+ comma = ','
+ yield f' {state_name} = {state_idx}{comma}\n'
+ state_idx += 1
+ yield f' GettingMsgId = {state_idx},\n'
+ state_idx += 1
+ if fmt.has_length:
+ yield f' GettingLength = {state_idx},\n'
+ state_idx += 1
+ yield f' GettingPayload = {state_idx}\n'
+ yield f' }}\n\n'
+
+ # Constants
+ for i, (sb_name, sb_value) in enumerate(fmt.start_bytes):
+ const_name = f'StartByte{i + 1}' if len(fmt.start_bytes) > 1 else 'StartByte'
+ yield f' public const byte {const_name} = 0x{sb_value:02X};\n'
+ yield f' public const int HeaderSize = {fmt.header_size};\n'
+ yield f' public const int FooterSize = {fmt.footer_size};\n'
+ yield f' public const int Overhead = {fmt.header_size + fmt.footer_size};\n'
+ if fmt.has_length:
+ yield f' public const int LengthBytes = {fmt.length_bytes};\n'
+ yield '\n'
+
+ # Instance variables
+ yield f' private ParserState _state;\n'
+ yield f' private List _buffer;\n'
+ yield f' private int _packetSize;\n'
+ yield f' private int _msgId;\n'
+ if fmt.has_length:
+ yield f' private int _msgLength;\n'
+ if fmt.length_bytes == 2:
+ yield f' private int _lengthLo;\n'
+ yield f' private Func _getMsgLength;\n\n'
+
+ # Constructor
+ yield f' /// \n'
+ yield f' /// Initialize the {name} parser\n'
+ yield f' /// \n'
+ yield f' /// Callback function to get message length from msg_id\n'
+ yield f' public {class_name}(Func getMsgLength = null)\n'
+ yield f' {{\n'
+ yield f' _getMsgLength = getMsgLength;\n'
+ yield f' _buffer = new List();\n'
+ yield f' Reset();\n'
+ yield f' }}\n\n'
+
+ # Reset method
+ yield f' /// \n'
+ yield f' /// Reset parser state\n'
+ yield f' /// \n'
+ yield f' public override void Reset()\n'
+ yield f' {{\n'
+ if fmt.start_bytes:
+ state_name = 'LookingForStart1' if len(fmt.start_bytes) > 1 else 'LookingForStart'
+ yield f' _state = ParserState.{state_name};\n'
+ else:
+ yield f' _state = ParserState.GettingMsgId;\n'
+ yield f' _buffer.Clear();\n'
+ yield f' _packetSize = 0;\n'
+ yield f' _msgId = 0;\n'
+ if fmt.has_length:
+ yield f' _msgLength = 0;\n'
+ if fmt.length_bytes == 2:
+ yield f' _lengthLo = 0;\n'
+ yield f' }}\n\n'
+
+ # ParseByte method
+ yield f' /// \n'
+ yield f' /// Parse a single byte\n'
+ yield f' /// \n'
+ yield f' /// The byte to parse\n'
+ yield f' /// FrameParseResult with Valid=true when a complete valid message is received\n'
+ yield f' public override FrameParseResult ParseByte(byte b)\n'
+ yield f' {{\n'
+ yield f' var result = new FrameParseResult();\n\n'
+
+ # State machine
+ yield f' switch (_state)\n'
+ yield f' {{\n'
+
+ if fmt.start_bytes:
+ for i, (sb_name, sb_value) in enumerate(fmt.start_bytes):
+ state_name = f'LookingForStart{i + 1}' if len(fmt.start_bytes) > 1 else 'LookingForStart'
+ const_name = f'StartByte{i + 1}' if len(fmt.start_bytes) > 1 else 'StartByte'
+
+ yield f' case ParserState.{state_name}:\n'
+ yield f' if (b == {const_name})\n'
+ yield f' {{\n'
+
+ if i == 0:
+ yield f' _buffer.Clear();\n'
+ yield f' _buffer.Add(b);\n'
+ else:
+ yield f' _buffer.Add(b);\n'
+
+ if i + 1 < len(fmt.start_bytes):
+ next_state = f'LookingForStart{i + 2}'
+ else:
+ next_state = 'GettingMsgId'
+ yield f' _state = ParserState.{next_state};\n'
+ yield f' }}\n'
+
+ if i > 0:
+ yield f' else if (b == StartByte1)\n'
+ yield f' {{\n'
+ yield f' _buffer.Clear();\n'
+ yield f' _buffer.Add(b);\n'
+ yield f' _state = ParserState.LookingForStart2;\n'
+ yield f' }}\n'
+ yield f' else\n'
+ yield f' {{\n'
+ yield f' _state = ParserState.LookingForStart1;\n'
+ yield f' }}\n'
+ yield f' break;\n\n'
+
+ # GETTING_MSG_ID state
+ yield f' case ParserState.GettingMsgId:\n'
+ yield f' _buffer.Add(b);\n'
+ yield f' _msgId = b;\n'
+
+ if fmt.has_length:
+ yield f' _state = ParserState.GettingLength;\n'
+ else:
+ yield f' if (_getMsgLength != null)\n'
+ yield f' {{\n'
+ yield f' int? msgLength = _getMsgLength(b);\n'
+ yield f' if (msgLength.HasValue)\n'
+ yield f' {{\n'
+ yield f' _packetSize = Overhead + msgLength.Value;\n'
+ yield f' _state = ParserState.GettingPayload;\n'
+ yield f' }}\n'
+ yield f' else\n'
+ yield f' {{\n'
+ reset_state = 'LookingForStart1' if len(fmt.start_bytes) > 1 else ('LookingForStart' if fmt.start_bytes else 'GettingMsgId')
+ yield f' _state = ParserState.{reset_state};\n'
+ yield f' }}\n'
+ yield f' }}\n'
+ yield f' else\n'
+ yield f' {{\n'
+ yield f' _state = ParserState.{reset_state};\n'
+ yield f' }}\n'
+ yield f' break;\n\n'
+
+ # GETTING_LENGTH state (if applicable)
+ if fmt.has_length:
+ yield f' case ParserState.GettingLength:\n'
+ yield f' _buffer.Add(b);\n'
+ if fmt.length_bytes == 1:
+ yield f' _msgLength = b;\n'
+ yield f' _packetSize = Overhead + _msgLength;\n'
+ yield f' _state = ParserState.GettingPayload;\n'
+ else:
+ yield f' if (_buffer.Count == {len(fmt.start_bytes) + 2})\n'
+ yield f' {{\n'
+ yield f' _lengthLo = b;\n'
+ yield f' }}\n'
+ yield f' else\n'
+ yield f' {{\n'
+ yield f' _msgLength = _lengthLo | (b << 8);\n'
+ yield f' _packetSize = Overhead + _msgLength;\n'
+ yield f' _state = ParserState.GettingPayload;\n'
+ yield f' }}\n'
+ yield f' break;\n\n'
+
+ # GETTING_PAYLOAD state
+ yield f' case ParserState.GettingPayload:\n'
+ yield f' _buffer.Add(b);\n'
+ yield f' if (_buffer.Count >= _packetSize)\n'
+ yield f' {{\n'
+ yield f' result = ValidatePacket(_buffer.ToArray(), _buffer.Count);\n'
+ reset_state = 'LookingForStart1' if len(fmt.start_bytes) > 1 else ('LookingForStart' if fmt.start_bytes else 'GettingMsgId')
+ yield f' _state = ParserState.{reset_state};\n'
+ yield f' }}\n'
+ yield f' break;\n'
+
+ yield f' }}\n\n'
+ yield f' return result;\n'
+ yield f' }}\n\n'
+
+ # Encode method
+ yield f' /// \n'
+ yield f' /// Encode a message with {name} format\n'
+ yield f' /// \n'
+ yield f' /// Message ID\n'
+ yield f' /// Message data bytes\n'
+ yield f' /// Encoded frame as byte array\n'
+ yield f' public override byte[] Encode(int msgId, byte[] msgData)\n'
+ yield f' {{\n'
+ yield f' var output = new List();\n'
+
+ # Write start bytes
+ for i, (sb_name, sb_value) in enumerate(fmt.start_bytes):
+ const_name = f'StartByte{i + 1}' if len(fmt.start_bytes) > 1 else 'StartByte'
+ yield f' output.Add({const_name});\n'
+
+ if fmt.has_length and fmt.has_crc:
+ # Default format with length and CRC
+ if fmt.length_bytes == 1:
+ yield f' output.Add((byte)(msgData.Length & 0xFF));\n'
+ else:
+ yield f' output.Add((byte)(msgData.Length & 0xFF));\n'
+ yield f' output.Add((byte)((msgData.Length >> 8) & 0xFF));\n'
+ yield f' output.Add((byte)msgId);\n'
+ yield f' output.AddRange(msgData);\n\n'
+ yield f' // Calculate Fletcher checksum\n'
+ yield f' var ck = FletcherChecksum(output.ToArray(), {len(fmt.start_bytes)}, output.Count);\n'
+ yield f' output.Add(ck.Item1);\n'
+ yield f' output.Add(ck.Item2);\n'
+ elif not fmt.has_length and not fmt.has_crc:
+ # Minimal format
+ yield f' output.Add((byte)msgId);\n'
+ yield f' output.AddRange(msgData);\n'
+ else:
+ # Other formats
+ yield f' output.Add((byte)msgId);\n'
+ if fmt.has_length:
+ if fmt.length_bytes == 1:
+ yield f' output.Add((byte)(msgData.Length & 0xFF));\n'
+ else:
+ yield f' output.Add((byte)(msgData.Length & 0xFF));\n'
+ yield f' output.Add((byte)((msgData.Length >> 8) & 0xFF));\n'
+ yield f' output.AddRange(msgData);\n'
+ if fmt.has_crc:
+ yield f' var ck = FletcherChecksum(output.ToArray(), {len(fmt.start_bytes)}, output.Count);\n'
+ yield f' output.Add(ck.Item1);\n'
+ yield f' output.Add(ck.Item2);\n'
+
+ yield f' return output.ToArray();\n'
+ yield f' }}\n\n'
+
+ # ValidatePacket method
+ yield f' /// \n'
+ yield f' /// Validate a complete {name} packet in a buffer\n'
+ yield f' /// \n'
+ yield f' /// Buffer containing the complete packet\n'
+ yield f' /// Length of the data in the buffer\n'
+ yield f' /// FrameParseResult with Valid=true if packet is valid\n'
+ yield f' public override FrameParseResult ValidatePacket(byte[] data, int length)\n'
+ yield f' {{\n'
+ yield f' var result = new FrameParseResult();\n\n'
+ yield f' if (length < Overhead)\n'
+ yield f' return result;\n\n'
+
+ # Check start bytes
+ for i, (sb_name, sb_value) in enumerate(fmt.start_bytes):
+ const_name = f'StartByte{i + 1}' if len(fmt.start_bytes) > 1 else 'StartByte'
+ yield f' if (data[{i}] != {const_name})\n'
+ yield f' return result;\n'
+
+ yield f'\n'
+
+ if fmt.has_crc:
+ yield f' int msgLength = length - Overhead;\n'
+ if fmt.has_length:
+ yield f' int crcDataLen = msgLength + 1 + LengthBytes;\n'
+ else:
+ yield f' int crcDataLen = msgLength + 1;\n'
+ yield f' var ck = FletcherChecksum(data, {len(fmt.start_bytes)}, {len(fmt.start_bytes)} + crcDataLen);\n\n'
+ yield f' if (ck.Item1 == data[length - 2] && ck.Item2 == data[length - 1])\n'
+ yield f' {{\n'
+ yield f' result.Valid = true;\n'
+ yield f' result.MsgId = data[HeaderSize - 1];\n'
+ yield f' result.MsgSize = msgLength;\n'
+ yield f' result.MsgData = new byte[msgLength];\n'
+ yield f' Array.Copy(data, HeaderSize, result.MsgData, 0, msgLength);\n'
+ yield f' }}\n'
+ else:
+ yield f' result.Valid = true;\n'
+ yield f' result.MsgId = data[HeaderSize - 1];\n'
+ yield f' result.MsgSize = length - HeaderSize;\n'
+ yield f' result.MsgData = new byte[result.MsgSize];\n'
+ yield f' Array.Copy(data, HeaderSize, result.MsgData, 0, result.MsgSize);\n'
+
+ yield f' return result;\n'
+ yield f' }}\n\n'
+
+ # Fletcher checksum helper
+ yield f' /// \n'
+ yield f' /// Calculate Fletcher-16 checksum\n'
+ yield f' /// \n'
+ yield f' private static (byte, byte) FletcherChecksum(byte[] buffer, int start, int end)\n'
+ yield f' {{\n'
+ yield f' byte byte1 = 0;\n'
+ yield f' byte byte2 = 0;\n\n'
+ yield f' for (int i = start; i < end; i++)\n'
+ yield f' {{\n'
+ yield f' byte1 = (byte)((byte1 + buffer[i]) % 256);\n'
+ yield f' byte2 = (byte)((byte2 + byte1) % 256);\n'
+ yield f' }}\n\n'
+ yield f' return (byte1, byte2);\n'
+ yield f' }}\n'
+
+ yield f' }}\n'
+
+ @staticmethod
+ def generate_base():
+ """Generate the base C# file with FrameParseResult, FrameFormatBase, StructHelper"""
+ yield '// Automatically generated frame base for C#\n'
+ yield f'// Generated by {version} at {time.asctime()}.\n'
+ yield '// This file provides base utilities for struct frame parsing and encoding\n\n'
+ yield 'using System;\n'
+ yield 'using System.Runtime.InteropServices;\n\n'
+ yield 'namespace StructFrame\n'
+ yield '{\n'
+
+ # FrameParseResult struct
+ yield ' /// \n'
+ yield ' /// Represents the result of parsing a frame\n'
+ yield ' /// \n'
+ yield ' public struct FrameParseResult\n'
+ yield ' {\n'
+ yield ' public bool Valid;\n'
+ yield ' public int MsgId;\n'
+ yield ' public byte[] MsgData;\n'
+ yield ' public int MsgSize;\n'
+ yield ' }\n\n'
+
+ # FrameFormatBase abstract class
+ yield ' /// \n'
+ yield ' /// Base class for frame format implementations\n'
+ yield ' /// \n'
+ yield ' public abstract class FrameFormatBase\n'
+ yield ' {\n'
+ yield ' /// \n'
+ yield ' /// Encodes a message into a byte array\n'
+ yield ' /// \n'
+ yield ' public abstract byte[] Encode(int msgId, byte[] msgData);\n\n'
+ yield ' /// \n'
+ yield ' /// Validates and parses a packet\n'
+ yield ' /// \n'
+ yield ' public abstract FrameParseResult ValidatePacket(byte[] data, int length);\n\n'
+ yield ' /// \n'
+ yield ' /// Parses a single byte (for streaming parsers)\n'
+ yield ' /// \n'
+ yield ' public abstract FrameParseResult ParseByte(byte b);\n\n'
+ yield ' /// \n'
+ yield ' /// Resets the parser state\n'
+ yield ' /// \n'
+ yield ' public abstract void Reset();\n'
+ yield ' }\n\n'
+
+ # StructHelper static class
+ yield ' /// \n'
+ yield ' /// Helper methods for struct serialization\n'
+ yield ' /// \n'
+ yield ' public static class StructHelper\n'
+ yield ' {\n'
+ yield ' /// \n'
+ yield ' /// Converts a struct to a byte array\n'
+ yield ' /// \n'
+ yield ' public static byte[] StructToBytes(T structure) where T : struct\n'
+ yield ' {\n'
+ yield ' int size = Marshal.SizeOf(typeof(T));\n'
+ yield ' byte[] buffer = new byte[size];\n'
+ yield ' IntPtr ptr = Marshal.AllocHGlobal(size);\n'
+ yield ' try\n'
+ yield ' {\n'
+ yield ' Marshal.StructureToPtr(structure, ptr, false);\n'
+ yield ' Marshal.Copy(ptr, buffer, 0, size);\n'
+ yield ' }\n'
+ yield ' finally\n'
+ yield ' {\n'
+ yield ' Marshal.FreeHGlobal(ptr);\n'
+ yield ' }\n'
+ yield ' return buffer;\n'
+ yield ' }\n\n'
+ yield ' /// \n'
+ yield ' /// Converts a byte array to a struct\n'
+ yield ' /// \n'
+ yield ' public static T BytesToStruct(byte[] buffer) where T : struct\n'
+ yield ' {\n'
+ yield ' return BytesToStruct(buffer, 0);\n'
+ yield ' }\n\n'
+ yield ' /// \n'
+ yield ' /// Converts a byte array to a struct starting at an offset\n'
+ yield ' /// \n'
+ yield ' public static T BytesToStruct(byte[] buffer, int offset) where T : struct\n'
+ yield ' {\n'
+ yield ' int size = Marshal.SizeOf(typeof(T));\n'
+ yield ' IntPtr ptr = Marshal.AllocHGlobal(size);\n'
+ yield ' try\n'
+ yield ' {\n'
+ yield ' Marshal.Copy(buffer, offset, ptr, size);\n'
+ yield ' return Marshal.PtrToStructure(ptr);\n'
+ yield ' }\n'
+ yield ' finally\n'
+ yield ' {\n'
+ yield ' Marshal.FreeHGlobal(ptr);\n'
+ yield ' }\n'
+ yield ' }\n'
+ yield ' }\n'
+
+ yield '}\n'
+
+ @staticmethod
+ def generate_format_file(fmt):
+ """Generate a single frame format C# file"""
+ yield '// Automatically generated frame parser for C#\n'
+ yield f'// Generated by {version} at {time.asctime()}.\n\n'
+ yield 'using System;\n'
+ yield 'using System.Collections.Generic;\n\n'
+ yield 'namespace StructFrame\n'
+ yield '{\n'
+
+ # Generate the frame format class
+ yield from FrameParserCSharpGen.generate_format(fmt)
+
+ yield '}\n'
+
+ @staticmethod
+ def generate_main(formats):
+ """Generate the main FrameParsers.cs file with the frame format enum"""
+ yield '// Automatically generated frame parsers for C#\n'
+ yield f'// Generated by {version} at {time.asctime()}.\n\n'
+ yield 'namespace StructFrame\n'
+ yield '{\n'
+
+ # Generate frame format enum
+ yield ' /// \n'
+ yield ' /// Frame format type enumeration\n'
+ yield ' /// \n'
+ yield ' public enum FrameFormatType\n'
+ yield ' {\n'
+ for i, fmt in enumerate(formats):
+ enum_name = camel_to_upper_snake(fmt.name)
+ comma = ',' if i < len(formats) - 1 else ''
+ yield f' {enum_name} = {i}{comma}\n'
+ yield ' }\n'
+
+ yield '}\n'
+
+
+def generate_csharp_frame_parsers(formats):
+ """Generate C# frame parser code from frame format definitions"""
+ return ''.join(FrameParserCSharpGen.generate(formats))
+
+
+def generate_csharp_frame_parsers_multi(formats):
+ """
+ Generate multiple C# files for frame parsers.
+
+ Returns a dictionary mapping filename to content:
+ - FrameBase.cs: Base utilities (FrameParseResult, FrameFormatBase, StructHelper)
+ - {PascalName}.cs: Individual frame format files
+ - FrameParsers.cs: Main file that includes all parsers
+ """
+ files = {}
+
+ # Generate base file with FrameParseResult, FrameFormatBase, StructHelper
+ files['FrameBase.cs'] = ''.join(FrameParserCSharpGen.generate_base())
+
+ # Generate individual frame format files
+ for fmt in formats:
+ pascal_name = fmt.name # Already PascalCase
+ files[f'{pascal_name}.cs'] = ''.join(FrameParserCSharpGen.generate_format_file(fmt))
+
+ # Generate main file that includes frame format enum
+ files['FrameParsers.cs'] = ''.join(FrameParserCSharpGen.generate_main(formats))
+
+ return files
diff --git a/src/struct_frame/generate.py b/src/struct_frame/generate.py
index 111dd93a..180e8b18 100644
--- a/src/struct_frame/generate.py
+++ b/src/struct_frame/generate.py
@@ -10,6 +10,7 @@
from struct_frame import FilePyGen
from struct_frame import FileGqlGen
from struct_frame import FileCppGen
+from struct_frame import FileCSharpGen
from proto_schema_parser.parser import Parser
from proto_schema_parser import ast
from proto_schema_parser.ast import FieldCardinality
@@ -457,12 +458,15 @@ def __str__(self):
parser.add_argument('--build_js', action='store_true')
parser.add_argument('--build_py', action='store_true')
parser.add_argument('--build_cpp', action='store_true')
+parser.add_argument('--build_csharp', action='store_true')
parser.add_argument('--c_path', nargs=1, type=str, default=['generated/c/'])
parser.add_argument('--ts_path', nargs=1, type=str, default=['generated/ts/'])
parser.add_argument('--js_path', nargs=1, type=str, default=['generated/js/'])
parser.add_argument('--py_path', nargs=1, type=str, default=['generated/py/'])
parser.add_argument('--cpp_path', nargs=1, type=str,
default=['generated/cpp/'])
+parser.add_argument('--csharp_path', nargs=1, type=str,
+ default=['generated/csharp/'])
parser.add_argument('--build_gql', action='store_true')
parser.add_argument('--gql_path', nargs=1, type=str,
default=['generated/gql/'])
@@ -572,14 +576,24 @@ def generateCppFileStrings(path):
return out
-def generateFrameParserFiles(frame_formats_file, c_path, ts_path, js_path, py_path, cpp_path,
- build_c, build_ts, build_js, build_py, build_cpp):
+def generateCSharpFileStrings(path):
+ out = {}
+ for key, value in packages.items():
+ name = os.path.join(path, value.name + ".sf.cs")
+ data = ''.join(FileCSharpGen.generate(value))
+ out[name] = data
+ return out
+
+
+def generateFrameParserFiles(frame_formats_file, c_path, ts_path, js_path, py_path, cpp_path, csharp_path,
+ build_c, build_ts, build_js, build_py, build_cpp, build_csharp):
"""Generate frame parser files from frame format definitions"""
from struct_frame.frame_format import parse_frame_formats
from struct_frame.frame_parser_c_gen import generate_c_frame_parsers
from struct_frame.frame_parser_py_gen import generate_py_frame_parsers
from struct_frame.frame_parser_ts_gen import generate_ts_frame_parsers, generate_js_frame_parsers
from struct_frame.frame_parser_cpp_gen import generate_cpp_frame_parsers
+ from struct_frame.frame_parser_csharp_gen import generate_csharp_frame_parsers
formats = parse_frame_formats(frame_formats_file)
files = {}
@@ -604,6 +618,10 @@ def generateFrameParserFiles(frame_formats_file, c_path, ts_path, js_path, py_pa
name = os.path.join(cpp_path, "frame_parsers_gen.hpp")
files[name] = generate_cpp_frame_parsers(formats)
+ if build_csharp:
+ name = os.path.join(csharp_path, "FrameParsersGen.cs")
+ files[name] = generate_csharp_frame_parsers(formats)
+
return files
@@ -660,7 +678,7 @@ def main():
# If validate mode is specified, skip build argument check and file generation
if args.validate:
print("Running in validate mode - no files will be generated")
- elif (not args.build_c and not args.build_ts and not args.build_js and not args.build_py and not args.build_cpp and not args.build_gql):
+ elif (not args.build_c and not args.build_ts and not args.build_js and not args.build_py and not args.build_cpp and not args.build_csharp and not args.build_gql):
print("Select at least one build argument")
return
@@ -700,6 +718,9 @@ def main():
if (args.build_cpp):
files.update(generateCppFileStrings(args.cpp_path[0]))
+ if (args.build_csharp):
+ files.update(generateCSharpFileStrings(args.csharp_path[0]))
+
if (args.build_gql):
for key, value in packages.items():
name = os.path.join(args.gql_path[0], value.name + '.graphql')
@@ -711,9 +732,9 @@ def main():
frame_parser_files = generateFrameParserFiles(
args.frame_formats[0],
args.c_path[0], args.ts_path[0], args.js_path[0],
- args.py_path[0], args.cpp_path[0],
+ args.py_path[0], args.cpp_path[0], args.csharp_path[0],
args.build_c, args.build_ts, args.build_js,
- args.build_py, args.build_cpp
+ args.build_py, args.build_cpp, args.build_csharp
)
files.update(frame_parser_files)
@@ -738,7 +759,8 @@ def main():
'cpp': ['frame_base.hpp'],
'ts': ['struct_base.ts', 'struct_frame.ts', 'struct_frame_types.ts', 'frame_base.ts'],
'js': ['struct_base.js', 'struct_frame.js', 'struct_frame_types.js', 'frame_base.js'],
- 'py': [] # __init__.py will be generated with the custom frame formats
+ 'py': [], # __init__.py will be generated with the custom frame formats
+ 'csharp': ['FrameBase.cs']
}
def copy_files_list(src_dir, dst_dir, files_to_copy):
@@ -789,6 +811,11 @@ def copy_all_files(src_dir, dst_dir):
copy_files_list(
os.path.join(dir_path, "boilerplate/cpp"),
args.cpp_path[0], utility_files['cpp'])
+
+ if args.build_csharp:
+ copy_files_list(
+ os.path.join(dir_path, "boilerplate/csharp"),
+ args.csharp_path[0], utility_files['csharp'])
else:
# Copy all boilerplate files (multi-file structure)
if (args.build_c):
@@ -816,6 +843,11 @@ def copy_all_files(src_dir, dst_dir):
os.path.join(dir_path, "boilerplate/cpp"),
args.cpp_path[0])
+ if (args.build_csharp):
+ copy_all_files(
+ os.path.join(dir_path, "boilerplate/csharp"),
+ args.csharp_path[0])
+
# No boilerplate for GraphQL currently
if args.debug:
diff --git a/src/struct_frame/generate_boilerplate.py b/src/struct_frame/generate_boilerplate.py
index 15e3c3c4..9281e501 100644
--- a/src/struct_frame/generate_boilerplate.py
+++ b/src/struct_frame/generate_boilerplate.py
@@ -37,6 +37,7 @@
from struct_frame.frame_parser_ts_gen import (generate_ts_frame_parsers, generate_js_frame_parsers,
generate_ts_frame_parsers_multi, generate_js_frame_parsers_multi)
from struct_frame.frame_parser_cpp_gen import generate_cpp_frame_parsers, generate_cpp_frame_parsers_multi
+from struct_frame.frame_parser_csharp_gen import generate_csharp_frame_parsers, generate_csharp_frame_parsers_multi
def get_default_frame_formats_path():
@@ -63,7 +64,7 @@ def write_file(path, content):
def generate_boilerplate_to_paths(frame_formats_file, c_path=None, ts_path=None,
js_path=None, py_path=None, cpp_path=None,
- multi_file=True):
+ csharp_path=None, multi_file=True):
"""
Generate frame parser boilerplate code from a frame_formats.proto file.
@@ -74,6 +75,7 @@ def generate_boilerplate_to_paths(frame_formats_file, c_path=None, ts_path=None,
js_path: Output directory for JavaScript code (if None, skip JS generation)
py_path: Output directory for Python code (if None, skip Python generation)
cpp_path: Output directory for C++ code (if None, skip C++ generation)
+ csharp_path: Output directory for C# code (if None, skip C# generation)
multi_file: If True, generate multiple files per language (default: True)
Returns:
@@ -127,6 +129,15 @@ def generate_boilerplate_to_paths(frame_formats_file, c_path=None, ts_path=None,
name = os.path.join(cpp_path, "frame_parsers_gen.hpp")
files[name] = generate_cpp_frame_parsers(formats)
+ if csharp_path:
+ if multi_file:
+ csharp_files = generate_csharp_frame_parsers_multi(formats)
+ for filename, content in csharp_files.items():
+ files[os.path.join(csharp_path, filename)] = content
+ else:
+ name = os.path.join(csharp_path, "FrameParsersGen.cs")
+ files[name] = generate_csharp_frame_parsers(formats)
+
return files
@@ -155,7 +166,8 @@ def update_src_boilerplate():
ts_path=os.path.join(boilerplate_dir, 'ts'),
js_path=os.path.join(boilerplate_dir, 'js'),
py_path=os.path.join(boilerplate_dir, 'py'),
- cpp_path=os.path.join(boilerplate_dir, 'cpp')
+ cpp_path=os.path.join(boilerplate_dir, 'cpp'),
+ csharp_path=os.path.join(boilerplate_dir, 'csharp')
)
for path, content in files.items():
@@ -179,9 +191,9 @@ def generate_to_custom_paths(args):
return False
# Check that at least one output path is specified
- if not any([args.c_path, args.ts_path, args.js_path, args.py_path, args.cpp_path]):
+ if not any([args.c_path, args.ts_path, args.js_path, args.py_path, args.cpp_path, args.csharp_path]):
print("Error: At least one output path must be specified")
- print("Use --c_path, --ts_path, --js_path, --py_path, or --cpp_path")
+ print("Use --c_path, --ts_path, --js_path, --py_path, --cpp_path, or --csharp_path")
return False
print(f"Generating boilerplate from: {frame_formats_file}")
@@ -192,7 +204,8 @@ def generate_to_custom_paths(args):
ts_path=args.ts_path[0] if args.ts_path else None,
js_path=args.js_path[0] if args.js_path else None,
py_path=args.py_path[0] if args.py_path else None,
- cpp_path=args.cpp_path[0] if args.cpp_path else None
+ cpp_path=args.cpp_path[0] if args.cpp_path else None,
+ csharp_path=args.csharp_path[0] if args.csharp_path else None
)
for path, content in files.items():
@@ -218,7 +231,7 @@ def main():
# Generate boilerplate for all languages
python generate_boilerplate.py --frame_formats my_formats.proto \\
--c_path output/c --ts_path output/ts --js_path output/js \\
- --py_path output/py --cpp_path output/cpp
+ --py_path output/py --cpp_path output/cpp --csharp_path output/csharp
"""
)
@@ -244,6 +257,9 @@ def main():
parser.add_argument('--cpp_path', nargs=1, type=str,
help='Output directory for C++ frame parser code')
+ parser.add_argument('--csharp_path', nargs=1, type=str,
+ help='Output directory for C# frame parser code')
+
args = parser.parse_args()
# If --update-src is specified, update the boilerplate in the src folder
diff --git a/tests/csharp/StructFrameTests.csproj b/tests/csharp/StructFrameTests.csproj
new file mode 100644
index 00000000..87f3a726
--- /dev/null
+++ b/tests/csharp/StructFrameTests.csproj
@@ -0,0 +1,9 @@
+
+
+ net8.0
+ Exe
+ disable
+ disable
+ true
+
+
diff --git a/tests/csharp/test_arrays.cs b/tests/csharp/test_arrays.cs
new file mode 100644
index 00000000..63260808
--- /dev/null
+++ b/tests/csharp/test_arrays.cs
@@ -0,0 +1,204 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace StructFrameTests
+{
+ class TestArrays
+ {
+ static void PrintFailureDetails(string label, string expected = null, string actual = null)
+ {
+ Console.WriteLine();
+ Console.WriteLine("============================================================");
+ Console.WriteLine($"FAILURE DETAILS: {label}");
+ Console.WriteLine("============================================================");
+
+ if (expected != null)
+ {
+ Console.WriteLine($"\nExpected: {expected}");
+ }
+ if (actual != null)
+ {
+ Console.WriteLine($"Actual: {actual}");
+ }
+
+ Console.WriteLine("============================================================");
+ Console.WriteLine();
+ }
+
+ static bool TestArrayOperations()
+ {
+ try
+ {
+ // Create a new message instance
+ var msg = new StructFrame.ComprehensiveArrays.ComprehensiveArraysComprehensiveArrayMessage();
+
+ // Initialize arrays
+ msg.FixedInts = new int[3];
+ msg.FixedFloats = new float[2];
+ msg.FixedBools = new bool[4];
+
+ // Set fixed array values
+ msg.FixedInts[0] = 1;
+ msg.FixedInts[1] = 2;
+ msg.FixedInts[2] = 3;
+
+ msg.FixedFloats[0] = 1.1f;
+ msg.FixedFloats[1] = 2.2f;
+
+ msg.FixedBools[0] = true;
+ msg.FixedBools[1] = false;
+ msg.FixedBools[2] = true;
+ msg.FixedBools[3] = false;
+
+ // Set bounded array values
+ msg.BoundedUintsCount = 3;
+ msg.BoundedUintsData = new ushort[3];
+ msg.BoundedUintsData[0] = 100;
+ msg.BoundedUintsData[1] = 200;
+ msg.BoundedUintsData[2] = 300;
+
+ msg.BoundedDoublesCount = 2;
+ msg.BoundedDoublesData = new double[2];
+ msg.BoundedDoublesData[0] = 1.5;
+ msg.BoundedDoublesData[1] = 2.5;
+
+ // Initialize string arrays as byte arrays
+ msg.FixedStrings = new byte[2 * 8]; // 2 strings * 8 chars
+ var str1 = System.Text.Encoding.ASCII.GetBytes("Hello");
+ var str2 = System.Text.Encoding.ASCII.GetBytes("World");
+ Array.Copy(str1, 0, msg.FixedStrings, 0, Math.Min(str1.Length, 8));
+ Array.Copy(str2, 0, msg.FixedStrings, 8, Math.Min(str2.Length, 8));
+
+ msg.BoundedStringsCount = 2;
+ msg.BoundedStringsData = new byte[2 * 12]; // 2 strings * 12 chars
+ var bstr1 = System.Text.Encoding.ASCII.GetBytes("Test1");
+ var bstr2 = System.Text.Encoding.ASCII.GetBytes("Test2");
+ Array.Copy(bstr1, 0, msg.BoundedStringsData, 0, Math.Min(bstr1.Length, 12));
+ Array.Copy(bstr2, 0, msg.BoundedStringsData, 12, Math.Min(bstr2.Length, 12));
+
+ // Initialize enum arrays
+ msg.FixedStatuses = new byte[2];
+ msg.FixedStatuses[0] = (byte)StructFrame.ComprehensiveArrays.ComprehensiveArraysStatus.ACTIVE;
+ msg.FixedStatuses[1] = (byte)StructFrame.ComprehensiveArrays.ComprehensiveArraysStatus.INACTIVE;
+
+ msg.BoundedStatusesCount = 2;
+ msg.BoundedStatusesData = new byte[2];
+ msg.BoundedStatusesData[0] = (byte)StructFrame.ComprehensiveArrays.ComprehensiveArraysStatus.ERROR;
+ msg.BoundedStatusesData[1] = (byte)StructFrame.ComprehensiveArrays.ComprehensiveArraysStatus.MAINTENANCE;
+
+ // Initialize nested message arrays
+ msg.FixedSensors = new StructFrame.ComprehensiveArrays.ComprehensiveArraysSensor[1];
+ msg.FixedSensors[0] = new StructFrame.ComprehensiveArrays.ComprehensiveArraysSensor();
+ msg.FixedSensors[0].Id = 1;
+ msg.FixedSensors[0].Value = 25.5f;
+ msg.FixedSensors[0].Status = StructFrame.ComprehensiveArrays.ComprehensiveArraysStatus.ACTIVE;
+ msg.FixedSensors[0].Name = new byte[16];
+ var sensorName = System.Text.Encoding.ASCII.GetBytes("Sensor1");
+ Array.Copy(sensorName, msg.FixedSensors[0].Name, Math.Min(sensorName.Length, 16));
+
+ msg.BoundedSensorsCount = 1;
+ msg.BoundedSensorsData = new StructFrame.ComprehensiveArrays.ComprehensiveArraysSensor[1];
+ msg.BoundedSensorsData[0] = new StructFrame.ComprehensiveArrays.ComprehensiveArraysSensor();
+ msg.BoundedSensorsData[0].Id = 2;
+ msg.BoundedSensorsData[0].Value = 30.0f;
+ msg.BoundedSensorsData[0].Status = StructFrame.ComprehensiveArrays.ComprehensiveArraysStatus.MAINTENANCE;
+ msg.BoundedSensorsData[0].Name = new byte[16];
+ var sensorName2 = System.Text.Encoding.ASCII.GetBytes("Sensor2");
+ Array.Copy(sensorName2, msg.BoundedSensorsData[0].Name, Math.Min(sensorName2.Length, 16));
+
+ // Verify struct size matches expected
+ int size = Marshal.SizeOf(typeof(StructFrame.ComprehensiveArrays.ComprehensiveArraysComprehensiveArrayMessage));
+ if (size != StructFrame.ComprehensiveArrays.ComprehensiveArraysComprehensiveArrayMessage.MaxSize)
+ {
+ PrintFailureDetails("Struct size mismatch",
+ StructFrame.ComprehensiveArrays.ComprehensiveArraysComprehensiveArrayMessage.MaxSize.ToString(),
+ size.ToString());
+ return false;
+ }
+
+ // Serialize to bytes
+ byte[] buffer = new byte[size];
+ IntPtr ptr = Marshal.AllocHGlobal(size);
+ try
+ {
+ Marshal.StructureToPtr(msg, ptr, false);
+ Marshal.Copy(ptr, buffer, 0, size);
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(ptr);
+ }
+
+ // Deserialize back and verify
+ ptr = Marshal.AllocHGlobal(size);
+ try
+ {
+ Marshal.Copy(buffer, 0, ptr, size);
+ var decoded = Marshal.PtrToStructure(ptr);
+
+ // Verify fixed int array
+ if (decoded.FixedInts[0] != 1 || decoded.FixedInts[1] != 2 || decoded.FixedInts[2] != 3)
+ {
+ PrintFailureDetails("FixedInts mismatch",
+ "1, 2, 3",
+ $"{decoded.FixedInts[0]}, {decoded.FixedInts[1]}, {decoded.FixedInts[2]}");
+ return false;
+ }
+
+ // Verify fixed float array
+ if (Math.Abs(decoded.FixedFloats[0] - 1.1f) > 0.01f ||
+ Math.Abs(decoded.FixedFloats[1] - 2.2f) > 0.01f)
+ {
+ PrintFailureDetails("FixedFloats mismatch",
+ "1.1, 2.2",
+ $"{decoded.FixedFloats[0]}, {decoded.FixedFloats[1]}");
+ return false;
+ }
+
+ // Verify bounded uint count
+ if (decoded.BoundedUintsCount != 3)
+ {
+ PrintFailureDetails("BoundedUintsCount mismatch",
+ "3", decoded.BoundedUintsCount.ToString());
+ return false;
+ }
+
+ // Verify bounded uint values
+ if (decoded.BoundedUintsData[0] != 100 ||
+ decoded.BoundedUintsData[1] != 200 ||
+ decoded.BoundedUintsData[2] != 300)
+ {
+ PrintFailureDetails("BoundedUintsData mismatch",
+ "100, 200, 300",
+ $"{decoded.BoundedUintsData[0]}, {decoded.BoundedUintsData[1]}, {decoded.BoundedUintsData[2]}");
+ return false;
+ }
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(ptr);
+ }
+
+ return true;
+ }
+ catch (Exception e)
+ {
+ PrintFailureDetails($"Exception: {e.GetType().Name}", "success", e.Message);
+ Console.WriteLine(e.StackTrace);
+ return false;
+ }
+ }
+
+ static int Main(string[] args)
+ {
+ Console.WriteLine("\n[TEST START] C# Array Operations");
+
+ bool success = TestArrayOperations();
+
+ string status = success ? "PASS" : "FAIL";
+ Console.WriteLine($"[TEST END] C# Array Operations: {status}\n");
+
+ return success ? 0 : 1;
+ }
+ }
+}
diff --git a/tests/csharp/test_basic_types.cs b/tests/csharp/test_basic_types.cs
new file mode 100644
index 00000000..0b4cb35f
--- /dev/null
+++ b/tests/csharp/test_basic_types.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace StructFrameTests
+{
+ class TestBasicTypes
+ {
+ static void PrintFailureDetails(string label, string expected = null, string actual = null)
+ {
+ Console.WriteLine();
+ Console.WriteLine("============================================================");
+ Console.WriteLine($"FAILURE DETAILS: {label}");
+ Console.WriteLine("============================================================");
+
+ if (expected != null)
+ {
+ Console.WriteLine($"\nExpected: {expected}");
+ }
+ if (actual != null)
+ {
+ Console.WriteLine($"Actual: {actual}");
+ }
+
+ Console.WriteLine("============================================================");
+ Console.WriteLine();
+ }
+
+ static bool TestBasicTypesMessage()
+ {
+ try
+ {
+ // Create a new message instance
+ var msg = new StructFrame.BasicTypes.BasicTypesBasicTypesMessage();
+
+ // Set values
+ msg.SmallInt = -42;
+ msg.MediumInt = -1000;
+ msg.RegularInt = -100000;
+ msg.LargeInt = -1000000000L;
+ msg.SmallUint = 255;
+ msg.MediumUint = 65535;
+ msg.RegularUint = 4294967295U;
+ msg.LargeUint = 18446744073709551615UL;
+ msg.SinglePrecision = 3.14159f;
+ msg.DoublePrecision = 2.718281828459045;
+ msg.Flag = true;
+ msg.DeviceId = new byte[32];
+ msg.DescriptionLength = 12;
+ msg.DescriptionData = new byte[128];
+
+ // Fill device id
+ var deviceIdBytes = System.Text.Encoding.ASCII.GetBytes("TEST_DEVICE_001");
+ Array.Copy(deviceIdBytes, msg.DeviceId, Math.Min(deviceIdBytes.Length, 32));
+
+ // Fill description
+ var descBytes = System.Text.Encoding.ASCII.GetBytes("Test message");
+ Array.Copy(descBytes, msg.DescriptionData, Math.Min(descBytes.Length, 128));
+
+ // Serialize to bytes using Marshal
+ int size = Marshal.SizeOf(typeof(StructFrame.BasicTypes.BasicTypesBasicTypesMessage));
+ if (size != StructFrame.BasicTypes.BasicTypesBasicTypesMessage.MaxSize)
+ {
+ PrintFailureDetails("Struct size mismatch",
+ StructFrame.BasicTypes.BasicTypesBasicTypesMessage.MaxSize.ToString(),
+ size.ToString());
+ return false;
+ }
+
+ byte[] buffer = new byte[size];
+ IntPtr ptr = Marshal.AllocHGlobal(size);
+ try
+ {
+ Marshal.StructureToPtr(msg, ptr, false);
+ Marshal.Copy(ptr, buffer, 0, size);
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(ptr);
+ }
+
+ // Deserialize back
+ ptr = Marshal.AllocHGlobal(size);
+ try
+ {
+ Marshal.Copy(buffer, 0, ptr, size);
+ var decoded = Marshal.PtrToStructure(ptr);
+
+ // Verify values
+ if (decoded.SmallInt != msg.SmallInt)
+ {
+ PrintFailureDetails("SmallInt mismatch",
+ msg.SmallInt.ToString(), decoded.SmallInt.ToString());
+ return false;
+ }
+
+ if (decoded.MediumInt != msg.MediumInt)
+ {
+ PrintFailureDetails("MediumInt mismatch",
+ msg.MediumInt.ToString(), decoded.MediumInt.ToString());
+ return false;
+ }
+
+ if (decoded.RegularInt != msg.RegularInt)
+ {
+ PrintFailureDetails("RegularInt mismatch",
+ msg.RegularInt.ToString(), decoded.RegularInt.ToString());
+ return false;
+ }
+
+ if (decoded.Flag != msg.Flag)
+ {
+ PrintFailureDetails("Flag mismatch",
+ msg.Flag.ToString(), decoded.Flag.ToString());
+ return false;
+ }
+
+ if (Math.Abs(decoded.SinglePrecision - msg.SinglePrecision) > 0.0001f)
+ {
+ PrintFailureDetails("SinglePrecision mismatch",
+ msg.SinglePrecision.ToString(), decoded.SinglePrecision.ToString());
+ return false;
+ }
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(ptr);
+ }
+
+ return true;
+ }
+ catch (Exception e)
+ {
+ PrintFailureDetails($"Exception: {e.GetType().Name}", "success", e.Message);
+ return false;
+ }
+ }
+
+ static int Main(string[] args)
+ {
+ Console.WriteLine("\n[TEST START] C# Basic Types");
+
+ bool success = TestBasicTypesMessage();
+
+ string status = success ? "PASS" : "FAIL";
+ Console.WriteLine($"[TEST END] C# Basic Types: {status}\n");
+
+ return success ? 0 : 1;
+ }
+ }
+}
diff --git a/tests/test_config.json b/tests/test_config.json
index 3f26cbab..077a4a1f 100644
--- a/tests/test_config.json
+++ b/tests/test_config.json
@@ -132,6 +132,27 @@
"enabled": false
},
"generation_only": true
+ },
+
+ "csharp": {
+ "name": "C#",
+ "enabled": true,
+ "code_generation": {
+ "flag": "--build_csharp",
+ "output_path_flag": "--csharp_path",
+ "output_dir": "tests/generated/csharp"
+ },
+ "compilation": {
+ "enabled": false
+ },
+ "execution": {
+ "interpreter": "dotnet",
+ "source_extension": ".cs",
+ "run_command": "dotnet run --project {test_dir}/StructFrameTests.csproj --no-build --verbosity quiet -- {test_name}"
+ },
+ "test_dir": "tests/csharp",
+ "build_dir": "tests/csharp/build",
+ "generation_only": true
}
},