wrps-demo-kit/03-plc/as-built/strucpp_runtime/include/iec_located.hpp
Clio Liu 55279ca78f docs(plc): as-built copy, versions, and a getting-started guide
Adds what the folder was missing for someone picking it up cold.

as-built/  the STruC++ output copied out of the live container - the C++
           the PLC is actually executing, plus program.st and the Modbus
           buffer config. The running image was made with docker commit
           and exists in no registry, so this is the only other copy of
           the compiled form.

           Verified while copying: the deployed program.st matches
           build/wrps.st generated from src/. Identical POU structure,
           ZERO differences in non-declaration lines. src/ is genuinely
           canonical and the running PLC agrees with it.

VERSIONS.md         every version read from the running system, not from
                    documentation: runtime v4.1.10, STruC++ 0.6.2, Editor
                    4.2.11, Debian 12, g++ 12.2, pymodbus 3.11.2, CI
                    Server R1.03. Plus which plugins are enabled -
                    ethercat is on for no reason - and a v3-vs-v4 table,
                    since most OpenPLC guidance online is for v3 and the
                    %MW HR1024 change silently produces wrong data.

GETTING-STARTED.md  three questions answered in a page: how to modify the
                    program, how to stand up a new PLC container from
                    scratch, how to move this one. Includes the compose
                    file, the Editor steps that are not generated and are
                    always missed, and a symptom-to-document table.
2026-09-02 16:48:30 +10:00

223 lines
7.6 KiB
C++

