A RAPID program is a set of modules, and a module is a text file the controller keeps in memory. `robot.Rws.Rapid` loads and saves these files, reads and rewrites their source, and reports what the controller refused when it linked them.

Everything on this page that writes needs the `Rapid` [mastership](/abb/documentation/rws-mastership). Editing the source of a task that is running is possible, but the controller can refuse a change that would invalidate the program pointer.

## Program files

A program is a `.pgf` file naming the modules it holds. `GetProgram` returns the program of a task, `null` when the task holds none.

**C# : RapidProgramFiles**
```csharp
using UnderAutomation.ABB;
using UnderAutomation.ABB.Rws.Data;

public class RapidProgramFiles
{
    static void Main()
    {
        AbbController robot = new AbbController();
        robot.Connect("192.168.0.1");

        /**/
        // The program the task holds, null when it holds none
        RapidProgramInfo program = robot.Rws.Rapid.GetProgram("T_ROB1");
        if (program != null)
            Console.WriteLine(program.Name + ", entry point " + program.EntryPoint);

        robot.Rws.Mastership.Request(MastershipDomain.Rapid);
        try
        {
            // The program file has to be on the controller already.
            // Upload it first with robot.Rws.File.
            robot.Rws.Rapid.LoadProgram("T_ROB1", "$HOME/myprogram.pgf", RapidProgramLoadMode.Replace);

            // Write every module of the task into a directory of the controller
            robot.Rws.Rapid.SaveProgram("T_ROB1", "$HOME/myprograms");

            robot.Rws.Rapid.SetProgramName("T_ROB1", "myprogram");
            robot.Rws.Rapid.SetEntryPoint("T_ROB1", "main");

            robot.Rws.Rapid.UnloadProgram("T_ROB1");
        }
        finally
        {
            robot.Rws.Mastership.Release(MastershipDomain.Rapid);
        }

        // Loading returns before the controller has finished, so read what it refused
        foreach (RapidBuildError error in robot.Rws.Rapid.GetBuildErrors("T_ROB1"))
            Console.WriteLine(error.ModuleName + " " + error.Row + "," + error.Column + ": " + error.Error);
        /**/

        robot.Disconnect();
    }
}
```

| `RapidProgramLoadMode` | What happens to the modules already loaded           |
| ---------------------- | ---------------------------------------------------- |
| `Add`                  | They are kept, the modules of the program are added  |
| `Replace`              | Everything the task holds is replaced by the program |

Points to know:

- The file has to be on the file system of the controller already. Upload it first with the [file system service](/abb/documentation/rws-files).
- `LoadProgram` returns before the controller has finished loading. Read the task state and the build errors afterwards, do not assume the program is ready.
- `SaveProgram` writes the modules of the task into a directory of the controller, not on your PC. Download them afterwards.
- `SetEntryPoint` sets the routine `ResetProgramPointer` goes back to, usually `main`.
- Loading one module instead of a whole program is done with `LoadModule`, see [RAPID tasks & program execution](/abb/documentation/rws-rapid-tasks).







## Modules of a task

`GetModules` lists the modules a task holds. `GetModule` gives the file a module came from and the properties declared on it.

**C# : RapidModuleList**
```csharp
using UnderAutomation.ABB;
using UnderAutomation.ABB.Rws.Data;

public class RapidModuleList
{
    static void Main()
    {
        AbbController robot = new AbbController();
        robot.Connect("192.168.0.1");

        /**/
        foreach (RapidModuleItem module in robot.Rws.Rapid.GetModules("T_ROB1"))
        {
            Console.WriteLine(module.Name + " " + module.Type);   // ProgramModule or SystemModule

            // The file the module came from and the properties declared on it
            RapidModuleInfo info = robot.Rws.Rapid.GetModule("T_ROB1", module.Name);
            Console.WriteLine(info.FileName);

            foreach (RapidModuleAttribute attribute in info.Attributes)
                Console.WriteLine("   " + attribute);             // SystemModule, NoStepIn, ...

            // How big the source is, in lines and columns
            RapidModuleExtension size = robot.Rws.Rapid.GetModuleExtension("T_ROB1", module.Name);
            Console.WriteLine(size.LineCount + " lines, " + size.MaxColumnCount + " columns");

            // A counter that only moves when the module changes, cheaper than reading it again
            Console.WriteLine(robot.Rws.Rapid.GetModuleChangeCount("T_ROB1", module.Name));
        }

        // Which of these properties this module accepts
        RapidModuleAttribute[] possible = robot.Rws.Rapid.GetPossibleModuleAttributes(
            "T_ROB1", "MainModule", RapidModuleAttribute.NoStepIn, RapidModuleAttribute.ViewOnly);
        Console.WriteLine(possible.Length);

        // Write one module as a file of the controller, the extension is added by the controller
        robot.Rws.Rapid.SaveModule("T_ROB1", "MainModule", "MainModule", "$HOME");
        /**/

        robot.Disconnect();
    }
}
```

