Files
dmxconnect/app.go

291 lines
7.9 KiB
Go
Raw Normal View History

2023-08-25 20:33:11 +00:00
package main
import (
2024-12-15 13:45:46 +01:00
"changeme/hardware"
2023-08-25 20:33:11 +00:00
"context"
"fmt"
"io"
"log"
"os"
"path/filepath"
"strings"
2024-12-15 13:45:46 +01:00
"sync"
2024-12-20 17:18:57 +01:00
"time"
"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
2023-08-25 20:33:11 +00:00
)
// App struct
type App struct {
2024-12-15 13:45:46 +01:00
ctx context.Context
hardwareManager *hardware.HardwareManager // For managing all the hardware
wmiMutex sync.Mutex // Avoid some WMI operations at the same time
2024-12-20 17:18:57 +01:00
projectInfo ProjectInfo // The project information structure
projectSave string // The file name of the project
2024-12-15 13:45:46 +01:00
// FOR TESTING PURPOSE ONLY
ftdi *hardware.FTDIPeripheral
2023-08-25 20:33:11 +00:00
}
// NewApp creates a new App application struct
func NewApp() *App {
2024-12-15 13:45:46 +01:00
// Create a new hadware manager
hardwareManager := hardware.NewHardwareManager()
hardwareManager.RegisterFinder(hardware.NewMIDIFinder())
hardwareManager.RegisterFinder(hardware.NewFTDIFinder())
return &App{
hardwareManager: hardwareManager,
2024-12-20 17:18:57 +01:00
projectSave: "",
2024-12-21 13:24:00 +01:00
projectInfo: ProjectInfo{
PeripheralsInfo: make(map[string]hardware.PeripheralInfo),
},
2024-12-15 13:45:46 +01:00
}
2023-08-25 20:33:11 +00:00
}
// 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
2024-12-15 13:45:46 +01:00
err := a.hardwareManager.Start(ctx)
if err != nil {
log.Fatalf("Unable to start the device manager: %s", err)
return
}
}
2024-12-20 17:18:57 +01:00
// CreateProject creates a new blank project
func (a *App) CreateProject() ShowInfo {
date := time.Now()
a.projectSave = ""
a.projectInfo.ShowInfo = ShowInfo{
Name: "My new show",
Date: fmt.Sprintf("%04d-%02d-%02dT%02d:%02d", date.Year(), date.Month(), date.Day(), date.Hour(), date.Minute()),
Avatar: "appicon.png",
Comments: "Write your comments here",
}
return a.projectInfo.ShowInfo
}
2024-12-15 13:45:46 +01:00
// GetPeripherals gets all the peripherals connected
2024-12-20 17:18:57 +01:00
func (a *App) GetPeripherals() error {
return a.hardwareManager.Scan(a.ctx)
2023-08-25 20:33:11 +00:00
}
// GetProjects gets all the projects in the projects directory
2024-12-20 17:18:57 +01:00
func (a *App) GetProjects() ([]ProjectMetaData, error) {
projects := []ProjectMetaData{}
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
2024-12-20 17:18:57 +01:00
projects = append(projects, ProjectMetaData{
Name: projectObject.ShowInfo.Name,
Save: fileInfo.Name(),
})
}
}
}
return projects, nil
}
// GetProjectInfo returns the information of the saved project
2024-12-20 17:18:57 +01:00
func (a *App) GetProjectInfo(projectFile string) (ProjectInfo, error) {
// Open the project file
projectPath := filepath.Join(projectsDirectory, projectFile)
content, err := os.ReadFile(projectPath)
if err != nil {
log.Fatalf("Unable to read the project file: %v", err)
2024-12-20 17:18:57 +01:00
return ProjectInfo{}, err
}
2024-12-21 13:24:00 +01:00
a.projectInfo = ProjectInfo{}
2024-12-20 17:18:57 +01:00
err = yaml.Unmarshal(content, &a.projectInfo)
if err != nil {
log.Fatalf("Unable to get the project information: %v", err)
2024-12-20 17:18:57 +01:00
return ProjectInfo{}, err
}
2024-12-20 17:18:57 +01:00
// Load it into the app
a.projectSave = projectFile
// Return the show information
return a.projectInfo, 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
}
2024-12-20 17:18:57 +01:00
// UpdateShowInfo updates the show information
func (a *App) UpdateShowInfo(showInfo ShowInfo) {
fmt.Printf("%s\n", showInfo)
a.projectInfo.ShowInfo = showInfo
}
// AddPeripheral adds a peripheral to the project
func (a *App) AddPeripheral(protocolName string, peripheralID string) error {
// Get the device from its finder
p, found := a.hardwareManager.GetPeripheral(protocolName, peripheralID)
if !found {
return fmt.Errorf("Unable to localize the peripheral %s", peripheralID)
}
// Add the peripheral ID to the project
2024-12-21 13:24:00 +01:00
a.projectInfo.PeripheralsInfo[peripheralID] = p.GetInfo()
2024-12-20 17:18:57 +01:00
// TODO: Connect the peripheral
return nil
}
// RemovePeripheral adds a peripheral to the project
func (a *App) RemovePeripheral(protocolName string, peripheralID string) error {
// TODO: Disconnect the peripheral
// Remove the peripheral ID from the project
2024-12-21 13:24:00 +01:00
delete(a.projectInfo.PeripheralsInfo, peripheralID)
2024-12-20 17:18:57 +01:00
return nil
}
// SaveProject saves the project
2024-12-20 17:18:57 +01:00
func (a *App) SaveProject() (string, error) {
log.Printf("Saving the project %s to %s", a.projectInfo.ShowInfo.Name, a.projectSave)
// If there is no save file, create a new one with the show name
2024-12-20 17:18:57 +01:00
if a.projectSave == "" {
date := time.Now()
a.projectSave = fmt.Sprintf("%04d%02d%02d%02d%02d%02d%s", date.Year(), date.Month(), date.Day(), date.Hour(), date.Minute(), date.Second(), projectExtension)
}
2024-12-20 17:18:57 +01:00
data, err := yaml.Marshal(a.projectInfo)
if err != nil {
return "", err
}
2024-12-20 17:18:57 +01:00
// Create the project directory if not exists
err = os.MkdirAll(projectsDirectory, os.ModePerm)
if err != nil {
return "", err
}
2024-12-20 17:18:57 +01:00
err = os.WriteFile(filepath.Join(projectsDirectory, a.projectSave), data, os.ModePerm)
if err != nil {
return "", err
}
return a.projectSave, nil
}
// ShowInfo defines the information of the show
type ShowInfo struct {
2024-12-20 17:18:57 +01:00
Name string `yaml:"name"`
Date string `yaml:"date"`
Avatar string `yaml:"avatar"`
Comments string `yaml:"comments"`
}
// ProjectMetaData defines all the minimum information for a lighting project
type ProjectMetaData struct {
Name string // Show name
Save string // The save file of the project
}
// ProjectInfo defines all the information for a lighting project
type ProjectInfo struct {
2024-12-21 13:24:00 +01:00
ShowInfo ShowInfo `yaml:"show"` // Show information
PeripheralsInfo map[string]hardware.PeripheralInfo `yaml:"peripherals"` // Peripherals information
}
func formatString(input string) string {
// Convertir en minuscules
lowerCaseString := strings.ToLower(input)
// Remplacer les espaces par des underscores
formattedString := strings.ReplaceAll(lowerCaseString, " ", "_")
return formattedString
}
func copy(src, dst string) (int64, error) {
sourceFileStat, err := os.Stat(src)
if err != nil {
return 0, err
}
if !sourceFileStat.Mode().IsRegular() {
return 0, fmt.Errorf("%s is not a regular file", src)
}
source, err := os.Open(src)
if err != nil {
return 0, err
}
defer source.Close()
destination, err := os.Create(dst)
if err != nil {
return 0, err
}
defer destination.Close()
nBytes, err := io.Copy(destination, source)
return nBytes, err
2023-08-25 20:33:11 +00:00
}
2024-12-15 13:45:46 +01:00
// FOR TESTING PURPOSE ONLY
func (a *App) ConnectFTDI() error {
// Create a new FTDI object
var err error
2024-12-20 17:18:57 +01:00
a.ftdi, err = hardware.NewFTDIPeripheral("FTDI TEST INTERFACE", "A50825I", 0, "Virtual FTDI finder")
2024-12-15 13:45:46 +01:00
if err != nil {
return err
}
return a.ftdi.Connect()
}
func (a *App) ActivateFTDI() error {
return a.ftdi.Activate()
}
func (a *App) SetDeviceFTDI(channelValue byte) error {
return a.ftdi.SetDeviceProperty(0, 0, channelValue)
}
func (a *App) DeactivateFTDI() error {
return a.ftdi.Deactivate()
}
func (a *App) DisconnectFTDI() error {
return a.ftdi.Disconnect()
}