github.com/blueinnovationsgroup/can-go@v0.0.0-20230518195432-d0567cda0028/pkg/canrunner/run.go (about) 1 package canrunner 2 3 import ( 4 "context" 5 "fmt" 6 "net" 7 "strings" 8 "sync" 9 "time" 10 11 "github.com/blueinnovationsgroup/can-go" 12 "github.com/blueinnovationsgroup/can-go/internal/clock" 13 "github.com/blueinnovationsgroup/can-go/pkg/descriptor" 14 "github.com/blueinnovationsgroup/can-go/pkg/generated" 15 "github.com/blueinnovationsgroup/can-go/pkg/socketcan" 16 "golang.org/x/sync/errgroup" 17 ) 18 19 // defaultSendTimeout is the send timeout used for messages without a cycle time. 20 const defaultSendTimeout = time.Second 21 22 // Node is an interface for a CAN node to be run by the runner. 23 type Node interface { 24 sync.Locker 25 Connect() (net.Conn, error) 26 Descriptor() *descriptor.Node 27 TransmittedMessages() []TransmittedMessage 28 ReceivedMessage(id uint32) (ReceivedMessage, bool) 29 } 30 31 // TransmittedMessage is an interface for a message to be transmitted by the runner. 32 type TransmittedMessage interface { 33 generated.Message 34 // SetTransmitTime sets the time the message was last transmitted. 35 SetTransmitTime(time.Time) 36 // IsCyclicTransmissionEnabled returns true when cyclic transmission is enabled. 37 IsCyclicTransmissionEnabled() bool 38 // WakeUpChan returns a channel for waking up and checking if cyclic transmission is enabled. 39 WakeUpChan() <-chan struct{} 40 // TransmitEventChan returns channel for event-based transmission of the message. 41 TransmitEventChan() <-chan struct{} 42 // BeforeTransmitHook returns a function to be called before the message is transmitted. 43 // 44 // If the hook returns an error, the transmitter halt. 45 BeforeTransmitHook() func(context.Context) error 46 } 47 48 // ReceivedMessage is an interface for a message to be received by the runner. 49 type ReceivedMessage interface { 50 generated.Message 51 // SetReceiveTime sets the time the message was last received. 52 SetReceiveTime(time.Time) 53 // AfterReceiveHook returns a function to be called after the message has been received. 54 // 55 // If the hook returns an error, the receiver will halt. 56 AfterReceiveHook() func(context.Context) error 57 } 58 59 // FrameTransmitter is an interface for the the CAN frame transmitter used by the runner. 60 type FrameTransmitter interface { 61 TransmitFrame(context.Context, can.Frame) error 62 } 63 64 // FrameReceiver is an interface for the CAN frame receiver used by the runner. 65 type FrameReceiver interface { 66 Receive() bool 67 Frame() can.Frame 68 Err() error 69 } 70 71 func Run(ctx context.Context, n Node) error { 72 conn, err := n.Connect() 73 if err != nil { 74 return fmt.Errorf("run %s node: %w", n.Descriptor().Name, err) 75 } 76 g, ctx := errgroup.WithContext(ctx) 77 g.Go(func() error { 78 <-ctx.Done() 79 return conn.Close() 80 }) 81 g.Go(func() error { 82 rx := socketcan.NewReceiver(conn) 83 return RunMessageReceiver(ctx, rx, n, clock.System()) 84 }) 85 for _, m := range n.TransmittedMessages() { 86 m := m 87 g.Go(func() error { 88 tx := socketcan.NewTransmitter(conn) 89 return RunMessageTransmitter(ctx, tx, n, m, clock.System()) 90 }) 91 } 92 if err := g.Wait(); err != nil { 93 if strings.Contains(err.Error(), "closed") { 94 return nil 95 } 96 return fmt.Errorf("run %s node: %w", n.Descriptor().Name, err) 97 } 98 return nil 99 } 100 101 func RunMessageReceiver(ctx context.Context, rx FrameReceiver, n Node, c clock.Clock) error { 102 for rx.Receive() { 103 f := rx.Frame() 104 m, ok := n.ReceivedMessage(f.ID) 105 if !ok { 106 continue 107 } 108 n.Lock() 109 hook := m.AfterReceiveHook() 110 m.SetReceiveTime(c.Now()) 111 err := m.UnmarshalFrame(f) 112 n.Unlock() 113 if err != nil { 114 return fmt.Errorf("receiver: %w", err) 115 } 116 if err := hook(ctx); err != nil { 117 return fmt.Errorf("receiver: %w", err) 118 } 119 } 120 if err := rx.Err(); err != nil { 121 return fmt.Errorf("receiver: %w", err) 122 } 123 return nil 124 } 125 126 func RunMessageTransmitter( 127 ctx context.Context, 128 tx FrameTransmitter, 129 l sync.Locker, 130 m TransmittedMessage, 131 c clock.Clock, 132 ) error { 133 sendTimeout := m.Descriptor().CycleTime 134 if sendTimeout == 0 { 135 sendTimeout = defaultSendTimeout 136 } 137 var cyclicTransmissionTicker *time.Ticker 138 var cyclicTransmissionTickChan <-chan time.Time 139 enableCyclicTransmission := func() { 140 isCyclic := m.Descriptor().SendType == descriptor.SendTypeCyclic 141 hasCycleTime := m.Descriptor().CycleTime > 0 142 if !isCyclic || !hasCycleTime || cyclicTransmissionTicker != nil { 143 return 144 } 145 cyclicTransmissionTicker = time.NewTicker(m.Descriptor().CycleTime) 146 cyclicTransmissionTickChan = cyclicTransmissionTicker.C 147 } 148 disableCyclicTransmission := func() { 149 if cyclicTransmissionTicker == nil { 150 return 151 } 152 cyclicTransmissionTicker.Stop() 153 cyclicTransmissionTicker = nil 154 } 155 setCyclicTransmission := func() { 156 l.Lock() 157 isCyclicTransmissionEnabled := m.IsCyclicTransmissionEnabled() 158 l.Unlock() 159 if isCyclicTransmissionEnabled { 160 enableCyclicTransmission() 161 } else { 162 disableCyclicTransmission() 163 } 164 } 165 transmit := func() error { 166 l.Lock() 167 hook := m.BeforeTransmitHook() 168 m.SetTransmitTime(c.Now()) 169 l.Unlock() 170 if err := hook(ctx); err != nil { 171 return fmt.Errorf("%s transmitter: %w", m.Descriptor().Name, err) 172 } 173 l.Lock() 174 f := m.Frame() 175 l.Unlock() 176 ctx, cancel := context.WithTimeout(ctx, sendTimeout) 177 err := tx.TransmitFrame(ctx, f) 178 cancel() 179 if err != nil { 180 return fmt.Errorf("%s transmitter: %w", m.Descriptor().Name, err) 181 } 182 return nil 183 } 184 ctxDone := ctx.Done() 185 transmitEventChan := m.TransmitEventChan() 186 setCyclicTransmission() 187 wakeUpChan := m.WakeUpChan() 188 for { 189 select { 190 case <-ctxDone: 191 return nil 192 case <-wakeUpChan: 193 setCyclicTransmission() 194 case <-transmitEventChan: 195 if err := transmit(); err != nil { 196 return err 197 } 198 case <-cyclicTransmissionTickChan: 199 if err := transmit(); err != nil { 200 return err 201 } 202 } 203 } 204 }