AAMotion

AAMotion is a C# .NET API for advanced motion controller communication and control, designed for high-performance and flexible integration with industrial motion systems.

Overview

AAMotion provides a comprehensive interface for connecting to, configuring, and controlling a wide range of motion controllers. The API supports both high-level and low-level operations, enabling users to perform tasks such as initialization, connection, axis control, IO operations, and advanced motion programming.

  • Supported platforms: Windows 10 or later (Windows 7 SP1 or later on the .NET Framework 4.8 build)
  • Supported frameworks: .NET Framework 4.8, .NET 8, .NET 10
  • Documentation: API documentation is available via IntelliSense (XML doc comments included with the package).

System Requirements

  • Microsoft Windows 10 or later; Windows 7 SP1 or later for the .NET Framework 4.8 build
  • .NET Framework 4.8, .NET 8 or .NET 10
  • Visual Studio 2022 or later (other IDEs may be used, but are not officially supported)

Installation

Install the NuGet package from nuget.org:

dotnet add package Agito.AAMotion

Or via the NuGet Package Manager in Visual Studio.

Getting Started

  1. Create a new C# project targeting .NET Framework 4.8, .NET 8 or .NET 10.
  2. Add the Agito.AAMotion NuGet package.
  3. Explore the API via IntelliSense — XML doc comments are included with the package.

Usage Example

Below is a minimal example demonstrating how to initialize a controller, connect to it, perform basic axis operations, and shut down:

using System; 
using System.Diagnostics; 
using AAMotion;

namespace ConsoleApp1 
{ 
    class Program 
    { 
        static void Main(string[] args) 
        { 
            const string ip = "172.1.1.101"; // the controller's factory default
            MotionController controller = AAMotionAPI.Initialize(ControllerType.AGM800);
            try
            {
                Console.WriteLine($"Connecting to {ip}");
                controller.Init();

                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();

                bool isConnected = AAMotionAPI.Connect(controller, ip);

                stopwatch.Stop();
                Console.WriteLine($"Connect execution time: {stopwatch.ElapsedMilliseconds} ms");

                if (isConnected)
                {
                    Console.WriteLine("Connect successful");
                    AAMotionAPI.MotorOn(controller, AxisRef.B);

                    // Example: perform additional axis or IO operations here
                }
                else
                {
                    Console.WriteLine("Connect failed");
                }
            }
            finally
            {
                AAMotionAPI.MotorOff(controller, AxisRef.B);
                Console.WriteLine($"MotorOff status for Axis B: {controller.GetAxis(AxisRef.B).MotorOn}");

                // Release THIS controller's resources. The shared AACommServer keeps running
                // for other clients; controller.Shutdown() would request a server shutdown,
                // which the server honors only when this is its last client — call it only
                // when you own the machine's comms lifecycle.
                controller.Dispose();
            }
        }
    }
}

Firmware simulation

The legacy file-based echo simulator (ConnectSimulator) was removed in 12.0.0. Local firmware simulation now runs through AACommServer's managed ChannelType.Simulation channel: the server spawns a firmware_sim.exe child, configures the motor, and bridges it over loopback.

The AAMotion package does not ship firmware_sim.exe. Provide the absolute path to a copy — for example one extracted from a firmware release's AG300_MAS01_sim_<product>_v<version>.zip, or one PCSuite imported under Documents\PCSuite\ApplicationData\Simulations\<product>\<version>\. A sibling sim.info in that folder is used by PCSuite's version picker only; this API reads the exe path alone.

controller.Init();

string simExe = @"C:\Users\me\Documents\PCSuite\ApplicationData\Simulations\AGD301\5.3.0\firmware_sim.exe";
bool ok = AAMotionAPI.ConnectSimulation(controller, simExe);

// Optional: pass catalog labels and a custom motor profile
// ok = AAMotionAPI.ConnectSimulation(controller, simExe, "AGD301", "5.0.0", "counts_per_rev=8000\npole_pairs=2\n");

if (ok)
{
    // ... operate against the simulated controller ...
}

controller.Disconnect();   // reaps the firmware_sim child via AACommServer
controller.Dispose();

simProduct is forwarded to the server, and a value the product catalog resolves also declares the session's controller type. That lets the connect verify the type rather than discover it, and discovering it costs a probe that spawns and reaps a whole extra firmware_sim child before the session's own. A declared type the sim disagrees with fails the connect — ConnectSimulation returns false, and the underlying InvalidControllerType appears in the AAComm debug log.

What resolves is the ProductTypes catalog spelling: AGM800, AGD155, AGD155-EC, AGD101-EC, AGD156-EC, DRV6-V3, Pulse-Sync, AGM800-EC and AGM400; the paired products under either half of their name — AGD301 and AGD200, the Simulations\<product>\ folder prefixes (including the one used above), or AGC301 and AGC300 — or their paired spelling (AGD301orAGC301, AGD200orAGC300); and the older spellings of the products that have them (AGD301/AGC301, AG300-CTL02, CiG1-MAS02, …). Passing myType.ToString() for a ControllerType resolves for every member but the four EtherCAT ones — AGD155EC is catalogued AGD155-EC.

An unresolved name discovers the type by probe, exactly as omitting the argument does.

TryReconnect is Ethernet-only and cannot recover a simulation link. To reopen one, call Disconnect() first and then ConnectSimulation(...). ConnectSimulation does not tear down a link it finds open, and the connect short-circuits on one, so calling it alone on a wedged session returns false without reopening anything — LastKnownAddress and AutoReconnectEnabled are recorded before the attempt, so a failed call still updates them.

