@abi-software/simulationvuer 1.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,8 +3,8 @@
3
3
  <p v-if="!hasValidSimulationUiInfo && !showUserMessage" class="default error"><span class="error">Error:</span> an unknown or invalid model was provided.</p>
4
4
  <div class="main" v-if="hasValidSimulationUiInfo">
5
5
  <div class="main-left">
6
- <p class="default name">{{name}}</p>
7
- <el-divider></el-divider>
6
+ <p class="default name" v-if="libopencor === undefined">{{name}}</p>
7
+ <el-divider v-if="libopencor === undefined"></el-divider>
8
8
  <p class="default input-parameters">Input parameters</p>
9
9
  <div class="input scrollbar">
10
10
  <SimulationVuerInput v-for="(input, index) in simulationUiInfo.input"
@@ -19,13 +19,16 @@
19
19
  />
20
20
  </div>
21
21
  <div class="primary-button">
22
- <el-button type="primary" size="small" @click="startSimulation()">Run Simulation</el-button>
22
+ <el-button type="primary" size="small" @click="startSimulation()" v-if="libopencor === undefined">Run Simulation</el-button>
23
23
  </div>
24
24
  <div class="secondary-button" v-if="uuid">
25
25
  <el-button size="small" @click="runOnOsparc()">Run on oSPARC</el-button>
26
26
  </div>
27
27
  <div class="secondary-button">
28
- <el-button size="small" @click="viewDataset()">View Dataset</el-button>
28
+ <el-button size="small" @click="viewDataset()" v-if="libopencor === undefined">View Dataset</el-button>
29
+ </div>
30
+ <div class="secondary-button">
31
+ <el-button size="small" @click="viewWorkspace()" v-if="libopencor !== undefined">View Workspace</el-button>
29
32
  </div>
30
33
  <p class="default note" v-if="uuid">Additional parameters are available on oSPARC</p>
31
34
  </div>
@@ -33,7 +36,7 @@
33
36
  <PlotVuer v-for="(outputPlot, index) in simulationUiInfo.output.plots"
34
37
  :key="`output-${index}`"
35
38
  :metadata="plotMetadata(index)"
36
- :data-source="{data: simulationData[index]}"
39
+ :data-source="{data: simulationResults[index]}"
37
40
  :plotLayout="layout[index]"
38
41
  :plotType="'plotly-only'"
39
42
  :selectorUi="false"
@@ -51,14 +54,24 @@ import { PlotVuer } from "@abi-software/plotvuer";
51
54
  import "@abi-software/plotvuer/dist/style.css";
52
55
  import SimulationVuerInput from "./SimulationVuerInput.vue";
53
56
  import { ElButton, ElDivider, ElLoading } from "element-plus";
54
- import { evaluateValue, evaluateSimulationValue, OPENCOR_SOLVER_NAME } from "./common.js";
57
+ import { evaluateValue, finaliseUi, OPENCOR_SOLVER_NAME } from "./common.js";
55
58
  import { validJson } from "./json.js";
56
- import { initialiseUi, finaliseUi } from "./ui.js";
59
+ import libOpenCOR from "./libopencor.js";
60
+ import { toRaw } from "vue";
61
+ import { create, all } from "mathjs";
62
+
63
+ const LIBOPENCOR_SOLVER = "libOpenCOR";
64
+ const OSPARC_SOLVER = "oSPARC";
65
+ const PMR_URL = "https://models.physiomeproject.org/";
66
+
67
+ const math = create(all, {});
57
68
 
58
69
  /**
59
70
  * SimulationVuer
60
71
  */