// SPDX-License-Identifier: GPL-3.0-or-later WITH STruCpp-runtime-exception
// Copyright (C) 2025 Autonomy / OpenPLC Project
// This file is part of the STruC++ Runtime Library and is covered by the
// STruC++ Runtime Library Exception. See COPYING.RUNTIME for details.
/**
* STruC++ Runtime - Located Variables Support
*
* This header defines the types and structures needed for located variables
* (AT %IX0.0, %QX0.0, %MW100, etc.) that bind to runtime I/O image tables.
*
* Located variables provide the interface between IEC programs and physical
* I/O in PLC systems. The compiler generates a descriptor array that the
* runtime uses to bind variables to I/O addresses.
*/
#pragma once
#include <cstddef>
#include <cstdint>
#include "iec_fault.hpp"
#if STRUCPP_HAS_EXCEPTIONS
#include <stdexcept>
#endif
namespace strucpp {
// =============================================================================
// Located Variable Area Types
// =============================================================================
/**
* Memory area for located variables.
* Corresponds to the first letter after % in IEC addresses:
* - I = Input (read from physical inputs)
* - Q = Output (write to physical outputs)
* - M = Memory (internal markers/flags)
*/
enum class LocatedArea : uint8_t {
Input = 0, // %I - Input area
Output = 1, // %Q - Output area
Memory = 2 // %M - Memory/Marker area
};
// =============================================================================
// Located Variable Size Types
// =============================================================================
/**
* Data size for located variables.
* Corresponds to the size specifier in IEC addresses:
* - X = Bit (1 bit)
* - B = Byte (8 bits)
* - W = Word (16 bits)
* - D = Double Word (32 bits)
* - L = Long Word (64 bits)
*/
enum class LocatedSize : uint8_t {
Bit = 0, // %?X - Single bit
Byte = 1, // %?B - 8 bits
Word = 2, // %?W - 16 bits
DWord = 3, // %?D - 32 bits
LWord = 4 // %?L - 64 bits
};
// =============================================================================
// Located Variable Descriptor
// =============================================================================
/**
* Descriptor for a located variable.
*
* This structure is generated by the compiler for each located variable
* in the program. The runtime uses this descriptor array to:
* 1. Bind variables to I/O image tables at startup
* 2. Copy values between I/O images and variables during scan cycle
* 3. Support variable forcing for debugging
*
* Memory Layout (16 bytes, aligned):
* - area: 1 byte
* - size: 1 byte
* - byte_index: 2 bytes (major address component)
* - bit_index: 1 byte (minor address component, 0-7 for bits)
* - reserved: 3 bytes (padding for alignment)
* - pointer: 8 bytes (pointer to variable storage)
*
* Address Format:
* %<area><size><byte_index>.<bit_index>
*
* Examples:
* %IX0.5 -> area=Input, size=Bit, byte_index=0, bit_index=5
* %QW10 -> area=Output, size=Word, byte_index=10, bit_index=0
* %MD100 -> area=Memory, size=DWord, byte_index=100, bit_index=0
*/
struct LocatedVar {
LocatedArea area; ///< Memory area (I, Q, or M)
LocatedSize size; ///< Data size (X, B, W, D, or L)
uint16_t byte_index; ///< Byte offset in the I/O image
uint8_t bit_index; ///< Bit offset within byte (0-7, only for X size)
uint8_t _reserved[3]; ///< Padding for alignment
void* pointer; ///< Pointer to the variable's raw storage
/**
* Check if this descriptor represents a bit-addressed variable.
*/
constexpr bool is_bit() const noexcept {
return size == LocatedSize::Bit;
}
/**
* Get the size in bytes for this variable.
* Returns 0 for bit-addressed variables (handled specially).
*/
constexpr size_t byte_size() const noexcept {
switch (size) {
case LocatedSize::Bit: return 0; // Special handling
case LocatedSize::Byte: return 1;
case LocatedSize::Word: return 2;
case LocatedSize::DWord: return 4;
case LocatedSize::LWord: return 8;
default: return 0;
}
}
};
// Verify expected layout (size varies by platform: 16 bytes on 64-bit, 8 on 32-bit, 6 on AVR)
#if INTPTR_MAX == INT64_MAX
static_assert(sizeof(LocatedVar) == 16, "LocatedVar should be 16 bytes on 64-bit");
static_assert(alignof(LocatedVar) == 8, "LocatedVar should be 8-byte aligned on 64-bit");
#endif
// =============================================================================
// Helper Functions for Address Parsing
// =============================================================================
/**
* Parse an area character to LocatedArea enum.
* @param c The area character ('I', 'Q', or 'M')
* @return The corresponding LocatedArea value
* @throws std::invalid_argument if character is invalid
*/
inline LocatedArea parse_area(char c) {
switch (c) {
case 'I': case 'i': return LocatedArea::Input;
case 'Q': case 'q': return LocatedArea::Output;
case 'M': case 'm': return LocatedArea::Memory;
#if STRUCPP_HAS_EXCEPTIONS
default: throw std::invalid_argument("Invalid area character");
#else
default: iec_runtime_fault(IecFault::BadLocation, "Invalid area character");
#endif
}
}
/**
* Parse a size character to LocatedSize enum.
* @param c The size character ('X', 'B', 'W', 'D', or 'L')
* @return The corresponding LocatedSize value
* @throws std::invalid_argument if character is invalid
*/
inline LocatedSize parse_size(char c) {
switch (c) {
case 'X': case 'x': return LocatedSize::Bit;
case 'B': case 'b': return LocatedSize::Byte;
case 'W': case 'w': return LocatedSize::Word;
case 'D': case 'd': return LocatedSize::DWord;
case 'L': case 'l': return LocatedSize::LWord;
#if STRUCPP_HAS_EXCEPTIONS
default: throw std::invalid_argument("Invalid size character");
#else
default: iec_runtime_fault(IecFault::BadLocation, "Invalid size character");
#endif
}
}
/**
* Get the area character for a LocatedArea enum.
* @param area The LocatedArea value
* @return The corresponding character ('I', 'Q', or 'M')
*/
constexpr char area_to_char(LocatedArea area) noexcept {
switch (area) {
case LocatedArea::Input: return 'I';
case LocatedArea::Output: return 'Q';
case LocatedArea::Memory: return 'M';
default: return '?';
}
}
/**
* Get the size character for a LocatedSize enum.
* @param size The LocatedSize value
* @return The corresponding character ('X', 'B', 'W', 'D', or 'L')
*/
constexpr char size_to_char(LocatedSize size) noexcept {
switch (size) {
case LocatedSize::Bit: return 'X';
case LocatedSize::Byte: return 'B';
case LocatedSize::Word: return 'W';
case LocatedSize::DWord: return 'D';
case LocatedSize::LWord: return 'L';
default: return '?';
}
}
// =============================================================================
// Located Variable Table Marker
// =============================================================================
/**
* End marker for the located variable descriptor array.
* The compiler generates this as the last entry in the array.
* Runtime scans the array until it finds this marker (pointer == nullptr).
*/
constexpr LocatedVar LOCATED_VAR_END = {
LocatedArea::Input, // Doesn't matter
LocatedSize::Bit, // Doesn't matter
0, // byte_index
0, // bit_index
{0, 0, 0}, // reserved
nullptr // End marker
};
} // namespace strucpp