388 lines
11 KiB
Dart
388 lines
11 KiB
Dart
/**
|
||
* 网表生成器模块
|
||
*
|
||
* 从原理图连接关系提取电气网表
|
||
* 支持网络命名/自动命名
|
||
* 输出 SPICE 格式网表
|
||
*
|
||
* @version 0.1.0
|
||
* @date 2026-03-07
|
||
*/
|
||
|
||
import '../models/core_models.dart';
|
||
|
||
/// 网表生成器
|
||
class NetlistGenerator {
|
||
/// 生成 SPICE 格式网表
|
||
///
|
||
/// [design] 设计数据
|
||
/// [options] 生成选项
|
||
///
|
||
/// 返回 SPICE 网表字符串
|
||
String generateSpiceNetlist(Design design, {SpiceOptions? options}) {
|
||
options ??= const SpiceOptions();
|
||
|
||
final buffer = StringBuffer();
|
||
|
||
// 1. 文件头
|
||
buffer.writeln('* ${design.name}');
|
||
buffer.writeln('* Generated by Mobile EDA');
|
||
buffer.writeln('* Date: ${DateTime.now().toIso8601String()}');
|
||
buffer.writeln('* Version: ${design.version}');
|
||
buffer.writeln();
|
||
|
||
// 2. 包含模型文件(如果有)
|
||
if (options.includeModelFiles.isNotEmpty) {
|
||
for (final modelFile in options.includeModelFiles) {
|
||
buffer.writeln('.INCLUDE "$modelFile"');
|
||
}
|
||
buffer.writeln();
|
||
}
|
||
|
||
// 3. 生成元件实例
|
||
buffer.writeln('* Components');
|
||
for (final component in design.components.values) {
|
||
buffer.writeln(_generateSpiceComponent(component, design));
|
||
}
|
||
buffer.writeln();
|
||
|
||
// 4. 生成网络连接
|
||
buffer.writeln('* Nets');
|
||
for (final net in design.nets.values) {
|
||
buffer.writeln(_generateSpiceNet(net));
|
||
}
|
||
buffer.writeln();
|
||
|
||
// 5. 生成模型定义(如果有自定义模型)
|
||
if (options.generateModelDefinitions) {
|
||
buffer.writeln('* Models');
|
||
for (final component in design.components.values) {
|
||
final modelDef = _generateSpiceModel(component);
|
||
if (modelDef != null) {
|
||
buffer.writeln(modelDef);
|
||
}
|
||
}
|
||
buffer.writeln();
|
||
}
|
||
|
||
// 6. 分析命令
|
||
buffer.writeln('* Analysis');
|
||
if (options.analysisType == 'dc') {
|
||
buffer.writeln('.DC ${options.dcSourceName} ${options.dcStart} ${options.dcStop} ${options.dcStep}');
|
||
} else if (options.analysisType == 'ac') {
|
||
buffer.writeln('.AC ${options.acType} ${options.acPoints} ${options.acStartFreq} ${options.acStopFreq}');
|
||
} else if (options.analysisType == 'tran') {
|
||
buffer.writeln('.TRAN ${options.transientStep} ${options.transientStop}');
|
||
}
|
||
|
||
// 7. 输出控制
|
||
if (options.printVariables.isNotEmpty) {
|
||
buffer.writeln('.PRINT ${options.printVariables.join(" ")}');
|
||
}
|
||
|
||
// 8. 文件尾
|
||
buffer.writeln('.END');
|
||
|
||
return buffer.toString();
|
||
}
|
||
|
||
/// 生成 SPICE 元件行
|
||
String _generateSpiceComponent(Component component, Design design) {
|
||
final prefix = _getSpicePrefix(component.type);
|
||
final name = '$prefix${component.name}';
|
||
|
||
// 获取引脚连接
|
||
final connections = <String>[];
|
||
for (final pin in component.pins) {
|
||
// 查找引脚所属的网络
|
||
String netName = '0'; // 默认为地
|
||
for (final net in design.nets.values) {
|
||
for (final conn in net.connections) {
|
||
if (conn.componentId == component.id && conn.pinId == pin.pinId) {
|
||
netName = net.name;
|
||
break;
|
||
}
|
||
}
|
||
if (netName != '0') break;
|
||
}
|
||
connections.add(netName);
|
||
}
|
||
|
||
// 获取元件值
|
||
final value = component.value ?? _getDefaultValue(component.type);
|
||
|
||
// 构建 SPICE 行
|
||
// 格式:Xname node1 node2 ... model [value]
|
||
final parts = [name, ...connections];
|
||
|
||
if (component.type == ComponentType.ic || component.type == ComponentType.transistor) {
|
||
// IC 和晶体管需要模型名
|
||
final modelName = component.partNumber ?? component.name;
|
||
parts.add(modelName);
|
||
}
|
||
|
||
parts.add(value);
|
||
|
||
return parts.join(' ');
|
||
}
|
||
|
||
/// 生成 SPICE 网络定义
|
||
String _generateSpiceNet(Net net) {
|
||
// SPICE 中网络通过元件连接隐式定义,这里生成注释
|
||
final connections = <String>[];
|
||
for (final conn in net.connections) {
|
||
if (conn.componentId != null && conn.pinId != null) {
|
||
connections.add('${conn.componentId}:${conn.pinId}');
|
||
}
|
||
}
|
||
|
||
return '* Net ${net.name}: ${connections.join(", ")}';
|
||
}
|
||
|
||
/// 生成 SPICE 模型定义
|
||
String? _generateSpiceModel(Component component) {
|
||
if (component.type == ComponentType.resistor) {
|
||
return null; // 电阻不需要模型定义
|
||
} else if (component.type == ComponentType.capacitor) {
|
||
return null; // 电容不需要模型定义
|
||
} else if (component.type == ComponentType.inductor) {
|
||
return null; // 电感不需要模型定义
|
||
} else if (component.type == ComponentType.diode) {
|
||
return '.MODEL ${component.partNumber ?? component.name} D()';
|
||
} else if (component.type == ComponentType.transistor) {
|
||
// 简化处理,实际需要更多信息
|
||
return '.MODEL ${component.partNumber ?? component.name} NPN()';
|
||
} else if (component.type == ComponentType.ic) {
|
||
// IC 需要子电路定义,这里简化处理
|
||
return null;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/// 获取 SPICE 元件前缀
|
||
String _getSpicePrefix(ComponentType type) {
|
||
switch (type) {
|
||
case ComponentType.resistor:
|
||
return 'R';
|
||
case ComponentType.capacitor:
|
||
return 'C';
|
||
case ComponentType.inductor:
|
||
return 'L';
|
||
case ComponentType.diode:
|
||
return 'D';
|
||
case ComponentType.transistor:
|
||
return 'Q';
|
||
case ComponentType.ic:
|
||
return 'X';
|
||
case ComponentType.connector:
|
||
return 'J';
|
||
case ComponentType.custom:
|
||
return 'X';
|
||
}
|
||
}
|
||
|
||
/// 获取默认元件值
|
||
String _getDefaultValue(ComponentType type) {
|
||
switch (type) {
|
||
case ComponentType.resistor:
|
||
return '1k';
|
||
case ComponentType.capacitor:
|
||
return '1u';
|
||
case ComponentType.inductor:
|
||
return '1m';
|
||
case ComponentType.diode:
|
||
return '1N4148';
|
||
case ComponentType.transistor:
|
||
return '2N2222';
|
||
case ComponentType.ic:
|
||
return '';
|
||
case ComponentType.connector:
|
||
return '';
|
||
case ComponentType.custom:
|
||
return '';
|
||
}
|
||
}
|
||
|
||
/// 生成网络表(JSON 格式)
|
||
Map<String, dynamic> generateJsonNetlist(Design design) {
|
||
final nets = <String, Map<String, dynamic>>{};
|
||
|
||
for (final net in design.nets.values) {
|
||
final connections = <Map<String, dynamic>>[];
|
||
|
||
for (final conn in net.connections) {
|
||
connections.add({
|
||
'type': conn.type.name,
|
||
if (conn.componentId != null) 'componentId': conn.componentId,
|
||
if (conn.pinId != null) 'pinId': conn.pinId,
|
||
if (conn.position != null)
|
||
'position': {
|
||
'x': conn.position!.x,
|
||
'y': conn.position!.y,
|
||
},
|
||
if (conn.layerId != null) 'layerId': conn.layerId,
|
||
});
|
||
}
|
||
|
||
nets[net.id] = {
|
||
'name': net.name,
|
||
'type': net.type.name,
|
||
'connections': connections,
|
||
if (net.voltage != null) 'voltage': net.voltage,
|
||
if (net.isDifferential) 'isDifferential': true,
|
||
if (net.differentialPair != null) 'differentialPair': net.differentialPair,
|
||
if (net.busName != null) 'busName': net.busName,
|
||
};
|
||
}
|
||
|
||
return {
|
||
'designId': design.id,
|
||
'designName': design.name,
|
||
'version': design.version,
|
||
'generatedAt': DateTime.now().toIso8601String(),
|
||
'componentCount': design.components.length,
|
||
'netCount': design.nets.length,
|
||
'nets': nets,
|
||
};
|
||
}
|
||
|
||
/// 自动命名网络
|
||
///
|
||
/// 为未命名的网络生成唯一名称
|
||
void autoRenameNets(Design design) {
|
||
var netCounter = 1;
|
||
final usedNames = <String>{};
|
||
|
||
// 收集已使用的网络名
|
||
for (final net in design.nets.values) {
|
||
usedNames.add(net.name);
|
||
}
|
||
|
||
// 为未命名的网络生成名称
|
||
for (final net in design.nets.values) {
|
||
if (net.name.startsWith('N') || net.name.isEmpty) {
|
||
String newName;
|
||
do {
|
||
newName = 'N$netCounter';
|
||
netCounter++;
|
||
} while (usedNames.contains(newName));
|
||
|
||
usedNames.add(newName);
|
||
|
||
// 更新网络名(实际应用中需要更新设计数据)
|
||
// net.name = newName;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 从连接关系提取网络
|
||
///
|
||
/// 分析元件引脚连接,自动生成网络
|
||
List<Net> extractNetsFromConnections(Design design) {
|
||
final nets = <Net>[];
|
||
final connectionMap = <String, List<ConnectionPoint>>{};
|
||
|
||
// 收集所有连接点
|
||
for (final trace in design.traces.values) {
|
||
// 走线连接的两个端点
|
||
if (trace.points.length >= 2) {
|
||
final startConn = ConnectionPoint(
|
||
id: '${trace.id}_start',
|
||
type: ConnectionType.wireEnd,
|
||
position: trace.points.first,
|
||
layerId: trace.layerId,
|
||
);
|
||
final endConn = ConnectionPoint(
|
||
id: '${trace.id}_end',
|
||
type: ConnectionType.wireEnd,
|
||
position: trace.points.last,
|
||
layerId: trace.layerId,
|
||
);
|
||
|
||
final netId = trace.netId;
|
||
connectionMap.putIfAbsent(netId, () => []).add(startConn);
|
||
connectionMap.putIfAbsent(netId, () => []).add(endConn);
|
||
}
|
||
}
|
||
|
||
// 创建网络对象
|
||
final timestamp = DateTime.now().millisecondsSinceEpoch;
|
||
for (final entry in connectionMap.entries) {
|
||
nets.add(Net(
|
||
id: entry.key,
|
||
name: 'N${entry.key.substring(0, 8)}', // 简化命名
|
||
type: NetType.signal,
|
||
connections: entry.value,
|
||
metadata: Metadata(
|
||
createdAt: timestamp,
|
||
updatedAt: timestamp,
|
||
),
|
||
));
|
||
}
|
||
|
||
return nets;
|
||
}
|
||
}
|
||
|
||
/// SPICE 生成选项
|
||
class SpiceOptions {
|
||
/// 包含的模型文件路径
|
||
final List<String> includeModelFiles;
|
||
|
||
/// 是否生成模型定义
|
||
final bool generateModelDefinitions;
|
||
|
||
/// 分析类型:'dc', 'ac', 'tran'
|
||
final String analysisType;
|
||
|
||
/// DC 分析电源名
|
||
final String dcSourceName;
|
||
|
||
/// DC 分析起始值
|
||
final double dcStart;
|
||
|
||
/// DC 分析结束值
|
||
final double dcStop;
|
||
|
||
/// DC 分析步长
|
||
final double dcStep;
|
||
|
||
/// AC 分析类型:'LIN', 'DEC', 'OCT'
|
||
final String acType;
|
||
|
||
/// AC 分析点数
|
||
final int acPoints;
|
||
|
||
/// AC 分析起始频率
|
||
final double acStartFreq;
|
||
|
||
/// AC 分析结束频率
|
||
final double acStopFreq;
|
||
|
||
/// 瞬态分析步长
|
||
final double transientStep;
|
||
|
||
/// 瞬态分析停止时间
|
||
final double transientStop;
|
||
|
||
/// 要打印的变量
|
||
final List<String> printVariables;
|
||
|
||
const SpiceOptions({
|
||
this.includeModelFiles = const [],
|
||
this.generateModelDefinitions = false,
|
||
this.analysisType = 'dc',
|
||
this.dcSourceName = 'V1',
|
||
this.dcStart = 0.0,
|
||
this.dcStop = 5.0,
|
||
this.dcStep = 0.1,
|
||
this.acType = 'DEC',
|
||
this.acPoints = 10,
|
||
this.acStartFreq = 1.0,
|
||
this.acStopFreq = 1e6,
|
||
this.transientStep = 1e-6,
|
||
this.transientStop = 1e-3,
|
||
this.printVariables = const [],
|
||
});
|
||
}
|