### A Pluto.jl notebook ###
# v0.20.4

using Markdown
using InteractiveUtils

# ╔═╡ 7c69d12c-80a5-11ef-2674-e155a3483342
begin
    using Pkg
    Pkg.activate(".")
    Pkg.status()
    true || include("../src/PSBoardDataBase.jl")
end

# ╔═╡ effa7ed9-2ac4-4468-a474-e2bb662580fe
begin
    using PSBoardDataBase
    using SQLite
    using DataFrames
    using FHist
    using DBInterface
    using Tables
    using CairoMakie
    using Statistics
    using PlutoUI
    using Dates
    using Random
    using Printf
end

# ╔═╡ f25e7e08-8a73-4cac-ac7c-d310725c558d
md"""
# Detailed research on clock skew measurement
- distribution of clock skews
- consistency of multiple measurement on the same board
"""

# ╔═╡ 1a6322d4-9deb-4709-aa2e-df7d8be5b16f
TableOfContents(depth = 4)

# ╔═╡ 11537f91-e16b-45f0-9768-6df842371d36
db = SQLite.DB("../psboard_qaqc.db")

# ╔═╡ 268d3015-b8d3-48d9-b74a-062e258e0ec1
SQLite.tables(db)

# ╔═╡ 62105832-df1f-4834-8da6-c542e22207d1
md"""
## Single runs
"""

# ╔═╡ dea6f143-7916-4765-92f6-2bfb97a72835
qaqc_single_results =
    DBInterface.execute(db, sql"select * from qaqc_single_run_results") |> DataFrame

# ╔═╡ 41543c0c-d7c4-447b-a268-0d356c88d92c
md"""
## PS Board list
"""

# ╔═╡ 633ecdee-7e2f-4de8-833a-21cd0351c1f1
DBInterface.execute(
    db,
    sql"""
    select * from ps_boards
    """,
) |> DataFrame

# ╔═╡ 87f1966e-3b07-4f9d-8fc4-7b7aa4319d50
md"""
## Run list
"""

# ╔═╡ f379d43c-9300-41f4-b0fc-3c9d749e3105
qaqc_runs = let df
    df = DBInterface.execute(db, sql"select * from qaqc_runs") |> DataFrame
    transform!(df, :run_datetime => ByRow(passmissing(s -> DateTime(s))) => :run_datetime)
    df
end

# ╔═╡ 33e099bc-ac4b-4b5f-88e7-20f4463c98ef
md"""
## Positions
"""

# ╔═╡ 0e13f848-0efb-4775-9e3e-518b32588a79
qaqc_positions = DBInterface.execute(db, sql"select * from qaqc_positions") |> DataFrame

# ╔═╡ 181c3fe6-d087-42e2-b175-3fb84c42e3e8
position_id_skew_map = select(qaqc_positions, [:id, :rising_ns]) |> Tables.rowtable |> Dict

# ╔═╡ dd669f14-989b-45ee-87d8-5d9cf282fafd
md"""
## Dispatch
"""

# ╔═╡ 1e9c3944-0cd4-40da-9014-a9153d4e95ed
qaqc_dispatch = DBInterface.execute(db, sql"select * from qaqc_dispatch") |> DataFrame

# ╔═╡ 322cb530-65a5-4973-86f8-01ccc2439cc4
md"""
# Clock Analysis
main part
"""

# ╔═╡ c1caca5f-4cfd-4f22-82b4-7925002359e6
clk_files =
    readdir("../test/input/slavelogs/main/", join = true) |>
    filter(endswith("_clk.txt")) |>
    filter(!contains("nagoya")) |>
    filter(!contains("630_190")) |>
    filter(!contains("627_344"))

# ╔═╡ 3e5607fd-2a8a-4a1a-9e7b-3f23ef216fad
"""
Get `(psbid, runid)`.
"""
function parse_filename(filename::AbstractString)
    m = match(r"(?<psbid>\d+)_(?<runid>\d+)_clk\.txt", filename)
    parse(Int64, m[:psbid]), parse(Int64, m[:runid])
end

# ╔═╡ c1b9c0c3-00f8-4199-b07f-8888f1be625c
parse_filename("190_23_clk.txt")

# ╔═╡ d6d04013-e0e4-49d5-a450-07ae164bfaa3
# Get skew and rise up time from clock measurement files
# Use measurements recorded in qaqc_single_results
df_rawskews =
    clk_files .|>
    (
        file -> begin
            skew_width = PSBoardDataBase.ClockParser.get_skew_and_riseup(file)
            riseup = PSBoardDataBase.ClockParser.count_riseup(file)
            psbid, runid = parse_filename(file)
            (
                psbid = psbid,
                runid = runid,
                skew = skew_width[1],
                width = skew_width[2],
                riseup = riseup,
            )
        end
    ) |>
    filter(
        x ->
            filter(
                [:psboard_id, :runid] =>
                    ((psbid, runid) -> (psbid == x.psbid && runid == x.runid)),
                qaqc_single_results,
            ) |> !isempty,
    ) |>
    DataFrame

# ╔═╡ 0e680044-e4e1-4f39-a5c5-afa5c53fc7a7
clk_files |> filter(contains("168")) .|> PSBoardDataBase.ClockParser.get_skew_and_riseup

# ╔═╡ d7541b93-4c49-4dcd-bda0-91e447f44596
# substract result of measurements of position dependency
df_skews = let
    df = leftjoin(
        df_rawskews,
        @view(qaqc_single_results[!, [:psboard_id, :runid, :position]]),
        on = [:psbid => :psboard_id, :runid],
    )
    leftjoin!(df, @view(qaqc_positions[!, [:id, :rising_ns]]), on = [:position => :id])
    transform!(df, [:skew, :rising_ns] => ByRow((x, y) -> x - y) => :skew)
    select!(df, Not(:rising_ns))
    select!(df, Not(:position))
end

# ╔═╡ e7faa647-79cd-4247-a3f2-9868c7b9d4ca
md"""
## skewの分布

``skew = rising\_time_{psbid, position} - rising\_time_{position}``
"""

