@opencor/opencor 0.20250813.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.
Files changed (63) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +61 -0
  3. package/dist/index-BxzyBYAw.js +98574 -0
  4. package/dist/index.d.ts +8 -0
  5. package/dist/opencor.css +1 -0
  6. package/dist/opencor.es.js +5 -0
  7. package/dist/opencor.umd.js +6275 -0
  8. package/dist/quill-BGLY3Ud3.js +7521 -0
  9. package/package.json +90 -0
  10. package/src/App.vue +27 -0
  11. package/src/assets/app.css +14 -0
  12. package/src/assets/base.css +28 -0
  13. package/src/assets/logo.svg +17 -0
  14. package/src/common/common.ts +86 -0
  15. package/src/common/constants.ts +12 -0
  16. package/src/common/electron.ts +23 -0
  17. package/src/common/electronApi.ts +63 -0
  18. package/src/common/locCommon.ts +170 -0
  19. package/src/common/settings.ts +95 -0
  20. package/src/common/vueCommon.ts +50 -0
  21. package/src/components/BackgroundComponent.vue +26 -0
  22. package/src/components/ContentsComponent.vue +262 -0
  23. package/src/components/DragNDropComponent.vue +29 -0
  24. package/src/components/LoadOpencorComponent.vue +33 -0
  25. package/src/components/MainMenu.vue +220 -0
  26. package/src/components/OpenCOR.vue +546 -0
  27. package/src/components/SpinningWheelComponent.vue +15 -0
  28. package/src/components/dialogs/AboutDialog.vue +51 -0
  29. package/src/components/dialogs/BaseDialog.vue +11 -0
  30. package/src/components/dialogs/OpenRemoteDialog.vue +37 -0
  31. package/src/components/dialogs/ResetAllDialog.vue +13 -0
  32. package/src/components/dialogs/SettingsDialog.vue +42 -0
  33. package/src/components/dialogs/UpdateAvailableDialog.vue +16 -0
  34. package/src/components/dialogs/UpdateDownloadProgressDialog.vue +17 -0
  35. package/src/components/dialogs/UpdateErrorDialog.vue +18 -0
  36. package/src/components/dialogs/UpdateNotAvailableDialog.vue +12 -0
  37. package/src/components/propertyEditors/GraphsPropertyEditor.vue +3 -0
  38. package/src/components/propertyEditors/ParametersPropertyEditor.vue +3 -0
  39. package/src/components/propertyEditors/PropertyEditor.vue +61 -0
  40. package/src/components/propertyEditors/SimulationPropertyEditor.vue +45 -0
  41. package/src/components/propertyEditors/SolversPropertyEditor.vue +3 -0
  42. package/src/components/views/IssuesView.vue +37 -0
  43. package/src/components/views/SimulationExperimentUiView.vue +152 -0
  44. package/src/components/views/SimulationExperimentView.vue +214 -0
  45. package/src/components/widgets/GraphPanelWidget.vue +137 -0
  46. package/src/components/widgets/InputWidget.vue +128 -0
  47. package/src/libopencor/locApi.ts +167 -0
  48. package/src/libopencor/locFileApi.ts +263 -0
  49. package/src/libopencor/locLoggerApi.ts +36 -0
  50. package/src/libopencor/locSedApi.ts +486 -0
  51. package/src/libopencor/locUiJsonApi.ts +485 -0
  52. package/src/libopencor/locVersionApi.ts +17 -0
  53. package/src/libopencor/src/common.cpp +67 -0
  54. package/src/libopencor/src/common.h +27 -0
  55. package/src/libopencor/src/file.cpp +72 -0
  56. package/src/libopencor/src/file.h +15 -0
  57. package/src/libopencor/src/main.cpp +78 -0
  58. package/src/libopencor/src/sed.cpp +348 -0
  59. package/src/libopencor/src/sed.h +53 -0
  60. package/src/libopencor/src/version.cpp +8 -0
  61. package/src/libopencor/src/version.h +5 -0
  62. package/src/main.ts +5 -0
  63. package/src/types/types.d.ts +9 -0
