@abi-software/simulationvuer 3.0.10-beta.1 → 3.0.11

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.
@@ -1,17 +1,14 @@
1
1
  <template>
2
2
  <div class="simulation-vuer" v-loading="showUserMessage" :element-loading-text="userMessage">
3
3
  <div class="container" v-if="opencorOmexFile === null">
4
- <p v-if="!hasValidSimulationUiInfo && !showUserMessage" class="default error">
5
- <span class="error">Error:</span> {{ errorMessage }}.
6
- </p>
4
+ <p v-if="!hasValidSimulationUiInfo && !showUserMessage" class="default error"><span class="error">Error:</span> {{ errorMessage }}.</p>
7
5
  <div class="main" v-if="hasValidSimulationUiInfo">
8
- <div class="main-left" :class="{ 'with-buttons': uuid }">
6
+ <div class="main-left" :class="{'with-buttons': uuid}">
9
7
  <p class="default name">{{ name }}</p>
10
8
  <el-divider></el-divider>
11
9
  <p class="default input-parameters">Input parameters</p>
12
10
  <div class="input scrollbar">
13
- <SimulationVuerInput
14
- v-for="(input, index) in simulationUiInfo.input"
11
+ <SimulationVuerInput v-for="(input, index) in simulationUiInfo.input"
15
12
  ref="simInput"
16
13
  :defaultValue="input.defaultValue"
17
14
  :key="`input-${index}`"
@@ -36,8 +33,7 @@
36
33
  </div>
37
34
  </div>
38
35
  <div class="main-right" ref="output" v-show="isSimulationValid">
39
- <PlotVuer
40
- v-for="(_outputPlot, index) in simulationUiInfo.output.plots"
36
+ <PlotVuer v-for="(_outputPlot, index) in simulationUiInfo.output.plots"
41
37
  :key="`output-${index}`"
42
38
  :metadata="plotMetadata(index)"
43
39
  :data-source="{ data: simulationResults[index] }"
@@ -47,155 +43,51 @@
47
43
  />
48
44
  </div>
49
45
  <div class="main-right" v-show="!isSimulationValid">
50
- <p class="default error">
51
- <span class="error">Error:</span>
52
- <span v-html="errorMessage"></span>.
53
- </p>
46
+ <p class="default error"><span class="error">Error:</span> <span v-html="errorMessage"></span>.</p>
54
47
  </div>
55
48
  </div>
56
49
  </div>
57
50
  <div v-else class="opencor">
58
- <OpenCOR :omex="opencorOmexFile" theme="light" />
51
+ <OpenCOR
52
+ ref="opencorRef"
53
+ :omex="opencorOmexFile"
54
+ theme="light"
55
+ @simulationData="onSimulationData($event)"
56
+ />
59
57
  </div>
60
58
  </div>
61
59
  </template>
62
60
 
63
- <script setup>
64
- import { onMounted, onUnmounted, ref } from 'vue'
65
-
66
- const emit = defineEmits(['data-notification'])
67
-
68
- const activeSubscriptions = ref([])
69
- // Only for demonstration purposes.
70
- let intervalTimer = null
71
-
72
- // Only for demonstration purposes.
73
- function getMockValue(type, tick) {
74
- if (type === 'sine') return Math.sin(tick * 0.1) * 10
75
- if (type === 'linear') return tick
76
- return Math.random() * 10
77
- }
78
-
79
- // Only for demonstration purposes.
80
- function mockType(req) {
81
- return Math.random() < 0.5 ? 'sine' : 'random'
82
- }
83
-
84
- // Only for demonstration purposes.
85
- function generateMockSeries(type, length = 100) {
86
- const data = []
87
- for (let i = 0; i < length; i++) {
88
- data.push(getMockValue(type, i))
89
- }
90
- return data
91
- }
92
-
93
- function addDataSubscription(subscription) {
94
- if (!subscription || typeof subscription !== 'object') {
95
- console.error('addDataSubscription: Subscription must be an object.')
96
- return
97
- }
98
-
99
- const EXPECTED_ID = 'nz.ac.auckland.simulation-data-request'
100
- const EXPECTED_MAJOR_VERSION = 0
101
-
102
- if (subscription.id !== EXPECTED_ID) {
103
- console.warn(`Request ignored: Invalid ID '${subscription.id}'`)
104
- return
105
- }
106
-
107
- const subscriptionMajorVersion = parseInt(subscription.version.split('.')[0])
108
- if (subscriptionMajorVersion !== EXPECTED_MAJOR_VERSION) {
109
- console.warn(
110
- `Request ignored: Version mismatch. Expected v${EXPECTED_MAJOR_VERSION}.x, got v${subscription.version}`
111
- )
112
- return
113
- }
114
-
115
- activeSubscriptions.value.push(subscription.payload)
116
- sendDataForSubscription(subscription.payload)
117
- }
118
-
119
- function sendDataForSubscription(dataSubscription) {
120
- // Only for demonstration purposes.
121
- const type = mockType(dataSubscription)
122
- let data = {
123
- y: generateMockSeries(type, 100),
124
- title: `${dataSubscription.component}.${dataSubscription.variable}`,
125
- }
126
- if (dataSubscription.withVOI) {
127
- const voi_type = 'linear'
128
- data.x = generateMockSeries(voi_type, 100)
129
- }
130
-
131
- const response = {
132
- id: 'nz.ac.auckland.simulation-data-response',
133
- version: '0.1.0',
134
- payload: {
135
- windowId: dataSubscription.windowId,
136
- ownerId: dataSubscription.ownerId,
137
- data: data,
138
- },
139
- }
140
-
141
- emit('data-notification', response)
142
- }
143
-
144
- function removeDataSubscription(subscriptionId) {
145
- activeSubscriptions.value = activeSubscriptions.value.filter(
146
- (sub) => sub.windowId !== subscriptionId
147
- )
148
- }
149
-
150
- defineExpose({ addDataSubscription, removeDataSubscription })
151
-
152
- // Only for demonstration purposes.
153
- onMounted(() => {
154
- intervalTimer = setInterval(() => {
155
- if (activeSubscriptions.value.length === 0) return
156
-
157
- activeSubscriptions.value.forEach((subscription) => {
158
- sendDataForSubscription(subscription)
159
- })
160
- }, 3000)
161
- })
162
-
163
- // Only for demonstration purposes.
164
- onUnmounted(() => {
165
- if (intervalTimer) clearInterval(intervalTimer)
166
- })
167
- </script>
168
-
169
61
  <script>
