generated from thinkode/modelRepository
clean up arch
This commit is contained in:
@@ -3,6 +3,7 @@ package hardware
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
goRuntime "runtime"
|
||||
"sync"
|
||||
@@ -19,20 +20,21 @@ import (
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// FTDIFinder represents how the protocol is defined
|
||||
// FTDIFinder manages all the FTDI peripherals
|
||||
type FTDIFinder struct {
|
||||
findTicker time.Ticker // Peripherals find ticker
|
||||
wg sync.WaitGroup
|
||||
|
||||
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
|
||||
goWait sync.WaitGroup // Check goroutines execution
|
||||
}
|
||||
|
||||
// NewFTDIFinder creates a new FTDI finder
|
||||
func NewFTDIFinder(findPeriod time.Duration) *FTDIFinder {
|
||||
log.Trace().Str("file", "FTDIFinder").Msg("FTDI finder created")
|
||||
return &FTDIFinder{
|
||||
findTicker: *time.NewTicker(findPeriod),
|
||||
findTicker: time.NewTicker(findPeriod),
|
||||
foundPeripherals: make(map[string]PeripheralInfo),
|
||||
registeredPeripherals: make(map[string]*FTDIPeripheral),
|
||||
scanChannel: make(chan struct{}),
|
||||
@@ -41,12 +43,15 @@ func NewFTDIFinder(findPeriod time.Duration) *FTDIFinder {
|
||||
|
||||
// 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")
|
||||
|
||||
// Peripheral created, connect it
|
||||
err = ftdiPeripheral.Connect(ctx)
|
||||
if err != nil {
|
||||
@@ -71,7 +76,7 @@ func (f *FTDIFinder) UnregisterPeripheral(ctx context.Context, peripheralID stri
|
||||
return err
|
||||
}
|
||||
// Disconnecting peripheral
|
||||
err = peripheral.Disconnect(ctx)
|
||||
err = peripheral.Disconnect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -93,9 +98,9 @@ func (f *FTDIFinder) Initialize() error {
|
||||
|
||||
// Start starts the finder and search for peripherals
|
||||
func (f *FTDIFinder) Start(ctx context.Context) error {
|
||||
f.goWait.Add(1)
|
||||
f.wg.Add(1)
|
||||
go func() {
|
||||
defer f.goWait.Done()
|
||||
defer f.wg.Done()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -120,18 +125,11 @@ func (f *FTDIFinder) Start(ctx context.Context) error {
|
||||
|
||||
// ForceScan explicily asks for scanning peripherals
|
||||
func (f *FTDIFinder) ForceScan() {
|
||||
f.scanChannel <- struct{}{}
|
||||
}
|
||||
|
||||
// Stop stops the finder
|
||||
func (f *FTDIFinder) Stop() error {
|
||||
log.Trace().Str("file", "FTDIFinder").Msg("stopping the FTDI finder...")
|
||||
// Wait for goroutines to stop
|
||||
f.goWait.Wait()
|
||||
// Stop the ticker
|
||||
f.findTicker.Stop()
|
||||
log.Trace().Str("file", "FTDIFinder").Msg("FTDI finder stopped")
|
||||
return nil
|
||||
select {
|
||||
case f.scanChannel <- struct{}{}:
|
||||
default:
|
||||
// Ignore if the channel is full or if it is closed
|
||||
}
|
||||
}
|
||||
|
||||
// GetName returns the name of the driver
|
||||
@@ -171,18 +169,12 @@ func (f *FTDIFinder) scanPeripherals(ctx context.Context) error {
|
||||
|
||||
log.Info().Int("number", count).Msg("number of FTDI devices connected")
|
||||
|
||||
// Alloue un tableau de structures côté C
|
||||
// Allocating C array
|
||||
size := C.size_t(count) * C.size_t(unsafe.Sizeof(C.FTDIPeripheralC{}))
|
||||
devicesPtr := C.malloc(size)
|
||||
defer C.free(devicesPtr)
|
||||
|
||||
devices := (*[1 << 30]C.FTDIPeripheralC)(devicesPtr)[:count:count]
|
||||
|
||||
type device struct {
|
||||
SerialNumber string
|
||||
Description string
|
||||
IsOpen bool
|
||||
}
|
||||
devices := (*[1 << 20]C.FTDIPeripheralC)(devicesPtr)[:count:count]
|
||||
|
||||
C.get_ftdi_devices((*C.FTDIPeripheralC)(devicesPtr), C.int(count))
|
||||
|
||||
@@ -202,7 +194,7 @@ func (f *FTDIFinder) scanPeripherals(ctx context.Context) error {
|
||||
ProtocolName: "FTDI",
|
||||
}
|
||||
|
||||
// Libération mémoire allouée côté C
|
||||
// Free C memory
|
||||
C.free_ftdi_device(&d)
|
||||
}
|
||||
|
||||
@@ -214,3 +206,34 @@ func (f *FTDIFinder) scanPeripherals(ctx context.Context) error {
|
||||
f.foundPeripherals = temporaryPeripherals
|
||||
return nil
|
||||
}
|
||||
|
||||
// WaitStop stops the finder
|
||||
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)
|
||||
|
||||
// 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))
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for goroutines to stop
|
||||
f.wg.Wait()
|
||||
|
||||
// Returning errors
|
||||
if len(errs) > 0 {
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
log.Trace().Str("file", "FTDIFinder").Msg("FTDI finder stopped")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
)
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
#cgo LDFLAGS: -L${SRCDIR}/../build/bin -ldmxSender
|
||||
#include "cpp/include/dmxSenderBridge.h"
|
||||
*/
|
||||
@@ -20,29 +21,23 @@ import "C"
|
||||
|
||||
// FTDIPeripheral contains the data of an FTDI peripheral
|
||||
type FTDIPeripheral struct {
|
||||
info PeripheralInfo // The peripheral basic data
|
||||
settings map[string]interface{} // The settings of the peripheral
|
||||
dmxSender unsafe.Pointer // The command object for piloting the DMX ouptut
|
||||
waitGroup sync.WaitGroup // Waitgroup to wait goroutines
|
||||
isConnected bool // If the FTDI is connected or not
|
||||
wg sync.WaitGroup
|
||||
|
||||
info PeripheralInfo // The peripheral basic data
|
||||
dmxSender unsafe.Pointer // The command object for piloting the DMX ouptut
|
||||
}
|
||||
|
||||
// NewFTDIPeripheral creates a new FTDI peripheral
|
||||
func NewFTDIPeripheral(info PeripheralInfo) (*FTDIPeripheral, error) {
|
||||
log.Info().Str("file", "FTDIPeripheral").Str("name", info.Name).Str("s/n", info.SerialNumber).Msg("FTDI peripheral created")
|
||||
settings := make(map[string]interface{})
|
||||
return &FTDIPeripheral{
|
||||
info: info,
|
||||
dmxSender: nil,
|
||||
settings: settings,
|
||||
isConnected: false,
|
||||
info: info,
|
||||
dmxSender: nil,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Connect connects the FTDI peripheral
|
||||
func (p *FTDIPeripheral) Connect(ctx context.Context) error {
|
||||
runtime.EventsEmit(ctx, string(PeripheralStatus), p.info, "connecting")
|
||||
|
||||
// Check if the device has already been created
|
||||
if p.dmxSender != nil {
|
||||
return errors.Errorf("the DMX device has already been created!")
|
||||
@@ -51,30 +46,31 @@ func (p *FTDIPeripheral) Connect(ctx context.Context) error {
|
||||
// Create the DMX sender
|
||||
p.dmxSender = C.dmx_create()
|
||||
|
||||
// Connect the peripheral
|
||||
log.Trace().Str("file", "FTDIPeripheral").Str("s/n", p.info.SerialNumber).Msg("connecting FTDI peripheral...")
|
||||
err := C.dmx_connect(p.dmxSender, C.CString(p.info.SerialNumber))
|
||||
if err != C.DMX_OK {
|
||||
log.Error().Str("file", "FTDIPeripheral").Str("s/n", p.info.SerialNumber).Any("err", err).Msg("unable to connect the DMX device")
|
||||
return errors.Errorf("Unable to connect the DMX Device on the specified port")
|
||||
// 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 {
|
||||
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'")
|
||||
}
|
||||
|
||||
p.waitGroup.Add(1)
|
||||
p.wg.Add(1)
|
||||
go func() {
|
||||
defer p.waitGroup.Done()
|
||||
defer p.wg.Done()
|
||||
<-ctx.Done()
|
||||
p.Disconnect(ctx)
|
||||
_ = p.Disconnect()
|
||||
}()
|
||||
|
||||
p.isConnected = true
|
||||
// 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
|
||||
}
|
||||
|
||||
// Disconnect disconnects the FTDI peripheral
|
||||
func (p *FTDIPeripheral) Disconnect(ctx context.Context) error {
|
||||
func (p *FTDIPeripheral) Disconnect() error {
|
||||
// Check if the device has already been created
|
||||
if p.dmxSender == nil {
|
||||
return errors.Errorf("the DMX device has not been connected!")
|
||||
@@ -82,15 +78,9 @@ func (p *FTDIPeripheral) Disconnect(ctx context.Context) error {
|
||||
|
||||
// Destroy the dmx sender
|
||||
C.dmx_destroy(p.dmxSender)
|
||||
p.isConnected = false
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsConnected returns if the FTDI is connected or not
|
||||
func (p *FTDIPeripheral) IsConnected() bool {
|
||||
return p.isConnected
|
||||
}
|
||||
|
||||
// Activate activates the FTDI peripheral
|
||||
func (p *FTDIPeripheral) Activate(ctx context.Context) error {
|
||||
// Check if the device has already been created
|
||||
@@ -136,8 +126,7 @@ func (p *FTDIPeripheral) Deactivate(ctx context.Context) error {
|
||||
|
||||
// SetSettings sets a specific setting for this peripheral
|
||||
func (p *FTDIPeripheral) SetSettings(settings map[string]interface{}) error {
|
||||
p.settings = settings
|
||||
return nil
|
||||
return errors.Errorf("unable to set the settings: not implemented")
|
||||
}
|
||||
|
||||
// SetDeviceProperty sends a command to the specified device
|
||||
@@ -161,10 +150,18 @@ func (p *FTDIPeripheral) SetDeviceProperty(ctx context.Context, channelNumber ui
|
||||
|
||||
// GetSettings gets the peripheral settings
|
||||
func (p *FTDIPeripheral) GetSettings() map[string]interface{} {
|
||||
return p.settings
|
||||
return map[string]interface{}{}
|
||||
}
|
||||
|
||||
// GetInfo gets all the peripheral information
|
||||
func (p *FTDIPeripheral) GetInfo() PeripheralInfo {
|
||||
return p.info
|
||||
}
|
||||
|
||||
// WaitStop wait about the peripheral to close
|
||||
func (p *FTDIPeripheral) WaitStop() error {
|
||||
log.Info().Str("file", "FTDIPeripheral").Str("s/n", p.info.SerialNumber).Msg("waiting for FTDI peripheral to close...")
|
||||
p.wg.Wait()
|
||||
log.Info().Str("file", "FTDIPeripheral").Str("s/n", p.info.SerialNumber).Msg("FTDI peripheral closed!")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ package hardware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
@@ -24,16 +24,13 @@ const (
|
||||
// debounceDuration = 500 * time.Millisecond
|
||||
)
|
||||
|
||||
var (
|
||||
debounceTimer *time.Timer
|
||||
)
|
||||
|
||||
// HardwareManager is the class who manages the hardware
|
||||
type HardwareManager struct {
|
||||
wg sync.WaitGroup
|
||||
|
||||
finders map[string]PeripheralFinder // The map of peripherals finders
|
||||
peripherals []Peripheral // The current list of peripherals
|
||||
peripheralsScanTrigger chan struct{} // Trigger the peripherals scans
|
||||
goWait sync.WaitGroup // Wait for goroutines to terminate
|
||||
}
|
||||
|
||||
// NewHardwareManager creates a new HardwareManager
|
||||
@@ -48,6 +45,7 @@ func NewHardwareManager() *HardwareManager {
|
||||
|
||||
// Start starts to find new peripheral events
|
||||
func (h *HardwareManager) Start(ctx context.Context) error {
|
||||
// Initialize all the finders
|
||||
for finderName, finder := range h.finders {
|
||||
err := finder.Initialize()
|
||||
if err != nil {
|
||||
@@ -60,9 +58,11 @@ func (h *HardwareManager) Start(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
h.goWait.Add(1)
|
||||
|
||||
// Periodically scan all the finders
|
||||
h.wg.Add(1)
|
||||
go func() {
|
||||
defer h.goWait.Done()
|
||||
defer h.wg.Done()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -95,40 +95,14 @@ func (h *HardwareManager) RegisterFinder(finder PeripheralFinder) {
|
||||
log.Info().Str("file", "hardware").Str("finderName", finder.GetName()).Msg("finder registered")
|
||||
}
|
||||
|
||||
// // GetPeripheral gets the peripheral object from the parent finder
|
||||
// func (h *HardwareManager) GetPeripheral(finderName string, peripheralID string) (Peripheral, bool) {
|
||||
// // Get the finder
|
||||
// parentFinder, found := h.finders[finderName]
|
||||
// // If no finder found, return false
|
||||
// if !found {
|
||||
// log.Error().Str("file", "hardware").Str("finderName", finderName).Msg("unable to get the finder")
|
||||
// return nil, false
|
||||
// }
|
||||
// log.Trace().Str("file", "hardware").Str("finderName", parentFinder.GetName()).Msg("finder got")
|
||||
// // Contact the finder to get the peripheral
|
||||
// return parentFinder.GetPeripheral(peripheralID)
|
||||
// }
|
||||
|
||||
// Scan scans all the peripherals for the registered finders
|
||||
func (h *HardwareManager) Scan() error {
|
||||
h.peripheralsScanTrigger <- struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop stops the hardware manager
|
||||
func (h *HardwareManager) Stop() error {
|
||||
log.Trace().Str("file", "hardware").Msg("closing the hardware manager")
|
||||
// Stop each finder
|
||||
for finderName, finder := range h.finders {
|
||||
err := finder.Stop()
|
||||
if err != nil {
|
||||
log.Err(err).Str("file", "hardware").Str("finderName", finderName).Msg("unable to stop the finder")
|
||||
}
|
||||
select {
|
||||
case h.peripheralsScanTrigger <- struct{}{}:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("scan trigger not available (manager stopped?)")
|
||||
}
|
||||
// Wait for goroutines to finish
|
||||
h.goWait.Wait()
|
||||
log.Info().Str("file", "hardware").Msg("hardware manager stopped")
|
||||
return nil
|
||||
}
|
||||
|
||||
// emitPeripheralsChanges compares the old and new peripherals to determine which ones have been added or removed.
|
||||
@@ -151,3 +125,30 @@ func emitPeripheralsChanges(ctx context.Context, oldPeripherals map[string]Perip
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WaitStop stops the hardware manager
|
||||
func (h *HardwareManager) WaitStop() error {
|
||||
log.Trace().Str("file", "hardware").Msg("closing the hardware manager")
|
||||
|
||||
// Closing trigger channel
|
||||
close(h.peripheralsScanTrigger)
|
||||
|
||||
// Stop each finder
|
||||
var errs []error
|
||||
for name, f := range h.finders {
|
||||
if err := f.WaitStop(); err != nil {
|
||||
errs = append(errs, fmt.Errorf("%s: %w", name, err))
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for goroutines to finish
|
||||
h.wg.Wait()
|
||||
|
||||
// Returning errors
|
||||
if len(errs) > 0 {
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
log.Info().Str("file", "hardware").Msg("hardware manager stopped")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -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(context.Context) 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
|
||||
@@ -29,7 +29,7 @@ type PeripheralInfo struct {
|
||||
type PeripheralFinder interface {
|
||||
Initialize() error // Initializes the protocol
|
||||
Start(context.Context) error // Start the detection
|
||||
Stop() error // Stop 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
|
||||
|
||||
Reference in New Issue
Block a user