61
72
  export default {
73
+ LIBOPENCOR_SOLVER: LIBOPENCOR_SOLVER,
74
+ OSPARC_SOLVER: OSPARC_SOLVER,
62
75
  name: "SimulationVuer",
63
76
  components: {
64
77
  PlotVuer,
@@ -76,50 +89,57 @@ export default {
76
89
  type: String,
77
90
  },
78
91
  /**
79
- * The ID of the simulation-based dataset.
92
+ * The ID of the simulation-based dataset, if it is a number, or the path
93
+ * to a PMR file, if it is a string.
80
94
  */
81
95
  id: {
82
96
  required: true,
83
- type: Number,
84
97
  },
85
98
  },
86
99
  data: function() {
87
- let xmlhttp = new XMLHttpRequest();
88
- let name = undefined;
89
- let uuid = undefined;
90
-
91
- xmlhttp.open("GET", this.apiLocation + "/sim/dataset/" + this.id, false);
92
- xmlhttp.setRequestHeader("Content-type", "application/json");
93
- xmlhttp.onreadystatechange = () => {
94
- if (xmlhttp.readyState === 4) {
95
- if (xmlhttp.status === 200) {
96
- let datasetInfo = JSON.parse(xmlhttp.responseText);
97
-
98
- name = datasetInfo.name;
99
- uuid = (datasetInfo.study !== undefined)?datasetInfo.study.uuid:undefined;
100
+ // Retrieve some information about the dataset.
101
+
102
+ if (this.id > 0) {
103
+ const xmlhttp = new XMLHttpRequest();
104
+
105
+ xmlhttp.open("GET", this.apiLocation + "/sim/dataset/" + this.id);
106
+ xmlhttp.onreadystatechange = () => {
107
+ if (xmlhttp.readyState === 4) {
108
+ if (xmlhttp.status === 200) {
109
+ const datasetInfo = JSON.parse(xmlhttp.responseText);
110
+
111
+ this.name = datasetInfo.name;
112
+ this.uuid = (datasetInfo.study !== undefined)?datasetInfo.study.uuid:undefined;
113
+ }
100
114
  }
101
- }
102
- };
103
- xmlhttp.send();
115
+ };
116
+ xmlhttp.send();
117
+ }
104
118
 
105
119
  return {
106
120
  errorMessage: "",
121
+ fileManager: undefined,
107
122
  hasFinalisedUi: false,
108
123
  hasValidSimulationUiInfo: false,
124
+ instance: undefined,
109
125
  isMounted: false,
110
126
  isSimulationValid: true,
111
127
  layout: [],
112
- name: name,
128
+ libopencor: undefined,
129
+ name: null,
130
+ opencorBasedSimulation: true,
131
+ output: undefined,
113
132
  perfectScollbarOptions: {
114
133
  suppressScrollX: true,
115
134
  },
116
135
  showUserMessage: false,
117
- simulationData: [],
118
- simulationDataId: {},
136
+ simulationResults: {},
137
+ simulationResultsId: {},
119
138
  simulationUiInfo: {},
139
+ solver: undefined,
120
140
  userMessage: "",
121
141
  ui: null,
122
- uuid: uuid,
142
+ uuid: null,
123
143
  };
124
144
  },
125
145
  methods: {
@@ -138,6 +158,115 @@ export default {
138
158
  },
139
159
  };
140
160
  },
161
+ /**
162
+ * @vuese
163
+ * Download the PMR file associated with the given `url`.
164
+ * @arg `url`
165
+ */
166
+ downloadPmrFile(url) {
167
+ return new Promise((resolve, reject) => {
168
+ const xmlhttp = new XMLHttpRequest();
169
+
170
+ xmlhttp.open("POST", this.apiLocation + "/pmr_file");
171
+ xmlhttp.setRequestHeader("Content-type", "application/json");
172
+ xmlhttp.onreadystatechange = () => {
173
+ if (xmlhttp.readyState === 4) {
174
+ if (xmlhttp.status === 200) {
175
+ resolve(Uint8Array.from(atob(xmlhttp.response), (c) => c.charCodeAt(0)));
176
+ }
177
+
178
+ reject();
179
+ }
180
+ };
181
+ xmlhttp.send(JSON.stringify({path: url.replace(PMR_URL, "")}));
182
+ });
183
+ },
184
+ /**
185
+ * @vuese
186
+ * Manage the file associated with the given `url` and `fileContents`.
187
+ * @arg `url`
188
+ * @arg `fileContents`
189
+ */
190
+ manageFile(url, fileContents) {
191
+ let file = toRaw(this.fileManager).file(url);
192
+
193
+ if (file === null) {
194
+ file = new this.libopencor.File(url);
195
+ }
196
+
197
+ const fileContentsPtr = this.libopencor._malloc(fileContents.length);
198
+ const mem = new Uint8Array(this.libopencor.HEAPU8.buffer, fileContentsPtr, fileContents.length);
199
+
200
+ mem.set(fileContents);
201
+
202
+ file.setContents(fileContentsPtr, fileContents.length);
203
+
204
+ this.libopencor._free(fileContentsPtr);
205
+
206
+ return file;
207
+ },
208
+ /**
209
+ * @vuese
210
+ * Run a PMR-based COMBINE archive using libOpenCOR.
211
+ */
212
+ runSimulation() {
213
+ if (this.instance === undefined) {
214
+ // Retrieve an instance of the model.
215
+
216
+ const document = new this.libopencor.SedDocument(toRaw(this.fileManager).file(PMR_URL + this.id));
217
+
218
+ this.instance = document.instantiate();
219
+
220
+ document.delete();
221
+ }
222
+
223
+ // Run the simulation after passing some initial conditions to it, if any.
224
+
225
+ const instance = toRaw(this.instance);
226
+
227
+ instance.removeAllInitialConditions();
228
+
229
+ for (const [parameter, value] of Object.entries(this.parametersData())) {
230
+ instance.addInitialCondition(parameter, value);
231
+ }
232
+
233
+ instance.run();
234
+
235
+ // Retrieve the simulation results.
236
+
237
+ const res = {};
238
+ const instanceTask = instance.tasks().get(0);
239
+
240
+ for (const output of this.outputData()) {
241
+ if (output === instanceTask.voiName()) {
242
+ res[output] = instanceTask.voiAsArray();
243
+ }
244
+
245
+ if (res[output] === undefined) {
246
+ for (let i = 0; i < instanceTask.stateCount(); ++i) {
247
+ if (output === instanceTask.stateName(i)) {
248
+ res[output] = instanceTask.stateAsArray(i);
249
+
250
+ break;
251
+ }
252
+ }
253
+ }
254
+
255
+ if (res[output] === undefined) {
256
+ for (let i = 0; instanceTask.variableCount(); ++i) {
257
+ if (output === instanceTask.variableName(i)) {
258
+ res[output] = instanceTask.variableAsArray(i);
259
+
260
+ break;
261
+ }
262
+ }
263
+ }
264
+ }
265
+
266
+ this.processSimulationResults(res);
267
+
268
+ this.showUserMessage = false;
269
+ },
141
270
  /**
142
271
  * @vuese
143
272
  * Build the simulation UI using `simulationUiInfo`, a JSON object that describes the contents of the simulation UI.
@@ -150,15 +279,128 @@ export default {
150
279
 
151
280
  // Make sure that the simulation UI information is valid.
152
281
 
153
- this.hasValidSimulationUiInfo = validJson(this.simulationUiInfo);
282
+ this.hasValidSimulationUiInfo = validJson(this.simulationUiInfo, this.libopencor === undefined);
154
283
 
155
284
  if (!this.hasValidSimulationUiInfo) {
156
285
  return;
157
286
  }
158
287
 
288
+ // Retrieve and keep track of the solver to be used for the simulation, if
289
+ // needed.
290
+
291
+ if (this.libopencor === undefined) {
292
+ this.simulationUiInfo.simulation.solvers.forEach((solver) => {
293
+ if ((solver.if === undefined) || evaluateValue(this, solver.if)) {
294
+ this.solver = solver;
295
+ }
296
+ });
297
+
298
+ if (this.solver === undefined) {
299
+ console.warn("SIMULATION: no solver name and/or solver version specified.");
300
+
301
+ return;
302
+ }
303
+
304
+ this.opencorBasedSimulation = this.solver.name === OPENCOR_SOLVER_NAME;
305
+ }
306
+
307
+ // Run the model if we are dealing with a PMR-based COMBINE archive or
308
+ // load libOpenCOR if we are dealing with an OpenCOR-based simulation and
309
+ // we want to use libOpenCOR.
310
+
311
+ if (this.libopencor !== undefined) {
312
+ this.userMessage = "Running the model...";
313
+ this.showUserMessage = true;
314
+
315
+ this.$nextTick(() => {
316
+ this.runSimulation();
317
+ });
318
+ } else if (this.opencorBasedSimulation && (this.libopencor !== undefined)) {
319
+ this.userMessage = "Retrieving and running the model...";
320
+ this.showUserMessage = true;
321
+
322
+ this.$nextTick(() => {
323
+ libOpenCOR().then((libopencor) => {
324
+ // Keep track of the libOpenCOR module and its file manager.
325
+
326
+ this.libopencor = libopencor;
327
+ this.fileManager = this.libopencor.FileManager.instance();
328
+
329
+ // Retrieve the model file, if needed.
330
+
331
+ const modelUrl = this.simulationUiInfo.simulation.opencor.resource;
332
+
333
+ this.downloadPmrFile(modelUrl).then((fileContents) => {
334
+ const file = this.manageFile(modelUrl, fileContents);
335
+
336
+ // In the case of a SED-ML file, we also need to retrieve its
337
+ // corresponding CellML file.
338
+
339
+ if (file.type().value === this.libopencor.File.Type.SEDML_FILE.value) {
340
+ const document = new this.libopencor.SedDocument(file);
341
+ const cellmlUrl = document.models().get(0).file().url();
342
+
343
+ this.downloadPmrFile(cellmlUrl).then((cellmlFileContents) => {
344
+ this.manageFile(cellmlUrl, cellmlFileContents);
345
+
346
+ this.runSimulation();
347
+ });
348
+
349
+ document.delete();
350
+ } else {
351
+ this.runSimulation();
352
+ }
353
+ });
354
+ });
355
+ });
356
+ }
357
+
159
358
  // Initialise our UI.
160
359
 
161
- initialiseUi(this);
360
+ this.simulationUiInfo.output.data.forEach((data) => {
361
+ this.simulationResultsId[data.id] = data.name;
362
+ });
363
+
364
+ let index = -1;
365
+
366
+ this.simulationUiInfo.output.plots.forEach((outputPlot) => {
367
+ ++index;
368
+
369
+ this.layout[index] = {
370
+ paper_bgcolor: "rgba(0, 0, 0, 0)",
371
+ plot_bgcolor: "rgba(0, 0, 0, 0)",
372
+ autosize: true,
373
+ margin: {
374
+ t: 25,
375
+ l: 55,
376
+ r: 25,
377
+ b: 30,
378
+ pad: 4,
379
+ },
380
+ loading: false,
381
+ options: {
382
+ responsive: true,
383
+ scrollZoom: true,
384
+ },
385
+ dragmode: "pan",
386
+ xaxis: {
387
+ title: {
388
+ text: outputPlot.xAxisTitle,
389
+ font: {
390
+ size: 10,
391
+ },
392
+ },
393
+ },
394
+ yaxis: {
395
+ title: {
396
+ text: outputPlot.yAxisTitle,
397
+ font: {
398
+ size: 10,
399
+ },
400
+ },
401
+ },
402
+ };
403
+ });
162
404
 
163
405
  // Finalise our UI.
164
406
  // Note: we try both here and in the mounted() function since we have no
@@ -167,14 +409,6 @@ export default {
167
409
 
168
410
  this.$nextTick(() => {
169
411
  finaliseUi(this);
170
-
171
- this.simulationData.forEach((data, index) => {
172
- this.simulationData[index] = [{
173
- x: [],
174
- y: [],
175
- type: "scatter",
176
- }];
177
- });
178
412
  });
179
413
  },
180
414
  /**
@@ -196,62 +430,78 @@ export default {
196
430
  },
197
431
  /**
198
432
  * @vuese
199
- * Finish creating the `request` that is going to be used by `startSimulation` to ask oSPARC to start the
200
- * simulation. `request` is a JSON object that initially contains the solver to be used by oSPARC and to which
201
- * additional is added.
202
- * @arg `request`
433
+ * View the simulation-based dataset on PMR. The simulation UI has a `View Workspace` button which, when clicked,
434
+ * calls this method.
203
435
  */
204
- retrieveRequest(request) {
205
- // Settings specific to OpenCOR/oSPARC.
436
+ viewWorkspace() {
437
+ const url = PMR_URL + this.id;
206
438
 
207
- let isOpencorSimulation = request.solver.name === OPENCOR_SOLVER_NAME;
439
+ window.open(url.substring(0, url.lastIndexOf("/")), "_blank");
440
+ },
441
+ /**
442
+ * @vuese
443
+ * Data needed to set a model's parameters.
444
+ */
445
+ parametersData() {
446
+ const res = {};
208
447
 
209
- if (isOpencorSimulation) {
210
- request.opencor = {
211
- model_url: this.simulationUiInfo.simulation.opencor.resource,
212
- json_config: {},
213
- };
214
- } else {
215
- request.osparc = {};
216
- }
448
+ this.simulationUiInfo.parameters.forEach((parameter) => {
449
+ res[parameter.name] = evaluateValue(this, parameter.value);
450
+ });
217
451
 
218
- // Specify the ending point and point interval, if we have some.
452
+ return res;
453
+ },
454
+ /**
455
+ * @vuese
456
+ * Data needed to specify the model output.
457
+ */
458
+ outputData() {
459
+ if (this.output === undefined) {
460
+ if (this.simulationUiInfo.output.data !== undefined) {
461
+ this.output = [];
219
462
 
220
- if ( isOpencorSimulation
221
- && (this.simulationUiInfo.simulation.opencor.endingPoint !== undefined)
222
- && (this.simulationUiInfo.simulation.opencor.pointInterval !== undefined)) {
223
- request.opencor.json_config.simulation = {
224
- "Ending point": this.simulationUiInfo.simulation.opencor.endingPoint,
225
- "Point interval": this.simulationUiInfo.simulation.opencor.pointInterval,
226
- };
463
+ this.simulationUiInfo.output.data.forEach((output) => {
464
+ this.output.push(output.name);
465
+ });
466
+ }
227
467
  }
228
468
 
229
- // Specify the parameters, if any.
230
-
231
- if (this.simulationUiInfo.parameters !== undefined) {
232
- let parameters = {};
469
+ return this.output;
470
+ },
471
+ /**
472
+ * @vuese
473
+ * Create the `request` that is going to be used by `startSimulation` to ask oSPARC to start the simulation.
474
+ */
475
+ retrieveRequest() {
476
+ const request = {
477
+ solver: this.solver
478
+ };
233
479
 
234
- this.simulationUiInfo.parameters.forEach((parameter) => {
235
- parameters[parameter.name] = evaluateValue(this, parameter.value);
236
- });
480
+ if (this.opencorBasedSimulation) {
481
+ request.opencor = {
482
+ model_url: this.simulationUiInfo.simulation.opencor.resource,
483
+ json_config: {},
484
+ };
237
485
 
238
- if (isOpencorSimulation) {
239
- request.opencor.json_config.parameters = parameters;
240
- } else {
241
- request.osparc.job_inputs = parameters;
486
+ if ( (this.simulationUiInfo.simulation.opencor.endingPoint !== undefined)
487
+ && (this.simulationUiInfo.simulation.opencor.pointInterval !== undefined)) {
488
+ request.opencor.json_config.simulation = {
489
+ "Ending point": this.simulationUiInfo.simulation.opencor.endingPoint,
490
+ "Point interval": this.simulationUiInfo.simulation.opencor.pointInterval,
491
+ };
242
492
  }
243
- }
244
493
 
245
- // Specify what we want to retrieve, if anything.
494
+ request.opencor.json_config.parameters = this.parametersData();
246
495
 
247
- if (isOpencorSimulation && (this.simulationUiInfo.output.data !== undefined)) {
248
- let index = -1;
496
+ const output = this.outputData();
249
497
 
250
- request.opencor.json_config.output = [];
498
+ if (output !== undefined) {
499
+ request.opencor.json_config.output = output;
500
+ }
501
+ } else {
502
+ request.osparc = {};
251
503
 
252
- this.simulationUiInfo.output.data.forEach((outputData) => {
253
- request.opencor.json_config.output[++index] = outputData.name;
254
- });
504
+ request.osparc.job_inputs = this.parametersData();
255
505
  }
256
506
 
257
507
  return request;
@@ -268,9 +518,8 @@ export default {
268
518
 
269
519
  if (typeof(results) === "string") {
270
520
  const SPACES = /[ \t]+/g;
271
-
272
- let lines = results.trim().split("\n");
273
- let iMax = lines[0].trim().split(SPACES).length;
521
+ const lines = results.trim().split("\n");
522
+ const iMax = lines[0].trim().split(SPACES).length;
274
523
 
275
524
  results = {};
276
525
 
@@ -284,7 +533,7 @@ export default {
284
533
  ++i;
285
534
 
286
535
  let j = -1;
287
- let values = line.trim().split(SPACES);
536
+ const values = line.trim().split(SPACES);
288
537
 
289
538
  values.forEach((value) => {
290
539
  results[++j][i] = Number(value);
@@ -294,27 +543,34 @@ export default {
294
543
 
295
544
  // Get the results ready for plotting.
296
545
 
297
- let index = -1;
298
- let iMax = results[this.simulationDataId[Object.keys(this.simulationDataId)[0]]].length;
546
+ const parser = new math.parser();
299
547
 
300
- this.simulationUiInfo.output.plots.forEach((outputPlot) => {
301
- let xValue = [];
302
- let yValue = [];
548
+ Object.keys(this.simulationResultsId).forEach((id) => {
549
+ parser.set(id, results[this.simulationResultsId[id]]);
550
+ });
303
551
 
304
- for (let i = 0; i < iMax; ++i) {
305
- xValue[i] = evaluateSimulationValue(this, results, outputPlot.xValue, i);
306
- yValue[i] = evaluateSimulationValue(this, results, outputPlot.yValue, i);
307
- }
552
+ let index = -1;
308
553
 
309
- this.simulationData[++index] = [
554
+ this.simulationUiInfo.output.plots.forEach((outputPlot) => {
555
+ this.simulationResults[++index] = [
310
556
  {
311
- x: xValue,
312
- y: yValue,
557
+ x: parser.evaluate(outputPlot.xValue),
558
+ y: parser.evaluate(outputPlot.yValue),
313
559
  type: "scatter",
314
560
  },
315
561
  ];
316
562
  });
317
563
  },
564
+ /**
565
+ * @vuese
566
+ * Show an HTTP issue using the given `xmlhttp`.
567
+ * @arg `xmlhttp`
568
+ */
569
+ showHttpIssue(xmlhttp) {
570
+ this.isSimulationValid = false;
571
+ this.showUserMessage = false;
572
+ this.errorMessage = xmlhttp.statusText.toLowerCase() + " (<a href='https://httpstatuses.com/" + xmlhttp.status + "/' target='_blank'>" + xmlhttp.status + "</a>)";
573
+ },
318
574
  /**
319
575
  * @vuese
320
576
  * Check the progress of the simulation using the given `data`, a JSON object that contains the simulation job ID,
@@ -325,9 +581,9 @@ export default {
325
581
  checkSimulation(data) {
326
582
  // Check the simulation.
327
583
 
328
- let xmlhttp = new XMLHttpRequest();
584
+ const xmlhttp = new XMLHttpRequest();
329
585
 
330
- xmlhttp.open("POST", this.apiLocation + "/check_simulation", true);
586
+ xmlhttp.open("POST", this.apiLocation + "/check_simulation");
331
587
  xmlhttp.setRequestHeader("Content-type", "application/json");
332
588
  xmlhttp.onreadystatechange = () => {
333
589
  if (xmlhttp.readyState === 4) {
@@ -355,12 +611,10 @@ export default {
355
611
  }
356
612
  } else {
357
613
  this.showUserMessage = false;
358
- this.errorMessage = response.description;
614
+ this.errorMessage = response.description + "QWEQWEQWE";
359
615
  }
360
616
  } else {
361
- this.isSimulationValid = false;
362
- this.showUserMessage = false;
363
- this.errorMessage = xmlhttp.statusText.toLowerCase() + " (<a href='https://httpstatuses.com/" + xmlhttp.status + "/' target='_blank'>" + xmlhttp.status + "</a>)";
617
+ this.showHttpIssue(xmlhttp);
364
618
  }
365
619
  }
366
620
  };
@@ -372,36 +626,18 @@ export default {
372
626
  * button which, when clicked, calls this method.
373
627
  */
374
628
  startSimulation() {
375
- // Retrieve the solver to be used for the simulation.
376
-
377
- let solver = undefined;
378
-
379
- this.simulationUiInfo.simulation.solvers.forEach((crtSolver) => {
380
- if ((crtSolver.if === undefined) || evaluateValue(this, crtSolver.if)) {
381
- solver = crtSolver;
382
- }
383
- });
384
-
385
- if (solver === undefined) {
386
- console.warn("SIMULATION: no solver name and/or solver version specified.");
387
-
388
- return;
389
- }
390
-
391
629
  // Start the simulation (after resetting our previous simulation data, in
392
630
  // case there were sonme).
393
- // Note: we use this.$nextTick() so that the user message is shown before
394
- // we get to post our HTTP request.
395
631
 
396
632
  this.userMessage = "Loading simulation results...";
397
633
  this.showUserMessage = true;
398
634
 
399
635
  this.$nextTick(() => {
400
- this.simulationData = [];
636
+ this.simulationResults = {};
401
637
 
402
- let xmlhttp = new XMLHttpRequest();
638
+ const xmlhttp = new XMLHttpRequest();
403
639
 
404
- xmlhttp.open("POST", this.apiLocation + "/start_simulation", true);
640
+ xmlhttp.open("POST", this.apiLocation + "/start_simulation");
405
641
  xmlhttp.setRequestHeader("Content-type", "application/json");
406
642
  xmlhttp.onreadystatechange = () => {
407
643
  if (xmlhttp.readyState === 4) {
@@ -414,48 +650,81 @@ export default {
414
650
  this.checkSimulation(response.data);
415
651
  } else {
416
652
  this.showUserMessage = false;
417
- this.errorMessage = response.description;
653
+ this.errorMessage = response.description + "ASDASDASD";
418
654
  }
419
655
  } else {
420
- this.isSimulationValid = false;
421
- this.showUserMessage = false;
422
- this.errorMessage = xmlhttp.statusText.toLowerCase() + " (<a href='https://httpstatuses.com/" + xmlhttp.status + "/' target='_blank'>" + xmlhttp.status + "</a>)";
656
+ this.showHttpIssue(xmlhttp);
423
657
  }
424
658
  }
425
659
  };
426
- xmlhttp.send(JSON.stringify(this.retrieveRequest({
427
- solver: solver
428
- })));
660
+ xmlhttp.send(JSON.stringify(this.retrieveRequest()));
429
661
  });
430
662
  },
431
663
  },
432
664
  created: function() {
433
- // Try to retrieve the UI information, but only if we have a name.
665
+ // Try to retrieve the UI information.
434
666
 
435
- if (this.name !== undefined) {
667
+ if (this.id > 0) {
436
668
  this.userMessage = "Retrieving UI information...";
437
669
  this.showUserMessage = true;
438
670
 
439
671
  // Retrieve and build the simulation UI.
440
- // Note: we use this.$nextTick() so that the user message is shown before
441
- // we get to post our HTTP request.
442
672
 
443
673
  this.$nextTick(() => {
444
- let xmlhttp = new XMLHttpRequest();
674
+ const xmlhttp = new XMLHttpRequest();
445
675
 
446
- xmlhttp.open("GET", this.apiLocation + "/simulation_ui_file/" + this.id, true);
447
- xmlhttp.setRequestHeader("Content-type", "application/json");
676
+ xmlhttp.open("GET", this.apiLocation + "/simulation_ui_file/" + this.id);
448
677
  xmlhttp.onreadystatechange = () => {
449
678
  if (xmlhttp.readyState === 4) {
450
679
  this.showUserMessage = false;
451
680
 
452
681
  if (xmlhttp.status === 200) {
453
- this.buildSimulationUi(JSON.parse(xmlhttp.responseText));
682
+ this.$nextTick(() => {
683
+ this.buildSimulationUi(JSON.parse(xmlhttp.responseText));
684
+ });
454
685
  }
455
686
  }
456
687
  };
457
688
  xmlhttp.send();
458
689
  });
690
+ } else if (this.id !== 0) {
691
+ this.userMessage = "Retrieving COMBINE archive from PMR...";
692
+ this.showUserMessage = true;
693
+
694
+ // Retrieve the OMEX file, extract the simulation UI JSON file from it and
695
+ // then build the simulation UI.
696
+
697
+ this.$nextTick(() => {
698
+ const xmlhttp = new XMLHttpRequest();
699
+
700
+ xmlhttp.open("POST", this.apiLocation + "/pmr_file");
701
+ xmlhttp.setRequestHeader("Content-type", "application/json");
702
+ xmlhttp.onreadystatechange = () => {
703
+ if (xmlhttp.readyState === 4) {
704
+ if (xmlhttp.status === 200) {
705
+ libOpenCOR().then((libopencor) => {
706
+ this.libopencor = libopencor;
707
+ this.fileManager = this.libopencor.FileManager.instance();
708
+
709
+ const fileContents = Uint8Array.from(atob(xmlhttp.response), (c) => c.charCodeAt(0));
710
+ const file = this.manageFile(PMR_URL + this.id, fileContents);
711
+
712
+ const decoder = new TextDecoder();
713
+ const simulationUiInfo = JSON.parse(decoder.decode(file.childFile("simulation.json").contents()));
714
+
715
+ this.showUserMessage = false;
716
+
717
+ this.$nextTick(() => {
718
+ this.buildSimulationUi(simulationUiInfo);
719
+ });
720
+ });
721
+ } else {
722
+ this.showUserMessage = false;
723
+ }
724
+ }
725
+ };
726
+ xmlhttp.send(JSON.stringify({path: this.id}));
727
+ });
459
728
  }
460
729
  },
461
730
  mounted: function() {