Skip to content
Merged
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
76 changes: 63 additions & 13 deletions scripts/extract-blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

try:
from docutils.core import publish_parts

HAS_DOCUTILS = True
except ImportError:
HAS_DOCUTILS = False
Expand All @@ -30,6 +31,7 @@
from pathsim.blocks import *
from pathsim.subsystem import Subsystem, Interface

from pathsim_chem.tritium import *

# Block configuration - defines which blocks to extract and their categories
BLOCK_CONFIG = {
Expand Down Expand Up @@ -100,6 +102,12 @@
"Spectrum",
],
# Subsystem blocks are manually defined in subsystem.ts but we still extract their docstrings
"Chemical": [
"Process",
"Bubbler4",
"Splitter",
"GLC",
],
}

# Extra blocks to extract docstrings from, but not auto-register
Expand All @@ -121,7 +129,6 @@
"PinkNoise": {"maxInputs": 0, "maxOutputs": 1},
"ChirpPhaseNoiseSource": {"maxInputs": 0, "maxOutputs": 1},
"RandomNumberGenerator": {"maxInputs": 0, "maxOutputs": 1},

# Dynamic - vector passthrough (unlimited) unless specified
"Integrator": {"maxInputs": None, "maxOutputs": None},
"Differentiator": {"maxInputs": None, "maxOutputs": None},
Expand All @@ -137,7 +144,6 @@
"ButterworthHighpassFilter": {"maxInputs": None, "maxOutputs": None},
"ButterworthBandpassFilter": {"maxInputs": None, "maxOutputs": None},
"ButterworthBandstopFilter": {"maxInputs": None, "maxOutputs": None},

# Algebraic
"Adder": {"maxInputs": None, "maxOutputs": 1},
"Multiplier": {"maxInputs": None, "maxOutputs": 1},
Expand All @@ -158,7 +164,6 @@
"Switch": {"maxInputs": None, "maxOutputs": 1},
"LUT": {"maxInputs": None, "maxOutputs": None},
"LUT1D": {"maxInputs": 1, "maxOutputs": 1},

# Mixed
"SampleHold": {},
"FIR": {},
Expand All @@ -168,16 +173,32 @@
"CounterUp": {"maxInputs": 1, "maxOutputs": 1},
"CounterDown": {"maxInputs": 1, "maxOutputs": 1},
"Relay": {"maxInputs": 1, "maxOutputs": 1},

# Recording - unlimited inputs, no outputs
"Scope": {"maxInputs": None, "maxOutputs": 0},
"Spectrum": {"maxInputs": None, "maxOutputs": 0},

"Splitter": {"maxInputs": 1, "maxOutputs": None},
}

# Parameter overrides - PathSim handles all validation at runtime
PARAM_OVERRIDES: dict[str, dict] = {}

# Input/output mapping overrides if can't be extracted automatically
IO_OVERRIDES: dict[str, dict] = {
"GLC": {
"inputs": ["c_T_in", "flow_l", "y_T2_inlet", "flow_g"],
"outputs": [
"c_T_out",
"y_T2_out",
"eff",
"P_out",
"Q_l",
"Q_g_out",
"n_T_out_liquid",
"n_T_out_gas",
],
}
}


def rst_to_html(rst_text: str) -> str:
"""Convert RST docstring to HTML, preserving LaTeX math for KaTeX rendering."""
Expand All @@ -195,7 +216,7 @@ def rst_to_html(rst_text: str) -> str:
# Use MathJax output mode to preserve LaTeX in <span class="math">
# This allows client-side KaTeX rendering
"math_output": "MathJax",
}
},
)
return parts["body"]
except Exception as e:
Expand Down Expand Up @@ -299,7 +320,9 @@ def extract_block(block_name: str) -> dict | None:
if name == "self":
continue

default = None if param.default is inspect.Parameter.empty else param.default
default = (
None if param.default is inspect.Parameter.empty else param.default
)
param_type = infer_param_type(default)

param_info = {
Expand All @@ -319,8 +342,16 @@ def extract_block(block_name: str) -> dict | None:
# Some blocks need arguments, try with defaults first
instance = cls()
# Register stores mapping in _mapping (private attribute)
inputs = list(getattr(instance.inputs, "_mapping", {}).keys()) if hasattr(instance, "inputs") else []
outputs = list(getattr(instance.outputs, "_mapping", {}).keys()) if hasattr(instance, "outputs") else []
inputs = (
list(getattr(instance.inputs, "_mapping", {}).keys())
if hasattr(instance, "inputs")
else []
)
outputs = (
list(getattr(instance.outputs, "_mapping", {}).keys())
if hasattr(instance, "outputs")
else []
)
except Exception:
# Fallback: just use empty ports, UI overrides will provide defaults
inputs = []
Expand All @@ -340,7 +371,9 @@ def extract_block(block_name: str) -> dict | None:
return None


def generate_typescript(blocks: dict[str, dict], config: dict[str, list[str]], overrides: dict) -> str:
def generate_typescript(
blocks: dict[str, dict], config: dict[str, list[str]], overrides: dict
) -> str:
"""Generate TypeScript file content."""
lines = [
"// Auto-generated by scripts/extract-blocks.py - DO NOT EDIT",
Expand Down Expand Up @@ -384,10 +417,12 @@ def generate_typescript(blocks: dict[str, dict], config: dict[str, list[str]], o
lines.append("")

# Add block config
lines.append("export const blockConfig: Record<Exclude<NodeCategory, 'Subsystem'>, string[]> = {")
lines.append(
"export const blockConfig: Record<Exclude<NodeCategory, 'Subsystem'>, string[]> = {"
)
for category, block_names in config.items():
names_str = ", ".join(f'"{name}"' for name in block_names)
lines.append(f' {category}: [{names_str}],')
lines.append(f" {category}: [{names_str}],")
lines.append("};")
lines.append("")

Expand Down Expand Up @@ -419,13 +454,28 @@ def main():
if block_data:
extracted_blocks[block_name] = block_data

# Override inputs/outputs from IO_OVERRIDES
for block_name, io_data in IO_OVERRIDES.items():
if block_name in extracted_blocks:
if "inputs" in io_data:
extracted_blocks[block_name]["inputs"] = io_data["inputs"]
if "outputs" in io_data:
extracted_blocks[block_name]["outputs"] = io_data["outputs"]

print(f"Extracted {len(extracted_blocks)} blocks")

# Generate TypeScript
ts_content = generate_typescript(extracted_blocks, BLOCK_CONFIG, UI_OVERRIDES)

# Write output file
output_path = Path(__file__).parent.parent / "src" / "lib" / "nodes" / "generated" / "blocks.ts"
output_path = (
Path(__file__).parent.parent
/ "src"
/ "lib"
/ "nodes"
/ "generated"
/ "blocks.ts"
)
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_text(ts_content, encoding="utf-8")

Expand Down
1 change: 1 addition & 0 deletions src/lib/constants/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const PROGRESS_MESSAGES = {
LOADING_PYODIDE: 'Loading Pyodide...',
INSTALLING_DEPS: 'Installing NumPy and SciPy...',
INSTALLING_PATHSIM: 'Installing PathSim...',
INSTALLING_PATHSIM_CHEM: 'Installing PathSim-Chem...',
STARTING_WORKER: 'Starting worker...',
STARTING_SIMULATION: 'Starting simulation...'
} as const;
Expand Down
Loading