add OS2L device creation

This commit is contained in:
2024-12-23 17:22:37 +01:00
parent 7cf222c4f9
commit 037735fb85
14 changed files with 476 additions and 292 deletions

213
app.go
View File

@@ -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()
}

View File

@@ -41,12 +41,12 @@
</script>
<div style="background-color: {$colors.second};">
<RoundIconButton on:click="{() => handleNavigation("settings")}" icon="bx-cog" width="2.5em" tooltip={$_("settingsMenuTooltip")} active={menuStates.settings}></RoundIconButton>
<RoundIconButton on:click="{() => handleNavigation("devices")}" icon="bx-video-plus" width="2.5em" tooltip={$_("devicesMenuTooltip")} active={menuStates.devices}></RoundIconButton>
<RoundIconButton on:click="{() => handleNavigation("preparation")}" icon="bx-layer" width="2.5em" tooltip="{$_("preparationMenuTooltip")}" active={menuStates.preparation}></RoundIconButton>
<RoundIconButton on:click="{() => handleNavigation("animation")}" icon="bx-film" width="2.5em" tooltip="{$_("animationMenuTooltip")}" active={menuStates.animation}></RoundIconButton>
<RoundIconButton on:click="{() => handleNavigation("show")}" icon="bxs-grid" width="2.5em" tooltip="{$_("showMenuTooltip")}" active={menuStates.show}></RoundIconButton>
<RoundIconButton on:click="{() => handleNavigation("console")}" icon="bx-slider" width="2.5em" tooltip="{$_("consoleMenuTooltip")}" active={menuStates.console}></RoundIconButton>
<RoundIconButton on:mousedown="{() => handleNavigation("settings")}" icon="bx-cog" width="2.5em" tooltip={$_("settingsMenuTooltip")} active={menuStates.settings}></RoundIconButton>
<RoundIconButton on:mousedown="{() => handleNavigation("devices")}" icon="bx-video-plus" width="2.5em" tooltip={$_("devicesMenuTooltip")} active={menuStates.devices}></RoundIconButton>
<RoundIconButton on:mousedown="{() => handleNavigation("preparation")}" icon="bx-layer" width="2.5em" tooltip="{$_("preparationMenuTooltip")}" active={menuStates.preparation}></RoundIconButton>
<RoundIconButton on:mousedown="{() => handleNavigation("animation")}" icon="bx-film" width="2.5em" tooltip="{$_("animationMenuTooltip")}" active={menuStates.animation}></RoundIconButton>
<RoundIconButton on:mousedown="{() => handleNavigation("show")}" icon="bxs-grid" width="2.5em" tooltip="{$_("showMenuTooltip")}" active={menuStates.show}></RoundIconButton>
<RoundIconButton on:mousedown="{() => handleNavigation("console")}" icon="bx-slider" width="2.5em" tooltip="{$_("consoleMenuTooltip")}" active={menuStates.console}></RoundIconButton>
<Toggle icon="bx-shape-square" width="2.5em" height="1.3em" tooltip="{$_("stageRenderingToggleTooltip")}"></Toggle>
<Toggle icon="bx-play" width="2.5em" height="1.3em" tooltip="{$_("showActivationToggleTooltip")}"></Toggle>
</div>

View File

@@ -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)
})
}
</script>
<div class="hardware">
@@ -103,9 +118,7 @@
{/if}
{/each}
<p style="color: var(--first-color);"><i class='bx bxs-network-chart' ></i> Others</p>
<DeviceCard disconnected on:click={() => console.log("Edit the OS2L hardware")} title="OS2L device" type="OS2L" line1="Add to configure" addable on:add={() => console.log("Add an OS2L device")}/>
<DeviceCard disconnected on:click={() => console.log("Edit the OSC hardware")} title="OSC device" type="OSC" line1="Add to configure" addable on:add={() => console.log("Add an OSC device")}/>
<DeviceCard disconnected on:click={() => console.log("Edit the ArtNet hardware")} title="ArtNet device" type="ArtNet" line1="Add to configure" addable on:add={() => console.log("Add an ArtNet device")}/>
<RoundedButton on:click={createOS2L} text="Add an OS2L peripheral" icon="bx-plus-circle" tooltip="Configure an OS2L connection"/>
</div>
</div>

View File

@@ -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"
}

View File

@@ -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
}

View File

@@ -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),

View File

@@ -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
}

View File

@@ -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,
}
}

60
hardware/OS2LDriver.go Normal file
View File

@@ -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
}

View File

@@ -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",
}
}

View File

@@ -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
}
}()

View File

@@ -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
}

77
peripherals.go Normal file
View File

@@ -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()
}

161
project.go Normal file
View File

@@ -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
}