# ╔═╡ 3a412a98-4a2d-4bfb-8053-b0f480fae921
let
    skews = skipmissing(df_skews.skew) |> collect
    npsbid = df_skews.psbid |> unique |> length
    fig = Figure()
    ax = Axis(
        fig[1, 1],
        title = "skews of all measurements (n_meas = $(length(skews)), n_psb = $(npsbid))",
        xlabel = "skew / ns",
    )
    stephist!(ax, skews, bins = range(minimum(skews), maximum(skews), step = 1 / 56))
    fig
end

# ╔═╡ b38bbed4-8721-4e92-a546-f7926cc07dd3
md"""
## 立ち上がり時間の分布(生)
"""

# ╔═╡ 420dce0e-4757-48d9-84ec-7ddfac2fdff6
let
    skew_widths = df_skews.width |> skipmissing |> collect
    bins = range(0, maximum(skew_widths) + 1 / 56, step = 1 / 56)# .- 0.01
    hist(
        skew_widths,
        bins = bins,
        bar_labels = :y,
        label_size = 14,
        label_formatter = x -> "$(round(Int, x))",
        axis = (
            title = "clock rise span distribution",
            xlabel = "rise up span / ns",
            ylabel = "counts",
            limits = ((0, 0.33), (0, nothing)),
            # xticks = (bins, string.(round.(bins, digits = 3))),
            # xticklabelrotation = π / 3,
        ),
    )
end

# ╔═╡ 99902640-fee3-4502-9c7e-cb08834bad0b
maximum(skipmissing(df_skews.width)) / (1 / 56)

# ╔═╡ c79c6684-1b03-41b5-aa90-ef8c7a8eb69c
md"""
この結果を元に、クロック試験のしきい値は$(round(9 * 1 / 56; digits = 2))ns以上に設定
"""

# ╔═╡ a1056aba-e484-4594-908b-4709896d0da0
let
    fig = Figure()
    ax = Axis(
        fig[1, 1],
        limits = ((-0.5, 1), nothing),
        title = "turn on curves for PSBID 1166",
        xlabel = "delay / ns (position dependency is removed)",
        ylabel = "counts",
    )

    clk_files_1166 = filter(clk_files) do filename
        psbid, runid = parse_filename(filename)
        psbid == 1166
    end
    for clk_file in clk_files_1166
        psbid, runid = parse_filename(clk_file)
        single_runs = filter(
            [:psboard_id, :runid] => (
                (ref_psbid, ref_runid) -> begin
                    psbid == ref_psbid && runid == ref_runid
                end
            ),
            qaqc_single_results,
        )
        offset_pos = let
            qaqc_positions.rising_ns[single_runs.position[1]]
        end

        points =
            eachline(clk_file) .|>
            PSBoardDataBase.ClockParser._parse_line .|>
            (x -> (x[1] - offset_pos, x[2]))

        @info "" single_runs points

        stds = map(points) do ((time, count))
            sqrt(count * (1000 - count) / 1000)
        end

        scatterlines!(ax, points, label = "psbid $(psbid), runid $(runid)")
        errorbars!(ax, points, stds)
    end
    axislegend(ax, position = :rb)

    fig
end

# ╔═╡ 875bec26-e576-4f48-ba14-464bce503d75
filter(:width => (x -> ismissing(x) || x < 0.06), df_skews)

# ╔═╡ ec774495-c0be-47a4-9d2c-b48159c07013
md"""
## 各PSBoardごとの統計
"""

# ╔═╡ d082e07c-3b42-4362-bebf-63356979a49b
gdf_skews_on_psbid = groupby(df_skews, :psbid)

# ╔═╡ 2d41c98b-630e-41e7-9332-25394a6285e8
md"""
### 同じPSBoardで同じ場所で複数回測定し、測定のばらつきが大きかったもの
すべて異常個体リストで把握済み
"""

# ╔═╡ 6ff9f397-5db9-45af-9bca-7a8d7756f8da
combine(filter(groupby(qaqc_single_results, [:psboard_id, :position])) do sdf
    nrow(dropmissing(sdf, [:lvds_tx_skew])) > 1
end) do sdf
    sdf = dropmissing(sdf, [:lvds_tx_skew])
    # @info "" select(sdf, [:psboard_id, :position, :lvds_tx_skew])
    (
        skew_mean = mean(sdf.lvds_tx_skew),
        skew_std = std(sdf.lvds_tx_skew),
        nrow = nrow(sdf),
    )
end |> (df -> begin
    filter(:skew_std => >(0.5), df)
end)

# ╔═╡ 25688d24-5aee-43d3-aff9-b9efa0556070
combine(nrow, gdf_skews_on_psbid)

# ╔═╡ 0d3f7f0a-1713-4e22-acbb-7d370fabf5a7
combine(gdf_skews_on_psbid, :skew => (v -> all(ismissing, v)) => :hasmissing) |> (df -> sort(df, :hasmissing, rev = true))

# ╔═╡ 239a808c-0411-4542-ae68-6ae6af333bd2
df_nrow_ordered = let
    df = combine(nrow, gdf_skews_on_psbid)
    sort!(df, :nrow, rev = true)
end

# ╔═╡ 8e57bde1-5f97-483d-906e-8ebfb65016d0
@view(df_nrow_ordered[findall(>(1), df_nrow_ordered.nrow), :])

# ╔═╡ 92c2ac3f-8034-4e9e-aadb-8bb166fbc948
df_skew_stats = let
    df = combine(
        gdf_skews_on_psbid,
        sdf -> begin
            if nrow(sdf) == 1
                (; mean_skew = mean(sdf.skew), std_skew = missing, n = 1)
            else
                (; mean_skew = mean(sdf.skew), std_skew = std(sdf.skew), n = nrow(sdf))
            end
        end,
    )
    dropmissing!(df)
    df
end

# ╔═╡ 893253c3-f0b2-401f-b892-b23291bcf5c1
fig_skew_stats = let
    fig, ax, sc = scatter(
        df_skew_stats.mean_skew,
        df_skew_stats.std_skew,
        marker = :x,
        color = (Makie.wong_colors()[1], 0.8),
        axis = (
            title = "mean vs std of multiple skew measurements for same PS Boards",
            xlabel = "mean / ns",
            ylabel = "std / ns",
        ),
    )
    text!(
        ax,
        df_skew_stats.mean_skew,
        df_skew_stats.std_skew,
        text = string.(df_skew_stats.psbid),
        color = (:gray, 0.5),
    )
    fig
end

