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 valid vbus_v profile.
  • MessageData.DynamicPtr is now a long (was int), so a 64-bit simulator's memory address fits. A consumer that reads the property into an int must widen it or use IsDynamicPtr, and a binary built against the old type must recompile. IsDynamicPtr is unchanged.
  • A message that a closing session drops before sending it, and a SendReceive that races the close, now reply PC Suite: ERR 4025 (the connection closed before the message was sent) instead of PC 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 new AACommEventArgs.IsSessionClosed.
  • The AssemblyVersion is pinned to 15.0.0.0 for 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 against 14.0.0.0 does not bind, and no binding redirect bridges the identity change. .NET 8/10 consumers see no difference.
  • IIdentityContainer gains PushEventQueueDepth. A consumer that implements the interface itself must add the member (CS0535); a consumer that only reads the container sees a new property.
  • ICommService gains both DownloadArray overloads. A consumer that implements the interface itself must add them (CS0535); a consumer that only calls it sees new members.
  • CommAPI.MessagesInBulk reports the bulk capacity, 50, on a ChannelType.Simulation connection, as it does on Ethernet. The firmware-simulator channel has always carried a MessageType.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 read MessagesInBulk == 1 as "not an Ethernet connection" now sees 50 on Simulation as well.
  • AllStatInterpreter.ParseAllStatData now takes the AllStat reply as AAComm delivers it — the resolved text a caller reads from AACommEventArgs.MsgReceived — and reads those values straight into AllStatData. 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 with AACommMessage.SkipUserUnits, which suppresses the User Units factor but not the IEEE 754 decode, so it never yields raw wire values.

