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>
This commit is contained in:
@@ -7,6 +7,8 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "secsgem/secs2/item.hpp"
|
||||
|
||||
namespace secsgem::gem {
|
||||
|
||||
enum class ProcessProgramAck : uint8_t {
|
||||
@@ -19,6 +21,28 @@ enum class ProcessProgramAck : uint8_t {
|
||||
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) {
|
||||
@@ -31,19 +55,47 @@ class RecipeStore {
|
||||
}
|
||||
std::vector<std::string> list() const {
|
||||
std::vector<std::string> out;
|
||||
out.reserve(by_ppid_.size());
|
||||
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) {
|
||||
if (!by_ppid_.count(ppid)) return ProcessProgramAck::PpidNotFound;
|
||||
by_ppid_.erase(ppid);
|
||||
return ProcessProgramAck::Accept;
|
||||
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;
|
||||
}
|
||||
std::size_t size() const { return by_ppid_.size(); }
|
||||
|
||||
private:
|
||||
std::map<std::string, std::string> by_ppid_;
|
||||
std::map<std::string, std::string> by_ppid_;
|
||||
std::map<std::string, FormattedRecipe> formatted_;
|
||||
};
|
||||
|
||||
} // namespace secsgem::gem
|
||||
|
||||
Reference in New Issue
Block a user