@@ -0,0 +1,137 @@
1
+ <template>
2
+ <div class="h-full">
3
+ <div v-if="showMarker" class="marker" />
4
+ <div ref="mainDiv" class="h-full" />
5
+ </div>
6
+ </template>
7
+
8
+ <script setup lang="ts">
9
+ import Plotly from 'plotly.js-gl2d-dist-min'
10
+ import * as vue from 'vue'
11
+
12
+ import * as vueCommon from '../../common/vueCommon'
13
+ import { MEDIUM_DELAY } from '../../common/constants'
14
+
15
+ let oldMainDivClientWidth = -1
16
+ let oldMainDivClientHeight = -1
17
+
18
+ function resizeIfNeeded() {
19
+ if (mainDiv.value !== null) {
20
+ if (mainDiv.value.clientWidth !== oldMainDivClientWidth || mainDiv.value.clientHeight !== oldMainDivClientHeight) {
21
+ oldMainDivClientWidth = mainDiv.value.clientWidth
22
+ oldMainDivClientHeight = mainDiv.value.clientHeight
23
+
24
+ Plotly.Plots.resize(mainDiv.value)
25
+ }
26
+ }
27
+
28
+ setTimeout(resizeIfNeeded, MEDIUM_DELAY)
29
+ }
30
+
31
+ vue.onMounted(() => {
32
+ resizeIfNeeded()
33
+ })
34
+
35
+ interface IGraphPanelPlotData {
36
+ data: number[]
37
+ }
38
+
39
+ export interface IGraphPanelPlot {
40
+ x: IGraphPanelPlotData
41
+ y: IGraphPanelPlotData
42
+ }
43
+
44
+ interface IProps {
45
+ plots: IGraphPanelPlot[]
46
+ showMarker?: boolean
47
+ }
48
+
49
+ const { plots, showMarker = false } = defineProps<IProps>()
50
+
51
+ const mainDiv = vue.ref<InstanceType<typeof Element> | null>(null)
52
+
53
+ function themeData() {
54
+ // Note: the various keys can be found at https://plotly.com/javascript/reference/.
55
+
56
+ function axisThemeData() {
57
+ return {
58
+ zerolinecolor: vueCommon.isDarkMode.value ? '#71717a' : '#94a3b8', // --p-surface-500 / --p-surface-400
59
+ gridcolor: vueCommon.isDarkMode.value ? '#3f3f46' : '#e2e8f0', // --p-surface-700 / --p-surface-200
60
+ minor: {
61
+ gridcolor: vueCommon.isDarkMode.value ? '#27272a' : '#f1f5f9' // --p-surface-800 / --p-surface-100
62
+ }
63
+ }
64
+ }
65
+
66
+ return {
67
+ paper_bgcolor: vueCommon.isDarkMode.value ? '#18181b' : '#ffffff', // --p-content-background
68
+ plot_bgcolor: vueCommon.isDarkMode.value ? '#18181b' : '#ffffff', // --p-content-background
69
+ font: {
70
+ color: vueCommon.isDarkMode.value ? '#ffffff' : '#334155' // --p-text-color
71
+ },
72
+ colorway: [
73
+ '#7289ab', // Blue
74
+ '#ea7e53', // Orange
75
+ '#eedd78', // Yellow
76
+ '#e69d87', // Pink
77
+ '#73a373', // Green
78
+ '#73b9bc', // Cyan
79
+ '#dd6b66' // Red
80
+ ],
81
+ xaxis: axisThemeData(),
82
+ yaxis: axisThemeData()
83
+ }
84
+ }
85
+
86
+ vue.watch(
87
+ () => [plots, vueCommon.isDarkMode.value],
88
+ () => {
89
+ Plotly.react(
90
+ mainDiv.value,
91
+ plots.map((plot) => ({
92
+ x: plot.x.data,
93
+ y: plot.y.data,
94
+ type: 'scattergl'
95
+ //---OPENCOR---
96
+ // WebGL rendering currently results in "Performance warning: clear() called with no buffers in bitmask" being
97
+ // logged to the console. This is a known issue and it should hopefully be fixed in the next version of Plotly.
98
+ // See https://github.com/plotly/plotly.js/issues/7387 and https://github.com/plotly/plotly.js/pull/7390.
99
+ })),
100
+ {
101
+ // Note: the various keys can be found at https://plotly.com/javascript/reference/.
102
+
103
+ ...themeData(),
104
+ margin: {
105
+ t: 5,
106
+ l: 30,
107
+ b: 20,
108
+ r: 5
109
+ },
110
+ showlegend: false,
111
+ xaxis: {
112
+ tickangle: 0
113
+ },
114
+ yaxis: {
115
+ tickangle: 0
116
+ }
117
+ },
118
+ {
119
+ // Note: the various keys can be found at https://plotly.com/javascript/configuration-options/.
120
+
121
+ responsive: true,
122
+ displayModeBar: false,
123
+ doubleClickDelay: 1000,
124
+ scrollZoom: true,
125
+ showTips: false
126
+ }
127
+ )
128
+ }
129
+ )
130
+ </script>
131
+
132
+ <style scoped>
133
+ .marker {
134
+ width: 3px;
135
+ background-color: var(--p-primary-color);
136
+ }
137
+ </style>
@@ -0,0 +1,128 @@
1
+ <template>
2
+ <div v-if="possibleValues !== undefined">
3
+ <FloatLabel variant="on">
4
+ <Select
5
+ v-model="discreteValue"
6
+ :options="possibleValues"
7
+ optionLabel="name"
8
+ @change="selectChange"
9
+ class="w-full"
10
+ size="small"
11
+ />
12
+ <label>{{ name }}</label>
13
+ </FloatLabel>
14
+ </div>
15
+ <div v-else>
16
+ <FloatLabel variant="on">
17
+ <InputText
18
+ v-model="scalarValueString"
19
+ v-keyfilter="{ pattern: /^[+-]?(\d*(\.\d*)?|\.d*)([eE][+-]?\d*)?$/, validateOnly: true }"
20
+ v-on:focusout="inputTextFocusOut"
21
+ v-on:keypress="inputTextKeyPress"
22
+ class="w-full"
23
+ size="small"
24
+ />
25
+ <label>{{ name }}</label>
26
+ </FloatLabel>
27
+ <Slider
28
+ v-model="scalarValue"
29
+ :min="minimumValue"
30
+ :max="maximumValue"
31
+ :step="stepValue"
32
+ @change="sliderChange"
33
+ class="w-full mt-3"
34
+ size="small"
35
+ />
36
+ </div>
37
+ </template>
38
+
39
+ <script setup lang="ts">
40
+ import * as vue from 'vue'
41
+
42
+ import * as locApi from '../../libopencor/locApi'
43
+
44
+ const value = defineModel<number>({ required: true })
45
+ const emits = defineEmits(['change'])
46
+ const props = defineProps<{
47
+ maximumValue?: number
48
+ minimumValue?: number
49
+ name: string
50
+ possibleValues?: locApi.IUiJsonDiscreteInputPossibleValue[]
51
+ stepValue?: number
52
+ }>()
53
+
54
+ let oldValue = value.value
55
+ const discreteValue = vue.ref<locApi.IUiJsonDiscreteInputPossibleValue | undefined>(
56
+ props.possibleValues ? props.possibleValues[value.value] : undefined
57
+ )
58
+ const scalarValue = vue.ref<number>(value.value)
59
+ const scalarValueString = vue.ref<string>(String(value.value))
60
+
61
+ // Some methods to handle a scalar value using an input text and a slider.
62
+
63
+ function emitChange(newValue: number) {
64
+ void vue.nextTick().then(() => {
65
+ value.value = newValue
66
+
67
+ if (props.possibleValues === undefined) {
68
+ scalarValue.value = newValue
69
+ scalarValueString.value = String(newValue) // This will properly format the input text.
70
+ }
71
+
72
+ oldValue = newValue
73
+
74
+ emits('change', props.name, newValue)
75
+ })
76
+ }
77
+
78
+ interface ISelectChangeEvent {
79
+ value: {
80
+ name: string
81
+ value: number
82
+ }
83
+ }
84
+
85
+ function selectChange(event: ISelectChangeEvent) {
86
+ if (event.value.value !== oldValue) {
87
+ emitChange(event.value.value)
88
+ }
89
+ }
90
+
91
+ function inputTextChange(newValueString: string) {
92
+ if (newValueString === '') {
93
+ newValueString = String(props.minimumValue)
94
+ }
95
+
96
+ if (props.minimumValue !== undefined && Number(newValueString) < props.minimumValue) {
97
+ newValueString = String(props.minimumValue)
98
+ }
99
+
100
+ if (props.maximumValue !== undefined && Number(newValueString) > props.maximumValue) {
101
+ newValueString = String(props.maximumValue)
102
+ }
103
+
104
+ const newValue = Number(newValueString)
105
+
106
+ if (newValue !== oldValue) {
107
+ emitChange(newValue)
108
+ }
109
+ }
110
+
111
+ function inputTextFocusOut(event: Event) {
112
+ inputTextChange((event.target as HTMLInputElement).value)
113
+ }
114
+
115
+ function inputTextKeyPress(event: KeyboardEvent) {
116
+ if (event.key === 'Enter') {
117
+ inputTextChange((event.target as HTMLInputElement).value)
118
+ }
119
+ }
120
+
121
+ function sliderChange(newValue: number | number[]) {
122
+ const valueToEmit = Array.isArray(newValue) ? newValue[0] : newValue
123
+
124
+ if (valueToEmit !== oldValue) {
125
+ emitChange(valueToEmit)
126
+ }
127
+ }
128
+ </script>
@@ -0,0 +1,167 @@
1
+ import { proxyUrl } from '../common/locCommon.js'
2
+
3
+ import { EFileType, type IWasmFile, type IWasmFileManager } from './locFileApi.js'
4
+ import type { IIssue } from './locLoggerApi.js'
5
+ import type { IWasmSedChangeAttribute, IWasmSedDocument } from './locSedApi.js'
6
+
7
+ export interface ICppLocApi {
8
+ // FileManager API.
9
+
10
+ fileManagerUnmanage: (path: string) => void
11
+
12
+ // File API.
13
+
14
+ fileContents: (path: string) => Uint8Array
15
+ fileCreate: (path: string, contents?: Uint8Array) => void
16
+ fileIssues: (path: string) => IIssue[]
17
+ fileType: (path: string) => EFileType
18
+ fileUiJson: (path: string) => Uint8Array | undefined
19
+
20
+ // SedDocument API.
21
+
22
+ sedDocumentCreate: (path: string) => void
23
+ sedDocumentInstantiate: (path: string) => void
24
+ sedDocumentIssues: (path: string) => IIssue[]
25
+ sedDocumentModelCount: (path: string) => number
26
+ sedDocumentModelAddChange: (
27
+ path: string,
28
+ index: number,
29
+ componentName: string,
30
+ variableName: string,
31
+ newValue: string
32
+ ) => void
33
+ sedDocumentModelRemoveAllChanges: (path: string, index: number) => void
34
+ sedDocumentSimulationCount: (path: string) => number
35
+ sedDocumentSimulationType: (path: string, index: number) => number
36
+ sedDocumentSimulationUniformTimeCourseInitialTime: (path: string, index: number) => number
37
+ sedDocumentSimulationUniformTimeCourseOutputStartTime: (path: string, index: number) => number
38
+ sedDocumentSimulationUniformTimeCourseSetOutputStartTime: (path: string, index: number, value: number) => void
39
+ sedDocumentSimulationUniformTimeCourseOutputEndTime: (path: string, index: number) => number
40
+ sedDocumentSimulationUniformTimeCourseSetOutputEndTime: (path: string, index: number, value: number) => void
41
+ sedDocumentSimulationUniformTimeCourseNumberOfSteps: (path: string, index: number) => number
42
+ sedDocumentSimulationUniformTimeCourseSetNumberOfSteps: (path: string, index: number, value: number) => void
43
+ sedDocumentSimulationOneStepStep: (path: string, index: number) => number
44
+
45
+ // SedInstance API.
46
+
47
+ sedInstanceIssues: (path: string) => IIssue[]
48
+ sedInstanceRun: (path: string) => number
49
+
50
+ // SedInstanceTask API.
51
+
52
+ sedInstanceTaskVoiName: (path: string, index: number) => string
53
+ sedInstanceTaskVoiUnit: (path: string, index: number) => string
54
+ sedInstanceTaskVoi: (path: string, index: number) => number[]
55
+ sedInstanceTaskStateCount: (path: string, index: number) => number
56
+ sedInstanceTaskStateName: (path: string, index: number, stateIndex: number) => string
57
+ sedInstanceTaskStateUnit: (path: string, index: number, stateIndex: number) => string
58
+ sedInstanceTaskState: (path: string, index: number, stateIndex: number) => number[]
59
+ sedInstanceTaskRateCount: (path: string, index: number) => number
60
+ sedInstanceTaskRateName: (path: string, index: number, rateIndex: number) => string
61
+ sedInstanceTaskRateUnit: (path: string, index: number, rateIndex: number) => string
62
+ sedInstanceTaskRate: (path: string, index: number, rateIndex: number) => number[]
63
+ sedInstanceTaskConstantCount: (path: string, index: number) => number
64
+ sedInstanceTaskConstantName: (path: string, index: number, constantIndex: number) => string
65
+ sedInstanceTaskConstantUnit: (path: string, index: number, constantIndex: number) => string
66
+ sedInstanceTaskConstant: (path: string, index: number, constantIndex: number) => number[]
67
+ sedInstanceTaskComputedConstantCount: (path: string, index: number) => number
68
+ sedInstanceTaskComputedConstantName: (path: string, index: number, computedConstantIndex: number) => string
69
+ sedInstanceTaskComputedConstantUnit: (path: string, index: number, computedConstantIndex: number) => string
70
+ sedInstanceTaskComputedConstant: (path: string, index: number, computedConstantIndex: number) => number[]
71
+ sedInstanceTaskAlgebraicCount: (path: string, index: number) => number
72
+ sedInstanceTaskAlgebraicName: (path: string, index: number, algebraicIndex: number) => string
73
+ sedInstanceTaskAlgebraicUnit: (path: string, index: number, algebraicIndex: number) => string
74
+ sedInstanceTaskAlgebraic: (path: string, index: number, algebraicIndex: number) => number[]
75
+
76
+ // Version API.
77
+
78
+ version: () => string
79
+ }
80
+
81
+ export interface IWasmLocApi {
82
+ // Memory management.
83
+
84
+ HEAPU8: Uint8Array
85
+ _malloc: (size: number) => number
86
+
87
+ // FileManager API.
88
+
89
+ FileManager: IWasmFileManager
90
+
91
+ // File API.
92
+
93
+ File: new (path: string) => IWasmFile
94
+
95
+ // SedDocument API
96
+
97
+ SedDocument: new (wasmFile: IWasmFile) => IWasmSedDocument
98
+ SedChangeAttribute: new (componentName: string, variableName: string, newValue: string) => IWasmSedChangeAttribute
99
+
100
+ // Version API.
101
+
102
+ versionString: () => string
103
+ }
104
+
105
+ // Retrieve the version of libOpenCOR that is to be used. Two options:
106
+ // - OpenCOR: libOpenCOR can be accessed using window.locApi, which references our C++ API.
107
+ // - OpenCOR's Web app: libOpenCOR can be accessed using our WebAssembly module.
108
+
109
+ export let _cppLocApi = {} as ICppLocApi
110
+ export let _wasmLocApi = {} as IWasmLocApi
111
+
112
+ export async function initialiseLocApi() {
113
+ // @ts-expect-error (window.locApi may or may not be defined which is why we test it)
114
+ if (window.locApi !== undefined) {
115
+ // We are running OpenCOR, so libOpenCOR can be accessed using window.locApi.
116
+
117
+ // @ts-expect-error (window.locApi is defined)
118
+ _cppLocApi = window.locApi
119
+ } else {
120
+ // We are running OpenCOR's Web app, so we must import libOpenCOR's WebAssembly module.
121
+
122
+ try {
123
+ const libOpenCOR = (
124
+ await import(/* @vite-ignore */ proxyUrl('https://opencor.ws/libopencor/downloads/0.20250805.0/libopencor.js'))
125
+ ).default
126
+
127
+ _wasmLocApi = (await libOpenCOR()) as IWasmLocApi
128
+ } catch (error) {
129
+ console.error("Failed to load libOpenCOR's WebAssembly module:", error)
130
+ }
131
+ }
132
+ }
133
+
134
+ // Logger API.
135
+
136
+ export { EIssueType, wasmIssuesToIssues, type IIssue, type IWasmIssues } from './locLoggerApi.js'
137
+
138
+ // File API.
139
+
140
+ export { EFileType, File, fileManager, type IWasmFile } from './locFileApi.js'
141
+
142
+ // SED-ML API.
143
+
144
+ export {
145
+ ESedSimulationType,
146
+ SedDocument,
147
+ SedInstance,
148
+ SedInstanceTask,
149
+ SedSimulationUniformTimeCourse
150
+ } from './locSedApi.js'
151
+
152
+ // UI JSON API.
153
+
154
+ export {
155
+ type IUiJson,
156
+ type IUiJsonDiscreteInputPossibleValue,
157
+ type IUiJsonInput,
158
+ type IUiJsonOutput,
159
+ type IUiJsonOutputData,
160
+ type IUiJsonOutputPlot,
161
+ type IUiJsonParameter,
162
+ uiJsonIssues
163
+ } from './locUiJsonApi.js'
164
+
165
+ // Version API.
166
+
167
+ export { cppVersion, wasmVersion, version } from './locVersionApi.js'
@@ -0,0 +1,263 @@
1
+ import * as vue from 'vue'
2
+
3
+ import {
4
+ _cppLocApi,
5
+ _wasmLocApi,
6
+ cppVersion,
7
+ EIssueType,
8
+ ESedSimulationType,
9
+ SedDocument,
10
+ SedInstance,
11
+ SedSimulationUniformTimeCourse,
12
+ wasmIssuesToIssues,
13
+ type IIssue,
14
+ type IUiJson,
15
+ type IWasmIssues
16
+ } from './locApi.js'
17
+
18
+ // FileManager API.
19
+
20
+ interface IWasmFileManagerInstance {
21
+ files: {
22
+ size(): number
23
+ get(index: number): { fileName: string }
24
+ }
25
+ unmanage(file: unknown): void
26
+ }
27
+
28
+ export interface IWasmFileManager {
29
+ instance(): IWasmFileManagerInstance
30
+ }
31
+
32
+ class FileManager {
33
+ private _fileManager: IWasmFileManagerInstance | undefined = undefined
34
+
35
+ private fileManager() {
36
+ if (this._fileManager === undefined && !cppVersion()) {
37
+ this._fileManager = _wasmLocApi.FileManager.instance()
38
+ }
39
+
40
+ return this._fileManager
41
+ }
42
+
43
+ unmanage(path: string): void {
44
+ if (cppVersion()) {
45
+ _cppLocApi.fileManagerUnmanage(path)
46
+ } else {
47
+ const fileManager = this.fileManager()
48
+
49
+ if (fileManager !== undefined) {
50
+ const files = fileManager.files
51
+
52
+ for (let i = 0; i < files.size(); ++i) {
53
+ const file = files.get(i)
54
+
55
+ if (file.fileName === path) {
56
+ fileManager.unmanage(file)
57
+
58
+ break
59
+ }
60
+ }
61
+ }
62
+ }
63
+ }
64
+ }
65
+
66
+ export const fileManager = new FileManager()
67
+
68
+ // File API.
69
+
70
+ export enum EFileType {
71
+ UNKNOWN_FILE,
72
+ CELLML_FILE,
73
+ SEDML_FILE,
74
+ COMBINE_ARCHIVE,
75
+ IRRETRIEVABLE_FILE
76
+ }
77
+
78
+ export interface IWasmFile {
79
+ type: { value: EFileType }
80
+ issues: IWasmIssues
81
+ contents(): Uint8Array
82
+ setContents(ptr: number, length: number): void
83
+ childFileFromFileName(fileName: string): File | null
84
+ }
85
+
86
+ export class File {
87
+ private _path: string
88
+ private _wasmFile: IWasmFile = {} as IWasmFile
89
+ private _document: SedDocument = {} as SedDocument
90
+ private _instance: SedInstance = {} as SedInstance
91
+ private _issues: IIssue[] = []
92
+
93
+ constructor(path: string, contents: Uint8Array | undefined = undefined) {
94
+ this._path = path
95
+
96
+ if (cppVersion()) {
97
+ _cppLocApi.fileCreate(path, contents)
98
+
99
+ this._issues = _cppLocApi.fileIssues(path)
100
+ } else if (contents !== undefined) {
101
+ this._wasmFile = new _wasmLocApi.File(path)
102
+
103
+ const heapContentsPtr = _wasmLocApi._malloc(contents.length)
104
+ const heapContents = new Uint8Array(_wasmLocApi.HEAPU8.buffer, heapContentsPtr, contents.length)
105
+
106
+ heapContents.set(contents)
107
+
108
+ this._wasmFile.setContents(heapContentsPtr, contents.length)
109
+
110
+ this._issues = wasmIssuesToIssues(this._wasmFile.issues)
111
+ } else {
112
+ // Note: we should never reach this point since we should always provide some file contents when using the WASM
113
+ // version of libOpenCOR.
114
+
115
+ console.error(`No contents provided for file '${path}'.`)
116
+
117
+ return
118
+ }
119
+
120
+ if (this._issues.length !== 0) {
121
+ return
122
+ }
123
+
124
+ // Retrieve the SED-ML file associated with this file.
125
+
126
+ this._document = vue.markRaw(new SedDocument(this._path, this._wasmFile))
127
+ this._issues = this._document.issues()
128
+
129
+ if (this._issues.length !== 0) {
130
+ return
131
+ }
132
+
133
+ //---OPENCOR---
134
+ // We only support a limited subset of SED-ML for now, so we need to check a few more things. Might wnat to check
135
+ // https://github.com/opencor/opencor/blob/master/src/plugins/support/SEDMLSupport/src/sedmlfile.cpp#L579-L1492.
136
+
137
+ // Make sure that there is only one model.
138
+
139
+ const modelCount = this._document.modelCount()
140
+
141
+ if (modelCount !== 1) {
142
+ this._issues.push({
143
+ type: EIssueType.WARNING,
144
+ description: 'Only SED-ML files with one model are currently supported.'
145
+ })
146
+
147
+ return
148
+ }
149
+
150
+ // Make sure that the SED-ML file has only one simulation.
151
+
152
+ const simulationCount = this._document.simulationCount()
153
+
154
+ if (simulationCount !== 1) {
155
+ this._issues.push({
156
+ type: EIssueType.WARNING,
157
+ description: 'Only SED-ML files with one simulation are currently supported.'
158
+ })
159
+
160
+ return
161
+ }
162
+
163
+ // Make sure that the simulation is a uniform time course simulation.
164
+
165
+ const simulation = this._document.simulation(0) as SedSimulationUniformTimeCourse
166
+
167
+ if (simulation.type() !== ESedSimulationType.UNIFORM_TIME_COURSE) {
168
+ this._issues.push({
169
+ type: EIssueType.WARNING,
170
+ description: 'Only uniform time course simulations are currently supported.'
171
+ })
172
+
173
+ return
174
+ }
175
+
176
+ // Make sure that the initial time and output start time are the same, that the output start time and output end
177
+ // time are different, and that the number of steps is greater than zero.
178
+
179
+ const initialTime = simulation.initialTime()
180
+ const outputStartTime = simulation.outputStartTime()
181
+ const outputEndTime = simulation.outputEndTime()
182
+ const numberOfSteps = simulation.numberOfSteps()
183
+
184
+ if (initialTime !== outputStartTime) {
185
+ this._issues.push({
186
+ type: EIssueType.WARNING,
187
+ description: `Only uniform time course simulations with the same values for 'initialTime' (${String(initialTime)}) and 'outputStartTime' (${String(outputStartTime)}) are currently supported.`
188
+ })
189
+ }
190
+
191
+ if (outputStartTime === outputEndTime) {
192
+ this._issues.push({
193
+ type: EIssueType.ERROR,
194
+ description: `The uniform time course simulation must have different values for 'outputStartTime' (${String(outputStartTime)}) and 'outputEndTime' (${String(outputEndTime)}).`
195
+ })
196
+ }
197
+
198
+ if (numberOfSteps <= 0) {
199
+ this._issues.push({
200
+ type: EIssueType.ERROR,
201
+ description: `The uniform time course simulation must have a positive value for 'numberOfSteps' (${String(numberOfSteps)}).`
202
+ })
203
+ }
204
+
205
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
206
+ if (this._issues.length !== 0) {
207
+ return
208
+ }
209
+
210
+ // Retrieve an instance of the model.
211
+
212
+ this._instance = this._document.instantiate()
213
+ this._issues = this._instance.issues()
214
+ }
215
+
216
+ type(): EFileType {
217
+ return cppVersion() ? _cppLocApi.fileType(this._path) : this._wasmFile.type.value
218
+ }
219
+
220
+ path(): string {
221
+ return this._path
222
+ }
223
+
224
+ issues(): IIssue[] {
225
+ return this._issues
226
+ }
227
+
228
+ contents(): Uint8Array {
229
+ return cppVersion() ? _cppLocApi.fileContents(this._path) : this._wasmFile.contents()
230
+ }
231
+
232
+ document(): SedDocument {
233
+ return this._document
234
+ }
235
+
236
+ instance(): SedInstance {
237
+ return this._instance
238
+ }
239
+
240
+ uiJson(): IUiJson | undefined {
241
+ let uiJsonContents: Uint8Array | undefined
242
+
243
+ if (cppVersion()) {
244
+ uiJsonContents = _cppLocApi.fileUiJson(this._path)
245
+
246
+ if (uiJsonContents === undefined) {
247
+ return undefined
248
+ }
249
+ } else {
250
+ const uiJson = this._wasmFile.childFileFromFileName('simulation.json')
251
+
252
+ if (uiJson === null) {
253
+ return undefined
254
+ }
255
+
256
+ uiJsonContents = uiJson.contents()
257
+ }
258
+
259
+ const decoder = new TextDecoder()
260
+
261
+ return JSON.parse(decoder.decode(uiJsonContents))
262
+ }
263
+ }