Files
AutoBOM/VBA_BOMConverter/Modules/M06_ModelParser.bas
Misaka_Company 1d5bad86f7
All checks were successful
NTFY Notification / notify (push) Successful in 4s
feat: add preprocessing with value mapping to BOM Extraction System
Add a preprocessing step to the BOM Extraction System to enhance parameter
extraction with mapping values from "对照表" worksheet. For azxs (安装形式)
and lcfw (量程范围), the system now stores both the raw parsed value AND the
mapped value from the lookup table, enabling flexible dual-value matching.

Changes:
- Create M06A_Mapper.bas: New module for value mapping
  - Load azxs/lcfw mappings from "对照表" worksheet
  - Return dual-value format "raw,mapped" (e.g., "A0,径向")
  - Support graceful degradation if "对照表" not found
  - Define constants locally to avoid VBA cross-module reference issues

- Modify M06_ModelParser.bas:
  - Extract raw parameter values first
  - Apply mapping via M06A_Mapper if initialized
  - Store dual values for azxs and lcfw parameters

- Modify M07_BOMMatcher.bas:
  - Update EvaluateCellCondition() to support dual-value matching
  - Split by comma and check if ANY value matches
  - Backward compatible with single-value parameters

- Modify M09_BOMExtractor.bas:
  - Initialize M06A_Mapper during RunBOMExtraction()
  - Add WorksheetExists() helper function
  - Log warning if "对照表" worksheet not found
  - Define MAPPING_SHEET_NAME constant locally

- Modify M04_Config.bas:
  - Add mapping table configuration constants
  - MAPPING_SHEET_NAME, MAPPING_COL_LCFW_KEY, etc.

- Update CLAUDE.md:
  - Document new M06A_Mapper module
  - Update data flow diagram with mapping step
  - Document dual-value matching behavior
  - Update system architecture diagram

- Update docs/RunBOMExtraction_运行机制详解.md:
  - Add M06A_Mapper to all diagrams and documentation
  - Add detailed value mapping section
  - Update sequence diagrams with mapping flow
  - Update parameter examples with dual-value format
  - Bump documentation version to 3.0

Example:
  Input:  Product model "YTHN-100.A0.531.G123.M04.Y3"
  Parse:  azxs="A0", lcfw="M04"
  Map:    azxs="A0,径向", lcfw="M04,高压"
  Match:  BOM库 with azxs="径向" OR "A0" → MATCH

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-12 17:52:03 +08:00

