diff --git a/docs/robot_setup_manual.md b/docs/robot_setup_manual.md index 3545033..14fc1cb 100644 --- a/docs/robot_setup_manual.md +++ b/docs/robot_setup_manual.md @@ -4,7 +4,7 @@ ABB Motion Program Exec requires software to be installed on the robot. This sof installed manually by copying files to the robot controller and importing configuration files. This documents covers manually installing the software on a virtual controller in Robot Studio. -## Step 1: Install RobotWare 6.14 +## 1: Install RobotWare 6.14 **This section only needs to be completed once on RobotWare installation.** @@ -43,6 +43,61 @@ Check which model the "real" robot is if using a robot other than IRB1200. Norma version is fine for simulation. **The variant in terms of reach and payload is very important.** The other options often don't affect simulation. +## 3. Configure Project + +This step may be done one of two ways: + +1. By using the `abb_motion_program_exec.robotstudio_setup` submodule ([Automatically Configure Project](#automatically-configure-project)), or +2. Manually through configuration dialogs ([Manually Configure Project](#manually-configure-project)) + +### Automatically Configure Project + +With the `abb_motion_program_exec` library installed, the `abb-motion-program-exec-robotstudio-setup.exe` script is available to make configuration of projects easier, specifically accessed through CLI command: + +```none +abb-motion-program-exec-robotstudio-setup [-h] [-e] [-d directory] [-i] project + +positional arguments: + project Name of project to configure + +options: + -h, --help show this help message and exit + -e, --egm Use Externally Guided Motion + -d, --dir directory Directory of project if not in default location + -i, --inplace Use this option if script is run within project directory. + If used, the project name and argument -d are ignored; + however, a dummy project name is still required. +``` + +This command will perform all steps necessary to configure a newly project for basic operation or using externally guided motion (using the `-e` flag). This command **should not be used** on projects that have been significantly modified past the instructions above as changes may be overwritten. + +Before running this command, ensure that the target project is closed within RobotStudio by clicking "File" then "Close" or by closing the program. In both cases, the project should be saved. + +If a project, named for example `projectEx`, was created in the default RobotStudio projects directory then the project may be configured using: + +``` +abb-motion-program-exec-robotstudio-setup projectEx +``` + +or with + +``` +abb-motion-program-exec-robotstudio-setup --egm projectEx +``` + +for using externally guided motion. + +A successful configuration will produce an output such as: + +```none +Attempting to configure project : C:\Users\user\Documents\RobotStudio\Projects\ProjectEx\ + Option: Externally Controlled Motion +Configuring controller IRB6640_130_320 +Done! +``` + +### Manually Configure Project + Click on the "Controller" tab on the top. Then right click on the virtual control listed in the left tree view under "Current Station". In this example, the controller is "IRB1200_5_90", but it will vary depending on the project configuration. Click "Change Options" on the right-click @@ -105,7 +160,7 @@ more signals): ![](figures/robotstudio_addin_installed5.png) -## 3. Run Programs +## 4. Run Programs Install the module using `pip install .`. diff --git a/pyproject.toml b/pyproject.toml index 9c6ae3c..6b2e545 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,7 @@ repository = "https://github.com/rpiRobotics/abb_motion_program_exec" [project.scripts] abb-motion-program-exec-robotraconteur = "abb_motion_program_exec.robotraconteur.abb_motion_program_exec_robotraconteur:main" +abb-motion-program-exec-robotstudio-setup = "abb_motion_program_exec.robotstudio_setup.__main__:main" [project.optional-dependencies] test = [ @@ -37,5 +38,37 @@ robotraconteur = [ "robotraconteur-abstract-robot" ] +[tool.setuptools] +packages = [ + "abb_motion_program_exec", + "abb_motion_program_exec.robotstudio_setup", + "abb_motion_program_exec.commands", + "abb_motion_program_exec.robotraconteur", + "abb_motion_program_exec.robotstudio_setup.robot", + "abb_motion_program_exec.robotstudio_setup.robot.config_params", + "abb_motion_program_exec.robotstudio_setup.robot.config_params_egm", + "abb_motion_program_exec.robotstudio_setup.robot.config_params_multimove", + "abb_motion_program_exec.robotstudio_setup.robot.HOME", + "abb_motion_program_exec.robotstudio_setup.robot.autosetup", + ] + +[tool.setuptools.package-dir] +"abb_motion_program_exec" = "src/abb_motion_program_exec" +"abb_motion_program_exec.commands" = "src/abb_motion_program_exec/commands" +"abb_motion_program_exec.robotraconteur" = "src/abb_motion_program_exec/robotraconteur" +"abb_motion_program_exec.robotstudio_setup" = "src/abb_motion_program_exec/robotstudio_setup" +"abb_motion_program_exec.robotstudio_setup.robot" = "robot" +"abb_motion_program_exec.robotstudio_setup.robot.config_params" = "robot/config_params" +"abb_motion_program_exec.robotstudio_setup.robot.config_params_egm" = "robot/config_params_egm" +"abb_motion_program_exec.robotstudio_setup.robot.config_params_multimove" = "robot/config_params_multimove" +"abb_motion_program_exec.robotstudio_setup.robot.HOME" = "robot/HOME" +"abb_motion_program_exec.robotstudio_setup.robot.autosetup" = "robot/autosetup" + + [tool.setuptools.package-data] -"abb_motion_program_exec.robotraconteur" = ["*.robdef"] \ No newline at end of file +"abb_motion_program_exec.robotraconteur" = ["*.robdef"] +"abb_motion_program_exec.robotstudio_setup.robot.config_params" = ["*.*"] +"abb_motion_program_exec.robotstudio_setup.robot.config_params_egm" = ["*.*"] +"abb_motion_program_exec.robotstudio_setup.robot.config_params_multimove" = ["*.*"] +"abb_motion_program_exec.robotstudio_setup.robot.HOME" = ["*.*"] +"abb_motion_program_exec.robotstudio_setup.robot.autosetup" = ["*.*"] diff --git a/robot/autosetup/backinfo.tail b/robot/autosetup/backinfo.tail new file mode 100644 index 0000000..bef946a --- /dev/null +++ b/robot/autosetup/backinfo.tail @@ -0,0 +1,16 @@ +>>TASK0: (TASKSHARED,,) + +>>TASK1: (T_ROB1,,) +SYSMOD\user.sys @ +PROGMOD\motion_program_exec.mod @ + +>>TASK2: (logger,,) +SYSMOD\user.sys @ +PROGMOD\motion_program_logger.mod @ + +>>TASK3: (error_reporter,,) +PROGMOD\error_reporter.mod @ +SYSMOD\user.sys @ + + +>>EOF: diff --git a/robot/autosetup/backinfo_egm.tail b/robot/autosetup/backinfo_egm.tail new file mode 100644 index 0000000..59b6dc6 --- /dev/null +++ b/robot/autosetup/backinfo_egm.tail @@ -0,0 +1,17 @@ +>>TASK0: (TASKSHARED,,) + +>>TASK1: (T_ROB1,,) +SYSMOD\user.sys @ +PROGMOD\motion_program_exec_egm.mod @ +PROGMOD\motion_program_exec.mod @ + +>>TASK2: (logger,,) +SYSMOD\user.sys @ +PROGMOD\motion_program_logger.mod @ + +>>TASK3: (error_reporter,,) +PROGMOD\error_reporter.mod @ +SYSMOD\user.sys @ + + +>>EOF: diff --git a/src/abb_motion_program_exec/robotstudio_setup/__init__.py b/src/abb_motion_program_exec/robotstudio_setup/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/abb_motion_program_exec/robotstudio_setup/__main__.py b/src/abb_motion_program_exec/robotstudio_setup/__main__.py new file mode 100644 index 0000000..bfe7e6f --- /dev/null +++ b/src/abb_motion_program_exec/robotstudio_setup/__main__.py @@ -0,0 +1,19 @@ +import argparse +import os +from .project_setup import project_setup + + +def main(): + parser = argparse.ArgumentParser(description="Set up a new RobotStudio project for use with abb_motion_program_exec") + + parser.add_argument("project", help="Name of project to configure") + parser.add_argument("-e","--egm", action="store_true", help="Use Externally Guided Motion") + parser.add_argument("-d","--dir", metavar="directory", action="store", help="Directory of project if not in default location") + parser.add_argument("-i","--inplace", action="store_true", help="Use this option if script is run within project directory. If used, the project name and argument -d are ignored; however, a dummy project name is still required.") + parser.set_defaults(func=project_setup) + + args = parser.parse_args() + args.func(args) + +if __name__ == "__main__": + main() diff --git a/src/abb_motion_program_exec/robotstudio_setup/project_setup.py b/src/abb_motion_program_exec/robotstudio_setup/project_setup.py new file mode 100644 index 0000000..2a32113 --- /dev/null +++ b/src/abb_motion_program_exec/robotstudio_setup/project_setup.py @@ -0,0 +1,187 @@ +from importlib import resources as impre +import xml.etree.ElementTree as ET +import shutil +import os +import re + +def project_setup(args): + + # Set working directory + wdir = os.getcwd() + if args.inplace: + print("Attempting to configure current directory...") + else: + if args.dir: + wdir=args.dir + else: + wdir = os.path.expanduser("~") + "\\Documents\\RobotStudio\\Projects\\" + wdir = wdir + args.project + print("Attempting to configure project : {}\\".format(wdir)) + os.chdir(wdir) + if args.egm: + print(" Option: Externally Guided Motion") + + + # Set up controller(s) + try: + os.chdir("Controller Data") + with os.scandir() as ctrlrs: + for cntrlr in ctrlrs: + if not cntrlr.is_file(): # is a controller directory + apply_setup(cntrlr.name,egm=args.egm) + except FileNotFoundError: + print("Warning: No Controllers found") + return + # Delete any existing virtual controllers (RobotStudio will automatically + # regenerate with modified configurations + os.chdir('..') + shutil.rmtree("Virtual Controllers") + + +def apply_setup(cntrlr,egm=False): + # Lists of files to modify + hfiles = ['error_reporter.mod', + 'motion_program_exec.mod', + 'motion_program_logger.mod', + 'motion_program_shared.sys'] + cfiles = ['SYS.cfg','EIO.cfg'] + # Options to add to system.xml and backinfo.txt + options = ['616-1 PC Interface', + '623-1 Multitasking'] + options_short = ['pcinterface','multitasking'] + # RAPID tasks tree + rapidtasks = {'TASK0':[[],['motion_program_shared.sys']], + 'TASK1':[['motion_program_exec.mod'],['user.sys']], + 'TASK2':[['motion_program_logger.mod'],['user.sys']], + 'TASK3':[['error_reporter.mod'],['user.sys']]} + # backinfo.txt tail append file + backinfotail = 'backinfo.tail' + + from .robot import HOME as rhome + from .robot import autosetup as rautosetup + if egm: + from .robot import config_params_egm as config_params + hfiles.append('motion_program_exec_egm.mod') + cfiles.append('MOC.cfg') + options.append('689-1 Externally Guided Motion (EGM)') + options.append('UDPUC Driver') + options_short.append('externallyguidedmotion') + options_short.append('udpuc') + backinfotail = 'backinfo_egm.tail' + rapidtasks['TASK1'] = [['motion_program_exec_egm.mod','motion_program_exec.mod'],['user.sys']] + else: + from .robot import config_params as config_params + + print('Configuring controller {}'.format(cntrlr)) + + ### Modifying files + + # Copying HOME files + for file in hfiles: + src = impre.files(rhome).joinpath(file) + shutil.copyfile(src,"{}\\HOME\\{}".format(cntrlr,file)) + + # Setting config options in system.xml + cfgfile = ET.parse('{}\\system.xml'.format(cntrlr)) + cfgroot = cfgfile.getroot() + cfgmod = cfgroot.find('ControlModule') + for option in options: + ET.SubElement(cfgmod,'Option',descr=option) + ET.indent(cfgfile) + cfgfile.write('{}\\system.xml'.format(cntrlr),short_empty_elements=False,encoding="utf-8",xml_declaration=True) + + # Setting config options in BACKINFO\controller.rsf + cfgfile = ET.parse('{}\\BACKINFO\\controller.rsf'.format(cntrlr)) + cfgroot = cfgfile.getroot() + cfgstg = cfgroot.find('.//Settings') + for option,soption in zip(options,options_short): + ET.SubElement(cfgstg,'Setting',id='abb.robotics.robotware.options.{}'.format(soption),displayName=option,robot='1') + ET.indent(cfgfile) + cfgfile.write('{}\\BACKINFO\\controller.rsf'.format(cntrlr),encoding="utf-8",xml_declaration=True) + + + # SYSPAR files + for file in cfiles: + src = impre.files(config_params).joinpath(file) + dst = '{}\\SYSPAR\\{}'.format(cntrlr,file) + merge_cfg(src,dst) + + # BACKINFO/backinfo.txt + fbi = open("{}\\BACKINFO\\backinfo.txt".format(cntrlr),'r',newline='\r\n') + fbid = fbi.read() + fbi.close() + # Trim end of file where tasks start + fbid = fbid.split(">>TASK1")[0] + # Append the required tasks + bitailfile = impre.files(rautosetup).joinpath(backinfotail) + with open(bitailfile,'r',newline='\r\n') as bif: + fbid += bif.read() + # add the options + # first convert to lines + fbid = fbid.split('\r\n') + for idx,option in enumerate(options): + fbid.insert(8+idx," "+option) # add spaces to match format of backinfo.txt + # Overwrite original file + with open("{}\\BACKINFO\\backinfo.txt".format(cntrlr),'w',newline='\r\n') as bio: + for line in fbid: + bio.write(line+'\n') + + # Building RAPID tasks + shutil.rmtree("{}\\RAPID".format(cntrlr)) + for task in rapidtasks.keys(): + progdir = '{}\\RAPID\\{}\\PROGMOD'.format(cntrlr,task) + sysdir = '{}\\RAPID\\{}\\SYSMOD'.format(cntrlr,task) + os.makedirs(progdir) + os.makedirs(sysdir) + for file in rapidtasks[task][0]: + shutil.copy('{}\\HOME\\{}'.format(cntrlr,file),progdir) + for file in rapidtasks[task][1]: + shutil.copy('{}\\HOME\\{}'.format(cntrlr,file),sysdir) + + print("Done!") + +def merge_cfg(src,dst): + src_cfg = read_cfg(src) + dst_cfg = read_cfg(dst,removetask=True) + for skey,sval in src_cfg.items(): + if skey == 'HEADER': # skip the header + continue + if not skey in dst_cfg: + dst_cfg[skey] = '' + dst_cfg[skey] += sval + write_cfg(dst,dst_cfg) + +def read_cfg(src,removetask=False): + cfg_dict = {} + with open(src,'r') as fcfg: + line = fcfg.readline() + key = 'HEADER' + while line: + if not key: + key = fcfg.readline() + line = fcfg.readline() + val = '' + while line: + if line[0] == '#': + break + if removetask: + if key.startswith("CAB_TASKS"): + if "MotionTask" in line: + val = val[:-1] + line = fcfg.readline() + continue + val += line + line = fcfg.readline() + cfg_dict[key] = val + key = '' + return cfg_dict + +def write_cfg(dst,cfg): + with open(dst,'w') as fdst: + fdst.write(cfg['HEADER']) + for ckey,cval in cfg.items(): + if ckey == 'HEADER': + continue + fdst.write('#\n') + fdst.write(ckey) + fdst.write(cval)