170
- import { PlotVuer } from '@abi-software/plotvuer'
171
- import '@abi-software/plotvuer/dist/style.css'
172
- import SimulationVuerInput from './SimulationVuerInput.vue'
173
- import { ElButton, ElDivider, ElLoading } from 'element-plus'
174
- import { evaluateValue, finaliseUi, OPENCOR_SOLVER_NAME } from './common.js'
175
- import { validJson } from './json.js'
176
- import { create, all } from 'mathjs'
177
- import OpenCOR from '@opencor/opencor'
178
- import '@opencor/opencor/style.css'
62
+ import { PlotVuer } from "@abi-software/plotvuer";
63
+ import "@abi-software/plotvuer/dist/style.css";
64
+ import SimulationVuerInput from "./SimulationVuerInput.vue";
65
+ import { ElButton, ElDivider, ElLoading } from "element-plus";
66
+ import { evaluateValue, finaliseUi, OPENCOR_SOLVER_NAME } from "./common.js";
67
+ import { validJson } from "./json.js";
68
+ import { create, all } from "mathjs";
69
+ import OpenCOR from '@opencor/opencor';
70
+ import '@opencor/opencor/style.css';
179
71
 
180
- const PMR_URL = 'https://models.physiomeproject.org/'
72
+ const PMR_URL = "https://models.physiomeproject.org/";
181
73
 
182
- const math = create(all, {})
74
+ const math = create(all, {});
183
75
 
184
76
  const IdType = Object.freeze({
185
77
  DATASET_ID: 'dataset_id',
186
78
  DATASET_URL: 'dataset_url',
187
79
  PMR_PATH: 'pmr_path',
188
80
  RAW_COMBINE_ARCHIVE: 'raw_combine_archive',
189
- })
81
+ });
190
82
 
191
83
  function isWebProtocol(urlString) {
192
84
  try {
193
- const url = new URL(urlString)
85
+ const url = new URL(urlString);
194
86
  // Protocol property includes the colon, e.g., "https:".
195
- return url.protocol === 'http:' || url.protocol === 'https:'
87
+ return url.protocol === "http:" || url.protocol === "https:";
196
88
  } catch (_err) {
197
89
  // String was not a valid URL.
198
- return false
90
+ return false;
199
91
  }
200
92
  }
201
93
 
