Files
revpipyload/revpipyload/proginit.py
NaruX 7d0234a1b2 RTLevel default 0 (aus)
Workdirectory prüfen vor dem Wechseln
Optimierung -OO entfernt
_setuprt(...) um ksoftirqd und ktimersoftd auf höhere Prioritäten zu ziehen
Python PLC Programm kann max auf Prio RR 1 laufen
2017-09-20 18:12:44 +02:00

291 lines
8.3 KiB
Python

# -*- coding: utf-8 -*-
#
# RevPiPyLoad
#
# Webpage: https://revpimodio.org/revpipyplc/
# (c) Sven Sager, License: LGPLv3
#
"""Main functions of our program."""
import logging
import os
import sys
from argparse import ArgumentParser
from subprocess import Popen, PIPE
forked = False
globalconffile = None
logapp = "revpipyloadapp.log"
logplc = "revpipyload.log"
logger = None
pargs = None
picontrolreset = "/opt/KUNBUS/piControlReset"
rapcatalog = None
startdir = None
def _setuprt(pid, evt_exit):
"""Konfiguriert Programm fuer den RT-Scheduler.
@param pid PID, der angehoben werden soll
@return None"""
if logger is not None:
logger.debug("enter _setuprt()")
dict_change = {
"ksoftirqd/0,ksoftirqd/1,ksoftirqd/2,ksoftirqd/3": 10,
"ktimersoftd/0,ktimersoftd/1,ktimersoftd/2,ktimersoftd/3": 20
}
for ps_change in dict_change:
# pid und prio ermitteln
kpidps = Popen([
"/bin/ps", "-o", "pid=,rtprio=", "-C", ps_change
], bufsize=1, stdout=PIPE)
# Timeout nachbilden da in Python 3.2 nicht vorhanden
count = 10
while kpidps.poll() is None:
count -= 1
if count == 0:
kpidps.kill()
if logger is not None:
logger.error(
"ps timeout to get rt prio info - no rt active"
)
return None
evt_exit.wait(0.5)
if evt_exit.is_set():
return None
try:
kpiddat = kpidps.communicate()[0]
lst_kpids = kpiddat.split()
except:
kpidps.kill()
if logger is not None:
logger.error(
"can not get pid and prio - no rt active"
)
return None
while len(lst_kpids) > 0:
# Elemente paarweise übernehmen
kpid = lst_kpids.pop(0)
kprio = lst_kpids.pop(0)
# Daten prüfen
if not kpid.isdigit():
if logger is not None:
logger.error(
"pid={} and prio={} are not valid - no rt active"
"".format(kpid, kprio)
)
return None
kpid = int(kpid)
# RTPrio ermitteln
if kprio.isdigit():
kprio = int(kprio)
else:
kprio = 0
if kprio < 10:
# Profile anpassen
ec = os.system("/usr/bin/env chrt -fp {} {}".format(
dict_change[ps_change], kpid
))
if ec != 0:
if logger is not None:
logger.error(
"could not adjust scheduler - no rt active"
)
return None
# SCHED_RR für pid setzen
if logger is not None:
logger.info("set scheduler profile of pid {}".format(pid))
ec = os.system("/usr/bin/env chrt -p 1 {}".format(pid))
if ec != 0 and logger is not None:
logger.error(
"could not set scheduler profile of pid {}"
"".format(pid)
)
if logger is not None:
logger.debug("leave _setuprt()")
def _zeroprocimg():
"""Setzt Prozessabbild auf NULL."""
procimg = "/dev/piControl0" if pargs is None else pargs.procimg
if os.access(procimg, os.W_OK):
with open(procimg, "w+b", 0) as f:
f.write(bytes(4096))
else:
if logger is not None:
logger.error("zeroprocimg can not write to piControl device")
def cleanup():
"""Clean up program."""
# NOTE: Pidfile wirklich löschen?
if pargs is not None and pargs.daemon:
if os.path.exists("/var/run/revpipyload.pid"):
os.remove("/var/run/revpipyload.pid")
# Logging beenden
logging.shutdown()
# Dateihandler schließen
if pargs.daemon:
sys.stdout.close()
def configure():
"""Initialize general program functions."""
# Command arguments
parser = ArgumentParser(
description="RevolutionPi Python Loader"
)
parser.add_argument(
"-d", "--daemon", action="store_true", dest="daemon",
help="Run program as a daemon in background"
)
parser.add_argument(
"-c", "--conffile", dest="conffile",
default="revpipyload.conf",
help="Application configuration file"
)
parser.add_argument(
"-f", "--logfile", dest="logfile",
help="Save log entries to this file"
)
parser.add_argument(
"--procimg", dest="procimg",
default="/dev/piControl0",
help="Path to process image"
)
parser.add_argument(
"--pictory", dest="configrsc",
help="piCtory file to use"
)
parser.add_argument(
"-v", "--verbose", action="count", dest="verbose",
help="Switch on verbose logging"
)
global pargs
pargs = parser.parse_args()
# Prüfen ob als Daemon ausgeführt werden soll
global forked
pidfile = "/var/run/revpipyload.pid"
pid = 0
if pargs.daemon and not forked:
# Prüfen ob daemon schon läuft
if os.path.exists(pidfile):
raise SystemError(
"program already running as daemon. check {}".format(pidfile)
)
# Zum daemon machen
pid = os.fork()
if pid > 0:
with open(pidfile, "w") as f:
f.write(str(pid))
sys.exit(0)
else:
forked = True
# piCtory Konfiguration prüfen
if pargs.configrsc is None:
lst_rsc = ["/etc/revpi/config.rsc", "/opt/KUNBUS/config.rsc"]
for rscfile in lst_rsc:
if os.access(rscfile, os.F_OK | os.R_OK):
pargs.configrsc = rscfile
break
elif not os.access(pargs.configrsc, os.F_OK | os.R_OK):
pargs.configrsc = None
if pargs.configrsc is None:
raise RuntimeError(
"can not find known pictory configurations at {}"
"".format(", ".join(lst_rsc))
)
# piControlReset suchen
global picontrolreset
if not os.access(picontrolreset, os.F_OK | os.X_OK):
picontrolreset = "/usr/bin/piTest -x"
# rap Katalog an bekannten Stellen prüfen und laden
global rapcatalog
lst_rap = [
"/opt/KUNBUS/pictory/resources/data/rap",
"/var/www/pictory/resources/data/rap"
]
for rapfolder in lst_rap:
if os.path.isdir(rapfolder):
rapcatalog = os.listdir(rapfolder)
# Pfade absolut umschreiben
global startdir
if startdir is None:
startdir = os.path.abspath(".")
if pargs.conffile is not None and os.path.dirname(pargs.conffile) == "":
pargs.conffile = os.path.join(startdir, pargs.conffile)
if pargs.logfile is not None and os.path.dirname(pargs.logfile) == "":
pargs.logfile = os.path.join(startdir, pargs.logfile)
global logapp
global logplc
if pargs.daemon:
# Ausgage vor Umhängen schließen
sys.stdout.close()
# Ausgaben umhängen in Logfile
logapp = "/var/log/revpipyloadapp"
logplc = "/var/log/revpipyload"
pargs.conffile = "/etc/revpipyload/revpipyload.conf"
sys.stdout = open(logplc, "a")
sys.stderr = sys.stdout
elif pargs.logfile is not None:
logplc = pargs.logfile
# Initialize configparser globalconfig
global globalconffile
globalconffile = pargs.conffile
# Program logger
global logger
if logger is None:
logger = logging.getLogger()
# Alle handler entfernen
for lhandler in logger.handlers:
logger.removeHandler(lhandler)
# Neue Handler bauen
logformat = logging.Formatter(
"{asctime} [{levelname:8}] {message}",
datefmt="%Y-%m-%d %H:%M:%S", style="{"
)
lhandler = logging.StreamHandler(sys.stdout)
lhandler.setFormatter(logformat)
logger.addHandler(lhandler)
if pargs.logfile is not None:
lhandler = logging.FileHandler(filename=pargs.logfile)
lhandler.setFormatter(logformat)
logger.addHandler(lhandler)
# Loglevel auswerten
if pargs.verbose is None:
loglevel = logging.WARNING
elif pargs.verbose == 1:
loglevel = logging.INFO
elif pargs.verbose > 1:
loglevel = logging.DEBUG
logger.setLevel(loglevel)