diff --git a/.golangci.yml b/.golangci.yml index 225d024..68c14b5 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -9,7 +9,7 @@ linters: presets: - bugs - comment - # - complexity + - complexity - error # - format - import @@ -20,6 +20,8 @@ linters: # - test - unused linters-settings: + cyclop: + max-complexity: 12 depguard: rules: main: @@ -29,3 +31,5 @@ linters-settings: - $gostd - github.com/go-ble/ble - github.com/stefanjenkner/fdf-console-monitor + funlen: + ignore-comments: true diff --git a/pkg/fitnessmachine/fitnessmachine.go b/pkg/fitnessmachine/fitnessmachine.go index c7646ad..00bdd48 100644 --- a/pkg/fitnessmachine/fitnessmachine.go +++ b/pkg/fitnessmachine/fitnessmachine.go @@ -70,6 +70,7 @@ func (f *FitnessMachine) Stop() { log.Println("Stopped FitnessMachine") } +//cyclop:ignore func (f *FitnessMachine) rowerDataNotifyHandler(_ ble.Request, n ble.Notifier) { log.Println("Subscription started") @@ -107,45 +108,31 @@ func (f *FitnessMachine) rowerDataNotifyHandler(_ ble.Request, n ble.Notifier) { // Bit 0 - Stroke rate and Stroke count (1 if NOT present) if dataEvent.StrokesPerMinute != nil && dataEvent.Strokes != nil { - strokeRate := uint8(*(dataEvent.StrokesPerMinute) * 2) - featureData = append(featureData, strokeRate) - strokeCount := make([]byte, 2) - binary.LittleEndian.PutUint16(strokeCount, *dataEvent.Strokes) - featureData = append(featureData, strokeCount...) + featureData = appendUint8(featureData, *(dataEvent.StrokesPerMinute)*2) + featureData = appendUint16(featureData, *dataEvent.Strokes) featureData[0] ^= 1 << 0 } // Bit 2 - Total Distance if dataEvent.Distance != nil { - distance := *dataEvent.Distance - totalDistance := make([]byte, 3) - totalDistance[0] = byte(distance & 255) - totalDistance[1] = byte((distance >> 8) & 255) - totalDistance[2] = 0 - featureData = append(featureData, totalDistance...) + featureData = appendUint24(featureData, *dataEvent.Distance) featureData[0] |= 4 } // Bit 3 - Instantaneous Pace if dataEvent.Time500mSplit != nil { - instantaneousPace := make([]byte, 2) - binary.LittleEndian.PutUint16(instantaneousPace, *dataEvent.Time500mSplit) - featureData = append(featureData, instantaneousPace...) + featureData = appendUint16(featureData, *dataEvent.Time500mSplit) featureData[0] |= 8 } // Bit 5 - Instantaneous Power if dataEvent.WattsPreviousStroke != nil { - instantaneousPower := make([]byte, 2) - binary.LittleEndian.PutUint16(instantaneousPower, *dataEvent.WattsPreviousStroke) - featureData = append(featureData, instantaneousPower...) + featureData = appendUint16(featureData, *dataEvent.WattsPreviousStroke) featureData[0] |= 32 } // Bit 11 - Elapsed Time in seconds - elapsedTime := make([]byte, 2) - binary.LittleEndian.PutUint16(elapsedTime, dataEvent.ElapsedTime) - featureData = append(featureData, elapsedTime...) + featureData = appendUint16(featureData, dataEvent.ElapsedTime) featureData[1] |= 8 _, err := n.Write(featureData) @@ -195,3 +182,21 @@ func (f *FitnessMachine) OnData(event events.DataEvent) { func (f *FitnessMachine) OnStatusChange(event events.StatusChangeEvent) { log.Printf("OnStatusChangeEvent: %+v\n", event) } + +func appendUint8(slice []byte, v uint8) []byte { + return append(slice, v) +} + +func appendUint16(slice []byte, v uint16) []byte { + bytes := make([]byte, 2) + binary.LittleEndian.PutUint16(bytes, v) + return append(slice, bytes...) +} + +func appendUint24(slice []byte, v uint16) []byte { + bytes := make([]byte, 3) + bytes[0] = byte(v & 255) + bytes[1] = byte((v >> 8) & 255) + bytes[2] = 0 + return append(slice, bytes...) +} diff --git a/pkg/serialmonitor/serialmonitor_test.go b/pkg/serialmonitor/serialmonitor_test.go index fbba818..038efdd 100644 --- a/pkg/serialmonitor/serialmonitor_test.go +++ b/pkg/serialmonitor/serialmonitor_test.go @@ -22,7 +22,7 @@ type MockObserver struct { statusChangeEvents []events.StatusChangeEvent } -func TestSerialMonitor_Run(t *testing.T) { +func TestSerialMonitor_RunCallsObserverForDataEvents(t *testing.T) { bufferString := bytes.NewBufferString("") // session one bufferString.WriteString("A8000040000710428014108067004\r\n") @@ -38,20 +38,13 @@ func TestSerialMonitor_Run(t *testing.T) { // connection check bufferString.WriteString("W\r\n") - mockSerialPort := MockSerialPort{ - readBuffer: *bufferString, - writeBuffer: bytes.Buffer{}, - } - var port serial.Port = &mockSerialPort + mockSerialPort, port := NewMockSerialPort(bufferString) serialMonitor := SerialMonitor{ portName: "/dev/mocked/serial/port", port: &port, observers: map[events.Observer]struct{}{}, } - observer := &MockObserver{ - dataEvents: make([]events.DataEvent, 0), - statusChangeEvents: make([]events.StatusChangeEvent, 0), - } + observer := NewMockObserver() serialMonitor.AddObserver(observer) serialMonitor.Run() @@ -62,57 +55,12 @@ func TestSerialMonitor_Run(t *testing.T) { return &v } wantedDataEvents := []events.DataEvent{ - { - ElapsedTime: 4, - Level: 4, - Distance: uint16Ptr(7), - Time500mSplit: uint16Ptr(268), - Strokes: uint16Ptr(1), - StrokesPerMinute: uint8Ptr(14), - WattsPreviousStroke: uint16Ptr(108), - CaloriesPerHour: uint16Ptr(670), - }, { - ElapsedTime: 6, - Level: 4, - Distance: uint16Ptr(14), - Time500mSplit: uint16Ptr(163), - Strokes: uint16Ptr(2), - StrokesPerMinute: uint8Ptr(28), - WattsPreviousStroke: uint16Ptr(105), - CaloriesPerHour: uint16Ptr(659), - }, { - ElapsedTime: 8, - Level: 4, - Distance: uint16Ptr(21), - Time500mSplit: uint16Ptr(148), - Strokes: uint16Ptr(3), - StrokesPerMinute: uint8Ptr(29), - WattsPreviousStroke: uint16Ptr(109), - CaloriesPerHour: uint16Ptr(674), - }, { - ElapsedTime: 2, - Level: 4, - RemainingDistance: uint16Ptr(0), - Time500mAverage: uint16Ptr(0), - WattsAverage: uint16Ptr(0), - CaloriesTotal: uint16Ptr(0), - }, { - ElapsedTime: 5, - Level: 4, - Distance: uint16Ptr(8), - Time500mSplit: uint16Ptr(319), - Strokes: uint16Ptr(1), - StrokesPerMinute: uint8Ptr(11), - WattsPreviousStroke: uint16Ptr(106), - CaloriesPerHour: uint16Ptr(663), - }, { - ElapsedTime: 1810, - Level: 4, - RemainingDistance: uint16Ptr(6015), - Time500mAverage: uint16Ptr(153), - WattsAverage: uint16Ptr(109), - CaloriesTotal: uint16Ptr(400), - }, + {ElapsedTime: 4, Level: 4, Distance: uint16Ptr(7), Time500mSplit: uint16Ptr(268), Strokes: uint16Ptr(1), StrokesPerMinute: uint8Ptr(14), WattsPreviousStroke: uint16Ptr(108), CaloriesPerHour: uint16Ptr(670)}, + {ElapsedTime: 6, Level: 4, Distance: uint16Ptr(14), Time500mSplit: uint16Ptr(163), Strokes: uint16Ptr(2), StrokesPerMinute: uint8Ptr(28), WattsPreviousStroke: uint16Ptr(105), CaloriesPerHour: uint16Ptr(659)}, + {ElapsedTime: 8, Level: 4, Distance: uint16Ptr(21), Time500mSplit: uint16Ptr(148), Strokes: uint16Ptr(3), StrokesPerMinute: uint8Ptr(29), WattsPreviousStroke: uint16Ptr(109), CaloriesPerHour: uint16Ptr(674)}, + {ElapsedTime: 2, Level: 4, RemainingDistance: uint16Ptr(0), Time500mAverage: uint16Ptr(0), WattsAverage: uint16Ptr(0), CaloriesTotal: uint16Ptr(0)}, + {ElapsedTime: 5, Level: 4, Distance: uint16Ptr(8), Time500mSplit: uint16Ptr(319), Strokes: uint16Ptr(1), StrokesPerMinute: uint8Ptr(11), WattsPreviousStroke: uint16Ptr(106), CaloriesPerHour: uint16Ptr(663)}, + {ElapsedTime: 1810, Level: 4, RemainingDistance: uint16Ptr(6015), Time500mAverage: uint16Ptr(153), WattsAverage: uint16Ptr(109), CaloriesTotal: uint16Ptr(400)}, } if got := len(observer.dataEvents); len(wantedDataEvents) != got { @@ -126,6 +74,37 @@ func TestSerialMonitor_Run(t *testing.T) { } } + if got := mockSerialPort.closed; got != 1 { + t.Errorf("mockSerialPort.closed = %v, wantedDataEvents 1", got) + } +} + +func TestSerialMonitor_RunCallsObserverForStatusChangeEvents(t *testing.T) { + bufferString := bytes.NewBufferString("") + // session one + bufferString.WriteString("A8000040000710428014108067004\r\n") + bufferString.WriteString("A8000060001410243028105065904\r\n") + bufferString.WriteString("A8000080002110228029109067404\r\n") + // reset + bufferString.WriteString("R\r\n") + // session two + bufferString.WriteString("A8000020000010000000000000004\r\n") + bufferString.WriteString("A8000050000810519011106066304\r\n") + // ... skip some + bufferString.WriteString("A8030100601510233000109040004\r\n") + // connection check + bufferString.WriteString("W\r\n") + + mockSerialPort, port := NewMockSerialPort(bufferString) + serialMonitor := SerialMonitor{ + portName: "/dev/mocked/serial/port", + port: &port, + observers: map[events.Observer]struct{}{}, + } + observer := NewMockObserver() + serialMonitor.AddObserver(observer) + serialMonitor.Run() + wantedStatusChangeEvents := []events.StatusChangeEvent{ {StatusChange: events.Started}, {StatusChange: events.Reset}, @@ -142,7 +121,6 @@ func TestSerialMonitor_Run(t *testing.T) { t.Errorf("statusChangeEvents() = %+v, wantedDataEvents %+v", got, wantedStatusChangeEvents[i]) } } - if got := mockSerialPort.closed; got != 1 { t.Errorf("mockSerialPort.closed = %v, wantedDataEvents 1", got) } @@ -158,6 +136,23 @@ func TestSerialMonitor_NewSerialMonitor(t *testing.T) { } } +func NewMockObserver() *MockObserver { + observer := &MockObserver{ + dataEvents: make([]events.DataEvent, 0), + statusChangeEvents: make([]events.StatusChangeEvent, 0), + } + return observer +} + +func NewMockSerialPort(bufferString *bytes.Buffer) (*MockSerialPort, serial.Port) { + mockSerialPort := &MockSerialPort{ + readBuffer: *bufferString, + writeBuffer: bytes.Buffer{}, + } + var port serial.Port = mockSerialPort + return mockSerialPort, port +} + func (m *MockObserver) OnData(event events.DataEvent) { m.dataEvents = append(m.dataEvents, event) }