# ╔═╡ 6467dcaa-6bd6-45c7-8c08-b310a09b8b0b
save("clock_skew_stats.svg", fig_skew_stats)

# ╔═╡ 19f85f1b-07d3-48c2-9ca6-a5a8eb1ab746
let
    fig, ax, sc = scatter(
        df_skew_stats.mean_skew,
        df_skew_stats.std_skew,
        marker = :x,
        color = (Makie.wong_colors()[1], 0.8),
        axis = (
            title = "mean vs std of multiple skew measurements for same PS Boards",
            xlabel = "mean / ns",
            ylabel = "std / ns",
            limits = ((-0.6, 1.2), (-0.01, 0.09)),
        ),
    )
    text!(
        ax,
        df_skew_stats.mean_skew,
        df_skew_stats.std_skew,
        text = string.(df_skew_stats.psbid),
        color = (:gray, 0.5),
    )
    fig
end

# ╔═╡ 0d758cdf-0dda-4dc3-b489-35831812b718
md"""
### 異常個体リスト
- 分散が1より大きいもの
"""

# ╔═╡ 79e2f5d8-4609-4e9f-949e-6dc1f88c0b19
df_skew_stats_abnormals = let
    df = filter([:mean_skew, :std_skew] => ((m, s) -> s > 1), df_skew_stats)
    sort!(df, :psbid)
    df
end

# ╔═╡ eae649db-6b2b-4530-83a8-3438f29423cc
let
    df = filter(:psboard_id => in(df_skew_stats_abnormals.psbid), qaqc_single_results)
    select!(df, [:runid, :psboard_id, :lvds_tx_skew, :note])
    df
end

# ╔═╡ d607e10e-854f-4652-9a34-9e22a188e315
let
    df = df_skew_stats_abnormals
    fig, ax, sc = scatter(
        df.mean_skew,
        df.std_skew,
        marker = :x,
        color = (Makie.wong_colors()[1], 0.8),
        axis = (
            title = "mean vs std of multiple skew measurements for same PS Boards",
            xlabel = "mean / ns",
            ylabel = "std / ns",
        ),
    )
    text!(
        ax,
        df.mean_skew,
        df.std_skew,
        text = string.(df.psbid) .* "," .* string.(df.n),
        color = (:gray, 0.7),
    )
    fig
end

# ╔═╡ 5bef43cf-0f9b-40a2-bd47-620f42c36d8d
gdf_skews_on_psbid[(psbid = 215,)]

# ╔═╡ 2795fd06-2f59-4e5b-829d-a8e428646790
md"""
### 分散が異常に大きいやつ
基本的に予想通り、分散は小さく複数回の測定で整合的な結果が得られているが、いくつか例外があった。

はじめはpsbid 127(4回測定)が含まれていたが、これはデータベースの編集ミスであることがわかり、修正した結果、消えた。

!!! todo
    他の例外も確認する。
    `df_skew_stats_abnormals`を確認

#### psbid: 215
- run: 43, 47
- run 43で"QSPI device ID does not match. inclueded in RUN45-, RUN47と49で問題が無いことを確認"
  - run 47を使用？
#### psbid: 291
- run: 83, 94
- 83でcommunication error(SFP半抜け)
#### psbid: 442
- run: 103, 132
- 103の結果がinvalid(460と同じ)
#### psbid: 460
- run: 105, 132
- psbid 444と同じく電源の抜き差しによってクロックの0と1000が繰り返されたパターン
- 追試に送られてる
#### psbid: 462
- run: 105, 132
- psbid 460と同じく105がだめ
#### psbid: 545
- run: 126, 132
- どちらも測定結果自体には変なところはない
  - どちらも1回だけ立ち上がりがある
  - 立ち上がりもそれほど長くない
- 126でQSPIpが失敗してるので結果は使わない、132を使う
- 126が電源が不安定なときだったかもしれないが、記録がない
#### psbid: 799
- run: 217, 236
- 217の結果が壊れてる
#### psbid: 802
- run: 217, 236
- 217の結果が壊れてる
#### psbid: 1001
- run: 319, 299
- 299でQSPIpが失敗している。319を使う
#### psbid: 1034
- run: 305, 319
- 305で振動、319を使う
"""

# ╔═╡ 26976b6c-3954-4a41-a99b-c1aaebdc645d
md"""
### skewの分布
"""

# ╔═╡ 38d472ca-6347-4096-828d-fd1256130a59
df_skews_selected = let df
    df = combine(
        gdf_skews_on_psbid,
        sdf -> begin
            all(ismissing, sdf.skew) && @info "" sdf
            if nrow(sdf) == 1
                # TODO: remove 1563 bypass
                sdf.psbid[1] == 1563 || @assert sdf.skew |> first |> !ismissing sdf
                (
                    skew = sdf.skew |> first,
                    width = sdf.width |> first,
                    riseup = sdf.riseup |> first,
                    runid = sdf.runid |> first,
                )
            else
                if sdf.psbid[1] == 291
                    df = filter(:runid => ==(94), sdf)
                    @assert nrow(df) == 1
                    (
                        skew = df.skew |> first,
                        width = sdf.width |> first,
                        riseup = sdf.riseup |> first,
                        runid = sdf.runid |> first,
                    )
                elseif sdf.psbid[1] == 460
                    df = filter(:runid => ==(132), sdf)
                    @assert nrow(df) == 1
                    (
                        skew = df.skew |> first,
                        width = sdf.width |> first,
                        riseup = sdf.riseup |> first,
                        runid = sdf.runid |> first,
                    )
                elseif sdf.psbid[1] == 545
                    df = filter(:runid => ==(132), sdf)
                    @assert nrow(df) == 1
                    (
                        skew = df.skew |> first,
                        width = sdf.width |> first,
                        riseup = sdf.riseup |> first,
                        runid = sdf.runid |> first,
                    )
                else
                    # assume that runid is chronological
                    i = argmax(sdf.runid .|> (id -> ismissing(id) ? -1 : id))
                    (
                        skew = sdf.skew[i],
                        width = sdf.width[i],
                        riseup = sdf.riseup[i],
                        runid = sdf.runid[1],
                    )
                end
            end
        end,
    )
    leftjoin!(df, select(qaqc_runs, [:id, :campaign_id]), on = :runid => :id)
    df
end

