Compare commits

..

3 Commits

17 changed files with 388 additions and 146 deletions

View File

@@ -9,17 +9,17 @@
import Show from './components/Show/Show.svelte';
import GeneralConsole from './components/Console/GeneralConsole.svelte';
import RoundIconButton from './components/General/RoundIconButton.svelte';
import { showInformation, needProjectSave, peripherals } from './stores';
import { generateToast, showInformation, needProjectSave, peripherals } from './stores';
import { SaveProject } from '../wailsjs/go/main/App.js';
import { construct_svelte_component } from 'svelte/internal';
import { EventsOn } from '../wailsjs/runtime'
import { CreateProject, GetPeripherals } from "../wailsjs/go/main/App";
import { WindowSetTitle } from "../wailsjs/runtime/runtime"
import { get } from "svelte/store"
import ToastNotification from './components/General/ToastNotification.svelte';
// Handle the event when a new peripheral is detected
EventsOn('PERIPHERAL_ARRIVAL', function(peripheralInfo){
console.log("Hardware has been added to the system");
// When a new peripheral is detected, add it to the map and:
// - Pass the isDetected key to true
// - Set the isSaved key to the last value
@@ -31,6 +31,8 @@
peripherals[peripheralInfo.SerialNumber] = peripheralInfo
return {...peripherals}
})
console.log("Hardware has been added to the system");
generateToast('info', 'bxs-hdd', 'Your <b>' + peripheralInfo.Name + '</b> device has been detected')
})
// Handle the event when a peripheral is removed from the system
@@ -49,6 +51,7 @@
storedPeripherals[peripheralInfo.SerialNumber].isDetected = false
return {...storedPeripherals}
})
generateToast('warning', 'bxs-hdd', 'Your <b>' + peripheralInfo.Name + '</b> device has been removed')
})
// Set the window title
@@ -67,8 +70,10 @@
SaveProject().then((filePath) => {
needProjectSave.set(false)
console.log("Project has been saved to " + filePath)
generateToast('info', 'bxs-save', 'The project has been saved')
}).catch((error) => {
console.error(`Unable to save the project: ${error}`)
generateToast('danger', 'bx-error', 'Unable to save the project: ' + error)
})
}
@@ -79,8 +84,9 @@
})
// Request the list of peripherals
// TODO: Handle the error here
GetPeripherals()
GetPeripherals().catch((error) => {
generateToast('danger', 'bx-error', 'Unable to get the list of peripherals: ' + error)
})
// Handle window shortcuts
document.addEventListener('keydown', function(event) {
@@ -116,6 +122,7 @@
{:else if selectedMenu === "console"}
<GeneralConsole />
{/if}
<ToastNotification/>
</main>
<style>

View File

@@ -0,0 +1,94 @@
<script lang=ts>
import { time } from 'svelte-i18n';
import {messages, colors} from '../../stores.js';
let timers = {};
function removeToast(id) {
// Supprime un toast par son ID
$messages = $messages.filter(message => message.id !== id);
clearTimeout(timers[id]);
}
$: {
// Ajoute un timer pour supprimer automatiquement les toasts
for (const message of $messages) {
if (!timers[message.id]) {
timers[message.id] = setTimeout(() => removeToast(message.id), 5000);
}
}
}
</script>
<div class="notificationsPanel">
{#each $messages as message}
<div class="toastMessage" style="background-color: {$colors.second}; color: {$colors.white}" on:mouseup={() => removeToast(message.id)}>
<div class="toastIndicator" style="background-color:{(message.type == 'danger') ? $colors.nok : (message.type == 'warning') ? $colors.orange : $colors.third};"></div>
<p><i class='bx {message.icon}'></i> {@html message.text}</p>
</div>
{/each}
</div>
<style>
p{
margin: 0.5em;
}
.toastIndicator{
width: 10px;
border-top-left-radius: 0.5em;
border-bottom-left-radius: 0.5em;
}
.toastMessage:hover {
opacity: 1;
}
.toastMessage{
word-wrap: break-word;
overflow-wrap: break-word;
white-space: normal;
z-index: 150;
display: flex;
opacity: 0.8;
border-radius: 0.5em;
border: 1px solid rgba(255, 255, 255, 0.3);
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1), /* Ombre principale douce */
0px 1px 3px rgba(0, 0, 0, 0.06); /* Ombre plus subtile */
animation: fade-in 0.3s ease-out, fade-out 0.3s ease-in 4.7s;
}
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 0.9;
transform: translateY(0);
}
}
@keyframes fade-out {
from {
opacity: 0.9;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(20px);
}
}
.notificationsPanel{
max-width: 50em;
align-items: flex-end;
position: fixed;
bottom: 1rem;
right: 1rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
z-index: 1000;
}
</style>

View File

@@ -2,7 +2,7 @@
import DeviceCard from "./DeviceCard.svelte";
import Tab from "../General/Tab.svelte";
import { _ } from 'svelte-i18n'
import { needProjectSave, peripherals } from "../../stores";
import { generateToast, needProjectSave, peripherals } from "../../stores";
import { get } from "svelte/store"
import { AddOS2LPeripheral, RemovePeripheral, ConnectFTDI, ActivateFTDI, DeactivateFTDI, DisconnectFTDI, SetDeviceFTDI, AddPeripheral } from "../../../wailsjs/go/main/App";
import RoundedButton from "../General/RoundedButton.svelte";
@@ -62,6 +62,7 @@
$needProjectSave = true
}).catch((error) => {
console.log("Unable to add the peripheral to the project: " + error)
generateToast('danger', 'bx-error', 'Unable to add this device to project')
})
}
@@ -85,6 +86,7 @@
$needProjectSave = true
}).catch((error) => {
console.log("Unable to remove the peripheral from the project: " + error)
generateToast('danger', 'bx-error', 'Unable to remove this device from project')
})
}
@@ -97,8 +99,11 @@
currentPeriph[os2lDevice.SerialNumber] = os2lDevice
return {...currentPeriph}
})
$needProjectSave = true
generateToast('info', 'bx-signal-5', 'Your OS2L peripheral has been created')
}).catch(error => {
console.log("Unable to add the OS2L peripheral: " + error)
generateToast('danger', 'bx-error', 'Unable to create the OS2L peripheral')
})
}
</script>
@@ -114,7 +119,7 @@
if(!peripheral.isSaved)
addPeripheral(peripheral)
}}
title={peripheral.Name} type={peripheral.ProtocolName} location={peripheral.Location ? peripheral.Location : ""} line1={peripheral.SerialNumber ? "S/N: " + peripheral.SerialNumber : ""} addable={!peripheral.isSaved}/>
title={peripheral.Name} type={peripheral.ProtocolName} location={peripheral.Location ? peripheral.Location : ""} line1={"S/N: " + peripheral.SerialNumber} addable={!peripheral.isSaved}/>
{/if}
{/each}
<p style="color: var(--first-color);"><i class='bx bxs-network-chart' ></i> Others</p>

