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  }