Skip to content

Instantly share code, notes, and snippets.

@SpareSimian
Last active July 14, 2025 23:20
Show Gist options
  • Save SpareSimian/980944221f3f04b1e2758ed02a6c16bd to your computer and use it in GitHub Desktop.
Save SpareSimian/980944221f3f04b1e2758ed02a6c16bd to your computer and use it in GitHub Desktop.
Python script for CODESYS to export project structs and enums to equivalent C++ types in header files
# based on scripts in https://github.com/arwie/controlOS_demo/
from __future__ import print_function
from scriptengine import projects #type:ignore
from os import path, mkdir
from itertools import chain
import re
project = projects.primary
export_path = path.join(path.dirname(project.path), 'codesys_types')
print('codesys_types path:', export_path)
if not path.exists(export_path):
mkdir(export_path)
types = {
'BOOL': ('std::int16_t', 'bool'),
'SINT': ('std::int8_t', 'int'),
'INT': ('std::int16_t', 'int'),
'DINT': ('std::int32_t', 'int'),
'LINT': ('std::int64_t', 'int'),
'USINT': ('std::uint8_t', 'int'),
'BYTE': ('std::uint8_t', 'int'),
'UINT': ('std::uint16_t', 'int'),
'WORD': ('std::uint16_t', 'int'),
'UDINT': ('std::uint32_t', 'int'),
'DWORD': ('std::uint32_t', 'int'),
'ULINT': ('std::uint64_t', 'int'),
'LWORD': ('std::uint64_t', 'int'),
'REAL': ('float', 'float'),
'LREAL': ('double', 'float'),
}
# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
export_enums = ['E_Command']
for enum_name in export_enums:
print('exporting enum:', enum_name)
objects = project.find(enum_name, True)
if not objects:
print('Undefined symbol: ', enum_name)
continue
enum_text = objects[0].textual_declaration.text
# extract just the part between the parens
members_index = enum_text.find('\n(\n')
members_end_index = enum_text.find('\n)', members_index)
print('members_index: ', members_index)
print('members_end_index: ', members_end_index)
members_text = enum_text[members_index:members_end_index]
print('members_text: ', members_text)
members = []
for match in re.findall(r'^\s*(\w+)\s*(:=\s*([^,\s]+))?\s*,?\s*(//.*)?$', members_text, re.MULTILINE):
member_name, member_value, member_comment = match[0], match[2], match[3]
if member_value:
members.append(member_name + ' = ' + member_value)
else:
members.append(member_name)
# get the base type
enum_base_type = "BYTE"
for match in re.findall(r'^\s*}}\s*(\w+)\s*;', enum_text, re.MULTILINE):
enum_base_type = match[0]
enum_base_ctype = types[enum_base_type][0]
with open(path.join(export_path, enum_name+'.h'), 'w') as file:
file.write('#include <cstdint>\n')
file.write('namespace CODESYSApp {\n')
file.write('enum class {} : {} {{\n'.format(enum_name, enum_base_ctype))
file.write(',\n'.join('\t{}'.format(m) for m in members))
file.write('\n};\n}\n')
# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
export_structs = ['SharedMemoryHdr', 'SubDeviceCommand', 'SubDeviceStatus']
for struct_name in export_structs:
print('exporting struct:', struct_name)
objects = project.find(struct_name, True)
if not objects:
print('Undefined symbol: ', struct_name)
continue
struct_text = objects[0].textual_declaration.text
imports = []
fields = []
for match in re.findall(r'^\s*(\w+)\s*:\s*(.+)\s*;', struct_text, re.MULTILINE):
field_name, field_type = match[0], match[1]
field_array = None
array_match = re.search(r'ARRAY *\[(\d+)\.\.(\d+)\] OF (\w+)', field_type)
if array_match:
print("exporting array of", array_match.group(3))
field_type = array_match.group(3)
field_array = int(array_match.group(2)) - int(array_match.group(1)) + 1
if field_type in types:
field_ctype, field_ptype = types[field_type]
else:
field_ctype = field_ptype = field_type
imports.append(field_type)
if not field_type in export_structs and not field_type in export_enums:
export_structs.append(field_type)
if field_array:
field_name = '{}[{}]'.format(field_name, field_array)
fields.append((field_name, field_ctype, field_ptype))
with open(path.join(export_path, struct_name+'.h'), 'w') as file:
file.write('\n'.join(
chain.from_iterable((part,) if isinstance(part, str) else part for part in (
'#include <cstdint>',
('#include "{}.h"'.format(m, m) for m in imports),
'namespace CODESYSApp {',
'struct {} {{'.format(struct_name),
('\t{} {};'.format(f[1], f[0]) for f in fields),
'};',
'}',
))
))
print('All done!')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment