Skip to content

Commit aa4089b

Browse files
committed
hal bridge -add new component
A (hopefully) universal bridge between GUI and HAL pins
1 parent c97a957 commit aa4089b

3 files changed

Lines changed: 242 additions & 1 deletion

File tree

src/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,7 @@ install-python: install-dirs
812812
$(EXE) ../bin/pncconf $(DESTDIR)$(bindir)
813813
$(EXE) ../bin/pyui $(DESTDIR)$(bindir)
814814
$(EXE) ../bin/hal_input $(DESTDIR)$(bindir)
815+
$(EXE) ../bin/hal_bridge $(DESTDIR)$(bindir)
815816
$(EXE) ../bin/mitsub_vfd $(DESTDIR)$(bindir)
816817
$(EXE) ../bin/mqtt-publisher $(DESTDIR)$(bindir)
817818
$(EXE) ../bin/z_level_compensation $(DESTDIR)$(bindir)

src/hal/user_comps/Submakefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
USER_COMP_PY = pyvcp hal_input gladevcp scorbot-er-3 mitsub_vfd pmx485 sim-torch z_level_compensation mqtt-publisher
1+
USER_COMP_PY = pyvcp hal_input gladevcp scorbot-er-3 mitsub_vfd pmx485 sim-torch z_level_compensation mqtt-publisher hal_bridge
22