@@ -203,7 +95,7 @@ function isWebProtocol(urlString) {
203
95
  * SimulationVuer
204
96
  */
205
97
  export default {
206
- name: 'SimulationVuer',
98
+ name: "SimulationVuer",
207
99
  components: {
208
100
  PlotVuer,
209
101
  SimulationVuerInput,
@@ -235,39 +127,39 @@ export default {
235
127
  data: function () {
236
128
  // Determine the ID's type.
237
129
 
238
- let idType
130
+ let idType;
239
131
 
240
- if (typeof this.id === 'number') {
241
- idType = IdType.DATASET_ID
132
+ if (typeof this.id === "number") {
133
+ idType = IdType.DATASET_ID;
242
134
  } else if (this.id instanceof Uint8Array) {
243
- idType = IdType.RAW_COMBINE_ARCHIVE
135
+ idType = IdType.RAW_COMBINE_ARCHIVE;
244
136
  } else if (isWebProtocol(this.id)) {
245
- idType = IdType.DATASET_URL
137
+ idType = IdType.DATASET_URL;
246
138
  } else {
247
- idType = IdType.PMR_PATH
139
+ idType = IdType.PMR_PATH;
248
140
  }
249
141
 
250
142
  // Retrieve some information about the dataset.
251
143
 
252
144
  if (idType === IdType.DATASET_ID) {
253
- const xmlhttp = new XMLHttpRequest()
145
+ const xmlhttp = new XMLHttpRequest();
254
146
 
255
- xmlhttp.open('GET', this.apiLocation + '/sim/dataset/' + this.id)
147
+ xmlhttp.open("GET", this.apiLocation + "/sim/dataset/" + this.id);
256
148
  xmlhttp.onreadystatechange = () => {
257
149
  if (xmlhttp.readyState === 4) {
258
150
  if (xmlhttp.status === 200) {
259
- const datasetInfo = JSON.parse(xmlhttp.responseText)
151
+ const datasetInfo = JSON.parse(xmlhttp.responseText);
260
152
 
261
- this.name = datasetInfo.name
262
- this.uuid = datasetInfo.study !== undefined ? datasetInfo.study.uuid : undefined
153
+ this.name = datasetInfo.name;
154
+ this.uuid = (datasetInfo.study !== undefined) ? datasetInfo.study.uuid : undefined;
263
155
  }
264
156
  }
265
- }
266
- xmlhttp.send()
157
+ };
158
+ xmlhttp.send();
267
159
  }
268
160
 
269
161
  return {
270
- errorMessage: '',
162
+ errorMessage: "",
271
163
  fileManager: undefined,
272
164
  hasFinalisedUi: false,
273
165
  hasValidSimulationUiInfo: false,
@@ -289,12 +181,210 @@ export default {
289
181
  simulationResultsId: {},
290
182
  simulationUiInfo: {},
291
183
  solver: undefined,
292
- userMessage: '',
184
+ userMessage: "",
293
185
  ui: null,
294
186
  uuid: null,
295
- }
187
+ activeSubscriptions: [],
188
+ };
296
189
  },
297
190
  methods: {
191
+ /**
192
+ * @public
193
+ * Add a data subscription.
194
+ * @arg `subscription `
195
+ */
196
+ addDataSubscription(subscription) {
197
+ // Check that the subscription is valid.
198
+
199
+ if (!subscription || typeof subscription !== 'object') {
200
+ console.error('addDataSubscription: subscription must be an object.');
201
+
202
+ return;
203
+ }
204
+
205
+ // Check that the subscription has the expected ID.
206
+
207
+ const EXPECTED_ID = 'nz.ac.auckland.simulation-data-request';
208
+ const EXPECTED_MAJOR_VERSION = 0;
209
+
210
+ if (subscription.id !== EXPECTED_ID) {
211
+ console.warn(`addDataSubscription: invalid ID (expected '${EXPECTED_ID}' but got '${subscription.id}').`);
212
+
213
+ return;
214
+ }
215
+
216
+ // Check that the version is valid and compatible with what we expect.
217
+
218
+ if (typeof subscription.version !== 'string') {
219
+ console.warn(`addDataSubscription: missing or non-string version ('${subscription.version}').`);
220
+
221
+ return;
222
+ }
223
+
224
+ const versionParts = subscription.version.split('.');
225
+
226
+ if (versionParts.length < 1 || !/^[0-9]+$/.test(versionParts[0])) {
227
+ console.warn(`addDataSubscription: malformed version ('${subscription.version}').`);
228
+
229
+ return;
230
+ }
231
+
232
+ const subscriptionMajorVersion = parseInt(versionParts[0], 10);
233
+
234
+ if (Number.isNaN(subscriptionMajorVersion)) {
235
+ console.warn(`addDataSubscription: could not parse the major version from ('${subscription.version}')`);
236
+
237
+ return;
238
+ }
239
+
240
+ if (subscriptionMajorVersion !== EXPECTED_MAJOR_VERSION) {
241
+ console.warn(`addDataSubscription: version mismatch (expected v${EXPECTED_MAJOR_VERSION}.y.z but got v${subscription.version}).`);
242
+
243
+ return;
244
+ }
245
+
246
+ // Check that the payload contains the required fields.
247
+
248
+ const payload = subscription.payload || {};
249
+ const missing = [];
250
+
251
+ if (payload.windowId == null) {
252
+ missing.push('windowId');
253
+ }
254
+
255
+ if (payload.ownerId == null) {
256
+ missing.push('ownerId');
257
+ }
258
+
259
+ if (!payload.component) {
260
+ missing.push('component');
261
+ }
262
+
263
+ if (!payload.variable) {
264
+ missing.push('variable');
265
+ }
266
+
267
+ if (missing.length) {
268
+ console.warn(`addDataSubscription: payload missing fields: ${missing.join(', ')}.`);
269
+
270
+ return;
271
+ }
272
+
273
+ // The subscription is valid, so add it to our list of active subscriptions.
274
+
275
+ this.activeSubscriptions.push(payload);
276
+
277
+ // Ask OpenCOR to track the simulation data associated with the subscription's component and variable (and the
278
+ // VOI, if requested).
279
+
280
+ const modelParameters = [];
281
+
282
+ if (subscription.payload?.withVOI) {
283
+ modelParameters.push('VOI');
284
+ }
285
+
286
+ modelParameters.push(`${subscription.payload?.component}/${subscription.payload?.variable}`);
287
+
288
+ this.$refs.opencorRef?.trackSimulationData(modelParameters);
289
+ },
290
+ /**
291
+ * @public
292
+ * Remove a data subscription.
293
+ * @arg `subscriptionId `
294
+ */
295
+ removeDataSubscription(subscriptionId) {
296
+ // Ask OpenCOR to stop tracking the simulation data associated with the subscription's component and variable (and
297
+ // the VOI, if requested and unless it's requested by another subscription).
298
+
299
+ const subscription = this.activeSubscriptions.find((activeSubscription) => {
300
+ return activeSubscription.windowId === subscriptionId;
301
+ });
302
+
303
+ if (!subscription) {
304
+ console.warn(`removeDataSubscription: no active subscription found for id ${subscriptionId}.`);
305
+
306
+ return;
307
+ }
308
+
309
+ const isVoiTrackedByAnotherSubscription = this.activeSubscriptions.some((activeSubscription) => {
310
+ return activeSubscription.windowId !== subscriptionId && activeSubscription.withVOI;
311
+ });
312
+ const isModelParameterTrackedByAnotherSubscription = this.activeSubscriptions.some((activeSubscription) => {
313
+ return (
314
+ activeSubscription.windowId !== subscriptionId &&
315
+ activeSubscription.component === subscription.component &&
316
+ activeSubscription.variable === subscription.variable
317
+ );
318
+ });
319
+
320
+ if (!isVoiTrackedByAnotherSubscription || !isModelParameterTrackedByAnotherSubscription) {
321
+ const modelParameters = [];
322
+
323
+ if (subscription.withVOI && !isVoiTrackedByAnotherSubscription) {
324
+ modelParameters.push('VOI');
325
+ }
326
+
327
+ if (!isModelParameterTrackedByAnotherSubscription) {
328
+ modelParameters.push(`${subscription.component}/${subscription.variable}`);
329
+ }
330
+
331
+ if (modelParameters.length) {
332
+ this.$refs.opencorRef?.untrackSimulationData(modelParameters);
333
+ }
334
+ }
335
+
336
+ // Remove the subscription from our list of active subscriptions.
337
+
338
+ this.activeSubscriptions = this.activeSubscriptions.filter((activeSubscription) => {
339
+ return activeSubscription.windowId !== subscriptionId;
340
+ });
341
+ },
342
+ /**
343
+ * @public
344
+ * Let the outside world know that we have received some simulation data from OpenCOR by emitting a `data-notification` event.
345
+ * @arg `event`
346
+ */
347
+ onSimulationData(event) {
348
+ const simulationData = event.simulationData || {};
349
+ let voi;
350
+
351
+ this.activeSubscriptions.forEach((activeSubscription) => {
352
+ const modelParameter = `${activeSubscription.component}/${activeSubscription.variable}`;
353
+
354
+ if (simulationData[modelParameter] == null) {
355
+ console.warn(`onSimulationData: no data for ${modelParameter}.`);
356
+
357
+ return;
358
+ }
359
+
360
+ const data = {
361
+ y: Array.from(simulationData[modelParameter]),
362
+ title: `${activeSubscription.component}.${activeSubscription.variable}`,
363
+ };
364
+
365
+ if (activeSubscription.withVOI) {
366
+ if (simulationData.VOI == null) {
367
+ console.warn('onSimulationData: no data for VOI.');
368
+
369
+ return;
370
+ } else {
371
+ voi ??= Array.from(simulationData.VOI);
372
+
373
+ data.x = voi;
374
+ }
375
+ }
376
+
377
+ this.$emit('data-notification', {
378
+ id: 'nz.ac.auckland.simulation-data-response',
379
+ version: '0.1.0',
380
+ payload: {
381
+ windowId: activeSubscription.windowId,
382
+ ownerId: activeSubscription.ownerId,
383
+ data,
384
+ },
385
+ });
386
+ });
387
+ },
298
388
  /**
299
389
  * @public
300
390
  * Generate the metadata associated with the plot which `index` is given.
@@ -302,13 +392,13 @@ export default {
302
392
  */
303
393
  plotMetadata(index) {
304
394
  return {
305
- version: '1.1.0',
306
- type: 'plot',
395
+ version: "1.1.0",
396
+ type: "plot",
307
397
  attrs: {
308
- style: 'timeseries',
398
+ style: "timeseries",
309
399
  layout: this.layout[index],
310
400
  },
311
- }
401
+ };
312
402
  },
313
403
  /**
314
404
  * @public
@@ -318,49 +408,49 @@ export default {
318
408
  buildSimulationUi(simulationUiInfo) {
319
409
  // Keep track of the simulation UI information.
320
410
 
321
- this.simulationUiInfo = simulationUiInfo
411
+ this.simulationUiInfo = simulationUiInfo;
322
412
 
323
413
  // Make sure that the simulation UI information is valid.
324
414
 
325
- this.hasValidSimulationUiInfo = validJson(this.simulationUiInfo)
415
+ this.hasValidSimulationUiInfo = validJson(this.simulationUiInfo);
326
416
 
327
417
  if (!this.hasValidSimulationUiInfo) {
328
- this.errorMessage = 'the simulation.json file is malformed'
418
+ this.errorMessage = "the simulation.json file is malformed";
329
419
 
330
- return
420
+ return;
331
421
  }
332
422
 
333
423
  // Retrieve and keep track of the solver to be used for the simulation.
334
424
 
335
425
  this.simulationUiInfo.simulation.solvers.forEach((solver) => {
336
- if (solver.if === undefined || evaluateValue(this, solver.if)) {
337
- this.solver = solver
426
+ if ((solver.if === undefined) || evaluateValue(this, solver.if)) {
427
+ this.solver = solver;
338
428
  }
339
- })
429
+ });
340
430
 
341
431
  if (this.solver === undefined) {
342
- this.hasValidSimulationUiInfo = false
343
- this.errorMessage = 'no solver name and/or solver version specified'
432
+ this.hasValidSimulationUiInfo = false;
433
+ this.errorMessage = "no solver name and/or solver version specified";
344
434
 
345
- return
435
+ return;
346
436
  }
347
437
 
348
- this.opencorBasedSimulation = this.solver.name === OPENCOR_SOLVER_NAME
438
+ this.opencorBasedSimulation = this.solver.name === OPENCOR_SOLVER_NAME;
349
439
 
350
440
  // Initialise our UI.
351
441
 
352
442
  this.simulationUiInfo.output.data.forEach((data) => {
353
- this.simulationResultsId[data.id] = data.name
354
- })
443
+ this.simulationResultsId[data.id] = data.name;
444
+ });
355
445
 
356
- let index = -1
446
+ let index = -1;
357
447
 
358
448
  this.simulationUiInfo.output.plots.forEach((outputPlot) => {
359
- ++index
449
+ ++index;
360
450
 
361
451
  this.layout[index] = {
362
- paper_bgcolor: 'rgba(0, 0, 0, 0)',
363
- plot_bgcolor: 'rgba(0, 0, 0, 0)',
452
+ paper_bgcolor: "rgba(0, 0, 0, 0)",
453
+ plot_bgcolor: "rgba(0, 0, 0, 0)",
364
454
  autosize: true,
365
455
  margin: {
366
456
  t: 25,
@@ -374,7 +464,7 @@ export default {
374
464
  responsive: true,
375
465
  scrollZoom: true,
376
466
  },
377
- dragmode: 'pan',
467
+ dragmode: "pan",
378
468
  xaxis: {
379
469
  title: {
380
470
  text: outputPlot.xAxisTitle,
@@ -391,8 +481,8 @@ export default {
391
481
  },
392
482
  },
393
483
  },
394
- }
395
- })
484
+ };
485
+ });
396
486
 
397
487
  // Finalise our UI.
398
488
  // Note: we try both here and in the mounted() function since we have no
@@ -400,8 +490,8 @@ export default {
400
490
  // information.
401
491
 
402
492
  this.$nextTick(() => {
403
- finaliseUi(this)
404
- })
493
+ finaliseUi(this);
494
+ });
405
495
  },
406
496
  /**
407
497
  * @public
@@ -410,7 +500,7 @@ export default {
410
500
  * method.
411
501
  */
412
502
  runOnOsparc() {
413
- window.open(`https://osparc.io/study/${this.uuid}`, '_blank')
503
+ window.open(`https://osparc.io/study/${this.uuid}`, "_blank");
414
504
  },
415
505
  /**
416
506
  * @public
@@ -418,7 +508,7 @@ export default {
418
508
  * clicked, calls this method.
419
509
  */
420
510
  viewDataset() {
421
- window.open(`https://sparc.science/datasets/${this.id}?type=dataset`, '_blank')
511
+ window.open(`https://sparc.science/datasets/${this.id}?type=dataset`, "_blank");
422
512
  },
423
513
  /**
424
514
  * @public
@@ -426,22 +516,22 @@ export default {
426
516
  * calls this method.
427
517
  */
428
518
  viewWorkspace() {
429
- const url = PMR_URL + this.id
519
+ const url = PMR_URL + this.id;
430
520
 
431
- window.open(url.substring(0, url.lastIndexOf('/')), '_blank')
521
+ window.open(url.substring(0, url.lastIndexOf("/")), "_blank");
432
522
  },
433
523
  /**
434
524
  * @public
435
525
  * Data needed to set a model's parameters.
436
526
  */
437
527
  parametersData() {
438
- const res = {}
528
+ const res = {};
439
529
 
440
530
  this.simulationUiInfo.parameters.forEach((parameter) => {
441
- res[parameter.name] = evaluateValue(this, parameter.value)
442
- })
531
+ res[parameter.name] = evaluateValue(this, parameter.value);
532
+ });
443
533
 
444
- return res
534
+ return res;
445
535
  },
446
536
  /**
447
537
  * @public
@@ -450,15 +540,15 @@ export default {
450
540
  outputData() {
451
541
  if (this.output === undefined) {
452
542
  if (this.simulationUiInfo.output.data !== undefined) {
453
- this.output = []
543
+ this.output = [];
454
544
 
455
545
  this.simulationUiInfo.output.data.forEach((output) => {
456
- this.output.push(output.name)
457
- })
546
+ this.output.push(output.name);
547
+ });
458
548
  }
459
549
  }
460
550
 
461
- return this.output
551
+ return this.output;
462
552
  },
463
553
  /**
464
554
  * @public
@@ -466,39 +556,37 @@ export default {
466
556
  */
467
557
  retrieveRequest() {
468
558
  const request = {
469
- solver: this.solver,
470
- }
559
+ solver: this.solver
560
+ };
471
561
 
472
562
  if (this.opencorBasedSimulation) {
473
563
  request.opencor = {
474
564
  model_url: this.simulationUiInfo.simulation.opencor.resource,
475
565
  json_config: {},
476
- }
566
+ };
477
567
 
478
- if (
479
- this.simulationUiInfo.simulation.opencor.endingPoint !== undefined &&
480
- this.simulationUiInfo.simulation.opencor.pointInterval !== undefined
481
- ) {
568
+ if ((this.simulationUiInfo.simulation.opencor.endingPoint !== undefined)
569
+ && (this.simulationUiInfo.simulation.opencor.pointInterval !== undefined)) {
482
570
  request.opencor.json_config.simulation = {
483
- 'Ending point': this.simulationUiInfo.simulation.opencor.endingPoint,
484
- 'Point interval': this.simulationUiInfo.simulation.opencor.pointInterval,
485
- }
571
+ "Ending point": this.simulationUiInfo.simulation.opencor.endingPoint,
572
+ "Point interval": this.simulationUiInfo.simulation.opencor.pointInterval,
573
+ };
486
574
  }
487
575
 
488
- request.opencor.json_config.parameters = this.parametersData()
576
+ request.opencor.json_config.parameters = this.parametersData();
489
577
 
490
- const output = this.outputData()
578
+ const output = this.outputData();
491
579
 
492
580
  if (output !== undefined) {
493
- request.opencor.json_config.output = output
581
+ request.opencor.json_config.output = output;
494
582
  }
495
583
  } else {
496
- request.osparc = {}
584
+ request.osparc = {};
497
585
 
498
- request.osparc.job_inputs = this.parametersData()
586
+ request.osparc.job_inputs = this.parametersData();
499
587
  }
500
588
 
501
- return request
589
+ return request;
502
590
  },
