Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/modbus optimizer #1744

Merged
merged 10 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions RELEASE_NOTES
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ New Features
them back together. It is now possible to read arrays of
almost unlimited size.
- Added auto-discovery to the EIP and KNXNet/IP Drivers.
- Added an Optimizer to the Modbus driver, that improves read
performance of multi-item read requests by more than 10 times.

Incompatible changes
--------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,11 @@ namespace org.apache.plc4net.drivers.${protocolName?replace("-", "")}.${outputFl
<#if arrayField.loopExpression.contains("curPos")>
curPos = readBuffer.getPos() - startPos;
</#if>
<#if elementTypeReference.isByteBased()>
var ${arrayField.name} = readBuffer.ReadByteArray("", ${helper.toParseExpression(arrayField, helper.intTypeReference, arrayField.loopExpression, parserArguments)});
<#else>
<#-- If this is a count array, we can directly initialize an array with the given size -->
<#if field.isCountArrayField()>
<#if field.isCountArrayField()>
// Count array
List<IPlcValue> ${arrayField.name};
{
Expand All @@ -111,40 +114,41 @@ namespace org.apache.plc4net.drivers.${protocolName?replace("-", "")}.${outputFl
}
}
<#-- In all other cases do we have to work with a list, that is later converted to an array -->
<#else>
<#else>
<#-- For a length array, we read data till the read position of the buffer reaches a given position -->
<#if arrayField.isLengthArrayField()>
<#if arrayField.isLengthArrayField()>
// Length array
var _${arrayField.name}Length = ${helper.toParseExpression(arrayField, helper.intTypeReference, arrayField.loopExpression,parserArguments)};
var ${arrayField.name}EndPos = readBuffer.getPos() + _${arrayField.name}Length;
var value = new List<IPlcValue>();
while(readBuffer.getPos() < ${arrayField.name}EndPos) {
value.Add(
<#if elementTypeReference.isSimpleTypeReference()>
<#if elementTypeReference.isSimpleTypeReference()>
new ${helper.getPlcValueTypeForTypeReference(elementTypeReference)}(${helper.getReadBufferReadMethodCall(elementTypeReference.asSimpleTypeReference().orElseThrow(), "", arrayField)})
<#else>${elementTypeReference.asNonSimpleTypeReference().orElseThrow().name}IO.StaticParse(readBuffer
<#if elementTypeReference.asNonSimpleTypeReference().orElseThrow().params.isPresent()>,
<#list elementTypeReference.asNonSimpleTypeReference().orElseThrow().params.orElseThrow() as parserArgument>
<#else>${elementTypeReference.asNonSimpleTypeReference().orElseThrow().name}IO.StaticParse(readBuffer
<#if elementTypeReference.asNonSimpleTypeReference().orElseThrow().params.isPresent()>,
<#list elementTypeReference.asNonSimpleTypeReference().orElseThrow().params.orElseThrow() as parserArgument>
(${helper.getLanguageTypeNameForTypeReference(helper.getArgumentType(elementTypeReference, parserArgument?index))}) (${helper.toParseExpression(arrayField,elementTypeReference, parserArgument,parserArguments)})
<#sep>, </#sep>
</#list>
</#if>
<#sep>, </#sep>
</#list>
</#if>
)
</#if>
</#if>
);
}
<#-- A terminated array keeps on reading data as long as the termination expression evaluates to false -->
<#elseif arrayField.isTerminatedArrayField()>
<#elseif arrayField.isTerminatedArrayField()>
// Terminated array
var ${arrayField.name} = new List<${helper.getLanguageTypeNameForField(arrayField)}>();
while(!((boolean) (${helper.toParseExpression(arrayField, helper.boolTypeReference, arrayField.loopExpression,parserArguments)}))) {
${arrayField.name}.Add(<#if elementTypeReference.isSimpleTypeReference()>${helper.getReadBufferReadMethodCall(elementTypeReference.asSimpleTypeReference().orElseThrow(), "", arrayField)}<#else>${elementTypeReference.asNonSimpleTypeReference().orElseThrow().name}IO.StaticParse(readBuffer<#if elementTypeReference.asNonSimpleTypeReference().orElseThrow().params.isPresent()>, <#list elementTypeReference.asNonSimpleTypeReference().orElseThrow().params.orElseThrow() as parserArgument>(${helper.getLanguageTypeNameForTypeReference(helper.getArgumentType(elementTypeReference, parserArgument?index))}) (${helper.toParseExpression(arrayField, elementTypeReference, parserArgument, parserArguments)})<#sep>, </#sep></#list></#if>)</#if>);

<#-- After parsing, update the current position, but only if it's needed -->
<#if arrayField.loopExpression.contains("curPos")>
<#if arrayField.loopExpression.contains("curPos")>
curPos = readBuffer.getPos() - startPos;
</#if>
</#if>
}
</#if>
</#if>
</#if>
<#if arrayField.name == "value">
Expand Down Expand Up @@ -218,8 +222,10 @@ namespace org.apache.plc4net.drivers.${protocolName?replace("-", "")}.${outputFl
<#-- In this case we need to wrap each field in a IPlcValue that matches it's natural type -->
var _map = new Dictionary<string, IPlcValue>();
<#list case.fields as field>
<#if field.isArrayField()>
<#if field.isArrayField() && field.asArrayField().orElseThrow().type.elementTypeReference.isByteBased()>
<#assign field=field.asArrayField().orElseThrow()>
_map["${field.name}"] = new PlcRawByteArray(${field.name});
<#elseif field.isArrayField()>
_map["${field.name}"] = new PlcList(${field.name});
<#elseif field.isPropertyField()>
<#assign field=field.asPropertyField().orElseThrow()>
Expand Down Expand Up @@ -321,7 +327,7 @@ namespace org.apache.plc4net.drivers.${protocolName?replace("-", "")}.${outputFl
return new Plc${case.name}(value);
</#switch>
</#if>
}<#sep> else </#sep></#list>
} </#list>
<#if !defaultCaseOutput>
return null;
</#if>
Expand Down Expand Up @@ -422,7 +428,7 @@ namespace org.apache.plc4net.drivers.${protocolName?replace("-", "")}.${outputFl
</#switch>
</#list>
return writeBuffer;
}<#sep> else </#sep></#list>
} </#list>
<#if !defaultCaseOutput>
return null;
</#if>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,15 @@ func ${type.name}ParseWithBuffer(ctx context.Context, readBuffer utils.ReadBuffe
<#assign arrayElementType = arrayField.type.elementTypeReference>

