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

- Started working on the parser and serializer code ... WIP #163

Merged
merged 4 commits into from
Jun 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,161 @@ ${helper.setConstants(type, protocolName, outputFlavor)}${helper.getSourceDirect
under the License.
*/

#include <stdio.h>
#include <plc4c/spi/read_buffer.h>
#include <plc4c/spi/write_buffer.h>
#include <plc4c/spi/evaluation_helper.h>
<#-- Add any needed import statements -->
<#if helper.getIncludeTypesForCFiles()?has_content>
<#list helper.getIncludeTypesForCFiles() as typeImport>
#include "${typeImport}.h"
</#list>
</#if>
<#-- Helper function to get the discriminator for a given enum type constant -->
<#if helper.isAbstractType(type)>

#include "${helper.camelCaseToSnakeCase(typeName)}.h"
// Array of discriminator values that match the enum type constants.
// (The order is identical to the enum constants so we can use the
// enum constant to directly access a given types discriminator values)
const plc4c_${helper.getCTypeName(type.name)}_discriminator plc4c_${helper.getCTypeName(type.name)}_discriminators[] = {
<#if helper.getDiscriminatorValues()?has_content>
<#list helper.getDiscriminatorValues() as name, typeValues>
{/* ${helper.getCTypeName(name)} */
<#list typeValues as key, value>.${key} = <#if value??>${value}<#else>-1</#if><#sep>, </#list>}<#sep >,
</#list>
</#if>

};

// Function returning the discriminator values for a given type constant.
plc4c_${helper.getCTypeName(type.name)}_discriminator plc4c_${helper.getCTypeName(type.name)}_get_discriminator(plc4c_${helper.getCTypeName(type.name)}_type type) {
return plc4c_${helper.getCTypeName(type.name)}_discriminators[type];
}
</#if>

// Parse function.
plc4c_return_code plc4c_${helper.getCTypeName(type.name)}_parse(plc4c_spi_read_buffer* buf, <#if type.parserArguments?has_content><#list type.parserArguments as parserArgument>${helper.getLanguageTypeName(parserArgument.type)}<#if !helper.isSimpleType(parserArgument.type)>*</#if> ${parserArgument.name}<#sep>, </#list>, </#if>plc4c_${helper.getCTypeName(type.name)}** message) {
uint16_t startPos = plc4c_spi_read_get_pos(buf);
uint16_t curPos;

// Pointer to the parsed datastructure.
void* msg = NULL;
<#if type.getPropertyFields()?has_content>
// Factory function that allows filling the properties of this type
void (*factory_ptr)(<#list type.getPropertyFields() as field>${helper.getLanguageTypeNameForField(field)}<#sep >, </#list>) = NULL;
</#if>
<#list type.fields as field>
<#switch field.typeName>
<#case "checksum">

// Checksum Field (${field.name})
${helper.getLanguageTypeNameForField(field)} ${field.name} = ${helper.getNullValueForType(field.type)};
{
// Create an array of all the bytes read in this message element so far.
byte[] checksumRawData = plc4c_spi_read_get_bytes(buf, startPos, plc4c_spi_read_get_pos(buf));
${helper.getLanguageTypeNameForField(field)} _${field.name}Ref = ${helper.getReadBufferReadMethodCall(field.type)?no_esc};
${field.name} = (${helper.getLanguageTypeNameForField(field)}) (${helper.toParseExpression(field, field.checksumExpression, type.parserArguments)});
if(${field.name} != _${field.name}Ref) {
return PARSE_ERROR;
// throw new ParseException(String.format("Checksum verification failed. Expected %04X but got %04X",_${field.name}Ref & 0xFFFF, ${field.name} & 0xFFFF));
}
}
<#break>
<#case "const">

// Const Field (${field.name})
${helper.getLanguageTypeNameForField(field)} ${field.name} = ${helper.getReadBufferReadMethodCall(field.type)?no_esc};
if(${field.name} != ${helper.getCTypeName(type.name)?upper_case}_${helper.camelCaseToSnakeCase(field.name)?upper_case}) {
return PARSE_ERROR;
// throw new ParseException("Expected constant value " + ${typeName}.${field.name?upper_case} + " but got " + ${field.name});
}
<#break>
<#case "enum">

// Enum field (${field.name})
${helper.getLanguageTypeNameForField(field)} ${field.name} = ${helper.getReadBufferReadMethodCall(helper.getEnumBaseType(field.type))?no_esc};
<#break>
<#case "discriminator">

// Discriminator Field (${field.name}) (Used as input to a switch field)
${helper.getLanguageTypeNameForField(field)} ${field.name} = ${helper.getReadBufferReadMethodCall(field.type)?no_esc};
<#break>
<#case "implicit">

// Implicit Field (${field.name}) (Used for parsing, but it's value is not stored as it's implicitly given by the objects content)
${helper.getLanguageTypeNameForField(field)} ${field.name} = ${helper.getReadBufferReadMethodCall(field.type)?no_esc};
<#break>
<#case "manual">

// Manual Field (${field.name})
${helper.getLanguageTypeNameForField(field)} ${field.name} = (${helper.getLanguageTypeNameForField(field)}) (${helper.toParseExpression(field, field.parseExpression, type.parserArguments)});
<#break>
<#case "optional">

// Optional Field (${field.name}) (Can be skipped, if a given expression evaluates to false)
<#if field.conditionExpression.contains("curPos")>
curPos = plc4c_spi_read_get_pos(buf) - startPos;
</#if>
${helper.getLanguageTypeNameForField(field)}<#if !helper.isSimpleType(field.type)>*</#if> ${field.name} = <#if !helper.isSimpleType(field.type)>NULL<#else>0</#if>;
if(${helper.toParseExpression(field, field.conditionExpression, type.parserArguments)?no_esc}) {
<#if helper.isSimpleType(field.type)>
${field.name} = ${helper.getReadBufferReadMethodCall(field.type)?no_esc};
<#else>
plc4c_${helper.getCTypeName(field.type.name)}_parse(buf<#if field.params?has_content>, <#list field.params as parserTerm>${helper.toParseExpression(field, parserTerm, type.parserArguments)}<#sep>, </#sep></#list></#if>, &${field.name});
</#if>
}
<#break>
<#case "padding">

// Padding Field (${field.name})
bool _${field.name}NeedsPadding = (bool) ((plc4c_spi_read_has_more(buf, ${helper.getNumBits(field.type)})) && (${helper.toParseExpression(field, field.paddingCondition, type.parserArguments)?no_esc}));
if(_${field.name}NeedsPadding) {
// Just read the padding data and ignore it
${helper.getReadBufferReadMethodCall(field.type)?no_esc};
}
<#break>
<#case "reserved">

// Reserved Field (Compartmentalized so the "reserved" variable can't leak)
{
${helper.getLanguageTypeNameForField(field)} reserved = ${helper.getReadBufferReadMethodCall(field.type)?no_esc};
if(reserved != ${helper.getReservedValue(field)}) {
printf("Expected constant value '%d' but got '%d' for reserved field.", ${field.referenceValue}, reserved);
}
}
<#break>
<#case "simple">

// Simple Field (${field.name})
<#-- Inizialize a local variable with the simple type (Intentionally keeping the java-style names so they can be used in expressions) -->
<#if helper.isSimpleType(field.type)>
${helper.getLanguageTypeNameForField(field)} ${field.name} = ${helper.getReadBufferReadMethodCall(field.type)?no_esc};
<#else>
<#-- Inizialize a local variable with the complex type (Intentionally keeping the java-style names so they can be used in expressions) -->
${helper.getLanguageTypeNameForField(field)}* ${field.name} = NULL;
plc4c_${helper.getCTypeName(field.type.name)}_parse(buf<#if field.params?has_content>, <#list field.params as parserTerm>${helper.toParseExpression(field, parserTerm, type.parserArguments)}<#sep>, </#sep></#list></#if>, &${field.name});
</#if>
<#break>
<#case "switch">

// Switch Field (Depending on the discriminator values, passes the instantiation to a sub-type)
<#list field.cases as case>
<#if case.discriminatorValues?has_content>if(<#list case.discriminatorValues as discriminatorValue><#if case.discriminatorValues?size &gt; 1>(</#if>${helper.toSwitchExpression(field.discriminatorNames[discriminatorValue?index])?no_esc} == ${discriminatorValue}<#if case.discriminatorValues?size &gt; 1>)</#if><#sep> && </#sep></#list>) </#if>{
plc4c_${helper.getCTypeName(case.name)}_parse(buf<#if case.parserArguments?has_content>, <#list case.parserArguments as parserArgument>${parserArgument.name}<#sep>, </#sep></#list></#if>, &msg);
}<#sep> else </#sep>
</#list>
<#break>
<#case "virtual">

// Virtual field (Just declare a local variable so we can access it in the parser)
${helper.getLanguageTypeNameForField(field)} ${field.name} = (${helper.getLanguageTypeNameForField(field)}) (${helper.toParseExpression(field, field.valueExpression, type.parserArguments)});
<#break>
</#switch>
</#list>

return OK;
}

plc4c_return_code plc4c_${helper.getCTypeName(type.name)}_serialize(plc4c_spi_write_buffer* buf, plc4c_${helper.getCTypeName(type.name)}* message) {
return OK;
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,68 @@ extern "C" {
#include <stdbool.h>
#include <stdint.h>
#include <plc4c/utils/list.h>
<#if helper.getComplexTypeReferencesInFields()?has_content>
<#list helper.getComplexTypeReferencesInFields() as complexType>
#include "${helper.camelCaseToSnakeCase(complexType.getName())}.h"
</#list>
<#--
Add any import statements for partent-types, complex types used in properties or parser arguments.
-->
<#if helper.getIncludeTypesForHFiles()?has_content>
<#list helper.getIncludeTypesForHFiles() as typeImport>
#include "${typeImport}.h"
</#list>
</#if>
<#--
Abstract types only occur as parents in the case of discriminated types.
As C doesn't have any form of polymorph type-system, we need to save the type information in the
data-structure. So with this enum we're doing exactly this. It will be used in the serializers and
parsers and stored in discriminated types.
-->
<#if helper.isAbstractType(type)>

// Structure used to contain the discriminator values for discriminated types using this as a parent
struct plc4c_${helper.getCTypeName(type.name)}_discriminator {
<#if helper.getDiscriminatorTypes()?has_content>
<#list helper.getDiscriminatorTypes()?keys as discriminatorName>
${helper.getLanguageTypeName(helper.getDiscriminatorTypes()[discriminatorName])} ${discriminatorName};
</#list>
</#if>
};
typedef struct plc4c_${helper.getCTypeName(type.name)}_discriminator plc4c_${helper.getCTypeName(type.name)}_discriminator;

// Enum assigning each sub-type an individual id.
enum plc4c_${helper.getCTypeName(type.name)}_type {
<#list helper.getDiscriminatedSubTypes() as subtype>
plc4c_${helper.getCTypeName(type.name)}_type_${helper.getCTypeName(subtype.name)} = ${subtype?index}<#sep>,
</#list>
};
typedef enum plc4c_${helper.getCTypeName(type.name)}_type plc4c_${helper.getCTypeName(type.name)}_type;

// Function to get the discriminator values for a given type.
plc4c_${helper.getCTypeName(type.name)}_discriminator plc4c_${helper.getCTypeName(type.name)}_get_discriminator(plc4c_${helper.getCTypeName(type.name)}_type type);
</#if>
<#--#if helper.isDiscriminatedType(type)>

// Discriminator values used by the parser to determine the type to be used. All values have to apply.
const void*[] DISCRIMINATOR_VALUES = {
<#list type.discriminatorValues as discriminatorValue>
<#- There are rare occasions where the discriminator is defined by a parser argument, in this case we currently can't detect the type ->
(${helper.getDiscriminatorConstantType(type, discriminatorValue?index)}) ${discriminatorValue}<#sep>, </#sep>
</#list>
};
</#if-->
<#--
When using const fields, output the constant reference values
as global const values so we can use them elsewhere.
-->
<#if type.constFields?has_content>

// Constant values.
<#list type.constFields as field>
const ${helper.getLanguageTypeNameForField(field)} ${helper.getCTypeName(type.name)?upper_case}_${helper.camelCaseToSnakeCase(field.name)?upper_case} = ${field.referenceValue};
</#list>
</#if>

<#--
Create the general data-structure for this type
-->
struct plc4c_${helper.getCTypeName(type.name)} {
<#if helper.isAbstractType(type)>
plc4c_${helper.getCTypeName(type.name)}_type _type;
Expand All @@ -58,27 +114,21 @@ struct plc4c_${helper.getCTypeName(type.name)} {
plc4c_${helper.getCTypeName(type.parentType.name)}_type _type;
</#if>
<#list type.allPropertyFields as field>
${helper.getLanguageTypeNameForField(field)} ${helper.camelCaseToSnakeCase(field.name)}${helper.getTypeSizeForField(field)}<#if field.loopType??>${helper.getLoopExpressionSuffix(field)}</#if>;
${helper.getLanguageTypeNameForField(field)}<#if !helper.isSimpleType(field.type)>*</#if> ${helper.camelCaseToSnakeCase(field.name)}${helper.getTypeSizeForField(field)}<#if field.loopType??>${helper.getLoopExpressionSuffix(field)}</#if>;
</#list>
};
typedef struct plc4c_${helper.getCTypeName(type.name)} plc4c_${helper.getCTypeName(type.name)};

<#--
Abstract types only occur as parents in the case of discriminated types.
As C doesn't have any form of polymorph type-system, we need to save the type information in the
data-structure. So with this enum we're doing exactly this. It will be used in the serializers and
parsers and stored in discriminated types.
Define the parse-method for elements of this tpye
-->
<#if helper.isAbstractType(type)>
// Enum assigning each sub-type an individual id.
enum plc4c_${helper.getCTypeName(type.name)}_type {
<#list helper.getDiscriminatedSubTypes(type) as subtype>
plc4c_${helper.getCTypeName(type.name)}_type_${helper.getCTypeName(subtype.name)} = ${subtype?index};
</#list>
}
typedef enum plc4c_${helper.getCTypeName(type.name)}_type plc4c_${helper.getCTypeName(type.name)}_type;
plc4c_return_code plc4c_${helper.getCTypeName(type.name)}_parse(plc4c_spi_read_buffer* buf, <#if type.parserArguments?has_content><#list type.parserArguments as parserArgument>${helper.getLanguageTypeName(parserArgument.type)}<#if !helper.isSimpleType(parserArgument.type)>*</#if> ${parserArgument.name}<#sep>, </#list>, </#if>plc4c_${helper.getCTypeName(type.name)}** message);

<#--
Define the serialize-method for elements of this tpye
-->
plc4c_return_code plc4c_${helper.getCTypeName(type.name)}_serialize(plc4c_spi_write_buffer* buf, plc4c_${helper.getCTypeName(type.name)}* message);

</#if>
#ifdef __cplusplus
}
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ public<#if type.abstract> abstract</#if> class ${typeName}<#if type.parentType??
public Object[] getDiscriminatorValues() {
return DISCRIMINATOR_VALUES;
}

public
</#if>

<#list type.propertyFields as field>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,10 +201,10 @@
]

['false','0x08','false' ModbusPDUDiagnosticRequest
// TODO: Implement the sub-request discriminated type [simple uint 8 'subfunction']
// TODO: Implement the sub-request discriminated type
]
['false','0x08','true' ModbusPDUDiagnosticResponse
// TODO: Implement the sub-request discriminated type [simple uint 8 'subfunction']
// TODO: Implement the sub-request discriminated type
]

['false','0x0B','false' ModbusPDUGetComEventCounterRequest
Expand Down
4 changes: 2 additions & 2 deletions protocols/s7/src/main/resources/protocols/s7/s7.mspec
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@
// Payloads

[discriminatedType 'S7Payload' [uint 8 'messageType', S7Parameter 'parameter']
[typeSwitch 'parameter.discriminatorValues[0]', 'messageType'
[typeSwitch 'parameter.parameterType', 'messageType'
['0x04','0x03' S7PayloadReadVarResponse
[array S7VarPayloadDataItem 'items' count 'CAST(parameter, S7ParameterReadVarResponse).numItems' ['lastItem']]
]
Expand Down Expand Up @@ -366,7 +366,7 @@
]
]

[enum int 8 'COTPTpduSize' [uint 8 'sizeInBytes']
[enum int 8 'COTPTpduSize' [uint 16 'sizeInBytes']
['0x07' SIZE_128 ['128']]
['0x08' SIZE_256 ['256']]
['0x09' SIZE_512 ['512']]
Expand Down
2 changes: 2 additions & 0 deletions sandbox/plc4c/api/include/plc4c/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ typedef enum plc4c_return_code {
NO_TRANSPORT_AVAILABLE,
UNKNOWN_TRANSPORT,

PARSE_ERROR,

UNKNOWN_ERROR,
INTERNAL_ERROR
} plc4c_return_code;
Expand Down
4 changes: 2 additions & 2 deletions sandbox/plc4c/drivers/modbus/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@
]]

include_directories("include" "../../api/include" "../../spi/include"
"${PLC4C_ROOT_DIR}/generated-sources/plc4x/modbus/includes")
"${PLC4C_ROOT_DIR}/generated-sources/modbus/includes")

# Add the sources
file(GLOB sources "src/*.c")

# Add the generated sources
file(GLOB generatedSources "${PLC4C_ROOT_DIR}/target/generated-sources/plc4x/modbus/src/*.c")
file(GLOB generatedSources "${PLC4C_ROOT_DIR}/generated-sources/modbus/src/*.c")

add_library(plc4c-driver-modbus ${sources} ${generatedSources})

Expand Down
4 changes: 2 additions & 2 deletions sandbox/plc4c/drivers/s7/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@
]]

include_directories("include" "../../api/include" "../../spi/include"
"${PLC4C_ROOT_DIR}/generated-sources/plc4x/s7/includes")
"${PLC4C_ROOT_DIR}/generated-sources/s7/includes")

# Add the sources
file(GLOB sources "src/*.c")

# Add the generated sources
file(GLOB generatedSources "${PLC4C_ROOT_DIR}/target/generated-sources/plc4x/s7/src/*.c")
file(GLOB generatedSources "${PLC4C_ROOT_DIR}/generated-sources/s7/src/*.c")

add_library(plc4c-driver-s7 ${sources} ${generatedSources})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,17 @@ extern "C" {
#include <stdint.h>
#include <plc4c/utils/list.h>

// Constant values.
const uint16_t MODBUS_READ_WRITE_MODBUS_CONSTANTS_MODBUS_TCP_DEFAULT_PORT = 502;

struct plc4c_modbus_read_write_modbus_constants {
};
typedef struct plc4c_modbus_read_write_modbus_constants plc4c_modbus_read_write_modbus_constants;

plc4c_return_code plc4c_modbus_read_write_modbus_constants_parse(plc4c_spi_read_buffer* buf, plc4c_modbus_read_write_modbus_constants** message);

plc4c_return_code plc4c_modbus_read_write_modbus_constants_serialize(plc4c_spi_write_buffer* buf, plc4c_modbus_read_write_modbus_constants* message);

#ifdef __cplusplus
}
#endif
Expand Down