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 }