User Units

When the connected controller has Global User Units armed for an axis, AAMotion carries that axis's scaled parameters in the controller's display units. The groups are position (main, auxiliary, pulse-direction), velocity (the same three), acceleration and force: on an axis configured at 0.01 mm/count, Pos reads 24.68 rather than 2468, and an assignment is converted back to counts on the way out. The conversion is applied by the communication layer, per parameter, from the group membership and per-axis factor it reads at connect. A controller with no Global User Units armed is unaffected, and so is one using the firmware's embedded user-units system — the firmware applies those on the wire, before AAMotion sees the value.

To keep an integration on raw controller counts, opt the controller out:

controller.UseUserUnits = false;   // before or after Connect — it is remembered either way
AAMotionAPI.Connect(controller, ip);

UseUserUnits defaults to true, and three things about it are worth knowing:

  • It survives connect and reconnect. The communication layer clears its own opt-out on every successful connect; the controller re-applies your setting once the connect succeeds, so configuring it up front works. A direct write to CommAPI.SkipUserUnits is visible through the property but is not remembered — only a value assigned through UseUserUnits is re-applied.
  • It is instance-wide, not call-wide. It forwards to CommAPI.SkipUserUnits, so two controllers sharing one CommAPI share the setting. To bypass the scaling for a single message, use AACommMessage.SkipUserUnits instead.
  • It does not unlock raw User Units configuration writes. A direct assignment to a *UnitFct or *UnitUnt parameter is rejected either way (error 4023) — change those through the API.

A multi-axis move requires its axes to agree on their units. AAMotionAPI.MoveAbs / MoveRel over several axes apply one speed and one accel to the vector distance across them, which only means something if the axes measure the same quantity: every axis must carry the same unit name in the position, velocity and acceleration groups. Different scaling factors under one name are fine — two encoder resolutions on the same gantry still measure millimetres — but a rad axis and a cm axis have no common speed, and an axis with User Units off is on controller counts, which does not match a scaled one. Axes that disagree make the call return false and command nothing, before any parameter is read from or written to the controller; the reason arrives on MotionController.ErrorOccurred as (-1, "<method>", <message naming the axes and the group>). Passing null as the controller does not opt out — the units are read from the axes themselves, so the call still returns false; there is just no channel to report it on. For the same reason the axes must be the SDK's own (controller.GetAxis(...) / controller.AxisList): a wrapper or mock of your own around ISingleAxis carries no axis identity, so its units cannot be checked and the call is refused. Check an axis's configuration with controller.CommAPI.GetUserUnitsEnabled('A') and controller.CommAPI.MessagesContainer.TryGetUserUnitsGroup(group)?.AxisUnits('A') — both are keyed by the axis letter, not by AxisRef — or opt out with UseUserUnits = false, which puts every axis back on counts and therefore always matches.

The vector surface carries the same rule. IVector.VectorLinear (both overloads) and VectorArc apply one VecSpeed/VecAccel/VecDecel across the axes named by the mask while each axis's AbsTrgt — and, for an arc, its VecArcCenter — is in that axis's own units, so the same three groups must agree and a mismatch is refused the same way, with the same (-1, "<method>", <message>) on ErrorOccurred. The profile slots are written to the axis the call was made on, which is always one of the members: the controller reads the profile from the axis that receives Begin, requires that axis to be the mask's lowest member, and needs at least two members, all among A..H. A call whose initiating axis is not a member of its mask, or not the lowest one, or that names fewer than two members or an axis past H, is therefore refused before anything is written, with the same ErrorOccurred shape.

Which units a value is in

controller.GetUserUnits(axis, keyword) returns the unit name and factor that parameter reads and writes in on that axis, as a UserUnitAxis: the controller's values when Global User Units are enabled on the axis and the parameter belongs to one of its groups, otherwise an empty name and factor 1 — controller counts. That identity answer is the counts pin: check Factor == 1 before treating a value as counts — a scaled axis may carry no unit name, so the name alone is not the test. It is never null, and it is also what an unknown keyword, an axis with units off, and a controller opted out through UseUserUnits = false return. axis names one axis; a combined mask is an ArgumentException.

SetPosition takes counts, not display units

ISingleAxis.SetPosition writes a parameter the controller declares unscaled, so its argument is in controller counts whether or not Global User Units are armed — not in the display units Pos reads in. SetPosition(axis.Pos) is therefore not a round trip on a scaled axis: on the 0.01 mm/count axis above it writes 24.68 as a count, rounded to 25. To set the current position from a display value, divide it by that axis's position factor —

double factor = controller.GetUserUnits(AxisRef.A, eKeyword.Pos).Factor;   // 1 when the axis is on counts
axis.SetPosition(displayValue / factor);

— or read the position in counts (controller.UseUserUnits = false) and pass that value straight back.

API Documentation

API documentation is available via IntelliSense. XML doc comments are included with the NuGet package.

Upgrading from 11.0.0

12.0.0 is a breaking release: download aamotion-12-change-disclosure.html, published with the 12.0.0 release, and open it in a browser for every break with the diagnostic it raises and compiled before/after samples — GitHub serves an .html release asset as a file rather than rendering it.

Release Notes

See CHANGELOG.md for detailed release notes and version history.

License

This software is licensed under the MIT License. See the NuGet package metadata for details.


For further information, consult the included documentation or contact Agito support.