Skip to content

Commit c48cab1

Browse files
committed
much faster and more efficient streamlined diagram and msl loading...
1 parent 13743fc commit c48cab1

7 files changed

Lines changed: 516 additions & 17 deletions

File tree

packages/shared/modelica/ModelicaEditor.vue

Lines changed: 183 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,7 @@ const {
10341034
mslDownloadUrl,
10351035
standardMslLoaded,
10361036
loadedLibraryCachePaths,
1037+
latestLazyLibraryClassTree,
10371038
downloadMslZipToOpfs,
10381039
loadCachedMslZipFromOpfs,
10391040
loadStandardMslZipFromOpfs,
@@ -1061,6 +1062,44 @@ const globalProcessBusy = computed(() => workerBusy.value || Boolean(libraryBusy
10611062
const globalProcessLabel = computed(
10621063
() => libraryBusyLabel.value || workerBusyLabel.value || 'Processing',
10631064
)
1065+
const lastAppliedLazyLibraryTreeSignature = ref('')
1066+
1067+
function applyLazyLibraryTreeToEditor(): boolean {
1068+
if (latestLazyLibraryClassTree.value.length === 0) return false
1069+
const treeSignature = latestLazyLibraryClassTree.value
1070+
.map((node) => String(node.qualified_name || node.name || ''))
1071+
.join('|')
1072+
if (
1073+
treeSignature &&
1074+
treeSignature === lastAppliedLazyLibraryTreeSignature.value &&
1075+
libraryTreeNodes.value.length > 0
1076+
) {
1077+
return true
1078+
}
1079+
const mappedNodes = mapRumocaClassTree(latestLazyLibraryClassTree.value)
1080+
lastAppliedLazyLibraryTreeSignature.value = treeSignature
1081+
libraryTreeNodes.value = mappedNodes
1082+
standardMslLoaded.value = mappedNodes.some((node) => node.qualifiedName === 'Modelica')
1083+
appendModelicaLog({
1084+
level: 'success',
1085+
phase: 'general',
1086+
message: `Loaded Modelica library tree from lazy index: ${mappedNodes.length} root nodes`,
1087+
details: {
1088+
lazyRootNodeCount: latestLazyLibraryClassTree.value.length,
1089+
rootNodeCount: mappedNodes.length,
1090+
standardMslLoaded: standardMslLoaded.value,
1091+
},
1092+
})
1093+
return true
1094+
}
1095+
1096+
watch(
1097+
latestLazyLibraryClassTree,
1098+
() => {
1099+
applyLazyLibraryTreeToEditor()
1100+
},
1101+
{ flush: 'sync' },
1102+
)
10641103
10651104
const fileNameFromPath = (path: string): string => {
10661105
const normalized = String(path || '')
@@ -1756,6 +1795,19 @@ const phaseForWorkerRequestType = (type: string): WorkerLiveLogEntry['phase'] =>
17561795
return 'general'
17571796
}
17581797
1798+
const shouldLogWorkerCompletion = (event: ModelicaWorkerActivityEvent): boolean => {
1799+
if (event.status === 'failed') return true
1800+
if ((event.elapsedMs ?? 0) >= 1000) return true
1801+
return [
1802+
'extract_diagram',
1803+
'materialize_diagram_classes',
1804+
'load_msl_zip',
1805+
'merge_msl_zip',
1806+
'restore_source_root_binary_cache',
1807+
'list_classes',
1808+
].includes(event.requestType)
1809+
}
1810+
17591811
function onWorkerActivity(event: ModelicaWorkerActivityEvent) {
17601812
if (event.requestType === 'lsp_completion_with_timing') return
17611813
if (event.status === 'started') {
@@ -1782,6 +1834,21 @@ function onWorkerActivity(event: ModelicaWorkerActivityEvent) {
17821834
workerLiveEntries.value = workerLiveEntries.value.filter(
17831835
(entry) => entry.requestId !== event.requestId,
17841836
)
1837+
if (shouldLogWorkerCompletion(event)) {
1838+
appendModelicaLog({
1839+
level: event.status === 'failed' ? 'error' : 'info',
1840+
phase: phaseForWorkerRequestType(event.requestType),
1841+
message:
1842+
event.status === 'failed'
1843+
? `${event.label} failed after ${event.elapsedMs ?? 0} ms: ${event.error ?? 'unknown error'}`
1844+
: `${event.label} finished in ${event.elapsedMs ?? 0} ms`,
1845+
details: {
1846+
requestId: event.requestId,
1847+
requestType: event.requestType,
1848+
elapsedMs: event.elapsedMs,
1849+
},
1850+
})
1851+
}
17851852
}
17861853
17871854
onMounted(() => {
@@ -3077,23 +3144,49 @@ const clearAll = () => {
30773144
}
30783145
30793146
async function refreshLibraryTree() {
3147+
if (applyLazyLibraryTreeToEditor()) return
30803148
const worker = modelicaWorker.value
30813149
if (!worker) {
30823150
libraryTreeNodes.value = []
30833151
standardMslLoaded.value = false
30843152
return
30853153
}
3154+
const startedAt = performance.now()
30863155
try {
3156+
appendModelicaLog({
3157+
level: 'info',
3158+
phase: 'general',
3159+
message: 'Refreshing Modelica library tree',
3160+
details: {
3161+
existingRootNodeCount: libraryTreeNodes.value.length,
3162+
},
3163+
})
30873164
const raw = await worker.listClasses()
3165+
const listClassesMs = Math.round(performance.now() - startedAt)
3166+
const mapStartedAt = performance.now()
30883167
const mappedNodes = mapRumocaClassTree(raw.classes)
3168+
const mapTreeMs = Math.round(performance.now() - mapStartedAt)
3169+
const rawClassCount = Array.isArray(raw.classes) ? raw.classes.length : 0
30893170
libraryTreeNodes.value = mappedNodes
30903171
standardMslLoaded.value = mappedNodes.some((node) => node.qualifiedName === 'Modelica')
3172+
appendModelicaLog({
3173+
level: 'success',
3174+
phase: 'general',
3175+
message: `Refreshed Modelica library tree in ${Math.round(performance.now() - startedAt)} ms: ${mappedNodes.length} root nodes`,
3176+
details: {
3177+
listClassesMs,
3178+
mapTreeMs,
3179+
rawClassCount,
3180+
rootNodeCount: mappedNodes.length,
3181+
standardMslLoaded: standardMslLoaded.value,
3182+
},
3183+
})
30913184
} catch (error) {
30923185
standardMslLoaded.value = false
30933186
appendModelicaLog({
30943187
level: 'warning',
30953188
phase: 'general',
3096-
message: `Failed to refresh library tree: ${(error as Error).message}`,
3189+
message: `Failed to refresh library tree after ${Math.round(performance.now() - startedAt)} ms: ${(error as Error).message}`,
30973190
})
30983191
}
30993192
}
@@ -3698,6 +3791,12 @@ const stopExecution = () => {
36983791
}
36993792
37003793
onMounted(async () => {
3794+
const mountedStartedAt = performance.now()
3795+
appendModelicaLog({
3796+
level: 'info',
3797+
phase: 'general',
3798+
message: 'Modelica editor startup: begin state hydration',
3799+
})
37013800
// 1) Global state (not bound to a specific model)
37023801
// - layout
37033802
// - template editor state
@@ -3720,6 +3819,17 @@ onMounted(async () => {
37203819
showAllInPrompt,
37213820
currentProjectId,
37223821
})
3822+
appendModelicaLog({
3823+
level: 'info',
3824+
phase: 'general',
3825+
message: `Modelica editor startup: global state hydrated in ${Math.round(performance.now() - mountedStartedAt)} ms`,
3826+
details: {
3827+
useModelicaStandardLibrary: useModelicaStandardLibrary.value,
3828+
requiredLibraryCount: requiredLibraries.value.length,
3829+
mslCachedZipPath: mslCachedZipPath.value,
3830+
standardMslCachedZipPath: standardMslCachedZipPath.value,
3831+
},
3832+
})
37233833
if (layoutContainsLegacyWorkbenchViews(initialLayout.value)) {
37243834
initialLayout.value = createDefaultLayout()
37253835
}
@@ -3731,6 +3841,15 @@ onMounted(async () => {
37313841
await syncStateWithOPFSFolder(`modelicaProject_${currentProjectId.value}`, {
37323842
projectFile,
37333843
})
3844+
appendModelicaLog({
3845+
level: 'info',
3846+
phase: 'general',
3847+
message: `Modelica editor startup: project state hydrated in ${Math.round(performance.now() - mountedStartedAt)} ms`,
3848+
details: {
3849+
currentProjectId: currentProjectId.value,
3850+
hasProjectFile: Boolean(projectFile.value),
3851+
},
3852+
})
37343853
37353854
// Hydrate project state into the editor
37363855
try {
@@ -3787,6 +3906,17 @@ onMounted(async () => {
37873906
37883907
// Initial project discovery
37893908
await refreshAvailableProjects()
3909+
appendModelicaLog({
3910+
level: 'info',
3911+
phase: 'general',
3912+
message: `Modelica editor startup: project discovery finished in ${Math.round(performance.now() - mountedStartedAt)} ms`,
3913+
details: {
3914+
availableProjectCount: availableProjectIds.value.length,
3915+
currentProjectId: currentProjectId.value,
3916+
useModelicaStandardLibrary: useModelicaStandardLibrary.value,
3917+
requiredLibraries: requiredLibraries.value,
3918+
},
3919+
})
37903920
// Keep projectFile updated when the editor changes (OPFS will persist it)
37913921
watchDebounced(
37923922
[
@@ -3831,11 +3961,23 @@ onMounted(async () => {
38313961
}
38323962
38333963
const initModelicaWorker = async (): Promise<ModelicaWorkerClient> => {
3964+
const startedAt = performance.now()
3965+
const requestedThreads = getRequestedWorkerThreads()
3966+
appendModelicaLog({
3967+
level: 'info',
3968+
phase: 'loadWasm',
3969+
message: `Initializing Modelica worker (requested threads=${requestedThreads})`,
3970+
details: {
3971+
crossOriginIsolated: globalThis.crossOriginIsolated === true,
3972+
hardwareConcurrency:
3973+
typeof navigator !== 'undefined' ? navigator.hardwareConcurrency : undefined,
3974+
},
3975+
})
38343976
const worker = new ModelicaWorkerClient()
38353977
modelicaWorker.value = worker
38363978
unsubscribeWorkerActivity?.()
38373979
unsubscribeWorkerActivity = worker.onActivity(onWorkerActivity)
3838-
const initInfo = await worker.init(getRequestedWorkerThreads())
3980+
const initInfo = await worker.init(requestedThreads)
38393981
configureModelicaLspExtensions(worker)
38403982
wasmLoaded.value = true
38413983
if (initInfo.version) rumocaWasmVersion.value = initInfo.version
@@ -3849,15 +3991,53 @@ onMounted(async () => {
38493991
rumocaSimulationModelDiscoveryAvailable.value = Boolean(
38503992
initInfo.simulationModelDiscoveryAvailable,
38513993
)
3994+
appendModelicaLog({
3995+
level: 'success',
3996+
phase: 'loadWasm',
3997+
message: `Initialized Modelica worker in ${Math.round(performance.now() - startedAt)} ms`,
3998+
details: {
3999+
requestedThreads,
4000+
rayonEnabled: initInfo.rayonEnabled,
4001+
version: initInfo.version,
4002+
gitCommit: initInfo.gitCommit,
4003+
simulationAvailable: initInfo.simulationAvailable,
4004+
simulationModelDiscoveryAvailable: initInfo.simulationModelDiscoveryAvailable,
4005+
},
4006+
})
38524007
return worker
38534008
}
38544009
38554010
// 3) Modelica worker
38564011
try {
4012+
const workerStartupStartedAt = performance.now()
38574013
const worker = await initModelicaWorker()
4014+
const documentCountStartedAt = performance.now()
38584015
const documentCount = await worker.getSourceRootDocumentCount()
4016+
appendModelicaLog({
4017+
level: 'info',
4018+
phase: 'general',
4019+
message: `Checked Modelica worker source-root document count in ${Math.round(performance.now() - documentCountStartedAt)} ms: ${documentCount}`,
4020+
details: {
4021+
documentCount,
4022+
useModelicaStandardLibrary: useModelicaStandardLibrary.value,
4023+
requiredLibraries: requiredLibraries.value,
4024+
},
4025+
})
38594026
const persistedLibraryLoadRequested =
38604027
Boolean(useModelicaStandardLibrary.value) || requiredLibraries.value.length > 0
4028+
appendModelicaLog({
4029+
level: 'info',
4030+
phase: 'general',
4031+
message: persistedLibraryLoadRequested
4032+
? 'Modelica editor startup: persisted library load requested'
4033+
: 'Modelica editor startup: no persisted library load requested',
4034+
details: {
4035+
documentCount,
4036+
useModelicaStandardLibrary: useModelicaStandardLibrary.value,
4037+
requiredLibraries: requiredLibraries.value,
4038+
mslCachedZipPath: mslCachedZipPath.value,
4039+
},
4040+
})
38614041
if (documentCount > 0 && persistedLibraryLoadRequested) {
38624042
mslLoaded.value = true
38634043
mslFileCount.value = documentCount
@@ -3893,7 +4073,7 @@ onMounted(async () => {
38934073
appendModelicaLog({
38944074
level: 'success',
38954075
phase: 'general',
3896-
message: 'Modelica worker loaded successfully! Ready to compile.',
4076+
message: `Modelica worker loaded successfully in ${Math.round(performance.now() - workerStartupStartedAt)} ms! Ready to compile.`,
38974077
})
38984078
} catch (error) {
38994079
appendModelicaLog({

packages/shared/modelica/components/ModelicaDiagramPane.vue

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,13 +713,18 @@ const loadDiagram = async () => {
713713
const preview = await props.extractor.extractPreview(request)
714714
if (token !== loadToken) return
715715
diagram.value = preview
716+
await nextTick()
716717
} catch (previewError) {
717718
console.info('[diagram][pane] preview unavailable', {
718719
qualifiedName: props.qualifiedName ?? 'unknown',
719720
error: previewError instanceof Error ? previewError.message : String(previewError),
720721
})
721722
}
722723
}
724+
if (props.extractor.materialize) {
725+
await props.extractor.materialize(request)
726+
if (token !== loadToken) return
727+
}
723728
diagram.value = await props.extractor.extract(request)
724729
if (token !== loadToken) return
725730
const components = Array.isArray(diagram.value?.components) ? diagram.value.components : []

packages/shared/modelica/diagram/rumocaModelicaDiagramExtractor.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,9 @@ export const createRumocaModelicaDiagramExtractor = (
5252
const payload = await worker.extractDiagramPreview(toWorkerPayload(request))
5353
return ensureDiagramShape(payload)
5454
},
55+
materialize: async (request: DiagramExtractRequest): Promise<void> => {
56+
const worker = getWorker()
57+
if (!worker) throw new Error('Modelica worker not loaded')
58+
await worker.materializeDiagramClasses(toWorkerPayload(request))
59+
},
5560
})

packages/shared/modelica/diagram/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,4 +127,5 @@ export type DiagramExtractRequest = {
127127
export interface ModelicaDiagramExtractor {
128128
extract: (request: DiagramExtractRequest) => Promise<ModelicaDiagramDto>
129129
extractPreview?: (request: DiagramExtractRequest) => Promise<ModelicaDiagramDto>
130+
materialize?: (request: DiagramExtractRequest) => Promise<void>
130131
}

0 commit comments

Comments
 (0)