1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
|
#!/usr/bin/env python3
# vim: sts=4 sw=4 et
import linuxcnc
from gi.repository import GObject
import inspect
import _hal
import hal
import traceback
from PyQt5.QtCore import QObject, QTimer, pyqtSignal
from hal_glib import GStat
from qtvcp.qt_istat import _IStat as IStatParent
# Set up logging
from . import logger
log = logger.getLogger(__name__)
# log.setLevel(logger.INFO) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL, VERBOSE
# The order of these classes is importanr, otherwise - circular imports.
# the some of the later classes reference the earlier classes
################################################################
# IStat class
################################################################
class Info(IStatParent):
_instance = None
_instanceNum = 0
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = IStatParent.__new__(cls, *args, **kwargs)
return cls._instance
# Now that the class is defined create a reference to it for the other classes
INI = Info()
class QPin(hal.Pin, QObject):
value_changed = pyqtSignal('PyQt_PyObject')
pinValueChanged = pyqtSignal('PyQt_PyObject','PyQt_PyObject')
REGISTRY = []
UPDATE = False
def __init__(self, *a, **kw):
super(QPin, self).__init__(*a, **kw)
QObject.__init__(self, None)
self._item_wrap(self._item)
self._prev = None
self.REGISTRY.append(self)
self.update_start()
def update(self):
tmp = self.get()
if tmp != self._prev:
self.value_changed.emit(tmp)
self.pinValueChanged.emit(self, tmp)
self._prev = tmp
def text(self):
return self.get_name()
# always returns False because
# there was no errpr 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)
for p in kill:
cls.REGISTRY.remove(p)
return cls.UPDATE
@classmethod
def update_start(cls):
if QPin.UPDATE:
return
QPin.UPDATE = True
cls.timer = QTimer()
cls.timer.timeout.connect(cls.update_all)
cls.timer.start(INI.HALPIN_CYCLE_TIME)
@classmethod
def update_stop(cls):
QPin.UPDATE = False
# so errors when making QPins aren't fatal
class DummyPin(QObject):
value_changed = pyqtSignal('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 errpr when making HAL pin
# see class QPin
def error(self):
return True
def getError(self):
print('{}'.format(self._kw.get('ERROR')))
def get(self):
return 0
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 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)
return p
def getpin(self, *a, **kw): return QPin(_hal.component.getpin(self.comp, *a, **kw))
def getvalue(self, name):
try:
return hal.get_value(name)
except Exception as e:
log.error("Qhal: Error getting value of {}\n {}".format(name, e))
def setp(self,name, value):
try:
return hal.set_p(name,value)
except Exception as e:
log.error("Qhal: Error setting value of {} 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
################################################################
# GStat class
################################################################
# use the same Gstat as gladeVCP uses
# by subclassing it
class Status(GStat):
_instance = None
_instanceNum = 0
__gsignals__ = {
'toolfile-stale': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_PYOBJECT,)),
}
# only make one instance of the class - pass it to all other
# requested instances
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = GStat.__new__(cls, *args, **kwargs)
return cls._instance
def __init__(self):
# only initialize once for all instances
if self.__class__._instanceNum >= 1:
return
GObject.Object.__init__(self)
self.__class__._instanceNum += 1
super(GStat, self).__init__()
# set the default jog speeds before the forced update
self.current_jog_rate = INI.DEFAULT_LINEAR_JOG_VEL
self.current_angular_jog_rate = INI.DEFAULT_ANGULAR_JOG_VEL
# can only have ONE error channel instance in qtvcp
self.ERROR = linuxcnc.error_channel()
self._block_polling = False
# we override this function from hal_glib
def set_timer(self):
GObject.threads_init()
GObject.timeout_add(int(INI.CYCLE_TIME), self.update)
# error polling is usually set up by screen_option widget
# to call this function
# but when using MDI subprograms, the subprogram must be the only
# polling instance.
# this is done by blocking the main screen polling until the
# subprogram is done.
def poll_error(self):
if self._block_polling: return None
return self.ERROR.poll()
def block_error_polling(self, name=''):
if name: print(name,'block')
self._block_polling = True
def unblock_error_polling(self,name=''):
if name: print(name,'unblock')
self._block_polling = False
################################################################
# PStat class
################################################################
from qtvcp.qt_pstat import _PStat as _PStatParent
class Path(_PStatParent):
_instance = None
_instanceNum = 0
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = _PStatParent.__new__(cls, *args, **kwargs)
return cls._instance
################################################################
# Lcnc_Action class
################################################################
from qtvcp.qt_action import _Lcnc_Action as _ActionParent
class Action(_ActionParent):
_instance = None
_instanceNum = 0
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = _ActionParent.__new__(cls, *args, **kwargs)
return cls._instance
################################################################
# TStat class
################################################################
from qtvcp.qt_tstat import _TStat as _TStatParent
class Tool(_TStatParent):
_instance = None
_instanceNum = 0
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = _TStatParent.__new__(cls, *args, **kwargs)
return cls._instance
|