Help us shape the future of Go! Take the 2026 Go Developer Survey

ethernetip

package module
v0.3.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 26, 2026 License: MIT Imports: 12 Imported by: 0

README

go-ethernetip - EtherNet/IP and CIP Scanner and Adapter Library for Go

Go Version Go Reference CI Status Interop Code Coverage

A pure Go implementation of EtherNet/IP and the Common Industrial Protocol (CIP), the industrial Ethernet protocol of programmable controllers, I/O adapters, drives and sensors. No cgo, no dependencies outside the standard library.

The library provides:

  • A client (originator of explicit messages): sessions, unconnected requests (SendRRData), routing through Unconnected_Send, connected explicit messaging over Class 3 connections (SendUnitData), Multiple Service Packet, Forward_Open, Large_Forward_Open and Forward_Close, ListIdentity, ListServices and ListInterfaces over TCP and UDP, discovery
  • A scanner (originator of implicit I/O): Class 1 and Class 0 connections to a target's assemblies, cyclic UDP exchange on port 2222, run/idle, sequence handling, timeout detection, automatic reopening, point-to-point and multicast T->O, exclusive owner, input-only and listen-only connection points
  • A server (target, adapter): the Message Router with the Identity, Message Router, Assembly, Connection Manager, Port, TCP/IP Interface and Ethernet Link objects, user-defined objects, Class 0/1/3 connections with ownership rules, multicast production shared between connections, every Connection Manager refusal with its extended status, resource limits
  • Rockwell Logix extensions (vendor specific, in package logix): tag reads and writes by name, fragmented reads and writes, Read-Modify-Write for bits, the tag list, templates and UDT decoding; and an emulation of a Logix tag database on the server, for tests and simulators
  • Electronic Data Sheets: parser, validation, writer, an EDS builder, and a generator of Go constants, assembly structs and scanner configurations from an EDS file
  • EtherNet/IP over TLS (the transport of CIP Security, port 2221) for explicit messaging, with crypto/tls
  • What production use needs: context.Context on every call, typed and sentinel errors with the peer's general and extended status, structured logging, metrics hooks, bounded allocations on every decoder, connection and session limits
  • Verified against twelve independent implementations: OpENer, EIPScanner, pycomm3, cpppo, libplctag, gologix, pylogix, etherip, eip-rs, node-ethernet-ip, EEIP.NET and loki-os's go-ethernet-ip, in both roles where they have them, in the containers of otfabric/ethernetip-interop (see INTEROPERABILITY.md)

Docs: API.md · ARCHITECTURE.md · INTEROPERABILITY.md (protocol coverage and interop results) · ERRORS.md · OBSERVABILITY.md · SECURITY.md · TESTING.md · CATALOG.md (CIP codes and their sources) · RELEASE.md · guides in docs/

Table of contents

go-ethernetip vs the reference stacks

go-ethernetip is tested against twelve independent implementations (see Interop tests). Each suits different needs; this table shows where they differ, from their public sources, the ethernetip-interop capability records and the interop runs, so you can pick the right one for your project, and see where more interop or more features would help.

