use super::artifact::build_artifact;
use crate::model::{ArtifactGroup, GeneratedArtifact, TransportKind, ValueType};
use crate::planner::{
normalize_type_name, to_snake_case, to_upper_snake_case, AreaKind, ContractPlan, ItemPlan,
MethodPlan, PlannedValue,
};
pub(super) fn render_common_c_artifacts(
plan: &ContractPlan,
transport: &TransportKind,
) -> Vec<GeneratedArtifact> {
let mut artifacts = vec![
build_artifact(
"Core/Inc/generated/modbus/okm_modbus_common.h",
None,
ArtifactGroup::CServiceContract,
transport.clone(),
render_c_common_header(),
),
build_artifact(
"Core/Inc/generated/modbus/okm_modbus_manifest.h",
None,
ArtifactGroup::CServiceContract,
transport.clone(),
render_c_manifest_header(plan),
),
build_artifact(
"Core/Inc/generated/modbus/okm_modbus_dispatch.h",
None,
ArtifactGroup::CTransportContract,
transport.clone(),
render_c_dispatch_header(),
),
build_artifact(
"Core/Src/generated/modbus/okm_modbus_dispatch.c",
None,
ArtifactGroup::CTransportContract,
transport.clone(),
render_c_dispatch_source(plan),
),
];
for service in &plan.services {
artifacts.push(build_artifact(
format!(
"Core/Inc/generated/modbus/{}_service.h",
to_snake_case(&service.interface_name)
),
Some(normalize_type_name(&service.interface_name)),
ArtifactGroup::CServiceContract,
transport.clone(),
render_c_service_header(plan, service),
));
}
artifacts
}
pub(super) fn render_transport_c_artifacts(
plan: &ContractPlan,
transport: &TransportKind,
) -> Vec<GeneratedArtifact> {
let segment = transport.file_segment();
let transport_label = transport.as_str();
vec![
build_artifact(
format!("Core/Inc/generated/modbus/{segment}/okm_modbus_{segment}.h"),
None,
ArtifactGroup::CTransportContract,
transport.clone(),
render_c_transport_header(transport_label, segment),
),
build_artifact(
format!("Core/Src/generated/modbus/{segment}/okm_modbus_{segment}.c"),
None,
ArtifactGroup::CTransportContract,
transport.clone(),
render_c_transport_source(plan, transport),
),
]
}
fn render_c_common_header() -> String {
vec![
"/* 自动生成:通用 Modbus 合同类型 */".to_string(),
"#ifndef OKM_MODBUS_COMMON_H".to_string(),
"#define OKM_MODBUS_COMMON_H".to_string(),
"".to_string(),
"#include <stdbool.h>".to_string(),
"#include <stddef.h>".to_string(),
"#include <stdint.h>".to_string(),
"".to_string(),
"#define OKM_MODBUS_STRING_BYTE_CAPACITY 32U".to_string(),
"#define OKM_MODBUS_STRING_REGISTER_WIDTH 17U".to_string(),
"".to_string(),
"typedef enum okm_modbus_exception_code {".to_string(),
" OKM_MODBUS_EXCEPTION_NONE = 0,".to_string(),
" OKM_MODBUS_EXCEPTION_ILLEGAL_FUNCTION = 1,".to_string(),
" OKM_MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS = 2,".to_string(),
" OKM_MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE = 3,".to_string(),
" OKM_MODBUS_EXCEPTION_SLAVE_DEVICE_FAILURE = 4".to_string(),
"} okm_modbus_exception_code_t;".to_string(),
"".to_string(),
"typedef struct okm_modbus_string32 {".to_string(),
" uint16_t length;".to_string(),
" char data[OKM_MODBUS_STRING_BYTE_CAPACITY + 1U];".to_string(),
"} okm_modbus_string32_t;".to_string(),
"".to_string(),
"typedef struct okm_modbus_bytes32 {".to_string(),
" uint16_t length;".to_string(),
" uint8_t data[OKM_MODBUS_STRING_BYTE_CAPACITY];".to_string(),
"} okm_modbus_bytes32_t;".to_string(),
"".to_string(),
"#endif /* OKM_MODBUS_COMMON_H */".to_string(),
"".to_string(),
]
.join("\n")
}
fn render_c_manifest_header(plan: &ContractPlan) -> String {
let mut lines = vec![
"/* 自动生成:Modbus 地址规划清单 */".to_string(),
"#ifndef OKM_MODBUS_MANIFEST_H".to_string(),
"#define OKM_MODBUS_MANIFEST_H".to_string(),
"".to_string(),
"#include \"okm_modbus_common.h\"".to_string(),
"".to_string(),
format!("#define OKM_MODBUS_TOTAL_COILS {}", plan.total_coils),
format!(
"#define OKM_MODBUS_TOTAL_DISCRETE_INPUTS {}",
plan.total_discrete_inputs
),
format!(
"#define OKM_MODBUS_TOTAL_HOLDING_REGISTERS {}",
plan.total_holding_registers
),
format!(
"#define OKM_MODBUS_TOTAL_INPUT_REGISTERS {}",
plan.total_input_registers
),
"".to_string(),
];
for method in &plan.methods {
let base = format!(
"OKM_MODBUS_{}_{}",
to_upper_snake_case(&method.interface_name),
to_upper_snake_case(&method.method_name)
);
lines.push(format!(
"#define {base}_START_ADDRESS {}",
method.start_address
));
lines.push(format!("#define {base}_QUANTITY {}", method.quantity));
lines.push(format!(
"#define {base}_AREA_{} 1",
to_upper_snake_case(method.area_kind.c_label())
));
lines.push(String::new());
}
lines.push("#endif /* OKM_MODBUS_MANIFEST_H */".to_string());
lines.push(String::new());
lines.join("\n")
}
fn render_c_service_header(plan: &ContractPlan, service: &crate::model::ContractService) -> String {
let interface_name = normalize_type_name(&service.interface_name);
let guard = format!("{}_SERVICE_H", to_upper_snake_case(&interface_name));
let mut lines = vec![
format!("/* 自动生成:{} 服务合同 */", interface_name),
format!("#ifndef {guard}"),
format!("#define {guard}"),
"".to_string(),
"#include \"okm_modbus_common.h\"".to_string(),
"".to_string(),
];
for method in plan
.methods
.iter()
.filter(|method| method.interface_name == interface_name)
{
if let PlannedValue::Dto { type_name, items } = &method.planned_value {
lines.push(format!("typedef struct {} {{", c_struct_name(type_name)));
for item in items {
lines.push(format!(
" {} {};",
c_value_type(&item.value_type),
item.name
));
}
lines.push(format!("}} {};", c_struct_name(type_name)));
lines.push(String::new());
}
}
for method in plan
.methods
.iter()
.filter(|method| method.interface_name == interface_name)
{
lines.push(format!(
"int okm_bridge_{}({});",
method.c_method_name,
c_bridge_signature(method)
));
}
lines.push(String::new());
lines.push(format!("#endif /* {guard} */"));
lines.push(String::new());
lines.join("\n")
}
fn render_c_dispatch_header() -> String {
vec![
"/* 自动生成:共享 Modbus dispatch 入口 */".to_string(),
"#ifndef OKM_MODBUS_DISPATCH_H".to_string(),
"#define OKM_MODBUS_DISPATCH_H".to_string(),
"".to_string(),
"#include \"okm_modbus_common.h\"".to_string(),
"".to_string(),
"int okm_modbus_read_discrete_inputs(uint16_t start_address, uint16_t quantity, uint8_t *dest_bits);"
.to_string(),
"int okm_modbus_read_input_registers(uint16_t start_address, uint16_t quantity, uint16_t *dest_registers);"
.to_string(),
"int okm_modbus_write_coils(uint16_t start_address, uint16_t quantity, const uint8_t *values);"
.to_string(),
"int okm_modbus_write_holding_registers(uint16_t start_address, uint16_t quantity, const uint16_t *values);"
.to_string(),
"".to_string(),
"#endif /* OKM_MODBUS_DISPATCH_H */".to_string(),
"".to_string(),
]
.join("\n")
}
fn render_c_dispatch_source(plan: &ContractPlan) -> String {
let mut lines = vec![
"/* 自动生成:共享 Modbus dispatch 实现 */".to_string(),
"#include \"okm_modbus_dispatch.h\"".to_string(),
"#include \"okm_modbus_manifest.h\"".to_string(),
"#include <string.h>".to_string(),
"".to_string(),
];
for service in &plan.services {
lines.push(format!(
"#include \"{}_service.h\"",
to_snake_case(&service.interface_name)
));
}
lines.push(String::new());
lines.push(
"static void okm_modbus_encode_string32(uint16_t *registers, const okm_modbus_string32_t *value) {"
.to_string(),
);
lines.push(" uint16_t length = value->length;".to_string());
lines.push(
" if (length > OKM_MODBUS_STRING_BYTE_CAPACITY) { length = OKM_MODBUS_STRING_BYTE_CAPACITY; }"
.to_string(),
);
lines.push(" registers[0] = length;".to_string());
lines.push(
" memset(®isters[1], 0, sizeof(uint16_t) * (OKM_MODBUS_STRING_REGISTER_WIDTH - 1U));"
.to_string(),
);
lines.push(" for (uint16_t index = 0; index < length; index += 2U) {".to_string());
lines.push(" uint8_t high = (uint8_t)value->data[index];".to_string());
lines.push(
" uint8_t low = (index + 1U < length) ? (uint8_t)value->data[index + 1U] : 0U;"
.to_string(),
);
lines.push(
" registers[1U + (index / 2U)] = ((uint16_t)high << 8) | (uint16_t)low;".to_string(),
);
lines.push(" }".to_string());
lines.push("}".to_string());
lines.push(String::new());
lines.push(
"static void okm_modbus_encode_bytes32(uint16_t *registers, const okm_modbus_bytes32_t *value) {"
.to_string(),
);
lines.push(" uint16_t length = value->length;".to_string());
lines.push(
" if (length > OKM_MODBUS_STRING_BYTE_CAPACITY) { length = OKM_MODBUS_STRING_BYTE_CAPACITY; }"
.to_string(),
);
lines.push(" registers[0] = length;".to_string());
lines.push(
" memset(®isters[1], 0, sizeof(uint16_t) * (OKM_MODBUS_STRING_REGISTER_WIDTH - 1U));"
.to_string(),
);
lines.push(" for (uint16_t index = 0; index < length; index += 2U) {".to_string());
lines.push(" uint8_t high = value->data[index];".to_string());
lines.push(
" uint8_t low = (index + 1U < length) ? value->data[index + 1U] : 0U;".to_string(),
);
lines.push(
" registers[1U + (index / 2U)] = ((uint16_t)high << 8) | (uint16_t)low;".to_string(),
);
lines.push(" }".to_string());
lines.push("}".to_string());
lines.push(String::new());
lines.push(
"static void okm_modbus_decode_string32(okm_modbus_string32_t *value, const uint16_t *registers) {"
.to_string(),
);
lines.push(" uint16_t length = registers[0];".to_string());
lines.push(
" if (length > OKM_MODBUS_STRING_BYTE_CAPACITY) { length = OKM_MODBUS_STRING_BYTE_CAPACITY; }"
.to_string(),
);
lines.push(" value->length = length;".to_string());
lines.push(" memset(value->data, 0, sizeof(value->data));".to_string());
lines.push(" for (uint16_t index = 0; index < length; index += 2U) {".to_string());
lines.push(" uint16_t register_value = registers[1U + (index / 2U)];".to_string());
lines.push(" value->data[index] = (char)((register_value >> 8) & 0x00FFU);".to_string());
lines.push(" if (index + 1U < length) {".to_string());
lines.push(
" value->data[index + 1U] = (char)(register_value & 0x00FFU);".to_string(),
);
lines.push(" }".to_string());
lines.push(" }".to_string());
lines.push("}".to_string());
lines.push(String::new());
lines.push(
"static void okm_modbus_decode_bytes32(okm_modbus_bytes32_t *value, const uint16_t *registers) {"
.to_string(),
);
lines.push(" uint16_t length = registers[0];".to_string());
lines.push(
" if (length > OKM_MODBUS_STRING_BYTE_CAPACITY) { length = OKM_MODBUS_STRING_BYTE_CAPACITY; }"
.to_string(),
);
lines.push(" value->length = length;".to_string());
lines.push(" memset(value->data, 0, sizeof(value->data));".to_string());
lines.push(" for (uint16_t index = 0; index < length; index += 2U) {".to_string());
lines.push(" uint16_t register_value = registers[1U + (index / 2U)];".to_string());
lines.push(
" value->data[index] = (uint8_t)((register_value >> 8) & 0x00FFU);".to_string(),
);
lines.push(" if (index + 1U < length) {".to_string());
lines.push(
" value->data[index + 1U] = (uint8_t)(register_value & 0x00FFU);".to_string(),
);
lines.push(" }".to_string());
lines.push(" }".to_string());
lines.push("}".to_string());
lines.push(String::new());
lines.push(render_c_read_bits_function(plan));
lines.push(String::new());
lines.push(render_c_read_registers_function(plan));
lines.push(String::new());
lines.push(render_c_write_bits_function(plan));
lines.push(String::new());
lines.push(render_c_write_registers_function(plan));
lines.push(String::new());
lines.join("\n")
}
fn render_c_read_bits_function(plan: &ContractPlan) -> String {
if plan.total_discrete_inputs == 0 {
return [
"int okm_modbus_read_discrete_inputs(uint16_t start_address, uint16_t quantity, uint8_t *dest_bits) {",
" (void)start_address;",
" (void)quantity;",
" (void)dest_bits;",
" return OKM_MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS;",
"}",
]
.join("\n");
}
let mut lines = vec![
"int okm_modbus_read_discrete_inputs(uint16_t start_address, uint16_t quantity, uint8_t *dest_bits) {"
.to_string(),
" if (quantity == 0U || dest_bits == NULL) {".to_string(),
" return OKM_MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE;".to_string(),
" }".to_string(),
format!(
" if ((uint32_t)start_address + (uint32_t)quantity > (uint32_t)OKM_MODBUS_TOTAL_DISCRETE_INPUTS) {{"
),
" return OKM_MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS;".to_string(),
" }".to_string(),
format!(
" uint8_t area_buffer[OKM_MODBUS_TOTAL_DISCRETE_INPUTS > 0 ? OKM_MODBUS_TOTAL_DISCRETE_INPUTS : 1] = {{0}};"
),
];
for method in plan
.methods
.iter()
.filter(|method| matches!(method.area_kind, AreaKind::DiscreteInput))
{
lines.extend(render_c_populate_bit_area(method));
}
lines.push(" memcpy(dest_bits, &area_buffer[start_address], quantity);".to_string());
lines.push(" return OKM_MODBUS_EXCEPTION_NONE;".to_string());
lines.push("}".to_string());
lines.join("\n")
}
fn render_c_populate_bit_area(method: &MethodPlan) -> Vec<String> {
match &method.planned_value {
PlannedValue::Dto { type_name, items } => {
let mut lines = vec![
format!(
" {} {}_value;",
c_struct_name(type_name),
method.c_method_name
),
format!(
" if (okm_bridge_{}(&{}_value) != 0) {{",
method.c_method_name, method.c_method_name
),
" return OKM_MODBUS_EXCEPTION_SLAVE_DEVICE_FAILURE;".to_string(),
" }".to_string(),
];
for item in items {
lines.push(format!(
" area_buffer[{}U] = {}_value.{} ? 1U : 0U;",
method.start_address + item.offset,
method.c_method_name,
item.name
));
}
lines
}
PlannedValue::Scalar { .. } => vec![
" bool value = false;".to_string(),
format!(
" if (okm_bridge_{}(&value) != 0) {{",
method.c_method_name
),
" return OKM_MODBUS_EXCEPTION_SLAVE_DEVICE_FAILURE;".to_string(),
" }".to_string(),
format!(
" area_buffer[{}U] = value ? 1U : 0U;",
method.start_address
),
],
PlannedValue::Parameters { .. } => Vec::new(),
}
}
fn render_c_read_registers_function(plan: &ContractPlan) -> String {
if plan.total_input_registers == 0 {
return [
"int okm_modbus_read_input_registers(uint16_t start_address, uint16_t quantity, uint16_t *dest_registers) {",
" (void)start_address;",
" (void)quantity;",
" (void)dest_registers;",
" return OKM_MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS;",
"}",
]
.join("\n");
}
let mut lines = vec![
"int okm_modbus_read_input_registers(uint16_t start_address, uint16_t quantity, uint16_t *dest_registers) {"
.to_string(),
" if (quantity == 0U || dest_registers == NULL) {".to_string(),
" return OKM_MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE;".to_string(),
" }".to_string(),
" if ((uint32_t)start_address + (uint32_t)quantity > (uint32_t)OKM_MODBUS_TOTAL_INPUT_REGISTERS) {"
.to_string(),
" return OKM_MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS;".to_string(),
" }".to_string(),
" uint16_t area_buffer[OKM_MODBUS_TOTAL_INPUT_REGISTERS > 0 ? OKM_MODBUS_TOTAL_INPUT_REGISTERS : 1] = {0};"
.to_string(),
];
for method in plan
.methods
.iter()
.filter(|method| matches!(method.area_kind, AreaKind::InputRegister))
{
lines.extend(render_c_populate_register_area(method));
}
lines.push(
" memcpy(dest_registers, &area_buffer[start_address], sizeof(uint16_t) * quantity);"
.to_string(),
);
lines.push(" return OKM_MODBUS_EXCEPTION_NONE;".to_string());
lines.push("}".to_string());
lines.join("\n")
}
fn render_c_populate_register_area(method: &MethodPlan) -> Vec<String> {
match &method.planned_value {
PlannedValue::Dto { type_name, items } => {
let mut lines = vec![
format!(
" {} {}_value;",
c_struct_name(type_name),
method.c_method_name
),
format!(
" if (okm_bridge_{}(&{}_value) != 0) {{",
method.c_method_name, method.c_method_name
),
" return OKM_MODBUS_EXCEPTION_SLAVE_DEVICE_FAILURE;".to_string(),
" }".to_string(),
];
for item in items {
lines.extend(render_c_encode_item(
method.start_address + item.offset,
&format!("{}_value.{}", method.c_method_name, item.name),
item,
));
}
lines
}
PlannedValue::Scalar { item } => {
let c_type = c_value_type(&item.value_type);
let value_name = if matches!(item.value_type, ValueType::String | ValueType::Bytes) {
format!("{c_type} value = {{0}};")
} else {
format!("{c_type} value = 0;")
};
let mut lines = vec![
format!(" {value_name}"),
format!(
" if (okm_bridge_{}(&value) != 0) {{",
method.c_method_name
),
" return OKM_MODBUS_EXCEPTION_SLAVE_DEVICE_FAILURE;".to_string(),
" }".to_string(),
];
lines.extend(render_c_encode_item(method.start_address, "value", item));
lines
}
PlannedValue::Parameters { .. } => Vec::new(),
}
}
fn render_c_encode_item(
start_address: u16,
value_expression: &str,
item: &ItemPlan,
) -> Vec<String> {
match item.value_type {
ValueType::Boolean => vec![format!(
" area_buffer[{}U] = {} ? 1U : 0U;",
start_address, value_expression
)],
ValueType::Int => vec![
format!(
" area_buffer[{}U] = (uint16_t)((((uint32_t)({})) >> 16) & 0xFFFFU);",
start_address, value_expression
),
format!(
" area_buffer[{}U] = (uint16_t)(((uint32_t)({})) & 0xFFFFU);",
start_address + 1,
value_expression
),
],
ValueType::String => vec![format!(
" okm_modbus_encode_string32(&area_buffer[{}U], &{});",
start_address, value_expression
)],
ValueType::Bytes => vec![format!(
" okm_modbus_encode_bytes32(&area_buffer[{}U], &{});",
start_address, value_expression
)],
}
}
fn render_c_write_bits_function(plan: &ContractPlan) -> String {
let write_methods = plan
.methods
.iter()
.filter(|method| matches!(method.area_kind, AreaKind::Coil))
.collect::<Vec<_>>();
if write_methods.is_empty() {
return [
"int okm_modbus_write_coils(uint16_t start_address, uint16_t quantity, const uint8_t *values) {",
" (void)start_address;",
" (void)quantity;",
" (void)values;",
" return OKM_MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS;",
"}",
]
.join("\n");
}
let mut lines = vec![
"int okm_modbus_write_coils(uint16_t start_address, uint16_t quantity, const uint8_t *values) {"
.to_string(),
" if (quantity == 0U || values == NULL) {".to_string(),
" return OKM_MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE;".to_string(),
" }".to_string(),
];
for method in write_methods {
let PlannedValue::Parameters { items } = &method.planned_value else {
continue;
};
lines.push(format!(
" if (start_address == {}U && quantity == {}U) {{",
method.start_address, method.quantity
));
let call_arguments = items
.iter()
.enumerate()
.map(|(index, _)| format!("values[{index}] != 0U"))
.collect::<Vec<_>>()
.join(", ");
lines.push(format!(
" return okm_bridge_{}({}) == 0 ? OKM_MODBUS_EXCEPTION_NONE : OKM_MODBUS_EXCEPTION_SLAVE_DEVICE_FAILURE;",
method.c_method_name, call_arguments
));
lines.push(" }".to_string());
}
lines.push(" return OKM_MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS;".to_string());
lines.push("}".to_string());
lines.join("\n")
}
fn render_c_write_registers_function(plan: &ContractPlan) -> String {
let write_methods = plan
.methods
.iter()
.filter(|method| matches!(method.area_kind, AreaKind::HoldingRegister))
.collect::<Vec<_>>();
if write_methods.is_empty() {
return [
"int okm_modbus_write_holding_registers(uint16_t start_address, uint16_t quantity, const uint16_t *values) {",
" (void)start_address;",
" (void)quantity;",
" (void)values;",
" return OKM_MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS;",
"}",
]
.join("\n");
}
let mut lines = vec![
"int okm_modbus_write_holding_registers(uint16_t start_address, uint16_t quantity, const uint16_t *values) {"
.to_string(),
" if (quantity == 0U || values == NULL) {".to_string(),
" return OKM_MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE;".to_string(),
" }".to_string(),
];
for method in write_methods {
let PlannedValue::Parameters { items } = &method.planned_value else {
continue;
};
lines.push(format!(
" if (start_address == {}U && quantity == {}U) {{",
method.start_address, method.quantity
));
for item in items {
let variable_name = format!("decoded_{}", item.name);
lines.push(c_decode_variable_declaration(item, &variable_name));
lines.extend(render_c_decode_item(item, "values", &variable_name));
}
let call_arguments = items
.iter()
.map(|item| {
if matches!(item.value_type, ValueType::String | ValueType::Bytes) {
format!("&decoded_{}", item.name)
} else {
format!("decoded_{}", item.name)
}
})
.collect::<Vec<_>>()
.join(", ");
lines.push(format!(
" return okm_bridge_{}({}) == 0 ? OKM_MODBUS_EXCEPTION_NONE : OKM_MODBUS_EXCEPTION_SLAVE_DEVICE_FAILURE;",
method.c_method_name, call_arguments
));
lines.push(" }".to_string());
}
lines.push(" return OKM_MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS;".to_string());
lines.push("}".to_string());
lines.join("\n")
}
fn c_decode_variable_declaration(item: &ItemPlan, variable_name: &str) -> String {
match item.value_type {
ValueType::Boolean => format!(" bool {variable_name} = false;"),
ValueType::Int => format!(" int32_t {variable_name} = 0;"),
ValueType::String => format!(" okm_modbus_string32_t {variable_name} = {{0}};"),
ValueType::Bytes => format!(" okm_modbus_bytes32_t {variable_name} = {{0}};"),
}
}
fn render_c_decode_item(item: &ItemPlan, buffer_name: &str, variable_name: &str) -> Vec<String> {
match item.value_type {
ValueType::Boolean => vec![format!(
" {variable_name} = {buffer_name}[{}U] != 0U;",
item.offset
)],
ValueType::Int => vec![format!(
" {variable_name} = (int32_t)(((uint32_t){buffer_name}[{}U] << 16) | (uint32_t){buffer_name}[{}U]);",
item.offset,
item.offset + 1
)],
ValueType::String => vec![format!(
" okm_modbus_decode_string32(&{variable_name}, &{buffer_name}[{}U]);",
item.offset
)],
ValueType::Bytes => vec![format!(
" okm_modbus_decode_bytes32(&{variable_name}, &{buffer_name}[{}U]);",
item.offset
)],
}
}
fn render_c_transport_header(transport_label: &str, segment: &str) -> String {
let guard = format!("OKM_MODBUS_{}_H", to_upper_snake_case(segment));
let request_type = if segment == "rtu" { "RTU" } else { "TCP" };
vec![
format!("/* 自动生成:{transport_label} transport 入口 */"),
format!("#ifndef {guard}"),
format!("#define {guard}"),
"".to_string(),
"#include <stddef.h>".to_string(),
"#include <stdint.h>".to_string(),
"#include \"okm_modbus_common.h\"".to_string(),
"".to_string(),
format!(
"int okm_modbus_{}_handle_request(const uint8_t *request_adu, size_t request_length, uint8_t *response_adu, size_t response_capacity);",
segment
),
"".to_string(),
format!("/* 该入口负责完整解析 {request_type} 请求帧并生成响应帧。 */"),
"".to_string(),
format!("#endif /* {guard} */"),
"".to_string(),
]
.join("\n")
}
fn render_c_transport_source(plan: &ContractPlan, transport: &TransportKind) -> String {
match transport {
TransportKind::Rtu => render_c_rtu_source(plan),
TransportKind::Tcp => render_c_tcp_source(plan),
}
}
fn render_c_rtu_source(_plan: &ContractPlan) -> String {
[
"/* 自动生成:RTU transport 入口 */",
"#include \"rtu/okm_modbus_rtu.h\"",
"#include \"okm_modbus_dispatch.h\"",
"#include <string.h>",
"",
"static uint16_t okm_modbus_crc16(const uint8_t *payload, size_t length) {",
" uint16_t crc = 0xFFFFU;",
" for (size_t index = 0; index < length; ++index) {",
" crc ^= (uint16_t)payload[index];",
" for (uint8_t bit = 0; bit < 8U; ++bit) {",
" uint16_t lsb = crc & 0x0001U;",
" crc >>= 1U;",
" if (lsb == 1U) {",
" crc ^= 0xA001U;",
" }",
" }",
" }",
" return crc;",
"}",
"",
"static void okm_modbus_append_crc(uint8_t *frame, size_t *length) {",
" uint16_t crc = okm_modbus_crc16(frame, *length);",
" frame[(*length)++] = (uint8_t)(crc & 0x00FFU);",
" frame[(*length)++] = (uint8_t)((crc >> 8) & 0x00FFU);",
"}",
"",
"static size_t okm_modbus_build_exception(uint8_t slave, uint8_t function_code, okm_modbus_exception_code_t exception, uint8_t *response_adu) {",
" size_t response_length = 0U;",
" response_adu[response_length++] = slave;",
" response_adu[response_length++] = (uint8_t)(function_code | 0x80U);",
" response_adu[response_length++] = (uint8_t)exception;",
" okm_modbus_append_crc(response_adu, &response_length);",
" return response_length;",
"}",
"",
"int okm_modbus_rtu_handle_request(const uint8_t *request_adu, size_t request_length, uint8_t *response_adu, size_t response_capacity) {",
" if (request_adu == NULL || response_adu == NULL || request_length < 8U || response_capacity < 5U) {",
" return -1;",
" }",
" uint16_t expected_crc = (uint16_t)request_adu[request_length - 2U] | ((uint16_t)request_adu[request_length - 1U] << 8);",
" uint16_t actual_crc = okm_modbus_crc16(request_adu, request_length - 2U);",
" if (expected_crc != actual_crc) {",
" return -1;",
" }",
" uint8_t slave = request_adu[0];",
" uint8_t function_code = request_adu[1];",
" uint16_t start_address = (uint16_t)((request_adu[2] << 8) | request_adu[3]);",
" uint16_t quantity = (uint16_t)((request_adu[4] << 8) | request_adu[5]);",
" okm_modbus_exception_code_t exception = OKM_MODBUS_EXCEPTION_ILLEGAL_FUNCTION;",
" size_t response_length = 0U;",
" if (function_code == 0x02U) {",
" uint8_t values[256] = {0};",
" exception = (okm_modbus_exception_code_t)okm_modbus_read_discrete_inputs(start_address, quantity, values);",
" if (exception == OKM_MODBUS_EXCEPTION_NONE) {",
" uint8_t byte_count = (uint8_t)((quantity + 7U) / 8U);",
" response_adu[response_length++] = slave;",
" response_adu[response_length++] = function_code;",
" response_adu[response_length++] = byte_count;",
" for (uint16_t index = 0; index < quantity; ++index) {",
" size_t byte_index = response_length + (size_t)(index / 8U);",
" if ((index % 8U) == 0U) {",
" response_adu[byte_index] = 0U;",
" }",
" if (values[index] != 0U) {",
" response_adu[byte_index] |= (uint8_t)(1U << (index % 8U));",
" }",
" }",
" response_length += byte_count;",
" okm_modbus_append_crc(response_adu, &response_length);",
" return (int)response_length;",
" }",
" }",
" if (function_code == 0x04U) {",
" uint16_t values[256] = {0};",
" exception = (okm_modbus_exception_code_t)okm_modbus_read_input_registers(start_address, quantity, values);",
" if (exception == OKM_MODBUS_EXCEPTION_NONE) {",
" response_adu[response_length++] = slave;",
" response_adu[response_length++] = function_code;",
" response_adu[response_length++] = (uint8_t)(quantity * 2U);",
" for (uint16_t index = 0; index < quantity; ++index) {",
" response_adu[response_length++] = (uint8_t)((values[index] >> 8) & 0x00FFU);",
" response_adu[response_length++] = (uint8_t)(values[index] & 0x00FFU);",
" }",
" okm_modbus_append_crc(response_adu, &response_length);",
" return (int)response_length;",
" }",
" }",
" if (function_code == 0x05U) {",
" uint8_t coil_value = request_adu[4] == 0xFFU ? 1U : 0U;",
" exception = (okm_modbus_exception_code_t)okm_modbus_write_coils(start_address, 1U, &coil_value);",
" if (exception == OKM_MODBUS_EXCEPTION_NONE) {",
" memcpy(response_adu, request_adu, request_length);",
" return (int)request_length;",
" }",
" }",
" if (function_code == 0x0FU) {",
" uint8_t byte_count = request_adu[6];",
" uint8_t values[256] = {0};",
" for (uint16_t index = 0; index < quantity; ++index) {",
" uint8_t source = request_adu[7U + (index / 8U)];",
" values[index] = (uint8_t)((source >> (index % 8U)) & 0x01U);",
" }",
" (void)byte_count;",
" exception = (okm_modbus_exception_code_t)okm_modbus_write_coils(start_address, quantity, values);",
" if (exception == OKM_MODBUS_EXCEPTION_NONE) {",
" response_adu[0] = slave;",
" response_adu[1] = function_code;",
" response_adu[2] = request_adu[2];",
" response_adu[3] = request_adu[3];",
" response_adu[4] = request_adu[4];",
" response_adu[5] = request_adu[5];",
" response_length = 6U;",
" okm_modbus_append_crc(response_adu, &response_length);",
" return (int)response_length;",
" }",
" }",
" if (function_code == 0x06U) {",
" uint16_t value = (uint16_t)((request_adu[4] << 8) | request_adu[5]);",
" exception = (okm_modbus_exception_code_t)okm_modbus_write_holding_registers(start_address, 1U, &value);",
" if (exception == OKM_MODBUS_EXCEPTION_NONE) {",
" memcpy(response_adu, request_adu, request_length);",
" return (int)request_length;",
" }",
" }",
" if (function_code == 0x10U) {",
" uint8_t byte_count = request_adu[6];",
" uint16_t values[256] = {0};",
" for (uint16_t index = 0; index < quantity; ++index) {",
" size_t source_index = 7U + ((size_t)index * 2U);",
" values[index] = (uint16_t)((request_adu[source_index] << 8) | request_adu[source_index + 1U]);",
" }",
" (void)byte_count;",
" exception = (okm_modbus_exception_code_t)okm_modbus_write_holding_registers(start_address, quantity, values);",
" if (exception == OKM_MODBUS_EXCEPTION_NONE) {",
" response_adu[0] = slave;",
" response_adu[1] = function_code;",
" response_adu[2] = request_adu[2];",
" response_adu[3] = request_adu[3];",
" response_adu[4] = request_adu[4];",
" response_adu[5] = request_adu[5];",
" response_length = 6U;",
" okm_modbus_append_crc(response_adu, &response_length);",
" return (int)response_length;",
" }",
" }",
" return (int)okm_modbus_build_exception(slave, function_code, exception, response_adu);",
"}",
"",
]
.join("\n")
}
fn render_c_tcp_source(_plan: &ContractPlan) -> String {
[
"/* 自动生成:TCP transport 入口 */",
"#include \"tcp/okm_modbus_tcp.h\"",
"#include \"okm_modbus_dispatch.h\"",
"#include <string.h>",
"",
"static size_t okm_modbus_tcp_build_exception(const uint8_t *request_adu, uint8_t function_code, okm_modbus_exception_code_t exception, uint8_t *response_adu) {",
" memcpy(response_adu, request_adu, 7U);",
" response_adu[7] = (uint8_t)(function_code | 0x80U);",
" response_adu[8] = (uint8_t)exception;",
" response_adu[4] = 0x00U;",
" response_adu[5] = 0x03U;",
" return 9U;",
"}",
"",
"int okm_modbus_tcp_handle_request(const uint8_t *request_adu, size_t request_length, uint8_t *response_adu, size_t response_capacity) {",
" if (request_adu == NULL || response_adu == NULL || request_length < 12U || response_capacity < 9U) {",
" return -1;",
" }",
" uint8_t function_code = request_adu[7];",
" uint16_t start_address = (uint16_t)((request_adu[8] << 8) | request_adu[9]);",
" uint16_t quantity = (uint16_t)((request_adu[10] << 8) | request_adu[11]);",
" okm_modbus_exception_code_t exception = OKM_MODBUS_EXCEPTION_ILLEGAL_FUNCTION;",
" memcpy(response_adu, request_adu, 7U);",
" if (function_code == 0x02U) {",
" uint8_t values[256] = {0};",
" exception = (okm_modbus_exception_code_t)okm_modbus_read_discrete_inputs(start_address, quantity, values);",
" if (exception == OKM_MODBUS_EXCEPTION_NONE) {",
" uint8_t byte_count = (uint8_t)((quantity + 7U) / 8U);",
" response_adu[7] = function_code;",
" response_adu[8] = byte_count;",
" memset(&response_adu[9], 0, byte_count);",
" for (uint16_t index = 0; index < quantity; ++index) {",
" if (values[index] != 0U) {",
" response_adu[9U + (index / 8U)] |= (uint8_t)(1U << (index % 8U));",
" }",
" }",
" response_adu[4] = 0x00U;",
" response_adu[5] = (uint8_t)(3U + byte_count);",
" return (int)(9U + byte_count);",
" }",
" }",
" if (function_code == 0x04U) {",
" uint16_t values[256] = {0};",
" exception = (okm_modbus_exception_code_t)okm_modbus_read_input_registers(start_address, quantity, values);",
" if (exception == OKM_MODBUS_EXCEPTION_NONE) {",
" response_adu[7] = function_code;",
" response_adu[8] = (uint8_t)(quantity * 2U);",
" size_t response_length = 9U;",
" for (uint16_t index = 0; index < quantity; ++index) {",
" response_adu[response_length++] = (uint8_t)((values[index] >> 8) & 0x00FFU);",
" response_adu[response_length++] = (uint8_t)(values[index] & 0x00FFU);",
" }",
" response_adu[4] = (uint8_t)(((response_length - 6U) >> 8) & 0x00FFU);",
" response_adu[5] = (uint8_t)((response_length - 6U) & 0x00FFU);",
" return (int)response_length;",
" }",
" }",
" if (function_code == 0x05U) {",
" uint8_t coil_value = request_adu[10] == 0xFFU ? 1U : 0U;",
" exception = (okm_modbus_exception_code_t)okm_modbus_write_coils(start_address, 1U, &coil_value);",
" if (exception == OKM_MODBUS_EXCEPTION_NONE) {",
" memcpy(response_adu, request_adu, 12U);",
" return 12;",
" }",
" }",
" if (function_code == 0x0FU) {",
" uint8_t values[256] = {0};",
" for (uint16_t index = 0; index < quantity; ++index) {",
" uint8_t source = request_adu[13U + (index / 8U)];",
" values[index] = (uint8_t)((source >> (index % 8U)) & 0x01U);",
" }",
" exception = (okm_modbus_exception_code_t)okm_modbus_write_coils(start_address, quantity, values);",
" if (exception == OKM_MODBUS_EXCEPTION_NONE) {",
" memcpy(response_adu, request_adu, 12U);",
" return 12;",
" }",
" }",
" if (function_code == 0x06U) {",
" uint16_t value = (uint16_t)((request_adu[10] << 8) | request_adu[11]);",
" exception = (okm_modbus_exception_code_t)okm_modbus_write_holding_registers(start_address, 1U, &value);",
" if (exception == OKM_MODBUS_EXCEPTION_NONE) {",
" memcpy(response_adu, request_adu, 12U);",
" return 12;",
" }",
" }",
" if (function_code == 0x10U) {",
" uint16_t values[256] = {0};",
" for (uint16_t index = 0; index < quantity; ++index) {",
" size_t source_index = 13U + ((size_t)index * 2U);",
" values[index] = (uint16_t)((request_adu[source_index] << 8) | request_adu[source_index + 1U]);",
" }",
" exception = (okm_modbus_exception_code_t)okm_modbus_write_holding_registers(start_address, quantity, values);",
" if (exception == OKM_MODBUS_EXCEPTION_NONE) {",
" memcpy(response_adu, request_adu, 12U);",
" return 12;",
" }",
" }",
" return (int)okm_modbus_tcp_build_exception(request_adu, function_code, exception, response_adu);",
"}",
"",
]
.join("\n")
}
fn c_struct_name(type_name: &str) -> String {
format!("{}_t", to_snake_case(type_name))
}
fn c_value_type(value_type: &ValueType) -> &'static str {
match value_type {
ValueType::Boolean => "bool",
ValueType::Int => "int32_t",
ValueType::String => "okm_modbus_string32_t",
ValueType::Bytes => "okm_modbus_bytes32_t",
}
}
fn c_bridge_signature(method: &MethodPlan) -> String {
match &method.planned_value {
PlannedValue::Dto { type_name, .. } => {
format!("{} *out_value", c_struct_name(type_name))
}
PlannedValue::Scalar { item } => {
format!("{} *out_value", c_value_type(&item.value_type))
}
PlannedValue::Parameters { items } => items
.iter()
.map(|item| match item.value_type {
ValueType::Boolean => format!("bool {}", item.name),
ValueType::Int => format!("int32_t {}", item.name),
ValueType::String => format!("const okm_modbus_string32_t *{}", item.name),
ValueType::Bytes => format!("const okm_modbus_bytes32_t *{}", item.name),
})
.collect::<Vec<_>>()
.join(", "),
}
}