# DMSerialMotion API reference

> The public API of DMSerialMotion, organized by task. Real type and member names, signatures, call order, units, events, the no-hardware mode, real-time rules and the traps, for an AI agent drafting a DM application.

<!-- directmotion.com, written for AI agents. Canonical: https://www.directmotion.com/software/api.md . Index: https://www.directmotion.com/llms.txt . Updated 2026-11-08. Publisher: Direct Motion, Inc. -->

Written for AI agents. Names were read from the library source on 2026-11-08 (the current .NET 10 line,
namespace `DMSerialMotion`). They are the names a program compiles against. Examples were checked by
search, **not compiled**: compile against the library DM supplies before relying on them. Use public
members only; non-public names change in shipped builds. The library comes with DM hardware under a
license that forbids reverse engineering; this page publishes the shape, not the implementation.

Conventions on this page: `[unverified]` marks something not yet confirmed by DM; everything else is
current. Spellings such as `DMAxisModual`, `Cordinate` and `Contined` are real and must be kept.

## 1. The types

| Type | What it is | How you get one |
|---|---|---|
| `DMControl` | the controller: factory, axes, IO, power, e-stop, trace, monitors | `DMControl.GetNewControl(name)` |
| `DMSPControl` | the real controller (card plus driver); a `DMControl` | returned when the driver opens |
| `DMNoControl` | the no-hardware controller; a `DMControl` (section 13) | returned when the driver is absent, or forced |
| `DMAxisModual` | one motor axis: linear, rotary or spindle | `control.GetDMAxis("X")` |
| `DMSyncMotion` | the coordinated-motion engine: planner, look-ahead, run, stop, hold | `control.MainMotion` (one per control) |
| `DMPowerModule` | the power board: e-stop, DC bus, brake, temperatures | `control.Power` |
| `DMLogicalIOContainer` | named digital IO | `control.GetSystemIO("MainIO")` |
| `DMLogicalIO` | one named input or output | `io.AddDMLogicalIO(name, owner)` |
| `DMSerialLink` | base class of every board on the link | |
| `DMIOModule`, `DMInputModule`, `DMRelayModule`, `DMEncoderModule` | physical IO and encoder boards | |
| `DMPoint` | a multi-axis point for the planner and the kinematics hooks | `new DMPoint(motion)` |
| `HomeMultipleAxis` | homing, one axis or a list in parallel | static method or `new HomeMultipleAxis(axis)` |
| `CommandList`, `Instruction` | a program and the base class of its steps | |
| `Mill`, `Rapid`, `Arc`, `Feeds`, `FeedsSpeeds`, `TimeDelay`, `StopMovementonIO`, `ModeChangeMS`, `LinearMove`, `FeedChange` | program steps | `new …` |
| `TraceContainer`, `TraceList`, `MonitorContainer`, `MonitorValue` | tracing and monitoring | `control.TraceCont`, `control.MonitorCont` |
| `AccelTest`, `ServoTune_Container` | motor phase find, servo auto-tune | `new …` |
| `ControlAddin` (interface) | a feature add-in for a DM application | implement it |
| `AddInHelper` | static helpers; `SetupResolves()` must run first | |

