ArctosRobotCnc 2.7e1 :
- primo commit.
This commit is contained in:
+18
@@ -0,0 +1,18 @@
|
||||
|
||||
# /
|
||||
/revision.h
|
||||
/*.aps
|
||||
/*.ncb
|
||||
/*.suo
|
||||
/*.user
|
||||
/*.sdf
|
||||
/*.opensdf
|
||||
/Debug32
|
||||
/Release32
|
||||
/Trial32
|
||||
/Debug64
|
||||
/Release64
|
||||
/ipch
|
||||
/bin
|
||||
/obj
|
||||
/.vs
|
||||
@@ -0,0 +1,626 @@
|
||||
//----------------------------------------------------------------------------
|
||||
// EgalTech 2025-2025
|
||||
//----------------------------------------------------------------------------
|
||||
// File : ArctosRobotCnc.cpp Data : 22.05.25 Versione : 2.7e2
|
||||
// Contenuto : Controllo numerico per robot Arctos con comunicazione SLCAN.
|
||||
//
|
||||
//
|
||||
//
|
||||
// Modifiche : 22.05.25 DS Creazione modulo.
|
||||
//
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//--------------------------- Include ----------------------------------------
|
||||
#include "stdafx.h"
|
||||
#include "serial.h"
|
||||
#include "slcan.h"
|
||||
#include "logger.h"
|
||||
#include "/EgtDev/Include/EGnGetModuleVer.h"
|
||||
#include "/EgtDEv/Include/EGnScanner.h"
|
||||
#include "/EgtDev/Include/EgtIniFile.h"
|
||||
#include "/EgtDev/Include/EgtStringConverter.h"
|
||||
|
||||
using namespace std ;
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
const int MAX_AXES = 6 ;
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
static string s_sTempDir ;
|
||||
static string s_sProgDir ;
|
||||
static string s_sFileIni ;
|
||||
static slcan_port_t s_port = NULL ;
|
||||
static int s_nAxesCnt = 0 ;
|
||||
static DBLVECTOR s_vdAxisK = { 1, 1, 1, 1, 1, 1} ;
|
||||
static DBLVECTOR s_vdAxisPos = { 0, 0, 0, 0, 0, 0} ;
|
||||
static int s_nAcc = 2 ;
|
||||
static int s_nFeed = 100 ;
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
struct GMove {
|
||||
string sLine ;
|
||||
int nLine ;
|
||||
int nG ;
|
||||
int nMask ;
|
||||
double vdAng[MAX_AXES] ;
|
||||
int nFeed ;
|
||||
GMove( void) : nLine( 0), nG( 0), nMask( 0), vdAng{ 0, 0, 0, 0, 0 ,0}, nFeed( 0) {}
|
||||
} ;
|
||||
typedef std::vector<GMove> GMOVEVECTOR ;
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
static void
|
||||
usleep( unsigned int usec)
|
||||
{
|
||||
if ( usec >= 100) {
|
||||
// Convert to 100 nanosecond interval, negative value indicates relative time
|
||||
LARGE_INTEGER ft ;
|
||||
ft.QuadPart = -( 10 * (LONGLONG)usec) ;
|
||||
HANDLE timer ;
|
||||
if ( ( timer = CreateWaitableTimer( NULL, TRUE, NULL)) != NULL) {
|
||||
SetWaitableTimer( timer, &ft, 0, NULL, NULL, 0) ;
|
||||
WaitForSingleObject( timer, INFINITE) ;
|
||||
CloseHandle( timer) ;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Sleep( 0) ;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
static bool
|
||||
CalcCRC( slcan_message_t& slmMsg)
|
||||
{
|
||||
if ( slmMsg.can_dlc == 0 || slmMsg.can_dlc > 8)
|
||||
return false ;
|
||||
unsigned int unCrc = slmMsg.can_id ;
|
||||
for ( int i = 0 ; i < slmMsg.can_dlc - 1 ; ++ i)
|
||||
unCrc += slmMsg.data[i] ;
|
||||
slmMsg.data[slmMsg.can_dlc - 1] = ( unCrc & 0xFF) ;
|
||||
return true ;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
static bool
|
||||
IsPowerOfTwo( int nVal)
|
||||
{
|
||||
return ( ( nVal > 0) && ( ( nVal & ( nVal - 1)) == 0)) ;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
static int
|
||||
OpenSLCAN( void)
|
||||
{
|
||||
// Log versione Api SLCAN
|
||||
log_printf( "%s\n", slcan_api_version( NULL, NULL, NULL)) ;
|
||||
|
||||
// Apertura porta SLCAN
|
||||
int nQueueSize = GetPrivateProfileInt( "SLCAN", "Buffer", 8, s_sFileIni.data()) ;
|
||||
s_port = slcan_create( (unsigned) nQueueSize) ;
|
||||
if ( s_port == NULL) {
|
||||
log_printf( "Error: slcan_create returned NULL (%i)\n", errno) ;
|
||||
return -4 ;
|
||||
}
|
||||
string sPort = GetPrivateProfileStringUtf8( "SLCAN", "Port", "COM4", s_sFileIni.data()) ;
|
||||
int nRes = slcan_connect( s_port, sPort.data(), NULL) ;
|
||||
if ( nRes < 0) {
|
||||
log_printf( "Error: slcan_connect returned %i (%i)\n", nRes, errno) ;
|
||||
return -3 ;
|
||||
}
|
||||
nRes = slcan_set_ack( s_port, false) ;
|
||||
if ( nRes < 0) {
|
||||
log_printf( "Error: slcan_set_ack false returned %i (%i)\n", nRes, errno) ;
|
||||
return -3 ;
|
||||
}
|
||||
log_printf( "Using CANable protocol (w/o ACK/NACK feedback)\n");
|
||||
nRes = slcan_setup_bitrate( s_port, CAN_500K) ;
|
||||
if ( nRes < 0) {
|
||||
log_printf( "Error: slcan_setup_bitrate returned %i (%i)\n", nRes, errno) ;
|
||||
return -2 ;
|
||||
}
|
||||
nRes = slcan_open_channel( s_port) ;
|
||||
if ( nRes < 0) {
|
||||
log_printf( "Error: slcan_open_channel returned %i (%i)\n", nRes, errno) ;
|
||||
return -2 ;
|
||||
}
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
static int
|
||||
CloseSLCAN( int nLev, int nRet)
|
||||
{
|
||||
if ( nLev > -2) {
|
||||
int nRes = slcan_close_channel( s_port) ;
|
||||
if ( nRes < 0) {
|
||||
log_printf( "Error: slcan_close_channel returnd %i (%i)\n", nRes, errno) ;
|
||||
}
|
||||
}
|
||||
|
||||
if ( nLev > -3) {
|
||||
int nRes = slcan_disconnect( s_port) ;
|
||||
if ( nRes < 0) {
|
||||
log_printf( "Error: slcan_disconnect returnd %i (%i)\n", nRes, errno) ;
|
||||
}
|
||||
}
|
||||
|
||||
if ( nLev > -4) {
|
||||
int nRes = slcan_destroy( s_port) ;
|
||||
if ( nRes < 0) {
|
||||
log_printf( "Error: slcan_destroy returnd %i (%i)\n", nRes, errno) ;
|
||||
}
|
||||
}
|
||||
|
||||
return nRet ;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
static int
|
||||
Homing( void)
|
||||
{
|
||||
fprintf( stdout, "Homing\n") ;
|
||||
// Impostazioni speciali per alcuni assi
|
||||
string sSpecId = GetPrivateProfileStringUtf8( "Robot", "SpecialAxes", "3,4", s_sFileIni.data()) ;
|
||||
INTVECTOR vSpecId ;
|
||||
FromString( sSpecId, vSpecId) ;
|
||||
for ( int nId : vSpecId) {
|
||||
if ( nId <= s_nAxesCnt) {
|
||||
slcan_message_t slmSpec ;
|
||||
slmSpec.can_id = nId ;
|
||||
slmSpec.can_dlc = 3 ;
|
||||
slmSpec.data[0] = 0x9E ;
|
||||
slmSpec.data[1] = 0x01 ;
|
||||
CalcCRC( slmSpec) ;
|
||||
int nRes = slcan_write_message( s_port, &slmSpec, 0) ;
|
||||
if ( nRes < 0) {
|
||||
log_printf( "Error: special setting failed on axis %d\n", nId) ;
|
||||
return -1 ;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Home contemporaneo possibile solo per i primi 5 assi
|
||||
int nHomeAxesCnt = min( s_nAxesCnt, 5) ;
|
||||
|
||||
// Lancio esecuzione homing
|
||||
for ( int nId = 1 ; nId <= nHomeAxesCnt ; ++ nId) {
|
||||
slcan_message_t slmHome ;
|
||||
slmHome.can_id = nId ;
|
||||
slmHome.can_dlc = 2 ;
|
||||
slmHome.data[0] = 0x91 ;
|
||||
CalcCRC( slmHome) ;
|
||||
int nRes = slcan_write_message( s_port, &slmHome, 0) ;
|
||||
if ( nRes < 0) {
|
||||
log_printf( "Error: run homing failed on axis %d\n", nId) ;
|
||||
return -1 ;
|
||||
}
|
||||
}
|
||||
// Attendo completamento homing
|
||||
const int nTarget = ( 1 << nHomeAxesCnt) - 1 ;
|
||||
int nStart = 0 ;
|
||||
int nHoming = 0 ;
|
||||
int nTry = 0 ;
|
||||
int nMaxTry = 200 ;
|
||||
slcan_message_t slmRead ;
|
||||
int nRes = slcan_read_message( s_port, &slmRead, 20) ;
|
||||
while ( nHoming != nTarget && nTry < nMaxTry) {
|
||||
if ( nRes == 0) {
|
||||
for ( int nId = 1 ; nId <= nHomeAxesCnt ; ++ nId) {
|
||||
if ( slmRead.can_id == nId) {
|
||||
if ( slmRead.data[1] == 0x01)
|
||||
nStart |= ( 1 << ( nId - 1)) ;
|
||||
else if ( slmRead.data[1] == 0x02)
|
||||
nHoming |= ( 1 << ( nId - 1)) ;
|
||||
}
|
||||
}
|
||||
if ( nStart == nTarget)
|
||||
nMaxTry = 3000 ;
|
||||
}
|
||||
else if ( nRes != -30)
|
||||
break ;
|
||||
nRes = slcan_read_message( s_port, &slmRead, 20) ;
|
||||
++ nTry ;
|
||||
}
|
||||
if ( nHoming != nTarget) {
|
||||
log_printf( "Error: homing failed (%d-%d)\n", nHoming, nTarget) ;
|
||||
return -1 ;
|
||||
}
|
||||
|
||||
// Azzero eventuale sesto asse
|
||||
if ( s_nAxesCnt >= 6) {
|
||||
int nId = 6 ;
|
||||
// eseguo azzeramento
|
||||
slcan_message_t slmHome ;
|
||||
slmHome.can_id = nId ;
|
||||
slmHome.can_dlc = 2 ;
|
||||
slmHome.data[0] = 0x92 ;
|
||||
CalcCRC( slmHome) ;
|
||||
int nRes = slcan_write_message( s_port, &slmHome, 0) ;
|
||||
if ( nRes < 0) {
|
||||
log_printf( "Error: run setzero failed on axis 6\n") ;
|
||||
return -1 ;
|
||||
}
|
||||
// controllo azzeramento
|
||||
int nStatus = -1 ;
|
||||
int nTry = 0 ;
|
||||
slcan_message_t slmRead ;
|
||||
nRes = slcan_read_message( s_port, &slmRead, 20) ;
|
||||
while ( nStatus == -1 && nTry < 200) {
|
||||
if ( nRes == 0) {
|
||||
if ( slmRead.can_id == nId) {
|
||||
if ( slmRead.data[1] == 0x01)
|
||||
nStatus = 0 ;
|
||||
}
|
||||
if ( nStart == nTarget)
|
||||
nMaxTry = 3000 ;
|
||||
}
|
||||
else if ( nRes != -30)
|
||||
break ;
|
||||
nRes = slcan_read_message( s_port, &slmRead, 20) ;
|
||||
++ nTry ;
|
||||
}
|
||||
if ( nStatus < 0) {
|
||||
log_printf( "Error: run setzero status read on axis 6\n") ;
|
||||
return -1 ;
|
||||
}
|
||||
}
|
||||
|
||||
// Azzero gli assi
|
||||
s_vdAxisPos = { 0, 0, 0, 0, 0, 0} ;
|
||||
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
static int
|
||||
MoveAxis( int nId, double dAng, int nFeed = s_nFeed)
|
||||
{
|
||||
// verifico indice asse
|
||||
if ( nId < 1 || nId > s_nAxesCnt) {
|
||||
log_printf( "Error: move axis with wrong Id (%d)\n", nId) ;
|
||||
return -1 ;
|
||||
}
|
||||
|
||||
// limito i parametri
|
||||
unsigned int unSpeed = (unsigned) clamp( nFeed, 0, 3000) ;
|
||||
unsigned int unAcc = (unsigned) clamp( s_nAcc, 0, 255) ;
|
||||
int nPulses = clamp( int( dAng * s_vdAxisK[nId-1]), -8388607, 8388607) ;
|
||||
|
||||
// eseguo movimento in assoluto
|
||||
slcan_message_t slmMove ;
|
||||
slmMove.can_id = nId ;
|
||||
slmMove.can_dlc = 8 ;
|
||||
slmMove.data[0] = 0xFE ;
|
||||
slmMove.data[1] = ( ( unSpeed >> 8) & 0xFF) ;
|
||||
slmMove.data[2] = ( unSpeed & 0xFF) ;
|
||||
slmMove.data[3] = ( unAcc & 0xFF) ;
|
||||
slmMove.data[4] = ( ( nPulses >> 16) & 0xFF) ;
|
||||
slmMove.data[5] = ( ( nPulses >> 8) & 0xFF) ;
|
||||
slmMove.data[6] = ( nPulses & 0xFF) ;
|
||||
CalcCRC( slmMove) ;
|
||||
int nRes = slcan_write_message( s_port, &slmMove, 0) ;
|
||||
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
static int
|
||||
WaitMoveAxisResult( int nId)
|
||||
{
|
||||
// verifico indice asse
|
||||
if ( nId < 1 || nId > s_nAxesCnt) {
|
||||
log_printf( "Error: move axis status with wrong Id (%d)\n", nId) ;
|
||||
return -1 ;
|
||||
}
|
||||
// attendo risultato
|
||||
int nStatus = -1 ;
|
||||
int nTry = 0 ;
|
||||
slcan_message_t slmRead ;
|
||||
int nRes = slcan_read_message( s_port, &slmRead, 10) ;
|
||||
while ( nStatus != 0 && nStatus != 2 && nStatus != 3 && nTry < 1000) {
|
||||
if ( nRes == 0) {
|
||||
if ( slmRead.can_id == nId && slmRead.data[0] == 0xFE)
|
||||
nStatus = slmRead.data[1] ;
|
||||
}
|
||||
else if ( nRes != -30)
|
||||
break ;
|
||||
nRes = slcan_read_message( s_port, &slmRead, 10) ;
|
||||
++ nTry ;
|
||||
}
|
||||
if ( nStatus != 2) {
|
||||
log_printf( "Error: move axis status (%d)\n", nStatus) ;
|
||||
return -1 ;
|
||||
}
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
static int
|
||||
WaitMoveAxesResult( int nMask)
|
||||
{
|
||||
// attendo risultato
|
||||
int nStatus = 0 ;
|
||||
int nTry = 0 ;
|
||||
slcan_message_t slmRead ;
|
||||
int nRes = slcan_read_message( s_port, &slmRead, 10) ;
|
||||
while ( nStatus != nMask && nTry < 10000) {
|
||||
if ( nRes == 0) {
|
||||
for ( int nId = 1 ; nId <= s_nAxesCnt ; ++ nId) {
|
||||
if ( ( nMask & ( 1 << ( nId - 1))) != 0 &&
|
||||
slmRead.can_id == nId && slmRead.data[0] == 0xFE && slmRead.data[1] == 2)
|
||||
nStatus |= ( 1 << ( nId - 1)) ;
|
||||
}
|
||||
}
|
||||
else if ( nRes != -30)
|
||||
break ;
|
||||
nRes = slcan_read_message( s_port, &slmRead, 1) ;
|
||||
++ nTry ;
|
||||
}
|
||||
if ( nStatus != nMask) {
|
||||
log_printf( "Error: wait multi move axis status (%d)\n", nStatus) ;
|
||||
return -1 ;
|
||||
}
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
static int
|
||||
MoveAxes( const GMOVEVECTOR& vGMove)
|
||||
{
|
||||
fprintf( stdout, "Moving\n") ;
|
||||
for ( const auto& CurrGMove : vGMove) {
|
||||
log_printf( "Move of line %d (%s)\n", CurrGMove.nLine, CurrGMove.sLine.data()) ;
|
||||
fprintf( stdout, "%s\n", CurrGMove.sLine.data()) ;
|
||||
// se nessun asse da muovere
|
||||
if ( CurrGMove.nMask <= 0)
|
||||
log_printf( " Empty\n") ;
|
||||
// se richiesto movimento di un singolo asse
|
||||
else if ( IsPowerOfTwo( CurrGMove.nMask)) {
|
||||
// cerco asse da muovere
|
||||
int nId = 0 ;
|
||||
for ( int i = 1 ; i <= s_nAxesCnt && nId == 0 ; ++ i) {
|
||||
if ( ( CurrGMove.nMask & ( 1 << ( i - 1))) != 0)
|
||||
nId = i ;
|
||||
}
|
||||
log_printf( " Single move (%d)\n", nId) ;
|
||||
// se trovato e valido, eseguo
|
||||
if ( nId > 0 && nId <= s_nAxesCnt) {
|
||||
int nRes = MoveAxis( nId, CurrGMove.vdAng[nId-1], CurrGMove.nFeed) ;
|
||||
if ( nRes != 0)
|
||||
return nRes ;
|
||||
nRes = WaitMoveAxisResult( nId) ;
|
||||
if ( nRes != 0)
|
||||
return nRes ;
|
||||
s_vdAxisPos[nId-1] = CurrGMove.vdAng[nId-1] ;
|
||||
}
|
||||
}
|
||||
// altrimenti devo muovere più assi contemporaneamente
|
||||
else {
|
||||
log_printf( " Multiple move (%d)\n", CurrGMove.nMask) ;
|
||||
// calcolo la massima lunghezza di movimento
|
||||
double dMaxMove = 0 ;
|
||||
for ( int nId = 1 ; nId <= s_nAxesCnt ; ++ nId) {
|
||||
if ( ( CurrGMove.nMask & ( 1 << ( nId - 1))) != 0) {
|
||||
double dMove = abs( CurrGMove.vdAng[nId-1] - s_vdAxisPos[nId-1]) ;
|
||||
if ( nId == 1)
|
||||
dMove = 10 * dMove ;
|
||||
else if (nId == 2)
|
||||
dMove = 5 * dMove ;
|
||||
else if ( nId == 3)
|
||||
dMove = 2 * dMove ;
|
||||
dMaxMove = max( dMaxMove, dMove) ;
|
||||
}
|
||||
}
|
||||
// spezzo il movimento in passi
|
||||
double dStep = ( CurrGMove.nG == 0 ? 10 : 2) ;
|
||||
int nStep = max( int( ceil( dMaxMove / dStep)), 1) ;
|
||||
for ( int nS = 1 ; nS <= nStep ; ++ nS) {
|
||||
double dCoeff = ( 1. * nS) / nStep ;
|
||||
// eseguo movimenti
|
||||
for ( int nId = 1 ; nId <= s_nAxesCnt ; ++ nId) {
|
||||
if ( ( CurrGMove.nMask & ( 1 << ( nId - 1))) != 0) {
|
||||
double dAng = ( 1 - dCoeff) * s_vdAxisPos[nId-1] + dCoeff * CurrGMove.vdAng[nId-1] ;
|
||||
int nRes = MoveAxis( nId, dAng, CurrGMove.nFeed) ;
|
||||
if ( nRes != 0)
|
||||
return nRes ;
|
||||
}
|
||||
}
|
||||
// attendo completamento movimenti
|
||||
int nRes = WaitMoveAxesResult( CurrGMove.nMask) ;
|
||||
if ( nRes != 0)
|
||||
return nRes ;
|
||||
}
|
||||
// aggiorno posizioni assi
|
||||
for ( int nId = 1 ; nId <= s_nAxesCnt ; ++ nId) {
|
||||
if ( ( CurrGMove.nMask & ( 1 << ( nId - 1))) != 0) {
|
||||
s_vdAxisPos[nId-1] = CurrGMove.vdAng[nId-1] ;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf( stdout, "Completed\n") ;
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
static bool
|
||||
ReadOneMove( const string& sMove, int nId, GMove& CurrGMove)
|
||||
{
|
||||
double dVal ;
|
||||
if ( FromString( sMove.substr( 1), dVal)) {
|
||||
CurrGMove.nMask |= ( 1 << ( nId - 1)) ;
|
||||
CurrGMove.vdAng[nId-1] = dVal ;
|
||||
return true ;
|
||||
}
|
||||
return false ;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
static bool
|
||||
ReadProgram( const string& sProgPath, GMOVEVECTOR& vGMove)
|
||||
{
|
||||
// Inizializzo la lettura
|
||||
Scanner TheScanner ;
|
||||
if ( ! TheScanner.Init( sProgPath, ";")) {
|
||||
log_printf( "Error: failed opening program %s\n", sProgPath) ;
|
||||
return false ;
|
||||
}
|
||||
|
||||
// Leggo le linee
|
||||
int nG = 0 ;
|
||||
int nFeed = s_nFeed ;
|
||||
string sLine ;
|
||||
while ( TheScanner.GetLine( sLine)) {
|
||||
bool bOk = true ;
|
||||
GMove CurrGMove ;
|
||||
CurrGMove.sLine = sLine ;
|
||||
CurrGMove.nLine = TheScanner.GetCurrLineNbr() ;
|
||||
// divido la linea in parti
|
||||
STRVECTOR vsTokens ;
|
||||
TokenizePlus( sLine, "GXYZABCF", vsTokens) ;
|
||||
for ( int i = 0 ; i < int( vsTokens.size()) ; ++ i) {
|
||||
// analizzo ogni parte
|
||||
switch ( vsTokens[i][0]) {
|
||||
case 'G' :
|
||||
FromString( vsTokens[i].substr( 1), nG) ;
|
||||
break ;
|
||||
case 'X' :
|
||||
bOk = bOk && ReadOneMove( vsTokens[i], 1, CurrGMove) ;
|
||||
break ;
|
||||
case 'Y' :
|
||||
bOk = bOk && ReadOneMove( vsTokens[i], 2, CurrGMove) ;
|
||||
break ;
|
||||
case 'Z' :
|
||||
bOk = bOk && ReadOneMove( vsTokens[i], 3, CurrGMove) ;
|
||||
break ;
|
||||
case 'A' :
|
||||
bOk = bOk && ReadOneMove( vsTokens[i], 4, CurrGMove) ;
|
||||
break ;
|
||||
case 'B' :
|
||||
bOk = bOk && ReadOneMove( vsTokens[i], 5, CurrGMove) ;
|
||||
break ;
|
||||
case 'C' :
|
||||
bOk = bOk && ReadOneMove( vsTokens[i], 6, CurrGMove) ;
|
||||
break ;
|
||||
case 'F' :
|
||||
FromString( vsTokens[i].substr( 1), nFeed) ;
|
||||
break ;
|
||||
}
|
||||
}
|
||||
if ( ! bOk) {
|
||||
log_printf( "Error: line %d not correct\n", CurrGMove.nLine) ;
|
||||
return false ;
|
||||
}
|
||||
CurrGMove.nG = nG ;
|
||||
CurrGMove.nFeed = nFeed ;
|
||||
if ( CurrGMove.nMask != 0)
|
||||
vGMove.emplace_back( CurrGMove) ;
|
||||
}
|
||||
|
||||
return true ;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
static int
|
||||
Exec( int nCmd, const string& sProgram)
|
||||
{
|
||||
// Recupero i dati degli assi del robot
|
||||
s_nAxesCnt = GetPrivateProfileInt( "Robot", "Axes", MAX_AXES, s_sFileIni.data()) ;
|
||||
if ( s_nAxesCnt < 1 || s_nAxesCnt > MAX_AXES) {
|
||||
log_printf( "Error: wrong robot axes number (%d)\n", s_nAxesCnt) ;
|
||||
return -1 ;
|
||||
}
|
||||
for ( int nId = 1 ; nId <= s_nAxesCnt ; ++nId ) {
|
||||
string sKey = "K" + ToString( nId) ;
|
||||
string sTmp = GetPrivateProfileStringUtf8( "Robot", sKey.data(), "1", s_sFileIni.data()) ;
|
||||
FromString( sTmp, s_vdAxisK[nId-1]) ;
|
||||
}
|
||||
s_nFeed = GetPrivateProfileInt( "Robot", "Feed", 300, s_sFileIni.data()) ;
|
||||
s_nAcc = GetPrivateProfileInt( "Robot", "Acc", 2, s_sFileIni.data()) ;
|
||||
|
||||
// Verifico cosa fare
|
||||
bool bHome = ( nCmd == 1) ;
|
||||
bool bProg = ( ! IsEmptyOrSpaces( sProgram)) ;
|
||||
|
||||
// Gestione programma da eseguire
|
||||
GMOVEVECTOR vGMove ;
|
||||
if ( bProg) {
|
||||
// Leggo il programma
|
||||
string sProgPath = s_sProgDir + "\\" + sProgram ;
|
||||
log_printf( "Homing and executing %s\n", sProgPath.data()) ;
|
||||
if ( ! ReadProgram( sProgPath, vGMove))
|
||||
return -1 ;
|
||||
log_printf( "Reading completed\n") ;
|
||||
}
|
||||
else
|
||||
log_printf( "Homing alone\n") ;
|
||||
|
||||
// Apertura porta SLCAN
|
||||
int nOpenRes = OpenSLCAN() ;
|
||||
if ( nOpenRes != 0)
|
||||
return CloseSLCAN( nOpenRes, nOpenRes) ;
|
||||
|
||||
// Eseguo homing degli assi
|
||||
if ( bHome) {
|
||||
int nHomeRes = Homing() ;
|
||||
if ( nHomeRes < 0)
|
||||
return CloseSLCAN( 0, nHomeRes) ;
|
||||
}
|
||||
|
||||
// Se non c'è programma, ho finito
|
||||
if ( ! bProg)
|
||||
return CloseSLCAN( 0, 0) ;
|
||||
|
||||
// Eseguo movimenti assi
|
||||
int nMoveRes = MoveAxes( vGMove) ;
|
||||
if ( nMoveRes < 0)
|
||||
return CloseSLCAN( 0, nMoveRes) ;
|
||||
|
||||
// Chiusura
|
||||
return CloseSLCAN( 0, 0) ;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
int
|
||||
wmain( int argc, wchar_t *argv[])
|
||||
{
|
||||
// Primo parametro della linea di comando : nome eseguibile
|
||||
// Non interessa
|
||||
// Secondo parametro della linea di comando : 0=niente, 1=home
|
||||
wstring swCmd = ( argc >= 2 ? argv[1] : L"0") ;
|
||||
int nCmd = 0 ;
|
||||
FromString( wstringtoA( swCmd), nCmd) ;
|
||||
// Terzo parametro della linea di comando : nome programma di movimento
|
||||
wstring swProgram = ( argc >= 3 ? argv[2] : L"") ;
|
||||
string sProgram = wstringtoA( swProgram) ;
|
||||
// Recupero il direttorio del programma e lo imposto come direttorio di lavoro
|
||||
string sExeDir ;
|
||||
GetModuleDirectory( NULL, sExeDir) ;
|
||||
SetCurrentDirectory( sExeDir) ;
|
||||
// Impostazione path radice per i dati
|
||||
string sDataRoot = GetPrivateProfileStringUtf8( "Data", "DataRoot", "", ( sExeDir + "\\DataRoot.ini").c_str()) ;
|
||||
if ( sDataRoot.empty())
|
||||
sDataRoot = sExeDir ;
|
||||
// Impostazione direttorio di configurazione
|
||||
string sConfigDir = sDataRoot + "\\Config" ;
|
||||
// Impostazione direttorio per i file temporanei
|
||||
s_sTempDir = sDataRoot + "\\Temp" ;
|
||||
// Impostazione direttorio per i file dei programmi
|
||||
s_sProgDir = sDataRoot + "\\Programs" ;
|
||||
// Impostazione path del file Ini
|
||||
s_sFileIni = sConfigDir + "\\ArctosRobotCnc.ini" ;
|
||||
// Impostazione path del file Log
|
||||
string sLogFile = s_sTempDir + "\\ArctosRobotLog.txt" ;
|
||||
|
||||
// Apro log
|
||||
log_init( sLogFile.data()) ;
|
||||
|
||||
// Eseguo
|
||||
int nRes = Exec( nCmd, sProgram) ;
|
||||
|
||||
// Chiudo log
|
||||
log_exit() ;
|
||||
|
||||
return nRes ;
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,31 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.14.36109.1 d17.14
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ArctosRobotCnc", "ArctosRobotCnc.vcxproj", "{0B0EB5A6-5E3C-46CC-9843-218A4B0EAF96}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{0B0EB5A6-5E3C-46CC-9843-218A4B0EAF96}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{0B0EB5A6-5E3C-46CC-9843-218A4B0EAF96}.Debug|x64.Build.0 = Debug|x64
|
||||
{0B0EB5A6-5E3C-46CC-9843-218A4B0EAF96}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{0B0EB5A6-5E3C-46CC-9843-218A4B0EAF96}.Debug|x86.Build.0 = Debug|Win32
|
||||
{0B0EB5A6-5E3C-46CC-9843-218A4B0EAF96}.Release|x64.ActiveCfg = Release|x64
|
||||
{0B0EB5A6-5E3C-46CC-9843-218A4B0EAF96}.Release|x64.Build.0 = Release|x64
|
||||
{0B0EB5A6-5E3C-46CC-9843-218A4B0EAF96}.Release|x86.ActiveCfg = Release|Win32
|
||||
{0B0EB5A6-5E3C-46CC-9843-218A4B0EAF96}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {B897F967-D98B-487F-BED1-6E52C396874F}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,193 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>17.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{0b0eb5a6-5e3c-46cc-9843-218a4b0eaf96}</ProjectGuid>
|
||||
<RootNamespace>ArctosRobotCnc</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<OutDir>$(SolutionDir)$(Configuration)$(PlatformArchitecture)\</OutDir>
|
||||
<IntDir>$(Configuration)$(PlatformArchitecture)\</IntDir>
|
||||
<TargetName>$(ProjectName)D$(PlatformArchitecture)</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<OutDir>$(SolutionDir)$(Configuration)$(PlatformArchitecture)\</OutDir>
|
||||
<IntDir>$(Configuration)$(PlatformArchitecture)\</IntDir>
|
||||
<TargetName>$(ProjectName)R$(PlatformArchitecture)</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<OutDir>$(SolutionDir)$(Configuration)$(PlatformArchitecture)\</OutDir>
|
||||
<IntDir>$(Configuration)$(PlatformArchitecture)\</IntDir>
|
||||
<TargetName>$(ProjectName)D$(PlatformArchitecture)</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<OutDir>$(SolutionDir)$(Configuration)$(PlatformArchitecture)\</OutDir>
|
||||
<IntDir>$(Configuration)$(PlatformArchitecture)\</IntDir>
|
||||
<TargetName>$(ProjectName)R$(PlatformArchitecture)</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>copy $(TargetPath) \EgtProg\ArctosRobotCnc</Command>
|
||||
</PostBuildEvent>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>_DEBUG;_DEB32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ResourceCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>copy $(TargetPath) \EgtProg\ArctosRobotCnc</Command>
|
||||
</PostBuildEvent>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>NDEBUG;NDEB32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ResourceCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>copy $(TargetPath) \EgtProg\ArctosRobotCnc</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>copy $(TargetPath) \EgtProg\ArctosRobotCnc</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ArctosRobotCnc.cpp" />
|
||||
<ClCompile Include="buffer_w.c" />
|
||||
<ClCompile Include="logger_w.c" />
|
||||
<ClCompile Include="queue_w.c" />
|
||||
<ClCompile Include="serial_w.c" />
|
||||
<ClCompile Include="slcan.c" />
|
||||
<ClCompile Include="timer_w.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="buffer.h" />
|
||||
<ClInclude Include="logger.h" />
|
||||
<ClInclude Include="queue.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="serial.h" />
|
||||
<ClInclude Include="serial_attr.h" />
|
||||
<ClInclude Include="slcan.h" />
|
||||
<ClInclude Include="stdafx.h" />
|
||||
<ClInclude Include="timer.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="ArctosRobotCnc.rc" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\SLCAN">
|
||||
<UniqueIdentifier>{5a454f69-551d-4cc7-a5ae-f11fac698e6b}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\SLCAN">
|
||||
<UniqueIdentifier>{e4289f38-3642-4f58-9ded-b0ca2e527da9}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ArctosRobotCnc.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="buffer_w.c">
|
||||
<Filter>Source Files\SLCAN</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="logger_w.c">
|
||||
<Filter>Source Files\SLCAN</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="queue_w.c">
|
||||
<Filter>Source Files\SLCAN</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="serial_w.c">
|
||||
<Filter>Source Files\SLCAN</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="slcan.c">
|
||||
<Filter>Source Files\SLCAN</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="timer_w.c">
|
||||
<Filter>Source Files\SLCAN</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="serial.h">
|
||||
<Filter>Header Files\SLCAN</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="slcan.h">
|
||||
<Filter>Header Files\SLCAN</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="serial_attr.h">
|
||||
<Filter>Header Files\SLCAN</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="logger.h">
|
||||
<Filter>Header Files\SLCAN</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="buffer.h">
|
||||
<Filter>Header Files\SLCAN</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="queue.h">
|
||||
<Filter>Header Files\SLCAN</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="timer.h">
|
||||
<Filter>Header Files\SLCAN</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="stdafx.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="resource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="ArctosRobotCnc.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,205 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR GPL-3.0-or-later */
|
||||
/*
|
||||
* Software for Industrial Communication, Motion Control and Automation
|
||||
*
|
||||
* Copyright (c) 2002-2024 Uwe Vogt, UV Software, Berlin (info@uv-software.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Module 'buffer'
|
||||
*
|
||||
* This module is dual-licensed under the BSD 2-Clause "Simplified" License
|
||||
* and under the GNU General Public License v3.0 (or any later version).
|
||||
* You can choose between one of them if you use this module.
|
||||
*
|
||||
* BSD 2-Clause "Simplified" License:
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS MODULE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS MODULE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* GNU General Public License v3.0 or later:
|
||||
* This module is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This module is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this module. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file buffer.h
|
||||
*
|
||||
* @brief Buffer for intertask communication.
|
||||
*
|
||||
* @remarks A producer thread writes data into the buffer, if it is empty.
|
||||
* A consumer thread waits until data has been written into the
|
||||
* buffer and reads them, or returns when a time-out occurs.
|
||||
*
|
||||
* @author $Author: quaoar $
|
||||
*
|
||||
* @version $Rev: 811 $
|
||||
*
|
||||
* @defgroup buffer Waitable Buffer
|
||||
* @{
|
||||
*/
|
||||
#ifndef BUFFER_H_INCLUDED
|
||||
#define BUFFER_H_INCLUDED
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
/* ----------- options ------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- defines ------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- types --------------------------------------------------
|
||||
*/
|
||||
|
||||
typedef void *buffer_t; /**< buffer (opaque data type) */
|
||||
|
||||
|
||||
/* ----------- variables ----------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- prototypes ---------------------------------------------
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @brief creates an instance of a waitable buffer (constructor).
|
||||
*
|
||||
* @param[in] size - size of the buffer (number of bytes)
|
||||
*
|
||||
* @returns pointer to a buffer instance if successful, or NULL on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval EINVAL - invalid argument (size)
|
||||
* @retval ENOMEM - out of memory (insufficient storage space)
|
||||
* @retval 'errno' - error code from called system functions:
|
||||
* 'pthread_mutex_init', 'pthread_cond_init'
|
||||
*/
|
||||
extern buffer_t buffer_create(size_t size);
|
||||
|
||||
|
||||
/** @brief destroys the buffer instance (destructor).
|
||||
*
|
||||
* @param[in] buffer - pointer to a buffer instance
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval EFAULT - bad address (invalid buffer instance)
|
||||
* @retval 'errno' - error code from called system functions:
|
||||
* 'pthread_mutex_destroy', 'pthread_cond_destroy'
|
||||
*/
|
||||
extern int buffer_destroy(buffer_t buffer);
|
||||
|
||||
|
||||
/** @brief empties the content of the buffer.
|
||||
*
|
||||
* @param[in] buffer - pointer to a buffer instance
|
||||
*
|
||||
* @returns the number of bytes removed from the buffer if successful, or
|
||||
* a negative value on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval EFAULT - bad address (invalid buffer instance)
|
||||
*/
|
||||
extern int buffer_clear(buffer_t buffer);
|
||||
|
||||
|
||||
/** @brief copies n data bytes into the buffer, if empty.
|
||||
*
|
||||
* @remarks The function copies not more data bytes than the size of the
|
||||
* buffer allows (see parameter 'size' of 'buffer_create').
|
||||
* @see buffer_create
|
||||
*
|
||||
* @param[in] buffer - pointer to a buffer instance
|
||||
* @param[in] data - data to be copied into the buffer
|
||||
* @param[in] nbytes - number of bytes to be copied into the buffer
|
||||
*
|
||||
* @returns the number of bytes copied into the buffer if successful, or
|
||||
* a negative value on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval EFAULT - bad address (invalid buffer instance)
|
||||
* @retval EINVAL - invalid argument (data or nbytes)
|
||||
* @retval EBUSY - device/resoure busy (not empty)
|
||||
*/
|
||||
extern int buffer_put(buffer_t buffer, const void *data, size_t nbytes);
|
||||
|
||||
|
||||
/** @brief copies the data bytes from the buffer, if any.
|
||||
*
|
||||
* @remark The buffer content is entirely emptied after this.
|
||||
*
|
||||
* @param[in] buffer - pointer to a buffer instance
|
||||
* @param[out] data - pointer to an array into which the data are copied
|
||||
* @param[in] maxbytes - maximum number of bytes to be copied from the buffer
|
||||
* @param[in] timeout - time to wait for data available in the buffer:
|
||||
* 0 means the function returns immediately,
|
||||
* 65535 means blocking read, and any other
|
||||
* value means the time to wait im milliseconds
|
||||
*
|
||||
* @returns the number of bytes copied from the buffer if successful, or
|
||||
* a negative value on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval EFAULT - bad address (invalid buffer instance)
|
||||
* @retval EINVAL - invalid argument (data or maxbytes)
|
||||
* @retval ENOMSG - no data available (polling)
|
||||
* @retval ETIMEDOUT - timed out (blocking read)
|
||||
*/
|
||||
extern int buffer_get(buffer_t buffer, void *data, size_t maxbytes, uint16_t timeout);
|
||||
|
||||
|
||||
/** @brief signals waiting objects, if any.
|
||||
*
|
||||
* @param[in] buffer - pointer to a buffer instance
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error.
|
||||
*/
|
||||
extern int buffer_signal(buffer_t buffer);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* BUFFER_H_INCLUDED */
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Uwe Vogt, UV Software, Chausseestrasse 33 A, 10115 Berlin, Germany
|
||||
* Tel.: +49-30-46799872, Fax: +49-30-46799873, Mobile: +49-170-3801903
|
||||
* E-Mail: uwe.vogt@uv-software.de, Homepage: http://www.uv-software.de/
|
||||
*/
|
||||
+288
@@ -0,0 +1,288 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR GPL-3.0-or-later */
|
||||
/*
|
||||
* Software for Industrial Communication, Motion Control and Automation
|
||||
*
|
||||
* Copyright (c) 2002-2024 Uwe Vogt, UV Software, Berlin (info@uv-software.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Module 'buffer'
|
||||
*
|
||||
* This module is dual-licensed under the BSD 2-Clause "Simplified" License
|
||||
* and under the GNU General Public License v3.0 (or any later version).
|
||||
* You can choose between one of them if you use this module.
|
||||
*
|
||||
* BSD 2-Clause "Simplified" License:
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS MODULE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS MODULE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* GNU General Public License v3.0 or later:
|
||||
* This module is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This module is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this module. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file buffer.c
|
||||
*
|
||||
* @brief Buffer for intertask communication.
|
||||
*
|
||||
* @remarks Windows compatible variant (_WIN32 and _WIN64)
|
||||
*
|
||||
* @author $Author: quaoar $
|
||||
*
|
||||
* @version $Rev: 811 $
|
||||
*
|
||||
* @addtogroup buffer
|
||||
* @{
|
||||
*/
|
||||
#include "buffer.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
|
||||
/* ----------- options ------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- defines ------------------------------------------------
|
||||
*/
|
||||
|
||||
#define MIN(x,y) ((x) < (y) ? (x) : (y))
|
||||
|
||||
#define ENTER_CRITICAL_SECTION(buf) do { (void)WaitForSingleObject(buf->hMutex, INFINITE); } while(0)
|
||||
#define LEAVE_CRITICAL_SECTION(buf) do { (void)ReleaseMutex(buf->hMutex); } while(0)
|
||||
|
||||
|
||||
/* ----------- types --------------------------------------------------
|
||||
*/
|
||||
|
||||
typedef struct object_t_ {
|
||||
size_t maxbytes;
|
||||
size_t nbytes;
|
||||
void *data;
|
||||
HANDLE hMutex;
|
||||
HANDLE hEvent;
|
||||
} object_t;
|
||||
|
||||
|
||||
/* ----------- prototypes ---------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- variables ----------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- functions ----------------------------------------------
|
||||
*/
|
||||
|
||||
buffer_t buffer_create(size_t size) {
|
||||
object_t *object = (object_t*)NULL;
|
||||
|
||||
/* reset errno variable */
|
||||
errno = 0;
|
||||
/* C language constructor */
|
||||
if ((object = (object_t*)malloc(sizeof(object_t))) != NULL) {
|
||||
memset(object, 0x00, sizeof(object_t));
|
||||
/* create a data buffer for data exchange */
|
||||
if ((object->data = malloc(size)) == NULL) {
|
||||
/* errno set */
|
||||
free(object);
|
||||
return NULL;
|
||||
}
|
||||
object->maxbytes = size;
|
||||
object->nbytes = 0;
|
||||
/* create a mutex and an event handle */
|
||||
if ((object->hMutex = CreateMutex(
|
||||
NULL, // default security attributes
|
||||
FALSE, // initially not owned
|
||||
NULL)) == NULL) {
|
||||
errno = ENODEV;
|
||||
free(object);
|
||||
return NULL;
|
||||
}
|
||||
if ((object->hEvent = CreateEvent(
|
||||
NULL, // default security attributes
|
||||
FALSE, // auto-reset event
|
||||
FALSE, // initial state is nonsignaled
|
||||
NULL)) == NULL) {
|
||||
errno = ENODEV;
|
||||
(void)CloseHandle(object->hMutex);
|
||||
free(object);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return (buffer_t)object;
|
||||
}
|
||||
|
||||
int buffer_destroy(buffer_t buffer) {
|
||||
object_t *object = (object_t*)buffer;
|
||||
|
||||
/* sanity check */
|
||||
errno = 0;
|
||||
if (!object) {
|
||||
errno = EFAULT;
|
||||
return -1;
|
||||
}
|
||||
/* destroy mutex and event handle */
|
||||
(void)CloseHandle(object->hEvent);
|
||||
(void)CloseHandle(object->hMutex);
|
||||
/* destroy the data buffer */
|
||||
if (object->data)
|
||||
free(object->data);
|
||||
/* C language destructor */
|
||||
free(object);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int buffer_signal(buffer_t buffer) {
|
||||
object_t *object = (object_t*)buffer;
|
||||
|
||||
/* sanity check */
|
||||
errno = 0;
|
||||
if (!object) {
|
||||
errno = EFAULT;
|
||||
return -1;
|
||||
}
|
||||
/* signal event object */
|
||||
(void)SetEvent(object->hEvent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int buffer_clear(buffer_t buffer) {
|
||||
object_t *object = (object_t*)buffer;
|
||||
int res = 0;
|
||||
|
||||
/* sanity check */
|
||||
errno = 0;
|
||||
if (!object) {
|
||||
errno = EFAULT;
|
||||
return -1;
|
||||
}
|
||||
/* remove data from buffer, if any */
|
||||
ENTER_CRITICAL_SECTION(object);
|
||||
res = (int)object->nbytes;
|
||||
object->nbytes = 0;
|
||||
LEAVE_CRITICAL_SECTION(object);
|
||||
/* reset pending event, if any */
|
||||
(void)ResetEvent(object->hEvent);
|
||||
/* return number of bytes removed */
|
||||
return res;
|
||||
}
|
||||
|
||||
int buffer_put(buffer_t buffer, const void *data, size_t nbytes) {
|
||||
object_t *object = (object_t*)buffer;
|
||||
int res = 0;
|
||||
|
||||
/* sanity check */
|
||||
errno = 0;
|
||||
if (!object || !object->data) {
|
||||
errno = EFAULT;
|
||||
return -1;
|
||||
}
|
||||
if (!data || !nbytes) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
/* copy data into buffer (with truncation), if empty */
|
||||
ENTER_CRITICAL_SECTION(object);
|
||||
if (object->nbytes == 0) {
|
||||
memcpy(object->data, data, MIN(nbytes, object->maxbytes));
|
||||
object->nbytes = MIN(nbytes, object->maxbytes);
|
||||
(void)SetEvent(object->hEvent);
|
||||
res = (int)object->nbytes;
|
||||
} else { /* not empty */
|
||||
errno = EBUSY;
|
||||
}
|
||||
LEAVE_CRITICAL_SECTION(object);
|
||||
/* return number of bytes copied */
|
||||
return res;
|
||||
}
|
||||
|
||||
int buffer_get(buffer_t buffer, void *data, size_t maxbytes, uint16_t timeout) {
|
||||
object_t *object = (object_t*)buffer;
|
||||
int res = 0;
|
||||
|
||||
/* sanity check */
|
||||
errno = 0;
|
||||
if (!object || !object->data) {
|
||||
errno = EFAULT;
|
||||
return -1;
|
||||
}
|
||||
if (!data || !maxbytes) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
/* copy buffer into data (with truncation), if any */
|
||||
ENTER_CRITICAL_SECTION(object);
|
||||
if (object->nbytes != 0) {
|
||||
memcpy(data, object->data, MIN(object->nbytes, maxbytes));
|
||||
res = (int)MIN(object->nbytes, maxbytes);
|
||||
object->nbytes = 0;
|
||||
}
|
||||
LEAVE_CRITICAL_SECTION(object);
|
||||
|
||||
/* when no data available - blocking read or polling */
|
||||
if (res <= 0) {
|
||||
if (timeout > 0U) { /* blocking read */
|
||||
switch (WaitForSingleObject(object->hEvent, (timeout != 65535U) ? (DWORD)timeout : INFINITE)) {
|
||||
case WAIT_OBJECT_0: /* event signalled */
|
||||
/* copy buffer into data (with truncation) */
|
||||
ENTER_CRITICAL_SECTION(object);
|
||||
if (object->nbytes != 0) {
|
||||
memcpy(data, object->data, MIN(object->nbytes, maxbytes));
|
||||
res = (int)MIN(object->nbytes, maxbytes);
|
||||
object->nbytes = 0;
|
||||
}
|
||||
LEAVE_CRITICAL_SECTION(object);
|
||||
/* - when signalled externally (e.g. by SIGINT) */
|
||||
if (res < 0)
|
||||
errno = ENOMSG;
|
||||
break;
|
||||
case WAIT_TIMEOUT: /* event timed out */
|
||||
errno = ETIMEDOUT;
|
||||
break;
|
||||
default: /* error: no data! */
|
||||
errno = ENOMSG;
|
||||
break;
|
||||
}
|
||||
} else { /* polling (timeout == 0) */
|
||||
errno = ENOMSG;
|
||||
}
|
||||
}
|
||||
/* return number of bytes copied, or negative value on error */
|
||||
return res;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Uwe Vogt, UV Software, Chausseestrasse 33 A, 10115 Berlin, Germany
|
||||
* Tel.: +49-30-46799872, Fax: +49-30-46799873, Mobile: +49-170-3801903
|
||||
* E-Mail: uwe.vogt@uv-software.de, Homepage: http://www.uv-software.de/
|
||||
*/
|
||||
@@ -0,0 +1,154 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR GPL-3.0-or-later */
|
||||
/*
|
||||
* Software for Industrial Communication, Motion Control and Automation
|
||||
*
|
||||
* Copyright (c) 2002-2024 Uwe Vogt, UV Software, Berlin (info@uv-software.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Module 'logger'
|
||||
*
|
||||
* This module is dual-licensed under the BSD 2-Clause "Simplified" License
|
||||
* and under the GNU General Public License v3.0 (or any later version).
|
||||
* You can choose between one of them if you use this module.
|
||||
*
|
||||
* BSD 2-Clause "Simplified" License:
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS MODULE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS MODULE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* GNU General Public License v3.0 or later:
|
||||
* This module is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This module is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this module. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file logger.h
|
||||
*
|
||||
* @brief Writing log messages into an ASCII file.
|
||||
*
|
||||
* @author $Author: quaoar $
|
||||
*
|
||||
* @version $Rev: 811 $
|
||||
*
|
||||
* @defgroup logger Logging into a file
|
||||
* @{
|
||||
*/
|
||||
#ifndef LOGGER_H_INCLUDED
|
||||
#define LOGGER_H_INCLUDED
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/* ----------- options ------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- defines ------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- types --------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- variables ----------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- prototypes ---------------------------------------------
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @brief opens a file for logging.
|
||||
*
|
||||
* @param[in] pathname - a pathname naming a file, or NULL
|
||||
* to write to stadard output stream
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error
|
||||
*/
|
||||
extern int log_init(const char *pathname/* = NULL, flags = 0*/);
|
||||
|
||||
|
||||
/** @brief closes a open log file.
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error
|
||||
*/
|
||||
extern int log_exit(void);
|
||||
|
||||
|
||||
/** @brief writes binary data as ASCII into the log file.
|
||||
*
|
||||
* @remarks This function can only be used by the log file
|
||||
* owner (e.g. the thread that opened the file).
|
||||
*
|
||||
* @param[in] buffer - pointer to a buffer containing binary
|
||||
* data to be written into the log file
|
||||
* @param[in] nbytes - number of bytes to be logged
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error
|
||||
*/
|
||||
extern int log_sync(const uint8_t *buffer, size_t nbytes);
|
||||
|
||||
|
||||
/** @brief writes binary data as ASCII into the log file.
|
||||
*
|
||||
* @remarks This function can be used by threads that do
|
||||
* not own the log file (e.g. callbak handler).
|
||||
*
|
||||
* @param[in] buffer - pointer to a buffer containing binary
|
||||
* data to be written into the log file
|
||||
* @param[in] nbytes - number of bytes to be logged
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error
|
||||
*/
|
||||
extern int log_async(const uint8_t *buffer, size_t nbytes);
|
||||
|
||||
|
||||
/** @brief writes a formatted string into the log file.
|
||||
*
|
||||
* @remarks This function can only be used by the log file
|
||||
* owner (e.g. the thread that opened the file).
|
||||
*
|
||||
* @param[in] format - format string (see printf)
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error
|
||||
*/
|
||||
extern int log_printf(const char *format, ...);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* LOGGER_H_INCLUDED */
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Uwe Vogt, UV Software, Chausseestrasse 33 A, 10115 Berlin, Germany
|
||||
* Tel.: +49-30-46799872, Fax: +49-30-46799873, Mobile: +49-170-3801903
|
||||
* E-Mail: uwe.vogt@uv-software.de, Homepage: http://www.uv-software.de/
|
||||
*/
|
||||
+310
@@ -0,0 +1,310 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR GPL-3.0-or-later */
|
||||
/*
|
||||
* Software for Industrial Communication, Motion Control and Automation
|
||||
*
|
||||
* Copyright (c) 2002-2024 Uwe Vogt, UV Software, Berlin (info@uv-software.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Module 'logger'
|
||||
*
|
||||
* This module is dual-licensed under the BSD 2-Clause "Simplified" License
|
||||
* and under the GNU General Public License v3.0 (or any later version).
|
||||
* You can choose between one of them if you use this module.
|
||||
*
|
||||
* BSD 2-Clause "Simplified" License:
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS MODULE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS MODULE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* GNU General Public License v3.0 or later:
|
||||
* This module is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This module is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this module. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file logger_w.c
|
||||
*
|
||||
* @brief Writing log messages into an ASCII file.
|
||||
*
|
||||
* @remarks Windows compatible variant (_WIN32 and _WIN64)
|
||||
*
|
||||
* @author $Author: quaoar $
|
||||
*
|
||||
* @version $Rev: 811 $
|
||||
*
|
||||
* @addtogroup logger
|
||||
* @{
|
||||
*/
|
||||
#include "logger.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
|
||||
/* ----------- options ------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- defines ------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef LOG_BUF_SIZE
|
||||
#define LOG_BUF_SIZE 1024
|
||||
#endif
|
||||
#ifndef LOG_CONSOLE
|
||||
#define LOG_CONSOLE stderr
|
||||
#endif
|
||||
#define LOG_PIPE_SIZE 16384
|
||||
|
||||
|
||||
/* ----------- types --------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- prototypes ---------------------------------------------
|
||||
*/
|
||||
|
||||
static DWORD WINAPI logging(LPVOID lpParam);
|
||||
|
||||
|
||||
/* ----------- variables ----------------------------------------------
|
||||
*/
|
||||
|
||||
static HANDLE hThread = NULL;
|
||||
static HANDLE hMutex = NULL;
|
||||
static HANDLE hPipo, hPipi;
|
||||
static FILE *logger = NULL;
|
||||
static volatile int running = 0;
|
||||
|
||||
|
||||
/* ----------- functions ----------------------------------------------
|
||||
*/
|
||||
|
||||
int log_init(const char *pathname/*, flags*/)
|
||||
{
|
||||
/* sanity check */
|
||||
errno = 0;
|
||||
if (logger) {
|
||||
errno = EALREADY;
|
||||
return -1;
|
||||
}
|
||||
/* create a pipe for log messages from a critical thread */
|
||||
if (!CreatePipe(&hPipo, &hPipi, NULL, // default security attributes
|
||||
LOG_PIPE_SIZE)) {
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
/* create a mutex for mutual write operations into the log file */
|
||||
if ((hMutex = CreateMutex(NULL, // default security attributes
|
||||
FALSE, // initially not owned
|
||||
NULL)) == NULL) {
|
||||
CloseHandle(hPipi);
|
||||
CloseHandle(hPipo);
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
/* create a logging thread for log messages from other threads */
|
||||
if ((hThread = CreateThread(NULL, // default security attributes
|
||||
0, // use default stack size
|
||||
logging, // thread function name
|
||||
NULL, // argument to thread function
|
||||
0, // use default creation flags
|
||||
NULL)) == NULL) {
|
||||
CloseHandle(hMutex);
|
||||
CloseHandle(hPipi);
|
||||
CloseHandle(hPipo);
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
/* open a log file (or use stderr when not given) */
|
||||
if (pathname) {
|
||||
if ((fopen_s(&logger, pathname, "w+")) != 0) {
|
||||
/* errno set */
|
||||
CloseHandle(hThread);
|
||||
CloseHandle(hMutex);
|
||||
CloseHandle(hPipi);
|
||||
CloseHandle(hPipo);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
logger = LOG_CONSOLE;
|
||||
}
|
||||
/* write a header into the log file */
|
||||
char str[26];
|
||||
time_t now = time(NULL);
|
||||
ctime_s(str, 26, &now);
|
||||
str[strlen(str)-1] = '\0';
|
||||
fprintf(logger, "*** ArctosRobotCnc (%s) ***\n", str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int log_exit(void)
|
||||
{
|
||||
/* sanity check */
|
||||
errno = 0;
|
||||
if (!logger) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
/* kill the logging thread and release all resources */
|
||||
running = 0;
|
||||
(void)CancelIoEx(hPipo, NULL); // to cancel ReadPipe
|
||||
(void)SetEvent(hThread);
|
||||
(void)WaitForSingleObject(hThread, 3000);
|
||||
(void)CloseHandle(hThread);
|
||||
(void)CloseHandle(hMutex);
|
||||
(void)CloseHandle(hPipi);
|
||||
(void)CloseHandle(hPipo);
|
||||
hThread = NULL;
|
||||
hMutex = NULL;
|
||||
/* write a footer into the log file */
|
||||
char str[26];
|
||||
time_t now = time(NULL);
|
||||
ctime_s(str, 26, &now);
|
||||
str[strlen(str)-1] = '\0';
|
||||
fprintf(logger, "*** ArctosRobotCnc (%s) ***\n", str);
|
||||
|
||||
/* that´s all folks! */
|
||||
if (logger != LOG_CONSOLE)
|
||||
fclose(logger);
|
||||
logger = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int log_sync(const uint8_t *buffer, size_t nbytes)
|
||||
{
|
||||
static uint64_t n = 1;
|
||||
size_t i;
|
||||
|
||||
/* sanity check */
|
||||
errno = 0;
|
||||
if (!logger) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
/* enter critical section */
|
||||
if (WaitForSingleObject(hMutex, INFINITE) != WAIT_OBJECT_0) {
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
/* write a log message into the log file */
|
||||
fprintf(logger, ">>> (%" PRIu64 ")", n++);
|
||||
for (i = 0; i < nbytes; i++)
|
||||
fprintf(logger, " %02X", buffer[i]);
|
||||
fputc('\n', logger);
|
||||
|
||||
/* leave critical section */
|
||||
(void)ReleaseMutex(hMutex);
|
||||
|
||||
/* return the number of bytes written */
|
||||
return (int)i;
|
||||
}
|
||||
|
||||
int log_async(const uint8_t *buffer, size_t nbytes)
|
||||
{
|
||||
/* sanity check */
|
||||
errno = 0;
|
||||
if (!logger) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
/* write a log message into the pipe */
|
||||
DWORD res;
|
||||
if (!WriteFile(hPipi, buffer, (DWORD)nbytes, &res, NULL)) {
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
/* return the number of bytes written */
|
||||
return (int)res;
|
||||
}
|
||||
|
||||
int log_printf(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
int res;
|
||||
|
||||
/* sanity check */
|
||||
errno = 0;
|
||||
if (!logger) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
/* enter critical section */
|
||||
if (WaitForSingleObject(hMutex, INFINITE) != WAIT_OBJECT_0) {
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
/* write a formatted string into the log file */
|
||||
va_start(args, format);
|
||||
res = vfprintf(logger, format, args);
|
||||
va_end(args);
|
||||
fflush(logger);
|
||||
|
||||
/* leave critical section */
|
||||
(void)ReleaseMutex(hMutex);
|
||||
|
||||
/* return result from printf */
|
||||
return res;
|
||||
}
|
||||
|
||||
static DWORD WINAPI logging(LPVOID lpParam)
|
||||
{
|
||||
uint8_t buffer[LOG_BUF_SIZE];
|
||||
DWORD nbytes, i;
|
||||
DWORD64 n = 1;
|
||||
(void)lpParam;
|
||||
|
||||
if (running)
|
||||
return 1;
|
||||
|
||||
running = 1;
|
||||
while (running) {
|
||||
if (ReadFile(hPipo, buffer, LOG_BUF_SIZE, &nbytes, NULL)) {
|
||||
if (logger && (nbytes > 0)) {
|
||||
if (WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) {
|
||||
fprintf(logger, "<<< (%" PRIu64 ")", n++);
|
||||
for (i = 0; i < nbytes; i++)
|
||||
fprintf(logger, " %02X", buffer[i]);
|
||||
fputc('\n', logger);
|
||||
(void)ReleaseMutex(hMutex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Uwe Vogt, UV Software, Chausseestrasse 33 A, 10115 Berlin, Germany
|
||||
* Tel.: +49-30-46799872, Fax: +49-30-46799873, Mobile: +49-170-3801903
|
||||
* E-Mail: uwe.vogt@uv-software.de, Homepage: http://www.uv-software.de/
|
||||
*/
|
||||
@@ -0,0 +1,229 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR GPL-3.0-or-later */
|
||||
/*
|
||||
* Software for Industrial Communication, Motion Control and Automation
|
||||
*
|
||||
* Copyright (c) 2002-2024 Uwe Vogt, UV Software, Berlin (info@uv-software.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Module 'queue'
|
||||
*
|
||||
* This module is dual-licensed under the BSD 2-Clause "Simplified" License
|
||||
* and under the GNU General Public License v3.0 (or any later version).
|
||||
* You can choose between one of them if you use this module.
|
||||
*
|
||||
* BSD 2-Clause "Simplified" License:
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS MODULE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS MODULE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* GNU General Public License v3.0 or later:
|
||||
* This module is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This module is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this module. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file queue.h
|
||||
*
|
||||
* @brief Queue for intertask communication.
|
||||
*
|
||||
* @remarks A producer thread puts a data element of configurable size into
|
||||
* the queue (FIFO), if it is not full.
|
||||
* A consumer thread waits until at least one element is available
|
||||
* in the queue and dequeues it, or returns when a time-out occurs.
|
||||
*
|
||||
* @note When the queue is full no further data element will be enqueued.
|
||||
*
|
||||
* @author $Author: quaoar $
|
||||
*
|
||||
* @version $Rev: 811 $
|
||||
*
|
||||
* @defgroup queue Waitable Queue
|
||||
* @{
|
||||
*/
|
||||
#ifndef QUEUE_H_INCLUDED
|
||||
#define QUEUE_H_INCLUDED
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
/* ----------- options ------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- defines ------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- types --------------------------------------------------
|
||||
*/
|
||||
|
||||
typedef void *queue_t; /**< queue (opaque data type) */
|
||||
|
||||
|
||||
/* ----------- variables ----------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- prototypes ---------------------------------------------
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @brief creates an instance of a waitable queue (constructor).
|
||||
*
|
||||
* @param[in] numElem - maximum number of elements in the queue
|
||||
* @param[in] elemSize - size of a queue element (number of bytes)
|
||||
*
|
||||
* @returns pointer to a queue instance if successful, or NULL on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval EINVAL - invalid argument (numElem or elemSize)
|
||||
* @retval ENOMEM - out of memory (insufficient storage space)
|
||||
* @retval 'errno' - error code from called system functions:
|
||||
* 'pthread_mutex_init', 'pthread_cond_init'
|
||||
*/
|
||||
extern queue_t queue_create(size_t numElem, size_t elemSize);
|
||||
|
||||
|
||||
/** @brief destroys the queue instance (destructor).
|
||||
*
|
||||
* @param[in] queue - pointer to a queue instance
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval EFAULT - bad address (invalid queue instance)
|
||||
* @retval 'errno' - error code from called system functions:
|
||||
* 'pthread_mutex_destroy', 'pthread_cond_destroy'
|
||||
*/
|
||||
extern int queue_destroy(queue_t queue);
|
||||
|
||||
|
||||
/** @brief removes all enqueued elements from the queue and reset the
|
||||
* overflow indicator and the overflow counter.
|
||||
*
|
||||
* @param[in] queue - pointer to a queue instance
|
||||
*
|
||||
* @returns the number of elements removed from the queue if successful, or
|
||||
* a negative value on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval EFAULT - bad address (invalid queue instance)
|
||||
*/
|
||||
extern int queue_clear(queue_t queue);
|
||||
|
||||
|
||||
/** @brief enqueues one element of n data bytes into the queue,
|
||||
* if the queue is not full.
|
||||
*
|
||||
* @remarks The function copies not more data bytes than the size of the
|
||||
* queue allows (see parameter 'elemSize' of 'queue_create').
|
||||
* @see queue_create
|
||||
*
|
||||
* @param[in] queue - pointer to a queue instance
|
||||
* @param[in] element - data element to be enqueued
|
||||
* @param[in] nbytes - number of bytes to be copied into the queue
|
||||
*
|
||||
* @returns the number of bytes copied into the queue if successful, or
|
||||
* a negative value on error.
|
||||
*
|
||||
* @retval -20 - when the queue is full (CAN API compatible)
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval EFAULT - bad address (invalid queue instance)
|
||||
* @retval EINVAL - invalid argument (element or nbytes)
|
||||
* @retval ENSPC - no space left (queue is full)
|
||||
*/
|
||||
extern int queue_enqueue(queue_t queue, const void *element, size_t nbytes);
|
||||
|
||||
|
||||
/** @brief dequeues one element from the queue, if any.
|
||||
*
|
||||
* @param[in] queue - pointer to a queue instance
|
||||
* @param[out] element - pointer to an array into which the element is copied
|
||||
* @param[in] maxbytes - maximum number of bytes to be copied from the queue
|
||||
* @param[in] timeout - time to wait for elements available in the queue:
|
||||
* 0 means the function returns immediately,
|
||||
* 65535 means blocking read, and any other
|
||||
* value means the time to wait im milliseconds
|
||||
*
|
||||
* @returns the number of bytes copied from the queue if successful, or
|
||||
* a negative value on error.
|
||||
*
|
||||
* @retval -30 - when the queue is empty (CAN API compatible)
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval EFAULT - bad address (invalid queue instance)
|
||||
* @retval EINVAL - invalid argument (element or maxbytes)
|
||||
* @retval ENOMSG - no data available (queue empty)
|
||||
*/
|
||||
extern int queue_dequeue(queue_t queue, void *element, size_t maxbytes, uint16_t timeout);
|
||||
|
||||
|
||||
/** @brief returns true when an overflow has occurred.
|
||||
*
|
||||
* @remarks The overflow indicator can be reset by a call of 'queue_clear'.
|
||||
* @see queue_clear
|
||||
*
|
||||
* @param[in] queue - pointer to a queue instance
|
||||
* @param[out] counter - number of overflows (optional)
|
||||
*
|
||||
* @returns 0 when no overflow has occurred, or a none-zero value otherwise.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval EFAULT - bad address (invalid queue instance)
|
||||
*/
|
||||
extern bool queue_overflow(queue_t queue, uint64_t *counter);
|
||||
|
||||
|
||||
/** @brief signals waiting objects, if any.
|
||||
*
|
||||
* @param[in] queue - pointer to a queue instance
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error.
|
||||
*/
|
||||
extern int queue_signal(queue_t queue);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* QUEUE_H_INCLUDED */
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Uwe Vogt, UV Software, Chausseestrasse 33 A, 10115 Berlin, Germany
|
||||
* Tel.: +49-30-46799872, Fax: +49-30-46799873, Mobile: +49-170-3801903
|
||||
* E-Mail: uwe.vogt@uv-software.de, Homepage: http://www.uv-software.de/
|
||||
*/
|
||||
@@ -0,0 +1,382 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR GPL-3.0-or-later */
|
||||
/*
|
||||
* Software for Industrial Communication, Motion Control and Automation
|
||||
*
|
||||
* Copyright (c) 2002-2024 Uwe Vogt, UV Software, Berlin (info@uv-software.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Module 'queue'
|
||||
*
|
||||
* This module is dual-licensed under the BSD 2-Clause "Simplified" License
|
||||
* and under the GNU General Public License v3.0 (or any later version).
|
||||
* You can choose between one of them if you use this module.
|
||||
*
|
||||
* BSD 2-Clause "Simplified" License:
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS MODULE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS MODULE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* GNU General Public License v3.0 or later:
|
||||
* This module is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This module is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this module. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file queue.c
|
||||
*
|
||||
* @brief Queue for intertask communication.
|
||||
*
|
||||
* @remarks Windows compatible variant (_WIN32 and _WIN64)
|
||||
*
|
||||
* @author $Author: quaoar $
|
||||
*
|
||||
* @version $Rev: 811 $
|
||||
*
|
||||
* @addtogroup queue
|
||||
* @{
|
||||
*/
|
||||
#include "queue.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
|
||||
/* ----------- options ------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- defines ------------------------------------------------
|
||||
*/
|
||||
|
||||
#define MIN(x,y) ((x) < (y) ? (x) : (y))
|
||||
|
||||
#define ENTER_CRITICAL_SECTION(que) do { (void)WaitForSingleObject(que->hMutex, INFINITE); } while(0)
|
||||
#define LEAVE_CRITICAL_SECTION(que) do { (void)ReleaseMutex(que->hMutex); } while(0)
|
||||
|
||||
|
||||
/* ----------- types --------------------------------------------------
|
||||
*/
|
||||
|
||||
typedef struct object_t_ {
|
||||
size_t size;
|
||||
size_t used;
|
||||
size_t head;
|
||||
size_t tail;
|
||||
uint8_t *queueElem;
|
||||
size_t elemSize;
|
||||
HANDLE hMutex;
|
||||
HANDLE hEvent;
|
||||
struct overflow_t {
|
||||
bool flag;
|
||||
uint64_t counter;
|
||||
} ovfl;
|
||||
} object_t;
|
||||
|
||||
|
||||
/* ----------- prototypes ---------------------------------------------
|
||||
*/
|
||||
|
||||
static bool enqueue_element(object_t *queue, const void *element, size_t nbytes);
|
||||
static bool dequeue_element(object_t *queue, void *element, size_t maxbytes);
|
||||
|
||||
|
||||
/* ----------- variables ----------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- functions ----------------------------------------------
|
||||
*/
|
||||
|
||||
queue_t queue_create(size_t numElem, size_t elemSize) {
|
||||
object_t *object = (object_t*)NULL;
|
||||
|
||||
/* reset errno variable */
|
||||
errno = 0;
|
||||
/* sanity check */
|
||||
if (!numElem || !elemSize) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
/* C language constructor */
|
||||
if ((object = (object_t*)malloc(sizeof(object_t))) != NULL) {
|
||||
(void)memset(object, 0x00, sizeof(object_t));
|
||||
/* create a fixed size queue for data exchenage */
|
||||
if ((object->queueElem = malloc(numElem * elemSize)) == NULL) {
|
||||
/* errno set */
|
||||
free(object);
|
||||
return NULL;
|
||||
}
|
||||
object->elemSize = elemSize;
|
||||
object->size = numElem;
|
||||
object->used = 0;
|
||||
object->head = 0;
|
||||
object->tail = 0;
|
||||
object->ovfl.flag = false;
|
||||
object->ovfl.counter = 0U;
|
||||
/* create a mutex and an event handle */
|
||||
if ((object->hMutex = CreateMutex(
|
||||
NULL, // default security attributes
|
||||
FALSE, // initially not owned
|
||||
NULL)) == NULL) {
|
||||
errno = ENODEV;
|
||||
free(object->queueElem);
|
||||
free(object);
|
||||
return NULL;
|
||||
}
|
||||
if ((object->hEvent = CreateEvent(
|
||||
NULL, // default security attributes
|
||||
FALSE, // auto-reset event
|
||||
FALSE, // initial state is nonsignaled
|
||||
NULL)) == NULL) {
|
||||
errno = ENODEV;
|
||||
(void)CloseHandle(object->hMutex);
|
||||
free(object->queueElem);
|
||||
free(object);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return (object_t*)object;
|
||||
}
|
||||
|
||||
int queue_destroy(queue_t queue) {
|
||||
object_t *object = (object_t*)queue;
|
||||
|
||||
/* sanity check */
|
||||
errno = 0;
|
||||
if (!object) {
|
||||
errno = EFAULT;
|
||||
return -1;
|
||||
}
|
||||
/* destroy mutex and event handle */
|
||||
(void)CloseHandle(object->hEvent);
|
||||
(void)CloseHandle(object->hMutex);
|
||||
/* destroy the message queue */
|
||||
if (object->queueElem)
|
||||
free(object->queueElem);
|
||||
/* C language destructor */
|
||||
free(object);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int queue_signal(queue_t buffer) {
|
||||
object_t *object = (object_t*)buffer;
|
||||
|
||||
/* sanity check */
|
||||
errno = 0;
|
||||
if (!object) {
|
||||
errno = EFAULT;
|
||||
return -1;
|
||||
}
|
||||
/* signal event object */
|
||||
(void)SetEvent(object->hEvent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int queue_clear(queue_t queue) {
|
||||
object_t *object = (object_t*)queue;
|
||||
int res = -1;
|
||||
|
||||
/* sanity check */
|
||||
errno = 0;
|
||||
if (!object) {
|
||||
errno = EFAULT;
|
||||
return -1;
|
||||
}
|
||||
/* remove elements from queue, if any */
|
||||
ENTER_CRITICAL_SECTION(object);
|
||||
res = (int)object->used;
|
||||
object->used = 0;
|
||||
object->head = 0;
|
||||
object->tail = 0;
|
||||
object->ovfl.flag = false;
|
||||
object->ovfl.counter = 0U;
|
||||
LEAVE_CRITICAL_SECTION(object);
|
||||
/* return number of elements removed */
|
||||
return res;
|
||||
}
|
||||
|
||||
bool queue_overflow(queue_t queue, uint64_t *counter) {
|
||||
object_t *object = (object_t*)queue;
|
||||
bool res = false;
|
||||
|
||||
/* sanity check */
|
||||
errno = 0;
|
||||
if (!object) {
|
||||
errno = EFAULT;
|
||||
return false;
|
||||
}
|
||||
/* get overflow flag from queue */
|
||||
ENTER_CRITICAL_SECTION(object);
|
||||
res = object->ovfl.flag;
|
||||
if (counter)
|
||||
*counter = object->ovfl.counter;
|
||||
LEAVE_CRITICAL_SECTION(object);
|
||||
/* return overflow flag */
|
||||
return res;
|
||||
}
|
||||
|
||||
int queue_enqueue(queue_t queue, const void *element, size_t nbytes) {
|
||||
object_t *object = (object_t*)queue;
|
||||
int res = -1;
|
||||
|
||||
/* sanity check */
|
||||
errno = 0;
|
||||
if (!object) {
|
||||
errno = EFAULT;
|
||||
return -1;
|
||||
}
|
||||
if (!element || !nbytes) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
/* enqueue element (with truncation), if queue not full */
|
||||
ENTER_CRITICAL_SECTION(object);
|
||||
if (enqueue_element(object, element, nbytes)) {
|
||||
res = (int)MIN(object->elemSize, nbytes);
|
||||
(void)SetEvent(object->hEvent);
|
||||
}
|
||||
else {
|
||||
errno = ENOSPC;
|
||||
res = -20;
|
||||
}
|
||||
LEAVE_CRITICAL_SECTION(object);
|
||||
/* return number of bytes enqueued, or negative value on error */
|
||||
return res;
|
||||
}
|
||||
|
||||
int queue_dequeue(queue_t queue, void *element, size_t maxbytes, uint16_t timeout) {
|
||||
object_t *object = (object_t*)queue;
|
||||
int res = -1;
|
||||
|
||||
/* sanity check */
|
||||
errno = 0;
|
||||
if (!object) {
|
||||
errno = EFAULT;
|
||||
return -1;
|
||||
}
|
||||
if (!element || !maxbytes) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
/* dequeue element (with truncation), if queue not empty */
|
||||
ENTER_CRITICAL_SECTION(object);
|
||||
if (dequeue_element(object, element, maxbytes)) {
|
||||
res = (int)MIN(object->elemSize, maxbytes);
|
||||
}
|
||||
LEAVE_CRITICAL_SECTION(object);
|
||||
|
||||
/* when no data available - blocking read or polling */
|
||||
if (res < 0) {
|
||||
if (timeout > 0U) { /* blocking read */
|
||||
switch (WaitForSingleObject(object->hEvent, (timeout != 65535U) ? (DWORD)timeout : INFINITE)) {
|
||||
case WAIT_OBJECT_0: /* event signalled */
|
||||
/* - dequeue element (with truncation) */
|
||||
ENTER_CRITICAL_SECTION(object);
|
||||
if (dequeue_element(object, element, maxbytes)) {
|
||||
res = (int)MIN(object->elemSize, maxbytes);
|
||||
}
|
||||
LEAVE_CRITICAL_SECTION(object);
|
||||
/* - when signalled externally (e.g. by SIGINT) */
|
||||
if (res < 0) {
|
||||
errno = ENOMSG;
|
||||
res = -30;
|
||||
}
|
||||
break;
|
||||
case WAIT_TIMEOUT: /* event timed out */
|
||||
errno = ETIMEDOUT;
|
||||
res = -30;
|
||||
break;
|
||||
default: /* error: no data! */
|
||||
errno = ENOMSG;
|
||||
res = -30;
|
||||
break;
|
||||
}
|
||||
} else { /* polling (timeout == 0) */
|
||||
errno = ENOMSG;
|
||||
res = -30;
|
||||
}
|
||||
}
|
||||
/* return number of bytes dequeued, or negative value on error */
|
||||
return res;
|
||||
}
|
||||
|
||||
/* --- FIFO ---
|
||||
*
|
||||
* size : total number of elements
|
||||
* head : read position of the queue
|
||||
* tail : write position of the queue
|
||||
* used : number of queued elements
|
||||
*
|
||||
* (�1) empty : used == 0
|
||||
* (�2) full : used == size && size > 0
|
||||
*/
|
||||
static bool enqueue_element(object_t *queue, const void *element, size_t nbytes) {
|
||||
assert(queue);
|
||||
assert(element);
|
||||
assert(queue->size);
|
||||
assert(queue->elemSize);
|
||||
assert(queue->queueElem);
|
||||
|
||||
if (queue->used < queue->size) {
|
||||
if (queue->used != 0U)
|
||||
queue->tail = (queue->tail + 1U) % queue->size;
|
||||
else
|
||||
queue->head = queue->tail; /* to make sure */
|
||||
(void)memcpy(&queue->queueElem[(queue->tail * queue->elemSize)], element, MIN(queue->elemSize, nbytes));
|
||||
queue->used += 1U;
|
||||
return true;
|
||||
} else {
|
||||
queue->ovfl.counter += 1U;
|
||||
queue->ovfl.flag = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool dequeue_element(object_t *queue, void *element, size_t maxbytes) {
|
||||
assert(queue);
|
||||
assert(element);
|
||||
assert(queue->size);
|
||||
assert(queue->elemSize);
|
||||
assert(queue->queueElem);
|
||||
|
||||
if (queue->used > 0U) {
|
||||
(void)memcpy(element, &queue->queueElem[(queue->head * queue->elemSize)], MIN(queue->elemSize, maxbytes));
|
||||
queue->head = (queue->head + 1U) % queue->size;
|
||||
queue->used -= 1U;
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Uwe Vogt, UV Software, Chausseestrasse 33 A, 10115 Berlin, Germany
|
||||
* Tel.: +49-30-46799872, Fax: +49-30-46799873, Mobile: +49-170-3801903
|
||||
* E-Mail: uwe.vogt@uv-software.de, Homepage: http://www.uv-software.de/
|
||||
*/
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by ArctosRobotCnc.rc
|
||||
//
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 101
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,229 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR GPL-3.0-or-later */
|
||||
/*
|
||||
* Software for Industrial Communication, Motion Control and Automation
|
||||
*
|
||||
* Copyright (c) 2002-2024 Uwe Vogt, UV Software, Berlin (info@uv-software.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Module 'serial'
|
||||
*
|
||||
* This module is dual-licensed under the BSD 2-Clause "Simplified" License
|
||||
* and under the GNU General Public License v3.0 (or any later version).
|
||||
* You can choose between one of them if you use this module.
|
||||
*
|
||||
* BSD 2-Clause "Simplified" License:
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS MODULE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS MODULE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* GNU General Public License v3.0 or later:
|
||||
* This module is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This module is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this module. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file serial.h
|
||||
*
|
||||
* @brief Serial data transmission.
|
||||
*
|
||||
* @author $Author: quaoar $
|
||||
*
|
||||
* @version $Rev: 811 $
|
||||
*
|
||||
* @defgroup serial Serial Data Transmission
|
||||
* @{
|
||||
*/
|
||||
#ifndef SERIAL_H_INCLUDED
|
||||
#define SERIAL_H_INCLUDED
|
||||
|
||||
#include "serial_attr.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/* ----------- options ------------------------------------------------
|
||||
*/
|
||||
|
||||
/** @name Compiler Switches
|
||||
* @brief Options for conditional compilation.
|
||||
* @{ */
|
||||
/** @note Set define OPTION_SERIAL_DEBUG_LEVEL to a non-zero value to compile
|
||||
* with logging of serial commumication (e.g. in the build environment).
|
||||
*/
|
||||
/** @} */
|
||||
|
||||
/* ----------- defines ------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- types --------------------------------------------------
|
||||
*/
|
||||
|
||||
typedef void *sio_port_t; /**< serial port (opaque data type) */
|
||||
|
||||
/** @brief reception callback function
|
||||
*
|
||||
* @param[in] receiver - pointer to an instance to handle the received data
|
||||
* @param[in] buffer - data buffer with the received data
|
||||
* @param[in] nbytes - number of received data bytes
|
||||
*/
|
||||
typedef void (*sio_recv_t)(const void *receiver, const uint8_t *buffer, size_t nbytes);
|
||||
|
||||
|
||||
/* ----------- variables ----------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- prototypes ---------------------------------------------
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @brief creates a port instance for communication with a serial
|
||||
* device (constructor).
|
||||
*
|
||||
* @param[in] callback - pointer to a reception callback function
|
||||
* @param[in] receiver - pointer to an instance to handle received data
|
||||
*
|
||||
* @returns a pointer to a port instance if successful, or NULL on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval ENOMEM - out of memory (insufficient storage space)
|
||||
*/
|
||||
extern sio_port_t sio_create(sio_recv_t callback, void *receiver);
|
||||
|
||||
|
||||
/** @brief destroys the port instance (destructor).
|
||||
*
|
||||
* @remarks An established connection will be terminated by this.
|
||||
*
|
||||
* @param[in] port - pointer to a port instance
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval ENODEV - no such device (invalid port instance)
|
||||
*/
|
||||
extern int sio_destroy(sio_port_t port);
|
||||
|
||||
|
||||
/** @brief establishes a connection with the serial communication device.
|
||||
*
|
||||
* @param[in] port - pointer to a port instance
|
||||
* @param[in] device - name of the serial device
|
||||
* @param[in] attr - serial port attributes (optional)
|
||||
*
|
||||
* @returns a file descriptor if successful, or a negative value on error.
|
||||
*
|
||||
* @remarks On Windows, the communication port number (zero based) is returned.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval ENODEV - no such device (invalid port instance)
|
||||
* @retval EINVAL - invalid argument (device name is NULL)
|
||||
* @retval EALREADY - already connected with the serial device
|
||||
* @retval 'errno' - error code from called system functions:
|
||||
* 'open', 'tcsetattr', 'pthread_create'
|
||||
*/
|
||||
extern int sio_connect(sio_port_t port, const char *device, const sio_attr_t *attr);
|
||||
|
||||
|
||||
/** @brief terminates the connection with the serial communication device.
|
||||
*
|
||||
* @param[in] port - pointer to a port instance
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval ENODEV - no such device (invalid port instance)
|
||||
* @retval EBADF - bad file descriptor (device not connected)
|
||||
* @retval 'errno' - error code from called system functions:
|
||||
* 'pthread_cancle', 'tcflush', 'close'
|
||||
*/
|
||||
extern int sio_disconnect(sio_port_t port);
|
||||
|
||||
|
||||
/** @brief returns the serial port attributes (baudrate, etc.).
|
||||
*
|
||||
* @param[in] port - pointer to a port instance
|
||||
* @param[out] attr - serial port attributes
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval ENODEV - no such device (invalid port instance)
|
||||
* @retval EINVAL - invalid argument (attr is NULL)
|
||||
*/
|
||||
extern int sio_get_attr(sio_port_t port, sio_attr_t* attr);
|
||||
|
||||
|
||||
/** @brief transmits n data bytes via a serial communication device.
|
||||
*
|
||||
* @remarks A connection with the serial communication device must be
|
||||
* established.
|
||||
*
|
||||
* @param[in] port - pointer to a port instance
|
||||
* @param[in] buffer - data buffer with the data to be sent
|
||||
* @param[in] nbytes - number of data bytes to be sent
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval ENODEV - no such device (invalid port instance)
|
||||
* @retval EINVAL - invalid argument (buffer is NULL)
|
||||
* @retval EBADF - bad file descriptor (device not connected)
|
||||
* @retval 'errno' - error code from called system functions:
|
||||
* 'write'
|
||||
*/
|
||||
extern int sio_transmit(sio_port_t port, const uint8_t *buffer, size_t nbytes);
|
||||
|
||||
|
||||
/** @brief signals waiting objects, if any.
|
||||
*
|
||||
* @param[in] port - pointer to a port instance
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error.
|
||||
*/
|
||||
extern int sio_signal(sio_port_t port);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* SERIAL_H_INCLUDED */
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Uwe Vogt, UV Software, Chausseestrasse 33 A, 10115 Berlin, Germany
|
||||
* Tel.: +49-30-46799872, Fax: +49-30-46799873, Mobile: +49-170-3801903
|
||||
* E-Mail: uwe.vogt@uv-software.de, Homepage: http://www.uv-software.de/
|
||||
*/
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR GPL-3.0-or-later */
|
||||
/*
|
||||
* Software for Industrial Communication, Motion Control and Automation
|
||||
*
|
||||
* Copyright (c) 2002-2024 Uwe Vogt, UV Software, Berlin (info@uv-software.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Module 'serial'
|
||||
*
|
||||
* This module is dual-licensed under the BSD 2-Clause "Simplified" License
|
||||
* and under the GNU General Public License v3.0 (or any later version).
|
||||
* You can choose between one of them if you use this module.
|
||||
*
|
||||
* BSD 2-Clause "Simplified" License:
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS MODULE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS MODULE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* GNU General Public License v3.0 or later:
|
||||
* This module is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This module is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this module. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file serial_attr.h
|
||||
*
|
||||
* @brief Serial transmission attributes.
|
||||
*
|
||||
* @author $Author: quaoar $
|
||||
*
|
||||
* @version $Rev: 811 $
|
||||
*
|
||||
* @addtogroup serial
|
||||
* @{
|
||||
*/
|
||||
#ifndef SERIAL_ATTR_H_INCLUDED
|
||||
#define SERIAL_ATTR_H_INCLUDED
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/* ----------- options ------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- defines ------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- types --------------------------------------------------
|
||||
*/
|
||||
|
||||
/** @brief Number of data bits (5, 6, 7, 8)
|
||||
*/
|
||||
typedef enum sio_bytesize_t_ {
|
||||
BYTESIZE5 = 5,
|
||||
BYTESIZE6 = 6,
|
||||
BYTESIZE7 = 7,
|
||||
BYTESIZE8 = 8,
|
||||
} sio_bytesize_t;
|
||||
|
||||
/** @brief Parity bit (none, odd, even, mark, space)
|
||||
*/
|
||||
typedef enum sio_parity_t_ {
|
||||
PARITYNONE = 0,
|
||||
PARITYODD = 1,
|
||||
PARITYEVEN = 2,
|
||||
PARITYMARK = 3,
|
||||
PARITYSPACE = 4
|
||||
} sio_parity_t;
|
||||
|
||||
/** @brief Number of stop bits (1 or 1.5 or 2)
|
||||
*/
|
||||
typedef enum sio_stopbits_t_ {
|
||||
STOPBITS1 = 0,
|
||||
STOPBITS1_5 = 1,
|
||||
STOPBITS2 = 2
|
||||
} sio_stopbits_t;
|
||||
|
||||
/** @brief Serial port attributes
|
||||
*/
|
||||
typedef struct sio_attr_t_ { /* serial port attributes: */
|
||||
uint32_t baudrate; /**< baud rate (in [bps]) */
|
||||
sio_bytesize_t bytesize; /**< number of data bits (5, 6, 7, 8) */
|
||||
sio_parity_t parity; /**< parity bit (none, odd, even, mark, space) */
|
||||
sio_stopbits_t stopbits; /**< number of stop bits (1 or 1.5 or 2) */
|
||||
} sio_attr_t;
|
||||
|
||||
|
||||
/* ----------- variables ----------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- prototypes ---------------------------------------------
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* SERIAL_ATTR_H_INCLUDED */
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Uwe Vogt, UV Software, Chausseestrasse 33 A, 10115 Berlin, Germany
|
||||
* Tel.: +49-30-46799872, Fax: +49-30-46799873, Mobile: +49-170-3801903
|
||||
* E-Mail: uwe.vogt@uv-software.de, Homepage: http://www.uv-software.de/
|
||||
*/
|
||||
+438
@@ -0,0 +1,438 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR GPL-3.0-or-later */
|
||||
/*
|
||||
* Software for Industrial Communication, Motion Control and Automation
|
||||
*
|
||||
* Copyright (c) 2002-2024 Uwe Vogt, UV Software, Berlin (info@uv-software.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Module 'serial'
|
||||
*
|
||||
* This module is dual-licensed under the BSD 2-Clause "Simplified" License
|
||||
* and under the GNU General Public License v3.0 (or any later version).
|
||||
* You can choose between one of them if you use this module.
|
||||
*
|
||||
* BSD 2-Clause "Simplified" License:
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS MODULE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS MODULE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* GNU General Public License v3.0 or later:
|
||||
* This module is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This module is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this module. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file serial.c
|
||||
*
|
||||
* @brief Serial data transmission.
|
||||
*
|
||||
* @remarks Windows compatible variant (_WIN32 and _WIN64)
|
||||
*
|
||||
* @author $Author: quaoar $
|
||||
*
|
||||
* @version $Rev: 811 $
|
||||
*
|
||||
* @addtogroup serial
|
||||
* @{
|
||||
*/
|
||||
#include "serial.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
|
||||
/* ----------- options ------------------------------------------------
|
||||
*/
|
||||
|
||||
#if (OPTION_SERIAL_DEBUG_LEVEL > 0)
|
||||
#define SERIAL_DEBUG_ERROR(...) log_printf(__VA_ARGS__)
|
||||
#else
|
||||
#define SERIAL_DEBUG_ERROR(...) while (0)
|
||||
#endif
|
||||
|
||||
#if (OPTION_SERIAL_DEBUG_LEVEL > 1)
|
||||
#define SERIAL_DEBUG_INFO(...) log_printf(__VA_ARGS__)
|
||||
#else
|
||||
#define SERIAL_DEBUG_INFO(...) while (0)
|
||||
#endif
|
||||
|
||||
#if (OPTION_SERIAL_DEBUG_LEVEL > 2)
|
||||
#define SERIAL_DEBUG_ASYNC(...) log_async(__VA_ARGS__)
|
||||
#define SERIAL_DEBUG_SYNC(...) log_sync(__VA_ARGS__)
|
||||
#undef SERIAL_DEBUG_INFO
|
||||
#define SERIAL_DEBUG_INFO(...) while (0)
|
||||
#else
|
||||
#define SERIAL_DEBUG_ASYNC(...) while (0)
|
||||
#define SERIAL_DEBUG_SYNC(...) while (0)
|
||||
#endif
|
||||
|
||||
/* ----------- defines ------------------------------------------------
|
||||
*/
|
||||
|
||||
#define BAUDRATE 57600U
|
||||
#define BYTESIZE 8
|
||||
#define STOPBITS TWOSTOPBITS
|
||||
#define IN_QUEUE 4096U
|
||||
#define OUT_QUEUE 4096U
|
||||
|
||||
|
||||
/* ----------- types --------------------------------------------------
|
||||
*/
|
||||
|
||||
typedef struct serial_t_ {
|
||||
HANDLE hPort;
|
||||
HANDLE hThread;
|
||||
sio_attr_t attr;
|
||||
sio_recv_t callback;
|
||||
void *receiver;
|
||||
int running;
|
||||
} serial_t;
|
||||
|
||||
|
||||
/* ----------- prototypes ---------------------------------------------
|
||||
*/
|
||||
|
||||
static DWORD WINAPI reception_loop(LPVOID lpParam);
|
||||
|
||||
|
||||
/* ----------- variables ----------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- functions ----------------------------------------------
|
||||
*/
|
||||
|
||||
sio_port_t sio_create(sio_recv_t callback, void *receiver) {
|
||||
serial_t *serial = (serial_t*)NULL;
|
||||
|
||||
/* reset errno variable */
|
||||
errno = 0;
|
||||
/* C language constructor */
|
||||
if ((serial = (serial_t*)malloc(sizeof(serial_t))) != NULL) {
|
||||
serial->hPort = INVALID_HANDLE_VALUE;
|
||||
serial->hThread = NULL;
|
||||
serial->attr.baudrate = BAUDRATE;
|
||||
serial->attr.bytesize = BYTESIZE8;
|
||||
serial->attr.stopbits = STOPBITS1;
|
||||
serial->attr.parity = PARITYNONE;
|
||||
serial->callback = callback;
|
||||
serial->receiver = receiver;
|
||||
serial->running = 0;
|
||||
}
|
||||
/* return a pointer to the instance */
|
||||
return (sio_port_t)serial;
|
||||
}
|
||||
|
||||
int sio_destroy(sio_port_t port) {
|
||||
serial_t *serial = (serial_t*)port;
|
||||
|
||||
/* sanity check */
|
||||
errno = 0;
|
||||
if (!serial) {
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
/* close opened file (if any) */
|
||||
(void)sio_disconnect(port);
|
||||
/* C language destructor */
|
||||
free(serial);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sio_get_attr(sio_port_t port, sio_attr_t* attr) {
|
||||
serial_t* serial = (serial_t*)port;
|
||||
|
||||
/* sanity check */
|
||||
errno = 0;
|
||||
if (!serial) {
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
if (!attr) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
/* serial attributes */
|
||||
attr->baudrate = serial->attr.baudrate;
|
||||
attr->bytesize = serial->attr.bytesize;
|
||||
attr->stopbits = serial->attr.stopbits;
|
||||
attr->parity = serial->attr.parity;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sio_signal(sio_port_t port) {
|
||||
serial_t *serial = (serial_t*)port;
|
||||
|
||||
/* sanity check */
|
||||
errno = 0;
|
||||
if (!serial) {
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
// TODO: purge?
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sio_connect(sio_port_t port, const char *device, const sio_attr_t *attr) {
|
||||
serial_t *serial = (serial_t*)port;
|
||||
int comm, n;
|
||||
char path[42];
|
||||
DCB dcb;
|
||||
DWORD errors;
|
||||
COMMTIMEOUTS timeouts;
|
||||
|
||||
/* sanity check */
|
||||
errno = 0;
|
||||
if (!serial) {
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
if (!device) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (serial->hPort != INVALID_HANDLE_VALUE) {
|
||||
errno = EALREADY;
|
||||
return -1;
|
||||
}
|
||||
/* set transmission attributes (optional) */
|
||||
if (attr) {
|
||||
serial->attr.baudrate = attr->baudrate;
|
||||
serial->attr.bytesize = attr->bytesize;
|
||||
serial->attr.stopbits = attr->stopbits;
|
||||
serial->attr.parity = attr->parity;
|
||||
}
|
||||
/* get comm port number from device name */
|
||||
if (((n = sscanf_s(device, "COM%i", &comm)) < 1) &&
|
||||
((n = sscanf_s(device, "\\\\.\\COM%i", &comm)) < 1)) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
/* create a file for the comm port */
|
||||
sprintf_s(path, 42, "\\\\.\\COM%i", comm); /* See Q115831 */
|
||||
#if defined(_UNICODE)
|
||||
if ((serial->hPort = CreateFileA(path, (GENERIC_READ | GENERIC_WRITE),
|
||||
#else
|
||||
if ((serial->hPort = CreateFile(path, (GENERIC_READ | GENERIC_WRITE),
|
||||
#endif
|
||||
0, // exclusive access
|
||||
NULL, // no security attrs
|
||||
OPEN_EXISTING, // default for devices other than files
|
||||
0, // flags: no overlapped I/O
|
||||
NULL)) == INVALID_HANDLE_VALUE) {
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
/* setup device buffers */
|
||||
if (!SetupComm(serial->hPort, IN_QUEUE, OUT_QUEUE)) {
|
||||
(void)ClearCommError(serial->hPort, &errors, NULL);
|
||||
(void)CloseHandle(serial->hPort);
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
/* purge any information in the buffer */
|
||||
if (!PurgeComm(serial->hPort,
|
||||
(PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR))) {
|
||||
CloseHandle(serial->hPort);
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
/* set up time-outs for overlapped I/O */
|
||||
timeouts.ReadIntervalTimeout = MAXDWORD;
|
||||
timeouts.ReadTotalTimeoutMultiplier = 0U;
|
||||
timeouts.ReadTotalTimeoutConstant = 0U;
|
||||
timeouts.WriteTotalTimeoutMultiplier = 0U;
|
||||
timeouts.WriteTotalTimeoutConstant = 0U;
|
||||
if (!SetCommTimeouts(serial->hPort, &timeouts)) {
|
||||
(void)CloseHandle(serial->hPort);
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
/* set up the device control block */
|
||||
dcb.DCBlength = (DWORD)sizeof(DCB);
|
||||
if (!GetCommState(serial->hPort, &dcb)) {
|
||||
(void)CloseHandle(serial->hPort);
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
int parity = (serial->attr.parity != PARITYNONE) ? 1 : 0;
|
||||
dcb.BaudRate = serial->attr.baudrate; // current baud rate
|
||||
dcb.fBinary = TRUE; // binary mode, no EOF check
|
||||
dcb.fParity = parity; // enable parity checking
|
||||
dcb.fOutxCtsFlow = FALSE; // CTS output flow control
|
||||
dcb.fOutxDsrFlow = FALSE; // DSR output flow control
|
||||
dcb.fDtrControl = DTR_CONTROL_DISABLE; // DTR flow control type
|
||||
dcb.fDsrSensitivity = FALSE; // DSR sensitivity
|
||||
dcb.fTXContinueOnXoff = FALSE; // XOFF continues Tx
|
||||
dcb.fOutX = FALSE; // XON/XOFF out flow control
|
||||
dcb.fInX = FALSE; // XON/XOFF in flow control
|
||||
dcb.fErrorChar = FALSE; // enable error replacement
|
||||
dcb.fNull = FALSE; // enable null stripping
|
||||
dcb.fRtsControl = RTS_CONTROL_DISABLE; // RTS flow control
|
||||
dcb.fAbortOnError = FALSE; // abort reads/writes on error
|
||||
//dcb.XonLim = 0; // transmit XON threshold
|
||||
//dcb.XoffLim = 30108; // transmit XOFF threshold
|
||||
dcb.ByteSize = serial->attr.bytesize; // number of bits/byte, 4-8
|
||||
dcb.Parity = serial->attr.parity; // 0-4=no,odd,even,mark,space
|
||||
dcb.StopBits = serial->attr.stopbits; // 0,1,2 = 1, 1.5, 2
|
||||
dcb.XonChar = 0x11; // Tx and Rx XON character
|
||||
dcb.XoffChar = 0x13; // Tx and Rx XOFF character
|
||||
dcb.ErrorChar = 0x00; // error replacement character
|
||||
dcb.EofChar = 0x1A; // end of input character
|
||||
//dcb.EvtChar = -3; // received event character
|
||||
if (!SetCommState(serial->hPort, &dcb)) {
|
||||
(void)CloseHandle(serial->hPort);
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
/* create the reception thread */
|
||||
if ((serial->hThread = CreateThread(
|
||||
NULL, // default security attributes
|
||||
0, // use default stack size
|
||||
reception_loop, // thread function name
|
||||
port, // argument to thread function
|
||||
0, // use default creation flags
|
||||
NULL)) == NULL) {
|
||||
(void)CloseHandle(serial->hPort);
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
/* return the comm port number (zero based) */
|
||||
(void)ClearCommError(serial->hPort, &errors, NULL);
|
||||
return (comm - 1);
|
||||
}
|
||||
|
||||
int sio_disconnect(sio_port_t port) {
|
||||
serial_t *serial = (serial_t*)port;
|
||||
DWORD errors;
|
||||
|
||||
/* sanity check */
|
||||
errno = 0;
|
||||
if (!serial) {
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
if (serial->hPort == INVALID_HANDLE_VALUE) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
/* kill the reception thread */
|
||||
serial->running = 0;
|
||||
(void)SetEvent(serial->hThread);
|
||||
(void)WaitForSingleObject(serial->hThread, 3000);
|
||||
(void)CloseHandle(serial->hThread);
|
||||
serial->hThread = NULL;
|
||||
/* purge all pending transfers */
|
||||
(void)PurgeComm(serial->hPort,
|
||||
(PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR));
|
||||
(void)ClearCommError(serial->hPort, &errors, NULL);
|
||||
/* disconnect from serial port */
|
||||
if (!CloseHandle(serial->hPort)) {
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
serial->hPort = INVALID_HANDLE_VALUE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sio_transmit(sio_port_t port, const uint8_t *buffer, size_t nbytes) {
|
||||
serial_t *serial = (serial_t*)port;
|
||||
DWORD sent = 0U;
|
||||
DWORD errors;
|
||||
|
||||
/* sanity check */
|
||||
errno = 0;
|
||||
if (!serial) {
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
if (!buffer) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (serial->hPort == INVALID_HANDLE_VALUE) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
/* send n bytes (set errno on error) */
|
||||
if (!WriteFile(serial->hPort, buffer, (DWORD)nbytes, &sent, NULL)) {
|
||||
(void)ClearCommError(serial->hPort, &errors, NULL);
|
||||
errno = EBUSY;
|
||||
return -1;
|
||||
}
|
||||
SERIAL_DEBUG_SYNC(buffer, (size_t)sent);
|
||||
// TODO: 'blocking write' required?
|
||||
return (int)sent;
|
||||
}
|
||||
|
||||
static DWORD WINAPI reception_loop(LPVOID lpParam) {
|
||||
serial_t *serial = (serial_t*)lpParam;
|
||||
DWORD errors;
|
||||
|
||||
/* sanity check */
|
||||
errno = 0;
|
||||
if (!serial) {
|
||||
errno = ENODEV;
|
||||
perror("serial");
|
||||
abort();
|
||||
}
|
||||
if (serial->hPort == INVALID_HANDLE_VALUE) {
|
||||
errno = EBADF;
|
||||
perror("serial");
|
||||
abort();
|
||||
}
|
||||
if (serial->running) {
|
||||
errno = EEXIST;
|
||||
perror("serial");
|
||||
abort();
|
||||
}
|
||||
/* Run, Forest, run! */
|
||||
serial->running = 1;
|
||||
while (serial->running) {
|
||||
DWORD nbytes = 0U;
|
||||
uint8_t buffer[1];
|
||||
|
||||
if (ReadFile(serial->hPort, buffer, 1, &nbytes, NULL)) {
|
||||
SERIAL_DEBUG_ASYNC(buffer, nbytes);
|
||||
if ((nbytes > 0) && serial->callback)
|
||||
serial->callback(serial->receiver, &buffer[0], (size_t)nbytes);
|
||||
}
|
||||
else {
|
||||
(void)ClearCommError(serial->hPort, &errors, NULL);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Uwe Vogt, UV Software, Chausseestrasse 33 A, 10115 Berlin, Germany
|
||||
* Tel.: +49-30-46799872, Fax: +49-30-46799873, Mobile: +49-170-3801903
|
||||
* E-Mail: uwe.vogt@uv-software.de, Homepage: http://www.uv-software.de/
|
||||
*/
|
||||
@@ -0,0 +1,573 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR GPL-3.0-or-later */
|
||||
/*
|
||||
* Software for Industrial Communication, Motion Control and Automation
|
||||
*
|
||||
* Copyright (c) 2002-2024 Uwe Vogt, UV Software, Berlin (info@uv-software.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Module 'SLCAN'
|
||||
*
|
||||
* This module is dual-licensed under the BSD 2-Clause "Simplified" License
|
||||
* and under the GNU General Public License v3.0 (or any later version).
|
||||
* You can choose between one of them if you use this module.
|
||||
*
|
||||
* BSD 2-Clause "Simplified" License:
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS MODULE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS MODULE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* GNU General Public License v3.0 or later:
|
||||
* This module is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This module is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this module. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file slcan.h
|
||||
*
|
||||
* @brief Lawicel SLCAN protocol.
|
||||
*
|
||||
* @author $Author: makemake $
|
||||
*
|
||||
* @version $Rev: 823 $
|
||||
*
|
||||
* @defgroup slcan Lawicel SLCAN Protocol
|
||||
* @{
|
||||
*/
|
||||
#ifndef SLCAN_H_INCLUDED
|
||||
#define SLCAN_H_INCLUDED
|
||||
|
||||
#include "serial_attr.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
/* ----------- options ------------------------------------------------
|
||||
*/
|
||||
|
||||
/** @name Compiler Switches
|
||||
* @brief Options for conditional compilation.
|
||||
* @{ */
|
||||
/** @note Set define OPTION_SLCAN_DEBUG_LEVEL to a non-zero value to compile
|
||||
* with logging of the SLCAN protocol (e.g. in the build environment).
|
||||
*/
|
||||
#if (OPTION_SLCAN_DLLEXPORT != 0)
|
||||
#define SLCANAPI __declspec(dllexport)
|
||||
#elif (OPTION_SLCAN_DLLIMPORT != 0)
|
||||
#define SLCANAPI __declspec(dllimport)
|
||||
#else
|
||||
#define SLCANAPI extern
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/* ----------- defines ------------------------------------------------
|
||||
*/
|
||||
|
||||
/** @name CAN Message flags
|
||||
* @brief CAN frame types (encoded in the CAN identifier)
|
||||
* @{
|
||||
*/
|
||||
#define CAN_STD_FRAME 0x00000000U /**< standard frame format (11-bit) */
|
||||
#define CAN_XTD_FRAME 0x80000000U /**< extended frame format (29-bit) */
|
||||
#define CAN_ERR_FRAME 0x40000000U /**< error frame (not supported) */
|
||||
#define CAN_RTR_FRAME 0x20000000U /**< remote frame */
|
||||
/** @} */
|
||||
|
||||
/** @name CAN Identifier
|
||||
* @brief CAN identifier masks
|
||||
* @{ */
|
||||
#define CAN_STD_MASK 0x000007FFU /**< highest 11-bit identifier */
|
||||
#define CAN_XTD_MASK 0x1FFFFFFFU /**< highest 29-bit identifier */
|
||||
/** @} */
|
||||
|
||||
/** @name CAN Data Length
|
||||
* @brief CAN payload length and DLC definition
|
||||
* @{ */
|
||||
#define CAN_DLC_MAX 8U /**< max. data lenth code (CAN 2.0) */
|
||||
#define CAN_LEN_MAX 8U /**< max. payload length (CAN 2.0) */
|
||||
/** @} */
|
||||
|
||||
/** @name CAN Baud Rate Indexes
|
||||
* @brief CAN baud rate indexes defined by SLCAN protocol
|
||||
* @{ */
|
||||
#define CAN_10K 0U /**< bit-rate: 10 kbit/s */
|
||||
#define CAN_20K 1U /**< bit-rate: 20 kbit/s */
|
||||
#define CAN_50K 2U /**< bit-rate: 50 kbit/s */
|
||||
#define CAN_100K 3U /**< bit-rate: 100 kbit/s */
|
||||
#define CAN_125K 4U /**< bit-rate: 120 kbit/s */
|
||||
#define CAN_250K 5U /**< bit-rate: 250 kbit/s */
|
||||
#define CAN_500K 6U /**< bit-rate: 500 kbit/s */
|
||||
#define CAN_800K 7U /**< bit-rate: 800 kbit/s */
|
||||
#define CAN_1000K 8U /**< bit-rate: 1000 kbit/s */
|
||||
#define CAN_1M CAN_1000K
|
||||
/** @} */
|
||||
|
||||
#define CAN_INFINITE 65535U /**< infinite time-out (blocking read) */
|
||||
|
||||
|
||||
/* ----------- types --------------------------------------------------
|
||||
*/
|
||||
|
||||
typedef void *slcan_port_t; /**< SLCAN port (opaque data type) */
|
||||
|
||||
typedef sio_attr_t slcan_attr_t; /**< serial port attributes */
|
||||
|
||||
/** @brief CAN message (SocketCAN compatible)
|
||||
*/
|
||||
typedef struct slcan_message_t_ { /* SLCAN message: */
|
||||
uint32_t can_id; /**< message identifier */
|
||||
uint8_t can_dlc; /**< data length code (0..8) */
|
||||
uint8_t __pad; /**< (padding) */
|
||||
uint8_t __res1; /**< (resvered for CAN FD) */
|
||||
uint8_t __res2; /**< (resvered for CAN FD) */
|
||||
uint8_t data[CAN_LEN_MAX]; /**< payload (max. 8 data bytes) */
|
||||
} slcan_message_t;
|
||||
|
||||
/** @brief SLCAN status flags
|
||||
*/
|
||||
typedef union slcan_flags_t_ { /* SLACAN status flags */
|
||||
uint8_t byte; /**< byte access */
|
||||
struct { /* bit access: */
|
||||
uint8_t BEI : 1; /**< Bus Error (BEI), see SJA1000 datasheet */
|
||||
uint8_t ALI : 1; /**< Arbitration Lost (ALI), see SJA1000 datasheet */
|
||||
uint8_t EPI : 1; /**< Error Passive (EPI), see SJA1000 datasheet */
|
||||
uint8_t : 1; /**< (not used) */
|
||||
uint8_t DOI : 1; /**< Data Overrun (DOI), see SJA1000 datasheet */
|
||||
uint8_t EI : 1; /**< Error warning (EI), see SJA1000 datasheet */
|
||||
uint8_t TxFIFO : 1; /**< CAN transmit FIFO queue full */
|
||||
uint8_t RxFIFO : 1; /**< CAN receive FIFO queue full */
|
||||
};
|
||||
} slcan_flags_t;
|
||||
|
||||
|
||||
/* ----------- variables ----------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- prototypes ---------------------------------------------
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @brief creates a port instance for communication with a SLCAN compatible
|
||||
* serial device (constructor).
|
||||
*
|
||||
* @param[in] queueSize - size of the reception queue (number of messages)
|
||||
*
|
||||
* @returns a pointer to a SLCAN instance if successful, or NULL on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval ENOMEM - out of memory (insufficient storage space)
|
||||
*/
|
||||
SLCANAPI slcan_port_t slcan_create(size_t queueSize);
|
||||
|
||||
|
||||
/** @brief destroys the port instance (destructor).
|
||||
*
|
||||
* @remarks An established connection will be terminated by this.
|
||||
*
|
||||
* @remarks Prior to this the CAN controller is set into INIT mode and
|
||||
* the CAN channel is closed (via command 'Close Channel').
|
||||
*
|
||||
* @param[in] port - pointer to a SLCAN instance
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval ENODEV - no such device (invalid port instance)
|
||||
* @retval EFAULT - bad address (release of resources)
|
||||
*/
|
||||
SLCANAPI int slcan_destroy(slcan_port_t port);
|
||||
|
||||
|
||||
/** @brief establishes a connection with the serial communication device.
|
||||
*
|
||||
* @remarks The SLCAN protocol is check by retrieving version information
|
||||
* of the CAN channel (via command 'HW/SW Version').
|
||||
*
|
||||
* @remarks Precautionary the CAN controller is set into INIT mode and
|
||||
* the CAN channel is closed (via command 'Close Channel').
|
||||
*
|
||||
* @param[in] port - pointer to a SLCAN instance
|
||||
* @param[in] device - name of the serial device
|
||||
* @param[in] attr - serial port attributes (optional)
|
||||
*
|
||||
* @returns a file descriptor if successful, or a negative value on error.
|
||||
*
|
||||
* @remarks On Windows, the communication port number (zero based) is returned.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval ENODEV - no such device (invalid port instance)
|
||||
* @retval EINVAL - invalid argument (device name is NULL)
|
||||
* @retval EALREADY - already connected with the serial device
|
||||
* @retval 'errno' - error code from called system functions:
|
||||
* 'open', 'tcsetattr', 'pthread_create'
|
||||
*/
|
||||
SLCANAPI int slcan_connect(slcan_port_t port, const char *device, const sio_attr_t *attr);
|
||||
|
||||
|
||||
/** @brief terminates the connection with the serial communication device.
|
||||
*
|
||||
* @remarks Prior to this the CAN controller is set into INIT mode and
|
||||
* the CAN channel is closed (via command 'Close Channel').
|
||||
*
|
||||
* @param[in] port - pointer to a SLCAN instance
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval ENODEV - no such device (invalid port instance)
|
||||
* @retval EBADF - bad file descriptor (device not connected)
|
||||
* @retval 'errno' - error code from called system functions:
|
||||
* 'pthread_cancel', 'tcflush', 'close'
|
||||
*/
|
||||
SLCANAPI int slcan_disconnect(slcan_port_t port);
|
||||
|
||||
|
||||
/** @brief returns the serial communication attributes (baudrate, etc.).
|
||||
*
|
||||
* @param[in] port - pointer to a SLCAN instance
|
||||
* @param[out] attr - serial communication attributes
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval ENODEV - no such device (invalid port instance)
|
||||
* @retval EINVAL - invalid argument (attr is NULL)
|
||||
*/
|
||||
SLCANAPI int slcan_get_attr(slcan_port_t port, slcan_attr_t *attr);
|
||||
|
||||
|
||||
/** @brief enables or disables ACK/NACK feedback for serial commands.
|
||||
* Defaults to ACK/NACK feedback enabled (Lawicel protocol).
|
||||
*
|
||||
* @param[in] port - pointer to a SLCAN instance
|
||||
* @param[in] on -
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval ENODEV - no such device (invalid port instance)
|
||||
*/
|
||||
SLCANAPI int slcan_set_ack(slcan_port_t port, bool on);
|
||||
|
||||
|
||||
/** @brief setup with standard CAN bit-rates.
|
||||
*
|
||||
* @remarks This command is only active if the CAN channel is closed.
|
||||
*
|
||||
* @param[in] port - pointer to a SLCAN instance
|
||||
* @param[in] index -
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval ENODEV - no such device (invalid port instance)
|
||||
* @retval EINVAL - invalid argument (index)
|
||||
* @retval EBADF - bad file descriptor (device not connected)
|
||||
* @retval EBUSY - device / resource busy (disturbance)
|
||||
* @retval EBADMSG - bad message (format or disturbance)
|
||||
* @retval ETIMEDOUT - timed out (command not acknowledged)
|
||||
* @retval 'errno' - error code from called system functions:
|
||||
* 'write', 'read', etc.
|
||||
*/
|
||||
SLCANAPI int slcan_setup_bitrate(slcan_port_t port, uint8_t index);
|
||||
|
||||
|
||||
/** @brief setup with BTR0/BTR1 CAN bit-rates; see SJA1000 datasheet.
|
||||
*
|
||||
* @remarks This command is only active if the CAN channel is closed.
|
||||
*
|
||||
* @param[in] port - pointer to a SLCAN instance
|
||||
* @param[in] btr - SJA1000 bit-timing register
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval ENODEV - no such device (invalid port instance)
|
||||
* @retval EINVAL - invalid argument (...)
|
||||
* @retval EBADF - bad file descriptor (device not connected)
|
||||
* @retval EBUSY - device / resource busy (disturbance)
|
||||
* @retval EBADMSG - bad message (format or disturbance)
|
||||
* @retval ETIMEDOUT - timed out (command not acknowledged)
|
||||
* @retval 'errno' - error code from called system functions:
|
||||
* 'write', 'read', etc.
|
||||
*/
|
||||
SLCANAPI int slcan_setup_btr(slcan_port_t port, uint16_t btr);
|
||||
|
||||
|
||||
/** @brief opens the CAN channel.
|
||||
*
|
||||
* @remarks This command is only active if the CAN channel is closed and
|
||||
* has been set up prior with either the 'Setup Bitrate' or
|
||||
* 'Setup BTR' command (i.e. initiated).
|
||||
*
|
||||
* @param[in] port - pointer to a SLCAN instance
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval ENODEV - no such device (invalid port instance)
|
||||
* @retval EINVAL - invalid argument (...)
|
||||
* @retval EBADF - bad file descriptor (device not connected)
|
||||
* @retval EBUSY - device / resource busy (disturbance)
|
||||
* @retval EBADMSG - bad message (format or disturbance)
|
||||
* @retval ETIMEDOUT - timed out (command not acknowledged)
|
||||
* @retval 'errno' - error code from called system functions:
|
||||
* 'write', 'read', etc.
|
||||
*/
|
||||
SLCANAPI int slcan_open_channel(slcan_port_t port);
|
||||
|
||||
|
||||
/** @brief closes the CAN channel.
|
||||
*
|
||||
* @remarks This command is only active if the CAN channel is open.
|
||||
*
|
||||
* @param[in] port - pointer to a SLCAN instance
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval ENODEV - no such device (invalid port instance)
|
||||
* @retval EINVAL - invalid argument (...)
|
||||
* @retval EBADF - bad file descriptor (device not connected)
|
||||
* @retval EBUSY - device / resource busy (disturbance)
|
||||
* @retval EBADMSG - bad message (format or disturbance)
|
||||
* @retval ETIMEDOUT - timed out (command not acknowledged)
|
||||
* @retval 'errno' - error code from called system functions:
|
||||
* 'write', 'read', etc.
|
||||
*/
|
||||
SLCANAPI int slcan_close_channel(slcan_port_t port);
|
||||
|
||||
|
||||
/** @brief transmits a CAN message.
|
||||
*
|
||||
* @remarks This command is only active if the CAN channel is open.
|
||||
*
|
||||
* @param[in] port - pointer to a SLCAN instance
|
||||
* @param[in] message - pointer to the message to be sent
|
||||
* @param[in] timeout - (not implemented yet)
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval ENODEV - no such device (invalid port instance)
|
||||
* @retval EINVAL - invalid argument (message)
|
||||
* @retval EBADF - bad file descriptor (device not connected)
|
||||
* @retval EBUSY - device / resource busy (disturbance)
|
||||
* @retval EBADMSG - bad message (format or disturbance)
|
||||
* @retval ETIMEDOUT - timed out (command not acknowledged)
|
||||
* @retval 'errno' - error code from called system functions:
|
||||
* 'write', 'read', etc.
|
||||
*/
|
||||
SLCANAPI int slcan_write_message(slcan_port_t port, const slcan_message_t *message, uint16_t timeout);
|
||||
|
||||
|
||||
/** @brief read one message from the message queue, if any.
|
||||
*
|
||||
* @param[in] port - pointer to a SLCAN instance
|
||||
* @param[out] message - pointer to a message buffer
|
||||
* @param[in] timeout - time to wait for the reception of a message:
|
||||
* 0 means the function returns immediately,
|
||||
* 65535 means blocking read, and any other
|
||||
* value means the time to wait im milliseconds
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error.
|
||||
*
|
||||
* @retval -30 - when the message queue is empty (CAN API compatible)
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval ENODEV - no such device (invalid port instance)
|
||||
* @retval EINVAL - invalid argument (message)
|
||||
* @retval ENOMSG - no data available (message queue empty)
|
||||
* @retval ENOSPC - no space left (message queue overflow)
|
||||
*
|
||||
* @remarks If a message has been successfully read from the message queue,
|
||||
* the value ENOSPC in the system variable 'errno' indicates that
|
||||
* a message queue overflow has occurred and that at least one
|
||||
* CAN message has been lost.
|
||||
*/
|
||||
SLCANAPI int slcan_read_message(slcan_port_t port, slcan_message_t *message, uint16_t timeout);
|
||||
|
||||
|
||||
/** @brief read status flags.
|
||||
*
|
||||
* @remarks This command is only active if the CAN channel is open.
|
||||
*
|
||||
* @param[in] port - pointer to a SLCAN instance
|
||||
* @param[out] flags - channel status flags
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval ENODEV - no such device (invalid port instance)
|
||||
* @retval EBADF - bad file descriptor (device not connected)
|
||||
* @retval EBUSY - device / resource busy (disturbance)
|
||||
* @retval EBADMSG - bad message (format or disturbance)
|
||||
* @retval ETIMEDOUT - timed out (command not acknowledged)
|
||||
* @retval 'errno' - error code from called system functions:
|
||||
* 'write', 'read', etc.
|
||||
*/
|
||||
SLCANAPI int slcan_status_flags(slcan_port_t port, slcan_flags_t *flags);
|
||||
|
||||
|
||||
/** @brief sets Acceptance Code Register (ACn Register of SJA1000).
|
||||
*
|
||||
* @remarks This command is only active if the CAN channel is initiated
|
||||
* and not opened.
|
||||
*
|
||||
* @param[in] port - pointer to a SLCAN instance
|
||||
* @param[in] code - acceptance code register
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval ENODEV - no such device (invalid port instance)
|
||||
* @retval EBADF - bad file descriptor (device not connected)
|
||||
* @retval EBUSY - device / resource busy (disturbance)
|
||||
* @retval EBADMSG - bad message (format or disturbance)
|
||||
* @retval ETIMEDOUT - timed out (command not acknowledged)
|
||||
* @retval 'errno' - error code from called system functions:
|
||||
* 'write', 'read', etc.
|
||||
*/
|
||||
SLCANAPI int slcan_acceptance_code(slcan_port_t port, uint32_t code);
|
||||
|
||||
|
||||
/** @brief sets Acceptance Mask Register (AMn Register of SJA1000).
|
||||
*
|
||||
* @remarks This command is only active if the CAN channel is initiated
|
||||
* and not opened.
|
||||
*
|
||||
* @param[in] port - pointer to a SLCAN instance
|
||||
* @param[in] mask - acceptance mask register
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval ENODEV - no such device (invalid port instance)
|
||||
* @retval EBADF - bad file descriptor (device not connected)
|
||||
* @retval EBUSY - device / resource busy (disturbance)
|
||||
* @retval EBADMSG - bad message (format or disturbance)
|
||||
* @retval ETIMEDOUT - timed out (command not acknowledged)
|
||||
* @retval 'errno' - error code from called system functions:
|
||||
* 'write', 'read', etc.
|
||||
*/
|
||||
SLCANAPI int slcan_acceptance_mask(slcan_port_t port, uint32_t mask);
|
||||
|
||||
|
||||
/** @brief get version number of both SLCAN hardware and software.
|
||||
*
|
||||
* @remarks This command is active always.
|
||||
*
|
||||
* @param[in] port - pointer to a SLCAN instance
|
||||
* @param[out] hardware - hardware version (8-bit: <major>.<minor>)
|
||||
* @param[out] software - software version (8-bit: <major>.<minor>)
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval ENODEV - no such device (invalid port instance)
|
||||
* @retval EBADF - bad file descriptor (device not connected)
|
||||
* @retval EBUSY - device / resource busy (disturbance)
|
||||
* @retval EBADMSG - bad message (format or disturbance)
|
||||
* @retval ETIMEDOUT - timed out (command not acknowledged)
|
||||
* @retval 'errno' - error code from called system functions:
|
||||
* 'write', 'read', etc.
|
||||
*/
|
||||
SLCANAPI int slcan_version_number(slcan_port_t port, uint8_t *hardware, uint8_t *software);
|
||||
|
||||
|
||||
/** @brief get serial number of the SLCAN device.
|
||||
*
|
||||
* @remarks This command is active always.
|
||||
*
|
||||
* @param[in] port - pointer to a SLCAN instance
|
||||
* @param[out] number - serial number (32-bit: 8 bytes)
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error.
|
||||
*
|
||||
* @note System variable 'errno' will be set in case of an error.
|
||||
*
|
||||
* @retval ENODEV - no such device (invalid port instance)
|
||||
* @retval EBADF - bad file descriptor (device not connected)
|
||||
* @retval EBUSY - device / resource busy (disturbance)
|
||||
* @retval EBADMSG - bad message (format or disturbance)
|
||||
* @retval ETIMEDOUT - timed out (command not acknowledged)
|
||||
* @retval 'errno' - error code from called system functions:
|
||||
* 'write', 'read', etc.
|
||||
*/
|
||||
SLCANAPI int slcan_serial_number(slcan_port_t port, uint32_t *number);
|
||||
|
||||
|
||||
/** @brief signal all waiting objects, if any.
|
||||
*
|
||||
* @param[in] port - pointer to a SLCAN instance
|
||||
*
|
||||
* @returns 0 if successful, or a negative value on error.
|
||||
*/
|
||||
SLCANAPI int slcan_signal(slcan_port_t port);
|
||||
|
||||
|
||||
/** @brief retrieves version information of the SLCAN API as a
|
||||
* zero-terminated string.
|
||||
*
|
||||
* @param[out] version_no - major and minor version number (optional)
|
||||
* @param[out] patch_no - patch number (optional)
|
||||
* @param[out] build_no - build number (optional)
|
||||
*
|
||||
* @returns pointer to a zero-terminated string, or NULL on error.
|
||||
*/
|
||||
SLCANAPI char *slcan_api_version(uint16_t *version_no, uint8_t *patch_no, uint32_t *build_no);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* SLCAN_H_INCLUDED */
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Uwe Vogt, UV Software, Chausseestrasse 33 A, 10115 Berlin, Germany
|
||||
* Tel.: +49-30-46799872, Fax: +49-30-46799873, Mobile: +49-170-3801903
|
||||
* E-Mail: uwe.vogt@uv-software.de, Homepage: http://www.uv-software.de/
|
||||
*/
|
||||
@@ -0,0 +1,31 @@
|
||||
// stdafx.h : file di inclusione per file di inclusione di sistema standard
|
||||
// o file di inclusione specifici del progetto utilizzati di frequente, ma
|
||||
// modificati raramente
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "/EgtDev/Include/EgtTargetVer.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <tchar.h>
|
||||
#include <cfloat>
|
||||
#include <cmath>
|
||||
|
||||
// in Debug riconoscimento memory leakage
|
||||
#if defined( _DEBUG)
|
||||
#define _CRTDBG_MAP_ALLOC
|
||||
#include <stdlib.h>
|
||||
#include <crtdbg.h>
|
||||
#endif
|
||||
|
||||
// in Debug controllo iteratori
|
||||
#if defined( _DEBUG)
|
||||
#define _SECURE_SCL 1
|
||||
#else
|
||||
#define _SECURE_SCL 0
|
||||
#endif
|
||||
|
||||
#include "/EgtDev/Include/EgtLibVer.h"
|
||||
|
||||
#pragma comment(lib, EGTLIBDIR "EgtGeneral" EGTLIBVER ".lib")
|
||||
@@ -0,0 +1,166 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR GPL-3.0-or-later */
|
||||
/*
|
||||
* Software for Industrial Communication, Motion Control and Automation
|
||||
*
|
||||
* Copyright (c) 2002-2024 Uwe Vogt, UV Software, Berlin (info@uv-software.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Module 'timer'
|
||||
*
|
||||
* This module is dual-licensed under the BSD 2-Clause "Simplified" License
|
||||
* and under the GNU General Public License v3.0 (or any later version).
|
||||
* You can choose between one of them if you use this module.
|
||||
*
|
||||
* BSD 2-Clause "Simplified" License:
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS MODULE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS MODULE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* GNU General Public License v3.0 or later:
|
||||
* This module is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This module is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this module. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file timer.h
|
||||
*
|
||||
* @brief A high-resolution Timer.
|
||||
*
|
||||
* Including a general purpose timer (GPT0).
|
||||
*
|
||||
* @author $Author: makemake $
|
||||
*
|
||||
* @version $Rev: 825 $
|
||||
*
|
||||
* @defgroup timer A high-resolution Timer
|
||||
* @{
|
||||
*/
|
||||
#ifndef TIMER_H_INCLUDED
|
||||
#define TIMER_H_INCLUDED
|
||||
|
||||
/* ----------- includes ----------------------------------------------
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/* ----------- options -----------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- defines -----------------------------------------------
|
||||
*/
|
||||
|
||||
#define TIMER_GPT0 NULL /**< general purpose timer */
|
||||
|
||||
#define TIMER_USEC(x) (uint64_t)((uint64_t)(x) * (uint64_t)1)
|
||||
#define TIMER_MSEC(x) (uint64_t)((uint64_t)(x) * (uint64_t)1000)
|
||||
#define TIMER_SEC(x) (uint64_t)((uint64_t)(x) * (uint64_t)1000000)
|
||||
#define TIMER_MIN(x) (uint64_t)((uint64_t)(x) * (uint64_t)60000000)
|
||||
|
||||
|
||||
/* ----------- types -------------------------------------------------
|
||||
*/
|
||||
|
||||
typedef uint64_t timer_obj_t; /**< timer object */
|
||||
typedef uint64_t timer_val_t; /**< timer value (in [usec]) */
|
||||
|
||||
|
||||
/* ----------- variables ---------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- prototypes --------------------------------------------
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @brief creates and starts a new timer object.
|
||||
*
|
||||
* @param[in] microseconds in [usec]
|
||||
*
|
||||
* @returns a timer object on success, or zero otherwise
|
||||
*/
|
||||
timer_obj_t timer_new(timer_val_t microseconds);
|
||||
|
||||
|
||||
/** @brief restarts an expired or running timer object.
|
||||
*
|
||||
* @param[in] self pointer to a timer object, or NULL (GPT0)
|
||||
* @param[in] microseconds in [usec]
|
||||
*
|
||||
* @returns none-zero value on success, or zero otherwise
|
||||
*/
|
||||
int timer_restart(timer_obj_t *self, timer_val_t microseconds);
|
||||
|
||||
|
||||
/** @brief returns True when the given timer object has expired.
|
||||
*
|
||||
* @param[in] self pointer to a timer object, or NULL (GPT0)
|
||||
*
|
||||
* @returns none-zero value when timed out, or zero otherwise
|
||||
*/
|
||||
int timer_timeout(timer_obj_t *self);
|
||||
|
||||
|
||||
/** @brief suspends the calling thread for the given time period.
|
||||
*
|
||||
* @param[in] microseconds in [usec]
|
||||
*
|
||||
* @returns none-zero value on success, or zero otherwise
|
||||
*/
|
||||
int timer_delay(timer_val_t microseconds);
|
||||
|
||||
/** @brief returns the current time as 'struct timespec'.
|
||||
*
|
||||
* @returns the current time in 'struct timespec'
|
||||
*/
|
||||
struct timespec timer_get_time(void);
|
||||
|
||||
/** @brief returns the time difference between two 'struct timespec'.
|
||||
*
|
||||
* @param[in] start pointer to a 'struct timespec'
|
||||
* @param[in] stop pointer to a 'struct timespec'
|
||||
*
|
||||
* @returns the time difference in [sec]
|
||||
*/
|
||||
double timer_diff_time(struct timespec *start, struct timespec *stop);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* TIMER_H_INCLUDED */
|
||||
/** @}
|
||||
*/
|
||||
/* ----------------------------------------------------------------------
|
||||
* Uwe Vogt, UV Software, Chausseestrasse 33 A, 10115 Berlin, Germany
|
||||
* Tel.: +49-30-46799872, Fax: +49-30-46799873, Mobile: +49-170-3801903
|
||||
* E-Mail: uwe.vogt@uv-software.de, Homepage: http://www.uv-software.de/
|
||||
*/
|
||||
@@ -0,0 +1,254 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR GPL-3.0-or-later */
|
||||
/*
|
||||
* Software for Industrial Communication, Motion Control and Automation
|
||||
*
|
||||
* Copyright (c) 2002-2024 Uwe Vogt, UV Software, Berlin (info@uv-software.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Module 'timer'
|
||||
*
|
||||
* This module is dual-licensed under the BSD 2-Clause "Simplified" License
|
||||
* and under the GNU General Public License v3.0 (or any later version).
|
||||
* You can choose between one of them if you use this module.
|
||||
*
|
||||
* BSD 2-Clause "Simplified" License:
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS MODULE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS MODULE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* GNU General Public License v3.0 or later:
|
||||
* This module is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This module is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this module. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file timer.c
|
||||
*
|
||||
* @brief A high-resolution Timer.
|
||||
*
|
||||
* @remarks Windows compatible variant (_WIN32 and _WIN64)
|
||||
*
|
||||
* @author $Author: makemake $
|
||||
*
|
||||
* @version $Rev: 822 $
|
||||
*
|
||||
* @addtogroup timer
|
||||
* @{
|
||||
*/
|
||||
|
||||
/*static const char _id[] = "$Id: timer_w.c 822 2024-08-27 09:23:18Z makemake $";*/
|
||||
|
||||
|
||||
/* ----------- includes ----------------------------------------------
|
||||
*/
|
||||
|
||||
#include "timer.h" /* interface prototypes */
|
||||
|
||||
#include <stdio.h> /* standard I/O routines */
|
||||
#include <errno.h> /* system wide error numbers */
|
||||
#include <string.h> /* string manipulation functions */
|
||||
#include <stdlib.h> /* commonly used library functions */
|
||||
#include <stdbool.h> /* standard library boolean data type */
|
||||
#include <windows.h> /* master include file for Windows */
|
||||
|
||||
#include <math.h> /* mathematical functions */
|
||||
|
||||
|
||||
/* ----------- options -----------------------------------------------
|
||||
*/
|
||||
|
||||
#define TIMER_WAITABLE_TIMER 1 /* set to zero to poll the HR timer */
|
||||
|
||||
|
||||
/* ----------- defines -----------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- types -------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- prototypes --------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* ----------- variables ---------------------------------------------
|
||||
*/
|
||||
|
||||
static timer_obj_t gpt0 = 0; /**< general purpose timer (GPT0) */
|
||||
|
||||
|
||||
/* ----------- functions ---------------------------------------------
|
||||
*/
|
||||
|
||||
timer_obj_t timer_new(uint64_t microseconds) {
|
||||
LARGE_INTEGER largeFrequency; // high-resolution timer frequency
|
||||
LARGE_INTEGER largeCounter; // high-resolution performance counter
|
||||
|
||||
LONGLONG llUntilStop = (LONGLONG)0; // counter value for the desired time-out
|
||||
|
||||
// retrieve the frequency of the high-resolution performance counter
|
||||
if (!QueryPerformanceFrequency(&largeFrequency))
|
||||
return (timer_obj_t)0;
|
||||
// retrieve the current value of the high-resolution performance counter
|
||||
if (!QueryPerformanceCounter(&largeCounter))
|
||||
return (timer_obj_t)0;
|
||||
// calculate the counter value for the desired time-out
|
||||
llUntilStop = largeCounter.QuadPart + ((largeFrequency.QuadPart * (LONGLONG)microseconds)
|
||||
/ (LONGLONG)1000000);
|
||||
return (timer_obj_t)llUntilStop;
|
||||
}
|
||||
|
||||
int timer_restart(timer_obj_t *self, uint64_t microseconds) {
|
||||
LARGE_INTEGER largeFrequency; // high-resolution timer frequency
|
||||
LARGE_INTEGER largeCounter; // high-resolution performance counter
|
||||
|
||||
LONGLONG llUntilStop = 0; // counter value when to stop (time-out)
|
||||
|
||||
// retrieve the frequency of the high-resolution performance counter
|
||||
if (!QueryPerformanceFrequency(&largeFrequency))
|
||||
return 0;
|
||||
// retrieve the current value of the high-resolution performance counter
|
||||
if (!QueryPerformanceCounter(&largeCounter))
|
||||
return 0;
|
||||
// calculate the counter value for the desired time-out
|
||||
llUntilStop = largeCounter.QuadPart + ((largeFrequency.QuadPart * (LONGLONG)microseconds)
|
||||
/ (LONGLONG)1000000);
|
||||
// update the timer instance or the general purpose timer (NULL)
|
||||
if (self)
|
||||
*self = (uint64_t)llUntilStop;
|
||||
else
|
||||
gpt0 = (uint64_t)llUntilStop;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int timer_timeout(timer_obj_t *self) {
|
||||
LARGE_INTEGER largeFrequency; // high-resolution timer frequency
|
||||
LARGE_INTEGER largeCounter; // high-resolution performance counter
|
||||
|
||||
LONGLONG llUntilStop = self ? (LONGLONG)*self : (LONGLONG)gpt0;
|
||||
|
||||
// retrieve the frequency of the high-resolution performance counter
|
||||
if (!QueryPerformanceFrequency(&largeFrequency))
|
||||
return 0;
|
||||
// retrieve the current value of the high-resolution performance counter
|
||||
if (!QueryPerformanceCounter(&largeCounter))
|
||||
return 0;
|
||||
// a time-out occurred, if the counter overruns the time-out value
|
||||
if (largeCounter.QuadPart < llUntilStop)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
int timer_delay(uint64_t microseconds) {
|
||||
#if (TIMER_WAITABLE_TIMER != 0)
|
||||
HANDLE timer;
|
||||
LARGE_INTEGER ft;
|
||||
|
||||
ft.QuadPart = -(10 * (LONGLONG)microseconds); // Convert to 100 nanosecond interval, negative value indicates relative time
|
||||
|
||||
if (microseconds >= 100) { // FIXME: Who made this decision?
|
||||
if ((timer = CreateWaitableTimer(NULL, TRUE, NULL)) != NULL) {
|
||||
SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
|
||||
WaitForSingleObject(timer, INFINITE);
|
||||
CloseHandle(timer);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// According to MSDN's documentation for Sleep:
|
||||
// | A value of zero causes the thread to relinquish the remainder of its time slice to any other
|
||||
// | thread that is ready to run.If there are no other threads ready to run, the function returns
|
||||
// | immediately, and the thread continues execution.
|
||||
Sleep(0);
|
||||
}
|
||||
return 1;
|
||||
#else
|
||||
timer_obj_t local = timer_new(microseconds);
|
||||
|
||||
while (!timer_timeout(&local)) {
|
||||
Sleep(0);
|
||||
}
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
struct timespec timer_get_time(void) {
|
||||
struct timespec now = { 0, 0 };
|
||||
static bool fInitialied = false; // initialization flag
|
||||
static struct timespec tsStartTime; // time at first call (UTC)
|
||||
static LARGE_INTEGER largeFrequency; // frequency in counts per second
|
||||
static LARGE_INTEGER largeStartCounter; // high-resolution performance counter
|
||||
LARGE_INTEGER largeCurrentCounter;
|
||||
|
||||
if (!fInitialied) {
|
||||
// retrieve the frequency of the high-resolution performance counter
|
||||
if (!QueryPerformanceFrequency(&largeFrequency))
|
||||
return now;
|
||||
// retrieve the value of the high-resolution performance counter
|
||||
if (!QueryPerformanceCounter(&largeStartCounter))
|
||||
return now;
|
||||
// retrieve the time as struct timespec
|
||||
if (!timespec_get(&tsStartTime, TIME_UTC))
|
||||
return now;
|
||||
fInitialied = true;
|
||||
}
|
||||
// retrieve the current value of the high-resolution performance counter
|
||||
if (!QueryPerformanceCounter(&largeCurrentCounter))
|
||||
return now;
|
||||
// calculate the current time with nanosecond resolution
|
||||
largeCurrentCounter.QuadPart -= largeStartCounter.QuadPart;
|
||||
time_t sec = largeCurrentCounter.QuadPart / largeFrequency.QuadPart;
|
||||
long nsec = (long)(((largeCurrentCounter.QuadPart - (sec * largeFrequency.QuadPart))
|
||||
* 1000000000UL) / largeFrequency.QuadPart);
|
||||
now.tv_sec = tsStartTime.tv_sec + sec;
|
||||
now.tv_nsec = tsStartTime.tv_nsec + nsec;
|
||||
if (now.tv_nsec >= 1000000000UL) {
|
||||
now.tv_sec += 1;
|
||||
now.tv_nsec -= 1000000000UL;
|
||||
}
|
||||
return now;
|
||||
}
|
||||
|
||||
double timer_diff_time(struct timespec *start, struct timespec *stop) {
|
||||
if (!start || !stop)
|
||||
return INFINITY;
|
||||
|
||||
return (((double)stop->tv_sec + ((double)stop->tv_nsec / 1000000000.f)) -
|
||||
((double)start->tv_sec + ((double)start->tv_nsec / 1000000000.f)));
|
||||
}
|
||||
|
||||
/* ----------- local functions ---------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/** @}
|
||||
*/
|
||||
/* ----------------------------------------------------------------------
|
||||
* Uwe Vogt, UV Software, Chausseestrasse 33 A, 10115 Berlin, Germany
|
||||
* Tel.: +49-30-46799872, Fax: +49-30-46799873, Mobile: +49-170-3801903
|
||||
* E-Mail: uwe.vogt@uv-software.de, Homepage: http://www.uv-software.de/
|
||||
*/
|
||||
Reference in New Issue
Block a user