Compare commits

...

5 Commits

28 changed files with 2433 additions and 663 deletions

View File

@@ -58,6 +58,9 @@
"streambuf": "cpp",
"thread": "cpp",
"typeinfo": "cpp",
"variant": "cpp"
"variant": "cpp",
"queue": "cpp",
"ranges": "cpp",
"text_encoding": "cpp"
}
}

4
app.go
View File

@@ -28,9 +28,9 @@ type App struct {
func NewApp() *App {
// Create a new hadware manager
hardwareManager := hardware.NewHardwareManager()
hardwareManager.RegisterFinder(hardware.NewMIDIFinder(5 * time.Second))
// hardwareManager.RegisterFinder(hardware.NewMIDIFinder(5 * time.Second))
hardwareManager.RegisterFinder(hardware.NewFTDIFinder(5 * time.Second))
hardwareManager.RegisterFinder(hardware.NewOS2LFinder())
// hardwareManager.RegisterFinder(hardware.NewOS2LFinder())
return &App{
hardwareManager: hardwareManager,
projectSave: "",

55
build.bat Normal file
View File

@@ -0,0 +1,55 @@
@echo off
setlocal
echo ============================================
echo [INFO] Starting Wails build script
echo ============================================
rem Détection du mode (par défaut : build)
set "MODE=build"
if /i "%~1"=="-dev" set "MODE=dev"
rem 1⃣ Essayer de récupérer le dernier tag
for /f "tokens=*" %%i in ('git describe --tags --abbrev=0 2^>nul') do set "GIT_TAG=%%i"
rem 2⃣ Si pas de tag, utiliser le hash du commit
if "%GIT_TAG%"=="" (
for /f "tokens=*" %%i in ('git rev-parse --short HEAD 2^>nul') do set "GIT_TAG=%%i"
)
rem 3⃣ Si Git nest pas dispo, mettre "unknown"
if "%GIT_TAG%"=="" set "GIT_TAG=unknown"
echo [INFO] Git version detected: %GIT_TAG%
echo [INFO] Mode selectionne : %MODE%
echo [INFO] Moving to the C++ folder...
cd /d "%~dp0hardware\cpp" || (
echo [ERROR] Impossible d'accéder à hardware\cpp
exit /b 1
)
echo [INFO] Compiling C++ libraries...
call generate.bat || (
echo [ERROR] Échec de la compilation C++
exit /b 1
)
echo [INFO] Returning to project root...
cd /d "%~dp0" || exit /b 1
if /i "%MODE%"=="dev" (
echo [INFO] Launching Wails in DEV mode...
wails dev
) else (
echo [INFO] Building Wails application...
wails build -o "dmxconnect-%GIT_TAG%.exe"
)
echo ============================================
echo [SUCCESS] Done!
echo ============================================
endlocal

View File

@@ -54,9 +54,24 @@
generateToast('warning', 'bxs-hdd', $_("peripheralRemovalToast") + ' <b>' + peripheralInfo.Name + '</b>')
})
// Handle the event when a peripheral status is updated
EventsOn('PERIPHERAL_STATUS', function(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,
},
}})
})
// Set the window title
$: {
WindowSetTitle("DMXConnect - " + $showInformation.Name + ($needProjectSave ? " (unsaved)" : ""))
WindowSetTitle("DMXConnect - " + $showInformation.Name + ($needProjectSave ? " (" + $_("unsavedProjectFlag") + ")" : ""))
}
let selectedMenu = "settings"

View File