// Array Field (${arrayField.name})
<#if arrayElementType.isByteBased()>
<#if !field.isCountArrayField() && !field.isLengthArrayField()>
return nil, errors.Wrap(_${arrayField.name}Err, "Array fields of type byte only support 'count' and 'length' loop-types.")<@emitImport import="github.com/pkg/errors" />
</#if>
${arrayField.name}, _${arrayField.name}Err := readBuffer.ReadByteArray("${arrayField.name}", int(${helper.toParseExpression(null, null, arrayField.loopExpression, parserArguments)}))<#if arrayField.loopExpression.contains("CEIL")><@emitImport import="math" /></#if>
if _${arrayField.name}Err != nil {
return nil, errors.Wrap(_${arrayField.name}Err, "Error parsing '${arrayField.name}' field")<@emitImport import="github.com/pkg/errors" />
}
<#else>
var ${arrayField.name} []api.PlcValue
for i := 0; i < int(${helper.toParseExpression(null, null, arrayField.loopExpression, parserArguments)}); i++ {
_item, _itemErr := <#if arrayElementType.isSimpleTypeReference()>${helper.getReadBufferReadMethodCall(arrayField.name, arrayElementType.asSimpleTypeReference().orElseThrow(), arrayField)}<#else>Complex type array in data-io parsing currently not implemented</#if>
Expand All @@ -128,6 +137,7 @@ func ${type.name}ParseWithBuffer(ctx context.Context, readBuffer utils.ReadBuffe
}
${arrayField.name} = append(${arrayField.name}, ${helper.getPlcValueTypeForTypeReference(arrayElementType)}(_item))
}
</#if>
<#if arrayField.name == "value">
<#assign valueDefined=true>
</#if>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class ${type.name}:
<#if discriminatorType.isEnumTypeReference()>
${helper.getLanguageTypeNameForTypeReference(discriminatorType)}.${helper.toParseExpression(dataIoTypeDefinition.switchField.orElseThrow(), discriminatorType, discriminatorValueTerm, parserArguments)}
<#else>
${helper.camelCaseToSnakeCase(helper.toParseExpression(dataIoTypeDefinition.switchField.orElseThrow(), discriminatorType, discriminatorValueTerm, parserArguments))}
${helper.toParseExpression(dataIoTypeDefinition.switchField.orElseThrow(), discriminatorType, discriminatorValueTerm, parserArguments)}
</#if>
<#sep> and </#sep>
</#list>
Expand Down Expand Up @@ -131,7 +131,7 @@ class ${type.name}:
)
</@compress>

