Skip to content

PLC JSON Integration Plan

Overview

This document describes how to integrate the Essert.MF OPC UA Server with Siemens S7-1500 PLCs, specifically addressing JSON data exchange using the Siemens LStream library.

Reference Documentation: - LStream Library: Siemens Entry ID 109781165 - Local copy: docs/OPCUA/109781165_LStream_DOC_v1_6_en.pdf


LStream Library Restrictions and Requirements

1. Encoding Restriction

Requirement Description
Encoding ASCII only
Impact Any other encoding (UTF-8 with special chars, Unicode) leads to undefined behavior
Risk High - may cause issues during serialization/deserialization

Server-Side Mitigation: - Ensure all string values contain only ASCII characters (0x00-0x7F) - Strip or replace non-ASCII characters before JSON serialization - Avoid German umlauts (ä, ö, ü, ß), special symbols, emojis

2. String Length Restriction

Requirement Description
Max Length 256 characters per value
Applies To Values of JSON elements, keys, attributes
Workaround Modify library code (not recommended)

Server-Side Mitigation: - Truncate all string values to max 250 characters (safety margin) - For long text fields, consider splitting or omitting from PLC responses - Create PLC-specific endpoints with guaranteed short values

Fields at Risk: | Field | Typical Length | Risk Level | Mitigation | |-------|----------------|------------|------------| | ProductName | < 100 | Low | Monitor | | DisplayName | < 100 | Low | Monitor | | ArticleNumber | < 50 | Low | None | | Description | Variable | High | Truncate or exclude | | NokReason | Variable | Medium | Truncate to 250 | | ErrorMessage | Variable | Medium | Truncate to 250 | | Creator | < 30 | Low | None |

3. JSON Format Restriction (CRITICAL)

Requirement Description
Format Compressed JSON only
Line breaks NOT allowed
Whitespace NOT allowed outside of values

Example - Invalid (Pretty-printed):

{
  "glossary": {
    "title": "example glossary",
    "items": ["XML", "S"]
  }
}

Example - Valid (Compressed):

{"glossary":{"title":"example glossary","items":["XML","S"]}}

Server Implementation Status: - WriteIndented = false configured in both NodeManagers (via PlcJsonHelper) - All JSON responses are compressed - No line breaks or unnecessary whitespace - All string values are truncated to 250 characters (safety margin) - All non-ASCII characters are replaced with '?'

4. Data Type Handling

The LStream library stores all values as strings in LStream_typeElement.value. The PLC must convert these to appropriate data types.

Type Mapping:

JSON Type LStream type PLC Conversion
Number (int) 3 STRING_TO_DINT()
Number (long) 3 STRING_TO_LINT() (if available) or split
Number (float) 3 STRING_TO_REAL()
String 2 Direct use
Boolean 4 value = 'true' check
Object 0 Container (no value)
Array 1 Container (no value)
Null - value = 'NULL' or value = 'null'

OPC UA Server Configuration

Endpoint Configuration

{
  "OpcUaServer": {
    "EndpointUrl": "opc.tcp://192.168.100.110:48400/Essert.MF",
    "ServerName": "Essert MF OPC UA Server",
    "EnableSecurity": false
  }
}

Available Namespaces

Namespace URI Index
Products http://essert.de/mf/products/ ns=2
Manufacturing http://essert.de/mf/manufacturing/ ns=3

OPC UA Methods and PLC Data Types

Products Namespace (ns=2)

Method Input Args Output Type PLC Return Type
Ping - String String[254]
GetProductCount - Int32 DInt
GetProductsPaged skip: Int32, take: Int32 String (JSON) Array[*] of Byte
GetAllProducts - String (JSON) Array[*] of Byte
GetProductById productUid: Int64 String (JSON) Array[*] of Byte
CreateProduct name, display, article: String Int64 LInt or DInt pair
UpdateProduct uid: Int64, name: String Boolean Bool
DeleteProduct uid: Int64 Boolean Bool
AddVersion uid: Int64, name: String Int64 LInt or DInt pair
DeleteVersion uid: Int64 Boolean Bool

Manufacturing Namespace (ns=3)

Method Input Args Output Type PLC Return Type
GetActiveProcessCount - Int32 DInt
GetActiveProcesses skip, take: Int32 String (JSON) Array[*] of Byte
GetProcessById uid: Int64 String (JSON) Array[*] of Byte
GetProcessByOrderNumber order: String String (JSON) Array[*] of Byte
GetProcessesByWpcId wpcUid: Int64 String (JSON) Array[*] of Byte
CreateProcess (7 params) Int64 LInt or DInt pair
StartProcess uid: Int64 Boolean Bool
CompleteProcess uid: Int64 Boolean Bool
FailProcess uid: Int64, reason: String Boolean Bool
CancelProcess uid: Int64, reason: String Boolean Bool
DeleteProcess uid: Int64 Boolean Bool