Capability go-ethernetip OpENer EIPScanner pycomm3 cpppo libplctag gologix pylogix etherip eip-rs node-ethernet-ip EEIP.NET go-ethernet-ip (loki-os)
Project
Language Go C C++ Python Python C Go Python Java Rust TypeScript C# Go
Licence MIT adapted BSD MIT MIT GPL-3.0 MPL-2.0 or LGPL-2.0+ MIT Apache-2.0 EPL-1.0 MIT MIT MIT WTFPL
Version compared this module master 0e1ab7e 1.3.0+33 1.2.16 5.2.5 2.7.2 0.44.0-beta 1.1.6 main ce0688a main 16e2236 2.0.0 master 58b91a0 master 535f797
Originator (scanner, client) ✅ — ✅ ✅ ✅ ✅ ✅ ✅ ✅ ✅ ✅ ✅ ✅
Target (adapter, server) ✅ ✅ — — Logix simulator ab_server simulator Logix tags, Class 1 I/O — — — — — —
Dependencies none (standard library) none none none greenery none crc16 none none (JRE) tokio and others none (Node.js) none (.NET) none
Discovery and encapsulation
ListIdentity over TCP C, T ✅ — ✅ C, T — C, T — ✅ ✅ ✅ — ✅
ListIdentity over UDP, discovery C, T; broadcast ✅ ✅ ✅ C, T — — broadcast only ✅ ✅ ✅ unicast ¹ —
ListServices C, T ✅ — — C, T — C ² — ✅ TCP — — ✅
ListInterfaces C, T ✅ — — T — — — — — — — not tested
EtherNet/IP over TLS (port 2221) explicit messaging — — — — — — — — — — — —
CIP Security beyond TLS (DTLS, security objects) — (open issue) — — — — — — — — — — — —
Explicit messaging
Get_Attribute_Single C, T ✅ ✅ ✅ C, T C ³ C ² ✅ ✅ ✅ ✅ ✅ ✅
Set_Attribute_Single C, T ✅ ✅ ✅ C, T C ³ C ² ✅ — ✅ ✅ ✅ ✅
Get_Attributes_All C, T ✅ ✅ ✅ C, T C ³ C; T (Identity) ✅ ✅ ✅ ✅ ✅ ✅
Get_Attribute_List C, T ✅ — — T — — — — — — — —
Multiple Service Packet C, T — C (adapter-built) Logix reads C, T C: tag reads; T C; T: Class 3 tag services tag reads tag reads ✅ ✅ — ✅
Unconnected_Send routing C, T — — ✅ C, T T ³ T ✅ ✅ ✅ — — ✅
Class 3 connected explicit messaging C, T ✅ — ✅ T C, T C (always), T Logix only — ✅ ✅ — ✅
Large_Forward_Open C, T ✅ ✅ ✅ T C, T C, T ✅ — — ✅ ✅ not used
Identity and Assembly objects (target) ✅ ✅ n/a n/a Identity; assemblies as attributes — Identity only n/a n/a n/a n/a n/a n/a
TCP/IP Interface, Ethernet Link objects (target) ✅ ✅ n/a n/a — — — n/a n/a n/a n/a n/a n/a
Implicit (Class 0/1) I/O
Class 1 exclusive owner C, T T C — — — T — — — — C —
Class 0 C, T not tested not tested — — — not tested — — — — not tested —
Input-only (heartbeat) C, T T C — — — T — — — — C —
Listen-only C, T T ⁴ — — — — — — — — — C —
T->O multicast C, T T — — — — — — — — — C —
Configuration data in the Forward_Open C, T T C — — — T: ignored — — — — — —
Large_Forward_Open for I/O C, T T C — — — T: no data — — — — C —
Rockwell Logix (vendor specific)
Read Tag, Write Tag client and emulation — — ✅ C, T C, T C, T ✅ ✅ ✅ ✅ — reads; scalar writes ⁸
Read Tag Fragmented client and emulation — — ✅ C, T C, T partial ✅ — ✅ — — —
Write Tag Fragmented client and emulation — — ✅ T T; C above 4000 octets — above 3.9 kB — ✅ — — —
Several tags in one Multiple Service Packet client and emulation — — ✅ T C, T C, T ✅ ✅ ✅ ✅ — ✅
LINT, LREAL client and emulation — — ✅ T C, T C, T ✅ — ✅ ✅ — ✅
Tag list (0x55) client and emulation — — ✅ — C C ⁵ ✅ — ✅ ✅ — ✅
STRING client and emulation — — ✅ not tested C, T C ✅ ✅ ✅ ✅ — ✅
Structures (templates): read client and emulation — — ✅ — raw octets raw octets raw octets — ✅ ✅ — raw octets
Structures: write client and emulation — — ✅ — members only members only members only — ✅ members only ⁶ — —
Bit writes on integers (Read-Modify-Write) client and emulation — — ✅ — C — ✅ — ✅ ✅ — —
BOOL array elements client and emulation — — ✅ not tested own form ⁷ — ✅ — ✅ — — —
Tooling and API
EDS files: parse, validate, write, generate Go code ✅ — — — — — — — — — — — —
Cancellation and deadlines per call context.Context — — socket timeout — timeout per call not tested socket timeout timeouts async (tokio) promises — —
Structured logging, metrics Logger (slog), Metrics own logger own logger logging logging debug levels log/slog print Java logging not tested not tested console not tested
Testing
Tested against go-ethernetip (matrix) — 17 pass 11 pass 18 pass 17 pass 17 pass 19 pass 14 pass 9 pass 21 pass 19 pass 11 pass 11 pass
Tested against other stacks in CI ✅ all twelve, both directions not tested not tested not tested not tested not tested not tested not tested not tested not tested not tested not tested not tested
ODVA conformance tested no, and no claim not tested not tested not tested not tested not tested not tested not tested not tested not tested not tested not tested not tested

