// 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 #ifndef __AVR__ #include #endif #include #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 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(value_) < static_cast(other.value_); } constexpr bool operator<=(const IEC_ENUM_Value& other) const noexcept { return static_cast(value_) <= static_cast(other.value_); } constexpr bool operator>(const IEC_ENUM_Value& other) const noexcept { return static_cast(value_) > static_cast(other.value_); } constexpr bool operator>=(const IEC_ENUM_Value& other) const noexcept { return static_cast(value_) >= static_cast(other.value_); } // Convert to integer (for serialization, debugging) constexpr int to_int() const noexcept { return static_cast(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 class IEC_ENUM_Var { public: using value_type = IEC_ENUM_Value; 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_` (= IEC_ENUM_Var). 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 myVar; */ template using IEC_ENUM = IEC_ENUM_Var; #ifndef __AVR__ // Stream output for IEC_ENUM_Var — outputs underlying integer value template inline std::ostream& operator<<(std::ostream& os, const IEC_ENUM_Var& v) { return os << static_cast::type>( static_cast(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; * using TrafficLight_Var = IEC_ENUM_Var; * * 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; * using Status_Var = IEC_ENUM_Var; */ } // namespace strucpp