# ╔═╡ 310710da-ebb2-4f54-b238-38d493a6a533
let
    skews = df_skews_selected.skew |> skipmissing |> collect
    fig = Figure()
    ax = Axis(fig[1, 1], title = "skews (n = $(length(skews)))", xlabel = "skew / ns")
    stephist!(ax, skews, bins = range(minimum(skews), maximum(skews), step = 1 / 56))
    fig
end

# ╔═╡ d92e0f9f-ed53-4ad6-a390-5a395e8ab8bc
md"""
#### 異常に小さい値 (psbid: 444, 1215)
psbid: 444
- 出荷済み
psbid: 1215
- QSPIp fail
- 未出荷
"""

# ╔═╡ e9964184-1a2a-4ab9-bc22-2705a25393ec
filter(:skew => (x -> !ismissing(x) && x < -2), df_skews_selected)

# ╔═╡ bedf8dff-e109-4757-82e5-3232fcad752d
md"""
#### 欠損値(psbid: 255)
"""

# ╔═╡ f2d0ea9b-ede7-496a-8d1f-9f748f9d1666
transform(
    filter(:skew => ismissing, df_skews_selected),
    :psbid => ByRow(psbid -> begin
        psbid in qaqc_dispatch.psb_id
    end) => :is_dispatched,
)

# ╔═╡ f5bc8b1a-3fee-48ed-9910-367a1cda632e
md"""
- psbid: 255
    - Si not locked
"""

# ╔═╡ 106b8abe-0040-48f1-b663-c00ca7673520
filter(:riseup => !=(1), df_skews_selected)

# ╔═╡ a06d16ee-a4be-40de-8dac-fd818754373c
md"""
#### 立ち上がりが複数回(psbid: 255, 435, 444, 460)
skew結果として使った測定について、立ち上がり回数が1ではないもののリスト
##### psbid: 435
- 立ち上がり後途中で何故か0が入ってる
- 出荷済み
"""

# ╔═╡ 9f3f780d-da6e-44b4-a002-c2f088681e9c
df_skews_selected_sorted = let
    df = sort(df_skews_selected, :skew)
    dropmissing!(df, :skew)
    df
end

# ╔═╡ 5c74d0a8-f856-4988-b8f4-d2e02dc26f52
sort(df_skews_selected_sorted, :skew, order = Base.Order.Reverse)

# ╔═╡ 07342d98-3889-4a19-8419-6d113fffb42e
md"""
#### 立ち上がり時間の分布
"""

# ╔═╡ 939aba6b-b03c-42b6-83b3-9cea5f4eb858
let
    skew_widths = df_skews_selected.width |> skipmissing |> collect
    bins = range(0, maximum(skew_widths), step = 1 / 56)# .- 0.01
    hist(
        skew_widths,
        bins = bins,
        bar_labels = :y,
        label_size = 14,
        label_formatter = x -> "$(round(Int, x))",
        axis = (
            title = "clock rise span distribution",
            xlabel = "rise up span / ns",
            ylabel = "counts",
            limits = ((0, 0.28), (0, nothing)),
            # xticks = (bins, string.(round.(bins, digits = 3))),
            # xticklabelrotation = π / 3,
        ),
    )
end

# ╔═╡ aa9b78bc-8d66-4df2-bd06-1cbf21190488
filter(:width => (x -> ismissing(x) || x < 0.06), df_skews_selected)

# ╔═╡ 76f44129-34c6-451d-af3f-4593dd1dda5c
md"""
## skewの時間依存性
以下のrun
- 382: ファームウェア最新
"""

# ╔═╡ 0dd4c5fe-71d0-45d0-897e-51d1aa06bd3b
df_extra_measurements = let
    df_configs = DataFrame(
        position_name = ["B-$(i)-$(j)" for j in 1:9 for i in 0:1],
        psbid = [
            001142,
            000973,
            000990,
            000992,
            001113,
            001030,
            001121,
            001141,
            001050,
            001053,
            001110,
            001248,
            001242,
            001276,
            000872,
            000861,
            000525,
            000862,
        ],
        position = [1, 10, 2, 11, 3, 12, 4, 13, 5, 14, 6, 15, 7, 16, 8, 17, 9, 18],
    )
    @info "" df_configs
    combine(
        groupby(df_configs, :psbid),
        AsTable(:) =>
            (
                sdf -> begin
                    @assert nrow(sdf) == 1
                    map([382, 404]) do runid
                        clkfile = "../test/input/slavelogs/main/$(sdf.psbid[1])_$(runid)_clk.txt"
                        skew, width =
                            PSBoardDataBase.ClockParser.get_skew_and_riseup(clkfile)
                        offset = qaqc_positions.rising_ns[findfirst(
                            ==(sdf.position[1]),
                            qaqc_positions.id,
                        )]
                        return (;
                            position_name = sdf.position_name |> first,
                            psbid = sdf.psbid |> first,
                            position = sdf.position |> first,
                            skew = skew - offset,
                            width,
                            runid,
                            campaign_id = 6,
                        )
                    end
                end
            ) => [
                :position_name,
                :psbid,
                :position,
                :skew,
                :width,
                :runid,
                :campaign_id,
            ],
    )
end

# ╔═╡ 148e42fc-d168-4e07-b4f8-f9c3f8c18efc
df_compare = let
    df_old_measurements =
        filter(:psbid => in(df_extra_measurements.psbid), df_skews_selected)
    left_cols = [:psbid, :skew, :width, :runid, :campaign_id]
    df = vcat(
        select!(df_old_measurements, left_cols),
        select(df_extra_measurements, left_cols),
    )
    sort!(df, :runid)
end

