123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 |
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- #
- # AWL simulator - LinuxCNC HAL module
- #
- # Copyright 2013-2016 Michael Buesch <m@bues.ch>
- #
- # This program is free software; you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation; either version 2 of the License, or
- # (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License along
- # with this program; if not, write to the Free Software Foundation, Inc.,
- # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- #
- from __future__ import division, absolute_import, print_function, unicode_literals
- import sys
- import os
- import time
- import getopt
- from awlsim_loader.common import *
- from awlsim_loader.coreserver import *
- from awlsim_loader.coreclient import *
- class LinuxCNC_NotRunning(Exception):
- pass
- # Check presence of LinuxCNC.
- # Returns normally, if LinuxCNC is detected.
- # Raises LinuxCNC_NotRunning, if LinuxCNC is not detected.
- def watchdogHook(_unused = None):
- # Check whether LinuxCNC is running.
- for lockname in ("/tmp/linuxcnc.lock", "/tmp/emc.lock"):
- if fileExists(lockname):
- return True
- if not opt_watchdog:
- # The check is disabled. Return success.
- return True
- printError("LinuxCNC doesn't seem to be running. "\
- "(Use '--watchdog off' to disable this check.)")
- raise LinuxCNC_NotRunning()
- # Create the LinuxCNC HAL pins
- def createHalPins(hal,
- inputBase, inputSize,
- outputBase, outputSize):
- HAL_BIT, HAL_U32, HAL_S32, HAL_FLOAT = \
- LinuxCNC_HAL.HAL_BIT, LinuxCNC_HAL.HAL_U32, \
- LinuxCNC_HAL.HAL_S32, LinuxCNC_HAL.HAL_FLOAT
- HAL_IN, HAL_OUT, HAL_RO, HAL_RW = \
- LinuxCNC_HAL.HAL_IN, LinuxCNC_HAL.HAL_OUT, \
- LinuxCNC_HAL.HAL_RO, LinuxCNC_HAL.HAL_RW
- printInfo("Mapped AWL/STL input area: P#E %d.0 BYTE %d" %\
- (inputBase, inputSize))
- printInfo("Mapped AWL/STL output area: P#A %d.0 BYTE %d" %\
- (outputBase, outputSize))
- # Create the input pins
- for i in range(inputBase, inputBase + inputSize):
- offset = i - inputBase
- for bit in range(8):
- hal.newpin("input.bit.%d.%d" % (i, bit), HAL_BIT, HAL_IN)
- hal.newparam("input.bit.%d.%d.active" % (i, bit), HAL_BIT, HAL_RW)
- hal.newpin("input.u8.%d" % i, HAL_U32, HAL_IN)
- hal.newparam("input.u8.%d.active" % i, HAL_BIT, HAL_RW)
- if i % 2:
- continue
- if inputSize - offset < 2:
- continue
- hal.newpin("input.u16.%d" % i, HAL_U32, HAL_IN)
- hal.newparam("input.u16.%d.active" % i, HAL_BIT, HAL_RW)
- hal.newpin("input.s16.%d" % i, HAL_S32, HAL_IN)
- hal.newparam("input.s16.%d.active" % i, HAL_BIT, HAL_RW)
- if inputSize - offset < 4:
- continue
- hal.newpin("input.u31.%d" % i, HAL_U32, HAL_IN)
- hal.newparam("input.u31.%d.active" % i, HAL_BIT, HAL_RW)
- hal.newpin("input.s32.%d" % i, HAL_S32, HAL_IN)
- hal.newparam("input.s32.%d.active" % i, HAL_BIT, HAL_RW)
- hal.newpin("input.float.%d" % i, HAL_FLOAT, HAL_IN)
- hal.newparam("input.float.%d.active" % i, HAL_BIT, HAL_RW)
- # Create the output pins
- for i in range(outputBase, outputBase + outputSize):
- offset = i - outputBase
- for bit in range(8):
- hal.newpin("output.bit.%d.%d" % (i, bit), HAL_BIT, HAL_OUT)
- hal.newparam("output.bit.%d.%d.active" % (i, bit), HAL_BIT, HAL_RW)
- hal.newpin("output.u8.%d" % i, HAL_U32, HAL_OUT)
- hal.newparam("output.u8.%d.active" % i, HAL_BIT, HAL_RW)
- if i % 2:
- continue
- if outputSize - offset < 2:
- continue
- hal.newpin("output.u16.%d" % i, HAL_U32, HAL_OUT)
- hal.newparam("output.u16.%d.active" % i, HAL_BIT, HAL_RW)
- hal.newpin("output.s16.%d" % i, HAL_S32, HAL_OUT)
- hal.newparam("output.s16.%d.active" % i, HAL_BIT, HAL_RW)
- if outputSize - offset < 4:
- continue
- hal.newpin("output.u31.%d" % i, HAL_U32, HAL_OUT)
- hal.newparam("output.u31.%d.active" % i, HAL_BIT, HAL_RW)
- hal.newpin("output.s32.%d" % i, HAL_S32, HAL_OUT)
- hal.newparam("output.s32.%d.active" % i, HAL_BIT, HAL_RW)
- hal.newpin("output.float.%d" % i, HAL_FLOAT, HAL_OUT)
- hal.newparam("output.float.%d.active" % i, HAL_BIT, HAL_RW)
- hal.newparam("config.ready", HAL_BIT, HAL_RW)
- def usage():
- print("awlsim-linuxcnc-hal version %s" % VERSION_STRING)
- print("")
- print("Usage: awlsim-linuxcnc-hal [OPTIONS] PROJECT.awlpro")
- print("")
- print("Options:")
- print(" -i|--input-size SIZE The input area size, in bytes.")
- print(" Overrides input-size from project file.")
- print(" -I|--input-base BASE The AWL/STL input address base.")
- print(" Overrides input-base from project file.")
- print(" -o|--output-size SIZE The output area size, in bytes.")
- print(" Overrides output-size from project file.")
- print(" -O|--output-base BASE The AWL/STL output address base.")
- print(" Overrides output-base from project file.")
- print("")
- print(" -l|--listen HOST:PORT Set the address and port where the")
- print(" awlsim core server should listen on.")
- print(" Defaults to %s:%d" %\
- (AwlSimServer.DEFAULT_HOST, AwlSimServer.DEFAULT_PORT))
- print("")
- print(" -L|--loglevel LVL Set the log level:")
- print(" 0: Log nothing")
- print(" 1: Log errors")
- print(" 2: Log errors and warnings")
- print(" 3: Log errors, warnings and info messages (default)")
- print(" 4: Verbose logging")
- print(" 5: Extremely verbose logging")
- print(" -N|--nice NICE Renice the process. -20 <= NICE <= 19.")
- print(" Default: Do not renice")
- print("")
- print("Debugging options:")
- print(" -W|--watchdog 1/0 Enable/disable LinuxCNC runtime watchdog.")
- print(" Default: on")
- print(" -M|--max-runtime SEC Module will be stopped after SEC seconds.")
- print(" Default: No timeout")
- print(" -x|--extended-insns Force-enable extended instructions")
- print("")
- print("For an example LinuxCNC HAL configuration see:")
- print(" examples/linuxcnc-demo/linuxcnc-demo.hal")
- def main():
- global LinuxCNC_HAL
- global opt_inputSize
- global opt_inputBase
- global opt_outputSize
- global opt_outputBase
- global opt_listen
- global opt_loglevel
- global opt_nice
- global opt_watchdog
- global opt_maxRuntime
- global opt_extInsns
- opt_inputSize = None
- opt_inputBase = None
- opt_outputSize = None
- opt_outputBase = None
- opt_listen = (AwlSimServer.DEFAULT_HOST, AwlSimServer.DEFAULT_PORT)
- opt_loglevel = Logging.LOG_INFO
- opt_nice = None
- opt_watchdog = True
- opt_maxRuntime = None
- opt_extInsns = None
- try:
- (opts, args) = getopt.getopt(sys.argv[1:],
- "hi:I:o:O:l:L:N:W:M:x",
- [ "help", "input-size=", "input-base=",
- "output-size=", "output-base=",
- "listen=",
- "loglevel=", "nice=",
- "watchdog=", "max-runtime=", "extended-insns", ])
- except getopt.GetoptError as e:
- printError(str(e))
- usage()
- return 1
- for (o, v) in opts:
- if o in ("-h", "--help"):
- usage()
- return 0
- if o in ("-i", "--input-size"):
- try:
- opt_inputSize = int(v)
- except ValueError:
- printError("-i|--input-size: Invalid argument")
- return 1
- if o in ("-I", "--input-base"):
- try:
- opt_inputBase = int(v)
- except ValueError:
- printError("-I|--input-base: Invalid argument")
- return 1
- if o in ("-o", "--output-size"):
- try:
- opt_outputSize = int(v)
- except ValueError:
- printError("-o|--output-size: Invalid argument")
- return 1
- if o in ("-O", "--output-base"):
- try:
- opt_outputBase = int(v)
- except ValueError:
- printError("-O|--output-base: Invalid argument")
- return 1
- if o in ("-l", "--listen"):
- try:
- host, port = parseNetAddress(v)
- if not host.strip() or\
- host in {"any", "all"}:
- host = ""
- if port is None:
- port = AwlSimServer.DEFAULT_PORT
- opt_listen = (host, port)
- except AwlSimError as e:
- printError("-l|--listen: Invalid host/port")
- return 1
- if o in ("-L", "--loglevel"):
- try:
- opt_loglevel = int(v)
- except ValueError:
- printError("-L|--loglevel: Invalid log level")
- return 1
- if o in ("-N", "--nice"):
- try:
- opt_nice = int(v)
- if opt_nice < -20 or opt_nice > 19:
- raise ValueError
- except ValueError:
- printError("-N|--nice: Invalid niceness level")
- return 1
- if o in ("-W", "--watchdog"):
- opt_watchdog = str2bool(v)
- if o in ("-M", "--max-runtime"):
- try:
- opt_maxRuntime = float(v)
- except ValueError:
- printError("-M|--max-runtime: Invalid time format")
- return 1
- if o in ("-x", "--extended-insns"):
- opt_extInsns = True
- if len(args) != 1:
- usage()
- return 1
- projectFile = args[0]
- result = ExitCodes.EXIT_OK
- server = None
- try:
- Logging.setPrefix("awlsim-linuxcnc: ")
- Logging.setLoglevel(opt_loglevel)
- # Adjust process priority
- if opt_nice is not None:
- try:
- os.nice(opt_nice)
- except OSError as e:
- printError("Failed to renice process to "
- "%d: %s" % (opt_nice, str(e)))
- return 1
- # Try to import the LinuxCNC HAL module
- try:
- import hal as LinuxCNC_HAL
- except ImportError as e:
- printError("Failed to import LinuxCNC HAL "
- "module: %s" % str(e))
- return 1
- # Create the LinuxCNC HAL component.
- hal = LinuxCNC_HAL.component("awlsim")
- # Read the project.
- project = Project.fromProjectOrRawAwlFile(projectFile)
- hwmodSettings = project.getHwmodSettings()
- # Get the 'linuxcnc' hardware module descriptor.
- linuxcncHwmodDesc = None
- for modDesc in hwmodSettings.getLoadedModules():
- if modDesc.getModuleName() == "linuxcnc":
- if linuxcncHwmodDesc:
- printError("ERROR: More than one 'linuxcnc' hardware "
- "module found in the project file. Only "
- "one 'linuxcnc' module is supported.")
- linuxcncHwmodDesc = modDesc
- if not linuxcncHwmodDesc:
- if opt_inputBase is None and\
- opt_outputBase is None and\
- opt_inputSize is None and\
- opt_outputSize is None:
- printWarning("Warning: Hardware module 'linuxcnc' not "
- "included in project file. "
- "Loading module nevertheless.")
- modDesc = HwmodDescriptor(moduleName = "linuxcnc",
- parameters = {
- "hal" : None,
- "removeOnReset" : "0",
- "inputAddressBase" : "0",
- "outputAddressBase" : "0",
- "inputSize" : "32",
- "outputSize" : "32",
- })
- hwmodSettings.addLoadedModule(modDesc)
- linuxcncHwmodDesc = modDesc
- # Override hardware module parameters, if required.
- linuxcncHwmodDesc.setParameterValue("hal", hal)
- linuxcncHwmodDesc.setParameterValue("removeOnReset", "0")
- if opt_inputBase is not None:
- linuxcncHwmodDesc.setParameterValue("inputAddressBase",
- str(opt_inputBase))
- if opt_inputSize is not None:
- linuxcncHwmodDesc.setParameterValue("inputSize",
- str(opt_inputSize))
- if opt_outputBase is not None:
- linuxcncHwmodDesc.setParameterValue("outputAddressBase",
- str(opt_outputBase))
- if opt_outputSize is not None:
- linuxcncHwmodDesc.setParameterValue("outputSize",
- str(opt_outputSize))
- # Create the HAL pins, as requested.
- try:
- modDesc = linuxcncHwmodDesc
- inputBase = int(modDesc.getParameter("inputAddressBase"))
- inputSize = int(modDesc.getParameter("inputSize"))
- outputBase = int(modDesc.getParameter("outputAddressBase"))
- outputSize = int(modDesc.getParameter("outputSize"))
- except ValueError:
- printError("ERROR: 'linuxcnc' module hardware parameter "
- "value error.")
- return 1
- createHalPins(hal = hal,
- inputBase = inputBase,
- inputSize = inputSize,
- outputBase = outputBase,
- outputSize = outputSize)
- # Configure the core and the core server.
- server = AwlSimServer()
- for modDesc in hwmodSettings.getLoadedModules():
- server.loadHardwareModule(modDesc)
- server.cpuSetSpecs(project.getCpuSpecs())
- conf = copy(project.getCpuConf())
- if opt_maxRuntime is not None:
- conf.setRunTimeLimitUs(int(round(opt_maxRuntime * 1000000.0)))
- if opt_extInsns is not None:
- conf.setExtInsnsEn(opt_extInsns)
- server.cpuSetConf(conf)
- # Load the program
- for symSrc in project.getSymTabSources():
- server.loadSymTabSource(symSrc)
- for libSel in project.getLibSelections():
- server.loadLibraryBlock(libSel)
- for awlSrc in project.getAwlSources():
- server.loadAwlSource(awlSrc)
- for fupSrc in project.getFupSources():
- server.loadFupSource(fupSrc)
- for kopSrc in project.getKopSources():
- server.loadKopSource(kopSrc)
- # Set the Linux-CNC watchdog as cycle exit hook.
- server.setCycleExitHook(watchdogHook)
- # Run the server
- printInfo("Starting interpreter core...")
- server.startup(host = opt_listen[0],
- port = opt_listen[1],
- handleExceptionServerside = True,
- handleMaintenanceServerside = True)
- server.setRunState(server.STATE_RUN)
- lastExceptionTime = None
- while True:
- try:
- server.run()
- except (AwlParserError, AwlSimError) as e:
- now = time.time()
- if lastExceptionTime is not None and\
- now - lastExceptionTime < 1.0:
- # The last fault is less than one second
- # in the past. Raise a fatal exception.
- printError("Fatal fault detected")
- raise e
- else:
- lastExceptionTime = now
- # Non-fatal fault.
- # Ensure the CPU is stopped and enter
- # the run loop again.
- printError(e.getReport())
- printError("CPU stopped due to fault")
- server.setRunState(server.STATE_STOP)
- continue
- # Run loop exited normally. Bail out.
- break
- except LinuxCNC_NotRunning as e:
- result = ExitCodes.EXIT_ERR_OTHER
- except KeyboardInterrupt as e:
- result = ExitCodes.EXIT_ERR_OTHER
- except (AwlParserError, AwlSimError) as e:
- printError(e.getReport())
- result = ExitCodes.EXIT_ERR_SIM
- except MaintenanceRequest as e:
- if e.requestType in (MaintenanceRequest.TYPE_SHUTDOWN,
- MaintenanceRequest.TYPE_STOP,
- MaintenanceRequest.TYPE_RTTIMEOUT):
- result = ExitCodes.EXIT_OK
- else:
- printError("Received invalid maintenance request %d" %\
- e.requestType)
- result = ExitCodes.EXIT_ERR_SIM
- finally:
- if server:
- server.shutdown()
- printInfo("LinuxCNC HAL module shutdown.")
- return result
- if __name__ == "__main__":
- sys.exit(main())
|