mirror of
https://gitlab.cern.ch/wotsubo/PSBoardDataBase.git
synced 2025-06-08 05:55:42 +09:00
443 lines
12 KiB
Julia
443 lines
12 KiB
Julia
"""
|
||
Parse QAQC JATHub slave log.
|
||
|
||
See [`parse_slavelog_file`](@ref) for the main function.
|
||
"""
|
||
module SlaveLogParser
|
||
|
||
using StaticArrays
|
||
using Printf
|
||
using AutoHashEquals
|
||
|
||
const HEADER_QSPIP_START = "=============== Test QAPIp Start ==============="
|
||
const HEADER_POWER_START = "=============== Test Power Start ==============="
|
||
const HEADER_ASDTP_START = "=============== Test ASDTP Start ==============="
|
||
const HEADER_RECOV_START = "=============== Test Recov Start ==============="
|
||
HEADER_STARTS =
|
||
SVector(HEADER_QSPIP_START, HEADER_POWER_START, HEADER_ASDTP_START, HEADER_RECOV_START)
|
||
|
||
"""
|
||
Indicate parser state.
|
||
Default is `MODE_NONE`.
|
||
In `MODE_NONE`, each line is fed into parser to detect the start of each section.
|
||
"""
|
||
@enum SlaveLogSection begin
|
||
MODE_NONE
|
||
MODE_QSPIP
|
||
MODE_POWER
|
||
MODE_ASDTP
|
||
MODE_RECOV
|
||
end
|
||
|
||
struct SlaveLogResult end
|
||
|
||
"""
|
||
get_psbid_runid_from_filename(filename::AbstractString)::Tuple{Int64,Int64,Bool}
|
||
|
||
Extract info from slave log filename.
|
||
|
||
Returns a tuple of `psbid`, `runid`, `islongrun`.
|
||
"""
|
||
function get_psbid_runid_from_filename(filename::AbstractString)::Tuple{Int64, Int64, Bool}
|
||
main, _ext = splitext(filename)
|
||
parts = split(main, '_')
|
||
psbid = parse(Int64, parts[1])
|
||
runid = parse(Int64, parts[2])
|
||
islongrun = if length(parts) == 3
|
||
true
|
||
else
|
||
false
|
||
end
|
||
|
||
(psbid, runid, islongrun)
|
||
end
|
||
|
||
function is_valid_slavelog(filename::AbstractString)::Bool
|
||
error("not yet implemented")
|
||
end
|
||
|
||
"""
|
||
detect_mode_start(line::AbstractString)
|
||
|
||
Detect [`SlaveLogSection`](@ref) from section starting header line.
|
||
If the line doesn't match any section, returns `nothing`.
|
||
"""
|
||
function detect_mode_start(line::AbstractString)
|
||
if line == HEADER_QSPIP_START
|
||
MODE_QSPIP
|
||
elseif line == HEADER_POWER_START
|
||
MODE_POWER
|
||
elseif line == HEADER_ASDTP_START
|
||
MODE_ASDTP
|
||
elseif line == HEADER_RECOV_START
|
||
MODE_RECOV
|
||
else
|
||
nothing
|
||
end
|
||
end
|
||
|
||
"""
|
||
detect_mode_start!(mode::SlaveLogSection, line::AbstractString)
|
||
|
||
Detect mode from the `line` and update `mode`.
|
||
"""
|
||
function detect_mode_start(mode::SlaveLogSection, line::AbstractString)
|
||
newmode = detect_mode_start(line)
|
||
if !isnothing(newmode)
|
||
mode = newmode
|
||
end
|
||
mode
|
||
end
|
||
|
||
"""
|
||
parse_qspip_section(lines::Base.Iterators.Stateful)
|
||
|
||
Parse QSPIp section of given stateful iterator of log.
|
||
|
||
# Args
|
||
- `lines`: Stateful iterator of slave log file lines
|
||
"""
|
||
function parse_qspip_section!(lines::Base.Iterators.Stateful)
|
||
# TODO
|
||
nothing
|
||
end
|
||
|
||
# ==================================
|
||
# Power
|
||
|
||
"""
|
||
Results from power test
|
||
|
||
# Fields
|
||
- result_3v3d::Float64
|
||
- result_3v3a::Float64
|
||
- result_n3va::Float64
|
||
- fpga_temp::Float64
|
||
- channelvals::SVector{16, @NamedTuple{dac::Int64, adc::Int64}}
|
||
|
||
- result::Bool
|
||
"""
|
||
struct PowerResult
|
||
result_3v3d::Float64
|
||
result_3v3a::Float64
|
||
result_n3va::Float64
|
||
fpga_temp::Float64
|
||
channelvals::SVector{16, @NamedTuple{dac::Int64, adc::Int64}}
|
||
|
||
result::Bool
|
||
end
|
||
|
||
"""
|
||
parse_power_section!(lines::Base.Iterators.Stateful)::PowerResult
|
||
|
||
Parse Power section of given stateful iterator of log.
|
||
|
||
# Args
|
||
- `lines`: Stateful iterator of slave log file lines
|
||
"""
|
||
function parse_power_section!(lines::Base.Iterators.Stateful)::PowerResult
|
||
line = popfirst!(lines)
|
||
result_3v3d = let
|
||
m = match(r"^3V3D \[V\] = ([\d|\.]+)$", line)
|
||
parse(Float64, m[1])
|
||
end
|
||
line = popfirst!(lines)
|
||
result_3v3a = let
|
||
m = match(r"^3V3A \[V\] = ([\d|\.]+)$", line)
|
||
parse(Float64, m[1])
|
||
end
|
||
line = popfirst!(lines)
|
||
result_n3va = let
|
||
m = match(r"^-3VA \[V\] = ([-|\d|\.]+)$", line)
|
||
parse(Float64, m[1])
|
||
end
|
||
line = popfirst!(lines)
|
||
fpga_temp = let
|
||
# can be minus (see 127_172.txt)
|
||
m = match(r"^FPGA Temprature \[C\] = (-?[\d|\.]+)$", line)
|
||
parse(Float64, m[1])
|
||
end
|
||
line = popfirst!(lines)
|
||
line = popfirst!(lines)
|
||
channelvals =
|
||
Iterators.map(1:16) do ch
|
||
ch_s = @sprintf "%x" (ch - 1)
|
||
re = Regex("channel $(ch_s): DAC \\[mV\\] = (\\d+), ADC \\[mV\\] = (\\d+)\$")
|
||
line = popfirst!(lines) # I'm not sure this mutating operation is called in sequence
|
||
m = match(re, line)
|
||
(dac = parse(Int64, m[1]), adc = parse(Int64, m[2]))
|
||
end |>
|
||
Tuple |>
|
||
SVector{16, @NamedTuple{dac::Int64, adc::Int64}}
|
||
line = popfirst!(lines)
|
||
result = let
|
||
m = match(r"^Test Power Reseult = (\d)$", line)
|
||
if m[1] == "1"
|
||
true
|
||
else
|
||
false
|
||
end
|
||
end
|
||
|
||
PowerResult(result_3v3d, result_3v3a, result_n3va, fpga_temp, channelvals, result)
|
||
end
|
||
|
||
# ==================================
|
||
# Asdtp
|
||
|
||
"""
|
||
Measurement result for asic in asdtp test.
|
||
"""
|
||
@auto_hash_equals struct AsdtpMeasurement
|
||
before::Float64
|
||
current::Float64
|
||
next::Float64
|
||
end
|
||
|
||
AsdtpMeasurement(x::NTuple{3, <:Real}) = AsdtpMeasurement(x...)
|
||
|
||
function Base.parse(::Type{AsdtpMeasurement}, s::AbstractString)
|
||
v = split(s, ':')
|
||
@assert length(v) == 3
|
||
AsdtpMeasurement(parse(Float64, v[1]), parse(Float64, v[2]), parse(Float64, v[3]))
|
||
end
|
||
|
||
@auto_hash_equals struct AsdtpResult
|
||
reconfig_done::Int64
|
||
always_hit_flag::Int64
|
||
autoreconfig::Union{Bool, Missing}
|
||
asdtp_main::Union{Vector{Vector{AsdtpMeasurement}}, Missing}
|
||
asdtp_reset::Int64
|
||
asdtp_total::Int64
|
||
reconfig_done_2::Int64
|
||
always_hit_flag_2::Int64
|
||
si_done::UInt64
|
||
lolb_in::UInt64
|
||
ppconfig_done::UInt64
|
||
ppconfig_error::UInt64
|
||
pllld_fail_counter::UInt64
|
||
ppconfig_fail_counter::UInt64
|
||
pp_pllds::MVector{8, UInt32}
|
||
end
|
||
|
||
"""
|
||
parse_asdtp_section!(lines::Base.Iterators.Stateful)::AsdtpResult
|
||
|
||
Parse ASDTP section of given stateful iterator of log.
|
||
|
||
# Args
|
||
- `lines`: Stateful iterator of slave log file lines
|
||
"""
|
||
function parse_asdtp_section!(lines::Base.Iterators.Stateful)::AsdtpResult
|
||
line = popfirst!(lines)
|
||
result_reconfig_done = let
|
||
m = match(r"^reconfig_done = (\d+)$", line)
|
||
parse(Int64, m[1])
|
||
end
|
||
line = popfirst!(lines)
|
||
result_always_hit_flag = let
|
||
m = match(r"^always_hit_flag = (\d+)$", line)
|
||
parse(Int64, m[1])
|
||
end
|
||
line = popfirst!(lines)
|
||
result_autoreconfig = if line == "Autoreconfig done"
|
||
true
|
||
elseif line == "Autoreconfig fail"
|
||
false
|
||
else
|
||
missing
|
||
end
|
||
|
||
result_asdtp_main = if !ismissing(result_autoreconfig) && result_autoreconfig
|
||
line_count = 0
|
||
line_count += 1
|
||
results = map(_ -> AsdtpMeasurement[], 1:8)
|
||
for asic_id in 1:8
|
||
header_line = "----PP$(asic_id)----"
|
||
while line != header_line
|
||
line = popfirst!(lines)
|
||
line_count += 1
|
||
end
|
||
for _ in 1:32
|
||
line = popfirst!(lines)
|
||
line_count += 1
|
||
mes = parse(AsdtpMeasurement, line)
|
||
push!(results[asic_id], mes)
|
||
end
|
||
end
|
||
@assert length(results[1]) == 32 "unexpected length: $(length(results[1]))"
|
||
|
||
line = popfirst!(lines)
|
||
@assert line == "100"
|
||
results
|
||
else
|
||
missing
|
||
end
|
||
|
||
line = popfirst!(lines)
|
||
result_asdtp_reset, result_asdtp_total = let
|
||
m = match(r"^ASDTP : (\d+) times reset : result = (\d+)$", line)
|
||
parse(Int64, m[1]), parse(Int64, m[2])
|
||
end
|
||
line = popfirst!(lines)
|
||
result_reconfig_done_2 = let
|
||
m = match(r"^reconfig_done = (\d+)$", line)
|
||
parse(Int64, m[1])
|
||
end
|
||
line = popfirst!(lines)
|
||
result_always_hit_flag_2 = let
|
||
m = match(r"^always_hit_flag = (\d+)$", line)
|
||
parse(Int64, m[1])
|
||
end
|
||
line = popfirst!(lines)
|
||
@assert line == ""
|
||
|
||
line = popfirst!(lines)
|
||
@assert line == "------- Done check -------" "actual line: $line"
|
||
line = popfirst!(lines)
|
||
result_si_done = let
|
||
m = match(r"^Si_done = (0x[[:xdigit:]]+)$", line)
|
||
parse(UInt64, m[1])
|
||
end
|
||
line = popfirst!(lines)
|
||
result_lolb_in = let
|
||
m = match(r"^LOLB_in = (0x[[:xdigit:]]+)$", line)
|
||
parse(UInt64, m[1])
|
||
end
|
||
line = popfirst!(lines)
|
||
result_ppconfig_done = let
|
||
m = match(r"^PPconfig_done = (0x[[:xdigit:]]+)$", line)
|
||
parse(UInt64, m[1])
|
||
end
|
||
line = popfirst!(lines)
|
||
result_ppconfig_error = let
|
||
m = match(r"^PPconfig_error = (0x[[:xdigit:]]+)$", line)
|
||
parse(UInt64, m[1])
|
||
end
|
||
line = popfirst!(lines)
|
||
result_pllld_fail_counter = let
|
||
m = match(r"^PLLLD_fail_counter = (0x[[:xdigit:]]+)$", line)
|
||
parse(UInt64, m[1])
|
||
end
|
||
line = popfirst!(lines)
|
||
result_ppconfig_fail_counter = let
|
||
m = match(r"^PPconfig_fail_counter = (0x[[:xdigit:]]+)$", line)
|
||
parse(UInt64, m[1])
|
||
end
|
||
result_pp_pllds = MVector{8, UInt32}(undef)
|
||
for ppid in 1:8
|
||
line = popfirst!(lines)
|
||
m = match(Regex("^PP$(ppid)_PLLLD = (0x[[:xdigit:]]+)\$"), line)
|
||
result_pp_pllds[ppid] = parse(UInt32, m[1])
|
||
end
|
||
|
||
AsdtpResult(
|
||
result_reconfig_done,
|
||
result_always_hit_flag,
|
||
result_autoreconfig,
|
||
result_asdtp_main,
|
||
result_asdtp_reset,
|
||
result_asdtp_total,
|
||
result_reconfig_done_2,
|
||
result_always_hit_flag_2,
|
||
result_si_done,
|
||
result_lolb_in,
|
||
result_ppconfig_done,
|
||
result_ppconfig_error,
|
||
result_pllld_fail_counter,
|
||
result_ppconfig_fail_counter,
|
||
result_pp_pllds,
|
||
)
|
||
end
|
||
|
||
# ==================================
|
||
# Recov
|
||
|
||
"""
|
||
parse_recov_section!(lines::Base.Iterators.Stateful)
|
||
|
||
Parse Recov section of given stateful iterator of log.
|
||
|
||
# Args
|
||
- `lines`: Stateful iterator of slave log file lines
|
||
|
||
# Return
|
||
- `missing`: if failed to parse
|
||
- `true`: if succeeded
|
||
- `false`
|
||
"""
|
||
function parse_recov_section!(lines::Base.Iterators.Stateful)::Union{Bool, Missing}
|
||
line = popfirst!(lines)
|
||
if startswith("====")(line)
|
||
line = popfirst!(lines)
|
||
end
|
||
m = match(r"Test Recov Result = (\d+)", line)
|
||
if isnothing(m)
|
||
return missing
|
||
else
|
||
return m[1] == "1"
|
||
end
|
||
end
|
||
|
||
# ==================================
|
||
# Main
|
||
|
||
"""
|
||
parse_slavelog_file(filename::AbstractString)
|
||
|
||
Main function to parse slave log file.
|
||
Returns `NamedTuple` with keys `adtp`, `power`, `recov` and their values are `Vector` of each results.
|
||
For a single run result, these `Vector`s have only one element and for a hundred(extra) runs, the lengths are usually 100.
|
||
For details on results for each section, see documents on dedicated types.
|
||
"""
|
||
function parse_slavelog_file(filename::AbstractString)
|
||
lines_iter = Iterators.Stateful(eachline(filename))
|
||
|
||
asdtp_results = AsdtpResult[]
|
||
power_results = PowerResult[]
|
||
recov_results = Union{Bool, Missing}[]
|
||
|
||
mode::SlaveLogSection = MODE_NONE
|
||
# main loop
|
||
while !isempty(lines_iter)
|
||
# @info "section" mode
|
||
# each sections
|
||
if mode == MODE_NONE
|
||
line = popfirst!(lines_iter)
|
||
mode = detect_mode_start(mode, line)
|
||
elseif mode == MODE_QSPIP
|
||
parse_qspip_section!(lines_iter)
|
||
mode = MODE_NONE
|
||
elseif mode == MODE_POWER
|
||
result = parse_power_section!(lines_iter)
|
||
push!(power_results, result)
|
||
mode = MODE_NONE
|
||
elseif mode == MODE_ASDTP
|
||
result = parse_asdtp_section!(lines_iter)
|
||
push!(asdtp_results, result)
|
||
mode = MODE_NONE
|
||
elseif mode == MODE_RECOV
|
||
result = parse_recov_section!(lines_iter)
|
||
push!(recov_results, result)
|
||
mode = MODE_NONE
|
||
end
|
||
end
|
||
|
||
return (asdtp = asdtp_results, power = power_results, recov = recov_results)
|
||
end
|
||
|
||
function eff99_count_map(asdtp_results)
|
||
# try(100) × channel(8) × channel(32)
|
||
@assert length(asdtp_results) == 100
|
||
@assert length(asdtp_results[begin]) == 8
|
||
@assert length(asdtp_results[begin][begin]) == 32
|
||
map(1:8) do i_asic
|
||
map(1:32) do i_channel
|
||
sum(1:100) do i_try
|
||
asdtp_results[i_try][i_asic][i_channel] != AsdtpMeasurement(0, 1, 0)
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
end # module SlaveLogParser
|