@abi-software/simulationvuer 1.0.0 → 2.0.0

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.
@@ -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="!this.libopencor">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="typeof this.id === 'number'">View Dataset</el-button>
29
+ </div>
30
+ <div class="secondary-button">
31
+ <el-button size="small" @click="viewWorkspace()" v-if="typeof this.id !== 'number'">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,
@@ -80,46 +93,62 @@ export default {
80
93
  */
81
94
  id: {
82
95
  required: true,
83
- type: Number,
96
+ },
97
+ /**
98
+ * The preferred solver to use for OpenCOR-based simulations. This property
99
+ * is optional and defaults to "oSPARC". The only value that is currently
100
+ * supported is "libOpenCOR". Any other value will default to "oSPARC".
101
+ */
102
+ preferredSolver: {
103
+ type: String,
104
+ default: OSPARC_SOLVER,
84
105
  },
85
106
  },
86
107
  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;
108
+ // Retrieve some information about the dataset.
109
+
110
+ if (this.id > 0) {
111
+ const xmlhttp = new XMLHttpRequest();
112
+
113
+ xmlhttp.open("GET", this.apiLocation + "/sim/dataset/" + this.id);
114
+ xmlhttp.onreadystatechange = () => {
115
+ if (xmlhttp.readyState === 4) {
116
+ if (xmlhttp.status === 200) {
117
+ const datasetInfo = JSON.parse(xmlhttp.responseText);
118
+
119
+ this.name = datasetInfo.name;
120
+ this.uuid = (datasetInfo.study !== undefined)?datasetInfo.study.uuid:undefined;
121
+ }
100
122
  }
101
- }
102
- };
103
- xmlhttp.send();
123
+ };
124
+ xmlhttp.send();
125
+ }
104
126
 
105
127
  return {
106
128
  errorMessage: "",
129
+ fileManager: undefined,
107
130
  hasFinalisedUi: false,
108
131
  hasValidSimulationUiInfo: false,
132
+ instance: undefined,
109
133
  isMounted: false,
110
134
  isSimulationValid: true,
111
135
  layout: [],
112
- name: name,
136
+ libopencor: undefined,
137
+ name: null,
138
+ opencorBasedSimulation: true,
139
+ output: undefined,
113
140
  perfectScollbarOptions: {
114
141
  suppressScrollX: true,
115
142
  },
143
+ pmrBasedCombineArchive: false,
116
144
  showUserMessage: false,
117
- simulationData: [],
118
- simulationDataId: {},
145
+ simulationResults: {},
146
+ simulationResultsId: {},
119
147
  simulationUiInfo: {},
148
+ solver: undefined,
120
149
  userMessage: "",
121
150
  ui: null,
122
- uuid: uuid,
151
+ uuid: null,
123
152
  };
124
153
  },
125
154
  methods: {
@@ -138,6 +167,128 @@ export default {
138
167
  },
139
168
  };
140
169
  },