33
USER_COMPS := $(sort $(wildcard hal/user_comps/*.comp))
44
USER_COMP_BINS := $(patsubst hal/user_comps/%.comp, ../bin/%, $(USER_COMPS))

src/hal/user_comps/hal_bridge.py

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
#!/usr/bin/env python3
2+
3+
import os
4+
import sys
5+
import time
6+
7+
import json
8+
import signal
9+
10+
import hal
11+
from PyQt5 import QtCore
12+
from qtvcp.qt_halobjects import Qhal
13+
from common.iniinfo import _IStat as IStatParent
14+
from common import logger
15+
16+
# LOG is for running code logging
17+
LOG = logger.initBaseLogger('HAL bridge', log_file=None,
18+
log_level=logger.WARNING, logToFile=False)
19+
20+
# Force the log level for this module
21+
#LOG.setLevel(logger.DEBUG) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL
22+
23+
try:
24+
import zmq
25+
ZMQ = True
26+
except:
27+
LOG.critical('ZMQ python library problem - Is python3-zmq installed?')
28+
ZMQ = False
29+
30+
class Info(IStatParent):
31+
_instance = None
32+
_instanceNum = 0
33+
34+
def __new__(cls, *args, **kwargs):
35+
if not cls._instance:
36+
cls._instance = IStatParent.__new__(cls, *args, **kwargs)
37+
return cls._instance
38+
39+
# Instantiate the library with global reference
40+
41+
42+
class Bridge(object):
43+
def __init__(self, readAddress = "tcp://127.0.0.1:5690",
44+
writeAddress = "tcp://127.0.0.1:5691"):
45+
super(Bridge, self).__init__()
46+
self.INFO = Info()
47+
48+
self.readAddress = readAddress
49+
self.writeAddress = writeAddress
50+
LOG.debug('read port: {}'.format(readAddress))
51+
LOG.debug('write port: {}'.format(writeAddress))
52+
53+
self.readTopic = ""
54+
self.writeTopic = "STATUSREQUEST"
55+
56+
# catch control c and terminate signals
57+
signal.signal(signal.SIGTERM, self.shutdown)
58+
signal.signal(signal.SIGINT, self.shutdown)
59+
60+
self.init_hal()
61+
if ZMQ:
62+
self.init_read()
63+
self.init_write()
64+
65+
def init_hal(self):
66+
self.comp = h = hal.component("bridge")
67+
QHAL = Qhal(comp=self.comp, hal=hal)
68+
69+
self.jogRate = QHAL.newpin("jog-rate", hal.HAL_FLOAT, hal.HAL_OUT)
70+
self.jogRateAngular = QHAL.newpin("jog-rate-angular", hal.HAL_FLOAT, hal.HAL_OUT)
71+
self.jogIncrement = QHAL.newpin("jog-increment", hal.HAL_FLOAT, hal.HAL_OUT)
72+
self.jogIncrementAngular = QHAL.newpin("jog-increment-angular", hal.HAL_FLOAT, hal.HAL_OUT)
73+
self.activeJoint = QHAL.newpin('joint-selected', hal.HAL_S32, hal.HAL_OUT)
74+
75+
for i in (self.INFO.AVAILABLE_AXES):
76+
let = i.lower()
77+
# input
78+
self['axis{}Select'.format(let)] = QHAL.newpin('axis-%s-select'%let, hal.HAL_BIT, hal.HAL_IN)
79+
self['axis{}Select'.format(let)].pinValueChanged.connect(self.pinChanged)
80+
# output
81+
self['Axis{}IsSelected'.format(let)] = QHAL.newpin('axis-%s-is-selected'%let, hal.HAL_BIT, hal.HAL_OUT)
82+
83+
self.cycle_start = QHAL.newpin('cycle-start-in',QHAL.HAL_BIT, QHAL.HAL_IN)
84+
self.cycle_start.pinValueChanged.connect(self.pinChanged)
85+
self.cycle_pause = QHAL.newpin('cycle-pause-in',QHAL.HAL_BIT, QHAL.HAL_IN)
86+
self.cycle_pause.pinValueChanged.connect(self.pinChanged)
87+
88+
for i in self.INFO.MDI_COMMAND_DICT:
89+
LOG.debug('{} {}'.format(i,self.INFO.MDI_COMMAND_DICT.get(i)))
90+
self[i] = QHAL.newpin('macro-cmd-{}'.format(i),QHAL.HAL_BIT, QHAL.HAL_IN)
91+
self[i].pinValueChanged.connect(self.runMacroChanged)
92+
93+
QHAL.setUpdateRate(100)
94+
h.ready()
95+
96+
def init_write(self):
97+
context = zmq.Context()
98+
self.writeSocket = context.socket(zmq.PUB)
99+
self.writeSocket.bind(self.writeAddress)
100+
101+
def init_read(self):
102+
# ZeroMQ Context
103+
self.readContext = zmq.Context()
104+
105+
# Define the socket using the "Context"
106+
self.readSocket = self.readContext.socket(zmq.SUB)
107+
108+
# Define subscription and messages with topic to accept.
109+
self.readSocket.setsockopt_string(zmq.SUBSCRIBE, self.readTopic)
110+
self.readSocket.connect(self.readAddress)
111+
self.readNotify = QtCore.QSocketNotifier(
112+
self.readSocket.getsockopt(zmq.FD),
113+
QtCore.QSocketNotifier.Read, None)
114+
self.readNotify.activated.connect(self.onReadMsg)
115+
116+
# callback from ZMQ read socket
117+
def onReadMsg(self, msg):
118+
if self.readSocket.getsockopt(zmq.EVENTS) & zmq.POLLIN:
119+
while self.readSocket.getsockopt(zmq.EVENTS) & zmq.POLLIN:
120+
# get raw message
121+
topic, data = self.readSocket.recv_multipart()
122+
# convert from json object to python object
123+
y = json.loads(data)
124+
self. action(y.get('MESSAGE'),y.get('ARGS'))
125+
126+
# set our output HAL pins from messages from hal_glib
127+
def action(self, msg, data):
128+
LOG.debug('{} {}'.format(msg, data))
129+
if msg == 'jograte-changed':
130+
self.jogRate.set(float(data[0]))
131+
if msg == 'jograte-angular-changed':
132+
self.jogRateAngular.set(float(data[0]))
133+
elif msg == 'jogincrements-changed':
134+
self.jogIncrement.set(float(data[0][0]))
135+
elif msg == 'jogincrement-angular-changed':
136+
self.jogIncremtAngular.set(float(data[0][0]))
137+
elif msg == 'joint-selection-changed':
138+
self.activeJoint.set(int(data[0]))
139+
elif msg == 'axis-selection-changed':
140+
for i in(self.INFO.AVAILABLE_AXES):
141+
if data[0] == i:
142+
state = True
143+
else:
144+
state = False
145+
self['Axis{}IsSelected'.format(i.lower())].set(state)
146+
147+
# send msg to hal_glib
148+
def writeMsg(self, msg, data):
149+
if ZMQ:
150+
topic = self.writeTopic
151+
message = json.dumps({'FUNCTION':msg,'ARGS':data})
152+
LOG.debug('Sending ZMQ Message:{} {}'.format(topic, message))
153+
self.writeSocket.send_multipart(
154+
[bytes(topic.encode('utf-8')),
155+
bytes((message).encode('utf-8'))])
156+
157+
# callback from HAL input pins
158+
def pinChanged(self, pinObject, value):
159+
LOG.debug('Pin name:{} changed value to {}'.format(pinObject.text(), value))
160+
#print(type(value))
161+
# Axis selction change request
162+
if 'select' in pinObject.text():
163+
if bool(value) == False:
164+
pass
165+
#print('Not true state')
166+
return
167+
for i in (self.INFO.AVAILABLE_AXES):
168+
if '-{}-'.format(i.lower()) in pinObject.text():
169+
self.writeMsg('set_selected_axis', i)
170+
break
171+
else:
172+
if 'None' in pinObject.text():
173+
self.writeMsg('set_selected_axis', '')
174+
175+
# cycle start
176+
elif self.cycle_start == pinObject:
177+
if value:
178+
self.writeMsg('request_cycle_start', value)
179+
180+
# cycle pause
181+
elif self.cycle_pause == pinObject:
182+
#if value:
183+
self.writeMsg('request_cycle_pause', value)
184+
185+
# catch all default
186+
else:
187+
self.writeMsg(pinObject.text(),value)
188+
189+
# callback; request to run a specific macro
190+
def runMacroChanged(self, pinObject, value):
191+
LOG.debug('Macro Pin name:{} changed value to {}'.format(pinObject.text(), value))
192+
#LOG.debug(type(value))
193+
name = pinObject.text().strip('macro-cmd-')
194+
if value:
195+
self.writeMsg('request_macro_call', name)
196+
197+
def shutdown(self,signum=None,stack_frame=None):
198+
LOG.debug('shutdown')
199+
global app
200+
app.quit()
201+
202+
def __getitem__(self, item):
203+
return getattr(self, item)
204+
def __setitem__(self, item, value):
205+
return setattr(self, item, value)
206+
207+
if __name__ == "__main__":
208+
import sys
209+
import getopt
210+
from PyQt5.QtWidgets import QApplication
211+
212+
letters = 'dh' # the : means an argument needs to be passed after the letter
213+
keywords = ['readport=', 'writeport=' ] # the = means that a value is expected after
214+
# the keyword
215+
216+
opts, extraparam = getopt.getopt(sys.argv[1:],letters,keywords)
217+
# starts at the second element of argv since the first one is the script name
218+
# extraparms are extra arguments passed after all option/keywords are assigned
219+
# opts is a list containing the pair "option"/"value"
220+
221+
readport = "tcp://127.0.0.1:5690"
222+
writeport = "tcp://127.0.0.1:5691"
223+
224+
for o,p in opts:
225+
if o in ['-d']:
226+
LOG.setLevel(logger.DEBUG)
227+
elif o in ['--readport']:
228+
readport = p
229+
elif o in ['--writeport']:
230+
writeport = p
231+
elif o in ['-h','--help']:
232+
print('HAL bridge: GUI to HAL interface using ZMQ')
233+
print('option "-d" = debug print mode')
234+
print('option "--readport=" read socket address')
235+
print('option "--writeport=" write socket address')
236+
print('example: hal_bridge -d --readport=tcp://127.0.0.1:5692')
237+
238+
app = QApplication(sys.argv)
239+
test = Bridge(readport, writeport)
240+
sys.exit(app.exec_())

0 commit comments

Comments
 (0)