| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462 |
- // src/features/imageAnnotation/services/AnnotationManager.ts
- // 全局注释管理器 - 负责监听Cornerstone事件并管理注释的保存/加载
- import { eventTarget, getRenderingEngines } from '@cornerstonejs/core';
- import { EVENTS } from '@cornerstonejs/core';
- import { annotation as corAnnotation, Enums } from '@cornerstonejs/tools';
- import { AnnotationSerializer } from './AnnotationSerializer';
- import { AnnotationValidator } from './AnnotationValidator';
- import { annotationAPI } from '../../../API/annotation';
- import { extractSopInstanceUid } from '../../../utils/dicomUtils';
- import type { AnnotationData, AnnotationEventType } from '../types/annotation';
- import { IRenderingEngine, IStackViewport } from '@cornerstonejs/core/dist/esm/types';
- export class AnnotationManager {
- private eventListenersRegistered: boolean = false;
- private activeImageId: string | null = null;
- private serializer: AnnotationSerializer;
- private validator: AnnotationValidator;
- private pendingChanges: Map<string, string> = new Map();
- private saveTimeout: NodeJS.Timeout | null = null;
- private isLoading: boolean = false;
- constructor() {
- this.serializer = new AnnotationSerializer();
- this.validator = new AnnotationValidator();
- }
- async initialize(): Promise<void> {
- console.log('【annotationmanager】🔧 AnnotationManager 初始化中...');
- // 注册全局事件监听器
- this.registerGlobalEventListeners();
- console.log('【annotationmanager】🎧 全局注释事件监听器已设置');
- }
- async cleanup(): Promise<void> {
- console.log('【annotationmanager】🧹 AnnotationManager 清理中...');
- // 取消待处理的保存操作
- if (this.saveTimeout) {
- clearTimeout(this.saveTimeout);
- this.saveTimeout = null;
- }
- // 移除全局事件监听器
- this.unregisterGlobalEventListeners();
- console.log('【annotationmanager】✅ AnnotationManager 清理完成');
- }
- // ============ 事件监听器注册 ============
- private registerGlobalEventListeners(): void {
- if (this.eventListenersRegistered) {
- console.warn('【annotationmanager】事件监听器已注册,跳过重复注册');
- return;
- }
- console.log('【annotationmanager】注册事件监听器...');
- // 图像加载完成事件
- eventTarget.addEventListener(
- EVENTS.IMAGE_LOADED,
- this.handleImageLoadCompleted
- );
- // 图像渲染完成事件
- eventTarget.addEventListener(
- EVENTS.IMAGE_RENDERED,
- this.handleImageRendered
- );
- // 注释完成事件
- eventTarget.addEventListener(
- Enums.Events.ANNOTATION_COMPLETED,
- this.handleAnnotationCompleted
- );
- // 注释添加事件
- eventTarget.addEventListener(
- Enums.Events.ANNOTATION_ADDED,
- this.handleAnnotationAdded
- );
- // 注释修改事件
- eventTarget.addEventListener(
- Enums.Events.ANNOTATION_MODIFIED,
- this.handleAnnotationModified
- );
- // 注释删除事件
- eventTarget.addEventListener(
- Enums.Events.ANNOTATION_REMOVED,
- this.handleAnnotationRemoved
- );
- this.eventListenersRegistered = true;
- console.log('【annotationmanager】✅ 事件监听器注册完成');
- }
- private unregisterGlobalEventListeners(): void {
- if (!this.eventListenersRegistered) {
- return;
- }
- console.log('【annotationmanager】移除事件监听器...');
- eventTarget.removeEventListener(
- EVENTS.IMAGE_LOADED,
- this.handleImageLoadCompleted
- );
- eventTarget.removeEventListener(
- EVENTS.IMAGE_RENDERED,
- this.handleImageRendered
- );
- eventTarget.removeEventListener(
- Enums.Events.ANNOTATION_COMPLETED,
- this.handleAnnotationCompleted
- );
- eventTarget.removeEventListener(
- Enums.Events.ANNOTATION_ADDED,
- this.handleAnnotationCompleted
- );
- eventTarget.removeEventListener(
- Enums.Events.ANNOTATION_MODIFIED,
- this.handleAnnotationModified
- );
- eventTarget.removeEventListener(
- Enums.Events.ANNOTATION_REMOVED,
- this.handleAnnotationRemoved
- );
- this.eventListenersRegistered = false;
- console.log('【annotationmanager】✅ 事件监听器移除完成');
- }
- // ============ 事件处理方法 ============
- private handleImageLoadCompleted = (evt: any) => {
- const rawImageId = evt.detail.image?.imageId || evt.detail.imageId;
- console.log(`【annotationmanager】📸 原始图像ID: ${rawImageId}`);
- // 提取SOP Instance UID
- const sopInstanceUid = extractSopInstanceUid(rawImageId);
- if (!sopInstanceUid) {
- console.warn('【annotationmanager】无法从图像URL中提取SOP Instance UID:', rawImageId);
- return;
- }
- console.log(`【annotationmanager】📸 提取的SOP Instance UID: ${sopInstanceUid}`);
- // 使用提取的SOP Instance UID加载注释
- this.loadAnnotationsForImage(sopInstanceUid);
- };
- private handleImageRendered = (evt: any) => {
- const { viewportId } = evt.detail;
- console.log(`【annotationmanager】🎨 图像渲染完成: ${viewportId}`);
- // TODO: 确保注释正确显示
- };
- private handleAnnotationCompleted = (evt: any) => {
- console.log('🎯 监听到注释完成事件:', evt);
- console.log('📋 事件详情:', evt.detail);
- const { annotation, toolName } = evt.detail;
- console.log(`【annotationmanager】✏️ 注释创建完成: ${toolName}`);
- // 确保有活动的图像ID
- if (!this.activeImageId) {
- console.warn('【annotationmanager】没有活动的图像ID,跳过注释保存');
- return;
- }
- // 实现注释保存逻辑
- this.saveAnnotation(annotation, toolName);
- };
- private handleAnnotationAdded = (evt: any) => {
- console.log('【annotationmanager】🎯 监听到注释添加事件:', evt);
- console.log('【annotationmanager】📋 事件详情:', evt.detail);
- const { annotation } = evt.detail;
- const toolName = annotation.metadata?.toolName;
- console.log(`【annotationmanager】➕ 注释添加: ${toolName}`);
- // 确保有活动的图像ID
- if (!this.activeImageId) {
- console.warn('【annotationmanager】没有活动的图像ID,跳过注释保存');
- return;
- }
- // 实现注释保存逻辑
- this.saveAnnotation(annotation, toolName);
- };
- private handleAnnotationModified = (evt: any) => {
- console.log('🎯 监听到注释修改事件:', evt);
- console.log('📋 事件详情:', evt.detail);
- const { annotation, toolName } = evt.detail;
- console.log(`【annotationmanager】🔄 注释修改完成: ${toolName}`);
- // 确保有活动的图像ID
- if (!this.activeImageId) {
- console.warn('【annotationmanager】没有活动的图像ID,跳过注释更新');
- return;
- }
- // 实现注释更新逻辑
- this.updateAnnotation(annotation, toolName);
- };
- private handleAnnotationRemoved = (evt: any) => {
- console.log('🎯 监听到注释删除事件:', evt);
- console.log('📋 事件详情:', evt.detail);
- const { annotation } = evt.detail;
- const annotationUID = annotation.annotationUID;
- console.log(`【annotationmanager】🗑️ 注释删除完成: ${annotationUID}`);
- // 确保有活动的图像ID
- if (!this.activeImageId) {
- console.warn('【annotationmanager】没有活动的图像ID,跳过注释删除');
- return;
- }
- // 实现注释删除逻辑
- this.deleteAnnotation(annotation);
- };
- // ============ 注释操作方法 ============
- private async loadAnnotationsForImage(imageId: string): Promise<void> {
- if (this.isLoading) {
- console.log(`【annotationmanager】注释正在加载中,跳过: ${imageId}`);
- return;
- }
- this.isLoading = true;
- this.activeImageId = imageId;
- try {
- console.log(`【annotationmanager】📥 加载注释数据: ${imageId}`);
- // 调用API获取注释数据
- const response = await annotationAPI.getAnnotations(imageId);
- if (response.data && Array.isArray(response.data)) {
- const annotationStrings = response.data;
- // 验证注释数据
- // this.validator.validateAnnotationArray(annotationStrings);
- // 反序列化并渲染注释
- await this.renderAnnotations(annotationStrings);
- console.log(`【annotationmanager】✅ 注释加载完成: ${imageId} (${annotationStrings.length} 个注释)`);
- } else {
- console.log(`【annotationmanager】ℹ️ 图像 ${imageId} 没有注释数据`);
- }
- } catch (error) {
- console.error(`【annotationmanager】❌ 注释加载失败 ${imageId}:`, error);
- // 不抛出错误,让应用继续运行
- } finally {
- this.isLoading = false;
- }
- }
- private async saveAnnotation(annotation: any, toolName: string): Promise<void> {
- try {
- console.log(`【annotationmanager】💾 保存注释: ${toolName}`);
- // 序列化注释数据
- const serializedData = this.serializer.serialize(annotation, toolName);
- // 验证序列化数据
- // this.validator.validateAnnotationData(serializedData);
- // 添加到待保存队列(延迟保存)
- this.scheduleSave(serializedData);
- console.log(`【annotationmanager】✅ 注释保存任务已安排: ${toolName}`);
- } catch (error) {
- console.error(`【annotationmanager】❌ 注释保存失败 ${toolName}:`, error);
- }
- }
- private async updateAnnotation(annotation: any, toolName: string): Promise<void> {
- try {
- console.log(`【annotationmanager】🔄 更新注释: ${toolName}`);
- // 复用保存逻辑
- await this.saveAnnotation(annotation, toolName);
- console.log(`【annotationmanager】✅ 注释更新完成: ${toolName}`);
- } catch (error) {
- console.error(`【annotationmanager】❌ 注释更新失败 ${toolName}:`, error);
- }
- }
- private async deleteAnnotation(annotation: any): Promise<void> {
- try {
- const annotationUID = annotation.annotationUID;
- console.log(`【annotationmanager】🗑️ 删除注释: ${annotationUID}`);
- if (!this.activeImageId) {
- console.warn('【annotationmanager】没有活动的图像ID,跳过删除操作');
- return;
- }
- // 从 annotation 中获取 FrameOfReferenceUID
- const forUID = annotation.metadata?.referencedImageId;
- if (!forUID) {
- console.warn('【annotationmanager】FrameOfReferenceUID 未找到,跳过删除操作');
- return;
- }
- // 用 FrameOfReferenceUID 查找相关的 elements
- const elements = this.findElementsByFOR(forUID);
- // 收集删除后的所有注释
- const allAnnotationStrings: string[] = [];
- elements.forEach(element => {
- const anns = corAnnotation.state.getAnnotations('', element);
- if (anns) {
- // 序列化所有注释
- for (const [toolName, ann] of Object.entries(anns as any)) {
- try {
- const serialized = this.serializer.serialize(ann, toolName);
- allAnnotationStrings.push(serialized);
- } catch (error) {
- console.error('序列化注释失败:', error);
- // 跳过序列化失败的注释
- }
- }
- }
- });
- // 保存所有注释
- await annotationAPI.saveAnnotations(this.activeImageId, allAnnotationStrings);
- console.log(`【annotationmanager】✅ 注释删除完成: ${annotationUID} (剩余 ${allAnnotationStrings.length} 个注释)`);
- } catch (error) {
- console.error(`【annotationmanager】❌ 注释删除失败:`, error);
- }
- }
- // 用 FrameOfReferenceUID 查找 viewport elements
- private findElementsByFOR(imageId: string): any[] {
- const elements: any[] = [];
- if (!!getRenderingEngines) {
- const renderingEngines = getRenderingEngines();
- renderingEngines?.forEach((re: IRenderingEngine) => {
- re.getViewports().forEach((vp: IStackViewport) => {
- if (vp && vp.hasImageId(imageId) === true && vp.element) {
- elements.push(vp.element);
- }
- });
- });
- } else {
- console.warn('【annotationmanager】getRenderingEngines 方法不可用,无法查找元素');
- }
- return elements;
- }
- private async renderAnnotations(annotationStrings: string[]): Promise<void> {
- console.log(`【annotationmanager】🎨 渲染 ${annotationStrings.length} 个注释`);
- for (const annotationString of annotationStrings) {
- try {
- // 反序列化为Cornerstone格式
- const deserializedAnnotation = this.serializer.deserialize(annotationString);
- // 添加到Cornerstone注释状态
- corAnnotation.state.addAnnotation(deserializedAnnotation, deserializedAnnotation.metadata?.toolName || 'UnknownTool');
- console.log(`【annotationmanager】✅ 注释渲染成功`);
- } catch (error) {
- console.error(`【annotationmanager】❌ 注释渲染失败:`, error);
- // 继续处理其他注释
- }
- }
- }
- // ============ 延迟保存机制 ============
- /**
- * 安排延迟保存
- */
- private scheduleSave(annotationString: string): void {
- try {
- // 从JSON字符串中提取ID作为key
- const annotation = JSON.parse(annotationString);
- const key = annotation.annotationUID || annotation.id || `temp_${Date.now()}`;
- // 添加到待保存队列
- this.pendingChanges.set(key, annotationString);
- // 取消之前的延迟保存
- if (this.saveTimeout) {
- clearTimeout(this.saveTimeout);
- }
- // 设置新的延迟保存(1秒后)
- this.saveTimeout = setTimeout(() => {
- this.performBulkSave();
- }, 1000);
- } catch (error) {
- console.error('安排保存时解析注释失败:', error);
- }
- }
- /**
- * 执行批量保存
- */
- private async performBulkSave(): Promise<void> {
- if (this.pendingChanges.size === 0 || !this.activeImageId) {
- return;
- }
- const changes = Array.from(this.pendingChanges.values());
- this.pendingChanges.clear();
- try {
- console.log(`【annotationmanager】💾 批量保存 ${changes.length} 个注释`);
- // 调用API保存注释
- await annotationAPI.saveAnnotations(this.activeImageId, changes);
- console.log(`【annotationmanager】✅ 批量保存成功: ${changes.length} 个注释`);
- } catch (error) {
- console.error(`【annotationmanager】❌ 批量保存失败:`, error);
- // 保存失败时,重新加入待保存队列
- changes.forEach(change => {
- try {
- const annotation = JSON.parse(change);
- const key = annotation.annotationUID || annotation.id || `temp_${Date.now()}`;
- this.pendingChanges.set(key, change);
- } catch (error) {
- console.error('重新加入待保存队列时解析失败:', error);
- }
- });
- // 可以在这里实现重试逻辑
- }
- }
- }
|