| `RapidModuleAttribute` | What the module declares                                           |
| ---------------------- | ------------------------------------------------------------------ |
| `SystemModule`         | The module belongs to the system, it is not saved with the program |
| `ReadOnly`             | The source cannot be changed                                       |
| `ViewOnly`             | The source can be read but not changed                             |
| `NoView`               | The source cannot even be read                                     |
| `NoStepIn`             | Stepping does not enter the routines of the module                 |
| `Encoded`              | The source is stored encoded                                       |

`GetModuleChangeCount` returns a counter that only moves when the module changes. Comparing it with the previous reading is much cheaper than downloading the source again to find out that nothing moved. `GetModuleExtension` gives the number of lines and the longest line of the source.

### Read and edit the source

> **Available on** RWS 1.0 (IRC5) : yes | RWS 2.0 (OmniCore) : yes. On an IRC5 reading the whole source costs two requests and DeclaredLength stays empty.

**C# : RapidModuleSource**
```csharp
using UnderAutomation.ABB;
using UnderAutomation.ABB.Rws.Data;

public class RapidModuleSource
{
    static void Main()
    {
        AbbController robot = new AbbController();
        robot.Connect("192.168.0.1");

        /**/
        // The whole source of the module
        RapidModuleText source = robot.Rws.Rapid.GetModuleText("T_ROB1", "MainModule");
        Console.WriteLine(source.Text);
        Console.WriteLine(source.ChangeCount);

        // Only a few lines. Rows and columns are counted from 1, and the controller clamps
        // the end of the range to what the module really holds.
        Console.WriteLine(robot.Rws.Rapid.GetModuleTextRange("T_ROB1", "MainModule", 1, 1, 10, 1));

        // Where a piece of text sits, Found is false when it is nowhere
        RapidTextPosition found = robot.Rws.Rapid.SearchModuleText("T_ROB1", "MainModule", "MoveJ");
        Console.WriteLine(found.Found + " " + found.Row + "," + found.Column);

        robot.Rws.Mastership.Request(MastershipDomain.Rapid);
        try
        {
            // Insert one line after a range, leaving the rest of the module alone
            RapidSetTextRangeResult result = robot.Rws.Rapid.SetModuleTextRange(
                "T_ROB1", "MainModule",
                RapidTextReplaceMode.After, RapidTextQueryMode.Try,
                5, 1, 5, 1,
                "    reg1 := 0;\r\n");

            // Rewriting the line that declares the module renames it
            Console.WriteLine(result.ModuleRenamed + " " + result.NewModuleName);

            // Replace the whole source
            robot.Rws.Rapid.SetModuleText("T_ROB1", "MainModule",
                "MODULE MainModule\r\n  PROC main()\r\n  ENDPROC\r\nENDMODULE\r\n");
        }
        finally
        {
            robot.Rws.Mastership.Release(MastershipDomain.Rapid);
        }
        /**/

        robot.Disconnect();
    }
}
```

Rows and columns are counted from 1, not from 0. The controller clamps the end of a range to what the module really holds, so a range running past the last line is not an error.

`SetModuleTextRange` writes into a range and returns what the controller did with the change.

| `RapidTextReplaceMode` | Where the new text goes                                |
| ---------------------- | ------------------------------------------------------ |
| `Replace`              | The range is replaced by the new text                  |
| `Before`               | The new text is inserted before the range, which stays |
| `After`                | The new text is inserted after the range, which stays  |

| `RapidTextQueryMode` | When the program pointer would become invalid                  |
| -------------------- | -------------------------------------------------------------- |
| `Try`                | The controller refuses the change                              |
| `Force`              | The controller applies it anyway and drops the program pointer |

Rewriting the line that declares the module renames it. This is why the result carries `ModuleRenamed` and `NewModuleName`. Use the new name in the calls that follow, the old one no longer exists.

`SearchModuleText` reports row and column 0 when it finds nothing, it does not fail. Test `Found` rather than the row.

`SaveModule` writes one module as a file of the controller. The controller adds the extension itself, so pass `MainModule` and not `MainModule.mod`.























## Build errors

Linking a program never fails the request itself. The controller accepts it, then reports what it refused. `GetBuildErrors` returns one entry per error, with the module, the position and the message.