<#-- A terminated array keeps on reading data as long as the termination expression evaluates to false -->
<#-- A terminated array keeps on reading data as long as the termination expression evaluates to False -->
<#elseif arrayField.isTerminatedArrayField()>
# Terminated array
${arrayField.name}: ${helper.getNonPrimitiveLanguageTypeNameForField(arrayField)} = new LinkedList<>()
Expand Down Expand Up @@ -377,8 +377,8 @@ class ${type.name}:
for val in values.get_list():
<#if elementTypeReference.isByteBased()>
<@emitImport import="from typing import List" />
value: list[byte] = val.get_raw()
write_buffer.write_byte_array("", value)
value: ${helper.getLanguageTypeNameForField(arrayField)} = val.get_raw()
write_buffer.write_byte_array("", value)
<#else>
value: ${helper.getLanguageTypeNameForTypeReference(elementTypeReference)} = val.get_${helper.camelCaseToSnakeCase(helper.getLanguageTypeNameForTypeReference(elementTypeReference)?cap_first)}()
${helper.getWriteBufferWriteMethodCall(elementTypeReference.asSimpleTypeReference().orElseThrow(), "(" + arrayField.name + ")", arrayField)}
Expand All @@ -387,7 +387,7 @@ class ${type.name}:

<#if case.name == "BOOL">
while write_buffer.getPos() < len(write_buffer.get_data()):
write_buffer.write_bit(false)
write_buffer.write_bit(False)
</#if>
<#break>
<#case "const">
Expand Down
4 changes: 3 additions & 1 deletion plc4c/generated-sources/modbus/include/modbus_data_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ enum plc4c_modbus_read_write_modbus_data_type {
plc4c_modbus_read_write_modbus_data_type_CHAR = 24,
plc4c_modbus_read_write_modbus_data_type_WCHAR = 25,
plc4c_modbus_read_write_modbus_data_type_STRING = 26,
plc4c_modbus_read_write_modbus_data_type_WSTRING = 27
plc4c_modbus_read_write_modbus_data_type_WSTRING = 27,
plc4c_modbus_read_write_modbus_data_type_RAW_COIL = 98,
plc4c_modbus_read_write_modbus_data_type_RAW_REGISTER = 99
};
typedef enum plc4c_modbus_read_write_modbus_data_type plc4c_modbus_read_write_modbus_data_type;

Expand Down
50 changes: 50 additions & 0 deletions plc4c/generated-sources/modbus/src/data_item.c
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,42 @@ plc4c_return_code plc4c_modbus_read_write_data_item_parse(plc4x_spi_context ctx,
}
*data_item = plc4c_data_create_list_data(value);

} else if(dataType == plc4c_modbus_read_write_modbus_data_type_RAW_COIL) { /* RawByteArray */

// Array field (value)
// Count array
plc4c_list* value;
plc4c_utils_list_create(&value);
int itemCount = (int) plc4c_spi_evaluation_helper_ceil((numberOfValues) / (8));
for(int curItem = 0; curItem < itemCount; curItem++) {
char* _val = malloc(sizeof(char) * 1);
_res = plc4c_spi_read_char(readBuffer, (char*) _val);
if(_res != OK) {
return _res;
}
plc4c_data* _item = plc4c_data_create_byte_data(*_val);
plc4c_utils_list_insert_head_value(value, _item);
}
*data_item = plc4c_data_create_list_data(value);

} else if(dataType == plc4c_modbus_read_write_modbus_data_type_RAW_REGISTER) { /* RawByteArray */

// Array field (value)
// Count array
plc4c_list* value;
plc4c_utils_list_create(&value);
int itemCount = (int) (numberOfValues) * (2);
for(int curItem = 0; curItem < itemCount; curItem++) {
char* _val = malloc(sizeof(char) * 1);
_res = plc4c_spi_read_char(readBuffer, (char*) _val);
if(_res != OK) {
return _res;
}
plc4c_data* _item = plc4c_data_create_byte_data(*_val);
plc4c_utils_list_insert_head_value(value, _item);
}
*data_item = plc4c_data_create_list_data(value);

}

