Skip to content

KV Host Link Rust API reference

This page indexes the supported user-facing surface. Maintainer raw frame and trace facilities are intentionally omitted from ordinary user documentation.

Connection and lifecycle

Purpose API
Validated options HostLinkConnectionOptions::new(host, port, transport, plc_profile)
Disconnected client HostLinkClient::new
Connected client HostLinkClient::connect, open_and_connect, HostLinkClientFactory::open_and_connect
Lifecycle open, close, is_open
Session values timeout, set_timeout, plc_profile
Transport selection HostLinkTransportMode::{Tcp, Udp}

Endpoints are IPv4-only. IPv6 literals are caller errors; hostnames without an IPv4 result fail as connection errors before protocol communication. IPv4 literals use the unbracketed form; [127.0.0.1] is rejected during option validation. Raw command bodies must be non-empty ASCII without CR/LF and are limited to 65,506 bytes, making the CR-terminated TCP/UDP request frame at most 65,507 bytes. Response bodies retain their internal 65,536-byte cap. There is no caller-controlled receive capacity; public results own their dynamic storage.

PLC operations

Purpose API
PLC mode change_mode, confirm_operating_mode, KvPlcMode
Error operation clear_error, check_error_no
PLC model query_model, KvModelInfo
PLC clock set_time, HostLinkClock
Forced bit control forced_set, forced_reset, forced_set_consecutive, forced_reset_consecutive
Bank selection switch_bank

Device operations

Purpose API
Low-level single read/write read, write
Low-level consecutive read/write read_consecutive, write_consecutive
Explicit bit-in-word write HostLinkClient::write_bit_in_word
Legacy command variants read_consecutive_legacy, write_consecutive_legacy
Timer/counter set value write_set_value, write_set_value_consecutive
Monitor registration register_monitor_bits, register_monitor_words, HostLinkMonitorWord
Monitor read read_monitor_bits, read_monitor_words
Expansion-unit buffer read_expansion_unit_buffer, write_expansion_unit_buffer
Expansion-buffer bit write HostLinkClient::write_bit_in_expansion_unit_buffer
Comment encoding HostLinkCommentEncoding::{Utf8, Cp932}
Comment text read_comments(device, encoding)
Comment bytes read_comment_bytes

Numeric low-level methods require a base device plus an explicit format. Direct bit methods use an unsuffixed device. Suffix-bearing low-level device strings are rejected. Direct-bit writes require bool; numeric and textual aliases are rejected before transport.

Custom low-level write values implement HostLinkPayloadValue. format_for_suffix returns Result<String, HostLinkError> and must return Err for an unsupported suffix or invalid value. The default append_to_payload appends only a complete successful token; formatter errors and an empty returned token are rejected without changing the output. Failures propagate through direct, consecutive, set-value, expansion-buffer, and typed writes before transport. There is no infallible compatibility formatter.

HostLinkClock.year is the explicit two-digit PLC year and must be 0..=99. Semantic reads validate command-derived response counts. Bare direct-bit responses accept only the exact tokens 0, 1, OFF, or ON without trimming or case folding. Formatted direct-bit single reads return one packed numeric token: .U, .S, and .H represent 16 bits, while .D and .L represent 32 bits. Signed .S and .L tokens may include an explicit leading +. Malformed semantic responses close the connection generation. UDP responses require a CR/LF terminator. A valid completed exchange reuses the connected UDP socket and local endpoint. Timeout, cancellation, I/O/protocol or framing failure, an extra response, or a pre-send unowned datagram discards that socket; the next operation creates a replacement from the resolved endpoint. Host Link has no request identifier, so a duplicate arriving between the pre-send check and current response remains inherently indistinguishable. TCP accepts one non-empty response per request and retires the connection when it receives an additional unowned non-empty response. The same request-identifier limitation leaves a TCP race between the final pre-send check and send. Healthy TCP connections remain persistent because a connection per request would add a handshake without adding a protocol identifier; every observable anomaly still retires the connection and requires explicit reopen. All non-format commands, including forced control, monitor-bit registration, comment reads, and timer/counter helpers, reject suffix-bearing devices. Monitor reads require a successful registration in the current connection generation and enforce the exact registered token count. Word-monitor registration also preserves each entry's ordered format, and MWR validates each token against its corresponding .U, .S, .H, .D, .L, or packed direct-bit unsigned 16-bit format before returning any values.

HostLinkMonitorWord::numeric(device, format) emits the explicit numeric suffix in MWS. HostLinkMonitorWord::packed_direct_bits_u16(device) accepts only an unsuffixed direct-bit device, emits that exact bare device in MWS, and validates the corresponding MWR field as exactly 1-5 ASCII decimal digits whose numeric value is from 0 through 65535. Leading zeros are optional and the returned String preserves them. Empty fields, signs, whitespace, nondecimal text, six or more digits, and overflow are rejected. It does not return one Boolean. For individual bit values, use register_monitor_bits followed by read_monitor_bits; those MBR fields remain strict 0/1/ON/OFF. The former DirectBit enum variant and direct_bit constructor were removed without an alias.

High-level helpers

Purpose API
Typed value HostLinkValue, read_typed, write_typed
Named read result NamedReadResult, read_named, read_named_with_comment_encoding
Polling poll, poll_with_comment_encoding
Timer/counter composite TimerCounterValue, read_timer_counter, read_timer, read_counter
Word reads read_words, read_words_single_request
Dword reads read_dwords, read_dwords_single_request
Word writes write_words_single_request
Dword writes write_dwords_single_request
Explicit bit-in-word write write_bit_in_word
Expansion-buffer bit write write_bit_in_expansion_unit_buffer

