github.com/simpleiot/simpleiot@v0.18.3/client/client-state.go (about)

     1  package client
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/nats-io/nats.go"
    10  	"github.com/simpleiot/simpleiot/data"
    11  )
    12  
    13  // clientState wraps the client, passes in initial state, and then runs the client
    14  type clientState[T any] struct {
    15  	nc   *nats.Conn
    16  	node data.NodeEdge
    17  	nec  data.NodeEdgeChildren
    18  
    19  	client Client
    20  
    21  	stopOnce sync.Once
    22  	chStop   chan struct{}
    23  }
    24  
    25  func newClientState[T any](nc *nats.Conn, construct func(*nats.Conn, T) Client,
    26  	n data.NodeEdge) (*clientState[T], error) {
    27  
    28  	c, err := GetNodes(nc, n.ID, "all", "", false)
    29  	if err != nil {
    30  		return nil, fmt.Errorf("Error getting children: %v", err)
    31  	}
    32  
    33  	ncc := make([]data.NodeEdgeChildren, len(c))
    34  
    35  	for i, nci := range c {
    36  		ncc[i] = data.NodeEdgeChildren{NodeEdge: nci, Children: nil}
    37  	}
    38  
    39  	nec := data.NodeEdgeChildren{NodeEdge: n, Children: ncc}
    40  
    41  	var config T
    42  
    43  	err = data.Decode(nec, &config)
    44  	if err != nil {
    45  		return nil, fmt.Errorf("Error decoding node: %w", err)
    46  	}
    47  
    48  	client := construct(nc, config)
    49  
    50  	ret := &clientState[T]{
    51  		nc:     nc,
    52  		node:   n,
    53  		nec:    nec,
    54  		client: client,
    55  		chStop: make(chan struct{}),
    56  	}
    57  
    58  	return ret, nil
    59  }
    60  
    61  func (cs *clientState[T]) run() (err error) {
    62  
    63  	chClientStopped := make(chan struct{})
    64  
    65  	go func() {
    66  		// the following blocks until client exits
    67  		err := cs.client.Run()
    68  		if err != nil {
    69  			log.Printf("Client Run %v %v returned error: %v\n",
    70  				cs.node.Type, cs.node.ID, err)
    71  		}
    72  		close(chClientStopped)
    73  	}()
    74  
    75  	<-cs.chStop
    76  	cs.client.Stop(nil)
    77  
    78  	select {
    79  	case <-chClientStopped:
    80  		// everything is OK
    81  	case <-time.After(5 * time.Second):
    82  		log.Println("Timeout stopping client:", cs.node.Type, cs.node.ID)
    83  	}
    84  
    85  	return nil
    86  }
    87  
    88  func (cs *clientState[T]) stop(_ error) {
    89  	cs.stopOnce.Do(func() { close(cs.chStop) })
    90  }