Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions src/engine2/src/internal/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
readOutPtr,
allocOutUsize,
readOutUsize,
copyFromWasm,
} from './memory';
import { SimlinModelPtr, SimlinLinksPtr } from './types';
import {
Expand Down Expand Up @@ -282,3 +283,129 @@ export function simlin_model_get_incoming_links(model: SimlinModelPtr, varName:
free(outErrPtr);
}
}

/**
* Get all stocks in a model as JSON bytes.
* @param model Model pointer
* @returns JSON bytes (UTF-8 encoded array of stock objects)
*/
export function simlin_model_get_stocks_json(model: SimlinModelPtr): Uint8Array {
const exports = getExports();
const fn = exports.simlin_model_get_stocks_json as (
model: number,
outBuf: number,
outLen: number,
outErr: number,
) => void;

const outBufPtr = allocOutPtr();
const outLenPtr = allocOutUsize();
const outErrPtr = allocOutPtr();

try {
fn(model, outBufPtr, outLenPtr, outErrPtr);
const errPtr = readOutPtr(outErrPtr);

if (errPtr !== 0) {
const code = simlin_error_get_code(errPtr);
const message = simlin_error_get_message(errPtr) ?? 'Unknown error';
const details = readAllErrorDetails(errPtr);
simlin_error_free(errPtr);
throw new SimlinError(message, code, details);
}

const bufPtr = readOutPtr(outBufPtr);
const len = readOutUsize(outLenPtr);
const data = copyFromWasm(bufPtr, len);
free(bufPtr);
return data;
} finally {
free(outBufPtr);
free(outLenPtr);
free(outErrPtr);
}
}

/**
* Get all flows in a model as JSON bytes.
* @param model Model pointer
* @returns JSON bytes (UTF-8 encoded array of flow objects)
*/
export function simlin_model_get_flows_json(model: SimlinModelPtr): Uint8Array {
const exports = getExports();
const fn = exports.simlin_model_get_flows_json as (
model: number,
outBuf: number,
outLen: number,
outErr: number,
) => void;

const outBufPtr = allocOutPtr();
const outLenPtr = allocOutUsize();
const outErrPtr = allocOutPtr();

try {
fn(model, outBufPtr, outLenPtr, outErrPtr);
const errPtr = readOutPtr(outErrPtr);

if (errPtr !== 0) {
const code = simlin_error_get_code(errPtr);
const message = simlin_error_get_message(errPtr) ?? 'Unknown error';
const details = readAllErrorDetails(errPtr);
simlin_error_free(errPtr);
throw new SimlinError(message, code, details);
}

const bufPtr = readOutPtr(outBufPtr);
const len = readOutUsize(outLenPtr);
const data = copyFromWasm(bufPtr, len);
free(bufPtr);
return data;
} finally {
free(outBufPtr);
free(outLenPtr);
free(outErrPtr);
}
}

/**
* Get all auxiliaries in a model as JSON bytes.
* @param model Model pointer
* @returns JSON bytes (UTF-8 encoded array of auxiliary objects)
*/
export function simlin_model_get_auxs_json(model: SimlinModelPtr): Uint8Array {
const exports = getExports();
const fn = exports.simlin_model_get_auxs_json as (
model: number,
outBuf: number,
outLen: number,
outErr: number,
) => void;

const outBufPtr = allocOutPtr();
const outLenPtr = allocOutUsize();
const outErrPtr = allocOutPtr();

try {
fn(model, outBufPtr, outLenPtr, outErrPtr);
const errPtr = readOutPtr(outErrPtr);

if (errPtr !== 0) {
const code = simlin_error_get_code(errPtr);
const message = simlin_error_get_message(errPtr) ?? 'Unknown error';
const details = readAllErrorDetails(errPtr);
simlin_error_free(errPtr);
throw new SimlinError(message, code, details);
}

const bufPtr = readOutPtr(outBufPtr);
const len = readOutUsize(outLenPtr);
const data = copyFromWasm(bufPtr, len);
free(bufPtr);
return data;
} finally {
free(outBufPtr);
free(outLenPtr);
free(outErrPtr);
}
}
27 changes: 21 additions & 6 deletions src/engine2/src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import {
simlin_model_get_incoming_links,
simlin_model_get_links,
simlin_model_get_latex_equation,
simlin_model_get_stocks_json,
simlin_model_get_flows_json,
simlin_model_get_auxs_json,
} from './internal/model';
import { readLinks, simlin_free_links } from './internal/analysis';
import { SimlinModelPtr, SimlinLinkPolarity, Link as LowLevelLink } from './internal/types';
Expand Down Expand Up @@ -215,8 +218,12 @@ export class Model {
return this._cachedStocks;
}

