Commit b05b3575 authored by Cláudio Gomes's avatar Cláudio Gomes
Browse files

initial import

parent 154fc524
import logging
import math
import time
import unittest
import numpy as np
from urinterface.robot_connection import RobotConnection
class RobotLiveTests(unittest.TestCase):
def test_start_stop_recording_ur5e(self):
ur5e = RobotConnection("192.168.12.245") # UR5e Robot @ DeepTech
ur5e.start_recording(filename=f'randomMotion.csv')
ur5e.stop_recording()
def test_movej(self):
# ur5e = RobotConnection("192.168.56.101") # VirtualBox
ur5e = RobotConnection("192.168.12.245") # Robot @ DeepTech
q0 = np.array([-2.*math.pi, 0., -2.7925, -.5*math.pi, 0., 0.])
q1 = np.array([2.*math.pi, 0., -2.7925, -.5*math.pi, 0., 0.])
acc = 20.0
v_abs_array = np.linspace(0.2, 3.2, 20)
ur5e.movej(q=q1) # move to start
i = 1
for v_abs in v_abs_array:
# record negative velocity
ur5e.start_recording(filename=f'velTest3_{i}_v=-{v_abs:.4f}.csv')
time.sleep(1.5) # wait for thread to start recording
ur5e.movej(q=q0, v=v_abs, a=acc)
time.sleep(1.5) # wait for thread to start recording
ur5e.stop_recording()
# record positive velocity
ur5e.start_recording(filename=f'velTest3_{i}_v={v_abs:.4f}.csv')
time.sleep(1.5)
ur5e.movej(q=q1, v=v_abs, a=acc)
time.sleep(1.5)
ur5e.stop_recording()
i += 1
# Analyze data
# 1. open file
# 2. take a subset of data (the data with constant-velocity)
# 3. average current of subset of data
# 4. generate friction model
def test_debug_first_startrecord_only(self):
"""
THis test shows the importantance of reverting the global state of the RobotConnection.
"""
# logging.basicConfig(level=logging.DEBUG, filename="test_debug_first_startrecord_only.log") # enables logging
ur5e = RobotConnection("192.168.56.101")
q0 = np.array([-2. * math.pi, 0., 0., -.5 * math.pi, 0., 0.])
q1 = np.array([2. * math.pi, 0., 0., -.5 * math.pi, 0., 0.])
v_abs = 1.0
ur5e.movej(q=q1) # move to start
# record negative velocity
ur5e.start_recording(filename=f'velTest2_v=-{v_abs:.5f}.csv')
time.sleep(1.0) # wait for thread to start recording
ur5e.movej(q=q0, v=v_abs)
time.sleep(1.0) # wait for thread to start recording
ur5e.stop_recording()
# record positive velocity
ur5e.start_recording(filename=f'velTest2_v={v_abs:.5f}.csv')
time.sleep(1.0)
ur5e.movej(q=q1, v=v_abs)
time.sleep(1.0)
ur5e.stop_recording()
# Analyze data
# 1. open file
# 2. take a subset of data (the data with constant-velocity)
# 3. average current of subset of data
# 4. generate friction model
def test_real_record(self):
ur5e = RobotConnection("192.168.56.101")
ur5e.record_samples(config_file="resources/record_configuration.xml", frequency=100, samples=10)
if __name__ == '__main__':
unittest.main()
#!/usr/bin/env python3
from pathlib import Path
import setuptools
project_dir = Path(__file__).parent
setuptools.setup(
name="urinterface",
version="0.0.1",
description="UR Robot Interface",
# Allow UTF-8 characters in README with encoding argument.
long_description=project_dir.joinpath("README.md").read_text(encoding="utf-8"),
long_description_content_type="text/markdown",
keywords=["python", "ur robot", "universal robot"],
author="Claudio Gomes and Emil Madsen",
author_email="claudio.gomes@eng.au.dk",
url="https://gitlab.au.dk/clagms/urinterface",
packages=setuptools.find_packages("src"),
package_dir={"": "src"},
license="INTOCPS",
license_files=["LICENSE"],
classifiers=[
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
]
)
# Copyright (c) 2016, Universal Robots A/S,
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the Universal Robots A/S nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL UNIVERSAL ROBOTS A/S BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from .rtde import *
from .rtde_config import *
# Copyright (c) 2016, Universal Robots A/S,
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the Universal Robots A/S nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL UNIVERSAL ROBOTS A/S BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import sys
sys.path.append('../..')
import struct
from third_party.rtde import serialize
class CSVBinaryWriter(object):
def __init__(self, file, names, types, delimiter=' '):
if len(names) != len(types):
raise ValueError('List sizes are not identical.')
self.__file = file
self.__names = names
self.__types = types
self.__delimiter = delimiter
self.__header_names = []
self.__columns = 0
for i in range(len(self.__names)):
size = serialize.get_item_size(self.__types[i])
self.__columns += size
if size > 1:
for j in range(size):
name = self.__names[i]+'_'+str(j)
self.__header_names.append(name)
else:
name = self.__names[i]
self.__header_names.append(name)
def getType(self, vtype):
if(vtype == 'VECTOR3D'):
return "DOUBLE" + self.__delimiter + "DOUBLE" + self.__delimiter + "DOUBLE"
elif(vtype == 'VECTOR6D'):
return "DOUBLE" + self.__delimiter + "DOUBLE" + self.__delimiter + "DOUBLE" + self.__delimiter + "DOUBLE" + self.__delimiter + "DOUBLE" + self.__delimiter + "DOUBLE"
elif(vtype == 'VECTOR6INT32'):
return "INT32" + self.__delimiter + "INT32" + self.__delimiter + "INT32" + self.__delimiter + "INT32" + self.__delimiter + "INT32" + self.__delimiter + "INT32"
elif(vtype == 'VECTOR6UINT32'):
return "UINT32" + self.__delimiter + "UINT32" + self.__delimiter + "UINT32" + self.__delimiter + "UINT32" + self.__delimiter + "UINT32" + self.__delimiter + "UINT32"
else:
return str(vtype)
def writeheader(self):
#Header names
headerStr=str("")
for i in range(len(self.__header_names)):
if(i != 0):
headerStr += self.__delimiter
headerStr += self.__header_names[i]
headerStr += "\n"
self.__file.write(struct.pack(str(len(headerStr)) + 's', headerStr))
#Header types
typeStr=str("")
for i in range(len(self.__names)):
if(i != 0):
typeStr += self.__delimiter
typeStr += self.getType(self.__types[i])
typeStr += "\n"
self.__file.write(struct.pack(str(len(typeStr)) + 's', typeStr))
def packToBinary(self, vtype, value):
print(vtype)
if(vtype == 'BOOL'):
print("isBOOL" + str(value))
if(vtype == 'UINT8'):
print("isUINT8" + str(value))
elif(vtype == 'INT32'):
print("isINT32" + str(value))
elif(vtype == 'INT64'):
print("isINT64" + str(value))
elif(vtype == 'UINT32'):
print("isUINT32" + str(value))
elif(vtype == 'UINT64'):
print("isUINT64" + str(value))
elif(vtype == 'DOUBLE'):
print("isDOUBLE" + str(value) + str(type(value)) + str(sys.getsizeof(value)))
elif(vtype == 'VECTOR3D'):
print("isVECTOR3D" + str(value[0]) + ","+ str(value[1]) + ","+ str(value[2]))
elif(vtype == 'VECTOR6D'):
print("isVECTOR6D" + str(value[0]) + ","+ str(value[1]) + ","+ str(value[2]) + ","+ str(value[3]) + ","+ str(value[4]) + ","+ str(value[5]))
elif(vtype == 'VECTOR6INT32'):
print("isVECTOR6INT32" + str(value[0]) + ","+ str(value[1]) + ","+ str(value[2]) + ","+ str(value[3]) + ","+ str(value[4]) + ","+ str(value[5]))
elif(vtype == 'VECTOR6UINT32'):
print("isVECTOR6UINT32" + str(value[0]) + ","+ str(value[1]) + ","+ str(value[2]) + ","+ str(value[3]) + ","+ str(value[4]) + ","+ str(value[5]))
def writerow(self, data_object):
self.__file.write(data_object)
\ No newline at end of file
# Copyright (c) 2016, Universal Robots A/S,
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the Universal Robots A/S nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL UNIVERSAL ROBOTS A/S BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import csv
import numpy as np
import logging
from .rtde import LOGNAME
_log = logging.getLogger(LOGNAME)
runtime_state = 'runtime_state'
runtime_state_running = '2'
class CSVReader(object):
__samples = None
__filename = None
def get_header_data(self,__reader):
header = next(__reader)
return header
def __init__(self, csvfile, delimiter = ' ', filter_running_program=False):
self.__filename = csvfile.name
csvfile = [csvfile for csvfile in csvfile.readlines() if csvfile.strip()] # remove any empty lines
reader = csv.reader(csvfile, delimiter=delimiter)
header = self.get_header_data(reader)
# read csv file
data = [row for row in reader]
if len(data)==0:
_log.warn('No data read from file: ' + self.__filename)
# filter data
if filter_running_program:
if runtime_state not in header:
_log.warn('Unable to filter data since runtime_state field is missing in data set')
else:
idx = header.index(runtime_state)
data = [row for row in data if row[idx] == runtime_state_running]
self.__samples = len(data)
if self.__samples == 0:
_log.warn('No data left from file: ' + self.__filename + ' after filtering')
# transpose data
data = list(zip(*data))
# create dictionary from header elements (keys) to float arrays
self.__dict__.update({header[i]: np.array(list(map(float, data[:][i]))) for i in range(len(header))})
def get_samples(self):
return self.__samples
def get_name(self):
return self.__filename
# Copyright (c) 2016, Universal Robots A/S,
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the Universal Robots A/S nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL UNIVERSAL ROBOTS A/S BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import csv
import sys
sys.path.append('../..')
from third_party.rtde import serialize
class CSVWriter(object):
def __init__(self, csvfile, names, types, delimiter=' '):
if len(names) != len(types):
raise ValueError('List sizes are not identical.')
self.__names = names
self.__types = types
self.__header_names = []
self.__columns = 0
for i in range(len(self.__names)):
size = serialize.get_item_size(self.__types[i])
self.__columns += size
if size > 1:
for j in range(size):
name = self.__names[i]+'_'+str(j)
self.__header_names.append(name)
else:
name = self.__names[i]
self.__header_names.append(name)
self.__writer = csv.writer(csvfile, delimiter=delimiter)
def writeheader(self):
self.__writer.writerow(self.__header_names)
def writerow(self, data_object):
data = []
for i in range(len(self.__names)):
size = serialize.get_item_size(self.__types[i])
value = data_object.__dict__[self.__names[i]]
if size > 1:
data.extend(value)
else:
data.append(value)
self.__writer.writerow(data)
\ No newline at end of file
#!/usr/bin/env python
# Copyright (c) 2020, Universal Robots A/S,
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the Universal Robots A/S nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL UNIVERSAL ROBOTS A/S BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import argparse
import logging
import sys
sys.path.append('..')
import third_party.rtde as rtde
import third_party.rtde.rtde_config as rtde_config
import third_party.rtde.csv_writer as csv_writer
import third_party.rtde.csv_binary_writer as csv_binary_writer
#parameters
parser = argparse.ArgumentParser()
parser.add_argument('--host', default='localhost', help='name of host to connect to (localhost)')
parser.add_argument('--port', type=int, default=30004, help='port number (30004)')
parser.add_argument('--samples', type=int, default=0, help='number of samples to record')
parser.add_argument('--frequency', type=int, default=125, help='the sampling frequency in Herz')
parser.add_argument('--config', default='record_configuration.xml', help='data configuration file to use (record_configuration.xml)')
parser.add_argument('--output', default='robot_data.csv', help='data output file to write to (robot_data.csv)')
parser.add_argument("--verbose", help="increase output verbosity", action="store_true")
parser.add_argument("--binary", help="save the data in binary format", action="store_true")
args = parser.parse_args()
if args.verbose:
logging.basicConfig(level=logging.INFO)
conf = rtde_config.ConfigFile(args.config)
output_names, output_types = conf.get_recipe('out')
con = rtde.RTDE(args.host, args.port)
con.connect()
# get controller version
con.get_controller_version()
# setup recipes
if not con.send_output_setup(output_names, output_types, frequency = args.frequency):
logging.error('Unable to configure output')
sys.exit()
#start data synchronization
if not con.send_start():
logging.error('Unable to start synchronization')
sys.exit()
writeModes = 'wb' if args.binary else 'w'
with open(args.output, writeModes) as csvfile:
writer = None
if args.binary:
writer = csv_binary_writer.CSVBinaryWriter(csvfile, output_names, output_types)
else:
writer = csv_writer.CSVWriter(csvfile, output_names, output_types)
writer.writeheader()
i = 1
keep_running = True
while keep_running:
if i%args.frequency == 0:
if args.samples > 0:
sys.stdout.write("\r")
sys.stdout.write("{:.2%} done.".format(float(i)/float(args.samples)))
sys.stdout.flush()
else:
sys.stdout.write("\r")
sys.stdout.write("{:3d} samples.".format(i))
sys.stdout.flush()
if args.samples > 0 and i >= args.samples:
keep_running = False
try:
state = con.receive(args.binary)
if state is not None:
writer.writerow(state)
i += 1
except KeyboardInterrupt:
keep_running = False
except rtde.RTDEException:
con.disconnect()
sys.exit()
sys.stdout.write("\rComplete! \n")
con.send_pause()
con.disconnect()
<?xml version="1.0"?>
<rtde_config>
<recipe key="out">
<field name="timestamp" type="DOUBLE"/>
<field name="target_q" type="VECTOR6D"/>
<field name="target_qd" type="VECTOR6D"/>
<field name="target_qdd" type="VECTOR6D"/>
<field name="target_current" type="VECTOR6D"/>
<field name="target_moment" type="VECTOR6D"/>
<field name="actual_q" type="VECTOR6D"/>
<field name="actual_qd" type="VECTOR6D"/>
<field name="actual_current" type="VECTOR6D"/>
<field name="joint_control_output" type="VECTOR6D"/>
<field name="actual_TCP_pose" type="VECTOR6D"/>
<field name="actual_TCP_speed" type="VECTOR6D"/>
<field name="actual_TCP_force" type="VECTOR6D"/>
<field name="target_TCP_pose" type="VECTOR6D"/>
<field name="target_TCP_speed" type="VECTOR6D"/>
<field name="actual_digital_input_bits" type="UINT64"/>
<field name="joint_temperatures" type="VECTOR6D"/>
<field name="actual_execution_time" type="DOUBLE"/>
<field name="robot_mode" type="INT32"/>
<field name="joint_mode" type="VECTOR6INT32"/>
<field name="safety_mode" type="INT32"/>
<field name="actual_tool_accelerometer" type="VECTOR3D"/>
<field name="speed_scaling" type="DOUBLE"/>
<field name="target_speed_fraction" type="DOUBLE"/>
<field name="actual_momentum" type="DOUBLE"/>
<field name="actual_main_voltage" type="DOUBLE"/>
<field name="actual_robot_voltage" type="DOUBLE"/>
<field name="actual_robot_current" type="DOUBLE"/>
<field name="actual_joint_voltage" type="VECTOR6D"/>
<field name="actual_digital_output_bits" type="UINT64"/>
<field name="runtime_state" type="UINT32"/>
<!--
<field name="script_control_line" type="UINT32"/>
<field name="output_bit_registers0_to_31" type="UINT32"/>
<field name="output_bit_registers32_to_63" type="UINT32"/>
<field name="output_int_register_0" type="INT32"/>
<field name="output_int_register_1" type="INT32"/>
<field name="output_int_register_2" type="INT32"/>
<field name="output_int_register_3" type="INT32"/>
<field name="output_int_register_4" type="INT32"/>
<field name="output_int_register_5" type="INT32"/>
<field name="output_int_register_6" type="INT32"/>
<field name="output_int_register_7" type="INT32"/>
<field name="output_int_register_8" type="INT32"/>
<field name="output_int_register_9" type="INT32"/>
<field name="output_int_register_10" type="INT32"/>
<field name="output_int_register_11" type="INT32"/>
<field name="output_int_register_12" type="INT32"/>
<field name="output_int_register_13" type="INT32"/>
<field name="output_int_register_14" type="INT32"/>
<field name="output_int_register_15" type="INT32"/>
<field name="output_int_register_16" type="INT32"/>