252 lines
7.1 KiB
TypeScript
252 lines
7.1 KiB
TypeScript
import * as vscode from "vscode";
|
|
import { getWebviewContent } from "../views/webviewContent";
|
|
import {
|
|
handleUserMessage,
|
|
insertCodeToEditor,
|
|
handleReadFile,
|
|
handleUpdateFile,
|
|
handleRenameFile,
|
|
handleReplaceInFile
|
|
} from "../utils/messageHandler";
|
|
import { VCDViewerPanel } from "./VCDViewerPanel";
|
|
|
|
/**
|
|
* 创建并显示 IC 助手面板
|
|
*/
|
|
export function showICHelperPanel(context: vscode.ExtensionContext) {
|
|
// 创建WebView面板
|
|
const panel = vscode.window.createWebviewPanel(
|
|
"icCoder", // 面板ID
|
|
"IC Coder", // 面板标题
|
|
vscode.ViewColumn.Beside, // 显示在旁边
|
|
{
|
|
enableScripts: true,
|
|
retainContextWhenHidden: true,
|
|
localResourceRoots: [vscode.Uri.joinPath(context.extensionUri, "media")],
|
|
}
|
|
);
|
|
|
|
// 设置标签页图标
|
|
panel.iconPath = vscode.Uri.joinPath(context.extensionUri, "media", "图案(方底).png");
|
|
|
|
// 获取页面内图标URI
|
|
const iconUri = panel.webview.asWebviewUri(
|
|
vscode.Uri.joinPath(context.extensionUri, "media", "图案(方底).png")
|
|
);
|
|
|
|
// 设置HTML内容
|
|
panel.webview.html = getWebviewContent(iconUri.toString());
|
|
|
|
// 处理消息
|
|
panel.webview.onDidReceiveMessage(
|
|
(message) => {
|
|
switch (message.command) {
|
|
case "sendMessage":
|
|
handleUserMessage(panel, message.text, context.extensionPath);
|
|
break;
|
|
case "readFile":
|
|
handleReadFile(panel, message.filePath);
|
|
break;
|
|
case "updateFile":
|
|
handleUpdateFile(panel, message.filePath, message.content);
|
|
break;
|
|
case "renameFile":
|
|
handleRenameFile(panel, message.oldPath, message.newPath);
|
|
break;
|
|
case "replaceInFile":
|
|
handleReplaceInFile(panel, message.filePath, message.searchText, message.replaceText);
|
|
break;
|
|
case "insertCode":
|
|
insertCodeToEditor(message.code);
|
|
break;
|
|
case "showInfo":
|
|
vscode.window.showInformationMessage(message.text);
|
|
break;
|
|
case "openWaveformViewer":
|
|
// 打开波形查看器
|
|
if (message.vcdFilePath) {
|
|
VCDViewerPanel.createOrShow(context.extensionUri, message.vcdFilePath);
|
|
}
|
|
break;
|
|
case "getVCDInfo":
|
|
// 获取 VCD 文件信息
|
|
if (message.vcdFilePath && message.containerId) {
|
|
getVCDFileInfo(panel, message.vcdFilePath, message.containerId);
|
|
}
|
|
break;
|
|
}
|
|
},
|
|
undefined,
|
|
context.subscriptions
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 获取 VCD 文件信息
|
|
*/
|
|
async function getVCDFileInfo(
|
|
panel: vscode.WebviewPanel,
|
|
vcdFilePath: string,
|
|
containerId: string
|
|
) {
|
|
try {
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
// 检查文件是否存在
|
|
if (!fs.existsSync(vcdFilePath)) {
|
|
panel.webview.postMessage({
|
|
command: "vcdInfo",
|
|
containerId: containerId,
|
|
vcdInfo: {
|
|
signalCount: 'N/A',
|
|
timeRange: 'N/A',
|
|
fileSize: 'N/A',
|
|
error: '文件不存在'
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 获取文件大小
|
|
const stats = fs.statSync(vcdFilePath);
|
|
const fileSizeKB = stats.size / 1024;
|
|
const fileSize = fileSizeKB < 1024
|
|
? `${fileSizeKB.toFixed(2)} KB`
|
|
: `${(fileSizeKB / 1024).toFixed(2)} MB`;
|
|
|
|
// 读取 VCD 文件内容
|
|
const content = fs.readFileSync(vcdFilePath, 'utf-8');
|
|
|
|
// 解析信号数量
|
|
const varMatches = content.match(/\$var/g);
|
|
const signalCount = varMatches ? varMatches.length : 0;
|
|
|
|
// 解析时间范围
|
|
let timeRange = 'N/A';
|
|
const timeMatch = content.match(/#(\d+)/g);
|
|
if (timeMatch && timeMatch.length > 0) {
|
|
const times = timeMatch.map((t: string) => parseInt(t.substring(1)));
|
|
const minTime = Math.min(...times);
|
|
const maxTime = Math.max(...times);
|
|
timeRange = `${minTime} - ${maxTime}`;
|
|
}
|
|
|
|
// 解析前几个信号的真实数据
|
|
const signals = parseVCDSignals(content, 3); // 只解析前3个信号
|
|
|
|
// 发送信息回前端
|
|
panel.webview.postMessage({
|
|
command: "vcdInfo",
|
|
containerId: containerId,
|
|
vcdInfo: {
|
|
signalCount: signalCount.toString(),
|
|
timeRange: timeRange,
|
|
fileSize: fileSize,
|
|
signals: signals // 添加真实信号数据
|
|
}
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('获取 VCD 文件信息失败:', error);
|
|
panel.webview.postMessage({
|
|
command: "vcdInfo",
|
|
containerId: containerId,
|
|
vcdInfo: {
|
|
signalCount: 'N/A',
|
|
timeRange: 'N/A',
|
|
fileSize: 'N/A',
|
|
error: error instanceof Error ? error.message : '未知错误'
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 解析 VCD 文件中的信号数据
|
|
*/
|
|
function parseVCDSignals(content: string, maxSignals: number = 3) {
|
|
const signals: Array<{
|
|
name: string;
|
|
identifier: string;
|
|
width: number;
|
|
values: Array<{ time: number; value: string }>;
|
|
}> = [];
|
|
|
|
try {
|
|
// 1. 解析信号定义部分
|
|
const varRegex = /\$var\s+(\w+)\s+(\d+)\s+(\S+)\s+([^\$]+?)\s+\$end/g;
|
|
let match;
|
|
const signalDefs: Array<{ name: string; identifier: string; width: number }> = [];
|
|
|
|
while ((match = varRegex.exec(content)) !== null && signalDefs.length < maxSignals) {
|
|
const width = parseInt(match[2]);
|
|
const identifier = match[3];
|
|
const name = match[4].trim();
|
|
|
|
signalDefs.push({ name, identifier, width });
|
|
}
|
|
|
|
// 2. 找到数据变化部分的起始位置
|
|
const dumpvarsIndex = content.indexOf('$dumpvars');
|
|
if (dumpvarsIndex === -1) {
|
|
return signals;
|
|
}
|
|
|
|
const dataSection = content.substring(dumpvarsIndex);
|
|
|
|
// 3. 解析每个信号的值变化
|
|
for (const signalDef of signalDefs) {
|
|
const values: Array<{ time: number; value: string }> = [];
|
|
let currentTime = 0;
|
|
|
|
// 分行处理数据
|
|
const lines = dataSection.split('\n');
|
|
|
|
for (const line of lines) {
|
|
const trimmedLine = line.trim();
|
|
|
|
// 解析时间戳
|
|
if (trimmedLine.startsWith('#')) {
|
|
currentTime = parseInt(trimmedLine.substring(1));
|
|
continue;
|
|
}
|
|
|
|
// 解析信号值变化
|
|
// 格式1: 单比特信号 "0!" 或 "1!"
|
|
// 格式2: 多比特信号 "b1010 !"
|
|
if (signalDef.width === 1) {
|
|
// 单比特信号
|
|
const singleBitMatch = trimmedLine.match(new RegExp(`^([01xz])${signalDef.identifier}$`));
|
|
if (singleBitMatch) {
|
|
values.push({ time: currentTime, value: singleBitMatch[1] });
|
|
}
|
|
} else {
|
|
// 多比特信号
|
|
const multiBitMatch = trimmedLine.match(new RegExp(`^b([01xz]+)\\s+${signalDef.identifier}$`));
|
|
if (multiBitMatch) {
|
|
values.push({ time: currentTime, value: multiBitMatch[1] });
|
|
}
|
|
}
|
|
|
|
// 限制采样点数量,避免数据过多
|
|
if (values.length >= 50) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
signals.push({
|
|
name: signalDef.name,
|
|
identifier: signalDef.identifier,
|
|
width: signalDef.width,
|
|
values: values
|
|
});
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('解析 VCD 信号数据失败:', error);
|
|
}
|
|
|
|
return signals;
|
|
}
|