@@ -125,7 +125,6 @@
.list {
z-index: 200;
padding: 0.2em;
- backdrop-filter: blur(20px);
margin-top: 0.2em;
position: absolute;
width: auto;
diff --git a/frontend/src/components/Settings/DeviceCard.svelte b/frontend/src/components/Settings/DeviceCard.svelte
index 8910208..8c72cb1 100644
--- a/frontend/src/components/Settings/DeviceCard.svelte
+++ b/frontend/src/components/Settings/DeviceCard.svelte
@@ -15,13 +15,14 @@
export let signalizable = false;
export let signalized = false;
export let selected = false;
- export let status = "disconnected";
+ export let status = "PERIPHERAL_DISCONNECTED";
// Emit a delete event when the device is being removed
const dispatch = createEventDispatcher();
function remove(event){
dispatch('delete')
}
+
function add(event){
dispatch('add')
}
@@ -37,11 +38,11 @@
-
+
-
{#if status == "disconnected" } {/if}{title}
+
{#if status == "PERIPHERAL_DISCONNECTED" } {/if}{title}
{type} {location != '' ? "- " : ""}{location}
- {#if status == "disconnected"}
+ {#if status == "PERIPHERAL_DISCONNECTED"}
Disconnected
{:else}
{line1}
@@ -50,9 +51,9 @@
-
-
-
+
+
+
diff --git a/frontend/src/components/Settings/InputsOutputsContent.svelte b/frontend/src/components/Settings/InputsOutputsContent.svelte
index 0f5b2c8..a8373a4 100644
--- a/frontend/src/components/Settings/InputsOutputsContent.svelte
+++ b/frontend/src/components/Settings/InputsOutputsContent.svelte
@@ -11,20 +11,8 @@
// Add the peripheral to the project
function addPeripheral(peripheral){
// Add the peripheral to the project (backend)
- AddPeripheral(peripheral).then((serialNumber) => {
- peripherals.update((storedPeripherals) => {
- return {
- ...storedPeripherals,
- [serialNumber]: {
- ...storedPeripherals[serialNumber],
- Name: peripheral.Name,
- ProtocolName: peripheral.ProtocolName,
- SerialNumber: serialNumber,
- isSaved: true,
- },
- }})
- $needProjectSave = true
- }).catch((error) => {
+ AddPeripheral(peripheral)
+ .catch((error) => {
console.log("Unable to add the peripheral to the project: " + error)
generateToast('danger', 'bx-error', $_("addPeripheralErrorToast"))
})
@@ -33,27 +21,8 @@
// Remove the peripheral from the project
function removePeripheral(peripheral) {
// Delete the peripheral from the project (backend)
- RemovePeripheral(peripheral.ProtocolName, peripheral.SerialNumber).then(() => {
- // If the peripheral is not detected, we can delete it form the store
- // If not, we only pass the isSaved key to false
- let peripheralsList = get(peripherals)
- let lastDetectedProperty = peripheralsList[peripheral.SerialNumber]?.isDetected
- let needToDelete = (lastDetectedProperty !== true) ? true : false
- peripherals.update((storedPeripherals) => {
- if (needToDelete){
- delete storedPeripherals[peripheral.SerialNumber];
- return { ...storedPeripherals };
- }
- storedPeripherals[peripheral.SerialNumber].isSaved = false
- return { ...storedPeripherals };
- })
- $needProjectSave = true
- // If the peripheral is currently selected, unselect it
- if (selectedPeripheralSN == peripheral.SerialNumber) {
- selectedPeripheralSN = null
- selectedPeripheralSettings = {}
- }
- }).catch((error) => {
+ RemovePeripheral(peripheral)
+ .catch((error) => {
console.log("Unable to remove the peripheral from the project: " + error)
generateToast('danger', 'bx-error', $_("removePeripheralErrorToast"))
})
@@ -76,7 +45,7 @@
}
}
- // Unselect the peripheral if it is disconnect
+ // Unselect the peripheral if it is disconnected
$: {
Object.entries($peripherals).filter(([serialNumber, peripheral]) => {
if (!peripheral.isDetected && peripheral.isSaved && selectedPeripheralSN == serialNumber) {
@@ -120,7 +89,7 @@
if(!peripheral.isSaved)
addPeripheral(peripheral)
}}
- status="connected" title={peripheral.Name} type={peripheral.ProtocolName} location={peripheral.Location ? peripheral.Location : ""} line1={"S/N: " + peripheral.SerialNumber} addable={!peripheral.isSaved}/>
+ status="PERIPHERAL_CONNECTED" title={peripheral.Name} type={peripheral.ProtocolName} location={peripheral.Location ? peripheral.Location : ""} line1={"S/N: " + peripheral.SerialNumber} addable={!peripheral.isSaved}/>
{/if}
{/each}
{$_("projectHardwareOthersLabel")}
@@ -134,7 +103,7 @@
{#if savedPeripheralNumber > 0}
{#each Object.entries($peripherals) as [serialNumber, peripheral]}
{#if peripheral.isSaved}
-
removePeripheral(peripheral)} on:dblclick={() => removePeripheral(peripheral)} on:click={() => selectPeripheral(peripheral)}
+ removePeripheral(peripheral)} on:dblclick={() => removePeripheral(peripheral)} on:click={() => selectPeripheral(peripheral)}
title={peripheral.Name} type={peripheral.ProtocolName} location={peripheral.Location ? peripheral.Location : ""} line1={peripheral.SerialNumber ? "S/N: " + peripheral.SerialNumber : ""} selected={serialNumber == selectedPeripheralSN} removable signalizable/>
{/if}
{/each}
diff --git a/frontend/src/runtime-events.js b/frontend/src/runtime-events.js
index 1902a40..59a290b 100644
--- a/frontend/src/runtime-events.js
+++ b/frontend/src/runtime-events.js
@@ -3,68 +3,91 @@ import { peripherals, generateToast, needProjectSave, showInformation } from './
import { get } from "svelte/store"
import { _ } from 'svelte-i18n'
-function addPeripheral (peripheralInfo){
- // 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
- let peripheralsList = get(peripherals)
- let lastSavedProperty = peripheralsList[peripheralInfo.SerialNumber]?.isSaved
- peripheralInfo.isDetected = true
- peripheralInfo.isSaved = (lastSavedProperty === true) ? true : false
- peripherals.update((peripherals) => {
- peripherals[peripheralInfo.SerialNumber] = peripheralInfo
- return {...peripherals}
- })
+// New peripheral has been added to the system
+function peripheralArrival (peripheralInfo){
+ // If not exists, add it to the map
+ // isDetected key to true
+
+ peripherals.update((storedPeripherals) => {
+ return {
+ ...storedPeripherals,
+ [peripheralInfo.SerialNumber]: {
+ ...storedPeripherals[peripheralInfo.SerialNumber],
+ Name: peripheralInfo.Name,
+ ProtocolName: peripheralInfo.ProtocolName,
+ SerialNumber: peripheralInfo.SerialNumber,
+ Settings: peripheralInfo.Settings,
+ isDetected: true,
+ },
+ }})
console.log("Hardware has been added to the system");
generateToast('info', 'bxs-hdd', get(_)("peripheralArrivalToast") + ' ' + peripheralInfo.Name + '')
}
-function removePeripheral (peripheralInfo){
- console.log("Hardware has been removed from the system");
- // When a peripheral is disconnected, pass its isDetected key to false
- // If the isSaved key is set to false, we can completely remove the peripheral from the list
- let peripheralsList = get(peripherals)
- let lastSavedProperty = peripheralsList[peripheralInfo.SerialNumber]?.isSaved
- let needToDelete = (lastSavedProperty !== true) ? true : false
- peripherals.update((storedPeripherals) => {
- if (needToDelete){
- delete storedPeripherals[peripheralInfo.SerialNumber];
- return { ...storedPeripherals };
- }
- storedPeripherals[peripheralInfo.SerialNumber].isDetected = false
- storedPeripherals[peripheralInfo.SerialNumber].Status = "disconnected"
- return {...storedPeripherals}
- })
- generateToast('warning', 'bxs-hdd', get(_)("peripheralRemovalToast") + ' ' + peripheralInfo.Name + '')
-}
+// Peripheral is removed from the system
+function peripheralRemoval (peripheralInfo){
+ // If not exists, add it to the map
+ // isDetected key to false
-function updatePeripheral(peripheral, status){
- console.log("Hardware status has been updated to " + status);
- // When a peripheral status is updated, update it in the store
peripherals.update((storedPeripherals) => {
return {
...storedPeripherals,
- [peripheral.SerialNumber]: {
- ...storedPeripherals[peripheral.SerialNumber],
- isSaved: true,
- Status: status,
+ [peripheralInfo.SerialNumber]: {
+ ...storedPeripherals[peripheralInfo.SerialNumber],
+ Name: peripheralInfo.Name,
+ ProtocolName: peripheralInfo.ProtocolName,
+ SerialNumber: peripheralInfo.SerialNumber,
+ Settings: peripheralInfo.Settings,
+ isDetected: false,
+ status: "PERIPHERAL_DISCONNECTED",
},
}})
+ console.log("Hardware has been removed from the system");
+ generateToast('warning', 'bxs-hdd', get(_)("peripheralRemovalToast") + ' ' + peripheralInfo.Name + '')
}
+// Update peripheral status
+function peripheralUpdateStatus(peripheralInfo, status){
+ // If not exists, add it to the map
+ // change status key
+
+ peripherals.update((storedPeripherals) => {
+ console.log(status)
+ return {
+ ...storedPeripherals,
+ [peripheralInfo.SerialNumber]: {
+ ...storedPeripherals[peripheralInfo.SerialNumber],
+ Name: peripheralInfo.Name,
+ ProtocolName: peripheralInfo.ProtocolName,
+ SerialNumber: peripheralInfo.SerialNumber,
+ Settings: peripheralInfo.Settings,
+ status: status,
+ },
+ }})
+
+ console.log("Hardware status has been updated to " + status);
+}
+
+// Load the peripheral in the project
function loadPeripheral (peripheralInfo) {
- peripherals.update((storedPeripherals) => {
- // Add the saved peripherals of the project
- // If already exists pass the isSaved key to true, if not create the peripheral and set it to disconnected
- // Add the peripheral to the list of peripherals, with the last isDetected key and the isSaved key to true
- let lastDetectedKey = storedPeripherals[peripheralInfo.SerialNumber]?.isDetected
- storedPeripherals[peripheralInfo.SerialNumber] = peripheralInfo
- storedPeripherals[peripheralInfo.SerialNumber].isDetected = (lastDetectedKey === true) ? true : false
- storedPeripherals[peripheralInfo.SerialNumber].isSaved = true
- return {...storedPeripherals}
- })
- //TODO: Lors d'un chargement/déchargement natif au démarrage, il ne doit pas y avoir de nécessité de sauvegarder
- needProjectSave.set(true)
+ // If not exists, add it to the map
+ // isSaved key to true
+
+ peripherals.update((storedPeripherals) => {
+ return {
+ ...storedPeripherals,
+ [peripheralInfo.SerialNumber]: {
+ ...storedPeripherals[peripheralInfo.SerialNumber],
+ Name: peripheralInfo.Name,
+ ProtocolName: peripheralInfo.ProtocolName,
+ SerialNumber: peripheralInfo.SerialNumber,
+ Settings: peripheralInfo.Settings,
+ isSaved: true,
+ },
+ }})
+ console.log("Hardware has been added to the project");
+ //TODO: Lors d'un chargement/déchargement natif au démarrage, il ne doit pas y avoir de nécessité de sauvegarder
+ needProjectSave.set(true)
}
function loadProject (showInfo){
@@ -75,19 +98,27 @@ function loadProject (showInfo){
generateToast('info', 'bx-folder-open', get(_)("projectOpenedToast") + ' ' + showInfo.Name + '')
}
+// Unload the hardware from the project
function unloadPeripheral (peripheralInfo) {
- peripherals.update((storedPeripherals) => {
- // Set all the isSaved keys to false and delete the disconnected peripherals
- storedPeripherals[peripheralInfo.SerialNumber].isSaved = false
- if (!storedPeripherals[peripheralInfo.SerialNumber].isDetected) {
- delete storedPeripherals[peripheralInfo.SerialNumber]
- }
- return {...storedPeripherals}
- })
+ // If not exists, add it to the map
+ // isSaved key to false
- //TODO: Lors d'un chargement/déchargement natif au démarrage, il ne doit pas y avoir de nécessité de sauvegarder
- needProjectSave.set(true)
- }
+ peripherals.update((storedPeripherals) => {
+ return {
+ ...storedPeripherals,
+ [peripheralInfo.SerialNumber]: {
+ ...storedPeripherals[peripheralInfo.SerialNumber],
+ Name: peripheralInfo.Name,
+ ProtocolName: peripheralInfo.ProtocolName,
+ SerialNumber: peripheralInfo.SerialNumber,
+ Settings: peripheralInfo.Settings,
+ isSaved: false,
+ },
+ }})
+ console.log("Hardware has been removed from the project");
+ //TODO: Lors d'un chargement/déchargement natif au démarrage, il ne doit pas y avoir de nécessité de sauvegarder
+ needProjectSave.set(true)
+}
let initialized = false
@@ -96,13 +127,13 @@ export function initRuntimeEvents(){
initialized = true
// Handle the event when a new peripheral is detected
- EventsOn('PERIPHERAL_ARRIVAL', addPeripheral)
+ EventsOn('PERIPHERAL_ARRIVAL', peripheralArrival)
// Handle the event when a peripheral is removed from the system
- EventsOn('PERIPHERAL_REMOVAL', removePeripheral)
+ EventsOn('PERIPHERAL_REMOVAL', peripheralRemoval)
// Handle the event when a peripheral status is updated
- EventsOn('PERIPHERAL_STATUS', updatePeripheral)
+ EventsOn('PERIPHERAL_STATUS', peripheralUpdateStatus)
// Handle the event when a new project need to be loaded
EventsOn('LOAD_PROJECT', loadProject)
diff --git a/frontend/src/stores.js b/frontend/src/stores.js
index 16d093e..02aeb53 100644
--- a/frontend/src/stores.js
+++ b/frontend/src/stores.js
@@ -34,4 +34,15 @@ export const secondSize = writable("14px")
export const thirdSize = writable("20px")
// List of current hardware
-export let peripherals = writable({})
\ No newline at end of file
+export let peripherals = writable({})
+
+// Peripheral structure :
+
+// 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 map[string]interface{} `yaml:"settings"` // Peripheral settings
+
+// isSaved // if the peripheral is saved in the project
+// isDetected // if the peripheral is detected by the system
+// status // the status of connection
\ No newline at end of file
diff --git a/hardware/FTDIFinder.go b/hardware/FTDIFinder.go
index df00560..2fc2e5b 100644
--- a/hardware/FTDIFinder.go
+++ b/hardware/FTDIFinder.go
@@ -2,8 +2,6 @@ package hardware
import (
"context"
- _ "embed"
- "errors"
"fmt"
goRuntime "runtime"
"sync"
@@ -24,74 +22,93 @@ import "C"
// FTDIFinder manages all the FTDI peripherals
type FTDIFinder struct {
wg sync.WaitGroup
+ mu sync.Mutex
- findTicker *time.Ticker // Peripherals find ticker
- foundPeripherals map[string]PeripheralInfo // The list of peripherals handled by this finder
- registeredPeripherals map[string]*FTDIPeripheral // The list of found peripherals
- scanChannel chan struct{} // The channel to trigger a scan event
+ saved map[string]PeripheralInfo // Peripherals saved in the project
+ detected map[string]*FTDIPeripheral // Detected peripherals
+
+ scanEvery time.Duration // Scans peripherals periodically
+
+ onArrival func(p PeripheralInfo) // When a peripheral arrives
+ onRemoval func(p PeripheralInfo) // When a peripheral goes away
}
// NewFTDIFinder creates a new FTDI finder
-func NewFTDIFinder(findPeriod time.Duration) *FTDIFinder {
+func NewFTDIFinder(scanEvery time.Duration) *FTDIFinder {
log.Trace().Str("file", "FTDIFinder").Msg("FTDI finder created")
return &FTDIFinder{
- findTicker: time.NewTicker(findPeriod),
- foundPeripherals: make(map[string]PeripheralInfo),
- registeredPeripherals: make(map[string]*FTDIPeripheral),
- scanChannel: make(chan struct{}),
+ scanEvery: scanEvery,
+ saved: make(map[string]PeripheralInfo),
+ detected: make(map[string]*FTDIPeripheral),
}
}
+// OnArrival is the callback function when a new peripheral arrives
+func (f *FTDIFinder) OnArrival(cb func(p PeripheralInfo)) {
+ f.onArrival = cb
+}
+
+// OnRemoval i the callback when a peripheral goes away
+func (f *FTDIFinder) OnRemoval(cb func(p PeripheralInfo)) {
+ f.onRemoval = cb
+}
+
// RegisterPeripheral registers a new peripheral
func (f *FTDIFinder) RegisterPeripheral(ctx context.Context, peripheralData PeripheralInfo) (string, error) {
- // Create a new FTDI peripheral
- ftdiPeripheral, err := NewFTDIPeripheral(peripheralData)
- if err != nil {
- return "", fmt.Errorf("unable to create the FTDI peripheral: %v", err)
- }
- // Register it in the finder
- f.registeredPeripherals[peripheralData.SerialNumber] = ftdiPeripheral
- log.Trace().Any("periph", &ftdiPeripheral).Str("file", "FTDIFinder").Str("peripheralName", peripheralData.Name).Msg("FTDI peripheral has been created")
+ f.mu.Lock()
+ defer f.mu.Unlock()
- // Emit the event to the front
+ f.saved[peripheralData.SerialNumber] = peripheralData
+
+ // If already detected, connect it
+ if peripheral, ok := f.detected[peripheralData.SerialNumber]; ok {
+ runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), peripheralData, PeripheralStatusConnecting)
+ err := peripheral.Connect(ctx)
+ if err != nil {
+ runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), peripheralData, PeripheralStatusDisconnected)
+ log.Err(err).Str("file", "FTDIFinder").Str("peripheralSN", peripheralData.SerialNumber).Msg("unable to connect the peripheral")
+ return peripheralData.SerialNumber, nil
+ }
+ runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), peripheralData, PeripheralStatusDeactivated)
+ // Peripheral connected, activate it
+ err = peripheral.Activate(ctx)
+ if err != nil {
+ log.Err(err).Str("file", "FTDIFinder").Str("peripheralSN", peripheralData.SerialNumber).Msg("unable to activate the FTDI peripheral")
+ return peripheralData.SerialNumber, nil
+ }
+ runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), peripheralData, PeripheralStatusActivated)
+ }
+
+ // Emits the event in the hardware
runtime.EventsEmit(ctx, "LOAD_PERIPHERAL", peripheralData)
- // Peripheral created, connect it
- err = ftdiPeripheral.Connect(ctx)
- if err != nil {
- log.Err(err).Str("file", "FTDIFinder").Str("peripheralSN", peripheralData.SerialNumber).Msg("unable to connect the peripheral")
- return peripheralData.SerialNumber, nil
- }
- // Peripheral connected, activate it
- err = ftdiPeripheral.Activate(ctx)
- if err != nil {
- log.Err(err).Str("file", "FTDIFinder").Str("peripheralSN", peripheralData.SerialNumber).Msg("unable to activate the peripheral")
- return peripheralData.SerialNumber, nil
- }
- // Peripheral activated
return peripheralData.SerialNumber, nil
}
// UnregisterPeripheral unregisters an existing peripheral
-func (f *FTDIFinder) UnregisterPeripheral(ctx context.Context, peripheralID string) error {
- peripheral, registered := f.registeredPeripherals[peripheralID]
- if registered {
+func (f *FTDIFinder) UnregisterPeripheral(ctx context.Context, peripheralData PeripheralInfo) error {
+ f.mu.Lock()
+ defer f.mu.Unlock()
+
+ if peripheral, detected := f.detected[peripheralData.SerialNumber]; detected {
// Deactivating peripheral
err := peripheral.Deactivate(ctx)
if err != nil {
- return err
+ log.Err(err).Str("sn", peripheralData.SerialNumber).Msg("unable to deactivate the peripheral")
+ return nil
}
+ runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), peripheralData, PeripheralStatusDeactivated)
// Disconnecting peripheral
err = peripheral.Disconnect()
if err != nil {
- return err
+ log.Err(err).Str("sn", peripheralData.SerialNumber).Msg("unable to disconnect the peripheral")
+ return nil
}
-
- delete(f.registeredPeripherals, peripheralID)
-
- // Emit the event to the front
- runtime.EventsEmit(ctx, "UNLOAD_PERIPHERAL", peripheral.info)
+ runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), peripheralData, PeripheralStatusDisconnected)
}
+ delete(f.saved, peripheralData.SerialNumber)
+ runtime.EventsEmit(ctx, "UNLOAD_PERIPHERAL", peripheralData)
+
return nil
}
@@ -110,18 +127,15 @@ func (f *FTDIFinder) Initialize() error {
func (f *FTDIFinder) Start(ctx context.Context) error {
f.wg.Add(1)
go func() {
+ ticker := time.NewTicker(f.scanEvery)
+ defer ticker.Stop()
defer f.wg.Done()
+
for {
select {
case <-ctx.Done():
return
- case <-f.findTicker.C:
- // Scan the peripherals
- err := f.scanPeripherals(ctx)
- if err != nil {
- log.Err(err).Str("file", "FTDIFinder").Msg("unable to scan FTDI peripherals")
- }
- case <-f.scanChannel:
+ case <-ticker.C:
// Scan the peripherals
err := f.scanPeripherals(ctx)
if err != nil {
@@ -135,11 +149,11 @@ func (f *FTDIFinder) Start(ctx context.Context) error {
// ForceScan explicitly asks for scanning peripherals
func (f *FTDIFinder) ForceScan() {
- select {
- case f.scanChannel <- struct{}{}:
- default:
- // Ignore if the channel is full or if it is closed
- }
+ // select {
+ // case f.scanChannel <- struct{}{}:
+ // default:
+ // // Ignore if the channel is full or if it is closed
+ // }
}
// GetName returns the name of the driver
@@ -150,25 +164,27 @@ func (f *FTDIFinder) GetName() string {
// GetPeripheralSettings gets the peripheral settings
func (f *FTDIFinder) GetPeripheralSettings(peripheralID string) (map[string]interface{}, error) {
// Return the specified peripheral
- peripheral, found := f.registeredPeripherals[peripheralID]
- if !found {
- log.Error().Str("file", "FTDIFinder").Str("peripheralID", peripheralID).Msg("unable to get this peripheral from the FTDI finder")
- return nil, fmt.Errorf("unable to found the peripheral")
- }
- log.Debug().Str("file", "FTDIFinder").Str("peripheralID", peripheralID).Msg("peripheral found by the FTDI finder")
- return peripheral.GetSettings(), nil
+ // peripheral, found := f.registeredPeripherals[peripheralID]
+ // if !found {
+ // log.Error().Str("file", "FTDIFinder").Str("peripheralID", peripheralID).Msg("unable to get this peripheral from the FTDI finder")
+ // return nil, fmt.Errorf("unable to found the peripheral")
+ // }
+ // log.Debug().Str("file", "FTDIFinder").Str("peripheralID", peripheralID).Msg("peripheral found by the FTDI finder")
+ // return peripheral.GetSettings(), nil
+ return make(map[string]interface{}), nil
}
// SetPeripheralSettings sets the peripheral settings
func (f *FTDIFinder) SetPeripheralSettings(peripheralID string, settings map[string]interface{}) error {
// Return the specified peripheral
- peripheral, found := f.registeredPeripherals[peripheralID]
- if !found {
- log.Error().Str("file", "FTDIFinder").Str("peripheralID", peripheralID).Msg("unable to get this peripheral from the FTDI finder")
- return fmt.Errorf("unable to found the peripheral")
- }
- log.Debug().Str("file", "FTDIFinder").Str("peripheralID", peripheralID).Msg("peripheral found by the FTDI finder")
- return peripheral.SetSettings(settings)
+ // peripheral, found := f.registeredPeripherals[peripheralID]
+ // if !found {
+ // log.Error().Str("file", "FTDIFinder").Str("peripheralID", peripheralID).Msg("unable to get this peripheral from the FTDI finder")
+ // return fmt.Errorf("unable to found the peripheral")
+ // }
+ // log.Debug().Str("file", "FTDIFinder").Str("peripheralID", peripheralID).Msg("peripheral found by the FTDI finder")
+ // return peripheral.SetSettings(settings)
+ return nil
}
// scanPeripherals scans the FTDI peripherals
@@ -188,19 +204,19 @@ func (f *FTDIFinder) scanPeripherals(ctx context.Context) error {
C.get_ftdi_devices((*C.FTDIPeripheralC)(devicesPtr), C.int(count))
- temporaryPeripherals := make(map[string]PeripheralInfo)
+ currentMap := make(map[string]PeripheralInfo)
for i := 0; i < count; i++ {
d := devices[i]
sn := C.GoString(d.serialNumber)
desc := C.GoString(d.description)
- isOpen := d.isOpen != 0
+ // isOpen := d.isOpen != 0
- temporaryPeripherals[sn] = PeripheralInfo{
+ currentMap[sn] = PeripheralInfo{
SerialNumber: sn,
Name: desc,
- IsOpen: isOpen,
+ // IsOpen: isOpen,
ProtocolName: "FTDI",
}
@@ -208,12 +224,63 @@ func (f *FTDIFinder) scanPeripherals(ctx context.Context) error {
C.free_ftdi_device(&d)
}
- log.Info().Any("peripherals", temporaryPeripherals).Msg("available FTDI peripherals")
+ log.Info().Any("peripherals", currentMap).Msg("available FTDI peripherals")
- // Emit the peripherals changes to the front
- emitPeripheralsChanges(ctx, f.foundPeripherals, temporaryPeripherals)
- // Store the new peripherals list
- f.foundPeripherals = temporaryPeripherals
+ // Detect arrivals
+ for sn, peripheralData := range currentMap {
+ if _, known := f.detected[sn]; !known {
+ peripheral := NewFTDIPeripheral(peripheralData)
+ f.detected[sn] = peripheral
+
+ if f.onArrival != nil {
+ go f.onArrival(peripheralData)
+ }
+ log.Info().Str("sn", sn).Str("name", peripheralData.Name).Msg("[FTDI] New peripheral detected")
+
+ // Disconnected by default
+ runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), peripheralData, PeripheralStatusDisconnected)
+
+ // If the peripheral is saved in the project => connect
+ if _, saved := f.saved[sn]; saved {
+ runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), peripheralData, PeripheralStatusConnecting)
+ err := peripheral.Connect(ctx)
+ if err != nil {
+ runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), peripheralData, PeripheralStatusDisconnected)
+ log.Err(err).Str("sn", sn).Msg("unable to connect the FTDI peripheral")
+ return nil
+ }
+ runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), peripheralData, PeripheralStatusDeactivated)
+ err = peripheral.Activate(ctx)
+ if err != nil {
+ log.Err(err).Str("sn", sn).Msg("unable to activate the FTDI peripheral")
+ return nil
+ }
+ runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), peripheralData, PeripheralStatusActivated)
+ }
+ }
+ }
+
+ // Detect removals
+ for sn, oldPeripheral := range f.detected {
+ if _, still := currentMap[sn]; !still {
+
+ // Properly clean the DMX device
+ err := oldPeripheral.Disconnect()
+ if err != nil {
+ log.Err(err).Str("sn", sn).Msg("unable to clean the FTDI peripheral after disconnection")
+ }
+ runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), oldPeripheral.GetInfo(), PeripheralStatusDisconnected)
+
+ // Delete it from the detected list
+ delete(f.detected, sn)
+ log.Info().Str("sn", sn).Str("name", oldPeripheral.GetInfo().Name).Msg("[FTDI] peripheral removed")
+
+ // Execute the removal callback
+ if f.onRemoval != nil {
+ go f.onRemoval(oldPeripheral.GetInfo())
+ }
+ }
+ }
return nil
}
@@ -221,29 +288,26 @@ func (f *FTDIFinder) scanPeripherals(ctx context.Context) error {
func (f *FTDIFinder) WaitStop() error {
log.Trace().Str("file", "FTDIFinder").Msg("stopping the FTDI finder...")
- // Stop the ticker
- f.findTicker.Stop()
-
// Close the channel
- close(f.scanChannel)
+ // close(f.scanChannel)
// Wait for all the peripherals to close
- log.Trace().Str("file", "FTDIFinder").Msg("closing all FTDI peripherals")
- var errs []error
- for registeredPeripheralSN, registeredPeripheral := range f.registeredPeripherals {
- err := registeredPeripheral.WaitStop()
- if err != nil {
- errs = append(errs, fmt.Errorf("%s: %w", registeredPeripheralSN, err))
- }
- }
+ // log.Trace().Str("file", "FTDIFinder").Msg("closing all FTDI peripherals")
+ // var errs []error
+ // for registeredPeripheralSN, registeredPeripheral := range f.registeredPeripherals {
+ // err := registeredPeripheral.WaitStop()
+ // if err != nil {
+ // errs = append(errs, fmt.Errorf("%s: %w", registeredPeripheralSN, err))
+ // }
+ // }
// Wait for goroutines to stop
f.wg.Wait()
// Returning errors
- if len(errs) > 0 {
- return errors.Join(errs...)
- }
+ // if len(errs) > 0 {
+ // return errors.Join(errs...)
+ // }
log.Trace().Str("file", "FTDIFinder").Msg("FTDI finder stopped")
return nil
}
diff --git a/hardware/FTDIPeripheral.go b/hardware/FTDIPeripheral.go
index c8ed185..8debace 100644
--- a/hardware/FTDIPeripheral.go
+++ b/hardware/FTDIPeripheral.go
@@ -2,14 +2,12 @@ package hardware
import (
"context"
- _ "embed"
"sync"
"unsafe"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
- "github.com/wailsapp/wails/v2/pkg/runtime"
)
/*
@@ -28,12 +26,12 @@ type FTDIPeripheral struct {
}
// NewFTDIPeripheral creates a new FTDI peripheral
-func NewFTDIPeripheral(info PeripheralInfo) (*FTDIPeripheral, error) {
+func NewFTDIPeripheral(info PeripheralInfo) *FTDIPeripheral {
log.Info().Str("file", "FTDIPeripheral").Str("name", info.Name).Str("s/n", info.SerialNumber).Msg("FTDI peripheral created")
return &FTDIPeripheral{
info: info,
dmxSender: nil,
- }, nil
+ }
}
// Connect connects the FTDI peripheral
@@ -47,13 +45,11 @@ func (p *FTDIPeripheral) Connect(ctx context.Context) error {
p.dmxSender = C.dmx_create()
// Connect the FTDI
- runtime.EventsEmit(ctx, string(PeripheralStatus), p.info, "connecting")
serialNumber := C.CString(p.info.SerialNumber)
defer C.free(unsafe.Pointer(serialNumber))
if C.dmx_connect(p.dmxSender, serialNumber) != C.DMX_OK {
- runtime.EventsEmit(ctx, string(PeripheralStatus), p.info, "disconnected")
log.Error().Str("file", "FTDIPeripheral").Str("s/n", p.info.SerialNumber).Msg("unable to connect the DMX device")
- return errors.Errorf("unable to connect '%s'")
+ return errors.Errorf("unable to connect '%s'", p.info.SerialNumber)
}
p.wg.Add(1)
@@ -63,10 +59,7 @@ func (p *FTDIPeripheral) Connect(ctx context.Context) error {
_ = p.Disconnect()
}()
- // Send deactivated state (connected but not activated for now)
- runtime.EventsEmit(ctx, string(PeripheralStatus), p.info, "deactivated")
log.Trace().Str("file", "FTDIPeripheral").Str("s/n", p.info.SerialNumber).Msg("DMX device connected successfully")
-
return nil
}
@@ -79,6 +72,9 @@ func (p *FTDIPeripheral) Disconnect() error {
// Destroy the dmx sender
C.dmx_destroy(p.dmxSender)
+
+ // Reset the pointer to the peripheral
+ p.dmxSender = nil
return nil
}
@@ -93,7 +89,6 @@ func (p *FTDIPeripheral) Activate(ctx context.Context) error {
err := C.dmx_activate(p.dmxSender)
if err != C.DMX_OK {
- runtime.EventsEmit(ctx, string(PeripheralStatus), p.info, "deactivated")
return errors.Errorf("unable to activate the DMX sender!")
}
@@ -101,10 +96,7 @@ func (p *FTDIPeripheral) Activate(ctx context.Context) error {
C.dmx_setValue(p.dmxSender, C.uint16_t(1), C.uint8_t(255))
C.dmx_setValue(p.dmxSender, C.uint16_t(5), C.uint8_t(255))
- runtime.EventsEmit(ctx, string(PeripheralStatus), p.info, "activated")
-
log.Trace().Str("file", "FTDIPeripheral").Str("s/n", p.info.SerialNumber).Msg("DMX device activated successfully")
-
return nil
}
@@ -123,7 +115,6 @@ func (p *FTDIPeripheral) Deactivate(ctx context.Context) error {
}
log.Trace().Str("file", "FTDIPeripheral").Str("s/n", p.info.SerialNumber).Msg("DMX device deactivated successfully")
-
return nil
}
diff --git a/hardware/OS2LPeripheral.go b/hardware/OS2LPeripheral.go
index 07e69a0..4bc9703 100644
--- a/hardware/OS2LPeripheral.go
+++ b/hardware/OS2LPeripheral.go
@@ -30,9 +30,9 @@ func NewOS2LPeripheral(peripheralData PeripheralInfo) (*OS2LPeripheral, error) {
func (p *OS2LPeripheral) Connect(ctx context.Context) error {
log.Info().Str("file", "OS2LPeripheral").Msg("OS2L peripheral connected")
go func() {
- runtime.EventsEmit(ctx, string(PeripheralStatus), p.info, "connecting")
+ runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), p.info, "connecting")
time.Sleep(5 * time.Second)
- runtime.EventsEmit(ctx, string(PeripheralStatus), p.info, "disconnected")
+ runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), p.info, "disconnected")
}()
return nil
}
diff --git a/hardware/hardware.go b/hardware/hardware.go
index cbc841d..466f4b4 100644
--- a/hardware/hardware.go
+++ b/hardware/hardware.go
@@ -7,21 +7,30 @@ import (
"sync"
"github.com/rs/zerolog/log"
-
"github.com/wailsapp/wails/v2/pkg/runtime"
)
// PeripheralEvent is trigger by the finders when the scan is complete
type PeripheralEvent string
+// PeripheralStatus is the peripheral status (DISCONNECTED => CONNECTING => DEACTIVATED => ACTIVATED)
+type PeripheralStatus string
+
const (
// PeripheralArrival is triggerd when a peripheral has been connected to the system
PeripheralArrival PeripheralEvent = "PERIPHERAL_ARRIVAL"
// PeripheralRemoval is triggered when a peripheral has been disconnected from the system
PeripheralRemoval PeripheralEvent = "PERIPHERAL_REMOVAL"
- // PeripheralStatus is triggered when a peripheral status has been updated (disconnected - connecting - connected)
- PeripheralStatus PeripheralEvent = "PERIPHERAL_STATUS"
- // debounceDuration = 500 * time.Millisecond
+ // PeripheralStatusUpdated is triggered when a peripheral status has been updated (disconnected - connecting - connected)
+ PeripheralStatusUpdated PeripheralEvent = "PERIPHERAL_STATUS"
+ // PeripheralStatusDisconnected : peripheral is now disconnected
+ PeripheralStatusDisconnected PeripheralStatus = "PERIPHERAL_DISCONNECTED"
+ // PeripheralStatusConnecting : peripheral is now connecting
+ PeripheralStatusConnecting PeripheralStatus = "PERIPHERAL_CONNECTING"
+ // PeripheralStatusDeactivated : peripheral is now deactivated
+ PeripheralStatusDeactivated PeripheralStatus = "PERIPHERAL_DEACTIVATED"
+ // PeripheralStatusActivated : peripheral is now activated
+ PeripheralStatusActivated PeripheralStatus = "PERIPHERAL_ACTIVATED"
)
// HardwareManager is the class who manages the hardware
@@ -45,13 +54,19 @@ func NewHardwareManager() *HardwareManager {
// Start starts to find new peripheral events
func (h *HardwareManager) Start(ctx context.Context) error {
- // Initialize all the finders
+ // Initialize all the finders and their callback functions
for finderName, finder := range h.finders {
err := finder.Initialize()
if err != nil {
log.Err(err).Str("file", "hardware").Str("finderName", finderName).Msg("unable to initialize finder")
return err
}
+ finder.OnArrival(func(p PeripheralInfo) {
+ runtime.EventsEmit(ctx, string(PeripheralArrival), p)
+ })
+ finder.OnRemoval(func(p PeripheralInfo) {
+ runtime.EventsEmit(ctx, string(PeripheralRemoval), p)
+ })
err = finder.Start(ctx)
if err != nil {
log.Err(err).Str("file", "hardware").Str("finderName", finderName).Msg("unable to start finder")
@@ -105,27 +120,6 @@ func (h *HardwareManager) Scan() error {
}
}
-// emitPeripheralsChanges compares the old and new peripherals to determine which ones have been added or removed.
-func emitPeripheralsChanges(ctx context.Context, oldPeripherals map[string]PeripheralInfo, newPeripherals map[string]PeripheralInfo) {
- log.Trace().Any("oldList", oldPeripherals).Any("newList", newPeripherals).Msg("emitting peripherals changes to the front")
-
- // Identify removed peripherals: present in the old list but not in the new list
- for oldPeriphName := range oldPeripherals {
- if _, exists := newPeripherals[oldPeriphName]; !exists {
- runtime.EventsEmit(ctx, string(PeripheralRemoval), oldPeripherals[oldPeriphName])
- log.Trace().Str("file", "hardware").Str("event", string(PeripheralRemoval)).Msg("emit peripheral removal event")
- }
- }
-
- // Identify added peripherals: present in the new list but not in the old list
- for newPeriphName := range newPeripherals {
- if _, exists := oldPeripherals[newPeriphName]; !exists {
- runtime.EventsEmit(ctx, string(PeripheralArrival), newPeripherals[newPeriphName])
- log.Trace().Str("file", "hardware").Str("event", string(PeripheralArrival)).Msg("emit peripheral arrival event")
- }
- }
-}
-
// WaitStop stops the hardware manager
func (h *HardwareManager) WaitStop() error {
log.Trace().Str("file", "hardware").Msg("closing the hardware manager")
diff --git a/hardware/interfaces.go b/hardware/interfaces.go
index 039cf13..d48b3fe 100644
--- a/hardware/interfaces.go
+++ b/hardware/interfaces.go
@@ -6,7 +6,7 @@ import "context"
type Peripheral interface {
Connect(context.Context) error // Connect the peripheral
IsConnected() bool // Return if the peripheral is connected or not
- Disconnect() error // Disconnect the peripheral
+ Disconnect() error // Disconnect the peripheral
Activate(context.Context) error // Activate the peripheral
Deactivate(context.Context) error // Deactivate the peripheral
SetSettings(map[string]interface{}) error // Set a peripheral setting
@@ -18,21 +18,25 @@ type Peripheral interface {
// PeripheralInfo represents a peripheral information
type PeripheralInfo struct {
- 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
- IsOpen bool // Open flag for peripheral connection
- Settings map[string]interface{} `yaml:"settings"` // Peripheral settings
+ 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
+ // IsConnected bool // If the peripheral is connected to the system
+ // IsActivated bool // If the peripheral is activated in the project
+ // IsDetected bool // If the peripheral is detected by the system
+ Settings map[string]interface{} `yaml:"settings"` // Peripheral settings
}
// PeripheralFinder represents how compatible peripheral drivers are implemented
type PeripheralFinder interface {
Initialize() error // Initializes the protocol
+ OnArrival(cb func(p PeripheralInfo)) // Callback function when a peripheral arrives
+ OnRemoval(cb func(p PeripheralInfo)) // Callback function when a peripheral goes away
Start(context.Context) error // Start the detection
WaitStop() error // Waiting for finder to close
ForceScan() // Explicitly scans for peripherals
RegisterPeripheral(context.Context, PeripheralInfo) (string, error) // Registers a new peripheral data
- UnregisterPeripheral(context.Context, string) error // Unregisters an existing peripheral
+ UnregisterPeripheral(context.Context, PeripheralInfo) error // Unregisters an existing peripheral
GetPeripheralSettings(string) (map[string]interface{}, error) // Gets the peripheral settings
SetPeripheralSettings(string, map[string]interface{}) error // Sets the peripheral settings
GetName() string // Get the name of the finder
diff --git a/peripherals.go b/peripherals.go
index e128c1d..91ae570 100644
--- a/peripherals.go
+++ b/peripherals.go
@@ -65,21 +65,21 @@ func (a *App) UpdatePeripheralSettings(protocolName, peripheralID string, settin
}
// RemovePeripheral removes a peripheral from the project
-func (a *App) RemovePeripheral(protocolName string, peripheralID string) error {
+func (a *App) RemovePeripheral(peripheralData hardware.PeripheralInfo) error {
// Unregister the peripheral from the finder
- f, err := a.hardwareManager.GetFinder(protocolName)
+ f, err := a.hardwareManager.GetFinder(peripheralData.ProtocolName)
if err != nil {
- log.Err(err).Str("file", "peripherals").Str("protocolName", protocolName).Msg("unable to find the finder")
+ log.Err(err).Str("file", "peripherals").Str("protocolName", peripheralData.ProtocolName).Msg("unable to find the finder")
return fmt.Errorf("unable to find the finder")
}
- err = f.UnregisterPeripheral(a.ctx, peripheralID)
+ err = f.UnregisterPeripheral(a.ctx, peripheralData)
if err != nil {
- log.Err(err).Str("file", "peripherals").Str("peripheralID", peripheralID).Msg("unable to unregister this peripheral")
+ log.Err(err).Str("file", "peripherals").Str("peripheralID", peripheralData.SerialNumber).Msg("unable to unregister this peripheral")
return fmt.Errorf("unable to unregister this 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")
+ delete(a.projectInfo.PeripheralsInfo, peripheralData.SerialNumber)
+ log.Info().Str("file", "peripheral").Str("protocolName", peripheralData.ProtocolName).Str("periphID", peripheralData.SerialNumber).Msg("peripheral removed from project")
return nil
}
diff --git a/project.go b/project.go
index 0a249ab..af7a157 100644
--- a/project.go
+++ b/project.go
@@ -151,7 +151,7 @@ func (a *App) CloseCurrentProject() error {
if err != nil {
return fmt.Errorf("unable to find the finder '%s': %w", value.ProtocolName, err)
}
- err = hostFinder.UnregisterPeripheral(a.ctx, key)
+ err = hostFinder.UnregisterPeripheral(a.ctx, value)
if err != nil {
return fmt.Errorf("unable to unregister the peripheral S/N '%s': %w", key, err)
}