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.
[15.0.0] - 2026-10-30
⚠️ Breaking
- Server-side
SimProfileWriter.DefaultText()no longer invents a48V supply; use its explicit-voltage overload or resolve a known product. A raw unidentified simulator requires an explicit validvbus_vprofile. MessageData.DynamicPtris now along(wasint), so a 64-bit simulator's memory address fits. A consumer that reads the property into anintmust widen it or useIsDynamicPtr, and a binary built against the old type must recompile.IsDynamicPtris unchanged.- A message that a closing session drops before sending it, and a
SendReceivethat races the close, now replyPC Suite: ERR 4025(the connection closed before the message was sent) instead ofPC Suite: ERR 4020. 4020 now means only that the API had no connection when the call was made. A consumer that read 4020 as a teardown also matches 4025, or tests the newAACommEventArgs.IsSessionClosed. - The
AssemblyVersionis pinned to15.0.0.0for the whole 15.x line (package and file version keep the full number). .NET Framework consumers must recompile against 15.0.0 — a binary built against14.0.0.0does not bind, and no binding redirect bridges the identity change. .NET 8/10 consumers see no difference. IIdentityContainergainsPushEventQueueDepth. A consumer that implements the interface itself must add the member (CS0535); a consumer that only reads the container sees a new property.ICommServicegains bothDownloadArrayoverloads. A consumer that implements the interface itself must add them (CS0535); a consumer that only calls it sees new members.CommAPI.MessagesInBulkreports the bulk capacity, 50, on aChannelType.Simulationconnection, as it does on Ethernet. The firmware-simulator channel has always carried aMessageType.BulkMessage, but the property said 1 there, so every consumer that routes on it took the one-message-per-command path: recording-parameter download (AACommDataRecManager.SendRecordingParams) on a Simulation connection now crosses as one bulk instead of one message per parameter, and the AAMotion parameter batches batch there too. A consumer that readMessagesInBulk == 1as "not an Ethernet connection" now sees 50 on Simulation as well.AllStatInterpreter.ParseAllStatDatanow takes the AllStat reply as AAComm delivers it — the resolved text a caller reads fromAACommEventArgs.MsgReceived— and reads those values straight intoAllStatData. It used to expect the raw controller longs and re-run the IEEE 754 decode and the User Units factor itself, so the only reply a consumer can actually obtain was converted twice: on a controller with 64-bit AllStat every floating-point field came back 0. The structured data now carries exactly the values the reply text shows, already scaled. Callers pass the received reply unchanged — and must not ask for it withAACommMessage.SkipUserUnits, which suppresses the User Units factor but not the IEEE 754 decode, so it never yields raw wire values.
Added
CiFpgaFeatures.FromReadbackdecodes the remote FPGA capability snapshot that AGM800 and AGM800-EC firmware publish inCiIdentity[24..41]: the generation, the availability (CiFeatureAvailability) and the main and auxiliary index-output bits. A read that is not bracketed by a matching generation and availability, or whose header or check word is wrong, decodes asCiFpgaFeatures.Unknown- Two
ConnectResultcodes name why a managedSimulationconnection could not launch itsfirmware_sim:SimExeNotFound(51), whenConnectionData.SimExePathis empty or names no file on the AACommServer host, andSimStarterParMissing(52), when nostarter.paris next to the simulator executable, as with afirmware_simbuilt from source rather than packaged. Every other launch failure is stillSimLaunchFailed(49), with its cause in the AACommServer log. A client older than the server getsFailOpenCommHandlerfor the two new codes - Add immutable verified event-table definitions and an explicit captured-session Load/Arm/Complete/Abort/acknowledgement client. Preparation validates and reads back all three raw columns/settings; ambiguous writes retain process-local ownership, and recovery never crosses to a replacement connection without retirement proof.
EventTableStatusReaderprovides bounded live coherent status/error/capability observations over a captured connection; typed unsigned fields retain the firmware publication and error provenance. Cancellation abandons unsent reads without implying remote Abort.CommAPI.CaptureSession()binds a multi-request operation to its original connection, including disconnect/reconnect to the same endpoint. Its cancellation token and replies reject a closed session.- Verified-table preload capability uses Identity[62] bit0x200; Central-i master keyword caches distinguish its presence without requiring manual cache deletion.
SimParameterFile.Configureexplicitly delivers a complete selected-motoragito.parand physical profile to a disconnected simulator's memory folder without deleting its user program. Fresh managed launches initialize from the packaged complete starter; reconnect keeps the saved parameter file.- Managed firmware simulation writes an explicit product-specific supply into the motor profile and preserves caller voltage overrides. Unknown products require an explicit finite positive
vbus_v; invalid or unresolved supplies fail before launch. SimSupplyVoltage.TryGetexposes verified nominal simulator supplies by product identity. Unknown products have no implicit supply;IsValidchecks an explicit finite positive voltage.PassiveCollectionRequest.EncoderSelectionsopts individual mapping scopes into main or auxiliary absolute-encoder metadata. The passive collector reports table-approved configured type/bit widths, the controller's processed absolute value and raw main diagnostics, with device identity, qualified reported resolution, full health and query capability kept explicitly unavailable. Main and auxiliary scopes remain distinct, no controller settings or protocol queries are sent, and callers without selections retain their existing reads.AAComm.Commissioningis a portable result contract for discovery and commissioning passes:CommissioningReportrecords per-controller/remote-port/axis/encoder observations with value, units, origin (configured, declared, controller- or device-reported, measured, inferred), acquisition source, outcome and reason, capture time, and linked attempts and hash-referenced evidence, andCommissioningValidator.Validaterejects a report that contradicts itself (a configured value presented as device-reported, a failed or partial acquisition presented as a success, nonfinite or ambiguous values, dangling or cyclic derivations, a storedComtStatuspresented as fresh phasing, a failed restore recorded as completed). A clean validation says the report is consistent, not that any hardware state is true.PassiveMetadataCollector(AAComm.Commissioning) is the first producer of aCommissioningReport. Over anICommServiceit records the identity the sharedIIdentityContainerexposes (axis and motor-axis counts, capability flags, current-loop sample rate, recording limits), and for each caller-supplied port/axis/encoder mapping the configuredEncTypeand the storedComtStatus[1], using ordinary parameter reads only. Identity values are marked as cached at connect and stamped with the host time of collection; a collection is not an atomic snapshot. A disconnected service, an unloaded or lacking message table, a failed read, an unmapped or out-of-range axis, and the product, firmware, FPGA and serial-number fields the interface does not expose each stay an explicit outcome with a reason, never zero or false.Collectappends one attempt per call, so a repeat keeps earlier failures beside later successes. Nothing here changesaacomm.rig-manifest/1.AACommEventArgs.IsSessionClosedis true for a reply that ended because its session closed:ERR Sync: Session terminatedfor a message already sent,PC Suite: ERR 4025for one never sent. A consumer can leave those to the oneOnCommsErrorreport of a lost connection instead of reporting each request.- Error texts for the lock/event guards of Firmware-Main #1256, in English and Chinese: ERR 403 (a lock/event source change refused while Lock or Event is on, LockEventMode 1/2) and ERR 404 (Lock/Event refused, the offset is stale).
LockEventWriteOrder(AAComm.Services) holds the write order a restore needs under those guards (Order, whosePlan.AddedandPlan.Initsname the rows it adds), the dependency list (EncSinCosHWEn,EncDir,VEncSrc,VEncOn,AuxEncDir,VEncFact,VEncFactDen),ReadCurrentModesfor the controller's current LockEventMode where the rows carry none, andMode2Supported, read fromLockEventMode's declared maximum: firmware that declares it up to 2 is the firmware with the guards, and only it needs the order. - AGM800-EC enable (Firmware-Main #1258, merged as 5fa8dcc2a): every EtherCAT Enable Operation forces
LockEventMode2 (when it isn't 2 yet, the drive first turnsLockEn/EventOnoff, which also drops an armed touch probe), and, on an axis with an ancillary counter, waits forLockEventStat1 before the motor turns on (an axis without one skips the wait). If the mode isn't taken, or the offset still isn't learned after 8LockEventInitattempts, the axis faults with 0x603F = 0x7300.LockEventWriteOrder.EnableForcesMode2(productType, lockEventMode)(an AGM800-EC whose firmware declares LockEventMode up to 2) andEcEnableLockOffsetFault(0x7300) expose this. That forcing happens only at the next enable, soOrder(rows, isSupported, currentMode)decidesLockEventInitfrom the mode the axis is in when it re-arms (the file'sLockEventModerow, else the controller's current mode fromReadCurrentModes), on an AGM800-EC too. The ERR 403 text (EN/ZH) and its explanation now also cover the firmware's refusal to enter LockEventMode 1/2 on an offset that moved since arming. MessageDataValueExtensions.UserUnitsValueStr(MessageData, double, UserUnitsData)formats a Min, Max or Default value in user units: the float format when the parameter is floating-point, when user units scale it, or when the value has a fraction.MessageData.GetDescriptionand the allowed range in a controller error message use it, so their text is unchanged.AACommRemoteFpgaInfo.AllowAga155CurrentRatingMismatchis a default-false engineering option for AGA155 2A06 ↔ 2A10 FPGA comparisons (identity current codes 6 ↔ 10). All other target checks remain;AllowForcekeeps its separate unknown-identity behavior and cannot bypass known mismatches.- The package now ships
lib/netstandard2.0alongsidenet48,net8.0andnet10.0. A project targeting netstandard2.0 can referenceAgito.AACommdirectly instead of building AAComm from source. Nothing changes for existing consumers: NuGet still resolves the closest-matching target, so a net48, net8.0 or net10.0 project gets exactly the assembly it got before. CommAPI.DownloadArraydownloads a contiguous array from typeddoubleor exactlongvalues and returns anArrayDownloadResultwith the selected transport, the applied count and the count that reached the controller, first failed index, error code, and known/unknown outcome. On Ethernet it uses the Zynq 0x04 stream when both controller and AACommServer advertise support, otherwise it falls back to legacy bulk or individual writes. Timeouts and disconnects are never retried. A download refused before anything is sent ends asArrayDownloadStatus.Rejectedwith the refusal's error code and a known outcome, nothing applied: AACommServer refused it (another client holds exclusive access, for one), or an element's index or value is outside the keyword's table, whichFirstFailedIndexthen names. A parameter the table refuses outright still throwsArgumentException. An element's error, on either transport, reads as one sentence inErrorMessage: its index, the reply (ERR 14, orPC Suite: ERR 14when refused before sending), the error's description and, for a value out of range, the keyword's allowed range.ArrayDownloadResult.StreamErrorreports the controller's array-stream protocol error as theArrayStreamErrorenum (bad version, flags, width, count or index range; checksum mismatch; trailing data; unexpected reply), andErrorMessagenames it. A checksum mismatch is reported only after the controller has applied every element, so that result carries a fullAppliedCountfor a write whose integrity check failed — read the array back rather than trusting the count.CommAPI.SupportsArrayStreamDownloadreports whether the connected controller and AACommServer both advertise stream transport. The controller advertises it inIdentity[62]bit0x80; do not probe by starting a download, which older firmware answers through its legacy command parser.- The packet sniffer raises
SnifferService.OnRetentionFailure(path, count) after 10 consecutive failed deletes during retention or byte-budget enforcement — a capture file held by antivirus or another process no longer fails silently forever. Capture continues; the count resets on the first successful delete. - Push events. A controller push with
SOURCE = 2now reachesCommAPI.OnPushMessageReceivedas anAACommPushMsgPushEvents—Missed,FreeRows, andEvents, a list ofPushEventRecord(Type,Axis,TimeSec,TimeTick,Data1..Data5;PushEventRecord.NoAxismarks a controller-wide event).PushSources.PushEventsnames the source. A packet whose length does not match its record count, or whoseVERSION_DATAis not 1, arrives as anAACommPushMsgError, as a malformed printf push does. Push events are binary-IPC-only: the bundled AACommServer never forwards aSOURCE = 2push to a legacy string-protocol client, whose framing cannot carry a binary payload safely, so a consumer whoseCommAPI.ServerCompatisLegacyAsciidoes not receive them. Printf pushes still reach both client kinds. - The push-events capability and queue depth read from the controller Identity.
ControllerIdentityFeature_02.PushEvents_Supportedis Identity[62] bit0x40(the firmware'sIDENTITY_NEW_FEATURES_2_PUSH_EVENTS), reachable throughIIdentityContainer.Features_02;IIdentityContainer.PushEventQueueDepthis Identity[75], the depth of the controller's push-event queue — 64 on an AGM800, 0 on a firmware without push events, whose Identity array ends before index 75. Implementors ofIIdentityContainermust add thePushEventQueueDepthmember. - The native C-ABI SDK gains a byte-safe, length-carrying push callback beside the
const char*one:aacomm_set_push_bytes_callback/aacomm_push_bytes_cbdeliver the push message's raw DATA payload as(const uint8_t* data, size_t len, void* user_data)— exactly the bytesAACommPushMsgBase.BinaryDataholds, the binary IPC dialect's trailing>\rterminator included — so aSOURCE = 2push-events packet — mostly zero bytes — reaches a C/C++ consumer intact, andaacommpyexposes it asClient.on_push_bytes. The existingaacomm_push_cbis unchanged; the addition is additive and the ABI generation stays1. - A new product type, AGM800-EC:
ProductTypes.AGM800_EC_ID(16) andProductTypes.AGM800_EC("AGM800-EC"). It is the first product in both families:ProductTypes.IsAGM800andProductTypes.IsEtherCATboth return true for it, and so doConnectionData.IsAGM800andConnectionData.IsECon a connection to one.IdToName/NameToIdresolve it. Its firmware image is its own, not the AGM800 one:AACommFwInfo.ValidateFwFilesrefuses an AGM400 / AGM800 / Pulse-Sync.binon an AGM800-EC connection and an AGM800-EC.binon those — by the image's identity markers (with or without a.ver), and by the.verType:line, which must start withAGM800-ECon an AGM800-EC connection and must not on the others — andAACommFwInfo.ProductTypeStrreports"AGM800-EC"for its image.ControllerIdentityContainer.DefaultSamplingRatereturns 16000 for it, the EtherCAT current-loop rate.AACommDownloadFWflashes an AGM800-EC's EtherCAT SII EEPROM when you passAACommFwInfo.Targets.EC_ESI: the SII.bingoes down as Intel-HEX lines afterDownloadESI, over Ethernet (or the firmware-simChannelType.Simulationchannel), while the controller runs its firmware — it is refused in boot. The target is yours to pass: an AGM800-EC SII image and its firmware image are both.binfiles, and AAComm does not recognize an SII image by its content, so with the defaultLegacytarget an SII image is flashed as firmware.EC_ESIrefuses a.hex, a.binlarger than the 16 KiB an ESI image can address (the AGM800-H1G's SII is 2 KB; a firmware image is megabytes), and a.bincarrying firmware identity markers, and on an AGM400, AGM800 or Pulse-Sync connection it is refused before anything is sent. - Controller fault 1082 (
CON_FLT_EXTERNAL_AMPLIFIER_FAULT, ADP05 / AGL105 with an external amplifier) has a description: "External amplifier fault (e.g. Varedan undervoltage)". - Remote FPGA download supports 16K (10M16) devices:
FlashSizes.Model16K(3), a.verwithFPGA Size: 16Kis accepted, and the image is written to the 10M16 CFM0 base (0x12800), including the upper flash-address bits (register 0x30B). First used by ADP05 (AGL105). ConnectionData.SimMemoryFolder— opt-in absolute path of a folder a managedSimulationconnection keeps its memory in. AACommServer creates it when missing, startsfirmware_simwith it as its working directory, and never deletes it, so what the simulation saved to flash is still present on the next connect. The simulation writes its saved files there: a 5.3.0 AGM800 simulator writes its parameters toagito.parand its user program toagito.prg. Left empty (the default), every connect still gets a fresh scratch folder, saved files included, that is removed on disconnect. A relative path is refused withConnectResult.SimLaunchFailed.ControllerIdentityVersions.BuildCiRemoteVersionString(ciIdentity4, ciIdentity15To19)returns a Ci remote unit's FPGA version text fromCiIdentity[4]andCiIdentity[15-19]. A unit whoseCiIdentity[4]is 4444 gets the five words formatted byBuildVersionString(so owner 0 with sub-version 2 reads5.5.0-rc2, and a null or short word list gives an empty string); any other unit gets the oldA.BBBtext, always with a dot, whatever the machine's culture.aacomm.clireads a remote unit's FPGA version through it.ControllerVersionWordis a public, comparable firmware or FPGA version word (Major.Minor.Patchplus owner and sub-version), built withFromWordsfrom the five identity words or withTryParsefromX.Y.Z,X.Y.Z-rcN,X.Y.Z.OorX.Y.Z.O-S. A release candidate orders below its final and a development build above it.ControllerIdentityVersions.FirmwareVersionWordreturns the connected controller's firmware as one (null for the old firmware format and in boot mode).
Changed
- When the packet sniffer is unavailable because Npcap is not installed, the AACommServer window and the AACommServerNative console now link to the Npcap download page, https://npcap.com/#download. Npcap is not redistributed with AAComm; install it yourself to use the sniffer.
- A
ChannelType.Simulationconnection starts itsfirmware_simchild withAGITO_DRAW_GCCset to%LOCALAPPDATA%\PCSuite\HostGcc\bin\gcc.exewhen that file exists, so Draw runs on the simulation with the host compiler PCSuite downloads, whichever client started AACommServer. A value already set in AACommServer's environment is passed through unchanged. - Simulation connections load their packaged or saved configuration without sending firmware requests during open. Saved motor types, including zero, and gains survive reconnect. Use Configure Motor or
SimParameterFile.Configurefor explicit reconfiguration. - A firmware or FPGA download (
AACommDownloadFW,AACommDownloadFPGA,AACommDownloadFPGARemote,AACommDownloadFPGAEtherCat) now takes the channel's exclusive comms just before its single-client check and holds it until the download ends, and takes it again after each reconnect it makes. A client that callsCommAPI.Connecton the same channel during the download is refused withConnectResult.ChannelInExclusiveCommsMode, and every message from another client (one connected withConnectSilentincluded) is answeredCHANNEL_IS_EXCLUSIVE, instead of interleaving with the flash. On an AACommServer older than 7.0.1 the lock is not taken and the download runs unprotected. - A
SendReceivewhose timeout closes the connection now also raisesCommAPI.OnCommsError(ERR Sync: Timeout (<message>)), on the calling thread before the call returns, so a consumer that follows the connection through the event learns of the disconnect. The call still returnsERR Sync: Timeout. - A
ChannelType.Simulationconnection starts itsfirmware_simchild withAGITO_CI_REAL_AMP=1, so an AGM800 or AGM800-EC simulation drives its simulated remote amplifier: the axis phases, the encoder moves and the phase currents are live. The drive simulations ignore the variable. A value already set in AACommServer's environment is passed through unchanged, soAGITO_CI_REAL_AMP=0keeps the stock simulated-amplifier path, where the axis arms but never moves. - The embedded fallback message table, the catalogue in force before a controller's About is read, carries seven more rows (
Print,ProgResetAll,PosKi,EmulRat,CurrAInTh,VelUnitFct,Speed), lifted from a real controller capture. In that window those mnemonics now resolve instead of reportingMnemonicNotFound,MessagesContainer.AllMnemonicscounts 21 rows, and a.parupload started there lists the four writable ones. - FPGA download no longer refuses every Ethernet address other than the factory default
172.1.1.101. The gate is now the same ARP guard the FW path runs: a non-default address is refused only while another device answers on the default one. A legacy (AGD200-class) FPGA download still reconnects at the default address mid-flow, so the default must be free on the subnet — which is exactly what the guard checks. - A cached About table that no longer matches the controller is read again from the controller on
Connectinstead of failing withCorruptAboutFile, which now means the controller's own About data is inconsistent. ProductTypes.NameToIdresolves either half of a paired product's name:AGD301andAGC301returnAGD301_AGC301_ID(11), andAGD200andAGC300returnAGD200_AGC300_ID(5), where they returned-1.AGD301andAGD200are how the firmware catalogue and PCSuite'sSimulations\<product>\folders name those products, so those names now resolve like every other product's.IdToNameis unchanged and still reportsAGD301orAGC301/AGD200orAGC300.- AACommServer extended Ethernet diagnostics summarize array-stream headers and cap raw packet previews at 64 bytes. Disabled extended logging no longer formats complete firmware or array payloads only to discard the result.
- The message that refuses a firmware download over CAN (
AACommDownloadFW) no longer ties Ethernet to the AGM800: it readsFirmware download is not supported over CAN. Download firmware over Ethernet or RS-232 (serial). - An exception thrown by an
OnProcessEndedhandler ofAACommDownloadFPGAEtherCatat the end of a download now propagates to the caller ofStartDownloading, which runs the whole download on the calling thread; the handler is called once. ControllerIdentityVersions.BuildVersionString, and through itFirmwareVersionStr,FPGAVersionStrandCMFirmwareVersionStr, shows a release candidate (owner 0, sub-version N greater than 0) asX.Y.Z-rcNinstead ofX.Y.Z.AACommFwInfo.AACommVersionFromStringfollows: a.verline5.5.0.0-1resolves to5.5.0-rc1. A user program compiled on a release candidate carries that form in itsFirmwareVersion:line, so it loads on that controller and not on its final; one compiled earlier carriesX.Y.Zand must be recompiled. The About cache folder of a release candidate is named by the new form.FirmwareSystemVersion,IsFPGAVersionEqualOrGreaterand the firmware-to-FPGA match check are unchanged.
Fixed
- A late dispatcher or synchronous timeout from a closed session cannot send commands to, or disconnect, a replacement connection at the same endpoint.
- RS-232 decimal text replies, including
100000.0, retain their exact text through ASCII and binary IPC instead of becoming incorrect integers when their lengths match binary reply frames. - Remote FPGA download verifies boot before erase/data writes and primary before success, including when the final reboot trigger returns
ERR 151after taking effect. The original error remains in status output; failed preparation, reconnect/readback, wrong images and ordinary flash errors still terminate without repeating the transfer or bypassing identity checks. - Recording uploads reject nonempty payloads with no resolved vectors or an incomplete interleaved sample frame, reporting the observed scalar/vector counts instead of returning uneven vectors. Header-only inputs and complete stopped captures whose actual length differs from the requested length remain accepted; asynchronous and sequential uploads report the reason through
OnErrorwithout a successful upload callback. - The ERR 412 description writes the PDSubType input type as
C0/C1(zero), as the firmware names it, instead ofCO/C1. - Connecting to a 64-bit Linux firmware simulator no longer fails with
ConnectResult.CorruptAboutFileabout half the time. The About table'sDynamicPtris a memory address, and when it landed aboveInt32.MaxValueits 32-bit parse threw, so the table load failed. - A managed
Simulationconnection no longer shares its memory with every other connection that runs the samefirmware_sim. AACommServer starts the simulation withAGITO_FLASH_FILEpointing into its working directory, theSimMemoryFolderwhen one is given, overriding a value in the server's own environment, so the parameters (agito.par) and user program (agito.prg) that a Save to Flash writes stay with that connection; without it the simulation kept them beside its own executable, so a Save to Flash on one connection appeared on the next connection to that simulation, whichever client opened it. ControllerErrorInterpreter.GetErrorCodeFromErrorStringreads the code from the multi-line textGetFormattedErrorTextbuilds ("ERR 21\nSent: ...\nError: ..."), and from any error text that continues after the code; it returned the unresolved code 4005 for those.- An exception thrown by an
AACommMessage.OnReplyReceivedcallback ends the process as an unhandled exception, as it did up to 12.x, after AAComm writes it, with its full stack trace, to a debug log snapshot under%LocalAppData%\PCSuite\AAComm_DebugLogs. From 13.0.0, which moved the callback onto the send dispatcher, it had been discarded without a trace, so a failing reply handler looked like a hang. A consumer whose handler can fail must catch inside it. - On .NET 8 and .NET 10, a fast stream of received data no longer overflows the stack and ends the process: the AAComm client, AACommServerNative and an in-process
AACommServerModelre-armed each socket receive from inside its own callback. - A
CommAPI.SendReceivethat races aDisconnectreturns thePC Suite: ERR 4020reply, like anySendReceiveon a disconnected channel. It used to throwObjectDisposedExceptionwhen the disconnect had already disposed the session it was about to use. - A firmware download over Ethernet to a drive ends at once, with
Failed sending Password. Reply: ERR <code>, when the controller refuses the first password. It used to reconnect for a boot that never started and report the refusal late (or as a timeout). - A firmware download over Ethernet to a legacy drive stops waiting for the boot on the factory address
172.1.1.101once 100 s have elapsed, checked before each probe, and then falls back to the address it started on. The wait was a fixed number of probes, so on a host where each took about 3 s the fallback ran late or never inside a caller's time budget (aacomm.cli flashreporteddid not complete within 300 s). The status line during that wait readsReconnecting TCP/IP to <ip>... (Attempt <n>)with no total. - An EtherCAT drive's ESI download (
EC_ESI) refuses an image larger than 16 KiB before sending it: the image is written as Intel-HEX records from address0xC000, so 16 KiB is all that can be addressed. - A user-program download started from inside
AACommDownloadUP.OnProcessEndedruns once, not twice.AACommExclusiveCommsManagerkept one completion callback per instance, so aStartissued from that handler while the previous download'sEndwas still in flight completed whichever callback was stored last, and the ending download's Stop reply started the next one a second time. EachStartandEndnow completes its own call's callback. Chained downloads — an N-iteration loop driven fromOnProcessEnded— are the affected case; the synchronous form (a nullonDone) and the public surface are unchanged. - The About cache is kept per firmware build. Controllers that share a product and firmware version but differ in build or current rating keep separate cached About tables (
ControllerMessagesList.<peakCL>.<count>.<fwinfo>.xmlin the product/version folder), so connecting to one no longer discards the other's, and a drive'sPeakCL/ContCLassignments are no longer range-checked against another rating's limits. The firstConnectto each controller after upgrading reads its About data once; the files an older AAComm wrote in that folder are left in place for it. A product/version folder keeps the tables of the 8 builds most recently read from a controller; reading another deletes the oldest, and a controller of that build reads its About data again on its nextConnect. - Applications that connect at the same time to controllers of the same firmware build no longer corrupt or delete each other's cached About table. A freshly read table is written beside the cached one and moved into place only once it passes its keyword-count check, so a
Connectnever loads a half-written table, a table that fails the check (CorruptAboutFile) is discarded instead of replacing the cached one, and an About reply that fails to parse (FailAboutData) no longer leaves a partial table behind.XmlHandleropens its file withFileShare.Delete, so replacing a file another process is reading no longer fails. - An About table whose
MinValue,MaxValueorDefaultValueis not a finite number is refused on every target. The-nana firmware can send for a floating-point parameter's limits loaded on .NET 8 and .NET 10 — the connect succeeded with NaN limits, and the cached About file kept them for every later connect over any transport — while .NET Framework already refused it;NaNandInfinityloaded on all three. Such a table now fails to load on every target,ConnectreportsCorruptAboutFile, and the load error names the parameter and the field. - A firmware
.hexwritten on Linux or macOS (LF record endings, ashex2000emits there), or one whose records mix LF and CRLF endings, is now parsed and validated like one written on Windows; the identity block was only found behind CRLF, so such a file failed the pre-flight check, with a substring error or as a file carrying no identity data. - A parameter file uploaded from a controller downloads back without range rejections on float parameters. A float parameter's reply carried the value at double precision —
ATorqueConstread9.9999997171806854E-10, the 17-digit form of the1e-9the controller actually holds — which lands fractionally below the keyword's declared minimum, so every such line came backAssignedValueOutOfRange(error 14) on download. An unscaled float parameter now reports in its own width (1E-09), which re-parses to the identical stored value. Reply text changes for every float parameter —.parfiles, Terminal output, anything reading a reply — and carries at most 9 significant digits instead of up to 17. Double parameters are unaffected, and a float value scaled by a User Units factor still reports the scaled value at double precision, where the extra digits carry the division back to the stored value. - A float32 parameter assignment whose value sits on the parameter's own declared bound is accepted instead of being refused as out of range. The bounds were compared at double precision while the value that goes on the wire is a float32, so any 17-digit rendering of a value on a bound — the form every
.parfile written before the reply-width fix above carries — parsed as outside them. Both sides now compare at float32 width, so a file already written that way downloads without hand-editing. - A recording-parameter download (
AACommDataRecManager.SendRecordingParams, and the sync manager over it) whose bulk the controller answers with an error now reports that error throughOnErrorinstead of never completing. A bulk stops at the failing command, so itsERR nnnis the last segment of the reply and has no terminator after it; the reply parser sliced past the end of the text and threw inside the reply callback, so neitherOnSendRecordingParamsnorOnErrorfired andAACommDataRecManagerSync.SendRecordingParamswaited forever. - A controller-link failure no longer takes the bundled AACommServer down with it. A hardware fault ends the channel, and the server closed that channel's client IPC socket as part of the eviction, then read its own teardown as the client having died — so with a single client attached (the usual case) the server exited about five seconds later, while the client was still running. An application that reported the FATAL to its user and waited for them to reconnect found the server gone, and the reconnect failed with
AACommServerNotRunninguntil it started a new one. The socket now survives the eviction, and aClosefrom a fatal-evicted client is read as the goodbye it is. The self-shutdown still fires when a last client dies without saying goodbye. The behavior lives in the bundled server, so client and sidecar move together: a client against an olderAACommServer.exestill sees the old exit. ParamAboutreports the bounds of aSCALINGparameter (InUserUnits4 —StuckTime,InTargetTime,RptWait,MaxVBusTime,DualStuckTimeand their siblings) in the unit the parameter reads in: divided once by the controller's scaling factor and truncated the way the parameter table's own bounds are, so the reply agrees withMessageData.MinValue/MaxValue/DefaultValue. It multiplied instead, so the min / max / default it returned for these were out by the square of the factor (a 250 msStuckTimedefault came back as67108.8671875). Live values were and are unaffected: the controller converts those itself.- A printf push (
SOURCE = 1) whoseVERSION_DATAis not 1 now reachesCommAPI.OnPushMessageReceivedas anAACommPushMsgErrorcarrying aNotSupportedException, as a push-events packet at an unknownVERSION_DATAalready does. It used to be parsed with the version-1 layout regardless, so a packet from a firmware with a newer printf format could arrive as a validAACommPushMsgUPcarrying wrong messages, or as an error only when its bytes happened not to fit. - The bundled AACommServer frames the controller push socket per packet. TCP may deliver two push packets in one read, or one packet across two, and each read was forwarded as one push: a printf packet followed by a push-events packet in the same read lost the events, the reverse order lost both, and a packet split across two reads reached
CommAPI.OnPushMessageReceivedas anAACommPushMsgError, or as a truncated push followed by one. Every packet now arrives whole and on its own, and the string-client filter forSOURCE = 2applies per packet. A push the server does not frame — anything but printf and push events atVERSION_DATA1 — is still forwarded verbatim; a packet whose terminator is not where its header says is dropped by the server instead of forwarded, and after a drop each read that does not start with a printf or push-events envelope is forwarded verbatim, framing resuming at the first read that does. The framing lives in the bundled server, so client and sidecar move together. - A controller-link FATAL tears down only the session it was raised for. The in-flight
SendReceiveis woken before the teardown runs, so a caller that treats that failure as expected and reconnects immediately could have its new session torn down by the old FATAL and seePC Suite: ERR 4020on its next message — the EtherCAT firmware download hit this at the reconnect right after the password that drops the drive into BOOT, where it reconnects at the drive's own address within ~100 ms, and then succeeded on the retry because the drive was already in BOOT.OnCommsErroris not raised for a FATAL whose session is already gone. - A
.verversion line written in the controller's own development form,5.4.4.12-1 (ALPHA), is accepted byAACommFwInfo.AACommVersionFromStringand by the FPGA-vs-FW restraint check (which compares its dotted head,5.4.4.12), instead of throwing on the suffix.5.4.4and5.4.4.12-1resolve as before. - An assignment to a parameter the firmware holds as an integer now crosses as an integer on the string transports too, rounded to-even in controller counts. Dividing a display value by the User Units factor rarely lands exactly on a count —
244.39at0.01 mm/countis24438.999999999996— and only the CCC frame builder rounded it, so the same command reached an RS-232 or Simulator controller one count short of what it carried over Ethernet. The transmit seam now rounds aParamType0 / 3 parameter in counts before rendering, matchingCanMessageParser, and does so at factor 1 as well, so a fractional value written to an integral parameter no longer reaches a string-transport controller as a decimal it cannot hold. Float and double parameters keep their fraction, and a command the seam does not rewrite still crosses byte-for-byte. - User Units scaling is now applied on the way out on every transport. A write only ever reached the scaler when it was built into a CAN frame, so the same assignment left in engineering units over RS-232, over a Simulator connection and as a single-command
MessageType.EthernetASCIImessage while the reply came back scaled — a read-modify-write on those paths divided the value by the factor on each cycle. The division now happens above the transport branch, so an assignment reaches the controller in counts whichever channel and whichever IPC dialect carries it, andAACommMessage.SkipUserUnitssuppresses it there just as it suppresses the receive factor. Firmware, user-program and CNC/SpecialBulk payloads stay raw in both directions, as before; bulk messages keep scaling per sub-message; anEthernetASCIIbody under one of the ASCII-bulk keys (L,I) carries a list this layer does not parse and stays raw as it always has. Existing callers that sent RS-232, Simulator or EthernetASCII assignments in controller counts while User Units were enabled must stop pre-dividing, or setSkipUserUnits. - The API-only User Units config gate now fires on every transport too: a raw assignment to
*UnitFct/*UnitUnt[]is rejected with error4023over RS-232, over a Simulator connection and on a single-commandEthernetASCIImessage, instead of changing the controller's factor silently and leaving the SDK's scaling cache stale. The rejection now also covers a spaced form (APosUnitFct =0.5— the controller reads it as the same write) and fires before the value is validated, so a malformed or out-of-range config write reports4023rather than a value error. TheAACommMessage.AllowUserUnitsConfigbypass is unchanged. CommAPI.OnUserUnitsEnabledChangedand the ack-authoritative enabled cache now also follow aUserUnitsEnwrite sent as aMessageType.EthernetASCIImessage, and one written with spaces around the assignment (AUserUnitsEn =1); both were missed, so toggling units that way leftCommAPI.GetUserUnitsEnabledreporting the old state and no event raised.- Recording-parameter download no longer risks scaling the trigger value twice.
TriggerContainerde-scalesRecTrigVal/RecTrigValMaxwhen it is built, andAACommDataRecManagernow sends the parameter batch with the per-message User Units opt-out, so the value crosses exactly as the trigger computed it. - User Units scaling no longer keeps the pre-reboot factors after a controller reset. The scaling configuration is read from the controller at
Connect; a reset the session survives — syncSendReceiveor asyncSendofMessageType.ResetRequest— now re-reads it, so a controller that comes back on a different*UnitFctorUserUnitsEnis scaled by its new values instead of the stale ones. A reset that returns an error leaves the cached configuration untouched. The re-read is synchronous: it completes before a syncSendReceivereturns, and for an async reset it runs after the recovery wait and before the reply callback. - Numbers now cross the AAComm boundary in the invariant culture in both directions. On a comma-decimal locale (German, French, most of continental Europe) the SDK formatted values in the process culture while parsing them back invariantly, so a User Units factor or a recording-trigger value of
0.01went to the controller as0,01and was read there as1— a hundredfold-wrong value accepted with no error — andParamAboutbounds in the controller's own About data threwFormatException, failing the connect. Reply text changes shape on those locales: a scaled reply, an AllStat value and aParamAboutbound now read0.01, not0,01. The text is also exact and stable — it parses back to the very value that produced it, and is byte-identical on .NET Framework 4.8, .NET 8 and .NET 10, where the previous rendering was 15-digit lossy on .NET Framework only. Integral wire tokens go invariant too: on a locale whose negative sign is U+2212 (Swedish, Finnish, Norwegian, Estonian, Lithuanian) a negative controller value used to lose its sign on .NET 8/10 and arrive positive.FirmwareVersionStrandFPGAVersionStrfollow, so a legacy controller now reports4.123rather than4,123— a.cupuser-program file written on a comma-decimal machine before this change still downloads, its stored version being normalised before the compatibility check. A consumer parsing an AAComm reply must parse it withCultureInfo.InvariantCulture. - Controller-FPGA download (
.dat,AACommDownloadFPGA) to an EtherCAT drive over Ethernet no longer strands mid-flash. After the password handshake the process unconditionally reconnected at the factory default172.1.1.101— but an EtherCAT drive keeps its own address in FPGA-download mode, so nothing answered there and the download failed withFailed reconnecting in boot modewhile the drive waited in download mode. The reconnect now targets the address the product actually comes up on (EtherCAT: its own; legacy: the factory default, as before), with the other address as fallback. - Controller-FPGA download (
.dat,AACommDownloadFPGA) no longer raisesOnProgressafter theOnProcessEndedthat ends its download. The progress it reports while the controller finalizes the flash stopped only once it found the instance idle, so a tick could arrive afterOnProcessEnded, and a download started on the same instance fromOnProcessEndedkept receiving that ramp mixed into its own progress. Each download now stops its own finalize progress, and waits for anOnProgresscall still running to return, before it raisesOnProcessEnded. - Firmware download to a legacy or EtherCAT controller over Ethernet no longer fails intermittently with
Failed reconnecting Ethernet ... AACommServerNotRunning(14.0.0 regression). The controller drops its link on the way into BOOT, the bundled AACommServer fatal-evicts the downloader — its only client — and, since 14.0.0, that client's socket closing ended the server before the downloader could reconnect to the boot image. The server now keeps a five-second grace window after its last client dies (sized by rig experiment — the reconnect goes straight to the boot image's known address and returns well inside it): a client that opens a channel within the window keeps the server up; otherwise it exits as before. AGM800 (.bin) and RS-232 downloads never reconnect and were unaffected. The window lives in the bundled server, so client and sidecar must move together: a new client against a 14.0.0AACommServer.exestill loses the race. - Reported parameter bounds now match the units the value itself comes back in.
ParamAboutscaled its min/max/default by the factor ofParamAbout— never by the factor of the parameter being asked about — so bounds came back in counts while the value came back in units; they now carry the queried parameter's factor. The same bounds (the parameter-info description and the "Allowed range" text on an out-of-range error) also ignored the per-axis User Units enable state and were reported in units on an axis that had them switched off; they now report counts until the axis enables units, and likewise while the consumer-wideCommAPI.SkipUserUnitsis set. AACommMessage.SkipUserUnitsnow really coversAAllStatreplies, as documented. The AllStat reply path re-read the API-wideCommAPI.SkipUserUnitsinstead, so a single raw-units query was silently scaled anyway. Related: on a controller without 64-bit AllStat support the raw AllStat reply was handed back completely unscaled, so an axis in user units read in mm throughAPosbut in counts throughAAllStat; those replies are now scaled like every other one. A reply that is not a numeric AllStat vector of the length the axes/groups masks describe — anERR nnreply above all — is passed through untouched, so a controller error still reads as an error.AACommDownloadFW: firmware download to a legacy (non-AGM800, non-EtherCAT) controller over Ethernet now completes from a non-default address. The legacy boot image comes up on the factory default172.1.1.101regardless of the application's address, and the download used to fail at "Failed reconnecting Ethernet" while retrying the original one; after the flash erase it now reconnects at the default address first, and at the original address if a boot image kept it (the existing guard that refuses the download while another device owns the default address is unchanged). After the download the controller reboots on its own address.AllStatData.IsParsedOKnow reports a failed AllStat parse instead of hiding it. A field that is not a number — a controllerERR nnreply above all — was read with a discardedTryParseand stored as 0 whileIsParsedOKstayed true, so a bad reply looked like a wall of valid zero readings; it now setsIsParsedOKto false. Fields are read in the invariant culture, matching the way the reply is written.AACommDownloadUP.DownloadUserProgramreports a rejected request throughOnProcessEndedinstead of never completing. A call that is refused before the download starts — a disconnected ornullCommAPI, or a user-program path that does not exist — raised its refusal on a background task nothing observed, because the exclusive-comms teardown dereferenced the controller handle the refused call had never taken; the callback that reports the outcome never fired. A consumer waiting on it saw no error text and waited out its own timeout, or waited indefinitely where it had none. The refusal now arrives as the callback's error description (API not connected,Invalid user program file), and runs no exclusive-comms teardown at all: a call that never took a controller handle no longer reaches for the one a previous call on the same instance left behind.AACommDownloadFPGARemote.StartDownloadingno longer disconnects aCommAPIthe refused call did not connect. Every refusal — anullargument, an out-of-range port, an API that is already connected (AAComm must be disconnected!), a non-AGM800 product, a channel other than Ethernet — ran the same teardown as a failed download, so on an instance that had already flashed once it disconnected the API of the previous call, and a caller that handed in a live connection to be told to disconnect it first had it dropped for them. A refusal now only reports throughOnProcessEnded. A failure after this call's ownConnectsucceeded still disconnects the connection it opened, so the documented end state (API disconnected, success or error) is unchanged for a call that got that far.AACommDownloadFPGA,AACommDownloadFPGAEtherCatandAACommDownloadFWdisconnect the connection they opened when a check fails after their own connect succeeded. A controller whose firmware does not implement FPGA download or whose boot version is too old, an unreadable.dat/.ver/.hexfile, an FPGA file for another product, an FW/FPGA version mismatch, a firmware file whose target does not match, another AAComm client on the controller — each reported throughOnProcessEndedand left theCommAPIconnected, so a caller that did not disconnect it itself was refused on its next attempt (AAComm must be disconnected!,AAComm is connected. Disconnect, and try again.). Each engine's documented end state (API disconnected, success or error) now holds for those failures too. A refusal raised before the connect still touches noCommAPI: that connection is the caller's own.AACommDownloadFW.StartDownloadingSaferefuses a call made while a download is already running on the instance (FW Download is already in progress!) before the pre-flight checks run. The currents verification (verifyCurrentRatings: true) connects to the controller, readsAIdentity[10]andAIdentity[9]and disconnects — and it ran first, on the API the running download owns and reconnects several times, so the refused call took the controller away mid-flash and reported a current-rating mismatch or a connection failure instead of the refusal.AACommDownloadFW.StartDownloadingrefuses anullapiorconnDatathroughOnProcessEnded(Invalid input argument: CommAPI,Invalid input argument: ConnectionData), the textsStartDownloadingSafereports for the same arguments, without theError:prefix its catch-all adds. AnullconnDatathrew aNullReferenceExceptionout of the call, and anullapiwas accepted: the instance marked itself busy, started its worker and reported the resultingNullReferenceExceptionmessage as the download's outcome.- An
AACommDownloadUP.DownloadUserProgramcall refused because another download is still running on the same instance (Download is already in process!) no longer disturbs that download. The refusal ended the running download's exclusive-communication session and marked the instance idle while the transfer continued, and a refusal that arrived while the running download was still waiting for exclusive access stopped it from ever starting, so its outcome was never reported. The refusal now only reports throughOnProcessEnded. That callback is shared by every call on the instance, so the running download's handler also receives the refusal text, in no guaranteed order relative to its own result.AACommDownloadFPGARemote.StartDownloadingrefuses a second call the same way (Remote FPGA Download is already in progress!), where it disconnected theCommAPIof the flash in progress. - An
AACommDataRecManagercall refused because an operation is already running on the same instance (Async operation in progress!) no longer marks that instance idle. The refusal cleared the running operation's in-progress flag, so the next call was accepted while the first was still driving the controller: the two then shared theCommAPIthe manager holds and the completion callback it fires, and the first operation's completion invoked the later caller's. Every entry point refused this way —SendRecordingParams,StartRecording,ForceTrigger,StopRecording,WaitForRecToEnd,UploadRecData,UploadRecDataNonblocking. A refusedStartRecording,ForceTriggerorStopRecordingalso replaced the parameter resolver the running operation sends its commands through, because that resolver was built before the call was refused; it is now built only once the call has been accepted. The same ordering means anullapipassed to those three threw aNullReferenceExceptionout of the call, since the resolver was built from it before the argument was checked; all three now reportInvalid input argument: API is nullthroughOnError, as their documentation already promised. The refusal now only reports throughOnError. That callback is shared by every call on the instance, so the running operation's handler also receives the refusal text, in no guaranteed order relative to its own result. AACommDataRecManager.CancelUploadends the operation that holds the instance instead of releasing the instance and abandoning that operation. It cleared the in-progress flag whatever was running, while cancelling only a sequentialUploadRecDataNonblockingupload: aWaitForRecToEndkept pollingRecStatand a blockingUploadRecDatastayed in flight, their caller told nothing, and the next call — accepted because the flag was clear — took over the same instance'sCommAPI, parameter resolver and completion callback, so the abandoned operation's reply invoked the later caller's. A cancel now reportsOperation cancelledthroughOnError, which is how the running operation's caller learns it ended; it cancels a sequential upload and stops theRecStatpoll between queries; and a cancel with nothing running does nothing, where it used to clear an already-clear flag. A caller that expectedCancelUploadto be silent now receives oneOnErrorfor the operation it cancelled. A request already on the wire is not recalled - a command, a parameter batch, a blocking upload or aRecStatquery in flight still gets its reply - but a reply that arrives after the cancel is discarded: it re-arms no poll, sends no further parameter, runs no completion and releases nothing. A reply already being handled when the cancel runs is not interrupted.- Global User Units now arm on a connection whose channel cannot carry a bulk (RS-232, CAN, the legacy
Simulatorchannel). The group/factor/units configuration read and the sanctionedControllerMessagesContainer.TrySetUnitsSyncwrite batched their commands into aMessageType.BulkMessage, which AAComm refuses outside Ethernet withPC Suite: ERR 4004— so the read failed at connect,UserUnitsSupportedstayedfalsefor the rest of the session, every factor resolved to 1 and nothing was scaled on those transports, with no error surfaced to the caller. Both paths now send one message per command where the channel cannot batch, so support, per-axis enable, factors and unit names come off the wire and scaling works over RS-232 and CAN.TrySetUnitsSyncwrites over RS-232 as it does over Ethernet; over CAN it now reportsErrorWritingDatawhen the controller declares*UnitFctas adouble, because a 64-bit assignment does not fit a CAN frame (error304, a pre-existing limitation of that channel) — the write fails loudly with the cached factor left describing the controller, where a partial write used to report success. The single-message path also keeps the bulk's halt-on-error contract: nothing is sent after a command the controller (or the transmit path) refuses. Channels that carry a bulk — Ethernet andSimulation— still send the single bulk, unchanged. - A process that starts the AACommServer no longer leaves its own output open behind it. The spawned server is shared and outlives the process that started it, and it held that process's stdout and stderr, so a caller reading them — a script piping a tool, a CI step capturing its log to a file — waited for the server to exit after the work had finished. On Windows the server is now started through the shell with a hidden window, so it inherits none of the starter's handles; elsewhere it gets its own standard streams. A console server host (
AACommServerNative, or a managed server run throughdotnet) spawned on Windows now has its own hidden console, so a Ctrl+C in the starter's console no longer reaches it;CloseAACommServerstill stops it. - Two calls that arrive together on one download engine no longer both take the download.
AACommDownloadUP,AACommDownloadFW,AACommDownloadFPGA,AACommDownloadFPGAEtherCatandAACommDownloadFPGARemoteread their in-progress flag on entry and set it several statements later, so two threads could both pass the check: both then ran, each overwriting per-download state the other had just stored - theCommAPIin every engine, and the file and connection data in those that keep them, and neither was told it was refused. Each engine now takes the flag in one atomic step at entry and releases it on every path that does not start a download, so exactly one call proceeds and the other gets that engine's existing refusal text throughOnProcessEnded—Download is already in process!,FW Download is already in progress!,FPGA Download is already in progress!,Remote FPGA Download is already in progress!.AACommDownloadFW.StartDownloadingSafeholds the flag across its currents pre-flight, so a call arriving while that pre-flight runs is refused instead of admitted. A caller that starts one download at a time sees no change. - Two calls that arrive together on one
AACommDataRecManagerno longer both take the instance. The manager read its in-progress flag on entry and set it a few statements later, inside the pre-operation validation, so two calls could both pass the check: both then ran on the one manager, each overwriting theCommAPI, the parameter resolver and the completion callback the other had just stored, and neither was told it was refused - so one operation's reply invoked the other caller's completion. Every entry point is affected:SendRecordingParams,StartRecording,ForceTrigger,StopRecording,WaitForRecToEnd,UploadRecDataandUploadRecDataNonblocking. The flag is now taken in one atomic step at entry and released on every path that does not start an operation, so exactly one call proceeds and the other getsAsync operation in progress!throughOnError, as a call refused for an already-running operation does. A caller that starts one operation at a time sees no change. - The
AACommDownloadFPGAEtherCat.OnProgressdocumentation now states the scale the event reports, percent (0 to 100); it said fractions of 1000. The values the event reports are unchanged. AACommDownloadFPGARemoteandAACommDownloadFPGAEtherCatrefuse an empty image, or one whose length is not a whole number of 4-byte words, before connecting, where they used to erase the FPGA's primary image and write only its whole words, so a partial last word was lost and an image under 4 bytes wrote nothing.OnProcessEndedreportsInvalid input argument: FPGA image length must be a non-zero multiple of 4 bytes (got <length>), and nothing is sent to the controller.AACommDownloadFPGAEtherCatrefuses a null image the same way, reportingInvalid input argument: FPGA image.- With the packet sniffer on, a controller connecting over Ethernet no longer waits on the sniffer's session lock just to record the capture task it started. A slow Npcap device open holds that lock, so the server's handling of the new channel could stall for the length of the open.
AACommRemoteFpgaInfo.VariantStrshows the variant from the.verfile; it showedNAwhenever the amplifier version wasNA.VariantStr,AmpVersionStrandCommVersionStrprint their value as upper-case hex behind0x, as it is written in the.verfile; they printed the decimal digits, so0x1D01showed as0x7425. A value of 0 showsNA.- The binary continuous-integer replies (
AxSt;andV1AxSt;AllStat and ParamAbout,V1RecUpload;recording upload) over Ethernet or a firmware simulation end only on a>that closes a whole number of 5-byte (32-bit) or 9-byte (64-bit) segments, or that follows less than one segment. They used to end on any read whose last byte was>, so a0x3Einside a 64-bit value could cut the reply short at a read boundary. The legacy forms (RecUploadOK;,RecUpload;,AxisStatusOK;) still end on the last byte. - A firmware simulation is configured on connect unless its
AMotorTypequery is answered with a plain non-zero value; an error or malformed reply used to read as "already configured" and left the simulated motor unconfigured. The commutation recipe now goes to axes A to L, not only axis A (an error from an axis the simulation does not have is skipped); aSimMotorProfilewith[A]..[L]sections gives an axis its own encoder resolution and pole pairs, and[global]/[all]open the block every axis starts from. ARPTools.LookupResult.DoesIpv4SubnetMatchandARPTools.EthernetLookupResults.IsSubnetSetOnAnymatch a subnet on whole octets. They compared a bare text prefix, so subnet172.1.1also matched an adapter at172.1.10.x,172.1.11.xor172.1.100.xto172.1.199.x. An Ethernet connect to a172.1.1.xcontroller from a PC whose adapters sit only on such an address now fails withEthernetInterfaceMismatch, as it does for any other subnet, instead of going on to ping the controller from no adapter on its subnet.- A throwing
OnProcessEndedhandler no longer gets a second, failure report fromAACommDownloadFPGA,AACommDownloadFPGARemoteandAACommDownloadFPGAEtherCat: the handler is called once, with the true outcome of the download, and the connection is disconnected and the instance released once. AACommDownloadFPGARemote.StartDownloadingwith anullfpgaInforeportsInvalid input argument: AACommRemoteFpgaInfothroughOnProcessEndedinstead of the runtime's NullReferenceException text.AACommRemoteFpgaInfo(string rpdFile)finds the.verbeside a relative.rpdpath such asamp.rpd, which it refused, and on Linux and macOS finds it for any path, which it always refused.ARPTools.EthernetLookupResults.GetSubnetMismatchStringon an empty lookup returns its text withNAfor the adapter and its address instead of throwingArgumentOutOfRangeException.- A disconnect closes its IPC socket to AACommServer even when AACommServer has already dropped the connection, instead of leaving the socket open until garbage collection.
AACommFwInforefuses each firmware-file mismatch with one text, whether its constructor orValidateFwFilesrefuses it: a non-.binfile for an AGM800 readsOnly '.bin' files can be flashed to AGM800 products, and a.binfor a product that takes.hexfiles reads<product> can be flashed with '.hex' files only.ValidateFwFilesworded both differently.AACommDownloadFPGAandAACommDownloadFPGAEtherCatraiseOnProcessEndedonly once the controller accepts a connection again after the reset that ends the download, so a handler may reconnect at once. A controller that does not answer within 60 seconds ends the download with an error.AACommDownloadFPGAno longer waits a fixed 6 seconds instead
Deprecated
ConnectResult.FwInfoMismatch—Connectno longer returns it.
[14.0.0] - 2026-08-26
⚠️ Breaking
AAComm.dllis now strong-named (PublicKeyToken=043a10799a9b732b), so strong-named consumers can reference it — previously they compiled with warning CS8002 and failed at load withFileLoadException 0x80131044: A strongly-named assembly is required. Adding the strong name changes the assembly identity: .NET Framework consumers must recompile against 14.0.0 (a binary built against a 13.xAAComm, PublicKeyToken=nullreference does not bind to the signed assembly, and no binding redirect bridges an identity change); .NET 8/10 consumers see no difference. TheAssemblyVersionis pinned to14.0.0.0for the whole 14.x line (package and file version keep the full number), so a consumer built against 14.0.0 binds to any 14.x without redirects. Persisted assembly-qualified type names that spellPublicKeyToken=null(e.g.BinaryFormatterpayloads,Type.GetTypefull-name strings) stop resolving — simple names (AAComm.CommAPI, AAComm) are unaffected.AACommServerCore.dll(bundled with the server) is signed too; theAACommServer.exeprocess itself is not — seedocs/strong-naming.mdin the repo for the assembly-by-assembly map and the reason.- The C++/CLI
AACommNativewrapper has been retired (with itsAACommNativeTester/AACommNativeTesterCLIprojects). The NuGet no longer ships thelib/cppCLI/net48/slot (IAACommNative.h,AACommNative.lib/.dll, shared sources). Native (C/C++) consumers move to the new C-ABI SDK — projectAACommNative(reclaiming the name) →aacomm.h+aacomm.dll/libaacomm.so/libaacomm.dylib. OnResetProgressreplaced by two events —OnLongOperationStarted(LongOperationType)andOnLongOperationEnded(LongOperationType, int errorCode)— now firing for Reset, Save, Load, SaveUser, and EventCorrect. The ended edge'serrorCodeis0on success or the controller error code (e.g.22= "motor on") on failure, so subscribers can suppress a "complete" message on a failed long operation (issue #85). For the non-Reset types, a reply that arrives within a short start-grace window (100 ms) raises neither edge — an immediately-rejected operation (e.g. ERR 22 declining a flash Save while the motor is on) is not long-running, so no transient "started" notification can race and overwrite the error the reply itself reports. Subscribers must update their handler signatures. The native C-ABI SDK mirrors this withaacomm_set_longop_started_callback/aacomm_set_longop_ended_callback.- Binary IPC dialect v3 — typed payloads: binary requests now carry a typed message kind plus the raw payload bytes instead of ASCII-rendered, prefix-tagged strings, removing a redundant render/parse on both ends of every message. Measured on AGM800 and AGD301, binary round-trips now match or beat the ASCII protocol (previously ~15 % slower) with materially tighter tail latencies. The dialect is negotiated at connect: a version-skewed binary peer — old client on a new server, or new client on an old server — degrades to the ASCII compat protocol; the previous binary dialect is removed. The AACommServer comm log keeps showing the readable command text (carried alongside the binary payload for display); FW/UP download payloads log masked as binary, as before.
CommAPInow negotiates the wire protocol with AACommServer at connect time — binary by default, falling back to the legacy ASCII protocol when the server is an old ASCII-only build. The fallback is no longer silent:CommAPI.ServerCompat(see Added) reports the attached server's classification, so hosts can warn the user. The consumer does not select the protocol —new CommAPI()remains the only construction, and the negotiated mode is reported byCommAPI.Protocol(see Added). The ASCII client pipeline is deprecated and frozen — kept solely for compat with servers started by older SDK applications.IChannelSnifferHook.OnChannelClosed(bool)is nowOnChannelClosed(SnifferConnectionInfo connectionInfo, bool isError)— the close notification carries the controller endpoint so a host capturing several controllers at once can route the snapshot to the right NIC. Host sniffer integrations must update the signature.AACommAxisHelperis merged into the newAAComm.Services.AACommMnemonicHelper(see Added) and removed.AAxis,PAxis,ToAxis,ToAxisIdx(char)andToAxisCharare unchanged and live in the same namespace, so their extension call sites recompile as-is; direct class-name references (AACommAxisHelper.AAxis→AACommMnemonicHelper.AAxis) need updating. Two redundant members are dropped:ToAxisStr(int)(useToAxis(idx).ToString()) and theToAxisIdx(string)overload (uses.ToAxisChar().ToAxisIdx()— makes the first-char truncation explicit).AACommMessage.StartsWithPAxis(string)removed — usemsg.ToAxisChar() == AACommMnemonicHelper.PAxis(identical semantics, including null/empty → false).LoggerDataContainer/ScopeDataContainerconstructors take a new requiredchar[]parameter (paramAxes/signalAxes— the per-vector axis letters, needed to resolve User-Units scaling per axis) in addition to theCommAPI→ICommServicenarrowing. Existing call sites must supply the axis array.TriggerContainerconstruction is reshaped into three overloads: by source CCC code (ICommService, int trigType, int trigSrcCCC, …), by source mnemonic (ICommService, int trigType, string trigParam, …), or source-less (int trigType, double trigVal, …) for triggers that carry no parameter source. The previous single-constructor call sites must pick the matching overload.MessageData.DefaultValueStr(deprecated in 11.1.0 with its Min/Max siblings) is removed with the rest of that property group; its replacement lives onMessageDataValueExtensionslike the others (see Added).- The native C-ABI SDK (
aacomm.*) adopts a uniform status convention: everyaacomm_*function now returnsint32_t aacomm_status(AACOMM_OK=0 /AACOMM_NULL_ARG=-1 /AACOMM_BAD_HANDLE=-2 /AACOMM_EXCEPTION=-3), and domain results (theConnectResult, the connected flag, reply/error byte sizes) move toout_*parameters. Soaacomm_create,aacomm_destroy,aacomm_is_connected,aacomm_last_error,aacomm_start_server,aacomm_test_connection,aacomm_connect/_silent,aacomm_send_receive, andaacomm_last_replyall change signature (e.g.aacomm_create(aacomm_handle* out),aacomm_is_connected(h, int32_t* out_connected)) — this removes the old ambiguity whereaacomm_is_connectedreturned0for both "not connected" and a bad handle. Handles now come from a table that rejects forged/stale/double-freed handles withAACOMM_BAD_HANDLEinstead of risking undefined behavior. Addsaacomm_close_server(h, force)to shut down a spawned AACS. ABI generation resets to 1 at first ship under the soname policy (the ABI number now bumps only on breaking changes; additive growth never bumps it — see the AACommNative README); native consumers rebuild against the shippedaacomm.h. XmlHandler.SaveXml()is removed in favour ofTrySaveXml(out Exception)(see Added). It returned nothing and reported a failed write only through aDebug.Assert— a no-op in Release, so a document that never reached disk passed silently.
Added
- Always-on client diagnostic log —
AAComm.Services.Diagnostics.AACommDebugLog, the client-side counterpart of AACommServer's debug log. Session-lifecycle events (construction, connect/test outcomes, sync-timeout expiries — including the phase-1 queue-abandon vs phase-2 teardown distinction — server FATALs, disconnects with their reason) accumulate in an in-memory ring with zero configuration and zero steady-state file IO; a severe event (sync phase-2 teardown, FATAL, comms error) snapshots the ring to%LocalAppData%\PCSuite\AAComm_DebugLogs\{timestamp}_{process}_{pid}_{trigger}.log(debounced, pruned at 200 files). Every line carries an identity tag (api#N/conn#M); the connect line logs the endpoint and the socket's local port, which joins against theUserId↔ client-endpoint line AACommServer now writes on each Open. Public surface:AACommDebugLog.Sink(anAction<string,string>bridge so hosts can forward the lines into their own log) andAACommDebugLog.Flush()(on-demand snapshot). Full spec:docs/diagnostics-srs.md. - User Units — per-axis engineering-unit scaling, applied transparently to parameter values on both send and receive. At connect, the API reads the controller's units configuration (the
*UnitGroup/*UnitFct/*UnitUntparameter families, grouped perUUGroups: Pos/PosAux/PosPD/Vel/VelAux/VelPD/Acc/Frc) and the per-axisUserUnitsEnenables; from then on values of parameters in an enabled group are scaled by the axis factor in both directions. The newAAComm.Services.UserUnitsnamespace carries the model (UserUnitGroup,UserUnitAxis,UserUnitsData,UUGroups,UUErrors,UUMnemonics). The surface:CommAPI:GetUserUnitsEnabled(char)/SetUserUnitsEnabled(char, bool)(per-axis query/enable — the setter is controller-ack-authoritative),RefreshUserUnits()(re-read the configuration),OnUserUnitsEnabledChanged(raised on a confirmed per-axis transition), andSkipUserUnits(consumer-wide bypass, reset tofalseatConnect).AACommMessage:SkipUserUnits(per-message bypass) andAllowUserUnitsConfig(sanctions a raw write of the units-config parameters themselves — without it such writes are rejected withERR 4023; set units throughTrySetUnitsSyncinstead), plus constructor overloads takingskipUserUnits.ControllerMessagesContainer:UserUnitsSupported,MaxUserUnitNameLen(UUGroups),TryGetUserUnitAxis/TryGetUserUnitsGroup(read-only lookups, also onIMessagesContainer— implementors must add them), andTrySetUnitsSync(ICommService, UUGroups, char, string, double)— the sanctioned, synchronous write of a group's units name + factor.MessageData:UserUnits(the parameter's group, read-only),GetUserUnitsData(char)returning aUserUnitsDatastruct (Min/Max/Default scaled for the axis + Factor/Units), andGetDescription(char)(the parameter description with per-axis units resolved).FloatHelper:ApplyUserUnits/RemoveUserUnits— the Rx/Tx scaling primitives, for consumers formatting values outside a message flow.ICommServiceexposesGetUserUnitsEnabled(char). The native C-ABI SDK mirrors the per-axis enable/query asaacomm_get_user_units_enabled/aacomm_set_user_units_enabled.
AAComm.Services.AACommMnemonicHelper— the SDK home for controller-message string manipulation, consolidating helpers previously spread across host-side libraries. Covers axis-letter conversions (absorbed fromAACommAxisHelper), the'?'active-axis /'*'all-axes sentinels, mnemonic extraction (Mnemonic,SplitMessage), array-index handling (ArrayIndex,SeparateArrayIndex,RemoveArrayIndex), assignment/equality location that skips comparison operators (IndexOfEquality,IndexOfAssignment,IndexOfMnemonicEnd).IsAxisInRange(char, IIdentityContainer)validates an axis letter against the connected controller's actual axis count (the dynamic'P'axis is always valid; disconnected falls back to the theoretical 12-axis'A'–'L'range).MessageDatavalue accessors (MinValueStr/MaxValueStr/DefaultValueStr,MinValueInt/MaxValueInt/DefaultValueInt,MinValueLong/MaxValueLong/DefaultValueLong) — extension methods onMessageDataValueExtensionsformatting Min/Max/Default per the parameter type (no User-Units scaling), replacing the host-side equivalents that backed the properties deprecated in 11.1.0.IIdentityContainerexposesFeatures_02,CurrentLoopSampleRate,MaxNumOfRecVectors,MaxDataRecordingPoints,NumOfMotorAxes— needed by Core consumers of the recording pipeline and the User Units API.IMessagesContainerexposesContains(string),CanCodeToData,IsInitialized;ICommServiceexposesMessagesInBulk. Implementors of these interfaces must add the members.- Recording-pipeline command builders are now public API:
TriggersDataContainer.GetDownloadCommands(RecParamsResolver)/TriggerContainer.GetDownloadCommands(RecParamsResolver, int)(the trigger setup commands a recording download sends) andRecParamsResolver.GetTriggerIndex/GetTriggerLogicIndex(slot resolution for trigger sources) — needed by host recording tools driving the pipeline through the abstractions. - Binary IPC protocol v2 (additive, version-safe both directions): every request carries a correlation id the server echoes on its reply, and the client routes replies by identity instead of arrival order — eliminating a class of cross-flow reply misrouting when
SendandSendReceiverun concurrently (server-internal queries could previously overtake a pending controller reply and each caller received the other's answer). Legacy servers/clients interoperate unchanged (id 0 keeps arrival-order routing). A newCHANNEL_EVENTreply kind — sent only to clients that declare support on Open — notifies a passive client when its channel dies; it surfaces viaOnCommsError. - The bundled
AACommServerpacket sniffer now captures every connected controller simultaneously — one capture session, ring buffer, and snapshot tree per controller endpoint (under per-NICip_portsubfolders) — instead of only the most-recently-opened channel's NIC. The server window lists a live status/packet row per controller with per-NIC Mark and Export. CommAPIandAACommServerModelnow implementIDisposable—Dispose()delegates toDisconnect()/Shutdown()respectively, so consumers can useusingblocks.CommAPI.Dispose()does not shut down the shared AACommServer process (callCloseAACommServer()for that); both are idempotent.CommAPI.FlushDebugLog()— asks the attached AACommServer to write its in-memory debug log to disk now, instead of only at server shutdown, so a support snapshot taken while the problem is happening captures the current session rather than the previous one. Works whether or not the caller is connected, does not clear the server's log or disturb a running session, and is a no-op when no server is running. It is fire-and-forget — there is no acknowledgement: the result is the newest{yyyy_MM_dd_HH-mm-ss}_Flush.logunder%LocalAppData%\PCSuite\AACommServer_DebugLogs. Available on both the binary and legacy ASCII wire protocols; against an older AACommServer the request is ignored.CommAPI.Protocol(newProtocolModeenum:Ascii/Binary) — read-only report of the wire protocol negotiated with AACommServer (valid afterConnect).CommAPI.ServerCompat(newServerCompatenum:Unknown/Current/LegacyBinary/LegacyAscii) — classification of the AACommServer the client attached to, valid afterConnect. The version floor classifies, it never rejects: an old server started by an older SDK application still serves this client in compat mode (it owns the shared port; a client never shuts down a server it did not spawn), but the degraded mode — arrival-order routing on ASCII, missing channel events, skipped version-gated features — is now queryable instead of silent. The server now reports its version in 3-partmajor.minor.patchform.- The package now also ships a net10.0 target (alongside net48 and net8.0) — .NET 10 is the current LTS runtime; net8.0 reaches end of support 2026-11.
.NETCoreAppconsumers on either runtime get the same native AACS sidecar. - Ethernet connections to loopback addresses (127.x) are now accepted — enables connecting to a PC-hosted firmware simulation without a physical network adapter.
ChannelType.Simulation— new managed firmware-sim channel. AACommServer launches a localfirmware_simprocess over loopback and bridges it, presenting a real moving plant. Replaces the legacyChannelType.Simulatorfor end-to-end simulation — that param-echo channel's server backend is removed; theSimulatorenum is retained only for AAComm's in-process virtual-test harness. The child process is spawned on connect and reaped on close.ConnectionData.SimExePath/SimProduct/SimVersion/SimMotorProfile— configure the managedSimulationchannel (thefirmware_simexecutable to launch plus its catalog identity and motor profile).ConnectResultInterpreter.Interpret(ConnectResult)now returns localized text (en-US / zh-CN) perLocaleProvider.Instance.Locale.XmlHandler.TrySaveXml(out Exception)— saves the document, returningfalsewith the failure that stopped the final attempt when it could not be written after ten retries. ReplacesSaveXml()(see Breaking).- Four new
ConnectResultcodes:CANDeviceNotConnected(48) — a CANConnectfound the Kvaser driver installed but no physical Kvaser interface connected (canlib enumerates only virtual channels; distinct fromCANLibMissing, the driver itself absent — the native C-ABI SDK exposes it asAACOMM_RESULT_CANDEVICE_NOT_CONNECTED);SimLaunchFailed(49) /SimNoPort(50) — the managedSimulationchannel'sfirmware_simprocess could not be launched / did not announce its loopback port; andServerNotBinaryCapable(47) — an internal protocol-negotiation signal (a binary Open that got no handshake ack), never returned from a publicConnect.
Fixed
ConnectandTestConnectionno longer fail on a busy machine while the controller is healthy. Both steps of the connect handshake were budgeted as if they measured controller response time, but each depended on ThreadPool scheduling, so CPU pressure — a build, a test run, another application — could produceAACommServerNotRunningagainst a server that was listening, orFailControllerTypeagainst a controller answering in under a millisecond. Three changes: the server-liveness probe completes its loopback check on the calling thread instead of waiting on a pool-dispatched completion; a message waiting to reach the wire is no longer charged that wait against its ownTimeoutMswhen nothing else is on the wire, since only client-side scheduling can delay it there; and a dispatched request is no longer torn down for delays that are not the controller's — client-side completion scheduling, or server-side queueing behind another client — the synchronous wait grants starvation headroom aboveTimeoutMs + 100before implicating the link, because the server itself polices controller silence atTimeoutMsand reports it as a FATAL. A genuinely unresponsive server is still detected and disconnected, a few seconds later than before. A message queued behind a live one keeps its caller's budget and stays as responsive as before, andTimeoutMsstill means what it always did to the server — the controller's reply budget — so a wedged controller is still detected promptly. Measured against a live controller on a 12-core host: the first two changes took connect failures under load from reproducible to rare cold-start-only cases, and the third targets that remainder; idle-path latency unchanged.AACommFwInfonow reads the identity block (product, version, current ratings, identity text) of v5.x AGD301/AGD200 hex images. It located the block by counting records after the last0x0014segment record; those images fill that segment with code ahead of the block, soDataValidcame backfalse,VerifyCurrentsthrew "selected FW file doesn't contain 'currents' information", andValidateFwFilesskipped its product/version cross-checks. The block is now found by its record addresses; v3.x images and AGD155 read exactly as before.- AACommServer no longer keeps a phantom channel user when a client's connection is reset while
the server is still processing that client's request (an aborted connect, or a peer that
dropped mid-request). The dead client stayed registered on its channel for the server's
lifetime, so a later client on the same controller could be reported as one of several users
and have its FW/FPGA download exclusivity check (
NonSingleErr) refused until the server restarted. - A push message whose payload legitimately ends in
\ror>is no longer corrupted on the ASCII client path — the classifier used to strip all trailing framing characters instead of the single>\rframing pair, eating those payload bytes. - The AACommServer's post-reset Ethernet reconnect now retries on a 30-second time budget instead
of 5 fixed attempts. A controller that recovers slowly from
AReset(11–17+ s observed on AGM800, during which the host link is also down) previously exhausted the attempts and fatally closed the channel ("Post-reset reconnect failed"); it now reconnects as soon as the controller returns. A controller still unreachable when the budget expires triggers the fatal teardown as before. - AACommServer connect failures (including the post-reset reconnect FATAL) now report the concrete
reason — e.g.
Comms: EthernetFailARP (28) - Failed sending an ARP request to the controller IP— instead of the generic "Exception of type 'AACommServerCore.Comms.ConnectException' was thrown." text, in both the client-facing error and the server debug log. - Ethernet discovery and connect now work on hosts with multiple NICs: ARP requests are sent
from every local NIC whose subnet matches the target (previously
SendARPused the OS "best-metric" interface only, so controllers behind other NICs were invisible to Discover, and a direct connect with an empty ARP table failed). Covers subnet discovery, the connect-path reachability check, and the FW/FPGA download ARP warm-ups. (issue #25) - A controller reply over Ethernet consisting of only the RS232 terminator (
>) with no value — a malformed/incomplete answer to a value query — now surfaces immediately asERR 4024instead of stalling until the channel times out and drops the connection. The server can tell it apart from a legitimateOKat the wire frame (a valid reply is[type-byte, …, '>']; the malformed one is a lone'>'), so no genuine reply is affected. (issue #45) - 64-bit integer parameter assignments now transmit byte-exact. Every assignment was parsed
through
double(which carries only 2^53 of integer precision), so an int64 value above ~9·10^15 — e.g. one written verbatim to a.parfile by a parameters upload — was silently rounded to a neighboring value on download/write. Plain-integer text now bypasses the double path when user units don't transform the value. - AACommServer now honors
MessageandCloserequests only from a socket whose open record names that channel (the membership gate). Previously a misbehaving client that never opened the channel — or sent a duplicate/staleClose— could latch exclusive comms on another client's channel (locking its members out and blockingShutDownForce) or close the channel under its remaining users. UserProgVarsAdapter.AdaptUserProgMessageno longer drops the per-message User Units flags (SkipUserUnits,AllowUserUnitsConfig) when duplicating the message with adapted task/global-var names — a raw-units message routed through the adapter (e.g. every AAMotion synchronous command) was silently re-scaled, and a sanctioned UU-config write was self-blocked by the Tx-build gate.- Spawning the bundled server is now robust across hosts and deployments:
- A spawned server is always started headless (
--headless+ redirected stdin) — a net8/native console consumer no longer gets the sidecar's Terminal.Gui monitoring TUI taking over its terminal (GUI apps no longer pop a console either). Started manually, the native host still opens its TUI; a second manual start now exits with a clear "port already in use" message instead of aborting. - The spawn wait is a real deadline (15 s, sized to a cold-starting net48 WPF server) polling the port and detecting a child that died during startup — previously a fixed 500 ms grace made a slow cold start fail spuriously and leave an orphaned zero-client server behind.
- Server resolution probes the application base directory (where the package places the sidecar) before the working directory — a Windows-service host (cwd = System32) now finds its own sidecar. A .NET-Core client prefers
AACommServerNative.exe, so a stale net48AACommServer.exeleft behind by an overlay upgrade can no longer be spawned into the old runtime-mismatch crash. - The PCSuite-installation registry fallback reads the 32-bit registry view the installer actually writes — 64-bit net48 consumers previously missed an installed PCSuite entirely — and a present key with a missing
InstallPathvalue reports the missing-installation result instead of an exception result.
- A spawned server is always started headless (
- AACommServer's implicit last-client self-shutdown now also covers a client whose channel died under it: when a controller-link FATAL evicts a channel and its last client then disconnects, the server terminates as it does for any last-client death. Previously the fatal path erased the client's record first, so its death went unnoticed and a zero-client server lingered indefinitely — silently attached (whatever its version) by the next client to connect. A graceful
Closestill deliberately leaves the server running for reconnect flows. - AACommServer: a repeated Open on the same client socket is now transactional — a re-Open that fails (unreachable target, disabled channel type) leaves the client's previous channel attachment fully intact. Previously the failure path released the previous membership and erased the client's open record up-front, orphaning the socket so the server's implicit last-client shutdown never triggered. A sole client re-Opening the same channel also no longer bounces the controller link through a momentarily-userless channel.
- A
SendReceivewhose timeout expires while the message is still queued behind a slower call no longer disconnects the whole API — it returnsERR Sync: Timeoutand the message is never sent (previously a short-timeout call queued behind e.g. a 20 sMotorOndeterministically tore down the connection). The wire-timeout clock now starts when the message is dispatched, matching the server-enforcedTimeoutMs; a timeout after the message reached the wire still disconnects, as before. - An asynchronous
Sendcallback now fires exactly once, even when the connection tears down (disconnect, controller-link FATAL) before the reply arrives — the in-flight message's callback getsERR Sync: Session terminatedand still-queued messages getPC Suite: ERR 4020, both withIsErrorset. Previously those callbacks were silently dropped, so a consumer waiting on the callback (e.g. a native-SDK / Python future) waited forever. - One AAComm client can no longer stall or corrupt other clients sharing the AACommServer. The server's channel lifecycle was reworked so that slow operations — opening a channel to an unreachable controller, the post-Reset recovery wait, a reply to a client that stopped reading its socket — never block message routing for other clients or channels. Previously, one application connecting to a wrong IP could time out (and force-disconnect) another application's live session, a close during another client's Reset stalled the whole server for the recovery window, and a hung client could wedge its channel indefinitely. Each client socket also gets a dedicated ordered writer, so concurrent replies/pushes can never interleave frame bytes.
- When a hardware channel dies (controller link lost, timeout), every client of that channel is now notified — including passive clients with no request in flight, which previously discovered the death only when their next command failed.
COMTools.GetFTDI_COMs()now lists the vendor-filtered (FTDI / Z-TEK / Prolific) USB-serial ports on net8.0 and native consumers — previously the WMI body was compiled out there (#if !NETCOREAPP), so those consumers got an empty list. Enumeration moved off WMI to the Windows registry (and udev/dev/serial/by-idon Linux), removing AAComm's lastSystem.Managementdependency.- A failed or partial connection — or a channel FATAL racing an explicit
Disconnect— is now reported as a properConnectResultinstead of being masked by aNullReferenceExceptionthrown from the connection-cleanup path while tearing down a not-yet-fully-open connection. - A controller-link FATAL or sync-timeout that tears the connection down while a
Connect/TestConnectionis still mid-flight (reading controller identity) no longer NREs the in-flight connect. Connection teardown is now serialized on the connect lock, so the receive-thread FATAL handler can't dispose the handler out from under the connecting thread. Surfaced by connect/close churn against a saturated server. - Rapidly closing a connection (tool-navigation churn) no longer emits a spurious comms error / debug assert from the binary-IPC receive loop racing the socket close — the disposed-socket teardown is now handled as the normal close it is.
- AACommServer no longer deadlocks when a client disconnects at the same moment one of its channels hits a FATAL timeout. A lock-order inversion between the channel registry and a channel's queue could wedge the server (hanging any connected client) until the process was killed.
- About-data parsing no longer overflows on
ScalingFactvalues emitted as unsigned float bit patterns. - Socket teardown no longer crashes the process on macOS/Linux hosts: connection-reset/abort detection now uses platform-normalized
SocketErrorCodeinstead of the Windows-only raw error 10054 (affected both the binary and ASCII IPC clients). Connection-aborted, network-reset, and shutdown errors now also report the connection as lost instead of a generic error. - A reply that fails inbound post-processing (e.g. a malformed
ParamAboutanswer) no longer tears down the whole session — conversion degrades to the raw reply string. Previously the exception unwound through the binary-IPC frame dispatch and every subsequent call failed with "PC Suite: ERR 4020". - A controller link that dies mid-session (reset, cable pull, or clean remote close) now reliably surfaces as a connection-loss error and the API recovers with a plain reconnect. Previously a clean remote close left the channel half-dead (every command timing out individually), and the racing teardown paths could crash the host process.
- net8.0 / .NET-Core consumers received the net48
AACommServer.exeas their bundled server and crashed it on launch (aSystem.Runtimeversion mismatch when the net48 process loaded the consumer's net8.0 assemblies). Each consumer now gets a server matching its own runtime (see Changed). The net48 sidecar additionally ships its full runtime closure (it had been missingGoogle.Protobuf.dll). - net8.0 / .NET-Core (and native C-ABI) clients could not connect over CAN — a client-side WMI Kvaser-presence pre-check was compiled out on .NET Core (
#if !NETCOREAPP) and always reported "no device", so the connect failed withRequiredConnectionUnavailablebefore the (working) CAN transport was reached. The presence check now runs server-side via canlib (cross-platform, excluding virtual channels), so CAN connects succeed from net8.0 / native clients; when the host has no physical Kvaser interface the connect returns the newConnectResult.CANDeviceNotConnected(see Added).
Changed
StartAACommServernow auto-locates a server on Linux and macOS (.NET 8/10 clients). It previously refused outright off Windows, so a non-Windows consumer had to pass an explicit path. The search is the application directory then the working directory,aacommserver/before flat, looking forAACommServerNative— off Windows the NativeAOT host carries no extension and the net48 WPF host does not exist, so it is the only candidate. There is no install-location convention to fall back on, so a miss there is the final answer and still reportsAACS_ResolveWrongOS, whose documentation now describes that rather than a blanket refusal. Windows resolution is unchanged, and .NET Framework clients keep their previous behaviour.- The daemon's execute bit is restored before spawning it (Unix only). Neither a zip nor a NuGet package carries the Unix executable bit, so a host delivered through either arrives non-executable and the spawn failed with EACCES. Best-effort: a read-only mount or another owner's file is left alone, and the start reports the real error.
- A server host is launched directly unless it is a managed
.dll. The launcher keyed off an.exesuffix, so the extensionless NativeAOT host on Linux/macOS was handed todotnetas though it were an assembly. - The release asset published to the public release page is now the
Agito.AAComm.<version>.nupkgitself — the same package nuget.org serves. TheAACommPackage_v.<version>.zipbundle is retired; its managed DLLs and server come from the NuGet package, and its DocFX copy of the API site is the site already published at https://akribis-agito.github.io/Agito-Releases/aacomm/. Non-.NET consumers keep the separateagito-aacomm-native-sdk-<version>.zip, which — withAgito.AAComm.Native.<version>.nupkg— now ships on its own publicSoftware/AAComm-Nativerelease line beside this one. - The bundled net48
AACommServerclosure now lands in anaacommserver\subfolder of the consumer's output directory instead of flat at its root — the server'sGoogle.Protobuf.dlland BCL shim DLLs no longer collide with the consumer's own same-name assemblies (the winner was previously decided by file timestamp). The server is resolved from the subfolder automatically; explicitStartAACommServer(path)calls and existing flat layouts keep working. - A spawned (headless) net48
AACommServerthat loses the socket-bind race — or finds the port blocked — now exits silently with a log entry instead of parking a modal retry dialog no one can dismiss; its spawner attaches to the winning instance. Interactive launches keep the dialog. - AACommServer debug logs and packet-sniffer captures now live under
%LOCALAPPDATA%\PCSuite\AACommServer_DebugLogs(was theDocumentsfolder). WhereDocumentsis OneDrive-redirected, sync locked the large rotating capture files and contended with the per-channel snapshot I/O; the local-app-data location is non-synced. The server's "Open Folder" button and log save-dialogs follow automatically. The per-snapshot total-size budget sweep is also throttled so connect/close churn doesn't re-walk the whole capture tree on every channel close. - Firmware download over CAN is now explicitly rejected up front (
AACommDownloadFW.StartDownloading/StartDownloadingSafe) with a clear message — "Firmware download is not supported over CAN. Download firmware over Ethernet (AGM800) or RS-232 (serial)." FW download was never functional over CAN (the controller-side CAN boot-loader FW-data contract is not met by this path); it previously failed mid-transfer with a cryptic controller error. Use Ethernet (AGM800) or RS-232. - Removed the YamlDotNet dependency — the parameter-info and error-code databases are now build-generated; consumers no longer deploy
YamlDotNet.dll. - Internal: the bundled server-engine assembly was renamed
AACommServerAPI.dll→AACommServerCore.dll(projectAACommServerAPI→AACommServerCore). No public API change — consumers referenceAgito.AAComm; only a consumer copying the engine DLL by name is affected. - NuGet
AACommServersidecar selection is TFM-based (it was OS-based as of 12.1.1):.NETFrameworkconsumers get the net48 WPFAACommServer.exe;.NETCoreApp(net8.0) consumers now get the self-contained NativeAOTAACommServerNativehost (win-x64 — no .NET runtime on target; the same exe also runs a Terminal.Gui monitoring UI when launched interactively). Because the native exe carries no managed assemblies beside it, it resolves the prior net8-consumer crash where the consumer's ownGoogle.Protobufcould shadow the server's. linux/osx net8 consumers have no sidecar until their native build runners land; consumers that need the WPF UI should target net48. The retired net8AACommServerNetConsole+AACommServerNetAvaloniahosts are gone. FloatHelper.ResolveFromCCC,CanDescriptionBuilder.GetParameterFromCanDescription,CanDescriptionBuilder.GetCanDescriptionFromParameter:dataContainerparameter type narrowed fromControllerMessagesContainertoIMessagesContainer. Source-compatible — the concrete still satisfies the interface.DataRecParams.ParamsToRecParamArray,DataRecParams.CalculateActualGapValue,DataRecParams.CalculateLengthValue:apiparameter type narrowed fromCommAPItoICommService. Source-compatible.AACommDataRecManager,AACommDataRecManagerSync,RecParamsResolverconstructors and methods:apiparameter type narrowed fromCommAPItoICommService. Source-compatible. (TriggerContainer,LoggerDataContainer,ScopeDataContainerget the same narrowing but their constructors also change shape — see Breaking.)ControllerMessagesContainer.CanCodeToDatadeclared type narrowed fromDictionary<int, MessageData>toIReadOnlyDictionary<int, MessageData>, matching the newIMessagesContainermember. Source-compatible for readers (lookups,TryGetValue, enumeration); code that mutated the map through this property no longer compiles — the container owns its content.
[13.0.0] - 2026-05-25
⚠️ Breaking
- Send()/SendReceive() now return "PC Suite: ERR 4020" when not connected (was "ERR Sync: Not Connected!"). Callers using
AACommEventArgs.IsErrorare unaffected. - Mixed-mode rejection strings removed — Send and SendReceive no longer reject each other (see Changed).
- Rename
HighPrecGainScale_Supported→HighPrecGainScaleAMAT_Supported - AACommServer closed-connection error payload changed to "PC Suite: ERR 4021" (was "AACommServerNotRunning"). Callers using
AACommEventArgs.IsErrorare unaffected.
Added
- New
CommAPI.IsResettingproperty — true during the post-Reset recovery window. - SendReceive returns "ERR Sync: Session terminated" on session death (distinct from "Timeout").
- New
OnUserProgramVarsUpdatedevent — fired when a User Program download changes cached global vars. - AACommSniffer:
MaxTotalSizeMB(default 1500) caps total disk usage;MinUserSessionMs(default 2000) filters noise from rapid reconnect cycles.
Fixed
- SendReceive no longer deadlocks when the internal dispatcher dies unexpectedly.
- Post-AReset reconnect retries with backoff (5 × 500 ms) instead of failing on first attempt. Eliminates mysterious timeouts when controller is slow to recover.
- IPC framing: coalesced TCP receives no longer cause spurious disconnects under sustained load (both server and client side).
IsConnectednow correctly becomes false when AACommServer closes cleanly (was silently stale until next Send failed).OnCommsErrorno longer silently suppressed during brief dispatcher-iteration gaps.
Changed
- Send and SendReceive fully thread-safe for concurrent use — no more mutual rejection. FIFO ordering preserved per-flow. Re-entry guard: calling SendReceive from inside an OnReplyReceived callback returns an error instead of deadlocking.
- Internal transport rebuilt on dual independent pipelines (no public API change).
- AACommSniffer: default disk footprint reduced from ~9.5 GB to ~1.3 GB; stale snapshots trimmed automatically.
[12.1.1] - 2026-04-17
Changed
- Packaging: NuGet sidecar selection is now OS-based instead of TFM-based.
Windows consumers (net48 or net8.0) always receive the WPF
AACommServer.exe(net48 build, runs on any Windows with .NET Framework 4.8 — keeps sniffer/UI available to net8.0 Windows consumers). Non-Windows net8.0 consumers continue to receiveAACommServerNetConsole
[12.1.0] - 2026-04-15
Fixed
-AAComm/Services/ARPTools: SendARP multi-NIC discovery fix: scan from all local Ethernet interfaces on the target subnet instead of relying on OS routing (SrcIP=0). Fixes discovery missing controllers when multiple NICs share the same subnet (e.g. 172.1.1.220 and 172.1.1.225). - New GetLocalIPsOnSubnet() returns local IPs matching a subnet - GetSubnetActiveIPs() now scans per-NIC in parallel using ScanSubnetFromSource() with explicit SrcIP binding - New PingAddressFromMatchingNICs() tries ARP from every matching NIC - PingAddress() overload accepts srcIP parameter - Thread-safe results using ConcurrentBag (replaces unsafe List)
[12.0.0] - 2026-04-03
⚠️ Breaking
- AAComm/Services/ControllerIdentityContainer: Removed deprecated const MaxNumOfRecParams (use MaxNumOfRecVectors)
- AAComm/Services/AllStatInterpreter: Constructor now requires CommAPI (old int-based constructor removed)
- AAComm/Shared/AllStatData: All fields upgraded from int to double
- AACommNative/ParseAllStatData: Removed newFeatures parameter
- AAComm/Services/CanDescriptionBuilder: Make class static, change methods
- AAComm/Extensions/RemoteFpgaInfo: Change FPGAVersion from 'Version' to 'string'
Changed
- AAComm/Services/Feature_02: Add LockSrcZero_Supported
- AAComm/Extensions/RecData: Expose RecParamsResolver class + more functionality
- AAComm/Extensions/RecData: Expose RecUploadNext functionality
- AAComm/Extensions/RecData: Expose LoggerDataContainer to parse LoggerUpload
- AAComm/Extensions/RecData: Expose ScopeDataContainer to parse ScopeUpload
- AAComm/Services/AllStatInterpreter: Add float/double support
- AAComm/Services/ParamAbout resolution:Add double support
- AAComm/Services: Add a 'SpecialMessages' static class with common controller parameters
- 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
Fixed
- AAComm/Extensions/RecData: Correct float/double rec upload data processing
- AAComm/Extensions/RecData: Legacy RecUpload header parsing trigger type/mask
- AAComm/Services/AllStatInterpreter: Correct float/double AllStat data resolution (was parsing all values as int)
[11.1.0] - 2026-03-08
⚠️ Breaking
- AAComm: Deprecate MessageData properties: Min/Max/DefaultValueStr Min/Max/DefaultValueInt/Long ToString() is replaced with GetDescription(char axis)
Changed
- AAComm: 'Push' notifications support + API
- AAComm: Read exact 'Identity' indexes at Connect() without 'out of range' controller error break
Fixed
- AACommServer: .NET SDK 10 Resource Loading Fix
- AACommServer: multi-threaded debug log access crash
[11.0.0] - 2026-01-07
⚠️ Breaking
- AAComm and AACommServer: Deprecate .NET 4.0 targets
Changed
- AAComm/Extensions/RecData: Fix incorrect trigger initialization at TriggersContainer
- AAComm/Services/Feature_02: Add HighPrecGainScaleOFR_Supported
- AAComm/FW download: Add 'AACommBinFwInfo' class for AGM800 FW file info
- Manage/FPGA: Refresh file contents during Remote FPGA download
[10.0.0] - 2025-12-01
⚠️ Breaking
- AAComm/IdentityContainer: Rename property: IsInBoot -> InBoot Rename property: NewFeatures_1 -> NewFeatures_01 Factor-out 'Features' containers (NewFeatures_1,2) Factor-out 'Versions' container (FW, FPGA, CM, ESI,..) Factor-out 'Connectivity' items (IP, MAC, COM port,..) Move 'IsCiMasterExt' from IdentityContainer to ProductTypes Move 'IsEtherCATExt' from IdentityContainer to ProductTypes Fix: RS-232 and USB connection failed if Ethernet cable disconnected
⚠️ Breaking
- AAComm: Remove: 'MinMsgDelayMsec', 'IgnoreDebug13', 'IsUser1', 'DrawCodeCRC', 'ChannelType.User1UDP', 'ChannelType.User1RS232'
Changed
- AAComm/IdentityContainer: Add support for 'New_Features_02' (Identity[62])
- AAComm/IdentityContainer: DrawModeProfiler_Supported, HomingStep_21_Supported bits
- AAComm/FW download: Allow FW download over any IP address
- AAComm/FW download: Fix flashing of AGD301 in boot mode
- AACommServer CAN connect: Fix native CANLibCLS dependency resolution
- AAComm/MessageData: Correctly apply scaling factor to 'float' and 'double' parameters
[9.1.0] - 2025-07-25
Changed
- AAComm: IdentityContainer: Add 'InjectTypeBeforeMotorOn' flag
- AACommServer: Improve Ethernet communication to fix 300 msec delay
[9.0.1] - 2025-05-23
Fixed
- AACommServerAPI: Ethernet: multiple NIC up/down connect issue
[9.0.0] - 2025-05-12
⚠️ Breaking
- AAComm: Change IsAACommServerRunning() to return 'ConnectResult' enum
Changed
- AAComm: Remove ParamAbout handling "hack"
- AAComm: Improve reliability of StartAACommServer() function
- AAComm: Correct error code for disconnected Ethernet cable at Connect() call
Fixed
- General: Correct the 'manual' AAComm package .net 6.0 and 8.0 targets
[8.0.0] - 2025-03-21
⚠️ Breaking
- AAComm/Services/ControllerMessagesContainer: Deprecate 'MnemonicToData' and 'AllMnemonicsHashLow' containers
Changed
- AAComm/OnResetProgress: Improve behavior and invoke in NEW task
- AAComm/Shared/ProductTypes: Mark AGM400 as 'deprecated'
- AAComm: Add implicit ParamAbout handling
[7.1.2] - 2025-02-12
Changed
- AAComm: Add protection against invalid AACommMessage format
- AAComm: Increase AGM800 'reset time' to 10 sec.
- AAComm: Extend FwInfo validations at Connect() with Current Rating
- General: Update YAML nuget to 16.3.0
Fixed
- AAComm/Extensions/AACommDownloadFW: Fix AGM800 DL over non-default IP
[7.1.1] - 2025-01-16
Changed
- AACommServer: Improve faults logging, add "save log" button
[7.1.0] - 2024-11-21
Added
- AAComm/Extensions/DlFPGA: Add support for EC FPGA DL
Changed
- AACommServer/API: Add .NET 4.0 target
- AAComm: Add PULSE_SYNC product type
- AAComm/Services: Add 'AACommAxisHelper' class
- AAComm/Extensions/AACommFwInfo: change signature of ValidateFwFiles()
[7.0.4] - 2024-09-16
Changed
- General: Allow connecting over wifi interfaces
[7.0.3] - 2024-06-16
Fixed
- General: .NET 4.0 YamlDotNet reference 4.2.2
[7.0.2] - 2024-06-11
Changed
- AAComm: Add 'MessagesInBulk' property
- AAComm/Shared/DataRecParams: Add 'ParamsToRecParamArray' helper function
Fixed
- AACommServer: "Exclusive comms. access" bug
[7.0.1] - 2024-06-01
⚠️ Breaking
- AAComm/Extensions/AACommFwInfo: Change CTOR signature
- ControllerIdentityContainer: SlowLoggerByTime -> DataLoggerByTime
Added
- Extensions: add "RecData" related classes and functionality
- Support 'float', 'double', 'long' parameters: Terminal, IDE+, Watch and Panel windows
- Add support for AGD101-EC product type
- Add support for AGD156-EC product type
- Add .NET 6.0 and 8.0 targets, deprecate 4.6, core 3.1, 5.0 targets
- AACommDownloadFW: Support EC ESI image flashing
- AACommServerAPI: extended logging mechanism, logs saved at Documents\PCSuite\AACommServer_DebugLogs
- AAComm/Services: Add 'VerifySingleAACommClient' helper class
- AAComm/Services: Add 'AACommExclusiveCommsManager' helper class
Changed
- AACommServer Simulator: Improve behavior for LARGE parameters file
- AACommDownloadFPGA: Add 'Product type' protection to '.ver; file
- Shared.ProductTypes: Add NameToId and IdToName functions
- ConFault LocaleProvider resolving for negative values: "Fault by user: xyz"
- AAComm: Fail initialization if YamlDotNet is missing
- ControllerIdentityContainer: FloatPhase_1_Supported property
- ControllerIdentityContainer: MultipleGantry_Supported property
- AAComm: Disable SendReceive() timeout when DEBUGGER is attached
- AACommServer window: Add "trim long messages" checkbox
- AACommServer: Add setting + folder for "implicit shutdown log" functionality
- AACommDownloadFW: Add protection vs. other device on IP 172.1.1.101
- AACommDownloadFW: Add protection vs. incorrect EC CM FW file
- Services/ConnectionData: Add static properties: ET_IP_Default and ET_IP_DefaultSubnet
- AACommServer window: Improve 'debug log' UI
- AACommServer: Improve protection vs. connect/disconnect re-entry
- Services/COMTools: Add support for 'Prolific' RS-232 manufacturer
- ControllerIdentityContainer: Add Draw Control related property
- AACommDownloadFPGA: Over Ethernet, allow only IP 172.1.1.101
- AACommServer: Allow "retrying" socket init. upon process start
- Services/AllStatInterpreter: Support axes I to L
- AACommServer: Improve logging
- AACommServer: don't show a messagebox if sim. file is missing
- ControllerIdentityContainer: Add DefaultSamplingRate(ProductType) function
- AAComm/Services/LocaleProvider: Add support for Draw err. 5001-6000
- ControllerIdentityContainer: HomingSteps_19_20_Supported property
Fixed
- AACommDownloadFW: Add protection CPU1/CM files mismatch
- AACommServer: RecUpload (all "continuous" messages) over Ethernet bug
- AACommServer: 'EA bulk' message: don't append duplicated '>'
- AACommServer: Download User Program over CAN bus bug
- ParametersInfoDB: Fix incorrect DB file encoding
- Services/CanDescriptionBuilder: Fix ParseCanDewscription() wrongly parsing axis > H
- AACommServer: Linux support: Ethernet socket 'fast ack' related bug
- AACommServer: Ethernet 'fast ACK': Enable only for Windows >= v.10
- Params Info: MotionStat bits map strings
- AACommServer: Protect against Debug Log overflow
- AACommServer: Ethernet: fix double '>' at end of 'continuous' messages (i.e. ProgInfo)
[6.0.0] - 2023-05-01
⚠️ Breaking
- Removed multiple unused properties and functions
Added
- ParametersInfoDB: Refactor + add support for 'values' descriptions
Changed
- LocaleProvider: Refactor, deprecate functions
- ControllerMessagesContainer: deprecate GetJerkMessage() function
[5.5.0] - 2023-03-13
Added
- Support for 'BMI:' prefix: Bulk message that ignores errors
Changed
- ControllerIdentityContainer: 'MasterPWM_Supported' property
- AAComm.CommAPIWrapper: Add GetCanDescriptionFromParameter()
Fixed
- AAComm: Set proper timeout when MotorOn,1 is sent using ','
[5.4.0] - 2023-03-03
Added
- AAComm.Services: Add ParametersInfoDB class(es) (also add to Services.MessageData)
- General: Support for RecUploadB - new recording stack
Changed
- ControllerIdentityContainer: 'DoubleFlash_Supported' property
- ControllerIdentityContainer: 'HomingSteps_17_18_Supported' property
- ControllerIdentityContainer: 'CNC_Feature_8_Supported' property
- ControllerMessagesContainer: properties, CTOR's, etc. ⚠️ Breaking (but no one uses this!)
- AAComm.Shared.Timeouts: Add DownloadUPBin = 10 sec.
- AAComm.Extensions.AACommDownloadUP: Increase process timeout
[5.3.1] - 2023-01-19
Changed
- ControllerIdentityContainer: Add IsEtherCAT property
- AAC: Improve big packets Rx handling
Fixed
- AACS: Fix RecUpload and ProgInfo error interpretation
- Fix nuget YamlDotNet dependency
[5.3.0] - 2022-12-21
Added
- AACommDownloadFW: Add support for AGD155_EC Core1 and CM FW DL
Changed
- ControllerIdentityContainer: Deprecate 'MaxNumOfRecParams' property
- Update YAML nuget
- AACS/Ethernet: Increase socket receive buffer to 2M bytes
- AACommDownloadFW: Improve protections
- AACS/RS232 handler: Refactoring
- AACS/Log: Trim long messages after 1K characters
- AAComm/AACS/Ethernet: Decrease 'long' messages handling time by a factor of x100 (i.e. RecUpload, ProgInfo, ...)
- AACS Wnd: Add 'Stay on top' checkbox
Fixed
- AACommDownloadFPGA: Deprecate unneeded Reset at end of process
[5.2.0] - 2022-11-15
Added
- AACS: Controller over Ethernet: 10x speed increase of "continuous messages"
Changed
- Add public class "Services/HeaderParser.cs"
- AAComm.Extensions.DL_FPGA: Add "reset" at the end of DL FPGA process
- AAComm.IdentityContainer: Add "Halls-only Phasing supported" property
- AAComm.IdentityContainer: Add "CNC 3D Arc supported" property
Fixed
- AACS: Simulation: Do NOT save changes to the Parameters file
- AACS: Simulation: Allow parallel use by multiple clients
[5.1.0] - 2022-06-19
Added
- AACS: Improve 'debug' logging and auto-save log on implicit AACS close
Changed
- Add support for AGD155-EC
- Internal: Add lock section for socket closure
- AAComm: Increase ASave time-out to 60 sec.
- AAComm.IdentityContainer: Add 'indirect array indexing is supported' flag
- AAComm.Services.CanDescriptionBuilder: Add new functions for User Program CCC handling (internal use!)
Fixed
- AAComm.Services.ControllerIdentityContainer.FPGAVersionStr: 'Dyn Brk Mismatch' is NOT an FPGA fault
- AACS: Fix CAN Tx/Rx bug when connecting to multiple controllers over a 'daisy chain'
- AACS: Deprecate a LOCK section that prevented send/receive while Connect was in progress
[5.0.1] - 2022-03-07
Changed
- AAComm.Services.ControllerErrorInterpreter: Add 'IsAssignedValOutOfRange()' static function
- AACommNative: Fix dependencies and add YAML dll to package
- Documentation: Some additional details
Fixed
- Simulator connection: Increase 'Open' timeout to accommodate for large parameters files
[5.0.0] - 2022-02-02
⚠️ Breaking
- Removed AAComm.Services.ControllerErrorInterpreter.MotionStatTests (now multi-lang.)
- Removed AAComm.Services.ControllerErrorInterpreter.GetErrorCodeByName() (deprecated)
- AAComm.ControllerIdentityContainer.AnyFPGA_Fault + IsFaultyFPGA... properties are replaced with "UnitStat" property. Contact Agito for faults interpretation.
Added
- Add .NET 5.0 target framework
- AAComm.Services.LocaleProvider class that handles multi-language support
- Add User Program Download protection (target controller FW version check)
- Add support for 16 bit (large) arrays
Changed
- Download FW: Increase MAS02 erase flash to 22 sec
- AAComm.Shared.ProductTypes: add public GetProductIdByName() function
- AACommNative: Add IsAACommServerRunning() function wrapper
- AAComm.Services.GetFTDI_COMs(): Add try-catch
[4.15.1] - 2021-09-29
Changed
- Add IdentityContainer "CNC_Feature_6_Supported" NewFeatures bit property
Fixed
- FW DL bug over RS-232
[4.15.0] - 2021-09-16 (DEPRECATED: FW DL bug over RS-232)
Added
- Introduce AAComm <-> AACommServer "error reporting" and logging protocol
Changed
- Improve ConnectResultInterpreter class
- Improve Connect() error reporting
[4.14.0] - 2021-09-10
Added
- Support for SBCB and BMCB message type for CNCB support
- Shared/ConnectResultInterpreter class for ConnectResult enum interpretation
Changed
- ControllerIdentityContainer/IsFPGAVersionEqualOrGreater(): allow 3 "version" values
Fixed
- Fix FW/UP/FPGA DL bug over non-Ethernet channels (due to 4.13.0 "protections" feature)
[4.13.0] - 2021-09-06 (DEPRECATED: FW/UP/FPGA DL bug over non-Ethernet channels)
Added
- AAComm property "AACommServerVersion"
- Add multi-AACommServer client protection for FW, FPGA, remote FPGA, UserProg download
Changed
- Add IdentityContainer "GearMasterProfiler" NewFeatures bit property
- Add IdentityContainer "SlowLoggerByTime" NewFeatures bit property
- Add IdentityContainer "SmoothAutophase" NewFeatures bit property
- Add IdentityContainer "IsNoGolden" UnitStat bit property
- Add IdentityContainer "ProductVariant" Identity property
- Add 'Vector' MotionStat texts
[4.12.1] - 2021-08-24
Changed
- Add 'Spline Buffer Stop' MotionStat text
[4.12.0] - 2021-08-12
Added
- AACommDownloadFPGA: Don't allow Ethernet FPGA DL over other than 172.1.1 subnets (non-AGM800 products only)
Changed
- AGM800 DL FW: Increase sleep time after DL FW from 8 to 12 sec
- FPGA DL: Don't allow download when BOOT version < 1.013
Fixed
- Don't sleep after "failed" Reset request
[4.11.0] - 2021-06-24
Added
- New "Extension": FPGA Download for Master remote unit
Changed
- FPGA DL: CAN is NOT supported
- Increase Load and Save timeouts
- MessagesContainer InitErr property to indicate failure reason
[4.10.0] - 2021-06-08
Added
- Extensions/DL FW & FPGA: Add FW/FPGA compatibility validation
- New MotionReason status: Controlled stop by input
Changed
- ControllerErrorInterpreter: Use random file name for errors list resource
- FwInfo: Add info item 26: "Additional Bi-Quad Filters bypassed"
- UserProgVarsAdapter: ignore white-spaces
[4.9.5] - 2021-05-13
Changed
- ControllerMessagesContainer: Use random file name for default messages list
- AACommServerAPI: Fix OpenEnded reported assembly version (not used ATM)
[4.9.4] - 2021-05-10
Fixed
- AGM400: increase reset time for proper reconnect
Added
- Extend FW/FPGA mismatch indication (Support for UnitStat bits[1,2])
Changed
- Increase ALoad command timeout from 2 sec to 10 sec
[4.9.3] - 2021-04-18
Changed
- Add DRV3-V6 controller type (8) to Product Types
- Connect: Do not fail if AIdentity[22] (RS232 cable type) doesn't exist
- FullCurrDirOperation property: Indicates that Current Direction operation is fully supported
[4.8.3] - 2021-03-24
Changed
- Add DRV3-V6 controller type (8) to Product Types
- Connect: Do not fail if AIdentity[22] (RS232 cable type) doesn't exist
[4.7.3] - 2021-02-05
Changed
- AACommCore must use explicit AACS file StartAACommServer() parameter
- Change LoggerUpload message from "Continuous" to "Regular"
- Don't allow RecUploadNext in a "bulk" message
- Add delay between "Discover connection" IP pings to not stress the connection. Introduced to fix a fault at AGD155 when user program is running
[4.7.2] - 2020-12-25
Fixed
- FPGA Download over RS-232
Changed
- AAComm Core: start AACS process improvement
- AAComm Core: default AACS: AACSCoreConsole on non-Win32 OS
[4.7.0] - 2020-12-07
Added
- AACommDownloadFW: Don't allow Ethernet FW DL over other than 172.1.1 subnets (non-AGM800 products only)
[4.6.1] - 2020-11-29
Added
- Support for recognition of User Program defined Global Variables and Task names (See CommAPI.Services.UserProgramInfo class for more details.)
- Add ControllerErrorInterpreter.GetErrorDescriptionByAACommEventArgs() function
Changed
- Support detection of multiple IP's set on a single NIC
[4.5.1] - 2020-11-09
Fixed
- AACommServer simulation mode 'file name' bug
Changed
- Add 'Bulk' error code 4002 - "unexpected reply format"
[4.5.0] - 2020-11-08
Added
- Improve FPGA Download error message
Changed
- Add ConnectResult.FailMacAddress error code
[4.4.0] - 2020-10-29
Added
- ControllerIdentityContainer.IsFPGAVersionEqualOrGreater function
[4.3.0] - 2020-10-18
Added
- Add "CNC_TrueJerk_Supported" property to ControllerIdentityContainer
- Add .NET 4.8, remove 4.62. target frameworks
- Add .Net Core RS-232 support (in dev!)
Changed
- Improve ConnectResult.CorruptAboutFile error code file logic
- Support "old" '/' FW info names: AGD200orAGC300 and AGD301/AGC301
- 'Bulk message': Add error code '4000' for empty, and '4001' for longer than 100 components
- 'Bulk message': Add protections for (bulk) unsupported keywords
[4.2.0] - 2020-09-17
Added
- Add "Group 10" to AllStatData: DInPortHigh and BoardTemp
- Add "HomingByLock_Supported" property to ControllerIdentityContainer
- Add "VectorMotion_Supported" property to ControllerIdentityContainer
[4.1.0] - 2020-09-10
Added
- Improve AAComm.StartAACommServer() capabilities to support .NET Core server
[4.0.0] - 2020-09-09
Added
- Resolve AAComm <=> AACommServer socket port number according to OS (instead of target framework) (results in interoperability between .NET and .NET Core assemblies on Windows)
- Add .NET 4.6.2 target framework
Changed
- Introduce Semantic Versioning 2.0.0 (https://semver.org/) to AAComm and AACommServer (no compilation breaking, or backwards incompatibility changes introduced)
[3.0.3.1] - 2020-09-06 (.101 for NUGET)
⚠️ Compilation-breaking AAComm changes
- Introduce new "AAComm.Shared.ProductTypes" class to manage product names and enums
- Products names were changed according to new names convention
- All previous ControllerIdentityContainer and AACommFwInfo references that relate to Product ID and name, must be redirected to ProductTypes
- CiG1-MAS01 => AGM400
- CiG1-MAS02 => AGM800
- AG100-DRV50 => AGD155
- AG300-CTL01 => AGD200/AGC300
- AG300-CTL02 => AGD301/AGC301
- Make property CommAPI.IsAACommServerRunning STATIC
- Make function CommAPI.StartAACommServer() STATIC
Changed
- Improve AAComm documentation (including 'Bulk' message type)
- Protection against LocalHost loopback "rogue" connect requests
Fixed
- AllStat handling time bug (MAS02)
Added
- Protect all synchronous API functions against multi-thread re-entry (mutual lock)
- Package and NUGET for .NET 4.0 and 4.6
- CNC_Feature_5_Supported ControllerIdentityContainer property
[3.0.2.8] - 2020-08-27
Fixed
- AllStat handling time bug
Added
- ControllerIdentityContainer.IsFaultyFPGA property, supported for FW >= 1.3.0.1-142
[3.0.2.7] - 2020-08-17 (DEPRECATED: AllStat handling time bug!!)
Fixed
- Protect AACommServer startup vs. corrupt user.config file
- AGD301/AGC301 product names
Added
- Backwards compatibility for old products names
[3.0.2.6] - 2020-08-16 (DEPRECATED: AllStat handling time bug!!)
Added
- AAComm.Extensions.AACommFwInfo: Rename products according to new definitions
- Services.COMTools.GetFTDI_COMs: add default "GetAllCOMPorts" parameter (no backwards compatibility issues)
Changed
- Update documentation
- CommAPI.DocsAppFolder to "PCSuite" (default folder for FW connection history)
[3.0.2.5] - 2020-08-05 (DEPRECATED: AllStat handling time bug!!)
Added
- FPGA download: rolling progress bar
Changed
- Remove Microsoft.Windows.Compatibility dependency
- Improve 'special' messages handling (implicit CTOR: need "BM:", explicit CTOR: NO "BM:")
[3.0.2.4] - 2020-08-04 (DEPRECATED)
Added
- FPGA download support
[3.0.2.3] - 2020-08-02
Changed
- StartAACommServer(string AACSFile = "") => AACS file NAME instead of path (defaults to "" -> look within working directory)
- Default timeout to IsCommServerRunning(int timeoutMS = 100)
- Rename TemwoutMs.ResetProceesType to TemwoutMs.Reset
- Rename MessageType.Reset to MessageType.ResetRequest
- Rename MessageType.RecUpload and TimeoutsMs.RecUploadTimeout to "Continuous"
- Remove MessageType.About and TimeoutsMs.AboutTimeout (now "Continuous" is used)
- Rename all TimeoutMs enums (remove "Timeout" from items names)
- Simplify AACommMessage class and CTORs - resolve messageType and timeout automatically!!
- Add SetTimeoutMs to AACommMessage
- AACommFwInfo.ValidateFwFiles() is no longer static
- New function: AACommFwInfo.VerifyCurrents()
- AACommDownloadFW.StartDownloadingSafe() now has a new parameter: "verifyCurrents" (does NOT work in BOOT mode, does NOT work with MAS02; FW DL will "fail" with an error, user must uncheck the checkbox, or pass "false" to the API function)