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.
277 lines
7.5 KiB
C++
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
|