Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FMPy CI - second try #62

Merged
merged 5 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/Eval.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ jobs:
julia-arch: [x64]

steps:
# Required by PkgEval.jl as xvfb runs into issues with ubuntu 24. See ci.yml workflow of PkgEval.jl repo
- name: "Allow unprivileged user namespaces"
run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0

# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- name: Check out repository
uses: actions/checkout@v4
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/Example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,17 @@ jobs:
needs: [jupyter, pluto]
if: github.event_name != 'pull_request' && github.ref_name == 'main'
runs-on: ubuntu-latest
env:
HAS_TRIGGER_TOKEN: ${{ secrets.FMI_DOC_TRIGGER_PAT != '' }}
steps:
# Trigger an repoisitory dispath event
- name: Repository Dispatch
if: ${{ env.HAS_TRIGGER_TOKEN == 'true' }}
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.FMI_DOC_TRIGGER_PAT }}
repository: 'ThummeTo/FMI.jl'
event-type: trigger-docu
- name: no-token-warning
if: ${{ env.HAS_TRIGGER_TOKEN != 'true' }}
run: echo "::warning title=no_token_for_FMI-Repo::Please trigger FMI-Docs manually!!! automatic building of documentation requires an accesstoken for FMI.jl github repository; it has to be added as secrets.FMI_DOC_TRIGGER_PAT to the FMIExport.jl repo "
4 changes: 4 additions & 0 deletions .github/workflows/TestLTS.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ jobs:
version: ${{ matrix.julia-version }}
arch: ${{ matrix.julia-arch }}

# Set up julia-cache
- name: Set up julia-cache
uses: julia-actions/cache@v2

# Set up cache
- name: "Set up cache"
uses: actions/cache@v4
Expand Down
10 changes: 7 additions & 3 deletions .github/workflows/TestLatest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
fail-fast: false # keep other os running if one fails
matrix:
julia-version: ['1']
julia-arch: [x64]
os: [windows-latest] # ubuntu-latest,
os: [windows-latest, ubuntu-latest]
experimental: [false]

steps:
Expand All @@ -35,6 +35,10 @@ jobs:
version: ${{ matrix.julia-version }}
arch: ${{ matrix.julia-arch }}

# Set up julia-cache
- name: Set up julia-cache
uses: julia-actions/cache@v2

# Set up cache
- name: "Set up cache"
uses: actions/cache@v4
Expand Down Expand Up @@ -66,4 +70,4 @@ jobs:
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
file: lcov.info
files: lcov.info
4 changes: 4 additions & 0 deletions test/Project.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
[deps]
Conda = "8f4d0f93-b110-5947-807f-2305c1781a2d"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
DifferentialEquations = "0c46a032-eb83-5123-abaf-570d42b7fbaa"
FMI = "14a09403-18e3-468f-ad8a-74f8dda2d9ac"
FMIBuild = "226f0e26-6dd6-4589-ada7-1d32f6e1d800"
FMIImport = "9fcbc62e-52a0-44e9-a616-1359a0008194"
FMIZoo = "724179cf-c260-40a9-bd27-cccc6fe2f195"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

Expand Down
55 changes: 0 additions & 55 deletions test/bouncing_ball.jl

This file was deleted.

208 changes: 208 additions & 0 deletions test/bouncing_ball/bouncing_ball.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
#
# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons
# Licensed under the MIT license. See LICENSE file in the project root for details.
#

# export FMU script, currently only available on Windows
if Sys.iswindows()
include(
joinpath(
@__DIR__,
"..",
"..",
"examples",
"FMI2",
"BouncingBall",
"src",
"BouncingBall.jl",
),
)
# check if FMU exists now
@test isfile(fmu_save_path)
fsize = filesize(fmu_save_path) / 1024 / 1024
@test fsize > 300

# TODO: as Exported FMUs are currently not able to be simulated with FMPy, use BouncingBall from FMIZoo instead to test Pipeline
println(
"::warning title=Test-Warning::using FMIZoo BouncingBallFMU instead of exported FMU. \r\n",
)
using FMIZoo
fmu_save_path = FMIZoo.get_model_filename("BouncingBall1D", "Dymola", "2023x")
else
# if not on windows, use BouncingBall from FMIZoo
using FMIZoo
fmu_save_path = FMIZoo.get_model_filename("BouncingBall1D", "Dymola", "2023x")

# check if FMU exists
@test isfile(fmu_save_path)
fsize = filesize(fmu_save_path) / 1024 # / 1024 # check for 300KB instead of 300 MB as FMIZoo FMU is smaller
@test fsize > 300
end

# mutex implementation: indicates running state of fmpy script. File must only be created and cleared afterwards by fmpy script
lockfile = joinpath(pwd(), "bouncing_ball", "lockfile.txt")
# fmpy script puts its logs here
logfile = joinpath(pwd(), "bouncing_ball", "FMPy-log.txt")
# output for scheduled command starting the fmpy script. meight be useful for debugging if logfile does not contain any helpful information on error
outlog = joinpath(pwd(), "bouncing_ball", "outlog.txt")
# fmu-experiment setup
t_start = "0.0"
t_stop = "5.0"
# flag (in logfile), that gets replaced by "@test " by this jl script and evaluated after fmpys completion
juliatestflag = "JULIA_@test:"

