This page shows how to download and upload the files of a Yaskawa Motoman controller over FTP with the SDK: text, bytes, local files and several files at once, with the progress of each transfer. It covers the YRC1000 and YRC1000micro controllers. The connection and the accounts are explained in [FTP](/yaskawa/documentation/ftp).

## File names and paths

Each method takes a file name or a full path on the controller:

- A name, for example `TEST.JBI`: the SDK finds the folder from the extension. `.JBI` and `.JBR` go to `/JOB`, `.DAT` to `/DAT`, `.PRM` to `/PRM`, and so on for `.CND`, `.SYS`, `.LST`, `.CSV`, `.LOG` and `.TXT`.
- A full path, for example `/JOB/TEST.JBI`.

## Download

**C# : FtpDownload**
```csharp
using UnderAutomation.Yaskawa;

public class FtpDownload
{
    static void Main()
    {
        var parameters = new ConnectParameters("192.168.0.1");
        parameters.Ftp.Enable = true;
        parameters.Ftp.FtpUser = "ftp";
        var robot = new YaskawaRobot();
        robot.Connect(parameters);

        /**/
        // Text of a file: the folder is deduced from the extension
        string job = robot.Ftp.GetFile("TEST.JBI");

        // Bytes of a file, with its full path
        byte[] variables = robot.Ftp.DownloadFile("/DAT/VAR.DAT");

        // To a file of the PC
        robot.Ftp.DownloadFileToLocal("/PRM/ALL.PRM", @"C:\Backup\ALL.PRM");

        // Several files into a folder of the PC: returns the local paths
        string[] saved = robot.Ftp.DownloadFilesToLocal(new[] { "/JOB/TEST.JBI", "/DAT/VAR.DAT" }, @"C:\Backup");
        /**/

        robot.Disconnect();
    }
}
```

**Python : FtpDownload**
```python
from underautomation.yaskawa.yaskawa_robot import YaskawaRobot
from underautomation.yaskawa.connect_parameters import ConnectParameters

parameters = ConnectParameters("192.168.0.1")
parameters.ftp.enable = True
parameters.ftp.ftp_user = "ftp"
robot = YaskawaRobot()
robot.connect(parameters)

##
# Text of a file: the folder is deduced from the extension
job = robot.ftp.get_file("TEST.JBI")

# Bytes of a file, with its full path
variables = bytes(robot.ftp.download_file("/DAT/VAR.DAT"))

# To a file of the PC
robot.ftp.download_file_to_local("/PRM/ALL.PRM", r"C:\Backup\ALL.PRM")

# Several files into a folder of the PC: returns the local paths
saved = robot.ftp.download_files_to_local(["/JOB/TEST.JBI", "/DAT/VAR.DAT"], r"C:\Backup")
##

robot.disconnect()
```

| Method                                    | Result                                                                  |
| ----------------------------------------- | ----------------------------------------------------------------------- |
| `GetFile(name)`                           | Text of the file                                                        |
| `DownloadFile(path)`                      | Bytes of the file                                                       |
| `DownloadFileToLocal(path, localPath)`    | File saved on the PC. An existing local file is replaced. Nothing is written if the download fails |
| `DownloadFilesToLocal(paths, localFolder)` | Files saved in a folder of the PC, created if needed. Returns the local paths |
| `DownloadFileToStream(path, stream)`      | Bytes written to a .NET stream (.NET only)                              |

## Upload

### Rules of the controller