return OK;
Expand Down Expand Up @@ -844,6 +880,12 @@ plc4c_return_code plc4c_modbus_read_write_data_item_serialize(plc4x_spi_context
}
} else if(dataType == plc4c_modbus_read_write_modbus_data_type_WCHAR) { /* List */

// Array field
} else if(dataType == plc4c_modbus_read_write_modbus_data_type_RAW_COIL) { /* RawByteArray */

// Array field
} else if(dataType == plc4c_modbus_read_write_modbus_data_type_RAW_REGISTER) { /* RawByteArray */

// Array field
}
return OK;
Expand Down Expand Up @@ -1022,6 +1064,14 @@ uint16_t plc4c_modbus_read_write_data_item_length_in_bits(plc4x_spi_context ctx,

// Array field
lengthInBits += 16 * plc4c_utils_list_size(data_item->data.list_value);
} else if(dataType == plc4c_modbus_read_write_modbus_data_type_RAW_COIL) { /* RawByteArray */

// Array field
lengthInBits += 8 * plc4c_utils_list_size(data_item->data.list_value);
} else if(dataType == plc4c_modbus_read_write_modbus_data_type_RAW_REGISTER) { /* RawByteArray */

// Array field
lengthInBits += 8 * plc4c_utils_list_size(data_item->data.list_value);
}
return lengthInBits;
}
Expand Down
20 changes: 19 additions & 1 deletion plc4c/generated-sources/modbus/src/modbus_data_type.c
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,17 @@ plc4c_modbus_read_write_modbus_data_type plc4c_modbus_read_write_modbus_data_typ
if(strcmp(value_string, "WSTRING") == 0) {
return plc4c_modbus_read_write_modbus_data_type_WSTRING;
}
if(strcmp(value_string, "RAW_COIL") == 0) {
return plc4c_modbus_read_write_modbus_data_type_RAW_COIL;
}
if(strcmp(value_string, "RAW_REGISTER") == 0) {
return plc4c_modbus_read_write_modbus_data_type_RAW_REGISTER;
}
return -1;
}

int plc4c_modbus_read_write_modbus_data_type_num_values() {
return 27;
return 29;
}

plc4c_modbus_read_write_modbus_data_type plc4c_modbus_read_write_modbus_data_type_value_for_index(int index) {
Expand Down Expand Up @@ -232,6 +238,12 @@ plc4c_modbus_read_write_modbus_data_type plc4c_modbus_read_write_modbus_data_typ
case 26: {
return plc4c_modbus_read_write_modbus_data_type_WSTRING;
}
case 27: {
return plc4c_modbus_read_write_modbus_data_type_RAW_COIL;
}
case 28: {
return plc4c_modbus_read_write_modbus_data_type_RAW_REGISTER;
}
default: {
return -1;
}
Expand Down Expand Up @@ -321,6 +333,12 @@ uint8_t plc4c_modbus_read_write_modbus_data_type_get_data_type_size(plc4c_modbus
case plc4c_modbus_read_write_modbus_data_type_LINT: { /* '9' */
return 8;
}
case plc4c_modbus_read_write_modbus_data_type_RAW_COIL: { /* '98' */
return 1;
}
case plc4c_modbus_read_write_modbus_data_type_RAW_REGISTER: { /* '99' */
return 2;
}
default: {
return 0;
}
Expand Down
Loading
Loading