Documentation
¶
Overview ¶
Package ethernetip is the module root of a pure Go implementation of EtherNet/IP and the Common Industrial Protocol (CIP), the industrial Ethernet protocol of ODVA used by programmable controllers, I/O adapters, drives and sensors.
Applications import the subpackages:
- github.com/otfabric/go-ethernetip/client: the originator of explicit messages: sessions, unconnected and connected (Class 3) messaging, Forward_Open and Forward_Close, routing through Unconnected_Send, Multiple Service Packet, discovery with ListIdentity
- github.com/otfabric/go-ethernetip/scanner: the originator of Class 1 (and Class 0) implicit I/O connections: cyclic UDP exchange with sequence handling, run/idle, timeouts and recovery
- github.com/otfabric/go-ethernetip/server: the target (adapter): the Message Router with the Identity, Message Router, Assembly, Connection Manager, Port, TCP/IP Interface and Ethernet Link objects, explicit and implicit connections, user-defined objects
- github.com/otfabric/go-ethernetip/logix: Rockwell Automation Logix extensions (vendor specific, not CIP): symbolic tag reads and writes, fragmentation, tag lists, templates and UDTs; on the client and, emulated, on the server
- github.com/otfabric/go-ethernetip/eds: Electronic Data Sheets: parser, validation, writer and the Go code generator behind ethernetipctl eds
- github.com/otfabric/go-ethernetip/cip: the CIP codec: data types, EPATHs, messages, the Connection Manager services, the catalog of codes
- github.com/otfabric/go-ethernetip/encap: the encapsulation codec: headers, commands, the common packet format
- github.com/otfabric/go-ethernetip/implicit: the codec and the UDP endpoint of implicit (I/O) messages, shared by scanner and server
This package holds what they share: the sentinel and typed errors, the Logger and Metrics interfaces and the State of sessions and connections.
Docs: README.md, API.md (public API), ARCHITECTURE.md, ERRORS.md (error semantics), OBSERVABILITY.md (logging and metrics), INTEROPERABILITY.md (coverage and interop results), SECURITY.md, CONTRIBUTING.md, CATALOG.md (codes and their provenance), RELEASE.md (changelog).
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrClosed reports an operation on a client, connection or server that // was closed by the application. ErrClosed = errors.New("ethernetip: closed") // ErrNotConnected reports a request on a client that has no registered // session. ErrNotConnected = errors.New("ethernetip: not connected") // ErrConnectFailed reports that a client could not establish its // session: the dial, the TLS handshake or RegisterSession failed. The // underlying error is wrapped alongside it. ErrConnectFailed = errors.New("ethernetip: connect failed") // ErrConnectionLost reports that the peer closed the TCP connection or // the transport failed while a request was in progress. ErrConnectionLost = errors.New("ethernetip: connection lost") // ErrTimeout reports that the peer did not answer within the request // timeout, or that an I/O connection's consumed data stopped arriving // within its connection timeout. ErrTimeout = errors.New("ethernetip: timeout") // ErrProtocol reports a peer that violated the protocol: a malformed // encapsulation packet or CIP response, a reply to a service that was // not requested, an unexpected item in a common packet format. ErrProtocol = errors.New("ethernetip: protocol violation") // ErrSession reports an encapsulation level failure of the session: the // peer rejected the session handle, the protocol version or the // command. An *[EncapError] carries the status. ErrSession = errors.New("ethernetip: session error") // ErrBusy reports a request that cannot be issued because a limit was // reached: pending requests of a client, sessions or connections of a // server. ErrBusy = errors.New("ethernetip: resource limit reached") // ErrUnsupported reports that the peer does not support the service or // attribute (CIP general status 0x08 or 0x14), or that the operation is // not available on this transport. ErrUnsupported = errors.New("ethernetip: unsupported") // ErrInvalidOption reports an option or argument value a client, scanner // or server cannot be constructed or called with. ErrInvalidOption = errors.New("ethernetip: invalid option") )
Sentinel errors shared by the client, the scanner and the server. They are usually wrapped with detail; test with errors.Is.
Functions ¶
Types ¶
type CIPError ¶
type CIPError struct {
// Service is the service code of the request (without the reply bit).
Service cip.Service
// Path is the request path, for messages; it may be nil.
Path cip.Path
// Status is the general status code.
Status cip.GeneralStatus
// Extended is the additional status, in the order sent.
Extended []uint16
// Data is the response data that came with the error, if any. Some
// services return data with an error (Forward_Open's remaining path
// size, Multiple Service Packet's replies).
Data []byte
}
CIPError reports a CIP response whose general status is not success. It keeps the service, the general status and the additional (extended) status words exactly as the peer sent them.
func (*CIPError) Error ¶
Error describes the failure, for example "ethernetip: Get_Attribute_Single @1/1/9: attribute not supported (0x14)".
func (*CIPError) ExtendedStatus ¶
ExtendedStatus returns the first additional status word, and false when there is none.
type ConnectionInfo ¶
type ConnectionInfo struct {
// Class is the transport class (0, 1 or 3).
Class uint8
// OTConnID and TOConnID are the network connection IDs.
OTConnID, TOConnID uint32
// Serial, VendorID and OriginatorSerial identify the connection (its
// connection triad).
Serial uint16
VendorID uint16
OriginatorSerial uint32
// OTRPI and TORPI are the actual packet intervals.
OTRPI, TORPI time.Duration
// Path is the connection path.
Path cip.Path
// Group is the multicast address of the T->O data, when it is
// multicast.
Group netip.Addr
}
ConnectionInfo describes a CIP connection for metrics and logs.
type ContextLogger ¶
type ContextLogger interface {
Logger
DebugContext(ctx context.Context, msg string, keysAndValues ...any)
InfoContext(ctx context.Context, msg string, keysAndValues ...any)
WarnContext(ctx context.Context, msg string, keysAndValues ...any)
ErrorContext(ctx context.Context, msg string, keysAndValues ...any)
}
ContextLogger extends Logger with context-aware structured logging, so that a handler can pick trace and span identifiers out of the context. When the Logger also implements ContextLogger, entries that belong to a request are logged with the context the request was called with.
type EncapError ¶
type EncapError struct {
// Command is the encapsulation command of the reply.
Command encap.Command
// Status is the encapsulation status.
Status encap.Status
}
EncapError reports an encapsulation packet whose status is not success: the peer refused the command, the session handle or the length.
func (*EncapError) Error ¶
func (e *EncapError) Error() string
Error describes the failure, for example "ethernetip: SendRRData: invalid session handle (0x0064)".
func (*EncapError) Is ¶
func (e *EncapError) Is(target error) bool
Is makes an EncapError match ErrSession, and ErrUnsupported for an unsupported command.
type FieldLogger ¶
type FieldLogger interface {
Logger
// With returns a logger that adds the given fields to every entry.
With(keysAndValues ...any) FieldLogger
DebugKV(msg string, keysAndValues ...any)
InfoKV(msg string, keysAndValues ...any)
WarnKV(msg string, keysAndValues ...any)
ErrorKV(msg string, keysAndValues ...any)
}
FieldLogger extends Logger with structured key-value logging. When the Logger given to a client or server also implements FieldLogger, the library logs through it: every entry is a constant message plus fields such as "component", "remote", "session", "connection" and "error", instead of one formatted string. NewSlogLogger returns such a logger.
keysAndValues alternate string keys and values, as in log/slog.
func NewSlogFieldLogger ¶
func NewSlogFieldLogger(h slog.Handler) FieldLogger
NewSlogFieldLogger is NewSlogLogger with the result typed as a FieldLogger, for callers that want to add fields with With before handing the logger to a client or server. A nil handler returns a no-op logger.
type IOMetrics ¶
type IOMetrics interface {
// OnIOSent is called after an I/O datagram was sent.
OnIOSent(connID uint32, size int)
// OnIOReceived is called for every I/O datagram accepted for a
// connection.
OnIOReceived(connID uint32, size int)
// OnIODropped is called for an I/O datagram that was dropped: unknown
// connection ID, stale sequence number, wrong size, malformed.
OnIODropped(remote net.Addr, reason string)
}
IOMetrics is an optional extension of Metrics for implicit (Class 0/1) traffic, for a scanner or a server.
type Logger ¶
type Logger interface {
Debugf(format string, args ...any)
Infof(format string, args ...any)
Warnf(format string, args ...any)
Errorf(format string, args ...any)
}
Logger is the logging interface accepted by the client and the server. Implement it to integrate any logging library.
Logging is silent by default. Methods are invoked synchronously on the protocol path and must not block.
func NewSlogLogger ¶
NewSlogLogger wraps a slog.Handler. The result implements FieldLogger and ContextLogger, so the library logs structured fields through it; messages are only built when the handler is enabled for their level. A nil handler returns a no-op logger.
func NewStdLogger ¶
NewStdLogger wraps a standard library logger. Each message is prefixed with its level. A nil l logs to os.Stderr with the standard flags.
type Metrics ¶
type Metrics interface {
// OnConnect is called when a TCP connection is established (client)
// or accepted (server).
OnConnect(remote net.Addr)
// OnDisconnect is called when a TCP connection ends. err is nil when
// the application closed it.
OnDisconnect(remote net.Addr, err error)
// OnPacketSent is called after an encapsulation packet was written.
// size is its length on the wire in octets.
OnPacketSent(remote net.Addr, cmd encap.Command, size int)
// OnPacketReceived is called for every encapsulation packet read.
OnPacketReceived(remote net.Addr, cmd encap.Command, size int)
// OnDecodeError is called when a packet, a common packet format or a
// CIP message could not be decoded and was dropped or refused.
OnDecodeError(remote net.Addr, err error)
}
Metrics is an optional callback interface for observing the sessions and the traffic of a client or a server. A nil Metrics disables collection.
The methods are called synchronously on the protocol path, possibly from several goroutines at once; implementations must be safe for concurrent use and must not block (increment a counter, send on a buffered channel).
Embed NopMetrics to implement only the callbacks you need and to stay compatible when methods are added.
type NopMetrics ¶
type NopMetrics struct{}
NopMetrics is a Metrics that does nothing. Embed it in your own type.
func (NopMetrics) OnDecodeError ¶
func (NopMetrics) OnDecodeError(net.Addr, error)
OnDecodeError does nothing.
func (NopMetrics) OnDisconnect ¶
func (NopMetrics) OnDisconnect(net.Addr, error)
OnDisconnect does nothing.
func (NopMetrics) OnPacketReceived ¶
OnPacketReceived does nothing.
func (NopMetrics) OnPacketSent ¶
OnPacketSent does nothing.
type RequestMetrics ¶
type RequestMetrics interface {
// OnRequest is called before a request is sent.
OnRequest(remote net.Addr, service cip.Service)
// OnRequestDone is called with the outcome. err is nil on success;
// classify failures with errors.Is and errors.As (*CIPError,
// ErrTimeout, ...).
OnRequestDone(remote net.Addr, service cip.Service, duration time.Duration, err error)
}
RequestMetrics is an optional extension of Metrics for a client. When the value given to client.WithMetrics also implements it, the client reports every explicit request.
type ServerMetrics ¶
type ServerMetrics interface {
// OnHandled is called after an explicit request was handled, with its
// general status.
OnHandled(remote net.Addr, service cip.Service, status cip.GeneralStatus, duration time.Duration)
// OnConnectionOpened is called after a Forward_Open succeeded.
OnConnectionOpened(remote net.Addr, info ConnectionInfo)
// OnConnectionClosed is called when a connection ends: by Forward_Close
// (err nil), by timeout (ErrTimeout), with its session, or on Close.
OnConnectionClosed(remote net.Addr, info ConnectionInfo, err error)
}
ServerMetrics is an optional extension of Metrics for a server. When the value given to server.WithMetrics also implements it, the server reports every request it handled and every connection it opened, closed or lost.
type State ¶
type State uint8
State is the state of a client's session or of an I/O connection.
const ( // StateDisconnected means there is no TCP connection, or the I/O // connection is closed. StateDisconnected State = iota // StateConnecting means the client is dialing and registering the // session, or the scanner is opening the I/O connection. StateConnecting // StateConnected means the session is registered, or the I/O // connection exchanges data. StateConnected // StateTimedOut means an I/O connection's consumed data stopped // arriving within the connection timeout. StateTimedOut )
Session and connection states.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package client is an EtherNet/IP originator of explicit messages.
|
Package client is an EtherNet/IP originator of explicit messages. |
|
Package eds reads, validates, writes and generates Electronic Data Sheets: the text files (.eds) in which a device vendor describes an EtherNet/IP device's identity, parameters, assemblies and I/O connections.
|
Package eds reads, validates, writes and generates Electronic Data Sheets: the text files (.eds) in which a device vendor describes an EtherNet/IP device's identity, parameters, assemblies and I/O connections. |
|
internal/sampledevice
Package sampledevice describes e2e adapter (vendor 65535, product code 42, revision 3.7), as its Electronic Data Sheet declares it.
|
Package sampledevice describes e2e adapter (vendor 65535, product code 42, revision 3.7), as its Electronic Data Sheet declares it. |
|
Package encap is the EtherNet/IP encapsulation codec: the 24-octet header, the commands and their status codes, the common packet format (CPF) and the items of ListIdentity, ListServices and the connected and I/O messages.
|
Package encap is the EtherNet/IP encapsulation codec: the 24-octet header, the commands and their status codes, the common packet format (CPF) and the items of ListIdentity, ListServices and the connected and I/O messages. |
|
examples
|
|
|
adapter
command
Command adapter runs an EtherNet/IP adapter with OpENer's sample layout: input assembly 100, output 150 (copied to the input), configuration 151, heartbeats 152 (input only) and 153 (listen only).
|
Command adapter runs an EtherNet/IP adapter with OpENer's sample layout: input assembly 100, output 150 (copied to the input), configuration 151, heartbeats 152 (input only) and 153 (listen only). |
|
discover
command
Command discover sends one ListIdentity request to a broadcast address (or one device) and lists the devices that answer:
|
Command discover sends one ListIdentity request to a broadcast address (or one device) and lists the devices that answer: |
|
eds
command
Command eds validates an EDS file and prints its identity, assemblies and connections, then the Go code eds.GenerateGo makes of it:
|
Command eds validates an EDS file and prints its identity, assemblies and connections, then the Go code eds.GenerateGo makes of it: |
|
io
command
Command io runs a Class 1 I/O connection between a scanner and an adapter in the same process: the adapter copies its output assembly to its input assembly, the scanner sends a counter and reads it back.
|
Command io runs a Class 1 I/O connection between a scanner and an adapter in the same process: the adapter copies its output assembly to its input assembly, the scanner sends a counter and reads it back. |
|
logix
command
Command logix reads and writes Logix tags.
|
Command logix reads and writes Logix tags. |
|
quickstart
command
Command quickstart starts an EtherNet/IP adapter and talks to it with a client in the same process: identity, attributes, a Multiple Service Packet and a Class 3 connection.
|
Command quickstart starts an EtherNet/IP adapter and talks to it with a client in the same process: identity, attributes, a Multiple Service Packet and a Class 3 connection. |
|
scanner
command
Command scanner opens an exclusive owner connection to an adapter with OpENer's sample layout (config 151, output 150, input 100, 32 octets) and prints its inputs for ten seconds:
|
Command scanner opens an exclusive owner connection to an adapter with OpENer's sample layout (config 151, output 150, input 100, 32 octets) and prints its inputs for ten seconds: |
|
Package implicit is EtherNet/IP implicit (I/O) messaging: the codec of Class 0 and Class 1 datagrams, a UDP Endpoint that receives them on port 2222 and dispatches them by connection ID, and the Connection that produces and consumes the data of one connection at its requested packet intervals, with sequence handling, run/idle and the inactivity watchdog.
|
Package implicit is EtherNet/IP implicit (I/O) messaging: the codec of Class 0 and Class 1 datagrams, a UDP Endpoint that receives them on port 2222 and dispatches them by connection ID, and the Connection that produces and consumes the data of one connection at its requested packet intervals, with sequence handling, run/idle and the inactivity watchdog. |
|
internal
|
|
|
cmd/gencatalog
command
Command gencatalog generates the code and documentation that follow from the CIP catalog, cip/catalog.json, and the vendor list, cip/vendors.json:
|
Command gencatalog generates the code and documentation that follow from the CIP catalog, cip/catalog.json, and the vendor list, cip/vendors.json: |
|
cmd/genmatrix
command
Command genmatrix renders the interop results the interop suite writes (interop/results/matrix.json, with ETHERNETIP_INTEROP_WRITE_MATRIX=1) into INTEROPERABILITY.md, between the markers "<!-- interop-matrix:begin -->" and "<!-- interop-matrix:end -->".
|
Command genmatrix renders the interop results the interop suite writes (interop/results/matrix.json, with ETHERNETIP_INTEROP_WRITE_MATRIX=1) into INTEROPERABILITY.md, between the markers "<!-- interop-matrix:begin -->" and "<!-- interop-matrix:end -->". |
|
cmd/gensample
command
Command gensample generates eds/internal/sampledevice/device_gen.go from eds/testdata/otfabric_adapter.eds with eds.GenerateGo: the generator's output, committed, compiled and tested like any package.
|
Command gensample generates eds/internal/sampledevice/device_gen.go from eds/testdata/otfabric_adapter.eds with eds.GenerateGo: the generator's output, committed, compiled and tested like any package. |
|
cmd/interopnode
command
Command interopnode is go-ethernetip behind the container contract of otfabric/ethernetip-interop: an adapter ("server") that serves a fixture, and an originator ("client") that runs one operation and prints one JSON result.
|
Command interopnode is go-ethernetip behind the container contract of otfabric/ethernetip-interop: an adapter ("server") that serves a fixture, and an originator ("client") that runs one operation and prints one JSON result. |
|
logx
Package logx is how the library writes its log entries: a constant message plus key-value fields.
|
Package logx is how the library writes its log entries: a constant message plus key-value fields. |
|
Package logix implements the Rockwell Automation Logix extensions of CIP: symbolic tag services (Read Tag, Write Tag, their fragmented forms, Read-Modify-Write), the tag list of the Symbol object, the Template object and the decoding of structures (UDTs).
|
Package logix implements the Rockwell Automation Logix extensions of CIP: symbolic tag services (Read Tag, Write Tag, their fragmented forms, Read-Modify-Write), the tag list of the Symbol object, the Template object and the decoding of structures (UDTs). |
|
Package scanner is the originator (scanner) of EtherNet/IP implicit I/O connections: it opens Class 1 (or Class 0) connections to a target's assemblies with Forward_Open over a client session, exchanges their data cyclically over UDP, detects their timeout and, when asked to, reopens them.
|
Package scanner is the originator (scanner) of EtherNet/IP implicit I/O connections: it opens Class 1 (or Class 0) connections to a target's assemblies with Forward_Open over a client session, exchanges their data cyclically over UDP, detects their timeout and, when asked to, reopens them. |