2025-11-04 13:28:06 +01:00
# include "csv_file_reader.h"
CCSVFileReader : : CCSVFileReader ( const std : : string & ssFileName , const bool bSilent , const bool bVerbose )
{
m_silent = bSilent ;
m_verbose = bVerbose ;
m_vdSignals . clear ( ) ;
m_bsSignals . clear ( ) ;
if ( ssFileName . empty ( ) )
{
Help ( ) ;
return ;
}
else if ( ! ReadCSVVSSFile ( ssFileName ) )
{
std : : cout < < " \n Error: ' " < < ssFileName . c_str ( ) < < " ' file could not be oppened or has no vss definitions. " < < std : : endl ;
Help ( ) ;
return ;
}
}
void CCSVFileReader : : Help ( )
{
std : : cout < < std : : endl ;
std : : cout < < " ----------------------------------------------------------------------------------------------------------- " < < std : : endl ;
std : : cout < < " Required: An excel text file in csv format, the separator should be ';' " < < std : : endl ;
std : : cout < < " [required] Cells must not contain spaces. " < < std : : endl ;
std : : cout < < " [required] Column 'Device Type' must be either 'VD' or 'BS'. " < < std : : endl ;
std : : cout < < " [required] Column 'vss' must contain a character '.' or can be empty. " < < std : : endl ;
std : : cout < < " [required] Column signal direction, either 'RX' or 'TX' or empty. " < < std : : endl ;
std : : cout < < " [required] Column signal value type must not be empty. " < < std : : endl ;
std : : cout < < " [required] Column 'CAN name' must contain a character '.' (for vehicle device signal). " < < std : : endl ;
std : : cout < < " [required] Last column: in case of vehicle device it is 'CAN name'. " < < std : : endl ;
std : : cout < < " [required] Last column: in case of basic service it is avss from vehicle device. " < < std : : endl ;
2025-11-12 15:40:23 +01:00
std : : cout < < " [optional] additonal column: c++ code to recalulate the signal value in the vehicle device. " < < std : : endl ;
2025-11-04 13:28:06 +01:00
std : : cout < < " [recommended] Cells may be empty. In that case either a default string is created or previous entry is used. " < < std : : endl ;
std : : cout < < " [recommended] Column Class name should start with uppercase character. " < < std : : endl ;
std : : cout < < " [recommended] Column Function name should start with uppercase character. " < < std : : endl ;
std : : cout < < " [recommended] Column Signal name should start with lowercase character. " < < std : : endl ;
std : : cout < < " Summary can be found in file 'summary.txt'. " < < std : : endl ;
std : : cout < < " Columns: " < < std : : endl ;
2025-11-12 15:40:23 +01:00
std : : cout < < " Device Type ';' Class name ';' Function name ';' Signal name ';' Interface (vss) ';' Direction ';' Signal value type ';' 'CAN name' or Interface(vss); Formula (is optional, normally empty) " < < std : : endl ;
2025-11-04 13:28:06 +01:00
std : : cout < < std : : endl ;
std : : cout < < " e.g. " < < std : : endl ;
std : : cout < < " VD ';' FrontWheel ';' SetAverageAngle ';' averageAngle ';' Vehicle.Chassis.Axle.Row.Wheel.AverageAngle ';' RX ';' float ';' CANMsg.Angle " < < std : : endl ;
std : : cout < < " BS ';' VehicleChassisAxle ';' SetAngleStatus ';' angleStatus ';' Vehicle.Chassis.Axle.Row.Wheel ';' TX ';' uint8_t ';' Vehicle.Chassis.Axle.Row.Wheel " < < std : : endl ;
std : : cout < < " ----------------------------------------------------------------------------------------------------------- " < < std : : endl ;
}
bool CCSVFileReader : : ReadCSVVSSFile ( const std : : string & ssFileName )
{
std : : ifstream file ( ssFileName ) ;
if ( ! file . is_open ( ) )
{
std : : cerr < < " ' " < < ssFileName < < " ' could not be opened. " < < std : : endl ;
return false ;
}
std : : string line ;
uint32_t index = 1 ;
while ( std : : getline ( file , line ) )
{
line . erase ( std : : remove ( line . begin ( ) , line . end ( ) , ' \r ' ) , line . end ( ) ) ;
ReadLine ( line , index + + , vssVDColumns : : column_end ) ;
}
file . close ( ) ;
if ( ( m_vdSignals . size ( ) = = 0 ) & & ( m_bsSignals . size ( ) = = 0 ) )
{
if ( ! m_silent )
{
std : : cout < < " No valid signal definitions found. " < < std : : endl ;
}
}
return true ;
}
void CCSVFileReader : : ReadLine ( const std : : string & ssLine , const uint32_t index , size_t endColumn )
{
std : : stringstream ss ( ssLine ) ;
std : : string item ;
std : : vector < std : : string > parts ;
while ( std : : getline ( ss , item , ' ; ' ) )
{
item = Trim ( item ) ;
auto cType = GetCTypeFromIDLType ( item ) ; // maybe its a IDL type
2025-11-12 15:40:23 +01:00
if ( ( parts . size ( ) = = ( vssVDColumns : : column_vdFormula ) ) & & ! item . empty ( ) )
{
// we have the formula column, if not empty, collect everything
auto collectItem = item ;
while ( std : : getline ( ss , item , ' ; ' ) )
{
collectItem . append ( " ; " ) ;
collectItem . append ( item ) ;
}
// Remove " from front and end
if ( ! collectItem . empty ( ) & & collectItem . front ( ) = = ' " ' & & collectItem . back ( ) = = ' " ' ) {
collectItem = collectItem . substr ( 1 , collectItem . size ( ) - 2 ) ;
}
// Replace all occurrences of "" with "
collectItem = std : : regex_replace ( collectItem , std : : regex ( " \" \" " ) , " \" " ) ;
parts . push_back ( collectItem ) ;
}
else if ( MustNotContainSpaces ( item ) | | ( cType . size ( ) ! = 0 ) )
2025-11-04 13:28:06 +01:00
{
parts . push_back ( item ) ;
}
else
{
if ( ( m_vdSignals . size ( ) ! = 0 ) & & ( m_bsSignals . size ( ) & & ! m_silent ) )
{
std : : cout < < " Invalid, contains spaces: ' " < < item < < " ' " < < std : : endl ;
}
}
}
2025-11-12 15:40:23 +01:00
if ( parts . size ( ) = = ( endColumn - 1 ) )
{
parts . push_back ( " " ) ;
}
2025-11-04 13:28:06 +01:00
if ( parts . size ( ) > = endColumn )
{
ParseColumns ( parts , index ) ;
}
}
void CCSVFileReader : : ParseColumns ( const std : : vector < std : : string > & parts , const uint32_t index )
{
static std : : string vss = " " ;
static sdv : : core : : ESignalDirection signalDirection = sdv : : core : : ESignalDirection : : sigdir_rx ;
if ( parts . size ( ) > = vssVDColumns : : column_end )
{
if ( parts [ vssVDColumns : : column_vssDefinition ] . size ( ) ! = 0 ) // if vss is empty use previous one
{
vss = parts [ vssVDColumns : : column_vssDefinition ] ;
}
if ( ! MustContainDotOrIsEmpty ( vss , false ) ) // vss can be empty
{
if ( ( m_vdSignals . size ( ) ! = 0 ) & & ( m_bsSignals . size ( ) & & ! m_silent ) )
{
std : : cout < < " Invalid, contains no charatcer '.': ' " < < vss < < " ', line number " < < std : : to_string ( index ) < < std : : endl ;
}
return ;
}
vss = ValidateVSSFormatNumbers ( vss ) ;
if ( ! parts [ vssVDColumns : : column_direction ] . compare ( " RX " ) )
signalDirection = sdv : : core : : ESignalDirection : : sigdir_rx ;
if ( ! parts [ vssVDColumns : : column_direction ] . compare ( " TX " ) )
signalDirection = sdv : : core : : ESignalDirection : : sigdir_tx ;
if ( ! parts [ vssVDColumns : : column_type ] . compare ( " VD " ) )
{
if ( AddToExistingVDSignal ( m_vdSignals , vss , signalDirection , parts , index ) )
{
return ;
}
SSignalVDDefinition signal ;
signal . vssDefinition = vss ;
signal . signalDirection = signalDirection ;
signal . className = GenerateDefaultIfEmpty ( parts [ vssVDColumns : : column_className ] , " ClassName " , index ) ;
if ( AddFunctionVDDefinition ( signal , parts [ vssVDColumns : : column_functionName ] , parts [ vssVDColumns : : column_signalName ] ,
2025-11-12 15:40:23 +01:00
parts [ vssVDColumns : : column_canSignalName ] , parts [ vssVDColumns : : column_signalCType ] , parts [ vssVDColumns : : column_vdFormula ] , index ) )
2025-11-04 13:28:06 +01:00
{
ValidateVDCodeStyle ( signal , m_verbose ) ;
m_vdSignals . push_back ( signal ) ;
}
}
else
{
if ( AddToExistingBSSignal ( m_bsSignals , vss , signalDirection , parts , index ) )
{
return ;
}
std : : string vssOfVehicleDevice = parts [ vssVDColumns : : column_vdDefinition ] ;
vssOfVehicleDevice = ValidateVSSFormatNumbers ( vssOfVehicleDevice ) ;
SSignalBSDefinition signal ;
signal . vssDefinition = vss ;
signal . signalDirection = signalDirection ;
signal . className = GenerateDefaultIfEmpty ( parts [ vssVDColumns : : column_className ] , " ClassName " , index ) ;
signal . vssVDDefinition = vssOfVehicleDevice ;
if ( AddFunctionBSDefinition ( signal , parts [ vssVDColumns : : column_functionName ] , parts [ vssVDColumns : : column_signalName ] ,
parts [ vssVDColumns : : column_canSignalName ] , parts [ vssVDColumns : : column_signalCType ] , index ) )
{
ValidateBSCodeStyle ( signal , m_verbose ) ;
m_bsSignals . push_back ( signal ) ;
}
}
}
}
bool CCSVFileReader : : AddToExistingVDSignal ( std : : vector < SSignalVDDefinition > & signals , const std : : string & vssDefinition ,
const sdv : : core : : ESignalDirection & directionX , const std : : vector < std : : string > & parts , const uint32_t index )
{
for ( auto & signal : signals )
{
if ( ( signal . vssDefinition . compare ( vssDefinition ) = = 0 ) & & ( signal . signalDirection = = directionX ) )
{
if ( ( signal . className ! = parts [ vssVDColumns : : column_className ] ) & & ! m_silent )
{
std : : cout < < " Signal already found, ignore class name ' " < < parts [ vssVDColumns : : column_className ] < < " ', line number " < < std : : to_string ( index ) < < std : : endl ;
}
AddFunctionVDDefinition ( signal , parts [ vssVDColumns : : column_functionName ] , parts [ vssVDColumns : : column_signalName ] ,
2025-11-12 15:40:23 +01:00
parts [ vssVDColumns : : column_canSignalName ] , parts [ vssVDColumns : : column_signalCType ] , parts [ vssVDColumns : : column_vdFormula ] , index ) ;
2025-11-04 13:28:06 +01:00
return true ;
}
}
return false ;
}
bool CCSVFileReader : : AddToExistingBSSignal ( std : : vector < SSignalBSDefinition > & signals , const std : : string & vssDefinition ,
const sdv : : core : : ESignalDirection & directionX , const std : : vector < std : : string > & parts , const uint32_t index )
{
for ( auto & signal : signals )
{
if ( ( signal . vssDefinition . compare ( vssDefinition ) = = 0 ) & & ( signal . signalDirection = = directionX ) )
{
if ( ( signal . className ! = parts [ vssVDColumns : : column_className ] ) & & ! m_silent )
{
std : : cout < < " Signal already found, ignore class name ' " < < parts [ vssVDColumns : : column_className ] < < " ', line number " < < std : : to_string ( index ) < < std : : endl ;
}
AddFunctionBSDefinition ( signal , parts [ vssVDColumns : : column_functionName ] , parts [ vssVDColumns : : column_signalName ] ,
parts [ vssVDColumns : : column_vdDefinition ] , parts [ vssVDColumns : : column_signalCType ] , index ) ;
return true ;
}
}
return false ;
}
bool CCSVFileReader : : AddFunctionVDDefinition ( SSignalVDDefinition & signal , const std : : string & functionName , const std : : string & signalName ,
2025-11-12 15:40:23 +01:00
const std : : string & canSignalName , const std : : string & idlType , const std : : string & formula , const uint32_t index )
2025-11-04 13:28:06 +01:00
{
if ( ! MustContainDotOrIsEmpty ( canSignalName , true ) ) // can signal must must not be empty
{
if ( ( m_vdSignals . size ( ) ! = 0 ) & & ( m_bsSignals . size ( ) & & ! m_silent ) )
{
std : : cout < < " Invalid, contains no charatcer '.' or is empty: ' " < < canSignalName < < " ', line number " < < std : : to_string ( index ) < < std : : endl ;
}
return false ;
}
auto cType = GetCTypeFromIDLType ( idlType ) ;
if ( ( cType . size ( ) = = 0 ) & & ! m_silent )
{
std : : cout < < " Invalid, idl type unknown: ' " < < idlType < < " ', line number " < < std : : to_string ( index ) < < std : : endl ;
return false ;
}
SFunctionVDDefinition func ;
func . functionName = GenerateDefaultIfEmpty ( functionName , " FunctionName " , index ) ;
func . signalName = GenerateDefaultIfEmpty ( signalName , " variableName " , index ) ;
func . canSignalName = canSignalName ;
func . idlType = idlType ;
2025-11-12 15:40:23 +01:00
func . formula = formula ;
2025-11-04 13:28:06 +01:00
ValidateVDCodeStyle ( func , m_verbose ) ;
signal . vecFunctions . push_back ( func ) ;
return true ;
}
bool CCSVFileReader : : AddFunctionBSDefinition ( SSignalBSDefinition & signal , const std : : string & functionName , const std : : string & signalName ,
const std : : string & canSignalName , const std : : string & idlType , const uint32_t index )
{
if ( ! MustContainDotOrIsEmpty ( canSignalName , true ) ) // can signal must must not be empty
{
if ( ( m_vdSignals . size ( ) ! = 0 ) & & ( m_bsSignals . size ( ) & & ! m_silent ) )
{
std : : cout < < " Invalid, contains no charatcer '.' or is empty: ' " < < canSignalName < < " ', line number " < < std : : to_string ( index ) < < std : : endl ;
}
return false ;
}
auto cType = GetCTypeFromIDLType ( idlType ) ;
if ( ( cType . size ( ) = = 0 ) & & ! m_silent )
{
std : : cout < < " Invalid, idl type unknown: ' " < < idlType < < " ', line number " < < std : : to_string ( index ) < < std : : endl ;
return false ;
}
SFunctionBSDefinition func ;
func . functionName = GenerateDefaultIfEmpty ( functionName , " FunctionName " , index ) ;
func . signalName = GenerateDefaultIfEmpty ( signalName , " variableName " , index ) ;
func . idlType = idlType ;
ValidateBSCodeStyle ( func , m_verbose ) ;
signal . vecFunctions . push_back ( func ) ;
return true ;
}