# ╔═╡ 3266d1fd-b8f0-4eb0-9a8e-2050bc9a626f
let
    fig = Figure(size = (600, 500))
    grd1 = fig[1, 1] = GridLayout()
    Label(
        grd1[0, 1:2],
        "comparison of multiple measurements of clock skew for 18 PS Boards",
        tellwidth = false,
    )
    ax1 = Axis(
        grd1[1, 1],
        limits = ((-0.15, 2.35), nothing),
        xticks = (0:2, ["old", "new(382)", "new(404)"]),
        ylabel = "skew / ns",
    )
    ax2 = Axis(
        grd1[1:2, 2],
        limits = ((2.9, 6.7), nothing),
        ylabel = "Δskew / ns",
        xticks = (3:6, string.(3:6)),
        xlabel = "campaign",
        yminorticksvisible = true,
        yminorgridvisible = true,
    )
    colsize!(grd1, 2, Relative(0.4))
    for (key, sdf) in pairs(groupby(df_compare, :psbid))
        @assert sdf.runid[2] == 382 && sdf.runid[3] == 404
        scatterlines!(
            ax1,
            0:1:2,
            sdf.skew,
            color = Makie.wong_colors()[sdf.campaign_id[1] |> Int64],
            alpha = 0.7,
        )
        scatter!(
            ax2,
            sdf.campaign_id[1],
            mean(@view(sdf.skew[2:3])) - sdf.skew[1],
            marker = :x,
            color = Makie.wong_colors()[sdf.campaign_id[1] |> Int64],
            alpha = 0.7,
        )
        errorbars!(
            ax2,
            [sdf.campaign_id[1]],
            [mean(@view(sdf.skew[2:3])) - sdf.skew[1]],
            [std(@view(sdf.skew[2:3]))],
            color = Makie.wong_colors()[sdf.campaign_id[1] |> Int64],
            alpha = 0.4,
            whiskerwidth = 5,
        )
        text!(
            ax1,
            2,
            sdf.skew |> last,
            text = string(sdf.psbid[1]),
            color = (:black, 0.5),
            align = (:left, :center),
        )
        text!(
            ax2,
            sdf.campaign_id[1],
            mean(@view(sdf.skew[2:3])) - sdf.skew[1],
            text = string(sdf.psbid[1]),
            color = (:black, 0.5),
            align = (:left, :center),
        )
    end
    campaigns = df_compare.campaign_id |> unique |> sort! .|> Int64
    Legend(
        grd1[2, 1],
        [
            [LineElement(color = color), MarkerElement(color = color, marker = :circle)] for
            color in Makie.wong_colors()[campaigns]
        ],
        string.(campaigns),
        "campaign id for old measurements",
        orientation = :horizontal,
    )

    fig
end

# ╔═╡ 660f2bd1-d4bc-45a8-9cf6-4e875aa9f7a2
let
    df = filter(:campaign_id => ==(6), df_skews_selected)
    leftjoin!(df, select(qaqc_runs, [:id, :run_datetime]), on = :runid => :id)
    # from UTC to JST(+9h)
    transform!(df, :run_datetime => ByRow(dt -> dt + Hour(9)) => :run_datetime)

    @assert all(!ismissing, df.run_datetime)
    dropmissing!(df, :run_datetime)

    transform!(df, :run_datetime => ByRow(Date) => :run_date)

    fig = Figure()
    gdf = groupby(df, :run_date)
    for (i, sdf) in enumerate(gdf)
        unit_converter = Makie.DateTimeConversion(Time)
        ax = Axis(
            fig[i, 1],
            title = string(keys(gdf)[i]),
            dim1_conversion = unit_converter,
            limits = (
                (
                    Makie.convert_dim_value(unit_converter, Time(9)),
                    Makie.convert_dim_value(unit_converter, Time(20)),
                ),
                (-2, 1),
            ),
        )
        scatter!(ax, sdf.run_datetime .|> Time, sdf.skew, markersize = 7, alpha = 0.7)
        mean_runs = Union{Float64, Missing}[]
        std_runs = Union{Float64, Missing}[]
        datetime_runs = Time[]
        for sdf_samerun in groupby(sdf, :runid)
            push!(mean_runs, mean(sdf_samerun.skew |> skipmissing))
            push!(std_runs, std(sdf_samerun.skew |> skipmissing))
            push!(datetime_runs, sdf_samerun.run_datetime |> first)
        end
        scatterlines!(
            ax,
            datetime_runs,
            mean_runs,
            marker = :hline,
            markersize = 10,
            color = Makie.wong_colors()[2],
        )
        errorbars!(ax, datetime_runs, mean_runs, std_runs, color = Makie.wong_colors()[2])
    end
    fig
end

# ╔═╡ 86437ee6-ccea-43fa-bd93-d86fe055f28d
md"""
# プロットまとめ
- 何らかの異常が見られたものをすべて除外してプロット

!!! todo
    skew測定が使い物になるかのフラグをデータベースにいれる？
"""