**C# : RapidBuildErrors**
```csharp
using UnderAutomation.ABB;
using UnderAutomation.ABB.Rws.Data;

public class RapidBuildErrors
{
    static void Main()
    {
        AbbController robot = new AbbController();
        robot.Connect("192.168.0.1");

        /**/
        // The controller answers at most thirty errors at a time, read the next ones by paging
        int start = 0;
        RapidBuildError[] errors = robot.Rws.Rapid.GetBuildErrors("T_ROB1", start, 30);

        while (errors.Length > 0)
        {
            foreach (RapidBuildError error in errors)
            {
                Console.WriteLine(error.ModuleName + " " + error.Row + "," + error.Column);
                Console.WriteLine("   " + error.Error);
            }

            start += errors.Length;
            errors = robot.Rws.Rapid.GetBuildErrors("T_ROB1", start, 30);
        }

        // An empty answer and a task state of Linked mean the program is runnable
        Console.WriteLine(robot.Rws.Rapid.GetTask("T_ROB1").TaskState);
        /**/

        robot.Disconnect();
    }
}
```

The controller returns at most thirty errors at a time, whatever larger limit is asked for. Use `start` and `limit` to read the rest. An empty array means the program linked cleanly, and the task state then becomes `Linked`.

`BuildTask` itself is described in [RAPID tasks & program execution](/abb/documentation/rws-rapid-tasks).





## Breakpoints

`SetBreakpoint` places a breakpoint at a position of a module. The controller snaps it to the whole instruction holding that position, so the range it answers is normally wider than what was asked for.

**C# : RapidBreakpoints**
```csharp
using UnderAutomation.ABB;
using UnderAutomation.ABB.Rws.Data;

public class RapidBreakpoints
{
    static void Main()
    {
        AbbController robot = new AbbController();
        robot.Connect("192.168.0.1");

        /**/
        robot.Rws.Mastership.Request(MastershipDomain.Rapid);
        try
        {
            // The controller snaps the breakpoint to the whole instruction holding the
            // position, so the range it answers is normally wider than what was asked for
            RapidBreakpoint placed = robot.Rws.Rapid.SetBreakpoint("T_ROB1", "MainModule", 12, 1);
            Console.WriteLine(placed.StartRow + "," + placed.StartColumn + " -> " +
                              placed.EndRow + "," + placed.EndColumn);
        }
        finally
        {
            robot.Rws.Mastership.Release(MastershipDomain.Rapid);
        }

        foreach (RapidBreakpoint breakpoint in robot.Rws.Rapid.GetBreakpoints("T_ROB1"))
            Console.WriteLine(breakpoint.ModuleName + " " + breakpoint.StartRow);

        // A breakpoint only stops the program when execution was started with stopAtBreakpoint
        robot.Rws.Rapid.Start(RapidRegainMode.Continue, RapidExecutionMode.Continue,
                              RapidExecutionCycle.Forever, RapidStartCondition.None, true, false);
        /**/

        robot.Disconnect();
    }
}
```

A breakpoint only stops the program when execution was started with `stopAtBreakpoint` set to `true`, see [RAPID tasks & program execution](/abb/documentation/rws-rapid-tasks). Editing the source moves the positions, so read the breakpoints again after a change.





## Modify a taught position

This is the teaching gesture of the FlexPendant: jog the robot where it should go, then write that position back into the motion instruction of the program.

**C# : RapidModifyPosition**
```csharp
using UnderAutomation.ABB;
using UnderAutomation.ABB.Rws.Data;

public class RapidModifyPosition
{
    static void Main()
    {
        AbbController robot = new AbbController();
        robot.Connect("192.168.0.1");

        /**/
        // How many motion instructions of this range can be rewritten
        RapidModifiablePositions modifiable =
            robot.Rws.Rapid.GetModifiablePositions("T_ROB1", "MainModule", 1, 1, 40, 1);
        Console.WriteLine(modifiable.ModifiableLineCount);

        // Every one of them, in every task of the system
        foreach (RapidModifiablePositionItem item in robot.Rws.Rapid.GetAllModifiablePositions())
            Console.WriteLine(item.TaskName + "/" + item.ModuleName + " line " + item.StartRow);

        // Jog the robot where it should go, then write that position into the program
        robot.Rws.Mastership.Request(MastershipDomain.Rapid);
        try
        {
            robot.Rws.Rapid.ModifyPosition("T_ROB1", "MainModule", 12, 1, 12, 1, true, true, false);
        }
        finally
        {
            robot.Rws.Mastership.Release(MastershipDomain.Rapid);
        }
        /**/

        robot.Disconnect();
    }
}
```

`GetModifiablePositions` says how many motion instructions of a range can be rewritten, `GetAllModifiablePositions` lists every one of them in every task of the system. Read one of the two before writing, so you know what is about to change.

- `checkLimits` makes the controller refuse a position outside the working range.
- `checkDeactivatedAxes` makes it refuse to rewrite an axis that is deactivated, and `allowDeactivated` rewrites it anyway.
- `ModifyAllPositions` rewrites everything at once. The controller only accepts it from a client it considers local.

