295 using namespace jsim_detail;
297 if (!doc)
throw InputError(
"read_jsim: cannot parse '" + path +
"' as XML");
300 const Element*
sim = doc->name ==
"sim" ? doc.get() :
nullptr;
302 const std::vector<const Element*> s = doc->by_tag(
"sim");
305 "' carries no <sim> element, so it is not a JSIM model");
310 const std::vector<const Element*> xnodes =
sim->child_tags(
"node");
311 const std::vector<const Element*> xclasses =
sim->child_tags(
"userClass");
312 if (xnodes.empty())
throw InputError(
"read_jsim: the model declares no node");
313 if (xclasses.empty())
throw InputError(
"read_jsim: the model declares no job class");
314 const std::size_t N = xnodes.size(), K = xclasses.size();
316 std::vector<std::string> orig_name(N), node_name(N);
317 for (std::size_t i = 0; i < N; ++i) {
318 orig_name[i] = xnodes[i]->attr(
"name");
319 node_name[i] = sanitize(orig_name[i]);
322 std::vector<std::vector<const Element*> > sec(N);
323 for (std::size_t i = 0; i < N; ++i) sec[i] = xnodes[i]->child_tags(
"section");
325 auto secclass = [&](std::size_t i, std::size_t k) -> std::string {
326 const Element* e = at(sec[i], k);
327 return e ? e->attr(
"className") : std::string();
331 std::vector<std::size_t> nidx(N, 0);
333 std::vector<bool> is_station(N,
false), is_source(N,
false), is_delay(N,
false);
334 std::vector<std::vector<T> > schedparam(N);
335 std::size_t forknode = 0;
337 for (std::size_t i = 0; i < N; ++i) {
338 const std::string in = secclass(i, 0), svc = secclass(i, 1), out = secclass(i, 2);
339 if (in ==
"JobSink") {
340 nidx[i] = net.
add_sink(node_name[i]);
343 if (in ==
"RandomSource") {
345 is_station[i] = is_source[i] =
true;
351 "read_jsim: a Join appears before any Fork; the importer supports at most one "
352 "fork-join pair and reads them in document order, as the reference does");
353 nidx[i] = net.
add_join(node_name[i], forknode);
356 if (in ==
"Storage") {
358 is_station[i] =
true;
361 if (in ==
"Enabling") {
367 if (in !=
"Queue" && in !=
"Buffer")
369 "' has input section '" + in +
370 "', which this reader does not build");
373 const std::vector<const Element*> op = params(at(sec[i], 2));
374 const double tpl = op.empty() ? 1.0 : to_num(value_of(op[0]));
375 nidx[i] = net.
add_fork(node_name[i], tpl);
379 if (svc ==
"ServiceTunnel") {
383 if (svc ==
"ClassSwitch" || svc ==
"StatelessClassSwitcher") {
395 const std::vector<const Element*> ip = params(at(sec[i], 0));
401 const std::size_t putpos =
402 (ip.size() > 2 && ip[2]->attr(
"name") ==
"retrialDistributions") ? 4 : 3;
403 const std::vector<const Element*> put = subs(at(ip, putpos));
404 const std::string putname = put.empty() ? std::string() : put[0]->attr(
"name");
414 if (svc ==
"Delay" || svc ==
"InfiniteServer") {
416 is_station[i] = is_delay[i] =
true;
417 }
else if (svc ==
"PSServer" || svc ==
"SharedServer") {
420 const std::vector<const Element*> sp = params(at(sec[i], 1));
421 std::string ps =
"EPSStrategy";
423 const std::vector<const Element*> ss = subs(sp[3]);
424 if (!ss.empty()) ps = ss[0]->attr(
"name");
434 for (
const Element* w : subs(sp[4]))
436 nidx[i] = net.
add_queue(node_name[i], sched[i]);
437 is_station[i] =
true;
438 }
else if (svc ==
"Server" || svc ==
"PreemptiveServer") {
439 nidx[i] = net.
add_queue(node_name[i], sched[i]);
440 is_station[i] =
true;
442 throw UnsupportedError(
"read_jsim: node '" + orig_name[i] +
"' has service section '" +
443 svc +
"', which this reader does not build");
448 const double cap = to_num(value_of(ip[0]));
451 net.
set_capacity(nidx[i], cap < 0 ? std::numeric_limits<double>::infinity() : cap);
453 if (svc !=
"Delay" && svc !=
"InfiniteServer") {
454 const std::vector<const Element*> sp = params(at(sec[i], 1));
455 const Element* ns = named(sp,
"maxJobs");
456 if (!ns && !sp.empty()) ns = sp[0];
458 const double c = to_num(value_of(ns));
460 nidx[i], c < 0 ? std::numeric_limits<double>::infinity() : c);
471 for (std::size_t r = 0; r < K; ++r)
472 maxprio = std::max(maxprio,
static_cast<int>(to_num(xclasses[r]->attr(
"priority"))));
474 std::map<std::string, std::size_t> node_by_name;
475 for (std::size_t i = 0; i < N; ++i)
477 node_by_name[node_name[i]] = nidx[i];
478 node_by_name[orig_name[i]] = nidx[i];
481 std::vector<std::size_t> cidx(K, 0);
482 std::vector<bool> cs_referenced(K,
false);
483 for (std::size_t r = 0; r < K; ++r) {
484 const std::string cname = xclasses[r]->attr(
"name");
485 const std::string type = xclasses[r]->attr(
"type");
486 const int prio = maxprio -
static_cast<int>(to_num(xclasses[r]->attr(
"priority")));
487 const std::string refsrc = xclasses[r]->attr(
"referenceSource");
488 if (type ==
"closed") {
489 const std::map<std::string, std::size_t>::const_iterator it =
490 node_by_name.find(refsrc);
491 if (it == node_by_name.end())
492 throw InputError(
"read_jsim: class '" + cname +
"' names reference source '" +
493 refsrc +
"', which is not a node of the model");
494 cidx[r] = net.
add_closed_class(cname, to_num(xclasses[r]->attr(
"customers")),
502 if (refsrc ==
"ClassSwitch" || refsrc ==
"StatelessClassSwitcher")
503 cs_referenced[r] =
true;
508 for (std::size_t i = 0; i < N; ++i) {
509 const std::string svc3 = secclass(i, 1);
510 if (nidx[i] && (svc3 ==
"ClassSwitch" || svc3 ==
"StatelessClassSwitcher")) {
511 const std::vector<const Element*> sp = params(at(sec[i], 1));
522 const std::vector<const Element*> rows = subs(sp[0]);
523 for (std::size_t r = 0; r < rows.size() && r < K; ++r) {
524 const std::vector<const Element*> cols = subs(rows[r]);
525 for (std::size_t c = 0; c < cols.size() && c < K; ++c)
531 const std::string in = secclass(i, 0);
532 if (in ==
"Storage") {
533 const std::vector<const Element*> ip = params(at(sec[i], 0));
535 const double cap = to_num(value_of(ip[0]));
537 cap < 0 ? std::numeric_limits<double>::infinity() : cap);
540 const std::vector<const Element*> pc = subs(ip[1]);
541 for (std::size_t c = 0; c < pc.size() && c < K; ++c) {
542 const double v = to_num(value_of(pc[c]));
544 v < 0 ? std::numeric_limits<double>::infinity() : v);
548 const std::vector<const Element*> dr = subs(ip[2]);
549 for (std::size_t c = 0; c < dr.size() && c < K; ++c) {
550 const std::string rule = value_of(dr[c]);
551 if (rule ==
"BAS blocking")
553 else if (rule ==
"drop")
555 else if (rule ==
"waiting queue")
561 if (in !=
"Enabling")
continue;
564 const std::vector<const Element*> ep = params(at(sec[i], 0));
565 const std::vector<const Element*> tp = params(at(sec[i], 1));
566 const std::vector<const Element*> fp = params(at(sec[i], 2));
567 if (ep.size() < 2 || tp.size() < 5 || fp.empty())
568 throw InputError(
"read_jsim: transition '" + orig_name[i] +
569 "' is missing one of the enabling, timing or firing parameters");
570 const std::vector<const Element*> enmodes = subs(ep[0]);
571 const std::vector<const Element*> inmodes = subs(ep[1]);
572 const std::vector<const Element*> names = subs(tp[0]);
573 const std::vector<const Element*> nserv = subs(tp[1]);
574 const std::vector<const Element*> timing = subs(tp[2]);
575 const std::vector<const Element*> fprio = subs(tp[3]);
576 const std::vector<const Element*> fweight = subs(tp[4]);
577 const std::vector<const Element*> fmodes = subs(fp[0]);
578 const std::size_t M = enmodes.size();
585 const std::size_t inf_marker = 0;
587 const double dinf = std::numeric_limits<double>::infinity();
596 auto read_arcs = [&](
const Element* mode,
Matrix<T>& row,
bool inhibiting) {
597 const std::vector<const Element*> lvl1 = subs(mode);
598 if (lvl1.empty())
return;
599 for (
const Element* arc : subs(lvl1[0])) {
600 const std::vector<const Element*> ap = subs(arc);
601 if (ap.size() < 2)
continue;
602 const std::string target = value_of(ap[0]);
603 const std::map<std::string, std::size_t>::const_iterator it =
604 node_by_name.find(target);
605 if (it == node_by_name.end())
continue;
606 const std::vector<const Element*> per = subs(ap[1]);
607 for (std::size_t c = 0; c < per.size() && c < K; ++c) {
608 const double w = to_num(value_of(per[c]));
609 const std::size_t rr = cidx[c] ? cidx[c] - 1 : c;
614 row(it->second - 1, rr) = (w <= 0.0)
618 row(it->second - 1, rr) = (w < 0.0)
626 for (std::size_t m = 0; m < M; ++m) {
627 par.
modenames.push_back(m < names.size() ? value_of(names[m])
628 :
"mode" + std::to_string(m + 1));
629 read_arcs(enmodes[m], par.
enabling[m],
false);
630 if (m < inmodes.size()) read_arcs(inmodes[m], par.
inhibiting[m],
true);
631 if (m < fmodes.size()) read_arcs(fmodes[m], par.
firing[m],
false);
633 const double ns = m < nserv.size() ? to_num(value_of(nserv[m])) : 1.0;
635 par.
firingprio.push_back(m < fprio.size() ? to_num(value_of(fprio[m])) : 0.0);
637 m < fweight.size() ? to_num(value_of(fweight[m])) : 1.0));
639 const Element* tm = at(timing, m);
640 const std::string cp = tm ? tm->attr(
"classPath") : std::string();
641 if (cp.find(
"ZeroServiceTimeStrategy") != std::string::npos) {
647 const std::vector<const Element*> ts = subs(tm);
649 at(ts, 0), at(ts, 1),
"read_jsim (transition '" + orig_name[i] +
"')");
654 par.
firingdep.assign(M, std::function<T(
const std::vector<T>&)>());
656 node_by_name[node_name[i]] = nidx[i];
657 node_by_name[orig_name[i]] = nidx[i];
661 for (std::size_t i = 0; i < N; ++i) {
662 if (!nidx[i] || !is_station[i])
continue;
664 const std::vector<const Element*> sp = params(at(sec[i], 0));
665 if (sp.empty())
continue;
666 const std::vector<const Element*> per = subs(sp[0]);
667 for (std::size_t r = 0; r < K; ++r) {
668 if (cs_referenced[r]) {
673 read_strategy_dist<T>(at(per, r),
674 "read_jsim (arrival at '" + orig_name[i] +
679 const std::string svc = secclass(i, 1);
680 if (svc ==
"ClassSwitch" || svc ==
"StatelessClassSwitcher")
continue;
686 const std::vector<const Element*> sp = params(at(sec[i], 1));
687 const Element* strat = named(sp,
"ServiceStrategy");
688 if (!strat) strat = is_delay[i] ? at(sp, 0) : at(sp, 2);
689 if (!strat)
continue;
690 const std::vector<const Element*> per = subs(strat);
691 for (std::size_t r = 0; r < K; ++r) {
693 read_strategy_dist<T>(at(per, r),
"read_jsim (service at '" +
694 orig_name[i] +
"')"));
695 if (r < schedparam[i].size()) net.
set_sched_param(nidx[i], cidx[r], schedparam[i][r]);
700 std::vector<std::vector<bool> > conn(N, std::vector<bool>(N,
false));
701 std::map<std::string, std::size_t> pos_by_name;
702 for (std::size_t i = 0; i < N; ++i) {
703 pos_by_name[orig_name[i]] = i;
704 pos_by_name[node_name[i]] = i;
706 for (
const Element* c :
sim->child_tags(
"connection")) {
707 const std::map<std::string, std::size_t>::const_iterator a =
708 pos_by_name.find(c->attr(
"source"));
709 const std::map<std::string, std::size_t>::const_iterator b =
710 pos_by_name.find(c->attr(
"target"));
711 if (a == pos_by_name.end() || b == pos_by_name.end())
continue;
712 conn[a->second][b->second] =
true;
717 for (std::size_t from = 0; from < N; ++from) {
718 if (!nidx[from])
continue;
719 const std::string in = secclass(from, 0);
720 if (in ==
"JobSink" || in ==
"Storage" || in ==
"Enabling")
continue;
721 const std::vector<const Element*> op = params(at(sec[from], 2));
722 const std::vector<const Element*> per = op.empty() ? std::vector<const Element*>()
724 std::vector<std::size_t> targets;
725 for (std::size_t j = 0; j < N; ++j)
726 if (conn[from][j] && nidx[j]) targets.push_back(j);
728 for (std::size_t r = 0; r < K; ++r) {
729 const Element* st = at(per, r);
730 const std::string rs = st ? st->attr(
"name") : std::string(
"Random");
731 if (rs ==
"Disabled") {
735 if (rs ==
"Probabilities" || rs ==
"Weighted Round Robin") {
736 const bool wrr = rs !=
"Probabilities";
742 const std::vector<const Element*> arr = subs(st);
743 const std::vector<const Element*> entries =
744 arr.empty() ? arr : subs(arr[0]);
745 std::map<std::size_t, double> w;
746 for (
const Element* e : entries) {
747 const std::vector<const Element*> kv = subs(e);
748 if (kv.size() < 2)
continue;
749 const std::map<std::string, std::size_t>::const_iterator it =
750 pos_by_name.find(value_of(kv[0]));
751 if (it == pos_by_name.end() || !nidx[it->second])
continue;
752 w[it->second] = to_num(value_of(kv[1]));
755 std::map<std::size_t, double> wt;
756 for (std::map<std::size_t, double>::const_iterator it = w.begin();
758 wt[nidx[it->first]] = it->second;
763 for (std::size_t j = 0; j < targets.size(); ++j)
764 P.
set(cidx[r], cidx[r], nidx[from], nidx[targets[j]],
766 static_cast<double>(targets.size())));
768 for (std::map<std::size_t, double>::const_iterator it = w.begin();
770 P.
set(cidx[r], cidx[r], nidx[from], nidx[it->first],
782 else if (rs !=
"Random")
784 "' uses routing strategy '" + rs +
785 "', which this reader does not build");
789 for (
const Element* pj : subs(st)) {
790 if (pj->attr(
"name") ==
"k") kk =
static_cast<int>(to_num(value_of(pj)));
791 if (pj->attr(
"name") ==
"withMemory") {
792 const std::string wm = value_of(pj);
793 if (wm ==
"true" || wm ==
"1")
795 "read_jsim: node '" + orig_name[from] +
796 "' selects Power-of-k WITH MEMORY, which is Anselmi & Dufour's "
797 "SQ(d,N) and not the memoryless SQ(d) this port implements; "
798 "importing it as SQ(d) would answer about a different policy");
803 for (std::size_t j = 0; j < targets.size(); ++j)
804 P.
set(cidx[r], cidx[r], nidx[from], nidx[targets[j]],
808 static_cast<double>(targets.size())));
825 const std::vector<const Element*> pre =
sim->child_tags(
"preload");
827 for (
const Element* sp : pre[0]->child_tags(
"stationPopulations")) {
828 const std::map<std::string, std::size_t>::const_iterator it =
829 pos_by_name.find(sp->attr(
"stationName"));
830 if (it == pos_by_name.end() || !nidx[it->second])
continue;
831 const std::size_t pos = it->second;
832 const bool is_place = secclass(pos, 0) ==
"Storage";
834 for (
const Element* cp : sp->child_tags(
"classPopulation")) {
835 const std::string cn = cp->attr(
"refClass");
836 const double pop = to_num(cp->attr(
"population"));
837 for (std::size_t r = 0; r < K; ++r) {
838 if (xclasses[r]->attr(
"name") != cn)
continue;
840 if (is_place || pop == 0.0)
continue;
844 const std::string type = xclasses[r]->attr(
"type");
845 if (type ==
"closed" && xclasses[r]->attr(
"referenceSource") == orig_name[pos])
848 "read_jsim: the <preload> block places " + std::to_string(pop) +
849 " job(s) of class '" + cn +
"' at '" + orig_name[pos] +
850 "', which is not that class's reference station. This port derives a "
851 "queueing station's initial state from the class populations and "
852 "reference stations and has no initFromMarginal to override it, so the "
853 "marking is refused rather than dropped -- dropping it would solve a "
854 "model whose population starts somewhere else");