From 9964ccef7e5bd4092bffdc86e3892000fab9035e Mon Sep 17 00:00:00 2001 From: Valentin Boulanger Date: Fri, 20 Dec 2024 17:18:57 +0100 Subject: [PATCH] save peripherals in project file --- .gitignore | 3 +- app.go | 130 +++++++++++++----- frontend/src/App.svelte | 66 +++++---- frontend/src/components/General/Input.svelte | 32 +++-- .../components/General/RoundIconButton.svelte | 10 +- .../components/General/RoundedButton.svelte | 5 + frontend/src/components/General/Tab.svelte | 1 - .../src/components/Settings/DeviceCard.svelte | 23 +--- .../Settings/InputsOutputsContent.svelte | 69 ++++++---- .../Settings/ProjectPropertiesContent.svelte | 42 +++--- .../src/components/Settings/Settings.svelte | 38 ++--- frontend/src/main.js | 26 +--- frontend/src/stores.js | 7 +- hardware/FTDIFinder.go | 17 ++- hardware/FTDIPeripheral.go | 11 +- hardware/MIDIFinder.go | 12 +- hardware/MIDIPeripheral.go | 12 +- hardware/hardware.go | 25 +++- hardware/interfaces.go | 15 +- 19 files changed, 316 insertions(+), 228 deletions(-) diff --git a/.gitignore b/.gitignore index 408a58b..a83910e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ frontend/wailsjs */package.json.md5 *.exe *.o -*.rc \ No newline at end of file +*.rc +frontend/public \ No newline at end of file diff --git a/app.go b/app.go index e99339e..eb4cec3 100644 --- a/app.go +++ b/app.go @@ -10,6 +10,7 @@ import ( "path/filepath" "strings" "sync" + "time" "github.com/wailsapp/wails/v2/pkg/runtime" @@ -27,6 +28,8 @@ type App struct { ctx context.Context 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 // FOR TESTING PURPOSE ONLY ftdi *hardware.FTDIPeripheral } @@ -39,6 +42,7 @@ func NewApp() *App { hardwareManager.RegisterFinder(hardware.NewFTDIFinder()) return &App{ hardwareManager: hardwareManager, + projectSave: "", } } @@ -53,14 +57,27 @@ func (a *App) startup(ctx context.Context) { } } +// 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 +} + // GetPeripherals gets all the peripherals connected -func (a *App) GetPeripherals() { - a.hardwareManager.Scan(a.ctx) +func (a *App) GetPeripherals() error { + return a.hardwareManager.Scan(a.ctx) } // GetProjects gets all the projects in the projects directory -func (a *App) GetProjects() ([]ProjectInfo, error) { - projects := []ProjectInfo{} +func (a *App) GetProjects() ([]ProjectMetaData, error) { + projects := []ProjectMetaData{} f, err := os.Open(projectsDirectory) if err != nil { @@ -82,8 +99,10 @@ func (a *App) GetProjects() ([]ProjectInfo, error) { err = yaml.Unmarshal(fileData, &projectObject) if err == nil { // Add the SaveFile property - projectObject.ShowInfo.SaveFile = fileInfo.Name() - projects = append(projects, projectObject) + projects = append(projects, ProjectMetaData{ + Name: projectObject.ShowInfo.Name, + Save: fileInfo.Name(), + }) } } } @@ -91,23 +110,23 @@ func (a *App) GetProjects() ([]ProjectInfo, error) { } // GetProjectInfo returns the information of the saved project -func (a *App) GetProjectInfo(projectFile string) (ShowInfo, error) { +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) - return ShowInfo{}, err + return ProjectInfo{}, err } - - projectInfo := ProjectInfo{} - - err = yaml.Unmarshal(content, &projectInfo) + err = yaml.Unmarshal(content, &a.projectInfo) if err != nil { log.Fatalf("Unable to get the project information: %v", err) - return ShowInfo{}, err + return ProjectInfo{}, err } - projectInfo.ShowInfo.SaveFile = projectFile - return projectInfo.ShowInfo, nil + // 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 @@ -134,40 +153,85 @@ func (a *App) ChooseAvatarPath() (string, error) { return filepath.Base(filePath), nil } +// 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 + a.projectInfo.PeripheralsInfo = append(a.projectInfo.PeripheralsInfo, p.GetInfo()) + // 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 + a.projectInfo.PeripheralsInfo = removePeripheralFromList(a.projectInfo.PeripheralsInfo, peripheralID) + return 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) +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 - if showInfo.SaveFile == "" { - showInfo.SaveFile = fmt.Sprintf("%s%s", formatString(showInfo.Name), projectExtension) + 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) } - project := ProjectInfo{} - log.Printf("The number of universes: %d", showInfo.UniversesNumber) - project.ShowInfo = showInfo - data, err := yaml.Marshal(project) + data, err := yaml.Marshal(a.projectInfo) if err != nil { return "", err } - err = os.WriteFile(filepath.Join(projectsDirectory, showInfo.SaveFile), data, os.ModePerm) + // Create the project directory if not exists + err = os.MkdirAll(projectsDirectory, os.ModePerm) if err != nil { return "", err } - return showInfo.SaveFile, nil + 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 { - Name string `yaml:"name"` - Date string `yaml:"date"` - UniversesNumber int `yaml:"universesNumber"` - Avatar string `yaml:"avatar"` - Comments string `yaml:"comments"` - SaveFile string `yaml:"-"` + 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 { - ShowInfo ShowInfo `yaml:"show"` // Show information + ShowInfo ShowInfo `yaml:"show"` // Show information + PeripheralsInfo []hardware.PeripheralInfo `yaml:"peripherals"` // Peripherals information +} + +func removePeripheralFromList(slice []hardware.PeripheralInfo, idToRemove string) []hardware.PeripheralInfo { + result := []hardware.PeripheralInfo{} + for _, peripheral := range slice { + if peripheral.SerialNumber != idToRemove { + result = append(result, peripheral) + } + } + return result } func formatString(input string) string { @@ -208,7 +272,7 @@ func copy(src, dst string) (int64, error) { func (a *App) ConnectFTDI() error { // Create a new FTDI object var err error - a.ftdi, err = hardware.NewFTDIPeripheral("FTDI TEST INTERFACE", "A50825I", 0) + a.ftdi, err = hardware.NewFTDIPeripheral("FTDI TEST INTERFACE", "A50825I", 0, "Virtual FTDI finder") if err != nil { return err } diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index a554ef0..c6a5d5e 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -9,29 +9,30 @@ import Show from './components/Show/Show.svelte'; import GeneralConsole from './components/Console/GeneralConsole.svelte'; import RoundIconButton from './components/General/RoundIconButton.svelte'; - import { currentProject, needProjectSave, peripherals } from './stores'; + import { showInformation, needProjectSave, availablePeripherals, savedPeripherals } from './stores'; import { SaveProject } from '../wailsjs/go/main/App.js'; import { construct_svelte_component } from 'svelte/internal'; import { EventsOn } from '../wailsjs/runtime' - import { GetPeripherals } from "../wailsjs/go/main/App"; + import { CreateProject, GetPeripherals } from "../wailsjs/go/main/App"; + import { WindowSetTitle } from "../wailsjs/runtime/runtime" - // When the list of hardware changed, update the store + // Handle the event when a new peripheral is detected EventsOn('PERIPHERAL_ARRIVAL', function(peripheralInfo){ console.log("Hardware has been added to the system"); - console.log(peripheralInfo) - $peripherals = [...$peripherals, peripheralInfo]; + $availablePeripherals = [...$availablePeripherals, peripheralInfo]; }) + // Handle the event when a peripheral is removed from the system EventsOn('PERIPHERAL_REMOVAL', function(peripheralInfo){ console.log("Hardware has been removed from the system"); - console.log(peripheralInfo) - $peripherals = $peripherals.filter(item => JSON.stringify(item) !== JSON.stringify(peripheralInfo)) - console.log($peripherals) - - // $peripherals = devicesList - // console.log(devicesList) + $availablePeripherals = $availablePeripherals.filter(item => JSON.stringify(item) !== JSON.stringify(peripheralInfo)) }) + // Set the window title + $: { + WindowSetTitle("DMXConnect - " + $showInformation.Name + ($needProjectSave ? " (unsaved)" : "")) + } + let selectedMenu = "settings" // When the navigation menu changed, update the selected menu function onNavigationChanged(event){ @@ -40,44 +41,41 @@ // Save the project function saveProject(){ - SaveProject($currentProject).then((saveFile) => { - console.log($currentProject) - $currentProject.SaveFile = saveFile + SaveProject().then((filePath) => { needProjectSave.set(false) - console.log("Project has been saved") + console.log("Project has been saved to " + filePath) }).catch((error) => { console.error(`Unable to save the project: ${error}`) }) } - function formatDate(date) { - const pad = (number) => number.toString().padStart(2, '0'); - const year = date.getFullYear(); - const month = pad(date.getMonth() + 1); // Les mois commencent à 0 - const day = pad(date.getDate()); - const hours = pad(date.getHours()); - const minutes = pad(date.getMinutes()); - return `${year}-${month}-${day}T${hours}:${minutes}`; - } - - currentProject.set({ - Name: "My new show", - Date: formatDate(new Date()), - Avatar: "appicon.png", - UniversesNumber: 1, - Comments: "Write your comments here", - SaveFile: "", - }); + // Instanciate a new project + CreateProject().then((showInfo) => { + showInformation.set(showInfo) + $needProjectSave = true + }) // Request the list of peripherals + // TODO: Handle the error here GetPeripherals() + // Handle window shortcuts + document.addEventListener('keydown', function(event) { + // Check the CTRL+S keys + if ((event.ctrlKey || event.metaKey) && event.key === 's') { + // Avoid the natural behaviour + event.preventDefault(); + // Save the current project + saveProject() + } + }); +
{#if $needProjectSave} - + {/if}
diff --git a/frontend/src/components/General/Input.svelte b/frontend/src/components/General/Input.svelte index d695018..11119e7 100644 --- a/frontend/src/components/General/Input.svelte +++ b/frontend/src/components/General/Input.svelte @@ -15,21 +15,27 @@ const dispatch = createEventDispatcher(); - function handleInput(event){ - value = event.target.value - dispatch('input', value) + function handleInput(){ + dispatch('input') } + function handleBlur(event){ + dispatch('blur', event) + } + + function handleDblClick(){ + dispatch('dblclick') + }

{label}

{#if type === 'large'} -