All word/Dword helpers are single-request operations. There are no chunked exports. read_named is the only automatic multi-request read aggregate. It groups wire-compatible device types by first appearance, sorts each group by address, merges contiguous ranges up to request limits, keeps multiword values whole, owns one FIFO turn through final decode/staging, and returns no partial result. Public result order remains input order; wire order is optimized. Pure result materialization occurs after releasing the turn. A multi-frame named read is not PLC-atomic; coherent readers must use one request or a PLC-side snapshot/handshake. Named keys must be semantically unique by device family, numeric address, dtype, bit index, and scalar count. Spelling-only variants are rejected before FIFO admission, while distinct dtype views, bit indices, and overlapping spans remain valid. Result keys preserve the original input strings.

write_bit_in_word is the one explicit two-request write helper. It accepts a Boolean value and bit index 0..=15, validates an ordinary complete 16-bit word target before FIFO admission, and keeps the immutable device route for its one read and one write. One absolute deadline covers both after activation. It always sends the write after a successful read, performs no fallback, retry, or success readback, and is not PLC-atomic against PLC logic or another connection.

write_bit_in_expansion_unit_buffer applies the same contract to one .U word on the existing URD/UWR unit/address route. The selected unit and address remain immutable across both requests and never fall back to an ordinary device route.

Numeric semantic .H values validate 1..4 hexadecimal digits and return exactly four uppercase digits (0000 through FFFF); raw reads and write spelling are not normalized. Timer/counter composite reads require exactly three semantic tokens. The first is a structural status field validated as the exact raw PLC token 0 or 1; .U, .S, .H, .D, or .L applies only to the current and preset fields. Consequently, low-level .H reads expose status as 0/1, not the former incorrect 0000/0001, while current and preset are canonical four-digit uppercase hexadecimal values. Public signatures and high-level return types are unchanged. Malformed semantic responses close the connection. Float32 parsing, formatting, reads, and writes use canonical family metadata and accept only the ordinary .U families DM, EM, FM, ZF, W, TM, CM, VM, D, E, and F. Direct-bit, Z, and special-response families such as R, T, C, and AT reject :F before FIFO admission and transport. Named reads and polls require a non-empty address set, and poll intervals must be greater than zero.

Every comment text read requires HostLinkCommentEncoding::Utf8 or HostLinkCommentEncoding::Cp932. Cp932 is CP932/Windows-31J and is the selection for KEYENCE documentation that calls the compatible encoding Shift_JIS; there is no separate strict-Shift-JIS, automatic, default, or profile-selected mode. Decoding is strict and never retries another codec or inserts replacement characters. The shared CP932 subset preserves ASCII controls, rejects standalone 80/A0/FD/FE/FF, and accepts defined NEC, IBM, and duplicate extension mappings. read_comment_bytes returns the exact terminator-free RDC payload, including trailing ASCII-space padding. UTF-8 decoding preserves an initial EF BB BF as U+FEFF; it is comment data, not a removable transport signature. The same bytes are invalid under Cp932. Syntactically valid PLC E0 through E9 replies return HostLinkError::Plc without retiring the connection for every semantic command, including writes; malformed framing or payload decoding retires it.

The ordinary read_named and poll APIs reject :COMMENT entries before transport. Use read_named_with_comment_encoding or poll_with_comment_encoding when an aggregate intentionally includes comments; both require one explicit comment encoding and at least one :COMMENT entry. Providing an unused comment encoding for a non-comment-only list is a pre-transport protocol error. Converting HostLinkValue to u16 is fallible through TryFrom; variants other than HostLinkValue::U16 return an error instead of producing zero.

Address and profile APIs

Purpose API
Address models HostLinkAddress, KvDeviceAddress, KvLogicalAddress
Address parsing parse_device, parse_logical_address
Address validation validate_device_type, validate_device_count, validate_device_span
Expansion validation validate_expansion_buffer_count, validate_expansion_buffer_span
Profile enumeration available_plc_profiles, plc_profile_descriptors
Profile selection normalize_plc_profile, profile_from_name, display_name
Range catalog device_range_catalog_for_plc_profile, KvDeviceRangeCatalog

Errors

HostLinkError distinguishes Protocol, Timeout, Closed, NotConnected, Transport, Plc, and OutcomeUnknown. PLC errors retain the returned code and response; the crate does not embed copied manual error descriptions. OutcomeUnknown has a machine-readable reason for timeout, close, transport failure, or malformed response. It is used when a state-changing request may have reached the PLC and the future returns a library Result; no automatic retry occurs.

Dropping a Rust future is caller-observed cancellation and returns no HostLinkError. A waiting drop sends nothing. A drop after transmission may have started poisons the exchange and retires its socket. TCP then returns NotConnected until an explicit open; UDP creates a replacement socket from the resolved logical endpoint when the next operation begins. The caller must treat a state-changing outcome as unknown. HostLinkOutcomeUnknownReason therefore has no cancellation variant, and a dropped future is distinct from a returned library Timeout.

The complete generated Rust API for a release is available through docs.rs.

Every HostLinkClient owns a FIFO admission queue and one wire turn shared by its clones. close invalidates active and waiting work from the old generation; only work admitted after an explicit reopen may use the new transport.

Traffic statistics

HostLinkClient::traffic_stats().await returns HostLinkTrafficStats. TCP receive bytes count the body plus the first CR/LF terminator, independent of separator segmentation; UDP receive bytes count the complete datagram.