✅ = available in the roles the stack has · C = as originator (client, scanner) · T = as target (adapter, server) · — = not available · n/a = the stack has no target role · not tested = neither documented nor exercised by the interop suite. For the versions go-ethernetip is tested against, as of October 2026.

¹ EEIP.NET's own ListIdentity broadcasts without SO_BROADCAST and fails on Linux; the adapter sends one unicast datagram built and parsed by EEIP.NET. ² gologix's target answers ListServices with the wrong command and Get_Attribute_Single with malformed replies; Set_Attribute_Single only on its own class 0xC8. ³ libplctag 2.7.2 builds unconnected requests without their route path, so its attribute requests work over Class 3 only; ab_server has no attributes. ⁴ OpENer stops the shared production and drops the owner when a listen-only connection closes first. ⁵ gologix's target answers the tag list without the requested attributes. ⁶ node-ethernet-ip writes a whole structure with the template instance ID instead of the structure handle (refused 0x2107). ⁷ libplctag writes a BOOL array element as Bits[1].5 (DWORD, bit), not Bits[37]. ⁸ go-ethernet-ip writes a tag only whole (all elements, by instance), so scalar tags only; it does not check the reply of a write.

The interop counts are the cases each stack passes against go-ethernetip, in both directions where it has a target, of those it declares; every documented limit is listed with its reason in the matrix. If you spot an inaccuracy, please open an issue and we will correct it.

Install

go get github.com/otfabric/go-ethernetip

Requires Go 1.23 or later.

Project structure

Package What it is
ethernetip Shared types: sentinel and typed errors, Logger, Metrics, State
client Originator of explicit messages; discovery
scanner Originator of Class 0/1 I/O connections
server Target (adapter): objects, sessions, connections
logix Rockwell Logix tag services, client and emulation (vendor specific)
eds Electronic Data Sheets: parse, validate, format, build, generate Go
cip CIP codec: data types, EPATHs, messages, Connection Manager services, generated catalog of codes
encap Encapsulation codec: header, commands, common packet format
implicit I/O datagram codec, UDP endpoint, connection runtime

cip, encap and the codec half of implicit are pure: useful on their own to analysers and test tools. See ARCHITECTURE.md.

Client quickstart

ctx := context.Background()
c, err := client.Dial(ctx, "192.168.1.10") // port 44818
if err != nil {
    log.Fatal(err)
}
defer c.Close()

id, err := c.Identity(ctx) // Get_Attributes_All of the Identity object
fmt.Println(id)            // OpENer PC (vendor 1, Communications Adapter, product 65001, rev 2.3, ...)

data, err := c.GetAttributeSingle(ctx, cip.ClassAssembly, 100, 3)

// Several requests in one Multiple Service Packet.
resps, err := c.MultipleService(ctx, []cip.Request{
    {Service: cip.ServiceGetAttributeSingle, Path: cip.AttributePath(cip.ClassIdentity, 1, 1)},
    {Service: cip.ServiceGetAttributeSingle, Path: cip.AttributePath(cip.ClassIdentity, 1, 7)},
})

// Connected explicit messaging (Class 3).
cn, err := c.OpenConnection(ctx, client.ConnectionOptions{})
name, err := cn.GetAttributeSingle(ctx, cip.ClassIdentity, 1, 7)
_ = cn.Close(ctx)

A controller behind a communication module is reached with a route: client.WithRoute(route) where route, _ := cip.ParseRoute("1/0") (port 1, the backplane, slot 0) sends every request through the module's Unconnected_Send.

client.Discover(ctx, client.DiscoverOptions{Address: "192.168.1.255"}) sends one ListIdentity datagram and lists the devices that answer.

I/O quickstart

