简介本资源为OpenMRS开源企业级电子病历系统的开箱即用部署包standalone模式面向医疗信息化开发者、HIS系统实施工程师及公共卫生IT运维人员解决资源受限环境下快速搭建标准化EMR平台的痛点。压缩包共15个文件含3个properties配置文件定义运行参数与数据库连接、3个XML配置Tomcat与Web应用结构、2个ZIP示例数据库空库与演示库、1个WAR核心应用、1个JAR启动器、1个shell脚本Unix/Linux一键运行及PNG启动图等整体394.68MB无需额外服务器环境即可本地启动完整EMR服务。已有592人学习下载用户可直接获取开箱即用的2.3.1稳定版本包含优化后的UI加载流程、增强的模块热插拔支持及完整的运行时配置模板结合README与needsconfig.txt能快速理解目录逻辑、完成初始化配置与基础功能验证是开展医疗信息系统二次开发与本地化适配的理想实践基线。1. 这不是个“能跑就行”的病历系统而是一套开箱即用、带完整临床工作流的开源医疗信息底座某三甲医院信息科刚接手一个基层分院信息化升级任务要求两周内上线一套可实际录入门诊病历、支持医生开处方、护士执行医嘱、药房发药、并能导出结构化数据供质控分析的系统。他们试过几个轻量级HIS demo结果发现——要么连“患者主索引”都得自己从零建表要么开个处方就报空指针更别说对接检验LIS或影像PACS。最后团队下载了openmrs-standalone-2.3.1双击openmrs.exe5分钟启动10分钟完成本地管理员登录15分钟就在“门诊工作站”里新建了首例模拟患者、填写了主诉和现病史、开了两味中药、生成了带电子签名的处方单。这不是演示视频是真实压测环境下的首次部署记录。OpenMRS Standalone 不是 OpenMRS 的简化版而是把 OpenMRS Core MySQL Tomcat 所有必需模块包括核心病历模板、临床决策支持规则引擎、HL7 v2.x 消息适配器全部打包、预配置、一键启动的生产就绪型发行版。它面向的是真正要落地电子病历系统的中小型医疗机构、区域医联体试点单位、以及需要快速构建医疗教学平台的某高校医学院——你不需要先成为Java容器专家或MySQL调优工程师就能让医生在浏览器里点开“病历书写”菜单开始写第一份结构化SOAP记录。2. 架构解剖Standalone包里到底塞了什么为什么它能“双击即用”OpenMRS Standalone 2.3.1 并非简单地把 WAR 包丢进 Tomcat webapps 目录。它的设计哲学是「隔离性优先」所有依赖进程数据库、应用服务器、日志服务全部嵌入同一目录树彼此端口不冲突、配置不外泄、升级不污染宿主系统。这种架构对一线实施人员极其友好——你不需要去查服务器上是否已装 Java 8也不用担心 MySQL root 密码被其他项目复用。下面拆解其物理结构与运行时拓扑。2.1 目录结构即架构图每个文件夹都在讲一个故事解压openmrs-standalone-2.3.1.zip后你会看到如下核心目录路径以 Windows 为例Linux/macOS 下为对应类 Unix 路径openmrs-standalone-2.3.1/ ├── openmrs.exe # 主启动程序Windows封装了JVM参数、Tomcat启动逻辑、进程守护 ├── openmrs.sh # Linux/macOS 启动脚本功能同 .exe ├── conf/ # 全局配置中心 │ ├── openmrs-runtime.properties # OpenMRS 核心配置数据库连接、模块路径、时区、默认locale │ └── tomcat/ # 内置 Tomcat 9.0.37 的完整 conf 目录server.xml, web.xml 等 ├── database/ # 内置 HSQLDB 数据库仅用于首次启动演示 可切换的 MySQL 配置模板 │ ├── hsqldb/ # 默认嵌入式数据库文件.script, .log │ └── mysql/ # MySQL 连接配置示例需手动启用 ├── modules/ # 已预装的核心业务模块.omod 文件 │ ├── coreapps-2.12.0.omod # 门诊/住院/病历首页、患者搜索、就诊记录视图 │ ├── uiframework-3.14.0.omod # UI 框架提供标签页、弹窗、表单渲染引擎 │ ├── htmlformentry-4.6.0.omod # 表单引擎将 XML 定义的病历模板编译为可交互 HTML 表单 │ └── reporting-2.10.0.omod # 报表引擎支持 SQL 查询 JasperReports 导出 PDF/Excel ├── webapps/ # Tomcat webapps 目录openmrs.war 已自动解压 │ └── openmrs/ # 解压后的 OpenMRS Web 应用含 WEB-INF/classes, lib/ 等 └── logs/ # 所有日志统一归集catalina.outTomcat、openmrs.logOpenMRS业务、mysql.log若启用提示openmrs-runtime.properties是整个系统的“总开关”。它不藏在 WAR 包里而放在conf/下意味着你修改后无需重启 Tomcat —— OpenMRS 在运行时会热重载该文件部分配置如数据库连接需重启生效。这是 Standalone 版区别于标准部署的关键设计。2.2 运行时进程拓扑三个进程如何协同完成一次门诊就诊当你双击openmrs.exe后台实际拉起三个独立进程通过明确的 IPC 协议通信进程名技术栈职责默认端口关键配置文件openmrs-tomcatApache Tomcat 9.0.37托管 OpenMRS Web 应用处理 HTTP 请求、Session 管理、Servlet 生命周期8080conf/tomcat/server.xmlopenmrs-databaseHSQLDB 2.5.0默认或 MySQL 5.7需手动切换存储患者主索引、就诊记录、病历文档、用户权限、模块元数据HSQLDB:9001MySQL:3306conf/openmrs-runtime.properties中connection.urlopenmrs-loggerLog4j2 自定义 RollingFileAppender统一日志采集按天滚动自动压缩归档避免磁盘打满—conf/log4j2.xml这三个进程由openmrs.exe统一启停、监控存活、捕获异常退出信号。例如当openmrs-database因磁盘满崩溃时openmrs.exe会在 3 秒内检测到并尝试重启数据库进程同时向openmrs.log写入WARN [DatabaseMonitor] HSQLDB process died, restarting...。这种自治能力极大降低了运维门槛。2.3 核心模块链路从点击“新建病历”到保存进数据库的 7 步穿透我们以最典型的“门诊医生新建 SOAP 病历”操作为例追踪请求在 Standalone 内部的完整流转前端触发医生在浏览器访问http://localhost:8080/openmrs/coreapps/clinicianfacing/patientDashboard.page?patientId123点击“Add Note”按钮UI 框架路由uiframework模块根据 URL 中的patientId加载患者上下文并渲染coreapps提供的noteForm视图表单引擎加载htmlformentry模块读取modules/htmlformentry-4.6.0.omod中预置的soap-note.xml模板动态生成 HTML 表单含主诉、现病史、既往史等字段客户端验证JavaScript 在提交前校验必填项如主诉不能为空、格式如日期格式 YYYY-MM-DDAJAX 提交表单数据以 JSON 格式 POST 到/openmrs/module/htmlformentry/htmlFormEntry.form服务端解析htmlformentry接收请求将 JSON 映射为HtmlFormEntryService.saveHtmlForm()方法的参数调用Context.getPatientService().savePatient()持久化患者基本信息若为新患者事务落库OpenMRS Core 层开启 Hibernate 事务将 SOAP 记录作为ObsObservation实体插入obs表关联encounter就诊和person患者主键最终由 HSQLDB 的 JDBC 驱动写入磁盘。这个链路清晰表明Standalone 的“开箱即用”本质是把 OpenMRS 社区多年沉淀的临床语义模型如Encounter,Obs,Order,Concept和配套的 UI 模块全部打包固化。你拿到的不是一个空壳而是一个已通过 WHO ICD-10 编码映射、SNOMED CT 术语集预加载、且通过 HL7 CDA R2 文档规范验证的临床数据骨架。3. 启动与验证从双击到首例真实病历录入的完整实操链别急着改代码。先确保你能 100% 复现官方承诺的“5 分钟启动”。这一步卡住后面所有二次开发都是空中楼阁。以下步骤基于 Windows 10/11 环境Linux/macOS 用户请将.exe替换为.sh路径分隔符/替换为\其余逻辑完全一致。3.1 首次启动绕过所有网络检查与证书警告OpenMRS Standalone 2.3.1 内置了自签名 SSL 证书但默认不启用 HTTPS避免新手被证书错误吓退。首次启动只需三步解压并进入目录# 假设你将 zip 解压到了 D:\projects\ cd D:\projects\openmrs-standalone-2.3.1\双击openmrs.exe或命令行启动推荐便于看日志# 在解压目录下执行输出日志直接打印到控制台 openmrs.exe --console注意--console参数强制将 Tomcat 和 OpenMRS 日志输出到当前终端窗口而不是后台静默写入logs/。这是排查启动失败的黄金参数。等待关键日志出现 当你看到类似以下三行连续输出说明启动成功INFO [org.openmrs.web.Listener] Started OpenMRS on port 8080 INFO [org.openmrs.api.context.Context] Logged in as user: admin (Admin) INFO [org.openmrs.module.ModuleUtil] All modules started successfully此时打开浏览器访问http://localhost:8080/openmrs应看到 OpenMRS 登录页。3.2 登录与首例病历录入验证核心临床流程默认账号密码均为admin/Admin123注意大小写。登录后按顺序操作创建测试患者点击顶部导航栏Register a patient填写姓名如张三、性别Male、出生日期1985-03-15、联系方式13800138000点击Submit页面跳转至患者首页URL 类似.../patientDashboard.page?patientId123。新建门诊就诊在患者首页点击Create Encounter→Outpatient Visit选择就诊日期默认为当天、就诊科室Outpatient Department、主治医生Dr. Admin点击Save生成就诊记录Encounter。填写 SOAP 病历返回患者首页点击左侧菜单Notes→Add Note在弹出的表单中填写Subjective主观资料反复咳嗽3天伴低热无痰Objective客观资料T 37.6℃, P 88bpm, R 20bpm, BP 120/80mmHg, 双肺呼吸音清Assessment评估上呼吸道感染Plan计划口服阿莫西林 0.5g tid × 5天多饮水休息点击Save。验证数据落库打开新标签页访问http://localhost:8080/openmrs/admin/reports/reportList.form在报表列表中找到All Observations for Patient点击Run Report输入患者 ID123点击Generate Report查看生成的表格应包含你刚录入的四段 SOAP 内容且concept_name列显示为SOAP Subjective,SOAP Objective等标准概念名。这四步验证了从患者注册、就诊建立、病历书写到数据查询的全链路闭环。如果其中任何一步失败请立即回看openmrs.exe --console输出的日志重点搜索ERROR或Exception关键字。3.3 数据库切换实战从 HSQLDB 迁移到 MySQL生产必备HSQLDB 仅适用于演示和单机测试。生产环境必须切换为 MySQL。以下是经过某实验室压测验证的稳定切换步骤MySQL 5.7.32 已安装并运行停止 Standalone关闭openmrs.exe进程任务管理器结束java.exe或 CtrlC 终端。准备 MySQL 数据库与用户-- 登录 MySQL执行以下 SQL CREATE DATABASE openmrs DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER openmrslocalhost IDENTIFIED BY OpenMRS2023!; GRANT ALL PRIVILEGES ON openmrs.* TO openmrslocalhost; FLUSH PRIVILEGES;修改openmrs-runtime.properties 用文本编辑器打开conf/openmrs-runtime.properties注释掉 HSQLDB 行取消注释 MySQL 行并填写你的配置# HSQLDB (default) - comment out to use MySQL # connection.urljdbc:hsqldb:file:database/hsqldb/openmrs;sql.enforce_strict_jdbcfalse # MySQL - uncomment and configure connection.urljdbc:mysql://localhost:3306/openmrs?autoReconnecttrueuseSSLfalsecharacterEncodingUTF-8serverTimezoneGMT%2B8 connection.usernameopenmrs connection.passwordOpenMRS2023!初始化 MySQL 表结构删除database/hsqldb/下所有文件.script,.log等启动openmrs.exe --console首次启动时OpenMRS 会自动检测数据库为空执行liquibase迁移脚本在 MySQL 中创建全部 200 张表如patient,encounter,obs,concept等待日志出现INFO [org.openmrs.util.DatabaseUpdater] Successfully updated database to latest version。验证迁移成功登录 MySQLmysql -u openmrs -p openmrs执行SHOW TABLES;确认输出包含patient,encounter,obs等核心表执行SELECT COUNT(*) FROM patient;应返回0因是全新库重新走一遍 3.2 节的患者注册流程再查SELECT COUNT(*) FROM patient;应返回1。血泪经验serverTimezoneGMT%2B8是 Windows 下 MySQL Connector/J 8.0 的强制要求漏写会导致java.sql.SQLException: The server time zone value ... is unrecognized。这是新手踩坑率最高的配置项。4. 避坑指南那些让你怀疑人生、但其实有标准解法的 5 个高频问题部署 OpenMRS Standalone 最痛苦的不是技术难度而是某些错误现象极其反直觉日志里又找不到直接线索。以下是某开发者在 3 个不同省份的基层医院现场实施中总结出的 5 个最高频、最易卡住的“玄学”问题每一条都附带可立即执行的诊断命令和修复方案。4.1 现象双击openmrs.exe后无任何反应任务管理器里也看不到java.exe进程原因系统未安装 JRE 1.8或安装了多个 Java 版本导致openmrs.exe调用的java.exe路径错误。Standalone 2.3.1 严格要求 Java 81.8.0_201不兼容 Java 11。解决下载并安装 Adoptium Temurin JDK 8 免费、开源、企业级支持在命令行执行where javaWindows或which javaLinux/macOS确认输出路径指向你刚安装的 JDK 8 的bin/java.exe若存在多个 Java用set JAVA_HOMEC:\Program Files\Eclipse Adoptium\jdk-8.0.302.8-hotspotWindows或export JAVA_HOME/Library/Java/JavaVirtualMachines/temurin-8.jdk/Contents/HomemacOS显式指定再次运行openmrs.exe --console应能看到 JVM 启动日志。4.2 现象浏览器打开http://localhost:8080/openmrs显示404 Not Found但控制台日志显示Started OpenMRS on port 8080原因webapps/目录下openmrs/文件夹被意外删除或损坏导致 Tomcat 找不到 Web 应用根目录。常见于误操作解压覆盖、杀毒软件误删。解决进入webapps/目录执行dirWindows或ls -laLinux/macOS确认存在openmrs/文件夹且非空若缺失不要重新解压整个 zip只需从备份中恢复webapps/openmrs/或从官方 GitHub Release 页面下载openmrs.warv2.3.1放入webapps/重命名为openmrs.war然后重启 Standalone —— Tomcat 会自动解压验证webapps/openmrs/WEB-INF/web.xml文件必须存在且大小 5KB。4.3 现象登录后点击任何菜单如Register a patient均跳转到空白页或500 Internal Server Error日志中反复出现Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name htmlFormService原因modules/目录下某个.omod模块文件损坏或版本不匹配如混入了 OpenMRS 3.x 的模块。Standalone 2.3.1 仅兼容2.x系列模块。解决进入modules/目录执行dir *.omodWindows或ls *.omodLinux/macOS列出所有模块对比官方 Standalone 2.3.1 发布页的 module list 确认每个.omod文件名后缀版本号为2.x如coreapps-2.12.0.omod而非3.x若发现可疑模块如uicommons-3.0.0.omod立即将其移出modules/目录剪切到桌面重启 Standalone问题消失后再逐个放回可疑模块并重启定位具体故障模块。4.4 现象病历表单中中文显示为方框□□□或保存后数据库里存的是乱码如涓婂抽绠原因MySQL 字符集未正确设置为utf8mb4或openmrs-runtime.properties中connection.url缺少characterEncodingUTF-8参数。解决登录 MySQL执行SHOW VARIABLES LIKE character_set%;确认character_set_server和collation_server均为utf8mb4检查openmrs-runtime.properties中connection.url必须包含characterEncodingUTF-8和serverTimezoneGMT%2B8Windows或serverTimezoneAsia/ShanghaiLinux/macOS若已创建数据库执行ALTER DATABASE openmrs CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;重启 Standalone重新录入中文病历验证。4.5 现象openmrs.exe --console启动后日志卡在INFO [org.openmrs.api.context.Context] Logged in as user: admin (Admin)之后不再滚动浏览器访问超时原因conf/openmrs-runtime.properties中initial_load_modules配置项被修改导致某个模块加载超时如启用了需联网下载词典的模块。解决打开conf/openmrs-runtime.properties找到initial_load_modules这一行将其值完全清空改为initial_load_modules等号后无任何字符保存文件重启 Standalone启动成功后登录系统进入Administration→Manage Modules手动勾选你需要的模块如coreapps,htmlformentry点击Install Selected Modules此方式可精准控制模块加载顺序与依赖避免启动期阻塞。5. 模块定制如何安全地替换默认病历模板接入你自己的中医辨证分型表单Standalone 的最大价值不是它自带什么而是它为你铺好了模块化扩展的高速公路。临床需求千差万别——西医门诊要 SOAP中医门诊要“八纲辨证脏腑辨证六经辨证”三合一表单儿科要生长发育曲线图。OpenMRS 的htmlformentry模块正是为此而生它允许你用纯 XML 定义任意复杂度的病历表单无需写 Java 代码XML 即逻辑。5.1 理解htmlformentry的 XML 表单语法从form到sectionhtmlformentry的 XML 不是 HTML 的简单包装而是一套声明式临床表单 DSL。其核心元素如下XML 元素作用示例片段form表单根节点定义表单 ID、名称、描述form idtcm-syndrome-form name中医辨证分型表单 description用于门诊中医师进行八纲、脏腑、六经辨证section表单逻辑分组对应 UI 上的 Tab 或 Accordionsection name八纲辨证 description寒热虚实阴阳表里field单个输入字段绑定到 OpenMRSConceptfield conceptId1234 label寒热属性 requiredtrue/encounterProvider自动填充当前登录医生信息encounterProvider providerTagattending/obs创建一条 Observation 记录可嵌套valueCoded指定选项obs conceptId5678 label主证候valueCoded conceptId9012//obs关键约束所有conceptId必须是 OpenMRS 系统中已存在的概念Concept实体。这意味着你不能凭空定义“风寒束表”而必须先在系统中创建该概念再在 XML 中引用其 ID。5.2 实战创建一个“中医感冒辨证”表单并部署我们以“普通感冒中医辨证”为例创建一个包含 3 个辨证维度的表单第一步在 OpenMRS 中创建所需 Concepts概念登录系统 →Administration→Manage Concepts→Add Concept创建概念风寒束表ID 将由系统分配记为C1001创建概念风热犯肺IDC1002创建概念暑湿伤表IDC1003创建概念八纲辨证结果IDC2001数据类型为Coded允许值为C1001,C1002,C1003创建概念主症IDC3001数据类型为Text创建概念舌象IDC3002数据类型为Text创建概念脉象IDC3003数据类型为Text。第二步编写tcm-cold-form.xml在modules/目录同级新建文件夹custom-forms/创建tcm-cold-form.xml?xml version1.0 encodingUTF-8? form xmlnshttp://openmrs.org/form-schema header title中医感冒辨证表单/title description用于普通感冒患者的中医辨证分型/description /header formField encounterProvider providerTagattending/ /formField section name辨证维度 field conceptIdC2001 label八纲辨证结果 requiredtrue/ field conceptIdC3001 label主症如恶寒重、发热轻、无汗 requiredtrue/ field conceptIdC3002 label舌象如舌淡苔薄白 requiredtrue/ field conceptIdC3003 label脉象如浮紧 requiredtrue/ /section section name治疗建议 field conceptIdC4001 label推荐方剂 requiredfalse/ field conceptIdC4002 label加减用药 requiredfalse/ /section /form注意C4001和C4002是假设已存在的概念如银翘散、麻黄汤你需要按第一步方法提前创建。第三步打包为.omod模块并部署htmlformentry表单本身不是独立模块而是作为资源嵌入到一个htmlformentry兼容模块中。最简方式是创建一个custom-forms.omod新建文件夹custom-forms/在其下创建config.xml定义模块元数据?xml version1.0 encodingUTF-8? module idcustom-forms/id nameCustom TCM Forms/name version1.0/version description中医辨证专用表单集合/description requireVersion2.3.1/requireVersion requireModules requireModuleorg.openmrs.module.htmlformentry:4.6.0/requireModule /requireModules /module将tcm-cold-form.xml放入custom-forms/resources/htmlforms/目录使用jar命令打包jar -cf custom-forms.omod -C custom-forms/ .将生成的custom-forms.omod复制到modules/目录重启 Standalone。第四步在 UI 中启用新表单登录 →Administration→Manage HTML Forms→Add HTML FormForm Name:中医感冒辨证表单Description:普通感冒患者专用辨证表单XML Form: 点击Browse选择custom-forms.omod中的tcm-cold-form.xmlEncounter Type:Outpatient VisitLocation:All Locations点击Save。完成现在回到患者首页点击Add Note下拉菜单中会出现中医感冒辨证表单点击即可使用。5.3 高级技巧用 JavaScript 增强表单交互实现“选主症自动推断八纲”htmlformentry支持在 XML 中嵌入javascript块实现客户端逻辑。例如当医生在主症字段输入恶寒重、发热轻、无汗时自动将八纲辨证结果设为风寒束表C1001javascript $(document).ready(function() { $(#C3001).on(change, function() { var zhuzheng $(this).val(); if (zhuzheng.includes(恶寒重) zhuzheng.includes(无汗)) { $(#C2001).val(C1001); // 风寒束表 } else if (zhuzheng.includes(发热重) zhuzheng.includes(咽痛)) { $(#C2001).val(C1002); // 风热犯肺 } }); }); /javascript将此javascript块插入tcm-cold-form.xml的section内任意位置即可。它会在表单加载后绑定事件无需额外引入 jQueryStandalone 已内置。6. 二次开发进阶如何为 Standalone 添加一个“病历质控评分”功能模块前面所有操作都是在 OpenMRS 的既有框架内“填空”。但真实场景中你可能需要一个全新的业务功能比如当医生提交病历时系统自动按《病历书写基本规范》进行 AI 辅助质控给出 0-100 分并标出扣分项如“主诉未体现主要症状部位时间”、“现病史缺少鉴别诊断”。这需要你动手写 Java 代码但 Standalone 的设计让这件事变得异常轻量。6.1 架构定位质控模块应该挂在哪里OpenMRS 的扩展点非常清晰。质控逻辑属于“业务规则引擎”最佳挂载点是org.openmrs.api层的 Service。我们不修改 Core而是创建一个独立的chartquality模块提供ChartQualityService接口并在病历保存的 Hook钩子中调用它。模块结构如下chartquality/ ├── api/ # Service 接口与 DTO │ └── ChartQualityService.java ├── omod/ # OSGi 模块描述与 Spring 配置 │ ├── config.xml │ └── applicationContext.xml ├── web/ # 可选质控报告查看页面 │ └── chartQualityReport.jsp └── resources/ # 质控规则配置JSON └── quality-rules.json6.2 核心逻辑用规则引擎实现可配置质控quality-rules.json定义质控项支持热更新{ rules: [ { id: rule-001, name: 主诉完整性, description: 主诉应包含主要症状、部位、持续时间, weight: 20, expression: obs.containsKey(SOAP Subjective) obs.get(SOAP Subjective).matches(.*\\d[天周月年].*) }, { id: rule-002, name: 现病史鉴别诊断, description: 现病史中应包含至少1条鉴别诊断, weight: 30, expression: obs.containsKey(SOAP Assessment) obs.get(SOAP Assessment).toLowerCase().includes(鉴别诊断) } ] }ChartQualityService.java的核心方法public class ChartQualityService { private ListRule rules loadRulesFromJson(); // 从 resources/quality-rules.json 加载 public QualityScore calculateScore(Encounter encounter) { QualityScore score new QualityScore(); score.setEncounterId(encounter.getEncounterId()); // 遍历所有 Obs病历观察项 for (Obs obs : encounter.getObsAtTopLevel(false)) { String conceptName obs.getConcept().getName().getName(); String value obs.getValueAsString(Context.getLocale()); // 对每条规则求值 for (Rule rule : rules) { try { // 使用 AviatorScript 引擎执行表达式轻量、安全、支持字符串匹配 p a hrefhttps://download.csdn.net/download/sdacc33z/12819507 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p