# ╔═╡ 2a24c277-6679-49f3-b387-6937b7661a25
invalid_measurements = [215, 291, 442, 460, 462, 545, 799, 802, 444, 255, 435, 1213, 1215, #= TODO: remove =# 1563]

# ╔═╡ 13b4112f-96ab-41a1-8ef1-d940771f6ece
df_skews_selected_valids = let
    df = filter(:psbid => !in(invalid_measurements), df_skews_selected)
    df
end

# ╔═╡ 2dde2b39-8f8c-473b-8fed-393a9e3286d8
sort(df_skews_selected_valids, :skew)

# ╔═╡ 2a579bc2-79f8-4773-8588-a413acb8a6d6
sort(df_skews_selected_valids, :skew, rev = true)

# ╔═╡ cf658de8-a4c5-413e-b5e3-56b77a80336f
sort(df_skews_selected, :width)

# ╔═╡ 7efd380b-a976-4a86-8dff-8afd551f03fb
sort(df_skews_selected, :width, rev = true)

# ╔═╡ 6a856a55-2611-41d0-a2c7-3645c066fc3c
begin
    @assert all(!ismissing, df_skews_selected_valids.skew)
    @assert all(!ismissing, df_skews_selected_valids.width)
    @assert all(==(1), df_skews_selected_valids.riseup)
end

# ╔═╡ 92d701aa-ab90-4c91-977d-2ce92823d130
md"""
## skew分布
"""

# ╔═╡ 4a4ef945-b312-44ed-ab62-ce01fc33f926
let
    bins = range(
        minimum(df_skews_selected_valids.skew) - 5 / 56,
        maximum(df_skews_selected_valids.skew) + 5 / 56,
        step = 2 / 56,
    )
    fig = Figure()
    ax = Axis(
        fig[1, 1],
        title = "skews",
        limits = (nothing, (nothing, nothing)),
        xlabel = "skew / ns",
    )
    sh1 = stephist!(ax, df_skews_selected_valids.skew, bins = bins)
    Legend(
        fig[1, 1],
        [sh1],
        ["""
         n = $(nrow(df_skews_selected_valids))
         μ = $(@sprintf "%.2g" mean(df_skews_selected_valids.skew))
         σ = $(@sprintf "%.2g" std(df_skews_selected_valids.skew))
         """],
        tellwidth = false,
        tellheight = false,
        halign = :right,
        valign = :top,
        margin = (10, 10, 10, 10),
    )
    save("psboard_skew_histogram.svg", fig)
    fig
end

# ╔═╡ 82063c4b-c0cf-4524-83fe-5207bb8363d8
md"""
### skew分布のcampaign依存性
"""

# ╔═╡ dff359b1-4827-40c6-86e4-0915974ef27d
let
    bins = range(
        minimum(df_skews_selected_valids.skew) - 5 / 56,
        maximum(df_skews_selected_valids.skew) + 5 / 56,
        step = 8 / 56,
    )
    fig = Figure()
    ax = Axis(
        fig[1, 1],
        title = "skews",
        limits = (nothing, (nothing, nothing)),
        xlabel = "skew / ns",
    )
    hists = Hist1D[]
    for gdf in groupby(df_skews_selected_valids, :campaign_id)
        push!(hists, Hist1D(gdf.skew, binedges = bins))
    end
    sh1 = stackedhist!(ax, hists, error_color = Pattern('/'))
    labels = ["campaign $i" for i in 1:7]
    elements =
        [PolyElement(polycolor = sh1.attributes.color[][i]) for i in 1:length(labels)]
    Legend(
        fig[1, 1],
        elements,
        labels,
        ["""
         n = $(nrow(df_skews_selected_valids))
         μ = $(@sprintf "%.2g" mean(df_skews_selected_valids.skew))
         σ = $(@sprintf "%.2g" std(df_skews_selected_valids.skew))
         """],
        tellwidth = false,
        tellheight = false,
        halign = :left,
        valign = :top,
        margin = (10, 10, 10, 10),
    )
    fig
end

# ╔═╡ 7920b03b-1d1a-4b51-bfc0-86d1361f2ff1
let
    fig = Figure()
    ax = Axis(
        fig[1, 1],
        xlabel = "psbid",
        ylabel = "skew / ns",
        title = "clock skew time dependency",
    )
    scatter!(ax, df_skews_selected_valids.psbid, df_skews_selected_valids.skew)
    fig
end

# ╔═╡ e640424b-7f7b-4cca-a634-92749ceee170
let
    fig = Figure()
    ax = Axis(
        fig[1, 1],
        xlabel = "runid",
        ylabel = "skew / ns",
        title = "clock skew time dependency",
    )
    @info "" qaqc_runs
    scatter!(ax, df_skews_selected_valids.runid, df_skews_selected_valids.skew)
    fig
end

# ╔═╡ c3236e51-12a3-43e1-afc4-71be54c0a869
let
    fig = Figure()
    ax = Axis(
        fig[1, 1],
        xlabel = "runid",
        ylabel = "skew / ns",
        title = "clock skew time dependency",
        limits = ((290, 320), nothing),
        xminorticksvisible = true,
        xminorgridvisible = true,
    )
    scatter!(ax, df_skews_selected_valids.runid, df_skews_selected_valids.skew)
    text!(
        ax,
        df_skews_selected_valids.runid,
        df_skews_selected_valids.skew |> disallowmissing,
        text = string.(df_skews_selected_valids.psbid),
        color = (:black, 0.3),
        align = (:left, :center),
    )
    df_byruns = combine(
        groupby(df_skews_selected_valids, :runid),
        AsTable(:) =>
            (sdf -> begin
                (mean = mean(sdf.skew), std = std(sdf.skew))
            end) => [:mean, :std],
    )
    scatter!(ax, df_byruns.runid, df_byruns.mean, label = "mean")
    # vlines!(ax, [304])
    let
        rangebars!(
            ax,
            [-1, -1],
            [293, 305],
            [304, 329],
            direction = :x,
            whiskerwidth = 10,
            color = :black,
        )
        text!(ax, [(293 + 304) / 2, (305 + 329) / 2], [-1, -1], text = ["11-11", "11-12"])
    end

    df_highlighted = filter(
        :psbid => in([973, 990, 992, 1030, 1050, 1053, 1110, 1113, 1121, 1141, 1142]),
        df_skews_selected_valids,
    )
    @info "" df_highlighted
    scatter!(
        ax,
        df_highlighted.runid,
        df_highlighted.skew |> disallowmissing,
        marker = :x,
        markersize = 20,
        alpha = 0.3,
    )
    axislegend(ax)
    fig
end

# ╔═╡ 126f5825-25c1-4628-b4ae-ffbd9830833c
let
    df = leftjoin(
        df_skews_selected_valids,
        select(qaqc_runs, [:id, :run_datetime]),
        on = :runid => :id,
    )
    # from UTC to JST(+9h)
    transform!(df, :run_datetime => ByRow(dt -> dt + Hour(9)) => :run_datetime)

    @assert all(!ismissing, df.run_datetime)
    dropmissing!(df, :run_datetime)

    transform!(df, :run_datetime => ByRow(Date) => :run_date)

    fig = Figure(size = (600, 900))
    gdf = groupby(df, :campaign_id)
    for (i, sdf) in enumerate(gdf)
        unit_converter = Makie.DateTimeConversion(Time)
        ax = Axis(
            fig[i, 1],
            title = string(keys(gdf)[i]),
            dim1_conversion = unit_converter,
            limits = (
                (
                    Makie.convert_dim_value(unit_converter, Time(0)),
                    Makie.convert_dim_value(unit_converter, Time(23, 59, 59)),
                ),
                # (-2, 2),
                nothing,
            ),
        )
        sdf = transform(sdf, :run_datetime => ByRow(Time) => :run_time)
        sort!(sdf, :run_time)
        scatter!(ax, sdf.run_datetime .|> Time, sdf.skew, markersize = 7, alpha = 0.7)
        df_runs = combine(
            groupby(sdf, :runid),
            AsTable(:) =>
                (
                    sdf -> begin
                        (;
                            mean = mean(sdf.skew |> skipmissing),
                            std = std(sdf.skew |> skipmissing),
                            run_time = first(sdf.run_time),
                        )
                    end
                ) => [:mean, :std, :run_time],
        )
        sort!(df_runs, :run_time)
        scatterlines!(
            ax,
            df_runs.run_time,
            df_runs.mean,
            marker = :hline,
            markersize = 10,
            color = Makie.wong_colors()[2],
        )
        errorbars!(
            ax,
            df_runs.run_time,
            df_runs.mean,
            df_runs.std,
            color = Makie.wong_colors()[2],
        )
    end
    fig
end

# ╔═╡ 46b2a3cd-d2e6-4277-8b65-9c61f25f69e8
3 / 56

# ╔═╡ 55bad662-cfdd-45c8-81bf-4e65e5c8434e
md"""
## 立ち上がり時間分布
"""

# ╔═╡ 13bb4978-b98d-44a3-a4b6-4241cadc609b
let
    bins = range(
        minimum(df_skews_selected_valids.width) - 1 / 56,
        maximum(df_skews_selected_valids.width) + 2 / 56,
        step = 1 / 56,
    )
    fig = Figure()
    ax = Axis(
        fig[1, 1],
        title = "distribution of rise up spans",
        xlabel = "time / ns",
        xticks = (bins, string.(round.(bins, digits = 3))),
        xticklabelrotation = π / 3,
    )
    h1 = hist!(
        ax,
        df_skews_selected_valids.width,
        bins = bins,
        bar_labels = :y,
        label_formatter = x -> "$(round(Int, x))",
        flip_labels_at = 330,
    )
    Legend(
        fig[1, 1],
        [h1],
        ["""
         n = $(nrow(df_skews_selected_valids))
         μ = $(@sprintf "%.2g" mean(df_skews_selected_valids.width))
         σ = $(@sprintf "%.2g" std(df_skews_selected_valids.width))
         """],
        tellwidth = false,
        tellheight = false,
        halign = :right,
        valign = :top,
        margin = (10, 10, 10, 10),
    )
    save("psboard_clock_rise_span_histogram.svg", fig)
    fig
end

# ╔═╡ 2835ff7c-d6cc-49d8-b35c-0071a8364376
scatter(
    df_skews_selected_valids.runid,
    df_skews_selected_valids.width,
    markersize = 7,
    alpha = 0.2,
    axis = (xlabel = "runid", ylabel = "width"),
)

# ╔═╡ 6c3c7669-d594-425d-bf05-9aa217c7656e
md"""
# turn on curves
- lines on
    - the minimum and maximum skews
    - the minimum and maximum widths
"""

# ╔═╡ e9b3f541-d87d-4424-8b82-be1b7b3273d8
let
    psbid_width_min, psbid_width_max, psbid_width_typical = let
        df_normal = filter(:width => >(0.06), df_skews_selected_sorted)
        sort!(df_normal, :width)
        df_typical = filter(:width => (x -> 0.08 < x < 0.11), df_normal)
        rng = Random.Xoshiro(123)
        id_rand_typical = rand(rng, 1:nrow(df_typical))
        first(df_normal).psbid, last(df_normal).psbid, df_typical[id_rand_typical, :psbid]
    end
    psbid_with_lines = [psbid_width_max, psbid_width_min, psbid_width_typical]

    fig = Figure()
    ax = Axis(
        fig[1, 1],
        title = "clock turn on curves",
        subtitle = "aligned on the last count = 0 points",
        xlabel = "ns",
        ylabel = "counts",
        limits = ((-0.05, 0.15), (0, 1000)),
    )
    for file in clk_files
        psbid, runid = parse_filename(file)
        if psbid == 255
            @info "Si not locked: skipped" psbid
            continue
        end
        single_runs = filter(
            [:psboard_id, :runid] => (
                (ref_psbid, ref_runid) -> begin
                    psbid == ref_psbid && runid == ref_runid
                end
            ),
            qaqc_single_results,
        )
        if nrow(single_runs) != 1
            if nrow(single_runs) != 0
                @info "skipped" psbid runid nrow(single_runs)
            end
            continue
        end
        # offset = qaqc_positions.rising_ns[single_runs.position[1]]
        offset_pos = let
            # df = filter(:psbid => ==(psbid), df_skews_selected_sorted)
            # @assert nrow(df) == 1 "nrow: $(nrow(df)), psbid: $(psbid)"
            # df.skew[1] + 
            qaqc_positions.rising_ns[single_runs.position[1]]
        end
        if psbid in psbid_with_lines
            points =
                eachline(file) .|>
                PSBoardDataBase.ClockParser._parse_line .|>
                (x -> (x[1] - offset_pos, x[2]))
            id_first_over0 = findfirst(points) do ((time, count))
                count > 0
            end
            points = map(points) do ((time, count))
                time - points[id_first_over0][1], count
            end
            stds = map(points) do ((time, count))
                sqrt(count * (1000 - count) / 1000)
            end
            label, color = if psbid == psbid_width_max
                "longest ($psbid)", (:red, 0.8)
            elseif psbid == psbid_width_min
                "shortest ($psbid)", (:blue, 0.8)
            elseif psbid == psbid_width_typical
                "typical ($psbid)", (:green, 0.8)
            end
            scatterlines!(ax, points, label = label, color = color)
            errorbars!(ax, points, stds, whiskerwidth = 10, color = color)
        end
    end
    axislegend(ax, position = :rb)
    save("psboard_clock_turnoncurve.svg", fig)
    fig
end

# ╔═╡ Cell order:
# ╟─f25e7e08-8a73-4cac-ac7c-d310725c558d
# ╠═7c69d12c-80a5-11ef-2674-e155a3483342
# ╠═effa7ed9-2ac4-4468-a474-e2bb662580fe
# ╠═1a6322d4-9deb-4709-aa2e-df7d8be5b16f
# ╠═11537f91-e16b-45f0-9768-6df842371d36
# ╠═268d3015-b8d3-48d9-b74a-062e258e0ec1
# ╟─62105832-df1f-4834-8da6-c542e22207d1
# ╠═dea6f143-7916-4765-92f6-2bfb97a72835
# ╟─41543c0c-d7c4-447b-a268-0d356c88d92c
# ╠═633ecdee-7e2f-4de8-833a-21cd0351c1f1
# ╟─87f1966e-3b07-4f9d-8fc4-7b7aa4319d50
# ╠═f379d43c-9300-41f4-b0fc-3c9d749e3105
# ╟─33e099bc-ac4b-4b5f-88e7-20f4463c98ef
# ╠═0e13f848-0efb-4775-9e3e-518b32588a79
# ╠═181c3fe6-d087-42e2-b175-3fb84c42e3e8
# ╟─dd669f14-989b-45ee-87d8-5d9cf282fafd
# ╠═1e9c3944-0cd4-40da-9014-a9153d4e95ed
# ╟─322cb530-65a5-4973-86f8-01ccc2439cc4
# ╠═c1caca5f-4cfd-4f22-82b4-7925002359e6
# ╠═3e5607fd-2a8a-4a1a-9e7b-3f23ef216fad
# ╠═c1b9c0c3-00f8-4199-b07f-8888f1be625c
# ╠═d6d04013-e0e4-49d5-a450-07ae164bfaa3
# ╠═0e680044-e4e1-4f39-a5c5-afa5c53fc7a7
# ╠═d7541b93-4c49-4dcd-bda0-91e447f44596
# ╟─e7faa647-79cd-4247-a3f2-9868c7b9d4ca
# ╠═3a412a98-4a2d-4bfb-8053-b0f480fae921
# ╟─b38bbed4-8721-4e92-a546-f7926cc07dd3
# ╠═420dce0e-4757-48d9-84ec-7ddfac2fdff6
# ╠═99902640-fee3-4502-9c7e-cb08834bad0b
# ╠═c79c6684-1b03-41b5-aa90-ef8c7a8eb69c
# ╠═a1056aba-e484-4594-908b-4709896d0da0
# ╠═875bec26-e576-4f48-ba14-464bce503d75
# ╟─ec774495-c0be-47a4-9d2c-b48159c07013
# ╠═d082e07c-3b42-4362-bebf-63356979a49b
# ╟─2d41c98b-630e-41e7-9332-25394a6285e8
# ╠═6ff9f397-5db9-45af-9bca-7a8d7756f8da
# ╠═25688d24-5aee-43d3-aff9-b9efa0556070
# ╠═0d3f7f0a-1713-4e22-acbb-7d370fabf5a7
# ╠═239a808c-0411-4542-ae68-6ae6af333bd2
# ╠═8e57bde1-5f97-483d-906e-8ebfb65016d0
# ╠═92c2ac3f-8034-4e9e-aadb-8bb166fbc948
# ╠═893253c3-f0b2-401f-b892-b23291bcf5c1
# ╠═6467dcaa-6bd6-45c7-8c08-b310a09b8b0b
# ╠═19f85f1b-07d3-48c2-9ca6-a5a8eb1ab746
# ╟─0d758cdf-0dda-4dc3-b489-35831812b718
# ╠═79e2f5d8-4609-4e9f-949e-6dc1f88c0b19
# ╠═eae649db-6b2b-4530-83a8-3438f29423cc
# ╠═d607e10e-854f-4652-9a34-9e22a188e315
# ╠═5bef43cf-0f9b-40a2-bd47-620f42c36d8d
# ╠═2795fd06-2f59-4e5b-829d-a8e428646790
# ╟─26976b6c-3954-4a41-a99b-c1aaebdc645d
# ╠═38d472ca-6347-4096-828d-fd1256130a59
# ╠═310710da-ebb2-4f54-b238-38d493a6a533
# ╟─d92e0f9f-ed53-4ad6-a390-5a395e8ab8bc
# ╠═e9964184-1a2a-4ab9-bc22-2705a25393ec
# ╟─bedf8dff-e109-4757-82e5-3232fcad752d
# ╠═f2d0ea9b-ede7-496a-8d1f-9f748f9d1666
# ╠═f5bc8b1a-3fee-48ed-9910-367a1cda632e
# ╠═106b8abe-0040-48f1-b663-c00ca7673520
# ╠═a06d16ee-a4be-40de-8dac-fd818754373c
# ╠═9f3f780d-da6e-44b4-a002-c2f088681e9c
# ╠═5c74d0a8-f856-4988-b8f4-d2e02dc26f52
# ╠═07342d98-3889-4a19-8419-6d113fffb42e
# ╠═939aba6b-b03c-42b6-83b3-9cea5f4eb858
# ╠═aa9b78bc-8d66-4df2-bd06-1cbf21190488
# ╠═76f44129-34c6-451d-af3f-4593dd1dda5c
# ╠═0dd4c5fe-71d0-45d0-897e-51d1aa06bd3b
# ╠═148e42fc-d168-4e07-b4f8-f9c3f8c18efc
# ╠═3266d1fd-b8f0-4eb0-9a8e-2050bc9a626f
# ╠═660f2bd1-d4bc-45a8-9cf6-4e875aa9f7a2
# ╠═86437ee6-ccea-43fa-bd93-d86fe055f28d
# ╠═2a24c277-6679-49f3-b387-6937b7661a25
# ╠═13b4112f-96ab-41a1-8ef1-d940771f6ece
# ╠═2dde2b39-8f8c-473b-8fed-393a9e3286d8
# ╠═2a579bc2-79f8-4773-8588-a413acb8a6d6
# ╠═cf658de8-a4c5-413e-b5e3-56b77a80336f
# ╠═7efd380b-a976-4a86-8dff-8afd551f03fb
# ╠═6a856a55-2611-41d0-a2c7-3645c066fc3c
# ╟─92d701aa-ab90-4c91-977d-2ce92823d130
# ╠═4a4ef945-b312-44ed-ab62-ce01fc33f926
# ╟─82063c4b-c0cf-4524-83fe-5207bb8363d8
# ╠═dff359b1-4827-40c6-86e4-0915974ef27d
# ╠═7920b03b-1d1a-4b51-bfc0-86d1361f2ff1
# ╠═e640424b-7f7b-4cca-a634-92749ceee170
# ╠═c3236e51-12a3-43e1-afc4-71be54c0a869
# ╠═126f5825-25c1-4628-b4ae-ffbd9830833c
# ╠═46b2a3cd-d2e6-4277-8b65-9c61f25f69e8
# ╟─55bad662-cfdd-45c8-81bf-4e65e5c8434e
# ╠═13bb4978-b98d-44a3-a4b6-4241cadc609b
# ╠═2835ff7c-d6cc-49d8-b35c-0071a8364376
# ╠═6c3c7669-d594-425d-bf05-9aa217c7656e
# ╠═e9b3f541-d87d-4424-8b82-be1b7b3273d8
