11-hardware-definition (#17)

- Detection of FTDI and MIDI peripherals
- Creation od OS2L peripherals
- Optimization

Reviewed-on: #17
This commit was merged in pull request #17.
This commit is contained in:
2025-01-18 14:53:29 +00:00
parent 1052dcc8d5
commit 0e3f57f5fb
45 changed files with 3952 additions and 296 deletions

172
app.go
View File

@@ -2,150 +2,78 @@ package main
import (
"context"
"dmxconnect/hardware"
"fmt"
"io"
"log"
"time"
"github.com/rs/zerolog/log"
"os"
"path/filepath"
"strings"
"github.com/wailsapp/wails/v2/pkg/runtime"
"gopkg.in/yaml.v2"
)
const (
projectsDirectory = "projects" // The directory were are stored all the projects
avatarsDirectory = "frontend/public" // The directory were are stored all the avatars
projectExtension = ".dmxproj" // The extension of a DMX Connect project
"sync"
)
// App struct
type App struct {
ctx context.Context
ctx context.Context
cancelFunc context.CancelFunc
hardwareManager *hardware.HardwareManager // For managing all the hardware
wmiMutex sync.Mutex // Avoid some WMI operations at the same time
projectInfo ProjectInfo // The project information structure
projectSave string // The file name of the project
}
// NewApp creates a new App application struct
func NewApp() *App {
return &App{}
// Create a new hadware manager
hardwareManager := hardware.NewHardwareManager()
// hardwareManager.RegisterFinder(hardware.NewMIDIFinder(5 * time.Second))
hardwareManager.RegisterFinder(hardware.NewFTDIFinder(5 * time.Second))
// hardwareManager.RegisterFinder(hardware.NewOS2LFinder())
return &App{
hardwareManager: hardwareManager,
projectSave: "",
projectInfo: ProjectInfo{
PeripheralsInfo: make(map[string]hardware.PeripheralInfo),
},
}
}
// startup is called when the app starts. The context is saved
// so we can call the runtime methods
func (a *App) startup(ctx context.Context) {
a.ctx = ctx
func (a *App) onStartup(ctx context.Context) {
a.ctx, a.cancelFunc = context.WithCancel(ctx)
err := a.hardwareManager.Start(a.ctx)
if err != nil {
log.Err(err).Str("file", "app").Msg("unable to start the hardware manager")
return
}
}
// GetProjects gets all the projects in the projects directory
func (a *App) GetProjects() ([]ProjectInfo, error) {
projects := []ProjectInfo{}
f, err := os.Open(projectsDirectory)
if err != nil {
log.Fatalf("Unable to open the projects directory: %v", err)
return nil, err
}
files, err := f.Readdir(0)
if err != nil {
log.Fatalf("Unable to read the projects directory: %v", err)
return nil, err
}
for _, fileInfo := range files {
// Open the file and get the show name
fileData, err := os.ReadFile(filepath.Join(projectsDirectory, fileInfo.Name()))
if err == nil {
projectObject := ProjectInfo{}
err = yaml.Unmarshal(fileData, &projectObject)
if err == nil {
// Add the SaveFile property
projectObject.ShowInfo.SaveFile = fileInfo.Name()
projects = append(projects, projectObject)
}
}
}
return projects, nil
// onReady is called when the DOM is ready
// We get the current peripherals connected
func (a *App) onReady(ctx context.Context) {
// log.Debug().Str("file", "peripherals").Msg("getting peripherals...")
// err := a.hardwareManager.Scan()
// if err != nil {
// log.Err(err).Str("file", "app").Msg("unable to get the peripherals")
// }
return
}
// GetProjectInfo returns the information of the saved project
func (a *App) GetProjectInfo(projectFile string) (ShowInfo, error) {
projectPath := filepath.Join(projectsDirectory, projectFile)
content, err := os.ReadFile(projectPath)
// onShutdown is called when the app is closing
// We stop all the pending processes
func (a *App) onShutdown(ctx context.Context) {
// Close the application properly
log.Trace().Str("file", "app").Msg("app is closing")
// Explicitly close the context
a.cancelFunc()
err := a.hardwareManager.Stop()
if err != nil {
log.Fatalf("Unable to read the project file: %v", err)
return ShowInfo{}, err
log.Err(err).Str("file", "app").Msg("unable to stop the hardware manager")
}
projectInfo := ProjectInfo{}
err = yaml.Unmarshal(content, &projectInfo)
if err != nil {
log.Fatalf("Unable to get the project information: %v", err)
return ShowInfo{}, err
}
projectInfo.ShowInfo.SaveFile = projectFile
return projectInfo.ShowInfo, nil
}
// ChooseAvatarPath opens a filedialog to choose the show avatar
func (a *App) ChooseAvatarPath() (string, error) {
// Open the file dialog box
filePath, err := runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{
Title: "Choose your show avatar",
Filters: []runtime.FileFilter{
{
DisplayName: "Images",
Pattern: "*.png;*.jpg;*.jpeg",
},
},
})
if err != nil {
return "", err
}
// Copy the avatar to the application avatars path
avatarPath := filepath.Join(avatarsDirectory, filepath.Base(filePath))
_, err = copy(filePath, avatarPath)
if err != nil {
return "", err
}
return filepath.Base(filePath), nil
}
// SaveProject saves the project
func (a *App) SaveProject(showInfo ShowInfo) (string, error) {
log.Printf("Saving the project %s to %s", showInfo.Name, showInfo.SaveFile)
// If there is no save file, create a new one with the show name
if showInfo.SaveFile == "" {
showInfo.SaveFile = fmt.Sprintf("%s%s", formatString(showInfo.Name), projectExtension)
}
project := ProjectInfo{}
log.Printf("The number of universes: %d", showInfo.UniversesNumber)
project.ShowInfo = showInfo
data, err := yaml.Marshal(project)
if err != nil {
return "", err
}
err = os.WriteFile(filepath.Join(projectsDirectory, showInfo.SaveFile), data, os.ModePerm)
if err != nil {
return "", err
}
return showInfo.SaveFile, nil
}
// ShowInfo defines the information of the show
type ShowInfo struct {
Name string `yaml:"name"`
Date string `yaml:"date"`
UniversesNumber int `yaml:"universesNumber"`
Avatar string `yaml:"avatar"`
Comments string `yaml:"comments"`
SaveFile string `yaml:"-"`
}
// ProjectInfo defines all the information for a lighting project
type ProjectInfo struct {
ShowInfo ShowInfo `yaml:"show"` // Show information
return
}
func formatString(input string) string {