From 037735fb85d895d03e370dcc41a76ee50c0d3d09 Mon Sep 17 00:00:00 2001 From: Valentin Boulanger Date: Mon, 23 Dec 2024 17:22:37 +0100 Subject: [PATCH] add OS2L device creation --- app.go | 213 +----------------- .../components/General/NavigationBar.svelte | 12 +- .../Settings/InputsOutputsContent.svelte | 21 +- frontend/src/lang/en.json | 4 +- hardware/{FTDIFinder.go => FTDIDriver.go} | 48 ++-- hardware/FTDIPeripheral.go | 4 +- hardware/{MIDIFinder.go => MIDIDriver.go} | 42 ++-- hardware/MIDIPeripheral.go | 4 +- hardware/OS2LDriver.go | 60 +++++ hardware/OS2LPeripheral.go | 55 +++++ hardware/hardware.go | 53 +++-- hardware/interfaces.go | 14 +- peripherals.go | 77 +++++++ project.go | 161 +++++++++++++ 14 files changed, 476 insertions(+), 292 deletions(-) rename hardware/{FTDIFinder.go => FTDIDriver.go} (69%) rename hardware/{MIDIFinder.go => MIDIDriver.go} (75%) create mode 100644 hardware/OS2LDriver.go create mode 100644 hardware/OS2LPeripheral.go create mode 100644 peripherals.go create mode 100644 project.go diff --git a/app.go b/app.go index 5886487..8532918 100644 --- a/app.go +++ b/app.go @@ -7,20 +7,8 @@ import ( "io" "log" "os" - "path/filepath" "strings" "sync" - "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 ) // App struct @@ -38,8 +26,9 @@ type App struct { func NewApp() *App { // Create a new hadware manager hardwareManager := hardware.NewHardwareManager() - hardwareManager.RegisterFinder(hardware.NewMIDIFinder()) - hardwareManager.RegisterFinder(hardware.NewFTDIFinder()) + hardwareManager.RegisterDriver(hardware.NewMIDIDriver()) + hardwareManager.RegisterDriver(hardware.NewFTDIDriver()) + hardwareManager.RegisterDriver(hardware.NewOS2LDriver()) return &App{ hardwareManager: hardwareManager, projectSave: "", @@ -60,174 +49,6 @@ 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() error { - return a.hardwareManager.Scan(a.ctx) -} - -// GetProjects gets all the projects in the projects directory -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 - projects = append(projects, ProjectMetaData{ - Name: projectObject.ShowInfo.Name, - Save: fileInfo.Name(), - }) - } - } - } - return projects, nil -} - -// GetProjectInfo returns the information of the saved project -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 ProjectInfo{}, err - } - a.projectInfo = ProjectInfo{} - err = yaml.Unmarshal(content, &a.projectInfo) - if err != nil { - log.Fatalf("Unable to get the project information: %v", err) - return ProjectInfo{}, err - } - // 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 -} - -// 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[peripheralID] = 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 - delete(a.projectInfo.PeripheralsInfo, peripheralID) - return nil -} - -// SaveProject saves the project -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 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) - } - data, err := yaml.Marshal(a.projectInfo) - if err != nil { - return "", err - } - // Create the project directory if not exists - err = os.MkdirAll(projectsDirectory, os.ModePerm) - if err != nil { - return "", err - } - 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"` - 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 - PeripheralsInfo map[string]hardware.PeripheralInfo `yaml:"peripherals"` // Peripherals information -} - func formatString(input string) string { // Convertir en minuscules lowerCaseString := strings.ToLower(input) @@ -260,31 +81,3 @@ func copy(src, dst string) (int64, error) { nBytes, err := io.Copy(destination, source) return nBytes, err } - -// FOR TESTING PURPOSE ONLY - -func (a *App) ConnectFTDI() error { - // Create a new FTDI object - var err error - a.ftdi, err = hardware.NewFTDIPeripheral("FTDI TEST INTERFACE", "A50825I", 0, "Virtual FTDI finder") - 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() -} diff --git a/frontend/src/components/General/NavigationBar.svelte b/frontend/src/components/General/NavigationBar.svelte index bd05ec7..3c6e315 100644 --- a/frontend/src/components/General/NavigationBar.svelte +++ b/frontend/src/components/General/NavigationBar.svelte @@ -41,12 +41,12 @@
- - - - - - + + + + + +
diff --git a/frontend/src/components/Settings/InputsOutputsContent.svelte b/frontend/src/components/Settings/InputsOutputsContent.svelte index e1c73e9..60792a8 100644 --- a/frontend/src/components/Settings/InputsOutputsContent.svelte +++ b/frontend/src/components/Settings/InputsOutputsContent.svelte @@ -4,7 +4,8 @@ import { _ } from 'svelte-i18n' import { needProjectSave, peripherals } from "../../stores"; import { get } from "svelte/store" - import { RemovePeripheral, ConnectFTDI, ActivateFTDI, DeactivateFTDI, DisconnectFTDI, SetDeviceFTDI, AddPeripheral } from "../../../wailsjs/go/main/App"; + import { AddOS2LPeripheral, RemovePeripheral, ConnectFTDI, ActivateFTDI, DeactivateFTDI, DisconnectFTDI, SetDeviceFTDI, AddPeripheral } from "../../../wailsjs/go/main/App"; + import RoundedButton from "../General/RoundedButton.svelte"; function ftdiConnect(){ ConnectFTDI().then(() => @@ -86,6 +87,20 @@ console.log("Unable to remove the peripheral from the project: " + error) }) } + + // Create the OS2L peripheral + function createOS2L(){ + AddOS2LPeripheral().then(os2lDevice => { + peripherals.update(currentPeriph => { + os2lDevice.isSaved = true + os2lDevice.isDetected = true + currentPeriph[os2lDevice.SerialNumber] = os2lDevice + return {...currentPeriph} + }) + }).catch(error => { + console.log("Unable to add the OS2L peripheral: " + error) + }) + }
@@ -103,9 +118,7 @@ {/if} {/each}

Others

- console.log("Edit the OS2L hardware")} title="OS2L device" type="OS2L" line1="Add to configure" addable on:add={() => console.log("Add an OS2L device")}/> - console.log("Edit the OSC hardware")} title="OSC device" type="OSC" line1="Add to configure" addable on:add={() => console.log("Add an OSC device")}/> - console.log("Edit the ArtNet hardware")} title="ArtNet device" type="ArtNet" line1="Add to configure" addable on:add={() => console.log("Add an ArtNet device")}/> +
diff --git a/frontend/src/lang/en.json b/frontend/src/lang/en.json index 9194a1f..a97cb8f 100644 --- a/frontend/src/lang/en.json +++ b/frontend/src/lang/en.json @@ -33,6 +33,6 @@ "projectHardwareShowLabel" : "My Show", "projectHardwareInputsLabel": "INPUTS", "projectHardwareOutputsLabel": "OUTPUTS", - "projectHardwareDeleteTooltip": "Delete this device", - "projectHardwareAddTooltip": "Add this device to project" + "projectHardwareDeleteTooltip": "Delete this peripheral", + "projectHardwareAddTooltip": "Add this peripheral to project" } diff --git a/hardware/FTDIFinder.go b/hardware/FTDIDriver.go similarity index 69% rename from hardware/FTDIFinder.go rename to hardware/FTDIDriver.go index 9c5f07d..c7bac99 100644 --- a/hardware/FTDIFinder.go +++ b/hardware/FTDIDriver.go @@ -17,41 +17,41 @@ const ( scanDelay = 4 * time.Second // Waiting delay before scanning the FTDI devices ) -// FTDIFinder represents how the protocol is defined -type FTDIFinder struct { +// FTDIDriver represents how the protocol is defined +type FTDIDriver struct { peripherals map[string]Peripheral } -// NewFTDIFinder creates a new FTDI finder -func NewFTDIFinder() *FTDIFinder { - return &FTDIFinder{ +// NewFTDIDriver creates a new FTDI finder +func NewFTDIDriver() *FTDIDriver { + return &FTDIDriver{ peripherals: make(map[string]Peripheral), } } -// Initialize initializes the FTDI finder -func (f *FTDIFinder) Initialize() error { +// Initialize initializes the FTDI driver +func (d *FTDIDriver) Initialize() error { if goRuntime.GOOS != "windows" { - return fmt.Errorf(" The FTDI finder is not compatible with your platform yet (%s)", goRuntime.GOOS) + return fmt.Errorf(" The FTDI driver is not compatible with your platform yet (%s)", goRuntime.GOOS) } - fmt.Println("FTDI finder initialized") + fmt.Println("FTDI driver initialized") return nil } -// GetName returns the name of the finder -func (f *FTDIFinder) GetName() string { +// GetName returns the name of the driver +func (d *FTDIDriver) GetName() string { return "FTDI" } // GetPeripheral gets the peripheral that correspond to the specified ID -func (f *FTDIFinder) GetPeripheral(peripheralID string) (Peripheral, bool) { +func (d *FTDIDriver) GetPeripheral(peripheralID string) (Peripheral, bool) { // Return the specified peripheral - peripheral := f.peripherals[peripheralID] + peripheral := d.peripherals[peripheralID] if peripheral == nil { - fmt.Println("Unable to get the peripheral in the finder") + fmt.Println("Unable to get the peripheral with the driver") return nil, false } - fmt.Println("Peripheral found in the finder") + fmt.Println("Peripheral found by the FTDI driver") return peripheral, true } @@ -60,7 +60,7 @@ func (f *FTDIFinder) GetPeripheral(peripheralID string) (Peripheral, bool) { var findFTDI []byte // Scan scans the FTDI peripherals -func (f *FTDIFinder) Scan(ctx context.Context) error { +func (d *FTDIDriver) Scan(ctx context.Context) error { time.Sleep(scanDelay) // Create a temporary file @@ -113,18 +113,28 @@ func (f *FTDIFinder) Scan(ctx context.Context) error { location = -1 } // Add the peripheral to the temporary list - peripheral, err := NewFTDIPeripheral(deviceInfo[2], deviceInfo[1], location, f.GetName()) + peripheral, err := NewFTDIPeripheral(deviceInfo[2], deviceInfo[1], location) if err != nil { return fmt.Errorf("Unable to create the FTDI peripheral: %v", err) } ftdiPeripherals[deviceInfo[1]] = peripheral } // Compare with the current peripherals to detect arrivals/removals - removedList, addedList := comparePeripherals(f.peripherals, ftdiPeripherals) + removedList, addedList := comparePeripherals(d.peripherals, ftdiPeripherals) // Emit the events emitPeripheralsEvents(ctx, removedList, PeripheralRemoval) emitPeripheralsEvents(ctx, addedList, PeripheralArrival) // Store the new peripherals list - f.peripherals = ftdiPeripherals + d.peripherals = ftdiPeripherals + return nil +} + +// CreatePeripheral is not implemented here +func (d *FTDIDriver) CreatePeripheral(context.Context) (Peripheral, error) { + return nil, nil +} + +// RemovePeripheral is not implemented here +func (d *FTDIDriver) RemovePeripheral(serialNumber string) error { return nil } diff --git a/hardware/FTDIPeripheral.go b/hardware/FTDIPeripheral.go index 9e8ba30..bb01147 100644 --- a/hardware/FTDIPeripheral.go +++ b/hardware/FTDIPeripheral.go @@ -26,7 +26,6 @@ type FTDIPeripheral struct { serialNumber string // The S/N of the FTDI peripheral location int // The location of the peripheral universesNumber int // The number of DMX universes handled by this peripheral - finderName string // The name of the parent finder programName string // The temp file name of the executable dmxSender *exec.Cmd // The command to pilot the DMX sender program stdin io.WriteCloser // For writing in the DMX sender @@ -38,7 +37,7 @@ type FTDIPeripheral struct { } // NewFTDIPeripheral creates a new FTDI peripheral -func NewFTDIPeripheral(name string, serialNumber string, location int, finderName string) (*FTDIPeripheral, error) { +func NewFTDIPeripheral(name string, serialNumber string, location int) (*FTDIPeripheral, error) { // Create a temporary file tempFile, err := os.CreateTemp("", "dmxSender*.exe") if err != nil { @@ -57,7 +56,6 @@ func NewFTDIPeripheral(name string, serialNumber string, location int, finderNam programName: tempFile.Name(), serialNumber: serialNumber, location: location, - finderName: finderName, universesNumber: 1, disconnectChan: make(chan struct{}), errorsChan: make(chan error, 1), diff --git a/hardware/MIDIFinder.go b/hardware/MIDIDriver.go similarity index 75% rename from hardware/MIDIFinder.go rename to hardware/MIDIDriver.go index a8ae141..c6c8629 100644 --- a/hardware/MIDIFinder.go +++ b/hardware/MIDIDriver.go @@ -29,33 +29,33 @@ import ( limitations under the License. */ -// MIDIFinder represents how the protocol is defined -type MIDIFinder struct { +// MIDIDriver represents how the protocol is defined +type MIDIDriver struct { peripherals map[string]Peripheral // The list of peripherals } -// NewMIDIFinder creates a new DMXUSB protocol -func NewMIDIFinder() *MIDIFinder { - return &MIDIFinder{ +// NewMIDIDriver creates a new DMXUSB protocol +func NewMIDIDriver() *MIDIDriver { + return &MIDIDriver{ peripherals: make(map[string]Peripheral), } } -// Initialize initializes the DMX-USB finder -func (f *MIDIFinder) Initialize() error { - fmt.Println("MIDI finder initialized") +// Initialize initializes the MIDI driver +func (d *MIDIDriver) Initialize() error { + fmt.Println("MIDI driver initialized") return nil } -// GetName returns the name of the finder -func (f *MIDIFinder) GetName() string { +// GetName returns the name of the driver +func (d *MIDIDriver) GetName() string { return "MIDI" } // GetPeripheral gets the peripheral that correspond to the specified ID -func (f *MIDIFinder) GetPeripheral(peripheralID string) (Peripheral, bool) { +func (d *MIDIDriver) GetPeripheral(peripheralID string) (Peripheral, bool) { // Return the specified peripheral - peripheral := f.peripherals[peripheralID] + peripheral := d.peripherals[peripheralID] if peripheral == nil { return nil, false } @@ -84,7 +84,7 @@ func splitStringAndNumber(input string) (string, int, error) { } // Scan scans the interfaces compatible with the MIDI protocol -func (f *MIDIFinder) Scan(ctx context.Context) error { +func (d *MIDIDriver) Scan(ctx context.Context) error { midiPeripherals := make(map[string]Peripheral) fmt.Println("Opening MIDI scanner port...") midiScanner, err := rtmidi.NewMIDIInDefault() @@ -109,14 +109,24 @@ func (f *MIDIFinder) Scan(ctx context.Context) error { } fmt.Printf("New MIDI device found: %s on %i\n", name, location) // Add the peripheral to the temporary list - midiPeripherals[portName] = NewMIDIPeripheral(name, location, f.GetName()) + midiPeripherals[portName] = NewMIDIPeripheral(name, location) } // Compare with the current peripherals to detect arrivals/removals - removedList, addedList := comparePeripherals(f.peripherals, midiPeripherals) + removedList, addedList := comparePeripherals(d.peripherals, midiPeripherals) // Emit the events emitPeripheralsEvents(ctx, removedList, PeripheralRemoval) emitPeripheralsEvents(ctx, addedList, PeripheralArrival) // Store the new peripherals list - f.peripherals = midiPeripherals + d.peripherals = midiPeripherals + return nil +} + +// CreatePeripheral is not implemented here +func (d *MIDIDriver) CreatePeripheral(context.Context) (Peripheral, error) { + return nil, nil +} + +// RemovePeripheral is not implemented here +func (d *MIDIDriver) RemovePeripheral(serialNumber string) error { return nil } diff --git a/hardware/MIDIPeripheral.go b/hardware/MIDIPeripheral.go index d8af465..a7dd461 100644 --- a/hardware/MIDIPeripheral.go +++ b/hardware/MIDIPeripheral.go @@ -4,15 +4,13 @@ package hardware type MIDIPeripheral struct { name string // The name of the peripheral location int // The location of the peripheral - finderName string // The name of the parent finder } // NewMIDIPeripheral creates a new MIDI peripheral -func NewMIDIPeripheral(name string, location int, finderName string) *MIDIPeripheral { +func NewMIDIPeripheral(name string, location int) *MIDIPeripheral { return &MIDIPeripheral{ name: name, location: location, - finderName: finderName, } } diff --git a/hardware/OS2LDriver.go b/hardware/OS2LDriver.go new file mode 100644 index 0000000..7ead8f7 --- /dev/null +++ b/hardware/OS2LDriver.go @@ -0,0 +1,60 @@ +package hardware + +import ( + "context" + "fmt" + "math/rand" +) + +// OS2LDriver represents how the protocol is defined +type OS2LDriver struct { + peripherals map[string]Peripheral // The list of peripherals +} + +// NewOS2LDriver creates a new OS2L driver +func NewOS2LDriver() *OS2LDriver { + return &OS2LDriver{ + peripherals: make(map[string]Peripheral), + } +} + +// Initialize initializes the MIDI driver +func (d *OS2LDriver) Initialize() error { + fmt.Println("OS2L driver initialized") + return nil +} + +// CreatePeripheral creates a new OS2L peripheral +func (d *OS2LDriver) CreatePeripheral(ctx context.Context) (Peripheral, error) { + // Create a random serial number for this peripheral + randomSerialNumber := fmt.Sprintf("%08x", rand.Intn(1<<32)) + peripheral := NewOS2LPeripheral("OS2L", randomSerialNumber) + d.peripherals[randomSerialNumber] = peripheral + return peripheral, nil +} + +// RemovePeripheral removes an OS2L dev +func (d *OS2LDriver) RemovePeripheral(serialNumber string) error { + delete(d.peripherals, serialNumber) + return nil +} + +// GetName returns the name of the driver +func (d *OS2LDriver) GetName() string { + return "OS2L" +} + +// GetPeripheral gets the peripheral that correspond to the specified ID +func (d *OS2LDriver) GetPeripheral(peripheralID string) (Peripheral, bool) { + // Return the specified peripheral + peripheral := d.peripherals[peripheralID] + if peripheral == nil { + return nil, false + } + return peripheral, true +} + +// Scan scans the interfaces compatible with the MIDI protocol +func (d *OS2LDriver) Scan(ctx context.Context) error { + return nil +} diff --git a/hardware/OS2LPeripheral.go b/hardware/OS2LPeripheral.go new file mode 100644 index 0000000..4d62703 --- /dev/null +++ b/hardware/OS2LPeripheral.go @@ -0,0 +1,55 @@ +package hardware + +import "fmt" + +// OS2LPeripheral contains the data of an OS2L peripheral +type OS2LPeripheral struct { + name string // The name of the peripheral + serialNumber string // The serial number of the peripheral +} + +// NewOS2LPeripheral creates a new OS2L peripheral +func NewOS2LPeripheral(name string, serialNumber string) *OS2LPeripheral { + return &OS2LPeripheral{ + name: name, + serialNumber: serialNumber, + } +} + +// Connect connects the MIDI peripheral +func (p *OS2LPeripheral) Connect() error { + fmt.Println("OS2L peripheral connected") + return nil +} + +// Disconnect disconnects the MIDI peripheral +func (p *OS2LPeripheral) Disconnect() error { + fmt.Println("OS2L peripheral disconnected") + return nil +} + +// Activate activates the MIDI peripheral +func (p *OS2LPeripheral) Activate() error { + fmt.Println("OS2L peripheral activated") + return nil +} + +// Deactivate deactivates the MIDI peripheral +func (p *OS2LPeripheral) Deactivate() error { + fmt.Println("OS2L peripheral deactivated") + return nil +} + +// SetDeviceProperty - not implemented for this kind of peripheral +func (p *OS2LPeripheral) SetDeviceProperty(uint32, uint32, byte) error { + return nil +} + +// GetInfo gets the peripheral information +func (p *OS2LPeripheral) GetInfo() PeripheralInfo { + return PeripheralInfo{ + Name: p.name, + SerialNumber: p.serialNumber, + ProtocolName: "OS2L", + } +} diff --git a/hardware/hardware.go b/hardware/hardware.go index 77e39e6..6c2ada3 100644 --- a/hardware/hardware.go +++ b/hardware/hardware.go @@ -29,7 +29,7 @@ var ( // HardwareManager is the class who manages the hardware type HardwareManager struct { - finders map[string]PeripheralFinder // The map of peripherals finders + drivers map[string]PeripheralDriver // The map of peripherals finders peripherals []Peripheral // The current list of peripherals deviceChangedEvent chan struct{} // The event when the devices list changed ctx context.Context @@ -38,7 +38,7 @@ type HardwareManager struct { // NewHardwareManager creates a new HardwareManager func NewHardwareManager() *HardwareManager { return &HardwareManager{ - finders: make(map[string]PeripheralFinder), + drivers: make(map[string]PeripheralDriver), peripherals: make([]Peripheral, 0), deviceChangedEvent: make(chan struct{}), } @@ -118,38 +118,45 @@ func (h *HardwareManager) Start(ctx context.Context) error { return nil } -// RegisterFinder registers a new peripherals finder -func (h *HardwareManager) RegisterFinder(finder PeripheralFinder) { - h.finders[finder.GetName()] = finder - fmt.Printf("Success registered the %s finder\n", finder.GetName()) +// GetDriver returns a register driver +func (h *HardwareManager) GetDriver(driverName string) (PeripheralDriver, error) { + driver, exists := h.drivers[driverName] + if !exists { + return nil, fmt.Errorf("Unable to locate the '%s' driver", driverName) + } + return driver, nil } -// GetPeripheral gets the peripheral object from the parent finder -func (h *HardwareManager) GetPeripheral(finderName string, peripheralID string) (Peripheral, bool) { - // Get the parent finder - parentFinder := h.finders[finderName] - // If no finder found, return false - if parentFinder == nil { - fmt.Println("Unable to get the finder") +// RegisterDriver registers a new peripherals driver +func (h *HardwareManager) RegisterDriver(driver PeripheralDriver) { + h.drivers[driver.GetName()] = driver + fmt.Printf("Success registered the %s driver\n", driver.GetName()) +} + +// GetPeripheral gets the peripheral object from the parent driver +func (h *HardwareManager) GetPeripheral(driverName string, peripheralID string) (Peripheral, bool) { + // Get the driver + parentDriver := h.drivers[driverName] + // If no driver found, return false + if parentDriver == nil { + fmt.Println("Unable to get the driver") return nil, false } - fmt.Println("Finder ok, returning the peripheral") - - // Contact the finder to get the device - return parentFinder.GetPeripheral(peripheralID) + // Contact the driver to get the device + return parentDriver.GetPeripheral(peripheralID) } // Scan scans all the peripherals for the registered finders func (h *HardwareManager) Scan(ctx context.Context) error { - if len(h.finders) == 0 { - return fmt.Errorf("No peripherals finder registered") + if len(h.drivers) == 0 { + return fmt.Errorf("No peripherals driver registered") } - for _, finder := range h.finders { - finder := finder + for _, driver := range h.drivers { + finder := driver go func() { - err := finder.Scan(ctx) + err := driver.Scan(ctx) if err != nil { - fmt.Printf("Unable to scan peripherals with the %s finder: %s\n", finder.GetName(), err) + fmt.Printf("Unable to scan peripherals with the %s driver: %s\n", finder.GetName(), err) return } }() diff --git a/hardware/interfaces.go b/hardware/interfaces.go index 363f74e..2c1fb15 100644 --- a/hardware/interfaces.go +++ b/hardware/interfaces.go @@ -21,10 +21,12 @@ type PeripheralInfo struct { Settings []interface{} `yaml:"settings"` // Number of DMX universes handled by the peripheral } -// PeripheralFinder represents how compatible peripheral finders are implemented -type PeripheralFinder interface { - Initialize() error // Initializes the protocol - GetName() string // Get the name of the finder - GetPeripheral(string) (Peripheral, bool) // Get the peripheral - Scan(context.Context) error // Scan for peripherals +// PeripheralDriver represents how compatible peripheral drivers are implemented +type PeripheralDriver interface { + Initialize() error // Initializes the protocol + GetName() string // Get the name of the finder + GetPeripheral(string) (Peripheral, bool) // Get the peripheral + Scan(context.Context) error // Scan for peripherals + CreatePeripheral(ctx context.Context) (Peripheral, error) // Creates a new peripheral + RemovePeripheral(serialNumber string) error // Removes a peripheral } diff --git a/peripherals.go b/peripherals.go new file mode 100644 index 0000000..d944aec --- /dev/null +++ b/peripherals.go @@ -0,0 +1,77 @@ +package main + +import ( + "changeme/hardware" + "fmt" +) + +// GetPeripherals gets all the peripherals connected +func (a *App) GetPeripherals() error { + return a.hardwareManager.Scan(a.ctx) +} + +// 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[peripheralID] = 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 + delete(a.projectInfo.PeripheralsInfo, peripheralID) + return nil +} + +// AddOS2LPeripheral adds a new OS2L peripheral +func (a *App) AddOS2LPeripheral() (hardware.PeripheralInfo, error) { + // Get the OS2L driver + os2lDriver, err := a.hardwareManager.GetDriver("OS2L") + if err != nil { + return hardware.PeripheralInfo{}, err + } + // Create a new OS2L peripheral with this driver + os2lPeripheral, err := os2lDriver.CreatePeripheral(a.ctx) + if err != nil { + return hardware.PeripheralInfo{}, nil + } + os2lInfo := os2lPeripheral.GetInfo() + // Add this new peripheral to the project + return os2lInfo, a.AddPeripheral(os2lDriver.GetName(), os2lInfo.SerialNumber) +} + +// FOR TESTING PURPOSE ONLY + +func (a *App) ConnectFTDI() error { + // Create a new FTDI object + var err error + a.ftdi, err = hardware.NewFTDIPeripheral("FTDI TEST INTERFACE", "A50825I", 0) + 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() +} diff --git a/project.go b/project.go new file mode 100644 index 0000000..dbcd539 --- /dev/null +++ b/project.go @@ -0,0 +1,161 @@ +package main + +import ( + "changeme/hardware" + "fmt" + "log" + "os" + "path/filepath" + "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 +) + +// GetProjects gets all the projects in the projects directory +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 + projects = append(projects, ProjectMetaData{ + Name: projectObject.ShowInfo.Name, + Save: fileInfo.Name(), + }) + } + } + } + return projects, nil +} + +// 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 +} + +// GetProjectInfo returns the information of the saved project +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 ProjectInfo{}, err + } + a.projectInfo = ProjectInfo{} + err = yaml.Unmarshal(content, &a.projectInfo) + if err != nil { + log.Fatalf("Unable to get the project information: %v", err) + return ProjectInfo{}, err + } + // 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 +} + +// UpdateShowInfo updates the show information +func (a *App) UpdateShowInfo(showInfo ShowInfo) { + fmt.Printf("%s\n", showInfo) + a.projectInfo.ShowInfo = showInfo +} + +// SaveProject saves the project +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 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) + } + data, err := yaml.Marshal(a.projectInfo) + if err != nil { + return "", err + } + // Create the project directory if not exists + err = os.MkdirAll(projectsDirectory, os.ModePerm) + if err != nil { + return "", err + } + 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"` + 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 + PeripheralsInfo map[string]hardware.PeripheralInfo `yaml:"peripherals"` // Peripherals information +}