Jogging the robot to the position is done with the [motion system service](/abb/documentation/rws-motion), and it needs the `Motion` [mastership](/abb/documentation/rws-mastership), which is a different domain from `Rapid`.







## Write an editor

The last group of methods exists for the applications that edit RAPID, not for the ones that drive a robot. They give what an editor needs: what is declared at a position, the arguments of a call, a complete instruction ready to be written, and the palette an operator picks an instruction from.

**C# : RapidSourceNavigation**
```csharp
using UnderAutomation.ABB;
using UnderAutomation.ABB.Rws.Data;

public class RapidSourceNavigation
{
    static void Main()
    {
        AbbController robot = new AbbController();
        robot.Connect("192.168.0.1");

        /**/
        // What is declared at this position of the source, null when nothing is
        RapidModuleSymbol symbol = robot.Rws.Rapid.GetModuleSymbol("T_ROB1", "MainModule", 12, 5);
        if (symbol != null)
            Console.WriteLine(symbol.Name + " " + symbol.SymbolType + " " + symbol.DataType);

        // The routine called at this position, and the arguments of that call
        RapidRoutineInfo routine = robot.Rws.Rapid.GetRoutine("T_ROB1", "MainModule", 12, 5);
        Console.WriteLine(routine.Name + " " + routine.ParameterCount);

        foreach (RapidRoutineArgument argument in
                 robot.Rws.Rapid.GetRoutineArguments("T_ROB1", "MainModule", 12, 5))
        {
            Console.WriteLine(argument.DataType + " at " + argument.StartRow + "," + argument.StartColumn);
        }

        // A complete instruction ready to be written, instead of a bare keyword
        RapidInstructionTemplate template = robot.Rws.Rapid.GetInstructionTemplate("T_ROB1", "MainModule", "MoveJ");
        foreach (RapidInstructionTemplateArgument argument in template.Arguments)
            Console.WriteLine(argument.Name + " = " + argument.Value + " (" + argument.DataType + ")");

        // Where the parts of an object begin and end, given the whole span of that object
        RapidObjectChild children = robot.Rws.Rapid.GetObjectChildren("T_ROB1", "MainModule", 1, 1, 40, 1);
        foreach (RapidObjectChildRange range in children.Ranges)
            Console.WriteLine(range.Name + ": " + range.Range);

        // Where one of the lists an object holds sits, without reading the module
        RapidObjectListExtension list = robot.Rws.Rapid.GetObjectListExtension(
            "RAPID/T_ROB1/MainModule", RapidObjectListType.RoutineDeclarations);
        Console.WriteLine(list.List + " first " + list.First + " last " + list.Last);
        /**/

        robot.Disconnect();
    }
}
```

`GetModuleSymbol` returns the declaration found at a position, `null` when there is none. `GetRoutine` and `GetRoutineArguments` work on a position sitting on a routine call, and the controller refuses the request when it does not.

`GetInstructionTemplate` returns a complete instruction with the arguments the controller suggests, instead of a bare keyword. `GetObjectChildren` and `GetObjectListExtension` give where the parts of an object begin and end, which is how an editor jumps to the declarations of a module without reading the whole source.

**C# : RapidEditorPalette**
```csharp
using UnderAutomation.ABB;
using UnderAutomation.ABB.Rws.Data;

public class RapidEditorPalette
{
    static void Main()
    {
        AbbController robot = new AbbController();
        robot.Connect("192.168.0.1");

        /**/
        // Categories of the instruction palette, the same ones an editor shows
        foreach (RapidPalletHeadItem head in robot.Rws.Rapid.GetPalletHeads("T_ROB1"))
        {
            Console.WriteLine(head.Number + ": " + head.Name);

            // Instructions of that category
            foreach (RapidPalletItem item in robot.Rws.Rapid.GetPallet("T_ROB1", head.Number.Value))
                Console.WriteLine("   " + item.Name + " " + item.Instruction);
        }

        // Types the controller suggests for one argument of an instruction
        foreach (RapidPreferredDataTypeItem type in
                 robot.Rws.Rapid.GetPreferredDataTypes("T_ROB1", "AliasIO", "FromSignal"))
        {
            Console.WriteLine(type.Name + " (" + type.DataType + ")");
        }
        /**/

        robot.Disconnect();
    }
}
```

`GetPalletHeads` returns the categories of the instruction palette, `GetPallet` the instructions of one category, and `GetPreferredDataTypes` the types that fit one argument of an instruction.



























## What is not on this page

The values the modules declare are read and written from [RAPID variables & symbols](/abb/documentation/rws-rapid-symbols). Starting the program, moving the program pointer and loading one module are on [RAPID tasks & program execution](/abb/documentation/rws-rapid-tasks). Uploading a module file to the controller, and downloading a saved one, are on [File system](/abb/documentation/rws-files).