feat: 右键分析test
This commit is contained in:
parent
ab22fcbf52
commit
3077c78c76
|
@ -6,4 +6,6 @@
|
||||||
# 解剖结构 - 模型的关系 1:1 or 1:n
|
# 解剖结构 - 模型的关系 1:1 or 1:n
|
||||||
|
|
||||||
- 换版本重新测量
|
- 换版本重新测量
|
||||||
-
|
|
||||||
|
|
||||||
|
pnpm config set virtual-store-dir-max-length 70
|
|
@ -1,107 +0,0 @@
|
||||||
// import http from "node:http";
|
|
||||||
import path from "node:path";
|
|
||||||
import { spawn, ChildProcess } from "node:child_process";
|
|
||||||
import { BrowserWindow } from "electron";
|
|
||||||
// import http from "node:http";
|
|
||||||
|
|
||||||
class PythonManager {
|
|
||||||
public flaskProcess: ChildProcess | null = null;
|
|
||||||
private intervalId: NodeJS.Timeout | null = null;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private mainWindow: BrowserWindow | null,
|
|
||||||
private url: string,
|
|
||||||
private interval = 5000
|
|
||||||
) {}
|
|
||||||
|
|
||||||
// 启动 Python 服务
|
|
||||||
public startFlask() {
|
|
||||||
if (this.flaskProcess) {
|
|
||||||
console.log("Flask service is already running.");
|
|
||||||
this.mainWindow?.webContents.send("flask", { running: true });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用 spawn 启动 Flask 服务
|
|
||||||
this.flaskProcess = spawn(path.join(process.env.VITE_PUBLIC!, "main.exe"));
|
|
||||||
|
|
||||||
// 实时获取 stdout 日志
|
|
||||||
this.flaskProcess.stdout?.on("data", (data) => {
|
|
||||||
const message = data.toString();
|
|
||||||
console.log(`Flask stdout: ${message}`);
|
|
||||||
this.mainWindow?.webContents.send("flask", { stdout: message });
|
|
||||||
});
|
|
||||||
|
|
||||||
// 实时获取 stderr 日志
|
|
||||||
this.flaskProcess.stderr?.on("data", (data) => {
|
|
||||||
const message = data.toString();
|
|
||||||
console.error(`Flask stderr: ${message}`);
|
|
||||||
this.mainWindow?.webContents.send("flask-service:response", {
|
|
||||||
stderr: message,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// 监听进程关闭事件
|
|
||||||
this.flaskProcess.on("close", (code) => {
|
|
||||||
console.log(`Flask process exited with code ${code}`);
|
|
||||||
this.flaskProcess = null;
|
|
||||||
this.mainWindow?.webContents.send("flask-service:response", {
|
|
||||||
exited: true,
|
|
||||||
code,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// 开始轮询服务状态
|
|
||||||
// this.startCheckingFlaskStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 停止 Python 服务
|
|
||||||
public stopFlask() {
|
|
||||||
if (this.flaskProcess) {
|
|
||||||
this.flaskProcess.kill();
|
|
||||||
console.log("Flask service stopped.");
|
|
||||||
this.flaskProcess = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 停止轮询
|
|
||||||
this.stopCheckingFlaskStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查 Flask 服务状态
|
|
||||||
// private checkFlaskStatus() {
|
|
||||||
// if (!this.mainWindow) return;
|
|
||||||
|
|
||||||
// http
|
|
||||||
// .get(this.url, (res) => {
|
|
||||||
// const { statusCode } = res;
|
|
||||||
// this.mainWindow?.webContents.send("flask-check", {
|
|
||||||
// running: statusCode === 200,
|
|
||||||
// });
|
|
||||||
// })
|
|
||||||
// .on("error", (err) => {
|
|
||||||
// console.error(`Error checking Flask service: ${err.message}`);
|
|
||||||
// this.mainWindow?.webContents.send("flask-check", {
|
|
||||||
// running: false,
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 开始轮询 Flask 服务状态
|
|
||||||
// private startCheckingFlaskStatus() {
|
|
||||||
// if (this.intervalId) {
|
|
||||||
// console.log("Already checking Flask status.");
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// this.intervalId = setInterval(() => this.checkFlaskStatus(), this.interval);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 停止轮询 Flask 服务状态
|
|
||||||
private stopCheckingFlaskStatus() {
|
|
||||||
if (this.intervalId) {
|
|
||||||
clearInterval(this.intervalId);
|
|
||||||
this.intervalId = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default PythonManager;
|
|
|
@ -3,7 +3,6 @@ import { JSONFilePreset } from "lowdb/node";
|
||||||
import { app } from "electron";
|
import { app } from "electron";
|
||||||
import { Low } from "node_modules/lowdb/lib/core/Low";
|
import { Low } from "node_modules/lowdb/lib/core/Low";
|
||||||
import { StructuredMetadata } from "./dicom";
|
import { StructuredMetadata } from "./dicom";
|
||||||
import { existsSync } from "node:fs";
|
|
||||||
import { merge } from "lodash";
|
import { merge } from "lodash";
|
||||||
|
|
||||||
export interface ICreateDatabase {
|
export interface ICreateDatabase {
|
||||||
|
|
|
@ -9,7 +9,6 @@ import {
|
||||||
import { db } from "./core/db";
|
import { db } from "./core/db";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { mkdirSync, statSync } from "fs";
|
|
||||||
import { mkdir, stat } from "fs/promises";
|
import { mkdir, stat } from "fs/promises";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,56 +26,62 @@ const registerIpcMainHandlers = (mainWindow: Electron.BrowserWindow | null) => {
|
||||||
|
|
||||||
ipcMain.on("ai:task", (_, data) => {
|
ipcMain.on("ai:task", (_, data) => {
|
||||||
const { selectDicoms } = data;
|
const { selectDicoms } = data;
|
||||||
const promises = selectDicoms.reduce((promiseChain, dicom) => {
|
const promises = selectDicoms.reduce(
|
||||||
return promiseChain.then(() => {
|
(
|
||||||
const { filePaths, PatientName, SeriesInstanceUID } = dicom;
|
promiseChain: Promise<any>,
|
||||||
const img_path = path.dirname(filePaths[0]);
|
dicom: { filePaths: any; PatientName: any; SeriesInstanceUID: any }
|
||||||
const save_path = path.join(
|
) => {
|
||||||
app.getPath("userData"),
|
return promiseChain.then(() => {
|
||||||
"output",
|
const { filePaths, PatientName, SeriesInstanceUID } = dicom;
|
||||||
PatientName,
|
const img_path = path.dirname(filePaths[0]);
|
||||||
SeriesInstanceUID
|
const save_path = path.join(
|
||||||
);
|
app.getPath("userData"),
|
||||||
const params = {
|
"output",
|
||||||
img_path,
|
PatientName,
|
||||||
save_path,
|
SeriesInstanceUID
|
||||||
pu: "GPU",
|
);
|
||||||
module: "root",
|
const params = {
|
||||||
turbo: true,
|
img_path,
|
||||||
};
|
save_path,
|
||||||
|
pu: "GPU",
|
||||||
|
module: "root",
|
||||||
|
turbo: true,
|
||||||
|
};
|
||||||
|
|
||||||
const startTime = Date.now(); // 记录请求开始时间
|
const startTime = Date.now(); // 记录请求开始时间
|
||||||
console.log(
|
console.log(
|
||||||
`Request for ${PatientName} started at ${new Date(
|
`Request for ${PatientName} started at ${new Date(
|
||||||
startTime
|
startTime
|
||||||
).toISOString()}`
|
).toISOString()}`
|
||||||
);
|
);
|
||||||
console.log(params);
|
console.log(params);
|
||||||
|
|
||||||
return axios
|
return axios
|
||||||
.post("http://127.0.0.1:5000/root", { ...params })
|
.post("http://127.0.0.1:5000/root", { ...params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
const endTime = Date.now(); // 记录请求结束时间
|
const endTime = Date.now(); // 记录请求结束时间
|
||||||
const duration = (endTime - startTime) / 1000; // 计算耗时,单位为秒
|
const duration = (endTime - startTime) / 1000; // 计算耗时,单位为秒
|
||||||
console.log(
|
console.log(
|
||||||
`Response for ${PatientName} received at ${new Date(
|
`Response for ${PatientName} received at ${new Date(
|
||||||
endTime
|
endTime
|
||||||
).toISOString()} (Duration: ${duration} seconds)`
|
).toISOString()} (Duration: ${duration} seconds)`
|
||||||
);
|
);
|
||||||
console.log(response.data);
|
console.log(response.data);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
const endTime = Date.now(); // 即使失败也记录结束时间
|
const endTime = Date.now(); // 即使失败也记录结束时间
|
||||||
const duration = (endTime - startTime) / 1000;
|
const duration = (endTime - startTime) / 1000;
|
||||||
console.error(
|
console.error(
|
||||||
`Error for ${PatientName} at ${new Date(
|
`Error for ${PatientName} at ${new Date(
|
||||||
endTime
|
endTime
|
||||||
).toISOString()} (Duration: ${duration} seconds)`
|
).toISOString()} (Duration: ${duration} seconds)`
|
||||||
);
|
);
|
||||||
console.error(error);
|
console.error(error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, Promise.resolve()); // 从 resolve() 开始,依次链接每个 Promise
|
},
|
||||||
|
Promise.resolve()
|
||||||
|
); // 从 resolve() 开始,依次链接每个 Promise
|
||||||
|
|
||||||
const overallStartTime = Date.now(); // 记录总的开始时间
|
const overallStartTime = Date.now(); // 记录总的开始时间
|
||||||
promises.then(() => {
|
promises.then(() => {
|
||||||
|
@ -124,6 +129,81 @@ const registerIpcMainHandlers = (mainWindow: Electron.BrowserWindow | null) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.on("one-step", async (event, dir) => {
|
||||||
|
const directory = path.resolve(dir);
|
||||||
|
const dcmPaths = await findDcmFiles(directory);
|
||||||
|
const batchSize = os.cpus().length * 1 || 10;
|
||||||
|
const items = await processFilesInBatches(dcmPaths, batchSize);
|
||||||
|
const structDicom = await structureMetadata(items);
|
||||||
|
// 存数据库
|
||||||
|
const changeTime = Date.now();
|
||||||
|
for (const item of structDicom) {
|
||||||
|
const existSeries = db.data.series.find(
|
||||||
|
(i) => i[keyProp] === item[keyProp]
|
||||||
|
);
|
||||||
|
existSeries
|
||||||
|
? Object.assign(existSeries, item, { updateTime: changeTime })
|
||||||
|
: db.data.series.push({
|
||||||
|
...item,
|
||||||
|
createTime: changeTime,
|
||||||
|
updateTime: changeTime,
|
||||||
|
});
|
||||||
|
await db.write();
|
||||||
|
}
|
||||||
|
// 启动分析
|
||||||
|
const promises = structDicom.reduce((promiseChain, dicom) => {
|
||||||
|
return promiseChain.then(() => {
|
||||||
|
const { filePaths, PatientName = "", SeriesInstanceUID = "" } = dicom;
|
||||||
|
const img_path = path.dirname(filePaths[0]);
|
||||||
|
const save_path = path.join(
|
||||||
|
app.getPath("userData"),
|
||||||
|
"output",
|
||||||
|
PatientName,
|
||||||
|
SeriesInstanceUID
|
||||||
|
);
|
||||||
|
const params = {
|
||||||
|
img_path,
|
||||||
|
save_path,
|
||||||
|
pu: "GPU",
|
||||||
|
module: "root",
|
||||||
|
turbo: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const startTime = Date.now(); // 记录请求开始时间
|
||||||
|
return axios
|
||||||
|
.post("http://127.0.0.1:5000/root", { ...params })
|
||||||
|
.then((response) => {
|
||||||
|
const endTime = Date.now(); // 记录请求结束时间
|
||||||
|
const duration = (endTime - startTime) / 1000; // 计算耗时,单位为秒
|
||||||
|
event.reply(
|
||||||
|
"taskFinished",
|
||||||
|
`Response for ${PatientName} received at ${new Date(
|
||||||
|
endTime
|
||||||
|
).toISOString()} (Duration: ${duration} seconds)`
|
||||||
|
);
|
||||||
|
console.log(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const endTime = Date.now(); // 即使失败也记录结束时间
|
||||||
|
const duration = (endTime - startTime) / 1000;
|
||||||
|
console.error(
|
||||||
|
`Error for ${PatientName} at ${new Date(
|
||||||
|
endTime
|
||||||
|
).toISOString()} (Duration: ${duration} seconds)`
|
||||||
|
);
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, Promise.resolve()); // 从 resolve() 开始,依次链接每个 Promise
|
||||||
|
|
||||||
|
const overallStartTime = Date.now(); // 记录总的开始时间
|
||||||
|
promises.then(() => {
|
||||||
|
const overallEndTime = Date.now(); // 记录总的结束时间
|
||||||
|
const overallDuration = (overallEndTime - overallStartTime) / 1000; // 总耗时
|
||||||
|
event.reply("tasksFinished", overallDuration);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* api 获取 列表的中数据
|
* api 获取 列表的中数据
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
|
||||||
import { useLocation } from "react-router-dom";
|
import { useLocation } from "react-router-dom";
|
||||||
import { SparkleIcon, Trash2 } from "lucide-react";
|
import { SparkleIcon, Trash2 } from "lucide-react";
|
||||||
import {
|
import {
|
||||||
|
@ -16,13 +13,12 @@ import { Card } from "@/components/ui/card";
|
||||||
import { Series } from "../Datasource/SeriesTable";
|
import { Series } from "../Datasource/SeriesTable";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const Boot = () => {
|
const Boot = () => {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const selectDicoms =
|
const selectDicoms =
|
||||||
location.state?.selectDicoms ??
|
location.state?.selectDicoms ??
|
||||||
JSON.parse(localStorage.getItem("selectDicoms") ?? "[]");
|
JSON.parse(localStorage.getItem("selectDicoms") ?? "[]");
|
||||||
|
const [messageText, setMessageText] = useState(["进度信息简化"]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* windows系统右键启动应用菜单的入口路径
|
* windows系统右键启动应用菜单的入口路径
|
||||||
|
@ -30,7 +26,6 @@ const Boot = () => {
|
||||||
const [bootDirectoryPath, setBootDirectoryPath] = useState("");
|
const [bootDirectoryPath, setBootDirectoryPath] = useState("");
|
||||||
const [tasks, setTasks] = useState(selectDicoms);
|
const [tasks, setTasks] = useState(selectDicoms);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传到electron主进程
|
* 上传到electron主进程
|
||||||
*/
|
*/
|
||||||
|
@ -45,11 +40,32 @@ const Boot = () => {
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (bootDirectoryPath) {
|
||||||
|
window.ipcRenderer.send("one-step");
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
window.ipcRenderer.off("one-step", () => {});
|
||||||
|
};
|
||||||
|
}, [bootDirectoryPath]);
|
||||||
|
|
||||||
const handleEmptyTasks = () => {
|
const handleEmptyTasks = () => {
|
||||||
setTasks([]);
|
setTasks([]);
|
||||||
localStorage.removeItem("selectDicoms");
|
localStorage.removeItem("selectDicoms");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.ipcRenderer.on("tasksFinished", (_event, data) => {
|
||||||
|
setMessageText((p) => [...p, `总用时: ${data}`]);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.ipcRenderer.on("taskFinished", (_event, data) => {
|
||||||
|
setMessageText((p) => [...p, data]);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ y: 10, opacity: 0 }}
|
initial={{ y: 10, opacity: 0 }}
|
||||||
|
@ -59,10 +75,7 @@ const Boot = () => {
|
||||||
className="h-full"
|
className="h-full"
|
||||||
>
|
>
|
||||||
<div className="p-4 h-full flex flex-col">
|
<div className="p-4 h-full flex flex-col">
|
||||||
<ResizablePanelGroup
|
<ResizablePanelGroup direction="horizontal" className="w-full h-full">
|
||||||
direction="horizontal"
|
|
||||||
className="w-full h-full"
|
|
||||||
>
|
|
||||||
<ResizablePanel defaultSize={38.2}>
|
<ResizablePanel defaultSize={38.2}>
|
||||||
<div className="flex flex-col h-full pt-4">
|
<div className="flex flex-col h-full pt-4">
|
||||||
<ScrollArea className="flex-grow w-full h-full px-4 pb-2">
|
<ScrollArea className="flex-grow w-full h-full px-4 pb-2">
|
||||||
|
@ -121,7 +134,9 @@ const Boot = () => {
|
||||||
<ResizableHandle withHandle />
|
<ResizableHandle withHandle />
|
||||||
<ResizablePanel defaultSize={61.8}>
|
<ResizablePanel defaultSize={61.8}>
|
||||||
<div className="flex h-full items-center justify-center p-6">
|
<div className="flex h-full items-center justify-center p-6">
|
||||||
<span className="font-semibold">分析进度将会呈现在这里</span>
|
{messageText.map((item) => (
|
||||||
|
<p className="font-semibold">{item}</p>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</ResizablePanel>
|
</ResizablePanel>
|
||||||
</ResizablePanelGroup>
|
</ResizablePanelGroup>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user