503
591
  /**
504
592
  * @public
@@ -510,50 +598,50 @@ export default {
510
598
  // Convert, if needed, the results to a JSON format that is compatible
511
599
  // with our OpenCOR results.
512
600
 
513
- if (typeof results === 'string') {
514
- const SPACES = /[ \t]+/g
515
- const lines = results.trim().split('\n')
516
- const iMax = lines[0].trim().split(SPACES).length
601
+ if (typeof (results) === "string") {
602
+ const SPACES = /[ \t]+/g;
603
+ const lines = results.trim().split("\n");
604
+ const iMax = lines[0].trim().split(SPACES).length;
517
605
 
518
- results = {}
606
+ results = {};
519
607
 
520
608
  for (let i = 0; i < iMax; ++i) {
521
- results[i] = []
609
+ results[i] = [];
522
610
  }
523
611
 
524
- let i = -1
612
+ let i = -1;
525
613
 
526
614
  lines.forEach((line) => {
527
- ++i
615
+ ++i;
528
616
 
529
- let j = -1
530
- const values = line.trim().split(SPACES)
617
+ let j = -1;
618
+ const values = line.trim().split(SPACES);
531
619
 
532
620
  values.forEach((value) => {
533
- results[++j][i] = Number(value)
534
- })
535
- })
621
+ results[++j][i] = Number(value);
622
+ });
623
+ });
536
624
  }
537
625
 
538
626
  // Get the results ready for plotting.
539
627
 
540
- const parser = new math.parser()
628
+ const parser = new math.parser();
541
629
 
542
630
  Object.keys(this.simulationResultsId).forEach((id) => {
543
- parser.set(id, results[this.simulationResultsId[id]])
544
- })
631
+ parser.set(id, results[this.simulationResultsId[id]]);
632
+ });
545
633
 
546
- let index = -1
634
+ let index = -1;
547
635
 
548
636
  this.simulationUiInfo.output.plots.forEach((outputPlot) => {
549
637
  this.simulationResults[++index] = [
550
638
  {
551
639
  x: parser.evaluate(outputPlot.xValue),
552
640
  y: parser.evaluate(outputPlot.yValue),
553
- type: 'scatter',
641
+ type: "scatter",
554
642
  },
555
- ]
556
- })
643
+ ];
644
+ });
557
645
  },
558
646
  /**
559
647
  * @public
@@ -561,15 +649,9 @@ export default {
561
649
  * @arg `xmlhttp`
562
650
  */
