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; }