5#ifndef LINE_IO_JMVA_READER_H
6#define LINE_IO_JMVA_READER_H
73 std::size_t b = 0, e = s.size();
74 while (b < e && (s[b] ==
' ' || s[b] ==
'\t' || s[b] ==
'\n' || s[b] ==
'\r')) ++b;
75 while (e > b && (s[e - 1] ==
' ' || s[e - 1] ==
'\t' || s[e - 1] ==
'\n' || s[e - 1] ==
'\r'))
77 const std::string t = s.substr(b, e - b);
78 if (t.empty())
throw InputError(
"jmva2line: " + what +
" is empty");
80 const double v = std::strtod(t.c_str(), &end);
81 if (end == t.c_str() || *end !=
'\0')
82 throw InputError(
"jmva2line: " + what +
" is '" + t +
83 "', which is not a number (a SPEX template variable must be "
84 "substituted before import)");
90 if (!e)
return nullptr;
91 const std::vector<const Element*> v = e->
child_tags(tag);
92 return v.empty() ? nullptr : v[0];
97 const std::string& cls) {
98 if (!parent)
return nullptr;
100 if (e->attr(
"customerclass") == cls)
return e;
106 const char sep = s.find(
';') != std::string::npos ?
';' :
',';
107 std::vector<double> out;
108 std::size_t start = 0;
109 while (start <= s.size()) {
110 std::size_t end = s.find(sep, start);
111 if (end == std::string::npos) end = s.size();
112 const std::string tok = s.substr(start, end - start);
113 char* stop =
nullptr;
114 const double v = std::strtod(tok.c_str(), &stop);
117 if (!(c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r')) blank =
false;
119 while (*stop ==
' ' || *stop ==
'\t' || *stop ==
'\n' || *stop ==
'\r') ++stop;
120 if (stop == tok.c_str() || *stop !=
'\0')
121 throw InputError(
"jmva2line: load-dependent service time list '" + s +
122 "' holds the non-numeric entry '" + tok +
"'");
132 const std::size_t slash = path.find_last_of(
"/\\");
133 std::string f = slash == std::string::npos ? path : path.substr(slash + 1);
134 const std::size_t dot = f.find_last_of(
'.');
135 if (dot != std::string::npos && dot > 0) f = f.substr(0, dot);
153 if (!doc)
throw InputError(
"jmva2line: cannot parse '" + path +
"' as XML");
156 const Element* root = doc.get();
158 const Element* params = child(root,
"parameters");
159 if (!params)
throw InputError(
"jmva2line: '" + path +
"' carries no <parameters> element");
160 const Element* xstations = child(params,
"stations");
161 const Element* xclasses = child(params,
"classes");
162 if (!xstations)
throw InputError(
"jmva2line: '" + path +
"' carries no <stations> element");
163 if (!xclasses)
throw InputError(
"jmva2line: '" + path +
"' carries no <classes> element");
167 const std::vector<const Element*> xdelay = xstations->
child_tags(
"delaystation");
168 const std::vector<const Element*> xli = xstations->
child_tags(
"listation");
169 const std::vector<const Element*> xld = xstations->
child_tags(
"ldstation");
172 std::vector<std::size_t> nodes;
173 std::vector<const Element*> xnode;
174 for (
const Element* e : xdelay) {
175 nodes.push_back(model.
add_delay(e->attr(
"name")));
184 double nservers = 1.0;
185 if (e->has_attr(
"servers"))
186 nservers = to_double_strict(e->attr(
"servers"),
187 "the 'servers' of ldstation '" + e->attr(
"name") +
"'");
192 const std::size_t nInf = xdelay.size(), nLI = xli.size();
193 const std::size_t M = nodes.size();
196 std::vector<std::string> cname;
197 std::vector<std::size_t> cls;
198 const std::vector<const Element*> xopen = xclasses->
child_tags(
"openclass");
199 const std::vector<const Element*> xclosed = xclasses->
child_tags(
"closedclass");
200 std::size_t source = 0, sink = 0;
201 if (!xopen.empty()) {
204 for (
const Element* e : xopen) {
205 const std::string nm = e->attr(
"name");
207 const double rate = to_double_strict(e->attr(
"rate"),
"the rate of open class '" + nm +
"'");
213 if (!xclosed.empty() && M == 0)
214 throw InputError(
"jmva2line: '" + path +
"' declares closed classes but no station");
215 for (
const Element* e : xclosed) {
216 const std::string nm = e->attr(
"name");
218 to_double_strict(e->attr(
"population"),
"the population of closed class '" + nm +
"'");
224 const std::size_t K = cls.size();
225 const std::size_t nOpen = xopen.size();
228 std::vector<std::vector<bool>> visited(M, std::vector<bool>(K,
false));
229 for (std::size_t i = 0; i < M; ++i) {
231 const std::string sname = st->
attr(
"name");
232 const Element* xst = child(st,
"servicetimes");
233 const Element* xvis = child(st,
"visits");
234 const bool is_ld = i >= nInf + nLI;
235 for (std::size_t r = 0; r < K; ++r) {
236 const Element* v = by_class(xvis,
"visit", cname[r]);
238 throw InputError(
"jmva2line: station '" + sname +
"' gives no visit for class '" +
240 const double visits =
241 to_double_strict(v->
text,
"the visits of class '" + cname[r] +
"' at '" + sname +
"'");
243 const Element* s = by_class(xst,
"servicetime", cname[r]);
245 throw InputError(
"jmva2line: station '" + sname +
246 "' gives no service time for class '" + cname[r] +
"'");
247 const double stime = to_double_strict(
248 s->
text,
"the service time of class '" + cname[r] +
"' at '" + sname +
"'");
252 visited[i][r] =
true;
254 model.
set_service(nodes[i], cls[r], D::disabled_dist());
259 std::vector<double> demands;
260 if (
const Element* s = by_class(xst,
"servicetimes", cname[r])) {
261 demands = split_demands(s->text);
264 demands.push_back(to_double_strict(
265 c->text,
"a service time of class '" + cname[r] +
"' at '" + sname +
"'"));
266 }
else if (
const Element* s1 = by_class(xst,
"servicetime", cname[r])) {
267 demands.push_back(to_double_strict(
268 s1->text,
"the service time of class '" + cname[r] +
"' at '" + sname +
"'"));
270 throw InputError(
"jmva2line: ldstation '" + sname +
271 "' gives no service times for class '" + cname[r] +
"'");
273 if (demands.size() > 1 && demands[0] == 0.0) demands.erase(demands.begin());
274 if (visits > 0.0 && !demands.empty() && demands[0] > 0.0) {
275 const double base = demands[0] * visits;
277 visited[i][r] =
true;
278 if (demands.size() > 1) {
279 std::vector<T> alpha(demands.size());
280 for (std::size_t n = 0; n < demands.size(); ++n)
282 demands[n] > 0.0 ? demands[0] / demands[n] : 0.0);
288 model.
set_service(nodes[i], cls[r], D::disabled_dist());
295 for (std::size_t r = 0; r < K; ++r) {
296 std::vector<std::size_t> path_nodes;
297 if (r < nOpen) path_nodes.push_back(source);
298 for (std::size_t i = 0; i < M; ++i)
299 if (visited[i][r]) path_nodes.push_back(nodes[i]);
300 if (r < nOpen) path_nodes.push_back(sink);
301 if (path_nodes.empty())
continue;
A queueing network under construction.
void set_load_dependence(std::size_t node, const std::vector< T > &alpha)
station.setLoadDependence(alpha): the rate multiplier at population 1, 2, ... The vector is indexed f...
std::size_t add_source(const std::string &nm)
The external arrival station.
std::size_t add_open_class(const std::string &nm, int prio=0)
An open class.
std::size_t add_delay(const std::string &nm)
An infinite-server station (a Delay, MATLAB's Delay / DelayStation).
RoutingMatrix< T > init_routing_matrix() const
An empty routing matrix, MATLAB's model.initRoutingMatrix.
void set_number_of_servers(std::size_t node, double n)
queue.setNumberOfServers(n).
RoutingMatrix< T > serial_routing(const std::vector< std::size_t > &nodes) const
model.serialRouting(nodes): a unit-probability path through nodes.
std::size_t add_queue(const std::string &nm, SchedStrategy sched=SchedStrategy::FCFS)
A queueing station.
std::size_t add_closed_class(const std::string &nm, double njobs, std::size_t refstat_node, int prio=0)
A closed class of the given population, referencing a station node.
std::size_t add_sink(const std::string &nm)
The external departure node.
void link(const RoutingMatrix< T > &Pm)
model.link(P): install the routing.
void set_service(std::size_t node, std::size_t cls, const Distrib< T > &d)
station.setService(class, dist).
void set_arrival(std::size_t node, std::size_t cls, const Distrib< T > &d)
source.setArrival(class, dist): the same table, at the Source.
The routing matrix a model script fills in, MATLAB's P cell array.
void set(std::size_t r, std::size_t s, std::size_t i, std::size_t j, const T &p)
The exception types the port throws.
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
std::string file_stem(const std::string &path)
Basename without directory or extension, MATLAB's [~,fname] = fileparts.
const Element * child(const Element *e, const std::string &tag)
The first direct child with the given tag, or null.
double to_double_strict(const std::string &s, const std::string &what)
Strict decimal parse: the whole (trimmed) text must be a number.
const Element * by_class(const Element *parent, const std::string &tag, const std::string &cls)
The entry of tag under parent whose customerclass is cls, or null.
std::vector< double > split_demands(const std::string &s)
str2double(strsplit(s, ';')) (or ','), NaN entries dropped as the reference does.
qn::Network< T > jmva2line(const std::string &path, const std::string &name=std::string())
Port of JMVA2LINE(filename, modelName).
std::unique_ptr< Element > parse_file(const std::string &path)
Read and parse a file.
Conservation laws of a layered queueing network, enumerated from its structure.
The Network constructor API: Queue, Delay, Source, Sink, Router, ClassSwitch, Cache,...
Number-type abstraction for the templated API port.
std::string text
Character data of a text-only element, as createTextNode supplies it.
std::string attr(const std::string &key) const
Attribute value, or the empty string when absent (org.w3c.dom semantics).
std::vector< const Element * > child_tags(const std::string &tag) const
Direct children with the given tag, in document order.
std::vector< const Element * > child_tags(const std::string &tag) const
Direct children with the given tag, in document order.
A minimal XML DOM: read for the .lqnx interchange format, write for the JMT .jsimg and ....