...
 
Commits (2)
......@@ -152,6 +152,44 @@ The hardware modules are the glue between the Awlsim core and the real world. Th
Awlsim supports programming in an S7-FUP like language. See [the FUP documentation](doc/fup/FUP.html) for more information about Awlsim's implementation of FUP.
## Environment variables
The following environment variables control Awlsim's basic behavior:
* `AWLSIM_GUI`<br />
`=auto` Automatically select the best GUI framework (default)<br />
`=pyside` Use PySide as GUI framework.<br />
`=pyqt` Use PyQt as GUI framework.<br />
* `AWLSIM_CYTHON`<br />
`=0` Do not attempt to use Cython core (default)<br />
`=1` Attempt to use Cython core, but fall back to Python<br />
`=2` Enforce Cython core<br />
* `AWLSIM_SCHED`<br />
`=default` Do not change the scheduling policy. Keep the policy that was assigned to Awlsim by the operating system. (default)<br />
`=normal` Use the normal non-realtime OS scheduling.<br />
`=fifo` Use FIFO realtime scheduling (`SCHED_FIFO`).<br />
`=rr` Use Round-robin realtime scheduling (`SCHED_RR`).<br />
`=deadline` Use Deadline realtime scheduling (`SCHED_DEADLINE`).<br />
`=realtime` Use a realtime scheduling algorithm that performs best in most situations. The actual algorithm selection might change between Awlsim releases.<br />
* `AWLSIM_PRIO`<br />
`=default` Do not change the priority (default).<br />
`=1-99` Set the scheduling priority. The meaning of the priority depends on the operating system and the selected scheduling algorithm. See `AWLSIM_SCHED`.<br />
* `AWLSIM_AFFINITY`<br />
`=0,2,...` Comma separated list of host CPU cores to run on. Default: all cores.<br />
* `AWLSIM_PROFILE`<br />
`=0` Disable profiling (default)<br />
`=1` Enable core cycle profiling<br />
`=2` Enable full core profiling (including startup)<br />
* `AWLSIM_COVERAGE`<br />
`=DATAFILE` Enable code coverage tracing.<br />
## Building Awlsim
Awlsim can be run from the source directory in interpreted Python mode without building it. Just `cd` into the Awlsim source directory and execute the desired main executable (e.g. `./awlsim-gui` or `./awlsim-server` etc...).
......
......@@ -15,7 +15,7 @@ Nice=-15
ExecStart=@PYTHON@ @PREFIX@/bin/awlsim-server -l localhost -4 -w @PROJECT@
ExecStartPost=-@PYTHON@ @PREFIX@/bin/awlsim-client -C localhost -r RUN
Environment=PYTHONPATH=@PYTHON_SITE@ PYTHONHASHSEED=0 PYTHONOPTIMIZE=2 PYTHONDONTWRITEBYTECODE=1 AWLSIM_CYTHON=1 AWLSIM_AFFINITY=-1
Environment=PYTHONPATH=@PYTHON_SITE@ PYTHONHASHSEED=0 PYTHONOPTIMIZE=2 PYTHONDONTWRITEBYTECODE=1 AWLSIM_CYTHON=1 AWLSIM_SCHED=realtime AWLSIM_PRIO=50 AWLSIM_AFFINITY=-1
[Install]
WantedBy=multi-user.target
......@@ -2,7 +2,7 @@
#
# AWL simulator - Environment variables
#
# Copyright 2017 Michael Buesch <m@bues.ch>
# Copyright 2017-2018 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
......@@ -103,3 +103,41 @@ class AwlSimEnv(object):
except ValueError as e:
affinity = []
return affinity
SCHED_DEFAULT = "default" # Do not change the scheduling policy
SCHED_NORMAL = "normal" # Use non-realtime scheduling policy
SCHED_FIFO = "fifo" # Use SCHED_FIFO realtime scheduling policy
SCHED_RR = "rr" # Use SCHED_RR realtime scheduling policy
SCHED_DEADLINE = "deadline" # Use SCHED_DEADLINE realtime scheduling policy
@classmethod
def getSched(cls):
"""Get AWLSIM_SCHED.
Returns one of the SCHED_... constants.
Returns None, if AWLSIM_SCHED has an invalid value.
"""
schedStr = cls.__getVar("SCHED", "").lower().strip()
if schedStr == cls.SCHED_DEFAULT:
return cls.SCHED_DEFAULT
if schedStr == cls.SCHED_NORMAL or schedStr == "other":
return cls.SCHED_NORMAL
if schedStr == cls.SCHED_FIFO or schedStr == "realtime":
return cls.SCHED_FIFO
if schedStr == cls.SCHED_RR:
return cls.SCHED_RR
if schedStr == cls.SCHED_DEADLINE:
return cls.SCHED_DEADLINE
return None
@classmethod
def getPrio(cls):
"""Get AWLSIM_PRIO.
Returns the scheduling priority as an integer or None.
"""
prioStr = cls.__getVar("PRIO", "").lower().strip()
if prioStr != "default":
try:
return int(prioStr)
except ValueError as e:
pass
return None
......@@ -42,18 +42,25 @@ def findExecutable(executable):
return distutils.spawn.find_executable(executable)
class PopenWrapper(object):
def __init__(self, argv, env, stdio = False):
def __init__(self, argv, env, stdio=False, hideWindow=False):
if isIronPython:
self.__pid = os.spawnve(os.P_NOWAIT,
argv[0], argv, dict(env))
else:
if osIsWindows and hideWindow:
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags = subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE
else:
startupinfo = None
self.__proc = subprocess.Popen(
argv,
env = dict(env),
shell = False,
stdin = subprocess.PIPE if stdio else None,
stdout = subprocess.PIPE if stdio else None,
stderr = subprocess.PIPE if stdio else None
env=dict(env),
shell=False,
stdin=subprocess.PIPE if stdio else None,
stdout=subprocess.PIPE if stdio else None,
stderr=subprocess.PIPE if stdio else None,
startupinfo=startupinfo
)
def terminate(self):
......
......@@ -3,10 +3,14 @@ from awlsim.core.main cimport *
#from posix.select cimport fd_set #@cy-posix
#cdef extern from "<sched.h>": #@cy-posix
# int sched_yield() nogil #@cy-posix
cdef class AwlSimServer(object):
cdef public AwlSim __sim
cdef public _Bool __affinityEnabled
cdef public _Bool __rtSchedEnabled
cdef public object __os_sched_yield
cdef public list __emptyList
cdef public _Bool __startupDone
cdef public int32_t __state
......@@ -44,3 +48,5 @@ cdef class AwlSimServer(object):
cdef __handleClientComm(self, client)
cdef __handleSocketComm(self, list sockList)
# cdef __handleCommunicationPosix(self) #@cy-posix
cdef void __yieldHostCPU(self)
......@@ -177,7 +177,8 @@ class AwlSimServer(object): #+cdef
forkServerProcess)
try:
serverProcess = PopenWrapper([proc],
env = env)
env=env,
hideWindow=True)
except OSError as e:
raise AwlSimError("Failed to run executable '%s': %s" %(
forkServerProcess, str(e)))
......@@ -192,7 +193,8 @@ class AwlSimServer(object): #+cdef
try:
serverProcess = PopenWrapper(
[interp, "-m", "awlsim.coreserver.run"],
env = env)
env=env,
hideWindow=True)
except OSError as e:
raise AwlSimError("Failed to run interpreter '%s': %s" %(
forkInterpreter, str(e)))
......@@ -228,6 +230,8 @@ class AwlSimServer(object): #+cdef
def __init__(self):
self.__affinityEnabled = False
self.__rtSchedEnabled = False
self.__os_sched_yield = getattr(os, "sched_yield", None)
self.__emptyList = []
self.__startupDone = False
self.__state = -1
......@@ -333,15 +337,103 @@ class AwlSimServer(object): #+cdef
if hasattr(os, "sched_setaffinity"):
try:
os.sched_setaffinity(0, affinity)
printVerbose("Set host CPU scheduling "
"affinity to %s." % str(affinity))
except (OSError, ValueError) as e: #@nocov
raise AwlSimError("Failed to set host CPU "
"affinity to %s: %s" % (
affinity, str(e)))
else: #@nocov
printError("Cannot set CPU affinity "
"on this version of Python. "
printError("Cannot set CPU affinity. "
"os.sched_setaffinity is not available.")
def __setSched(self, allowRtPolicy=True):
"""Set the scheduling policy and priority to what is set by
AWLSIM_SCHED and AWLSIM_PRIO environment variable, if enable==True.
"""
self.__rtSchedEnabled = False
sched = AwlSimEnv.getSched()
if (sched is not None and
sched != AwlSimEnv.SCHED_DEFAULT):
if not allowRtPolicy:
sched = AwlSimEnv.SCHED_NORMAL
policy = None
if sched == AwlSimEnv.SCHED_NORMAL:
policy = getattr(os, "SCHED_OTHER", None)
isRealtime = False
elif sched == AwlSimEnv.SCHED_FIFO:
policy = getattr(os, "SCHED_FIFO", None)
isRealtime = True
elif sched == AwlSimEnv.SCHED_RR:
policy = getattr(os, "SCHED_RR", None)
isRealtime = True
elif sched == AwlSimEnv.SCHED_DEADLINE:
policy = getattr(os, "SCHED_DEADLINE", None)
isRealtime = True
policy = None #TODO we also need to set the deadline scheduling parameters.
if policy is None: #@nocov
raise AwlSimError("Host CPU scheduling policy "
"'%s' is not supported by the system. "
"Please change the AWLSIM_SCHED "
"environment variable." % sched)
if hasattr(os, "sched_setscheduler"):
try:
minPrio = getattr(os, "sched_get_priority_min",
lambda p: 1)(policy)
param = os.sched_param(minPrio)
os.sched_setscheduler(0, policy, param)
self.__rtSchedEnabled = isRealtime
printVerbose("Set host CPU scheduling "
"policy to '%s'." % sched)
except (OSError, ValueError) as e: #@nocov
raise AwlSimError("Failed to set host CPU "
"scheduling policy to %s: %s" % (
sched, str(e)))
else: #@nocov
printError("Cannot set CPU scheduling policy. "
"os.sched_setscheduler is not available.")
prio = AwlSimEnv.getPrio()
if prio is not None and allowRtPolicy:
if (hasattr(os, "sched_getscheduler") and
hasattr(os, "sched_setparam")):
try:
policy = os.sched_getscheduler(0)
minPrio = getattr(os, "sched_get_priority_min",
lambda p: 1)(policy)
maxPrio = getattr(os, "sched_get_priority_max",
lambda p: 99)(policy)
prio = clamp(prio, minPrio, maxPrio)
param = os.sched_param(prio)
os.sched_setparam(0, param)
printVerbose("Set host CPU scheduling "
"priority to %d." % prio)
except (OSError, ValueError) as e: #@nocov
raise AwlSimError("Failed to set host CPU "
"scheduling priority to %d: %s" % (
prio, str(e)))
else: #@nocov
printError("Cannot set CPU scheduling priority. "
"os.sched_setparam/os.sched_getscheduler "
"is not available.")
def __yieldHostCPU(self): #@nocy
#@cy cdef void __yieldHostCPU(self):
if not self.__rtSchedEnabled:
return
# On Posix + Cython call the system sched_yield directly.
# with nogil: #@cy-posix
# sched_yield() #@cy-posix
# return #@cy-posix
# Otherwise try to call sched_yield from the os module,
# if it is available.
if self.__os_sched_yield: #@nocy
self.__os_sched_yield() #@nocy
return #@nocy
def getRunState(self):
return self.__state
......@@ -361,10 +453,12 @@ class AwlSimServer(object): #+cdef
if runstate == self._STATE_INIT:
# We just entered initialization state.
printVerbose("Putting CPU into INIT state.")
self.__setSched(False)
self.__setAffinity(False)
self.__needOB10x = True
elif runstate == self.STATE_RUN:
# We just entered RUN state.
self.__setSched(True)
self.__setAffinity(False)
if self.__needOB10x:
printVerbose("CPU startup (OB 10x).")
......@@ -375,13 +469,16 @@ class AwlSimServer(object): #+cdef
elif runstate == self.STATE_STOP:
# We just entered STOP state.
printVerbose("Putting CPU into STOP state.")
self.__setSched(False)
self.__setAffinity(False)
elif runstate == self.STATE_MAINTENANCE:
# We just entered MAINTENANCE state.
printVerbose("Putting CPU into MAINTENANCE state.")
self.__setSched(False)
self.__setAffinity(False)
self.__needOB10x = True
else:
self.__setSched(False)
self.__setAffinity(False)
self.__state = runstate
......@@ -1262,6 +1359,7 @@ class AwlSimServer(object): #+cdef
self.__handleMemReadReqs()
self.__handleCommunication() #@cy-win
# self.__handleCommunicationPosix() #@cy-posix
self.__yieldHostCPU()
continue
except (AwlSimError, AwlParserError) as e:
......