View File

@@ -1,5 +1,5 @@
<script lang=ts>
import { projectsList, showInformation, needProjectSave, peripherals } from '../../stores.js';
import { generateToast, projectsList, showInformation, needProjectSave, peripherals } from '../../stores.js';
import RoundedButton from "../General/RoundedButton.svelte";
import ProjectPropertiesContent from "./ProjectPropertiesContent.svelte";
import DropdownList from "../General/DropdownList.svelte";
@@ -23,6 +23,7 @@
$projectsList = projects
}).catch((error) => {
console.error(`Unable to get the projects list: ${error}`)
generateToast('danger', 'bx-error', 'Unable to get the projects list')
})
}
@@ -51,8 +52,10 @@
return {...storedPeripherals}
})
needProjectSave.set(false)
generateToast('info', 'bx-folder-open', 'The project <b>' + projectInfo.ShowInfo.Name + '</b> was opened')
}).catch((error) => {
console.error(`Unable to open the project: ${error}`)
generateToast('danger', 'bx-error', 'Unable to open the project')
})
}
@@ -61,6 +64,7 @@
CreateProject().then((showInfo) => {
$showInformation = showInfo
$needProjectSave = true
generateToast('info', 'bxs-folder-plus', 'The project was created')
})
}
</script>
@@ -68,7 +72,6 @@
<!-- Project buttons -->
<RoundedButton on:click={initializeNewProject} text={$_("newProjectString")} icon='bxs-plus-square' tooltip={$_("newProjectTooltip")}/>
<DropdownList icon='bxs-folder-open' text={$_("openProjectString")} choices={choices} tooltip={$_("openProjectTooltip")} on:click={loadProjectsList} on:selected={openSelectedProject}/>
<!-- Project tabcontrol -->
<Tab { tabs } maxHeight='73vh'/>

View File