const model = this.getModelJson();
this._cachedStocks = (model.stocks || []).map((s: JsonStock) => ({
// Use direct JSON API instead of serializing entire project
const jsonBytes = simlin_model_get_stocks_json(this._ptr);
const jsonStr = new TextDecoder().decode(jsonBytes);
const stocksJson: JsonStock[] = JSON.parse(jsonStr);

this._cachedStocks = stocksJson.map((s: JsonStock) => ({
type: 'stock' as const,
name: s.name,
initialEquation: this.extractEquation(s.initialEquation, s.arrayedEquation, 'initialEquation'),
Expand All @@ -240,8 +247,12 @@ export class Model {
return this._cachedFlows;
}

const model = this.getModelJson();
this._cachedFlows = (model.flows || []).map((f: JsonFlow) => {
// Use direct JSON API instead of serializing entire project
const jsonBytes = simlin_model_get_flows_json(this._ptr);
const jsonStr = new TextDecoder().decode(jsonBytes);
const flowsJson: JsonFlow[] = JSON.parse(jsonStr);

this._cachedFlows = flowsJson.map((f: JsonFlow) => {
let gf: GraphicalFunction | undefined;
if (f.graphicalFunction) {
gf = this.parseJsonGraphicalFunction(f.graphicalFunction);
Expand Down Expand Up @@ -271,8 +282,12 @@ export class Model {
return this._cachedAuxs;
}

const model = this.getModelJson();
this._cachedAuxs = (model.auxiliaries || []).map((a: JsonAuxiliary) => {
// Use direct JSON API instead of serializing entire project
const jsonBytes = simlin_model_get_auxs_json(this._ptr);
const jsonStr = new TextDecoder().decode(jsonBytes);
const auxsJson: JsonAuxiliary[] = JSON.parse(jsonStr);

this._cachedAuxs = auxsJson.map((a: JsonAuxiliary) => {
let gf: GraphicalFunction | undefined;
if (a.graphicalFunction) {
gf = this.parseJsonGraphicalFunction(a.graphicalFunction);
Expand Down
57 changes: 57 additions & 0 deletions src/libsimlin/simlin.h
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,63 @@ char *simlin_model_get_latex_equation(SimlinModel *model,
const char *ident,
SimlinError **out_error);

// Returns all stocks in a model as a JSON array.
//
// The returned JSON is an array of stock objects, each containing fields like
// `name`, `initialEquation`, `inflows`, `outflows`, `units`, etc.
//
// # Safety
// - `model` must be a valid pointer to a SimlinModel
// - `out_buffer` and `out_len` must be valid pointers where the serialized
// bytes and length will be written
// - `out_error` may be null or a valid pointer to receive error details
//
// # Ownership
// - The returned buffer is exclusively owned by the caller and MUST be freed with `simlin_free`.
// - The caller is responsible for freeing the buffer even if subsequent operations fail.
void simlin_model_get_stocks_json(SimlinModel *model,
uint8_t **out_buffer,
uintptr_t *out_len,
SimlinError **out_error);

// Returns all flows in a model as a JSON array.
//
// The returned JSON is an array of flow objects, each containing fields like
// `name`, `equation`, `units`, `nonNegative`, `graphicalFunction`, etc.
//
// # Safety
// - `model` must be a valid pointer to a SimlinModel
// - `out_buffer` and `out_len` must be valid pointers where the serialized
// bytes and length will be written
// - `out_error` may be null or a valid pointer to receive error details
//
// # Ownership
// - The returned buffer is exclusively owned by the caller and MUST be freed with `simlin_free`.
// - The caller is responsible for freeing the buffer even if subsequent operations fail.
void simlin_model_get_flows_json(SimlinModel *model,
uint8_t **out_buffer,
uintptr_t *out_len,
SimlinError **out_error);

// Returns all auxiliaries in a model as a JSON array.
//
// The returned JSON is an array of auxiliary objects, each containing fields like
// `name`, `equation`, `initialEquation`, `units`, `graphicalFunction`, etc.
//
// # Safety
// - `model` must be a valid pointer to a SimlinModel
// - `out_buffer` and `out_len` must be valid pointers where the serialized
// bytes and length will be written
// - `out_error` may be null or a valid pointer to receive error details
//
// # Ownership
// - The returned buffer is exclusively owned by the caller and MUST be freed with `simlin_free`.
// - The caller is responsible for freeing the buffer even if subsequent operations fail.
void simlin_model_get_auxs_json(SimlinModel *model,
uint8_t **out_buffer,
uintptr_t *out_len,
SimlinError **out_error);

// Creates a new simulation context
//
// # Safety
Expand Down
Loading
Loading