diff --git a/apps/desktop/electron/core/dicom.ts b/apps/desktop/electron/core/dicom.ts index e3e90ab..188a129 100644 --- a/apps/desktop/electron/core/dicom.ts +++ b/apps/desktop/electron/core/dicom.ts @@ -1,6 +1,7 @@ import path from "path"; import * as dicomParser from "dicom-parser"; import fs from "fs"; +import crypto from 'crypto'; export interface StructuredData { [SeriesInstanceUID: string]: ExtractMetadata[]; @@ -128,35 +129,58 @@ export interface StructuredMetadata { StudyInstanceUID?: string; SeriesInstanceUID?: string; PatientName?: string; + fileHash: string[] } -export const structureMetadata = ( - data: ExtractMetadata[] -): StructuredMetadata[] => { +export interface ScanProgress { + percentage: number +} + +// 计算文件的哈希值 +async function calculateFileHash(filePath: string): Promise { + return new Promise((resolve, reject) => { + const hash = crypto.createHash('sha256'); + const stream = fs.createReadStream(filePath); + + stream.on('error', reject); + stream.pipe(hash).on('finish', () => resolve(hash.digest('hex'))); + }); +} + +export const structureMetadata = async ( + data: ExtractMetadata[], + progressCallback?: (progress: ScanProgress) => void +): Promise => { const result: StructuredMetadata[] = []; + // 序列级别 + const keyProp = 'SeriesInstanceUID' - data.forEach((item) => { - // 查找是否已经有相同 UID 和 PatientName 的条目 - const existingEntry = result.find( - (entry) => - entry.StudyInstanceUID === item.StudyInstanceUID && - entry.SeriesInstanceUID === item.SeriesInstanceUID && - entry.PatientName === item.PatientName - ); + for (let i = 0; i < data.length; i++) { + const item = data[i] + const existItem = result.find((i) => i[keyProp] === item[keyProp]); + const hash = await calculateFileHash(item.filePath); - if (existingEntry) { + if (existItem) { // 如果找到了相同的条目,合并 filePath - existingEntry.filePaths.push(item.filePath); + if (!existItem.fileHash.includes(hash)) { + existItem.filePaths.push(item.filePath); + existItem.fileHash.push(hash) + } } else { // 如果没有找到,创建一个新的条目 result.push({ filePaths: [item.filePath], + fileHash: [hash], StudyInstanceUID: item.StudyInstanceUID, SeriesInstanceUID: item.SeriesInstanceUID, PatientName: item.PatientName, }); } - }); + const progress: ScanProgress = { + percentage: ((i + 1) / data.length) * 100 + }; + progressCallback?.(progress); + } return result; }; diff --git a/apps/desktop/electron/ipcMainHandlers.ts b/apps/desktop/electron/ipcMainHandlers.ts index af69e87..9500061 100644 --- a/apps/desktop/electron/ipcMainHandlers.ts +++ b/apps/desktop/electron/ipcMainHandlers.ts @@ -47,17 +47,19 @@ const registerIpcMainHandlers = ( running ? pythonManager.startFlask() : pythonManager.stopFlask(); }); - ipcMain.on("scan-dicom", async () => { + ipcMain.on("scan-dicom", async (event) => { const result = await dialog.showOpenDialog({ properties: ["openDirectory"], }); if (result.canceled) return null; const filePaths = result.filePaths[0]; - const dcmPaths = await findDcmFiles(filePaths); - const items = await processFilesInBatches(dcmPaths, 4); - const structDicom = await structureMetadata(items); - Database.getDb().data.posts.push("hello world"); - return structDicom; + const dcmPaths = await findDcmFiles(filePaths) + const batchSize = os.cpus().length * 1 || 10; + const items = await processFilesInBatches(dcmPaths, batchSize); + const structDicom = await structureMetadata(items, (progress) => { + event.reply('scan-progress', progress) + }); + event.reply('scan-progress-done', structDicom) }); }; diff --git a/apps/desktop/index.html b/apps/desktop/index.html index e4b78ea..819f9cd 100644 --- a/apps/desktop/index.html +++ b/apps/desktop/index.html @@ -4,7 +4,7 @@ - Vite + React + TS + Cvpilot AI
diff --git a/apps/desktop/src/pages/Datasource/index.tsx b/apps/desktop/src/pages/Datasource/index.tsx index dfbd93c..9b559cf 100644 --- a/apps/desktop/src/pages/Datasource/index.tsx +++ b/apps/desktop/src/pages/Datasource/index.tsx @@ -1,12 +1,46 @@ import { Button } from "@/components/ui/button"; +import { useEffect, useState } from "react"; + +interface ScanProgress { + percentage: number +} export const Datasource = () => { + const [progress, setProgress] = useState() + const [result, setResult] = useState() + + useEffect(() => { + window.ipcRenderer.on('scan-progress', (event, data) => { + setProgress(data) + if (data.error) return; + }); + + return () => { + window.ipcRenderer.off('scan-progress', () => { }); + }; + }, []); + + useEffect(() => { + window.ipcRenderer.on('scan-progress-done', (event, data) => { + console.log('%capps\desktop\src\pages\Datasource\index.tsx:25 data', 'color: #007acc;', data); + setResult(data) + if (data.error) return; + }); + + return () => { + window.ipcRenderer.off('scan-progress-done', () => { }); + }; + }, []); + + return (
+
{progress?.percentage}
+
{JSON.stringify(result)}
);