5#ifndef LINE_IO_ENVIRONMENT_READER_H
6#define LINE_IO_ENVIRONMENT_READER_H
64inline void reject_unconsumed_env_keys(
const json& model) {
65 static const char* kEnvKeys[] = {
"name",
"type",
"numStages",
"stages",
66 "transitions",
"format",
"version",
"nodeFailures"};
67 static const char* kStageKeys[] = {
"name",
"type",
"model"};
68 static const char* kArcKeys[] = {
"from",
"to",
"distribution"};
69 static const char* kFailKeys[] = {
"node",
"breakdownRate",
"repairRate",
70 "downService",
"breakdownResetPolicy",
"repairResetPolicy"};
71 auto known = [](
const char*
const* tab, std::size_t n,
const std::string& k) {
72 for (std::size_t i = 0; i < n; ++i)
73 if (k == tab[i])
return true;
76 const std::string why =
77 "', which this reader does not implement. Refusing rather than dropping it: a "
78 "constraint silently discarded here would make the solver return a confident answer "
79 "for a different environment";
80 for (json::const_iterator it = model.begin(); it != model.end(); ++it)
81 if (!known(kEnvKeys,
sizeof(kEnvKeys) /
sizeof(*kEnvKeys), it.key()))
82 throw UnsupportedError(
"environment_reader: the model carries '" + it.key() + why);
83 if (model.contains(
"nodeFailures"))
84 for (
const json& nf : model.at(
"nodeFailures"))
85 for (json::const_iterator it = nf.begin(); it != nf.end(); ++it)
86 if (!known(kFailKeys,
sizeof(kFailKeys) /
sizeof(*kFailKeys), it.key()))
88 nf.value(
"node", std::string(
"?")) +
"' carries '" +
90 if (model.contains(
"stages"))
91 for (
const json& st : model.at(
"stages"))
92 for (json::const_iterator it = st.begin(); it != st.end(); ++it)
93 if (!known(kStageKeys,
sizeof(kStageKeys) /
sizeof(*kStageKeys), it.key()))
95 st.value(
"name", std::string(
"?")) +
"' carries '" +
97 if (model.contains(
"transitions"))
98 for (
const json& tr : model.at(
"transitions"))
99 for (json::const_iterator it = tr.begin(); it != tr.end(); ++it)
100 if (!known(kArcKeys,
sizeof(kArcKeys) /
sizeof(*kArcKeys), it.key()))
101 throw UnsupportedError(
"environment_reader: an environment transition carries '" +
107struct NodeFailureSpec {
109 lang::Distrib<T> breakdown, down_service, repair;
110 bool has_repair =
false;
111 std::string breakdown_reset =
"keep";
112 std::string repair_reset =
"keep";
122NodeFailureSpec<T> node_failure_fields(
const json& nf) {
123 NodeFailureSpec<T> s;
124 if (!nf.contains(
"node"))
126 "environment_reader: a 'nodeFailures' entry is missing the required 'node' field, "
127 "which names the node that breaks down");
128 s.node = nf.at(
"node").get<std::string>();
129 if (!nf.contains(
"breakdownRate") || nf.at(
"breakdownRate").is_null())
130 throw InputError(
"environment_reader: the node failure on '" + s.node +
131 "' is missing the required 'breakdownRate' field, the time to failure");
132 if (!nf.contains(
"downService") || nf.at(
"downService").is_null())
133 throw InputError(
"environment_reader: the node failure on '" + s.node +
134 "' is missing the required 'downService' field, the service the node "
135 "gives while it is down");
136 s.breakdown = dist_from_json<T>(nf.at(
"breakdownRate"));
137 s.down_service = dist_from_json<T>(nf.at(
"downService"));
138 if (nf.contains(
"repairRate") && !nf.at(
"repairRate").is_null()) {
139 s.repair = dist_from_json<T>(nf.at(
"repairRate"));
142 if (nf.contains(
"breakdownResetPolicy") && !nf.at(
"breakdownResetPolicy").is_null()) {
143 const std::string p = nf.at(
"breakdownResetPolicy").get<std::string>();
144 if (!p.empty()) s.breakdown_reset = p;
146 if (nf.contains(
"repairResetPolicy") && !nf.at(
"repairResetPolicy").is_null()) {
147 const std::string p = nf.at(
"repairResetPolicy").get<std::string>();
148 if (!p.empty()) s.repair_reset = p;
163env::Environment<T> expand_node_failures(
const json& model,
const json& stage0,
164 const std::vector<NodeFailureSpec<T> >& fails) {
165 if (model.at(
"stages").size() != 1)
167 "environment_reader: 'nodeFailures' expands the base model into the UP and "
168 "DOWN_<node> stages, so 'stages' must declare exactly one stage, holding the base "
170 if (model.contains(
"transitions") && !model.at(
"transitions").empty())
172 "environment_reader: 'nodeFailures' implies the breakdown and repair transitions; "
173 "'transitions' must not be declared alongside it");
174 if (!stage0.contains(
"model") || stage0.at(
"model").is_null())
176 "environment_reader: 'nodeFailures' requires the base stage to carry a 'model'");
179 const qn::NetworkStruct<T> base = base_net.get_struct();
180 const std::size_t E = 1 + fails.size();
185 if (model.contains(
"numStages")) {
186 const std::size_t declared = model.at(
"numStages").get<std::size_t>();
187 if (declared != E && declared != 1)
188 throw InputError(
"environment_reader: 'numStages' is " + std::to_string(declared) +
189 " but the 'nodeFailures' block expands one base stage into " +
190 std::to_string(E) +
" stages");
193 env::Environment<T> e(model.value(
"name", std::string(
"env")), E);
194 for (std::size_t k = 0; k < fails.size(); ++k) {
195 const NodeFailureSpec<T>& nf = fails[k];
196 e.add_node_breakdown(0, k + 1, base, nf.node, nf.breakdown, nf.down_service,
198 if (nf.has_repair) e.add_node_repair(nf.node, nf.repair, nf.repair_reset);
218 const json& model = root.contains(
"model") ? root.at(
"model") : root;
219 const std::string mtype = model.value(
"type", std::string(
""));
220 if (mtype !=
"Environment")
222 "' is not an Environment; -s env solves a random-environment "
223 "model and the Network path solves the rest");
224 detail::reject_unconsumed_env_keys(model);
226 if (!model.contains(
"stages"))
227 throw InputError(
"environment_reader: the environment declares no 'stages'");
228 const json& stages = model.at(
"stages");
229 const std::size_t D = stages.size();
230 if (D == 0)
throw InputError(
"environment_reader: the environment declares no stage");
231 std::vector<std::string> declared_names(D);
232 for (std::size_t s = 0; s < D; ++s)
233 declared_names[s] = stages[s].value(
"name", std::string(
"Stage") + std::to_string(s + 1));
239 std::vector<detail::NodeFailureSpec<T> > fails;
240 if (model.contains(
"nodeFailures"))
241 for (
const json& nf : model.at(
"nodeFailures"))
242 fails.push_back(detail::node_failure_fields<T>(nf));
243 bool macro = !fails.empty();
244 for (std::size_t k = 0; k < fails.size() && macro; ++k) {
246 for (std::size_t s = 0; s < D; ++s)
247 if (declared_names[s] == down) macro =
false;
249 for (std::size_t k = 0; k < fails.size(); ++k)
250 for (std::size_t j = k + 1; j < fails.size(); ++j)
251 if (fails[k].node == fails[j].node)
252 throw InputError(
"environment_reader: 'nodeFailures' declares node '" +
254 "' twice, and a node has ONE down stage; merge the two entries");
256 if (macro)
return detail::expand_node_failures<T>(model, stages[0], fails);
258 const std::size_t E = D;
263 if (model.contains(
"numStages")) {
264 const std::size_t declared = model.at(
"numStages").get<std::size_t>();
266 throw InputError(
"environment_reader: 'numStages' is " + std::to_string(declared) +
267 " but 'stages' holds " + std::to_string(E) +
" entries");
271 for (std::size_t s = 0; s < E; ++s) {
272 const json& st = stages[s];
273 const std::string nm = st.value(
"name", std::string(
"Stage") + std::to_string(s + 1));
277 if (!st.contains(
"model"))
278 throw InputError(
"environment_reader: stage '" + nm +
279 "' carries no 'model'; every stage of a random environment holds "
280 "the network in force while it lasts");
285 if (model.contains(
"transitions")) {
286 for (
const json&
tr : model.at(
"transitions")) {
287 if (!
tr.contains(
"from") || !
tr.contains(
"to"))
289 "environment_reader: an environment transition is missing 'from' or 'to'");
290 const long long from =
tr.at(
"from").get<
long long>();
291 const long long to =
tr.at(
"to").get<
long long>();
292 if (from < 0 || to < 0 ||
static_cast<std::size_t
>(from) >= E ||
293 static_cast<std::size_t
>(to) >= E)
294 throw InputError(
"environment_reader: transition " + std::to_string(from) +
" -> " +
296 " names a stage outside 0.." + std::to_string(E - 1) +
297 " (the wire index is zero-based in both writers)");
298 if (!
tr.contains(
"distribution"))
299 throw InputError(
"environment_reader: the transition " + std::to_string(from) +
300 " -> " + std::to_string(to) +
301 " carries no 'distribution'; the arc holding time is what the "
302 "environment process is made of");
303 e.
add_transition(
static_cast<std::size_t
>(from),
static_cast<std::size_t
>(to),
304 detail::dist_from_json<T>(
tr.at(
"distribution")));
311 for (std::size_t k = 0; k < fails.size(); ++k) {
312 const detail::NodeFailureSpec<T>& nf = fails[k];
314 nf.breakdown_reset, nf.repair_reset);
322 std::ifstream in(path.c_str());
323 if (!in)
throw InputError(
"environment_reader: cannot open " + path);
327 }
catch (
const detail::json::parse_error& err) {
328 throw InputError(
"environment_reader: malformed JSON in " + path +
": " + err.what());
UnsupportedError(const std::string &what)
void register_node_failure(const std::string &node_name, const lang::Distrib< T > &breakdown, const lang::Distrib< T > &down_service, bool has_repair, const lang::Distrib< T > &repair, const std::string &breakdown_reset, const std::string &repair_reset)
Port of registerNodeFailure: attach a breakdown descriptor, and its reset policies,...
static std::string down_stage_name(const std::string &nm)
The name addNodeBreakdown gives the stage in which nm is down.
void set_stage(std::size_t e, const std::string &nm, const std::string &type, const qn::NetworkStruct< T > &model)
addStage: name the stage and give it its network.
void add_transition(std::size_t e, std::size_t h, const lang::Distrib< T > &d, const ResetMarginal &reset=ResetMarginal())
addTransition: enable e -> h with a distribution and a reset policy.
A queueing network under construction.
const NetworkStruct< T > & get_struct()
The refreshed struct, MATLAB's model.getStruct().
A random environment: a port of matlab/src/lang/Environment.m, restricted to what SolverENV reads out...
The exception types the port throws.
env::Environment< T > build_environment_from_json(const detail::json &root)
Build an env::Environment<T> from a parsed model.json envelope.
env::Environment< T > read_environment_json(const std::string &path)
Parse a model.json file into an env::Environment<T>.
qn::Network< T > build_network_from_json(const detail::json &root)
Build a qn::Network<T> from a parsed model.json envelope.
Reader for the LINE model.json interchange (a Network model) into a qn::Network<T> built through the ...
Number-type abstraction for the templated API port.