Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions LIMITATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@
are opened without a data phase bit rate. `monitor` and `decode` skip
error frames (counted in `frames`).
- **Predefined connection set.** `monitor` and `decode` interpret frames by
their default identifiers (as Wireshark does); a device that moved a PDO or
SDO channel elsewhere is decoded by the identifier's default meaning. The
their default identifiers (as Wireshark does), except PDO COB-IDs they see
set or read over SDO (0x1400..0x15FF and 0x1800..0x19FF sub-index 1,
expedited): frames on those decode as that node's PDO (`"configured":
true`). A PDO moved before the trace starts, or an SDO channel moved
elsewhere, is decoded by the identifier's default meaning. The
SDO commands use the default SDO channel (0x600/0x580 + node-ID); other
channels (0x1200 sub 1/2, 0x1280..) are not selectable.
- **LSS** (`lss`) acts on one network with one LSS master: canopenctl does not
Expand Down
40 changes: 40 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,46 @@ with the version bump). It computes the tag, builds the binaries for linux
publishes the GitHub release with the section of this file that matches the
version as its notes. Add the section before running it.

## v0.7.0

**Date:** 2026-11-08
**Previous release:** v0.6.1

### Summary

go-canopen v0.5.0 and go-canbus v0.9.0. go-canopen is now tested against
recorded traffic of real Copley Controls drives; two of the decoder fixes
it brought show in `monitor` and `decode`.

### Fixed

- `monitor`, `decode`: a node guarding answer with the toggle bit clear
(`701#05` after `701#R`) was shown as a heartbeat; it is now a
`guard-response`, as the answers with the toggle bit set already were
(go-canopen's `dissect.Tracker`). **JSON**: such frames change `kind`
from `heartbeat` to `guard-response`; `--service heartbeat` shows both
as before.

### Added

- `monitor`, `decode`: PDOs on COB-IDs set or read over SDO earlier in the
trace are decoded as that node's PDO, with `"configured": true` in the
`pdo` object (record schema updated). A recorded Copley drive sends its
TPDO5 on 0x180, which was `unknown`.

### Dependencies

- go-canopen v0.5.0 (from v0.4.1), go-canbus v0.9.0 (from v0.8.0). canopenctl
only reads traces; go-canbus's writer and ISO-TP fixes do not change its
behaviour.

### Tests

- `decode` of frames recorded from a Copley drive (go-canopen's
testdata/captures): both guard answers, the learned TPDO5.

---

## v0.6.1

**Date:** 2026-11-08
Expand Down
28 changes: 28 additions & 0 deletions cmd/monitor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,31 @@ func TestDecode(t *testing.T) {
want(t, run(t, "", "decode", bad), 3, "")
want(t, run(t, "", "decode", path, "--service", "x"), 3, "unknown service")
}

// copleyTrace holds frames recorded from a Copley Controls drive (go-canopen
// testdata/captures/copley/unknown_copley-1100_absolute-move.log, lines
// 426-427, 449-460 and 466-468), written as a candump log: node guarding
// answers with both toggle values, the TPDO5 COB-ID set to 0x180 over SDO,
// and a TPDO5 frame (the drive's microsecond time stamp 0x1013).
const copleyTrace = `(3383.105884) can0 701#R
(3383.105958) can0 701#85
(3383.338300) can0 701#R
(3383.338350) can0 701#05
(3383.132009) can0 601#2304180180010000
(3383.132223) can0 581#6004180100000000
(3383.213675) can0 180#B71FE879
`

// TestDecodeRecordedCopley: decode labels both guard answers as guard
// responses (the one with the toggle bit clear was a heartbeat before
// go-canopen v0.5.0) and the frame on 0x180 as the drive's TPDO5, learned
// from the SDO transfer ("configured").
func TestDecodeRecordedCopley(t *testing.T) {
fresh(t)
r := run(t, copleyTrace, "decode", "-", "--format", "json")
want(t, r, 0, `"kind":"guard-response"`, `"summary":"TPDO5 node 1: B7 1F E8 79"`, `"configured":true`)
if strings.Contains(r.out, `"kind":"heartbeat"`) || strings.Count(r.out, `"kind":"guard-response"`) != 2 {
t.Fatalf("guard answers: %s", r.out)
}
want(t, run(t, copleyTrace, "decode", "-", "--service", "tpdo"), 0, "TPDO5 node 1", "1 shown")
}
2 changes: 1 addition & 1 deletion docs/json-output.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Streaming commands write JSON Lines: one compact object per line with

| Command | `type` | Fields |
|---------|--------|--------|
| `monitor`, `decode` | `message` | the frame (`timestamp`, `interface`, `id`, `idHex`, `extended`, `remote`, `len` (up to 64), `fd` and `brs` (CAN FD), `data`), `kind` (`nmt`, `sync`, `time`, `emcy`, `tpdo`, `rpdo`, `sdo-request`, `sdo-response`, `usdo-request`, `usdo-response`, `heartbeat`, `boot-up`, `guard-request`, `guard-response`, `lss-master`, `lss-slave`, `unknown`), `node` (for a USDO message its sender), `summary` (one line), `error` (malformed), and one object for the service: `nmt`, `sync`, `timeStamp`, `emcy` (`code`, `codeText`, `register`, `registerNames`, `msef`, `reset`, and `fd` for a CANopen FD emergency: `logicalDevice`, `ciaSpec`, `status`, `ms`, `days`, `time`), `pdo`, `sdo` (command, phase, object, data, abort code and text, block transfer fields; segments carry the object of their transfer), `usdo` (`client`, `dest`, `destination`, `cs`, `command`, `session`, `index`, `subIndex`, `hasIndex`, `dataType`, `size`, `counter`, `tProc`, `abortCode`, `abortText`, `data`), `errorControl`, `lss` |
| `monitor`, `decode` | `message` | the frame (`timestamp`, `interface`, `id`, `idHex`, `extended`, `remote`, `len` (up to 64), `fd` and `brs` (CAN FD), `data`), `kind` (`nmt`, `sync`, `time`, `emcy`, `tpdo`, `rpdo`, `sdo-request`, `sdo-response`, `usdo-request`, `usdo-response`, `heartbeat`, `boot-up`, `guard-request`, `guard-response`, `lss-master`, `lss-slave`, `unknown`), `node` (for a USDO message its sender), `summary` (one line), `error` (malformed), and one object for the service: `nmt`, `sync`, `timeStamp`, `emcy` (`code`, `codeText`, `register`, `registerNames`, `msef`, `reset`, and `fd` for a CANopen FD emergency: `logicalDevice`, `ciaSpec`, `status`, `ms`, `days`, `time`), `pdo`, `sdo` (command, phase, object, data, abort code and text, block transfer fields; segments carry the object of their transfer), `usdo` (`client`, `dest`, `destination`, `cs`, `command`, `session`, `index`, `subIndex`, `hasIndex`, `dataType`, `size`, `counter`, `tProc`, `abortCode`, `abortText`, `data`), `errorControl`, `lss`. `pdo` is `number`, `transmit` and `configured` (true when the PDO was identified by a COB-ID set or read over SDO earlier in the trace, not by the predefined connection set). A node guarding answer that follows a request is `guard-response` whatever its toggle bit |
| | `summary` | `frames`, `shown`, `malformed`, `dropped`, `kinds` (count per kind) |
| `heartbeat watch` | `heartbeat` | `timestamp`, `node`, `event` (`boot-up`, `state-change`, `timeout`, `resumed`), `state`, `previous` |
| | `summary` | `events`, `timeouts`, `bootUps`, `nodes` (`node`, `state`, `bootUps`, `lost`, `lastSeen`, `timeoutMs`) |
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ module github.com/otfabric/canopenctl
go 1.26.0

require (
github.com/otfabric/go-canbus v0.8.0
github.com/otfabric/go-canopen v0.4.1
github.com/otfabric/go-canbus v0.9.0
github.com/otfabric/go-canopen v0.5.0
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2
github.com/spf13/cobra v1.10.2
github.com/spf13/pflag v1.0.10
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxK
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/otfabric/go-canbus v0.8.0 h1:z0etLlCxHqyQf17MB4/joDrVisNj+gzh1Zxih00xRZc=
github.com/otfabric/go-canbus v0.8.0/go.mod h1:/+y8PRSu7tTGdoh2f7NTxaRAML86ePZSXLfjehaFDgc=
github.com/otfabric/go-canopen v0.4.1 h1:SG9cJCQjCD7ZReE00EpTA5irfu2ueB8kH8HfnZIHm9c=
github.com/otfabric/go-canopen v0.4.1/go.mod h1:mnQwOvdhOfyB10pK1JkiUQdS7jnv58Urbt7ly6eFsDE=
github.com/otfabric/go-canbus v0.9.0 h1:DzRDY3eLzNAFxcqrnls1x2k7TFHDQzxOkQsUCT3N/xc=
github.com/otfabric/go-canbus v0.9.0/go.mod h1:/+y8PRSu7tTGdoh2f7NTxaRAML86ePZSXLfjehaFDgc=
github.com/otfabric/go-canopen v0.5.0 h1:bQJearjMs7yL6lKQgMXHZJd6r2RtTijJr8JWT6J8Ak4=
github.com/otfabric/go-canopen v0.5.0/go.mod h1:kIC0XAM59pii2korvrptJ0O9ucIQSvGimAzQx64eDd4=
github.com/otfabric/go-serial v0.2.1 h1:PCOdnCDAHIHFaxTBNmisC/4SlD+NVd74d+EJPnXrCOw=
github.com/otfabric/go-serial v0.2.1/go.mod h1:TqbYpWWg6W+IuCLs8YM2cndlMAEaTyaKyUX6Z08gtq8=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
Expand Down
2 changes: 1 addition & 1 deletion schemas/record-v1.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"sync": {"type": "object", "required": ["hasCounter"]},
"timeStamp": {"type": "object", "required": ["ms", "days", "time"]},
"emcy": {"$ref": "#/$defs/emcy"},
"pdo": {"type": "object", "required": ["number", "transmit"]},
"pdo": {"type": "object", "required": ["number", "transmit"], "properties": {"number": {"type": "integer", "minimum": 1, "maximum": 512}, "transmit": {"type": "boolean"}, "configured": {"type": "boolean"}}},
"sdo": {"type": "object", "required": ["client", "cs", "command", "phase", "index", "subIndex", "hasIndex"],
"properties": {"phase": {"enum": ["initiate", "segment", "block", "sub-block", "end", "abort", "unknown"]}}},
"usdo": {"type": "object", "required": ["client", "dest", "destination", "cs", "command", "session", "index", "subIndex", "hasIndex"]},
Expand Down
Loading