Added

  • CiFpgaFeatures.FromReadback decodes the remote FPGA capability snapshot that AGM800 and AGM800-EC firmware publish in CiIdentity[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 as CiFpgaFeatures.Unknown
  • Two ConnectResult codes name why a managed Simulation connection could not launch its firmware_sim: SimExeNotFound (51), when ConnectionData.SimExePath is empty or names no file on the AACommServer host, and SimStarterParMissing (52), when no starter.par is next to the simulator executable, as with a firmware_sim built from source rather than packaged. Every other launch failure is still SimLaunchFailed (49), with its cause in the AACommServer log. A client older than the server gets FailOpenCommHandler for 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.
  • EventTableStatusReader provides 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.Configure explicitly delivers a complete selected-motor agito.par and 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.TryGet exposes verified nominal simulator supplies by product identity. Unknown products have no implicit supply; IsValid checks an explicit finite positive voltage.
  • PassiveCollectionRequest.EncoderSelections opts 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.Commissioning is a portable result contract for discovery and commissioning passes: CommissioningReport records 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, and CommissioningValidator.Validate rejects 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 stored ComtStatus presented 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 a CommissioningReport. Over an ICommService it records the identity the shared IIdentityContainer exposes (axis and motor-axis counts, capability flags, current-loop sample rate, recording limits), and for each caller-supplied port/axis/encoder mapping the configured EncType and the stored ComtStatus[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. Collect appends one attempt per call, so a repeat keeps earlier failures beside later successes. Nothing here changes aacomm.rig-manifest/1.
  • AACommEventArgs.IsSessionClosed is true for a reply that ended because its session closed: ERR Sync: Session terminated for a message already sent, PC Suite: ERR 4025 for one never sent. A consumer can leave those to the one OnCommsError report 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, whose Plan.Added and Plan.Inits name the rows it adds), the dependency list (EncSinCosHWEn, EncDir, VEncSrc, VEncOn, AuxEncDir, VEncFact, VEncFactDen), ReadCurrentModes for the controller's current LockEventMode where the rows carry none, and Mode2Supported, read from LockEventMode'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 LockEventMode 2 (when it isn't 2 yet, the drive first turns LockEn/EventOn off, which also drops an armed touch probe), and, on an axis with an ancillary counter, waits for LockEventStat 1 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 8 LockEventInit attempts, the axis faults with 0x603F = 0x7300. LockEventWriteOrder.EnableForcesMode2(productType, lockEventMode) (an AGM800-EC whose firmware declares LockEventMode up to 2) and EcEnableLockOffsetFault (0x7300) expose this. That forcing happens only at the next enable, so Order(rows, isSupported, currentMode) decides LockEventInit from the mode the axis is in when it re-arms (the file's LockEventMode row, else the controller's current mode from ReadCurrentModes), 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.GetDescription and the allowed range in a controller error message use it, so their text is unchanged.
  • AACommRemoteFpgaInfo.AllowAga155CurrentRatingMismatch is a default-false engineering option for AGA155 2A06 ↔ 2A10 FPGA comparisons (identity current codes 6 ↔ 10). All other target checks remain; AllowForce keeps its separate unknown-identity behavior and cannot bypass known mismatches.
  • The package now ships lib/netstandard2.0 alongside net48, net8.0 and net10.0. A project targeting netstandard2.0 can reference Agito.AAComm directly 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.DownloadArray downloads a contiguous array from typed double or exact long values and returns an ArrayDownloadResult with 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 as ArrayDownloadStatus.Rejected with 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, which FirstFailedIndex then names. A parameter the table refuses outright still throws ArgumentException. An element's error, on either transport, reads as one sentence in ErrorMessage: its index, the reply (ERR 14, or PC Suite: ERR 14 when refused before sending), the error's description and, for a value out of range, the keyword's allowed range.
  • ArrayDownloadResult.StreamError reports the controller's array-stream protocol error as the ArrayStreamError enum (bad version, flags, width, count or index range; checksum mismatch; trailing data; unexpected reply), and ErrorMessage names it. A checksum mismatch is reported only after the controller has applied every element, so that result carries a full AppliedCount for a write whose integrity check failed — read the array back rather than trusting the count.
  • CommAPI.SupportsArrayStreamDownload reports whether the connected controller and AACommServer both advertise stream transport. The controller advertises it in Identity[62] bit 0x80; 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 = 2 now reaches CommAPI.OnPushMessageReceived as an AACommPushMsgPushEvents — Missed, FreeRows, and Events, a list of PushEventRecord (Type, Axis, TimeSec, TimeTick, Data1..Data5; PushEventRecord.NoAxis marks a controller-wide event). PushSources.PushEvents names the source. A packet whose length does not match its record count, or whose VERSION_DATA is not 1, arrives as an AACommPushMsgError, as a malformed printf push does. Push events are binary-IPC-only: the bundled AACommServer never forwards a SOURCE = 2 push to a legacy string-protocol client, whose framing cannot carry a binary payload safely, so a consumer whose CommAPI.ServerCompat is LegacyAscii does 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_Supported is Identity[62] bit 0x40 (the firmware's IDENTITY_NEW_FEATURES_2_PUSH_EVENTS), reachable through IIdentityContainer.Features_02; IIdentityContainer.PushEventQueueDepth is 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 of IIdentityContainer must add the PushEventQueueDepth member.
  • 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_cb deliver the push message's raw DATA payload as (const uint8_t* data, size_t len, void* user_data) — exactly the bytes AACommPushMsgBase.BinaryData holds, the binary IPC dialect's trailing >\r terminator included — so a SOURCE = 2 push-events packet — mostly zero bytes — reaches a C/C++ consumer intact, and aacommpy exposes it as Client.on_push_bytes. The existing aacomm_push_cb is unchanged; the addition is additive and the ABI generation stays 1.
  • A new product type, AGM800-EC: ProductTypes.AGM800_EC_ID (16) and ProductTypes.AGM800_EC ("AGM800-EC"). It is the first product in both families: ProductTypes.IsAGM800 and ProductTypes.IsEtherCAT both return true for it, and so do ConnectionData.IsAGM800 and ConnectionData.IsEC on a connection to one. IdToName / NameToId resolve it. Its firmware image is its own, not the AGM800 one: AACommFwInfo.ValidateFwFiles refuses an AGM400 / AGM800 / Pulse-Sync .bin on an AGM800-EC connection and an AGM800-EC .bin on those — by the image's identity markers (with or without a .ver), and by the .ver Type: line, which must start with AGM800-EC on an AGM800-EC connection and must not on the others — and AACommFwInfo.ProductTypeStr reports "AGM800-EC" for its image. ControllerIdentityContainer.DefaultSamplingRate returns 16000 for it, the EtherCAT current-loop rate. AACommDownloadFW flashes an AGM800-EC's EtherCAT SII EEPROM when you pass AACommFwInfo.Targets.EC_ESI: the SII .bin goes down as Intel-HEX lines after DownloadESI, over Ethernet (or the firmware-sim ChannelType.Simulation channel), 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 .bin files, and AAComm does not recognize an SII image by its content, so with the default Legacy target an SII image is flashed as firmware. EC_ESI refuses a .hex, a .bin larger than the 16 KiB an ESI image can address (the AGM800-H1G's SII is 2 KB; a firmware image is megabytes), and a .bin carrying 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 .ver with FPGA Size: 16K is 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 managed Simulation connection keeps its memory in. AACommServer creates it when missing, starts firmware_sim with 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 to agito.par and its user program to agito.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 with ConnectResult.SimLaunchFailed.
  • ControllerIdentityVersions.BuildCiRemoteVersionString(ciIdentity4, ciIdentity15To19) returns a Ci remote unit's FPGA version text from CiIdentity[4] and CiIdentity[15-19]. A unit whose CiIdentity[4] is 4444 gets the five words formatted by BuildVersionString (so owner 0 with sub-version 2 reads 5.5.0-rc2, and a null or short word list gives an empty string); any other unit gets the old A.BBB text, always with a dot, whatever the machine's culture. aacomm.cli reads a remote unit's FPGA version through it.
  • ControllerVersionWord is a public, comparable firmware or FPGA version word (Major.Minor.Patch plus owner and sub-version), built with FromWords from the five identity words or with TryParse from X.Y.Z, X.Y.Z-rcN, X.Y.Z.O or X.Y.Z.O-S. A release candidate orders below its final and a development build above it. ControllerIdentityVersions.FirmwareVersionWord returns 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.Simulation connection starts its firmware_sim child with AGITO_DRAW_GCC set to %LOCALAPPDATA%\PCSuite\HostGcc\bin\gcc.exe when 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.Configure for 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 calls CommAPI.Connect on the same channel during the download is refused with ConnectResult.ChannelInExclusiveCommsMode, and every message from another client (one connected with ConnectSilent included) is answered CHANNEL_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 SendReceive whose timeout closes the connection now also raises CommAPI.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 returns ERR Sync: Timeout.
  • A ChannelType.Simulation connection starts its firmware_sim child with AGITO_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, so AGITO_CI_REAL_AMP=0 keeps 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 reporting MnemonicNotFound, MessagesContainer.AllMnemonics counts 21 rows, and a .par upload 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 Connect instead of failing with CorruptAboutFile, which now means the controller's own About data is inconsistent.
  • ProductTypes.NameToId resolves either half of a paired product's name: AGD301 and AGC301 return AGD301_AGC301_ID (11), and AGD200 and AGC300 return AGD200_AGC300_ID (5), where they returned -1. AGD301 and AGD200 are how the firmware catalogue and PCSuite's Simulations\<product>\ folders name those products, so those names now resolve like every other product's. IdToName is unchanged and still reports AGD301orAGC301 / 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 reads Firmware download is not supported over CAN. Download firmware over Ethernet or RS-232 (serial).
  • An exception thrown by an OnProcessEnded handler of AACommDownloadFPGAEtherCat at the end of a download now propagates to the caller of StartDownloading, which runs the whole download on the calling thread; the handler is called once.
  • ControllerIdentityVersions.BuildVersionString, and through it FirmwareVersionStr, FPGAVersionStr and CMFirmwareVersionStr, shows a release candidate (owner 0, sub-version N greater than 0) as X.Y.Z-rcN instead of X.Y.Z. AACommFwInfo.AACommVersionFromString follows: a .ver line 5.5.0.0-1 resolves to 5.5.0-rc1. A user program compiled on a release candidate carries that form in its FirmwareVersion: line, so it loads on that controller and not on its final; one compiled earlier carries X.Y.Z and must be recompiled. The About cache folder of a release candidate is named by the new form. FirmwareSystemVersion, IsFPGAVersionEqualOrGreater and 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 151 after 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 OnError without a successful upload callback.
  • The ERR 412 description writes the PDSubType input type as C0/C1 (zero), as the firmware names it, instead of CO/C1.
  • Connecting to a 64-bit Linux firmware simulator no longer fails with ConnectResult.CorruptAboutFile about half the time. The About table's DynamicPtr is a memory address, and when it landed above Int32.MaxValue its 32-bit parse threw, so the table load failed.
  • A managed Simulation connection no longer shares its memory with every other connection that runs the same firmware_sim. AACommServer starts the simulation with AGITO_FLASH_FILE pointing into its working directory, the SimMemoryFolder when 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.GetErrorCodeFromErrorString reads the code from the multi-line text GetFormattedErrorText builds ("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.OnReplyReceived callback 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 AACommServerModel re-armed each socket receive from inside its own callback.
  • A CommAPI.SendReceive that races a Disconnect returns the PC Suite: ERR 4020 reply, like any SendReceive on a disconnected channel. It used to throw ObjectDisposedException when 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.101 once 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 flash reported did not complete within 300 s). The status line during that wait reads Reconnecting 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 address 0xC000, so 16 KiB is all that can be addressed.
  • A user-program download started from inside AACommDownloadUP.OnProcessEnded runs once, not twice. AACommExclusiveCommsManager kept one completion callback per instance, so a Start issued from that handler while the previous download's End was still in flight completed whichever callback was stored last, and the ending download's Stop reply started the next one a second time. Each Start and End now completes its own call's callback. Chained downloads — an N-iteration loop driven from OnProcessEnded — are the affected case; the synchronous form (a null onDone) 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>.xml in the product/version folder), so connecting to one no longer discards the other's, and a drive's PeakCL / ContCL assignments are no longer range-checked against another rating's limits. The first Connect to 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 next Connect.
  • 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 Connect never 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. XmlHandler opens its file with FileShare.Delete, so replacing a file another process is reading no longer fails.
  • An About table whose MinValue, MaxValue or DefaultValue is not a finite number is refused on every target. The -nan a 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; NaN and Infinity loaded on all three. Such a table now fails to load on every target, Connect reports CorruptAboutFile, and the load error names the parameter and the field.
  • A firmware .hex written on Linux or macOS (LF record endings, as hex2000 emits 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 — ATorqueConst read 9.9999997171806854E-10, the 17-digit form of the 1e-9 the controller actually holds — which lands fractionally below the keyword's declared minimum, so every such line came back AssignedValueOutOfRange (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 — .par files, 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 .par file 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 through OnError instead of never completing. A bulk stops at the failing command, so its ERR nnn is 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 neither OnSendRecordingParams nor OnError fired and AACommDataRecManagerSync.SendRecordingParams waited 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 AACommServerNotRunning until it started a new one. The socket now survives the eviction, and a Close from 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 older AACommServer.exe still sees the old exit.
  • ParamAbout reports the bounds of a SCALING parameter (InUserUnits 4 — StuckTime, InTargetTime, RptWait, MaxVBusTime, DualStuckTime and 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 with MessageData.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 ms StuckTime default came back as 67108.8671875). Live values were and are unaffected: the controller converts those itself.
  • A printf push (SOURCE = 1) whose VERSION_DATA is not 1 now reaches CommAPI.OnPushMessageReceived as an AACommPushMsgError carrying a NotSupportedException, as a push-events packet at an unknown VERSION_DATA already 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 valid AACommPushMsgUP carrying 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.OnPushMessageReceived as an AACommPushMsgError, or as a truncated push followed by one. Every packet now arrives whole and on its own, and the string-client filter for SOURCE = 2 applies per packet. A push the server does not frame — anything but printf and push events at VERSION_DATA 1 — 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 SendReceive is 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 see PC Suite: ERR 4020 on 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. OnCommsError is not raised for a FATAL whose session is already gone.
  • A .ver version line written in the controller's own development form, 5.4.4.12-1 (ALPHA), is accepted by AACommFwInfo.AACommVersionFromString and by the FPGA-vs-FW restraint check (which compares its dotted head, 5.4.4.12), instead of throwing on the suffix. 5.4.4 and 5.4.4.12-1 resolve 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.39 at 0.01 mm/count is 24438.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 a ParamType 0 / 3 parameter in counts before rendering, matching CanMessageParser, 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.EthernetASCII message 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, and AACommMessage.SkipUserUnits suppresses 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; an EthernetASCII body 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 set SkipUserUnits.
  • The API-only User Units config gate now fires on every transport too: a raw assignment to *UnitFct / *UnitUnt[] is rejected with error 4023 over RS-232, over a Simulator connection and on a single-command EthernetASCII message, 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 reports 4023 rather than a value error. The AACommMessage.AllowUserUnitsConfig bypass is unchanged.
  • CommAPI.OnUserUnitsEnabledChanged and the ack-authoritative enabled cache now also follow a UserUnitsEn write sent as a MessageType.EthernetASCII message, and one written with spaces around the assignment (AUserUnitsEn =1); both were missed, so toggling units that way left CommAPI.GetUserUnitsEnabled reporting the old state and no event raised.
  • Recording-parameter download no longer risks scaling the trigger value twice. TriggerContainer de-scales RecTrigVal / RecTrigValMax when it is built, and AACommDataRecManager now 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 — sync SendReceive or async Send of MessageType.ResetRequest — now re-reads it, so a controller that comes back on a different *UnitFct or UserUnitsEn is 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 sync SendReceive returns, 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.01 went to the controller as 0,01 and was read there as 1 — a hundredfold-wrong value accepted with no error — and ParamAbout bounds in the controller's own About data threw FormatException, failing the connect. Reply text changes shape on those locales: a scaled reply, an AllStat value and a ParamAbout bound now read 0.01, not 0,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. FirmwareVersionStr and FPGAVersionStr follow, so a legacy controller now reports 4.123 rather than 4,123 — a .cup user-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 with CultureInfo.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 default 172.1.1.101 — but an EtherCAT drive keeps its own address in FPGA-download mode, so nothing answered there and the download failed with Failed reconnecting in boot mode while 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 raises OnProgress after the OnProcessEnded that 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 after OnProcessEnded, and a download started on the same instance from OnProcessEnded kept receiving that ramp mixed into its own progress. Each download now stops its own finalize progress, and waits for an OnProgress call still running to return, before it raises OnProcessEnded.
  • 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.0 AACommServer.exe still loses the race.
  • Reported parameter bounds now match the units the value itself comes back in. ParamAbout scaled its min/max/default by the factor of ParamAbout — 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-wide CommAPI.SkipUserUnits is set.
  • AACommMessage.SkipUserUnits now really covers AAllStat replies, as documented. The AllStat reply path re-read the API-wide CommAPI.SkipUserUnits instead, 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 through APos but in counts through AAllStat; 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 — an ERR nn reply 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 default 172.1.1.101 regardless 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.IsParsedOK now reports a failed AllStat parse instead of hiding it. A field that is not a number — a controller ERR nn reply above all — was read with a discarded TryParse and stored as 0 while IsParsedOK stayed true, so a bad reply looked like a wall of valid zero readings; it now sets IsParsedOK to false. Fields are read in the invariant culture, matching the way the reply is written.
  • AACommDownloadUP.DownloadUserProgram reports a rejected request through OnProcessEnded instead of never completing. A call that is refused before the download starts — a disconnected or null CommAPI, 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.StartDownloading no longer disconnects a CommAPI the refused call did not connect. Every refusal — a null argument, 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 through OnProcessEnded. A failure after this call's own Connect succeeded 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, AACommDownloadFPGAEtherCat and AACommDownloadFW disconnect 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 / .hex file, 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 through OnProcessEnded and left the CommAPI connected, 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 no CommAPI: that connection is the caller's own.
  • AACommDownloadFW.StartDownloadingSafe refuses 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, reads AIdentity[10] and AIdentity[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.StartDownloading refuses a null api or connData through OnProcessEnded (Invalid input argument: CommAPI, Invalid input argument: ConnectionData), the texts StartDownloadingSafe reports for the same arguments, without the Error: prefix its catch-all adds. A null connData threw a NullReferenceException out of the call, and a null api was accepted: the instance marked itself busy, started its worker and reported the resulting NullReferenceException message as the download's outcome.
  • An AACommDownloadUP.DownloadUserProgram call 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 through OnProcessEnded. 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.StartDownloading refuses a second call the same way (Remote FPGA Download is already in progress!), where it disconnected the CommAPI of the flash in progress.
  • An AACommDataRecManager call 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 the CommAPI the 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 refused StartRecording, ForceTrigger or StopRecording also 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 a null api passed to those three threw a NullReferenceException out of the call, since the resolver was built from it before the argument was checked; all three now report Invalid input argument: API is null through OnError, as their documentation already promised. The refusal now only reports through OnError. 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.CancelUpload ends 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 sequential UploadRecDataNonblocking upload: a WaitForRecToEnd kept polling RecStat and a blocking UploadRecData stayed in flight, their caller told nothing, and the next call — accepted because the flag was clear — took over the same instance's CommAPI, parameter resolver and completion callback, so the abandoned operation's reply invoked the later caller's. A cancel now reports Operation cancelled through OnError, which is how the running operation's caller learns it ended; it cancels a sequential upload and stops the RecStat poll between queries; and a cancel with nothing running does nothing, where it used to clear an already-clear flag. A caller that expected CancelUpload to be silent now receives one OnError for the operation it cancelled. A request already on the wire is not recalled - a command, a parameter batch, a blocking upload or a RecStat query 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 Simulator channel). The group/factor/units configuration read and the sanctioned ControllerMessagesContainer.TrySetUnitsSync write batched their commands into a MessageType.BulkMessage, which AAComm refuses outside Ethernet with PC Suite: ERR 4004 — so the read failed at connect, UserUnitsSupported stayed false for 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. TrySetUnitsSync writes over RS-232 as it does over Ethernet; over CAN it now reports ErrorWritingData when the controller declares *UnitFct as a double, because a 64-bit assignment does not fit a CAN frame (error 304, 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 and Simulation — 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 through dotnet) spawned on Windows now has its own hidden console, so a Ctrl+C in the starter's console no longer reaches it; CloseAACommServer still stops it.
  • Two calls that arrive together on one download engine no longer both take the download. AACommDownloadUP, AACommDownloadFW, AACommDownloadFPGA, AACommDownloadFPGAEtherCat and AACommDownloadFPGARemote read 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 - the CommAPI in 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 through OnProcessEnded — 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.StartDownloadingSafe holds 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 AACommDataRecManager no 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 the CommAPI, 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, UploadRecData and UploadRecDataNonblocking. 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 gets Async operation in progress! through OnError, as a call refused for an already-running operation does. A caller that starts one operation at a time sees no change.
  • The AACommDownloadFPGAEtherCat.OnProgress documentation now states the scale the event reports, percent (0 to 100); it said fractions of 1000. The values the event reports are unchanged.
  • AACommDownloadFPGARemote and AACommDownloadFPGAEtherCat refuse 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. OnProcessEnded reports Invalid input argument: FPGA image length must be a non-zero multiple of 4 bytes (got <length>), and nothing is sent to the controller. AACommDownloadFPGAEtherCat refuses a null image the same way, reporting Invalid 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.VariantStr shows the variant from the .ver file; it showed NA whenever the amplifier version was NA. VariantStr, AmpVersionStr and CommVersionStr print their value as upper-case hex behind 0x, as it is written in the .ver file; they printed the decimal digits, so 0x1D01 showed as 0x7425. A value of 0 shows NA.
  • The binary continuous-integer replies (AxSt; and V1AxSt; 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 a 0x3E inside 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 AMotorType query 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); a SimMotorProfile with [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.DoesIpv4SubnetMatch and ARPTools.EthernetLookupResults.IsSubnetSetOnAny match a subnet on whole octets. They compared a bare text prefix, so subnet 172.1.1 also matched an adapter at 172.1.10.x, 172.1.11.x or 172.1.100.x to 172.1.199.x. An Ethernet connect to a 172.1.1.x controller from a PC whose adapters sit only on such an address now fails with EthernetInterfaceMismatch, as it does for any other subnet, instead of going on to ping the controller from no adapter on its subnet.
  • A throwing OnProcessEnded handler no longer gets a second, failure report from AACommDownloadFPGA, AACommDownloadFPGARemote and AACommDownloadFPGAEtherCat: the handler is called once, with the true outcome of the download, and the connection is disconnected and the instance released once.
  • AACommDownloadFPGARemote.StartDownloading with a null fpgaInfo reports Invalid input argument: AACommRemoteFpgaInfo through OnProcessEnded instead of the runtime's NullReferenceException text.
  • AACommRemoteFpgaInfo(string rpdFile) finds the .ver beside a relative .rpd path such as amp.rpd, which it refused, and on Linux and macOS finds it for any path, which it always refused.
  • ARPTools.EthernetLookupResults.GetSubnetMismatchString on an empty lookup returns its text with NA for the adapter and its address instead of throwing ArgumentOutOfRangeException.
  • 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.
  • AACommFwInfo refuses each firmware-file mismatch with one text, whether its constructor or ValidateFwFiles refuses it: a non-.bin file for an AGM800 reads Only '.bin' files can be flashed to AGM800 products, and a .bin for a product that takes .hex files reads <product> can be flashed with '.hex' files only. ValidateFwFiles worded both differently.
  • AACommDownloadFPGA and AACommDownloadFPGAEtherCat raise OnProcessEnded only 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. AACommDownloadFPGA no longer waits a fixed 6 seconds instead

Deprecated

  • ConnectResult.FwInfoMismatch — Connect no longer returns it.

[14.0.0] - 2026-08-26

⚠️ Breaking

  • AAComm.dll is now strong-named (PublicKeyToken=043a10799a9b732b), so strong-named consumers can reference it — previously they compiled with warning CS8002 and failed at load with FileLoadException 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.x AAComm, PublicKeyToken=null reference does not bind to the signed assembly, and no binding redirect bridges an identity change); .NET 8/10 consumers see no difference. The AssemblyVersion is pinned to 14.0.0.0 for 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 spell PublicKeyToken=null (e.g. BinaryFormatter payloads, Type.GetType full-name strings) stop resolving — simple names (AAComm.CommAPI, AAComm) are unaffected. AACommServerCore.dll (bundled with the server) is signed too; the AACommServer.exe process itself is not — see docs/strong-naming.md in the repo for the assembly-by-assembly map and the reason.
  • The C++/CLI AACommNative wrapper has been retired (with its AACommNativeTester/AACommNativeTesterCLI projects). The NuGet no longer ships the lib/cppCLI/net48/ slot (IAACommNative.h, AACommNative.lib/.dll, shared sources). Native (C/C++) consumers move to the new C-ABI SDK — project AACommNative (reclaiming the name) → aacomm.h + aacomm.dll/libaacomm.so/libaacomm.dylib.
  • OnResetProgress replaced by two events — OnLongOperationStarted(LongOperationType) and OnLongOperationEnded(LongOperationType, int errorCode) — now firing for Reset, Save, Load, SaveUser, and EventCorrect. The ended edge's errorCode is 0 on 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 with aacomm_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.
  • CommAPI now 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 by CommAPI.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 now OnChannelClosed(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.
  • AACommAxisHelper is merged into the new AAComm.Services.AACommMnemonicHelper (see Added) and removed. AAxis, PAxis, ToAxis, ToAxisIdx(char) and ToAxisChar are 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) (use ToAxis(idx).ToString()) and the ToAxisIdx(string) overload (use s.ToAxisChar().ToAxisIdx() — makes the first-char truncation explicit).
  • AACommMessage.StartsWithPAxis(string) removed — use msg.ToAxisChar() == AACommMnemonicHelper.PAxis (identical semantics, including null/empty → false).
  • LoggerDataContainer / ScopeDataContainer constructors take a new required char[] parameter (paramAxes / signalAxes — the per-vector axis letters, needed to resolve User-Units scaling per axis) in addition to the CommAPI→ICommService narrowing. Existing call sites must supply the axis array.
  • TriggerContainer construction 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 on MessageDataValueExtensions like the others (see Added).
  • The native C-ABI SDK (aacomm.*) adopts a uniform status convention: every aacomm_* function now returns int32_t aacomm_status (AACOMM_OK=0 / AACOMM_NULL_ARG=-1 / AACOMM_BAD_HANDLE=-2 / AACOMM_EXCEPTION=-3), and domain results (the ConnectResult, the connected flag, reply/error byte sizes) move to out_* parameters. So aacomm_create, aacomm_destroy, aacomm_is_connected, aacomm_last_error, aacomm_start_server, aacomm_test_connection, aacomm_connect/_silent, aacomm_send_receive, and aacomm_last_reply all change signature (e.g. aacomm_create(aacomm_handle* out), aacomm_is_connected(h, int32_t* out_connected)) — this removes the old ambiguity where aacomm_is_connected returned 0 for both "not connected" and a bad handle. Handles now come from a table that rejects forged/stale/double-freed handles with AACOMM_BAD_HANDLE instead of risking undefined behavior. Adds aacomm_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 shipped aacomm.h.
  • XmlHandler.SaveXml() is removed in favour of TrySaveXml(out Exception) (see Added). It returned nothing and reported a failed write only through a Debug.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 the UserId ↔ client-endpoint line AACommServer now writes on each Open. Public surface: AACommDebugLog.Sink (an Action<string,string> bridge so hosts can forward the lines into their own log) and AACommDebugLog.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/*UnitUnt parameter families, grouped per UUGroups: Pos/PosAux/PosPD/Vel/VelAux/VelPD/Acc/Frc) and the per-axis UserUnitsEn enables; from then on values of parameters in an enabled group are scaled by the axis factor in both directions. The new AAComm.Services.UserUnits namespace 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), and SkipUserUnits (consumer-wide bypass, reset to false at Connect).
    • AACommMessage: SkipUserUnits (per-message bypass) and AllowUserUnitsConfig (sanctions a raw write of the units-config parameters themselves — without it such writes are rejected with ERR 4023; set units through TrySetUnitsSync instead), plus constructor overloads taking skipUserUnits.
    • ControllerMessagesContainer: UserUnitsSupported, MaxUserUnitNameLen(UUGroups), TryGetUserUnitAxis/TryGetUserUnitsGroup (read-only lookups, also on IMessagesContainer — implementors must add them), and TrySetUnitsSync(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 a UserUnitsData struct (Min/Max/Default scaled for the axis + Factor/Units), and GetDescription(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.
    • ICommService exposes GetUserUnitsEnabled(char). The native C-ABI SDK mirrors the per-axis enable/query as aacomm_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 from AACommAxisHelper), 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).
  • MessageData value accessors (MinValueStr/MaxValueStr/DefaultValueStr, MinValueInt/MaxValueInt/DefaultValueInt, MinValueLong/MaxValueLong/DefaultValueLong) — extension methods on MessageDataValueExtensions formatting 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.
  • IIdentityContainer exposes Features_02, CurrentLoopSampleRate, MaxNumOfRecVectors, MaxDataRecordingPoints, NumOfMotorAxes — needed by Core consumers of the recording pipeline and the User Units API. IMessagesContainer exposes Contains(string), CanCodeToData, IsInitialized; ICommService exposes MessagesInBulk. 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) and RecParamsResolver.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 Send and SendReceive run 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 new CHANNEL_EVENT reply kind — sent only to clients that declare support on Open — notifies a passive client when its channel dies; it surfaces via OnCommsError.
  • The bundled AACommServer packet sniffer now captures every connected controller simultaneously — one capture session, ring buffer, and snapshot tree per controller endpoint (under per-NIC ip_port subfolders) — 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.
  • CommAPI and AACommServerModel now implement IDisposable — Dispose() delegates to Disconnect() / Shutdown() respectively, so consumers can use using blocks. CommAPI.Dispose() does not shut down the shared AACommServer process (call CloseAACommServer() 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.log under %LocalAppData%\PCSuite\AACommServer_DebugLogs. Available on both the binary and legacy ASCII wire protocols; against an older AACommServer the request is ignored.
  • CommAPI.Protocol (new ProtocolMode enum: Ascii/Binary) — read-only report of the wire protocol negotiated with AACommServer (valid after Connect).
  • CommAPI.ServerCompat (new ServerCompat enum: Unknown/Current/LegacyBinary/LegacyAscii) — classification of the AACommServer the client attached to, valid after Connect. 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-part major.minor.patch form.
  • 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. .NETCoreApp consumers 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 local firmware_sim process over loopback and bridges it, presenting a real moving plant. Replaces the legacy ChannelType.Simulator for end-to-end simulation — that param-echo channel's server backend is removed; the Simulator enum 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 managed Simulation channel (the firmware_sim executable to launch plus its catalog identity and motor profile).
  • ConnectResultInterpreter.Interpret(ConnectResult) now returns localized text (en-US / zh-CN) per LocaleProvider.Instance.Locale.
  • XmlHandler.TrySaveXml(out Exception) — saves the document, returning false with the failure that stopped the final attempt when it could not be written after ten retries. Replaces SaveXml() (see Breaking).
  • Four new ConnectResult codes: CANDeviceNotConnected (48) — a CAN Connect found the Kvaser driver installed but no physical Kvaser interface connected (canlib enumerates only virtual channels; distinct from CANLibMissing, the driver itself absent — the native C-ABI SDK exposes it as AACOMM_RESULT_CANDEVICE_NOT_CONNECTED); SimLaunchFailed (49) / SimNoPort (50) — the managed Simulation channel's firmware_sim process could not be launched / did not announce its loopback port; and ServerNotBinaryCapable (47) — an internal protocol-negotiation signal (a binary Open that got no handshake ack), never returned from a public Connect.

Fixed

  • Connect and TestConnection no 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 produce AACommServerNotRunning against a server that was listening, or FailControllerType against 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 own TimeoutMs when 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 above TimeoutMs + 100 before implicating the link, because the server itself polices controller silence at TimeoutMs and 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, and TimeoutMs still 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.
  • AACommFwInfo now 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 last 0x0014 segment record; those images fill that segment with code ahead of the block, so DataValid came back false, VerifyCurrents threw "selected FW file doesn't contain 'currents' information", and ValidateFwFiles skipped 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 \r or > is no longer corrupted on the ASCII client path — the classifier used to strip all trailing framing characters instead of the single >\r framing 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 SendARP used 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 as ERR 4024 instead of stalling until the channel times out and drops the connection. The server can tell it apart from a legitimate OK at 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 .par file 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 Message and Close requests 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/stale Close — could latch exclusive comms on another client's channel (locking its members out and blocking ShutDownForce) or close the channel under its remaining users.
  • UserProgVarsAdapter.AdaptUserProgMessage no 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 net48 AACommServer.exe left 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 InstallPath value reports the missing-installation result instead of an exception result.
  • 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 Close still 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 SendReceive whose timeout expires while the message is still queued behind a slower call no longer disconnects the whole API — it returns ERR Sync: Timeout and the message is never sent (previously a short-timeout call queued behind e.g. a 20 s MotorOn deterministically tore down the connection). The wire-timeout clock now starts when the message is dispatched, matching the server-enforced TimeoutMs; a timeout after the message reached the wire still disconnects, as before.
  • An asynchronous Send callback now fires exactly once, even when the connection tears down (disconnect, controller-link FATAL) before the reply arrives — the in-flight message's callback gets ERR Sync: Session terminated and still-queued messages get PC Suite: ERR 4020, both with IsError set. 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-id on Linux), removing AAComm's last System.Management dependency.
  • A failed or partial connection — or a channel FATAL racing an explicit Disconnect — is now reported as a proper ConnectResult instead of being masked by a NullReferenceException thrown 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/TestConnection is 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 ScalingFact values 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 SocketErrorCode instead 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 ParamAbout answer) 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.exe as their bundled server and crashed it on launch (a System.Runtime version 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 missing Google.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 with RequiredConnectionUnavailable before 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 new ConnectResult.CANDeviceNotConnected (see Added).

Changed

  • StartAACommServer now 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 for AACommServerNative — 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 reports AACS_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 .exe suffix, so the extensionless NativeAOT host on Linux/macOS was handed to dotnet as though it were an assembly.
  • The release asset published to the public release page is now the Agito.AAComm.<version>.nupkg itself — the same package nuget.org serves. The AACommPackage_v.<version>.zip bundle 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 separate agito-aacomm-native-sdk-<version>.zip, which — with Agito.AAComm.Native.<version>.nupkg — now ships on its own public Software/AAComm-Native release line beside this one.
  • The bundled net48 AACommServer closure now lands in an aacommserver\ subfolder of the consumer's output directory instead of flat at its root — the server's Google.Protobuf.dll and 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; explicit StartAACommServer(path) calls and existing flat layouts keep working.
  • A spawned (headless) net48 AACommServer that 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 the Documents folder). Where Documents is 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 (project AACommServerAPI → AACommServerCore). No public API change — consumers reference Agito.AAComm; only a consumer copying the engine DLL by name is affected.
  • NuGet AACommServer sidecar selection is TFM-based (it was OS-based as of 12.1.1): .NETFramework consumers get the net48 WPF AACommServer.exe; .NETCoreApp (net8.0) consumers now get the self-contained NativeAOT AACommServerNative host (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 own Google.Protobuf could 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 net8 AACommServerNetConsole + AACommServerNetAvalonia hosts are gone.
  • FloatHelper.ResolveFromCCC, CanDescriptionBuilder.GetParameterFromCanDescription, CanDescriptionBuilder.GetCanDescriptionFromParameter: dataContainer parameter type narrowed from ControllerMessagesContainer to IMessagesContainer. Source-compatible — the concrete still satisfies the interface.
  • DataRecParams.ParamsToRecParamArray, DataRecParams.CalculateActualGapValue, DataRecParams.CalculateLengthValue: api parameter type narrowed from CommAPI to ICommService. Source-compatible.
  • AACommDataRecManager, AACommDataRecManagerSync, RecParamsResolver constructors and methods: api parameter type narrowed from CommAPI to ICommService. Source-compatible. (TriggerContainer, LoggerDataContainer, ScopeDataContainer get the same narrowing but their constructors also change shape — see Breaking.)
  • ControllerMessagesContainer.CanCodeToData declared type narrowed from Dictionary<int, MessageData> to IReadOnlyDictionary<int, MessageData>, matching the new IMessagesContainer member. 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.IsError are 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.IsError are unaffected.

Added

  • New CommAPI.IsResetting property — true during the post-Reset recovery window.
  • SendReceive returns "ERR Sync: Session terminated" on session death (distinct from "Timeout").
  • New OnUserProgramVarsUpdated event — 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).
  • IsConnected now correctly becomes false when AACommServer closes cleanly (was silently stale until next Send failed).
  • OnCommsError no 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 receive AACommServerNetConsole

[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)