Skip to content

fix: Add missing IEC 61131-3 type mappings to OPC-UA plugin - #93

Merged
thiagoralves merged 3 commits into
developmentfrom
fix/opcua-sint-type-mapping
Jan 29, 2026
Merged

fix: Add missing IEC 61131-3 type mappings to OPC-UA plugin#93
thiagoralves merged 3 commits into
developmentfrom
fix/opcua-sint-type-mapping

Conversation

@thiagoralves

Copy link
Copy Markdown
Contributor

Summary

  • Fixes BadInternalError when reading SINT variables via OPC-UA clients (e.g., UaExpert)
  • Adds missing IEC 61131-3 type mappings: SINT, USINT, UINT, WORD, UDINT, DWORD, ULINT, LWORD, LREAL
  • Adds value conversion handling for both read and write operations

Problem

When reading a SINT variable (e.g., TON0.STATE) via an OPC-UA client, the server returned BadInternalError with:

AttributeError: 'int' object has no attribute 'VariantType'

This occurred because SINT was not mapped in map_plc_to_opcua_type(), defaulting to ua.VariantType.Variant, which the asyncua library couldn't serialize with raw integer values.

Changes

opcua_utils.py:

  • Added type mappings for all missing IEC 61131-3 integer and float types
  • Added value conversion handling in convert_value_for_opcua() and convert_value_for_plc()

test_type_conversions.py:

  • Added tests for new type mappings
  • Added SINT conversion and roundtrip tests

Test plan

  • Read SINT variable from OPC-UA client (UaExpert)
  • Write SINT variable from OPC-UA client
  • Run existing OPC-UA type conversion tests

🤖 Generated with Claude Code

SINT variables were not mapped in map_plc_to_opcua_type(), causing
BadInternalError when OPC-UA clients tried to read them. The asyncua
library failed to serialize responses because unmapped types defaulted
to ua.VariantType.Variant, which couldn't handle raw integer values.

Added type mappings for:
- SINT (SByte), USINT (Byte)
- UINT (UInt16), WORD (UInt16)
- UDINT (UInt32), DWORD (UInt32)
- ULINT (UInt64), LWORD (UInt64)
- LREAL (Double)

Also added corresponding value conversion handling in both
convert_value_for_opcua() and convert_value_for_plc() functions
to support both read and write operations.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@thiagoralves
thiagoralves requested a review from Copilot January 29, 2026 00:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a BadInternalError that occurred when reading SINT variables via OPC-UA clients by adding comprehensive type mappings for IEC 61131-3 data types that were previously missing.

Changes:

  • Added type mappings for 9 missing IEC 61131-3 types (SINT, USINT, UINT, WORD, UDINT, DWORD, ULINT, LWORD, LREAL)
  • Implemented value conversion logic for both read and write operations for all new types
  • Added comprehensive test coverage for the new type mappings and conversions

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
core/src/drivers/plugins/python/opcua/opcua_utils.py Added type mappings and conversion handlers for 9 missing IEC 61131-3 types in map_plc_to_opcua_type(), convert_value_for_opcua(), and convert_value_for_plc()
tests/pytest/plugins/opcua/test_type_conversions.py Added test cases for new type mappings (SINT, USINT, UINT, WORD, UDINT, DWORD, ULINT, LWORD, LREAL) and SINT value conversion/roundtrip tests

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +115 to +118
elif datatype.upper() in ["UINT", "Uint", "WORD", "Word"]:
# Ensure proper uint16 type for OPC-UA compatibility
clamped_value = max(0, min(65535, int(value)))
return ctypes.c_uint16(clamped_value).value

Copilot AI Jan 29, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The clamping logic is duplicated across multiple type handlers. Consider extracting the clamping and conversion into a helper function, e.g., _clamp_and_convert(value, min_val, max_val, ctype) to reduce code duplication and improve maintainability.

Copilot uses AI. Check for mistakes.
Comment on lines +292 to +295
elif datatype.upper() in ["UINT", "Uint", "WORD", "Word"]:
# Ensure proper uint16 type for PLC compatibility
clamped_value = max(0, min(65535, int(value)))
return ctypes.c_uint16(clamped_value).value

Copilot AI Jan 29, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The clamping logic is duplicated across multiple type handlers in this function as well. Consider extracting the clamping and conversion into a helper function to reduce code duplication, consistent with the suggestion for convert_value_for_opcua().

Copilot uses AI. Check for mistakes.
claude and others added 2 commits January 29, 2026 00:59
- Remove redundant mixed-case strings from datatype checks since
  .upper() is already called (e.g., "Sint" is unreachable after
  "sint".upper() == "SINT")
- Replace bare except clauses with specific struct.error catches
  to avoid catching unintended exceptions like KeyboardInterrupt

https://claude.ai/code/session_01FULyWikeb5YVJX8NVX5hax
refactor: Clean up type checking in OPC-UA plugin
@thiagoralves
thiagoralves merged commit ba97d11 into development Jan 29, 2026
1 check passed
@thiagoralves
thiagoralves deleted the fix/opcua-sint-type-mapping branch January 29, 2026 01:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants