wrps-demo-kit/03-plc/as-built/strucpp_runtime/include/iec_enum.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

277 lines
7.5 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 - IEC Enumeration Support
*
* This header provides infrastructure for IEC 61131-3 enumeration types.
* Enumerations in IEC 61131-3 are strongly typed, similar to C++ enum class.
* Actual enum definitions are generated by the compiler based on user TYPE declarations.
*/
#pragma once
#include <cstdint>
#ifndef __AVR__
#include <ostream>
#endif
#include <type_traits>
#include "iec_var.hpp"
namespace strucpp {
/**
* Value wrapper for IEC enumerations.
* Provides a type-safe wrapper around C++ enum class types.
*
* @tparam EnumType The underlying C++ enum class type
*/
template<typename EnumType>
class IEC_ENUM_Value {
private:
EnumType value_;
public:
using enum_type = EnumType;
// Default constructor - initializes to first enum value (0)
constexpr IEC_ENUM_Value() noexcept : value_{} {}
// Constructor from enum value
constexpr IEC_ENUM_Value(EnumType val) noexcept : value_(val) {}
// Implicit conversion to underlying enum
constexpr operator EnumType() const noexcept { return value_; }
// Get the underlying enum value
constexpr EnumType get() const noexcept { return value_; }
// Assignment from enum value
IEC_ENUM_Value& operator=(EnumType val) noexcept {
value_ = val;
return *this;
}
// Comparison operators
constexpr bool operator==(const IEC_ENUM_Value& other) const noexcept {
return value_ == other.value_;
}
constexpr bool operator!=(const IEC_ENUM_Value& other) const noexcept {
return value_ != other.value_;
}
constexpr bool operator==(EnumType val) const noexcept {
return value_ == val;
}
constexpr bool operator!=(EnumType val) const noexcept {
return value_ != val;
}
// Ordering operators (for enums with numeric values)
constexpr bool operator<(const IEC_ENUM_Value& other) const noexcept {
return static_cast<int>(value_) < static_cast<int>(other.value_);
}
constexpr bool operator<=(const IEC_ENUM_Value& other) const noexcept {
return static_cast<int>(value_) <= static_cast<int>(other.value_);
}
constexpr bool operator>(const IEC_ENUM_Value& other) const noexcept {
return static_cast<int>(value_) > static_cast<int>(other.value_);
}
constexpr bool operator>=(const IEC_ENUM_Value& other) const noexcept {
return static_cast<int>(value_) >= static_cast<int>(other.value_);
}
// Convert to integer (for serialization, debugging)
constexpr int to_int() const noexcept {
return static_cast<int>(value_);
}
};
/**
* IEC enumeration variable with forcing support.
* Wraps IEC_ENUM_Value in IECVar for debugging capabilities.
*
* @tparam EnumType The underlying C++ enum class type
*/
template<typename EnumType>
class IEC_ENUM_Var {
public:
using value_type = IEC_ENUM_Value<EnumType>;
using enum_type = EnumType;
private:
value_type value_;
bool forced_;
value_type forced_value_;
public:
IEC_ENUM_Var() noexcept : value_{}, forced_{false}, forced_value_{} {}
// Non-explicit so raw `EnumType` values can implicitly convert at call
// sites — matches the implicit `IECVar(T v)` constructor for elementary
// types. Without this, codegen would have to wrap every raw enum
// literal at the call site, since enum-typed variables are now
// declared as `IEC_<name>` (= IEC_ENUM_Var<EnumType>).
IEC_ENUM_Var(EnumType val) noexcept
: value_{val}, forced_{false}, forced_value_{} {}
IEC_ENUM_Var(value_type val) noexcept
: value_{val}, forced_{false}, forced_value_{} {}
IEC_ENUM_Var(const IEC_ENUM_Var&) = default;
IEC_ENUM_Var(IEC_ENUM_Var&&) = default;
IEC_ENUM_Var& operator=(const IEC_ENUM_Var&) = default;
IEC_ENUM_Var& operator=(IEC_ENUM_Var&&) = default;
// Get current value (returns forced value if forced)
value_type get() const noexcept {
return forced_ ? forced_value_ : value_;
}
// Set value (ignored if forced)
void set(value_type v) noexcept {
value_ = v;
}
void set(EnumType v) noexcept {
value_ = v;
}
// Get underlying value (ignoring forcing)
value_type get_underlying() const noexcept {
return value_;
}
// Force to a specific value
void force(value_type v) noexcept {
forced_ = true;
forced_value_ = v;
}
void force(EnumType v) noexcept {
forced_ = true;
forced_value_ = v;
}
// Remove forcing
void unforce() noexcept {
forced_ = false;
}
// Check if forced
bool is_forced() const noexcept {
return forced_;
}
// Get forced value
value_type get_forced_value() const noexcept {
return forced_value_;
}
// Implicit conversion to value_type
operator value_type() const noexcept {
return get();
}
// Implicit conversion to enum_type
operator EnumType() const noexcept {
return get().get();
}
// Assignment operators
IEC_ENUM_Var& operator=(value_type v) noexcept {
set(v);
return *this;
}
IEC_ENUM_Var& operator=(EnumType v) noexcept {
set(v);
return *this;
}
// Comparison operators
bool operator==(const IEC_ENUM_Var& other) const noexcept {
return get() == other.get();
}
bool operator!=(const IEC_ENUM_Var& other) const noexcept {
return get() != other.get();
}
bool operator==(EnumType val) const noexcept {
return get() == val;
}
bool operator!=(EnumType val) const noexcept {
return get() != val;
}
};
/**
* Convenience alias for IEC enumeration with forcing support.
* Usage: IEC_ENUM<MyEnumClass> myVar;
*/
template<typename EnumType>
using IEC_ENUM = IEC_ENUM_Var<EnumType>;
#ifndef __AVR__
// Stream output for IEC_ENUM_Var — outputs underlying integer value
template<typename EnumType>
inline std::ostream& operator<<(std::ostream& os, const IEC_ENUM_Var<EnumType>& v) {
return os << static_cast<typename std::underlying_type<EnumType>::type>(
static_cast<EnumType>(v));
}
#endif
/*
* Example generated enumeration:
*
* ST Source:
* TYPE TrafficLight : (RED, YELLOW, GREEN); END_TYPE
*
* Generated C++:
* enum class TrafficLight : int16_t {
* RED = 0,
* YELLOW = 1,
* GREEN = 2
* };
* using TrafficLight_Value = IEC_ENUM_Value<TrafficLight>;
* using TrafficLight_Var = IEC_ENUM_Var<TrafficLight>;
*
* Usage:
* TrafficLight_Var light;
* light = TrafficLight::RED;
*
* if (light == TrafficLight::RED) {
* // Handle red light
* }
*
* // Force for debugging
* light.force(TrafficLight::GREEN);
* light = TrafficLight::RED; // Ignored
* assert(light == TrafficLight::GREEN);
*/
/*
* Example typed enumeration (IEC v3):
*
* ST Source:
* TYPE Status : INT (IDLE := 0, RUNNING := 1, ERROR := -1); END_TYPE
*
* Generated C++:
* enum class Status : int16_t {
* IDLE = 0,
* RUNNING = 1,
* ERROR = -1
* };
* using Status_Value = IEC_ENUM_Value<Status>;
* using Status_Var = IEC_ENUM_Var<Status>;
*/
} // namespace strucpp