170
+ /**
171
+ * @vuese
172
+ * Download the PMR file associated with the given `url`.
173
+ * @arg `url`
174
+ */
175
+ downloadPmrFile(url) {
176
+ return new Promise((resolve, reject) => {
177
+ const xmlhttp = new XMLHttpRequest();
178
+
179
+ xmlhttp.open("POST", this.apiLocation + "/pmr_file");
180
+ xmlhttp.setRequestHeader("Content-type", "application/json");
181
+ xmlhttp.onreadystatechange = () => {
182
+ if (xmlhttp.readyState === 4) {
183
+ if (xmlhttp.status === 200) {
184
+ resolve(Uint8Array.from(atob(xmlhttp.response), (c) => c.charCodeAt(0)));
185
+ }
186
+
187
+ reject();
188
+ }
189
+ };
190
+ xmlhttp.send(JSON.stringify({path: url.replace(PMR_URL, "")}));
191
+ });
192
+ },
193
+ /**
194
+ * @vuese
195
+ * Manage the file associated with the given `url` and `fileContents`.
196
+ * @arg `url`
197
+ * @arg `fileContents`
198
+ */
199
+ manageFile(url, fileContents) {
200
+ let file = toRaw(this.fileManager).file(url);
201
+
202
+ if (file === null) {
203
+ file = new this.libopencor.File(url);
204
+ }
205
+
206
+ const fileContentsPtr = this.libopencor._malloc(fileContents.length);
207
+ const mem = new Uint8Array(this.libopencor.HEAPU8.buffer, fileContentsPtr, fileContents.length);
208
+
209
+ mem.set(fileContents);
210
+
211
+ file.setContents(fileContentsPtr, fileContents.length);
212
+
213
+ this.libopencor._free(fileContentsPtr);
214
+
215
+ return file;
216
+ },
217
+ /**
218
+ * @vuese
219
+ * Run the simulation using libOpenCOR.
220
+ */
221
+ runSimulation() {
222
+ if (this.instance === undefined) {
223
+ const fileNameOrUrl = this.pmrBasedCombineArchive ? PMR_URL + this.id : this.simulationUiInfo.simulation.opencor.resource;
224
+ const document = new this.libopencor.SedDocument(toRaw(this.fileManager).file(fileNameOrUrl));
225
+
226
+ // Customise the ending point and point interval of the simulation, if
227
+ // needed.
228
+
229
+ if ( !this.pmrBasedCombineArchive
230
+ && (this.simulationUiInfo.simulation.opencor.endingPoint !== undefined)
231
+ && (this.simulationUiInfo.simulation.opencor.pointInterval !== undefined)) {
232
+ const simulation = document.simulations().get(0);
233
+
234
+ simulation.setOutputEndTime(this.simulationUiInfo.simulation.opencor.endingPoint);
235
+ simulation.setNumberOfSteps(this.simulationUiInfo.simulation.opencor.endingPoint / this.simulationUiInfo.simulation.opencor.pointInterval);
236
+ }
237
+
238
+ // Retrieve an instance of the model.
239
+
240
+ this.instance = document.instantiate();
241
+
242
+ document.delete();
243
+ }
244
+
245
+ // Run the simulation after passing some initial conditions to it, if any.
246
+
247
+ const instance = toRaw(this.instance);
248
+
249
+ instance.removeAllInitialConditions();
250
+
251
+ for (const [parameter, value] of Object.entries(this.parametersData())) {
252
+ instance.addInitialCondition(parameter, value);
253
+ }
254
+
255
+ instance.run();
256
+
257
+ // Retrieve the simulation results.
258
+
259
+ const res = {};
260
+ const instanceTask = instance.tasks().get(0);
261
+
262
+ for (const output of this.outputData()) {
263
+ if (output === instanceTask.voiName()) {
264
+ res[output] = instanceTask.voiAsArray();
265
+ }
266
+
267
+ if (res[output] === undefined) {
268
+ for (let i = 0; i < instanceTask.stateCount(); ++i) {
269
+ if (output === instanceTask.stateName(i)) {
270
+ res[output] = instanceTask.stateAsArray(i);
271
+
272
+ break;
273
+ }
274
+ }
275
+ }
276
+
277
+ if (res[output] === undefined) {
278
+ for (let i = 0; instanceTask.variableCount(); ++i) {
279
+ if (output === instanceTask.variableName(i)) {
280
+ res[output] = instanceTask.variableAsArray(i);
281
+
282
+ break;
283
+ }
284
+ }
285
+ }
286
+ }
287
+
288
+ this.processSimulationResults(res);
289
+
290
+ this.showUserMessage = false;
291
+ },
141
292
  /**
142
293
  * @vuese
143
294
  * Build the simulation UI using `simulationUiInfo`, a JSON object that describes the contents of the simulation UI.
@@ -150,15 +301,128 @@ export default {
150
301
 
151
302
  // Make sure that the simulation UI information is valid.
152
303
 
153
- this.hasValidSimulationUiInfo = validJson(this.simulationUiInfo);
304
+ this.hasValidSimulationUiInfo = validJson(this.simulationUiInfo, !this.pmrBasedCombineArchive);
154
305
 
155
306
  if (!this.hasValidSimulationUiInfo) {
156
307
  return;
157
308
  }
158
309
 
310
+ // Retrieve and keep track of the solver to be used for the simulation, if
311
+ // needed.
312
+
313
+ if (!this.pmrBasedCombineArchive) {
314
+ this.simulationUiInfo.simulation.solvers.forEach((solver) => {
315
+ if ((solver.if === undefined) || evaluateValue(this, solver.if)) {
316
+ this.solver = solver;
317
+ }
318
+ });
319
+
320
+ if (this.solver === undefined) {
321
+ console.warn("SIMULATION: no solver name and/or solver version specified.");
322
+
323
+ return;
324
+ }
325
+
326
+ this.opencorBasedSimulation = this.solver.name === OPENCOR_SOLVER_NAME;
327
+ }
328
+
329
+ // Run the model if we are dealing with a PMR-based COMBINE archive or
330
+ // load libOpenCOR if we are dealing with an OpenCOR-based simulation and
331
+ // we want to use libOpenCOR.
332
+
333
+ if (this.pmrBasedCombineArchive) {
334
+ this.userMessage = "Running the model...";
335
+ this.showUserMessage = true;
336
+
337
+ this.$nextTick(() => {
338
+ this.runSimulation();
339
+ });
340
+ } else if (this.opencorBasedSimulation && (this.preferredSolver === LIBOPENCOR_SOLVER)) {
341
+ this.userMessage = "Retrieving and running the model...";
342
+ this.showUserMessage = true;
343
+
344
+ this.$nextTick(() => {
345
+ libOpenCOR().then((libopencor) => {
346
+ // Keep track of the libOpenCOR module and its file manager.
347
+
348
+ this.libopencor = libopencor;
349
+ this.fileManager = this.libopencor.FileManager.instance();
350
+
351
+ // Retrieve the model file, if needed.
352
+
353
+ const modelUrl = this.simulationUiInfo.simulation.opencor.resource;
354
+
355
+ this.downloadPmrFile(modelUrl).then((fileContents) => {
356
+ const file = this.manageFile(modelUrl, fileContents);
357
+
358
+ // In the case of a SED-ML file, we also need to retrieve its
359
+ // corresponding CellML file.
360
+
361
+ if (file.type().value === this.libopencor.File.Type.SEDML_FILE.value) {
362
+ const document = new this.libopencor.SedDocument(file);
363
+ const cellmlUrl = document.models().get(0).file().url();
364
+
365
+ this.downloadPmrFile(cellmlUrl).then((cellmlFileContents) => {
366
+ this.manageFile(cellmlUrl, cellmlFileContents);
367
+
368
+ this.runSimulation();
369
+ });
370
+
371
+ document.delete();
372
+ } else {
373
+ this.runSimulation();
374
+ }
375
+ });
376
+ });
377
+ });
378
+ }
379
+
159
380
  // Initialise our UI.
160
381
 
161
- initialiseUi(this);
382
+ this.simulationUiInfo.output.data.forEach((data) => {
383
+ this.simulationResultsId[data.id] = data.name;
384
+ });
385
+
386
+ let index = -1;
387
+
388
+ this.simulationUiInfo.output.plots.forEach((outputPlot) => {
389
+ ++index;
390
+
391
+ this.layout[index] = {
392
+ paper_bgcolor: "rgba(0, 0, 0, 0)",
393
+ plot_bgcolor: "rgba(0, 0, 0, 0)",
394
+ autosize: true,
395
+ margin: {
396
+ t: 25,
397
+ l: 55,
398
+ r: 25,
399
+ b: 30,
400
+ pad: 4,
401
+ },
402
+ loading: false,
403
+ options: {
404
+ responsive: true,
405
+ scrollZoom: true,
406
+ },
407
+ dragmode: "pan",
408
+ xaxis: {
409
+ title: {
410
+ text: outputPlot.xAxisTitle,
411
+ font: {
412
+ size: 10,
413
+ },
414
+ },
415
+ },
416
+ yaxis: {
417
+ title: {
418
+ text: outputPlot.yAxisTitle,
419
+ font: {
420
+ size: 10,
421
+ },
422
+ },
423
+ },
424
+ };
425
+ });
162
426
 
163
427
  // Finalise our UI.
164
428
  // Note: we try both here and in the mounted() function since we have no
@@ -167,14 +431,6 @@ export default {
167
431
 
168
432
  this.$nextTick(() => {
169
433
  finaliseUi(this);
170
-
171
- this.simulationData.forEach((data, index) => {
172
- this.simulationData[index] = [{
173
- x: [],
174
- y: [],
175
- type: "scatter",
176
- }];
177
- });
178
434
  });
179
435
  },
180
436
  /**
@@ -196,62 +452,78 @@ export default {
196
452
  },
197
453
  /**
198
454
  * @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`
455
+ * View the simulation-based dataset on PMR. The simulation UI has a `View Workspace` button which, when clicked,
456
+ * calls this method.
203
457
  */
