Make connections auto-retry on failure for connections to MQTT/MIDI devices. This should improve stability with physical devices when ran as a service.
This commit is contained in:
parent
24ecc9870e
commit
1eb7440496
10
README.md
10
README.md
@ -110,7 +110,7 @@ On MacOS, there is an IAC Driver that can be enabled in Audio MIDI Setup.
|
|||||||
midi_routers:
|
midi_routers:
|
||||||
- name: service_notifications
|
- name: service_notifications
|
||||||
device: IAC Driver Bus 1
|
device: IAC Driver Bus 1
|
||||||
log_level: 1
|
log_level: 2
|
||||||
```
|
```
|
||||||
|
|
||||||
### Example note trigger configuration
|
### Example note trigger configuration
|
||||||
@ -120,7 +120,7 @@ midi_routers:
|
|||||||
midi_routers:
|
midi_routers:
|
||||||
- name: service_notifications
|
- name: service_notifications
|
||||||
device: IAC Driver Bus 1
|
device: IAC Driver Bus 1
|
||||||
log_level: 1
|
log_level: 2
|
||||||
note_triggers:
|
note_triggers:
|
||||||
- channel: 0
|
- channel: 0
|
||||||
note: 0
|
note: 0
|
||||||
@ -136,7 +136,7 @@ midi_routers:
|
|||||||
midi_routers:
|
midi_routers:
|
||||||
- name: service_notifications
|
- name: service_notifications
|
||||||
device: IAC Driver Bus 1
|
device: IAC Driver Bus 1
|
||||||
log_level: 1
|
log_level: 2
|
||||||
request_triggers:
|
request_triggers:
|
||||||
- channel: 0
|
- channel: 0
|
||||||
note: 0
|
note: 0
|
||||||
@ -152,7 +152,7 @@ midi_routers:
|
|||||||
midi_routers:
|
midi_routers:
|
||||||
- name: service_notifications
|
- name: service_notifications
|
||||||
device: IAC Driver Bus 1
|
device: IAC Driver Bus 1
|
||||||
log_level: 2
|
log_level: 3
|
||||||
note_triggers:
|
note_triggers:
|
||||||
- channel: 0
|
- channel: 0
|
||||||
note: 0
|
note: 0
|
||||||
@ -190,7 +190,7 @@ midi_routers:
|
|||||||
user: mqtt
|
user: mqtt
|
||||||
password: password
|
password: password
|
||||||
topic: midi/behringer_wing
|
topic: midi/behringer_wing
|
||||||
log_level: 3
|
log_level: 4
|
||||||
note_triggers:
|
note_triggers:
|
||||||
- channel: 0
|
- channel: 0
|
||||||
note: 1
|
note: 1
|
||||||
|
2
main.go
2
main.go
@ -15,7 +15,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
serviceName = "midi-request-trigger"
|
serviceName = "midi-request-trigger"
|
||||||
serviceDescription = "Takes trigger MIDI messages by HTTP or MQTT requests and trigger HTTP or MQTT requests by MIDI messages"
|
serviceDescription = "Takes trigger MIDI messages by HTTP or MQTT requests and trigger HTTP or MQTT requests by MIDI messages"
|
||||||
serviceVersion = "0.2.1"
|
serviceVersion = "0.2.2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// App is the global application structure for communicating between servers and storing information.
|
// App is the global application structure for communicating between servers and storing information.
|
||||||
|
177
midiRouter.go
177
midiRouter.go
@ -11,6 +11,7 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
mqtt "github.com/eclipse/paho.mqtt.golang"
|
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||||
"gitlab.com/gomidi/midi/v2"
|
"gitlab.com/gomidi/midi/v2"
|
||||||
@ -21,8 +22,10 @@ import (
|
|||||||
type LogLevel int
|
type LogLevel int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Logs only errors.
|
// Logs info messages.
|
||||||
ErrorLog LogLevel = iota
|
InfoLog LogLevel = iota
|
||||||
|
// Log only errors.
|
||||||
|
ErrorLog
|
||||||
// MQTT, HTTP, and MIDI receive logging.
|
// MQTT, HTTP, and MIDI receive logging.
|
||||||
ReceiveLog
|
ReceiveLog
|
||||||
// MQTT, HTTP, and MIDI send logging.
|
// MQTT, HTTP, and MIDI send logging.
|
||||||
@ -33,7 +36,7 @@ const (
|
|||||||
|
|
||||||
// Provides a string value for a log level.
|
// Provides a string value for a log level.
|
||||||
func (l LogLevel) String() string {
|
func (l LogLevel) String() string {
|
||||||
return [...]string{"Error", "Receive", "Send", "Debug"}[l]
|
return [...]string{"Info", "Error", "Receive", "Send", "Debug"}[l]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configurations relating to MQTT connection.
|
// Configurations relating to MQTT connection.
|
||||||
@ -134,10 +137,11 @@ type MidiRouter struct {
|
|||||||
RequestTriggers []RequestTrigger `fig:"request_triggers"`
|
RequestTriggers []RequestTrigger `fig:"request_triggers"`
|
||||||
|
|
||||||
// How much logging.
|
// How much logging.
|
||||||
// 0 - Errors
|
// 0 - Info
|
||||||
// 1 - MQTT, HTTP, and MIDI receive logging.
|
// 1 - Errors
|
||||||
// 2 - MQTT, HTTP, and MIDI send logging.
|
// 2 - MQTT, HTTP, and MIDI receive logging.
|
||||||
// 3 - Debug
|
// 3 - MQTT, HTTP, and MIDI send logging.
|
||||||
|
// 4 - Debug
|
||||||
LogLevel LogLevel `fig:"log_level"`
|
LogLevel LogLevel `fig:"log_level"`
|
||||||
|
|
||||||
// Connection to MIDI device.
|
// Connection to MIDI device.
|
||||||
@ -462,81 +466,105 @@ func (r *MidiRouter) MqttSubscribe(topic string) {
|
|||||||
func (r *MidiRouter) Connect() {
|
func (r *MidiRouter) Connect() {
|
||||||
// If request triggers defined, find the out port.
|
// If request triggers defined, find the out port.
|
||||||
if len(r.RequestTriggers) != 0 {
|
if len(r.RequestTriggers) != 0 {
|
||||||
out, err := midi.FindOutPort(r.Device)
|
go func() {
|
||||||
if err != nil {
|
for {
|
||||||
log.Println("Can't find output device:", r.Device)
|
out, err := midi.FindOutPort(r.Device)
|
||||||
} else {
|
if err != nil {
|
||||||
r.MidiOut = out
|
r.Log(ErrorLog, "Can't find output device: %s", r.Device)
|
||||||
}
|
} else {
|
||||||
|
r.MidiOut = out
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Log(ErrorLog, "Retrying in 1 minute.")
|
||||||
|
time.Sleep(time.Minute)
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// If listener is disabled, stop here.
|
// If listener is disabled, stop here.
|
||||||
if r.DisableListener {
|
if !r.DisableListener {
|
||||||
return
|
go func() {
|
||||||
|
for {
|
||||||
|
// Try finding input port.
|
||||||
|
r.Log(InfoLog, "Connecting to input device: %s", r.Device)
|
||||||
|
in, err := midi.FindInPort(r.Device)
|
||||||
|
if err != nil {
|
||||||
|
r.Log(ErrorLog, "Can't find input device: %s", r.Device)
|
||||||
|
r.Log(ErrorLog, "Retrying in 1 minute.")
|
||||||
|
time.Sleep(time.Minute)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start listening to MIDI messages.
|
||||||
|
stop, err := midi.ListenTo(in, func(msg midi.Message, timestampms int32) {
|
||||||
|
var channel, note, velocity uint8
|
||||||
|
switch {
|
||||||
|
// Get notes with an velocity set.
|
||||||
|
case msg.GetNoteStart(&channel, ¬e, &velocity):
|
||||||
|
r.Log(ReceiveLog, "starting note %s(%d) on channel %v with velocity %v", midi.Note(note), note, channel, velocity)
|
||||||
|
// Process request.
|
||||||
|
r.sendRequest(channel, note, velocity)
|
||||||
|
|
||||||
|
// If no velocity is set, an note end message is received.
|
||||||
|
case msg.GetNoteEnd(&channel, ¬e):
|
||||||
|
r.Log(ReceiveLog, "ending note %s(%d) on channel %v", midi.Note(note), note, channel)
|
||||||
|
// Process request.
|
||||||
|
r.sendRequest(channel, note, 0)
|
||||||
|
default:
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
r.Log(ErrorLog, "Error listening to device: %s", err)
|
||||||
|
r.Log(ErrorLog, "Retrying in 1 minute.")
|
||||||
|
time.Sleep(time.Minute)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r.Log(InfoLog, "Connected to input device: %s", r.Device)
|
||||||
|
|
||||||
|
// Update stop function for disconnects.
|
||||||
|
r.ListenerStop = stop
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try finding input port.
|
|
||||||
log.Println("Connecting to device:", r.Device)
|
|
||||||
in, err := midi.FindInPort(r.Device)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Can't find device:", r.Device)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start listening to MIDI messages.
|
|
||||||
stop, err := midi.ListenTo(in, func(msg midi.Message, timestampms int32) {
|
|
||||||
var channel, note, velocity uint8
|
|
||||||
switch {
|
|
||||||
// Get notes with an velocity set.
|
|
||||||
case msg.GetNoteStart(&channel, ¬e, &velocity):
|
|
||||||
r.Log(ReceiveLog, "starting note %s(%d) on channel %v with velocity %v", midi.Note(note), note, channel, velocity)
|
|
||||||
// Process request.
|
|
||||||
r.sendRequest(channel, note, velocity)
|
|
||||||
|
|
||||||
// If no velocity is set, an note end message is received.
|
|
||||||
case msg.GetNoteEnd(&channel, ¬e):
|
|
||||||
r.Log(ReceiveLog, "ending note %s(%d) on channel %v", midi.Note(note), note, channel)
|
|
||||||
// Process request.
|
|
||||||
r.sendRequest(channel, note, 0)
|
|
||||||
default:
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
r.Log(ErrorLog, "Error listening to device: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update stop function for disconnects.
|
|
||||||
r.ListenerStop = stop
|
|
||||||
|
|
||||||
if r.MQTT.Host != "" && r.MQTT.Port != 0 {
|
if r.MQTT.Host != "" && r.MQTT.Port != 0 {
|
||||||
// Connect to MQTT.
|
go func() {
|
||||||
mqtt_opts := mqtt.NewClientOptions()
|
for {
|
||||||
mqtt_opts.AddBroker(fmt.Sprintf("tcp://%s:%d", r.MQTT.Host, r.MQTT.Port))
|
// Connect to MQTT.
|
||||||
mqtt_opts.SetClientID(r.MQTT.ClientId)
|
mqtt_opts := mqtt.NewClientOptions()
|
||||||
mqtt_opts.SetUsername(r.MQTT.User)
|
mqtt_opts.AddBroker(fmt.Sprintf("tcp://%s:%d", r.MQTT.Host, r.MQTT.Port))
|
||||||
mqtt_opts.SetPassword(r.MQTT.Password)
|
mqtt_opts.SetClientID(r.MQTT.ClientId)
|
||||||
r.MqttClient = mqtt.NewClient(mqtt_opts)
|
mqtt_opts.SetUsername(r.MQTT.User)
|
||||||
|
mqtt_opts.SetPassword(r.MQTT.Password)
|
||||||
|
r.MqttClient = mqtt.NewClient(mqtt_opts)
|
||||||
|
|
||||||
// Connect and failures are fatal exiting service.
|
// Connect and failures are fatal exiting service.
|
||||||
r.Log(DebugLog, "Connecting to MQTT")
|
r.Log(DebugLog, "Connecting to MQTT")
|
||||||
if t := r.MqttClient.Connect(); t.Wait() && t.Error() != nil {
|
if t := r.MqttClient.Connect(); t.Wait() && t.Error() != nil {
|
||||||
log.Fatalf("MQTT error: %s", t.Error())
|
log.Fatalf("MQTT error: %s", t.Error())
|
||||||
return
|
r.Log(ErrorLog, "Retrying in 1 minute.")
|
||||||
}
|
time.Sleep(time.Minute)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Subscribe to MQTT topics.
|
// Subscribe to MQTT topics.
|
||||||
r.MqttSubscribe(r.MQTT.Topic + "/send")
|
r.MqttSubscribe(r.MQTT.Topic + "/send")
|
||||||
r.MqttSubscribe(r.MQTT.Topic + "/status/check")
|
r.MqttSubscribe(r.MQTT.Topic + "/status/check")
|
||||||
// Subscribe to command topics configured.
|
// Subscribe to command topics configured.
|
||||||
for _, trig := range r.RequestTriggers {
|
for _, trig := range r.RequestTriggers {
|
||||||
if trig.MqttTopic != "" {
|
if trig.MqttTopic != "" {
|
||||||
r.MqttSubscribe(trig.MqttTopic)
|
r.MqttSubscribe(trig.MqttTopic)
|
||||||
|
}
|
||||||
|
if trig.MqttSubTopic != "" {
|
||||||
|
r.MqttSubscribe(r.MQTT.Topic + "/" + trig.MqttSubTopic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
if trig.MqttSubTopic != "" {
|
}()
|
||||||
r.MqttSubscribe(r.MQTT.Topic + "/" + trig.MqttSubTopic)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -546,4 +574,5 @@ func (r *MidiRouter) Disconnect() {
|
|||||||
if r.ListenerStop != nil {
|
if r.ListenerStop != nil {
|
||||||
r.ListenerStop()
|
r.ListenerStop()
|
||||||
}
|
}
|
||||||
|
r.MqttClient.Disconnect(0)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user