十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

OHIF v3 ServicesManager 服务管理器实战指南:注册、访问、自定义与生命周期

OHIF v3 ServicesManager 服务管理器实战指南:注册、访问、自定义与生命周期 OHIF v3 ServicesManager 服务管理器实战指南注册、访问、自定义与生命周期【免费下载链接】ViewersOHIF zero-footprint DICOM viewer and oncology specific Lesion Tracker, plus shared extension packages项目地址: https://gitcode.com/GitHub_Trending/vi/Viewers本文是 OHIF v3 平台 Managers 系列的核心篇聚焦ServicesManager这一服务注册与访问的单一入口。结合 platform/docs/versioned_docs/version-3.11/platform/managers/service.md 与 platform/core/src/services/ServicesManager.ts 的真实实现你将掌握内置服务如何被注册、如何在组件与扩展中访问服务、如何编写自定义服务并通过扩展preRegistration挂载以及模式Mode进出时的服务状态生命周期契约。ServicesManager 概述服务注册的单一入口ServicesManager是整个 OHIF v3 应用的服务注册中心。所有功能模块测量、显示集、挂片协议、工具栏、弹窗等都以服务的形式存在而ServicesManager是它们唯一的注册点与访问点。其核心约定十分简单每个服务必须实现一个create方法。ServicesManager在注册时调用该方法来实例化服务实例化完成后服务会被挂到ServicesManager的services属性上应用内任何位置都可以通过servicesManager.services访问已注册的服务实例。从源码结构看ServicesManager持有三个关键成员见 platform/core/src/services/ServicesManager.tsservices注册完成后保存所有服务实例的映射表name - instanceregisteredServiceNames已注册服务名的数组用于去重与跟踪_commandsManager构造函数注入的命令管理器在调用create时透传给服务使服务可以发布/执行命令。export default class ServicesManager { public services: AppTypes.Services {}; public registeredServiceNames: string[] []; private _commandsManager: CommandsManager; private _extensionManager: ExtensionManager; constructor(commandsManager: CommandsManager) { this._commandsManager commandsManager; this._extensionManager null; this.services {}; this.registeredServiceNames []; } // ... }ServicesManager还通过setExtensionManager(extensionManager)在应用初始化阶段拿到扩展管理器引用见 platform/app/src/appInit.js并在创建服务时一并注入使服务能够反向使用扩展体系。SkeletonregisterService 与 registerServicesServicesManager对外暴露两个公开注册方法registerService(service, configuration {})注册单个服务可附带配置registerServices(services)批量注册一组服务。文档中给出了简化骨架而当前仓库中的真实实现platform/core/src/services/ServicesManager.ts在此之上增加了完整校验逻辑public registerService(service, configuration {}) { if (!service) { log.warn(Attempting to register a null/undefined service. Exiting early.); return; } if (!service.name) { log.warn(Service name not set. Exiting early.); return; } if (this.registeredServiceNames.includes(service.name)) { log.warn(Service name ${service.name} has already been registered. Exiting before duplicating services.); return; } if (service.create) { this.services[service.name] service.create({ configuration, extensionManager: this._extensionManager, commandsManager: this._commandsManager, servicesManager: this, }); if (service.altName) { // TODO - remove this registration this.services[service.altName] this.services[service.name]; } } else { log.warn(Service create factory function not defined. Exiting early.); return; } this.registeredServiceNames.push(service.name); } public registerServices(services) { services.forEach(service { const hasConfiguration Array.isArray(service); if (hasConfiguration) { const [ohifService, configuration] service; this.registerService(ohifService, configuration); } else { this.registerService(service); } }); }值得注意的四个实现细节四重校验空服务、缺name、重复注册、缺create工厂函数都会打印log.warn并提前退出避免产生损坏的半注册状态create收到的注入参数除了configuration还包含extensionManager、commandsManager和servicesManager自身服务可以在工厂函数中按需使用这些依赖altName别名机制部分服务如MeasurementService会提供altName注册时同一实例会同时挂载到主名与别名下保持向后兼容见下文元组批量注册registerServices支持[service, configuration]元组形式为单个服务附带专属配置——这是appInit中为MultiMonitorService、CustomizationService、StudyPrefetcherService注入应用配置的手段。这些校验行为全部由单元测试覆盖见 platform/core/src/services/ServicesManager.test.js包括空服务警告、缺 name 警告、缺 create 警告、重复注册警告、配置透传等场景可作为自定义服务开发时的行为参照。默认注册服务清单在应用初始化文件 platform/app/src/appInit.js 中OHIF v3 默认注册了一批核心服务真实实现比文档示例更完整部分服务以元组形式携带应用配置servicesManager.registerServices([ [MultiMonitorService.REGISTRATION, appConfig.multimonitor], UINotificationService.REGISTRATION, UIModalService.REGISTRATION, UIDialogService.REGISTRATION, UIViewportDialogService.REGISTRATION, MeasurementService.REGISTRATION, DisplaySetService.REGISTRATION, [CustomizationService.REGISTRATION, appConfig.customizationService], ToolbarService.REGISTRATION, ViewportGridService.REGISTRATION, HangingProtocolService.REGISTRATION, CineService.REGISTRATION, UserAuthenticationService.REGISTRATION, PanelService.REGISTRATION, WorkflowStepsService.REGISTRATION, [StudyPrefetcherService.REGISTRATION, appConfig.studyPrefetcher], ]);这些服务覆盖了 v3 应用运行所需的全部横切能力服务职责MultiMonitorService多显示器布局支持注册时传入appConfig.multimonitorUINotificationService全局通知toast提示UIModalService模态弹窗管理UIDialogService/UIViewportDialogService通用对话框与视口内对话框MeasurementService测量数据存储、事件发布与报告导出DisplaySetService显示集DisplaySet的注册与检索CustomizationService定制化配置自定义 UI/工具行为传入appConfig.customizationServiceToolbarService工具栏按钮、节与组的管理ViewportGridService视口网格布局状态管理HangingProtocolService挂片协议解析与执行CineService电影循环CINE播放UserAuthenticationService用户认证PanelService侧边面板管理WorkflowStepsService工作流步骤管理StudyPrefetcherService影像预取注册时传入appConfig.studyPrefetcher注意当前仓库 v3 的默认注册清单比文档所列的 11 个服务更多新增了UserAuthenticationService、PanelService、WorkflowStepsService、StudyPrefetcherService、MultiMonitorService这是版本演进的结果。若你的应用基于本仓库版本应以appInit.js的实际清单为准。服务架构namecreate的对象约定打开platform/core/src/services目录下的任一服务文件夹会发现每个服务都以导出对象的形式对外暴露该对象只要求两个字段name与create。以ToolbarService为例v3 中实际位于 platform/core/src/services/ToolBarService/index.ts其真正的注册定义在 platform/core/src/services/ToolBarService/ToolbarService.tspublic static REGISTRATION { // ... name: toolbarService, create: ({ configuration {}, commandsManager }) { return new ToolbarService(commandsManager); }, };即ToolbarService类被封装为一个{ name, create }的注册对象create负责用命令管理器构造服务实例。服务实现类与注册入口位于同一目录。注意create方法是任何自定义服务能否被成功注册的关键。没有create的服务会被ServicesManager直接拒绝log.warn后跳过。命名约定lower camel case 与 upper camel case从源码中可以总结出官方推荐的命名规范文档中的 TypeScript 演进建议服务实例名name使用小驼峰lower camel case例如measurementService、toolbarService、backEndService服务类/类型使用大驼峰upper camel case例如MeasurementService、ToolbarService、BackEndService。以 platform/core/src/services/MeasurementService/MeasurementService.ts 为实例这正是当前服务的标准形态public static REGISTRATION { name: measurementService, altName: MeasurementService, // 兼容旧版访问方式的别名 create: _options { return new MeasurementService(); }, };这种static REGISTRATION静态属性模式在CineService、DisplaySetService、HangingProtocolService、CustomizationService、PanelService等服务中已全面铺开可在platform/core/src/services下搜索public static REGISTRATION验证是 v3 推荐的服务导出范式。同时建议将服务类型作为模块的 Types 导出例如export { BackEndService }便于消费者获得完整的类型提示。访问服务servicesManager.services应用内任何持有servicesManager的地方都可以通过services属性解构出目标服务实例。文档中给出的PanelMeasurementTableTrackinglongitudinal 模式的右侧测量面板示例展示了这一访问模式function PanelMeasurementTableTracking({ servicesManager }) { const { MeasurementService } servicesManager.services; // ... async function exportReport() { const measurements MeasurementService.getMeasurements(); // ... downloadCSVReport(measurements, MeasurementService); } // ... }由于注册时MeasurementService同时注册了measurementService主名与MeasurementService别名见上文altName机制两种写法都可以解构出同一实例这也是旧版代码能平滑迁移到 v3 的原因之一。访问到实例后即可调用其公开 API如getMeasurements()驱动业务逻辑。注册自定义服务扩展的 preRegistration 钩子如果你需要在扩展Extension中新增业务服务文档给出了标准做法在扩展的preRegistration钩子中调用servicesManager.registerService。扩展入口示例// extensions/customExtension/src/index.js import WrappedBackEndService from ./services/backEndService; export default { id: myExtension, preRegistration({ servicesManager }) { servicesManager.registerService(WrappedBackEndService(servicesManager)); }, };服务封装模块注意name采用小驼峰backEndService类采用大驼峰BackEndService// extensions/customExtension/src/services/backEndService/index.js import BackEndService from ./BackEndService; export default function WrappedBackEndService(servicesManager) { return { name: backEndService, create: ({ configuration {} }) { return new BackEndService(servicesManager); }, }; }服务实现类构造函数中可以持有servicesManager从而在服务内部访问其他服务export default class BackEndService { constructor(servicesManager) { this.servicesManager servicesManager; } putAnnotations() { return post(/*...*/); } }类型导出便于消费者获得类型提示// types/index.ts import BackEndService from ../services/BackEndService/BackEndService; export { BackEndService };从ServicesManager.registerService的真实实现可以推断create在自定义服务场景下同样会收到{ configuration, extensionManager, commandsManager, servicesManager }四个依赖因此即使你的工厂函数签名只声明了{ configuration {} }也仍然可以按需取用其余注入项。服务模式生命周期onModeEnter 与 onModeExitOHIF v3 对服务提出了一个重要的模式状态契约服务在模式进入时的状态应当与首次进入或退出后再次进入时保持一致。这一契约用于防止首次显示与后续显示之间因状态残留而产生差异缺陷。具体来说状态的保存与恢复由模式Mode负责模式在onModeExit时保存持久数据在onModeEnter时恢复数据。例如模式可以在退出时保留测量数据、进入时恢复它们。这并不违反契约因为是否应用已缓存的状态是模式自己的决策满足契约后onModeExit在模式自身的onModeExit已经存储完持久数据之后执行服务可以据此清理自己的状态。两个可选的服务钩子onModeEnter服务可以实现该方法用于初始化服务、为进入某个模式做好准备。它会在模式自身的onModeEnter之前被调用onModeExit服务可以实现该方法用于在模式onModeExit完成持久数据存储后清理自身状态确保下次进入模式时服务处于干净、确定的状态。组合起来完整的生命周期时序是进入模式: 服务.onModeEnter → 模式.onModeEnter → ...使用模式 退出模式: 模式.onModeExit保存持久数据→ 服务.onModeExit清理临时状态这种模式负责持久化、服务负责重置的分工保证了同一个研究Study无论被展示一次还是多次服务侧看到的状态都是一致的从架构上消除了状态漂移类缺陷。小结ServicesManager是 OHIF v3 面向服务的架构基石注册任意服务以{ name, create }对象形态通过registerService/registerServices注册create工厂获得configuration、commandsManager、extensionManager、servicesManager四类注入访问全应用通过servicesManager.services按名获取实例altName保证兼容性扩展自定义服务在扩展preRegistration中注册遵循小驼峰实例名 大驼峰类型名 类型导出的命名规范生命周期服务可通过onModeEnter/onModeExit与模式协同维护进入模式时的状态一致性。进一步阅读完整实现见 platform/core/src/services/ServicesManager.ts行为验证见 platform/core/src/services/ServicesManager.test.js默认服务注册清单见 platform/app/src/appInit.js各服务的标准REGISTRATION定义可在 platform/core/src/services 目录下逐一查看。【免费下载链接】ViewersOHIF zero-footprint DICOM viewer and oncology specific Lesion Tracker, plus shared extension packages项目地址: https://gitcode.com/GitHub_Trending/vi/Viewers创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表