yeg
5 天以前 f20b8ca6cd7f237dc542052713f4bdfcf7e5d8de
scripts/j22-introspect-files.ps1
@@ -2,8 +2,11 @@
  22 所上位机文件侧只读探查:工步方案(含可能的工步名称)在不在软件目录的文件里。
  为什么走文件侧:该机 SQL Server 的 5141 个用户库**全部**是 DB_<时间> 测试库、其他库 0 个,
  所以工步方案不可能在库里。上一轮现场已在运行目录见到 VariableMap.xml、UnitConvert.xml、
  DeviceConfiguration.xml、TaskModel.xml 等文件,另有遗留的 .sto 工步方案(gzip 压缩的 XML)。
  所以工步方案不可能在库里。
  方案文件是 `.pcj`(2026-10-09 现场确认):`SysSchedule` 格式的 XML、**没有压缩**,声明 utf-8,
  每个工步一条 `<Step>`,带 `CtrlTypeName`(控制类型名)、`CtrlValue`(设定值)、`Limit`(截止条件)、
  `GotoStep`(跳转),方案名在 `<ScheduleName>`。遗留的 `.sto`(gzip 压缩的 XML)也一并看。
  只读纪律:只列目录、只读文件内容;不写、不改厂商目录与文件;报告只写到本脚本所在目录。
@@ -21,6 +24,9 @@
    [int]$MaxFiles = 3,
    [int]$MaxHeadChars = 400,
    [int]$MaxNames = 25,
    # 工步方案里打印前几个与后几个工步(中间省略),以及每个工步最多打印多少字。
    [int]$StepPreview = 4,
    [int]$MaxStepChars = 300,
    [string]$ReportFile = ''
)
@@ -36,12 +42,18 @@
    [void]$report.Add($text)
}
# 方案文件的判据:.sto 一定是方案;名字里带 step/plan/task/recipe/scheme/program/工步/方案的也算。
# 方案文件的判据:.pcj 是这台机器上软件的工步方案(SysSchedule XML,2026-10-09 现场确认),
# .sto 是遗留方案(gzip 压缩的 XML);名字里带 step/plan/task/recipe/scheme/program/工步/方案的也算。
# 其余文件(设备配置、单位换算表等)不看——它们存不下工步名称。
function Test-PlanFile([string]$name) {
    $extension = [System.IO.Path]::GetExtension($name)
    if ($extension -ieq '.sto') { return $true }
    if ($extension -ieq '.pcj' -or $extension -ieq '.sto') { return $true }
    return $name -imatch '(step|plan|task|recipe|scheme|program|工步|方案)'
}
# 取一个 XML 标签的文本值;没有这个标签就返回空串。
function Get-TagValue([string]$text, [string]$tag) {
    return [regex]::Match($text, '<' + $tag + '>([^<]*)</' + $tag + '>').Groups[1].Value
}
# 读文件文本:gzip(.sto 是 gzip 压缩的 XML)自动解压;只读前若干字符,避免把大文件全读进来。
@@ -203,6 +215,24 @@
        $head = $text.Trim()
        if ($head.Length -gt $MaxHeadChars) { $head = $head.Substring(0, $MaxHeadChars) + ' …' }
        Say ('    文本开头:' + $head)
        # 工步方案(.pcj / SysSchedule):逐工步的设定值、截止条件与跳转都在这里。
        # 工步的"名称"通常就是每个工步的 CtrlTypeName(例如"恒流充电""搁置")——这一节就是要看它。
        if ($text -match '<Steps>') {
            $steps = [regex]::Matches($text, '(?s)<Step>.*?</Step>')
            Say ('    方案名: ' + (Get-TagValue $text 'ScheduleName') + '    任务目录: ' + (Get-TagValue $text 'SchedulePath'))
            Say ('    方案标题: ' + (Get-TagValue $text 'Caption') + '    说明: ' + (Get-TagValue $text 'Description') + '    最后修改: ' + (Get-TagValue $text 'LastUpdateTime'))
            Say ('    工步数: ' + $steps.Count)
            $index = 0
            foreach ($step in $steps) {
                $index++
                # 只打印前几个与后几个:中间的省略,否则一个方案就能刷满屏。
                if ($index -gt $StepPreview -and $index -le ($steps.Count - $StepPreview)) { continue }
                $flat = $step.Value -replace '\s+', ' '
                if ($flat.Length -gt $MaxStepChars) { $flat = $flat.Substring(0, $MaxStepChars) + ' …' }
                Say ('      工步 ' + $index + ': ' + $flat)
            }
        }
    }
    Say ''