459 lines
14 KiB
Bash
Executable File
459 lines
14 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -u
|
|
|
|
SCRIPT_DIR="$(
|
|
cd "$(dirname "${BASH_SOURCE[0]}")" &&
|
|
pwd
|
|
)"
|
|
PACKAGE_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
|
|
DEFAULT_SOURCE="$PACKAGE_ROOT/baseline/smoke_s2_binary_recording_from_s01.marte"
|
|
DEFAULT_OUTPUT_ROOT="$PACKAGE_ROOT/configurations/generated/S03H1/from_patcher"
|
|
|
|
EXPECTED_SOURCE_SHA256="8ecc8ba55e46e9f6fb8f304404d4070ff4c2ea5b9c783a69113bc08b457859d1"
|
|
EXPECTED_TEST_SHA256="d037561b32760fbd0e2aec9f1eefc5efda27b1918eca4742c89092a8a2f7e92e"
|
|
EXPECTED_DIAGNOSTIC_SHA256="cd999ff249452298d650a22c8fe5b0b03bf48c0735f4630befdec8209cbb13a3"
|
|
|
|
fail() {
|
|
code="$1"
|
|
shift
|
|
printf "S03_H1_BUILD_STATUS=FAIL\n" >&2
|
|
printf "S03_H1_BUILD_ERROR=%s\n" "$*" >&2
|
|
exit "$code"
|
|
}
|
|
|
|
usage() {
|
|
printf "Uso:\n" >&2
|
|
printf " %s TEST|DIAGNOSTIC\n" "$0" >&2
|
|
printf " %s <source-S02> <output-config> TEST|DIAGNOSTIC\n" "$0" >&2
|
|
}
|
|
|
|
case "$#" in
|
|
1)
|
|
SOURCE_CONFIG="$DEFAULT_SOURCE"
|
|
MODE="$1"
|
|
case "$MODE" in
|
|
TEST)
|
|
OUTPUT_CONFIG="$DEFAULT_OUTPUT_ROOT/smoke_s03h1_execution_profiles_test_from_s02.marte"
|
|
;;
|
|
DIAGNOSTIC)
|
|
OUTPUT_CONFIG="$DEFAULT_OUTPUT_ROOT/smoke_s03h1_execution_profiles_diagnostic_from_s02.marte"
|
|
;;
|
|
*)
|
|
usage
|
|
fail 20 "UNKNOWN_MODE:$MODE"
|
|
;;
|
|
esac
|
|
;;
|
|
3)
|
|
SOURCE_CONFIG="$1"
|
|
OUTPUT_CONFIG="$2"
|
|
MODE="$3"
|
|
;;
|
|
*)
|
|
usage
|
|
fail 20 "INVALID_ARGUMENT_COUNT:$#"
|
|
;;
|
|
esac
|
|
|
|
case "$MODE" in
|
|
TEST)
|
|
EXPECTED_OUTPUT_SHA256="$EXPECTED_TEST_SHA256"
|
|
DISPLAY_CLASS="GAMDataSource"
|
|
;;
|
|
DIAGNOSTIC)
|
|
EXPECTED_OUTPUT_SHA256="$EXPECTED_DIAGNOSTIC_SHA256"
|
|
DISPLAY_CLASS="LoggerDataSource"
|
|
;;
|
|
*)
|
|
usage
|
|
fail 20 "UNKNOWN_MODE:$MODE"
|
|
;;
|
|
esac
|
|
|
|
if [ ! -f "$SOURCE_CONFIG" ]; then
|
|
fail 21 "SOURCE_NOT_FOUND:$SOURCE_CONFIG"
|
|
fi
|
|
|
|
SOURCE_SHA256="$(sha256sum -- "$SOURCE_CONFIG" | awk '{print $1}')"
|
|
printf "MODE=%s\n" "$MODE"
|
|
printf "SOURCE_CONFIG=%s\n" "$SOURCE_CONFIG"
|
|
printf "EXPECTED_SOURCE_SHA256=%s\n" "$EXPECTED_SOURCE_SHA256"
|
|
printf "ACTUAL_SOURCE_SHA256=%s\n" "$SOURCE_SHA256"
|
|
|
|
if [ "$SOURCE_SHA256" != "$EXPECTED_SOURCE_SHA256" ]; then
|
|
fail 22 "SOURCE_HASH_MISMATCH:$SOURCE_SHA256"
|
|
fi
|
|
|
|
OUTPUT_DIR="$(dirname "$OUTPUT_CONFIG")"
|
|
if ! mkdir -p -- "$OUTPUT_DIR"; then
|
|
fail 23 "OUTPUT_DIRECTORY_CREATE_FAILED:$OUTPUT_DIR"
|
|
fi
|
|
|
|
if [ -e "$OUTPUT_CONFIG" ] && [ ! -f "$OUTPUT_CONFIG" ]; then
|
|
fail 24 "OUTPUT_EXISTS_NOT_REGULAR_FILE:$OUTPUT_CONFIG"
|
|
fi
|
|
|
|
TEMP_CONFIG="$(mktemp "$OUTPUT_DIR/.s03h1_candidate_XXXXXX.marte")" ||
|
|
fail 25 "TEMPORARY_FILE_CREATE_FAILED:$OUTPUT_DIR"
|
|
|
|
cleanup() {
|
|
rm -f -- "$TEMP_CONFIG"
|
|
}
|
|
trap cleanup EXIT HUP INT TERM
|
|
|
|
PYTHONHASHSEED=0 python3 - \
|
|
"$SOURCE_CONFIG" \
|
|
"$TEMP_CONFIG" \
|
|
"$MODE" \
|
|
"$DISPLAY_CLASS" <<'PY_S03H1_PATCHER'
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
source = Path(sys.argv[1])
|
|
destination = Path(sys.argv[2])
|
|
mode = sys.argv[3]
|
|
display_class = sys.argv[4]
|
|
text = source.read_text(encoding="utf-8")
|
|
|
|
|
|
def require_count(haystack, needle, expected, label):
|
|
observed = haystack.count(needle)
|
|
print(f"{label}_COUNT={observed}")
|
|
if observed != expected:
|
|
raise RuntimeError(
|
|
f"{label}_COUNT_EXPECTED_{expected}_OBSERVED_{observed}"
|
|
)
|
|
|
|
|
|
def replace_once(haystack, old, new, label):
|
|
require_count(haystack, old, 1, label)
|
|
return haystack.replace(old, new, 1)
|
|
|
|
|
|
for forbidden in (
|
|
"+SentinelProducer =",
|
|
"SentinelScalarA",
|
|
"SentinelScalarB",
|
|
"SentinelVector",
|
|
"s03_output.bin",
|
|
):
|
|
if forbidden in text:
|
|
raise RuntimeError("SOURCE_ALREADY_CONTAINS_S03_CONTENT:" + forbidden)
|
|
|
|
if mode not in {"TEST", "DIAGNOSTIC"}:
|
|
raise RuntimeError("UNKNOWN_MODE:" + mode)
|
|
if display_class not in {"GAMDataSource", "LoggerDataSource"}:
|
|
raise RuntimeError("UNKNOWN_DISPLAY_CLASS:" + display_class)
|
|
|
|
constant_gam = """ +SentinelProducer = {
|
|
Class = ConstantGAM
|
|
OutputSignals = {
|
|
SentinelScalarA = {
|
|
DataSource = DDB1
|
|
Type = uint32
|
|
NumberOfElements = 1
|
|
Default = 324508639
|
|
}
|
|
SentinelScalarB = {
|
|
DataSource = DDB1
|
|
Type = uint32
|
|
NumberOfElements = 1
|
|
Default = 610839776
|
|
}
|
|
SentinelVector = {
|
|
DataSource = DDB1
|
|
Type = uint32
|
|
NumberOfDimensions = 1
|
|
NumberOfElements = 3
|
|
Default = {16909060 286397204 555885348}
|
|
}
|
|
}
|
|
}
|
|
"""
|
|
|
|
gam_display = f""" +GAMDisplay = {{
|
|
Class = IOGAM
|
|
InputSignals = {{
|
|
Counter = {{
|
|
DataSource = DDB1
|
|
Type = uint32
|
|
}}
|
|
Time = {{
|
|
DataSource = DDB1
|
|
Type = uint32
|
|
}}
|
|
State1_Thread1_CycleTime = {{
|
|
Alias = State1.Thread1_CycleTime
|
|
DataSource = Timings
|
|
Type = uint32
|
|
}}
|
|
SentinelScalarA = {{
|
|
DataSource = DDB1
|
|
Type = uint32
|
|
NumberOfElements = 1
|
|
}}
|
|
SentinelScalarB = {{
|
|
DataSource = DDB1
|
|
Type = uint32
|
|
NumberOfElements = 1
|
|
}}
|
|
SentinelVector = {{
|
|
DataSource = DDB1
|
|
Type = uint32
|
|
NumberOfDimensions = 1
|
|
NumberOfElements = 3
|
|
}}
|
|
}}
|
|
OutputSignals = {{
|
|
Counter = {{
|
|
DataSource = Display
|
|
Type = uint32
|
|
}}
|
|
Time = {{
|
|
DataSource = Display
|
|
Type = uint32
|
|
}}
|
|
State1_Thread1_CycleTime = {{
|
|
DataSource = Display
|
|
Type = uint32
|
|
}}
|
|
SentinelScalarA = {{
|
|
DataSource = Display
|
|
Type = uint32
|
|
NumberOfElements = 1
|
|
}}
|
|
SentinelScalarB = {{
|
|
DataSource = Display
|
|
Type = uint32
|
|
NumberOfElements = 1
|
|
}}
|
|
SentinelVector = {{
|
|
DataSource = Display
|
|
Type = uint32
|
|
NumberOfDimensions = 1
|
|
NumberOfElements = 3
|
|
}}
|
|
}}
|
|
}}
|
|
"""
|
|
|
|
sentinel_signals = """ SentinelScalarA = {
|
|
DataSource = DDB1
|
|
Type = uint32
|
|
NumberOfElements = 1
|
|
}
|
|
SentinelScalarB = {
|
|
DataSource = DDB1
|
|
Type = uint32
|
|
NumberOfElements = 1
|
|
}
|
|
SentinelVector = {
|
|
DataSource = DDB1
|
|
Type = uint32
|
|
NumberOfDimensions = 1
|
|
NumberOfElements = 3
|
|
}
|
|
"""
|
|
|
|
filewriter_signals = """ SentinelScalarA = {
|
|
Type = uint32
|
|
NumberOfElements = 1
|
|
}
|
|
SentinelScalarB = {
|
|
Type = uint32
|
|
NumberOfElements = 1
|
|
}
|
|
SentinelVector = {
|
|
Type = uint32
|
|
NumberOfDimensions = 1
|
|
NumberOfElements = 3
|
|
}
|
|
"""
|
|
|
|
require_count(text, " +GAMDisplay = {\n", 1, "GAMDISPLAY_START_ANCHOR")
|
|
require_count(text, " +IOGAM_Writer = {\n", 1, "IOGAM_WRITER_START_ANCHOR")
|
|
display_start = text.index(" +GAMDisplay = {\n")
|
|
writer_start = text.index(" +IOGAM_Writer = {\n", display_start)
|
|
text = text[:display_start] + constant_gam + gam_display + text[writer_start:]
|
|
|
|
writer_start = text.index(" +IOGAM_Writer = {\n")
|
|
writer_end = text.index(" }\n +Data = {\n", writer_start)
|
|
writer = text[writer_start:writer_end]
|
|
|
|
writer = replace_once(
|
|
writer,
|
|
""" State1_Thread1_CycleTime = {
|
|
Alias = State1.Thread1_CycleTime
|
|
DataSource = Timings
|
|
Type = uint32
|
|
}
|
|
}
|
|
OutputSignals = {
|
|
""",
|
|
""" State1_Thread1_CycleTime = {
|
|
Alias = State1.Thread1_CycleTime
|
|
DataSource = Timings
|
|
Type = uint32
|
|
}
|
|
""" + sentinel_signals + """ }
|
|
OutputSignals = {
|
|
""",
|
|
"WRITER_INPUT_ANCHOR",
|
|
)
|
|
|
|
writer_output_signals = sentinel_signals.replace(
|
|
"DataSource = DDB1", "DataSource = FileWriter"
|
|
)
|
|
writer = replace_once(
|
|
writer,
|
|
""" State1_Thread1_CycleTime = {
|
|
DataSource = FileWriter
|
|
Type = uint32
|
|
}
|
|
}
|
|
}
|
|
""",
|
|
""" State1_Thread1_CycleTime = {
|
|
DataSource = FileWriter
|
|
Type = uint32
|
|
}
|
|
""" + writer_output_signals + """ }
|
|
}
|
|
""",
|
|
"WRITER_OUTPUT_ANCHOR",
|
|
)
|
|
text = text[:writer_start] + writer + text[writer_end:]
|
|
|
|
text = replace_once(
|
|
text,
|
|
""" +Display = {
|
|
Class = LoggerDataSource
|
|
}
|
|
""",
|
|
f""" +Display = {{
|
|
Class = {display_class}
|
|
}}
|
|
""",
|
|
"DISPLAY_DATASOURCE_ANCHOR",
|
|
)
|
|
|
|
filewriter_start = text.index(" +FileWriter = {\n")
|
|
filewriter_end = text.index(" }\n +States = {\n", filewriter_start)
|
|
filewriter = text[filewriter_start:filewriter_end]
|
|
filewriter = replace_once(
|
|
filewriter,
|
|
' Filename = "__MARTE_RUN_DIR__/s02_output.bin"\n',
|
|
' Filename = "__MARTE_RUN_DIR__/s03_output.bin"\n',
|
|
"OUTPUT_FILENAME_ANCHOR",
|
|
)
|
|
filewriter = replace_once(
|
|
filewriter,
|
|
""" State1_Thread1_CycleTime = {
|
|
Type = uint32
|
|
}
|
|
}
|
|
}
|
|
""",
|
|
""" State1_Thread1_CycleTime = {
|
|
Type = uint32
|
|
}
|
|
""" + filewriter_signals + """ }
|
|
}
|
|
""",
|
|
"FILEWRITER_SIGNAL_ANCHOR",
|
|
)
|
|
text = text[:filewriter_start] + filewriter + text[filewriter_end:]
|
|
|
|
text = replace_once(
|
|
text,
|
|
"Functions = {GAMTimer GAMDisplay IOGAM_Writer}",
|
|
"Functions = {GAMTimer SentinelProducer GAMDisplay IOGAM_Writer}",
|
|
"THREAD_FUNCTION_LIST_ANCHOR",
|
|
)
|
|
|
|
text = text.rstrip("\n") + "\n"
|
|
|
|
checks = {
|
|
"SENTINEL_PRODUCER": text.count("+SentinelProducer ="),
|
|
"CONSTANT_GAM_CLASS": text.count("Class = ConstantGAM"),
|
|
"GAMDISPLAY_OBJECT": text.count("+GAMDisplay ="),
|
|
"SENTINEL_SCALAR_A_DECLARATIONS": text.count("SentinelScalarA ="),
|
|
"SENTINEL_SCALAR_B_DECLARATIONS": text.count("SentinelScalarB ="),
|
|
"SENTINEL_VECTOR_DECLARATIONS": text.count("SentinelVector ="),
|
|
"VECTOR_DIMENSION_DECLARATIONS": text.count("NumberOfDimensions = 1"),
|
|
"VECTOR_CARDINALITY_DECLARATIONS": text.count("NumberOfElements = 3"),
|
|
"S03_OUTPUT_FILENAME": text.count("__MARTE_RUN_DIR__/s03_output.bin"),
|
|
"S02_OUTPUT_FILENAME": text.count("s02_output.bin"),
|
|
"RUN_DIR_PLACEHOLDER": text.count("__MARTE_RUN_DIR__"),
|
|
"FILEWRITER_CLASS": text.count("Class = FileDataSource::FileWriter"),
|
|
"THREAD_FUNCTION_LIST": text.count(
|
|
"Functions = {GAMTimer SentinelProducer GAMDisplay IOGAM_Writer}"
|
|
),
|
|
"DISPLAY_DATASOURCE_BLOCK": text.count(
|
|
f" +Display = {{\n Class = {display_class}\n }}\n"
|
|
),
|
|
}
|
|
expected = {
|
|
"SENTINEL_PRODUCER": 1,
|
|
"CONSTANT_GAM_CLASS": 1,
|
|
"GAMDISPLAY_OBJECT": 1,
|
|
"SENTINEL_SCALAR_A_DECLARATIONS": 6,
|
|
"SENTINEL_SCALAR_B_DECLARATIONS": 6,
|
|
"SENTINEL_VECTOR_DECLARATIONS": 6,
|
|
"VECTOR_DIMENSION_DECLARATIONS": 6,
|
|
"VECTOR_CARDINALITY_DECLARATIONS": 6,
|
|
"S03_OUTPUT_FILENAME": 1,
|
|
"S02_OUTPUT_FILENAME": 0,
|
|
"RUN_DIR_PLACEHOLDER": 1,
|
|
"FILEWRITER_CLASS": 1,
|
|
"THREAD_FUNCTION_LIST": 1,
|
|
"DISPLAY_DATASOURCE_BLOCK": 1,
|
|
}
|
|
|
|
for name in sorted(checks):
|
|
print(f"{name}_COUNT={checks[name]}")
|
|
if checks != expected:
|
|
raise RuntimeError(f"STRUCTURAL_CHECK_MISMATCH:{checks!r}")
|
|
|
|
destination.write_text(text, encoding="utf-8")
|
|
print("TRANSFORM_STATUS=PASS")
|
|
print("STRUCTURAL_CHECK_STATUS=PASS")
|
|
PY_S03H1_PATCHER
|
|
PYTHON_RC=$?
|
|
|
|
if [ "$PYTHON_RC" -ne 0 ]; then
|
|
fail 26 "TRANSFORM_FAILED_RC_$PYTHON_RC"
|
|
fi
|
|
|
|
GENERATED_SHA256="$(sha256sum -- "$TEMP_CONFIG" | awk '{print $1}')"
|
|
printf "GENERATED_SHA256=%s\n" "$GENERATED_SHA256"
|
|
printf "EXPECTED_OUTPUT_SHA256=%s\n" "$EXPECTED_OUTPUT_SHA256"
|
|
|
|
if [ "$GENERATED_SHA256" != "$EXPECTED_OUTPUT_SHA256" ]; then
|
|
fail 27 "OUTPUT_HASH_MISMATCH:$GENERATED_SHA256"
|
|
fi
|
|
|
|
if [ -f "$OUTPUT_CONFIG" ]; then
|
|
if cmp -s -- "$TEMP_CONFIG" "$OUTPUT_CONFIG"; then
|
|
printf "S03_H1_BUILD_STATUS=ALREADY_VALID\n"
|
|
printf "MODE=%s\n" "$MODE"
|
|
printf "OUTPUT_CONFIG=%s\n" "$OUTPUT_CONFIG"
|
|
printf "OUTPUT_SHA256=%s\n" "$GENERATED_SHA256"
|
|
exit 0
|
|
fi
|
|
fail 28 "OUTPUT_EXISTS_BUT_DIFFERS:$OUTPUT_CONFIG"
|
|
fi
|
|
|
|
chmod 0644 "$TEMP_CONFIG" || fail 29 "OUTPUT_CHMOD_FAILED:$TEMP_CONFIG"
|
|
if ! mv -- "$TEMP_CONFIG" "$OUTPUT_CONFIG"; then
|
|
fail 30 "ATOMIC_PUBLISH_FAILED:$OUTPUT_CONFIG"
|
|
fi
|
|
trap - EXIT HUP INT TERM
|
|
|
|
printf "S03_H1_BUILD_STATUS=CREATED\n"
|
|
printf "MODE=%s\n" "$MODE"
|
|
printf "OUTPUT_CONFIG=%s\n" "$OUTPUT_CONFIG"
|
|
printf "OUTPUT_SHA256=%s\n" "$GENERATED_SHA256"
|