Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[12.0.0] - 2026-10-30
⚠️ Breaking
- The 11.0.0 → 12.0.0 upgrade page walks the whole release for a consumer — every break with the diagnostic it raises, the changes that raise none, and compiled before/after samples. It is published with the 12.0.0 release as
aamotion-12-change-disclosure.html; download and open it in a browser, as GitHub serves an.htmlrelease asset as a file rather than rendering it. - Requires
Agito.AAComm15.x (see its changelog for the breaking communication-layer changes: theIIdentityContainermember,MessagesInBulkon a Simulation channel, theAllStatInterpreterinput contract). - The
AssemblyVersionis pinned to12.0.0.0for the whole 12.x line (package and file version keep the full number). .NET Framework consumers must recompile against 12.0.0 — a binary built against11.0.0.0does not bind, and no binding redirect bridges the identity change. .NET 8/10 consumers see no difference. - A type of yours that implements one of the seven retyped interfaces —
ISingleAxis,IVector,IIOModule,IECAM,IGroupAxis,ICiGroupAxisorICiSlave; a wrapper, an adapter, a test double — no longer satisfies it until every affected member carries the new type: 73 members losedynamic, 59 properties move frominttodouble, 56 array members move fromAAValueArray<int>toAAValueArray<double>, the four gantry gains become per-setAAValueArray<double>members and 34 methods widen their parameters — 226 members, none of them counted twice. The compiler reports each unsatisfied member at the class declaration, so this surfaces as one wall of errors on that type rather than at call sites. - Parameter values are now carried as
doubleend to end. Every array property that wasAAValueArray<int>—Identity,GenData,ErrLog,DInMode,HomingDef,LoggerParams,LoggerStatus,EventTableand the rest, onISingleAxis,IIOModule,IGroupAxis,IVector,IECAM,ICiSlave,ICiGroupAxisandMotionController— is nowAAValueArray<double>, and yields the parameter's exact value instead of a roundedint. Assigning an element into anintneeds an explicit cast. dynamicis gone from the AAMotion surface. Everydynamicmember is now statically typed: the value properties (Pos,AuxPos,PosRef,Speed,Accel,Decel,MaxVel,MaxAcc,Force,MotorCurr,ContCL,Ia…Vq, theVec*members onIVector, …) aredouble; the array-valued ones (ISingleAxis.Vel,ISingleAxis.AInGain, andIIOModule.AInPort/AInFilt/AInOffSet/AInDB/AInGain) areAADynamicValueArray;MotionController.GenDataDisAADoubleArray. Code that relied on late binding — calling a member the static type does not have, or letting adynamicconvert silently toint— no longer compiles, and a narrowing conversion now needs an explicit(int). In exchange the members are IntelliSense-visible and bind at compile time.- Quantity-carrying properties are
double; modes, indices, counts, bitmasks and millisecond durations keep their integer type (int, oruintfor the homing timeouts). The properties that moved frominttodoubleare the positions and distances (MasterPos,PosErr,RelTrgt,MaxPosErr,InTargetTol,LockVal,ModRev,AuxModRev,ComtAng,EventNextPos,EventBegPos,EventEndPos,EventGap,SpeedChgPos,CurrPosTh,CurrPosErrTh,PosPosTh,ForcePosErrTh,RetractTarget,MapErrOffset,MapErrOffRamp,MapErrOnStep,CNCAPosRef,CNCAdPosRef,CNCBAbsTrgt,CNCBPosRef,CNCBdPosRef), the velocities and profile magnitudes (StuckVel,SpeedChgNew,RetractSpeed,CNCASpeed,CNCBSpeed,CNCBEndSpeed,CNCBAccel,CNCBDecel,CNCBJerk,CNCAEmrgDec,CNCBEmrgDec), the gains and force parameters (ForceGain,ForceKi,ForceKd,ForceFFW,ForceVelFFW,ForceRefFilt,MaxForceErr,MaxForceErrOL,ForceAInTh,CurrCurrTh,CurrAInTh), and the voltages and temperatures (VBus,VLogic,MinVBus,MaxVBus,MaxVBusAbs,RegenOn,RegenOff,BoardTemp,PwrTemp,MotorTemp,MaxPwrTemp). A caller that stored one in anintmust widen it or add a cast; one reading a scaled User-Units axis now receives the fractional value instead of a truncated one. Status, mode and configuration properties (MotionStat,StatReg,ConFlt,LimitsStat,MotorOn,MotorType,EncType,EncRes,EncDir,UsrUnits,EmulRat,ProtectMask,InTargetTime,StuckTime,RptWait,MaxVBusTime,PeakTime,MaxPWM, and the*Flag/*Dir/*Src/*Type/*Stat/ table-index members) keepint. The four gantry gains (GantryAccFFW,GantryPosGain,GantryVelGain,GantryVelKi) move fromintto a per-setAAValueArray<double>instead; see the next entry. ISingleAxis.GantryAccFFW,GantryPosGain,GantryVelGainandGantryVelKiare per-setAAValueArray<double>members, get-only. On firmware with gain scheduling — the AGM800 — each gain holds one value per gain-scheduling set, and the controller lists it as an array of five: read or write setnasaxis.GantryPosGain[n],nfrom 1 to 5. There the member's.ValueraisesInvalidOperationException, as on every array member. On the AGD200 and AGD155-EC the gain is a scalar: read and write it through.Value. Asintproperties they sent the unindexed mnemonic, which the AGM800 refuses (ERR 24), so no set could be read or written there. Reading one into a number no longer compiles (CS0029), nor does assigning to the member (CS0200); an implementer ofISingleAxismust declare the four the same way.GantryVelGainandGantryVelKiare the velocity loop's proportional and integral gains; their documentation named the position loop.- The single-axis motion, homing, vector, PEG and force-control methods take
double.MoveAbs,MoveReland theirAndWait/Repetitive/RepetitiveCycle/TwoSpeedvariants,Jog,SetPosition,Home,HomeToHardStop,HomeToIndex,HomeToLimitThenIndex,HomeToHardStopThenIndex,SetSingleEventPEG,SetEventFixedGapPEG,SetEventTableValue, theForceCurr_Set*family andIVector'sVectorLinear/VectorArc/SetVectorParamcarry their position, distance, speed, acceleration, deceleration, threshold, offset, arc-centre and gain arguments asdouble/double?— including the ones that wereuinton the homing methods — andForceCurr_SetForceCmdSourcetakesdouble[]forforceCurrSlopeandforceCmdVal,VectorLinearparams double[] pos. Millisecond timeouts and dwell times keep their integer type (uintfor the homing timeouts,intelsewhere); cycle counts, directions, sources, polarities, table indices, command hold times and the vector smoothing factor (VecJerk) stayint.VecAccel/VecDecel/VecEmrgDecare float registers, soSetVectorParam(…, 1234.5, …)used to truncate on every controller, User Units or not. The same signatures change onAAMotionAPIand on the Java binding. Passing anintliteral still compiles; passing anint?variable still compiles too (the nullable conversion lifts); anint[]into a wideneddouble[]parameter does not — declare itdouble[]. TheIGroupAxis/ICiGroupAxisCNC path builders and theIECAMtable helpers are deliberately unchanged: their arguments are controller counts, unaffected by Global User Units — a CNC segment crosses as a rawSpecialBulkpayload the scaling seam leaves alone, and an ECAM table is filled with the counts the controller consumes — not display values. ISingleAxis.MoveAbs(dynamic position, dynamic speed = null, …)is nowMoveAbs(double position, double? speed = null, double? accel = null, double? decel = null). The optional-argument call shape is unchanged: nullable value types replace thedynamicnulls rather than an overload set.- The multi-axis
AAMotionAPI.MoveAbs/MoveReloverloads takedouble accel, double speed, double decelanddouble[]targets (double aPos, double bPos, …on the fixed-arity overloads). Anint[]target array no longer binds — declare itdouble[]. ParameterTypeConverter.GetSmartValueandGetArrayreturndoubleinstead ofdynamic, andAASmartArrayWrapper's indexer returnsdouble. A 64-bit (long) parameter is still read with its own command rather than from the value slot; the controller'slongparameters stay inside ±2^51, which adoubleholds exactly.AAMotionConfig.UseNativeDataTypesis removed. It chose between a roundedintview and a native-type view of float/double/long parameters; the value now always arrives at full precision, so the switch has nothing left to select. Delete the assignment.ParameterTypeConverter.ConvertStringToRawInt(eKeyword, string)is renamedConvertStringToRawValueand returnsdouble;ConvertValueForWrite(eKeyword, int)now takes adouble.FastReadCache.GetCachedValue/GetCachedArrayValuereturndouble?instead ofint?.- A multi-axis
AAMotionAPI.MoveAbs/MoveRelacross axes whose Global User Units differ now returnsfalseand commands nothing. These overloads apply onespeedand oneaccelto the vector distance over every axis, so the axes have to measure the same quantity: each must carry the same unit name in the position, velocity and acceleration groups.sqrt(dA² + dB²)over a radian and a centimetre is not a length, and the singlespeedthe caller passes has no unit to give it — so instead of norming incommensurable deltas and running the move, the call is refused before any parameter is read from or written to the controller, and the reason arrives onMotionController.ErrorOccurredas(-1, "<method>", <message naming the axes and the group>). Different scaling factors under the same unit name still match, so two encoder resolutions on one gantry are unaffected; an axis with User Units switched off is on controller counts and does not match a scaled axis. Each axis's units come from the axis itself, so passingnullas the controller does not opt out — the call still returnsfalse, only the error event is lost — and the axes must be the SDK's own (GetAxis/AxisList): a consumer's ownISingleAxiswrapper or mock carries no axis identity, so its units cannot be checked and the call is refused. Check an axis withCommAPI.GetUserUnitsEnabled(axis)andCommAPI.MessagesContainer.TryGetUserUnitsGroup(group)?.AxisUnits(axis), or setMotionController.UseUserUnits = falseto put every axis back on counts — which always matches and restores the counts-based behaviour. A machine with no Global User Units armed is unaffected. - A vector move across axes whose Global User Units differ now returns
falseand commands nothing.IVector.VectorLinear(both overloads) andVectorArc— and theirAAMotionAPIequivalents — apply oneVecSpeed/VecAccel/VecDecelacross the whole move while each axis'sAbsTrgt, and for an arc itsVecArcCenter, is in that axis's own units. That only means something if the axes measure the same quantity, so the same rule the multi-axisMoveAbs/MoveReloverloads apply now covers the vector surface: every axis must carry the same unit name in the position, velocity and acceleration groups, different scaling factors under one name still match, and an axis with User Units off is on counts and does not match a scaled one. A mismatch is refused before any parameter is read from or written to the controller, and reported onMotionController.ErrorOccurredas(-1, "<method>", <message naming the axes and the group>). The profile slots are written to the axis the call was made on, which is one of the members, so it is checked as one. SetMotionController.UseUserUnits = falseto put every axis back on counts, which always matches. A machine with no Global User Units armed is unaffected. ISingleAxis.GantryYawRefand theeKeyword.GantryYawRefmember are removed. Controller firmware deprecated theGantryYawRefkeyword on 2023-11-30; every supported (post-deprecation) firmware rejects it, so the property fails against current controllers. There is no replacement — delete the calls. The sibling gantry parametersGantryFdbkandGantryOffsetare unaffected.ISingleAxis.ForceRelFOn/eKeyword.ForceRelFOnare renamedForceRefFOn. The controller's mnemonic isForceRefFOn(CAN 579); the misspelled members never resolved in the message table, so every read returned 0 and every write was rejected. Rename the call sites; the C-ABI constant becomesAAMOTION_KEYWORD_FORCE_REF_FON.IIOModule.AOutGainis added besideIIOModule.AOutShifts, which is unchanged. They are two different parameters that share CAN 221 across firmware generations, not one parameter under two names.AOutShiftsis an integer shift exponent — the output is scaled by 2^value — and is what firmware without float analog IO reports.AOutGainis a plain multiplier and is what firmware with it reports. So 2 means x4 on one and x2 on the other, and they are not interchangeable. Each resolves only on the firmware that carries it; on the other, reading or writing an output —AOutGain[i]withifrom 1, on firmware that reportsAOutShifts, or the reverse — raisesIndexOutOfRangeExceptionbefore anything is sent. PCSuite makes the same distinction, editing one as an integer and the other as a float.IGroupAxis.CNCAEncRatio/eKeyword.CNCAEncRatioare replaced by read-onlyCNCACumPosRef(double). CAN 468 is the controller'sCNCACumPosRef, not an encoder ratio. The C-ABI constant becomesAAMOTION_KEYWORD_CNCACUM_POS_REF.MotionStatandStatRegare[Flags]enums, andISingleAxis.MotionStatEnum/StatRegEnumreturn the whole register as a bit set. Every member is now the bit it names (1 << n; aNone = 0member is added), and a mirror reports every set bit — an unmapped one included — instead of the one member whose index happened to equal the register value, which mislabelled any multi-bit register (bits 1 and 2 read as the member numbered 6) and turned an unmatched value intoInMotion/Commutation. Test a bit with a mask ((axis.MotionStatEnum & MotionStat.Accelerating) != 0), not with==; code that used a member's numeric value as a bit index must use the member as the mask it now is.StatReg.StepperOpenLoopStallis bit 31,int.MinValueas anint.EnumPropertyHelper.GetStatRegDes(0)describes an empty register as "No status bits set" instead of "Commutation", bit 0's own label.AAMotionAPI.ConnectSimulator,MotionController.ConnectSimulatorandCommAPIInterface.ConnectSimulator(both overloads of each) are removed, together with theAllowSimandSimParamsFileuser settings of AACommServer. They belonged to the file-based parameter-echo simulator that was deleted from AACommServer in June 2026; since then no shipped server could accept the connection, so every call returnedfalse. Delete the calls. The managed firmware simulation (ChannelType.Simulation) is the replacement, andConnectSimulation— added in this release, see Added below — is its entry point.OpticalCouplingLightFinding.SpiralDirandSpiralConfig.SpiralDirare removed. They wroteAGenData[525], a spiral-direction slot the controller's light-finding process no longer defines — writing it left a value on a slot the firmware may later reuse for something else, and the direction it claimed to select had no effect. There is no replacement: delete the assignment, and drop the field from anySpiralConfiginitializer. Every other spiral parameter (NumOfXAxis,NumOfYAxis,SpiralPitch,SpiralMaxRadius,SpiralAngleCoeff,SpiralScanSpeed,SpiralRtnSpeed,PowerTh) is unchanged.SingleAxisConfigcarriesFlatZoneDecay,OptimalCoeffandFiberArrayWidth, andApplySingleAxisConfigapplies them —AGenData[509],[508]and[506], the flat-zone return gradient, the peak-versus-balance weighting and the fiber-array width. They are single-axis process parameters, so a config that leaves them at their default0now writes three zeros where it previously wrote nothing, which turns flat-zone return mode off rather than leaving whatever a previous run set. A caller that assigned the properties directly after applying a config should move the values into the config instead. The properties themselves are unchanged.- No compile error — the call now throws.
HomeToIndex,HomeToLimitThenIndexandHomeToHardStopThenIndexvalidate their profile arguments before arming anything: a speed, acceleration or hard-stop threshold that is not a finite positive number, or adirectionother than1or-1, raisesArgumentOutOfRangeExceptionnaming the argument, and noHomingDefslot is written. These arguments wereuintbefore thedoubleretype, so the type carried the non-negativity the docs state. Such a call used to return as if it had succeeded — the firmware silently rejected the out-of-range slot (its acceleration floor is 100.0 counts/s²) and the sequence ran on whatever profile the slots already held. - No compile error — a multi-axis call now writes nothing.
WriteUserParam,UserParamWait,MultiWriteUserParamandMultiWriteUserParamWaitrefuse anAxisRefthat does not name exactly one axis A..L: the call returnsfalseand raisesErrorOccurred, and nothing is pushed. The segment's axis field is the port index the controller subscriptsUserParam[]with, so a multi-axis mask composed a fractional field that no wire could carry. A single-axis call composes the same segment as before, with the index now an integer field. - No compile error — the same number now means a different duration. Homing timeouts are applied in milliseconds, the unit the API documents.
Home,HomeToHardStop,HomeToIndex,HomeToLimitThenIndexandHomeToHardStopThenIndexwrote theHomingDeftimeout slots assampleRate * timeout, which treats the value as seconds — a documented 15000 ms timeout became 15000 s, so a stuck homing step effectively never gave up. The slots now carryround(timeout / 1000 * sampleRate)samples, the same conversion the PCSuite homing tool applies. A caller that compensated by passing seconds must switch to milliseconds. - An
eKeywordmember's number is an opaque identifier, not the controller's CAN code. It always was in practice — a keyword is resolved by its member name against the message table the connected controller reports, so the number never reaches the wire — but the enum documented the numbers as CAN codes and a few had drifted from any real one. A code is also product- and firmware-specific, which no single number can express: measured on three controllers, an AGM800 reportsRptCounteras 732 where an AGD200 reports 714, and they agree on all 303 parameters they share.ModeSwitchPosmoves to 438 and theRptMode/RptCycles/RptCountertrio to 730 / 731 / 732, the values the current AGM800 firmware uses, andPosKiis added at 714.AOutShiftsmoves to 2221, because 221 now namesAOutGain, so a stored(eKeyword)221meansAOutGain. Managed behaviour is unchanged by all of this. A C consumer that stored the old numbers should recompile; one that treated a constant as a CAN code should read the real one from the controller withMotionController.GetKeywordCANCode. MotionController.ProgHaltAllis removed. It exposed a controller command as a readable property, and reading one sends it — touchingcontroller.ProgHaltAll.Valuehalted every active user program thread instead of reporting a number.ISingleAxis.ProgHaltAll()is the method that has always meant to do that, and it stays.MotionController.IsConnected(andAAMotionAPI.IsConnected) no longer pings or reconnects. The getter used to actively probe the controller via ICMP on every read and, when auto-reconnect was armed, disconnect and reconnect the link on a failed probe — a property read could block for up to ~19 s, and a debugger watch window or a WPF binding could disconnect a healthy controller just by evaluating it. The getter is now a cheap, non-blocking read of the comm layer's own logical connection state. Call the newMotionController.CheckConnection()/AAMotionAPI.CheckConnection(controller)to actively probe reachability, andTryReconnectto recover a link that is actually down.enableAutoReconnect(Connect's parameter,AutoReconnectEnabled) is recorded but no longer has any automatic effect.- A typed read that fails now throws
ControllerCommandExceptioninstead of answering0. A parameter read through a typed property, through one of theIs*status helpers built on them, or throughMotionController.GetValue, used to answer0when the controller refused the read or replied with something that is not a number — indistinguishable from the controller genuinely reporting0, which is what a position, a velocity and a status word all report at rest. The exception carries the controller's own error code, the keyword and the exact command text. No signature changes, so the compiler reports nothing: a poll loop that read a status property over a dead link used to spin on an invented0and now leaves the loop at the first failed read.ISingleAxis.TryRead,ISingleAxis.TryReadArrayandMotionController.TryGetValueare unchanged — they report the same failure asfalseand never throw — andLastReadErrorandReadFailuresrecord the failure on both routes, throwing and reporting alike. AnAADoubleArrayelement read, such asMotionController.GenDataD[i], a directParameterTypeConverter.GetDoubleArraycall, and the raw register readsReadGenDataF,ReadGenDataD,ReadUserParam,ReadUserParamFandReadUserParamDonMotionControllerandAAMotionAPIfollow the same contract; they used to raiseInvalidOperationExceptionorFormatException, record nothing, and hand back aNaNor infinite reply as a value. A consumer catchingInvalidOperationExceptionaround aGenDataD[i]or otherAADoubleArrayelement read,FormatException/InvalidOperationExceptionaround aParameterTypeConverter.GetDoubleArraycall, orFormatExceptionaround one of those five register reads, must catchControllerCommandExceptioninstead. Nothing new crosses the C ABI: every export already turns a managed throw into its failure status, soaamotion_get_valuenow reportsfalsefor a reply that is not a number where it used to hand back0as if it had been read. - A typed set the controller refuses now throws
ControllerCommandExceptioninstead of doing nothing. Assigning to a typed property —axis.Speed = 1000,axis.AbsTrgt,axis.Vel[1],controller.GenData[3]— used to discard the controller's answer, so a refused write left the parameter holding its old value with nothing at the call site to say so, and only a subscriber toErrorOccurredever heard about it. The exception carries the controller's own error code, the keyword, and the exact command text including the assignment (ASpeed=1000), so the failure names the value that did not take. No signature changes, so the compiler reports nothing. The consequence to plan for is that a call which writes several parameters before it commands anything —MoveAbs,MoveRel,ConfigureSlowLogger— now stops at the first refusal instead of running to the end, so an axis no longer begins on a profile the controller never took. The optical-coupling configuration is the exception and is unchanged: its properties assign throughMotionController.WriteGenData, which catches the refusal into afalsethe setters discard, so it still runs to the end and still reports throughErrorOccurredonce per refused slot.MotionController.WriteGenDatareports a refused slot write asfalse, which is what its documented contract always promised. A failed write is deliberately NOT recorded inLastReadErrorand NOT counted byReadFailures— both are the read record, by name and by their own documentation, so a consumer polling them for stale reads keeps being told about reads and nothing else. There is noTrySet: catch the exception, or keep usingErrorOccurred, which is still raised for every refusal. The two array paths that reach the controller throughAASmartArrayWrapper's setter rather than the changed-value event — a long-typed array element, andMotionController.GenDataD— raise the same type as everything else: they used to raiseInvalidOperationExceptionwith a message carrying no error code, socatch (ControllerCommandException)missed them and the payload was reachable only as text. A consumer catchingInvalidOperationExceptionaround aGenDataD[i]or long-array element write must catchControllerCommandExceptioninstead. Nothing new crosses the C ABI — every write export already turns a managed throw into its failure status, and the status bytes are unchanged. - No compile error —
.Valueon an array member now throws. An array parameter has no single value. On anyAAValueArray<double>member —MotionController.GenData,IIOModule.AInMode,ISingleAxis.HomingDefand the rest — whose parameter the connected controller lists as an array of one or more elements,.ValueraisesInvalidOperationExceptionon get and on set, before anything is sent, with a message naming the parameter and the indexes it has (AInMode is an array parameter; read an element from index 1 to 4.). The getter used to answer the slot's cached first element without reading the controller —0before any element was read — and the setter sent nothing, so a write was dropped and the next.Valuehanded it back as though the controller held it.ParameterTypeConverter.GetSmartValueandSetSmartValuego through.Value, so they throw the same for such a keyword, except a 64-bit (long) one, which they still send unindexed with its own command; the controller refuses that (ERR 24), raised asControllerCommandException. Read or write an element by its index instead, fromStartIndextoEndIndex:io.AInMode[1], orMotionController.GetValue(axis, keyword, index), which already refused index 0 on an array parameter. Nothing crosses the C ABI:aamotion_get_valuereads throughGetValue. IGroupAxis.IsCNCCompletedraisesControllerCommandExceptionon a failed status read, on both groups. It is a poll-until-flips predicate, and a predicate has no answer for a link that did not reply, so it now raises rather than reporting a completion state it could not read. The Group B branch previously returnedfalse, which left awhile (!IsCNCCompleted(...))loop spinning forever on a dead link; Group A already raised, because it reads through the value-answering path that 12.0.0 made throw. Wrap the poll, or read the status yourself withTryGetValueif a failed poll is an expected outcome. The C ABI is unchanged —aamotion_is_cnc_completedalready caught and returned0either way. TheWait*family is deliberately untouched: a wait returns whether it succeeded, and a failed read means it did not, which is the abort behaviour 12.0.0 already introduced.
Added
- Add
MotionController.CreateEventTableClient(axis)for the shared explicit verified-table lifecycle; it does not commission, move or automatically arm an axis. AAMotionAPI.DownloadParametersorders the lock/event writes for firmware with the LockEventMode 1/2 guards (Firmware-Main #1256). On such firmware the controller refuses a change toEncSinCosHWEn,EncDir,VEncSrcorVEncOn(andAuxEncDirwith the aux source,VEncFact/VEncFactDenwith the VEnc source) whileLockEnorEventOnis on (ERR 403), and refusesLockEn/EventOnwith a stale offset (ERR 404; ERR 335 still means LockEventInit never ran). A.paris written in file order, so a restore over an armed axis, or one that re-arms after changing the source, failed partway. When the controller's firmware has the guards (it declaresLockEventModeup to 2) and an axis's rows touch a dependency orLockEventMode, the download now writesLockEnandEventOn= 0 first (a.parcarries neither, as they are not flash parameters, so the restore leaves that axis's Lock and Event off), then the other rows in file order, thenLockEventMode, thenLockEventInitwhen the axis is in mode 1 at re-arm (the file'sLockEventModerow, else the controller's current mode, which the download reads first; if that read fails nothing is written), then the file's ownLockEn/EventOnrows in file order, so the last one requested is the state left. The axis letter is matched case-insensitively (aandAare one axis). The added rows are sent but not counted as written. A refused disarm counts inErrorCount, is listed inRejectionsmarked as added, fails the download with a completion error, and nothing is committed. A refusedLockEventInit(ERR 151 when the remote can't be reached, say) counts inErrorCountand is listed inRejections, marked to be run before enabling Lock or Event, and the download still commits: the firmware refuses to arm that axis (ERR 335 or 404) untilLockEventInitsucceeds. Firmware that declaresLockEventModeonly up to 1 has no such guards and is written in file order. Integrators who write these keywords themselves need the same order;EncFilt,AuxEncFiltandCurrDirare not dependencies.- AGM800-EC:
DownloadParameterstreats the drive like any other until its next enable (Firmware-Main #1258). The drive's EtherCAT enable forcesLockEventMode2 and learns the offset itself, but only at that enable, so a restore that leaves the axis in mode 1 still runsLockEventInitbefore re-arming. The disarm still comes first. On EtherCAT it also drops an armed touch probe, which the master re-arms by toggling the 0x60B8 enable bit. MotionController.UserProgramPrintReceived/AAMotionAPI.SetUserProgramPrintHandler/ClearUserProgramPrintHandler— subscribe to user-programprintflines (FW v5+, AGM800 Ethernet) asUserProgramPrintMessage. Wire fields:TimeSec,TimeTick,Thread,Message. Console-friendly fields:Timestamp(wall clock fromATimeboot + stamp at the current-loop sample rate) andDisplayText(strips a leading!R!/!G!/!Y!, trims trailing CR/LF, expands literal\n/\r/\t).ATimeis queried once (asyncCommAPI.Send, same path as the PCSuite Console — not syncSendReceive, so a clock timeout cannot tear down a fresh connect) when the first handler is added while connected, or on connect / a successfulTryReconnectthat rebuilt the link when a handler is already subscribed; the first printf line seeds the boot if that query fails. Disconnect/Dispose stop delivery so a late push cannot reseed the clock;TryReconnectreopens delivery after a disconnect. Clearing a handler mid-packet stops further lines in that packet. Typical Console use:Console.WriteLine($"{m.Timestamp:yyyy-MM-dd HH:mm:ss.fff} [T{m.Thread}] {m.DisplayText}"). Delivery rides the existing AAComm push path (CommAPI.OnPushMessageReceived→AACommPushMsgUP); no second socket on port 50010. Other push sources are ignored.ControllerCommandException— the exception a failed command raises, and the only one AAMotion defines. It carriesErrorCode(the controller's own code, orNoControllerCodewhen the SDK detected the failure itself — a reply that is not a number),Keyword,Command(the exact text that was sent, array subscript and assigned value included) and aMessagecomposed from all three, so the one line a user pastes into a bug report names the command as well as the error.MotionController.ConnectSimulation/AAMotionAPI.ConnectSimulation(controller, simExePath[, …])— the managed firmware-simulation entry point overChannelType.Simulation(#902). Pass the absolute path to afirmware_sim.exeon disk — a relative path is refused, because the SDK checks it against the calling process's working directory while AACommServer spawns it from its own, so a relative path that looks valid here can name a different file there. AACommServer spawns it, configures the motor, and bridges loopback. The SDK does not ship the binary and does not read a siblingsim.info— optionalsimProduct,simVersionandsimMotorProfileparameters are forwarded when supplied. AsimProductthe product catalog knows also declares the controller type (AGM800,AGD155,AGD155-EC,AGD101-EC,AGD156-EC,DRV6-V3,Pulse-Sync,AGM800-EC,AGD301,AGC301,AGD200,AGC300, the paired spellingsAGD301orAGC301andAGD200orAGC300, the deprecatedAGM400, and the older spellings of the ones that have them), which roughly halves the connect: discovering it instead costs a probe that spawns and reaps a whole extrafirmware_simchild before the session's own. A declared type is verified like any other connection's, so asimProductthat disagrees with what the sim reports fails the connect —ConnectSimulationreturnsfalse, and the underlyingInvalidControllerTypeappears in the AAComm debug log — instead of connecting to whatever the sim actually is. A name the catalog does not resolve is discovered by that probe, exactly as omitting it is — among them the four EtherCATControllerTypemember names (AGD155ECis cataloguedAGD155-EC). Disconnect reaps the child process.TryReconnectremains Ethernet-only. To reopen a simulation session callDisconnect()first, thenConnectSimulation(...)—ConnectSimulationdoes not tear down a link it finds open, and the connect short-circuits on one, so calling it alone on a wedged session returnsfalsewithout reopening anything. On the C ABI the sequence has an extra step, becauseaamotion_disconnectalso frees the handle: disconnect, then a newaamotion_initialize, thenaamotion_connect_simulation. The native SDK exposes the same path asaamotion_connect_simulation.MotionController.CheckConnection()/AAMotionAPI.CheckConnection(controller)actively probe physical reachability via ICMP, without the side effectsIsConnectedused to have — never disconnects or reconnects by itself. Call it from a background timer or a manual health check, not from a binding or a tight loop.ControllerType.AGM800EC(the product id,16) and theAGM800ECcontroller it builds.AAMotionAPI.Initialize(ControllerType.AGM800EC)returns anAGM800EC, which has theAGM800's shape — axesA–H, I/O modulesIO_A–IO_L, andGroup/GroupB.ISingleAxis.TryRead(keyword, out value)/TryReadArray(keyword, index, out value)andMotionController.TryGetValue(axis, keyword, out value, index = 0)— a read that says whether it succeeded, rather than throwing. A typed property now throwsControllerCommandExceptionwhen the controller replies with an error or the link is dead (see the Breaking entry above); theTryforms returnfalseinstead, withvalue0, for an error reply and for a reply that is not a number. Use them where a failed read is an expected outcome — a poll, a probe, an optional parameter — and let the typed property throw everywhere else. They are always a controller round trip in the units the axis reads in, never the FastReadCache;TryGetValueappliesGetValue's argument checks, and a command keyword is refused the same way. An implementer ofISingleAxishas two members more to satisfy — the same wall of errors 12.0.0 already raises on such a type.MotionController.LastReadErrorandReadFailures— the recordErrorOccurrednever kept.LastReadErroris the most recent failed read (aReadError: the controller's error code, or -1 for a reply that was not a number; the keyword; the exact command sent, such asAGenData[3]; the formatted error text; and a UTC timestamp), ornullwhen none has failed since the last successful connect; a later successful read does not clear it.ReadFailurescounts every failed read since the last successful connect. Both cover every read the SDK makes for a value — the typed properties,GetValue, theTryforms, the raw register reads and the motion waits' own polls — so a loop that only checksReadFailuresbefore and after can tell a real 0 from an unanswered read.ErrorOccurredis raised, as before, for a read the controller refused, but not for a reply that is not a number.- A motion wait ends at the first failed poll.
MoveAbsAndWait,MoveRelAndWaitand theirRepetitive/TwoSpeedvariants pollInTargetStatlive; on an error reply or a dead link the poll used to read 0 — "not in target" — and the wait spun to its full timeout (five minutes by default). It now returnsfalseat once, andLastReadErrornamesInTargetStatwith the failure, which is how a caller tells that outcome from a timeout. The signatures are unchanged. - Sine PTP motion (MotionMode 20 / 21) —
MotionMode.SinePTPandMotionMode.SinePTPRepeat,ISingleAxis.JerkInAcc/JerkInDec, and the optional pre-cruise phasePreCruAbsTrgt/PreCruRelTrgt/PreCruiseSpd.ISingleAxis.CommitMotion()replaces the profile of the Sine PTP motion running on that axis with the parameters written since;MotionController.CommitMotion(axes)andAAMotionAPI.CommitMotion(controller, axes)do it for several axes in one synchronised step (AxisRef.A | AxisRef.B), refused whole when any named axis is not in a Sine PTP motion.AAMotionAPI.SinePtpBegin/SinePtpCommitwrite a wholeSinePtpMotionConfigand start or commit it. Needs firmware with the Sine PTP profiler (the AGM800); other controllers refuse MotionMode 20 and 21. ISingleAxis.LockValBefMapandeKeyword.LockValBefMap— the lock capture before error mapping, read-only (Firmware-Main #1380). It is latched on the same lock edge asLockVal, but before error compensation: the map table,MapErrOffsetand theMapTypeon/off ramp are excluded, as inPosBeforeMap, so it is in the same space asEventPos. It is read at the product's full position width, 64-bit on the AGM800 and AGM800-EC, whereLockValstays 32-bit, so thereLockVal - LockValBefMapis the map correction only modulo 2^32. Only firmware that reports theLockValBefMapkeyword carries it; on older firmware a read throwsControllerCommandException. It is not saved to flash, soUploadParametersleaves it out of a.par.ISingleAxis.LockBMapTableandeKeyword.LockBMapTable(CAN 958) — the lock table before error mapping, read-only (Firmware-Main #1544): rowiis the same capture asLockValTable[i], taken before error compensation asLockValBefMapis forLockVal. It is sized from the controller's table likeLockValTableand indexed from 1, and it is not saved to flash, soUploadParametersleaves it out of a.par. Only firmware that reports the keyword carries it: CI-master builds (the AGM800) withCFG_CI_LOCK_FIFO=1. LikeLockValTable, its entries are 32-bit, so on the AGM800 they holdLockValBefMapmodulo 2^32. On other builds, where CAN 958 is a boot-stub row, and on older firmware an indexed read throwsIndexOutOfRangeExceptionand sends nothing.ISingleAxis.LockValTabB,LockTimeTabBandLockBMapTabBwitheKeyword.LockValTabB(478),LockTimeTabB(479) andLockBMapTabB(959): the second half of the lock tables, read-only. The firmware stores lockninLockValTable[n]whilenis within it, then inLockValTabB[n - LockValTable.EndIndex](EndIndex= LOCK_TABLE_ARRAY_SIZE - 1), and stores none past twice thatEndIndex, whileLockCntrkeeps counting.LockBMapTabBis the before-map companion, only on CI-master builds (the AGM800) withCFG_CI_LOCK_FIFO=1(Firmware-Main #1544); its entries are 32-bit, likeLockValTable, so on the AGM800 they holdLockValBefMapmodulo 2^32. Each is sized from the controller's table and indexed from 1; on firmware without the keyword an indexed read throwsIndexOutOfRangeExceptionand sends nothing.- FIFO position tracking and PVAJ motion modes, and the PVAJ stop reasons —
MotionMode.FIFOPositionTracking(19),MotionMode.PVAJ(22) andMotionMode.MultiAxisPVAJ(23), andMotionReason.PVAJMemberWasStopped,PVAJMemberWasAborted,PVAJMemberRLSFLSandPVAJMemberGotMotorOff(42–45): the axis stopped because another member of its PVAJ group was stopped, was aborted, hit a limit switch or went motor-off.ISingleAxis.MotionModeEnumandMotionReasonEnumname them, andEnumPropertyHelper.GetMotionModeDes/GetMotionReasonDesdescribe them instead of answering "Unknown motion mode" / "Unknown status". The PVAJ modes need firmware with the PVAJ profiler (AGM800 5.3.0). AAMotion has no members for the PVAJ list or itsPVAJValidate/PVAJArmcommands. ConFltnames every controller fault the firmware reports (1055–1082) —ConFlt.PositionErrorExceedsLimitOpenLoop,VelocityErrorExceedsLimitOpenLoop,ForceErrorExceedsLimitOpenLoop(1055–1057),Undervoltage10v5Supply(1058),OverCurrentMotorC(1059),BoardTemperatureTooHigh(1060),OtherGantryMemberAxisMotorOff(1061),FaultInputControlledStop(1062),OtherVectorMemberAxisMotorOff(1063),StallDetectedMotorOff(1065),VirtualEncoderMaxPulsesExceeded(1066),AnomalyDetected(1067),AbsEncoderErrorBit,AbsEncoderCRCError,AbsEncoderDisconnected(1068–1070),UnstableCurrentLoop(1071),HighPIVNoise(1072),CollisionDetected(1073),LoadEstimatorNotReady(1074),NoPhasingDetected(1080),CPUBackgroundWatchdog(1081) andExternalAmplifierFault(1082).ISingleAxis.ConFltEnumused to readConFlt.NoFaultfor any of these, so an axis stopped by one of them looked fault-free; it now returns the fault, andEnumPropertyHelper.GetConFltDesdescribes it instead of answering "Unknown fault status". 1064 is not a member: the firmware no longer raises it.ParamHandlingSummary.Rejections— every entry the controller refused during an upload or download, in send order, each carrying the parameter as it was sent and the controller's reply (ParamRejection); a download lists the rows it skipped before any of those. The summary previously reported only anErrorCount, so which parameters failed, and why, could not be recovered from the callback at all.ParamHandlingSummary.Conversions— every download row whose value was rewritten before it was sent, as read and as sent (ParamConversion.ParameterandParamConversion.Sent), so a caller can compare a readback against what the controller was given.aacomm.cli params-downloadprints each asconverted <row as read> -> <row as sent>.MotionController.UseUserUnits— whether values cross this controller's communication layer with the Global User Units scaling applied. Reading it reports what the layer will actually do; a value assigned through it is remembered and re-applied at the next connect. Opting out does not unlock raw*UnitFct/*UnitUntwrites — those stay rejected either way.MotionController.GetValue(axis, keyword, index = 0)— the value a parameter holds on an axis, read by keyword rather than through the member that exposes it, in the units that axis reads in (GetUserUnitsnames them).index0 reads the parameter itself; 1 and up read one element of an array parameter. One axis A..L; a combined mask is anArgumentException, as onGetUserUnits, and so is a keyword the message table flags as a controller command — reading one would send it.MotionController.LoggerUpload(params object[] parameterSpecifications)— the logger upload resolved and scaled: pass the same specificationsConfigureSlowLoggertook, in the same order, and get aLoggerDataContainerwhose packets carry the packet id and the values in that order, in the units every other read uses. AAMotion keeps no record of the configuration, so the caller supplies it; an error reply raisesErrorOccurredand yields no packets. The parameterlessLoggerUpload()is unchanged and returns the controller's raw text — no float resolution, no User Units scaling.MotionController.GetUserUnits(axis, keyword)— the unit name and factor a parameter reads and writes in on an axis, as aUserUnitAxis: the controller's values when Global User Units are enabled on the axis and the parameter is in one of its groups, otherwise an empty name and factor1(controller counts) — which is also the answer for an unknown keyword, an axis with units off, and a controller opted out throughUseUserUnits. Nevernull, soFactor == 1is the check for counts.ISingleAxis.GetHomingDefStep(int step)and theHomingDefStepview it returns.HomingDefis one flat array of 20 steps of 10 slots whose slot roles depend on the step type in slot 1; the view names them —TypeplusParam1…Param9, or the[1..9]indexer — so a caller no longer computes(step - 1) * 10 + slotby hand.HomingDefitself stays as the escape hatch for a step type the SDK does not name.ParameterTypeConverter.ToInt32Register(double)— the controller's 32-bit two's-complement view of a value slot, for bit arithmetic on a port or status word now that the slot is adouble. A plain(int)cast of a value above 2,147,483,647 — aDOutPortreported as an unsigned full-width word, say — is unspecified in C# and differs between .NET Framework and .NET 8/10; this wraps modulo 2^32, the bits the controller holds, on every framework.GroupDOutSetBit/ClearBit/ToggleBit, theirCitwins and the CNC completion poll fold through it.
Changed
- The digital-output bit API states its index conventions.
DOutSetBit/DOutClearBit/DOutToggleBitand the group and Central-I twins take the 1-based output number the controller'sDOutPortSBit[n]takes;GetDOutPort/GetDInPortread a 0-based register bit, so outputnisGetDOutPort(n - 1). Documentation only — no member changes behavior. - A write carries the caller's value whole to AAComm, which divides it by the User Units factor and rounds a firmware-integral parameter in controller counts. AAMotion used to narrow the display value first — to an
intfor a 32-bit parameter, to alongfor a 64-bit one — which quantised a scaled axis to whole units: at0.01 mm/count,axis.AbsTrgt = 12.345reached the controller as 1200 counts instead of 1234. The two setter families also disagreed, so the same fraction was rounded onAbsTrgtand passed through raw onMaxVBus. Both now behave the same, and the rounding — to-even, on the counts — happens once, in AAComm's transmit seam. An integral 64-bit assignment still crosses with its exact digits, so a.parround-trip above 2^53 is unaffected. - The commands that render a value straight into their command text —
ISingleAxis.SetPosition,SetEventTableValue, and theVectorLinear/VectorArctarget and arc-centre writes — render it with the invariant wire format. They were culture-safe only for as long as those arguments wereint. - Connecting no longer retries with forced About data; a failed connect fails on the first attempt (AAComm now refreshes a stale About cache itself). The same applies to the auto-reconnect path.
Fixed
- A
.parfor another product is refused.DownloadParametersread no product from the file, so a.partaken from an AGD200 wrote onto an AGM800. When the file's// Controller type:header names a product other than the connected controller's, nothing is sent and the completion callback gets an error naming both products. The controller's legacy names (AG300-CTL02for an AGD301, say) and the nameUploadParameterswrites for it are accepted; a header name no product answers to is refused the same way, and a file with no header downloads as before. - An integer keyword takes the rounded value on every transport.
DownloadParameterssent an integer keyword's123.5as 124 over Ethernet, where AAComm's binary form rounds it, and as text over RS-232, where firmware refuses a value with a fraction or an exponent. It now rewrites the value of an integer keyword that is not already an integer literal to the integer firmware's own rounding gives (half away from zero:124.5is 125,-2.5is -3) before it is checked or sent, so both carry the same integer for a value within the keyword's range. A float keyword's value is sent as written. - Every row a parameter download skips is named. A line the reader dropped (no
=, under 3 characters, an axis outside A-L) was neither counted nor named, and a row the connected controller does not support and theProductSNrow were counted inSkippedCountwithout aRejectionsentry. Each now has an entry with aPC Suite: ERRreason and counts inTotalCount; they are the firstSkippedCountentries ofRejections.aacomm.cli params-downloadprints every one asskipped <row> -> <reason>. - Two
FastReadCacherefreshes can no longer run at once. The cache read its in-progress flag and set it a line later, so aRefreshNow()and the refresh timer arriving together both got through: two bulk reads went out on one connection,RefreshStartedfired twice for the one refresh a caller expected, and both completions re-armed the timer. The flag is now claimed in a single atomic step and freed on every way out of the refresh — including aRefreshStartedhandler that throws, which used to leave the cache refusing every later refresh for the life of the process; such a throw is now reported onRefreshErrorlike any other refresh failure.IsRefreshingis unchanged. - A
FastReadCachewhoseRefreshCompletedhandler throws keeps refreshing. The refresh raisedRefreshCompletedbefore it scheduled the next one, and the refresh timer fires once per schedule, so a throwing handler stopped the periodic refresh for good:IsRefreshingreadfalse, no error was raised, and readers kept getting the last values read. The next refresh is now scheduled first, and the handler's exception is reported onRefreshError, as aRefreshStartedhandler's already was;RefreshNow()no longer throws it at the caller. Because the next refresh is already scheduled, it can start before a slowRefreshCompletedhandler returns. - A throwing
UploadParameters/DownloadParameterscompletion callback is called once. A callback that threw was called again with its own exception ("Exception in upload completion: …", "Upload failed: …", "Failed reading .par file: …", "Exception in download completion: …"), and an upload then counted its rows a second time and wrote the.paragain, reporting doubledSuccessCount/ErrorCount. Each upload and download now calls the callback exactly once, whatever the callback does; its exception is not reported back to it. - A parameter upload or download that stops partway reports the failure and saves nothing. When the bulk run ended with an error after some replies had arrived (a throwing
onProgresscallback is one way),UploadParametersreported success and wrote the.parwith empty values for every row it never read, andDownloadParameterscounted the unsent rows as written and, withcalcAndSave, saved the partial transfer to flash. Both now report the run's error: the upload writes no file, and the download counts only the rows that were answered, never sendsASave/CalcFiltersfor a partial transfer, still resyncs User Units, and reports no final 100% progress. A throwingonProgresson the download's final 100% no longer leavesonDoneuncalled: the exception's message is appended to the reported error. - A parameter download that fails before its first message reaches the wire now reports the failure. The bulk runner behind
DownloadParametersreads the connection's bulk size and connected state at the start of a transfer, and an exception from either escaped onto the background task driving the download —OnDonenever fired and the call went quiet. Both reads are now covered and the failure arrives onOnDonewith the exception's message, and a download whose transfer sent nothing no longer reports every parameter as written: the completion treated an empty reply set as a clean sweep, counted the whole file as successful and then committed it to flash withASave. It now reports the failure and commits nothing, as the upload path already did. The runner's one-at-a-time start guard is claimed atomically as well, so two starts on one runner cannot both proceed. MotionController.EnsureAACommServerRunning(serverExePath)starts the server through the same contract every other entry point uses, and answers on the port. It span its own process and judged “running” by the process list, so it missed--headless, the hidden window, the zone-check opt-out that keeps a Mark-of-the-Web executable from stopping on a modal security prompt, the lock that serialises two spawns, and the readiness poll that notices a child which died on startup; it also answeredtruefor a process that had not bound its port yet, and never recognised a runningAACommServerNative. It now delegates toCommAPI.StartAACommServerand reports whether a server is serving. A null or empty path starts nothing and reports whether one is already serving — delegating it would auto-locate and could start the machine-wide install the caller never named. The signature and theboolcontract are unchanged.TryReconnectno longer tears down a healthy link on a single flaky ping. Its "already connected and reachable" check sent one ICMP probe; a switch or firewall that blocks or rate-limits ICMP could make a live link look dead and trigger an unnecessary disconnect/reconnect cycle. Two independent probes are now sent, and either succeeding is treated as reachable.- A parameter download no longer moves the controller off its own network address.
DownloadParameterswroteEthernetIPandEthernetMAClike any other row, so a.partaken from one bench and written to another re-addressed the target controller and the operator lost it mid-restore. Both are the controller's identity rather than part of its configuration, so the download now withholds them whatever the file carries — the same pair PCSuite's Parameters Download window excludes. A withheld row counts as skipped, not as a failure, and is listed inParamHandlingSummary.Rejectionswith a client-side reply naming the exclusion, so nothing is dropped silently.EthernetPortis not identity and is still written. - A parameter file round-trips: what a controller uploads, it accepts back. A
.parholds raw controller values, but the transfer applied Global User Units scaling on the way out and could not write the User Units configuration rows at all —*UnitFct/*UnitUnt[]are refused over raw messaging, which is 1068 of the 1106 rejections a full AGM800 download still reported. Both legs now carry raw values and are permitted to write that configuration, and the scaling cache is resynced afterwards, which is the contract for that bypass. Measured on an AGM800: a self-produced.pardownloads back with 36 rejections where it had 4110, and those 36 are a firmware defect fixed in FW 5.2.1.16-3 rather than anything the SDK does. - Keywords the firmware refuses to carry inside a bulk packet (ERR 184 —
UserUnitsEnis one of a set) are re-sent one at a time after the batched pass, instead of being reported as rejections. The retry keys on the reply, so it covers every keyword in that set rather than a list that goes stale. ProductSNis no longer attempted by a download. It is writable only with elevated permission and answers ERR 328 otherwise, so it counts as skipped — the same class the connected controller does not support — rather than as a failure.- A parameter download no longer abandons the rest of a bulk packet at the first refused row.
DownloadParametersbatches up to 50 assignments per packet, and a plain bulk packet stops executing at its first error — so one row the controller refused silently discarded up to 49 writes behind it, and the unanswered tail was counted as a rejection per slot. A full.pardownload reported thousands of errors where only a few rows were genuinely refused, and the parameters that never reached the controller were not reported at all. Both legs now use the continue-on-error bulk packet, so every assignment is attempted and every reply is the controller's own. Firmware that does not implement that packet kind (it answers ERR 160) is detected on the first batch and the transfer falls back to the previous behaviour. - A
.parsaved byUploadParametersnames the controller by product name. The header's// Controller type:line carried a hex product id (0xAfor an AGM800), which PCSuite's Parameters Download cannot resolve, so it skipped the Central-i master / standalone mismatch warning for every file AAMotion wrote. The line now carries the product name PCSuite writes there for every controller it supports, so that warning applies to AAMotion's files as it does to PCSuite's own. - A parameter download with
calcAndSave— the default — commits to flash.DownloadParametersis meant to finish withASaveand aCalcFiltersper axis, and it sent neither: the commit ran on the transfer's reply callback, where AAComm refuses a synchronous send, and looked forCalcFiltersunder an axis-prefixed name the message table never carries. The completion callback still reported success, so every parameter the download wrote was gone after the next power cycle. The commit now runs off that callback, and a failure in it arrives inonDone's error argument instead of being dropped. - The DataLogger CSV export (
GenerateCsvFile) is readable on a comma-decimal host: every time, value and statistics field is written with a dot decimal separator. It rendered under the host culture, so on de-DE, fr-FR and similar locales each fractional value became two columns and no reader could recover the rows. ConfigureSlowLoggeraccepts the documented forty parameters and clears the fortieth slot.LoggerParamsis a 1-based table, and the configure walked it as if it were 0-based: the fortieth parameter was refused as a forty-first, and a stale code left in slot 40 by an earlier configuration kept being logged.ConfigureSlowLoggerrefuses an array parameter given without its index, and an index given on a scalar, with anArgumentExceptionand oneErrorOccurred— the fast recorder's verdicts, on all three specification shapes (string, CAN code,SlowLoggerParameter). An un-indexed array used to be accepted and logged nothing.- Every vector entry point —
VectorLinear(both overloads),VectorArcand theirAAMotionAPIequivalents — refuses a call whose initiating axis is not a member of the move, or not its lowest member, before anything is written: the call returnsfalseand raisesErrorOccurred. The controller reads the profile from the axis that receivesBeginand rejects any other shape (errors 192 and 266), sob.VectorArc(C, H, …)never ran; it used to leave the profile and the members' targets on the controller before the controller refusedBegin. VectorLinear(both overloads) andVectorArcrefuse a move that names an axis past H or fewer than two members, before anything is written. The controller'sVecMemberAxesreaches H and a vector move needs two members (error 196), so such a call never ran; the refusal is now local and reported.VectorLinear(mask overload) refuses a position array shorter than the mask's axis count before the first write. It used to notice the missing position only when its walk reached that axis, after the profile and the earlier axes — motors on — were already on the controller.- The four-axis Central-I
LinearAbsolute(aPos, bPos, cPos, dPos, velCruise, velEnd)now sends the D position. It named D in the segment header but composed only three positions, so the controller refused every call with ERR 181 and no such move ever ran.ArcXT/ArcYT/ArcZThad the same slip - nine fields where the arc row takes ten - and are corrected with it. GenDataWaitandUserParamWaitnow push the wait-array segment type (12). They carried the write type (9) with a wait body, which the controller refuses with ERR 181 before pushing anything; the field order was already the wait segment's, so the arguments are unchanged.- Position-class values no longer read back as 0 on a controller with User Units enabled — the 64-bit parameters
PosandPosRef, which are re-read from the controller by their own path, included. A scaled reply for an integral parameter is a decimal number (2468counts at a0.01 mm/countfactor is24.68), and the client parsed every reply as an integer —24.68failed to parse and the value became 0, soa.Posread zero and any move computed from it went to the wrong place. Replies are now read as decimal numbers and carried at full precision, so a User-Units axis reports its real position. An integration that wants the raw controller counts instead can opt out per controller with the newMotionController.UseUserUnits: set it tofalseat any point, before or afterConnect, and the controller keeps it across connects and reconnects. The opt-out is instance-wide (two controllers sharing oneCommAPIshare it) and does not unlock raw*UnitFct/*UnitUntwrites.ISingleAxis.SetPositionsits outside the scaling: it writes a parameter the controller declares unscaled, so its argument is always in controller counts —SetPosition(axis.Pos)is not a round trip on a scaled axis; divide the display value by the axis position factor, or read the position withUseUserUnits = falseand pass that count back. - Float- and double-typed parameters no longer come back as a re-interpreted bit pattern.
ForceGain,ForceKi,ForceKdand their siblings return the value the controller reports, and writing one sends that value rather than the integer its bits happen to spell. The write is spelled as the shortest invariant text that reads back as the same value, so it no longer loses digits on .NET Framework, and the same value is spelled identically on every framework. - Multi-axis
AAMotionAPI.MoveAbs/MoveRelno longer commandSpeed = 0— a move that silently never runs — when a requested component speed exceeds an axis'sMaxVel. The profile clamp divided twointoperands, so the quotient truncated to zero and every axis was scaled to a standstill; where it did not truncate to zero it still dropped the fraction and under-drove the whole move. The profile is now computed in double precision, with one shared scaling factor equal to the tightest axis constraint, preserving the equal-duration (PTP) relation between the axes. The same clamp turned the shared factor negative when the constraining axis travelled backwards, commanding a negative speed on the axes travelling forwards; the factor is now always a magnitude.Accel/Decelwere scaled by the same clamp and are corrected with it. - Multi-axis
AAMotionAPI.MoveAbs/MoveRelno longer command a negativeSpeed,AccelandDecelon an axis travelling backwards. Each axis was written its signed component of the profile, so the controller rejected that axis'sAccel/Decelwith ERR 14 ("Assigned value out of the range defined for this keyword" — the keyword minimum is 100.0) and ran the move on whatever acceleration profile the axis happened to hold; the negativeSpeed, which the controller does accept, persisted as an axis parameter and then drove the next move on that axis that supplied no speed of its own —MoveAbs/MoveRelwriteSpeedonly when the argument is given, whileJogalways writes its own. Every axis is now commanded the magnitude of its component — direction comes from the target position, as before. - Multi-axis
AAMotionAPI.MoveRelno longer overwrites the caller's distance array with the absolute positions it computes. The array passed in is left untouched, so a caller that reuses it for a second move is no longer silently moving to the wrong place. MotionController.ReadGenDataF/ReadGenDataDandReadUserParamF/ReadUserParamDnow read the controller's reply in the invariant culture. The single-argumentfloat.Parse/double.Parsethey used applied the process culture with its default styles — which accept a group separator, and on a comma-decimal locale that separator is.— so a reply of1.5came back as 15, a tenfold-wrong reading with no error. They now parse invariantly, matching the text AAComm emits.MotionController.WriteGenDataF/WriteGenDataDandWriteUserParam/WriteUserParamF/WriteUserParamDnow render the value they send in the invariant culture. They formatted it with the process culture, so on a comma-decimal localeWriteGenDataF(1, 0.25f)sentAGenDataF[1] = 0,25— and because the controller reads an assignment invariantly and accepts a group separator, the slot silently ended up holding 25. The command was never rejected, so the wrong value went unnoticed; the slot now receives the value that was passed in, on every locale.- Multi-axis
AAMotionAPI.MoveRelno longer truncates the axis's position reference when it converts a relative move to absolute. It accumulatedaryTrg_Pos[i] + (int)aryAxes[i].PosRefinto anintarray, so a fractionalPosRef— what a User-Units axis reports — was dropped before the target was computed and the move landed short by up to one unit. The conversion is now done indouble. - Homing speeds, accelerations, offsets and thresholds are no longer truncated on their way into
HomingDef.Home,HomeToHardStop,HomeToIndex,HomeToLimitThenIndexandHomeToHardStopThenIndexcast every profile argument tointbefore writing its slot; they now carry it whole. ISingleAxis.Home(string filePath)reads the homing-definition file's values as decimal numbers. It parsed eachHomingDef[i] = valueline with an integer parse, so a fractional speed or acceleration in the file was skipped silently and the slot kept whatever it already held.- The ECAM table generators apply the requested range before they push the points, so a generated table lands at the
startIndexthe caller asked for.ECAM_CreateLinearTableandECAM_CreateSinusoidalTablecalledECAM_SetTableDatafirst, and that method takes both its destination and its length cap from the range in force at the time — so on a table that had never been ranged the push failed with anIndexOutOfRangeExceptionbefore a single point reached the controller, and on a table reused from an earlier profile the points landed at the previous start index, truncated to the previous length, under a range the data never filled. ECAM_SetTableDatawrites the last point of a range that ends at the finalGenDataslot. Its loop was bounded by the array's element count on a 1-based array, so the highest slot was silently skipped — a table configured as1022..1024on a controller whoseGenDataends at 1024 arrived one point short. A range that starts belowGenData's first index is now rejected without writing anything, instead of throwingIndexOutOfRangeExceptionon its first slot.ECAM_CreateSinusoidalTablecloses the sweep at any offset. The closing sample was computed fromsin(2π), a tiny negative residue rather than zero, so with a non-zero offset the cast landed the last point one below the first — amplitude 1000, offset 500 over 5 points ended on 499 instead of 500 — and the cam profile stepped at the seam on every cycle. The closing sample is now the opening one exactly.OpticalCouplingLightFinding.GetSingleAxisScanResultnow returns the per-channel peak indices.SingleAxisScanResult.ChannelPeakPowerIndiceswas never filled, so it came back asnull— aNullReferenceExceptionfor a caller reading it alongside the peak powers and positions it sits with. It now carries channels 1-4, from the same slots theChannel1..4PeakPowerIndexproperties read.- The per-axis IO register reads keep the controller's bit pattern when bit 31 is set.
IIOModule.DInPort,DInPortHigh,DInLog,DInLogHigh,DInFilt,DOutLog,DOutType,DOutPortandBiDirConfignarrowed the value slot with a plain cast, so a register reported as an unsigned full-width word —4294967295, every output asserted — read back as-2147483648on .NET Framework and .NET 8 and as2147483647on .NET 10, none of which is the register the controller holds. They now take the same 32-bit two's-complement view the groupDOutbit helpers use, which is identical on every framework. - The analog outputs can be read and written again, one element per output.
IIOModule.AOutGain,AOutShifts,AOutOffset,AOutModeandAOutPortwere not sized from the controller's parameter table, so every element access —io.AOutGain[i]for any output, read or written, orMotionController.GetValueon one of them — threwIndexOutOfRangeExceptionbefore anything was sent.AOutGain[i]reads and writes the i-th analog output, from 1 to the count the controller reports; read an output by its index, because on a controller that lists that parameter with one or more outputs the member's own.ValueraisesInvalidOperationException, as on every array member. A drive without analog outputs (AGD101-EC, AGD156-EC) lists these parameters with no elements; it connects as before, and reading or writing an output there raisesIndexOutOfRangeExceptionwith nothing sent. - An array parameter the controller lists with a single element can be read and written. A parameter at max index 1 was sized as a scalar, so its element 1 threw (
NullReferenceException, orInvalidOperationExceptionthroughIIOModule.AInGain) and a write to it returned normally and sent nothing, losing the value. It is an array of one element, read and written as element 1 like any other array, andGetValue(axis, keyword, 1)reads it. The AGD200 (3.0.33) and AGD155-EC (1.5.0.20) tables listISingleAxis.PosFiltOnandMapTableBtoMapTableEthis way, and the AGD101-EC and AGD156-EC can list the analog inputs so (IIOModule.AInGain,AInMode,AInFilt,AInOffSet,AInDB). - An element of a scalar parameter is refused instead of lost. On an
AAValueArray<double>member whose parameter the connected controller lists as a scalar, such asISingleAxis.GantryPosGainon an AGD200, its one index[0]and enumerating the member raiseInvalidOperationExceptionbefore anything is sent, naming the parameter (GantryPosGain is a scalar parameter; read it through Value.). A read of[0]used to raiseNullReferenceException, and a write returned normally and sent nothing, and enumerating the member then handed the value back as though the controller held it. Read and write such a parameter through.Value. - An element of a 64-bit array parameter is read once.
MotionController.GetValue(axis, keyword, index), and with itaamotion_get_value, composed the element read through the parameter slot and then again through the 64-bit path, so a successful read of an element ofVel,MapStartPos,ModeSwitchPos,ECAMMasterIniorCNCBVel— the 64-bit arrays the client sizes from the AGM800's table — cost two round trips instead of one.ISingleAxis.Vel[i]reads through that same path and doubled with it; the other four are read once through their own members. The value is unchanged, and so are the refusals: an index the parameter does not have is still refused before anything is sent. MotionController.GetValue(axis, keyword, index)reads an element of the arrays the AGM800 lists forGantryPosGain,GantryAccFFW,GantryVelGain,GantryVelKiandPosKi(to 5),GenDataF(to 100),UserParamFandUserParamD(to 20). They were sized as scalars, so an element read raisedIndexOutOfRangeExceptionbefore anything was sent, whileTryGetValueread it.- The CNC segment builders no longer corrupt a segment on a comma-decimal locale.
IGroupAxis.SetMotionProfile's S-factor, and the axis index theWriteUserParam/UserParamWait/MultiWriteUserParam/MultiWriteUserParamWaitbuilders derive from theAxisRefthey are given, were rendered under the process culture; on de-DE, fr-FR or ru-RU the decimal comma became an extra separator inside the comma-delimited segment and shifted every field behind it into the wrong slot. Both now render in the invariant wire format, so a segment is spelled identically on every host. - Reading a controller command as though it were a parameter no longer runs it.
ProgRun,ProgHalt,ProgHaltAll,ProgReset,StopECAMandLoggerUploadname functions the controller performs on receipt, not storage slots — so composing a read of one is the command:ParameterTypeConverter.GetSmartValue(axis, …, eKeyword.ProgRun)started the user program and reported its reply as a value,GetDoubleArray(axis, …, "ProgHalt", 1)sentAProgHalt[1]and halted user-program thread 1, andMotionController.RegisterCacheKeywords(axis, eKeyword.ProgRun)put the command in the fast-read cache's refresh bulk, re-sending it every millisecond for as long as the cache ran. All six are now refused with anArgumentExceptiononGetSmartValue,GetArrayandGetDoubleArray— which covers theprotected Node.GetSmartValuea derived controller inherits — and onRegisterCacheKeywords/FastReadCache.RegisterArrayElements, before anything reaches the wire; a registration naming one is refused whole, so no keyword beside it is registered.MotionController.GetValuealready refused them. This is not a rule that a command never answers with data: many controller commands take a parameter and reply with a value, andMotionController.LoggerUpload()— which retrieves the slow-logger data by sendingALoggerUploaditself — is unaffected. - Expanding a
MotionController, an axis or a parameter array in the debugger no longer hangs the evaluation. Every value property on them is a controller round trip, so a debugger expanding one sent a read per member while it held frozen the thread that delivers the reply, and hung on the first. The debugger now shows their identity only — the kind and axis, a controller's last known address, an array's keyword and index range — and sends nothing to the controller.
Removed
- The
MotionController.ProgHaltAllproperty. It exposed theProgHaltAllcommand as a readable value slot, so evaluating it — in code, or in a debugger watch expanding the controller — sentAProgHaltAlland halted every user program. Halt them deliberately withISingleAxis.ProgHaltAll()orAAMotionAPI.ProgHaltAll(controller, axis), which are unchanged.
[11.0.0] - 2026-08-26
⚠️ Breaking
AAMotion.dllis now strong-named (PublicKeyToken=043a10799a9b732b, the same key asAgito.AAComm14.x), so strong-named consumers can reference it. .NET Framework consumers must recompile against 11.0.0 — a binary built against a 10.xAAMotion, PublicKeyToken=nullreference does not bind to the signed assembly (see the Agito.AAComm 14.0.0 changelog for the details; the same identity change andAssemblyVersionpinning, here11.0.0.0, apply). .NET 8/10 consumers see no difference.- Requires
Agito.AAComm14.x (see its changelog for the breaking communication-layer changes — long-operation events, binary IPC v3, protocol negotiation). AAMotionAPI.WriteUserParam(controller, AxisRef, index, value)is renamedGroupWriteUserParam— it issues the CNC group-buffered write (command 9), not a direct register write, and the old name collided with the new directWriteUserParam(controller, index, value)(see Added). TheIGroupAxis.WriteUserParammember keeps its name (the receiver disambiguates); the Java binding follows the facade rename.AAMotionAPI.OpendAACommServer()is renamedOpenAACommServer()(typo fix).AAMotionAPI.ForceCurr_SetSlowApproacheis renamedForceCurr_SetSlowApproach(typo fix) — also onISingleAxis, the native ABI export, and the Java binding.MotionController.ErrorOccurredandCommAPIInterface.ErrorOccurredare noweventmembers (were public delegate fields).+=/-=subscriptions recompile as-is; direct assignment or external invocation no longer compiles.
Fixed
Connectno longer returnsfalseon a busy machine while the server is running. It gates onCommAPI.IsAACommServerRunning, whose loopback probe used to depend on ThreadPool scheduling and so reported a listening server as absent under CPU pressure — reported only through aDebug.WriteLine, making it look like a silent connect failure. Fixed in AAComm; see its changelog.AAMotionAPI.AutoCorner: thecornerAxesargument was incorrectly passed toGetGroup(), whose dictionary keys are onlyAxisRef.A(orAxisRef.Bon AGM800). Every call path was broken —GetGroup(axis)withaxis = AxisRef.Aresolved to a 1-axis group (rejected by the controller asCNC_FIFO_WRONG_NUM_OF_INVOLVED_AXES), andaxis = AxisRef.A | AxisRef.Bcaused aKeyNotFoundExceptionon the unguarded indexer. Now resolves the group viaGetGroup(AxisRef.A)like all other CNC facade methods, and forwards the full mask to the wire segment. Source- and binary-compatible; no signature or ABI change.
Added
Native
aamotion.*C-ABI SDK (Agito.AAMotion.Native): a NativeAOT shared library (aamotion.dll/libaamotion.so/libaamotion.dylib+aamotion.h) exposing the full flat motion surface (199 functions, eachaamotion_+ the managed name in snake_case, constantsAAMOTION_*) over the AAMotion client with no .NET runtime on the target, for C / C++ / Python (ctypes) consumers. Packaged as theAgito.AAMotion.NativeNuGet (runtimes/<rid>/native/, for .NET P/Invoke) and anagito-aamotion-native-sdk-*.zipC/C++ archive, version-locked toAgito.AAMotionand published together on the publicSoftware/AAMotion-Nativerelease line at https://github.com/akribis-agito/Agito-Releases/releases — not on nuget.org, because the C ABI is exact-match gated pre-1.0. win-x64 (HW-proven on AGM800) and linux-x64 ship; osx pends its build runner.AAMotionAPI.UploadParameters()/DownloadParameters(): New static methods onAAMotionAPIthat upload controller parameters to a.parfile or download parameters from a.parfile to the controller. Follows the PCSuite "Parameters Upload/Download" convention: upload queries all supported parameters and writes them with a header (product type, firmware/FPGA version, date, checksum) to an ASCII.parfile; download reads the.parfile, validates parameter compatibility, then writes each as a SET command. Ethernet connections use Bulk messages (up to 50 per packet); other channels send one by one. After a successful download, optionally sendsASaveplus per-axisCalcFiltersto persist parameters to flash.ISingleAxis.MoveAbsRepetitiveCycle()/AAMotionAPI.MoveAbsRepetitiveCycle(): New method onISingleAxis/SingleAxisand a corresponding static wrapper onAAMotionAPIthat moves the axis to an absolute position repeatedly for a finite number of cycles with a dwell time at each end point.rptCycles= 0 requests infinite cycles (controller default). The method setsRptMode = 0,RptCycles = {rptCycles},RelTrgt = 0,AbsTrgt = {position},RptWait = {msDwellTime},MotionMode = 2, then issuesBegin.The package now also ships a net10.0 target (alongside net48 and net8.0) — matching
Agito.AAComm14.x; .NET 10 is the current LTS runtime.Two-speed point-to-point moves:
ISingleAxis/AAMotionAPIMoveAbsTwoSpeed(AndWait)/MoveRelTwoSpeed(AndWait)— the move runs atspeed1and switches tospeed2when crossingspeedChangePosition(Abs) / afterspeedChangeDistance(Rel), for slow-approach flows (bonding, dispensing). The optionalspeedChgDir(0 = forward, 1 = reverse crossing) defaults to 0 on Abs and to the distance sign on Rel; the native ABI takes it as a plainintwhere a negative value selects that default.Direct GenData/UserParam register I/O in float and double widths:
MotionController/AAMotionAPIReadGenDataF/ReadGenDataD,WriteGenDataF/WriteGenDataD,ReadUserParam/ReadUserParamF/ReadUserParamD,WriteUserParam/WriteUserParamF/WriteUserParamD(writesA<reg>[i]=vdirectly, complementing the int-typedReadGenData/WriteGenData), backed by the neweKeyword.UserParam/UserParamF/UserParamDmappings.Optical coupling light-finding API: new
OpticalCouplingLightFindingclass inAAMotion/Shared/OpticalCouplingLightFinding.csproviding a 2-step workflow (parameter configuration, then start) for optical alignment processes.- Supports spiral scan (
ScanType.Spiral = 1) and single-axis scan (ScanType.SingleAxis = 5). - Exposes configuration properties mapped to controller
AGenDataregisters (e.g.ScanCode,CouplingAxis,SpiralPitch,MaxScanSpeed,OptimalCoeff). - Provides typed result models:
SpiralScanResultandSingleAxisScanResultwith coupling result, error code, sampled positions, and channel power data. - Includes helper methods to start scans, apply configurations, read sampled arrays,
and batch-sync
AGenDataviaSyncAllGenData.
- Supports spiral scan (
Fixed
AAMotionAPI.OpenAACommServer(string)andCommAPIInterface.OpenAACommServer(string)now spawn the server through the AAComm spawn contract (headless + redirected stdin, correct working directory, socket-ready polling with a child-death probe) instead of a bareProcess.Start+ fixed 1.5 s sleep — a slow cold start, a lost bind race, or an interactive native host no longer yields a false "started" (or a TUI taking over the consumer's terminal).AAMotionAPI.OpenAACommServer()no longer latches "started" after a FAILED start: the first failure (missing sidecar, blocked port) previously made every later call a silent no-op even after the environment was fixed.- The
*AndWaitmotion waits (MoveAbsAndWait,MoveRelAndWait, the two-speed and repetitive variants) now poll the controller's in-target status live, never through the FastReadCache. WithFastReadEnabled, a cached pre-moveInTargetStat=4(the axis parked on its previous target) — or a value stranded by a link failure, which never invalidates cache entries — could satisfy the wait instantly, reporting "target reached" for a move that never happened. MotionController.TryReconnectno longer trusts the raw socket alone: a logically-connected link is verified by ping (where the address is a real IP) before the call short-circuits to success, so a pulled cable / network partition now triggers a real disconnect-and-reconnect instead of "reconnected" with a dead link — this also makesIsConnected's auto-reconnect path honest. With no requested address and no last known address the call now fails instead of silently targeting a hardcoded default IP (a reachable but unintended controller could be connected). A successful reconnect to an explicit new address now updates the last known address, so reachability probes and later reconnects track the live link, not the device the controller left behind.SendBulkCommandStringno longer reports success when an error rides in a non-first segment of the bulk reply (e.g.OK>OK>ERR 4010) — the whole-reply error flag misses embedded segments, so classification is now per-segment against the full reply taxonomy (ERR/BOOT ERR/PC Suite: ERR).- Controller replies carrying int/long values are now parsed culture-invariantly, matching the
float/double path. On a machine whose culture uses a non-ASCII negative sign (several ICU
cultures on modern .NET), a negative reply like
-123failed to parse and read as0; parameter values written to the controller now also format invariantly on every path. MotionController.IsConnectedno longer reportsfalsefor healthy non-IP connections: the ICMP reachability probe now runs only when the controller was connected via a real IP address — a Simulator / serial / CAN connection reports the logical connection state (previously the unparseable address failed the ping and the property was permanentlyfalse), and when no address is known the property no longer pings a hardcoded default IP (another device's reachability was reported as this controller's).- Bulk parameter upload/download keeps request↔reply alignment: an empty reply segment no
longer shifts every later reply onto the previous command's slot (misattributed values in
the
.parfile / on the controller), and a short bulk reply pads the unanswered tail with the no-reply error filler instead of re-sending the boundary commands (a re-send could execute a parameter write twice). BulkCommandParser(index, response)now honors its documented 1-based contract: index 1 returns the first segment (previously the second), the last index returns the last segment (previously threwIndexOutOfRangeException), and out-of-range indexes returnnull.- Tearing down one
MotionControllerno longer harms the others in the process. A garbage-collected (undisposed) controller's finalizer calledShutdown()— which could take down the shared AACommServer — and its disconnect cleared the process-wide parameter-metadata cache under live sibling controllers, silently degrading their float parameter reads/writes to int handling until their next connect. The finalizer now only disconnects its own controller,Dispose()suppresses the finalizer, disconnect no longer clears the shared cache (each connect atomically rebuilds it), and only an explicitShutdown()touches the server. The nativeaamotionSDK'sRelease/Disconnectlikewise no longer shut the server down — the explicitShutdownexport is the only path that may. - Enabling the fast-read cache no longer risks tearing down the connection when any call slower than ~200 ms is in flight — the background cache refresh used a hardcoded 100 ms timeout, tighter than any other command's wire budget; it now uses the default message timeout (a slow reply just delays the next refresh while readers serve the cached value).
FastReadCachereads now honor validity: a registered-but-never-refreshed entry is a cache miss (falls back to a live read) instead of a fabricated0, and a value whose refresh failed is not served stale. Disconnect invalidates all entries (no unbounded-stale values while no refresh can run) and the background refresh no-ops while disconnected instead of spamming an error event per interval — throttled to an idle poll (500 ms) so a 1 ms-interval cache no longer burns ~1000 no-op tasks per second while disconnected; it resumes the configured cadence by itself after a reconnect.FastReadCache.Dispose()no longer throwsObjectDisposedExceptionon any cache that was ever used (its own disposed-guard fired mid-dispose) — on .NET Framework a garbage-collected undisposed cache could take the process down from the finalizer thread.UploadParameters()/DownloadParameters()summaries no longer count API-synthesized error replies (PC Suite: ERR …,BOOT … ERR, e.g. from a connection lost mid-transfer) as successes — error classification now matches the full AAComm reply taxonomy instead of only controllerERR …replies.
Removed
- The C++/CLI
AAMotionCPP(mixed-mode) andAGAAMotion(native shim) projects have been retired. Non-.NET (C/C++) consumers move to the new C-ABI SDK —aamotion.h+aamotion.dll/libaamotion.so/libaamotion.dylib(projectAAMotionNative). The managedAgito.AAMotionAPI and the Java binding are unaffected.
[10.0.0] - 2026-05-25
Added
EnsureAACommServerRunning(): New method onIMotionController/MotionControllerthat ensures the AACommServer is running and ready to accept connections. Launches the server if not already running, with proper path resolution.
Deprecated
AAMotionConfig.UseAsyncCommTransport: in AAComm 13.0.0 both flag values produce equivalent observable transport behavior for callers using a single path serially (false→CommAPI.SendReceiveover the sync flow;true→CommAPI.Send+ an internal wait-handle over the async flow — see issue #32 history). The flag is therefore a no-op for typical usage and will be removed in a future release.
Changed
- DataLogger
StartDataRecording: AGD155 and AGD155-EC now useARecStart[1]command syntax (firmware v2.3+); other controllers continue to useARecStartwithout index. - Refactored DataLogger and SlowLogger to route all communication through
CommAPIInterface.SendAndRecieve()instead of directly callingCommAPI.SendReceive(). This ensures consistent behavior whenAAMotionConfig.UseAsyncCommTransportis enabled (async) or disabled (sync).
[9.1.1] - 2026-04-17
Changed
- Packaging: picks up AAComm 12.1.1 sidecar change. AACommServer sidecar is now
selected by consumer OS rather than TFM: Windows consumers (net48 or net8.0)
get the WPF
AACommServer.exe; non-Windows net8.0 consumers getAACommServerNetConsole. AAMotion consumers inherit this transitively — no code or reference changes required.
[9.1.0] - 2026-04-15
Fixed
ScanController/ScanControllerDetailed: Replaced unreliable TCP probe (TcpPortReachable) with ARP-based discovery viaARPTools.GetSubnetActiveIPs. This uses the same multi-NIC ARP scan as PCSuite's Discover Connections, ensuring controllers on all Ethernet interfaces are reliably found.- Fixes: random discovery failures with multiple NICs on the same subnet (OS routing picked one interface only)
- Fixes:
NullReferenceExceptioninTcpClient.EndConnectwhen scanning IPs with no controller (disposed socket from danglingConnectAsynctask) - Removed
TcpPortReachableprivate method (no longer needed)
[9.0.0] - 2026-04-03
Changed
- Decoupled nuget from direct AAComm dependency: AAComm is now resolved via nuget
[8.4.1] - 2026-03-27
Added
- Added
LockSrcZeroSupported()method toISingleAxis/SingleAxis, exposing Identity[62] bit 0x10 (ControllerIdentityFeature_02.LockSrcZero_Supported). WhenlockSource = 0is passed to the three multi-step homing methods on an unsupported controller,NotSupportedExceptionis thrown. - Added
ControllerIdentityFeature_02.IDENTITY_NEW_FEATURES_2_LOCKSRC_ZERO_SUPPORTED = 0x00000010constant. - Added three extended multi-step homing methods to
ISingleAxis/SingleAxis:HomeToHardStopThenIndex— motor on → arm Lock → hard stop on high following error → jog to hardware index → move to Locked position → disarm Lock → wait → set position → end. Accel/emer decel auto-computed (×10 and ×100 of velocity); all timeouts fixed at 15000 ms. PopulatesHomingDefas PCSuite step types 8, 14, 10, 15, 16, 14, 7, 6, 0.HomeToIndex— motor on → arm Lock → jog to hardware index → move to Locked position → disarm Lock → wait → set position → end. Accel/emer decel auto-computed; all timeouts fixed at 15000 ms. PopulatesHomingDefas PCSuite step types 8, 14, 15, 16, 14, 7, 6, 0.HomeToLimitThenIndex— motor on → arm Lock → jog to limit switch → jog to hardware index → move to Locked position → disarm Lock → wait → set position → end. Accel/emer decel auto-computed; all timeouts fixed at 15000 ms. PopulatesHomingDefas PCSuite step types 8, 14, 1, 15, 16, 14, 7, 6, 0.
[8.4.0] - 2026-03-25
Added
- Added FastReadCache high-performance cached read mechanism:
- New
FastReadCacheclass inShared/FastReadCache.csfor low-latency parameter reads. - Supports caching both single-value keywords (e.g.,
Speed,Pos,MotionStat) and array elements (e.g.,AInPort[1]). - Uses a background timer to batch-refresh all registered values via bulk commands at configurable intervals (default 1ms).
- Built-in events:
RefreshStarted,RefreshCompleted,RefreshErrorfor monitoring. - Methods:
Enable()/Disable()to activate/deactivate,RegisterKeywords()/RegisterArrayElements()to specify what to cache,GetCachedValue()/GetCachedArrayValue()to read,RefreshNow()for forced refresh,GetCacheAgeMs()to check staleness.
- New
Changed
AAMotionConfignow exposesFastReadEnabled(global on/off switch) andFastReadCacheRefreshIntervalMs(default 1ms).MotionControllernow exposesFastReadCacheproperty,EnableFastReadCache(),DisableFastReadCache(),RegisterCacheKeywords(),RefreshCacheNow(), andGetCacheAgeMs().CommAPIInterface.HandleSingleValueAccessedandHandleArrayValueAccessednow check the cache first when enabled, falling back to controller reads.InitializeFastReadCacheIfEnabled()is automatically called after successfulConnect()— registers A/B/C axes' common keywords (Pos,MotionStat,InTargetStat) andAInPort[1-4], then enables the cache ifAAMotionConfig.FastReadEnabledis true.
Added
- Added
CommAPIproperty onMotionControllerto expose the underlyingAAComm.CommAPIinstance for direct low-level communication with maximum performance.
[8.3.0] - 2026-03-17
-AACommServer + API: Ethernet packet sniffer for debugging communication issues - Auto-captures traffic on Ethernet channel open/close - Session snapshots saved to Error/User/Marked folders on disconnect - UI: status indicator, statistics, enable checkbox, mark capture button - Ring buffer with configurable retention policies
[8.2.2] - 2026-02-26
Added
- Added
GenerateCsvFile(DataLoggerResult result, string filePath = null)in DataLogger API to export recorded data to a CSV file:- Accepts a directory path or full file path; uses current directory when path is null or empty.
- Generates timestamped file name when a directory is specified.
- Writes UTF-8 with BOM for correct Chinese display in Excel.
- Includes metadata (export time, record length, gap, parameter count, data point count), parameter information (name, CAN code, user units), data rows, and statistics (Min, Max, Average).
Changed
- DataLogger
GetDataRecordingStatus: AGD155 and AGD155-EC now use keywordARecStat(no array index); other controllers continue to useARecStat[1].
[8.2.1] - 2026-01-14
Added
- Added
GetDataRecordingCapacitymethod to DataLogger API for querying the maximum recording capacity of the controller.
Changed
- Improved auto-reconnect logic in
IsConnectedproperty:- Enhanced reconnection mechanism to continuously retry when connection is lost
- Added thread-safe reconnection with lock mechanism to prevent multiple threads from triggering reconnection simultaneously
- Improved state tracking to ensure reconnection attempts continue even after initial failure
- Reconnection will retry every 2 seconds if connection remains lost, up to 4 attempts per cycle
[8.2.0] - 2026-01-10
Added
- Added DataLogger high-speed recording API on
MotionController:- New partial implementation in
Shared/DataLogger.cswrapping AACommRecDataservices. DataLoggerConfigurationto configure duration (sec), gap (ms), parameters list, and trigger position.DataLoggerTriggerto configure trigger type, source, value, mask and optional max value (for range triggers).DataLoggerResult/DataLoggerVectorcontainers exposing recorded data, time vector, CAN codes and user units.- High-level helper methods:
ConfigureDataLogger,StartDataRecording,StopDataRecording,ForceDataLoggerTrigger,WaitForDataRecordingToEnd,UploadDataLoggerData, and one-shotRecordData.
- New partial implementation in
Changed
- Improved XML documentation for DataLogger trigger parameters with clearer English/Chinese descriptions to match PCSuite terminology (持续时间, 间隔, 触发器, 掩码, 最大触发值, 触发时刻).
[8.1.1] - 2026-01-09
Changed
- Keyword filtering now relies solely on MessagesContainer (from AAbout command) instead of ControllerKeywordFilter
- Removed
ControllerKeywordFilterclass and all firmware version-based filtering rules - Keyword availability is now determined directly from the controller's
AAboutresponse, which contains the authoritative list of supported keywords - This approach automatically supports any firmware version without requiring manual filter rule updates
- Keywords are filtered based on what the controller actually reports, ensuring perfect compatibility with any firmware version
- Simplified
KeywordRegistryconstructor by removingcontrollerTypeandfirmwareVersionparameters - Removed firmware version reading from
AIdentity[26-28]during connection (no longer needed)
- Removed
Removed
- Removed
ControllerKeywordFilterclass and all keyword filtering rules - Removed firmware version detection code from
CommAPIInterface.Connect()method
[8.1.0] - 2026-01-07
Change
- Exclude the keywords of the CNCB group (except AGM800).
[8.0.9] - 2026-01-06
Fixed
- Corrected CNC buffer clear routing so GroupA and GroupB send to their own CNC groups.
[8.0.8] - 2026-01-06
Added
- Added CNCB (CNC Group B) keyword properties to
ICiGroupAxisinterface andCiGroupAxisimplementation for AGM800 controller:- Read-only properties:
CNCBSpeed,CNCBAccel,CNCBDecel,CNCBJerk,CNCBEndSpeed,CNCBAbsTrgt,CNCBPosRef,CNCBdPosRef,CNCBStatus,CNCBVel - Read/Write properties:
CNCBEmrgDec,CNCBPercents,CNCBSpeedPer,CNCBPause - These properties allow full access to CNCB parameters and status, enabling complete control and monitoring of CNC Group B operations on AGM800 controllers.
- Read-only properties:
[8.0.7] - 2026-01-06
Changed
IsCNCCompletednow supports AGM800's GroupB (AxisRef.B). For GroupB, it reads{axis}CNCBStatus[10]instead ofCNCAStatus[10].ControllerTypeenum values now referenceProductTypesconstants to eliminate duplicate definitions and ensure single source of truth for product type IDs.
[8.0.6] - 2026-01-04
Added
- Added CNC
Begin(AxisRef axisRef)method to specify the axis for CNC operations.
[8.0.5] - 2026-01-04
Added
- Added firmware version-based keyword filtering system in
ControllerKeywordFilter:- Unified configuration dictionary
KeywordFilterRulesfor all keyword filtering rules (controller type and firmware version dependent) - Automatic firmware version detection from
AIdentity[26-30]during connection:AIdentity[26]= Major versionAIdentity[27]= Minor versionAIdentity[28]= Patch version- Combined version format:
Major * 1000 + Minor * 100 + Patch(e.g., 3.0.42.0.0 → 3042)
- Support for firmware version-dependent keyword filtering:
- Keywords can be configured to require a minimum firmware version
- If firmware version is unknown, version-dependent keywords are skipped by default (safe default)
- AGD301 controller:
MapTableB,MapTableC,MapTableD,MapTableEkeywords now require firmware version >= 3042 (3.0.42.0.0)- These keywords are automatically skipped for AGD301 controllers with firmware version < 3042
- These keywords are available for AGD301 controllers with firmware version >= 3042
- Unified configuration dictionary
Changed
- Refactored keyword filtering system to use unified configuration:
- Consolidated all keyword filtering rules (always-skip and version-dependent) into a single
KeywordFilterRulesdictionary - Removed separate
ControllerBlacklistdictionary in favor of unified configuration - Improved maintainability: all filtering rules are now in one place with clear version requirements
- Consolidated all keyword filtering rules (always-skip and version-dependent) into a single
- Enhanced
KeywordRegistryto accept firmware version parameter for dynamic keyword filtering - Improved
CommAPIInterface.Connect()method to read and parse firmware version from controller during connection
Fixed
- Fixed keyword filtering to properly handle firmware version-dependent keywords for different controller types
- Improved keyword availability detection based on actual controller firmware version
[8.0.4] - 2025-12-22
Fixed
EventPulseResnow reports the correct value (previously always 0).
Added
- Added
CommutatedandIsCommutatedAPIs.
[8.0.3] - 2025-12-17
Fixed
- Fixed vector motion
Beginbehavior inSingleAxis.Vector*helpers to only send oneBegincommand for the currently selected axis, ensuring correct behavior when multiple member axes participate in a vector move.
[8.0.2] - 2025-12-12
Added
- Added controller discovery helpers in
AAMotionAPI:ScanControllerquickly probes a subnet for reachable controllers and returns their IP addresses.ScanControllerDetailedreturns fullConnectionDataentries for discovered controllers using parallel probing.
[8.0.1] - 2025-12-02
Added
- Added global communication transport switch
AAMotionConfig.UseAsyncCommTransportto control how AAMotion sends commands to AAComm:false(default): Legacy synchronous mode usingCommAPI.SendReceivewith internal locking for thread safety.true: Asynchronous queue-based mode usingCommAPI.Sendplus per-call wait handles for high-concurrency scenarios.
- Added
SendViaAsyncQueueimplementation inCommAPIInterfaceto wrap AAComm's asyncSendAPI into a synchronous-style call at AAMotion level.
Changed
- Centralized communication error handling in
CommAPIInterfacevia unifiedErrorHandlers(AACommEventArgs status)helper:- All synchronous and asynchronous send paths now route errors through a single handler.
MotionController.SendCommandStringandSendBulkCommandStringrely onCommAPIInterface.ErrorOccurredinstead of duplicating error formatting logic.
- Improved thread-safety and debuggability of communication:
- Synchronous mode uses an internal lock to serialize access to
SendReceive, preventing"SendReceive is in progress"errors under multi-threading. - Asynchronous mode avoids mixing AAComm's synchronous and asynchronous APIs while still providing a synchronous programming model to AAMotion callers.
- Synchronous mode uses an internal lock to serialize access to
[8.0.0] - 2025-12-01
Added
- Deprecate .NET 4.0 target framework. The minimum supported .NET framework is now 4.8
[7.6.2] - 2025-11-25
Added
- Added SlowLogger functionality for data logging and monitoring
- Added
LoggerOnproperty to enable/disable slow logger (1 = enabled, 0 = disabled) - Added
LoggerGapproperty to set sampling interval (16 samples per group, approximately 1.0 ms per group) - Added
LoggerParamsarray property to configure up to 40 logger parameters (1-40, organized in 8 groups) - Added
LoggerFullModproperty to control FIFO full mode (0 = pause logging, 1 = overwrite oldest data) - Added
LoggerStatusarray property to monitor logger status and statistics - Added
LoggerAboutarray property to get controller slow logger metadata - Added convenience properties:
LoggerFreeSpace,LoggerState,LoggerPushedPackets,LoggerLostPackets - Added
ConfigureSlowLogger()method to configure logger parameters in one call with flexible parameter specification:- Supports string parameters (e.g., "CPos", "AVel[2]", "Pos")
- Supports int CAN codes (direct assignment)
- Supports
SlowLoggerParameterstruct for strongly-typed parameter description - Supports
eKeywordenum values
- Added
LoggerUpload()method to upload slow logger data from the controller - Added
SlowLoggerParameterstruct for strongly-typed logger parameter specification (Axis/Keyword/Array index) - Added
GetKeywordCANCode()method overloads to get CAN codes for keywords:- Base CAN code retrieval (without axis/array index)
- Complete CAN code retrieval (with axis and optional array index)
- Support for both
eKeywordenum and string keyword names
- Added
[7.6.1] - 2025-11-17
Added
- Added
EnumPropertyHelperstatic class to provide enumeration value description functionality- Added
GetDescription()extension method for all enum types to automatically retrieve friendly descriptions from resource files - Added description retrieval methods for specific enumeration types:
GetMotionStatDes()- Get description for MotionStat (bit flag enum)GetMotionReasonDes()- Get description for MotionReasonGetStatRegDes()- Get description for StatReg (bit flag enum)GetHomingStatDes()- Get description for HomingStatGetInTargetStatDes()- Get description for InTargetStatGetConFltDes()- Get description for ConFltGetEncTypeDes()- Get description for EncTypeGetMotionModeDes()- Get description for MotionModeGetMapTypeDes()- Get description for MapTypeGetOperationModeDes()- Get description for OperationModeGetMotorOnDes()- Get description for MotorOnGetInjectTypeDes()- Get description for InjectType
- Support for both bit flag enums and regular enums with automatic description combination for multiple set bits
- Automatic fallback to "Unknown" descriptions when enum values are invalid or not found in resource files
- All descriptions are retrieved from
Descriptions.resxresource file using unified naming convention:{EnumName}_{EnumValue}
- Added
[7.6.0] - 2025-10-31
Added
- Added global configuration class
AAMotionConfigwithUseNativeDataTypesproperty to control data type behaviorUseNativeDataTypes = false(default): Backward compatibility mode - float parameters return int (rounded), long parameters return int if within int rangeUseNativeDataTypes = true: Native type mode - returns real float/double/long values with full precision
- Added
ParameterMetadataCacheclass for caching parameter type information to improve performance - Added
ParameterTypeConverterstatic class to centralize type conversion logic for controller parameters - Added unified
AASmartArrayWrapperclass to automatically handle type conversion for array properties (int/float/double/long) - Added support for native float, double, and long data types with automatic type detection and conversion
Changed
- Refactored parameter type conversion logic from
SingleAxis.csto centralizedParameterTypeConverterclass - Enhanced array wrapper system to automatically detect and handle int/float/double/long types based on parameter metadata
- Improved type conversion performance by caching parameter metadata on connection initialization
- Refactored
GetSmartValueandSetSmartValuemethods inNode.csto use centralizedParameterTypeConverter - Improved code organization in
PropertyTypeSystem.cswith better method grouping and formatting - Enhanced backward compatibility: old code expecting int types continues to work without modification
- Optimized
KeywordMappingsinitialization inConnect()method to handle controller type detection failures gracefully
Fixed
- Fixed type conversion errors when accessing float/double/long parameters in old code that expects int types
- Fixed
KeyNotFoundExceptionwhenKeywordMappingswas not initialized due to controller connection failures - Fixed missing error handling for controller type detection during connection
[7.5.5] - 2025-09-24
Changed
- Add the retry reconnection mechanism. Method: TryReconnect()
- Add the automatic connection mechanism. In the Connect() method, add the parameter enableAutoReconnect to enable/disable the automatic reconnection mechanism.
- AGM800 supports linear and ARC movements for all axes.
[7.5.4] - 2025-09-12
Changed
- Add CNC attributes :CNCASpeed, CNCAAccel, CNCADecel, CNCAJerk
- Add ECAM attributes: ECAMStart, ECAMStartCyc, ECAMEndCyc, ECAMEnd, ECAMGap, ECAMCycles, ECAMMasterIni, ECAMCycCount, ECAMInterp, ECAMMaster, StopECAM, ECAMTableNum
- Increase the setting for communication timeout
- Enhance ECAM functions
[7.5.3] - 2025-08-26
Changed
- Added a check for relative motion to disallow zero distance.
- Added IO_B for AGD20.
[7.5.2] - 2025-08-08
Changed
- Change "Commutated" to "AutoPhase". Change "IsCommutated" to "IsAutoPhase"
- Add the keywords CNCAEncFactNu, CNCAEncFactDn, CNCASpeedPer, CNCAEndSegMod,AccelFact
- Add methods BulkCommandParser and BulkCommandParserInt
[7.5.1] - 2025-08-04
Changed
- Add WriteGenData and ReadGenData to read and write GenData
- A dynamic keyword shielding mechanism based on controller type has been implemented, which has enhanced the system's compatibility and maintainability.
- Initialization and connection exception information optimization
Fixed
- The issue of the AGD101-EC controller connection failure has been resolved.
[7.4.12] - 2025-07-30
Changed
- Add the keyword "EventPulseRes"
- Override MoveAbs to achieve position override
[7.4.11] - 2025-07-22
Changed
- Add methods SetEventTableBeginIndex, SetEventTableEndIndex, SetEventTableSelect, and SetEventTableValue to be compatible with older versions of PEG
- Modify the return type of ClearConFlt to be bool
[7.4.10] - 2025-07-18
Changed
- Add method RunCNCProgramFromFile (use the CNC file generated by PCSuite to perform interpolation movement)
- Add method IsCNCCompleted (determine whether the CNC movement has been completed)
[7.4.9] - 2025-07-17
Changed
- Add the MotorOn and InjectType enumeration types along with the English descriptions of the parameters.
- Added methods MoveAbsAndWait, MoveAbsRepetitiveAndWait, MoveRelAndWait, MoveRelRepetitiveAndWait. Return true after movement and positioning.
[7.4.8] - 2025-07-04
Changed
- Add enumeration types and English descriptions for parameters to enhance usability.
- After the vector motion parameters are configured, the "Begin" command is automatically sent.
[7.4.7] - 2025-06-26
Fixed
- Modify the array length of HomingDef.
[7.4.6] - 2025-06-16
Changed
- The AAValueArray array starts from index 1.
- Add the keywords MapTableB, MapTableC, MapTableD, and MapTableE.
[7.4.5] - 2025-06-11
Changed
- Add the method ClearConFlt() to clear ConFlt.
- Add the methods GetDOutPort and GetDInPort to obtain the IO status of the specified bit.
[7.4.4] - 2025-06-05
Changed
- Add annotations to methods and attributes.
[7.4.3] - 2025-06-03
Changed
- Add method and attribute annotations.
Fixed
- Add the minimum axis judgment of the VectorLinear method of Vector.
[7.4.2] - 2025-05-30
Changed
- Change IsHomed to IsHoming and determine whether the return to home is complete.
[7.4.1] - 2025-05-29
Changed
- Add Home() to set HomingOn=1.
[7.4.0] - 2025-05-29
Changed
- Add the Vector function.
[7.3.13] - 2025-05-23
Changed
- OpenAACommServer increases the return value ConnectResult.
[7.3.12] - 2025-05-23
Changed
- The Amotion API of CiGroup adds operations on GroupB.
[7.3.11] - 2025-05-21
Changed
- The MoveRel and MoveAbs of the AAMotionAPI add MotionController (different controller instances can be used).
[7.3.10] - 2025-05-20
Changed
- Commutation success added judgment 200: Not required, 300/400: Successfully completed (using Hall), 500: Successfully completed (parameters have been modified).
- Adding a judgment to return to zero completes IsHoming().
[7.3.9] - 2025-05-06
Changed
- Add GroupB to AGM800.
[7.3.8] - 2025-04-24
Changed
- Add force control function.
- Add attributes "RptMode", "RptCycles", and "RptCounter".
[7.3.7] - 2025-04-16
Changed
- Added axis properties CurrDir, RevPLim, FwdPLim, MaxPosErr, MaxVelErr.
[7.3.6] - 2025-04-03
Changed
- Adapt to AAComm.StartAACommServer() return type change from 'string' to 'ConnectResult'.
[7.3.5] - 2025-04-03
Changed
- PEG and Lock do not need to specify an axis when using an axis object.
[7.3.4] - 2025-03-27
Changed
- Increase the Commutate method commutate and determine whether commutation is complete.
[7.3.3] - 2025-03-27
Changed
- The fourth parameter of SetMotionProfile is double.
[7.3.2] - 2025-03-21
Changed
- Add the SetLock method to set the Lock function.
Fixed
- Delete the attribute array VecPosFDef. This attribute is not in the dictionary.
- Make four connection attempts while making a connection in AAmotion.
[7.3.1] - 2025-03-20
Changed
- Repair Group can set the specified bit of the specified axis.
- Add PEG methods, SetSingleEventPEG and SetEventFixedGapPEG.
- Maintain the AAMotionAPI and do not need to specify an axis when using Group.
[7.3.0] - 2025-03-18
Changed
- Implement AAValueArray's foreach method.
- Add and implement the Java language call AAMotion project. AAMotionJava.
Fixed
- Fixed VecPosFDef attribute being AAValueArray
. - Adds the undefined attribute int UserPWMDiv. AAValueArray
MapTable. int EventOn.
[7.2.4] - 2025-03-05
Changed
- Add the method CiGroupDOutSetBit, CiGroupDOutClearBit, CiGroupDOutToggleBit. When using CNCGroup, can set, clear, and switch the specified axis (AAMotionAPI also add).
- AAValueArray
CNCAStatus, added to the registry.
Fixed
- Fixed the return value error of MotionReason, ForceCmdValVecArcDir, VecSpeed, VecType, LoggerFullMod.
[7.2.3] - 2025-02-18
Changed
- Add Home(filepath) to return to zero based on the zeroback file.
Fixed
- Fix the Group API method using GetGroup.
[7.2.2] - 2025-02-12
Changed
- Add the noAck parameter of Connect.
- Deleted the directory "\AAMotion\Implementations\Central_I\Core".
- Update yaml.net nuget version to 16.3.0.
[7.2.1] - 2025-02-11
Fixed
- Fix AAMotion MoveAbs by adding Begin().
- Fix MoveAbs() of AAMotionAPI; MoveAbs is called instead of MoveRel.
Changed
- Improve the AAMotionAPI and add the API of IGroupAxis, ICiSlave, ICiGroupAxis and IGroupAxis interfaces.
[7.2.0] - 2025-02-10
Changed
- Add the bit operation of Group on IO output (GroupDOutSetBit, GroupDOutClearBit, GroupDOutToggleBit).
[7.1.0] - 2025-02-07
Changed
- Increase the usage methods of the keywords, ProgHalt, ProgRun, AutoExec, ProgHaltAll and ProgReset.
- Reload connect and add the port number.
[7.0.5] - 2025-01-16
Fixed
- If connection fail due to FwInfoMismatch, retry connection with ForceAboutData set to true.
[7.0.4] - 2025-01-16
Changed
- Reduce AAMotionAPI overloading: use methods with optional parameters.
- Add the calling method of the AAMotionAPI.
[7.0.3] - 2025-01-09
Fixed
- Add a timeout mechanism to the initialization loop in the CommAPIInterface constructor to prevent infinite waiting. Set the maximum wait time to 3 seconds (30 ticks of 100ms).
[7.0.2] - 2025-01-09
Fixed
- Make sure the dictionary's life cycle is permanent.
[7.0.1] - 2025-01-08
Fixed
- Include controller parameters eKeyword.cs file.
[7.0.0] - 2024-12-30
⚠️ Major Release
- Introduction of AAMotionAPI, a functional programming style API interface for users more familiar with C++ style programming. This interface is incomplete.
- AAMotion infrastructural level has been cleaned and redesigned:
- There are no more status updates.
- There are no more settings updates.
- There is a new type called AAValueArray which functions exactly like an array.
- All keywords are now accessible as a property.
- Some keywords are more like functions, in which case they are exposed via the API as methods.
- Hooking onto errors is now done via EventOccurred action in MotionController class.
- Documentations have currently all been removed, due to a large effort to clean the code base and remove code bloat, unify and to standardize the code style. Since there is now a direct link to keywords of the controller, it may be better to centralize efforts in documentations first.
- All design considerations are in an effort to speed up the performance of the API usage, as well as to simplify the overall design. It is now extremely straightforward to add support for new keywords.
[6.0.4] - 2024-11-20
Fixed
- Fixed indexing bug for ForceCurr_SetForceCmdSource() and ForceCurr_SetCurrCmdSource() methods.
[6.0.3] - 2024-07-01
Fixed
- Setting and Querying the MotorOn keyword in PCSuite is 20 seconds, so here we do the same thing at the communicator interface.
[6.0.2] - 2024-06-16
Fixed
- .NET 4.0 YamlDotNet reference 4.2.2.
[6.0.1] - 2024-05-31
Fixed
- Set motion mode when MoveAbs is called.
[6.0.0] - 2024-05-08
Added
- Reference AAComm 7.0.0.
[5.13.0] - 2023-12-20
Added
- Added support for interpreting user program variable names via AdaptUserProgMessage.
[5.12.3] - 2023-11-30
Fixed
- AllStatInterpreter now supports 12 axes to compliment changes in 5.10.0.
- VectorMotion Pause Continue Fix, Added Enum for Vector Motion.
[5.12.2] - 2023-11-27
Changed
- AACommServer: Allow "retrying" socket init. upon process start.
[5.12.1] - 2023-11-22
Changed
- Added support for on-the-fly target/speed/accel change for MoveAbs().
[5.12.0] - 2023-10-31
Changed
- Add .net 6.0 and 7.0 targets. (nuget 5.11.0 is wrongly built and deprecated).
[5.11.0] - 2023-09-08
Fixed
- Fix the errcode of 'FailAboutData' when connecting.
[5.10.0] - 2023-08-04
Fixed
- AllStatUpdate to query for all 12 axes (instead of 8) such that the auxiliary ports will be updated too.
[5.9.0] - 2023-08-03
Fixed
- AllStatUpdate to include AGD155_EC_ID as well.
[5.8.0] - 2023-04-13
Fixed
- CRITICAL! AAComm RecUpload and "continuous" messages related bug.
[5.7.0] - 2023-04-13
Fixed
- Add YamlDotNet nuget dependency.
[5.6.10] - 2023-04-13
Changed
- Add vector motion methods.
[5.6.9] - 2023-01-12
Changed
- Added gcode analysis methods.
[5.6.8] - 2023-01-12
Changed
- Added methods to cigroup-axis class to do 6-axes linear motion and write doutport value related to axis to buffer.
[5.6.6] - 2023-01-12
Changed
- Move params and methods of event class to single axis class.
[5.6.5] - 2023-01-12
Changed
- Added params of gantry.
[5.6.4] - 2023-01-12
Changed
- Added params and methods of error map.
[5.6.3] - 2023-01-12
Changed
- Added params of userpwm.
[5.6.2] - 2023-01-12
Changed
- Added params and methods of group9(userparam).
[5.6.1] - 2023-01-11
Changed
- Added params and methods of position lock.
[5.6.0] - 2023-01-03
Added
- Added override for Home method to do homing based on sequence stored in memory.
[5.5.0] - 2022-08-17
Changed
- Communicator interface, AllStat Instantiation is cleaned to used feature_1 integer directly.
[5.5.0] - 2022-08-03
Added
- All the Move commands in Single axis motion is now updated to include speed, accel, decel parameters.
[5.4.13] - 2022-08-03
Changed
- MotionStat is added to the end of each Move Command. This should eliminate the timing issue that we experienced with the Taiwan customer.
[5.4.12] - 2022-08-03
Fixed
- Removing MotorCurrent, Fixing MotorCurr and CurrRef Statuses in ISingleAxis.
[5.4.11] - 2021-10-04
Changed
- Modify all Properties related to Force/Current/Position Mode Switch feature to be based on InternalProperies.
[5.4.10] - 2021-09-13
Changed
- If packet number in a SlowLogger UpLoad is zero, jump to next UpLoad without processing.
[5.4.9] - 2021-09-10
Changed
- Slow logger API StopSlowLogData: check the ID of the packet that was last logged, remove unwanted packet ID and contents from the main log list.
[5.4.8] - 2021-09-09
Added
- Slow logger API functions: StartSLowLogProcess and StopSlowLogData.
[5.4.7] - 2021-09-07
Fixed
- BuffTime and BuffPos command format must be array type.
[5.4.6] - 2021-09-06
Added
- Add properties and methods for Spline buffer motion in CiSingleAxis.
[5.4.5] - 2021-08-04
Fixed
- HomeToLimitThenIndex, Jog to index - 4, the first parameter is speed, argu 1.
[5.4.4] - 2021-06-30
Changed
- Turn on AGMotion.
- Open the CloseAACommServer API to the user.
[5.4.3] - 2021-06-08
Changed
- Hide AGMotion for interim version release.
[5.4.3] - 2021-05-24
Changed
- Do some Clearance works.
[5.4.3] - 2021-05-23
Added
- Add Force/Current/Position Mode Switch feature.
[5.4.2] - 2021-05-20
Added
- Add new AGMotion feature.
Changed
- Eliminate all compilation Warnings.
- AGMotion as an individual namespace.
[5.4.1] - 2021-05-12
Fixed
- Default set time out as 2 seconds when send out packet.
[5.4.0] - 2021-04-16
Added
- Add an option to set time out when send out packet, convenient for the user to debug for breakpoint.
[5.3.2] - 2021-03-24
Fixed
- Corrected tag for CNC Arc Motion.
[5.3.1] - 2021-03-04
Fixed
- Fix the bug on AIO Output Readback value incorrect.
[5.2.10] - 2021-02-17
Changed
- Move VBus, VLogic, PwrTemp, MotorTemp into IMotionController to be used for all controller class.
[5.2.9] - 2021-02-17
Changed
- Extend AGM400 Axes, IO, GroupAxis, PositionEventEx, full range from A to F.
- Extend AGM800 Axes, IO, GroupAxis, PositionEventEx, full range from A to L.
[5.2.8] - 2020-10-29
Changed
- AllStat for different controllers.
[5.2.7] - 2020-09-29
Changed
- Change 4.6.2 target frameworks to 4.8 (remove redundancy).
[5.2.6] - 2020-09-29
Fixed
- Updated calls to GetAllAxisStat within Group.cs.
[5.2.5] - 2020-09-19
Added
- And multiple target frameworks support: .net 4, 4.6, 4.6.2.
[5.1.5] - 2020-09-18
Changed
- Updated definitions and bug fixes for CNC Motion.
[5.1.4] - 2020-09-18
Changed
- Call GetAllAxisStat using a new parameter to decide if AllStatAxisData group 10 is included.
- Add DInPortHigh in AllStatAxisData group 10.
- Add BoardTemp in AllStatAxisData group 10.
- Clearance in IOMoudle.cs.
[5.1.3] - 2020-09-17
Changed
- Array Type parameter index start from 1 for AAMotion user.
- Rename and add elements in enum ControllerType.
Fixed
- Instantiate eEventSetting dictionary for read.
[5.1.2] - 2020-09-16
Added
- Add BiDirConfig feature.
[5.1.1] - 2020-09-15
Changed
- Rename Class AG100 to AGD155.
- Rename Class MAS02 to AGM800.
- Rename Class MAS01 to AGM400.
- Rename Class AG300 to AGC300.
- Rename Class AGD301 to AGC301.
Added
- Add DInLogHigh feature.
[5.1.0] - 2020-09-10
Added
- Add AGD301 class.
- Add TimerHandler in TimerEvent for internal debug from AAMotion user source code program.
Changed
- Change the commands of GetAllSettingsDIOAG300, GetAllSettingsAIOAG300 to dynamic contents, initialize them in the CommandList and rebuild later dynamically.
Fixed
- Dynamic Command fix the AGD301 system down after connection.
- Add eIOSetting.DInMode in UpdatePropertyListIOSettingArray.
- NumOfAOut for output array-type keywords.
[5.0.1] - 2020-09-09
Fixed
- FIX nuget package.
[5.0.0] - 2020-09-09
Changed
- Merge from 4.21.8.
- ParseDIOSettings, ParseAIOSettings don't trigger event to write into Array-Type IO variables.
[4.21.8] - 2020-09-04
Changed
- Replace PosEvent with PosEvent_A, PosEvent_B, PosEvent_C in AG300 class.
[4.21.7] - 2020-09-04
Fixed
- Take CiStatus from AllStat returning data structure.
[4.21.6] - 2020-08-31
Fixed
- Make AllStat "Continuous" message type.
[4.21.5] - 2020-08-27
Fixed
- Increase Axis to L.
[4.21.4] - 2020-08-27
Added
- Add CiStatus feature (Modified). Temporarily use .PropertyList[Axis].CiSlaveSettingsArray[eCiSlaveSetting.CiStatus][1] to replace .PropertyList[Axis].Statuses.CiStatus_1.
[4.21.3] - 2020-08-27
Added
- Add CiStatus feature.
- Temporarily disable CiSlaveRefreshRequired since AllStat[255] cannot work set.
- Increase the AxisRef to Axis K.
- Define GetAllCiAxisStat to replace GetAllAxisStatExtended1.
- Add CiIdentity, CiStatus into eCiSlaveSetting.
[4.21.2] - 2020-08-06
Added
- Optimize Array Properties Access.
- Add new command GetAllSettingDIOCiMaster, GetAllSettingAIOCiMaster.
[4.21.1] - 2020-07-24
Added
- Add MAS02 (AGM800) class, initial version.
[4.20.1] - 2020-06-30
Added
- Add AG100 class, initial version.
[4.19.2] - 2020-06-30
Changed
- Rename the property arrayStorage.
Fixed
- Refresh takes after the communication OK with the controller.
- VBus, VLogic, PwrTemp, MotorTemp updated valid only after the communication OK with the controller.
[4.19.1] - 2020-06-26
Changed
- Set a flag called "AxisInternalPropertyUpdatedFromController" to true after the connection is OK, which prevent intermittently read PropertyList after connection but InternalProperty have not been updated.
[4.19.0] - 2020-06-23
Changed
- Optimize the IO array properties update event handler.
- Property read must occur after connected, otherwise default as "unreasonable" values.
[4.18.0] - 2020-06-19
Added
- Replace the Set methods on "Array-Like" IO Features with direct "Array" format Read/Write Properties. These are DInMode, DOutMode, DOutSelect, AInFilt, AInOffSet, AInDB, AInGain, AInMode, AOutOffset, AOutShifts, AOutMode, AOutPort.
[4.17.0] - 2020-06-17
Added
- Add dynamic allocation array size of Digital and Analogue input/output Mode, Filt, Offset, DB, Gain.
[4.16.0] - 2020-06-12
Changed
- Upgraded AAComm DLL is upgraded from v3.0.0.2 to v3.0.0.18.
[4.15.0] - 2020-04-30
Added
- AOutPort has additional option to change to openloop mode.
Changed
- Clean up namespaces to use only one AAMotion namespace.
[4.13.1] - 2019-01-10
Fixed
- Put back bulk command method which was removed in v4.12.0.
[4.13.0] - 2019-01-07
Added
- MultiController.MoveAbs/MoveRel: Basic (Cross-controller) Multi-axis Synchronized PTP Method.
[4.12.1] - 2019-01-06
Changed
- Changed [UpdateInternalStatus()] to type boolean.
[4.12.0] - 2019-12-30
Added
- New method [UpdateInternalStatus()] on all MotionControllers which allow user to explicitly update internal status.
[4.11.0] - 2019-10-17
Added
- New Homing method, which can take in sequence file .hseq file which is created from PCSuite.
[4.10.2] - 2019-10-13
Fixed
- Edited summary of SendBulkCommandString method.
[4.10.1] - 2019-10-10
Fixed
- Differentiated between internal and external bulk messaging.
[4.10.0] - 2019-10-10
Added
- Support for bulk messaging.
[4.9.0] - 2019-08-26
Added
- Support PositionEvents from AG300 Controller.
[4.8.0] - 2019-08-26
Added
- LinearAbsolute: Generic CNC Method.
- MultiSegmentOn: Multi-segment Option.
[4.7.0] - 2019-08-26
Added
- Added InTargetTol, Added InTargetTime Keywords.
- Added Arc functions in Group Axis to include Additional Cycles Input Parameter.
Fixed
- Arc functions in Group axis was missing 1 parameter.
[4.6.0] - 2019-08-26
Changed
- AAComm is now taken from nuget package.
[4.5.0] - 2019-08-26
Added
- AllStat: Extension to be backward compatible and for supported MotorTemp.
[4.4.0] - 2019-08-26
Added
- Controller has new constructor overload to allow configurable internal update rate.
[4.3.0] - 2019-08-26
Changed
- Whenever motion method is called from either SingleAxis or GroupAxis, AllStat command is called immediately afterwards to update the statuses of the internal variables.