@@ -14,8 +14,8 @@
export let addable = false;
export let signalizable = false;
export let signalized = false;
export let disconnected = false;
export let selected = false;
export let status = "disconnected";
// Emit a delete event when the device is being removed
const dispatch = createEventDispatcher();
@@ -37,11 +37,11 @@
</script>
<div class="card" on:dblclick={dblclick}>
<div class="profile {selected ? "selected" : "unselected"}" on:mousedown={click} style="color: {disconnected ? $colors.first : $colors.white};">
<div>
<p>{#if disconnected}<i class='bx bx-no-signal' style="font-size:100%; color: var(--nok-color);"></i> {/if}{title}</p>
<div class="{selected ? "selected" : "unselected"} {status == "connecting" ? "waiting" : ""}" on:mousedown={click} style="color: {(status == "disconnected") ? $colors.first : $colors.white};">
<div style="z-index: 1;">
<p>{#if status == "disconnected" }<i class='bx bx-no-signal' style="font-size:100%; color: var(--nok-color);"></i> {/if}{title}</p>
<h6 class="subtitle">{type} {location != '' ? "- " : ""}<i>{location}</i></h6>
{#if disconnected}
{#if status == "disconnected"}
<h6><b>Disconnected</b></h6>
{:else}
<h6>{line1}</h6>
@@ -50,9 +50,9 @@
</div>
<div class="actions">
<InfoButton on:click={add} color="{disconnected ? $colors.first : $colors.white}" style="margin: 0.2em; display: { addable ? 'flex' : 'none' }" icon='bxs-message-square-add' interactive message={$_("projectHardwareAddTooltip")}/>
<InfoButton on:click={remove} color="{disconnected ? $colors.first : $colors.white}" style="margin: 0.2em; display: { removable ? 'flex' : 'none' }" icon='bx-trash' interactive message={$_("projectHardwareDeleteTooltip")}/>
<InfoButton style="margin: 0.2em;" background={ signalized ? $colors.orange : $colors.first } icon='bx-pulse' hide={!signalizable}/>
<InfoButton on:click={add} color="{(status == "disconnected") ? $colors.first : $colors.white}" style="margin: 0.2em; display: { addable ? 'flex' : 'none' }" icon='bxs-message-square-add' interactive message={$_("projectHardwareAddTooltip")}/>
<InfoButton on:click={remove} color="{(status == "disconnected") ? $colors.first : $colors.white}" style="margin: 0.2em; display: { removable ? 'flex' : 'none' }" icon='bx-trash' interactive message={$_("projectHardwareDeleteTooltip")}/>
<InfoButton style="margin: 0.2em; display: { (status == "activated" || status == "deactivated") ? 'flex' : 'none' }" background={ (status == "activated") ? $colors.ok : (status == "deactivated") ? $colors.nok : null} icon='bx-pulse' hide={!signalizable}/>
</div>
</div>
</div>
@@ -77,23 +77,12 @@
text-align: left;
cursor: pointer;
}
.unselected{
background-color: var(--third-color);
background: fixed;
margin: 0.2em;
padding-left: 0.3em;
padding-bottom: 0.3em;
border-radius: 0.2em;
display: flex;
justify-content: space-between;
text-align: left;
cursor: pointer;
}
.subtitle{
margin-bottom: 0.5em;
}
.actions {
margin-left: 0.2em;
z-index: 2;
}
p{
margin: 0;
@@ -102,4 +91,46 @@
margin: 0;
font-weight: 1;
}
.waiting::before{
content: '';
position: absolute;
background: linear-gradient(var(--second-color), var(--first-color));
width: 100%;
height: 60%;
animation: rotate 3s linear infinite;
}
.waiting::after{
content: '';
position: absolute;
background: var(--second-color);
inset: 2px;
border-radius: 0.2em;
}
.unselected{
background-color: var(--third-color);
background: fixed;
position: relative;
margin: 0.2em;
padding-left: 0.3em;
padding-bottom: 0.3em;
border-radius: 0.2em;
display: flex;
justify-content: space-between;
text-align: left;
cursor: pointer;
overflow: hidden;
}
/* Définition de l'animation */
@keyframes rotate {
from {
transform: rotate(0deg); /* Début de la rotation */
}
to {
transform: rotate(360deg); /* Fin de la rotation */
}
}
</style>

View File

@@ -5,61 +5,24 @@
import { t, _ } from 'svelte-i18n'
import { generateToast, needProjectSave, peripherals } from "../../stores";
import { get } from "svelte/store"
import { UpdatePeripheralSettings, GetPeripheralSettings, AddOS2LPeripheral, RemovePeripheral, ConnectFTDI, ActivateFTDI, DeactivateFTDI, DisconnectFTDI, SetDeviceFTDI, AddPeripheral } from "../../../wailsjs/go/main/App";
import { UpdatePeripheralSettings, GetPeripheralSettings, RemovePeripheral, AddPeripheral } from "../../../wailsjs/go/main/App";
import RoundedButton from "../General/RoundedButton.svelte";
function ftdiConnect(){
ConnectFTDI().then(() =>
console.log("FTDI connected"))
.catch((error) => {
console.log("Error when trying to connect: " + error)
})
}
function ftdiActivate(){
ActivateFTDI().then(() =>
console.log("FTDI activated"))
.catch((error) => {
console.log("Error when trying to activate: " + error)
})
}
function ftdiDeactivate(){
DeactivateFTDI().then(() =>
console.log("FTDI deactivated"))
.catch((error) => {
console.log("Error when trying to deactivate: " + error)
})
}
let sliderValue = 0
function ftdiSetDevice(value){
console.log("value is " + value)
SetDeviceFTDI(value).then(() =>
console.log("FTDI device set up"))
.catch((error) => {
console.log("Error when trying to set the device: " + error)
})
}
function ftdiDisconnect(){
DisconnectFTDI().then(() =>
console.log("FTDI disconnected"))
.catch((error) => {
console.log("Error when trying to disconnect: " + error)
})
}
// Add the peripheral to the project
function addPeripheral(peripheral){
// Add the peripheral to the project (backend)
AddPeripheral(peripheral.ProtocolName, peripheral.SerialNumber).then(() => {
peripherals.update((value) => {
if (value[peripheral.SerialNumber]) {
value[peripheral.SerialNumber].isSaved = true;
}
return {...value}
})
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) => {
console.log("Unable to add the peripheral to the project: " + error)
@@ -96,37 +59,20 @@
})
}
// Create the OS2L peripheral
function createOS2L(){
AddOS2LPeripheral().then(os2lDevice => {
peripherals.update(currentPeriph => {
os2lDevice.isSaved = true
os2lDevice.isDetected = true
currentPeriph[os2lDevice.SerialNumber] = os2lDevice
return {...currentPeriph}
})
$needProjectSave = true
generateToast('info', 'bx-signal-5', $_("os2lPeripheralCreatedToast"))
}).catch(error => {
console.log("Unable to add the OS2L peripheral: " + error)
generateToast('danger', 'bx-error', $_("os2lPeripheralCreateErrorToast"))
})
}
// Select the peripheral to edit its settings
let selectedPeripheralSN = null
let selectedPeripheralSettings = {}
function selectPeripheral(peripheral){
// Load the settings array if the peripheral is detected
if (peripheral.isDetected){
if (peripheral.isSaved){
GetPeripheralSettings(peripheral.ProtocolName, peripheral.SerialNumber).then((peripheralSettings) => {
selectedPeripheralSettings = peripheralSettings
// Select the current peripheral
selectedPeripheralSN = peripheral.SerialNumber
}).catch((error) => {
console.log("Unable to get the peripheral settings: " + error)
generateToast('danger', 'bx-error', $_("getPeripheralSettingsErrorToast"))
})
// Select the current peripheral
selectedPeripheralSN = peripheral.SerialNumber
}
}
@@ -153,7 +99,6 @@
boolean: Boolean,
}[typeof(selectedPeripheralSettings[settingName])] || (x => x)
selectedPeripheralSettings[settingName] = convert(settingValue)
console.log(typeof(selectedPeripheralSettings[settingName]))
let peripheralProtocolName = get(peripherals)[selectedPeripheralSN].ProtocolName
UpdatePeripheralSettings(peripheralProtocolName, selectedPeripheralSN, selectedPeripheralSettings).then(()=> {
$needProjectSave = true
@@ -175,11 +120,11 @@
if(!peripheral.isSaved)
addPeripheral(peripheral)
}}
title={peripheral.Name} type={peripheral.ProtocolName} location={peripheral.Location ? peripheral.Location : ""} line1={"S/N: " + peripheral.SerialNumber} addable={!peripheral.isSaved}/>
status="connected" 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> {$_("projectHardwareOthersLabel")}</p>
<RoundedButton on:click={createOS2L} text="Add an OS2L peripheral" icon="bx-plus-circle" tooltip="Configure an OS2L connection"/>
<RoundedButton on:click={()=>addPeripheral({Name: "OS2L connection", ProtocolName: "OS2L"})} text="Add an OS2L peripheral" icon="bx-plus-circle" tooltip="Configure an OS2L connection"/>
</div>
</div>
@@ -189,8 +134,8 @@
{#if savedPeripheralNumber > 0}
{#each Object.entries($peripherals) as [serialNumber, peripheral]}
{#if peripheral.isSaved}
<DeviceCard on:delete={() => removePeripheral(peripheral)} on:dblclick={() => removePeripheral(peripheral)} on:click={() => selectPeripheral(peripheral)}
disconnected={!peripheral.isDetected} title={peripheral.Name} type={peripheral.ProtocolName} location={peripheral.Location ? peripheral.Location : ""} line1={peripheral.SerialNumber ? "S/N: " + peripheral.SerialNumber : ""} selected={serialNumber == selectedPeripheralSN} removable signalizable/>
<DeviceCard status="{peripheral.Status}" on:delete={() => 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}
{:else}

View File

@@ -13,6 +13,7 @@
"newProjectTooltip": "Create a new project",
"openProjectString": "Open",
"openProjectTooltip": "Open an existing project",
"unsavedProjectFlag": "unsaved",
"projectPropertiesTab": "Project properties",
"projectPropertiesTooltip": "The project properties",
"projectInputOutputTab": "Hardware",

2
go.mod
View File

@@ -6,6 +6,7 @@ toolchain go1.21.3
require (
github.com/mattrtaylor/go-rtmidi v0.0.0-20220428034745-af795b1c1a79
github.com/pkg/errors v0.9.1
github.com/rs/zerolog v1.33.0
github.com/wailsapp/wails/v2 v2.9.1
gopkg.in/yaml.v2 v2.4.0
@@ -26,7 +27,6 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/samber/lo v1.38.1 // indirect
github.com/tkrajina/go-reflector v0.5.6 // indirect

View File

@@ -15,37 +15,60 @@ import (
"time"
"github.com/rs/zerolog/log"
"github.com/wailsapp/wails/v2/pkg/runtime"
)
const (
ftdiFinderExecutableName = "FTDI_finder.exe"
ftdiSenderExecutableName = "FTDI_sender.exe"
)
// FTDIFinder represents how the protocol is defined
type FTDIFinder struct {
findTicker time.Ticker // Peripherals find ticker
peripherals map[string]Peripheral // The list of peripherals handled by this finder
scanChannel chan struct{} // The channel to trigger a scan event
goWait sync.WaitGroup // Check goroutines execution
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),
peripherals: make(map[string]Peripheral),
scanChannel: make(chan struct{}),
findTicker: *time.NewTicker(findPeriod),
foundPeripherals: make(map[string]PeripheralInfo),
registeredPeripherals: make(map[string]FTDIPeripheral),
scanChannel: make(chan struct{}),
}
}
// RegisterPeripheral registers a new peripheral
func (f *FTDIFinder) RegisterPeripheral(ctx context.Context, peripheralData PeripheralInfo) (string, error) {
ftdiPeripheral, err := NewFTDIPeripheral(peripheralData)
if err != nil {
return "", fmt.Errorf("unable to create the FTDI peripheral: %v", err)
}
f.registeredPeripherals[peripheralData.SerialNumber] = *ftdiPeripheral
log.Trace().Any("periph", &ftdiPeripheral).Str("file", "FTDIFinder").Str("peripheralName", peripheralData.Name).Msg("FTDI peripheral has been created")
return peripheralData.SerialNumber, nil
}
// UnregisterPeripheral unregisters an existing peripheral
func (f *FTDIFinder) UnregisterPeripheral(peripheralID string) error {
peripheral, registered := f.registeredPeripherals[peripheralID]
if registered {
err := peripheral.Disconnect()
if err != nil {
return err
}
}
delete(f.registeredPeripherals, peripheralID)
return nil
}
//go:embed third-party/ftdi/detectFTDI.exe
var finderExe []byte
//go:embed third-party/ftdi/dmxSender.exe
var senderExe []byte
// Initialize initializes the FTDI finder
func (f *FTDIFinder) Initialize() error {
// Check platform
@@ -58,10 +81,6 @@ func (f *FTDIFinder) Initialize() error {
if err != nil {
return err
}
createExecutable(ftdiSenderExecutableName, senderExe)
if err != nil {
return err
}
log.Trace().Str("file", "FTDIFinder").Msg("FTDI finder initialized")
return nil
}
@@ -130,11 +149,6 @@ func (f *FTDIFinder) Stop() error {
if err != nil {
log.Warn().Str("file", "FTDIFinder").Str("fileName", fileToDelete).AnErr("error", err).Msg("unable to remove the executable file")
}
fileToDelete = filepath.Join(os.TempDir(), ftdiSenderExecutableName)
err = os.Remove(fileToDelete)
if err != nil {
log.Warn().Str("file", "FTDIFinder").Str("fileName", fileToDelete).AnErr("error", err).Msg("unable to remove the executable file")
}
log.Trace().Str("file", "FTDIFinder").Msg("FTDI finder stopped")
return nil
}
@@ -144,16 +158,28 @@ func (f *FTDIFinder) GetName() string {
return "FTDI"
}
// GetPeripheral gets the peripheral that correspond to the specified ID
func (f *FTDIFinder) GetPeripheral(peripheralID string) (Peripheral, bool) {
// GetPeripheralSettings gets the peripheral settings
func (f *FTDIFinder) GetPeripheralSettings(peripheralID string) (map[string]interface{}, error) {
// Return the specified peripheral
peripheral := f.peripherals[peripheralID]
if peripheral == 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, false
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, true
return peripheral.GetSettings(), 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)
}
// scanPeripherals scans the FTDI peripherals
@@ -163,8 +189,6 @@ func (f *FTDIFinder) scanPeripherals(ctx context.Context) error {
log.Trace().Str("file", "FTDIFinder").Msg("FTDI scan triggered")
ftdiPeripherals := make(map[string]Peripheral)
finder := exec.CommandContext(detectionCtx, filepath.Join(os.TempDir(), ftdiFinderExecutableName))
log.Trace().Str("file", "FTDIFinder").Msg("has executed the FIND executable")
@@ -190,42 +214,51 @@ func (f *FTDIFinder) scanPeripherals(ctx context.Context) error {
return fmt.Errorf("unable to find FTDI peripherals: %s", scannerErr.Text())
}
temporaryPeripherals := make(map[string]PeripheralInfo)
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
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
// The program output is like '0:1:2:3' where 0 is the location, 1 is the S/N, 2 is the name and 3 is the open flag [O/C]
peripheralInfo := strings.Split(peripheralString, ":")
log.Trace().Str("file", "FTDIFinder").Str("scannedString", peripheralString).Str("peripheralName", peripheralInfo[2]).Str("peripheralSN", peripheralInfo[1]).Msg("new FTDI peripheral detected")
log.Trace().Str("file", "FTDIFinder").Str("scannedString", peripheralString).Str("peripheralOpenFlag", peripheralInfo[3]).Str("peripheralName", peripheralInfo[2]).Str("peripheralSN", peripheralInfo[1]).Msg("new FTDI peripheral detected")
// Convert the location to an integer
location, err := strconv.Atoi(peripheralInfo[0])
if err != nil {
log.Warn().Str("file", "FTDIFinder").Str("peripheralName", peripheralInfo[2]).Msg("no location provided for this FTDI peripheral")
location = -1
}
// Add the peripheral to the temporary list
peripheral, err := NewFTDIPeripheral(peripheralInfo[2], peripheralInfo[1], location)
if err != nil {
return fmt.Errorf("unable to create the FTDI peripheral: %v", err)
// Add the peripheral info to the found list
temporaryPeripherals[peripheralInfo[1]] = PeripheralInfo{
Name: peripheralInfo[2],
SerialNumber: peripheralInfo[1],
IsOpen: peripheralInfo[3] == "O",
ProtocolName: "FTDI",
}
log.Trace().Any("periph", &peripheral).Str("file", "FTDIFinder").Str("peripheralName", peripheralInfo[2]).Msg("has been created")
ftdiPeripherals[peripheralInfo[1]] = peripheral
log.Trace().Any("periph", ftdiPeripherals).Str("file", "FTDIFinder").Str("peripheralName", peripheralInfo[2]).Msg("successfully added the FTDI peripheral to the finder")
// If this peripheral is already registered, connect it and activate it
peripheral, registered := f.registeredPeripherals[peripheralInfo[1]]
if registered {
runtime.EventsEmit(ctx, string(PeripheralStatus), peripheral.info, "connecting")
err := peripheral.Connect(ctx, location)
if err != nil {
log.Err(err).Str("file", "FTDIFinder").Str("peripheralSN", peripheralInfo[1]).Msg("unable to connect the peripheral")
}
runtime.EventsEmit(ctx, string(PeripheralStatus), peripheral.info, "deactivated")
time.Sleep(2 * time.Second)
err = peripheral.Activate(ctx)
if err != nil {
log.Err(err).Str("file", "FTDIFinder").Str("peripheralSN", peripheralInfo[1]).Msg("unable to activate the peripheral")
}
runtime.EventsEmit(ctx, string(PeripheralStatus), peripheral.info, "activated")
}
log.Trace().Any("periph", temporaryPeripherals).Str("file", "FTDIFinder").Str("peripheralName", peripheralInfo[2]).Msg("successfully added the FTDI peripheral to the finder")
}
// Emit the peripherals changes to the front
emitPeripheralsChanges(ctx, f.peripherals, ftdiPeripherals)
emitPeripheralsChanges(ctx, f.foundPeripherals, temporaryPeripherals)
// Store the new peripherals list
f.peripherals = ftdiPeripherals
return nil
}
// CreatePeripheral is not implemented here
func (f *FTDIFinder) CreatePeripheral(context.Context) (Peripheral, error) {
return nil, nil
}
// DeletePeripheral is not implemented here
func (f *FTDIFinder) DeletePeripheral(serialNumber string) error {
f.foundPeripherals = temporaryPeripherals
return nil
}

View File

@@ -1,32 +1,27 @@
package hardware
import (
"bufio"
"context"
_ "embed"
"fmt"
"io"
"unsafe"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
"os"
"os/exec"
)
const (
activateCommandString = 0x01
deactivateCommandString = 0x02
setCommandString = 0x03
)
/*
#cgo LDFLAGS: -L${SRCDIR}/../build/bin -ldmxSender
#include "cpp/include/dmxSenderBridge.h"
*/
import "C"
// FTDIPeripheral contains the data of an FTDI peripheral
type FTDIPeripheral struct {
name string // The name of the peripheral
serialNumber string // The S/N of the FTDI peripheral
location int // The location of the peripheral
programName string // The temp file name of the executable
info PeripheralInfo // The peripheral basic data
settings map[string]interface{} // The settings of the peripheral
dmxSender *exec.Cmd // The command to pilot the DMX sender program
dmxDevice unsafe.Pointer // The command object for piloting the DMX ouptut
stdin io.WriteCloser // For writing in the DMX sender
stdout io.ReadCloser // For reading from the DMX sender
stderr io.ReadCloser // For reading the errors
@@ -35,14 +30,12 @@ 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")
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{
name: name,
dmxSender: nil,
serialNumber: serialNumber,
location: location,
info: info,
dmxDevice: C.dmx_create(),
settings: settings,
disconnectChan: make(chan struct{}),
errorsChan: make(chan error, 1),
@@ -50,147 +43,90 @@ func NewFTDIPeripheral(name string, serialNumber string, location int) (*FTDIPer
}
// Connect connects the FTDI peripheral
func (p *FTDIPeripheral) Connect(ctx context.Context) error {
// Connect if no connection is already running
log.Trace().Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("connecting FTDI peripheral...")
// Check if the connection has already been established
if p.dmxSender != nil {
log.Warn().Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("dmxSender already initialized")
return nil
func (p *FTDIPeripheral) Connect(ctx context.Context, location int) error {
// Check if the device has already been created
if p.dmxDevice == nil {
return errors.Errorf("the DMX device has not been created!")
}
// Initialize the exec.Command for running the process
p.dmxSender = exec.Command(p.programName, fmt.Sprintf("%d", p.location))
log.Trace().Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("dmxSender instance created")
// Create the pipes for stdin, stdout, and stderr asynchronously without blocking
var err error
if p.stdout, err = p.dmxSender.StdoutPipe(); err != nil {
log.Err(err).Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("unable to create stdout pipe")
return fmt.Errorf("unable to create stdout pipe: %v", err)
}
if p.stdin, err = p.dmxSender.StdinPipe(); err != nil {
log.Err(err).Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("unable to create stdin pipe")
return fmt.Errorf("unable to create stdin pipe: %v", err)
}
if p.stderr, err = p.dmxSender.StderrPipe(); err != nil {
log.Err(err).Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("unable to create stderr pipe")
return fmt.Errorf("unable to create stderr pipe: %v", err)
// Connect the peripheral
log.Trace().Str("file", "FTDIPeripheral").Str("s/n", p.info.SerialNumber).Msg("connecting FTDI peripheral...")
err := C.dmx_connect(p.dmxDevice)
if err {
log.Error().Str("file", "FTDIPeripheral").Str("s/n", p.info.SerialNumber).Msg("unable to connect the DMX device")
return errors.Errorf("Unable to connect the DMX Device on the specified port")
}
// Launch a goroutine to read stderr asynchronously
go func() {
scanner := bufio.NewScanner(p.stderr)
for scanner.Scan() {
// Process each line read from stderr
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.Err(err).Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("error reading from stderr")
}
}()
//TODO: Destroy the object when context is done to avoid memory loss
// Launch the command asynchronously in another goroutine
go func() {
// Run the command, respecting the context cancellation
err := p.dmxSender.Run()
select {
case <-ctx.Done():
// If the context is canceled, handle it gracefully
log.Warn().Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("dmxSender was canceled by context")
return
default:
// Handle command exit normally
if err != nil {
log.Err(err).Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("error while execution of dmx sender")
if exitError, ok := err.(*exec.ExitError); ok {
log.Warn().Str("file", "FTDIPeripheral").Int("exitCode", exitError.ExitCode()).Str("s/n", p.serialNumber).Msg("dmx sender exited with code")
}
} else {
log.Debug().Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("dmx sender exited successfully")
}
}
}()
log.Debug().Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("dmxSender process started successfully")
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 {
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 {
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 {
log.Warn().Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Str("senderPath", p.programName).Msg("unable to delete the dmx sender temporary file")
return fmt.Errorf("unable to delete the temporary file: %v", err)
}
return nil
func (p *FTDIPeripheral) Disconnect() error {
// Check if the device has already been created
if p.dmxDevice == nil {
return errors.Errorf("the DMX device has not been created!")
}
log.Warn().Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("error while disconnecting: not connected")
return fmt.Errorf("unable to disconnect: not connected")
//TODO: What actions for disconnecting the DMX device?
return nil
}
// Activate activates the FTDI peripheral
func (p *FTDIPeripheral) Activate(ctx context.Context) 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 {
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
// Check if the device has already been created
if p.dmxDevice == nil {
return errors.Errorf("the DMX device has not been created!")
}
log.Warn().Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("error while activating: not connected")
return fmt.Errorf("unable to activate: not connected")
log.Trace().Str("file", "FTDIPeripheral").Str("s/n", p.info.SerialNumber).Msg("activating FTDI peripheral...")
C.dmx_activate(p.dmxDevice)
log.Trace().Str("file", "FTDIPeripheral").Str("s/n", p.info.SerialNumber).Msg("DMX device activated successfully")
return nil
}
// Deactivate deactivates the FTDI peripheral
func (p *FTDIPeripheral) Deactivate(ctx context.Context) 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 {
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
// Check if the device has already been created
if p.dmxDevice == nil {
return errors.Errorf("the DMX device has not been created!")
}
log.Warn().Str("file", "FTDIPeripheral").Str("s/n", p.serialNumber).Msg("error while deactivating: not connected")
return fmt.Errorf("unable to deactivate: not connected")
log.Trace().Str("file", "FTDIPeripheral").Str("s/n", p.info.SerialNumber).Msg("deactivating FTDI peripheral...")
C.dmx_deactivate(p.dmxDevice)
log.Trace().Str("file", "FTDIPeripheral").Str("s/n", p.info.SerialNumber).Msg("DMX device deactivated successfully")
return nil
}
// SetPeripheralSettings sets a specific setting for this peripheral
func (p *FTDIPeripheral) SetPeripheralSettings(settings map[string]interface{}) error {
// SetSettings sets a specific setting for this peripheral
func (p *FTDIPeripheral) SetSettings(settings map[string]interface{}) error {
p.settings = settings
return nil
}
// SetDeviceProperty sends a command to the specified device
func (p *FTDIPeripheral) SetDeviceProperty(ctx context.Context, uint32, channelNumber uint32, channelValue byte) error {
if p.dmxSender != nil {
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 {
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
func (p *FTDIPeripheral) SetDeviceProperty(ctx context.Context, channelNumber uint32, channelValue byte) error {
// Check if the device has already been created
if p.dmxDevice == nil {
return errors.Errorf("the DMX device has not been created!")
}
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")
log.Trace().Str("file", "FTDIPeripheral").Str("s/n", p.info.SerialNumber).Msg("setting device property on FTDI peripheral...")
C.dmx_setValue(p.dmxDevice, C.int(channelNumber), C.int(channelValue))
log.Trace().Str("file", "FTDIPeripheral").Str("s/n", p.info.SerialNumber).Msg("device property set on FTDI peripheral successfully")
return nil
}
// GetSettings gets the peripheral settings
@@ -200,9 +136,5 @@ func (p *FTDIPeripheral) GetSettings() map[string]interface{} {
// GetInfo gets all the peripheral information
func (p *FTDIPeripheral) GetInfo() PeripheralInfo {
return PeripheralInfo{
Name: p.name,
SerialNumber: p.serialNumber,
ProtocolName: "FTDI",
}
return p.info
}

View File

@@ -15,18 +15,18 @@ import (
// MIDIFinder represents how the protocol is defined
type MIDIFinder struct {
findTicker time.Ticker // Peripherals find ticker
peripherals map[string]Peripheral // The list of peripherals
scanChannel chan struct{} // The channel to trigger a scan event
goWait sync.WaitGroup // Check goroutines execution
findTicker time.Ticker // Peripherals find ticker
registeredPeripherals map[string]MIDIPeripheral // The list of peripherals
scanChannel chan struct{} // The channel to trigger a scan event
goWait sync.WaitGroup // Check goroutines execution
}
// NewMIDIFinder creates a new DMXUSB protocol
func NewMIDIFinder(findPeriod time.Duration) *MIDIFinder {
log.Trace().Str("file", "MIDIFinder").Msg("MIDI finder created")
return &MIDIFinder{
findTicker: *time.NewTicker(findPeriod),
peripherals: make(map[string]Peripheral),
findTicker: *time.NewTicker(findPeriod),
registeredPeripherals: make(map[string]MIDIPeripheral),
}
}
@@ -36,6 +36,30 @@ func (f *MIDIFinder) Initialize() error {
return nil
}
// RegisterPeripheral registers a new peripheral
func (f *MIDIFinder) RegisterPeripheral(ctx context.Context, peripheralData PeripheralInfo) (string, error) {
peripheral, err := NewMIDIPeripheral(peripheralData)
if err != nil {
return "", fmt.Errorf("unable to create the MIDI peripheral: %v", err)
}
f.registeredPeripherals[peripheralData.SerialNumber] = *peripheral
log.Trace().Any("periph", &peripheral).Str("file", "MIDIFinder").Str("peripheralName", peripheralData.Name).Msg("FTDI peripheral has been created")
return peripheralData.SerialNumber, nil
}
// UnregisterPeripheral unregisters an existing peripheral
func (f *MIDIFinder) UnregisterPeripheral(peripheralID string) error {
peripheral, registered := f.registeredPeripherals[peripheralID]
if registered {
err := peripheral.Disconnect()
if err != nil {
return err
}
}
delete(f.registeredPeripherals, peripheralID)
return nil
}
// Start starts the finder and search for peripherals
func (f *MIDIFinder) Start(ctx context.Context) error {
f.goWait.Add(1)
@@ -79,16 +103,28 @@ func (f *MIDIFinder) GetName() string {
return "MIDI"
}
// GetPeripheral gets the peripheral that correspond to the specified ID
func (f *MIDIFinder) GetPeripheral(peripheralID string) (Peripheral, bool) {
// GetPeripheralSettings gets the peripheral settings
func (f *MIDIFinder) GetPeripheralSettings(peripheralID string) (map[string]interface{}, error) {
// Return the specified peripheral
peripheral, found := f.peripherals[peripheralID]
peripheral, found := f.registeredPeripherals[peripheralID]
if !found {
log.Error().Str("file", "MIDIFinder").Str("peripheralID", peripheralID).Msg("unable to get this peripheral in the MIDI finder")
return nil, false
log.Error().Str("file", "MIDIFinder").Str("peripheralID", peripheralID).Msg("unable to get this peripheral from the FTDI finder")
return nil, fmt.Errorf("unable to found the peripheral")
}
log.Trace().Str("file", "MIDIFinder").Str("peripheralID", peripheralID).Msg("MIDI peripheral found in the driver")
return peripheral, true
log.Debug().Str("file", "MIDIFinder").Str("peripheralID", peripheralID).Msg("peripheral found by the FTDI finder")
return peripheral.GetSettings(), nil
}
// SetPeripheralSettings sets the peripheral settings
func (f *MIDIFinder) SetPeripheralSettings(peripheralID string, settings map[string]interface{}) error {
// Return the specified peripheral
peripheral, found := f.registeredPeripherals[peripheralID]
if !found {
log.Error().Str("file", "MIDIFinder").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", "MIDIFinder").Str("peripheralID", peripheralID).Msg("peripheral found by the FTDI finder")
return peripheral.SetSettings(settings)
}
func splitStringAndNumber(input string) (string, int, error) {
@@ -119,7 +155,7 @@ func (f *MIDIFinder) ForceScan() {
// scanPeripherals scans the MIDI peripherals
func (f *MIDIFinder) scanPeripherals(ctx context.Context) error {
midiPeripherals := make(map[string]Peripheral)
// midiPeripherals := make(map[string]Peripheral)
log.Trace().Str("file", "MIDIFinder").Msg("opening MIDI scanner port...")
midiScanner, err := rtmidi.NewMIDIInDefault()
if err != nil {
@@ -148,8 +184,8 @@ func (f *MIDIFinder) scanPeripherals(ctx context.Context) error {
}
log.Info().Str("file", "MIDIFinder").Str("name", name).Int("location", location).Msg("MIDI peripheral found")
// Add the peripheral to the temporary list
sn := strings.ToLower(strings.Replace(name, " ", "_", -1))
midiPeripherals[sn] = NewMIDIPeripheral(name, location, sn)
// 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(f.peripherals, midiPeripherals)
@@ -159,7 +195,7 @@ func (f *MIDIFinder) scanPeripherals(ctx context.Context) error {
// emitPeripheralsEvents(ctx, addedList, PeripheralArrival)
log.Info().Str("file", "MIDIFinder").Msg("MIDI add list emitted to the front")
// Store the new peripherals list
f.peripherals = midiPeripherals
// f.peripherals = midiPeripherals
return nil
}

View File

@@ -8,22 +8,18 @@ import (
// 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
serialNumber string // The S/N of the peripheral
settings map[string]interface{} // The settings of the peripheral
info PeripheralInfo // The peripheral info
location int // The location of the peripheral
settings map[string]interface{} // The settings of the peripheral
}
// NewMIDIPeripheral creates a new MIDI peripheral
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")
settings := make(map[string]interface{})
func NewMIDIPeripheral(peripheralData PeripheralInfo) (*MIDIPeripheral, error) {
log.Trace().Str("file", "MIDIPeripheral").Str("name", peripheralData.Name).Str("s/n", peripheralData.SerialNumber).Msg("MIDI peripheral created")
return &MIDIPeripheral{
name: name,
location: location,
serialNumber: serialNumber,
settings: settings,
}
info: peripheralData,
settings: peripheralData.Settings,
}, nil
}
// Connect connects the MIDI peripheral
@@ -32,7 +28,7 @@ func (p *MIDIPeripheral) Connect(ctx context.Context) error {
}
// Disconnect disconnects the MIDI peripheral
func (p *MIDIPeripheral) Disconnect(ctx context.Context) error {
func (p *MIDIPeripheral) Disconnect() error {
return nil
}
@@ -46,8 +42,8 @@ func (p *MIDIPeripheral) Deactivate(ctx context.Context) error {
return nil
}
// SetPeripheralSettings sets a specific setting for this peripheral
func (p *MIDIPeripheral) SetPeripheralSettings(settings map[string]interface{}) error {
// SetSettings sets a specific setting for this peripheral
func (p *MIDIPeripheral) SetSettings(settings map[string]interface{}) error {
p.settings = settings
return nil
}
@@ -64,9 +60,5 @@ func (p *MIDIPeripheral) GetSettings() map[string]interface{} {
// GetInfo gets the peripheral information
func (p *MIDIPeripheral) GetInfo() PeripheralInfo {
return PeripheralInfo{
Name: p.name,
ProtocolName: "MIDI",
SerialNumber: p.serialNumber,
}
return p.info
}

View File

@@ -11,14 +11,14 @@ import (
// OS2LFinder represents how the protocol is defined
type OS2LFinder struct {
peripherals map[string]Peripheral // The list of peripherals
registeredPeripherals map[string]OS2LPeripheral // The list of found peripherals
}
// NewOS2LFinder creates a new OS2L finder
func NewOS2LFinder() *OS2LFinder {
log.Trace().Str("file", "OS2LFinder").Msg("OS2L finder created")
return &OS2LFinder{
peripherals: make(map[string]Peripheral),
registeredPeripherals: make(map[string]OS2LPeripheral),
}
}
@@ -28,21 +28,37 @@ func (f *OS2LFinder) Initialize() error {
return nil
}
// CreatePeripheral creates a new OS2L peripheral
func (f *OS2LFinder) CreatePeripheral(ctx context.Context) (Peripheral, error) {
// RegisterPeripheral registers a new peripheral
func (f *OS2LFinder) RegisterPeripheral(ctx context.Context, peripheralData PeripheralInfo) (string, error) {
// Create a random serial number for this peripheral
randomSerialNumber := strings.ToUpper(fmt.Sprintf("%08x", rand.Intn(1<<32)))
log.Trace().Str("file", "OS2LFinder").Str("serialNumber", randomSerialNumber).Msg("OS2L peripheral created")
peripheral := NewOS2LPeripheral("OS2L", randomSerialNumber)
f.peripherals[randomSerialNumber] = peripheral
log.Info().Str("file", "OS2LFinder").Str("serialNumber", randomSerialNumber).Msg("OS2L peripheral created and registered")
return peripheral, nil
peripheralData.SerialNumber = strings.ToUpper(fmt.Sprintf("%08x", rand.Intn(1<<32)))
log.Trace().Str("file", "OS2LFinder").Str("serialNumber", peripheralData.SerialNumber).Msg("OS2L peripheral created")
os2lPeripheral, err := NewOS2LPeripheral(peripheralData)
if err != nil {
return "", fmt.Errorf("unable to create the OS2L peripheral: %v", err)
}
// Connect this peripheral
err = os2lPeripheral.Connect(ctx)
if err != nil {
return "", err
}
f.registeredPeripherals[peripheralData.SerialNumber] = *os2lPeripheral
log.Trace().Any("periph", &os2lPeripheral).Str("file", "OS2LFinder").Str("peripheralName", peripheralData.Name).Msg("OS2L peripheral has been created")
return peripheralData.SerialNumber, nil
}
// DeletePeripheral removes an OS2L peripheral
func (f *OS2LFinder) DeletePeripheral(serialNumber string) error {
delete(f.peripherals, serialNumber)
log.Info().Str("file", "OS2LFinder").Str("serialNumber", serialNumber).Msg("OS2L peripheral removed")
// UnregisterPeripheral unregisters an existing peripheral
func (f *OS2LFinder) UnregisterPeripheral(peripheralID string) error {
peripheral, registered := f.registeredPeripherals[peripheralID]
if registered {
err := peripheral.Disconnect()
if err != nil {
return err
}
}
delete(f.registeredPeripherals, peripheralID)
return nil
}
@@ -51,16 +67,28 @@ func (f *OS2LFinder) GetName() string {
return "OS2L"
}
// GetPeripheral gets the peripheral that correspond to the specified ID
func (f *OS2LFinder) GetPeripheral(peripheralID string) (Peripheral, bool) {
// GetPeripheralSettings gets the peripheral settings
func (f *OS2LFinder) GetPeripheralSettings(peripheralID string) (map[string]interface{}, error) {
// Return the specified peripheral
peripheral, found := f.peripherals[peripheralID]
peripheral, found := f.registeredPeripherals[peripheralID]
if !found {
log.Error().Str("file", "OS2LFinder").Str("peripheralID", peripheralID).Msg("unable to get this peripheral in the OS2L finder")
return nil, false
log.Error().Str("file", "OS2LFinder").Str("peripheralID", peripheralID).Msg("unable to get this peripheral from the OS2L finder")
return nil, fmt.Errorf("unable to found the peripheral")
}
log.Trace().Str("file", "OS2LFinder").Str("peripheralID", peripheralID).Msg("OS2L peripheral found in the finder")
return peripheral, true
log.Debug().Str("file", "OS2LFinder").Str("peripheralID", peripheralID).Msg("peripheral found by the OS2L finder")
return peripheral.GetSettings(), nil
}
// SetPeripheralSettings sets the peripheral settings
func (f *OS2LFinder) SetPeripheralSettings(peripheralID string, settings map[string]interface{}) error {
// Return the specified peripheral
peripheral, found := f.registeredPeripherals[peripheralID]
if !found {
log.Error().Str("file", "OS2LFinder").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", "OS2LFinder").Str("peripheralID", peripheralID).Msg("peripheral found by the FTDI finder")
return peripheral.SetSettings(settings)
}
// Start starts the finder

View File

@@ -3,37 +3,42 @@ package hardware
import (
"context"
"fmt"
"time"
"github.com/rs/zerolog/log"
"github.com/wailsapp/wails/v2/pkg/runtime"
)
// OS2LPeripheral contains the data of an OS2L peripheral
type OS2LPeripheral struct {
name string // The name of the peripheral
serialNumber string // The serial number of the peripheral
serverIP string // OS2L server IP
serverPort int // OS2L server port
info PeripheralInfo // The basic info for this peripheral
serverIP string // OS2L server IP
serverPort int // OS2L server port
}
// 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")
func NewOS2LPeripheral(peripheralData PeripheralInfo) (*OS2LPeripheral, error) {
log.Trace().Str("file", "OS2LPeripheral").Str("name", peripheralData.Name).Str("s/n", peripheralData.SerialNumber).Msg("OS2L peripheral created")
return &OS2LPeripheral{
name: name,
serverIP: "127.0.0.1",
serverPort: 9995,
serialNumber: serialNumber,
}
info: peripheralData,
serverIP: "127.0.0.1",
serverPort: 9005,
}, nil
}
// Connect connects the MIDI peripheral
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")
time.Sleep(5 * time.Second)
runtime.EventsEmit(ctx, string(PeripheralStatus), p.info, "disconnected")
}()
return nil
}
// Disconnect disconnects the MIDI peripheral
func (p *OS2LPeripheral) Disconnect(ctx context.Context) error {
func (p *OS2LPeripheral) Disconnect() error {
log.Info().Str("file", "OS2LPeripheral").Msg("OS2L peripheral disconnected")
return nil
}
@@ -50,8 +55,8 @@ func (p *OS2LPeripheral) Deactivate(ctx context.Context) error {
return nil
}
// SetPeripheralSettings sets a specific setting for this peripheral
func (p *OS2LPeripheral) SetPeripheralSettings(settings map[string]interface{}) error {
// SetSettings sets a specific setting for this peripheral
func (p *OS2LPeripheral) SetSettings(settings map[string]interface{}) error {
// Check if the IP exists
serverIP, found := settings["os2lIp"]
if !found {
@@ -95,9 +100,5 @@ func (p *OS2LPeripheral) GetSettings() map[string]interface{} {
// GetInfo gets the peripheral information
func (p *OS2LPeripheral) GetInfo() PeripheralInfo {
return PeripheralInfo{
Name: p.name,
SerialNumber: p.serialNumber,
ProtocolName: "OS2L",
}
return p.info
}

21
hardware/cpp/generate.bat Normal file
View File

@@ -0,0 +1,21 @@
@REM windres dmxSender.rc dmxSender.o
@REM windres detectFTDI.rc detectFTDI.o
@REM g++ -o dmxSender.exe dmxSender.cpp dmxSender.o -I"include" -L"lib" -lftd2xx -mwindows
@REM g++ -o detectFTDI.exe detectFTDI.cpp detectFTDI.o -I"include" -L"lib" -lftd2xx -mwindows
@REM g++ -o dmxSender.exe dmxSender.cpp -I"include" -L"lib" -lftd2xx
@REM g++ -o detectFTDI.exe detectFTDI.cpp -I"include" -L"lib" -lftd2xx
@REM g++ -c dmxSender.cpp -o dmxSender.o -I"include" -L"lib" -lftd2xx -mwindows
@REM g++ -c dmxSender_wrapper.cpp -o dmxSender_wrapper.o -I"include" -L"lib" -lftd2xx -mwindows
@REM g++ -shared -o ../../build/bin/libdmxSender.dll src/dmxSender.cpp -fPIC -Wl,--out-implib,../../build/bin/libdmxSender.dll.a -L"lib" -lftd2xx
@REM Compiling DMXSENDER library
g++ -shared -o ../../build/bin/libdmxSender.dll src/dmxSender.cpp -fPIC -L"lib" -lftd2xx
@REM g++ -shared -o libdmxSender.so dmxSender.cpp -fPIC -I"include" -L"lib" -lftd2xx -mwindows

View File

@@ -0,0 +1,15 @@
// Declare the C++ function from the shared library
typedef void DMXDevice;
extern DMXDevice* dmx_create();
extern void* dmx_destroy(DMXDevice* dev);
extern bool dmx_connect(DMXDevice* dev);
extern void dmx_activate(DMXDevice* dev);
extern void dmx_deactivate(DMXDevice* dev);
extern void dmx_setValue(DMXDevice* dev, int channel, int value);

BIN
hardware/cpp/lib/ftd2xx.lib Normal file

Binary file not shown.

View File

@@ -0,0 +1,127 @@
//dmxSender.cpp
#include "dmxSender.h"
#define DMX_START_CODE 0x00
#define BREAK_DURATION_US 110
#define MAB_DURATION_US 16
#define DMX_CHANNELS 512
#define FREQUENCY 44
#define INTERVAL (1000000 / FREQUENCY)
// Initialize default values for starting the DMX device
DMXDevice::DMXDevice(){
ftHandle = nullptr;
isOutputActivated = false;
}
// Properly close the DMX device
DMXDevice::~DMXDevice(){
deactivate();
if (ftHandle != nullptr){
FT_Close(ftHandle);
ftHandle = nullptr;
}
}
// Connect the device on a specific port
bool DMXDevice::connect(int port){
ftStatus = FT_Open(port, &ftHandle);
if (ftStatus != FT_OK) {
return true;
}
ftStatus = FT_SetBaudRate(ftHandle, 250000);
ftStatus |= FT_SetDataCharacteristics(ftHandle, 8, FT_STOP_BITS_2, FT_PARITY_NONE); // 8 bits, no parity, 1 stop bit
ftStatus |= FT_SetFlowControl(ftHandle, FT_FLOW_NONE, 0, 0);
if (ftStatus != FT_OK) {
FT_Close(ftHandle);
return true;
}
// Send the DMX frames
std::thread updateThread([this]() {
this->sendDMX(ftHandle);
});
return false;
}
// Activate the DMX flow
void DMXDevice::activate(){
isOutputActivated.store(true);
}
// Deactivate the DMX flow
void DMXDevice::deactivate(){
isOutputActivated.store(false);
}
// Set the value of a DMX channe
void DMXDevice::setValue(int channel, int value){
dmxData[channel].store(value);
}
// Send a break line
void DMXDevice::sendBreak(FT_HANDLE ftHandle) {
FT_SetBreakOn(ftHandle); // Set BREAK ON
std::this_thread::sleep_for(std::chrono::microseconds(BREAK_DURATION_US));
FT_SetBreakOff(ftHandle); // Set BREAK OFF
}
// Continuously send the DMX frame
void DMXDevice::sendDMX(FT_HANDLE ftHandle) {
while (true) {
if(isOutputActivated){
// Send the BREAK
sendBreak(ftHandle);
// Send the MAB
std::this_thread::sleep_for(std::chrono::microseconds(MAB_DURATION_US));
DWORD bytesWritten = 0;
// Send the DMX frame
FT_STATUS status = FT_Write(ftHandle, dmxData, DMX_CHANNELS, &bytesWritten);
if (status != FT_OK || bytesWritten != DMX_CHANNELS) { // Error detected when trying to send the frame. Deactivate the line.
deactivate();
continue;
}
// Wait before sending the next frame
std::this_thread::sleep_for(std::chrono::microseconds(INTERVAL - BREAK_DURATION_US - MAB_DURATION_US));
}
}
}
// Linkable functions from Golang
extern "C" {
// Create a new DMX device
DMXDevice* dmx_create() {
return new DMXDevice();
}
// Destroy a DMX device
void dmx_destroy(DMXDevice* dev) {
dev->~DMXDevice();
}
// Connect a DMX device
bool dmx_connect(DMXDevice* dev, int port) {
return dev->connect(port);
}
// Activate a DMX device
void dmx_activate(DMXDevice* dev) {
dev->activate();
}
// Deactivate a DMX device
void dmx_deactivate(DMXDevice* dev) {
dev->deactivate();
}
// Set the channel value of a DMX device
void dmx_setValue(DMXDevice* dev, int channel, int value) {
dev->setValue(channel, value);
}
}

View File

@@ -0,0 +1,49 @@
// dmxSender.h
#pragma once
#include <vector>
#include <cstdint>
#include <atomic>
#include <thread>
#include "ftd2xx.h"
#define DMX_START_CODE 0x00
#define BREAK_DURATION_US 110
#define MAB_DURATION_US 16
#define DMX_CHANNELS 512
#define FREQUENCY 44
#define INTERVAL (1000000 / FREQUENCY)
class DMXDevice {
public:
// Initialize default values for starting the DMX device
DMXDevice();
// Properly close the DMX device
~DMXDevice();
// Connect the device on a specific port
bool connect(int port);
// Activate the DMX flow
void activate();
// Deactivate the DMX flow
void deactivate();
// Set the value of a DMX channel
void setValue(int channel, int value);
private:
FT_STATUS ftStatus; // FTDI peripheral status
FT_HANDLE ftHandle = nullptr; // FTDI object
std::atomic<unsigned char> dmxData[DMX_CHANNELS + 1]; // For storing dynamically the DMX data
std::atomic<bool> isOutputActivated = false; // Boolean to start/stop the DMX flow
// Send a break line
void sendBreak(FT_HANDLE ftHandle);
// Continuously send the DMX frame
void sendDMX(FT_HANDLE ftHandle);
};

1667
hardware/cpp/src/ftd2xx.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -19,6 +19,8 @@ const (
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
)
@@ -93,19 +95,19 @@ 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)
}
// // 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 {
@@ -130,13 +132,13 @@ func (h *HardwareManager) Stop() 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]Peripheral, newPeripherals map[string]Peripheral) {
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].GetInfo())
runtime.EventsEmit(ctx, string(PeripheralRemoval), oldPeripherals[oldPeriphName])
log.Trace().Str("file", "hardware").Str("event", string(PeripheralRemoval)).Msg("emit peripheral removal event")
}
}
@@ -144,7 +146,7 @@ func emitPeripheralsChanges(ctx context.Context, oldPeripherals map[string]Perip
// 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].GetInfo())
runtime.EventsEmit(ctx, string(PeripheralArrival), newPeripherals[newPeriphName])
log.Trace().Str("file", "hardware").Str("event", string(PeripheralArrival)).Msg("emit peripheral arrival event")
}
}

View File

@@ -4,12 +4,12 @@ import "context"
// Peripheral represents the methods used to manage a peripheral (input or output hardware)
type Peripheral interface {
Connect(context.Context) error // Connect the peripheral
Disconnect(context.Context) error // Disconnect the peripheral
Activate(context.Context) error // Activate the peripheral
Deactivate(context.Context) error // Deactivate the peripheral
SetPeripheralSettings(map[string]interface{}) error // Set a peripheral setting
SetDeviceProperty(context.Context, uint32, uint32, byte) error // Update a device property
Connect(context.Context) error // Connect 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
SetDeviceProperty(context.Context, uint32, byte) error // Update a device property
GetInfo() PeripheralInfo // Get the peripheral information
GetSettings() map[string]interface{} // Get the peripheral settings
@@ -20,17 +20,19 @@ 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
}
// PeripheralFinder represents how compatible peripheral drivers are implemented
type PeripheralFinder interface {
Initialize() error // Initializes the protocol
Start(context.Context) error // Start the detection
Stop() error // Stop the detection
ForceScan() // Explicitly scans for peripherals
CreatePeripheral(ctx context.Context) (Peripheral, error) // Creates a new peripheral
DeletePeripheral(serialNumber string) error // Removes a peripheral
GetName() string // Get the name of the finder
GetPeripheral(string) (Peripheral, bool) // Get the peripheral
Initialize() error // Initializes the protocol
Start(context.Context) error // Start the detection
Stop() error // Stop the detection
ForceScan() // Explicitly scans for peripherals
RegisterPeripheral(context.Context, PeripheralInfo) (string, error) // Registers a new peripheral data
UnregisterPeripheral(string) 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
}

View File

@@ -28,8 +28,17 @@ int main() {
for (int i = 0; i < numDevs; i++) {
if (devInfo[i].SerialNumber[0] != '\0') {
std::cout << i << ":" << devInfo[i].SerialNumber << ":" << devInfo[i].Description << std::endl;
std::cout << i << ":" << devInfo[i].SerialNumber << ":" << devInfo[i].Description << ":";
// Add information about the hardware open state
if (devInfo[i].Flags & FT_FLAGS_OPENED) {
std::cout << "O" << std::endl;
} else {
std::cout << "C" << std::endl;
}
}
}
free(devInfo);
}
}

View File

@@ -1,142 +0,0 @@
#include <cstring>
#include <errno.h>
#include <fcntl.h>
#include <iostream>
#include <unistd.h>
#include <fstream>
#include <windows.h>
#include <string>
#include <math.h>
#include <chrono>
#include <thread>
#include <cstdint>
#include <vector>
#include <atomic>
#include <fcntl.h>
#include <io.h>
#include "ftd2xx.h"
#define DMX_START_CODE 0x00
#define BREAK_DURATION_US 110
#define MAB_DURATION_US 16
#define DMX_CHANNELS 512
#define FREQUENCY 44
#define INTERVAL (1000000 / FREQUENCY)
std::atomic<unsigned char> dmxData[DMX_CHANNELS + 1];
std::atomic<bool> isOutputActivated = false;
using namespace std;
void sendBreak(FT_HANDLE ftHandle) {
FT_SetBreakOn(ftHandle); // Envoie le signal de BREAK
std::this_thread::sleep_for(std::chrono::microseconds(BREAK_DURATION_US));
FT_SetBreakOff(ftHandle); // Arrête le signal de BREAK
}
void sendDMX(FT_HANDLE ftHandle) {
while (true) {
if(isOutputActivated){
// Envoi du BREAK suivi du MAB
sendBreak(ftHandle);
std::this_thread::sleep_for(std::chrono::microseconds(MAB_DURATION_US));
// Envoi de la trame DMX512
DWORD bytesWritten = 0;
// Envoyer la trame DMX512
FT_STATUS status = FT_Write(ftHandle, dmxData, DMX_CHANNELS, &bytesWritten);
if (status != FT_OK || bytesWritten != DMX_CHANNELS) {
std::cerr << "Unable to send the DMX frame" << std::endl;
FT_Close(ftHandle);
return;
}
// Attendre avant d'envoyer la prochaine trame
std::this_thread::sleep_for(std::chrono::microseconds(INTERVAL - BREAK_DURATION_US - MAB_DURATION_US));
}
}
}
void processCommand(const char* buffer) {
if (buffer[0] == 0x01) {
// Activate the DMX512
isOutputActivated.store(true);
} else if(buffer[0] == 0x02) {
// Deactivate the DMX512
isOutputActivated.store(false);
} else if(buffer[0] == 0x03) {
// Get the channel number
uint16_t channelNumber = (static_cast<unsigned char>(buffer[1]) |
(static_cast<unsigned char>(buffer[2]) << 8));
// Get the channel value
uint8_t channelValue = static_cast<unsigned char>(buffer[3]);
// // Update the DMX array
dmxData[channelNumber].store(channelValue);
} else if(buffer[0] == 0x04) {
// Close this sender
exit(0);
} else {
std::cerr << "Unknown command" << endl;
}
}
// Entry point
int main(int argc, char* argv[]) {
#ifdef _WIN32
_setmode(_fileno(stdin), _O_BINARY);
#endif
FT_STATUS ftStatus;
FT_HANDLE ftHandle = nullptr;
// Check if the serial port is specified
if (argc != 2) {
std::cerr << "Invalid call to DMX sender" << std::endl;
return 1;
}
// Connect the serial port
int deviceDev;
try {
deviceDev = std::stoi(argv[1]);
}catch(const std::exception& e){
std::cerr << "Invalid call to DMX sender" << std::endl;
return 1;
}
ftStatus = FT_Open(deviceDev, &ftHandle);
if (ftStatus != FT_OK) {
std::cerr << "Unable to open the FTDI device" << std::endl;
return 1;
}
ftStatus = FT_SetBaudRate(ftHandle, 250000);
ftStatus |= FT_SetDataCharacteristics(ftHandle, 8, FT_STOP_BITS_2, FT_PARITY_NONE); // 8 bits, pas de parité, 1 bit de stop
ftStatus |= FT_SetFlowControl(ftHandle, FT_FLOW_NONE, 0, 0);
if (ftStatus != FT_OK) {
std::cerr << "Unable to configure the FTDI device" << std::endl;
FT_Close(ftHandle);
return 1;
}
// Send the DMX frames
std::thread updateThread(sendDMX, ftHandle);
// Intercept commands from the GO program
char buffer[4]; // Tampon pour stocker les 4 octets d'une commande
while (true) {
std::cin.read(buffer, 4); // Attente bloquante jusqu'à ce que 4 octets soient lus
if (std::cin.gcount() == 4) { // Vérifier que 4 octets ont été lus
processCommand(buffer);
} else if (std::cin.eof()) {
std::cerr << "Fin de l'entrée standard (EOF)" << std::endl;
break;
} else if (std::cin.fail()) {
std::cerr << "Erreur de lecture sur stdin" << std::endl;
break;
}
}
return 0;
}

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="DMXSender" type="win32"/>
<description>DMXSender</description>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>

View File

@@ -1,8 +0,0 @@
windres dmxSender.rc dmxSender.o
windres detectFTDI.rc detectFTDI.o
g++ -o dmxSender.exe dmxSender.cpp dmxSender.o -I"include" -L"lib" -lftd2xx -mwindows
g++ -o detectFTDI.exe detectFTDI.cpp detectFTDI.o -I"include" -L"lib" -lftd2xx -mwindows
@REM g++ -o dmxSender.exe dmxSender.cpp -I"include" -L"lib" -lftd2xx
@REM g++ -o detectFTDI.exe detectFTDI.cpp -I"include" -L"lib" -lftd2xx

View File

@@ -8,41 +8,48 @@ import (
)
// AddPeripheral adds a peripheral to the project
func (a *App) AddPeripheral(protocolName string, peripheralID string) error {
func (a *App) AddPeripheral(peripheralData hardware.PeripheralInfo) (string, error) {
// Get the peripheral from its finder
p, found := a.hardwareManager.GetPeripheral(protocolName, peripheralID)
if !found {
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)
f, err := a.hardwareManager.GetFinder(peripheralData.ProtocolName)
if err != nil {
log.Error().Str("file", "peripheral").Str("protocolName", peripheralData.ProtocolName).Msg("unable to found the specified finder")
return "", fmt.Errorf("unable to found the peripheral ID '%s'", peripheralData.SerialNumber)
}
// Register this new peripheral
serialNumber, err := f.RegisterPeripheral(a.ctx, peripheralData)
if err != nil {
log.Trace().Str("file", "peripheral").Str("protocolName", peripheralData.ProtocolName).Str("periphID", serialNumber).Msg("device registered to the finder")
return "", fmt.Errorf("unable to register the peripheral '%s'", serialNumber)
}
// Rewrite the serialnumber for virtual devices
peripheralData.SerialNumber = serialNumber
// Add the peripheral ID to the project
if a.projectInfo.PeripheralsInfo == nil {
a.projectInfo.PeripheralsInfo = make(map[string]hardware.PeripheralInfo)
}
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
a.projectInfo.PeripheralsInfo[peripheralData.SerialNumber] = peripheralData
log.Info().Str("file", "peripheral").Str("protocolName", peripheralData.ProtocolName).Str("periphID", peripheralData.SerialNumber).Msg("peripheral added to project")
return peripheralData.SerialNumber, nil
}
// GetPeripheralSettings gets the peripheral settings
func (a *App) GetPeripheralSettings(protocolName, peripheralID string) (map[string]interface{}, error) {
// Get the peripheral from its finder
p, found := a.hardwareManager.GetPeripheral(protocolName, peripheralID)
if !found {
f, err := a.hardwareManager.GetFinder(protocolName)
if err != nil {
log.Error().Str("file", "peripheral").Str("protocolName", protocolName).Str("periphID", peripheralID).Msg("unable to found the specified peripheral")
return nil, fmt.Errorf("unable to found the peripheral ID '%s'", peripheralID)
}
// Return the peripheral settings
return p.GetSettings(), nil
return f.GetPeripheralSettings(peripheralID)
}
// UpdatePeripheralSettings updates a specific setting of a peripheral
func (a *App) UpdatePeripheralSettings(protocolName, peripheralID string, settings map[string]interface{}) error {
// Get the peripheral from its finder
p, found := a.hardwareManager.GetPeripheral(protocolName, peripheralID)
if !found {
// Sets the settings with the finder
f, err := a.hardwareManager.GetFinder(protocolName)
if err != nil {
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)
}
@@ -54,104 +61,65 @@ func (a *App) UpdatePeripheralSettings(protocolName, peripheralID string, settin
pInfo.Settings = settings
a.projectInfo.PeripheralsInfo[peripheralID] = pInfo
// Apply changes in the peripheral
return p.SetPeripheralSettings(pInfo.Settings)
return f.SetPeripheralSettings(peripheralID, pInfo.Settings)
}
// RemovePeripheral adds a peripheral to the project
// RemovePeripheral removes a peripheral from the project
func (a *App) RemovePeripheral(protocolName string, peripheralID string) error {
// TODO: Disconnect the peripheral
// Unregister the peripheral from the finder
f, err := a.hardwareManager.GetFinder(protocolName)
if err != nil {
log.Err(err).Str("file", "peripherals").Str("protocolName", protocolName).Msg("unable to find the finder")
return fmt.Errorf("unable to find the finder")
}
err = f.UnregisterPeripheral(peripheralID)
if err != nil {
log.Err(err).Str("file", "peripherals").Str("peripheralID", peripheralID).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")
return nil
}
// AddOS2LPeripheral adds a new OS2L peripheral
func (a *App) AddOS2LPeripheral() (hardware.PeripheralInfo, error) {
// Get the OS2L finder
os2lDriver, err := a.hardwareManager.GetFinder("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 finder
os2lPeripheral, err := os2lDriver.CreatePeripheral(a.ctx)
if err != 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)
}
// FOR TESTING PURPOSE ONLY
func (a *App) ConnectFTDI() error {
// Connect the FTDI
driver, err := a.hardwareManager.GetFinder("FTDI")
if err != nil {
return err
}
periph, found := driver.GetPeripheral("A50285BI")
if !found {
return fmt.Errorf("unable to find the peripheral s/n %s", "A50285BI")
}
return periph.Connect(a.ctx)
}
// func (a *App) ActivateFTDI() error {
// // Connect the FTDI
// driver, err := a.hardwareManager.GetFinder("FTDI")
// if err != nil {
// return err
// }
// periph, found := driver.GetPeripheral("A50285BI")
// if !found {
// return fmt.Errorf("unable to find the peripheral s/n %s", "A50285BI")
// }
// return periph.Activate(a.ctx)
// }
func (a *App) ActivateFTDI() error {
// Connect the FTDI
driver, err := a.hardwareManager.GetFinder("FTDI")
if err != nil {
return err
}
periph, found := driver.GetPeripheral("A50285BI")
if !found {
return fmt.Errorf("unable to find the peripheral s/n %s", "A50285BI")
}
return periph.Activate(a.ctx)
}
// func (a *App) SetDeviceFTDI(channelValue byte) error {
// // Connect the FTDI
// driver, err := a.hardwareManager.GetFinder("FTDI")
// if err != nil {
// return err
// }
// periph, found := driver.GetPeripheral("A50285BI")
// if !found {
// return fmt.Errorf("unable to find the peripheral s/n %s", "A50285BI")
// }
// return periph.SetDeviceProperty(a.ctx, 0, 0, channelValue)
// }
func (a *App) SetDeviceFTDI(channelValue byte) error {
// Connect the FTDI
driver, err := a.hardwareManager.GetFinder("FTDI")
if err != nil {
return err
}
periph, found := driver.GetPeripheral("A50285BI")
if !found {
return fmt.Errorf("unable to find the peripheral s/n %s", "A50285BI")
}
return periph.SetDeviceProperty(a.ctx, 0, 0, channelValue)
}
func (a *App) DeactivateFTDI() error {
// Connect the FTDI
driver, err := a.hardwareManager.GetFinder("FTDI")
if err != nil {
return err
}
periph, found := driver.GetPeripheral("A50285BI")
if !found {
return fmt.Errorf("unable to find the peripheral s/n %s", "A50285BI")
}
return periph.Deactivate(a.ctx)
}
func (a *App) DisconnectFTDI() error {
// Connect the FTDI
driver, err := a.hardwareManager.GetFinder("FTDI")
if err != nil {
return err
}
periph, found := driver.GetPeripheral("A50285BI")
if !found {
return fmt.Errorf("unable to find the peripheral s/n %s", "A50285BI")
}
return periph.Disconnect(a.ctx)
}
// func (a *App) DeactivateFTDI() error {
// // Connect the FTDI
// driver, err := a.hardwareManager.GetFinder("FTDI")
// if err != nil {
// return err
// }
// periph, found := driver.GetPeripheral("A50285BI")
// if !found {
// return fmt.Errorf("unable to find the peripheral s/n %s", "A50285BI")
// }
// return periph.Deactivate(a.ctx)
// }

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://wails.io/schemas/config.v2.json",
"name": "dmxconnect",
"outputfilename": "dmxconnect",
"outputfilename": "dmxconnect.exe",
"frontend:install": "npm install",
"frontend:build": "npm run build",
"frontend:dev:watcher": "npm run dev",