563
651
  showHttpIssue(xmlhttp) {
564
- this.isSimulationValid = false
565
- this.showUserMessage = false
566
- this.errorMessage =
567
- xmlhttp.statusText.toLowerCase() +
568
- " (<a href='https://httpstatuses.com/" +
569
- xmlhttp.status +
570
- "/' target='_blank'>" +
571
- xmlhttp.status +
572
- '</a>)'
652
+ this.isSimulationValid = false;
653
+ this.showUserMessage = false;
654
+ this.errorMessage = xmlhttp.statusText.toLowerCase() + " (<a href='https://httpstatuses.com/" + xmlhttp.status + "/' target='_blank'>" + xmlhttp.status + "</a>)";
573
655
  },
574
656
  /**
575
657
  * @public
@@ -581,44 +663,44 @@ export default {
581
663
  checkSimulation(data) {
582
664
  // Check the simulation.
583
665
 
584
- const xmlhttp = new XMLHttpRequest()
666
+ const xmlhttp = new XMLHttpRequest();
585
667
 
586
- xmlhttp.open('POST', this.apiLocation + '/check_simulation')
587
- xmlhttp.setRequestHeader('Content-type', 'application/json')
668
+ xmlhttp.open("POST", this.apiLocation + "/check_simulation");
669
+ xmlhttp.setRequestHeader("Content-type", "application/json");
588
670
  xmlhttp.onreadystatechange = () => {
589
671
  if (xmlhttp.readyState === 4) {
590
672
  if (xmlhttp.status === 200) {
591
- let response = JSON.parse(xmlhttp.responseText)
673
+ let response = JSON.parse(xmlhttp.responseText);
592
674
 
593
- this.isSimulationValid = response.status === 'ok'
675
+ this.isSimulationValid = response.status === "ok";
594
676
 
595
677
  if (this.isSimulationValid) {
596
678
  if (response.results !== undefined) {
597
679
  // The simulation is finished, so process its results.
598
680
 
599
- this.showUserMessage = false
681
+ this.showUserMessage = false;
600
682
 
601
- this.processSimulationResults(response.results)
683
+ this.processSimulationResults(response.results);
602
684
  } else {
603
685
  // The simulation is not yet finished, so check again in a
604
686
  // second.
605
687
 
606
- let that = this
688
+ let that = this;
607
689
 
608
690
  setTimeout(function () {
609
- that.checkSimulation(data)
610
- }, 1000)
691
+ that.checkSimulation(data);
692
+ }, 1000);
611
693
  }
612
694
  } else {
613
- this.showUserMessage = false
614
- this.errorMessage = response.description
695
+ this.showUserMessage = false;
696
+ this.errorMessage = response.description;
615
697
  }
616
698
  } else {
617
- this.showHttpIssue(xmlhttp)
699
+ this.showHttpIssue(xmlhttp);
618
700
  }
619
701
  }
620
- }
621
- xmlhttp.send(JSON.stringify(data))
702
+ };
703
+ xmlhttp.send(JSON.stringify(data));
622
704
  },
623
705
  /**
624
706
  * @public
@@ -629,73 +711,72 @@ export default {
629
711
  // Start the simulation (after resetting our previous simulation data, in
630
712
  // case there were sonme).
631
713
 
632
- this.userMessage = 'Loading simulation results...'
633
- this.showUserMessage = true
714
+ this.userMessage = "Loading simulation results...";
715
+ this.showUserMessage = true;
634
716
 
635
717
  this.$nextTick(() => {
636
- this.simulationResults = {}
718
+ this.simulationResults = {};
637
719
 
638
- const xmlhttp = new XMLHttpRequest()
720
+ const xmlhttp = new XMLHttpRequest();
639
721
 
640
- xmlhttp.open('POST', this.apiLocation + '/start_simulation')
641
- xmlhttp.setRequestHeader('Content-type', 'application/json')
722
+ xmlhttp.open("POST", this.apiLocation + "/start_simulation");
723
+ xmlhttp.setRequestHeader("Content-type", "application/json");
642
724
  xmlhttp.onreadystatechange = () => {
643
725
  if (xmlhttp.readyState === 4) {
644
726
  if (xmlhttp.status === 200) {
645
- let response = JSON.parse(xmlhttp.responseText)
727
+ let response = JSON.parse(xmlhttp.responseText);
646
728
 
647
- this.isSimulationValid = response.status === 'ok'
729
+ this.isSimulationValid = response.status === "ok";
648
730
 
649
731
  if (this.isSimulationValid) {
650
- this.checkSimulation(response.data)
732
+ this.checkSimulation(response.data);
651
733
  } else {
652
- this.showUserMessage = false
653
- this.errorMessage = response.description
734
+ this.showUserMessage = false;
735
+ this.errorMessage = response.description;
654
736
  }
655
737
  } else {
656
- this.showHttpIssue(xmlhttp)
738
+ this.showHttpIssue(xmlhttp);
657
739
  }
658
740
  }
659
- }
660
- xmlhttp.send(JSON.stringify(this.retrieveRequest()))
661
- })
741
+ };
742
+ xmlhttp.send(JSON.stringify(this.retrieveRequest()));
743
+ });
662
744
  },
663
745
  },
664
746
  created: function () {
665
747
  // Try to retrieve the UI information.
666
748
 
667
749
  if (this.idType === IdType.DATASET_ID) {
668
- this.userMessage = 'Retrieving UI information...'
669
- this.showUserMessage = true
750
+ this.userMessage = "Retrieving UI information...";
751
+ this.showUserMessage = true;
670
752
 
671
753
  // Retrieve and build the simulation UI.
672
754
 
673
755
  this.$nextTick(() => {
674
- const xmlhttp = new XMLHttpRequest()
756
+ const xmlhttp = new XMLHttpRequest();
675
757
 
676
- xmlhttp.open('GET', this.apiLocation + '/simulation_ui_file/' + this.id)
758
+ xmlhttp.open("GET", this.apiLocation + "/simulation_ui_file/" + this.id);
677
759
  xmlhttp.onreadystatechange = () => {
678
760
  if (xmlhttp.readyState === 4) {
679
- this.showUserMessage = false
761
+ this.showUserMessage = false;
680
762
 
681
763
  if (xmlhttp.status === 200) {
682
764
  this.$nextTick(() => {
683
- this.buildSimulationUi(JSON.parse(xmlhttp.responseText))
684
- })
765
+ this.buildSimulationUi(JSON.parse(xmlhttp.responseText));
766
+ });
685
767
  } else {
686
- this.errorMessage = 'the simulation dataset could not be retrieved'
768
+ this.errorMessage = "the simulation dataset could not be retrieved";
687
769
  }
688
770
  }
689
- }
690
- xmlhttp.send()
691
- })
771
+ };
772
+ xmlhttp.send();
773
+ });
692
774
  } else if (this.idType === IdType.DATASET_URL) {
693
- this.opencorOmexFile = this.id
775
+ this.opencorOmexFile = this.id;
694
776
  } else if (this.idType === IdType.PMR_PATH) {
695
- this.opencorOmexFile = PMR_URL + this.id
696
- } else {
697
- // IdType.RAW_COMBINE_ARCHIVE
698
- this.opencorOmexFile = this.id
777
+ this.opencorOmexFile = PMR_URL + this.id;
778
+ } else { // IdType.RAW_COMBINE_ARCHIVE
779
+ this.opencorOmexFile = this.id;
699
780
  }
700
781
  },
701
782
  mounted: function () {
@@ -704,20 +785,20 @@ export default {
704
785
  // idea how long it's going to take to retrieve the simulation UI
705
786
  // information.
706
787
 
707
- this.isMounted = true
788
+ this.isMounted = true;
708
789
 
709
- finaliseUi(this)
790
+ finaliseUi(this);
710
791
  },
711
- }
792
+ };
712
793
  </script>
713
794
 
714
795
  <!-- Add "scoped" attribute to limit CSS to this component only -->
715
796
  <style scoped lang="scss">
716
797
  .simulation-vuer {
717
- --el-color-primary: #8300bf;
718
- --el-color-primary-light-7: #dab3ec;
719
- --el-color-primary-light-8: #e6ccf2;
720
- --el-color-primary-light-9: #f3e6f9;
798
+ --el-color-primary: #8300BF;
799
+ --el-color-primary-light-7: #DAB3EC;
800
+ --el-color-primary-light-8: #E6CCF2;
801
+ --el-color-primary-light-9: #F3E6F9;
721
802
  }
722
803
 
723
804
  :deep(.el-button:hover) {
@@ -731,32 +812,29 @@ export default {
731
812
 
732
813
  :deep(.el-loading-spinner) {
733
814
  .path {
734
- stroke: #8300bf;
815
+ stroke: #8300BF;
735
816
  }
736
817
 
737
818
  i,
738
819
  .el-loading-text {
739
- color: #8300bf;
820
+ color: #8300BF;
740
821
  }
741
822
  }
742
823
 
743
- :deep(.p-floatlabel:has(input:focus)) label,
744
- :deep(.p-floatlabel:has(input:-webkit-autofill)) label,
745
- :deep(.p-floatlabel:has(textarea:focus)) label,
746
- :deep(.p-floatlabel:has(.p-inputwrapper-focus)) label {
747
- color: #8300bf;
824
+ :deep(.p-floatlabel:has(input:focus)) label, :deep(.p-floatlabel:has(input:-webkit-autofill)) label, :deep(.p-floatlabel:has(textarea:focus)) label, :deep(.p-floatlabel:has(.p-inputwrapper-focus)) label {
825
+ color: #8300BF;
748
826
  }
749
827
 
750
828
  :deep(.p-inputtext:enabled:focus) {
751
- border-color: #8300bf;
829
+ border-color: #8300BF;
752
830
  }
753
831
 
754
832
  :deep(.p-select:not(.p-disabled).p-focus) {
755
- border-color: #8300bf;
833
+ border-color: #8300BF;
756
834
  }
757
835
 
758
836
  :deep(.p-slider-range) {
759
- background-color: #8300bf;
837
+ background-color: #8300BF;
760
838
  }
761
839
 
762
840
  div.input {
@@ -958,23 +1036,26 @@ span.error {
958
1036
  <style>
959
1037
  /* Note: not sure why, but the following rules need to be global!? */
960
1038
 
1039
+ .p-progressbar-value {
1040
+ background-color: #8300BF !important;
1041
+ }
1042
+
961
1043
  .p-select-option:not(.p-select-option-selected):not(.p-disabled).p-focus {
962
- background: #f5f7fa !important;
1044
+ background: #F5F7FA !important;
963
1045
  }
964
1046
 
965
1047
  .p-select-option.p-select-option-selected.p-focus {
966
- background: #f5f7fa !important;
967
- color: #8300bf !important;
1048
+ background: #F5F7FA !important;
1049
+ color: #8300BF !important;
968
1050
  }
969
1051
 
970
1052
  .p-select-option.p-select-option-selected {
971
- background: white !important;
972
- color: #8300bf !important;
1053
+ background: white !important;
1054
+ color: #8300BF !important;
973
1055
  }
974
1056
 
975
1057
  .p-select-option-label {
976
- font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans',
977
- 'Droid Sans', 'Helvetica Neue', sans-serif;
978
- font-size: 0.875rem;
1058
+ font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
1059
+ font-size: 0.875rem;
979
1060
  }
980
1061
  </style>