Enums: `DMAxisModual.AxisTypes { Unknown, Linear, Rotary, Spindle }`;
`DMAxisModual.MotorTypes { AC3PhasePermanentMagentServo, Induction, Induction_withEncoder, DC_OpenLoop }`;
`DMAxisModual.LoopOperatingModes { ClosedLoop, OpenLoop, FOC }`;
`DMAxisModual.EncoderTypes { ABI_Encoder, Biss_Encoder, SinCos_Encoder, BissC }`;
`DMAxisModual.CordinatedMode { MasterAxis, SlaveAxis }` (master axes define a path's length);
`DMControl.EStopStatuses { EStopIn, EStopOut }`;
`DMControl.ControlType { SmartPlugControl, ParellelControl, NoControl }`;
`OverTravelEOT_CheckingOptions { NoCheck, Clamp, ErrorMessage, Ask }` (`Ask` shows a dialog).

## 2. Open, start, close

```csharp
AddInHelper.SetupResolves();                              // 1  lets DM's DLLs resolve
DMControl control = DMControl.GetNewControl("MyApp");     // 2  one per process
DMSyncMotion motion = control.MainMotion;
DMAxisModual x = control.GetDMAxis("X");                  // 3  every axis, by name
motion.AddSerialLink(x);                                  //    axes that move together
DMLogicalIOContainer io = control.GetSystemIO("MainIO");  // 4  then add your named IO (section 8)
control.PostAddedDevices();                               // 5  once; starts the 20 kHz stream
motion.LockAllAxis();                                     // 6  close the loops
control.StopHard = false;                                 //    release the controller-wide hard stop
// 7  home, move, read
control.Power.RemovePowerAndDischargeDC();                // 8  on exit
control.DeleteEverything();
```

- `static DMControl GetNewControl(string sn, bool maketimer = true)`: `sn` is your software's name.
  Returns a `DMSPControl` when the driver opens, otherwise a `DMNoControl`. **Process-wide singleton**:
  a second call shows a message box and returns the first. `GetNewControl_NoReuse(sn)` ignores the
  cached one. The driver serves one control application at a time.
- `static bool DMControl.ForceNoControl`: set `true` before `GetNewControl` to force no-hardware mode.
- `bool control.DriverLoaded`; `control.ControlSolution` (`ControlType`).
- `PostAddedDevices(bool autoadd = false, bool supressmessage = false, bool startirqs = true)`. Register
  **every** axis on a board first: a board carrying two axes is serviced only when both are registered.
- `control.StopHard` (bool): the controller-wide hard stop. It reads `true` until you clear it.
- `control.AddLinkfor_GraphicsMotions(axis)`: adds an axis to the preview motion, if your program draws one.
- Applications that use the real driver run **as administrator**.

## 3. Axes: find, power, hold

- `DMAxisModual GetDMAxis(string name, int linknum = -1, int sublink = 0)`: creates or returns the named
  axis and reads its saved settings. Names are free text.
- `axis.IsEnabled`: mapped to a link (`SerialLink >= 0`). `axis.IsAligned`: motor phase aligned.
- `bool axis.RemovePower { get; set; }`: `true` = no power. It also reads `true` while e-stop or
  `StopHard` is active, so check it after setting it `false`.
- `axis.LockAxis()`, `axis.SimpleLockAxis(bool ChangeRemovePower = true, bool UseAPos = true)`: hold
  the axis where it is. `motion.LockAllAxis()`, `control.LockAllAxis(bool removepower)`.
- `axis.TypeofAxis` (`AxisTypes`), `axis.CordinateAxis` (`CordinatedMode`, a field).
- `axis.MotorRevtoAxisUnits_Ratio`, `axis.MultiplyFactor` (section 7).

## 4. Move one axis

Absolute, non-blocking:

```csharp
bool JogToLocation(double value, double feed = -1, bool handleremovepower = true,
                   bool AlwaysGoForward = false, bool LimitToMaxJogSpeed = true,
                   double accelrate = -1, double decelrate = -1,
                   OverTravelEOT_CheckingOptions EOTOption = OverTravelEOT_CheckingOptions.ErrorMessage,
                   bool RestartControlAllowed = false)
```

`value` includes `MultiplyFactor` (section 7). `feed` in axis units per minute; `-1` = the axis's
`MaxJogSpeed`. `accelrate > 0` with `decelrate = -1` makes decel equal to accel. Returns `false` when
refused: not ready, soft limit, bad value. `EOTOption.ErrorMessage` shows a dialog on a soft-limit
refusal; pass `Clamp` or `NoCheck` to avoid dialogs.

- Blocking: `JogToLocationAndWait`, `JogToLocationAndWaitForDecel`, `JogToLocationAndWaitForDecel_Fast`,
  `JogToLocationAndWaitForDPosOnly`, `JogToLocationAndWaitForBrake`, `JogToLocationWSlowDown(value, percent)`.
- Load-limited: `JogToLocationAndWaitWithPercentLoadLimit(value, feed = -1, percentloadlimit = 20,
  accelwhenloadexceed = -1, …)` and `JogToLocationAndWaitWithCurrentLoadLimit(value, feed = -1,
  currentloadlimit = 3, accelwhenloadexceed = -1, …)`. The waiting thread checks `GetCurrentPercent()`
  or `RMSCurrent` about every millisecond and calls `JogStopEmergency(accelwhenloadexceed)` when the
  limit is passed; the call then returns `false`. It **stops**; it does not hold a force.
- Other units: `JogToLocation_MotorRevs(value, feed)`, `JogToLocation_RawPosition(…)`.
- **No relative move exists.** `axis.JogToLocation((axis.DesiredPosition + d) * axis.MultiplyFactor)`.
- Waits: `WaitForDecel()`, `WaitForAccel()`, `WaitForJogMotiontoStop(…)`, `WaitForCoasting()`.
- Stops: `axis.StopJogToLocation()`, `axis.JogStopEmergency(double decelrate = -1)`,
  `control.StopJoggingAllAxis(bool includespindle = true, …)`.
- Rates: `AccelRate`, `DecelRate`, `JerkRate`, `MaxJogSpeed`, `MaxRapidSpeed`, `SetAccelDecel(accel, decel)`.
- Endless rotary axes: `axis.ModulusJog = true` (position wraps each turn),
  `axis.ModulusThePosition(bool ChangeRemovePower = true)`. The encoder must count incrementally (section 7).
- In-driver following: `axis.FollowAxis` (a `DMAxisModual`, `null` = off). Exists and is used by DM; not
  documented for general use. Ask DM before planning on it.

## 5. Velocity mode

`double axis.JogSpeed { get; set; }`: axis units per minute, signed. `0` stops. Used for spindles,
conveyors, pumps, fans, and any continuously running motor. `JogSpeed_WithStoponEOT(feed,
LimitToMaxJogSpeed = true)` respects end-of-travel switches. `AtJogSpeed()`, `IsJogging()`.
Fast stop: `JogStopEmergency()`. A spindle is an axis with `TypeofAxis = AxisTypes.Spindle`; at ratio 1
its `JogSpeed` is rpm. Induction axes: `LoopOperatingMode` (ClosedLoop, OpenLoop, FOC),
`InductionVelocityMode`, `ISCoasting()`, `WaitForCoasting()`.

## 6. Coordinated motion and programs

The planner takes a program, not single calls. Two ways to feed it.

A `CommandList` of instructions:

```csharp
var cl = new CommandList { Name = "square" };
void Add(Instruction i) { i.ParentCL = cl; i.Initialize(); cl.Instructions.AddNoCalling(i); }

Add(new Feeds { StandardRapidD = 100, StandardCoarseD = 20, StandardFinishD = 20 });
Add(new FeedsSpeeds { FeedD = 20, FeedUnits = FeedsSpeeds.FeedsUnits.UnitsPerMinute,
                      SpindleUnits = FeedsSpeeds.SpindlesUnits.DontChange });
Add(new Mill(1, 0, 0));          // straight cut to (1, 0, 0)
Add(new Mill(1, 1, 0));
Add(new Mill(0, 1, 0));
Add(new Mill(0, 0, 0));
Add(new TimeDelay(500));         // dwell, ms

motion.SupressToolChange = true;
bool done = cl.RunToMotionAndWait(motion);      // blocks; RunToMotion(motion, …) does not
```

Single moves straight into the first pass:

```csharp
motion.AllowAddingInstructions = true;
motion.MakeNewPasses();
var pt = new DMPoint(motion);
pt.SetAxisValue(x, x.DesiredPosition + 0.25);
motion.FirstPass.Add(new ModeChangeMS(true, false));
motion.FirstPass.Add(new FeedChange(10));
motion.FirstPass.Add(new LinearMove(pt));
bool ok = motion.RunAndWait(10);
```

- Run and stop: `motion.Run(double startingfeed)`, `RunAndWait(double)`, `StopAutoMode(bool emergency =
  false)`, `WaitForAutoModetoComplete()`, `motion.AutoMode`.
- Hold and override: `FeedHold`, `VelocityOverride`, `SpindleOverride`, `EnableFeedRateOverride`.
- Limits: `MaxFeedRate`, `MaxAccelRate`, `MaxJerkRate`, `JerkMode`.
- Smoothing: `EnableLookAhead`, `EnableRealTimeLookAhead`, `EnableC2Blend`; tuning:
  `TimeBeforeAccelerating`, `TimeAheadToBeAtSpeed`, `DistancetoEvaluateLookAhead`.
- Where it is: `GetCurrentWorldLocation()`, `GetCurrentPartLocation()`, `GetCurrentDesiredWorldLocation()`
  → `DMPoint`.
- Stop on an input: `motion.SetStopMotiononDigitalIOChange(io)`, or a `StopMovementonIO` instruction
  with its `StoponIO` field set (section 8).
- `ModeChangeMS(bool ms, bool AllowLineartoBlendSmoothness, bool rotaetcordsystem = true, bool
  rotaterapids = false)`: switches master/slave mode for the moves after it. Master/slave mode is what
  makes the planner apply the kinematics hooks per point (section 10).
- Instructions: `Mill(x, y, z)`, `Rapid(DMPoint3D)`, `Arc`, `Feeds`, `FeedsSpeeds`, `TimeDelay(ms)`,
  `StopMovementonIO`, `LinearMove(DMPoint)`, `FeedChange(feed)`, `ModeChangeMS`.
- **G-code is not in the core library.** DM's G-code reader, canned cycles, probing and tool changers
  are add-ins that produce `Instruction`s. Whether an add-in is supplied is DM's decision; ask.
- **One coordinated group per control** (`control.MainMotion`). Axes outside it move independently.

## 7. Units

- `MotorRevtoAxisUnits_Ratio` = motor revolutions **per** axis unit. Position = revs ÷ ratio. Never
  multiply. (A 12 mm-per-turn screw in inches: about −2.1167 rev/in, sign by direction.)
- `JogToLocation(v)` takes `v` **including** `MultiplyFactor`: pass `target * axis.MultiplyFactor`.
  `DesiredPositionWithMultiplyFactor` is the matching read.
- Speeds in axis units per **minute**: `JogSpeed`, `MaxJogSpeed`, `MaxRapidSpeed`, `HomeSpeed`,
  `MaxFeedRate`, every `feed` argument.
- Accelerations in axis units per **second²**: `AccelRate`, `DecelRate`, every `accelrate` argument.
  The property's description text says "units/minute/second"; that text is wrong.
- A spindle at ratio 1: one axis unit = one motor revolution, so `JogSpeed` is rpm and
  `AccelRate × 60` is rpm per second.
- `ActualMotorPosition` is motor revs. `DesiredPosition`, `ActualAxisPosition` and the `DPos`, `APos`,
  `PosError` histories are axis units. `AMotorPos_TraceHistory` is motor revs.
- Velocity: `ActualMotorVelocity` is averaged over about 50 ms; use it. `ActualAxisVelocity` is one
  50 µs cycle of whole counts and is noisy. `ActualAveragedAxisVelocity` also exists.
- The planner's linear units are inches internally; `*_WithMetric` properties convert for display.
- Endless rotary axes (`ModulusJog`) need incremental encoder counting. With a BiSS encoder in
  multi-turn absolute counting the wrap is undone by the next absolute read, and the axis faults on
  following error one turn later. DM's setup tool sets the counting mode.
- Time base: 20,000 cycles per second (`control.IRQsPerSecond`). One cycle = 50 µs.
- Test with a ratio other than 1. Ratio-1 benches hide unit bugs.

## 8. IO
```csharp
DMLogicalIO guard = io.AddDMLogicalIO("GuardClosed", "MyApp");    // before PostAddedDevices
guard.IODirection = DMLogicalIO.Direction.DigitalInput;
DMLogicalIO clamp = io.AddDMLogicalIO("Clamp", "MyApp");
clamp.IODirection = DMLogicalIO.Direction.DigitalOutput;

if (guard.BitNumber != -1)                 // -1: the name is not mapped to a pin yet
    guard.DigitalIOChanging += (s, e) => { if (!e.NewState && motion.AutoMode) motion.StopAutoMode(true); };
clamp.On = true;
bool present = partSensor.WaitForBittoChange(true, 5000);        // up to 5 s
```

- `DMLogicalIO AddDMLogicalIO(string name, string addinName, int monitorSeconds = 0)` (also a
  `TimeSpan` overload). `io.GetIOFromString(name)`. `bool On { get; set; }`.
- Pins come from saved settings (`AssignedPhysicalIO`); `BitNumber == -1` means unmapped.
- Events `DigitalIOChanging`, `DigitalIOChanged` with `OldState`, `NewState`, `ShouldCancel`. They fire
  on background threads and are not hard real time.
- `bool WaitForBittoChange(bool WaitFor, int MaxWait)`.
- **Stop a move on an input:** `motion.SetStopMotiononDigitalIOChange(io)`, or the `StopMovementonIO`
  instruction (`StoponIO` field) inside a `CommandList`. This is handled in the driver's time, not in
  your event handler.
- **Position captured when an input switched:** `axis.LastPositionWhenIOOn(io)`,
  `axis.LastPositionWhenIOOff(io)`. The driver records the axis position at the edge. Use these for
  registration marks and probing, not a position read in your handler.
- **No analog IO API.** The power board exposes `control.Power.DCBusVoltage` and temperatures only.

## 9. Homing

- `static bool HomeMultipleAxis.HomeAListofAxis(List<DMAxisModual> axes)`: parallel, no dialogs. Per
  axis: `new HomeMultipleAxis(axis).Home()`.
- Steps: `axis.FindHomeSwitch()`, `JogToHomeSwitch()`, `JogToLastIndexBeforeHomeSwitch()`, `FindIndexPulse()`.
- Settings: `HomeSpeed`, `HomePreset`, `HomeSwitchEnabled`, `StopAxisonHomeSwitch`,
  `ChangeHomeSwitchBit(DMLogicalIO, bool)`.
- `control.HomeMachine()` is the operator-screen version: it shows windows, returns at once and fires
  `HomedEvent(sender, bool homed)` at the end. Use `HomeMultipleAxis` from code. Induction axes report
  homed without moving.

## 10. Kinematics hooks (5-axis heads, robot arms)

Static delegates on `DMSyncMotion`, type `TranslateEvent(DMPoint pt, TranslateArgs args)`:

| Hook | Converts |
|---|---|
| `TranslateToWorldCordinates_Mechanical` | part (Cartesian) → world (motor axes): inverse kinematics |
| `TranslateToPartCordinates_Mechanical` | world → part: forward kinematics |
| `TranslateToWorldCordinates_Mechanical_Fast`, `TranslateToPartCordinates_Mechanical_Fast` | same, used in the per-cycle path when set |
| `TranslateToWorldCordinates_Software`, `TranslateToPartCordinates_Software` (+`_Fast`) | a second layer DM's CAD/CAM uses for part setups; leave it alone |
| `InitStatics` | called when the planner starts a pass |
| `TranslateVectorToAngles`, `TranslateAnglesToVector` | tool direction ↔ rotary angles |

- `TranslateArgs`: `ToolLength`, `RotateCordinateSystem`, `IsVector`, `WorkingPlane`.
- Edit the point in place: `pt.GetAxisValue(axis | int | string)`, `pt.SetAxisValue(axis, value)`,
  `pt.PartCords` (bool). Looking an axis up by object costs time; cache the index and use the `int`
  overload in a per-cycle hook.
- With master/slave mode on (`ModeChangeMS(true, …)` before the moves), the planner applies the world
  hook to every point it sends to the driver, 20,000 times a second. Straight lines stay straight at
  the tool.
- The hook must not allocate, lock, log or call anything slow.
- One owner only: check `DMSyncMotion.ToolLengthCompHandled`, then set it `true` and
  `ToolLengthCompHandledBy = "YourName"`.
- Current use: DM's 5-axis heads. Robot arms: `[unverified]`; no arm has been built on it. See the
  [recipes](https://www.directmotion.com/software/recipes.md#arm).

## 11. Limits, e-stop, faults, status

- End of travel: `EnableEOTs`, `topswitch`, `bottomswitch` (`DMLogicalIO`); soft limits
  `EnableTopSoftLimit`, `SoftTopLimitLocation`, `EnableBottomSoftLimit`, `SoftBottomLimitLocation`;
  `axis.CheckLimitsforProperSetup()`, `control.AxisonLimit()`, `SetAllAxisLimits()`.
- E-stop: `control.EStopStatus()` → `EStopStatuses`; events `EStopChange`, `StopTriggerChanged`
  (`delegate void BitChange(object sender, EStopStatuses NewValue)`); `IsMachineReady()`,
  `IsMachineReady_NoLimitSwitches()`, `SafeStop(bool controlspindlestop = true)`,
  `PreventAllMovement()`, `AllowAllMovement()`.
- Axis faults: `ModuleFaulted_Error`, `ModuleFaulted_ErrorCode`, `BadLogicPower_Error`,
  `CurrentLimitWithNoPWM_Error`, `NumberofModuleFaults`, `NumberofCurrentLimitHits`,
  `NumberofEncoderErrors`, `TotalFollowingErrors`, `TotalStallErrors`; thresholds `StopErrorAxisUnits`,
  `StopWarningAxisUnits`, `StallTimeForError`.
- Power board: `control.Power.DCBusVoltage`, `BusCharged`, `BusChargingError`, `PowerLostError`,
  `EStopStatus`. Temperatures: `control.HighestAmpTemperature()`, `HighestHeatSinkTemperature()`,
  `HighestCabinetTemperature()`.
- Controller: `TotalBufferUnderruns`, `UnderrunOccured`, `TotalIrqsStoppedError`, `IRQsPerSecond`,
  `FilteredCPUTemperature`, `IsIdle`.
- Axis status: `ActualAxisPosition`, `DesiredPosition`, `DesiredPositionWithMultiplyFactor`,
  `ActualMotorPosition`, `DesiredMotorPosition`, `ActualMotorVelocity`, `ActualAveragedAxisVelocity`,
  `PosErrorAxisUnits`, `PosErrorMotorUnits`, `PosErrorEncoderPulses`, `VelError`,
  `LargestPosErrorSinceLastResetAxisUnits`, `GetVoltagePercent()`, `GetCurrentPercent()`, `RMSCurrent`,
  `CurrentLimit`, `ActiveCurrentLimit`, `IsJogging()`, `AtJogSpeed()`, `DrivingToAccurateLocation`.
- 20 kHz history per axis (ring, indexed): `APos_TraceHistory(i)`, `DPos_TraceHistory(i)`,
  `PosError_TraceHistory(i)`, `CurrentU_TraceHistory(i)`, `CurrentV_…`, `CurrentW_…`,
  `TorqueCommand_TraceHistory(i)`, `Fault_TraceHistory(i)`, `PWM_TraceHistory(i)`, about 40 in all.
  Current index from `control.TraceCurCycle(…)`.

## 12. Events

- Axis: `JogActivating`, `JogActivated`, `JogDeActivating`, `JogDeActivated`, `PreHome`,
  `JogtoLocationActivating`, `JogtoLocationActivating_InAutoMode`, `JogtoLocationDeActivating`,
  `PowerRemoving`, `PowerActivating`. Args carry `ShouldCancel` (set `true` to veto) and
  `ReasonforMovingAxis` (UserMoving, Homeing, AutoCycle, OneTimeSetup, …).
- Motion: `PreAutoCycle`, `PostAutoCycle`, `PreMotionCaculatedAutoCycle`, `PreProbingCycle`,
  `PostProbingCycle`, `AutoCycleCanceledEvent`.
- Control: `EStopChange`, `StopTriggerChanged`, `HomedEvent`, `ControlSleepEvent`, `ControlWakeEvent`.
- IO: `DigitalIOChanging`, `DigitalIOChanged`. Trace: `TraceContainer.FullTraceEvent`.
- All fire on background threads.

## 13. No-hardware mode

- Without the driver, `GetNewControl` returns `DMNoControl` (`DriverLoaded == false`). Force it with
  `DMControl.ForceNoControl = true`.
- It runs the **real planner**: look-ahead, passes, blending, instruction handling. The commanded
  stream can be inspected. Digital inputs can be simulated.
- It is **not a motor simulator**: commanded positions jump straight to their targets,
  `ActualAxisPosition` returns the commanded position, and there is no following error, current, fault
  or encoder behavior.
- Use it to check program logic, instruction sequences and kinematics hooks before hardware arrives.
  "Did the axis arrive in time" needs hardware, or a motor model you write.

## 14. Tracing, monitoring, settings, add-ins

- Trace messages: `control.TraceCont.APIEvents.AddMessage(string)`; per axis `axis.TraceLink`; lists
  `UserEvents`, `APIEvents`, `LinksEventsList`, `AddinEventsList`.
- Trace files: `TraceContainer.Serialize()` writes a `.DMTrace`, `DeSerialize(filename)` reads one.
  DM's tools analyze them. A public call that saves the full 20 kHz ring on demand is not documented;
  ask DM.
- Monitoring: `control.MonitorCont.AddValueChangedBasedValueToMonitor(name, msToWatch,
  differenceToRegister)` → `MonitorValue`; supply the number in a `GetMonitorValue` handler.
- Settings: per axis in the Windows registry, `SavetoReg()`, `ReadfromReg()` on axis, motion and
  control; XML import and export `DMControl.ReadXMLSettings(file)`, `ImportfromControlSettings(file)`.
  A fresh PC has unconfigured axes. Some statistics are written machine-wide, which needs administrator
  rights.
- Folders: `DMControl.UserDataStoragePath` (`%APPDATA%\DMMotion\`), `CommonDataStoragePath`
  (`C:\ProgramData\DMMotion\`).
- Add-ins implement `ControlAddin`: `Initialize(PartSolution)`, `int AddLogicalIOs(DMLogicalIOContainer)`,
  `int AddAxis(DMControl)`, `bool ShouldLoadAddin()`, `string GetDefaultCategory()`,
  `HomeControlAddin()`, `CloseDevices()`. A DLL with no `ControlAddin` and no `Instruction` subclass is
  not loaded.

## 15. Motor setup and tuning from code

- Phase find, headless: `var t = new AccelTest(); bool ok = t.RunHeadless(axis); t.LastOutcome`. It
  writes the phase offset, pole count and `Invert3Phase`. Settings: `Method`, `PowerPercentForTest`,
  `PulseMs`, `CoarsePulses`, `FinePulses`, `TravelBudgetMotorRevs`, `DetectNumberofPoles`. Only on an
  axis free to turn.
- Servo auto-tune: `ServoTune_Container` with `Preset` (Conservative, Normal, Aggressive), `RepeatCount`,
  `TravelBudgetMotorRevs`, `MaxTorqueDuty`, `WriteGainsToAxis`, `TuneControlSystem`, and `Run(axis)`.
  The `RunWithProgress…` variants open a window.
- Gain sets: `axis.CaptureGainSet()`, `ApplyGainSet(string)`, `StoreLiveGainsInSet()`.
- The remaining one-touch setup steps (motor electrical values, back-EMF, induction identification) run
  from DM's setup screen and have no public entry point today.

## 16. Real-time rules for your code

- The driver consumes 20,000 points a second from a buffer the planner fills. If the fill falls behind,
  the driver stops with a buffer underrun (`UnderrunOccured`, `TotalBufferUnderruns`).
- Per-point code (kinematics hooks, custom instructions): no allocation, no locks, no strings, no slow
  calls.
- Keep allocations low while a program runs; garbage-collection pauses can starve the stream.
- Blocking moves (`…AndWait`, `RunAndWait`) block the caller. Never on the UI thread.
- Check `IsMachineReady()` and e-stop out before a cycle. Both read not-ready for a moment after a
  cycle ends.
- `control.RealTimePriorityClass`, `ThreadPriorityLevel`, `SetCoreAffinity` are exposed. DM sets up the
  PC's power and core settings.

## 17. Traps
1. **Dialogs inside calls.** A refused `JogToLocation` with the default `EOTOption`, `HomeMachine`, a
   second `GetNewControl`, and some other refusals show a message box. For unattended programs: check
   `IsMachineReady()` first, pass `OverTravelEOT_CheckingOptions.Clamp` or `NoCheck`, use
   `HomeMultipleAxis`, and plan for an operator to be reachable. A library-wide no-dialog switch is
   not yet public.
2. **`DMControl.GetGUID()`** builds a form. Do not call it from a program with no window.
3. **Errors are `bool` returns, flags and trace messages, not exceptions.** Check every return.
4. **One control application per PC; one `GetNewControl` per process.**
5. **Registry settings per axis.** A new PC has unconfigured axes.
6. **No relative move, no move-and-verify helper.** Read `ActualAxisPosition` and `PosErrorAxisUnits`.
7. **Heavy dependencies.** The library references WinForms, WPF and DM's CAD libraries; Windows only;
   `net10.0-windows`.
8. **Library and driver must match.** A mismatched pair refuses to start with a "buffer size" message.
   DM supplies them together. There is no NuGet package.
9. **Spelling quirks:** `Modual`, `Cordinate`, `Contined`, `Homeing`, `Caculated`; `DPos` and
   `DesiredPosition` both appear.
10. **Units** (section 7), above all `MultiplyFactor` and the wrong `AccelRate` description.
