From 142e72e312d05838387cdd501300c18701326849 Mon Sep 17 00:00:00 2001 From: aalimonov_2 <aalimonov_2@miem.hse.ru> Date: Fri, 22 Dec 2023 09:51:46 +0300 Subject: [PATCH 1/3] Add namespaces to signals and commands for midi --- README.md | 91 -------------------------------------- cmd/main.go | 26 ++++++++--- pkg/midi/device_manager.go | 41 ++++++++++++++--- pkg/midi/in.go | 36 ++++++++++++--- pkg/midi/midi_device.go | 18 ++++---- pkg/model/commands.go | 38 ++++++++++++++++ pkg/model/signals.go | 28 +++++++----- 7 files changed, 148 insertions(+), 130 deletions(-) diff --git a/README.md b/README.md index b5cf5a9..50ac3e7 100644 --- a/README.md +++ b/README.md @@ -1,92 +1 @@ # Midi Manipulator - - - -## Getting started - -To make it easy for you to get started with GitLab, here's a list of recommended next steps. - -Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! - -## Add your files - -- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files -- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: - -``` -cd existing_repo -git remote add origin https://git.miem.hse.ru/hubman/midi_manipulator.git -git branch -M master -git push -uf origin master -``` - -## Integrate with your tools - -- [ ] [Set up project integrations](https://git.miem.hse.ru/hubman/midi_manipulator/-/settings/integrations) - -## Collaborate with your team - -- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) -- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) -- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) -- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) -- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) - -## Test and Deploy - -Use the built-in continuous integration in GitLab. - -- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) -- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) -- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) -- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) -- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) - -*** - -# Editing this README - -When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template. - -## Suggestions for a good README -Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. - -## Name -Choose a self-explaining name for your project. - -## Description -Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. - -## Badges -On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. - -## Visuals -Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. - -## Installation -Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. - -## Usage -Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. - -## Support -Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. - -## Roadmap -If you have ideas for releases in the future, it is a good idea to list them in the README. - -## Contributing -State if you are open to contributions and what your requirements are for accepting them. - -For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. - -You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. - -## Authors and acknowledgment -Show your appreciation to those who have contributed to the project. - -## License -For open source projects, say how it is licensed. - -## Project status -If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. diff --git a/cmd/main.go b/cmd/main.go index 9d9b7f0..9d2533e 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -7,6 +7,7 @@ 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" "log" "midi_manipulator/pkg/config" midiHermophrodite "midi_manipulator/pkg/midi" @@ -38,7 +39,11 @@ func main() { } func setupApp(systemConfig *core.SystemConfig, userConfig *config.UserConfig) { - deviceManager := midiHermophrodite.NewDeviceManager() + logger, err := zap.NewProduction() + if err != nil { // FIXME: use app container api after + log.Fatal(err) + } + deviceManager := midiHermophrodite.NewDeviceManager(logger) defer deviceManager.Close() agentConf := core.AgentConfiguration{ @@ -64,7 +69,7 @@ func setupApp(systemConfig *core.SystemConfig, userConfig *config.UserConfig) { func(command core.SerializedCommand, parser executor.CommandParser) { var cmd model.TurnLightOnCommand parser(&cmd) - err := deviceManager.ExecuteOnDevice(cmd.DeviceAlias, cmd) + err := deviceManager.ExecuteOnDevice(cmd.DeviceAlias, cmd.Namespace, cmd) if err != nil { log.Println(err) } @@ -73,7 +78,7 @@ func setupApp(systemConfig *core.SystemConfig, userConfig *config.UserConfig) { func(command core.SerializedCommand, parser executor.CommandParser) { var cmd model.TurnLightOffCommand parser(&cmd) - err := deviceManager.ExecuteOnDevice(cmd.DeviceAlias, cmd) + err := deviceManager.ExecuteOnDevice(cmd.DeviceAlias, cmd.Namespace, cmd) if err != nil { log.Println(err) } @@ -82,7 +87,7 @@ func setupApp(systemConfig *core.SystemConfig, userConfig *config.UserConfig) { func(command core.SerializedCommand, parser executor.CommandParser) { var cmd model.SingleBlinkCommand parser(&cmd) - err := deviceManager.ExecuteOnDevice(cmd.DeviceAlias, cmd) + err := deviceManager.ExecuteOnDevice(cmd.DeviceAlias, cmd.Namespace, cmd) if err != nil { log.Println(err) } @@ -91,7 +96,7 @@ func setupApp(systemConfig *core.SystemConfig, userConfig *config.UserConfig) { func(command core.SerializedCommand, parser executor.CommandParser) { var cmd model.SingleReversedBlinkCommand parser(&cmd) - err := deviceManager.ExecuteOnDevice(cmd.DeviceAlias, cmd) + err := deviceManager.ExecuteOnDevice(cmd.DeviceAlias, cmd.Namespace, cmd) if err != nil { log.Println(err) } @@ -100,11 +105,18 @@ func setupApp(systemConfig *core.SystemConfig, userConfig *config.UserConfig) { func(command core.SerializedCommand, parser executor.CommandParser) { var cmd model.ContinuousBlinkCommand parser(&cmd) - err := deviceManager.ExecuteOnDevice(cmd.DeviceAlias, cmd) + err := deviceManager.ExecuteOnDevice(cmd.DeviceAlias, cmd.Namespace, cmd) if err != nil { log.Println(err) } - })), + }), + hubman.WithCommand(model.SetActiveNamespaceCommand{}, + func(s core.SerializedCommand, p executor.CommandParser) { + var cmd model.SetActiveNamespaceCommand + p(&cmd) + deviceManager.SetActiveNamespace(cmd.Namespace) + }), + ), hubman.WithOnConfigRefresh(func(configuration core.AgentConfiguration) { update, _ := configuration.User.([]config.DeviceConfig) deviceManager.UpdateDevices(update) diff --git a/pkg/midi/device_manager.go b/pkg/midi/device_manager.go index 74f4f1e..418521f 100644 --- a/pkg/midi/device_manager.go +++ b/pkg/midi/device_manager.go @@ -4,6 +4,7 @@ import ( "fmt" "git.miem.hse.ru/hubman/hubman-lib/core" _ "gitlab.com/gomidi/midi/v2" + "go.uber.org/zap" "log" "midi_manipulator/pkg/config" "midi_manipulator/pkg/model" @@ -14,6 +15,10 @@ type DeviceManager struct { devices map[string]*MidiDevice mutex sync.Mutex signals chan core.Signal + logger *zap.Logger + + activeNamespace string + nmMutex sync.RWMutex } func (dm *DeviceManager) getDevice(alias string) (*MidiDevice, bool) { @@ -44,14 +49,22 @@ func (dm *DeviceManager) Close() { } } -func (dm *DeviceManager) ExecuteOnDevice(alias string, cmd model.MidiCommand) error { +func (dm *DeviceManager) ExecuteOnDevice(alias string, namespace string, cmd model.MidiCommand) error { device, found := dm.getDevice(alias) if !found { + dm.logger.Warn("Received command for non existing device", zap.String("device", alias)) return fmt.Errorf("device with alias {%s} doesn't exist", alias) } - if !device.active { + if active, hasActive := dm.getActiveNamespace(); !hasActive { + dm.logger.Warn("Received command, but device manager has no active namespace", zap.String("wantNamespace", namespace)) + } else if active != namespace { + dm.logger.Warn("Received command for inactive namespace", zap.String("wantNamespace", namespace)) + } + + if !device.active { // Possibly should be deprecated? + dm.logger.Warn("Received command for inactive device", zap.String("device", alias)) return fmt.Errorf("device with alias {%s} is not active", alias) } @@ -65,6 +78,7 @@ func (dm *DeviceManager) ExecuteOnDevice(alias string, cmd model.MidiCommand) er } func (dm *DeviceManager) AddDevice(device *MidiDevice) error { + dm.logger.Info("Adding device", zap.String("alias", device.GetAlias())) _, found := dm.getDevice(device.GetAlias()) if found { @@ -72,7 +86,7 @@ func (dm *DeviceManager) AddDevice(device *MidiDevice) error { } dm.addDevice(device) - err := device.RunDevice(dm.signals) + err := device.RunDevice(dm.signals, dm.getActiveNamespace) if err != nil { return err @@ -82,6 +96,8 @@ func (dm *DeviceManager) AddDevice(device *MidiDevice) error { } func (dm *DeviceManager) RemoveDevice(alias string) error { + dm.logger.Info("Removing device", zap.String("device", alias)) + device, found := dm.getDevice(alias) if !found { return fmt.Errorf("device {%s} doesn't exist", device.GetAlias()) @@ -99,6 +115,7 @@ func (dm *DeviceManager) RemoveDevice(alias string) error { } func (dm *DeviceManager) UpdateDevices(midiConfig []config.DeviceConfig) { + dm.logger.Info("Updating devices", zap.Int("deviceCount", len(midiConfig))) var midiConfigMap = make(map[string]config.DeviceConfig) for _, device := range midiConfig { @@ -138,10 +155,24 @@ func (dm *DeviceManager) GetSignals() chan core.Signal { return dm.signals } -func NewDeviceManager() *DeviceManager { - dm := DeviceManager{} +func (dm *DeviceManager) getActiveNamespace() (string, bool) { + dm.nmMutex.RLock() + defer dm.nmMutex.RUnlock() + return dm.activeNamespace, dm.activeNamespace == "" +} + +func (dm *DeviceManager) SetActiveNamespace(newActive string) { + dm.logger.Info("Setting namespace as active", zap.String("namespace", newActive)) + dm.nmMutex.Lock() + dm.activeNamespace = newActive + dm.nmMutex.Unlock() +} + +func NewDeviceManager(logger *zap.Logger) *DeviceManager { + dm := DeviceManager{logger: logger} dm.devices = make(map[string]*MidiDevice) dm.signals = make(chan core.Signal) + logger.Info("Created device manager") return &dm } diff --git a/pkg/midi/in.go b/pkg/midi/in.go index 61b3a8d..e8a3101 100644 --- a/pkg/midi/in.go +++ b/pkg/midi/in.go @@ -15,7 +15,7 @@ func (md *MidiDevice) sendSignal(signals chan<- core.Signal, signal core.Signal) } } -func (md *MidiDevice) getMidiMessage(msg midi.Message, timestamps int32) { +func (md *MidiDevice) getMidiMessage(msg midi.Message, _ int32) { md.mutex.Lock() defer md.mutex.Unlock() var channel, key, velocity uint8 @@ -47,26 +47,48 @@ func (md *MidiDevice) messageToSignal() []core.Signal { for _, kctx := range md.clickBuffer { switch kctx.status.(type) { case nil: - signal := model.NotePushed{Device: md.name, KeyCode: int(kctx.key), Velocity: int(kctx.velocity)} + ns, _ := md.getNamespace() + signal := model.NotePushed{ + Device: md.name, + KeyCode: int(kctx.key), + Velocity: int(kctx.velocity), + Namespace: ns, + } signalSequence = append(signalSequence, signal) // UPDATE KEY STATUS IN BUFFER kctx.status = signal case model.NotePushed: if time.Now().Sub(kctx.usedAt) >= md.holdDelta { - signal := model.NoteHold{Device: md.name, KeyCode: int(kctx.key), Velocity: int(kctx.velocity)} + ns, _ := md.getNamespace() + signal := model.NoteHold{ + Device: md.name, + KeyCode: int(kctx.key), + Velocity: int(kctx.velocity), + Namespace: ns, + } signalSequence = append(signalSequence, signal) // UPDATE KEY STATUS IN BUFFER kctx.status = signal } case model.NoteReleased: - signal := model.NoteReleased{Device: md.name, KeyCode: int(kctx.key), - Velocity: int(kctx.velocity)} + ns, _ := md.getNamespace() + signal := model.NoteReleased{ + Device: md.name, + KeyCode: int(kctx.key), + Velocity: int(kctx.velocity), + Namespace: ns, + } signalSequence = append(signalSequence, signal) // DELETE KEY FROM BUFFER delete(md.clickBuffer, kctx.key) case model.ControlPushed: - signal := model.ControlPushed{Device: md.name, KeyCode: int(kctx.key), - Value: int(kctx.velocity)} + ns, _ := md.getNamespace() + signal := model.ControlPushed{ + Device: md.name, + KeyCode: int(kctx.key), + Value: int(kctx.velocity), + Namespace: ns, + } signalSequence = append(signalSequence, signal) // DELETE KEY FROM BUFFER delete(md.clickBuffer, kctx.key) diff --git a/pkg/midi/midi_device.go b/pkg/midi/midi_device.go index db2e41c..caa1325 100644 --- a/pkg/midi/midi_device.go +++ b/pkg/midi/midi_device.go @@ -13,13 +13,14 @@ import ( ) type MidiDevice struct { - name string - active bool - ports MidiPorts - clickBuffer ClickBuffer - holdDelta time.Duration - mutex sync.Mutex - stop chan bool + name string + active bool + ports MidiPorts + clickBuffer ClickBuffer + holdDelta time.Duration + mutex sync.Mutex + stop chan bool + getNamespace func() (string, bool) } type MidiPorts struct { @@ -49,8 +50,9 @@ func (md *MidiDevice) StopDevice() error { return nil } -func (md *MidiDevice) RunDevice(signals chan<- core.Signal) error { +func (md *MidiDevice) RunDevice(signals chan<- core.Signal, getActiveNamespace func() (string, bool)) error { log.Printf("MIDI DEVICE {%s} CONNECTING ...\n", md.name) + md.getNamespace = getActiveNamespace go md.startupIllumination() go md.listen(signals) return nil diff --git a/pkg/model/commands.go b/pkg/model/commands.go index 4c02aba..d7e478c 100644 --- a/pkg/model/commands.go +++ b/pkg/model/commands.go @@ -3,11 +3,17 @@ package model type MidiCommand interface { Code() string Description() string + GetNamespace() string } type TurnLightOnCommand struct { KeyCode int `hubman:"key_code"` DeviceAlias string `hubman:"device_alias"` + Namespace string `hubman:"namespace"` +} + +func (c TurnLightOnCommand) GetNamespace() string { + return c.Namespace } func (c TurnLightOnCommand) Code() string { @@ -21,6 +27,11 @@ func (c TurnLightOnCommand) Description() string { type TurnLightOffCommand struct { KeyCode int `hubman:"key_code"` DeviceAlias string `hubman:"device_alias"` + Namespace string `hubman:"namespace"` +} + +func (c TurnLightOffCommand) GetNamespace() string { + return c.Namespace } func (c TurnLightOffCommand) Code() string { @@ -34,6 +45,11 @@ func (c TurnLightOffCommand) Description() string { type SingleBlinkCommand struct { KeyCode int `hubman:"key_code"` DeviceAlias string `hubman:"device_alias"` + Namespace string `hubman:"namespace"` +} + +func (c SingleBlinkCommand) GetNamespace() string { + return c.Namespace } func (c SingleBlinkCommand) Code() string { @@ -47,6 +63,11 @@ func (c SingleBlinkCommand) Description() string { type SingleReversedBlinkCommand struct { KeyCode int `hubman:"key_code"` DeviceAlias string `hubman:"device_alias"` + Namespace string `hubman:"namespace"` +} + +func (c SingleReversedBlinkCommand) GetNamespace() string { + return c.Namespace } func (c SingleReversedBlinkCommand) Code() string { @@ -60,6 +81,11 @@ func (c SingleReversedBlinkCommand) Description() string { type ContinuousBlinkCommand struct { KeyCode int `hubman:"key_code"` DeviceAlias string `hubman:"device_alias"` + Namespace string `hubman:"namespace"` +} + +func (c ContinuousBlinkCommand) GetNamespace() string { + return c.Namespace } func (c ContinuousBlinkCommand) Code() string { @@ -69,3 +95,15 @@ func (c ContinuousBlinkCommand) Code() string { func (c ContinuousBlinkCommand) Description() string { return "Continuous blink (until next discontinuous command) specified MIDI key" } + +type SetActiveNamespaceCommand struct { + Namespace string `hubman:"namespace"` +} + +func (s SetActiveNamespaceCommand) Code() string { + return "SetActiveNamespaceCommand" +} + +func (s SetActiveNamespaceCommand) Description() string { + return `Sets given namespace as active, all signals will be received from it and commands are executed only if them belong to it` +} diff --git a/pkg/model/signals.go b/pkg/model/signals.go index a26b02a..e88e184 100644 --- a/pkg/model/signals.go +++ b/pkg/model/signals.go @@ -1,9 +1,10 @@ package model type NotePushed struct { - Device string `hubman:"device"` - KeyCode int `hubman:"key_code"` - Velocity int `hubman:"velocity"` + Device string `hubman:"device"` + Namespace string `hubman:"namespace"` + KeyCode int `hubman:"key_code"` + Velocity int `hubman:"velocity"` } func (s NotePushed) Code() string { @@ -15,9 +16,10 @@ func (s NotePushed) Description() string { } type NoteHold struct { - Device string `hubman:"device"` - KeyCode int `hubman:"key_code"` - Velocity int `hubman:"velocity"` + Device string `hubman:"device"` + Namespace string `hubman:"namespace"` + KeyCode int `hubman:"key_code"` + Velocity int `hubman:"velocity"` } func (s NoteHold) Code() string { @@ -29,9 +31,10 @@ func (s NoteHold) Description() string { } type NoteReleased struct { - Device string `hubman:"device"` - KeyCode int `hubman:"key_code"` - Velocity int `hubman:"velocity"` + Device string `hubman:"device"` + KeyCode int `hubman:"key_code"` + Velocity int `hubman:"velocity"` + Namespace string `hubman:"namespace"` } func (s NoteReleased) Code() string { @@ -45,9 +48,10 @@ func (s NoteReleased) Description() string { // ControlPushed Ð’ MIDI Control имеет только один тип Ñобытий "ControlChange", // поÑтому длительноÑÑ‚ÑŒ и конец Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ Ð·Ð´ÐµÑÑŒ не отÑлеживаютÑÑ type ControlPushed struct { - Device string `hubman:"device"` - KeyCode int `hubman:"key_code"` - Value int `hubman:"velocity"` + Device string `hubman:"device"` + Namespace string `hubman:"namespace"` + KeyCode int `hubman:"key_code"` + Value int `hubman:"velocity"` } func (s ControlPushed) Code() string { -- GitLab From 57b9d4478dc79646b1f283742fccc787a93147d8 Mon Sep 17 00:00:00 2001 From: aalimonov_2 <aalimonov_2@miem.hse.ru> Date: Fri, 22 Dec 2023 12:27:13 +0300 Subject: [PATCH 2/3] Fix hasActive comparison --- pkg/midi/device_manager.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/midi/device_manager.go b/pkg/midi/device_manager.go index 418521f..7cadc90 100644 --- a/pkg/midi/device_manager.go +++ b/pkg/midi/device_manager.go @@ -158,7 +158,7 @@ func (dm *DeviceManager) GetSignals() chan core.Signal { func (dm *DeviceManager) getActiveNamespace() (string, bool) { dm.nmMutex.RLock() defer dm.nmMutex.RUnlock() - return dm.activeNamespace, dm.activeNamespace == "" + return dm.activeNamespace, dm.activeNamespace != "" } func (dm *DeviceManager) SetActiveNamespace(newActive string) { -- GitLab From 3103c79021256c3227f0657f272434d187143bd5 Mon Sep 17 00:00:00 2001 From: Olex1313 <aa1limonov@gmail.com> Date: Sat, 23 Dec 2023 08:40:30 +0300 Subject: [PATCH 3/3] Device namespaces v2 --- cmd/main.go | 12 ++++++------ configs/user_config.yaml | 3 ++- pkg/config/config.go | 14 ++++---------- pkg/midi/device_manager.go | 28 ++++++++++------------------ pkg/midi/in.go | 12 ++++-------- pkg/midi/midi_device.go | 20 ++++++++++---------- pkg/model/commands.go | 29 ++--------------------------- 7 files changed, 38 insertions(+), 80 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 9d2533e..71cf88b 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -69,7 +69,7 @@ func setupApp(systemConfig *core.SystemConfig, userConfig *config.UserConfig) { func(command core.SerializedCommand, parser executor.CommandParser) { var cmd model.TurnLightOnCommand parser(&cmd) - err := deviceManager.ExecuteOnDevice(cmd.DeviceAlias, cmd.Namespace, cmd) + err := deviceManager.ExecuteOnDevice(cmd.DeviceAlias, cmd) if err != nil { log.Println(err) } @@ -78,7 +78,7 @@ func setupApp(systemConfig *core.SystemConfig, userConfig *config.UserConfig) { func(command core.SerializedCommand, parser executor.CommandParser) { var cmd model.TurnLightOffCommand parser(&cmd) - err := deviceManager.ExecuteOnDevice(cmd.DeviceAlias, cmd.Namespace, cmd) + err := deviceManager.ExecuteOnDevice(cmd.DeviceAlias, cmd) if err != nil { log.Println(err) } @@ -87,7 +87,7 @@ func setupApp(systemConfig *core.SystemConfig, userConfig *config.UserConfig) { func(command core.SerializedCommand, parser executor.CommandParser) { var cmd model.SingleBlinkCommand parser(&cmd) - err := deviceManager.ExecuteOnDevice(cmd.DeviceAlias, cmd.Namespace, cmd) + err := deviceManager.ExecuteOnDevice(cmd.DeviceAlias, cmd) if err != nil { log.Println(err) } @@ -96,7 +96,7 @@ func setupApp(systemConfig *core.SystemConfig, userConfig *config.UserConfig) { func(command core.SerializedCommand, parser executor.CommandParser) { var cmd model.SingleReversedBlinkCommand parser(&cmd) - err := deviceManager.ExecuteOnDevice(cmd.DeviceAlias, cmd.Namespace, cmd) + err := deviceManager.ExecuteOnDevice(cmd.DeviceAlias, cmd) if err != nil { log.Println(err) } @@ -105,7 +105,7 @@ func setupApp(systemConfig *core.SystemConfig, userConfig *config.UserConfig) { func(command core.SerializedCommand, parser executor.CommandParser) { var cmd model.ContinuousBlinkCommand parser(&cmd) - err := deviceManager.ExecuteOnDevice(cmd.DeviceAlias, cmd.Namespace, cmd) + err := deviceManager.ExecuteOnDevice(cmd.DeviceAlias, cmd) if err != nil { log.Println(err) } @@ -114,7 +114,7 @@ func setupApp(systemConfig *core.SystemConfig, userConfig *config.UserConfig) { func(s core.SerializedCommand, p executor.CommandParser) { var cmd model.SetActiveNamespaceCommand p(&cmd) - deviceManager.SetActiveNamespace(cmd.Namespace) + deviceManager.SetActiveNamespace(cmd.Namespace, cmd.Device) }), ), hubman.WithOnConfigRefresh(func(configuration core.AgentConfiguration) { diff --git a/configs/user_config.yaml b/configs/user_config.yaml index 0e61060..133d138 100644 --- a/configs/user_config.yaml +++ b/configs/user_config.yaml @@ -1,4 +1,5 @@ midi_devices: - device_name: MPD226 active: true - hold_delta: 1 \ No newline at end of file + hold_delta: 1 + namespace: default diff --git a/pkg/config/config.go b/pkg/config/config.go index 171e709..aee89e4 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -3,13 +3,13 @@ package config import ( "fmt" "gopkg.in/yaml.v3" - "os" ) 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"` } type UserConfig struct { @@ -30,6 +30,9 @@ func (conf *UserConfig) Validate() error { "Now {%f} is provided", idx, device.DeviceName, device.HoldDelta) } + if device.Namespace == "" { + return fmt.Errorf("device #{%d} ({%s}) has no namespace specified", idx, device.DeviceName) + } if device.HoldDelta < 0 { return fmt.Errorf("device #{%d} ({%s}): "+ "valid MIDI hold_delta must be provided in config."+ @@ -53,15 +56,6 @@ func (conf *UserConfig) hasDuplicateDevices() (string, bool) { return "", false } -func InitConfig(confPath string) (*UserConfig, error) { - jsonFile, err := os.ReadFile(confPath) - if err != nil { - return nil, err - } - cfg, err := ParseConfigFromBytes(jsonFile) - return cfg, err -} - func ParseConfigFromBytes(data []byte) (*UserConfig, error) { cfg := UserConfig{} diff --git a/pkg/midi/device_manager.go b/pkg/midi/device_manager.go index 7cadc90..34decfa 100644 --- a/pkg/midi/device_manager.go +++ b/pkg/midi/device_manager.go @@ -49,7 +49,7 @@ func (dm *DeviceManager) Close() { } } -func (dm *DeviceManager) ExecuteOnDevice(alias string, namespace string, cmd model.MidiCommand) error { +func (dm *DeviceManager) ExecuteOnDevice(alias string, cmd model.MidiCommand) error { device, found := dm.getDevice(alias) if !found { @@ -57,12 +57,6 @@ func (dm *DeviceManager) ExecuteOnDevice(alias string, namespace string, cmd mod return fmt.Errorf("device with alias {%s} doesn't exist", alias) } - if active, hasActive := dm.getActiveNamespace(); !hasActive { - dm.logger.Warn("Received command, but device manager has no active namespace", zap.String("wantNamespace", namespace)) - } else if active != namespace { - dm.logger.Warn("Received command for inactive namespace", zap.String("wantNamespace", namespace)) - } - if !device.active { // Possibly should be deprecated? dm.logger.Warn("Received command for inactive device", zap.String("device", alias)) return fmt.Errorf("device with alias {%s} is not active", alias) @@ -86,7 +80,7 @@ func (dm *DeviceManager) AddDevice(device *MidiDevice) error { } dm.addDevice(device) - err := device.RunDevice(dm.signals, dm.getActiveNamespace) + err := device.RunDevice(dm.signals) if err != nil { return err @@ -155,17 +149,15 @@ func (dm *DeviceManager) GetSignals() chan core.Signal { return dm.signals } -func (dm *DeviceManager) getActiveNamespace() (string, bool) { - dm.nmMutex.RLock() - defer dm.nmMutex.RUnlock() - return dm.activeNamespace, dm.activeNamespace != "" -} - -func (dm *DeviceManager) SetActiveNamespace(newActive string) { +func (dm *DeviceManager) SetActiveNamespace(newActive string, device string) { dm.logger.Info("Setting namespace as active", zap.String("namespace", newActive)) - dm.nmMutex.Lock() - dm.activeNamespace = newActive - dm.nmMutex.Unlock() + dm.mutex.Lock() + d, ok := dm.devices[device] + d.namespace = newActive + if !ok { + dm.logger.Error("Not found given device for namespace", zap.String("device", device), zap.String("newNamespace", newActive)) + } + dm.mutex.Unlock() } func NewDeviceManager(logger *zap.Logger) *DeviceManager { diff --git a/pkg/midi/in.go b/pkg/midi/in.go index e8a3101..2909d12 100644 --- a/pkg/midi/in.go +++ b/pkg/midi/in.go @@ -47,47 +47,43 @@ func (md *MidiDevice) messageToSignal() []core.Signal { for _, kctx := range md.clickBuffer { switch kctx.status.(type) { case nil: - ns, _ := md.getNamespace() signal := model.NotePushed{ Device: md.name, KeyCode: int(kctx.key), Velocity: int(kctx.velocity), - Namespace: ns, + Namespace: md.namespace, } signalSequence = append(signalSequence, signal) // UPDATE KEY STATUS IN BUFFER kctx.status = signal case model.NotePushed: if time.Now().Sub(kctx.usedAt) >= md.holdDelta { - ns, _ := md.getNamespace() signal := model.NoteHold{ Device: md.name, KeyCode: int(kctx.key), Velocity: int(kctx.velocity), - Namespace: ns, + Namespace: md.namespace, } signalSequence = append(signalSequence, signal) // UPDATE KEY STATUS IN BUFFER kctx.status = signal } case model.NoteReleased: - ns, _ := md.getNamespace() signal := model.NoteReleased{ Device: md.name, KeyCode: int(kctx.key), Velocity: int(kctx.velocity), - Namespace: ns, + Namespace: md.namespace, } signalSequence = append(signalSequence, signal) // DELETE KEY FROM BUFFER delete(md.clickBuffer, kctx.key) case model.ControlPushed: - ns, _ := md.getNamespace() signal := model.ControlPushed{ Device: md.name, KeyCode: int(kctx.key), Value: int(kctx.velocity), - Namespace: ns, + Namespace: md.namespace, } signalSequence = append(signalSequence, signal) // DELETE KEY FROM BUFFER diff --git a/pkg/midi/midi_device.go b/pkg/midi/midi_device.go index caa1325..cd856f7 100644 --- a/pkg/midi/midi_device.go +++ b/pkg/midi/midi_device.go @@ -13,14 +13,14 @@ import ( ) type MidiDevice struct { - name string - active bool - ports MidiPorts - clickBuffer ClickBuffer - holdDelta time.Duration - mutex sync.Mutex - stop chan bool - getNamespace func() (string, bool) + name string + active bool + ports MidiPorts + clickBuffer ClickBuffer + holdDelta time.Duration + mutex sync.Mutex + stop chan bool + namespace string } type MidiPorts struct { @@ -50,9 +50,8 @@ func (md *MidiDevice) StopDevice() error { return nil } -func (md *MidiDevice) RunDevice(signals chan<- core.Signal, getActiveNamespace func() (string, bool)) error { +func (md *MidiDevice) RunDevice(signals chan<- core.Signal) error { log.Printf("MIDI DEVICE {%s} CONNECTING ...\n", md.name) - md.getNamespace = getActiveNamespace go md.startupIllumination() go md.listen(signals) return nil @@ -109,6 +108,7 @@ func (md *MidiDevice) applyConfiguration(config config.DeviceConfig) { md.holdDelta = time.Duration(float64(time.Second) * config.HoldDelta) md.clickBuffer = make(map[uint8]*KeyContext) md.stop = make(chan bool, 1) + md.namespace = config.Namespace } func (md *MidiDevice) updateConfiguration(config config.DeviceConfig) { diff --git a/pkg/model/commands.go b/pkg/model/commands.go index d7e478c..cb683d3 100644 --- a/pkg/model/commands.go +++ b/pkg/model/commands.go @@ -3,17 +3,11 @@ package model type MidiCommand interface { Code() string Description() string - GetNamespace() string } type TurnLightOnCommand struct { KeyCode int `hubman:"key_code"` DeviceAlias string `hubman:"device_alias"` - Namespace string `hubman:"namespace"` -} - -func (c TurnLightOnCommand) GetNamespace() string { - return c.Namespace } func (c TurnLightOnCommand) Code() string { @@ -27,11 +21,6 @@ func (c TurnLightOnCommand) Description() string { type TurnLightOffCommand struct { KeyCode int `hubman:"key_code"` DeviceAlias string `hubman:"device_alias"` - Namespace string `hubman:"namespace"` -} - -func (c TurnLightOffCommand) GetNamespace() string { - return c.Namespace } func (c TurnLightOffCommand) Code() string { @@ -45,11 +34,6 @@ func (c TurnLightOffCommand) Description() string { type SingleBlinkCommand struct { KeyCode int `hubman:"key_code"` DeviceAlias string `hubman:"device_alias"` - Namespace string `hubman:"namespace"` -} - -func (c SingleBlinkCommand) GetNamespace() string { - return c.Namespace } func (c SingleBlinkCommand) Code() string { @@ -63,11 +47,6 @@ func (c SingleBlinkCommand) Description() string { type SingleReversedBlinkCommand struct { KeyCode int `hubman:"key_code"` DeviceAlias string `hubman:"device_alias"` - Namespace string `hubman:"namespace"` -} - -func (c SingleReversedBlinkCommand) GetNamespace() string { - return c.Namespace } func (c SingleReversedBlinkCommand) Code() string { @@ -81,11 +60,6 @@ func (c SingleReversedBlinkCommand) Description() string { type ContinuousBlinkCommand struct { KeyCode int `hubman:"key_code"` DeviceAlias string `hubman:"device_alias"` - Namespace string `hubman:"namespace"` -} - -func (c ContinuousBlinkCommand) GetNamespace() string { - return c.Namespace } func (c ContinuousBlinkCommand) Code() string { @@ -98,6 +72,7 @@ func (c ContinuousBlinkCommand) Description() string { type SetActiveNamespaceCommand struct { Namespace string `hubman:"namespace"` + Device string `hubman:"device"` } func (s SetActiveNamespaceCommand) Code() string { @@ -105,5 +80,5 @@ func (s SetActiveNamespaceCommand) Code() string { } func (s SetActiveNamespaceCommand) Description() string { - return `Sets given namespace as active, all signals will be received from it and commands are executed only if them belong to it` + return `Sets given namespace as active on given device, all signals will be received from will contain active namespace attribute` } -- GitLab