191 lines
10 KiB
Markdown
191 lines
10 KiB
Markdown
# atMb 0.4 user guide
|
|
|
|
## Thread and PLC calls
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
## Optional worker configuration and a separate core
|
|
|
|
```iecst
|
|
VAR
|
|
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);
|
|
```
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
## Scan-loss watchdog
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
## TCP read
|
|
|
|
```iecst
|
|
VAR
|
|
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);
|
|
```
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
## Requests, data and priorities
|
|
|
|
| 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 |
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
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 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
|
|
|
|
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.
|
|
|
|
`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.
|
|
|
|
`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.
|
|
|
|
## Client statistics
|
|
|
|
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.
|
|
|
|
| Client member | Aggregation |
|
|
|---|---|
|
|
| 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 |
|
|
|
|
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.
|
|
|
|
`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.
|
|
|
|
`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 memory exchange
|
|
|
|
`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.
|
|
|
|
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.
|
|
|
|
Online-change pointer ownership and physical hardware certification remain future
|
|
work. This release needs a full download and on-target validation.
|