Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring, fixes and improvements #5

Merged
merged 46 commits into from
May 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
3e5f611
Reworking HTTP gateway
samsamfire Jan 19, 2024
535f7c6
Moving HTTP gateway to a seperate package
samsamfire Jan 31, 2024
f00d6ab
Starting to have a pretty good architecture, easy to add new routes
samsamfire Feb 5, 2024
ae6f4df
Fixed broken http example
samsamfire Feb 6, 2024
26dc03a
Added TIME configurator with tests
samsamfire Feb 8, 2024
3822336
Seperating CAN driver implementation into a seperate package
samsamfire Mar 14, 2024
fe52200
Major refactor :
samsamfire Mar 16, 2024
51bd379
Reenabled all the tests, and passing
samsamfire Mar 17, 2024
7fb356f
Working on linting improvements
samsamfire Mar 18, 2024
b4991b0
Fixes reported by gocritic : essentially some simplifications, and ad…
samsamfire Mar 18, 2024
7911c17
Replacing long if-else-if with switch statements, less indentation
samsamfire Mar 18, 2024
cb974c1
Embed sdoClient in config objects
samsamfire Mar 22, 2024
c9d7f0b
Docs are not capitalized anymore.
samsamfire Mar 22, 2024
461dc63
Added test for set internal time and moved time origin as a global var
samsamfire Mar 23, 2024
967992f
TIME :
samsamfire Mar 23, 2024
ed84876
Fixed : TIME was checking frame length instead of frame.DLC, meaning …
samsamfire Mar 23, 2024
7c0f125
Added : Read software information objects, will probably refactor
samsamfire Mar 23, 2024
51f8086
Fixed : Logic error introduced in time call subscribe when isConsumer…
samsamfire Mar 23, 2024
0128b4e
Fixed : stop ever growing callbacks when subscribing by checking if h…
samsamfire Mar 23, 2024
dad1c62
Changed : updated od.md documentation
samsamfire Mar 23, 2024
0860623
Changed : tiny bit of cleaning
samsamfire Mar 23, 2024
141e072
Fixed : added inhibit time entry to addPDO, which was causing NewRemo…
samsamfire Mar 23, 2024
4f715b4
Fixed : race conditions on TIME object
samsamfire Mar 23, 2024
f65ebca
Fixed : getting rid of race conditions with -race tool
samsamfire Mar 23, 2024
f7f14c4
Fix : add locking for SYNC object
samsamfire Mar 23, 2024
a94b7d5
Fix : error in Subscribe callback in for look, func parameter had sam…
samsamfire Mar 23, 2024
03ef546
Fixed : Deadlock for sdo abort
samsamfire Mar 24, 2024
547cad6
Fixed : Deadlock in SYNC send, when receiving own messages. We need t…
samsamfire Mar 24, 2024
971c4ab
Fixed : race condition on SYNC extensions, because the []byte slice c…
samsamfire Mar 24, 2024
31ffaa6
Changed : PDOCommon is now a pointer, for project consistency and als…
samsamfire Mar 24, 2024
95656c3
Fixed / Changed : Variable objects now have a mutex to prevent concur…
samsamfire Mar 24, 2024
0863cfd
Changed : moved CAN interface to top level (in canopen module)
samsamfire Mar 29, 2024
c21971f
Fixed : fixed python tests, should run properly
samsamfire Mar 29, 2024
82acec0
Changed : embed Stream in Streamer
samsamfire Mar 29, 2024
0bf41f0
Changed : AddRemoteNode does not initialize PDOs straight away, Start…
samsamfire Apr 1, 2024
afa1461
Refactored : Moved node specific tests to node_test (removed network_…
samsamfire Apr 1, 2024
9b406e6
Refactored : moved read/write node to seperate sdo file
samsamfire Apr 1, 2024
dcc708b
Feature : Add ReadIdentity and ReadManufacturerInformation, added tests
samsamfire Apr 2, 2024
7a3ea85
Changed : RemoveNode returns an error in case no id was found
samsamfire Apr 2, 2024
8fc779c
Changed : renamed Encode in od to EncodeFromString and added EncodeFr…
samsamfire Apr 11, 2024
2e45054
Changed/Feature : changes for implementing io.Reader and io.Writer in…
samsamfire Apr 11, 2024
cd7eeff
Fixed : wrong order inside downloadMain of sdo client
samsamfire Apr 15, 2024
844be19
Added : profiling in canopen_test
samsamfire Apr 27, 2024
84d9a72
Changed : piling up defers in HB consumer seems to increase CPU usage…
samsamfire Apr 27, 2024
ba70a30
Refactored : changed kebab_case to camelCase for NMT related constants
samsamfire Apr 28, 2024
4936852
Refactored : continued refactoring of kebab_case constants
samsamfire Apr 28, 2024
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
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
Loading