enip

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Oct 11, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

README

enip

EtherNet/IP in pure Go: a client (scanner) and an adapter (server) for Logix controller tags, tag browsing, structures, CIP object attributes and class 1 implicit I/O.

Go Reference test Go

The module reads and writes Logix controller tags by name and reads and sets CIP object attributes on any CIP device, over a class 3 connection (Forward Open or Large Forward Open) or unconnected, directly or through a backplane route. It discovers devices with ListIdentity over UDP. Since v0.2.0 it browses a controller's tags and structure definitions (ListTags, ReadTemplate), runs class 1 I/O as a scanner (OpenIO), and the server package is the adapter role: a Logix-like target with tags, structures, browsing, member access and class 1 assemblies. It has no cgo and no dependency beyond the standard library and lanbcast. It was written for SigmaM2M, a oneM2M platform, where it serves the EtherNet/IP integration; it does not depend on it.

Features

Feature Status
Encapsulation: RegisterSession, UnRegisterSession, SendRRData, SendUnitData, ListIdentity over TCP and UDP yes (tested against cpppo)
Unconnected Send with a route (backplane port and slot, or a network address) yes (tested against cpppo)
Forward Open, Large Forward Open, Forward Close; class 3 connected messaging; reconnection after the target drops an idle connection; fallback to unconnected yes (tested against cpppo)
Multiple Service Packet (ReadTags) yes (tested against cpppo)
Logix Read Tag, Write Tag, Read Tag Fragmented, Write Tag Fragmented; program-scoped tags, members, array indexes yes (tested against cpppo)
Types: atomic types, STRING, structures as raw bytes, by named member offsets, or decoded through their template (Template.DecodeStruct, nested structures, packed BOOLs, STRING members) yes (Logix STRING and structures tested against pycomm3 1.2.16, which read and wrote them on this module's adapter)
Get_Attribute_Single, Get_Attributes_All, Set_Attribute_Single; any other service through Do yes
Status texts for general and extended CIP status and encapsulation status yes
Tag browsing: the Symbol object's tag list, controller and program scope, paged (ListTags); structure definitions from the Template object (ReadTemplate) yes (since v0.2.0; client side against this module's adapter only, see Not yet)
Adapter (server) role (server): tag table with program scopes, structures (DefineStruct, CheckStruct), member access ("Tag.Member.Sub", BOOL bits), the Symbol and Template objects, symbol instance addressing, class 3 connections, Multiple Service Packet, fragmentation, attributes, ListIdentity yes (since v0.2.0; was enip-sim's; pycomm3 1.2.16 browsed and used it)
Implicit (class 1) I/O: scanner (OpenIO: Forward Open on assembly connection points, cyclic O→T with the run/idle header, T→O inputs, timeouts) and adapter (AddAssembly, SetAssembly, Assembly) yes (since v0.2.0; scanner and adapter against each other only, see Not yet)

Choosing an EtherNet/IP library

Compared from each project's source at the version named, in October 2026. "Tested" means run against this module in the interop tests below. Check the projects themselves before relying on a row.

this module gologix v0.44.0-beta pycomm3 1.2.16 cpppo 5.2.5 libplctag 2.7.2
Language Go, no cgo Go Python Python C (Go use through cgo)
Licence Apache-2.0 MIT MIT GPL MPL-2.0 or LGPL-2
Logix tag read and write, fragmented yes (tested against cpppo) yes yes (tested against this module's simulator) yes yes
Multiple Service Packet yes yes yes yes yes
Large Forward Open yes yes yes yes yes
Tag browsing, UDT definitions yes yes yes not checked yes (tag listing)
Implicit I/O yes, scanner and adapter yes (server side) no not checked not checked
Server (adapter) yes, with browsing (tested with pycomm3); simulator in enip-sim yes (a Logix-like server) no yes (tested) no
PCCC, other PLC families no not checked SLC/PLC-5 (PCCC) not checked yes

Pick this module for a Go program without cgo that reads, writes and browses Logix tags and structures, CIP attributes and class 1 I/O, or that must act as an EtherNet/IP adapter a Logix-style client can browse. Pick gologix if its I/O server has been checked against your scanner (this module's I/O is not yet checked against another implementation); pick libplctag when cgo is fine and you need PCCC or many PLC families; pick pycomm3 in Python.

Install

go get github.com/cB-Sigma/enip@latest

Requires Go 1.22 or later.

Quick start

import "github.com/cB-Sigma/enip"

route, _ := enip.Route("1,0") // backplane port 1, slot 0
c := &enip.Client{Addr: "192.0.2.20:44818", Route: route, Timeout: 2 * time.Second, Connected: true}
defer c.Close()
ty, data, err := c.ReadTag(ctx, "Program:Main.Flow", 1)
v, _ := enip.Decode(ty, data, 1, nil)
err = c.WriteTag(ctx, "Setpoint", enip.Type{Code: enip.TypeREAL}, 1, b)
id, err := c.ListIdentity(ctx)

Browsing and the adapter (v0.2.0):

tags, _ := c.ListTags(ctx, "")              // controller scope; ListTags(ctx, "Main") for a program
tmpl, _ := c.ReadTemplate(ctx, tags[0].Template)
_, data, _ := c.ReadTag(ctx, tags[0].Name, 1)
v, _ := tmpl.DecodeStruct(data, nil)       // map of member name to value

s := server.New()
s.DefineStruct(server.StructDef{Name: "Motor", Handle: 0x2002, Size: 8, Fields: fields})
s.SetTag("Pump", enip.Type{Code: enip.TypeStruct, Handle: 0x2002}, 1, raw)
s.Start(":44818")

TestListTagsAndTemplates and TestImplicitIO (server/) run these. The runnable examples in this module are ExampleEncodeString and ExampleRoute; enip-sim has a client example against the simulated controller.

Documentation

API reference: https://pkg.go.dev/github.com/cB-Sigma/enip

Package What it does
enip (module root) Client (ReadTag, ReadTags, WriteTag, GetAttribute, SetAttribute, ListIdentity, Do, Mode), Discover, Route, TagPath, Decode/Encode with Type and Member, DecodeString/EncodeString, ParseTypeName, AtomicTypeCode, the encapsulation and CIP codecs (Header, Item, Request, Response, Error, StatusText)
enip (v0.2.0 additions) ListTags, TagInfo, ReadTemplate, Template, TemplateMember, DecodeStruct; OpenIO, IOConfig, IOConn, IOPacket, ParseIOPacket
server the adapter role: New, Server (SetTag, SetReadOnly, SetAttr, SetIdentity, DefineStruct, CheckStruct, AddAssembly, SetAssembly, Assembly, Start, Stop, …), StructDef, Field, StringDef
interop (nested module, tests only) this module's client against cpppo 5.2.5, and pycomm3 1.2.16 against the adapter, in Docker

Testing

go vet ./...
go test -race ./...

Tests carry // Covers: SIG-... comments: requirement IDs of the SigmaM2M project's compliance report, with no meaning outside it. The tests that drive the simulated controller are in enip-sim, so this module does not depend on its simulator.

interop/ is a nested module with its own go.mod. It runs cpppo 5.2.5 (GPL, installed from PyPI in a container, used only as a test peer) as a simulated Logix controller and runs this module's client against it. The tests are skipped unless SIGMA_ENIP_INTEROP=1 is set and Docker is running:

cd interop && SIGMA_ENIP_INTEROP=1 go test -race -v ./...
Interop tested against

Run on 2026-10-11 on macOS with Docker Desktop; every item below passed. cpppo 5.2.5's EtherNet/IP server, this module's client, unconnected without a route, over Forward Open (500 octets), over Large Forward Open (4000 octets), and through route 1/2 both unconnected and connected:

Area What was checked
Identity ListIdentity over TCP, and over UDP through Discover; Get_Attribute_Single and Get_Attributes_All on the identity object
Tags REAL, DINT, LREAL and SINT written and read back; an INT[10] array and one element of it; SSTRING and CIP STRING as raw bytes
Fragmentation a DINT[300] written with Write Tag Fragmented and read with Read Tag Fragmented (the read routed or connected, see below)
Multiple Service Packet several tags in one request, an unknown tag answered with its error in place, a member needing fragmentation
Errors an unknown tag (cpppo answers with CIP status, or encapsulation status 0x08 unconnected); a route to an empty slot refused
Connections mode reported as connected; Large Forward Open negotiated 3998 octets

Found on the way and fixed in this module (v0.1.0): cpppo puts our own O→T connection ID in the address item of its connected replies, where the T→O ID is expected, and the client refused every reply. The client now accepts either of its own two IDs and still refuses any other connection (TestConnectedReplyWithEchoedOTID).

Behaviour of the reference peer met on the way (not ours): cpppo treats any unrouted unconnected service 0x52 as Unconnected Send, so a fragmented read sent straight to it, unrouted and unconnected, cannot be tested; it wants a pad octet after an odd-length CIP STRING in a write.

pycomm3 1.2.16's LogixDriver against this module's adapter role (2026-10-12, python:3.12.10-slim in Docker; interop/pycomm3_test.go, our script in interop/pycomm3), opened with its own tag upload (init_tags, init_program_tags); every item passed:

Area What was checked
Browsing its tag list of controller and program scope (5 user tags, the program entry recognised); the Motor UDT's template (members in order, 116 bytes, a nested PID and a STRING member, packed BOOLs in a hidden host byte); the built-in STRING's template (82 characters)
Reads the whole UDT decoded by pycomm3 (BOOLs, REAL, DINT[3], nested structure, STRING), a member, a STRING tag, a program-scoped DINT and an INT[10], by symbol instance (pycomm3 chose instance addressing)
Writes a REAL member, a BOOL member (the bit in the host byte), a STRING tag and a program-scoped tag, read back by pycomm3 and checked in the adapter's bytes; a read-only tag refused with Permission denied

Found on the way (in this module's own test, not in pycomm3): the test's UDT had a member overlapping the next; CheckStruct now reports such layouts (TestCheckStruct).

Not tested against an independent implementation:

  • This module's ListTags and ReadTemplate against another adapter (cpppo has no Symbol or Template object; a real controller is hardware).
  • Class 1 I/O: scanner and adapter are tested against each other only. gologix's I/O server binds UDP 2222 itself, which a scanner on the same host cannot share; a run in separate containers is next.
  • Whether a route inside the Forward Open path is checked (cpppo checks routes on Unconnected Send only).
  • Real controllers (ControlLogix, CompactLogix) and other CIP devices.
  • In process: the simulated controller of enip-sim (same author, so not independent). pycomm3 1.2.16 is run against that simulator in enip-sim's interop tests.

Not yet

What Why
Class 1 I/O against another implementation (gologix's server, a real adapter) see above; planned with both peers in containers
Class 0 and multicast T→O, input-only and listen-only connections, Forward Close of I/O connections, configuration assembly data in the Forward Open the point-to-point class 1 case comes first; these follow
ListServices, ListInterfaces, CIP Security (TLS and DTLS) not started
PCCC (SLC, PLC-5, MicroLogix), Modbus not started
Read-Modify-Write not started
Real controllers needs hardware
  • enip-sim: a simulated Logix controller, as a Go package and a command.
  • lanbcast: the LAN broadcast helper used by Discover.

Status

v0.x: the API may change before v1.

Contributing

See CONTRIBUTING.md.

Security

See SECURITY.md to report a vulnerability privately.

Licence

Apache-2.0 (see LICENSE and NOTICE). Copyright 2026 Abhinav Gautam. EtherNet/IP and CIP are trademarks of ODVA; Logix, ControlLogix and CompactLogix are trademarks of Rockwell Automation. This project is not affiliated with or endorsed by either.

Documentation

Overview

Package enip is an EtherNet/IP client with CIP explicit messaging, in pure Go: the encapsulation layer (sessions, SendRRData, SendUnitData, ListIdentity over TCP and UDP), CIP requests and replies with paths, routes and Multiple Service Packets, Unconnected Send, Forward Open and Large Forward Open (class 3 connections), the Logix tag services with fragmentation, the attribute services, and the Logix atomic, STRING and structure type encodings.

The simulator in package sim behaves like a Logix controller and is used by the tests.

Index

Examples

Constants

View Source
const (
	ClassSymbol                 uint16 = 0x6B
	ClassTemplate               uint16 = 0x6C
	SvcGetAttributeList         byte   = 0x03
	SvcGetInstanceAttributeList byte   = 0x55
)

Logix objects used to browse tags and structures.

View Source
const (
	SvcGetAttributesAll    byte   = 0x01
	SvcMultipleService     byte   = 0x0A
	SvcGetAttributeSingle  byte   = 0x0E
	SvcSetAttributeSingle  byte   = 0x10
	SvcForwardClose        byte   = 0x4E
	SvcReadTag             byte   = 0x4C
	SvcWriteTag            byte   = 0x4D
	SvcReadTagFragmented   byte   = 0x52 // to a symbol
	SvcUnconnectedSend     byte   = 0x52 // to the connection manager
	SvcWriteTagFragmented  byte   = 0x53
	SvcForwardOpen         byte   = 0x54
	SvcLargeForwardOpen    byte   = 0x5B
	ReplyBit               byte   = 0x80
	ClassIdentity          uint16 = 0x01
	ClassMessageRouter     uint16 = 0x02
	ClassConnectionManager uint16 = 0x06
)

CIP service codes used here.

View Source
const (
	StatusSuccess          byte = 0x00
	StatusConnFailure      byte = 0x01
	StatusPathSegment      byte = 0x04
	StatusPathUnknown      byte = 0x05
	StatusPartial          byte = 0x06
	StatusServiceNotSup    byte = 0x08
	StatusNotSettable      byte = 0x0E
	StatusReplyTooLarge    byte = 0x11
	StatusNotEnoughData    byte = 0x13
	StatusAttrNotSupported byte = 0x14
	StatusTooMuchData      byte = 0x15
	StatusObjectMissing    byte = 0x16
	StatusEmbeddedError    byte = 0x1E
	StatusVendor           byte = 0xFF
)

General status codes this package acts on.

View Source
const (
	CmdNOP               uint16 = 0x0000
	CmdListServices      uint16 = 0x0004
	CmdListIdentity      uint16 = 0x0063
	CmdListInterfaces    uint16 = 0x0064
	CmdRegisterSession   uint16 = 0x0065
	CmdUnRegisterSession uint16 = 0x0066
	CmdSendRRData        uint16 = 0x006F
	CmdSendUnitData      uint16 = 0x0070
)

Encapsulation commands (the 16-bit command field of the 24-byte header).

View Source
const (
	ItemNullAddress      uint16 = 0x0000
	ItemIdentity         uint16 = 0x000C
	ItemConnectedAddress uint16 = 0x00A1
	ItemConnectedData    uint16 = 0x00B1
	ItemUnconnectedData  uint16 = 0x00B2
	ItemSockaddrOT       uint16 = 0x8000
	ItemSockaddrTO       uint16 = 0x8001
)

Common packet format item type IDs.

View Source
const (
	// ItemSequencedAddress is the CPF item of a class 0/1 packet.
	ItemSequencedAddress uint16 = 0x8002
	// ClassAssembly is the Assembly object.
	ClassAssembly uint16 = 0x04
	// IOPort is the UDP port of class 0/1 I/O.
	IOPort = 2222
)
View Source
const (
	TypeBOOL   uint16 = 0xC1
	TypeSINT   uint16 = 0xC2
	TypeINT    uint16 = 0xC3
	TypeDINT   uint16 = 0xC4
	TypeLINT   uint16 = 0xC5
	TypeUSINT  uint16 = 0xC6
	TypeUINT   uint16 = 0xC7
	TypeUDINT  uint16 = 0xC8
	TypeULINT  uint16 = 0xC9
	TypeREAL   uint16 = 0xCA
	TypeLREAL  uint16 = 0xCB
	TypeBYTE   uint16 = 0xD1
	TypeWORD   uint16 = 0xD2
	TypeDWORD  uint16 = 0xD3
	TypeLWORD  uint16 = 0xD4
	TypeStruct uint16 = 0x02A0 // followed by a structure handle

	// HandleSTRING is the structure handle of the built-in Logix STRING
	// (LEN DINT + DATA SINT[82], 88 bytes with padding).
	HandleSTRING uint16 = 0x0FCE
	// StringSize is the Logix STRING's size in bytes and StringChars its
	// character capacity.
	StringSize  = 88
	StringChars = 82
)

Atomic data type codes (the Logix tag type word).

View Source
const DefaultConnSize = 500

DefaultConnSize is the class 3 connection size, in bytes, used when a Client does not set one.

View Source
const DefaultPort = 44818

DefaultPort is the EtherNet/IP explicit messaging port (TCP and UDP).

View Source
const HeaderLen = 24

HeaderLen is the size of the encapsulation header.

Variables

This section is empty.

Functions

func AtomicTypeCode

func AtomicTypeCode(name string) (code uint16, ok bool)

AtomicTypeCode returns the code of the atomic type with exactly this name (upper case: "DINT", "REAL" …); ok is false otherwise, including for STRING.

func Decode

func Decode(t Type, data []byte, elements int, members []Member) (any, error)

Decode turns a read reply's data into a JSON-able value: one element is a scalar, several an array. A structure without members is {"handle": "0x…", "hex": "…"}.

func DecodeAtomic

func DecodeAtomic(code uint16, b []byte) (any, error)

DecodeAtomic reads one atomic value; BOOL gives a bool, integers and reals a float64 (LINT/ULINT beyond 2^53 lose precision).

func DecodeString

func DecodeString(b []byte) (string, error)

DecodeString reads a Logix STRING (LEN then characters).

func Element

func Element(b []byte, i uint32) []byte

Element appends a member (element index) segment.

func EncapStatusText

func EncapStatusText(s uint32) string

EncapStatusText names encapsulation status codes.

func Encode

func Encode(t Type, v any, elements int) ([]byte, error)

Encode turns a JSON value into tag data for elements elements of type t: numbers/booleans for atomics, text for STRING, arrays for several elements, {"hex": "…"} or a hex string for structures.

func EncodeAtomic

func EncodeAtomic(code uint16, v any) ([]byte, error)

EncodeAtomic writes one atomic value (bool or number), refusing values out of the type's range.

func EncodeString

func EncodeString(s string) ([]byte, error)

EncodeString writes a Logix STRING of StringSize bytes.

Example

Encode a string for a write and decode it again.

package main

import (
	"fmt"

	"github.com/cB-Sigma/enip"
)

func main() {
	b, _ := enip.EncodeString("Pump 7")
	s, _ := enip.DecodeString(b)
	fmt.Println(s)
}
Output:
Pump 7

func ExtStatusText

func ExtStatusText(general byte, ext uint16) string

ExtStatusText names the extended status of connection manager errors (general 0x01) and Logix tag errors (general 0xFF).

func IOPacket added in v0.2.0

func IOPacket(connID, encapSeq uint32, cipSeq uint16, data []byte) []byte

IOPacket builds a class 0/1 UDP packet.

func IdentityItem

func IdentityItem(id Identity, major, minor uint8, serial uint32) []byte

IdentityItem encodes an identity item's data (socket address fields are big-endian, the rest little-endian).

func IsCIP

func IsCIP(err error) bool

IsCIP reports a CIP status error (the device answered and refused).

func Logical

func Logical(class, instance uint16, attr int) []byte

Logical appends class, instance and (when attr >= 0) attribute segments, each 8-bit when it fits and 16-bit otherwise.

func MarshalCPF

func MarshalCPF(items ...Item) []byte

MarshalCPF encodes an item count followed by the items.

func MarshalResponse

func MarshalResponse(service, status byte, ext []uint16, data []byte) []byte

MarshalResponse encodes a CIP reply.

func MultipleService

func MultipleService(reqs [][]byte) []byte

MultipleService packs requests into one Multiple Service Packet body.

func ParseIOPacket added in v0.2.0

func ParseIOPacket(b []byte) (connID, encapSeq uint32, cipSeq uint16, data []byte, err error)

ParseIOPacket reads a class 0/1 UDP packet.

func ParseRequest

func ParseRequest(b []byte) (service byte, path, data []byte, err error)

ParseRequest splits a CIP request.

func ParseUnconnectedSend

func ParseUnconnectedSend(b []byte) (msg, route []byte, err error)

ParseUnconnectedSend splits an Unconnected Send body (for simulators).

func PortSegment

func PortSegment(b []byte, port uint16, link string) ([]byte, error)

PortSegment appends one port segment.

func RRData

func RRData(timeout uint16, items ...Item) []byte

RRData builds the body of SendRRData or SendUnitData: interface handle 0, a timeout in seconds and the items.

func RegisterSessionData

func RegisterSessionData() []byte

RegisterSessionData is the request/reply body of RegisterSession: protocol version 1, options 0.

func Request

func Request(service byte, path, data []byte) []byte

Request encodes a CIP request: service, path size in words, path, data.

func Route

func Route(s string) ([]byte, error)

Route parses a route such as "1,0" (backplane port 1, slot 0) or "1,0,2,10.0.0.9,1,2" into port segments. Links that are IP addresses (or above 255) use the extended link address form.

Example

The route to the CPU in slot 2 of a ControlLogix chassis (backplane port 1), and the symbolic path of a program-scoped tag.

package main

import (
	"fmt"

	"github.com/cB-Sigma/enip"
)

func main() {
	route, _ := enip.Route("1,2")
	path, _ := enip.TagPath("Program:Main.Count")
	fmt.Printf("% x\n% x\n", route, path)
}
Output:
01 02
91 0c 50 72 6f 67 72 61 6d 3a 4d 61 69 6e 91 05 43 6f 75 6e 74 00

func SplitMultiple

func SplitMultiple(b []byte) ([][]byte, error)

SplitMultiple splits a Multiple Service Packet body (request or reply) into its embedded messages.

func StatusText

func StatusText(s byte) string

StatusText names a general status code.

func Symbolic

func Symbolic(b []byte, name string) []byte

Symbolic appends an ANSI extended symbolic segment, padded to a word.

func TagPath

func TagPath(tag string) ([]byte, error)

TagPath encodes a Logix tag reference such as "Program:Main.Motor[2].Speed" or "Grid[1,2]": each dotted name becomes a symbolic segment and each index a member segment. The "Program:<name>" scope is one segment.

Types

type Client

type Client struct {
	Addr      string        // host:port
	Route     []byte        // port segments to the CPU; nil talks to the target itself
	Timeout   time.Duration // per exchange
	Connected bool          // try a Forward Open
	ConnSize  int           // connection size; above 511 uses Large Forward Open
	// KeepAlive is how long the connection must survive without traffic
	// (it sets the RPI × multiplier timeout); default 1 minute.
	KeepAlive time.Duration
	// contains filtered or unexported fields
}

Client is one EtherNet/IP session with a target, optionally with a class 3 connection. It is safe for concurrent use; requests are serialised.

func (*Client) Close

func (c *Client) Close()

Close ends the connection and session.

func (*Client) Do

func (c *Client) Do(ctx context.Context, service byte, path, data []byte) (*Response, error)

Do sends one CIP request and returns the reply. A transport failure closes the session (the next call reconnects); a CIP error status comes back in the reply, not as the error.

func (*Client) GetAttribute

func (c *Client) GetAttribute(ctx context.Context, class, instance uint16, attr int) ([]byte, error)

GetAttribute reads one attribute (Get_Attribute_Single), or every attribute of the instance when attr < 0 (Get_Attribute_All).

func (*Client) Identity

func (c *Client) Identity() *Identity

Identity returns the target's identity as read on connect, if any.

func (*Client) ListIdentity

func (c *Client) ListIdentity(ctx context.Context) (*Identity, error)

ListIdentity asks the target for its identity over TCP.

func (*Client) ListTags added in v0.2.0

func (c *Client) ListTags(ctx context.Context, program string) ([]TagInfo, error)

ListTags lists the tags of the controller (program "") or of one program, reading the Symbol object's instances (Get_Instance_Attribute_List, repeated until the controller says the list is complete).

func (*Client) MaxMessage

func (c *Client) MaxMessage() int

MaxMessage is the largest CIP message (request or reply) the current transport carries.

func (*Client) Mode

func (c *Client) Mode() (string, error)

Mode reports "connected", "unconnected" or "" when not connected, and why connected messaging was refused, if it was.

func (*Client) OpenIO added in v0.2.0

func (c *Client) OpenIO(ctx context.Context, cfg IOConfig) (*IOConn, error)

OpenIO opens a class 1 connection through the client's session (its Route, if any, reaches the adapter) and starts sending outputs at the RPI. The outputs start zeroed and in idle mode; SetOutput and SetRun change them.

func (*Client) ReadTag

func (c *Client) ReadTag(ctx context.Context, tag string, elements int) (Type, []byte, error)

ReadTag reads elements elements of a tag, continuing with Read Tag Fragmented when the value does not fit in one reply.

func (*Client) ReadTags

func (c *Client) ReadTags(ctx context.Context, reads []*TagRead) error

ReadTags reads several tags in one Multiple Service Packet (the caller keeps the batch within MaxMessage); replies that were cut short are completed with fragmented reads. The error is for the whole batch.

func (*Client) ReadTemplate added in v0.2.0

func (c *Client) ReadTemplate(ctx context.Context, instance uint16) (Template, error)

ReadTemplate reads a structure's definition from its template instance (TagInfo.Template): its attributes, then the definition itself.

func (*Client) SetAttribute

func (c *Client) SetAttribute(ctx context.Context, class, instance uint16, attr int, data []byte) error

SetAttribute writes one attribute (Set_Attribute_Single).

func (*Client) WriteTag

func (c *Client) WriteTag(ctx context.Context, tag string, t Type, elements int, data []byte) error

WriteTag writes a tag's value, with Write Tag Fragmented when the data does not fit in one message.

type Error

type Error struct {
	Service byte
	Status  byte
	Ext     []uint16
}

Error is a CIP error status.

func (*Error) Error

func (e *Error) Error() string
type Header struct {
	Command uint16
	Length  uint16 // bytes of data after the header
	Session uint32
	Status  uint32
	Context [8]byte
	Options uint32
}

Header is the encapsulation header; every field is little-endian.

func ParseHeader

func ParseHeader(b []byte) (Header, error)

ParseHeader decodes the first 24 bytes of b.

func ReadPacket

func ReadPacket(r io.Reader) (Header, []byte, error)

ReadPacket reads one encapsulation packet from a stream.

func (Header) Marshal

func (h Header) Marshal(data []byte) []byte

Marshal writes the header followed by data (Length is set from data).

type IOConfig added in v0.2.0

type IOConfig struct {
	// ConfigInstance, OutputInstance (O→T, consumed by the adapter) and
	// InputInstance (T→O, produced by it) are assembly instances.
	ConfigInstance, OutputInstance, InputInstance uint16
	// OutputSize and InputSize are the data sizes in bytes (without the
	// sequence count and run/idle header).
	OutputSize, InputSize int
	// RPI is the requested packet interval of both directions (default
	// 100 ms); the connection times out after RPI × 4 << Multiplier.
	RPI        time.Duration
	Multiplier byte // default 2 (16 × RPI)
	// LocalUDP is where this side receives T→O data (default ":2222");
	// the adapter is told the port.
	LocalUDP string
	// AdapterUDPPort is the adapter's I/O port when it does not say it in
	// its Forward Open reply (default 2222).
	AdapterUDPPort int
}

IOConfig describes a class 1 connection to an adapter's assemblies.

type IOConn added in v0.2.0

type IOConn struct {
	// contains filtered or unexported fields
}

IOConn is an open class 1 connection; this side is the scanner (originator). Its methods are safe for concurrent use.

func (*IOConn) Close added in v0.2.0

func (io *IOConn) Close()

Close stops sending and closes the UDP socket. The adapter drops the connection when its timeout passes (a Forward Close is not sent).

func (*IOConn) Err added in v0.2.0

func (io *IOConn) Err() error

Err is why the connection ended (nil while it runs or after Close).

func (*IOConn) Input added in v0.2.0

func (io *IOConn) Input() ([]byte, uint16, time.Time)

Input is the latest T→O data, its sequence count and when it came.

func (*IOConn) SetOutput added in v0.2.0

func (io *IOConn) SetOutput(b []byte)

SetOutput sets the O→T data sent from the next RPI on.

func (*IOConn) SetRun added in v0.2.0

func (io *IOConn) SetRun(run bool)

SetRun sets the run/idle header: true is run mode.

func (*IOConn) WaitInput added in v0.2.0

func (io *IOConn) WaitInput(ctx context.Context) ([]byte, error)

WaitInput waits for the next T→O packet.

type Identity

type Identity struct {
	Protocol    uint16 `json:"protocol"`
	IP          string `json:"ip"`
	Port        uint16 `json:"port"`
	Vendor      uint16 `json:"vendor"`
	DeviceType  uint16 `json:"deviceType"`
	ProductCode uint16 `json:"productCode"`
	Revision    string `json:"revision"`
	Status      uint16 `json:"status"`
	Serial      string `json:"serial"`
	ProductName string `json:"productName"`
	State       uint8  `json:"state"`
}

Identity is a ListIdentity reply item (the identity object's key attributes plus the device's socket address).

func Discover

func Discover(ctx context.Context, addr string, wait time.Duration) ([]Identity, error)

Discover sends ListIdentity over UDP to addr (a broadcast or unicast host:port) and collects replies for wait.

func ParseIdentity

func ParseIdentity(b []byte) (Identity, error)

ParseIdentity decodes an identity item.

func ParseListIdentity

func ParseListIdentity(b []byte) ([]Identity, error)

ParseListIdentity decodes a ListIdentity reply body.

type Item

type Item struct {
	Type uint16
	Data []byte
}

Item is one common packet format item.

func ParseCPF

func ParseCPF(b []byte) ([]Item, error)

ParseCPF decodes a common packet format list.

func ParseRRData

func ParseRRData(b []byte) ([]Item, error)

ParseRRData splits the body of SendRRData/SendUnitData into its items.

type Member

type Member struct {
	Name   string `json:"name"`
	Offset int    `json:"offset"`
	Type   string `json:"type"`
	Bit    int    `json:"bit,omitempty"` // for BOOL members packed in a host word
}

Member is a structure member the configuration names, for decoding a structure's bytes without the controller's template.

type Response

type Response struct {
	Service byte // with the reply bit
	Status  byte
	Ext     []uint16
	Data    []byte
}

Response is a decoded CIP reply.

func ParseResponse

func ParseResponse(b []byte) (*Response, error)

ParseResponse decodes a CIP reply.

func (*Response) Err

func (r *Response) Err() error

Err returns the reply's status as an error, or nil on success (and on partial transfer, which callers handle).

func (*Response) WriteErr

func (r *Response) WriteErr() error

WriteErr is Err for a write service (Write Tag, Write Tag Fragmented, Set Attribute Single): partial transfer is not success there, since only reads continue in further fragments.

type TagInfo added in v0.2.0

type TagInfo struct {
	Name     string
	Instance uint32 // the Symbol object instance
	// Type is the tag's data type: an atomic code, or TypeStruct with
	// Template set (the structure's template instance; ReadTemplate gives
	// its definition and handle).
	Type     Type
	Template uint16
	Dims     [3]int // array dimensions; zero for a scalar
	ReadOnly bool   // external access read only (or none)
	// System marks entries a controller lists that are not user tags:
	// programs ("Program:<name>"), routines, tasks, module and system
	// tags.
	System bool
}

TagInfo is one entry of a controller's (or program's) tag list.

func (TagInfo) Elements added in v0.2.0

func (t TagInfo) Elements() int

Elements is the number of elements of the tag (1 for a scalar).

type TagRead

type TagRead struct {
	Tag      string
	Elements int
	Type     Type
	Data     []byte
	Err      error
}

TagRead is one tag of a batched read and its result.

type Template added in v0.2.0

type Template struct {
	Instance uint16
	Name     string
	Handle   uint16 // the structure handle in reads and writes
	Size     int    // bytes of one value
	Members  []TemplateMember
}

Template is a structure (UDT) definition read from the controller.

func (Template) DecodeStruct added in v0.2.0

func (t Template) DecodeStruct(b []byte, lookup func(instance uint16) (Template, bool)) (map[string]any, error)

DecodeStruct decodes one value of the template's structure into a map of member name to value, nested templates read through lookup (nil leaves nested structures as raw bytes). Hidden members are left out.

type TemplateMember added in v0.2.0

type TemplateMember struct {
	Name     string
	Type     Type   // atomic, or TypeStruct with Template set
	Template uint16 // a nested structure's template instance
	Offset   int
	Array    int // element count of an array member (0: scalar)
	Bit      int // a BOOL member's bit at Offset
	Hidden   bool
}

TemplateMember is one member of a structure definition.

type Type

type Type struct {
	Code   uint16
	Handle uint16
}

Type is a tag's data type: an atomic code, or TypeStruct and a handle.

func ParseType

func ParseType(b []byte) (Type, []byte, error)

ParseType decodes the type at the start of b and returns the rest.

func ParseTypeName

func ParseTypeName(s string) (Type, bool)

ParseTypeName turns "DINT", "REAL", "STRING" … into a Type. ok is false for unknown names.

func (Type) IsStruct

func (t Type) IsStruct() bool

IsStruct reports a structure type.

func (Type) Marshal

func (t Type) Marshal() []byte

Marshal encodes the type as it leads read replies and write requests.

func (Type) Name

func (t Type) Name() string

Name returns the type's name.

func (Type) Size

func (t Type) Size() int

Size is one element's size in bytes, 0 when unknown (user structures).

Directories

Path Synopsis
Package server is the EtherNet/IP adapter (target) role, answering like a Logix controller: a tag table with program scopes and structures, the identity object, sessions, Unconnected Send routing to a CPU slot, class 3 connections (Forward Open and Large Forward Open, Forward Close, SendUnitData), Multiple Service Packets, Read and Write Tag with fragmentation, member access in structures, tag browsing (the Symbol and Template objects, symbol instance addressing), attribute services, and ListIdentity over TCP and UDP.
Package server is the EtherNet/IP adapter (target) role, answering like a Logix controller: a tag table with program scopes and structures, the identity object, sessions, Unconnected Send routing to a CPU slot, class 3 connections (Forward Open and Large Forward Open, Forward Close, SendUnitData), Multiple Service Packets, Read and Write Tag with fragmentation, member access in structures, tag browsing (the Symbol and Template objects, symbol instance addressing), attribute services, and ListIdentity over TCP and UDP.

Jump to

Keyboard shortcuts

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