Skip to content

Commit

Permalink
Merge pull request #5 from samsamfire/dev
Browse files Browse the repository at this point in the history
Refactoring, fixes and improvements
  • Loading branch information
samsamfire authored May 1, 2024
2 parents 369b1a4 + 4936852 commit 71e5bdd
Show file tree
Hide file tree
Showing 107 changed files with 7,603 additions and 6,053 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
__pycache__/
*.py[cod]
*$py.class
*.vscode
*.vscode
node_modules
129 changes: 20 additions & 109 deletions bus.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
package canopen

import (
"fmt"

log "github.com/sirupsen/logrus"
)

const canRtrFlag uint32 = 0x40000000
const canSffMask uint32 = 0x000007FF

// CAN bus errors
const (
canErrorTxWarning = 0x0001 // CAN transmitter warning
canErrorTxPassive = 0x0002 // CAN transmitter passive
canErrorTxBusOff = 0x0004 // CAN transmitter bus off
canErrorTxOverflow = 0x0008 // CAN transmitter overflow
canErrorPdoLate = 0x0080 // TPDO is outside sync window
canErrorRxWarning = 0x0100 // CAN receiver warning
canErrorRxPassive = 0x0200 // CAN receiver passive
canErrorRxOverflow = 0x0800 // CAN receiver overflow */
canErrorWarnPassive = 0x0303 // Combination
CanErrorTxWarning = 0x0001 // CAN transmitter warning
CanErrorTxPassive = 0x0002 // CAN transmitter passive
CanErrorTxBusOff = 0x0004 // CAN transmitter bus off
CanErrorTxOverflow = 0x0008 // CAN transmitter overflow
CanErrorPdoLate = 0x0080 // TPDO is outside sync window
CanErrorRxWarning = 0x0100 // CAN receiver warning
CanErrorRxPassive = 0x0200 // CAN receiver passive
CanErrorRxOverflow = 0x0800 // CAN receiver overflow
CanErrorWarnPassive = 0x0303 // Combination
CanRtrFlag uint32 = 0x40000000
CanSffMask uint32 = 0x000007FF
)

// A CAN frame
// A CAN Bus interface
type Bus interface {
Connect(...any) error // Connect to the CAN bus
Disconnect() error // Disconnect from CAN bus
Send(frame Frame) error // Send a frame on the bus
Subscribe(callback FrameListener) error // Subscribe to all received CAN frames
}