s, err := scanner.New(c) // UDP port 2222
io, err := s.Open(ctx, scanner.IOConfig{
    ConfigInstance: 151, OutputInstance: 150, InputInstance: 100,
    OutputSize: 32, InputSize: 32, RPI: 10 * time.Millisecond,
    Reconnect: &scanner.Reconnect{}, // reopen after a timeout
})
_ = io.SetOutput(out)       // sent from the next interval on
in, run, at := io.Input()   // the last input and when it arrived
<-io.Done()                 // only without Reconnect, after a timeout
_ = io.Close(ctx)           // Forward_Close

Adapter quickstart

srv, err := server.New(server.WithIdentity(cip.Identity{
    VendorID: 0xFFFF, DeviceType: cip.DeviceTypeCommunicationsAdapter,
    ProductCode: 1, Revision: cip.Revision{Major: 1, Minor: 0},
    SerialNumber: 42, ProductName: "My adapter",
}))
in, _ := srv.AddAssembly(100, server.AssemblyInput, 32)
out, _ := srv.AddAssembly(150, server.AssemblyOutput, 32)
_, _ = srv.AddAssembly(151, server.AssemblyConfig, 10)
out.OnWrite(func(data []byte, run bool) { /* drive the outputs */ })
_ = in.SetData(inputs) // produced to every connection from the next RPI
log.Fatal(srv.ListenAndServe(":44818")) // TCP+UDP 44818, I/O UDP 2222

Objects of your own implement server.Object, or are built from server.NewObject(class, revision) with attributes and services.

Logix tags

The logix package speaks the Rockwell Automation extensions, which only Logix controllers (and compatible devices) implement:

route, _ := cip.ParseRoute("1/0")
c, _ := client.Dial(ctx, "192.168.1.10", client.WithRoute(route))
lc := logix.NewClient(c)
v, err := lc.Read(ctx, "Program:Main.Speeds[3]", 1)
f, _ := v.Float(0)
dv, _ := logix.Ints(cip.TypeDINT, 42)
err = lc.Write(ctx, "Counter", dv)
tags, err := lc.ListTags(ctx, "")

logix.NewTagServer() attached to a server.Server emulates a controller's tags (symbolic and instance addressing, fragmentation, templates), enough for pycomm3 and cpppo to work against it. See docs/LOGIX.md.

EDS files

f, err := eds.Parse(file)
for _, issue := range eds.Validate(f) { fmt.Println(issue) }
src, err := eds.GenerateGo(f, eds.GenerateOptions{Package: "drive"})

The generated package has the identity, a descriptor and range check per parameter, enumerations, assembly instances with Encode/Decode structs, and a scanner.IOConfig per I/O connection. See docs/EDS.md.

Error semantics

Errors are sentinels tested with errors.Is (ethernetip.ErrTimeout, ErrConnectionLost, ErrConnectFailed, ErrProtocol, ErrSession, ErrUnsupported, ...) and typed errors for the peer's answers, matched with errors.As: *ethernetip.CIPError (general status, extended status, service and path) and *ethernetip.EncapError (encapsulation status). A response with an error status is returned together with its error. See ERRORS.md.

Examples

Runnable programs under examples/: quickstart, io, logix and eds run both sides in one process; adapter, scanner and discover work with real devices.

go run ./examples/quickstart

Development

make check      # generated files, format, lint, vet, govulncheck, race tests, coverage gate, examples
make test       # tests with the race detector
make coverage-check   # combined coverage of the library packages, 90% minimum
make fuzz       # every fuzz target (FUZZTIME=30s each)
make chaos      # chaos suites, 10 times (ETHERNETIP_CHAOS_SEED repeats a run)
make stress     # parallel and stress suites, 10 times, race detector
make bench      # codec and end-to-end benchmarks
make generate   # regenerate the CIP catalog and the sample EDS package
make interop    # the interop suite (Docker)

Tests need nothing but Go: clients, scanners and servers run against each other over loopback. See TESTING.md.

Interop tests

make interop runs the library's originator against the reference targets and the reference originators against the library's target, in the containers of otfabric/ethernetip-interop. The library runs in a container on the same Docker network as the references (I/O connections need both ends to reach each other without NAT).