335 lines
11 KiB
QBasic
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
' ==============================================================================
' 模块: M06_ModelParser
' 职责: 产品型号解析,从完整产品型号中提取关键参数
'
' 产品型号结构: [表头]|[表盘]|[附件]|[法兰隔膜]
' 表头结构: [型号]-[公称外径].[安装形式].[壳体形式].[过程连接&接液材质].[量程范围].[仪表特性]
'
' 示例: YTHN-100.A0.531.G123.M04.Y3|BP-088.2312.B09.0A3
' 表头: YTHN-100.A0.531.G123.M04.Y3
' azxs: A0, bkxs: 531, gclj: G12, jycz: 3, lcfw: M04, fjgn: Y3
'
' 注意: 仅处理表头部分,其他部分(表盘、附件、法兰隔膜)暂时丢弃
' ==============================================================================
Option Explicit
' 模块级变量 - 错误记录器
Private g_Logger As clsErrorLogger
' ------------------------------------------------------------------------------
' 初始化型号解析器
' ------------------------------------------------------------------------------
Public Sub InitModelParser(logger As clsErrorLogger)
Set g_Logger = logger
End Sub
' ------------------------------------------------------------------------------
' 主入口: 解析产品型号
'
' 输入:
' modelString - 完整的产品型号字符串
'
' 输出:
' Object (Scripting.Dictionary) - 包含提取的参数
' 键值对: "xh"->型号, "gcwj"->公称外径, "azxs"->安装形式,
' "bkxs"->表壳形式, "gclj"->过程连接, "jycz"->接液材质,
' "lcfw"->量程范围, "fjgn"->附加功能
'
' 示例:
' Set params = ParseProductModel("YTHN-100.A0.531.G123.M04.Y3|BP-088.2312.B09.0A3")
' ' params("azxs") = "A0"
' ' params("gclj") = "G12"
' ' params("jycz") = "3"
' ------------------------------------------------------------------------------
Public Function ParseProductModel(ByVal modelString As String) As Object
On Error GoTo ErrorHandler
Dim params As Object
Set params = CreateObject("Scripting.Dictionary")
' 预处理:去除前后空格
modelString = Trim(modelString)
' 如果型号为空,返回空字典
If Len(modelString) = 0 Then
Set ParseProductModel = params
Exit Function
End If
' 步骤1: 提取表头部分(第一个管道符之前的部分)
Dim headerPart As String
headerPart = ExtractHeaderPart(modelString)
If Len(headerPart) = 0 Then
If Not g_Logger Is Nothing Then
g_Logger.Record 0, "M06.ParseProductModel", "ModelParseError", _
"无法提取表头部分,型号可能为空或格式错误", modelString
End If
Set ParseProductModel = params
Exit Function
End If
' 步骤2: 分割表头为段
Dim segments As Variant
segments = SplitHeaderPart(headerPart)
If Not IsArray(segments) Then
If Not g_Logger Is Nothing Then
g_Logger.Record 0, "M06.ParseProductModel", "ModelParseError", _
"表头分割失败", headerPart
End If
Set ParseProductModel = params
Exit Function
End If
' 步骤3: 验证段数是否足够
If UBound(segments) - LBound(segments) + 1 < MODEL_HEADER_MIN_SEGMENTS Then
If Not g_Logger Is Nothing Then
g_Logger.Record 0, "M06.ParseProductModel", "ModelParseError", _
"表头段数不足,需要至少" & MODEL_HEADER_MIN_SEGMENTS & "段,实际" & _
(UBound(segments) - LBound(segments) + 1) & "段", headerPart
End If
End If
' 步骤4: 提取型号和公称外径预留暂不参与BOM匹配
Call ExtractModelAndSize(headerPart, params)
' 步骤5: 提取各参数
' 注意segments(0)是"型号-公称外径"需要从segments(1)开始提取参数
' 提取原始值
Dim azxsRaw As String, bkxsRaw As String
Dim gcljRaw As String, jyczRaw As String
Dim lcfwRaw As String, fjgnRaw As String
If UBound(segments) - LBound(segments) + 1 >= 2 Then
azxsRaw = ExtractAzxs(segments(1))
End If
If UBound(segments) - LBound(segments) + 1 >= 3 Then
bkxsRaw = ExtractBkxs(segments(2))
End If
If UBound(segments) - LBound(segments) + 1 >= 4 Then
Call ExtractGcljAndJycz(segments(3), gcljRaw, jyczRaw)
End If
If UBound(segments) - LBound(segments) + 1 >= 5 Then
lcfwRaw = ExtractLcfw(segments(4))
End If
If UBound(segments) - LBound(segments) + 1 >= 6 Then
fjgnRaw = ExtractFjgn(segments, 5)
End If
' 步骤6: 应用映射(如果映射器已初始化)
If M06A_Mapper.IsInitialized() Then
' 应用azxs和lcfw映射返回双值格式raw,mapped
If Len(azxsRaw) > 0 Then
params("azxs") = M06A_Mapper.MapAzxs(azxsRaw)
End If
If Len(lcfwRaw) > 0 Then
params("lcfw") = M06A_Mapper.MapLcfw(lcfwRaw)
End If
Else
' 映射器未初始化,使用原始值
If Len(azxsRaw) > 0 Then
params("azxs") = azxsRaw
End If
If Len(lcfwRaw) > 0 Then
params("lcfw") = lcfwRaw
End If
End If
' 添加其他参数(不需要映射)
If Len(bkxsRaw) > 0 Then
params("bkxs") = bkxsRaw
End If
If Len(gcljRaw) > 0 Then
params("gclj") = gcljRaw
End If
If Len(jyczRaw) > 0 Then
params("jycz") = jyczRaw
End If
If Len(fjgnRaw) > 0 Then
params("fjgn") = fjgnRaw
End If
Set ParseProductModel = params
Exit Function
ErrorHandler:
If Not g_Logger Is Nothing Then
g_Logger.Record 0, "M06.ParseProductModel", "SystemError", _
"解析过程发生错误: " & Err.Description, modelString
End If
Set ParseProductModel = CreateObject("Scripting.Dictionary")
End Function
' ------------------------------------------------------------------------------
' 提取表头部分(管道符之前)
'
' 输入: YTHN-100.A0.531.G123.M04.Y3|BP-088.2312.B09.0A3
' 输出: YTHN-100.A0.531.G123.M04.Y3
' ------------------------------------------------------------------------------
Private Function ExtractHeaderPart(ByVal fullModel As String) As String
Dim pipePos As Long
pipePos = InStr(fullModel, MODEL_SEPARATOR_PIPELINE)
If pipePos > 0 Then
ExtractHeaderPart = Left(fullModel, pipePos - 1)
Else
ExtractHeaderPart = fullModel
End If
End Function
' ------------------------------------------------------------------------------
' 分割表头部分为段数组
'
' 输入: YTHN-100.A0.531.G123.M04.Y3
' 输出: Array("YTHN-100", "A0", "531", "G123", "M04", "Y3")
' ------------------------------------------------------------------------------
Private Function SplitHeaderPart(ByVal headerPart As String) As Variant
' 按点号分割
Dim rawSegments As Variant
rawSegments = Split(headerPart, MODEL_SEPARATOR_DOT)
' 如果分割失败或结果为空
If Not IsArray(rawSegments) Then
SplitHeaderPart = Null
Exit Function
End If
' 去除每个段的空格
Dim i As Long
For i = LBound(rawSegments) To UBound(rawSegments)
rawSegments(i) = Trim(CStr(rawSegments(i)))
Next i
SplitHeaderPart = rawSegments
End Function
' ------------------------------------------------------------------------------
' 提取型号和公称外径(预留字段)
'
' 输入: YTHN-100
' 输出: params("xh")="YTHN", params("gcwj")="100"
' ------------------------------------------------------------------------------
Private Sub ExtractModelAndSize(ByVal firstSegment As String, ByRef params As Object)
Dim dashPos As Long
dashPos = InStr(firstSegment, "-")
If dashPos > 1 Then
params("xh") = Left(firstSegment, dashPos - 1)
Dim afterDash As String
afterDash = Mid(firstSegment, dashPos + 1)
' 提取-号后的数字部分(公称外径)
' 因为firstSegment是"YTHN-100.A0...",需要去掉后面的点号内容
Dim dotPos As Long
dotPos = InStr(afterDash, ".")
If dotPos > 0 Then
params("gcwj") = Left(afterDash, dotPos - 1)
Else
params("gcwj") = afterDash
End If
End If
End Sub
' ------------------------------------------------------------------------------
' 提取安装形式
'
' 输入: A0
' 输出: A0
'
' 规则: 直接返回第一段的值
' ------------------------------------------------------------------------------
Private Function ExtractAzxs(ByVal segment As String) As String
ExtractAzxs = Trim(segment)
End Function
' ------------------------------------------------------------------------------
' 提取表壳形式
'
' 输入: 531
' 输出: 531
'
' 规则: 直接返回第二段的值
' ------------------------------------------------------------------------------
Private Function ExtractBkxs(ByVal segment As String) As String
ExtractBkxs = Trim(segment)
End Function
' ------------------------------------------------------------------------------
' 提取过程连接和接液材质
'
' 输入: G123
' 输出: gclj="G12", jycz="3"
'
' 规则:
' - 过程连接: 去除最后一位
' - 接液材质: 最后一位
' ------------------------------------------------------------------------------
Private Sub ExtractGcljAndJycz(ByVal segment As String, ByRef gclj As String, ByRef jycz As String)
segment = Trim(segment)
If Len(segment) >= 1 Then
jycz = Right(segment, 1)
If Len(segment) > 1 Then
gclj = Left(segment, Len(segment) - 1)
Else
gclj = ""
End If
Else
gclj = ""
jycz = ""
End If
End Sub
' ------------------------------------------------------------------------------
' 提取量程范围
'
' 输入: M04
' 输出: M04
'
' 规则: 直接返回第四段的值
' ------------------------------------------------------------------------------
Private Function ExtractLcfw(ByVal segment As String) As String
ExtractLcfw = Trim(segment)
End Function
' ------------------------------------------------------------------------------
' 提取附加功能
'
' 输入: segments数组起始索引为4
' 输出: Y3 或 N1,N2 或 N1.N2
'
' 规则:
' - 从第五段开始,所有段合并为附加功能
' - 用逗号或点号分隔的多个功能,保留原分隔符
' - 示例: Y3 -> Y3
' - 示例: N1,N2.Y3 -> N1,N2,Y3
' ------------------------------------------------------------------------------
Private Function ExtractFjgn(ByRef segments As Variant, ByVal startIndex As Long) As String
Dim result As String
result = ""
Dim i As Long
For i = startIndex To UBound(segments)
Dim segment As String
segment = Trim(CStr(segments(i)))
If Len(segment) > 0 Then
' 替换点号为逗号(统一分隔符)
segment = Replace(segment, MODEL_SEPARATOR_DOT, ",")
If Len(result) > 0 Then
result = result & "," & segment
Else
result = segment
End If
End If
Next i
ExtractFjgn = result
End Function