24-activate-peripherals #26

Merged
thinkode merged 7 commits from 24-activate-peripherals into develop 2025-11-02 10:00:14 +00:00
15 changed files with 2002 additions and 306 deletions
Showing only changes of commit cb5c5b688e - Show all commits

View File

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

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

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

@@ -20,7 +20,6 @@ import (
const (
ftdiFinderExecutableName = "FTDI_finder.exe"
ftdiSenderExecutableName = "FTDI_sender.exe"
)
// FTDIFinder represents how the protocol is defined
@@ -70,9 +69,6 @@ func (f *FTDIFinder) UnregisterPeripheral(peripheralID string) error {
//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
@@ -85,10 +81,6 @@ func (f *FTDIFinder) Initialize() error {
if err != nil {
return err
}
err = createExecutable(ftdiSenderExecutableName, senderExe)
if err != nil {
return err
}
log.Trace().Str("file", "FTDIFinder").Msg("FTDI finder initialized")
return nil
}
@@ -157,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
}

View File

@@ -1,31 +1,27 @@
package hardware
import (
"bufio"
"context"
_ "embed"
"fmt"
"io"
"path/filepath"
"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 {
info PeripheralInfo // The peripheral basic data
programName string // The temp file name of the executable
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
@@ -38,9 +34,8 @@ 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{
programName: filepath.Join(os.TempDir(), ftdiSenderExecutableName),
info: info,
dmxSender: nil,
dmxDevice: C.dmx_create(),
settings: settings,
disconnectChan: make(chan struct{}),
errorsChan: make(chan error, 1),
@@ -49,128 +44,67 @@ func NewFTDIPeripheral(info PeripheralInfo) (*FTDIPeripheral, error) {
// Connect connects the FTDI peripheral
func (p *FTDIPeripheral) Connect(ctx context.Context, location int) error {
// Connect if no connection is already running
// Check if the device has already been created
if p.dmxDevice == nil {
return errors.Errorf("the DMX device has not been created!")
}
// Connect the peripheral
log.Trace().Str("file", "FTDIPeripheral").Str("s/n", p.info.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.info.SerialNumber).Msg("dmxSender already initialized")
return nil
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")
}
// Initialize the exec.Command for running the process
p.dmxSender = exec.Command(p.programName, fmt.Sprintf("%d", location))
log.Trace().Str("file", "FTDIPeripheral").Str("s/n", p.info.SerialNumber).Msg("dmxSender instance created")
//TODO: Destroy the object when context is done to avoid memory loss
// 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.info.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.info.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.info.SerialNumber).Msg("unable to create stderr pipe")
return fmt.Errorf("unable to create stderr pipe: %v", err)
}
// 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.info.SerialNumber).Msg("error detected in dmx sender")
}
if err := scanner.Err(); err != nil {
log.Err(err).Str("file", "FTDIPeripheral").Str("s/n", p.info.SerialNumber).Msg("error reading from stderr")
}
}()
// 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
err = p.Disconnect()
if err != nil {
log.Err(err).Str("file", "FTDIPeripheral").Msg("unable to disconnect the peripheral")
}
log.Warn().Str("file", "FTDIPeripheral").Str("s/n", p.info.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.info.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.info.SerialNumber).Msg("dmx sender exited with code")
}
} else {
log.Debug().Str("file", "FTDIPeripheral").Str("s/n", p.info.SerialNumber).Msg("dmx sender exited successfully")
}
}
}()
log.Debug().Str("file", "FTDIPeripheral").Str("s/n", p.info.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() error {
log.Trace().Str("file", "FTDIPeripheral").Str("s/n", p.info.SerialNumber).Msg("disconnecting FTDI peripheral...")
if p.dmxSender != nil {
log.Debug().Str("file", "FTDIPeripheral").Str("s/n", p.info.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.info.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.info.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
// 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.info.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.info.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.info.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.info.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.info.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.info.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.info.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
}
// SetSettings sets a specific setting for this peripheral
@@ -181,18 +115,18 @@ func (p *FTDIPeripheral) SetSettings(settings map[string]interface{}) error {
// SetDeviceProperty sends a command to the specified device
func (p *FTDIPeripheral) SetDeviceProperty(ctx context.Context, channelNumber uint32, channelValue byte) error {
if p.dmxSender != nil {
log.Debug().Str("file", "FTDIPeripheral").Str("s/n", p.info.SerialNumber).Msg("dmxsender is defined for this FTDI")
commandString := []byte{0x03, byte(channelNumber & 0xFF), byte((channelNumber >> 8) & 0xFF), channelValue}
_, err := io.WriteString(p.stdin, string(commandString))
if err != nil {
log.Err(err).Str("file", "FTDIPeripheral").Str("s/n", p.info.SerialNumber).Msg("unable to write command to sender")
return fmt.Errorf("unable to set device property: %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.info.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

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

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

@@ -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",