From f332888543986bbcfdb3ac4ca0bc278cd5ff08f6 Mon Sep 17 00:00:00 2001 From: Kyle Wilt Date: Wed, 8 Jul 2026 16:29:23 -0400 Subject: [PATCH 1/5] Added automatic configuration of projects for basic and egm --- pyproject.toml | 32 +++- robot/autosetup/backinfo.tail | 16 ++ robot/autosetup/backinfo_egm.tail | 17 ++ src/abb_motion_program_exec/__main__.py | 29 +++ src/abb_motion_program_exec/project_setup.py | 187 +++++++++++++++++++ 5 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 robot/autosetup/backinfo.tail create mode 100644 robot/autosetup/backinfo_egm.tail create mode 100644 src/abb_motion_program_exec/__main__.py create mode 100644 src/abb_motion_program_exec/project_setup.py diff --git a/pyproject.toml b/pyproject.toml index 9c6ae3c..17248d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,5 +37,35 @@ robotraconteur = [ "robotraconteur-abstract-robot" ] +[tool.setuptools] +packages = [ + "abb_motion_program_exec", + "abb_motion_program_exec.commands", + "abb_motion_program_exec.robotraconteur", + "abb_motion_program_exec.robot", + "abb_motion_program_exec.robot.config_params", + "abb_motion_program_exec.robot.config_params_egm", + "abb_motion_program_exec.robot.config_params_multimove", + "abb_motion_program_exec.robot.HOME", + "abb_motion_program_exec.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.robot" = "robot" +"abb_motion_program_exec.robotraconteur" = "src/abb_motion_program_exec/robotraconteur" +"abb_motion_program_exec.robot.config_params" = "robot/config_params" +"abb_motion_program_exec.robot.config_params_egm" = "robot/config_params_egm" +"abb_motion_program_exec.robot.config_params_multimove" = "robot/config_params_multimove" +"abb_motion_program_exec.robot.HOME" = "robot/HOME" +"abb_motion_program_exec.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.robot.config_params" = ["*.*"] +"abb_motion_program_exec.robot.config_params_egm" = ["*.*"] +"abb_motion_program_exec.robot.config_params_multimove" = ["*.*"] +"abb_motion_program_exec.robot.HOME" = ["*.*"] +"abb_motion_program_exec.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/__main__.py b/src/abb_motion_program_exec/__main__.py new file mode 100644 index 0000000..fd55304 --- /dev/null +++ b/src/abb_motion_program_exec/__main__.py @@ -0,0 +1,29 @@ +import argparse +import os +from .project_setup import project_setup + +def func_example(args): + print("example test {}".format(args.example)) + +parser = argparse.ArgumentParser(description="abb_motion_program_exec help") +subparsers = parser.add_subparsers(dest="command",required=True) + +# project setup command +setup_parser = subparsers.add_parser("setup", help="Set up a new project for use with abb_motion_program_exec") +setup_parser.add_argument("project", help="Name of project to configure") +setup_parser.add_argument("-e","--egm", action="store_true", help="Use Externally Guided Motion") +setup_parser.add_argument("-d","--dir", metavar="directory", action="store", help="Directory of project if not in default location") +setup_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 project name must still be given") +setup_parser.set_defaults(func=project_setup) + +# copy example project +#setup_parser = subparsers.add_parser("example", help="Run an example from the abb_motion_program_exec repository") +#setup_parser.add_argument("example", help="Name of example to run") +#setup_parser.add_argument("-a","--address", action="store", default="127.0.0.1", help="Set the ip address of the target other than default (default: 127.0.0.1)") +#setup_parser.add_argument("-p","--port", action="store", default="80", help="Set the port of the target other than default (default: 80). This may alternatively be set by the -a option") +#setup_parser.set_defaults(func=func_example) + +args = parser.parse_args() +args.func(args) + + diff --git a/src/abb_motion_program_exec/project_setup.py b/src/abb_motion_program_exec/project_setup.py new file mode 100644 index 0000000..2a32113 --- /dev/null +++ b/src/abb_motion_program_exec/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) From c8bf3b13f67dd31c5cb96a86ad005d895f27261f Mon Sep 17 00:00:00 2001 From: Kyle Wilt Date: Wed, 8 Jul 2026 17:09:54 -0400 Subject: [PATCH 2/5] added documentation for automatic configuration of project --- docs/robot_setup_manual.md | 57 +++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/docs/robot_setup_manual.md b/docs/robot_setup_manual.md index 3545033..ed5b1fd 100644 --- a/docs/robot_setup_manual.md +++ b/docs/robot_setup_manual.md @@ -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 setup` command ([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, a setup script is exposed to make configuration of projects easier, specifically accessed through the `setup` command: + +```none +python -m abb_motion_program_exec 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: + +``` +python -m abb_motion_program_exec setup projectEx +``` + +or with + +``` +python -m abb_motion_program_exec setup -e 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 .`. From 023d347bb24298ab79b17505c8180b1af5b96bc2 Mon Sep 17 00:00:00 2001 From: Kyle Wilt Date: Fri, 10 Jul 2026 10:39:58 -0400 Subject: [PATCH 3/5] updating docs to use abb-motion-program-exec-robotstudio-setup script --- docs/robot_setup_manual.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/robot_setup_manual.md b/docs/robot_setup_manual.md index ed5b1fd..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.** @@ -47,15 +47,15 @@ other options often don't affect simulation. This step may be done one of two ways: -1. By using the `abb_motion_program_exec setup` command ([Automatically Configure Project](#automatically-configure-project)), or +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, a setup script is exposed to make configuration of projects easier, specifically accessed through the `setup` command: +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 -python -m abb_motion_program_exec setup [-h] [-e] [-d directory] [-i] project +abb-motion-program-exec-robotstudio-setup [-h] [-e] [-d directory] [-i] project positional arguments: project Name of project to configure @@ -66,7 +66,7 @@ options: -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 + 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. @@ -76,13 +76,13 @@ Before running this command, ensure that the target project is closed within Rob If a project, named for example `projectEx`, was created in the default RobotStudio projects directory then the project may be configured using: ``` -python -m abb_motion_program_exec setup projectEx +abb-motion-program-exec-robotstudio-setup projectEx ``` or with ``` -python -m abb_motion_program_exec setup -e projectEx +abb-motion-program-exec-robotstudio-setup --egm projectEx ``` for using externally guided motion. From 5da6d488543132a0f8cdc1e5840359660771773d Mon Sep 17 00:00:00 2001 From: Kyle Wilt Date: Fri, 10 Jul 2026 10:40:51 -0400 Subject: [PATCH 4/5] modified project setup functionality to be submodule robotstudio_setup --- pyproject.toml | 37 ++-- src/abb_motion_program_exec/__main__.py | 29 --- src/abb_motion_program_exec/project_setup.py | 187 ------------------- 3 files changed, 20 insertions(+), 233 deletions(-) delete mode 100644 src/abb_motion_program_exec/__main__.py delete mode 100644 src/abb_motion_program_exec/project_setup.py diff --git a/pyproject.toml b/pyproject.toml index 17248d2..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 = [ @@ -40,32 +41,34 @@ robotraconteur = [ [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.robot", - "abb_motion_program_exec.robot.config_params", - "abb_motion_program_exec.robot.config_params_egm", - "abb_motion_program_exec.robot.config_params_multimove", - "abb_motion_program_exec.robot.HOME", - "abb_motion_program_exec.robot.autosetup", + "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.robot" = "robot" "abb_motion_program_exec.robotraconteur" = "src/abb_motion_program_exec/robotraconteur" -"abb_motion_program_exec.robot.config_params" = "robot/config_params" -"abb_motion_program_exec.robot.config_params_egm" = "robot/config_params_egm" -"abb_motion_program_exec.robot.config_params_multimove" = "robot/config_params_multimove" -"abb_motion_program_exec.robot.HOME" = "robot/HOME" -"abb_motion_program_exec.robot.autosetup" = "robot/autosetup" +"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"] -"abb_motion_program_exec.robot.config_params" = ["*.*"] -"abb_motion_program_exec.robot.config_params_egm" = ["*.*"] -"abb_motion_program_exec.robot.config_params_multimove" = ["*.*"] -"abb_motion_program_exec.robot.HOME" = ["*.*"] -"abb_motion_program_exec.robot.autosetup" = ["*.*"] +"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/src/abb_motion_program_exec/__main__.py b/src/abb_motion_program_exec/__main__.py deleted file mode 100644 index fd55304..0000000 --- a/src/abb_motion_program_exec/__main__.py +++ /dev/null @@ -1,29 +0,0 @@ -import argparse -import os -from .project_setup import project_setup - -def func_example(args): - print("example test {}".format(args.example)) - -parser = argparse.ArgumentParser(description="abb_motion_program_exec help") -subparsers = parser.add_subparsers(dest="command",required=True) - -# project setup command -setup_parser = subparsers.add_parser("setup", help="Set up a new project for use with abb_motion_program_exec") -setup_parser.add_argument("project", help="Name of project to configure") -setup_parser.add_argument("-e","--egm", action="store_true", help="Use Externally Guided Motion") -setup_parser.add_argument("-d","--dir", metavar="directory", action="store", help="Directory of project if not in default location") -setup_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 project name must still be given") -setup_parser.set_defaults(func=project_setup) - -# copy example project -#setup_parser = subparsers.add_parser("example", help="Run an example from the abb_motion_program_exec repository") -#setup_parser.add_argument("example", help="Name of example to run") -#setup_parser.add_argument("-a","--address", action="store", default="127.0.0.1", help="Set the ip address of the target other than default (default: 127.0.0.1)") -#setup_parser.add_argument("-p","--port", action="store", default="80", help="Set the port of the target other than default (default: 80). This may alternatively be set by the -a option") -#setup_parser.set_defaults(func=func_example) - -args = parser.parse_args() -args.func(args) - - diff --git a/src/abb_motion_program_exec/project_setup.py b/src/abb_motion_program_exec/project_setup.py deleted file mode 100644 index 2a32113..0000000 --- a/src/abb_motion_program_exec/project_setup.py +++ /dev/null @@ -1,187 +0,0 @@ -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) From 99f471cc0f2fd4899b67638e87f74e422978d066 Mon Sep 17 00:00:00 2001 From: Kyle Wilt Date: Fri, 10 Jul 2026 10:41:49 -0400 Subject: [PATCH 5/5] adding the submodule directory which was missing from previous commit --- .../robotstudio_setup/__init__.py | 0 .../robotstudio_setup/__main__.py | 19 ++ .../robotstudio_setup/project_setup.py | 187 ++++++++++++++++++ 3 files changed, 206 insertions(+) create mode 100644 src/abb_motion_program_exec/robotstudio_setup/__init__.py create mode 100644 src/abb_motion_program_exec/robotstudio_setup/__main__.py create mode 100644 src/abb_motion_program_exec/robotstudio_setup/project_setup.py 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)