7.5 KiB
SuiteLink Tag Client Design
Goal
Build a cross-platform .NET 10 C# client that communicates with AVEVA SuiteLink from macOS, Linux, and Windows for tag operations only.
The v1 scope is limited to:
- Connect to a SuiteLink endpoint
- Subscribe to tags
- Receive tag updates
- Write tag values
- Unsubscribe cleanly
The v1 scope explicitly excludes:
- AlarmMgr
- Alarm and event handling
- Secure SuiteLink V3 support
- Automatic reconnect and subscription rebuild
Constraints
- The protocol is proprietary and the current design is based on reverse-engineered public evidence plus AVEVA product documentation.
- The initial target is non-encrypted SuiteLink V2 behavior, or servers configured for mixed mode that still permit legacy SuiteLink traffic.
- The first implementation only supports primitive value types:
boolint32float32string
- The design should remain extensible for later support of
double,int64, andDateTimeif packet captures confirm their wire representation.
Protocol Target
The target protocol surface is the normal SuiteLink tag exchange path, not AlarmMgr.
Observed normal message structure:
uint16 little-endian remaining_lengthuint16 little-endian message_type- payload
- trailing byte
0xA5
Observed message types to support:
CONNECTADVISEADVISE ACKUPDATEUNADVISEUNADVISE ACKPOKEPOKE ACKTIME- Ping/pong keepalive messages
Observed normal wire value types:
- binary
- integer
- real
- message
Architecture
The client is split into three layers.
Transport
Responsible for:
- Opening and closing TCP connections
- Reading complete SuiteLink frames
- Writing complete SuiteLink frames
- Cancellation and socket lifetime management
Protocol
Responsible for:
- Encoding the startup handshake
- Encoding
CONNECT - Encoding
ADVISE,UNADVISE, andPOKE - Decoding handshake acknowledgements
- Decoding
ADVISE ACK,UPDATE, and keepalive traffic - Converting between wire values and typed client values
Client API
Responsible for:
- Exposing a minimal public API for connect, subscribe, read, write, and disconnect
- Hiding protocol details such as server-assigned tag ids
- Dispatching updates to user callbacks or future stream abstractions
Session Model
The session uses one persistent TCP connection to one SuiteLink endpoint and one configured application/topic pair.
State model:
DisconnectedTcpConnectedHandshakeCompleteSessionConnectedFaulted
Startup flow:
- Open TCP connection.
- Send SuiteLink handshake for the normal tag protocol.
- Validate handshake acknowledgement.
- Send
CONNECTwith application, topic, and client identity fields. - Transition to connected session state.
- Send
ADVISEfor one or more items. - Capture
tag_idmappings from server responses. - Receive
UPDATEframes and dispatch typed values to subscribers. - Send
POKEfor writes. - Send
UNADVISEwhen a subscription is disposed.
Read behavior in v1 is implemented as a temporary subscription:
- Send
ADVISEfor the requested item. - Wait for the first matching
UPDATE. - Return the decoded value.
- Send
UNADVISE.
This is preferred over inventing a direct read request that has not yet been proven by packet captures.
Public API
public sealed class SuiteLinkClient : IAsyncDisposable
{
Task ConnectAsync(SuiteLinkConnectionOptions options, CancellationToken ct = default);
Task DisconnectAsync(CancellationToken ct = default);
Task<SubscriptionHandle> SubscribeAsync(
string itemName,
Action<SuiteLinkTagUpdate> onUpdate,
CancellationToken ct = default);
Task<SuiteLinkTagUpdate> ReadAsync(
string itemName,
TimeSpan timeout,
CancellationToken ct = default);
Task WriteAsync(
string itemName,
SuiteLinkValue value,
CancellationToken ct = default);
}
Supporting models:
SuiteLinkConnectionOptionsHostPortdefault5413ApplicationTopicClientNameClientNodeUserNameServerNode
SuiteLinkValue- typed union for
bool,int,float, andstring
- typed union for
SuiteLinkTagUpdateItemNameTagIdValueQualityElapsedMillisecondsReceivedAtUtc
SubscriptionHandle- caller-facing subscription lifetime object
- disposes via
UNADVISE
Internal Components
SuiteLinkFrameReader
Responsibilities:
- Read complete normal SuiteLink frames from a
NetworkStream - Parse the 2-byte little-endian remaining length
- Validate trailing
0xA5 - Return frame payload slices to the codec
SuiteLinkFrameWriter
Responsibilities:
- Build frames in memory
- Write little-endian lengths and message types
- Encode UTF-16LE strings where required
- Append trailing
0xA5
SuiteLinkMessageCodec
Responsibilities:
- Encode handshake and normal session messages
- Decode incoming acknowledgements and updates
- Map wire value types to
SuiteLinkValue
Expected methods:
EncodeHandshakeEncodeConnectEncodeAdviseEncodeUnadviseEncodePokeDecodeHandshakeAckDecodeAdviseAckDecodeUpdateDecodeKeepAlive
SuiteLinkSession
Responsibilities:
- Own the send and receive loops
- Maintain session state
- Track
itemName <-> tagIdmappings - Track active subscriptions
- Route decoded updates to the correct subscriber callbacks
Type Strategy
The first release supports only:
boolint32float32string
The public type wrapper must be extensible so later additions do not require replacing the whole API. The intended future-compatible additions are:
doubleint64DateTime
The protocol layer should fail fast when an unsupported wire type or unsupported outgoing write type is encountered.
Error Handling
The v1 client should be explicit and conservative.
- Any malformed frame transitions the session to
Faulted - Any unexpected message type during startup fails the connection attempt
- Write attempts for unsupported types fail immediately
- Reconnect is not automatic in v1
- Subscription rebuild after reconnect is deferred to a later version
Validation Strategy
Testing is divided into three levels.
Unit Tests
Validate:
- frame length handling
- trailing marker validation
- UTF-16LE string encoding/decoding
- primitive value encoding/decoding
Golden Packet Tests
Use known byte sequences and captures to verify:
- handshake
CONNECTADVISEADVISE ACKUPDATEPOKEUNADVISE
Integration Tests
Run against a real AVEVA/OI server configured to allow legacy or mixed-mode SuiteLink traffic.
Success criteria:
- connect successfully from macOS or Linux
- subscribe to one boolean tag
- subscribe to one integer tag
- subscribe to one real tag
- subscribe to one message tag
- receive live updates for each
- write to each supported tag type and verify the result
- disconnect cleanly
Non-Goals
The following are intentionally deferred:
- AlarmMgr support
- Secure SuiteLink V3 support
- automatic reconnect
- batched subscription optimization
- broad type support beyond the four proven primitive classes
- production hardening for all undocumented server variants
Recommended Next Step
Create a detailed implementation plan that:
- establishes the project structure
- defines the test-first workflow
- identifies capture-driven fixtures needed for codec tests
- breaks the implementation into transport, codec, session, and API slices