Releases: otfabric/go-iec61850
Release list
v1.0.8
Date: 2026-07-30
Previous release: v1.0.7
Summary
Patch release: bump go-mms to v1.0.7 (pulls go-cotp v1.0.4 / go-tpkt v1.0.1).
No public API surface or behavioural changes. Go floor remains 1.24.
Dependencies
- go-mms —
v1.0.6→ v1.0.7 - go-cotp (indirect) —
v1.0.3→ v1.0.4 - go-tpkt (indirect) —
v1.0.0→ v1.0.1
Unchanged
- No API, wire, or behavioural changes. Import path remains
github.com/otfabric/go-iec61850.
v1.0.7
Date: 2026-07-30
Previous release: v1.0.6
Summary
Dial now propagates DialOptions.Logger into the underlying MMS client when
MMS.Logger is unset (matching existing Server behaviour). Dependency bump to
go-mms v1.0.6 / go-cotp v1.0.3. Docs and Makefile tooling aligned with sibling
OT Fabric libraries, including a real make fuzz target. Broad unit-test
coverage lift (non-example packages ~90–100%). Go floor remains 1.24.
Fixed
- Dial logger → MMS — when
DialOptions.Loggeris set and
DialOptions.MMS.Loggeris nil, the IEC logger is passed through to
iso.Dial/mms.Client(Server already did this).
Dependencies
- go-mms —
v1.0.5→ v1.0.6 - go-cotp (indirect) —
v1.0.2→ v1.0.3
Changed
- OBSERVABILITY.md / options godoc / doc.go — clarify IEC→MMS logger
inheritance and thatiso.WithLoggeris independent. - API.md — document
IEDNameon DialOptions/ClientOptions; note logger
propagation.
Build / tooling
- Makefile — exports
GOWORK=off;make fuzzruns the eight root fuzz
targets plusFuzzParsein./scl(replaces the stale “planned for M6”
stub).
Tests
Coverage pass across client, server, SCL, and internal/servermodel
(approximate statement coverage: root ~90%, scl ~92%,
scl/validate 100%, scl/index ~93%, scl/internal/genir ~93%,
internal/servermodel ~87%). Notable additions:
- Values / Write / client helpers — type-mismatch and nil guards on
accessors;Writevalidation edges;ldDomain/refToMMS/
stripIEDPrefix. - Server —
installWriteInterceptor(RCB / SGCB / CO / DA notify),
parseCOStoreKey/parseRCBStoreKey. - Control / reports — Operate / Select / SelectWithValue / Cancel /
ReadCtlModel / ReadLastApplError edges;buildCancel;OrCat/
AddCauseString;ListReportsVerified/verifyReportCandidates. - Setting groups (client) —
GetSettingGroupInfo,SelectActiveSG/
SelectEditSG/ConfirmEditSG,GetEditSGValue/SetEditSGValue/
GetActiveSGValue,decodeSGCBmember-type edges; server
GetEditSettingGroup. - Data sets — List / Get / Read / Create / Delete validation and
loopback paths;memberIdentity; lazy-cacheReadDataSet. - SCL —
Parse/ParseFile/ParseFileOpts/ParseWithOptions/
ParseBytes(strict, MaxDiagnostics, read failures);DetectKind/
KindFromPath; flatten GSE/SMV/ConnectedAP exports; index
FindGSEControl/FindSMVControl; validate package edge matrix;
v1.7 convert Private / SMV / SettingControl / LogControl; genir
Emit/ enums / types / SCL root / type-resolve helpers; sclparse
list-smv/ completion / version string. - servermodel —
registerSGCB, compoundregisterDA/
registerDAWithFC, RCB register Read/Write via MMS loopback,
makeStructRead/buildAttrElemAndRead.
No public API surface changes. No breaking changes.
Import path remains github.com/otfabric/go-iec61850.
v1.0.6
Fixed
- Report subscription overflow tests —
TestSubscribeReport_QueueOverflowand
TestSubscribeReport_OverflowDropOldestno longer use a fixed sleep plus non-blocking
drain. They wait for the first delivered report (timeout) before draining the channel,
which removes a flake under-raceon busy CI (observed on Go 1.26). - Report engine logging — removed accidental
stderrdebug prints from the URCB
delayed-send path ([DBG] send OK/send failed). Failures remain on the structured
logger.
No API changes. No breaking changes.
Import path remains github.com/otfabric/go-iec61850.
v1.0.5
Dependencies
- Bumped
github.com/otfabric/go-mmsfrom v1.0.4 → v1.0.5 (fix 32-bit / linux armv7 compile overflow in file PDU error formatting).
No API changes. No breaking changes.
Import path remains github.com/otfabric/go-iec61850.
v1.0.4
Documentation, lint hardening, and code-quality improvements. No API changes, no behavior changes.
Dependencies
- Bumped
github.com/otfabric/go-mmsfrom v1.0.3 → v1.0.4 (documentation and lint hardening; no API or behavior changes). - Bumped
github.com/otfabric/go-cotp(indirect) from v1.0.1 → v1.0.2.
Documentation — error taxonomy covering all 20 sentinel values, 7 typed error structs
(ReferenceError, DecodeError, ModelError, ReportError, SCLParseError,
DataAccessError, ControlError), underlying MMS error passthrough, and errors.Is /
errors.As usage patterns.
- Added link to
ERRORS.mdinREADME.mddocumentation table and repository file-tree section.
Linting and code quality
golangci-lintexclusion-free (except two documented gocritic disables):- Removed blanket
errchecksuppression for test files; all issues fixed in code. - Removed dead
staticcheck SA2002test-file exclusion (SA2002 does not occur in this repo). ifElseChaindisabled: three-branchif/else if/elsepatterns are preferred overswitch
for exclusive error-then-nil-then-value decisions.exitAfterDeferdisabled:Example_*documentation functions intentionally show
defer client.Closefollowed bylog.Fatalto model correct cleanup for users.
- Removed blanket
- Fixed unchecked error returns across 10 test files:
c.Abort,sub.Close,defer sub.Close→_ = ...ordefer func() { _ = ... }()srv.MMS().SetVariableRead(...)→ wrapped inif err := ...; err != nil { t.Fatalf(...) }srv.RegisterControl(...)inexample_test.go→_ = srv.RegisterControl(...)
- Converted
if/else if/elsechains inbulk.goanddataset.gotoswitchstatements. - Fixed
exitAfterDeferin all example programs (basic-client,control,files,reports,
ied-client,ied-server) andscl/cmd/sclgen: moveddefer cancel()/defer stop()to
after the last potential fatal exit; explicit cleanup called before eachlog.Fatal. - Removed unused test helper return values:
setupRCBLoopbackdropped*mms.Serverreturn (never used by any caller).setupWritableLoopbackdropped*writableVarsreturn (never used by any caller).executeCmdinscl/cmd/sclparsedroppedstringreturn (never used by any caller).
- Added
unparamandgocriticlinters to.golangci.yml. - Added
vulntarget (govulncheck) toMakefileand wired it intomake check.
No API changes. No breaking changes.
Import path remains github.com/otfabric/go-iec61850.
v1.0.3
Added: Recovery, concurrency, resource-pressure, and decoder-robustness hardening.
Recovery & lifecycle (reconnect_test.go)
TestClientReconnect_AfterServerRestart— reconnect after server shutdown; new-port server model.TestClientReconnect_MultipleSequential— 5 sequential connect/close cycles without resource leak.TestClientReconnect_URCBResubscription— URCB re-established after reconnect on same server.TestClient_NoGoroutineLeakOnConnectDisconnect— 10 cycles; goroutine delta ≤ 3.TestServer_Close_StopsReportEngine— post-close connections refused immediately.TestClientReconnect_ReadAfterServerClose— reads return errors (not hang) after server gone.
BRCB EntryID resume (interop/brcb_resume_test.go, report_engine.go)
- Implemented
decodeEntryIDandfilterEntriesAfterhelpers. enableRCBreads the client's pre-written EntryID and filters buffer replay to entries strictly after that point.- Policy: exclusive boundary — ID N means replay entries > N; zero means full replay; future ID means no replay.
- EntryIDs are 8-byte big-endian, monotonically increasing from 1, non-persistent across server restarts.
PurgeBuf=trueinvalidates all previously issued EntryIDs.- Three interop tests:
Resume,ZeroResume(full buffer),ResumeFromFuture(no replay).
Bug fix: BRCB replay skipped after unconditional re-enable (report_engine.go)
- Root cause: when a BRCB generated a buffered entry it wrote
EntryIDto the value store. On re-enable,enableRCBread that server-written value as the resume filter, excluding the only buffered entry — so no replay was delivered. - Fix:
EntryIDin the store is reset to all-zero bytes whenRptEna=falseis written. A subsequent re-enable with no client-written EntryID decodes toresumeID=0, which means full replay. Tests that explicitly write an EntryID before re-enabling are unaffected — they overwrite the cleared value beforeRptEna=true. - Covered by
TestGoServer_BRCB_Replay(was timing out, now passes).
Concurrent request hardening (concurrent_test.go)
- Context cancellation releases all in-flight confirmed requests.
- Asynchronous report delivery does not stall or corrupt concurrent read responses.
- Aborting the client while 20 writes are in flight — all goroutines exit cleanly.
- Timed-out requests do not accumulate goroutines (delta ≤ 3 after 10 timeouts).
Resource pressure tests (resource_exhaust_test.go)
- 50 sequential connections and 20 concurrent connections without descriptor leak.
- Report flood (30 GIs, queue-size=2) — reads succeed throughout;
OverflowDropNewestdrops silently. - 10×200 concurrent
SetValuewith 100 simultaneous client reads — zero read errors.
SCL corpus expansion (scl_corpus_test.go)
- Multi-LN model (LLN0 + 3×GGIO + MMXU with AnalogueValue DAType/BDA).
- Multi-LD model (4 logical devices).
- SDO chain (DPC with Oper sub-object).
- Enum DAs (ENS with custom enum type).
- Multi-LN URCB report with cross-LN dataset members.
Report decoder robustness (additional fuzz targets in fuzz_test.go)
FuzzDecodeReportIndication— arbitrary value arrays (4 seeds: minimal, empty, all-zeros, all-ones).FuzzDecodeOptFlds— arbitrary bit-string bytes for the OptFlds decoder.FuzzDecodeTrgOps— arbitrary bit-string bytes for the TrgOps decoder.FuzzDecodeReportIndication_NilValues— selective nil entries via fuzz-driven bit masks.
API documentation (example_test.go)
- Added
Example*functions forDial,SubscribeReport,Write,GetReportControlBlock,NewServer,SetValue,RegisterControl, andParseRef.
Service profile (INTEROP.md)
- IEC 61850-7-2 service table (client + server columns).
- CDC conformance table with interop-tested column.
- Full report features table (URCB/BRCB, all OptFlds).
- New
BRCB EntryID resumesection with explicit exclusive-boundary contract, byte format, stability guarantees, overflow interaction, and purge invalidation. - New
Report backpressure and loss policysection documenting URCB vs. BRCB queue loss semantics. - Known limitations and deferred features table.
Scheduled fuzz workflow (.github/workflows/fuzz.yml)
- Nightly 5-minute fuzz run per target, with
workflow_dispatchoverride. - Failing corpus entries uploaded as artifacts (30-day retention).
Race detector: go test -race ./... passes cleanly in both go-iec61850 and go-mms.
v1.0.2
Fixed: SBO/SBOw control server — connection-scoped ownership and ctlVal matching.
controlRegistrationnow tracksselectConn *mms.ServerConn(SBO normal and SBOw contention) andselectCtlVal *mms.Value(SBOw value matching).executeSelectdenies a second client's select when an active selection by a different connection exists; stale selections (owner disconnected or timed out) are cleared automatically.executeOperate(SBOw) rejects operates where the operating connection does not match the selecting connection (AddCauseSelectFailed) or where thectlValdiffers from the one provided at select time (AddCauseParameterChange).executeCancelenforces ownership for both SBO normal (connection identity) and SBOw (connection + origin identity).handleSBOSelectReadlazily clears a stale selection when the previous owner's connection is no longer active, allowing immediate re-selection by a new client.isConnActivehelper queries the livemms.Server.Connections()list to determine whether a stored*ServerConnis still open.mmsValuesEqualhelper compares two*mms.Valueinstances by type and byte content.
Added: ControlHandler.SelectTimeout — configurable per-control select reservation timeout.
When SelectTimeout > 0 in a ControlHandler, that duration overrides DefaultSelectTimeout (30 s) for that specific control object. Allows tests and applications to use short timeouts without changing the global default.
Fixed: Panic containment for ControlHandler callbacks.
callControlHandler wraps every invocation of OnSelect and OnOperate in a defer recover(). A panic in application callback code is caught and returned as a *ControlError instead of crashing the server goroutine.
Fixed: Panic containment for OnOverflow report callback.
deliver in report.go wraps the OverflowCallback invocation in a defer recover(), preventing an application panic from terminating the report delivery goroutine.
Fixed: RCB write interceptor value-restore on rejection.
internal/servermodel.registerRCB now saves the previous store value before calling vs.Set, and restores it if the write interceptor (e.g. ReportEngine.HandleRCBWrite) returns an error. This prevents a rejected URCB Resv or RptEna write from leaving the value store in an inconsistent state.
Fixed: URCB reservation and enable ownership enforcement.
ReportEngine.HandleRCBWrite now enforces connection-scoped ownership for Resv and RptEna writes:
Resv=trueis rejected withDataAccessErrorObjectAccessDeniedif another active connection already holds the reservation.Resv=falseis rejected if the clearing client does not own the reservation.RptEna=trueis rejected for URCBs if the enabling client is not the reservation owner.- Stale reservations (owner disconnected) are released automatically so a new client can reserve immediately.
Added: BRCB replay on re-enable.
ReportEngine.enableRCB now replays all buffered entries to the enabling connection when a BRCB is re-enabled with a non-empty buffer. Replay is delivered asynchronously via replayBRCBEntries after a 150 ms delay (to ensure the RptEna write response reaches the client before the first replayed report). ReportEngine.SetRCBBufMax allows test code to shrink the buffer for overflow testing without changing the default.
Added: DPS and BCR CDCs to the interop fixture.
interop/testdata/interop.icd (and the matching mms-interop fixture) now includes:
GGIO1.DPS1— double-point status (Dbpos2-bit BitString, cdc=DPS).MMTR1.TotVAh— binary counter reading (INT32U, cdc=BCR).brcb01buffered report control block withentryID=trueandbufOvfl=trueinOptFields.
Added: Interop test coverage for new phases.
New test files gated behind -tags=interop:
interop/brcb_test.go—TestGoServer_BRCB_EntryID,_Replay,_Purge,_Overflow.interop/cdc_test.go— DPS and BCR read tests in all four directions (adapter directions skip pendingmms-interopimage rebuild).interop/quality_timestamp_test.go— Quality validity states (good/invalid/questionable/detail-bits), UTC timestamp round-trip, quality flags (LeapSecondsKnown,ClockNotSynchronized), and fractional-second accuracy sub-tests.interop/sbo_state_test.go— Full SBO/SBOw state-machine suite: cancel by non-owner, configurable select timeout, repeated select, second-client contention, disconnect releases selection, SBOwctlValmismatch, SBOw second-client contention.interop/urcb_reservation_test.go— Double-reserve same/different connection, enable without ownership, disconnect releases reservation.interop/urcb_integrity_test.go— Integrity-period delivery, coexistence with dchg, disable stops timer.interop/urcb_trigger_test.go— Data-change, quality-change, data-update, no-dchg-on-same-value, multiple-trigger coalescing.interop/multi_client_test.go— Concurrent reads (5 clients), concurrent writes to independent variables, invoke-ID isolation (3 clients × 10 reads), disconnect-one-keeps-others.interop/dataset_depth_test.goadditions —TestGoServer_DS_WritePartialFail(type-mismatch partial failure inWriteMultiple),TestGoServer_DS_NestedDORead(structured MX read ofTotW.mag).
v1.0.1
Added: MIT LICENSE with copyright Copyright (c) 2026 OT Fabric.
Added: // SPDX-License-Identifier: MIT headers on first-party Go sources (generated scl/internal/raw code left unchanged).
Changed: README license section and badge block for public release — added pkg.go.dev, removed Go Report Card, and switched Codecov to a tokenless public badge URL.
v1.0.0
Changed: Bump dependencies to stable releases (go-mms v1.0.0, go-cotp v1.0.1).
Added: DialOptions.IEDName — when set, the client prepends the IED identifier to every MMS domain name it sends and strips it from domain names returned by the server. Callers can always use bare LD instance names regardless of whether the target is a stand-alone Go server (no prefix) or a full IEC 61850 server with a vendor IED name.
Added: SBO normal (select-before-operate, ctlModel=2) — Client.Select reads SBO[CO] and grants/denies ownership per connection; Server.RegisterControl with CtlModelSBONormal installs a read-interceptor that enforces single-owner semantics. executeOperate checks the owning connection identity for SBO normal and the originator identity for SBO enhanced, so both paths share a single control-execution function.
Added: SBO enhanced (select-before-operate with enhanced security, ctlModel=4) — Client.SelectWithValue sends an SBOw[CO] write containing a full Oper structure; Server.RegisterControl with CtlModelSBOEnhanced validates that the ctlNum in the subsequent Operate matches the one used at select time. Cancel clears the select reservation for both SBO models.
Added: Client.Cancel sends an MMS write to Cancel[CO], releasing any SBO reservation held by the caller.
Fixed: OperateParams / CancelParams Oper structure encoding — operTm is now omitted from the encoded structure when not set, instead of sending a zero-time seventh element. Several IEC 61850 servers (including libiec61850 and iec61850bean) define OperSPC_Type without operTm and reject a seven-element structure with TypeInconsistent.
Fixed: Server.RegisterControl now correctly handles the CtlModelSBOEnhanced (ctlModel=4) path: executeOperate validates ctlNum consistency between SelectWithValue and Operate, and executeCancel clears both selectOwner and selectCtlNum.
Added: General interrogation — Client.TriggerGI sends an RptEna+GI write sequence; the report engine emits a GI report carrying all dataset members with ReasonGeneralInterrogation set in the inclusion bits.
Fixed: Report engine multi-member dchg — writes to multiple dataset members in a single MMS Write now correctly produce one report containing all changed members rather than emitting separate per-member reports or silently dropping later changes.
Added: Server SetVariableRead(domain, itemID, fn) — overrides the MMS read function for an already-registered variable at runtime. Used internally by RegisterControl for SBO normal; also available to applications that need custom read-time behaviour.
Changed: internal/servermodel.Register now includes RP/BR functional-constraint groups in the LN TypeSpec so that external tools (e.g. iec61850bean) can discover URCBs via GetVariableAccessAttributes.
Fixed: buildRCBFields stores DatSet as a fully-qualified domain/LNName$dsName reference so that iec61850bean's processReport can resolve the dataset from its ServerModel.
Fixed: SCL ctlModel DAI values are now emitted as enum name strings (direct-with-normal-security, sbo-with-normal-security, etc.) rather than ordinals, for compatibility with iec61850bean's SCL parser.
Changed: Examples directory renamed from _examples/ to examples/.
Added: interop/ test suite — consumed via -tags=interop; exercises go-iec61850 against live libiec61850 and iec61850bean adapters from mms-interop. Covers: basic operations, URCB reporting (dchg, GI, multi-member), dataset depth, negative/error cases, direct control, SBO normal, and SBO enhanced (SBOw). See INTEROP.md for the full compatibility matrix.
Added: .github/workflows/interop.yml — CI workflow that runs the full interop suite against pinned mms-interop adapter images.
v0.1.3
Changed: CI/release pipeline, SCL tooling, and documentation overhaul.
- Add GitHub Actions CI (
ci.yml) and release (release.yml) workflows for package and binary releases. sclgenandsclparsebinaries now embed full build metadata via ldflags (version,tag,commit,buildDate) and expose it through a dedicatedversionsubcommand.- Makefile build targets derive version from git tags instead of
version.txtfiles. - SCL model: add topology
LNodereferences onSubstation,VoltageLevel, andBay; losslessPrivate.InnerXMLcapture. - SCL validation: add topology LNode resolution, GOOSE/SMV
cbNamelinkage checks; deprecatescl.Validate()in favour ofscl/validate.All(). - SCL CLI: add
list-goose,list-smv,list-connected-ap,list-types, andinspectcommands; deprecateParseWithOptions/ParseFileWithOptions. - Add content-based
DetectKindfor document classification. - New
API.md(merged fromERRORS.md); updatedKNOWN_LIMITATIONS.md,OBSERVABILITY.md,interop/README.md.