241 lines
7.1 KiB
Python
241 lines
7.1 KiB
Python
import hal
|
|
import _hal
|
|
import traceback
|
|
from qtpy.QtCore import QObject, QTimer, Signal
|
|
|
|
# Set up logging
|
|
from common import logger
|
|
log = logger.getLogger(__name__)
|
|
# log.setLevel(logger.INFO) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL, VERBOSE
|
|
|
|
class QPin(hal.Pin, QObject):
|
|
|
|
value_changed = Signal('PyQt_PyObject')# depreciated
|
|
pinValueChanged = Signal('PyQt_PyObject','PyQt_PyObject')
|
|
isDrivenChanged = Signal('PyQt_PyObject','PyQt_PyObject')
|
|
REGISTRY = []
|
|
UPDATE = False
|
|
|
|
def __init__(self, *a, **kw):
|
|
super(QPin, self).__init__(*a, **kw)
|
|
QObject.__init__(self, None)
|
|
self.hal = hal
|
|
self._item_wrap(self._item)
|
|
self._prev = None
|
|
self._prevDriven = None
|
|
self.REGISTRY.append(self)
|
|
self.prefix = None
|
|
|
|
def update(self):
|
|
tmp = self.get()
|
|
if tmp != self._prev:
|
|
self.value_changed.emit(tmp)
|
|
self.pinValueChanged.emit(self, tmp)
|
|
self._prev = tmp
|
|
|
|
def isDriven(self):
|
|
return hal.pin_has_writer('{}.{}'.format(self.prefix, self.get_name()))
|
|
|
|
def updateDriven(self):
|
|
tmp = self.isDriven()
|
|
if tmp != self._prevDriven:
|
|
self.isDrivenChanged.emit(self, tmp)
|
|
self._prevDriven = tmp
|
|
|
|
def text(self):
|
|
return self.get_name()
|
|
|
|
# always returns False because
|
|
# there was no error when making pin
|
|
# see class DUMMY
|
|
def error(self):
|
|
return False
|
|
|
|
@classmethod
|
|
def update_all(cls):
|
|
if not cls.UPDATE:
|
|
return
|
|
kill = []
|
|
for p in cls.REGISTRY:
|
|
try:
|
|
p.update()
|
|
except Exception as e:
|
|
kill.append(p)
|
|
log.error("Error updating pin {}; Removing".format(p))
|
|
log.exception(e)
|
|
if p.receivers(p.isDrivenChanged) > 0:
|
|
p.updateDriven()
|
|
|
|
for p in kill:
|
|
cls.REGISTRY.remove(p)
|
|
return cls.UPDATE
|
|
|
|
@classmethod
|
|
def update_start(cls, cycletime=None):
|
|
if QPin.UPDATE:
|
|
return
|
|
QPin.UPDATE = True
|
|
cls.timer = QTimer()
|
|
cls.timer.timeout.connect(cls.update_all)
|
|
cls.timer.start(cycletime)
|
|
|
|
@classmethod
|
|
def update_stop(cls):
|
|
QPin.UPDATE = False
|
|
|
|
|
|
# so errors when making QPins aren't fatal
|
|
class DummyPin(QObject):
|
|
value_changed = Signal('PyQt_PyObject')
|
|
|
|
def __init__(self, *a, **kw):
|
|
super(DummyPin, self).__init__(None)
|
|
self._a = a
|
|
self._kw = kw
|
|
|
|
# always returns True because
|
|
# there was an error when making HAL pin
|
|
# see class QPin
|
|
def error(self):
|
|
return True
|
|
|
|
def getError(self):
|
|
print('{}'.format(self._kw.get('ERROR')))
|
|
|
|
# send a safe value
|
|
def get(self):
|
|
return 0
|
|
|
|
# accept but don't set anything
|
|
def set(self, *a, **kw):
|
|
pass
|
|
|
|
def get_name(self):
|
|
return self._a[0]
|
|
|
|
class _QHal(object):
|
|
HAL_BIT = hal.HAL_BIT
|
|
HAL_FLOAT = hal.HAL_FLOAT
|
|
HAL_S32 = hal.HAL_S32
|
|
HAL_U32 = hal.HAL_U32
|
|
|
|
HAL_IN = hal.HAL_IN
|
|
HAL_OUT = hal.HAL_OUT
|
|
HAL_IO = hal.HAL_IO
|
|
HAL_RO = hal.HAL_RO
|
|
HAL_RW = hal.HAL_RW
|
|
|
|
def __new__(cls, *a, **kw):
|
|
instance = super(_QHal, cls).__new__(cls)
|
|
instance.__init__(*a, **kw)
|
|
return instance
|
|
|
|
def __init__(self, comp=None, hal=None):
|
|
# only initialize once for all instances
|
|
if not self.__class__._instance is None:
|
|
return
|
|
|
|
if isinstance(comp, _QHal):
|
|
comp = comp.comp
|
|
self.comp = comp
|
|
self.hal = hal
|
|
|
|
def setUpdateRate(self, cyclerate):
|
|
QPin.update_start(cyclerate)
|
|
|
|
def newpin(self, *a, **kw):
|
|
return self.newPin(*a,**kw)
|
|
def newPin(self, *a, **kw):
|
|
try:
|
|
p = QPin(_hal.component.newpin(self.comp, *a, **kw))
|
|
except ValueError as e:
|
|
# if pin is already made, find a new name
|
|
if 'Duplicate pin name' in '{}'.format(e):
|
|
try:
|
|
# tuples are immutable, convert to list
|
|
y = list(a)
|
|
y[0] = self.makeUniqueName(y[0])
|
|
a = tuple(y)
|
|
# this late in the game, component is probably already 'ready'
|
|
if self.hal.component_is_ready(self.comp.getprefix()):
|
|
self.comp.unready()
|
|
p = QPin(_hal.component.newpin(self.comp, *a, **kw))
|
|
self.comp.ready()
|
|
else:
|
|
p = QPin(_hal.component.newpin(self.comp, *a, **kw))
|
|
except Exception as e:
|
|
raise
|
|
except Exception as e:
|
|
if log.getEffectiveLevel() == logger.VERBOSE:
|
|
raise
|
|
t = inspect.getframeinfo(inspect.currentframe().f_back)
|
|
log.error("QHal: Error making new HAL pin: {}\n {}\n Line {}\n Function: {}".
|
|
format(e, t[0], t[1], t[2]))
|
|
log.error("QHal: {}".format(traceback.format_exc()))
|
|
p = DummyPin(*a, ERROR=e)
|
|
p.prefix = self.comp.getprefix()
|
|
return p
|
|
|
|
# accept either, first is depreciated
|
|
def getpin(self, *a, **kw):
|
|
return self.getPinObject(*a, **kw)
|
|
def getPinObject(self, *a, **kw):
|
|
return QPin(_hal.component.getpin(self.comp, *a, **kw))
|
|
|
|
# accept either, first is depreciated
|
|
def getvalue(self, name):
|
|
return self.getValue(name)
|
|
def getValue(self, name):
|
|
try:
|
|
return hal.get_value(name)
|
|
except Exception as e:
|
|
raise("QHal: Error getting value of {}\n {}".format(name, e))
|
|
|
|
# accept either, first is depreciated
|
|
def setp(self,name, value):
|
|
return self.setPin(name, value)
|
|
def setPin(self,name, value):
|
|
try:
|
|
return hal.set_p(name,value)
|
|
except Exception as e:
|
|
raise("QHal: Error setting pin {} to {}\n {}".format(name,value, e))
|
|
|
|
# accept either, first is depreciated
|
|
def sets(self,name, value):
|
|
return self.setSignal(name, value)
|
|
def setSignal(self,name, value):
|
|
try:
|
|
return hal.set_s(name,value)
|
|
except Exception as e:
|
|
raise("QHal: Error setting signal {} to {}\n {}".format(name,value, e))
|
|
|
|
def exit(self, *a, **kw):
|
|
return self.comp.exit(*a, **kw)
|
|
|
|
# find a unique HAL pin name by adding '-x' to the base name
|
|
# x being an ever increasing number till name is unique
|
|
def makeUniqueName(self, name):
|
|
num = 2
|
|
base = self.comp.getprefix()
|
|
while True:
|
|
trial = ('{}.{}-{}').format(base,name,num)
|
|
for i in hal.get_info_pins():
|
|
if i['NAME'] == trial:
|
|
num +=1
|
|
trial = ('{}.{}-{}').format(base,name,num)
|
|
break
|
|
else:
|
|
break
|
|
return ('{}-{}').format(name,num)
|
|
|
|
def __getitem__(self, k): return self.comp[k]
|
|
def __setitem__(self, k, v): self.comp[k] = v
|
|
|
|
class Qhal(_QHal):
|
|
_instance = None
|
|
|
|
def __new__(cls, *args, **kwargs):
|
|
if cls._instance is None:
|
|
cls._instance = _QHal.__new__(cls, *args, **kwargs)
|
|
return cls._instance
|