#!/bin/bash ################################################################################ # usage: linuxcnc [options] [] # # options: see usage() function below # # this version calls pickconfig.tcl to pick an INI file if one # is not specified on the command line # ################################################################################ # Author: # License: GPL Version 2 # System: Linux # # Copyright (c) 2004-2009 All rights reserved. ################################################################################ # -1. Get all rip-environment items if we are RIP # Shellcheck doesn't know about substitutions # shellcheck disable=SC2050 if [ "yes" = "@RUN_IN_PLACE@" ]; then if test "${EMC2_HOME:-}" != "@EMC2_HOME@"; then exec @EMC2_HOME@/scripts/rip-environment linuxcnc "$@" fi fi # Force pyopengl (python3-opengl) to use its GLX backend, even # if we're running on Wayland where it would normally use EGL. # Similar for the Gtk and Qt backends. if [[ ! -v LINUXCNC_OPENGL_PLATFORM || "${LINUXCNC_OPENGL_PLATFORM}" == "glx" ]]; then export PYOPENGL_PLATFORM="x11" export GDK_BACKEND="x11" export QT_QPA_PLATFORM="xcb" fi ################################################################################ # 0. Values that come from configure ################################################################################ # Shellcheck doesn't know about substitution # shellcheck disable=SC2034 prefix=@prefix@ # shellcheck disable=SC2034 exec_prefix=@exec_prefix@ PIDOF="@PIDOF@ -x" PS=@PS@ AWK=@AWK@ GREP=@GREP@ KILL=@KILL@ LINUXCNC_HOME=@EMC2_HOME@; export LINUXCNC_HOME LINUXCNC_BIN_DIR=@EMC2_BIN_DIR@ LINUXCNC_TCL_DIR=@EMC2_TCL_DIR@ LINUXCNC_HELP_DIR=@EMC2_HELP_DIR@ LINUXCNC_RTLIB_DIR=@EMC2_RTLIB_DIR@ # shellcheck disable=SC2034 LINUXCNC_CONFIG_PATH="@LINUXCNC_CONFIG_PATH@" LINUXCNC_NCFILES_DIR=@EMC2_NCFILES_DIR@ LINUXCNC_LANG_DIR=@EMC2_LANG_DIR@ REALTIME=@REALTIME@ LINUXCNC_IMAGEDIR=@EMC2_IMAGE_DIR@ # shellcheck disable=SC2034 LINUXCNC_TCL_LIB_DIR=@EMC2_TCL_LIB_DIR@ HALLIB_DIR=@HALLIB_DIR@; export HALLIB_DIR #HALLIB_PATH: see also -H option HALLIB_PATH=.:$HALLIB_DIR; export HALLIB_PATH # put ~.local/bin in PATH if missing. See: # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=839155 if [ -d "$HOME"/.local/bin ]; then if [[ "$PATH" != *".local/bin"* ]]; then PATH=$HOME/.local/bin:$PATH fi fi #put the LINUXCNC_BIN_DIR in PATH PATH=$LINUXCNC_BIN_DIR:$PATH #ditto scripts if not RIP [ -d $LINUXCNC_HOME/scripts ] && PATH=$LINUXCNC_HOME/scripts:$PATH # Shellcheck doesn't know about substitutions # shellcheck disable=SC2050 if [ "yes" = "@RUN_IN_PLACE@" ]; then if [ -z "$LD_LIBRARY_PATH" ]; then LD_LIBRARY_PATH=$LINUXCNC_HOME/lib else LD_LIBRARY_PATH=$LINUXCNC_HOME/lib:"$LD_LIBRARY_PATH" fi export LD_LIBRARY_PATH fi if [ -z "$PYTHONPATH" ]; then PYTHONPATH=$LINUXCNC_HOME/lib/python else PYTHONPATH=$LINUXCNC_HOME/lib/python:"$PYTHONPATH" fi export PYTHONPATH if [ "yes" != "@RUN_IN_PLACE@" ]; then if [ -z "$TCLLIBPATH" ]; then TCLLIBPATH=$LINUXCNC_HOME/lib/tcltk else TCLLIBPATH="$LINUXCNC_HOME/lib/tcltk $TCLLIBPATH" fi export TCLLIBPATH fi MODULE_EXT=@MODEXT@ # module extension, used when insmod'ing DEBUG_FILE=$(mktemp /tmp/linuxcnc.debug.XXXXXX) PRINT_FILE=$(mktemp /tmp/linuxcnc.print.XXXXXX) program_available () { type -path "$1" > /dev/null 2>&1 } usage () { P=${0##*/} cat <$DEBUG_FILE fi set -x;; v) # enable printing of verbose messages if tty -s; then PRINT_FILE=/dev/fd/1 echo "Verbose mode on" >$PRINT_FILE fi;; r) RUNTESTS=yes ;; l) USE_LAST_INIFILE=1;; k) DASHK=-k;; h) usage exit 0;; H) # -H dirname: prepend dirname to HALLIB_PATH if [ -d "$OPTARG" ]; then HALLIB_PATH=$(realpath "$OPTARG"):$HALLIB_PATH echo "HALLIB_PATH = $HALLIB_PATH" else echo "Invalid dirname specified: -H $OPTARG" exit 1 fi ;; t) # -t modname: user-supplied trajectory planning module # modname must conform to tpmod api, # and be located in: $LINUXCNC_RTLIB_DIR TPMOD=$OPTARG ;; m) # -m modname: user-supplied homing module # modname must conform to homing api, # and be located in: $LINUXCNC_RTLIB_DIR HOMEMOD=$OPTARG ;; *) usage exit 1 esac done shift $((OPTIND - 1)) # Shellcheck doesn't know about substitutions # shellcheck disable=SC2194 case "@KERNEL_VERS@" in "") ;; *) if [ "$(uname -r)" != "@KERNEL_VERS@" ]; then if tty -s || [ -z "$DISPLAY" ]; then echo "LinuxCNC requires the real-time kernel @KERNEL_VERS@ to run." echo "Before running LinuxCNC, reboot and choose this kernel at the boot menu." else @WISH@ <> "$DEBUG_FILE" exec >> "$PRINT_FILE" fi fi # Stop complaining unreachable. It is trap code. # shellcheck disable=SC2317,SC2329 function ErrorCheck () { result=$? if [ -n "$DISPLAY" ]; then echo "catch {send -async popimage destroy .}; destroy ." | @WISH@ fi if [ "$result" -ne 0 ]; then if tty -s || [ -z "$DISPLAY" ] ; then if [ -f "$DEBUG_FILE" ]; then cp "$DEBUG_FILE" "$HOME/linuxcnc_debug.txt" else echo "(debug information was sent to stderr)" \ > "$HOME/linuxcnc_debug.txt" fi if [ -f "$PRINT_FILE" ]; then cp "$PRINT_FILE" "$HOME/linuxcnc_print.txt" else echo "(print information was sent to stdout)" \ > "$HOME/linuxcnc_print.txt" fi echo "\ LinuxCNC terminated with an error. You can find more information in the log: $HOME/linuxcnc_debug.txt and $HOME/linuxcnc_print.txt as well as in the output of the shell command 'dmesg' and in the terminal" else @WISH@ "$LINUXCNC_TCL_DIR/show_errors.tcl" "$DEBUG_FILE" "$PRINT_FILE" fi fi rm -f "$DEBUG_FILE" "$PRINT_FILE" 2>/dev/null exit "$result" } trap ErrorCheck EXIT ################################################################################ # 1.3. INIFILE find INI file to use # ################################################################################ if [ -n "$1" ]; then case "$1" in -) USE_LAST_INIFILE=1;; /*) INIFILE="$1" ;; *) INIFILE="$(pwd)/$1";; esac shift fi EXTRA_ARGS=("$@") # 1.3.1. Determine if we have run-in place or installed system RUN_IN_PLACE=@RUN_IN_PLACE@ echo "RUN_IN_PLACE=$RUN_IN_PLACE" >> "$PRINT_FILE" if [ RUN_IN_PLACE = "yes" ]; then LINUXCNC_RIP_FLAG="True"; export LINUXCNC_RIP_FLAG fi LINUXCNCVERSION="@EMC2VERSION@"; export LINUXCNCVERSION # common from here.. INIVAR=inivar HALCMD="halcmd $DASHK" PICKCONFIG="@WISH@ $LINUXCNC_TCL_DIR/bin/pickconfig.tcl" LINUXCNC_EMCSH=@WISH@ { echo "LINUXCNC_DIR=$LINUXCNC_DIR" echo "LINUXCNC_BIN_DIR=$LINUXCNC_BIN_DIR" echo "LINUXCNC_TCL_DIR=$LINUXCNC_TCL_DIR" echo "LINUXCNC_SCRIPT_DIR=$LINUXCNC_SCRIPT_DIR" echo "LINUXCNC_RTLIB_DIR=$LINUXCNC_RTLIB_DIR" echo "LINUXCNC_CONFIG_DIR=$LINUXCNC_CONFIG_DIR" echo "LINUXCNC_LANG_DIR=$LINUXCNC_LANG_DIR" echo "INIVAR=$INIVAR" echo "HALCMD=$HALCMD" echo "LINUXCNC_EMCSH=$LINUXCNC_EMCSH" } >> "$PRINT_FILE" #export some common directories, used by some of the GUI's export LINUXCNC_TCL_DIR export LINUXCNC_EMCSH export LINUXCNC_HELP_DIR export LINUXCNC_LANG_DIR export REALTIME export HALCMD export LINUXCNC_NCFILES_DIR [ -z "$RUNTESTS" ] && echo "LINUXCNC - $LINUXCNCVERSION" # was an INI file specified on the command line? if [ -n "$USE_LAST_INIFILE" ]; then INIFILE=$($INIVAR -ini ~/.linuxcncrc -var LAST_CONFIG -sec PICKCONFIG 2>> "$DEBUG_FILE") echo "Using previous INI file: $INIFILE" >> "$PRINT_FILE" fi if [ -z "$INIFILE" ] ; then # nothing specified, get from the user # it returns either a path, or nothing at all INIFILE=$($PICKCONFIG) # if name is xxxx.demo, then: # execute xxxx.demo in background and exit if [ "${INIFILE%%.demo}".demo = "${INIFILE}" ] ; then "${INIFILE}" & exit 0 fi fi if [ -z "$INIFILE" ] ; then # still nothing specified, exit exit 0 fi function split_app_items () { app_name=$1 shift app_args=$* } function run_applications () { NUM=1 APPFILE=$($INIVAR -tildeexpand -ini "$INIFILE" -var APP -sec APPLICATIONS -num $NUM 2> /dev/null) if [ -z "$APPFILE" ] ; then return ; fi DEFAULT_APPLICATION_DELAY=0 GetFromIni DELAY APPLICATIONS DELAY=${retval:-$DEFAULT_APPLICATION_DELAY} while [ -n "$APPFILE" ] ; do # These may be unquoted, the ini-file entry must be quoted # shellcheck disable=SC2086 split_app_items $APPFILE # --> app_name app_args # try all explicit specifications before trying PATH case "$app_name" in /*) # absolute pathname exe_name=$app_name;; \./*) # name relative to INI file directory exe_name="$(pwd)/$app_name";; *) # try local first then PATH exe_name=$(pwd)/$app_name if [ ! -x "$exe_name" ] ; then exe_name=$(command -v "$app_name") fi esac if [ ! -f "$exe_name" ] ; then echo "APP: Cannot find executable file for: <$app_name>" else if [ ! -x "$exe_name" ] ; then echo "APP: File not executable: <$exe_name>" else echo "APP: $app_name found: <$exe_name>" # app_args may be unquoted, the ini-file entry must be quoted # shellcheck disable=SC2086 (sleep "$DELAY"; eval "$exe_name" $app_args) & fi fi NUM=$((NUM + 1)) APPFILE=$($INIVAR -tildeexpand -ini "$INIFILE" -var APP -sec APPLICATIONS -num $NUM 2> /dev/null) done } # Make sure the INI file exists before trying to parse it. Otherwise a # missing file would be reported as a file with errors. if [ ! -f "$INIFILE" ] ; then echo "Could not find INI file '$INIFILE'" trap '' EXIT exit 1 fi # Test whether we have a valid INI-file that can be parsed without error. # Running inivalue without section/variable filter should return the value of # the first variable in the first section of the file. If that fails, there is # a parse error in the file or it is empty. inivalue "$INIFILE" > /dev/null || { echo "E: The INI-file contains errors that need to be fixed."; exit 1; } # delete directories from path, save name only INI_NAME="${INIFILE##*/}" INI_DIR="${INIFILE%/*}" CONFIG_DIR="${INIFILE%/*}" export CONFIG_DIR export PATH=$CONFIG_DIR/bin:$PATH [ -z "$RUNTESTS" ] && echo "Machine configuration directory is '$INI_DIR'" echo "Machine configuration file is '$INI_NAME'" echo INIFILE="$INIFILE" >> "$PRINT_FILE" ################################################################################ # 2. extract info from the INI file that we will need later ################################################################################ retval= # 2.1. define helper function function GetFromIniQuiet { #$1 var name $2 - section name name=$1 retval=$($INIVAR -ini "$INIFILE" -var "$1" -sec "$2" 2> /dev/null) if [ -z "$1" ] ; then exit 1 fi echo "$name=$retval" >> "$PRINT_FILE" } function GetFromIni { #$1 var name $2 - section name name=$1 retval=$($INIVAR -ini "$INIFILE" -var "$1" -sec "$2" 2>> "$DEBUG_FILE") if [ -z "$1" ] ; then echo "Can't find variable $1 in section [$2] of file $INIFILE." exit 1 fi echo "$name=$retval" >> "$PRINT_FILE" } # Usage: # GetFromIniEx VAR1 SEC1 [VAR2 SEC2...VARn SECn] [default] function GetFromIniEx { original_var="[$2]$1" while [ $# -ge 2 ]; do if retval=$($INIVAR -ini "$INIFILE" -var "$1" -sec "$2" 2>/dev/null); then return; fi shift 2 done if [ $# -eq 0 ]; then echo "Can't find $original_var in $INIFILE." exit 1 fi retval="$1" } # 2.1.5 check version GetFromIni VERSION EMC if [ "$retval" != "1.1" ]; then if [ -z "$DISPLAY" ]; then echo "INI file [EMC]VERSION indicates update is needed, but the update GUI can't run without an X display" exit 1 fi update_ini -d "$INIFILE" exitval=$? case "$exitval" in 0) ;; 42) echo "update_ini cancelled by user" ; exit 0;; *) echo "update script failed in an unexpected way."; exit $exitval ;; esac fi linuxcnc_check_ini "$INIFILE" exitval=$? case "$exitval" in 0) ;; 1) echo "linuxcnc_check_ini validation failed"; exit $exitval ;; *) echo "linuxcnc_check_ini validation failed in an unexpected way."; exit $exitval ;; esac # 2.2. get param file GetFromIni PARAMETER_FILE RS274NGC #RS274NGC_PARAMFILE=$retval # 2.3. get mot information GetFromIniEx MOT MOT EMCMOT EMCMOT motmod EMCMOT=$retval$MODULE_EXT # add module extension # commandline -t option supersedes INI file setting if [ -z "$TPMOD" ] ; then GetFromIniQuiet TPMOD TRAJ TPMOD=${retval:-tpmod} #use default if not found fi # commandline -m option supersedes INI file setting if [ -z "$HOMEMOD" ] ; then GetFromIniQuiet HOMEMOD EMCMOT HOMEMOD=${retval:-homemod} #use default if not found fi # 2.5. get task information GetFromIni TASK TASK EMCTASK=$retval if [ "$EMCTASK" = emctask ]; then EMCTASK=linuxcnctask; fi # 2.6. we hardcode the server name, change if needed # linuxcncsvr now holds/creates all the NML channels, # so it needs to start by default, as the first process EMCSERVER=linuxcncsvr # 2.7. get halui information GetFromIniQuiet HALUI HAL HALUI=$retval # 2.7.1 get halbridge info from INI GetFromIniQuiet HALBRIDGE HAL HALBRIDGE=$retval # 2.8. get display information GetFromIni DISPLAY DISPLAY # Intentional split to make it values of an array. # Then split the array into the program and its args. # shellcheck disable=SC2206 __emcdisplay=( $retval ) EMCDISPLAY="${__emcdisplay[*]:0:1}" EMCDISPLAYARGS=( "${__emcdisplay[@]:1}" ) unset __emcdisplay case $EMCDISPLAY in tkemc) EMCDISPLAY=tklinuxcnc ;; esac # 2.9. get NML config information GetFromIniEx NML_FILE LINUXCNC NML_FILE EMC @DEFAULT_NMLFILE@ NMLFILE=$retval export NMLFILE # 2.10. INI information that may be needed by other apps in process tree GetFromIni COORDINATES TRAJ TRAJ_COORDINATES=$retval export TRAJ_COORDINATES GetFromIni KINEMATICS KINS KINS_KINEMATICS=$retval export KINS_KINEMATICS ################################################################################ # 3. Done gathering information, define a few functions # Execution resumes after function definitions... ################################################################################ KILL_TASK= KILL_TIMEOUT=20 ################################################################################ # 3.1. Kills a list of tasks with timeout # if it doesn't work, kill -9 is used ################################################################################ function KillTaskWithTimeout() { if [ -z "$KILL_PIDS" ] ; then KILL_PIDS=$($PIDOF $KILL_TASK) fi if [ -z "$KILL_PIDS" ] ; then echo "Could not find pid(s) for task $KILL_TASK" return 1 fi local NPROCS for KILL_PID in $KILL_PIDS ; do if "$PS" -o stat= -o comm= "$KILL_PID" | "$GREP" -q '^Z'; then echo "Skipping defunct task $KILL_TASK, PID=$KILL_PID" >> "$PRINT_FILE" continue fi # first a "gentle" kill with signal TERM "$KILL" "$KILL_PID" WAIT=$KILL_TIMEOUT # wait and see if it disappears while [ $WAIT -gt 1 ] ; do # see if it's still alive NPROCS=$("$PS" -o stat= -o comm= "$KILL_PID" | "$GREP" -v '^Z' | wc -l) if [ "$NPROCS" -gt 0 ]; then WAIT=$((WAIT - 1)) sleep .1 else WAIT=0 fi done if [ $WAIT -gt 0 ] ; then # gentle didn't work, get serious echo "Timeout, trying kill -9" >> "$PRINT_FILE" "$KILL" -9 "$KILL_PID" WAIT=$KILL_TIMEOUT # wait and see if it disappears while [ $WAIT -gt 1 ] ; do # see if it's still alive NPROCS=$("$PS" -o stat= -o comm= "$KILL_PID" | "$GREP" -v '^Z' | wc -l) if [ "$NPROCS" -gt 0 ]; then WAIT=$((WAIT - 1)) sleep .1 else WAIT=0 fi done fi if [ $WAIT -gt 0 ] ; then echo "Could not kill task $KILL_TASK, PID=$KILL_PID" fi done KILL_PIDS= KILL_TASK= } ################################################################################ # 3.2. define the cleanup function # # this cleanup function doesn't know or care what was actually # loaded - it simply kills _any_ processes in its list of # components ################################################################################ function Cleanup() { echo "Shutting down and cleaning up LinuxCNC..." # Kill displays first - that should cause an orderly # shutdown of the rest of linuxcnc for KILL_TASK in linuxcncpanel iosh linuxcncsh linuxcncrsh linuxcnctop mdi debuglevel gmoccapy gscreen; do if $PIDOF $KILL_TASK >> "$DEBUG_FILE" ; then KillTaskWithTimeout fi done if program_available axis-remote ; then if [ -n "$DISPLAY" ]; then axis-remote --ping && axis-remote --quit fi fi if [ "$1" = "other" ]; then echo -n "Waiting for other session to finish exiting..." WAIT=$KILL_TIMEOUT while [ $WAIT -gt 1 ]; do if [ ! -f "$LOCKFILE" ]; then echo " Ok" return 0 fi WAIT=$((WAIT - 1)) sleep .1 done echo "lockfile still not removed" fi SHUTDOWN=$($INIVAR -ini "$INIFILE" -var SHUTDOWN -sec HAL 2> /dev/null) if [ -n "$SHUTDOWN" ]; then echo "Running HAL shutdown script" $HALCMD -f "$SHUTDOWN" fi # now kill all the other user space components for KILL_TASK in linuxcncsvr milltask motion-logger; do if $PIDOF $KILL_TASK >> "$DEBUG_FILE" ; then KillTaskWithTimeout fi done echo "Stopping realtime threads" >> "$DEBUG_FILE" $HALCMD stop echo "Unloading hal components" >> "$DEBUG_FILE" $HALCMD unload all # Unused variable 'i', just a counter # shellcheck disable=SC2034 for i in $(seq 10); do # (the one component is the halcmd itself) if [ "$($HALCMD list comp | wc -w)" = 1 ]; then break; fi sleep .2 done echo "Removing HAL_LIB, RTAPI, and Real Time OS modules" >> "$PRINT_FILE" $REALTIME stop echo "Removing NML shared memory segments" >> "$PRINT_FILE" # Most field are unused, ignore warning # shellcheck disable=SC2034 while read -r b x t x x x x x x m x; do case "$b$t" in BSHMEM) ipcrm -M "$m" 2>/dev/null;; esac done < "$NMLFILE" # remove lock file if [ -f "$LOCKFILE" ] ; then rm "$LOCKFILE" fi } ################################################################################ # 4. done with function definitions, execution resumes here ################################################################################ # Name of lock file to check for that signifies that LinuxCNC is up, # to prevent multiple copies of controller LOCKFILE=/tmp/linuxcnc.lock # Check for lock file if [ -f "$LOCKFILE" ]; then if tty -s; then echo -n "LinuxCNC is still running. Restart it? [Y/n] " read -r input; [ -z "$input" ] && input=y elif [ -z "$DISPLAY" ]; then echo "No display, no tty, trying to clean up other instance automatically" input=y else input=$(@WISH@ < when run-in-place, or # /usr/local/share/linuxcnc/configs/ (wherever it was installed) cd "$INI_DIR" || { echo "E: Could not change directory to '$INI_DIR'"; exit 1; } # Create the lock file touch "$LOCKFILE" ################################################################################ # 4.1. pop up intro graphic ################################################################################ img=$($INIVAR -ini "$INIFILE" -var INTRO_GRAPHIC -sec DISPLAY 2>> "$DEBUG_FILE") imgtime=$($INIVAR -ini "$INIFILE" -var INTRO_TIME -sec DISPLAY 2>> "$DEBUG_FILE") if [ "$imgtime" = "" ] ; then imgtime=5 fi if [ "$img" != "" ] ; then if [ -e "$img" ]; then true elif [ -e "$INI_DIR/$img" ]; then img="$INI_DIR/$img" elif [ -e "$LINUXCNC_IMAGEDIR/$img" ]; then img="$LINUXCNC_IMAGEDIR/$img" else img= fi fi if [ "$img" != "" ] ; then if [ -x "$LINUXCNC_TCL_DIR/bin/popimage" ] ; then "$LINUXCNC_TCL_DIR/bin/popimage" "$img" "$imgtime" & fi fi ################################################################################ # 4.2. Now we can finally start loading LinuxCNC ################################################################################ # 4.3.1. Run linuxcncserver in background, always (it owns/creates the NML buffers) echo "Starting LinuxCNC server program: $EMCSERVER" >> "$PRINT_FILE" if ! program_available "$EMCSERVER"; then echo "Can't execute server program $EMCSERVER" Cleanup exit 1 fi INI_FILE_NAME="$INIFILE"; export INI_FILE_NAME $EMCSERVER -ini "$INIFILE" # 4.3.2. Start REALTIME echo "Loading Real Time OS, RTAPI, and HAL_LIB modules" >> "$PRINT_FILE" if ! $REALTIME start ; then echo "Realtime system did not load" Cleanup exit 1 fi # 4.3.3. export the location of the HAL realtime modules so that # "halcmd loadrt" can find them HAL_RTMOD_DIR=$LINUXCNC_RTLIB_DIR; export HAL_RTMOD_DIR echo "$(basename "$0") TPMOD=$TPMOD HOMEMOD=$HOMEMOD EMCMOT=${EMCMOT%.*}" # The HALCMD variable must be split because it may contain options. # shellcheck disable=SC2086 eval $HALCMD loadrt "$TPMOD" # shellcheck disable=SC2086 eval $HALCMD loadrt "$HOMEMOD" # 4.3.7. Run task in background echo "Starting TASK program: $EMCTASK" >> "$PRINT_FILE" if ! program_available "$EMCTASK" ; then echo "Can't execute TASK program $EMCTASK" Cleanup exit 1 fi halcmd loadusr -Wn inihal "$EMCTASK" -ini "$INIFILE" # 4.3.5. Run halui in background, if necessary if [ -n "$HALUI" ] ; then echo "Starting HAL User Interface program: $HALUI" >> "$PRINT_FILE" if ! program_available "$HALUI" ; then echo "Can't execute halui program $HALUI" Cleanup exit 1 fi $HALCMD loadusr -Wn halui "$HALUI" -ini "$INIFILE" fi # run hal bridge program IF requested if [ -n "$HALBRIDGE" ] ; then echo "Starting HAL User Interface program: $HALBRIDGE" >> "$PRINT_FILE" # HALBRIDGE must be word split # shellcheck disable=SC2086 $HALCMD loadusr -Wn bridge $HALBRIDGE fi # 4.3.6. execute HALCMD config files (if any) TWOPASS=$($INIVAR -ini "$INIFILE" -var TWOPASS -sec HAL -num 1 2> /dev/null) if [ -n "$TWOPASS" ] ; then # 4.3.6.1. if [HAL]TWOPASS is defined, handle all [HAL]HALFILE entries here: CFGFILE=@EMC2_TCL_LIB_DIR@/twopass.tcl export PRINT_FILE # twopass can append to PRINT_FILE if ! haltcl -i "$INIFILE" "$CFGFILE" && [ -z "$DASHK" ]; then Cleanup exit 1 fi else # 4.3.6.2. conventional execution of HALCMD config files # get first config file name from INI file NUM=1 CFGFILE=$($INIVAR -tildeexpand -ini "$INIFILE" -var HALFILE -sec HAL -num $NUM 2> /dev/null) while [ -n "$CFGFILE" ] ; do # IFS backslash will take care of read not mangling backslashes # shellcheck disable=SC2141,SC2162 IFS='\ ' read CFGFILE CFGFILE_ARGS <<< "$CFGFILE" # separate args foundmsg="" saveIFS=$IFS; IFS=: # colon (:) path separator for HALLIB_PATH explicit_file_in_hallib=${CFGFILE#LIB:} # strip leading 'LIB:' if [ -z "$explicit_file_in_hallib" ] ; then echo "ILLFORMED LIB:file:<$CFGFILE>" fi if [ "$explicit_file_in_hallib" != "$CFGFILE" ] ; then foundfile="$HALLIB_DIR/$explicit_file_in_hallib" if [ ! -r "$foundfile" ] ; then echo "CANNOT READ LIB:file:$foundfile" fi foundmsg="Found file(LIB): $foundfile" else if [ "${CFGFILE:0:1}" = "/" ] ; then foundfile=$CFGFILE ;# absolute path specified foundmsg="Found file(ABS): $foundfile" else for pathdir in $HALLIB_PATH ; do foundfile=$pathdir/$CFGFILE if [ -r "$foundfile" ] ; then # use first file found in HALLIB_PATH if [ "${pathdir:0:1}" = "." ] ; then foundmsg="Found file(REL): $foundfile" else foundmsg="Found file(lib): $foundfile" fi break fi done fi fi [ -d "$foundfile" ] && foundmsg="" IFS=$saveIFS if [ -z "$foundmsg" ] ; then echo "CANNOT FIND FILE FOR:$CFGFILE" Cleanup exit 1 fi echo "$foundmsg" CFGFILE="$foundfile" case $CFGFILE in *.tcl) # CFGFILE_ARGS must be word split # shellcheck disable=SC2086 if ! haltcl -i "$INIFILE" "$CFGFILE" $CFGFILE_ARGS \ && [ -z "$DASHK" ]; then Cleanup exit 1 fi ;; *) if ! $HALCMD -i "$INIFILE" -f "$CFGFILE" && [ -z "$DASHK" ]; then Cleanup exit 1 fi esac # get next config file name from INI file NUM=$((NUM + 1)) CFGFILE=$($INIVAR -tildeexpand -ini "$INIFILE" -var HALFILE -sec HAL -num $NUM 2> /dev/null) done fi # 4.3.8. execute discrete HAL commands from INI file (if any) # get first command from INI file NUM=1 HALCOMMAND=$($INIVAR -ini "$INIFILE" -var HALCMD -sec HAL -num $NUM 2> /dev/null) while [ -n "$HALCOMMAND" ] ; do if [ -n "$HALCOMMAND" ] ; then echo "Running HAL command: $HALCOMMAND" >> "$PRINT_FILE" # The HALCOMMAND must be word-split # shellcheck disable=SC2086 if ! $HALCMD $HALCOMMAND && [ -z "$DASHK" ]; then echo "INI file HAL command $HALCOMMAND failed." Cleanup exit 1 fi fi # get next command from INI file NUM=$((NUM + 1)) HALCOMMAND=$($INIVAR -ini "$INIFILE" -var HALCMD -sec HAL -num $NUM 2> /dev/null) done # 4.3.9. start the realtime stuff ticking $HALCMD start # 4.3.10. run other applications run_applications # wait for traj to process for up to 10s before screen loading do to race condition RACE_TIMEOUT=$((SECONDS + 10)) chk=$(halcmd getp ini.traj_max_velocity) while (( $("$AWK" 'BEGIN {print ('"$chk"' == 0)}') )); do if [ $SECONDS -ge $RACE_TIMEOUT ]; then echo "ini.traj_max_velocity still 0.0 after $SECONDS seconds" | tee -a "$PRINT_FILE" "$DEBUG_FILE" break fi chk=$(halcmd getp ini.traj_max_velocity) done # 4.3.11. Run display in foreground echo "Starting DISPLAY program: $EMCDISPLAY" >> "$PRINT_FILE" result=0 case $EMCDISPLAY in tklinuxcnc) # tklinuxcnc is in the tcl directory, not the bin directory if [ ! -x "$LINUXCNC_TCL_DIR/$EMCDISPLAY.tcl" ] ; then echo "Can't execute DISPLAY program $LINUXCNC_TCL_DIR/$EMCDISPLAY.tcl ${EMCDISPLAYARGS[*]}" Cleanup exit 1 fi "$LINUXCNC_TCL_DIR/$EMCDISPLAY.tcl" -ini "$INIFILE" "${EMCDISPLAYARGS[@]}" result=$? ;; dummy) # dummy display just waits for echo "DUMMY DISPLAY MODULE, press to continue." read -r ; ;; linuxcncrsh) if ! program_available "$EMCDISPLAY"; then echo "Can't execute DISPLAY program $EMCDISPLAY ${EMCDISPLAYARGS[*]} ${EXTRA_ARGS[*]}" Cleanup exit 1 fi $EMCDISPLAY "${EMCDISPLAYARGS[@]}" "${EXTRA_ARGS[@]}" -- -ini "$INIFILE" result=$? ;; *) # all other displays are assumed to be commands on the PATH if ! program_available "$EMCDISPLAY"; then echo "Can't execute DISPLAY program $EMCDISPLAY ${EMCDISPLAYARGS[*]} ${EXTRA_ARGS[*]}" Cleanup exit 1 fi $EMCDISPLAY -ini "$INIFILE" "${EMCDISPLAYARGS[@]}" "${EXTRA_ARGS[@]}" result=$? ;; esac # the display won't return until you shut it down, # so when you get here it's time to clean up Cleanup exit $result