@@ -19,8 +19,12 @@ import (
1919 "github.com/brutella/can"
2020 rs "github.com/jens18/lgresu/lgresustatus"
2121 log "github.com/sirupsen/logrus"
22+ "io/ioutil"
23+ "net"
2224 "net/http"
2325 "net/http/httptest"
26+ "os/exec"
27+ "syscall"
2428 "testing"
2529 "time"
2630)
@@ -48,9 +52,77 @@ func (c *MockCanbus) Disconnect() error {
4852 return nil
4953}
5054
55+ // canbusTestMessages contains test messages generated by the LG Resu 10 LV battery BMS
56+ var canbusTestMessages = []struct {
57+ Identifier uint32
58+ Data [8 ]byte
59+ }{
60+ // volt/amp/temp (LG Resu -> Inverter):
61+ {
62+ Identifier : rs .BMS_VOLT_AMP_TEMP ,
63+ Data : [8 ]byte {0x4b , 0x15 , 0xed , 0xff , 0xba , 0x00 , 0x00 , 0x00 },
64+ },
65+ // ? (LG Resu -> Inverter): unknown message type (appears to be a constant)
66+ {
67+ Identifier : rs .BMS_SERIAL_NUM ,
68+ Data : [8 ]byte {0x04 , 0xc0 , 0x00 , 0x1f , 0x03 , 0x00 , 0x00 , 0x00 },
69+ },
70+ // configuration parameters (LG Resu -> Inverter):
71+ {
72+ Identifier : rs .BMS_LIMITS ,
73+ Data : [8 ]byte {0x41 , 0x02 , 0x96 , 0x03 , 0x96 , 0x03 , 0x00 , 0x00 },
74+ },
75+ // state of charge/health (LG Resu -> Inverter):
76+ {
77+ Identifier : rs .BMS_SOC_SOH ,
78+ Data : [8 ]byte {0x4d , 0x00 , 0x63 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 },
79+ },
80+ // warnings/alarms (LG Resu -> Inverter):
81+ {
82+ Identifier : rs .BMS_WARN_ALARM ,
83+ Data : [8 ]byte {0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 },
84+ },
85+ }
86+
87+ func sendCanbusMessages (ifName string ) {
88+ iface , err := net .InterfaceByName (ifName )
89+
90+ if err != nil {
91+ log .Fatalf ("sendCanbusMessages(): Could not find network interface %s (%v) \n " , ifName , err )
92+ }
93+
94+ // bind to socket
95+ conn , err := can .NewReadWriteCloserForInterface (iface )
96+
97+ if err != nil {
98+ log .Fatalf ("sendCanbusMessages(): Could not bind to socket %v \n " , err )
99+ }
100+
101+ bus := can .NewBus (conn )
102+
103+ f := can.Frame {}
104+
105+ for {
106+ // send all LG Resu 10 test messages in one block
107+ for _ , tm := range canbusTestMessages {
108+
109+ f .ID = tm .Identifier
110+ f .Length = uint8 (len (tm .Data ))
111+ f .Data = tm .Data
112+
113+ bus .Publish (f )
114+
115+ log .Debugf ("%#4x # % -24X \n " , tm .Identifier , tm .Data )
116+ }
117+
118+ // wait for 1 second
119+ <- time .After (time .Second * 1 )
120+ }
121+ }
122+
51123func init () {
52124 // only log warning severity or above.
53- log .SetLevel (log .DebugLevel )
125+ log .SetLevel (log .WarnLevel )
54126}
55127
56128// https://blog.questionable.services/article/testing-http-handlers-go/
@@ -137,8 +209,6 @@ func TestWriteRecord(t *testing.T) {
137209
138210 time .Sleep (3 * time .Second )
139211
140- t .Logf ("dataRecorder invocations: %d \n " , dataRecorder .Cnt )
141-
142212 if dataRecorder .Cnt != 2 {
143213 t .Errorf ("writeRecorder() requested %d lgResu objects, expect %d requests \n " ,
144214 dataRecorder .Cnt , 2 )
@@ -197,7 +267,7 @@ func TestDecodeCanFrame(t *testing.T) {
197267
198268 lgResu := rs.LgResuStatus {}
199269 lgResu = <- recordEmitChan
200- t . Logf ( "lgResu.Soc = %d \n " , lgResu . Soc )
270+
201271 if lgResu .Soc != 77 {
202272 t .Errorf ("decodeCanFrame() produce Soc = %d, expect Soc = 77 \n " ,
203273 lgResu .Soc )
@@ -243,3 +313,51 @@ func TestBrokerRecord(t *testing.T) {
243313 lgResuWrite .Soc )
244314 }
245315}
316+
317+ // TestIntegration tests if the HTTP request returns a JSON object.
318+ func TestIntegration (t * testing.T ) {
319+
320+ // start lgresu_mon server, combine stdout and stderr
321+ cmd := exec .Command ("sh" , "-c" ,
322+ "go run lgresu_mon.go lgresu_actors.go -if vcan0 -d debug -p 9090 -dr data -r 7 > lg_resu_mon.log 2>&1" )
323+ // https://medium.com/@felixge/killing-a-child-process-and-all-of-its-children-in-go-54079af94773
324+ cmd .SysProcAttr = & syscall.SysProcAttr {Setpgid : true }
325+
326+ // start in background
327+ err := cmd .Start ()
328+ if err != nil {
329+ log .Fatal (err )
330+ }
331+
332+ // generate CANBus messages
333+ go sendCanbusMessages ("vcan0" )
334+
335+ // time to startup lg_resu_mon server
336+ time .Sleep (3 * time .Second )
337+
338+ // HTTP request
339+ resp , err := http .Get ("http://localhost:9090" )
340+ if err != nil {
341+ t .Log (err ) // handle error
342+ }
343+ defer resp .Body .Close ()
344+ body , err := ioutil .ReadAll (resp .Body )
345+
346+ log .Debugf ("body = %s \n " , body )
347+
348+ // re-constructed lgResuStatus message
349+ var status rs.LgResuStatus
350+ err = json .Unmarshal (body , & status )
351+ if err != nil {
352+ t .Error (err )
353+ }
354+
355+ // test Soc received against Soc send
356+ if status .Soc != 77 {
357+ t .Errorf ("Index handler() returned Soc value %v, expect Soc value %v \n " ,
358+ status .Soc , 77 )
359+ }
360+
361+ // stop process when test is complete
362+ syscall .Kill (- cmd .Process .Pid , syscall .SIGKILL )
363+ }
0 commit comments