# NI analog write task

> Learn how to send analog commands to NI devices with Synnax.

This task follows the standard [task lifecycle](https://docs.synnaxlabs.com/reference/driver/task-basics#task-lifecycle).

## Task configuration reference

| Parameter    | Type   | Required | Default | Description                                   |
| ------------ | ------ | -------- | ------- | --------------------------------------------- |
| `device`     | string | Yes      | -       | Device key to write to                        |
| `state_rate` | number | No       | `10`    | Rate (Hz) at which state channels are updated |
| `channels`   | array  | Yes      | -       | List of analog output channel configurations  |

## Channel types reference

Voltage ([`ao_voltage`](https://www.ni.com/docs/en-US/bundle/ni-daqmx-c-api-ref/page/group__ni-daqmx__c__functions__channel__configuration__creation__create__analog__output__channel_1gad278075876eeff4223fdb90ce58f0a15.html))

| Parameter       | Type   | Required | Default | Description                |
| --------------- | ------ | -------- | ------- | -------------------------- |
| `cmd_channel`   | number | Yes      | -       | Synnax command channel key |
| `state_channel` | number | Yes      | -       | Synnax state channel key   |
| `port`          | number | Yes      | -       | Physical port              |
| `min_val`       | number | No       | `0`     | Minimum voltage (Volts)    |
| `max_val`       | number | No       | `1`     | Maximum voltage (Volts)    |
| `custom_scale`  | object | No       | -       | Custom scaling             |

Current ([`ao_current`](https://www.ni.com/docs/en-US/bundle/ni-daqmx-c-api-ref/page/group__ni-daqmx__c__functions__channel__configuration__creation__create__analog__output__channel_1ga1ae6afadb89de214c4823ca7144ab28d.html))

| Parameter       | Type   | Required | Default | Description                |
| --------------- | ------ | -------- | ------- | -------------------------- |
| `cmd_channel`   | number | Yes      | -       | Synnax command channel key |
| `state_channel` | number | Yes      | -       | Synnax state channel key   |
| `port`          | number | Yes      | -       | Physical port              |
| `min_val`       | number | No       | `0`     | Minimum current (Amps)     |
| `max_val`       | number | No       | `1`     | Maximum current (Amps)     |
| `custom_scale`  | object | No       | -       | Custom scaling             |

Function generator ([`ao_func_gen`](https://www.ni.com/docs/en-US/bundle/ni-daqmx-c-api-ref/page/group__ni-daqmx__c__functions__channel__configuration__creation__create__analog__output__channel_1ga7146d8ebdf2336e5200c4d2549f67494.html))

| Parameter       | Type   | Required | Default | Description                              |
| --------------- | ------ | -------- | ------- | ---------------------------------------- |
| `cmd_channel`   | number | Yes      | -       | Synnax command channel key               |
| `state_channel` | number | Yes      | -       | Synnax state channel key                 |
| `port`          | number | Yes      | -       | Physical port                            |
| `wave_type`     | enum   | No       | `Sine`  | `Sine`, `Triangle`, `Square`, `Sawtooth` |
| `frequency`     | number | No       | `0`     | Waveform frequency (Hz)                  |
| `amplitude`     | number | No       | `0`     | Waveform amplitude                       |
| `offset`        | number | No       | `0`     | Waveform DC offset                       |

## Important rules

* **One task per module** -> Only one running task can claim a module at a time.

## How-to

**Console**

[](https://synnax.nyc3.cdn.digitaloceanspaces.com/docs/device-drivers/ni/analog-write-task/ni-analog-write-dark.mp4#t=0.001)

**Python**

Configure and run task

```python
import synnax as sy
from synnax import ni

client = sy.Synnax()

# Retrieve device
c_dev = client.devices.retrieve(name="Mod1_Current")

# Create command and state time indices
ao_cmd_time = client.channels.create(
    name="ao_cmd_time",
    is_index=True,
    data_type=sy.DataType.TIMESTAMP,
    retrieve_if_name_exists=True,
)
ao_state_time = client.channels.create(
    name="ao_state_time",
    is_index=True,
    data_type=sy.DataType.TIMESTAMP,
    retrieve_if_name_exists=True,
)

# Create command and state channels
current_cmd = client.channels.create(
    name="current_cmd",
    index=ao_cmd_time.key,
    data_type=sy.DataType.FLOAT32,
    retrieve_if_name_exists=True,
)
current_state = client.channels.create(
    name="current_state",
    index=ao_state_time.key,
    data_type=sy.DataType.FLOAT32,
    retrieve_if_name_exists=True,
)

# Create and configure task
task = ni.AnalogWriteTask(
    name="NI Analog Write Task",
    device=c_dev.key,
    state_rate=sy.Rate.HZ * 20,
    channels=[
        ni.AOCurrentChannel(
            cmd_channel=current_cmd.key,
            state_channel=current_state.key,
            port=0,
            min_val=0.004,
            max_val=0.02,
        ),
    ],
)

client.tasks.configure(task)

# Start task and write commands
with task.run():
    with client.open_writer(
        start=sy.TimeStamp.now(),
        channels=["current_cmd"],
    ) as writer:
        # Set output to 0.01A
        writer.write({
            "current_cmd": 0.01,
        })

        # Read back state
        with client.open_streamer(["current_state"]) as streamer:
            frame = streamer.read()
            print(f"State: {frame['current_state']}")
```

Edit task configuration

```python
# Retrieve existing task
task = client.tasks.retrieve(name="NI Analog Write Task")
task = ni.AnalogWriteTask(internal=task)

# Update task-level configuration
task.config.auto_start = True
task.config.state_rate = int(sy.Rate.HZ * 50)

# Update channel-level configuration
task.config.channels[0].port = 1
task.config.channels[0].min_val = 0.002
task.config.channels[0].max_val = 0.01

# Apply changes
client.tasks.configure(task)
```

**TypeScript**

Configure and run task

```typescript
import { Synnax, TimeStamp, Rate } from "@synnaxlabs/client";

const client = new Synnax();

// Retrieve device
const [cDev] = await client.devices.retrieve({ names: ["Mod1_Current"] });

// Create command and state time indices
const aoCmdTime = await client.channels.create(
  {
    name: "ao_cmd_time",
    isIndex: true,
    dataType: "timestamp",
  },
  { retrieveIfNameExists: true },
);
const aoStateTime = await client.channels.create(
  {
    name: "ao_state_time",
    isIndex: true,
    dataType: "timestamp",
  },
  { retrieveIfNameExists: true },
);

// Create command and state channels
const currentCmd = await client.channels.create(
  {
    name: "current_cmd",
    index: aoCmdTime.key,
    dataType: "float32",
  },
  { retrieveIfNameExists: true },
);
const currentState = await client.channels.create(
  {
    name: "current_state",
    index: aoStateTime.key,
    dataType: "float32",
  },
  { retrieveIfNameExists: true },
);

// Create and configure task
const task = await client.tasks.create({
  name: "NI Analog Write Task",
  rack: cDev.rack,
  type: "ni_analog_write",
  config: {
    device: cDev.key,
    stateRate: 20,
    channels: [
      {
        type: "ao_current",
        cmdChannel: currentCmd.key,
        stateChannel: currentState.key,
        port: 0,
        minVal: 0.004,
        maxVal: 0.02,
      },
    ],
  },
});

// Start task
await task.start();

// Write command
const writer = await client.openWriter({
  start: TimeStamp.now(),
  channels: ["current_cmd"],
});

await writer.write({
  current_cmd: 0.01,
});

// Read state
const streamer = await client.openStreamer(["current_state"]);
const frame = await streamer.read();
console.log(`State: ${frame.get("current_state").at(-1)}`);

// Stop task
await task.stop();
await writer.close();
await streamer.close();
```

Edit task configuration

```typescript
import { ni } from "@synnaxlabs/client";

// Retrieve existing task
const task = await client.tasks.retrieve({ name: "NI Analog Write Task" });

// Parse and update configuration
const config = ni.analogWriteConfigZ.parse(task.config);

// Update task-level configuration
config.autoStart = true;
config.stateRate = 50;

// Update channel-level configuration
const current = config.channels[0] as ni.AOCurrentChannel;
current.port = 1;
current.minVal = 0.002;
current.maxVal = 0.01;

// Apply changes
await client.tasks.create({
  key: task.key,
  rack: task.rack,
  name: task.name,
  type: task.type,
  config,
});
```
