78fb0c3826
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>
102 lines
3.0 KiB
C++
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
|