All checks were successful
NTFY Notification / notify (push) Successful in 4s
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>
335 lines
11 KiB
QBasic
335 lines
11 KiB
QBasic
' ==============================================================================
|
||
' 模块: 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 |