Расчёт тиристора
Автор
import Pkg
for pkg in ["Plots", "DataFrames"]
if Base.find_package(pkg) === nothing
Pkg.add(pkg)
end
end
using Plots
using DataFrames
gr()
default(fmt = :png)
model_file = "VT.engee" # @param {type:"string"}
model_name = "VT"
max_plot_points = 1000 # @param {type:"slider",min:100,max:1000,step:100}
network_period = 0.02
simulation_stop_time = 1.0
script_dir = @__DIR__
output_dir = script_dir
"""
Ищет файл модели рядом со скриптом или в текущей рабочей папке.
Это полезно при запуске из GUI Engee и через API, где `pwd()` может отличаться.
"""
function resolve_existing_file(file_name)
candidates = [
file_name,
joinpath(pwd(), file_name),
joinpath(script_dir, file_name),
]
for candidate in candidates
if isfile(candidate)
return candidate
end
end
error("Файл не найден: " * file_name * "; pwd=" * pwd() * "; script_dir=" * script_dir)
end
"""
Закрывает ранее открытую модель с тем же именем.
Ошибки намеренно подавляются: если модель не была открыта, продолжать безопасно.
"""
function try_close_model(name)
try
engee.close(name; force=true)
catch
end
end
"""
Включает логирование выбранного порта и возвращает `false`, если порт не найден.
Так скрипт легче переносить между версиями модели с немного другими именами портов.
"""
function safe_set_log(port_path)
try
engee.set_log(port_path)
return true
catch err
@warn "Не удалось включить логирование" port_path exception=(err, catch_backtrace())
return false
end
end
"""
Читает строковое значение параметра модели.
`fallback` нужен, чтобы диагностическая строка в конце ячейки формировалась даже при ошибке чтения.
"""
function safe_model_param(model, name, fallback="")
try
return string(engee.get_param(model, name))
catch
return fallback
end
end
"""
Временно задает параметр модели в открытой сессии.
В этом скрипте так ограничивается `StopTime`; исходный файл `VT.engee` не сохраняется.
"""
function safe_set_model_param!(model, name, value)
try
engee.set_param!(model, name => value)
return true
catch err
@warn "Не удалось временно задать параметр модели" name value exception=(err, catch_backtrace())
return false
end
end
"""
Приводит значения WorkspaceArray/DataFrame к `Vector{Float64}`.
Некоторые сигналы Engee приходят как скаляры, а некоторые как одноэлементные контейнеры.
"""
function values_to_float(raw)
y = Float64[]
for v in raw
if v isa Number
push!(y, Float64(v))
else
push!(y, Float64(first(v)))
end
end
return y
end
"""
Извлекает пары `(t, y)` из результата моделирования.
Функция поддерживает DataFrame и WorkspaceArray, потому что формат результата зависит от способа логирования.
"""
function signal_xy(obj)
df = obj
if !(df isa AbstractDataFrame)
try
df = DataFrame(df)
catch
try
df = collect(df)
catch
end
end
end
if df isa AbstractDataFrame
nms = names(df)
if length(nms) == 1
y = values_to_float(collect(df[!, 1]))
t = collect(range(0.0, stop=simulation_stop_time, length=length(y)))
return t, y
end
t_idx = findfirst(x -> lowercase(String(x)) in ["time", "t"], nms)
t_idx === nothing && (t_idx = 1)
y_idx = findfirst(i -> i != t_idx, eachindex(nms))
y_idx === nothing && (y_idx = length(nms))
t = Float64.(collect(df[!, t_idx]))
y = values_to_float(collect(df[!, y_idx]))
return t, y
end
if hasproperty(df, :time) && hasproperty(df, :value)
t = Float64.(collect(getproperty(df, :time)))
raw = collect(getproperty(df, :value))
y = [v isa Number ? Float64(v) : Float64(first(v)) for v in raw]
return t, y
end
error("Неизвестный формат результата: " * string(typeof(df)))
end
"""
Ищет сигнал по списку возможных имен.
Это защищает скрипт от переименования линии или автоматического имени порта в Engee.
"""
function get_signal(results, candidates)
for key in candidates
try
return signal_xy(results[key]), key
catch
end
end
error("Не найден сигнал по ключам: " * join(candidates, ", "))
end
"""
Прореживает сигнал до `max_points` точек без изменения диапазона времени.
Так графики остаются читаемыми и не перегружают память Engee.
"""
function thin(t, y; max_points=1000)
n = min(length(t), length(y))
n == 0 && return Float64[], Float64[]
if n <= max_points
return t[1:n], y[1:n]
end
idx = unique(round.(Int, range(1, n; length=max_points)))
return t[idx], y[idx]
end
"""
Вычисляет скользящее среднее по временному окну `window_s`.
Для сети 50 Гц окно 0.02 с соответствует одному периоду и подавляет коммутационную пульсацию.
"""
function moving_average_by_time(t, y, window_s)
n = min(length(t), length(y))
out = similar(y, n)
left = 1
acc = 0.0
for right in 1:n
acc += y[right]
while t[right] - t[left] > window_s && left < right
acc -= y[left]
left += 1
end
out[right] = acc / (right - left + 1)
end
return out
end