204
- retrieveRequest(request) {
205
- // Settings specific to OpenCOR/oSPARC.
458
+ viewWorkspace() {
459
+ const url = PMR_URL + this.id;
206
460
 
207
- let isOpencorSimulation = request.solver.name === OPENCOR_SOLVER_NAME;
461
+ window.open(url.substring(0, url.lastIndexOf("/")), "_blank");
462
+ },
463
+ /**
464
+ * @vuese
465
+ * Data needed to set a model's parameters.
466
+ */
467
+ parametersData() {
468
+ const res = {};
208
469
 
209
- if (isOpencorSimulation) {
210
- request.opencor = {
211
- model_url: this.simulationUiInfo.simulation.opencor.resource,
212
- json_config: {},
213
- };
214
- } else {
215
- request.osparc = {};
216
- }
470
+ this.simulationUiInfo.parameters.forEach((parameter) => {
471
+ res[parameter.name] = evaluateValue(this, parameter.value);
472
+ });
217
473
 
218
- // Specify the ending point and point interval, if we have some.
474
+ return res;
475
+ },
476
+ /**
477
+ * @vuese
478
+ * Data needed to specify the model output.
479
+ */
480
+ outputData() {
481
+ if (this.output === undefined) {
482
+ if (this.simulationUiInfo.output.data !== undefined) {
483
+ this.output = [];
219
484
 
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
- };
485
+ this.simulationUiInfo.output.data.forEach((output) => {
486
+ this.output.push(output.name);
487
+ });
488
+ }
227
489
  }
228
490
 
229
- // Specify the parameters, if any.
230
-
231
- if (this.simulationUiInfo.parameters !== undefined) {
232
- let parameters = {};
491
+ return this.output;
492
+ },
493
+ /**
494
+ * @vuese
495
+ * Create the `request` that is going to be used by `startSimulation` to ask oSPARC to start the simulation.
496
+ */
497
+ retrieveRequest() {
498
+ const request = {
499
+ solver: this.solver
500
+ };
233
501
 
234
- this.simulationUiInfo.parameters.forEach((parameter) => {
235
- parameters[parameter.name] = evaluateValue(this, parameter.value);
236
- });
502
+ if (this.opencorBasedSimulation) {
503
+ request.opencor = {
504
+ model_url: this.simulationUiInfo.simulation.opencor.resource,
505
+ json_config: {},
506
+ };
237
507
 
238
- if (isOpencorSimulation) {
239
- request.opencor.json_config.parameters = parameters;
240
- } else {
241
- request.osparc.job_inputs = parameters;
508
+ if ( (this.simulationUiInfo.simulation.opencor.endingPoint !== undefined)
509
+ && (this.simulationUiInfo.simulation.opencor.pointInterval !== undefined)) {
510
+ request.opencor.json_config.simulation = {
511
+ "Ending point": this.simulationUiInfo.simulation.opencor.endingPoint,
512
+ "Point interval": this.simulationUiInfo.simulation.opencor.pointInterval,
513
+ };
242
514
  }
243
- }
244
515
 
245
- // Specify what we want to retrieve, if anything.
516
+ request.opencor.json_config.parameters = this.parametersData();
246
517
 
247
- if (isOpencorSimulation && (this.simulationUiInfo.output.data !== undefined)) {
248
- let index = -1;
518
+ const output = this.outputData();
249
519
 
250
- request.opencor.json_config.output = [];
520
+ if (output !== undefined) {
521
+ request.opencor.json_config.output = output;
522
+ }
523
+ } else {
524
+ request.osparc = {};
251
525
 
252
- this.simulationUiInfo.output.data.forEach((outputData) => {
253
- request.opencor.json_config.output[++index] = outputData.name;
254
- });
526
+ request.osparc.job_inputs = this.parametersData();
255
527
  }
256
528
 
257
529
  return request;
@@ -268,9 +540,8 @@ export default {
268
540
 
269
541
  if (typeof(results) === "string") {
270
542
  const SPACES = /[ \t]+/g;
271
-
272
- let lines = results.trim().split("\n");
273
- let iMax = lines[0].trim().split(SPACES).length;
543
+ const lines = results.trim().split("\n");
544
+ const iMax = lines[0].trim().split(SPACES).length;
274
545
 
275
546
  results = {};
276
547
 
@@ -284,7 +555,7 @@ export default {
284
555
  ++i;
285
556
 
286
557
  let j = -1;
287
- let values = line.trim().split(SPACES);
558
+ const values = line.trim().split(SPACES);
288
559
 
289
560
  values.forEach((value) => {
290
561
  results[++j][i] = Number(value);
@@ -294,22 +565,19 @@ export default {
294
565
 
295
566
  // Get the results ready for plotting.
296
567
 
297
- let index = -1;
298
- let iMax = results[this.simulationDataId[Object.keys(this.simulationDataId)[0]]].length;
568
+ const parser = new math.parser();
299
569
 
300
- this.simulationUiInfo.output.plots.forEach((outputPlot) => {
301
- let xValue = [];
302
- let yValue = [];
570
+ Object.keys(this.simulationResultsId).forEach((id) => {
571
+ parser.set(id, results[this.simulationResultsId[id]]);
572
+ });
303
573
 
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
- }
574
+ let index = -1;
308
575
 
309
- this.simulationData[++index] = [
576
+ this.simulationUiInfo.output.plots.forEach((outputPlot) => {
577
+ this.simulationResults[++index] = [
310
578
  {
311
- x: xValue,
312
- y: yValue,
579
+ x: parser.evaluate(outputPlot.xValue),
580
+ y: parser.evaluate(outputPlot.yValue),
313
581
  type: "scatter",
314
582
  },
315
583
  ];
@@ -325,9 +593,9 @@ export default {
325
593
  checkSimulation(data) {
326
594
  // Check the simulation.
327
595
 
328
- let xmlhttp = new XMLHttpRequest();
596
+ const xmlhttp = new XMLHttpRequest();
329
597
 
330
- xmlhttp.open("POST", this.apiLocation + "/check_simulation", true);
598
+ xmlhttp.open("POST", this.apiLocation + "/check_simulation");
331
599
  xmlhttp.setRequestHeader("Content-type", "application/json");
332
600
  xmlhttp.onreadystatechange = () => {
333
601
  if (xmlhttp.readyState === 4) {
@@ -372,36 +640,18 @@ export default {
372
640
  * button which, when clicked, calls this method.
373
641
  */
374
642
  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
643
  // Start the simulation (after resetting our previous simulation data, in
392
644
  // 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
645
 
396
646
  this.userMessage = "Loading simulation results...";
397
647
  this.showUserMessage = true;
398
648
 
399
649
  this.$nextTick(() => {
400
- this.simulationData = [];
650
+ this.simulationResults = {};
401
651
 
402
- let xmlhttp = new XMLHttpRequest();
652
+ const xmlhttp = new XMLHttpRequest();
403
653
 
404
- xmlhttp.open("POST", this.apiLocation + "/start_simulation", true);
654
+ xmlhttp.open("POST", this.apiLocation + "/start_simulation");
405
655
  xmlhttp.setRequestHeader("Content-type", "application/json");
406
656
  xmlhttp.onreadystatechange = () => {
407
657
  if (xmlhttp.readyState === 4) {
@@ -423,39 +673,71 @@ export default {
423
673
  }
424
674
  }
425
675
  };
426
- xmlhttp.send(JSON.stringify(this.retrieveRequest({
427
- solver: solver
428
- })));
676
+ xmlhttp.send(JSON.stringify(this.retrieveRequest()));
429
677
  });
430
678
  },
431
679
  },
432
680
  created: function() {
433
- // Try to retrieve the UI information, but only if we have a name.
681
+ // Try to retrieve the UI information.
434
682
 
435
- if (this.name !== undefined) {
683
+ if (this.id > 0) {
436
684
  this.userMessage = "Retrieving UI information...";
437
685
  this.showUserMessage = true;
438
686
 
439
687
  // 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
688
 
443
689
  this.$nextTick(() => {
444
- let xmlhttp = new XMLHttpRequest();
690
+ const xmlhttp = new XMLHttpRequest();
445
691
 
446
- xmlhttp.open("GET", this.apiLocation + "/simulation_ui_file/" + this.id, true);
447
- xmlhttp.setRequestHeader("Content-type", "application/json");
692
+ xmlhttp.open("GET", this.apiLocation + "/simulation_ui_file/" + this.id);
448
693
  xmlhttp.onreadystatechange = () => {
449
694
  if (xmlhttp.readyState === 4) {
450
695
  this.showUserMessage = false;
451
696
 
452
697
  if (xmlhttp.status === 200) {
453
- this.buildSimulationUi(JSON.parse(xmlhttp.responseText));
698
+ this.$nextTick(() => {
699
+ this.buildSimulationUi(JSON.parse(xmlhttp.responseText));
700
+ });
454
701
  }
455
702
  }
456
703
  };
457
704
  xmlhttp.send();
458
705
  });
706
+ } else if (this.id !== 0) {
707
+ this.userMessage = "Retrieving OMEX file...";
708
+ this.showUserMessage = true;
709
+
710
+ // Retrieve the OMEX file, extract the simulation UI JSON file from it and
711
+ // then build the simulation UI.
712
+
713
+ this.$nextTick(() => {
714
+ const xmlhttp = new XMLHttpRequest();
715
+
716
+ xmlhttp.open("POST", this.apiLocation + "/pmr_file");
717
+ xmlhttp.setRequestHeader("Content-type", "application/json");
718
+ xmlhttp.onreadystatechange = () => {
719
+ if ((xmlhttp.readyState === 4) && (xmlhttp.status === 200)) {
720
+ libOpenCOR().then((libopencor) => {
721
+ this.pmrBasedCombineArchive = true;
722
+ this.libopencor = libopencor;
723
+ this.fileManager = this.libopencor.FileManager.instance();
724
+
725
+ const fileContents = Uint8Array.from(atob(xmlhttp.response), (c) => c.charCodeAt(0));
726
+ const file = this.manageFile(PMR_URL + this.id, fileContents);
727
+
728
+ const decoder = new TextDecoder();
729
+ const simulationUiInfo = JSON.parse(decoder.decode(file.childFile("simulation.json").contents()));
730
+
731
+ this.showUserMessage = false;
732
+
733
+ this.$nextTick(() => {
734
+ this.buildSimulationUi(simulationUiInfo);
735
+ });
736
+ });
737
+ }
738
+ };
739
+ xmlhttp.send(JSON.stringify({path: this.id}));
740
+ });
459
741
  }
460
742
  },
461
743
  mounted: function() {