Threaded version 0.7
This commit is contained in:
+146
-176
@@ -1,220 +1,190 @@
|
||||
# atMb usage and behavior
|
||||
# atMb 0.4 user guide
|
||||
|
||||
This guide describes the last supplied working IEC export, including `ClientTCP`.
|
||||
The current repository stores the working file at `library/CmpAtomModbus.library`.
|
||||
Use that file in CODESYS. No IEC or C implementation is changed by repository cleanup.
|
||||
The binary library format was not decoded to reverify its member declarations;
|
||||
when changing IEC code, update this guide against a fresh CODESYS export.
|
||||
## Thread and PLC calls
|
||||
|
||||
## Choosing the block
|
||||
One C worker progresses all TCP/RTU client transactions and TCP slave connections.
|
||||
It owns no pointers into IEC memory: request values, replies and slave tables live
|
||||
in fixed C storage. The external call copies buffers synchronously and serializes
|
||||
state access using a priority-inheritance engine mutex. No network wait or sleep
|
||||
is performed while that mutex is held. Serial driver calls can still take time;
|
||||
this is not a hard real-time or lock-free PLC interface.
|
||||
|
||||
| Application | Block | Data |
|
||||
|---|---|---|
|
||||
| TCP master/client | `atMb.ClientTCP` | IPv4 byte array and TCP port |
|
||||
| RTU master/client | `atMb.ClientRTU` | Linux serial device and serial settings |
|
||||
| Read coils, FC1 | `atMb.ReqReadCoil` | BOOL array |
|
||||
| Read discrete inputs, FC2 | `atMb.ReqReadDiscrete` | BOOL array |
|
||||
| Read holding registers, FC3 | `atMb.ReqReadHolding` | UINT array |
|
||||
| Read input registers, FC4 | `atMb.ReqReadInput` | UINT array |
|
||||
| Write coils, FC15 | `atMb.ReqWriteCoil` | BOOL array |
|
||||
| Write holding registers, FC16 | `atMb.ReqWriteHolding` | UINT array |
|
||||
| TCP slave/server | `atMb.SlaveTCP` or `atMb.ModbusSlave` | Four memory arrays |
|
||||
Continue calling each client/request and slave every PLC scan. Client request
|
||||
selection is still performed by the IEC scheduler. A transaction can finish while
|
||||
the PLC is not scanning, and its result waits for the next scan; the worker does
|
||||
not independently cycle through the PLC request registry. TCP slaves respond from
|
||||
the last copied memory snapshot between scans.
|
||||
|
||||
The two slave blocks have identical code/interfaces. They do not provide RTU slave operation. `ClientBase` and `RequestBase` are abstract and are not instantiated directly.
|
||||
|
||||
## TCP read example
|
||||
|
||||
Declarations:
|
||||
## Optional worker configuration and a separate core
|
||||
|
||||
```iecst
|
||||
VAR
|
||||
mbClient : atMb.ClientTCP;
|
||||
readRegisters : atMb.ReqReadHolding;
|
||||
registers : ARRAY[0..9] OF UINT;
|
||||
dataUsable : BOOL;
|
||||
commWorker : atMb.Worker;
|
||||
END_VAR
|
||||
|
||||
// Example only: CPU 2 must be online and permitted on this PLC.
|
||||
commWorker(in_cpu := 2, in_interval := T#1MS, in_watchdog := T#2S);
|
||||
```
|
||||
|
||||
Cyclic body:
|
||||
Use only one Worker configuration instance for the component. Clients/servers
|
||||
start the worker automatically with defaults even without this FB. Default CPU -1
|
||||
uses the thread creator's inherited affinity mask; it is not a promise of all CPUs.
|
||||
A nonnegative value pins the worker to that Linux logical CPU ID. CPU affinity does
|
||||
not reserve an exclusive core or move the PLC task: choose a CPU outside the PLC
|
||||
task's configured affinity where the runtime allows it. Changes affect this worker
|
||||
only. Failure is visible in `out_errorCode` and `out_osError`; no silent fallback.
|
||||
|
||||
```iecst
|
||||
mbClient(
|
||||
in_ipAddress := atMb.To_IPAddress(192,168,50,10),
|
||||
in_port := 502,
|
||||
in_enable := TRUE,
|
||||
in_connectTimeout := T#2S,
|
||||
in_reconnectDelay := T#100MS,
|
||||
in_timeout := T#500MS);
|
||||
The worker uses normal OS scheduling and explicitly avoids inheriting a PLC task's
|
||||
real-time priority. Sleep interval is 1..20 ms, default 1 ms; scheduling jitter can
|
||||
make actual intervals longer. Very short reply deadlines must account for worker
|
||||
interval and transaction framing. `out_cycles` helps monitor worker progress.
|
||||
|
||||
readRegisters(
|
||||
client := mbClient,
|
||||
in_unitId := 1,
|
||||
in_startAddr := 0,
|
||||
in_quantity := 10,
|
||||
in_dataOffset := 0,
|
||||
in_minDelay := T#100MS,
|
||||
in_staleAfter := T#2S,
|
||||
data := registers);
|
||||
## Scan-loss watchdog
|
||||
|
||||
dataUsable := readRegisters.out_validData AND NOT readRegisters.out_stale;
|
||||
```
|
||||
Each client/server refresh call renews its own lease; another client's scans do
|
||||
not keep a stopped client alive. Default expiry is 2 s, configurable 100 ms..60 s.
|
||||
If scanning stops, the worker closes that context's connection/listener. In-flight
|
||||
requests finish as cancelled; potentially transmitted writes retain uncertainty.
|
||||
Handles/storage remain allocated for recovery. The next client/server refresh
|
||||
resumes connection attempts using the same context. Explicit release frees it.
|
||||
|
||||
Call the client and each request every scan from one IEC task. On the first scan the client initializes its native mode, and the request registers afterwards; polling can begin on a subsequent scan. The request body binds the current array and usable count; the client dispatches the request method. No separate execute trigger is required for cyclic polling.
|
||||
Choose a watchdog longer than the worst expected PLC scan gap. This bounds how
|
||||
long a stopped PLC's server can respond with its last memory snapshot; it is not
|
||||
an immediate CODESYS application-stop hook. Component exit stops and joins the
|
||||
worker before freeing connections. Repeated thread shutdown/start is tested.
|
||||
|
||||
Keep instances and arrays stable. Avoid temporarily allocated request objects or sharing native contexts across tasks. Multiple requests can share a client; they are serialized. The supplied C implementation has 16 client contexts shared between TCP/RTU, 100 registrations per IEC client, and one in-flight transaction per client.
|
||||
|
||||
## Addresses and lengths
|
||||
|
||||
Protocol addresses are zero-based: a manual describing its first holding register as 40001 often maps to `in_startAddr := 0`. Confirm the device's mapping; do not pass 40001 automatically.
|
||||
|
||||
`in_dataOffset` is the actual array index. With `ARRAY[1..10]`, use 1 to access the first element. `in_quantity` is clamped to the remaining array capacity rather than always rejected. Check `stats.reqLength` for the effective count.
|
||||
|
||||
Limits per transaction: FC1/2 2000 bits, FC3/4 125 registers, FC15 1968 coils, FC16 123 registers. A one-element write still uses FC15/16 in the public write blocks. Register values are unsigned 16-bit words; converting floats, signed values or multi-register values and selecting word order are application responsibilities.
|
||||
|
||||
## RTU example
|
||||
|
||||
Use the same requests with this client:
|
||||
## TCP read
|
||||
|
||||
```iecst
|
||||
VAR
|
||||
serialClient : atMb.ClientRTU;
|
||||
readRegisters : atMb.ReqReadHolding;
|
||||
registers : ARRAY[0..9] OF UINT;
|
||||
mb : atMb.ClientTCP;
|
||||
readHolding : atMb.ReqReadHolding;
|
||||
values : ARRAY[0..9] OF UINT;
|
||||
END_VAR
|
||||
|
||||
mb(in_ipAddress := atMb.To_IPAddress(192,168,50,10), in_port := 502,
|
||||
in_enable := TRUE, in_timeout := T#500MS, in_reconnectDelay := T#100MS);
|
||||
readHolding(client := mb, in_unitId := 1, in_startAddr := 0,
|
||||
in_quantity := 10, in_dataOffset := 0, data := values);
|
||||
```
|
||||
|
||||
```iecst
|
||||
serialClient(
|
||||
in_device := '/dev/ttyUSB0',
|
||||
in_baudRate := 9600,
|
||||
in_parity := 0,
|
||||
in_dataBits := 8,
|
||||
in_stopBits := 1,
|
||||
in_rs485 := FALSE,
|
||||
in_enable := TRUE,
|
||||
in_timeout := T#500MS,
|
||||
in_reconnectDelay := T#100MS);
|
||||
Keep one IEC task as owner of the related FB instances and arrays. Call the client
|
||||
before request registration on startup; polling starts after registration. The C
|
||||
entry point is serialized but that does not make concurrent IEC FB access safe.
|
||||
TCP addresses are four BYTEs; slave bind addresses are packed DWORDs.
|
||||
|
||||
readRegisters(client := serialClient, in_unitId := 1,
|
||||
in_startAddr := 0, in_quantity := 10, in_dataOffset := 0,
|
||||
data := registers);
|
||||
```
|
||||
RTU uses `ClientRTU(in_device := '/dev/ttyUSB0', in_baudRate := 9600,
|
||||
in_parity := 0, in_dataBits := 8, in_stopBits := 1, in_rs485 := FALSE)` and the same
|
||||
request FBs. RTU IDs must be 1..247. The runtime account needs serial permissions.
|
||||
Kernel RS485 direction control requires a supported driver. USB automatic-direction
|
||||
adapters generally use FALSE. Supported baud rates: 1200,2400,4800,9600,19200,38400,
|
||||
57600,115200. Physical timing/driver validation remains necessary.
|
||||
|
||||
Set RTU unit ID explicitly to 1..247; the inherited request default is 0 and broadcast is unsupported. The Linux runtime account needs serial device permissions. Use a stable device path where available. `in_rs485` requests kernel-managed direction control and requires driver support; USB adapters that control direction automatically generally use FALSE. Serial-port configuration must match the device.
|
||||
## Requests, data and priorities
|
||||
|
||||
The supplied native source supports 1200/2400/4800/9600/19200/38400/57600/115200 baud, 8 bits, parity none/odd/even and one/two stop bits. Timing uses nonblocking I/O and scan-driven millisecond deadlines; physical adapter timing must be verified on the target hardware.
|
||||
| Block | Function | Array | Maximum quantity |
|
||||
|---|---|---|---|
|
||||
| ReqReadCoil | 1 | BOOL | 2000 |
|
||||
| ReqReadDiscrete | 2 | BOOL | 2000 |
|
||||
| ReqReadHolding | 3 | UINT | 125 |
|
||||
| ReqReadInput | 4 | UINT | 125 |
|
||||
| ReqWriteCoil | 15 | BOOL | 1968 |
|
||||
| ReqWriteHolding | 16 | UINT | 123 |
|
||||
|
||||
## Polling and priority
|
||||
Protocol addresses are zero-based; `in_dataOffset` is the actual array index, not
|
||||
a relative offset. Effective length is MIN(quantity, remaining array capacity).
|
||||
Zero length disables a request and now retains its statistics history. Arrays and
|
||||
FB instances must stay valid. There are 16 shared TCP/RTU client slots, 100 IEC
|
||||
requests per client, one in-flight transaction per client and four TCP server slots.
|
||||
|
||||
Requests become active when their effective length is nonzero. They register automatically and poll while the client is enabled. `in_quantity := 0` makes a registered request inactive; it does not remove it from the registry.
|
||||
Lower `in_priorityCurrent` wins; oldest start age breaks ties. A request need not
|
||||
reach zero to run. Success restores `in_prioritySetpoint`; errors use 10/40/90
|
||||
priority backoff. `in_minDelay` is start-to-start eligibility, not a fixed period.
|
||||
|
||||
`in_minDelay` controls eligibility by age since the previous start. It is not a guaranteed polling period. Lowest `in_priorityCurrent` wins, and age breaks equal-priority ties. On success, current priority is restored to `in_prioritySetpoint`. On error it becomes `10 * MIN(consecutive errors,3)^2`: 10, 40, then 90. The current scheduler does not require priority to reach zero before submitting. Avoid overwriting current priority every scan unless that is intended.
|
||||
Successful reads update the destination slice. Errors retain previous data; use
|
||||
`out_validData AND NOT out_stale`. Data age includes time the completed reply waited
|
||||
for a PLC scan. Client release invalidates cached data but does not erase arrays.
|
||||
An exception reply indicates communication, but does not produce valid read data.
|
||||
|
||||
## Writes and uncertainty
|
||||
|
||||
Write arrays are copied at submission, so changes made afterwards affect a later transaction. An active write request is cyclic, just like a read; `writeNow()` is a priority/hold-release operation, not a single-shot write API.
|
||||
|
||||
When a connection fails after a write may have been transmitted, `out_writeUncertain` means the target could already have applied it. With `in_holdUncertainWrites := FALSE`, normal scheduling can retry. With TRUE, that write request is held until its `writeNow()` is called. Other eligible requests can continue.
|
||||
|
||||
```iecst
|
||||
// On an application-controlled event, after preparing the source array:
|
||||
writeRegisters.writeNow();
|
||||
```
|
||||
|
||||
This clears the hold/uncertain output and sets priority 0. The client must still be enabled/connected, the request active and its minimum-delay condition satisfied. Confirm target behavior before retrying commands whose effects should occur only once.
|
||||
Writes snapshot their source slice at submission. The worker never automatically
|
||||
replays a request. The IEC scheduler still polls write FBs cyclically. Set client
|
||||
`in_holdUncertainWrites := TRUE` to pause an uncertain write after a failure;
|
||||
`writeNow()` clears the hold and sets priority zero once the application decides
|
||||
retrying is appropriate. It does not bypass min-delay/connection conditions.
|
||||
|
||||
## Timeouts and reconnects
|
||||
|
||||
Reply timeout precedence:
|
||||
Request `in_timeout` is numeric milliseconds (SYSTIME); client `in_timeout` is TIME.
|
||||
Nonzero request timeout wins, then client timeout, then the existing baud/data-rate
|
||||
formula. TCP's calculated default is about 38 ms, so an explicit timeout is clearer.
|
||||
The C worker uses CLOCK_MONOTONIC for deadlines, independent of PLC-provided time.
|
||||
|
||||
1. Request `in_timeout`: SYSTIME numeric milliseconds, when nonzero.
|
||||
2. Client `in_timeout`: TIME, when nonzero.
|
||||
3. Calculated timeout using client dataRate.
|
||||
4. A one-second fallback if dataRate is zero.
|
||||
`in_reconnectDelay` defaults to 100 ms. Recovery proceeds without PLC ticks while
|
||||
the scan lease is valid. Actual downtime includes failure detection, retry delay,
|
||||
worker scheduling and remote-device readiness. Idle TCP disconnects are detected;
|
||||
there is no zero-downtime guarantee. TCP_NODELAY reduces small-message coalescing.
|
||||
|
||||
The internal property calculates microseconds; submission rounds up to milliseconds with a minimum of 1 ms. TCP defaults its dataRate hint to 105200; the calculated timeout is approximately 38 ms. RTU uses its baud rate; at 9600 the formula gives approximately 411 ms. Explicit client/request timeouts make behavior easier to control.
|
||||
`recreateClient()` closes/recreates a context and cancels any active request.
|
||||
`release()` also sets `in_enable := FALSE`; a subsequent call explicitly passing
|
||||
TRUE re-enables it. Closing a context clears C lifetime counters.
|
||||
|
||||
`in_connectTimeout` is the TCP connection deadline, separate from reply timeout. `in_reconnectDelay` defaults to 100 ms. It is the recovery wait after detecting failure, not a guarantee of recovery within 100 ms: scan time, connection timeout, reply timeout, OS behavior and target readiness also matter.
|
||||
## Client statistics
|
||||
|
||||
Endpoint/configuration changes release and recreate the native context. `recreateClient()` forces this process; it does not permanently disable the client. `release()` also sets `in_enable := FALSE`. To restart, set TRUE and continue scanning. Supplying TRUE as a call parameter every scan will override that disabled input on the next call.
|
||||
Both clients expose `stats : ClientStats` and a public string property `toString`.
|
||||
The monitor display and `stats.toString` use `OK:<doneCnt>/Er:<errorCnt>`. For example,
|
||||
requests with 10/2 and 7/1 completions/errors show `OK:17/Er:3` on the client.
|
||||
|
||||
## Data quality and statistics
|
||||
|
||||
| Diagnostic | Meaning |
|
||||
| Client member | Aggregation |
|
||||
|---|---|
|
||||
| Client `out_connected` | Native transport is ready/sending/receiving |
|
||||
| Request `stats.online` | Valid response evidence, including Modbus exceptions |
|
||||
| Request `out_validData` | A successful read has populated data since invalidation |
|
||||
| Request `out_stale` | No valid data, or last successful read is older than threshold |
|
||||
| Request `out_dataAgeMs` | Age of successful read data |
|
||||
| Request `out_nativeError` | Last native request result |
|
||||
| Request `stats.lastErrorId` | Mapped IEC error classification |
|
||||
| Request `stats.lastExceptionCode` | Modbus exception or local statistics classification |
|
||||
| `out_socketError` | OS errno snapshot for socket/serial failure |
|
||||
| doneCnt / errorCnt | Sum of registered request successes/failures |
|
||||
| sentCnt / replyCnt | Sum of accepted submissions / normal or exception replies |
|
||||
| exceptionCnt / timeoutCnt | Sum of exception replies / result 5 or 6 timeouts |
|
||||
| offlineCnt | Sum of request online-to-offline transitions |
|
||||
| requestCount | Registered requests, including disabled ones |
|
||||
| activeCount | Nonzero effective-length requests |
|
||||
| onlineCount / offlineCount / unknownCount | Active request communication state |
|
||||
| busyCount | Active requests executing |
|
||||
| staleCount | Active read requests with stale data |
|
||||
| heldWriteCount | Active write requests paused after uncertain delivery |
|
||||
| totalValidDuration | Sum of successful transaction duration totals, ms |
|
||||
| averageValidDuration | Weighted mean: totalValidDuration / doneCnt, integer ms |
|
||||
| longestValidDuration | Maximum successful duration across requests |
|
||||
| online / offline | Nonempty active set, all online / all explicitly offline |
|
||||
|
||||
These represent different conditions. Exception replies can make communication online while the read fails. Read failures retain old values and may retain `out_validData`; use freshness as well. Releasing the client marks cached data invalid but does not erase arrays. Some completion paths return before updating age, so freshness outputs can refresh on the following scan.
|
||||
Historical counters include registered inactive requests; state counts include
|
||||
only active requests. An empty client is neither online nor offline. Aggregate
|
||||
counters are ULINT. Per-request counters retain their existing types and saturate
|
||||
rather than wrap; request offline counts saturate at UINT max. Successful duration
|
||||
accumulation stops when the associated success counter saturates to keep the mean
|
||||
consistent. There is no misleading aggregate of consecutive-error streaks or
|
||||
unrelated start intervals.
|
||||
|
||||
`reqDone`/`reqError` are internal completion flags; the light outputs use a 100 ms off-delay for monitoring. `timeoutCnt` currently counts non-exception errors that statistics classify as TIMEOUT, not only native reply deadlines. Prefer native result 6 for a precise reply timeout.
|
||||
`numMsgSent`/`numMsgReply` remain as saturated UDINT mirrors of aggregate totals.
|
||||
The one-second window fields remain scan-published counts/duration totals.
|
||||
Request `lastValidDuration` now measures C submit-to-completion, excluding the PLC
|
||||
pickup delay. `resetStats()` resets totals, duration history, timeout and offline
|
||||
counts consistently, while retaining connection state and data freshness. Resetting
|
||||
during a transaction allows its eventual completion to be counted after reset.
|
||||
|
||||
Client `resetStats()` resets IEC send/reply/window counters and each request's four success/error counters. Request `stats.resetStats()` resets only `doneCnt`, `doneCntRst`, `errorCnt`, `errorCntRst`. Durations, timestamps, offline count, timeout count and native reconnect/errno history are retained. `in_resetStats` is consumed and cleared by the client; providing TRUE every scan keeps resetting it.
|
||||
`out_errorCode` is the request result; `out_lastErrorCode` is the client's last
|
||||
engine error. OS errno is `out_socketError`. Historical last errors may persist
|
||||
after recovery. Result 260 means worker/affinity setup failed. A non-timeout failure
|
||||
no longer increments timeoutCnt. See the API reference for all declarations.
|
||||
|
||||
## TCP slave example
|
||||
## TCP slave memory exchange
|
||||
|
||||
```iecst
|
||||
VAR
|
||||
server : atMb.SlaveTCP;
|
||||
coils : ARRAY[0..15] OF BOOL;
|
||||
discreteInputs : ARRAY[0..15] OF BOOL;
|
||||
holding : ARRAY[0..31] OF UINT;
|
||||
inputs : ARRAY[0..31] OF UINT;
|
||||
END_VAR
|
||||
```
|
||||
`SlaveTCP` and `ModbusSlave` provide the same TCP server behavior. Supply four arrays:
|
||||
`aCoilsMemory`, `aDiscreteInputsMemory`, `aHoldingRegistersMemory`, `aInputRegistersMemory`.
|
||||
Their lower/upper bounds are Modbus addresses within 0..65535; max 2000 per table.
|
||||
FC1/2/3/4 read; FC5/6/15/16 write coils/holding registers. Bind IP 0 means all
|
||||
interfaces; interfaceName must remain empty. Up to eight peers per server.
|
||||
|
||||
```iecst
|
||||
// Publish application inputs before the server call.
|
||||
inputs[0] := 123;
|
||||
server(in_enable := TRUE, in_ipAddress := DWORD#0, port := 1502,
|
||||
aCoilsMemory := coils,
|
||||
aDiscreteInputsMemory := discreteInputs,
|
||||
aHoldingRegistersMemory := holding,
|
||||
aInputRegistersMemory := inputs);
|
||||
// Consume network-written coils/holding values after this call.
|
||||
```
|
||||
Remote changes are marked dirty in C. An old PLC push cannot overwrite dirty
|
||||
values before pull acknowledges them. Multiple remote writes before a scan expose
|
||||
the latest value, not an event history. After pull, a later application push may
|
||||
intentionally override those values. Other tasks must not race the array exchange.
|
||||
Table bounds/length changes require `in_reset := TRUE` to recreate the server;
|
||||
a changed mapping is rejected rather than silently discarding pending writes.
|
||||
|
||||
Port 1502 is an example; select the application's port. Keep `interfaceName` empty. `in_ipAddress := 0` binds all interfaces. A specific bind IPv4 is packed into DWORD, e.g. `DWORD#16#C0A8321F` for 192.168.50.31.
|
||||
|
||||
Each table has independent addresses. `ARRAY[100..109] OF UINT` maps addresses 100..109, not 0..9. All tables must use nonnegative bounds within 0..65535 and at most 2000 elements. The current API requires four arrays; a small placeholder can represent an otherwise unused table.
|
||||
|
||||
The wrapper snapshots all four application tables into native memory, processes bounded network work, then copies the tables back. Network writes affect coils and holding registers. Avoid overwriting externally controlled values unconditionally in application code each scan unless that override is intentional. The supplied C engine supports four server contexts and eight peers per server. Server contexts are separate from the 16-client pool.
|
||||
|
||||
`out_running` means the listener is bound; `out_connectedClients` indicates peers. `in_reset` is consumed and cleared; a constant TRUE recreates the server every scan. Listener bind failures retry while enabled. Counters reset with server context recreation. Some early-return paths leave prior outputs unchanged; interpret diagnostic outputs together.
|
||||
|
||||
## Native result codes and client states
|
||||
|
||||
| Result | Meaning |
|
||||
|---|---|
|
||||
| 0 | Success |
|
||||
| 1 | Invalid argument |
|
||||
| 2 | Native client busy |
|
||||
| 3 | Client offline/not ready |
|
||||
| 4 | Socket/serial OS error |
|
||||
| 5 | TCP connection deadline |
|
||||
| 6 | Reply deadline |
|
||||
| 7 | Peer closed |
|
||||
| 8 | Invalid protocol/frame/reply |
|
||||
| 9 | Modbus exception response |
|
||||
| 10 | Cancelled |
|
||||
| 256 | Invalid/stale native handle |
|
||||
| 257 | Native pool full |
|
||||
| 258 | Insufficient/invalid buffer capacity |
|
||||
| 259 | Unknown command |
|
||||
|
||||
These are distinct from the sparse IEC `Error` enum. IEC mapping combines 4/7 into `TcpSocketError`, 1/258 into `RequestParameterError`; other unmapped results become `InternalError`. TCP names also classify RTU failures.
|
||||
|
||||
States: 0 disabled, 1 retry wait, 2 connecting, 3 ready, 4 sending, 5 receiving.
|
||||
|
||||
## Maintenance and scope
|
||||
|
||||
The cleanup preserves the supplied working library and standalone native binary. Internal native fields are sometimes exposed as VAR_INPUT for compatibility; leave them under library control. Several legacy BOOL methods do not assign a return value; the API reference identifies them so callers rely on their side effects.
|
||||
|
||||
Lifecycle hooks exist, but reliable online-change ownership and pointer relocation remain future work. Documentation adds no behavior changes or online-change guarantee. Use the API reference for every method/property, including internal accessors.
|
||||
Online-change pointer ownership and physical hardware certification remain future
|
||||
work. This release needs a full download and on-target validation.
|
||||
|
||||
Reference in New Issue
Block a user