@@ -7,6 +7,16 @@ export let needProjectSave = writable(true)
// Show settings
export let showInformation = writable({})
// Toasts notifications
export let messages = writable([])
export function generateToast(type, icon, text){
messages.update((value) => {
value.push( { id: Date.now(), type: type, icon: icon, text: text } )
return value.slice(-5)
})
}
// Application colors
export const colors = writable({
first: "#1B262C",

1
go.mod
View File

@@ -7,6 +7,7 @@ toolchain go1.21.3
require (
github.com/lxn/win v0.0.0-20210218163916-a377121e959e
github.com/mattrtaylor/go-rtmidi v0.0.0-20220428034745-af795b1c1a79
github.com/rs/zerolog v1.33.0
github.com/wailsapp/wails/v2 v2.9.1
golang.org/x/sys v0.20.0
gopkg.in/yaml.v2 v2.4.0

6
go.sum
View File

@@ -1,10 +1,12 @@
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
@@ -48,6 +50,9 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -85,6 +90,7 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=

View File

@@ -11,6 +11,8 @@ import (
"strconv"
"strings"
"time"
"github.com/rs/zerolog/log"
)
const (
@@ -24,6 +26,7 @@ type FTDIDriver struct {
// NewFTDIDriver creates a new FTDI finder
func NewFTDIDriver() *FTDIDriver {
log.Trace().Str("file", "FTDIDriver").Msg("FTDI driver created")
return &FTDIDriver{
peripherals: make(map[string]Peripheral),
}
@@ -32,9 +35,10 @@ func NewFTDIDriver() *FTDIDriver {
// Initialize initializes the FTDI driver
func (d *FTDIDriver) Initialize() error {
if goRuntime.GOOS != "windows" {
log.Error().Str("file", "FTDIDriver").Str("platform", goRuntime.GOOS).Msg("FTDI driver not compatible with your platform")
return fmt.Errorf("<!> The FTDI driver is not compatible with your platform yet (%s)", goRuntime.GOOS)
}
fmt.Println("FTDI driver initialized")
log.Trace().Str("file", "FTDIDriver").Msg("FTDI driver initialized")
return nil
}
@@ -48,11 +52,10 @@ func (d *FTDIDriver) GetPeripheral(peripheralID string) (Peripheral, bool) {
// Return the specified peripheral
peripheral := d.peripherals[peripheralID]
if peripheral == nil {
fmt.Println("Unable to get the peripheral with the driver")
log.Error().Str("file", "FTDIDriver").Str("peripheralID", peripheralID).Msg("unable to get this peripheral from the FTDI driver")
return nil, false
}
fmt.Println("Peripheral found by the FTDI driver")
log.Debug().Str("file", "FTDIDriver").Str("peripheralID", peripheralID).Msg("peripheral found by the FTDI driver")
return peripheral, true
}
@@ -61,6 +64,7 @@ var findFTDI []byte
// Scan scans the FTDI peripherals
func (d *FTDIDriver) Scan(ctx context.Context) error {
log.Trace().Str("file", "FTDIDriver").Msg("FTDI scan triggered")
time.Sleep(scanDelay)
// Create a temporary file
@@ -69,61 +73,68 @@ func (d *FTDIDriver) Scan(ctx context.Context) error {
return err
}
defer os.Remove(tempFile.Name())
log.Trace().Str("file", "FTDIDriver").Msg("has created the FIND executable temp")
// Write the embedded executable to the temp file
if _, err := tempFile.Write(findFTDI); err != nil {
return err
}
tempFile.Close()
log.Trace().Str("file", "FTDIDriver").Msg("has written the FIND executable")
ftdiPeripherals := make(map[string]Peripheral)
finder := exec.Command(tempFile.Name())
log.Trace().Str("file", "FTDIDriver").Msg("has executed the FIND executable")
stdout, err := finder.StdoutPipe()
if err != nil {
return fmt.Errorf("Unable to create the stdout pipe: %s", err)
return fmt.Errorf("unable to create the stdout pipe: %s", err)
}
stderr, err := finder.StderrPipe()
if err != nil {
return fmt.Errorf("Unable to create the stderr pipe: %s", err)
return fmt.Errorf("unable to create the stderr pipe: %s", err)
}
err = finder.Start()
if err != nil {
return fmt.Errorf("Unable to find FTDI devices: %s", err)
return fmt.Errorf("unable to find FTDI devices: %s", err)
}
scannerErr := bufio.NewScanner(stderr)
for scannerErr.Scan() {
return fmt.Errorf("Unable to find FTDI devices: %s", scannerErr.Text())
return fmt.Errorf("unable to find FTDI devices: %s", scannerErr.Text())
}
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
deviceString := scanner.Text()
peripheralString := scanner.Text()
// The program output is like '0:1:2' where 0 is the location, 1 is the S/N and 2 is the name
deviceInfo := strings.Split(deviceString, ":")
peripheralInfo := strings.Split(peripheralString, ":")
fmt.Println("FTDI device: " + deviceString)
log.Debug().Str("file", "FTDIDriver").Str("peripheralName", peripheralInfo[2]).Str("peripheralSN", peripheralInfo[1]).Msg("new FTDI peripheral detected")
// Convert the location to an integer
location, err := strconv.Atoi(deviceInfo[0])
location, err := strconv.Atoi(peripheralInfo[0])
if err != nil {
log.Warn().Str("file", "FTDIDriver").Str("peripheralName", peripheralInfo[2]).Msg("no location provided for this FTDI peripheral")
location = -1
}
// Add the peripheral to the temporary list
peripheral, err := NewFTDIPeripheral(deviceInfo[2], deviceInfo[1], location)
peripheral, err := NewFTDIPeripheral(peripheralInfo[2], peripheralInfo[1], location)
if err != nil {
return fmt.Errorf("Unable to create the FTDI peripheral: %v", err)
}
ftdiPeripherals[deviceInfo[1]] = peripheral
ftdiPeripherals[peripheralInfo[1]] = peripheral
log.Trace().Str("file", "FTDIDriver").Str("peripheralName", peripheralInfo[2]).Msg("successfully added the FTDI peripheral to the driver")
}
// Compare with the current peripherals to detect arrivals/removals
removedList, addedList := comparePeripherals(d.peripherals, ftdiPeripherals)
// Emit the events
emitPeripheralsEvents(ctx, removedList, PeripheralRemoval)
log.Info().Str("file", "FTDIDriver").Msg("FTDI remove list emitted to the front")
emitPeripheralsEvents(ctx, addedList, PeripheralArrival)
log.Info().Str("file", "FTDIDriver").Msg("FTDI add list emitted to the front")
// Store the new peripherals list
d.peripherals = ftdiPeripherals
return nil

View File

@@ -5,7 +5,9 @@ import (
_ "embed"
"fmt"
"io"
"log"
"github.com/rs/zerolog/log"
"os"
"os/exec"
"sync"
@@ -38,16 +40,19 @@ type FTDIPeripheral struct {
// NewFTDIPeripheral creates a new FTDI peripheral
func NewFTDIPeripheral(name string, serialNumber string, location int) (*FTDIPeripheral, error) {
log.Info().Str("file", "FTDIPeripheral").Str("name", name).Str("s/n", serialNumber).Int("location", location).Msg("FTDI peripheral created")
// Create a temporary file
tempFile, err := os.CreateTemp("", "dmxSender*.exe")
if err != nil {
return nil, err
}
log.Trace().Str("file", "FTDIPeripheral").Str("s/n", serialNumber).Msg("FTDI sender temp created")
// Write the embedded executable to the temp file
if _, err := tempFile.Write(dmxSender); err != nil {
return nil, err
}
log.Trace().Str("file", "FTDIPeripheral").Str("s/n", serialNumber).Msg("FTDI sender written")
tempFile.Close()
@@ -65,35 +70,40 @@ func NewFTDIPeripheral(name string, serialNumber string, location int) (*FTDIPer
// Connect connects the FTDI peripheral
func (p *FTDIPeripheral) Connect() error {
// Connect if no connection is already running
fmt.Println(p.dmxSender)
log.Trace().Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("connecting FTDI peripheral...")
if p.dmxSender == nil {
log.Debug().Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("no instance of dmxSender for this FTDI")
// Executing the command
p.dmxSender = exec.Command(p.programName, fmt.Sprintf("%d", p.location))
log.Trace().Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("no instance of dmxSender for this FTDI")
var err error
p.stdout, err = p.dmxSender.StdoutPipe()
if err != nil {
log.Fatalf("Unable to create the stdout pipe: %v", err)
log.Err(err).Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("unable to create the stdout pipe")
return fmt.Errorf("unable to create the stdout pipe: %v", err)
}
p.stdin, err = p.dmxSender.StdinPipe()
if err != nil {
log.Fatalf("Unable to create the stdin pipe: %v", err)
log.Err(err).Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("unable to create the stdin pipe")
return fmt.Errorf("unable to create the stdin pipe: %v", err)
}
p.stderr, err = p.dmxSender.StderrPipe()
if err != nil {
log.Fatalf("Unable to create the stderr pipe: %v", err)
log.Err(err).Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("unable to create the stderr pipe")
return fmt.Errorf("unable to create the stderr pipe: %v", err)
}
go func() {
scanner := bufio.NewScanner(p.stderr)
for scanner.Scan() {
// Traitez chaque ligne lue depuis stderr
fmt.Printf("Erreur : %s\n", scanner.Text())
log.Err(fmt.Errorf(scanner.Text())).Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("error detected in dmx sender")
}
if err := scanner.Err(); err != nil {
log.Printf("Error reading from stderr: %v", err)
log.Err(err).Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("error reading from stderr")
}
}()
@@ -102,17 +112,12 @@ func (p *FTDIPeripheral) Connect() error {
defer p.wg.Done()
err = p.dmxSender.Run()
if err != nil {
// Affiche l'erreur (cela inclut également les erreurs de retour du programme)
fmt.Printf("Erreur lors de l'exécution : %v\n", err)
// Vérifie si l'erreur est liée au code de retour
log.Err(err).Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("error while execution of dmw sender")
if exitError, ok := err.(*exec.ExitError); ok {
// Récupère et affiche le code de retour
fmt.Printf("Le programme s'est terminé avec le code : %d\n", exitError.ExitCode())
fmt.Println(p.stderr)
log.Warn().Str("file", "FTDIPeripheral").Int("exitCode", exitError.ExitCode()).Str("s/n", p.serialNumber).Msg("dmx sender exited with code")
}
} else {
fmt.Println("Le programme s'est terminé avec succès.")
log.Debug().Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("dmx sender exited successfully")
}
}()
}
@@ -121,74 +126,72 @@ func (p *FTDIPeripheral) Connect() error {
// Disconnect disconnects the FTDI peripheral
func (p *FTDIPeripheral) Disconnect() error {
log.Trace().Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("disconnecting FTDI peripheral...")
if p.dmxSender != nil {
log.Debug().Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("dmxsender is defined for this FTDI")
_, err := io.WriteString(p.stdin, string([]byte{0x04, 0x00, 0x00, 0x00}))
if err != nil {
return fmt.Errorf("Unable to disconnect: %v", err)
log.Err(err).Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("unable to write command to sender")
return fmt.Errorf("unable to disconnect: %v", err)
}
p.stdin.Close()
p.stdout.Close()
p.dmxSender = nil
err = os.Remove(p.programName)
if err != nil {
return fmt.Errorf("Unable to delete the temporary file: %v", err)
log.Warn().Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("unable to delete the dmx sender temporary file")
return fmt.Errorf("unable to delete the temporary file: %v", err)
}
return nil
}
return fmt.Errorf("Unable to disconnect: not connected")
// if p.dmxSender != nil {
// err := p.dmxSender.Process.Kill()
// if err != nil {
// return fmt.Errorf("Unable to disconnect: %v", err)
// }
// p.stdin.Close()
// p.stdout.Close()
// p.dmxSender = nil
// err = os.Remove(p.programName)
// if err != nil {
// return fmt.Errorf("Unable to delete the temporary file: %v", err)
// }
// return nil
// }
// return fmt.Errorf("Unable to disconnect: not connected")
log.Warn().Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("error while disconnecting: not connected")
return fmt.Errorf("unable to disconnect: not connected")
}
// Activate activates the FTDI peripheral
func (p *FTDIPeripheral) Activate() error {
if p.dmxSender != nil {
log.Debug().Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("dmxsender is defined for this FTDI")
_, err := io.WriteString(p.stdin, string([]byte{0x01, 0x00, 0x00, 0x00}))
if err != nil {
return fmt.Errorf("Unable to activate: %v", err)
log.Err(err).Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("unable to write command to sender")
return fmt.Errorf("unable to activate: %v", err)
}
return nil
}
return fmt.Errorf("Unable to activate: not connected")
log.Warn().Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("error while activating: not connected")
return fmt.Errorf("unable to activate: not connected")
}
// Deactivate deactivates the FTDI peripheral
func (p *FTDIPeripheral) Deactivate() error {
if p.dmxSender != nil {
log.Debug().Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("dmxsender is defined for this FTDI")
_, err := io.WriteString(p.stdin, string([]byte{0x02, 0x00, 0x00, 0x00}))
if err != nil {
return fmt.Errorf("Unable to deactivate: %v", err)
log.Err(err).Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("unable to write command to sender")
return fmt.Errorf("unable to deactivate: %v", err)
}
return nil
}
return fmt.Errorf("Unable to deactivate: not connected")
log.Warn().Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("error while deactivating: not connected")
return fmt.Errorf("unable to deactivate: not connected")
}
// SetDeviceProperty sends a command to the specified device
func (p *FTDIPeripheral) SetDeviceProperty(uint32, channelNumber uint32, channelValue byte) error {
if p.dmxSender != nil {
fmt.Println(channelValue)
log.Debug().Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("dmxsender is defined for this FTDI")
commandString := []byte{0x03, 0x01, 0x00, 0xff, 0x03, 0x02, 0x00, channelValue}
_, err := io.WriteString(p.stdin, string(commandString))
if err != nil {
return fmt.Errorf("Unable to set device property: %v", err)
log.Err(err).Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("unable to write command to sender")
return fmt.Errorf("unable to set device property: %v", err)
}
return nil
}
return fmt.Errorf("Unable to set device property: not connected")
log.Warn().Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("error while setting device property: not connected")
return fmt.Errorf("unable to set device property: not connected")
}
// GetInfo gets all the peripheral information

View File

@@ -8,27 +8,9 @@ import (
"strings"
"github.com/mattrtaylor/go-rtmidi"
"github.com/rs/zerolog/log"
)
/*
DMXConnect
DMXUSBProtocol.go
Copyright (c) Valentin Boulanger
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.txt
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// MIDIDriver represents how the protocol is defined
type MIDIDriver struct {
peripherals map[string]Peripheral // The list of peripherals
@@ -36,6 +18,7 @@ type MIDIDriver struct {
// NewMIDIDriver creates a new DMXUSB protocol
func NewMIDIDriver() *MIDIDriver {
log.Trace().Str("file", "MIDIDriver").Msg("MIDI driver created")
return &MIDIDriver{
peripherals: make(map[string]Peripheral),
}
@@ -43,7 +26,7 @@ func NewMIDIDriver() *MIDIDriver {
// Initialize initializes the MIDI driver
func (d *MIDIDriver) Initialize() error {
fmt.Println("MIDI driver initialized")
log.Trace().Str("file", "MIDIDriver").Msg("MIDI driver initialized")
return nil
}
@@ -55,10 +38,12 @@ func (d *MIDIDriver) GetName() string {
// GetPeripheral gets the peripheral that correspond to the specified ID
func (d *MIDIDriver) GetPeripheral(peripheralID string) (Peripheral, bool) {
// Return the specified peripheral
peripheral := d.peripherals[peripheralID]
if peripheral == nil {
peripheral, found := d.peripherals[peripheralID]
if !found {
log.Error().Str("file", "MIDIDriver").Str("peripheralID", peripheralID).Msg("unable to get this peripheral in the MIDI driver")
return nil, false
}
log.Trace().Str("file", "MIDIDriver").Str("peripheralID", peripheralID).Msg("MIDI peripheral found in the driver")
return peripheral, true
}
@@ -86,36 +71,46 @@ func splitStringAndNumber(input string) (string, int, error) {
// Scan scans the interfaces compatible with the MIDI protocol
func (d *MIDIDriver) Scan(ctx context.Context) error {
midiPeripherals := make(map[string]Peripheral)
fmt.Println("Opening MIDI scanner port...")
log.Trace().Str("file", "MIDIDriver").Msg("opening MIDI scanner port...")
midiScanner, err := rtmidi.NewMIDIInDefault()
if err != nil {
return fmt.Errorf("Error when opening the MIDI scanner: %s", err)
log.Err(err).Str("file", "MIDIDriver").Msg("unable to open the MIDI scanner port...")
return fmt.Errorf("unable to open the MIDI scanner: %s", err)
}
defer midiScanner.Close()
fmt.Println("Scanning MIDI devices...")
midiScanner.SetCallback(func(m rtmidi.MIDIIn, b []byte, f float64) {
})
log.Trace().Str("file", "MIDIDriver").Msg("scanning MIDI peripherals...")
devicesCount, err := midiScanner.PortCount()
if err != nil {
return fmt.Errorf("Error when scanning MIDI devices: %s", err)
log.Err(err).Str("file", "MIDIDriver").Msg("unable to scan MIDI peripherals...")
return fmt.Errorf("unable to scan MIDI peripherals: %s", err)
}
for i := 0; i < devicesCount; i++ {
portName, err := midiScanner.PortName(i)
if err != nil {
log.Warn().Str("file", "MIDIPeripheral").Msg("found peripheral without a correct name, set it to unknown")
portName = "Unknown device 0"
}
// Separate data
name, location, err := splitStringAndNumber(portName)
if err != nil {
return fmt.Errorf("Unable to get information from the string: %s", err)
log.Err(err).Str("file", "MIDIDriver").Str("description", portName).Msg("invalid peripheral description")
return fmt.Errorf("invalid pripheral description: %s", err)
}
fmt.Printf("New MIDI device found: %s on %i\n", name, location)
log.Info().Str("file", "MIDIDriver").Str("name", name).Int("location", location).Msg("MIDI peripheral found")
// Add the peripheral to the temporary list
midiPeripherals[portName] = NewMIDIPeripheral(name, location)
sn := strings.ToLower(strings.Replace(name, " ", "_", -1))
midiPeripherals[sn] = NewMIDIPeripheral(name, location, sn)
}
// Compare with the current peripherals to detect arrivals/removals
removedList, addedList := comparePeripherals(d.peripherals, midiPeripherals)
// Emit the events
emitPeripheralsEvents(ctx, removedList, PeripheralRemoval)
log.Info().Str("file", "MIDIDriver").Msg("MIDI remove list emitted to the front")
emitPeripheralsEvents(ctx, addedList, PeripheralArrival)
log.Info().Str("file", "MIDIDriver").Msg("MIDI add list emitted to the front")
// Store the new peripherals list
d.peripherals = midiPeripherals
return nil

View File

@@ -1,16 +1,21 @@
package hardware
import "github.com/rs/zerolog/log"
// 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
serialNumber string // The S/N of the peripheral
}
// NewMIDIPeripheral creates a new MIDI peripheral
func NewMIDIPeripheral(name string, location int) *MIDIPeripheral {
func NewMIDIPeripheral(name string, location int, serialNumber string) *MIDIPeripheral {
log.Trace().Str("file", "MIDIPeripheral").Str("name", name).Str("s/n", serialNumber).Int("location", location).Msg("MIDI peripheral created")
return &MIDIPeripheral{
name: name,
location: location,
name: name,
location: location,
serialNumber: serialNumber,
}
}
@@ -44,5 +49,6 @@ func (p *MIDIPeripheral) GetInfo() PeripheralInfo {
return PeripheralInfo{
Name: p.name,
ProtocolName: "MIDI",
SerialNumber: p.serialNumber,
}
}

View File

@@ -4,6 +4,9 @@ import (
"context"
"fmt"
"math/rand"
"strings"
"github.com/rs/zerolog/log"
)
// OS2LDriver represents how the protocol is defined
@@ -13,6 +16,7 @@ type OS2LDriver struct {
// NewOS2LDriver creates a new OS2L driver
func NewOS2LDriver() *OS2LDriver {
log.Trace().Str("file", "OS2LDriver").Msg("OS2L driver created")
return &OS2LDriver{
peripherals: make(map[string]Peripheral),
}
@@ -20,22 +24,25 @@ func NewOS2LDriver() *OS2LDriver {
// Initialize initializes the MIDI driver
func (d *OS2LDriver) Initialize() error {
fmt.Println("OS2L driver initialized")
log.Trace().Str("file", "OS2LDriver").Msg("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))
randomSerialNumber := strings.ToUpper(fmt.Sprintf("%08x", rand.Intn(1<<32)))
log.Trace().Str("file", "OS2LDriver").Str("serialNumber", randomSerialNumber).Msg("OS2L peripheral created")
peripheral := NewOS2LPeripheral("OS2L", randomSerialNumber)
d.peripherals[randomSerialNumber] = peripheral
log.Info().Str("file", "OS2LDriver").Str("serialNumber", randomSerialNumber).Msg("OS2L peripheral created and registered")
return peripheral, nil
}
// RemovePeripheral removes an OS2L dev
func (d *OS2LDriver) RemovePeripheral(serialNumber string) error {
delete(d.peripherals, serialNumber)
log.Info().Str("file", "OS2LDriver").Str("serialNumber", serialNumber).Msg("OS2L peripheral removed")
return nil
}
@@ -47,10 +54,12 @@ func (d *OS2LDriver) GetName() string {
// 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 {
peripheral, found := d.peripherals[peripheralID]
if !found {
log.Error().Str("file", "OS2LDriver").Str("peripheralID", peripheralID).Msg("unable to get this peripheral in the OS2L driver")
return nil, false
}
log.Trace().Str("file", "OS2LDriver").Str("peripheralID", peripheralID).Msg("OS2L peripheral found in the driver")
return peripheral, true
}

View File

@@ -1,6 +1,8 @@
package hardware
import "fmt"
import (
"github.com/rs/zerolog/log"
)
// OS2LPeripheral contains the data of an OS2L peripheral
type OS2LPeripheral struct {
@@ -10,6 +12,7 @@ type OS2LPeripheral struct {
// NewOS2LPeripheral creates a new OS2L peripheral
func NewOS2LPeripheral(name string, serialNumber string) *OS2LPeripheral {
log.Trace().Str("file", "OS2LPeripheral").Str("name", name).Str("s/n", serialNumber).Msg("OS2L peripheral created")
return &OS2LPeripheral{
name: name,
serialNumber: serialNumber,
@@ -18,25 +21,25 @@ func NewOS2LPeripheral(name string, serialNumber string) *OS2LPeripheral {
// Connect connects the MIDI peripheral
func (p *OS2LPeripheral) Connect() error {
fmt.Println("OS2L peripheral connected")
log.Info().Str("file", "OS2LPeripheral").Msg("OS2L peripheral connected")
return nil
}
// Disconnect disconnects the MIDI peripheral
func (p *OS2LPeripheral) Disconnect() error {
fmt.Println("OS2L peripheral disconnected")
log.Info().Str("file", "OS2LPeripheral").Msg("OS2L peripheral disconnected")
return nil
}
// Activate activates the MIDI peripheral
func (p *OS2LPeripheral) Activate() error {
fmt.Println("OS2L peripheral activated")
log.Info().Str("file", "OS2LPeripheral").Msg("OS2L peripheral activated")
return nil
}
// Deactivate deactivates the MIDI peripheral
func (p *OS2LPeripheral) Deactivate() error {
fmt.Println("OS2L peripheral deactivated")
log.Info().Str("file", "OS2LPeripheral").Msg("OS2L peripheral deactivated")
return nil
}

View File

@@ -7,6 +7,8 @@ import (
"time"
"unsafe"
"github.com/rs/zerolog/log"
"github.com/lxn/win"
"github.com/wailsapp/wails/v2/pkg/runtime"
"golang.org/x/sys/windows"
@@ -37,6 +39,7 @@ type HardwareManager struct {
// NewHardwareManager creates a new HardwareManager
func NewHardwareManager() *HardwareManager {
log.Trace().Str("package", "hardware").Msg("Hardware instance created")
return &HardwareManager{
drivers: make(map[string]PeripheralDriver),
peripherals: make([]Peripheral, 0),
@@ -44,13 +47,17 @@ func NewHardwareManager() *HardwareManager {
}
}
// Start starts to finding new device events
// Start starts to find new peripheral events
func (h *HardwareManager) Start(ctx context.Context) error {
cb := windows.NewCallback(h.wndProc)
log.Trace().Str("file", "hardware").Msg("wndProc callback set")
inst := win.GetModuleHandle(nil)
cn, err := syscall.UTF16PtrFromString("DMXConnect device watcher")
log.Trace().Str("file", "hardware").Msg("got windows API instance")
cn, err := syscall.UTF16PtrFromString("DMXConnect peripheral watcher")
if err != nil {
log.Err(err).Str("file", "hardware").Msg("failed to convert window class name to UTF16")
return fmt.Errorf("failed to convert window class name to UTF16: %w", err)
}
wc := win.WNDCLASSEX{
@@ -58,12 +65,18 @@ func (h *HardwareManager) Start(ctx context.Context) error {
LpfnWndProc: cb,
LpszClassName: cn,
}
log.Trace().Str("file", "hardware").Msg("windows API class created")
wc.CbSize = uint32(unsafe.Sizeof(wc))
if win.RegisterClassEx(&wc) == 0 {
log.Err(syscall.GetLastError()).Str("file", "hardware").Msg("failed to register window class")
return fmt.Errorf("failed to register window class: %w", syscall.GetLastError())
}
log.Trace().Str("file", "hardware").Msg("window class registered")
wName, err := syscall.UTF16PtrFromString("usbevent.exe")
if err != nil {
log.Err(err).Str("file", "hardware").Msg("failed to convert window class name to UTF16")
return fmt.Errorf("failed to convert window name to UTF16: %w", err)
}
wdw := win.CreateWindowEx(
@@ -80,14 +93,18 @@ func (h *HardwareManager) Start(ctx context.Context) error {
wc.HInstance,
nil)
if wdw == 0 {
log.Err(syscall.GetLastError()).Str("file", "hardware").Msg("failed to create window")
return fmt.Errorf("failed to create window: %w", syscall.GetLastError())
}
log.Trace().Str("file", "hardware").Msg("window created successfully")
_ = win.ShowWindow(wdw, win.SW_HIDE)
win.UpdateWindow(wdw)
log.Trace().Str("file", "hardware").Msg("window shown and updated")
// To continuously get the devices events from Windows
go func() {
defer log.Debug().Str("file", "hardware").Msg("peripheral watcher goroutine exited")
for {
select {
case <-ctx.Done():
@@ -105,13 +122,17 @@ func (h *HardwareManager) Start(ctx context.Context) error {
// To handle the peripheral changed
go func() {
defer log.Debug().Str("file", "hardware").Msg("peripheral getter goroutine exited")
for {
select {
case <-ctx.Done():
return
case <-h.deviceChangedEvent:
fmt.Println("This is the list of devices")
h.Scan(ctx)
log.Debug().Str("file", "hardware").Msg("peripheral change event, triggering scan...")
err := h.Scan(ctx)
if err != nil {
log.Err(err).Str("file", "hardware").Msg("unable to scan peripherals")
}
}
}
}()
@@ -122,26 +143,29 @@ func (h *HardwareManager) Start(ctx context.Context) error {
func (h *HardwareManager) GetDriver(driverName string) (PeripheralDriver, error) {
driver, exists := h.drivers[driverName]
if !exists {
log.Error().Str("file", "hardware").Str("driverName", driverName).Msg("unable to get the driver")
return nil, fmt.Errorf("Unable to locate the '%s' driver", driverName)
}
log.Debug().Str("file", "hardware").Str("driverName", driverName).Msg("got driver")
return driver, nil
}
// 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())
log.Info().Str("file", "hardware").Str("driverName", driver.GetName()).Msg("driver registered")
}
// 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]
parentDriver, found := h.drivers[driverName]
// If no driver found, return false
if parentDriver == nil {
fmt.Println("Unable to get the driver")
if !found {
log.Error().Str("file", "hardware").Str("driverName", driverName).Msg("unable to get the driver")
return nil, false
}
log.Trace().Str("file", "hardware").Str("driverName", parentDriver.GetName()).Msg("driver got")
// Contact the driver to get the device
return parentDriver.GetPeripheral(peripheralID)
}
@@ -149,14 +173,15 @@ func (h *HardwareManager) GetPeripheral(driverName string, peripheralID string)
// Scan scans all the peripherals for the registered finders
func (h *HardwareManager) Scan(ctx context.Context) error {
if len(h.drivers) == 0 {
return fmt.Errorf("No peripherals driver registered")
log.Warn().Str("file", "hardware").Msg("no driver registered")
return fmt.Errorf("no driver registered")
}
for _, driver := range h.drivers {
finder := driver
driverCopy := driver
go func() {
err := driver.Scan(ctx)
err := driverCopy.Scan(ctx)
if err != nil {
fmt.Printf("Unable to scan peripherals with the %s driver: %s\n", finder.GetName(), err)
log.Err(err).Str("file", "hardware").Str("driverName", driverCopy.GetName()).Msg("unable to scan peripheral")
return
}
}()
@@ -170,9 +195,10 @@ func (h *HardwareManager) wndProc(hwnd windows.HWND, msg uint32, wParam, lParam
// Trigger the devices scan when the last DEVICE_CHANGE event is received
if debounceTimer != nil {
debounceTimer.Stop()
log.Trace().Str("file", "hardware").Msg("scan debounce timer stopped")
}
debounceTimer = time.AfterFunc(debounceDuration, func() {
fmt.Printf("Devices list has changed, refresh the devices list\n")
log.Debug().Str("file", "hardware").Msg("peripheral changed")
h.deviceChangedEvent <- struct{}{}
})
}
@@ -183,6 +209,7 @@ func (h *HardwareManager) wndProc(hwnd windows.HWND, msg uint32, wParam, lParam
func emitPeripheralsEvents(ctx context.Context, peripheralsList map[string]Peripheral, peripheralEvent PeripheralEvent) {
for _, peripheral := range peripheralsList {
runtime.EventsEmit(ctx, string(peripheralEvent), peripheral.GetInfo())
log.Trace().Str("file", "hardware").Str("event", string(peripheralEvent)).Msg("emit peripheral event")
}
}
@@ -195,9 +222,11 @@ func comparePeripherals(oldPeripherals map[string]Peripheral, newPeripherals map
for key, value := range oldPeripherals {
oldList[key] = value
}
log.Trace().Str("file", "hardware").Any("oldList", oldList).Msg("peripheral oldList comparison")
for key, value := range newPeripherals {
newList[key] = value
}
log.Trace().Str("file", "hardware").Any("newList", newList).Msg("peripheral newList comparison")
// Remove in these lists all the commons peripherals
for key := range newList {
if _, exists := oldList[key]; exists {
@@ -206,7 +235,7 @@ func comparePeripherals(oldPeripherals map[string]Peripheral, newPeripherals map
}
}
// Now the old list contains the removed peripherals, and the new list contains the added peripherals
fmt.Printf("%s\n", oldList)
fmt.Printf("%s\n", newList)
log.Trace().Str("file", "hardware").Any("oldList", oldList).Msg("peripheral oldList computed")
log.Trace().Str("file", "hardware").Any("newList", newList).Msg("peripheral newList computed")
return oldList, newList
}

21
main.go
View File

@@ -2,8 +2,14 @@ package main
import (
"embed"
"time"
"os"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/logger"
"github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
)
@@ -12,6 +18,16 @@ import (
var assets embed.FS
func main() {
// Configure the logger
log.Logger = log.Output(zerolog.ConsoleWriter{
Out: os.Stderr,
TimeFormat: "2006-01-02 15:04:05",
})
zerolog.TimestampFunc = func() time.Time {
return time.Now().Local()
}
zerolog.SetGlobalLevel(zerolog.TraceLevel)
// Create an instance of the app structure
app := NewApp()
@@ -28,9 +44,10 @@ func main() {
Bind: []interface{}{
app,
},
LogLevel: logger.ERROR,
})
if err != nil {
println("Error:", err.Error())
log.Err(err).Str("file", "main").Msg("unable to start the application")
}
}
}

View File

@@ -3,10 +3,13 @@ package main
import (
"changeme/hardware"
"fmt"
"github.com/rs/zerolog/log"
)
// GetPeripherals gets all the peripherals connected
func (a *App) GetPeripherals() error {
log.Debug().Str("file", "peripherals").Msg("getting peripherals...")
return a.hardwareManager.Scan(a.ctx)
}
@@ -15,10 +18,13 @@ 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)
log.Error().Str("file", "peripheral").Str("protocolName", protocolName).Str("periphID", peripheralID).Msg("unable to found the specified peripheral")
return fmt.Errorf("unable to found the peripheral ID '%s'", peripheralID)
}
// Add the peripheral ID to the project
a.projectInfo.PeripheralsInfo[peripheralID] = p.GetInfo()
log.Info().Str("file", "peripheral").Str("protocolName", protocolName).Str("periphID", peripheralID).Msg("peripheral added to project")
// TODO: Connect the peripheral
return nil
}
@@ -28,6 +34,7 @@ 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)
log.Info().Str("file", "peripheral").Str("protocolName", protocolName).Str("periphID", peripheralID).Msg("peripheral removed from project")
return nil
}
@@ -36,14 +43,20 @@ func (a *App) AddOS2LPeripheral() (hardware.PeripheralInfo, error) {
// Get the OS2L driver
os2lDriver, err := a.hardwareManager.GetDriver("OS2L")
if err != nil {
log.Err(err).Str("file", "peripheral").Msg("unable to found the OS2L driver")
return hardware.PeripheralInfo{}, err
}
log.Trace().Str("file", "peripheral").Msg("OS2L driver got")
// Create a new OS2L peripheral with this driver
os2lPeripheral, err := os2lDriver.CreatePeripheral(a.ctx)
if err != nil {
return hardware.PeripheralInfo{}, nil
log.Err(err).Str("file", "peripheral").Msg("unable to create the OS2L peripheral")
return hardware.PeripheralInfo{}, err
}
os2lInfo := os2lPeripheral.GetInfo()
log.Info().Str("file", "peripheral").Str("s/n", os2lInfo.SerialNumber).Msg("OS2L peripheral created, adding to project")
// Add this new peripheral to the project
return os2lInfo, a.AddPeripheral(os2lDriver.GetName(), os2lInfo.SerialNumber)
}

View File

@@ -3,7 +3,9 @@ package main
import (
"changeme/hardware"
"fmt"
"log"
"github.com/rs/zerolog/log"
"os"
"path/filepath"
"time"
@@ -24,31 +26,42 @@ func (a *App) GetProjects() ([]ProjectMetaData, error) {
f, err := os.Open(projectsDirectory)
if err != nil {
log.Fatalf("Unable to open the projects directory: %v", err)
return nil, err
log.Err(err).Str("file", "project").Msg("unable to open the projects directory")
return nil, fmt.Errorf("unable to open the projects directory: %v", err)
}
log.Trace().Str("file", "project").Str("projectsDirectory", projectsDirectory).Msg("projects directory opened")
files, err := f.Readdir(0)
if err != nil {
log.Fatalf("Unable to read the projects directory: %v", err)
return nil, err
log.Err(err).Str("file", "project").Msg("unable to read the projects directory")
return nil, fmt.Errorf("unable to read the projects directory: %v", err)
}
log.Trace().Str("file", "project").Any("projectsFiles", files).Msg("project files got")
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(),
})
}
if err != nil {
log.Warn().Str("file", "project").Str("projectFile", fileInfo.Name()).Msg("unable to open the project file")
continue
}
log.Trace().Str("file", "project").Str("projectFile", fileInfo.Name()).Any("fileData", fileData).Msg("project file read")
projectObject := ProjectInfo{}
err = yaml.Unmarshal(fileData, &projectObject)
if err != nil {
log.Warn().Str("file", "project").Str("projectFile", fileInfo.Name()).Msg("project has invalid format")
continue
}
log.Trace().Str("file", "project").Str("projectFile", fileInfo.Name()).Msg("project file unmarshalled")
// Add the SaveFile property
projects = append(projects, ProjectMetaData{
Name: projectObject.ShowInfo.Name,
Save: fileInfo.Name(),
})
}
log.Info().Str("file", "project").Any("projectsList", projects).Msg("got the projects list")
return projects, nil
}
@@ -62,6 +75,7 @@ func (a *App) CreateProject() ShowInfo {
Avatar: "appicon.png",
Comments: "Write your comments here",
}
log.Info().Str("file", "project").Any("showInfo", a.projectInfo.ShowInfo).Msg("project has been created")
return a.projectInfo.ShowInfo
}
@@ -69,20 +83,24 @@ func (a *App) CreateProject() ShowInfo {
func (a *App) GetProjectInfo(projectFile string) (ProjectInfo, error) {
// Open the project file
projectPath := filepath.Join(projectsDirectory, projectFile)
log.Trace().Str("file", "project").Str("projectPath", projectPath).Msg("project path is created")
content, err := os.ReadFile(projectPath)
if err != nil {
log.Fatalf("Unable to read the project file: %v", err)
return ProjectInfo{}, err
log.Err(err).Str("file", "project").Str("projectFile", projectFile).Msg("Unable to read the project file")
return ProjectInfo{}, fmt.Errorf("unable to read the project file: %v", err)
}
log.Trace().Str("file", "project").Str("projectPath", projectPath).Msg("project file read")
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
log.Err(err).Str("file", "project").Str("projectFile", projectFile).Msg("Unable to get the project information")
return ProjectInfo{}, fmt.Errorf("unable to get the project information: %v", err)
}
log.Trace().Str("file", "project").Str("projectPath", projectPath).Msg("project information got")
// Load it into the app
a.projectSave = projectFile
// Return the show information
log.Info().Str("file", "project").Any("projectInfo", a.projectInfo).Msg("got the project information")
return a.projectInfo, nil
}
@@ -99,44 +117,56 @@ func (a *App) ChooseAvatarPath() (string, error) {
},
})
if err != nil {
log.Err(err).Str("file", "project").Msg("unable to open the avatar dialog")
return "", err
}
log.Debug().Str("file", "project").Msg("avatar dialog is opened")
// Copy the avatar to the application avatars path
avatarPath := filepath.Join(avatarsDirectory, filepath.Base(filePath))
log.Trace().Str("file", "project").Str("avatarPath", avatarPath).Msg("avatar path is created")
_, err = copy(filePath, avatarPath)
if err != nil {
log.Err(err).Str("file", "project").Str("avatarsDirectory", avatarsDirectory).Str("fileBase", filepath.Base(filePath)).Msg("unable to copy the avatar file")
return "", err
}
log.Info().Str("file", "project").Str("avatarFileName", filepath.Base(filePath)).Msg("got the new avatar file")
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
log.Info().Str("file", "project").Any("showInfo", showInfo).Msg("show information was updated")
}
// 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)
log.Debug().Str("file", "project").Str("newProjectSave", a.projectSave).Msg("projectSave is null, getting a new one")
}
data, err := yaml.Marshal(a.projectInfo)
if err != nil {
log.Err(err).Str("file", "project").Any("projectInfo", a.projectInfo).Msg("unable to format the project information")
return "", err
}
log.Trace().Str("file", "project").Any("projectInfo", a.projectInfo).Msg("projectInfo has been marshalled")
// Create the project directory if not exists
err = os.MkdirAll(projectsDirectory, os.ModePerm)
if err != nil {
log.Err(err).Str("file", "project").Str("projectsDirectory", projectsDirectory).Msg("unable to create the projects directory")
return "", err
}
log.Trace().Str("file", "project").Str("projectsDirectory", projectsDirectory).Msg("projects directory has been created")
err = os.WriteFile(filepath.Join(projectsDirectory, a.projectSave), data, os.ModePerm)
if err != nil {
log.Err(err).Str("file", "project").Str("projectsDirectory", projectsDirectory).Str("projectSave", a.projectSave).Msg("unable to save the project")
return "", err
}
log.Info().Str("file", "project").Str("projectFileName", a.projectSave).Msg("project has been saved")
return a.projectSave, nil
}