Reference Version Licence Roles tested
OpENer master 0e1ab7e (v2.3+591) adapted BSD target (adapter)
EIPScanner 1.3.0+33 (12c89a5) MIT originator (scanner)
pycomm3 1.2.16 MIT originator (generic CIP and Logix)
cpppo 5.2.5 GPL-3.0 target (Logix simulator) and originator
libplctag 2.7.2 MPL-2.0 or LGPL-2.0+ originator (Logix and generic CIP) and target (ab_server simulator)
gologix 0.44.0-beta MIT originator and target (Logix tags, Class 1 I/O)
pylogix 1.1.6 Apache-2.0 originator (Logix)
etherip main ce0688a EPL-1.0 originator (Logix, generic)
eip-rs (rseip) main 16e2236 MIT originator (generic CIP and Logix)
node-ethernet-ip 2.0.0 MIT originator (Logix, generic)
EEIP.NET master 58b91a0 MIT originator (scanner: explicit, Class 1 I/O)
go-ethernet-ip (loki-os) master 535f797 WTFPL originator (generic CIP and Logix)

The coverage matrix lists each feature per reference and direction, with the features a reference does not have marked N/A. The reference code runs in its own container only: nothing of it is linked into this module.

Standards, licensing and conformance

EtherNet/IP and CIP are specified by ODVA (The CIP Networks Library, Volumes 1, 2 and 8), sold by subscription. This project does not have them: it is built from publicly available information and the behaviour of the independent implementations above, and every code it names is listed with its public source in CATALOG.md. EtherNet/IP and CIP are trademarks of ODVA. Products that implement EtherNet/IP need an ODVA vendor ID and the ODVA Terms of Usage; ODVA conformance requires its conformance test. This library is not ODVA conformance tested and makes no conformance claim. See docs/STANDARDS.md.

Limitations

  • CIP Security: EtherNet/IP over TLS for explicit messaging only; DTLS for I/O and the CIP Security, EtherNet/IP Security and Certificate Management objects are not implemented (no DTLS in the standard library, no reference peer in the interop suite).
  • No routing beyond the device: the server answers routes to its own slot; it does not bridge to other ports.
  • Class 2 connections, safety (CIP Safety), motion (CIP Motion), DLR, QoS and the File object are not implemented.
  • Multicast I/O, with a listen-only connection next to its owner, is verified against OpENer and EEIP.NET and between the library's own ends.
  • The Logix emulation is a test tool: no programs, no tasks, no online edits, no alarms.
  • See docs/LIMITATIONS.md for the complete list.

License

MIT. The reference implementations the interop tests run are under their own licences and are not part of this module; see THIRD_PARTY_NOTICES.md.

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:

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

View Source
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

func StatusOf

func StatusOf(err error) (cip.GeneralStatus, bool)

StatusOf returns the CIP general status of err when it is or wraps a *CIPError, and false otherwise.

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

func (e *CIPError) Error() string

Error describes the failure, for example "ethernetip: Get_Attribute_Single @1/1/9: attribute not supported (0x14)".

func (*CIPError) ExtendedStatus

func (e *CIPError) ExtendedStatus() (uint16, bool)

ExtendedStatus returns the first additional status word, and false when there is none.

func (*CIPError) Is

func (e *CIPError) Is(target error) bool

Is makes a CIPError with status 0x08 (service not supported) or 0x14 (attribute not supported) match ErrUnsupported.

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

func NewSlogLogger(h slog.Handler) Logger

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

func NewStdLogger(l *log.Logger) Logger

NewStdLogger wraps a standard library logger. Each message is prefixed with its level. A nil l logs to os.Stderr with the standard flags.

func NopLogger

func NopLogger() Logger

NopLogger returns a Logger that discards everything.

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) OnConnect

func (NopMetrics) OnConnect(net.Addr)

OnConnect does nothing.

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

func (NopMetrics) OnPacketReceived(net.Addr, encap.Command, int)

OnPacketReceived does nothing.

func (NopMetrics) OnPacketSent

func (NopMetrics) OnPacketSent(net.Addr, encap.Command, int)

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.

func (State) String

func (s State) String() string

String returns "disconnected", "connecting", "connected" or "timed-out".

Directories

Path Synopsis
Package client is an EtherNet/IP originator of explicit messages.
Package client is an EtherNet/IP originator of explicit messages.
eds
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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL