Skip to content

Commit cc7c9c8

Browse files
committed
fix(simulator): keep report object banks stable during live edits
1 parent e10b50e commit cc7c9c8

1 file changed

Lines changed: 104 additions & 0 deletions

File tree

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#!/usr/bin/env python3
2+
from pathlib import Path
3+
4+
path = Path("tools/static_ied_server.cpp")
5+
text = path.read_text(encoding="utf-8")
6+
changed = False
7+
8+
root_anchor = '''void rebuild_manifest_roots(ManifestModel& model) {
9+
for (std::size_t index = 0U; index < model.root_trees.size(); ++index) {
10+
const auto value_index = model.root_value_indices[index];
11+
const auto& tree = model.root_trees[index];
12+
if (tree.children.empty()) continue;
13+
auto& root = model.values[value_index];
14+
root.type = node_type(tree, model, {});
15+
root.data = node_data(tree, model);
16+
root.type_specification = mms::MmsServiceCodec::encode_type_specification(root.type);
17+
root.encoded = mms::MmsDataCodec::encode(*root.data);
18+
}
19+
}
20+
'''
21+
root_replacement = root_anchor + '''
22+
void rebuild_manifest_root_values(ManifestModel& model) {
23+
// Runtime edits change only leaf values. Keep the structural MMS type and
24+
// its encoded type-specification storage stable for the lifetime of every
25+
// composed URCB/BRCB object table. Those tables intentionally copy object
26+
// entries (including spans into type-specification storage), while their
27+
// read callbacks retain pointers to ManifestValue and therefore observe
28+
// freshly encoded data without rebuilding the table hierarchy.
29+
for (std::size_t index = 0U; index < model.root_trees.size(); ++index) {
30+
const auto value_index = model.root_value_indices[index];
31+
const auto& tree = model.root_trees[index];
32+
if (tree.children.empty()) continue;
33+
auto& root = model.values[value_index];
34+
root.data = node_data(tree, model);
35+
root.encoded = mms::MmsDataCodec::encode(*root.data);
36+
}
37+
}
38+
'''
39+
if "void rebuild_manifest_root_values(ManifestModel& model)" not in text:
40+
if text.count(root_anchor) != 1:
41+
raise SystemExit("manifest root rebuild anchor not found exactly once")
42+
text = text.replace(root_anchor, root_replacement, 1)
43+
changed = True
44+
45+
old_refresh = ''' if (changed != 0U) {
46+
rebuild_manifest_roots(model);
47+
for (const auto value_index : model.root_value_indices) {
48+
model.objects[value_index].type_specification =
49+
model.values[value_index].type_specification;
50+
}
51+
}
52+
'''
53+
new_refresh = ''' if (changed != 0U) {
54+
// Value refresh is deliberately data-only. Re-encoding structural type
55+
// specifications here can reallocate their backing vectors and leave
56+
// the spans copied into composed control/URCB/BRCB tables dangling.
57+
rebuild_manifest_root_values(model);
58+
}
59+
'''
60+
if old_refresh in text:
61+
if text.count(old_refresh) != 1:
62+
raise SystemExit("expected exactly one legacy manifest refresh block")
63+
text = text.replace(old_refresh, new_refresh, 1)
64+
changed = True
65+
elif new_refresh not in text:
66+
raise SystemExit("manifest value-refresh block not found")
67+
68+
old_banks = ''' if (changed != 0U) {
69+
if (urcb_bank != nullptr && !urcb_bank->initialize()) {
70+
throw std::runtime_error(
71+
"URCB object bank could not refresh its base model views.");
72+
}
73+
for (auto& brcb : brcb_runtimes) {
74+
if (brcb != nullptr && brcb->bank != nullptr &&
75+
!brcb->bank->initialize()) {
76+
throw std::runtime_error(
77+
"BRCB object bank could not refresh its base model views.");
78+
}
79+
}
80+
notify_brcb_changes(
81+
'''
82+
new_banks = ''' if (changed != 0U) {
83+
// Object-bank topology, callback contexts and MMS type
84+
// specifications are structural and remain immutable after
85+
// association setup. ManifestValue callbacks read the
86+
// updated encoded payload directly, so reinitializing a
87+
// composed bank here is both unnecessary and unsafe: a
88+
// nested bank can copy aliases from its own storage while
89+
// it is being rebuilt. Notify report runtimes only.
90+
notify_brcb_changes(
91+
'''
92+
if old_banks in text:
93+
if text.count(old_banks) != 1:
94+
raise SystemExit("expected exactly one legacy report-bank refresh block")
95+
text = text.replace(old_banks, new_banks, 1)
96+
changed = True
97+
elif new_banks not in text:
98+
raise SystemExit("report-bank live-refresh block not found")
99+
100+
if changed:
101+
path.write_text(text, encoding="utf-8")
102+
print("Made live manifest refresh data-only and kept composed report banks structurally stable.")
103+
else:
104+
print("Report-bank live refresh is already structurally stable.")

0 commit comments

Comments
 (0)