Skip to content

Commit

Permalink
debug version of FMPy tests
Browse files Browse the repository at this point in the history
#TODO should be removed before PR
  • Loading branch information
0815Creeper committed Dec 17, 2024
1 parent 88da6a2 commit 4e2cba4
Show file tree
Hide file tree
Showing 11 changed files with 308 additions and 73 deletions.
11 changes: 10 additions & 1 deletion .github/workflows/Example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ on:
push:
branches:
- main
#TODO
- tests-on-fmPy-0.4.1-unformatted
paths:
- 'src/**'
- 'examples/**'
Expand Down Expand Up @@ -140,13 +142,20 @@ jobs:
call-docu:
needs: [jupyter, pluto]
if: github.event_name != 'pull_request' && github.ref_name == 'main'
#TODO
if: (github.event_name != 'pull_request' && github.ref_name == 'main') || github.ref_name == 'tests-on-fmPy-0.4.1-unformatted'
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 "
6 changes: 4 additions & 2 deletions .github/workflows/TestLatest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ on:
push:
branches:
- main
#TODO
- tests-on-fmPy-0.4.1-unformatted
paths:
- 'src/**'
- 'test/**'
Expand All @@ -16,11 +18,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 Down
2 changes: 1 addition & 1 deletion examples/FMI2/BouncingBall/src/BouncingBall.jl
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ fmu_save_path = joinpath(tmpDir, "BouncingBall.fmu")

fmu = FMIBUILD_CONSTRUCTOR()
using FMIBuild: saveFMU # <= this must be excluded during export, because FMIBuild cannot execute itself (but it is able to build)
saveFMU(fmu, fmu_save_path; debug=true, compress=false) # <= this must be excluded during export, because saveFMU would start an infinite build loop with itself (debug=true allows debug messages, but is slow during execution!)
#TODO saveFMU(fmu, fmu_save_path; debug=true, compress=false) # <= this must be excluded during export, because saveFMU would start an infinite build loop with itself (debug=true allows debug messages, but is slow during execution!)

### some tests ###
using FMI, DifferentialEquations
Expand Down
2 changes: 1 addition & 1 deletion examples/FMI2/Manipulation/src/Manipulation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ fmu_save_path = joinpath(tmpDir, "Manipulation.fmu")
sourceFMU = FMIZoo.get_model_filename("SpringDamperPendulum1D", "Dymola", "2022x")
fmu = FMIBUILD_CONSTRUCTOR(dirname(sourceFMU))
import FMIBuild:saveFMU # <= this must be excluded during export, because FMIBuild cannot execute itself (but it is able to build)
saveFMU(fmu, fmu_save_path; resources=Dict(sourceFMU=>"SpringDamperPendulum1D.fmu")) # <= this must be excluded during export, because fmi2Save would start an infinte build loop with itself
#TODO saveFMU(fmu, fmu_save_path; resources=Dict(sourceFMU=>"SpringDamperPendulum1D.fmu")) # <= this must be excluded during export, because fmi2Save would start an infinte build loop with itself

# some tests
# using FMI
Expand Down
2 changes: 1 addition & 1 deletion examples/FMI2/NeuralFMU/src/NeuralFMU.jl
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ fmu_save_path = joinpath(tmpDir, "NeuralFMU.fmu")
sourceFMU = FMIZoo.get_model_filename("SpringDamperPendulum1D", "Dymola", "2022x")
fmu = FMIBUILD_CONSTRUCTOR(dirname(sourceFMU))
import FMIBuild:saveFMU # <= this must be excluded during export, because FMIBuild cannot execute itself (but it is able to build)
saveFMU(fmu, fmu_save_path; compress=false, debug=true, resources=Dict(sourceFMU=>"SpringDamperPendulum1D.fmu")) # <= this must be excluded during export, because fmi2Save would start an infinte build loop with itself
#TODO saveFMU(fmu, fmu_save_path; compress=false, debug=true, resources=Dict(sourceFMU=>"SpringDamperPendulum1D.fmu")) # <= this must be excluded during export, because fmi2Save would start an infinte build loop with itself

### some tests ###
# using FMI
Expand Down
4 changes: 2 additions & 2 deletions examples/jupyter-src/Export.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@
"metadata": {},
"outputs": [],
"source": [
"saveFMU(fmu, fmu_save_path, fmu_source_path; debug=false, compress=false) # feel free to set debug true, disabled for documentation building\n",
"#TODO saveFMU(fmu, fmu_save_path, fmu_source_path; debug=false, compress=false) # feel free to set debug true, disabled for documentation building\n",
"#saveFMU(fmu_save_path, fmu_source_path; debug=false, compress=false) this meight be the format after the next release"
]
},
Expand All @@ -311,7 +311,7 @@
"outputs": [],
"source": [
"mkpath(\"Export_files\")\n",
"cp(fmu_save_path, joinpath(\"Export_files\", \"BouncingBall.fmu\"))"
"#TODO cp(fmu_save_path, joinpath(\"Export_files\", \"BouncingBall.fmu\"))"
]
},
{
Expand Down
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
#TODO @test isfile(fmu_save_path)
#TODO fsize = filesize(fmu_save_path) / 1024 / 1024
#TODO @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

0 comments on commit 4e2cba4

Please sign in to comment.