opp-repl-result-analysis — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited opp-repl-result-analysis (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
On current opp_repl main, reading results from a completed run_simulations() is a one-liner — .get_scalars() / .get_vectors() / .get_histograms() on the result object returns pandas DataFrames ready for aggregation. For older opp_repl or for external / CLI scripting, use opp_scavetool or the scave Python API directly.
| Approach | Use when |
|---|---|
(A) r.get_scalars() etc. | Current opp_repl, inside the REPL / MCP. |
(B) opp_scavetool CLI | Shell scripts, CI, agents without Python deps |
(C) omnetpp.scave.results | Direct scave API (older opp_repl, more control) |
SimulationTaskResult)r = run_simulations(simulation_project=p, sim_time_limit="100s") tr = r.results[0]
df = tr.get_scalars() # .sca -> DataFrame df = tr.get_scalars(include_fields=False) # skip statistic fields df = tr.get_scalars(include_runattrs=True) # add run attributes as columns
df = tr.get_vectors() # .vec -> DataFrame df = tr.get_histograms() # histograms from .sca
Docstrings quote the exact signatures:
get_scalars(include_fields=True, include_runattrs=False, kwargs) get_vectors(include_runattrs=False, kwargs) get_histograms(include_runattrs=False, **kwargs)
MultipleSimulationTaskResults)r = run_simulations(simulation_project=p, sim_time_limit="100s")
df = r.get_scalars() # concatenated across all DONE runs df = r.get_vectors() # same df = r.get_histograms() # same
MultipleSimulationTaskResults.get_*() skips any non-DONE result (an FAILed / ERRORed task contributes nothing) and concatenates the remaining per-run DataFrames with ignore_index=True.
df = r.get_scalars() means = df.groupby("name").value.mean() sems = df.groupby("name").value.sem() # if you want error bars
agg = df.groupby(["module", "name"]).value.mean()
Columns in the returned DataFrame: runID, module, name, value, configname, experiment, inifile, network, repetition, runnumber, seedset, plus whatever run attributes the simulation emitted. include_runattrs=True adds extra run-attr columns.
Reference: https://github.com/omnetpp/opp_repl/blob/main/doc/task_results.md#reading-simulation-results
opp_scavetool CLIThe opp_scavetool binary queries and exports .sca / .vec files. Useful when you want CSV / JSON for an external pipeline or when omnetpp is not importable in the calling Python process.
opp_scavetool query -l results/.sca # list all items opp_scavetool query -n -T s results/.sca # unique scalar names opp_scavetool query -l -p -g --tabs results/*.sca # per-run, machine-readable
opp_scavetool export -F CSV-R \ --add-fields-as-scalars \ -o scalars.csv \ results/*.sca
CSV columns include: run, type, module, name, value, count, mean, stddev, min, max, vectime, vecvalue ...
opp_scavetool export -F JSON -o scalars.json results/*.sca
opp_scavetool index results/*.vec
opp_scavetool query -l --filter 'name("dropCount")' results/.sca opp_scavetool query -l --filter 'module("queue")' results/.sca
Run opp_scavetool help filter for the full grammar.
omnetpp.scave.results Python APIThis module is bundled with every OMNeT++ install at $__omnetpp_root_dir/python/omnetpp/scave/results.py. After sourcing OMNeT++'s setenv, its path is on PYTHONPATH.
from omnetpp.scave.results import ( read_result_files, # load .sca/.vec into internal table get_scalars, # DataFrame of scalars get_vectors, # DataFrame of time-series get_statistics, # aggregated statistics get_histograms, # histograms get_parameters, # NED parameter assignments get_runs, # run metadata get_runattrs, # per-run attributes get_itervars, # iteration variables )
df = read_result_files("results/*.sca", include_fields_as_scalars=True) scalars = get_scalars(df, include_runattrs=True)
r.get_scalars() (path A) is a thin wrapper over this; use path C directly when you need read_result_files glob / filter parameters that the task-result method doesn't expose.
scripts/parse_scalars.py wraps both paths B and C so any caller can get scalars out of a results directory without needing to reason about which path is available:
python3 scripts/parse_scalars.py results/ python3 scripts/parse_scalars.py results/ --filter 'name("dropCount")' python3 scripts/parse_scalars.py results/ --group-by name --output table
The script sets up $__omnetpp_root_dir/python on PYTHONPATH itself, tries the Python API first, falls back to opp_scavetool on ImportError. Outputs valid JSON (NaN-clean), CSV, or ASCII table.
Use the script when:
When you're already inside opp_repl, prefer r.get_scalars() — same result, no subprocess.
.sca file format.sca is line-oriented text. Keywords per line:
version 3 run PureAloha1-0-20260420-13:30:58-35234 attr configname PureAloha1 config cpu-time-limit 1s param Aloha.host[*].iaTime exponential(2s) scalar Aloha.host[0] channelUtilization 0.1234 statistic Aloha.server collisionLength:mean 0.0456 field Aloha.server collisionLength:mean:count 123
Do NOT write a regex parser — the format has edge cases (quoting, multi-line histograms, encoding) that make hand-rolled parsers fragile. Always go through scavetool or the Python API.
Each SimulationTaskResult exposes file paths too, for cases the .get_*() methods don't cover:
tr.stdout_file_path # e.g. "results/General-#0.out" tr.scalar_file_path # e.g. "results/General-#0.sca" tr.vector_file_path # e.g. "results/General-#0.vec" tr.eventlog_file_path # eventlog, if recorded
To feed these into scavetool / read_result_files manually:
p = tr.task.simulation_config.simulation_project working_dir = tr.task.simulation_config.working_directory sca_path = p.get_full_path(working_dir + "/" + tr.scalar_file_path)
But 9 times out of 10 tr.get_scalars() is what you want.
.vec fileswritten by the simulation don't have a .vci index until opp_scavetool index runs (or any export / get_vectors call reads them). Large batches — index once after the batch finishes.
r.get_scalars() returnsone row per (run, module, name). Aggregate explicitly: df.groupby(["module","name"]).value.mean().
tr.get_scalars()) unpacks statistic fields (count, mean, stddev, min, max) as individual scalar rows. For run comparisons that's usually what you want. For raw per-@statistic queries, pass include_fields=False.
r.get_vectors() loadsevery timestamp into memory. Filter at the scavetool level first when working with long time-series: opp_scavetool export -F CSV-R --filter 'name("throughput")' -o thr.csv results/*.vec.
results/ is ALWAYS relative tothe simulation's working directory, not the project root or the CWD of the REPL. Use simulation_project.get_full_path(...) when building paths manually.
MultipleSimulationTaskResults.get_*()silently skips non-DONE results. Check r.is_all_results_done() or r.get_error_results() first if your aggregation looks off.
opp-repl-running-simulations — produces the .sca / .vec files.opp-repl-tasks-and-results — full SimulationTaskResult API.opp-repl-project-scaffolding — getting to a state where .scafiles exist in the first place.
opp-repl-troubleshooting — when runs fail before producing results.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.