From b822f2becc00eeca49ca48faa5af98b18c845fff Mon Sep 17 00:00:00 2001 From: Alexey Efremov <amefremov@edu.hse.ru> Date: Fri, 26 Jan 2024 13:02:46 +0300 Subject: [PATCH 1/9] unstable reconnect --- cmd/main.go | 18 +++++++---- pkg/midi/device_health.go | 21 ------------- pkg/midi/device_manager.go | 31 ++----------------- pkg/midi/in.go | 13 +++++--- pkg/midi/midi_device.go | 61 +++++++++++++++++++++++++++++++------- pkg/midi/out.go | 5 ++++ 6 files changed, 79 insertions(+), 70 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 9ebf275..b5a5953 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -2,17 +2,18 @@ package main import ( "fmt" + "log" + "midi_manipulator/pkg/backlight" + "midi_manipulator/pkg/config" + midiHermophrodite "midi_manipulator/pkg/midi" + "midi_manipulator/pkg/model" + "git.miem.hse.ru/hubman/hubman-lib" "git.miem.hse.ru/hubman/hubman-lib/core" "git.miem.hse.ru/hubman/hubman-lib/executor" "gitlab.com/gomidi/midi/v2" _ "gitlab.com/gomidi/midi/v2/drivers/rtmididrv" "go.uber.org/zap" - "log" - "midi_manipulator/pkg/backlight" - "midi_manipulator/pkg/config" - midiHermophrodite "midi_manipulator/pkg/midi" - "midi_manipulator/pkg/model" ) func main() { @@ -51,7 +52,7 @@ func setupApp(systemConfig *core.SystemConfig, userConfig *config.UserConfig) { } deviceManager.UpdateDevices(userConfig.MidiDevices) - go midiHermophrodite.CheckDevicesHealth(deviceManager) + // go midiHermophrodite.CheckDevicesHealth(deviceManager) signals := deviceManager.GetSignals() app := core.NewContainer(agentConf.System.Logging) @@ -123,6 +124,11 @@ func setupApp(systemConfig *core.SystemConfig, userConfig *config.UserConfig) { hubman.WithOnConfigRefresh(func(configuration core.AgentConfiguration) { update, _ := configuration.User.(*config.UserConfig) deviceManager.UpdateDevices(update.MidiDevices) + // go func () { + // for err != nil { + // unreachableDevs, err = deviceManager.UpdateDevices(unreachableDevs) + // } + // } }), ), ) diff --git a/pkg/midi/device_health.go b/pkg/midi/device_health.go index 0b87138..d4d0eef 100644 --- a/pkg/midi/device_health.go +++ b/pkg/midi/device_health.go @@ -1,34 +1,13 @@ package midi import ( - "gitlab.com/gomidi/midi/v2" "gitlab.com/gomidi/midi/v2/drivers" - "go.uber.org/zap" - "os" "strings" "time" ) var healthCheckDelay = 400 * time.Millisecond -func CheckDevicesHealth(manager *DeviceManager) { - for { - manager.dMutex.Lock() - for _, deviceName := range manager.deviceNames { - if !HasDeviceWithName(deviceName, midi.GetInPorts()) { - manager.logger.Error("InPort is unreachable for device", zap.String("alias", deviceName)) - os.Exit(1) - } - if !HasDeviceWithName(deviceName, midi.GetOutPorts()) { - manager.logger.Error("OutPort is unreachable for device", zap.String("alias", deviceName)) - os.Exit(1) - } - } - manager.dMutex.Unlock() - time.Sleep(healthCheckDelay) - } -} - func HasDeviceWithName[T drivers.Port](deviceName string, deviceList []T) bool { for _, portName := range deviceList { validPortName := GetValidPortName(portName.String()) diff --git a/pkg/midi/device_manager.go b/pkg/midi/device_manager.go index 928e353..86a3bb2 100644 --- a/pkg/midi/device_manager.go +++ b/pkg/midi/device_manager.go @@ -21,9 +21,6 @@ type DeviceManager struct { activeNamespace string nmMutex sync.RWMutex - - deviceNames []string - dMutex sync.Mutex } func (dm *DeviceManager) SetBacklightConfig(cfg *backlight.DecodedDeviceBacklightConfig) { @@ -43,28 +40,12 @@ func (dm *DeviceManager) addDevice(device *MidiDevice) { dm.devices[device.GetAlias()] = device } -func (dm *DeviceManager) addDeviceName(deviceName string) { - dm.dMutex.Lock() - defer dm.dMutex.Unlock() - dm.deviceNames = append(dm.deviceNames, deviceName) -} - func (dm *DeviceManager) removeDevice(alias string) { dm.mutex.Lock() defer dm.mutex.Unlock() delete(dm.devices, alias) } -func (dm *DeviceManager) removeDeviceName(alias string) { - dm.dMutex.Lock() - defer dm.dMutex.Unlock() - for i, deviceName := range dm.deviceNames { - if deviceName == alias { - dm.deviceNames = append(dm.deviceNames[:i], dm.deviceNames[i+1:]...) - } - } -} - func (dm *DeviceManager) Close() { for _, device := range dm.devices { err := dm.RemoveDevice(device.GetAlias()) @@ -105,8 +86,7 @@ func (dm *DeviceManager) AddDevice(device *MidiDevice) error { } dm.addDevice(device) - dm.addDeviceName(device.GetAlias()) - err := device.RunDevice(dm.signals, dm.backlightConfig) + err := device.RunDevice(dm.backlightConfig) if err != nil { return err @@ -130,7 +110,6 @@ func (dm *DeviceManager) RemoveDevice(alias string) error { } dm.removeDevice(alias) - dm.removeDeviceName(alias) return nil } @@ -146,13 +125,9 @@ func (dm *DeviceManager) UpdateDevices(midiConfig []config.DeviceConfig) { for _, deviceConfig := range midiConfigMap { device, found := dm.getDevice(deviceConfig.DeviceName) if !found { - newDevice, err := NewDevice(deviceConfig) - - if err != nil { - panic(err) - } + newDevice, _ := NewDevice(deviceConfig, dm.signals) - err = dm.AddDevice(newDevice) + err := dm.AddDevice(newDevice) if err != nil { panic(err) diff --git a/pkg/midi/in.go b/pkg/midi/in.go index d05a148..a9d3970 100644 --- a/pkg/midi/in.go +++ b/pkg/midi/in.go @@ -8,10 +8,10 @@ import ( "time" ) -func (md *MidiDevice) sendSignal(signals chan<- core.Signal, signal core.Signal) { +func (md *MidiDevice) sendSignal(signal core.Signal) { if signal != nil { log.Println(signal.Code(), signal) - signals <- signal + md.signals <- signal } } @@ -110,7 +110,12 @@ func (md *MidiDevice) messageToSignal() []core.Signal { return signalSequence } -func (md *MidiDevice) listen(signals chan<- core.Signal) { +func (md *MidiDevice) listen() { + md.mutex.Lock() + if !md.connected { + md.mutex.Unlock() + return + } stop, err := midi.ListenTo(*md.ports.in, md.getMidiMessage, midi.UseSysEx()) if err != nil { @@ -126,7 +131,7 @@ func (md *MidiDevice) listen(signals chan<- core.Signal) { return default: for _, signal := range signalSequence { - md.sendSignal(signals, signal) + md.sendSignal(signal) } } } diff --git a/pkg/midi/midi_device.go b/pkg/midi/midi_device.go index c1acc9d..7824c62 100644 --- a/pkg/midi/midi_device.go +++ b/pkg/midi/midi_device.go @@ -22,7 +22,9 @@ type MidiDevice struct { mutex sync.Mutex stop chan bool namespace string + connected bool controls map[byte]*Control + signals chan<- core.Signal } type MidiPorts struct { @@ -55,19 +57,53 @@ func (md *MidiDevice) StopDevice() error { return nil } -func (md *MidiDevice) RunDevice(signals chan<- core.Signal, backlightConfig *backlight.DecodedDeviceBacklightConfig) error { +func (md *MidiDevice) RunDevice(backlightConfig *backlight.DecodedDeviceBacklightConfig) error { go md.startupIllumination(backlightConfig) - go md.listen(signals) + go md.listen() + go md.reconnect(backlightConfig) return nil } +func (md *MidiDevice) reconnect(backlightConfig *backlight.DecodedDeviceBacklightConfig) error { + log.Println("RECONNECT") + for { + md.mutex.Lock() + + if md.connected && (!HasDeviceWithName(md.name, midi.GetInPorts()) || !HasDeviceWithName(md.name, midi.GetOutPorts())) { + // manager.logger.Error("In/Out ports are unreachable for device", zap.String("alias", deviceName)) + md.connected = false + md.stop <- true + } + + if !md.connected { + // manager.logger.Error("In/Out ports are unreachable for device", zap.String("alias", deviceName)) + err := md.connectDevice() + + if err == nil { + time.Sleep(4000 * time.Millisecond) + md.startupIllumination(backlightConfig) + go md.listen() + log.Println(md.name, "was reconnected") + } + } + + md.mutex.Unlock() + time.Sleep(2000 * time.Millisecond) + } +} + + func (md *MidiDevice) connectDevice() error { - var err error + var err error = nil in_err := md.connectInPort() out_err := md.connectOutPort() if in_err != nil || out_err != nil { err = fmt.Errorf("connection of device \"{%s}\" failed", md.name) + } + if in_err == nil && out_err == nil { + md.connected = true + err = nil } return err } @@ -106,7 +142,7 @@ func (md *MidiDevice) connectInPort() error { return nil } -func (md *MidiDevice) applyConfiguration(deviceConfig config.DeviceConfig) { +func (md *MidiDevice) applyConfiguration(deviceConfig config.DeviceConfig, signals chan<- core.Signal) { md.name = deviceConfig.DeviceName md.active = deviceConfig.Active md.holdDelta = time.Duration(float64(time.Millisecond) * deviceConfig.HoldDelta) @@ -114,6 +150,8 @@ func (md *MidiDevice) applyConfiguration(deviceConfig config.DeviceConfig) { md.stop = make(chan bool, 1) md.namespace = deviceConfig.Namespace md.controls = make(map[byte]*Control) + md.connected = false + md.signals = signals md.applyControls(deviceConfig.Controls) } @@ -132,18 +170,19 @@ func (md *MidiDevice) applyControls(controls config.Controls) { } func (md *MidiDevice) updateConfiguration(config config.DeviceConfig) { + md.mutex.Lock() + defer md.mutex.Unlock() md.active = config.Active md.holdDelta = time.Duration(float64(time.Millisecond) * config.HoldDelta) } -func NewDevice(deviceConfig config.DeviceConfig) (*MidiDevice, error) { +func NewDevice(deviceConfig config.DeviceConfig, signals chan<- core.Signal) (*MidiDevice, error) { midiDevice := MidiDevice{} - midiDevice.applyConfiguration(deviceConfig) + midiDevice.applyConfiguration(deviceConfig, signals) err := midiDevice.connectDevice() - if err != nil { - fmt.Println(err) - return nil, err - } - return &midiDevice, nil + + log.Println(err) + + return &midiDevice, err } diff --git a/pkg/midi/out.go b/pkg/midi/out.go index 726b258..54ae79b 100644 --- a/pkg/midi/out.go +++ b/pkg/midi/out.go @@ -47,6 +47,11 @@ func (md *MidiDevice) singleReversedBlink(cmd model.SingleReversedBlinkCommand, } func (md *MidiDevice) startupIllumination(config *backlight.DecodedDeviceBacklightConfig) { + md.mutex.Lock() + if !md.connected { + md.mutex.Unlock() + return + } backlightTimeOffset := time.Duration(config.DeviceBacklightTimeOffset[md.name]) for _, keyRange := range config.DeviceKeyRangeMap[md.name] { for i := keyRange[0]; i <= keyRange[1]; i++ { -- GitLab From 63d0321d095b5d0d49130a51ffc1454a6ee873c6 Mon Sep 17 00:00:00 2001 From: Alexey Efremov <amefremov@edu.hse.ru> Date: Fri, 26 Jan 2024 16:50:44 +0300 Subject: [PATCH 2/9] stable reconnect v1 --- pkg/midi/device_manager.go | 14 +++--------- pkg/midi/in.go | 2 ++ pkg/midi/midi_device.go | 47 +++++++++++++++++--------------------- pkg/midi/out.go | 1 + 4 files changed, 27 insertions(+), 37 deletions(-) diff --git a/pkg/midi/device_manager.go b/pkg/midi/device_manager.go index 86a3bb2..4771d5a 100644 --- a/pkg/midi/device_manager.go +++ b/pkg/midi/device_manager.go @@ -86,11 +86,7 @@ func (dm *DeviceManager) AddDevice(device *MidiDevice) error { } dm.addDevice(device) - err := device.RunDevice(dm.backlightConfig) - - if err != nil { - return err - } + device.RunDevice(dm.backlightConfig) return nil } @@ -125,13 +121,9 @@ func (dm *DeviceManager) UpdateDevices(midiConfig []config.DeviceConfig) { for _, deviceConfig := range midiConfigMap { device, found := dm.getDevice(deviceConfig.DeviceName) if !found { - newDevice, _ := NewDevice(deviceConfig, dm.signals) + newDevice, _ := NewDevice(deviceConfig, dm.signals, dm.logger) - err := dm.AddDevice(newDevice) - - if err != nil { - panic(err) - } + dm.AddDevice(newDevice) } else { device.updateConfiguration(deviceConfig) } diff --git a/pkg/midi/in.go b/pkg/midi/in.go index a9d3970..5964be7 100644 --- a/pkg/midi/in.go +++ b/pkg/midi/in.go @@ -116,6 +116,8 @@ func (md *MidiDevice) listen() { md.mutex.Unlock() return } + md.mutex.Unlock() + stop, err := midi.ListenTo(*md.ports.in, md.getMidiMessage, midi.UseSysEx()) if err != nil { diff --git a/pkg/midi/midi_device.go b/pkg/midi/midi_device.go index 7824c62..e5b75be 100644 --- a/pkg/midi/midi_device.go +++ b/pkg/midi/midi_device.go @@ -5,12 +5,12 @@ import ( "git.miem.hse.ru/hubman/hubman-lib/core" "gitlab.com/gomidi/midi/v2" "gitlab.com/gomidi/midi/v2/drivers" - "log" "midi_manipulator/pkg/backlight" "midi_manipulator/pkg/config" "midi_manipulator/pkg/model" "sync" "time" + "go.uber.org/zap" ) type MidiDevice struct { @@ -22,9 +22,10 @@ type MidiDevice struct { mutex sync.Mutex stop chan bool namespace string - connected bool + connected bool controls map[byte]*Control - signals chan<- core.Signal + signals chan<- core.Signal + logger *zap.Logger } type MidiPorts struct { @@ -58,32 +59,32 @@ func (md *MidiDevice) StopDevice() error { } func (md *MidiDevice) RunDevice(backlightConfig *backlight.DecodedDeviceBacklightConfig) error { - go md.startupIllumination(backlightConfig) + md.startupIllumination(backlightConfig) go md.listen() go md.reconnect(backlightConfig) return nil } func (md *MidiDevice) reconnect(backlightConfig *backlight.DecodedDeviceBacklightConfig) error { - log.Println("RECONNECT") for { md.mutex.Lock() - - if md.connected && (!HasDeviceWithName(md.name, midi.GetInPorts()) || !HasDeviceWithName(md.name, midi.GetOutPorts())) { - // manager.logger.Error("In/Out ports are unreachable for device", zap.String("alias", deviceName)) + + if md.connected && (!HasDeviceWithName(md.name, midi.GetInPorts()) || !HasDeviceWithName(md.name, midi.GetOutPorts())) { + md.logger.Warn("Device disconnected", zap.String("alias", md.name)) md.connected = false md.stop <- true } if !md.connected { - // manager.logger.Error("In/Out ports are unreachable for device", zap.String("alias", deviceName)) err := md.connectDevice() if err == nil { + md.mutex.Unlock() time.Sleep(4000 * time.Millisecond) md.startupIllumination(backlightConfig) go md.listen() - log.Println(md.name, "was reconnected") + md.logger.Warn("Device reconnected", zap.String("alias", md.name)) + continue } } @@ -92,7 +93,6 @@ func (md *MidiDevice) reconnect(backlightConfig *backlight.DecodedDeviceBackligh } } - func (md *MidiDevice) connectDevice() error { var err error = nil in_err := md.connectInPort() @@ -100,7 +100,7 @@ func (md *MidiDevice) connectDevice() error { if in_err != nil || out_err != nil { err = fmt.Errorf("connection of device \"{%s}\" failed", md.name) - } + } if in_err == nil && out_err == nil { md.connected = true err = nil @@ -111,13 +111,11 @@ func (md *MidiDevice) connectDevice() error { func (md *MidiDevice) connectOutPort() error { port, err := midi.FindOutPort(md.name) if err != nil { - log.Printf("Output port named {%s} was not found\n", md.name) return err } port, err = midi.OutPort(port.Number()) if err != nil { - log.Printf("Output port #{%d} was not found\n", port.Number()) return err } @@ -128,13 +126,11 @@ func (md *MidiDevice) connectOutPort() error { func (md *MidiDevice) connectInPort() error { port, err := midi.FindInPort(md.name) if err != nil { - log.Printf("Input port named {%s} was not found", md.name) return err } port, err = midi.InPort(port.Number()) if err != nil { - log.Printf("Input port #{%d} was not found\n", port.Number()) return err } @@ -142,7 +138,7 @@ func (md *MidiDevice) connectInPort() error { return nil } -func (md *MidiDevice) applyConfiguration(deviceConfig config.DeviceConfig, signals chan<- core.Signal) { +func (md *MidiDevice) applyConfiguration(deviceConfig config.DeviceConfig, signals chan<- core.Signal, logger *zap.Logger) { md.name = deviceConfig.DeviceName md.active = deviceConfig.Active md.holdDelta = time.Duration(float64(time.Millisecond) * deviceConfig.HoldDelta) @@ -152,17 +148,18 @@ func (md *MidiDevice) applyConfiguration(deviceConfig config.DeviceConfig, signa md.controls = make(map[byte]*Control) md.connected = false md.signals = signals + md.logger = logger md.applyControls(deviceConfig.Controls) } func (md *MidiDevice) applyControls(controls config.Controls) { for _, controlKey := range controls.Keys { control := Control{ - Key: controlKey, - Rotate: controls.Rotate, - ValueRange: controls.ValueRange, - InitialValue: controls.InitialValue, - DecrementTrigger: controls.Triggers.Decrement, + Key: controlKey, + Rotate: controls.Rotate, + ValueRange: controls.ValueRange, + InitialValue: controls.InitialValue, + DecrementTrigger: controls.Triggers.Decrement, IncrementTrigger: controls.Triggers.Increment, } md.controls[controlKey] = &control @@ -176,13 +173,11 @@ func (md *MidiDevice) updateConfiguration(config config.DeviceConfig) { md.holdDelta = time.Duration(float64(time.Millisecond) * config.HoldDelta) } -func NewDevice(deviceConfig config.DeviceConfig, signals chan<- core.Signal) (*MidiDevice, error) { +func NewDevice(deviceConfig config.DeviceConfig, signals chan<- core.Signal, logger *zap.Logger) (*MidiDevice, error) { midiDevice := MidiDevice{} - midiDevice.applyConfiguration(deviceConfig, signals) + midiDevice.applyConfiguration(deviceConfig, signals, logger) err := midiDevice.connectDevice() - log.Println(err) - return &midiDevice, err } diff --git a/pkg/midi/out.go b/pkg/midi/out.go index 54ae79b..d657417 100644 --- a/pkg/midi/out.go +++ b/pkg/midi/out.go @@ -52,6 +52,7 @@ func (md *MidiDevice) startupIllumination(config *backlight.DecodedDeviceBacklig md.mutex.Unlock() return } + md.mutex.Unlock() backlightTimeOffset := time.Duration(config.DeviceBacklightTimeOffset[md.name]) for _, keyRange := range config.DeviceKeyRangeMap[md.name] { for i := keyRange[0]; i <= keyRange[1]; i++ { -- GitLab From e545bb7204e5d4296c1fd6f3bffea01864cfe40e Mon Sep 17 00:00:00 2001 From: Alexey Efremov <amefremov@edu.hse.ru> Date: Fri, 26 Jan 2024 19:09:37 +0300 Subject: [PATCH 3/9] fixed post merge conflicts, deadlocks --- cmd/main.go | 8 -------- pkg/midi/device_health.go | 4 ---- pkg/midi/device_manager.go | 2 +- pkg/midi/midi_device.go | 3 +-- 4 files changed, 2 insertions(+), 15 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index f1925bf..f69199b 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -51,8 +51,6 @@ func setupApp(systemConfig *core.SystemConfig, userConfig *config.UserConfig) { ParseUserConfig: func(data []byte) (core.Configuration, error) { return config.ParseConfigFromBytes(data) }, } - deviceManager.UpdateDevices(userConfig.MidiDevices) - signals := deviceManager.GetSignals() app := core.NewContainer(agentConf.System.Logging) app.RegisterPlugin( @@ -124,17 +122,11 @@ func setupApp(systemConfig *core.SystemConfig, userConfig *config.UserConfig) { hubman.WithOnConfigRefresh(func(configuration core.AgentConfiguration) { update, _ := configuration.User.(*config.UserConfig) deviceManager.UpdateDevices(update.MidiDevices) - // go func () { - // for err != nil { - // unreachableDevs, err = deviceManager.UpdateDevices(unreachableDevs) - // } - // } }), ), ) deviceManager.UpdateDevices(userConfig.MidiDevices) - go midiHermophrodite.CheckDevicesHealth(deviceManager) <-app.WaitShutdown() } diff --git a/pkg/midi/device_health.go b/pkg/midi/device_health.go index 849b287..54fd634 100644 --- a/pkg/midi/device_health.go +++ b/pkg/midi/device_health.go @@ -4,10 +4,6 @@ import ( "gitlab.com/gomidi/midi/v2/drivers" "strings" "time" - - "gitlab.com/gomidi/midi/v2" - "gitlab.com/gomidi/midi/v2/drivers" - "go.uber.org/zap" ) var healthCheckDelay = 400 * time.Millisecond diff --git a/pkg/midi/device_manager.go b/pkg/midi/device_manager.go index d8e2343..fcf830e 100644 --- a/pkg/midi/device_manager.go +++ b/pkg/midi/device_manager.go @@ -122,7 +122,7 @@ func (dm *DeviceManager) UpdateDevices(midiConfig []config.DeviceConfig) { dm.AddDevice(newDevice) } else { - device.updateConfiguration(deviceConfig, dm.signals) + device.updateConfiguration(deviceConfig) } } diff --git a/pkg/midi/midi_device.go b/pkg/midi/midi_device.go index dcf13aa..607717b 100644 --- a/pkg/midi/midi_device.go +++ b/pkg/midi/midi_device.go @@ -174,10 +174,9 @@ func (md *MidiDevice) updateConfiguration(config config.DeviceConfig) { if md.namespace != config.Namespace { var oldNamespace = md.namespace md.namespace = config.Namespace - md.sendNamespaceChangedSignal(signals, oldNamespace, config.Namespace) + md.sendNamespaceChangedSignal(md.signals, oldNamespace, config.Namespace) } md.applyControls(config.Controls) - md.mutex.Unlock() } func NewDevice(deviceConfig config.DeviceConfig, signals chan<- core.Signal, logger *zap.Logger) (*MidiDevice, error) { -- GitLab From c606fc566dad52e51d29938572419fe0bc41b8ba Mon Sep 17 00:00:00 2001 From: Alexey Efremov <amefremov@edu.hse.ru> Date: Fri, 26 Jan 2024 20:54:18 +0300 Subject: [PATCH 4/9] minor fixes --- cmd/main.go | 1 - pkg/midi/in.go | 1 - pkg/midi/midi_device.go | 9 +++++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 8dcc56e..cac85d7 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -13,7 +13,6 @@ import ( "git.miem.hse.ru/hubman/hubman-lib/executor" "gitlab.com/gomidi/midi/v2" _ "gitlab.com/gomidi/midi/v2/drivers/rtmididrv" - "go.uber.org/zap" ) func main() { diff --git a/pkg/midi/in.go b/pkg/midi/in.go index 5964be7..403cfad 100644 --- a/pkg/midi/in.go +++ b/pkg/midi/in.go @@ -10,7 +10,6 @@ import ( func (md *MidiDevice) sendSignal(signal core.Signal) { if signal != nil { - log.Println(signal.Code(), signal) md.signals <- signal } } diff --git a/pkg/midi/midi_device.go b/pkg/midi/midi_device.go index 607717b..7022587 100644 --- a/pkg/midi/midi_device.go +++ b/pkg/midi/midi_device.go @@ -2,14 +2,15 @@ package midi import ( "fmt" - "git.miem.hse.ru/hubman/hubman-lib/core" - "gitlab.com/gomidi/midi/v2" - "gitlab.com/gomidi/midi/v2/drivers" "midi_manipulator/pkg/backlight" "midi_manipulator/pkg/config" "midi_manipulator/pkg/model" "sync" "time" + + "git.miem.hse.ru/hubman/hubman-lib/core" + "gitlab.com/gomidi/midi/v2" + "gitlab.com/gomidi/midi/v2/drivers" "go.uber.org/zap" ) @@ -79,11 +80,11 @@ func (md *MidiDevice) reconnect(backlightConfig *backlight.DecodedDeviceBackligh err := md.connectDevice() if err == nil { + md.logger.Warn("Device reconnected", zap.String("alias", md.name)) md.mutex.Unlock() time.Sleep(4000 * time.Millisecond) md.startupIllumination(backlightConfig) go md.listen() - md.logger.Warn("Device reconnected", zap.String("alias", md.name)) continue } } -- GitLab From 3465a2a1ae8620a63825098c99f13783ec9cea8c Mon Sep 17 00:00:00 2001 From: Alexey Efremov <amefremov@edu.hse.ru> Date: Sat, 27 Jan 2024 00:14:00 +0300 Subject: [PATCH 5/9] added debug logs for signals --- pkg/midi/in.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/midi/in.go b/pkg/midi/in.go index 403cfad..ecb3200 100644 --- a/pkg/midi/in.go +++ b/pkg/midi/in.go @@ -6,10 +6,12 @@ import ( "log" "midi_manipulator/pkg/model" "time" + "go.uber.org/zap" ) func (md *MidiDevice) sendSignal(signal core.Signal) { if signal != nil { + md.logger.Debug("", zap.String("signal", string(signal.Code())), zap.Any("payload", signal)) md.signals <- signal } } -- GitLab From 0bdd8561cff97726db43ceb72b7bf522f4398a40 Mon Sep 17 00:00:00 2001 From: Alexey Efremov <amefremov@edu.hse.ru> Date: Sat, 27 Jan 2024 11:01:20 +0300 Subject: [PATCH 6/9] added log msg --- pkg/midi/in.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/midi/in.go b/pkg/midi/in.go index ecb3200..12b3e12 100644 --- a/pkg/midi/in.go +++ b/pkg/midi/in.go @@ -11,7 +11,7 @@ import ( func (md *MidiDevice) sendSignal(signal core.Signal) { if signal != nil { - md.logger.Debug("", zap.String("signal", string(signal.Code())), zap.Any("payload", signal)) + md.logger.Debug("Received signal from MIDI device", zap.String("signal", string(signal.Code())), zap.Any("payload", signal)) md.signals <- signal } } -- GitLab From 413a3b5e0ea690dd3762f61f877ad4c506a981be Mon Sep 17 00:00:00 2001 From: Alexey Efremov <amefremov@edu.hse.ru> Date: Sat, 27 Jan 2024 11:59:06 +0300 Subject: [PATCH 7/9] added startup_delay field to config, fixed type for hold_delta --- configs/backlight_config.yaml | 8 +++---- configs/user_config.yaml | 1 + pkg/config/config.go | 17 ++++++++++----- pkg/midi/midi_device.go | 41 ++++++++++++++++++++--------------- 4 files changed, 40 insertions(+), 27 deletions(-) diff --git a/configs/backlight_config.yaml b/configs/backlight_config.yaml index d7d813e..0fcf641 100644 --- a/configs/backlight_config.yaml +++ b/configs/backlight_config.yaml @@ -170,11 +170,11 @@ device_light_configuration: statuses: on: type: Sysex - fallback_color: red + fallback_color: white bytes: F0 47 7F 43 65 00 04 %key %payload F7 off: type: Sysex - fallback_color: light_red + fallback_color: light_white bytes: F0 47 7F 43 65 00 04 %key %payload F7 - key_range: - 70 @@ -184,11 +184,11 @@ device_light_configuration: statuses: on: type: Sysex - fallback_color: green + fallback_color: white bytes: F0 47 7F 43 65 00 04 %key %payload F7 off: type: Sysex - fallback_color: light_green + fallback_color: light_white bytes: F0 47 7F 43 65 00 04 %key %payload F7 - key_range: - 86 diff --git a/configs/user_config.yaml b/configs/user_config.yaml index 9464b39..9e4b94f 100644 --- a/configs/user_config.yaml +++ b/configs/user_config.yaml @@ -1,5 +1,6 @@ midi_devices: - device_name: MPD226 + startup_delay: 1000 active: true hold_delta: 1000 namespace: default diff --git a/pkg/config/config.go b/pkg/config/config.go index b02ff51..b18f21e 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -19,11 +19,12 @@ type Controls struct { } type DeviceConfig struct { - DeviceName string `json:"device_name" yaml:"device_name"` - Active bool `json:"active" yaml:"active"` - HoldDelta float64 `json:"hold_delta" yaml:"hold_delta"` - Namespace string `json:"namespace" yaml:"namespace"` - Controls Controls `json:"accumulate_controls" yaml:"accumulate_controls"` + DeviceName string `json:"device_name" yaml:"device_name"` + StartupDelay int `json:"startup_delay" yaml:"startup_delay"` + Active bool `json:"active" yaml:"active"` + HoldDelta int `json:"hold_delta" yaml:"hold_delta"` + Namespace string `json:"namespace" yaml:"namespace"` + Controls Controls `json:"accumulate_controls" yaml:"accumulate_controls"` } type UserConfig struct { @@ -53,6 +54,12 @@ func (conf *UserConfig) Validate() error { " Now {%f} is provided", idx, device.DeviceName, device.HoldDelta) } + if device.StartupDelay < 0 { + return fmt.Errorf("device #{%d} ({%s}): "+ + "valid MIDI startup_delay must be provided in config."+ + " Now {%d} is provided", + idx, device.DeviceName, device.StartupDelay) + } } return nil } diff --git a/pkg/midi/midi_device.go b/pkg/midi/midi_device.go index 7022587..6b45e4e 100644 --- a/pkg/midi/midi_device.go +++ b/pkg/midi/midi_device.go @@ -14,19 +14,22 @@ import ( "go.uber.org/zap" ) +var DEVICE_RECONNECT_INTERVAL = 2000 * time.Millisecond + type MidiDevice struct { - name string - active bool - ports MidiPorts - clickBuffer ClickBuffer - holdDelta time.Duration - mutex sync.Mutex - stop chan bool - namespace string - connected bool - controls map[byte]*Control - signals chan<- core.Signal - logger *zap.Logger + name string + active bool + ports MidiPorts + clickBuffer ClickBuffer + holdDelta time.Duration + startupDelay time.Duration + mutex sync.Mutex + stop chan bool + namespace string + connected bool + controls map[byte]*Control + signals chan<- core.Signal + logger *zap.Logger } type MidiPorts struct { @@ -60,6 +63,7 @@ func (md *MidiDevice) StopDevice() error { } func (md *MidiDevice) RunDevice(backlightConfig *backlight.DecodedDeviceBacklightConfig) error { + time.Sleep(md.startupDelay) md.startupIllumination(backlightConfig) go md.listen() go md.reconnect(backlightConfig) @@ -82,7 +86,7 @@ func (md *MidiDevice) reconnect(backlightConfig *backlight.DecodedDeviceBackligh if err == nil { md.logger.Warn("Device reconnected", zap.String("alias", md.name)) md.mutex.Unlock() - time.Sleep(4000 * time.Millisecond) + time.Sleep(md.startupDelay) md.startupIllumination(backlightConfig) go md.listen() continue @@ -90,7 +94,7 @@ func (md *MidiDevice) reconnect(backlightConfig *backlight.DecodedDeviceBackligh } md.mutex.Unlock() - time.Sleep(2000 * time.Millisecond) + time.Sleep(DEVICE_RECONNECT_INTERVAL) } } @@ -142,7 +146,8 @@ func (md *MidiDevice) connectInPort() error { func (md *MidiDevice) applyConfiguration(deviceConfig config.DeviceConfig, signals chan<- core.Signal, logger *zap.Logger) { md.name = deviceConfig.DeviceName md.active = deviceConfig.Active - md.holdDelta = time.Duration(float64(time.Millisecond) * deviceConfig.HoldDelta) + md.holdDelta = time.Duration(int(time.Millisecond) * deviceConfig.HoldDelta) + md.startupDelay = time.Duration(int(time.Millisecond) * deviceConfig.StartupDelay) md.clickBuffer = make(map[uint8]*KeyContext) md.stop = make(chan bool, 1) md.namespace = deviceConfig.Namespace @@ -171,7 +176,7 @@ func (md *MidiDevice) updateConfiguration(config config.DeviceConfig) { md.mutex.Lock() defer md.mutex.Unlock() md.active = config.Active - md.holdDelta = time.Duration(float64(time.Millisecond) * config.HoldDelta) + md.holdDelta = time.Duration(int(time.Millisecond) * config.HoldDelta) if md.namespace != config.Namespace { var oldNamespace = md.namespace md.namespace = config.Namespace @@ -189,9 +194,9 @@ func NewDevice(deviceConfig config.DeviceConfig, signals chan<- core.Signal, log return &midiDevice, err } -func (md *MidiDevice) sendNamespaceChangedSignal(signals chan<- core.Signal, oldNamespace string, newNamespace string){ +func (md *MidiDevice) sendNamespaceChangedSignal(signals chan<- core.Signal, oldNamespace string, newNamespace string) { signal := model.NamespaceChanged{ - Device: md.name, + Device: md.name, OldNamespace: oldNamespace, NewNamespace: newNamespace, } -- GitLab From 5451bcab8c0ee88e4417997adf9ec9b4a92e4410 Mon Sep 17 00:00:00 2001 From: Alexey Efremov <amefremov@edu.hse.ru> Date: Sat, 27 Jan 2024 12:01:02 +0300 Subject: [PATCH 8/9] fixed config validation errors --- pkg/config/config.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index b18f21e..a397c8f 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -42,8 +42,8 @@ func (conf *UserConfig) Validate() error { if device.DeviceName == "" { return fmt.Errorf("device #{%d} ({%s}): "+ "valid MIDI device_name must be provided in config. "+ - "Now {%f} is provided", - idx, device.DeviceName, device.HoldDelta) + "Now {%s} is provided", + idx, device.DeviceName, device.DeviceName) } if device.Namespace == "" { return fmt.Errorf("device #{%d} ({%s}) has no namespace specified", idx, device.DeviceName) @@ -51,7 +51,7 @@ func (conf *UserConfig) Validate() error { if device.HoldDelta < 0 { return fmt.Errorf("device #{%d} ({%s}): "+ "valid MIDI hold_delta must be provided in config."+ - " Now {%f} is provided", + " Now {%d} is provided", idx, device.DeviceName, device.HoldDelta) } if device.StartupDelay < 0 { -- GitLab From 6684ef4f654ceb81c5a74f063d1a1155e34a4994 Mon Sep 17 00:00:00 2001 From: Alexey Efremov <amefremov@edu.hse.ru> Date: Sat, 27 Jan 2024 12:47:21 +0300 Subject: [PATCH 9/9] added configurable reconnect_interval, resolved race conditions for time delays --- configs/user_config.yaml | 1 + pkg/config/config.go | 19 +++++++++++++------ pkg/midi/midi_device.go | 38 +++++++++++++++++++++----------------- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/configs/user_config.yaml b/configs/user_config.yaml index 9e4b94f..682ab47 100644 --- a/configs/user_config.yaml +++ b/configs/user_config.yaml @@ -1,6 +1,7 @@ midi_devices: - device_name: MPD226 startup_delay: 1000 + reconnect_interval: 2000 active: true hold_delta: 1000 namespace: default diff --git a/pkg/config/config.go b/pkg/config/config.go index a397c8f..47de12a 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -19,12 +19,13 @@ type Controls struct { } type DeviceConfig struct { - DeviceName string `json:"device_name" yaml:"device_name"` - StartupDelay int `json:"startup_delay" yaml:"startup_delay"` - Active bool `json:"active" yaml:"active"` - HoldDelta int `json:"hold_delta" yaml:"hold_delta"` - Namespace string `json:"namespace" yaml:"namespace"` - Controls Controls `json:"accumulate_controls" yaml:"accumulate_controls"` + DeviceName string `json:"device_name" yaml:"device_name"` + StartupDelay int `json:"startup_delay" yaml:"startup_delay"` + ReconnectInterval int `json:"reconnect_interval" yaml:"reconnect_interval"` + Active bool `json:"active" yaml:"active"` + HoldDelta int `json:"hold_delta" yaml:"hold_delta"` + Namespace string `json:"namespace" yaml:"namespace"` + Controls Controls `json:"accumulate_controls" yaml:"accumulate_controls"` } type UserConfig struct { @@ -60,6 +61,12 @@ func (conf *UserConfig) Validate() error { " Now {%d} is provided", idx, device.DeviceName, device.StartupDelay) } + if device.ReconnectInterval < 2 { + return fmt.Errorf("device #{%d} ({%s}): "+ + "valid MIDI reconnect_interval must be provided in config (> 2000ms)."+ + " Now {%d} is provided", + idx, device.DeviceName, device.ReconnectInterval) + } } return nil } diff --git a/pkg/midi/midi_device.go b/pkg/midi/midi_device.go index 6b45e4e..541aeff 100644 --- a/pkg/midi/midi_device.go +++ b/pkg/midi/midi_device.go @@ -14,22 +14,21 @@ import ( "go.uber.org/zap" ) -var DEVICE_RECONNECT_INTERVAL = 2000 * time.Millisecond - type MidiDevice struct { - name string - active bool - ports MidiPorts - clickBuffer ClickBuffer - holdDelta time.Duration - startupDelay time.Duration - mutex sync.Mutex - stop chan bool - namespace string - connected bool - controls map[byte]*Control - signals chan<- core.Signal - logger *zap.Logger + name string + active bool + ports MidiPorts + clickBuffer ClickBuffer + holdDelta time.Duration + startupDelay time.Duration + reconnectInterval time.Duration + mutex sync.Mutex + stop chan bool + namespace string + connected bool + controls map[byte]*Control + signals chan<- core.Signal + logger *zap.Logger } type MidiPorts struct { @@ -85,16 +84,19 @@ func (md *MidiDevice) reconnect(backlightConfig *backlight.DecodedDeviceBackligh if err == nil { md.logger.Warn("Device reconnected", zap.String("alias", md.name)) + var startupDelay = md.startupDelay md.mutex.Unlock() - time.Sleep(md.startupDelay) + + time.Sleep(startupDelay) md.startupIllumination(backlightConfig) go md.listen() continue } } + var reconnectInterval = md.reconnectInterval md.mutex.Unlock() - time.Sleep(DEVICE_RECONNECT_INTERVAL) + time.Sleep(reconnectInterval) } } @@ -148,6 +150,7 @@ func (md *MidiDevice) applyConfiguration(deviceConfig config.DeviceConfig, signa md.active = deviceConfig.Active md.holdDelta = time.Duration(int(time.Millisecond) * deviceConfig.HoldDelta) md.startupDelay = time.Duration(int(time.Millisecond) * deviceConfig.StartupDelay) + md.reconnectInterval = time.Duration(int(time.Millisecond) * deviceConfig.ReconnectInterval) md.clickBuffer = make(map[uint8]*KeyContext) md.stop = make(chan bool, 1) md.namespace = deviceConfig.Namespace @@ -177,6 +180,7 @@ func (md *MidiDevice) updateConfiguration(config config.DeviceConfig) { defer md.mutex.Unlock() md.active = config.Active md.holdDelta = time.Duration(int(time.Millisecond) * config.HoldDelta) + md.reconnectInterval = time.Duration(int(time.Millisecond) * config.ReconnectInterval) if md.namespace != config.Namespace { var oldNamespace = md.namespace md.namespace = config.Namespace -- GitLab