ArctosRobotCnc 2.7e1 :

- primo commit.
This commit is contained in:
Dario Sassi
2025-05-25 11:04:09 +02:00
commit 28d1eeb31e
21 changed files with 5539 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
# /
/revision.h
/*.aps
/*.ncb
/*.suo
/*.user
/*.sdf
/*.opensdf
/Debug32
/Release32
/Trial32
/Debug64
/Release64
/ipch
/bin
/obj
/.vs
+626
View File
@@ -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 ;
}
BIN
View File
Binary file not shown.
+31
View File
@@ -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
+193
View File
@@ -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>
+80
View File
@@ -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>
+205
View File
@@ -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
View File
@@ -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/
*/
+154
View File
@@ -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
View File
@@ -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/
*/
+229
View File
@@ -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/
*/
+382
View File
@@ -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
View File
@@ -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
+229
View File
@@ -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
View File
@@ -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
View File
@@ -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/
*/
+1184
View File
File diff suppressed because it is too large Load Diff
+573
View File
@@ -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/
*/
+31
View File
@@ -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")
+166
View File
@@ -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/
*/
+254
View File
@@ -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/
*/