Files
secs-gem/include/secsgem/gem/store/recipes.hpp
T
raphael 78fb0c3826 e42: enhanced (formatted) process programs S7F23-F26
E42 was an explicit out-of-scope item in the prior COMPLIANCE.md.
This commit closes it.

Wire messages added via the catalog:
  S7F23  Formatted PP Send       (H↔E, W=1)
  S7F24  Formatted PP Ack        (ProcessProgramAck)
  S7F25  Formatted PP Request    (PPID, W=1)
  S7F26  Formatted PP Data       (E→H, no reply)

Body shape: <L,4 PPID MDLN SOFTREV <L,n <L,2 CCODE <L,m <L,2
PNAME PVAL>>>>>.  PVAL is declared ITEM so any SECS-II Item type
round-trips — proven by a test that mixes ASCII, BOOLEAN, U4, F8,
Binary, and nested List values in one step.

RecipeStore extension:
  add_formatted(ppid, FormattedRecipe{mdln, softrev, steps})
  get_formatted(ppid) -> optional<FormattedRecipe>
  has_formatted(ppid) -> bool

Formatted + opaque views live alongside each other: a PPID can carry
both, size() counts unique PPIDs.  remove() kills both views.

Six new tests cover wire round-trip per function, every
ProcessProgramAck code, ITEM passthrough, and the store's dual-view
semantics.

COMPLIANCE.md updated: E30 §6.17 row mentions S7F23-F26, S5 message
table grows two rows, §8 "out of scope" entry for E42 removed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 11:58:03 +02:00

102 lines
3.0 KiB
C++

#pragma once
#include <cstdint>
#include <map>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "secsgem/secs2/item.hpp"
namespace secsgem::gem {
enum class ProcessProgramAck : uint8_t {
Accept = 0,
PermissionNotGranted = 1,
LengthError = 2,
MatrixOverflow = 3,
PpidNotFound = 4,
ModeUnsupported = 5,
PerformanceError = 6,
};
// One parameter on a formatted-PP step (E42). Name + arbitrary
// SECS-II Item value.
struct FormattedRecipeParam {
std::string name;
secs2::Item value;
};
// One step of a formatted PP: a CCODE (command name) and its
// parameter list. Matches the wire shape of S7F23/F26 row entries.
struct FormattedRecipeStep {
std::string ccode;
std::vector<FormattedRecipeParam> params;
};
// Equipment / model metadata that travels with a formatted PP. Set
// on add_formatted; round-tripped through S7F23/F26.
struct FormattedRecipe {
std::string mdln; // model name
std::string softrev; // software revision
std::vector<FormattedRecipeStep> steps;
};
class RecipeStore {
public:
void add(std::string ppid, std::string body) {
by_ppid_.insert_or_assign(std::move(ppid), std::move(body));
}
std::optional<std::string> get(const std::string& ppid) const {
auto it = by_ppid_.find(ppid);
if (it == by_ppid_.end()) return std::nullopt;
return it->second;
}
std::vector<std::string> list() const {
std::vector<std::string> out;
out.reserve(by_ppid_.size() + formatted_.size());
for (const auto& [k, _] : by_ppid_) out.push_back(k);
for (const auto& [k, _] : formatted_) {
if (!by_ppid_.count(k)) out.push_back(k);
}
return out;
}
ProcessProgramAck remove(const std::string& ppid) {
bool found = (by_ppid_.erase(ppid) > 0) | (formatted_.erase(ppid) > 0);
return found ? ProcessProgramAck::Accept : ProcessProgramAck::PpidNotFound;
}
// Unique PPID count. A PPID with both opaque and formatted views
// counts once.
std::size_t size() const {
std::size_t n = by_ppid_.size();
for (const auto& [k, _] : formatted_) {
if (!by_ppid_.count(k)) ++n;
}
return n;
}
// ---- E42 formatted PPs ------------------------------------------------
// A formatted recipe lives alongside any opaque-body PP with the same
// PPID. Most tools pick one form or the other; storing both
// independently lets a host poke at the formatted view while a legacy
// app reads the opaque view.
void add_formatted(std::string ppid, FormattedRecipe r) {
formatted_.insert_or_assign(std::move(ppid), std::move(r));
}
std::optional<FormattedRecipe> get_formatted(const std::string& ppid) const {
auto it = formatted_.find(ppid);
if (it == formatted_.end()) return std::nullopt;
return it->second;
}
bool has_formatted(const std::string& ppid) const {
return formatted_.count(ppid) > 0;
}
private:
std::map<std::string, std::string> by_ppid_;
std::map<std::string, FormattedRecipe> formatted_;
};
} // namespace secsgem::gem