save peripherals in project file

This commit is contained in:
2024-12-20 17:18:57 +01:00
parent 17b5d39fc4
commit 9964ccef7e
19 changed files with 316 additions and 228 deletions

3
.gitignore vendored
View File

@@ -8,4 +8,5 @@ frontend/wailsjs
*/package.json.md5
*.exe
*.o
*.rc
*.rc
frontend/public

130
app.go
View File

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

View File

@@ -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()
}
});
</script>
<header>
<NavigationBar on:navigationChanged="{onNavigationChanged}"/>
{#if $needProjectSave}
<RoundIconButton on:click={saveProject} icon="bx-save" width="2.5em" tooltip={$_("saveButtonTooltip")}></RoundIconButton>
<RoundIconButton on:mouseup={saveProject} icon="bx-save" width="2.5em" tooltip={$_("saveButtonTooltip")}></RoundIconButton>
{/if}
<Clock/>
</header>

View File

@@ -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')
}
</script>
<div style="width: {width}; height: {height};">
<p style="color: {$colors.white};">{label}</p>
<!-- Handle the textarea input -->
{#if type === 'large'}
<textarea style="background-color: {$colors.second}; color: {$colors.white};" placeholder={placeholder} value={value} on:input={handleInput}/>
<textarea style="background-color: {$colors.second}; color: {$colors.white};" placeholder={placeholder} value={value} on:dblclick={handleDblClick} on:input={handleInput} on:blur={handleBlur}/>
<!-- Handle the simple inputs -->
{:else}
<input style="background-color: {$colors.second}; color: {$colors.white};" type={type} min={min} max={max} src={src} alt={alt} value={value} placeholder={placeholder} on:input={handleInput}/>
<input style="background-color: {$colors.second}; color: {$colors.white};" type={type} min={min} max={max} src={src} alt={alt} value={value} placeholder={placeholder} on:dblclick={handleDblClick} on:input={handleInput} on:blur={handleBlur}/>
{/if}
</div>
@@ -48,14 +54,14 @@
}
input::selection {
background: #0F4C75; /* Couleur de fond de la sélection */
color: #FFFFFF; /* Couleur du texte de la sélection */
background: var(--first-color); /* Couleur de fond de la sélection */
color: var(--white-color); /* Couleur du texte de la sélection */
}
/* Pour Firefox */
input::-moz-selection {
background: #0F4C75; /* Couleur de fond de la sélection */
color: #FFFFFF; /* Couleur du texte de la sélection */
background: var(--first-color); /* Couleur de fond de la sélection */
color: var(--white-color); /* Couleur du texte de la sélection */
}
input:focus {
outline: 1px solid #BBE1FA;
@@ -69,14 +75,14 @@
}
textarea::selection {
background: #0F4C75; /* Couleur de fond de la sélection */
color: #FFFFFF; /* Couleur du texte de la sélection */
background: var(--first-color); /* Couleur de fond de la sélection */
color: var(--white-color); /* Couleur du texte de la sélection */
}
/* Pour Firefox */
textarea::-moz-selection {
background: #0F4C75; /* Couleur de fond de la sélection */
color: #FFFFFF; /* Couleur du texte de la sélection */
background: var(--first-color); /* Couleur de fond de la sélection */
color: var(--white-color); /* Couleur du texte de la sélection */
}
textarea:focus {
outline: 1px solid #BBE1FA;

View File

@@ -48,8 +48,11 @@
// Emit a click event when the button is clicked
const dispatch = createEventDispatcher();
function emitClick() {
dispatch('click');
function emitMouseDown() {
dispatch('mousedown');
}
function emitMouseUp() {
dispatch('mouseup');
}
let tooltipPosition = {top: 0, left: 0}
@@ -71,7 +74,8 @@
<div class="container">
<button bind:this={buttonRef}
style="width:{width}; height:{width}; border-radius:{width}; background-color:{background}; color:{foreground};"
on:mousedown={emitClick}
on:mousedown={emitMouseDown}
on:mouseup={emitMouseUp}
on:mouseenter={() => { toggleTooltip(true) }}
on:mouseleave={() => { toggleTooltip(false) }}>
<i class='bx {icon}' style="font-size:100%;"></i>

View File

@@ -29,10 +29,15 @@
dispatch('click');
}
function handleBlur(){
dispatch('blur')
}
</script>
<div class="container">
<button bind:this={buttonRef}
on:blur={handleBlur}
on:mousedown={emitClick}
on:mouseenter={() => { toggleTooltip(true) }}
on:mouseleave={() => { toggleTooltip(false) }}

View File

@@ -32,7 +32,6 @@
<style>
.headerContainer{
cursor: pointer;
margin:0;
border-radius: 0.5em;
}

View File

@@ -25,16 +25,17 @@
dispatch('add')
}
let showOptions = false
function click(){
showOptions = !showOptions
dispatch('click')
}
function dblclick(){
dispatch('dblclick')
}
</script>
<div class="card">
<div class="card" on:dblclick={dblclick}>
<div class="profile" on:mousedown={click} style="color: {disconnected ? $colors.first : $colors.white};">
<div>
<p>{#if disconnected}<i class='bx bx-no-signal' style="font-size:100%; color: var(--nok-color);"></i> {/if}{title}</p>
@@ -53,9 +54,6 @@
<InfoButton style="margin: 0.2em;" background={ signalized ? $colors.orange : $colors.first } icon='bx-pulse' hide={!signalizable}/>
</div>
</div>
<div class="options" style="display: {showOptions ? "block" : "none"};">
<Input label={$_("projectCommentsLabel")} type='large' width='100%'/>
</div>
</div>
@@ -83,17 +81,6 @@
.actions {
margin-left: 0.2em;
}
.options {
color: var(--first-color);
padding: 0.2em;
margin-top: 0.2em;
position: absolute;
display:block;
border-radius: 0.5em;
max-width: 50;
background-color: var(--fourth-color);
text-align: left;
}
p{
margin: 0;
}

View File

@@ -2,8 +2,8 @@
import DeviceCard from "./DeviceCard.svelte";
import Tab from "../General/Tab.svelte";
import { _ } from 'svelte-i18n'
import { peripherals } from "../../stores";
import { ConnectFTDI, ActivateFTDI, DeactivateFTDI, DisconnectFTDI, SetDeviceFTDI } from "../../../wailsjs/go/main/App";
import { availablePeripherals, needProjectSave, savedPeripherals } from "../../stores";
import { RemovePeripheral, ConnectFTDI, ActivateFTDI, DeactivateFTDI, DisconnectFTDI, SetDeviceFTDI, AddPeripheral } from "../../../wailsjs/go/main/App";
function ftdiConnect(){
ConnectFTDI().then(() =>
@@ -47,45 +47,60 @@
})
}
// Add the peripheral to the project
function addPeripheral(peripheral){
// Add the peripheral to the project (backend)
AddPeripheral(peripheral.ProtocolName, peripheral.SerialNumber).then(() => {
$needProjectSave = true
$savedPeripherals = [...$savedPeripherals, peripheral];
}).catch((error) => {
console.log("Unable to add the peripheral to the project: " + error)
})
}
// Remove the peripheral from the project
function removePeripheral(peripheral) {
// Delete the peripheral from the project (backend)
RemovePeripheral(peripheral.ProtocolName, peripheral.SerialNumber).then(() => {
$needProjectSave = true
$savedPeripherals = $savedPeripherals.filter(item => JSON.stringify(item) !== JSON.stringify(peripheral))
}).catch((error) => {
console.log("Unable to remove the peripheral from the project: " + error)
})
}
</script>
<div class="hardware">
<div style="padding: 0.5em;">
<p style="margin-bottom: 1em;">Available devices</p>
<p style="margin-bottom: 1em;">Available peripherals</p>
<div class="availableHardware">
<p style="color: var(--first-color);"><i class='bx bxs-plug'></i> Detected</p>
{#each $peripherals as peripheral}
<DeviceCard title={peripheral.Name == "" ? "Please wait..." : peripheral.Name} type={peripheral.ProtocolName} location={peripheral.Location ? peripheral.Location : ""} line1={peripheral.SerialNumber ? "S/N: " + peripheral.SerialNumber : ""} addable/>
{/each}
{#each $availablePeripherals as peripheral}
<DeviceCard on:add={() => addPeripheral(peripheral)} on:dblclick={() => addPeripheral(peripheral)}
title={peripheral.Name == "" ? "Please wait..." : peripheral.Name} type={peripheral.ProtocolName} location={peripheral.Location ? peripheral.Location : ""} line1={peripheral.SerialNumber ? "S/N: " + peripheral.SerialNumber : ""} addable={!peripheral.isAdded}/>
{/each}
<p style="color: var(--first-color);"><i class='bx bxs-network-chart' ></i> Others</p>
<DeviceCard on:click={() => console.log("Edit the OS2L hardware")} title="OS2L device" type="OS2L" line1="Add to configure" addable on:delete={() => console.log("Delete the OS2L device")}/>
<DeviceCard on:click={() => console.log("Edit the OSC hardware")} title="OSC device" type="OSC" line1="Add to configure" addable on:delete={() => console.log("Delete the OSC device")}/>
<DeviceCard on:click={() => console.log("Edit the ArtNet hardware")} title="ArtNet device" type="ArtNet" line1="Add to configure" addable on:delete={() => console.log("Delete the ArtNet device")}/>
<DeviceCard on:click={() => console.log("Edit the ArtNet hardware")} title="ArtNet device" type="ArtNet" line1="Add to configure" addable on:delete={() => console.log("Delete the ArtNet device")}/>
<DeviceCard on:click={() => console.log("Edit the ArtNet hardware")} title="ArtNet device" type="ArtNet" line1="Add to configure" addable on:delete={() => console.log("Delete the ArtNet device")}/>
<DeviceCard on:click={() => console.log("Edit the ArtNet hardware")} title="ArtNet device" type="ArtNet" line1="Add to configure" addable on:delete={() => console.log("Delete the ArtNet device")}/>
<DeviceCard on:click={() => console.log("Edit the ArtNet hardware")} title="ArtNet device" type="ArtNet" line1="Add to configure" addable on:delete={() => console.log("Delete the ArtNet device")}/>
<DeviceCard on:click={() => console.log("Edit the ArtNet hardware")} title="ArtNet device" type="ArtNet" line1="Add to configure" addable on:delete={() => console.log("Delete the ArtNet device")}/>
<DeviceCard on:click={() => console.log("Edit the ArtNet hardware")} title="ArtNet device" type="ArtNet" line1="Add to configure" addable on:delete={() => console.log("Delete the ArtNet device")}/>
<DeviceCard on:click={() => console.log("Edit the ArtNet hardware")} title="ArtNet device" type="ArtNet" line1="Add to configure" addable on:delete={() => console.log("Delete the ArtNet device")}/>
<DeviceCard on:click={() => console.log("Edit the ArtNet hardware")} title="ArtNet device" type="ArtNet" line1="Add to configure" addable on:delete={() => console.log("Delete the ArtNet device")}/>
<DeviceCard on:click={() => console.log("Edit the ArtNet hardware")} title="ArtNet device" type="ArtNet" line1="Add to configure" addable on:delete={() => console.log("Delete the ArtNet device")}/>
<DeviceCard on:click={() => console.log("Edit the ArtNet hardware")} title="ArtNet device" type="ArtNet" line1="Add to configure" addable on:delete={() => console.log("Delete the ArtNet device")}/>
<DeviceCard on:click={() => console.log("Edit the ArtNet hardware")} title="ArtNet device" type="ArtNet" line1="Add to configure" addable on:delete={() => console.log("Delete the ArtNet device")}/>
<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")}/>
</div>
</div>
<div style="padding: 0.5em; flex:2; width:100%;">
<p style="margin-bottom: 1em;">Project devices</p>
<p style="margin-bottom: 1em;">Project peripherals</p>
<div class="configuredHardware">
<DeviceCard disconnected on:click={() => console.log("Edit the VDJ hardware")} title="Virtual DJ" type="OS2L" location="192.168.1.53" line1="Please update your OS2L client" removable signalizable on:delete={() => console.log("Delete the Virtual DJ device")}/>
<DeviceCard disconnected title="Pioneer DDJ-FLX4" type="MIDI" location="COM14" line1="Version: 0-alpha-13.15" removable signalizable/>
<DeviceCard title="Platformia LM730" type="OSC" location="192.168.1.418" line1="Please update your OSC client" removable signalizable/>
{#if $savedPeripherals.length == 0}
<i>Add peripherals from the left panel</i>
{/if}
{#each $savedPeripherals as peripheral}
<DeviceCard on:delete={() => removePeripheral(peripheral)} on:dblclick={() => removePeripheral(peripheral)}
title={peripheral.Name == "" ? "Please wait..." : peripheral.Name} type={peripheral.ProtocolName} location={peripheral.Location ? peripheral.Location : ""} line1={peripheral.SerialNumber ? "S/N: " + peripheral.SerialNumber : ""} removable signalizable/>
{/each}
</div>
<p style="margin-bottom: 1em;">Device settings</p>
<p style="margin-bottom: 1em;">Peripheral settings</p>
<div>
<p><i>Select a device to edit its settings</i></p>
<p><i>Select a peripheral to edit its settings</i></p>
<button on:click={ftdiConnect}>Connect FTDI 0</button>
<button on:click={ftdiActivate}>Activate FTDI 0</button>
<div class="slidecontainer">

View File

@@ -1,6 +1,7 @@
<script lang=ts>
import { ChooseAvatarPath } from '../../../wailsjs/go/main/App.js';
import { currentProject } from '../../stores.js';
import { set_data_contenteditable } from 'svelte/internal';
import { ChooseAvatarPath, UpdateShowInfo } from '../../../wailsjs/go/main/App.js';
import { showInformation, needProjectSave } from '../../stores.js';
import Input from "../General/Input.svelte";
import RoundedButton from '../General/RoundedButton.svelte';
import { _ } from 'svelte-i18n'
@@ -8,41 +9,42 @@
// Choose the avatar path
function chooseAvatar(){
ChooseAvatarPath().then((avatarPath) => {
console.log(`Avatar path is ${avatarPath}`)
$currentProject.Avatar = avatarPath
$showInformation["Avatar"] = avatarPath
UpdateShowInfo($showInformation).then(()=> {
$needProjectSave = true
})
}).catch((error) => {
console.error(`An error occured: ${error}`)
})
}
function updateUniverses(event) {
currentProject.update(obj => {
return {
...obj,
UniversesNumber: parseInt(event.detail, 10) // Conversion en entier
};
});
function validate(field, value){
$showInformation[field] = value
console.log($showInformation)
UpdateShowInfo($showInformation).then(()=> {
$needProjectSave = true
})
}
</script>
<div class='flexSettings'>
<div>
<Input bind:value={$currentProject.Name} label={$_("projectShowNameLabel")} type='text'/>
<Input bind:value={$currentProject.Date} label={$_("projectShowDateLabel")} type='datetime-local'/>
<Input bind:value={$currentProject.UniversesNumber} on:input={updateUniverses} label={$_("projectUniversesLabel")} type='number' min=1 max=10/>
<Input on:blur={(event) => validate("Name", event.detail.target.value)} label={$_("projectShowNameLabel")} type='text' value={$showInformation.Name}/>
<Input on:blur={(event) => validate("Date", event.detail.target.value)} label={$_("projectShowDateLabel")} type='datetime-local' value={$showInformation.Date}/>
</div>
<div>
<Input bind:src={$currentProject.Avatar} label={$_("projectAvatarLabel")} type='image' alt={$_("projectAvatarLabel")} width='11em'/>
<RoundedButton on:click={chooseAvatar} style='display:block;' tooltip={$_("projectAvatarTooltip")} text={$_("projectLoadAvatarButton")} icon='bxs-image' active/>
</div>
<div>
<Input bind:value={$currentProject.Comments} label={$_("projectCommentsLabel")} type='large' width='100%'/>
<Input on:dblclick={chooseAvatar} label={$_("projectAvatarLabel")} type='image' alt={$_("projectAvatarLabel")} width='11em' src={$showInformation.Avatar}/>
</div>
</div>
<div>
<Input on:blur={(event) => validate("Comments", event.detail.target.value)} label={$_("projectCommentsLabel")} type='large' width='100%' value={$showInformation.Comments}/>
</div>
<style>
.flexSettings{
display: flex;
flex-direction: column;
flex-wrap: wrap;
width: 100%;
justify-content: space-between;
}
</style>

View File

@@ -1,11 +1,11 @@
<script lang=ts>
import { projectsList, currentProject, needProjectSave } from '../../stores.js';
import { projectsList, showInformation, needProjectSave, savedPeripherals } from '../../stores.js';
import RoundedButton from "../General/RoundedButton.svelte";
import ProjectPropertiesContent from "./ProjectPropertiesContent.svelte";
import DropdownList from "../General/DropdownList.svelte";
import InputsOutputsContent from "./InputsOutputsContent.svelte";
import Tab from "../General/Tab.svelte";
import { GetProjects, GetProjectInfo } from "../../../wailsjs/go/main/App";
import { CreateProject, GetProjects, GetProjectInfo } from "../../../wailsjs/go/main/App";
import { _ } from 'svelte-i18n'
import {colors} from '../../stores.js';
@@ -14,14 +14,12 @@
{ title: $_("projectInputOutputTab"), icon: 'bxs-plug', tooltip: $_("projectInputOutputTooltip"), component: InputsOutputsContent },
];
// Refresh the projects list
let choices = new Map()
function loadProjectsList(){
GetProjects().then((projects) => {
choices = new Map(projects.map(item => [item.ShowInfo.SaveFile, item.ShowInfo.Name]));
$projectsList = projects
console.log(projectsList)
choices = new Map(projects.map(item => [item.Save, item.Name]));
$projectsList = projects
}).catch((error) => {
console.error(`Unable to get the projects list: ${error}`)
})
@@ -31,8 +29,10 @@
let selectedOption = event.detail.key
// Open the selected project
GetProjectInfo(selectedOption).then((projectInfo) => {
console.log(projectInfo)
console.log("Project opened")
$currentProject = projectInfo
$showInformation = projectInfo.ShowInfo
$savedPeripherals = projectInfo.PeripheralsInfo
needProjectSave.set(false)
}).catch((error) => {
console.error(`Unable to open the project: ${error}`)
@@ -40,24 +40,12 @@
}
function initializeNewProject(){
currentProject.set({
Name: "My new show",
Date: formatDate(new Date()),
Avatar: "appicon.png",
UniversesNumber: 1,
Comments: "Write your comments here",
SaveFile: "",
});
}
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}`;
// Instanciate a new project
CreateProject().then((showInfo) => {
$showInformation = showInfo
$savedPeripherals = []
$needProjectSave = true
})
}
</script>

View File

@@ -2,7 +2,7 @@ import App from './App.svelte';
import { WindowSetTitle } from "../wailsjs/runtime/runtime"
import {currentProject, needProjectSave} from './stores.js';
import {showInformation, needProjectSave} from './stores.js';
// Load dictionaries
import { addMessages, init } from 'svelte-i18n';
@@ -24,28 +24,4 @@ const app = new App({
target: document.body,
});
// Set the initial title
WindowSetTitle("DMXConnect")
// When the current project data is modified, pass it to unsaved and change the title
let title;
currentProject.subscribe(value => {
needProjectSave.set(true)
title = value.Name
});
// If the project need to be saved, show the information in the title
needProjectSave.subscribe(value => {
if (value) {
console.log(`<!> The current project need to be save`);
WindowSetTitle("DMXConnect - " + title + " (unsaved)")
} else {
WindowSetTitle("DMXConnect - " + title)
}
})
document.addEventListener("DOMContentLoaded", function() {
});
export default app;

View File

@@ -2,10 +2,10 @@ import { writable } from 'svelte/store';
// Projects management
export let projectsList = writable([])
export let needProjectSave = writable(true)
// Show settings
export let currentProject = writable({})
export let needProjectSave = writable(false)
export let showInformation = writable({})
// Application colors
export const colors = writable({
@@ -25,4 +25,5 @@ export const secondSize = writable("14px")
export const thirdSize = writable("20px")
// List of current hardware
export let peripherals = writable([])
export let availablePeripherals = writable([])
export let savedPeripherals = writable([])

View File

@@ -40,7 +40,20 @@ func (f *FTDIFinder) Initialize() error {
// GetName returns the name of the finder
func (f *FTDIFinder) GetName() string {
return "FTDI Finder"
return "FTDI"
}
// GetPeripheral gets the peripheral that correspond to the specified ID
func (f *FTDIFinder) GetPeripheral(peripheralID string) (Peripheral, bool) {
// Return the specified peripheral
peripheral := f.peripherals[peripheralID]
if peripheral == nil {
fmt.Println("Unable to get the peripheral in the finder")
return nil, false
}
fmt.Println("Peripheral found in the finder")
return peripheral, true
}
//go:embed third-party/ftdi/detectFTDI.exe
@@ -100,7 +113,7 @@ 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)
peripheral, err := NewFTDIPeripheral(deviceInfo[2], deviceInfo[1], location, f.GetName())
if err != nil {
return fmt.Errorf("Unable to create the FTDI peripheral: %v", err)
}

View File

@@ -26,6 +26,7 @@ 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
@@ -37,7 +38,7 @@ type FTDIPeripheral struct {
}
// NewFTDIPeripheral creates a new FTDI peripheral
func NewFTDIPeripheral(name string, serialNumber string, location int) (*FTDIPeripheral, error) {
func NewFTDIPeripheral(name string, serialNumber string, location int, finderName string) (*FTDIPeripheral, error) {
// Create a temporary file
tempFile, err := os.CreateTemp("", "dmxSender*.exe")
if err != nil {
@@ -56,6 +57,7 @@ func NewFTDIPeripheral(name string, serialNumber string, location int) (*FTDIPer
programName: tempFile.Name(),
serialNumber: serialNumber,
location: location,
finderName: finderName,
universesNumber: 1,
disconnectChan: make(chan struct{}),
errorsChan: make(chan error, 1),
@@ -194,9 +196,8 @@ func (p *FTDIPeripheral) SetDeviceProperty(uint32, channelNumber uint32, channel
// GetInfo gets all the peripheral information
func (p *FTDIPeripheral) GetInfo() PeripheralInfo {
return PeripheralInfo{
Name: p.name,
SerialNumber: p.serialNumber,
ProtocolName: "FTDI",
UniversesNumber: p.universesNumber,
Name: p.name,
SerialNumber: p.serialNumber,
ProtocolName: "FTDI",
}
}

View File

@@ -52,6 +52,16 @@ func (f *MIDIFinder) GetName() string {
return "MIDI"
}
// GetPeripheral gets the peripheral that correspond to the specified ID
func (f *MIDIFinder) GetPeripheral(peripheralID string) (Peripheral, bool) {
// Return the specified peripheral
peripheral := f.peripherals[peripheralID]
if peripheral == nil {
return nil, false
}
return peripheral, true
}
func splitStringAndNumber(input string) (string, int, error) {
// Regular expression to match the text part and the number at the end
re := regexp.MustCompile(`^(.*?)(\d+)$`)
@@ -99,7 +109,7 @@ 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)
midiPeripherals[portName] = NewMIDIPeripheral(name, location, f.GetName())
}
// Compare with the current peripherals to detect arrivals/removals
removedList, addedList := comparePeripherals(f.peripherals, midiPeripherals)

View File

@@ -2,15 +2,17 @@ package hardware
// MIDIPeripheral contains the data of a MIDI peripheral
type MIDIPeripheral struct {
name string // The name of the peripheral
location int // The location of the peripheral
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) *MIDIPeripheral {
func NewMIDIPeripheral(name string, location int, finderName string) *MIDIPeripheral {
return &MIDIPeripheral{
name: name,
location: location,
name: name,
location: location,
finderName: finderName,
}
}

View File

@@ -29,16 +29,16 @@ var (
// HardwareManager is the class who manages the hardware
type HardwareManager struct {
finders []PeripheralFinder // The list of peripherals finders
peripherals []Peripheral // The current list of peripherals
deviceChangedEvent chan struct{} // The event when the devices list changed
finders map[string]PeripheralFinder // 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
}
// NewHardwareManager creates a new HardwareManager
func NewHardwareManager() *HardwareManager {
return &HardwareManager{
finders: make([]PeripheralFinder, 0),
finders: make(map[string]PeripheralFinder),
peripherals: make([]Peripheral, 0),
deviceChangedEvent: make(chan struct{}),
}
@@ -120,10 +120,25 @@ func (h *HardwareManager) Start(ctx context.Context) error {
// RegisterFinder registers a new peripherals finder
func (h *HardwareManager) RegisterFinder(finder PeripheralFinder) {
h.finders = append(h.finders, finder)
h.finders[finder.GetName()] = finder
fmt.Printf("Success registered the %s finder\n", finder.GetName())
}
// 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")
return nil, false
}
fmt.Println("Finder ok, returning the peripheral")
// Contact the finder to get the device
return parentFinder.GetPeripheral(peripheralID)
}
// Scan scans all the peripherals for the registered finders
func (h *HardwareManager) Scan(ctx context.Context) error {
if len(h.finders) == 0 {

View File

@@ -15,15 +15,16 @@ type Peripheral interface {
// PeripheralInfo represents a peripheral information
type PeripheralInfo struct {
Name string // Name of the peripheral
SerialNumber string // S/N of the peripheral
ProtocolName string // Protocol name of the peripheral
UniversesNumber int // Number of DMX universes handled by the peripheral
Name string `yaml:"name"` // Name of the peripheral
SerialNumber string `yaml:"sn"` // S/N of the peripheral
ProtocolName string `yaml:"protocol"` // Protocol name of the peripheral
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
Scan(context.Context) error // Scan for peripherals
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
}