JSON Response Structures

GetProductsPaged Response

{"Items":[{"Uid":1,"ProductName":"Product A","DisplayName":"Display A","ArticleNumber":"ART-001","Versions":[]}],"TotalCount":42,"Skip":0,"Take":10,"TotalPages":5,"CurrentPage":1,"HasNextPage":true,"HasPreviousPage":false}

LStream Tree Structure: | Index | type | key | value | depth | closingElement | |-------|------|-----|-------|-------|----------------| | 0 | 0 | (root) | | 1 | false | | 1 | 1 | Items | | 2 | false | | 2 | 0 | | | 3 | false | | 3 | 3 | Uid | 1 | 4 | false | | 4 | 2 | ProductName | Product A | 4 | false | | 5 | 2 | DisplayName | Display A | 4 | false | | 6 | 2 | ArticleNumber | ART-001 | 4 | true | | 7 | 3 | TotalCount | 42 | 2 | false | | ... | ... | ... | ... | ... | ... |

GetProductById Response

{"Uid":1,"ProductName":"Product A","DisplayName":"Display A","ArticleNumber":"ART-001","Versions":[{"Uid":10,"VersionName":"V1.0"}]}

PLC Implementation Guidelines

1. Data Block Setup

DB_OpcUa_Products
├── execute : Bool
├── connectionId : DWord
├── inputArgs
│   ├── skip : DInt
│   └── take : DInt
├── outputRaw : Array[0..9999] of Byte
├── outputTree : Array[0..99] of LStream_typeElement
├── status
│   ├── busy : Bool
│   ├── done : Bool
│   ├── error : Bool
│   └── errorCode : Word
└── result
    ├── totalCount : DInt
    ├── currentPage : DInt
    └── products : Array[0..9] of UDT_Product

2. Workflow Sequence

1. [OPC_UA_Connect]      → Establish connection
2. [OPC_UA_MethodCall]   → Call GetProductsPaged(skip, take)
3. [String to Bytes]     → Convert response to byte array
4. [LStream_JsonDeserializer] → Parse JSON to tree
5. [Map to UDT]          → Extract values to PLC structure
6. [Use data]            → Application logic

3. Error Handling

LStream Status Meaning Action
16#0000 Success Process data
16#8201 Array too small Increase tree/byte array size
16#8401 No raw data Check OPC UA response
16#8600 State machine error Reset and retry

Implementation Checklist

Server Side (Essert.MF.API.OpcUa)

  • JSON serialization uses compressed format (WriteIndented = false)
  • OPC UA methods have proper OpcArgumentAttribute annotations
  • Add string truncation helper for values > 250 chars (PlcJsonHelper.SanitizeString)
  • Add ASCII validation/sanitization for string values (PlcJsonHelper.Serialize)
  • Add unit tests for PlcJsonHelper (35 tests)
  • Add integration tests for max string length scenarios (11 tests)
  • [~] ~~Create PLC-optimized endpoints~~ — Not needed: The existing endpoints with PlcJsonHelper already handle all LStream constraints. PLCs can use pagination (e.g., GetProductsPaged(0, 10)) to control payload size and ignore unused fields. Revisit only if PLC integration testing reveals specific memory or parsing issues.

PLC Side (TIA Portal)

Note: Most of these items can be auto-generated using the PLC Code Generator.

  • Import LStream library (V1.6+)
  • Configure OPC UA client connection
  • Create UDTs for product/process data (can be generated)
  • Create data blocks for method parameters (can be generated)
  • Implement OPC UA method call sequence (can be generated)
  • Implement JSON deserialization with LStream (can be generated)
  • Implement value extraction and type conversion (can be generated)
  • Add error handling for all failure scenarios

Testing Recommendations

1. Connectivity Test

  • Use Ping method (no parameters, returns "pong")
  • Verify OPC UA connection and method call works

2. Simple Data Test

  • Use GetProductCount (returns single DInt)
  • No JSON parsing required

3. JSON Parsing Test

  • Use GetProductsPaged(0, 1) (single product)
  • Verify LStream deserializes correctly
  • Check all fields are accessible

4. Boundary Tests

  • Test with maximum string lengths (250 chars)
  • Test with special ASCII characters
  • Test with empty arrays/null values

References


Document History

Version Date Author Changes
1.0 2026-01-21 Claude Initial version
1.1 2026-01-21 Claude Implemented PlcJsonHelper with ASCII sanitization and string truncation
1.2 2026-01-21 Claude Marked PLC-optimized endpoints as not needed (existing endpoints sufficient)