Commit be696eb6 authored by fenghen777's avatar fenghen777

feat: line drawing tool, package import for .mo export, simplify paramMap

parent 7dcaa541
......@@ -91,33 +91,17 @@ export default function ComponentEditor() {
}
}, []);
// 快捷键:空格旋转 / ESC退回选择 / Ctrl+Z撤销
useEffect(() => {
function handleKeyDown(e) {
// Ctrl+Z 撤销
if ((e.ctrlKey || e.metaKey) && e.key === 'z') {
e.preventDefault();
undo();
return;
}
// ESC 退回选择工具
if (e.key === 'Escape') {
setTool(TOOLS.SELECT);
setSelectedShapeIndex(-1);
return;
}
if (e.code === 'Space' && selectedShapeIndex >= 0 && editingTemplate) {
e.preventDefault();
// 旋转选中形状(90°)
const handleRotateShape = useCallback(() => {
if (selectedShapeIndex < 0 || !editingTemplate) return;
const shape = editingTemplate.shapes[selectedShapeIndex];
if (!shape) return;
// 根据形状类型计算旋转
const current = shape.props.transform || '';
const match = current.match(/rotate\(([^)]+)\)/);
const currentAngle = match ? parseFloat(match[1]) : 0;
const newAngle = (currentAngle + 90) % 360;
// 计算形状中心点
let cx, cy;
if (shape.type === 'rect') {
cx = shape.props.x + shape.props.width / 2;
......@@ -128,19 +112,42 @@ export default function ComponentEditor() {
} else if (shape.type === 'ellipse') {
cx = shape.props.cx;
cy = shape.props.cy;
} else if (shape.type === 'triangle' && shape.props.points) {
const pts = shape.props.points;
cx = (pts[0][0] + pts[1][0] + pts[2][0]) / 3;
cy = (pts[0][1] + pts[1][1] + pts[2][1]) / 3;
} else if (shape.type === 'line') {
cx = (shape.props.x1 + shape.props.x2) / 2;
cy = (shape.props.y1 + shape.props.y2) / 2;
} else {
return;
}
const transform = newAngle === 0 ? undefined : `rotate(${newAngle}, ${cx}, ${cy})`;
updateShape(selectedShapeIndex, {
props: { ...shape.props, transform },
});
updateShape(selectedShapeIndex, { props: { ...shape.props, transform } });
}, [selectedShapeIndex, editingTemplate, updateShape]);
// 快捷键:空格旋转 / ESC退回选择 / Ctrl+Z撤销
useEffect(() => {
function handleKeyDown(e) {
if ((e.ctrlKey || e.metaKey) && e.key === 'z') {
e.preventDefault();
undo();
return;
}
if (e.key === 'Escape') {
setTool(TOOLS.SELECT);
setSelectedShapeIndex(-1);
return;
}
if (e.code === 'Space' && selectedShapeIndex >= 0) {
e.preventDefault();
handleRotateShape();
}
}
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [selectedShapeIndex, editingTemplate, updateShape, undo]);
}, [selectedShapeIndex, handleRotateShape, undo]);
if (!editingTemplate) return null;
......@@ -190,6 +197,15 @@ export default function ComponentEditor() {
<span className={styles.toolIcon}>&#9673;</span>
<span className={styles.toolLabel}>端点</span>
</button>
<button
className={styles.toolBtn}
onClick={handleRotateShape}
disabled={selectedShapeIndex < 0}
title="旋转选中形状 90°(快捷键:空格)"
>
<span className={styles.toolIcon}>&#8635;</span>
<span className={styles.toolLabel}>旋转</span>
</button>
</div>
<div className={styles.toolSection}>
......@@ -218,6 +234,22 @@ export default function ComponentEditor() {
<span className={styles.toolIcon}>&#11053;</span>
<span className={styles.toolLabel}>椭圆</span>
</button>
<button
className={`${styles.toolBtn} ${tool === TOOLS.TRIANGLE ? styles.toolActive : ''}`}
onClick={() => setTool(TOOLS.TRIANGLE)}
title="三角形"
>
<span className={styles.toolIcon}>&#9651;</span>
<span className={styles.toolLabel}>三角形</span>
</button>
<button
className={`${styles.toolBtn} ${tool === TOOLS.LINE ? styles.toolActive : ''}`}
onClick={() => setTool(TOOLS.LINE)}
title="直线"
>
<span className={styles.toolIcon}>&#9585;</span>
<span className={styles.toolLabel}>直线</span>
</button>
</div>
{/* 常用模板 */}
......@@ -304,7 +336,7 @@ export default function ComponentEditor() {
<span className={styles.canvasHint}>
{tool === TOOLS.SELECT && '点击选择 | 拖拽框选 | 空格旋转 | 中键平移 | 滚轮缩放'}
{tool === TOOLS.PORT && '点击组件边缘添加端点'}
{(tool === TOOLS.RECT || tool === TOOLS.CIRCLE || tool === TOOLS.ELLIPSE) &&
{(tool === TOOLS.RECT || tool === TOOLS.CIRCLE || tool === TOOLS.ELLIPSE || tool === TOOLS.TRIANGLE || tool === TOOLS.LINE) &&
'拖拽绘制形状'}
</span>
</div>
......
......@@ -72,6 +72,14 @@ export default function NodePreview() {
case 'rect': return <rect key={i} {...props} style={s} />;
case 'circle': return <circle key={i} {...props} style={s} />;
case 'ellipse': return <ellipse key={i} {...props} style={s} />;
case 'triangle': {
const pts = props.points;
if (!pts || pts.length < 3) return null;
const pointsStr = pts.map(p => p.join(',')).join(' ');
return <polygon key={i} points={pointsStr} transform={props.transform} style={s} />;
}
case 'line':
return <line key={i} x1={props.x1} y1={props.y1} x2={props.x2} y2={props.y2} transform={props.transform} style={s} />;
default: return null;
}
})}
......
......@@ -90,6 +90,14 @@ function CustomDeviceNode({ data, selected }) {
case 'rect': return <rect key={i} {...props} style={s} />;
case 'circle': return <circle key={i} {...props} style={s} />;
case 'ellipse': return <ellipse key={i} {...props} style={s} />;
case 'triangle': {
const pts = props.points;
if (!pts || pts.length < 3) return null;
const pointsStr = pts.map(p => p.join(',')).join(' ');
return <polygon key={i} points={pointsStr} transform={props.transform} style={s} />;
}
case 'line':
return <line key={i} x1={props.x1} y1={props.y1} x2={props.x2} y2={props.y2} transform={props.transform} style={s} />;
default: return null;
}
})}
......
......@@ -26,6 +26,20 @@ export default function Sidebar() {
DEVICE_CATEGORIES.forEach(cat => { init[`__b_${cat.category}`] = true; });
return init;
});
// 包名覆盖(localStorage 持久化)
const [packageOverrides, setPackageOverrides] = useState(() => {
try { return JSON.parse(localStorage.getItem('eplan_package_overrides') || '{}'); } catch { return {}; }
});
const [editingPkgKey, setEditingPkgKey] = useState(null); // 当前正在编辑包名的 key
/** 保存包名 */
const savePkgName = useCallback((key, value, catObj) => {
const next = { ...packageOverrides, [key]: value };
setPackageOverrides(next);
localStorage.setItem('eplan_package_overrides', JSON.stringify(next));
if (catObj) catObj.packageName = value || undefined;
setEditingPkgKey(null);
}, [packageOverrides]);
const [sidebarWidth, setSidebarWidth] = useState(280);
const isDragging = useRef(false);
......@@ -351,6 +365,30 @@ export default function Sidebar() {
{isCollapsed ? '\u25B6' : '\u25BC'}
</span>
<span className={styles.categoryName}>{cat.category}</span>
{builtinEditMode && cat.packageName !== undefined && (
editingPkgKey === cat.category ? (
<input
className={styles.pkgInput}
autoFocus
defaultValue={packageOverrides[cat.category] ?? cat.packageName ?? ''}
placeholder="包名"
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => {
if (e.key === 'Enter') savePkgName(cat.category, e.target.value, cat);
if (e.key === 'Escape') setEditingPkgKey(null);
}}
onBlur={(e) => savePkgName(cat.category, e.target.value, cat)}
/>
) : (
<button
className={styles.pkgBtn}
onClick={(e) => { e.stopPropagation(); setEditingPkgKey(cat.category); }}
title={`包名: ${packageOverrides[cat.category] ?? cat.packageName ?? '未设置'}`}
>
{packageOverrides[cat.category] ?? cat.packageName ? '●' : '○'}
</button>
)
)}
<span className={styles.categoryCount}>{cat.items.length}</span>
</div>
{!isCollapsed && (
......@@ -450,6 +488,28 @@ export default function Sidebar() {
{isCollapsed ? '\u25B6' : '\u25BC'}
</span>
<span className={styles.categoryName}>{catName}</span>
{editingPkgKey === `__custom_${catName}` ? (
<input
className={styles.pkgInput}
autoFocus
defaultValue={packageOverrides[`__custom_${catName}`] || ''}
placeholder="包名"
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => {
if (e.key === 'Enter') savePkgName(`__custom_${catName}`, e.target.value);
if (e.key === 'Escape') setEditingPkgKey(null);
}}
onBlur={(e) => savePkgName(`__custom_${catName}`, e.target.value)}
/>
) : (
<button
className={styles.pkgBtn}
onClick={(e) => { e.stopPropagation(); setEditingPkgKey(`__custom_${catName}`); }}
title={`包名: ${packageOverrides[`__custom_${catName}`] || '未设置'}`}
>
{packageOverrides[`__custom_${catName}`] ? '●' : '○'}
</button>
)}
<span className={styles.categoryCount}>{catTemplates.length}</span>
{catName !== '未分类' && (
<button
......
......@@ -345,6 +345,44 @@
border-radius: 8px;
}
.pkgBtn {
width: 18px;
height: 18px;
border: none;
border-radius: 50%;
background: transparent;
color: #6366f1;
font-size: 10px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
transition: all 0.12s;
}
.pkgBtn:hover {
background: rgba(99, 102, 241, 0.2);
color: #818cf8;
}
.pkgInput {
width: 80px;
padding: 2px 6px;
border: 1px solid #6366f1;
border-radius: 4px;
background: #1a1a28;
color: #6366f1;
font-size: 10px;
font-family: 'Consolas', monospace;
outline: none;
flex-shrink: 0;
}
.pkgInput::placeholder {
color: #444;
}
/* ===== Tree hierarchy ===== */
.rootHeader {
padding: 8px 4px;
......
......@@ -67,7 +67,7 @@ export const DEVICE_CATEGORIES = [
],
},
{
code: 5007, type: 'logic_switch', label: 'Switch', color: '#7E57C2', icon: '⇋',
code: 5007, type: 'logic_switch', label: 'If', color: '#7E57C2', icon: '⇋',
width: 140, height: 100,
ports: [
{ id: 'p-switch', name: 'Switch', side: 'left', position: 0.2, type: 'signal', connector: 'switchCtrl' },
......@@ -124,6 +124,7 @@ export const DEVICE_CATEGORIES = [
},
{
category: '基本电子元件',
packageName: 'Electrical',
items: [
{
code: 1001, type: 'resistor', label: '电阻', color: '#FF9800', icon: 'R',
......@@ -134,19 +135,17 @@ export const DEVICE_CATEGORIES = [
],
params: [
{ key: 'R', label: '阻值', unit: 'Ω', defaultValue: '10k' },
{ key: 'P', label: '功率', unit: 'W', defaultValue: '0.25' },
],
},
{
code: 1002, type: 'capacitor', label: '电容', color: '#2196F3', icon: 'C',
width: 120, height: 60,
ports: [
{ id: 'p-pos', name: '+', side: 'left', position: 0.5, type: 'power', connector: 'p' },
{ id: 'p-neg', name: '-', side: 'right', position: 0.5, type: 'power', connector: 'n' },
{ id: 'p1', name: '1', side: 'left', position: 0.5, type: 'power', connector: 'p' },
{ id: 'p2', name: '2', side: 'right', position: 0.5, type: 'power', connector: 'n' },
],
params: [
{ key: 'C', label: '容值', unit: 'F', defaultValue: '100u' },
{ key: 'V', label: '耐压', unit: 'V', defaultValue: '50' },
],
},
{
......@@ -176,7 +175,7 @@ export const DEVICE_CATEGORIES = [
{ id: 'p-neg', name: 'V-', side: 'right', position: 0.5, type: 'power', connector: 'n' },
],
params: [
{ key: 'V', label: '电压', unit: 'V', defaultValue: '24' },
{ key: 'V0', label: '电压', unit: 'V', defaultValue: '24' },
],
},
{
......@@ -186,10 +185,22 @@ export const DEVICE_CATEGORIES = [
{ id: 'p-gnd', name: 'GND', side: 'left', position: 0.5, type: 'power', connector: 'p' },
],
},
{
code: 1007, type: 'ideal_switch', label: '开关', color: '#00BCD4', icon: 'SW',
width: 120, height: 60,
ports: [
{ id: 'p-in', name: 'IN', side: 'left', position: 0.5, type: 'power', connector: 'p' },
{ id: 'p-out', name: 'OUT', side: 'right', position: 0.5, type: 'power', connector: 'n' },
],
params: [
{ key: 'closed', label: '初始状态', unit: '', defaultValue: 'false' },
],
},
],
},
{
category: '电气控制',
packageName: 'ElectricalControl',
items: [
{
code: 100, type: 'terminal', label: '端子排', color: '#4CAF50', icon: '⊞',
......@@ -288,6 +299,7 @@ export const DEVICE_CATEGORIES = [
},
{
category: 'PLC',
packageName: 'PLC',
items: [
{
code: 2001, type: 'plc_cpu', label: 'PLC CPU', color: '#3F51B5', icon: 'CPU',
......@@ -349,6 +361,7 @@ export const DEVICE_CATEGORIES = [
},
{
category: '水利/液压',
packageName: 'Hydraulic',
items: [
{
code: 3001, type: 'pump', label: '水泵', color: '#00BCD4', icon: 'P',
......@@ -391,6 +404,7 @@ export const DEVICE_CATEGORIES = [
},
{
category: '机械/气动',
packageName: 'Mechanical',
items: [
{
code: 4001, type: 'cylinder', label: '气缸', color: '#8BC34A', icon: 'CY',
......
......@@ -9,178 +9,150 @@
* 模型映射表
* type : EPLAN 符号类型(constants.js 中的 type 字段)
* modelName : Modelica 模型名称(用于 .mo 声明)
* portMap : EPLAN 端口 id → Modelica 端口名
* paramMap : EPLAN 参数 key → Modelica 参数名
* portMap : EPLAN 端口 id -> Modelica 端口名(connector 字段优先)
*/
const MODEL_MAP = {
// ===== 基本电子元件 =====
resistor: {
modelName: 'Resistor',
portMap: { 'p1': 'p', 'p2': 'n' },
paramMap: { R: 'R', P: 'P_nominal' },
},
capacitor: {
modelName: 'Capacitor',
portMap: { 'p-pos': 'p', 'p-neg': 'n' },
paramMap: { C: 'C', V: 'V_nominal' },
portMap: { 'p1': 'p', 'p2': 'n' },
},
inductor: {
modelName: 'Inductor',
portMap: { 'p1': 'p', 'p2': 'n' },
paramMap: { L: 'L' },
},
diode: {
modelName: 'Diode',
portMap: { 'p-a': 'p', 'p-k': 'n' },
paramMap: {},
},
voltage_source: {
modelName: 'VoltageSource',
portMap: { 'p-pos': 'p', 'p-neg': 'n' },
paramMap: { V: 'V' },
},
ground: {
modelName: 'Ground',
portMap: { 'p-gnd': 'p' },
paramMap: {},
},
ideal_switch: {
modelName: 'IdealSwitch',
portMap: { 'p-in': 'p', 'p-out': 'n' },
},
// ===== 电气控制 =====
terminal: {
modelName: 'Terminal',
portMap: { 'p-in-1': 'p_in', 'p-out-1': 'p_out' },
paramMap: {},
},
contactor: {
modelName: 'Contactor',
portMap: { 'p-l1': 'L1', 'p-l2': 'L2', 'p-l3': 'L3', 'p-t1': 'T1', 'p-t2': 'T2', 'p-t3': 'T3', 'p-a1': 'A1', 'p-a2': 'A2' },
paramMap: {},
},
relay: {
modelName: 'Relay',
portMap: { 'p-a1': 'A1', 'p-a2': 'A2', 'p-11': 'p11', 'p-14': 'p14' },
paramMap: {},
},
breaker: {
modelName: 'CircuitBreaker',
portMap: { 'p-in': 'p_in', 'p-out': 'p_out' },
paramMap: { In: 'I_nominal' },
},
switch: {
modelName: 'Switch',
portMap: { 'p-com': 'COM', 'p-no': 'NO', 'p-nc': 'NC' },
paramMap: {},
},
motor: {
modelName: 'Motor',
portMap: { 'p-u': 'U', 'p-v': 'V', 'p-w': 'W', 'p-pe': 'PE' },
paramMap: { Pw: 'P_nominal', Rpm: 'n_nominal' },
},
transformer: {
modelName: 'Transformer',
portMap: { 'p-pri1': 'p1', 'p-pri2': 'n1', 'p-sec1': 'p2', 'p-sec2': 'n2' },
paramMap: {},
},
sensor: {
modelName: 'Sensor',
portMap: { 'p-vcc': 'VCC', 'p-gnd': 'GND', 'p-sig': 'SIG' },
paramMap: {},
},
cable: {
modelName: 'Cable',
portMap: { 'p-in': 'p_in', 'p-out': 'p_out' },
paramMap: {},
},
// ===== PLC =====
plc_cpu: {
modelName: 'PLC_CPU',
portMap: { 'p-24v': 'V24', 'p-0v': 'V0', 'p-di1': 'DI1', 'p-di2': 'DI2', 'p-di3': 'DI3', 'p-do1': 'DO1', 'p-do2': 'DO2', 'p-do3': 'DO3' },
paramMap: {},
},
plc_di: {
modelName: 'PLC_DI',
portMap: { 'p-di1': 'DI1', 'p-di2': 'DI2', 'p-di3': 'DI3', 'p-di4': 'DI4', 'p-com': 'COM' },
paramMap: {},
},
plc_do: {
modelName: 'PLC_DO',
portMap: { 'p-do1': 'DO1', 'p-do2': 'DO2', 'p-do3': 'DO3', 'p-do4': 'DO4', 'p-com': 'COM' },
paramMap: {},
},
plc_ai: {
modelName: 'PLC_AI',
portMap: { 'p-ai1': 'AI1', 'p-ai2': 'AI2', 'p-com': 'COM' },
paramMap: {},
},
plc_ao: {
modelName: 'PLC_AO',
portMap: { 'p-ao1': 'AO1', 'p-ao2': 'AO2', 'p-com': 'COM' },
paramMap: {},
},
// ===== 流程节点 =====
greater_than: {
modelName: 'GreaterThan', isFlowNode: true, operator: '>',
portMap: { 'p-a': 'a', 'p-b': 'b', 'p-out': 'out' },
paramMap: {},
},
less_than: {
modelName: 'LessThan', isFlowNode: true, operator: '<',
portMap: { 'p-a': 'a', 'p-b': 'b', 'p-out': 'out' },
paramMap: {},
},
equal: {
modelName: 'Equal', isFlowNode: true, operator: '==',
portMap: { 'p-a': 'a', 'p-b': 'b', 'p-out': 'out' },
paramMap: {},
},
logic_and: {
modelName: 'And', isFlowNode: true, operator: 'and',
portMap: { 'p-a': 'a', 'p-b': 'b', 'p-out': 'out' },
paramMap: {},
},
logic_or: {
modelName: 'Or', isFlowNode: true, operator: 'or',
portMap: { 'p-a': 'a', 'p-b': 'b', 'p-out': 'out' },
paramMap: {},
},
logic_not: {
modelName: 'Not', isFlowNode: true, operator: 'not',
portMap: { 'p-in': 'inVal', 'p-out': 'out' },
paramMap: {},
},
logic_switch: {
modelName: 'Switch', isFlowNode: true, operator: 'if',
portMap: { 'p-switch': 'switchCtrl', 'p-false': 'falseVal', 'p-true': 'trueVal', 'p-out': 'out' },
paramMap: {},
},
val_integer: {
modelName: 'IntegerValue', isFlowNode: true, valueType: 'Integer',
portMap: { 'p-out': 'out' },
paramMap: { value: 'value' },
},
val_real: {
modelName: 'RealValue', isFlowNode: true, valueType: 'Real',
portMap: { 'p-out': 'out' },
paramMap: { value: 'value' },
},
val_string: {
modelName: 'StringValue', isFlowNode: true, valueType: 'String',
portMap: { 'p-out': 'out' },
paramMap: { value: 'value' },
},
val_boolean: {
modelName: 'BooleanValue', isFlowNode: true, valueType: 'Boolean',
portMap: { 'p-out': 'out' },
paramMap: { value: 'value' },
},
};
/**
* 获取符号类型对应的模型映射
* @param {string} type - 符号类型
* @returns {Object|null} - { modelName, portMap, paramMap } 或 null
* @returns {Object|null} - { modelName, portMap } 或 null
*/
export function getModelMapping(type) {
return MODEL_MAP[type] || null;
......
......@@ -4,6 +4,7 @@
* 使用 modelMapping.js 中的映射表进行类型和端口转换。
*/
import { getModelMapping, resolvePortName } from './modelMapping';
import { DEVICE_CATEGORIES } from './constants';
// ===== 工程记号解析 =====
const SI_PREFIXES = {
......@@ -57,7 +58,8 @@ export function exportToModelica(data, modelName = 'Circuit') {
const warnings = [];
const errors = [];
const lines = [];
const instanceMap = {}; // nodeId → { instanceName, type, ports, isFlowNode }
const instanceMap = {}; // nodeId -> { instanceName, type, ports, isFlowNode }
const instanceCounter = {}; // baseName -> count
// 读取用户自定义的映射覆盖
let mappingOverrides = {};
......@@ -68,15 +70,35 @@ export function exportToModelica(data, modelName = 'Circuit') {
lines.push(`model ${toMoId(modelName)}`);
lines.push('');
// 构建 type → packageName 查找表
const typeToPackage = {};
DEVICE_CATEGORIES.forEach(cat => {
if (cat.packageName) {
cat.items.forEach(item => { typeToPackage[item.type] = cat.packageName; });
}
});
const usedPackages = new Set();
// ===== 1. 组件声明(跳过流程节点) =====
nodes.forEach((node, idx) => {
const td = node.data?.templateData;
const type = td?.type;
const label = node.data?.label || `comp_${idx}`;
const instanceName = toMoId(label) + '_' + (idx + 1);
const mapping = getModelMapping(type);
// 实例名:优先用 modelName 首字母小写,无映射则用 label 转换
let baseName;
if (mapping && mapping.modelName) {
baseName = mapping.modelName.charAt(0).toLowerCase() + mapping.modelName.slice(1);
} else {
baseName = toMoId(label);
}
// 按 baseName 分组计数,生成唯一序号
if (!instanceCounter[baseName]) instanceCounter[baseName] = 0;
instanceCounter[baseName]++;
const instanceName = baseName + '_' + instanceCounter[baseName];
instanceMap[node.id] = {
instanceName,
type,
......@@ -84,6 +106,9 @@ export function exportToModelica(data, modelName = 'Circuit') {
isFlowNode: mapping?.isFlowNode || false,
};
// 记录使用的包
if (typeToPackage[type]) usedPackages.add(typeToPackage[type]);
// 流程节点不生成组件声明
if (mapping?.isFlowNode) return;
......@@ -109,18 +134,18 @@ export function exportToModelica(data, modelName = 'Circuit') {
return;
}
// 有映射:使用模型名称声明
// 有映射:使用模型名称声明,参数直接用 constants.js 中的 key
const paramParts = [];
const pv = node.data?.paramValues || {};
Object.entries(mapping.paramMap).forEach(([eplanKey, moName]) => {
const rawVal = pv[eplanKey];
(td?.params || []).forEach(p => {
const rawVal = pv[p.key];
if (rawVal != null && rawVal !== '') {
const numVal = parseEngValue(rawVal);
if (numVal != null) {
paramParts.push(`${moName}=${formatMoValue(numVal)}`);
paramParts.push(`${p.key}=${formatMoValue(numVal)}`);
} else {
paramParts.push(`${moName}=${rawVal}`);
warnings.push(`"${label}".${eplanKey} = "${rawVal}" 无法解析为数值`);
paramParts.push(`${p.key}=${rawVal}`);
warnings.push(`"${label}".${p.key} = "${rawVal}" 无法解析为数值`);
}
}
});
......@@ -131,6 +156,13 @@ export function exportToModelica(data, modelName = 'Circuit') {
lines.push(` ${finalModelName} ${instanceName}${paramStr};`);
});
// 插入 import 语句(在 model 声明与组件声明之间)
if (usedPackages.size > 0) {
const importLines = [...usedPackages].sort().map(pkg => ` import ${pkg}.*;`);
// lines[0] = 'model XXX', lines[1] = '', 组件声明从 lines[2] 开始
lines.splice(2, 0, ...importLines, '');
}
lines.push('');
// ===== 2. 构建流程节点端口到连接源的映射 =====
......@@ -217,11 +249,7 @@ export function exportToModelica(data, modelName = 'Circuit') {
lines.push('');
// ===== 4. annotation =====
if (nodes.length > 0) {
lines.push(' annotation(Diagram(coordinateSystem(preserveAspectRatio=false,');
lines.push(' extent={{-200,-200},{800,800}})));');
}
lines.push(`end ${toMoId(modelName)};`);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment