|
| 1 | +# run_julia_mixed_demo.jl -- run Python/Julia concore demo pairs |
| 2 | +# |
| 3 | +# Usage: |
| 4 | +# julia demo/run_julia_mixed_demo.jl [maxtime] |
| 5 | + |
| 6 | +const DEMO_DIR = @__DIR__ |
| 7 | +const REPO_ROOT = dirname(DEMO_DIR) |
| 8 | +const MAXTIME = length(ARGS) >= 1 ? parse(Int, ARGS[1]) : 5 |
| 9 | +const PYTHON = get(ENV, "PYTHON", Sys.iswindows() ? "python" : "python3") |
| 10 | + |
| 11 | +function link_dir(target, link) |
| 12 | + ispath(link) && return |
| 13 | + try |
| 14 | + symlink(target, link; dir_target=true) |
| 15 | + catch |
| 16 | + if Sys.iswindows() |
| 17 | + run(`cmd /c mklink /J $link $target`) |
| 18 | + else |
| 19 | + rethrow() |
| 20 | + end |
| 21 | + end |
| 22 | +end |
| 23 | + |
| 24 | +function prepare_node(node_dir, in_edge, out_edge, source_file, runtime_files) |
| 25 | + mkpath(node_dir) |
| 26 | + link_dir(in_edge, joinpath(node_dir, "in1")) |
| 27 | + link_dir(out_edge, joinpath(node_dir, "out1")) |
| 28 | + cp(source_file, joinpath(node_dir, basename(source_file)); force=true) |
| 29 | + for runtime_file in runtime_files |
| 30 | + cp(runtime_file, joinpath(node_dir, basename(runtime_file)); force=true) |
| 31 | + end |
| 32 | +end |
| 33 | + |
| 34 | +function wait_with_timeout(procs, timeout_sec) |
| 35 | + start = time() |
| 36 | + while any(process_running, procs) |
| 37 | + if time() - start > timeout_sec |
| 38 | + for proc in procs |
| 39 | + process_running(proc) && kill(proc) |
| 40 | + end |
| 41 | + return false |
| 42 | + end |
| 43 | + sleep(0.1) |
| 44 | + end |
| 45 | + return true |
| 46 | +end |
| 47 | + |
| 48 | +function run_pair(label, controller_source, controller_cmd, pm_source, pm_cmd) |
| 49 | + workspace = mktempdir(; cleanup=true) |
| 50 | + cu_dir = joinpath(workspace, "CU") |
| 51 | + pym_dir = joinpath(workspace, "PYM") |
| 52 | + cz_dir = joinpath(workspace, "CZ") |
| 53 | + pz_dir = joinpath(workspace, "PZ") |
| 54 | + |
| 55 | + mkpath(cu_dir) |
| 56 | + mkpath(pym_dir) |
| 57 | + |
| 58 | + write(joinpath(cu_dir, "u"), "[0.0, 0.0]") |
| 59 | + write(joinpath(pym_dir, "ym"), "[0.0, 0.0]") |
| 60 | + write(joinpath(cu_dir, "concore.maxtime"), string(MAXTIME)) |
| 61 | + write(joinpath(pym_dir, "concore.maxtime"), string(MAXTIME)) |
| 62 | + |
| 63 | + prepare_node( |
| 64 | + cz_dir, |
| 65 | + pym_dir, |
| 66 | + cu_dir, |
| 67 | + controller_source, |
| 68 | + endswith(controller_source, ".jl") ? |
| 69 | + [joinpath(REPO_ROOT, "concore.jl")] : |
| 70 | + [joinpath(REPO_ROOT, "concore.py"), joinpath(REPO_ROOT, "concore_base.py")], |
| 71 | + ) |
| 72 | + prepare_node( |
| 73 | + pz_dir, |
| 74 | + cu_dir, |
| 75 | + pym_dir, |
| 76 | + pm_source, |
| 77 | + endswith(pm_source, ".jl") ? |
| 78 | + [joinpath(REPO_ROOT, "concore.jl")] : |
| 79 | + [joinpath(REPO_ROOT, "concore.py"), joinpath(REPO_ROOT, "concore_base.py")], |
| 80 | + ) |
| 81 | + |
| 82 | + write(joinpath(cz_dir, "concore.iport"), "{'ym': 1}") |
| 83 | + write(joinpath(cz_dir, "concore.oport"), "{'u': 1}") |
| 84 | + write(joinpath(pz_dir, "concore.iport"), "{'u': 1}") |
| 85 | + write(joinpath(pz_dir, "concore.oport"), "{'ym': 1}") |
| 86 | + |
| 87 | + controller_out = joinpath(cz_dir, "concoreout.txt") |
| 88 | + pm_out = joinpath(pz_dir, "concoreout.txt") |
| 89 | + |
| 90 | + println("Running $label") |
| 91 | + controller_proc = open(controller_out, "w") do out |
| 92 | + run(pipeline(Cmd(controller_cmd; dir=cz_dir), stdout=out, stderr=out); wait=false) |
| 93 | + end |
| 94 | + pm_proc = open(pm_out, "w") do out |
| 95 | + run(pipeline(Cmd(pm_cmd; dir=pz_dir), stdout=out, stderr=out); wait=false) |
| 96 | + end |
| 97 | + |
| 98 | + completed = wait_with_timeout([controller_proc, pm_proc], 60) |
| 99 | + wait(controller_proc) |
| 100 | + wait(pm_proc) |
| 101 | + |
| 102 | + controller_log = read(controller_out, String) |
| 103 | + pm_log = read(pm_out, String) |
| 104 | + final_ym = read(joinpath(pym_dir, "ym"), String) |
| 105 | + |
| 106 | + ok = completed && |
| 107 | + controller_proc.exitcode == 0 && |
| 108 | + pm_proc.exitcode == 0 && |
| 109 | + occursin("retry=", controller_log) && |
| 110 | + occursin("retry=", pm_log) && |
| 111 | + startswith(strip(final_ym), "[") |
| 112 | + |
| 113 | + if ok |
| 114 | + println("PASS: $label") |
| 115 | + else |
| 116 | + println("FAIL: $label") |
| 117 | + println("--- controller output ---") |
| 118 | + print(controller_log) |
| 119 | + println("--- plant output ---") |
| 120 | + print(pm_log) |
| 121 | + end |
| 122 | + |
| 123 | + return ok |
| 124 | +end |
| 125 | + |
| 126 | +julia_cmd = Base.julia_cmd() |
| 127 | + |
| 128 | +ok_controller_jl = run_pair( |
| 129 | + "Julia controller + Python plant", |
| 130 | + joinpath(DEMO_DIR, "controller_jl.jl"), |
| 131 | + `$julia_cmd controller_jl.jl`, |
| 132 | + joinpath(DEMO_DIR, "pm.py"), |
| 133 | + `$PYTHON pm.py`, |
| 134 | +) |
| 135 | + |
| 136 | +ok_pm_jl = run_pair( |
| 137 | + "Python controller + Julia plant", |
| 138 | + joinpath(DEMO_DIR, "controller.py"), |
| 139 | + `$PYTHON controller.py`, |
| 140 | + joinpath(DEMO_DIR, "pm_jl.jl"), |
| 141 | + `$julia_cmd pm_jl.jl`, |
| 142 | +) |
| 143 | + |
| 144 | +exit(ok_controller_jl && ok_pm_jl ? 0 : 1) |
0 commit comments