5#ifndef LINE_IO_LQN_JSON_READER_H
6#define LINE_IO_LQN_JSON_READER_H
57 if (s ==
"INF" || s ==
"inf")
return S::INF;
58 if (s ==
"FCFS" || s ==
"fcfs")
return S::FCFS;
59 if (s ==
"PS" || s ==
"ps")
return S::PS;
60 if (s ==
"HOL" || s ==
"hol")
return S::HOL;
61 if (s ==
"REF" || s ==
"ref")
return S::REF;
62 if (s ==
"SIRO" || s ==
"siro")
return S::SIRO;
63 if (s ==
"LCFS" || s ==
"lcfs")
return S::LCFS;
64 if (s ==
"LCFSPR" || s ==
"lcfspr")
return S::LCFSPR;
65 throw UnsupportedError(
"lqn_json_reader: unsupported scheduling discipline '" + s +
"'");
75inline double lqn_mult_from_json(
const json& v) {
77 const std::string s = v.get<std::string>();
78 if (s ==
"Infinity" || s ==
"inf")
return std::numeric_limits<double>::infinity();
79 return std::atof(s.c_str());
81 const double m = v.get<
double>();
82 return m >= 2147483647.0 ? std::numeric_limits<double>::infinity() : m;
94 const json& model = root.contains(
"model") ? root.at(
"model") : root;
95 const std::string mtype = model.value(
"type", std::string(
"LayeredNetwork"));
96 if (mtype !=
"LayeredNetwork")
98 "' is not a LayeredNetwork; a Network model is read by "
106 const json empty = json::array();
107 const json& hosts = model.contains(
"hosts")
109 : (model.contains(
"processors") ? model.at(
"processors") : empty);
110 for (
const json& h : hosts) {
111 const std::string name = h.at(
"name").get<std::string>();
112 if (h.contains(
"admissionConstraints"))
114 "lqn_json_reader: host '" + name +
115 "' carries admission constraints, which this reader does not rebuild");
117 detail::lqn_sched_from_json(h.value(
"scheduling", std::string(
"PS")));
118 const double mult = h.contains(
"multiplicity")
119 ? detail::lqn_mult_from_json(h.at(
"multiplicity"))
121 b.
processor(name, mult, sched, h.value(
"replication", 1.0));
125 for (
const json& t : model.at(
"tasks")) {
126 const std::string name = t.at(
"name").get<std::string>();
127 if (t.contains(
"fanIn") || t.contains(
"fanOut"))
129 "lqn_json_reader: task '" + name +
130 "' declares fan-in or fan-out replication, which the layer decomposition in this "
131 "port does not honour; a call multiplicity applied at 1 would understate every "
132 "visit through the replicated task");
133 if (t.contains(
"admissionConstraints"))
135 "lqn_json_reader: task '" + name +
136 "' carries admission constraints, which this reader does not rebuild");
137 const std::string on = t.at(
"host").get<std::string>();
139 detail::lqn_sched_from_json(t.value(
"scheduling", std::string(
"FCFS")));
140 const double mult = t.contains(
"multiplicity")
141 ? detail::lqn_mult_from_json(t.at(
"multiplicity"))
143 const double repl = t.value(
"replication", 1.0);
144 const std::string kind = t.value(
"taskType", std::string());
145 if (kind ==
"CacheTask") {
146 std::vector<int> cap;
147 if (t.at(
"cacheCapacity").is_array())
148 cap = t.at(
"cacheCapacity").get<std::vector<int> >();
150 cap.push_back(t.at(
"cacheCapacity").get<
int>());
151 b.
cache_task(name, mult, sched, on, t.at(
"totalItems").get<std::size_t>(), cap,
152 detail::replacement_from_json(
153 t.value(
"replacementStrategy", std::string(
"FIFO"))),
156 b.
task(name, mult, sched, on, repl);
160 if (t.contains(
"thinkTime"))
161 b.
think_time(name, detail::dist_from_json<T>(t.at(
"thinkTime")));
162 else if (t.contains(
"thinkTimeMean") && t.at(
"thinkTimeMean").get<
double>() > 0)
164 t.at(
"thinkTimeMean").get<
double>())));
174 const bool has_setup_obj = t.contains(
"setupTime");
175 const bool has_setup_mean =
176 t.contains(
"setupTimeMean") && t.at(
"setupTimeMean").get<
double>() > 0;
177 if (has_setup_obj || has_setup_mean) {
178 const bool has_off_obj = t.contains(
"delayOffTime");
179 const bool has_off_mean =
180 t.contains(
"delayOffTimeMean") && t.at(
"delayOffTimeMean").get<
double>() > 0;
181 if (!has_off_obj && !has_off_mean)
182 throw InputError(
"lqn_json_reader: task '" + name +
183 "' declares a setup time with no delay-off time; a server that "
184 "never shuts down pays the setup at most once");
186 has_setup_obj ? detail::dist_from_json<T>(t.at(
"setupTime"))
188 t.at(
"setupTimeMean").get<
double>()));
190 has_off_obj ? detail::dist_from_json<T>(t.at(
"delayOffTime"))
192 t.at(
"delayOffTimeMean").get<
double>()));
198 for (
const json& e : model.at(
"entries")) {
199 const std::string name = e.at(
"name").get<std::string>();
200 const std::string on = e.at(
"task").get<std::string>();
201 if (e.value(
"entryType", std::string()) ==
"ItemEntry") {
202 const std::size_t card = e.at(
"totalItems").get<std::size_t>();
207 if (e.contains(
"accessProb"))
208 pop = detail::pmf_from_json<T>(e.at(
"accessProb"), card);
215 if (e.contains(
"arrival"))
216 b.
open_arrival(name, detail::dist_from_json<T>(e.at(
"arrival")));
220 for (
const json& a : model.at(
"activities")) {
221 const std::string name = a.at(
"name").get<std::string>();
222 const std::string on = a.at(
"task").get<std::string>();
226 ? detail::dist_from_json<T>(a.at(
"hostDemand"))
229 if (a.contains(
"boundToEntry"))
230 b.
bound_to(name, a.at(
"boundToEntry").get<std::string>());
231 else if (a.contains(
"boundTo"))
232 b.
bound_to(name, a.at(
"boundTo").get<std::string>());
233 if (a.contains(
"repliesTo")) b.
replies_to(name, a.at(
"repliesTo").get<std::string>());
234 const char* kCallKey[2] = {
"synchCalls",
"asynchCalls"};
235 for (
int which = 0; which < 2; ++which) {
236 if (!a.contains(kCallKey[which]))
continue;
237 for (
const json& c : a.at(kCallKey[which])) {
239 const std::string dest = c.contains(
"dest")
240 ? c.at(
"dest").get<std::string>()
241 : c.at(
"entry").get<std::string>();
243 if (which == 0) b.
sync_call(name, dest, mean);
251 for (
const json& e : model.at(
"entries")) {
252 if (!e.contains(
"forwarding"))
continue;
253 const std::string name = e.at(
"name").get<std::string>();
254 for (
const json& f : e.at(
"forwarding"))
255 b.
forward(name, f.at(
"dest").get<std::string>(),
265 if (model.contains(
"precedences")) {
266 for (
const json& p : model.at(
"precedences")) {
267 const std::string kind = p.value(
"type", std::string());
270 const std::string pre = p.at(
"preType").get<std::string>();
271 const std::string post = p.at(
"postType").get<std::string>();
272 std::vector<std::string> pres, posts;
273 for (
const json& x : p.at(
"preActs")) pres.push_back(x.get<std::string>());
274 for (
const json& x : p.at(
"postActs")) posts.push_back(x.get<std::string>());
275 if (pre ==
"PRE_SEQ" && post ==
"POST_SEQ") b.
serial(pres.at(0), posts.at(0));
276 else if (pre ==
"PRE_SEQ" && post ==
"POST_AND") b.
and_fork(pres.at(0), posts);
277 else if (pre ==
"PRE_AND" && post ==
"POST_SEQ") b.
and_join(pres, posts.at(0));
278 else if (pre ==
"PRE_OR" && post ==
"POST_SEQ") b.
or_join(pres, posts.at(0));
279 else if (pre ==
"PRE_SEQ" && post ==
"POST_OR") {
280 std::vector<T> probs;
281 if (p.contains(
"postParams"))
282 probs = detail::num_vec_from_json<T>(p.at(
"postParams"));
283 b.
or_fork(pres.at(0), posts, probs);
284 }
else if (post ==
"POST_LOOP") {
288 if (posts.size() < 2)
290 "lqn_json_reader: a loop's postActs is the body followed by the "
291 "activity it exits to, so it holds at least two entries");
292 const T count = p.contains(
"postParams")
293 ? detail::num_vec_from_json<T>(p.at(
"postParams")).at(0)
295 b.
loop(pres.at(0), std::vector<std::string>(posts.begin(), posts.end() - 1),
296 posts.back(), count);
297 }
else if (post ==
"POST_CACHE") {
300 throw UnsupportedError(
"lqn_json_reader: precedence " + pre +
" -> " + post +
301 " has no builder counterpart");
305 std::vector<std::string> acts;
306 for (
const json& x : p.at(
"activities")) acts.push_back(x.get<std::string>());
307 if (kind ==
"Serial") {
310 for (std::size_t i = 0; i + 1 < acts.size(); ++i) b.
serial(acts[i], acts[i + 1]);
311 }
else if (kind ==
"AndFork") {
312 b.
and_fork(acts.at(0), std::vector<std::string>(acts.begin() + 1, acts.end()));
313 }
else if (kind ==
"AndJoin") {
314 b.
and_join(std::vector<std::string>(acts.begin(), acts.end() - 1), acts.back());
315 }
else if (kind ==
"OrFork") {
316 std::vector<T> probs;
317 if (p.contains(
"probabilities"))
318 probs = detail::num_vec_from_json<T>(p.at(
"probabilities"));
319 b.
or_fork(acts.at(0), std::vector<std::string>(acts.begin() + 1, acts.end()),
321 }
else if (kind ==
"OrJoin") {
322 b.
or_join(std::vector<std::string>(acts.begin(), acts.end() - 1), acts.back());
323 }
else if (kind ==
"Loop") {
326 if (!p.contains(
"preActivity"))
328 "lqn_json_reader: a Loop precedence carries its trigger as 'preActivity'");
331 "lqn_json_reader: a Loop precedence lists the body followed by the "
332 "activity it exits to, so it holds at least two entries");
333 b.
loop(p.at(
"preActivity").get<std::string>(),
334 std::vector<std::string>(acts.begin(), acts.end() - 1), acts.back(),
336 }
else if (kind ==
"CacheAccess") {
339 "lqn_json_reader: a CacheAccess precedence names the read activity plus "
340 "its hit and miss branches");
344 "' has no builder counterpart");
360 std::ifstream in(path.c_str());
361 if (!in)
return false;
363 while (in.get(c) && std::isspace(
static_cast<unsigned char>(c))) {}
364 if (c !=
'{')
return false;
369 }
catch (
const detail::json::parse_error&) {
372 const detail::json& model = root.contains(
"model") ? root.at(
"model") : root;
373 return model.value(
"type", std::string()) ==
"LayeredNetwork";
379 std::ifstream in(path.c_str());
380 if (!in)
throw InputError(
"lqn_json_reader: cannot open " + path);
384 }
catch (
const detail::json::parse_error& e) {
385 throw InputError(
"lqn_json_reader: malformed JSON in " + path +
": " + e.what());
UnsupportedError(const std::string &what)
void bound_to(const std::string &act, const std::string &entry_name)
Bind an activity to an entry: it is the entry's first activity.
std::size_t activity(const std::string &name, const Distrib< T > &hostdem, const std::string &on_task)
Add an activity on a task, with its host demand.
LqnStruct< T > build() const
Flatten into the struct SolverLN consumes.
void loop(const std::string &pre, const std::vector< std::string > &body, const std::string &end, const T &count)
pre -> body, repeated count times in expectation, then -> end.
void open_arrival(const std::string &entry_name, const Distrib< T > &d)
An open arrival stream at an entry.
void setup_time(const std::string &task_name, const Distrib< T > &setup, const Distrib< T > &delayoff)
A SetupTask: a server that powers down when idle and pays to restart.
std::size_t processor(const std::string &name, double mult, SchedStrategy sched, double repl=1.0)
Add a processor.
void replies_to(const std::string &act, const std::string &entry_name)
Mark an activity as the one that replies to an entry.
void forward(const std::string &src_entry, const std::string &dest_entry, const T &prob)
Forwarding: whenever src_entry is invoked, with probability prob the request is handed onward to dest...
void and_fork(const std::string &pre, const std::vector< std::string > &posts)
pre -> every post, concurrently.
void cache_access(const std::string &pre, const std::string &hit, const std::string &miss)
ActivityPrecedence.CacheAccess(pre, {hit, miss}).
std::size_t cache_task(const std::string &name, double mult, SchedStrategy sched, const std::string &on_processor, std::size_t nitems, const std::vector< int > &itemcap, ReplacementStrategy replacestrat, double repl=1.0)
A CacheTask: a task whose entries are looked up in a cache of nitems.
void async_call(const std::string &act, const std::string &dest_entry, const T &mean)
An asynchronous call from an activity to an entry of another task.
void and_join(const std::vector< std::string > &pres, const std::string &post, std::size_t quorum=0)
all pres (or quorum of them) -> post.
void serial(const std::string &pre, const std::string &post)
pre -> post, a plain sequence.
void sync_call(const std::string &act, const std::string &dest_entry, const T &mean)
A synchronous call from an activity to an entry of another task.
void think_time(const std::string &task_name, const Distrib< T > &d)
Set a task's think time.
void or_fork(const std::string &pre, const std::vector< std::string > &posts, const std::vector< T > &probs)
pre -> one of the posts, with the given branch probabilities.
void or_join(const std::vector< std::string > &pres, const std::string &post)
any of the pres -> post.
std::size_t item_entry(const std::string &name, const std::string &on_task, std::size_t cardinality, const std::vector< T > &popularity)
An ItemEntry: the entry a cache read enters, over cardinality items.
std::size_t entry(const std::string &name, const std::string &on_task)
Add an entry on a task.
std::size_t task(const std::string &name, double mult, SchedStrategy sched, const std::string &on_processor, double repl=1.0)
Add a task on a processor.
The exception types the port throws.
Build a layered queueing network in code, as the MATLAB constructors do.
.lqnx -> LqnStruct, a port of matlab/src/lang/layered/@LayeredNetwork/parseXML.m followed by ....
LayeredNetworkStruct, the flattened description of a layered queueing network.
bool is_layered_json(const std::string &path)
True when a file is a LayeredNetwork model.json rather than an .lqnx.
lqn::LqnStruct< T > read_lqn_json(const std::string &path)
Parse a LayeredNetwork model.json file into an LqnStruct<T>.
lqn::LqnStruct< T > read_layered_model(const std::string &path)
Read a layered model from either interchange: the LINE model.json or the LQNS .lqnx.
lqn::LqnStruct< T > build_lqn_from_json(const detail::json &root)
Build an LqnStruct<T> from a parsed model.json envelope carrying a LayeredNetwork.
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
LqnStruct< T > read_lqnx(const std::string &path)
Read a .lqnx model.
Reader for the LINE model.json interchange (a Network model) into a qn::Network<T> built through the ...
static Distrib immediate()
The Immediate singleton.
static Distrib exp_mean(const T &m)