- DataTreeCore:
- Rename enum EDataType -> EJsonDataType
- DeviceCore:
- Rename enum EMBDataType -> EDeviceDataType
- Add method GetNextTypeDevice() method
- Bug fix: Add Type Name: 0 -> "none"
- Added permanent Event Channel to TDevice
- Add param Type to TDevice
- Removed UpdateInterval/Timeout params & methods from TDevice
- Separated DeviceInit() method from Init()
- Add param DeviceInit to force/avoid DeviceInit() in Init()
- Renamed and restructured SetUpdate() & SetParamScan() methods to:
SetParamAccess() & SetParamEvent()
- Converted DestroyDevice() and DestroyDeviceParam() to inline methods
- Moved methods GetCmdParam() and HandleCommand() from CModbusInterface
- Renamed TDeviceParam field:
UpdateInterval/Timeout -> EventInterval/Timeout
- Renamed TDeviceParam field: Scan -> Read
- Added TDeviceParam field: Write
655 lines
17 KiB
C++
655 lines
17 KiB
C++
/*
|
|
* DataTreeCore.cpp
|
|
*
|
|
* Created on: 5 Mar 2017
|
|
* Author: wentzelc
|
|
*/
|
|
|
|
// Standard C/C++ Libraries
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
// redA Libraries
|
|
#include "DataTreeCore.h"
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
CDataMember::CDataMember( const char * pName, const int pLen )
|
|
{
|
|
if (pName) {
|
|
NameLen = (pLen == -1)? strlen( pName ) : pLen ;
|
|
Name = (char *)malloc( NameLen+1 );
|
|
memcpy( Name, pName, NameLen );
|
|
Name[ NameLen ] = 0;
|
|
}
|
|
else {
|
|
Name = NULL;
|
|
NameLen = 0;
|
|
}
|
|
|
|
Type = jtNull;
|
|
Value = NULL;
|
|
Len = 0;
|
|
|
|
FirstChild = NULL;
|
|
LastChild = NULL;
|
|
|
|
Parent = NULL;
|
|
PrevPeer = NULL;
|
|
NextPeer = NULL;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
CDataMember::CDataMember( CDataMember * pParent, const char * pName, const int pLen )
|
|
{
|
|
if (pName) {
|
|
NameLen = (pLen == -1)? strlen( pName ) : pLen ;
|
|
Name = (char *)malloc( NameLen+1 );
|
|
memcpy( Name, pName, NameLen );
|
|
Name[ NameLen ] = 0;
|
|
}
|
|
else {
|
|
Name = NULL;
|
|
NameLen = 0;
|
|
}
|
|
|
|
Type = jtNull;
|
|
Value = NULL;
|
|
Len = 0;
|
|
|
|
FirstChild = NULL;
|
|
LastChild = NULL;
|
|
|
|
if (!pParent) {
|
|
Parent = NULL;
|
|
PrevPeer = NULL;
|
|
NextPeer = NULL;
|
|
}
|
|
else {
|
|
// Clear/reset parent if not object
|
|
Parent = pParent;
|
|
if ((Parent->Type != jtNull) && (Parent->Type != jtObject) && (Parent->Type != jtArray)) {
|
|
Parent->Clear();
|
|
}
|
|
|
|
// Insert into Parent & Peer lists
|
|
Parent->Len++;
|
|
if (!Parent->FirstChild) {
|
|
PrevPeer = NULL;
|
|
NextPeer = NULL;
|
|
|
|
Parent->FirstChild = this;
|
|
Parent->LastChild = this;
|
|
}
|
|
else {
|
|
PrevPeer = Parent->LastChild;
|
|
Parent->LastChild->NextPeer = this;
|
|
NextPeer = NULL;
|
|
|
|
Parent->LastChild = this;
|
|
}
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
CDataMember::~CDataMember()
|
|
{
|
|
// Remove from parent
|
|
if (Parent) {
|
|
Parent->Len--;
|
|
if (this == Parent->LastChild) {
|
|
Parent->LastChild = PrevPeer;
|
|
}
|
|
if (this == Parent->FirstChild) {
|
|
Parent->FirstChild = NextPeer;
|
|
}
|
|
}
|
|
if (PrevPeer) {
|
|
PrevPeer->NextPeer = NextPeer;
|
|
}
|
|
if (NextPeer) {
|
|
NextPeer->PrevPeer = PrevPeer;
|
|
}
|
|
|
|
// Destroy value/children
|
|
Clear();
|
|
|
|
// Destroy member
|
|
if (Name)
|
|
free( Name );
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
CDataMember * CDataMember::CreateChild( const char * Name, const int Len )
|
|
{
|
|
CDataMember * Member;
|
|
Member = new CDataMember( this, Name, Len );
|
|
return Member;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CDataMember::Clear()
|
|
{
|
|
// Clear value
|
|
if (Value) {
|
|
free( Value );
|
|
Value = NULL;
|
|
}
|
|
|
|
// Clear children
|
|
while (FirstChild) {
|
|
delete FirstChild;
|
|
}
|
|
|
|
// Reset value
|
|
Type = jtNull;
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CDataMember::ClearCh( const char * Path )
|
|
{
|
|
CDataMember * Child = NULL;
|
|
|
|
// Find member
|
|
if (!(Child = (!Path || !*Path)? this : GetChild( Path, false )))
|
|
return false;
|
|
|
|
// Clear
|
|
Child->Clear();
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
CDataMember * CDataMember::GetChild( const char * Path, bool Create )
|
|
{
|
|
CDataMember * Member;
|
|
CDataMember ** Child;
|
|
char * Pos;
|
|
char * EndPos;
|
|
char * Key;
|
|
unsigned short KeyLen;
|
|
int Index;
|
|
int Count;
|
|
|
|
// Validate
|
|
if (!Path || !*Path) {
|
|
return this;
|
|
}
|
|
|
|
// Set init references
|
|
Child = NULL;
|
|
Member = this;
|
|
|
|
// Split path
|
|
Pos = (char*)Path;
|
|
while (Member && *Pos)
|
|
{
|
|
// Reset Child reference
|
|
Child = NULL;
|
|
|
|
// Find delimiter
|
|
if (*Pos == '[')
|
|
{
|
|
// Validate
|
|
if (Create && (Member->Type == jtNull)) {
|
|
Member->Type = jtArray; // Convert to array
|
|
}
|
|
if (Member->Type != jtArray) {
|
|
break; // Can't convert something else to an array
|
|
}
|
|
|
|
// Set Index start
|
|
Pos++;
|
|
Key = Pos;
|
|
|
|
// Get Index value
|
|
while (*Pos && (*Pos != ']'))
|
|
Pos++;
|
|
if (!*Pos)
|
|
break;
|
|
Pos++;
|
|
|
|
if (Pos == Key+1) {
|
|
// Empty bracket only allowed for create
|
|
if (!Create)
|
|
break;
|
|
Index = -1;
|
|
|
|
// find end of list
|
|
Child = &(Member->FirstChild);
|
|
while (*Child) {
|
|
Child = &((*Child)->NextPeer);
|
|
}
|
|
}
|
|
else {
|
|
// Get requested index
|
|
Index = (int)strtoul( Key, &EndPos, 10 );
|
|
if (EndPos != Pos-1)
|
|
break;
|
|
|
|
// Find element at requested index
|
|
Child = &(Member->FirstChild);
|
|
Count = 0;
|
|
while (*Child && (Count < Index)) {
|
|
Child = &((*Child)->NextPeer);
|
|
Count++;
|
|
}
|
|
}
|
|
|
|
// Create element if needed
|
|
if (!*Child && Create) {
|
|
if ((Index == -1) || (Index == Count)) {
|
|
*Child = new CDataMember( Member, NULL );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Skip separator
|
|
if (*Pos == '/') {
|
|
Pos++;
|
|
}
|
|
|
|
// Validate
|
|
if (Create && (Member->Type == jtNull)) {
|
|
Member->Type = jtObject; // Convert to object
|
|
}
|
|
if (Member->Type != jtObject) {
|
|
break; // Can't convert something else to an object
|
|
}
|
|
|
|
// Get key value
|
|
Key = Pos;
|
|
KeyLen = 0;
|
|
while (*Pos && (*Pos != '/') && (*Pos != '[')) {
|
|
KeyLen++;
|
|
Pos++;
|
|
}
|
|
|
|
// Find next parent
|
|
Child = &(Member->FirstChild);
|
|
while (*Child && (((*Child)->NameLen != KeyLen) || strncasecmp( (*Child)->Name, Key, KeyLen ))) {
|
|
Child = &((*Child)->NextPeer);
|
|
}
|
|
if (!*Child && Create) {
|
|
*Child = new CDataMember( Member, Key, KeyLen );
|
|
}
|
|
}
|
|
|
|
// Next level
|
|
Member = *Child;
|
|
}
|
|
return (Child)? *Child : NULL;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
CDataMember * CDataMember::GetChFirstChild( const char * Path, bool Create )
|
|
{
|
|
CDataMember * Member = NULL;
|
|
|
|
// Find member
|
|
Member = (!Path || !*Path)? this : GetChild( Path, Create );
|
|
|
|
// Check if valid type
|
|
if (!Member || ((Member->Type != jtObject) && (Member->Type != jtNull))) {
|
|
return NULL;
|
|
}
|
|
else if (Create && (Member->Type == jtNull)) {
|
|
Member->SetObject(); // Set newly created member to object
|
|
}
|
|
|
|
return Member->FirstChild;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
CDataMember * CDataMember::GetChElement( const char * Path, const int Index, bool Create )
|
|
{
|
|
CDataMember * Member = NULL;
|
|
CDataMember * Child = NULL;
|
|
int Count;
|
|
|
|
// Find Member
|
|
Member = (!Path || !*Path)? this : GetChild( Path, Create );
|
|
|
|
// Check if valid type
|
|
if (!Member || ((Member->Type != jtArray) && (Member->Type != jtNull))) {
|
|
return NULL;
|
|
}
|
|
else if (Create && (Member->Type == jtNull)) {
|
|
Member->SetArray(); // Set newly created member to array
|
|
}
|
|
|
|
// Find element at position
|
|
Count = 0;
|
|
Child = Member->FirstChild;
|
|
while (Child && (Count < Index)) {
|
|
Child = Child->NextPeer;
|
|
Count++;
|
|
}
|
|
|
|
// Return child
|
|
return Child;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CDataMember::DeleteCh( const char * Path )
|
|
{
|
|
CDataMember * Member;
|
|
|
|
if (!Path || !*Path) {
|
|
// No path - destroy value
|
|
Clear();
|
|
return true;
|
|
}
|
|
else if ((Member = GetChild( Path, false ))) {
|
|
// If valid path, destroy member
|
|
delete Member;
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CDataMember::SetValuePtr( EJsonDataType pType, const char * pValue, int pLen )
|
|
{
|
|
Clear();
|
|
Type = pType;
|
|
|
|
if ((pType == jtString) || (pType == jtFloat) || (pType == jtInt) || (pType == jtBool))
|
|
{
|
|
Len = (pLen == -1)? strlen(pValue) : pLen;
|
|
Value = (char*)pValue;
|
|
}
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CDataMember::SetValue( EJsonDataType pType, const char * pValue, int pLen )
|
|
{
|
|
// Clear & Update Type
|
|
Clear();
|
|
Type = pType;
|
|
|
|
// Set new primitive value
|
|
if ((pType == jtString) || (pType == jtFloat) || (pType == jtInt) || (pType == jtBool))
|
|
{
|
|
Len = (pLen == -1)? strlen(pValue) : pLen;
|
|
Value = (char*)malloc( Len+1 );
|
|
memcpy( Value, pValue, Len );
|
|
Value[Len] = 0;
|
|
}
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CDataMember::SetChObject( const char * Path )
|
|
{
|
|
CDataMember * Member;
|
|
|
|
// Validate
|
|
if (!(Member = GetChild( Path, true ))) {
|
|
return false;
|
|
}
|
|
|
|
// Set as Object
|
|
Member->SetValue( jtObject, NULL );
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CDataMember::SetChArray( const char * Path )
|
|
{
|
|
CDataMember * Member;
|
|
|
|
// Validate
|
|
if (!(Member = GetChild( Path, true ))) {
|
|
return false;
|
|
}
|
|
|
|
// Set as Object
|
|
Member->SetValue( jtArray, NULL );
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CDataMember::SetChStr( const char * Path, const char * Value, const int Len )
|
|
{
|
|
CDataMember * Member;
|
|
|
|
// Validate
|
|
if (!(Member = GetChild( Path, true ))) {
|
|
return false;
|
|
}
|
|
|
|
// Create Value
|
|
if (!Value) {
|
|
Member->SetValue( jtString, "", 0 );
|
|
}
|
|
else {
|
|
Member->SetValue( jtString, Value, Len );
|
|
}
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CDataMember::SetChInt( const char * Path, const long Value, const char * Mask )
|
|
{
|
|
CDataMember * Member;
|
|
char ValueStr[20];
|
|
|
|
// Validate
|
|
if (!(Member = GetChild( Path, true ))) {
|
|
return false;
|
|
}
|
|
|
|
// Create Value
|
|
sprintf( ValueStr, ((Mask)? Mask : "%ld"), Value );
|
|
Member->SetValue( jtInt, ValueStr );
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CDataMember::SetChFloat( const char * Path, const double Value, const char * Mask )
|
|
{
|
|
CDataMember * Member;
|
|
char ValueStr[20];
|
|
|
|
// Validate
|
|
if (!(Member = GetChild( Path, true ))) {
|
|
return false;
|
|
}
|
|
|
|
// Create Value
|
|
sprintf( ValueStr, ((Mask)? Mask : "%lf"), Value );
|
|
Member->SetValue( jtFloat, ValueStr );
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CDataMember::SetChBool( const char * Path, const bool Value )
|
|
{
|
|
CDataMember * Member;
|
|
|
|
// Validate
|
|
if (!(Member = GetChild( Path, true ))) {
|
|
return false;
|
|
}
|
|
|
|
// Create Value
|
|
Member->SetValue( jtBool, ((Value == 0)? "0" : "1") );
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CDataMember::SetChNull( const char * Path )
|
|
{
|
|
CDataMember * Member;
|
|
|
|
// Validate
|
|
if (!(Member = GetChild( Path, true ))) {
|
|
return false;
|
|
}
|
|
|
|
// Create Value
|
|
Member->SetValue( jtNull, NULL );
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
const char * CDataMember::GetChName( const char * Path )
|
|
{
|
|
CDataMember * Member;
|
|
|
|
// Validate
|
|
if (!(Member = GetChild( Path, false ))) {
|
|
return NULL;
|
|
}
|
|
return Member->Name;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
EJsonDataType CDataMember::GetChType( const char * Path )
|
|
{
|
|
CDataMember * Member;
|
|
|
|
// Validate
|
|
if (!(Member = GetChild( Path, false ))) {
|
|
return jtNull;
|
|
}
|
|
return Member->Type;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
const int CDataMember::GetChLen( const char * Path )
|
|
{
|
|
CDataMember * Member;
|
|
|
|
// Validate
|
|
if ((Member = GetChild( Path, false ))) {
|
|
return Member->Len;
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
const char * CDataMember::GetChStr( const char * Path, const char * Default, bool Create )
|
|
{
|
|
CDataMember * Member;
|
|
|
|
// Validate
|
|
if ((Member = GetChild( Path, Create )) &&
|
|
((Member->Type == jtString) || (Member->Type == jtFloat) || (Member->Type == jtInt) || (Member->Type == jtBool)) ) {
|
|
return Member->Value;
|
|
}
|
|
else if (Member && Create && (Member->Type == jtNull)) {
|
|
Member->SetStr( Default );
|
|
return Member->Value;
|
|
}
|
|
else {
|
|
return Default;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
const char * CDataMember::GetChStr( const char * Path, int &Len, const char * Default, bool Create )
|
|
{
|
|
CDataMember * Member;
|
|
|
|
// Validate
|
|
if ((Member = GetChild( Path, Create )) &&
|
|
((Member->Type == jtString) || (Member->Type == jtFloat) || (Member->Type == jtInt) || (Member->Type == jtBool)) ) {
|
|
Len = Member->Len;
|
|
return Member->Value;
|
|
}
|
|
else if (Member && Create && (Member->Type == jtNull)) {
|
|
Member->SetStr( Default );
|
|
Len = Member->Len;
|
|
return Member->Value;
|
|
}
|
|
else {
|
|
Len = 0;
|
|
return Default;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
const long CDataMember::GetChInt( const char * Path, long Default, bool Create, const char * Mask )
|
|
{
|
|
CDataMember * Member;
|
|
|
|
// Validate
|
|
if ((Member = GetChild( Path, Create )) &&
|
|
((Member->Type == jtString) || (Member->Type == jtFloat) || (Member->Type == jtInt) || (Member->Type == jtBool)) ) {
|
|
return strtol( Member->Value, NULL, 10 );
|
|
}
|
|
else if (Member && Create && (Member->Type == jtNull)) {
|
|
char TempStr[20];
|
|
sprintf( TempStr, ((Mask)? Mask : "%ld"), Default );
|
|
Member->SetValue( jtInt, TempStr );
|
|
return Default;
|
|
}
|
|
else {
|
|
return Default;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
const double CDataMember::GetChFloat( const char * Path, double Default, bool Create, const char * Mask )
|
|
{
|
|
CDataMember * Member;
|
|
|
|
// Validate
|
|
if ((Member = GetChild( Path, Create )) &&
|
|
((Member->Type == jtString) || (Member->Type == jtFloat) || (Member->Type == jtInt) || (Member->Type == jtBool)) ) {
|
|
return strtod( Member->Value, NULL );
|
|
}
|
|
else if (Member && Create && (Member->Type == jtNull)) {
|
|
char TempStr[20];
|
|
sprintf( TempStr, ((Mask)? Mask : "%lf"), Default );
|
|
Member->SetValue( jtFloat, TempStr );
|
|
return Default;
|
|
}
|
|
else {
|
|
return Default;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
const bool CDataMember::GetChBool( const char * Path, bool Default, bool Create )
|
|
{
|
|
CDataMember * Member;
|
|
|
|
// Validate
|
|
if ((Member = GetChild( Path, Create ))) {
|
|
if (Member->Type == jtString) {
|
|
return ((!*Member->Value)? false : true);
|
|
}
|
|
else if (Member->Type == jtFloat) {
|
|
return ((strtod( Member->Value, NULL ) == 0)? false : true );
|
|
}
|
|
else if (Member->Type == jtInt) {
|
|
return ((strtol( Member->Value, NULL, 10 ) == 0)? false : true );
|
|
}
|
|
else if (Member->Type == jtBool) {
|
|
return ((!strcasecmp( Member->Value, "0" ))? false : true );
|
|
}
|
|
else if ((Member->Type == jtNull) && Create) {
|
|
Member->SetValue( jtBool, ((Default)? "1" : "0") );
|
|
return Default;
|
|
}
|
|
else {
|
|
return Default;
|
|
}
|
|
}
|
|
else {
|
|
return Default;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|