- Only jobs (`.JBI`, `.JBR`), condition files (`.CND`) and general data (`.DAT`) can be uploaded.
- The account must be `ftp` or `rcmaster`. `anonymous` cannot upload.
- The controller does not overwrite a job: delete it first, see [Delete a file](/yaskawa/documentation/ftp#delete_a_file). Otherwise the upload fails with the reason `JobAlreadyExists`.
- An empty file is refused: the SDK throws an `ArgumentException` before the transfer.
- The controller checks the syntax of a job when it receives it.

### Methods

**C# : FtpUpload**
```csharp
using UnderAutomation.Yaskawa;
using System.Text;

public class FtpUpload
{
    static void Main()
    {
        var parameters = new ConnectParameters("192.168.0.1");
        parameters.Ftp.Enable = true;
        parameters.Ftp.FtpUser = "ftp";
        var robot = new YaskawaRobot();
        robot.Connect(parameters);

        /**/
        // A job written on the PC: the folder is deduced from the extension
        string job = File.ReadAllText("NEWJOB.JBI");
        robot.Ftp.LoadFile("NEWJOB.JBI", job);

        // Bytes, with the full path on the controller
        robot.Ftp.UploadFile("/JOB/OTHER.JBI", Encoding.ASCII.GetBytes(job));

        // A file of the PC: the remote path is deduced from its name and extension
        robot.Ftp.UploadFileFromLocal(@"C:\Jobs\PICK.JBI");

        // Several files of the PC: returns the remote paths
        string[] sent = robot.Ftp.UploadFilesFromLocal(new[] { @"C:\Jobs\A.JBI", @"C:\Jobs\B.JBI" });
        /**/

        robot.Disconnect();
    }
}
```

**Python : FtpUpload**
```python
from underautomation.yaskawa.yaskawa_robot import YaskawaRobot
from underautomation.yaskawa.connect_parameters import ConnectParameters

parameters = ConnectParameters("192.168.0.1")
parameters.ftp.enable = True
parameters.ftp.ftp_user = "ftp"
robot = YaskawaRobot()
robot.connect(parameters)

##
# A job written on the PC: the folder is deduced from the extension
with open("NEWJOB.JBI", newline="") as f:
    job = f.read()
robot.ftp.load_file("NEWJOB.JBI", job)

# Bytes, with the full path on the controller
robot.ftp.upload_file("/JOB/OTHER.JBI", list(job.encode("ascii")))

# A file of the PC: the remote path is deduced from its name and extension
robot.ftp.upload_file_from_local(r"C:\Jobs\PICK.JBI")

# Several files of the PC: returns the remote paths
sent = robot.ftp.upload_files_from_local([r"C:\Jobs\A.JBI", r"C:\Jobs\B.JBI"])
##

robot.disconnect()
```

| Method                                   | Source                                                                  |
| ---------------------------------------- | ----------------------------------------------------------------------- |
| `LoadFile(name, text)`                   | Text                                                                    |
| `UploadFile(path, bytes)`                | Bytes                                                                   |
| `UploadFileFromLocal(localPath, path)`   | File of the PC. Without `path`, the name of the local file is used      |
| `UploadFilesFromLocal(localPaths)`       | Several files of the PC, each with its own name. Stops at the first error. Returns the remote names |
| `UploadFileFromStream(path, stream)`     | A .NET stream, read from its current position (.NET only)               |

## Progress

The transfer methods take an optional callback. It receives the progress in percent, from 0 to 100, or -1 when the size is not known. For several files, the progress is the global progress.

**C# : FtpProgress**
```csharp
using UnderAutomation.Yaskawa;

public class FtpProgress
{
    static void Main()
    {
        var parameters = new ConnectParameters("192.168.0.1");
        parameters.Ftp.Enable = true;
        parameters.Ftp.FtpUser = "ftp";
        var robot = new YaskawaRobot();
        robot.Connect(parameters);

        /**/
        // Percentage from 0 to 100, -1 when the size is not known
        byte[] backup = robot.Ftp.DownloadFile("/PRM/ALL.PRM",
            progress => Console.WriteLine($"{progress:F0} %"));
        /**/

        robot.Disconnect();
    }
}
```

**Python : FtpProgress**
```python
from underautomation.yaskawa.yaskawa_robot import YaskawaRobot
from underautomation.yaskawa.connect_parameters import ConnectParameters

parameters = ConnectParameters("192.168.0.1")
parameters.ftp.enable = True
parameters.ftp.ftp_user = "ftp"
robot = YaskawaRobot()
robot.connect(parameters)

##
# Percentage from 0 to 100, -1 when the size is not known
backup = robot.ftp.download_file("/PRM/ALL.PRM",
    lambda progress: print(f"{progress:.0f} %"))
##

robot.disconnect()
```

In Python, pass a function or a lambda.

## Async methods

In .NET, every FTP method has an async version that takes a `CancellationToken`: `ConnectAsync`, `GetFileAsync`, `DownloadFilesToLocalAsync`, `UploadFileFromLocalAsync`... Use them in a user interface, so that a long transfer does not block it. They are not available on .NET Framework 3.5 and 4.0, and not in Python.

**C# : FtpAsync**
```csharp
using UnderAutomation.Yaskawa.Ftp;

public class FtpAsync
{
    static async Task Main()
    {
        /**/
        var client = new FtpClient();
        await client.ConnectAsync("192.168.0.1", "ftp");

        // Every FTP method has an async version with a CancellationToken
        using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(1));
        string[] jobs = await client.GetFileListByPatternAsync("*.JBI", cts.Token);
        string[] saved = await client.DownloadFilesToLocalAsync(
            jobs.Select(j => "/JOB/" + j).ToArray(), @"C:\Backup", null, cts.Token);

        client.Close();
        /**/
    }
}
```

## Reference

**Methods of FtpClientBase**
```csharp
// Downloads a file from the controller and returns its content.
byte[] DownloadFile(string remotePath, OnProgressDelegate progress = null);

// Downloads a file from the controller and saves it on the local file system. Overwrites the local file if it already exists. The local file is not created if the download fails.
void DownloadFileToLocal(string remotePath, string localPath, OnProgressDelegate progress = null);

// Downloads a file from the controller and writes its content into a stream.
void DownloadFileToStream(string remotePath, Stream destination, OnProgressDelegate progress = null);

// Downloads several files from the controller into a local folder. Each file is saved with its name, and existing local files are overwritten.
string[] DownloadFilesToLocal(string[] remotePaths, string localFolder, OnProgressDelegate progress = null);

// Downloads a text file from the robot controller and returns its content.
string GetFile(string fileName);

// Lists the files of the specified type on the controller.
string[] GetFileList(FileExtension fileExtension);

// Lists the files whose names match the specified pattern. When the pattern has a known extension (e.g. "*.JBI"), only the matching folder is listed. Otherwise, all folders of the controller are listed.
string[] GetFileListByPattern(string pattern);

// Uploads text content to the robot controller as a file. The file type is given by the extension of fileName (e.g. ".JBI" for a job).
void LoadFile(string fileName, string content);

// Uploads a byte array as a file onto the controller. Only jobs (.JBI, .JBR), condition files (.CND) and general data (.DAT) can be uploaded, with the "ftp" or "rcmaster" user. An existing job is not overwritten: delete it first with DeleteFile(System.String).
void UploadFile(string remotePath, byte[] data, OnProgressDelegate progress = null);

// Uploads a local file onto the controller. Only jobs (.JBI, .JBR), condition files (.CND) and general data (.DAT) can be uploaded, with the "ftp" or "rcmaster" user. An existing job is not overwritten: delete it first with DeleteFile(System.String).
void UploadFileFromLocal(string localPath, string remotePath = null, OnProgressDelegate progress = null);

// Uploads the content of a stream as a file onto the controller. The stream is read from its current position to its end. Only jobs (.JBI, .JBR), condition files (.CND) and general data (.DAT) can be uploaded, with the "ftp" or "rcmaster" user. An existing job is not overwritten: delete it first with DeleteFile(System.String).
void UploadFileFromStream(string remotePath, Stream source, OnProgressDelegate progress = null);

// Uploads several local files onto the controller. Each file is uploaded with its local file name. The controller stores each file in the folder of its type (e.g. a ".JBI" file goes to the JOB folder). The upload stops at the first error.
string[] UploadFilesFromLocal(string[] localPaths, OnProgressDelegate progress = null);
```

Every method also exists in an asynchronous version, with the same name followed by `Async` and an optional `CancellationToken`.

**Members of Ftp.OnProgressDelegate**
```csharp
public delegate void OnProgressDelegate(double progress) {
    public OnProgressDelegate(object @object, nint method)

    public virtual IAsyncResult BeginInvoke(double progress, AsyncCallback callback, object @object)

    public virtual void EndInvoke(IAsyncResult result)

    public virtual void Invoke(double progress)
}
```

## What to read next

- [Transfer files and backups](/yaskawa/documentation/how-to-transfer-files): a complete backup program, and which protocol to choose.
- [Kinematics models](/yaskawa/documentation/kinematics-models): read the geometry of the robot from its `ALL.PRM` file.