# as commandline interface for task sheduling in windows does only allow 261 characters for \TR option, we need an external config file
config_file = joinpath(pwd(), "bouncing_ball", "fmpy-bouncing_ball.config")
open(config_file, "w+") do io
#line 1: lockfile
write(io, lockfile)
write(io, "\n")
#line 2: logfile
write(io, logfile)
write(io, "\n")
#line 3: fmu_save_path
write(io, fmu_save_path)
write(io, "\n")
#line 4: juliatestflag
write(io, juliatestflag)
write(io, "\n")
#line 5: t_start
write(io, t_start)
write(io, "\n")
#line 5: t_stop
write(io, t_stop)
write(io, "\n")
end
script_file = joinpath(pwd(), "bouncing_ball", "fmpy-bouncing_ball.py")

# should not exist but cleanup anyway
if isfile(lockfile)
rm(lockfile)
end
if isfile(logfile)
rm(logfile)
end

# install fmpy
println(readchomp(`python -m pip install FMPy`))

using Dates
# task can only be sheduled at full minutes, schedule with at least one full minute until start to avoid cornercases. 120s achives this optimally (seconds get truncated in minute-based-scheduling)
tasktime = now() + Second(120)
# cleanup github-actions logs
flush(stdout)
flush(stderr)

# the fmpy task that we want to schedule (its stdout and stderr get redirected for debugging, remains empty/non existent if no error occurs)
task_string = "python $script_file $config_file > $outlog 2>&1"

if Sys.iswindows()
# in windows only 261 chars are allowed as command with args
@test length(task_string) < 261
time = Dates.format(tasktime, "HH:MM")
println(
readchomp(
`SCHTASKS /CREATE /SC ONCE /TN "ExternalFMIExportTesting\\BouncingBall-FMPy" /TR "$task_string" /ST $time`,
),
)
elseif Sys.islinux()
time = Dates.format(tasktime, "M")
open("crontab_fmiexport_fmpy_bouncingball", "w+") do io
# hourly as there were issues when scheduling at fixed hour (not starting, possibly due to timzone issues or am/pm; did not investigate further)
write(io, "$time * * * * $task_string")
write(io, "\n")
end
println(readchomp(`crontab crontab_fmiexport_fmpy_bouncingball`))
end

# print schedule status for debugging
if Sys.iswindows()
println(
readchomp(
`SCHTASKS /query /tn "ExternalFMIExportTesting\\BouncingBall-FMPy" /v /fo list`,
),
)
elseif Sys.islinux()
println(readchomp(`crontab -l`))
end

# wait until task has started for shure
sleep(150)

# cleanup
rm(config_file)

# we will wait a maximum time for fmpy. usually it should be done within seconds... (keep in mind maximum runtime on github runner)
time_wait_max = datetime2unix(now()) + 900.0

# fmpy still running or generated output in its logfile
if isfile(lockfile) || isfile(logfile)
if isfile(lockfile)
println(
"FMPy-Task still running, will wait for termination or a maximum time of " *
string(round((time_wait_max - datetime2unix(now())) / 60.0, digits = 2)) *
" minutes from now.",
)
end
while isfile(lockfile) && datetime2unix(now()) < time_wait_max
sleep(10)
end

# print schedule status for debugging
if Sys.iswindows()
println(
readchomp(
`SCHTASKS /query /tn "ExternalFMIExportTesting\\BouncingBall-FMPy" /v /fo list`,
),
)
elseif Sys.islinux()
println(readchomp(`crontab -l`))
end

println("wating for FMPy-Task ended; FMPy-Task done: " * string(!isfile(lockfile)))

# sould not be existing/be empty; if there was no error, fmpy script redirected all its output to its own logfile (see FMPy_log below)
if isfile(outlog)
println("CMD output of FMPy-Task: ")
for line in readlines(outlog)
println(line)
end
println("------------------END_of_CMD_output--------------------")
end

# FMPy_log
if !isfile(logfile)
println("No log of FMPy-Task found")
@test false # error: no log by fmpy created
else
println("Log of FMPy-Task: ")
for line in readlines(logfile)
println(line)
# if there is a testflag, evaluate the line
if contains(line, juliatestflag)
eval(Meta.parse("@test " * split(line, juliatestflag)[2]))
end
end
println("------------------END_of_FMPy_log--------------------")

fmpy_log = String(read(logfile))
# if no testflags occur in log, why are we running the script?! we need testflags in the log to evaluate the result...
@test occursin(juliatestflag, fmpy_log)
end
else
println(
"Error in FMPy-testsetup: Windows task scheduler or cron did not start FMPy successfully or FMPy terminated prematurely before generating lockfile or logfile",
)
@test false
end

# cleanup scheduling
if Sys.iswindows()
println(
readchomp(`SCHTASKS /DELETE /TN ExternalFMIExportTesting\\BouncingBall-FMPy /f`),
)
elseif Sys.islinux()
println(readchomp(`crontab -r`))
end

rm(fmu_save_path)
Loading
Loading