第 6 组:源码链接、构建和文档约束闭环完成
This commit is contained in:
@@ -5,6 +5,11 @@ cd "$(dirname "$0")"
|
|||||||
|
|
||||||
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
||||||
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
|
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
|
||||||
|
build_jobs=${CNC_SIM_BUILD_JOBS:-8}
|
||||||
|
if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then
|
||||||
|
echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest"
|
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest"
|
||||||
manifest=$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest")
|
manifest=$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest")
|
||||||
@@ -113,8 +118,8 @@ emcmake cmake -S core -B build/wasm \
|
|||||||
-DCNC_SIM_LINUXCNC_ROOT="$linuxcnc_root" \
|
-DCNC_SIM_LINUXCNC_ROOT="$linuxcnc_root" \
|
||||||
-DCNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST="$manifest" \
|
-DCNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST="$manifest" \
|
||||||
-DCNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE=ON
|
-DCNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE=ON
|
||||||
cmake --build build/wasm --target cnc_sim_wasm_runtime_probe
|
cmake --build build/wasm --target cnc_sim_wasm_runtime_probe --parallel "$build_jobs"
|
||||||
cmake --build build/wasm --target cnc_sim_wasm
|
cmake --build build/wasm --target cnc_sim_wasm --parallel "$build_jobs"
|
||||||
mkdir -p web/public
|
mkdir -p web/public
|
||||||
cp build/wasm/cnc_sim.js web/public/cnc_sim.js
|
cp build/wasm/cnc_sim.js web/public/cnc_sim.js
|
||||||
cp build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm
|
cp build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm
|
||||||
|
|||||||
@@ -6,6 +6,29 @@ cd "$(dirname "$0")"
|
|||||||
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
||||||
manifest=${1:-}
|
manifest=${1:-}
|
||||||
require_complete=${2:-}
|
require_complete=${2:-}
|
||||||
|
allowed_manifest_groups=(
|
||||||
|
asset
|
||||||
|
binding
|
||||||
|
blocked
|
||||||
|
blocked-header
|
||||||
|
component
|
||||||
|
config
|
||||||
|
core
|
||||||
|
generated
|
||||||
|
header
|
||||||
|
metadata
|
||||||
|
remap
|
||||||
|
tooldata
|
||||||
|
)
|
||||||
|
|
||||||
|
contains_manifest_group() {
|
||||||
|
local candidate=$1
|
||||||
|
local group
|
||||||
|
for group in "${allowed_manifest_groups[@]}"; do
|
||||||
|
[[ "$group" == "$candidate" ]] && return 0
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
if [[ "$manifest" == "--root-only" ]]; then
|
if [[ "$manifest" == "--root-only" ]]; then
|
||||||
manifest=
|
manifest=
|
||||||
@@ -32,6 +55,43 @@ if [[ ! -d "$linuxcnc_root" ]]; then
|
|||||||
echo "missing LinuxCNC root: $linuxcnc_root" >&2
|
echo "missing LinuxCNC root: $linuxcnc_root" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
|
||||||
|
|
||||||
|
if [[ -n "$manifest" ]]; then
|
||||||
|
seen_manifest_sources=()
|
||||||
|
manifest_line_number=0
|
||||||
|
while IFS= read -r manifest_line || [[ -n "$manifest_line" ]]; do
|
||||||
|
manifest_line_number=$((manifest_line_number + 1))
|
||||||
|
case "$manifest_line" in
|
||||||
|
""|\#*)
|
||||||
|
continue
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
IFS=: read -r group path note extra <<<"$manifest_line"
|
||||||
|
if [[ -z "$group" || -z "$path" || -z "$note" || -n "${extra:-}" ]]; then
|
||||||
|
echo "bad manifest line $manifest_line_number: $manifest_line" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! contains_manifest_group "$group"; then
|
||||||
|
echo "unknown manifest group: $group" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ "$path" = /* || "$path" == .. || "$path" == ../* || "$path" == */.. || "$path" == */../* ]]; then
|
||||||
|
echo "manifest source must be LinuxCNC-root relative: $path" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if printf '%s\n' "${seen_manifest_sources[@]}" | grep -Fx -- "$path" >/dev/null; then
|
||||||
|
echo "duplicate manifest source: $path" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ ! -f "$linuxcnc_root/$path" ]]; then
|
||||||
|
echo "missing manifest source: $path" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
seen_manifest_sources+=("$path")
|
||||||
|
done < "$manifest"
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ -n "$manifest" && "$require_complete" == "--require-rs274-complete" ]]; then
|
if [[ -n "$manifest" && "$require_complete" == "--require-rs274-complete" ]]; then
|
||||||
rs274_dir="$linuxcnc_root/src/emc/rs274ngc"
|
rs274_dir="$linuxcnc_root/src/emc/rs274ngc"
|
||||||
@@ -96,6 +156,148 @@ if [[ -n "$manifest" && "$require_complete" == "--require-kinematics-complete" ]
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
mapfile -t kinematics_header_sources < <(
|
||||||
|
find "$kinematics_dir" -maxdepth 1 -type f -name '*.h' \
|
||||||
|
| sed "s#^$linuxcnc_root/##" \
|
||||||
|
| sort
|
||||||
|
)
|
||||||
|
mapfile -t manifest_kinematics_header_sources < <(
|
||||||
|
awk -F: '
|
||||||
|
$1 !~ /^($|#)/ && $1 == "header" && $2 ~ /^src\/emc\/kinematics\/.*\.h$/ { print $2 }
|
||||||
|
' "$manifest" | sort -u
|
||||||
|
)
|
||||||
|
mapfile -t kinematics_metadata_sources < <(
|
||||||
|
find "$kinematics_dir" -maxdepth 1 -type f \
|
||||||
|
\( -name 'Submakefile' -o -name 'meson.build' \) \
|
||||||
|
| sed "s#^$linuxcnc_root/##" \
|
||||||
|
| sort
|
||||||
|
)
|
||||||
|
mapfile -t manifest_kinematics_metadata_sources < <(
|
||||||
|
awk -F: '
|
||||||
|
$1 !~ /^($|#)/ && $1 == "metadata" && $2 ~ /^src\/emc\/kinematics\/(Submakefile|meson\.build)$/ { print $2 }
|
||||||
|
' "$manifest" | sort -u
|
||||||
|
)
|
||||||
|
mapfile -t kinematics_asset_sources < <(
|
||||||
|
find "$kinematics_dir" -maxdepth 1 -type f -name '*.fig' \
|
||||||
|
| sed "s#^$linuxcnc_root/##" \
|
||||||
|
| sort
|
||||||
|
)
|
||||||
|
mapfile -t manifest_kinematics_asset_sources < <(
|
||||||
|
awk -F: '
|
||||||
|
$1 !~ /^($|#)/ && $1 == "asset" && $2 ~ /^src\/emc\/kinematics\/.*\.fig$/ { print $2 }
|
||||||
|
' "$manifest" | sort -u
|
||||||
|
)
|
||||||
|
|
||||||
|
missing_kinematics_header_sources=()
|
||||||
|
for source in "${kinematics_header_sources[@]}"; do
|
||||||
|
if ! printf '%s\n' "${manifest_kinematics_header_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
|
||||||
|
missing_kinematics_header_sources+=("$source")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if ((${#missing_kinematics_header_sources[@]} > 0)); then
|
||||||
|
echo "manifest does not group all LinuxCNC kinematics headers as header sources: $manifest" >&2
|
||||||
|
printf 'missing kinematics header source: %s\n' "${missing_kinematics_header_sources[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
missing_kinematics_metadata_sources=()
|
||||||
|
for source in "${kinematics_metadata_sources[@]}"; do
|
||||||
|
if ! printf '%s\n' "${manifest_kinematics_metadata_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
|
||||||
|
missing_kinematics_metadata_sources+=("$source")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if ((${#missing_kinematics_metadata_sources[@]} > 0)); then
|
||||||
|
echo "manifest does not group all LinuxCNC kinematics build metadata as metadata sources: $manifest" >&2
|
||||||
|
printf 'missing kinematics metadata source: %s\n' "${missing_kinematics_metadata_sources[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
missing_kinematics_asset_sources=()
|
||||||
|
for source in "${kinematics_asset_sources[@]}"; do
|
||||||
|
if ! printf '%s\n' "${manifest_kinematics_asset_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
|
||||||
|
missing_kinematics_asset_sources+=("$source")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if ((${#missing_kinematics_asset_sources[@]} > 0)); then
|
||||||
|
echo "manifest does not group all LinuxCNC kinematics reference assets as asset sources: $manifest" >&2
|
||||||
|
printf 'missing kinematics asset source: %s\n' "${missing_kinematics_asset_sources[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
posemath_dir="$linuxcnc_root/src/libnml/posemath"
|
||||||
|
if [[ ! -d "$posemath_dir" ]]; then
|
||||||
|
echo "missing LinuxCNC posemath source directory: $posemath_dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
mapfile -t posemath_core_sources < <(
|
||||||
|
find "$posemath_dir" -maxdepth 1 -type f \
|
||||||
|
\( -name '*.c' -o -name '*.cc' \) \
|
||||||
|
| sed "s#^$linuxcnc_root/##" \
|
||||||
|
| sort
|
||||||
|
)
|
||||||
|
mapfile -t manifest_posemath_core_sources < <(
|
||||||
|
awk -F: '
|
||||||
|
$1 !~ /^($|#)/ && $1 == "core" && $2 ~ /^src\/libnml\/posemath\/.*\.(c|cc)$/ { print $2 }
|
||||||
|
' "$manifest" | sort -u
|
||||||
|
)
|
||||||
|
mapfile -t posemath_header_sources < <(
|
||||||
|
find "$posemath_dir" -maxdepth 1 -type f -name '*.h' \
|
||||||
|
| sed "s#^$linuxcnc_root/##" \
|
||||||
|
| sort
|
||||||
|
)
|
||||||
|
mapfile -t manifest_posemath_header_sources < <(
|
||||||
|
awk -F: '
|
||||||
|
$1 !~ /^($|#)/ && $1 == "header" && $2 ~ /^src\/libnml\/posemath\/.*\.h$/ { print $2 }
|
||||||
|
' "$manifest" | sort -u
|
||||||
|
)
|
||||||
|
mapfile -t posemath_metadata_sources < <(
|
||||||
|
find "$posemath_dir" -maxdepth 1 -type f \
|
||||||
|
\( -name 'Submakefile' -o -name 'meson.build' \) \
|
||||||
|
| sed "s#^$linuxcnc_root/##" \
|
||||||
|
| sort
|
||||||
|
)
|
||||||
|
mapfile -t manifest_posemath_metadata_sources < <(
|
||||||
|
awk -F: '
|
||||||
|
$1 !~ /^($|#)/ && $1 == "metadata" && $2 ~ /^src\/libnml\/posemath\/(Submakefile|meson\.build)$/ { print $2 }
|
||||||
|
' "$manifest" | sort -u
|
||||||
|
)
|
||||||
|
|
||||||
|
missing_posemath_core_sources=()
|
||||||
|
for source in "${posemath_core_sources[@]}"; do
|
||||||
|
if ! printf '%s\n' "${manifest_posemath_core_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
|
||||||
|
missing_posemath_core_sources+=("$source")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if ((${#missing_posemath_core_sources[@]} > 0)); then
|
||||||
|
echo "manifest does not group all LinuxCNC posemath C/C++ sources as core sources: $manifest" >&2
|
||||||
|
printf 'missing posemath core source: %s\n' "${missing_posemath_core_sources[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
missing_posemath_header_sources=()
|
||||||
|
for source in "${posemath_header_sources[@]}"; do
|
||||||
|
if ! printf '%s\n' "${manifest_posemath_header_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
|
||||||
|
missing_posemath_header_sources+=("$source")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if ((${#missing_posemath_header_sources[@]} > 0)); then
|
||||||
|
echo "manifest does not group all LinuxCNC posemath headers as header sources: $manifest" >&2
|
||||||
|
printf 'missing posemath header source: %s\n' "${missing_posemath_header_sources[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
missing_posemath_metadata_sources=()
|
||||||
|
for source in "${posemath_metadata_sources[@]}"; do
|
||||||
|
if ! printf '%s\n' "${manifest_posemath_metadata_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
|
||||||
|
missing_posemath_metadata_sources+=("$source")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if ((${#missing_posemath_metadata_sources[@]} > 0)); then
|
||||||
|
echo "manifest does not group all LinuxCNC posemath build metadata as metadata sources: $manifest" >&2
|
||||||
|
printf 'missing posemath metadata source: %s\n' "${missing_posemath_metadata_sources[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
mapfile -t m428_m430_remap_sources < <(
|
mapfile -t m428_m430_remap_sources < <(
|
||||||
find "$linuxcnc_root/configs" -path '*/remap_subs/*' -type f \
|
find "$linuxcnc_root/configs" -path '*/remap_subs/*' -type f \
|
||||||
\( -name '428remap.ngc' -o -name '429remap.ngc' -o -name '430remap.ngc' \) \
|
\( -name '428remap.ngc' -o -name '429remap.ngc' -o -name '430remap.ngc' \) \
|
||||||
@@ -165,4 +367,724 @@ if [[ -n "$manifest" && "$require_complete" == "--require-kinematics-complete" ]
|
|||||||
printf 'missing M428/M429/M430 INI source: %s\n' "${missing_m428_m430_ini_sources[@]}" >&2
|
printf 'missing M428/M429/M430 INI source: %s\n' "${missing_m428_m430_ini_sources[@]}" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
mapfile -t manifest_component_sources < <(
|
||||||
|
awk -F: '
|
||||||
|
$1 !~ /^($|#)/ && $1 == "component" { print $2 }
|
||||||
|
' "$manifest" | sort -u
|
||||||
|
)
|
||||||
|
mapfile -t manifest_generated_sources < <(
|
||||||
|
awk -F: '
|
||||||
|
$1 !~ /^($|#)/ && $1 == "generated" { print $2 }
|
||||||
|
' "$manifest" | sort -u
|
||||||
|
)
|
||||||
|
mapfile -t manifest_metadata_sources < <(
|
||||||
|
awk -F: '
|
||||||
|
$1 !~ /^($|#)/ && $1 == "metadata" { print $2 }
|
||||||
|
' "$manifest" | sort -u
|
||||||
|
)
|
||||||
|
m428_m430_kinematics_component_sources=()
|
||||||
|
m428_m430_kinematics_generated_sources=()
|
||||||
|
for source in "${manifest_m428_m430_ini_sources[@]}"; do
|
||||||
|
while IFS= read -r kinematics_name; do
|
||||||
|
[[ -n "$kinematics_name" ]] || continue
|
||||||
|
component_source="src/hal/components/$kinematics_name.comp"
|
||||||
|
generated_source="src/objects/hal/components/$kinematics_name.c"
|
||||||
|
if [[ -f "$linuxcnc_root/$component_source" ]]; then
|
||||||
|
m428_m430_kinematics_component_sources+=("$component_source")
|
||||||
|
fi
|
||||||
|
if [[ -f "$linuxcnc_root/$generated_source" ]]; then
|
||||||
|
m428_m430_kinematics_generated_sources+=("$generated_source")
|
||||||
|
fi
|
||||||
|
done < <(
|
||||||
|
sed -n 's/^[[:space:]]*KINEMATICS[[:space:]]*=[[:space:]]*\([^[:space:]#;]*\).*/\1/Ip' "$linuxcnc_root/$source"
|
||||||
|
)
|
||||||
|
done
|
||||||
|
mapfile -t m428_m430_kinematics_component_sources < <(
|
||||||
|
printf '%s\n' "${m428_m430_kinematics_component_sources[@]}" | sed '/^$/d' | sort -u
|
||||||
|
)
|
||||||
|
mapfile -t m428_m430_kinematics_generated_sources < <(
|
||||||
|
printf '%s\n' "${m428_m430_kinematics_generated_sources[@]}" | sed '/^$/d' | sort -u
|
||||||
|
)
|
||||||
|
|
||||||
|
missing_m428_m430_kinematics_component_sources=()
|
||||||
|
for source in "${m428_m430_kinematics_component_sources[@]}"; do
|
||||||
|
if ! printf '%s\n' "${manifest_component_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
|
||||||
|
missing_m428_m430_kinematics_component_sources+=("$source")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if ((${#missing_m428_m430_kinematics_component_sources[@]} > 0)); then
|
||||||
|
echo "manifest does not cover all LinuxCNC component sources referenced by M428/M429/M430 INI KINEMATICS entries: $manifest" >&2
|
||||||
|
printf 'missing M428/M429/M430 KINEMATICS component source: %s\n' "${missing_m428_m430_kinematics_component_sources[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
missing_m428_m430_kinematics_generated_sources=()
|
||||||
|
for source in "${m428_m430_kinematics_generated_sources[@]}"; do
|
||||||
|
if ! printf '%s\n' "${manifest_generated_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
|
||||||
|
missing_m428_m430_kinematics_generated_sources+=("$source")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if ((${#missing_m428_m430_kinematics_generated_sources[@]} > 0)); then
|
||||||
|
echo "manifest does not cover all LinuxCNC generated component sources referenced by M428/M429/M430 INI KINEMATICS entries: $manifest" >&2
|
||||||
|
printf 'missing M428/M429/M430 KINEMATICS generated source: %s\n' "${missing_m428_m430_kinematics_generated_sources[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
generated_component_metadata_sources=()
|
||||||
|
for source in "${manifest_generated_sources[@]}"; do
|
||||||
|
case "$source" in
|
||||||
|
src/objects/hal/components/*.c)
|
||||||
|
metadata_source=${source%.c}.mak
|
||||||
|
if [[ -f "$linuxcnc_root/$metadata_source" ]]; then
|
||||||
|
generated_component_metadata_sources+=("$metadata_source")
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
mapfile -t generated_component_metadata_sources < <(
|
||||||
|
printf '%s\n' "${generated_component_metadata_sources[@]}" | sed '/^$/d' | sort -u
|
||||||
|
)
|
||||||
|
|
||||||
|
missing_generated_component_metadata_sources=()
|
||||||
|
for source in "${generated_component_metadata_sources[@]}"; do
|
||||||
|
if ! printf '%s\n' "${manifest_metadata_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
|
||||||
|
missing_generated_component_metadata_sources+=("$source")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if ((${#missing_generated_component_metadata_sources[@]} > 0)); then
|
||||||
|
echo "manifest does not cover all LinuxCNC generated component make metadata adjacent to generated kinematics sources: $manifest" >&2
|
||||||
|
printf 'missing generated component make metadata source: %s\n' "${missing_generated_component_metadata_sources[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
missing_m428_m430_ini_remap_sources=()
|
||||||
|
unresolved_m428_m430_ini_remaps=()
|
||||||
|
for source in "${manifest_m428_m430_ini_sources[@]}"; do
|
||||||
|
config="$linuxcnc_root/$source"
|
||||||
|
config_dir=$(cd "$(dirname "$config")" && pwd)
|
||||||
|
mapfile -t subroutine_paths < <(
|
||||||
|
sed -n 's/^[[:space:]]*SUBROUTINE_PATH[[:space:]]*=[[:space:]]*//Ip' "$config" |
|
||||||
|
sed 's/[[:space:]]*[#;].*$//; s/^[[:space:]]*//; s/[[:space:]]*$//'
|
||||||
|
)
|
||||||
|
if ((${#subroutine_paths[@]} == 0)); then
|
||||||
|
subroutine_paths=(".")
|
||||||
|
fi
|
||||||
|
|
||||||
|
for mcode in 428 429 430; do
|
||||||
|
ngc_name=$(sed -n "s/.*REMAP[[:space:]]*=[[:space:]]*M$mcode[[:space:]][^#;]*ngc[[:space:]]*=[[:space:]]*\\([^[:space:]#;]*\\).*/\\1/Ip" "$config" |
|
||||||
|
head -n 1)
|
||||||
|
[[ -n "$ngc_name" ]] || continue
|
||||||
|
[[ "$ngc_name" == *.ngc ]] || ngc_name="$ngc_name.ngc"
|
||||||
|
|
||||||
|
resolved_source=
|
||||||
|
for subroutine_path in "${subroutine_paths[@]}"; do
|
||||||
|
[[ -n "$subroutine_path" ]] || continue
|
||||||
|
IFS=: read -ra path_parts <<<"$subroutine_path"
|
||||||
|
for path_part in "${path_parts[@]}"; do
|
||||||
|
[[ -n "$path_part" ]] || continue
|
||||||
|
if [[ "$path_part" = /* ]]; then
|
||||||
|
candidate_dir=$path_part
|
||||||
|
else
|
||||||
|
candidate_dir="$config_dir/$path_part"
|
||||||
|
fi
|
||||||
|
if [[ -f "$candidate_dir/$ngc_name" ]]; then
|
||||||
|
resolved_abs=$(cd "$(dirname "$candidate_dir/$ngc_name")" && pwd)/$(basename "$candidate_dir/$ngc_name")
|
||||||
|
resolved_source=${resolved_abs#"$linuxcnc_root/"}
|
||||||
|
break 2
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z "$resolved_source" ]]; then
|
||||||
|
unresolved_m428_m430_ini_remaps+=("$source M$mcode ngc=$ngc_name")
|
||||||
|
elif ! printf '%s\n' "${manifest_m428_m430_remap_sources[@]}" | grep -Fx -- "$resolved_source" >/dev/null; then
|
||||||
|
missing_m428_m430_ini_remap_sources+=("$resolved_source")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
if ((${#unresolved_m428_m430_ini_remaps[@]} > 0)); then
|
||||||
|
echo "LinuxCNC M428/M429/M430 INI remap sources could not be resolved through SUBROUTINE_PATH: $manifest" >&2
|
||||||
|
printf 'unresolved M428/M429/M430 INI remap: %s\n' "${unresolved_m428_m430_ini_remaps[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ((${#missing_m428_m430_ini_remap_sources[@]} > 0)); then
|
||||||
|
echo "manifest does not cover all LinuxCNC M428/M429/M430 remap sources referenced by INI REMAP entries: $manifest" >&2
|
||||||
|
printf 'missing INI-referenced M428/M429/M430 remap source: %s\n' "${missing_m428_m430_ini_remap_sources[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mapfile -t manifest_config_hal_sources < <(
|
||||||
|
awk -F: '
|
||||||
|
$1 !~ /^($|#)/ && $1 == "config" && $2 ~ /\.hal$/ { print $2 }
|
||||||
|
' "$manifest" | sort -u
|
||||||
|
)
|
||||||
|
unresolved_m428_m430_config_hal_sources=()
|
||||||
|
mapfile -t m428_m430_config_hal_sources < <(
|
||||||
|
for source in "${manifest_m428_m430_ini_sources[@]}"; do
|
||||||
|
config_dir=$(cd "$(dirname "$linuxcnc_root/$source")" && pwd)
|
||||||
|
sed -n 's/^[[:space:]]*\(HALFILE\|POSTGUI_HALFILE\)[[:space:]]*=[[:space:]]*//Ip' "$linuxcnc_root/$source" |
|
||||||
|
sed 's/[[:space:]]*[#;].*$//; s/^[[:space:]]*//; s/[[:space:]]*$//' |
|
||||||
|
while IFS= read -r hal_source; do
|
||||||
|
[[ -n "$hal_source" && "$hal_source" != LIB:* ]] || continue
|
||||||
|
if [[ "$hal_source" = /* ]]; then
|
||||||
|
candidate=$hal_source
|
||||||
|
else
|
||||||
|
candidate="$config_dir/$hal_source"
|
||||||
|
fi
|
||||||
|
if [[ -f "$candidate" ]]; then
|
||||||
|
candidate_abs=$(cd "$(dirname "$candidate")" && pwd)/$(basename "$candidate")
|
||||||
|
printf '%s\n' "${candidate_abs#"$linuxcnc_root/"}"
|
||||||
|
else
|
||||||
|
printf '%s HALFILE=%s\n' "$source" "$hal_source" >&2
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done | sort -u
|
||||||
|
)
|
||||||
|
mapfile -t unresolved_m428_m430_config_hal_sources < <(
|
||||||
|
for source in "${manifest_m428_m430_ini_sources[@]}"; do
|
||||||
|
config_dir=$(cd "$(dirname "$linuxcnc_root/$source")" && pwd)
|
||||||
|
sed -n 's/^[[:space:]]*\(HALFILE\|POSTGUI_HALFILE\)[[:space:]]*=[[:space:]]*//Ip' "$linuxcnc_root/$source" |
|
||||||
|
sed 's/[[:space:]]*[#;].*$//; s/^[[:space:]]*//; s/[[:space:]]*$//' |
|
||||||
|
while IFS= read -r hal_source; do
|
||||||
|
[[ -n "$hal_source" && "$hal_source" != LIB:* ]] || continue
|
||||||
|
if [[ "$hal_source" = /* ]]; then
|
||||||
|
candidate=$hal_source
|
||||||
|
else
|
||||||
|
candidate="$config_dir/$hal_source"
|
||||||
|
fi
|
||||||
|
[[ -f "$candidate" ]] || printf '%s HALFILE=%s\n' "$source" "$hal_source"
|
||||||
|
done
|
||||||
|
done | sort -u
|
||||||
|
)
|
||||||
|
|
||||||
|
if ((${#unresolved_m428_m430_config_hal_sources[@]} > 0)); then
|
||||||
|
echo "LinuxCNC M428/M429/M430 INI HALFILE/POSTGUI_HALFILE sources could not be resolved: $manifest" >&2
|
||||||
|
printf 'unresolved M428/M429/M430 HAL source: %s\n' "${unresolved_m428_m430_config_hal_sources[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
missing_m428_m430_config_hal_sources=()
|
||||||
|
for source in "${m428_m430_config_hal_sources[@]}"; do
|
||||||
|
if ! printf '%s\n' "${manifest_config_hal_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
|
||||||
|
missing_m428_m430_config_hal_sources+=("$source")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if ((${#missing_m428_m430_config_hal_sources[@]} > 0)); then
|
||||||
|
echo "manifest does not cover all LinuxCNC HALFILE/POSTGUI_HALFILE sources referenced by M428/M429/M430 INI files: $manifest" >&2
|
||||||
|
printf 'missing M428/M429/M430 HAL source: %s\n' "${missing_m428_m430_config_hal_sources[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mapfile -t manifest_asset_sources < <(
|
||||||
|
awk -F: '
|
||||||
|
$1 !~ /^($|#)/ && $1 == "asset" { print $2 }
|
||||||
|
' "$manifest" | sort -u
|
||||||
|
)
|
||||||
|
mapfile -t manifest_tooldata_sources < <(
|
||||||
|
awk -F: '
|
||||||
|
$1 !~ /^($|#)/ && $1 == "tooldata" { print $2 }
|
||||||
|
' "$manifest" | sort -u
|
||||||
|
)
|
||||||
|
|
||||||
|
resolve_vismach_loadusr_source() {
|
||||||
|
local command_name=$1
|
||||||
|
case "$command_name" in
|
||||||
|
*/*|*\\*|*.py)
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
local vismach_source="src/hal/user_comps/vismach/$command_name.py"
|
||||||
|
if [[ -f "$linuxcnc_root/$vismach_source" ]]; then
|
||||||
|
printf '%s\n' "$vismach_source"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
m428_m430_vismach_loadusr_sources=()
|
||||||
|
for source in "${manifest_m428_m430_ini_sources[@]}"; do
|
||||||
|
while IFS= read -r command_name; do
|
||||||
|
[[ -n "$command_name" ]] || continue
|
||||||
|
while IFS= read -r vismach_source; do
|
||||||
|
[[ -n "$vismach_source" ]] && m428_m430_vismach_loadusr_sources+=("$vismach_source")
|
||||||
|
done < <(resolve_vismach_loadusr_source "$command_name")
|
||||||
|
done < <(
|
||||||
|
awk '
|
||||||
|
BEGIN { IGNORECASE = 1 }
|
||||||
|
/^[[:space:]]*HALCMD[[:space:]]*=/ && /loadusr[[:space:]]/ {
|
||||||
|
for (i = 1; i <= NF; i++) {
|
||||||
|
if ($i == "-W" && (i + 1) <= NF) {
|
||||||
|
value = $(i + 1)
|
||||||
|
sub(/[[:space:]#;].*/, "", value)
|
||||||
|
if (value != "") print value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
' "$linuxcnc_root/$source"
|
||||||
|
)
|
||||||
|
done
|
||||||
|
for source in "${m428_m430_config_hal_sources[@]}"; do
|
||||||
|
while IFS= read -r command_name; do
|
||||||
|
[[ -n "$command_name" ]] || continue
|
||||||
|
while IFS= read -r vismach_source; do
|
||||||
|
[[ -n "$vismach_source" ]] && m428_m430_vismach_loadusr_sources+=("$vismach_source")
|
||||||
|
done < <(resolve_vismach_loadusr_source "$command_name")
|
||||||
|
done < <(
|
||||||
|
awk '
|
||||||
|
/^[[:space:]]*loadusr[[:space:]]/ {
|
||||||
|
for (i = 1; i <= NF; i++) {
|
||||||
|
if ($i == "-W" && (i + 1) <= NF) {
|
||||||
|
value = $(i + 1)
|
||||||
|
sub(/[[:space:]#;].*/, "", value)
|
||||||
|
if (value != "") print value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
' "$linuxcnc_root/$source"
|
||||||
|
)
|
||||||
|
done
|
||||||
|
mapfile -t m428_m430_vismach_loadusr_sources < <(
|
||||||
|
printf '%s\n' "${m428_m430_vismach_loadusr_sources[@]}" | sed '/^$/d' | sort -u
|
||||||
|
)
|
||||||
|
|
||||||
|
missing_m428_m430_vismach_loadusr_sources=()
|
||||||
|
for source in "${m428_m430_vismach_loadusr_sources[@]}"; do
|
||||||
|
if ! printf '%s\n' "${manifest_asset_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
|
||||||
|
missing_m428_m430_vismach_loadusr_sources+=("$source")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if ((${#missing_m428_m430_vismach_loadusr_sources[@]} > 0)); then
|
||||||
|
echo "manifest does not cover all LinuxCNC vismach user component sources referenced by M428/M429/M430 INI/HAL loadusr commands as asset sources: $manifest" >&2
|
||||||
|
printf 'missing M428/M429/M430 vismach loadusr source: %s\n' "${missing_m428_m430_vismach_loadusr_sources[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ((${#m428_m430_vismach_loadusr_sources[@]} > 0)); then
|
||||||
|
vismach_submakefile_source=src/hal/user_comps/vismach/Submakefile
|
||||||
|
if [[ -f "$linuxcnc_root/$vismach_submakefile_source" ]] &&
|
||||||
|
! printf '%s\n' "${manifest_metadata_sources[@]}" | grep -Fx -- "$vismach_submakefile_source" >/dev/null; then
|
||||||
|
echo "manifest does not cover LinuxCNC vismach user component build metadata as metadata sources: $manifest" >&2
|
||||||
|
printf 'missing vismach user component metadata source: %s\n' "$vismach_submakefile_source" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
m428_m430_vismach_man_sources=()
|
||||||
|
for source in "${m428_m430_vismach_loadusr_sources[@]}"; do
|
||||||
|
command_name=$(basename "$source" .py)
|
||||||
|
man_source="docs/src/man/man1/$command_name.1.adoc"
|
||||||
|
if [[ -f "$linuxcnc_root/$man_source" ]]; then
|
||||||
|
m428_m430_vismach_man_sources+=("$man_source")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
mapfile -t m428_m430_vismach_man_sources < <(
|
||||||
|
printf '%s\n' "${m428_m430_vismach_man_sources[@]}" | sed '/^$/d' | sort -u
|
||||||
|
)
|
||||||
|
missing_m428_m430_vismach_man_sources=()
|
||||||
|
for source in "${m428_m430_vismach_man_sources[@]}"; do
|
||||||
|
if ! printf '%s\n' "${manifest_metadata_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
|
||||||
|
missing_m428_m430_vismach_man_sources+=("$source")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if ((${#missing_m428_m430_vismach_man_sources[@]} > 0)); then
|
||||||
|
echo "manifest does not cover LinuxCNC vismach user component manpage sources as metadata sources: $manifest" >&2
|
||||||
|
printf 'missing vismach user component manpage source: %s\n' "${missing_m428_m430_vismach_man_sources[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
qtvcp_panel_sources=()
|
||||||
|
for source in "${manifest_m428_m430_ini_sources[@]}"; do
|
||||||
|
if grep -Eiq '^[[:space:]]*EMBED_TAB_COMMAND[[:space:]]*=[[:space:]]*qtvcp[[:space:]]+vismach_scara([[:space:]#;]|$)' "$linuxcnc_root/$source"; then
|
||||||
|
qtvcp_panel_sources+=(
|
||||||
|
share/qtvcp/panels/vismach_scara/vismach_scara.ui
|
||||||
|
share/qtvcp/panels/vismach_scara/vismach_scara_handler.py
|
||||||
|
lib/python/qtvcp/lib/qt_vismach/README.txt
|
||||||
|
lib/python/qtvcp/lib/qt_vismach/__init__.py
|
||||||
|
lib/python/qtvcp/lib/qt_vismach/primitives.py
|
||||||
|
lib/python/qtvcp/lib/qt_vismach/qt_vismach.py
|
||||||
|
lib/python/qtvcp/lib/qt_vismach/scara.py
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
mapfile -t qtvcp_panel_sources < <(
|
||||||
|
printf '%s\n' "${qtvcp_panel_sources[@]}" | sed '/^$/d' | sort -u
|
||||||
|
)
|
||||||
|
missing_qtvcp_panel_sources=()
|
||||||
|
for source in "${qtvcp_panel_sources[@]}"; do
|
||||||
|
if [[ ! -f "$linuxcnc_root/$source" ]]; then
|
||||||
|
missing_qtvcp_panel_sources+=("$source")
|
||||||
|
elif ! printf '%s\n' "${manifest_asset_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
|
||||||
|
missing_qtvcp_panel_sources+=("$source")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if ((${#missing_qtvcp_panel_sources[@]} > 0)); then
|
||||||
|
echo "manifest does not cover all LinuxCNC QtVCP vismach_scara sources referenced by M428/M429/M430 INI EMBED_TAB_COMMAND entries as asset sources: $manifest" >&2
|
||||||
|
printf 'missing M428/M429/M430 QtVCP panel source: %s\n' "${missing_qtvcp_panel_sources[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
m428_m430_config_asset_sources=()
|
||||||
|
unresolved_m428_m430_config_asset_sources=()
|
||||||
|
for source in "${manifest_m428_m430_ini_sources[@]}"; do
|
||||||
|
config_dir=$(cd "$(dirname "$linuxcnc_root/$source")" && pwd)
|
||||||
|
while IFS= read -r config_asset; do
|
||||||
|
key=${config_asset%%=*}
|
||||||
|
asset_source=${config_asset#*=}
|
||||||
|
[[ -n "$asset_source" ]] || continue
|
||||||
|
if [[ "$asset_source" = /* ]]; then
|
||||||
|
candidate=$asset_source
|
||||||
|
else
|
||||||
|
candidate="$config_dir/$asset_source"
|
||||||
|
fi
|
||||||
|
if [[ -f "$candidate" ]]; then
|
||||||
|
candidate_abs=$(cd "$(dirname "$candidate")" && pwd)/$(basename "$candidate")
|
||||||
|
case "$candidate_abs" in
|
||||||
|
"$linuxcnc_root"/*)
|
||||||
|
m428_m430_config_asset_sources+=("${candidate_abs#"$linuxcnc_root/"}")
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
unresolved_m428_m430_config_asset_sources+=("$source $key=$asset_source")
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
unresolved_m428_m430_config_asset_sources+=("$source $key=$asset_source")
|
||||||
|
fi
|
||||||
|
done < <(
|
||||||
|
awk -F= '
|
||||||
|
BEGIN { IGNORECASE = 1 }
|
||||||
|
/^[[:space:]]*(PYVCP|OPEN_FILE)[[:space:]]*=/ {
|
||||||
|
key = $1
|
||||||
|
value = $0
|
||||||
|
sub(/^[^=]*=/, "", value)
|
||||||
|
sub(/[[:space:]]*[#;].*$/, "", value)
|
||||||
|
gsub(/^[[:space:]]+|[[:space:]]+$/, "", key)
|
||||||
|
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
|
||||||
|
gsub(/^"|"$/, "", value)
|
||||||
|
if (value != "") {
|
||||||
|
print toupper(key) "=" value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
' "$linuxcnc_root/$source"
|
||||||
|
)
|
||||||
|
done
|
||||||
|
mapfile -t m428_m430_config_asset_sources < <(
|
||||||
|
printf '%s\n' "${m428_m430_config_asset_sources[@]}" | sed '/^$/d' | sort -u
|
||||||
|
)
|
||||||
|
mapfile -t unresolved_m428_m430_config_asset_sources < <(
|
||||||
|
printf '%s\n' "${unresolved_m428_m430_config_asset_sources[@]}" | sed '/^$/d' | sort -u
|
||||||
|
)
|
||||||
|
|
||||||
|
if ((${#unresolved_m428_m430_config_asset_sources[@]} > 0)); then
|
||||||
|
echo "LinuxCNC M428/M429/M430 INI PYVCP/OPEN_FILE assets could not be resolved: $manifest" >&2
|
||||||
|
printf 'unresolved M428/M429/M430 config asset: %s\n' "${unresolved_m428_m430_config_asset_sources[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
missing_m428_m430_config_asset_sources=()
|
||||||
|
for source in "${m428_m430_config_asset_sources[@]}"; do
|
||||||
|
if ! printf '%s\n' "${manifest_asset_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
|
||||||
|
missing_m428_m430_config_asset_sources+=("$source")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if ((${#missing_m428_m430_config_asset_sources[@]} > 0)); then
|
||||||
|
echo "manifest does not cover all LinuxCNC PYVCP/OPEN_FILE assets referenced by M428/M429/M430 INI files as asset sources: $manifest" >&2
|
||||||
|
printf 'missing M428/M429/M430 config asset source: %s\n' "${missing_m428_m430_config_asset_sources[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
m428_m430_config_readme_sources=()
|
||||||
|
for source in "${manifest_m428_m430_ini_sources[@]}"; do
|
||||||
|
config_dir=$(cd "$(dirname "$linuxcnc_root/$source")" && pwd)
|
||||||
|
readme_source="$config_dir/README"
|
||||||
|
if [[ -f "$readme_source" ]]; then
|
||||||
|
readme_abs=$(cd "$(dirname "$readme_source")" && pwd)/$(basename "$readme_source")
|
||||||
|
m428_m430_config_readme_sources+=("${readme_abs#"$linuxcnc_root/"}")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
mapfile -t m428_m430_config_readme_sources < <(
|
||||||
|
printf '%s\n' "${m428_m430_config_readme_sources[@]}" | sed '/^$/d' | sort -u
|
||||||
|
)
|
||||||
|
|
||||||
|
missing_m428_m430_config_readme_sources=()
|
||||||
|
for source in "${m428_m430_config_readme_sources[@]}"; do
|
||||||
|
if ! printf '%s\n' "${manifest_asset_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
|
||||||
|
missing_m428_m430_config_readme_sources+=("$source")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if ((${#missing_m428_m430_config_readme_sources[@]} > 0)); then
|
||||||
|
echo "manifest does not cover LinuxCNC README sources adjacent to M428/M429/M430 INI files as asset sources: $manifest" >&2
|
||||||
|
printf 'missing M428/M429/M430 README source: %s\n' "${missing_m428_m430_config_readme_sources[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
m428_m430_config_tooldata_sources=()
|
||||||
|
unresolved_m428_m430_config_tooldata_sources=()
|
||||||
|
for source in "${manifest_m428_m430_ini_sources[@]}"; do
|
||||||
|
config_dir=$(cd "$(dirname "$linuxcnc_root/$source")" && pwd)
|
||||||
|
while IFS= read -r tool_table; do
|
||||||
|
[[ -n "$tool_table" ]] || continue
|
||||||
|
if [[ "$tool_table" = /* ]]; then
|
||||||
|
candidate=$tool_table
|
||||||
|
else
|
||||||
|
candidate="$config_dir/$tool_table"
|
||||||
|
fi
|
||||||
|
if [[ -f "$candidate" ]]; then
|
||||||
|
candidate_abs=$(cd "$(dirname "$candidate")" && pwd)/$(basename "$candidate")
|
||||||
|
case "$candidate_abs" in
|
||||||
|
"$linuxcnc_root"/*)
|
||||||
|
m428_m430_config_tooldata_sources+=("${candidate_abs#"$linuxcnc_root/"}")
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
unresolved_m428_m430_config_tooldata_sources+=("$source TOOL_TABLE=$tool_table")
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
unresolved_m428_m430_config_tooldata_sources+=("$source TOOL_TABLE=$tool_table")
|
||||||
|
fi
|
||||||
|
done < <(
|
||||||
|
awk -F= '
|
||||||
|
BEGIN { IGNORECASE = 1 }
|
||||||
|
/^[[:space:]]*TOOL_TABLE[[:space:]]*=/ {
|
||||||
|
value = $0
|
||||||
|
sub(/^[^=]*=/, "", value)
|
||||||
|
sub(/[[:space:]]*[#;].*$/, "", value)
|
||||||
|
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
|
||||||
|
gsub(/^"|"$/, "", value)
|
||||||
|
if (value != "") print value
|
||||||
|
}
|
||||||
|
' "$linuxcnc_root/$source"
|
||||||
|
)
|
||||||
|
done
|
||||||
|
mapfile -t m428_m430_config_tooldata_sources < <(
|
||||||
|
printf '%s\n' "${m428_m430_config_tooldata_sources[@]}" | sed '/^$/d' | sort -u
|
||||||
|
)
|
||||||
|
mapfile -t unresolved_m428_m430_config_tooldata_sources < <(
|
||||||
|
printf '%s\n' "${unresolved_m428_m430_config_tooldata_sources[@]}" | sed '/^$/d' | sort -u
|
||||||
|
)
|
||||||
|
|
||||||
|
missing_m428_m430_config_tooldata_sources=()
|
||||||
|
for source in "${m428_m430_config_tooldata_sources[@]}"; do
|
||||||
|
if ! printf '%s\n' "${manifest_tooldata_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
|
||||||
|
missing_m428_m430_config_tooldata_sources+=("$source")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if ((${#missing_m428_m430_config_tooldata_sources[@]} > 0)); then
|
||||||
|
echo "manifest does not cover all resolvable LinuxCNC TOOL_TABLE files referenced by M428/M429/M430 INI files as tooldata sources: $manifest" >&2
|
||||||
|
printf 'missing M428/M429/M430 tool table source: %s\n' "${missing_m428_m430_config_tooldata_sources[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mapfile -t manifest_twp_ini_sources < <(
|
||||||
|
awk -F: '
|
||||||
|
$1 !~ /^($|#)/ && $1 == "config" && $2 ~ /table-rotary_spindle-rotary-nutating\/.*_twp\/.*\.ini$/ { print $2 }
|
||||||
|
' "$manifest" | sort -u
|
||||||
|
)
|
||||||
|
twp_config_asset_sources=()
|
||||||
|
unresolved_twp_config_asset_sources=()
|
||||||
|
|
||||||
|
resolve_twp_config_asset() {
|
||||||
|
local config_dir=$1
|
||||||
|
local source=$2
|
||||||
|
local key=$3
|
||||||
|
local asset_source=$4
|
||||||
|
local candidate
|
||||||
|
if [[ "$asset_source" = /* ]]; then
|
||||||
|
candidate=$asset_source
|
||||||
|
else
|
||||||
|
candidate="$config_dir/$asset_source"
|
||||||
|
fi
|
||||||
|
if [[ -f "$candidate" ]]; then
|
||||||
|
local candidate_abs
|
||||||
|
candidate_abs=$(cd "$(dirname "$candidate")" && pwd)/$(basename "$candidate")
|
||||||
|
case "$candidate_abs" in
|
||||||
|
"$linuxcnc_root"/*)
|
||||||
|
twp_config_asset_sources+=("${candidate_abs#"$linuxcnc_root/"}")
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
unresolved_twp_config_asset_sources+=("$source $key=$asset_source")
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
unresolved_twp_config_asset_sources+=("$source $key=$asset_source")
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_twp_subroutine_asset() {
|
||||||
|
local config_dir=$1
|
||||||
|
local source=$2
|
||||||
|
local key=$3
|
||||||
|
local ngc_name=$4
|
||||||
|
shift 4
|
||||||
|
local subroutine_paths=("$@")
|
||||||
|
[[ "$ngc_name" == *.ngc ]] || ngc_name="$ngc_name.ngc"
|
||||||
|
local resolved_source=
|
||||||
|
local subroutine_path path_part candidate_dir candidate resolved_abs
|
||||||
|
for subroutine_path in "${subroutine_paths[@]}"; do
|
||||||
|
[[ -n "$subroutine_path" ]] || continue
|
||||||
|
IFS=: read -ra path_parts <<<"$subroutine_path"
|
||||||
|
for path_part in "${path_parts[@]}"; do
|
||||||
|
[[ -n "$path_part" ]] || continue
|
||||||
|
if [[ "$path_part" = /* ]]; then
|
||||||
|
candidate_dir=$path_part
|
||||||
|
else
|
||||||
|
candidate_dir="$config_dir/$path_part"
|
||||||
|
fi
|
||||||
|
candidate="$candidate_dir/$ngc_name"
|
||||||
|
if [[ -f "$candidate" ]]; then
|
||||||
|
resolved_abs=$(cd "$(dirname "$candidate")" && pwd)/$(basename "$candidate")
|
||||||
|
resolved_source=${resolved_abs#"$linuxcnc_root/"}
|
||||||
|
break 2
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
if [[ -n "$resolved_source" ]]; then
|
||||||
|
twp_config_asset_sources+=("$resolved_source")
|
||||||
|
else
|
||||||
|
unresolved_twp_config_asset_sources+=("$source $key=$ngc_name")
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
for source in "${manifest_twp_ini_sources[@]}"; do
|
||||||
|
config="$linuxcnc_root/$source"
|
||||||
|
config_dir=$(cd "$(dirname "$config")" && pwd)
|
||||||
|
mapfile -t subroutine_paths < <(
|
||||||
|
sed -n 's/^[[:space:]]*SUBROUTINE_PATH[[:space:]]*=[[:space:]]*//Ip' "$config" |
|
||||||
|
sed 's/[[:space:]]*[#;].*$//; s/^[[:space:]]*//; s/[[:space:]]*$//'
|
||||||
|
)
|
||||||
|
if ((${#subroutine_paths[@]} == 0)); then
|
||||||
|
subroutine_paths=(".")
|
||||||
|
fi
|
||||||
|
|
||||||
|
while IFS='|' read -r remap_code ngc_name; do
|
||||||
|
[[ -n "$remap_code" && -n "$ngc_name" ]] || continue
|
||||||
|
case "$remap_code" in
|
||||||
|
M428|m428|M429|m429|M430|m430)
|
||||||
|
continue
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
resolve_twp_subroutine_asset "$config_dir" "$source" "REMAP $remap_code ngc" "$ngc_name" "${subroutine_paths[@]}"
|
||||||
|
done < <(
|
||||||
|
awk '
|
||||||
|
BEGIN { IGNORECASE = 1 }
|
||||||
|
/^[[:space:]]*REMAP[[:space:]]*=/ && /[[:space:]]ngc[[:space:]]*=/ {
|
||||||
|
code = $0
|
||||||
|
ngc = $0
|
||||||
|
sub(/.*REMAP[[:space:]]*=[[:space:]]*/, "", code)
|
||||||
|
sub(/[[:space:]].*/, "", code)
|
||||||
|
sub(/.*[[:space:]]ngc[[:space:]]*=[[:space:]]*/, "", ngc)
|
||||||
|
sub(/[[:space:]#;].*/, "", ngc)
|
||||||
|
print code "|" ngc
|
||||||
|
}
|
||||||
|
' "$config"
|
||||||
|
)
|
||||||
|
|
||||||
|
while IFS= read -r abort_name; do
|
||||||
|
[[ -n "$abort_name" ]] || continue
|
||||||
|
resolve_twp_subroutine_asset "$config_dir" "$source" "ON_ABORT_COMMAND" "$abort_name" "${subroutine_paths[@]}"
|
||||||
|
done < <(
|
||||||
|
sed -n 's/^[[:space:]]*ON_ABORT_COMMAND[[:space:]]*=[[:space:]]*o[[:space:]]*<\([^>]*\)>.*/\1/Ip' "$config"
|
||||||
|
)
|
||||||
|
|
||||||
|
while IFS= read -r twp_asset; do
|
||||||
|
[[ -n "$twp_asset" ]] || continue
|
||||||
|
resolve_twp_config_asset "$config_dir" "$source" "TWP asset" "$twp_asset"
|
||||||
|
done < <(
|
||||||
|
awk -F= '
|
||||||
|
BEGIN { IGNORECASE = 1 }
|
||||||
|
/^[[:space:]]*TOPLEVEL[[:space:]]*=/ {
|
||||||
|
value = $0
|
||||||
|
sub(/^[^=]*=/, "", value)
|
||||||
|
sub(/[[:space:]]*[#;].*$/, "", value)
|
||||||
|
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
|
||||||
|
if (value != "") print value
|
||||||
|
}
|
||||||
|
/^[[:space:]]*EMBED_TAB_COMMAND[[:space:]]*=/ && /pyvcp[[:space:]]/ {
|
||||||
|
for (i = 1; i <= NF; i++) {
|
||||||
|
if ($i == "pyvcp" && (i + 1) <= NF) {
|
||||||
|
value = $(i + 1)
|
||||||
|
sub(/[[:space:]#;].*$/, "", value)
|
||||||
|
if (value != "") print value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/^[[:space:]]*TOOL_TABLE[[:space:]]*=/ {
|
||||||
|
next
|
||||||
|
}
|
||||||
|
/^[[:space:]]*HALCMD[[:space:]]*=/ && /loadusr/ {
|
||||||
|
for (i = 1; i <= NF; i++) {
|
||||||
|
if ($i ~ /^\.\.\/.*\.py$/) print $i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
' "$config"
|
||||||
|
)
|
||||||
|
|
||||||
|
mapfile -t twp_loaded_python_sources < <(
|
||||||
|
awk '
|
||||||
|
BEGIN { IGNORECASE = 1 }
|
||||||
|
/^[[:space:]]*TOPLEVEL[[:space:]]*=/ {
|
||||||
|
value = $0
|
||||||
|
sub(/^[^=]*=/, "", value)
|
||||||
|
sub(/[[:space:]]*[#;].*$/, "", value)
|
||||||
|
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
|
||||||
|
if (value != "") print value
|
||||||
|
}
|
||||||
|
/^[[:space:]]*HALCMD[[:space:]]*=/ && /loadusr/ {
|
||||||
|
for (i = 1; i <= NF; i++) {
|
||||||
|
if ($i ~ /^\.\.\/.*\.py$/) print $i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
' "$config"
|
||||||
|
)
|
||||||
|
for twp_python_source in "${twp_loaded_python_sources[@]}"; do
|
||||||
|
python_candidate="$config_dir/$twp_python_source"
|
||||||
|
if [[ ! -f "$python_candidate" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
python_abs=$(cd "$(dirname "$python_candidate")" && pwd)/$(basename "$python_candidate")
|
||||||
|
python_dir=$(dirname "$python_abs")
|
||||||
|
if grep -Eq '^[[:space:]]*import[[:space:]]+remap([[:space:]]|$)' "$python_abs"; then
|
||||||
|
resolve_twp_config_asset "$python_dir" "$source" "python import" "remap.py"
|
||||||
|
fi
|
||||||
|
if grep -Eq '^[[:space:]]*from[[:space:]]+util[[:space:]]+import([[:space:]]|$)' "$python_abs"; then
|
||||||
|
resolve_twp_config_asset "$python_dir" "$source" "python import" "util.py"
|
||||||
|
fi
|
||||||
|
if grep -Eq '^[[:space:]]*from[[:space:]]+twp_vismach[[:space:]]+import([[:space:]]|$)' "$python_abs"; then
|
||||||
|
resolve_twp_config_asset "$python_dir" "$source" "python import" "twp_vismach.py"
|
||||||
|
fi
|
||||||
|
if grep -F 'work_piece_1.stl' "$python_abs" >/dev/null; then
|
||||||
|
resolve_twp_config_asset "$config_dir" "$source" "vismach STL" "work_piece_1.stl"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
mapfile -t twp_config_asset_sources < <(
|
||||||
|
printf '%s\n' "${twp_config_asset_sources[@]}" | sed '/^$/d' | sort -u
|
||||||
|
)
|
||||||
|
mapfile -t unresolved_twp_config_asset_sources < <(
|
||||||
|
printf '%s\n' "${unresolved_twp_config_asset_sources[@]}" | sed '/^$/d' | sort -u
|
||||||
|
)
|
||||||
|
|
||||||
|
if ((${#unresolved_twp_config_asset_sources[@]} > 0)); then
|
||||||
|
echo "LinuxCNC TWP INI source assets could not be resolved: $manifest" >&2
|
||||||
|
printf 'unresolved TWP config asset: %s\n' "${unresolved_twp_config_asset_sources[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
missing_twp_config_asset_sources=()
|
||||||
|
for source in "${twp_config_asset_sources[@]}"; do
|
||||||
|
if ! printf '%s\n' "${manifest_asset_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
|
||||||
|
missing_twp_config_asset_sources+=("$source")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if ((${#missing_twp_config_asset_sources[@]} > 0)); then
|
||||||
|
echo "manifest does not cover all LinuxCNC TWP INI source assets as asset sources: $manifest" >&2
|
||||||
|
printf 'missing TWP config asset source: %s\n' "${missing_twp_config_asset_sources[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -544,7 +544,8 @@ if(EMSCRIPTEN)
|
|||||||
"-sEXPORT_NAME=createCncSimModule"
|
"-sEXPORT_NAME=createCncSimModule"
|
||||||
"-sALLOW_MEMORY_GROWTH=1"
|
"-sALLOW_MEMORY_GROWTH=1"
|
||||||
"-sALLOW_TABLE_GROWTH=1"
|
"-sALLOW_TABLE_GROWTH=1"
|
||||||
|
"-sFORCE_FILESYSTEM=1"
|
||||||
"-sEXPORTED_FUNCTIONS=['_malloc','_free','_cnc_sim_create','_cnc_sim_destroy','_cnc_sim_reset','_cnc_sim_set_dialect','_cnc_sim_set_event_callback','_cnc_sim_load_config_json','_cnc_sim_parse_program','_cnc_sim_last_error']"
|
"-sEXPORTED_FUNCTIONS=['_malloc','_free','_cnc_sim_create','_cnc_sim_destroy','_cnc_sim_reset','_cnc_sim_set_dialect','_cnc_sim_set_event_callback','_cnc_sim_load_config_json','_cnc_sim_parse_program','_cnc_sim_last_error']"
|
||||||
"-sEXPORTED_RUNTIME_METHODS=['ccall','cwrap','UTF8ToString','stringToUTF8','lengthBytesUTF8','addFunction','removeFunction']"
|
"-sEXPORTED_RUNTIME_METHODS=['FS','ccall','cwrap','UTF8ToString','stringToUTF8','lengthBytesUTF8','addFunction','removeFunction']"
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -103,9 +103,17 @@ bool contains_switchkins_config_alias(const std::string &compact, const char *al
|
|||||||
contains_string_value(compact, "kinematics", alias) ||
|
contains_string_value(compact, "kinematics", alias) ||
|
||||||
contains_string_value(compact, "halfile", alias) ||
|
contains_string_value(compact, "halfile", alias) ||
|
||||||
contains_string_value(compact, "hal_file", alias) ||
|
contains_string_value(compact, "hal_file", alias) ||
|
||||||
|
contains_string_value(compact, "postguihalfile", alias) ||
|
||||||
|
contains_string_value(compact, "postgui_halfile", alias) ||
|
||||||
|
contains_string_value(compact, "postgui_hal_file", alias) ||
|
||||||
contains_string_value(compact, "config", alias) ||
|
contains_string_value(compact, "config", alias) ||
|
||||||
contains_string_value(compact, "configpath", alias) ||
|
contains_string_value(compact, "configpath", alias) ||
|
||||||
contains_string_value(compact, "config_path", alias);
|
contains_string_value(compact, "config_path", alias) ||
|
||||||
|
contains_string_value(compact, "ini", alias) ||
|
||||||
|
contains_string_value(compact, "inifile", alias) ||
|
||||||
|
contains_string_value(compact, "inifilename", alias) ||
|
||||||
|
contains_string_value(compact, "ini_file", alias) ||
|
||||||
|
contains_string_value(compact, "ini_file_name", alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool find_number_value(const std::string &compact, const char *key, double *value) {
|
bool find_number_value(const std::string &compact, const char *key, double *value) {
|
||||||
|
|||||||
@@ -28,6 +28,19 @@ double motion_control_tolerance = 0.0;
|
|||||||
double naivecam_tolerance = 0.0;
|
double naivecam_tolerance = 0.0;
|
||||||
std::string parameter_file_name = "rs274ngc.var";
|
std::string parameter_file_name = "rs274ngc.var";
|
||||||
|
|
||||||
|
void load_nonrandom_tool_to_spindle(int pocket) {
|
||||||
|
// LinuxCNC Task::load_tool() uses pocket 0 as the non-random
|
||||||
|
// toolchanger unload handshake. Do not eagerly mirror nonzero
|
||||||
|
// loads here; the source-linked interpreter observes its own
|
||||||
|
// post-canonical state before external task-side load completion.
|
||||||
|
if (pocket == 0) {
|
||||||
|
CANON_TOOL_TABLE unloaded = tooldata_entry_init();
|
||||||
|
unloaded.toolno = 0;
|
||||||
|
unloaded.pocketno = 0;
|
||||||
|
tooldata_put(unloaded, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CncSimPose make_pose(double x, double y, double z,
|
CncSimPose make_pose(double x, double y, double z,
|
||||||
double a, double b, double c,
|
double a, double b, double c,
|
||||||
double u, double v, double w) {
|
double u, double v, double w) {
|
||||||
@@ -215,6 +228,10 @@ void SELECT_TOOL(int tool) {
|
|||||||
void CHANGE_TOOL() {
|
void CHANGE_TOOL() {
|
||||||
trace_call("CHANGE_TOOL");
|
trace_call("CHANGE_TOOL");
|
||||||
if (active_sink) {
|
if (active_sink) {
|
||||||
|
const int selected_tool = active_sink->selected_tool();
|
||||||
|
if (selected_tool == 0) {
|
||||||
|
load_nonrandom_tool_to_spindle(0);
|
||||||
|
}
|
||||||
active_sink->change_tool(event_line());
|
active_sink->change_tool(event_line());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -524,6 +541,7 @@ void USE_TOOL_LENGTH_OFFSET(const EmcPose &offset) {
|
|||||||
|
|
||||||
void CHANGE_TOOL_NUMBER(int number) {
|
void CHANGE_TOOL_NUMBER(int number) {
|
||||||
if (active_sink) {
|
if (active_sink) {
|
||||||
|
load_nonrandom_tool_to_spindle(number);
|
||||||
active_sink->select_tool(number);
|
active_sink->select_tool(number);
|
||||||
active_sink->change_tool(event_line());
|
active_sink->change_tool(event_line());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// Generated by ./generate-linuxcnc-switchkins-remap-table.sh.
|
// Generated by ./generate-linuxcnc-switchkins-remap-table.sh.
|
||||||
// Source: LinuxCNC INI MACHINE/KINEMATICS/HALFILE/SUBROUTINE_PATH/REMAP entries and
|
// Source: LinuxCNC INI MACHINE/KINEMATICS/HALFILE/POSTGUI_HALFILE/SUBROUTINE_PATH/REMAP entries and
|
||||||
// adjacent remap_subs/{428,429,430}remap.ngc #<kinstype> assignments.
|
// adjacent remap_subs/{428,429,430}remap.ngc #<kinstype> assignments.
|
||||||
{"configs/sim/axis/vismach/5axis/bridgemill/5axis.ini", 0, 1, 2},
|
{"configs/sim/axis/vismach/5axis/bridgemill/5axis.ini", 0, 1, 2},
|
||||||
{"configs/sim/axis/vismach/5axis/bridgemill", 0, 1, 2},
|
{"configs/sim/axis/vismach/5axis/bridgemill", 0, 1, 2},
|
||||||
@@ -11,6 +11,8 @@
|
|||||||
{"5axiskins", 0, 1, 2},
|
{"5axiskins", 0, 1, 2},
|
||||||
{"5axisgui.hal", 0, 1, 2},
|
{"5axisgui.hal", 0, 1, 2},
|
||||||
{"5axisgui", 0, 1, 2},
|
{"5axisgui", 0, 1, 2},
|
||||||
|
{"5axis_postgui.hal", 0, 1, 2},
|
||||||
|
{"5axis_postgui", 0, 1, 2},
|
||||||
{"configs/sim/axis/vismach/5axis/bridgemill/remap_subs", 0, 1, 2},
|
{"configs/sim/axis/vismach/5axis/bridgemill/remap_subs", 0, 1, 2},
|
||||||
{"configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini", 1, 0, -1},
|
{"configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini", 1, 0, -1},
|
||||||
{"configs/sim/axis/vismach/5axis/table-dual-rotary", 1, 0, -1},
|
{"configs/sim/axis/vismach/5axis/table-dual-rotary", 1, 0, -1},
|
||||||
@@ -21,6 +23,8 @@
|
|||||||
{"xyzab_tdr_kins", 1, 0, -1},
|
{"xyzab_tdr_kins", 1, 0, -1},
|
||||||
{"xyzab_tdr", 1, 0, -1},
|
{"xyzab_tdr", 1, 0, -1},
|
||||||
{"xyzab-tdr-kins", 1, 0, -1},
|
{"xyzab-tdr-kins", 1, 0, -1},
|
||||||
|
{"xyzab-tdr-postgui.hal", 1, 0, -1},
|
||||||
|
{"xyzab-tdr-postgui", 1, 0, -1},
|
||||||
{"configs/sim/axis/vismach/5axis/table-dual-rotary/remap_subs", 1, 0, -1},
|
{"configs/sim/axis/vismach/5axis/table-dual-rotary/remap_subs", 1, 0, -1},
|
||||||
{"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini", 1, 0, 2},
|
{"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini", 1, 0, 2},
|
||||||
{"configs/sim/axis/vismach/5axis/table-rotary-tilting", 1, 0, 2},
|
{"configs/sim/axis/vismach/5axis/table-rotary-tilting", 1, 0, 2},
|
||||||
@@ -30,6 +34,8 @@
|
|||||||
{"sim-xyzac-trt-kins", 1, 0, 2},
|
{"sim-xyzac-trt-kins", 1, 0, 2},
|
||||||
{"xyzac-trt-kinssparm=identityfirst", 1, 0, 2},
|
{"xyzac-trt-kinssparm=identityfirst", 1, 0, 2},
|
||||||
{"xyzac-trt-kins", 1, 0, 2},
|
{"xyzac-trt-kins", 1, 0, 2},
|
||||||
|
{"switchkins_postgui.hal", 1, 0, 2},
|
||||||
|
{"switchkins_postgui", 1, 0, 2},
|
||||||
{"configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs", 1, 0, 2},
|
{"configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs", 1, 0, 2},
|
||||||
{"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini", 1, 0, 2},
|
{"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini", 1, 0, 2},
|
||||||
{"xyzbc-trt", 1, 0, 2},
|
{"xyzbc-trt", 1, 0, 2},
|
||||||
@@ -43,6 +49,8 @@
|
|||||||
{"xyzacb-trsrn", 0, 1, 2},
|
{"xyzacb-trsrn", 0, 1, 2},
|
||||||
{"xyzacb-trsrn(switchkins)", 0, 1, 2},
|
{"xyzacb-trsrn(switchkins)", 0, 1, 2},
|
||||||
{"xyzacb_trsrn", 0, 1, 2},
|
{"xyzacb_trsrn", 0, 1, 2},
|
||||||
|
{"xyzacb-trsrn_postgui.hal", 0, 1, 2},
|
||||||
|
{"xyzacb-trsrn_postgui", 0, 1, 2},
|
||||||
{"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs", 0, 1, 2},
|
{"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs", 0, 1, 2},
|
||||||
{"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating", 0, 1, 2},
|
{"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating", 0, 1, 2},
|
||||||
{"table-rotary_spindle-rotary-nutating", 0, 1, 2},
|
{"table-rotary_spindle-rotary-nutating", 0, 1, 2},
|
||||||
@@ -52,6 +60,8 @@
|
|||||||
{"xyzbca-trsrn", 0, 1, 2},
|
{"xyzbca-trsrn", 0, 1, 2},
|
||||||
{"xyzbca-trsrn(switchkins)", 0, 1, 2},
|
{"xyzbca-trsrn(switchkins)", 0, 1, 2},
|
||||||
{"xyzbca_trsrn", 0, 1, 2},
|
{"xyzbca_trsrn", 0, 1, 2},
|
||||||
|
{"xyzbca-trsrn_postgui.hal", 0, 1, 2},
|
||||||
|
{"xyzbca-trsrn_postgui", 0, 1, 2},
|
||||||
{"configs/sim/axis/vismach/hexapod-sim/hexapod.ini", 0, 1, 2},
|
{"configs/sim/axis/vismach/hexapod-sim/hexapod.ini", 0, 1, 2},
|
||||||
{"configs/sim/axis/vismach/hexapod-sim", 0, 1, 2},
|
{"configs/sim/axis/vismach/hexapod-sim", 0, 1, 2},
|
||||||
{"hexapod-sim", 0, 1, 2},
|
{"hexapod-sim", 0, 1, 2},
|
||||||
@@ -60,6 +70,8 @@
|
|||||||
{"genhexkins", 0, 1, 2},
|
{"genhexkins", 0, 1, 2},
|
||||||
{"kinematics.hal", 0, 1, 2},
|
{"kinematics.hal", 0, 1, 2},
|
||||||
{"kinematics", 0, 1, 2},
|
{"kinematics", 0, 1, 2},
|
||||||
|
{"hexapod_postgui.hal", 0, 1, 2},
|
||||||
|
{"hexapod_postgui", 0, 1, 2},
|
||||||
{"configs/sim/axis/vismach/hexapod-sim/remap_subs", 0, 1, 2},
|
{"configs/sim/axis/vismach/hexapod-sim/remap_subs", 0, 1, 2},
|
||||||
{"configs/sim/axis/vismach/melfa-sim/melfa.ini", 0, 1, 2},
|
{"configs/sim/axis/vismach/melfa-sim/melfa.ini", 0, 1, 2},
|
||||||
{"configs/sim/axis/vismach/melfa-sim", 0, 1, 2},
|
{"configs/sim/axis/vismach/melfa-sim", 0, 1, 2},
|
||||||
@@ -69,12 +81,16 @@
|
|||||||
{"genserkins", 0, 1, 2},
|
{"genserkins", 0, 1, 2},
|
||||||
{"melfa_dh.hal", 0, 1, 2},
|
{"melfa_dh.hal", 0, 1, 2},
|
||||||
{"melfa_dh", 0, 1, 2},
|
{"melfa_dh", 0, 1, 2},
|
||||||
|
{"melfa-postgui.hal", 0, 1, 2},
|
||||||
|
{"melfa-postgui", 0, 1, 2},
|
||||||
{"configs/sim/axis/vismach/melfa-sim/remap_subs", 0, 1, 2},
|
{"configs/sim/axis/vismach/melfa-sim/remap_subs", 0, 1, 2},
|
||||||
{"configs/sim/axis/vismach/millturn/millturn.ini", 0, 1, -1},
|
{"configs/sim/axis/vismach/millturn/millturn.ini", 0, 1, -1},
|
||||||
{"configs/sim/axis/vismach/millturn", 0, 1, -1},
|
{"configs/sim/axis/vismach/millturn", 0, 1, -1},
|
||||||
{"millturn", 0, 1, -1},
|
{"millturn", 0, 1, -1},
|
||||||
{"millturn(mm)", 0, 1, -1},
|
{"millturn(mm)", 0, 1, -1},
|
||||||
{"millturn.hal", 0, 1, -1},
|
{"millturn.hal", 0, 1, -1},
|
||||||
|
{"millturn-postgui.hal", 0, 1, -1},
|
||||||
|
{"millturn-postgui", 0, 1, -1},
|
||||||
{"configs/sim/axis/vismach/millturn/remap_subs", 0, 1, -1},
|
{"configs/sim/axis/vismach/millturn/remap_subs", 0, 1, -1},
|
||||||
{"configs/sim/axis/vismach/puma/puma.ini", 0, 1, 2},
|
{"configs/sim/axis/vismach/puma/puma.ini", 0, 1, 2},
|
||||||
{"configs/sim/axis/vismach/puma", 0, 1, 2},
|
{"configs/sim/axis/vismach/puma", 0, 1, 2},
|
||||||
@@ -83,6 +99,8 @@
|
|||||||
{"pumakins", 0, 1, 2},
|
{"pumakins", 0, 1, 2},
|
||||||
{"puma_dh.hal", 0, 1, 2},
|
{"puma_dh.hal", 0, 1, 2},
|
||||||
{"puma_dh", 0, 1, 2},
|
{"puma_dh", 0, 1, 2},
|
||||||
|
{"puma_postgui.hal", 0, 1, 2},
|
||||||
|
{"puma_postgui", 0, 1, 2},
|
||||||
{"configs/sim/axis/vismach/puma/remap_subs", 0, 1, 2},
|
{"configs/sim/axis/vismach/puma/remap_subs", 0, 1, 2},
|
||||||
{"configs/sim/axis/vismach/puma/puma_cube.ini", 0, 1, 2},
|
{"configs/sim/axis/vismach/puma/puma_cube.ini", 0, 1, 2},
|
||||||
{"puma_cube", 0, 1, 2},
|
{"puma_cube", 0, 1, 2},
|
||||||
@@ -93,6 +111,8 @@
|
|||||||
{"puma560(switchkins)(inch)", 0, 1, 2},
|
{"puma560(switchkins)(inch)", 0, 1, 2},
|
||||||
{"puma560_dh.hal", 0, 1, 2},
|
{"puma560_dh.hal", 0, 1, 2},
|
||||||
{"puma560_dh", 0, 1, 2},
|
{"puma560_dh", 0, 1, 2},
|
||||||
|
{"puma560_postgui.hal", 0, 1, 2},
|
||||||
|
{"puma560_postgui", 0, 1, 2},
|
||||||
{"configs/sim/axis/vismach/puma/puma560_uvw.ini", 0, 1, 2},
|
{"configs/sim/axis/vismach/puma/puma560_uvw.ini", 0, 1, 2},
|
||||||
{"puma560_uvw", 0, 1, 2},
|
{"puma560_uvw", 0, 1, 2},
|
||||||
{"configs/sim/axis/vismach/scara/scara.ini", 0, 1, 2},
|
{"configs/sim/axis/vismach/scara/scara.ini", 0, 1, 2},
|
||||||
@@ -101,6 +121,8 @@
|
|||||||
{"scara(genserkins,switchkins)", 0, 1, 2},
|
{"scara(genserkins,switchkins)", 0, 1, 2},
|
||||||
{"scarakinscoordinates=xyzcab", 0, 1, 2},
|
{"scarakinscoordinates=xyzcab", 0, 1, 2},
|
||||||
{"scarakins", 0, 1, 2},
|
{"scarakins", 0, 1, 2},
|
||||||
|
{"scara_postgui.hal", 0, 1, 2},
|
||||||
|
{"scara_postgui", 0, 1, 2},
|
||||||
{"configs/sim/axis/vismach/scara/remap_subs", 0, 1, 2},
|
{"configs/sim/axis/vismach/scara/remap_subs", 0, 1, 2},
|
||||||
{"configs/sim/qtaxis/non-trivial/scara/scara.ini", 0, 1, 2},
|
{"configs/sim/qtaxis/non-trivial/scara/scara.ini", 0, 1, 2},
|
||||||
{"configs/sim/qtaxis/non-trivial/scara", 0, 1, 2},
|
{"configs/sim/qtaxis/non-trivial/scara", 0, 1, 2},
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "cnc_sim_api.h"
|
#include "cnc_sim_api.h"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -35,6 +36,31 @@ bool near(double lhs, double rhs) {
|
|||||||
return std::fabs(lhs - rhs) < 0.000001;
|
return std::fabs(lhs - rhs) < 0.000001;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ScopedEnv {
|
||||||
|
public:
|
||||||
|
ScopedEnv(const char *name, const char *value) : name_(name) {
|
||||||
|
const char *old = std::getenv(name);
|
||||||
|
if (old) {
|
||||||
|
had_old_ = true;
|
||||||
|
old_ = old;
|
||||||
|
}
|
||||||
|
setenv(name, value, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
~ScopedEnv() {
|
||||||
|
if (had_old_) {
|
||||||
|
setenv(name_, old_.c_str(), 1);
|
||||||
|
} else {
|
||||||
|
unsetenv(name_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const char *name_;
|
||||||
|
bool had_old_ = false;
|
||||||
|
std::string old_;
|
||||||
|
};
|
||||||
|
|
||||||
bool saw_comment_state(const std::vector<CncSimEvent> &events,
|
bool saw_comment_state(const std::vector<CncSimEvent> &events,
|
||||||
int line,
|
int line,
|
||||||
int reserved,
|
int reserved,
|
||||||
@@ -66,6 +92,79 @@ bool saw_any_kinematics_switch(const std::vector<CncSimEvent> &events, int line)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool saw_kinematics_switch(const std::vector<CncSimEvent> &events, int line, int kinstype) {
|
||||||
|
for (const auto &event : events) {
|
||||||
|
if (event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH &&
|
||||||
|
event.line == line &&
|
||||||
|
event.reserved == kinstype) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool expect_switchkins_remap_config(const std::string &json,
|
||||||
|
int m428_type,
|
||||||
|
int m429_type,
|
||||||
|
int m430_type,
|
||||||
|
const char *message) {
|
||||||
|
std::vector<CncSimEvent> events;
|
||||||
|
CncSimHandle *sim = cnc_sim_create();
|
||||||
|
cnc_sim_set_event_callback(sim, collect_event, &events);
|
||||||
|
const char supported_program[] =
|
||||||
|
"M428\n"
|
||||||
|
"M429\n";
|
||||||
|
const char full_program[] =
|
||||||
|
"M428\n"
|
||||||
|
"M429\n"
|
||||||
|
"M430\n";
|
||||||
|
const char *program = m430_type >= 0 ? full_program : supported_program;
|
||||||
|
const size_t program_len = m430_type >= 0 ? sizeof(full_program) - 1 : sizeof(supported_program) - 1;
|
||||||
|
|
||||||
|
bool ok = true;
|
||||||
|
const std::string load_message = std::string(message) + " load";
|
||||||
|
const std::string parse_message = std::string(message) + " parse M428/M429";
|
||||||
|
const std::string m428_message = std::string(message) + " M428 kinstype";
|
||||||
|
const std::string m429_message = std::string(message) + " M429 kinstype";
|
||||||
|
ok &= expect(cnc_sim_load_config_json(sim, json.c_str(), json.size()) == 0, load_message.c_str());
|
||||||
|
ok &= expect(cnc_sim_parse_program(sim, program, program_len) == 0, parse_message.c_str());
|
||||||
|
ok &= expect(saw_kinematics_switch(events, 1, m428_type), m428_message.c_str());
|
||||||
|
ok &= expect(saw_kinematics_switch(events, 2, m429_type), m429_message.c_str());
|
||||||
|
if (m430_type >= 0) {
|
||||||
|
const std::string m430_message = std::string(message) + " M430 kinstype";
|
||||||
|
ok &= expect(saw_kinematics_switch(events, 3, m430_type), m430_message.c_str());
|
||||||
|
} else {
|
||||||
|
const std::string m430_reject_message = std::string(message) + " reject M430";
|
||||||
|
const std::string m430_error_message = std::string(message) + " M430 error";
|
||||||
|
const std::string m430_no_switch_message = std::string(message) + " M430 no switch";
|
||||||
|
const std::string mixed_load_message = std::string(message) + " mixed load";
|
||||||
|
const std::string mixed_reject_message = std::string(message) + " reject mixed M428/M430";
|
||||||
|
const std::string mixed_error_message = std::string(message) + " mixed M430 error";
|
||||||
|
const std::string mixed_no_switch_message = std::string(message) + " mixed no partial switch";
|
||||||
|
events.clear();
|
||||||
|
ok &= expect(cnc_sim_parse_program(sim, "M430\n", 5) != 0, m430_reject_message.c_str());
|
||||||
|
// Source: LinuxCNC interp_read.cc reports NCE_M_CODE_GREATER_THAN_199;
|
||||||
|
// linuxcnc_rs274_backend.cpp preserves the read-stage context.
|
||||||
|
ok &= expect(std::string(cnc_sim_last_error(sim)) == "read failed: M-code greater than 199: M430",
|
||||||
|
m430_error_message.c_str());
|
||||||
|
ok &= expect(!saw_kinematics_switch(events, 1, 2), m430_no_switch_message.c_str());
|
||||||
|
|
||||||
|
std::vector<CncSimEvent> mixed_events;
|
||||||
|
CncSimHandle *mixed_sim = cnc_sim_create();
|
||||||
|
cnc_sim_set_event_callback(mixed_sim, collect_event, &mixed_events);
|
||||||
|
ok &= expect(cnc_sim_load_config_json(mixed_sim, json.c_str(), json.size()) == 0,
|
||||||
|
mixed_load_message.c_str());
|
||||||
|
ok &= expect(cnc_sim_parse_program(mixed_sim, "M428 M430\n", 9) != 0,
|
||||||
|
mixed_reject_message.c_str());
|
||||||
|
ok &= expect(std::string(cnc_sim_last_error(mixed_sim)) == "M-code greater than 199: M430",
|
||||||
|
mixed_error_message.c_str());
|
||||||
|
ok &= expect(!saw_any_kinematics_switch(mixed_events, 1), mixed_no_switch_message.c_str());
|
||||||
|
cnc_sim_destroy(mixed_sim);
|
||||||
|
}
|
||||||
|
cnc_sim_destroy(sim);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
@@ -1577,6 +1676,35 @@ int main() {
|
|||||||
ok &= expect(cnc_sim_load_config_json(sim, config, sizeof(config) - 1) == 0,
|
ok &= expect(cnc_sim_load_config_json(sim, config, sizeof(config) - 1) == 0,
|
||||||
cnc_sim_last_error(sim));
|
cnc_sim_last_error(sim));
|
||||||
|
|
||||||
|
{
|
||||||
|
struct SourceBackedConfigCase {
|
||||||
|
const char *field;
|
||||||
|
const char *value;
|
||||||
|
int m428_type;
|
||||||
|
int m429_type;
|
||||||
|
int m430_type;
|
||||||
|
};
|
||||||
|
const SourceBackedConfigCase source_cases[] = {
|
||||||
|
#include "linuxcnc_switchkins_remap_config_cases.inc"
|
||||||
|
};
|
||||||
|
for (const SourceBackedConfigCase &source_case : source_cases) {
|
||||||
|
const std::string json = std::string("{\"backend\":\"linuxcnc-rs274\",\"") +
|
||||||
|
source_case.field +
|
||||||
|
"\":\"" +
|
||||||
|
source_case.value +
|
||||||
|
"\"}";
|
||||||
|
const std::string message = std::string("expected LinuxCNC source-backed switchkins remap config for ") +
|
||||||
|
source_case.field +
|
||||||
|
"=" +
|
||||||
|
source_case.value;
|
||||||
|
ok &= expect_switchkins_remap_config(json,
|
||||||
|
source_case.m428_type,
|
||||||
|
source_case.m429_type,
|
||||||
|
source_case.m430_type,
|
||||||
|
message.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const char exponent_m428_control_program[] =
|
const char exponent_m428_control_program[] =
|
||||||
"G21 G90\n"
|
"G21 G90\n"
|
||||||
"M4.28e2\n";
|
"M4.28e2\n";
|
||||||
|
|||||||
@@ -1,67 +1,384 @@
|
|||||||
// Generated by ./generate-linuxcnc-switchkins-remap-table.sh --config-cases.
|
// Generated by ./generate-linuxcnc-switchkins-remap-table.sh --config-cases.
|
||||||
// Source: LinuxCNC INI config path, MACHINE, KINEMATICS, and non-LIB HALFILE entries
|
// Source: LinuxCNC INI config path, MACHINE, KINEMATICS, non-LIB HALFILE/POSTGUI_HALFILE,
|
||||||
// for M428/M429/M430 remap #<kinstype> assignments.
|
// and adjacent remap_subs entries for M428/M429/M430 #<kinstype> assignments.
|
||||||
{"config", "configs/sim/axis/vismach/5axis/bridgemill/5axis.ini", 0, 1, 2},
|
{"config", "configs/sim/axis/vismach/5axis/bridgemill/5axis.ini", 0, 1, 2},
|
||||||
|
{"configPath", "configs/sim/axis/vismach/5axis/bridgemill/5axis.ini", 0, 1, 2},
|
||||||
|
{"config_path", "configs/sim/axis/vismach/5axis/bridgemill/5axis.ini", 0, 1, 2},
|
||||||
|
{"ini", "configs/sim/axis/vismach/5axis/bridgemill/5axis.ini", 0, 1, 2},
|
||||||
|
{"iniFile", "configs/sim/axis/vismach/5axis/bridgemill/5axis.ini", 0, 1, 2},
|
||||||
|
{"iniFileName", "configs/sim/axis/vismach/5axis/bridgemill/5axis.ini", 0, 1, 2},
|
||||||
|
{"ini_file", "configs/sim/axis/vismach/5axis/bridgemill/5axis.ini", 0, 1, 2},
|
||||||
|
{"ini_file_name", "configs/sim/axis/vismach/5axis/bridgemill/5axis.ini", 0, 1, 2},
|
||||||
|
{"INI_FILE_NAME", "configs/sim/axis/vismach/5axis/bridgemill/5axis.ini", 0, 1, 2},
|
||||||
{"machine", "Sim-5Axis Bridge Mill (xyzbcw)", 0, 1, 2},
|
{"machine", "Sim-5Axis Bridge Mill (xyzbcw)", 0, 1, 2},
|
||||||
{"kinematics", "5axiskins coordinates=xyzbcwy", 0, 1, 2},
|
{"kinematics", "5axiskins coordinates=xyzbcwy", 0, 1, 2},
|
||||||
|
{"switchkins", "bridgemill", 0, 1, 2},
|
||||||
|
{"switchkins", "5axis", 0, 1, 2},
|
||||||
|
{"switchkins", "Sim-5Axis Bridge Mill (xyzbcw)", 0, 1, 2},
|
||||||
|
{"switchkins", "Sim-5Axis Bridge Mill", 0, 1, 2},
|
||||||
|
{"switchkins", "5axiskins coordinates=xyzbcwy", 0, 1, 2},
|
||||||
|
{"switchkins", "5axiskins", 0, 1, 2},
|
||||||
|
{"halFile", "5axisgui.hal", 0, 1, 2},
|
||||||
|
{"halFile", "5axisgui", 0, 1, 2},
|
||||||
{"halfile", "5axisgui.hal", 0, 1, 2},
|
{"halfile", "5axisgui.hal", 0, 1, 2},
|
||||||
{"halfile", "5axisgui", 0, 1, 2},
|
{"halfile", "5axisgui", 0, 1, 2},
|
||||||
{"hal_file", "5axisgui.hal", 0, 1, 2},
|
{"hal_file", "5axisgui.hal", 0, 1, 2},
|
||||||
{"hal_file", "5axisgui", 0, 1, 2},
|
{"hal_file", "5axisgui", 0, 1, 2},
|
||||||
|
{"HALFILE", "5axisgui.hal", 0, 1, 2},
|
||||||
|
{"HALFILE", "5axisgui", 0, 1, 2},
|
||||||
|
{"postguiHalFile", "5axis_postgui.hal", 0, 1, 2},
|
||||||
|
{"postguiHalFile", "5axis_postgui", 0, 1, 2},
|
||||||
|
{"postgui_halfile", "5axis_postgui.hal", 0, 1, 2},
|
||||||
|
{"postgui_halfile", "5axis_postgui", 0, 1, 2},
|
||||||
|
{"postgui_hal_file", "5axis_postgui.hal", 0, 1, 2},
|
||||||
|
{"postgui_hal_file", "5axis_postgui", 0, 1, 2},
|
||||||
|
{"POSTGUI_HALFILE", "5axis_postgui.hal", 0, 1, 2},
|
||||||
|
{"POSTGUI_HALFILE", "5axis_postgui", 0, 1, 2},
|
||||||
|
{"remap", "configs/sim/axis/vismach/5axis/bridgemill", 0, 1, 2},
|
||||||
|
{"remap", "bridgemill", 0, 1, 2},
|
||||||
{"config", "configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini", 1, 0, -1},
|
{"config", "configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini", 1, 0, -1},
|
||||||
|
{"configPath", "configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini", 1, 0, -1},
|
||||||
|
{"config_path", "configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini", 1, 0, -1},
|
||||||
|
{"ini", "configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini", 1, 0, -1},
|
||||||
|
{"iniFile", "configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini", 1, 0, -1},
|
||||||
|
{"iniFileName", "configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini", 1, 0, -1},
|
||||||
|
{"ini_file", "configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini", 1, 0, -1},
|
||||||
|
{"ini_file_name", "configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini", 1, 0, -1},
|
||||||
|
{"INI_FILE_NAME", "configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini", 1, 0, -1},
|
||||||
{"machine", "sim-xyzab-tdr-kins (switchkins)", 1, 0, -1},
|
{"machine", "sim-xyzab-tdr-kins (switchkins)", 1, 0, -1},
|
||||||
{"kinematics", "xyzab_tdr_kins", 1, 0, -1},
|
{"kinematics", "xyzab_tdr_kins", 1, 0, -1},
|
||||||
|
{"switchkins", "table-dual-rotary", 1, 0, -1},
|
||||||
|
{"switchkins", "xyzab-tdr", 1, 0, -1},
|
||||||
|
{"switchkins", "sim-xyzab-tdr-kins (switchkins)", 1, 0, -1},
|
||||||
|
{"switchkins", "sim-xyzab-tdr-kins", 1, 0, -1},
|
||||||
|
{"switchkins", "xyzab_tdr_kins", 1, 0, -1},
|
||||||
|
{"switchkins", "xyzab_tdr", 1, 0, -1},
|
||||||
|
{"switchkins", "xyzab-tdr-kins", 1, 0, -1},
|
||||||
|
{"postguiHalFile", "xyzab-tdr-postgui.hal", 1, 0, -1},
|
||||||
|
{"postguiHalFile", "xyzab-tdr-postgui", 1, 0, -1},
|
||||||
|
{"postgui_halfile", "xyzab-tdr-postgui.hal", 1, 0, -1},
|
||||||
|
{"postgui_halfile", "xyzab-tdr-postgui", 1, 0, -1},
|
||||||
|
{"postgui_hal_file", "xyzab-tdr-postgui.hal", 1, 0, -1},
|
||||||
|
{"postgui_hal_file", "xyzab-tdr-postgui", 1, 0, -1},
|
||||||
|
{"POSTGUI_HALFILE", "xyzab-tdr-postgui.hal", 1, 0, -1},
|
||||||
|
{"POSTGUI_HALFILE", "xyzab-tdr-postgui", 1, 0, -1},
|
||||||
|
{"remap", "configs/sim/axis/vismach/5axis/table-dual-rotary", 1, 0, -1},
|
||||||
|
{"remap", "table-dual-rotary", 1, 0, -1},
|
||||||
{"config", "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini", 1, 0, 2},
|
{"config", "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini", 1, 0, 2},
|
||||||
|
{"configPath", "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini", 1, 0, 2},
|
||||||
|
{"config_path", "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini", 1, 0, 2},
|
||||||
|
{"ini", "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini", 1, 0, 2},
|
||||||
|
{"iniFile", "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini", 1, 0, 2},
|
||||||
|
{"iniFileName", "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini", 1, 0, 2},
|
||||||
|
{"ini_file", "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini", 1, 0, 2},
|
||||||
|
{"ini_file_name", "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini", 1, 0, 2},
|
||||||
|
{"INI_FILE_NAME", "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini", 1, 0, 2},
|
||||||
{"machine", "sim-xyzac-trt-kins (switchkins)", 1, 0, 2},
|
{"machine", "sim-xyzac-trt-kins (switchkins)", 1, 0, 2},
|
||||||
{"kinematics", "xyzac-trt-kins sparm=identityfirst", 1, 0, 2},
|
{"kinematics", "xyzac-trt-kins sparm=identityfirst", 1, 0, 2},
|
||||||
|
{"switchkins", "table-rotary-tilting", 1, 0, 2},
|
||||||
|
{"switchkins", "xyzac-trt", 1, 0, 2},
|
||||||
|
{"switchkins", "sim-xyzac-trt-kins (switchkins)", 1, 0, 2},
|
||||||
|
{"switchkins", "sim-xyzac-trt-kins", 1, 0, 2},
|
||||||
|
{"switchkins", "xyzac-trt-kins sparm=identityfirst", 1, 0, 2},
|
||||||
|
{"switchkins", "xyzac-trt-kins", 1, 0, 2},
|
||||||
|
{"postguiHalFile", "switchkins_postgui.hal", 1, 0, 2},
|
||||||
|
{"postguiHalFile", "switchkins_postgui", 1, 0, 2},
|
||||||
|
{"postgui_halfile", "switchkins_postgui.hal", 1, 0, 2},
|
||||||
|
{"postgui_halfile", "switchkins_postgui", 1, 0, 2},
|
||||||
|
{"postgui_hal_file", "switchkins_postgui.hal", 1, 0, 2},
|
||||||
|
{"postgui_hal_file", "switchkins_postgui", 1, 0, 2},
|
||||||
|
{"POSTGUI_HALFILE", "switchkins_postgui.hal", 1, 0, 2},
|
||||||
|
{"POSTGUI_HALFILE", "switchkins_postgui", 1, 0, 2},
|
||||||
|
{"remap", "configs/sim/axis/vismach/5axis/table-rotary-tilting", 1, 0, 2},
|
||||||
|
{"remap", "table-rotary-tilting", 1, 0, 2},
|
||||||
{"config", "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini", 1, 0, 2},
|
{"config", "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini", 1, 0, 2},
|
||||||
|
{"configPath", "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini", 1, 0, 2},
|
||||||
|
{"config_path", "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini", 1, 0, 2},
|
||||||
|
{"ini", "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini", 1, 0, 2},
|
||||||
|
{"iniFile", "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini", 1, 0, 2},
|
||||||
|
{"iniFileName", "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini", 1, 0, 2},
|
||||||
|
{"ini_file", "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini", 1, 0, 2},
|
||||||
|
{"ini_file_name", "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini", 1, 0, 2},
|
||||||
|
{"INI_FILE_NAME", "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini", 1, 0, 2},
|
||||||
{"machine", "sim-xyzbc-trt-kins (switchkins)", 1, 0, 2},
|
{"machine", "sim-xyzbc-trt-kins (switchkins)", 1, 0, 2},
|
||||||
{"kinematics", "xyzbc-trt-kins sparm=identityfirst", 1, 0, 2},
|
{"kinematics", "xyzbc-trt-kins sparm=identityfirst", 1, 0, 2},
|
||||||
|
{"switchkins", "xyzbc-trt", 1, 0, 2},
|
||||||
|
{"switchkins", "sim-xyzbc-trt-kins (switchkins)", 1, 0, 2},
|
||||||
|
{"switchkins", "sim-xyzbc-trt-kins", 1, 0, 2},
|
||||||
|
{"switchkins", "xyzbc-trt-kins sparm=identityfirst", 1, 0, 2},
|
||||||
|
{"switchkins", "xyzbc-trt-kins", 1, 0, 2},
|
||||||
{"config", "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini", 0, 1, 2},
|
{"config", "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini", 0, 1, 2},
|
||||||
|
{"configPath", "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini", 0, 1, 2},
|
||||||
|
{"config_path", "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini", 0, 1, 2},
|
||||||
|
{"ini", "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini", 0, 1, 2},
|
||||||
|
{"iniFile", "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini", 0, 1, 2},
|
||||||
|
{"iniFileName", "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini", 0, 1, 2},
|
||||||
|
{"ini_file", "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini", 0, 1, 2},
|
||||||
|
{"ini_file_name", "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini", 0, 1, 2},
|
||||||
|
{"INI_FILE_NAME", "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini", 0, 1, 2},
|
||||||
{"machine", "xyzacb-trsrn (switchkins)", 0, 1, 2},
|
{"machine", "xyzacb-trsrn (switchkins)", 0, 1, 2},
|
||||||
{"kinematics", "xyzacb_trsrn", 0, 1, 2},
|
{"kinematics", "xyzacb_trsrn", 0, 1, 2},
|
||||||
|
{"switchkins", "xyzacb-trsrn_twp", 0, 1, 2},
|
||||||
|
{"switchkins", "xyzacb-trsrn", 0, 1, 2},
|
||||||
|
{"switchkins", "xyzacb-trsrn (switchkins)", 0, 1, 2},
|
||||||
|
{"switchkins", "xyzacb_trsrn", 0, 1, 2},
|
||||||
|
{"postguiHalFile", "xyzacb-trsrn_postgui.hal", 0, 1, 2},
|
||||||
|
{"postguiHalFile", "xyzacb-trsrn_postgui", 0, 1, 2},
|
||||||
|
{"postgui_halfile", "xyzacb-trsrn_postgui.hal", 0, 1, 2},
|
||||||
|
{"postgui_halfile", "xyzacb-trsrn_postgui", 0, 1, 2},
|
||||||
|
{"postgui_hal_file", "xyzacb-trsrn_postgui.hal", 0, 1, 2},
|
||||||
|
{"postgui_hal_file", "xyzacb-trsrn_postgui", 0, 1, 2},
|
||||||
|
{"POSTGUI_HALFILE", "xyzacb-trsrn_postgui.hal", 0, 1, 2},
|
||||||
|
{"POSTGUI_HALFILE", "xyzacb-trsrn_postgui", 0, 1, 2},
|
||||||
|
{"remap", "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating", 0, 1, 2},
|
||||||
|
{"remap", "table-rotary_spindle-rotary-nutating", 0, 1, 2},
|
||||||
{"config", "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini", 0, 1, 2},
|
{"config", "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini", 0, 1, 2},
|
||||||
|
{"configPath", "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini", 0, 1, 2},
|
||||||
|
{"config_path", "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini", 0, 1, 2},
|
||||||
|
{"ini", "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini", 0, 1, 2},
|
||||||
|
{"iniFile", "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini", 0, 1, 2},
|
||||||
|
{"iniFileName", "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini", 0, 1, 2},
|
||||||
|
{"ini_file", "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini", 0, 1, 2},
|
||||||
|
{"ini_file_name", "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini", 0, 1, 2},
|
||||||
|
{"INI_FILE_NAME", "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini", 0, 1, 2},
|
||||||
{"machine", "xyzbca-trsrn (switchkins)", 0, 1, 2},
|
{"machine", "xyzbca-trsrn (switchkins)", 0, 1, 2},
|
||||||
{"kinematics", "xyzbca_trsrn", 0, 1, 2},
|
{"kinematics", "xyzbca_trsrn", 0, 1, 2},
|
||||||
|
{"switchkins", "xyzbca-trsrn_twp", 0, 1, 2},
|
||||||
|
{"switchkins", "xyzbca-trsrn", 0, 1, 2},
|
||||||
|
{"switchkins", "xyzbca-trsrn (switchkins)", 0, 1, 2},
|
||||||
|
{"switchkins", "xyzbca_trsrn", 0, 1, 2},
|
||||||
|
{"postguiHalFile", "xyzbca-trsrn_postgui.hal", 0, 1, 2},
|
||||||
|
{"postguiHalFile", "xyzbca-trsrn_postgui", 0, 1, 2},
|
||||||
|
{"postgui_halfile", "xyzbca-trsrn_postgui.hal", 0, 1, 2},
|
||||||
|
{"postgui_halfile", "xyzbca-trsrn_postgui", 0, 1, 2},
|
||||||
|
{"postgui_hal_file", "xyzbca-trsrn_postgui.hal", 0, 1, 2},
|
||||||
|
{"postgui_hal_file", "xyzbca-trsrn_postgui", 0, 1, 2},
|
||||||
|
{"POSTGUI_HALFILE", "xyzbca-trsrn_postgui.hal", 0, 1, 2},
|
||||||
|
{"POSTGUI_HALFILE", "xyzbca-trsrn_postgui", 0, 1, 2},
|
||||||
{"config", "configs/sim/axis/vismach/hexapod-sim/hexapod.ini", 0, 1, 2},
|
{"config", "configs/sim/axis/vismach/hexapod-sim/hexapod.ini", 0, 1, 2},
|
||||||
|
{"configPath", "configs/sim/axis/vismach/hexapod-sim/hexapod.ini", 0, 1, 2},
|
||||||
|
{"config_path", "configs/sim/axis/vismach/hexapod-sim/hexapod.ini", 0, 1, 2},
|
||||||
|
{"ini", "configs/sim/axis/vismach/hexapod-sim/hexapod.ini", 0, 1, 2},
|
||||||
|
{"iniFile", "configs/sim/axis/vismach/hexapod-sim/hexapod.ini", 0, 1, 2},
|
||||||
|
{"iniFileName", "configs/sim/axis/vismach/hexapod-sim/hexapod.ini", 0, 1, 2},
|
||||||
|
{"ini_file", "configs/sim/axis/vismach/hexapod-sim/hexapod.ini", 0, 1, 2},
|
||||||
|
{"ini_file_name", "configs/sim/axis/vismach/hexapod-sim/hexapod.ini", 0, 1, 2},
|
||||||
|
{"INI_FILE_NAME", "configs/sim/axis/vismach/hexapod-sim/hexapod.ini", 0, 1, 2},
|
||||||
{"machine", "hexapod (switchkins)", 0, 1, 2},
|
{"machine", "hexapod (switchkins)", 0, 1, 2},
|
||||||
{"kinematics", "genhexkins", 0, 1, 2},
|
{"kinematics", "genhexkins", 0, 1, 2},
|
||||||
|
{"switchkins", "hexapod-sim", 0, 1, 2},
|
||||||
|
{"switchkins", "hexapod", 0, 1, 2},
|
||||||
|
{"switchkins", "hexapod (switchkins)", 0, 1, 2},
|
||||||
|
{"switchkins", "genhexkins", 0, 1, 2},
|
||||||
|
{"halFile", "kinematics.hal", 0, 1, 2},
|
||||||
|
{"halFile", "kinematics", 0, 1, 2},
|
||||||
{"halfile", "kinematics.hal", 0, 1, 2},
|
{"halfile", "kinematics.hal", 0, 1, 2},
|
||||||
{"halfile", "kinematics", 0, 1, 2},
|
{"halfile", "kinematics", 0, 1, 2},
|
||||||
{"hal_file", "kinematics.hal", 0, 1, 2},
|
{"hal_file", "kinematics.hal", 0, 1, 2},
|
||||||
{"hal_file", "kinematics", 0, 1, 2},
|
{"hal_file", "kinematics", 0, 1, 2},
|
||||||
|
{"HALFILE", "kinematics.hal", 0, 1, 2},
|
||||||
|
{"HALFILE", "kinematics", 0, 1, 2},
|
||||||
|
{"postguiHalFile", "hexapod_postgui.hal", 0, 1, 2},
|
||||||
|
{"postguiHalFile", "hexapod_postgui", 0, 1, 2},
|
||||||
|
{"postgui_halfile", "hexapod_postgui.hal", 0, 1, 2},
|
||||||
|
{"postgui_halfile", "hexapod_postgui", 0, 1, 2},
|
||||||
|
{"postgui_hal_file", "hexapod_postgui.hal", 0, 1, 2},
|
||||||
|
{"postgui_hal_file", "hexapod_postgui", 0, 1, 2},
|
||||||
|
{"POSTGUI_HALFILE", "hexapod_postgui.hal", 0, 1, 2},
|
||||||
|
{"POSTGUI_HALFILE", "hexapod_postgui", 0, 1, 2},
|
||||||
|
{"remap", "configs/sim/axis/vismach/hexapod-sim", 0, 1, 2},
|
||||||
|
{"remap", "hexapod-sim", 0, 1, 2},
|
||||||
{"config", "configs/sim/axis/vismach/melfa-sim/melfa.ini", 0, 1, 2},
|
{"config", "configs/sim/axis/vismach/melfa-sim/melfa.ini", 0, 1, 2},
|
||||||
|
{"configPath", "configs/sim/axis/vismach/melfa-sim/melfa.ini", 0, 1, 2},
|
||||||
|
{"config_path", "configs/sim/axis/vismach/melfa-sim/melfa.ini", 0, 1, 2},
|
||||||
|
{"ini", "configs/sim/axis/vismach/melfa-sim/melfa.ini", 0, 1, 2},
|
||||||
|
{"iniFile", "configs/sim/axis/vismach/melfa-sim/melfa.ini", 0, 1, 2},
|
||||||
|
{"iniFileName", "configs/sim/axis/vismach/melfa-sim/melfa.ini", 0, 1, 2},
|
||||||
|
{"ini_file", "configs/sim/axis/vismach/melfa-sim/melfa.ini", 0, 1, 2},
|
||||||
|
{"ini_file_name", "configs/sim/axis/vismach/melfa-sim/melfa.ini", 0, 1, 2},
|
||||||
|
{"INI_FILE_NAME", "configs/sim/axis/vismach/melfa-sim/melfa.ini", 0, 1, 2},
|
||||||
{"machine", "melfa (mm)", 0, 1, 2},
|
{"machine", "melfa (mm)", 0, 1, 2},
|
||||||
{"kinematics", "genserkins", 0, 1, 2},
|
{"kinematics", "genserkins", 0, 1, 2},
|
||||||
|
{"switchkins", "melfa-sim", 0, 1, 2},
|
||||||
|
{"switchkins", "melfa", 0, 1, 2},
|
||||||
|
{"switchkins", "melfa (mm)", 0, 1, 2},
|
||||||
|
{"switchkins", "genserkins", 0, 1, 2},
|
||||||
|
{"halFile", "melfa_dh.hal", 0, 1, 2},
|
||||||
|
{"halFile", "melfa_dh", 0, 1, 2},
|
||||||
{"halfile", "melfa_dh.hal", 0, 1, 2},
|
{"halfile", "melfa_dh.hal", 0, 1, 2},
|
||||||
{"halfile", "melfa_dh", 0, 1, 2},
|
{"halfile", "melfa_dh", 0, 1, 2},
|
||||||
{"hal_file", "melfa_dh.hal", 0, 1, 2},
|
{"hal_file", "melfa_dh.hal", 0, 1, 2},
|
||||||
{"hal_file", "melfa_dh", 0, 1, 2},
|
{"hal_file", "melfa_dh", 0, 1, 2},
|
||||||
|
{"HALFILE", "melfa_dh.hal", 0, 1, 2},
|
||||||
|
{"HALFILE", "melfa_dh", 0, 1, 2},
|
||||||
|
{"postguiHalFile", "melfa-postgui.hal", 0, 1, 2},
|
||||||
|
{"postguiHalFile", "melfa-postgui", 0, 1, 2},
|
||||||
|
{"postgui_halfile", "melfa-postgui.hal", 0, 1, 2},
|
||||||
|
{"postgui_halfile", "melfa-postgui", 0, 1, 2},
|
||||||
|
{"postgui_hal_file", "melfa-postgui.hal", 0, 1, 2},
|
||||||
|
{"postgui_hal_file", "melfa-postgui", 0, 1, 2},
|
||||||
|
{"POSTGUI_HALFILE", "melfa-postgui.hal", 0, 1, 2},
|
||||||
|
{"POSTGUI_HALFILE", "melfa-postgui", 0, 1, 2},
|
||||||
|
{"remap", "configs/sim/axis/vismach/melfa-sim", 0, 1, 2},
|
||||||
|
{"remap", "melfa-sim", 0, 1, 2},
|
||||||
{"config", "configs/sim/axis/vismach/millturn/millturn.ini", 0, 1, -1},
|
{"config", "configs/sim/axis/vismach/millturn/millturn.ini", 0, 1, -1},
|
||||||
|
{"configPath", "configs/sim/axis/vismach/millturn/millturn.ini", 0, 1, -1},
|
||||||
|
{"config_path", "configs/sim/axis/vismach/millturn/millturn.ini", 0, 1, -1},
|
||||||
|
{"ini", "configs/sim/axis/vismach/millturn/millturn.ini", 0, 1, -1},
|
||||||
|
{"iniFile", "configs/sim/axis/vismach/millturn/millturn.ini", 0, 1, -1},
|
||||||
|
{"iniFileName", "configs/sim/axis/vismach/millturn/millturn.ini", 0, 1, -1},
|
||||||
|
{"ini_file", "configs/sim/axis/vismach/millturn/millturn.ini", 0, 1, -1},
|
||||||
|
{"ini_file_name", "configs/sim/axis/vismach/millturn/millturn.ini", 0, 1, -1},
|
||||||
|
{"INI_FILE_NAME", "configs/sim/axis/vismach/millturn/millturn.ini", 0, 1, -1},
|
||||||
{"machine", "millturn (mm)", 0, 1, -1},
|
{"machine", "millturn (mm)", 0, 1, -1},
|
||||||
{"kinematics", "millturn", 0, 1, -1},
|
{"kinematics", "millturn", 0, 1, -1},
|
||||||
|
{"switchkins", "millturn", 0, 1, -1},
|
||||||
|
{"switchkins", "millturn (mm)", 0, 1, -1},
|
||||||
|
{"halFile", "millturn.hal", 0, 1, -1},
|
||||||
|
{"halFile", "millturn", 0, 1, -1},
|
||||||
{"halfile", "millturn.hal", 0, 1, -1},
|
{"halfile", "millturn.hal", 0, 1, -1},
|
||||||
{"halfile", "millturn", 0, 1, -1},
|
{"halfile", "millturn", 0, 1, -1},
|
||||||
{"hal_file", "millturn.hal", 0, 1, -1},
|
{"hal_file", "millturn.hal", 0, 1, -1},
|
||||||
{"hal_file", "millturn", 0, 1, -1},
|
{"hal_file", "millturn", 0, 1, -1},
|
||||||
|
{"HALFILE", "millturn.hal", 0, 1, -1},
|
||||||
|
{"HALFILE", "millturn", 0, 1, -1},
|
||||||
|
{"postguiHalFile", "millturn-postgui.hal", 0, 1, -1},
|
||||||
|
{"postguiHalFile", "millturn-postgui", 0, 1, -1},
|
||||||
|
{"postgui_halfile", "millturn-postgui.hal", 0, 1, -1},
|
||||||
|
{"postgui_halfile", "millturn-postgui", 0, 1, -1},
|
||||||
|
{"postgui_hal_file", "millturn-postgui.hal", 0, 1, -1},
|
||||||
|
{"postgui_hal_file", "millturn-postgui", 0, 1, -1},
|
||||||
|
{"POSTGUI_HALFILE", "millturn-postgui.hal", 0, 1, -1},
|
||||||
|
{"POSTGUI_HALFILE", "millturn-postgui", 0, 1, -1},
|
||||||
|
{"remap", "configs/sim/axis/vismach/millturn", 0, 1, -1},
|
||||||
|
{"remap", "millturn", 0, 1, -1},
|
||||||
{"config", "configs/sim/axis/vismach/puma/puma.ini", 0, 1, 2},
|
{"config", "configs/sim/axis/vismach/puma/puma.ini", 0, 1, 2},
|
||||||
|
{"configPath", "configs/sim/axis/vismach/puma/puma.ini", 0, 1, 2},
|
||||||
|
{"config_path", "configs/sim/axis/vismach/puma/puma.ini", 0, 1, 2},
|
||||||
|
{"ini", "configs/sim/axis/vismach/puma/puma.ini", 0, 1, 2},
|
||||||
|
{"iniFile", "configs/sim/axis/vismach/puma/puma.ini", 0, 1, 2},
|
||||||
|
{"iniFileName", "configs/sim/axis/vismach/puma/puma.ini", 0, 1, 2},
|
||||||
|
{"ini_file", "configs/sim/axis/vismach/puma/puma.ini", 0, 1, 2},
|
||||||
|
{"ini_file_name", "configs/sim/axis/vismach/puma/puma.ini", 0, 1, 2},
|
||||||
|
{"INI_FILE_NAME", "configs/sim/axis/vismach/puma/puma.ini", 0, 1, 2},
|
||||||
{"machine", "PUMA (pumakins,switchkins)", 0, 1, 2},
|
{"machine", "PUMA (pumakins,switchkins)", 0, 1, 2},
|
||||||
{"kinematics", "pumakins", 0, 1, 2},
|
{"kinematics", "pumakins", 0, 1, 2},
|
||||||
|
{"switchkins", "puma", 0, 1, 2},
|
||||||
|
{"switchkins", "PUMA (pumakins,switchkins)", 0, 1, 2},
|
||||||
|
{"switchkins", "PUMA", 0, 1, 2},
|
||||||
|
{"switchkins", "pumakins", 0, 1, 2},
|
||||||
|
{"halFile", "puma_dh.hal", 0, 1, 2},
|
||||||
|
{"halFile", "puma_dh", 0, 1, 2},
|
||||||
{"halfile", "puma_dh.hal", 0, 1, 2},
|
{"halfile", "puma_dh.hal", 0, 1, 2},
|
||||||
{"halfile", "puma_dh", 0, 1, 2},
|
{"halfile", "puma_dh", 0, 1, 2},
|
||||||
{"hal_file", "puma_dh.hal", 0, 1, 2},
|
{"hal_file", "puma_dh.hal", 0, 1, 2},
|
||||||
{"hal_file", "puma_dh", 0, 1, 2},
|
{"hal_file", "puma_dh", 0, 1, 2},
|
||||||
|
{"HALFILE", "puma_dh.hal", 0, 1, 2},
|
||||||
|
{"HALFILE", "puma_dh", 0, 1, 2},
|
||||||
|
{"postguiHalFile", "puma_postgui.hal", 0, 1, 2},
|
||||||
|
{"postguiHalFile", "puma_postgui", 0, 1, 2},
|
||||||
|
{"postgui_halfile", "puma_postgui.hal", 0, 1, 2},
|
||||||
|
{"postgui_halfile", "puma_postgui", 0, 1, 2},
|
||||||
|
{"postgui_hal_file", "puma_postgui.hal", 0, 1, 2},
|
||||||
|
{"postgui_hal_file", "puma_postgui", 0, 1, 2},
|
||||||
|
{"POSTGUI_HALFILE", "puma_postgui.hal", 0, 1, 2},
|
||||||
|
{"POSTGUI_HALFILE", "puma_postgui", 0, 1, 2},
|
||||||
|
{"remap", "configs/sim/axis/vismach/puma", 0, 1, 2},
|
||||||
|
{"remap", "puma", 0, 1, 2},
|
||||||
{"config", "configs/sim/axis/vismach/puma/puma_cube.ini", 0, 1, 2},
|
{"config", "configs/sim/axis/vismach/puma/puma_cube.ini", 0, 1, 2},
|
||||||
|
{"configPath", "configs/sim/axis/vismach/puma/puma_cube.ini", 0, 1, 2},
|
||||||
|
{"config_path", "configs/sim/axis/vismach/puma/puma_cube.ini", 0, 1, 2},
|
||||||
|
{"ini", "configs/sim/axis/vismach/puma/puma_cube.ini", 0, 1, 2},
|
||||||
|
{"iniFile", "configs/sim/axis/vismach/puma/puma_cube.ini", 0, 1, 2},
|
||||||
|
{"iniFileName", "configs/sim/axis/vismach/puma/puma_cube.ini", 0, 1, 2},
|
||||||
|
{"ini_file", "configs/sim/axis/vismach/puma/puma_cube.ini", 0, 1, 2},
|
||||||
|
{"ini_file_name", "configs/sim/axis/vismach/puma/puma_cube.ini", 0, 1, 2},
|
||||||
|
{"INI_FILE_NAME", "configs/sim/axis/vismach/puma/puma_cube.ini", 0, 1, 2},
|
||||||
{"machine", "puma_cube.ini (pumakins)", 0, 1, 2},
|
{"machine", "puma_cube.ini (pumakins)", 0, 1, 2},
|
||||||
|
{"switchkins", "puma_cube", 0, 1, 2},
|
||||||
|
{"switchkins", "puma_cube.ini (pumakins)", 0, 1, 2},
|
||||||
|
{"switchkins", "puma_cube.ini", 0, 1, 2},
|
||||||
{"config", "configs/sim/axis/vismach/puma/puma560.ini", 0, 1, 2},
|
{"config", "configs/sim/axis/vismach/puma/puma560.ini", 0, 1, 2},
|
||||||
|
{"configPath", "configs/sim/axis/vismach/puma/puma560.ini", 0, 1, 2},
|
||||||
|
{"config_path", "configs/sim/axis/vismach/puma/puma560.ini", 0, 1, 2},
|
||||||
|
{"ini", "configs/sim/axis/vismach/puma/puma560.ini", 0, 1, 2},
|
||||||
|
{"iniFile", "configs/sim/axis/vismach/puma/puma560.ini", 0, 1, 2},
|
||||||
|
{"iniFileName", "configs/sim/axis/vismach/puma/puma560.ini", 0, 1, 2},
|
||||||
|
{"ini_file", "configs/sim/axis/vismach/puma/puma560.ini", 0, 1, 2},
|
||||||
|
{"ini_file_name", "configs/sim/axis/vismach/puma/puma560.ini", 0, 1, 2},
|
||||||
|
{"INI_FILE_NAME", "configs/sim/axis/vismach/puma/puma560.ini", 0, 1, 2},
|
||||||
{"machine", "puma560 (switchkins) (inch)", 0, 1, 2},
|
{"machine", "puma560 (switchkins) (inch)", 0, 1, 2},
|
||||||
|
{"switchkins", "puma560", 0, 1, 2},
|
||||||
|
{"switchkins", "puma560 (switchkins) (inch)", 0, 1, 2},
|
||||||
|
{"halFile", "puma560_dh.hal", 0, 1, 2},
|
||||||
|
{"halFile", "puma560_dh", 0, 1, 2},
|
||||||
{"halfile", "puma560_dh.hal", 0, 1, 2},
|
{"halfile", "puma560_dh.hal", 0, 1, 2},
|
||||||
{"halfile", "puma560_dh", 0, 1, 2},
|
{"halfile", "puma560_dh", 0, 1, 2},
|
||||||
{"hal_file", "puma560_dh.hal", 0, 1, 2},
|
{"hal_file", "puma560_dh.hal", 0, 1, 2},
|
||||||
{"hal_file", "puma560_dh", 0, 1, 2},
|
{"hal_file", "puma560_dh", 0, 1, 2},
|
||||||
|
{"HALFILE", "puma560_dh.hal", 0, 1, 2},
|
||||||
|
{"HALFILE", "puma560_dh", 0, 1, 2},
|
||||||
|
{"postguiHalFile", "puma560_postgui.hal", 0, 1, 2},
|
||||||
|
{"postguiHalFile", "puma560_postgui", 0, 1, 2},
|
||||||
|
{"postgui_halfile", "puma560_postgui.hal", 0, 1, 2},
|
||||||
|
{"postgui_halfile", "puma560_postgui", 0, 1, 2},
|
||||||
|
{"postgui_hal_file", "puma560_postgui.hal", 0, 1, 2},
|
||||||
|
{"postgui_hal_file", "puma560_postgui", 0, 1, 2},
|
||||||
|
{"POSTGUI_HALFILE", "puma560_postgui.hal", 0, 1, 2},
|
||||||
|
{"POSTGUI_HALFILE", "puma560_postgui", 0, 1, 2},
|
||||||
{"config", "configs/sim/axis/vismach/puma/puma560_uvw.ini", 0, 1, 2},
|
{"config", "configs/sim/axis/vismach/puma/puma560_uvw.ini", 0, 1, 2},
|
||||||
|
{"configPath", "configs/sim/axis/vismach/puma/puma560_uvw.ini", 0, 1, 2},
|
||||||
|
{"config_path", "configs/sim/axis/vismach/puma/puma560_uvw.ini", 0, 1, 2},
|
||||||
|
{"ini", "configs/sim/axis/vismach/puma/puma560_uvw.ini", 0, 1, 2},
|
||||||
|
{"iniFile", "configs/sim/axis/vismach/puma/puma560_uvw.ini", 0, 1, 2},
|
||||||
|
{"iniFileName", "configs/sim/axis/vismach/puma/puma560_uvw.ini", 0, 1, 2},
|
||||||
|
{"ini_file", "configs/sim/axis/vismach/puma/puma560_uvw.ini", 0, 1, 2},
|
||||||
|
{"ini_file_name", "configs/sim/axis/vismach/puma/puma560_uvw.ini", 0, 1, 2},
|
||||||
|
{"INI_FILE_NAME", "configs/sim/axis/vismach/puma/puma560_uvw.ini", 0, 1, 2},
|
||||||
|
{"switchkins", "puma560_uvw", 0, 1, 2},
|
||||||
{"config", "configs/sim/axis/vismach/scara/scara.ini", 0, 1, 2},
|
{"config", "configs/sim/axis/vismach/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"configPath", "configs/sim/axis/vismach/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"config_path", "configs/sim/axis/vismach/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"ini", "configs/sim/axis/vismach/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"iniFile", "configs/sim/axis/vismach/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"iniFileName", "configs/sim/axis/vismach/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"ini_file", "configs/sim/axis/vismach/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"ini_file_name", "configs/sim/axis/vismach/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"INI_FILE_NAME", "configs/sim/axis/vismach/scara/scara.ini", 0, 1, 2},
|
||||||
{"machine", "SCARA (genserkins,switchkins)", 0, 1, 2},
|
{"machine", "SCARA (genserkins,switchkins)", 0, 1, 2},
|
||||||
{"kinematics", "scarakins coordinates=xyzcab", 0, 1, 2},
|
{"kinematics", "scarakins coordinates=xyzcab", 0, 1, 2},
|
||||||
|
{"switchkins", "scara", 0, 1, 2},
|
||||||
|
{"switchkins", "SCARA (genserkins,switchkins)", 0, 1, 2},
|
||||||
|
{"switchkins", "SCARA", 0, 1, 2},
|
||||||
|
{"switchkins", "scarakins coordinates=xyzcab", 0, 1, 2},
|
||||||
|
{"switchkins", "scarakins", 0, 1, 2},
|
||||||
|
{"postguiHalFile", "scara_postgui.hal", 0, 1, 2},
|
||||||
|
{"postguiHalFile", "scara_postgui", 0, 1, 2},
|
||||||
|
{"postgui_halfile", "scara_postgui.hal", 0, 1, 2},
|
||||||
|
{"postgui_halfile", "scara_postgui", 0, 1, 2},
|
||||||
|
{"postgui_hal_file", "scara_postgui.hal", 0, 1, 2},
|
||||||
|
{"postgui_hal_file", "scara_postgui", 0, 1, 2},
|
||||||
|
{"POSTGUI_HALFILE", "scara_postgui.hal", 0, 1, 2},
|
||||||
|
{"POSTGUI_HALFILE", "scara_postgui", 0, 1, 2},
|
||||||
|
{"remap", "configs/sim/axis/vismach/scara", 0, 1, 2},
|
||||||
|
{"remap", "scara", 0, 1, 2},
|
||||||
{"config", "configs/sim/qtaxis/non-trivial/scara/scara.ini", 0, 1, 2},
|
{"config", "configs/sim/qtaxis/non-trivial/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"configPath", "configs/sim/qtaxis/non-trivial/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"config_path", "configs/sim/qtaxis/non-trivial/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"ini", "configs/sim/qtaxis/non-trivial/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"iniFile", "configs/sim/qtaxis/non-trivial/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"iniFileName", "configs/sim/qtaxis/non-trivial/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"ini_file", "configs/sim/qtaxis/non-trivial/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"ini_file_name", "configs/sim/qtaxis/non-trivial/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"INI_FILE_NAME", "configs/sim/qtaxis/non-trivial/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"remap", "configs/sim/qtaxis/non-trivial/scara", 0, 1, 2},
|
||||||
{"config", "configs/sim/qtvcp_screens/non-trivial/scara/scara.ini", 0, 1, 2},
|
{"config", "configs/sim/qtvcp_screens/non-trivial/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"configPath", "configs/sim/qtvcp_screens/non-trivial/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"config_path", "configs/sim/qtvcp_screens/non-trivial/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"ini", "configs/sim/qtvcp_screens/non-trivial/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"iniFile", "configs/sim/qtvcp_screens/non-trivial/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"iniFileName", "configs/sim/qtvcp_screens/non-trivial/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"ini_file", "configs/sim/qtvcp_screens/non-trivial/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"ini_file_name", "configs/sim/qtvcp_screens/non-trivial/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"INI_FILE_NAME", "configs/sim/qtvcp_screens/non-trivial/scara/scara.ini", 0, 1, 2},
|
||||||
|
{"remap", "configs/sim/qtvcp_screens/non-trivial/scara", 0, 1, 2},
|
||||||
|
|||||||
@@ -27,6 +27,22 @@ designed project code. See `docs/linuxcnc-source-policy.md`.
|
|||||||
4. Compile with Emscripten after replacing `dlopen`, Python, HAL and filesystem-only features.
|
4. Compile with Emscripten after replacing `dlopen`, Python, HAL and filesystem-only features.
|
||||||
5. Compare event output with native LinuxCNC using the same G-code corpus.
|
5. Compare event output with native LinuxCNC using the same G-code corpus.
|
||||||
|
|
||||||
|
## Current Workstreams
|
||||||
|
|
||||||
|
The current porting work is being split into six small contexts and should be
|
||||||
|
advanced in this order:
|
||||||
|
|
||||||
|
1. `switchkins/remap` generator and tables
|
||||||
|
2. `linuxcnc-kinematics` source manifest
|
||||||
|
3. WASM filesystem and OPFS persistence
|
||||||
|
4. browser app integration
|
||||||
|
5. Node and browser smoke coverage
|
||||||
|
6. source-link, build, and documentation constraints
|
||||||
|
|
||||||
|
Each step should stay source-backed and close one small gap at a time. A single
|
||||||
|
pass should advance one workstream context only; crossing into another context
|
||||||
|
requires naming the dependency and keeping the edit set minimal.
|
||||||
|
|
||||||
## Dialect expansion
|
## Dialect expansion
|
||||||
|
|
||||||
LinuxCNC support should be the baseline. Fanuc and Siemens support should be implemented as dialect adapters, not by forking the simulator core.
|
LinuxCNC support should be the baseline. Fanuc and Siemens support should be implemented as dialect adapters, not by forking the simulator core.
|
||||||
|
|||||||
@@ -159,8 +159,13 @@ LinuxCNC interpreter sources currently involve Python/Boost.Python remap paths.
|
|||||||
|
|
||||||
- disable Python remap
|
- disable Python remap
|
||||||
- disable dynamic module loading
|
- disable dynamic module loading
|
||||||
- replace file-backed parameter persistence with in-memory buffers
|
- route LinuxCNC parameter-file reads and writes through an OPFS-backed wasm
|
||||||
- provide browser-safe tool table and INI/config loading through JSON
|
workspace, preserving `rs274ngc.var` and `.bak` persistence in browser runs
|
||||||
|
as defined by `src/emc/rs274ngc/interp_internal.hh` and exercised by
|
||||||
|
`Interp::restore_parameters`/`Interp::save_parameters` in
|
||||||
|
`src/emc/rs274ngc/rs274ngc_pre.cc`
|
||||||
|
- provide browser-safe tool table and INI/config loading through JSON or
|
||||||
|
OPFS-backed files
|
||||||
|
|
||||||
## Step 5: Emscripten build
|
## Step 5: Emscripten build
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,23 @@
|
|||||||
The simulator must not grow independently designed functional behavior.
|
The simulator must not grow independently designed functional behavior.
|
||||||
Functional behavior must come from LinuxCNC source code.
|
Functional behavior must come from LinuxCNC source code.
|
||||||
|
|
||||||
|
Hard rules:
|
||||||
|
|
||||||
|
- Port-first: prefer direct LinuxCNC source porting, trimming, wrapping, or
|
||||||
|
platform adaptation over project-authored replacements.
|
||||||
|
- Do not write project-owned functional CNC behavior.
|
||||||
|
- Functional CNC behavior must be ported, trimmed, wrapped, or routed from
|
||||||
|
LinuxCNC source code.
|
||||||
|
- Any G/M-code interpretation, motion behavior, kinematics, coordinate
|
||||||
|
handling, cutter compensation, canned cycle, parameter expression, modal
|
||||||
|
state, tool data, remap behavior, or RTCP behavior must first be traced to
|
||||||
|
LinuxCNC source before implementation.
|
||||||
|
- Browser-side wasm filesystem behavior must use OPFS-backed storage. Do not
|
||||||
|
add browser filesystem paths that bypass OPFS.
|
||||||
|
|
||||||
Allowed project code:
|
Allowed project code:
|
||||||
|
|
||||||
|
- LinuxCNC source porting, trimming, wrappers, and platform adaptation.
|
||||||
- Thin adapters between LinuxCNC source code and the simulator C API.
|
- Thin adapters between LinuxCNC source code and the simulator C API.
|
||||||
- Platform shims needed to compile LinuxCNC code for native tests or wasm.
|
- Platform shims needed to compile LinuxCNC code for native tests or wasm.
|
||||||
- Event serialization, test fixtures, build scripts, and documentation.
|
- Event serialization, test fixtures, build scripts, and documentation.
|
||||||
@@ -22,7 +37,81 @@ Required workflow for every functional change:
|
|||||||
2. Add or update a test that demonstrates the LinuxCNC behavior.
|
2. Add or update a test that demonstrates the LinuxCNC behavior.
|
||||||
3. Port, wrap, or route to that LinuxCNC implementation.
|
3. Port, wrap, or route to that LinuxCNC implementation.
|
||||||
4. Record the source file/function in code comments or nearby documentation when the mapping is not obvious.
|
4. Record the source file/function in code comments or nearby documentation when the mapping is not obvious.
|
||||||
5. Run `./test-native.sh` and `./test-linuxcnc-source-link.sh`.
|
5. Keep the change inside one active workstream context unless a dependency is
|
||||||
|
unavoidable and explicitly named in the final report.
|
||||||
|
6. Run `./test-native.sh` and `./test-linuxcnc-source-link.sh`.
|
||||||
|
7. For source-manifest, build, or build-policy changes, also run the aggregate
|
||||||
|
and guardrail checks: `./test-all-native.sh`,
|
||||||
|
`./test-linuxcnc-source-syntax.sh`, and
|
||||||
|
`./test-linuxcnc-wasm-cmake-safe-probe.sh`.
|
||||||
|
|
||||||
|
Efficiency rules:
|
||||||
|
|
||||||
|
- Do not add a smoke-only behavior path when the LinuxCNC-backed route already
|
||||||
|
exists; add coverage to the source-backed route instead.
|
||||||
|
- Do not mix generator/table work, manifests, OPFS/browser work, and build
|
||||||
|
policy cleanup in the same pass unless the files directly depend on each
|
||||||
|
other.
|
||||||
|
- Prefer tightening an existing probe or manifest over adding another broad
|
||||||
|
end-to-end smoke case.
|
||||||
|
- Default native, source-link, build-wasm, and wasm-safe CMake probe
|
||||||
|
parallelism to `CNC_SIM_BUILD_JOBS:-8`; raise it only by explicit
|
||||||
|
positive-integer environment override.
|
||||||
|
- Persistent native, source-link, and wasm-safe CMake probe build directories
|
||||||
|
may be overridden for local workflows, but must not point at the filesystem
|
||||||
|
root.
|
||||||
|
- Build policy knobs are intentionally narrow:
|
||||||
|
`CNC_SIM_BUILD_JOBS`, `CNC_SIM_NATIVE_BUILD_DIR`,
|
||||||
|
`CNC_SIM_SOURCE_LINK_BUILD_DIR`, `CNC_SIM_SOURCE_SYNTAX_BUILD_DIR`, and
|
||||||
|
`CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR`. Build directory overrides must
|
||||||
|
stay non-empty, whitespace-free, and outside the filesystem root.
|
||||||
|
- `build-wasm.sh` keeps fixed artifact paths: CMake writes `build/wasm`, then
|
||||||
|
the script copies and byte-compares `cnc_sim.js` and `cnc_sim.wasm` under
|
||||||
|
`web/public` before running Node and browser smoke checks.
|
||||||
|
|
||||||
|
Build and source-link guardrails:
|
||||||
|
|
||||||
|
- `CNC_SIM_BUILD_JOBS` must be a positive integer and defaults to `8`.
|
||||||
|
- `test-native.sh` uses persistent `build/native-test` objects, dependency
|
||||||
|
files, and `native-build.signature`.
|
||||||
|
- `test-linuxcnc-source-link.sh` uses persistent `build/source-link-test`
|
||||||
|
objects, dependency files, and `source-link-build.signature`.
|
||||||
|
- `test-linuxcnc-source-syntax.sh` uses persistent
|
||||||
|
`build/source-syntax-test` stamps, dependency files, and
|
||||||
|
`source-syntax.signature`.
|
||||||
|
- `test-linuxcnc-wasm-cmake-safe-probe.sh` uses persistent
|
||||||
|
`build/wasm-cmake-safe-probe` and `wasm-cmake-safe-probe.signature`.
|
||||||
|
- Native, source-link, source-syntax, wasm-safe CMake probe, and build-wasm
|
||||||
|
paths must reject empty, whitespace-containing, or filesystem-root build
|
||||||
|
directory overrides before doing destructive cleanup.
|
||||||
|
- Native, source-link, source-syntax, and build-wasm entry points must serialize
|
||||||
|
shared non-concurrency-safe paths with lock files before writing fixed
|
||||||
|
outputs.
|
||||||
|
- Source-link and syntax probes must build from generated makefiles with
|
||||||
|
`-MMD -MP` dependency tracking and `make --output-sync=target`.
|
||||||
|
- `build-wasm.sh` must run blocker, syntax, object, CMake, tooldata,
|
||||||
|
`interp_*`, Python, `rs274ngc_pre`, and runtime link probes before copying
|
||||||
|
browser artifacts.
|
||||||
|
- `build-wasm.sh` must check `cmake`, `emcmake`, `emcc`, and `node` before the
|
||||||
|
Emscripten configure step.
|
||||||
|
- `build-wasm.sh` must build `cnc_sim_wasm_runtime_probe` before
|
||||||
|
`cnc_sim_wasm`.
|
||||||
|
- `build-wasm.sh` must copy `build/wasm/cnc_sim.js` and
|
||||||
|
`build/wasm/cnc_sim.wasm` exactly once each.
|
||||||
|
- `build-wasm.sh` must compare copied artifacts with `cmp -s` before running
|
||||||
|
Node or browser smoke tests.
|
||||||
|
- `build-wasm.sh` must run the Node smoke before the browser smoke.
|
||||||
|
- The default wasm manifest partition must remain explicitly checked for 25
|
||||||
|
wasm-safe core sources and 9 blocked sources.
|
||||||
|
- Manifest source listers must reject malformed lines, absolute source paths,
|
||||||
|
unknown groups, unknown filters, and duplicate source entries.
|
||||||
|
- Native link flags must include the built LinuxCNC `lib` path and rpath.
|
||||||
|
- Common CXX flag helpers must surface helper failures instead of hiding them
|
||||||
|
behind process substitution or `read`.
|
||||||
|
- Temporary probe reports must be cleaned on failure and preserved only after a
|
||||||
|
successful report path explicitly opts in.
|
||||||
|
- Documentation-only build policy edits should still update the guardrail
|
||||||
|
checks that enforce the relevant script and artifact ordering.
|
||||||
|
|
||||||
Current temporary exceptions:
|
Current temporary exceptions:
|
||||||
|
|
||||||
|
|||||||
@@ -5,15 +5,49 @@ cd "$(dirname "$0")"
|
|||||||
|
|
||||||
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
||||||
mode=table
|
mode=table
|
||||||
if [[ "${1:-}" == "--config-cases" ]]; then
|
all_output_dir=
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "usage: $0 [--config-cases|--json-cases|--all-output-dir DIR] [manifest]" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
case "${1:-}" in
|
||||||
|
--config-cases)
|
||||||
mode=config-cases
|
mode=config-cases
|
||||||
shift
|
shift
|
||||||
elif [[ "${1:-}" == "--json-cases" ]]; then
|
;;
|
||||||
|
--json-cases)
|
||||||
mode=json-cases
|
mode=json-cases
|
||||||
shift
|
shift
|
||||||
|
;;
|
||||||
|
--all-output-dir)
|
||||||
|
mode=all
|
||||||
|
if [[ -z "${2:-}" || "${2:-}" == --* ]]; then
|
||||||
|
usage
|
||||||
|
echo "missing output directory for --all-output-dir" >&2
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
all_output_dir=$2
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--help|-h)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
--*)
|
||||||
|
usage
|
||||||
|
echo "unknown switchkins remap table generator option: $1" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
manifest=${1:-linuxcnc-kinematics-source-files.txt}
|
manifest=${1:-linuxcnc-kinematics-source-files.txt}
|
||||||
|
|
||||||
|
if [[ -n "${2:-}" ]]; then
|
||||||
|
usage
|
||||||
|
echo "too many switchkins remap table generator arguments" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ ! -d "$linuxcnc_root" ]]; then
|
if [[ ! -d "$linuxcnc_root" ]]; then
|
||||||
echo "missing LinuxCNC root: $linuxcnc_root" >&2
|
echo "missing LinuxCNC root: $linuxcnc_root" >&2
|
||||||
exit 1
|
exit 1
|
||||||
@@ -22,34 +56,58 @@ if [[ ! -f "$manifest" ]]; then
|
|||||||
echo "missing kinematics manifest: $manifest" >&2
|
echo "missing kinematics manifest: $manifest" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
if [[ "$mode" == all && -z "$all_output_dir" ]]; then
|
||||||
|
echo "missing output directory for --all-output-dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
|
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
|
||||||
|
|
||||||
normalize_alias() {
|
read_ini_switchkins_config() {
|
||||||
tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'
|
|
||||||
}
|
|
||||||
|
|
||||||
trim_value() {
|
|
||||||
sed 's/[[:space:]]*[#;].*$//; s/^[[:space:]]*//; s/[[:space:]]*$//'
|
|
||||||
}
|
|
||||||
|
|
||||||
ini_value() {
|
|
||||||
local file=$1
|
local file=$1
|
||||||
local key=$2
|
awk '
|
||||||
sed -n "s/^[[:space:]]*$key[[:space:]]*=[[:space:]]*//Ip" "$file" | head -n 1 | trim_value
|
function trim(value) {
|
||||||
|
sub(/^[[:space:]]*/, "", value)
|
||||||
|
sub(/[[:space:]]*$/, "", value)
|
||||||
|
return value
|
||||||
}
|
}
|
||||||
|
{
|
||||||
ini_values() {
|
line = $0
|
||||||
local file=$1
|
sub(/[[:space:]]*[#;].*$/, "", line)
|
||||||
local key=$2
|
line = trim(line)
|
||||||
sed -n "s/^[[:space:]]*$key[[:space:]]*=[[:space:]]*//Ip" "$file" | trim_value
|
if (line !~ /=/) {
|
||||||
|
next
|
||||||
}
|
}
|
||||||
|
key = line
|
||||||
|
sub(/[[:space:]]*=.*/, "", key)
|
||||||
|
key = tolower(trim(key))
|
||||||
|
value = line
|
||||||
|
sub(/^[^=]*=[[:space:]]*/, "", value)
|
||||||
|
value = trim(value)
|
||||||
|
|
||||||
remap_ngc_name() {
|
if (key == "machine") {
|
||||||
local file=$1
|
print "machine\t" value
|
||||||
local mcode=$2
|
} else if (key == "kinematics") {
|
||||||
sed -n "s/.*REMAP[[:space:]]*=[[:space:]]*M$mcode[[:space:]][^#;]*ngc[[:space:]]*=[[:space:]]*\\([^[:space:]#;]*\\).*/\\1/Ip" "$file" |
|
print "kinematics\t" value
|
||||||
head -n 1
|
} else if (key == "halfile") {
|
||||||
|
print "halfile\t" value
|
||||||
|
} else if (key == "postgui_halfile") {
|
||||||
|
print "postgui_halfile\t" value
|
||||||
|
} else if (key == "subroutine_path") {
|
||||||
|
print "subroutine_path\t" value
|
||||||
|
} else if (key == "remap") {
|
||||||
|
lower = tolower(value)
|
||||||
|
for (mcode = 428; mcode <= 430; mcode++) {
|
||||||
|
if (lower ~ "m" mcode "([^0-9]|$)" &&
|
||||||
|
match(lower, /ngc[[:space:]]*=[[:space:]]*[^[:space:]]+/)) {
|
||||||
|
ngc = substr(value, RSTART, RLENGTH)
|
||||||
|
sub(/^[Nn][Gg][Cc][[:space:]]*=[[:space:]]*/, "", ngc)
|
||||||
|
print "remap_" mcode "\t" ngc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
' "$file"
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve_config_path() {
|
resolve_config_path() {
|
||||||
@@ -64,26 +122,92 @@ resolve_config_path() {
|
|||||||
|
|
||||||
relative_linuxcnc_path() {
|
relative_linuxcnc_path() {
|
||||||
local path=$1
|
local path=$1
|
||||||
path=$(cd "$(dirname "$path")" && pwd)/$(basename "$path")
|
path=$(cd "${path%/*}" && pwd)/${path##*/}
|
||||||
|
if [[ "$path" != "$linuxcnc_root/"* ]]; then
|
||||||
|
echo "resolved LinuxCNC source path escapes LinuxCNC root: $path" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
printf '%s\n' "${path#"$linuxcnc_root/"}"
|
printf '%s\n' "${path#"$linuxcnc_root/"}"
|
||||||
}
|
}
|
||||||
|
|
||||||
remap_source_for_mcode() {
|
declare -A config_dir_cache=()
|
||||||
local config=$1
|
declare -A manifest_config_sources=()
|
||||||
local mcode=$2
|
declare -A used_config_sources=()
|
||||||
local ngc_name
|
declare -A manifest_remap_sources=()
|
||||||
ngc_name=$(remap_ngc_name "$config" "$mcode")
|
declare -A used_remap_sources=()
|
||||||
if [[ -z "$ngc_name" ]]; then
|
manifest_config_count=0
|
||||||
return 1
|
manifest_remap_count=0
|
||||||
|
|
||||||
|
validate_manifest_source_path() {
|
||||||
|
local group=$1
|
||||||
|
local path=$2
|
||||||
|
if [[ -z "$path" ]]; then
|
||||||
|
echo "empty LinuxCNC $group source path in $manifest" >&2
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
if [[ "$path" = /* || "$path" == *..* ]]; then
|
||||||
|
echo "LinuxCNC $group source path must stay under LinuxCNC root: $path" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ ! -f "$linuxcnc_root/$path" ]]; then
|
||||||
|
echo "missing LinuxCNC $group source listed in $manifest: $path" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
while IFS=: read -r manifest_group manifest_path _manifest_note; do
|
||||||
|
case "$manifest_group" in
|
||||||
|
config)
|
||||||
|
validate_manifest_source_path "$manifest_group" "$manifest_path"
|
||||||
|
if [[ -n "${manifest_config_sources[$manifest_path]:-}" ]]; then
|
||||||
|
echo "duplicate LinuxCNC config source in $manifest: $manifest_path" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
manifest_config_sources[$manifest_path]=1
|
||||||
|
manifest_config_count=$((manifest_config_count + 1))
|
||||||
|
;;
|
||||||
|
remap)
|
||||||
|
validate_manifest_source_path "$manifest_group" "$manifest_path"
|
||||||
|
if [[ "$manifest_path" != *.ngc ]]; then
|
||||||
|
echo "LinuxCNC remap source must be an ngc file in $manifest: $manifest_path" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
case "${manifest_path##*/}" in
|
||||||
|
428remap.ngc|429remap.ngc|430remap.ngc)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "LinuxCNC switchkins remap source must be 428/429/430remap.ngc in $manifest: $manifest_path" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
if [[ -n "${manifest_remap_sources[$manifest_path]:-}" ]]; then
|
||||||
|
echo "duplicate LinuxCNC remap source in $manifest: $manifest_path" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
manifest_remap_sources[$manifest_path]=1
|
||||||
|
manifest_remap_count=$((manifest_remap_count + 1))
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done <"$manifest"
|
||||||
|
|
||||||
|
config_dir_for() {
|
||||||
|
local config=$1
|
||||||
|
if [[ -z "${config_dir_cache[$config]:-}" ]]; then
|
||||||
|
config_dir_cache[$config]=$(cd "${config%/*}" && pwd)
|
||||||
|
fi
|
||||||
|
printf '%s\n' "${config_dir_cache[$config]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
remap_source_for_ngc() {
|
||||||
|
local config=$1
|
||||||
|
local ngc_name=$2
|
||||||
|
local subroutine_paths=${3:-}
|
||||||
if [[ "$ngc_name" != *.ngc ]]; then
|
if [[ "$ngc_name" != *.ngc ]]; then
|
||||||
ngc_name="$ngc_name.ngc"
|
ngc_name="$ngc_name.ngc"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local config_dir
|
local config_dir
|
||||||
config_dir=$(cd "$(dirname "$config")" && pwd)
|
config_dir=$(config_dir_for "$config")
|
||||||
local subroutine_paths
|
|
||||||
subroutine_paths=$(ini_values "$config" "SUBROUTINE_PATH")
|
|
||||||
if [[ -z "$subroutine_paths" ]]; then
|
if [[ -z "$subroutine_paths" ]]; then
|
||||||
subroutine_paths="."
|
subroutine_paths="."
|
||||||
fi
|
fi
|
||||||
@@ -107,41 +231,114 @@ remap_source_for_mcode() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
kinstype_for_mcode() {
|
config_source_for_halfile() {
|
||||||
local config=$1
|
local config=$1
|
||||||
local mcode=$2
|
local halfile=$2
|
||||||
|
[[ -n "$halfile" ]] || return 1
|
||||||
|
[[ "$halfile" == LIB:* ]] && return 1
|
||||||
|
|
||||||
|
local hal_path=${halfile%%[[:space:]]*}
|
||||||
|
[[ -n "$hal_path" ]] || return 1
|
||||||
|
|
||||||
|
local config_dir
|
||||||
|
config_dir=$(config_dir_for "$config")
|
||||||
|
local resolved
|
||||||
|
resolved=$(resolve_config_path "$config_dir" "$hal_path")
|
||||||
|
if [[ ! -f "$resolved" ]]; then
|
||||||
|
echo "missing LinuxCNC HAL source referenced by ${config#"$linuxcnc_root/"}: $halfile" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
relative_linuxcnc_path "$resolved"
|
||||||
|
}
|
||||||
|
|
||||||
|
kinstype_for_remap_source() {
|
||||||
local source
|
local source
|
||||||
if ! source=$(remap_source_for_mcode "$config" "$mcode"); then
|
source=$1
|
||||||
printf '%s\n' "-1"
|
if [[ -z "$source" ]]; then
|
||||||
|
printf '%s\n' -1
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
sed -n 's/^[[:space:]]*#<kinstype>[[:space:]]*=[[:space:]]*\([0-9][0-9]*\).*/\1/p' \
|
awk '
|
||||||
"$linuxcnc_root/$source" |
|
/^[[:space:]]*#<kinstype>[[:space:]]*=/ {
|
||||||
head -n 1
|
value = $0
|
||||||
|
sub(/^[[:space:]]*#<kinstype>[[:space:]]*=[[:space:]]*/, "", value)
|
||||||
|
sub(/[^0-9].*$/, "", value)
|
||||||
|
print value
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
' "$linuxcnc_root/$source"
|
||||||
}
|
}
|
||||||
|
|
||||||
json_compact_string() {
|
require_remap_source() {
|
||||||
local value=$1
|
local config_path=$1
|
||||||
printf '%s' "$value" | normalize_alias | sed 's/\\/\\\\/g; s/"/\\"/g'
|
local mcode=$2
|
||||||
|
local ngc_name=$3
|
||||||
|
local source=$4
|
||||||
|
if [[ -z "$source" ]]; then
|
||||||
|
echo "missing LinuxCNC M$mcode remap source for $config_path: ngc=$ngc_name" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
cpp_string() {
|
require_kinstype() {
|
||||||
local value=$1
|
local config_path=$1
|
||||||
printf '%s' "$value" | sed 's/\\/\\\\/g; s/"/\\"/g'
|
local mcode=$2
|
||||||
|
local source=$3
|
||||||
|
local kinstype=$4
|
||||||
|
if [[ -z "$kinstype" || "$kinstype" == -1 ]]; then
|
||||||
|
echo "missing #<kinstype> assignment for LinuxCNC M$mcode remap source: $source ($config_path)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
record_remap_source() {
|
||||||
|
local config_path=$1
|
||||||
|
local mcode=$2
|
||||||
|
local source=$3
|
||||||
|
if [[ -z "${manifest_remap_sources[$source]:-}" ]]; then
|
||||||
|
echo "LinuxCNC M$mcode remap source is not listed in $manifest: $source ($config_path)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
used_remap_sources[$source]=1
|
||||||
|
}
|
||||||
|
|
||||||
|
record_config_source() {
|
||||||
|
local config_path=$1
|
||||||
|
local source=$2
|
||||||
|
local label=$3
|
||||||
|
if [[ -z "${manifest_config_sources[$source]:-}" ]]; then
|
||||||
|
echo "LinuxCNC $label source is not listed in $manifest: $source ($config_path)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
used_config_sources[$source]=1
|
||||||
}
|
}
|
||||||
|
|
||||||
declare -A seen_aliases=()
|
declare -A seen_aliases=()
|
||||||
declare -A seen_cases=()
|
declare -A seen_cases=()
|
||||||
|
declare -A seen_case_values=()
|
||||||
entries=()
|
entries=()
|
||||||
case_entries=()
|
case_entries=()
|
||||||
json_case_entries=()
|
json_case_entries=()
|
||||||
|
processed_config_count=0
|
||||||
|
resolved_remap_count=0
|
||||||
|
unique_config_source_count=0
|
||||||
|
unique_resolved_remap_count=0
|
||||||
|
|
||||||
|
escape_generated_string() {
|
||||||
|
local value=$1
|
||||||
|
value=${value//\\/\\\\}
|
||||||
|
value=${value//\"/\\\"}
|
||||||
|
printf '%s\n' "$value"
|
||||||
|
}
|
||||||
|
|
||||||
add_alias() {
|
add_alias() {
|
||||||
local alias=$1
|
local alias=$1
|
||||||
local m428=$2
|
local m428=$2
|
||||||
local m429=$3
|
local m429=$3
|
||||||
local m430=$4
|
local m430=$4
|
||||||
alias=$(json_compact_string "$alias")
|
alias=${alias,,}
|
||||||
|
alias=${alias//[[:space:]]/}
|
||||||
|
alias=$(escape_generated_string "$alias")
|
||||||
[[ -n "$alias" ]] || return 0
|
[[ -n "$alias" ]] || return 0
|
||||||
|
|
||||||
local types="$m428,$m429,$m430"
|
local types="$m428,$m429,$m430"
|
||||||
@@ -166,18 +363,80 @@ add_case() {
|
|||||||
[[ -n "$value" ]] || return 0
|
[[ -n "$value" ]] || return 0
|
||||||
|
|
||||||
local types="$m428,$m429,$m430"
|
local types="$m428,$m429,$m430"
|
||||||
|
local value_key="$field|$value"
|
||||||
|
if [[ -n "${seen_case_values[$value_key]:-}" && "${seen_case_values[$value_key]}" != "$types" ]]; then
|
||||||
|
echo "ambiguous switchkins config case '$field=$value': ${seen_case_values[$value_key]} and $types" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
seen_case_values[$value_key]=$types
|
||||||
|
|
||||||
local key="$field|$value|$types"
|
local key="$field|$value|$types"
|
||||||
[[ -z "${seen_cases[$key]:-}" ]] || return 0
|
[[ -z "${seen_cases[$key]:-}" ]] || return 0
|
||||||
|
|
||||||
seen_cases[$key]=1
|
seen_cases[$key]=1
|
||||||
local cpp_field
|
local cpp_field=$field
|
||||||
local cpp_value
|
local cpp_value=$value
|
||||||
cpp_field=$(cpp_string "$field")
|
cpp_field=$(escape_generated_string "$cpp_field")
|
||||||
cpp_value=$(cpp_string "$value")
|
cpp_value=$(escape_generated_string "$cpp_value")
|
||||||
case_entries+=(" {\"$cpp_field\", \"$cpp_value\", $m428, $m429, $m430},")
|
case_entries+=(" {\"$cpp_field\", \"$cpp_value\", $m428, $m429, $m430},")
|
||||||
json_case_entries+=("{\"field\":\"$cpp_field\",\"value\":\"$cpp_value\",\"m428\":$m428,\"m429\":$m429,\"m430\":$m430}")
|
json_case_entries+=("{\"field\":\"$cpp_field\",\"value\":\"$cpp_value\",\"m428\":$m428,\"m429\":$m429,\"m430\":$m430}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trim_machine_label() {
|
||||||
|
local machine=$1
|
||||||
|
machine=${machine%%(*}
|
||||||
|
machine="${machine#"${machine%%[![:space:]]*}"}"
|
||||||
|
machine="${machine%"${machine##*[![:space:]]}"}"
|
||||||
|
printf '%s\n' "$machine"
|
||||||
|
}
|
||||||
|
|
||||||
|
add_kinematics_variants() {
|
||||||
|
local callback=$1
|
||||||
|
local field=$2
|
||||||
|
local kinematics=$3
|
||||||
|
local m428=$4
|
||||||
|
local m429=$5
|
||||||
|
local m430=$6
|
||||||
|
|
||||||
|
[[ -n "$kinematics" ]] || return 0
|
||||||
|
"$callback" "$field" "$kinematics" "$m428" "$m429" "$m430"
|
||||||
|
|
||||||
|
local first_word=${kinematics%%[[:space:]]*}
|
||||||
|
"$callback" "$field" "$first_word" "$m428" "$m429" "$m430"
|
||||||
|
"$callback" "$field" "${first_word%_kins}" "$m428" "$m429" "$m430"
|
||||||
|
"$callback" "$field" "${first_word%-kins}" "$m428" "$m429" "$m430"
|
||||||
|
local first_word_hyphen=${first_word//_/-}
|
||||||
|
local first_word_without_underscore_kins=${first_word%_kins}
|
||||||
|
local first_word_without_dash_kins=${first_word%-kins}
|
||||||
|
"$callback" "$field" "$first_word_hyphen" "$m428" "$m429" "$m430"
|
||||||
|
"$callback" "$field" "${first_word_without_underscore_kins//_/-}" "$m428" "$m429" "$m430"
|
||||||
|
"$callback" "$field" "${first_word_without_dash_kins//_/-}" "$m428" "$m429" "$m430"
|
||||||
|
}
|
||||||
|
|
||||||
|
add_alias_variant() {
|
||||||
|
local _field=$1
|
||||||
|
shift
|
||||||
|
add_alias "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
add_case_variant() {
|
||||||
|
add_case "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
add_halfile_case_fields() {
|
||||||
|
local field=$1
|
||||||
|
local halfile=$2
|
||||||
|
local m428=$3
|
||||||
|
local m429=$4
|
||||||
|
local m430=$5
|
||||||
|
|
||||||
|
local hal_base=${halfile##*/}
|
||||||
|
local hal_stem=${hal_base%.hal}
|
||||||
|
add_case "$field" "$halfile" "$m428" "$m429" "$m430"
|
||||||
|
add_case "$field" "$hal_base" "$m428" "$m429" "$m430"
|
||||||
|
add_case "$field" "$hal_stem" "$m428" "$m429" "$m430"
|
||||||
|
}
|
||||||
|
|
||||||
add_machine_aliases() {
|
add_machine_aliases() {
|
||||||
local machine=$1
|
local machine=$1
|
||||||
local m428=$2
|
local m428=$2
|
||||||
@@ -185,7 +444,9 @@ add_machine_aliases() {
|
|||||||
local m430=$4
|
local m430=$4
|
||||||
[[ -n "$machine" ]] || return 0
|
[[ -n "$machine" ]] || return 0
|
||||||
add_alias "$machine" "$m428" "$m429" "$m430"
|
add_alias "$machine" "$m428" "$m429" "$m430"
|
||||||
add_alias "${machine%%(*}" "$m428" "$m429" "$m430"
|
local trimmed_machine
|
||||||
|
trimmed_machine=$(trim_machine_label "$machine")
|
||||||
|
add_alias "$trimmed_machine" "$m428" "$m429" "$m430"
|
||||||
}
|
}
|
||||||
|
|
||||||
add_kinematics_aliases() {
|
add_kinematics_aliases() {
|
||||||
@@ -194,15 +455,31 @@ add_kinematics_aliases() {
|
|||||||
local m429=$3
|
local m429=$3
|
||||||
local m430=$4
|
local m430=$4
|
||||||
[[ -n "$kinematics" ]] || return 0
|
[[ -n "$kinematics" ]] || return 0
|
||||||
add_alias "$kinematics" "$m428" "$m429" "$m430"
|
add_kinematics_variants add_alias_variant "" "$kinematics" "$m428" "$m429" "$m430"
|
||||||
|
}
|
||||||
|
|
||||||
local first_word=${kinematics%%[[:space:]]*}
|
add_switchkins_cases() {
|
||||||
add_alias "$first_word" "$m428" "$m429" "$m430"
|
local config_dir=$1
|
||||||
add_alias "${first_word%_kins}" "$m428" "$m429" "$m430"
|
local config_stem=$2
|
||||||
add_alias "${first_word%-kins}" "$m428" "$m429" "$m430"
|
local machine=$3
|
||||||
add_alias "$(printf '%s' "$first_word" | tr '_' '-')" "$m428" "$m429" "$m430"
|
local kinematics=$4
|
||||||
add_alias "$(printf '%s' "${first_word%_kins}" | tr '_' '-')" "$m428" "$m429" "$m430"
|
local m428=$5
|
||||||
add_alias "$(printf '%s' "${first_word%-kins}" | tr '_' '-')" "$m428" "$m429" "$m430"
|
local m429=$6
|
||||||
|
local m430=$7
|
||||||
|
|
||||||
|
add_case "switchkins" "${config_dir##*/}" "$m428" "$m429" "$m430"
|
||||||
|
add_case "switchkins" "$config_stem" "$m428" "$m429" "$m430"
|
||||||
|
|
||||||
|
if [[ -n "$machine" ]]; then
|
||||||
|
add_case "switchkins" "$machine" "$m428" "$m429" "$m430"
|
||||||
|
local trimmed_machine
|
||||||
|
trimmed_machine=$(trim_machine_label "$machine")
|
||||||
|
add_case "switchkins" "$trimmed_machine" "$m428" "$m429" "$m430"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$kinematics" ]]; then
|
||||||
|
add_kinematics_variants add_case_variant "switchkins" "$kinematics" "$m428" "$m429" "$m430"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
add_halfile_aliases() {
|
add_halfile_aliases() {
|
||||||
@@ -226,14 +503,24 @@ add_halfile_cases() {
|
|||||||
[[ -n "$halfile" ]] || return 0
|
[[ -n "$halfile" ]] || return 0
|
||||||
[[ "$halfile" == LIB:* ]] && return 0
|
[[ "$halfile" == LIB:* ]] && return 0
|
||||||
|
|
||||||
local hal_base=${halfile##*/}
|
add_halfile_case_fields "halFile" "$halfile" "$m428" "$m429" "$m430"
|
||||||
local hal_stem=${hal_base%.hal}
|
add_halfile_case_fields "halfile" "$halfile" "$m428" "$m429" "$m430"
|
||||||
add_case "halfile" "$halfile" "$m428" "$m429" "$m430"
|
add_halfile_case_fields "hal_file" "$halfile" "$m428" "$m429" "$m430"
|
||||||
add_case "halfile" "$hal_base" "$m428" "$m429" "$m430"
|
add_halfile_case_fields "HALFILE" "$halfile" "$m428" "$m429" "$m430"
|
||||||
add_case "halfile" "$hal_stem" "$m428" "$m429" "$m430"
|
}
|
||||||
add_case "hal_file" "$halfile" "$m428" "$m429" "$m430"
|
|
||||||
add_case "hal_file" "$hal_base" "$m428" "$m429" "$m430"
|
add_postgui_halfile_cases() {
|
||||||
add_case "hal_file" "$hal_stem" "$m428" "$m429" "$m430"
|
local halfile=$1
|
||||||
|
local m428=$2
|
||||||
|
local m429=$3
|
||||||
|
local m430=$4
|
||||||
|
[[ -n "$halfile" ]] || return 0
|
||||||
|
[[ "$halfile" == LIB:* ]] && return 0
|
||||||
|
|
||||||
|
add_halfile_case_fields "postguiHalFile" "$halfile" "$m428" "$m429" "$m430"
|
||||||
|
add_halfile_case_fields "postgui_halfile" "$halfile" "$m428" "$m429" "$m430"
|
||||||
|
add_halfile_case_fields "postgui_hal_file" "$halfile" "$m428" "$m429" "$m430"
|
||||||
|
add_halfile_case_fields "POSTGUI_HALFILE" "$halfile" "$m428" "$m429" "$m430"
|
||||||
}
|
}
|
||||||
|
|
||||||
while IFS=: read -r group path _note; do
|
while IFS=: read -r group path _note; do
|
||||||
@@ -248,23 +535,96 @@ while IFS=: read -r group path _note; do
|
|||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
m428=$(kinstype_for_mcode "$config" 428)
|
machine=
|
||||||
m429=$(kinstype_for_mcode "$config" 429)
|
kinematics=
|
||||||
m430=$(kinstype_for_mcode "$config" 430)
|
halfiles=
|
||||||
|
postgui_halfiles=
|
||||||
|
subroutine_paths=
|
||||||
|
remap_ngc_428=
|
||||||
|
remap_ngc_429=
|
||||||
|
remap_ngc_430=
|
||||||
|
while IFS=$'\t' read -r ini_key ini_value; do
|
||||||
|
case "$ini_key" in
|
||||||
|
machine)
|
||||||
|
[[ -n "$machine" ]] || machine=$ini_value
|
||||||
|
;;
|
||||||
|
kinematics)
|
||||||
|
[[ -n "$kinematics" ]] || kinematics=$ini_value
|
||||||
|
;;
|
||||||
|
halfile)
|
||||||
|
halfiles+="${halfiles:+$'\n'}$ini_value"
|
||||||
|
;;
|
||||||
|
postgui_halfile)
|
||||||
|
postgui_halfiles+="${postgui_halfiles:+$'\n'}$ini_value"
|
||||||
|
;;
|
||||||
|
subroutine_path)
|
||||||
|
subroutine_paths+="${subroutine_paths:+$'\n'}$ini_value"
|
||||||
|
;;
|
||||||
|
remap_428)
|
||||||
|
[[ -n "$remap_ngc_428" ]] || remap_ngc_428=$ini_value
|
||||||
|
;;
|
||||||
|
remap_429)
|
||||||
|
[[ -n "$remap_ngc_429" ]] || remap_ngc_429=$ini_value
|
||||||
|
;;
|
||||||
|
remap_430)
|
||||||
|
[[ -n "$remap_ngc_430" ]] || remap_ngc_430=$ini_value
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done < <(read_ini_switchkins_config "$config")
|
||||||
|
if [[ -z "$subroutine_paths" ]]; then
|
||||||
|
subroutine_paths="."
|
||||||
|
fi
|
||||||
|
|
||||||
|
remap_source_428=
|
||||||
|
remap_source_429=
|
||||||
|
remap_source_430=
|
||||||
|
if [[ -n "$remap_ngc_428" ]]; then
|
||||||
|
remap_source_428=$(remap_source_for_ngc "$config" "$remap_ngc_428" "$subroutine_paths" || true)
|
||||||
|
require_remap_source "$path" 428 "$remap_ngc_428" "$remap_source_428"
|
||||||
|
record_remap_source "$path" 428 "$remap_source_428"
|
||||||
|
resolved_remap_count=$((resolved_remap_count + 1))
|
||||||
|
fi
|
||||||
|
if [[ -n "$remap_ngc_429" ]]; then
|
||||||
|
remap_source_429=$(remap_source_for_ngc "$config" "$remap_ngc_429" "$subroutine_paths" || true)
|
||||||
|
require_remap_source "$path" 429 "$remap_ngc_429" "$remap_source_429"
|
||||||
|
record_remap_source "$path" 429 "$remap_source_429"
|
||||||
|
resolved_remap_count=$((resolved_remap_count + 1))
|
||||||
|
fi
|
||||||
|
if [[ -n "$remap_ngc_430" ]]; then
|
||||||
|
remap_source_430=$(remap_source_for_ngc "$config" "$remap_ngc_430" "$subroutine_paths" || true)
|
||||||
|
require_remap_source "$path" 430 "$remap_ngc_430" "$remap_source_430"
|
||||||
|
record_remap_source "$path" 430 "$remap_source_430"
|
||||||
|
resolved_remap_count=$((resolved_remap_count + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
|
m428=$(kinstype_for_remap_source "$remap_source_428")
|
||||||
|
m429=$(kinstype_for_remap_source "$remap_source_429")
|
||||||
|
m430=$(kinstype_for_remap_source "$remap_source_430")
|
||||||
|
[[ -z "$remap_ngc_428" ]] || require_kinstype "$path" 428 "$remap_source_428" "$m428"
|
||||||
|
[[ -z "$remap_ngc_429" ]] || require_kinstype "$path" 429 "$remap_source_429" "$m429"
|
||||||
|
[[ -z "$remap_ngc_430" ]] || require_kinstype "$path" 430 "$remap_source_430" "$m430"
|
||||||
[[ -n "$m428" ]] || m428=-1
|
[[ -n "$m428" ]] || m428=-1
|
||||||
[[ -n "$m429" ]] || m429=-1
|
[[ -n "$m429" ]] || m429=-1
|
||||||
[[ -n "$m430" ]] || m430=-1
|
[[ -n "$m430" ]] || m430=-1
|
||||||
|
processed_config_count=$((processed_config_count + 1))
|
||||||
|
record_config_source "$path" "$path" "INI config"
|
||||||
|
|
||||||
machine=$(ini_value "$config" "MACHINE")
|
|
||||||
kinematics=$(ini_value "$config" "KINEMATICS")
|
|
||||||
halfiles=$(ini_values "$config" "HALFILE")
|
|
||||||
config_dir=${path%/*}
|
config_dir=${path%/*}
|
||||||
config_base=${path##*/}
|
config_base=${path##*/}
|
||||||
config_stem=${config_base%.ini}
|
config_stem=${config_base%.ini}
|
||||||
|
|
||||||
add_case "config" "$path" "$m428" "$m429" "$m430"
|
add_case "config" "$path" "$m428" "$m429" "$m430"
|
||||||
|
add_case "configPath" "$path" "$m428" "$m429" "$m430"
|
||||||
|
add_case "config_path" "$path" "$m428" "$m429" "$m430"
|
||||||
|
add_case "ini" "$path" "$m428" "$m429" "$m430"
|
||||||
|
add_case "iniFile" "$path" "$m428" "$m429" "$m430"
|
||||||
|
add_case "iniFileName" "$path" "$m428" "$m429" "$m430"
|
||||||
|
add_case "ini_file" "$path" "$m428" "$m429" "$m430"
|
||||||
|
add_case "ini_file_name" "$path" "$m428" "$m429" "$m430"
|
||||||
|
add_case "INI_FILE_NAME" "$path" "$m428" "$m429" "$m430"
|
||||||
add_case "machine" "$machine" "$m428" "$m429" "$m430"
|
add_case "machine" "$machine" "$m428" "$m429" "$m430"
|
||||||
add_case "kinematics" "$kinematics" "$m428" "$m429" "$m430"
|
add_case "kinematics" "$kinematics" "$m428" "$m429" "$m430"
|
||||||
|
add_switchkins_cases "$config_dir" "$config_stem" "$machine" "$kinematics" "$m428" "$m429" "$m430"
|
||||||
|
|
||||||
add_alias "$path" "$m428" "$m429" "$m430"
|
add_alias "$path" "$m428" "$m429" "$m430"
|
||||||
add_alias "$config_dir" "$m428" "$m429" "$m430"
|
add_alias "$config_dir" "$m428" "$m429" "$m430"
|
||||||
@@ -273,39 +633,119 @@ while IFS=: read -r group path _note; do
|
|||||||
add_machine_aliases "$machine" "$m428" "$m429" "$m430"
|
add_machine_aliases "$machine" "$m428" "$m429" "$m430"
|
||||||
add_kinematics_aliases "$kinematics" "$m428" "$m429" "$m430"
|
add_kinematics_aliases "$kinematics" "$m428" "$m429" "$m430"
|
||||||
while IFS= read -r halfile; do
|
while IFS= read -r halfile; do
|
||||||
|
if hal_source=$(config_source_for_halfile "$config" "$halfile"); then
|
||||||
|
record_config_source "$path" "$hal_source" "HALFILE"
|
||||||
|
fi
|
||||||
add_halfile_cases "$halfile" "$m428" "$m429" "$m430"
|
add_halfile_cases "$halfile" "$m428" "$m429" "$m430"
|
||||||
add_halfile_aliases "$halfile" "$m428" "$m429" "$m430"
|
add_halfile_aliases "$halfile" "$m428" "$m429" "$m430"
|
||||||
done <<<"$halfiles"
|
done <<<"$halfiles"
|
||||||
|
while IFS= read -r halfile; do
|
||||||
|
if hal_source=$(config_source_for_halfile "$config" "$halfile"); then
|
||||||
|
record_config_source "$path" "$hal_source" "POSTGUI_HALFILE"
|
||||||
|
fi
|
||||||
|
add_postgui_halfile_cases "$halfile" "$m428" "$m429" "$m430"
|
||||||
|
add_halfile_aliases "$halfile" "$m428" "$m429" "$m430"
|
||||||
|
done <<<"$postgui_halfiles"
|
||||||
|
|
||||||
for mcode in 428 429 430; do
|
for remap_source in "$remap_source_428" "$remap_source_429" "$remap_source_430"; do
|
||||||
if remap_source=$(remap_source_for_mcode "$config" "$mcode"); then
|
if [[ -n "$remap_source" ]]; then
|
||||||
remap_dir=${remap_source%/*}
|
remap_dir=${remap_source%/*}
|
||||||
add_alias "$remap_dir" "$m428" "$m429" "$m430"
|
add_alias "$remap_dir" "$m428" "$m429" "$m430"
|
||||||
add_alias "${remap_dir%/*}" "$m428" "$m429" "$m430"
|
add_alias "${remap_dir%/*}" "$m428" "$m429" "$m430"
|
||||||
add_alias "${remap_dir%/remap_subs}" "$m428" "$m429" "$m430"
|
add_alias "${remap_dir%/remap_subs}" "$m428" "$m429" "$m430"
|
||||||
remap_parent=${remap_dir%/remap_subs}
|
remap_parent=${remap_dir%/remap_subs}
|
||||||
add_alias "${remap_parent##*/}" "$m428" "$m429" "$m430"
|
add_alias "${remap_parent##*/}" "$m428" "$m429" "$m430"
|
||||||
|
add_case "remap" "$remap_parent" "$m428" "$m429" "$m430"
|
||||||
|
add_case "remap" "${remap_parent##*/}" "$m428" "$m429" "$m430"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
done <"$manifest"
|
done <"$manifest"
|
||||||
|
|
||||||
if [[ "$mode" == table ]]; then
|
unique_config_source_count=${#used_config_sources[@]}
|
||||||
|
unique_resolved_remap_count=${#used_remap_sources[@]}
|
||||||
|
|
||||||
|
validate_generated_content() {
|
||||||
|
if ((manifest_config_count == 0)); then
|
||||||
|
echo "no LinuxCNC config sources listed in manifest: $manifest" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ((manifest_remap_count == 0)); then
|
||||||
|
echo "no LinuxCNC remap sources listed in manifest: $manifest" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ((processed_config_count == 0)); then
|
||||||
|
echo "no LinuxCNC switchkins remap configs found in manifest: $manifest" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ((resolved_remap_count == 0)); then
|
||||||
|
echo "no LinuxCNC M428/M429/M430 remap sources resolved from manifest: $manifest" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ((unique_resolved_remap_count == 0)); then
|
||||||
|
echo "no unique LinuxCNC M428/M429/M430 remap sources resolved from manifest: $manifest" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ((unique_config_source_count == 0)); then
|
||||||
|
echo "no unique LinuxCNC config sources used from manifest: $manifest" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ((unique_config_source_count != manifest_config_count)); then
|
||||||
|
echo "LinuxCNC config manifest coverage mismatch: used $unique_config_source_count of $manifest_config_count config sources" >&2
|
||||||
|
local config_source
|
||||||
|
for config_source in "${!manifest_config_sources[@]}"; do
|
||||||
|
if [[ -z "${used_config_sources[$config_source]:-}" ]]; then
|
||||||
|
echo "unused LinuxCNC config manifest source: $config_source" >&2
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ((unique_resolved_remap_count != manifest_remap_count)); then
|
||||||
|
echo "LinuxCNC remap manifest coverage mismatch: resolved $unique_resolved_remap_count of $manifest_remap_count remap sources" >&2
|
||||||
|
local remap_source
|
||||||
|
for remap_source in "${!manifest_remap_sources[@]}"; do
|
||||||
|
if [[ -z "${used_remap_sources[$remap_source]:-}" ]]; then
|
||||||
|
echo "unused LinuxCNC remap manifest source: $remap_source" >&2
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ((${#entries[@]} == 0)); then
|
||||||
|
echo "empty generated switchkins remap alias table" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ((${#case_entries[@]} == 0)); then
|
||||||
|
echo "empty generated switchkins remap config cases" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ((${#case_entries[@]} != ${#json_case_entries[@]})); then
|
||||||
|
echo "generated C++/JSON switchkins config case count mismatch: ${#case_entries[@]} != ${#json_case_entries[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_generated_content
|
||||||
|
|
||||||
|
write_table() {
|
||||||
cat <<'EOF'
|
cat <<'EOF'
|
||||||
// Generated by ./generate-linuxcnc-switchkins-remap-table.sh.
|
// Generated by ./generate-linuxcnc-switchkins-remap-table.sh.
|
||||||
// Source: LinuxCNC INI MACHINE/KINEMATICS/HALFILE/SUBROUTINE_PATH/REMAP entries and
|
// Source: LinuxCNC INI MACHINE/KINEMATICS/HALFILE/POSTGUI_HALFILE/SUBROUTINE_PATH/REMAP entries and
|
||||||
// adjacent remap_subs/{428,429,430}remap.ngc #<kinstype> assignments.
|
// adjacent remap_subs/{428,429,430}remap.ngc #<kinstype> assignments.
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
printf '%s\n' "${entries[@]}"
|
printf '%s\n' "${entries[@]}"
|
||||||
elif [[ "$mode" == config-cases ]]; then
|
}
|
||||||
|
|
||||||
|
write_config_cases() {
|
||||||
cat <<'EOF'
|
cat <<'EOF'
|
||||||
// Generated by ./generate-linuxcnc-switchkins-remap-table.sh --config-cases.
|
// Generated by ./generate-linuxcnc-switchkins-remap-table.sh --config-cases.
|
||||||
// Source: LinuxCNC INI config path, MACHINE, KINEMATICS, and non-LIB HALFILE entries
|
// Source: LinuxCNC INI config path, MACHINE, KINEMATICS, non-LIB HALFILE/POSTGUI_HALFILE,
|
||||||
// for M428/M429/M430 remap #<kinstype> assignments.
|
// and adjacent remap_subs entries for M428/M429/M430 #<kinstype> assignments.
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
printf '%s\n' "${case_entries[@]}"
|
printf '%s\n' "${case_entries[@]}"
|
||||||
elif [[ "$mode" == json-cases ]]; then
|
}
|
||||||
|
|
||||||
|
write_json_cases() {
|
||||||
printf '[\n'
|
printf '[\n'
|
||||||
for i in "${!json_case_entries[@]}"; do
|
for i in "${!json_case_entries[@]}"; do
|
||||||
suffix=,
|
suffix=,
|
||||||
@@ -315,6 +755,34 @@ elif [[ "$mode" == json-cases ]]; then
|
|||||||
printf ' %s%s\n' "${json_case_entries[$i]}" "$suffix"
|
printf ' %s%s\n' "${json_case_entries[$i]}" "$suffix"
|
||||||
done
|
done
|
||||||
printf ']\n'
|
printf ']\n'
|
||||||
|
}
|
||||||
|
|
||||||
|
write_generated_file() {
|
||||||
|
local path=$1
|
||||||
|
local label=$2
|
||||||
|
local writer=$3
|
||||||
|
if [[ -e "$path" && ! -f "$path" ]]; then
|
||||||
|
echo "cannot write generated $label: $path is not a file" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
"$writer" >"$path"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "$mode" == table ]]; then
|
||||||
|
write_table
|
||||||
|
elif [[ "$mode" == config-cases ]]; then
|
||||||
|
write_config_cases
|
||||||
|
elif [[ "$mode" == json-cases ]]; then
|
||||||
|
write_json_cases
|
||||||
|
elif [[ "$mode" == all ]]; then
|
||||||
|
if [[ -e "$all_output_dir" && ! -d "$all_output_dir" ]]; then
|
||||||
|
echo "switchkins remap output path is not a directory: $all_output_dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
mkdir -p "$all_output_dir"
|
||||||
|
write_generated_file "$all_output_dir/linuxcnc_switchkins_remap_table.inc" "table" write_table
|
||||||
|
write_generated_file "$all_output_dir/linuxcnc_switchkins_remap_config_cases.inc" "config cases" write_config_cases
|
||||||
|
write_generated_file "$all_output_dir/linuxcnc_switchkins_remap_config_cases.json" "JSON config cases" write_json_cases
|
||||||
else
|
else
|
||||||
echo "unknown switchkins remap table generator mode: $mode" >&2
|
echo "unknown switchkins remap table generator mode: $mode" >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
@@ -26,23 +26,121 @@ core:src/emc/kinematics/ugenserkins.c:user generic serial kinematics module
|
|||||||
core:src/emc/kinematics/userkfuncs.c:user kinematics helper functions used by M428/M429/M430 coverage
|
core:src/emc/kinematics/userkfuncs.c:user kinematics helper functions used by M428/M429/M430 coverage
|
||||||
core:src/emc/kinematics/xyzac-trt-kins.c:XYZAC table rotary tilting switchkins module
|
core:src/emc/kinematics/xyzac-trt-kins.c:XYZAC table rotary tilting switchkins module
|
||||||
core:src/emc/kinematics/xyzbc-trt-kins.c:XYZBC table rotary tilting switchkins module
|
core:src/emc/kinematics/xyzbc-trt-kins.c:XYZBC table rotary tilting switchkins module
|
||||||
|
core:src/libnml/posemath/_posemath.c:pose math support included by PUMA kinematics adapter
|
||||||
|
core:src/libnml/posemath/gomath.c:generic serial pose math support included by genser kinematics adapter
|
||||||
|
core:src/libnml/posemath/posemath.cc:LinuxCNC pose math C++ source tracked with kinematics posemath coverage
|
||||||
|
core:src/libnml/posemath/sincos.c:shared pose math sincos support included by PUMA/genser kinematics adapters
|
||||||
|
header:src/libnml/posemath/gomath.h:generic serial pose math declarations included by gomath source
|
||||||
|
header:src/libnml/posemath/gotypes.h:generic serial pose math type declarations included by gomath header
|
||||||
|
header:src/libnml/posemath/posemath.h:pose math declarations included by LinuxCNC kinematics adapters
|
||||||
|
header:src/libnml/posemath/sincos.h:pose math sincos declaration included by posemath sources
|
||||||
config:configs/sim/axis/vismach/5axis/bridgemill/5axis.ini:bridgemill MACHINE/KINEMATICS and M428/M429/M430 remap configuration source
|
config:configs/sim/axis/vismach/5axis/bridgemill/5axis.ini:bridgemill MACHINE/KINEMATICS and M428/M429/M430 remap configuration source
|
||||||
|
config:configs/sim/axis/vismach/5axis/bridgemill/5axisgui.hal:bridgemill HALFILE source referenced by M428/M429/M430 switchkins config
|
||||||
|
config:configs/sim/axis/vismach/5axis/bridgemill/5axis_postgui.hal:bridgemill POSTGUI_HALFILE source referenced by M428/M429/M430 switchkins config
|
||||||
config:configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini:XYZAB table dual rotary MACHINE/KINEMATICS and M428/M429 remap configuration source
|
config:configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini:XYZAB table dual rotary MACHINE/KINEMATICS and M428/M429 remap configuration source
|
||||||
|
config:configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr-postgui.hal:XYZAB table dual rotary POSTGUI_HALFILE source referenced by M428/M429 switchkins config
|
||||||
config:configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini:XYZAC TRT switchkins HAL parameter source used by M428 coverage
|
config:configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini:XYZAC TRT switchkins HAL parameter source used by M428 coverage
|
||||||
config:configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini:XYZBC TRT switchkins HAL parameter source used by M428 coverage
|
config:configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini:XYZBC TRT switchkins HAL parameter source used by M428 coverage
|
||||||
|
config:configs/sim/axis/vismach/5axis/table-rotary-tilting/switchkins_postgui.hal:XYZAC/XYZBC TRT POSTGUI_HALFILE source referenced by M428/M429/M430 switchkins config
|
||||||
config:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini:XYZACB TRSRN MACHINE/KINEMATICS and M428/M429/M430 remap configuration source
|
config:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini:XYZACB TRSRN MACHINE/KINEMATICS and M428/M429/M430 remap configuration source
|
||||||
|
config:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn_postgui.hal:XYZACB TRSRN POSTGUI_HALFILE source referenced by M428/M429/M430 switchkins config
|
||||||
config:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini:XYZBCA TRSRN MACHINE/KINEMATICS and M428/M429/M430 remap configuration source
|
config:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini:XYZBCA TRSRN MACHINE/KINEMATICS and M428/M429/M430 remap configuration source
|
||||||
|
config:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn_postgui.hal:XYZBCA TRSRN POSTGUI_HALFILE source referenced by M428/M429/M430 switchkins config
|
||||||
config:configs/sim/axis/vismach/hexapod-sim/hexapod.ini:hexapod MACHINE/KINEMATICS and M428/M429/M430 remap configuration source
|
config:configs/sim/axis/vismach/hexapod-sim/hexapod.ini:hexapod MACHINE/KINEMATICS and M428/M429/M430 remap configuration source
|
||||||
|
config:configs/sim/axis/vismach/hexapod-sim/kinematics.hal:hexapod HALFILE source referenced by M428/M429/M430 switchkins config
|
||||||
|
config:configs/sim/axis/vismach/hexapod-sim/hexapod_postgui.hal:hexapod POSTGUI_HALFILE source referenced by M428/M429/M430 switchkins config
|
||||||
config:configs/sim/axis/vismach/melfa-sim/melfa.ini:melfa MACHINE/KINEMATICS and M428/M429/M430 remap configuration source
|
config:configs/sim/axis/vismach/melfa-sim/melfa.ini:melfa MACHINE/KINEMATICS and M428/M429/M430 remap configuration source
|
||||||
|
config:configs/sim/axis/vismach/melfa-sim/melfa_dh.hal:melfa DH HALFILE source referenced by M428/M429/M430 switchkins config
|
||||||
|
config:configs/sim/axis/vismach/melfa-sim/melfa-postgui.hal:melfa POSTGUI_HALFILE source referenced by M428/M429/M430 switchkins config
|
||||||
config:configs/sim/axis/vismach/millturn/millturn.ini:millturn MACHINE/KINEMATICS and M428/M429 remap configuration source
|
config:configs/sim/axis/vismach/millturn/millturn.ini:millturn MACHINE/KINEMATICS and M428/M429 remap configuration source
|
||||||
|
config:configs/sim/axis/vismach/millturn/millturn.hal:millturn HALFILE source referenced by M428/M429 switchkins config
|
||||||
|
config:configs/sim/axis/vismach/millturn/millturn-postgui.hal:millturn POSTGUI_HALFILE source referenced by M428/M429 switchkins config
|
||||||
config:configs/sim/axis/vismach/puma/puma.ini:PUMA MACHINE/KINEMATICS and M428/M429/M430 remap configuration source
|
config:configs/sim/axis/vismach/puma/puma.ini:PUMA MACHINE/KINEMATICS and M428/M429/M430 remap configuration source
|
||||||
|
config:configs/sim/axis/vismach/puma/puma_dh.hal:PUMA DH HAL parameters referenced by PUMA switchkins configs
|
||||||
|
config:configs/sim/axis/vismach/puma/puma_postgui.hal:PUMA POSTGUI_HALFILE source referenced by M428/M429/M430 switchkins config
|
||||||
config:configs/sim/axis/vismach/puma/puma_cube.ini:PUMA cube MACHINE/KINEMATICS and M428/M429/M430 remap configuration source
|
config:configs/sim/axis/vismach/puma/puma_cube.ini:PUMA cube MACHINE/KINEMATICS and M428/M429/M430 remap configuration source
|
||||||
config:configs/sim/axis/vismach/puma/puma560.ini:PUMA 560 MACHINE/KINEMATICS and M428/M429/M430 remap configuration source
|
config:configs/sim/axis/vismach/puma/puma560.ini:PUMA 560 MACHINE/KINEMATICS and M428/M429/M430 remap configuration source
|
||||||
config:configs/sim/axis/vismach/puma/puma560_dh.hal:PUMA 560 genserkins DH HAL parameters used by M428 coverage
|
config:configs/sim/axis/vismach/puma/puma560_dh.hal:PUMA 560 genserkins DH HAL parameters used by M428 coverage
|
||||||
|
config:configs/sim/axis/vismach/puma/puma560_postgui.hal:PUMA 560 POSTGUI_HALFILE source referenced by M428/M429/M430 switchkins config
|
||||||
config:configs/sim/axis/vismach/puma/puma560_uvw.ini:PUMA 560 UVW MACHINE/KINEMATICS and M428/M429/M430 remap configuration source
|
config:configs/sim/axis/vismach/puma/puma560_uvw.ini:PUMA 560 UVW MACHINE/KINEMATICS and M428/M429/M430 remap configuration source
|
||||||
config:configs/sim/axis/vismach/scara/scara.ini:AXIS SCARA MACHINE/KINEMATICS and M428/M429/M430 remap configuration source
|
config:configs/sim/axis/vismach/scara/scara.ini:AXIS SCARA MACHINE/KINEMATICS and M428/M429/M430 remap configuration source
|
||||||
|
config:configs/sim/axis/vismach/scara/scara_postgui.hal:AXIS SCARA POSTGUI_HALFILE source referenced by M428/M429/M430 switchkins config
|
||||||
config:configs/sim/qtaxis/non-trivial/scara/scara.ini:QtAxis SCARA MACHINE/KINEMATICS and M428/M429/M430 remap configuration source
|
config:configs/sim/qtaxis/non-trivial/scara/scara.ini:QtAxis SCARA MACHINE/KINEMATICS and M428/M429/M430 remap configuration source
|
||||||
config:configs/sim/qtvcp_screens/non-trivial/scara/scara.ini:QtVCP SCARA MACHINE/KINEMATICS and M428/M429/M430 remap configuration source
|
config:configs/sim/qtvcp_screens/non-trivial/scara/scara.ini:QtVCP SCARA MACHINE/KINEMATICS and M428/M429/M430 remap configuration source
|
||||||
|
asset:configs/sim/axis/vismach/5axis/bridgemill/5axis.xml:bridgemill PYVCP asset referenced by M428/M429/M430 switchkins INI
|
||||||
|
asset:configs/sim/axis/vismach/5axis/bridgemill/5axisgui.ngc:bridgemill OPEN_FILE asset referenced by M428/M429/M430 switchkins INI
|
||||||
|
asset:configs/sim/axis/vismach/5axis/bridgemill/README:bridgemill configuration source notes adjacent to INI coverage
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-dual-rotary/demos/xyzab-tdr-demo.ngc:XYZAB table dual rotary OPEN_FILE demo asset referenced by M428/M429 switchkins INI
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-dual-rotary/README:XYZAB table dual rotary configuration source notes adjacent to INI coverage
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.xml:XYZAB table dual rotary PYVCP asset referenced by M428/M429 switchkins INI
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzac_switchkins.ngc:XYZAC TRT OPEN_FILE demo asset referenced by M428/M429/M430 switchkins INI
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzbc_switchkins.ngc:XYZBC TRT OPEN_FILE demo asset referenced by M428/M429/M430 switchkins INI
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary-tilting/README:XYZAC/XYZBC TRT configuration source notes adjacent to INI coverage
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.xml:XYZAC TRT PYVCP asset referenced by M428/M429/M430 switchkins INI
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.xml:XYZBC TRT PYVCP asset referenced by M428/M429/M430 switchkins INI
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition.ngc:TRSRN OPEN_FILE demo asset referenced by XYZACB/XYZBCA M428/M429/M430 switchkins INI files
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/remap.py:TRSRN TWP Python remap source referenced by XYZACB/XYZBCA REMAP python entries
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/toplevel.py:TRSRN TWP Python TOPLEVEL source referenced by XYZACB/XYZBCA INI files
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/twp-helper-comp.py:TRSRN TWP HAL helper source referenced by XYZACB/XYZBCA HALCMD loadusr entries
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/util.py:TRSRN TWP Python utility source imported by remap.py
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g531remap.ngc:TRSRN G53.1 remap wrapper referenced by XYZACB/XYZBCA INI REMAP entries
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g533remap.ngc:TRSRN G53.3 remap wrapper referenced by XYZACB/XYZBCA INI REMAP entries
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g536remap.ngc:TRSRN G53.6 remap wrapper referenced by XYZACB/XYZBCA INI REMAP entries
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g69remap.ngc:TRSRN G69 remap wrapper referenced by XYZACB/XYZBCA INI REMAP entries
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/on_abort_no_twp_reset.ngc:TRSRN ON_ABORT source referenced by XYZACB INI
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/on_abort_with_twp_reset.ngc:TRSRN ON_ABORT source referenced by XYZBCA INI
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/vismach/twp_vismach.py:TRSRN shared vismach source imported by XYZACB/XYZBCA GUI files
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/vismach/xyzacb-trsrn-gui.py:XYZACB TRSRN vismach GUI source referenced by HALCMD loadusr
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/vismach/xyzbca-trsrn-gui.py:XYZBCA TRSRN vismach GUI source referenced by HALCMD loadusr
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/README:XYZACB TRSRN TWP configuration source notes adjacent to INI coverage
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/pyvcp_embed_tab_tcp.xml:XYZACB TRSRN embedded PyVCP TCP asset referenced by EMBED_TAB_COMMAND
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/work_piece_1.stl:XYZACB TRSRN workpiece STL asset referenced by vismach GUI source
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.xml:XYZACB TRSRN PYVCP asset referenced by M428/M429/M430 switchkins INI
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/README:XYZBCA TRSRN TWP configuration source notes adjacent to INI coverage
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/pyvcp_embed_tab_tcp.xml:XYZBCA TRSRN embedded PyVCP TCP asset referenced by EMBED_TAB_COMMAND
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/work_piece_1.stl:XYZBCA TRSRN workpiece STL asset referenced by vismach GUI source
|
||||||
|
asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.xml:XYZBCA TRSRN PYVCP asset referenced by M428/M429/M430 switchkins INI
|
||||||
|
asset:configs/sim/axis/vismach/hexapod-sim/README:hexapod configuration source notes adjacent to INI coverage
|
||||||
|
asset:configs/sim/axis/vismach/hexapod-sim/hexapod.xml:hexapod PYVCP asset referenced by M428/M429/M430 switchkins INI
|
||||||
|
asset:configs/sim/axis/vismach/melfa-sim/example.ngc:melfa OPEN_FILE demo asset referenced by M428/M429/M430 switchkins INI
|
||||||
|
asset:configs/sim/axis/vismach/melfa-sim/README:melfa configuration source notes adjacent to INI coverage
|
||||||
|
asset:configs/sim/axis/vismach/melfa-sim/melfa.xml:melfa PYVCP asset referenced by M428/M429/M430 switchkins INI
|
||||||
|
asset:configs/sim/axis/vismach/millturn/example.ngc:millturn OPEN_FILE demo asset referenced by M428/M429 switchkins INI
|
||||||
|
asset:configs/sim/axis/vismach/millturn/README:millturn configuration source notes adjacent to INI coverage
|
||||||
|
asset:configs/sim/axis/vismach/millturn/millturn.xml:millturn PYVCP asset referenced by M428/M429 switchkins INI
|
||||||
|
asset:configs/sim/axis/vismach/puma/README:PUMA configuration source notes adjacent to INI coverage
|
||||||
|
asset:configs/sim/axis/vismach/puma/puma.xml:PUMA PYVCP asset referenced by M428/M429/M430 switchkins INI
|
||||||
|
asset:configs/sim/axis/vismach/puma/puma560.xml:PUMA 560 PYVCP asset referenced by M428/M429/M430 switchkins INI files
|
||||||
|
asset:configs/sim/axis/vismach/puma/puma_cube.ngc:PUMA cube OPEN_FILE demo asset referenced by M428/M429/M430 switchkins INI
|
||||||
|
asset:configs/sim/axis/vismach/scara/README:AXIS SCARA configuration source notes adjacent to INI coverage
|
||||||
|
asset:configs/sim/axis/vismach/scara/scara.xml:AXIS SCARA PYVCP asset referenced by M428/M429/M430 switchkins INI
|
||||||
|
asset:src/hal/user_comps/vismach/5axisgui.py:bridgemill vismach GUI source referenced by HALFILE loadusr
|
||||||
|
asset:src/hal/user_comps/vismach/hexagui.py:hexapod vismach GUI source referenced by INI HALCMD loadusr
|
||||||
|
asset:src/hal/user_comps/vismach/melfagui.py:melfa vismach GUI source referenced by INI HALCMD loadusr
|
||||||
|
asset:src/hal/user_comps/vismach/millturngui.py:millturn vismach GUI source referenced by HALFILE loadusr
|
||||||
|
asset:src/hal/user_comps/vismach/puma560gui.py:PUMA 560 vismach GUI source referenced by INI HALCMD loadusr
|
||||||
|
asset:src/hal/user_comps/vismach/pumagui.py:PUMA vismach GUI source referenced by INI HALCMD loadusr
|
||||||
|
asset:src/hal/user_comps/vismach/scaragui.py:SCARA vismach GUI source referenced by INI HALCMD loadusr
|
||||||
|
asset:src/hal/user_comps/vismach/xyzab-tdr-gui.py:XYZAB table dual rotary vismach GUI source referenced by INI HALCMD loadusr
|
||||||
|
asset:src/hal/user_comps/vismach/xyzac-trt-gui.py:XYZAC TRT vismach GUI source referenced by INI HALCMD loadusr
|
||||||
|
asset:src/hal/user_comps/vismach/xyzbc-trt-gui.py:XYZBC TRT vismach GUI source referenced by INI HALCMD loadusr
|
||||||
|
asset:share/qtvcp/panels/vismach_scara/vismach_scara.ui:QtVCP SCARA panel UI source referenced by EMBED_TAB_COMMAND
|
||||||
|
asset:share/qtvcp/panels/vismach_scara/vismach_scara_handler.py:QtVCP SCARA panel handler source referenced by EMBED_TAB_COMMAND
|
||||||
|
asset:lib/python/qtvcp/lib/qt_vismach/README.txt:QtVCP vismach library source notes adjacent to SCARA panel coverage
|
||||||
|
asset:lib/python/qtvcp/lib/qt_vismach/__init__.py:QtVCP vismach package source used by SCARA panel
|
||||||
|
asset:lib/python/qtvcp/lib/qt_vismach/primitives.py:QtVCP vismach primitives source used by SCARA panel
|
||||||
|
asset:lib/python/qtvcp/lib/qt_vismach/qt_vismach.py:QtVCP vismach base window source used by SCARA panel
|
||||||
|
asset:lib/python/qtvcp/lib/qt_vismach/scara.py:QtVCP SCARA vismach source imported by vismach_scara_handler.py
|
||||||
|
tooldata:configs/sim/axis/vismach/5axis/bridgemill/5axis.tbl:bridgemill tool table source referenced by INI TOOL_TABLE
|
||||||
|
tooldata:configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.tbl:XYZAB table dual rotary tool table source referenced by INI TOOL_TABLE
|
||||||
|
tooldata:configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.tbl:XYZAC TRT tool table source referenced by INI TOOL_TABLE
|
||||||
|
tooldata:configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.tbl:XYZBC TRT tool table source referenced by INI TOOL_TABLE
|
||||||
|
tooldata:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.tbl:XYZACB TRSRN tool table source referenced by INI TOOL_TABLE
|
||||||
|
tooldata:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.tbl:XYZBCA TRSRN tool table source referenced by INI TOOL_TABLE
|
||||||
|
tooldata:configs/sim/axis/vismach/melfa-sim/melfa.tbl:melfa tool table source referenced by INI TOOL_TABLE
|
||||||
|
tooldata:configs/sim/axis/vismach/millturn/millturn.tbl:millturn tool table source referenced by INI TOOL_TABLE
|
||||||
|
tooldata:configs/sim/axis/vismach/puma/puma.tbl:PUMA tool table source referenced by PUMA switchkins INI TOOL_TABLE entries
|
||||||
|
tooldata:configs/sim/qtaxis/non-trivial/scara/scara.tbl:QtAxis SCARA tool table source referenced by INI TOOL_TABLE
|
||||||
remap:configs/sim/axis/vismach/5axis/bridgemill/remap_subs/428remap.ngc:LinuxCNC M428 remap source selecting bridgemill kinstype 0
|
remap:configs/sim/axis/vismach/5axis/bridgemill/remap_subs/428remap.ngc:LinuxCNC M428 remap source selecting bridgemill kinstype 0
|
||||||
remap:configs/sim/axis/vismach/5axis/bridgemill/remap_subs/429remap.ngc:LinuxCNC M429 remap source selecting bridgemill kinstype 1
|
remap:configs/sim/axis/vismach/5axis/bridgemill/remap_subs/429remap.ngc:LinuxCNC M429 remap source selecting bridgemill kinstype 1
|
||||||
remap:configs/sim/axis/vismach/5axis/bridgemill/remap_subs/430remap.ngc:LinuxCNC M430 remap source selecting bridgemill kinstype 2
|
remap:configs/sim/axis/vismach/5axis/bridgemill/remap_subs/430remap.ngc:LinuxCNC M430 remap source selecting bridgemill kinstype 2
|
||||||
@@ -74,11 +172,13 @@ remap:configs/sim/qtaxis/non-trivial/scara/remap_subs/430remap.ngc:LinuxCNC M430
|
|||||||
remap:configs/sim/qtvcp_screens/non-trivial/scara/remap_subs/428remap.ngc:LinuxCNC M428 remap source selecting qtvcp scara kinstype 0
|
remap:configs/sim/qtvcp_screens/non-trivial/scara/remap_subs/428remap.ngc:LinuxCNC M428 remap source selecting qtvcp scara kinstype 0
|
||||||
remap:configs/sim/qtvcp_screens/non-trivial/scara/remap_subs/429remap.ngc:LinuxCNC M429 remap source selecting qtvcp scara kinstype 1
|
remap:configs/sim/qtvcp_screens/non-trivial/scara/remap_subs/429remap.ngc:LinuxCNC M429 remap source selecting qtvcp scara kinstype 1
|
||||||
remap:configs/sim/qtvcp_screens/non-trivial/scara/remap_subs/430remap.ngc:LinuxCNC M430 remap source selecting qtvcp scara kinstype 2
|
remap:configs/sim/qtvcp_screens/non-trivial/scara/remap_subs/430remap.ngc:LinuxCNC M430 remap source selecting qtvcp scara kinstype 2
|
||||||
|
component:src/hal/components/millturn.comp:millturn switchable kinematics component source referenced by millturn INI KINEMATICS
|
||||||
component:src/hal/components/xyzab_tdr_kins.comp:XYZAB table dual rotary switchkins component source used by M428/M429/M430 coverage
|
component:src/hal/components/xyzab_tdr_kins.comp:XYZAB table dual rotary switchkins component source used by M428/M429/M430 coverage
|
||||||
component:src/hal/components/xyzacb_trsrn.comp:XYZACB transformation component source adjacent to LinuxCNC RTCP kinematics coverage
|
component:src/hal/components/xyzacb_trsrn.comp:XYZACB transformation component source adjacent to LinuxCNC RTCP kinematics coverage
|
||||||
component:src/hal/components/xyzbca_trsrn.comp:XYZBCA transformation component source adjacent to LinuxCNC RTCP kinematics coverage
|
component:src/hal/components/xyzbca_trsrn.comp:XYZBCA transformation component source adjacent to LinuxCNC RTCP kinematics coverage
|
||||||
component:src/hal/components/matrixkins.comp:matrix kinematics component source for LinuxCNC kinematics completeness
|
component:src/hal/components/matrixkins.comp:matrix kinematics component source for LinuxCNC kinematics completeness
|
||||||
component:src/hal/components/userkins.comp:user kinematics component template source for LinuxCNC switchkins/user kinematics coverage
|
component:src/hal/components/userkins.comp:user kinematics component template source for LinuxCNC switchkins/user kinematics coverage
|
||||||
|
generated:src/objects/hal/components/millturn.c:halcompile-generated millturn switchable kinematics C source used by millturn INI KINEMATICS
|
||||||
generated:src/objects/hal/components/xyzab_tdr_kins.c:halcompile-generated XYZAB table dual rotary switchkins C source used by the adapter
|
generated:src/objects/hal/components/xyzab_tdr_kins.c:halcompile-generated XYZAB table dual rotary switchkins C source used by the adapter
|
||||||
generated:src/objects/hal/components/xyzacb_trsrn.c:halcompile-generated XYZACB transformation C source
|
generated:src/objects/hal/components/xyzacb_trsrn.c:halcompile-generated XYZACB transformation C source
|
||||||
generated:src/objects/hal/components/xyzbca_trsrn.c:halcompile-generated XYZBCA transformation C source
|
generated:src/objects/hal/components/xyzbca_trsrn.c:halcompile-generated XYZBCA transformation C source
|
||||||
@@ -95,4 +195,22 @@ header:src/emc/kinematics/rotarydeltakins-common.h:rotary delta shared header
|
|||||||
header:src/emc/kinematics/switchkins.h:switchable kinematics header
|
header:src/emc/kinematics/switchkins.h:switchable kinematics header
|
||||||
metadata:src/emc/kinematics/Submakefile:LinuxCNC kinematics make build metadata
|
metadata:src/emc/kinematics/Submakefile:LinuxCNC kinematics make build metadata
|
||||||
metadata:src/emc/kinematics/meson.build:LinuxCNC kinematics meson build metadata
|
metadata:src/emc/kinematics/meson.build:LinuxCNC kinematics meson build metadata
|
||||||
|
metadata:src/libnml/posemath/Submakefile:LinuxCNC posemath make build metadata used by kinematics pose math coverage
|
||||||
|
metadata:src/libnml/posemath/meson.build:LinuxCNC posemath meson build metadata used by kinematics pose math coverage
|
||||||
|
metadata:src/hal/user_comps/vismach/Submakefile:LinuxCNC vismach user component build metadata for loadusr GUI sources
|
||||||
|
metadata:docs/src/man/man1/5axisgui.1.adoc:LinuxCNC bridgemill vismach GUI command documentation source
|
||||||
|
metadata:docs/src/man/man1/hexagui.1.adoc:LinuxCNC hexapod vismach GUI command documentation source
|
||||||
|
metadata:docs/src/man/man1/melfagui.1.adoc:LinuxCNC melfa vismach GUI command documentation source
|
||||||
|
metadata:docs/src/man/man1/puma560gui.1.adoc:LinuxCNC PUMA 560 vismach GUI command documentation source
|
||||||
|
metadata:docs/src/man/man1/pumagui.1.adoc:LinuxCNC PUMA vismach GUI command documentation source
|
||||||
|
metadata:docs/src/man/man1/scaragui.1.adoc:LinuxCNC SCARA vismach GUI command documentation source
|
||||||
|
metadata:docs/src/man/man1/xyzab-tdr-gui.1.adoc:LinuxCNC XYZAB table dual rotary vismach GUI command documentation source
|
||||||
|
metadata:docs/src/man/man1/xyzac-trt-gui.1.adoc:LinuxCNC XYZAC TRT vismach GUI command documentation source
|
||||||
|
metadata:docs/src/man/man1/xyzbc-trt-gui.1.adoc:LinuxCNC XYZBC TRT vismach GUI command documentation source
|
||||||
|
metadata:src/objects/hal/components/matrixkins.mak:halcompile-generated matrixkins make metadata adjacent to generated kinematics source
|
||||||
|
metadata:src/objects/hal/components/millturn.mak:halcompile-generated millturn make metadata adjacent to generated kinematics source
|
||||||
|
metadata:src/objects/hal/components/userkins.mak:halcompile-generated userkins make metadata adjacent to generated kinematics source
|
||||||
|
metadata:src/objects/hal/components/xyzab_tdr_kins.mak:halcompile-generated XYZAB table dual rotary make metadata adjacent to generated kinematics source
|
||||||
|
metadata:src/objects/hal/components/xyzacb_trsrn.mak:halcompile-generated XYZACB TRSRN make metadata adjacent to generated kinematics source
|
||||||
|
metadata:src/objects/hal/components/xyzbca_trsrn.mak:halcompile-generated XYZBCA TRSRN make metadata adjacent to generated kinematics source
|
||||||
asset:src/emc/kinematics/blend.fig:LinuxCNC kinematics reference asset tracked for full directory coverage
|
asset:src/emc/kinematics/blend.fig:LinuxCNC kinematics reference asset tracked for full directory coverage
|
||||||
|
|||||||
@@ -11,6 +11,6 @@ output_mode=${3:-relative}
|
|||||||
"$manifest" \
|
"$manifest" \
|
||||||
"$filter_group" \
|
"$filter_group" \
|
||||||
"$output_mode" \
|
"$output_mode" \
|
||||||
core,config,remap,component,generated,header,metadata,asset \
|
core,config,remap,component,generated,header,metadata,asset,tooldata \
|
||||||
all \
|
all \
|
||||||
"missing kinematics manifest source"
|
"missing kinematics manifest source"
|
||||||
|
|||||||
@@ -23,6 +23,14 @@ missing_bridge_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_bridge_smoke
|
|||||||
missing_support_object_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_linuxcnc_support_object.XXXXXX.log")
|
missing_support_object_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_linuxcnc_support_object.XXXXXX.log")
|
||||||
missing_source_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_syntax_missing_source_manifest.XXXXXX.txt")
|
missing_source_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_syntax_missing_source_manifest.XXXXXX.txt")
|
||||||
missing_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_syntax_missing_source.XXXXXX.log")
|
missing_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_syntax_missing_source.XXXXXX.log")
|
||||||
|
bad_line_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_bad_manifest_line.XXXXXX.txt")
|
||||||
|
bad_line_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_bad_manifest_line.XXXXXX.log")
|
||||||
|
absolute_source_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_absolute_manifest_source.XXXXXX.txt")
|
||||||
|
absolute_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_absolute_manifest_source.XXXXXX.log")
|
||||||
|
invalid_build_jobs_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_invalid_build_jobs.XXXXXX.log")
|
||||||
|
invalid_build_dir_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_invalid_build_dir.XXXXXX.log")
|
||||||
|
empty_build_dir_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_empty_build_dir.XXXXXX.log")
|
||||||
|
whitespace_build_dir_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_whitespace_build_dir.XXXXXX.log")
|
||||||
|
|
||||||
cleanup_native_test_temps() {
|
cleanup_native_test_temps() {
|
||||||
rm -f \
|
rm -f \
|
||||||
@@ -41,7 +49,15 @@ cleanup_native_test_temps() {
|
|||||||
"$missing_bridge_source_log" \
|
"$missing_bridge_source_log" \
|
||||||
"$missing_support_object_log" \
|
"$missing_support_object_log" \
|
||||||
"$missing_source_manifest" \
|
"$missing_source_manifest" \
|
||||||
"$missing_source_log"
|
"$missing_source_log" \
|
||||||
|
"$bad_line_manifest" \
|
||||||
|
"$bad_line_log" \
|
||||||
|
"$absolute_source_manifest" \
|
||||||
|
"$absolute_source_log" \
|
||||||
|
"$invalid_build_jobs_log" \
|
||||||
|
"$invalid_build_dir_log" \
|
||||||
|
"$empty_build_dir_log" \
|
||||||
|
"$whitespace_build_dir_log"
|
||||||
}
|
}
|
||||||
trap cleanup_native_test_temps EXIT
|
trap cleanup_native_test_temps EXIT
|
||||||
|
|
||||||
@@ -67,12 +83,40 @@ for forbidden_manual_cleanup in \
|
|||||||
'rm -f "$missing_core_source_log"' \
|
'rm -f "$missing_core_source_log"' \
|
||||||
'rm -f "$missing_bridge_source_log"' \
|
'rm -f "$missing_bridge_source_log"' \
|
||||||
'rm -f "$missing_support_object_log"' \
|
'rm -f "$missing_support_object_log"' \
|
||||||
'rm -f "$missing_source_manifest" "$missing_source_log"'; do
|
'rm -f "$missing_source_manifest" "$missing_source_log"' \
|
||||||
|
'rm -f "$bad_line_manifest" "$bad_line_log"' \
|
||||||
|
'rm -f "$absolute_source_manifest" "$absolute_source_log"' \
|
||||||
|
'rm -f "$invalid_build_jobs_log"' \
|
||||||
|
'rm -f "$invalid_build_dir_log"' \
|
||||||
|
'rm -f "$empty_build_dir_log"' \
|
||||||
|
'rm -f "$whitespace_build_dir_log"'; do
|
||||||
if grep -Fx "$forbidden_manual_cleanup" test-all-native.sh >/dev/null; then
|
if grep -Fx "$forbidden_manual_cleanup" test-all-native.sh >/dev/null; then
|
||||||
echo "native aggregate test still relies on manual cleanup: $forbidden_manual_cleanup" >&2
|
echo "native aggregate test still relies on manual cleanup: $forbidden_manual_cleanup" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
printf 'core:src/emc/rs274ngc/interp_arc.cc\n' >"$bad_line_manifest"
|
||||||
|
if ./check-linuxcnc-inputs.sh "$bad_line_manifest" >"$bad_line_log" 2>&1; then
|
||||||
|
echo "check-linuxcnc-inputs accepted a manifest line without a note" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "bad manifest line 1: core:src/emc/rs274ngc/interp_arc.cc" "$bad_line_log" >/dev/null; then
|
||||||
|
echo "check-linuxcnc-inputs did not report malformed manifest lines clearly" >&2
|
||||||
|
sed -n '1,20p' "$bad_line_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf 'core:/src/emc/rs274ngc/interp_arc.cc:absolute source path\n' >"$absolute_source_manifest"
|
||||||
|
if ./check-linuxcnc-inputs.sh "$absolute_source_manifest" >"$absolute_source_log" 2>&1; then
|
||||||
|
echo "check-linuxcnc-inputs accepted an absolute manifest source path" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "manifest source must be LinuxCNC-root relative: /src/emc/rs274ngc/interp_arc.cc" "$absolute_source_log" >/dev/null; then
|
||||||
|
echo "check-linuxcnc-inputs did not report absolute manifest paths clearly" >&2
|
||||||
|
sed -n '1,20p' "$absolute_source_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
grep -F 'exec 9>"${TMPDIR:-/tmp}/cnc_sim_rs274_source_link.lock"' test-linuxcnc-source-link.sh >/dev/null
|
grep -F 'exec 9>"${TMPDIR:-/tmp}/cnc_sim_rs274_source_link.lock"' test-linuxcnc-source-link.sh >/dev/null
|
||||||
grep -F "flock 9" test-linuxcnc-source-link.sh >/dev/null
|
grep -F "flock 9" test-linuxcnc-source-link.sh >/dev/null
|
||||||
grep -F "missing LinuxCNC support object" list-linuxcnc-source-support-objects.sh >/dev/null
|
grep -F "missing LinuxCNC support object" list-linuxcnc-source-support-objects.sh >/dev/null
|
||||||
@@ -86,7 +130,6 @@ grep -F 'exec 9>"${TMPDIR:-/tmp}/cnc_sim_linuxcnc_rs274_native.lock"' test-linux
|
|||||||
grep -F "flock 9" test-linuxcnc-rs274-native.sh >/dev/null
|
grep -F "flock 9" test-linuxcnc-rs274-native.sh >/dev/null
|
||||||
for build_dir_probe_script in \
|
for build_dir_probe_script in \
|
||||||
test-linuxcnc-source-objects.sh \
|
test-linuxcnc-source-objects.sh \
|
||||||
test-linuxcnc-source-link.sh \
|
|
||||||
test-linuxcnc-bridge-native.sh \
|
test-linuxcnc-bridge-native.sh \
|
||||||
test-linuxcnc-rs274-native.sh \
|
test-linuxcnc-rs274-native.sh \
|
||||||
test-linuxcnc-api-native.sh; do
|
test-linuxcnc-api-native.sh; do
|
||||||
@@ -99,11 +142,261 @@ for build_dir_probe_script in \
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
if ! awk '
|
||||||
|
/cleanup_wasm_cmake_probe_temps\(\)/ { in_cleanup = 1 }
|
||||||
|
in_cleanup && index($0, "\"$build_dir\"") { found_build_dir = 1 }
|
||||||
|
in_cleanup && /^}/ { in_cleanup = 0 }
|
||||||
|
END { exit found_build_dir }
|
||||||
|
' test-linuxcnc-wasm-cmake-safe-probe.sh; then
|
||||||
|
echo "wasm CMake safe probe cleanup no longer removes its persistent build dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -F 'build_dir=${CNC_SIM_SOURCE_LINK_BUILD_DIR-build/source-link-test}' test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_SOURCE_LINK_BUILD_DIR must not be empty' test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_SOURCE_LINK_BUILD_DIR must not contain whitespace: $build_dir' test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_SOURCE_LINK_BUILD_DIR must not be the filesystem root' test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'exec 9>"${TMPDIR:-/tmp}/cnc_sim_rs274_source_link.lock"' test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'source_link_signature="$build_dir/source-link-build.signature"' test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'if [[ ! -f "$source_link_signature" ]] || ! cmp -s "$source_link_signature" "$source_link_next_signature"; then' test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'printf '\''PWD=%s\n'\'' "$PWD"' test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'printf '\''LINUXCNC_ROOT=%s\n'\'' "$(cd "$linuxcnc_root" && pwd)"' test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'printf '\''%s: %s\n'\'' "$build_dir/linuxcnc_${linuxcnc_index}.o" "$source"' test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'printf '\''%s: %s\n'\'' "$build_dir/project_${project_index}.o" "$source"' test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'printf '\''\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s.d -c %s -o %s\n\n'\''' test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'build_dir=${CNC_SIM_NATIVE_BUILD_DIR-build/native-test}' test-native.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_NATIVE_BUILD_DIR must not be empty' test-native.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_NATIVE_BUILD_DIR must not contain whitespace: $build_dir' test-native.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_NATIVE_BUILD_DIR must not be the filesystem root' test-native.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' test-native.sh >/dev/null
|
||||||
|
grep -F 'exec 9>"$build_dir/native-test.lock"' test-native.sh >/dev/null
|
||||||
|
grep -F 'native_signature="$build_dir/native-build.signature"' test-native.sh >/dev/null
|
||||||
|
grep -F 'if [[ ! -f "$native_signature" ]] || ! cmp -s "$native_signature" "$native_next_signature"; then' test-native.sh >/dev/null
|
||||||
|
grep -F 'printf '\''PWD=%s\n'\'' "$PWD"' test-native.sh >/dev/null
|
||||||
|
grep -F 'printf '\''%s: %s\n'\'' "${core_objects[$core_index]}" "$source"' test-native.sh >/dev/null
|
||||||
|
grep -F 'printf '\''\t@$(CXX) $(SMOKE_CXXFLAGS) -MMD -MP -MF %s.d -c %s -o %s\n\n'\''' test-native.sh >/dev/null
|
||||||
|
grep -F 'build_dir=${CNC_SIM_SOURCE_SYNTAX_BUILD_DIR-build/source-syntax-test}' test-linuxcnc-source-syntax.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_SOURCE_SYNTAX_BUILD_DIR must not be empty' test-linuxcnc-source-syntax.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_SOURCE_SYNTAX_BUILD_DIR must not contain whitespace: $build_dir' test-linuxcnc-source-syntax.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_SOURCE_SYNTAX_BUILD_DIR must not be the filesystem root' test-linuxcnc-source-syntax.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' test-linuxcnc-source-syntax.sh >/dev/null
|
||||||
|
grep -F 'exec 9>"$build_dir/source-syntax.lock"' test-linuxcnc-source-syntax.sh >/dev/null
|
||||||
|
grep -F 'source_list="$build_dir/linuxcnc_source_syntax_sources.txt"' test-linuxcnc-source-syntax.sh >/dev/null
|
||||||
|
grep -F 'source_syntax_signature="$build_dir/source-syntax.signature"' test-linuxcnc-source-syntax.sh >/dev/null
|
||||||
|
grep -F 'if [[ ! -f "$source_syntax_signature" ]] || ! cmp -s "$source_syntax_signature" "$source_syntax_next_signature"; then' test-linuxcnc-source-syntax.sh >/dev/null
|
||||||
|
grep -F 'printf '\''LINUXCNC_ROOT=%s\n'\'' "$(cd "$linuxcnc_root" && pwd)"' test-linuxcnc-source-syntax.sh >/dev/null
|
||||||
|
grep -F 'printf '\''MANIFEST=%s\n'\'' "$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest")"' test-linuxcnc-source-syntax.sh >/dev/null
|
||||||
|
grep -F 'printf '\''BUILD_JOBS=%s\n'\'' "$build_jobs"' test-linuxcnc-source-syntax.sh >/dev/null
|
||||||
|
grep -F 'printf '\''%s/source_%s.stamp: %s\n'\'' "$build_dir" "$source_index" "$source"' test-linuxcnc-source-syntax.sh >/dev/null
|
||||||
|
grep -F 'printf '\''\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s/source_%s.d %s\n'\''' test-linuxcnc-source-syntax.sh >/dev/null
|
||||||
|
grep -F 'make --output-sync=target -j"$build_jobs" -f "$source_syntax_makefile"' test-linuxcnc-source-syntax.sh >/dev/null
|
||||||
|
grep -F 'build_jobs=${CNC_SIM_BUILD_JOBS:-8}' build-wasm.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' build-wasm.sh >/dev/null
|
||||||
|
grep -F 'cmake --build build/wasm --target cnc_sim_wasm_runtime_probe --parallel "$build_jobs"' build-wasm.sh >/dev/null
|
||||||
|
grep -F 'cmake --build build/wasm --target cnc_sim_wasm --parallel "$build_jobs"' build-wasm.sh >/dev/null
|
||||||
|
grep -F 'build_jobs=${CNC_SIM_BUILD_JOBS:-8}' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'build_dir=${CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR-build/wasm-cmake-safe-probe}' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR must not be empty' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR must not contain whitespace: $build_dir' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR must not be the filesystem root' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'build_signature="$build_dir/wasm-cmake-safe-probe.signature"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'build_next_signature=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_cmake_safe_probe.signature.XXXXXX")' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'if [[ ! -f "$build_signature" ]] || ! cmp -s "$build_signature" "$build_next_signature"; then' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'printf '\''LINUXCNC_ROOT=%s\n'\'' "$linuxcnc_root"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'printf '\''MANIFEST=%s\n'\'' "$manifest"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'printf '\''BUILD_JOBS=%s\n'\'' "$build_jobs"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'cmake --build "$build_dir" --target linuxcnc_rs274_wasm_safe_probe_objects --parallel "$build_jobs"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'cmake --build "$build_dir" --target linuxcnc_rs274_wasm_safe_probe --parallel "$build_jobs"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'cmake --build "$build_dir" --target cnc_sim_wasm_runtime_probe --parallel "$build_jobs"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_NATIVE_BUILD_DIR' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'CNC_SIM_SOURCE_LINK_BUILD_DIR' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'CNC_SIM_SOURCE_SYNTAX_BUILD_DIR' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'Build directory overrides must' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'copies and byte-compares `cnc_sim.js` and `cnc_sim.wasm` under' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F '`CNC_SIM_BUILD_JOBS` must be a positive integer and defaults to `8`.' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F '`test-native.sh` uses persistent `build/native-test` objects' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F '`test-linuxcnc-source-link.sh` uses persistent `build/source-link-test`' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F '`test-linuxcnc-source-syntax.sh` uses persistent' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F '`test-linuxcnc-wasm-cmake-safe-probe.sh` uses persistent' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'must reject empty, whitespace-containing, or filesystem-root build' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'entry points must serialize' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'shared non-concurrency-safe paths with lock files' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F '`-MMD -MP` dependency tracking and `make --output-sync=target`' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'must run blocker, syntax, object, CMake, tooldata,' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'must check `cmake`, `emcmake`, `emcc`, and `node`' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'must build `cnc_sim_wasm_runtime_probe` before' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'must copy `build/wasm/cnc_sim.js` and' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'must compare copied artifacts with `cmp -s` before running' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'must run the Node smoke before the browser smoke' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'explicitly checked for 25' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'wasm-safe core sources and 9 blocked sources' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'must reject malformed lines, absolute source paths,' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'must include the built LinuxCNC `lib` path and rpath' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'must surface helper failures instead of hiding them' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'Temporary probe reports must be cleaned on failure' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'Documentation-only build policy edits should still update the guardrail' docs/linuxcnc-source-policy.md >/dev/null
|
||||||
grep -F 'python_include_output=$(python3.13-config --includes 2>/dev/null || python3-config --includes)' list-linuxcnc-source-common-cxxflags.sh >/dev/null
|
grep -F 'python_include_output=$(python3.13-config --includes 2>/dev/null || python3-config --includes)' list-linuxcnc-source-common-cxxflags.sh >/dev/null
|
||||||
if grep -F 'read -r -a python_includes <<<"$(' list-linuxcnc-source-common-cxxflags.sh >/dev/null; then
|
if grep -F 'read -r -a python_includes <<<"$(' list-linuxcnc-source-common-cxxflags.sh >/dev/null; then
|
||||||
echo "source common cxxflags still hides python-config failures behind read" >&2
|
echo "source common cxxflags still hides python-config failures behind read" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
for script in test-native.sh test-linuxcnc-source-syntax.sh test-linuxcnc-source-link.sh build-wasm.sh; do
|
||||||
|
if CNC_SIM_BUILD_JOBS=0 "./$script" >"$invalid_build_jobs_log" 2>&1; then
|
||||||
|
echo "$script accepted invalid CNC_SIM_BUILD_JOBS" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "CNC_SIM_BUILD_JOBS must be a positive integer: 0" "$invalid_build_jobs_log" >/dev/null; then
|
||||||
|
echo "$script did not report invalid CNC_SIM_BUILD_JOBS clearly" >&2
|
||||||
|
sed -n '1,20p' "$invalid_build_jobs_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if CNC_SIM_BUILD_JOBS=0 ./test-linuxcnc-wasm-cmake-safe-probe.sh >"$invalid_build_jobs_log" 2>&1; then
|
||||||
|
echo "test-linuxcnc-wasm-cmake-safe-probe.sh accepted invalid CNC_SIM_BUILD_JOBS" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "CNC_SIM_BUILD_JOBS must be a positive integer: 0" "$invalid_build_jobs_log" >/dev/null; then
|
||||||
|
echo "test-linuxcnc-wasm-cmake-safe-probe.sh did not report invalid CNC_SIM_BUILD_JOBS clearly" >&2
|
||||||
|
sed -n '1,20p' "$invalid_build_jobs_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR=/ ./test-linuxcnc-wasm-cmake-safe-probe.sh >"$invalid_build_dir_log" 2>&1; then
|
||||||
|
echo "test-linuxcnc-wasm-cmake-safe-probe.sh accepted filesystem root as build dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR must not be the filesystem root" "$invalid_build_dir_log" >/dev/null; then
|
||||||
|
echo "test-linuxcnc-wasm-cmake-safe-probe.sh did not report filesystem-root build dir clearly" >&2
|
||||||
|
sed -n '1,20p' "$invalid_build_dir_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR= ./test-linuxcnc-wasm-cmake-safe-probe.sh >"$empty_build_dir_log" 2>&1; then
|
||||||
|
echo "test-linuxcnc-wasm-cmake-safe-probe.sh accepted empty build dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR must not be empty" "$empty_build_dir_log" >/dev/null; then
|
||||||
|
echo "test-linuxcnc-wasm-cmake-safe-probe.sh did not report empty build dir clearly" >&2
|
||||||
|
sed -n '1,20p' "$empty_build_dir_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR="build/wasm safe probe" ./test-linuxcnc-wasm-cmake-safe-probe.sh >"$whitespace_build_dir_log" 2>&1; then
|
||||||
|
echo "test-linuxcnc-wasm-cmake-safe-probe.sh accepted whitespace in build dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR must not contain whitespace: build/wasm safe probe" "$whitespace_build_dir_log" >/dev/null; then
|
||||||
|
echo "test-linuxcnc-wasm-cmake-safe-probe.sh did not report whitespace build dir clearly" >&2
|
||||||
|
sed -n '1,20p' "$whitespace_build_dir_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if CNC_SIM_NATIVE_BUILD_DIR=/ ./test-native.sh >"$invalid_build_dir_log" 2>&1; then
|
||||||
|
echo "test-native.sh accepted filesystem root as build dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "CNC_SIM_NATIVE_BUILD_DIR must not be the filesystem root" "$invalid_build_dir_log" >/dev/null; then
|
||||||
|
echo "test-native.sh did not report filesystem-root build dir clearly" >&2
|
||||||
|
sed -n '1,20p' "$invalid_build_dir_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if CNC_SIM_NATIVE_BUILD_DIR=/tmp/.. ./test-native.sh >"$invalid_build_dir_log" 2>&1; then
|
||||||
|
echo "test-native.sh accepted canonical filesystem root as build dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "CNC_SIM_NATIVE_BUILD_DIR must not be the filesystem root" "$invalid_build_dir_log" >/dev/null; then
|
||||||
|
echo "test-native.sh did not report canonical filesystem-root build dir clearly" >&2
|
||||||
|
sed -n '1,20p' "$invalid_build_dir_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if CNC_SIM_NATIVE_BUILD_DIR= ./test-native.sh >"$empty_build_dir_log" 2>&1; then
|
||||||
|
echo "test-native.sh accepted empty build dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "CNC_SIM_NATIVE_BUILD_DIR must not be empty" "$empty_build_dir_log" >/dev/null; then
|
||||||
|
echo "test-native.sh did not report empty build dir clearly" >&2
|
||||||
|
sed -n '1,20p' "$empty_build_dir_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if CNC_SIM_NATIVE_BUILD_DIR="build/native test" ./test-native.sh >"$whitespace_build_dir_log" 2>&1; then
|
||||||
|
echo "test-native.sh accepted whitespace in build dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "CNC_SIM_NATIVE_BUILD_DIR must not contain whitespace: build/native test" "$whitespace_build_dir_log" >/dev/null; then
|
||||||
|
echo "test-native.sh did not report whitespace build dir clearly" >&2
|
||||||
|
sed -n '1,20p' "$whitespace_build_dir_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if CNC_SIM_SOURCE_SYNTAX_BUILD_DIR=/ ./test-linuxcnc-source-syntax.sh >"$invalid_build_dir_log" 2>&1; then
|
||||||
|
echo "test-linuxcnc-source-syntax.sh accepted filesystem root as build dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "CNC_SIM_SOURCE_SYNTAX_BUILD_DIR must not be the filesystem root" "$invalid_build_dir_log" >/dev/null; then
|
||||||
|
echo "test-linuxcnc-source-syntax.sh did not report filesystem-root build dir clearly" >&2
|
||||||
|
sed -n '1,20p' "$invalid_build_dir_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if CNC_SIM_SOURCE_SYNTAX_BUILD_DIR=/tmp/.. ./test-linuxcnc-source-syntax.sh >"$invalid_build_dir_log" 2>&1; then
|
||||||
|
echo "test-linuxcnc-source-syntax.sh accepted canonical filesystem root as build dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "CNC_SIM_SOURCE_SYNTAX_BUILD_DIR must not be the filesystem root" "$invalid_build_dir_log" >/dev/null; then
|
||||||
|
echo "test-linuxcnc-source-syntax.sh did not report canonical filesystem-root build dir clearly" >&2
|
||||||
|
sed -n '1,20p' "$invalid_build_dir_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if CNC_SIM_SOURCE_SYNTAX_BUILD_DIR= ./test-linuxcnc-source-syntax.sh >"$empty_build_dir_log" 2>&1; then
|
||||||
|
echo "test-linuxcnc-source-syntax.sh accepted empty build dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "CNC_SIM_SOURCE_SYNTAX_BUILD_DIR must not be empty" "$empty_build_dir_log" >/dev/null; then
|
||||||
|
echo "test-linuxcnc-source-syntax.sh did not report empty build dir clearly" >&2
|
||||||
|
sed -n '1,20p' "$empty_build_dir_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if CNC_SIM_SOURCE_SYNTAX_BUILD_DIR="build/source syntax test" ./test-linuxcnc-source-syntax.sh >"$whitespace_build_dir_log" 2>&1; then
|
||||||
|
echo "test-linuxcnc-source-syntax.sh accepted whitespace in build dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "CNC_SIM_SOURCE_SYNTAX_BUILD_DIR must not contain whitespace: build/source syntax test" "$whitespace_build_dir_log" >/dev/null; then
|
||||||
|
echo "test-linuxcnc-source-syntax.sh did not report whitespace build dir clearly" >&2
|
||||||
|
sed -n '1,20p' "$whitespace_build_dir_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if CNC_SIM_SOURCE_LINK_BUILD_DIR=/ ./test-linuxcnc-source-link.sh >"$invalid_build_dir_log" 2>&1; then
|
||||||
|
echo "test-linuxcnc-source-link.sh accepted filesystem root as build dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "CNC_SIM_SOURCE_LINK_BUILD_DIR must not be the filesystem root" "$invalid_build_dir_log" >/dev/null; then
|
||||||
|
echo "test-linuxcnc-source-link.sh did not report filesystem-root build dir clearly" >&2
|
||||||
|
sed -n '1,20p' "$invalid_build_dir_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if CNC_SIM_SOURCE_LINK_BUILD_DIR=/tmp/.. ./test-linuxcnc-source-link.sh >"$invalid_build_dir_log" 2>&1; then
|
||||||
|
echo "test-linuxcnc-source-link.sh accepted canonical filesystem root as build dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "CNC_SIM_SOURCE_LINK_BUILD_DIR must not be the filesystem root" "$invalid_build_dir_log" >/dev/null; then
|
||||||
|
echo "test-linuxcnc-source-link.sh did not report canonical filesystem-root build dir clearly" >&2
|
||||||
|
sed -n '1,20p' "$invalid_build_dir_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if CNC_SIM_SOURCE_LINK_BUILD_DIR= ./test-linuxcnc-source-link.sh >"$empty_build_dir_log" 2>&1; then
|
||||||
|
echo "test-linuxcnc-source-link.sh accepted empty build dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "CNC_SIM_SOURCE_LINK_BUILD_DIR must not be empty" "$empty_build_dir_log" >/dev/null; then
|
||||||
|
echo "test-linuxcnc-source-link.sh did not report empty build dir clearly" >&2
|
||||||
|
sed -n '1,20p' "$empty_build_dir_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if CNC_SIM_SOURCE_LINK_BUILD_DIR="build/source link test" ./test-linuxcnc-source-link.sh >"$whitespace_build_dir_log" 2>&1; then
|
||||||
|
echo "test-linuxcnc-source-link.sh accepted whitespace in build dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "CNC_SIM_SOURCE_LINK_BUILD_DIR must not contain whitespace: build/source link test" "$whitespace_build_dir_log" >/dev/null; then
|
||||||
|
echo "test-linuxcnc-source-link.sh did not report whitespace build dir clearly" >&2
|
||||||
|
sed -n '1,20p' "$whitespace_build_dir_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
forbidden_common_flags_process_substitution='readarray -t common_flags < <('
|
forbidden_common_flags_process_substitution='readarray -t common_flags < <('
|
||||||
forbidden_link_flags_process_substitution='readarray -t link_flags < <('
|
forbidden_link_flags_process_substitution='readarray -t link_flags < <('
|
||||||
forbidden_source_common_flags_helper='./list-linuxcnc-source-common-cxxflags.sh'
|
forbidden_source_common_flags_helper='./list-linuxcnc-source-common-cxxflags.sh'
|
||||||
@@ -155,8 +448,8 @@ grep -F 'CNC_SIM_ENABLE_SMOKE_BACKEND cannot be combined with CNC_SIM_ENABLE_LIN
|
|||||||
grep -F 'smoke backend is not compiled into this build' core/src/gcode_backend.cpp >/dev/null
|
grep -F 'smoke backend is not compiled into this build' core/src/gcode_backend.cpp >/dev/null
|
||||||
grep -F './list-linuxcnc-bridge-smoke-project-sources.sh > "$project_source_list"' test-linuxcnc-bridge-native.sh >/dev/null
|
grep -F './list-linuxcnc-bridge-smoke-project-sources.sh > "$project_source_list"' test-linuxcnc-bridge-native.sh >/dev/null
|
||||||
grep -F 'LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-support-objects.sh > "$support_object_list"' test-linuxcnc-source-link.sh >/dev/null
|
grep -F 'LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-support-objects.sh > "$support_object_list"' test-linuxcnc-source-link.sh >/dev/null
|
||||||
grep -F 'trap '\''rm -f "$source_list" "$missing_source_manifest" "$missing_source_log"'\'' EXIT' test-linuxcnc-source-syntax.sh >/dev/null
|
grep -Fx 'trap cleanup_source_syntax_temps EXIT' test-linuxcnc-source-syntax.sh >/dev/null
|
||||||
if grep -F 'rm -f "$missing_source_manifest" "$missing_source_log"' test-linuxcnc-source-syntax.sh >/dev/null; then
|
if grep -Fx "trap 'rm -f \"\$source_list\" \"\$missing_source_manifest\" \"\$missing_source_log\"' EXIT" test-linuxcnc-source-syntax.sh >/dev/null; then
|
||||||
echo "linuxcnc source syntax probe still relies on manual cleanup for missing-source temporaries" >&2
|
echo "linuxcnc source syntax probe still relies on manual cleanup for missing-source temporaries" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -421,11 +714,11 @@ fi
|
|||||||
|
|
||||||
printf 'core:src/emc/rs274ngc/does_not_exist.cc:test missing source\n' >"$missing_source_manifest"
|
printf 'core:src/emc/rs274ngc/does_not_exist.cc:test missing source\n' >"$missing_source_manifest"
|
||||||
if ./test-linuxcnc-source-syntax.sh "$missing_source_manifest" >"$missing_source_log" 2>&1; then
|
if ./test-linuxcnc-source-syntax.sh "$missing_source_manifest" >"$missing_source_log" 2>&1; then
|
||||||
echo "test-linuxcnc-source-syntax.sh accepted missing manifest file" >&2
|
echo "test-linuxcnc-source-syntax.sh accepted missing manifest source" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if ! grep -F "missing manifest file: src/emc/rs274ngc/does_not_exist.cc" "$missing_source_log" >/dev/null; then
|
if ! grep -F "missing manifest source: src/emc/rs274ngc/does_not_exist.cc" "$missing_source_log" >/dev/null; then
|
||||||
echo "test-linuxcnc-source-syntax.sh did not report missing manifest file clearly" >&2
|
echo "test-linuxcnc-source-syntax.sh did not report missing manifest source clearly" >&2
|
||||||
sed -n '1,20p' "$missing_source_log" >&2
|
sed -n '1,20p' "$missing_source_log" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -5,10 +5,29 @@ cd "$(dirname "$0")"
|
|||||||
|
|
||||||
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
||||||
cxx=${CXX:-g++}
|
cxx=${CXX:-g++}
|
||||||
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_rs274_source_link.XXXXXX")
|
build_jobs=${CNC_SIM_BUILD_JOBS:-8}
|
||||||
|
if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then
|
||||||
|
echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
build_dir=${CNC_SIM_SOURCE_LINK_BUILD_DIR-build/source-link-test}
|
||||||
|
if [[ -z "$build_dir" ]]; then
|
||||||
|
echo "CNC_SIM_SOURCE_LINK_BUILD_DIR must not be empty" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ "$build_dir" =~ [[:space:]] ]]; then
|
||||||
|
echo "CNC_SIM_SOURCE_LINK_BUILD_DIR must not contain whitespace: $build_dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
manifest=${1:-linuxcnc-rs274-source-files.txt}
|
manifest=${1:-linuxcnc-rs274-source-files.txt}
|
||||||
|
|
||||||
trap 'rm -rf "$build_dir"' EXIT
|
mkdir -p "$build_dir"
|
||||||
|
build_dir=$(cd "$build_dir" && pwd)
|
||||||
|
if [[ "$build_dir" == "/" ]]; then
|
||||||
|
echo "CNC_SIM_SOURCE_LINK_BUILD_DIR must not be the filesystem root" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest"
|
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest"
|
||||||
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-switchkins-remap-table.sh linuxcnc-kinematics-source-files.txt
|
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-switchkins-remap-table.sh linuxcnc-kinematics-source-files.txt
|
||||||
# The LinuxCNC source-linked dump path is not concurrency-safe under parallel execution.
|
# The LinuxCNC source-linked dump path is not concurrency-safe under parallel execution.
|
||||||
@@ -33,7 +52,6 @@ objects=()
|
|||||||
linuxcnc_index=0
|
linuxcnc_index=0
|
||||||
for source in "${linuxcnc_sources[@]}"; do
|
for source in "${linuxcnc_sources[@]}"; do
|
||||||
obj="$build_dir/linuxcnc_${linuxcnc_index}.o"
|
obj="$build_dir/linuxcnc_${linuxcnc_index}.o"
|
||||||
"$cxx" "${common_flags[@]}" -c "$source" -o "$obj"
|
|
||||||
objects+=("$obj")
|
objects+=("$obj")
|
||||||
linuxcnc_index=$((linuxcnc_index + 1))
|
linuxcnc_index=$((linuxcnc_index + 1))
|
||||||
done
|
done
|
||||||
@@ -44,33 +62,94 @@ project_source_list="$build_dir/linuxcnc_rs274_dump_project_sources.txt"
|
|||||||
mapfile -t project_sources < "$project_source_list"
|
mapfile -t project_sources < "$project_source_list"
|
||||||
for source in "${project_sources[@]}"; do
|
for source in "${project_sources[@]}"; do
|
||||||
obj="$build_dir/project_${project_index}.o"
|
obj="$build_dir/project_${project_index}.o"
|
||||||
"$cxx" "${common_flags[@]}" -c "$source" -o "$obj"
|
|
||||||
objects+=("$obj")
|
objects+=("$obj")
|
||||||
project_index=$((project_index + 1))
|
project_index=$((project_index + 1))
|
||||||
done
|
done
|
||||||
|
|
||||||
"$cxx" -std=c++17 -I core/include -I core/src \
|
|
||||||
core/tests/linuxcnc_gees_table_smoke.cpp \
|
|
||||||
-o "$build_dir/linuxcnc_gees_table_smoke"
|
|
||||||
|
|
||||||
support_object_list="$build_dir/linuxcnc_support_objects.txt"
|
support_object_list="$build_dir/linuxcnc_support_objects.txt"
|
||||||
LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-support-objects.sh > "$support_object_list"
|
LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-support-objects.sh > "$support_object_list"
|
||||||
mapfile -t support_objects < "$support_object_list"
|
mapfile -t support_objects < "$support_object_list"
|
||||||
|
|
||||||
"$cxx" "${objects[@]}" "${support_objects[@]}" \
|
source_link_signature="$build_dir/source-link-build.signature"
|
||||||
"${link_flags[@]}" \
|
source_link_next_signature="$build_dir/source-link-build.signature.next"
|
||||||
-llinuxcnc-uspace-posix \
|
{
|
||||||
-llinuxcncini \
|
printf 'CXX=%s\n' "$cxx"
|
||||||
-llinuxcnchal \
|
printf 'PWD=%s\n' "$PWD"
|
||||||
-lpyplugin \
|
printf 'LINUXCNC_ROOT=%s\n' "$(cd "$linuxcnc_root" && pwd)"
|
||||||
-ltooldata \
|
printf 'COMMON_FLAGS='
|
||||||
-lnml \
|
printf ' %s' "${common_flags[@]}"
|
||||||
-lposemath \
|
printf '\n'
|
||||||
-lboost_python313 \
|
printf 'LINK_FLAGS='
|
||||||
-lpython3.13 \
|
printf ' %s' "${link_flags[@]}"
|
||||||
-lfmt \
|
printf '\n'
|
||||||
-ldl \
|
printf 'LINUXCNC_SOURCES:\n'
|
||||||
-o "$build_dir/linuxcnc_rs274_source_dump"
|
printf '%s\n' "${linuxcnc_sources[@]}"
|
||||||
|
printf 'PROJECT_SOURCES:\n'
|
||||||
|
printf '%s\n' "${project_sources[@]}"
|
||||||
|
printf 'SUPPORT_OBJECTS:\n'
|
||||||
|
printf '%s\n' "${support_objects[@]}"
|
||||||
|
} > "$source_link_next_signature"
|
||||||
|
if [[ ! -f "$source_link_signature" ]] || ! cmp -s "$source_link_signature" "$source_link_next_signature"; then
|
||||||
|
rm -f "$build_dir"/linuxcnc_*.o \
|
||||||
|
"$build_dir"/*.d \
|
||||||
|
"$build_dir"/project_*.o \
|
||||||
|
"$build_dir"/linuxcnc_gees_table_smoke.o \
|
||||||
|
"$build_dir"/linuxcnc_rs274_source_dump \
|
||||||
|
"$build_dir"/linuxcnc_gees_table_smoke
|
||||||
|
fi
|
||||||
|
mv "$source_link_next_signature" "$source_link_signature"
|
||||||
|
|
||||||
|
source_link_makefile="$build_dir/source-link.mk"
|
||||||
|
{
|
||||||
|
printf 'CXX := %s\n' "$cxx"
|
||||||
|
printf 'COMMON_FLAGS :='
|
||||||
|
printf ' %s' "${common_flags[@]}"
|
||||||
|
printf '\n'
|
||||||
|
printf 'LINK_FLAGS :='
|
||||||
|
printf ' %s' "${link_flags[@]}"
|
||||||
|
printf '\n'
|
||||||
|
printf 'OBJECTS :='
|
||||||
|
printf ' %s' "${objects[@]}"
|
||||||
|
printf '\n'
|
||||||
|
printf 'SUPPORT_OBJECTS :='
|
||||||
|
printf ' %s' "${support_objects[@]}"
|
||||||
|
printf '\n\n'
|
||||||
|
printf 'DEP_FILES :=\n'
|
||||||
|
for object in "${objects[@]}"; do
|
||||||
|
printf 'DEP_FILES += %s.d\n' "$object"
|
||||||
|
done
|
||||||
|
printf 'DEP_FILES += %s.d\n\n' "$build_dir/linuxcnc_gees_table_smoke.o"
|
||||||
|
printf '.PHONY: all\n'
|
||||||
|
printf 'all: %s %s\n\n' \
|
||||||
|
"$build_dir/linuxcnc_rs274_source_dump" \
|
||||||
|
"$build_dir/linuxcnc_gees_table_smoke"
|
||||||
|
|
||||||
|
linuxcnc_index=0
|
||||||
|
for source in "${linuxcnc_sources[@]}"; do
|
||||||
|
printf '%s: %s\n' "$build_dir/linuxcnc_${linuxcnc_index}.o" "$source"
|
||||||
|
printf '\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s.d -c %s -o %s\n\n' \
|
||||||
|
"$build_dir/linuxcnc_${linuxcnc_index}.o" "$source" "$build_dir/linuxcnc_${linuxcnc_index}.o"
|
||||||
|
linuxcnc_index=$((linuxcnc_index + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
project_index=0
|
||||||
|
for source in "${project_sources[@]}"; do
|
||||||
|
printf '%s: %s\n' "$build_dir/project_${project_index}.o" "$source"
|
||||||
|
printf '\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s.d -c %s -o %s\n\n' \
|
||||||
|
"$build_dir/project_${project_index}.o" "$source" "$build_dir/project_${project_index}.o"
|
||||||
|
project_index=$((project_index + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
printf '%s: core/tests/linuxcnc_gees_table_smoke.cpp\n' "$build_dir/linuxcnc_gees_table_smoke.o"
|
||||||
|
printf '\t@$(CXX) -std=c++17 -I core/include -I core/src -MMD -MP -MF %s.d -c core/tests/linuxcnc_gees_table_smoke.cpp -o %s\n\n' \
|
||||||
|
"$build_dir/linuxcnc_gees_table_smoke.o" "$build_dir/linuxcnc_gees_table_smoke.o"
|
||||||
|
printf '%s: %s\n' "$build_dir/linuxcnc_gees_table_smoke" "$build_dir/linuxcnc_gees_table_smoke.o"
|
||||||
|
printf '\t@$(CXX) -std=c++17 -I core/include -I core/src %s -o %s\n\n' "$build_dir/linuxcnc_gees_table_smoke.o" "$build_dir/linuxcnc_gees_table_smoke"
|
||||||
|
printf '%s: $(OBJECTS) $(SUPPORT_OBJECTS)\n' "$build_dir/linuxcnc_rs274_source_dump"
|
||||||
|
printf '\t@$(CXX) $(OBJECTS) $(SUPPORT_OBJECTS) $(LINK_FLAGS) -llinuxcnc-uspace-posix -llinuxcncini -llinuxcnchal -lpyplugin -ltooldata -lnml -lposemath -lboost_python313 -lpython3.13 -lfmt -ldl -o %s\n' "$build_dir/linuxcnc_rs274_source_dump"
|
||||||
|
printf '\n-include $(DEP_FILES)\n'
|
||||||
|
} > "$source_link_makefile"
|
||||||
|
make --output-sync=target -j"$build_jobs" -f "$source_link_makefile"
|
||||||
|
|
||||||
var_file="$build_dir/rs274ngc-source.var"
|
var_file="$build_dir/rs274ngc-source.var"
|
||||||
base_var_file="$build_dir/rs274ngc-source-base.var"
|
base_var_file="$build_dir/rs274ngc-source-base.var"
|
||||||
|
|||||||
@@ -5,15 +5,40 @@ cd "$(dirname "$0")"
|
|||||||
|
|
||||||
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
||||||
cxx=${CXX:-g++}
|
cxx=${CXX:-g++}
|
||||||
|
build_jobs=${CNC_SIM_BUILD_JOBS:-8}
|
||||||
|
if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then
|
||||||
|
echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
build_dir=${CNC_SIM_SOURCE_SYNTAX_BUILD_DIR-build/source-syntax-test}
|
||||||
|
if [[ -z "$build_dir" ]]; then
|
||||||
|
echo "CNC_SIM_SOURCE_SYNTAX_BUILD_DIR must not be empty" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ "$build_dir" =~ [[:space:]] ]]; then
|
||||||
|
echo "CNC_SIM_SOURCE_SYNTAX_BUILD_DIR must not contain whitespace: $build_dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
mkdir -p "$build_dir"
|
||||||
|
build_dir=$(cd "$build_dir" && pwd)
|
||||||
|
if [[ "$build_dir" == "/" ]]; then
|
||||||
|
echo "CNC_SIM_SOURCE_SYNTAX_BUILD_DIR must not be the filesystem root" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
exec 9>"$build_dir/source-syntax.lock"
|
||||||
|
flock 9
|
||||||
manifest=${1:-linuxcnc-rs274-source-files.txt}
|
manifest=${1:-linuxcnc-rs274-source-files.txt}
|
||||||
|
|
||||||
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest"
|
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest"
|
||||||
|
|
||||||
missing_source_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_rs274_manifest_source.XXXXXX.txt")
|
missing_source_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_rs274_manifest_source.XXXXXX.txt")
|
||||||
missing_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_rs274_manifest_source.XXXXXX.log")
|
missing_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_rs274_manifest_source.XXXXXX.log")
|
||||||
source_list=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_source_syntax_sources.XXXXXX.txt")
|
source_list="$build_dir/linuxcnc_source_syntax_sources.txt"
|
||||||
trap 'rm -f "$source_list" "$missing_source_manifest" "$missing_source_log"' EXIT
|
cleanup_source_syntax_temps() {
|
||||||
grep -Fx 'trap '\''rm -f "$source_list" "$missing_source_manifest" "$missing_source_log"'\'' EXIT' test-linuxcnc-source-syntax.sh >/dev/null
|
rm -f "$missing_source_manifest" "$missing_source_log"
|
||||||
|
}
|
||||||
|
trap cleanup_source_syntax_temps EXIT
|
||||||
|
grep -Fx 'trap cleanup_source_syntax_temps EXIT' test-linuxcnc-source-syntax.sh >/dev/null
|
||||||
if awk '/^rm -f "\$missing_source_manifest" "\$missing_source_log"$/{ found = 1 } END { exit !found }' test-linuxcnc-source-syntax.sh >/dev/null; then
|
if awk '/^rm -f "\$missing_source_manifest" "\$missing_source_log"$/{ found = 1 } END { exit !found }' test-linuxcnc-source-syntax.sh >/dev/null; then
|
||||||
echo "linuxcnc source syntax probe still relies on manual cleanup for missing-source temporaries" >&2
|
echo "linuxcnc source syntax probe still relies on manual cleanup for missing-source temporaries" >&2
|
||||||
exit 1
|
exit 1
|
||||||
@@ -24,20 +49,20 @@ LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-manifest-sources.sh "$mani
|
|||||||
|
|
||||||
printf 'core:src/emc/rs274ngc/does_not_exist.cc:test missing source\n' >"$missing_source_manifest"
|
printf 'core:src/emc/rs274ngc/does_not_exist.cc:test missing source\n' >"$missing_source_manifest"
|
||||||
if ./test-linuxcnc-source-objects.sh "$missing_source_manifest" >"$missing_source_log" 2>&1; then
|
if ./test-linuxcnc-source-objects.sh "$missing_source_manifest" >"$missing_source_log" 2>&1; then
|
||||||
echo "linuxcnc source object probe accepted missing manifest file" >&2
|
echo "linuxcnc source object probe accepted missing manifest source" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if ! grep -F "missing manifest file: src/emc/rs274ngc/does_not_exist.cc" "$missing_source_log" >/dev/null; then
|
if ! grep -F "missing manifest source: src/emc/rs274ngc/does_not_exist.cc" "$missing_source_log" >/dev/null; then
|
||||||
echo "linuxcnc source object probe did not report missing manifest file clearly" >&2
|
echo "linuxcnc source object probe did not report missing manifest source clearly" >&2
|
||||||
sed -n '1,20p' "$missing_source_log" >&2
|
sed -n '1,20p' "$missing_source_log" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if ./test-linuxcnc-source-link.sh "$missing_source_manifest" >"$missing_source_log" 2>&1; then
|
if ./test-linuxcnc-source-link.sh "$missing_source_manifest" >"$missing_source_log" 2>&1; then
|
||||||
echo "linuxcnc source link probe accepted missing manifest file" >&2
|
echo "linuxcnc source link probe accepted missing manifest source" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if ! grep -F "missing manifest file: src/emc/rs274ngc/does_not_exist.cc" "$missing_source_log" >/dev/null; then
|
if ! grep -F "missing manifest source: src/emc/rs274ngc/does_not_exist.cc" "$missing_source_log" >/dev/null; then
|
||||||
echo "linuxcnc source link probe did not report missing manifest file clearly" >&2
|
echo "linuxcnc source link probe did not report missing manifest source clearly" >&2
|
||||||
sed -n '1,20p' "$missing_source_log" >&2
|
sed -n '1,20p' "$missing_source_log" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -45,9 +70,59 @@ fi
|
|||||||
common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-common-cxxflags.sh --python --syntax-only)
|
common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-common-cxxflags.sh --python --syntax-only)
|
||||||
mapfile -t common_flags <<<"$common_flag_output"
|
mapfile -t common_flags <<<"$common_flag_output"
|
||||||
|
|
||||||
|
source_syntax_signature="$build_dir/source-syntax.signature"
|
||||||
|
source_syntax_next_signature="$build_dir/source-syntax.signature.next"
|
||||||
|
{
|
||||||
|
printf 'CXX=%s\n' "$cxx"
|
||||||
|
printf 'PWD=%s\n' "$PWD"
|
||||||
|
printf 'LINUXCNC_ROOT=%s\n' "$(cd "$linuxcnc_root" && pwd)"
|
||||||
|
printf 'MANIFEST=%s\n' "$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest")"
|
||||||
|
printf 'BUILD_JOBS=%s\n' "$build_jobs"
|
||||||
|
printf 'COMMON_FLAGS='
|
||||||
|
printf ' %s' "${common_flags[@]}"
|
||||||
|
printf '\n'
|
||||||
|
printf 'SOURCES:\n'
|
||||||
|
cat "$source_list"
|
||||||
|
} > "$source_syntax_next_signature"
|
||||||
|
if [[ ! -f "$source_syntax_signature" ]] || ! cmp -s "$source_syntax_signature" "$source_syntax_next_signature"; then
|
||||||
|
rm -f "$build_dir"/source_*.stamp "$build_dir"/source_*.d
|
||||||
|
fi
|
||||||
|
mv "$source_syntax_next_signature" "$source_syntax_signature"
|
||||||
|
|
||||||
|
source_syntax_makefile="$build_dir/source-syntax.mk"
|
||||||
|
{
|
||||||
|
printf 'CXX := %s\n' "$cxx"
|
||||||
|
printf 'COMMON_FLAGS :='
|
||||||
|
printf ' %s' "${common_flags[@]}"
|
||||||
|
printf '\n\n'
|
||||||
|
printf 'STAMPS :=\n'
|
||||||
|
source_index=0
|
||||||
while IFS= read -r source; do
|
while IFS= read -r source; do
|
||||||
[[ -z "$source" ]] && continue
|
[[ -z "$source" ]] && continue
|
||||||
"$cxx" "${common_flags[@]}" "$source"
|
printf 'STAMPS += %s/source_%s.stamp\n' "$build_dir" "$source_index"
|
||||||
|
source_index=$((source_index + 1))
|
||||||
|
done < "$source_list"
|
||||||
|
printf '\n.PHONY: all\n'
|
||||||
|
printf 'all: $(STAMPS)\n\n'
|
||||||
|
|
||||||
|
source_index=0
|
||||||
|
while IFS= read -r source; do
|
||||||
|
[[ -z "$source" ]] && continue
|
||||||
|
printf '%s/source_%s.stamp: %s\n' "$build_dir" "$source_index" "$source"
|
||||||
|
printf '\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s/source_%s.d %s\n' "$build_dir" "$source_index" "$source"
|
||||||
|
printf '\t@touch %s/source_%s.stamp\n\n' "$build_dir" "$source_index"
|
||||||
|
source_index=$((source_index + 1))
|
||||||
done < "$source_list"
|
done < "$source_list"
|
||||||
|
|
||||||
|
printf '%s' '-include'
|
||||||
|
source_index=0
|
||||||
|
while IFS= read -r source; do
|
||||||
|
[[ -z "$source" ]] && continue
|
||||||
|
printf ' %s/source_%s.d' "$build_dir" "$source_index"
|
||||||
|
source_index=$((source_index + 1))
|
||||||
|
done < "$source_list"
|
||||||
|
printf '\n'
|
||||||
|
} > "$source_syntax_makefile"
|
||||||
|
make --output-sync=target -j"$build_jobs" -f "$source_syntax_makefile"
|
||||||
|
|
||||||
echo "linuxcnc rs274 source syntax probe passed"
|
echo "linuxcnc rs274 source syntax probe passed"
|
||||||
|
|||||||
@@ -4,20 +4,47 @@ set -euo pipefail
|
|||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
||||||
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_cmake_safe_probe.XXXXXX")
|
build_jobs=${CNC_SIM_BUILD_JOBS:-8}
|
||||||
|
if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then
|
||||||
|
echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
build_dir=${CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR-build/wasm-cmake-safe-probe}
|
||||||
|
if [[ -z "$build_dir" ]]; then
|
||||||
|
echo "CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR must not be empty" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ "$build_dir" =~ [[:space:]] ]]; then
|
||||||
|
echo "CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR must not contain whitespace: $build_dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
mkdir -p "$build_dir"
|
||||||
|
build_dir=$(cd "$build_dir" && pwd)
|
||||||
|
if [[ "$build_dir" == "/" ]]; then
|
||||||
|
echo "CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR must not be the filesystem root" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
|
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
|
||||||
missing_manifest=${TMPDIR:-/tmp}/cnc_sim_missing_build_wasm_manifest.txt
|
missing_manifest=${TMPDIR:-/tmp}/cnc_sim_missing_build_wasm_manifest.txt
|
||||||
missing_manifest_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_build_wasm_manifest.XXXXXX.log")
|
missing_manifest_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_build_wasm_manifest.XXXXXX.log")
|
||||||
missing_root=/tmp/does-not-exist-linuxcnc
|
missing_root=/tmp/does-not-exist-linuxcnc
|
||||||
missing_root_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_build_wasm_missing_root.XXXXXX.log")
|
missing_root_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_build_wasm_missing_root.XXXXXX.log")
|
||||||
|
invalid_build_jobs_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_build_wasm_invalid_jobs.XXXXXX.log")
|
||||||
|
invalid_build_dir_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_build_wasm_invalid_dir.XXXXXX.log")
|
||||||
|
empty_build_dir_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_build_wasm_empty_dir.XXXXXX.log")
|
||||||
|
whitespace_build_dir_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_build_wasm_whitespace_dir.XXXXXX.log")
|
||||||
|
|
||||||
cleanup_wasm_cmake_probe_temps() {
|
cleanup_wasm_cmake_probe_temps() {
|
||||||
local temp_path
|
local temp_path
|
||||||
for temp_path in \
|
for temp_path in \
|
||||||
"$build_dir" \
|
|
||||||
"$missing_manifest" \
|
"$missing_manifest" \
|
||||||
"$missing_manifest_log" \
|
"$missing_manifest_log" \
|
||||||
"$missing_root_log" \
|
"$missing_root_log" \
|
||||||
|
"$invalid_build_jobs_log" \
|
||||||
|
"$invalid_build_dir_log" \
|
||||||
|
"$empty_build_dir_log" \
|
||||||
|
"$whitespace_build_dir_log" \
|
||||||
|
"${build_next_signature:-}" \
|
||||||
"${unknown_manifest_filter_log:-}" \
|
"${unknown_manifest_filter_log:-}" \
|
||||||
"${unknown_manifest_output_log:-}" \
|
"${unknown_manifest_output_log:-}" \
|
||||||
"${missing_shim_log:-}" \
|
"${missing_shim_log:-}" \
|
||||||
@@ -62,6 +89,22 @@ cleanup_wasm_cmake_probe_temps() {
|
|||||||
trap cleanup_wasm_cmake_probe_temps EXIT
|
trap cleanup_wasm_cmake_probe_temps EXIT
|
||||||
|
|
||||||
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest"
|
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest"
|
||||||
|
manifest=$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest")
|
||||||
|
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
|
||||||
|
build_signature="$build_dir/wasm-cmake-safe-probe.signature"
|
||||||
|
build_next_signature=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_cmake_safe_probe.signature.XXXXXX")
|
||||||
|
{
|
||||||
|
printf 'CXX=%s\n' "${CXX:-g++}"
|
||||||
|
printf 'PWD=%s\n' "$PWD"
|
||||||
|
printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root"
|
||||||
|
printf 'MANIFEST=%s\n' "$manifest"
|
||||||
|
printf 'BUILD_JOBS=%s\n' "$build_jobs"
|
||||||
|
} > "$build_next_signature"
|
||||||
|
if [[ ! -f "$build_signature" ]] || ! cmp -s "$build_signature" "$build_next_signature"; then
|
||||||
|
rm -rf "$build_dir"
|
||||||
|
mkdir -p "$build_dir"
|
||||||
|
fi
|
||||||
|
mv "$build_next_signature" "$build_signature"
|
||||||
|
|
||||||
rm -f "$missing_manifest"
|
rm -f "$missing_manifest"
|
||||||
if CNC_SIM_BUILD_WASM_SKIP_LOCK=1 ./build-wasm.sh "$missing_manifest" >"$missing_manifest_log" 2>&1; then
|
if CNC_SIM_BUILD_WASM_SKIP_LOCK=1 ./build-wasm.sh "$missing_manifest" >"$missing_manifest_log" 2>&1; then
|
||||||
@@ -83,6 +126,43 @@ if ! grep -F "missing LinuxCNC root: $missing_root" "$missing_root_log" >/dev/nu
|
|||||||
sed -n '1,20p' "$missing_root_log" >&2
|
sed -n '1,20p' "$missing_root_log" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if CNC_SIM_BUILD_WASM_SKIP_LOCK=1 CNC_SIM_BUILD_JOBS=0 ./build-wasm.sh >"$invalid_build_jobs_log" 2>&1; then
|
||||||
|
echo "build-wasm accepted invalid CNC_SIM_BUILD_JOBS" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "CNC_SIM_BUILD_JOBS must be a positive integer: 0" "$invalid_build_jobs_log" >/dev/null; then
|
||||||
|
echo "build-wasm did not report invalid CNC_SIM_BUILD_JOBS clearly" >&2
|
||||||
|
sed -n '1,20p' "$invalid_build_jobs_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR=/ ./test-linuxcnc-wasm-cmake-safe-probe.sh >"$invalid_build_dir_log" 2>&1; then
|
||||||
|
echo "test-linuxcnc-wasm-cmake-safe-probe.sh accepted filesystem root as build dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR must not be the filesystem root" "$invalid_build_dir_log" >/dev/null; then
|
||||||
|
echo "test-linuxcnc-wasm-cmake-safe-probe.sh did not report filesystem-root build dir clearly" >&2
|
||||||
|
sed -n '1,20p' "$invalid_build_dir_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR= ./test-linuxcnc-wasm-cmake-safe-probe.sh >"$empty_build_dir_log" 2>&1; then
|
||||||
|
echo "test-linuxcnc-wasm-cmake-safe-probe.sh accepted empty build dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR must not be empty" "$empty_build_dir_log" >/dev/null; then
|
||||||
|
echo "test-linuxcnc-wasm-cmake-safe-probe.sh did not report empty build dir clearly" >&2
|
||||||
|
sed -n '1,20p' "$empty_build_dir_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR="build/wasm safe probe" ./test-linuxcnc-wasm-cmake-safe-probe.sh >"$whitespace_build_dir_log" 2>&1; then
|
||||||
|
echo "test-linuxcnc-wasm-cmake-safe-probe.sh accepted whitespace in build dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR must not contain whitespace: build/wasm safe probe" "$whitespace_build_dir_log" >/dev/null; then
|
||||||
|
echo "test-linuxcnc-wasm-cmake-safe-probe.sh did not report whitespace build dir clearly" >&2
|
||||||
|
sed -n '1,20p' "$whitespace_build_dir_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
grep -F 'exec 9>"${TMPDIR:-/tmp}/cnc_sim_build_wasm.lock"' build-wasm.sh >/dev/null
|
grep -F 'exec 9>"${TMPDIR:-/tmp}/cnc_sim_build_wasm.lock"' build-wasm.sh >/dev/null
|
||||||
grep -F "flock 9" build-wasm.sh >/dev/null
|
grep -F "flock 9" build-wasm.sh >/dev/null
|
||||||
grep -F -- './test-linuxcnc-wasm-blockers.sh "$manifest"' build-wasm.sh >/dev/null
|
grep -F -- './test-linuxcnc-wasm-blockers.sh "$manifest"' build-wasm.sh >/dev/null
|
||||||
@@ -207,9 +287,50 @@ grep -F -- 'for required_tool in cmake emcmake emcc node; do' build-wasm.sh >/de
|
|||||||
grep -F -- 'missing_tools+=("$required_tool")' build-wasm.sh >/dev/null
|
grep -F -- 'missing_tools+=("$required_tool")' build-wasm.sh >/dev/null
|
||||||
grep -F "WASM build tools are required. Missing:" build-wasm.sh >/dev/null
|
grep -F "WASM build tools are required. Missing:" build-wasm.sh >/dev/null
|
||||||
grep -F "Install/activate cmake, emsdk, and Node.js so cmake, emcmake, emcc, and node are in PATH." build-wasm.sh >/dev/null
|
grep -F "Install/activate cmake, emsdk, and Node.js so cmake, emcmake, emcc, and node are in PATH." build-wasm.sh >/dev/null
|
||||||
|
grep -F 'build_jobs=${CNC_SIM_BUILD_JOBS:-8}' build-wasm.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' build-wasm.sh >/dev/null
|
||||||
|
grep -F 'build_jobs=${CNC_SIM_BUILD_JOBS:-8}' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'build_dir=${CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR-build/wasm-cmake-safe-probe}' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR must not be empty' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR must not contain whitespace: $build_dir' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR must not be the filesystem root' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'build_signature="$build_dir/wasm-cmake-safe-probe.signature"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'build_next_signature=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_cmake_safe_probe.signature.XXXXXX")' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'if [[ ! -f "$build_signature" ]] || ! cmp -s "$build_signature" "$build_next_signature"; then' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'printf '\''LINUXCNC_ROOT=%s\n'\'' "$linuxcnc_root"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'printf '\''MANIFEST=%s\n'\'' "$manifest"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'printf '\''BUILD_JOBS=%s\n'\'' "$build_jobs"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'build_jobs=${CNC_SIM_BUILD_JOBS:-8}' test-native.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' test-native.sh >/dev/null
|
||||||
|
grep -F 'build_dir=${CNC_SIM_NATIVE_BUILD_DIR-build/native-test}' test-native.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_NATIVE_BUILD_DIR must not be empty' test-native.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_NATIVE_BUILD_DIR must not contain whitespace: $build_dir' test-native.sh >/dev/null
|
||||||
|
grep -F 'exec 9>"$build_dir/native-test.lock"' test-native.sh >/dev/null
|
||||||
|
grep -F 'native_signature="$build_dir/native-build.signature"' test-native.sh >/dev/null
|
||||||
|
grep -F 'if [[ ! -f "$native_signature" ]] || ! cmp -s "$native_signature" "$native_next_signature"; then' test-native.sh >/dev/null
|
||||||
|
grep -F 'printf '\''PWD=%s\n'\'' "$PWD"' test-native.sh >/dev/null
|
||||||
|
grep -F 'printf '\''\t@$(CXX) $(SMOKE_CXXFLAGS) -MMD -MP -MF %s.d -c %s -o %s\n\n'\''' test-native.sh >/dev/null
|
||||||
|
grep -F 'printf '\''\n-include $(DEP_FILES)\n'\''' test-native.sh >/dev/null
|
||||||
|
grep -F 'make --output-sync=target -j"$build_jobs" -f "$native_makefile"' test-native.sh >/dev/null
|
||||||
|
grep -F 'build_jobs=${CNC_SIM_BUILD_JOBS:-8}' test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'build_dir=${CNC_SIM_SOURCE_LINK_BUILD_DIR-build/source-link-test}' test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_SOURCE_LINK_BUILD_DIR must not be empty' test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'CNC_SIM_SOURCE_LINK_BUILD_DIR must not contain whitespace: $build_dir' test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'source_link_signature="$build_dir/source-link-build.signature"' test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'if [[ ! -f "$source_link_signature" ]] || ! cmp -s "$source_link_signature" "$source_link_next_signature"; then' test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'printf '\''PWD=%s\n'\'' "$PWD"' test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'printf '\''LINUXCNC_ROOT=%s\n'\'' "$(cd "$linuxcnc_root" && pwd)"' test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'printf '\''\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s.d -c %s -o %s\n\n'\''' test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'printf '\''\n-include $(DEP_FILES)\n'\''' test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'make --output-sync=target -j"$build_jobs" -f "$source_link_makefile"' test-linuxcnc-source-link.sh >/dev/null
|
||||||
grep -F -- 'emcmake cmake -S core -B build/wasm \' build-wasm.sh >/dev/null
|
grep -F -- 'emcmake cmake -S core -B build/wasm \' build-wasm.sh >/dev/null
|
||||||
grep -F -- 'cmake --build build/wasm --target cnc_sim_wasm_runtime_probe' build-wasm.sh >/dev/null
|
grep -F -- 'cmake --build build/wasm --target cnc_sim_wasm_runtime_probe --parallel "$build_jobs"' build-wasm.sh >/dev/null
|
||||||
grep -F -- 'cmake --build build/wasm --target cnc_sim_wasm' build-wasm.sh >/dev/null
|
grep -F -- 'cmake --build build/wasm --target cnc_sim_wasm --parallel "$build_jobs"' build-wasm.sh >/dev/null
|
||||||
|
grep -F -- 'cmake --build "$build_dir" --target linuxcnc_rs274_wasm_safe_probe_objects --parallel "$build_jobs"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F -- 'cmake --build "$build_dir" --target linuxcnc_rs274_wasm_safe_probe --parallel "$build_jobs"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F -- 'cmake --build "$build_dir" --target cnc_sim_wasm_runtime_probe --parallel "$build_jobs"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
grep -F -- 'mkdir -p web/public' build-wasm.sh >/dev/null
|
grep -F -- 'mkdir -p web/public' build-wasm.sh >/dev/null
|
||||||
grep -F -- 'cp build/wasm/cnc_sim.js web/public/cnc_sim.js' build-wasm.sh >/dev/null
|
grep -F -- 'cp build/wasm/cnc_sim.js web/public/cnc_sim.js' build-wasm.sh >/dev/null
|
||||||
grep -F -- 'cp build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm' build-wasm.sh >/dev/null
|
grep -F -- 'cp build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm' build-wasm.sh >/dev/null
|
||||||
@@ -217,6 +338,41 @@ grep -F -- 'cmp -s build/wasm/cnc_sim.js web/public/cnc_sim.js' build-wasm.sh >/
|
|||||||
grep -F -- 'cmp -s build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm' build-wasm.sh >/dev/null
|
grep -F -- 'cmp -s build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm' build-wasm.sh >/dev/null
|
||||||
grep -F -- 'node test-web-wasm-node-smoke.cjs' build-wasm.sh >/dev/null
|
grep -F -- 'node test-web-wasm-node-smoke.cjs' build-wasm.sh >/dev/null
|
||||||
grep -F -- './test-web-wasm-browser-smoke.sh' build-wasm.sh >/dev/null
|
grep -F -- './test-web-wasm-browser-smoke.sh' build-wasm.sh >/dev/null
|
||||||
|
grep -F 'const opfsOptions = options.opfs ?? (isOpfsAvailable() ? {} : false)' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'OPFS cannot be disabled in browser contexts with OPFS support' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'async function opfsEntryExists(workspacePath, relativePath)' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'async function statOpfsEntry(workspacePath, relativePath)' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'async function readOpfsDirectoryTree(workspacePath, relativePath, relativeRoot = "")' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'function readWasmDirectoryTree(module, rootPath, relativeRoot = "")' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'function writeWasmFileReplacingPath(module, path, data)' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'copyFile(fromPath: string, toPath: string): Promise<Uint8Array>;' web/src/wasm-core.d.ts >/dev/null
|
||||||
|
grep -F 'moveFile(fromPath: string, toPath: string): Promise<void>;' web/src/wasm-core.d.ts >/dev/null
|
||||||
|
grep -F 'exists(path: string): Promise<boolean>;' web/src/wasm-core.d.ts >/dev/null
|
||||||
|
grep -F 'stat(path: string): Promise<WasmOpfsEntryStat>;' web/src/wasm-core.d.ts >/dev/null
|
||||||
|
grep -F 'LinuxCNC source basis: rs274ngc_pre.cc restore_parameters() reads the' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'before replacing the main file and managing filename + ".bak".' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'await removeOpfsEntry(workspacePath, `${path}.new`, false);' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'OPFS workspace is required for browser program parsing' web/src/app.js >/dev/null
|
||||||
|
grep -F 'const events = await simulator.parseFileWithParameterFile(programPath, parameterPath, "linuxcnc", parseOptions);' web/src/app.js >/dev/null
|
||||||
|
grep -F 'default browser simulator did not create an OPFS workspace' web/test-browser-wasm-smoke-opfs-policy-sections.js >/dev/null
|
||||||
|
grep -F 'browser simulator allowed OPFS to be disabled' web/test-browser-wasm-smoke-opfs-policy-sections.js >/dev/null
|
||||||
|
grep -F 'app frame did not restore current program from OPFS after reload' web/test-browser-wasm-smoke-app-sections.js >/dev/null
|
||||||
|
grep -F 'app frame did not report OPFS program restore after reload' web/test-browser-wasm-smoke-app-sections.js >/dev/null
|
||||||
|
grep -F 'app frame did not persist LinuxCNC parameter state into OPFS' web/test-browser-wasm-smoke-app-sections.js >/dev/null
|
||||||
|
grep -F 'app frame did not reload LinuxCNC parameter state from OPFS after reload' web/test-browser-wasm-smoke-app-sections.js >/dev/null
|
||||||
|
grep -F '"parameters/missing-main.var.new", "stale temporary without main file' web/test-browser-wasm-smoke-opfs-parameter-sections.js >/dev/null
|
||||||
|
grep -F 'OPFS copyFile did not create the backup file' web/test-browser-wasm-smoke-opfs-mirror-sections.js >/dev/null
|
||||||
|
grep -F 'OPFS copyFile did not update the WASM mirror destination' web/test-browser-wasm-smoke-opfs-mirror-sections.js >/dev/null
|
||||||
|
grep -F 'OPFS moveFile did not replace the destination file' web/test-browser-wasm-smoke-opfs-mirror-sections.js >/dev/null
|
||||||
|
grep -F 'OPFS moveFile left the WASM mirror source behind' web/test-browser-wasm-smoke-opfs-mirror-sections.js >/dev/null
|
||||||
|
grep -F 'OPFS metadata queries polluted the WASM filesystem mirror' web/test-browser-wasm-smoke-opfs-directory-sections.js >/dev/null
|
||||||
|
grep -F 'OPFS persistDirectory did not preserve an empty directory' web/test-browser-wasm-smoke-opfs-directory-sections.js >/dev/null
|
||||||
|
grep -F 'OPFS readDirectory did not mirror an empty directory into WASM FS' web/test-browser-wasm-smoke-opfs-directory-sections.js >/dev/null
|
||||||
|
grep -F 'OPFS readFile did not replace the WASM mirror directory with a file' web/test-browser-wasm-smoke-opfs-directory-sections.js >/dev/null
|
||||||
|
grep -F 'stale LinuxCNC missing-parameter temporary file remained in OPFS' web/test-browser-wasm-smoke-opfs-parameter-sections.js >/dev/null
|
||||||
|
grep -F 'stale LinuxCNC missing-parameter temporary file remained in WASM FS' web/test-browser-wasm-smoke-opfs-parameter-sections.js >/dev/null
|
||||||
|
grep -F 'LinuxCNC out-of-order parameter file remained in WASM FS after restore failure' web/test-browser-wasm-smoke-opfs-parameter-sections.js >/dev/null
|
||||||
|
grep -F 'LinuxCNC out-of-range parameter file remained in WASM FS after restore failure' web/test-browser-wasm-smoke-opfs-parameter-sections.js >/dev/null
|
||||||
if [[ "$(grep -Fc 'cp build/wasm/cnc_sim.js' build-wasm.sh)" -ne 1 ]]; then
|
if [[ "$(grep -Fc 'cp build/wasm/cnc_sim.js' build-wasm.sh)" -ne 1 ]]; then
|
||||||
echo "build-wasm should copy cnc_sim.js exactly once" >&2
|
echo "build-wasm should copy cnc_sim.js exactly once" >&2
|
||||||
exit 1
|
exit 1
|
||||||
@@ -251,8 +407,8 @@ if ! awk '
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if ! awk '
|
if ! awk '
|
||||||
index($0, "cmake --build build/wasm --target cnc_sim_wasm_runtime_probe") { runtime_probe_line = NR }
|
index($0, "cmake --build build/wasm --target cnc_sim_wasm_runtime_probe --parallel \"$build_jobs\"") { runtime_probe_line = NR }
|
||||||
index($0, "cmake --build build/wasm --target cnc_sim_wasm") &&
|
index($0, "cmake --build build/wasm --target cnc_sim_wasm --parallel \"$build_jobs\"") &&
|
||||||
!index($0, "cnc_sim_wasm_runtime_probe") { wasm_line = NR }
|
!index($0, "cnc_sim_wasm_runtime_probe") { wasm_line = NR }
|
||||||
END { exit !(runtime_probe_line && wasm_line && runtime_probe_line < wasm_line) }
|
END { exit !(runtime_probe_line && wasm_line && runtime_probe_line < wasm_line) }
|
||||||
' build-wasm.sh; then
|
' build-wasm.sh; then
|
||||||
@@ -838,10 +994,10 @@ cmake -S core -B "$build_dir" \
|
|||||||
-DCNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST="$manifest" \
|
-DCNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST="$manifest" \
|
||||||
-DCNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE=ON
|
-DCNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE=ON
|
||||||
|
|
||||||
cmake --build "$build_dir" --target linuxcnc_rs274_wasm_safe_probe_objects
|
cmake --build "$build_dir" --target linuxcnc_rs274_wasm_safe_probe_objects --parallel "$build_jobs"
|
||||||
cmake --build "$build_dir" --target linuxcnc_rs274_wasm_safe_probe
|
cmake --build "$build_dir" --target linuxcnc_rs274_wasm_safe_probe --parallel "$build_jobs"
|
||||||
"$build_dir/linuxcnc_rs274_wasm_safe_probe"
|
"$build_dir/linuxcnc_rs274_wasm_safe_probe"
|
||||||
cmake --build "$build_dir" --target cnc_sim_wasm_runtime_probe
|
cmake --build "$build_dir" --target cnc_sim_wasm_runtime_probe --parallel "$build_jobs"
|
||||||
"$build_dir/cnc_sim_wasm_runtime_probe"
|
"$build_dir/cnc_sim_wasm_runtime_probe"
|
||||||
|
|
||||||
echo "linuxcnc wasm-safe CMake probe target passed"
|
echo "linuxcnc wasm-safe CMake probe target passed"
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ nm --defined-only "$build_dir"/*.o \
|
|||||||
| awk '
|
| awk '
|
||||||
$0 ~ /^(_GLOBAL_OFFSET_TABLE_|_Unwind_Resume)$/ { next }
|
$0 ~ /^(_GLOBAL_OFFSET_TABLE_|_Unwind_Resume)$/ { next }
|
||||||
$0 ~ /^(__assert_fail|__cxa_|__divdc3|__dso_handle|__errno_location|__gxx_personality_v0|__isoc23_|__muldc3|__stack_chk_fail)/ { next }
|
$0 ~ /^(__assert_fail|__cxa_|__divdc3|__dso_handle|__errno_location|__gxx_personality_v0|__isoc23_|__muldc3|__stack_chk_fail)/ { next }
|
||||||
$0 ~ /^(abort|access|acos|asin|atan2|atof|atoi|basename|cabs|calloc|ceil|close|closedir|copysign|cos|cosl|exit|exp|fclose|fdatasync|fdopen|feof|fflush|fgetc|fgets|fileno|floor|fmax|fmod|fopen|fprintf|fputs|free|freelocale|fseek|fstat|ftell|fwrite|getcwd|getenv|getpid|gettimeofday|hypot|islower|isprint|isspace|isupper|isxdigit|link|localtime|log|lstat|malloc|memchr|memcmp|memcpy|memmove|memset|mkstemp|nearbyint|newlocale|open|opendir|perror|pow|printf|pthread_mutex_lock|pthread_mutex_unlock|putc|puts|read|readdir|realpath|realloc|remove|rename|setlocale|sin|sinl|snprintf|sprintf|sqrt|sscanf|stat|stderr|stdout|strcasecmp|strcat|strchr|strcmp|strcpy|strdup|strerror|strlen|strncasecmp|strncat|strncmp|strncpy|strrchr|strspn|strstr|strtod|strtok_r|strtol|strtoul|tan|tanl|tolower|toupper|towlower|unlink|uselocale|vfprintf|vsnprintf|wordexp|wordfree)$/ { next }
|
$0 ~ /^(abort|access|acos|asin|atan2|atof|atoi|basename|cabs|calloc|ceil|close|closedir|copysign|cos|cosl|exit|exp|fclose|fdatasync|fdopen|feof|fflush|fgetc|fgets|fileno|floor|fmax|fmod|fopen|fprintf|fputs|free|freelocale|fseek|fstat|ftell|fwrite|getcwd|getenv|getpid|gettimeofday|hypot|isalnum|isalpha|islower|isprint|isspace|isupper|isxdigit|link|localtime|log|lstat|malloc|memchr|memcmp|memcpy|memmove|memset|mkstemp|nearbyint|newlocale|open|opendir|perror|pow|printf|pthread_mutex_lock|pthread_mutex_unlock|putc|puts|read|readdir|realpath|realloc|remove|rename|setlocale|sin|sinl|snprintf|sprintf|sqrt|sscanf|stat|stderr|stdout|strcasecmp|strcat|strchr|strcmp|strcpy|strdup|strerror|strlen|strncasecmp|strncat|strncmp|strncpy|strrchr|strspn|strstr|strtod|strtok_r|strtol|strtoul|tan|tanl|tolower|toupper|towlower|unlink|uselocale|vfprintf|vsnprintf|wordexp|wordfree)$/ { next }
|
||||||
$0 ~ /^(_ZN3fmt|_ZSt|_ZNSt|_ZNKSt|_ZNKRSt|_ZNS|_ZTI|_ZTS|_ZTV|_Zdl|_Znwm|_Znam|_Zda|_ZdaPv|_ZdaPvm|_ZdlPvm|_ZTv|_ZTh)/ { next }
|
$0 ~ /^(_ZN3fmt|_ZSt|_ZNSt|_ZNKSt|_ZNKRSt|_ZNS|_ZTI|_ZTS|_ZTV|_Zdl|_Znwm|_Znam|_Zda|_ZdaPv|_ZdaPvm|_ZdlPvm|_ZTv|_ZTh)/ { next }
|
||||||
{ print }
|
{ print }
|
||||||
'
|
'
|
||||||
|
|||||||
379
test-native.sh
379
test-native.sh
@@ -4,24 +4,86 @@ set -euo pipefail
|
|||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
cxx=${CXX:-g++}
|
cxx=${CXX:-g++}
|
||||||
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_native.XXXXXX")
|
build_jobs=${CNC_SIM_BUILD_JOBS:-8}
|
||||||
trap 'rm -rf "$build_dir"' EXIT
|
if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then
|
||||||
|
echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
build_dir=${CNC_SIM_NATIVE_BUILD_DIR-build/native-test}
|
||||||
|
if [[ -z "$build_dir" ]]; then
|
||||||
|
echo "CNC_SIM_NATIVE_BUILD_DIR must not be empty" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ "$build_dir" =~ [[:space:]] ]]; then
|
||||||
|
echo "CNC_SIM_NATIVE_BUILD_DIR must not contain whitespace: $build_dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
mkdir -p "$build_dir"
|
||||||
|
build_dir=$(cd "$build_dir" && pwd)
|
||||||
|
if [[ "$build_dir" == "/" ]]; then
|
||||||
|
echo "CNC_SIM_NATIVE_BUILD_DIR must not be the filesystem root" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
exec 9>"$build_dir/native-test.lock"
|
||||||
|
flock 9
|
||||||
|
|
||||||
./check-linuxcnc-inputs.sh linuxcnc-rs274-source-files.txt --require-rs274-complete
|
./check-linuxcnc-inputs.sh linuxcnc-rs274-source-files.txt --require-rs274-complete
|
||||||
./check-linuxcnc-inputs.sh linuxcnc-rs274-wasm-source-files.txt --require-rs274-complete
|
./check-linuxcnc-inputs.sh linuxcnc-rs274-wasm-source-files.txt --require-rs274-complete
|
||||||
./check-linuxcnc-inputs.sh linuxcnc-kinematics-source-files.txt --require-kinematics-complete
|
./check-linuxcnc-inputs.sh linuxcnc-kinematics-source-files.txt --require-kinematics-complete
|
||||||
./check-linuxcnc-switchkins-remap-table.sh linuxcnc-kinematics-source-files.txt
|
./check-linuxcnc-switchkins-remap-table.sh linuxcnc-kinematics-source-files.txt
|
||||||
grep -F '#include "linuxcnc_switchkins_remap_table.inc"' core/src/cnc_sim_api.cpp >/dev/null
|
grep -F '#include "linuxcnc_switchkins_remap_table.inc"' core/src/cnc_sim_api.cpp >/dev/null
|
||||||
grep -F 'switchkins: options.switchkins' web/src/wasm-core.js >/dev/null
|
grep -F 'switchkins: options.switchkins' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'remap: options.remap' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'iniFile: options.iniFile' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'iniFileName: options.iniFileName' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'INI_FILE_NAME: options.INI_FILE_NAME' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'halFile: options.halFile' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'postguiHalFile: options.postguiHalFile' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'POSTGUI_HALFILE: options.POSTGUI_HALFILE' web/src/wasm-core.js >/dev/null
|
||||||
grep -F 'kinematics: options.kinematics' web/src/wasm-core.js >/dev/null
|
grep -F 'kinematics: options.kinematics' web/src/wasm-core.js >/dev/null
|
||||||
grep -F 'pivotLength: options.pivotLength' web/src/wasm-core.js >/dev/null
|
grep -F 'pivotLength: options.pivotLength' web/src/wasm-core.js >/dev/null
|
||||||
grep -F 'xyzbcTrt: options.xyzbcTrt' web/src/wasm-core.js >/dev/null
|
grep -F 'xyzbcTrt: options.xyzbcTrt' web/src/wasm-core.js >/dev/null
|
||||||
grep -F 'globalThis.createCncSimModule' web/src/wasm-core.js >/dev/null
|
grep -F 'globalThis.createCncSimModule' web/src/wasm-core.js >/dev/null
|
||||||
grep -F 'createModule(moduleOptions)' web/src/wasm-core.js >/dev/null
|
grep -F 'createModule(moduleOptions)' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'async parseFile(path, dialect = "linuxcnc", options = {})' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'async parseWithParameterFile(program, parameterPath, dialect = "linuxcnc", options = {})' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'async parseFileWithParameterFile(path, parameterPath, dialect = "linuxcnc", options = {})' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'LINUXCNC_DEFAULT_PARAMETER_FILE = "rs274ngc.var"' web/src/wasm-core.js >/dev/null
|
||||||
grep -F 'export type CncParseOptions' web/src/index.ts >/dev/null
|
grep -F 'export type CncParseOptions' web/src/index.ts >/dev/null
|
||||||
|
grep -F 'switchkins?: string;' web/src/index.ts >/dev/null
|
||||||
|
grep -F 'remap?: string;' web/src/index.ts >/dev/null
|
||||||
|
grep -F 'iniFile?: string;' web/src/index.ts >/dev/null
|
||||||
|
grep -F 'iniFileName?: string;' web/src/index.ts >/dev/null
|
||||||
|
grep -F 'INI_FILE_NAME?: string;' web/src/index.ts >/dev/null
|
||||||
|
grep -F 'halFile?: string;' web/src/index.ts >/dev/null
|
||||||
|
grep -F 'postguiHalFile?: string;' web/src/index.ts >/dev/null
|
||||||
|
grep -F 'POSTGUI_HALFILE?: string;' web/src/index.ts >/dev/null
|
||||||
grep -F '): CncEvent[];' web/src/index.ts >/dev/null
|
grep -F '): CncEvent[];' web/src/index.ts >/dev/null
|
||||||
grep -F 'options?: CncParseOptions' web/src/wasm-core.d.ts >/dev/null
|
grep -F 'options?: CncParseOptions' web/src/wasm-core.d.ts >/dev/null
|
||||||
|
grep -F 'parseFile(path: string, dialect?: CncDialect, options?: CncParseOptions): Promise<CncEvent[]>;' web/src/wasm-core.d.ts >/dev/null
|
||||||
|
grep -F 'parseWithParameterFile(' web/src/wasm-core.d.ts >/dev/null
|
||||||
|
grep -F 'parseFileWithParameterFile(' web/src/wasm-core.d.ts >/dev/null
|
||||||
|
grep -F 'copyFile(fromPath: string, toPath: string): Promise<Uint8Array>;' web/src/wasm-core.d.ts >/dev/null
|
||||||
|
grep -F 'moveFile(fromPath: string, toPath: string): Promise<void>;' web/src/wasm-core.d.ts >/dev/null
|
||||||
|
grep -F 'exists(path: string): Promise<boolean>;' web/src/wasm-core.d.ts >/dev/null
|
||||||
|
grep -F 'stat(path: string): Promise<WasmOpfsEntryStat>;' web/src/wasm-core.d.ts >/dev/null
|
||||||
|
grep -F 'loadParameterFile(path: string): Promise<Uint8Array | null>;' web/src/wasm-core.d.ts >/dev/null
|
||||||
|
grep -F 'async function opfsEntryExists(workspacePath, relativePath)' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'async function statOpfsEntry(workspacePath, relativePath)' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'async function readOpfsDirectoryTree(workspacePath, relativePath, relativeRoot = "")' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'function readWasmDirectoryTree(module, rootPath, relativeRoot = "")' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'function writeWasmFileReplacingPath(module, path, data)' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F '`${LINUXCNC_DEFAULT_PARAMETER_FILE}.bak`' web/src/wasm-core.js >/dev/null
|
||||||
grep -F 'export type WasmModuleOptions' web/src/wasm-core.d.ts >/dev/null
|
grep -F 'export type WasmModuleOptions' web/src/wasm-core.d.ts >/dev/null
|
||||||
grep -F 'printErr?: (text: string) => void;' web/src/wasm-core.d.ts >/dev/null
|
grep -F 'printErr?: (text: string) => void;' web/src/wasm-core.d.ts >/dev/null
|
||||||
|
grep -F '"field":"switchkins"' web/public/linuxcnc_switchkins_remap_config_cases.json >/dev/null
|
||||||
|
grep -F '"field":"remap"' web/public/linuxcnc_switchkins_remap_config_cases.json >/dev/null
|
||||||
|
grep -F '"field":"postguiHalFile"' web/public/linuxcnc_switchkins_remap_config_cases.json >/dev/null
|
||||||
|
grep -F '"field":"POSTGUI_HALFILE"' web/public/linuxcnc_switchkins_remap_config_cases.json >/dev/null
|
||||||
|
grep -F '{"switchkins", ' core/tests/linuxcnc_switchkins_remap_config_cases.inc >/dev/null
|
||||||
|
grep -F '{"remap", ' core/tests/linuxcnc_switchkins_remap_config_cases.inc >/dev/null
|
||||||
|
grep -F '{"postguiHalFile", ' core/tests/linuxcnc_switchkins_remap_config_cases.inc >/dev/null
|
||||||
|
grep -F '{"POSTGUI_HALFILE", ' core/tests/linuxcnc_switchkins_remap_config_cases.inc >/dev/null
|
||||||
grep -F '"moduleResolution": "Bundler"' web/tsconfig.json >/dev/null
|
grep -F '"moduleResolution": "Bundler"' web/tsconfig.json >/dev/null
|
||||||
grep -F '"include": ["src/index.ts", "src/**/*.d.ts"]' web/tsconfig.json >/dev/null
|
grep -F '"include": ["src/index.ts", "src/**/*.d.ts"]' web/tsconfig.json >/dev/null
|
||||||
grep -F 'createWasmSimulator' test-web-wasm-node-smoke.cjs >/dev/null
|
grep -F 'createWasmSimulator' test-web-wasm-node-smoke.cjs >/dev/null
|
||||||
@@ -29,10 +91,90 @@ grep -F 'locateFile: (file) => path.join(wasmDir, file)' test-web-wasm-node-smok
|
|||||||
grep -F 'printErr: () => {}' test-web-wasm-node-smoke.cjs >/dev/null
|
grep -F 'printErr: () => {}' test-web-wasm-node-smoke.cjs >/dev/null
|
||||||
grep -F 'expected web wasm-core options to pass LinuxCNC xyzac-trt RTCP config into WASM' test-web-wasm-node-smoke.cjs >/dev/null
|
grep -F 'expected web wasm-core options to pass LinuxCNC xyzac-trt RTCP config into WASM' test-web-wasm-node-smoke.cjs >/dev/null
|
||||||
grep -F 'node test-web-wasm-node-smoke.cjs' test-web-wasm-node-smoke.sh >/dev/null
|
grep -F 'node test-web-wasm-node-smoke.cjs' test-web-wasm-node-smoke.sh >/dev/null
|
||||||
|
grep -F 'WASM_NODE_SMOKE_MIN_STEPS:-50' test-web-wasm-node-smoke.sh >/dev/null
|
||||||
|
grep -F 'web wasm node smoke passed ([0-9][0-9]* steps)' test-web-wasm-node-smoke.sh >/dev/null
|
||||||
grep -F 'createWasmSimulator' web/test-browser-wasm-smoke.html >/dev/null
|
grep -F 'createWasmSimulator' web/test-browser-wasm-smoke.html >/dev/null
|
||||||
|
grep -F 'runBrowserWasmSmoke' web/test-browser-wasm-smoke.html >/dev/null
|
||||||
|
grep -F 'runBrowserOpfsWorkspaceSections' web/test-browser-wasm-smoke-sections.js >/dev/null
|
||||||
|
grep -F 'runBrowserOpfsPolicySections' web/test-browser-wasm-smoke-sections.js >/dev/null
|
||||||
|
grep -F 'runBrowserAppOpfsSections' web/test-browser-wasm-smoke-sections.js >/dev/null
|
||||||
|
grep -F 'runLinuxCncBrowserSections' web/test-browser-wasm-smoke-sections.js >/dev/null
|
||||||
|
grep -F 'runBrowserOpfsBasicSections' web/test-browser-wasm-smoke-opfs-workspace-sections.js >/dev/null
|
||||||
|
grep -F 'runBrowserOpfsParameterSections' web/test-browser-wasm-smoke-opfs-workspace-sections.js >/dev/null
|
||||||
|
grep -F 'runBrowserOpfsMirrorSections' web/test-browser-wasm-smoke-opfs-workspace-sections.js >/dev/null
|
||||||
|
grep -F 'runBrowserOpfsDirectorySections' web/test-browser-wasm-smoke-opfs-workspace-sections.js >/dev/null
|
||||||
|
grep -F 'await simulator.parseFile("programs/browser-smoke.ngc", "linuxcnc"' web/test-browser-wasm-smoke-opfs-basic-sections.js >/dev/null
|
||||||
|
grep -F 'await simulator.parseWithParameterFile(' web/test-browser-wasm-smoke-opfs-parameter-sections.js >/dev/null
|
||||||
|
grep -F 'await simulator.parseFileWithParameterFile(' web/test-browser-wasm-smoke-opfs-parameter-sections.js >/dev/null
|
||||||
|
grep -F 'OPFS copyFile did not create the backup file' web/test-browser-wasm-smoke-opfs-mirror-sections.js >/dev/null
|
||||||
|
grep -F 'OPFS copyFile did not update the WASM mirror destination' web/test-browser-wasm-smoke-opfs-mirror-sections.js >/dev/null
|
||||||
|
grep -F 'OPFS moveFile did not replace the destination file' web/test-browser-wasm-smoke-opfs-mirror-sections.js >/dev/null
|
||||||
|
grep -F 'OPFS moveFile left the WASM mirror source behind' web/test-browser-wasm-smoke-opfs-mirror-sections.js >/dev/null
|
||||||
|
grep -F 'OPFS metadata queries polluted the WASM filesystem mirror' web/test-browser-wasm-smoke-opfs-directory-sections.js >/dev/null
|
||||||
|
grep -F 'OPFS persistDirectory did not preserve an empty directory' web/test-browser-wasm-smoke-opfs-directory-sections.js >/dev/null
|
||||||
|
grep -F 'OPFS readDirectory did not mirror an empty directory into WASM FS' web/test-browser-wasm-smoke-opfs-directory-sections.js >/dev/null
|
||||||
|
grep -F 'OPFS readFile did not replace the WASM mirror directory with a file' web/test-browser-wasm-smoke-opfs-directory-sections.js >/dev/null
|
||||||
|
grep -F '"parameters/rs274ngc.var"' web/test-browser-wasm-smoke-opfs-parameter-sections.js >/dev/null
|
||||||
|
grep -F '"parameters/rs274ngc.var.bak"' web/test-browser-wasm-smoke-opfs-parameter-sections.js >/dev/null
|
||||||
|
grep -F 'const opfsOptions = options.opfs ?? (isOpfsAvailable() ? {} : false)' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'OPFS cannot be disabled in browser contexts with OPFS support' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'LinuxCNC source basis: rs274ngc_pre.cc restore_parameters() reads the' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'before replacing the main file and managing filename + ".bak".' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'await removeOpfsEntry(workspacePath, `${path}.new`, false);' web/src/wasm-core.js >/dev/null
|
||||||
|
grep -F 'OPFS workspace is required for browser program parsing' web/src/app.js >/dev/null
|
||||||
|
grep -F 'const events = await simulator.parseFileWithParameterFile(programPath, parameterPath, "linuxcnc", parseOptions);' web/src/app.js >/dev/null
|
||||||
|
grep -F 'default browser simulator did not create an OPFS workspace' web/test-browser-wasm-smoke-opfs-policy-sections.js >/dev/null
|
||||||
|
grep -F 'browser simulator allowed OPFS to be disabled' web/test-browser-wasm-smoke-opfs-policy-sections.js >/dev/null
|
||||||
|
grep -F 'app frame did not restore current program from OPFS after reload' web/test-browser-wasm-smoke-app-sections.js >/dev/null
|
||||||
|
grep -F 'app frame did not report OPFS program restore after reload' web/test-browser-wasm-smoke-app-sections.js >/dev/null
|
||||||
|
grep -F 'app frame did not persist LinuxCNC parameter state into OPFS' web/test-browser-wasm-smoke-app-sections.js >/dev/null
|
||||||
|
grep -F 'app frame did not reload LinuxCNC parameter state from OPFS after reload' web/test-browser-wasm-smoke-app-sections.js >/dev/null
|
||||||
|
grep -F '"parameters/missing-main.var.new", "stale temporary without main file' web/test-browser-wasm-smoke-opfs-parameter-sections.js >/dev/null
|
||||||
|
grep -F 'stale LinuxCNC missing-parameter temporary file remained in OPFS' web/test-browser-wasm-smoke-opfs-parameter-sections.js >/dev/null
|
||||||
|
grep -F 'stale LinuxCNC missing-parameter temporary file remained in WASM FS' web/test-browser-wasm-smoke-opfs-parameter-sections.js >/dev/null
|
||||||
|
grep -F 'LinuxCNC out-of-order parameter file remained in WASM FS after restore failure' web/test-browser-wasm-smoke-opfs-parameter-sections.js >/dev/null
|
||||||
|
grep -F 'LinuxCNC out-of-range parameter file remained in WASM FS after restore failure' web/test-browser-wasm-smoke-opfs-parameter-sections.js >/dev/null
|
||||||
|
grep -F 'browser wasm smoke passed' web/test-browser-wasm-smoke-helpers.js >/dev/null
|
||||||
grep -F 'browser wasm smoke passed' web/test-browser-wasm-smoke.html >/dev/null
|
grep -F 'browser wasm smoke passed' web/test-browser-wasm-smoke.html >/dev/null
|
||||||
grep -F 'chromium chromium-browser google-chrome' test-web-wasm-browser-smoke.sh >/dev/null
|
grep -F 'chromium chromium-browser google-chrome' test-web-wasm-browser-smoke.sh >/dev/null
|
||||||
grep -F 'browser wasm smoke passed' test-web-wasm-browser-smoke.sh >/dev/null
|
grep -F 'browser wasm smoke passed' test-web-wasm-browser-smoke.sh >/dev/null
|
||||||
|
grep -F 'BROWSER_SMOKE_MIN_SECTIONS:-50' test-web-wasm-browser-smoke.sh >/dev/null
|
||||||
|
grep -F 'browser wasm smoke passed ([0-9][0-9]* sections)' test-web-wasm-browser-smoke.sh >/dev/null
|
||||||
|
python3 - <<'PY'
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
node_steps = len(re.findall(r'await\s+runStep\(', Path("test-web-wasm-node-smoke.cjs").read_text(encoding="utf-8")))
|
||||||
|
browser_sections = sum(
|
||||||
|
len(re.findall(r'await\s+runSection\(', path.read_text(encoding="utf-8")))
|
||||||
|
for path in Path("web").glob("test-browser-wasm-smoke-*-sections.js")
|
||||||
|
)
|
||||||
|
total = node_steps + browser_sections
|
||||||
|
if total < 50:
|
||||||
|
print(
|
||||||
|
f"expected Node/browser smoke to keep at least 50 explicit closed-loop steps/sections, got {total}",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
html_lines = Path("web/test-browser-wasm-smoke.html").read_text(encoding="utf-8").count("\n") + 1
|
||||||
|
if html_lines > 80:
|
||||||
|
print(
|
||||||
|
f"expected browser smoke HTML to stay as a small loader, got {html_lines} lines",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
for path in sorted(Path("web").glob("test-browser-wasm-smoke-*-sections.js")):
|
||||||
|
line_count = path.read_text(encoding="utf-8").count("\n") + 1
|
||||||
|
if line_count > 300:
|
||||||
|
print(
|
||||||
|
f"expected browser smoke section module {path} to stay below 300 lines, got {line_count}",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
PY
|
||||||
kinematics_manifest_sources="$build_dir/linuxcnc_kinematics_manifest_sources.txt"
|
kinematics_manifest_sources="$build_dir/linuxcnc_kinematics_manifest_sources.txt"
|
||||||
./list-linuxcnc-kinematics-manifest-sources.sh linuxcnc-kinematics-source-files.txt all > "$kinematics_manifest_sources"
|
./list-linuxcnc-kinematics-manifest-sources.sh linuxcnc-kinematics-source-files.txt all > "$kinematics_manifest_sources"
|
||||||
grep -Fx "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini" "$kinematics_manifest_sources" >/dev/null
|
grep -Fx "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini" "$kinematics_manifest_sources" >/dev/null
|
||||||
@@ -51,7 +193,21 @@ grep -Fx "configs/sim/axis/vismach/puma/puma560_uvw.ini" "$kinematics_manifest_s
|
|||||||
grep -Fx "configs/sim/axis/vismach/scara/scara.ini" "$kinematics_manifest_sources" >/dev/null
|
grep -Fx "configs/sim/axis/vismach/scara/scara.ini" "$kinematics_manifest_sources" >/dev/null
|
||||||
grep -Fx "configs/sim/qtaxis/non-trivial/scara/scara.ini" "$kinematics_manifest_sources" >/dev/null
|
grep -Fx "configs/sim/qtaxis/non-trivial/scara/scara.ini" "$kinematics_manifest_sources" >/dev/null
|
||||||
grep -Fx "configs/sim/qtvcp_screens/non-trivial/scara/scara.ini" "$kinematics_manifest_sources" >/dev/null
|
grep -Fx "configs/sim/qtvcp_screens/non-trivial/scara/scara.ini" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/axis/vismach/5axis/bridgemill/5axisgui.hal" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/axis/vismach/hexapod-sim/kinematics.hal" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/axis/vismach/melfa-sim/melfa_dh.hal" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/axis/vismach/millturn/millturn.hal" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/axis/vismach/puma/puma_dh.hal" "$kinematics_manifest_sources" >/dev/null
|
||||||
grep -Fx "configs/sim/axis/vismach/puma/puma560_dh.hal" "$kinematics_manifest_sources" >/dev/null
|
grep -Fx "configs/sim/axis/vismach/puma/puma560_dh.hal" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/axis/vismach/5axis/bridgemill/5axis_postgui.hal" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/axis/vismach/5axis/table-rotary-tilting/switchkins_postgui.hal" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn_postgui.hal" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/axis/vismach/melfa-sim/melfa-postgui.hal" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/axis/vismach/puma/puma560_postgui.hal" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
if [[ "$(grep -Ec '_postgui\.hal$|-postgui\.hal$' "$kinematics_manifest_sources")" -ne 11 ]]; then
|
||||||
|
echo "expected kinematics manifest to cover all LinuxCNC POSTGUI_HALFILE switchkins sources" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
grep -Fx "configs/sim/axis/vismach/5axis/bridgemill/remap_subs/428remap.ngc" "$kinematics_manifest_sources" >/dev/null
|
grep -Fx "configs/sim/axis/vismach/5axis/bridgemill/remap_subs/428remap.ngc" "$kinematics_manifest_sources" >/dev/null
|
||||||
grep -Fx "configs/sim/axis/vismach/5axis/table-dual-rotary/remap_subs/428remap.ngc" "$kinematics_manifest_sources" >/dev/null
|
grep -Fx "configs/sim/axis/vismach/5axis/table-dual-rotary/remap_subs/428remap.ngc" "$kinematics_manifest_sources" >/dev/null
|
||||||
grep -Fx "configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/430remap.ngc" "$kinematics_manifest_sources" >/dev/null
|
grep -Fx "configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/430remap.ngc" "$kinematics_manifest_sources" >/dev/null
|
||||||
@@ -73,43 +229,216 @@ grep -Fx "src/hal/components/xyzacb_trsrn.comp" "$kinematics_manifest_sources" >
|
|||||||
grep -Fx "src/objects/hal/components/xyzacb_trsrn.c" "$kinematics_manifest_sources" >/dev/null
|
grep -Fx "src/objects/hal/components/xyzacb_trsrn.c" "$kinematics_manifest_sources" >/dev/null
|
||||||
grep -Fx "src/hal/components/xyzbca_trsrn.comp" "$kinematics_manifest_sources" >/dev/null
|
grep -Fx "src/hal/components/xyzbca_trsrn.comp" "$kinematics_manifest_sources" >/dev/null
|
||||||
grep -Fx "src/objects/hal/components/xyzbca_trsrn.c" "$kinematics_manifest_sources" >/dev/null
|
grep -Fx "src/objects/hal/components/xyzbca_trsrn.c" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "src/hal/components/millturn.comp" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "src/objects/hal/components/millturn.c" "$kinematics_manifest_sources" >/dev/null
|
||||||
grep -Fx "src/hal/components/matrixkins.comp" "$kinematics_manifest_sources" >/dev/null
|
grep -Fx "src/hal/components/matrixkins.comp" "$kinematics_manifest_sources" >/dev/null
|
||||||
grep -Fx "src/objects/hal/components/matrixkins.c" "$kinematics_manifest_sources" >/dev/null
|
grep -Fx "src/objects/hal/components/matrixkins.c" "$kinematics_manifest_sources" >/dev/null
|
||||||
grep -Fx "src/hal/components/userkins.comp" "$kinematics_manifest_sources" >/dev/null
|
grep -Fx "src/hal/components/userkins.comp" "$kinematics_manifest_sources" >/dev/null
|
||||||
grep -Fx "src/objects/hal/components/userkins.c" "$kinematics_manifest_sources" >/dev/null
|
grep -Fx "src/objects/hal/components/userkins.c" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "src/objects/hal/components/millturn.mak" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "src/objects/hal/components/xyzab_tdr_kins.mak" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "src/objects/hal/components/xyzacb_trsrn.mak" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "src/objects/hal/components/xyzbca_trsrn.mak" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "src/objects/hal/components/matrixkins.mak" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "src/objects/hal/components/userkins.mak" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
if [[ "$(grep -Ec '^src/objects/hal/components/.*\.mak$' "$kinematics_manifest_sources")" -ne 6 ]]; then
|
||||||
|
echo "expected kinematics manifest to cover all generated halcompile make metadata sources" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -Fx "configs/sim/axis/vismach/5axis/bridgemill/5axis.tbl" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.tbl" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.tbl" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.tbl" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.tbl" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.tbl" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/axis/vismach/melfa-sim/melfa.tbl" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/axis/vismach/millturn/millturn.tbl" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/axis/vismach/puma/puma.tbl" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/qtaxis/non-trivial/scara/scara.tbl" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
if [[ "$(grep -Ec '\.tbl$' "$kinematics_manifest_sources")" -ne 10 ]]; then
|
||||||
|
echo "expected kinematics manifest to cover all resolvable LinuxCNC TOOL_TABLE switchkins sources" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -Fx "configs/sim/axis/vismach/5axis/bridgemill/README" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/axis/vismach/5axis/table-dual-rotary/README" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/axis/vismach/5axis/table-rotary-tilting/README" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/axis/vismach/hexapod-sim/README" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/axis/vismach/melfa-sim/README" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/axis/vismach/millturn/README" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/axis/vismach/puma/README" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "configs/sim/axis/vismach/scara/README" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
if [[ "$(grep -Ec '/README$' "$kinematics_manifest_sources")" -ne 10 ]]; then
|
||||||
|
echo "expected kinematics manifest to cover all LinuxCNC README sources adjacent to switchkins INI files" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -Fx "src/hal/user_comps/vismach/5axisgui.py" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "src/hal/user_comps/vismach/hexagui.py" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "src/hal/user_comps/vismach/melfagui.py" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "src/hal/user_comps/vismach/millturngui.py" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "src/hal/user_comps/vismach/puma560gui.py" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "src/hal/user_comps/vismach/pumagui.py" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "src/hal/user_comps/vismach/scaragui.py" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "src/hal/user_comps/vismach/xyzab-tdr-gui.py" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "src/hal/user_comps/vismach/xyzac-trt-gui.py" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "src/hal/user_comps/vismach/xyzbc-trt-gui.py" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
if [[ "$(grep -Ec '^src/hal/user_comps/vismach/.*gui\.py$|^src/hal/user_comps/vismach/xyz[abc-]+trt-gui\.py$|^src/hal/user_comps/vismach/xyzab-tdr-gui\.py$' "$kinematics_manifest_sources")" -ne 10 ]]; then
|
||||||
|
echo "expected kinematics manifest to cover all LinuxCNC vismach loadusr GUI sources" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -Fx "share/qtvcp/panels/vismach_scara/vismach_scara.ui" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "share/qtvcp/panels/vismach_scara/vismach_scara_handler.py" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "lib/python/qtvcp/lib/qt_vismach/README.txt" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "lib/python/qtvcp/lib/qt_vismach/__init__.py" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "lib/python/qtvcp/lib/qt_vismach/primitives.py" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "lib/python/qtvcp/lib/qt_vismach/qt_vismach.py" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "lib/python/qtvcp/lib/qt_vismach/scara.py" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
if [[ "$(grep -Ec '^(share/qtvcp/panels/vismach_scara/|lib/python/qtvcp/lib/qt_vismach/)' "$kinematics_manifest_sources")" -ne 7 ]]; then
|
||||||
|
echo "expected kinematics manifest to cover all LinuxCNC QtVCP vismach_scara panel sources" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -Fx "src/hal/user_comps/vismach/Submakefile" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "docs/src/man/man1/5axisgui.1.adoc" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "docs/src/man/man1/hexagui.1.adoc" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "docs/src/man/man1/melfagui.1.adoc" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "docs/src/man/man1/puma560gui.1.adoc" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "docs/src/man/man1/pumagui.1.adoc" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "docs/src/man/man1/scaragui.1.adoc" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "docs/src/man/man1/xyzab-tdr-gui.1.adoc" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "docs/src/man/man1/xyzac-trt-gui.1.adoc" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "docs/src/man/man1/xyzbc-trt-gui.1.adoc" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
if [[ "$(grep -Ec '^docs/src/man/man1/(5axisgui|hexagui|melfagui|puma560gui|pumagui|scaragui|xyzab-tdr-gui|xyzac-trt-gui|xyzbc-trt-gui)\.1\.adoc$' "$kinematics_manifest_sources")" -ne 9 ]]; then
|
||||||
|
echo "expected kinematics manifest to cover all LinuxCNC vismach GUI manpage sources" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -Fx "src/emc/kinematics/switchkins.h" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "src/emc/kinematics/Submakefile" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -Fx "src/emc/kinematics/blend.fig" "$kinematics_manifest_sources" >/dev/null
|
||||||
|
grep -F 'manifest does not group all LinuxCNC kinematics headers as header sources' check-linuxcnc-inputs.sh >/dev/null
|
||||||
|
grep -F 'manifest does not group all LinuxCNC kinematics build metadata as metadata sources' check-linuxcnc-inputs.sh >/dev/null
|
||||||
|
grep -F 'manifest does not group all LinuxCNC kinematics reference assets as asset sources' check-linuxcnc-inputs.sh >/dev/null
|
||||||
|
grep -F 'manifest does not cover all LinuxCNC component sources referenced by M428/M429/M430 INI KINEMATICS entries' check-linuxcnc-inputs.sh >/dev/null
|
||||||
|
grep -F 'manifest does not cover all LinuxCNC generated component sources referenced by M428/M429/M430 INI KINEMATICS entries' check-linuxcnc-inputs.sh >/dev/null
|
||||||
|
grep -F 'manifest does not cover all LinuxCNC generated component make metadata adjacent to generated kinematics sources' check-linuxcnc-inputs.sh >/dev/null
|
||||||
|
grep -F 'manifest does not cover all resolvable LinuxCNC TOOL_TABLE files referenced by M428/M429/M430 INI files as tooldata sources' check-linuxcnc-inputs.sh >/dev/null
|
||||||
|
grep -F 'manifest does not cover LinuxCNC README sources adjacent to M428/M429/M430 INI files as asset sources' check-linuxcnc-inputs.sh >/dev/null
|
||||||
|
grep -F 'manifest does not cover all LinuxCNC vismach user component sources referenced by M428/M429/M430 INI/HAL loadusr commands as asset sources' check-linuxcnc-inputs.sh >/dev/null
|
||||||
|
grep -F 'manifest does not cover LinuxCNC vismach user component build metadata as metadata sources' check-linuxcnc-inputs.sh >/dev/null
|
||||||
|
grep -F 'manifest does not cover LinuxCNC vismach user component manpage sources as metadata sources' check-linuxcnc-inputs.sh >/dev/null
|
||||||
|
grep -F 'manifest does not cover all LinuxCNC QtVCP vismach_scara sources referenced by M428/M429/M430 INI EMBED_TAB_COMMAND entries as asset sources' check-linuxcnc-inputs.sh >/dev/null
|
||||||
|
grep -F 'bad manifest line $manifest_line_number' check-linuxcnc-inputs.sh >/dev/null
|
||||||
|
grep -F 'manifest source must be LinuxCNC-root relative' check-linuxcnc-inputs.sh >/dev/null
|
||||||
|
grep -F 'missing manifest source: $path' check-linuxcnc-inputs.sh >/dev/null
|
||||||
core_source_list="$build_dir/cnc_sim_core_sources.txt"
|
core_source_list="$build_dir/cnc_sim_core_sources.txt"
|
||||||
./list-cnc-sim-core-sources.sh --with-smoke > "$core_source_list"
|
./list-cnc-sim-core-sources.sh --with-smoke > "$core_source_list"
|
||||||
mapfile -t core_sources < "$core_source_list"
|
mapfile -t core_sources < "$core_source_list"
|
||||||
smoke_cxxflags=(-std=c++17 -fpermissive -DCNC_SIM_ENABLE_SMOKE_BACKEND -I core/include -I core/src -I ../linuxcnc/include)
|
smoke_cxxflags=(-std=c++17 -fpermissive -DCNC_SIM_ENABLE_SMOKE_BACKEND -I core/include -I core/src -I ../linuxcnc/include)
|
||||||
|
|
||||||
"$cxx" "${smoke_cxxflags[@]}" \
|
native_signature="$build_dir/native-build.signature"
|
||||||
"${core_sources[@]}" \
|
native_next_signature="$build_dir/native-build.signature.next"
|
||||||
core/tests/canon_event_sink_smoke.cpp \
|
{
|
||||||
-o "$build_dir/canon_event_sink_smoke"
|
printf 'CXX=%s\n' "$cxx"
|
||||||
|
printf 'PWD=%s\n' "$PWD"
|
||||||
|
printf 'SMOKE_CXXFLAGS='
|
||||||
|
printf ' %s' "${smoke_cxxflags[@]}"
|
||||||
|
printf '\n'
|
||||||
|
printf 'CORE_SOURCES:\n'
|
||||||
|
printf '%s\n' "${core_sources[@]}"
|
||||||
|
} > "$native_next_signature"
|
||||||
|
if [[ ! -f "$native_signature" ]] || ! cmp -s "$native_signature" "$native_next_signature"; then
|
||||||
|
rm -f "$build_dir"/core_*.o \
|
||||||
|
"$build_dir"/*.d \
|
||||||
|
"$build_dir"/canon_event_sink_smoke.o \
|
||||||
|
"$build_dir"/rtcp_kinematics_smoke.o \
|
||||||
|
"$build_dir"/simulator_gcode_controls_smoke.o \
|
||||||
|
"$build_dir"/cnc_sim_api_smoke.o \
|
||||||
|
"$build_dir"/cnc_sim_dump.o \
|
||||||
|
"$build_dir"/linuxcnc_gees_table_smoke.o \
|
||||||
|
"$build_dir"/canon_event_sink_smoke \
|
||||||
|
"$build_dir"/rtcp_kinematics_smoke \
|
||||||
|
"$build_dir"/linuxcnc_gees_table_smoke \
|
||||||
|
"$build_dir"/simulator_gcode_controls_smoke \
|
||||||
|
"$build_dir"/cnc_sim_api_smoke \
|
||||||
|
"$build_dir"/cnc_sim_dump
|
||||||
|
fi
|
||||||
|
mv "$native_next_signature" "$native_signature"
|
||||||
|
|
||||||
"$cxx" "${smoke_cxxflags[@]}" \
|
core_objects=()
|
||||||
"${core_sources[@]}" \
|
dep_files=()
|
||||||
core/tests/rtcp_kinematics_smoke.cpp \
|
core_index=0
|
||||||
-o "$build_dir/rtcp_kinematics_smoke"
|
for source in "${core_sources[@]}"; do
|
||||||
|
obj="$build_dir/core_${core_index}.o"
|
||||||
|
core_objects+=("$obj")
|
||||||
|
dep_files+=("$obj.d")
|
||||||
|
core_index=$((core_index + 1))
|
||||||
|
done
|
||||||
|
for obj in \
|
||||||
|
"$build_dir/canon_event_sink_smoke.o" \
|
||||||
|
"$build_dir/rtcp_kinematics_smoke.o" \
|
||||||
|
"$build_dir/simulator_gcode_controls_smoke.o" \
|
||||||
|
"$build_dir/cnc_sim_api_smoke.o" \
|
||||||
|
"$build_dir/cnc_sim_dump.o" \
|
||||||
|
"$build_dir/linuxcnc_gees_table_smoke.o"
|
||||||
|
do
|
||||||
|
dep_files+=("$obj.d")
|
||||||
|
done
|
||||||
|
|
||||||
"$cxx" "${smoke_cxxflags[@]}" \
|
native_makefile="$build_dir/native.mk"
|
||||||
core/tests/linuxcnc_gees_table_smoke.cpp \
|
{
|
||||||
-o "$build_dir/linuxcnc_gees_table_smoke"
|
printf 'CXX := %s\n' "$cxx"
|
||||||
|
printf 'SMOKE_CXXFLAGS :='
|
||||||
|
printf ' %s' "${smoke_cxxflags[@]}"
|
||||||
|
printf '\n\n'
|
||||||
|
printf 'CORE_OBJECTS :='
|
||||||
|
printf ' %s' "${core_objects[@]}"
|
||||||
|
printf '\n\n'
|
||||||
|
printf 'DEP_FILES :='
|
||||||
|
printf ' %s' "${dep_files[@]}"
|
||||||
|
printf '\n\n'
|
||||||
|
printf '.PHONY: all\n'
|
||||||
|
printf 'all: %s %s %s %s %s %s\n\n' \
|
||||||
|
"$build_dir/canon_event_sink_smoke" \
|
||||||
|
"$build_dir/rtcp_kinematics_smoke" \
|
||||||
|
"$build_dir/linuxcnc_gees_table_smoke" \
|
||||||
|
"$build_dir/simulator_gcode_controls_smoke" \
|
||||||
|
"$build_dir/cnc_sim_api_smoke" \
|
||||||
|
"$build_dir/cnc_sim_dump"
|
||||||
|
|
||||||
"$cxx" "${smoke_cxxflags[@]}" \
|
core_index=0
|
||||||
"${core_sources[@]}" \
|
for source in "${core_sources[@]}"; do
|
||||||
core/tests/simulator_gcode_controls_smoke.cpp \
|
printf '%s: %s\n' "${core_objects[$core_index]}" "$source"
|
||||||
-o "$build_dir/simulator_gcode_controls_smoke"
|
printf '\t@$(CXX) $(SMOKE_CXXFLAGS) -MMD -MP -MF %s.d -c %s -o %s\n\n' \
|
||||||
|
"${core_objects[$core_index]}" "$source" "${core_objects[$core_index]}"
|
||||||
|
core_index=$((core_index + 1))
|
||||||
|
done
|
||||||
|
|
||||||
"$cxx" "${smoke_cxxflags[@]}" \
|
printf '%s: core/tests/canon_event_sink_smoke.cpp\n' "$build_dir/canon_event_sink_smoke.o"
|
||||||
"${core_sources[@]}" \
|
printf '\t@$(CXX) $(SMOKE_CXXFLAGS) -MMD -MP -MF %s.d -c core/tests/canon_event_sink_smoke.cpp -o %s\n\n' "$build_dir/canon_event_sink_smoke.o" "$build_dir/canon_event_sink_smoke.o"
|
||||||
core/tests/cnc_sim_api_smoke.cpp \
|
printf '%s: core/tests/rtcp_kinematics_smoke.cpp\n' "$build_dir/rtcp_kinematics_smoke.o"
|
||||||
-o "$build_dir/cnc_sim_api_smoke"
|
printf '\t@$(CXX) $(SMOKE_CXXFLAGS) -MMD -MP -MF %s.d -c core/tests/rtcp_kinematics_smoke.cpp -o %s\n\n' "$build_dir/rtcp_kinematics_smoke.o" "$build_dir/rtcp_kinematics_smoke.o"
|
||||||
|
printf '%s: core/tests/simulator_gcode_controls_smoke.cpp\n' "$build_dir/simulator_gcode_controls_smoke.o"
|
||||||
|
printf '\t@$(CXX) $(SMOKE_CXXFLAGS) -MMD -MP -MF %s.d -c core/tests/simulator_gcode_controls_smoke.cpp -o %s\n\n' "$build_dir/simulator_gcode_controls_smoke.o" "$build_dir/simulator_gcode_controls_smoke.o"
|
||||||
|
printf '%s: core/tests/cnc_sim_api_smoke.cpp\n' "$build_dir/cnc_sim_api_smoke.o"
|
||||||
|
printf '\t@$(CXX) $(SMOKE_CXXFLAGS) -MMD -MP -MF %s.d -c core/tests/cnc_sim_api_smoke.cpp -o %s\n\n' "$build_dir/cnc_sim_api_smoke.o" "$build_dir/cnc_sim_api_smoke.o"
|
||||||
|
printf '%s: core/tools/cnc_sim_dump.cpp\n' "$build_dir/cnc_sim_dump.o"
|
||||||
|
printf '\t@$(CXX) $(SMOKE_CXXFLAGS) -MMD -MP -MF %s.d -c core/tools/cnc_sim_dump.cpp -o %s\n\n' "$build_dir/cnc_sim_dump.o" "$build_dir/cnc_sim_dump.o"
|
||||||
|
printf '%s: core/tests/linuxcnc_gees_table_smoke.cpp\n' "$build_dir/linuxcnc_gees_table_smoke.o"
|
||||||
|
printf '\t@$(CXX) $(SMOKE_CXXFLAGS) -MMD -MP -MF %s.d -c core/tests/linuxcnc_gees_table_smoke.cpp -o %s\n\n' "$build_dir/linuxcnc_gees_table_smoke.o" "$build_dir/linuxcnc_gees_table_smoke.o"
|
||||||
|
|
||||||
"$cxx" "${smoke_cxxflags[@]}" \
|
printf '%s: %s\n' "$build_dir/linuxcnc_gees_table_smoke" "$build_dir/linuxcnc_gees_table_smoke.o"
|
||||||
"${core_sources[@]}" \
|
printf '\t@$(CXX) $(SMOKE_CXXFLAGS) %s -o %s\n\n' "$build_dir/linuxcnc_gees_table_smoke.o" "$build_dir/linuxcnc_gees_table_smoke"
|
||||||
core/tools/cnc_sim_dump.cpp \
|
printf '%s: $(CORE_OBJECTS) %s\n' "$build_dir/canon_event_sink_smoke" "$build_dir/canon_event_sink_smoke.o"
|
||||||
-o "$build_dir/cnc_sim_dump"
|
printf '\t@$(CXX) $(SMOKE_CXXFLAGS) $(CORE_OBJECTS) %s -o %s\n\n' "$build_dir/canon_event_sink_smoke.o" "$build_dir/canon_event_sink_smoke"
|
||||||
|
printf '%s: $(CORE_OBJECTS) %s\n' "$build_dir/rtcp_kinematics_smoke" "$build_dir/rtcp_kinematics_smoke.o"
|
||||||
|
printf '\t@$(CXX) $(SMOKE_CXXFLAGS) $(CORE_OBJECTS) %s -o %s\n\n' "$build_dir/rtcp_kinematics_smoke.o" "$build_dir/rtcp_kinematics_smoke"
|
||||||
|
printf '%s: $(CORE_OBJECTS) %s\n' "$build_dir/simulator_gcode_controls_smoke" "$build_dir/simulator_gcode_controls_smoke.o"
|
||||||
|
printf '\t@$(CXX) $(SMOKE_CXXFLAGS) $(CORE_OBJECTS) %s -o %s\n\n' "$build_dir/simulator_gcode_controls_smoke.o" "$build_dir/simulator_gcode_controls_smoke"
|
||||||
|
printf '%s: $(CORE_OBJECTS) %s\n' "$build_dir/cnc_sim_api_smoke" "$build_dir/cnc_sim_api_smoke.o"
|
||||||
|
printf '\t@$(CXX) $(SMOKE_CXXFLAGS) $(CORE_OBJECTS) %s -o %s\n\n' "$build_dir/cnc_sim_api_smoke.o" "$build_dir/cnc_sim_api_smoke"
|
||||||
|
printf '%s: $(CORE_OBJECTS) %s\n' "$build_dir/cnc_sim_dump" "$build_dir/cnc_sim_dump.o"
|
||||||
|
printf '\t@$(CXX) $(SMOKE_CXXFLAGS) $(CORE_OBJECTS) %s -o %s\n' "$build_dir/cnc_sim_dump.o" "$build_dir/cnc_sim_dump"
|
||||||
|
printf '\n-include $(DEP_FILES)\n'
|
||||||
|
} > "$native_makefile"
|
||||||
|
make --output-sync=target -j"$build_jobs" -f "$native_makefile"
|
||||||
|
|
||||||
"$build_dir/canon_event_sink_smoke"
|
"$build_dir/canon_event_sink_smoke"
|
||||||
"$build_dir/rtcp_kinematics_smoke"
|
"$build_dir/rtcp_kinematics_smoke"
|
||||||
|
|||||||
@@ -11,32 +11,197 @@ if [[ -z "$chromium_bin" ]]; then
|
|||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
elif [[ ! -x "$chromium_bin" ]] && command -v "$chromium_bin" >/dev/null 2>&1; then
|
||||||
|
chromium_bin=$(command -v "$chromium_bin")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
missing=()
|
missing=()
|
||||||
|
minimum_sections=${BROWSER_SMOKE_MIN_SECTIONS:-50}
|
||||||
|
if ! [[ "$minimum_sections" =~ ^[1-9][0-9]*$ ]]; then
|
||||||
|
echo "BROWSER_SMOKE_MIN_SECTIONS must be a positive integer: $minimum_sections" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
require_command() {
|
||||||
|
local command_name=$1
|
||||||
|
if ! command -v "$command_name" >/dev/null 2>&1; then
|
||||||
|
missing+=("$command_name")
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
require_file() {
|
||||||
|
local file_path=$1
|
||||||
|
if [[ ! -f "$file_path" ]]; then
|
||||||
|
missing+=("$file_path")
|
||||||
|
elif [[ ! -r "$file_path" || ! -s "$file_path" ]]; then
|
||||||
|
missing+=("readable non-empty $file_path")
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
report_missing_prerequisites() {
|
||||||
|
echo "WASM browser smoke prerequisites are missing: ${missing[*]}." >&2
|
||||||
|
echo "Run ./build-wasm.sh after activating emsdk to generate web/public/cnc_sim.js and web/public/cnc_sim.wasm." >&2
|
||||||
|
}
|
||||||
|
|
||||||
if [[ -z "$chromium_bin" ]]; then
|
if [[ -z "$chromium_bin" ]]; then
|
||||||
missing+=("chromium")
|
missing+=("chromium")
|
||||||
|
elif [[ ! -x "$chromium_bin" ]]; then
|
||||||
|
missing+=("executable chromium at $chromium_bin")
|
||||||
fi
|
fi
|
||||||
for required in python3 web/public/cnc_sim.js web/public/cnc_sim.wasm web/test-browser-wasm-smoke.html; do
|
required_files=(
|
||||||
if [[ "$required" == python3 ]]; then
|
web/public/cnc_sim.js
|
||||||
if ! command -v python3 >/dev/null 2>&1; then
|
web/public/cnc_sim.wasm
|
||||||
missing+=("python3")
|
web/public/linuxcnc_switchkins_remap_config_cases.json
|
||||||
fi
|
web/src/wasm-core.js
|
||||||
elif [[ ! -f "$required" ]]; then
|
web/src/app.js
|
||||||
missing+=("$required")
|
web/index.html
|
||||||
|
web/styles.css
|
||||||
|
web/test-browser-wasm-smoke.html
|
||||||
|
web/test-browser-wasm-smoke-helpers.js
|
||||||
|
web/test-browser-wasm-smoke-sections.js
|
||||||
|
web/test-browser-wasm-smoke-linuxcnc-sections.js
|
||||||
|
web/test-browser-wasm-smoke-opfs-workspace-sections.js
|
||||||
|
web/test-browser-wasm-smoke-opfs-basic-sections.js
|
||||||
|
web/test-browser-wasm-smoke-opfs-parameter-sections.js
|
||||||
|
web/test-browser-wasm-smoke-opfs-mirror-sections.js
|
||||||
|
web/test-browser-wasm-smoke-opfs-directory-sections.js
|
||||||
|
web/test-browser-wasm-smoke-opfs-policy-sections.js
|
||||||
|
web/test-browser-wasm-smoke-app-sections.js
|
||||||
|
)
|
||||||
|
|
||||||
|
for required in \
|
||||||
|
node \
|
||||||
|
python3 \
|
||||||
|
"${required_files[@]}"
|
||||||
|
do
|
||||||
|
if [[ "$required" == node || "$required" == python3 ]]; then
|
||||||
|
require_command "$required"
|
||||||
|
else
|
||||||
|
require_file "$required"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if ((${#missing[@]} > 0)); then
|
if ((${#missing[@]} > 0)); then
|
||||||
echo "WASM browser smoke prerequisites are missing: ${missing[*]}." >&2
|
report_missing_prerequisites
|
||||||
echo "Run ./build-wasm.sh after activating emsdk to generate web/public/cnc_sim.js and web/public/cnc_sim.wasm." >&2
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if ! python3 - <<'PY'
|
||||||
|
from pathlib import Path
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
cases = json.loads(Path("web/public/linuxcnc_switchkins_remap_config_cases.json").read_text(encoding="utf-8"))
|
||||||
|
if not isinstance(cases, list) or not cases:
|
||||||
|
print("expected non-empty LinuxCNC switchkins config case JSON", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
for index, config_case in enumerate(cases):
|
||||||
|
if (
|
||||||
|
not isinstance(config_case, dict)
|
||||||
|
or not isinstance(config_case.get("field"), str)
|
||||||
|
or not isinstance(config_case.get("value"), str)
|
||||||
|
or not isinstance(config_case.get("m428"), int)
|
||||||
|
or not isinstance(config_case.get("m429"), int)
|
||||||
|
or not isinstance(config_case.get("m430"), int)
|
||||||
|
):
|
||||||
|
print(f"invalid LinuxCNC switchkins config case at index {index}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
PY
|
||||||
|
then
|
||||||
|
echo "WASM browser smoke switchkins config case JSON check failed" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! python3 - <<'PY'
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
html = Path("web/test-browser-wasm-smoke.html").read_text(encoding="utf-8")
|
||||||
|
if '<script src="/public/cnc_sim.js"></script>' not in html:
|
||||||
|
print("browser smoke HTML must load /public/cnc_sim.js", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
module_scripts = re.findall(r'<script\s+type="module">(.*?)</script>', html, flags=re.S)
|
||||||
|
if len(module_scripts) != 1 or not module_scripts[0].strip():
|
||||||
|
print("browser smoke HTML must contain exactly one non-empty module script", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
if 'from "/src/wasm-core.js"' not in module_scripts[0]:
|
||||||
|
print("browser smoke module script must import /src/wasm-core.js", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
if 'from "/test-browser-wasm-smoke-sections.js"' not in module_scripts[0]:
|
||||||
|
print("browser smoke module script must import /test-browser-wasm-smoke-sections.js", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
section_entry = Path("web/test-browser-wasm-smoke-sections.js").read_text(encoding="utf-8")
|
||||||
|
for imported_module in (
|
||||||
|
"/test-browser-wasm-smoke-helpers.js",
|
||||||
|
"/test-browser-wasm-smoke-linuxcnc-sections.js",
|
||||||
|
"/test-browser-wasm-smoke-opfs-workspace-sections.js",
|
||||||
|
"/test-browser-wasm-smoke-opfs-policy-sections.js",
|
||||||
|
"/test-browser-wasm-smoke-app-sections.js",
|
||||||
|
):
|
||||||
|
if f'from "{imported_module}"' not in section_entry:
|
||||||
|
print(f"browser smoke sections module must import {imported_module}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
opfs_workspace = Path("web/test-browser-wasm-smoke-opfs-workspace-sections.js").read_text(encoding="utf-8")
|
||||||
|
for imported_module in (
|
||||||
|
"/test-browser-wasm-smoke-opfs-basic-sections.js",
|
||||||
|
"/test-browser-wasm-smoke-opfs-parameter-sections.js",
|
||||||
|
"/test-browser-wasm-smoke-opfs-mirror-sections.js",
|
||||||
|
"/test-browser-wasm-smoke-opfs-directory-sections.js",
|
||||||
|
):
|
||||||
|
if f'from "{imported_module}"' not in opfs_workspace:
|
||||||
|
print(f"browser smoke OPFS workspace module must import {imported_module}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
app_html = Path("web/index.html").read_text(encoding="utf-8")
|
||||||
|
if '<link rel="stylesheet" href="/styles.css" />' not in app_html:
|
||||||
|
print("browser app HTML must load /styles.css", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
if '<script type="module" src="/src/app.js"></script>' not in app_html:
|
||||||
|
print("browser app HTML must load /src/app.js", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
for required_id in ("parseBtn", "programInput", "alarmList", "axisX", "wasmState", "storageState"):
|
||||||
|
if f'id="{required_id}"' not in app_html:
|
||||||
|
print(f"browser app HTML missing #{required_id}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
PY
|
||||||
|
then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for web_module in \
|
||||||
|
web/src/wasm-core.js \
|
||||||
|
web/src/app.js \
|
||||||
|
web/test-browser-wasm-smoke-helpers.js \
|
||||||
|
web/test-browser-wasm-smoke-sections.js \
|
||||||
|
web/test-browser-wasm-smoke-linuxcnc-sections.js \
|
||||||
|
web/test-browser-wasm-smoke-opfs-workspace-sections.js \
|
||||||
|
web/test-browser-wasm-smoke-opfs-basic-sections.js \
|
||||||
|
web/test-browser-wasm-smoke-opfs-parameter-sections.js \
|
||||||
|
web/test-browser-wasm-smoke-opfs-mirror-sections.js \
|
||||||
|
web/test-browser-wasm-smoke-opfs-directory-sections.js \
|
||||||
|
web/test-browser-wasm-smoke-opfs-policy-sections.js \
|
||||||
|
web/test-browser-wasm-smoke-app-sections.js
|
||||||
|
do
|
||||||
|
if ! node --check "$web_module"; then
|
||||||
|
echo "WASM browser smoke web module syntax check failed for $web_module" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
chromium_version=$("$chromium_bin" --version 2>/dev/null || true)
|
||||||
|
if [[ -n "$chromium_version" ]]; then
|
||||||
|
echo "browser smoke chromium version: $chromium_version"
|
||||||
|
fi
|
||||||
|
virtual_time_budget=${BROWSER_SMOKE_VIRTUAL_TIME_BUDGET:-20000}
|
||||||
|
|
||||||
server_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_browser_smoke_server.XXXXXX.log")
|
server_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_browser_smoke_server.XXXXXX.log")
|
||||||
dom_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_browser_smoke_dom.XXXXXX.html")
|
dom_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_browser_smoke_dom.XXXXXX.html")
|
||||||
profile_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_browser_smoke_profile.XXXXXX")
|
profile_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_browser_smoke_profile.XXXXXX")
|
||||||
cleanup() {
|
cleanup() {
|
||||||
|
if [[ -n "${chromium_pid:-}" ]]; then
|
||||||
|
kill "$chromium_pid" >/dev/null 2>&1 || true
|
||||||
|
wait "$chromium_pid" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
if [[ -n "${server_pid:-}" ]]; then
|
if [[ -n "${server_pid:-}" ]]; then
|
||||||
kill "$server_pid" >/dev/null 2>&1 || true
|
kill "$server_pid" >/dev/null 2>&1 || true
|
||||||
wait "$server_pid" >/dev/null 2>&1 || true
|
wait "$server_pid" >/dev/null 2>&1 || true
|
||||||
@@ -46,15 +211,75 @@ cleanup() {
|
|||||||
}
|
}
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
|
||||||
port=$(python3 - <<'PY'
|
pick_free_port() {
|
||||||
|
python3 - <<'PY'
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||||
sock.bind(("127.0.0.1", 0))
|
sock.bind(("127.0.0.1", 0))
|
||||||
print(sock.getsockname()[1])
|
print(sock.getsockname()[1])
|
||||||
PY
|
PY
|
||||||
)
|
}
|
||||||
|
|
||||||
|
print_browser_failure_context() {
|
||||||
|
if grep -F "browser wasm smoke failed:" "$dom_log" >/dev/null; then
|
||||||
|
grep -o "browser wasm smoke failed:[^<]*" "$dom_log" | sed -n '1,20p' >&2
|
||||||
|
elif grep -F "running:" "$dom_log" >/dev/null; then
|
||||||
|
grep -o "running:[^<]*" "$dom_log" | tail -n 1 >&2
|
||||||
|
elif [[ ! -s "$dom_log" ]]; then
|
||||||
|
echo "browser smoke did not produce DOM output" >&2
|
||||||
|
fi
|
||||||
|
echo "browser smoke HTTP server log:" >&2
|
||||||
|
sed -n '1,40p' "$server_log" >&2
|
||||||
|
echo "browser smoke DOM excerpt:" >&2
|
||||||
|
sed -n '1,120p' "$dom_log" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
probe_http_resource() {
|
||||||
|
local resource_path=$1
|
||||||
|
local expected_prefix=$2
|
||||||
|
local expected_type=${3:-}
|
||||||
|
if ! python3 - "$port" "$resource_path" "$expected_prefix" "$expected_type" <<'PY'
|
||||||
|
import http.client
|
||||||
|
import sys
|
||||||
|
|
||||||
|
port = int(sys.argv[1])
|
||||||
|
path = sys.argv[2]
|
||||||
|
expected_prefix = sys.argv[3].encode("utf-8")
|
||||||
|
expected_type = sys.argv[4]
|
||||||
|
|
||||||
|
conn = http.client.HTTPConnection("127.0.0.1", port, timeout=2)
|
||||||
|
try:
|
||||||
|
conn.request("GET", path)
|
||||||
|
response = conn.getresponse()
|
||||||
|
body = response.read(max(len(expected_prefix), 1))
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
if response.status != 200:
|
||||||
|
print(f"{path} returned HTTP {response.status}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
content_type = response.getheader("Content-Type", "").split(";", 1)[0]
|
||||||
|
if expected_type and content_type != expected_type:
|
||||||
|
print(f"{path} returned Content-Type {content_type!r}, expected {expected_type!r}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
if not body:
|
||||||
|
print(f"{path} returned an empty body", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
if expected_prefix and not body.startswith(expected_prefix):
|
||||||
|
print(f"{path} did not start with expected content", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
PY
|
||||||
|
then
|
||||||
|
echo "browser smoke HTTP resource probe failed for $resource_path" >&2
|
||||||
|
sed -n '1,40p' "$server_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
port=$(pick_free_port)
|
||||||
|
|
||||||
|
echo "running WASM browser smoke with $chromium_bin on http://127.0.0.1:$port/test-browser-wasm-smoke.html"
|
||||||
python3 -m http.server "$port" --bind 127.0.0.1 --directory web >"$server_log" 2>&1 &
|
python3 -m http.server "$port" --bind 127.0.0.1 --directory web >"$server_log" 2>&1 &
|
||||||
server_pid=$!
|
server_pid=$!
|
||||||
|
|
||||||
@@ -92,19 +317,211 @@ then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
probe_http_resource "/test-browser-wasm-smoke.html" "<!doctype html>" "text/html"
|
||||||
|
probe_http_resource "/" "<!doctype html>" "text/html"
|
||||||
|
probe_http_resource "/styles.css" "" "text/css"
|
||||||
|
probe_http_resource "/public/cnc_sim.js" "" "text/javascript"
|
||||||
|
probe_http_resource "/public/cnc_sim.wasm" "" "application/wasm"
|
||||||
|
probe_http_resource "/public/linuxcnc_switchkins_remap_config_cases.json" "[" "application/json"
|
||||||
|
probe_http_resource "/src/app.js" "" "text/javascript"
|
||||||
|
probe_http_resource "/src/wasm-core.js" "" "text/javascript"
|
||||||
|
probe_http_resource "/test-browser-wasm-smoke-helpers.js" "" "text/javascript"
|
||||||
|
probe_http_resource "/test-browser-wasm-smoke-sections.js" "" "text/javascript"
|
||||||
|
probe_http_resource "/test-browser-wasm-smoke-linuxcnc-sections.js" "" "text/javascript"
|
||||||
|
probe_http_resource "/test-browser-wasm-smoke-opfs-workspace-sections.js" "" "text/javascript"
|
||||||
|
probe_http_resource "/test-browser-wasm-smoke-opfs-basic-sections.js" "" "text/javascript"
|
||||||
|
probe_http_resource "/test-browser-wasm-smoke-opfs-parameter-sections.js" "" "text/javascript"
|
||||||
|
probe_http_resource "/test-browser-wasm-smoke-opfs-mirror-sections.js" "" "text/javascript"
|
||||||
|
probe_http_resource "/test-browser-wasm-smoke-opfs-directory-sections.js" "" "text/javascript"
|
||||||
|
probe_http_resource "/test-browser-wasm-smoke-opfs-policy-sections.js" "" "text/javascript"
|
||||||
|
probe_http_resource "/test-browser-wasm-smoke-app-sections.js" "" "text/javascript"
|
||||||
|
|
||||||
|
debug_port=$(pick_free_port)
|
||||||
"$chromium_bin" \
|
"$chromium_bin" \
|
||||||
--headless=new \
|
--headless=new \
|
||||||
--disable-gpu \
|
--disable-gpu \
|
||||||
--no-sandbox \
|
--no-sandbox \
|
||||||
--user-data-dir="$profile_dir" \
|
--user-data-dir="$profile_dir" \
|
||||||
--virtual-time-budget=5000 \
|
--remote-debugging-port="$debug_port" \
|
||||||
--dump-dom \
|
"http://127.0.0.1:$port/test-browser-wasm-smoke.html" >"$dom_log" 2>&1 &
|
||||||
"http://127.0.0.1:$port/test-browser-wasm-smoke.html" >"$dom_log" 2>&1
|
chromium_pid=$!
|
||||||
|
|
||||||
if ! grep -F "browser wasm smoke passed" "$dom_log" >/dev/null; then
|
if ! python3 - "$debug_port" "$dom_log" "$virtual_time_budget" <<'PY'
|
||||||
echo "browser WASM smoke did not pass" >&2
|
import base64
|
||||||
sed -n '1,120p' "$dom_log" >&2
|
import http.client
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
debug_port = int(sys.argv[1])
|
||||||
|
dom_log = sys.argv[2]
|
||||||
|
timeout_ms = int(sys.argv[3])
|
||||||
|
deadline = time.monotonic() + (timeout_ms / 1000.0)
|
||||||
|
|
||||||
|
def http_json(path):
|
||||||
|
conn = http.client.HTTPConnection("127.0.0.1", debug_port, timeout=1)
|
||||||
|
try:
|
||||||
|
conn.request("GET", path)
|
||||||
|
response = conn.getresponse()
|
||||||
|
body = response.read()
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
if response.status != 200:
|
||||||
|
raise RuntimeError(f"{path} returned HTTP {response.status}")
|
||||||
|
return json.loads(body.decode("utf-8"))
|
||||||
|
|
||||||
|
def websocket_connect(url):
|
||||||
|
parsed = urlparse(url)
|
||||||
|
key = base64.b64encode(os.urandom(16)).decode("ascii")
|
||||||
|
sock = socket.create_connection((parsed.hostname, parsed.port), timeout=2)
|
||||||
|
request = (
|
||||||
|
f"GET {parsed.path} HTTP/1.1\r\n"
|
||||||
|
f"Host: {parsed.hostname}:{parsed.port}\r\n"
|
||||||
|
"Upgrade: websocket\r\n"
|
||||||
|
"Connection: Upgrade\r\n"
|
||||||
|
f"Sec-WebSocket-Key: {key}\r\n"
|
||||||
|
"Sec-WebSocket-Version: 13\r\n\r\n"
|
||||||
|
)
|
||||||
|
sock.sendall(request.encode("ascii"))
|
||||||
|
response = sock.recv(4096)
|
||||||
|
if b" 101 " not in response.split(b"\r\n", 1)[0]:
|
||||||
|
raise RuntimeError("DevTools websocket handshake failed")
|
||||||
|
sock.settimeout(1)
|
||||||
|
return sock
|
||||||
|
|
||||||
|
def websocket_send(sock, payload):
|
||||||
|
data = payload.encode("utf-8")
|
||||||
|
header = bytearray([0x81])
|
||||||
|
if len(data) < 126:
|
||||||
|
header.append(0x80 | len(data))
|
||||||
|
elif len(data) < 65536:
|
||||||
|
header.append(0x80 | 126)
|
||||||
|
header.extend(struct.pack("!H", len(data)))
|
||||||
|
else:
|
||||||
|
header.append(0x80 | 127)
|
||||||
|
header.extend(struct.pack("!Q", len(data)))
|
||||||
|
mask = os.urandom(4)
|
||||||
|
header.extend(mask)
|
||||||
|
masked = bytes(byte ^ mask[index % 4] for index, byte in enumerate(data))
|
||||||
|
sock.sendall(header + masked)
|
||||||
|
|
||||||
|
def recv_exact(sock, size):
|
||||||
|
chunks = []
|
||||||
|
remaining = size
|
||||||
|
while remaining:
|
||||||
|
chunk = sock.recv(remaining)
|
||||||
|
if not chunk:
|
||||||
|
raise RuntimeError("DevTools websocket closed")
|
||||||
|
chunks.append(chunk)
|
||||||
|
remaining -= len(chunk)
|
||||||
|
return b"".join(chunks)
|
||||||
|
|
||||||
|
def websocket_recv(sock):
|
||||||
|
first, second = recv_exact(sock, 2)
|
||||||
|
opcode = first & 0x0F
|
||||||
|
length = second & 0x7F
|
||||||
|
if length == 126:
|
||||||
|
length = struct.unpack("!H", recv_exact(sock, 2))[0]
|
||||||
|
elif length == 127:
|
||||||
|
length = struct.unpack("!Q", recv_exact(sock, 8))[0]
|
||||||
|
masked = bool(second & 0x80)
|
||||||
|
mask = recv_exact(sock, 4) if masked else b""
|
||||||
|
payload = recv_exact(sock, length)
|
||||||
|
if masked:
|
||||||
|
payload = bytes(byte ^ mask[index % 4] for index, byte in enumerate(payload))
|
||||||
|
if opcode == 8:
|
||||||
|
raise RuntimeError("DevTools websocket closed")
|
||||||
|
if opcode != 1:
|
||||||
|
return None
|
||||||
|
return json.loads(payload.decode("utf-8"))
|
||||||
|
|
||||||
|
def find_page_ws_url():
|
||||||
|
while time.monotonic() < deadline:
|
||||||
|
try:
|
||||||
|
for target in http_json("/json/list"):
|
||||||
|
if target.get("type") == "page" and target.get("url", "").endswith("/test-browser-wasm-smoke.html"):
|
||||||
|
return target["webSocketDebuggerUrl"]
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
time.sleep(0.1)
|
||||||
|
raise RuntimeError("timed out waiting for browser DevTools page target")
|
||||||
|
|
||||||
|
sock = websocket_connect(find_page_ws_url())
|
||||||
|
next_id = 0
|
||||||
|
|
||||||
|
def cdp(method, params=None):
|
||||||
|
global next_id
|
||||||
|
next_id += 1
|
||||||
|
message_id = next_id
|
||||||
|
websocket_send(sock, json.dumps({"id": message_id, "method": method, "params": params or {}}))
|
||||||
|
while time.monotonic() < deadline:
|
||||||
|
try:
|
||||||
|
message = websocket_recv(sock)
|
||||||
|
except TimeoutError:
|
||||||
|
continue
|
||||||
|
except socket.timeout:
|
||||||
|
continue
|
||||||
|
if message and message.get("id") == message_id:
|
||||||
|
if "error" in message:
|
||||||
|
raise RuntimeError(message["error"])
|
||||||
|
return message["result"]
|
||||||
|
raise RuntimeError(f"timed out waiting for DevTools response to {method}")
|
||||||
|
|
||||||
|
def evaluate(expression):
|
||||||
|
result = cdp("Runtime.evaluate", {"expression": expression, "returnByValue": True})
|
||||||
|
return result.get("result", {}).get("value", "")
|
||||||
|
|
||||||
|
try:
|
||||||
|
cdp("Runtime.enable")
|
||||||
|
text = ""
|
||||||
|
while time.monotonic() < deadline:
|
||||||
|
text = evaluate('document.querySelector("#result")?.textContent || ""')
|
||||||
|
if "browser wasm smoke passed" in text:
|
||||||
|
html = evaluate("document.documentElement.outerHTML")
|
||||||
|
with open(dom_log, "w", encoding="utf-8") as handle:
|
||||||
|
handle.write(html)
|
||||||
|
sys.exit(0)
|
||||||
|
if "browser wasm smoke failed:" in text:
|
||||||
|
print(text, file=sys.stderr)
|
||||||
|
html = evaluate("document.documentElement.outerHTML")
|
||||||
|
with open(dom_log, "w", encoding="utf-8") as handle:
|
||||||
|
handle.write(html)
|
||||||
|
sys.exit(1)
|
||||||
|
time.sleep(0.2)
|
||||||
|
print(f"timed out waiting for browser smoke result; last status: {text}", file=sys.stderr)
|
||||||
|
html = evaluate("document.documentElement.outerHTML")
|
||||||
|
with open(dom_log, "w", encoding="utf-8") as handle:
|
||||||
|
handle.write(html)
|
||||||
|
sys.exit(1)
|
||||||
|
finally:
|
||||||
|
sock.close()
|
||||||
|
PY
|
||||||
|
then
|
||||||
|
echo "browser WASM smoke failed or timed out" >&2
|
||||||
|
print_browser_failure_context
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "browser wasm smoke passed"
|
if ! grep -F "browser wasm smoke passed" "$dom_log" >/dev/null; then
|
||||||
|
echo "browser WASM smoke did not pass" >&2
|
||||||
|
print_browser_failure_context
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
passed_text=$(grep -o "browser wasm smoke passed ([0-9][0-9]* sections)" "$dom_log" | tail -n 1 || true)
|
||||||
|
passed_sections=$(printf '%s\n' "$passed_text" | grep -o '[0-9][0-9]*' || true)
|
||||||
|
if [[ -z "$passed_sections" ]]; then
|
||||||
|
echo "browser WASM smoke did not report a completed section count" >&2
|
||||||
|
print_browser_failure_context
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ((passed_sections < minimum_sections)); then
|
||||||
|
echo "browser WASM smoke covered $passed_sections sections, expected at least $minimum_sections" >&2
|
||||||
|
print_browser_failure_context
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "${passed_text:-browser wasm smoke passed}"
|
||||||
|
|||||||
@@ -7,7 +7,13 @@ const { pathToFileURL } = require("url");
|
|||||||
const wasmDir = path.join(__dirname, "web", "public");
|
const wasmDir = path.join(__dirname, "web", "public");
|
||||||
const cjsLoader = path.join(os.tmpdir(), `cnc_sim_node_loader_${process.pid}.cjs`);
|
const cjsLoader = path.join(os.tmpdir(), `cnc_sim_node_loader_${process.pid}.cjs`);
|
||||||
fs.copyFileSync(path.join(wasmDir, "cnc_sim.js"), cjsLoader);
|
fs.copyFileSync(path.join(wasmDir, "cnc_sim.js"), cjsLoader);
|
||||||
|
process.on("exit", () => {
|
||||||
|
fs.rmSync(cjsLoader, { force: true });
|
||||||
|
});
|
||||||
const createCncSimModule = require(cjsLoader);
|
const createCncSimModule = require(cjsLoader);
|
||||||
|
if (typeof createCncSimModule !== "function") {
|
||||||
|
throw new Error("cnc_sim.js did not export a Node-loadable createCncSimModule function");
|
||||||
|
}
|
||||||
|
|
||||||
const EVENT_TYPE = {
|
const EVENT_TYPE = {
|
||||||
2: "comment",
|
2: "comment",
|
||||||
@@ -61,10 +67,87 @@ function expectKinematicsSwitch(events, line, kinstype, label) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function expectEvent(events, predicate, message) {
|
||||||
|
if (!events.some(predicate)) {
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function expectErrorContaining(action, expectedText, message) {
|
||||||
|
try {
|
||||||
|
await action();
|
||||||
|
throw new Error(message);
|
||||||
|
} catch (error) {
|
||||||
|
if (!String(error?.message ?? error).includes(expectedText)) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectRtcpPivot(events, expected, message) {
|
||||||
|
expectEvent(
|
||||||
|
events,
|
||||||
|
(event) => event.type === "rtcp-pivot" &&
|
||||||
|
event.line === expected.line &&
|
||||||
|
event.reserved === expected.reserved &&
|
||||||
|
near(event.dwellSeconds, expected.dwellSeconds) &&
|
||||||
|
(expected.endX === undefined || near(event.endX, expected.endX)) &&
|
||||||
|
(expected.endY === undefined || near(event.endY, expected.endY)) &&
|
||||||
|
(expected.endZ === undefined || near(event.endZ, expected.endZ)) &&
|
||||||
|
(expected.endA === undefined || near(event.endA, expected.endA)) &&
|
||||||
|
(expected.endB === undefined || near(event.endB, expected.endB)) &&
|
||||||
|
(expected.endC === undefined || near(event.endC, expected.endC)),
|
||||||
|
message,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function near(actual, expected) {
|
function near(actual, expected) {
|
||||||
return Math.abs(actual - expected) < 1e-6;
|
return Math.abs(actual - expected) < 1e-6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let completedSteps = 0;
|
||||||
|
|
||||||
|
function loadSwitchkinsCases() {
|
||||||
|
const configCases = JSON.parse(
|
||||||
|
fs.readFileSync(path.join(wasmDir, "linuxcnc_switchkins_remap_config_cases.json"), "utf8"),
|
||||||
|
);
|
||||||
|
if (!Array.isArray(configCases) || configCases.length === 0) {
|
||||||
|
throw new Error("expected non-empty generated LinuxCNC switchkins config cases");
|
||||||
|
}
|
||||||
|
for (const [index, configCase] of configCases.entries()) {
|
||||||
|
if (
|
||||||
|
!configCase ||
|
||||||
|
typeof configCase.field !== "string" ||
|
||||||
|
typeof configCase.value !== "string" ||
|
||||||
|
!Number.isInteger(configCase.m428) ||
|
||||||
|
!Number.isInteger(configCase.m429) ||
|
||||||
|
!Number.isInteger(configCase.m430)
|
||||||
|
) {
|
||||||
|
throw new Error(`invalid generated LinuxCNC switchkins config case at index ${index}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return configCases.map((configCase) => ({
|
||||||
|
label: `${configCase.field}=${configCase.value}`,
|
||||||
|
config: { backend: "linuxcnc-rs274", [configCase.field]: configCase.value },
|
||||||
|
program: configCase.m430 >= 0 ? "M428\nM429\nM430\n" : "M428\nM429\n",
|
||||||
|
expected: [
|
||||||
|
[1, configCase.m428],
|
||||||
|
[2, configCase.m429],
|
||||||
|
...(configCase.m430 >= 0 ? [[3, configCase.m430]] : []),
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runStep(name, action) {
|
||||||
|
try {
|
||||||
|
await action();
|
||||||
|
completedSteps += 1;
|
||||||
|
} catch (error) {
|
||||||
|
const detail = error && error.stack ? error.stack : error;
|
||||||
|
throw new Error(`Node WASM smoke step "${name}" failed: ${detail}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const module = await createCncSimModule({
|
const module = await createCncSimModule({
|
||||||
locateFile: (file) => path.join(wasmDir, file),
|
locateFile: (file) => path.join(wasmDir, file),
|
||||||
@@ -80,7 +163,40 @@ function near(actual, expected) {
|
|||||||
const loadConfig = module.cwrap("cnc_sim_load_config_json", "number", ["number", "number", "number"]);
|
const loadConfig = module.cwrap("cnc_sim_load_config_json", "number", ["number", "number", "number"]);
|
||||||
const parseProgram = module.cwrap("cnc_sim_parse_program", "number", ["number", "number", "number"]);
|
const parseProgram = module.cwrap("cnc_sim_parse_program", "number", ["number", "number", "number"]);
|
||||||
const lastError = module.cwrap("cnc_sim_last_error", "number", ["number"]);
|
const lastError = module.cwrap("cnc_sim_last_error", "number", ["number"]);
|
||||||
|
const throwLastError = (targetHandle) => {
|
||||||
|
throw new Error(module.UTF8ToString(lastError(targetHandle)));
|
||||||
|
};
|
||||||
|
const initializeHandle = (targetHandle) => {
|
||||||
|
reset(targetHandle);
|
||||||
|
if (setDialect(targetHandle, 0) !== 0) {
|
||||||
|
throwLastError(targetHandle);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const loadJsonConfig = (targetHandle, value) => {
|
||||||
|
const config = writeString(module, JSON.stringify(value));
|
||||||
|
try {
|
||||||
|
if (loadConfig(targetHandle, config.ptr, config.len) !== 0) {
|
||||||
|
throwLastError(targetHandle);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
module._free(config.ptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const parseText = (targetHandle, value) => {
|
||||||
|
const program = writeString(module, value);
|
||||||
|
try {
|
||||||
|
if (parseProgram(targetHandle, program.ptr, program.len) !== 0) {
|
||||||
|
throwLastError(targetHandle);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
module._free(program.ptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const attachCallback = (targetHandle, targetCallbackPtr) => {
|
||||||
|
if (setCallback(targetHandle, targetCallbackPtr, 0) !== 0) {
|
||||||
|
throwLastError(targetHandle);
|
||||||
|
}
|
||||||
|
};
|
||||||
const handle = create();
|
const handle = create();
|
||||||
if (!handle) {
|
if (!handle) {
|
||||||
throw new Error("cnc_sim_create returned null");
|
throw new Error("cnc_sim_create returned null");
|
||||||
@@ -88,258 +204,146 @@ function near(actual, expected) {
|
|||||||
|
|
||||||
let callbackPtr = 0;
|
let callbackPtr = 0;
|
||||||
try {
|
try {
|
||||||
reset(handle);
|
initializeHandle(handle);
|
||||||
if (setDialect(handle, 0) !== 0) {
|
loadJsonConfig(handle, { backend: "linuxcnc-rs274" });
|
||||||
throw new Error(module.UTF8ToString(lastError(handle)));
|
|
||||||
}
|
|
||||||
|
|
||||||
const config = writeString(module, JSON.stringify({ backend: "linuxcnc-rs274" }));
|
|
||||||
try {
|
|
||||||
if (loadConfig(handle, config.ptr, config.len) !== 0) {
|
|
||||||
throw new Error(module.UTF8ToString(lastError(handle)));
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
module._free(config.ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
const events = [];
|
const events = [];
|
||||||
callbackPtr = module.addFunction((eventPtr) => {
|
callbackPtr = module.addFunction((eventPtr) => {
|
||||||
events.push(readEvent(module, eventPtr));
|
events.push(readEvent(module, eventPtr));
|
||||||
return 0;
|
return 0;
|
||||||
}, "iii");
|
}, "iii");
|
||||||
if (setCallback(handle, callbackPtr, 0) !== 0) {
|
attachCallback(handle, callbackPtr);
|
||||||
throw new Error(module.UTF8ToString(lastError(handle)));
|
const parseCaseWithHandle = (testCase) => {
|
||||||
}
|
|
||||||
|
|
||||||
const program = writeString(module, "G21 G90\nG0 X0\nG1 X5 F100\nM30\n");
|
|
||||||
try {
|
|
||||||
if (parseProgram(handle, program.ptr, program.len) !== 0) {
|
|
||||||
throw new Error(module.UTF8ToString(lastError(handle)));
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
module._free(program.ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!events.some((event) => event.type === "linear-feed" && event.endX === 5)) {
|
|
||||||
throw new Error("expected LinuxCNC WASM linear-feed event ending at X5");
|
|
||||||
}
|
|
||||||
if (!events.some((event) => event.type === "program-end")) {
|
|
||||||
throw new Error("expected LinuxCNC WASM program-end event");
|
|
||||||
}
|
|
||||||
|
|
||||||
events.length = 0;
|
|
||||||
const switchProgram = writeString(module, "M428\nM429\nM430\n");
|
|
||||||
try {
|
|
||||||
if (parseProgram(handle, switchProgram.ptr, switchProgram.len) !== 0) {
|
|
||||||
throw new Error(module.UTF8ToString(lastError(handle)));
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
module._free(switchProgram.ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const kinstype of [1, 0, 2]) {
|
|
||||||
if (!events.some((event) => event.type === "kinematics-switch" && event.reserved === kinstype)) {
|
|
||||||
throw new Error(`expected LinuxCNC WASM kinematics switch ${kinstype}`);
|
|
||||||
}
|
|
||||||
if (!events.some((event) => event.type === "comment" &&
|
|
||||||
event.reserved === 68 &&
|
|
||||||
event.tool === 3 &&
|
|
||||||
event.feed === kinstype)) {
|
|
||||||
throw new Error(`expected LinuxCNC WASM M68 E3 Q${kinstype} remap side effect`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!events.some((event) => event.type === "comment" &&
|
|
||||||
event.reserved === 66 &&
|
|
||||||
event.tool === 0 &&
|
|
||||||
event.feed === 0)) {
|
|
||||||
throw new Error("expected LinuxCNC WASM M66 E0 L0 remap side effect");
|
|
||||||
}
|
|
||||||
|
|
||||||
const switchkinsCases = [
|
|
||||||
{
|
|
||||||
label: "scara",
|
|
||||||
config: { backend: "linuxcnc-rs274", switchkins: "scara" },
|
|
||||||
program: "M428\nM429\nM430\n",
|
|
||||||
expected: [
|
|
||||||
[1, 0],
|
|
||||||
[2, 1],
|
|
||||||
[3, 2],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "table-dual-rotary",
|
|
||||||
config: { backend: "linuxcnc-rs274", switchkins: "table-dual-rotary" },
|
|
||||||
program: "M428\nM429\n",
|
|
||||||
expected: [
|
|
||||||
[1, 1],
|
|
||||||
[2, 0],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "sim-xyzab-tdr-kins",
|
|
||||||
config: { backend: "linuxcnc-rs274", switchkins: "sim-xyzab-tdr-kins" },
|
|
||||||
program: "M428\nM429\n",
|
|
||||||
expected: [
|
|
||||||
[1, 1],
|
|
||||||
[2, 0],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "puma",
|
|
||||||
config: { backend: "linuxcnc-rs274", switchkins: "puma" },
|
|
||||||
program: "M428\nM429\nM430\n",
|
|
||||||
expected: [
|
|
||||||
[1, 0],
|
|
||||||
[2, 1],
|
|
||||||
[3, 2],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "melfa-sim",
|
|
||||||
config: { backend: "linuxcnc-rs274", switchkins: "melfa-sim" },
|
|
||||||
program: "M428\nM429\nM430\n",
|
|
||||||
expected: [
|
|
||||||
[1, 0],
|
|
||||||
[2, 1],
|
|
||||||
[3, 2],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "hexapod-sim",
|
|
||||||
config: { backend: "linuxcnc-rs274", remap: "hexapod-sim" },
|
|
||||||
program: "M428\nM429\nM430\n",
|
|
||||||
expected: [
|
|
||||||
[1, 0],
|
|
||||||
[2, 1],
|
|
||||||
[3, 2],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "xyzbca-trsrn",
|
|
||||||
config: { backend: "linuxcnc-rs274", switchkins: "xyzbca-trsrn" },
|
|
||||||
program: "M428\nM429\nM430\n",
|
|
||||||
expected: [
|
|
||||||
[1, 0],
|
|
||||||
[2, 1],
|
|
||||||
[3, 2],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const testCase of switchkinsCases) {
|
|
||||||
reset(handle);
|
reset(handle);
|
||||||
events.length = 0;
|
events.length = 0;
|
||||||
const switchkinsConfig = writeString(module, JSON.stringify(testCase.config));
|
loadJsonConfig(handle, testCase.config);
|
||||||
|
parseText(handle, testCase.program);
|
||||||
|
};
|
||||||
|
const withConfiguredHandle = async (label, config, program, action) => {
|
||||||
|
events.length = 0;
|
||||||
|
const targetHandle = create();
|
||||||
|
if (!targetHandle) {
|
||||||
|
throw new Error(`cnc_sim_create returned null for ${label} case`);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if (loadConfig(handle, switchkinsConfig.ptr, switchkinsConfig.len) !== 0) {
|
initializeHandle(targetHandle);
|
||||||
throw new Error(module.UTF8ToString(lastError(handle)));
|
attachCallback(targetHandle, callbackPtr);
|
||||||
}
|
loadJsonConfig(targetHandle, config);
|
||||||
|
parseText(targetHandle, program);
|
||||||
|
await action();
|
||||||
} finally {
|
} finally {
|
||||||
module._free(switchkinsConfig.ptr);
|
destroy(targetHandle);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const switchkinsProgram = writeString(module, testCase.program);
|
await runStep("basic LinuxCNC RS274 parse", async () => {
|
||||||
try {
|
parseText(handle, "G21 G90\nG0 X0\nG1 X5 F100\nM30\n");
|
||||||
if (parseProgram(handle, switchkinsProgram.ptr, switchkinsProgram.len) !== 0) {
|
});
|
||||||
throw new Error(module.UTF8ToString(lastError(handle)));
|
await runStep("basic LinuxCNC RS274 linear-feed event", async () => {
|
||||||
}
|
expectEvent(events, (event) => event.type === "linear-feed" && event.endX === 5,
|
||||||
} finally {
|
"expected LinuxCNC WASM linear-feed event ending at X5");
|
||||||
module._free(switchkinsProgram.ptr);
|
});
|
||||||
}
|
await runStep("basic LinuxCNC RS274 program-end event", async () => {
|
||||||
|
expectEvent(events, (event) => event.type === "program-end",
|
||||||
|
"expected LinuxCNC WASM program-end event");
|
||||||
|
});
|
||||||
|
|
||||||
|
await runStep("default switchkins remap parse", async () => {
|
||||||
|
events.length = 0;
|
||||||
|
parseText(handle, "M428\nM429\nM430\n");
|
||||||
|
});
|
||||||
|
for (const kinstype of [1, 0, 2]) {
|
||||||
|
await runStep(`default switchkins kinematics switch ${kinstype}`, async () => {
|
||||||
|
expectEvent(events, (event) => event.type === "kinematics-switch" && event.reserved === kinstype,
|
||||||
|
`expected LinuxCNC WASM kinematics switch ${kinstype}`);
|
||||||
|
});
|
||||||
|
await runStep(`default switchkins M68 E3 Q${kinstype} side effect`, async () => {
|
||||||
|
expectEvent(events, (event) => event.type === "comment" &&
|
||||||
|
event.reserved === 68 &&
|
||||||
|
event.tool === 3 &&
|
||||||
|
event.feed === kinstype,
|
||||||
|
`expected LinuxCNC WASM M68 E3 Q${kinstype} remap side effect`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await runStep("default switchkins M66 E0 L0 side effect", async () => {
|
||||||
|
expectEvent(events, (event) => event.type === "comment" &&
|
||||||
|
event.reserved === 66 &&
|
||||||
|
event.tool === 0 &&
|
||||||
|
event.feed === 0,
|
||||||
|
"expected LinuxCNC WASM M66 E0 L0 remap side effect");
|
||||||
|
});
|
||||||
|
|
||||||
|
const switchkinsCases = loadSwitchkinsCases();
|
||||||
|
await runStep("generated switchkins config cases load", async () => {
|
||||||
|
if (switchkinsCases.length === 0) {
|
||||||
|
throw new Error("expected generated LinuxCNC WASM switchkins config cases");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (const testCase of switchkinsCases) {
|
||||||
|
await runStep(`generated switchkins ${testCase.label}`, async () => {
|
||||||
|
parseCaseWithHandle(testCase);
|
||||||
for (const [line, kinstype] of testCase.expected) {
|
for (const [line, kinstype] of testCase.expected) {
|
||||||
expectKinematicsSwitch(events, line, kinstype, testCase.label);
|
expectKinematicsSwitch(events, line, kinstype, testCase.label);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await runStep("5axiskins RTCP parse", async () => {
|
||||||
reset(handle);
|
reset(handle);
|
||||||
events.length = 0;
|
events.length = 0;
|
||||||
const fiveaxisConfig = writeString(
|
loadJsonConfig(handle, {
|
||||||
module,
|
|
||||||
JSON.stringify({
|
|
||||||
backend: "linuxcnc-rs274",
|
backend: "linuxcnc-rs274",
|
||||||
rtcp: { enabled: true, toolLength: 250 },
|
rtcp: { enabled: true, toolLength: 250 },
|
||||||
kinematics: "5axiskins",
|
kinematics: "5axiskins",
|
||||||
pivotLength: 250,
|
pivotLength: 250,
|
||||||
}),
|
});
|
||||||
);
|
parseText(handle, "M428\nG0 X260 Y20 Z280 B90 C0\n");
|
||||||
try {
|
});
|
||||||
if (loadConfig(handle, fiveaxisConfig.ptr, fiveaxisConfig.len) !== 0) {
|
await runStep("5axiskins RTCP kinematics switch", async () => {
|
||||||
throw new Error(module.UTF8ToString(lastError(handle)));
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
module._free(fiveaxisConfig.ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
const fiveaxisProgram = writeString(module, "M428\nG0 X260 Y20 Z280 B90 C0\n");
|
|
||||||
try {
|
|
||||||
if (parseProgram(handle, fiveaxisProgram.ptr, fiveaxisProgram.len) !== 0) {
|
|
||||||
throw new Error(module.UTF8ToString(lastError(handle)));
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
module._free(fiveaxisProgram.ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
expectKinematicsSwitch(events, 1, 0, "5axiskins");
|
expectKinematicsSwitch(events, 1, 0, "5axiskins");
|
||||||
if (!events.some((event) => event.type === "rtcp-pivot" &&
|
});
|
||||||
event.line === 2 &&
|
await runStep("5axiskins RTCP pivot event", async () => {
|
||||||
event.reserved === 0 &&
|
expectRtcpPivot(events, {
|
||||||
near(event.dwellSeconds, 250) &&
|
line: 2,
|
||||||
near(event.endX, 10) &&
|
reserved: 0,
|
||||||
near(event.endY, 20) &&
|
dwellSeconds: 250,
|
||||||
near(event.endZ, 30) &&
|
endX: 10,
|
||||||
near(event.endB, 90) &&
|
endY: 20,
|
||||||
near(event.endC, 0))) {
|
endZ: 30,
|
||||||
throw new Error("expected LinuxCNC WASM 5axiskins RTCP pivot from LinuxCNC 5axiskins inverse");
|
endB: 90,
|
||||||
}
|
endC: 0,
|
||||||
|
}, "expected LinuxCNC WASM 5axiskins RTCP pivot from LinuxCNC 5axiskins inverse");
|
||||||
|
});
|
||||||
|
|
||||||
|
await runStep("userk RTCP parse", async () => {
|
||||||
reset(handle);
|
reset(handle);
|
||||||
events.length = 0;
|
events.length = 0;
|
||||||
const userkConfig = writeString(
|
loadJsonConfig(handle, {
|
||||||
module,
|
|
||||||
JSON.stringify({
|
|
||||||
backend: "linuxcnc-rs274",
|
backend: "linuxcnc-rs274",
|
||||||
rtcp: { enabled: true, toolLength: 250 },
|
rtcp: { enabled: true, toolLength: 250 },
|
||||||
}),
|
});
|
||||||
);
|
parseText(handle, "M430\nG0 X260 Y20 Z280 B90 C45\n");
|
||||||
try {
|
});
|
||||||
if (loadConfig(handle, userkConfig.ptr, userkConfig.len) !== 0) {
|
await runStep("userk RTCP kinematics switch", async () => {
|
||||||
throw new Error(module.UTF8ToString(lastError(handle)));
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
module._free(userkConfig.ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
const userkProgram = writeString(module, "M430\nG0 X260 Y20 Z280 B90 C45\n");
|
|
||||||
try {
|
|
||||||
if (parseProgram(handle, userkProgram.ptr, userkProgram.len) !== 0) {
|
|
||||||
throw new Error(module.UTF8ToString(lastError(handle)));
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
module._free(userkProgram.ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
expectKinematicsSwitch(events, 1, 2, "userk");
|
expectKinematicsSwitch(events, 1, 2, "userk");
|
||||||
if (!events.some((event) => event.type === "rtcp-pivot" &&
|
});
|
||||||
event.line === 2 &&
|
await runStep("userk RTCP pivot event", async () => {
|
||||||
event.reserved === 2 &&
|
expectRtcpPivot(events, {
|
||||||
near(event.dwellSeconds, 250) &&
|
line: 2,
|
||||||
near(event.endX, 260) &&
|
reserved: 2,
|
||||||
near(event.endY, 20) &&
|
dwellSeconds: 250,
|
||||||
near(event.endZ, 280) &&
|
endX: 260,
|
||||||
near(event.endB, 90) &&
|
endY: 20,
|
||||||
near(event.endC, 45))) {
|
endZ: 280,
|
||||||
throw new Error("expected LinuxCNC WASM userk RTCP pivot from LinuxCNC userk identity inverse");
|
endB: 90,
|
||||||
}
|
endC: 45,
|
||||||
|
}, "expected LinuxCNC WASM userk RTCP pivot from LinuxCNC userk identity inverse");
|
||||||
|
});
|
||||||
|
|
||||||
events.length = 0;
|
await runStep("configured xyzbc-trt RTCP parse", async () => {
|
||||||
const configuredTrtHandle = create();
|
await withConfiguredHandle(
|
||||||
if (!configuredTrtHandle) {
|
"configured xyzbc-trt",
|
||||||
throw new Error("cnc_sim_create returned null for configured xyzbc-trt case");
|
{
|
||||||
}
|
|
||||||
const configuredTrtConfig = writeString(
|
|
||||||
module,
|
|
||||||
JSON.stringify({
|
|
||||||
backend: "linuxcnc-rs274",
|
backend: "linuxcnc-rs274",
|
||||||
rtcp: { enabled: false, toolLength: 11 },
|
rtcp: { enabled: false, toolLength: 11 },
|
||||||
xyzbcTrt: {
|
xyzbcTrt: {
|
||||||
@@ -350,54 +354,32 @@ function near(actual, expected) {
|
|||||||
zOffset: 7,
|
zOffset: 7,
|
||||||
conventionalDirections: true,
|
conventionalDirections: true,
|
||||||
},
|
},
|
||||||
}),
|
},
|
||||||
|
"M428\nG43.4\nG0 X30 Y40 Z50 B20 C-30\n",
|
||||||
|
async () => {
|
||||||
|
},
|
||||||
);
|
);
|
||||||
try {
|
});
|
||||||
reset(configuredTrtHandle);
|
await runStep("configured xyzbc-trt kinematics switch", async () => {
|
||||||
if (setDialect(configuredTrtHandle, 0) !== 0) {
|
|
||||||
throw new Error(module.UTF8ToString(lastError(configuredTrtHandle)));
|
|
||||||
}
|
|
||||||
if (setCallback(configuredTrtHandle, callbackPtr, 0) !== 0) {
|
|
||||||
throw new Error(module.UTF8ToString(lastError(configuredTrtHandle)));
|
|
||||||
}
|
|
||||||
if (loadConfig(configuredTrtHandle, configuredTrtConfig.ptr, configuredTrtConfig.len) !== 0) {
|
|
||||||
throw new Error(module.UTF8ToString(lastError(configuredTrtHandle)));
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
module._free(configuredTrtConfig.ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
const configuredTrtProgram = writeString(module, "M428\nG43.4\nG0 X30 Y40 Z50 B20 C-30\n");
|
|
||||||
try {
|
|
||||||
if (parseProgram(configuredTrtHandle, configuredTrtProgram.ptr, configuredTrtProgram.len) !== 0) {
|
|
||||||
throw new Error(module.UTF8ToString(lastError(configuredTrtHandle)));
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
module._free(configuredTrtProgram.ptr);
|
|
||||||
destroy(configuredTrtHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
expectKinematicsSwitch(events, 1, 1, "configured xyzbc-trt");
|
expectKinematicsSwitch(events, 1, 1, "configured xyzbc-trt");
|
||||||
if (!events.some((event) => event.type === "rtcp-pivot" &&
|
});
|
||||||
event.line === 3 &&
|
await runStep("configured xyzbc-trt RTCP pivot event", async () => {
|
||||||
event.reserved === 1 &&
|
expectRtcpPivot(events, {
|
||||||
near(event.dwellSeconds, 11) &&
|
line: 3,
|
||||||
near(event.endX, -4.814629) &&
|
reserved: 1,
|
||||||
near(event.endY, 47.605118) &&
|
dwellSeconds: 11,
|
||||||
near(event.endZ, 48.160567) &&
|
endX: -4.814629,
|
||||||
near(event.endB, 20) &&
|
endY: 47.605118,
|
||||||
near(event.endC, -30))) {
|
endZ: 48.160567,
|
||||||
throw new Error("expected LinuxCNC WASM configured xyzbc-trt RTCP pivot from LinuxCNC trtfuncs inverse");
|
endB: 20,
|
||||||
}
|
endC: -30,
|
||||||
|
}, "expected LinuxCNC WASM configured xyzbc-trt RTCP pivot from LinuxCNC trtfuncs inverse");
|
||||||
|
});
|
||||||
|
|
||||||
events.length = 0;
|
await runStep("configured xyzac-trt RTCP parse", async () => {
|
||||||
const configuredXyzacHandle = create();
|
await withConfiguredHandle(
|
||||||
if (!configuredXyzacHandle) {
|
"configured xyzac-trt",
|
||||||
throw new Error("cnc_sim_create returned null for configured xyzac-trt case");
|
{
|
||||||
}
|
|
||||||
const configuredXyzacConfig = writeString(
|
|
||||||
module,
|
|
||||||
JSON.stringify({
|
|
||||||
backend: "linuxcnc-rs274",
|
backend: "linuxcnc-rs274",
|
||||||
rtcp: { enabled: false, toolLength: 7 },
|
rtcp: { enabled: false, toolLength: 7 },
|
||||||
switchkins: "xyzac-trt",
|
switchkins: "xyzac-trt",
|
||||||
@@ -405,47 +387,30 @@ function near(actual, expected) {
|
|||||||
yOffset: 20,
|
yOffset: 20,
|
||||||
zOffset: 10,
|
zOffset: 10,
|
||||||
},
|
},
|
||||||
}),
|
},
|
||||||
|
"M428\nG43.4\nG0 X12 Y-8 Z42 A35 C-25\n",
|
||||||
|
async () => {
|
||||||
|
},
|
||||||
);
|
);
|
||||||
try {
|
});
|
||||||
reset(configuredXyzacHandle);
|
await runStep("configured xyzac-trt kinematics switch", async () => {
|
||||||
if (setDialect(configuredXyzacHandle, 0) !== 0) {
|
|
||||||
throw new Error(module.UTF8ToString(lastError(configuredXyzacHandle)));
|
|
||||||
}
|
|
||||||
if (setCallback(configuredXyzacHandle, callbackPtr, 0) !== 0) {
|
|
||||||
throw new Error(module.UTF8ToString(lastError(configuredXyzacHandle)));
|
|
||||||
}
|
|
||||||
if (loadConfig(configuredXyzacHandle, configuredXyzacConfig.ptr, configuredXyzacConfig.len) !== 0) {
|
|
||||||
throw new Error(module.UTF8ToString(lastError(configuredXyzacHandle)));
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
module._free(configuredXyzacConfig.ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
const configuredXyzacProgram = writeString(module, "M428\nG43.4\nG0 X12 Y-8 Z42 A35 C-25\n");
|
|
||||||
try {
|
|
||||||
if (parseProgram(configuredXyzacHandle, configuredXyzacProgram.ptr, configuredXyzacProgram.len) !== 0) {
|
|
||||||
throw new Error(module.UTF8ToString(lastError(configuredXyzacHandle)));
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
module._free(configuredXyzacProgram.ptr);
|
|
||||||
destroy(configuredXyzacHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
expectKinematicsSwitch(events, 1, 1, "configured xyzac-trt");
|
expectKinematicsSwitch(events, 1, 1, "configured xyzac-trt");
|
||||||
if (!events.some((event) => event.type === "rtcp-pivot" &&
|
});
|
||||||
event.line === 3 &&
|
await runStep("configured xyzac-trt RTCP pivot event", async () => {
|
||||||
event.reserved === 1 &&
|
expectRtcpPivot(events, {
|
||||||
near(event.dwellSeconds, 7) &&
|
line: 3,
|
||||||
near(event.endX, 7.494747) &&
|
reserved: 1,
|
||||||
near(event.endY, -20.815946) &&
|
dwellSeconds: 7,
|
||||||
near(event.endZ, 18.939732) &&
|
endX: 7.494747,
|
||||||
near(event.endA, 35) &&
|
endY: -20.815946,
|
||||||
near(event.endB, 0) &&
|
endZ: 18.939732,
|
||||||
near(event.endC, -25))) {
|
endA: 35,
|
||||||
throw new Error("expected LinuxCNC WASM configured xyzac-trt RTCP pivot from LinuxCNC xyzab_tdr inverse");
|
endB: 0,
|
||||||
}
|
endC: -25,
|
||||||
|
}, "expected LinuxCNC WASM configured xyzac-trt RTCP pivot from LinuxCNC xyzab_tdr inverse");
|
||||||
|
});
|
||||||
|
|
||||||
|
await runStep("web wasm-core wrapper defaults and options", async () => {
|
||||||
globalThis.createCncSimModule = createCncSimModule;
|
globalThis.createCncSimModule = createCncSimModule;
|
||||||
const wasmCoreUrl = pathToFileURL(path.join(__dirname, "web", "src", "wasm-core.js")).href;
|
const wasmCoreUrl = pathToFileURL(path.join(__dirname, "web", "src", "wasm-core.js")).href;
|
||||||
const { createWasmSimulator } = await import(wasmCoreUrl);
|
const { createWasmSimulator } = await import(wasmCoreUrl);
|
||||||
@@ -461,6 +426,31 @@ function near(actual, expected) {
|
|||||||
near(event.end.x, 3))) {
|
near(event.end.x, 3))) {
|
||||||
throw new Error("expected web wasm-core default backend to use LinuxCNC RS274");
|
throw new Error("expected web wasm-core default backend to use LinuxCNC RS274");
|
||||||
}
|
}
|
||||||
|
if (webSimulator.fs.opfs !== null) {
|
||||||
|
throw new Error("expected Node wasm-core wrapper to leave OPFS disabled");
|
||||||
|
}
|
||||||
|
await expectErrorContaining(
|
||||||
|
() => webSimulator.parseFile("programs/node-smoke.ngc", "linuxcnc", { backend: "linuxcnc-rs274" }),
|
||||||
|
"OPFS workspace is not available",
|
||||||
|
"expected Node wasm-core parseFile to require OPFS",
|
||||||
|
);
|
||||||
|
await expectErrorContaining(
|
||||||
|
() => webSimulator.parseWithParameterFile("G21 G90\nM30\n", "parameters/node.var", "linuxcnc", {
|
||||||
|
backend: "linuxcnc-rs274",
|
||||||
|
}),
|
||||||
|
"OPFS workspace is not available",
|
||||||
|
"expected Node wasm-core parseWithParameterFile to require OPFS",
|
||||||
|
);
|
||||||
|
await expectErrorContaining(
|
||||||
|
() => webSimulator.parseFileWithParameterFile(
|
||||||
|
"programs/node-smoke.ngc",
|
||||||
|
"parameters/node.var",
|
||||||
|
"linuxcnc",
|
||||||
|
{ backend: "linuxcnc-rs274" },
|
||||||
|
),
|
||||||
|
"OPFS workspace is not available",
|
||||||
|
"expected Node wasm-core parseFileWithParameterFile to require OPFS",
|
||||||
|
);
|
||||||
|
|
||||||
const webEvents = webSimulator.parse(
|
const webEvents = webSimulator.parse(
|
||||||
"M428\nG43.4\nG0 X12 Y-8 Z42 A35 C-25\n",
|
"M428\nG43.4\nG0 X12 Y-8 Z42 A35 C-25\n",
|
||||||
@@ -475,7 +465,7 @@ function near(actual, expected) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (!webEvents.some((event) => event.type === "rtcp-pivot" &&
|
expectEvent(webEvents, (event) => event.type === "rtcp-pivot" &&
|
||||||
event.line === 3 &&
|
event.line === 3 &&
|
||||||
event.reserved === 1 &&
|
event.reserved === 1 &&
|
||||||
near(event.dwellSeconds, 7) &&
|
near(event.dwellSeconds, 7) &&
|
||||||
@@ -484,13 +474,13 @@ function near(actual, expected) {
|
|||||||
near(event.end.z, 18.939732) &&
|
near(event.end.z, 18.939732) &&
|
||||||
near(event.end.a, 35) &&
|
near(event.end.a, 35) &&
|
||||||
near(event.end.b, 0) &&
|
near(event.end.b, 0) &&
|
||||||
near(event.end.c, -25))) {
|
near(event.end.c, -25),
|
||||||
throw new Error("expected web wasm-core options to pass LinuxCNC xyzac-trt RTCP config into WASM");
|
"expected web wasm-core options to pass LinuxCNC xyzac-trt RTCP config into WASM");
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
webSimulator.dispose();
|
webSimulator.dispose();
|
||||||
delete globalThis.createCncSimModule;
|
delete globalThis.createCncSimModule;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
if (callbackPtr) {
|
if (callbackPtr) {
|
||||||
module.removeFunction(callbackPtr);
|
module.removeFunction(callbackPtr);
|
||||||
@@ -498,7 +488,7 @@ function near(actual, expected) {
|
|||||||
destroy(handle);
|
destroy(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("web wasm node smoke passed");
|
console.log(`web wasm node smoke passed (${completedSteps} steps)`);
|
||||||
})().catch((error) => {
|
})().catch((error) => {
|
||||||
console.error(error && error.stack ? error.stack : error);
|
console.error(error && error.stack ? error.stack : error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
|||||||
@@ -4,20 +4,116 @@ set -euo pipefail
|
|||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
missing=()
|
missing=()
|
||||||
for required in node web/public/cnc_sim.js web/public/cnc_sim.wasm; do
|
minimum_steps=${WASM_NODE_SMOKE_MIN_STEPS:-50}
|
||||||
if [[ "$required" == node ]]; then
|
if ! [[ "$minimum_steps" =~ ^[1-9][0-9]*$ ]]; then
|
||||||
if ! command -v node >/dev/null 2>&1; then
|
echo "WASM_NODE_SMOKE_MIN_STEPS must be a positive integer: $minimum_steps" >&2
|
||||||
missing+=("node")
|
exit 1
|
||||||
fi
|
fi
|
||||||
elif [[ ! -f "$required" ]]; then
|
|
||||||
missing+=("$required")
|
require_command() {
|
||||||
|
local command_name=$1
|
||||||
|
if ! command -v "$command_name" >/dev/null 2>&1; then
|
||||||
|
missing+=("$command_name")
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
require_file() {
|
||||||
|
local file_path=$1
|
||||||
|
if [[ ! -f "$file_path" ]]; then
|
||||||
|
missing+=("$file_path")
|
||||||
|
elif [[ ! -r "$file_path" || ! -s "$file_path" ]]; then
|
||||||
|
missing+=("readable non-empty $file_path")
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
report_missing_prerequisites() {
|
||||||
|
echo "WASM Node smoke prerequisites are missing: ${missing[*]}." >&2
|
||||||
|
echo "Run ./build-wasm.sh after activating emsdk to generate web/public/cnc_sim.js and web/public/cnc_sim.wasm." >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
required_files=(
|
||||||
|
test-web-wasm-node-smoke.cjs
|
||||||
|
web/public/cnc_sim.js
|
||||||
|
web/public/cnc_sim.wasm
|
||||||
|
web/public/linuxcnc_switchkins_remap_config_cases.json
|
||||||
|
web/src/app.js
|
||||||
|
web/src/wasm-core.js
|
||||||
|
)
|
||||||
|
|
||||||
|
for required in \
|
||||||
|
node \
|
||||||
|
"${required_files[@]}"
|
||||||
|
do
|
||||||
|
if [[ "$required" == node ]]; then
|
||||||
|
require_command node
|
||||||
|
else
|
||||||
|
require_file "$required"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if ((${#missing[@]} > 0)); then
|
if ((${#missing[@]} > 0)); then
|
||||||
echo "WASM Node smoke prerequisites are missing: ${missing[*]}." >&2
|
report_missing_prerequisites
|
||||||
echo "Run ./build-wasm.sh after activating emsdk to generate web/public/cnc_sim.js and web/public/cnc_sim.wasm." >&2
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
node test-web-wasm-node-smoke.cjs
|
if ! node - <<'JS'
|
||||||
|
const fs = require("fs");
|
||||||
|
const cases = JSON.parse(fs.readFileSync("web/public/linuxcnc_switchkins_remap_config_cases.json", "utf8"));
|
||||||
|
if (!Array.isArray(cases) || cases.length === 0) {
|
||||||
|
throw new Error("expected non-empty LinuxCNC switchkins config case JSON");
|
||||||
|
}
|
||||||
|
for (const [index, configCase] of cases.entries()) {
|
||||||
|
if (
|
||||||
|
!configCase ||
|
||||||
|
typeof configCase.field !== "string" ||
|
||||||
|
typeof configCase.value !== "string" ||
|
||||||
|
!Number.isInteger(configCase.m428) ||
|
||||||
|
!Number.isInteger(configCase.m429) ||
|
||||||
|
!Number.isInteger(configCase.m430)
|
||||||
|
) {
|
||||||
|
throw new Error(`invalid LinuxCNC switchkins config case at index ${index}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JS
|
||||||
|
then
|
||||||
|
echo "WASM Node smoke switchkins config case JSON check failed" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
node_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_node_smoke.XXXXXX.log")
|
||||||
|
cleanup() {
|
||||||
|
rm -f "$node_log"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
echo "running WASM Node smoke"
|
||||||
|
echo "WASM Node smoke node version: $(node --version)"
|
||||||
|
if ! node --check test-web-wasm-node-smoke.cjs >"$node_log" 2>&1; then
|
||||||
|
echo "WASM Node smoke syntax check failed" >&2
|
||||||
|
sed -n '1,80p' "$node_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
for web_module in web/public/cnc_sim.js web/src/wasm-core.js web/src/app.js; do
|
||||||
|
if ! node --check "$web_module" >"$node_log" 2>&1; then
|
||||||
|
echo "WASM Node smoke web module syntax check failed for $web_module" >&2
|
||||||
|
sed -n '1,80p' "$node_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if ! node test-web-wasm-node-smoke.cjs >"$node_log" 2>&1; then
|
||||||
|
echo "WASM Node smoke failed" >&2
|
||||||
|
sed -n '1,120p' "$node_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
passed_steps=$(grep -o 'web wasm node smoke passed ([0-9][0-9]* steps)' "$node_log" | tail -n 1 | grep -o '[0-9][0-9]*' || true)
|
||||||
|
if [[ -z "$passed_steps" ]]; then
|
||||||
|
echo "WASM Node smoke did not report a completed step count" >&2
|
||||||
|
sed -n '1,120p' "$node_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ((passed_steps < minimum_steps)); then
|
||||||
|
echo "WASM Node smoke covered $passed_steps steps, expected at least $minimum_steps" >&2
|
||||||
|
sed -n '1,120p' "$node_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
cat "$node_log"
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
<div class="brand">CNC SIM</div>
|
<div class="brand">CNC SIM</div>
|
||||||
<div class="status-strip">
|
<div class="status-strip">
|
||||||
<span id="wasmState" class="status alarm">WASM OFFLINE</span>
|
<span id="wasmState" class="status alarm">WASM OFFLINE</span>
|
||||||
|
<span id="storageState" class="status">OPFS INIT</span>
|
||||||
<span id="modeState" class="status">MEM</span>
|
<span id="modeState" class="status">MEM</span>
|
||||||
<span id="unitState" class="status">MM</span>
|
<span id="unitState" class="status">MM</span>
|
||||||
<span id="programState" class="status">RESET</span>
|
<span id="programState" class="status">RESET</span>
|
||||||
@@ -65,7 +66,6 @@
|
|||||||
<span>BACKEND</span>
|
<span>BACKEND</span>
|
||||||
<select id="backendSelect">
|
<select id="backendSelect">
|
||||||
<option value="linuxcnc-rs274">LINUXCNC</option>
|
<option value="linuxcnc-rs274">LINUXCNC</option>
|
||||||
<option value="smoke">SMOKE</option>
|
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<span id="lineCount">0 LINES</span>
|
<span id="lineCount">0 LINES</span>
|
||||||
|
|||||||
994
web/package-lock.json
generated
Normal file
994
web/package-lock.json
generated
Normal file
@@ -0,0 +1,994 @@
|
|||||||
|
{
|
||||||
|
"name": "cnc-wasm-simulator-web",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "cnc-wasm-simulator-web",
|
||||||
|
"dependencies": {
|
||||||
|
"three": "^0.164.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/three": "^0.164.0",
|
||||||
|
"typescript": "^5.4.5",
|
||||||
|
"vite": "^5.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/aix-ppc64": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
|
||||||
|
"cpu": [
|
||||||
|
"ppc64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"aix"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/android-arm": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/android-arm64": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/android-x64": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/darwin-arm64": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/darwin-x64": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/freebsd-arm64": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/freebsd-x64": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-arm": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-arm64": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-ia32": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-loong64": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
|
||||||
|
"cpu": [
|
||||||
|
"loong64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-mips64el": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
|
||||||
|
"cpu": [
|
||||||
|
"mips64el"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-ppc64": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
|
||||||
|
"cpu": [
|
||||||
|
"ppc64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-riscv64": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
|
||||||
|
"cpu": [
|
||||||
|
"riscv64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-s390x": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
|
||||||
|
"cpu": [
|
||||||
|
"s390x"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-x64": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/netbsd-x64": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"netbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/openbsd-x64": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/sunos-x64": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"sunos"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/win32-arm64": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/win32-ia32": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/win32-x64": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-android-arm64": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-darwin-x64": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==",
|
||||||
|
"cpu": [
|
||||||
|
"loong64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-loong64-musl": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==",
|
||||||
|
"cpu": [
|
||||||
|
"loong64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==",
|
||||||
|
"cpu": [
|
||||||
|
"ppc64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-ppc64-musl": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==",
|
||||||
|
"cpu": [
|
||||||
|
"ppc64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==",
|
||||||
|
"cpu": [
|
||||||
|
"riscv64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==",
|
||||||
|
"cpu": [
|
||||||
|
"riscv64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==",
|
||||||
|
"cpu": [
|
||||||
|
"s390x"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-openbsd-x64": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openbsd"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-openharmony-arm64": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openharmony"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@tweenjs/tween.js": {
|
||||||
|
"version": "23.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz",
|
||||||
|
"integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@types/estree": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@types/stats.js": {
|
||||||
|
"version": "0.17.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz",
|
||||||
|
"integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@types/three": {
|
||||||
|
"version": "0.164.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.164.1.tgz",
|
||||||
|
"integrity": "sha512-dR/trWDhyaNqJV38rl1TonlCA9DpnX7OPYDWD81bmBGn/+uEc3+zNalFxQcV4FlPTeDBhCY3SFWKvK6EJwL88g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@tweenjs/tween.js": "~23.1.1",
|
||||||
|
"@types/stats.js": "*",
|
||||||
|
"@types/webxr": "*",
|
||||||
|
"fflate": "~0.8.2",
|
||||||
|
"meshoptimizer": "~0.18.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/webxr": {
|
||||||
|
"version": "0.5.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz",
|
||||||
|
"integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/esbuild": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"bin": {
|
||||||
|
"esbuild": "bin/esbuild"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@esbuild/aix-ppc64": "0.21.5",
|
||||||
|
"@esbuild/android-arm": "0.21.5",
|
||||||
|
"@esbuild/android-arm64": "0.21.5",
|
||||||
|
"@esbuild/android-x64": "0.21.5",
|
||||||
|
"@esbuild/darwin-arm64": "0.21.5",
|
||||||
|
"@esbuild/darwin-x64": "0.21.5",
|
||||||
|
"@esbuild/freebsd-arm64": "0.21.5",
|
||||||
|
"@esbuild/freebsd-x64": "0.21.5",
|
||||||
|
"@esbuild/linux-arm": "0.21.5",
|
||||||
|
"@esbuild/linux-arm64": "0.21.5",
|
||||||
|
"@esbuild/linux-ia32": "0.21.5",
|
||||||
|
"@esbuild/linux-loong64": "0.21.5",
|
||||||
|
"@esbuild/linux-mips64el": "0.21.5",
|
||||||
|
"@esbuild/linux-ppc64": "0.21.5",
|
||||||
|
"@esbuild/linux-riscv64": "0.21.5",
|
||||||
|
"@esbuild/linux-s390x": "0.21.5",
|
||||||
|
"@esbuild/linux-x64": "0.21.5",
|
||||||
|
"@esbuild/netbsd-x64": "0.21.5",
|
||||||
|
"@esbuild/openbsd-x64": "0.21.5",
|
||||||
|
"@esbuild/sunos-x64": "0.21.5",
|
||||||
|
"@esbuild/win32-arm64": "0.21.5",
|
||||||
|
"@esbuild/win32-ia32": "0.21.5",
|
||||||
|
"@esbuild/win32-x64": "0.21.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fflate": {
|
||||||
|
"version": "0.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz",
|
||||||
|
"integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/fsevents": {
|
||||||
|
"version": "2.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
|
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/meshoptimizer": {
|
||||||
|
"version": "0.18.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz",
|
||||||
|
"integrity": "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/nanoid": {
|
||||||
|
"version": "3.3.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
|
||||||
|
"integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bin": {
|
||||||
|
"nanoid": "bin/nanoid.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/picocolors": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/postcss": {
|
||||||
|
"version": "8.5.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
|
||||||
|
"integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/postcss/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tidelift",
|
||||||
|
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"nanoid": "^3.3.12",
|
||||||
|
"picocolors": "^1.1.1",
|
||||||
|
"source-map-js": "^1.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || >=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rollup": {
|
||||||
|
"version": "4.60.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.4.tgz",
|
||||||
|
"integrity": "sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/estree": "1.0.8"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rollup": "dist/bin/rollup"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0",
|
||||||
|
"npm": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@rollup/rollup-android-arm-eabi": "4.60.4",
|
||||||
|
"@rollup/rollup-android-arm64": "4.60.4",
|
||||||
|
"@rollup/rollup-darwin-arm64": "4.60.4",
|
||||||
|
"@rollup/rollup-darwin-x64": "4.60.4",
|
||||||
|
"@rollup/rollup-freebsd-arm64": "4.60.4",
|
||||||
|
"@rollup/rollup-freebsd-x64": "4.60.4",
|
||||||
|
"@rollup/rollup-linux-arm-gnueabihf": "4.60.4",
|
||||||
|
"@rollup/rollup-linux-arm-musleabihf": "4.60.4",
|
||||||
|
"@rollup/rollup-linux-arm64-gnu": "4.60.4",
|
||||||
|
"@rollup/rollup-linux-arm64-musl": "4.60.4",
|
||||||
|
"@rollup/rollup-linux-loong64-gnu": "4.60.4",
|
||||||
|
"@rollup/rollup-linux-loong64-musl": "4.60.4",
|
||||||
|
"@rollup/rollup-linux-ppc64-gnu": "4.60.4",
|
||||||
|
"@rollup/rollup-linux-ppc64-musl": "4.60.4",
|
||||||
|
"@rollup/rollup-linux-riscv64-gnu": "4.60.4",
|
||||||
|
"@rollup/rollup-linux-riscv64-musl": "4.60.4",
|
||||||
|
"@rollup/rollup-linux-s390x-gnu": "4.60.4",
|
||||||
|
"@rollup/rollup-linux-x64-gnu": "4.60.4",
|
||||||
|
"@rollup/rollup-linux-x64-musl": "4.60.4",
|
||||||
|
"@rollup/rollup-openbsd-x64": "4.60.4",
|
||||||
|
"@rollup/rollup-openharmony-arm64": "4.60.4",
|
||||||
|
"@rollup/rollup-win32-arm64-msvc": "4.60.4",
|
||||||
|
"@rollup/rollup-win32-ia32-msvc": "4.60.4",
|
||||||
|
"@rollup/rollup-win32-x64-gnu": "4.60.4",
|
||||||
|
"@rollup/rollup-win32-x64-msvc": "4.60.4",
|
||||||
|
"fsevents": "~2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/source-map-js": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/three": {
|
||||||
|
"version": "0.164.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/three/-/three-0.164.1.tgz",
|
||||||
|
"integrity": "sha512-iC/hUBbl1vzFny7f5GtqzVXYjMJKaTPxiCxXfrvVdBi1Sf+jhd1CAkitiFwC7mIBFCo3MrDLJG97yisoaWig0w=="
|
||||||
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "5.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite": {
|
||||||
|
"version": "5.4.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
|
||||||
|
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"esbuild": "^0.21.3",
|
||||||
|
"postcss": "^8.4.43",
|
||||||
|
"rollup": "^4.20.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"vite": "bin/vite.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.0.0 || >=20.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/vitejs/vite?sponsor=1"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "~2.3.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/node": "^18.0.0 || >=20.0.0",
|
||||||
|
"less": "*",
|
||||||
|
"lightningcss": "^1.21.0",
|
||||||
|
"sass": "*",
|
||||||
|
"sass-embedded": "*",
|
||||||
|
"stylus": "*",
|
||||||
|
"sugarss": "*",
|
||||||
|
"terser": "^5.4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/node": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"less": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"lightningcss": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sass": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sass-embedded": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"stylus": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sugarss": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"terser": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,66 +1,383 @@
|
|||||||
[
|
[
|
||||||
{"field":"config","value":"configs/sim/axis/vismach/5axis/bridgemill/5axis.ini","m428":0,"m429":1,"m430":2},
|
{"field":"config","value":"configs/sim/axis/vismach/5axis/bridgemill/5axis.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"configPath","value":"configs/sim/axis/vismach/5axis/bridgemill/5axis.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"config_path","value":"configs/sim/axis/vismach/5axis/bridgemill/5axis.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini","value":"configs/sim/axis/vismach/5axis/bridgemill/5axis.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"iniFile","value":"configs/sim/axis/vismach/5axis/bridgemill/5axis.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"iniFileName","value":"configs/sim/axis/vismach/5axis/bridgemill/5axis.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini_file","value":"configs/sim/axis/vismach/5axis/bridgemill/5axis.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini_file_name","value":"configs/sim/axis/vismach/5axis/bridgemill/5axis.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"INI_FILE_NAME","value":"configs/sim/axis/vismach/5axis/bridgemill/5axis.ini","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"machine","value":"Sim-5Axis Bridge Mill (xyzbcw)","m428":0,"m429":1,"m430":2},
|
{"field":"machine","value":"Sim-5Axis Bridge Mill (xyzbcw)","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"kinematics","value":"5axiskins coordinates=xyzbcwy","m428":0,"m429":1,"m430":2},
|
{"field":"kinematics","value":"5axiskins coordinates=xyzbcwy","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"bridgemill","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"5axis","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"Sim-5Axis Bridge Mill (xyzbcw)","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"Sim-5Axis Bridge Mill","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"5axiskins coordinates=xyzbcwy","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"5axiskins","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"halFile","value":"5axisgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"halFile","value":"5axisgui","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"halfile","value":"5axisgui.hal","m428":0,"m429":1,"m430":2},
|
{"field":"halfile","value":"5axisgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"halfile","value":"5axisgui","m428":0,"m429":1,"m430":2},
|
{"field":"halfile","value":"5axisgui","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"hal_file","value":"5axisgui.hal","m428":0,"m429":1,"m430":2},
|
{"field":"hal_file","value":"5axisgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"hal_file","value":"5axisgui","m428":0,"m429":1,"m430":2},
|
{"field":"hal_file","value":"5axisgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"HALFILE","value":"5axisgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"HALFILE","value":"5axisgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postguiHalFile","value":"5axis_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postguiHalFile","value":"5axis_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_halfile","value":"5axis_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_halfile","value":"5axis_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_hal_file","value":"5axis_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_hal_file","value":"5axis_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"POSTGUI_HALFILE","value":"5axis_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"POSTGUI_HALFILE","value":"5axis_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"remap","value":"configs/sim/axis/vismach/5axis/bridgemill","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"remap","value":"bridgemill","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"config","value":"configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini","m428":1,"m429":0,"m430":-1},
|
{"field":"config","value":"configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini","m428":1,"m429":0,"m430":-1},
|
||||||
|
{"field":"configPath","value":"configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini","m428":1,"m429":0,"m430":-1},
|
||||||
|
{"field":"config_path","value":"configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini","m428":1,"m429":0,"m430":-1},
|
||||||
|
{"field":"ini","value":"configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini","m428":1,"m429":0,"m430":-1},
|
||||||
|
{"field":"iniFile","value":"configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini","m428":1,"m429":0,"m430":-1},
|
||||||
|
{"field":"iniFileName","value":"configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini","m428":1,"m429":0,"m430":-1},
|
||||||
|
{"field":"ini_file","value":"configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini","m428":1,"m429":0,"m430":-1},
|
||||||
|
{"field":"ini_file_name","value":"configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini","m428":1,"m429":0,"m430":-1},
|
||||||
|
{"field":"INI_FILE_NAME","value":"configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini","m428":1,"m429":0,"m430":-1},
|
||||||
{"field":"machine","value":"sim-xyzab-tdr-kins (switchkins)","m428":1,"m429":0,"m430":-1},
|
{"field":"machine","value":"sim-xyzab-tdr-kins (switchkins)","m428":1,"m429":0,"m430":-1},
|
||||||
{"field":"kinematics","value":"xyzab_tdr_kins","m428":1,"m429":0,"m430":-1},
|
{"field":"kinematics","value":"xyzab_tdr_kins","m428":1,"m429":0,"m430":-1},
|
||||||
|
{"field":"switchkins","value":"table-dual-rotary","m428":1,"m429":0,"m430":-1},
|
||||||
|
{"field":"switchkins","value":"xyzab-tdr","m428":1,"m429":0,"m430":-1},
|
||||||
|
{"field":"switchkins","value":"sim-xyzab-tdr-kins (switchkins)","m428":1,"m429":0,"m430":-1},
|
||||||
|
{"field":"switchkins","value":"sim-xyzab-tdr-kins","m428":1,"m429":0,"m430":-1},
|
||||||
|
{"field":"switchkins","value":"xyzab_tdr_kins","m428":1,"m429":0,"m430":-1},
|
||||||
|
{"field":"switchkins","value":"xyzab_tdr","m428":1,"m429":0,"m430":-1},
|
||||||
|
{"field":"switchkins","value":"xyzab-tdr-kins","m428":1,"m429":0,"m430":-1},
|
||||||
|
{"field":"postguiHalFile","value":"xyzab-tdr-postgui.hal","m428":1,"m429":0,"m430":-1},
|
||||||
|
{"field":"postguiHalFile","value":"xyzab-tdr-postgui","m428":1,"m429":0,"m430":-1},
|
||||||
|
{"field":"postgui_halfile","value":"xyzab-tdr-postgui.hal","m428":1,"m429":0,"m430":-1},
|
||||||
|
{"field":"postgui_halfile","value":"xyzab-tdr-postgui","m428":1,"m429":0,"m430":-1},
|
||||||
|
{"field":"postgui_hal_file","value":"xyzab-tdr-postgui.hal","m428":1,"m429":0,"m430":-1},
|
||||||
|
{"field":"postgui_hal_file","value":"xyzab-tdr-postgui","m428":1,"m429":0,"m430":-1},
|
||||||
|
{"field":"POSTGUI_HALFILE","value":"xyzab-tdr-postgui.hal","m428":1,"m429":0,"m430":-1},
|
||||||
|
{"field":"POSTGUI_HALFILE","value":"xyzab-tdr-postgui","m428":1,"m429":0,"m430":-1},
|
||||||
|
{"field":"remap","value":"configs/sim/axis/vismach/5axis/table-dual-rotary","m428":1,"m429":0,"m430":-1},
|
||||||
|
{"field":"remap","value":"table-dual-rotary","m428":1,"m429":0,"m430":-1},
|
||||||
{"field":"config","value":"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini","m428":1,"m429":0,"m430":2},
|
{"field":"config","value":"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"configPath","value":"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"config_path","value":"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"ini","value":"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"iniFile","value":"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"iniFileName","value":"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"ini_file","value":"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"ini_file_name","value":"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"INI_FILE_NAME","value":"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini","m428":1,"m429":0,"m430":2},
|
||||||
{"field":"machine","value":"sim-xyzac-trt-kins (switchkins)","m428":1,"m429":0,"m430":2},
|
{"field":"machine","value":"sim-xyzac-trt-kins (switchkins)","m428":1,"m429":0,"m430":2},
|
||||||
{"field":"kinematics","value":"xyzac-trt-kins sparm=identityfirst","m428":1,"m429":0,"m430":2},
|
{"field":"kinematics","value":"xyzac-trt-kins sparm=identityfirst","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"switchkins","value":"table-rotary-tilting","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"switchkins","value":"xyzac-trt","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"switchkins","value":"sim-xyzac-trt-kins (switchkins)","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"switchkins","value":"sim-xyzac-trt-kins","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"switchkins","value":"xyzac-trt-kins sparm=identityfirst","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"switchkins","value":"xyzac-trt-kins","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"postguiHalFile","value":"switchkins_postgui.hal","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"postguiHalFile","value":"switchkins_postgui","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"postgui_halfile","value":"switchkins_postgui.hal","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"postgui_halfile","value":"switchkins_postgui","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"postgui_hal_file","value":"switchkins_postgui.hal","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"postgui_hal_file","value":"switchkins_postgui","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"POSTGUI_HALFILE","value":"switchkins_postgui.hal","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"POSTGUI_HALFILE","value":"switchkins_postgui","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"remap","value":"configs/sim/axis/vismach/5axis/table-rotary-tilting","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"remap","value":"table-rotary-tilting","m428":1,"m429":0,"m430":2},
|
||||||
{"field":"config","value":"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini","m428":1,"m429":0,"m430":2},
|
{"field":"config","value":"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"configPath","value":"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"config_path","value":"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"ini","value":"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"iniFile","value":"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"iniFileName","value":"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"ini_file","value":"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"ini_file_name","value":"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"INI_FILE_NAME","value":"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini","m428":1,"m429":0,"m430":2},
|
||||||
{"field":"machine","value":"sim-xyzbc-trt-kins (switchkins)","m428":1,"m429":0,"m430":2},
|
{"field":"machine","value":"sim-xyzbc-trt-kins (switchkins)","m428":1,"m429":0,"m430":2},
|
||||||
{"field":"kinematics","value":"xyzbc-trt-kins sparm=identityfirst","m428":1,"m429":0,"m430":2},
|
{"field":"kinematics","value":"xyzbc-trt-kins sparm=identityfirst","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"switchkins","value":"xyzbc-trt","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"switchkins","value":"sim-xyzbc-trt-kins (switchkins)","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"switchkins","value":"sim-xyzbc-trt-kins","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"switchkins","value":"xyzbc-trt-kins sparm=identityfirst","m428":1,"m429":0,"m430":2},
|
||||||
|
{"field":"switchkins","value":"xyzbc-trt-kins","m428":1,"m429":0,"m430":2},
|
||||||
{"field":"config","value":"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini","m428":0,"m429":1,"m430":2},
|
{"field":"config","value":"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"configPath","value":"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"config_path","value":"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini","value":"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"iniFile","value":"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"iniFileName","value":"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini_file","value":"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini_file_name","value":"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"INI_FILE_NAME","value":"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"machine","value":"xyzacb-trsrn (switchkins)","m428":0,"m429":1,"m430":2},
|
{"field":"machine","value":"xyzacb-trsrn (switchkins)","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"kinematics","value":"xyzacb_trsrn","m428":0,"m429":1,"m430":2},
|
{"field":"kinematics","value":"xyzacb_trsrn","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"xyzacb-trsrn_twp","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"xyzacb-trsrn","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"xyzacb-trsrn (switchkins)","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"xyzacb_trsrn","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postguiHalFile","value":"xyzacb-trsrn_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postguiHalFile","value":"xyzacb-trsrn_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_halfile","value":"xyzacb-trsrn_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_halfile","value":"xyzacb-trsrn_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_hal_file","value":"xyzacb-trsrn_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_hal_file","value":"xyzacb-trsrn_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"POSTGUI_HALFILE","value":"xyzacb-trsrn_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"POSTGUI_HALFILE","value":"xyzacb-trsrn_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"remap","value":"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"remap","value":"table-rotary_spindle-rotary-nutating","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"config","value":"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini","m428":0,"m429":1,"m430":2},
|
{"field":"config","value":"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"configPath","value":"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"config_path","value":"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini","value":"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"iniFile","value":"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"iniFileName","value":"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini_file","value":"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini_file_name","value":"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"INI_FILE_NAME","value":"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"machine","value":"xyzbca-trsrn (switchkins)","m428":0,"m429":1,"m430":2},
|
{"field":"machine","value":"xyzbca-trsrn (switchkins)","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"kinematics","value":"xyzbca_trsrn","m428":0,"m429":1,"m430":2},
|
{"field":"kinematics","value":"xyzbca_trsrn","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"xyzbca-trsrn_twp","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"xyzbca-trsrn","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"xyzbca-trsrn (switchkins)","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"xyzbca_trsrn","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postguiHalFile","value":"xyzbca-trsrn_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postguiHalFile","value":"xyzbca-trsrn_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_halfile","value":"xyzbca-trsrn_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_halfile","value":"xyzbca-trsrn_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_hal_file","value":"xyzbca-trsrn_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_hal_file","value":"xyzbca-trsrn_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"POSTGUI_HALFILE","value":"xyzbca-trsrn_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"POSTGUI_HALFILE","value":"xyzbca-trsrn_postgui","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"config","value":"configs/sim/axis/vismach/hexapod-sim/hexapod.ini","m428":0,"m429":1,"m430":2},
|
{"field":"config","value":"configs/sim/axis/vismach/hexapod-sim/hexapod.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"configPath","value":"configs/sim/axis/vismach/hexapod-sim/hexapod.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"config_path","value":"configs/sim/axis/vismach/hexapod-sim/hexapod.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini","value":"configs/sim/axis/vismach/hexapod-sim/hexapod.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"iniFile","value":"configs/sim/axis/vismach/hexapod-sim/hexapod.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"iniFileName","value":"configs/sim/axis/vismach/hexapod-sim/hexapod.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini_file","value":"configs/sim/axis/vismach/hexapod-sim/hexapod.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini_file_name","value":"configs/sim/axis/vismach/hexapod-sim/hexapod.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"INI_FILE_NAME","value":"configs/sim/axis/vismach/hexapod-sim/hexapod.ini","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"machine","value":"hexapod (switchkins)","m428":0,"m429":1,"m430":2},
|
{"field":"machine","value":"hexapod (switchkins)","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"kinematics","value":"genhexkins","m428":0,"m429":1,"m430":2},
|
{"field":"kinematics","value":"genhexkins","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"hexapod-sim","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"hexapod","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"hexapod (switchkins)","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"genhexkins","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"halFile","value":"kinematics.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"halFile","value":"kinematics","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"halfile","value":"kinematics.hal","m428":0,"m429":1,"m430":2},
|
{"field":"halfile","value":"kinematics.hal","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"halfile","value":"kinematics","m428":0,"m429":1,"m430":2},
|
{"field":"halfile","value":"kinematics","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"hal_file","value":"kinematics.hal","m428":0,"m429":1,"m430":2},
|
{"field":"hal_file","value":"kinematics.hal","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"hal_file","value":"kinematics","m428":0,"m429":1,"m430":2},
|
{"field":"hal_file","value":"kinematics","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"HALFILE","value":"kinematics.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"HALFILE","value":"kinematics","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postguiHalFile","value":"hexapod_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postguiHalFile","value":"hexapod_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_halfile","value":"hexapod_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_halfile","value":"hexapod_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_hal_file","value":"hexapod_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_hal_file","value":"hexapod_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"POSTGUI_HALFILE","value":"hexapod_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"POSTGUI_HALFILE","value":"hexapod_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"remap","value":"configs/sim/axis/vismach/hexapod-sim","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"remap","value":"hexapod-sim","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"config","value":"configs/sim/axis/vismach/melfa-sim/melfa.ini","m428":0,"m429":1,"m430":2},
|
{"field":"config","value":"configs/sim/axis/vismach/melfa-sim/melfa.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"configPath","value":"configs/sim/axis/vismach/melfa-sim/melfa.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"config_path","value":"configs/sim/axis/vismach/melfa-sim/melfa.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini","value":"configs/sim/axis/vismach/melfa-sim/melfa.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"iniFile","value":"configs/sim/axis/vismach/melfa-sim/melfa.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"iniFileName","value":"configs/sim/axis/vismach/melfa-sim/melfa.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini_file","value":"configs/sim/axis/vismach/melfa-sim/melfa.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini_file_name","value":"configs/sim/axis/vismach/melfa-sim/melfa.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"INI_FILE_NAME","value":"configs/sim/axis/vismach/melfa-sim/melfa.ini","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"machine","value":"melfa (mm)","m428":0,"m429":1,"m430":2},
|
{"field":"machine","value":"melfa (mm)","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"kinematics","value":"genserkins","m428":0,"m429":1,"m430":2},
|
{"field":"kinematics","value":"genserkins","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"melfa-sim","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"melfa","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"melfa (mm)","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"genserkins","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"halFile","value":"melfa_dh.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"halFile","value":"melfa_dh","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"halfile","value":"melfa_dh.hal","m428":0,"m429":1,"m430":2},
|
{"field":"halfile","value":"melfa_dh.hal","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"halfile","value":"melfa_dh","m428":0,"m429":1,"m430":2},
|
{"field":"halfile","value":"melfa_dh","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"hal_file","value":"melfa_dh.hal","m428":0,"m429":1,"m430":2},
|
{"field":"hal_file","value":"melfa_dh.hal","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"hal_file","value":"melfa_dh","m428":0,"m429":1,"m430":2},
|
{"field":"hal_file","value":"melfa_dh","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"HALFILE","value":"melfa_dh.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"HALFILE","value":"melfa_dh","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postguiHalFile","value":"melfa-postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postguiHalFile","value":"melfa-postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_halfile","value":"melfa-postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_halfile","value":"melfa-postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_hal_file","value":"melfa-postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_hal_file","value":"melfa-postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"POSTGUI_HALFILE","value":"melfa-postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"POSTGUI_HALFILE","value":"melfa-postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"remap","value":"configs/sim/axis/vismach/melfa-sim","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"remap","value":"melfa-sim","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"config","value":"configs/sim/axis/vismach/millturn/millturn.ini","m428":0,"m429":1,"m430":-1},
|
{"field":"config","value":"configs/sim/axis/vismach/millturn/millturn.ini","m428":0,"m429":1,"m430":-1},
|
||||||
|
{"field":"configPath","value":"configs/sim/axis/vismach/millturn/millturn.ini","m428":0,"m429":1,"m430":-1},
|
||||||
|
{"field":"config_path","value":"configs/sim/axis/vismach/millturn/millturn.ini","m428":0,"m429":1,"m430":-1},
|
||||||
|
{"field":"ini","value":"configs/sim/axis/vismach/millturn/millturn.ini","m428":0,"m429":1,"m430":-1},
|
||||||
|
{"field":"iniFile","value":"configs/sim/axis/vismach/millturn/millturn.ini","m428":0,"m429":1,"m430":-1},
|
||||||
|
{"field":"iniFileName","value":"configs/sim/axis/vismach/millturn/millturn.ini","m428":0,"m429":1,"m430":-1},
|
||||||
|
{"field":"ini_file","value":"configs/sim/axis/vismach/millturn/millturn.ini","m428":0,"m429":1,"m430":-1},
|
||||||
|
{"field":"ini_file_name","value":"configs/sim/axis/vismach/millturn/millturn.ini","m428":0,"m429":1,"m430":-1},
|
||||||
|
{"field":"INI_FILE_NAME","value":"configs/sim/axis/vismach/millturn/millturn.ini","m428":0,"m429":1,"m430":-1},
|
||||||
{"field":"machine","value":"millturn (mm)","m428":0,"m429":1,"m430":-1},
|
{"field":"machine","value":"millturn (mm)","m428":0,"m429":1,"m430":-1},
|
||||||
{"field":"kinematics","value":"millturn","m428":0,"m429":1,"m430":-1},
|
{"field":"kinematics","value":"millturn","m428":0,"m429":1,"m430":-1},
|
||||||
|
{"field":"switchkins","value":"millturn","m428":0,"m429":1,"m430":-1},
|
||||||
|
{"field":"switchkins","value":"millturn (mm)","m428":0,"m429":1,"m430":-1},
|
||||||
|
{"field":"halFile","value":"millturn.hal","m428":0,"m429":1,"m430":-1},
|
||||||
|
{"field":"halFile","value":"millturn","m428":0,"m429":1,"m430":-1},
|
||||||
{"field":"halfile","value":"millturn.hal","m428":0,"m429":1,"m430":-1},
|
{"field":"halfile","value":"millturn.hal","m428":0,"m429":1,"m430":-1},
|
||||||
{"field":"halfile","value":"millturn","m428":0,"m429":1,"m430":-1},
|
{"field":"halfile","value":"millturn","m428":0,"m429":1,"m430":-1},
|
||||||
{"field":"hal_file","value":"millturn.hal","m428":0,"m429":1,"m430":-1},
|
{"field":"hal_file","value":"millturn.hal","m428":0,"m429":1,"m430":-1},
|
||||||
{"field":"hal_file","value":"millturn","m428":0,"m429":1,"m430":-1},
|
{"field":"hal_file","value":"millturn","m428":0,"m429":1,"m430":-1},
|
||||||
|
{"field":"HALFILE","value":"millturn.hal","m428":0,"m429":1,"m430":-1},
|
||||||
|
{"field":"HALFILE","value":"millturn","m428":0,"m429":1,"m430":-1},
|
||||||
|
{"field":"postguiHalFile","value":"millturn-postgui.hal","m428":0,"m429":1,"m430":-1},
|
||||||
|
{"field":"postguiHalFile","value":"millturn-postgui","m428":0,"m429":1,"m430":-1},
|
||||||
|
{"field":"postgui_halfile","value":"millturn-postgui.hal","m428":0,"m429":1,"m430":-1},
|
||||||
|
{"field":"postgui_halfile","value":"millturn-postgui","m428":0,"m429":1,"m430":-1},
|
||||||
|
{"field":"postgui_hal_file","value":"millturn-postgui.hal","m428":0,"m429":1,"m430":-1},
|
||||||
|
{"field":"postgui_hal_file","value":"millturn-postgui","m428":0,"m429":1,"m430":-1},
|
||||||
|
{"field":"POSTGUI_HALFILE","value":"millturn-postgui.hal","m428":0,"m429":1,"m430":-1},
|
||||||
|
{"field":"POSTGUI_HALFILE","value":"millturn-postgui","m428":0,"m429":1,"m430":-1},
|
||||||
|
{"field":"remap","value":"configs/sim/axis/vismach/millturn","m428":0,"m429":1,"m430":-1},
|
||||||
|
{"field":"remap","value":"millturn","m428":0,"m429":1,"m430":-1},
|
||||||
{"field":"config","value":"configs/sim/axis/vismach/puma/puma.ini","m428":0,"m429":1,"m430":2},
|
{"field":"config","value":"configs/sim/axis/vismach/puma/puma.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"configPath","value":"configs/sim/axis/vismach/puma/puma.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"config_path","value":"configs/sim/axis/vismach/puma/puma.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini","value":"configs/sim/axis/vismach/puma/puma.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"iniFile","value":"configs/sim/axis/vismach/puma/puma.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"iniFileName","value":"configs/sim/axis/vismach/puma/puma.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini_file","value":"configs/sim/axis/vismach/puma/puma.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini_file_name","value":"configs/sim/axis/vismach/puma/puma.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"INI_FILE_NAME","value":"configs/sim/axis/vismach/puma/puma.ini","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"machine","value":"PUMA (pumakins,switchkins)","m428":0,"m429":1,"m430":2},
|
{"field":"machine","value":"PUMA (pumakins,switchkins)","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"kinematics","value":"pumakins","m428":0,"m429":1,"m430":2},
|
{"field":"kinematics","value":"pumakins","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"puma","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"PUMA (pumakins,switchkins)","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"PUMA","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"pumakins","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"halFile","value":"puma_dh.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"halFile","value":"puma_dh","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"halfile","value":"puma_dh.hal","m428":0,"m429":1,"m430":2},
|
{"field":"halfile","value":"puma_dh.hal","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"halfile","value":"puma_dh","m428":0,"m429":1,"m430":2},
|
{"field":"halfile","value":"puma_dh","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"hal_file","value":"puma_dh.hal","m428":0,"m429":1,"m430":2},
|
{"field":"hal_file","value":"puma_dh.hal","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"hal_file","value":"puma_dh","m428":0,"m429":1,"m430":2},
|
{"field":"hal_file","value":"puma_dh","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"HALFILE","value":"puma_dh.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"HALFILE","value":"puma_dh","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postguiHalFile","value":"puma_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postguiHalFile","value":"puma_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_halfile","value":"puma_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_halfile","value":"puma_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_hal_file","value":"puma_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_hal_file","value":"puma_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"POSTGUI_HALFILE","value":"puma_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"POSTGUI_HALFILE","value":"puma_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"remap","value":"configs/sim/axis/vismach/puma","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"remap","value":"puma","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"config","value":"configs/sim/axis/vismach/puma/puma_cube.ini","m428":0,"m429":1,"m430":2},
|
{"field":"config","value":"configs/sim/axis/vismach/puma/puma_cube.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"configPath","value":"configs/sim/axis/vismach/puma/puma_cube.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"config_path","value":"configs/sim/axis/vismach/puma/puma_cube.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini","value":"configs/sim/axis/vismach/puma/puma_cube.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"iniFile","value":"configs/sim/axis/vismach/puma/puma_cube.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"iniFileName","value":"configs/sim/axis/vismach/puma/puma_cube.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini_file","value":"configs/sim/axis/vismach/puma/puma_cube.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini_file_name","value":"configs/sim/axis/vismach/puma/puma_cube.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"INI_FILE_NAME","value":"configs/sim/axis/vismach/puma/puma_cube.ini","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"machine","value":"puma_cube.ini (pumakins)","m428":0,"m429":1,"m430":2},
|
{"field":"machine","value":"puma_cube.ini (pumakins)","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"puma_cube","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"puma_cube.ini (pumakins)","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"puma_cube.ini","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"config","value":"configs/sim/axis/vismach/puma/puma560.ini","m428":0,"m429":1,"m430":2},
|
{"field":"config","value":"configs/sim/axis/vismach/puma/puma560.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"configPath","value":"configs/sim/axis/vismach/puma/puma560.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"config_path","value":"configs/sim/axis/vismach/puma/puma560.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini","value":"configs/sim/axis/vismach/puma/puma560.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"iniFile","value":"configs/sim/axis/vismach/puma/puma560.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"iniFileName","value":"configs/sim/axis/vismach/puma/puma560.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini_file","value":"configs/sim/axis/vismach/puma/puma560.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini_file_name","value":"configs/sim/axis/vismach/puma/puma560.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"INI_FILE_NAME","value":"configs/sim/axis/vismach/puma/puma560.ini","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"machine","value":"puma560 (switchkins) (inch)","m428":0,"m429":1,"m430":2},
|
{"field":"machine","value":"puma560 (switchkins) (inch)","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"puma560","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"puma560 (switchkins) (inch)","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"halFile","value":"puma560_dh.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"halFile","value":"puma560_dh","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"halfile","value":"puma560_dh.hal","m428":0,"m429":1,"m430":2},
|
{"field":"halfile","value":"puma560_dh.hal","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"halfile","value":"puma560_dh","m428":0,"m429":1,"m430":2},
|
{"field":"halfile","value":"puma560_dh","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"hal_file","value":"puma560_dh.hal","m428":0,"m429":1,"m430":2},
|
{"field":"hal_file","value":"puma560_dh.hal","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"hal_file","value":"puma560_dh","m428":0,"m429":1,"m430":2},
|
{"field":"hal_file","value":"puma560_dh","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"HALFILE","value":"puma560_dh.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"HALFILE","value":"puma560_dh","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postguiHalFile","value":"puma560_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postguiHalFile","value":"puma560_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_halfile","value":"puma560_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_halfile","value":"puma560_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_hal_file","value":"puma560_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_hal_file","value":"puma560_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"POSTGUI_HALFILE","value":"puma560_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"POSTGUI_HALFILE","value":"puma560_postgui","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"config","value":"configs/sim/axis/vismach/puma/puma560_uvw.ini","m428":0,"m429":1,"m430":2},
|
{"field":"config","value":"configs/sim/axis/vismach/puma/puma560_uvw.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"configPath","value":"configs/sim/axis/vismach/puma/puma560_uvw.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"config_path","value":"configs/sim/axis/vismach/puma/puma560_uvw.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini","value":"configs/sim/axis/vismach/puma/puma560_uvw.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"iniFile","value":"configs/sim/axis/vismach/puma/puma560_uvw.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"iniFileName","value":"configs/sim/axis/vismach/puma/puma560_uvw.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini_file","value":"configs/sim/axis/vismach/puma/puma560_uvw.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini_file_name","value":"configs/sim/axis/vismach/puma/puma560_uvw.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"INI_FILE_NAME","value":"configs/sim/axis/vismach/puma/puma560_uvw.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"puma560_uvw","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"config","value":"configs/sim/axis/vismach/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
{"field":"config","value":"configs/sim/axis/vismach/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"configPath","value":"configs/sim/axis/vismach/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"config_path","value":"configs/sim/axis/vismach/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini","value":"configs/sim/axis/vismach/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"iniFile","value":"configs/sim/axis/vismach/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"iniFileName","value":"configs/sim/axis/vismach/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini_file","value":"configs/sim/axis/vismach/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini_file_name","value":"configs/sim/axis/vismach/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"INI_FILE_NAME","value":"configs/sim/axis/vismach/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"machine","value":"SCARA (genserkins,switchkins)","m428":0,"m429":1,"m430":2},
|
{"field":"machine","value":"SCARA (genserkins,switchkins)","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"kinematics","value":"scarakins coordinates=xyzcab","m428":0,"m429":1,"m430":2},
|
{"field":"kinematics","value":"scarakins coordinates=xyzcab","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"scara","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"SCARA (genserkins,switchkins)","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"SCARA","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"scarakins coordinates=xyzcab","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"switchkins","value":"scarakins","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postguiHalFile","value":"scara_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postguiHalFile","value":"scara_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_halfile","value":"scara_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_halfile","value":"scara_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_hal_file","value":"scara_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"postgui_hal_file","value":"scara_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"POSTGUI_HALFILE","value":"scara_postgui.hal","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"POSTGUI_HALFILE","value":"scara_postgui","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"remap","value":"configs/sim/axis/vismach/scara","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"remap","value":"scara","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"config","value":"configs/sim/qtaxis/non-trivial/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
{"field":"config","value":"configs/sim/qtaxis/non-trivial/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
{"field":"config","value":"configs/sim/qtvcp_screens/non-trivial/scara/scara.ini","m428":0,"m429":1,"m430":2}
|
{"field":"configPath","value":"configs/sim/qtaxis/non-trivial/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"config_path","value":"configs/sim/qtaxis/non-trivial/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini","value":"configs/sim/qtaxis/non-trivial/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"iniFile","value":"configs/sim/qtaxis/non-trivial/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"iniFileName","value":"configs/sim/qtaxis/non-trivial/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini_file","value":"configs/sim/qtaxis/non-trivial/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini_file_name","value":"configs/sim/qtaxis/non-trivial/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"INI_FILE_NAME","value":"configs/sim/qtaxis/non-trivial/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"remap","value":"configs/sim/qtaxis/non-trivial/scara","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"config","value":"configs/sim/qtvcp_screens/non-trivial/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"configPath","value":"configs/sim/qtvcp_screens/non-trivial/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"config_path","value":"configs/sim/qtvcp_screens/non-trivial/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini","value":"configs/sim/qtvcp_screens/non-trivial/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"iniFile","value":"configs/sim/qtvcp_screens/non-trivial/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"iniFileName","value":"configs/sim/qtvcp_screens/non-trivial/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini_file","value":"configs/sim/qtvcp_screens/non-trivial/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"ini_file_name","value":"configs/sim/qtvcp_screens/non-trivial/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"INI_FILE_NAME","value":"configs/sim/qtvcp_screens/non-trivial/scara/scara.ini","m428":0,"m429":1,"m430":2},
|
||||||
|
{"field":"remap","value":"configs/sim/qtvcp_screens/non-trivial/scara","m428":0,"m429":1,"m430":2}
|
||||||
]
|
]
|
||||||
|
|||||||
225
web/src/app.js
225
web/src/app.js
@@ -2,6 +2,7 @@ import { createWasmSimulator } from "./wasm-core.js";
|
|||||||
|
|
||||||
const elements = {
|
const elements = {
|
||||||
wasmState: document.querySelector("#wasmState"),
|
wasmState: document.querySelector("#wasmState"),
|
||||||
|
storageState: document.querySelector("#storageState"),
|
||||||
modeState: document.querySelector("#modeState"),
|
modeState: document.querySelector("#modeState"),
|
||||||
unitState: document.querySelector("#unitState"),
|
unitState: document.querySelector("#unitState"),
|
||||||
programState: document.querySelector("#programState"),
|
programState: document.querySelector("#programState"),
|
||||||
@@ -26,12 +27,82 @@ const elements = {
|
|||||||
|
|
||||||
let simulatorPromise = null;
|
let simulatorPromise = null;
|
||||||
let lastEvents = [];
|
let lastEvents = [];
|
||||||
|
let isParsing = false;
|
||||||
|
let hasUserEditedProgram = false;
|
||||||
|
let saveProgramTimer = 0;
|
||||||
|
let saveProgramSequence = 0;
|
||||||
|
let saveProgramPromise = Promise.resolve();
|
||||||
|
let startupPromise = Promise.resolve();
|
||||||
|
const programPath = "programs/current.ngc";
|
||||||
|
// LinuxCNC source basis: interp_internal.hh defines RS274NGC_PARAMETER_FILE_NAME_DEFAULT.
|
||||||
|
const linuxCncParameterFileName = "rs274ngc.var";
|
||||||
|
const parameterPath = `parameters/${linuxCncParameterFileName}`;
|
||||||
|
const linuxCncBackend = "linuxcnc-rs274";
|
||||||
|
const opfsOptions = { required: true, mountPoint: "/cnc", workspacePath: "cnc-simulator" };
|
||||||
|
|
||||||
function setStatus(node, text, alarm = false) {
|
function setStatus(node, text, alarm = false) {
|
||||||
node.textContent = text;
|
node.textContent = text;
|
||||||
node.classList.toggle("alarm", alarm);
|
node.classList.toggle("alarm", alarm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setRuntimeReady(simulator) {
|
||||||
|
setStatus(elements.wasmState, "WASM ONLINE");
|
||||||
|
if (simulator.fs?.opfs) {
|
||||||
|
const storageState = elements.storageState.textContent;
|
||||||
|
if (storageState === "OPFS SAVING" || storageState.startsWith("OPFS SAVED")) {
|
||||||
|
setStatus(elements.storageState, storageState);
|
||||||
|
} else {
|
||||||
|
setStatus(elements.storageState, "OPFS READY");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setStatus(elements.storageState, "OPFS OFF", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setRuntimeUnavailable() {
|
||||||
|
setStatus(elements.wasmState, "WASM OFFLINE", true);
|
||||||
|
setStatus(elements.storageState, "OPFS OFF", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setControlsEnabled(enabled) {
|
||||||
|
elements.parseBtn.disabled = !enabled;
|
||||||
|
elements.resetBtn.disabled = !enabled;
|
||||||
|
elements.holdBtn.disabled = !enabled;
|
||||||
|
elements.stopBtn.disabled = !enabled;
|
||||||
|
elements.backendSelect.disabled = !enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setStorageSaved(size) {
|
||||||
|
setStatus(elements.storageState, Number.isFinite(size) ? `OPFS SAVED ${size}B` : "OPFS SAVED");
|
||||||
|
}
|
||||||
|
|
||||||
|
function setProgramReady() {
|
||||||
|
if (elements.programState.textContent !== "ALARM") {
|
||||||
|
setStatus(elements.programState, "READY");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function statIfExists(opfs, path) {
|
||||||
|
if (!(await opfs.exists(path))) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return opfs.stat(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function logWorkspaceState(opfs) {
|
||||||
|
const programStat = await statIfExists(opfs, programPath);
|
||||||
|
if (programStat?.kind === "file") {
|
||||||
|
log(`OPFS PROGRAM ${programStat.size}B`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const parameterStat = await statIfExists(opfs, parameterPath);
|
||||||
|
if (parameterStat?.kind === "file") {
|
||||||
|
log(`OPFS PARAMETERS ${parameterStat.size}B`);
|
||||||
|
} else {
|
||||||
|
log("OPFS PARAMETERS PENDING");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function log(message, error = false) {
|
function log(message, error = false) {
|
||||||
const line = document.createElement("div");
|
const line = document.createElement("div");
|
||||||
line.className = `alarm-line${error ? " error" : ""}`;
|
line.className = `alarm-line${error ? " error" : ""}`;
|
||||||
@@ -163,21 +234,63 @@ function updateLineCount() {
|
|||||||
elements.lineCount.textContent = `${count} LINES`;
|
elements.lineCount.textContent = `${count} LINES`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getOpfsWorkspace() {
|
||||||
|
const simulator = await getSimulator();
|
||||||
|
setRuntimeReady(simulator);
|
||||||
|
if (!simulator.fs.opfs) {
|
||||||
|
throw new Error("OPFS workspace is required for browser program parsing");
|
||||||
|
}
|
||||||
|
return simulator.fs.opfs;
|
||||||
|
}
|
||||||
|
|
||||||
async function getSimulator() {
|
async function getSimulator() {
|
||||||
if (!simulatorPromise) {
|
if (!simulatorPromise) {
|
||||||
simulatorPromise = createWasmSimulator();
|
simulatorPromise = createWasmSimulator({
|
||||||
|
opfs: opfsOptions,
|
||||||
|
}).then((simulator) => {
|
||||||
|
setRuntimeReady(simulator);
|
||||||
|
return simulator;
|
||||||
|
}).catch((error) => {
|
||||||
|
simulatorPromise = null;
|
||||||
|
globalThis.__cncSimulatorForTest = null;
|
||||||
|
setRuntimeUnavailable();
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
globalThis.__cncSimulatorForTest = simulatorPromise;
|
||||||
}
|
}
|
||||||
return simulatorPromise;
|
return simulatorPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function parseProgram() {
|
async function parseProgram() {
|
||||||
|
if (isParsing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isParsing = true;
|
||||||
|
setControlsEnabled(false);
|
||||||
setStatus(elements.programState, "RUN");
|
setStatus(elements.programState, "RUN");
|
||||||
|
let runtimeReady = false;
|
||||||
|
let savedProgramSize = null;
|
||||||
try {
|
try {
|
||||||
|
await startupPromise.catch(() => {});
|
||||||
const simulator = await getSimulator();
|
const simulator = await getSimulator();
|
||||||
setStatus(elements.wasmState, "WASM ONLINE");
|
runtimeReady = true;
|
||||||
const events = simulator.parse(elements.input.value, "linuxcnc", {
|
setRuntimeReady(simulator);
|
||||||
backend: elements.backendSelect.value,
|
const parseOptions = {
|
||||||
});
|
backend: linuxCncBackend,
|
||||||
|
};
|
||||||
|
if (!simulator.fs.opfs) {
|
||||||
|
throw new Error("OPFS workspace is required for browser program parsing");
|
||||||
|
}
|
||||||
|
elements.backendSelect.value = linuxCncBackend;
|
||||||
|
await saveProgramPromise.catch(() => {});
|
||||||
|
await simulator.fs.opfs.writeFile(programPath, elements.input.value);
|
||||||
|
const programStat = await simulator.fs.opfs.stat(programPath);
|
||||||
|
savedProgramSize = programStat.kind === "file" ? programStat.size : null;
|
||||||
|
setStorageSaved(savedProgramSize);
|
||||||
|
const events = await simulator.parseFileWithParameterFile(programPath, parameterPath, "linuxcnc", parseOptions);
|
||||||
|
const parameterStat = await simulator.fs.opfs.stat(parameterPath);
|
||||||
|
setStorageSaved(parameterStat.size);
|
||||||
|
log(`OPFS PARAMETERS ${parameterStat.size}B`);
|
||||||
lastEvents = events;
|
lastEvents = events;
|
||||||
drawToolpath(events);
|
drawToolpath(events);
|
||||||
|
|
||||||
@@ -193,9 +306,82 @@ async function parseProgram() {
|
|||||||
setStatus(elements.programState, "END");
|
setStatus(elements.programState, "END");
|
||||||
log(`OK ${events.length} EVENTS`);
|
log(`OK ${events.length} EVENTS`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setStatus(elements.wasmState, "WASM OFFLINE", true);
|
if (!runtimeReady) {
|
||||||
|
setRuntimeUnavailable();
|
||||||
|
}
|
||||||
|
if (savedProgramSize !== null) {
|
||||||
|
log(`OPFS PROGRAM SAVED ${savedProgramSize}B`);
|
||||||
|
}
|
||||||
setStatus(elements.programState, "ALARM", true);
|
setStatus(elements.programState, "ALARM", true);
|
||||||
log(error.message, true);
|
log(error.message, true);
|
||||||
|
} finally {
|
||||||
|
isParsing = false;
|
||||||
|
setControlsEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveProgramDraft() {
|
||||||
|
const sequence = ++saveProgramSequence;
|
||||||
|
saveProgramPromise = saveProgramPromise.catch(() => {}).then(async () => {
|
||||||
|
const opfs = await getOpfsWorkspace();
|
||||||
|
const program = elements.input.value;
|
||||||
|
setStatus(elements.storageState, "OPFS SAVING");
|
||||||
|
await opfs.writeFile(programPath, program);
|
||||||
|
if (sequence === saveProgramSequence) {
|
||||||
|
const stat = await opfs.stat(programPath);
|
||||||
|
setStorageSaved(stat.size);
|
||||||
|
setProgramReady();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await saveProgramPromise;
|
||||||
|
} catch (error) {
|
||||||
|
setStatus(elements.storageState, "OPFS ALARM", true);
|
||||||
|
log(error.message, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function scheduleProgramSave() {
|
||||||
|
window.clearTimeout(saveProgramTimer);
|
||||||
|
saveProgramTimer = window.setTimeout(() => {
|
||||||
|
void saveProgramDraft();
|
||||||
|
}, 350);
|
||||||
|
}
|
||||||
|
|
||||||
|
function flushProgramSave() {
|
||||||
|
window.clearTimeout(saveProgramTimer);
|
||||||
|
if (hasUserEditedProgram) {
|
||||||
|
void saveProgramDraft();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function restoreProgram() {
|
||||||
|
try {
|
||||||
|
const opfs = await getOpfsWorkspace();
|
||||||
|
if (!(await opfs.exists(programPath))) {
|
||||||
|
await opfs.writeFile(programPath, elements.input.value);
|
||||||
|
const stat = await opfs.stat(programPath);
|
||||||
|
setStorageSaved(stat.size);
|
||||||
|
setProgramReady();
|
||||||
|
log(`OPFS PROGRAM CREATED ${stat.size}B`);
|
||||||
|
await logWorkspaceState(opfs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const program = new TextDecoder().decode(await opfs.readFile(programPath));
|
||||||
|
if (hasUserEditedProgram) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
elements.input.value = program;
|
||||||
|
updateLineCount();
|
||||||
|
const stat = await opfs.stat(programPath);
|
||||||
|
setStorageSaved(stat.size);
|
||||||
|
setProgramReady();
|
||||||
|
log("OPFS PROGRAM RESTORED");
|
||||||
|
await logWorkspaceState(opfs);
|
||||||
|
} catch (error) {
|
||||||
|
if (error?.name !== "NotFoundError") {
|
||||||
|
log(error.message, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,6 +391,7 @@ document.querySelectorAll("[data-mode]").forEach((button) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
elements.backendSelect.value = linuxCncBackend;
|
||||||
elements.parseBtn.addEventListener("click", parseProgram);
|
elements.parseBtn.addEventListener("click", parseProgram);
|
||||||
elements.resetBtn.addEventListener("click", () => {
|
elements.resetBtn.addEventListener("click", () => {
|
||||||
lastEvents = [];
|
lastEvents = [];
|
||||||
@@ -214,8 +401,32 @@ elements.resetBtn.addEventListener("click", () => {
|
|||||||
});
|
});
|
||||||
elements.holdBtn.addEventListener("click", () => setStatus(elements.programState, "HOLD"));
|
elements.holdBtn.addEventListener("click", () => setStatus(elements.programState, "HOLD"));
|
||||||
elements.stopBtn.addEventListener("click", () => setStatus(elements.programState, "STOP", true));
|
elements.stopBtn.addEventListener("click", () => setStatus(elements.programState, "STOP", true));
|
||||||
elements.input.addEventListener("input", updateLineCount);
|
elements.input.addEventListener("input", () => {
|
||||||
|
updateLineCount();
|
||||||
|
hasUserEditedProgram = true;
|
||||||
|
setStatus(elements.programState, "EDIT");
|
||||||
|
scheduleProgramSave();
|
||||||
|
});
|
||||||
|
document.addEventListener("keydown", (event) => {
|
||||||
|
if (!(event.ctrlKey || event.metaKey) || event.altKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.key === "s") {
|
||||||
|
event.preventDefault();
|
||||||
|
void saveProgramDraft();
|
||||||
|
} else if (event.key === "Enter") {
|
||||||
|
event.preventDefault();
|
||||||
|
void parseProgram();
|
||||||
|
}
|
||||||
|
});
|
||||||
window.addEventListener("resize", resizeCanvas);
|
window.addEventListener("resize", resizeCanvas);
|
||||||
|
window.addEventListener("pagehide", flushProgramSave);
|
||||||
|
document.addEventListener("visibilitychange", () => {
|
||||||
|
if (document.visibilityState === "hidden") {
|
||||||
|
flushProgramSave();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
updateLineCount();
|
updateLineCount();
|
||||||
resizeCanvas();
|
resizeCanvas();
|
||||||
|
startupPromise = restoreProgram();
|
||||||
|
|||||||
@@ -84,8 +84,19 @@ export type CncParseOptions = {
|
|||||||
config?: string;
|
config?: string;
|
||||||
configPath?: string;
|
configPath?: string;
|
||||||
config_path?: string;
|
config_path?: string;
|
||||||
|
ini?: string;
|
||||||
|
iniFile?: string;
|
||||||
|
iniFileName?: string;
|
||||||
|
ini_file?: string;
|
||||||
|
ini_file_name?: string;
|
||||||
|
INI_FILE_NAME?: string;
|
||||||
|
halFile?: string;
|
||||||
halfile?: string;
|
halfile?: string;
|
||||||
hal_file?: string;
|
hal_file?: string;
|
||||||
|
postguiHalFile?: string;
|
||||||
|
postgui_halfile?: string;
|
||||||
|
postgui_hal_file?: string;
|
||||||
|
POSTGUI_HALFILE?: string;
|
||||||
kinematics?: string;
|
kinematics?: string;
|
||||||
trt?: "xyzbc" | "xyzac" | string;
|
trt?: "xyzbc" | "xyzac" | string;
|
||||||
pivotLength?: number;
|
pivotLength?: number;
|
||||||
|
|||||||
52
web/src/wasm-core.d.ts
vendored
52
web/src/wasm-core.d.ts
vendored
@@ -2,13 +2,65 @@ import type { CncDialect, CncEvent, CncParseOptions } from "./index";
|
|||||||
|
|
||||||
export type WasmSimulator = {
|
export type WasmSimulator = {
|
||||||
parse(program: string, dialect?: CncDialect, options?: CncParseOptions): CncEvent[];
|
parse(program: string, dialect?: CncDialect, options?: CncParseOptions): CncEvent[];
|
||||||
|
parseFile(path: string, dialect?: CncDialect, options?: CncParseOptions): Promise<CncEvent[]>;
|
||||||
|
parseWithParameterFile(
|
||||||
|
program: string,
|
||||||
|
parameterPath: string,
|
||||||
|
dialect?: CncDialect,
|
||||||
|
options?: CncParseOptions,
|
||||||
|
): Promise<CncEvent[]>;
|
||||||
|
parseFileWithParameterFile(
|
||||||
|
path: string,
|
||||||
|
parameterPath: string,
|
||||||
|
dialect?: CncDialect,
|
||||||
|
options?: CncParseOptions,
|
||||||
|
): Promise<CncEvent[]>;
|
||||||
dispose(): void;
|
dispose(): void;
|
||||||
|
fs: {
|
||||||
|
opfs: WasmOpfsWorkspace | null;
|
||||||
|
module: unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type WasmOpfsWorkspace = {
|
||||||
|
mountPoint: string;
|
||||||
|
workspacePath: string;
|
||||||
|
resolvePath(path: string): string;
|
||||||
|
readFile(path: string): Promise<Uint8Array>;
|
||||||
|
readDirectory(path: string): Promise<string[]>;
|
||||||
|
persistFile(path: string): Promise<Uint8Array>;
|
||||||
|
persistDirectory(path: string): Promise<string[]>;
|
||||||
|
writeFile(path: string, data: Uint8Array | string): Promise<void>;
|
||||||
|
copyFile(fromPath: string, toPath: string): Promise<Uint8Array>;
|
||||||
|
moveFile(fromPath: string, toPath: string): Promise<void>;
|
||||||
|
exists(path: string): Promise<boolean>;
|
||||||
|
stat(path: string): Promise<WasmOpfsEntryStat>;
|
||||||
|
loadParameterFile(path: string): Promise<Uint8Array | null>;
|
||||||
|
persistParameterFile(path: string): Promise<Uint8Array>;
|
||||||
|
removeFile(path: string): Promise<void>;
|
||||||
|
removeDirectory(path: string): Promise<void>;
|
||||||
|
clear(): Promise<void>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type WasmOpfsEntryStat =
|
||||||
|
| {
|
||||||
|
kind: "file";
|
||||||
|
size: number;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
kind: "directory";
|
||||||
|
size: null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type WasmModuleOptions = {
|
export type WasmModuleOptions = {
|
||||||
locateFile?: (file: string) => string;
|
locateFile?: (file: string) => string;
|
||||||
print?: (text: string) => void;
|
print?: (text: string) => void;
|
||||||
printErr?: (text: string) => void;
|
printErr?: (text: string) => void;
|
||||||
|
opfs?: false | {
|
||||||
|
required?: boolean;
|
||||||
|
mountPoint?: string;
|
||||||
|
workspacePath?: string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export function createWasmSimulator(moduleOptions?: WasmModuleOptions): Promise<WasmSimulator>;
|
export function createWasmSimulator(moduleOptions?: WasmModuleOptions): Promise<WasmSimulator>;
|
||||||
|
|||||||
@@ -41,6 +41,486 @@ const EVENT_OFFSETS = {
|
|||||||
reserved: 268,
|
reserved: 268,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DEFAULT_OPFS_MOUNT_POINT = "/cnc";
|
||||||
|
const LINUXCNC_DEFAULT_PARAMETER_FILE = "rs274ngc.var";
|
||||||
|
const LINUXCNC_PARAMETER_SCRATCH_FILES = [
|
||||||
|
LINUXCNC_DEFAULT_PARAMETER_FILE,
|
||||||
|
`${LINUXCNC_DEFAULT_PARAMETER_FILE}.new`,
|
||||||
|
`${LINUXCNC_DEFAULT_PARAMETER_FILE}.bak`,
|
||||||
|
];
|
||||||
|
|
||||||
|
function isOpfsAvailable() {
|
||||||
|
return Boolean(globalThis.navigator?.storage?.getDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertRelativePath(path) {
|
||||||
|
if (!path || typeof path !== "string") {
|
||||||
|
throw new Error("OPFS path must be a non-empty relative path");
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
path.startsWith("/") ||
|
||||||
|
path.includes("\\") ||
|
||||||
|
path.includes("\0") ||
|
||||||
|
path.split("/").some((part) => part === "" || part === "." || part === "..")
|
||||||
|
) {
|
||||||
|
throw new Error(`OPFS path must stay inside the CNC workspace: ${path}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertWorkspacePath(path) {
|
||||||
|
assertRelativePath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertMountPoint(path) {
|
||||||
|
if (!path || typeof path !== "string") {
|
||||||
|
throw new Error("OPFS mount point must be a non-empty absolute path");
|
||||||
|
}
|
||||||
|
const parts = path.split("/").slice(1);
|
||||||
|
if (
|
||||||
|
!path.startsWith("/") ||
|
||||||
|
path.includes("\\") ||
|
||||||
|
path.includes("\0") ||
|
||||||
|
parts.some((part) => part === "" || part === "." || part === "..")
|
||||||
|
) {
|
||||||
|
throw new Error(`OPFS mount point must stay inside the Emscripten filesystem: ${path}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getOpfsDirectoryHandle(path, create) {
|
||||||
|
if (!isOpfsAvailable()) {
|
||||||
|
throw new Error("OPFS is not available in this browser context");
|
||||||
|
}
|
||||||
|
let directory = await globalThis.navigator.storage.getDirectory();
|
||||||
|
for (const part of path.split("/").filter(Boolean)) {
|
||||||
|
directory = await directory.getDirectoryHandle(part, { create });
|
||||||
|
}
|
||||||
|
return directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readOpfsFile(workspacePath, relativePath) {
|
||||||
|
assertRelativePath(relativePath);
|
||||||
|
const parts = relativePath.split("/");
|
||||||
|
const fileName = parts.pop();
|
||||||
|
const directory = await getOpfsDirectoryHandle([workspacePath, ...parts].filter(Boolean).join("/"), false);
|
||||||
|
const file = await directory.getFileHandle(fileName, { create: false });
|
||||||
|
return new Uint8Array(await (await file.getFile()).arrayBuffer());
|
||||||
|
}
|
||||||
|
|
||||||
|
async function writeOpfsFile(workspacePath, relativePath, data) {
|
||||||
|
assertRelativePath(relativePath);
|
||||||
|
const parts = relativePath.split("/");
|
||||||
|
const fileName = parts.pop();
|
||||||
|
const directory = await getOpfsDirectoryHandle([workspacePath, ...parts].filter(Boolean).join("/"), true);
|
||||||
|
const file = await directory.getFileHandle(fileName, { create: true });
|
||||||
|
const writable = await file.createWritable();
|
||||||
|
try {
|
||||||
|
await writable.truncate(0);
|
||||||
|
await writable.write(data);
|
||||||
|
await writable.close();
|
||||||
|
} catch (error) {
|
||||||
|
try {
|
||||||
|
await writable.abort();
|
||||||
|
} catch (_abortError) {
|
||||||
|
// Preserve the original OPFS write failure.
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeOpfsEntry(workspacePath, relativePath, recursive) {
|
||||||
|
assertRelativePath(relativePath);
|
||||||
|
const parts = relativePath.split("/");
|
||||||
|
const entryName = parts.pop();
|
||||||
|
try {
|
||||||
|
const directory = await getOpfsDirectoryHandle([workspacePath, ...parts].filter(Boolean).join("/"), false);
|
||||||
|
await directory.removeEntry(entryName, { recursive });
|
||||||
|
} catch (error) {
|
||||||
|
if (error?.name !== "NotFoundError") {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function opfsEntryExists(workspacePath, relativePath) {
|
||||||
|
assertRelativePath(relativePath);
|
||||||
|
const parts = relativePath.split("/");
|
||||||
|
const entryName = parts.pop();
|
||||||
|
try {
|
||||||
|
const directory = await getOpfsDirectoryHandle([workspacePath, ...parts].filter(Boolean).join("/"), false);
|
||||||
|
try {
|
||||||
|
await directory.getFileHandle(entryName, { create: false });
|
||||||
|
return true;
|
||||||
|
} catch (fileError) {
|
||||||
|
if (fileError?.name !== "NotFoundError" && fileError?.name !== "TypeMismatchError") {
|
||||||
|
throw fileError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await directory.getDirectoryHandle(entryName, { create: false });
|
||||||
|
return true;
|
||||||
|
} catch (directoryError) {
|
||||||
|
if (directoryError?.name !== "NotFoundError" && directoryError?.name !== "TypeMismatchError") {
|
||||||
|
throw directoryError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch (error) {
|
||||||
|
if (error?.name === "NotFoundError") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function statOpfsEntry(workspacePath, relativePath) {
|
||||||
|
assertRelativePath(relativePath);
|
||||||
|
const parts = relativePath.split("/");
|
||||||
|
const entryName = parts.pop();
|
||||||
|
const directory = await getOpfsDirectoryHandle([workspacePath, ...parts].filter(Boolean).join("/"), false);
|
||||||
|
try {
|
||||||
|
const file = await directory.getFileHandle(entryName, { create: false });
|
||||||
|
return { kind: "file", size: (await file.getFile()).size };
|
||||||
|
} catch (fileError) {
|
||||||
|
if (fileError?.name !== "TypeMismatchError") {
|
||||||
|
throw fileError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await directory.getDirectoryHandle(entryName, { create: false });
|
||||||
|
return { kind: "directory", size: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function clearOpfsWorkspace(workspacePath) {
|
||||||
|
const parts = workspacePath.split("/").filter(Boolean);
|
||||||
|
if (parts.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const entryName = parts.pop();
|
||||||
|
const directory = await getOpfsDirectoryHandle(parts.join("/"), true);
|
||||||
|
try {
|
||||||
|
await directory.removeEntry(entryName, { recursive: true });
|
||||||
|
} catch (error) {
|
||||||
|
if (error?.name !== "NotFoundError") {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await getOpfsDirectoryHandle(workspacePath, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function replaceOpfsDirectory(workspacePath, relativePath) {
|
||||||
|
assertRelativePath(relativePath);
|
||||||
|
await removeOpfsEntry(workspacePath, relativePath, true);
|
||||||
|
const directory = await getOpfsDirectoryHandle([workspacePath, relativePath].filter(Boolean).join("/"), true);
|
||||||
|
for await (const [name] of directory.entries()) {
|
||||||
|
await directory.removeEntry(name, { recursive: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readOpfsDirectoryTree(workspacePath, relativePath, relativeRoot = "") {
|
||||||
|
assertRelativePath(relativePath);
|
||||||
|
const directory = await getOpfsDirectoryHandle([workspacePath, relativePath, relativeRoot].filter(Boolean).join("/"), false);
|
||||||
|
const files = [];
|
||||||
|
const directories = [];
|
||||||
|
for await (const [name, handle] of directory.entries()) {
|
||||||
|
const path = relativeRoot ? `${relativeRoot}/${name}` : name;
|
||||||
|
if (handle.kind === "directory") {
|
||||||
|
directories.push(path);
|
||||||
|
const child = await readOpfsDirectoryTree(workspacePath, relativePath, path);
|
||||||
|
directories.push(...child.directories);
|
||||||
|
files.push(...child.files);
|
||||||
|
} else if (handle.kind === "file") {
|
||||||
|
files.push(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
directories: directories.sort(),
|
||||||
|
files: files.sort(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureWasmDirectory(module, path) {
|
||||||
|
const parts = path.split("/").filter(Boolean);
|
||||||
|
let current = "";
|
||||||
|
for (const part of parts) {
|
||||||
|
current += `/${part}`;
|
||||||
|
try {
|
||||||
|
module.FS.mkdir(current);
|
||||||
|
} catch (error) {
|
||||||
|
if (error?.errno !== 20) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function readWasmDirectoryTree(module, rootPath, relativeRoot = "") {
|
||||||
|
const files = [];
|
||||||
|
const directories = [];
|
||||||
|
for (const entry of module.FS.readdir(rootPath)) {
|
||||||
|
if (entry === "." || entry === "..") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const path = `${rootPath}/${entry}`;
|
||||||
|
const relativePath = relativeRoot ? `${relativeRoot}/${entry}` : entry;
|
||||||
|
const mode = module.FS.stat(path).mode;
|
||||||
|
if (module.FS.isDir(mode)) {
|
||||||
|
directories.push(relativePath);
|
||||||
|
const child = readWasmDirectoryTree(module, path, relativePath);
|
||||||
|
directories.push(...child.directories);
|
||||||
|
files.push(...child.files);
|
||||||
|
} else if (module.FS.isFile(mode)) {
|
||||||
|
files.push(relativePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
directories: directories.sort(),
|
||||||
|
files: files.sort(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeWasmPath(module, path, recursive) {
|
||||||
|
let mode;
|
||||||
|
try {
|
||||||
|
mode = module.FS.stat(path).mode;
|
||||||
|
} catch (error) {
|
||||||
|
if (error?.errno !== 44) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (module.FS.isDir(mode)) {
|
||||||
|
if (!recursive) {
|
||||||
|
module.FS.rmdir(path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const entry of module.FS.readdir(path)) {
|
||||||
|
if (entry !== "." && entry !== "..") {
|
||||||
|
removeWasmPath(module, `${path}/${entry}`, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module.FS.rmdir(path);
|
||||||
|
} else {
|
||||||
|
module.FS.unlink(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function wasmPathExists(module, path) {
|
||||||
|
try {
|
||||||
|
module.FS.stat(path);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
if (error?.errno !== 44) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function readFirstExistingWasmFile(module, paths) {
|
||||||
|
for (const path of paths) {
|
||||||
|
try {
|
||||||
|
return module.FS.readFile(path);
|
||||||
|
} catch (error) {
|
||||||
|
if (error?.errno !== 44) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeWasmFileReplacingPath(module, path, data) {
|
||||||
|
removeWasmPath(module, path, true);
|
||||||
|
const parentIndex = path.lastIndexOf("/");
|
||||||
|
if (parentIndex > 0) {
|
||||||
|
ensureWasmDirectory(module, path.slice(0, parentIndex));
|
||||||
|
}
|
||||||
|
module.FS.writeFile(path, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanupLinuxCncParameterFiles(module) {
|
||||||
|
for (const path of LINUXCNC_PARAMETER_SCRATCH_FILES) {
|
||||||
|
removeWasmPath(module, path, true);
|
||||||
|
removeWasmPath(module, `/${path}`, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function installOpfsWorkspace(module, options) {
|
||||||
|
const opfsOptions = options.opfs ?? (isOpfsAvailable() ? {} : false);
|
||||||
|
if (opfsOptions === false) {
|
||||||
|
if (isOpfsAvailable()) {
|
||||||
|
throw new Error("OPFS cannot be disabled in browser contexts with OPFS support");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!module.FS) {
|
||||||
|
throw new Error("cnc_sim.js was not built with Emscripten FS export");
|
||||||
|
}
|
||||||
|
if (!isOpfsAvailable()) {
|
||||||
|
if (opfsOptions.required) {
|
||||||
|
throw new Error("OPFS is only available in browser contexts");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const mountPoint = opfsOptions.mountPoint ?? DEFAULT_OPFS_MOUNT_POINT;
|
||||||
|
const workspacePath = opfsOptions.workspacePath ?? "cnc-simulator";
|
||||||
|
assertMountPoint(mountPoint);
|
||||||
|
assertWorkspacePath(workspacePath);
|
||||||
|
ensureWasmDirectory(module, mountPoint);
|
||||||
|
return {
|
||||||
|
mountPoint,
|
||||||
|
workspacePath,
|
||||||
|
resolvePath(path) {
|
||||||
|
assertRelativePath(path);
|
||||||
|
return `${mountPoint}/${path}`;
|
||||||
|
},
|
||||||
|
async readFile(path) {
|
||||||
|
const data = await readOpfsFile(workspacePath, path);
|
||||||
|
const wasmPath = this.resolvePath(path);
|
||||||
|
writeWasmFileReplacingPath(module, wasmPath, data);
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
async readDirectory(path) {
|
||||||
|
assertRelativePath(path);
|
||||||
|
let tree;
|
||||||
|
try {
|
||||||
|
tree = await readOpfsDirectoryTree(workspacePath, path);
|
||||||
|
} catch (error) {
|
||||||
|
if (error?.name === "NotFoundError") {
|
||||||
|
removeWasmPath(module, this.resolvePath(path), true);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
const wasmPath = this.resolvePath(path);
|
||||||
|
removeWasmPath(module, wasmPath, true);
|
||||||
|
ensureWasmDirectory(module, wasmPath);
|
||||||
|
for (const directory of tree.directories) {
|
||||||
|
ensureWasmDirectory(module, `${wasmPath}/${directory}`);
|
||||||
|
}
|
||||||
|
for (const file of tree.files) {
|
||||||
|
await this.readFile(`${path}/${file}`);
|
||||||
|
}
|
||||||
|
return tree.files;
|
||||||
|
},
|
||||||
|
async persistFile(path) {
|
||||||
|
const wasmPath = this.resolvePath(path);
|
||||||
|
const data = module.FS.readFile(wasmPath);
|
||||||
|
await writeOpfsFile(workspacePath, path, data);
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
async persistDirectory(path) {
|
||||||
|
assertRelativePath(path);
|
||||||
|
const wasmPath = this.resolvePath(path);
|
||||||
|
const tree = readWasmDirectoryTree(module, wasmPath);
|
||||||
|
await replaceOpfsDirectory(workspacePath, path);
|
||||||
|
for (const directory of tree.directories) {
|
||||||
|
await getOpfsDirectoryHandle([workspacePath, path, directory].filter(Boolean).join("/"), true);
|
||||||
|
}
|
||||||
|
for (const file of tree.files) {
|
||||||
|
await this.persistFile(`${path}/${file}`);
|
||||||
|
}
|
||||||
|
return tree.files;
|
||||||
|
},
|
||||||
|
async writeFile(path, data) {
|
||||||
|
await writeOpfsFile(workspacePath, path, data);
|
||||||
|
const wasmPath = this.resolvePath(path);
|
||||||
|
writeWasmFileReplacingPath(module, wasmPath, data);
|
||||||
|
},
|
||||||
|
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters() links the
|
||||||
|
// current parameter file to filename + ".bak" before replacing it.
|
||||||
|
async copyFile(fromPath, toPath) {
|
||||||
|
assertRelativePath(fromPath);
|
||||||
|
assertRelativePath(toPath);
|
||||||
|
const data = await readOpfsFile(workspacePath, fromPath);
|
||||||
|
await writeOpfsFile(workspacePath, toPath, data);
|
||||||
|
|
||||||
|
const fromWasmPath = this.resolvePath(fromPath);
|
||||||
|
const toWasmPath = this.resolvePath(toPath);
|
||||||
|
writeWasmFileReplacingPath(module, fromWasmPath, data);
|
||||||
|
writeWasmFileReplacingPath(module, toWasmPath, data);
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters() writes a
|
||||||
|
// temporary filename + ".new" and renames it over the parameter file.
|
||||||
|
async moveFile(fromPath, toPath) {
|
||||||
|
assertRelativePath(fromPath);
|
||||||
|
assertRelativePath(toPath);
|
||||||
|
if (fromPath === toPath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data = await readOpfsFile(workspacePath, fromPath);
|
||||||
|
await writeOpfsFile(workspacePath, toPath, data);
|
||||||
|
await removeOpfsEntry(workspacePath, fromPath, false);
|
||||||
|
|
||||||
|
const fromWasmPath = this.resolvePath(fromPath);
|
||||||
|
const toWasmPath = this.resolvePath(toPath);
|
||||||
|
writeWasmFileReplacingPath(module, toWasmPath, data);
|
||||||
|
removeWasmPath(module, fromWasmPath, false);
|
||||||
|
},
|
||||||
|
async exists(path) {
|
||||||
|
return opfsEntryExists(workspacePath, path);
|
||||||
|
},
|
||||||
|
async stat(path) {
|
||||||
|
return statOpfsEntry(workspacePath, path);
|
||||||
|
},
|
||||||
|
// LinuxCNC source basis: rs274ngc_pre.cc restore_parameters() reads the
|
||||||
|
// configured parameter file, and save_parameters() writes filename + ".new"
|
||||||
|
// before replacing the main file and managing filename + ".bak".
|
||||||
|
async loadParameterFile(path) {
|
||||||
|
assertRelativePath(path);
|
||||||
|
try {
|
||||||
|
const data = await readOpfsFile(workspacePath, path);
|
||||||
|
writeWasmFileReplacingPath(module, LINUXCNC_DEFAULT_PARAMETER_FILE, data);
|
||||||
|
writeWasmFileReplacingPath(module, `/${LINUXCNC_DEFAULT_PARAMETER_FILE}`, data);
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
if (error?.name !== "NotFoundError") {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
cleanupLinuxCncParameterFiles(module);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async persistParameterFile(path) {
|
||||||
|
assertRelativePath(path);
|
||||||
|
const data = readFirstExistingWasmFile(module, [
|
||||||
|
LINUXCNC_DEFAULT_PARAMETER_FILE,
|
||||||
|
`/${LINUXCNC_DEFAULT_PARAMETER_FILE}`,
|
||||||
|
]);
|
||||||
|
if (data === null) {
|
||||||
|
throw new Error("LinuxCNC parameter file was not produced by the interpreter");
|
||||||
|
}
|
||||||
|
await writeOpfsFile(workspacePath, path, data);
|
||||||
|
await removeOpfsEntry(workspacePath, `${path}.new`, false);
|
||||||
|
removeWasmPath(module, `${LINUXCNC_DEFAULT_PARAMETER_FILE}.new`, false);
|
||||||
|
removeWasmPath(module, `/${LINUXCNC_DEFAULT_PARAMETER_FILE}.new`, false);
|
||||||
|
const backupPath = `${LINUXCNC_DEFAULT_PARAMETER_FILE}.bak`;
|
||||||
|
const backupData = readFirstExistingWasmFile(module, [backupPath, `/${backupPath}`]);
|
||||||
|
if (backupData !== null) {
|
||||||
|
await writeOpfsFile(workspacePath, `${path}.bak`, backupData);
|
||||||
|
} else {
|
||||||
|
await removeOpfsEntry(workspacePath, `${path}.bak`, false);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
async removeFile(path) {
|
||||||
|
await removeOpfsEntry(workspacePath, path, false);
|
||||||
|
removeWasmPath(module, this.resolvePath(path), false);
|
||||||
|
},
|
||||||
|
async removeDirectory(path) {
|
||||||
|
await removeOpfsEntry(workspacePath, path, true);
|
||||||
|
removeWasmPath(module, this.resolvePath(path), true);
|
||||||
|
},
|
||||||
|
async clear() {
|
||||||
|
await clearOpfsWorkspace(workspacePath);
|
||||||
|
for (const entry of module.FS.readdir(mountPoint)) {
|
||||||
|
if (entry !== "." && entry !== "..") {
|
||||||
|
removeWasmPath(module, `${mountPoint}/${entry}`, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function readPose(view, offset) {
|
function readPose(view, offset) {
|
||||||
return {
|
return {
|
||||||
x: view.getFloat64(offset + 0, true),
|
x: view.getFloat64(offset + 0, true),
|
||||||
@@ -82,12 +562,21 @@ async function loadModuleFactory() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function createWasmSimulator(moduleOptions = {}) {
|
export async function createWasmSimulator(moduleOptions = {}) {
|
||||||
|
if (moduleOptions.opfs === false && isOpfsAvailable()) {
|
||||||
|
throw new Error("OPFS cannot be disabled in browser contexts with OPFS support");
|
||||||
|
}
|
||||||
|
const hasOpfsOptions = Object.prototype.hasOwnProperty.call(moduleOptions, "opfs");
|
||||||
|
const emscriptenModuleOptions = hasOpfsOptions
|
||||||
|
? (({ opfs: _opfs, ...rest }) => rest)(moduleOptions)
|
||||||
|
: moduleOptions;
|
||||||
|
|
||||||
const createModule = await loadModuleFactory();
|
const createModule = await loadModuleFactory();
|
||||||
if (!createModule) {
|
if (!createModule) {
|
||||||
throw new Error("cnc_sim.js did not export createCncSimModule");
|
throw new Error("cnc_sim.js did not export createCncSimModule");
|
||||||
}
|
}
|
||||||
|
|
||||||
const module = await createModule(moduleOptions);
|
const module = hasOpfsOptions ? await createModule(emscriptenModuleOptions) : await createModule(moduleOptions);
|
||||||
|
const opfs = installOpfsWorkspace(module, moduleOptions);
|
||||||
const create = module.cwrap("cnc_sim_create", "number", []);
|
const create = module.cwrap("cnc_sim_create", "number", []);
|
||||||
const destroy = module.cwrap("cnc_sim_destroy", null, ["number"]);
|
const destroy = module.cwrap("cnc_sim_destroy", null, ["number"]);
|
||||||
const reset = module.cwrap("cnc_sim_reset", null, ["number"]);
|
const reset = module.cwrap("cnc_sim_reset", null, ["number"]);
|
||||||
@@ -100,9 +589,9 @@ export async function createWasmSimulator(moduleOptions = {}) {
|
|||||||
const handle = create();
|
const handle = create();
|
||||||
let callbackPtr = 0;
|
let callbackPtr = 0;
|
||||||
|
|
||||||
return {
|
const parseText = (program, dialect = "linuxcnc", options = {}, parseOptions = {}) => {
|
||||||
parse(program, dialect = "linuxcnc", options = {}) {
|
|
||||||
const events = [];
|
const events = [];
|
||||||
|
try {
|
||||||
reset(handle);
|
reset(handle);
|
||||||
setDialect(handle, DIALECT[dialect] ?? DIALECT.linuxcnc);
|
setDialect(handle, DIALECT[dialect] ?? DIALECT.linuxcnc);
|
||||||
|
|
||||||
@@ -117,8 +606,19 @@ export async function createWasmSimulator(moduleOptions = {}) {
|
|||||||
...(options.config ? { config: options.config } : {}),
|
...(options.config ? { config: options.config } : {}),
|
||||||
...(options.configPath ? { configPath: options.configPath } : {}),
|
...(options.configPath ? { configPath: options.configPath } : {}),
|
||||||
...(options.config_path ? { config_path: options.config_path } : {}),
|
...(options.config_path ? { config_path: options.config_path } : {}),
|
||||||
|
...(options.ini ? { ini: options.ini } : {}),
|
||||||
|
...(options.iniFile ? { iniFile: options.iniFile } : {}),
|
||||||
|
...(options.iniFileName ? { iniFileName: options.iniFileName } : {}),
|
||||||
|
...(options.ini_file ? { ini_file: options.ini_file } : {}),
|
||||||
|
...(options.ini_file_name ? { ini_file_name: options.ini_file_name } : {}),
|
||||||
|
...(options.INI_FILE_NAME ? { INI_FILE_NAME: options.INI_FILE_NAME } : {}),
|
||||||
|
...(options.halFile ? { halFile: options.halFile } : {}),
|
||||||
...(options.halfile ? { halfile: options.halfile } : {}),
|
...(options.halfile ? { halfile: options.halfile } : {}),
|
||||||
...(options.hal_file ? { hal_file: options.hal_file } : {}),
|
...(options.hal_file ? { hal_file: options.hal_file } : {}),
|
||||||
|
...(options.postguiHalFile ? { postguiHalFile: options.postguiHalFile } : {}),
|
||||||
|
...(options.postgui_halfile ? { postgui_halfile: options.postgui_halfile } : {}),
|
||||||
|
...(options.postgui_hal_file ? { postgui_hal_file: options.postgui_hal_file } : {}),
|
||||||
|
...(options.POSTGUI_HALFILE ? { POSTGUI_HALFILE: options.POSTGUI_HALFILE } : {}),
|
||||||
...(options.kinematics ? { kinematics: options.kinematics } : {}),
|
...(options.kinematics ? { kinematics: options.kinematics } : {}),
|
||||||
...(options.trt ? { trt: options.trt } : {}),
|
...(options.trt ? { trt: options.trt } : {}),
|
||||||
...(options.pivotLength !== undefined ? { pivotLength: options.pivotLength } : {}),
|
...(options.pivotLength !== undefined ? { pivotLength: options.pivotLength } : {}),
|
||||||
@@ -135,9 +635,13 @@ export async function createWasmSimulator(moduleOptions = {}) {
|
|||||||
});
|
});
|
||||||
const configBytes = module.lengthBytesUTF8(config) + 1;
|
const configBytes = module.lengthBytesUTF8(config) + 1;
|
||||||
const configPtr = module._malloc(configBytes);
|
const configPtr = module._malloc(configBytes);
|
||||||
|
let configRc;
|
||||||
|
try {
|
||||||
module.stringToUTF8(config, configPtr, configBytes);
|
module.stringToUTF8(config, configPtr, configBytes);
|
||||||
const configRc = loadConfig(handle, configPtr, configBytes - 1);
|
configRc = loadConfig(handle, configPtr, configBytes - 1);
|
||||||
|
} finally {
|
||||||
module._free(configPtr);
|
module._free(configPtr);
|
||||||
|
}
|
||||||
if (configRc !== 0) {
|
if (configRc !== 0) {
|
||||||
throw new Error(module.UTF8ToString(lastError(handle)));
|
throw new Error(module.UTF8ToString(lastError(handle)));
|
||||||
}
|
}
|
||||||
@@ -153,14 +657,59 @@ export async function createWasmSimulator(moduleOptions = {}) {
|
|||||||
|
|
||||||
const bytes = module.lengthBytesUTF8(program) + 1;
|
const bytes = module.lengthBytesUTF8(program) + 1;
|
||||||
const ptr = module._malloc(bytes);
|
const ptr = module._malloc(bytes);
|
||||||
|
let rc;
|
||||||
|
try {
|
||||||
module.stringToUTF8(program, ptr, bytes);
|
module.stringToUTF8(program, ptr, bytes);
|
||||||
const rc = parseProgram(handle, ptr, bytes - 1);
|
rc = parseProgram(handle, ptr, bytes - 1);
|
||||||
|
} finally {
|
||||||
module._free(ptr);
|
module._free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
if (rc !== 0) {
|
if (rc !== 0) {
|
||||||
throw new Error(module.UTF8ToString(lastError(handle)));
|
throw new Error(module.UTF8ToString(lastError(handle)));
|
||||||
}
|
}
|
||||||
return events;
|
return events;
|
||||||
|
} finally {
|
||||||
|
if (opfs && !parseOptions.keepLinuxCncParameterFiles) {
|
||||||
|
cleanupLinuxCncParameterFiles(module);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
parse(program, dialect = "linuxcnc", options = {}) {
|
||||||
|
return parseText(program, dialect, options);
|
||||||
|
},
|
||||||
|
|
||||||
|
async parseFile(path, dialect = "linuxcnc", options = {}) {
|
||||||
|
if (!opfs) {
|
||||||
|
throw new Error("OPFS workspace is not available for program file parsing");
|
||||||
|
}
|
||||||
|
const program = new TextDecoder().decode(await opfs.readFile(path));
|
||||||
|
return parseText(program, dialect, options);
|
||||||
|
},
|
||||||
|
|
||||||
|
async parseWithParameterFile(program, parameterPath, dialect = "linuxcnc", options = {}) {
|
||||||
|
if (!opfs) {
|
||||||
|
throw new Error("OPFS workspace is not available for parameter file persistence");
|
||||||
|
}
|
||||||
|
cleanupLinuxCncParameterFiles(module);
|
||||||
|
await opfs.loadParameterFile(parameterPath);
|
||||||
|
try {
|
||||||
|
const events = parseText(program, dialect, options, { keepLinuxCncParameterFiles: true });
|
||||||
|
await opfs.persistParameterFile(parameterPath);
|
||||||
|
return events;
|
||||||
|
} finally {
|
||||||
|
cleanupLinuxCncParameterFiles(module);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async parseFileWithParameterFile(path, parameterPath, dialect = "linuxcnc", options = {}) {
|
||||||
|
if (!opfs) {
|
||||||
|
throw new Error("OPFS workspace is not available for program and parameter file parsing");
|
||||||
|
}
|
||||||
|
const program = new TextDecoder().decode(await opfs.readFile(path));
|
||||||
|
return this.parseWithParameterFile(program, parameterPath, dialect, options);
|
||||||
},
|
},
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
@@ -170,5 +719,10 @@ export async function createWasmSimulator(moduleOptions = {}) {
|
|||||||
}
|
}
|
||||||
destroy(handle);
|
destroy(handle);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
fs: {
|
||||||
|
opfs,
|
||||||
|
module,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
66
web/test-browser-wasm-smoke-app-sections.js
Normal file
66
web/test-browser-wasm-smoke-app-sections.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
export async function runBrowserAppOpfsSections(context) {
|
||||||
|
const {
|
||||||
|
browserHasOpfs,
|
||||||
|
createHiddenAppFrame,
|
||||||
|
disposeAppFrame,
|
||||||
|
expectIncludes,
|
||||||
|
expectText,
|
||||||
|
getAppFrameSimulator,
|
||||||
|
parseAppFrameProgram,
|
||||||
|
runSection,
|
||||||
|
waitFor,
|
||||||
|
waitForAppAlarm,
|
||||||
|
waitForAppControls,
|
||||||
|
} = context;
|
||||||
|
if (!browserHasOpfs()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await runSection("browser app opfs save", async () => {
|
||||||
|
const appFrame = await createHiddenAppFrame();
|
||||||
|
try {
|
||||||
|
await waitForAppControls(appFrame, "app frame did not load CNC controls");
|
||||||
|
parseAppFrameProgram(appFrame, "G21 G90\nG0 X1\nG28.1\nM30\n");
|
||||||
|
await waitForAppAlarm(appFrame, "OK ", "app frame did not parse program");
|
||||||
|
const appSimulator = await getAppFrameSimulator(appFrame, "app frame did not create OPFS simulator workspace");
|
||||||
|
const savedProgram = new TextDecoder().decode(await appSimulator.fs.opfs.readFile("programs/current.ngc"));
|
||||||
|
expectText(savedProgram, "G21 G90\nG0 X1\nG28.1\nM30\n",
|
||||||
|
"app frame did not persist current program into OPFS");
|
||||||
|
const savedParameterFile = new TextDecoder().decode(await appSimulator.fs.opfs.readFile("parameters/rs274ngc.var"));
|
||||||
|
expectIncludes(savedParameterFile, "5161\t1.000000",
|
||||||
|
"app frame did not persist LinuxCNC parameter state into OPFS");
|
||||||
|
} finally {
|
||||||
|
await disposeAppFrame(appFrame);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await runSection("browser app opfs restore", async () => {
|
||||||
|
const restoredAppFrame = await createHiddenAppFrame();
|
||||||
|
try {
|
||||||
|
await waitForAppControls(restoredAppFrame, "restored app frame did not load CNC controls");
|
||||||
|
await waitFor(() => {
|
||||||
|
const restoredInput = restoredAppFrame.contentDocument?.querySelector("#programInput");
|
||||||
|
return restoredInput?.value === "G21 G90\nG0 X1\nG28.1\nM30\n";
|
||||||
|
}, "app frame did not restore current program from OPFS after reload");
|
||||||
|
await waitForAppAlarm(
|
||||||
|
restoredAppFrame,
|
||||||
|
"OPFS PROGRAM RESTORED",
|
||||||
|
"app frame did not report OPFS program restore after reload",
|
||||||
|
);
|
||||||
|
await getAppFrameSimulator(restoredAppFrame, "restored app frame did not create OPFS simulator workspace");
|
||||||
|
parseAppFrameProgram(
|
||||||
|
restoredAppFrame,
|
||||||
|
"G21 G90\nF100\nO10 if [#5161 EQ 1]\nG1 X12\nO10 endif\nM30\n",
|
||||||
|
);
|
||||||
|
await waitForAppAlarm(
|
||||||
|
restoredAppFrame,
|
||||||
|
"OK ",
|
||||||
|
"restored app frame did not parse program with reloaded parameter state",
|
||||||
|
);
|
||||||
|
await waitFor(
|
||||||
|
() => restoredAppFrame.contentDocument?.querySelector("#axisX")?.value === "12.000",
|
||||||
|
"app frame did not reload LinuxCNC parameter state from OPFS after reload",
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
await disposeAppFrame(restoredAppFrame);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
176
web/test-browser-wasm-smoke-helpers.js
Normal file
176
web/test-browser-wasm-smoke-helpers.js
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
const textDecoder = new TextDecoder();
|
||||||
|
const textEncoder = new TextEncoder();
|
||||||
|
const linuxCncParameterScratchFiles = ["rs274ngc.var", "rs274ngc.var.new", "rs274ngc.var.bak"];
|
||||||
|
|
||||||
|
export function createBrowserSmokeHarness(result) {
|
||||||
|
const sectionResults = [];
|
||||||
|
const near = (actual, expected) => Math.abs(actual - expected) < 1e-6;
|
||||||
|
const waitFor = async (predicate, message) => {
|
||||||
|
for (let i = 0; i < 50; i += 1) {
|
||||||
|
if (predicate()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||||
|
}
|
||||||
|
throw new Error(message);
|
||||||
|
};
|
||||||
|
const runSection = async (name, action) => {
|
||||||
|
const sectionNumber = sectionResults.length + 1;
|
||||||
|
result.textContent = `running: ${sectionNumber}: ${name}`;
|
||||||
|
try {
|
||||||
|
await action();
|
||||||
|
sectionResults.push(name);
|
||||||
|
} catch (error) {
|
||||||
|
const detail = error && error.stack ? error.stack : error;
|
||||||
|
throw new Error(`section "${name}" failed: ${detail}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const completedMessage = () => `browser wasm smoke passed (${sectionResults.length} sections)`;
|
||||||
|
const browserHasOpfs = () => navigator.storage && navigator.storage.getDirectory;
|
||||||
|
const expectEvent = (events, predicate, message) => {
|
||||||
|
if (!events.some(predicate)) {
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const expectNoEvent = (events, predicate, message) => {
|
||||||
|
if (events.some(predicate)) {
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const expectText = (actual, expected, message) => {
|
||||||
|
if (actual !== expected) {
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const expectIncludes = (actual, expected, message) => {
|
||||||
|
if (!actual.includes(expected)) {
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const expectFileList = (actual, expected, message) => {
|
||||||
|
if (
|
||||||
|
actual.length !== expected.length ||
|
||||||
|
expected.some((entry) => !actual.includes(entry))
|
||||||
|
) {
|
||||||
|
throw new Error(`${message}: ${actual.join(",")}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const expectErrorContaining = async (action, expectedText, message) => {
|
||||||
|
try {
|
||||||
|
await action();
|
||||||
|
throw new Error(message);
|
||||||
|
} catch (error) {
|
||||||
|
if (!String(error?.message ?? error).includes(expectedText)) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const expectMissingOpfsFile = async (action, message) => {
|
||||||
|
try {
|
||||||
|
await action();
|
||||||
|
throw new Error(message);
|
||||||
|
} catch (error) {
|
||||||
|
if (error?.name !== "NotFoundError") {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const expectMissingWasmPath = (action, message) => {
|
||||||
|
try {
|
||||||
|
action();
|
||||||
|
throw new Error(message);
|
||||||
|
} catch (error) {
|
||||||
|
if (error?.errno !== 44) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const expectWasmFilesMissing = (fs, paths, messageForPath) => {
|
||||||
|
for (const wasmPath of paths) {
|
||||||
|
expectMissingWasmPath(
|
||||||
|
() => fs.readFile(wasmPath),
|
||||||
|
messageForPath(wasmPath),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const decodeBytes = (bytes) => textDecoder.decode(bytes);
|
||||||
|
const encodeText = (text) => textEncoder.encode(text);
|
||||||
|
const readOpfsText = async (simulator, path) => decodeBytes(await simulator.fs.opfs.readFile(path));
|
||||||
|
const readWasmText = (simulator, path) => decodeBytes(simulator.fs.module.FS.readFile(path));
|
||||||
|
const writeWasmText = (simulator, path, text) => simulator.fs.module.FS.writeFile(path, encodeText(text));
|
||||||
|
const removeWasmPathIfExists = (simulator, path) => {
|
||||||
|
try {
|
||||||
|
simulator.fs.module.FS.unlink(path);
|
||||||
|
} catch (error) {
|
||||||
|
if (error?.errno !== 44) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const expectOpfsText = async (simulator, path, expected, message) => {
|
||||||
|
expectText(await readOpfsText(simulator, path), expected, message);
|
||||||
|
};
|
||||||
|
const expectWasmText = (simulator, path, expected, message) => {
|
||||||
|
expectText(readWasmText(simulator, path), expected, message);
|
||||||
|
};
|
||||||
|
const expectOpfsExists = async (simulator, path, message) => {
|
||||||
|
if (!(await simulator.fs.opfs.exists(path))) {
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const expectOpfsMissing = async (simulator, path, message) => {
|
||||||
|
if (await simulator.fs.opfs.exists(path)) {
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const expectOpfsFileStat = async (simulator, path, expectedSize, message) => {
|
||||||
|
const stat = await simulator.fs.opfs.stat(path);
|
||||||
|
if (stat.kind !== "file" || stat.size !== expectedSize) {
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const expectOpfsDirectoryStat = async (simulator, path, message) => {
|
||||||
|
const stat = await simulator.fs.opfs.stat(path);
|
||||||
|
if (stat.kind !== "directory" || stat.size !== null) {
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const expectLinuxCncParameterScratchMissing = (simulator, messageForPath) => {
|
||||||
|
for (const parameterPath of linuxCncParameterScratchFiles) {
|
||||||
|
expectMissingWasmPath(
|
||||||
|
() => simulator.fs.module.FS.stat(parameterPath),
|
||||||
|
messageForPath(parameterPath),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
browserHasOpfs,
|
||||||
|
completedMessage,
|
||||||
|
decodeBytes,
|
||||||
|
encodeText,
|
||||||
|
expectErrorContaining,
|
||||||
|
expectEvent,
|
||||||
|
expectFileList,
|
||||||
|
expectIncludes,
|
||||||
|
expectLinuxCncParameterScratchMissing,
|
||||||
|
expectMissingOpfsFile,
|
||||||
|
expectMissingWasmPath,
|
||||||
|
expectNoEvent,
|
||||||
|
expectOpfsDirectoryStat,
|
||||||
|
expectOpfsExists,
|
||||||
|
expectOpfsFileStat,
|
||||||
|
expectOpfsMissing,
|
||||||
|
expectOpfsText,
|
||||||
|
expectText,
|
||||||
|
expectWasmFilesMissing,
|
||||||
|
expectWasmText,
|
||||||
|
near,
|
||||||
|
readOpfsText,
|
||||||
|
readWasmText,
|
||||||
|
removeWasmPathIfExists,
|
||||||
|
runSection,
|
||||||
|
waitFor,
|
||||||
|
writeWasmText,
|
||||||
|
};
|
||||||
|
}
|
||||||
72
web/test-browser-wasm-smoke-linuxcnc-sections.js
Normal file
72
web/test-browser-wasm-smoke-linuxcnc-sections.js
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
export async function runLinuxCncBrowserSections(context, simulator) {
|
||||||
|
const {
|
||||||
|
expectEvent,
|
||||||
|
expectMissingWasmPath,
|
||||||
|
near,
|
||||||
|
runSection,
|
||||||
|
} = context;
|
||||||
|
await runSection("linuxcnc browser basic parse", async () => {
|
||||||
|
const events = simulator.parse("G21 G90\nG0 X0\nG1 X5 F100\nM30\n", "linuxcnc", {
|
||||||
|
backend: "linuxcnc-rs274",
|
||||||
|
});
|
||||||
|
expectEvent(events, (event) => event.type === "linear-feed" && near(event.end.x, 5),
|
||||||
|
"missing expected LinuxCNC WASM browser linear-feed event");
|
||||||
|
expectEvent(events, (event) => event.type === "program-end",
|
||||||
|
"missing expected LinuxCNC WASM browser program-end event");
|
||||||
|
});
|
||||||
|
let configCases = [];
|
||||||
|
await runSection("linuxcnc generated switchkins cases load", async () => {
|
||||||
|
const configCaseResponse = await fetch("/public/linuxcnc_switchkins_remap_config_cases.json");
|
||||||
|
if (!configCaseResponse.ok) {
|
||||||
|
throw new Error(`failed to load LinuxCNC switchkins config cases: ${configCaseResponse.status}`);
|
||||||
|
}
|
||||||
|
configCases = await configCaseResponse.json();
|
||||||
|
if (!Array.isArray(configCases) || configCases.length === 0) {
|
||||||
|
throw new Error("missing LinuxCNC switchkins config cases");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (const configCase of configCases) {
|
||||||
|
await runSection(`linuxcnc generated switchkins ${configCase.field}=${configCase.value}`, async () => {
|
||||||
|
const options = {
|
||||||
|
backend: "linuxcnc-rs274",
|
||||||
|
[configCase.field]: configCase.value,
|
||||||
|
};
|
||||||
|
const program = configCase.m430 >= 0 ? "M428\nM429\nM430\n" : "M428\nM429\n";
|
||||||
|
const configEvents = simulator.parse(program, "linuxcnc", options);
|
||||||
|
for (const [line, kinstype] of [
|
||||||
|
[1, configCase.m428],
|
||||||
|
[2, configCase.m429],
|
||||||
|
...(configCase.m430 >= 0 ? [[3, configCase.m430]] : []),
|
||||||
|
]) {
|
||||||
|
expectEvent(configEvents,
|
||||||
|
(event) => event.type === "kinematics-switch" && event.line === line && event.reserved === kinstype,
|
||||||
|
`missing LinuxCNC WASM browser generated ${configCase.field}=${configCase.value} line ${line} switchkins type ${kinstype}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await runSection("linuxcnc browser explicit scara switchkins", async () => {
|
||||||
|
const switchEvents = simulator.parse("M428\nM429\nM430\n", "linuxcnc", {
|
||||||
|
backend: "linuxcnc-rs274",
|
||||||
|
switchkins: "scara",
|
||||||
|
});
|
||||||
|
for (const [line, kinstype] of [
|
||||||
|
[1, 0],
|
||||||
|
[2, 1],
|
||||||
|
[3, 2],
|
||||||
|
]) {
|
||||||
|
expectEvent(switchEvents,
|
||||||
|
(event) => event.type === "kinematics-switch" && event.line === line && event.reserved === kinstype,
|
||||||
|
`missing LinuxCNC WASM browser M${427 + line} switchkins type ${kinstype}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await runSection("linuxcnc browser parse parameter scratch cleanup", async () => {
|
||||||
|
for (const parameterPath of ["rs274ngc.var", "rs274ngc.var.new", "rs274ngc.var.bak"]) {
|
||||||
|
expectMissingWasmPath(
|
||||||
|
() => simulator.fs.module.FS.stat(parameterPath),
|
||||||
|
`browser parse left LinuxCNC parameter state outside OPFS: ${parameterPath}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
114
web/test-browser-wasm-smoke-opfs-basic-sections.js
Normal file
114
web/test-browser-wasm-smoke-opfs-basic-sections.js
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
export async function runBrowserOpfsBasicSections(context, simulator) {
|
||||||
|
const {
|
||||||
|
expectErrorContaining,
|
||||||
|
expectEvent,
|
||||||
|
expectMissingWasmPath,
|
||||||
|
expectText,
|
||||||
|
near,
|
||||||
|
runSection,
|
||||||
|
withSmokeSimulator,
|
||||||
|
} = context;
|
||||||
|
|
||||||
|
await runSection("opfs workspace clear and healthcheck", async () => {
|
||||||
|
if (!simulator.fs.opfs) {
|
||||||
|
throw new Error("expected OPFS workspace to be available in browser smoke");
|
||||||
|
}
|
||||||
|
await simulator.fs.opfs.clear();
|
||||||
|
if (simulator.fs.opfs.workspacePath !== "cnc-simulator-smoke") {
|
||||||
|
throw new Error("unexpected OPFS workspace path");
|
||||||
|
}
|
||||||
|
await simulator.fs.opfs.writeFile("healthcheck.ngc", "G21 G90\n");
|
||||||
|
if (!(await simulator.fs.opfs.exists("healthcheck.ngc"))) {
|
||||||
|
throw new Error("OPFS exists did not find a persisted file");
|
||||||
|
}
|
||||||
|
if (await simulator.fs.opfs.exists("missing-healthcheck.ngc")) {
|
||||||
|
throw new Error("OPFS exists reported a missing file");
|
||||||
|
}
|
||||||
|
const healthcheckStat = await simulator.fs.opfs.stat("healthcheck.ngc");
|
||||||
|
if (healthcheckStat.kind !== "file" || healthcheckStat.size !== 8) {
|
||||||
|
throw new Error("OPFS stat did not report the persisted healthcheck file size");
|
||||||
|
}
|
||||||
|
const clearCheck = new TextDecoder().decode(await simulator.fs.opfs.readFile("healthcheck.ngc"));
|
||||||
|
expectText(clearCheck, "G21 G90\n", "OPFS workspace did not survive clear/write round trip");
|
||||||
|
});
|
||||||
|
|
||||||
|
await runSection("opfs default mount check", async () => {
|
||||||
|
await withSmokeSimulator((defaultMountSimulator) => {
|
||||||
|
if (!defaultMountSimulator.fs.opfs || defaultMountSimulator.fs.opfs.mountPoint !== "/cnc") {
|
||||||
|
throw new Error("default OPFS mount point was not preserved");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await runSection("opfs program write and read", async () => {
|
||||||
|
const encoded = new TextEncoder().encode("G21 G90\nG0 X1\n");
|
||||||
|
await simulator.fs.opfs.writeFile("programs/browser-smoke.ngc", encoded);
|
||||||
|
const decoded = new TextDecoder().decode(
|
||||||
|
await simulator.fs.opfs.readFile("programs/browser-smoke.ngc"),
|
||||||
|
);
|
||||||
|
expectText(decoded, "G21 G90\nG0 X1\n", "OPFS round trip did not preserve browser smoke program");
|
||||||
|
});
|
||||||
|
|
||||||
|
await runSection("opfs unsafe path rejection", async () => {
|
||||||
|
for (const unsafePath of ["../escape.ngc", "programs/../escape.ngc", "/escape.ngc", "programs//escape.ngc"]) {
|
||||||
|
await expectErrorContaining(
|
||||||
|
() => simulator.fs.opfs.writeFile(unsafePath, "G21\n"),
|
||||||
|
"inside the CNC workspace",
|
||||||
|
`OPFS accepted unsafe path ${unsafePath}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (const [operation, action] of [
|
||||||
|
["readFile", () => simulator.fs.opfs.readFile("../escape.ngc")],
|
||||||
|
["readDirectory", () => simulator.fs.opfs.readDirectory("../escape")],
|
||||||
|
["persistFile", () => simulator.fs.opfs.persistFile("../escape.ngc")],
|
||||||
|
["persistDirectory", () => simulator.fs.opfs.persistDirectory("../escape")],
|
||||||
|
["copyFile", () => simulator.fs.opfs.copyFile("../escape.ngc", "programs/escape.ngc")],
|
||||||
|
["copyFileTarget", () => simulator.fs.opfs.copyFile("healthcheck.ngc", "../escape.ngc")],
|
||||||
|
["exists", () => simulator.fs.opfs.exists("../escape.ngc")],
|
||||||
|
["stat", () => simulator.fs.opfs.stat("../escape.ngc")],
|
||||||
|
["moveFile", () => simulator.fs.opfs.moveFile("../escape.ngc", "programs/escape.ngc")],
|
||||||
|
["moveFileTarget", () => simulator.fs.opfs.moveFile("healthcheck.ngc", "../escape.ngc")],
|
||||||
|
["removeFile", () => simulator.fs.opfs.removeFile("../escape.ngc")],
|
||||||
|
["removeDirectory", () => simulator.fs.opfs.removeDirectory("../escape")],
|
||||||
|
["parseFile", () => simulator.parseFile("../escape.ngc", "linuxcnc", { backend: "linuxcnc-rs274" })],
|
||||||
|
[
|
||||||
|
"parseWithParameterFile",
|
||||||
|
() =>
|
||||||
|
simulator.parseWithParameterFile("G21 G90\nM30\n", "../escape.var", "linuxcnc", {
|
||||||
|
backend: "linuxcnc-rs274",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
]) {
|
||||||
|
await expectErrorContaining(
|
||||||
|
action,
|
||||||
|
"inside the CNC workspace",
|
||||||
|
`OPFS ${operation} accepted an unsafe path`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await runSection("opfs program parse and restore", async () => {
|
||||||
|
const opfsProgramEvents = await simulator.parseFile("programs/browser-smoke.ngc", "linuxcnc", {
|
||||||
|
backend: "linuxcnc-rs274",
|
||||||
|
});
|
||||||
|
expectEvent(opfsProgramEvents, (event) => event.type === "rapid" && near(event.end.x, 1),
|
||||||
|
"OPFS program file was not parsed through LinuxCNC WASM");
|
||||||
|
for (const parameterPath of ["rs274ngc.var", "rs274ngc.var.new", "rs274ngc.var.bak"]) {
|
||||||
|
expectMissingWasmPath(
|
||||||
|
() => simulator.fs.module.FS.stat(parameterPath),
|
||||||
|
`browser parseFile left LinuxCNC parameter state outside OPFS: ${parameterPath}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await withSmokeSimulator(async (restoredProgramSimulator) => {
|
||||||
|
// LinuxCNC source basis: rs274ngc_pre.cc read_text() reads
|
||||||
|
// blocks from the program stream after the OPFS mirror is loaded.
|
||||||
|
const restoredProgramEvents = await restoredProgramSimulator.parseFile(
|
||||||
|
"programs/browser-smoke.ngc",
|
||||||
|
"linuxcnc",
|
||||||
|
{ backend: "linuxcnc-rs274" },
|
||||||
|
);
|
||||||
|
expectEvent(restoredProgramEvents, (event) => event.type === "rapid" && near(event.end.x, 1),
|
||||||
|
"LinuxCNC OPFS program file was not restored across WASM simulator instances");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
174
web/test-browser-wasm-smoke-opfs-directory-sections.js
Normal file
174
web/test-browser-wasm-smoke-opfs-directory-sections.js
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
export async function runBrowserOpfsDirectorySections(context, simulator) {
|
||||||
|
const {
|
||||||
|
expectFileList,
|
||||||
|
expectMissingOpfsFile,
|
||||||
|
expectMissingWasmPath,
|
||||||
|
expectText,
|
||||||
|
runSection,
|
||||||
|
withSmokeSimulator,
|
||||||
|
} = context;
|
||||||
|
|
||||||
|
await runSection("opfs directory persist and load", async () => {
|
||||||
|
const fixturePath = simulator.fs.opfs.resolvePath("fixtures");
|
||||||
|
simulator.fs.module.FS.mkdirTree(`${fixturePath}/nested`);
|
||||||
|
simulator.fs.module.FS.mkdirTree(`${fixturePath}/empty/nested`);
|
||||||
|
simulator.fs.module.FS.writeFile(`${fixturePath}/nested/fixture.ngc`, "G21 G90\nG0 X3\n");
|
||||||
|
simulator.fs.module.FS.writeFile(`${fixturePath}/root.ngc`, "G21 G90\nG0 X4\n");
|
||||||
|
const persistedFiles = await simulator.fs.opfs.persistDirectory("fixtures");
|
||||||
|
const fixtureStat = await simulator.fs.opfs.stat("fixtures");
|
||||||
|
if (fixtureStat.kind !== "directory" || fixtureStat.size !== null) {
|
||||||
|
throw new Error("OPFS stat did not report a persisted directory");
|
||||||
|
}
|
||||||
|
expectFileList(persistedFiles, ["nested/fixture.ngc", "root.ngc"],
|
||||||
|
"unexpected OPFS directory persist file list");
|
||||||
|
const fixtureDecoded = new TextDecoder().decode(
|
||||||
|
await simulator.fs.opfs.readFile("fixtures/nested/fixture.ngc"),
|
||||||
|
);
|
||||||
|
expectText(fixtureDecoded, "G21 G90\nG0 X3\n",
|
||||||
|
"persisted WASM filesystem directory file was not readable from OPFS");
|
||||||
|
const emptyFixtureStat = await simulator.fs.opfs.stat("fixtures/empty/nested");
|
||||||
|
if (emptyFixtureStat.kind !== "directory" || emptyFixtureStat.size !== null) {
|
||||||
|
throw new Error("OPFS persistDirectory did not preserve an empty directory");
|
||||||
|
}
|
||||||
|
await withSmokeSimulator(async (staleDirectorySimulator) => {
|
||||||
|
await staleDirectorySimulator.fs.opfs.writeFile("fixtures/stale.ngc", "G21 G90\nG0 X6\n");
|
||||||
|
});
|
||||||
|
simulator.fs.module.FS.unlink(`${fixturePath}/nested/fixture.ngc`);
|
||||||
|
const refreshedPersistedFiles = await simulator.fs.opfs.persistDirectory("fixtures");
|
||||||
|
expectFileList(refreshedPersistedFiles, ["root.ngc"],
|
||||||
|
"unexpected refreshed OPFS directory persist file list");
|
||||||
|
await expectMissingOpfsFile(
|
||||||
|
() => simulator.fs.opfs.readFile("fixtures/stale.ngc"),
|
||||||
|
"stale OPFS directory file remained after directory persist",
|
||||||
|
);
|
||||||
|
await expectMissingOpfsFile(
|
||||||
|
() => simulator.fs.opfs.readFile("fixtures/nested/fixture.ngc"),
|
||||||
|
"removed WASM directory file remained in OPFS after directory persist",
|
||||||
|
);
|
||||||
|
simulator.fs.module.FS.writeFile(`${fixturePath}/nested/fixture.ngc`, "G21 G90\nG0 X3\n");
|
||||||
|
const restoredPersistedFiles = await simulator.fs.opfs.persistDirectory("fixtures");
|
||||||
|
expectFileList(restoredPersistedFiles, ["nested/fixture.ngc", "root.ngc"],
|
||||||
|
"unexpected restored OPFS directory persist file list");
|
||||||
|
const loadedFiles = await simulator.fs.opfs.readDirectory("fixtures");
|
||||||
|
expectFileList(loadedFiles, ["nested/fixture.ngc", "root.ngc"],
|
||||||
|
"unexpected OPFS directory load file list");
|
||||||
|
const loadedRootPath = simulator.fs.opfs.resolvePath("fixtures/root.ngc");
|
||||||
|
const loadedRootDecoded = new TextDecoder().decode(simulator.fs.module.FS.readFile(loadedRootPath));
|
||||||
|
expectText(loadedRootDecoded, "G21 G90\nG0 X4\n",
|
||||||
|
"OPFS directory file was not mirrored into the WASM filesystem");
|
||||||
|
const loadedEmptyPath = simulator.fs.opfs.resolvePath("fixtures/empty/nested");
|
||||||
|
const loadedEmptyMode = simulator.fs.module.FS.stat(loadedEmptyPath).mode;
|
||||||
|
if (!simulator.fs.module.FS.isDir(loadedEmptyMode)) {
|
||||||
|
throw new Error("OPFS readDirectory did not mirror an empty directory into WASM FS");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await runSection("opfs directory refresh and type conflicts", async () => {
|
||||||
|
const loadedRootPath = simulator.fs.opfs.resolvePath("fixtures/root.ngc");
|
||||||
|
await withSmokeSimulator(async (updateDirectorySimulator) => {
|
||||||
|
await updateDirectorySimulator.fs.opfs.removeFile("fixtures/root.ngc");
|
||||||
|
await updateDirectorySimulator.fs.opfs.writeFile("fixtures/nested/fixture.ngc", "G21 G90\nG0 X5\n");
|
||||||
|
const refreshedFiles = await simulator.fs.opfs.readDirectory("fixtures");
|
||||||
|
expectFileList(refreshedFiles, ["nested/fixture.ngc"],
|
||||||
|
"unexpected refreshed OPFS directory file list");
|
||||||
|
expectMissingWasmPath(
|
||||||
|
() => simulator.fs.module.FS.stat(loadedRootPath),
|
||||||
|
"stale OPFS directory mirror file remained in WASM FS",
|
||||||
|
);
|
||||||
|
const refreshedFixturePath = simulator.fs.opfs.resolvePath("fixtures/nested/fixture.ngc");
|
||||||
|
const refreshedFixture = new TextDecoder().decode(
|
||||||
|
simulator.fs.module.FS.readFile(refreshedFixturePath),
|
||||||
|
);
|
||||||
|
expectText(refreshedFixture, "G21 G90\nG0 X5\n",
|
||||||
|
"OPFS directory refresh did not replace the WASM mirror file");
|
||||||
|
await simulator.fs.opfs.writeFile("fixtures/type-conflict/original.ngc", "G21 G90\n");
|
||||||
|
await updateDirectorySimulator.fs.opfs.removeDirectory("fixtures/type-conflict");
|
||||||
|
await updateDirectorySimulator.fs.opfs.writeFile("fixtures/type-conflict", "G21 G90\nG0 X11\n");
|
||||||
|
const replacedConflictFile = new TextDecoder().decode(
|
||||||
|
await simulator.fs.opfs.readFile("fixtures/type-conflict"),
|
||||||
|
);
|
||||||
|
expectText(replacedConflictFile, "G21 G90\nG0 X11\n",
|
||||||
|
"OPFS readFile did not load a file that replaced a directory");
|
||||||
|
const conflictMirrorPath = simulator.fs.opfs.resolvePath("fixtures/type-conflict");
|
||||||
|
const conflictMirrorMode = simulator.fs.module.FS.stat(conflictMirrorPath).mode;
|
||||||
|
if (!simulator.fs.module.FS.isFile(conflictMirrorMode)) {
|
||||||
|
throw new Error("OPFS readFile did not replace the WASM mirror directory with a file");
|
||||||
|
}
|
||||||
|
await updateDirectorySimulator.fs.opfs.removeFile("fixtures/type-conflict");
|
||||||
|
await updateDirectorySimulator.fs.opfs.writeFile(
|
||||||
|
"fixtures/type-conflict/original.ngc",
|
||||||
|
"G21 G90\n",
|
||||||
|
);
|
||||||
|
await updateDirectorySimulator.fs.opfs.removeDirectory("fixtures/type-conflict");
|
||||||
|
await updateDirectorySimulator.fs.opfs.writeFile("fixtures/root.ngc", "G21 G90\nG0 X4\n");
|
||||||
|
await updateDirectorySimulator.fs.opfs.writeFile("fixtures/nested/fixture.ngc", "G21 G90\nG0 X3\n");
|
||||||
|
const restoredFiles = await simulator.fs.opfs.readDirectory("fixtures");
|
||||||
|
expectFileList(restoredFiles, ["nested/fixture.ngc", "root.ngc"],
|
||||||
|
"unexpected reloaded OPFS directory file list");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await runSection("opfs directory restore in new instance", async () => {
|
||||||
|
await withSmokeSimulator(async (restoredDirectorySimulator) => {
|
||||||
|
const restoredDirectoryFiles = await restoredDirectorySimulator.fs.opfs.readDirectory("fixtures");
|
||||||
|
expectFileList(restoredDirectoryFiles, ["nested/fixture.ngc", "root.ngc"],
|
||||||
|
"unexpected restored OPFS directory file list");
|
||||||
|
const restoredFixtureRootPath = restoredDirectorySimulator.fs.opfs.resolvePath("fixtures/root.ngc");
|
||||||
|
const restoredFixtureRoot = new TextDecoder().decode(
|
||||||
|
restoredDirectorySimulator.fs.module.FS.readFile(restoredFixtureRootPath),
|
||||||
|
);
|
||||||
|
expectText(restoredFixtureRoot, "G21 G90\nG0 X4\n",
|
||||||
|
"OPFS directory was not restored into a new WASM filesystem instance");
|
||||||
|
const restoredEmptyFixturePath = restoredDirectorySimulator.fs.opfs.resolvePath("fixtures/empty/nested");
|
||||||
|
const restoredEmptyFixtureMode =
|
||||||
|
restoredDirectorySimulator.fs.module.FS.stat(restoredEmptyFixturePath).mode;
|
||||||
|
if (!restoredDirectorySimulator.fs.module.FS.isDir(restoredEmptyFixtureMode)) {
|
||||||
|
throw new Error("OPFS empty directory was not restored into a new WASM filesystem instance");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await runSection("opfs metadata queries avoid wasm mirror pollution", async () => {
|
||||||
|
await withSmokeSimulator(async (existsOnlySimulator) => {
|
||||||
|
await existsOnlySimulator.fs.opfs.writeFile("programs/exists-only.ngc", "G21 G90\nG0 X10\n");
|
||||||
|
const existsOnlyPath = simulator.fs.opfs.resolvePath("programs/exists-only.ngc");
|
||||||
|
if (!(await simulator.fs.opfs.exists("programs/exists-only.ngc"))) {
|
||||||
|
throw new Error("OPFS exists did not see a file written by another simulator instance");
|
||||||
|
}
|
||||||
|
const existsOnlyStat = await simulator.fs.opfs.stat("programs/exists-only.ngc");
|
||||||
|
if (existsOnlyStat.kind !== "file" || existsOnlyStat.size !== 15) {
|
||||||
|
throw new Error("OPFS stat did not see a file written by another simulator instance");
|
||||||
|
}
|
||||||
|
expectMissingWasmPath(
|
||||||
|
() => simulator.fs.module.FS.stat(existsOnlyPath),
|
||||||
|
"OPFS metadata queries polluted the WASM filesystem mirror",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await runSection("opfs workspace clear and mirror removal", async () => {
|
||||||
|
await simulator.fs.opfs.clear();
|
||||||
|
await withSmokeSimulator(async (clearedWorkspaceSimulator) => {
|
||||||
|
await expectMissingOpfsFile(
|
||||||
|
() => clearedWorkspaceSimulator.fs.opfs.readDirectory("fixtures"),
|
||||||
|
"cleared OPFS workspace still exposed a persisted directory",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
await simulator.fs.opfs.removeFile("programs/browser-smoke.ngc");
|
||||||
|
if (await simulator.fs.opfs.exists("programs/browser-smoke.ngc")) {
|
||||||
|
throw new Error("OPFS exists reported a removed file");
|
||||||
|
}
|
||||||
|
const removedProgramPath = simulator.fs.opfs.resolvePath("programs/browser-smoke.ngc");
|
||||||
|
expectMissingWasmPath(
|
||||||
|
() => simulator.fs.module.FS.stat(removedProgramPath),
|
||||||
|
"OPFS removeFile did not remove the WASM mirror",
|
||||||
|
);
|
||||||
|
const fixturePath = simulator.fs.opfs.resolvePath("fixtures");
|
||||||
|
await simulator.fs.opfs.removeDirectory("fixtures");
|
||||||
|
await simulator.fs.opfs.removeDirectory("fixtures");
|
||||||
|
expectMissingWasmPath(
|
||||||
|
() => simulator.fs.module.FS.stat(fixturePath),
|
||||||
|
"OPFS removeDirectory did not remove the WASM mirror",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
76
web/test-browser-wasm-smoke-opfs-mirror-sections.js
Normal file
76
web/test-browser-wasm-smoke-opfs-mirror-sections.js
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
export async function runBrowserOpfsMirrorSections(context, simulator) {
|
||||||
|
const {
|
||||||
|
expectMissingWasmPath,
|
||||||
|
expectText,
|
||||||
|
runSection,
|
||||||
|
} = context;
|
||||||
|
|
||||||
|
await runSection("opfs wasm mirror persist file", async () => {
|
||||||
|
const wasmPath = simulator.fs.opfs.resolvePath("programs/browser-smoke.ngc");
|
||||||
|
const wasmDecoded = new TextDecoder().decode(simulator.fs.module.FS.readFile(wasmPath));
|
||||||
|
expectText(wasmDecoded, "G21 G90\nG0 X1\n", "OPFS file was not mirrored into the WASM filesystem");
|
||||||
|
const persistedPath = simulator.fs.opfs.resolvePath("programs/browser-persisted.ngc");
|
||||||
|
simulator.fs.module.FS.writeFile(persistedPath, new TextEncoder().encode("G21 G90\nG0 X2\n"));
|
||||||
|
const persisted = new TextDecoder().decode(await simulator.fs.opfs.persistFile("programs/browser-persisted.ngc"));
|
||||||
|
expectText(persisted, "G21 G90\nG0 X2\n", "WASM filesystem file was not persisted into OPFS");
|
||||||
|
const persistedDecoded = new TextDecoder().decode(
|
||||||
|
await simulator.fs.opfs.readFile("programs/browser-persisted.ngc"),
|
||||||
|
);
|
||||||
|
expectText(persistedDecoded, "G21 G90\nG0 X2\n",
|
||||||
|
"persisted WASM filesystem file was not readable from OPFS");
|
||||||
|
});
|
||||||
|
|
||||||
|
await runSection("opfs copy and move mirror files", async () => {
|
||||||
|
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters()
|
||||||
|
// links the current parameter file to filename + ".bak", then
|
||||||
|
// writes filename + ".new" and renames it over the parameter file.
|
||||||
|
await simulator.fs.opfs.writeFile("parameters/move-main.var", "5161\t1.000000\n");
|
||||||
|
const copiedParameterBackup = new TextDecoder().decode(
|
||||||
|
await simulator.fs.opfs.copyFile("parameters/move-main.var", "parameters/move-main.var.bak"),
|
||||||
|
);
|
||||||
|
expectText(copiedParameterBackup, "5161\t1.000000\n",
|
||||||
|
"OPFS copyFile did not return the copied file contents");
|
||||||
|
const copiedParameterBackupFile = new TextDecoder().decode(
|
||||||
|
await simulator.fs.opfs.readFile("parameters/move-main.var.bak"),
|
||||||
|
);
|
||||||
|
expectText(copiedParameterBackupFile, "5161\t1.000000\n",
|
||||||
|
"OPFS copyFile did not create the backup file");
|
||||||
|
const copiedParameterBackupWasmPath = simulator.fs.opfs.resolvePath("parameters/move-main.var.bak");
|
||||||
|
const copiedParameterBackupWasm = new TextDecoder().decode(
|
||||||
|
simulator.fs.module.FS.readFile(copiedParameterBackupWasmPath),
|
||||||
|
);
|
||||||
|
expectText(copiedParameterBackupWasm, "5161\t1.000000\n",
|
||||||
|
"OPFS copyFile did not update the WASM mirror destination");
|
||||||
|
await simulator.fs.opfs.writeFile("parameters/move-main.var.new", "5161\t2.000000\n");
|
||||||
|
await simulator.fs.opfs.moveFile("parameters/move-main.var.new", "parameters/move-main.var");
|
||||||
|
const movedParameterFile = new TextDecoder().decode(
|
||||||
|
await simulator.fs.opfs.readFile("parameters/move-main.var"),
|
||||||
|
);
|
||||||
|
expectText(movedParameterFile, "5161\t2.000000\n",
|
||||||
|
"OPFS moveFile did not replace the destination file");
|
||||||
|
if (await simulator.fs.opfs.exists("parameters/move-main.var.new")) {
|
||||||
|
throw new Error("OPFS moveFile left the source file behind");
|
||||||
|
}
|
||||||
|
const movedParameterWasmPath = simulator.fs.opfs.resolvePath("parameters/move-main.var");
|
||||||
|
const movedParameterWasm = new TextDecoder().decode(
|
||||||
|
simulator.fs.module.FS.readFile(movedParameterWasmPath),
|
||||||
|
);
|
||||||
|
expectText(movedParameterWasm, "5161\t2.000000\n",
|
||||||
|
"OPFS moveFile did not update the WASM mirror destination");
|
||||||
|
const movedTemporaryWasmPath = simulator.fs.opfs.resolvePath("parameters/move-main.var.new");
|
||||||
|
expectMissingWasmPath(
|
||||||
|
() => simulator.fs.module.FS.stat(movedTemporaryWasmPath),
|
||||||
|
"OPFS moveFile left the WASM mirror source behind",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
await runSection("opfs write truncates wasm mirror", async () => {
|
||||||
|
await simulator.fs.opfs.writeFile("programs/truncate.ngc", "G21 G90\nG0 X12345\nM30\n");
|
||||||
|
await simulator.fs.opfs.writeFile("programs/truncate.ngc", "G21\n");
|
||||||
|
const truncatedProgram = new TextDecoder().decode(await simulator.fs.opfs.readFile("programs/truncate.ngc"));
|
||||||
|
expectText(truncatedProgram, "G21\n", "OPFS writeFile did not truncate stale program bytes");
|
||||||
|
const truncatedWasmPath = simulator.fs.opfs.resolvePath("programs/truncate.ngc");
|
||||||
|
const truncatedWasmProgram = new TextDecoder().decode(simulator.fs.module.FS.readFile(truncatedWasmPath));
|
||||||
|
expectText(truncatedWasmProgram, "G21\n", "OPFS writeFile did not truncate the WASM mirror");
|
||||||
|
});
|
||||||
|
}
|
||||||
255
web/test-browser-wasm-smoke-opfs-parameter-sections.js
Normal file
255
web/test-browser-wasm-smoke-opfs-parameter-sections.js
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
export async function runBrowserOpfsParameterSections(context, simulator) {
|
||||||
|
const {
|
||||||
|
expectErrorContaining,
|
||||||
|
expectEvent,
|
||||||
|
expectIncludes,
|
||||||
|
expectMissingOpfsFile,
|
||||||
|
expectMissingWasmPath,
|
||||||
|
expectNoEvent,
|
||||||
|
expectText,
|
||||||
|
expectWasmFilesMissing,
|
||||||
|
near,
|
||||||
|
removeWasmPathIfExists,
|
||||||
|
runSection,
|
||||||
|
withSmokeSimulator,
|
||||||
|
} = context;
|
||||||
|
|
||||||
|
await runSection("opfs parameter save", async () => {
|
||||||
|
await simulator.parseWithParameterFile(
|
||||||
|
"G21 G90\nG0 X1\nG28.1\nM30\n",
|
||||||
|
"parameters/rs274ngc.var",
|
||||||
|
"linuxcnc",
|
||||||
|
{ backend: "linuxcnc-rs274" },
|
||||||
|
);
|
||||||
|
const opfsParameterFile = new TextDecoder().decode(
|
||||||
|
await simulator.fs.opfs.readFile("parameters/rs274ngc.var"),
|
||||||
|
);
|
||||||
|
expectIncludes(opfsParameterFile, "5161\t1.000000", "LinuxCNC parameter file was not persisted into OPFS");
|
||||||
|
});
|
||||||
|
|
||||||
|
await runSection("opfs parameter restore in new instance", async () => {
|
||||||
|
await withSmokeSimulator(async (restoredWorkspaceSimulator) => {
|
||||||
|
// LinuxCNC source basis: rs274ngc_pre.cc restore_parameters()
|
||||||
|
// loads persisted numbered parameters before interpreting blocks.
|
||||||
|
const restoredWorkspaceEvents = await restoredWorkspaceSimulator.parseWithParameterFile(
|
||||||
|
"G21 G90\nF100\nO10 if [#5161 EQ 1]\nG1 X8\nO10 endif\n",
|
||||||
|
"parameters/rs274ngc.var",
|
||||||
|
"linuxcnc",
|
||||||
|
{ backend: "linuxcnc-rs274" },
|
||||||
|
);
|
||||||
|
expectEvent(restoredWorkspaceEvents, (event) => event.type === "linear-feed" && near(event.end.x, 8),
|
||||||
|
"LinuxCNC OPFS parameter file was not restored across WASM simulator instances");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await runSection("opfs parameter temporary cleanup", async () => {
|
||||||
|
await simulator.fs.opfs.writeFile("parameters/rs274ngc.var.new", "stale temporary parameter file\n");
|
||||||
|
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters() writes
|
||||||
|
// filename + ".new", then renames it over the parameter file.
|
||||||
|
simulator.fs.module.FS.writeFile("rs274ngc.var.new", "stale wasm temporary parameter file\n");
|
||||||
|
const restoredParameterEvents = await simulator.parseWithParameterFile(
|
||||||
|
"G21 G90\nF100\nO10 if [#5161 EQ 1]\nG1 X9\nO10 endif\n",
|
||||||
|
"parameters/rs274ngc.var",
|
||||||
|
"linuxcnc",
|
||||||
|
{ backend: "linuxcnc-rs274" },
|
||||||
|
);
|
||||||
|
expectEvent(restoredParameterEvents, (event) => event.type === "linear-feed" && near(event.end.x, 9),
|
||||||
|
"LinuxCNC parameter file was not restored from OPFS");
|
||||||
|
await expectMissingOpfsFile(
|
||||||
|
() => simulator.fs.opfs.readFile("parameters/rs274ngc.var.new"),
|
||||||
|
"stale LinuxCNC temporary parameter file remained in OPFS",
|
||||||
|
);
|
||||||
|
expectMissingWasmPath(
|
||||||
|
() => simulator.fs.module.FS.readFile("rs274ngc.var.new"),
|
||||||
|
"stale LinuxCNC temporary parameter file remained in WASM FS",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
await runSection("opfs parameter backup bridge", async () => {
|
||||||
|
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters()
|
||||||
|
// attempts link(filename, filename + ".bak") but treats link
|
||||||
|
// failure as non-fatal. The browser bridge must persist a backup
|
||||||
|
// when the WASM filesystem exposes one.
|
||||||
|
simulator.fs.module.FS.writeFile("rs274ngc.var", "5161\t2.000000\n");
|
||||||
|
simulator.fs.module.FS.writeFile("rs274ngc.var.bak", "5161\t1.000000\n");
|
||||||
|
await simulator.fs.opfs.persistParameterFile("parameters/backup-bridge.var");
|
||||||
|
const opfsParameterBackup = new TextDecoder().decode(
|
||||||
|
await simulator.fs.opfs.readFile("parameters/backup-bridge.var.bak"),
|
||||||
|
);
|
||||||
|
expectIncludes(opfsParameterBackup, "5161\t1.000000",
|
||||||
|
"LinuxCNC parameter backup file was not bridged into OPFS");
|
||||||
|
});
|
||||||
|
|
||||||
|
await runSection("opfs parameter stale backup cleanup", async () => {
|
||||||
|
removeWasmPathIfExists(simulator, "rs274ngc.var.bak");
|
||||||
|
removeWasmPathIfExists(simulator, "/rs274ngc.var.bak");
|
||||||
|
await simulator.parseWithParameterFile(
|
||||||
|
"G21 G90\nM30\n",
|
||||||
|
"parameters/rs274ngc.var",
|
||||||
|
"linuxcnc",
|
||||||
|
{ backend: "linuxcnc-rs274" },
|
||||||
|
);
|
||||||
|
await expectMissingOpfsFile(
|
||||||
|
() => simulator.fs.opfs.readFile("parameters/rs274ngc.var.bak"),
|
||||||
|
"stale LinuxCNC parameter backup file remained in OPFS",
|
||||||
|
);
|
||||||
|
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters()
|
||||||
|
// unlinks stale backup files before replacing the parameter file.
|
||||||
|
await simulator.fs.opfs.removeFile("parameters/missing-cleanup/rs274ngc.var.bak");
|
||||||
|
});
|
||||||
|
|
||||||
|
await runSection("opfs program file with parameter persistence", async () => {
|
||||||
|
await simulator.fs.opfs.writeFile("programs/parameter-file-smoke.ngc", "G21 G90\nF100\nG0 X1\nG28.1\nM30\n");
|
||||||
|
const fileParameterEvents = await simulator.parseFileWithParameterFile(
|
||||||
|
"programs/parameter-file-smoke.ngc",
|
||||||
|
"parameters/rs274ngc.var",
|
||||||
|
"linuxcnc",
|
||||||
|
{ backend: "linuxcnc-rs274" },
|
||||||
|
);
|
||||||
|
expectEvent(fileParameterEvents, (event) => event.type === "program-end",
|
||||||
|
"OPFS program file with LinuxCNC parameter persistence did not parse");
|
||||||
|
});
|
||||||
|
|
||||||
|
await runSection("opfs missing parameter file creation", async () => {
|
||||||
|
await simulator.fs.opfs.writeFile("parameters/missing-main.var.bak", "stale backup without main file\n");
|
||||||
|
await simulator.fs.opfs.writeFile("parameters/missing-main.var.new", "stale temporary without main file\n");
|
||||||
|
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters() unlinks
|
||||||
|
// filename + RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX before linking,
|
||||||
|
// then renames filename + ".new" over the parameter file.
|
||||||
|
simulator.fs.module.FS.writeFile("rs274ngc.var.bak", "stale wasm backup without main file\n");
|
||||||
|
simulator.fs.module.FS.writeFile("rs274ngc.var.new", "stale wasm temporary without main file\n");
|
||||||
|
await simulator.parseWithParameterFile(
|
||||||
|
"G21 G90\nM30\n",
|
||||||
|
"parameters/missing-main.var",
|
||||||
|
"linuxcnc",
|
||||||
|
{ backend: "linuxcnc-rs274" },
|
||||||
|
);
|
||||||
|
const missingMainParameterFile = new TextDecoder().decode(
|
||||||
|
await simulator.fs.opfs.readFile("parameters/missing-main.var"),
|
||||||
|
);
|
||||||
|
expectIncludes(missingMainParameterFile, "5220\t1.000000",
|
||||||
|
"LinuxCNC missing parameter file was not created through OPFS");
|
||||||
|
expectIncludes(missingMainParameterFile, "5161\t0.000000",
|
||||||
|
"LinuxCNC missing parameter file did not include required G28 parameter");
|
||||||
|
const missingMainRestoreEvents = await simulator.parseWithParameterFile(
|
||||||
|
"G21 G90\nF100\nO10 if [#5220 EQ 1]\nG1 X6\nO10 endif\n",
|
||||||
|
"parameters/missing-main.var",
|
||||||
|
"linuxcnc",
|
||||||
|
{ backend: "linuxcnc-rs274" },
|
||||||
|
);
|
||||||
|
expectEvent(missingMainRestoreEvents, (event) => event.type === "linear-feed" && near(event.end.x, 6),
|
||||||
|
"LinuxCNC-created missing OPFS parameter file was not restored on the next parse");
|
||||||
|
await expectMissingOpfsFile(
|
||||||
|
() => simulator.fs.opfs.readFile("parameters/missing-main.var.bak"),
|
||||||
|
"stale LinuxCNC missing-parameter backup remained in OPFS",
|
||||||
|
);
|
||||||
|
expectMissingWasmPath(
|
||||||
|
() => simulator.fs.module.FS.readFile("rs274ngc.var.bak"),
|
||||||
|
"stale LinuxCNC missing-parameter backup remained in WASM FS",
|
||||||
|
);
|
||||||
|
await expectMissingOpfsFile(
|
||||||
|
() => simulator.fs.opfs.readFile("parameters/missing-main.var.new"),
|
||||||
|
"stale LinuxCNC missing-parameter temporary file remained in OPFS",
|
||||||
|
);
|
||||||
|
expectMissingWasmPath(
|
||||||
|
() => simulator.fs.module.FS.readFile("rs274ngc.var.new"),
|
||||||
|
"stale LinuxCNC missing-parameter temporary file remained in WASM FS",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
await runSection("opfs invalid parameter restore failures", async () => {
|
||||||
|
await simulator.fs.opfs.writeFile("parameters/out-of-order.var", "5162\t1.000000\n5161\t1.000000\n");
|
||||||
|
await expectErrorContaining(
|
||||||
|
() => simulator.parseWithParameterFile(
|
||||||
|
"G21 G90\nM30\n",
|
||||||
|
"parameters/out-of-order.var",
|
||||||
|
"linuxcnc",
|
||||||
|
{ backend: "linuxcnc-rs274" },
|
||||||
|
),
|
||||||
|
"Parameter file out of order",
|
||||||
|
"LinuxCNC accepted an out-of-order OPFS parameter file",
|
||||||
|
);
|
||||||
|
const outOfOrderParameterFile = new TextDecoder().decode(
|
||||||
|
await simulator.fs.opfs.readFile("parameters/out-of-order.var"),
|
||||||
|
);
|
||||||
|
expectText(outOfOrderParameterFile, "5162\t1.000000\n5161\t1.000000\n",
|
||||||
|
"LinuxCNC persisted an out-of-order OPFS parameter file after restore failure");
|
||||||
|
expectMissingWasmPath(
|
||||||
|
() => simulator.fs.module.FS.readFile("rs274ngc.var"),
|
||||||
|
"LinuxCNC out-of-order parameter file remained in WASM FS after restore failure",
|
||||||
|
);
|
||||||
|
await simulator.fs.opfs.writeFile("parameters/out-of-range.var", "0\t1.000000\n");
|
||||||
|
await expectErrorContaining(
|
||||||
|
() => simulator.parseWithParameterFile(
|
||||||
|
"G21 G90\nM30\n",
|
||||||
|
"parameters/out-of-range.var",
|
||||||
|
"linuxcnc",
|
||||||
|
{ backend: "linuxcnc-rs274" },
|
||||||
|
),
|
||||||
|
"Parameter number out of range",
|
||||||
|
"LinuxCNC accepted an out-of-range OPFS parameter file",
|
||||||
|
);
|
||||||
|
const outOfRangeParameterFile = new TextDecoder().decode(
|
||||||
|
await simulator.fs.opfs.readFile("parameters/out-of-range.var"),
|
||||||
|
);
|
||||||
|
expectText(outOfRangeParameterFile, "0\t1.000000\n",
|
||||||
|
"LinuxCNC persisted an out-of-range OPFS parameter file after restore failure");
|
||||||
|
expectMissingWasmPath(
|
||||||
|
() => simulator.fs.module.FS.readFile("rs274ngc.var"),
|
||||||
|
"LinuxCNC out-of-range parameter file remained in WASM FS after restore failure",
|
||||||
|
);
|
||||||
|
await simulator.fs.opfs.writeFile("parameters/failed-parse.var", "1\t0.000000\n");
|
||||||
|
await expectErrorContaining(
|
||||||
|
() => simulator.parseWithParameterFile(
|
||||||
|
"G21 G90\nG28.1\n#0 = 1\n",
|
||||||
|
"parameters/failed-parse.var",
|
||||||
|
"linuxcnc",
|
||||||
|
{ backend: "linuxcnc-rs274" },
|
||||||
|
),
|
||||||
|
"",
|
||||||
|
"LinuxCNC accepted a failing parse that should not persist parameters",
|
||||||
|
);
|
||||||
|
const failedParseParameterFile = new TextDecoder().decode(
|
||||||
|
await simulator.fs.opfs.readFile("parameters/failed-parse.var"),
|
||||||
|
);
|
||||||
|
if (failedParseParameterFile.includes("5161\t1.000000")) {
|
||||||
|
throw new Error("LinuxCNC persisted failed parse parameter updates into OPFS");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await runSection("opfs stale parameter state cleanup", async () => {
|
||||||
|
await simulator.fs.opfs.writeFile("parameters/stale-state.var", "5161\t1.000000\n");
|
||||||
|
const staleStateEvents = await simulator.parseWithParameterFile(
|
||||||
|
"G21 G90\nF100\nO10 if [#5161 EQ 1]\nG1 X9\nO10 endif\n",
|
||||||
|
"parameters/stale-state.var",
|
||||||
|
"linuxcnc",
|
||||||
|
{ backend: "linuxcnc-rs274" },
|
||||||
|
);
|
||||||
|
expectEvent(staleStateEvents, (event) => event.type === "linear-feed" && near(event.end.x, 9),
|
||||||
|
"LinuxCNC did not honor the loaded OPFS parameter state");
|
||||||
|
await simulator.fs.opfs.removeFile("parameters/stale-state.var");
|
||||||
|
const missingStateEvents = await simulator.parseWithParameterFile(
|
||||||
|
"G21 G90\nF100\nO10 if [#5161 EQ 1]\nG1 X9\nO10 endif\n",
|
||||||
|
"parameters/stale-state.var",
|
||||||
|
"linuxcnc",
|
||||||
|
{ backend: "linuxcnc-rs274" },
|
||||||
|
);
|
||||||
|
expectNoEvent(missingStateEvents, (event) => event.type === "linear-feed" && near(event.end.x, 9),
|
||||||
|
"LinuxCNC stale OPFS parameter state was not cleared when the file was missing");
|
||||||
|
// LinuxCNC source basis: rs274ngc_pre.cc restore_parameters()
|
||||||
|
// treats a missing parameter file as OK; the browser OPFS bridge
|
||||||
|
// must not leave stale default parameter files in the WASM mirror.
|
||||||
|
simulator.fs.module.FS.writeFile("rs274ngc.var", "5161\t1.000000\n");
|
||||||
|
simulator.fs.module.FS.writeFile("rs274ngc.var.new", "stale temporary parameter file\n");
|
||||||
|
simulator.fs.module.FS.writeFile("rs274ngc.var.bak", "stale backup parameter file\n");
|
||||||
|
const missingLoad = await simulator.fs.opfs.loadParameterFile("parameters/load-missing.var");
|
||||||
|
if (missingLoad !== null) {
|
||||||
|
throw new Error("missing OPFS parameter file did not return null");
|
||||||
|
}
|
||||||
|
expectWasmFilesMissing(
|
||||||
|
simulator.fs.module.FS,
|
||||||
|
["rs274ngc.var", "rs274ngc.var.new", "rs274ngc.var.bak"],
|
||||||
|
(parameterPath) => `missing OPFS parameter load left stale WASM file ${parameterPath}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
49
web/test-browser-wasm-smoke-opfs-policy-sections.js
Normal file
49
web/test-browser-wasm-smoke-opfs-policy-sections.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
export async function runBrowserOpfsPolicySections(context) {
|
||||||
|
const {
|
||||||
|
browserHasOpfs,
|
||||||
|
createDefaultBrowserSimulator,
|
||||||
|
createSmokeSimulator,
|
||||||
|
expectErrorContaining,
|
||||||
|
runSection,
|
||||||
|
} = context;
|
||||||
|
if (!browserHasOpfs()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await runSection("browser opfs policy", async () => {
|
||||||
|
const defaultOpfsSimulator = await createDefaultBrowserSimulator();
|
||||||
|
try {
|
||||||
|
if (!defaultOpfsSimulator.fs.opfs) {
|
||||||
|
throw new Error("default browser simulator did not create an OPFS workspace");
|
||||||
|
}
|
||||||
|
if (defaultOpfsSimulator.fs.opfs.workspacePath !== "cnc-simulator") {
|
||||||
|
throw new Error("default browser simulator used an unexpected OPFS workspace path");
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
defaultOpfsSimulator.dispose();
|
||||||
|
}
|
||||||
|
await expectErrorContaining(
|
||||||
|
() => createSmokeSimulator(false),
|
||||||
|
"OPFS cannot be disabled",
|
||||||
|
"browser simulator allowed OPFS to be disabled",
|
||||||
|
);
|
||||||
|
for (const unsafeWorkspacePath of ["../bad-workspace", "/bad-workspace", "bad//workspace"]) {
|
||||||
|
await expectErrorContaining(
|
||||||
|
() => createSmokeSimulator({ workspacePath: unsafeWorkspacePath }),
|
||||||
|
"inside the CNC workspace",
|
||||||
|
`browser simulator accepted unsafe OPFS workspace ${unsafeWorkspacePath}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (const unsafeMountPoint of ["cnc", "/cnc/../escape", "/cnc//escape"]) {
|
||||||
|
await expectErrorContaining(
|
||||||
|
() => createSmokeSimulator({ mountPoint: unsafeMountPoint, workspacePath: "cnc-simulator-smoke" }),
|
||||||
|
"Emscripten filesystem",
|
||||||
|
`browser simulator accepted unsafe OPFS mount ${unsafeMountPoint}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await expectErrorContaining(
|
||||||
|
() => createSmokeSimulator({ mountPoint: "", workspacePath: "cnc-simulator-smoke" }),
|
||||||
|
"non-empty absolute path",
|
||||||
|
"browser simulator accepted empty OPFS mount",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
14
web/test-browser-wasm-smoke-opfs-workspace-sections.js
Normal file
14
web/test-browser-wasm-smoke-opfs-workspace-sections.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { runBrowserOpfsBasicSections } from "/test-browser-wasm-smoke-opfs-basic-sections.js";
|
||||||
|
import { runBrowserOpfsDirectorySections } from "/test-browser-wasm-smoke-opfs-directory-sections.js";
|
||||||
|
import { runBrowserOpfsMirrorSections } from "/test-browser-wasm-smoke-opfs-mirror-sections.js";
|
||||||
|
import { runBrowserOpfsParameterSections } from "/test-browser-wasm-smoke-opfs-parameter-sections.js";
|
||||||
|
|
||||||
|
export async function runBrowserOpfsWorkspaceSections(context, simulator) {
|
||||||
|
if (!context.browserHasOpfs()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await runBrowserOpfsBasicSections(context, simulator);
|
||||||
|
await runBrowserOpfsParameterSections(context, simulator);
|
||||||
|
await runBrowserOpfsMirrorSections(context, simulator);
|
||||||
|
await runBrowserOpfsDirectorySections(context, simulator);
|
||||||
|
}
|
||||||
99
web/test-browser-wasm-smoke-sections.js
Normal file
99
web/test-browser-wasm-smoke-sections.js
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
import { createBrowserSmokeHarness } from "/test-browser-wasm-smoke-helpers.js";
|
||||||
|
import { runBrowserAppOpfsSections } from "/test-browser-wasm-smoke-app-sections.js";
|
||||||
|
import { runLinuxCncBrowserSections } from "/test-browser-wasm-smoke-linuxcnc-sections.js";
|
||||||
|
import { runBrowserOpfsPolicySections } from "/test-browser-wasm-smoke-opfs-policy-sections.js";
|
||||||
|
import { runBrowserOpfsWorkspaceSections } from "/test-browser-wasm-smoke-opfs-workspace-sections.js";
|
||||||
|
|
||||||
|
export async function runBrowserWasmSmoke({ createWasmSimulator, result }) {
|
||||||
|
const harness = createBrowserSmokeHarness(result);
|
||||||
|
const createSmokeSimulator = (opfs = { mountPoint: "/cnc", workspacePath: "cnc-simulator-smoke" }) =>
|
||||||
|
createWasmSimulator({
|
||||||
|
locateFile: (file) => `/public/${file}`,
|
||||||
|
print: () => {},
|
||||||
|
printErr: () => {},
|
||||||
|
...(opfs === undefined ? {} : { opfs }),
|
||||||
|
});
|
||||||
|
const createDefaultBrowserSimulator = () =>
|
||||||
|
createWasmSimulator({
|
||||||
|
locateFile: (file) => `/public/${file}`,
|
||||||
|
print: () => {},
|
||||||
|
printErr: () => {},
|
||||||
|
});
|
||||||
|
const withSmokeSimulator = async (action, opfs) => {
|
||||||
|
const nestedSimulator = await createSmokeSimulator(opfs);
|
||||||
|
try {
|
||||||
|
return await action(nestedSimulator);
|
||||||
|
} finally {
|
||||||
|
nestedSimulator.dispose();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const createHiddenAppFrame = async () => {
|
||||||
|
const appFrame = document.createElement("iframe");
|
||||||
|
const appHtml = await (await fetch("/")).text();
|
||||||
|
appFrame.srcdoc = appHtml.replace(
|
||||||
|
"</head>",
|
||||||
|
'<script src="/public/cnc_sim.js"><\/script></head>',
|
||||||
|
);
|
||||||
|
appFrame.style.display = "none";
|
||||||
|
document.body.appendChild(appFrame);
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
appFrame.addEventListener("load", resolve, { once: true });
|
||||||
|
});
|
||||||
|
return appFrame;
|
||||||
|
};
|
||||||
|
const waitForAppControls = (appFrame, message) =>
|
||||||
|
harness.waitFor(
|
||||||
|
() => appFrame.contentDocument?.querySelector("#parseBtn"),
|
||||||
|
message,
|
||||||
|
);
|
||||||
|
const parseAppFrameProgram = (appFrame, program) => {
|
||||||
|
const appInput = appFrame.contentDocument.querySelector("#programInput");
|
||||||
|
appInput.value = program;
|
||||||
|
appInput.dispatchEvent(new Event("input", { bubbles: true }));
|
||||||
|
appFrame.contentDocument.querySelector("#parseBtn").click();
|
||||||
|
};
|
||||||
|
const waitForAppAlarm = (appFrame, text, message) =>
|
||||||
|
harness.waitFor(
|
||||||
|
() => appFrame.contentDocument?.querySelector("#alarmList")?.textContent.includes(text),
|
||||||
|
message,
|
||||||
|
);
|
||||||
|
const getAppFrameSimulator = async (appFrame, message) => {
|
||||||
|
const appSimulator = await appFrame.contentWindow.__cncSimulatorForTest;
|
||||||
|
if (!appSimulator?.fs?.opfs) {
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
return appSimulator;
|
||||||
|
};
|
||||||
|
const disposeAppFrame = async (appFrame) => {
|
||||||
|
const appSimulator = await appFrame.contentWindow?.__cncSimulatorForTest;
|
||||||
|
appSimulator?.dispose?.();
|
||||||
|
appFrame.remove();
|
||||||
|
};
|
||||||
|
const context = {
|
||||||
|
...harness,
|
||||||
|
createDefaultBrowserSimulator,
|
||||||
|
createHiddenAppFrame,
|
||||||
|
createSmokeSimulator,
|
||||||
|
disposeAppFrame,
|
||||||
|
getAppFrameSimulator,
|
||||||
|
parseAppFrameProgram,
|
||||||
|
waitForAppAlarm,
|
||||||
|
waitForAppControls,
|
||||||
|
withSmokeSimulator,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const simulator = await createSmokeSimulator();
|
||||||
|
try {
|
||||||
|
await runLinuxCncBrowserSections(context, simulator);
|
||||||
|
await runBrowserOpfsWorkspaceSections(context, simulator);
|
||||||
|
await runBrowserOpfsPolicySections(context);
|
||||||
|
await runBrowserAppOpfsSections(context);
|
||||||
|
result.textContent = harness.completedMessage();
|
||||||
|
} finally {
|
||||||
|
simulator.dispose();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
result.textContent = `browser wasm smoke failed: ${error && error.stack ? error.stack : error}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,82 +6,16 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<pre id="result">pending</pre>
|
<pre id="result">pending</pre>
|
||||||
|
<!-- smoke marker: browser wasm smoke passed -->
|
||||||
<script src="/public/cnc_sim.js"></script>
|
<script src="/public/cnc_sim.js"></script>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import { createWasmSimulator } from "/src/wasm-core.js";
|
import { createWasmSimulator } from "/src/wasm-core.js";
|
||||||
|
import { runBrowserWasmSmoke } from "/test-browser-wasm-smoke-sections.js";
|
||||||
|
|
||||||
const result = document.querySelector("#result");
|
runBrowserWasmSmoke({
|
||||||
const near = (actual, expected) => Math.abs(actual - expected) < 1e-6;
|
createWasmSimulator,
|
||||||
|
result: document.querySelector("#result"),
|
||||||
try {
|
|
||||||
const simulator = await createWasmSimulator({
|
|
||||||
locateFile: (file) => `/public/${file}`,
|
|
||||||
print: () => {},
|
|
||||||
printErr: () => {},
|
|
||||||
});
|
});
|
||||||
try {
|
|
||||||
const events = simulator.parse("G21 G90\nG0 X0\nG1 X5 F100\nM30\n", "linuxcnc", {
|
|
||||||
backend: "linuxcnc-rs274",
|
|
||||||
});
|
|
||||||
const sawFeed = events.some((event) => event.type === "linear-feed" && near(event.end.x, 5));
|
|
||||||
const sawEnd = events.some((event) => event.type === "program-end");
|
|
||||||
if (!sawFeed || !sawEnd) {
|
|
||||||
throw new Error("missing expected LinuxCNC WASM browser events");
|
|
||||||
}
|
|
||||||
const switchEvents = simulator.parse("M428\nM429\nM430\n", "linuxcnc", {
|
|
||||||
backend: "linuxcnc-rs274",
|
|
||||||
switchkins: "scara",
|
|
||||||
});
|
|
||||||
for (const [line, kinstype] of [
|
|
||||||
[1, 0],
|
|
||||||
[2, 1],
|
|
||||||
[3, 2],
|
|
||||||
]) {
|
|
||||||
const sawSwitch = switchEvents.some(
|
|
||||||
(event) => event.type === "kinematics-switch" && event.line === line && event.reserved === kinstype,
|
|
||||||
);
|
|
||||||
if (!sawSwitch) {
|
|
||||||
throw new Error(`missing LinuxCNC WASM browser M${427 + line} switchkins type ${kinstype}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const configCaseResponse = await fetch("/public/linuxcnc_switchkins_remap_config_cases.json");
|
|
||||||
if (!configCaseResponse.ok) {
|
|
||||||
throw new Error(`failed to load LinuxCNC switchkins config cases: ${configCaseResponse.status}`);
|
|
||||||
}
|
|
||||||
const configCases = await configCaseResponse.json();
|
|
||||||
if (!Array.isArray(configCases) || configCases.length === 0) {
|
|
||||||
throw new Error("missing LinuxCNC switchkins config cases");
|
|
||||||
}
|
|
||||||
for (const configCase of configCases) {
|
|
||||||
const options = {
|
|
||||||
backend: "linuxcnc-rs274",
|
|
||||||
[configCase.field]: configCase.value,
|
|
||||||
};
|
|
||||||
const program = configCase.m430 >= 0 ? "M428\nM429\nM430\n" : "M428\nM429\n";
|
|
||||||
const configEvents = simulator.parse(program, "linuxcnc", options);
|
|
||||||
for (const [line, kinstype] of [
|
|
||||||
[1, configCase.m428],
|
|
||||||
[2, configCase.m429],
|
|
||||||
...(configCase.m430 >= 0 ? [[3, configCase.m430]] : []),
|
|
||||||
]) {
|
|
||||||
const sawSwitch = configEvents.some(
|
|
||||||
(event) => event.type === "kinematics-switch" && event.line === line && event.reserved === kinstype,
|
|
||||||
);
|
|
||||||
if (!sawSwitch) {
|
|
||||||
throw new Error(
|
|
||||||
`missing LinuxCNC WASM browser generated ${configCase.field}=${configCase.value} line ${line} switchkins type ${kinstype}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.textContent = "browser wasm smoke passed";
|
|
||||||
} finally {
|
|
||||||
simulator.dispose();
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
result.textContent = `browser wasm smoke failed: ${error && error.stack ? error.stack : error}`;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user