From 4ab7fd882cba26fc51cf15d2c999fe796ecbe6f6 Mon Sep 17 00:00:00 2001 From: Olivier R-D Date: Sun, 3 Feb 2013 21:12:52 +0100 Subject: [PATCH] moved SecondaryMonitor to its own file together with the parser --- README | 2 +- urx/urparser.py | 170 --------------------------- urx/ursecmon.py | 302 ++++++++++++++++++++++++++++++++++++++++++++++++ urx/urx.py | 136 +--------------------- 4 files changed, 305 insertions(+), 305 deletions(-) delete mode 100644 urx/urparser.py create mode 100644 urx/ursecmon.py diff --git a/README b/README index 4f6a55e..0ee0ba4 100644 --- a/README +++ b/README @@ -1,5 +1,5 @@ High level driver to control a Universal Robot robot from python through its TCP/IP interface urx.py is the main file in this repository. It is a python library to control an UR robot from python. Internally it uses URSCript. urx can optionally use the math3d library to receive and send transformation matrices to the robot -urparser.py is only the parser for the binary data frmo the UR +ursecmon.py is a parser for the binary data from the UR urrtmon.py is a parser for the "real-time" interface of the robot. It is hardcoded for some controller versions and therefore disabled by default diff --git a/urx/urparser.py b/urx/urparser.py deleted file mode 100644 index 8b20487..0000000 --- a/urx/urparser.py +++ /dev/null @@ -1,170 +0,0 @@ -""" -parse data from UR socket client -REMARK -Only the last connected socket on 3001 is the primary client !!!! so it is unreliable to rely on it -http://support.universal-robots.com/Technical/PrimaryAndSecondaryClientInterface -""" - -__author__ = "Olivier Roulet-Dubonnet" -__copyright__ = "Copyright 2011-2012, Olivier Roulet-Dubonnet" -__credits__ = ["Olivier Roulet-Dubonnet"] -__license__ = "GPLv3" -__version__ = "0.3" -__status__ = "Development" - - -import struct -from copy import copy - -class ParsingException(Exception): - def __init__(self, *args): - Exception.__init__(self, *args) - - -def _get_data(data, fmt, names): - """ - fill data into a dictionary - data is data from robot packet - fmt is struct format, but with added A for arrays and no support for numerical in fmt - names args are strings used to store values - """ - - tmpdata = copy(data) - fmt = fmt.strip() # space may confuse us - d = dict() - i = 0 - j = 0 - while j < len(fmt) and i < len(names): - f = fmt[j] - if f in (" ", "!", ">", "<"): - j += 1 - elif f == "A": #we got an array - # first we need to find its size - if j == len(fmt) - 2: # we are last element, size is the rest of data in packet - arraysize = len(tmpdata) - else: # size should be given in last element - asn = names[i-1] - if not asn.endswith("Size"): - raise ParsingException("Error, array without size ! %s %s" % (asn, i)) - else: - arraysize = d[asn] - d[names[i]] = tmpdata[0:arraysize] - #print "Array is ", names[i], d[names[i]] - tmpdata = tmpdata[arraysize:] - j += 2 - i += 1 - else: - fmtsize = struct.calcsize(fmt[j]) - #print "reading ", f , i, j, fmtsize, len(tmpdata) - if len(tmpdata) < fmtsize: #seems to happen on windows - raise ParsingException("Error, length of data smaller than advertized: ", len(tmpdata), fmtsize, "for names ", names, f, i, j) - d[names[i]] = struct.unpack("!" + f, tmpdata[0:fmtsize])[0] - #print names[i], d[names[i]] - tmpdata = tmpdata[fmtsize:] - j += 1 - i += 1 - - return d - -def get_header(data): - return struct.unpack("!iB", data[0:5]) - -def analyze_header(data): - """ - read first 5 bytes and return complete packet - """ - if not len(data) >= 5: - raise ParsingException("Packet size %s smaller than header size (5 bytes)" % len(data)) - else: - psize, ptype = get_header(data) - if psize < 5: - raise ParsingException("Error, declared length of data smaller than its own header(5): ", psize) - elif psize > len(data): - raise ParsingException("Error, length of data smaller (%s) than declared (%s)" %( len(data), psize)) - return psize, ptype, data[:psize], data[psize:] - - -def find_first_packet(data): - """ - find the first complete packet in a string - returns None if none found - """ - counter = 0 - while True: - if len(data) >= 5: - psize, ptype = get_header(data) - if psize < 5 or psize > 500 or ptype != 16: - data = data[1:] - elif len(data) > psize: - if counter: - print("Had to remove % bytes of garbage at begining of packet" % counter) - #ok we we have somehting which looks like a packet" - return (data[:psize], data[psize:]) - else: - #packet is not complete - return None - else: - return None - - - -def parse(data): - """ - parse a packet from the UR socket and return a dictionary with the data - """ - allData = {} - #print "Total size ", len(data) - while data: - psize, ptype, pdata, data = analyze_header(data) - #print "We got packet with size %i and type %s" % (psize, ptype) - if ptype == 16: - allData["SecondaryClientData"] = _get_data(pdata, "!iB", ("size", "type")) - data = (pdata + data)[5:] # This is the total size so we resend data to parser - elif ptype == 0: - allData["RobotModeData"] = _get_data(pdata, "!iBQ???????Bd", ("size", "type", "timestamp", "isRobotConnected", "isRealRobotEnabled", "isPowerOnRobot", "isEmergencyStopped", "isSecurityStopped", "isProgramRunning", "isProgramPaused", "robotMode", "speedFraction")) - elif ptype == 1: - tmpstr = ["size", "type"] - for i in range(0, 6): - tmpstr += ["q_actual%s" % i, "q_target%s" % i, "qd_actual%s" % i, "I_actual%s" % i, "V_actual%s" % i, "T_motor%s" % i ,"T_micro%s" % i, "jointMode%s" % i] - - allData["JointData"] = _get_data(pdata, "!iB dddffffB dddffffB dddffffB dddffffB dddffffB dddffffB", tmpstr) - - elif ptype == 4: - allData["CartesianInfo"] = _get_data(pdata, "iBdddddd", ("size", "type", "X", "Y", "Z", "Rx", "Ry", "Rz")) - elif ptype == 5: - allData["LaserPointer(OBSOLETE)"] = _get_data(pdata, "iBddd" , ("size", "type")) - elif ptype == 3: - allData["MasterBoardData"] = _get_data(pdata, "iBhhbbddbbddffffBBb" , ("size", "type", "digitalInputBits", "digitalOutputBits", "analogInputRange0", "analogInputRange1", "analogInput0", "analogInput1", "analogInputDomain0", "analogInputDomain1", "analogOutput0", "analogOutput1", "masterBoardTemperature", "robotVoltage48V", "robotCurrent", "masterIOCurrent"))#, "masterSafetyState" ,"masterOnOffState", "euromap67InterfaceInstalled" )) - elif ptype == 2: - allData["ToolData"] = _get_data(pdata, "iBbbddfBffB" , ("size", "type", "analoginputRange2", "analoginputRange3", "analogInput2", "analogInput3", "toolVoltage48V", "toolOutputVoltage", "toolCurrent", "toolTemperature", "toolMode" )) - - elif ptype == 20: - tmp = _get_data(pdata, "!iB Qbb", ("size", "type", "timestamp", "source", "robotMessageType")) - if tmp["robotMessageType"] == 3: - allData["VersionMessage"] = _get_data(pdata, "!iBQbb bAbBBiAb", ("size", "type", "timestamp", "source", "robotMessageType", "projectNameSize", "projectName", "majorVersion", "minorVersion", "svnRevision", "buildDate")) - elif tmp["robotMessageType"] == 6: - allData["robotCommMessage"] = _get_data(pdata, "!iBQbb iiAc", ("size", "type", "timestamp", "source", "robotMessageType", "code", "argument", "messageText")) - elif tmp["robotMessageType"] == 1: - allData["labelMessage"] = _get_data(pdata, "!iBQbb iAc", ("size", "type", "timestamp", "source", "robotMessageType", "id", "messageText")) - elif tmp["robotMessageType"] == 2: - allData["popupMessage"] = _get_data(pdata, "!iBQbb ??BAcAc", ("size", "type", "timestamp", "source", "robotMessageType", "warning", "error", "titleSize", "messageTitle", "messageText")) - elif tmp["robotMessageType"] == 0: - allData["messageText"] = _get_data(pdata, "!iBQbb Ac", ("size", "type", "timestamp", "source", "robotMessageType", "messageText")) - elif tmp["robotMessageType"] == 8: - allData["varMessage"] = _get_data(pdata, "!iBQbb iiBAcAc", ("size", "type", "timestamp", "source", "robotMessageType", "code", "argument", "titleSize", "messageTitle", "messageText")) - elif tmp["robotMessageType"] == 7: - allData["keyMessage"] = _get_data(pdata, "!iBQbb iiBAcAc", ("size", "type", "timestamp", "source", "robotMessageType", "code", "argument", "titleSize", "messageTitle", "messageText")) - elif tmp["robotMessageType"] == 5: - allData["keyMessage"] = _get_data(pdata, "!iBQbb iiAc", ("size", "type", "timestamp", "source", "robotMessageType", "code", "argument", "messageText")) - else: - print("Message type parser not implemented ", tmp) - else: - print("Uknown packet type %s with size %s" % (ptype, psize)) - - return allData - - - - - - diff --git a/urx/ursecmon.py b/urx/ursecmon.py new file mode 100644 index 0000000..06801fb --- /dev/null +++ b/urx/ursecmon.py @@ -0,0 +1,302 @@ +""" +This file contains 2 classes: + - ParseUtils containing utilies to parse data from UR robot + - SecondaryMonitor, a class opening a socket to the robot and with methods to access data and send programs to the robot +Both use data from the secondary port of the URRobot. +Only the last connected socket on 3001 is the primary client !!!! so it is unreliable to rely on it +http://support.universal-robots.com/Technical/PrimaryAndSecondaryClientInterface +""" + +__author__ = "Olivier Roulet-Dubonnet" +__copyright__ = "Copyright 2011-2012, Olivier Roulet-Dubonnet" +__credits__ = ["Olivier Roulet-Dubonnet"] +__license__ = "GPLv3" +__version__ = "0.4" +__status__ = "Development" + +from threading import Thread, Condition, Lock +import logging +import struct +import socket +from copy import copy + + + +class ParsingException(Exception): + def __init__(self, *args): + Exception.__init__(self, *args) + + + +class ParserUtils(object): + def __init__(self): + self.logger = logging.getLogger(__name__) + + def parse(self, data): + """ + parse a packet from the UR socket and return a dictionary with the data + """ + allData = {} + #print "Total size ", len(data) + while data: + psize, ptype, pdata, data = self.analyze_header(data) + #print "We got packet with size %i and type %s" % (psize, ptype) + if ptype == 16: + allData["SecondaryClientData"] = self._get_data(pdata, "!iB", ("size", "type")) + data = (pdata + data)[5:] # This is the total size so we resend data to parser + elif ptype == 0: + allData["RobotModeData"] = self._get_data(pdata, "!iBQ???????Bd", ("size", "type", "timestamp", "isRobotConnected", "isRealRobotEnabled", "isPowerOnRobot", "isEmergencyStopped", "isSecurityStopped", "isProgramRunning", "isProgramPaused", "robotMode", "speedFraction")) + elif ptype == 1: + tmpstr = ["size", "type"] + for i in range(0, 6): + tmpstr += ["q_actual%s" % i, "q_target%s" % i, "qd_actual%s" % i, "I_actual%s" % i, "V_actual%s" % i, "T_motor%s" % i ,"T_micro%s" % i, "jointMode%s" % i] + + allData["JointData"] = self._get_data(pdata, "!iB dddffffB dddffffB dddffffB dddffffB dddffffB dddffffB", tmpstr) + + elif ptype == 4: + allData["CartesianInfo"] = self._get_data(pdata, "iBdddddd", ("size", "type", "X", "Y", "Z", "Rx", "Ry", "Rz")) + elif ptype == 5: + allData["LaserPointer(OBSOLETE)"] = self._get_data(pdata, "iBddd" , ("size", "type")) + elif ptype == 3: + allData["MasterBoardData"] = self._get_data(pdata, "iBhhbbddbbddffffBBb" , ("size", "type", "digitalInputBits", "digitalOutputBits", "analogInputRange0", "analogInputRange1", "analogInput0", "analogInput1", "analogInputDomain0", "analogInputDomain1", "analogOutput0", "analogOutput1", "masterBoardTemperature", "robotVoltage48V", "robotCurrent", "masterIOCurrent"))#, "masterSafetyState" ,"masterOnOffState", "euromap67InterfaceInstalled" )) + elif ptype == 2: + allData["ToolData"] = self._get_data(pdata, "iBbbddfBffB" , ("size", "type", "analoginputRange2", "analoginputRange3", "analogInput2", "analogInput3", "toolVoltage48V", "toolOutputVoltage", "toolCurrent", "toolTemperature", "toolMode" )) + + elif ptype == 20: + tmp = self._get_data(pdata, "!iB Qbb", ("size", "type", "timestamp", "source", "robotMessageType")) + if tmp["robotMessageType"] == 3: + allData["VersionMessage"] = self._get_data(pdata, "!iBQbb bAbBBiAb", ("size", "type", "timestamp", "source", "robotMessageType", "projectNameSize", "projectName", "majorVersion", "minorVersion", "svnRevision", "buildDate")) + elif tmp["robotMessageType"] == 6: + allData["robotCommMessage"] = self._get_data(pdata, "!iBQbb iiAc", ("size", "type", "timestamp", "source", "robotMessageType", "code", "argument", "messageText")) + elif tmp["robotMessageType"] == 1: + allData["labelMessage"] = self._get_data(pdata, "!iBQbb iAc", ("size", "type", "timestamp", "source", "robotMessageType", "id", "messageText")) + elif tmp["robotMessageType"] == 2: + allData["popupMessage"] = self._get_data(pdata, "!iBQbb ??BAcAc", ("size", "type", "timestamp", "source", "robotMessageType", "warning", "error", "titleSize", "messageTitle", "messageText")) + elif tmp["robotMessageType"] == 0: + allData["messageText"] = self._get_data(pdata, "!iBQbb Ac", ("size", "type", "timestamp", "source", "robotMessageType", "messageText")) + elif tmp["robotMessageType"] == 8: + allData["varMessage"] = self._get_data(pdata, "!iBQbb iiBAcAc", ("size", "type", "timestamp", "source", "robotMessageType", "code", "argument", "titleSize", "messageTitle", "messageText")) + elif tmp["robotMessageType"] == 7: + allData["keyMessage"] = self._get_data(pdata, "!iBQbb iiBAcAc", ("size", "type", "timestamp", "source", "robotMessageType", "code", "argument", "titleSize", "messageTitle", "messageText")) + elif tmp["robotMessageType"] == 5: + allData["keyMessage"] = self._get_data(pdata, "!iBQbb iiAc", ("size", "type", "timestamp", "source", "robotMessageType", "code", "argument", "messageText")) + else: + print("Message type parser not implemented ", tmp) + else: + print("Uknown packet type %s with size %s" % (ptype, psize)) + + return allData + + def _get_data(self, data, fmt, names): + """ + fill data into a dictionary + data is data from robot packet + fmt is struct format, but with added A for arrays and no support for numerical in fmt + names args are strings used to store values + """ + tmpdata = copy(data) + fmt = fmt.strip() # space may confuse us + d = dict() + i = 0 + j = 0 + while j < len(fmt) and i < len(names): + f = fmt[j] + if f in (" ", "!", ">", "<"): + j += 1 + elif f == "A": #we got an array + # first we need to find its size + if j == len(fmt) - 2: # we are last element, size is the rest of data in packet + arraysize = len(tmpdata) + else: # size should be given in last element + asn = names[i-1] + if not asn.endswith("Size"): + raise ParsingException("Error, array without size ! %s %s" % (asn, i)) + else: + arraysize = d[asn] + d[names[i]] = tmpdata[0:arraysize] + #print "Array is ", names[i], d[names[i]] + tmpdata = tmpdata[arraysize:] + j += 2 + i += 1 + else: + fmtsize = struct.calcsize(fmt[j]) + #print "reading ", f , i, j, fmtsize, len(tmpdata) + if len(tmpdata) < fmtsize: #seems to happen on windows + raise ParsingException("Error, length of data smaller than advertized: ", len(tmpdata), fmtsize, "for names ", names, f, i, j) + d[names[i]] = struct.unpack("!" + f, tmpdata[0:fmtsize])[0] + #print names[i], d[names[i]] + tmpdata = tmpdata[fmtsize:] + j += 1 + i += 1 + return d + + def get_header(self, data): + return struct.unpack("!iB", data[0:5]) + + def analyze_header(self, data): + """ + read first 5 bytes and return complete packet + """ + if not len(data) >= 5: + raise ParsingException("Packet size %s smaller than header size (5 bytes)" % len(data)) + else: + psize, ptype = self.get_header(data) + if psize < 5: + raise ParsingException("Error, declared length of data smaller than its own header(5): ", psize) + elif psize > len(data): + raise ParsingException("Error, length of data smaller (%s) than declared (%s)" %( len(data), psize)) + return psize, ptype, data[:psize], data[psize:] + + def find_first_packet(self, data): + """ + find the first complete packet in a string + returns None if none found + """ + counter = 0 + while True: + if len(data) >= 5: + psize, ptype = self.get_header(data) + if psize < 5 or psize > 500 or ptype != 16: + data = data[1:] + elif len(data) > psize: + if counter: + self.logger.info("Remove {} bytes of garbage at begining of packet".format(counter)) + #ok we we have somehting which looks like a packet" + return (data[:psize], data[psize:]) + else: + #packet is not complete + return None + else: + return None + + + +class SecondaryMonitor(Thread): + """ + Monitor data from secondary port and send programs to robot + """ + def __init__(self, host): + Thread.__init__(self) + self.logger = logging.getLogger(self.__class__.__name__) + self._parser = ParserUtils() + self._s_secondary = None + self._dict = {} + self._dictLock = Lock() + self.host = host + secondary_port = 30002 # Secondary client interface on Universal Robots + self._s_secondary = socket.create_connection((self.host, secondary_port), timeout=0.5) + self._prog_queue = [] + self._prog_queue_lock = Lock() + self._dataqueue = bytes() + self._trystop = False # to stop thread + self.running = False #True when robot is on and listening + self._dataEvent = Condition() + + self.start() + self._dataEvent.wait() # make sure we got some data before someone calls us + + def sendProgram(self, prog): + """ + send program to robot in URRobot format + If another program is send while a program is running the first program is aborded. + """ + with self._prog_queue_lock: + prog.strip() + self.logger.debug("Sending program: prog") + self._prog_queue.append(prog.encode() + b"\n") + + + def run(self): + """ + check program execution status in the secondary client data packet we get from the robot + This interface uses only data from the secondary client interface (see UR doc) + Only the last connected client is the primary client, + so this is not guaranted and we cannot rely on information only + send to the primary client(like program execution start ?!?!?) + """ + + while not self._trystop: + with self._prog_queue_lock: + if len(self._prog_queue) > 0: + prog = self._prog_queue.pop(0) + self._s_secondary.send(prog) + + data = self._get_data() + try: + with self._dictLock: + self._dict = self._parser.parse(data) + except ParsingException as ex: + self.logger.warn("Error parsing data from urrobot: " + str(ex) ) + continue + + if "RobotModeData" not in self._dict: + self.logger.warn( "Got a packet from robot without RobotModeData, strange ...") + continue + + if self._dict["RobotModeData"]["robotMode"] == 0 \ + and self._dict["RobotModeData"]["isRealRobotEnabled"] == True \ + and self._dict["RobotModeData"]["isEmergencyStopped"] == False \ + and self._dict["RobotModeData"]["isSecurityStopped"] == False \ + and self._dict["RobotModeData"]["isRobotConnected"] == True \ + and self._dict["RobotModeData"]["isPowerOnRobot"] == True: + self.running = True + else: + if self.running == True: + self.logger.error("Robot not running: " + str( self._dict["RobotModeData"])) + self.running = False + self._dataEvent.notifyAll() + + def _get_data(self): + """ + returns something that looks like a packet, nothing is guaranted + """ + while True: + ans = self._parser.find_first_packet(self._dataqueue[:]) + if ans: + self._dataqueue = ans[1] + return ans[0] + else: + tmp = self._s_secondary.recv(1024) + self._dataqueue += tmp + + def getCartesianInfo(self): + with self._dictLock: + if "CartesianInfo" in self._dict: + return self._dict["CartesianInfo"] + else: + return None + + def getAllData(self): + """ + return last data obtained from robot in dictionnary format + """ + with self._dictLock: + return self._dict.copy() + + + def getJointData(self): + with self._dictLock: + if "JointData" in self._dict: + return self._dict["JointData"] + else: + return None + + def isProgramRunning(self): + """ + return True if robot is executing a program + Rmq: The refresh rate is only 10Hz so the information may be outdated + """ + with self._dictLock: + return self._dict["RobotModeData"]["isProgramRunning"] + + + def cleanup(self): + self._trystop = True + self.join() + if self._s_secondary: + with self._prog_queue_lock: + self._s_secondary.close() + + + + diff --git a/urx/urx.py b/urx/urx.py index ee0eae4..f5f7d9b 100644 --- a/urx/urx.py +++ b/urx/urx.py @@ -65,8 +65,6 @@ __status__ = "Development" -from threading import Thread, Lock, Condition -import socket import time import logging @@ -79,7 +77,7 @@ except ImportError: print("pymath3d library could not be found on this computer, disabling use of matrices") from urx import urrtmon -from urx import urparser +from urx import ursecmon from urx import tracker @@ -87,136 +85,6 @@ class RobotException(Exception): pass -class SecondaryMonitor(Thread): - """ - Monitor data from secondry port and send programs to robot - """ - def __init__(self, host): - Thread.__init__(self) - self.logger = logging.getLogger(self.__class__.__name__) - self._lock = Lock() - self._s_secondary = None - self._dict = {} - self._dictLock = Lock() - self.host = host - secondary_port = 30002 # Secondary client interface on Universal Robots - self._s_secondary = socket.create_connection((self.host, secondary_port), timeout=0.5) - self._queue = [] - self._dataqueue = bytes() - self._trystop = False # to stop thread - self.running = False #True when robot is on and listening - self._dataEvent = Condition() - - self.start() - with self._dataEvent: - self._dataEvent.wait() # make sure we got some data before someone calls us - - def sendProgram(self, prog): - """ - send program to robot in URRobot format - If another program is send while a program is running the first program is aborded. - """ - - with self._lock: - prog.strip() - self.logger.debug("Sending program: prog") - self._queue.append(prog.encode() + b"\n") - - - def run(self): - """ - check program execution status in the secondary client data packet we get from the robot - This interface uses only data from the secondary client interface (see UR doc) - Only the last connected client is the primary client, - so this is not guaranted and we cannot rely on information only - send to the primary client(like program execution start ?!?!?) - """ - - while not self._trystop: - with self._lock: - if len(self._queue) > 0: - prog = self._queue.pop(0) - self._s_secondary.send(prog) - - data = self._get_data() - try: - with self._dictLock: - self._dict = urparser.parse(data) - except urparser.ParsingException as ex: - self.logger.warn("Error parsing data from urrobot: " + str(ex) ) - continue - - if "RobotModeData" not in self._dict: - self.logger.warn( "Got a packet from robot without RobotModeData, strange ...") - continue - - if self._dict["RobotModeData"]["robotMode"] == 0 \ - and self._dict["RobotModeData"]["isRealRobotEnabled"] == True \ - and self._dict["RobotModeData"]["isEmergencyStopped"] == False \ - and self._dict["RobotModeData"]["isSecurityStopped"] == False \ - and self._dict["RobotModeData"]["isRobotConnected"] == True \ - and self._dict["RobotModeData"]["isPowerOnRobot"] == True: - self.running = True - else: - if self.running == True: - self.logger.error("Robot not running: " + str( self._dict["RobotModeData"])) - self.running = False - with self._dataEvent: - self._dataEvent.notifyAll() - - - def _get_data(self): - """ - returns something that looks like a packet, nothing is guaranted - """ - while True: - ans = urparser.find_first_packet(self._dataqueue[:]) - if ans: - self._dataqueue = ans[1] - return ans[0] - else: - tmp = self._s_secondary.recv(1024) - self._dataqueue += tmp - - def getCartesianInfo(self): - with self._dictLock: - if "CartesianInfo" in self._dict: - return self._dict["CartesianInfo"] - else: - return None - - def getAllData(self): - """ - return last data obtained from robot in dictionnary format - """ - with self._dictLock: - return self._dict.copy() - - - def getJointData(self): - with self._dictLock: - if "JointData" in self._dict: - return self._dict["JointData"] - else: - return None - - def isProgramRunning(self): - """ - return True if robot is executing a program - Rmq: The refresh rate is only 10Hz so the information may be outdated - """ - with self._dictLock: - return self._dict["RobotModeData"]["isProgramRunning"] - - - def cleanup(self): - self._trystop = True - self.join() - if self._s_secondary: - with self._lock: - self._s_secondary.close() - - class URRobot(object): """ Python interface to socket interface of UR robot. @@ -231,7 +99,7 @@ class URRobot(object): self.host = host self.logger.info("Opening secondary monitor socket") - self.secmon = SecondaryMonitor(self.host) #data from robot at 10Hz + self.secmon = ursecmon.SecondaryMonitor(self.host) #data from robot at 10Hz if useRTInterface: self.logger.info("Opening real-time monitor socket")