DMO 炮孔数据库文件
dimine_python_sdk.lib.io 提供 DmoFile 类用于读写 Dimine 炮孔数据库文件(.dmo)。DMO 本质上是带固定表头(炮孔数据库标准字段,新版 26 列)的 DMT 数据表文件;DmoFile 是类型化模型容器——读取后把炮孔与连线信息解析为 dmo.holes(每孔一个 DmoHole)与 dmo.network(连线拓扑 DmoNetwork)供程序直接使用,加载时对表头做宽松校验(缺列/多列/类型不符只记录告警、不抛异常,见 dmo.header_warnings),保存时按固定表头还原字段类型,避免 pandas dtype 推断丢失 long/binary 等类型。
若要从爆破设计算法(布孔/装药/网络)直接生成或落盘
.dmo,推荐经dimine_python_sdk.lib.blasting的build_dmo/resolve_schedule装配 (见 爆破设计指南)。本文只讲DmoFile本身。
一、加载与保存
从文件加载
读取即解析并缓存 holes / network 类型化模型:
from dimine_python_sdk.lib.io import DmoFile
dmo = DmoFile("炮孔数据库-data.dmo")
print(dmo.holes) # list[DmoHole]——炮孔结构(坐标/孔参数/装药信息)
print(dmo.network) # DmoNetwork——连线拓扑 + 地表起爆时间
print(dmo.network.numbers) # 全部炮孔编号
print(dmo.header_warnings) # 表头非标准告警(如有)
也可以先建空对象再显式加载(链式):DmoFile().load("炮孔数据库.dmo")。
保存到文件
就地修改模型后 save() 写回。只要模型持有了连线信息(network.edges 非空),
保存前会沿连线网络重算「地表起爆时间」:
dmo.holes[0].diameter = 250.0 # 就地改某孔孔径
dmo.network.line_of("A-01", "A-02").delay = 42.0 # 改某条连线的延时雷管
dmo.save("output.dmo", burn_velocity=2000.0) # 另存(无扩展名时自动追加 .dmo)
dmo.save() # 覆盖原文件(用默认/全局燃速)
burn_velocity(导爆管燃速 m/s,缺省取全局 HoleParam.tube_burn_velocity,无 DIMINE_HOME
回退 2000)只影响导爆管雷管的连线燃烧时长;数码电子雷管网络(count_line_burn=False)不需要。
从类型化模型新建并保存
DmoFile() 创建空容器,赋值 holes / network 后保存即生成 .dmo:
from dimine_python_sdk.lib.io import DmoFile
from dimine_python_sdk.lib.io.dmo_file import BlastLine, ChargeSegment, DmoHole, DmoNetwork
dmo = DmoFile()
dmo.holes = [
DmoHole(number="BH-01", region="M01", mouth_x=0.0, mouth_y=0.0, mouth_z=765.0,
bottom_x=0.0, bottom_y=0.0, bottom_z=750.0,
diameter=220.0, depth=15.0, azimuth=0.0, inclination=-90.0,
segments=[ChargeSegment(fill_type="填塞物", length=4.5, delay=-1),
ChargeSegment(fill_type="乳化炸药", length=10.5, delay=350)]),
DmoHole(number="BH-02", region="M01", mouth_x=5.0, mouth_y=0.0, mouth_z=765.0,
bottom_x=5.0, bottom_y=0.0, bottom_z=750.0,
diameter=220.0, depth=15.0, azimuth=0.0, inclination=-90.0),
]
dmo.network = DmoNetwork(
nodes=dmo.holes,
edges={("BH-01", "BH-02"): BlastLine(end_x=5.0, end_y=0.0, end_z=765.0, delay=25.0)},
delays={"BH-01": 0},
initiating_hole="BH-01",
)
dmo.save("new.dmo")
「装药结构」「连线信息」「孔内分段起爆」三个 binary 列在 save() 时由
segments / edges / delays + 孔口/孔底坐标自动编码,无需手工构造二进制。
上下文管理器
from dimine_python_sdk.lib.io import dmo_conn
with dmo_conn("炮孔数据库.dmo") as dmo:
print(dmo.info) # {file, hole_count, edge_count, initiating_hole, header_warnings}
dmo.holes[0].depth = 16.0
dmo.save()
二、固定表头 schema
DMO 文件拥有固定的 26 列标准字段,定义于模块常量 DMO_FIELD_SCHEMA:
from dimine_python_sdk.lib.io import DMO_FIELD_NAMES, DMO_FIELD_TYPES
print(DMO_FIELD_NAMES) # 字段名列表(26 项)
print(DMO_FIELD_TYPES) # {'爆破区域': 'string', '炮孔Handle': 'long', ...}
dmo.schema # [{"name": "爆破区域", "type": "string"}, ...]
字段类型支持 string / long / int / double / binary。标准字段覆盖:
- 标识:
爆破区域、炮孔编号、虚拟炮孔、起爆点炮孔、炮孔Handle - 起爆参数:
起爆延时、所属爆堆、地表起爆时间、孔内起爆时间、孔内分段起爆 - 孔口/孔底坐标:
孔口坐标X/Y/Z、孔底坐标X/Y/Z - 孔参数:
孔径、孔深、方位角、倾角 - 装药参数:
装药半径相对孔径的收缩比例、装药重量、装药长度、单耗面积、装药结构、连线信息
三个时间列易混淆,语义(已用真实 Dimine 文件核对):
起爆延时:仅起爆点孔有意义 —— 从按下开关到燃烧到达该起爆点的延时,非起爆点孔恒为 0。一个网络可有多个起爆点(导爆管/数码电子均可,模型DmoNetwork.initiating_holes),各起爆点等于其自身的「地表起爆时间」,保存时自动写该孔delays值,其余孔写 0。地表起爆时间:从按下总开关到燃烧到达该炮孔的延时,沿连线网络逐孔累积(父孔 + 孔间延时 + 连线燃烧时间 = 距离/导爆管燃速)。数码电子雷管逐孔独立编程、无导爆管传爆,不计连线燃烧时间(模型DmoNetwork.count_line_burn=False),起爆时间 = 父孔 + 孔间延时。count_line_burn不落盘:读取.dmo时由 load 按「地表起爆时间」列与连线拓扑反推(含 travel→导爆管、纯 relay→数码电子,证据不足时默认导爆管),使数码电子文件的 load→save 重算不误加连线燃烧时长,并按「首爆点锚 0」推导。孔内起爆时间=地表起爆时间+ 孔口到第一段炸药段的雷管延时之和(ChargeSegment.delay)。孔内分段起爆(binary):只写不读,不落入模型。保存时按装药分段 + 孔口/孔底坐标 +delays推导——每个雷管延时 >= 0 的分段在其段中点产生一个起爆点(坐标沿孔口→孔底线性插值,时间 = 地表起爆时间 + 该段雷管延时),编码为 Dimine 原生二进制(int32 条数 + 每条 float64 x/y/z + int32 t)。
对应 DmoNetwork.delays(地表起爆时间)。只要模型持有了连线信息(edges 非空,
即设计过/修改过连线网络),save() 保存前即沿连线网络重算 delays:根孔(各起爆点等
无父孔者,导爆管可有多个)锚定各自 delays 里已有值(=「起爆延时」列值,代表从总开关起的
绝对起点),其后代 = 父孔地表起爆时间 + 孔间延时雷管 + 连线燃烧时长(孔口距离 / 导爆管
燃速),燃速缺省取全局 HoleParam.tube_burn_velocity(无 DIMINE_HOME 时回退 2000 m/s,
也可经 save(burn_velocity=…) 显式指定);数码电子雷管时 count_line_burn=False,不累加
连线燃烧时长,且无起爆延时锚——首爆点(入度 0 节点,可有多个)地表起爆时间锚 0,只累加
孔间延时雷管;无连线时 delays 原样透传。起爆点另受「起爆延时」
列回填约束。孔内起爆时间不落入模型,保存时按上式自动推导(= delays + 首段炸药
雷管延时之和)。连线拓扑 DmoNetwork.edges 为 { (源孔编号, 目标孔编号): BlastLine }
单一数据源:每条边的 BlastLine.delay 是该连线安装的延时雷管(真实文件如 17ms,
不是两孔时间差),箭头/字高/线宽为显示属性,读入后保存原样写回——重算只影响
delays(地表起爆时间),不会把连线延时改写成时间差。
三、表头宽松校验
加载时 DmoFile 会对表头做宽松校验,非标准列或缺列不会抛异常,而是记录到 header_warnings:
dmo = DmoFile("旧版炮孔数据.dmo")
for w in dmo.header_warnings:
print(w)
# 缺少标准列: ['孔内分段起爆', '连线信息']
# 含非标准列: ['孔内延时']
# 字段 '炮孔Handle' 类型不符: 期望 long, 实际 double
校验规则: - 缺标准列:记录告警,缺失标准列不属于模型(对应字段取默认 / 空) - 含非标准列:记录告警,非标准列不进入模型,保存时不会写回 - 类型不符:记录告警,保存时仍按 schema 中的标准类型还原
dmo.info 返回摘要 {file, hole_count, edge_count, initiating_hole, header_warnings},
其中 header_warnings 即上述加载告警。
四、类型化数据访问
DmoFile 不直接暴露内部数据表,程序通过 holes 与 network 两个类型化模型访问:
holes:每孔一个DmoHole——炮孔编号、坐标、孔参数、装药信息(charge_weight/charge_length/segments…);network:DmoNetwork连线图——节点即holes里的DmoHole,边为{(源孔编号, 目标孔编号): BlastLine}。
# —— 炮孔(DmoHole)——
len(dmo.holes) # 炮孔数
dmo.holes[0].number # 炮孔编号
dmo.holes[0].depth # 孔深 m
dmo.holes[0].diameter # 孔径 mm
dmo.holes[0].stemming_length # 填塞长度 m(由 segments 计算)
dmo.holes[0].spacer_length # 间隔总长度 m(由 segments 计算)
for seg in dmo.holes[0].segments: # 装药分段(ChargeSegment)
print(seg.fill_type, seg.length, seg.delay)
# —— 连线网络(DmoNetwork)——
dmo.network.numbers # 炮孔编号列表
dmo.network.edge_pairs # 连线 [(from, to), ...]
"A-01" in dmo.network # 孔是否存在
dmo.network.get("A-01") # 按编号取 DmoHole(None 若无)
dmo.network.line_of("A-01", "A-02") # 该连线的 BlastLine(None 若无)
dmo.network.outgoing("A-01") # 出边相邻孔
dmo.network.incoming("A-01") # 入边相邻孔
dmo.network.neighbors("A-01") # 全部相邻孔
dmo.network.degree("A-01") # 度
dmo.network.delays # {孔号: 地表起爆时间 ms}
dmo.network.initiating_holes # 起爆点孔编号(可多个)
# —— 模型就地修改后 save() 生效 ——
dmo.network.connect("A-01", "A-03") # 加一条连线(默认 BlastLine)
dmo.network.disconnect("A-01", "A-03") # 删连线
dmo.network.add_hole(DmoHole(number="A-09", ...)) # 加孔
dmo.network.remove_hole("A-09") # 删孔(连带移除其连线)
dmo.network.line_of("A-01", "A-02").delay = 42.0 # 改某条连线延时雷管
dmo.save()
# —— 汇总 / schema ——
dmo.info # {file, hole_count, edge_count, initiating_hole, header_warnings}
dmo.schema # 26 列 [{"name": ..., "type": ...}, ...]
dmo.recompute_delays(burn_velocity=...)可显式沿连线网络重算「地表起爆时间」(有连线时save()前自动调用,一般无需手动)。底层仍按标准 26 列表读写;DmoFile不继承DmtFile,DMT 通用数据表的 DataFrame 式访问见 DMT 数据表文件。
五、异常处理
from dimine_python_sdk.lib.io import DmoError, DmoLoadError, DmoSaveError
try:
dmo = DmoFile("not_exist.dmo")
except DmoLoadError as e:
print(f"加载失败: {e}")
try:
dmo = DmoFile()
dmo.save() # 未指定路径
except DmoSaveError as e:
print(f"保存失败: {e}")
异常层次:
- DmoError(RuntimeError) — 基类
- DmoLoadError(DmoError) — 加载失败
- DmoSaveError(DmoError) — 保存失败
参考链接
- DMT 数据表文件 — 基础数据表访问接口
- API 参考:dmo_file