├── .gitignore ├── LICENSE ├── README.md └── abb_rapid.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # UV 98 | # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | #uv.lock 102 | 103 | # poetry 104 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 105 | # This is especially recommended for binary packages to ensure reproducibility, and is more 106 | # commonly ignored for libraries. 107 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 108 | #poetry.lock 109 | 110 | # pdm 111 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 112 | #pdm.lock 113 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 114 | # in version control. 115 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 116 | .pdm.toml 117 | .pdm-python 118 | .pdm-build/ 119 | 120 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 121 | __pypackages__/ 122 | 123 | # Celery stuff 124 | celerybeat-schedule 125 | celerybeat.pid 126 | 127 | # SageMath parsed files 128 | *.sage.py 129 | 130 | # Environments 131 | .env 132 | .venv 133 | env/ 134 | venv/ 135 | ENV/ 136 | env.bak/ 137 | venv.bak/ 138 | 139 | # Spyder project settings 140 | .spyderproject 141 | .spyproject 142 | 143 | # Rope project settings 144 | .ropeproject 145 | 146 | # mkdocs documentation 147 | /site 148 | 149 | # mypy 150 | .mypy_cache/ 151 | .dmypy.json 152 | dmypy.json 153 | 154 | # Pyre type checker 155 | .pyre/ 156 | 157 | # pytype static type analyzer 158 | .pytype/ 159 | 160 | # Cython debug symbols 161 | cython_debug/ 162 | 163 | # PyCharm 164 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 165 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 166 | # and can be added to the global gitignore or merged into this file. For a more nuclear 167 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 168 | #.idea/ 169 | 170 | # PyPI configuration file 171 | .pypirc 172 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Madeline Gannon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python to RAPID Generator for ABB Robots 2 | Use this python class to generate simple RAPID modules for programming ABB robots. 3 | 4 | ## Installation 5 | No installation is required. Just include `rapid.py` in your project and import the `RAPID` class. 6 | 7 | ## Functions 8 | | Function Name | Description | 9 | |----------------------------------|--------------------------------------------------------------| 10 | | `set_tool(...)` | Creates a tooldata variable for the RAPID program. | 11 | | `set_zone(...)` | Creates a zone variable for the RAPID program. | 12 | | `set_speed(...)` | Creates a speed data structure for the RAPID program. | 13 | | `set_workobject(...)` | Creates a workobject variable for the RAPID program. | 14 | | `set_robtarget(...)` | Creates a robot target variable for positioning. | 15 | | `add_MoveL(...)` | Creates a linear move instruction. | 16 | | `add_MoveJ(...)` | Creates a joint move instruction. | 17 | | `add_MoveAbsJ(...)` | Creates an absolute joint move instruction. | 18 | | `add_wait(...)` | Creates a wait instruction. | 19 | | `add_wait_digital_input(...)` | Creates a wait for digital input instruction. | 20 | | `set_digital_output(...)` | Creates a set digital output instruction. | 21 | | `set_analog_output(...)` | Creates a set analog output instruction. | 22 | | `add_comment(...)` | Creates a comment in the RAPID program. | 23 | | `add_print_statement(...)` | Creates a TPWrite instruction for printing messages. | 24 | | `begin_module(...)` | Starts a new module in the RAPID program. | 25 | | `end_module()` | Ends the current module in the RAPID program. | 26 | | `save_module(...)` | Saves the generated RAPID program to a file. | 27 | 28 | ## Usage Example 29 | 30 | ```python 31 | from abb_rapid import RAPID, Zone 32 | 33 | filename = "my_module.mod" 34 | test_points = [(0, 250, 300), (0, 250, 800), 35 | (0, -250, 800), (0, -250, 300)] 36 | 37 | # Initialize the RAPID generator 38 | module = RAPID() 39 | 40 | # Set the module and procedure names 41 | module.begin_module(name="TestModule", name_procedure="main") 42 | 43 | # Create tool, zone and speed variables at the beginning of the module 44 | module.add_comment("Variables must be defined at the top of the module") 45 | module.set_tool(name="tool") 46 | module.set_tool(name="gripper", tool_frame_pos=[ 47 | 0, 0, 100], load_data_mass=2) 48 | module.set_zone(name="zone") 49 | module.set_zone(name="zone_10", val=Zone.Z10) 50 | module.set_speed(name="speed_slow", velocity_tcp=50, velocity_orient=50) 51 | module.set_speed(name="speed_fast", velocity_tcp=500, velocity_orient=150) 52 | module.set_workobject(name="wobj", uf_frame_pos=[800, 0, 0]) 53 | 54 | # Example of how to use signal functions 55 | module.add_comment("Examples of how to use signal functions") 56 | module.set_digital_output(name="do1", value=1) 57 | module.add_wait(time=1) 58 | module.set_digital_output(name="do1", value=0) 59 | module.add_wait(time=1) 60 | module.set_analog_output(name="ao1", value=50) 61 | module.add_wait(time=1) 62 | module.set_analog_output(name="ao1", value=0) 63 | module.add_wait(time=1) 64 | 65 | # Example of how to use motion functions 66 | module.add_comment("Examples of how to use motion functions") 67 | module.add_MoveAbsJ(joint_positions=[0, 0, 0, 0, 0, 0], name_speed="speed_slow", name_zone="zone") 68 | for point in test_points: 69 | module.add_MoveL(list(point), [1, 0, 0, 0], name_speed="speed_fast", name_tool="tool", name_zone="zone_10") 70 | for point in test_points: 71 | module.add_MoveJ(list(point), [1, 0, 0, 0], name_speed="speed_fast", name_tool="gripper", name_zone="zone") 72 | 73 | # Example of how to use print statements 74 | module.add_comment("Examples of how to use TP Write functions") 75 | module.add_print_statement("Hello, world!") 76 | module.add_print_statement("Number of MoveL points:", len(test_points)) 77 | module.add_print_statement("We done?", True) 78 | module.add_print_statement("We really done?", "Yes") 79 | 80 | module.end_module() 81 | 82 | module.save_module(filename) 83 | print(f"RAPID module saved as {filename}") 84 | ``` 85 | ### Output 86 | ```mod 87 | MODULE TestModule 88 | PROC main() 89 | ! Variables must be defined at the top of the module 90 | VAR tooldata tool:=[TRUE,[[0, 0, 0],[1, 0, 0, 0]],[0,[0, 0, 0],[1, 0, 0, 0],0,0,0]]; 91 | VAR tooldata gripper:=[TRUE,[[0, 0, 100],[1, 0, 0, 0]],[2,[0, 0, 0],[1, 0, 0, 0],0,0,0]]; 92 | VAR zonedata zone:=z0; 93 | VAR zonedata zone_10:=z10; 94 | VAR speeddata speed_slow:=[50,50,0,0]; 95 | VAR speeddata speed_fast:=[500,150,0,0]; 96 | VAR wobjdata wobj:=[FALSE,TRUE,"",[[800, 0, 0],[1, 0, 0, 0]],[[0, 0, 0],[1, 0, 0, 0]]]; 97 | ! Examples of how to use signal functions 98 | SetDO do1, 1; 99 | WaitTime 1; 100 | SetDO do1, 0; 101 | WaitTime 1; 102 | SetAO ao1, 50; 103 | WaitTime 1; 104 | SetAO ao1, 0; 105 | WaitTime 1; 106 | ! Examples of how to use motion functions 107 | MoveAbsJ [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]], speed_slow, zone, tool\Wobj:=wobj; 108 | MoveL [[0, 250, 300],[1, 0, 0, 0],[0, 0, 0, 1],[0, 0, 0, 0, 0, 0]], speed_fast, zone_10, tool\Wobj:=wobj; 109 | MoveL [[0, 250, 800],[1, 0, 0, 0],[0, 0, 0, 1],[0, 0, 0, 0, 0, 0]], speed_fast, zone_10, tool\Wobj:=wobj; 110 | MoveL [[0, -250, 800],[1, 0, 0, 0],[0, 0, 0, 1],[0, 0, 0, 0, 0, 0]], speed_fast, zone_10, tool\Wobj:=wobj; 111 | MoveL [[0, -250, 300],[1, 0, 0, 0],[0, 0, 0, 1],[0, 0, 0, 0, 0, 0]], speed_fast, zone_10, tool\Wobj:=wobj; 112 | MoveJ [[0, 250, 300],[1, 0, 0, 0],[0, 0, 0, 1],[0, 0, 0, 0, 0, 0]], speed_fast, zone, gripper\Wobj:=wobj; 113 | MoveJ [[0, 250, 800],[1, 0, 0, 0],[0, 0, 0, 1],[0, 0, 0, 0, 0, 0]], speed_fast, zone, gripper\Wobj:=wobj; 114 | MoveJ [[0, -250, 800],[1, 0, 0, 0],[0, 0, 0, 1],[0, 0, 0, 0, 0, 0]], speed_fast, zone, gripper\Wobj:=wobj; 115 | MoveJ [[0, -250, 300],[1, 0, 0, 0],[0, 0, 0, 1],[0, 0, 0, 0, 0, 0]], speed_fast, zone, gripper\Wobj:=wobj; 116 | ! Examples of how to use TP Write functions 117 | TPWrite "Hello, world!"; 118 | TPWrite "Number of MoveL points:"\Num:=4; 119 | TPWrite "We done?"\Bool:=TRUE; 120 | TPWrite "We really done? Yes"; 121 | ENDPROC 122 | ENDMODULE 123 | ``` 124 | -------------------------------------------------------------------------------- /abb_rapid.py: -------------------------------------------------------------------------------- 1 | """ 2 | Simple python class that generates RAPID modules for ABB robots. 3 | 4 | Author: Madeline Gannon (atonaton.com) 5 | Date: 02.08.2025 6 | """ 7 | 8 | from enum import Enum 9 | 10 | 11 | class Zone(Enum): 12 | Z0 = 0 13 | Z5 = 5 14 | Z10 = 10 15 | Z50 = 50 16 | Z100 = 100 17 | Z200 = 200 18 | 19 | 20 | class RAPID: 21 | 22 | def __init__(self): 23 | self.program: str = "" 24 | self.name_wobj = None 25 | 26 | def set_tool( 27 | self, 28 | name: str = "tool", 29 | rob_hold: bool = True, 30 | tool_frame_pos=[0, 0, 0], 31 | tool_frame_orient=[1, 0, 0, 0], 32 | load_data_mass=0, 33 | load_data_cog=[0, 0, 0], 34 | load_data_orient=[1, 0, 0, 0], 35 | load_data_inertia=[0, 0, 0]): 36 | """Creates a tooldata variable for the RAPID program. 37 | 38 | Args: 39 | name (str, optional): Tool variable name. Defaults to "tool". 40 | rob_hold (bool, optional): Flag for if robot is holding tool. Defaults to True. 41 | tool_frame_pos (list, optional): Position of Tool Frame. Defaults to [0, 0, 0]. 42 | tool_frame_orient (list, optional): Orientation of Tool Frame. Defaults to [1, 0, 0, 0]. 43 | load_data_mass (int, optional): Tool Load Mass. Defaults to 0. 44 | load_data_cog (list, optional): Tool Load Center of Gravity. Defaults to [0, 0, 0]. 45 | load_data_orient (list, optional): Tool Load Orientation. Defaults to [1, 0, 0, 0]. 46 | load_data_inertia (list, optional): Tool Load Inertia. Defaults to [0, 0, 0]. 47 | """ 48 | 49 | tool = f"VAR tooldata {name}:=[{str(rob_hold).upper()},[{tool_frame_pos},{tool_frame_orient}],[{ 50 | load_data_mass},{load_data_cog},{load_data_orient},{load_data_inertia[0]},{load_data_inertia[1]},{load_data_inertia[2]}]];" 51 | self.program += tool + "\n" 52 | 53 | def set_zone(self, name="zone", val=Zone.Z0): 54 | """Creates a zone variable for the RAPID program. 55 | 56 | Args: 57 | name (str, optional): Zone variable name. Defaults to "zone". 58 | val (int, optional): Zone value. Valid options are 0, 5, 10, 50, 100, 200. Defaults to 0 (fine). 59 | """ 60 | 61 | zone = f"VAR zonedata {name}:=z{val.value};" 62 | self.program += zone + "\n" 63 | 64 | def set_speed(self, name="speed", velocity_tcp=50, velocity_orient=50, velocity_leax=0, velocity_reax=0): 65 | """Creates a speed data structure for the RAPID program. 66 | 67 | Args: 68 | name(str, optional): Speed variable name. Defaults to "speed". 69 | velocity_tcp(int, optional): TCP Velocity. Defaults to "50". 70 | velocity_orient(int, optional): Orientation Velocity. Defaults to "50". 71 | velocity_leax(int, optional): Linear External Axis Velocity. Defaults to "0". 72 | velocity_reax(int, optional): Rotary External Axis Velocity. Defaults to "0". 73 | """ 74 | 75 | speed = f"VAR speeddata {name}:=[{velocity_tcp},{ 76 | velocity_orient},{velocity_leax},{velocity_reax}];" 77 | self.program += speed + "\n" 78 | 79 | def set_workobject( 80 | self, 81 | name="wobj", 82 | rob_hold=False, 83 | uf_prog=True, 84 | uf_mecunit="", 85 | uf_frame_pos=[0, 0, 0], 86 | uf_frame_orient=[1, 0, 0, 0], 87 | of_frame_pos=[0, 0, 0], 88 | of_frame_orient=[1, 0, 0, 0] 89 | ): 90 | """Creates a workobject variable for the RAPID program. 91 | 92 | Args: 93 | name (str, optional): Work object variable name. Defaults to "wobj". 94 | rob_hold (bool, optional): Whether the robot holds the work object. Defaults to False. 95 | uf_prog (bool, optional): Whether user frame is used. Defaults to True. 96 | uf_mecunit (str, optional): User mechanical unit name defined in system parameters. Defaults to "". 97 | uf_frame_pos (list, optional): User frame position [X, Y, Z]. Defaults to [0, 0, 0]. 98 | uf_frame_orient (list, optional): User frame orientation [Q1, Q2, Q3, Q4]. Defaults to [1, 0, 0, 0]. 99 | of_frame_pos (list, optional): Object frame position [X, Y, Z]. Defaults to [0, 0, 0]. 100 | of_frame_orient (list, optional): Object frame orientation [Q1, Q2, Q3, Q4]. Defaults to [1, 0, 0, 0]. 101 | """ 102 | self.name_wobj = name 103 | wobj = ( 104 | f"VAR wobjdata {name}:=[{str(rob_hold).upper()},{str(uf_prog).upper()},\"{uf_mecunit}\"," 105 | f"[{uf_frame_pos},{uf_frame_orient}],[{of_frame_pos},{of_frame_orient}]];" 106 | ) 107 | self.program += wobj + "\n" 108 | 109 | def set_robtarget(self, pos=[0, 0, 0], orient=[1, 0, 0, 0], confdata=[0, 0, 0, 1], extax=[0, 0, 0, 0, 0, 0]): 110 | """_summary_ 111 | 112 | Args: 113 | pos (list, optional): Position. Defaults to [0, 0, 0]. 114 | orient (list, optional): Orientation. Defaults to [1, 0, 0, 0]. 115 | confdata (list, optional): Configuration. Defaults to [0, 0, 0, 1]. 116 | extax (list, optional): Position of each external axis. Defaults to [0, 0, 0, 0, 0, 0]. 117 | 118 | Returns: 119 | string: robtarget 120 | """ 121 | return f"[{pos},{orient},{confdata},{extax}]" 122 | 123 | def add_MoveL(self, pos=[0, 0, 0], orient=[1, 0, 0, 0], confdata=[0, 0, 0, 1], extax=[0, 0, 0, 0, 0, 0], name_speed="speed", name_zone="zone", name_tool="tool"): 124 | """Creates a linear move. 125 | 126 | Args: 127 | pos (list, optional): Position. Defaults to [0, 0, 0]. 128 | orient (list, optional): Orientation. Defaults to [1, 0, 0, 0]. 129 | confdata (list, optional): Configuration. Defaults to [0, 0, 0, 1]. 130 | extax (list, optional): Position of each external axis. Defaults to [0, 0, 0, 0, 0, 0]. 131 | name_speed (str, optional): Name of speeddata variable. Defaults to "speed". 132 | name_zone (str, optional): Name of zonedata variable. Defaults to "zone". 133 | name_tool (str, optional): Name of tooldata variable. Defaults to "tool". 134 | 135 | """ 136 | self.program += f"MoveL {self.set_robtarget(pos, orient, confdata, extax)}, { 137 | name_speed}, {name_zone}, {name_tool}" 138 | if self.name_wobj is not None: 139 | self.program += f"\\Wobj:={self.name_wobj}" 140 | self.program += ";\n" 141 | 142 | def add_MoveJ(self, pos=[0, 0, 0], orient=[1, 0, 0, 0], confdata=[0, 0, 0, 1], extax=[0, 0, 0, 0, 0, 0], name_speed="speed", name_zone="zone", name_tool="tool"): 143 | """Creates a joint move. 144 | 145 | Args: 146 | pos (list, optional): Position. Defaults to [0, 0, 0]. 147 | orient (list, optional): Orientation. Defaults to [1, 0, 0, 0]. 148 | confdata (list, optional): Configuration. Defaults to [0, 0, 0, 1]. 149 | extax (list, optional): Position of each external axis. Defaults to [0, 0, 0, 0, 0, 0]. 150 | name_speed (str, optional): Name of speeddata variable. Defaults to "speed". 151 | name_zone (str, optional): Name of zonedata variable. Defaults to "zone". 152 | name_tool (str, optional): Name of tooldata variable. Defaults to "tool". 153 | """ 154 | self.program += f"MoveJ {self.set_robtarget(pos, orient, confdata, extax)}, {name_speed}, {name_zone}, {name_tool}" 155 | if self.name_wobj is not None: 156 | self.program += f"\\Wobj:={self.name_wobj}" 157 | self.program += ";\n" 158 | 159 | def add_MoveAbsJ(self, joint_positions=[0, 0, 0, 0, 0, 0], extax=[0, 0, 0, 0, 0, 0], name_speed="speed", name_zone="zone", name_tool="tool"): 160 | """Creates an absolute joint move. 161 | 162 | Args: 163 | joint_positions (list, optional): Angle of each joint (in degrees). Defaults to [0, 0, 0, 0, 0, 0]. 164 | extax (list, optional): Position of each external axis. Defaults to [0, 0, 0, 0, 0, 0]. 165 | name_speed (str, optional): Name of speeddata variable. Defaults to "speed". 166 | name_zone (str, optional): Name of zonedata variable. Defaults to "zone". 167 | name_tool (str, optional): Name of tooldata variable. Defaults to "tool". 168 | """ 169 | 170 | self.program += f"MoveAbsJ [{joint_positions}, {extax}], {name_speed}, {name_zone}, {name_tool}" 171 | if self.name_wobj is not None: 172 | self.program += f"\\Wobj:={self.name_wobj}" 173 | self.program += ";\n" 174 | 175 | def add_wait(self, time): 176 | """Creates a wait instruction. 177 | 178 | Args: 179 | time (int, optional): Time to wait (in seconds). 180 | """ 181 | self.program += f"WaitTime {time};" + "\n" 182 | 183 | def add_wait_digital_input(self, name, value): 184 | """Creates a wait digital input instruction. 185 | 186 | Args: 187 | name (str): Name of digital input signal. 188 | value (int): Value to wait for (0 or 1). 189 | """ 190 | self.program += f"WaitDI {name}, {value};" + "\n" 191 | 192 | def set_digital_output(self, name, value): 193 | """Creates a set digital output instruction. 194 | 195 | Args: 196 | name (str): Name of digital output signal. 197 | value (int): Value to set (0 or 1). 198 | """ 199 | self.program += f"SetDO {name}, {value};" + "\n" 200 | 201 | def set_analog_output(self, name, value): 202 | """Creates a set analog output instruction. 203 | 204 | Args: 205 | name (str): Name of analog output signal. 206 | value (int): Value to set (0-100). 207 | """ 208 | self.program += f"SetAO {name}, {value};" + "\n" 209 | 210 | def add_comment(self, msg): 211 | """Creates a comment. 212 | 213 | Args: 214 | msg (str): Comment message. 215 | """ 216 | self.program += f"! {msg}" + "\n" 217 | 218 | def add_print_statement(self, msg, val=None): 219 | """Creates a TPWrite instruction. 220 | 221 | Args: 222 | msg (str): Message to write. 223 | val (str, optional): Value to write. Defaults to None. 224 | """ 225 | if val is not None: 226 | if isinstance(val, bool): 227 | # Convert True/False to RAPID-style TRUE/FALSE 228 | self.program += f'TPWrite "{msg}"\\Bool:={str(val).upper()};\n' 229 | elif isinstance(val, int): 230 | self.program += f'TPWrite "{msg}"\\Num:={val};\n' 231 | elif isinstance(val, float): 232 | self.program += f'TPWrite "{msg}"\\Dnum:={val};\n' 233 | elif isinstance(val, str): 234 | self.program += f'TPWrite "{msg} {val}";\n' 235 | else: 236 | raise TypeError(f"Unsupported type for val: {type(val)}") 237 | else: 238 | self.program += f"TPWrite \"{msg}\";" + "\n" 239 | 240 | def begin_module(self, name="MainModule", name_procedure="main"): 241 | self.program += f"MODULE {name}\nPROC {name_procedure}()" + "\n" 242 | 243 | def end_module(self): 244 | self.program += "ENDPROC\nENDMODULE" 245 | 246 | def save_module(self, filename): 247 | with open(filename, 'w') as file: 248 | file.write(self.program) 249 | --------------------------------------------------------------------------------