// A generic 11bit CAN frame
type Frame struct {
ID uint32
Flags uint8
Expand All @@ -38,92 +38,3 @@ func NewFrame(id uint32, flags uint8, dlc uint8) Frame {
type FrameListener interface {
Handle(frame Frame)
}

// A CAN Bus interface
type Bus interface {
Connect(...any) error // Connect to the CAN bus
Disconnect() error // Disconnect from CAN bus
Send(frame Frame) error // Send a frame on the bus
Subscribe(callback FrameListener) error // Subscribe to all received CAN frames
}

// Bus manager is a wrapper around the CAN bus interface
// Used by the CANopen stack to control errors, callbacks for specific IDs, etc.
type busManager struct {
bus Bus // Bus interface that can be adapted
frameListeners map[uint32][]FrameListener
canError uint16
}

// Implements the FrameListener interface
// This handles all received CAN frames from Bus
func (bm *busManager) Handle(frame Frame) {
listeners, ok := bm.frameListeners[frame.ID]
if !ok {
return
}
for _, listener := range listeners {
listener.Handle(frame)
}
}

// Send a CAN message
// Limited error handling
func (bm *busManager) Send(frame Frame) error {
err := bm.bus.Send(frame)
if err != nil {
log.Warnf("[CAN ERROR] %v", err)
}
return err
}

// This should be called cyclically to update errors
func (bm *busManager) process() error {
// TODO get bus state error
bm.canError = 0
return nil
}

// Subscribe to a specific CAN ID
func (bm *busManager) Subscribe(ident uint32, mask uint32, rtr bool, callback FrameListener) error {
ident = ident & canSffMask
if rtr {
ident |= canRtrFlag
}
_, ok := bm.frameListeners[ident]
if !ok {
bm.frameListeners[ident] = []FrameListener{callback}
return nil
}
// TODO add error if callback exists already
bm.frameListeners[ident] = append(bm.frameListeners[ident], callback)
return nil
}

func NewBusManager(bus Bus) *busManager {
bm := &busManager{
bus: bus,
frameListeners: make(map[uint32][]FrameListener),
canError: 0,
}
return bm
}

// Create Bus from local available buses
// Currently supported : socketcan, virtualcan
func createBusInternal(canInterface string, channel string, bitrate int) (Bus, error) {
var bus Bus
var err error
switch canInterface {
case "socketcan", "":
bus, err = NewSocketCanBus(channel)
case "virtualcan":
bus = NewVirtualCanBus(channel)
default:
err = fmt.Errorf("unsupported interface : %v", canInterface)
}
if err != nil {
return nil, err
}
return bus, err
}
102 changes: 102 additions & 0 deletions bus_manager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package canopen

import (
"sync"

log "github.com/sirupsen/logrus"
)

// Bus manager is a wrapper around the CAN bus interface
// Used by the CANopen stack to control errors, callbacks for specific IDs, etc.
type BusManager struct {
mu sync.Mutex
bus Bus // Bus interface that can be adapted
frameListeners map[uint32][]FrameListener
canError uint16
}

// Implements the FrameListener interface
// This handles all received CAN frames from Bus
func (bm *BusManager) Handle(frame Frame) {
bm.mu.Lock()
defer bm.mu.Unlock()
listeners, ok := bm.frameListeners[frame.ID]
if !ok {
return
}
for _, listener := range listeners {
listener.Handle(frame)
}
}

// Set bus
func (bm *BusManager) SetBus(bus Bus) {
bm.mu.Lock()
defer bm.mu.Unlock()
bm.bus = bus
}

func (bm *BusManager) Bus() Bus {
bm.mu.Lock()
defer bm.mu.Unlock()
return bm.bus
}

// Send a CAN message
// Limited error handling
func (bm *BusManager) Send(frame Frame) error {
err := bm.bus.Send(frame)
if err != nil {
log.Warnf("[CAN] %v", err)
}
return err
}

// This should be called cyclically to update errors
func (bm *BusManager) Process() error {
bm.mu.Lock()
defer bm.mu.Unlock()
// TODO get bus state error
bm.canError = 0
return nil
}

// Subscribe to a specific CAN ID
func (bm *BusManager) Subscribe(ident uint32, mask uint32, rtr bool, callback FrameListener) error {
bm.mu.Lock()
defer bm.mu.Unlock()
ident = ident & CanSffMask
if rtr {
ident |= CanRtrFlag
}
_, ok := bm.frameListeners[ident]
if !ok {
bm.frameListeners[ident] = []FrameListener{callback}
return nil
}
// Iterate over all callbacks and verify that we are not adding the same one twice
for _, cb := range bm.frameListeners[ident] {
if cb == callback {
log.Warnf("[CAN] callback for frame id %x already added", ident)
return nil
}
}
bm.frameListeners[ident] = append(bm.frameListeners[ident], callback)
return nil
}

// Get CAN error
func (bm *BusManager) Error() uint16 {
bm.mu.Lock()
defer bm.mu.Unlock()
return bm.canError
}

func NewBusManager(bus Bus) *BusManager {
bm := &BusManager{
bus: bus,
frameListeners: make(map[uint32][]FrameListener),
canError: 0,
}
return bm
}
7 changes: 4 additions & 3 deletions cmd/canopen_http/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import (
"flag"
"fmt"

canopen "github.com/samsamfire/gocanopen"
"github.com/samsamfire/gocanopen/pkg/gateway/http"
"github.com/samsamfire/gocanopen/pkg/network"
log "github.com/sirupsen/logrus"
)

Expand All @@ -24,12 +25,12 @@ func main() {
channel := flag.String("i", DEFAULT_CAN_INTERFACE, "socketcan channel e.g. can0,vcan0")
flag.Parse()

network := canopen.NewNetwork(nil)
network := network.NewNetwork(nil)
e := network.Connect("", *channel, 500000)
if e != nil {
panic(e)
}
gateway := canopen.NewGateway(1, 1, 100, &network)
gateway := http.NewGatewayServer(&network, 1, 1, 1000)
gateway.ListenAndServe(fmt.Sprintf(":%d", DEFAULT_HTTP_PORT))

}
8 changes: 5 additions & 3 deletions cmd/canopen_master/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
package main

import (
canopen "github.com/samsamfire/gocanopen"
"github.com/samsamfire/gocanopen/pkg/network"
log "github.com/sirupsen/logrus"
)

Expand All @@ -14,17 +14,19 @@ var EDS_PATH = "../../testdata/base.eds"
func main() {
log.SetLevel(log.DebugLevel)

network := canopen.NewNetwork(nil)
network := network.NewNetwork(nil)
err := network.Connect("socketcan", DEFAULT_CAN_INTERFACE, DEFAULT_CAN_BITRATE)
if err != nil {
panic(err)
}

// Add a remote node for master control
node, err := network.AddRemoteNode(DEFAULT_NODE_ID, "../../testdata/base.eds", true)
node, err := network.AddRemoteNode(DEFAULT_NODE_ID, "../../testdata/base.eds")
if err != nil {
panic(err)
}
// Start PDOs, without reading remote configuration (useLocal = true)
node.StartPDOs(true)
// Read values via SDO
val, err := node.ReadUint("UNSIGNED32 value", "")
if err == nil {
Expand Down
25 changes: 13 additions & 12 deletions cmd/canopen_test/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ package main
// Demo used for automated testing

import (
"flag"
"os"

canopen "github.com/samsamfire/gocanopen"
"net/http"
_ "net/http/pprof"

"github.com/samsamfire/gocanopen/pkg/network"
"github.com/samsamfire/gocanopen/pkg/od"
log "github.com/sirupsen/logrus"
)

Expand All @@ -21,26 +24,24 @@ const (

func main() {
log.SetLevel(log.DebugLevel)
// Command line arguments
eds_path := flag.String("p", "", "eds file path")
flag.Parse()

network := canopen.NewNetwork(nil)
go func() {
http.ListenAndServe("localhost:6060", nil)
}()

network := network.NewNetwork(nil)
err := network.Connect("virtualcan", "127.0.0.1:18889", 500000)
if err != nil {
panic(err)
}

// Load node EDS, this will be used to generate all the CANopen objects
// Basic template can be found in the current directory
node, err := network.CreateLocalNode(uint8(DEFAULT_NODE_ID), *eds_path)
if err != nil {
panic(err)
}
// Add file extension
err = node.GetOD().AddFile(0x200F, "File", "example.bin", os.O_RDONLY|os.O_CREATE, os.O_CREATE|os.O_TRUNC|os.O_WRONLY)
node, err := network.CreateLocalNode(uint8(DEFAULT_NODE_ID), od.Default())
if err != nil {
panic(err)
}
//Add file extension
node.GetOD().AddFile(0x200F, "File", "example.bin", os.O_RDONLY|os.O_CREATE, os.O_CREATE|os.O_TRUNC|os.O_WRONLY)
select {}
}
27 changes: 0 additions & 27 deletions docs/OD.md

This file was deleted.

File renamed without changes.
File renamed without changes.
Loading

0 comments on commit 71e5bdd

Please sign in to comment.