git-svn-id: svn://db.shs.com.ru/pip@4 12ceb7fc-bf1f-11e4-8940-5bc7170c53b5
This commit is contained in:
BIN
doc/doxygen_sqlite3.db
Normal file
BIN
doc/doxygen_sqlite3.db
Normal file
Binary file not shown.
BIN
doc/events_handlers.odp
Normal file
BIN
doc/events_handlers.odp
Normal file
Binary file not shown.
BIN
doc/packet_detection.odp
Normal file
BIN
doc/packet_detection.odp
Normal file
Binary file not shown.
BIN
doc/piconnection.odg
Normal file
BIN
doc/piconnection.odg
Normal file
Binary file not shown.
BIN
doc/piconnection_conf.odg
Normal file
BIN
doc/piconnection_conf.odg
Normal file
Binary file not shown.
BIN
doc/piconnection_filters.odg
Normal file
BIN
doc/piconnection_filters.odg
Normal file
Binary file not shown.
BIN
doc/piconnection_senders.odg
Normal file
BIN
doc/piconnection_senders.odg
Normal file
Binary file not shown.
BIN
doc/piconsole_layout.odp
Normal file
BIN
doc/piconsole_layout.odp
Normal file
Binary file not shown.
137
src/_unsused/pigeometry.h
Executable file
137
src/_unsused/pigeometry.h
Executable file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Geometry
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIGEOMETRY_H
|
||||
#define PIGEOMETRY_H
|
||||
|
||||
#include "pimath.h"
|
||||
|
||||
template<typename Type>
|
||||
class PIP_EXPORT PIPoint {
|
||||
public:
|
||||
Type x;
|
||||
Type y;
|
||||
|
||||
PIPoint() {x = y = 0;};
|
||||
PIPoint(Type x_, Type y_) {set(x_, y_);}
|
||||
|
||||
PIPoint<Type> & set(Type x_, Type y_) {x = x_; y = y_; return *this;}
|
||||
PIPoint<Type> & move(Type x_, Type y_) {x += x_; y += y_; return *this;}
|
||||
PIPoint<Type> & move(const PIPoint<Type> & p) {x += p.x; y += p.y; return *this;}
|
||||
double angleRad() const {return atan2(y, x);}
|
||||
int angleDeg() const {return round(atan2(y, x) * 180. / M_PI);}
|
||||
PIPoint<Type> toPolar(bool isDeg = false) const {return PIPoint<Type>(sqrt(x*x + y*y), isDeg ? angleDeg() : angleRad());}
|
||||
static PIPoint<Type> fromPolar(const PIPoint<Type> & p) {return PIPoint<Type>(p.y * cos(p.x), p.y * sin(p.x));}
|
||||
|
||||
PIPoint<Type> operator +(const PIPoint<Type> & p) {return PIPoint<Type>(x + p.x, y + p.y);}
|
||||
PIPoint<Type> operator +(const Type & p) {return PIPoint<Type>(x + p, y + p);}
|
||||
PIPoint<Type> operator -(const PIPoint<Type> & p) {return PIPoint<Type>(x - p.x, y - p.y);}
|
||||
PIPoint<Type> operator -(const Type & p) {return PIPoint<Type>(x - p, y - p);}
|
||||
PIPoint<Type> operator -() {return PIPoint<Type>(-x, -y);}
|
||||
PIPoint<Type> operator *(const Type & d) {return PIPoint<Type>(x * d, y * d);}
|
||||
PIPoint<Type> operator /(const Type & d) {return PIPoint<Type>(x / d, y / d);}
|
||||
bool operator ==(const PIPoint<Type> & p) const {return (x == p.x && y == p.y);}
|
||||
bool operator !=(const PIPoint<Type> & p) const {return (x != p.x || y != p.y);}
|
||||
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
std::ostream & operator <<(std::ostream & s, const PIPoint<Type> & v) {s << '{' << v.x << ", " << v.y << '}'; return s;}
|
||||
|
||||
template<typename Type>
|
||||
class PIP_EXPORT PIRect {
|
||||
public:
|
||||
Type x0;
|
||||
Type y0;
|
||||
Type x1;
|
||||
Type y1;
|
||||
|
||||
PIRect() {x0 = y0 = x1 = y1 = 0;};
|
||||
PIRect(Type x, Type y, Type w, Type h) {set(x, y, w, h);}
|
||||
PIRect(const PIPoint<Type> & tl, const PIPoint<Type> & br) {set(tl.x, tl.y, br.x, br.y);}
|
||||
PIRect(const PIPoint<Type> & p0, const PIPoint<Type> & p1, const PIPoint<Type> & p2) {set(piMin<Type>(p0.x, p1.x, p2.x), piMin<Type>(p0.y, p1.y, p2.y),
|
||||
piMax<Type>(p0.x, p1.x, p2.x), piMax<Type>(p0.y, p1.y, p2.y));}
|
||||
|
||||
PIRect<Type> & set(Type x, Type y, Type w, Type h) {x0 = x; y0 = y; x1 = x + w; y1 = y + h; return *this;}
|
||||
bool pointIn(Type x, Type y) const {return (x <= x1 && x >= x0 && y <= y1 && y >= y0);}
|
||||
bool pointIn(const PIPoint<Type> & p) const {return pointIn(p.x, p.y);}
|
||||
bool isEmpty() const {return (x1 - x0 == 0 && y1 - y0 == 0);}
|
||||
PIRect<Type> & translate(Type x, Type y) {x0 += x; x1 += x; y0 += y; y1 += y; return *this;}
|
||||
PIRect<Type> & translate(const PIPoint<Type> & p) {x0 += p.x; x1 += p.x; y0 += p.y; y1 += p.y; return *this;}
|
||||
PIRect<Type> translated(Type x, Type y) {PIRect<Type> r(*this); r.translate(x, y); return r;}
|
||||
PIRect<Type> translated(const PIPoint<Type> & p) {PIRect<Type> r(*this); r.translate(p); return r;}
|
||||
PIRect<Type> & scale(Type x, Type y) {setWidth(width() * x); setHeight(height() * y); return *this;}
|
||||
PIRect<Type> & scale(const PIPoint<Type> & p) {setWidth(width() * p.x); setHeight(height() * p.y); return *this;}
|
||||
PIRect<Type> scaled(Type x, Type y) {PIRect<Type> r(*this); r.scale(x, y); return r;}
|
||||
PIRect<Type> scaled(const PIPoint<Type> & p) {PIRect<Type> r(*this); r.scale(p); return r;}
|
||||
PIRect<Type> & normalize() {if (x0 > x1) piSwap<Type>(x0, x1); if (y0 > y1) piSwap<Type>(y0, y1); return *this;}
|
||||
PIRect<Type> normalized() {PIRect<Type> r(*this); r.normalize(); return r;}
|
||||
PIRect<Type> & unite(const PIRect<Type> & r) {x0 = piMin<Type>(x0, r.x0); y0 = piMin<Type>(y0, r.y0); x1 = piMax<Type>(x1, r.x1); y1 = piMax<Type>(y1, r.y1); return *this;}
|
||||
PIRect<Type> united(const PIRect<Type> & rect) {PIRect<Type> r(*this); r.unite(rect); return r;}
|
||||
PIRect<Type> & intersect(const PIRect<Type> & r) {x0 = piMax<Type>(x0, r.x0); y0 = piMax<Type>(y0, r.y0); x1 = piMin<Type>(x1, r.x1); y1 = piMin<Type>(y1, r.y1); if (x0 > x1 || y0 > y1) x0 = x1 = y0 = y1 = Type(0); return *this;}
|
||||
PIRect<Type> intersected(const PIRect<Type> & rect) {PIRect<Type> r(*this); r.intersect(rect); return r;}
|
||||
Type top() const {return y0;}
|
||||
Type left() const {return x0;}
|
||||
Type right() const {return x1;}
|
||||
Type bottom() const {return y1;}
|
||||
Type width() const {return x1 - x0;}
|
||||
Type height() const {return y1 - y0;}
|
||||
PIPoint<Type> topLeft() {return PIPoint<Type>(x0, y0);}
|
||||
PIPoint<Type> topRigth() {return PIPoint<Type>(x1, y0);}
|
||||
PIPoint<Type> bottomLeft() {return PIPoint<Type>(x0, y1);}
|
||||
PIPoint<Type> bottomRight() {return PIPoint<Type>(x1, y1);}
|
||||
void setTop(Type v) {y0 = v;}
|
||||
void setLeft(Type v) {x0 = v;}
|
||||
void setRigth(Type v) {x1 = v;}
|
||||
void setBottom(Type v) {y1 = v;}
|
||||
void setWidth(Type v) {x1 = x0 + v;}
|
||||
void setHeight(Type v) {y1 = y0 + v;}
|
||||
|
||||
PIRect<Type> operator -() {return PIRect<Type>(-x0, -y0, -width(), -height());}
|
||||
void operator +=(Type x) {translate(x, x);}
|
||||
void operator +=(const PIPoint<Type> & p) {translate(p);}
|
||||
void operator -=(Type x) {translate(-x, -x);}
|
||||
void operator -=(const PIPoint<Type> & p) {translate(-p);}
|
||||
void operator *=(Type p) {x0 *= p; x1 *= p; y0 *= p; y1 *= p;}
|
||||
void operator /=(Type p) {x0 /= p; x1 /= p; y0 /= p; y1 /= p;}
|
||||
void operator |=(const PIRect<Type> & r) {unite(r);}
|
||||
void operator &=(const PIRect<Type> & r) {intersect(r);}
|
||||
PIRect<Type> operator +(const PIPoint<Type> & p) {return PIRect<Type>(*this).translated(p);}
|
||||
PIRect<Type> operator -(const PIPoint<Type> & p) {return PIRect<Type>(*this).translated(-p);}
|
||||
PIRect<Type> operator |(const PIRect<Type> & r) {return PIRect<Type>(*this).united(r);}
|
||||
PIRect<Type> operator &(const PIRect<Type> & r) {return PIRect<Type>(*this).intersected(r);}
|
||||
bool operator ==(const PIRect<Type> & r) const {return (x0 == r.x0 && y0 == r.y0 && x1 == r.x1 && y1 == r.y10);}
|
||||
bool operator !=(const PIRect<Type> & r) const {return (x0 != r.x0 || y0 != r.y0 || x1 != r.x1 || y1 != r.y10);}
|
||||
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
std::ostream & operator <<(std::ostream & s, const PIRect<Type> & v) {s << '{' << v.x0 << ", " << v.y0 << "; " << v.x1 - v.x0 << ", " << v.y1 - v.y0 << '}'; return s;}
|
||||
|
||||
typedef PIPoint<int> PIPointi;
|
||||
typedef PIPoint<uint> PIPointu;
|
||||
typedef PIPoint<float> PIPointf;
|
||||
typedef PIPoint<double> PIPointd;
|
||||
|
||||
typedef PIRect<int> PIRecti;
|
||||
typedef PIRect<uint> PIRectu;
|
||||
typedef PIRect<float> PIRectf;
|
||||
typedef PIRect<double> PIRectd;
|
||||
|
||||
#endif // PIGEOMETRY_H
|
||||
249
src/_unsused/pivariable.cpp
Executable file
249
src/_unsused/pivariable.cpp
Executable file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Variable, Struct (simple serialization)
|
||||
Copyright (C) 2013 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pivariable.h"
|
||||
|
||||
|
||||
bool PIVariant::operator ==(const PIVariant & v) const {
|
||||
if (type != v.type) return false;
|
||||
switch (type) {
|
||||
case PIVariant::Bool: return vBool == v.vBool;
|
||||
case PIVariant::Char: return vChar == v.vChar;
|
||||
case PIVariant::Short: return vShort == v.vShort;
|
||||
case PIVariant::Int: return vInt == v.vInt;
|
||||
case PIVariant::Long: return vLong == v.vLong;
|
||||
case PIVariant::LLong: return vLLong == v.vLLong;
|
||||
case PIVariant::UChar: return vUChar == v.vUChar;
|
||||
case PIVariant::UShort: return vUShort == v.vUShort;
|
||||
case PIVariant::UInt: return vUInt == v.vUInt;
|
||||
case PIVariant::ULong: return vULong == v.vULong;
|
||||
case PIVariant::ULLong: return vULLong == v.vULLong;
|
||||
case PIVariant::Float: return vFloat == v.vFloat;
|
||||
case PIVariant::Double: return vDouble == v.vDouble;
|
||||
case PIVariant::LDouble: return vLDouble == v.vLDouble;
|
||||
case PIVariant::String: return vString == v.vString;
|
||||
case PIVariant::StringList: return vStringList == v.vStringList;
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void PIVariant::setValueOnly(const PIString & s) {
|
||||
switch (type) {
|
||||
case PIVariant::Bool: vBool = s.toBool(); break;
|
||||
case PIVariant::Char: vChar = s.toChar(); break;
|
||||
case PIVariant::Short: vShort = s.toShort(); break;
|
||||
case PIVariant::Int: vInt = s.toInt(); break;
|
||||
case PIVariant::Long: vLong = s.toLong(); break;
|
||||
case PIVariant::LLong: vLLong = s.toLLong(); break;
|
||||
case PIVariant::UChar: vUChar = s.toChar(); break;
|
||||
case PIVariant::UShort: vUShort = s.toShort(); break;
|
||||
case PIVariant::UInt: vUInt = s.toInt(); break;
|
||||
case PIVariant::ULong: vULong = s.toLong(); break;
|
||||
case PIVariant::ULLong: vULLong = s.toLLong(); break;
|
||||
case PIVariant::Float: vFloat = s.toFloat(); break;
|
||||
case PIVariant::Double: vDouble = s.toDouble(); break;
|
||||
case PIVariant::LDouble: vLDouble = s.toLDouble(); break;
|
||||
case PIVariant::String: vString = s; break;
|
||||
case PIVariant::StringList: vStringList = s.split("%|%"); break;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
PIString PIVariant::stringValue() const {
|
||||
switch (type) {
|
||||
case PIVariant::Bool: return (vBool ? "true" : "false");
|
||||
case PIVariant::Char: return PIString::fromNumber(vChar);
|
||||
case PIVariant::Short: return PIString::fromNumber(vShort);
|
||||
case PIVariant::Int: return PIString::fromNumber(vInt);
|
||||
case PIVariant::Long: return PIString::fromNumber(vLong);
|
||||
case PIVariant::LLong: return PIString::fromNumber(static_cast<long>(vLLong));
|
||||
case PIVariant::UChar: return PIString::fromNumber(vUChar);
|
||||
case PIVariant::UShort: return PIString::fromNumber(vUShort);
|
||||
case PIVariant::UInt: return PIString::fromNumber(vUInt);
|
||||
case PIVariant::ULong: return PIString::fromNumber(static_cast<long>(vULong));
|
||||
case PIVariant::ULLong: return PIString::fromNumber(static_cast<long>(vULLong));
|
||||
case PIVariant::Float: return PIString::fromNumber(vFloat);
|
||||
case PIVariant::Double: return PIString::fromNumber(vDouble);
|
||||
case PIVariant::LDouble: return PIString::fromNumber(vLDouble);
|
||||
case PIVariant::String: return vString;
|
||||
case PIVariant::StringList: return vStringList.join("%|%");
|
||||
};
|
||||
return vString;
|
||||
}
|
||||
|
||||
|
||||
PIVariant PIVariant::readFromString(const PIString & s) {
|
||||
int i = s.find(':');
|
||||
if (i < 0 || s.length() < 2) return PIVariant(s);
|
||||
PIVariant ret;
|
||||
ret.type = PIVariant::fromString(s.left(i));
|
||||
ret.setValueOnly(s.right(s.length() - i - 1));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIVariant::Type PIVariant::fromString(const PIString & str) {
|
||||
PIString s = str.trimmed().toLowerCase().replace(" ", "");
|
||||
if (s == "bool") return PIVariant::Bool;
|
||||
if (s == "char" || s == "sbyte") return PIVariant::Char;
|
||||
if (s == "short" || s == "short int" || s == "signed short" || s == "signed short int" || s == "sword") return PIVariant::Short;
|
||||
if (s == "int" || s == "signed" || s == "signed int") return PIVariant::Int;
|
||||
if (s == "long" || s == "long int" || s == "signed long" || s == "signed long int" || s == "sdword") return PIVariant::Long;
|
||||
if (s == "llong" || s == "long long" || s == "long long int" || s == "signed long long" || s == "signed long long int" || s == "sqword") return PIVariant::LLong;
|
||||
if (s == "uchar" || s == "byte") return PIVariant::UChar;
|
||||
if (s == "ushort" || s == "unsigned short" || s == "unsigned short int" || s == "word") return PIVariant::UShort;
|
||||
if (s == "uint" || s == "unsigned" || s == "unsigned int") return PIVariant::UInt;
|
||||
if (s == "ulong" || s == "unsigned long" || s == "unsigned long int" || s == "dword") return PIVariant::ULong;
|
||||
if (s == "ullong" || s == "unsigned long long" || s == "unsigned long long int" || s == "qword") return PIVariant::ULLong;
|
||||
if (s == "float") return PIVariant::Float;
|
||||
if (s == "double" || s == "real") return PIVariant::Double;
|
||||
if (s == "ldouble" || s == "long double") return PIVariant::LDouble;
|
||||
if (s == "pistring" || s == "string") return PIVariant::String;
|
||||
if (s == "pistringlist" || s == "vector<string>" || s == "vector<pistring>" || s == "pivector<string>" || s == "pivector<pistring>") return PIVariant::StringList;
|
||||
return PIVariant::Double;
|
||||
}
|
||||
|
||||
|
||||
PIString PIVariant::toString(const PIVariant::Type & var) {
|
||||
switch (var) {
|
||||
case PIVariant::Bool: return "bool";
|
||||
case PIVariant::Char: return "char";
|
||||
case PIVariant::Short: return "short";
|
||||
case PIVariant::Int: return "int";
|
||||
case PIVariant::Long: return "long";
|
||||
case PIVariant::LLong: return "llong";
|
||||
case PIVariant::UChar: return "uchar";
|
||||
case PIVariant::UShort: return "ushort";
|
||||
case PIVariant::UInt: return "uint";
|
||||
case PIVariant::ULong: return "ulong";
|
||||
case PIVariant::ULLong: return "ullong";
|
||||
case PIVariant::Float: return "float";
|
||||
case PIVariant::Double: return "double";
|
||||
case PIVariant::LDouble: return "ldouble";
|
||||
case PIVariant::String: return "string";
|
||||
case PIVariant::StringList: return "stringlist";
|
||||
}
|
||||
return "double";
|
||||
}
|
||||
|
||||
|
||||
uint PIVariant::variableSize(const PIVariant::Type & var) {
|
||||
switch (var) {
|
||||
case PIVariant::Bool: return sizeof(bool);
|
||||
case PIVariant::Char: return sizeof(char);
|
||||
case PIVariant::Short: return sizeof(short);
|
||||
case PIVariant::Int: return sizeof(int);
|
||||
case PIVariant::Long: return sizeof(long);
|
||||
case PIVariant::LLong: return sizeof(llong);
|
||||
case PIVariant::UChar: return sizeof(uchar);
|
||||
case PIVariant::UShort: return sizeof(ushort);
|
||||
case PIVariant::UInt: return sizeof(uint);
|
||||
case PIVariant::ULong: return sizeof(ulong);
|
||||
case PIVariant::ULLong: return sizeof(ullong);
|
||||
case PIVariant::Float: return sizeof(float);
|
||||
case PIVariant::Double: return sizeof(double);
|
||||
case PIVariant::LDouble: return sizeof(ldouble);
|
||||
default: break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
double PIVariant::variableValue(const void * var_ptr, const PIVariant::Type & var) {
|
||||
switch (var) {
|
||||
case PIVariant::Bool: return (double)(*((bool * )var_ptr));
|
||||
case PIVariant::Char: return (double)(*((char * )var_ptr));
|
||||
case PIVariant::Short: return (double)(*((short * )var_ptr));
|
||||
case PIVariant::Int: return (double)(*((int * )var_ptr));
|
||||
case PIVariant::Long: return (double)(*((long * )var_ptr));
|
||||
case PIVariant::LLong: return (double)(*((llong * )var_ptr));
|
||||
case PIVariant::UChar: return (double)(*((uchar * )var_ptr));
|
||||
case PIVariant::UShort: return (double)(*((ushort * )var_ptr));
|
||||
case PIVariant::UInt: return (double)(*((uint * )var_ptr));
|
||||
case PIVariant::ULong: return (double)(*((ulong * )var_ptr));
|
||||
case PIVariant::ULLong: return (double)(*((ullong * )var_ptr));
|
||||
case PIVariant::Float: return (double)(*((float * )var_ptr));
|
||||
case PIVariant::Double: return (double)(*((double * )var_ptr));
|
||||
case PIVariant::LDouble: return (ldouble)(*((ldouble * )var_ptr));
|
||||
default: break;
|
||||
}
|
||||
return 0.;
|
||||
}
|
||||
|
||||
|
||||
void PIVariable::setVariable(const PIString & str) {
|
||||
type_ = PIVariant::fromString(str);
|
||||
size_ = PIVariant::variableSize(type_);
|
||||
}
|
||||
|
||||
|
||||
void PIVariable::writeVariable(void * dest) {
|
||||
switch (type_) {
|
||||
case PIVariant::Bool: *((bool * )((ullong)dest + offset)) = value_ > 0.; return;
|
||||
case PIVariant::Char: *((char * )((ullong)dest + offset)) = char(value_); return;
|
||||
case PIVariant::Short: *((short * )((ullong)dest + offset)) = short(value_); return;
|
||||
case PIVariant::Int: *((int * )((ullong)dest + offset)) = int(value_); return;
|
||||
case PIVariant::Long: *((long * )((ullong)dest + offset)) = long(value_); return;
|
||||
case PIVariant::LLong: *((llong * )((ullong)dest + offset)) = llong(value_); return;
|
||||
case PIVariant::UChar: *((uchar * )((ullong)dest + offset)) = uchar(value_); return;
|
||||
case PIVariant::UShort: *((ushort * )((ullong)dest + offset)) = ushort(value_); return;
|
||||
case PIVariant::UInt: *((uint * )((ullong)dest + offset)) = uint(value_); return;
|
||||
case PIVariant::ULong: *((ulong * )((ullong)dest + offset)) = ulong(value_); return;
|
||||
case PIVariant::ULLong: *((ullong * )((ullong)dest + offset)) = ullong(value_); return;
|
||||
case PIVariant::Float: *((float * )((ullong)dest + offset)) = float(value_); return;
|
||||
case PIVariant::Double: *((double * )((ullong)dest + offset)) = value_; return;
|
||||
case PIVariant::LDouble: *((ldouble * )((ullong)dest + offset)) = ldouble(value_); return;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIStruct::parseFile(const PIString & file) {
|
||||
PIConfig conf(file, PIIODevice::ReadOnly);
|
||||
PIVariable var;
|
||||
PIString ts;
|
||||
uint sz = 0;
|
||||
vars.clear();
|
||||
for (int i = 0; i < conf.entriesCount(); ++i) {
|
||||
var.setVariable(conf.getValue(i));
|
||||
var.setName(conf.getName(i));
|
||||
var.offset = sz;
|
||||
sz += var.size();
|
||||
ts = conf.getComment(i);
|
||||
if (ts.length() > 0)
|
||||
var.setValue(ts.toDouble());
|
||||
else var.setValue(0.);
|
||||
vars.push_back(var);
|
||||
}
|
||||
size_ = sz;
|
||||
}
|
||||
|
||||
|
||||
void PIStruct::readData(const void * data) {
|
||||
for (uint i = 0; i < vars.size(); ++i)
|
||||
vars[i].readVariable(data);
|
||||
}
|
||||
|
||||
|
||||
void PIStruct::writeData(void * data) {
|
||||
for (uint i = 0; i < vars.size(); ++i)
|
||||
vars[i].writeVariable(data);
|
||||
}
|
||||
|
||||
249
src/_unsused/pivariable.cpp_
Normal file
249
src/_unsused/pivariable.cpp_
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Variable, Struct (simple serialization)
|
||||
Copyright (C) 2013 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pivariable.h"
|
||||
|
||||
|
||||
bool PIVariant::operator ==(const PIVariant & v) const {
|
||||
if (type != v.type) return false;
|
||||
switch (type) {
|
||||
case PIVariant::Bool: return vBool == v.vBool;
|
||||
case PIVariant::Char: return vChar == v.vChar;
|
||||
case PIVariant::Short: return vShort == v.vShort;
|
||||
case PIVariant::Int: return vInt == v.vInt;
|
||||
case PIVariant::Long: return vLong == v.vLong;
|
||||
case PIVariant::LLong: return vLLong == v.vLLong;
|
||||
case PIVariant::UChar: return vUChar == v.vUChar;
|
||||
case PIVariant::UShort: return vUShort == v.vUShort;
|
||||
case PIVariant::UInt: return vUInt == v.vUInt;
|
||||
case PIVariant::ULong: return vULong == v.vULong;
|
||||
case PIVariant::ULLong: return vULLong == v.vULLong;
|
||||
case PIVariant::Float: return vFloat == v.vFloat;
|
||||
case PIVariant::Double: return vDouble == v.vDouble;
|
||||
case PIVariant::LDouble: return vLDouble == v.vLDouble;
|
||||
case PIVariant::String: return vString == v.vString;
|
||||
case PIVariant::StringList: return vStringList == v.vStringList;
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void PIVariant::setValueOnly(const PIString & s) {
|
||||
switch (type) {
|
||||
case PIVariant::Bool: vBool = s.toBool(); break;
|
||||
case PIVariant::Char: vChar = s.toChar(); break;
|
||||
case PIVariant::Short: vShort = s.toShort(); break;
|
||||
case PIVariant::Int: vInt = s.toInt(); break;
|
||||
case PIVariant::Long: vLong = s.toLong(); break;
|
||||
case PIVariant::LLong: vLLong = s.toLLong(); break;
|
||||
case PIVariant::UChar: vUChar = s.toChar(); break;
|
||||
case PIVariant::UShort: vUShort = s.toShort(); break;
|
||||
case PIVariant::UInt: vUInt = s.toInt(); break;
|
||||
case PIVariant::ULong: vULong = s.toLong(); break;
|
||||
case PIVariant::ULLong: vULLong = s.toLLong(); break;
|
||||
case PIVariant::Float: vFloat = s.toFloat(); break;
|
||||
case PIVariant::Double: vDouble = s.toDouble(); break;
|
||||
case PIVariant::LDouble: vLDouble = s.toLDouble(); break;
|
||||
case PIVariant::String: vString = s; break;
|
||||
case PIVariant::StringList: vStringList = s.split("%|%"); break;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
PIString PIVariant::stringValue() const {
|
||||
switch (type) {
|
||||
case PIVariant::Bool: return (vBool ? "true" : "false");
|
||||
case PIVariant::Char: return PIString::fromNumber(vChar);
|
||||
case PIVariant::Short: return PIString::fromNumber(vShort);
|
||||
case PIVariant::Int: return PIString::fromNumber(vInt);
|
||||
case PIVariant::Long: return PIString::fromNumber(vLong);
|
||||
case PIVariant::LLong: return PIString::fromNumber(static_cast<long>(vLLong));
|
||||
case PIVariant::UChar: return PIString::fromNumber(vUChar);
|
||||
case PIVariant::UShort: return PIString::fromNumber(vUShort);
|
||||
case PIVariant::UInt: return PIString::fromNumber(vUInt);
|
||||
case PIVariant::ULong: return PIString::fromNumber(static_cast<long>(vULong));
|
||||
case PIVariant::ULLong: return PIString::fromNumber(static_cast<long>(vULLong));
|
||||
case PIVariant::Float: return PIString::fromNumber(vFloat);
|
||||
case PIVariant::Double: return PIString::fromNumber(vDouble);
|
||||
case PIVariant::LDouble: return PIString::fromNumber(vLDouble);
|
||||
case PIVariant::String: return vString;
|
||||
case PIVariant::StringList: return vStringList.join("%|%");
|
||||
};
|
||||
return vString;
|
||||
}
|
||||
|
||||
|
||||
PIVariant PIVariant::readFromString(const PIString & s) {
|
||||
int i = s.find(':');
|
||||
if (i < 0 || s.length() < 2) return PIVariant(s);
|
||||
PIVariant ret;
|
||||
ret.type = PIVariant::fromString(s.left(i));
|
||||
ret.setValueOnly(s.right(s.length() - i - 1));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIVariant::Type PIVariant::fromString(const PIString & str) {
|
||||
PIString s = str.trimmed().toLowerCase().replace(" ", "");
|
||||
if (s == "bool") return PIVariant::Bool;
|
||||
if (s == "char" || s == "sbyte") return PIVariant::Char;
|
||||
if (s == "short" || s == "short int" || s == "signed short" || s == "signed short int" || s == "sword") return PIVariant::Short;
|
||||
if (s == "int" || s == "signed" || s == "signed int") return PIVariant::Int;
|
||||
if (s == "long" || s == "long int" || s == "signed long" || s == "signed long int" || s == "sdword") return PIVariant::Long;
|
||||
if (s == "llong" || s == "long long" || s == "long long int" || s == "signed long long" || s == "signed long long int" || s == "sqword") return PIVariant::LLong;
|
||||
if (s == "uchar" || s == "byte") return PIVariant::UChar;
|
||||
if (s == "ushort" || s == "unsigned short" || s == "unsigned short int" || s == "word") return PIVariant::UShort;
|
||||
if (s == "uint" || s == "unsigned" || s == "unsigned int") return PIVariant::UInt;
|
||||
if (s == "ulong" || s == "unsigned long" || s == "unsigned long int" || s == "dword") return PIVariant::ULong;
|
||||
if (s == "ullong" || s == "unsigned long long" || s == "unsigned long long int" || s == "qword") return PIVariant::ULLong;
|
||||
if (s == "float") return PIVariant::Float;
|
||||
if (s == "double" || s == "real") return PIVariant::Double;
|
||||
if (s == "ldouble" || s == "long double") return PIVariant::LDouble;
|
||||
if (s == "pistring" || s == "string") return PIVariant::String;
|
||||
if (s == "pistringlist" || s == "vector<string>" || s == "vector<pistring>" || s == "pivector<string>" || s == "pivector<pistring>") return PIVariant::StringList;
|
||||
return PIVariant::Double;
|
||||
}
|
||||
|
||||
|
||||
PIString PIVariant::toString(const PIVariant::Type & var) {
|
||||
switch (var) {
|
||||
case PIVariant::Bool: return "bool";
|
||||
case PIVariant::Char: return "char";
|
||||
case PIVariant::Short: return "short";
|
||||
case PIVariant::Int: return "int";
|
||||
case PIVariant::Long: return "long";
|
||||
case PIVariant::LLong: return "llong";
|
||||
case PIVariant::UChar: return "uchar";
|
||||
case PIVariant::UShort: return "ushort";
|
||||
case PIVariant::UInt: return "uint";
|
||||
case PIVariant::ULong: return "ulong";
|
||||
case PIVariant::ULLong: return "ullong";
|
||||
case PIVariant::Float: return "float";
|
||||
case PIVariant::Double: return "double";
|
||||
case PIVariant::LDouble: return "ldouble";
|
||||
case PIVariant::String: return "string";
|
||||
case PIVariant::StringList: return "stringlist";
|
||||
}
|
||||
return "double";
|
||||
}
|
||||
|
||||
|
||||
uint PIVariant::variableSize(const PIVariant::Type & var) {
|
||||
switch (var) {
|
||||
case PIVariant::Bool: return sizeof(bool);
|
||||
case PIVariant::Char: return sizeof(char);
|
||||
case PIVariant::Short: return sizeof(short);
|
||||
case PIVariant::Int: return sizeof(int);
|
||||
case PIVariant::Long: return sizeof(long);
|
||||
case PIVariant::LLong: return sizeof(llong);
|
||||
case PIVariant::UChar: return sizeof(uchar);
|
||||
case PIVariant::UShort: return sizeof(ushort);
|
||||
case PIVariant::UInt: return sizeof(uint);
|
||||
case PIVariant::ULong: return sizeof(ulong);
|
||||
case PIVariant::ULLong: return sizeof(ullong);
|
||||
case PIVariant::Float: return sizeof(float);
|
||||
case PIVariant::Double: return sizeof(double);
|
||||
case PIVariant::LDouble: return sizeof(ldouble);
|
||||
default: break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
double PIVariant::variableValue(const void * var_ptr, const PIVariant::Type & var) {
|
||||
switch (var) {
|
||||
case PIVariant::Bool: return (double)(*((bool * )var_ptr));
|
||||
case PIVariant::Char: return (double)(*((char * )var_ptr));
|
||||
case PIVariant::Short: return (double)(*((short * )var_ptr));
|
||||
case PIVariant::Int: return (double)(*((int * )var_ptr));
|
||||
case PIVariant::Long: return (double)(*((long * )var_ptr));
|
||||
case PIVariant::LLong: return (double)(*((llong * )var_ptr));
|
||||
case PIVariant::UChar: return (double)(*((uchar * )var_ptr));
|
||||
case PIVariant::UShort: return (double)(*((ushort * )var_ptr));
|
||||
case PIVariant::UInt: return (double)(*((uint * )var_ptr));
|
||||
case PIVariant::ULong: return (double)(*((ulong * )var_ptr));
|
||||
case PIVariant::ULLong: return (double)(*((ullong * )var_ptr));
|
||||
case PIVariant::Float: return (double)(*((float * )var_ptr));
|
||||
case PIVariant::Double: return (double)(*((double * )var_ptr));
|
||||
case PIVariant::LDouble: return (ldouble)(*((ldouble * )var_ptr));
|
||||
default: break;
|
||||
}
|
||||
return 0.;
|
||||
}
|
||||
|
||||
|
||||
void PIVariable::setVariable(const PIString & str) {
|
||||
type_ = PIVariant::fromString(str);
|
||||
size_ = PIVariant::variableSize(type_);
|
||||
}
|
||||
|
||||
|
||||
void PIVariable::writeVariable(void * dest) {
|
||||
switch (type_) {
|
||||
case PIVariant::Bool: *((bool * )((ullong)dest + offset)) = value_ > 0.; return;
|
||||
case PIVariant::Char: *((char * )((ullong)dest + offset)) = char(value_); return;
|
||||
case PIVariant::Short: *((short * )((ullong)dest + offset)) = short(value_); return;
|
||||
case PIVariant::Int: *((int * )((ullong)dest + offset)) = int(value_); return;
|
||||
case PIVariant::Long: *((long * )((ullong)dest + offset)) = long(value_); return;
|
||||
case PIVariant::LLong: *((llong * )((ullong)dest + offset)) = llong(value_); return;
|
||||
case PIVariant::UChar: *((uchar * )((ullong)dest + offset)) = uchar(value_); return;
|
||||
case PIVariant::UShort: *((ushort * )((ullong)dest + offset)) = ushort(value_); return;
|
||||
case PIVariant::UInt: *((uint * )((ullong)dest + offset)) = uint(value_); return;
|
||||
case PIVariant::ULong: *((ulong * )((ullong)dest + offset)) = ulong(value_); return;
|
||||
case PIVariant::ULLong: *((ullong * )((ullong)dest + offset)) = ullong(value_); return;
|
||||
case PIVariant::Float: *((float * )((ullong)dest + offset)) = float(value_); return;
|
||||
case PIVariant::Double: *((double * )((ullong)dest + offset)) = value_; return;
|
||||
case PIVariant::LDouble: *((ldouble * )((ullong)dest + offset)) = ldouble(value_); return;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIStruct::parseFile(const PIString & file) {
|
||||
PIConfig conf(file, PIIODevice::ReadOnly);
|
||||
PIVariable var;
|
||||
PIString ts;
|
||||
uint sz = 0;
|
||||
vars.clear();
|
||||
for (int i = 0; i < conf.entriesCount(); ++i) {
|
||||
var.setVariable(conf.getValue(i));
|
||||
var.setName(conf.getName(i));
|
||||
var.offset = sz;
|
||||
sz += var.size();
|
||||
ts = conf.getComment(i);
|
||||
if (ts.length() > 0)
|
||||
var.setValue(ts.toDouble());
|
||||
else var.setValue(0.);
|
||||
vars.push_back(var);
|
||||
}
|
||||
size_ = sz;
|
||||
}
|
||||
|
||||
|
||||
void PIStruct::readData(const void * data) {
|
||||
for (uint i = 0; i < vars.size(); ++i)
|
||||
vars[i].readVariable(data);
|
||||
}
|
||||
|
||||
|
||||
void PIStruct::writeData(void * data) {
|
||||
for (uint i = 0; i < vars.size(); ++i)
|
||||
vars[i].writeVariable(data);
|
||||
}
|
||||
|
||||
196
src/_unsused/pivariable.h
Executable file
196
src/_unsused/pivariable.h
Executable file
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Variable, Struct (simple serialization)
|
||||
Copyright (C) 2013 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIVARIABLE_H
|
||||
#define PIVARIABLE_H
|
||||
|
||||
#include "piconfig.h"
|
||||
|
||||
class PIP_EXPORT PIVariant {
|
||||
friend class PIVariable;
|
||||
public:
|
||||
enum Type {Bool, Char, Short, Int, Long, LLong, UChar, UShort, UInt, ULong, ULLong, Float, Double, LDouble, String, StringList};
|
||||
|
||||
PIVariant() {setValue(0.);}
|
||||
PIVariant(const char * v) {setValue(v);}
|
||||
PIVariant(const bool & v) {setValue(v);}
|
||||
PIVariant(const char & v) {setValue(v);}
|
||||
PIVariant(const short & v) {setValue(v);}
|
||||
PIVariant(const int & v) {setValue(v);}
|
||||
PIVariant(const long & v) {setValue(v);}
|
||||
PIVariant(const llong & v) {setValue(v);}
|
||||
PIVariant(const uchar & v) {setValue(v);}
|
||||
PIVariant(const ushort & v) {setValue(v);}
|
||||
PIVariant(const uint & v) {setValue(v);}
|
||||
PIVariant(const ulong & v) {setValue(v);}
|
||||
PIVariant(const ullong & v) {setValue(v);}
|
||||
PIVariant(const float & v) {setValue(v);}
|
||||
PIVariant(const double & v) {setValue(v);}
|
||||
PIVariant(const ldouble & v) {setValue(v);}
|
||||
PIVariant(const PIString & v) {setValue(v);}
|
||||
PIVariant(const PIStringList & v) {setValue(v);}
|
||||
|
||||
void setValue(const char * v) {setValue(PIString(v));}
|
||||
void setValue(const bool & v) {type = PIVariant::Bool; vBool = v;}
|
||||
void setValue(const char & v) {type = PIVariant::Char; vChar = v;}
|
||||
void setValue(const short & v) {type = PIVariant::Short; vShort = v;}
|
||||
void setValue(const int & v) {type = PIVariant::Int; vInt = v;}
|
||||
void setValue(const long & v) {type = PIVariant::Long; vLong = v;}
|
||||
void setValue(const llong & v) {type = PIVariant::LLong; vLLong = v;}
|
||||
void setValue(const uchar & v) {type = PIVariant::UChar; vUChar = v;}
|
||||
void setValue(const ushort & v) {type = PIVariant::UShort; vUShort = v;}
|
||||
void setValue(const uint & v) {type = PIVariant::UInt; vUInt = v;}
|
||||
void setValue(const ulong & v) {type = PIVariant::ULong; vULong = v;}
|
||||
void setValue(const ullong & v) {type = PIVariant::ULLong; vULLong = v;}
|
||||
void setValue(const float & v) {type = PIVariant::Float; vFloat = v;}
|
||||
void setValue(const double & v) {type = PIVariant::Double; vDouble = v;}
|
||||
void setValue(const ldouble & v) {type = PIVariant::LDouble; vLDouble = v;}
|
||||
void setValue(const PIString & v) {type = PIVariant::String; vString = v;}
|
||||
void setValue(const PIStringList & v) {type = PIVariant::StringList; vStringList = v;}
|
||||
void setValueOnly(const PIString & v);
|
||||
PIString typeName() const {return PIVariant::toString(type);}
|
||||
double doubleValue() const {return PIVariant::variableValue(&vChar, type);}
|
||||
PIString stringValue() const;
|
||||
void typeFromString(const PIString & str) {type = PIVariant::fromString(str);}
|
||||
PIString typeToString() const {return PIVariant::toString(type);}
|
||||
uint size() {if (type != PIVariant::String && type != PIVariant::StringList) return PIVariant::variableSize(type); if (type == PIVariant::String) return vString.size(); else return vStringList.contentSize();}
|
||||
PIString writeToString() const {return typeName() + ":" + stringValue();}
|
||||
|
||||
#ifdef QNX
|
||||
void operator =(const PIVariant & v) {type = v.type; vLDouble = v.vLDouble; vString = v.vString; vStringList = v.vStringList;}
|
||||
#endif
|
||||
void operator =(const char * v) {setValue(PIString(v));}
|
||||
void operator =(const bool & v) {type = PIVariant::Bool; vBool = v;}
|
||||
void operator =(const char & v) {type = PIVariant::Char; vChar = v;}
|
||||
void operator =(const short & v) {type = PIVariant::Short; vShort = v;}
|
||||
void operator =(const int & v) {type = PIVariant::Int; vInt = v;}
|
||||
void operator =(const long & v) {type = PIVariant::Long; vLong = v;}
|
||||
void operator =(const llong & v) {type = PIVariant::LLong; vLLong = v;}
|
||||
void operator =(const uchar & v) {type = PIVariant::UChar; vUChar = v;}
|
||||
void operator =(const ushort & v) {type = PIVariant::UShort; vUShort = v;}
|
||||
void operator =(const uint & v) {type = PIVariant::UInt; vUInt = v;}
|
||||
void operator =(const ulong & v) {type = PIVariant::ULong; vULong = v;}
|
||||
void operator =(const ullong & v) {type = PIVariant::ULLong; vULLong = v;}
|
||||
void operator =(const float & v) {type = PIVariant::Float; vFloat = v;}
|
||||
void operator =(const double & v) {type = PIVariant::Double; vDouble = v;}
|
||||
void operator =(const ldouble & v) {type = PIVariant::LDouble; vLDouble = v;}
|
||||
void operator =(const PIString & v) {type = PIVariant::String; vString = v;}
|
||||
void operator =(const PIStringList & v) {type = PIVariant::StringList; vStringList = v;}
|
||||
|
||||
bool operator ==(const PIVariant & v) const;
|
||||
bool operator !=(const PIVariant & v) const {return !(*this == v);}
|
||||
|
||||
PIVariant::Type type;
|
||||
union {
|
||||
bool vBool;
|
||||
char vChar;
|
||||
short vShort;
|
||||
int vInt;
|
||||
long vLong;
|
||||
llong vLLong;
|
||||
uchar vUChar;
|
||||
ushort vUShort;
|
||||
uint vUInt;
|
||||
ulong vULong;
|
||||
ullong vULLong;
|
||||
float vFloat;
|
||||
double vDouble;
|
||||
ldouble vLDouble;
|
||||
};
|
||||
PIString vString;
|
||||
PIStringList vStringList;
|
||||
|
||||
static PIVariant readFromString(const PIString & s);
|
||||
|
||||
private:
|
||||
static PIVariant::Type fromString(const PIString & str);
|
||||
static PIString toString(const PIVariant::Type & var);
|
||||
static uint variableSize(const PIVariant::Type & var);
|
||||
static double variableValue(const void * var_ptr, const PIVariant::Type & var);
|
||||
|
||||
};
|
||||
|
||||
inline std::ostream & operator <<(std::ostream & s, const PIVariant & v) {s << v.typeName() << ": " << v.stringValue(); return s;}
|
||||
|
||||
class PIP_EXPORT PIVariable {
|
||||
public:
|
||||
PIVariable() {;}
|
||||
PIVariable(const PIString & str) {setVariable(str);}
|
||||
~PIVariable() {;}
|
||||
|
||||
void setVariable(const PIString & str);
|
||||
void writeVariable(void * dest);
|
||||
void readVariable(const void * var_ptr) {value_ = PIVariant::variableValue((char * )((long)var_ptr + offset), type_);}
|
||||
PIVariant::Type type() const {return type_;}
|
||||
uint size() const {return size_;}
|
||||
const PIString & name() {return name_;}
|
||||
void setName(const PIString & str) {name_ = str;}
|
||||
double value() const {return value_;}
|
||||
void setValue(const double & val) {value_ = val;}
|
||||
|
||||
int offset;
|
||||
|
||||
private:
|
||||
PIVariant::Type type_;
|
||||
uint size_;
|
||||
PIString name_;
|
||||
double value_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* PIStruct is abstract structure, described by *.conf file with format of each line:
|
||||
* "<name> = <type> #<n|f|b> <start_value>".
|
||||
* e.g. "pi = double #f 3.1418"
|
||||
*
|
||||
* You can write or read binary content of this struct
|
||||
* by functions "writeData" and "readData", e.g.
|
||||
* "char * data = new char[struct.size()];
|
||||
* struct.writeData(data);"
|
||||
*
|
||||
* Access to each variable in struct is looks like
|
||||
* "double value = struct["pi"].value();"
|
||||
*/
|
||||
|
||||
class PIP_EXPORT PIStruct {
|
||||
public:
|
||||
PIStruct() {;}
|
||||
PIStruct(const PIString & str) {parseFile(str);}
|
||||
|
||||
void parseFile(const PIString & file);
|
||||
void readData(const void * data);
|
||||
void writeData(void * data);
|
||||
void clear() {vars.clear(); size_ = 0;}
|
||||
uint count() const {return vars.size();}
|
||||
uint size() const {return size_;}
|
||||
const PIString & name() {return name_;}
|
||||
void setName(const PIString & str) {name_ = str;}
|
||||
PIVariable & operator[](const uint & index) {return vars[index];}
|
||||
PIVariable & operator[](const PIString & name) {for (uint i = 0; i < vars.size(); ++i) if (vars[i].name() == name) return vars[i]; return def;}
|
||||
|
||||
private:
|
||||
uint size_;
|
||||
PIString name_;
|
||||
PIVariable def;
|
||||
PIVector<PIVariable> vars;
|
||||
|
||||
};
|
||||
|
||||
#endif // PIVARIABLE_H
|
||||
196
src/_unsused/pivariable.h_
Normal file
196
src/_unsused/pivariable.h_
Normal file
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Variable, Struct (simple serialization)
|
||||
Copyright (C) 2013 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIVARIABLE_H
|
||||
#define PIVARIABLE_H
|
||||
|
||||
#include "piconfig.h"
|
||||
|
||||
class PIP_EXPORT PIVariant {
|
||||
friend class PIVariable;
|
||||
public:
|
||||
enum Type {Bool, Char, Short, Int, Long, LLong, UChar, UShort, UInt, ULong, ULLong, Float, Double, LDouble, String, StringList};
|
||||
|
||||
PIVariant() {setValue(0.);}
|
||||
PIVariant(const char * v) {setValue(v);}
|
||||
PIVariant(const bool & v) {setValue(v);}
|
||||
PIVariant(const char & v) {setValue(v);}
|
||||
PIVariant(const short & v) {setValue(v);}
|
||||
PIVariant(const int & v) {setValue(v);}
|
||||
PIVariant(const long & v) {setValue(v);}
|
||||
PIVariant(const llong & v) {setValue(v);}
|
||||
PIVariant(const uchar & v) {setValue(v);}
|
||||
PIVariant(const ushort & v) {setValue(v);}
|
||||
PIVariant(const uint & v) {setValue(v);}
|
||||
PIVariant(const ulong & v) {setValue(v);}
|
||||
PIVariant(const ullong & v) {setValue(v);}
|
||||
PIVariant(const float & v) {setValue(v);}
|
||||
PIVariant(const double & v) {setValue(v);}
|
||||
PIVariant(const ldouble & v) {setValue(v);}
|
||||
PIVariant(const PIString & v) {setValue(v);}
|
||||
PIVariant(const PIStringList & v) {setValue(v);}
|
||||
|
||||
void setValue(const char * v) {setValue(PIString(v));}
|
||||
void setValue(const bool & v) {type = PIVariant::Bool; vBool = v;}
|
||||
void setValue(const char & v) {type = PIVariant::Char; vChar = v;}
|
||||
void setValue(const short & v) {type = PIVariant::Short; vShort = v;}
|
||||
void setValue(const int & v) {type = PIVariant::Int; vInt = v;}
|
||||
void setValue(const long & v) {type = PIVariant::Long; vLong = v;}
|
||||
void setValue(const llong & v) {type = PIVariant::LLong; vLLong = v;}
|
||||
void setValue(const uchar & v) {type = PIVariant::UChar; vUChar = v;}
|
||||
void setValue(const ushort & v) {type = PIVariant::UShort; vUShort = v;}
|
||||
void setValue(const uint & v) {type = PIVariant::UInt; vUInt = v;}
|
||||
void setValue(const ulong & v) {type = PIVariant::ULong; vULong = v;}
|
||||
void setValue(const ullong & v) {type = PIVariant::ULLong; vULLong = v;}
|
||||
void setValue(const float & v) {type = PIVariant::Float; vFloat = v;}
|
||||
void setValue(const double & v) {type = PIVariant::Double; vDouble = v;}
|
||||
void setValue(const ldouble & v) {type = PIVariant::LDouble; vLDouble = v;}
|
||||
void setValue(const PIString & v) {type = PIVariant::String; vString = v;}
|
||||
void setValue(const PIStringList & v) {type = PIVariant::StringList; vStringList = v;}
|
||||
void setValueOnly(const PIString & v);
|
||||
PIString typeName() const {return PIVariant::toString(type);}
|
||||
double doubleValue() const {return PIVariant::variableValue(&vChar, type);}
|
||||
PIString stringValue() const;
|
||||
void typeFromString(const PIString & str) {type = PIVariant::fromString(str);}
|
||||
PIString typeToString() const {return PIVariant::toString(type);}
|
||||
uint size() {if (type != PIVariant::String && type != PIVariant::StringList) return PIVariant::variableSize(type); if (type == PIVariant::String) return vString.size(); else return vStringList.contentSize();}
|
||||
PIString writeToString() const {return typeName() + ":" + stringValue();}
|
||||
|
||||
#ifdef QNX
|
||||
void operator =(const PIVariant & v) {type = v.type; vLDouble = v.vLDouble; vString = v.vString; vStringList = v.vStringList;}
|
||||
#endif
|
||||
void operator =(const char * v) {setValue(PIString(v));}
|
||||
void operator =(const bool & v) {type = PIVariant::Bool; vBool = v;}
|
||||
void operator =(const char & v) {type = PIVariant::Char; vChar = v;}
|
||||
void operator =(const short & v) {type = PIVariant::Short; vShort = v;}
|
||||
void operator =(const int & v) {type = PIVariant::Int; vInt = v;}
|
||||
void operator =(const long & v) {type = PIVariant::Long; vLong = v;}
|
||||
void operator =(const llong & v) {type = PIVariant::LLong; vLLong = v;}
|
||||
void operator =(const uchar & v) {type = PIVariant::UChar; vUChar = v;}
|
||||
void operator =(const ushort & v) {type = PIVariant::UShort; vUShort = v;}
|
||||
void operator =(const uint & v) {type = PIVariant::UInt; vUInt = v;}
|
||||
void operator =(const ulong & v) {type = PIVariant::ULong; vULong = v;}
|
||||
void operator =(const ullong & v) {type = PIVariant::ULLong; vULLong = v;}
|
||||
void operator =(const float & v) {type = PIVariant::Float; vFloat = v;}
|
||||
void operator =(const double & v) {type = PIVariant::Double; vDouble = v;}
|
||||
void operator =(const ldouble & v) {type = PIVariant::LDouble; vLDouble = v;}
|
||||
void operator =(const PIString & v) {type = PIVariant::String; vString = v;}
|
||||
void operator =(const PIStringList & v) {type = PIVariant::StringList; vStringList = v;}
|
||||
|
||||
bool operator ==(const PIVariant & v) const;
|
||||
bool operator !=(const PIVariant & v) const {return !(*this == v);}
|
||||
|
||||
PIVariant::Type type;
|
||||
union {
|
||||
bool vBool;
|
||||
char vChar;
|
||||
short vShort;
|
||||
int vInt;
|
||||
long vLong;
|
||||
llong vLLong;
|
||||
uchar vUChar;
|
||||
ushort vUShort;
|
||||
uint vUInt;
|
||||
ulong vULong;
|
||||
ullong vULLong;
|
||||
float vFloat;
|
||||
double vDouble;
|
||||
ldouble vLDouble;
|
||||
};
|
||||
PIString vString;
|
||||
PIStringList vStringList;
|
||||
|
||||
static PIVariant readFromString(const PIString & s);
|
||||
|
||||
private:
|
||||
static PIVariant::Type fromString(const PIString & str);
|
||||
static PIString toString(const PIVariant::Type & var);
|
||||
static uint variableSize(const PIVariant::Type & var);
|
||||
static double variableValue(const void * var_ptr, const PIVariant::Type & var);
|
||||
|
||||
};
|
||||
|
||||
inline std::ostream & operator <<(std::ostream & s, const PIVariant & v) {s << v.typeName() << ": " << v.stringValue(); return s;}
|
||||
|
||||
class PIP_EXPORT PIVariable {
|
||||
public:
|
||||
PIVariable() {;}
|
||||
PIVariable(const PIString & str) {setVariable(str);}
|
||||
~PIVariable() {;}
|
||||
|
||||
void setVariable(const PIString & str);
|
||||
void writeVariable(void * dest);
|
||||
void readVariable(const void * var_ptr) {value_ = PIVariant::variableValue((char * )((long)var_ptr + offset), type_);}
|
||||
PIVariant::Type type() const {return type_;}
|
||||
uint size() const {return size_;}
|
||||
const PIString & name() {return name_;}
|
||||
void setName(const PIString & str) {name_ = str;}
|
||||
double value() const {return value_;}
|
||||
void setValue(const double & val) {value_ = val;}
|
||||
|
||||
int offset;
|
||||
|
||||
private:
|
||||
PIVariant::Type type_;
|
||||
uint size_;
|
||||
PIString name_;
|
||||
double value_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* PIStruct is abstract structure, described by *.conf file with format of each line:
|
||||
* "<name> = <type> #<n|f|b> <start_value>".
|
||||
* e.g. "pi = double #f 3.1418"
|
||||
*
|
||||
* You can write or read binary content of this struct
|
||||
* by functions "writeData" and "readData", e.g.
|
||||
* "char * data = new char[struct.size()];
|
||||
* struct.writeData(data);"
|
||||
*
|
||||
* Access to each variable in struct is looks like
|
||||
* "double value = struct["pi"].value();"
|
||||
*/
|
||||
|
||||
class PIP_EXPORT PIStruct {
|
||||
public:
|
||||
PIStruct() {;}
|
||||
PIStruct(const PIString & str) {parseFile(str);}
|
||||
|
||||
void parseFile(const PIString & file);
|
||||
void readData(const void * data);
|
||||
void writeData(void * data);
|
||||
void clear() {vars.clear(); size_ = 0;}
|
||||
uint count() const {return vars.size();}
|
||||
uint size() const {return size_;}
|
||||
const PIString & name() {return name_;}
|
||||
void setName(const PIString & str) {name_ = str;}
|
||||
PIVariable & operator[](const uint & index) {return vars[index];}
|
||||
PIVariable & operator[](const PIString & name) {for (uint i = 0; i < vars.size(); ++i) if (vars[i].name() == name) return vars[i]; return def;}
|
||||
|
||||
private:
|
||||
uint size_;
|
||||
PIString name_;
|
||||
PIVariable def;
|
||||
PIVector<PIVariable> vars;
|
||||
|
||||
};
|
||||
|
||||
#endif // PIVARIABLE_H
|
||||
42
src/code/picodeinfo.cpp
Executable file
42
src/code/picodeinfo.cpp
Executable file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
C++ code info structs
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "picodeinfo.h"
|
||||
|
||||
|
||||
PIString PICodeInfo::EnumInfo::memberName(int value_) const {
|
||||
piForeachC (PICodeInfo::EnumeratorInfo & e, members)
|
||||
if (e.value == value_)
|
||||
return e.name;
|
||||
return PIString();
|
||||
}
|
||||
|
||||
|
||||
int PICodeInfo::EnumInfo::memberValue(const PIString & name_) const {
|
||||
piForeachC (PICodeInfo::EnumeratorInfo & e, members)
|
||||
if (e.name == name_)
|
||||
return e.value;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
PIMap<PIString, PICodeInfo::ClassInfo * > * PICodeInfo::classesInfo;
|
||||
PIMap<PIString, PICodeInfo::EnumInfo * > * PICodeInfo::enumsInfo;
|
||||
|
||||
bool __PICodeInfoInitializer__::_inited_ = false;
|
||||
158
src/code/picodeinfo.h
Executable file
158
src/code/picodeinfo.h
Executable file
@@ -0,0 +1,158 @@
|
||||
/*! \file picodeinfo.h
|
||||
* \brief C++ code info structs
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
C++ code info structs
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PICODEINFO_H
|
||||
#define PICODEINFO_H
|
||||
|
||||
#include "pistring.h"
|
||||
|
||||
namespace PICodeInfo {
|
||||
|
||||
enum TypeFlag {
|
||||
NoFlag,
|
||||
Const = 0x01,
|
||||
Static = 0x02,
|
||||
Mutable = 0x04,
|
||||
Volatile = 0x08,
|
||||
Inline = 0x10,
|
||||
Virtual = 0x20,
|
||||
Extern = 0x40
|
||||
};
|
||||
|
||||
typedef PIFlags<PICodeInfo::TypeFlag> TypeFlags;
|
||||
|
||||
struct TypeInfo {
|
||||
TypeInfo(const PIString & n = PIString(), const PIString & t = PIString(), PICodeInfo::TypeFlags f = 0) {name = n; type = t; flags = f;}
|
||||
PIString name;
|
||||
PIString type;
|
||||
PICodeInfo::TypeFlags flags;
|
||||
};
|
||||
|
||||
struct FunctionInfo {
|
||||
PIString name;
|
||||
TypeInfo return_type;
|
||||
PIVector<PICodeInfo::TypeInfo> arguments;
|
||||
};
|
||||
|
||||
struct ClassInfo {
|
||||
PIString name;
|
||||
PIStringList parents;
|
||||
PIVector<PICodeInfo::TypeInfo> variables;
|
||||
PIVector<PICodeInfo::FunctionInfo> functions;
|
||||
};
|
||||
|
||||
struct EnumeratorInfo {
|
||||
EnumeratorInfo(const PIString & n = PIString(), int v = 0) {name = n; value = v;}
|
||||
PIString name;
|
||||
int value;
|
||||
};
|
||||
|
||||
struct EnumInfo {
|
||||
PIString memberName(int value) const;
|
||||
int memberValue(const PIString & name) const;
|
||||
PIString name;
|
||||
PIVector<PICodeInfo::EnumeratorInfo> members;
|
||||
};
|
||||
|
||||
|
||||
inline PICout operator <<(PICout s, const PICodeInfo::TypeInfo & v) {
|
||||
if (v.flags[Inline]) s << "inline ";
|
||||
if (v.flags[Virtual]) s << "virtual ";
|
||||
if (v.flags[Mutable]) s << "mutable ";
|
||||
if (v.flags[Volatile]) s << "volatile ";
|
||||
if (v.flags[Static]) s << "static ";
|
||||
if (v.flags[Const]) s << "const ";
|
||||
s << v.type;
|
||||
if (!v.name.isEmpty())
|
||||
s << " " << v.name;
|
||||
return s;
|
||||
}
|
||||
|
||||
inline PICout operator <<(PICout s, const PICodeInfo::EnumeratorInfo & v) {s << v.name << " = " << v.value; return s;}
|
||||
|
||||
inline PICout operator <<(PICout s, const PICodeInfo::ClassInfo & v) {
|
||||
s.setControl(0, true);
|
||||
s << "class " << v.name;
|
||||
if (!v.parents.isEmpty()) {
|
||||
s << ": ";
|
||||
bool first = true;
|
||||
piForeachC (PIString & i, v.parents) {
|
||||
if (first) first = false;
|
||||
else s << ", ";
|
||||
s << i;
|
||||
}
|
||||
}
|
||||
s << " {\n";
|
||||
piForeachC (FunctionInfo & i, v.functions) {
|
||||
s << Tab << i.return_type << " " << i.name << "(";
|
||||
bool fa = true;
|
||||
piForeachC (TypeInfo & a, i.arguments) {
|
||||
if (fa) fa = false;
|
||||
else s << ", ";
|
||||
s << a;
|
||||
}
|
||||
s << ");\n";
|
||||
}
|
||||
if (!v.functions.isEmpty() && !v.variables.isEmpty())
|
||||
s << "\n";
|
||||
piForeachC (TypeInfo & i, v.variables) {
|
||||
s << Tab << i << ";\n";
|
||||
}
|
||||
s << "}\n";
|
||||
s.restoreControl();
|
||||
return s;
|
||||
}
|
||||
|
||||
inline PICout operator <<(PICout s, const PICodeInfo::EnumInfo & v) {
|
||||
s.setControl(0, true);
|
||||
s << "enum " << v.name << " {\n";
|
||||
piForeachC (EnumeratorInfo & i, v.members) {
|
||||
bool f = true;
|
||||
if (f) f = false;
|
||||
else s << ", ";
|
||||
s << Tab << i << "\n";
|
||||
}
|
||||
s << "}\n";
|
||||
s.restoreControl();
|
||||
return s;
|
||||
}
|
||||
|
||||
extern PIMap<PIString, PICodeInfo::ClassInfo * > * classesInfo;
|
||||
extern PIMap<PIString, PICodeInfo::EnumInfo * > * enumsInfo;
|
||||
|
||||
}
|
||||
|
||||
class __PICodeInfoInitializer__ {
|
||||
public:
|
||||
__PICodeInfoInitializer__() {
|
||||
if (_inited_) return;
|
||||
_inited_ = true;
|
||||
PICodeInfo::classesInfo = new PIMap<PIString, PICodeInfo::ClassInfo * >;
|
||||
PICodeInfo::enumsInfo = new PIMap<PIString, PICodeInfo::EnumInfo * >;
|
||||
}
|
||||
static bool _inited_;
|
||||
};
|
||||
|
||||
static __PICodeInfoInitializer__ __picodeinfoinitializer__;
|
||||
|
||||
#endif // PICODEINFO_H
|
||||
846
src/code/picodeparser.cpp
Executable file
846
src/code/picodeparser.cpp
Executable file
@@ -0,0 +1,846 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
C++ code parser
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "picodeparser.h"
|
||||
|
||||
|
||||
|
||||
PIString PICodeParser::Macro::expand(PIString args_, bool * ok) const {
|
||||
PIStringList arg_vals;
|
||||
while (!args_.isEmpty()) {
|
||||
int ci = args_.find(","), bi = args_.find("(");
|
||||
if (ci < 0) {
|
||||
arg_vals << args_;
|
||||
break;
|
||||
}
|
||||
PIString ca;
|
||||
if (bi >= 0 && bi < ci) {
|
||||
ca = args_.left(args_.takeLeft(bi));
|
||||
ci -= ca.size_s(); bi -= ca.size_s();
|
||||
ca += "(" + args_.takeRange("(", ")") + ")";
|
||||
} else {
|
||||
ca = args_.takeLeft(ci);
|
||||
}
|
||||
arg_vals << ca;
|
||||
args_.trim(); args_.takeLeft(1); args_.trim();
|
||||
}
|
||||
if (args.size() != arg_vals.size()) {
|
||||
piCout << ("Error: in expansion of macro \"" + name + "(" + args.join(", ") + ")\": expect")
|
||||
<< args.size() << "arguments but takes" << arg_vals.size() << "!";
|
||||
if (ok != 0) *ok = false;
|
||||
return PIString();
|
||||
}
|
||||
PIString ret = value;
|
||||
for (int i = 0; i < args.size_s(); ++i) {
|
||||
const PIString & an(args[i]), av(arg_vals[i]);
|
||||
int ind(-1);
|
||||
while ((ind = ret.find(an, ind + 1)) >= 0) {
|
||||
PIChar ppc(0), pc(0), nc(0);
|
||||
if (ind > 1) ppc = ret[ind - 2];
|
||||
if (ind > 0) pc = ret[ind - 1];
|
||||
if (ind + an.size_s() < ret.size_s()) nc = ret[ind + an.size_s()];
|
||||
if (ppc != '#' && pc == '#' && !_isCChar(nc)) { // to chars
|
||||
ind--;
|
||||
ret.replace(ind, an.size_s() + 1, "\"" + av + "\"");
|
||||
ind -= an.size_s() - av.size_s() - 1;
|
||||
continue;
|
||||
}
|
||||
if (_isCChar(pc) || _isCChar(nc)) continue;
|
||||
ret.replace(ind, an.size_s(), av);
|
||||
ind -= an.size_s() - av.size_s();
|
||||
}
|
||||
}
|
||||
ret.replaceAll("##", "");
|
||||
if (ok != 0) *ok = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
PICodeParser::PICodeParser() {
|
||||
macros_iter = 32;
|
||||
with_includes = true;
|
||||
clear();
|
||||
includes << "";
|
||||
}
|
||||
|
||||
|
||||
void PICodeParser::parseFile(const PIString & file, bool follow_includes) {
|
||||
clear();
|
||||
parseFileInternal(file, follow_includes);
|
||||
/*piCout << "\n\nDefines:";
|
||||
piForeachC (Define & m, defines)
|
||||
piCout << "define" << m.first << m.second;
|
||||
piCout << "\n\nMacros:";
|
||||
piForeachC (Macro & m, macros)
|
||||
piCout << "Macro:" << m.name << m.args << m.value;
|
||||
piCout << "\n\nClasses:";
|
||||
piForeachC (Entity * c, entities)
|
||||
piCout << "class" << c->name << c->parents;
|
||||
piCout << "\n\nEnums:";
|
||||
piForeachC (Enum & c, enums)
|
||||
piCout << "enum" << c.name << c.members;
|
||||
piCout << "\n\nTypedefs:";
|
||||
piForeachC (Typedef & c, typedefs)
|
||||
piCout << "typedef" << c;*/
|
||||
}
|
||||
|
||||
|
||||
void PICodeParser::parseFiles(const PIStringList & files, bool follow_includes) {
|
||||
clear();
|
||||
piForeachC (PIString & f, files)
|
||||
parseFileInternal(f, follow_includes);
|
||||
/*piCout << "\n\nDefines:";
|
||||
piForeachC (Define & m, defines)
|
||||
piCout << "define" << m.first << m.second;
|
||||
piCout << "\n\nMacros:";
|
||||
piForeachC (Macro & m, macros)
|
||||
piCout << "Macro:" << m.name << m.args << m.value;
|
||||
piCout << "\n\nClasses:";
|
||||
piForeachC (Entity * c, entities)
|
||||
piCout << "class" << c->name << c->parents;
|
||||
piCout << "\n\nEnums:";
|
||||
piForeachC (Enum & c, enums)
|
||||
piCout << "enum" << c.name << c.members;
|
||||
piCout << "\n\nTypedefs:";
|
||||
piForeachC (Typedef & c, typedefs)
|
||||
piCout << "typedef" << c;*/
|
||||
}
|
||||
|
||||
|
||||
bool PICodeParser::isEnum(const PIString & name) {
|
||||
piForeachC (Enum & e, enums)
|
||||
if (e.name == name)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool PICodeParser::parseFileInternal(const PIString & file, bool follow_includes) {
|
||||
if (proc_files[file]) return true;
|
||||
with_includes = follow_includes;
|
||||
cur_file = file;
|
||||
PIFile f(file, PIIODevice::ReadOnly);
|
||||
int ii = 0;
|
||||
while (!f.isOpened() && ii < (includes.size_s() - 1)) {
|
||||
f.setPath(includes[++ii] + "/" + file);
|
||||
//piCout << "try" << f.path();
|
||||
f.open(PIIODevice::ReadOnly);
|
||||
}
|
||||
if (!f.isOpened()) {
|
||||
//piCout << ("Error: can`t open file \"" + file + "\"!");
|
||||
return false;
|
||||
}
|
||||
//piCout << "add" << file;
|
||||
proc_files << f.path();
|
||||
PIString fc = f.readAll();
|
||||
piCout << "parsing" << f.path() << "...";
|
||||
bool is_main = isMainFile(fc);
|
||||
if (is_main) main_file = f.path();
|
||||
bool ret = parseFileContent(fc, is_main);
|
||||
piCout << "parsing" << f.path() << "done";
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void PICodeParser::clear() {
|
||||
piForeach (Entity * i, entities) delete i;
|
||||
defines.clear();
|
||||
macros.clear();
|
||||
enums.clear();
|
||||
typedefs.clear();
|
||||
entities.clear();
|
||||
proc_files.clear();
|
||||
cur_namespace.clear();
|
||||
main_file.clear();
|
||||
evaluator.clearCustomVariables();
|
||||
defines << Define("PICODE", "") << custom_defines;
|
||||
}
|
||||
|
||||
|
||||
bool PICodeParser::parseFileContent(PIString & fc, bool main) {
|
||||
bool mlc = false, cc = false;
|
||||
int mls = 0, ole = -1, /*ccs = 0,*/ end = 0;
|
||||
char c = 0, pc = 0;
|
||||
PIString pfc, line, ccmn, tmp;
|
||||
PIMap<PIString, PIString> cchars;
|
||||
|
||||
/// Remove comments, join multiline "*" and replace "*" to $n (cchars)
|
||||
fc.replaceAll("\r\n", "\n");
|
||||
fc.replaceAll("\r", "\n");
|
||||
for (int i = 0; i < fc.size_s() - 1; ++i) {
|
||||
if (i > 0) pc = c;
|
||||
c = fc[i].toAscii();
|
||||
if (c == '"' && !mlc && pc != '\'') {
|
||||
if (i > 0) if (fc[i - 1] == '\\') continue;
|
||||
cc = !cc;
|
||||
/*if (cc) ccs = i;
|
||||
if (!cc) {
|
||||
ccmn = "$" + PIString::fromNumber(cchars.size());
|
||||
cchars[ccmn] = fc.mid(ccs, i - ccs + 1);
|
||||
fc.replace(ccs, i - ccs + 1, ccmn);
|
||||
i = ccs - 1 + ccmn.size_s();
|
||||
}*/
|
||||
continue;
|
||||
}
|
||||
if (i > 0)
|
||||
if (c == '\\' && fc[i - 1].toAscii() != '\\') {
|
||||
fc.cutMid(i, 2);
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
if (cc) continue;
|
||||
if (fc.mid(i, 2) == "/*") {mlc = true; mls = i; ++i; continue;}
|
||||
if (fc.mid(i, 2) == "*/" && mlc) {mlc = false; fc.cutMid(mls, i - mls + 2); i = mls - 1; continue;}
|
||||
if (fc.mid(i, 2) == "//" && !mlc) {ole = fc.find('\n', i); fc.cutMid(i, ole < 0 ? -1 : ole - i); --i; continue;}
|
||||
}
|
||||
//piCout << fc;
|
||||
pfc = procMacros(fc);
|
||||
|
||||
if (main) return true;
|
||||
|
||||
bool replaced = true;
|
||||
int replaced_cnt = 0;
|
||||
while (replaced) {
|
||||
//piCout << "MACRO iter" << replaced_cnt;
|
||||
if (replaced_cnt >= macros_iter) {
|
||||
piCout << "Error: recursive macros detected!";
|
||||
break;//return false;
|
||||
}
|
||||
replaced_cnt++;
|
||||
replaced = false;
|
||||
piForeachC (Define & d, defines) {
|
||||
int ind(-1);
|
||||
while ((ind = pfc.find(d.first, ind + 1)) >= 0) {
|
||||
PIChar pc(0), nc(0);
|
||||
if (ind > 0) pc = pfc[ind - 1];
|
||||
if (ind + d.first.size_s() < pfc.size_s()) nc = pfc[ind + d.first.size_s()];
|
||||
if (_isCChar(pc) || _isCChar(nc) || nc.isDigit()) continue;
|
||||
pfc.replace(ind, d.first.size_s(), d.second);
|
||||
ind -= d.first.size_s() - d.second.size_s();
|
||||
replaced = true;
|
||||
}
|
||||
}
|
||||
piForeachC (Macro & m, macros) {
|
||||
int ind(-1);
|
||||
while ((ind = pfc.find(m.name, ind + 1)) >= 0) {
|
||||
PIChar pc(0), nc(0);
|
||||
if (ind > 0) pc = pfc[ind - 1];
|
||||
if (ind + m.name.size_s() < pfc.size_s()) nc = pfc[ind + m.name.size_s()];
|
||||
if (_isCChar(pc) || _isCChar(nc) || nc.isDigit()) continue;
|
||||
PIString ret, range; bool ok(false);
|
||||
range = pfc.mid(ind + m.name.size_s()).takeRange("(", ")");
|
||||
ret = m.expand(range, &ok);
|
||||
if (!ok) return false;
|
||||
int rlen = pfc.find(range, ind + m.name.size_s()) + range.size_s() + 1 - ind;
|
||||
pfc.replace(ind, rlen, ret);
|
||||
ind -= rlen - ret.size_s();
|
||||
replaced = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//piCout << NewLine << "file" << cur_file << pfc;
|
||||
int pl = -1;
|
||||
while (!pfc.isEmpty()) {
|
||||
pfc.trim();
|
||||
int nl = pfc.size_s();
|
||||
if (pl == nl) break;
|
||||
pl = nl;
|
||||
if (pfc.left(9) == "namespace") {
|
||||
pfc.cutLeft(pfc.find("{") + 1);
|
||||
continue;
|
||||
}
|
||||
if (pfc.left(8) == "template") {
|
||||
pfc.cutLeft(8);
|
||||
pfc.takeRange("<", ">");
|
||||
bool def = !isDeclaration(pfc, 0, &end);
|
||||
pfc.cutLeft(end);
|
||||
if (def) pfc.takeRange("{", "}");
|
||||
else pfc.takeSymbol();
|
||||
continue;
|
||||
}
|
||||
if (pfc.left(5) == "class" || pfc.left(6) == "struct") {
|
||||
int dind = pfc.find("{", 0), find = pfc.find(";", 0);
|
||||
if (dind < 0 && find < 0) {pfc.cutLeft(6); continue;}
|
||||
if (dind < 0 || find < dind) {pfc.cutLeft(6); continue;}
|
||||
ccmn = pfc.left(dind) + "{\n" + pfc.mid(dind).takeRange('{', '}') + "\n}\n";
|
||||
pfc.remove(0, ccmn.size());
|
||||
parseClass(ccmn);
|
||||
continue;
|
||||
}
|
||||
if (pfc.left(4) == "enum") {
|
||||
pfc.cutLeft(4);
|
||||
tmp = pfc.takeCWord();
|
||||
parseEnum(cur_namespace + tmp, pfc.takeRange("{", "}"));
|
||||
pfc.takeSymbol();
|
||||
continue;
|
||||
}
|
||||
if (pfc.left(7) == "typedef") {
|
||||
pfc.cutLeft(7);
|
||||
typedefs << parseTypedef(pfc.takeLeft(pfc.find(";")));
|
||||
if (typedefs.back().first.isEmpty()) typedefs.pop_back();
|
||||
else root_.typedefs << typedefs.back();
|
||||
pfc.takeSymbol();
|
||||
continue;
|
||||
}
|
||||
int sci = pfc.find(";", 0), obi = pfc.find("{", 0);
|
||||
if (sci < 0 && obi < 0) {
|
||||
pfc.takeLeft(1);
|
||||
continue;
|
||||
}
|
||||
PIString str;
|
||||
if (sci < obi) {
|
||||
str = pfc.takeLeft(sci + 1);
|
||||
} else {
|
||||
str = pfc.takeLeft(obi);
|
||||
pfc.cutLeft(pfc.takeRange("{", "}"));
|
||||
}
|
||||
parseMember(&root_, str);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
PICodeParser::Entity * PICodeParser::parseClassDeclaration(const PIString & fc) {
|
||||
PIString cd = fc.trimmed().removeAll('\n').replaceAll("\t", " ").replaceAll(" ", " "), pn;
|
||||
//piCout << "found class <****\n" << cd << "\n****>";
|
||||
int ind = cd.find(":");
|
||||
PIVector<Entity * > parents;
|
||||
if (ind > 0) {
|
||||
PIStringList pl = cd.takeMid(ind + 1).trim().split(",");
|
||||
cd.cutRight(1);
|
||||
Entity * pe = 0;
|
||||
piForeachC (PIString & p, pl) {
|
||||
if (p.contains(" ")) pn = p.mid(p.find(" ") + 1);
|
||||
else pn = p;
|
||||
pe = findEntityByName(pn);
|
||||
if (pe == 0) ;//{piCout << "Error: can`t find" << pn;}
|
||||
else parents << pe;
|
||||
}
|
||||
}
|
||||
bool is_class = cd.left(5) == "class";
|
||||
cur_def_vis = (is_class ? Private : Public);
|
||||
PIString cn = cd.mid(6).trim();
|
||||
if (cn.isEmpty()) return 0;
|
||||
Entity * e = new Entity();
|
||||
e->name = cur_namespace + cn;
|
||||
e->type = (is_class ? "class" : "struct");
|
||||
e->parents = parents;
|
||||
e->file = cur_file;
|
||||
entities << e;
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
PIString PICodeParser::parseClass(PIString & fc) {
|
||||
Visibility prev_vis = cur_def_vis;
|
||||
int dind = fc.find("{"), find = fc.find(";"), end = 0;
|
||||
if (dind < 0 && find < 0) return PIString();
|
||||
if (dind < 0 || find < dind) return fc.left(find);
|
||||
Entity * ce = parseClassDeclaration(fc.takeLeft(dind));
|
||||
fc.trim().cutLeft(1).cutRight(1).trim();
|
||||
//piCout << "found class <****\n" << fc << "\n****>";
|
||||
if (!ce) return PIString();
|
||||
int ps = -1;
|
||||
bool def = false;
|
||||
PIString prev_namespace = cur_namespace, stmp;
|
||||
cur_namespace = ce->name + "::";
|
||||
//piCout << "parse class" << ce->name << "namespace" << cur_namespace;
|
||||
//piCout << "\nparse class" << ce->name << "namespace" << cur_namespace;
|
||||
while (!fc.isEmpty()) {
|
||||
PIString cw = fc.takeCWord(), tmp;
|
||||
//piCout << "\ntaked word" << cw;
|
||||
if (cw == "public") {cur_def_vis = Public; fc.cutLeft(1); continue;}
|
||||
if (cw == "protected") {cur_def_vis = Protected; fc.cutLeft(1); continue;}
|
||||
if (cw == "private") {cur_def_vis = Private; fc.cutLeft(1); continue;}
|
||||
if (cw == "class") {if (isDeclaration(fc, 0, &end)) {fc.cutLeft(end); fc.takeSymbol(); continue;} tmp = fc.takeLeft(fc.find("{")); stmp = fc.takeRange("{", "}"); fc.takeSymbol(); stmp = "class " + tmp + "{" + stmp + "}"; parseClass(stmp); continue;}
|
||||
if (cw == "struct") {if (isDeclaration(fc, 0, &end)) {fc.cutLeft(end); fc.takeSymbol(); continue;} tmp = fc.takeLeft(fc.find("{")); stmp = fc.takeRange("{", "}"); fc.takeSymbol(); stmp = "struct " + tmp + "{" + stmp + "}"; parseClass(stmp); continue;}
|
||||
if (cw == "enum") {tmp = fc.takeCWord(); parseEnum(cur_namespace + tmp, fc.takeRange("{", "}")); fc.takeSymbol(); continue;}
|
||||
if (cw == "friend") {fc.cutLeft(fc.find(";") + 1); continue;}
|
||||
if (cw == "typedef") {ce->typedefs << parseTypedef(fc.takeLeft(fc.find(";"))); typedefs << ce->typedefs.back(); typedefs.back().first.insert(0, cur_namespace); if (ce->typedefs.back().first.isEmpty()) ce->typedefs.pop_back(); fc.takeSymbol(); continue;}
|
||||
if (cw == "template") {
|
||||
fc.takeRange("<", ">");
|
||||
def = !isDeclaration(fc, 0, &end);
|
||||
fc.cutLeft(end);
|
||||
if (def) fc.takeRange("{", "}");
|
||||
else fc.takeSymbol();
|
||||
continue;
|
||||
}
|
||||
def = !isDeclaration(fc, 0, &end);
|
||||
tmp = (cw + fc.takeLeft(end)).trim();
|
||||
if (!tmp.isEmpty())
|
||||
parseMember(ce, tmp);
|
||||
if (def) fc.takeRange("{", "}");
|
||||
else fc.takeSymbol();
|
||||
if (ps == fc.size_s()) {/*cur_namespace = prev_namespace;*/ fc.cutLeft(1);/*return false*/;}
|
||||
ps = fc.size_s();
|
||||
}
|
||||
cur_def_vis = prev_vis;
|
||||
cur_namespace = prev_namespace;
|
||||
return ce->name;
|
||||
}
|
||||
|
||||
|
||||
bool PICodeParser::parseEnum(const PIString & name, PIString fc) {
|
||||
//piCout << "enum" << name << fc;
|
||||
Enum e(name);
|
||||
PIStringList vl(fc.split(","));
|
||||
PIString vn;
|
||||
int cv = -1, ind = 0;
|
||||
piForeachC (PIString & v, vl) {
|
||||
vn = v; ind = v.find("=");
|
||||
if (ind > 0) {cv = v.right(v.size_s() - ind - 1).toInt(); vn = v.left(ind);}
|
||||
if (ind < 0) ++cv;
|
||||
e.members << Enumerator(vn.trim(), cv);
|
||||
}
|
||||
enums << e;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
PICodeParser::Typedef PICodeParser::parseTypedef(PIString fc) {
|
||||
//piCout << "parse typedef" << fc;
|
||||
Typedef td;
|
||||
fc.replaceAll("\t", " ");
|
||||
|
||||
if (fc.contains("(")) {
|
||||
int start = fc.find("("), end = fc.find(")");
|
||||
td.first = fc.takeMid(start + 1, end - start - 1).trim();
|
||||
if (td.first.left(1) == "*") {td.first.cutLeft(1).trim(); fc.insert(start + 1, "*");}
|
||||
td.second = fc.trim();
|
||||
} else {
|
||||
td.first = fc.takeMid(fc.findLast(" ")).trim();
|
||||
td.second = fc.trim();
|
||||
}
|
||||
//piCout << "found typedef" << td;
|
||||
return td;
|
||||
}
|
||||
|
||||
|
||||
bool PICodeParser::parseMember(Entity * parent, PIString & fc) {
|
||||
if (fc.trim().isEmpty()) return true;
|
||||
if (fc.find("operator") >= 0) return true;
|
||||
tmp_temp.clear();
|
||||
//piCout << "parse member" << fc;
|
||||
int ts = fc.find("<"), te = 0;
|
||||
PIString ctemp, crepl;
|
||||
while (ts >= 0) {
|
||||
ctemp = fc.mid(ts).takeRange("<", ">");
|
||||
if (ctemp.isEmpty()) {te = ts + 1; ts = fc.find("<", te); continue;}
|
||||
crepl = "$" + PIString::fromNumber(tmp_temp.size_s()).expandLeftTo(3, "0");
|
||||
fc.replace(ts, ctemp.size_s() + 2, crepl);
|
||||
tmp_temp[crepl] = "<" + ctemp + ">";
|
||||
ts = fc.find("<", te);
|
||||
}
|
||||
fc.replaceAll("\n", " ").replaceAll("\t", " ").replaceAll(" ", " ").replaceAll(", ", ",");
|
||||
PIStringList tl, al;
|
||||
Member me;
|
||||
//piCout << fc;
|
||||
if (fc.contains("(")) {
|
||||
fc.cutRight(fc.size_s() - fc.findLast(")") - 1);
|
||||
te = fc.find("(");
|
||||
//piCout << fc;
|
||||
for (ts = te - 1; ts >= 0; --ts)
|
||||
if (!_isCChar(fc[ts]) && !(fc[ts].isDigit())) break;
|
||||
//piCout << "takeMid" << ts + 1 << te - ts - 1;
|
||||
me.name = fc.takeMid(ts + 1, te - ts - 1);
|
||||
if (me.name == parent->name) return true;
|
||||
me.arguments_full = fc.takeMid(ts + 2).cutRight(1).split(",");
|
||||
me.type = fc.cutRight(1).trim();
|
||||
me.visibility = cur_def_vis;
|
||||
if (me.type.find("inline ") >= 0) {
|
||||
me.attributes |= Inline;
|
||||
me.type.removeAll("inline ");
|
||||
}
|
||||
if (me.type.find("static ") >= 0) {
|
||||
me.attributes |= Static;
|
||||
me.type.removeAll("static ");
|
||||
}
|
||||
if (me.type.find("virtual ") >= 0) {
|
||||
me.attributes |= Virtual;
|
||||
me.type.removeAll("virtual ");
|
||||
}
|
||||
normalizeEntityNamespace(me.type);
|
||||
int i = 0;
|
||||
//piCout << me.arguments_full;
|
||||
piForeach (PIString & a, me.arguments_full)
|
||||
if ((i = a.find("=")) > 0)
|
||||
a.cutRight(a.size_s() - i).trim();
|
||||
for (int j = 0; j < me.arguments_full.size_s(); ++j)
|
||||
if (me.arguments_full[j] == "void") {
|
||||
me.arguments_full.remove(j);
|
||||
--j;
|
||||
}
|
||||
me.arguments_type = me.arguments_full;
|
||||
piForeach (PIString & a, me.arguments_type) {
|
||||
crepl.clear();
|
||||
if (a.contains("["))
|
||||
crepl = a.takeMid(a.find("["), a.findLast("]") - a.find("[") + 1);
|
||||
for (ts = a.size_s() - 1; ts >= 0; --ts)
|
||||
if (!_isCChar(a[ts]) && !(a[ts].isDigit())) break;
|
||||
a.cutRight(a.size_s() - ts - 1);
|
||||
normalizeEntityNamespace(a);
|
||||
a += crepl;
|
||||
a.trim();
|
||||
}
|
||||
restoreTmpTemp(&me);
|
||||
//piCout << "func" << me.type << me.name << me.arguments_full << me.arguments_type;
|
||||
parent->functions << me;
|
||||
} else {
|
||||
if (fc.endsWith(";")) fc.cutRight(1);
|
||||
if (fc.startsWith("using") || !(fc.contains(' ') || fc.contains('\t') || fc.contains('\n'))) return true;
|
||||
tl = fc.split(",");
|
||||
//piCout << "\tmember" << fc;
|
||||
if (tl.isEmpty()) return true;
|
||||
bool vn = true;
|
||||
ctemp = tl.front();
|
||||
for (ts = ctemp.size_s() - 1; ts > 0; --ts) {
|
||||
if (vn) {if (!_isCChar(ctemp[ts]) && !ctemp[ts].isDigit() && ctemp[ts] != '[' && ctemp[ts] != ']') vn = false;}
|
||||
else {if (_isCChar(ctemp[ts]) || ctemp[ts].isDigit()) break;}
|
||||
}
|
||||
me.type = ctemp.takeLeft(ts + 1);
|
||||
me.visibility = cur_def_vis;
|
||||
restoreTmpTemp(&me);
|
||||
PIString type = " " + me.type;
|
||||
if (type.find(" const ") >= 0) {
|
||||
me.attributes |= Const;
|
||||
type.replaceAll(" const ", " ");
|
||||
}
|
||||
if (type.find(" static ") >= 0) {
|
||||
me.attributes |= Static;
|
||||
type.replaceAll(" static ", " ");
|
||||
}
|
||||
if (type.find(" mutable ") >= 0) {
|
||||
me.attributes |= Mutable;
|
||||
type.replaceAll(" mutable ", " ");
|
||||
}
|
||||
if (type.find(" volatile ") >= 0) {
|
||||
me.attributes |= Volatile;
|
||||
type.replaceAll(" volatile ", " ");
|
||||
}
|
||||
if (type.find(" extern ") >= 0) {
|
||||
me.attributes |= Extern;
|
||||
type.replaceAll(" extern ", " ");
|
||||
}
|
||||
type.trim();
|
||||
normalizeEntityNamespace(type);
|
||||
tl[0] = ctemp.trim();
|
||||
piForeachC (PIString & v, tl) {
|
||||
crepl.clear();
|
||||
me.name = v.trimmed();
|
||||
me.type = type;
|
||||
if (me.name.isEmpty()) continue;
|
||||
if (me.name.contains("["))
|
||||
crepl = me.name.takeMid(me.name.find("["), me.name.findLast("]") - me.name.find("[") + 1);
|
||||
while (!me.name.isEmpty()) {
|
||||
if (me.name.front() == "*" || me.name.front() == "&") {
|
||||
me.type += me.name.takeLeft(1);
|
||||
me.name.trim();
|
||||
} else break;
|
||||
}
|
||||
me.is_type_ptr = (me.type.right(1) == "]" || me.type.right(1) == "*");
|
||||
me.type += crepl;
|
||||
//piCout << "var" << me.type << me.name << me.is_const << me.is_static;
|
||||
parent->members << me;
|
||||
}
|
||||
}
|
||||
//piCout << "parse member" << fc;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void PICodeParser::normalizeEntityNamespace(PIString & n) {
|
||||
PIString suff, pref;
|
||||
for (int i = n.size_s() - 1; i > 0; --i)
|
||||
if (_isCChar(n[i]) || n[i].isDigit()) {
|
||||
suff = n.right(n.size_s() - i - 1);
|
||||
n.cutRight(suff.size_s());
|
||||
break;
|
||||
}
|
||||
n.push_front(" ");
|
||||
if (n.find(" static ") >= 0) {n.replaceAll(" static ", ""); pref += "static ";}
|
||||
if (n.find(" const ") >= 0) {n.replaceAll(" const ", ""); pref += "const ";}
|
||||
if (n.find(" mutable ") >= 0) {n.replaceAll(" mutable ", ""); pref += "mutable ";}
|
||||
if (n.find(" volatile ") >= 0) {n.replaceAll(" volatile ", ""); pref += "volatile ";}
|
||||
n.trim();
|
||||
int f = 0;
|
||||
piForeachC (Entity * e, entities) {
|
||||
if (e->name == n) {
|
||||
n = (pref + n + suff).trim();
|
||||
return;
|
||||
}
|
||||
if ((f = e->name.find(n)) >= 0)
|
||||
if (e->name.mid(f - 1, 1) == ":")
|
||||
if (e->name.find(cur_namespace) >= 0) {
|
||||
n = pref + e->name + suff;
|
||||
return;
|
||||
}
|
||||
}
|
||||
piForeachC (Enum & e, enums)
|
||||
if ((f = e.name.find(n)) >= 0)
|
||||
if (e.name.mid(f - 1, 1) == ":")
|
||||
if (e.name.find(cur_namespace) >= 0) {
|
||||
//piCout << "change" << n << "to" << e.name + suff;
|
||||
n = pref + e.name + suff;
|
||||
return;
|
||||
}
|
||||
piForeachC (Typedef & e, typedefs)
|
||||
if ((f = e.first.find(n)) >= 0)
|
||||
if (e.first.mid(f - 1, 1) == ":")
|
||||
if (e.first.find(cur_namespace) >= 0) {
|
||||
//piCout << "change" << n << "to" << e.name + suff;
|
||||
n = pref + e.first + suff;
|
||||
return;
|
||||
}
|
||||
n = (pref + n + suff).trim();
|
||||
}
|
||||
|
||||
|
||||
void PICodeParser::restoreTmpTemp(Member * e) {
|
||||
int i = 0;
|
||||
piForeach (PIString & a, e->arguments_full) {
|
||||
while ((i = a.find("$")) >= 0)
|
||||
a.replace(i, 4, tmp_temp[a.mid(i, 4)]);
|
||||
}
|
||||
piForeach (PIString & a, e->arguments_type) {
|
||||
while ((i = a.find("$")) >= 0)
|
||||
a.replace(i, 4, tmp_temp[a.mid(i, 4)]);
|
||||
}
|
||||
while ((i = e->type.find("$")) >= 0)
|
||||
e->type.replace(i, 4, tmp_temp[e->type.mid(i, 4)]);
|
||||
}
|
||||
|
||||
|
||||
bool PICodeParser::macroCondition(const PIString & mif, PIString mifcond) {
|
||||
//piCout << "macroCondition" << mif << mifcond;
|
||||
if (mif == "ifdef") return isDefineExists(mifcond);
|
||||
if (mif == "ifndef") return !isDefineExists(mifcond);
|
||||
if (mif == "if" || mif == "elif") {
|
||||
mifcond.removeAll(" ").removeAll("\t");
|
||||
return procMacrosCond(mifcond) > 0.;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
double PICodeParser::procMacrosCond(PIString fc) {
|
||||
bool neg = false, first = true, br = false;
|
||||
double ret = 0., brv = 0.;
|
||||
int oper = 0, ps = -1;
|
||||
char cc, nc;
|
||||
PIString ce;
|
||||
fc.removeAll("defined");
|
||||
//piCout << "procMacrosCond" << fc;
|
||||
while (!fc.isEmpty()) {
|
||||
cc = fc[0].toAscii();
|
||||
nc = (fc.size() > 1 ? fc[1].toAscii() : 0);
|
||||
if (cc == '!') {neg = true; fc.pop_front(); continue;}
|
||||
if (cc == '(') {br = true; brv = procMacrosCond(fc.takeRange('(', ')'));}
|
||||
if (cc == '&' && nc == '&') {fc.remove(0, 2); oper = 1; continue;}
|
||||
if (cc == '|' && nc == '|') {fc.remove(0, 2); oper = 2; continue;}
|
||||
if (!br) {
|
||||
ce = fc.takeCWord();
|
||||
if (ce.isEmpty()) ce = fc.takeNumber();
|
||||
}
|
||||
if (first) {
|
||||
first = false;
|
||||
ret = br ? brv : defineValue(ce);
|
||||
if (neg) ret = -ret;
|
||||
} else {
|
||||
//piCout << "oper" << oper << "with" << ce;
|
||||
if (!br) brv = defineValue(ce);
|
||||
switch (oper) {
|
||||
case 1: ret = ret && (neg ? -brv : brv); break;
|
||||
case 2: ret = ret || (neg ? -brv : brv); break;
|
||||
}
|
||||
}
|
||||
if (ps == fc.size_s()) fc.cutLeft(1);
|
||||
ps = fc.size_s();
|
||||
br = neg = false;
|
||||
}
|
||||
//piCout << "return" << ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool PICodeParser::isDefineExists(const PIString & dn) {
|
||||
piForeachC (Define & d, defines) {
|
||||
if (d.first == dn)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
double PICodeParser::defineValue(const PIString & dn) {
|
||||
piForeachC (Define & d, defines) {
|
||||
if (d.first == dn)
|
||||
return d.second.isEmpty() ? 1. : d.second.toDouble();
|
||||
}
|
||||
return dn.toDouble();
|
||||
}
|
||||
|
||||
|
||||
PICodeParser::Entity * PICodeParser::findEntityByName(const PIString & en) {
|
||||
piForeach (Entity * e, entities)
|
||||
if (e->name == en)
|
||||
return e;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool PICodeParser::isDeclaration(const PIString & fc, int start, int * end) {
|
||||
int dind = fc.find("{", start), find = fc.find(";", start);
|
||||
//piCout << "isDeclaration" << dind << find;
|
||||
if (dind < 0 && find < 0) {if (end) *end = -1; return true;}
|
||||
if (dind < 0 || find < dind) {if (end) *end = find; return true;}
|
||||
if (end) *end = dind;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool PICodeParser::isMainFile(const PIString & fc) {
|
||||
int si = 0;
|
||||
while (si >= 0) {
|
||||
int csi = fc.find(" main", si);
|
||||
if (csi < 0) csi = fc.find("\tmain", si);
|
||||
if (csi < 0) csi = fc.find("\nmain", si);
|
||||
if (csi < 0) return false;
|
||||
si = csi;
|
||||
int fi = fc.find("(", si + 5);
|
||||
if (fi < 0) return false;
|
||||
if (fi - si < 10) {
|
||||
PIString ms(fc.mid(si, fi - si + 1));
|
||||
ms.removeAll(" ").removeAll("\t").removeAll("\n");
|
||||
if (ms == "main(") return true;
|
||||
}
|
||||
si += 5;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
PIString PICodeParser::procMacros(PIString fc) {
|
||||
if (fc.isEmpty()) return PIString();
|
||||
int ifcnt = 0;
|
||||
bool grab = false, skip = false, cond_ok = false;
|
||||
PIString pfc, nfc, line, mif, mifcond;
|
||||
//piCout << "procMacros\n<******" << fc << "\n******>";
|
||||
fc += "\n";
|
||||
while (!fc.isEmpty()) {
|
||||
line = fc.takeLine().trimmed();
|
||||
if (line.left(1) == "#") {
|
||||
mifcond = line.mid(1);
|
||||
mif = mifcond.takeCWord();
|
||||
//piCout << "mif mifcond" << mif << mifcond << ifcnt;
|
||||
if (skip || grab) {
|
||||
if (mif.left(2) == "if") ifcnt++;
|
||||
if (mif.left(5) == "endif") {
|
||||
if (ifcnt > 0) ifcnt--;
|
||||
else {
|
||||
//piCout << "main endif" << skip << grab;
|
||||
if (grab) pfc << procMacros(nfc);
|
||||
skip = grab = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (mif.left(4) == "elif" && ifcnt == 0) {
|
||||
//piCout << "main elif" << skip << grab << cond_ok;
|
||||
if (cond_ok) {
|
||||
if (grab) {
|
||||
pfc << procMacros(nfc);
|
||||
skip = true; grab = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (skip) {
|
||||
//piCout << "check elif" << skip << grab << cond_ok;
|
||||
if (!macroCondition(mif, mifcond.trimmed())) continue;
|
||||
//piCout << "check elif ok";
|
||||
skip = false; grab = cond_ok = true;
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (mif.left(4) == "else" && ifcnt == 0) {
|
||||
//piCout << "main else" << skip << grab;
|
||||
if (grab) pfc << procMacros(nfc);
|
||||
if (skip && !cond_ok) {skip = false; grab = true;}
|
||||
else {skip = true; grab = false;}
|
||||
continue;
|
||||
}
|
||||
if (grab) nfc << line << "\n";
|
||||
continue;
|
||||
}
|
||||
if (mif.left(2) == "if") {
|
||||
//piCout << "main if";
|
||||
skip = grab = cond_ok = false;
|
||||
if (macroCondition(mif, mifcond.trimmed())) grab = cond_ok = true;
|
||||
else skip = true;
|
||||
ifcnt = 0;
|
||||
nfc.clear();
|
||||
} else {
|
||||
if (!parseDirective(line.cutLeft(1).trim()))
|
||||
;//return false; /// WARNING
|
||||
}
|
||||
} else {
|
||||
if (grab) nfc << line << "\n";
|
||||
else if (!skip) pfc << line << "\n";
|
||||
}
|
||||
}
|
||||
return pfc;
|
||||
}
|
||||
|
||||
|
||||
bool PICodeParser::parseDirective(PIString d) {
|
||||
if (d.isEmpty()) return true;
|
||||
PIString dname = d.takeCWord();
|
||||
//piCout << "parseDirective" << d;
|
||||
if (dname == "include") {
|
||||
d.replaceAll("<", "\"").replaceAll(">", "\"");
|
||||
PIString cf = cur_file, ifc = d.takeRange("\"", "\"");
|
||||
if (with_includes) {
|
||||
bool ret = parseFileInternal(ifc, with_includes);
|
||||
cur_file = cf;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
if (dname == "define") {
|
||||
PIString mname = d.takeCWord();
|
||||
if (d.left(1) == "(") { // macro
|
||||
PIStringList args = d.takeRange("(", ")").split(",").trim();
|
||||
macros << Macro(mname, d.trim(), args);
|
||||
} else { // define
|
||||
d.trim();
|
||||
//if (mname == d) d.clear();
|
||||
defines << Define(mname, d);
|
||||
evaluator.setVariable(mname, complexd_1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (dname == "undef") {
|
||||
PIString mname = d.takeCWord();
|
||||
for (int i = 0; i < defines.size_s(); ++i)
|
||||
if (defines[i].first == mname) {defines.remove(i); --i;}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
162
src/code/picodeparser.h
Executable file
162
src/code/picodeparser.h
Executable file
@@ -0,0 +1,162 @@
|
||||
/*! \file picodeparser.h
|
||||
* \brief C++ code parser
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
C++ code parser
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PICODEPARSER_H
|
||||
#define PICODEPARSER_H
|
||||
|
||||
#include "pifile.h"
|
||||
#include "pievaluator.h"
|
||||
|
||||
inline bool _isCChar(const PIChar & c) {return (c.isAlpha() || (c.toAscii() == '_'));}
|
||||
inline bool _isCChar(const PIString & c) {if (c.isEmpty()) return false; return _isCChar(c[0]);}
|
||||
|
||||
class PIP_EXPORT PICodeParser {
|
||||
public:
|
||||
PICodeParser();
|
||||
|
||||
enum PIP_EXPORT Visibility {Global, Public, Protected, Private};
|
||||
enum PIP_EXPORT Attribute {
|
||||
NoAttributes = 0x0,
|
||||
Const = 0x01,
|
||||
Static = 0x02,
|
||||
Mutable = 0x04,
|
||||
Volatile = 0x08,
|
||||
Inline = 0x10,
|
||||
Virtual = 0x20,
|
||||
Extern = 0x40
|
||||
};
|
||||
|
||||
typedef PIFlags<Attribute> Attributes;
|
||||
typedef PIPair<PIString, PIString> Define;
|
||||
typedef PIPair<PIString, PIString> Typedef;
|
||||
typedef PIPair<PIString, int> Enumerator;
|
||||
|
||||
struct PIP_EXPORT Macro {
|
||||
Macro(const PIString & n = PIString(), const PIString & v = PIString(), const PIStringList & a = PIStringList()) {
|
||||
name = n;
|
||||
value = v;
|
||||
args = a;
|
||||
}
|
||||
PIString expand(PIString args_, bool * ok = 0) const;
|
||||
PIString name;
|
||||
PIString value;
|
||||
PIStringList args;
|
||||
};
|
||||
|
||||
struct PIP_EXPORT Member {
|
||||
Member() {
|
||||
visibility = Global;
|
||||
size = 0;
|
||||
is_type_ptr = false;
|
||||
attributes = NoAttributes;
|
||||
}
|
||||
PIString type;
|
||||
PIString name;
|
||||
PIStringList arguments_full;
|
||||
PIStringList arguments_type;
|
||||
Visibility visibility;
|
||||
Attributes attributes;
|
||||
bool is_type_ptr;
|
||||
int size;
|
||||
};
|
||||
|
||||
struct PIP_EXPORT Entity {
|
||||
Entity() {
|
||||
visibility = Global;
|
||||
size = 0;
|
||||
}
|
||||
PIString type;
|
||||
PIString name;
|
||||
PIString file;
|
||||
Visibility visibility;
|
||||
int size;
|
||||
PIVector<Entity * > parents;
|
||||
//PIVector<Entity * > children;
|
||||
PIVector<Member> functions;
|
||||
PIVector<Member> members;
|
||||
PIVector<Typedef> typedefs;
|
||||
};
|
||||
|
||||
struct PIP_EXPORT Enum {
|
||||
Enum(const PIString & n = PIString()) {
|
||||
name = n;
|
||||
}
|
||||
PIString name;
|
||||
PIVector<Enumerator> members;
|
||||
};
|
||||
|
||||
void parseFile(const PIString & file, bool follow_includes = true);
|
||||
void parseFiles(const PIStringList & files, bool follow_includes = true);
|
||||
|
||||
void includeDirectory(const PIString & dir) {includes << dir;}
|
||||
void addDefine(const PIString & def_name, const PIString & def_value) {custom_defines << Define(def_name, def_value);}
|
||||
bool isEnum(const PIString & name);
|
||||
Entity * findEntityByName(const PIString & en);
|
||||
PIStringList parsedFiles() const {return PIStringList(proc_files.toVector());}
|
||||
PIString mainFile() const {return main_file;}
|
||||
const PICodeParser::Entity * global() const {return &root_;}
|
||||
|
||||
int macrosSubstitutionMaxIterations() const {return macros_iter;}
|
||||
void setMacrosSubstitutionMaxIterations(int value) {macros_iter = value;}
|
||||
|
||||
PIVector<Define> defines, custom_defines;
|
||||
PIVector<Macro> macros;
|
||||
PIVector<Enum> enums;
|
||||
PIVector<Typedef> typedefs;
|
||||
PIVector<Entity * > entities;
|
||||
|
||||
private:
|
||||
void clear();
|
||||
bool parseFileInternal(const PIString & file, bool follow_includes);
|
||||
bool parseFileContent(PIString & fc, bool main);
|
||||
bool parseDirective(PIString d);
|
||||
Entity * parseClassDeclaration(const PIString & fc);
|
||||
PIString parseClass(PIString & fc);
|
||||
bool parseEnum(const PIString & name, PIString fc);
|
||||
Typedef parseTypedef(PIString fc);
|
||||
bool parseMember(Entity * parent, PIString & fc);
|
||||
void restoreTmpTemp(Member * e);
|
||||
bool macroCondition(const PIString & mif, PIString mifcond);
|
||||
bool isDefineExists(const PIString & dn);
|
||||
double defineValue(const PIString & dn);
|
||||
PIString procMacros(PIString fc);
|
||||
double procMacrosCond(PIString fc);
|
||||
bool isDeclaration(const PIString & fc, int start, int * end);
|
||||
bool isMainFile(const PIString & fc);
|
||||
void normalizeEntityNamespace(PIString & n);
|
||||
|
||||
int macros_iter;
|
||||
bool with_includes;
|
||||
PIEvaluator evaluator;
|
||||
//PIVector<Entity * > tree;
|
||||
PISet<PIString> proc_files;
|
||||
PIString cur_file, main_file;
|
||||
PIStringList includes;
|
||||
Entity root_;
|
||||
Visibility cur_def_vis;
|
||||
PIString cur_namespace;
|
||||
PIMap<PIString, PIString> tmp_temp;
|
||||
|
||||
};
|
||||
|
||||
#endif // PICODEPARSER_H
|
||||
175
src/containers/picontainers.cpp
Executable file
175
src/containers/picontainers.cpp
Executable file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Generic containers
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// * This class based on std::vector, expanding his functionality
|
||||
|
||||
/** \class PIVector
|
||||
* \brief Dynamic array of any type
|
||||
* \details This class used to store dynamic array of any
|
||||
* type of data. In memory data stored linear. You can insert
|
||||
* item in any place of remove some items from any place.
|
||||
* For quick add elements this is stream operator <<.
|
||||
|
||||
* \fn PIVector::PIVector();
|
||||
* Contructs an empty vector
|
||||
|
||||
* \fn PIVector::PIVector(ullong size, const Type & value = Type());
|
||||
* \brief Contructs vector with size "size" filled elements "value"
|
||||
* \details Example: \snippet picontainers.cpp PIVector::PIVector
|
||||
|
||||
* \fn const Type & PIVector::at(ullong index) const;
|
||||
* \brief Read-only access to element by index "index"
|
||||
* \details Example: \snippet picontainers.cpp PIVector::at_c
|
||||
* \sa \a operator[]
|
||||
|
||||
* \fn Type & PIVector::at(ullong index);
|
||||
* \brief Full access to element by index "index"
|
||||
* \details Example: \snippet picontainers.cpp PIVector::at
|
||||
* \sa \a operator[]
|
||||
|
||||
* \fn const Type * PIVector::data(ullong index = 0) const;
|
||||
* \brief Read-only pointer to element by index "index"
|
||||
* \details Example: \snippet picontainers.cpp PIVector::data_c
|
||||
|
||||
* \fn Type * PIVector::data(ullong index = 0);
|
||||
* \brief Pointer to element by index "index"
|
||||
* \details Example: \snippet picontainers.cpp PIVector::data
|
||||
|
||||
* \fn ullong PIVector::size() const;
|
||||
* \brief Elements count
|
||||
|
||||
* \fn int PIVector::size_s() const;
|
||||
* \brief Elements count
|
||||
|
||||
* \fn bool PIVector::isEmpty() const;
|
||||
* \brief Return \c "true" if vector is empty, i.e. size = 0
|
||||
|
||||
* \fn bool PIVector::has(const Type & t) const;
|
||||
|
||||
* \fn bool PIVector::contains(const Type & v) const;
|
||||
* \brief Return \c "true" if vector has at least one element equal "t"
|
||||
|
||||
* \fn int PIVector::etries(const Type & t) const;
|
||||
* \brief Return how many times element "t" appears in vector
|
||||
|
||||
* \fn static int PIVector::compare_func(const Type * t0, const Type * t1);
|
||||
* \brief Standard compare function for type "Type". Return 0 if t0 = t1, -1 if t0 < t1 and 1 if t0 > t1.
|
||||
|
||||
* \fn void PIVector::resize(ullong size, const Type & new_type = Type());
|
||||
* \brief Resize vector to size "size"
|
||||
* \details Elements removed from end of vector if new size < old size, or added new elements = "new_type" if new size > old size.\n
|
||||
* Example: \snippet picontainers.cpp PIVector::resize
|
||||
* \sa \a size(), \a clear()
|
||||
|
||||
* \fn PIVector<T> & PIVector::enlarge(ullong size);
|
||||
* \brief Increase vector size with "size" elements
|
||||
|
||||
* \fn void PIVector::clear();
|
||||
* \brief Clear vector. Equivalent to call <tt>"resize(0)"</tt>
|
||||
|
||||
* \fn PIVector<T> & PIVector::sort(CompareFunc compare = compare_func);
|
||||
* \brief Sort vector using quick sort algorithm and standard compare function
|
||||
* \details Example: \snippet picontainers.cpp PIVector::sort_0
|
||||
* With custom compare function: \snippet picontainers.cpp PIVector::sort_1
|
||||
|
||||
* \fn PIVector<T> & PIVector::fill(const Type & t);
|
||||
* \brief Fill vector with elements "t" leave size is unchanged and return reference to vector
|
||||
* \details Example: \snippet picontainers.cpp PIVector::fill
|
||||
|
||||
* \fn Type & PIVector::back();
|
||||
* \brief Last element of the vector
|
||||
|
||||
* \fn const Type & PIVector::back() const;
|
||||
* \brief Last element of the vector
|
||||
|
||||
* \fn Type & PIVector::front();
|
||||
* \brief First element of the vector
|
||||
|
||||
* \fn const Type & PIVector::front() const;
|
||||
* \brief First element of the vector
|
||||
|
||||
* \fn PIVector<T> & PIVector::push_back(const Type & t);
|
||||
* \brief Add new element "t" at the end of vector and return reference to vector
|
||||
|
||||
* \fn PIVector<T> & PIVector::push_front(const Type & t);
|
||||
* \brief Add new element "t" at the beginning of vector and return reference to vector
|
||||
|
||||
* \fn PIVector<T> & PIVector::pop_back();
|
||||
* \brief Remove one element from the end of vector and return reference to vector
|
||||
|
||||
* \fn PIVector<T> & PIVector::pop_front();
|
||||
* \brief Remove one element from the beginning of vector and return reference to vector
|
||||
|
||||
* \fn Type PIVector::take_back();
|
||||
* \brief Remove one element from the end of vector and return it
|
||||
|
||||
* \fn Type PIVector::take_front();
|
||||
* \brief Remove one element from the beginning of vector and return it
|
||||
|
||||
* \fn PIVector<T> & PIVector::remove(uint index);
|
||||
* \brief Remove one element by index "index" and return reference to vector
|
||||
* \details Example: \snippet picontainers.cpp PIVector::remove_0
|
||||
* \sa \a removeOne(), \a removeAll()
|
||||
|
||||
* \fn PIVector<T> & PIVector::remove(uint index, uint count);
|
||||
* \brief Remove "count" elements by first index "index" and return reference to vector
|
||||
* \details Example: \snippet picontainers.cpp PIVector::remove_1
|
||||
* \sa \a removeOne(), \a removeAll()
|
||||
|
||||
* \fn PIVector<T> & PIVector::removeOne(const Type & v);
|
||||
* \brief Remove no more than one element equal "v" and return reference to vector
|
||||
* \details Example: \snippet picontainers.cpp PIVector::removeOne
|
||||
* \sa \a remove(), \a removeAll()
|
||||
|
||||
* \fn PIVector<T> & PIVector::removeAll(const Type & v);
|
||||
* \brief Remove all elements equal "v" and return reference to vector
|
||||
* \details Example: \snippet picontainers.cpp PIVector::removeAll
|
||||
* \sa \a remove(), \a removeOne()
|
||||
|
||||
* \fn PIVector<T> & PIVector::insert(uint pos, const Type & t);
|
||||
* \brief Insert element "t" after index "pos" and return reference to vector
|
||||
* \details Example: \snippet picontainers.cpp PIVector::insert_0
|
||||
|
||||
* \fn PIVector<T> & PIVector::insert(uint pos, const PIVector<T> & t);
|
||||
* \brief Insert other vector "t" after index "pos" and return reference to vector
|
||||
* \details Example: \snippet picontainers.cpp PIVector::insert_1
|
||||
|
||||
* \fn Type & PIVector::operator [](uint index);
|
||||
* \brief Full access to element by index "index"
|
||||
* \details Example: \snippet picontainers.cpp PIVector::()
|
||||
* \sa \a at()
|
||||
|
||||
* \fn const Type & PIVector::operator [](uint index) const;
|
||||
* \brief Read-only access to element by index "index"
|
||||
* \details Example: \snippet picontainers.cpp PIVector::()_c
|
||||
* \sa \a at()
|
||||
|
||||
* \fn PIVector<T> & PIVector::operator <<(const Type & t);
|
||||
* \brief Add new element "t" at the end of vector and return reference to vector
|
||||
|
||||
* \fn PIVector<T> & PIVector::operator <<(const PIVector<T> & t);
|
||||
* \brief Add vector "t" at the end of vector and return reference to vector
|
||||
|
||||
* \fn bool PIVector::operator ==(const PIVector<T> & t);
|
||||
* \brief Compare with vector "t"
|
||||
|
||||
* \fn bool PIVector::operator !=(const PIVector<T> & t);
|
||||
* \brief Compare with vector "t"
|
||||
|
||||
* */
|
||||
286
src/containers/picontainers.h
Executable file
286
src/containers/picontainers.h
Executable file
@@ -0,0 +1,286 @@
|
||||
/*! \file picontainers.h
|
||||
* \brief Generic containers
|
||||
*
|
||||
* This file declare all containers and useful macros
|
||||
* to use them
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Generic containers
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PICONTAINERS_H
|
||||
#define PICONTAINERS_H
|
||||
|
||||
#include "picout.h"
|
||||
|
||||
template<typename Type0, typename Type1>
|
||||
class PIP_EXPORT PIPair {
|
||||
public:
|
||||
PIPair() {first = Type0(); second = Type1();}
|
||||
PIPair(const Type0 & value0, const Type1 & value1) {first = value0; second = value1;}
|
||||
Type0 first;
|
||||
Type1 second;
|
||||
};
|
||||
template<typename Type0, typename Type1>
|
||||
inline bool operator <(const PIPair<Type0, Type1> & value0, const PIPair<Type0, Type1> & value1) {return value0.first < value1.first;}
|
||||
template<typename Type0, typename Type1>
|
||||
inline bool operator ==(const PIPair<Type0, Type1> & value0, const PIPair<Type0, Type1> & value1) {return (value0.first == value1.first) && (value0.second == value1.second);}
|
||||
template<typename Type0, typename Type1>
|
||||
inline bool operator !=(const PIPair<Type0, Type1> & value0, const PIPair<Type0, Type1> & value1) {return (value0.first != value1.first) || (value0.second != value1.second);}
|
||||
template<typename Type0, typename Type1>
|
||||
inline std::ostream & operator <<(std::ostream & s, const PIPair<Type0, Type1> & v) {s << "(" << v.first << ", " << v.second << ")"; return s;}
|
||||
template<typename Type0, typename Type1>
|
||||
inline PICout operator <<(PICout s, const PIPair<Type0, Type1> & v) {s.space(); s.setControl(0, true); s << "(" << v.first << ", " << v.second << ")"; s.restoreControl(); return s;}
|
||||
|
||||
#include "pivector.h"
|
||||
#include "pistack.h"
|
||||
#include "piqueue.h"
|
||||
#include "pideque.h"
|
||||
#include "pimap.h"
|
||||
#include "piset.h"
|
||||
|
||||
#ifdef DOXYGEN
|
||||
|
||||
/*!\brief Macro for iterate any container
|
||||
* \details Use this macros instead of standard "for"
|
||||
* to get read/write access to each element of container.
|
||||
* Pass direction is direct \n
|
||||
* Example: \snippet picontainers.cpp foreach
|
||||
*/
|
||||
# define piForeach(i,c)
|
||||
|
||||
/*!\brief Macro for iterate any container only for read
|
||||
* \details Use this macros instead of standard "for"
|
||||
* to get read access to each element of container.
|
||||
* Pass direction is direct \n
|
||||
* Example: \snippet picontainers.cpp foreachC
|
||||
*/
|
||||
# define piForeachC(i,c)
|
||||
|
||||
/*!\brief Macro for iterate any container with reverse direction
|
||||
* \details Use this macros instead of standard "for"
|
||||
* to get read/write access to each element of container.
|
||||
* Pass direction is reverse \n
|
||||
* Example: \snippet picontainers.cpp foreachR
|
||||
*/
|
||||
# define piForeachR(i,c)
|
||||
|
||||
/*!\brief Macro for iterate any container only for read with reverse direction
|
||||
* \details Use this macros instead of standard "for"
|
||||
* to get read access to each element of container.
|
||||
* Pass direction is reverse \n
|
||||
* Example: \snippet picontainers.cpp foreachCR
|
||||
*/
|
||||
# define piForeachCR(i,c)
|
||||
|
||||
/*!\brief Macro for break from any piForeach* loop
|
||||
* \details \warning C++ ordinary "break" doesn`t work inside piForeach*
|
||||
* loops! Always use "piBreak" instead!
|
||||
*/
|
||||
# define piBreak
|
||||
|
||||
#else
|
||||
|
||||
# define piBreak {_for._end = true; break;}
|
||||
|
||||
# define piForTimes(c) for(int _i##c = 0; _i##c < c; ++_i##c)
|
||||
|
||||
#ifdef CC_GCC
|
||||
|
||||
template<typename Type>
|
||||
class _PIForeach {
|
||||
public:
|
||||
_PIForeach(Type & t): _t(t), _break(false), _end(false) {_it = _t.begin();}
|
||||
typename Type::value_type _var;
|
||||
typename Type::iterator _it;
|
||||
Type & _t;
|
||||
bool _break, _end;
|
||||
inline bool isEnd() {return _it == _t.end();}
|
||||
inline void operator ++() {if (_end) _it = _t.end(); else _it++; _break = false;}
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
class _PIForeachR {
|
||||
public:
|
||||
_PIForeachR(Type & t): _t(t), _break(false), _end(false) {_rit = _t.rbegin();}
|
||||
typename Type::value_type _var;
|
||||
typename Type::reverse_iterator _rit;
|
||||
Type & _t;
|
||||
bool _break, _end;
|
||||
inline bool isEnd() {return _rit == _t.rend();}
|
||||
inline void operator ++() {if (_end) _rit = _t.rend(); else _rit++; _break = false;}
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
class _PIForeachC {
|
||||
public:
|
||||
_PIForeachC(const Type & t): _t(t), _break(false), _end(false) {_it = _t.begin();}
|
||||
typename Type::value_type _var;
|
||||
typename Type::const_iterator _it;
|
||||
const Type & _t;
|
||||
bool _break, _end;
|
||||
inline bool isEnd() {return _it == _t.end();}
|
||||
inline void operator ++() {if (_end) _it = _t.end(); else _it++; _break = false;}
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
class _PIForeachCR {
|
||||
public:
|
||||
_PIForeachCR(const Type & t): _t(t), _break(false), _end(false) {_rit = _t.rbegin();}
|
||||
typename Type::value_type _var;
|
||||
typename Type::const_reverse_iterator _rit;
|
||||
const Type & _t;
|
||||
bool _break, _end;
|
||||
inline bool isEnd() {return _rit == _t.rend();}
|
||||
inline void operator ++() {if (_end) _rit = _t.rend(); else _rit++; _break = false;}
|
||||
};
|
||||
|
||||
#define piForeach(i,c) for(_PIForeach<typeof(c)> _for(c); !_for.isEnd(); ++_for) \
|
||||
for(i(*_for._it); !_for._break; _for._break = true)
|
||||
#define piForeachR(i,c) for(_PIForeachR<typeof(c)> _for(c); !_for.isEnd(); ++_for) \
|
||||
for(i(*_for._rit); !_for._break; _for._break = true)
|
||||
#define piForeachA(i,c) for(_PIForeach<typeof(c)> _for(c); !_for.isEnd(); ++_for) \
|
||||
for(typeof(_for._var) & i(*_for._it); !_for._break; _for._break = true)
|
||||
#define piForeachAR(i,c) for(_PIForeachR<typeof(c)> _for(c); !_for.isEnd(); ++_for) \
|
||||
for(typeof(_for._var) & i(*_for._rit); !_for._break; _for._break = true)
|
||||
#define piForeachC(i,c) for(_PIForeachC<typeof(c)> _for(c); !_for.isEnd(); ++_for) \
|
||||
for(const i(*_for._it); !_for._break; _for._break = true)
|
||||
#define piForeachCR(i,c) for(_PIForeachCR<typeof(c)> _for(c); !_for.isEnd(); ++_for) \
|
||||
for(const i(*_for._rit); !_for._break; _for._break = true)
|
||||
#define piForeachCA(i,c) for(_PIForeachC<typeof(c)> _for(c); !_for.isEnd(); ++_for) \
|
||||
for(const typeof(_for._var) & i(*_for._it); !_for._break; _for._break = true)
|
||||
#define piForeachCAR(i,c) for(_PIForeachCR<typeof(c)> _for(c); !_for.isEnd(); ++_for) \
|
||||
for(const typeof(_for._var) & i(*_for._rit); !_for._break; _for._break = true)
|
||||
|
||||
#define piForeachRA piForeachAR
|
||||
#define piForeachAC piForeachCA
|
||||
#define piForeachCRA piForeachCAR
|
||||
#define piForeachARC piForeachCAR
|
||||
#define piForeachACR piForeachCAR
|
||||
#define piForeachRCA piForeachCAR
|
||||
#define piForeachRAC piForeachCAR
|
||||
|
||||
#else
|
||||
|
||||
struct _PIForeachBase {mutable bool _break, _end;};
|
||||
|
||||
template<typename Type>
|
||||
class _PIForeach: public _PIForeachBase {
|
||||
public:
|
||||
_PIForeach(Type & t, bool i = false): _break(false), _end(false), _t(t), _inv(i) {if (_inv) _rit = _t.rbegin(); else _it = _t.begin();}
|
||||
mutable typename Type::value_type _var;
|
||||
mutable typename Type::iterator _it;
|
||||
mutable typename Type::reverse_iterator _rit;
|
||||
Type & _t;
|
||||
bool _inv;
|
||||
bool isEnd() {if (_inv) return _rit == _t.rend(); else return _it == _t.end();}
|
||||
void operator ++() {if (_inv) {if (_end) _rit = _t.rend(); else _rit++;} else {if (_end) _it = _t.end(); else _it++;} _break = false;}
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
class _PIForeachC: public _PIForeachBase {
|
||||
public:
|
||||
_PIForeachC(const Type & t, bool i = false): _break(false), _end(false), _t(t), _inv(i) {if (_inv) _rit = _t.rbegin(); else _it = _t.begin();}
|
||||
mutable typename Type::value_type _var;
|
||||
mutable typename Type::const_iterator _it;
|
||||
mutable typename Type::const_reverse_iterator _rit;
|
||||
const Type & _t;
|
||||
bool _inv;
|
||||
bool isEnd() {if (_inv) return _rit == _t.rend(); else return _it == _t.end();}
|
||||
void operator ++() {if (_inv) {if (_end) _rit = _t.rend(); else _rit++;} else {if (_end) _it = _t.end(); else _it++;} _break = false;}
|
||||
};
|
||||
|
||||
template <typename T> inline _PIForeach<T> _PIForeachNew(T & t, bool i = false) {return _PIForeach<T>(t, i);}
|
||||
template <typename T> inline _PIForeach<T> * _PIForeachCast(_PIForeachBase & c, T & ) {return static_cast<_PIForeach<T> * >(&c);}
|
||||
|
||||
template <typename T> inline _PIForeachC<T> _PIForeachNewC(const T & t, bool i = false) {return _PIForeachC<T>(t, i);}
|
||||
template <typename T> inline _PIForeachC<T> * _PIForeachCastC(_PIForeachBase & c, const T & ) {return static_cast<_PIForeachC<T> * >(&c);}
|
||||
|
||||
#define piForeach(i,c) for(_PIForeachBase & _for = _PIForeachNew(c); !_PIForeachCast(_for, c)->isEnd(); ++(*_PIForeachCast(_for, c))) \
|
||||
for(i = *(_PIForeachCast(_for, c)->_it); !_for._break; _for._break = true)
|
||||
#define piForeachR(i,c) for(_PIForeachBase & _for = _PIForeachNew(c, true); !_PIForeachCast(_for, c)->isEnd(); ++(*_PIForeachCast(_for, c))) \
|
||||
for(i = *(_PIForeachCast(_for, c)->_rit); !_for._break; _for._break = true)
|
||||
#define piForeachC(i,c) for(_PIForeachBase & _for = _PIForeachNewC(c); !_PIForeachCastC(_for, c)->isEnd(); ++(*_PIForeachCastC(_for, c))) \
|
||||
for(const i = *(_PIForeachCastC(_for, c)->_it); !_for._break; _for._break = true)
|
||||
#define piForeachCR(i,c) for(_PIForeachBase & _for = _PIForeachNewC(c, false); !_PIForeachCastC(_for, c)->isEnd(); ++(*_PIForeachCastC(_for, c))) \
|
||||
for(const i = *(_PIForeachCastC(_for, c)->_rit); !_for._break; _for._break = true)
|
||||
|
||||
#endif
|
||||
|
||||
#define piForeachRC piForeachCR
|
||||
|
||||
#endif // DOXYGEN
|
||||
|
||||
template<typename Type, typename Allocator = std::allocator<Type> >
|
||||
class PIP_EXPORT PIList: public list<Type, Allocator> {
|
||||
typedef PIList<Type, Allocator> _CList;
|
||||
typedef list<Type, Allocator> _stlc;
|
||||
public:
|
||||
PIList() {piMonitor.containers++;}
|
||||
PIList(const Type & value) {piMonitor.containers++; _stlc::resize(1, value);}
|
||||
PIList(const Type & v0, const Type & v1) {piMonitor.containers++; _stlc::push_back(v0); _stlc::push_back(v1);}
|
||||
PIList(const Type & v0, const Type & v1, const Type & v2) {piMonitor.containers++; _stlc::push_back(v0); _stlc::push_back(v1); _stlc::push_back(v2);}
|
||||
PIList(const Type & v0, const Type & v1, const Type & v2, const Type & v3) {piMonitor.containers++; _stlc::push_back(v0); _stlc::push_back(v1); _stlc::push_back(v2); _stlc::push_back(v3);}
|
||||
PIList(uint size, const Type & value = Type()) {piMonitor.containers++; _stlc::resize(size, value);}
|
||||
~PIList() {piMonitor.containers--;}
|
||||
Type & operator [](uint index) {return (*this)[index];}
|
||||
Type & operator [](uint index) const {return (*this)[index];}
|
||||
const Type * data(uint index = 0) const {return &(*this)[index];}
|
||||
Type * data(uint index = 0) {return &(*this)[index];}
|
||||
int size_s() const {return static_cast<int>(_stlc::size());}
|
||||
bool isEmpty() const {return _stlc::empty();}
|
||||
bool has(const Type & t) const {for (typename _stlc::const_iterator i = _stlc::begin(); i != _stlc::end(); ++i) if (t == *i) return true; return false;}
|
||||
int etries(const Type & t) const {int ec = 0; for (typename _stlc::const_iterator i = _stlc::begin(); i != _stlc::end(); ++i) if (t == *i) ++ec; return ec;}
|
||||
_CList & fill(const Type & t) {_stlc::assign(_stlc::size(), t); return *this;}
|
||||
_CList & remove(uint index) {_stlc::erase(_stlc::begin() + index); return *this;}
|
||||
_CList & remove(uint index, uint count) {_stlc::erase(_stlc::begin() + index, _stlc::begin() + index + count); return *this;}
|
||||
_CList & insert(uint pos, const Type & t) {_stlc::insert(_stlc::begin() + pos, t); return *this;}
|
||||
_CList & operator <<(const Type & t) {_stlc::push_back(t); return *this;}
|
||||
PIVector<Type> toVector() const {PIVector<Type> v; for (typename _stlc::const_iterator i = _stlc::begin(); i != _stlc::end(); ++i) v << *i; return v;}
|
||||
};
|
||||
|
||||
|
||||
#ifndef PIP_CONTAINERS_STL
|
||||
|
||||
# define __PICONTAINERS_SIMPLE_TYPE__(T) \
|
||||
__PIDEQUE_SIMPLE_TYPE__(T)\
|
||||
__PIVECTOR_SIMPLE_TYPE__(T)
|
||||
|
||||
__PICONTAINERS_SIMPLE_TYPE__(bool)
|
||||
__PICONTAINERS_SIMPLE_TYPE__(char)
|
||||
__PICONTAINERS_SIMPLE_TYPE__(uchar)
|
||||
__PICONTAINERS_SIMPLE_TYPE__(short)
|
||||
__PICONTAINERS_SIMPLE_TYPE__(ushort)
|
||||
__PICONTAINERS_SIMPLE_TYPE__(int)
|
||||
__PICONTAINERS_SIMPLE_TYPE__(uint)
|
||||
__PICONTAINERS_SIMPLE_TYPE__(long)
|
||||
__PICONTAINERS_SIMPLE_TYPE__(ulong)
|
||||
__PICONTAINERS_SIMPLE_TYPE__(llong)
|
||||
__PICONTAINERS_SIMPLE_TYPE__(ullong)
|
||||
__PICONTAINERS_SIMPLE_TYPE__(float)
|
||||
__PICONTAINERS_SIMPLE_TYPE__(double)
|
||||
__PICONTAINERS_SIMPLE_TYPE__(ldouble)
|
||||
__PICONTAINERS_SIMPLE_TYPE__(complexi)
|
||||
__PICONTAINERS_SIMPLE_TYPE__(complexf)
|
||||
__PICONTAINERS_SIMPLE_TYPE__(complexd)
|
||||
__PICONTAINERS_SIMPLE_TYPE__(complexld)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif // PICONTAINERS_H
|
||||
480
src/containers/pideque.h
Executable file
480
src/containers/pideque.h
Executable file
@@ -0,0 +1,480 @@
|
||||
/*! \file pideque.h
|
||||
* \brief Dynamic array of any type
|
||||
*
|
||||
* This file declares PIDeque
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Dynamic array of any type
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIDEQUE_H
|
||||
#define PIDEQUE_H
|
||||
|
||||
#include "piincludes.h"
|
||||
|
||||
|
||||
#if !defined(PIP_CONTAINERS_STL) || defined(DOXYGEN)
|
||||
|
||||
|
||||
template <typename T>
|
||||
class PIDeque {
|
||||
public:
|
||||
PIDeque(): pid_data(0), pid_size(0), pid_rsize(0), pid_start(0) {
|
||||
//printf("new vector 1 %p (%s) ... !{\n", this, typeid(T).name());
|
||||
//printf("(s=%d, d=%p) }!\n", int(pid_size), pid_data);
|
||||
}
|
||||
PIDeque(const PIDeque<T> & other): pid_data(0), pid_size(0), pid_rsize(0), pid_start(0) {
|
||||
//printf("new vector 2 %p (%s) ... !{\n", this, typeid(T).name());
|
||||
alloc(other.pid_size, true);
|
||||
newT(pid_data + pid_start, other.pid_data + other.pid_start, pid_size);
|
||||
//printf("(s=%d, d=%p) }!\n", int(pid_size), pid_data);
|
||||
}
|
||||
PIDeque(const T * data, size_t size): pid_data(0), pid_size(0), pid_rsize(0), pid_start(0) {
|
||||
//printf("new vector 2 %p (%s) ... !{\n", this, typeid(T).name());
|
||||
alloc(size, true);
|
||||
newT(pid_data + pid_start, data, pid_size);
|
||||
//printf("(s=%d, d=%p) }!\n", int(pid_size), pid_data);
|
||||
}
|
||||
PIDeque(size_t pid_size, const T & f = T()): pid_data(0), pid_size(0), pid_rsize(0), pid_start(0) {
|
||||
//printf("new vector 3 %p (%s) ... !{\n", this, typeid(T).name());
|
||||
resize(pid_size, f);
|
||||
//printf("(s=%d, d=%p) }!\n", int(pid_size), pid_data);
|
||||
}
|
||||
~PIDeque() {
|
||||
//printf("delete deque %p (%s) (s=%d, rs=%d, st=%d, d=%p) ... ~{\n", this, typeid(T).name(), int(pid_size), int(pid_rsize), int(pid_start), pid_data);
|
||||
deleteT(pid_data + pid_start, pid_size);
|
||||
dealloc();
|
||||
//deleteRaw(pid_tdata);
|
||||
_reset();
|
||||
//printf("}~\n");
|
||||
}
|
||||
|
||||
PIDeque<T> & operator =(const PIDeque<T> & other) {
|
||||
if (this == &other) return *this;
|
||||
deleteT(pid_data + pid_start, pid_size);
|
||||
alloc(other.pid_size, true);
|
||||
newT(pid_data + pid_start, other.pid_data + other.pid_start, pid_size);
|
||||
return *this;
|
||||
}
|
||||
|
||||
typedef T value_type;
|
||||
|
||||
class iterator {
|
||||
friend class PIDeque<T>;
|
||||
private:
|
||||
iterator(PIDeque<T> * v, size_t p): parent(v), pos(p) {}
|
||||
PIDeque<T> * parent;
|
||||
size_t pos;
|
||||
public:
|
||||
iterator(): parent(0) {}
|
||||
T & operator *() {return (*parent)[pos];}
|
||||
const T & operator *() const {return (*parent)[pos];}
|
||||
void operator ++() {++pos;}
|
||||
void operator ++(int) {++pos;}
|
||||
void operator --() {--pos;}
|
||||
void operator --(int) {--pos;}
|
||||
bool operator ==(const iterator & it) const {return (pos == it.pos);}
|
||||
bool operator !=(const iterator & it) const {return (pos != it.pos);}
|
||||
};
|
||||
|
||||
class const_iterator {
|
||||
friend class PIDeque<T>;
|
||||
private:
|
||||
const_iterator(const PIDeque<T> * v, size_t p): parent(v), pos(p) {}
|
||||
const PIDeque<T> * parent;
|
||||
size_t pos;
|
||||
public:
|
||||
const_iterator(): parent(0) {}
|
||||
//T & operator *() {return (*parent)[pos];}
|
||||
const T & operator *() const {return (*parent)[pos];}
|
||||
void operator ++() {++pos;}
|
||||
void operator ++(int) {++pos;}
|
||||
void operator --() {--pos;}
|
||||
void operator --(int) {--pos;}
|
||||
bool operator ==(const const_iterator & it) const {return (pos == it.pos);}
|
||||
bool operator !=(const const_iterator & it) const {return (pos != it.pos);}
|
||||
};
|
||||
|
||||
class reverse_iterator {
|
||||
friend class PIDeque<T>;
|
||||
private:
|
||||
reverse_iterator(PIDeque<T> * v, size_t p): parent(v), pos(p) {}
|
||||
PIDeque<T> * parent;
|
||||
size_t pos;
|
||||
public:
|
||||
reverse_iterator(): parent(0) {}
|
||||
T & operator *() {return (*parent)[pos];}
|
||||
const T & operator *() const {return (*parent)[pos];}
|
||||
void operator ++() {--pos;}
|
||||
void operator ++(int) {--pos;}
|
||||
void operator --() {++pos;}
|
||||
void operator --(int) {++pos;}
|
||||
bool operator ==(const reverse_iterator & it) const {return (pos == it.pos);}
|
||||
bool operator !=(const reverse_iterator & it) const {return (pos != it.pos);}
|
||||
};
|
||||
|
||||
class const_reverse_iterator {
|
||||
friend class PIDeque<T>;
|
||||
private:
|
||||
const_reverse_iterator(const PIDeque<T> * v, size_t p): parent(v), pos(p) {}
|
||||
const PIDeque<T> * parent;
|
||||
size_t pos;
|
||||
public:
|
||||
const_reverse_iterator(): parent(0) {}
|
||||
//T & operator *() {return (*parent)[pos];}
|
||||
const T & operator *() const {return (*parent)[pos];}
|
||||
void operator ++() {--pos;}
|
||||
void operator ++(int) {--pos;}
|
||||
void operator --() {++pos;}
|
||||
void operator --(int) {++pos;}
|
||||
bool operator ==(const const_reverse_iterator & it) const {return (pos == it.pos);}
|
||||
bool operator !=(const const_reverse_iterator & it) const {return (pos != it.pos);}
|
||||
};
|
||||
|
||||
iterator begin() {return iterator(this, 0);}
|
||||
iterator end() {return iterator(this, pid_size);}
|
||||
const_iterator begin() const {return const_iterator(this, 0);}
|
||||
const_iterator end() const {return const_iterator(this, pid_size);}
|
||||
reverse_iterator rbegin() {return reverse_iterator(this, pid_size - 1);}
|
||||
reverse_iterator rend() {return reverse_iterator(this, -1);}
|
||||
const_reverse_iterator rbegin() const {return const_reverse_iterator(this, pid_size - 1);}
|
||||
const_reverse_iterator rend() const {return const_reverse_iterator(this, -1);}
|
||||
|
||||
size_t size() const {return pid_size;}
|
||||
ssize_t size_s() const {return pid_size;}
|
||||
size_t length() const {return pid_size;}
|
||||
size_t capacity() const {return pid_rsize;}
|
||||
bool isEmpty() const {return (pid_size == 0);}
|
||||
|
||||
T & operator [](size_t index) {return pid_data[pid_start + index];}
|
||||
T & at(size_t index) {return pid_data[pid_start + index];}
|
||||
const T & operator [](size_t index) const {return pid_data[pid_start + index];}
|
||||
const T & at(size_t index) const {return pid_data[pid_start + index];}
|
||||
T & back() {return pid_data[pid_start + pid_size - 1];}
|
||||
const T & back() const {return pid_data[pid_start + pid_size - 1];}
|
||||
T & front() {return pid_data[pid_start];}
|
||||
const T & front() const {return pid_data[pid_start];}
|
||||
bool operator ==(const PIDeque<T> & t) const {if (pid_size != t.pid_size) return false; for (size_t i = 0; i < pid_size; ++i) if (t[i] != (*this)[i]) return false; return true;}
|
||||
bool operator !=(const PIDeque<T> & t) const {if (pid_size != t.pid_size) return true; for (size_t i = 0; i < pid_size; ++i) if (t[i] != (*this)[i]) return true; return false;}
|
||||
bool contains(const T & v) const {for (size_t i = pid_start; i < pid_start + pid_size; ++i) if (v == pid_data[i]) return true; return false;}
|
||||
int etries(const T & v) const {int ec = 0; for (size_t i = pid_start; i < pid_start + pid_size; ++i) if (v == pid_data[i]) ++ec; return ec;}
|
||||
|
||||
T * data(size_t index = 0) {return &(pid_data[pid_start + index]);}
|
||||
const T * data(size_t index = 0) const {return &(pid_data[pid_start + index]);}
|
||||
PIDeque<T> & clear() {resize(0); return *this;}
|
||||
PIDeque<T> & fill(const T & f = T()) {
|
||||
//if (sizeof(T) == 1) memset(pid_data, f, pid_size);
|
||||
deleteT(pid_data + pid_start, pid_size);
|
||||
//zeroRaw(pid_data, pid_size);
|
||||
for (size_t i = pid_start; i < pid_start + pid_size; ++i)
|
||||
elementNew(pid_data + i, f);
|
||||
return *this;
|
||||
}
|
||||
PIDeque<T> & assign(const T & f = T()) {return fill(f);}
|
||||
PIDeque<T> & assign(size_t new_size, const T & f) {resize(new_size); return fill(f);}
|
||||
PIDeque<T> & resize(size_t new_size, const T & f = T()) {
|
||||
if (new_size < pid_size) {
|
||||
deleteT(&(pid_data[new_size + pid_start]), pid_size - new_size);
|
||||
pid_size = new_size;
|
||||
}
|
||||
if (new_size > pid_size) {
|
||||
size_t os = pid_size;
|
||||
alloc(new_size, true);
|
||||
//if (sizeof(T) == 1) memset(&(pid_data[os]), f, ds);
|
||||
//zeroRaw(&(pid_data[os]), new_size - os);
|
||||
for (size_t i = os + pid_start; i < new_size + pid_start; ++i) elementNew(pid_data + i, f);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
PIDeque<T> & reserve(size_t new_size) {if (new_size <= pid_rsize) return *this; size_t os = pid_size; alloc(new_size, true); pid_size = os; return *this;}
|
||||
|
||||
PIDeque<T> & insert(size_t index, const T & v = T()) {
|
||||
bool dir = pid_rsize <= 2 ? true : (index >= pid_rsize / 2 ? true : false);
|
||||
//piCout << "insert" << dir << index << pid_size << pid_rsize << pid_start << "!<";
|
||||
if (dir) {
|
||||
alloc(pid_size + 1, true);
|
||||
if (index < pid_size - 1) {
|
||||
size_t os = pid_size - index - 1;
|
||||
memmove(&(pid_data[index + pid_start + 1]), &(pid_data[index + pid_start]), os * sizeof(T));
|
||||
}
|
||||
} else {
|
||||
pid_start--;
|
||||
alloc(pid_size + 1, false);
|
||||
//piCout << "insert front" << pid_size << pid_rsize << pid_start << "!<";
|
||||
if (index > 0)
|
||||
memmove(&(pid_data[pid_start]), &(pid_data[pid_start + 1]), index * sizeof(T));
|
||||
}
|
||||
//piCout << "insert" << pid_start << index << (pid_start + ssize_t(index)) << pid_size << ">!";
|
||||
elementNew(pid_data + pid_start + index, v);
|
||||
return *this;
|
||||
}
|
||||
PIDeque<T> & insert(size_t index, const PIDeque<T> & other) {
|
||||
if (other.isEmpty()) return *this;
|
||||
bool dir = pid_rsize <= 2 ? true : (index >= pid_rsize / 2 ? true : false);
|
||||
//piCout << "insert" << dir << index << pid_size << pid_rsize << pid_start << "!<";
|
||||
if (dir) {
|
||||
ssize_t os = pid_size - index;
|
||||
alloc(pid_size + other.pid_size, true);
|
||||
if (os > 0)
|
||||
memmove(&(pid_data[index + pid_start + other.pid_size]), &(pid_data[index + pid_start]), os * sizeof(T));
|
||||
} else {
|
||||
pid_start -= other.pid_size;
|
||||
alloc(pid_size + other.pid_size, false);
|
||||
//piCout << "insert front" << pid_size << pid_rsize << pid_start << "!<";
|
||||
if (index > 0)
|
||||
memmove(&(pid_data[pid_start]), &(pid_data[pid_start + other.pid_size]), index * sizeof(T));
|
||||
}
|
||||
//piCout << "insert" << pid_start << index << (pid_start + ssize_t(index)) << pid_size << ">!";
|
||||
newT(pid_data + pid_start + index, other.pid_data + other.pid_start, other.pid_size);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PIDeque<T> & remove(size_t index, size_t count = 1) {
|
||||
if (count == 0) return *this;
|
||||
if (index + count >= pid_size) {
|
||||
resize(index);
|
||||
return *this;
|
||||
}
|
||||
size_t os = pid_size - index - count;
|
||||
deleteT(&(pid_data[index + pid_start]), count);
|
||||
if (os <= index) {
|
||||
//if (true) {
|
||||
if (os > 0) memmove(&(pid_data[index + pid_start]), &(pid_data[index + pid_start + count]), os * sizeof(T));
|
||||
} else {
|
||||
if (index > 0) memmove(&(pid_data[pid_start + count]), &(pid_data[pid_start]), index * sizeof(T));
|
||||
pid_start += count;
|
||||
}
|
||||
pid_size -= count;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(PIDeque<T> & other) {
|
||||
piSwap<T*>(pid_data, other.pid_data);
|
||||
piSwap<size_t>(pid_size, other.pid_size);
|
||||
piSwap<size_t>(pid_rsize, other.pid_rsize);
|
||||
piSwap<size_t>(pid_start, other.pid_start);
|
||||
}
|
||||
|
||||
typedef int (*CompareFunc)(const T * , const T * );
|
||||
static int compare_func(const T * t0, const T * t1) {return (*t0) < (*t1) ? -1 : ((*t0) == (*t1) ? 0 : 1);}
|
||||
PIDeque<T> & sort(CompareFunc compare = compare_func) {qsort(pid_data + pid_start, pid_size, sizeof(T), (int(*)(const void * , const void * ))compare); return *this;}
|
||||
|
||||
PIDeque<T> & enlarge(llong pid_size) {llong ns = size_s() + pid_size; if (ns <= 0) clear(); else resize(size_t(ns)); return *this;}
|
||||
|
||||
PIDeque<T> & removeOne(const T & v) {for (size_t i = 0; i < pid_size; ++i) if (pid_data[i + pid_start] == v) {remove(i); return *this;} return *this;}
|
||||
PIDeque<T> & removeAll(const T & v) {for (llong i = 0; i < pid_size; ++i) if (pid_data[i + pid_start] == v) {remove(i); --i;} return *this;}
|
||||
|
||||
PIDeque<T> & push_back(const T & v) {alloc(pid_size + 1, true); elementNew(pid_data + pid_start + pid_size - 1, v); return *this;}
|
||||
PIDeque<T> & append(const T & v) {return push_back(v);}
|
||||
PIDeque<T> & operator <<(const T & v) {return push_back(v);}
|
||||
PIDeque<T> & operator <<(const PIDeque<T> & t) {
|
||||
size_t ps = pid_size;
|
||||
alloc(pid_size + t.pid_size, true);
|
||||
newT(pid_data + ps + pid_start, t.pid_data + t.pid_start, t.pid_size);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PIDeque<T> & push_front(const T & v) {insert(0, v); return *this;}
|
||||
PIDeque<T> & prepend(const T & v) {return push_front(v);}
|
||||
|
||||
PIDeque<T> & pop_back() {if (pid_size == 0) return *this; resize(pid_size - 1); return *this;}
|
||||
PIDeque<T> & pop_front() {if (pid_size == 0) return *this; remove(0); return *this;}
|
||||
|
||||
T take_back() {T t(back()); pop_back(); return t;}
|
||||
T take_front() {T t(front()); pop_front(); return t;}
|
||||
|
||||
template <typename ST>
|
||||
PIDeque<ST> toType() const {PIDeque<ST> ret(pid_size); for (uint i = 0; i < pid_size; ++i) ret[i] = ST(pid_data[i + pid_start]); return ret;}
|
||||
|
||||
private:
|
||||
void _reset() {pid_size = pid_rsize = pid_start = 0; pid_data = 0;}
|
||||
/*void * qmemmove(void * dst, void * src, size_t size) {
|
||||
if (piAbs<ssize_t>(ssize_t(dst) - ssize_t(src)) >= size)
|
||||
memcpy(dst, src, size);
|
||||
else {
|
||||
char * tb = new char[size];
|
||||
memcpy(tb, src, size);
|
||||
memcpy(dst, tb, size);
|
||||
delete tb;
|
||||
}
|
||||
return dst;
|
||||
}*/
|
||||
inline size_t asize(ssize_t s) {
|
||||
if (s <= 0) return 0;
|
||||
if (pid_rsize + pid_rsize >= size_t(s) && pid_rsize < size_t(s))
|
||||
return pid_rsize + pid_rsize;
|
||||
size_t t = 0, s_ = size_t(s) - 1;
|
||||
while (s_ >> t) ++t;
|
||||
return (1 << t);
|
||||
}
|
||||
inline void newT(T * dst, const T * src, size_t s) {
|
||||
for (size_t i = 0; i < s; ++i)
|
||||
elementNew(dst + i, src[i]);
|
||||
}
|
||||
inline T * newRaw(size_t s) {
|
||||
//cout << std::dec << " ![("<<this<<")newRaw " << s << " elements ... <\n" << endl;
|
||||
//uchar * ret = new uchar[s * sizeof(T)];
|
||||
uchar * ret = (uchar*)(malloc(s * sizeof(T)));//new uchar[];
|
||||
//zeroRaw((T*)ret, s);
|
||||
//cout << std::hex << " > (new 0x" << (llong)ret << ") ok]!" << endl;
|
||||
return (T*)ret;
|
||||
}
|
||||
/*void reallocRawTemp(size_t s) {
|
||||
if (pid_tdata == 0) pid_tdata = (T*)(malloc(s * sizeof(T)));
|
||||
else pid_tdata = (T*)(realloc(pid_tdata, s * sizeof(T)));
|
||||
}*/
|
||||
inline void deleteT(T * d, size_t sz) {
|
||||
//cout << " ~[("<<this<<")deleteT " << std::dec << sz << " elements " << std::hex << "0x" << (llong)d << " ... <\n" << endl;
|
||||
if ((uchar*)d != 0) {
|
||||
for (size_t i = 0; i < sz; ++i)
|
||||
elementDelete(d[i]);
|
||||
//zeroRaw(d, sz);
|
||||
}
|
||||
//cout << " > ok]~" << endl;
|
||||
}
|
||||
inline void deleteRaw(T *& d) {
|
||||
//cout << " ~[("<<this<<")deleteRaw " << std::dec << pid_rsize << " elements " << std::hex << "0x" << (llong)d << " ... <\n" << endl;
|
||||
if ((uchar*)d != 0) free((uchar*)d);
|
||||
d = 0;
|
||||
//cout << " > ok]~" << endl;
|
||||
}
|
||||
void zeroRaw(T * d, size_t s) {
|
||||
//cout << " ~[("<<this<<")zeroRaw " << std::dec << s << " elements " << std::hex << "0x" << (llong)d << " ... <\n" << endl;
|
||||
if ((uchar*)d != 0) memset(d, 0, s*sizeof(T));
|
||||
//cout << " > ok]~" << endl;
|
||||
}
|
||||
inline void elementNew(T * to, const T & from) {new(to)T(from);}
|
||||
inline void elementDelete(T & from) {from.~T();}
|
||||
void dealloc() {deleteRaw(pid_data);}
|
||||
inline void checkMove(bool direction) {
|
||||
if (pid_size >= 4 && pid_size < pid_rsize / 4) {
|
||||
/*if (direction) {
|
||||
if (pid_start >= 4 && pid_start > pid_size + pid_size && pid_start > pid_rsize / 2) {
|
||||
piCout << (int)this << "checkMove" << direction << pid_start << (int)pid_data << pid_rsize << pid_size;
|
||||
piCout << (int)this << "move from" << pid_start << "to" << pid_size << "," << (int)pid_data << pid_rsize << pid_size;
|
||||
memmove(pid_data + pid_size, pid_data + pid_start, pid_size * sizeof(T));
|
||||
pid_start = pid_size;
|
||||
}
|
||||
} else {
|
||||
if (ssize_t(pid_start) < ssize_t(pid_rsize) - pid_size - pid_size && ssize_t(pid_start) < ssize_t(pid_rsize / 2) - pid_size) {
|
||||
piCout << (int)this << "checkMove" << direction << pid_start << (int)pid_data << pid_rsize << pid_size;
|
||||
piCout << (int)this << "move from" << pid_start << "to" << (ssize_t(pid_rsize) - pid_size) << "," << (int)pid_data << pid_rsize << pid_size;
|
||||
memmove(pid_data + ssize_t(pid_rsize) - pid_size - pid_size, pid_data + pid_start, pid_size * sizeof(T));
|
||||
pid_start = ssize_t(pid_rsize) - pid_size - pid_size;
|
||||
}
|
||||
}*/
|
||||
if (pid_start < pid_size + pid_size || pid_start > pid_rsize - pid_size - pid_size) {
|
||||
size_t ns = (pid_rsize - pid_size) / 2;
|
||||
if (pid_start != ns) {
|
||||
memmove(pid_data + ns, pid_data + pid_start, pid_size * sizeof(T));
|
||||
pid_start = ns;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void alloc(size_t new_size, bool direction) { // direction == true -> alloc forward
|
||||
if (direction) {
|
||||
if (pid_start + new_size <= pid_rsize) {
|
||||
pid_size = new_size;
|
||||
checkMove(direction);
|
||||
return;
|
||||
}
|
||||
pid_size = new_size;
|
||||
size_t as = asize(pid_start + new_size);
|
||||
if (as != pid_rsize) {
|
||||
pid_data = (T*)(realloc(pid_data, as*sizeof(T)));
|
||||
pid_rsize = as;
|
||||
}
|
||||
} else {
|
||||
size_t as = asize(piMax<ssize_t>(new_size, pid_rsize) - pid_start);
|
||||
//piCout << "alloc" << new_size << pid_size << pid_rsize << as << pid_start;
|
||||
if (pid_start >= 0 && as <= pid_rsize) {
|
||||
pid_size = new_size;
|
||||
checkMove(direction);
|
||||
return;
|
||||
}
|
||||
size_t os = pid_size;
|
||||
pid_size = new_size;
|
||||
if (as > pid_rsize) {
|
||||
//piCout << "alloc new size" << as;
|
||||
//cout << std::hex << " ![("<<this<<")realloc " << pid_data << " data ... <\n" << endl;
|
||||
T * td = newRaw(as);
|
||||
//piCout << "pid_start" << pid_start << (pid_start + ssize_t(as) - os);
|
||||
ssize_t ost = pid_start, ns = 0;
|
||||
if (ost < 0) {ns -= ost; ost = 0;}
|
||||
pid_start += ssize_t(as) - os;
|
||||
if (os > 0 && pid_data != 0) {
|
||||
memcpy(td + pid_start + ns, pid_data + ost, os * sizeof(T));
|
||||
deleteRaw(pid_data);
|
||||
}
|
||||
pid_data = td;
|
||||
pid_rsize = as;
|
||||
}
|
||||
}
|
||||
//checkMove(direction);
|
||||
//piCout << "alloc new start" << pid_start;
|
||||
}
|
||||
|
||||
T * pid_data;
|
||||
volatile size_t pid_size, pid_rsize;
|
||||
volatile size_t pid_start;
|
||||
};
|
||||
|
||||
#define __PIDEQUE_SIMPLE_TYPE__(T) \
|
||||
template<> inline void PIDeque<T>::newT(T * dst, const T * src, size_t s) {memcpy(dst, src, s * sizeof(T));} \
|
||||
template<> inline void PIDeque<T>::deleteT(T * d, size_t sz) {;} \
|
||||
template<> inline void PIDeque<T>::elementNew(T * to, const T & from) {(*to) = from;} \
|
||||
template<> inline void PIDeque<T>::elementDelete(T & from) {;}
|
||||
|
||||
#else
|
||||
|
||||
|
||||
template<typename Type, typename Allocator = std::allocator<Type> >
|
||||
class PIP_EXPORT PIDeque: public deque<Type, Allocator> {
|
||||
typedef PIDeque<Type, Allocator> _CDeque;
|
||||
typedef deque<Type, Allocator> _stlc;
|
||||
public:
|
||||
PIDeque() {piMonitor.containers++;}
|
||||
PIDeque(const Type & value) {piMonitor.containers++; _stlc::resize(1, value);}
|
||||
PIDeque(const Type & v0, const Type & v1) {piMonitor.containers++; _stlc::push_back(v0); _stlc::push_back(v1);}
|
||||
PIDeque(const Type & v0, const Type & v1, const Type & v2) {piMonitor.containers++; _stlc::push_back(v0); _stlc::push_back(v1); _stlc::push_back(v2);}
|
||||
PIDeque(const Type & v0, const Type & v1, const Type & v2, const Type & v3) {piMonitor.containers++; _stlc::push_back(v0); _stlc::push_back(v1); _stlc::push_back(v2); _stlc::push_back(v3);}
|
||||
~PIDeque() {piMonitor.containers--;}
|
||||
int size_s() const {return static_cast<int>(_stlc::size());}
|
||||
bool isEmpty() const {return _stlc::empty();}
|
||||
bool has(const Type & t) const {for (typename _stlc::const_iterator i = _stlc::begin(); i != _stlc::end(); ++i) if (t == *i) return true; return false;}
|
||||
int etries(const Type & t) const {int ec = 0; for (typename _stlc::const_iterator i = _stlc::begin(); i != _stlc::end(); ++i) if (t == *i) ++ec; return ec;}
|
||||
_CDeque & operator <<(const Type & t) {_CDeque::push_back(t); return *this;}
|
||||
PIDeque<Type> toVector() {PIDeque<Type> v; for (typename _stlc::const_iterator i = _stlc::begin(); i != _stlc::end(); ++i) v << *i; return v;}
|
||||
};
|
||||
|
||||
|
||||
#define __PIDEQUE_SIMPLE_FUNCTIONS__(T)
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
template<typename T>
|
||||
inline std::ostream & operator <<(std::ostream & s, const PIDeque<T> & v) {s << "{"; for (size_t i = 0; i < v.size(); ++i) {s << v[i]; if (i < v.size() - 1) s << ", ";} s << "}"; return s;}
|
||||
|
||||
template<typename T>
|
||||
inline PICout operator <<(PICout s, const PIDeque<T> & v) {s.space(); s.setControl(0, true); s << "{"; for (size_t i = 0; i < v.size(); ++i) {s << v[i]; if (i < v.size() - 1) s << ", ";} s << "}"; s.restoreControl(); return s;}
|
||||
|
||||
|
||||
#endif // PIDEQUE_H
|
||||
453
src/containers/pimap.h
Normal file
453
src/containers/pimap.h
Normal file
@@ -0,0 +1,453 @@
|
||||
/*! \file pimap.h
|
||||
* \brief Associative array with custom types of key and value
|
||||
*
|
||||
* This file declares PIMap
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Dynamic array of any type
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIMAP_H
|
||||
#define PIMAP_H
|
||||
|
||||
#include "pivector.h"
|
||||
#include "pideque.h"
|
||||
|
||||
|
||||
#if !defined(PIP_CONTAINERS_STL) || defined(DOXYGEN)
|
||||
|
||||
template<class T>
|
||||
void piQuickSort(T * a, ssize_t N) {
|
||||
if (N < 1) return;
|
||||
ssize_t i = 0, j = N;
|
||||
T & p(a[N >> 1]);
|
||||
do {
|
||||
while (a[i] < p) i++;
|
||||
while (a[j] > p) j--;
|
||||
if (i <= j) {
|
||||
if (i != j) {
|
||||
//piCout << "swap" << i << j << a[i] << a[j];
|
||||
piSwapBinary<T>(a[i], a[j]);
|
||||
}
|
||||
i++; j--;
|
||||
}
|
||||
} while (i <= j);
|
||||
if (j > 0) piQuickSort(a, j);
|
||||
if (N > i) piQuickSort(a + i, N - i);
|
||||
}
|
||||
|
||||
template <typename Key, typename T>
|
||||
class PIMap {
|
||||
public:
|
||||
PIMap() {;}
|
||||
PIMap(const PIMap<Key, T> & other) {*this = other;}
|
||||
~PIMap() {;}
|
||||
|
||||
PIMap<Key, T> & operator =(const PIMap<Key, T> & other) {
|
||||
if (this == &other) return *this;
|
||||
clear();
|
||||
pim_content = other.pim_content;
|
||||
pim_index = other.pim_index;
|
||||
return *this;
|
||||
}
|
||||
|
||||
typedef T mapped_type;
|
||||
typedef Key key_type;
|
||||
typedef PIPair<Key, T> value_type;
|
||||
|
||||
class iterator {
|
||||
friend class PIMap<Key, T>;
|
||||
private:
|
||||
iterator(const PIMap<Key, T> * v, ssize_t p): parent(v), pos(p) {}
|
||||
const PIMap<Key, T> * parent;
|
||||
ssize_t pos;
|
||||
public:
|
||||
iterator(): parent(0) {}
|
||||
const Key & key() const {return const_cast<PIMap<Key, T> * >(parent)->_key(pos);}
|
||||
T & value() const {return const_cast<PIMap<Key, T> * >(parent)->_value(pos);}
|
||||
void operator ++() {++pos;}
|
||||
void operator ++(int) {++pos;}
|
||||
void operator --() {--pos;}
|
||||
void operator --(int) {--pos;}
|
||||
bool operator ==(const iterator & it) const {return (pos == it.pos);}
|
||||
bool operator !=(const iterator & it) const {return (pos != it.pos);}
|
||||
};
|
||||
|
||||
class reverse_iterator {
|
||||
friend class PIMap<Key, T>;
|
||||
private:
|
||||
reverse_iterator(const PIMap<Key, T> * v, ssize_t p): parent(v), pos(p) {}
|
||||
const PIMap<Key, T> * parent;
|
||||
ssize_t pos;
|
||||
public:
|
||||
reverse_iterator(): parent(0) {}
|
||||
const Key & key() const {return const_cast<PIMap<Key, T> * >(parent)->_key(pos);}
|
||||
T & value() const {return const_cast<PIMap<Key, T> * >(parent)->_value(pos);}
|
||||
void operator ++() {--pos;}
|
||||
void operator ++(int) {--pos;}
|
||||
void operator --() {++pos;}
|
||||
void operator --(int) {++pos;}
|
||||
bool operator ==(const reverse_iterator & it) const {return (pos == it.pos);}
|
||||
bool operator !=(const reverse_iterator & it) const {return (pos != it.pos);}
|
||||
};
|
||||
|
||||
class const_iterator {
|
||||
friend class PIMap<Key, T>;
|
||||
private:
|
||||
const_iterator(const PIMap<Key, T> * v, ssize_t p): parent(v), pos(p) {}
|
||||
const PIMap<Key, T> * parent;
|
||||
ssize_t pos;
|
||||
public:
|
||||
const_iterator(): parent(0) {}
|
||||
const PIMap<Key, T>::value_type operator *() const {return parent->_pair(pos);}
|
||||
const PIMap<Key, T>::value_type* operator ->() const {cval = parent->_pair(pos); return &cval;}
|
||||
void operator ++() {++pos;}
|
||||
void operator ++(int) {++pos;}
|
||||
void operator --() {--pos;}
|
||||
void operator --(int) {--pos;}
|
||||
bool operator ==(const const_iterator & it) const {return (pos == it.pos);}
|
||||
bool operator !=(const const_iterator & it) const {return (pos != it.pos);}
|
||||
mutable value_type cval;
|
||||
};
|
||||
|
||||
class const_reverse_iterator {
|
||||
friend class PIMap<Key, T>;
|
||||
private:
|
||||
const_reverse_iterator(const PIMap<Key, T> * v, ssize_t p): parent(v), pos(p) {}
|
||||
const PIMap<Key, T> * parent;
|
||||
ssize_t pos;
|
||||
public:
|
||||
const_reverse_iterator(): parent(0) {}
|
||||
const PIMap<Key, T>::value_type operator *() const {return parent->_pair(pos);}
|
||||
const PIMap<Key, T>::value_type* operator ->() const {cval = parent->_pair(pos); return &cval;}
|
||||
void operator ++() {--pos;}
|
||||
void operator ++(int) {--pos;}
|
||||
void operator --() {++pos;}
|
||||
void operator --(int) {++pos;}
|
||||
bool operator ==(const const_reverse_iterator & it) const {return (pos == it.pos);}
|
||||
bool operator !=(const const_reverse_iterator & it) const {return (pos != it.pos);}
|
||||
mutable value_type cval;
|
||||
};
|
||||
|
||||
iterator begin() {return iterator(this, 0);}
|
||||
iterator end() {return iterator(this, size());}
|
||||
const_iterator begin() const {return const_iterator(this, 0);}
|
||||
const_iterator end() const {return const_iterator(this, size());}
|
||||
reverse_iterator rbegin() {return reverse_iterator(this, size() - 1);}
|
||||
reverse_iterator rend() {return reverse_iterator(this, -1);}
|
||||
const_reverse_iterator rbegin() const {return const_reverse_iterator(this, size() - 1);}
|
||||
const_reverse_iterator rend() const {return const_reverse_iterator(this, -1);}
|
||||
|
||||
size_t size() const {return pim_content.size();}
|
||||
int size_s() const {return pim_content.size_s();}
|
||||
size_t length() const {return pim_content.size();}
|
||||
bool isEmpty() const {return (pim_content.size() == 0);}
|
||||
|
||||
T & operator [](const Key & key) {
|
||||
bool f(false);
|
||||
ssize_t i = _find(key, f);
|
||||
if (f) return pim_content[pim_index[i].index];
|
||||
pim_content.push_back(T());
|
||||
pim_index.insert(i, MapIndex(key, pim_content.size() - 1));
|
||||
return pim_content.back();
|
||||
}
|
||||
const T operator [](const Key & key) const {bool f(false); ssize_t i = _find(key, f); if (f) return pim_content[pim_index[i].index]; return T();}
|
||||
T & at(const Key & key) {return (*this)[key];}
|
||||
const T at(const Key & key) const {return (*this)[key];}
|
||||
|
||||
PIMap<Key, T> & operator <<(const PIMap<Key, T> & other) {
|
||||
if (other.isEmpty()) return *this;
|
||||
if (other.size() == 1) {insert(other.pim_index[0].key, other.pim_content[0]); return *this;}
|
||||
if (other.size() == 2) {insert(other.pim_index[0].key, other.pim_content[0]); insert(other.pim_index[1].key, other.pim_content[1]); return *this;}
|
||||
pim_content << other.pim_content;
|
||||
size_t si = pim_index.size();
|
||||
for (int i = 0; i < other.pim_index.size_s(); ++i)
|
||||
pim_index << MapIndex(other.pim_index[i].key, other.pim_index[i].index + si);
|
||||
_sort();
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator ==(const PIMap<Key, T> & t) const {return (pim_content == t.pim_content && pim_index == t.pim_index);}
|
||||
bool operator !=(const PIMap<Key, T> & t) const {return (pim_content != t.pim_content || pim_index != t.pim_index);}
|
||||
bool contains(const Key & key) const {bool f(false); _find(key, f); return f;}
|
||||
//int etries(const T & v) const {int ec = 0; for (size_t i = 0; i < pim_size; ++i) if (v == pim_data[i]) ++ec; return ec;}
|
||||
|
||||
PIMap<Key, T> & reserve(size_t new_size) {pim_content.reserve(new_size); pim_index.reserve(new_size); return *this;}
|
||||
|
||||
//PIMap<Key, T> & removeAll(const T & v) {for (llong i = 0; i < pim_size; ++i) if (pim_data[i] == v) {remove(i); --i;} return *this;}
|
||||
PIMap<Key, T> & removeOne(const Key & key) {bool f(false); ssize_t i = _find(key, f); if (f) _remove(i); return *this;}
|
||||
PIMap<Key, T> & remove(const Key & key) {return removeOne(key);}
|
||||
PIMap<Key, T> & erase(const Key & key) {return removeOne(key);}
|
||||
PIMap<Key, T> & clear() {pim_content.clear(); pim_index.clear(); return *this;}
|
||||
|
||||
void swap(PIMap<Key, T> & other) {
|
||||
piSwapBinary<PIVector<T> >(pim_content, other.pim_content);
|
||||
piSwapBinary<PIVector<MapIndex> >(pim_index, other.pim_index);
|
||||
}
|
||||
|
||||
PIMap<Key, T> & insert(const Key & key, const T & value) {
|
||||
//MapIndex * i = _find(key);
|
||||
bool f(false);
|
||||
ssize_t i = _find(key, f);
|
||||
//piCout << "insert key=" << key << "found=" << f << "index=" << i << "value=" << value;
|
||||
if (f) {
|
||||
pim_content[pim_index[i].index] = value;
|
||||
} else {
|
||||
pim_content.push_back(value);
|
||||
pim_index.insert(i, MapIndex(key, pim_content.size() - 1));
|
||||
//_sort();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
//const T value(const Key & key, const T & default_ = T()) const {MapIndex * i = _find(key); if (i == 0) return default_; return pim_content[i->index];}
|
||||
const T value(const Key & key, const T & default_ = T()) const {bool f(false); ssize_t i = _find(key, f); if (!f) return default_; return pim_content[pim_index[i].index];}
|
||||
PIVector<T> values() const {return pim_content;}
|
||||
Key key(const T & value_, const Key & default_ = Key()) const {for (int i = 0; i < pim_index.size_s(); ++i) if (pim_content[pim_index[i].index] == value_) return pim_index[i].key; return default_;}
|
||||
PIVector<Key> keys() const {
|
||||
PIVector<Key> ret;
|
||||
for (int i = 0; i < pim_index.size_s(); ++i)
|
||||
ret << pim_index[i].key;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dump() {
|
||||
piCout << "PIMap" << size() << "entries" << NewLine << "content:";
|
||||
for (size_t i = 0; i < pim_content.size(); ++i)
|
||||
piCout << Tab << i << ":" << pim_content[i];
|
||||
piCout << "index:";
|
||||
for (size_t i = 0; i < pim_index.size(); ++i)
|
||||
piCout << Tab << i << ":" << pim_index[i].key << "->" << pim_index[i].index;
|
||||
}
|
||||
|
||||
protected:
|
||||
struct MapIndex {
|
||||
MapIndex(Key k = Key(), size_t i = 0): key(k), index(i) {;}
|
||||
Key key;
|
||||
size_t index;
|
||||
bool operator ==(const MapIndex & s) const {return key == s.key;}
|
||||
bool operator !=(const MapIndex & s) const {return key != s.key;}
|
||||
bool operator <(const MapIndex & s) const {return key < s.key;}
|
||||
bool operator >(const MapIndex & s) const {return key > s.key;}
|
||||
};
|
||||
|
||||
ssize_t binarySearch(ssize_t first, ssize_t last, const Key & key, bool & found) const {
|
||||
ssize_t mid;
|
||||
while (first <= last) {
|
||||
mid = (first + last) / 2;
|
||||
if (key > pim_index[mid].key) first = mid + 1;
|
||||
else if (key < pim_index[mid].key) last = mid - 1;
|
||||
else {found = true; return mid;}
|
||||
}
|
||||
found = false;
|
||||
return first;
|
||||
}
|
||||
void _sort() {piQuickSort<MapIndex>(pim_index.data(), pim_index.size_s() - 1);}
|
||||
ssize_t _find(const Key & k, bool & found) const {
|
||||
/*for (size_t i = 0; i < pim_index.size(); ++i)
|
||||
if (pim_index[i].key == k) {
|
||||
return (MapIndex * )&(pim_index[i]);
|
||||
}
|
||||
return 0;*/
|
||||
//piCout << "find for" << k << pim_index.size_s();
|
||||
if (pim_index.isEmpty()) {
|
||||
found = false;
|
||||
return 0;
|
||||
}
|
||||
//piCout << k << ret << found;
|
||||
return binarySearch(0, pim_index.size_s() - 1, k, found);
|
||||
}
|
||||
void _remove(ssize_t index) {
|
||||
//if (index >= pim_index.size()) return;
|
||||
size_t ci = pim_index[index].index, bi = pim_index.size() - 1;
|
||||
pim_index.remove(index);
|
||||
for (size_t i = 0; i < pim_index.size(); ++i)
|
||||
if (pim_index[i].index == bi) {
|
||||
pim_index[i].index = ci;
|
||||
break;
|
||||
}
|
||||
piSwapBinary<T>(pim_content[ci], pim_content.back());
|
||||
pim_content.resize(pim_index.size());
|
||||
}
|
||||
const value_type _pair(ssize_t index) const {
|
||||
if (index < 0 || index >= pim_index.size_s())
|
||||
return value_type();
|
||||
//piCout << "_pair" << index << pim_index[index].index;
|
||||
return value_type(pim_index[index].key, pim_content[pim_index[index].index]);
|
||||
}
|
||||
Key & _key(ssize_t index) {return pim_index[index].key;}
|
||||
T & _value(ssize_t index) {return pim_content[pim_index[index].index];}
|
||||
|
||||
PIVector<T> pim_content;
|
||||
PIDeque<MapIndex> pim_index;
|
||||
};
|
||||
//template <typename Key, typename T> bool operator <(const typename PIMap<Key, T>::MapIndex & f, const typename PIMap<Key, T>::MapIndex & s) {return f.key < s.key;}
|
||||
//template <typename Key, typename T> bool operator >(const typename PIMap<Key, T>::MapIndex & f, const typename PIMap<Key, T>::MapIndex & s) {return f.key > s.key;}
|
||||
|
||||
|
||||
/*#define __PIMAP_SIMPLE_FUNCTIONS__(T)
|
||||
template<> inline PIMap<Key, T>::~PIMap() {dealloc(); _reset();} \
|
||||
template<> inline PIMap<Key, T> & PIMap<Key, T>::push_back(const T & v) {alloc(pim_size + 1); pim_data[pim_size - 1] = v; return *this;} \
|
||||
template<> inline PIMap<Key, T> & PIMap<Key, T>::fill(const T & f) { \
|
||||
for (size_t i = 0; i < pim_size; ++i) \
|
||||
pim_data[i] = f; \
|
||||
return *this; \
|
||||
} \
|
||||
template<> inline PIMap<Key, T> & PIMap<Key, T>::resize(size_t new_size, const T & f) { \
|
||||
if (new_size < pim_size) \
|
||||
pim_size = new_size; \
|
||||
if (new_size > pim_size) { \
|
||||
size_t os = pim_size; \
|
||||
alloc(new_size); \
|
||||
for (size_t i = os; i < new_size; ++i) pim_data[i] = f; \
|
||||
} \
|
||||
return *this; \
|
||||
} \
|
||||
template<> inline PIMap<Key, T> & PIMap<Key, T>::insert(size_t index, const T & v) { \
|
||||
alloc(pim_size + 1); \
|
||||
if (index < pim_size - 1) { \
|
||||
size_t os = pim_size - index - 1; \
|
||||
memmove(&(pim_data[index + 1]), &(pim_data[index]), os * sizeof(T)); \
|
||||
} \
|
||||
pim_data[index] = v; \
|
||||
return *this; \
|
||||
} \
|
||||
template<> inline PIMap<Key, T> & PIMap<Key, T>::remove(size_t index, size_t count) { \
|
||||
if (index + count >= pim_size) { \
|
||||
resize(index); \
|
||||
return *this; \
|
||||
} \
|
||||
size_t os = pim_size - index - count; \
|
||||
memmove(&(pim_data[index]), &(pim_data[index + count]), os * sizeof(T)); \
|
||||
pim_size -= count; \
|
||||
return *this; \
|
||||
}
|
||||
|
||||
__PIMAP_SIMPLE_FUNCTIONS__(char)
|
||||
__PIMAP_SIMPLE_FUNCTIONS__(uchar)
|
||||
__PIMAP_SIMPLE_FUNCTIONS__(short)
|
||||
__PIMAP_SIMPLE_FUNCTIONS__(ushort)
|
||||
__PIMAP_SIMPLE_FUNCTIONS__(int)
|
||||
__PIMAP_SIMPLE_FUNCTIONS__(uint)
|
||||
__PIMAP_SIMPLE_FUNCTIONS__(long)
|
||||
__PIMAP_SIMPLE_FUNCTIONS__(ulong)
|
||||
__PIMAP_SIMPLE_FUNCTIONS__(llong)
|
||||
__PIMAP_SIMPLE_FUNCTIONS__(ullong)
|
||||
__PIMAP_SIMPLE_FUNCTIONS__(float)
|
||||
__PIMAP_SIMPLE_FUNCTIONS__(double)
|
||||
__PIMAP_SIMPLE_FUNCTIONS__(ldouble)*/
|
||||
|
||||
#else
|
||||
|
||||
|
||||
template<typename Key, typename Type>
|
||||
class PIP_EXPORT PIMap: public map<Key, Type> {
|
||||
typedef PIMap<Key, Type> _CMap;
|
||||
typedef map<Key, Type> _stlc;
|
||||
typedef std::pair<Key, Type> _stlpair;
|
||||
public:
|
||||
PIMap() {;}
|
||||
PIMap(const Key & key_, const Type & value_) {insert(key_, value_);}
|
||||
bool isEmpty() const {return _stlc::empty();}
|
||||
bool contains(const Key & key_) const {return _stlc::count(key_) > 0;}
|
||||
int size_s() const {return static_cast<int>(_stlc::size());}
|
||||
_CMap & insert(const Key & key_, const Type & value_) {_stlc::insert(_stlpair(key_, value_)); return *this;}
|
||||
_CMap & insert(PIPair<Key, Type> entry_) {_stlc::insert(_stlpair(entry_.first, entry_.second)); return *this;}
|
||||
Key key(Type value_, const Key & default_ = Key()) const {for (typename _stlc::const_iterator i = _stlc::begin(); i != _stlc::end(); i++) if (i->second == value_) return i->first; return default_;}
|
||||
PIVector<Key> keys() const {
|
||||
PIVector<Key> ret;
|
||||
for (typename _stlc::const_iterator i = _stlc::begin(); i != _stlc::end(); i++)
|
||||
ret << i->first;
|
||||
return ret;
|
||||
}
|
||||
Type & at(const Key & key_) {return _stlc::find(key_)->second;}
|
||||
Type value(const Key & key_) const {typename _stlc::const_iterator it = _stlc::find(key_); if (it != _stlc::end()) return it->second; return Type();}
|
||||
};
|
||||
|
||||
|
||||
template<typename Key, typename Type>
|
||||
class PIP_EXPORT PIMultiMap: public multimap<Key, Type> {
|
||||
typedef PIMultiMap<Key, Type> _CMultiMap;
|
||||
typedef multimap<Key, Type> _stlc;
|
||||
typedef std::pair<Key, Type> _stlpair;
|
||||
public:
|
||||
PIMultiMap() {;}
|
||||
PIMultiMap(const Key & key_, const Type & value_) {insert(key_, value_);}
|
||||
_CMultiMap & insert(const Key & key_, const Type & value_) {_stlc::insert(_stlpair(key_, value_)); return *this;}
|
||||
_CMultiMap & insert(PIPair<Key, Type> entry_) {_stlc::insert(_stlpair(entry_.first, entry_.second)); return *this;}
|
||||
bool isEmpty() const {return _stlc::empty();}
|
||||
bool contains(const Key & key_) const {return _stlc::count(key_) > 0;}
|
||||
Key key(Type value_, const Key & default_ = Key()) const {for (typename _stlc::const_iterator i = _stlc::begin(); i != _stlc::end(); i++) if (i->second == value_) return i->first; return default_;}
|
||||
PIVector<Key> keys(Type value_) const {
|
||||
PIVector<Key> ret;
|
||||
for (typename _stlc::const_iterator i = _stlc::begin(); i != _stlc::end(); i++)
|
||||
if (i->second == value_)
|
||||
ret << i->first;
|
||||
return ret;
|
||||
}
|
||||
Type & value(const Key & key_) {typename _stlc::iterator i = _stlc::find(key_); if (i == _stlc::end()) return Type(); return i->second;}
|
||||
Type value(const Key & key_) const {typename _stlc::const_iterator i = _stlc::find(key_); if (i == _stlc::end()) return Type(); return i->second;}
|
||||
PIVector<Type> values(const Key & key_) const {
|
||||
std::pair<typename _stlc::const_iterator, typename _stlc::const_iterator> range = _stlc::equal_range(key_);
|
||||
PIVector<Type> ret;
|
||||
for (typename _stlc::const_iterator i = range.first; i != range.second; ++i)
|
||||
ret << i->second;
|
||||
return ret;
|
||||
}
|
||||
Type & operator [](const Key & key_) {if (!contains(key_)) return _stlc::insert(_stlpair(key_, Type()))->second; return _stlc::find(key_)->second;}
|
||||
Type operator [](const Key & key_) const {return _stlc::find(key_)->second;}
|
||||
};
|
||||
|
||||
#define __PIMAP_SIMPLE_FUNCTIONS__(T)
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
template<typename Key, typename Type>
|
||||
inline std::ostream & operator <<(std::ostream & s, const PIMap<Key, Type> & v) {
|
||||
s << "{";
|
||||
bool first = true;
|
||||
for (typename PIMap<Key, Type>::const_iterator i = v.begin(); i != v.end(); i++) {
|
||||
if (!first)
|
||||
s << ", ";
|
||||
first = false;
|
||||
s << i->first << ": " << i->second;
|
||||
}
|
||||
s << "}";
|
||||
return s;
|
||||
}
|
||||
|
||||
template<typename Key, typename Type>
|
||||
inline PICout operator <<(PICout s, const PIMap<Key, Type> & v) {
|
||||
s.space();
|
||||
s.setControl(0, true);
|
||||
s << "{";
|
||||
bool first = true;
|
||||
for (typename PIMap<Key, Type>::const_iterator i = v.begin(); i != v.end(); i++) {
|
||||
if (!first)
|
||||
s << ", ";
|
||||
first = false;
|
||||
s << i->first << ": " << i->second;
|
||||
}
|
||||
s << "}";
|
||||
s.restoreControl();
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
#endif // PIMAP_H
|
||||
41
src/containers/piqueue.h
Executable file
41
src/containers/piqueue.h
Executable file
@@ -0,0 +1,41 @@
|
||||
/*! \file picontainers.h
|
||||
* \brief Queue container
|
||||
*
|
||||
* This file declare PIQueue
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Queue container
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIQUEUE_H
|
||||
#define PIQUEUE_H
|
||||
|
||||
#include "pivector.h"
|
||||
|
||||
template<typename T>
|
||||
class PIP_EXPORT PIQueue: public PIVector<T> {
|
||||
public:
|
||||
PIQueue() {;}
|
||||
PIVector<T> & enqueue(const T & v) {PIVector<T>::push_front(v); return *this;}
|
||||
T dequeue() {return PIVector<T>::take_back();}
|
||||
T & head() {return PIVector<T>::back();}
|
||||
const T & head() const {return PIVector<T>::back();}
|
||||
PIVector<T> toVector() {PIVector<T> v(PIVector<T>::size()); for (uint i = 0; i < PIVector<T>::size(); ++i) v[i] = PIVector<T>::at(i); return v;}
|
||||
};
|
||||
|
||||
#endif // PIQUEUE_H
|
||||
72
src/containers/piset.h
Normal file
72
src/containers/piset.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/*! \file piset.h
|
||||
* \brief Set container
|
||||
*
|
||||
* This file declare PISet
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Set container
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PISET_H
|
||||
#define PISET_H
|
||||
|
||||
#include "pimap.h"
|
||||
|
||||
/*! \brief Set of any type
|
||||
* \details This class used to store collection of unique elements
|
||||
* of any type. You can only add values to set with \a operator<< or
|
||||
* with function \a insert(). You can discover if value already in
|
||||
* set with \a operator[] or with function \a find(). These function
|
||||
* has logarithmic complexity.
|
||||
*/
|
||||
template <typename T>
|
||||
class PIP_EXPORT PISet: public PIMap<T, uchar> {
|
||||
typedef PIMap<T, uchar> _CSet;
|
||||
public:
|
||||
|
||||
//! Contructs an empty set
|
||||
PISet() {}
|
||||
|
||||
//! Contructs set with one element "value"
|
||||
PISet(const T & value) {_CSet::insert(value, 0);}
|
||||
|
||||
//! Contructs set with elements "v0" and "v1"
|
||||
PISet(const T & v0, const T & v1) {_CSet::insert(v0, 0); _CSet::insert(v1, 0);}
|
||||
|
||||
//! Contructs set with elements "v0", "v1" and "v2"
|
||||
PISet(const T & v0, const T & v1, const T & v2) {_CSet::insert(v0, 0); _CSet::insert(v1, 0); _CSet::insert(v2, 0);}
|
||||
|
||||
//! Contructs set with elements "v0", "v1", "v2" and "v3"
|
||||
PISet(const T & v0, const T & v1, const T & v2, const T & v3) {_CSet::insert(v0, 0); _CSet::insert(v1, 0); _CSet::insert(v2, 0); _CSet::insert(v3, 0);}
|
||||
|
||||
typedef T key_type;
|
||||
|
||||
PISet<T> & operator <<(const T & t) {_CSet::insert(t, 0); return *this;}
|
||||
PISet<T> & operator <<(const PISet<T> & other) {(*(_CSet*)this) << *((_CSet*)&other); return *this;}
|
||||
|
||||
//! Returns if element "t" exists in this set
|
||||
bool operator [](const T & t) const {return _CSet::contains(t);}
|
||||
|
||||
//! Returns if element "t" exists in this set
|
||||
PISet<T> & remove(const T & t) {_CSet::remove(t); return *this;}
|
||||
|
||||
//! Returns content of set as PIVector
|
||||
PIVector<T> toVector() const {PIVector<T> ret; for (typename _CSet::const_iterator i = _CSet::begin(); i != _CSet::end(); ++i) ret << (*i).first; return ret;}
|
||||
};
|
||||
|
||||
#endif // PISET_H
|
||||
41
src/containers/pistack.h
Executable file
41
src/containers/pistack.h
Executable file
@@ -0,0 +1,41 @@
|
||||
/*! \file pistack.h
|
||||
* \brief Stack container
|
||||
*
|
||||
* This file declare PIStack
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Stack container
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PISTACK_H
|
||||
#define PISTACK_H
|
||||
|
||||
#include "pivector.h"
|
||||
|
||||
template<typename T>
|
||||
class PIP_EXPORT PIStack: public PIVector<T> {
|
||||
public:
|
||||
PIStack() {;}
|
||||
PIVector<T> & push(const T & v) {PIVector<T>::push_back(v); return *this;}
|
||||
T pop() {return PIVector<T>::take_back();}
|
||||
T & top() {return PIVector<T>::back();}
|
||||
const T & top() const {return PIVector<T>::back();}
|
||||
PIVector<T> toVector() {PIVector<T> v(PIVector<T>::size()); for (uint i = 0; i < PIVector<T>::size(); ++i) v[i] = PIVector<T>::at(i); return v;}
|
||||
};
|
||||
|
||||
#endif // PISTACK_H
|
||||
530
src/containers/pivector.h
Executable file
530
src/containers/pivector.h
Executable file
@@ -0,0 +1,530 @@
|
||||
/*! \file pivector.h
|
||||
* \brief Dynamic array of any type
|
||||
*
|
||||
* This file declares PIVector
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Dynamic array of any type
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIVECTOR_H
|
||||
#define PIVECTOR_H
|
||||
|
||||
#include "piincludes.h"
|
||||
|
||||
|
||||
#if !defined(PIP_CONTAINERS_STL) || defined(DOXYGEN)
|
||||
|
||||
|
||||
template <typename T>
|
||||
class PIVector {
|
||||
public:
|
||||
PIVector(): piv_data(0), piv_size(0), piv_rsize(0) {
|
||||
//printf("new vector 1 %p (%s) ... !{\n", this, typeid(T).name());
|
||||
//printf("(s=%d, d=%p) }!\n", int(piv_size), piv_data);
|
||||
}
|
||||
PIVector(const T * data, size_t size): piv_data(0), piv_size(0), piv_rsize(0) {
|
||||
//printf("new vector 2 %p (%s) ... !{\n", this, typeid(T).name());
|
||||
alloc(size);
|
||||
newT(piv_data, data, piv_size);
|
||||
//printf("(s=%d, d=%p) }!\n", int(pid_size), pid_data);
|
||||
}
|
||||
PIVector(const PIVector<T> & other): piv_data(0), piv_size(0), piv_rsize(0) {
|
||||
//printf("new vector 2 %p (%s) ... !{\n", this, typeid(T).name());
|
||||
alloc(other.piv_size);
|
||||
newT(piv_data, other.piv_data, piv_size);
|
||||
//printf("(s=%d, d=%p) }!\n", int(piv_size), piv_data);
|
||||
}
|
||||
PIVector(size_t piv_size, const T & f = T()): piv_data(0), piv_size(0), piv_rsize(0) {
|
||||
//printf("new vector 3 %p (%s) ... !{\n", this, typeid(T).name());
|
||||
resize(piv_size, f);
|
||||
//printf("(s=%d, d=%p) }!\n", int(piv_size), piv_data);
|
||||
}
|
||||
~PIVector() {
|
||||
//printf("delete vector %p (%s) (s=%d, d=%p) ... ~{\n", this, typeid(T).name(), int(piv_size), piv_data);
|
||||
deleteT(piv_data, piv_size);
|
||||
dealloc();
|
||||
//deleteRaw(piv_tdata);
|
||||
_reset();
|
||||
//printf("}~\n");
|
||||
}
|
||||
|
||||
PIVector<T> & operator =(const PIVector<T> & other) {
|
||||
if (this == &other) return *this;
|
||||
bool tj, oj;
|
||||
tj = (piv_size != 0 && piv_data == 0);// || (piv_size == 0 && piv_data != 0);
|
||||
oj = (other.piv_size != 0 && other.piv_data == 0);// || (other.piv_size == 0 && other.piv_data != 0);
|
||||
//printf("operator= (%p = %p) (s=%d, d=%p, o.s=%d, o.d=%p) (%d, %d) ... o[\n", this, &other, int(piv_size), piv_data, int(other.piv_size), other.piv_data, int(tj), int(oj));
|
||||
if (tj) {
|
||||
printf("JUNK this\n");
|
||||
_reset();
|
||||
} else {
|
||||
clear();
|
||||
}
|
||||
/*if (piv_size == other.piv_size) {
|
||||
for (size_t i = 0; i < piv_size; ++i)
|
||||
piv_data[i] = other.piv_data[i];
|
||||
return *this;
|
||||
}*/
|
||||
if (!oj) {
|
||||
deleteT(piv_data, piv_size);
|
||||
alloc(other.piv_size);
|
||||
//zeroRaw(piv_data, piv_size);
|
||||
for (size_t i = 0; i < piv_size; ++i)
|
||||
elementNew(piv_data + i, other.piv_data[i]); //piv_data[i] = other.piv_data[i];
|
||||
} else {
|
||||
printf("JUNK other\n");
|
||||
}
|
||||
//printf("o]\n");
|
||||
return *this;
|
||||
}
|
||||
|
||||
typedef T value_type;
|
||||
|
||||
class iterator {
|
||||
friend class PIVector<T>;
|
||||
private:
|
||||
iterator(PIVector<T> * v, size_t p): parent(v), pos(p) {}
|
||||
PIVector<T> * parent;
|
||||
size_t pos;
|
||||
public:
|
||||
iterator(): parent(0) {}
|
||||
T & operator *() {return (*parent)[pos];}
|
||||
const T & operator *() const {return (*parent)[pos];}
|
||||
void operator ++() {++pos;}
|
||||
void operator ++(int) {++pos;}
|
||||
void operator --() {--pos;}
|
||||
void operator --(int) {--pos;}
|
||||
bool operator ==(const iterator & it) const {return (pos == it.pos);}
|
||||
bool operator !=(const iterator & it) const {return (pos != it.pos);}
|
||||
};
|
||||
|
||||
class const_iterator {
|
||||
friend class PIVector<T>;
|
||||
private:
|
||||
const_iterator(const PIVector<T> * v, size_t p): parent(v), pos(p) {}
|
||||
const PIVector<T> * parent;
|
||||
size_t pos;
|
||||
public:
|
||||
const_iterator(): parent(0) {}
|
||||
//T & operator *() {return (*parent)[pos];}
|
||||
const T & operator *() const {return (*parent)[pos];}
|
||||
void operator ++() {++pos;}
|
||||
void operator ++(int) {++pos;}
|
||||
void operator --() {--pos;}
|
||||
void operator --(int) {--pos;}
|
||||
bool operator ==(const const_iterator & it) const {return (pos == it.pos);}
|
||||
bool operator !=(const const_iterator & it) const {return (pos != it.pos);}
|
||||
};
|
||||
|
||||
class reverse_iterator {
|
||||
friend class PIVector<T>;
|
||||
private:
|
||||
reverse_iterator(PIVector<T> * v, size_t p): parent(v), pos(p) {}
|
||||
PIVector<T> * parent;
|
||||
size_t pos;
|
||||
public:
|
||||
reverse_iterator(): parent(0) {}
|
||||
T & operator *() {return (*parent)[pos];}
|
||||
const T & operator *() const {return (*parent)[pos];}
|
||||
void operator ++() {--pos;}
|
||||
void operator ++(int) {--pos;}
|
||||
void operator --() {++pos;}
|
||||
void operator --(int) {++pos;}
|
||||
bool operator ==(const reverse_iterator & it) const {return (pos == it.pos);}
|
||||
bool operator !=(const reverse_iterator & it) const {return (pos != it.pos);}
|
||||
};
|
||||
|
||||
class const_reverse_iterator {
|
||||
friend class PIVector<T>;
|
||||
private:
|
||||
const_reverse_iterator(const PIVector<T> * v, size_t p): parent(v), pos(p) {}
|
||||
const PIVector<T> * parent;
|
||||
size_t pos;
|
||||
public:
|
||||
const_reverse_iterator(): parent(0) {}
|
||||
//T & operator *() {return (*parent)[pos];}
|
||||
const T & operator *() const {return (*parent)[pos];}
|
||||
void operator ++() {--pos;}
|
||||
void operator ++(int) {--pos;}
|
||||
void operator --() {++pos;}
|
||||
void operator --(int) {++pos;}
|
||||
bool operator ==(const const_reverse_iterator & it) const {return (pos == it.pos);}
|
||||
bool operator !=(const const_reverse_iterator & it) const {return (pos != it.pos);}
|
||||
};
|
||||
|
||||
iterator begin() {return iterator(this, 0);}
|
||||
iterator end() {return iterator(this, piv_size);}
|
||||
const_iterator begin() const {return const_iterator(this, 0);}
|
||||
const_iterator end() const {return const_iterator(this, piv_size);}
|
||||
reverse_iterator rbegin() {return reverse_iterator(this, piv_size - 1);}
|
||||
reverse_iterator rend() {return reverse_iterator(this, -1);}
|
||||
const_reverse_iterator rbegin() const {return const_reverse_iterator(this, piv_size - 1);}
|
||||
const_reverse_iterator rend() const {return const_reverse_iterator(this, -1);}
|
||||
|
||||
size_t size() const {return piv_size;}
|
||||
ssize_t size_s() const {return piv_size;}
|
||||
size_t length() const {return piv_size;}
|
||||
size_t capacity() const {return piv_rsize;}
|
||||
bool isEmpty() const {return (piv_size == 0);}
|
||||
|
||||
T & operator [](size_t index) {return piv_data[index];}
|
||||
T & at(size_t index) {return piv_data[index];}
|
||||
const T & operator [](size_t index) const {return piv_data[index];}
|
||||
const T & at(size_t index) const {return piv_data[index];}
|
||||
T & back() {return piv_data[piv_size - 1];}
|
||||
const T & back() const {return piv_data[piv_size - 1];}
|
||||
T & front() {return piv_data[0];}
|
||||
const T & front() const {return piv_data[0];}
|
||||
bool operator ==(const PIVector<T> & t) const {if (piv_size != t.piv_size) return false; for (size_t i = 0; i < piv_size; ++i) if (t[i] != piv_data[i]) return false; return true;}
|
||||
bool operator !=(const PIVector<T> & t) const {if (piv_size != t.piv_size) return true; for (size_t i = 0; i < piv_size; ++i) if (t[i] != piv_data[i]) return true; return false;}
|
||||
bool contains(const T & v) const {for (size_t i = 0; i < piv_size; ++i) if (v == piv_data[i]) return true; return false;}
|
||||
int etries(const T & v) const {int ec = 0; for (size_t i = 0; i < piv_size; ++i) if (v == piv_data[i]) ++ec; return ec;}
|
||||
|
||||
T * data(size_t index = 0) {return &(piv_data[index]);}
|
||||
const T * data(size_t index = 0) const {return &(piv_data[index]);}
|
||||
PIVector<T> & clear() {resize(0); return *this;}
|
||||
PIVector<T> & fill(const T & f = T()) {
|
||||
//if (sizeof(T) == 1) memset(piv_data, f, piv_size);
|
||||
deleteT(piv_data, piv_size);
|
||||
//zeroRaw(piv_data, piv_size);
|
||||
for (size_t i = 0; i < piv_size; ++i)
|
||||
elementNew(piv_data + i, f);
|
||||
return *this;
|
||||
}
|
||||
PIVector<T> & assign(const T & f = T()) {return fill(f);}
|
||||
PIVector<T> & assign(size_t new_size, const T & f) {resize(new_size); return fill(f);}
|
||||
PIVector<T> & resize(size_t new_size, const T & f = T()) {
|
||||
if (new_size < piv_size) {
|
||||
T * de = &(piv_data[new_size]);
|
||||
deleteT(de, piv_size - new_size);
|
||||
piv_size = new_size;
|
||||
}
|
||||
if (new_size > piv_size) {
|
||||
size_t os = piv_size;
|
||||
alloc(new_size);
|
||||
//if (sizeof(T) == 1) memset(&(piv_data[os]), f, ds);
|
||||
//zeroRaw(&(piv_data[os]), new_size - os);
|
||||
for (size_t i = os; i < new_size; ++i) elementNew(piv_data + i, f);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
PIVector<T> & reserve(size_t new_size) {if (new_size <= piv_rsize) return *this; size_t os = piv_size; alloc(new_size); piv_size = os; return *this;}
|
||||
|
||||
PIVector<T> & insert(size_t index, const T & v = T()) {
|
||||
alloc(piv_size + 1);
|
||||
if (index < piv_size - 1) {
|
||||
size_t os = piv_size - index - 1;
|
||||
memmove(&(piv_data[index + 1]), &(piv_data[index]), os * sizeof(T));
|
||||
}
|
||||
//zeroRaw(&(piv_data[index]), 1);
|
||||
elementNew(piv_data + index, v);
|
||||
return *this;
|
||||
}
|
||||
PIVector<T> & insert(size_t index, const PIVector<T> & other) {
|
||||
if (other.isEmpty()) return *this;
|
||||
ssize_t os = piv_size - index;
|
||||
alloc(piv_size + other.piv_size);
|
||||
if (os > 0)
|
||||
memmove(&(piv_data[index + other.piv_size]), &(piv_data[index]), os * sizeof(T));
|
||||
newT(piv_data + index, other.piv_data, other.piv_size);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PIVector<T> & remove(size_t index, size_t count = 1) {
|
||||
if (count == 0) return *this;
|
||||
if (index + count >= piv_size) {
|
||||
resize(index);
|
||||
return *this;
|
||||
}
|
||||
size_t os = piv_size - index - count;
|
||||
deleteT(&(piv_data[index]), count);
|
||||
memmove(&(piv_data[index]), &(piv_data[index + count]), os * sizeof(T));
|
||||
piv_size -= count;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(PIVector<T> & other) {
|
||||
piSwap<T*>(piv_data, other.piv_data);
|
||||
piSwap<size_t>(piv_size, other.piv_size);
|
||||
piSwap<size_t>(piv_rsize, other.piv_rsize);
|
||||
}
|
||||
|
||||
typedef int (*CompareFunc)(const T * , const T * );
|
||||
static int compare_func(const T * t0, const T * t1) {return (*t0) < (*t1) ? -1 : ((*t0) == (*t1) ? 0 : 1);}
|
||||
PIVector<T> & sort(CompareFunc compare = compare_func) {qsort(piv_data, piv_size, sizeof(T), (int(*)(const void * , const void * ))compare); return *this;}
|
||||
|
||||
PIVector<T> & enlarge(llong piv_size) {llong ns = size_s() + piv_size; if (ns <= 0) clear(); else resize(size_t(ns)); return *this;}
|
||||
|
||||
PIVector<T> & removeOne(const T & v) {for (size_t i = 0; i < piv_size; ++i) if (piv_data[i] == v) {remove(i); return *this;} return *this;}
|
||||
PIVector<T> & removeAll(const T & v) {for (llong i = 0; i < piv_size; ++i) if (piv_data[i] == v) {remove(i); --i;} return *this;}
|
||||
|
||||
PIVector<T> & push_back(const T & v) {alloc(piv_size + 1); elementNew(piv_data + piv_size - 1, v); return *this;}
|
||||
PIVector<T> & append(const T & v) {return push_back(v);}
|
||||
PIVector<T> & operator <<(const T & v) {return push_back(v);}
|
||||
PIVector<T> & operator <<(const PIVector<T> & other) {
|
||||
size_t ps = piv_size;
|
||||
alloc(piv_size + other.piv_size);
|
||||
newT(piv_data + ps, other.piv_data, other.piv_size);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PIVector<T> & push_front(const T & v) {insert(0, v); return *this;}
|
||||
PIVector<T> & prepend(const T & v) {return push_front(v);}
|
||||
|
||||
PIVector<T> & pop_back() {if (piv_size == 0) return *this; resize(piv_size - 1); return *this;}
|
||||
PIVector<T> & pop_front() {if (piv_size == 0) return *this; remove(0); return *this;}
|
||||
|
||||
T take_back() {T t(back()); pop_back(); return t;}
|
||||
T take_front() {T t(front()); pop_front(); return t;}
|
||||
|
||||
template <typename ST>
|
||||
PIVector<ST> toType() const {PIVector<ST> ret(piv_size); for (uint i = 0; i < piv_size; ++i) ret[i] = ST(piv_data[i]); return ret;}
|
||||
|
||||
private:
|
||||
void _reset() {piv_size = piv_rsize = 0; piv_data = 0;}
|
||||
size_t asize(size_t s) {
|
||||
if (s == 0) return 0;
|
||||
if (piv_rsize + piv_rsize >= s && piv_rsize < s)
|
||||
return piv_rsize + piv_rsize;
|
||||
size_t t = 0, s_ = s - 1;
|
||||
while (s_ >> t) ++t;
|
||||
return (1 << t);
|
||||
}
|
||||
inline void newT(T * dst, const T * src, size_t s) {
|
||||
for (size_t i = 0; i < s; ++i)
|
||||
elementNew(dst + i, src[i]);
|
||||
}
|
||||
T * newRaw(size_t s) {
|
||||
//cout << std::dec << " ![("<<this<<")newRaw " << s << " elements ... <\n" << endl;
|
||||
//uchar * ret = new uchar[s * sizeof(T)];
|
||||
uchar * ret = (uchar*)(malloc(s * sizeof(T)));//new uchar[];
|
||||
//zeroRaw((T*)ret, s);
|
||||
//cout << std::hex << " > (new 0x" << (llong)ret << ") ok]!" << endl;
|
||||
return (T*)ret;
|
||||
}
|
||||
/*void reallocRawTemp(size_t s) {
|
||||
if (piv_tdata == 0) piv_tdata = (T*)(malloc(s * sizeof(T)));
|
||||
else piv_tdata = (T*)(realloc(piv_tdata, s * sizeof(T)));
|
||||
}*/
|
||||
inline void deleteT(T * d, size_t sz) {
|
||||
//cout << " ~[("<<this<<")deleteT " << std::dec << sz << " elements " << std::hex << "0x" << (llong)d << " ... <\n" << endl;
|
||||
if ((uchar*)d != 0) {
|
||||
for (size_t i = 0; i < sz; ++i)
|
||||
elementDelete(d[i]);
|
||||
//zeroRaw(d, sz);
|
||||
}
|
||||
//cout << " > ok]~" << endl;
|
||||
}
|
||||
void deleteRaw(T *& d) {
|
||||
//cout << " ~[("<<this<<")deleteRaw " << std::dec << piv_rsize << " elements " << std::hex << "0x" << (llong)d << " ... <\n" << endl;
|
||||
if ((uchar*)d != 0) free((uchar*)d);
|
||||
d = 0;
|
||||
//cout << " > ok]~" << endl;
|
||||
}
|
||||
void zeroRaw(T * d, size_t s) {
|
||||
//cout << " ~[("<<this<<")zeroRaw " << std::dec << s << " elements " << std::hex << "0x" << (llong)d << " ... <\n" << endl;
|
||||
if ((uchar*)d != 0) memset(d, 0, s*sizeof(T));
|
||||
//cout << " > ok]~" << endl;
|
||||
}
|
||||
inline void elementNew(T * to, const T & from) {new(to)T(from);}
|
||||
inline void elementDelete(T & from) {from.~T();}
|
||||
void dealloc() {deleteRaw(piv_data);}
|
||||
inline void alloc(size_t new_size) {
|
||||
if (new_size <= piv_rsize) {
|
||||
piv_size = new_size;
|
||||
return;
|
||||
}
|
||||
//int os = piv_size;
|
||||
piv_size = new_size;
|
||||
size_t as = asize(new_size);
|
||||
if (as == piv_rsize) return;
|
||||
|
||||
//cout << std::hex << " ![("<<this<<")realloc " << piv_data << " data ... <\n" << endl;
|
||||
piv_data = (T*)(realloc(piv_data, as*sizeof(T)));
|
||||
//zeroRaw(&(piv_data[os]), as - os);
|
||||
piv_rsize = as;
|
||||
//cout << std::hex << " > (new 0x" << (llong)piv_data << ") ok]!" << endl;
|
||||
/*piv_rsize = as;
|
||||
T * pd = newRaw(piv_rsize);
|
||||
if (os > 0 && piv_data != 0) {
|
||||
memcpy(pd, piv_data, os * sizeof(T));
|
||||
deleteRaw(piv_data);
|
||||
}
|
||||
piv_data = pd;*/
|
||||
}
|
||||
|
||||
T * piv_data;
|
||||
volatile size_t piv_size, piv_rsize;
|
||||
};
|
||||
/*
|
||||
#define __PIVECTOR_SIMPLE_FUNCTIONS__(T) \
|
||||
template<> inline PIVector<T>::~PIVector() {dealloc(); _reset();} \
|
||||
template<> inline PIVector<T> & PIVector<T>::push_back(const T & v) {alloc(piv_size + 1); piv_data[piv_size - 1] = v; return *this;} \
|
||||
template<> inline PIVector<T> & PIVector<T>::fill(const T & f) { \
|
||||
for (size_t i = 0; i < piv_size; ++i) \
|
||||
piv_data[i] = f; \
|
||||
return *this; \
|
||||
} \
|
||||
template<> inline PIVector<T> & PIVector<T>::resize(size_t new_size, const T & f) { \
|
||||
if (new_size < piv_size) \
|
||||
piv_size = new_size; \
|
||||
if (new_size > piv_size) { \
|
||||
size_t os = piv_size; \
|
||||
alloc(new_size); \
|
||||
for (size_t i = os; i < new_size; ++i) piv_data[i] = f; \
|
||||
} \
|
||||
return *this; \
|
||||
} \
|
||||
template<> inline PIVector<T> & PIVector<T>::insert(size_t index, const T & v) { \
|
||||
alloc(piv_size + 1); \
|
||||
if (index < piv_size - 1) { \
|
||||
size_t os = piv_size - index - 1; \
|
||||
memmove(&(piv_data[index + 1]), &(piv_data[index]), os * sizeof(T)); \
|
||||
} \
|
||||
piv_data[index] = v; \
|
||||
return *this; \
|
||||
} \
|
||||
template<> inline PIVector<T> & PIVector<T>::remove(size_t index, size_t count) { \
|
||||
if (count == 0) return *this; \
|
||||
if (index + count >= piv_size) { \
|
||||
resize(index); \
|
||||
return *this; \
|
||||
} \
|
||||
size_t os = piv_size - index - count; \
|
||||
memmove(&(piv_data[index]), &(piv_data[index + count]), os * sizeof(T)); \
|
||||
piv_size -= count; \
|
||||
return *this; \
|
||||
}
|
||||
|
||||
__PIVECTOR_SIMPLE_FUNCTIONS__(char)
|
||||
__PIVECTOR_SIMPLE_FUNCTIONS__(uchar)
|
||||
__PIVECTOR_SIMPLE_FUNCTIONS__(short)
|
||||
__PIVECTOR_SIMPLE_FUNCTIONS__(ushort)
|
||||
__PIVECTOR_SIMPLE_FUNCTIONS__(int)
|
||||
__PIVECTOR_SIMPLE_FUNCTIONS__(uint)
|
||||
__PIVECTOR_SIMPLE_FUNCTIONS__(long)
|
||||
__PIVECTOR_SIMPLE_FUNCTIONS__(ulong)
|
||||
__PIVECTOR_SIMPLE_FUNCTIONS__(llong)
|
||||
__PIVECTOR_SIMPLE_FUNCTIONS__(ullong)
|
||||
__PIVECTOR_SIMPLE_FUNCTIONS__(float)
|
||||
__PIVECTOR_SIMPLE_FUNCTIONS__(double)
|
||||
__PIVECTOR_SIMPLE_FUNCTIONS__(ldouble)*/
|
||||
#define __PIVECTOR_SIMPLE_TYPE__(T) \
|
||||
template<> inline void PIVector<T>::newT(T * dst, const T * src, size_t s) {memcpy(dst, src, s * sizeof(T));} \
|
||||
template<> inline void PIVector<T>::deleteT(T * d, size_t sz) {;} \
|
||||
template<> inline void PIVector<T>::elementNew(T * to, const T & from) {(*to) = from;} \
|
||||
template<> inline void PIVector<T>::elementDelete(T & from) {;}
|
||||
|
||||
#else
|
||||
|
||||
|
||||
template<typename T, typename Allocator = std::allocator<T> >
|
||||
class PIP_EXPORT PIVector: public vector<T, Allocator> {
|
||||
typedef PIVector<T, Allocator> _CVector;
|
||||
typedef vector<T, Allocator> _stlc;
|
||||
public:
|
||||
|
||||
PIVector() {piMonitor.containers++;}
|
||||
PIVector(uint size, const T & value = T()) {piMonitor.containers++; _stlc::resize(size, value);}
|
||||
~PIVector() {piMonitor.containers--;}
|
||||
|
||||
const T & at(uint index) const {return (*this)[index];}
|
||||
T & at(uint index) {return (*this)[index];}
|
||||
const T * data(uint index = 0) const {return &(*this)[index];}
|
||||
T * data(uint index = 0) {return &(*this)[index];}
|
||||
|
||||
#ifdef DOXYGEN
|
||||
uint size() const;
|
||||
#endif
|
||||
|
||||
int size_s() const {return static_cast<int>(_stlc::size());}
|
||||
bool isEmpty() const {return _stlc::empty();}
|
||||
bool has(const T & t) const {for (typename _stlc::const_iterator i = _stlc::begin(); i != _stlc::end(); ++i) if (t == *i) return true; return false;}
|
||||
int etries(const T & t) const {int ec = 0; for (typename _stlc::const_iterator i = _stlc::begin(); i != _stlc::end(); ++i) if (t == *i) ++ec; return ec;}
|
||||
|
||||
typedef int (*CompareFunc)(const T * , const T * );
|
||||
|
||||
static int compare_func(const T * t0, const T * t1) {return (*t0) == (*t1) ? 0 : ((*t0) < (*t1) ? -1 : 1);}
|
||||
#ifdef DOXYGEN
|
||||
|
||||
void resize(uint size, const T & new_type = T());
|
||||
PIVector<T, Allocator> & enlarge(uint size);
|
||||
void clear();
|
||||
PIVector<T, Allocator> & sort(CompareFunc compare = compare_func) {qsort(&at(0), _stlc::size(), sizeof(T), (int(*)(const void * , const void * ))compare); return *this;}
|
||||
PIVector<T, Allocator> & fill(const T & t) {_stlc::assign(_stlc::size(), t); return *this;}
|
||||
T & back();
|
||||
const T & back() const;
|
||||
T & front();
|
||||
const T & front() const;
|
||||
PIVector<T, Allocator> & push_back(const T & t);
|
||||
PIVector<T, Allocator> & push_front(const T & t) {_stlc::insert(_stlc::begin(), t); return *this;}
|
||||
PIVector<T, Allocator> & pop_back();
|
||||
PIVector<T, Allocator> & pop_front() {_stlc::erase(_stlc::begin()); return *this;}
|
||||
T take_back() {T t(_stlc::back()); _stlc::pop_back(); return t;}
|
||||
T take_front() {T t(_stlc::front()); pop_front(); return t;}
|
||||
PIVector<T, Allocator> & remove(uint index) {_stlc::erase(_stlc::begin() + index); return *this;}
|
||||
PIVector<T, Allocator> & remove(uint index, uint count) {_stlc::erase(_stlc::begin() + index, _stlc::begin() + index + count); return *this;}
|
||||
PIVector<T, Allocator> & removeOne(const T & v) {for (typename _stlc::iterator i = _stlc::begin(); i != _stlc::end(); ++i) if (v == *i) {_stlc::erase(i); return *this;} return *this;}
|
||||
PIVector<T, Allocator> & removeAll(const T & v) {for (typename _stlc::iterator i = _stlc::begin(); i != _stlc::end(); ++i) if (v == *i) {_stlc::erase(i); --i;} return *this;}
|
||||
PIVector<T, Allocator> & insert(uint pos, const T & t) {_stlc::insert(_stlc::begin() + pos, t); return *this;}
|
||||
PIVector<T, Allocator> & insert(uint pos, const PIVector<T, Allocator> & t) {_stlc::insert(_stlc::begin() + pos, t.begin(), t.end()); return *this;}
|
||||
T & operator [](uint index);
|
||||
const T & operator [](uint index) const;
|
||||
PIVector<T, Allocator> & operator <<(const T & t) {_stlc::push_back(t); return *this;}
|
||||
PIVector<T, Allocator> & operator <<(const PIVector<T, Allocator> & t) {for (typename _stlc::const_iterator i = t.begin(); i != t.end(); i++) _stlc::push_back(*i); return *this;}
|
||||
bool operator ==(const PIVector<T, Allocator> & t) {for (uint i = 0; i < _stlc::size(); ++i) if (t[i] != at(i)) return false; return true;}
|
||||
bool operator !=(const PIVector<T, Allocator> & t) {for (uint i = 0; i < _stlc::size(); ++i) if (t[i] != at(i)) return true; return false;}
|
||||
bool contains(const T & v) const {for (uint i = 0; i < _stlc::size(); ++i) if (v == at(i)) return true; return false;}
|
||||
|
||||
#else
|
||||
_CVector & enlarge(int size_) {int ns = size_s() + size_; if (ns <= 0) _stlc::clear(); else _stlc::resize(ns); return *this;}
|
||||
_CVector & sort(CompareFunc compare = compare_func) {qsort(&at(0), _stlc::size(), sizeof(T), (int(*)(const void * , const void * ))compare); return *this;}
|
||||
_CVector & fill(const T & t) {_stlc::assign(_stlc::size(), t); return *this;}
|
||||
_CVector & pop_front() {_stlc::erase(_stlc::begin()); return *this;}
|
||||
_CVector & push_front(const T & t) {_stlc::insert(_stlc::begin(), t); return *this;}
|
||||
T take_front() {T t(_stlc::front()); pop_front(); return t;}
|
||||
T take_back() {T t(_stlc::back()); _stlc::pop_back(); return t;}
|
||||
_CVector & remove(uint index) {_stlc::erase(_stlc::begin() + index); return *this;}
|
||||
_CVector & remove(uint index, uint count) {_stlc::erase(_stlc::begin() + index, _stlc::begin() + index + count); return *this;}
|
||||
_CVector & removeOne(const T & v) {for (typename _stlc::iterator i = _stlc::begin(); i != _stlc::end(); ++i) if (v == *i) {_stlc::erase(i); return *this;} return *this;}
|
||||
_CVector & removeAll(const T & v) {for (typename _stlc::iterator i = _stlc::begin(); i != _stlc::end(); ++i) if (v == *i) {_stlc::erase(i); --i;} return *this;}
|
||||
_CVector & insert(uint pos, const T & t) {_stlc::insert(_stlc::begin() + pos, t); return *this;}
|
||||
_CVector & insert(uint pos, const _CVector & t) {_stlc::insert(_stlc::begin() + pos, t.begin(), t.end()); return *this;}
|
||||
_CVector & operator <<(const T & t) {_stlc::push_back(t); return *this;}
|
||||
_CVector & operator <<(const _CVector & t) {for (typename _stlc::const_iterator i = t.begin(); i != t.end(); i++) _stlc::push_back(*i); return *this;}
|
||||
bool operator ==(const _CVector & t) {for (uint i = 0; i < _stlc::size(); ++i) if (t[i] != at(i)) return false; return true;}
|
||||
bool operator !=(const _CVector & t) {for (uint i = 0; i < _stlc::size(); ++i) if (t[i] != at(i)) return true; return false;}
|
||||
bool contains(const T & v) const {for (uint i = 0; i < _stlc::size(); ++i) if (v == at(i)) return true; return false;}
|
||||
#endif
|
||||
};
|
||||
|
||||
#define __PIVECTOR_SIMPLE_TYPE__(T)
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
template<typename T>
|
||||
inline std::ostream & operator <<(std::ostream & s, const PIVector<T> & v) {s << "{"; for (size_t i = 0; i < v.size(); ++i) {s << v[i]; if (i < v.size() - 1) s << ", ";} s << "}"; return s;}
|
||||
|
||||
template<typename T>
|
||||
inline PICout operator <<(PICout s, const PIVector<T> & v) {s.space(); s.setControl(0, true); s << "{"; for (size_t i = 0; i < v.size(); ++i) {s << v[i]; if (i < v.size() - 1) s << ", ";} s << "}"; s.restoreControl(); return s;}
|
||||
|
||||
|
||||
#endif // PIVECTOR_H
|
||||
104
src/core/pibitarray.h
Executable file
104
src/core/pibitarray.h
Executable file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Bit array
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIBITARRAY_H
|
||||
#define PIBITARRAY_H
|
||||
|
||||
#include "picontainers.h"
|
||||
|
||||
class PIP_EXPORT PIBitArray {
|
||||
public:
|
||||
PIBitArray(const int & size = 0) {resize(size);}
|
||||
PIBitArray(uchar val) {resize(sizeof(val) * 8); data_[0] = val;}
|
||||
PIBitArray(ushort val) {resize(sizeof(val) * 8); memcpy(data(), &val, sizeof(val));}
|
||||
PIBitArray(uint val) {resize(sizeof(val) * 8); memcpy(data(), &val, sizeof(val));}
|
||||
PIBitArray(ulong val) {resize(sizeof(val) * 8); memcpy(data(), &val, sizeof(val));}
|
||||
PIBitArray(ullong val) {resize(sizeof(val) * 8); memcpy(data(), &val, sizeof(val));}
|
||||
PIBitArray(uchar * bytes, uint size) {resize(size * 8); memcpy(data(), bytes, size);}
|
||||
|
||||
uint bitSize() const {return size_;}
|
||||
uint byteSize() const {return bytesInBits(size_);}
|
||||
PIBitArray & resize(const uint & size) {size_ = size; data_.resize(bytesInBits(size_)); return *this;}
|
||||
|
||||
PIBitArray & clearBit(const uint & index) {data_[index / 8] &= ~(1 << (index % 8)); return *this;}
|
||||
PIBitArray & setBit(const uint & index) {data_[index / 8] |= (1 << (index % 8)); return *this;}
|
||||
PIBitArray & writeBit(const uint & index, const bool & value) {if (value) setBit(index); else clearBit(index); return *this;}
|
||||
PIBitArray & writeBit(const uint & index, const uchar & value) {return writeBit(index, value > 0);}
|
||||
|
||||
PIBitArray & push_back(const bool & value) {resize(size_ + 1); writeBit(size_ - 1, value); return *this;}
|
||||
PIBitArray & push_back(const uchar & value) {return push_back(value > 0);}
|
||||
PIBitArray & insert(const uint & index, const bool & value) {
|
||||
resize(size_ + 1);
|
||||
uint fi = byteSize() - 1, si = index / 8, ti = index % 8;
|
||||
uchar c = data_[si];
|
||||
for (uint i = fi; i > si; --i) {
|
||||
data_[i] <<= 1;
|
||||
if ((0x80 & data_[i - 1]) == 0x80) data_[i] |= 1;
|
||||
else data_[i] &= 0xFE;}
|
||||
data_[si] &= (0xFF >> (7 - ti));
|
||||
data_[si] |= ((c << 1) & (0xFF << (ti)));
|
||||
if (value) data_[si] |= (1 << ti);
|
||||
else data_[si] &= ~(1 << ti);
|
||||
return *this;}
|
||||
PIBitArray & insert(const uint & index, const uchar & value) {return insert(index, value > 0);}
|
||||
PIBitArray & push_front(const bool & value) {return insert(0, value);}
|
||||
PIBitArray & push_front(const uchar & value) {return push_front(value > 0);}
|
||||
PIBitArray & pop_back() {return resize(size_ - 1);}
|
||||
PIBitArray & pop_front() {
|
||||
if (size_ == 0) return *this;
|
||||
uint fi = byteSize() - 1;
|
||||
for (uint i = 0; i < fi; ++i) {
|
||||
data_[i] >>= 1;
|
||||
if ((1 & data_[i + 1]) == 1) data_[i] |= 0x80;
|
||||
else data_[i] &= 0x7F;}
|
||||
data_[fi] >>= 1;
|
||||
resize(size_ - 1);
|
||||
return *this;}
|
||||
PIBitArray & append(const PIBitArray & ba) {for (uint i = 0; i < ba.bitSize(); ++i) push_back(ba[i]); return *this;}
|
||||
|
||||
uchar * data() {return data_.data();}
|
||||
uchar toUChar() {if (size_ == 0) return 0; return data_[0];}
|
||||
ushort toUShort() {ushort t = 0; memcpy(&t, data(), piMin<uint>(byteSize(), sizeof(t))); return t;}
|
||||
uint toUInt() {uint t = 0; memcpy(&t, data(), piMin<uint>(byteSize(), sizeof(t))); return t;}
|
||||
ulong toULong() {ulong t = 0; memcpy(&t, data(), piMin<uint>(byteSize(), sizeof(t))); return t;}
|
||||
ullong toULLong() {ullong t = 0; memcpy(&t, data(), piMin<uint>(byteSize(), sizeof(t))); return t;}
|
||||
|
||||
bool at(const uint & index) const {return (1 & (data_[index / 8] >> (index % 8))) == 1 ? true : false;}
|
||||
bool operator [](const uint & index) const {return at(index);}
|
||||
void operator +=(const PIBitArray & ba) {append(ba);}
|
||||
bool operator ==(const PIBitArray & ba) const {if (bitSize() != ba.bitSize()) return false; for (uint i = 0; i < bitSize(); ++i) if (at(i) != ba[i]) return false; return true;}
|
||||
bool operator !=(const PIBitArray & ba) const {return !(*this == ba);}
|
||||
void operator =(const uchar & val) {resize(sizeof(val) * 8); data_[0] = val;}
|
||||
void operator =(const ushort & val) {resize(sizeof(val) * 8); memcpy(data(), &val, sizeof(val));}
|
||||
void operator =(const uint & val) {resize(sizeof(val) * 8); memcpy(data(), &val, sizeof(val));}
|
||||
void operator =(const ulong & val) {resize(sizeof(val) * 8); memcpy(data(), &val, sizeof(val));}
|
||||
void operator =(const ullong & val) {resize(sizeof(val) * 8); memcpy(data(), &val, sizeof(val));}
|
||||
|
||||
private:
|
||||
uint bytesInBits(const uint & bits) const {return (bits + 7) / 8;}
|
||||
|
||||
PIVector<uchar> data_;
|
||||
uint size_;
|
||||
|
||||
};
|
||||
|
||||
inline std::ostream & operator <<(std::ostream & s, const PIBitArray & ba) {for (uint i = 0; i < ba.bitSize(); ++i) {s << ba[i]; if (i % 8 == 7) s << ' ';} return s;}
|
||||
inline PICout operator <<(PICout s, const PIBitArray & ba) {s.space(); s.setControl(0, true); for (uint i = 0; i < ba.bitSize(); ++i) {s << ba[i]; if (i % 8 == 7) s << ' ';} s.restoreControl(); return s;}
|
||||
|
||||
#endif // PIBITARRAY_H
|
||||
293
src/core/pibytearray.cpp
Executable file
293
src/core/pibytearray.cpp
Executable file
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Byte array
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pibytearray.h"
|
||||
#include "pistring.h"
|
||||
|
||||
/*! \class PIByteArray
|
||||
* \brief Byte array
|
||||
* \details This class based on PIDeque<uchar> and provide some handle function
|
||||
* to manipulate it.
|
||||
*
|
||||
* \section PIByteArray_sec0 Usage
|
||||
* %PIByteArray can be used to store custom data and manipulate it. There are many
|
||||
* stream operators to store/restore common types to byte array. Store operators
|
||||
* places data at the end of array, restore operators takes data from the beginning
|
||||
* of array.
|
||||
* In addition there are Base 64 convertions and checksums:
|
||||
* * plain 8-bit
|
||||
* * plain 32-bit
|
||||
*
|
||||
* One of the major usage of %PIByteArray is stream functions. You can form binary
|
||||
* packet from many types (also dynamic types, e.g. PIVector) with one line:
|
||||
* \snippet pibytearray.cpp 0
|
||||
*
|
||||
* Or you can descibe stream operator of your own type and store/restore vectors of
|
||||
* your type:
|
||||
* \snippet pibytearray.cpp 1
|
||||
*
|
||||
* For store/restore custom data blocks there is PIByteArray::RawData class. Stream
|
||||
* operators of this class simply store/restore data block to/from byte array.
|
||||
* \snippet pibytearray.cpp 2
|
||||
*
|
||||
* \section PIByteArray_sec1 Attention
|
||||
* Stream operator of %PIByteArray store byte array as vector, not simply append
|
||||
* content of byte array. This operators useful to transmit custom data as %PIByteArray
|
||||
* packed into parent byte array, e.g. to form packet from %PIByteArray.
|
||||
* To append one byte array to another use funtion \a append().
|
||||
* \snippet pibytearray.cpp 3
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
const char PIByteArray::base64Table[64] = {
|
||||
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
|
||||
0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
|
||||
0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
||||
0x59, 0x5a, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
|
||||
0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
|
||||
0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
|
||||
0x77, 0x78, 0x79, 0x7a, 0x30, 0x31, 0x32, 0x33,
|
||||
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2b, 0x2f};
|
||||
|
||||
const char PIByteArray::base64InvTable[256] = {
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x3E, 0x0, 0x0, 0x0, 0x3F,
|
||||
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B,
|
||||
0x3C, 0x3D, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6,
|
||||
0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE,
|
||||
0xF, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
|
||||
0x17, 0x18, 0x19, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
|
||||
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
|
||||
0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
|
||||
0x31, 0x32, 0x33, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
|
||||
int PIHuffman::nodeCompare(const void * f, const void * s) {
|
||||
return (reinterpret_cast<node * >(const_cast<void * >(s))->freq -
|
||||
reinterpret_cast<node * >(const_cast<void * >(f))->freq);
|
||||
}
|
||||
|
||||
|
||||
PIDeque<uchar> PIHuffman::compress(const PIDeque<uchar> & src) {
|
||||
calcFrequencies(src);
|
||||
return src;
|
||||
}
|
||||
|
||||
|
||||
void PIHuffman::calcFrequencies(const PIDeque<uchar> & src) {
|
||||
nodes.resize(256);
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
nodes[i].parent = nodes[i].right = nodes[i].left = 0;
|
||||
nodes[i].freq = 0;
|
||||
nodes[i].word.resize(1);
|
||||
nodes[i].word[0] = static_cast<uchar>(i);
|
||||
}
|
||||
for (int i = 0; i < src.size_s(); ++i)
|
||||
nodes[src[i]].freq++;
|
||||
std::qsort(nodes.data(), 256, sizeof(node), nodeCompare);
|
||||
for (int i = 255; i >= 0; --i)
|
||||
if (nodes[i].freq > 0 && i < 255)
|
||||
{nodes.remove(i + 1, 255 - i); break;}
|
||||
for (int i = 0; i < nodes.size_s(); ++i)
|
||||
cout << string((char*)nodes[i].word.data(), 1) << ": " << nodes[i].freq << endl;
|
||||
}
|
||||
|
||||
|
||||
PIHuffman PIByteArray::huffman;
|
||||
|
||||
PIByteArray & PIByteArray::convertToBase64() {
|
||||
base64HelpStruct hs;
|
||||
PIByteArray t;
|
||||
if (size() == 0) return *this;
|
||||
int sz = (size_s() / 3) * 3;
|
||||
for (int i = 0; i < sz; ++i) {
|
||||
hs.byte.byte0 = hs.byte.byte1 = hs.byte.byte2 = 0;
|
||||
hs.byte.byte0 = at(i);
|
||||
hs.byte.byte1 = at(++i);
|
||||
hs.byte.byte2 = at(++i);
|
||||
t.push_back(base64Table[hs.ascii.ascii0]);
|
||||
t.push_back(base64Table[hs.ascii.ascii1]);
|
||||
t.push_back(base64Table[hs.ascii.ascii2]);
|
||||
t.push_back(base64Table[hs.ascii.ascii3]);
|
||||
}
|
||||
hs.byte.byte0 = hs.byte.byte1 = hs.byte.byte2 = 0; sz = size() % 3;
|
||||
switch (sz) {
|
||||
case 1:
|
||||
hs.byte.byte0 = back();
|
||||
t.push_back(base64Table[hs.ascii.ascii0]);
|
||||
t.push_back(base64Table[hs.ascii.ascii1]);
|
||||
t.push_back('=');
|
||||
t.push_back('=');
|
||||
break;
|
||||
case 2:
|
||||
hs.byte.byte0 = at(size() - 2); hs.byte.byte1 = back();
|
||||
t.push_back(base64Table[hs.ascii.ascii0]);
|
||||
t.push_back(base64Table[hs.ascii.ascii1]);
|
||||
t.push_back(base64Table[hs.ascii.ascii2]);
|
||||
t.push_back('=');
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
*this = t;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
PIByteArray & PIByteArray::convertFromBase64() {
|
||||
base64HelpStruct hs;
|
||||
PIByteArray t;
|
||||
uint sz = size();
|
||||
if (sz == 0) return *this;
|
||||
for (uint i = 0; i < sz; ++i) {
|
||||
hs.byte.byte0 = hs.byte.byte1 = hs.byte.byte2 = 0;
|
||||
hs.ascii.ascii0 = base64InvTable[at(i)];
|
||||
hs.ascii.ascii1 = base64InvTable[at(++i)];
|
||||
hs.ascii.ascii2 = base64InvTable[at(++i)];
|
||||
hs.ascii.ascii3 = base64InvTable[at(++i)];
|
||||
t.push_back(hs.byte.byte0);
|
||||
t.push_back(hs.byte.byte1);
|
||||
t.push_back(hs.byte.byte2);
|
||||
}
|
||||
if (back() == '=') t.pop_back();
|
||||
if (sz > 1) if (at(sz - 2) == '=') t.pop_back();
|
||||
*this = t;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
PIByteArray & PIByteArray::compressRLE(uchar threshold) {
|
||||
PIByteArray t;
|
||||
uchar fb, clen, mlen = 255 - threshold;
|
||||
for (uint i = 0; i < size();) {
|
||||
fb = at(i);
|
||||
clen = 1;
|
||||
while (at(++i) == fb) {
|
||||
++clen;
|
||||
if (clen == mlen)
|
||||
break;
|
||||
}
|
||||
if (clen > 1) {
|
||||
t.push_back(threshold + clen);
|
||||
t.push_back(fb);
|
||||
continue;
|
||||
}
|
||||
if (fb >= threshold) {
|
||||
t.push_back(threshold + 1);
|
||||
t.push_back(fb);
|
||||
} else
|
||||
t.push_back(fb);
|
||||
}
|
||||
*this = t;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
PIByteArray & PIByteArray::decompressRLE(uchar threshold) {
|
||||
PIByteArray t;
|
||||
uchar fb, clen;
|
||||
for (uint i = 0; i < size(); ++i) {
|
||||
fb = at(i);
|
||||
if (fb >= threshold) {
|
||||
clen = fb - threshold;
|
||||
fb = at(++i);
|
||||
for (uint j = 0; j < clen; ++j)
|
||||
t.push_back(fb);
|
||||
continue;
|
||||
} else
|
||||
t.push_back(fb);
|
||||
}
|
||||
*this = t;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
uchar PIByteArray::checksumPlain8() const {
|
||||
uchar c = 0;
|
||||
int sz = size_s();
|
||||
for (int i = 0; i < sz; ++i)
|
||||
c += at(i);
|
||||
c = ~(c + 1);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
uint PIByteArray::checksumPlain32() const {
|
||||
uint c = 0;
|
||||
int sz = size_s();
|
||||
for (int i = 0; i < sz; ++i)
|
||||
c += at(i) * (i + 1);
|
||||
c = ~(c + 1);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
PIString PIByteArray::toString(int base) const {
|
||||
PIString ret;
|
||||
int sz = size_s();
|
||||
for (int i = 0; i < sz; ++i) {
|
||||
if (i > 0) ret += " ";
|
||||
if (base == 2) ret += "b";
|
||||
if (base == 8) ret += "0";
|
||||
if (base == 16) ret += "0x";
|
||||
ret += PIString::fromNumber(at(i), base);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIByteArray PIByteArray::fromString(PIString str) {
|
||||
PIByteArray ret;
|
||||
if (str.trim().isEmpty()) return ret;
|
||||
str.replaceAll("\n", " ").replaceAll("\t", " ").replaceAll(" ", " ");
|
||||
PIStringList bl(str.split(" "));
|
||||
bool ok(false);
|
||||
piForeachC (PIString & b, bl) {
|
||||
int bv = b.toInt(-1, &ok);
|
||||
if (ok) ret << uchar(bv);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
261
src/core/pibytearray.h
Executable file
261
src/core/pibytearray.h
Executable file
@@ -0,0 +1,261 @@
|
||||
/*! \file pibytearray.h
|
||||
* \brief Byte array
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Byte array
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIBYTEARRAY_H
|
||||
#define PIBYTEARRAY_H
|
||||
|
||||
#ifdef DOXYGEN
|
||||
//! This macro allow stream template operators for write and read any type from byte array. Use it with attention!
|
||||
# define PIP_BYTEARRAY_STREAM_ANY_TYPE
|
||||
#endif
|
||||
|
||||
#include "pibitarray.h"
|
||||
|
||||
class PIString;
|
||||
class PIByteArray;
|
||||
|
||||
class PIHuffman {
|
||||
public:
|
||||
PIDeque<uchar> compress(const PIDeque<uchar> & src);
|
||||
|
||||
private:
|
||||
struct node {
|
||||
int freq;
|
||||
PIDeque<uchar> word;
|
||||
PIBitArray path;
|
||||
node * parent;
|
||||
node * right;
|
||||
node * left;
|
||||
};
|
||||
static int nodeCompare(const void * f, const void * s);
|
||||
void calcFrequencies(const PIDeque<uchar> & src);
|
||||
PIVector<node> nodes;
|
||||
};
|
||||
|
||||
class PIP_EXPORT PIByteArray: public PIDeque<uchar>
|
||||
{
|
||||
public:
|
||||
|
||||
//! Constructs an empty byte array
|
||||
PIByteArray() {;}
|
||||
|
||||
//! Constructs 0-filled byte array with size "size"
|
||||
PIByteArray(const uint size) {resize(size);}
|
||||
|
||||
//! Constructs byte array from data "data" and size "size"
|
||||
PIByteArray(const void * data, const uint size): PIDeque<uchar>((const uchar*)data, size_t(size)) {/*for (uint i = 0; i < size; ++i) push_back(((uchar * )data)[i]);*/}
|
||||
|
||||
|
||||
//! Help struct to store/restore custom blocks of data to/from PIByteArray
|
||||
struct RawData {
|
||||
friend PIByteArray & operator <<(PIByteArray & s, const PIByteArray::RawData & v);
|
||||
friend PIByteArray & operator >>(PIByteArray & s, PIByteArray::RawData v);
|
||||
public:
|
||||
//! Constructs data block
|
||||
RawData(void * data = 0, int size = 0) {d = data; s = size;}
|
||||
//! Constructs data block
|
||||
RawData(const void * data, const int size) {d = const_cast<void * >(data); s = size;}
|
||||
RawData & operator =(const RawData & o) {d = o.d; s = o.s; return *this;}
|
||||
private:
|
||||
void * d;
|
||||
int s;
|
||||
};
|
||||
|
||||
//! Return resized byte array
|
||||
PIByteArray resized(int new_size) const {PIByteArray tv(*this); tv.resize(new_size); return tv;}
|
||||
|
||||
//! Convert data to Base 64 and return this byte array
|
||||
PIByteArray & convertToBase64();
|
||||
|
||||
//! Convert data from Base 64 and return this byte array
|
||||
PIByteArray & convertFromBase64();
|
||||
|
||||
//! Return converted to Base 64 data
|
||||
PIByteArray toBase64() const {PIByteArray ba(*this); ba.convertToBase64(); return ba;}
|
||||
|
||||
//! Return converted from Base 64 data
|
||||
PIByteArray fromBase64() const {PIByteArray ba(*this); ba.convertFromBase64(); return ba;}
|
||||
|
||||
PIByteArray & compressRLE(uchar threshold = 192);
|
||||
PIByteArray & decompressRLE(uchar threshold = 192);
|
||||
PIByteArray compressedRLE(uchar threshold = 192) {PIByteArray ba(*this); ba.compressRLE(threshold); return ba;}
|
||||
PIByteArray decompressedRLE(uchar threshold = 192) {PIByteArray ba(*this); ba.decompressRLE(threshold); return ba;}
|
||||
|
||||
PIByteArray & compressHuffman() {*this = huffman.compress(*this); return *this;}
|
||||
|
||||
PIString toString(int base = 16) const;
|
||||
|
||||
//! Add to the end data "data" with size "size"
|
||||
PIByteArray & append(const void * data_, int size_) {uint ps = size(); enlarge(size_); memcpy(data(ps), data_, size_); return *this;}
|
||||
|
||||
//! Add to the end byte array "data"
|
||||
PIByteArray & append(const PIByteArray & data_) {uint ps = size(); enlarge(data_.size_s()); memcpy(data(ps), data_.data(), data_.size()); return *this;}
|
||||
/*PIByteArray & operator <<(short v) {for (uint i = 0; i < sizeof(v); ++i) push_back(((uchar*)(&v))[i]); return *this;}
|
||||
PIByteArray & operator <<(ushort v) {for (uint i = 0; i < sizeof(v); ++i) push_back(((uchar*)(&v))[i]); return *this;}
|
||||
PIByteArray & operator <<(int v) {for (uint i = 0; i < sizeof(v); ++i) push_back(((uchar*)(&v))[i]); return *this;}
|
||||
PIByteArray & operator <<(uint v) {for (uint i = 0; i < sizeof(v); ++i) push_back(((uchar*)(&v))[i]); return *this;}
|
||||
PIByteArray & operator <<(llong v) {for (uint i = 0; i < sizeof(v); ++i) push_back(((uchar*)(&v))[i]); return *this;}
|
||||
PIByteArray & operator <<(ullong v) {for (uint i = 0; i < sizeof(v); ++i) push_back(((uchar*)(&v))[i]); return *this;}*/
|
||||
//PIByteArray & operator <<(const PIByteArray & v) {for (uint i = 0; i < v.size(); ++i) push_back(v[i]); return *this;}
|
||||
|
||||
//! Returns plain 8-bit checksum
|
||||
uchar checksumPlain8() const;
|
||||
|
||||
//! Returns plain 32-bit checksum
|
||||
uint checksumPlain32() const;
|
||||
|
||||
void operator =(const PIDeque<uchar> & d) {resize(d.size()); memcpy(data(), d.data(), d.size());}
|
||||
|
||||
static PIByteArray fromString(PIString str);
|
||||
|
||||
private:
|
||||
union base64HelpStruct {
|
||||
base64HelpStruct() {memset(this, 0, sizeof(base64HelpStruct));}
|
||||
struct {
|
||||
uchar ascii0: 6;
|
||||
uchar ascii1: 6;
|
||||
uchar ascii2: 6;
|
||||
uchar ascii3: 6;
|
||||
} ascii;
|
||||
struct {
|
||||
uchar byte0;
|
||||
uchar byte1;
|
||||
uchar byte2;
|
||||
} byte;
|
||||
};
|
||||
|
||||
static const char base64Table[64];
|
||||
static const char base64InvTable[256];
|
||||
static PIHuffman huffman;
|
||||
|
||||
};
|
||||
|
||||
inline bool operator <(const PIByteArray & v0, const PIByteArray & v1) {if (v0.size() == v1.size()) {for (uint i = 0; i < v0.size(); ++i) if (v0[i] != v1[i]) return v0[i] < v1[i]; return false;} return v0.size() < v1.size();}
|
||||
//! \relatesalso PIByteArray \brief Output to std::ostream operator
|
||||
inline std::ostream & operator <<(std::ostream & s, const PIByteArray & ba) {s << "{"; for (uint i = 0; i < ba.size(); ++i) {s << ba[i]; if (i < ba.size() - 1) s << ", ";} s << "}"; return s;}
|
||||
|
||||
//! \relatesalso PIByteArray \brief Output to PICout operator
|
||||
inline PICout operator <<(PICout s, const PIByteArray & ba) {s.space(); s.setControl(0, true); s << "{"; for (uint i = 0; i < ba.size(); ++i) {s << ba[i]; if (i < ba.size() - 1) s << ", ";} s << "}"; s.restoreControl(); return s;}
|
||||
|
||||
#define PBA_OPERATOR_TO int os = s.size_s(); s.enlarge(sizeof(v)); memcpy(s.data(os), &v, sizeof(v));
|
||||
|
||||
//! \relatesalso PIByteArray \brief Store operator
|
||||
inline PIByteArray & operator <<(PIByteArray & s, uchar v) {s.push_back(v); return s;}
|
||||
//! \relatesalso PIByteArray \brief Store operator
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const short v) {PBA_OPERATOR_TO return s;}
|
||||
//! \relatesalso PIByteArray \brief Store operator
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const int v) {PBA_OPERATOR_TO return s;}
|
||||
//! \relatesalso PIByteArray \brief Store operator
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const long & v) {PBA_OPERATOR_TO return s;}
|
||||
//! \relatesalso PIByteArray \brief Store operator
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const llong & v) {PBA_OPERATOR_TO return s;}
|
||||
//! \relatesalso PIByteArray \brief Store operator
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const ushort v) {PBA_OPERATOR_TO return s;}
|
||||
//! \relatesalso PIByteArray \brief Store operator
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const uint v) {PBA_OPERATOR_TO return s;}
|
||||
//! \relatesalso PIByteArray \brief Store operator
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const ulong & v) {PBA_OPERATOR_TO return s;}
|
||||
//! \relatesalso PIByteArray \brief Store operator
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const ullong & v) {PBA_OPERATOR_TO return s;}
|
||||
//! \relatesalso PIByteArray \brief Store operator
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const float v) {PBA_OPERATOR_TO return s;}
|
||||
//! \relatesalso PIByteArray \brief Store operator
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const double & v) {PBA_OPERATOR_TO return s;}
|
||||
//! \relatesalso PIByteArray \brief Store operator, see \ref PIByteArray_sec1 for details
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PIByteArray & v) {s << v.size_s(); int os = s.size_s(); s.enlarge(v.size_s()); if (v.size_s() > 0) memcpy(s.data(os), v.data(), v.size()); return s;}
|
||||
//! \relatesalso PIByteArray \brief Store operator, see \ref PIByteArray_sec1 for details
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PIByteArray::RawData & v) {int os = s.size_s(); s.enlarge(v.s); if (v.s > 0) memcpy(s.data(os), v.d, v.s); return s;}
|
||||
//! \relatesalso PIByteArray \brief Store operator
|
||||
template<typename Type0, typename Type1>
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PIPair<Type0, Type1> & v) {s << v.first << v.second; return s;}
|
||||
//! \relatesalso PIByteArray \brief Store operator
|
||||
template<typename T>
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PIVector<T> & v) {s << v.size_s(); for (uint i = 0; i < v.size(); ++i) s << v[i]; return s;}
|
||||
//! \relatesalso PIByteArray \brief Store operator
|
||||
template<typename T>
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PIList<T> & v) {s << v.size_s(); for (uint i = 0; i < v.size(); ++i) s << v[i]; return s;}
|
||||
//! \relatesalso PIByteArray \brief Store operator
|
||||
template<typename T>
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PIDeque<T> & v) {s << v.size_s(); for (uint i = 0; i < v.size(); ++i) s << v[i]; return s;}
|
||||
#ifdef PIP_BYTEARRAY_STREAM_ANY_TYPE
|
||||
template<typename T>
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const T & v) {PBA_OPERATOR_TO return s;}
|
||||
#endif
|
||||
|
||||
#undef PBA_OPERATOR_TO
|
||||
#define PBA_OPERATOR_FROM memcpy(&v, s.data(), sizeof(v)); s.remove(0, sizeof(v));
|
||||
|
||||
//! \relatesalso PIByteArray \brief Restore operator
|
||||
inline PIByteArray & operator >>(PIByteArray & s, uchar & v) {assert(s.size() >= 1u); v = s.take_front(); return s;}
|
||||
//! \relatesalso PIByteArray \brief Restore operator
|
||||
inline PIByteArray & operator >>(PIByteArray & s, short & v) {assert(s.size() >= sizeof(v)); PBA_OPERATOR_FROM return s;}
|
||||
//! \relatesalso PIByteArray \brief Restore operator
|
||||
inline PIByteArray & operator >>(PIByteArray & s, int & v) {assert(s.size() >= sizeof(v)); PBA_OPERATOR_FROM return s;}
|
||||
//! \relatesalso PIByteArray \brief Restore operator
|
||||
inline PIByteArray & operator >>(PIByteArray & s, long & v) {assert(s.size() >= sizeof(v)); PBA_OPERATOR_FROM return s;}
|
||||
//! \relatesalso PIByteArray \brief Restore operator
|
||||
inline PIByteArray & operator >>(PIByteArray & s, llong & v) {assert(s.size() >= sizeof(v)); PBA_OPERATOR_FROM return s;}
|
||||
//! \relatesalso PIByteArray \brief Restore operator
|
||||
inline PIByteArray & operator >>(PIByteArray & s, ushort & v) {assert(s.size() >= sizeof(v)); PBA_OPERATOR_FROM return s;}
|
||||
//! \relatesalso PIByteArray \brief Restore operator
|
||||
inline PIByteArray & operator >>(PIByteArray & s, uint & v) {assert(s.size() >= sizeof(v)); PBA_OPERATOR_FROM return s;}
|
||||
//! \relatesalso PIByteArray \brief Restore operator
|
||||
inline PIByteArray & operator >>(PIByteArray & s, ulong & v) {assert(s.size() >= sizeof(v)); PBA_OPERATOR_FROM return s;}
|
||||
//! \relatesalso PIByteArray \brief Restore operator
|
||||
inline PIByteArray & operator >>(PIByteArray & s, ullong & v) {assert(s.size() >= sizeof(v)); PBA_OPERATOR_FROM return s;}
|
||||
//! \relatesalso PIByteArray \brief Restore operator
|
||||
inline PIByteArray & operator >>(PIByteArray & s, float & v) {assert(s.size() >= sizeof(v)); PBA_OPERATOR_FROM return s;}
|
||||
//! \relatesalso PIByteArray \brief Restore operator
|
||||
inline PIByteArray & operator >>(PIByteArray & s, double & v) {assert(s.size() >= sizeof(v)); PBA_OPERATOR_FROM return s;}
|
||||
//! \relatesalso PIByteArray \brief Restore operator, see \ref PIByteArray_sec1 for details
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PIByteArray & v) {assert(s.size_s() >= 4); int sz; s >> sz; v.resize(sz); if (sz > 0) memcpy(v.data(), s.data(), v.size()); s.remove(0, v.size()); return s;}
|
||||
//! \relatesalso PIByteArray \brief Restore operator, see \ref PIByteArray_sec1 for details
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PIByteArray::RawData v) {assert(s.size_s() >= v.s); if (v.s > 0) memcpy(v.d, s.data(), v.s); s.remove(0, v.s); return s;}
|
||||
//! \relatesalso PIByteArray \brief Restore operator
|
||||
template<typename Type0, typename Type1>
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PIPair<Type0, Type1> & v) {s >> v.first >> v.second; return s;}
|
||||
//! \relatesalso PIByteArray \brief Restore operator
|
||||
template<typename T>
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PIVector<T> & v) {assert(s.size_s() >= 4); int sz; s >> sz; v.resize(sz); for (int i = 0; i < sz; ++i) s >> v[i]; return s;}
|
||||
//! \relatesalso PIByteArray \brief Restore operator
|
||||
template<typename T>
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PIList<T> & v) {assert(s.size_s() >= 4); int sz; s >> sz; v.resize(sz); for (int i = 0; i < sz; ++i) s >> v[i]; return s;}
|
||||
//! \relatesalso PIByteArray \brief Restore operator
|
||||
template<typename T>
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PIDeque<T> & v) {assert(s.size_s() >= 4); int sz; s >> sz; v.resize(sz); for (int i = 0; i < sz; ++i) s >> v[i]; return s;}
|
||||
// //! \relatesalso PIByteArray \brief Restore operator
|
||||
//template <typename Key, typename T>
|
||||
//inline PIByteArray & operator >>(PIByteArray & s, PIMap<Key, T> & v) {assert(s.size_s() >= 4); int sz; s >> sz; v.resize(sz); for (int i = 0; i < sz; ++i) s >> v[i]; return s;}
|
||||
#ifdef PIP_BYTEARRAY_STREAM_ANY_TYPE
|
||||
template<typename T>
|
||||
inline PIByteArray & operator >>(PIByteArray & s, T & v) {assert(s.size() >= sizeof(v)); PBA_OPERATOR_FROM return s;}
|
||||
#endif
|
||||
|
||||
|
||||
#undef PBA_OPERATOR_FROM
|
||||
|
||||
//! \relatesalso PIByteArray \brief Byte arrays compare operator
|
||||
inline bool operator ==(PIByteArray & f, PIByteArray & s) {if (f.size_s() != s.size_s()) return false; for (int i = 0; i < f.size_s(); ++i) if (f[i] != s[i]) return false; return true;}
|
||||
//! \relatesalso PIByteArray \brief Byte arrays compare operator
|
||||
inline bool operator !=(PIByteArray & f, PIByteArray & s) {if (f.size_s() != s.size_s()) return true; for (int i = 0; i < f.size_s(); ++i) if (f[i] != s[i]) return true; return false;}
|
||||
|
||||
#endif // PIBYTEARRAY_H
|
||||
221
src/core/pichar.h
Executable file
221
src/core/pichar.h
Executable file
@@ -0,0 +1,221 @@
|
||||
/*! \file pichar.h
|
||||
* \brief Unicode char
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Unicode char
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PICHAR_H
|
||||
#define PICHAR_H
|
||||
|
||||
#include "pibytearray.h"
|
||||
/*! \brief Unicode char
|
||||
* \details This class is wrapper around \c "uint".
|
||||
* There are many contructors and information functions
|
||||
*/
|
||||
class PIP_EXPORT PIChar
|
||||
{
|
||||
friend class PIString;
|
||||
friend PIByteArray & operator <<(PIByteArray & s, const PIChar & v);
|
||||
friend PIByteArray & operator >>(PIByteArray & s, PIChar & v);
|
||||
public:
|
||||
//! Contructs ascii symbol
|
||||
PIChar(const char c) {ch = c; ch &= 0xFF;}
|
||||
|
||||
//! Contructs 2-bytes symbol
|
||||
PIChar(const short c) {ch = c; ch &= 0xFFFF;}
|
||||
|
||||
//! Contructs 4-bytes symbol
|
||||
PIChar(const int c) {ch = c;}
|
||||
|
||||
//! Contructs ascii symbol
|
||||
PIChar(const uchar c) {ch = c; ch &= 0xFF;}
|
||||
|
||||
//! Contructs 2-bytes symbol
|
||||
PIChar(const ushort c) {ch = c; ch &= 0xFFFF;}
|
||||
|
||||
//! Default constructor. Contructs 4-bytes symbol
|
||||
PIChar(const uint c = 0) {ch = c;}
|
||||
|
||||
//! Contructs symbol from no more than 4 bytes of string
|
||||
PIChar(const char * c) {ch = *reinterpret_cast<const int * >(c);}
|
||||
|
||||
//inline operator const int() {return static_cast<const int>(ch);}
|
||||
//inline operator const char() {return toAscii();}
|
||||
|
||||
//! Copy operator
|
||||
PIChar & operator =(const char v) {ch = v; return *this;}
|
||||
/*inline PIChar & operator =(const short v) {ch = v; return *this;}
|
||||
inline PIChar & operator =(const int v) {ch = v; return *this;}
|
||||
inline PIChar & operator =(const uchar v) {ch = v; return *this;}
|
||||
inline PIChar & operator =(const ushort v) {ch = v; return *this;}
|
||||
inline PIChar & operator =(const uint v) {ch = v; return *this;}*/
|
||||
|
||||
//! Compare operator
|
||||
bool operator ==(const PIChar & o) const {return strcmp(o.toCharPtr(), toCharPtr()) == 0;}
|
||||
/*inline bool operator ==(const PIChar & o) const {if (o.isAscii() ^ isAscii()) return false;
|
||||
if (isAscii()) return (o.toAscii() == toAscii());
|
||||
return (o.toInt() == toInt());}
|
||||
inline bool operator ==(const char o) const {return (PIChar(o) == *this);}
|
||||
inline bool operator ==(const short o) const {return (PIChar(o) == *this);}
|
||||
inline bool operator ==(const int o) const {return (PIChar(o) == *this);}
|
||||
inline bool operator ==(const uchar o) const {return (PIChar(o) == *this);}
|
||||
inline bool operator ==(const ushort o) const {return (PIChar(o) == *this);}
|
||||
inline bool operator ==(const uint o) const {return (PIChar(o) == *this);}*/
|
||||
|
||||
//! Compare operator
|
||||
bool operator !=(const PIChar & o) const {return !(o == *this);}
|
||||
/*inline bool operator !=(const char o) const {return (PIChar(o) != *this);}
|
||||
inline bool operator !=(const short o) const {return (PIChar(o) != *this);}
|
||||
inline bool operator !=(const int o) const {return (PIChar(o) != *this);}
|
||||
inline bool operator !=(const uchar o) const {return (PIChar(o) != *this);}
|
||||
inline bool operator !=(const ushort o) const {return (PIChar(o) != *this);}
|
||||
inline bool operator !=(const uint o) const {return (PIChar(o) != *this);}*/
|
||||
|
||||
//! Compare operator
|
||||
bool operator >(const PIChar & o) const {return strcmp(o.toCharPtr(), toCharPtr()) < 0;}
|
||||
|
||||
//! Compare operator
|
||||
bool operator <(const PIChar & o) const {return strcmp(o.toCharPtr(), toCharPtr()) > 0;}
|
||||
|
||||
//! Compare operator
|
||||
bool operator >=(const PIChar & o) const {return strcmp(o.toCharPtr(), toCharPtr()) <= 0;}
|
||||
|
||||
//! Compare operator
|
||||
bool operator <=(const PIChar & o) const {return strcmp(o.toCharPtr(), toCharPtr()) >= 0;}
|
||||
|
||||
//! Return \b true if symbol is digit ('0' to '9')
|
||||
bool isDigit() const {return isdigit(ch) != 0;}
|
||||
|
||||
//! Return \b true if symbol is HEX digit ('0' to '9', 'a' to 'f', 'A' to 'F')
|
||||
bool isHex() const {return isxdigit(ch) != 0;}
|
||||
|
||||
//! Return \b true if symbol is drawable (without space)
|
||||
bool isGraphical() const {return isgraph(ch) != 0;}
|
||||
|
||||
//! Return \b true if symbol is control byte (< 32 or 127)
|
||||
bool isControl() const {return iscntrl(ch) != 0;}
|
||||
|
||||
//! Return \b true if symbol is in lower case
|
||||
bool isLower() const {return islower(ch) != 0;}
|
||||
|
||||
//! Return \b true if symbol is in upper case
|
||||
bool isUpper() const {return isupper(ch) != 0;}
|
||||
|
||||
//! Return \b true if symbol is printable (with space)
|
||||
bool isPrint() const {return isprint(ch) != 0;}
|
||||
|
||||
//! Return \b true if symbol is space or tab
|
||||
bool isSpace() const {return isspace(ch) != 0;}
|
||||
|
||||
//! Return \b true if symbol is alphabetical letter
|
||||
bool isAlpha() const {return isalpha(ch) != 0;}
|
||||
|
||||
//! Return \b true if symbol is ascii (< 128)
|
||||
bool isAscii() const {return isascii(ch) != 0;}
|
||||
|
||||
int toInt() const {return int(ch);}
|
||||
const wchar_t * toWCharPtr() const {return reinterpret_cast<const wchar_t * >(&ch);}
|
||||
|
||||
//! Return as <tt>"char * "</tt> string
|
||||
const char * toCharPtr() const {return reinterpret_cast<const char * >(&ch);}
|
||||
|
||||
wchar_t toWChar() const {return wchar_t(ch);}
|
||||
char toAscii() const {return ch % 256;}
|
||||
int unicode16Code() const {wchar_t wc; if (mbtowc(&wc, toCharPtr(), 4) > 0) return wc; return 0;}
|
||||
//#ifdef WINDOWS
|
||||
// inline PIChar toUpper() const __attribute__ ((optimize(0))) {return PIChar(toupper(ch));}
|
||||
// inline PIChar toLower() const __attribute__ ((optimize(0))) {return PIChar(tolower(ch));}
|
||||
//#else
|
||||
|
||||
//! Return symbol in upper case
|
||||
PIChar toUpper() const {return PIChar(toupper(ch));}
|
||||
|
||||
//! Return symbol in lower case
|
||||
PIChar toLower() const {return PIChar(tolower(ch));}
|
||||
//#endif
|
||||
|
||||
private:
|
||||
uint ch;
|
||||
|
||||
};
|
||||
|
||||
__PICONTAINERS_SIMPLE_TYPE__(PIChar)
|
||||
|
||||
//! Output operator to \c std::ostream
|
||||
inline std::ostream & operator <<(std::ostream & s, const PIChar & v) {s << v.toCharPtr(); return s;}
|
||||
|
||||
//! Output operator to \a PICout
|
||||
inline PICout operator <<(PICout s, const PIChar & v) {s.space(); s.setControl(0, true); s << v.toCharPtr(); s.restoreControl(); return s;}
|
||||
|
||||
|
||||
//! Write operator to \c PIByteArray
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PIChar & v) {s << uint(v.ch); return s;}
|
||||
|
||||
//! Read operator from \c PIByteArray
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PIChar & v) {uint i; s >> i; v.ch = wchar_t(i); return s;}
|
||||
|
||||
|
||||
//! Compare operator
|
||||
inline bool operator ==(const char v, const PIChar & c) {return (PIChar(v) == c);}
|
||||
|
||||
//! Compare operator
|
||||
inline bool operator >(const char v, const PIChar & c) {return (PIChar(v) > c);}
|
||||
|
||||
//! Compare operator
|
||||
inline bool operator <(const char v, const PIChar & c) {return (PIChar(v) < c);}
|
||||
|
||||
//! Compare operator
|
||||
inline bool operator >=(const char v, const PIChar & c) {return (PIChar(v) >= c);}
|
||||
|
||||
//! Compare operator
|
||||
inline bool operator <=(const char v, const PIChar & c) {return (PIChar(v) <= c);}
|
||||
|
||||
|
||||
//! Compare operator
|
||||
inline bool operator ==(const char * v, const PIChar & c) {return (PIChar(v) == c);}
|
||||
|
||||
//! Compare operator
|
||||
inline bool operator >(const char * v, const PIChar & c) {return (PIChar(v) > c);}
|
||||
|
||||
//! Compare operator
|
||||
inline bool operator <(const char * v, const PIChar & c) {return (PIChar(v) < c);}
|
||||
|
||||
//! Compare operator
|
||||
inline bool operator >=(const char * v, const PIChar & c) {return (PIChar(v) >= c);}
|
||||
|
||||
//! Compare operator
|
||||
inline bool operator <=(const char * v, const PIChar & c) {return (PIChar(v) <= c);}
|
||||
|
||||
|
||||
//! Compare operator
|
||||
inline bool operator ==(const int v, const PIChar & c) {return (PIChar(v) == c);}
|
||||
|
||||
//! Compare operator
|
||||
inline bool operator >(const int v, const PIChar & c) {return (PIChar(v) > c);}
|
||||
|
||||
//! Compare operator
|
||||
inline bool operator <(const int v, const PIChar & c) {return (PIChar(v) < c);}
|
||||
|
||||
//! Compare operator
|
||||
inline bool operator >=(const int v, const PIChar & c) {return (PIChar(v) >= c);}
|
||||
|
||||
//! Compare operator
|
||||
inline bool operator <=(const int v, const PIChar & c) {return (PIChar(v) <= c);}
|
||||
|
||||
#endif // PICHAR_H
|
||||
101
src/core/picli.cpp
Executable file
101
src/core/picli.cpp
Executable file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Command-Line Parser
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "picli.h"
|
||||
#include "pisysteminfo.h"
|
||||
|
||||
|
||||
/*! \class PICLI
|
||||
* \brief Command-line arguments parser
|
||||
*
|
||||
* \section PICLI_sec0 Synopsis
|
||||
* This class provide handy parsing of command-line arguments. First you should add
|
||||
* arguments to PICLI with function \a addArgument(). Then you can check if there
|
||||
* is some argument in application command-line with function \a hasArgument();
|
||||
* \section PICLI_sec1 Example
|
||||
* \snippet picli.cpp main
|
||||
*/
|
||||
|
||||
|
||||
PICLI::PICLI(int argc, char * argv[]) {
|
||||
setName("CLI");
|
||||
needParse = true;
|
||||
_prefix_short = "-";
|
||||
_prefix_full = "--";
|
||||
_count_opt = 0;
|
||||
_count_mand = 0;
|
||||
for (int i = 0; i < argc; ++i)
|
||||
_args_raw << argv[i];
|
||||
if (argc > 0)
|
||||
PISystemInfo::instance()->execCommand = argv[0];
|
||||
}
|
||||
|
||||
|
||||
void PICLI::parse() {
|
||||
if (!needParse) return;
|
||||
PIString cra, full;
|
||||
Argument * last = 0;
|
||||
for (int i = 1; i < _args_raw.size_s(); ++i) {
|
||||
cra = _args_raw[i];
|
||||
if (cra.left(2) == _prefix_full) {
|
||||
last = 0;
|
||||
full = cra.right(cra.length() - 2);
|
||||
piForeach (Argument & a, _args) {
|
||||
if (a.full_key == full) {
|
||||
a.found = true;
|
||||
last = &a;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (cra.left(1) == _prefix_short) {
|
||||
last = 0;
|
||||
for (int j = 1; j < cra.length(); ++j) {
|
||||
bool found = false;
|
||||
piForeach (Argument & a, _args) {
|
||||
if (a.short_key == cra[j]) {
|
||||
a.found = true;
|
||||
last = &a;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) break;
|
||||
}
|
||||
} else {
|
||||
if (last == 0 ? true : !last->has_value) {
|
||||
if (_args_mand.size_s() < _count_mand) {
|
||||
_args_mand << cra;
|
||||
continue;
|
||||
}
|
||||
if (_args_opt.size_s() < _count_opt || _count_opt < 0) {
|
||||
_args_opt << cra;
|
||||
continue;
|
||||
}
|
||||
piCoutObj << "[PICli] Arguments overflow, \"" << cra << "\" ignored";
|
||||
}
|
||||
if (last == 0 ? false : last->has_value) {
|
||||
last->value = cra;
|
||||
last = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
needParse = false;
|
||||
}
|
||||
101
src/core/picli.h
Executable file
101
src/core/picli.h
Executable file
@@ -0,0 +1,101 @@
|
||||
/*! \file picli.h
|
||||
* \brief Command-Line parser
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Command-Line Parser
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PICLI_H
|
||||
#define PICLI_H
|
||||
|
||||
#include "piobject.h"
|
||||
|
||||
class PIP_EXPORT PICLI: public PIObject
|
||||
{
|
||||
PIOBJECT(PICLI)
|
||||
public:
|
||||
|
||||
//! Constructor
|
||||
PICLI(int argc, char * argv[]);
|
||||
|
||||
|
||||
//! Add argument with name "name", short key = name first letter, full key = name
|
||||
void addArgument(const PIString & name, bool value = false) {_args << Argument(name, name[0], name, value); needParse = true;}
|
||||
|
||||
//! Add argument with name "name", short key = "shortKey", full key = name
|
||||
void addArgument(const PIString & name, const PIChar & shortKey, bool value = false) {_args << Argument(name, shortKey, name, value); needParse = true;}
|
||||
|
||||
//! Add argument with name "name", short key = "shortKey", full key = name
|
||||
void addArgument(const PIString & name, const char * shortKey, bool value = false) {_args << Argument(name, PIChar(shortKey), name, value); needParse = true;}
|
||||
|
||||
//! Add argument with name "name", short key = "shortKey", full key = "fullKey"
|
||||
void addArgument(const PIString & name, const PIChar & shortKey, const PIString & fullKey, bool value = false) {_args << Argument(name, shortKey, fullKey, value); needParse = true;}
|
||||
|
||||
//! Add argument with name "name", short key = "shortKey", full key = "fullKey"
|
||||
void addArgument(const PIString & name, const char * shortKey, const PIString & fullKey, bool value = false) {_args << Argument(name, PIChar(shortKey), fullKey, value); needParse = true;}
|
||||
|
||||
|
||||
//! Returns unparsed command-line argument by index "index". Index 0 is program execute command.
|
||||
PIString rawArgument(int index) {parse(); return _args_raw[index];}
|
||||
PIString mandatoryArgument(int index) {parse(); return _args_mand[index];}
|
||||
PIString optionalArgument(int index) {parse(); return _args_opt[index];}
|
||||
|
||||
//! Returns unparsed command-line arguments
|
||||
const PIStringList & rawArguments() {parse(); return _args_raw;}
|
||||
const PIStringList & mandatoryArguments() {parse(); return _args_mand;}
|
||||
const PIStringList & optionalArguments() {parse(); return _args_opt;}
|
||||
|
||||
//! Returns program execute command without arguments
|
||||
PIString programCommand() {parse(); return _args_raw.size() > 0 ? _args_raw.front() : PIString();}
|
||||
bool hasArgument(const PIString & name) {parse(); piForeach (Argument & i, _args) if (i.name == name && i.found) return true; return false;}
|
||||
PIString argumentValue(const PIString & name) {parse(); piForeach (Argument &i, _args) if (i.name == name && i.found) return i.value; return PIString();}
|
||||
PIString argumentShortKey(const PIString & name) {piForeach (Argument &i, _args) if (i.name == name) return i.short_key; return PIString();}
|
||||
PIString argumentFullKey(const PIString & name) {piForeach (Argument &i, _args) if (i.name == name) return i.full_key; return PIString();}
|
||||
|
||||
const PIString & shortKeyPrefix() const {return _prefix_short;}
|
||||
const PIString & fullKeyPrefix() const {return _prefix_full;}
|
||||
int mandatoryArgumentsCount() const {return _count_mand;}
|
||||
int optionalArgumentsCount() const {return _count_opt;}
|
||||
void setShortKeyPrefix(const PIString & prefix) {_prefix_short = prefix; needParse = true;}
|
||||
void setFullKeyPrefix(const PIString & prefix) {_prefix_full = prefix; needParse = true;}
|
||||
void setMandatoryArgumentsCount(const int count) {_count_mand = count; needParse = true;}
|
||||
void setOptionalArgumentsCount(const int count) {_count_opt = count; needParse = true;}
|
||||
|
||||
private:
|
||||
struct Argument {
|
||||
Argument() {has_value = found = false;}
|
||||
Argument(const PIString & n, const PIChar & s, const PIString & f, bool v) {name = n; short_key = s; full_key = f; has_value = v; found = false;}
|
||||
PIString name;
|
||||
PIChar short_key;
|
||||
PIString full_key;
|
||||
PIString value;
|
||||
bool has_value, found;
|
||||
};
|
||||
|
||||
void parse();
|
||||
|
||||
PIString _prefix_short, _prefix_full;
|
||||
PIStringList _args_raw, _args_mand, _args_opt;
|
||||
PISet<PIString> keys_full, keys_short;
|
||||
PIVector<Argument> _args;
|
||||
int _count_mand, _count_opt;
|
||||
bool needParse;
|
||||
|
||||
};
|
||||
|
||||
#endif // PICLI_H
|
||||
57
src/core/picollection.cpp
Executable file
57
src/core/picollection.cpp
Executable file
@@ -0,0 +1,57 @@
|
||||
#include "picollection.h"
|
||||
|
||||
|
||||
/** \class PICollection
|
||||
* \brief Interface to discover element groups
|
||||
* \details
|
||||
* \section PICollection_sec0 Synopsis
|
||||
* This class has only static functions so no need to create instance of the
|
||||
* %PICollection. This class provide macros to add some classes or existing
|
||||
* objects to global collection and access to them from any place of the code.
|
||||
* \snippet picollection.cpp main
|
||||
* */
|
||||
|
||||
|
||||
PIStringList PICollection::groups() {
|
||||
PIStringList sl;
|
||||
piForeachC (Group & g, *_groups)
|
||||
sl << g.name;
|
||||
return sl;
|
||||
}
|
||||
|
||||
|
||||
PIVector<const PIObject * > PICollection::groupElements(const PIString & group) {
|
||||
piForeachC (Group & g, *_groups)
|
||||
if (g.name == group)
|
||||
return g.elements;
|
||||
return PIVector<const PIObject * >();
|
||||
}
|
||||
|
||||
|
||||
void PICollection::addToGroup(const PIString & group, const PIObject * element) {
|
||||
//piCout << "add to" << group << element;
|
||||
PIString n = element->className();
|
||||
piForeach (Group & g, *_groups)
|
||||
if (g.name == group) {
|
||||
for (int i = 0; i < g.elements.size_s(); ++i)
|
||||
if (PIString(g.elements[i]->className()) == n)
|
||||
return;
|
||||
g.elements << element;
|
||||
//piCout << "new group" << group << ", ok";
|
||||
return;
|
||||
}
|
||||
*_groups << Group(group);
|
||||
_groups->back().elements << element;
|
||||
//piCout << "new group" << group << ", ok";
|
||||
}
|
||||
|
||||
|
||||
PICollection::CollectionAdder::CollectionAdder(const PIString & group, const PIObject * element, const PIString & name) {
|
||||
if (element == 0) return;
|
||||
const_cast<PIObject * >(element)->setName(name);
|
||||
PICollection::addToGroup(group, element);
|
||||
}
|
||||
|
||||
|
||||
bool __PICollectionInitializer::_inited_(false);
|
||||
PIVector<PICollection::Group> * PICollection::_groups;
|
||||
96
src/core/picollection.h
Executable file
96
src/core/picollection.h
Executable file
@@ -0,0 +1,96 @@
|
||||
/*! \file picollection.h
|
||||
* \brief Custom elements collection
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Peer - named I/O ethernet node, forming self-organized peering network
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PICOLLECTION_H
|
||||
#define PICOLLECTION_H
|
||||
|
||||
#include "piobject.h"
|
||||
|
||||
#ifdef DOXYGEN
|
||||
|
||||
/** \brief Add existing element "object" in group with name "group"
|
||||
* \relatesalso PICollection
|
||||
* \details If there is no group with name "group" it will be created.
|
||||
* Only one element of the class "object" can be in group "group". If
|
||||
* this is already exists nothing be happens. \n "object" should to
|
||||
* be pointer to object based on PIObject. */
|
||||
# define ADD_TO_COLLECTION(group, object)
|
||||
|
||||
/** \brief Add new element of class "class" in group with name "group"
|
||||
* \relatesalso PICollection
|
||||
* \details If there is no group with name "group" it will be created.
|
||||
* Only one element of the class "class" can be in group "group". If
|
||||
* this is already exists nothing be happens. \n "class" should to
|
||||
* be name of the any class based on PIObject. */
|
||||
# define ADD_NEW_TO_COLLECTION(group, class)
|
||||
|
||||
#else
|
||||
# define ADD_TO_COLLECTION(group, object) static PICollection::CollectionAdder __##group##_##__LINE__##_##adder##__(#group, object);
|
||||
# define ADD_TO_COLLECTION_WITH_NAME(group, object, name) static PICollection::CollectionAdder __##group##_##__LINE__##_##adder##__(#group, object, #name);
|
||||
# define ADD_NEW_TO_COLLECTION(group, class) static PICollection::CollectionAdder __##group##_##class##_##adder##__(#group, new class());
|
||||
# define ADD_NEW_TO_COLLECTION_WITH_NAME(group, class, name) static PICollection::CollectionAdder __##group##_##class##_##adder##__(#group, new class(), #name);
|
||||
#endif
|
||||
|
||||
class PIP_EXPORT PICollection
|
||||
{
|
||||
friend class __PICollectionInitializer;
|
||||
public:
|
||||
PICollection() {;}
|
||||
|
||||
//! \brief Returns all existing groups by their names
|
||||
static PIStringList groups();
|
||||
|
||||
//! \brief Returns all elements of group "group"
|
||||
static PIVector<const PIObject * > groupElements(const PIString & group);
|
||||
|
||||
static void addToGroup(const PIString & group, const PIObject * element);
|
||||
|
||||
class CollectionAdder {
|
||||
public:
|
||||
CollectionAdder(const PIString & group, const PIObject * element, const PIString & name = PIString());
|
||||
};
|
||||
|
||||
protected:
|
||||
struct Group {
|
||||
Group(const PIString & name_ = PIString()) {name = name_;}
|
||||
//~Group() {piCout << "delete group" << name << this; piForeach (const PIObject * o, elements) delete o; elements.clear();}
|
||||
PIString name;
|
||||
PIVector<const PIObject * > elements;
|
||||
};
|
||||
|
||||
static PIVector<Group> * _groups;
|
||||
|
||||
};
|
||||
|
||||
class PIP_EXPORT __PICollectionInitializer {
|
||||
public:
|
||||
__PICollectionInitializer() {
|
||||
if (_inited_) return;
|
||||
_inited_ = true;
|
||||
PICollection::_groups = new PIVector<PICollection::Group>();
|
||||
}
|
||||
static bool _inited_;
|
||||
};
|
||||
|
||||
static __PICollectionInitializer __picollectioninitializer;
|
||||
|
||||
#endif // PICOLLECTION_H
|
||||
348
src/core/picout.cpp
Normal file
348
src/core/picout.cpp
Normal file
@@ -0,0 +1,348 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Universal output to console class
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "picout.h"
|
||||
#include "piconsole.h"
|
||||
|
||||
/*! \class PICout
|
||||
* \brief Class for formatted output similar std::cout
|
||||
*
|
||||
* \section PICout_sec0 Synopsis
|
||||
* This class provide many stream operators for output with some features.
|
||||
* Output to PICout is thread-sequential, i.e. doesn`t mixed from parallel
|
||||
* threads.
|
||||
*
|
||||
* \section PICout_sec1 Features
|
||||
* - insertion spaces between entries
|
||||
* - insertion new line at the end of output
|
||||
* - strings are quoted
|
||||
* - custom output operator can be easily written
|
||||
*
|
||||
* \section PICout_ex0 Usage
|
||||
* \snippet picout.cpp 0
|
||||
*
|
||||
* \section PICout_ex1 Writing your own output operator
|
||||
* \snippet picout.cpp own
|
||||
*/
|
||||
|
||||
PIMutex __PICout_mutex__;
|
||||
PIString __PICout_string__;
|
||||
|
||||
#ifdef WINDOWS
|
||||
void * PICout::hOut = 0;
|
||||
WORD PICout::dattr = 0;
|
||||
DWORD PICout::smode = 0;
|
||||
#endif
|
||||
|
||||
bool PICout::buffer_ = false;
|
||||
|
||||
|
||||
PICout::PICout(PIFlags<PICoutControl> controls): fo_(true), cc_(false), fc_(false), cnb_(10), co_(controls) {
|
||||
#ifdef WINDOWS
|
||||
if (hOut == 0) {
|
||||
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
CONSOLE_SCREEN_BUFFER_INFO sbi;
|
||||
GetConsoleScreenBufferInfo(hOut, &sbi);
|
||||
dattr = sbi.wAttributes;
|
||||
}
|
||||
attr_ = dattr;
|
||||
#endif
|
||||
__PICout_mutex__.lock();
|
||||
}
|
||||
|
||||
|
||||
PICout::~PICout() {
|
||||
if (fc_) applyFormat(PICoutManipulators::Default);
|
||||
if (cc_) return;
|
||||
newLine();
|
||||
__PICout_mutex__.unlock();
|
||||
}
|
||||
|
||||
|
||||
PICout PICout::operator <<(const PICoutAction v) {
|
||||
#ifdef WINDOWS
|
||||
CONSOLE_SCREEN_BUFFER_INFO sbi;
|
||||
COORD coord;
|
||||
CONSOLE_CURSOR_INFO curinfo;
|
||||
#endif
|
||||
switch (v) {
|
||||
case PICoutManipulators::Flush:
|
||||
if (!PICout::buffer_)
|
||||
std::cout << std::flush;
|
||||
break;
|
||||
case PICoutManipulators::Backspace:
|
||||
if (!PICout::buffer_) {
|
||||
#ifdef WINDOWS
|
||||
GetConsoleScreenBufferInfo(hOut, &sbi);
|
||||
coord = sbi.dwCursorPosition;
|
||||
coord.X = piMax<int>(0, int(coord.X) - 1);
|
||||
SetConsoleCursorPosition(hOut, coord);
|
||||
printf(" ");
|
||||
SetConsoleCursorPosition(hOut, coord);
|
||||
#else
|
||||
printf("\e[1D \e[1D");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case PICoutManipulators::ShowCursor:
|
||||
if (!PICout::buffer_) {
|
||||
#ifdef WINDOWS
|
||||
GetConsoleCursorInfo(hOut, &curinfo);
|
||||
curinfo.bVisible = true;
|
||||
SetConsoleCursorInfo(hOut, &curinfo);
|
||||
#else
|
||||
printf("\e[?25h");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case PICoutManipulators::HideCursor:
|
||||
if (!PICout::buffer_) {
|
||||
#ifdef WINDOWS
|
||||
GetConsoleCursorInfo(hOut, &curinfo);
|
||||
curinfo.bVisible = false;
|
||||
SetConsoleCursorInfo(hOut, &curinfo);
|
||||
#else
|
||||
printf("\e[?25l");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case PICoutManipulators::ClearScreen:
|
||||
if (!PICout::buffer_) {
|
||||
#ifdef WINDOWS
|
||||
/// TODO !!!
|
||||
/*GetConsoleCursorInfo(hOut, &curinfo);
|
||||
curinfo.bVisible = false;
|
||||
SetConsoleCursorInfo(hOut, &curinfo);
|
||||
|
||||
SetConsoleCursorPosition(hOut, ulcoord);
|
||||
FillConsoleOutputAttribute(hOut, dattr, width * (height + 1), ulcoord, &written);
|
||||
FillConsoleOutputCharacter(hOut, ' ', width * (height + 1), ulcoord, &written);*/
|
||||
#else
|
||||
printf("\e[H\e[J");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case PICoutManipulators::SaveContol: saveControl(); break;
|
||||
case PICoutManipulators::RestoreControl: restoreControl(); break;
|
||||
default: break;
|
||||
};
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
#define PICOUTTOTARGET(v) {if (PICout::buffer_) __PICout_string__ << (v); else std::cout << (v);}
|
||||
#define PINUMERICCOUT if (cnb_ == 10) PICOUTTOTARGET(v) else PICOUTTOTARGET(PIString::fromNumber(v, cnb_))
|
||||
|
||||
|
||||
PICout PICout::operator <<(const char * v) {if (v == '\0') return *this; space(); quote(); PICOUTTOTARGET(v) quote(); return *this;}
|
||||
|
||||
PICout PICout::operator <<(const string & v) {space(); quote(); PICOUTTOTARGET(v) quote(); return *this;}
|
||||
|
||||
PICout PICout::operator <<(const bool v) {space(); if (v) PICOUTTOTARGET("true") else PICOUTTOTARGET("false") return *this;}
|
||||
|
||||
PICout PICout::operator <<(const char v) {space(); PICOUTTOTARGET(v) return *this;}
|
||||
|
||||
PICout PICout::operator <<(const uchar v) {space(); if (cnb_ == 10) PICOUTTOTARGET(ushort(v)) else PICOUTTOTARGET(PIString::fromNumber(v, cnb_)) return *this;}
|
||||
|
||||
PICout PICout::operator <<(const short int v) {space(); PINUMERICCOUT return *this;}
|
||||
|
||||
PICout PICout::operator <<(const ushort v) {space(); PINUMERICCOUT return *this;}
|
||||
|
||||
PICout PICout::operator <<(const int v) {space(); PINUMERICCOUT return *this;}
|
||||
|
||||
PICout PICout::operator <<(const uint v) {space(); PINUMERICCOUT return *this;}
|
||||
|
||||
PICout PICout::operator <<(const long v) {space(); PINUMERICCOUT return *this;}
|
||||
|
||||
PICout PICout::operator <<(const ulong v) {space(); PINUMERICCOUT return *this;}
|
||||
|
||||
PICout PICout::operator <<(const llong v) {space(); PINUMERICCOUT return *this;}
|
||||
|
||||
PICout PICout::operator <<(const ullong v) {space(); PINUMERICCOUT return *this;}
|
||||
|
||||
PICout PICout::operator <<(const float v) {space(); PICOUTTOTARGET(v) return *this;}
|
||||
|
||||
PICout PICout::operator <<(const double v) {space(); PICOUTTOTARGET(v) return *this;}
|
||||
|
||||
PICout PICout::operator <<(const void * v) {space(); PICOUTTOTARGET("0x") PICOUTTOTARGET(PIString::fromNumber(ullong(v), 16)) return *this;}
|
||||
|
||||
PICout PICout::operator <<(const PIObject * v) {
|
||||
space();
|
||||
if (v == 0) PICOUTTOTARGET("PIObject*(0x0)")
|
||||
else {
|
||||
PICOUTTOTARGET(v->className())
|
||||
PICOUTTOTARGET("*(0x")
|
||||
PICOUTTOTARGET(PIString::fromNumber(ullong(v), 16))
|
||||
PICOUTTOTARGET(", \"")
|
||||
PICOUTTOTARGET(v->name())
|
||||
PICOUTTOTARGET("\")")
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
PICout PICout::operator <<(const PICoutSpecialChar v) {
|
||||
switch (v) {
|
||||
case Null:
|
||||
if (PICout::buffer_) __PICout_string__ << PIChar(0);
|
||||
else std::cout << char(0);
|
||||
break;
|
||||
case NewLine:
|
||||
if (PICout::buffer_) __PICout_string__ << "\n";
|
||||
else std::cout << '\n';
|
||||
fo_ = true;
|
||||
break;
|
||||
case Tab:
|
||||
if (PICout::buffer_) __PICout_string__ << "\t";
|
||||
else std::cout << '\t';
|
||||
break;
|
||||
case Esc:
|
||||
#ifdef CC_VC
|
||||
if (PICout::buffer_) __PICout_string__ << PIChar(27);
|
||||
else std::cout << char(27);
|
||||
#else
|
||||
if (PICout::buffer_) __PICout_string__ << "\e";
|
||||
else std::cout << '\e';
|
||||
#endif
|
||||
break;
|
||||
case Quote:
|
||||
if (PICout::buffer_) __PICout_string__ << "\"";
|
||||
else std::cout << '"';
|
||||
break;
|
||||
};
|
||||
return *this;
|
||||
}
|
||||
|
||||
#undef PICOUTTOTARGET
|
||||
#undef PINUMERICCOUT
|
||||
|
||||
PICout & PICout::space() {
|
||||
if (!fo_ && co_[AddSpaces]) {
|
||||
if (PICout::buffer_) __PICout_string__ << " ";
|
||||
else std::cout << ' ';
|
||||
}
|
||||
fo_ = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PICout & PICout::quote() {
|
||||
if (co_[AddQuotes]) {
|
||||
if (PICout::buffer_) __PICout_string__ << "\"";
|
||||
else std::cout << '"';
|
||||
}
|
||||
fo_ = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PICout & PICout::newLine() {
|
||||
if (co_[AddNewLine]) {
|
||||
if (PICout::buffer_) __PICout_string__ << "\n";
|
||||
else std::cout << std::endl;
|
||||
}
|
||||
fo_ = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void PICout::applyFormat(PICoutFormat f) {
|
||||
if (PICout::buffer_) return;
|
||||
fc_ = true;
|
||||
#ifdef WINDOWS
|
||||
static int mask_fore = ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
||||
static int mask_back = ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE);
|
||||
switch (f) {
|
||||
case Bin: case Oct: case Dec: case Hex: break;
|
||||
case PICoutManipulators::Bold: attr_ |= FOREGROUND_INTENSITY; break;
|
||||
case PICoutManipulators::Underline: attr_ |= COMMON_LVB_UNDERSCORE; break;
|
||||
case PICoutManipulators::Black: attr_ = (attr_ & mask_fore); break;
|
||||
case PICoutManipulators::Red: attr_ = (attr_ & mask_fore) | FOREGROUND_RED; break;
|
||||
case PICoutManipulators::Green: attr_ = (attr_ & mask_fore) | FOREGROUND_GREEN; break;
|
||||
case PICoutManipulators::Blue: attr_ = (attr_ & mask_fore) | FOREGROUND_BLUE; break;
|
||||
case PICoutManipulators::Yellow: attr_ = (attr_ & mask_fore) | FOREGROUND_RED | FOREGROUND_GREEN; break;
|
||||
case PICoutManipulators::Magenta: attr_ = (attr_ & mask_fore) | FOREGROUND_RED | FOREGROUND_BLUE; break;
|
||||
case PICoutManipulators::Cyan: attr_ = (attr_ & mask_fore) | FOREGROUND_GREEN | FOREGROUND_BLUE; break;
|
||||
case PICoutManipulators::White: attr_ = (attr_ & mask_fore) | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; break;
|
||||
case PICoutManipulators::BackBlack: attr_ = (attr_ & mask_back); break;
|
||||
case PICoutManipulators::BackRed: attr_ = (attr_ & mask_back) | BACKGROUND_RED; break;
|
||||
case PICoutManipulators::BackGreen: attr_ = (attr_ & mask_back) | BACKGROUND_GREEN; break;
|
||||
case PICoutManipulators::BackBlue: attr_ = (attr_ & mask_back) | BACKGROUND_BLUE; break;
|
||||
case PICoutManipulators::BackYellow: attr_ = (attr_ & mask_back) | BACKGROUND_RED | BACKGROUND_GREEN; break;
|
||||
case PICoutManipulators::BackMagenta: attr_ = (attr_ & mask_back) | BACKGROUND_RED | BACKGROUND_BLUE; break;
|
||||
case PICoutManipulators::BackCyan: attr_ = (attr_ & mask_back) | BACKGROUND_GREEN | BACKGROUND_BLUE; break;
|
||||
case PICoutManipulators::BackWhite: attr_ = (attr_ & mask_back) | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE; break;
|
||||
case PICoutManipulators::Default: attr_ = dattr; break;
|
||||
default: break;
|
||||
}
|
||||
SetConsoleTextAttribute(hOut, attr_);
|
||||
#else
|
||||
switch (f) {
|
||||
case Bin: case Oct: case Dec: case Hex: break;
|
||||
case PICoutManipulators::Bold: printf("\e[1m"); break;
|
||||
case PICoutManipulators::Faint: printf("\e[2m"); break;
|
||||
case PICoutManipulators::Italic: printf("\e[3m"); break;
|
||||
case PICoutManipulators::Underline: printf("\e[4m"); break;
|
||||
case PICoutManipulators::Blink: printf("\e[5m"); break;
|
||||
case PICoutManipulators::Black: printf("\e[30m"); break;
|
||||
case PICoutManipulators::Red: printf("\e[31m"); break;
|
||||
case PICoutManipulators::Green: printf("\e[32m"); break;
|
||||
case PICoutManipulators::Blue: printf("\e[34m"); break;
|
||||
case PICoutManipulators::Yellow: printf("\e[33m"); break;
|
||||
case PICoutManipulators::Magenta: printf("\e[35m"); break;
|
||||
case PICoutManipulators::Cyan: printf("\e[36m"); break;
|
||||
case PICoutManipulators::White: printf("\e[37m"); break;
|
||||
case PICoutManipulators::BackBlack: printf("\e[40m"); break;
|
||||
case PICoutManipulators::BackRed: printf("\e[41m"); break;
|
||||
case PICoutManipulators::BackGreen: printf("\e[42m"); break;
|
||||
case PICoutManipulators::BackBlue: printf("\e[44m"); break;
|
||||
case PICoutManipulators::BackYellow: printf("\e[43m"); break;
|
||||
case PICoutManipulators::BackMagenta: printf("\e[45m"); break;
|
||||
case PICoutManipulators::BackCyan: printf("\e[46m"); break;
|
||||
case PICoutManipulators::BackWhite: printf("\e[47m"); break;
|
||||
case PICoutManipulators::Default: printf("\e[0m"); break;
|
||||
default: break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool PICout::setBufferActive(bool on, bool clear) {
|
||||
PIMutexLocker ml(__PICout_mutex__);
|
||||
bool ret = PICout::buffer_;
|
||||
if (clear) __PICout_string__.clear();
|
||||
PICout::buffer_ = on;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool PICout::isBufferActive() {
|
||||
return PICout::buffer_;
|
||||
}
|
||||
|
||||
|
||||
PIString PICout::buffer(bool clear) {
|
||||
PIMutexLocker ml(__PICout_mutex__);
|
||||
PIString ret = __PICout_string__;
|
||||
if (clear) __PICout_string__.clear();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void PICout::clearBuffer() {
|
||||
PIMutexLocker ml(__PICout_mutex__);
|
||||
__PICout_string__.clear();
|
||||
}
|
||||
266
src/core/picout.h
Normal file
266
src/core/picout.h
Normal file
@@ -0,0 +1,266 @@
|
||||
/*! \file picout.h
|
||||
* \brief Universal output to console class
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Universal output to console class
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PICOUT_H
|
||||
#define PICOUT_H
|
||||
|
||||
#include "piincludes.h"
|
||||
|
||||
#ifdef DOXYGEN
|
||||
|
||||
//! \brief Macro used for conditional (piDebug) output to PICout
|
||||
# define piCout
|
||||
|
||||
//! \relatesalso PIObject \brief Macro used for conditional (piDebug and PIObject::debug()) output to PICout for subclasses of PIObject
|
||||
# define piCoutObj
|
||||
|
||||
#else
|
||||
# define piCout if (piDebug) PICout()
|
||||
# define piCoutObj if (piDebug && debug()) PICout() << "" << (PIString("[") + className() + " \"" + name() + "\"]")
|
||||
#endif
|
||||
|
||||
extern PIMutex __PICout_mutex__;
|
||||
extern PIString __PICout_string__;
|
||||
|
||||
//! \brief Namespace contains enums controlled PICout
|
||||
namespace PICoutManipulators {
|
||||
|
||||
//! \brief Enum contains special characters
|
||||
enum PIP_EXPORT PICoutSpecialChar {
|
||||
Null /*! Null-character, '\\0' */,
|
||||
NewLine /*! New line character, '\\n' */,
|
||||
Tab /*! Tab character, '\\t' */,
|
||||
Esc /*! Escape character, '\\e' */,
|
||||
Quote /*! Quote character, '"' */
|
||||
};
|
||||
|
||||
//! \brief Enum contains immediate action
|
||||
enum PIP_EXPORT PICoutAction {
|
||||
Flush /*! Flush the output */,
|
||||
Backspace /*! Remove last symbol */,
|
||||
ShowCursor /*! Show cursor */,
|
||||
HideCursor /*! Hide cursor */,
|
||||
ClearScreen /*! Clear the screen */,
|
||||
SaveContol /*! Save control flags, equivalent to \a saveControl() */,
|
||||
RestoreControl /*! Restore control flags, equivalent to \a restoreControl() */
|
||||
};
|
||||
|
||||
//! \brief Enum contains control of PICout
|
||||
enum PIP_EXPORT PICoutControl {
|
||||
AddNone /*! No controls */ = 0x0,
|
||||
AddSpaces /*! Spaces will be appear after each output */ = 0x1,
|
||||
AddNewLine /*! New line will be appear after all output */ = 0x2,
|
||||
AddQuotes /*! Each string will be quoted */ = 0x4,
|
||||
AddAll /*! All controls */ = 0xFFFFFFFF
|
||||
};
|
||||
|
||||
//! \brief Enum contains output format
|
||||
enum PIP_EXPORT PICoutFormat {
|
||||
Bin /*! Binary representation of integers */ = 0x01,
|
||||
Oct /*! Octal representation of integers */ = 0x02,
|
||||
Dec /*! Decimal representation of integers */ = 0x04,
|
||||
Hex /*! Hexadecimal representation of integers */ = 0x08,
|
||||
Bold /*! Bold */ = 0x10,
|
||||
Faint /*! */ = 0x20,
|
||||
Italic /*! */ = 0x40,
|
||||
Underline /*! Underline */ = 0x80,
|
||||
Blink /*! Blink */ = 0x100,
|
||||
Black /*! Black font */ = 0x400,
|
||||
Red /*! Red font */ = 0x800,
|
||||
Green /*! Green font */ = 0x1000,
|
||||
Blue /*! Blue font */ = 0x2000,
|
||||
Yellow /*! Yellow font */ = 0x4000,
|
||||
Magenta /*! Magenta font */ = 0x8000,
|
||||
Cyan /*! Cyan font */ = 0x10000,
|
||||
White /*! White font */ = 0x20000,
|
||||
BackBlack /*! Black background */ = 0x40000,
|
||||
BackRed /*! Red background */ = 0x80000,
|
||||
BackGreen /*! Green background */ = 0x100000,
|
||||
BackBlue /*! Blue background */ = 0x200000,
|
||||
BackYellow /*! Yellow background */ = 0x400000,
|
||||
BackMagenta /*! Magenta background */ = 0x800000,
|
||||
BackCyan /*! Cyan background */ = 0x1000000,
|
||||
BackWhite /*! White background */ = 0x2000000,
|
||||
Default /*! Default format */ = 0x4000000
|
||||
};
|
||||
};
|
||||
|
||||
using namespace PICoutManipulators;
|
||||
|
||||
typedef PIFlags<PICoutControl> PICoutControls;
|
||||
|
||||
class PIP_EXPORT PICout {
|
||||
public:
|
||||
//! Default constructor with default features (AddSpaces and AddNewLine)
|
||||
PICout(PIFlags<PICoutControl> controls = AddSpaces | AddNewLine);
|
||||
|
||||
PICout(const PICout & other): fo_(other.fo_), cc_(true), fc_(false), cnb_(other.cnb_), attr_(other.attr_), co_(other.co_) {;}
|
||||
~PICout();
|
||||
|
||||
//! Output operator for strings with <tt>"const char * "</tt> type
|
||||
PICout operator <<(const char * v);
|
||||
|
||||
//! Output operator for strings with <tt>"std::string"</tt> type
|
||||
PICout operator <<(const string & v);
|
||||
|
||||
//! Output operator for boolean values
|
||||
PICout operator <<(const bool v);
|
||||
|
||||
//! Output operator for <tt>"char"</tt> values
|
||||
PICout operator <<(const char v);
|
||||
|
||||
//! Output operator for <tt>"unsigned char"</tt> values
|
||||
PICout operator <<(const uchar v);
|
||||
|
||||
//! Output operator for <tt>"short"</tt> values
|
||||
PICout operator <<(const short v);
|
||||
|
||||
//! Output operator for <tt>"unsigned short"</tt> values
|
||||
PICout operator <<(const ushort v);
|
||||
|
||||
//! Output operator for <tt>"int"</tt> values
|
||||
PICout operator <<(const int v);
|
||||
|
||||
//! Output operator for <tt>"unsigned int"</tt> values
|
||||
PICout operator <<(const uint v);
|
||||
|
||||
//! Output operator for <tt>"long"</tt> values
|
||||
PICout operator <<(const long v);
|
||||
|
||||
//! Output operator for <tt>"unsigned long"</tt> values
|
||||
PICout operator <<(const ulong v);
|
||||
|
||||
//! Output operator for <tt>"long long"</tt> values
|
||||
PICout operator <<(const llong v);
|
||||
|
||||
//! Output operator for <tt>"unsigned long long"</tt> values
|
||||
PICout operator <<(const ullong v);
|
||||
|
||||
//! Output operator for <tt>"float"</tt> values
|
||||
PICout operator <<(const float v);
|
||||
|
||||
//! Output operator for <tt>"double"</tt> values
|
||||
PICout operator <<(const double v);
|
||||
|
||||
//! Output operator for pointers
|
||||
PICout operator <<(const void * v);
|
||||
|
||||
//! Output operator for PIObject and ancestors
|
||||
PICout operator <<(const PIObject * v);
|
||||
|
||||
//! Output operator for \a PICoutSpecialChar values
|
||||
PICout operator <<(const PICoutSpecialChar v);
|
||||
|
||||
//! Output operator for \a PIFlags<PICoutFormat> values
|
||||
PICout operator <<(const PIFlags<PICoutFormat> v) {
|
||||
if (v[Bin]) cnb_ = 2;
|
||||
if (v[Oct]) cnb_ = 8;
|
||||
if (v[Dec]) cnb_ = 10;
|
||||
if (v[Hex]) cnb_ = 16;
|
||||
if (v[Bold]) applyFormat(Bold);
|
||||
if (v[Faint]) applyFormat(Faint);
|
||||
if (v[Italic]) applyFormat(Italic);
|
||||
if (v[Underline]) applyFormat(Underline);
|
||||
if (v[Blink]) applyFormat(Blink);
|
||||
if (v[Black]) applyFormat(Black);
|
||||
if (v[Red]) applyFormat(Red);
|
||||
if (v[Green]) applyFormat(Green);
|
||||
if (v[Blue]) applyFormat(Blue);
|
||||
if (v[Yellow]) applyFormat(Yellow);
|
||||
if (v[Magenta]) applyFormat(Magenta);
|
||||
if (v[Cyan]) applyFormat(Cyan);
|
||||
if (v[White]) applyFormat(White);
|
||||
if (v[BackBlack]) applyFormat(BackBlack);
|
||||
if (v[BackRed]) applyFormat(BackRed);
|
||||
if (v[BackGreen]) applyFormat(BackGreen);
|
||||
if (v[BackBlue]) applyFormat(BackBlue);
|
||||
if (v[BackYellow]) applyFormat(BackYellow);
|
||||
if (v[BackMagenta]) applyFormat(BackMagenta);
|
||||
if (v[BackCyan]) applyFormat(BackCyan);
|
||||
if (v[BackWhite]) applyFormat(BackWhite);
|
||||
if (v[Default]) applyFormat(Default);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! Output operator for \a PICoutFormat values
|
||||
PICout operator <<(const PICoutFormat v) {
|
||||
switch (v) {
|
||||
case Bin: cnb_ = 2; break;
|
||||
case Oct: cnb_ = 8; break;
|
||||
case Dec: cnb_ = 10; break;
|
||||
case Hex: cnb_ = 16; break;
|
||||
default: applyFormat(v);
|
||||
};
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! Do some action
|
||||
PICout operator <<(const PICoutAction v);
|
||||
|
||||
//! Set control flag "c" is "on" state
|
||||
PICout & setControl(PICoutControl c, bool on = true) {co_.setFlag(c, on); return *this;}
|
||||
|
||||
//! Set control flags "c" and if "save" exec \a saveControl()
|
||||
PICout & setControl(PICoutControls c, bool save = false) {if (save) saveControl(); co_ = c; return *this;}
|
||||
|
||||
//! Save control flags to internal stack \sa \a restoreControl()
|
||||
PICout & saveControl() {cos_.push(co_); return *this;}
|
||||
|
||||
//! Restore control flags from internal stack \sa \a saveControl()
|
||||
PICout & restoreControl() {if (!cos_.empty()) {co_ = cos_.top(); cos_.pop();} return *this;}
|
||||
|
||||
/*! \brief Conditional put space character to output
|
||||
* \details If it is not a first output and control \a AddSpaces is set
|
||||
* space character is put \sa \a quote(), \a newLine() */
|
||||
PICout & space();
|
||||
|
||||
/*! \brief Conditional put quote character to output
|
||||
* \details If control \a AddQuotes is set
|
||||
* quote character is put \sa \a space(), \a newLine() */
|
||||
PICout & quote();
|
||||
|
||||
/*! \brief Conditional put new line character to output
|
||||
* \details If control \a AddNewLine is set
|
||||
* new line character is put \sa \a space(), \a quote() */
|
||||
PICout & newLine();
|
||||
|
||||
static bool setBufferActive(bool on, bool clear = false);
|
||||
static bool isBufferActive();
|
||||
static PIString buffer(bool clear = false);
|
||||
static void clearBuffer();
|
||||
|
||||
private:
|
||||
void applyFormat(PICoutFormat f);
|
||||
|
||||
static bool buffer_;
|
||||
bool fo_, cc_, fc_;
|
||||
int cnb_, attr_;
|
||||
PICoutControls co_;
|
||||
std::stack<PICoutControls> cos_;
|
||||
#ifdef WINDOWS
|
||||
static void * hOut;
|
||||
static WORD dattr;
|
||||
static DWORD smode;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // PICOUT_H
|
||||
137
src/core/piflags.h
Normal file
137
src/core/piflags.h
Normal file
@@ -0,0 +1,137 @@
|
||||
/*! \file piflags.h
|
||||
* \brief General flags class
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
General flags class
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIFLAGS_H
|
||||
#define PIFLAGS_H
|
||||
|
||||
#include "pimonitor.h"
|
||||
|
||||
/*! \brief This class used as container for bit flags
|
||||
* \details PIFlags is wrapper around \c "int". There are many
|
||||
* bit-wise operators, native conversion to int and function
|
||||
* to test flag. \n Example:
|
||||
* \snippet piincludes.cpp flags
|
||||
*/
|
||||
template<typename Enum>
|
||||
class PIP_EXPORT PIFlags {
|
||||
public:
|
||||
//! Constructor with flags = 0
|
||||
PIFlags(): flags(0) {;}
|
||||
//! Constructor with flags = Enum "e"
|
||||
PIFlags(Enum e): flags(e) {;}
|
||||
//! Constructor with flags = PIFlags "f"
|
||||
PIFlags(const PIFlags & f): flags(f.flags) {;}
|
||||
//! Constructor with flags = int "i"
|
||||
PIFlags(const int i): flags(i) {;}
|
||||
//! Set flags "f" to value "on"
|
||||
PIFlags & setFlag(const PIFlags & f, bool on = true) {if (on) flags |= f.flags; else flags &= ~f.flags; return *this;}
|
||||
//! Set flag "e" to value "on"
|
||||
PIFlags & setFlag(const Enum & e, bool on = true) {if (on) flags |= e; else flags &= ~e; return *this;}
|
||||
//! Set flag "i" to value "on"
|
||||
PIFlags & setFlag(const int & i, bool on = true) {if (on) flags |= i; else flags &= ~i; return *this;}
|
||||
//! copy operator
|
||||
void operator =(const PIFlags & f) {flags = f.flags;}
|
||||
//! copy operator
|
||||
void operator =(const Enum & e) {flags = e;}
|
||||
//! copy operator
|
||||
void operator =(const int & i) {flags = i;}
|
||||
//! compare operator
|
||||
bool operator ==(const PIFlags & f) {return flags == f.flags;}
|
||||
//! compare operator
|
||||
bool operator ==(const Enum & e) {return flags == e;}
|
||||
//! compare operator
|
||||
bool operator ==(const int i) {return flags == i;}
|
||||
//! compare operator
|
||||
bool operator !=(const PIFlags & f) {return flags != f.flags;}
|
||||
//! compare operator
|
||||
bool operator !=(const Enum & e) {return flags != e;}
|
||||
//! compare operator
|
||||
bool operator !=(const int i) {return flags != i;}
|
||||
//! compare operator
|
||||
bool operator >(const PIFlags & f) {return flags > f.flags;}
|
||||
//! compare operator
|
||||
bool operator >(const Enum & e) {return flags > e;}
|
||||
//! compare operator
|
||||
bool operator >(const int i) {return flags > i;}
|
||||
//! compare operator
|
||||
bool operator <(const PIFlags & f) {return flags < f.flags;}
|
||||
//! compare operator
|
||||
bool operator <(const Enum & e) {return flags < e;}
|
||||
//! compare operator
|
||||
bool operator <(const int i) {return flags < i;}
|
||||
//! compare operator
|
||||
bool operator >=(const PIFlags & f) {return flags >= f.flags;}
|
||||
//! compare operator
|
||||
bool operator >=(const Enum & e) {return flags >= e;}
|
||||
//! compare operator
|
||||
bool operator >=(const int i) {return flags >= i;}
|
||||
//! compare operator
|
||||
bool operator <=(const PIFlags & f) {return flags <= f.flags;}
|
||||
//! compare operator
|
||||
bool operator <=(const Enum & e) {return flags <= e;}
|
||||
//! compare operator
|
||||
bool operator <=(const int i) {return flags <= i;}
|
||||
//! Bit-wise AND operator
|
||||
void operator &=(const PIFlags & f) {flags &= f.flags;}
|
||||
//! Bit-wise AND operator
|
||||
void operator &=(const Enum & e) {flags &= e;}
|
||||
//! Bit-wise AND operator
|
||||
void operator &=(const int i) {flags &= i;}
|
||||
//! Bit-wise OR operator
|
||||
void operator |=(const PIFlags & f) {flags |= f.flags;}
|
||||
//! Bit-wise OR operator
|
||||
void operator |=(const Enum & e) {flags |= e;}
|
||||
//! Bit-wise OR operator
|
||||
void operator |=(const int i) {flags |= i;}
|
||||
//! Bit-wise XOR operator
|
||||
void operator ^=(const PIFlags & f) {flags ^= f.flags;}
|
||||
//! Bit-wise XOR operator
|
||||
void operator ^=(const Enum & e) {flags ^= e;}
|
||||
//! Bit-wise XOR operator
|
||||
void operator ^=(const int i) {flags ^= i;}
|
||||
//! Bit-wise AND operator
|
||||
PIFlags operator &(PIFlags f) const {PIFlags tf(flags & f.flags); return tf;}
|
||||
//! Bit-wise AND operator
|
||||
PIFlags operator &(Enum e) const {PIFlags tf(flags & e); return tf;}
|
||||
//! Bit-wise AND operator
|
||||
PIFlags operator &(int i) const {PIFlags tf(flags & i); return tf;}
|
||||
//! Bit-wise OR operator
|
||||
PIFlags operator |(PIFlags f) const {PIFlags tf(flags | f.flags); return tf;}
|
||||
//! Bit-wise OR operator
|
||||
PIFlags operator |(Enum e) const {PIFlags tf(flags | e); return tf;}
|
||||
//! Bit-wise OR operator
|
||||
PIFlags operator |(int i) const {PIFlags tf(flags | i); return tf;}
|
||||
//! Bit-wise XOR operator
|
||||
PIFlags operator ^(PIFlags f) const {PIFlags tf(flags ^ f.flags); return tf;}
|
||||
//! Bit-wise XOR operator
|
||||
PIFlags operator ^(Enum e) const {PIFlags tf(flags ^ e); return tf;}
|
||||
//! Bit-wise XOR operator
|
||||
PIFlags operator ^(int i) const {PIFlags tf(flags ^ i); return tf;}
|
||||
//! Test flag operator
|
||||
bool operator [](Enum e) const {return (flags & e) == e;}
|
||||
//! Implicity conversion to \c int
|
||||
operator int() const {return flags;}
|
||||
private:
|
||||
int flags;
|
||||
};
|
||||
|
||||
#endif // PIFLAGS_H
|
||||
235
src/core/piincludes.cpp
Executable file
235
src/core/piincludes.cpp
Executable file
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Global includes
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "piincludes.h"
|
||||
#include "piconsole.h"
|
||||
|
||||
bool piDebug = true;
|
||||
double piMountInfoRefreshIntervalMs = 10000.;
|
||||
|
||||
lconv * currentLocale =
|
||||
#ifdef ANDROID
|
||||
0;
|
||||
#else
|
||||
std::localeconv();
|
||||
#endif
|
||||
|
||||
#ifdef MAC_OS
|
||||
clock_serv_t __pi_mac_clock;
|
||||
#endif
|
||||
|
||||
#ifdef WINDOWS
|
||||
FILETIME __pi_ftjan1970;
|
||||
long long __pi_perf_freq = -1;
|
||||
PINtSetTimerResolution setTimerResolutionAddr = 0;
|
||||
#endif
|
||||
|
||||
void errorClear() {
|
||||
#ifdef WINDOWS
|
||||
SetLastError(0);
|
||||
#else
|
||||
errno = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
PIString errorString() {
|
||||
#ifdef WINDOWS
|
||||
char * msg;
|
||||
int err = GetLastError();
|
||||
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&msg, 0, NULL);
|
||||
return "code " + itos(err) + " - " + string(msg);
|
||||
#else
|
||||
int e = errno;
|
||||
return PIString("code ") + PIString::fromNumber(e) + " - " + PIString(strerror(e));
|
||||
#endif
|
||||
}
|
||||
|
||||
PIString PIPVersion() {
|
||||
static PIString ret(PIString::fromNumber(PIP_VERSION_MAJOR) + "." +
|
||||
PIString::fromNumber(PIP_VERSION_MINOR) + "." +
|
||||
PIString::fromNumber(PIP_VERSION_REVISION) +
|
||||
PIP_VERSION_SUFFIX);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*! \class PICout
|
||||
* \brief Class for formatted output similar std::cout
|
||||
*
|
||||
* \section PICout_sec0 Synopsis
|
||||
* This class provide many stream operators for output with some features.
|
||||
* Output to PICout is thread-sequential, i.e. doesn`t mixed from parallel
|
||||
* threads.
|
||||
*
|
||||
* \section PICout_sec1 Features
|
||||
* - insertion spaces between entries
|
||||
* - insertion new line at the end of output
|
||||
* - strings are quoted
|
||||
* - custom output operator can be easily written
|
||||
*
|
||||
* \section PICout_ex0 Usage
|
||||
* \snippet picout.cpp 0
|
||||
*
|
||||
* \section PICout_ex1 Writing your own output operator
|
||||
* \snippet picout.cpp own
|
||||
*/
|
||||
|
||||
|
||||
/*! \mainpage What is PIP
|
||||
* PIP - Platform-Independent Primitives - is crossplatform library for C++ developers.
|
||||
* It is wrap around STL and pure C++. This library can help developers write non-GUI
|
||||
* projects much more quickly, efficiently and customizable than on pure C++.
|
||||
* Library contains many classes, some of them are pure abstract, some classes
|
||||
* can be used as they are, some classes should be inherited to new classes.
|
||||
* PIP provide classes:
|
||||
* * direct output to console (\a PICout)
|
||||
* * containers (\a PIVector, \a PIList, \a PIMap, \a PIStack)
|
||||
* * byte array (\a PIByteArray)
|
||||
* * string (\a PIString, \a PIStringList)
|
||||
* * base object (events and handlers) (\a PIObject)
|
||||
* * thread (\a PIThread)
|
||||
* * timer (\a PITimer)
|
||||
* * console (information output) (\a PIConsole)
|
||||
* * stand-alone
|
||||
* * server
|
||||
* * client
|
||||
* * I/O devices
|
||||
* * base class (\a PIIODevice)
|
||||
* * file (\a PIFile)
|
||||
* * serial port (\a PISerial)
|
||||
* * ethernet (\a PIEthernet)
|
||||
* * USB (\a PIUSB)
|
||||
* * packets extractor (\a PIPacketExtractor)
|
||||
* * binary log (\a PIBinaryLog)
|
||||
* * complex I/O point (\a PIConnection)
|
||||
* * connection quality diagnotic (\a PIDiagnostics)
|
||||
* * command-line arguments parser (\a PICLI)
|
||||
* * math evaluator (\a PIEvaluator)
|
||||
* * peering net node (\a PIPeer)
|
||||
* * process (\a PIProcess)
|
||||
* * state machine (\a PIStateMachine)
|
||||
* \n \n Basic using of PIP described at page \ref using_basic */
|
||||
|
||||
|
||||
/*! \page using_basic Getting started
|
||||
* Many novice programmers are solved many common task with system integrity: output to console,
|
||||
* keyboard buttons press detecting, working with serial ports, ethernet or files, and many other.
|
||||
* These tasks can solve this library, and code, based only on PIP will be compile and work
|
||||
* similar on many systems: Windows, any Linux, Red Hat, FreeBSD, MacOS X and QNX.
|
||||
* Typical application on PIP looks like this: \n
|
||||
\code{.cpp}
|
||||
#include <pip.h>
|
||||
|
||||
|
||||
// declare key press handler
|
||||
void key_event(char key, void * );
|
||||
|
||||
|
||||
PIConsole console(false, key_event); // don`t start now, key handler is "key_event"
|
||||
|
||||
|
||||
// some vars
|
||||
int i = 2, j = 3;
|
||||
|
||||
|
||||
// implicit key press handler
|
||||
void key_event(char key, void * ) {
|
||||
switch (key) {
|
||||
case '-':
|
||||
i--;
|
||||
break;
|
||||
case '+':
|
||||
i++;
|
||||
break;
|
||||
case '(':
|
||||
j--;
|
||||
break;
|
||||
case ')':
|
||||
j++;
|
||||
break;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
class MainClass: public PITimer {
|
||||
PIOBJECT(MainClass)
|
||||
public:
|
||||
MainClass() {}
|
||||
protected:
|
||||
void tick(void * data, int delimiter) {
|
||||
piCout << "timer tick";
|
||||
// timer tick
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
MainClass main_class;
|
||||
|
||||
|
||||
int main(int argc, char * argv[]) {
|
||||
// enabling auto-detection of exit button press, by default 'Q' (shift+q)
|
||||
console.enableExitCapture();
|
||||
|
||||
// if we want to parse command-line arguments
|
||||
PICLI cli(argc, argv);
|
||||
cli.addArgument("console"); // "-c" or "--console"
|
||||
cli.addArgument("debug"); // "-d" or "--debug"
|
||||
|
||||
// enabling or disabling global debug flag
|
||||
piDebug = cli.hasArgument("debug");
|
||||
|
||||
// configure console
|
||||
console.addTab("first tab", '1');
|
||||
console.addString("PIP console", 1, PIConsole::Bold);
|
||||
console.addVariable("int var (i)", &i, 1);
|
||||
console.addVariable("int green var (j)", &j, 1, PIConsole::Green);
|
||||
console.addString("'-' - i--", 2);
|
||||
console.addString("'+' - i++", 2);
|
||||
console.addString("'(' - j--", 2);
|
||||
console.addString("')' - j++", 2);
|
||||
console.addTab("second tab", '2');
|
||||
console.addString("col 1", 1);
|
||||
console.addString("col 2", 2);
|
||||
console.addString("col 3", 3);
|
||||
console.setTab("first tab");
|
||||
|
||||
// start output to console if "console" argument exists
|
||||
if (cli.hasArgument("console"))
|
||||
console.start();
|
||||
|
||||
// start main class, e.g. 40 Hz
|
||||
main_class.start(25.);
|
||||
|
||||
// wait for 'Q' press, independently if console is started or not
|
||||
console.waitForFinish();
|
||||
|
||||
return 0;
|
||||
};
|
||||
\endcode
|
||||
* This code demonstrates simple interactive configurable program, which can be started with console
|
||||
* display or not, and with debug or not. \b MainClass is central class that also can be inherited from
|
||||
* \a PIThread and reimplement \a run() function.
|
||||
* \n Many PIP classes has events and event handlers, which can be connected one to another.
|
||||
* Details you can see at \a PIObject reference page (\ref PIObject_sec0).
|
||||
* \n To configure your program from file use \a PIConfig.
|
||||
* \n If you want more information see \ref using_advanced */
|
||||
|
||||
|
||||
/*! \page using_advanced Advanced using
|
||||
* Sorry, creativity crysis xD
|
||||
*/
|
||||
538
src/core/piincludes.h
Executable file
538
src/core/piincludes.h
Executable file
@@ -0,0 +1,538 @@
|
||||
/*! \file piincludes.h
|
||||
* \brief Global includes of PIP
|
||||
*
|
||||
* This file include all needed system headers, STL
|
||||
* and declare many useful macros and functions
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Global includes
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIINCLUDES_H
|
||||
#define PIINCLUDES_H
|
||||
|
||||
#include "piversion.h"
|
||||
|
||||
//! Version of PIP in hex - 0x##(Major)##(Minor)##(Revision)
|
||||
#define PIP_VERSION ((PIP_VERSION_MAJOR << 16) | (PIP_VERSION_MINOR < 8) | PIP_VERSION_REVISION)
|
||||
|
||||
#ifdef DOXYGEN
|
||||
|
||||
//! Major value of PIP version
|
||||
# define PIP_VERSION_MAJOR
|
||||
|
||||
//! Minor value of PIP version
|
||||
# define PIP_VERSION_MINOR
|
||||
|
||||
//! Revision value of PIP version
|
||||
# define PIP_VERSION_REVISION
|
||||
|
||||
//! Suffix of PIP version
|
||||
# define PIP_VERSION_SUFFIX
|
||||
|
||||
//! Macro is defined when compile-time debug is enabled
|
||||
# define PIP_DEBUG
|
||||
|
||||
//! Macro is defined when host is any Windows
|
||||
# define WINDOWS
|
||||
|
||||
//! Macro is defined when host is QNX
|
||||
# define QNX
|
||||
|
||||
//! Macro is defined when host is FreeBSD
|
||||
# define FREE_BSD
|
||||
|
||||
//! Macro is defined when host is Mac OS
|
||||
# define MAC_OS
|
||||
|
||||
//! Macro is defined when host is Android
|
||||
# define ANDROID
|
||||
|
||||
//! Macro is defined when host is any Linux
|
||||
# define LINUX
|
||||
|
||||
//! Macro is defined when compiler is GCC or MinGW
|
||||
# define CC_GCC
|
||||
|
||||
//! Macro is defined when PIP is decided that host is support language
|
||||
# define HAS_LOCALE
|
||||
|
||||
//! Macro is defined when compiler is Visual Studio
|
||||
# define CC_VC
|
||||
|
||||
//! Macro is defined when compiler is unknown
|
||||
# define CC_OTHER
|
||||
|
||||
//! Macro is defined when PIP use "rt" library for timers implementation
|
||||
# define PIP_TIMER_RT
|
||||
|
||||
//! Define this macro to use STL implementation of containers, else PIP implementation will be used
|
||||
# define PIP_CONTAINERS_STL
|
||||
|
||||
#endif
|
||||
|
||||
#include "piplatform.h"
|
||||
|
||||
#include "pip_export.h"
|
||||
#if defined(DOXYGEN) || defined(CC_GCC) || defined(PICODE)
|
||||
# undef PIP_EXPORT
|
||||
# define PIP_EXPORT
|
||||
#endif
|
||||
#include <iostream>
|
||||
#ifdef CC_GCC
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#ifndef QNX
|
||||
# include <cstdio>
|
||||
# include <cstdlib>
|
||||
# include <clocale>
|
||||
# include <complex>
|
||||
# include <cmath>
|
||||
#else
|
||||
# include <stdio.h>
|
||||
# include <locale.h>
|
||||
# include <complex.h>
|
||||
# include <math.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <cctype>
|
||||
#include <ctime>
|
||||
#include <csignal>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
//#include <signal.h>
|
||||
#include <typeinfo>
|
||||
#include <algorithm>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <queue>
|
||||
#include <deque>
|
||||
#include <stack>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#ifdef WINDOWS
|
||||
# include <conio.h>
|
||||
# include <io.h>
|
||||
# include <winsock2.h>
|
||||
# ifdef CC_VC
|
||||
# define SHUT_RDWR 2
|
||||
# pragma comment(lib, "Ws2_32.lib")
|
||||
# pragma comment(lib, "Iphlpapi.lib")
|
||||
# pragma comment(lib, "Psapi.lib")
|
||||
# else
|
||||
# define SHUT_RDWR SD_BOTH
|
||||
# endif
|
||||
# include <windows.h>
|
||||
# include <wincon.h>
|
||||
# include <iphlpapi.h>
|
||||
# include <psapi.h>
|
||||
typedef int socklen_t;
|
||||
typedef void(*PINtSetTimerResolution)(ULONG, BOOLEAN, PULONG);
|
||||
extern FILETIME __pi_ftjan1970;
|
||||
extern long long __pi_perf_freq;
|
||||
extern PINtSetTimerResolution setTimerResolutionAddr;
|
||||
inline long long __PIQueryPerformanceCounter() {LARGE_INTEGER li; QueryPerformanceCounter(&li); return li.QuadPart;}
|
||||
inline void __PISetTimerResolution() {if (setTimerResolutionAddr == NULL) return; ULONG ret; setTimerResolutionAddr(1, TRUE, &ret);}
|
||||
#else
|
||||
# include <netinet/in.h>
|
||||
# include <arpa/inet.h>
|
||||
# include <sys/socket.h>
|
||||
# include <fcntl.h>
|
||||
# include <sys/ioctl.h>
|
||||
# include <net/if.h>
|
||||
# include <sys/utsname.h>
|
||||
# include <pthread.h>
|
||||
# ifndef ANDROID
|
||||
# include <ifaddrs.h>
|
||||
# endif
|
||||
#endif
|
||||
#ifdef ANDROID
|
||||
# define tcdrain(fd) ioctl(fd, TCSBRK, 1)
|
||||
inline int wctomb(char * c, wchar_t w) {*c = ((char * )&w)[0]; return 1;}
|
||||
inline int mbtowc(wchar_t * w, const char * c, size_t) {*w = ((wchar_t * )&c)[0]; return 1;}
|
||||
#endif
|
||||
#ifdef MAC_OS
|
||||
# include <mach/mach_traps.h>
|
||||
# include <mach/mach.h>
|
||||
# include <mach/clock.h>
|
||||
# include <crt_externs.h>
|
||||
# define environ (*_NSGetEnviron())
|
||||
typedef long time_t;
|
||||
extern clock_serv_t __pi_mac_clock;
|
||||
#endif
|
||||
#ifdef LINUX
|
||||
# define environ __environ
|
||||
#endif
|
||||
#if !defined(WINDOWS) && !defined(MAC_OS)
|
||||
//# define PIP_TIMER_RT
|
||||
#endif
|
||||
#ifdef FREE_BSD
|
||||
extern char ** environ;
|
||||
#endif
|
||||
#if defined(DOXYGEN) || defined(PICODE)
|
||||
# undef PIP_EXPORT
|
||||
# define PIP_EXPORT
|
||||
# undef DEPRECATED
|
||||
# define DEPRECATED
|
||||
#endif
|
||||
|
||||
#include "pimonitor.h"
|
||||
#include "piflags.h"
|
||||
|
||||
extern PIMonitor piMonitor;
|
||||
|
||||
//! Macro used for infinite loop
|
||||
#define FOREVER for (;;)
|
||||
|
||||
//! Macro used for infinite wait
|
||||
#define FOREVER_WAIT FOREVER msleep(1);
|
||||
|
||||
//! Macro used for infinite wait
|
||||
#define WAIT_FOREVER FOREVER msleep(1);
|
||||
|
||||
using std::cout;
|
||||
using std::cin;
|
||||
using std::endl;
|
||||
using std::flush;
|
||||
using std::vector;
|
||||
using std::list;
|
||||
using std::queue;
|
||||
using std::deque;
|
||||
using std::stack;
|
||||
using std::set;
|
||||
using std::map;
|
||||
using std::multimap;
|
||||
using std::string;
|
||||
using std::complex;
|
||||
#ifndef QNX
|
||||
using std::wstring;
|
||||
#else
|
||||
typedef std::basic_string<wchar_t> wstring;
|
||||
#endif
|
||||
|
||||
typedef long long llong;
|
||||
typedef unsigned char uchar;
|
||||
typedef unsigned short int ushort;
|
||||
typedef unsigned int uint;
|
||||
typedef unsigned long ulong;
|
||||
typedef unsigned long long ullong;
|
||||
typedef long double ldouble;
|
||||
typedef complex<int> complexi;
|
||||
typedef complex<float> complexf;
|
||||
typedef complex<double> complexd;
|
||||
typedef complex<ldouble> complexld;
|
||||
|
||||
const complexld complexld_i(0., 1.);
|
||||
const complexld complexld_0(0.);
|
||||
const complexld complexld_1(1.);
|
||||
const complexd complexd_i(0., 1.);
|
||||
const complexd complexd_0(0.);
|
||||
const complexd complexd_1(1.);
|
||||
|
||||
/*! \brief Templated function for swap two values
|
||||
* \details Example:\n \snippet piincludes.cpp swap */
|
||||
template<typename T> inline void piSwap(T & f, T & s) {T t = f; f = s; s = t;}
|
||||
|
||||
/*! \brief Templated function for swap two values without "="
|
||||
* \details Example:\n \snippet piincludes.cpp swapBinary */
|
||||
template<typename T> inline void piSwapBinary(T & f, T & s) {
|
||||
static size_t j = (sizeof(T) / sizeof(size_t)), bs = j * sizeof(size_t), bf = sizeof(T);
|
||||
size_t i = 0;
|
||||
for (i = 0; i < j; ++i) {
|
||||
((size_t*)(&f))[i] ^= ((size_t*)(&s))[i];
|
||||
((size_t*)(&s))[i] ^= ((size_t*)(&f))[i];
|
||||
((size_t*)(&f))[i] ^= ((size_t*)(&s))[i];
|
||||
}
|
||||
for (i = bs; i < bf; ++i) {
|
||||
((uchar*)(&f))[i] ^= ((uchar*)(&s))[i];
|
||||
((uchar*)(&s))[i] ^= ((uchar*)(&f))[i];
|
||||
((uchar*)(&f))[i] ^= ((uchar*)(&s))[i];
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Templated function return round of float falue
|
||||
* \details Round is the nearest integer value \n
|
||||
* There are some macros:
|
||||
* - \c piRoundf for "float"
|
||||
* - \c piRoundd for "double"
|
||||
*
|
||||
* Example:
|
||||
* \snippet piincludes.cpp round */
|
||||
template<typename T> inline int piRound(const T & v) {return int(v >= T(0.) ? v + T(0.5) : v - T(0.5));}
|
||||
|
||||
/*! \brief Templated function return floor of float falue
|
||||
* \details Floor is the largest integer that is not greater than value \n
|
||||
* There are some macros:
|
||||
* - \c piFloorf for "float"
|
||||
* - \c piFloord for "double"
|
||||
*
|
||||
* Example:
|
||||
* \snippet piincludes.cpp floor */
|
||||
template<typename T> inline int piFloor(const T & v) {return v < T(0) ? int(v) - 1 : int(v);}
|
||||
|
||||
/*! \brief Templated function return ceil of float falue
|
||||
* \details Ceil is the smallest integer that is not less than value \n
|
||||
* There are some macros:
|
||||
* - \c piCeilf for "float"
|
||||
* - \c piCeild for "double"
|
||||
*
|
||||
* Example:
|
||||
* \snippet piincludes.cpp ceil */
|
||||
template<typename T> inline int piCeil(const T & v) {return v < T(0) ? int(v) : int(v) + 1;}
|
||||
|
||||
/*! \brief Templated function return absolute of numeric falue
|
||||
* \details Absolute is the positive or equal 0 value \n
|
||||
* There are some macros:
|
||||
* - \c piAbss for "short"
|
||||
* - \c piAbsi for "int"
|
||||
* - \c piAbsl for "long"
|
||||
* - \c piAbsll for "llong"
|
||||
* - \c piAbsf for "float"
|
||||
* - \c piAbsd for "double"
|
||||
*
|
||||
* Example:
|
||||
* \snippet piincludes.cpp abs */
|
||||
template<typename T> inline T piAbs(const T & v) {return (v >= T(0) ? v : -v);}
|
||||
|
||||
/*! \brief Templated function return minimum of two values
|
||||
* \details There are some macros:
|
||||
* - \c piMins for "short"
|
||||
* - \c piMini for "int"
|
||||
* - \c piMinl for "long"
|
||||
* - \c piMinll for "llong"
|
||||
* - \c piMinf for "float"
|
||||
* - \c piMind for "double"
|
||||
*
|
||||
* Example:
|
||||
* \snippet piincludes.cpp min2 */
|
||||
template<typename T> inline T piMin(const T & f, const T & s) {return ((f > s) ? s : f);}
|
||||
|
||||
/*! \brief Templated function return minimum of tree values
|
||||
* \details There are some macros:
|
||||
* - \c piMins for "short"
|
||||
* - \c piMini for "int"
|
||||
* - \c piMinl for "long"
|
||||
* - \c piMinll for "llong"
|
||||
* - \c piMinf for "float"
|
||||
* - \c piMind for "double"
|
||||
*
|
||||
* Example:
|
||||
* \snippet piincludes.cpp min3 */
|
||||
template<typename T> inline T piMin(const T & f, const T & s, const T & t) {return ((f < s && f < t) ? f : ((s < t) ? s : t));}
|
||||
|
||||
/*! \brief Templated function return maximum of two values
|
||||
* \details There are some macros:
|
||||
* - \c piMaxs for "short"
|
||||
* - \c piMaxi for "int"
|
||||
* - \c piMaxl for "long"
|
||||
* - \c piMaxll for "llong"
|
||||
* - \c piMaxf for "float"
|
||||
* - \c piMaxd for "double"
|
||||
*
|
||||
* Example:
|
||||
* \snippet piincludes.cpp max2 */
|
||||
template<typename T> inline T piMax(const T & f, const T & s) {return ((f < s) ? s : f);}
|
||||
|
||||
/*! \brief Templated function return maximum of tree values
|
||||
* \details There are some macros:
|
||||
* - \c piMaxs for "short"
|
||||
* - \c piMaxi for "int"
|
||||
* - \c piMaxl for "long"
|
||||
* - \c piMaxll for "llong"
|
||||
* - \c piMaxf for "float"
|
||||
* - \c piMaxd for "double"
|
||||
*
|
||||
* Example:
|
||||
* \snippet piincludes.cpp max3 */
|
||||
template<typename T> inline T piMax(const T & f, const T & s, const T & t) {return ((f > s && f > t) ? f : ((s > t) ? s : t));}
|
||||
|
||||
/*! \brief Templated function return clamped value
|
||||
* \details Clamped is the not greater than "max" and not lesser than "min" value \n
|
||||
* There are some macros:
|
||||
* - \c piClamps for "short"
|
||||
* - \c piClampi for "int"
|
||||
* - \c piClampl for "long"
|
||||
* - \c piClampll for "llong"
|
||||
* - \c piClampf for "float"
|
||||
* - \c piClampd for "double"
|
||||
*
|
||||
* Example:
|
||||
* \snippet piincludes.cpp clamp */
|
||||
template<typename T> inline T piClamp(const T & v, const T & min, const T & max) {return (v > max ? max : (v < min ? min : v));}
|
||||
|
||||
/// Function inverse byte order in memory block
|
||||
inline void piLetobe(void * data, int size) {
|
||||
for (int i = 0; i < size / 2; i++)
|
||||
piSwap<uchar>(((uchar*)data)[size - i - 1], ((uchar*)data)[i]);
|
||||
}
|
||||
|
||||
/// \brief Templated function that inverse byte order of value "v"
|
||||
template<typename T> inline void piLetobe(T * v) {piLetobe(v, sizeof(T));}
|
||||
|
||||
/*! \brief Templated function that returns "v" with inversed byte order
|
||||
* \details This function used to convert values between little and big endian \n
|
||||
* There are some macros:
|
||||
* - \c piLetobes for "ushort"
|
||||
* - \c piLetobei for "uint"
|
||||
* - \c piLetobel for "ulong"
|
||||
* - \c piLetobell for "ullong"
|
||||
*
|
||||
* Example:
|
||||
* \snippet piincludes.cpp letobe */
|
||||
template<typename T> inline T piLetobe(const T & v) {T tv(v); piLetobe(&tv, sizeof(T)); return tv;}
|
||||
|
||||
// specialization
|
||||
template<> inline ushort piLetobe(const ushort & v) {return (v << 8) | (v >> 8);}
|
||||
template<> inline uint piLetobe(const uint & v) {return (v >> 24) | ((v >> 8) & 0xFF00) | ((v << 8) & 0xFF0000) | ((v << 24) & 0xFF000000);}
|
||||
|
||||
DEPRECATED inline ushort letobe_s(const ushort & v) {return (v << 8) | (v >> 8);}
|
||||
DEPRECATED inline uint letobe_i(const uint & v) {return (v >> 24) | ((v >> 8) & 0xFF00) | ((v << 8) & 0xFF0000) | ((v << 24) & 0xFF000000);}
|
||||
|
||||
#ifdef DOXYGEN
|
||||
|
||||
/// \deprecated \brief Use \a piLetobe() instead of this function
|
||||
ushort letobe_s(ushort v) {return (v << 8) | (v >> 8);}
|
||||
|
||||
/// \deprecated \brief Use \a piLetobe() instead of this function
|
||||
uint letobe_i(uint v) {return (v >> 24) | ((v >> 8) & 0xFF00) | ((v << 8) & 0xFF0000) | ((v << 24) & 0xFF000000);}
|
||||
|
||||
#endif
|
||||
|
||||
#define piRoundf piRound<float>
|
||||
#define piRoundd piRound<double>
|
||||
#define piFloorf piFloor<float>
|
||||
#define piFloord piFloor<double>
|
||||
#define piCeilf piCeil<float>
|
||||
#define piCeild piCeil<double>
|
||||
#define piAbss piAbs<short>
|
||||
#define piAbsi piAbs<int>
|
||||
#define piAbsl piAbs<long>
|
||||
#define piAbsll piAbs<llong>
|
||||
#define piAbsf piAbs<float>
|
||||
#define piAbsd piAbs<double>
|
||||
#define piMins piMin<short>
|
||||
#define piMini piMin<int>
|
||||
#define piMinl piMin<long>
|
||||
#define piMinll piMin<llong>
|
||||
#define piMinf piMin<float>
|
||||
#define piMind piMin<double>
|
||||
#define piMaxs piMax<short>
|
||||
#define piMaxi piMax<int>
|
||||
#define piMaxl piMax<long>
|
||||
#define piMaxll piMax<llong>
|
||||
#define piMaxf piMax<float>
|
||||
#define piMaxd piMax<double>
|
||||
#define piClamps piClamp<short>
|
||||
#define piClampi piClamp<int>
|
||||
#define piClampl piClamp<long>
|
||||
#define piClampll piClamp<llong>
|
||||
#define piClampf piClamp<float>
|
||||
#define piClampd piClamp<double>
|
||||
#define piLetobes piLetobe<ushort>
|
||||
#define piLetobei piLetobe<uint>
|
||||
#define piLetobel piLetobe<ulong>
|
||||
#define piLetobell piLetobe<ullong>
|
||||
|
||||
class PIObject;
|
||||
class PIMutex;
|
||||
class PIString;
|
||||
class PIInit;
|
||||
|
||||
//! global variable enabling output to piCout, default is true
|
||||
extern PIP_EXPORT bool piDebug;
|
||||
|
||||
//! global variable that set minimum real update interval
|
||||
//! for function PIInit::mountInfo(), default is 10000 ms
|
||||
extern PIP_EXPORT double piMountInfoRefreshIntervalMs;
|
||||
|
||||
extern lconv * currentLocale;
|
||||
|
||||
#ifdef WINDOWS
|
||||
inline int random() {return rand();}
|
||||
# ifdef CC_VC
|
||||
inline double round(const double & v) {return floor(v + 0.5);}
|
||||
# endif
|
||||
#endif
|
||||
inline bool atob(const string & str) {return str == "1" ? true : false;}
|
||||
inline string btos(const bool num) {return num ? "0" : "1";}
|
||||
inline string itos(const int num) {
|
||||
char ch[256];
|
||||
#ifndef CC_VC
|
||||
sprintf(ch, "%d", num);
|
||||
#else
|
||||
sprintf_s(ch, 256, "%d", num);
|
||||
#endif
|
||||
return string(ch);}
|
||||
inline string ltos(const long num) {
|
||||
char ch[256];
|
||||
#ifndef CC_VC
|
||||
sprintf(ch, "%ld", num);
|
||||
#else
|
||||
sprintf_s(ch, 256, "%ld", num);
|
||||
#endif
|
||||
return string(ch);}
|
||||
inline string uitos(const uint num) {
|
||||
char ch[256];
|
||||
#ifndef CC_VC
|
||||
sprintf(ch, "%u", num);
|
||||
#else
|
||||
sprintf_s(ch, 256, "%u", num);
|
||||
#endif
|
||||
return string(ch);}
|
||||
inline string ultos(const ulong num) {
|
||||
char ch[256];
|
||||
#ifndef CC_VC
|
||||
sprintf(ch, "%lu", num);
|
||||
#else
|
||||
sprintf_s(ch, 256, "%lu", num);
|
||||
#endif
|
||||
return string(ch);}
|
||||
inline string ftos(const float num) {
|
||||
char ch[256];
|
||||
#ifndef CC_VC
|
||||
sprintf(ch, "%.8f", num);
|
||||
#else
|
||||
sprintf_s(ch, 256, "%.8f", num);
|
||||
#endif
|
||||
return string(ch);}
|
||||
inline string dtos(const double num) {
|
||||
char ch[256];
|
||||
#ifndef CC_VC
|
||||
sprintf(ch, "%.8f", num);
|
||||
#else
|
||||
sprintf_s(ch, 256, "%.8f", num);
|
||||
#endif
|
||||
return string(ch);}
|
||||
|
||||
/*! \fn errorString()
|
||||
* \brief Return readable error description in format "code <number> - <description>" */
|
||||
PIString errorString();
|
||||
|
||||
void errorClear();
|
||||
|
||||
/// Return readable version of PIP
|
||||
PIString PIPVersion();
|
||||
|
||||
#endif // PIINCLUDES_H
|
||||
212
src/core/piinit.cpp
Normal file
212
src/core/piinit.cpp
Normal file
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Initialization
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "piplatform.h"
|
||||
#include "piinit.h"
|
||||
#include "pisignals.h"
|
||||
#include "piobject.h"
|
||||
#include "pisysteminfo.h"
|
||||
#include "pidir.h"
|
||||
#include "piprocess.h"
|
||||
#ifdef WINDOWS
|
||||
#else
|
||||
# include <pwd.h>
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef HAS_LOCALE
|
||||
static locale_t currentLocale_t = 0;
|
||||
#endif
|
||||
|
||||
|
||||
void __sighandler__(PISignals::Signal s) {
|
||||
//piCout << Hex << int(s);
|
||||
if (s == PISignals::StopTTYInput || s == PISignals::StopTTYOutput)
|
||||
piMSleep(10);
|
||||
if (s == PISignals::UserDefined1)
|
||||
dumpApplicationToFile(PIDir::home().path() + PIDir::separator + "_PIP_DUMP_" + PIString::fromNumber(PIProcess::currentPID()));
|
||||
}
|
||||
|
||||
|
||||
PIInit::PIInit() {
|
||||
PISystemInfo * sinfo = PISystemInfo::instance();
|
||||
sinfo->execDateTime = PIDateTime::current();
|
||||
PISignals::setSlot(__sighandler__);
|
||||
PISignals::grabSignals(PISignals::UserDefined1);
|
||||
#ifndef WINDOWS
|
||||
PISignals::grabSignals(PISignals::StopTTYInput | PISignals::StopTTYOutput);
|
||||
sigset_t ss;
|
||||
sigemptyset(&ss);
|
||||
sigaddset(&ss, SIGALRM);
|
||||
sigprocmask(SIG_BLOCK, &ss, 0);
|
||||
pthread_sigmask(SIG_BLOCK, &ss, 0);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
PIStringList ifpathes;
|
||||
ifpathes << "/bin/ifconfig" << "/sbin/ifconfig" << "/usr/bin/ifconfig" << "/usr/sbin/ifconfig";
|
||||
piForeachC (PIString & i, ifpathes)
|
||||
if (fileExists(i)) {
|
||||
sinfo->ifconfigPath = i;
|
||||
piBreak;
|
||||
}
|
||||
#else
|
||||
// OS version
|
||||
DWORD dwVersion = GetVersion();
|
||||
DWORD dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
|
||||
DWORD dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
|
||||
sinfo->OS_version = PIString(dwMajorVersion) + "." + PIString(dwMinorVersion);
|
||||
|
||||
// WinSock inint
|
||||
WSADATA wsaData;
|
||||
WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
|
||||
// Timers init
|
||||
SYSTEMTIME jan1970 = {1970, 1, 4, 1, 0, 14, 15, 0};
|
||||
SystemTimeToFileTime(&jan1970, &__pi_ftjan1970);
|
||||
LARGE_INTEGER pf;
|
||||
pf.QuadPart = -1;
|
||||
if (QueryPerformanceFrequency(&pf) != 0) __pi_perf_freq = pf.QuadPart;
|
||||
if (__pi_perf_freq == 0) __pi_perf_freq = -1;
|
||||
|
||||
// Sleep precision init
|
||||
ntlib = LoadLibrary("ntdll.dll");
|
||||
if (ntlib) setTimerResolutionAddr = (PINtSetTimerResolution)GetProcAddress(ntlib, "NtSetTimerResolution");
|
||||
/*if (setTimerResolution) setTimerResolutionAddr(1, TRUE, &prev_res);*/
|
||||
#endif
|
||||
//piDebug = true;
|
||||
#ifdef HAS_LOCALE
|
||||
//cout << "has locale" << endl;
|
||||
if (currentLocale_t != 0) {
|
||||
freelocale(currentLocale_t);
|
||||
currentLocale_t = 0;
|
||||
}
|
||||
currentLocale_t = newlocale(LC_ALL, setlocale(LC_ALL, ""), 0);
|
||||
#else
|
||||
setlocale(LC_ALL, "");
|
||||
setlocale(LC_NUMERIC, "C");
|
||||
#endif
|
||||
#ifdef MAC_OS
|
||||
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &__pi_mac_clock);
|
||||
#endif
|
||||
char cbuff[1024];
|
||||
memset(cbuff, 0, 1024);
|
||||
if (gethostname(cbuff, 1023) == 0)
|
||||
sinfo->hostname = cbuff;
|
||||
#ifdef WINDOWS
|
||||
SYSTEM_INFO sysinfo;
|
||||
GetSystemInfo(&sysinfo);
|
||||
sinfo->processorsCount = sysinfo.dwNumberOfProcessors;
|
||||
switch (sysinfo.wProcessorArchitecture) {
|
||||
case PROCESSOR_ARCHITECTURE_AMD64: sinfo->architecture = "x64"; break;
|
||||
case PROCESSOR_ARCHITECTURE_ARM: sinfo->architecture = "ARM"; break;
|
||||
case PROCESSOR_ARCHITECTURE_IA64: sinfo->architecture = "Intel Itanium-based"; break;
|
||||
case PROCESSOR_ARCHITECTURE_INTEL: sinfo->architecture = "x86"; break;
|
||||
case PROCESSOR_ARCHITECTURE_UNKNOWN:
|
||||
default: sinfo->architecture = "unknown"; break;
|
||||
}
|
||||
int argc_(0);
|
||||
wchar_t ** argv_ = CommandLineToArgvW(GetCommandLineW(), &argc_);
|
||||
if (argc_ > 0 && argv_ != 0)
|
||||
sinfo->execCommand = argv_[0];
|
||||
LocalFree(argv_);
|
||||
memset(cbuff, 0, 1024);
|
||||
ulong unlen = 1023;
|
||||
if (GetUserName(cbuff, &unlen) != 0)
|
||||
sinfo->user = cbuff;
|
||||
#else
|
||||
sinfo->processorsCount = piMaxi(1, int(sysconf(_SC_NPROCESSORS_ONLN)));
|
||||
passwd * ps = getpwuid(getuid());
|
||||
if (ps)
|
||||
sinfo->user = ps->pw_name;
|
||||
else {
|
||||
memset(cbuff, 0, 1024);
|
||||
if (getlogin_r(cbuff, 1023) == 0)
|
||||
sinfo->user = cbuff;
|
||||
}
|
||||
struct utsname uns;
|
||||
if (uname(&uns) == 0) {
|
||||
sinfo->OS_version = uns.release;
|
||||
sinfo->architecture = uns.machine;
|
||||
}
|
||||
#endif
|
||||
sinfo->OS_name =
|
||||
#ifdef WINDOWS
|
||||
"Windows";
|
||||
#else
|
||||
# ifdef QNX
|
||||
"QNX";
|
||||
# else
|
||||
# ifdef MAC_OS
|
||||
"MacOS";
|
||||
# else
|
||||
# ifdef ANDROID
|
||||
"Android";
|
||||
# else
|
||||
uns.sysname;
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
PIInit::~PIInit() {
|
||||
#ifdef WINDOWS
|
||||
WSACleanup();
|
||||
//if (setTimerResolution) setTimerResolutionAddr(prev_res, TRUE, &prev_res);
|
||||
if (ntlib) FreeLibrary(ntlib);
|
||||
ntlib = 0;
|
||||
#endif
|
||||
#ifdef MAC_OS
|
||||
mach_port_deallocate(mach_task_self(), __pi_mac_clock);
|
||||
#endif
|
||||
//if (currentLocale_t != 0) freelocale(currentLocale_t);
|
||||
}
|
||||
|
||||
|
||||
bool PIInit::fileExists(const PIString & p) {
|
||||
FILE * f = fopen(p.data(), "r");
|
||||
if (f == 0)
|
||||
return false;
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int __PIInit_Initializer__::count_(0);
|
||||
PIInit * __PIInit_Initializer__::__instance__(0);
|
||||
|
||||
|
||||
__PIInit_Initializer__::__PIInit_Initializer__() {
|
||||
count_++;
|
||||
if (count_ > 1) return;
|
||||
//piCout << "create PIInit";
|
||||
__instance__ = new PIInit();
|
||||
}
|
||||
|
||||
|
||||
__PIInit_Initializer__::~__PIInit_Initializer__() {
|
||||
count_--;
|
||||
if (count_ > 1) return;
|
||||
//piCout << "delete PIInit";
|
||||
if (__instance__ != 0) {
|
||||
delete __instance__;
|
||||
__instance__ = 0;
|
||||
}
|
||||
}
|
||||
56
src/core/piinit.h
Normal file
56
src/core/piinit.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*! \file piinit.h
|
||||
* \brief Initialization
|
||||
*
|
||||
*
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Initialization
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIINIT_H
|
||||
#define PIINIT_H
|
||||
|
||||
#include "piincludes.h"
|
||||
|
||||
|
||||
class __PIInit_Initializer__ {
|
||||
public:
|
||||
__PIInit_Initializer__();
|
||||
~__PIInit_Initializer__();
|
||||
static int count_;
|
||||
static PIInit * __instance__;
|
||||
};
|
||||
|
||||
|
||||
class PIInit {
|
||||
public:
|
||||
PIInit();
|
||||
~PIInit();
|
||||
static PIInit * instance() {return __PIInit_Initializer__::__instance__;}
|
||||
private:
|
||||
bool fileExists(const PIString & p);
|
||||
#ifdef WINDOWS
|
||||
HMODULE ntlib;
|
||||
ULONG prev_res;
|
||||
#endif
|
||||
};
|
||||
|
||||
static __PIInit_Initializer__ __piinit_initializer__;
|
||||
|
||||
|
||||
#endif // PIINIT_H
|
||||
417
src/core/piobject.cpp
Executable file
417
src/core/piobject.cpp
Executable file
@@ -0,0 +1,417 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Object, base class of some PIP classes, provide EVENT -> EVENT_HANDLER mechanism
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "piobject.h"
|
||||
#include "pifile.h"
|
||||
#include "pisysteminfo.h"
|
||||
|
||||
|
||||
/** \class PIObject
|
||||
* \brief This is base class for any classes which use events -> handlers mechanism.
|
||||
* \details
|
||||
* \section PIObject_sec0 Events and Event handlers
|
||||
* %PIObject provide notification mechanism similar Qt but implemented
|
||||
* on language capabilities without any special preprocessors or compilers.
|
||||
* Any class inherits PIObject should use macro \a PIOBJECT() immediate
|
||||
* after declaration to proper compile.
|
||||
*
|
||||
* Event is a some abstract event that can be raised at any time.
|
||||
* Event is a function but declared with special macro \a EVENT().
|
||||
* To raise event simply execute event function.
|
||||
*
|
||||
* Event handler is a function but declared with special macro
|
||||
* \a EVENT_HANDLER(). You can use event handlers as ordinary functions.
|
||||
*
|
||||
* Main goal of this mechanism is perform abstract connections between
|
||||
* various objects. This functionality provide macro \a CONNECT() which
|
||||
* connect some event of first object to some event handler or event of
|
||||
* second object. Each event can be connected any times to any event handlers.
|
||||
*
|
||||
* \image html events_handlers.png
|
||||
*
|
||||
* Example: \snippet piobject.cpp main
|
||||
* Result:
|
||||
\code{.cpp}
|
||||
handler B: 2 , 0.5
|
||||
handler A: event to handler
|
||||
handler A: event to event
|
||||
\endcode
|
||||
*/
|
||||
|
||||
|
||||
PIVector<PIObject * > PIObject::objects;
|
||||
PIMutex PIObject::__eh_mutex;
|
||||
PIMap<PIString, PIObject::__EHData> PIObject::__eh_data;
|
||||
|
||||
|
||||
PIString PIObject::__EHFunc::fullFormat() const {
|
||||
PIString ret = type_ret + " " + scope + "::" + func_name +"(";
|
||||
for (int i = 0; i < types.size_s(); ++i) {
|
||||
if (i > 0) ret += ", ";
|
||||
ret += types[i] + " " + names[i];
|
||||
}
|
||||
ret += ")";
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIObject::PIObject(const PIString & name): _signature_(__PIOBJECT_SIGNATURE__), emitter_(0), thread_safe_(false) {
|
||||
piMonitor.objects++;
|
||||
setName(name);
|
||||
setDebug(true);
|
||||
objects << this;
|
||||
//piCout << "new" << this;
|
||||
}
|
||||
|
||||
|
||||
PIObject::~PIObject() {
|
||||
//piCout << "delete" << this;
|
||||
piMonitor.objects--;
|
||||
objects.removeAll(this);
|
||||
piDisconnect(this);
|
||||
}
|
||||
|
||||
|
||||
void PIObject::piConnect(const PIString & src, const PIString & sig, void * dest, void * ev_h) {
|
||||
PIObject * o = findByName(src);
|
||||
if (o == 0) {
|
||||
piCout << "[PIObject] Can`t find object with name \"" << src << "\"!";
|
||||
return;
|
||||
}
|
||||
PIMutexLocker _ml(o->mutex_connect);
|
||||
o->connections << Connection(ev_h, 0, sig, (PIObject*)dest, dest);
|
||||
((PIObject*)dest)->connectors << o;
|
||||
}
|
||||
|
||||
|
||||
void PIObject::piConnect(PIObject * src, const PIString & sig, const PIString & dest, void * ev_h) {
|
||||
PIObject * o = findByName(dest);
|
||||
if (o == 0) {
|
||||
piCout << "[PIObject] Can`t find object with name \"" << dest << "\"!";
|
||||
return;
|
||||
}
|
||||
PIMutexLocker _ml(o->mutex_connect);
|
||||
src->connections << Connection(ev_h, 0, sig, o, o);
|
||||
((PIObject*)o)->connectors << src;
|
||||
}
|
||||
|
||||
|
||||
void PIObject::piConnect(const PIString & src, const PIString & sig, const PIString & dest, void * ev_h) {
|
||||
PIObject * s = findByName(src);
|
||||
if (s == 0) {
|
||||
piCout << "[PIObject] Can`t find object with name \"" << src << "\"!";
|
||||
return;
|
||||
}
|
||||
PIObject * d = findByName(dest);
|
||||
if (d == 0) {
|
||||
piCout << "[PIObject] Can`t find object with name \"" << dest << "\"!";
|
||||
return;
|
||||
}
|
||||
PIMutexLocker _ml(s->mutex_connect);
|
||||
s->connections << Connection(ev_h, 0, sig, d, d);
|
||||
d->connectors << s;
|
||||
}
|
||||
|
||||
/*
|
||||
PIStringList PIObject::events() {
|
||||
PIStringList l;
|
||||
for (PIMap<NamedFunction, PIString>::const_iterator i = signals_.begin(); i != signals_.end(); i++)
|
||||
l << (*i).first;
|
||||
return l;
|
||||
}
|
||||
*/
|
||||
|
||||
PIStringList PIObject::methodsEH() {
|
||||
PIMutexLocker ml(__eh_mutex);
|
||||
PIStringList ret;
|
||||
__EHData & ehd(__eh_data[className()]);
|
||||
piForeachC (__EHPair & eh, ehd.eh_func)
|
||||
ret << eh.second.fullFormat();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool PIObject::isMethodEHContains(const PIString & name) const {
|
||||
PIMutexLocker ml(__eh_mutex);
|
||||
__EHData & ehd(__eh_data[className()]);
|
||||
piForeachC (__EHPair & eh, ehd.eh_func)
|
||||
if (eh.second.func_name == name)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
PIString PIObject::methodEHArguments(const PIString & name) const {
|
||||
PIMutexLocker ml(__eh_mutex);
|
||||
__EHData & ehd(__eh_data[className()]);
|
||||
piForeachC (__EHPair & eh, ehd.eh_func)
|
||||
if (eh.second.func_name == name)
|
||||
return eh.second.arguments();
|
||||
return PIString();
|
||||
}
|
||||
|
||||
|
||||
PIString PIObject::methodEHFullFormat(const PIString & name) const {
|
||||
PIMutexLocker ml(__eh_mutex);
|
||||
__EHData & ehd(__eh_data[className()]);
|
||||
piForeachC (__EHPair & eh, ehd.eh_func)
|
||||
if (eh.second.func_name == name)
|
||||
return eh.second.fullFormat();
|
||||
return PIString();
|
||||
}
|
||||
|
||||
|
||||
PIString PIObject::methodEHFromAddr(const void * addr) const {
|
||||
return methodEH(addr).func_name;
|
||||
}
|
||||
|
||||
|
||||
PIVector<PIObject::__EHFunc> PIObject::findEH(const PIString & name) const {
|
||||
PIVector<__EHFunc> ret;
|
||||
__EHData & ehd(__eh_data[className()]);
|
||||
piForeachC (__EHPair & eh, ehd.eh_func)
|
||||
if (eh.second.func_name == name)
|
||||
ret << eh.second;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIObject::__EHFunc PIObject::methodEH(const void * addr) const {
|
||||
PIMutexLocker ml(__eh_mutex);
|
||||
return __eh_data[className()].eh_func.value(addr);
|
||||
}
|
||||
|
||||
|
||||
void PIObject::piConnect(PIObject * src, const PIString & sig, PIObject * dest_o, void * dest, void * ev_h, void * e_h, int args) {
|
||||
//piCout << "piConnect ...";
|
||||
//piCout << "piConnect" << src << (void*)(dest) << sig;
|
||||
//piCout << "piConnect" << src->className() << "->" << ((PIObject*)dest)->className();
|
||||
PIMutexLocker _ml(src->mutex_connect);
|
||||
src->connections << Connection(ev_h, e_h, sig, dest_o, dest, args);
|
||||
//piCout << "piConnect" << ((PIObject*)dest) << sig << ((PIObject*)dest)->connectors.size_s() << "...";
|
||||
//piCout << "addConnector" << dest_o << src;
|
||||
dest_o->connectors << src;
|
||||
//piCout << "piConnect" << ((PIObject*)dest) << sig << ((PIObject*)dest)->connectors.size_s();
|
||||
//piCout << "piConnect ok";
|
||||
}
|
||||
|
||||
|
||||
bool PIObject::piConnectU(PIObject * src, const PIString & ename, PIObject * dest_o, void * dest, const PIString & hname) {
|
||||
if (src == 0 || dest_o == 0 || dest == 0) return false;
|
||||
PIMutexLocker ml(__eh_mutex);
|
||||
PIVector<__EHFunc> m_src = src->findEH(ename), m_dest = dest_o->findEH(hname);
|
||||
if (m_src.isEmpty()) {
|
||||
piCout << "[piConnectU] Error: can`t find event \"" << ename << "\" in class \"" << src->className() << "\"!";
|
||||
return false;
|
||||
}
|
||||
if (m_dest.isEmpty()) {
|
||||
piCout << "[piConnectU] Error: can`t find handler \"" << hname << "\" in class \"" << dest_o->className() << "\"!";
|
||||
return false;
|
||||
}
|
||||
void * addr_src(0), * addr_dest(0);
|
||||
int args(0);
|
||||
piForeachC (__EHFunc & fs, m_src) {
|
||||
if (addr_src != 0) break;
|
||||
piForeachC (__EHFunc & fd, m_dest) {
|
||||
if (addr_src != 0) break;
|
||||
if (fs.arguments().startsWith(fd.arguments()) || fd.arguments().isEmpty()) {
|
||||
addr_src = fs.addr;
|
||||
addr_dest = fd.addr;
|
||||
args = fd.names.size_s();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (addr_src == 0) {
|
||||
piCout << "[piConnectU] Error: can`t find suitable pair of event \"" << ename << "\" in class \"" << src->className()
|
||||
<< "\" and handler \"" << hname << "\" in class \"" << dest_o->className() << "\"!";
|
||||
return false;
|
||||
}
|
||||
//piCout << "connect" << ename << "->" << hname << "with" << args << "args";
|
||||
src->connections << PIObject::Connection(addr_dest, addr_src, ename, dest_o, dest, args);
|
||||
dest_o->connectors << src;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void PIObject::piDisconnect(PIObject * src, const PIString & sig, PIObject * dest, void * ev_h) {
|
||||
PIMutexLocker _ml(src->mutex_connect);
|
||||
for (int i = 0; i < src->connections.size_s(); ++i) {
|
||||
Connection & cc(src->connections[i]);
|
||||
if (cc.event == sig && cc.dest_o == dest && cc.slot == ev_h) {
|
||||
src->connections.remove(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
((PIObject*)dest)->updateConnectors();
|
||||
}
|
||||
|
||||
|
||||
void PIObject::piDisconnect(PIObject * src, const PIString & sig, PIObject * dest) {
|
||||
PIMutexLocker _ml(src->mutex_connect);
|
||||
for (int i = 0; i < src->connections.size_s(); ++i) {
|
||||
Connection & cc(src->connections[i]);
|
||||
if (cc.event == sig && cc.dest_o == dest) {
|
||||
src->connections.remove(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
((PIObject*)dest)->updateConnectors();
|
||||
}
|
||||
|
||||
|
||||
void PIObject::piDisconnect(PIObject * src, const PIString & sig) {
|
||||
PIMutexLocker _ml(src->mutex_connect);
|
||||
for (int i = 0; i < src->connections.size_s(); ++i) {
|
||||
Connection & cc(src->connections[i]);
|
||||
if (cc.event == sig) {
|
||||
PIObject * dest = cc.dest_o;
|
||||
src->connections.remove(i);
|
||||
i--;
|
||||
dest->updateConnectors();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIObject::piDisconnect(PIObject * src) {
|
||||
PIMutexLocker _ml(src->mutex_connect);
|
||||
PIVector<PIObject * > cv = src->connectors.toVector();
|
||||
piForeach (PIObject * o, cv) {
|
||||
if (o == src) continue;
|
||||
PIVector<Connection> & oc(o->connections);
|
||||
for (int i = 0; i < oc.size_s(); ++i) {
|
||||
//piCout << " check" << (void*)(oc[i].dest_o) << "==" << (void*)(src);
|
||||
if (oc[i].dest_o == src) {
|
||||
oc.remove(i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
piForeachC (PIObject::Connection & c, src->connections)
|
||||
c.dest_o->connectors.remove(src);
|
||||
src->connections.clear();
|
||||
}
|
||||
|
||||
|
||||
void PIObject::updateConnectors() {
|
||||
//piCout << "*** updateConnectors" << this;
|
||||
connectors.clear();
|
||||
piForeach (PIObject * o, objects) {
|
||||
if (o == this) continue;
|
||||
PIVector<Connection> & oc(o->connections);
|
||||
piForeach (Connection & c, oc)
|
||||
if (c.dest == this)
|
||||
connectors << o;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool PIObject::execute(const PIString & method) {
|
||||
if (method.isEmpty()) return false;
|
||||
PIVector<__EHFunc> ml = findEH(method);
|
||||
piForeachC (__EHFunc & m, ml) {
|
||||
if (!m.names.isEmpty()) continue;
|
||||
((void(*)(void*))m.addr)(this);
|
||||
return true;
|
||||
}
|
||||
piCoutObj << "Error: can`t find event or handler \"" << (method + "()") << "\" to execute!";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void PIObject::dump(const PIString & line_prefix) const {
|
||||
//printf("dump %s \"%s\"\n", className(), name().data());
|
||||
PICout(AddNewLine) << line_prefix << "class " << className() << " (" << (const void*)this << ", \"" << name() << "\") {";
|
||||
PICout(AddNewLine) << line_prefix << " properties {";
|
||||
PICout(AddNewLine) << line_prefix << " count: " << properties_.size_s();
|
||||
//printf("dump %d properties\n", properties_.size());
|
||||
piForeachC (Property p, properties_)
|
||||
if (p.first != "name")
|
||||
PICout(AddNewLine) << line_prefix << " " << p.first << ": " << p.second;
|
||||
//printf("dump %d properties ok\n", properties_.size());
|
||||
PICout(AddNewLine) << line_prefix << " }";
|
||||
PICout(AddNewLine) << line_prefix << " methodsEH {";
|
||||
__EHData & ehd(__eh_data[className()]);
|
||||
PICout(AddNewLine) << line_prefix << " count: " << ehd.eh_func.size_s();
|
||||
//printf("dump %d methods\n", ehd.eh_func.size());
|
||||
piForeachC (__EHPair & eh, ehd.eh_func) {
|
||||
PICout(AddNewLine) << line_prefix << " " << eh.second.fullFormat();
|
||||
}
|
||||
//printf("dump %d methods ok\n", ehd.eh_func.size());
|
||||
PICout(AddNewLine) << line_prefix << " }";
|
||||
PICout(AddNewLine) << line_prefix << " connections {";
|
||||
PICout(AddNewLine) << line_prefix << " count: " << connections.size_s();
|
||||
//printf("dump %d connections\n",connections.size());
|
||||
piForeachC (Connection & c, connections) {
|
||||
PIObject * dst = c.dest_o;
|
||||
__EHFunc hf = dst->methodEH(c.slot);
|
||||
__EHFunc ef = methodEH(c.signal);
|
||||
if (hf.func_name.isEmpty()) hf.func_name = "[BROKEN]";
|
||||
else hf.func_name += "(" + hf.arguments() + ")";
|
||||
PIString src(c.event);
|
||||
if (!ef.func_name.isEmpty())
|
||||
src = ef.func_name + "(" + ef.arguments() + ")";
|
||||
PICout(AddNewLine) << line_prefix << " " << src << " -> " << dst->className() << " (" << c.dest << ", \"" << dst->name() << "\")::" << hf.func_name;
|
||||
}
|
||||
//printf("dump %d connections ok\n",connections.size());
|
||||
PICout(AddNewLine) << line_prefix << " }";
|
||||
PICout(AddNewLine) << line_prefix << "}";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void dumpApplication() {
|
||||
//printf("dump application ...\n");
|
||||
PIDateTime cd = PIDateTime::current();
|
||||
PISystemInfo * pi = PISystemInfo::instance();
|
||||
PICout(AddNewLine) << "application {";
|
||||
PICout(AddNewLine) << " PIP version: " << PIPVersion();
|
||||
PICout(AddNewLine) << " processors: " << pi->processorsCount;
|
||||
PICout(AddNewLine) << " hostname: \"" << pi->hostname << "\"";
|
||||
PICout(AddNewLine) << " user: \"" << pi->user << "\"";
|
||||
PICout(AddNewLine) << " exec command: \"" << pi->execCommand << "\"";
|
||||
PICout(AddNewLine) << " started: " << pi->execDateTime.toString();
|
||||
PICout(AddNewLine) << " uptime, s: " << (cd.toSystemTime() - pi->execDateTime.toSystemTime()).toSeconds();
|
||||
PICout(AddNewLine) << " PIObjects {";
|
||||
PICout(AddNewLine) << " count: " << PIObject::objects.size_s();
|
||||
piForeachC (PIObject * o, PIObject::objects)
|
||||
o->dump(" ");
|
||||
PICout(AddNewLine) << " }";
|
||||
PICout(AddNewLine) << "}";
|
||||
//printf("dump application done\n");
|
||||
}
|
||||
|
||||
|
||||
bool dumpApplicationToFile(const PIString & path) {
|
||||
PIFile f(path + "_tmp");
|
||||
f.setName("__S__DumpFile");
|
||||
f.clear();
|
||||
if (!f.open(PIIODevice::WriteOnly)) return false;
|
||||
bool ba = PICout::isBufferActive();
|
||||
PICout::setBufferActive(true, true);
|
||||
dumpApplication();
|
||||
f << PICout::buffer();
|
||||
f.close();
|
||||
PICout::setBufferActive(ba, true);
|
||||
PIFile::rename(path + "_tmp", path);
|
||||
return true;
|
||||
}
|
||||
733
src/core/piobject.h
Executable file
733
src/core/piobject.h
Executable file
@@ -0,0 +1,733 @@
|
||||
/*! \file piobject.h
|
||||
* \brief Base object
|
||||
*
|
||||
* This file declare PIObject class and associated macros
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Object, base class of some PIP classes, provide EVENT -> EVENT_HANDLER mechanism
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIOBJECT_H
|
||||
#define PIOBJECT_H
|
||||
|
||||
#include "piinit.h"
|
||||
#include "pivariant.h"
|
||||
#include "pimutex.h"
|
||||
|
||||
|
||||
#ifdef DOXYGEN
|
||||
|
||||
|
||||
/// \relatesalso PIObject \brief you should use this macro after class declaration to use EVENT and EVENT_HANDLER and correct piCoutObj output
|
||||
#define PIOBJECT(name)
|
||||
|
||||
/// \relatesalso PIObject \brief you should use this macro after class declaration to use EVENT and EVENT_HANDLER of parent class
|
||||
#define PIOBJECT_SUBCLASS(name, parent)
|
||||
|
||||
|
||||
/// \relatesalso PIObject \brief declare event handler \"event\" with name \"name\" and return type \"ret\", ret name()
|
||||
#define EVENT_HANDLER0(ret, name) ret name()
|
||||
|
||||
/// \relatesalso PIObject \brief declare event handler \"event\" with name \"name\" and return type \"ret\", ret name(type0 var0)
|
||||
#define EVENT_HANDLER1(ret, name, type0, var0) ret name(type0 var0)
|
||||
|
||||
/// \relatesalso PIObject \brief declare event handler \"event\" with name \"name\" and return type \"ret\", ret name(type0 var0, type1 var1)
|
||||
#define EVENT_HANDLER2(ret, name, type0, var0, type1, var1) ret name(type0 var0, type1 var1)
|
||||
|
||||
/// \relatesalso PIObject \brief declare event handler \"event\" with name \"name\" and return type \"ret\", ret name(type0 var0, type1 var1, type2 var2)
|
||||
#define EVENT_HANDLER3(ret, name, type0, var0, type1, var1, type2, var2) ret name(type0 var0, type1 var1, type2 var2)
|
||||
|
||||
/// \relatesalso PIObject \brief declare event handler \"event\" with name \"name\" and return type \"ret\", ret name(type0 var0, type1 var1, type2 var2, type3 var3)
|
||||
#define EVENT_HANDLER4(ret, name, type0, var0, type1, var1, type2, var2, type3, var3) ret name(type0 var0, type1 var1, type2 var2, type3 var3)
|
||||
|
||||
/// \relatesalso PIObject \brief EVENT_HANDLER is synonym of EVENT_HANDLER0
|
||||
#define EVENT_HANDLER EVENT_HANDLER0
|
||||
|
||||
|
||||
/// \relatesalso PIObject \brief declare virtual event handler \"event\" with name \"name\" and return type \"ret\", virtual ret name()
|
||||
#define EVENT_VHANDLER0(ret, name) virtual ret name()
|
||||
|
||||
/// \relatesalso PIObject \brief declare virtual event handler \"event\" with name \"name\" and return type \"ret\", virtual ret name(type0 var0)
|
||||
#define EVENT_VHANDLER1(ret, name, type0, var0) virtual ret name(type0 var0)
|
||||
|
||||
/// \relatesalso PIObject \brief declare virtual event handler \"event\" with name \"name\" and return type \"ret\", virtual ret name(type0 var0, type1 var1)
|
||||
#define EVENT_VHANDLER2(ret, name, type0, var0, type1, var1) virtual ret name(type0 var0, type1 var1)
|
||||
|
||||
/// \relatesalso PIObject \brief declare virtual event handler \"event\" with name \"name\" and return type \"ret\", virtual ret name(type0 var0, type1 var1, type2 var2)
|
||||
#define EVENT_VHANDLER3(ret, name, type0, var0, type1, var1, type2, var2) virtual ret name(type0 var0, type1 var1, type2 var2)
|
||||
|
||||
/// \relatesalso PIObject \brief declare virtual event handler \"event\" with name \"name\" and return type \"ret\", virtual ret name(type0 var0, type1 var1, type2 var2, type3 var3)
|
||||
#define EVENT_VHANDLER4(ret, name, type0, var0, type1, var1, type2, var2, type3, var3) virtual ret name(type0 var0, type1 var1, type2 var2, type3 var3)
|
||||
|
||||
/// \relatesalso PIObject \brief EVENT_VHANDLER is synonym of EVENT_VHANDLER0
|
||||
#define EVENT_VHANDLER EVENT_VHANDLER0
|
||||
|
||||
|
||||
/// \relatesalso PIObject \brief declare event \"event\" with name \"name\", void name();
|
||||
#define EVENT0(name) void name();
|
||||
|
||||
/// \relatesalso PIObject \brief declare event \"event\" with name \"name\", void name(type0 var0);
|
||||
#define EVENT1(name, type0, var0) void name(type0 var0);
|
||||
|
||||
/// \relatesalso PIObject \brief declare event \"event\" with name \"name\", void name(type0 var0, type1 var1);
|
||||
#define EVENT2(name, type0, var0, type1, var1) void name(type0 var0, type1 var1);
|
||||
|
||||
/// \relatesalso PIObject \brief declare event \"event\" with name \"name\", void name(type0 var0, type1 var1, type2 var2);
|
||||
#define EVENT3(name, type0, var0, type1, var1, type2, var2) void name(type0 var0, type1 var1, type2 var2);
|
||||
|
||||
/// \relatesalso PIObject \brief declare event \"event\" with name \"name\", void name(type0 var0, type1 var1, type2 var2, type3 var3);
|
||||
#define EVENT4(name, type0, var0, type1, var1, type2, var2, type3, var3) void name(type0 var0, type1 var1, type2 var2, type3 var3);
|
||||
|
||||
/// \relatesalso PIObject \brief EVENT is synonym of EVENT0
|
||||
#define EVENT EVENT0
|
||||
|
||||
|
||||
#define RAISE_EVENT0(src, event)
|
||||
#define RAISE_EVENT1(src, event, v0)
|
||||
#define RAISE_EVENT2(src, event, v0, v1)
|
||||
#define RAISE_EVENT3(src, event, v0, v1, v2)
|
||||
#define RAISE_EVENT4(src, event, v0, v1, v2, v3)
|
||||
#define RAISE_EVENT RAISE_EVENT0
|
||||
|
||||
|
||||
/// \relatesalso PIObject \brief connect event \"event\" from object \"src\" to event handler \"handler\". \"Event\" and \"handler\" must has equal argument lists.
|
||||
#define CONNECTU(src, event, dest, handler)
|
||||
|
||||
/// \relatesalso PIObject \brief connect event \"event\" from object \"src\" to event handler \"handler\" with return type \"ret\" from object \"dest\" with check of event and handler exists
|
||||
#define CONNECT0(ret, src, event, dest, handler)
|
||||
|
||||
/// \relatesalso PIObject \brief connect event \"event\" from object \"src\" to event handler \"handler\" with return type \"ret\" from object \"dest\" with check of event and handler exists
|
||||
#define CONNECT1(ret, type0, src, event, dest, handler)
|
||||
|
||||
/// \relatesalso PIObject \brief connect event \"event\" from object \"src\" to event handler \"handler\" with return type \"ret\" from object \"dest\" with check of event and handler exists
|
||||
#define CONNECT2(ret, type0, type1, src, event, dest, handler)
|
||||
|
||||
/// \relatesalso PIObject \brief connect event \"event\" from object \"src\" to event handler \"handler\" with return type \"ret\" from object \"dest\" with check of event and handler exists
|
||||
#define CONNECT3(ret, type0, type1, type2, src, event, dest, handler)
|
||||
|
||||
/// \relatesalso PIObject \brief connect event \"event\" from object \"src\" to event handler \"handler\" with return type \"ret\" from object \"dest\" with check of event and handler exists
|
||||
#define CONNECT4(ret, type0, type1, type2, type3, src, event, dest, handler)
|
||||
|
||||
/// \relatesalso PIObject \brief CONNECT is synonym of CONNECT0
|
||||
#define CONNECT CONNECT0
|
||||
|
||||
|
||||
/// \relatesalso PIObject \brief connect event \"event\" from object \"src\" to event handler \"handler\" with return type \"ret\" from object \"dest\" without check of event exists
|
||||
#define WEAK_CONNECT0(ret, src, event, dest, handler)
|
||||
|
||||
/// \relatesalso PIObject \brief connect event \"event\" from object \"src\" to event handler \"handler\" with return type \"ret\" from object \"dest\" without check of event exists
|
||||
#define WEAK_CONNECT1(ret, type0, src, event, dest, handler)
|
||||
|
||||
/// \relatesalso PIObject \brief connect event \"event\" from object \"src\" to event handler \"handler\" with return type \"ret\" from object \"dest\" without check of event exists
|
||||
#define WEAK_CONNECT2(ret, type0, type1, src, event, dest, handler)
|
||||
|
||||
/// \relatesalso PIObject \brief connect event \"event\" from object \"src\" to event handler \"handler\" with return type \"ret\" from object \"dest\" without check of event exists
|
||||
#define WEAK_CONNECT3(ret, type0, type1, type2, src, event, dest, handler)
|
||||
|
||||
/// \relatesalso PIObject \brief connect event \"event\" from object \"src\" to event handler \"handler\" with return type \"ret\" from object \"dest\" without check of event exists
|
||||
#define WEAK_CONNECT4(ret, type0, type1, type2, type3, src, event, dest, handler)
|
||||
|
||||
/// \relatesalso PIObject \brief WEAK_CONNECT is synonym of WEAK_CONNECT0
|
||||
#define WEAK_CONNECT WEAK_CONNECT0
|
||||
|
||||
|
||||
/// \relatesalso PIObject \brief piDisconnect event \"event\" from object \"src\" from event handler \"handler\" with return type \"ret\" from object \"dest\"
|
||||
#define DISCONNECT0(ret, src, event, dest, handler)
|
||||
|
||||
/// \relatesalso PIObject \brief piDisconnect event \"event\" from object \"src\" from event handler \"handler\" with return type \"ret\" from object \"dest\"
|
||||
#define DISCONNECT1(ret, type0, src, event, dest, handler)
|
||||
|
||||
/// \relatesalso PIObject \brief piDisconnect event \"event\" from object \"src\" from event handler \"handler\" with return type \"ret\" from object \"dest\"
|
||||
#define DISCONNECT2(ret, type0, type1, src, event, dest, handler)
|
||||
|
||||
/// \relatesalso PIObject \brief piDisconnect event \"event\" from object \"src\" from event handler \"handler\" with return type \"ret\" from object \"dest\"
|
||||
#define DISCONNECT3(ret, type0, type1, type2, src, event, dest, handler)
|
||||
|
||||
/// \relatesalso PIObject \brief piDisconnect event \"event\" from object \"src\" from event handler \"handler\" with return type \"ret\" from object \"dest\"
|
||||
#define DISCONNECT4(ret, type0, type1, type2, type3, src, event, dest, handler)
|
||||
|
||||
/// \relatesalso PIObject \brief DISCONNECT is synonym of DISCONNECT0
|
||||
#define DISCONNECT DISCONNECT0
|
||||
|
||||
|
||||
/// \relatesalso PIObject \brief Returns pointer to events handler \"handler\"
|
||||
#define HANDLER(handler)
|
||||
|
||||
|
||||
#define PIOBJECT(name)
|
||||
#define PIOBJECT_SUBCLASS(name)
|
||||
|
||||
|
||||
#else
|
||||
|
||||
|
||||
#define PIOBJECT(name) \
|
||||
protected: \
|
||||
typedef name __PIObject__; \
|
||||
static const PIString __classNameS() {return PIString(#name);} \
|
||||
public: \
|
||||
virtual const char * className() const {return #name;} \
|
||||
private:
|
||||
|
||||
#define PIOBJECT_PARENT(name) \
|
||||
class __##name##_ParentInitializer__ { \
|
||||
public: \
|
||||
__##name##_ParentInitializer__() { \
|
||||
PIString pn(name::__classNameS()); \
|
||||
if (pn.isEmpty()) return; \
|
||||
PIMutexLocker ml(__eh_mutex); \
|
||||
if (__eh_data.contains(__classNameS())) return; \
|
||||
__eh_data[pn]; \
|
||||
__eh_data[__classNameS()]; \
|
||||
__EHData & ehp(__eh_data[pn]); \
|
||||
__EHData & eh(__eh_data[__classNameS()]); \
|
||||
eh.eh_set << ehp.eh_set; \
|
||||
eh.eh_func << ehp.eh_func; \
|
||||
} \
|
||||
}; \
|
||||
__##name##_ParentInitializer__ __##name##_parent_init__; \
|
||||
public: \
|
||||
virtual const char * superClassName() const {return #name;} \
|
||||
private:
|
||||
|
||||
#define PIOBJECT_SUBCLASS(name, parent) PIOBJECT(name) PIOBJECT_PARENT(parent)
|
||||
|
||||
|
||||
#define EH_INIT0(ret, name) \
|
||||
class __##name##0_Initializer__ { \
|
||||
public: \
|
||||
__##name##0_Initializer__() { \
|
||||
PIMutexLocker ml(__eh_mutex); \
|
||||
__EHData & eh(__eh_data[__classNameS()]); \
|
||||
void * fp = (void*)(ret(*)(void*))__stat_eh_##name##__; \
|
||||
if (eh.eh_set[fp]) return; \
|
||||
eh.eh_set << fp; \
|
||||
__EHFunc & f(eh.eh_func[fp]); \
|
||||
f.scope = __classNameS(); \
|
||||
f.func_name = #name; \
|
||||
f.addr = fp; \
|
||||
f.type_ret = #ret; \
|
||||
} \
|
||||
}; \
|
||||
__##name##0_Initializer__ __##name##0_init__; \
|
||||
|
||||
#define EH_INIT1(ret, name, a0, n0) \
|
||||
class __##name##1##n0##_Initializer__ { \
|
||||
public: \
|
||||
__##name##1##n0##_Initializer__() { \
|
||||
PIMutexLocker ml(__eh_mutex); \
|
||||
__EHData & eh(__eh_data[__classNameS()]); \
|
||||
void * fp = (void*)(ret(*)(void*, a0))__stat_eh_##name##__; \
|
||||
if (eh.eh_set[fp]) return; \
|
||||
eh.eh_set << fp; \
|
||||
__EHFunc & f(eh.eh_func[fp]); \
|
||||
f.scope = __classNameS(); \
|
||||
f.func_name = #name; \
|
||||
f.addr = fp; \
|
||||
f.type_ret = #ret; \
|
||||
f.types << #a0; \
|
||||
f.names << #n0; \
|
||||
} \
|
||||
}; \
|
||||
__##name##1##n0##_Initializer__ __##name##1##n0##_init__; \
|
||||
|
||||
#define EH_INIT2(ret, name, a0, n0, a1, n1) \
|
||||
class __##name##2##n0##n1##_Initializer__ { \
|
||||
public: \
|
||||
__##name##2##n0##n1##_Initializer__() { \
|
||||
PIMutexLocker ml(__eh_mutex); \
|
||||
__EHData & eh(__eh_data[__classNameS()]); \
|
||||
void * fp = (void*)(ret(*)(void*, a0, a1))__stat_eh_##name##__; \
|
||||
if (eh.eh_set[fp]) return; \
|
||||
eh.eh_set << fp; \
|
||||
__EHFunc & f(eh.eh_func[fp]); \
|
||||
f.scope = __classNameS(); \
|
||||
f.func_name = #name; \
|
||||
f.addr = fp; \
|
||||
f.type_ret = #ret; \
|
||||
f.types << #a0 << #a1; \
|
||||
f.names << #n0 << #n1; \
|
||||
} \
|
||||
}; \
|
||||
__##name##2##n0##n1##_Initializer__ __##name##2##n0##n1##_init__; \
|
||||
|
||||
#define EH_INIT3(ret, name, a0, n0, a1, n1, a2, n2) \
|
||||
class __##name##3##n0##n1##n2##_Initializer__ { \
|
||||
public: \
|
||||
__##name##3##n0##n1##n2##_Initializer__() { \
|
||||
PIMutexLocker ml(__eh_mutex); \
|
||||
__EHData & eh(__eh_data[__classNameS()]); \
|
||||
void * fp = (void*)(ret(*)(void*, a0, a1, a2))__stat_eh_##name##__; \
|
||||
if (eh.eh_set[fp]) return; \
|
||||
eh.eh_set << fp; \
|
||||
__EHFunc & f(eh.eh_func[fp]); \
|
||||
f.scope = __classNameS(); \
|
||||
f.func_name = #name; \
|
||||
f.addr = fp; \
|
||||
f.type_ret = #ret; \
|
||||
f.types << #a0 << #a1 << #a2; \
|
||||
f.names << #n0 << #n1 << #n2; \
|
||||
} \
|
||||
}; \
|
||||
__##name##3##n0##n1##n2##_Initializer__ __##name##3##n0##n1##n2##_init__; \
|
||||
|
||||
#define EH_INIT4(ret, name, a0, n0, a1, n1, a2, n2, a3, n3) \
|
||||
class __##name##4##n0##n1##n2##n3##_Initializer__ { \
|
||||
public: \
|
||||
__##name##4##n0##n1##n2##n3##_Initializer__() { \
|
||||
PIMutexLocker ml(__eh_mutex); \
|
||||
__EHData & eh(__eh_data[__classNameS()]); \
|
||||
void * fp = (void*)(ret(*)(void*, a0, a1, a2, a3))__stat_eh_##name##__; \
|
||||
if (eh.eh_set[fp]) return; \
|
||||
eh.eh_set << fp; \
|
||||
__EHFunc & f(eh.eh_func[fp]); \
|
||||
f.scope = __classNameS(); \
|
||||
f.func_name = #name; \
|
||||
f.addr = fp; \
|
||||
f.type_ret = #ret; \
|
||||
f.types << #a0 << #a1 << #a2 << #a3; \
|
||||
f.names << #n0 << #n1 << #n2 << #n3; \
|
||||
} \
|
||||
}; \
|
||||
__##name##4##n0##n1##n2##n3##_Initializer__ __##name##4##n0##n1##n2##n3##_init__; \
|
||||
|
||||
|
||||
#define EVENT_HANDLER0(ret, name) \
|
||||
EH_INIT0(ret, name) \
|
||||
static ret __stat_eh_##name##__(void * __o__) {return ((__PIObject__*)__o__)->name();} \
|
||||
ret name()
|
||||
|
||||
#define EVENT_HANDLER1(ret, name, a0, n0) \
|
||||
EH_INIT1(ret, name, a0, n0) \
|
||||
static ret __stat_eh_##name##__(void * __o__, a0 n0) {return ((__PIObject__*)__o__)->name(n0);} \
|
||||
ret name(a0 n0)
|
||||
|
||||
#define EVENT_HANDLER2(ret, name, a0, n0, a1, n1) \
|
||||
EH_INIT2(ret, name, a0, n0, a1, n1) \
|
||||
static ret __stat_eh_##name##__(void * __o__, a0 n0, a1 n1) {return ((__PIObject__*)__o__)->name(n0, n1);} \
|
||||
ret name(a0 n0, a1 n1)
|
||||
|
||||
#define EVENT_HANDLER3(ret, name, a0, n0, a1, n1, a2, n2) \
|
||||
EH_INIT3(ret, name, a0, n0, a1, n1, a2, n2) \
|
||||
static ret __stat_eh_##name##__(void * __o__, a0 n0, a1 n1, a2 n2) {return ((__PIObject__*)__o__)->name(n0, n1, n2);} \
|
||||
ret name(a0 n0, a1 n1, a2 n2)
|
||||
|
||||
#define EVENT_HANDLER4(ret, name, a0, n0, a1, n1, a2, n2, a3, n3) \
|
||||
EH_INIT4(ret, name, a0, n0, a1, n1, a2, n2, a3, n3) \
|
||||
static ret __stat_eh_##name##__(void * __o__, a0 n0, a1 n1, a2 n2, a3 n3) {return ((__PIObject__*)__o__)->name(n0, n1, n2, n3);} \
|
||||
ret name(a0 n0, a1 n1, a2 n2, a3 n3)
|
||||
|
||||
#define EVENT_HANDLER EVENT_HANDLER0
|
||||
|
||||
|
||||
#define EVENT_VHANDLER0(ret, name) \
|
||||
EH_INIT0(ret, name) \
|
||||
static ret __stat_eh_##name##__(void * __o__) {return ((__PIObject__*)__o__)->name();} \
|
||||
virtual ret name()
|
||||
|
||||
#define EVENT_VHANDLER1(ret, name, a0, n0) \
|
||||
EH_INIT1(ret, name, a0, n0) \
|
||||
static ret __stat_eh_##name##__(void * __o__, a0 n0) {return ((__PIObject__*)__o__)->name(n0);} \
|
||||
virtual ret name(a0 n0)
|
||||
|
||||
#define EVENT_VHANDLER2(ret, name, a0, n0, a1, n1) \
|
||||
EH_INIT2(ret, name, a0, n0, a1, n1) \
|
||||
static ret __stat_eh_##name##__(void * __o__, a0 n0, a1 n1) {return ((__PIObject__*)__o__)->name(n0, n1);} \
|
||||
virtual ret name(a0 n0, a1 n1)
|
||||
|
||||
#define EVENT_VHANDLER3(ret, name, a0, n0, a1, n1, a2, n2) \
|
||||
EH_INIT3(ret, name, a0, n0, a1, n1, a2, n2) \
|
||||
static ret __stat_eh_##name##__(void * __o__, a0 n0, a1 n1, a2 n2) {return ((__PIObject__*)__o__)->name(n0, n1, n2);} \
|
||||
virtual ret name(a0 n0, a1 n1, a2 n2)
|
||||
|
||||
#define EVENT_VHANDLER4(ret, name, a0, n0, a1, n1, a2, n2, a3, n3) \
|
||||
EH_INIT4(ret, name, a0, n0, a1, n1, a2, n2, a3, n3) \
|
||||
static ret __stat_eh_##name##__(void * __o__, a0 n0, a1 n1, a2 n2, a3 n3) {return ((__PIObject__*)__o__)->name(n0, n1, n2, n3);} \
|
||||
virtual ret name(a0 n0, a1 n1, a2 n2, a3 n3)
|
||||
|
||||
#define EVENT_VHANDLER EVENT_VHANDLER0
|
||||
|
||||
|
||||
#define EVENT0(name) EVENT_HANDLER0(void, name) {PIObject::raiseEvent(this, #name);}
|
||||
#define EVENT1(name, a0, n0) EVENT_HANDLER1(void, name, a0, n0) {PIObject::raiseEvent(this, #name, n0);}
|
||||
#define EVENT2(name, a0, n0, a1, n1) EVENT_HANDLER2(void, name, a0, n0, a1, n1) {PIObject::raiseEvent(this, #name, n0, n1);}
|
||||
#define EVENT3(name, a0, n0, a1, n1, a2, n2) EVENT_HANDLER3(void, name, a0, n0, a1, n1, a2, n2) {PIObject::raiseEvent(this, #name, n0, n1, n2);}
|
||||
#define EVENT4(name, a0, n0, a1, n1, a2, n2, a3, n3) EVENT_HANDLER4(void, name, a0, n0, a1, n1, a2, n2, a3, n3) {PIObject::raiseEvent(this, #name, n0, n1, n2, n3);}
|
||||
#define EVENT EVENT0
|
||||
|
||||
#define RAISE_EVENT0(src, event) (src)->event();
|
||||
#define RAISE_EVENT1(src, event, v0) (src)->event(v0);
|
||||
#define RAISE_EVENT2(src, event, v0, v1) (src)->event(v0, v1);
|
||||
#define RAISE_EVENT3(src, event, v0, v1, v2) (src)->event(v0, v1, v2);
|
||||
#define RAISE_EVENT4(src, event, v0, v1, v2, v3) (src)->event(v0, v1, v2, v3);
|
||||
#define RAISE_EVENT RAISE_EVENT0
|
||||
|
||||
#define CONNECTU(src, event, dest, handler) PIObject::piConnectU(src, #event, dest, dest, #handler);
|
||||
|
||||
#define CONNECT0(ret, src, event, dest, handler) PIObject::piConnect(src, #event, dest, dest, (void*)(ret(*)(void*))(&(dest)->__stat_eh_##handler##__), (void*)(void(*)(void*))(&(src)->__stat_eh_##event##__), 0);
|
||||
#define CONNECT1(ret, a0, src, event, dest, handler) PIObject::piConnect(src, #event, dest, dest, (void*)(ret(*)(void*, a0))(&(dest)->__stat_eh_##handler##__), (void*)(void(*)(void*, a0))(&(src)->__stat_eh_##event##__), 1);
|
||||
#define CONNECT2(ret, a0, a1, src, event, dest, handler) PIObject::piConnect(src, #event, dest, dest, (void*)(ret(*)(void*, a0, a1))(&(dest)->__stat_eh_##handler##__), (void*)(void(*)(void*, a0, a1))(&(src)->__stat_eh_##event##__), 2);
|
||||
#define CONNECT3(ret, a0, a1, a2, src, event, dest, handler) PIObject::piConnect(src, #event, dest, dest, (void*)(ret(*)(void*, a0, a1, a2))(&(dest)->__stat_eh_##handler##__), (void*)(void(*)(void*, a0, a1, a2))(&(src)->__stat_eh_##event##__), 3);
|
||||
#define CONNECT4(ret, a0, a1, a2, a3, src, event, dest, handler) PIObject::piConnect(src, #event, dest, dest, (void*)(ret(*)(void*, a0, a1, a2, a3))(&(dest)->__stat_eh_##handler##__), (void*)(void(*)(void*, a0, a1, a2, a3))(&(src)->__stat_eh_##event##__), 4);
|
||||
#define CONNECT CONNECT0
|
||||
|
||||
#define WEAK_CONNECT0(ret, src, event, dest, handler) PIObject::piConnect(src, #event, dest, dest, (void*)(ret(*)(void*))(&(dest)->__stat_eh_##handler##__), 0, 0);
|
||||
#define WEAK_CONNECT1(ret, a0, src, event, dest, handler) PIObject::piConnect(src, #event, dest, dest, (void*)(ret(*)(void*, a0))(&(dest)->__stat_eh_##handler##__), 0, 1);
|
||||
#define WEAK_CONNECT2(ret, a0, a1, src, event, dest, handler) PIObject::piConnect(src, #event, dest, dest, (void*)(ret(*)(void*, a0, a1))(&(dest)->__stat_eh_##handler##__), 0, 2);
|
||||
#define WEAK_CONNECT3(ret, a0, a1, a2, src, event, dest, handler) PIObject::piConnect(src, #event, dest, dest, (void*)(ret(*)(void*, a0, a1, a2))(&(dest)->__stat_eh_##handler##__), 0, 3);
|
||||
#define WEAK_CONNECT4(ret, a0, a1, a2, a3, src, event, dest, handler) PIObject::piConnect(src, #event, dest, dest, (void*)(ret(*)(void*, a0, a1, a2, a3))(&(dest)->__stat_eh_##handler##__), 0, 4);
|
||||
#define WEAK_CONNECT WEAK_CONNECT0
|
||||
|
||||
#define DISCONNECT0(ret, src, event, dest, handler) PIObject::piDisconnect(src, #event, dest, (void*)(ret(*)(void*))(&(dest)->__stat_eh_##handler##__));
|
||||
#define DISCONNECT1(ret, a0, src, event, dest, handler) PIObject::piDisconnect(src, #event, dest, (void*)(ret(*)(void*, a0))(&(dest)->__stat_eh_##handler##__));
|
||||
#define DISCONNECT2(ret, a0, a1, src, event, dest, handler) PIObject::piDisconnect(src, #event, dest, (void*)(ret(*)(void*, a0, a1))(&(dest)->__stat_eh_##handler##__));
|
||||
#define DISCONNECT3(ret, a0, a1, a2, src, event, dest, handler) PIObject::piDisconnect(src, #event, dest, (void*)(ret(*)(void*, a0, a1, a2))(&(dest)->__stat_eh_##handler##__));
|
||||
#define DISCONNECT4(ret, a0, a1, a2, a3, src, event, dest, handler) PIObject::piDisconnect(src, #event, dest, (void*)(ret(*)(void*, a0, a1, a2, a3))(&(dest)->__stat_eh_##handler##__));
|
||||
#define DISCONNECT DISCONNECT0
|
||||
|
||||
#define HANDLER(handler) __stat_eh_##handler##__
|
||||
|
||||
#define __PIOBJECT_SIGNATURE__ 0xabcdbadc
|
||||
|
||||
#endif
|
||||
|
||||
typedef void (*Handler)(void * );
|
||||
|
||||
class PIP_EXPORT PIObject
|
||||
{
|
||||
friend class PIObjectManager;
|
||||
friend void dumpApplication();
|
||||
public:
|
||||
|
||||
//! Contructs PIObject with name "name"
|
||||
PIObject(const PIString & name = PIString());
|
||||
|
||||
virtual ~PIObject();
|
||||
|
||||
private:
|
||||
uint _signature_;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
//! Returns object name
|
||||
PIString name() const {return property("name").toString();}
|
||||
|
||||
//! Returns object class name
|
||||
virtual const char * className() const {return "PIObject";}
|
||||
|
||||
//! Returns object superclass name
|
||||
virtual const char * superClassName() const {return "";}
|
||||
|
||||
//! Return if debug of this object is active
|
||||
bool debug() const {return property("debug").toBool();}
|
||||
|
||||
|
||||
//! Set object name
|
||||
void setName(const PIString & name) {setProperty("name", name);}
|
||||
|
||||
//! Set object debug active
|
||||
void setDebug(bool debug) {setProperty("debug", debug);}
|
||||
|
||||
//! Returns properties of the object
|
||||
const PIMap<PIString, PIVariant> & properties() const {return properties_;}
|
||||
|
||||
//! Returns properties count of the object
|
||||
int propertiesCount() const {return properties_.size_s();}
|
||||
|
||||
//! Returns property with name "name"
|
||||
PIVariant property(const PIString & name) const {if (!properties_.contains(name)) return PIVariant(); return properties_.value(name);}
|
||||
|
||||
//! Set property with name "name" to "value". If there is no such property in object it will be added
|
||||
void setProperty(const PIString & name, const PIVariant & value) {properties_[name] = value; propertyChanged(name);}
|
||||
|
||||
//! Returns if property with name "name" exists
|
||||
bool isPropertyExists(const PIString & name) const {return properties_.contains(name);}
|
||||
|
||||
void setThreadSafe(bool yes) {thread_safe_ = yes;}
|
||||
bool isThreadSafe() const {return thread_safe_;}
|
||||
|
||||
void dump(const PIString & line_prefix = PIString()) const;
|
||||
|
||||
|
||||
PIStringList methodsEH();
|
||||
bool isMethodEHContains(const PIString & name) const;
|
||||
PIString methodEHArguments(const PIString & name) const;
|
||||
PIString methodEHFullFormat(const PIString & name) const;
|
||||
PIString methodEHFromAddr(const void * addr) const;
|
||||
|
||||
/*
|
||||
template <typename RS, typename RD>
|
||||
static void piConnectU(PIObject * src, RS(*sig)(void*), PIObject * dest, RD(*slt)(void*), PIString signame) {
|
||||
src->connections << PIObject::Connection((void*)slt, (void*)sig, signame, dest);
|
||||
}
|
||||
template <typename RS, typename RD, typename A0>
|
||||
static void piConnectU(PIObject * src, RS(*sig)(void*,A0), PIObject * dest, RD(*slt)(void*,A0), PIString signame) {
|
||||
src->connections << PIObject::Connection((void*)slt, (void*)sig, signame, dest);
|
||||
}
|
||||
template <typename RS, typename RD, typename A0, typename A1>
|
||||
static void piConnectU(PIObject * src, RS(*sig)(void*,A0,A1), PIObject * dest, RD(*slt)(void*,A0,A1), PIString signame) {
|
||||
src->connections << PIObject::Connection((void*)slt, (void*)sig, signame, dest);
|
||||
}
|
||||
template <typename RS, typename RD, typename A0, typename A1, typename A2>
|
||||
static void piConnectU(PIObject * src, RS(*sig)(void*,A0,A1,A2), PIObject * dest, RD(*slt)(void*,A0,A1,A2), PIString signame) {
|
||||
src->connections << PIObject::Connection((void*)slt, (void*)sig, signame, dest);
|
||||
}
|
||||
template <typename RS, typename RD, typename A0, typename A1, typename A2, typename A3>
|
||||
static void piConnectU(PIObject * src, RS(*sig)(void*,A0,A1,A2,A3), PIObject * dest, RD(*slt)(void*,A0,A1,A2,A3), PIString signame) {
|
||||
src->connections << PIObject::Connection((void*)slt, (void*)sig, signame, dest);
|
||||
}
|
||||
template <typename RS, typename RD, typename A0, typename A1, typename A2, typename A3, typename A4>
|
||||
static void piConnectU(PIObject * src, RS(*sig)(void*,A0,A1,A2,A3,A4), PIObject * dest, RD(*slt)(void*,A0,A1,A2,A3,A4), PIString signame) {
|
||||
src->connections << PIObject::Connection((void*)slt, (void*)sig, signame, dest);
|
||||
}
|
||||
*/
|
||||
|
||||
// / Direct connect
|
||||
static void piConnect(PIObject * src, const PIString & sig, PIObject * dest_o, void * dest, void * ev_h, void * e_h, int args);
|
||||
static bool piConnectU(PIObject * src, const PIString & ename, PIObject * dest_o, void * dest, const PIString & hname);
|
||||
|
||||
// / Through names and mixed
|
||||
static void piConnect(const PIString & src, const PIString & sig, void * dest, void * ev_h);
|
||||
static void piConnect(PIObject * src, const PIString & sig, const PIString & dest, void * ev_h);
|
||||
static void piConnect(const PIString & src, const PIString & sig, const PIString & dest, void * ev_h);
|
||||
|
||||
|
||||
static void piDisconnect(PIObject * src, const PIString & sig, PIObject * dest, void * ev_h);
|
||||
static void piDisconnect(PIObject * src, const PIString & sig, PIObject * dest);
|
||||
|
||||
//! Disconnect object "src" from all connections with event name "sig"
|
||||
static void piDisconnect(PIObject * src, const PIString & sig);
|
||||
|
||||
//! Disconnect object "src" from all connections, i.e. all connections where object "src" is emitter
|
||||
static void piDisconnect(PIObject * src);
|
||||
|
||||
// / Raise events
|
||||
static void raiseEvent(PIObject * sender, const PIString & event) {
|
||||
for (int j = 0; j < sender->connections.size_s(); ++j) {
|
||||
Connection & i(sender->connections[j]);
|
||||
if (i.event != event) continue;
|
||||
//piCout << uint(i.dest) << uint(i.dest_o);
|
||||
if (sender->thread_safe_) i.dest_o->mutex_.lock();
|
||||
i.dest_o->emitter_ = sender;
|
||||
((void( *)(void * ))i.slot)(i.dest);
|
||||
i.dest_o->emitter_ = 0;
|
||||
if (sender->thread_safe_) i.dest_o->mutex_.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T0>
|
||||
static void raiseEvent(PIObject * sender, const PIString & event, const T0 & v0 = T0()) {
|
||||
for (int j = 0; j < sender->connections.size_s(); ++j) {
|
||||
Connection & i(sender->connections[j]);
|
||||
if (i.event != event) continue;
|
||||
if (sender->thread_safe_) i.dest_o->mutex_.lock();
|
||||
i.dest_o->emitter_ = sender;
|
||||
if (i.args_count == 0) ((void(*)(void *))i.slot)(i.dest);
|
||||
else ((void(*)(void * , T0))i.slot)(i.dest, v0);
|
||||
i.dest_o->emitter_ = 0;
|
||||
if (sender->thread_safe_) i.dest_o->mutex_.unlock();
|
||||
}
|
||||
}
|
||||
template <typename T0, typename T1>
|
||||
static void raiseEvent(PIObject * sender, const PIString & event, const T0 & v0 = T0(), const T1 & v1 = T1()) {
|
||||
for (int j = 0; j < sender->connections.size_s(); ++j) {
|
||||
Connection & i(sender->connections[j]);
|
||||
if (i.event != event) continue;
|
||||
if (sender->thread_safe_) i.dest_o->mutex_.lock();
|
||||
i.dest_o->emitter_ = sender;
|
||||
switch (i.args_count) {
|
||||
case 0: ((void(*)(void *))i.slot)(i.dest); break;
|
||||
case 1: ((void(*)(void * , T0))i.slot)(i.dest, v0); break;
|
||||
default: ((void(*)(void * , T0, T1))i.slot)(i.dest, v0, v1); break;
|
||||
}
|
||||
i.dest_o->emitter_ = 0;
|
||||
if (sender->thread_safe_) i.dest_o->mutex_.unlock();
|
||||
}
|
||||
}
|
||||
template <typename T0, typename T1, typename T2>
|
||||
static void raiseEvent(PIObject * sender, const PIString & event, const T0 & v0 = T0(), const T1 & v1 = T1(), const T2 & v2 = T2()) {
|
||||
for (int j = 0; j < sender->connections.size_s(); ++j) {
|
||||
Connection & i(sender->connections[j]);
|
||||
if (i.event != event) continue;
|
||||
if (sender->thread_safe_) i.dest_o->mutex_.lock();
|
||||
i.dest_o->emitter_ = sender;
|
||||
switch (i.args_count) {
|
||||
case 0: ((void(*)(void *))i.slot)(i.dest); break;
|
||||
case 1: ((void(*)(void * , T0))i.slot)(i.dest, v0); break;
|
||||
case 2: ((void(*)(void * , T0, T1))i.slot)(i.dest, v0, v1); break;
|
||||
default: ((void(*)(void * , T0, T1, T2))i.slot)(i.dest, v0, v1, v2); break;
|
||||
}
|
||||
i.dest_o->emitter_ = 0;
|
||||
if (sender->thread_safe_) i.dest_o->mutex_.unlock();
|
||||
}
|
||||
}
|
||||
template <typename T0, typename T1, typename T2, typename T3>
|
||||
static void raiseEvent(PIObject * sender, const PIString & event, const T0 & v0 = T0(), const T1 & v1 = T1(), const T2 & v2 = T2(), const T3 & v3 = T3()) {
|
||||
for (int j = 0; j < sender->connections.size_s(); ++j) {
|
||||
Connection & i(sender->connections[j]);
|
||||
if (i.event != event) continue;
|
||||
if (sender->thread_safe_) i.dest_o->mutex_.lock();
|
||||
i.dest_o->emitter_ = sender;
|
||||
switch (i.args_count) {
|
||||
case 0: ((void(*)(void *))i.slot)(i.dest); break;
|
||||
case 1: ((void(*)(void * , T0))i.slot)(i.dest, v0); break;
|
||||
case 2: ((void(*)(void * , T0, T1))i.slot)(i.dest, v0, v1); break;
|
||||
case 3: ((void(*)(void * , T0, T1, T2))i.slot)(i.dest, v0, v1, v2); break;
|
||||
default: ((void(*)(void * , T0, T1, T2, T3))i.slot)(i.dest, v0, v1, v2, v3); break;
|
||||
}
|
||||
i.dest_o->emitter_ = 0;
|
||||
if (sender->thread_safe_) i.dest_o->mutex_.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// / Raise events through manager
|
||||
static void raiseEvent(const PIString & destObject, const PIString & name) {
|
||||
PIObject * dest = findByName(destObject);
|
||||
if (dest == 0) {
|
||||
cout << "PIObject::piConnect: can`t find PIObject with \"" << destObject << "\" name!" << endl;
|
||||
return;
|
||||
}
|
||||
raiseEvent(dest, name);
|
||||
}
|
||||
template <typename T0>
|
||||
static void raiseEvent(const PIString & destObject, const PIString & name, const T0 & v0 = T0()) {
|
||||
PIObject * dest = findByName(destObject);
|
||||
if (dest == 0) {
|
||||
cout << "PIObject::piConnect: can`t find PIObject with \"" << destObject << "\" name!" << endl;
|
||||
return;
|
||||
}
|
||||
raiseEvent<T0>(dest, name, v0);
|
||||
}
|
||||
template <typename T0, typename T1>
|
||||
static void raiseEvent(const PIString & destObject, const PIString & name, const T0 & v0 = T0(), const T1 & v1 = T1()) {
|
||||
PIObject * dest = findByName(destObject);
|
||||
if (dest == 0) {
|
||||
cout << "PIObject::piConnect: can`t find PIObject with \"" << destObject << "\" name!" << endl;
|
||||
return;
|
||||
}
|
||||
raiseEvent<T0, T1>(dest, name, v0, v1);
|
||||
}
|
||||
template <typename T0, typename T1, typename T2>
|
||||
static void raiseEvent(const PIString & destObject, const PIString & name, const T0 & v0 = T0(), const T1 & v1 = T1(), const T2 & v2 = T2()) {
|
||||
PIObject * dest = findByName(destObject);
|
||||
if (dest == 0) {
|
||||
cout << "PIObject::piConnect: can`t find PIObject with \"" << destObject << "\" name!" << endl;
|
||||
return;
|
||||
}
|
||||
raiseEvent<T0, T1, T2>(name, dest, v0, v1, v2);
|
||||
}
|
||||
template <typename T0, typename T1, typename T2, typename T3>
|
||||
static void raiseEvent(const PIString & destObject, const PIString & name, const T0 & v0 = T0(), const T1 & v1 = T1(), const T2 & v2 = T2(), const T3 & v3 = T3()) {
|
||||
PIObject * dest = findByName(destObject);
|
||||
if (dest == 0) {
|
||||
cout << "PIObject::piConnect: can`t find PIObject with \"" << destObject << "\" name!" << endl;
|
||||
return;
|
||||
}
|
||||
raiseEvent<T0, T1, T2, T3>(name,dest , v0, v1, v2, v3);
|
||||
}
|
||||
*/
|
||||
|
||||
//! Returns PIObject* with name "name" or 0, if there is no object found
|
||||
static PIObject * findByName(const PIString & name) {
|
||||
piForeach (PIObject * i, PIObject::objects) {
|
||||
if (i->name() != name) continue;
|
||||
return i;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
bool isPIObject() const {return isPIObject(this);}
|
||||
bool execute(const PIString & method);
|
||||
static bool isPIObject(const PIObject * o) {return o->_signature_ == __PIOBJECT_SIGNATURE__;}
|
||||
static bool isPIObject(const void * o) {return isPIObject((PIObject*)o);}
|
||||
static bool execute(PIObject * o, const PIString & method) {return o->execute(method);}
|
||||
static bool execute(void * o, const PIString & method) {return ((PIObject*)o)->execute(method);}
|
||||
|
||||
struct __EHFunc {
|
||||
__EHFunc(): addr(0) {;}
|
||||
bool isNull() const {return addr == 0;}
|
||||
PIString arguments() const {return types.join(",").removeAll(" ").removeAll("\t");}
|
||||
PIString fullFormat() const;
|
||||
void * addr;
|
||||
PIString func_name;
|
||||
PIString type_ret;
|
||||
PIString scope;
|
||||
PIStringList types;
|
||||
PIStringList names;
|
||||
};
|
||||
struct __EHData {
|
||||
PISet<const void * > eh_set;
|
||||
PIMap<const void * , __EHFunc> eh_func;
|
||||
};
|
||||
typedef PIPair<const void * , __EHFunc> __EHPair;
|
||||
static PIMap<PIString, __EHData> __eh_data;
|
||||
static PIMutex __eh_mutex;
|
||||
|
||||
protected:
|
||||
|
||||
//! Returns PIObject* which has raised an event. This value is correct only in definition of some event handler
|
||||
PIObject * emitter() const {return emitter_;}
|
||||
|
||||
//! Virtual function executes after property with name "name" has been changed
|
||||
virtual void propertyChanged(const PIString & name) {}
|
||||
|
||||
|
||||
static const PIString __classNameS() {return PIString();} \
|
||||
|
||||
private:
|
||||
struct Connection {
|
||||
Connection(void * sl = 0, void * si = 0, const PIString & e = PIString(), PIObject * d_o = 0, void * d = 0, int ac = 0) {
|
||||
slot = sl;
|
||||
signal = si;
|
||||
event = e;
|
||||
dest_o = d_o;
|
||||
dest = d;
|
||||
args_count = ac;
|
||||
}
|
||||
void * slot;
|
||||
void * signal;
|
||||
PIString event;
|
||||
PIObject * dest_o;
|
||||
void * dest;
|
||||
int args_count;
|
||||
};
|
||||
|
||||
PIVector<__EHFunc> findEH(const PIString & name) const;
|
||||
__EHFunc methodEH(const void * addr) const;
|
||||
void updateConnectors();
|
||||
|
||||
PIVector<Connection> connections;
|
||||
typedef PIPair<PIString, PIVariant> Property;
|
||||
PIMap<PIString, PIVariant> properties_;
|
||||
|
||||
static PIVector<PIObject * > objects;
|
||||
|
||||
PISet<PIObject * > connectors;
|
||||
PIMutex mutex_, mutex_connect;
|
||||
PIObject * emitter_;
|
||||
bool thread_safe_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
void dumpApplication();
|
||||
bool dumpApplicationToFile(const PIString & path);
|
||||
|
||||
#endif // PIOBJECT_H
|
||||
334
src/core/pistatemachine.h
Executable file
334
src/core/pistatemachine.h
Executable file
@@ -0,0 +1,334 @@
|
||||
/*! \file pistatemachine.h
|
||||
* \brief Base class for custom state machine
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
State machine
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PISTATEMACHINE_H
|
||||
#define PISTATEMACHINE_H
|
||||
|
||||
#include "piobject.h"
|
||||
|
||||
/*! \brief Base class for custom state machine
|
||||
*
|
||||
* \section PIStateMachine_synopsis Synopsis
|
||||
* This class provide functionality of state machine.
|
||||
* You should inherit from this class, implement \a execution()
|
||||
* and \a transition() functions, set rules and periodically
|
||||
* call \a tick() function to proper work of machine.
|
||||
*
|
||||
* \section PIStateMachine_prepare Prepare for work
|
||||
* %State machine operates with "state", "rule" and "condition".
|
||||
* * "State" is some class (by default \c int), associated name and
|
||||
* optional "handler" - pointer to function executed on every \a tick();
|
||||
* * "Rule" define rule of transition from one machine state to other.
|
||||
* It is also has optional "handler";
|
||||
* * "Condition" is a part of rule and define possibility of transition.
|
||||
*
|
||||
* First of all you should define states of your machine by function
|
||||
* \a addState(). Then you should define transition rules for machine
|
||||
* by function \a addRule(). Finally you can set initial state by function
|
||||
* \a setInitialState() and provide periodically execution of function
|
||||
* \a tick().
|
||||
*
|
||||
* \section PIStateMachine_principle Principle of work
|
||||
* At any time the state machine is in some state. You can ask machine
|
||||
* to enter in new state by function \a switchToState(). If all conditions
|
||||
* done machine switch it state immediately, else machine remember request
|
||||
* and will be try switch to the new state every tick. Successfull state
|
||||
* switching execute function \a transition(), every tick execute
|
||||
* function \a execution() with current state. On successfull transition
|
||||
* if rule "handler" is not null it execute. Every \a tick() if current
|
||||
* state "handler" is not null it execute.
|
||||
*
|
||||
* \section PIStateMachine_conditions Conditions
|
||||
* Each rule has transition condition. Condition is array of pairs
|
||||
* (string, number). It means that every condition by name "string"
|
||||
* should be performed as least "number" times. Empty condition always
|
||||
* permits transition.
|
||||
*
|
||||
* %State machine have current performed conditions. You can read this
|
||||
* conditions by function \a currentConditions() and perform new
|
||||
* conditions by functions \a performCondition() and \a performConditions().
|
||||
* Currend conditions can de erased by function \a resetConditions().
|
||||
*
|
||||
* \section PIStateMachine_example Example
|
||||
* This is simple example demonstrates all features:
|
||||
* \snippet pistatemachine.cpp main
|
||||
*/
|
||||
template <typename Type = int>
|
||||
class PIP_EXPORT PIStateMachine: public PIObject
|
||||
{
|
||||
PIOBJECT(PIStateMachine)
|
||||
public:
|
||||
//! Constructs an empty state machine
|
||||
PIStateMachine(void * _parent = 0) {if (_parent == 0) parent_ = this; else parent_ = _parent; resetConditions();}
|
||||
~PIStateMachine() {;}
|
||||
|
||||
//! %Condition is a pair (string, number)
|
||||
typedef PIPair<PIString, int> Condition;
|
||||
|
||||
//! %Rule of transition between states of machine
|
||||
struct Rule {
|
||||
//! Constuctor
|
||||
Rule() {handler = 0;}
|
||||
//! Constuctor
|
||||
Rule(Type f, Type t, const PIStringList & c = PIStringList(), Handler h = 0, bool at = false, bool rac = false) {
|
||||
from = f;
|
||||
to = t;
|
||||
for (int i = 0; i < c.size_s(); ++i)
|
||||
conditions << Condition(c[i], 1);
|
||||
autoTransition = at;
|
||||
resetAllConditions = rac;
|
||||
handler = h;
|
||||
}
|
||||
//! Source state
|
||||
Type from;
|
||||
//! Destination state
|
||||
Type to;
|
||||
//! %Conditions of transition
|
||||
PIVector<Condition> conditions;
|
||||
//! Automatic transition
|
||||
bool autoTransition;
|
||||
//! Reset or not all performed conditions of machine on transition
|
||||
bool resetAllConditions;
|
||||
//! Pointer to function executed on transition
|
||||
Handler handler;
|
||||
//! Add condition of transition
|
||||
void addCondition(const PIString & name, int times = 1) {if (times > 0) conditions << Condition(name, times);}
|
||||
bool operator ==(const Rule & other) const {return (from == other.from) && (to == other.to);}
|
||||
bool operator !=(const Rule & other) const {return (from != other.from) || (to != other.to);}
|
||||
};
|
||||
|
||||
//! %State of machine
|
||||
struct State {
|
||||
//! Constuctor
|
||||
State() {handler = 0;}
|
||||
//! Constuctor
|
||||
State(Type v, const PIString & n = "", Handler h = 0) {value = v; name = n; handler = h;}
|
||||
//! %State value
|
||||
Type value;
|
||||
//! %State name
|
||||
PIString name;
|
||||
//! Pointer to function executed on tick
|
||||
Handler handler;
|
||||
bool operator ==(const State & other) const {return value == other.value;}
|
||||
bool operator !=(const State & other) const {return value != other.value;}
|
||||
};
|
||||
|
||||
void * parent() const {return parent_;}
|
||||
void setParent(void * parent) {parent_ = parent;}
|
||||
|
||||
//! Add state of machine
|
||||
void addState(Type value, const PIString & name = "", Handler handler = 0) {if (states_.contains(State(value, name))) return; states_ << State(value, name, handler);}
|
||||
|
||||
//! States count
|
||||
int statesCount() const {return states_.size_s();}
|
||||
|
||||
//! Remove all states
|
||||
void clearStates() {states_.clear();}
|
||||
|
||||
|
||||
//! Add rule of transition
|
||||
void addRule(Type from, Type to, const PIString & condition, Handler handler = 0, bool autoTransition = false, bool resetAllConditions = false) {if (rules_.contains(Rule(from, to))) return; rules_ << Rule(from, to, PIStringList(condition), handler, autoTransition, resetAllConditions);}
|
||||
|
||||
//! Add rule of transition
|
||||
void addRule(Type from, Type to, Handler handler, bool autoTransition = false, bool resetAllConditions = false) {if (rules_.contains(Rule(from, to))) return; rules_ << Rule(from, to, PIStringList(), handler, autoTransition, resetAllConditions);}
|
||||
|
||||
//! Add rule of transition
|
||||
void addRule(Type from, Type to, const PIStringList & conditions = PIStringList(), Handler handler = 0, bool autoTransition = false, bool resetAllConditions = false) {if (rules_.contains(Rule(from, to))) return; rules_ << Rule(from, to, conditions, handler, autoTransition, resetAllConditions);}
|
||||
|
||||
//! Add rule of transition
|
||||
void addRule(const Rule & rule) {if (rules_.contains(rule)) return; rules_ << rule;}
|
||||
|
||||
//! Rules count
|
||||
int rulesCount() const {return rules_.size_s();}
|
||||
|
||||
//! Remove all rules
|
||||
void clearRules() {rules_.clear();}
|
||||
|
||||
|
||||
//! Setup initial state. \a reset() will set machine state to "value"
|
||||
void setInitialState(Type value) {
|
||||
for (int i = 0; i < states_.size_s(); ++i)
|
||||
if (states_[i].value == value) {
|
||||
init_ = state_ = states_[i];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief Try to switch machine state to state "to"
|
||||
* \details If there is rule of transition exists and this rule conditions
|
||||
* is performed then machine switched to new state immediately. Otherwise machine
|
||||
* will be try to enter to new state every \a tick().
|
||||
* \return \c true if state switched immediately, otherwise \c false */
|
||||
bool switchToState(Type to) {
|
||||
switch_to = to;
|
||||
for (int i = 0; i < rules_.size_s(); ++i) {
|
||||
Rule & r(rules_[i]);
|
||||
if ((r.from != state_.value) || (r.to != to)) continue;
|
||||
if (!checkConditions(r)) continue;
|
||||
State ts = findState(to);
|
||||
if (r.handler != 0 && parent_ != 0) r.handler(parent_);
|
||||
transition(state_, ts);
|
||||
state_ = ts;
|
||||
resetConditions(r);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//! Reset machine state to initial and clear all conditions
|
||||
void reset() {state_ = init_; resetConditions();}
|
||||
|
||||
//! Returns current state of machine
|
||||
const State & currentState() const {return state_;}
|
||||
|
||||
|
||||
//! Reset all performed conditions
|
||||
void resetConditions() {cond.clear(); cond_count = 0;}
|
||||
|
||||
//! Reset performed condition with name "name"
|
||||
void resetCondition(const PIString & name) {
|
||||
for (int i = 0; i < cond.size_s(); ++i)
|
||||
if (cond[i].first == name) {
|
||||
cond.remove(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
//! Perform condition with name "name" "times" times.
|
||||
void performCondition(const PIString & name, int times = 1) {
|
||||
if (times <= 0) return;
|
||||
for (int i = 0; i < cond.size_s(); ++i)
|
||||
if (cond[i].first == name) {
|
||||
cond[i].second += times;
|
||||
return;
|
||||
}
|
||||
cond << Condition(name, times);
|
||||
}
|
||||
|
||||
//! Perform every condition with name from "names" one time.
|
||||
void performConditions(const PIStringList & names) {
|
||||
bool ok;
|
||||
for (int n = 0; n < names.size_s(); ++n) {
|
||||
ok = false;
|
||||
for (int i = 0; i < cond.size_s(); ++i) {
|
||||
if (cond[i].first == names[n]) {
|
||||
cond[i].second++;
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ok) continue;
|
||||
cond << Condition(names[n], 1);
|
||||
}
|
||||
}
|
||||
|
||||
//! Returns all current performed conditions
|
||||
const PIVector<Condition> & currentConditions() const {return cond;}
|
||||
|
||||
Type * currentState_ptr() {return &state_.value;}
|
||||
int * conditionsCount_ptr() {cond_count = cond.size_s(); return &cond_count;}
|
||||
|
||||
//! \handlers
|
||||
//! \{
|
||||
|
||||
//! \fn void tick()
|
||||
//! \brief Main function of machine. Execute \a execution() and check if need to switch state
|
||||
|
||||
//! \fn void tick(void * data, int delim)
|
||||
//! \brief Main function of machine. Execute \a execution() and check if need to switch state
|
||||
|
||||
//! \}
|
||||
|
||||
EVENT_HANDLER(void, tick) {tick(0, 0);}
|
||||
EVENT_HANDLER2(void, tick, void * , data, int, delim) {
|
||||
execution(state_);
|
||||
if (state_.handler != 0 && parent_ != 0) state_.handler(parent_);
|
||||
if (switch_to != state_.value) switchToState(switch_to);
|
||||
else {
|
||||
piForeachC (Rule & r, rules_) {
|
||||
if (!r.autoTransition || r.from != state_.value) continue;
|
||||
if (checkConditions(r)) {
|
||||
switchToState(r.to);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
//! Reimplement this function to process current state of machine
|
||||
virtual void execution(const State & state) {;}
|
||||
|
||||
//! Reimplement this function to process switching current state of machine
|
||||
virtual void transition(const State & from, const State & to) {;}
|
||||
|
||||
private:
|
||||
State findState(Type value) {
|
||||
for (int i = 0; i < states_.size_s(); ++i)
|
||||
if (states_[i].value == value)
|
||||
return states_[i];
|
||||
return State();
|
||||
}
|
||||
bool checkConditions(const Rule & rule) {
|
||||
//if (cond.size_s() < rule.conditions.size_s()) return false;
|
||||
int oc = 0;
|
||||
for (int i = 0; i < cond.size_s(); ++i) {
|
||||
PIString & rn(cond[i].first);
|
||||
for (int j = 0; j < rule.conditions.size_s(); ++j) {
|
||||
if (rn != rule.conditions[j].first) continue;
|
||||
if (cond[i].second < rule.conditions[j].second) return false;
|
||||
oc++;
|
||||
}
|
||||
}
|
||||
return (rule.conditions.size_s() == oc);
|
||||
}
|
||||
void resetConditions(const Rule & rule) {
|
||||
if (rule.resetAllConditions) {
|
||||
cond.clear();
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < cond.size_s(); ++i) {
|
||||
PIString & rn(cond[i].first);
|
||||
for (int j = 0; j < rule.conditions.size_s(); ++j) {
|
||||
if (rn != rule.conditions[j].first) continue;
|
||||
cond[i].second -= rule.conditions[j].second;
|
||||
if (cond[i].second <= 0) {
|
||||
cond.remove(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PIVector<State> states_;
|
||||
PIVector<Rule> rules_;
|
||||
State init_, state_;
|
||||
Type switch_to;
|
||||
void * parent_;
|
||||
int cond_count;
|
||||
PIVector<Condition> cond;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // PISTATEMACHINE_H
|
||||
775
src/core/pistring.cpp
Executable file
775
src/core/pistring.cpp
Executable file
@@ -0,0 +1,775 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
String
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pistring.h"
|
||||
|
||||
|
||||
/*! \class PIString
|
||||
* \brief String class
|
||||
* \details PIP use this class for use string information.
|
||||
*
|
||||
* \section PIString_sec0 Synopsis
|
||||
* This class based on \a PIVector to store information.
|
||||
* String is a sequence of \a PIChar and can contain multibyte
|
||||
* symbols. Therefore real memory size of string is symbols count * 4.
|
||||
* String can be constucted from many types of data and can be converted
|
||||
* to many types. There are man operators and handly functions to use
|
||||
* string as you wish.
|
||||
*
|
||||
* \section PIString_sec1 To/from data convertions
|
||||
* Most common constructor is \a PIString(const char * str), where "str"
|
||||
* is null-terminated string, e.g. \c "string". This is 7 chars with last char = 0.
|
||||
* Also you can constructs \a PIString from single \a PIChar, \a PIByteArray,
|
||||
* other \a PIString or sequency of the same characters with custom length.\n \n
|
||||
* This class has implicit conversions to <tt>const char * </tt> and
|
||||
* \c std::string. Also there are functions to make same convertions:
|
||||
* * \a data() - to <tt>const char * </tt>,
|
||||
* * \a stdString() - to \c std::string,
|
||||
* * \a toByteArray() - to \a PIByteArray.
|
||||
*
|
||||
* \section PIString_sec2 Numeric operations
|
||||
* You can get symbolic representation of any numeric value with function
|
||||
* \a setNumber(any integer value, int base = 10, bool * ok = 0). Default
|
||||
* arguments are set for decimal base system, but you can choose any system
|
||||
* from 2 to 40. There are the same static functions \a fromNumber(), that
|
||||
* returns \a PIString. \n
|
||||
* Also there is function \a setReadableSize() which is set human-readable
|
||||
* size in bytes, Kb, Mb, Gb or Pb. Static analog is \a readableSize().
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
const char PIString::toBaseN[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
|
||||
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
|
||||
'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^'};
|
||||
const int PIString::fromBaseN[] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
|
||||
-1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, -1,
|
||||
-1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
|
||||
|
||||
|
||||
void PIString::appendFromChars(const char * c, int s) {
|
||||
int sz;
|
||||
wchar_t wc;
|
||||
for (int i = 0; i < s; ++i) {
|
||||
if (/*isascii(c[i])*/c[i] >= 0) {
|
||||
push_back(PIChar(c[i]));
|
||||
continue;
|
||||
}
|
||||
sz = mbtowc(&wc, &(c[i]), 4);
|
||||
//cout << sz << endl;
|
||||
switch (sz) {
|
||||
case 4:
|
||||
push_back(PIChar(*(int*)&(c[i])));
|
||||
i += 3;
|
||||
continue;
|
||||
case 3:
|
||||
push_back(PIChar(*(int*)&(c[i])));
|
||||
back().ch &= 0xFFFFFF;
|
||||
i += 2;
|
||||
continue;
|
||||
case 2:
|
||||
push_back(PIChar(*(short * )&(c[i])));
|
||||
++i;
|
||||
continue;
|
||||
default:
|
||||
push_back(PIChar(c[i]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PIString & PIString::operator +=(const char * str) {
|
||||
int l = 0;
|
||||
while (str[l] != '\0') ++l;
|
||||
appendFromChars(str, l);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
PIString & PIString::operator +=(const wchar_t * str) {
|
||||
//cout << "wc" << endl;
|
||||
int l = 0, sz;
|
||||
char * c = new char[MB_CUR_MAX];
|
||||
while (str[l] != 0) ++l;
|
||||
for (int i = 0; i < l; ++i) {
|
||||
sz = wctomb(c, str[i]);
|
||||
switch (sz) {
|
||||
case 4:
|
||||
push_back(PIChar(*(int*)c));
|
||||
continue;
|
||||
case 3:
|
||||
push_back(PIChar(*(int*)c));
|
||||
back().ch &= 0xFFFFFF;
|
||||
continue;
|
||||
case 2:
|
||||
push_back(PIChar(*(short * )c));
|
||||
continue;
|
||||
default:
|
||||
push_back(PIChar(c[0]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
delete[] c;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAS_LOCALE
|
||||
PIString & PIString::operator +=(const wstring & str) {
|
||||
uint l = str.size();
|
||||
for (uint i = 0; i < l; ++i) push_back(str[i]);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
PIString & PIString::operator +=(const PIString & str) {
|
||||
//uint l = str.size();
|
||||
*((PIDeque<PIChar>*)this) << *((PIDeque<PIChar>*)&str);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*
|
||||
#ifdef WINDOWS
|
||||
PIString & PIString::operator +=(const WCHAR * str) {
|
||||
int l = 0;
|
||||
while (str[l] != 0) ++l;
|
||||
for (int i = 0; i < l; ++i)
|
||||
push_back(str[i]);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
*/
|
||||
|
||||
bool PIString::operator ==(const PIString & str) const {
|
||||
uint l = str.size();
|
||||
if (size() != l) return false;
|
||||
for (uint i = 0; i < l; ++i)
|
||||
if (str[i] != at(i))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PIString::operator !=(const PIString & str) const {
|
||||
uint l = str.size();
|
||||
if (size() != l) return true;
|
||||
for (uint i = 0; i < l; ++i)
|
||||
if (str[i] != at(i))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool PIString::operator <(const PIString & str) const {
|
||||
uint l = str.size();
|
||||
if (size() < l) return true;
|
||||
if (size() > l) return false;
|
||||
for (uint i = 0; i < l; ++i) {
|
||||
if (str[i] == at(i)) continue;
|
||||
if (str[i] < at(i)) return true;
|
||||
else return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool PIString::operator >(const PIString & str) const {
|
||||
uint l = str.size();
|
||||
if (size() < l) return false;
|
||||
if (size() > l) return true;
|
||||
for (uint i = 0; i < l; ++i) {
|
||||
if (str[i] == at(i)) continue;
|
||||
if (str[i] < at(i)) return false;
|
||||
else return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
PIString PIString::mid(const int start, const int len) const {
|
||||
//PIString str;
|
||||
int s = start, l = len;
|
||||
if (l == 0) return PIString();
|
||||
if (s < 0) {
|
||||
l += s;
|
||||
s = 0;
|
||||
}
|
||||
if (l < 0) {
|
||||
//for (uint i = s; i < size(); ++i)
|
||||
// str += at(i);
|
||||
return PIString(&(at(s)), size() - s);
|
||||
} else {
|
||||
if (l > length() - s)
|
||||
l = length() - s;
|
||||
//for (int i = s; i < s + l; ++i)
|
||||
// str += at(i);
|
||||
return PIString(&(at(s)), l);
|
||||
}
|
||||
return PIString();
|
||||
}
|
||||
|
||||
|
||||
PIString & PIString::cutMid(const int start, const int len) {
|
||||
int s = start, l = len;
|
||||
if (l == 0) return *this;
|
||||
if (s < 0) {
|
||||
l += s;
|
||||
s = 0;
|
||||
}
|
||||
if (l < 0)
|
||||
remove(s, size() - s);
|
||||
else {
|
||||
if (l > length() - s)
|
||||
l = length() - s;
|
||||
remove(s, l);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
PIString & PIString::trim() {
|
||||
int st = 0, fn = 0;
|
||||
for (int i = 0; i < length(); ++i)
|
||||
if (at(i) != ' ' && at(i) != '\t' && at(i) != '\n' && at(i) != '\r' && at(i) != char(12))
|
||||
{st = i; break;}
|
||||
for (int i = length() - 1; i >= 0; --i)
|
||||
if (at(i) != ' ' && at(i) != '\t' && at(i) != '\n' && at(i) != '\r' && at(i) != char(12))
|
||||
{fn = i; break;}
|
||||
//*this = mid(st, fn - st + 1);
|
||||
if (fn < size_s() - 1) cutRight(size_s() - fn - 1);
|
||||
if (st > 0) cutLeft(st);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
PIString PIString::trimmed() const {
|
||||
int st = 0, fn = 0;
|
||||
for (int i = 0; i < length(); ++i)
|
||||
if (at(i) != ' ' && at(i) != '\t' && at(i) != '\n' && at(i) != '\r' && at(i) != char(12))
|
||||
{st = i; break;}
|
||||
for (int i = length() - 1; i >= 0; --i)
|
||||
if (at(i) != ' ' && at(i) != '\t' && at(i) != '\n' && at(i) != '\r' && at(i) != char(12))
|
||||
{fn = i; break;}
|
||||
return mid(st, fn - st + 1);
|
||||
}
|
||||
|
||||
|
||||
PIString & PIString::replace(int from, int count, const PIString & with) {
|
||||
if (count < length() - from) remove(from, count);
|
||||
else remove(from, length() - from);
|
||||
uint c = with.length();
|
||||
for (uint i = 0; i < c; ++i) insert(from + i, with[i]);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
PIString & PIString::replace(const PIString & what, const PIString & with, bool * ok) {
|
||||
//piCout << "replace" << what << with;
|
||||
if (what.isEmpty()) {
|
||||
if (ok != 0) *ok = false;
|
||||
return *this;
|
||||
}
|
||||
int s = find(what);
|
||||
if (s >= 0) replace(s, what.length(), with);
|
||||
if (ok != 0) *ok = (s >= 0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
PIString & PIString::replaceAll(const PIString & what, const PIString & with) {
|
||||
if (what.isEmpty() || what == with) return *this;
|
||||
bool ok = true;
|
||||
while (ok) replace(what, with, &ok);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
PIString & PIString::insert(int index, const PIString & str) {
|
||||
//uint c = str.length();
|
||||
//for (uint i = 0; i < c; ++i) insert(index + i, str[i]);
|
||||
PIDeque<PIChar>::insert(index, *((const PIDeque<PIChar>*)&str));
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
PIStringList PIString::split(const PIString & delim) const {
|
||||
PIStringList sl;
|
||||
if (isEmpty() || delim.isEmpty()) return sl;
|
||||
PIString ts(*this);
|
||||
int ci = ts.find(delim);
|
||||
while (ci >= 0) {
|
||||
sl << ts.left(ci);
|
||||
ts.cutLeft(ci + delim.length());
|
||||
ci = ts.find(delim);
|
||||
}
|
||||
if (ts.length() > 0) sl << ts;
|
||||
return sl;
|
||||
}
|
||||
|
||||
|
||||
int PIString::find(const char str, const int start) const {
|
||||
for (int i = start; i < length(); ++i)
|
||||
if (at(i) == str)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int PIString::find(const PIString str, const int start) const {
|
||||
int l = str.length();
|
||||
for (int i = start; i < length() - l + 1; ++i)
|
||||
if (mid(i, l) == str)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int PIString::findLast(const char str, const int start) const {
|
||||
for (int i = length() - 1; i >= start; --i)
|
||||
if (at(i) == str)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int PIString::findLast(const PIString str, const int start) const {
|
||||
int l = str.length();
|
||||
for (int i = length() - l; i >= start; --i)
|
||||
if (mid(i, l) == str)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int PIString::findWord(const PIString & word, const int start) const {
|
||||
int f = start - 1, tl = length(), wl = word.length();
|
||||
while ((f = find(word, f + 1)) >= 0) {
|
||||
bool ok = true;
|
||||
PIChar c;
|
||||
if (f > 0) {c = (*this)[f - 1]; if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r')) {ok = false; continue;}}
|
||||
if (f + wl < tl) {c = (*this)[f + wl]; if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r')) {ok = false; continue;}}
|
||||
if (ok) return f;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int PIString::findCWord(const PIString & word, const int start) const {
|
||||
int f = start - 1, tl = length(), wl = word.length();
|
||||
while ((f = find(word, f + 1)) >= 0) {
|
||||
bool ok = true;
|
||||
PIChar c;
|
||||
if (f > 0) {c = (*this)[f - 1]; if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r' || (c != '_' && !c.isAlpha() && !c.isDigit()))) {ok = false; continue;}}
|
||||
if (f + wl < tl) {c = (*this)[f + wl]; if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r' || (c != '_' && !c.isAlpha() && !c.isDigit()))) {ok = false; continue;}}
|
||||
if (ok) return f;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
bool PIString::startsWith(const PIString & str) const {
|
||||
if (size() < str.size()) return false;
|
||||
return str == left(str.size());
|
||||
}
|
||||
|
||||
|
||||
bool PIString::endsWith(const PIString & str) const {
|
||||
if (size() < str.size()) return false;
|
||||
return str == right(str.size());
|
||||
}
|
||||
|
||||
|
||||
PIString PIString::takeSymbol() {
|
||||
PIString ret;
|
||||
int sz = size_s(), ss = -1;
|
||||
for (int i = 0; i < sz; ++i) {
|
||||
PIChar c = at(i);
|
||||
if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
|
||||
continue;
|
||||
ss = i;
|
||||
break;
|
||||
}
|
||||
if (ss < 0) return ret;
|
||||
ret = mid(ss, 1);
|
||||
cutLeft(ss + 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIString PIString::takeWord() {
|
||||
int sz = size_s(), ws = -1, we = -1;
|
||||
for (int i = 0; i < sz; ++i) {
|
||||
PIChar c = at(i);
|
||||
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
|
||||
if (we < 0 && ws >= 0) {
|
||||
we = i;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (ws < 0) ws = i;
|
||||
if (we >= 0) break;
|
||||
}
|
||||
}
|
||||
PIString ret = mid(ws, we - ws);
|
||||
cutLeft(we < 0 ? sz : we);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIString PIString::takeCWord() {
|
||||
PIString ret;
|
||||
int sz = size_s(), ws = -1, we = -1;
|
||||
for (int i = 0; i < sz; ++i) {
|
||||
PIChar c = at(i);
|
||||
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
|
||||
if (we < 0 && ws >= 0) {
|
||||
we = i;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (ws < 0) {
|
||||
if (c.isAlpha() || c == '_')
|
||||
ws = i;
|
||||
else
|
||||
return ret;
|
||||
} else {
|
||||
if (!c.isAlpha() && !c.isDigit() && c != '_') {
|
||||
we = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (we >= 0) break;
|
||||
}
|
||||
}
|
||||
ret = mid(ws, we - ws);
|
||||
cutLeft(we < 0 ? sz : we);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIString PIString::takeLine() {
|
||||
int sz = size_s(), le = -1;
|
||||
for (int i = 0; i < sz; ++i) {
|
||||
PIChar c = at(i);
|
||||
if (c == '\n') {
|
||||
le = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
PIString ret = left(le);
|
||||
if (!ret.isEmpty())
|
||||
if (ret.back() == '\r')
|
||||
ret.cutRight(1);
|
||||
cutLeft(le < 0 ? sz : le + 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIString PIString::takeNumber() {
|
||||
PIString ret;
|
||||
int sz = size_s(), ls = -1, le = -1, phase = 0;
|
||||
for (int i = 0; i < sz; ++i) {
|
||||
if (phase > 7) break;
|
||||
PIChar c = at(i);
|
||||
//piCout << "char " << c << "phase" << phase;
|
||||
switch (phase) {
|
||||
case 0: // trim
|
||||
if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
|
||||
continue;
|
||||
phase = 7;
|
||||
case 7: // sign
|
||||
if (c == '-' || c == '+') {ls = i; phase = 1; break;}
|
||||
case 1: // search start
|
||||
if (c >= '0' && c <= '9') {le = i; if (ls < 0) ls = i; phase = 2; break;}
|
||||
if (c == '.') {le = i; if (ls < 0) ls = i; phase = 3; break;}
|
||||
phase = 9;
|
||||
break;
|
||||
case 2: // integer
|
||||
if (c == '.') {le = i; phase = 3; break;}
|
||||
if (c == 'e' || c == 'E') {le = i; phase = 4; break;}
|
||||
if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || c == 'x') {le = i; break;}
|
||||
phase = 6;
|
||||
break;
|
||||
case 3: // point
|
||||
if (c == 'e' || c == 'E') {le = i; phase = 4; break;}
|
||||
if (c >= '0' && c <= '9') {le = i; break;}
|
||||
phase = 6;
|
||||
break;
|
||||
case 4: // exp
|
||||
if ((c >= '0' && c <= '9') || c == '-' || c == '+') {le = i; phase = 5; break;}
|
||||
phase = 6;
|
||||
break;
|
||||
case 5: // power
|
||||
if (c >= '0' && c <= '9') {le = i; break;}
|
||||
phase = 6;
|
||||
break;
|
||||
case 6: // suffix
|
||||
if (c == 'f' || c == 's' || c == 'u' || c == 'l' || c == 'L') {le = i; break;}
|
||||
phase = 9;
|
||||
break;
|
||||
}
|
||||
if (phase == 6) {
|
||||
if (c == 'f' || c == 's' || c == 'u' || c == 'l' || c == 'L') le = i;
|
||||
else phase = 9;
|
||||
}
|
||||
}
|
||||
//piCout << ls << le;
|
||||
if (le < ls) return ret;
|
||||
ret = mid(ls, le - ls + 1);
|
||||
cutLeft(le + 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIString PIString::takeRange(const PIChar & start, const PIChar & end, const PIChar & shield) {
|
||||
PIString ret;
|
||||
bool trim_ = (start != ' ' && start != '\t' && start != '\n' && start != '\r'), eq = (start == end);
|
||||
int sz = size_s(), ls = -1, le = -1, cnt = 0;
|
||||
for (int i = 0; i < sz; ++i) {
|
||||
PIChar c = at(i);
|
||||
if (c == shield) {++i; continue;}
|
||||
if (trim_) {
|
||||
if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
|
||||
continue;
|
||||
trim_ = false;
|
||||
}
|
||||
if (eq) {
|
||||
if (c == start) {
|
||||
if (cnt == 0) ls = i;
|
||||
else {le = i; cnt = 0; break;}
|
||||
cnt++;
|
||||
}
|
||||
} else {
|
||||
if (c == start) {
|
||||
if (cnt == 0) ls = i;
|
||||
cnt++;
|
||||
}
|
||||
if (c == end) {
|
||||
cnt--;
|
||||
if (cnt == 0) le = i;
|
||||
}
|
||||
}
|
||||
if (cnt <= 0) break;
|
||||
}
|
||||
//piCout << ls << le << cnt;
|
||||
if (le < ls || ls < 0 || le < 0 || cnt != 0) return ret;
|
||||
ret = mid(ls + 1, le - ls - 1);
|
||||
cutLeft(le + 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIString PIString::toUpperCase() const {
|
||||
PIString str(*this);
|
||||
int l = str.size();
|
||||
for (int i = 0; i < l; ++i) str[i] = str[i].toUpper();
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
PIString PIString::toLowerCase() const {
|
||||
PIString str(*this);
|
||||
int l = str.size();
|
||||
for (int i = 0; i < l; ++i) str[i] = str[i].toLower();
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
int PIString::lengthAscii() const {
|
||||
int j = 0;
|
||||
for (int i = 0; i < size_s(); ++i, ++j)
|
||||
if (!at(i).isAscii()) ++j;
|
||||
return j;
|
||||
}
|
||||
|
||||
|
||||
const char * PIString::data() const {
|
||||
data_.clear();
|
||||
uint wc;
|
||||
uchar tc;
|
||||
//printf("PIString::data %d\n", size_s());
|
||||
for (int i = 0, j = 0; i < size_s(); ++i) {
|
||||
wc = uint(at(i).toInt());
|
||||
//printf("__%d_%d\n", i, wc);
|
||||
while (tc = wc & 0xFF, tc) {
|
||||
data_.push_back(uchar(tc)); ++j;
|
||||
wc >>= 8;
|
||||
//printf("____%d\n", wc);
|
||||
}
|
||||
/*if (at(i).isAscii())
|
||||
data_.push_back(uchar(at(i).toAscii()));
|
||||
else {
|
||||
data_.push_back((at(i).toCharPtr()[0])); ++j;
|
||||
data_.push_back((at(i).toCharPtr()[1]));
|
||||
}*/
|
||||
}
|
||||
data_.push_back(uchar('\0'));
|
||||
return (const char * )data_.data();
|
||||
}
|
||||
|
||||
|
||||
string PIString::convertToStd() const {
|
||||
string s;
|
||||
uint wc;
|
||||
uchar tc;
|
||||
if (size() > 0) {
|
||||
for (int i = 0; i < length(); ++i) {
|
||||
wc = uint(at(i).toInt());
|
||||
while (tc = wc & 0xFF, tc) {
|
||||
s.push_back(char(tc));
|
||||
wc >>= 8;
|
||||
}
|
||||
/*if (at(i).isAscii())
|
||||
s.push_back(at(i).toAscii());
|
||||
else {
|
||||
s.push_back(at(i).toCharPtr()[0]);
|
||||
s.push_back(at(i).toCharPtr()[1]);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
char PIString::toChar() const {
|
||||
PIString s(toNativeDecimalPoints());
|
||||
char v;
|
||||
sscanf(s.data(), "%c", &v);
|
||||
return v;
|
||||
}
|
||||
|
||||
/*
|
||||
short PIString::toShort() const {
|
||||
PIString s(trimmed().toLowerCase().toNativeDecimalPoints());
|
||||
short v;
|
||||
if (s.left(2) == "0x") {sscanf(s.data(), "%hx", &v); return v;}
|
||||
if (s.left(1) == "0") {sscanf(s.data(), "%ho", &v); return v;}
|
||||
sscanf(s.data(), "%hd", &v);
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
int PIString::toInt() const {
|
||||
PIString s(trimmed().toLowerCase().toNativeDecimalPoints());
|
||||
int v;
|
||||
if (s.left(2) == "0x") {sscanf(s.data(), "%x", &v); return v;}
|
||||
if (s.left(1) == "0") {sscanf(s.data(), "%o", &v); return v;}
|
||||
sscanf(s.data(), "%d", &v);
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
long PIString::toLong() const {
|
||||
PIString s(trimmed().toLowerCase().toNativeDecimalPoints());
|
||||
long v;
|
||||
if (s.left(2) == "0x") {sscanf(s.data(), "%lx", &v); return v;}
|
||||
if (s.left(1) == "0") {sscanf(s.data(), "%lo", &v); return v;}
|
||||
sscanf(s.data(), "%ld", &v);
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
llong PIString::toLLong() const {
|
||||
PIString s(trimmed().toLowerCase().toNativeDecimalPoints());
|
||||
llong v;
|
||||
if (s.left(2) == "0x") {sscanf(s.data(), "%llx", &v); return v;}
|
||||
if (s.left(1) == "0") {sscanf(s.data(), "%llo", &v); return v;}
|
||||
sscanf(s.data(), "%lld", &v);
|
||||
return v;
|
||||
}
|
||||
*/
|
||||
|
||||
PIString & PIString::setReadableSize(llong bytes) {
|
||||
clear();
|
||||
if (bytes < 1024) {*this += (PIString::fromNumber(bytes) + " B"); return *this;}
|
||||
double fres = bytes / 1024.;
|
||||
llong res = bytes / 1024;
|
||||
fres -= res;
|
||||
if (res < 1024) {*this += (PIString::fromNumber(res) + "." + PIString::fromNumber(llong(fres * 10)).left(1) + " kB"); return *this;}
|
||||
fres = res / 1024.;
|
||||
res /= 1024;
|
||||
fres -= res;
|
||||
if (res < 1024) {*this += (PIString::fromNumber(res) + "." + PIString::fromNumber(llong(fres * 10)).left(1) + " MB"); return *this;}
|
||||
fres = res / 1024.;
|
||||
res /= 1024;
|
||||
fres -= res;
|
||||
if (res < 1024) {*this += (PIString::fromNumber(res) + "." + PIString::fromNumber(llong(fres * 10)).left(1) + " GB"); return *this;}
|
||||
fres = res / 1024.;
|
||||
res /= 1024;
|
||||
fres -= res;
|
||||
if (res < 1024) {*this += (PIString::fromNumber(res) + "." + PIString::fromNumber(llong(fres * 10)).left(1) + " TB"); return *this;}
|
||||
fres = res / 1024.;
|
||||
res /= 1024;
|
||||
fres -= res;
|
||||
*this += (PIString::fromNumber(res) + "." + PIString::fromNumber(llong(fres * 10)).left(1) + " PB");
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
inline char chrUpr(char c) {
|
||||
if (c >= 'a' && c <= 'z') return c + 'A' - 'a';
|
||||
//if (c >= 'а' && c <= 'я') return c + 'А' - 'а';
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
inline char chrLwr(char c) {
|
||||
if (c >= 'A' && c <= 'Z') return c + 'a' - 'A';
|
||||
//if (c >= 'А' && c <= 'Я') return c + 'а' - 'А';
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
|
||||
PIStringList& PIStringList::removeDuplicates() {
|
||||
PIStringList l;
|
||||
PIString s;
|
||||
bool ae;
|
||||
for (int i = 0; i < size_s(); ++i) {
|
||||
ae = false;
|
||||
s = at(i);
|
||||
for (int j = 0; j < l.size_s(); ++j) {
|
||||
if (s != l[j]) continue;
|
||||
ae = true; break;
|
||||
}
|
||||
if (!ae) {
|
||||
l << s;
|
||||
continue;
|
||||
}
|
||||
remove(i);
|
||||
--i;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
916
src/core/pistring.h
Executable file
916
src/core/pistring.h
Executable file
@@ -0,0 +1,916 @@
|
||||
/*! \file pistring.h
|
||||
* \brief String
|
||||
*
|
||||
* This file declare string and string list classes
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
String
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PISTRING_H
|
||||
#define PISTRING_H
|
||||
|
||||
#include "pibytearray.h"
|
||||
#include "pichar.h"
|
||||
#include "math.h"
|
||||
|
||||
class PIStringList;
|
||||
|
||||
class PIP_EXPORT PIString: public PIDeque<PIChar>
|
||||
{
|
||||
public:
|
||||
//! Contructs an empty string
|
||||
PIString(): PIDeque<PIChar>() {/*reserve(256); */piMonitor.strings++; piMonitor.containers--;}
|
||||
|
||||
//inline PIString & operator +=(const char c) {push_back(c); return *this;}
|
||||
PIString & operator +=(const PIChar & c) {push_back(c); return *this;}
|
||||
PIString & operator +=(const char * str);
|
||||
PIString & operator +=(const wchar_t * str);
|
||||
PIString & operator +=(const string & str) {appendFromChars(str.c_str(), str.length()); return *this;}
|
||||
PIString & operator +=(const PIByteArray & ba) {appendFromChars((const char * )ba.data(), ba.size_s()); return *this;}
|
||||
PIString & operator +=(const PIString & str);
|
||||
#ifdef HAS_LOCALE
|
||||
PIString & operator +=(const wstring & str);
|
||||
#endif
|
||||
|
||||
//PIString(const char c) {*this += c;}
|
||||
PIString(const PIString & o): PIDeque<PIChar>() {/*reserve(256); */piMonitor.strings++; piMonitor.containers--; *this += o;}
|
||||
|
||||
|
||||
//! Contructs string with single symbol "c"
|
||||
PIString(const PIChar & c): PIDeque<PIChar>() {/*reserve(256); */piMonitor.strings++; piMonitor.containers--; *this += c;}
|
||||
PIString(const char c): PIDeque<PIChar>() {/*reserve(256); */piMonitor.strings++; piMonitor.containers--; *this += PIChar(c);}
|
||||
|
||||
/*! \brief Contructs string from c-string "str"
|
||||
* \details "str" should be null-terminated\n
|
||||
* Example: \snippet pistring.cpp PIString(char * ) */
|
||||
PIString(const char * str): PIDeque<PIChar>() {/*reserve(256); */piMonitor.strings++; piMonitor.containers--; *this += str;}
|
||||
|
||||
/*! \brief Contructs string from \c wchar_t c-string "str"
|
||||
* \details "str" should be null-terminated\n
|
||||
* Example: \snippet pistring.cpp PIString(wchar_t * ) */
|
||||
PIString(const wchar_t * str): PIDeque<PIChar>() {/*reserve(256); */piMonitor.strings++; piMonitor.containers--; *this += str;}
|
||||
|
||||
//! Contructs string from std::string "str"
|
||||
PIString(const string & str): PIDeque<PIChar>() {/*reserve(256); */piMonitor.strings++; piMonitor.containers--; *this += str;}
|
||||
|
||||
#ifdef HAS_LOCALE
|
||||
PIString(const wstring & str): PIDeque<PIChar>() {/*reserve(256); */piMonitor.strings++; piMonitor.containers--; *this += str;}
|
||||
#endif
|
||||
|
||||
//! Contructs string from byte array "ba"
|
||||
PIString(const PIByteArray & ba): PIDeque<PIChar>() {/*reserve(256); */piMonitor.strings++; piMonitor.containers--; *this += ba;}
|
||||
|
||||
//! \brief Contructs string from "len" characters of buffer "str"
|
||||
PIString(const PIChar * str, const int len): PIDeque<PIChar>(str, size_t(len)) {/*reserve(256); */piMonitor.strings++; piMonitor.containers--;}
|
||||
|
||||
/*! \brief Contructs string from "len" characters of buffer "str"
|
||||
* \details Example: \snippet pistring.cpp PIString(char * , int) */
|
||||
PIString(const char * str, const int len): PIDeque<PIChar>() {/*reserve(256); */piMonitor.strings++; piMonitor.containers--; *this += string(str, len);}
|
||||
|
||||
/*! \brief Contructs string as sequence of characters "c" of buffer with length "len"
|
||||
* \details Example: \snippet pistring.cpp PIString(int, char) */
|
||||
PIString(const int len, const char c): PIDeque<PIChar>() {/*reserve(256); */piMonitor.strings++; piMonitor.containers--; for (int i = 0; i < len; ++i) push_back(c);}
|
||||
|
||||
/*! \brief Contructs string as sequence of symbols "c" of buffer with length "len"
|
||||
* \details Example: \snippet pistring.cpp PIString(int, PIChar) */
|
||||
PIString(const int len, const PIChar & c): PIDeque<PIChar>() {/*reserve(256); */piMonitor.strings++; piMonitor.containers--; for (int i = 0; i < len; ++i) push_back(c);}
|
||||
/*
|
||||
#ifdef WINDOWS
|
||||
PIString(const WCHAR * str): PIDeque<PIChar>() {piMonitor.strings++; piMonitor.containers--; *this += str;}
|
||||
PIString & operator +=(const WCHAR * str);
|
||||
PIString & operator <<(const WCHAR * str) {*this += str; return *this;}
|
||||
#endif
|
||||
*/
|
||||
|
||||
PIString(const short & value): PIDeque<PIChar>() {/*reserve(256); */piMonitor.strings++; piMonitor.containers--; *this = fromNumber(value);}
|
||||
PIString(const ushort & value): PIDeque<PIChar>() {/*reserve(256); */piMonitor.strings++; piMonitor.containers--; *this = fromNumber(value);}
|
||||
PIString(const int & value): PIDeque<PIChar>() {/*reserve(256); */piMonitor.strings++; piMonitor.containers--; *this = fromNumber(value);}
|
||||
PIString(const uint & value): PIDeque<PIChar>() {/*reserve(256); */piMonitor.strings++; piMonitor.containers--; *this = fromNumber(value);}
|
||||
PIString(const long & value): PIDeque<PIChar>() {/*reserve(256); */piMonitor.strings++; piMonitor.containers--; *this = fromNumber(value);}
|
||||
PIString(const ulong & value): PIDeque<PIChar>() {/*reserve(256); */piMonitor.strings++; piMonitor.containers--; *this = fromNumber(value);}
|
||||
PIString(const llong & value): PIDeque<PIChar>() {/*reserve(256); */piMonitor.strings++; piMonitor.containers--; *this = fromNumber(value);}
|
||||
PIString(const ullong & value): PIDeque<PIChar>() {/*reserve(256); */piMonitor.strings++; piMonitor.containers--; *this = fromNumber(value);}
|
||||
PIString(const float & value): PIDeque<PIChar>() {/*reserve(256); */piMonitor.strings++; piMonitor.containers--; *this = fromNumber(value);}
|
||||
PIString(const double & value): PIDeque<PIChar>() {/*reserve(256); */piMonitor.strings++; piMonitor.containers--; *this = fromNumber(value);}
|
||||
|
||||
|
||||
//~PIString() {piMonitor.strings--; piMonitor.containers++;}
|
||||
|
||||
|
||||
PIString & operator =(const PIString & o) {clear(); *this += o; return *this;}
|
||||
|
||||
/*! \brief Return c-string representation of string
|
||||
* \details Converts content of string to c-string and return
|
||||
* pointer to first char. This buffer is valid until new convertion
|
||||
* or execution \a data() or \a toByteArray().\n
|
||||
* Example: \snippet pistring.cpp PIString::char* */
|
||||
operator const char*() {return data();}
|
||||
|
||||
//! Return std::string representation of string
|
||||
operator const string() {if (size() == 0) return string(); string s; for (int i = 0; i < length(); ++i) s.push_back(at(i).toAscii()); return s;}
|
||||
|
||||
//! Return symbol at index "pos"
|
||||
PIChar operator [](const int pos) const {return at(pos);}
|
||||
|
||||
//! Return reference to symbol at index "pos"
|
||||
PIChar & operator [](const int pos) {return at(pos);}
|
||||
|
||||
//! Compare operator
|
||||
bool operator ==(const PIString & str) const;
|
||||
|
||||
//! Compare operator
|
||||
bool operator ==(const PIChar c) const {return *this == PIString(c);}
|
||||
//inline bool operator ==(const char c) const {return *this == PIString(c);}
|
||||
|
||||
//! Compare operator
|
||||
bool operator ==(const char * str) const {return *this == PIString(str);}
|
||||
|
||||
//! Compare operator
|
||||
bool operator ==(const string & str) const {return *this == PIString(str);}
|
||||
|
||||
|
||||
//! Compare operator
|
||||
bool operator !=(const PIString & str) const;
|
||||
|
||||
//! Compare operator
|
||||
bool operator !=(const PIChar c) const {return *this != PIString(c);}
|
||||
//inline bool operator !=(const char c) const {return *this != PIString(c);}
|
||||
|
||||
//! Compare operator
|
||||
bool operator !=(const char * str) const {return *this != PIString(str);}
|
||||
|
||||
//! Compare operator
|
||||
bool operator !=(const string & str) const {return *this != PIString(str);}
|
||||
|
||||
|
||||
//! Compare operator
|
||||
bool operator <(const PIString & str) const;
|
||||
|
||||
//! Compare operator
|
||||
bool operator <(const PIChar c) const {return *this < PIString(c);}
|
||||
//inline bool operator <(const char c) const {return *this < PIString(c);}
|
||||
|
||||
//! Compare operator
|
||||
bool operator <(const char * str) const {return *this < PIString(str);}
|
||||
|
||||
//! Compare operator
|
||||
bool operator <(const string & str) const {return *this < PIString(str);}
|
||||
|
||||
|
||||
//! Compare operator
|
||||
bool operator >(const PIString & str) const;
|
||||
|
||||
//! Compare operator
|
||||
bool operator >(const PIChar c) const {return *this > PIString(c);}
|
||||
//inline bool operator >(const char c) const {return *this > PIString(c);}
|
||||
|
||||
//! Compare operator
|
||||
bool operator >(const char * str) const {return *this > PIString(str);}
|
||||
|
||||
//! Compare operator
|
||||
bool operator >(const string & str) const {return *this > PIString(str);}
|
||||
|
||||
|
||||
//! Compare operator
|
||||
bool operator <=(const PIString & str) const {return !(*this > str);}
|
||||
|
||||
//! Compare operator
|
||||
bool operator <=(const PIChar c) const {return *this <= PIString(c);}
|
||||
//inline bool operator <=(const char c) const {return *this <= PIString(c);}
|
||||
|
||||
//! Compare operator
|
||||
bool operator <=(const char * str) const {return *this <= PIString(str);}
|
||||
|
||||
//! Compare operator
|
||||
bool operator <=(const string & str) const {return *this <= PIString(str);}
|
||||
|
||||
|
||||
//! Compare operator
|
||||
bool operator >=(const PIString & str) const {return !(*this < str);}
|
||||
|
||||
//! Compare operator
|
||||
bool operator >=(const PIChar c) const {return *this >= PIString(c);}
|
||||
//inline bool operator >=(const char c) const {return *this >= PIString(c);}
|
||||
|
||||
//! Compare operator
|
||||
bool operator >=(const char * str) const {return *this >= PIString(str);}
|
||||
|
||||
//! Compare operator
|
||||
bool operator >=(const string & str) const {return *this >= PIString(str);}
|
||||
|
||||
|
||||
operator bool() const {return toBool();}
|
||||
operator short() const {return toShort();}
|
||||
operator ushort() const {return toUShort();}
|
||||
operator int() const {return toInt();}
|
||||
operator uint() const {return toUInt();}
|
||||
operator long() const {return toLong();}
|
||||
operator ulong() const {return toULong();}
|
||||
operator llong() const {return toLLong();}
|
||||
operator ullong() const {return toULLong();}
|
||||
operator float() const {return toFloat();}
|
||||
operator double() const {return toDouble();}
|
||||
|
||||
|
||||
/*! \brief Append string "str" at the end of string
|
||||
* \details Example: \snippet pistring.cpp PIString::<<(PIString) */
|
||||
PIString & operator <<(const PIString & str) {*this += str; return *this;}
|
||||
//inline PIString & operator <<(const char c) {*this += c; return *this;}
|
||||
|
||||
/*! \brief Append symbol "c" at the end of string
|
||||
* \details Example: \snippet pistring.cpp PIString::<<(PIChar) */
|
||||
PIString & operator <<(const PIChar & c) {*this += c; return *this;}
|
||||
|
||||
/*! \brief Append c-string "str" at the end of string
|
||||
* \details Example: \snippet pistring.cpp PIString::<<(char * ) */
|
||||
PIString & operator <<(const char * str) {*this += str; return *this;}
|
||||
|
||||
/*! \brief Append \c wchar_t c-string "str" at the end of string
|
||||
* \details Example: \snippet pistring.cpp PIString::<<(wchar_t * ) */
|
||||
PIString & operator <<(const wchar_t * str) {*this += str; return *this;}
|
||||
|
||||
//! Append std::string "str" at the end of string
|
||||
PIString & operator <<(const string & str) {*this += str; return *this;}
|
||||
|
||||
/*! \brief Append string representation of "num" at the end of string
|
||||
* \details Example: \snippet pistring.cpp PIString::<<(int) */
|
||||
PIString & operator <<(const int & num) {*this += PIString::fromNumber(num); return *this;}
|
||||
PIString & operator <<(const uint & num) {*this += PIString::fromNumber(num); return *this;}
|
||||
|
||||
/*! \brief Append string representation of "num" at the end of string
|
||||
* \details Example: \snippet pistring.cpp PIString::<<(int) */
|
||||
PIString & operator <<(const short & num) {*this += PIString::fromNumber(num); return *this;}
|
||||
PIString & operator <<(const ushort & num) {*this += PIString::fromNumber(num); return *this;}
|
||||
|
||||
/*! \brief Append string representation of "num" at the end of string
|
||||
* \details Example: \snippet pistring.cpp PIString::<<(int) */
|
||||
PIString & operator <<(const long & num) {*this += PIString::fromNumber(num); return *this;}
|
||||
PIString & operator <<(const ulong & num) {*this += PIString::fromNumber(num); return *this;}
|
||||
|
||||
PIString & operator <<(const llong & num) {*this += PIString::fromNumber(num); return *this;}
|
||||
PIString & operator <<(const ullong & num) {*this += PIString::fromNumber(num); return *this;}
|
||||
|
||||
/*! \brief Append string representation of "num" at the end of string
|
||||
* \details Example: \snippet pistring.cpp PIString::<<(int) */
|
||||
PIString & operator <<(const float & num) {*this += PIString::fromNumber(num); return *this;}
|
||||
|
||||
/*! \brief Append string representation of "num" at the end of string
|
||||
* \details Example: \snippet pistring.cpp PIString::<<(int) */
|
||||
PIString & operator <<(const double & num) {*this += PIString::fromNumber(num); return *this;}
|
||||
|
||||
|
||||
//! \brief Insert string "str" at the begin of string
|
||||
PIString & prepend(const PIString & str) {insert(0, str); return *this;}
|
||||
|
||||
//! \brief Insert string "str" at the end of string
|
||||
PIString & append(const PIString & str) {*this += str; return *this;}
|
||||
|
||||
|
||||
/*! \brief Return part of string from symbol at index "start" and maximum length "len"
|
||||
* \details All variants demonstrated in example: \snippet pistring.cpp PIString::mid
|
||||
* \sa \a left(), \a right() */
|
||||
PIString mid(const int start, const int len = -1) const;
|
||||
|
||||
/*! \brief Return part of string from left and maximum length "len"
|
||||
* \details Example: \snippet pistring.cpp PIString::left
|
||||
* \sa \a mid(), \a right() */
|
||||
PIString left(const int len) const {return len <= 0 ? PIString() : mid(0, len);}
|
||||
|
||||
/*! \brief Return part of string from right and maximum length "len"
|
||||
* \details Example: \snippet pistring.cpp PIString::right
|
||||
* \sa \a mid(), \a left() */
|
||||
PIString right(const int len) const {return len <= 0 ? PIString() : mid(size() - len, len);}
|
||||
|
||||
/*! \brief Remove part of string from symbol as index "start" and maximum length "len"
|
||||
* and return this string
|
||||
* \details All variants demonstrated in example: \snippet pistring.cpp PIString::cutMid
|
||||
* \sa \a cutLeft(), \a cutRight() */
|
||||
PIString & cutMid(const int start, const int len);
|
||||
|
||||
/*! \brief Remove part of string from left and maximum length "len" and return this string
|
||||
* \details Example: \snippet pistring.cpp PIString::cutLeft
|
||||
* \sa \a cutMid(), \a cutRight() */
|
||||
PIString & cutLeft(const int len) {return len <= 0 ? *this : cutMid(0, len);}
|
||||
|
||||
/*! \brief Remove part of string from right and maximum length "len" and return this string
|
||||
* \details Example: \snippet pistring.cpp PIString::cutRight
|
||||
* \sa \a cutMid(), \a cutLeft() */
|
||||
PIString & cutRight(const int len) {return len <= 0 ? *this : cutMid(size() - len, len);}
|
||||
|
||||
/*! \brief Remove spaces at the start and at the end of string and return this string
|
||||
* \details Example: \snippet pistring.cpp PIString::trim
|
||||
* \sa \a trimmed() */
|
||||
PIString & trim();
|
||||
|
||||
/*! \brief Return copy of this string without spaces at the start and at the end
|
||||
* \details Example: \snippet pistring.cpp PIString::trimmed
|
||||
* \sa \a trim() */
|
||||
PIString trimmed() const;
|
||||
|
||||
/*! \brief Replace part of string from index "from" and maximum length "len"
|
||||
* with string "with" and return this string
|
||||
* \details Example: \snippet pistring.cpp PIString::replace_0
|
||||
* \sa \a replaced(), \a replaceAll() */
|
||||
PIString & replace(const int from, const int count, const PIString & with);
|
||||
|
||||
/*! \brief Replace part copy of this string from index "from" and maximum length "len"
|
||||
* with string "with" and return copied string
|
||||
* \details Example: \snippet pistring.cpp PIString::replaced_0
|
||||
* \sa \a replace(), \a replaceAll() */
|
||||
PIString replaced(const int from, const int count, const PIString & with) const {PIString str(*this); str.replace(from, count, with); return str;}
|
||||
|
||||
/*! \brief Replace first founded substring "what" with string "with" and return this string
|
||||
* \details If "ok" is not null, it set to "true" if something was replaced\n
|
||||
* Example: \snippet pistring.cpp PIString::replace_1
|
||||
* \sa \a replaced(), \a replaceAll() */
|
||||
PIString & replace(const PIString & what, const PIString & with, bool * ok = 0);
|
||||
|
||||
/*! \brief Replace first founded substring "what" with string "with" and return copied string
|
||||
* \details If "ok" is not null, it set to "true" if something was replaced\n
|
||||
* Example: \snippet pistring.cpp PIString::replaced_1
|
||||
* \sa \a replaced(), \a replaceAll() */
|
||||
PIString replaced(const PIString & what, const PIString & with, bool * ok = 0) const {PIString str(*this); str.replace(what, with, ok); return str;}
|
||||
|
||||
/*! \brief Replace all founded substrings "what" with strings "with" and return this string
|
||||
* \details Example: \snippet pistring.cpp PIString::replaceAll
|
||||
* \sa \a replace(), \a replaced() */
|
||||
PIString & replaceAll(const PIString & what, const PIString & with);
|
||||
PIString replaceAll(const PIString & what, const PIString & with) const {PIString str(*this); str.replaceAll(what, with); return str;}
|
||||
|
||||
/*! \brief Repeat content of string "times" times and return this string
|
||||
* \details Example: \snippet pistring.cpp PIString::repeat */
|
||||
PIString & repeat(int times) {PIString ss(*this); times--; piForTimes (times) *this += ss; return *this;}
|
||||
|
||||
/*! \brief Returns repeated "times" times string
|
||||
* \details Example: \snippet pistring.cpp PIString::repeated */
|
||||
PIString repeated(int times) const {PIString ss(*this); return ss.repeat(times);}
|
||||
|
||||
/*! \brief Insert symbol "c" after index "index" and return this string
|
||||
* \details Example: \snippet pistring.cpp PIString::insert_0 */
|
||||
PIString & insert(const int index, const PIChar & c) {PIDeque<PIChar>::insert(index, c); return *this;}
|
||||
|
||||
/*! \brief Insert symbol "c" after index "index" and return this string
|
||||
* \details Example: \snippet pistring.cpp PIString::insert_1 */
|
||||
PIString & insert(const int index, const char & c) {return insert(index, PIChar(c));}
|
||||
|
||||
/*! \brief Insert string "str" after index "index" and return this string
|
||||
* \details Example: \snippet pistring.cpp PIString::insert_2 */
|
||||
PIString & insert(const int index, const PIString & str);
|
||||
|
||||
/*! \brief Insert string "str" after index "index" and return this string
|
||||
* \details Example: \snippet pistring.cpp PIString::insert_2 */
|
||||
PIString & insert(const int index, const char * c) {return insert(index, PIString(c));}
|
||||
|
||||
/*! \brief Enlarge string to length "len" by addition sequence of symbols
|
||||
* "c" at the end of string, and return this string
|
||||
* \details Example: \snippet pistring.cpp PIString::expandRightTo
|
||||
* \sa \a expandLeftTo() */
|
||||
PIString & expandRightTo(const int len, const PIChar & c) {if (len > length()) resize(len, c); return *this;}
|
||||
|
||||
/*! \brief Enlarge string to length "len" by addition sequence of symbols
|
||||
* "c" at the beginning of string, and return this string
|
||||
* \details Example: \snippet pistring.cpp PIString::expandLeftTo
|
||||
* \sa \a expandRightTo() */
|
||||
PIString & expandLeftTo(const int len, const PIChar & c) {if (len > length()) insert(0, PIString(len - length(), c)); return *this;}
|
||||
|
||||
/*! \brief Reverse string and return this string
|
||||
* \details Example: \snippet pistring.cpp PIString::reverse
|
||||
* \sa \a reversed() */
|
||||
PIString & reverse() {PIString str(*this); clear(); piForeachR (const PIChar & c, str) push_back(c); return *this;}
|
||||
|
||||
/*! \brief Reverse copy of this string and return it
|
||||
* \details Example: \snippet pistring.cpp PIString::reversed
|
||||
* \sa \a reverse() */
|
||||
PIString reversed() const {PIString str(*this); str.reverse(); return str;}
|
||||
|
||||
|
||||
/*! \brief Take a part of string from symbol at index "start" and maximum length "len" and return it
|
||||
* \details Example: \snippet pistring.cpp PIString::takeMid
|
||||
* \sa \a takeLeft, \a takeRight() */
|
||||
PIString takeMid(const int start, const int len = -1) {PIString ret(mid(start, len)); cutMid(start, len); return ret;}
|
||||
|
||||
/*! \brief Take a part from the begin of string with maximum length "len" and return it
|
||||
* \details Example: \snippet pistring.cpp PIString::takeLeft
|
||||
* \sa \a takeMid(), \a takeRight() */
|
||||
PIString takeLeft(const int len) {PIString ret(left(len)); cutLeft(len); return ret;}
|
||||
|
||||
/*! \brief Take a part from the end of string with maximum length "len" and return it
|
||||
* \details Example: \snippet pistring.cpp PIString::takeRight
|
||||
* \sa \a takeMid(), \a takeLeft() */
|
||||
PIString takeRight(const int len) {PIString ret(right(len)); cutRight(len); return ret;}
|
||||
|
||||
/*! \brief Take a symbol from the begin of this string and return it
|
||||
* \details Example: \snippet pistring.cpp PIString::takeSymbol
|
||||
* \sa \a takeWord(), \a takeCWord(), \a takeLine(), \a takeNumber(), \a takeRange() */
|
||||
PIString takeSymbol();
|
||||
|
||||
/*! \brief Take a word from the begin of this string and return it
|
||||
* \details Example: \snippet pistring.cpp PIString::takeWord
|
||||
* \sa \a takeSymbol(), \a takeCWord(), \a takeLine(), \a takeNumber(), \a takeRange() */
|
||||
PIString takeWord();
|
||||
|
||||
/*! \brief Take a word with letters, numbers and '_' symbols from the
|
||||
* begin of this string and return it
|
||||
* \details Example: \snippet pistring.cpp PIString::takeCWord
|
||||
* \sa \a takeSymbol(), \a takeWord(), \a takeLine(), \a takeNumber(), \a takeRange() */
|
||||
PIString takeCWord();
|
||||
|
||||
/*! \brief Take a line from the begin of this string and return it
|
||||
* \details Example: \snippet pistring.cpp PIString::takeLine
|
||||
* \sa \a takeSymbol(), \a takeWord(), \a takeCWord(), \a takeNumber(), \a takeRange() */
|
||||
PIString takeLine();
|
||||
|
||||
/*! \brief Take a number with C-format from the begin of this string and return it
|
||||
* \details Example: \snippet pistring.cpp PIString::takeNumber
|
||||
* \sa \a takeSymbol(), \a takeWord(), \a takeCWord(), \a takeLine(), \a takeRange() */
|
||||
PIString takeNumber();
|
||||
|
||||
/*! \brief Take a range between "start" and "end" symbols from the begin of this
|
||||
* string and return it.
|
||||
* \details "Shield" symbol prevent analysis of the next symbol.
|
||||
* Example: \snippet pistring.cpp PIString::takeRange
|
||||
* \sa \a takeSymbol(), \a takeWord(), \a takeLine(), \a takeNumber() */
|
||||
PIString takeRange(const PIChar & start, const PIChar & end, const PIChar & shield = '\\');
|
||||
|
||||
//const char * data() {return convertToStd().c_str();}
|
||||
|
||||
|
||||
/*! \brief Return real bytes count of this string
|
||||
* \details It`s equivalent length of char sequence
|
||||
* returned by function \a data() \n
|
||||
* Example: \snippet pistring.cpp PIString::lengthAscii
|
||||
* \sa \a data() */
|
||||
int lengthAscii() const;
|
||||
|
||||
/*! \brief Return \c char * representation of this string
|
||||
* \details This function fill buffer by sequence
|
||||
* of chars. Minimum length of this buffer is count
|
||||
* of symbols. Returned \c char * is valid until next
|
||||
* execution of this function.\n
|
||||
* Example: \snippet pistring.cpp PIString::data
|
||||
* \sa \a lengthAscii() */
|
||||
const char * data() const;
|
||||
|
||||
//! \brief Return \c std::string representation of this string
|
||||
std::string stdString() const {return convertToStd();}
|
||||
#ifdef HAS_LOCALE
|
||||
wstring stdWString() const {return convertToWString();}
|
||||
#endif
|
||||
|
||||
//! \brief Return \a PIByteArray contains \a data() of this string
|
||||
PIByteArray toByteArray() const {const char * d = data(); return PIByteArray(d, lengthAscii());}
|
||||
|
||||
/*! \brief Split string with delimiter "delim" to \a PIStringList and return it
|
||||
* \details Example: \snippet pistring.cpp PIString::split */
|
||||
PIStringList split(const PIString & delim) const;
|
||||
|
||||
|
||||
//! \brief Convert each symbol in copyed string to upper case and return it
|
||||
PIString toUpperCase() const;
|
||||
|
||||
//! \brief Convert each symbol in copyed string to lower case and return it
|
||||
PIString toLowerCase() const;
|
||||
#ifdef HAS_LOCALE
|
||||
PIString toNativeDecimalPoints() const {PIString s(*this); if (currentLocale == 0) return s; return s.replaceAll(".", currentLocale->decimal_point).replaceAll(",", currentLocale->decimal_point);}
|
||||
#else
|
||||
PIString toNativeDecimalPoints() const {return PIString(*this).replaceAll(",", ".");}
|
||||
#endif
|
||||
|
||||
|
||||
//! \brief Search substring "str" from symbol at index "start" and return first occur position
|
||||
//! \details Example: \snippet pistring.cpp PIString::find
|
||||
int find(const char str, const int start = 0) const;
|
||||
|
||||
//! \brief Search substring "str" from symbol at index "start" and return first occur position
|
||||
//! \details Example: \snippet pistring.cpp PIString::find
|
||||
int find(const PIString str, const int start = 0) const;
|
||||
|
||||
//! \brief Search substring "str" from symbol at index "start" and return first occur position
|
||||
//! \details Example: \snippet pistring.cpp PIString::find
|
||||
int find(const char * str, const int start = 0) const {return find(PIString(str), start);}
|
||||
|
||||
//! \brief Search substring "str" from symbol at index "start" and return first occur position
|
||||
//! \details Example: \snippet pistring.cpp PIString::find
|
||||
int find(const string str, const int start = 0) const {return find(PIString(str), start);}
|
||||
|
||||
//! \brief Search substring "str" from symbol at index "start" and return last occur position
|
||||
//! \details Example: \snippet pistring.cpp PIString::findLast
|
||||
int findLast(const char str, const int start = 0) const;
|
||||
|
||||
//! \brief Search substring "str" from symbol at index "start" and return last occur position
|
||||
//! \details Example: \snippet pistring.cpp PIString::findLast
|
||||
int findLast(const PIString str, const int start = 0) const;
|
||||
|
||||
//! \brief Search substring "str" from symbol at index "start" and return last occur position
|
||||
//! \details Example: \snippet pistring.cpp PIString::findLast
|
||||
int findLast(const char * str, const int start = 0) const {return findLast(PIString(str), start);}
|
||||
|
||||
//! \brief Search substring "str" from symbol at index "start" and return last occur position
|
||||
//! \details Example: \snippet pistring.cpp PIString::findLast
|
||||
int findLast(const string str, const int start = 0) const {return findLast(PIString(str), start);}
|
||||
|
||||
//! \brief Search word "word" from symbol at index "start" and return first occur position.
|
||||
//! \details Example: \snippet pistring.cpp PIString::findWord
|
||||
int findWord(const PIString & word, const int start = 0) const;
|
||||
|
||||
//! \brief Search C-style word "word" from symbol at index "start" and return first occur position.
|
||||
//! \details Example: \snippet pistring.cpp PIString::findCWord
|
||||
int findCWord(const PIString & word, const int start = 0) const;
|
||||
|
||||
//! \brief Return if string starts with "str"
|
||||
bool startsWith(const PIString & str) const;
|
||||
|
||||
//! \brief Return if string ends with "str"
|
||||
bool endsWith(const PIString & str) const;
|
||||
|
||||
//! \brief Return symbols length of string
|
||||
int length() const {return size();}
|
||||
|
||||
//! \brief Return \c true if string is empty, i.e. length = 0
|
||||
bool isEmpty() const {return (size() == 0 || *this == "");}
|
||||
|
||||
|
||||
//! \brief Return \c true if string equal "true", "yes", "on" or positive not null numeric value
|
||||
bool toBool() const {PIString s(*this); if (atof(s.toNativeDecimalPoints().data()) > 0. || s.trimmed().toLowerCase() == "true" || s.trimmed().toLowerCase() == "yes" || s.trimmed().toLowerCase() == "on") return true; return false;}
|
||||
|
||||
//! \brief Return \c char numeric value of string
|
||||
char toChar() const;
|
||||
|
||||
//! \brief Return \c short numeric value of string in base "base"
|
||||
//! \details Example: \snippet pistring.cpp PIString::toNumber
|
||||
short toShort(int base = -1, bool * ok = 0) const {return short(toNumberBase(*this, base, ok));}
|
||||
|
||||
//! \brief Return \c ushort numeric value of string in base "base"
|
||||
//! \details Example: \snippet pistring.cpp PIString::toNumber
|
||||
ushort toUShort(int base = -1, bool * ok = 0) const {return ushort(toNumberBase(*this, base, ok));}
|
||||
|
||||
//! \brief Return \c int numeric value of string in base "base"
|
||||
//! \details Example: \snippet pistring.cpp PIString::toNumber
|
||||
int toInt(int base = -1, bool * ok = 0) const {return int(toNumberBase(*this, base, ok));}
|
||||
|
||||
//! \brief Return \c uint numeric value of string in base "base"
|
||||
//! \details Example: \snippet pistring.cpp PIString::toNumber
|
||||
uint toUInt(int base = -1, bool * ok = 0) const {return uint(toNumberBase(*this, base, ok));}
|
||||
|
||||
//! \brief Return \c long numeric value of string in base "base"
|
||||
//! \details Example: \snippet pistring.cpp PIString::toNumber
|
||||
long toLong(int base = -1, bool * ok = 0) const {return long(toNumberBase(*this, base, ok));}
|
||||
|
||||
//! \brief Return \c ulong numeric value of string in base "base"
|
||||
//! \details Example: \snippet pistring.cpp PIString::toNumber
|
||||
ulong toULong(int base = -1, bool * ok = 0) const {return ulong(toNumberBase(*this, base, ok));}
|
||||
|
||||
//! \brief Return \c llong numeric value of string in base "base"
|
||||
//! \details Example: \snippet pistring.cpp PIString::toNumber
|
||||
llong toLLong(int base = -1, bool * ok = 0) const {return toNumberBase(*this, base, ok);}
|
||||
|
||||
//! \brief Return \c ullong numeric value of string in base "base"
|
||||
//! \details Example: \snippet pistring.cpp PIString::toNumber
|
||||
ullong toULLong(int base = -1, bool * ok = 0) const {return ullong(toNumberBase(*this, base, ok));}
|
||||
|
||||
//! \brief Return \c float numeric value of string
|
||||
//! \details Example: \snippet pistring.cpp PIString::toFloat
|
||||
float toFloat() const {return (float)atof(toNativeDecimalPoints().data());}
|
||||
|
||||
//! \brief Return \c double numeric value of string
|
||||
//! \details Example: \snippet pistring.cpp PIString::toFloat
|
||||
double toDouble() const {return atof(toNativeDecimalPoints().data());}
|
||||
|
||||
//! \brief Return \c ldouble numeric value of string
|
||||
//! \details Example: \snippet pistring.cpp PIString::toFloat
|
||||
ldouble toLDouble() const {return atof(toNativeDecimalPoints().data());}
|
||||
|
||||
//inline PIString & setNumber(const char value) {clear(); *this += itos(value); return *this;}
|
||||
|
||||
//! \brief Set string content to numeric representation of "value" in base "base"
|
||||
//! \details Example: \snippet pistring.cpp PIString::setNumber
|
||||
PIString & setNumber(const short value, int base = 10, bool * ok = 0) {clear(); *this += PIString::fromNumber(value, base, ok); return *this;}
|
||||
|
||||
//! \brief Set string content to numeric representation of "value" in base "base"
|
||||
//! \details Example: \snippet pistring.cpp PIString::setNumber
|
||||
PIString & setNumber(const ushort value, int base = 10, bool * ok = 0) {clear(); *this += PIString::fromNumber(value, base, ok); return *this;}
|
||||
|
||||
//! \brief Set string content to numeric representation of "value" in base "base"
|
||||
//! \details Example: \snippet pistring.cpp PIString::setNumber
|
||||
PIString & setNumber(const int value, int base = 10, bool * ok = 0) {clear(); *this += PIString::fromNumber(value, base, ok); return *this;}
|
||||
|
||||
//! \brief Set string content to numeric representation of "value" in base "base"
|
||||
//! \details Example: \snippet pistring.cpp PIString::setNumber
|
||||
PIString & setNumber(const uint value, int base = 10, bool * ok = 0) {clear(); *this += PIString::fromNumber(value, base, ok); return *this;}
|
||||
|
||||
//! \brief Set string content to numeric representation of "value" in base "base"
|
||||
//! \details Example: \snippet pistring.cpp PIString::setNumber
|
||||
PIString & setNumber(const long value, int base = 10, bool * ok = 0) {clear(); *this += PIString::fromNumber(value, base, ok); return *this;}
|
||||
|
||||
//! \brief Set string content to numeric representation of "value" in base "base"
|
||||
//! \details Example: \snippet pistring.cpp PIString::setNumber
|
||||
PIString & setNumber(const ulong value, int base = 10, bool * ok = 0) {clear(); *this += PIString::fromNumber(value, base, ok); return *this;}
|
||||
|
||||
//! \brief Set string content to numeric representation of "value" in base "base"
|
||||
//! \details Example: \snippet pistring.cpp PIString::setNumber
|
||||
PIString & setNumber(const llong & value, int base = 10, bool * ok = 0) {clear(); *this += PIString::fromNumber(value, base, ok); return *this;}
|
||||
|
||||
//! \brief Set string content to numeric representation of "value" in base "base"
|
||||
//! \details Example: \snippet pistring.cpp PIString::setNumber
|
||||
PIString & setNumber(const ullong & value, int base = 10, bool * ok = 0) {clear(); *this += PIString::fromNumber(value, base, ok); return *this;}
|
||||
|
||||
//! \brief Set string content to numeric representation of "value"
|
||||
//! \details Example: \snippet pistring.cpp PIString::setFloat
|
||||
PIString & setNumber(const float value) {clear(); *this += ftos(value); return *this;}
|
||||
|
||||
//! \brief Set string content to numeric representation of "value"
|
||||
//! \details Example: \snippet pistring.cpp PIString::setFloat
|
||||
PIString & setNumber(const double & value) {clear(); *this += dtos(value); return *this;}
|
||||
|
||||
//! \brief Set string content to numeric representation of "value"
|
||||
//! \details Example: \snippet pistring.cpp PIString::setFloat
|
||||
PIString & setNumber(const ldouble & value) {clear(); *this += dtos(value); return *this;}
|
||||
|
||||
//! \brief Set string content to human readable size in B/kB/MB/GB/TB
|
||||
//! \details Example: \snippet pistring.cpp PIString::setReadableSize
|
||||
PIString & setReadableSize(llong bytes);
|
||||
|
||||
//inline static PIString fromNumber(const char value) {return PIString(itos(value));}
|
||||
|
||||
//! \brief Return string contains numeric representation of "value" in base "base"
|
||||
//! \details Example: \snippet pistring.cpp PIString::fromNumber
|
||||
static PIString fromNumber(const short value, int base = 10, bool * ok = 0) {return fromNumberBaseS(llong(value), base, ok);}
|
||||
|
||||
//! \brief Return string contains numeric representation of "value" in base "base"
|
||||
//! \details Example: \snippet pistring.cpp PIString::fromNumber
|
||||
static PIString fromNumber(const ushort value, int base = 10, bool * ok = 0) {return fromNumberBaseU(ullong(value), base, ok);}
|
||||
|
||||
//! \brief Return string contains numeric representation of "value" in base "base"
|
||||
//! \details Example: \snippet pistring.cpp PIString::fromNumber
|
||||
static PIString fromNumber(const int value, int base = 10, bool * ok = 0) {return fromNumberBaseS(llong(value), base, ok);}
|
||||
|
||||
//! \brief Return string contains numeric representation of "value" in base "base"
|
||||
//! \details Example: \snippet pistring.cpp PIString::fromNumber
|
||||
static PIString fromNumber(const uint value, int base = 10, bool * ok = 0) {return fromNumberBaseU(ullong(value), base, ok);}
|
||||
|
||||
//! \brief Return string contains numeric representation of "value" in base "base"
|
||||
//! \details Example: \snippet pistring.cpp PIString::fromNumber
|
||||
static PIString fromNumber(const long value, int base = 10, bool * ok = 0) {return fromNumberBaseS(llong(value), base, ok);}
|
||||
|
||||
//! \brief Return string contains numeric representation of "value" in base "base"
|
||||
//! \details Example: \snippet pistring.cpp PIString::fromNumber
|
||||
static PIString fromNumber(const ulong value, int base = 10, bool * ok = 0) {return fromNumberBaseU(ullong(value), base, ok);}
|
||||
|
||||
//! \brief Return string contains numeric representation of "value" in base "base"
|
||||
//! \details Example: \snippet pistring.cpp PIString::fromNumber
|
||||
static PIString fromNumber(const llong & value, int base = 10, bool * ok = 0) {return fromNumberBaseS(value, base, ok);}
|
||||
|
||||
//! \brief Return string contains numeric representation of "value" in base "base"
|
||||
//! \details Example: \snippet pistring.cpp PIString::fromNumber
|
||||
static PIString fromNumber(const ullong & value, int base = 10, bool * ok = 0) {return fromNumberBaseU(value, base, ok);}
|
||||
|
||||
//! \brief Return string contains numeric representation of "value"
|
||||
//! \details Example: \snippet pistring.cpp PIString::fromFloat
|
||||
static PIString fromNumber(const float value) {return PIString(ftos(value));}
|
||||
|
||||
//! \brief Return string contains numeric representation of "value"
|
||||
//! \details Example: \snippet pistring.cpp PIString::fromFloat
|
||||
static PIString fromNumber(const double & value) {return PIString(dtos(value));}
|
||||
|
||||
//! \brief Return string contains numeric representation of "value"
|
||||
//! \details Example: \snippet pistring.cpp PIString::fromFloat
|
||||
static PIString fromNumber(const ldouble & value) {return PIString(dtos(value));}
|
||||
|
||||
//! \brief Return "true" or "false"
|
||||
static PIString fromBool(const bool value) {return PIString(value ? "true" : "false");}
|
||||
|
||||
//! \brief Return string contains human readable size in B/kB/MB/GB/TB
|
||||
//! \details Example: \snippet pistring.cpp PIString::readableSize
|
||||
static PIString readableSize(llong bytes) {PIString s; s.setReadableSize(bytes); return s;}
|
||||
|
||||
PIString & removeAll(char v) {replaceAll(v, ""); return *this;}
|
||||
PIString & removeAll(const PIString & v) {replaceAll(v, ""); return *this;}
|
||||
|
||||
private:
|
||||
static const char toBaseN[];
|
||||
static const int fromBaseN[];
|
||||
|
||||
static PIString fromNumberBaseS(const llong value, int base = 10, bool * ok = 0) {
|
||||
if (value == 0) return PIString("0");
|
||||
if (base < 2 || base > 40) {if (ok != 0) *ok = false; return PIString();}
|
||||
if (ok != 0) *ok = true;
|
||||
if (base == 10) return itos(value);
|
||||
PIString ret;
|
||||
llong v = value < 0 ? -value : value, cn;
|
||||
int b = base;
|
||||
while (v >= llong(base)) {
|
||||
cn = v % b;
|
||||
v /= b;
|
||||
//cout << int(cn) << ", " << int(v) << endl;
|
||||
ret.push_front(PIChar(toBaseN[cn]));
|
||||
}
|
||||
if (v > 0) ret.push_front(PIChar(toBaseN[v]));
|
||||
if (value < 0) ret.push_front('-');
|
||||
return ret;
|
||||
}
|
||||
static PIString fromNumberBaseU(const ullong value, int base = 10, bool * ok = 0) {
|
||||
if (value == 0) return PIString("0");
|
||||
if (base < 2 || base > 40) {if (ok != 0) *ok = false; return PIString();}
|
||||
if (ok != 0) *ok = true;
|
||||
if (base == 10) return itos(value);
|
||||
PIString ret;
|
||||
ullong v = value, cn;
|
||||
int b = base;
|
||||
while (v >= ullong(base)) {
|
||||
cn = v % b;
|
||||
v /= b;
|
||||
//cout << int(cn) << ", " << int(v) << endl;
|
||||
ret.push_front(PIChar(toBaseN[cn]));
|
||||
}
|
||||
if (v > 0) ret.push_front(PIChar(toBaseN[v]));
|
||||
return ret;
|
||||
}
|
||||
static llong toNumberBase(const PIString & value, int base = -1, bool * ok = 0) {
|
||||
PIString v = value.trimmed();
|
||||
if (base < 0) {
|
||||
int ind = v.find("0x");
|
||||
if (ind == 0 || ind == 1) {v.remove(ind, 2); base = 16;}
|
||||
else base = 10;
|
||||
} else
|
||||
if (base < 2 || base > 40) {if (ok != 0) *ok = false; return 0;}
|
||||
//v.reverse();
|
||||
if (ok != 0) *ok = true;
|
||||
PIVector<int> digits;
|
||||
llong ret = 0, m = 1;
|
||||
bool neg = false;
|
||||
int cs;
|
||||
for (int i = 0; i < v.size_s(); ++i) {
|
||||
if (v[i] == PIChar('-')) {neg = !neg; continue;}
|
||||
cs = fromBaseN[int(v[i].toAscii())];
|
||||
if (cs < 0 || cs >= base) break;
|
||||
digits << cs;
|
||||
}
|
||||
for (int i = digits.size_s() - 1; i >= 0; --i) {
|
||||
ret += digits[i] * m;
|
||||
m *= base;
|
||||
}
|
||||
if (neg) ret = -ret;
|
||||
/*piForeachC (PIChar & i, v) {
|
||||
if (i == PIChar('-')) {ret = -ret; continue;}
|
||||
cs = fromBaseN[int(i.toAscii())];
|
||||
cout << i << " = " << cs << endl;
|
||||
if (cs < 0 || cs >= base) return ret;
|
||||
ret += cs * m;
|
||||
m *= base;
|
||||
}*/
|
||||
return ret;
|
||||
}
|
||||
void appendFromChars(const char * c, int s);
|
||||
string convertToStd() const;
|
||||
#ifdef HAS_LOCALE
|
||||
wstring convertToWString() const {wstring s; for (int i = 0; i < length(); ++i) s.push_back(at(i).toWChar()); return s;}
|
||||
#endif
|
||||
|
||||
mutable PIByteArray data_;
|
||||
//string std_string;
|
||||
//wstring std_wstring;
|
||||
|
||||
};
|
||||
|
||||
|
||||
//! \relatesalso PIString \brief Output operator to std::ostream (cout)
|
||||
inline std::ostream & operator <<(std::ostream & s, const PIString & v) {for (int i = 0; i < v.length(); ++i) s << v[i]; return s;}
|
||||
|
||||
//! \relatesalso PIString \brief Input operator from std::istream (cin)
|
||||
inline std::istream & operator >>(std::istream & s, PIString & v) {string ss; s >> ss; v << PIString(ss); return s;}
|
||||
|
||||
//! \relatesalso PIString \relatesalso PICout \brief Output operator to PICout
|
||||
inline PICout operator <<(PICout s, const PIString & v) {s.space(); s.quote(); s.setControl(0, true); for (int i = 0; i < v.length(); ++i) s << v[i]; s.restoreControl(); s.quote(); return s;}
|
||||
|
||||
|
||||
//! \relatesalso PIString \relatesalso PIByteArray \brief Output operator to PIByteArray
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PIString & v) {int l = v.lengthAscii(); s << l; if (l <= 0) return s; int os = s.size_s(); s.enlarge(l); memcpy(s.data(os), v.data(), l); return s;}
|
||||
|
||||
//! \relatesalso PIString \relatesalso PIByteArray \brief Input operator from PIByteArray
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PIString & v) {if (s.size() < 4) {v.clear(); return s;} int l; s >> l; if (l <= 0) return s; v = PIString((const char * )s.data(), l); s.remove(0, l); return s;}
|
||||
|
||||
|
||||
//! \relatesalso PIString \brief Return concatenated string
|
||||
inline PIString operator +(const PIString & str, const PIString & f) {PIString s(str); s += f; return s;}
|
||||
|
||||
//inline PIString operator +(const PIString & f, const char c) {PIString s(f); s.push_back(c); return s;}
|
||||
|
||||
//! \relatesalso PIString \brief Return concatenated string
|
||||
inline PIString operator +(const PIString & f, const char * str) {PIString s(f); s += str; return s;}
|
||||
|
||||
//! \relatesalso PIString \brief Return concatenated string
|
||||
inline PIString operator +(const PIString & f, const string & str) {PIString s(f); s += str; return s;}
|
||||
|
||||
//inline PIString operator +(const char c, const PIString & f) {return PIString(c) + f;}
|
||||
|
||||
//! \relatesalso PIString \brief Return concatenated string
|
||||
inline PIString operator +(const char * str, const PIString & f) {return PIString(str) + f;}
|
||||
|
||||
//! \relatesalso PIString \brief Return concatenated string
|
||||
inline PIString operator +(const string & str, const PIString & f) {return PIString(str) + f;}
|
||||
|
||||
inline char chrUpr(char c);
|
||||
inline char chrLwr(char c);
|
||||
|
||||
|
||||
/*!\brief Strings array class
|
||||
* \details This class is based on \a PIDeque<PIString> and
|
||||
* expand it functionality. */
|
||||
class PIP_EXPORT PIStringList: public PIDeque<PIString>
|
||||
{
|
||||
public:
|
||||
|
||||
//! Contructs empty strings list
|
||||
PIStringList() {;}
|
||||
|
||||
//! Contructs strings list with one string "str"
|
||||
PIStringList(const PIString & str) {push_back(str);}
|
||||
|
||||
//! Contructs empty strings list with strings "s0" and "s1"
|
||||
PIStringList(const PIString & s0, const PIString & s1) {push_back(s0); push_back(s1);}
|
||||
|
||||
//! Contructs empty strings list with strings "s0", "s1" and "s2"
|
||||
PIStringList(const PIString & s0, const PIString & s1, const PIString & s2) {push_back(s0); push_back(s1); push_back(s2);}
|
||||
|
||||
//! Contructs empty strings list with strings "s0", "s1", "s2" and "s3"
|
||||
PIStringList(const PIString & s0, const PIString & s1, const PIString & s2, const PIString & s3) {push_back(s0); push_back(s1); push_back(s2); push_back(s3);}
|
||||
|
||||
PIStringList(const PIStringList & o): PIDeque<PIString>() {resize(o.size()); for (uint i = 0; i < size(); ++i) (*this)[i] = o[i];}
|
||||
PIStringList(const PIVector<PIString> & o): PIDeque<PIString>() {resize(o.size()); for (uint i = 0; i < size(); ++i) (*this)[i] = o[i];}
|
||||
PIStringList(const PIDeque<PIString> & o): PIDeque<PIString>() {resize(o.size()); for (uint i = 0; i < size(); ++i) (*this)[i] = o[i];}
|
||||
|
||||
|
||||
//! \brief Join all strings in one with delimiter "delim" and return it
|
||||
//! \details Example: \snippet pistring.cpp PIStringList::join
|
||||
PIString join(const PIString & delim) const {PIString s; for (uint i = 0; i < size(); ++i) {s += at(i); if (i < size() - 1) s += delim;} return s;}
|
||||
|
||||
//! \brief Remove all strings equal "value" and return this
|
||||
//! \details Example: \snippet pistring.cpp PIStringList::removeStrings
|
||||
PIStringList & removeStrings(const PIString & value) {for (uint i = 0; i < size(); ++i) {if (at(i) == value) {remove(i); --i;}} return *this;}
|
||||
|
||||
PIStringList & remove(uint num) {PIDeque<PIString>::remove(num); return *this;}
|
||||
PIStringList & remove(uint num, uint count) {PIDeque<PIString>::remove(num, count); return *this;}
|
||||
|
||||
//! \brief Remove duplicated strings and return this
|
||||
//! \details Example: \snippet pistring.cpp PIStringList::removeDuplicates
|
||||
PIStringList & removeDuplicates();
|
||||
|
||||
//! \brief Trim all strings
|
||||
//! \details Example: \snippet pistring.cpp PIStringList::trim
|
||||
PIStringList & trim() {for (uint i = 0; i < size(); ++i) at(i).trim(); return *this;}
|
||||
|
||||
//! Return sum of lengths of all strings
|
||||
uint contentSize() {uint s = 0; for (uint i = 0; i < size(); ++i) s += at(i).size(); return s;}
|
||||
|
||||
//! Compare operator
|
||||
bool operator ==(const PIStringList & o) const {if (size() != o.size()) return false; for (size_t i = 0; i < size(); ++i) if (o[i] != (*this)[i]) return false; return true;}
|
||||
|
||||
//! Compare operator
|
||||
bool operator !=(const PIStringList & o) const {return !(o == (*this));}
|
||||
|
||||
PIStringList & operator =(const PIStringList & o) {clear(); for (uint i = 0; i < o.size(); ++i) *this << o[i]; return *this;}
|
||||
|
||||
PIStringList & operator <<(const PIString & str) {push_back(str); return *this;}
|
||||
PIStringList & operator <<(const PIStringList & sl) {piForeachC (PIString & i, sl) push_back(i); return *this;}
|
||||
//inline PIStringList & operator <<(const char c) {push_back(PIString(c)); return *this;}
|
||||
PIStringList & operator <<(const char * str) {push_back(PIString(str)); return *this;}
|
||||
PIStringList & operator <<(const string & str) {push_back(str); return *this;}
|
||||
PIStringList & operator <<(const int & num) {push_back(PIString::fromNumber(num)); return *this;}
|
||||
PIStringList & operator <<(const short & num) {push_back(PIString::fromNumber(num)); return *this;}
|
||||
PIStringList & operator <<(const long & num) {push_back(PIString::fromNumber(num)); return *this;}
|
||||
PIStringList & operator <<(const float & num) {push_back(PIString::fromNumber(num)); return *this;}
|
||||
PIStringList & operator <<(const double & num) {push_back(PIString::fromNumber(num)); return *this;}
|
||||
|
||||
};
|
||||
|
||||
|
||||
//! \relatesalso PIStringList \relatesalso PIByteArray \brief Output operator to PIByteArray
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PIStringList & v) {s << v.size_s(); for (int i = 0; i < v.size_s(); ++i) s << v[i]; return s;}
|
||||
|
||||
//! \relatesalso PIStringList \relatesalso PIByteArray \brief Input operator from PIByteArray
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PIStringList & v) {int sz; s >> sz; v.resize(sz); for (int i = 0; i < sz; ++i) s >> v[i]; return s;}
|
||||
|
||||
|
||||
//! \relatesalso PIStringList \brief Output operator to std::ostream (cout)
|
||||
inline std::ostream & operator <<(std::ostream & s, const PIStringList & v) {s << "{"; for (uint i = 0; i < v.size(); ++i) {s << '\"' << v[i] << '\"'; if (i < v.size() - 1) s << ", ";} s << "}"; return s;}
|
||||
|
||||
//! \relatesalso PIStringList \relatesalso PICout \brief Output operator to PICout
|
||||
inline PICout operator <<(PICout s, const PIStringList & v) {s.space(); s.setControl(0, true); s << "{"; for (uint i = 0; i < v.size(); ++i) {s << '\"' << v[i] << '\"'; if (i < v.size() - 1) s << ", ";} s << "}"; s.restoreControl(); return s;}
|
||||
|
||||
#endif // PISTRING_H
|
||||
404
src/core/pitime.cpp
Executable file
404
src/core/pitime.cpp
Executable file
@@ -0,0 +1,404 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Timer
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pitime.h"
|
||||
#include "pisystemtests.h"
|
||||
|
||||
|
||||
/*! \class PISystemTime
|
||||
* \brief System time
|
||||
*
|
||||
* \section PISystemTime_sec0 Synopsis
|
||||
* This class provide arithmetic functions for POSIX system time.
|
||||
* This time represents as seconds and nanosecons in integer formats.
|
||||
* You can take current system time with function \a PISystemTime::current(),
|
||||
* compare times, sum or subtract two times, convert time to/from
|
||||
* seconds, milliseconds, microseconds or nanoseconds.
|
||||
* \section PISystemTime_sec1 Example
|
||||
* \snippet pitimer.cpp system_time
|
||||
*/
|
||||
|
||||
|
||||
/*! \class PITimeMeasurer
|
||||
* \brief Time measurements
|
||||
*
|
||||
* \section PITimeMeasurer_sec0 Synopsis
|
||||
* Function \a reset() set time mark to current
|
||||
* system time, then functions double elapsed_*() returns time elapsed from this mark.
|
||||
* These functions can returns nano-, micro-, milli- and seconds with suffixes "n", "u", "m"
|
||||
* and "s"
|
||||
*/
|
||||
|
||||
|
||||
void piUSleep(int usecs) {
|
||||
if (usecs <= 0) return;
|
||||
#ifdef WINDOWS
|
||||
if (usecs > 0) Sleep(usecs / 1000);
|
||||
#else
|
||||
usecs -= PISystemTests::usleep_offset_us;
|
||||
if (usecs > 0) usleep(usecs);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool operator ==(const PITime & t0, const PITime & t1) {
|
||||
return (t0.hours == t1.hours && t0.minutes == t1.minutes && t0.seconds == t1.seconds);
|
||||
}
|
||||
|
||||
|
||||
bool operator <(const PITime & t0, const PITime & t1) {
|
||||
if (t0.hours == t1.hours) {
|
||||
if (t0.minutes == t1.minutes) {
|
||||
return t0.seconds < t1.seconds;
|
||||
} else return t0.minutes < t1.minutes;
|
||||
} else return t0.hours < t1.hours;
|
||||
}
|
||||
|
||||
|
||||
bool operator >(const PITime & t0, const PITime & t1) {
|
||||
if (t0.hours == t1.hours) {
|
||||
if (t0.minutes == t1.minutes) {
|
||||
return t0.seconds > t1.seconds;
|
||||
} else return t0.minutes > t1.minutes;
|
||||
} else return t0.hours > t1.hours;
|
||||
}
|
||||
|
||||
bool operator ==(const PIDate & t0, const PIDate & t1) {
|
||||
return (t0.year == t1.year && t0.month == t1.month && t0.day == t1.day);
|
||||
}
|
||||
|
||||
|
||||
bool operator <(const PIDate & t0, const PIDate & t1) {
|
||||
if (t0.year == t1.year) {
|
||||
if (t0.month == t1.month) {
|
||||
return t0.day < t1.day;
|
||||
} else return t0.month < t1.month;
|
||||
} else return t0.year < t1.year;
|
||||
}
|
||||
|
||||
|
||||
bool operator >(const PIDate & t0, const PIDate & t1) {
|
||||
if (t0.year == t1.year) {
|
||||
if (t0.month == t1.month) {
|
||||
return t0.day > t1.day;
|
||||
} else return t0.month > t1.month;
|
||||
} else return t0.year > t1.year;
|
||||
}
|
||||
|
||||
bool operator ==(const PIDateTime & t0, const PIDateTime & t1) {
|
||||
return (t0.year == t1.year && t0.month == t1.month && t0.day == t1.day &&
|
||||
t0.hours == t1.hours && t0.minutes == t1.minutes && t0.seconds == t1.seconds);
|
||||
}
|
||||
|
||||
|
||||
bool operator <(const PIDateTime & t0, const PIDateTime & t1) {
|
||||
if (t0.year == t1.year) {
|
||||
if (t0.month == t1.month) {
|
||||
if (t0.day == t1.day) {
|
||||
if (t0.hours == t1.hours) {
|
||||
if (t0.minutes == t1.minutes) {
|
||||
return t0.seconds < t1.seconds;
|
||||
} else return t0.minutes < t1.minutes;
|
||||
} else return t0.hours < t1.hours;
|
||||
} else return t0.day < t1.day;
|
||||
} else return t0.month < t1.month;
|
||||
} else return t0.year < t1.year;
|
||||
}
|
||||
|
||||
|
||||
bool operator >(const PIDateTime & t0, const PIDateTime & t1) {
|
||||
if (t0.year == t1.year) {
|
||||
if (t0.month == t1.month) {
|
||||
if (t0.day == t1.day) {
|
||||
if (t0.hours == t1.hours) {
|
||||
if (t0.minutes == t1.minutes) {
|
||||
return t0.seconds > t1.seconds;
|
||||
} else return t0.minutes > t1.minutes;
|
||||
} else return t0.hours > t1.hours;
|
||||
} else return t0.day > t1.day;
|
||||
} else return t0.month > t1.month;
|
||||
} else return t0.year > t1.year;
|
||||
}
|
||||
|
||||
|
||||
PITime PITime::current() {
|
||||
time_t rt = ::time(0);
|
||||
tm * pt = localtime(&rt);
|
||||
PITime t;
|
||||
t.seconds = pt->tm_sec;
|
||||
t.minutes = pt->tm_min;
|
||||
t.hours = pt->tm_hour;
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
PIDate PIDate::current() {
|
||||
time_t rt = ::time(0);
|
||||
tm * pt = localtime(&rt);
|
||||
PIDate d;
|
||||
d.day = pt->tm_mday;
|
||||
d.month = pt->tm_mon + 1;
|
||||
d.year = pt->tm_year + 1900;
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
PIDateTime PIDateTime::current() {
|
||||
time_t rt = ::time(0);
|
||||
tm * pt = localtime(&rt);
|
||||
PIDateTime dt;
|
||||
dt.milliseconds = 0;
|
||||
dt.seconds = pt->tm_sec;
|
||||
dt.minutes = pt->tm_min;
|
||||
dt.hours = pt->tm_hour;
|
||||
dt.day = pt->tm_mday;
|
||||
dt.month = pt->tm_mon + 1;
|
||||
dt.year = pt->tm_year + 1900;
|
||||
return dt;
|
||||
}
|
||||
|
||||
|
||||
#ifdef WINDOWS
|
||||
PISystemTime::PISystemTime(const FILETIME & t) {
|
||||
ullong lt = ullong(t.dwHighDateTime) * 0x100000000U + ullong(t.dwLowDateTime);
|
||||
seconds = lt / 10000000U;
|
||||
nanoseconds = (lt % 10000000U) * 100U;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
PISystemTime PISystemTime::abs() const {
|
||||
if (seconds < 0)
|
||||
return PISystemTime(piAbsl(seconds) - 1, 1e+9 - piAbsl(nanoseconds));
|
||||
else
|
||||
return PISystemTime(piAbsl(seconds), piAbsl(nanoseconds));
|
||||
}
|
||||
|
||||
|
||||
PISystemTime PISystemTime::current(bool precise_but_not_system) {
|
||||
#ifdef WINDOWS
|
||||
if (precise_but_not_system) {
|
||||
llong qpc(0);
|
||||
if (__pi_perf_freq > 0) {
|
||||
qpc = __PIQueryPerformanceCounter();
|
||||
return PISystemTime::fromSeconds(qpc / double(__pi_perf_freq));
|
||||
}
|
||||
return PISystemTime();
|
||||
} else {
|
||||
FILETIME ft, sft;
|
||||
# if (_WIN32_WINNT >= 0x0602)
|
||||
GetSystemTimePreciseAsFileTime(&ft);
|
||||
# else
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
# endif
|
||||
sft.dwHighDateTime = ft.dwHighDateTime - __pi_ftjan1970.dwHighDateTime;
|
||||
if (ft.dwLowDateTime < __pi_ftjan1970.dwLowDateTime) {
|
||||
sft.dwLowDateTime = ft.dwLowDateTime + (0xFFFFFFFF - __pi_ftjan1970.dwLowDateTime);
|
||||
sft.dwHighDateTime++;
|
||||
} else
|
||||
sft.dwLowDateTime = ft.dwLowDateTime - __pi_ftjan1970.dwLowDateTime;
|
||||
ullong lt = ullong(sft.dwHighDateTime) * 0x100000000U + ullong(sft.dwLowDateTime);
|
||||
return PISystemTime(lt / 10000000U, (lt % 10000000U) * 100U);
|
||||
}
|
||||
//long t_cur = GetCurrentTime();
|
||||
//return PISystemTime(t_cur / 1000, (t_cur % 1000) * 1000000);
|
||||
#else
|
||||
# ifdef MAC_OS
|
||||
mach_timespec_t t_cur;
|
||||
clock_get_time(__pi_mac_clock, &t_cur);
|
||||
# else
|
||||
timespec t_cur;
|
||||
clock_gettime(0, &t_cur);
|
||||
# endif
|
||||
return PISystemTime(t_cur.tv_sec, t_cur.tv_nsec);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
PIString PITime::toString(const PIString & format) const {
|
||||
PIString ts = format;
|
||||
ts.replace("hh", PIString::fromNumber(hours).expandLeftTo(2, '0'));
|
||||
ts.replace("h", PIString::fromNumber(hours));
|
||||
ts.replace("mm", PIString::fromNumber(minutes).expandLeftTo(2, '0'));
|
||||
ts.replace("m", PIString::fromNumber(minutes));
|
||||
ts.replace("ss", PIString::fromNumber(seconds).expandLeftTo(2, '0'));
|
||||
ts.replace("s", PIString::fromNumber(seconds));
|
||||
ts.replace("zzz", PIString::fromNumber(milliseconds).expandLeftTo(3, '0'));
|
||||
ts.replace("zz", PIString::fromNumber(milliseconds).expandLeftTo(2, '0'));
|
||||
ts.replace("z", PIString::fromNumber(milliseconds));
|
||||
return ts;
|
||||
}
|
||||
|
||||
|
||||
PIString PIDate::toString(const PIString & format) const {
|
||||
PIString ts = format;
|
||||
ts.replace("yyyy", PIString::fromNumber(year).expandLeftTo(4, '0'));
|
||||
ts.replace("yy", PIString::fromNumber(year).right(2));
|
||||
ts.replace("y", PIString::fromNumber(year).right(1));
|
||||
ts.replace("MM", PIString::fromNumber(month).expandLeftTo(2, '0'));
|
||||
ts.replace("M", PIString::fromNumber(month));
|
||||
ts.replace("dd", PIString::fromNumber(day).expandLeftTo(2, '0'));
|
||||
ts.replace("d", PIString::fromNumber(day));
|
||||
return ts;
|
||||
}
|
||||
|
||||
|
||||
PIString PIDateTime::toString(const PIString & format) const {
|
||||
PIString ts = format;
|
||||
ts.replace("yyyy", PIString::fromNumber(year).expandLeftTo(4, '0'));
|
||||
ts.replace("yy", PIString::fromNumber(year).right(2));
|
||||
ts.replace("y", PIString::fromNumber(year).right(1));
|
||||
ts.replace("MM", PIString::fromNumber(month).expandLeftTo(2, '0'));
|
||||
ts.replace("M", PIString::fromNumber(month));
|
||||
ts.replace("dd", PIString::fromNumber(day).expandLeftTo(2, '0'));
|
||||
ts.replace("d", PIString::fromNumber(day));
|
||||
ts.replace("hh", PIString::fromNumber(hours).expandLeftTo(2, '0'));
|
||||
ts.replace("h", PIString::fromNumber(hours));
|
||||
ts.replace("mm", PIString::fromNumber(minutes).expandLeftTo(2, '0'));
|
||||
ts.replace("m", PIString::fromNumber(minutes));
|
||||
ts.replace("ss", PIString::fromNumber(seconds).expandLeftTo(2, '0'));
|
||||
ts.replace("s", PIString::fromNumber(seconds));
|
||||
ts.replace("zzz", PIString::fromNumber(milliseconds).expandLeftTo(3, '0'));
|
||||
ts.replace("zz", PIString::fromNumber(milliseconds).expandLeftTo(2, '0'));
|
||||
ts.replace("z", PIString::fromNumber(milliseconds));
|
||||
return ts;
|
||||
}
|
||||
|
||||
|
||||
#ifdef WINDOWS
|
||||
PIDateTime::PIDateTime(FILETIME t) {
|
||||
FILETIME lt;
|
||||
FileTimeToLocalFileTime(&t, <);
|
||||
SYSTEMTIME st;
|
||||
FileTimeToSystemTime(<, &st);
|
||||
year = st.wYear;
|
||||
month = st.wMonth;
|
||||
day = st.wDay;
|
||||
hours = st.wHour;
|
||||
minutes = st.wMinute;
|
||||
seconds = st.wSecond;
|
||||
milliseconds = st.wMilliseconds;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
time_t PIDateTime::toSecondSinceEpoch() const {
|
||||
tm pt;
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.tm_sec = seconds;
|
||||
pt.tm_min = minutes;
|
||||
pt.tm_hour = hours;
|
||||
pt.tm_mday = day;
|
||||
pt.tm_mon = month - 1;
|
||||
#ifdef WINDOWS
|
||||
pt.tm_year = piMaxi(year - 1900, 70);
|
||||
#else
|
||||
pt.tm_year = piMaxi(year - 1900, 0);
|
||||
#endif
|
||||
return mktime(&pt);
|
||||
}
|
||||
|
||||
|
||||
PIDateTime PIDateTime::fromSecondSinceEpoch(const time_t sec) {
|
||||
tm * pt = localtime(&sec);
|
||||
PIDateTime dt;
|
||||
dt.seconds = pt->tm_sec;
|
||||
dt.minutes = pt->tm_min;
|
||||
dt.hours = pt->tm_hour;
|
||||
dt.day = pt->tm_mday;
|
||||
dt.month = pt->tm_mon + 1;
|
||||
dt.year = pt->tm_year + 1900;
|
||||
return dt;
|
||||
|
||||
}
|
||||
|
||||
|
||||
PIString time2string(const PITime & time, const PIString & format) {
|
||||
PIString ts = format;
|
||||
ts.replace("hh", PIString::fromNumber(time.hours).expandLeftTo(2, '0'));
|
||||
ts.replace("h", PIString::fromNumber(time.hours));
|
||||
ts.replace("mm", PIString::fromNumber(time.minutes).expandLeftTo(2, '0'));
|
||||
ts.replace("m", PIString::fromNumber(time.minutes));
|
||||
ts.replace("ss", PIString::fromNumber(time.seconds).expandLeftTo(2, '0'));
|
||||
ts.replace("s", PIString::fromNumber(time.seconds));
|
||||
return ts;
|
||||
}
|
||||
|
||||
|
||||
PIString date2string(const PIDate & date, const PIString & format) {
|
||||
PIString ts = format;
|
||||
ts.replace("yyyy", PIString::fromNumber(date.year).expandLeftTo(4, '0'));
|
||||
ts.replace("yy", PIString::fromNumber(date.year).right(2));
|
||||
ts.replace("y", PIString::fromNumber(date.year).right(1));
|
||||
ts.replace("MM", PIString::fromNumber(date.month).expandLeftTo(2, '0'));
|
||||
ts.replace("M", PIString::fromNumber(date.month));
|
||||
ts.replace("dd", PIString::fromNumber(date.day).expandLeftTo(2, '0'));
|
||||
ts.replace("d", PIString::fromNumber(date.day));
|
||||
return ts;
|
||||
}
|
||||
|
||||
|
||||
PIString datetime2string(const PIDateTime & date, const PIString & format) {
|
||||
PIString ts = format;
|
||||
ts.replace("hh", PIString::fromNumber(date.hours).expandLeftTo(2, '0'));
|
||||
ts.replace("h", PIString::fromNumber(date.hours));
|
||||
ts.replace("mm", PIString::fromNumber(date.minutes).expandLeftTo(2, '0'));
|
||||
ts.replace("m", PIString::fromNumber(date.minutes));
|
||||
ts.replace("ss", PIString::fromNumber(date.seconds).expandLeftTo(2, '0'));
|
||||
ts.replace("s", PIString::fromNumber(date.seconds));
|
||||
ts.replace("yyyy", PIString::fromNumber(date.year).expandLeftTo(4, '0'));
|
||||
ts.replace("yy", PIString::fromNumber(date.year).right(2));
|
||||
ts.replace("y", PIString::fromNumber(date.year).right(1));
|
||||
ts.replace("MM", PIString::fromNumber(date.month).expandLeftTo(2, '0'));
|
||||
ts.replace("M", PIString::fromNumber(date.month));
|
||||
ts.replace("dd", PIString::fromNumber(date.day).expandLeftTo(2, '0'));
|
||||
ts.replace("d", PIString::fromNumber(date.day));
|
||||
return ts;
|
||||
}
|
||||
|
||||
|
||||
|
||||
PITimeMeasurer::PITimeMeasurer() {
|
||||
reset();
|
||||
}
|
||||
|
||||
|
||||
double PITimeMeasurer::elapsed_n() {
|
||||
return (PISystemTime::current(true) - t_st).toNanoseconds() - PISystemTests::time_elapsed_ns;
|
||||
}
|
||||
|
||||
|
||||
double PITimeMeasurer::elapsed_u() {
|
||||
return (PISystemTime::current(true) - t_st).toMicroseconds() - PISystemTests::time_elapsed_ns / 1.E+3;
|
||||
}
|
||||
|
||||
|
||||
double PITimeMeasurer::elapsed_m() {
|
||||
return (PISystemTime::current(true) - t_st).toMilliseconds() - PISystemTests::time_elapsed_ns / 1.E+6;
|
||||
}
|
||||
|
||||
|
||||
double PITimeMeasurer::elapsed_s() {
|
||||
return (PISystemTime::current(true) - t_st).toSeconds() - PISystemTests::time_elapsed_ns / 1.E+9;
|
||||
}
|
||||
|
||||
|
||||
PISystemTime PITimeMeasurer::elapsed() {
|
||||
return (PISystemTime::current(true) - t_st);
|
||||
}
|
||||
328
src/core/pitime.h
Executable file
328
src/core/pitime.h
Executable file
@@ -0,0 +1,328 @@
|
||||
/*! \file pitime.h
|
||||
* \brief Time structs
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Time structs
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PITIME_H
|
||||
#define PITIME_H
|
||||
|
||||
#include <ctime>
|
||||
#include <csignal>
|
||||
#include "pistring.h"
|
||||
|
||||
#ifdef DOXYGEN
|
||||
//! \brief Sleep for "msecs" milliseconds
|
||||
void msleep(int msecs);
|
||||
#else
|
||||
# ifdef WINDOWS
|
||||
inline void msleep(int msecs) {Sleep(msecs);}
|
||||
# else
|
||||
inline void msleep(int msecs) {usleep(msecs * 1000);}
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*! \brief Precise sleep for "usecs" microseconds
|
||||
* \details This function consider \c "usleep" offset
|
||||
* on QNX/Linux/Mac, which is calculated with
|
||||
* \a pip_sys_test program. If there is correct
|
||||
* offset value in system config, this function
|
||||
* wait \b exactly "usecs" microseconds. */
|
||||
void piUSleep(int usecs); // on !Windows consider constant "usleep" offset
|
||||
|
||||
/*! \brief Precise sleep for "msecs" milliseconds
|
||||
* \details This function exec \a piUSleep (msecs * 1000). */
|
||||
inline void piMSleep(double msecs) {piUSleep(msecs * 1000);} // on !Windows consider constant "usleep" offset
|
||||
|
||||
/*! \brief Precise sleep for "secs" seconds
|
||||
* \details This function exec \a piUSleep (msecs * 1000000). */
|
||||
inline void piSleep(double secs) {piUSleep(secs * 1000000);} // on !Windows consider constant "usleep" offset
|
||||
|
||||
class PIP_EXPORT PISystemTime {
|
||||
public:
|
||||
|
||||
//! Contructs system time with s = ns = 0
|
||||
PISystemTime() {seconds = nanoseconds = 0;}
|
||||
|
||||
//! Contructs system time with s = "s" and ns = "ns"
|
||||
PISystemTime(long s, long ns) {seconds = s; nanoseconds = ns; checkOverflows();}
|
||||
|
||||
//! Contructs system time from another
|
||||
PISystemTime(const PISystemTime & t) {seconds = t.seconds; nanoseconds = t.nanoseconds;}
|
||||
|
||||
#ifdef WINDOWS
|
||||
PISystemTime(const FILETIME & t);
|
||||
#endif
|
||||
|
||||
//! Returns stored system time value in seconds
|
||||
double toSeconds() const {return double(seconds) + nanoseconds / 1.e+9;}
|
||||
|
||||
//! Returns stored system time value in milliseconds
|
||||
double toMilliseconds() const {return seconds * 1.e+3 + nanoseconds / 1.e+6;}
|
||||
|
||||
//! Returns stored system time value in microseconds
|
||||
double toMicroseconds() const {return seconds * 1.e+6 + nanoseconds / 1.e+3;}
|
||||
|
||||
//! Returns stored system time value in nanoseconds
|
||||
double toNanoseconds() const {return seconds * 1.e+9 + double(nanoseconds);}
|
||||
|
||||
|
||||
//! Add to stored system time "v" seconds
|
||||
PISystemTime & addSeconds(double v) {*this += fromSeconds(v); return *this;}
|
||||
|
||||
//! Add to stored system time "v" milliseconds
|
||||
PISystemTime & addMilliseconds(double v) {*this += fromMilliseconds(v); return *this;}
|
||||
|
||||
//! Add to stored system time "v" microseconds
|
||||
PISystemTime & addMicroseconds(double v) {*this += fromMicroseconds(v); return *this;}
|
||||
|
||||
//! Add to stored system time "v" nanoseconds
|
||||
PISystemTime & addNanoseconds(double v) {*this += fromNanoseconds(v); return *this;}
|
||||
|
||||
|
||||
//! Sleep for stored value. \warning Use this function to sleep for difference of system times or constructs system time.
|
||||
//! If you call this function on system time returned with \a PISystemTime::current() thread will be sleep almost forever.
|
||||
void sleep() {piUSleep(piFloord(toMicroseconds()));} // wait self value, useful to wait some dT = (t1 - t0)
|
||||
|
||||
|
||||
//! Returns copy of this system time with absolutely values of s and ns
|
||||
PISystemTime abs() const;
|
||||
|
||||
//! Returns sum of this system time with "t"
|
||||
PISystemTime operator +(const PISystemTime & t) const {PISystemTime tt(*this); tt.seconds += t.seconds; tt.nanoseconds += t.nanoseconds; tt.checkOverflows(); return tt;}
|
||||
|
||||
//! Returns difference between this system time and "t"
|
||||
PISystemTime operator -(const PISystemTime & t) const {PISystemTime tt(*this); tt.seconds -= t.seconds; tt.nanoseconds -= t.nanoseconds; tt.checkOverflows(); return tt;}
|
||||
|
||||
//! Returns multiplication between this system time and "t"
|
||||
PISystemTime operator *(const double & v) const {return fromMilliseconds(toMilliseconds() * v);}
|
||||
|
||||
//! Returns division between this system time and "t"
|
||||
PISystemTime operator /(const double & v) const {return fromMilliseconds(toMilliseconds() / v);}
|
||||
|
||||
//! Add to stored value system time "t"
|
||||
PISystemTime & operator +=(const PISystemTime & t) {seconds += t.seconds; nanoseconds += t.nanoseconds; checkOverflows(); return *this;}
|
||||
|
||||
//! Subtract from stored value system time "t"
|
||||
PISystemTime & operator -=(const PISystemTime & t) {seconds -= t.seconds; nanoseconds -= t.nanoseconds; checkOverflows(); return *this;}
|
||||
|
||||
//! Multiply stored value system time by "v"
|
||||
PISystemTime & operator *=(const double & v) {*this = fromMilliseconds(toMilliseconds() * v); return *this;}
|
||||
|
||||
//! Divide stored value system time by "v"
|
||||
PISystemTime & operator /=(const double & v) {*this = fromMilliseconds(toMilliseconds() / v); return *this;}
|
||||
|
||||
|
||||
//! Compare system times
|
||||
bool operator ==(const PISystemTime & t) const {return ((seconds == t.seconds) && (nanoseconds == t.nanoseconds));}
|
||||
|
||||
//! Compare system times
|
||||
bool operator !=(const PISystemTime & t) const {return ((seconds != t.seconds) || (nanoseconds != t.nanoseconds));}
|
||||
|
||||
//! Compare system times
|
||||
bool operator >(const PISystemTime & t) const {if (seconds == t.seconds) return nanoseconds > t.nanoseconds; return seconds > t.seconds;}
|
||||
|
||||
//! Compare system times
|
||||
bool operator <(const PISystemTime & t) const {if (seconds == t.seconds) return nanoseconds < t.nanoseconds; return seconds < t.seconds;}
|
||||
|
||||
//! Compare system times
|
||||
bool operator >=(const PISystemTime & t) const {if (seconds == t.seconds) return nanoseconds >= t.nanoseconds; return seconds >= t.seconds;}
|
||||
|
||||
//! Compare system times
|
||||
bool operator <=(const PISystemTime & t) const {if (seconds == t.seconds) return nanoseconds <= t.nanoseconds; return seconds <= t.seconds;}
|
||||
|
||||
|
||||
//! Contructs system time from seconds "v"
|
||||
static PISystemTime fromSeconds(double v) {long s = piFloord(v); return PISystemTime(s, (v - s) * 1000000000);}
|
||||
|
||||
//! Contructs system time from milliseconds "v"
|
||||
static PISystemTime fromMilliseconds(double v) {long s = piFloord(v / 1000.); return PISystemTime(s, (v / 1000. - s) * 1000000000);}
|
||||
|
||||
//! Contructs system time from microseconds "v"
|
||||
static PISystemTime fromMicroseconds(double v) {long s = piFloord(v / 1000000.); return PISystemTime(s, (v / 1000000. - s) * 1000000000);}
|
||||
|
||||
//! Contructs system time from nanoseconds "v"
|
||||
static PISystemTime fromNanoseconds(double v) {long s = piFloord(v / 1000000000.); return PISystemTime(s, (v / 1000000000. - s) * 1000000000);}
|
||||
|
||||
//! Returns current system time
|
||||
static PISystemTime current(bool precise_but_not_system = false);
|
||||
|
||||
//! Seconds of stored system time
|
||||
long seconds;
|
||||
|
||||
//! Nanoseconds of stored system time
|
||||
long nanoseconds;
|
||||
|
||||
private:
|
||||
void checkOverflows() {while (nanoseconds >= 1000000000) {nanoseconds -= 1000000000; seconds++;} while (nanoseconds < 0) {nanoseconds += 1000000000; seconds--;}}
|
||||
|
||||
};
|
||||
|
||||
//! \relatesalso PICout \relatesalso PIByteArray \brief Output operator to PICout
|
||||
inline PICout operator <<(PICout s, const PISystemTime & v) {s.space(); s.setControl(0, true); s << "(" << v.seconds << " s, " << v.nanoseconds << " ns)"; s.restoreControl(); return s;}
|
||||
|
||||
//! \relatesalso PISystemTime \relatesalso PIByteArray \brief Output operator to PIByteArray
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PISystemTime & v) {s << v.seconds << v.nanoseconds; return s;}
|
||||
|
||||
//! \relatesalso PISystemTime \relatesalso PIByteArray \brief Input operator from PIByteArray
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PISystemTime & v) {s >> v.seconds >> v.nanoseconds; return s;}
|
||||
|
||||
struct PIP_EXPORT PITime {
|
||||
PITime(int hours_ = 0, int minutes_ = 0, int seconds_ = 0, int milliseconds_ = 0): hours(hours_), minutes(minutes_), seconds(seconds_), milliseconds(milliseconds_) {;}
|
||||
int hours;
|
||||
int minutes;
|
||||
int seconds;
|
||||
int milliseconds;
|
||||
PIString toString(const PIString & format = "h:mm:ss") const;
|
||||
static PITime current();
|
||||
};
|
||||
PIP_EXPORT bool operator ==(const PITime & t0, const PITime & t1);
|
||||
PIP_EXPORT bool operator <(const PITime & t0, const PITime & t1);
|
||||
PIP_EXPORT bool operator >(const PITime & t0, const PITime & t1);
|
||||
inline bool operator !=(const PITime & t0, const PITime & t1) {return !(t0 == t1);}
|
||||
inline bool operator <=(const PITime & t0, const PITime & t1) {return !(t0 > t1);}
|
||||
inline bool operator >=(const PITime & t0, const PITime & t1) {return !(t0 < t1);}
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PITime & v) {s << v.hours << v.minutes << v.seconds << v.milliseconds; return s;}
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PITime & v) {s >> v.hours >> v.minutes >> v.seconds >> v.milliseconds; return s;}
|
||||
|
||||
struct PIP_EXPORT PIDate {
|
||||
PIDate(int year_ = 0, int month_ = 0, int day_ = 0): year(year_), month(month_), day(day_) {;}
|
||||
int year;
|
||||
int month;
|
||||
int day;
|
||||
PIString toString(const PIString & format = "d.MM.yyyy") const;
|
||||
static PIDate current();
|
||||
};
|
||||
PIP_EXPORT bool operator ==(const PIDate & t0, const PIDate & t1);
|
||||
PIP_EXPORT bool operator <(const PIDate & t0, const PIDate & t1);
|
||||
PIP_EXPORT bool operator >(const PIDate & t0, const PIDate & t1);
|
||||
inline bool operator !=(const PIDate & t0, const PIDate & t1) {return !(t0 == t1);}
|
||||
inline bool operator <=(const PIDate & t0, const PIDate & t1) {return !(t0 > t1);}
|
||||
inline bool operator >=(const PIDate & t0, const PIDate & t1) {return !(t0 < t1);}
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PIDate & v) {s << v.year << v.month << v.day; return s;}
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PIDate & v) {s >> v.year >> v.month >> v.day; return s;}
|
||||
|
||||
struct PIP_EXPORT PIDateTime {
|
||||
PIDateTime() {year = month = day = hours = minutes = seconds = milliseconds = 0;}
|
||||
PIDateTime(const PITime & time) {year = month = day = 0; hours = time.hours; minutes = time.minutes; seconds = time.seconds; milliseconds = time.milliseconds;}
|
||||
PIDateTime(const PIDate & date) {year = date.year; month = date.month; day = date.day; hours = minutes = seconds = milliseconds = 0;}
|
||||
PIDateTime(const PIDate & date, const PITime & time) {year = date.year; month = date.month; day = date.day; hours = time.hours; minutes = time.minutes; seconds = time.seconds; milliseconds = time.milliseconds;}
|
||||
#ifdef WINDOWS
|
||||
PIDateTime(FILETIME t);
|
||||
#endif
|
||||
int year;
|
||||
int month;
|
||||
int day;
|
||||
int hours;
|
||||
int minutes;
|
||||
int seconds;
|
||||
int milliseconds;
|
||||
PIDateTime normalized() const {return PIDateTime::fromSecondSinceEpoch(toSecondSinceEpoch());}
|
||||
void normalize() {*this = normalized();}
|
||||
PIString toString(const PIString & format = "h:mm:ss d.MM.yyyy") const;
|
||||
time_t toSecondSinceEpoch() const;
|
||||
PISystemTime toSystemTime() const {return PISystemTime(int(toSecondSinceEpoch()), milliseconds * 1000000);}
|
||||
PIDate date() const {return PIDate(year, month, day);}
|
||||
PITime time() const {return PITime(hours, minutes, seconds, milliseconds);}
|
||||
void setDate(const PIDate & d) {year = d.year; month = d.month; day = d.day;}
|
||||
void setTime(const PITime & t) {hours = t.hours; minutes = t.minutes; seconds = t.seconds; milliseconds = t.milliseconds;}
|
||||
void operator +=(const PIDateTime & d1) {year += d1.year; month += d1.month; day += d1.day; hours += d1.hours; minutes += d1.minutes; seconds += d1.seconds; normalize();}
|
||||
void operator -=(const PIDateTime & d1) {year -= d1.year; month -= d1.month; day -= d1.day; hours -= d1.hours; minutes -= d1.minutes; seconds -= d1.seconds; normalize();}
|
||||
static PIDateTime fromSecondSinceEpoch(const time_t sec);
|
||||
static PIDateTime fromSystemTime(const PISystemTime & st) {PIDateTime dt = fromSecondSinceEpoch(st.seconds); dt.milliseconds = piClampi(st.nanoseconds / 1000000, 0, 999); return dt;}
|
||||
static PIDateTime current();
|
||||
};
|
||||
inline PIDateTime operator +(const PIDateTime & d0, const PIDateTime & d1) {PIDateTime td = d0; td += d1; return td.normalized();}
|
||||
inline PIDateTime operator -(const PIDateTime & d0, const PIDateTime & d1) {PIDateTime td = d0; td -= d1; return td.normalized();}
|
||||
PIP_EXPORT bool operator ==(const PIDateTime & t0, const PIDateTime & t1);
|
||||
PIP_EXPORT bool operator <(const PIDateTime & t0, const PIDateTime & t1);
|
||||
PIP_EXPORT bool operator >(const PIDateTime & t0, const PIDateTime & t1);
|
||||
inline bool operator !=(const PIDateTime & t0, const PIDateTime & t1) {return !(t0 == t1);}
|
||||
inline bool operator <=(const PIDateTime & t0, const PIDateTime & t1) {return !(t0 > t1);}
|
||||
inline bool operator >=(const PIDateTime & t0, const PIDateTime & t1) {return !(t0 < t1);}
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PIDateTime & v) {s << v.year << v.month << v.day << v.hours << v.minutes << v.seconds << v.milliseconds; return s;}
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PIDateTime & v) {s >> v.year >> v.month >> v.day >> v.hours >> v.minutes >> v.seconds >> v.milliseconds; return s;}
|
||||
|
||||
DEPRECATED inline PITime currentTime() {return PITime::current();} // obsolete, use PITime::current() instead
|
||||
DEPRECATED inline PIDate currentDate() {return PIDate::current();} // obsolete, use PIDate::current() instead
|
||||
DEPRECATED inline PIDateTime currentDateTime() {return PIDateTime::current();} // obsolete, use PIDateTime::current() instead
|
||||
|
||||
//! \brief Returns current system time \deprecated Use \a PISystemTime::current() instead
|
||||
DEPRECATED inline PISystemTime currentSystemTime() {return PISystemTime::current();} // obsolete, use PISystemTime::current() instead
|
||||
DEPRECATED PIP_EXPORT PIString time2string(const PITime & time, const PIString & format = "h:mm:ss"); // obsolete, use PITime.toString() instead
|
||||
DEPRECATED PIP_EXPORT PIString date2string(const PIDate & date, const PIString & format = "d.MM.yyyy"); // obsolete, use PITime.toString() instead
|
||||
DEPRECATED PIP_EXPORT PIString datetime2string(const PIDateTime & datetime, const PIString & format = "h:mm:ss d.MM.yyyy"); // obsolete, use PIDateTime.toString() instead
|
||||
|
||||
|
||||
class PITimeMeasurer {
|
||||
public:
|
||||
PITimeMeasurer();
|
||||
|
||||
/** \brief Set internal time mark to current system time
|
||||
* \details This function used for set start time mark. Later
|
||||
* you can find out elapsed time from this time mark to any
|
||||
* moment of time with \a elapsed_s(), \a elapsed_m(),
|
||||
* \a elapsed_u() or \a elapsed_n() functions.
|
||||
* \sa \a elapsed_s(), \a elapsed_m(), \a elapsed_u(), \a elapsed_n() */
|
||||
void reset() {t_st = PISystemTime::current(true);}
|
||||
|
||||
//! \brief Returns nanoseconds elapsed from last \a reset() execution or from timer measurer creation.
|
||||
double elapsed_n();
|
||||
|
||||
//! \brief Returns microseconds elapsed from last \a reset() execution or from timer measurer creation.
|
||||
double elapsed_u();
|
||||
|
||||
//! \brief Returns milliseconds elapsed from last \a reset() execution or from timer measurer creation.
|
||||
double elapsed_m();
|
||||
|
||||
//! \brief Returns seconds elapsed from last \a reset() execution or from timer measurer creation.
|
||||
double elapsed_s();
|
||||
|
||||
//! \brief Returns PISystemTime elapsed from last \a reset() execution or from timer measurer creation.
|
||||
PISystemTime elapsed();
|
||||
|
||||
double reset_time_n() {return t_st.toNanoseconds();}
|
||||
double reset_time_u() {return t_st.toMicroseconds();}
|
||||
double reset_time_m() {return t_st.toMilliseconds();}
|
||||
double reset_time_s() {return t_st.toSeconds();}
|
||||
|
||||
//! \brief Returns time mark of last \a reset() execution or timer measurer creation.
|
||||
PISystemTime reset_time() {return t_st;}
|
||||
|
||||
//! \brief Returns nanoseconds representation of current system time.
|
||||
static double elapsed_system_n() {return PISystemTime::current(true).toNanoseconds();}
|
||||
|
||||
//! \brief Returns microseconds representation of current system time.
|
||||
static double elapsed_system_u() {return PISystemTime::current(true).toMicroseconds();}
|
||||
|
||||
//! \brief Returns milliseconds representation of current system time.
|
||||
static double elapsed_system_m() {return PISystemTime::current(true).toMilliseconds();}
|
||||
|
||||
//! \brief Returns seconds representation of current system time.
|
||||
static double elapsed_system_s() {return PISystemTime::current(true).toSeconds();}
|
||||
|
||||
//! \brief Returns time mark of current system time.
|
||||
static PISystemTime elapsed_system() {return PISystemTime::current(true);}
|
||||
|
||||
private:
|
||||
PISystemTime t_st, t_cur;
|
||||
|
||||
};
|
||||
|
||||
#endif // PITIME_H
|
||||
534
src/core/pivariant.cpp
Executable file
534
src/core/pivariant.cpp
Executable file
@@ -0,0 +1,534 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Variant type
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pivariant.h"
|
||||
|
||||
|
||||
/** \class PIVariant
|
||||
* \brief Variant type
|
||||
* \details
|
||||
* \section PIVariant_sec0 Synopsis
|
||||
* This class provides general type that can contains all standard types, some
|
||||
* PIP types or custom type. In case of standard types this class also provides
|
||||
* convertions between them.
|
||||
*
|
||||
* \section PIVariant_sec1 Usage
|
||||
* %PIVariant useful if you want pass many variables with different types in
|
||||
* single array, e.g.:
|
||||
* \code{cpp}
|
||||
* PIVector<PIVariant> array;
|
||||
* array << PIVariant(10) << PIVariant(1.61) << PIVariant(true) << PIVariant("0xFF");
|
||||
* piCout << array;
|
||||
* piForeachC (PIVariant & i, array)
|
||||
* piCout << i.toInt();
|
||||
* \endcode
|
||||
* Result:
|
||||
* \code{cpp}
|
||||
* {PIVariant(Int, 10), PIVariant(Double, 1,61), PIVariant(Bool, true), PIVariant(String, 0xFF)}
|
||||
* 10
|
||||
* 1
|
||||
* 1
|
||||
* 255
|
||||
* \endcode
|
||||
* */
|
||||
|
||||
|
||||
PIVariant::PIVariant() {
|
||||
type_ = PIVariant::Invalid;
|
||||
memset(_vraw, 0, __PIVARIANT_UNION_SIZE__);
|
||||
}
|
||||
|
||||
|
||||
PIVariant & PIVariant::operator =(const PIVariant & v) {
|
||||
type_ = v.type_;
|
||||
memcpy(_vraw, v._vraw, __PIVARIANT_UNION_SIZE__);
|
||||
_vbytearray = v._vbytearray;
|
||||
_vbitarray = v._vbitarray;
|
||||
_vstring = v._vstring;
|
||||
_vstringlist = v._vstringlist;
|
||||
_vcustom = v._vcustom;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
bool PIVariant::operator ==(const PIVariant & v) const {
|
||||
if (type_ != v.type_) return false;
|
||||
switch (type_) {
|
||||
case PIVariant::Bool:
|
||||
case PIVariant::Char:
|
||||
case PIVariant::UChar:
|
||||
case PIVariant::Short:
|
||||
case PIVariant::UShort:
|
||||
case PIVariant::Int:
|
||||
case PIVariant::UInt:
|
||||
case PIVariant::Long:
|
||||
case PIVariant::ULong: return _vint == v._vint;
|
||||
case PIVariant::LLong:
|
||||
case PIVariant::ULLong: return _vllong == v._vllong;
|
||||
case PIVariant::Float: return _vfloat == v._vfloat;
|
||||
case PIVariant::Double: return _vdouble == v._vdouble;
|
||||
case PIVariant::LDouble: return _vldouble == v._vldouble;
|
||||
case PIVariant::Complexd: return _vcomplexd == _vvcomplexd(v);
|
||||
case PIVariant::Complexld: return _vcomplexld == _vvcomplexld(v);
|
||||
case PIVariant::BitArray: return _vbitarray == v._vbitarray;
|
||||
case PIVariant::ByteArray: return _vbytearray == v._vbytearray;
|
||||
case PIVariant::String: return _vstring == v._vstring;
|
||||
case PIVariant::StringList: return _vstringlist == v._vstringlist;
|
||||
case PIVariant::Time: return _vtime == _vvtime(v);
|
||||
case PIVariant::Date: return _vdate == _vvdate(v);
|
||||
case PIVariant::DateTime: return _vdatetime == _vvdatetime(v);
|
||||
case PIVariant::SystemTime: return _vsystime == _vvsystime(v);
|
||||
default: break;
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
PIVariant::Type PIVariant::typeFromName(const PIString & tname) {
|
||||
PIString s = tname.trimmed().toLowerCase().replaceAll(" ", "");
|
||||
if (s == "bool" || s == "boolean") return PIVariant::Bool;
|
||||
if (s == "char" || s == "sbyte") return PIVariant::Char;
|
||||
if (s == "short" || s == "shortint" || s == "signedshort" || s == "signedshortint" || s == "sword") return PIVariant::Short;
|
||||
if (s == "int" || s == "signed" || s == "signedint") return PIVariant::Int;
|
||||
if (s == "long" || s == "longint" || s == "signedlong" || s == "signedlongint" || s == "sdword") return PIVariant::Long;
|
||||
if (s == "llong" || s == "longlong" || s == "longlongint" || s == "signedlonglong" || s == "signedlonglongint" || s == "sqword") return PIVariant::LLong;
|
||||
if (s == "uchar" || s == "byte") return PIVariant::UChar;
|
||||
if (s == "ushort" || s == "unsignedshort" || s == "unsignedshortint" || s == "word") return PIVariant::UShort;
|
||||
if (s == "uint" || s == "unsigned" || s == "unsignedint") return PIVariant::UInt;
|
||||
if (s == "ulong" || s == "unsignedlong" || s == "unsignedlongint" || s == "dword") return PIVariant::ULong;
|
||||
if (s == "ullong" || s == "unsignedlonglong" || s == "unsignedlonglongint" || s == "qword") return PIVariant::ULLong;
|
||||
if (s == "float") return PIVariant::Float;
|
||||
if (s == "double" || s == "real") return PIVariant::Double;
|
||||
if (s == "ldouble" || s == "longdouble") return PIVariant::LDouble;
|
||||
if (s == "complexd" || s == "complex<double>") return PIVariant::Complexd;
|
||||
if (s == "complexld" || s == "complex<ldouble>" || s == "complex<longdouble>") return PIVariant::Complexld;
|
||||
if (s == "pibitarray" || s == "bitarray") return PIVariant::BitArray;
|
||||
if (s == "pibytearray" || s == "bytearray" || s == "vector<uchar>" || s == "pivector<uchar>" || s == "vector<unsignedchar>" || s == "pivector<unsignedchar>" ||
|
||||
s == "vector<char>" || s == "pivector<char>") return PIVariant::ByteArray;
|
||||
if (s == "pistring" || s == "string") return PIVariant::String;
|
||||
if (s == "pistringlist" || s == "stringlist" || s == "vector<string>" || s == "vector<pistring>" || s == "pivector<string>" || s == "pivector<pistring>") return PIVariant::StringList;
|
||||
if (s == "pitime" || s == "time") return PIVariant::Time;
|
||||
if (s == "pidate" || s == "date") return PIVariant::Date;
|
||||
if (s == "pidatetime" || s == "datetime") return PIVariant::DateTime;
|
||||
if (s == "pisystemtime" || s == "systemtime") return PIVariant::SystemTime;
|
||||
return PIVariant::Invalid;
|
||||
}
|
||||
|
||||
|
||||
PIString PIVariant::typeName(PIVariant::Type type) {
|
||||
switch (type) {
|
||||
case PIVariant::Bool: return "Bool";
|
||||
case PIVariant::Char: return "Char";
|
||||
case PIVariant::UChar: return "UChar";
|
||||
case PIVariant::Short: return "Short";
|
||||
case PIVariant::UShort: return "UShort";
|
||||
case PIVariant::Int: return "Int";
|
||||
case PIVariant::UInt: return "UInt";
|
||||
case PIVariant::Long: return "Long";
|
||||
case PIVariant::ULong: return "ULong";
|
||||
case PIVariant::LLong: return "LLong";
|
||||
case PIVariant::ULLong: return "ULLong";
|
||||
case PIVariant::Float: return "Float";
|
||||
case PIVariant::Double: return "Double";
|
||||
case PIVariant::LDouble: return "LDouble";
|
||||
case PIVariant::Complexd: return "Complexd";
|
||||
case PIVariant::Complexld: return "Complexld";
|
||||
case PIVariant::BitArray: return "BitArray";
|
||||
case PIVariant::ByteArray: return "ByteArray";
|
||||
case PIVariant::String: return "String";
|
||||
case PIVariant::StringList: return "StringList";
|
||||
case PIVariant::Time: return "Time";
|
||||
case PIVariant::Date: return "Date";
|
||||
case PIVariant::DateTime: return "DateTime";
|
||||
case PIVariant::SystemTime: return "SystemTime";
|
||||
case PIVariant::Custom: return "Custom";
|
||||
default: break;
|
||||
}
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
|
||||
/** \brief Returns variant content as boolean
|
||||
* \details In case of numeric types returns \b true if value != 0. \n
|
||||
* In case of String type returns \a PIString::toBool(). \n
|
||||
* In case of StringList type returns \b false if string list is empty,
|
||||
* otherwise returns \a PIString::toBool() of first string. \n
|
||||
* In case of other types returns \b false. */
|
||||
bool PIVariant::toBool() const {
|
||||
switch (type_) {
|
||||
case PIVariant::Bool:
|
||||
case PIVariant::Char:
|
||||
case PIVariant::UChar:
|
||||
case PIVariant::Short:
|
||||
case PIVariant::UShort:
|
||||
case PIVariant::Int:
|
||||
case PIVariant::UInt:
|
||||
case PIVariant::Long:
|
||||
case PIVariant::ULong: return _vint != 0;
|
||||
case PIVariant::LLong:
|
||||
case PIVariant::ULLong: return _vllong != 0;
|
||||
case PIVariant::Float: return _vfloat != 0;
|
||||
case PIVariant::Double: return _vdouble != 0;
|
||||
case PIVariant::LDouble: return _vldouble != 0;
|
||||
case PIVariant::Complexd: return _vcomplexd.real() != 0;
|
||||
case PIVariant::Complexld: return _vcomplexld.real() != 0;
|
||||
case PIVariant::String: return _vstring.toBool();
|
||||
case PIVariant::StringList: if (_vstringlist.isEmpty()) return false; return _vstringlist.front().toBool();
|
||||
default: break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \brief Returns variant content as int
|
||||
* \details In case of numeric types returns integer value. \n
|
||||
* In case of String type returns \a PIString::toInt(). \n
|
||||
* In case of StringList type returns \b 0 if string list is empty,
|
||||
* otherwise returns \a PIString::toInt() of first string. \n
|
||||
* In case of other types returns \b 0. */
|
||||
int PIVariant::toInt() const {
|
||||
switch (type_) {
|
||||
case PIVariant::Bool:
|
||||
case PIVariant::Char:
|
||||
case PIVariant::UChar:
|
||||
case PIVariant::Short:
|
||||
case PIVariant::UShort:
|
||||
case PIVariant::Int:
|
||||
case PIVariant::UInt:
|
||||
case PIVariant::Long:
|
||||
case PIVariant::ULong: return _vint;
|
||||
case PIVariant::LLong:
|
||||
case PIVariant::ULLong: return _vllong;
|
||||
case PIVariant::Float: return _vfloat;
|
||||
case PIVariant::Double: return _vdouble;
|
||||
case PIVariant::LDouble: return _vldouble;
|
||||
case PIVariant::Complexd: return _vcomplexd.real();
|
||||
case PIVariant::Complexld: return _vcomplexld.real();
|
||||
case PIVariant::String: return _vstring.toInt();
|
||||
case PIVariant::StringList: if (_vstringlist.isEmpty()) return 0; return _vstringlist.front().toInt();
|
||||
default: break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/** \brief Returns variant content as long long
|
||||
* \details In case of numeric types returns integer value. \n
|
||||
* In case of String type returns \a PIString::toLLong(). \n
|
||||
* In case of StringList type returns \b 0L if string list is empty,
|
||||
* otherwise returns \a PIString::toLLong() of first string. \n
|
||||
* In case of other types returns \b 0L. */
|
||||
llong PIVariant::toLLong() const {
|
||||
switch (type_) {
|
||||
case PIVariant::Bool:
|
||||
case PIVariant::Char:
|
||||
case PIVariant::UChar:
|
||||
case PIVariant::Short:
|
||||
case PIVariant::UShort:
|
||||
case PIVariant::Int:
|
||||
case PIVariant::UInt:
|
||||
case PIVariant::Long:
|
||||
case PIVariant::ULong: return _vint;
|
||||
case PIVariant::LLong:
|
||||
case PIVariant::ULLong: return _vllong;
|
||||
case PIVariant::Float: return _vfloat;
|
||||
case PIVariant::Double: return _vdouble;
|
||||
case PIVariant::LDouble: return _vldouble;
|
||||
case PIVariant::Complexd: return _vcomplexd.real();
|
||||
case PIVariant::Complexld: return _vcomplexld.real();
|
||||
case PIVariant::String: return _vstring.toLLong();
|
||||
case PIVariant::StringList: if (_vstringlist.isEmpty()) return 0L; return _vstringlist.front().toLLong();
|
||||
default: break;
|
||||
}
|
||||
return 0L;
|
||||
}
|
||||
|
||||
|
||||
/** \brief Returns variant content as float
|
||||
* \details In case of numeric types returns float value. \n
|
||||
* In case of String type returns \a PIString::toFloat(). \n
|
||||
* In case of StringList type returns \b 0.f if string list is empty,
|
||||
* otherwise returns \a PIString::toFloat() of first string. \n
|
||||
* In case of other types returns \b 0.f. */
|
||||
float PIVariant::toFloat() const {
|
||||
switch (type_) {
|
||||
case PIVariant::Bool:
|
||||
case PIVariant::Char:
|
||||
case PIVariant::UChar:
|
||||
case PIVariant::Short:
|
||||
case PIVariant::UShort:
|
||||
case PIVariant::Int:
|
||||
case PIVariant::UInt:
|
||||
case PIVariant::Long:
|
||||
case PIVariant::ULong: return _vint;
|
||||
case PIVariant::LLong:
|
||||
case PIVariant::ULLong: return _vllong;
|
||||
case PIVariant::Float: return _vfloat;
|
||||
case PIVariant::Double: return _vdouble;
|
||||
case PIVariant::LDouble: return _vldouble;
|
||||
case PIVariant::Complexd: return _vcomplexd.real();
|
||||
case PIVariant::Complexld: return _vcomplexld.real();
|
||||
case PIVariant::String: return _vstring.toFloat();
|
||||
case PIVariant::StringList: if (_vstringlist.isEmpty()) return 0.f; return _vstringlist.front().toFloat();
|
||||
default: break;
|
||||
}
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
|
||||
/** \brief Returns variant content as double
|
||||
* \details In case of numeric types returns double value. \n
|
||||
* In case of String type returns \a PIString::toDouble(). \n
|
||||
* In case of StringList type returns \b 0. if string list is empty,
|
||||
* otherwise returns \a PIString::toDouble() of first string. \n
|
||||
* In case of other types returns \b 0.. */
|
||||
double PIVariant::toDouble() const {
|
||||
switch (type_) {
|
||||
case PIVariant::Bool:
|
||||
case PIVariant::Char:
|
||||
case PIVariant::UChar:
|
||||
case PIVariant::Short:
|
||||
case PIVariant::UShort:
|
||||
case PIVariant::Int:
|
||||
case PIVariant::UInt:
|
||||
case PIVariant::Long:
|
||||
case PIVariant::ULong: return _vint;
|
||||
case PIVariant::LLong:
|
||||
case PIVariant::ULLong: return _vllong;
|
||||
case PIVariant::Float: return _vfloat;
|
||||
case PIVariant::Double: return _vdouble;
|
||||
case PIVariant::LDouble: return _vldouble;
|
||||
case PIVariant::Complexd: return _vcomplexd.real();
|
||||
case PIVariant::Complexld: return _vcomplexld.real();
|
||||
case PIVariant::String: return _vstring.toDouble();
|
||||
case PIVariant::StringList: if (_vstringlist.isEmpty()) return 0.; return _vstringlist.front().toDouble();
|
||||
default: break;
|
||||
}
|
||||
return 0.;
|
||||
}
|
||||
|
||||
|
||||
/** \brief Returns variant content as long double
|
||||
* \details In case of numeric types returns long double value. \n
|
||||
* In case of String type returns \a PIString::toLDouble(). \n
|
||||
* In case of StringList type returns \b 0. if string list is empty,
|
||||
* otherwise returns \a PIString::toLDouble() of first string. \n
|
||||
* In case of other types returns \b 0.. */
|
||||
ldouble PIVariant::toLDouble() const {
|
||||
switch (type_) {
|
||||
case PIVariant::Bool:
|
||||
case PIVariant::Char:
|
||||
case PIVariant::UChar:
|
||||
case PIVariant::Short:
|
||||
case PIVariant::UShort:
|
||||
case PIVariant::Int:
|
||||
case PIVariant::UInt:
|
||||
case PIVariant::Long:
|
||||
case PIVariant::ULong: return _vint;
|
||||
case PIVariant::LLong:
|
||||
case PIVariant::ULLong: return _vllong;
|
||||
case PIVariant::Float: return _vfloat;
|
||||
case PIVariant::Double: return _vdouble;
|
||||
case PIVariant::LDouble: return _vldouble;
|
||||
case PIVariant::Complexd: return _vcomplexd.real();
|
||||
case PIVariant::Complexld: return _vcomplexld.real();
|
||||
case PIVariant::String: return _vstring.toLDouble();
|
||||
case PIVariant::StringList: if (_vstringlist.isEmpty()) return 0.; return _vstringlist.front().toLDouble();
|
||||
default: break;
|
||||
}
|
||||
return 0.;
|
||||
}
|
||||
|
||||
|
||||
/** \brief Returns variant content as complex
|
||||
* \details In case of numeric types returns complex value. \n
|
||||
* In case of String type returns \a PIString::toDouble(). \n
|
||||
* In case of StringList type returns \b 0. if string list is empty,
|
||||
* otherwise returns \a PIString::toDouble() of first string. \n
|
||||
* In case of other types returns \b 0.. */
|
||||
complexd PIVariant::toComplexd() const {
|
||||
switch (type_) {
|
||||
case PIVariant::Bool:
|
||||
case PIVariant::Char:
|
||||
case PIVariant::UChar:
|
||||
case PIVariant::Short:
|
||||
case PIVariant::UShort:
|
||||
case PIVariant::Int:
|
||||
case PIVariant::UInt:
|
||||
case PIVariant::Long:
|
||||
case PIVariant::ULong: return _vint;
|
||||
case PIVariant::LLong:
|
||||
case PIVariant::ULLong: return _vllong;
|
||||
case PIVariant::Float: return _vfloat;
|
||||
case PIVariant::Double: return _vdouble;
|
||||
case PIVariant::LDouble: return _vldouble;
|
||||
case PIVariant::Complexd: return _vcomplexd.real();
|
||||
case PIVariant::Complexld: return _vcomplexld.real();
|
||||
case PIVariant::String: return _vstring.toDouble();
|
||||
case PIVariant::StringList: if (_vstringlist.isEmpty()) return complexd_0; return _vstringlist.front().toDouble();
|
||||
default: break;
|
||||
}
|
||||
return complexd_0;
|
||||
}
|
||||
|
||||
|
||||
/** \brief Returns variant content as long complex
|
||||
* \details In case of numeric types returns long complex value. \n
|
||||
* In case of String type returns \a PIString::toLDouble(). \n
|
||||
* In case of StringList type returns \b 0. if string list is empty,
|
||||
* otherwise returns \a PIString::toLDouble() of first string. \n
|
||||
* In case of other types returns \b 0.. */
|
||||
complexld PIVariant::toComplexld() const {
|
||||
switch (type_) {
|
||||
case PIVariant::Bool:
|
||||
case PIVariant::Char:
|
||||
case PIVariant::UChar:
|
||||
case PIVariant::Short:
|
||||
case PIVariant::UShort:
|
||||
case PIVariant::Int:
|
||||
case PIVariant::UInt:
|
||||
case PIVariant::Long:
|
||||
case PIVariant::ULong: return _vint;
|
||||
case PIVariant::LLong:
|
||||
case PIVariant::ULLong: return _vllong;
|
||||
case PIVariant::Float: return _vfloat;
|
||||
case PIVariant::Double: return _vdouble;
|
||||
case PIVariant::LDouble: return _vldouble;
|
||||
case PIVariant::Complexd: return _vcomplexd.real();
|
||||
case PIVariant::Complexld: return _vcomplexld.real();
|
||||
case PIVariant::String: return _vstring.toLDouble();
|
||||
case PIVariant::StringList: if (_vstringlist.isEmpty()) return complexld_0; return _vstringlist.front().toLDouble();
|
||||
default: break;
|
||||
}
|
||||
return complexld_0;
|
||||
}
|
||||
|
||||
|
||||
/** \brief Returns variant content as time
|
||||
* \details In case of Time type returns time value. \n
|
||||
* In case of DateTime type returns time part of value. \n
|
||||
* In case of other types returns \a PITime(). */
|
||||
PITime PIVariant::toTime() const {
|
||||
if (type_ == PIVariant::Time) return _vtime;
|
||||
if (type_ == PIVariant::DateTime) return _vtime;
|
||||
return PITime();
|
||||
}
|
||||
|
||||
|
||||
/** \brief Returns variant content as date
|
||||
* \details In case of Date type returns date value. \n
|
||||
* In case of DateTime type returns date part of value. \n
|
||||
* In case of other types returns \a PIDate(). */
|
||||
PIDate PIVariant::toDate() const {
|
||||
if (type_ == PIVariant::Date) return _vdate;
|
||||
if (type_ == PIVariant::DateTime) return *((PIDate*)(&(_vdatetime.day)));
|
||||
return PIDate();
|
||||
}
|
||||
|
||||
|
||||
/** \brief Returns variant content as date and time
|
||||
* \details In case of Time type returns time value with null date. \n
|
||||
* In case of Date type returns date value with null time. \n
|
||||
* In case of DateTime type returns date and time. \n
|
||||
* In case of other types returns \a PIDateTime(). */
|
||||
PIDateTime PIVariant::toDateTime() const {
|
||||
if (type_ == PIVariant::DateTime) return _vdatetime;
|
||||
if (type_ == PIVariant::Time) return PIDateTime(_vtime);
|
||||
if (type_ == PIVariant::Date) return PIDateTime(_vdate);
|
||||
return PIDateTime();
|
||||
}
|
||||
|
||||
|
||||
/** \brief Returns variant content as system time
|
||||
* \details In case of SystemTime type returns system time. \n
|
||||
* In case of other types returns \a PISystemTime::fromSeconds() from
|
||||
* double value of variant content. */
|
||||
PISystemTime PIVariant::toSystemTime() const {
|
||||
if (type_ == PIVariant::SystemTime) return _vsystime;
|
||||
return PISystemTime::fromSeconds(toDouble());
|
||||
}
|
||||
|
||||
|
||||
/** \brief Returns variant content as string
|
||||
* \details In case of numeric types returns \a PIString::fromNumber(). \n
|
||||
* In case of String type returns string value. \n
|
||||
* In case of StringList type returns joined string ("(" + PIStringList::join("; ") + ")"). \n
|
||||
* In case of BitArray or ByteArray types returns number of bits/bytes. \n
|
||||
* In case of Time, Date or DateTime types returns toString() of this values. \n
|
||||
* In case of SystemTime types returns second and nanoseconds of time
|
||||
* ("(PISystemTime::seconds s, PISystemTime::nanoseconds ns)"). \n
|
||||
* In case of other types returns \b "". */
|
||||
PIString PIVariant::toString() const {
|
||||
switch (type_) {
|
||||
case PIVariant::Bool: return _vint == 0 ? "false" : "true";
|
||||
case PIVariant::Char:
|
||||
case PIVariant::UChar:
|
||||
case PIVariant::Short:
|
||||
case PIVariant::UShort:
|
||||
case PIVariant::Int:
|
||||
case PIVariant::UInt:
|
||||
case PIVariant::Long:
|
||||
case PIVariant::ULong: return PIString::fromNumber(_vint);
|
||||
case PIVariant::LLong:
|
||||
case PIVariant::ULLong: return PIString::fromNumber(_vllong);
|
||||
case PIVariant::Float: return PIString::fromNumber(_vfloat);
|
||||
case PIVariant::Double: return PIString::fromNumber(_vdouble);
|
||||
case PIVariant::LDouble: return PIString::fromNumber(_vldouble);
|
||||
case PIVariant::Complexd: return "(" + PIString::fromNumber(_vcomplexd.real()) + "; " + PIString::fromNumber(_vcomplexd.imag()) + ")";
|
||||
case PIVariant::Complexld: return "(" + PIString::fromNumber(_vcomplexld.real()) + "; " + PIString::fromNumber(_vcomplexld.imag()) + ")";
|
||||
case PIVariant::BitArray: return PIString::fromNumber(_vbitarray.bitSize()) + " bits";
|
||||
case PIVariant::ByteArray: return _vbytearray.toString();
|
||||
case PIVariant::String: return _vstring;
|
||||
case PIVariant::StringList: return "(" + _vstringlist.join("; ") + ")";
|
||||
case PIVariant::Time: return _vtime.toString();
|
||||
case PIVariant::Date: return _vdate.toString();
|
||||
case PIVariant::DateTime: return _vdatetime.toString();
|
||||
case PIVariant::SystemTime: return "(" + PIString::fromNumber(_vsystime.seconds) + " s, " + PIString::fromNumber(_vsystime.nanoseconds) + " ns)";
|
||||
default: break;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
/** \brief Returns variant content as strings list
|
||||
* \details In case of StringList type returns strings list value. \n
|
||||
* In case of other types returns \a PIStringList with one string value of variant content. */
|
||||
PIStringList PIVariant::toStringList() const {
|
||||
if (type_ == PIVariant::StringList) return _vstringlist;
|
||||
return PIStringList(toString());
|
||||
}
|
||||
|
||||
|
||||
/** \brief Returns variant content as bit array
|
||||
* \details In case of BitArray type returns bit array value. \n
|
||||
* In case of other types returns \a PIBitArray from \a toLLong() value. */
|
||||
PIBitArray PIVariant::toBitArray() const {
|
||||
if (type_ == PIVariant::BitArray) return _vbitarray;
|
||||
return PIBitArray(ullong(toLLong()));
|
||||
}
|
||||
|
||||
|
||||
/** \brief Returns variant content as byte array
|
||||
* \details In case of ByteArray type returns byte array value. \n
|
||||
* In case of other types returns empty \a PIByteArray. */
|
||||
PIByteArray PIVariant::toByteArray() const {
|
||||
if (type_ == PIVariant::ByteArray) return _vbytearray;
|
||||
return PIByteArray();
|
||||
}
|
||||
461
src/core/pivariant.h
Executable file
461
src/core/pivariant.h
Executable file
@@ -0,0 +1,461 @@
|
||||
/*! \file pivariant.h
|
||||
* \brief Variant type
|
||||
*
|
||||
* This file declares PIVariant
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Variant type
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIVARIANT_H
|
||||
#define PIVARIANT_H
|
||||
|
||||
#include "pistring.h"
|
||||
#include "pibitarray.h"
|
||||
#include "pitime.h"
|
||||
#include "pimath.h"
|
||||
|
||||
#define __PIVARIANT_UNION_SIZE__ 32
|
||||
|
||||
#define _vcomplexd (*((complexd*)_vraw))
|
||||
#define _vcomplexld (*((complexld*)_vraw))
|
||||
#define _vtime (*((PITime*)_vraw))
|
||||
#define _vdate (*((PIDate*)_vraw))
|
||||
#define _vdatetime (*((PIDateTime*)_vraw))
|
||||
#define _vsystime (*((PISystemTime*)_vraw))
|
||||
|
||||
#define _vvcomplexd(v) (*((complexd*)v._vraw))
|
||||
#define _vvcomplexld(v) (*((complexld*)v._vraw))
|
||||
#define _vvtime(v) (*((PITime*)v._vraw))
|
||||
#define _vvdate(v) (*((PIDate*)v._vraw))
|
||||
#define _vvdatetime(v) (*((PIDateTime*)v._vraw))
|
||||
#define _vvsystime(v) (*((PISystemTime*)v._vraw))
|
||||
|
||||
class PIP_EXPORT PIVariant {
|
||||
friend PICout operator <<(PICout s, const PIVariant & v);
|
||||
public:
|
||||
|
||||
//! Type of %PIVariant content
|
||||
enum Type {
|
||||
Invalid /** Invalid type , default type of empty contructor */ = 0 ,
|
||||
Bool /** bool */ ,
|
||||
Char /** char */ ,
|
||||
UChar /** uchar */ ,
|
||||
Short /** short */ ,
|
||||
UShort /** ushort */ ,
|
||||
Int /** int */ ,
|
||||
UInt /** uint */ ,
|
||||
Long /** long */ ,
|
||||
ULong /** ulong */ ,
|
||||
LLong /** llong */ ,
|
||||
ULLong /** ullong */ ,
|
||||
Float /** float */ ,
|
||||
Double /** double */ ,
|
||||
LDouble /** ldouble */ ,
|
||||
Complexd /** complexd */ ,
|
||||
Complexld /** complexld */ ,
|
||||
BitArray /** PIBitArray */ ,
|
||||
ByteArray /** PIByteArray */ ,
|
||||
String /** PIString */ ,
|
||||
StringList /** PIStringList */ ,
|
||||
Time /** PITime */ ,
|
||||
Date /** PIDate */ ,
|
||||
DateTime /** PIDateTime */ ,
|
||||
SystemTime /** PISystemTime */ ,
|
||||
Custom /** Custom */ = 0xFF
|
||||
};
|
||||
|
||||
//! Empty constructor, \a type() will be set to \a Invalid
|
||||
PIVariant();
|
||||
|
||||
//! Constructs variant from string
|
||||
PIVariant(const char * v) {setValue(PIString(v));}
|
||||
|
||||
//! Constructs variant from boolean
|
||||
PIVariant(const bool v) {setValue(v);}
|
||||
|
||||
//! Constructs variant from char
|
||||
PIVariant(const char v) {setValue(v);}
|
||||
|
||||
//! Constructs variant from integer
|
||||
PIVariant(const uchar v) {setValue(v);}
|
||||
|
||||
//! Constructs variant from integer
|
||||
PIVariant(const short v) {setValue(v);}
|
||||
|
||||
//! Constructs variant from integer
|
||||
PIVariant(const ushort v) {setValue(v);}
|
||||
|
||||
//! Constructs variant from integer
|
||||
PIVariant(const int & v) {setValue(v);}
|
||||
|
||||
//! Constructs variant from integer
|
||||
PIVariant(const uint & v) {setValue(v);}
|
||||
|
||||
//! Constructs variant from integer
|
||||
PIVariant(const long & v) {setValue(v);}
|
||||
|
||||
//! Constructs variant from integer
|
||||
PIVariant(const ulong & v) {setValue(v);}
|
||||
|
||||
//! Constructs variant from integer
|
||||
PIVariant(const llong & v) {setValue(v);}
|
||||
|
||||
//! Constructs variant from integer
|
||||
PIVariant(const ullong & v) {setValue(v);}
|
||||
|
||||
//! Constructs variant from float
|
||||
PIVariant(const float & v) {setValue(v);}
|
||||
|
||||
//! Constructs variant from double
|
||||
PIVariant(const double & v) {setValue(v);}
|
||||
|
||||
//! Constructs variant from long double
|
||||
PIVariant(const ldouble & v) {setValue(v);}
|
||||
|
||||
//! Constructs variant from complex
|
||||
PIVariant(const complexd & v) {setValue(v);}
|
||||
|
||||
//! Constructs variant from complex
|
||||
PIVariant(const complexld & v) {setValue(v);}
|
||||
|
||||
//! Constructs variant from bit array
|
||||
PIVariant(const PIBitArray & v) {setValue(v);}
|
||||
|
||||
//! Constructs variant from byte array
|
||||
PIVariant(const PIByteArray & v) {setValue(v);}
|
||||
|
||||
//! Constructs variant from string
|
||||
PIVariant(const PIString & v) {setValue(v);}
|
||||
|
||||
//! Constructs variant from strings list
|
||||
PIVariant(const PIStringList & v) {setValue(v);}
|
||||
|
||||
//! Constructs variant from time
|
||||
PIVariant(const PITime & v) {setValue(v);}
|
||||
|
||||
//! Constructs variant from date
|
||||
PIVariant(const PIDate & v) {setValue(v);}
|
||||
|
||||
//! Constructs variant from date and time
|
||||
PIVariant(const PIDateTime & v) {setValue(v);}
|
||||
|
||||
//! Constructs variant from system time
|
||||
PIVariant(const PISystemTime & v) {setValue(v);}
|
||||
|
||||
|
||||
//! Set variant content and type to string
|
||||
void setValue(const char * v) {setValue(PIString(v));}
|
||||
|
||||
//! Set variant content and type to boolean
|
||||
void setValue(const bool v) {type_ = PIVariant::Bool; _vint = (v ? 1 : 0);}
|
||||
|
||||
//! Set variant content and type to char
|
||||
void setValue(const char v) {type_ = PIVariant::Char; _vint = v;}
|
||||
|
||||
//! Set variant content and type to integer
|
||||
void setValue(const uchar v) {type_ = PIVariant::UChar; _vint = v;}
|
||||
|
||||
//! Set variant content and type to integer
|
||||
void setValue(const short v) {type_ = PIVariant::Short; _vint = v;}
|
||||
|
||||
//! Set variant content and type to integer
|
||||
void setValue(const ushort v) {type_ = PIVariant::UShort; _vint = v;}
|
||||
|
||||
//! Set variant content and type to integer
|
||||
void setValue(const int & v) {type_ = PIVariant::Int; _vint = v;}
|
||||
|
||||
//! Set variant content and type to integer
|
||||
void setValue(const uint & v) {type_ = PIVariant::UInt; _vint = v;}
|
||||
|
||||
//! Set variant content and type to integer
|
||||
void setValue(const long & v) {type_ = PIVariant::Long; _vint = v;}
|
||||
|
||||
//! Set variant content and type to integer
|
||||
void setValue(const ulong & v) {type_ = PIVariant::ULong; _vint = v;}
|
||||
|
||||
//! Set variant content and type to integer
|
||||
void setValue(const llong & v) {type_ = PIVariant::LLong; _vllong = v;}
|
||||
|
||||
//! Set variant content and type to integer
|
||||
void setValue(const ullong & v) {type_ = PIVariant::ULLong; _vllong = v;}
|
||||
|
||||
//! Set variant content and type to float
|
||||
void setValue(const float & v) {type_ = PIVariant::Float; _vfloat = v;}
|
||||
|
||||
//! Set variant content and type to double
|
||||
void setValue(const double & v) {type_ = PIVariant::Double; _vdouble = v;}
|
||||
|
||||
//! Set variant content and type to long double
|
||||
void setValue(const ldouble & v) {type_ = PIVariant::LDouble; _vldouble = v;}
|
||||
|
||||
//! Set variant content and type to complex
|
||||
void setValue(const complexd & v) {type_ = PIVariant::Complexd; _vcomplexd = v;}
|
||||
|
||||
//! Set variant content and type to complex
|
||||
void setValue(const complexld & v) {type_ = PIVariant::Complexld; _vcomplexld = v;}
|
||||
|
||||
//! Set variant content and type to bit array
|
||||
void setValue(const PIBitArray & v) {type_ = PIVariant::BitArray; _vbitarray = v;}
|
||||
|
||||
//! Set variant content and type to byte array
|
||||
void setValue(const PIByteArray & v) {type_ = PIVariant::ByteArray; _vbytearray = v;}
|
||||
|
||||
//! Set variant content and type to string
|
||||
void setValue(const PIString & v) {type_ = PIVariant::String; _vstring = v;}
|
||||
|
||||
//! Set variant content and type to strings list
|
||||
void setValue(const PIStringList & v) {type_ = PIVariant::StringList; _vstringlist = v;}
|
||||
|
||||
//! Set variant content and type to time
|
||||
void setValue(const PITime & v) {type_ = PIVariant::Time; _vtime = v;}
|
||||
|
||||
//! Set variant content and type to date
|
||||
void setValue(const PIDate & v) {type_ = PIVariant::Date; _vdate = v;}
|
||||
|
||||
//! Set variant content and type to date and time
|
||||
void setValue(const PIDateTime & v) {type_ = PIVariant::DateTime; _vdatetime = v;}
|
||||
|
||||
//! Set variant content and type to system time
|
||||
void setValue(const PISystemTime & v) {type_ = PIVariant::SystemTime; _vsystime = v;}
|
||||
|
||||
|
||||
bool toBool() const;
|
||||
int toInt() const;
|
||||
llong toLLong() const;
|
||||
float toFloat() const;
|
||||
double toDouble() const;
|
||||
ldouble toLDouble() const;
|
||||
complexd toComplexd() const;
|
||||
complexld toComplexld() const;
|
||||
PITime toTime() const;
|
||||
PIDate toDate() const;
|
||||
PIDateTime toDateTime() const;
|
||||
PISystemTime toSystemTime() const;
|
||||
PIString toString() const;
|
||||
PIStringList toStringList() const;
|
||||
PIBitArray toBitArray() const;
|
||||
PIByteArray toByteArray() const;
|
||||
|
||||
|
||||
/** \brief Returns variant content as custom type
|
||||
* \details In case of known types this function equivalent \a to<Type> function. \n
|
||||
* Otherwise returns content as type T. */
|
||||
template<typename T>
|
||||
T toValue() const {if (_vcustom.size() != sizeof(T)) return T(); return *((T*)_vcustom.data());}
|
||||
/*
|
||||
operator bool() const {return toBool();}
|
||||
operator char() const {return toInt();}
|
||||
operator uchar() const {return toInt();}
|
||||
operator short() const {return toInt();}
|
||||
operator ushort() const {return toInt();}
|
||||
operator int() const {return toInt();}
|
||||
operator uint() const {return toInt();}
|
||||
operator long() const {return toInt();}
|
||||
operator ulong() const {return toInt();}
|
||||
operator llong() const {return toLLong();}
|
||||
operator ullong() const {return (ullong)toLLong();}
|
||||
operator float() const {return toFloat();}
|
||||
operator double() const {return toDouble();}
|
||||
operator ldouble() const {return toLDouble();}
|
||||
operator complexd() const {return toComplexd();}
|
||||
operator complexld() const {return toComplexld();}
|
||||
operator PITime() const {return toTime();}
|
||||
operator PIDate() const {return toDate();}
|
||||
operator PIDateTime() const {return toDateTime();}
|
||||
operator PIString() const {return toString();}
|
||||
operator PIStringList() const {return toStringList();}
|
||||
operator PIBitArray() const {return toBitArray();}
|
||||
operator PIByteArray() const {return toByteArray();}
|
||||
operator const char*() const {return toString().data();}
|
||||
operator void*() const {return (void*)(toLLong());}
|
||||
*/
|
||||
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const PIVariant & v);
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const char * v) {setValue(PIString(v)); return *this;}
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const bool v) {setValue(v); return *this;}
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const char v) {setValue(v); return *this;}
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const uchar v) {setValue(v); return *this;}
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const short v) {setValue(v); return *this;}
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const ushort v) {setValue(v); return *this;}
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const int & v) {setValue(v); return *this;}
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const uint & v) {setValue(v); return *this;}
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const long & v) {setValue(v); return *this;}
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const ulong & v) {setValue(v); return *this;}
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const llong & v) {setValue(v); return *this;}
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const ullong & v) {setValue(v); return *this;}
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const float & v) {setValue(v); return *this;}
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const double & v) {setValue(v); return *this;}
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const ldouble & v) {setValue(v); return *this;}
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const complexd & v) {setValue(v); return *this;}
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const complexld & v) {setValue(v); return *this;}
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const PIBitArray & v) {setValue(v); return *this;}
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const PIByteArray & v) {setValue(v); return *this;}
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const PIString & v) {setValue(v); return *this;}
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const PIStringList & v) {setValue(v); return *this;}
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const PITime & v) {setValue(v); return *this;}
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const PIDate & v) {setValue(v); return *this;}
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const PIDateTime & v) {setValue(v); return *this;}
|
||||
//! Assign operator
|
||||
PIVariant & operator =(const PISystemTime & v) {setValue(v); return *this;}
|
||||
|
||||
|
||||
//! Compare operator
|
||||
bool operator ==(const PIVariant & v) const;
|
||||
//! Compare operator
|
||||
bool operator !=(const PIVariant & v) const {return !(*this == v);}
|
||||
|
||||
|
||||
//! Returns type of variant content
|
||||
PIVariant::Type type() const {return type_;}
|
||||
|
||||
//! Returns type name of variant content
|
||||
PIString typeName() const {return typeName(type_);}
|
||||
|
||||
|
||||
//! Returns \b true if type is not Invalid
|
||||
bool isValid() const {return type_ != PIVariant::Invalid;}
|
||||
|
||||
|
||||
/** \brief Returns new variant from custom type
|
||||
* \details In case of known types this function equivalent \a PIVariant(T) constructors. \n
|
||||
* Otherwise returns variant with content \a v and type Custom. */
|
||||
template <typename T>
|
||||
static PIVariant fromValue(const T & v) {PIVariant ret; ret._vcustom.resize(sizeof(T)); new((T*)(ret._vcustom.data()))T(v); ret.type_ = PIVariant::Custom; return ret;}
|
||||
|
||||
|
||||
//! Returns type from name
|
||||
static PIVariant::Type typeFromName(const PIString & tname);
|
||||
|
||||
//! Returns type name
|
||||
static PIString typeName(PIVariant::Type type);
|
||||
|
||||
private:
|
||||
void destroy() {_vcustom.clear();}
|
||||
|
||||
union {
|
||||
int _vint;
|
||||
llong _vllong;
|
||||
float _vfloat;
|
||||
double _vdouble;
|
||||
ldouble _vldouble;
|
||||
uchar _vraw[__PIVARIANT_UNION_SIZE__];
|
||||
/*complexd _vcomplexd;
|
||||
complexld _vcomplexld;
|
||||
PITime _vtime;
|
||||
PIDate _vdate;
|
||||
PIDateTime _vdatetime;
|
||||
PISystemTime _vsystime;*/
|
||||
};
|
||||
PIBitArray _vbitarray;
|
||||
PIByteArray _vbytearray;
|
||||
PIString _vstring;
|
||||
PIStringList _vstringlist;
|
||||
PIByteArray _vcustom;
|
||||
PIVariant::Type type_;
|
||||
|
||||
};
|
||||
|
||||
template<> inline bool PIVariant::toValue() const {return toBool();}
|
||||
template<> inline char PIVariant::toValue() const {return (char)toInt();}
|
||||
template<> inline uchar PIVariant::toValue() const {return (uchar)toInt();}
|
||||
template<> inline short PIVariant::toValue() const {return (short)toInt();}
|
||||
template<> inline ushort PIVariant::toValue() const {return (ushort)toInt();}
|
||||
template<> inline int PIVariant::toValue() const {return toInt();}
|
||||
template<> inline uint PIVariant::toValue() const {return (uint)toInt();}
|
||||
template<> inline long PIVariant::toValue() const {return (long)toInt();}
|
||||
template<> inline ulong PIVariant::toValue() const {return (ulong)toInt();}
|
||||
template<> inline llong PIVariant::toValue() const {return toLLong();}
|
||||
template<> inline ullong PIVariant::toValue() const {return (ullong)toLLong();}
|
||||
template<> inline float PIVariant::toValue() const {return toFloat();}
|
||||
template<> inline double PIVariant::toValue() const {return toDouble();}
|
||||
template<> inline ldouble PIVariant::toValue() const {return toLDouble();}
|
||||
template<> inline complexd PIVariant::toValue() const {return toComplexd();}
|
||||
template<> inline complexld PIVariant::toValue() const {return toComplexld();}
|
||||
template<> inline void* PIVariant::toValue() const {return (void*)toLLong();}
|
||||
template<> inline const char* PIVariant::toValue() const {return toString().data();}
|
||||
template<> inline PITime PIVariant::toValue() const {return toTime();}
|
||||
template<> inline PIDate PIVariant::toValue() const {return toDate();}
|
||||
template<> inline PIDateTime PIVariant::toValue() const {return toDateTime();}
|
||||
template<> inline PIString PIVariant::toValue() const {return toString();}
|
||||
template<> inline PIStringList PIVariant::toValue() const {return toStringList();}
|
||||
template<> inline PIBitArray PIVariant::toValue() const {return toBitArray();}
|
||||
template<> inline PIByteArray PIVariant::toValue() const {return toByteArray();}
|
||||
|
||||
//template<> inline PIVariant PIVariant::fromValue(const char * v) {return PIVariant(PIString(v));}
|
||||
template<> inline PIVariant PIVariant::fromValue(const bool & v) {return PIVariant(v);}
|
||||
template<> inline PIVariant PIVariant::fromValue(const char & v) {return PIVariant(v);}
|
||||
template<> inline PIVariant PIVariant::fromValue(const uchar & v) {return PIVariant(v);}
|
||||
template<> inline PIVariant PIVariant::fromValue(const short & v) {return PIVariant(v);}
|
||||
template<> inline PIVariant PIVariant::fromValue(const ushort & v) {return PIVariant(v);}
|
||||
template<> inline PIVariant PIVariant::fromValue(const int & v) {return PIVariant(v);}
|
||||
template<> inline PIVariant PIVariant::fromValue(const uint & v) {return PIVariant(v);}
|
||||
template<> inline PIVariant PIVariant::fromValue(const long & v) {return PIVariant(v);}
|
||||
template<> inline PIVariant PIVariant::fromValue(const ulong & v) {return PIVariant(v);}
|
||||
template<> inline PIVariant PIVariant::fromValue(const llong & v) {return PIVariant(v);}
|
||||
template<> inline PIVariant PIVariant::fromValue(const ullong & v) {return PIVariant(v);}
|
||||
template<> inline PIVariant PIVariant::fromValue(const float & v) {return PIVariant(v);}
|
||||
template<> inline PIVariant PIVariant::fromValue(const double & v) {return PIVariant(v);}
|
||||
template<> inline PIVariant PIVariant::fromValue(const ldouble & v) {return PIVariant(v);}
|
||||
template<> inline PIVariant PIVariant::fromValue(const complexd & v) {return PIVariant(v);}
|
||||
template<> inline PIVariant PIVariant::fromValue(const complexld & v) {return PIVariant(v);}
|
||||
template<> inline PIVariant PIVariant::fromValue(const PIBitArray & v) {return PIVariant(v);}
|
||||
template<> inline PIVariant PIVariant::fromValue(const PIByteArray & v) {return PIVariant(v);}
|
||||
template<> inline PIVariant PIVariant::fromValue(const PIString & v) {return PIVariant(v);}
|
||||
template<> inline PIVariant PIVariant::fromValue(const PIStringList & v) {return PIVariant(v);}
|
||||
template<> inline PIVariant PIVariant::fromValue(const PITime & v) {return PIVariant(v);}
|
||||
template<> inline PIVariant PIVariant::fromValue(const PIDate & v) {return PIVariant(v);}
|
||||
template<> inline PIVariant PIVariant::fromValue(const PIDateTime & v) {return PIVariant(v);}
|
||||
template<> inline PIVariant PIVariant::fromValue(const PISystemTime & v) {return PIVariant(v);}
|
||||
|
||||
inline PICout operator <<(PICout s, const PIVariant & v) {
|
||||
s.space(); s.setControl(0, true);
|
||||
s << "PIVariant(" << PIVariant::typeName(v.type()) << ", ";
|
||||
if (v.type() == PIVariant::Custom) s << v._vcustom.size() << " bytes";
|
||||
else s << v.toString();
|
||||
s << ")";
|
||||
s.restoreControl(); return s;
|
||||
}
|
||||
|
||||
|
||||
#endif // PIVARIANT_H
|
||||
524
src/io/pibinarylog.cpp
Normal file
524
src/io/pibinarylog.cpp
Normal file
@@ -0,0 +1,524 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Class for write binary data to logfile, and read or playback this data
|
||||
Copyright (C) 2014 Andrey Bychkov work.a.b@yandex.ru
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pibinarylog.h"
|
||||
#include "pidir.h"
|
||||
|
||||
/*! \class PIBinaryLog
|
||||
* \brief Class for read and write binary data to logfile, and playback this data in realtime, or custom speed
|
||||
*
|
||||
* \section PIBinaryLog_sec0 Synopsis
|
||||
* Binary Log is a file with simple header, where you can read and write some binary data.
|
||||
* Any written data include special header with ID, size and timestamp.
|
||||
* This header provides separation different messages from the one file by choosing different IDs.
|
||||
* With \a filterID or special functions, like \a readBinLog() you can choose IDs what you want to read.
|
||||
* With function \a writeBinLog() or \a setDefaultID() you can choose ID that mark you data.
|
||||
* By default ID = 1, and \a filterID is empty, that mean you read any ID without filtering.
|
||||
* ThreadedRead provide you playback data, with delay that you write data.
|
||||
* You can choose different playbak modes by set \a PlayMode.
|
||||
*
|
||||
* \section PIBinaryLog_sec1 Basic usage
|
||||
* This class provide all functions of \a PIIODevice, such \a open(), \a close(),
|
||||
* \a read() ,\a write(), and threaded read/write.
|
||||
* function \a setLogDir() need to set directory for BinLog files
|
||||
* function \a createNewFile() need to create new binlog file
|
||||
* function \a restart() need start from the begining of binlog file
|
||||
*
|
||||
*/
|
||||
|
||||
REGISTER_DEVICE(PIBinaryLog)
|
||||
|
||||
PIBinaryLog::PIBinaryLog() {
|
||||
setThreadedReadBufferSize(65536);
|
||||
is_started = is_indexed = false;
|
||||
current_index = -1;
|
||||
setPlaySpeed(1.);
|
||||
setDefaultID(1);
|
||||
setPlaySpeed(1.0);
|
||||
setPlayDelay(PISystemTime::fromSeconds(1.0));
|
||||
setPlayRealTime();
|
||||
setSplitTime(PISystemTime(600, 0));
|
||||
setSplitRecordCount(1000);
|
||||
setSplitFileSize(0xFFFFFF);
|
||||
setSplitMode(SplitNone);
|
||||
setLogDir(PIString());
|
||||
setFilePrefix(PIString());
|
||||
setRapidStart(false);
|
||||
file.setName("__S__PIBinaryLog::file");
|
||||
}
|
||||
|
||||
|
||||
bool PIBinaryLog::openDevice() {
|
||||
lastrecord.timestamp = PISystemTime();
|
||||
lastrecord.id = 0;
|
||||
write_count = 0;
|
||||
is_started = false;
|
||||
is_thread_ok = true;
|
||||
is_indexed = false;
|
||||
index.clear();
|
||||
index_pos.clear();
|
||||
if (mode_ == ReadWrite) {
|
||||
piCoutObj << "Error: ReadWrite mode not supported, use WriteOnly or ReadOnly";
|
||||
return false;
|
||||
}
|
||||
if (!file.open(path(), mode_)) {
|
||||
piCoutObj << "Error: Can't open file" << path();
|
||||
return false;
|
||||
}
|
||||
setName(path());
|
||||
if (mode_ == WriteOnly) {
|
||||
file.clear();
|
||||
if (!writeFileHeader()) {
|
||||
piCoutObj << "Error: Can't write binlog file header" << path();
|
||||
return false;
|
||||
}
|
||||
is_started = true;
|
||||
}
|
||||
if (mode_ == ReadOnly) {
|
||||
if (file.isEmpty()) {
|
||||
piCoutObj << "Error: File is null" << path();
|
||||
fileError();
|
||||
return false;
|
||||
}
|
||||
if (!checkFileHeader()) {
|
||||
fileError();
|
||||
return false;
|
||||
}
|
||||
if (isEmpty()) {
|
||||
piCoutObj << "Warning: Empty BinLog file" << path();
|
||||
fileEnd();
|
||||
}
|
||||
play_time = 0;
|
||||
if (!rapidStart()) is_started = true;
|
||||
}
|
||||
startlogtime = PISystemTime::current();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PIBinaryLog::closeDevice() {
|
||||
moveIndex(-1);
|
||||
is_indexed = false;
|
||||
index.clear();
|
||||
index_pos.clear();
|
||||
if (canWrite() && isEmpty()) {
|
||||
file.remove();
|
||||
return true;
|
||||
}
|
||||
return file.close();
|
||||
}
|
||||
|
||||
|
||||
bool PIBinaryLog::threadedRead(uchar *readed, int size) {
|
||||
is_thread_ok = false;
|
||||
PISystemTime pt;
|
||||
double delay;
|
||||
switch (play_mode) {
|
||||
case PlayRealTime:
|
||||
pt = PISystemTime::current() - startlogtime;
|
||||
if (is_started) {
|
||||
if (lastrecord.timestamp > pt)
|
||||
(lastrecord.timestamp - pt).sleep();
|
||||
} else {
|
||||
startlogtime = PISystemTime::current() - lastrecord.timestamp;
|
||||
is_started = true;
|
||||
}
|
||||
break;
|
||||
case PlayVariableSpeed:
|
||||
delay = lastrecord.timestamp.toMilliseconds() - play_time;
|
||||
delay /= play_speed;
|
||||
if (is_started) {
|
||||
if (delay > 0)
|
||||
/// TODO: Sleep by steps (about 100ms)
|
||||
PISystemTime::fromMilliseconds(delay).sleep();
|
||||
} else is_started = true;
|
||||
play_time = lastrecord.timestamp.toMilliseconds();
|
||||
break;
|
||||
case PlayStaticDelay:
|
||||
if (is_started) play_delay.sleep();
|
||||
else is_started = true;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
bool res = PIIODevice::threadedRead(readed, size);
|
||||
is_thread_ok = true;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
PIString PIBinaryLog::createNewFile() {
|
||||
if (!file.close()) return PIString();
|
||||
PIDir dir(logDir());
|
||||
dir.setDir(dir.absolutePath());
|
||||
if (!dir.isExists()) {
|
||||
piCoutObj << "Creating directory" << dir.path();
|
||||
dir.make(true);
|
||||
}
|
||||
PIString npath = logDir() + "/" + filePrefix() + PIDateTime::current().toString("yyyy_MM_dd__hh_mm_ss");
|
||||
PIString cnpath = npath + ".binlog";
|
||||
int i = 1;
|
||||
while (PIFile::isExists(cnpath)) {
|
||||
cnpath = npath + "_" + PIString::fromNumber(i) + ".binlog";
|
||||
i++;
|
||||
}
|
||||
if (open(cnpath, PIIODevice::WriteOnly)) {
|
||||
newFile(file.path());
|
||||
return file.path();
|
||||
}
|
||||
piCoutObj << "Can't create new file, maybe LogDir is invalid.";
|
||||
return PIString();
|
||||
}
|
||||
|
||||
|
||||
int PIBinaryLog::writeBinLog(int id, const void *data, int size) {
|
||||
if (size <= 0 || !canWrite()) return -1;
|
||||
switch (split_mode) {
|
||||
case SplitSize:
|
||||
if (logSize() > split_size) createNewFile();
|
||||
break;
|
||||
case SplitTime:
|
||||
if ((PISystemTime::current() - startlogtime) > split_time) createNewFile();
|
||||
break;
|
||||
case SplitCount:
|
||||
if (write_count > split_count) createNewFile();
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
PIByteArray logdata;
|
||||
logdata << id << size << (PISystemTime::current() - startlogtime) << PIByteArray::RawData(data, size);
|
||||
int res = file.write(logdata.data(), logdata.size());
|
||||
file.flush();
|
||||
write_count++;
|
||||
if (res > 0) return size;
|
||||
else return res;
|
||||
}
|
||||
|
||||
|
||||
PIByteArray PIBinaryLog::readBinLog(int id) {
|
||||
if (!canRead()) return PIByteArray();
|
||||
BinLogRecord br = readRecord();
|
||||
if (br.id == -1) {
|
||||
piCoutObj << "End of BinLog file";
|
||||
fileEnd();
|
||||
return PIByteArray();
|
||||
}
|
||||
if (id == 0 && br.id > 0) return br.data;
|
||||
while (br.id != id && !isEnd()) br = readRecord();
|
||||
if (br.id == -1) {
|
||||
piCoutObj << "End of BinLog file";
|
||||
fileEnd();
|
||||
return PIByteArray();
|
||||
}
|
||||
if (br.id == id) return br.data;
|
||||
piCoutObj << "Can't find record with id =" << id;
|
||||
return PIByteArray();
|
||||
}
|
||||
|
||||
|
||||
int PIBinaryLog::readBinLog(int id, void *read_to, int max_size) {
|
||||
if (max_size <= 0 || read_to == 0) return -1;
|
||||
PIByteArray ba = readBinLog(id);
|
||||
if (ba.isEmpty()) return -1;
|
||||
int sz = piMini(max_size, ba.size());
|
||||
memcpy(read_to, ba.data(), sz);
|
||||
return sz;
|
||||
}
|
||||
|
||||
|
||||
int PIBinaryLog::read(void *read_to, int max_size) {
|
||||
if (lastrecord.id == -1 || isEnd()) return 0;
|
||||
if(!is_thread_ok && lastrecord.id > 0) return lastrecord.data.size();
|
||||
if (!canRead()) return -1;
|
||||
if (max_size <= 0 || read_to == 0) return -1;
|
||||
BinLogRecord br;
|
||||
br.id = 0;
|
||||
if (filterID.isEmpty()) br = readRecord();
|
||||
else {
|
||||
while (!filterID.contains(br.id) && !isEnd()) br = readRecord();
|
||||
}
|
||||
if (br.id == -1) {
|
||||
fileEnd();
|
||||
piCoutObj << "End of BinLog file";
|
||||
return 0;
|
||||
}
|
||||
if (br.id <= 0) {
|
||||
piCoutObj << "Read record error";
|
||||
return -1;
|
||||
}
|
||||
int sz = piMini(max_size, br.data.size());
|
||||
memcpy(read_to, br.data.data(), sz);
|
||||
return sz;
|
||||
}
|
||||
|
||||
|
||||
void PIBinaryLog::restart() {
|
||||
bool th = isRunning();
|
||||
if (th) stopThreadedRead();
|
||||
if (!canRead()) return;
|
||||
lastrecord.timestamp = PISystemTime();
|
||||
lastrecord.id = 0;
|
||||
is_thread_ok = true;
|
||||
is_started = !rapidStart();
|
||||
play_time = 0;
|
||||
file.seekToBegin();
|
||||
checkFileHeader();
|
||||
moveIndex(0);
|
||||
startlogtime = PISystemTime::current();
|
||||
if (th) startThreadedRead();
|
||||
}
|
||||
|
||||
|
||||
bool PIBinaryLog::writeFileHeader() {
|
||||
if (file.write(&__S__PIBinaryLog::binlog_sig, PIBINARYLOG_SIGNATURE_SIZE) <= 0) return false;
|
||||
uchar version = PIBINARYLOG_VERSION;
|
||||
if (file.write(&version, 1) <= 0) return false;
|
||||
file.flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PIBinaryLog::checkFileHeader() {
|
||||
uchar read_sig[PIBINARYLOG_SIGNATURE_SIZE];
|
||||
for (uint i=0; i<PIBINARYLOG_SIGNATURE_SIZE; i++) read_sig[i] = 0;
|
||||
if (file.read(read_sig, PIBINARYLOG_SIGNATURE_SIZE) < 0) return false;
|
||||
bool correct = true;
|
||||
for (uint i=0; i<PIBINARYLOG_SIGNATURE_SIZE; i++)
|
||||
if (read_sig[i] != __S__PIBinaryLog::binlog_sig[i]) correct = false;
|
||||
if (!correct) {
|
||||
piCoutObj << "BinLogFile signature is corrupted or invalid file";
|
||||
return false;
|
||||
}
|
||||
uchar read_version = 0;
|
||||
if (file.read(&read_version, 1) < 0) return false;
|
||||
if (read_version == PIBINARYLOG_VERSION) return true;
|
||||
if (read_version == 0)
|
||||
piCoutObj << "BinLogFile has invalid version";
|
||||
if (read_version < PIBINARYLOG_VERSION)
|
||||
piCoutObj << "BinLogFile has too old verion";
|
||||
if (read_version > PIBINARYLOG_VERSION)
|
||||
piCoutObj << "BinLogFile has too newest version";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
PIBinaryLog::BinLogRecord PIBinaryLog::readRecord() {
|
||||
PIByteArray ba;
|
||||
BinLogRecord br;
|
||||
lastrecord.id = 0;
|
||||
lastrecord.data.clear();
|
||||
lastrecord.timestamp = PISystemTime();
|
||||
ba.resize(sizeof(BinLogRecord) - sizeof(PIByteArray));
|
||||
if(file.read(ba.data(), ba.size_s()) > 0) {
|
||||
ba >> br.id >> br.size >> br.timestamp;
|
||||
} else {
|
||||
br.id = -1;
|
||||
return br;
|
||||
}
|
||||
if (br.id > 0 && br.size > 0) {
|
||||
ba.resize(br.size);
|
||||
if(file.read(ba.data(), ba.size_s()) > 0) br.data = ba;
|
||||
else br.id = 0;
|
||||
} else br.id = 0;
|
||||
lastrecord = br;
|
||||
if (br.id == 0) fileError();
|
||||
moveIndex(index_pos.value(file.pos(), -1));
|
||||
return br;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PIBinaryLog::parseLog(PIFile * f, PIBinaryLog::BinLogInfo * info, PIVector<PIBinaryLog::BinLogIndex> * index) {
|
||||
BinLogInfo * bi = info;
|
||||
bool ginfo = info != 0;
|
||||
bool gindex = index != 0;
|
||||
if (!ginfo && !gindex) return;
|
||||
if (ginfo) {
|
||||
bi->log_size = -1;
|
||||
bi->records_count = 0;
|
||||
bi->records.clear();
|
||||
}
|
||||
if (gindex) index->clear();
|
||||
if (f == 0) return;
|
||||
if (!f->canRead()) return;
|
||||
if (ginfo) {
|
||||
bi->path = f->path();
|
||||
bi->log_size = f->size();
|
||||
}
|
||||
uchar read_sig[PIBINARYLOG_SIGNATURE_SIZE];
|
||||
for (uint i=0; i<PIBINARYLOG_SIGNATURE_SIZE; i++) read_sig[i] = 0;
|
||||
bool ok = true;
|
||||
if (f->read(read_sig, PIBINARYLOG_SIGNATURE_SIZE) < 0) {if (ginfo) bi->records_count = -1; ok = false;}
|
||||
for (uint i=0; i<PIBINARYLOG_SIGNATURE_SIZE; i++)
|
||||
if (read_sig[i] != __S__PIBinaryLog::binlog_sig[i]) {if (ginfo) bi->records_count = -2; ok = false;}
|
||||
uchar read_version = 0;
|
||||
if (f->read(&read_version, 1) < 0) {if (ginfo) bi->records_count = -3; ok = false;}
|
||||
if (read_version == 0) {if (ginfo) bi->records_count = -4; ok = false;}
|
||||
if (read_version < PIBINARYLOG_VERSION) {if (ginfo) bi->records_count = -5; ok = false;}
|
||||
if (read_version > PIBINARYLOG_VERSION) {if (ginfo) bi->records_count = -6; ok = false;}
|
||||
if (!ok) return;
|
||||
PIByteArray ba;
|
||||
BinLogRecord br;
|
||||
bool first = true;
|
||||
llong hdr_size = sizeof(BinLogRecord) - sizeof(PIByteArray);
|
||||
ba.resize(hdr_size);
|
||||
while (1) {
|
||||
ba.resize(hdr_size);
|
||||
if(f->read(ba.data(), ba.size_s()) > 0) {
|
||||
ba >> br.id >> br.size >> br.timestamp;
|
||||
} else break;
|
||||
if (f->size() - f->pos() >= br.size) f->seek(f->pos() + br.size);
|
||||
else break;
|
||||
if (br.id > 0) {
|
||||
if (gindex) {
|
||||
BinLogIndex bl_ind;
|
||||
bl_ind.id = br.id;
|
||||
bl_ind.pos = f->pos() - br.size - hdr_size;
|
||||
bl_ind.timestamp = br.timestamp;
|
||||
index->append(bl_ind);
|
||||
}
|
||||
if (ginfo) {
|
||||
bi->records_count++;
|
||||
if (first) {
|
||||
bi->start_time = br.timestamp;
|
||||
first = false;
|
||||
}
|
||||
BinLogRecordInfo &bri(bi->records[br.id]);
|
||||
bri.count++;
|
||||
if (bri.id == 0) {
|
||||
bri.id = br.id;
|
||||
bri.minimum_size = bri.maximum_size = br.size;
|
||||
bri.start_time = br.timestamp;
|
||||
} else {
|
||||
bri.end_time = br.timestamp;
|
||||
if (bri.minimum_size > br.size) bri.minimum_size = br.size;
|
||||
if (bri.maximum_size < br.size) bri.maximum_size = br.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ginfo) bi->end_time = br.timestamp;
|
||||
}
|
||||
|
||||
|
||||
void PIBinaryLog::moveIndex(int i) {
|
||||
if (is_indexed) {
|
||||
current_index = i;
|
||||
posChanged(current_index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PIBinaryLog::BinLogInfo PIBinaryLog::getLogInfo(const PIString & path) {
|
||||
BinLogInfo bi;
|
||||
bi.path = path;
|
||||
bi.records_count = 0;
|
||||
PIFile tfile;
|
||||
if (!tfile.open(path, PIIODevice::ReadOnly)) return bi;
|
||||
parseLog(&tfile, &bi, 0);
|
||||
return bi;
|
||||
}
|
||||
|
||||
|
||||
PIString PIBinaryLog::constructFullPath() const {
|
||||
PIString ret(fullPathPrefix() + "://");
|
||||
ret << logDir() << ":" << filePrefix() << ":" << defaultID();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool PIBinaryLog::createIndex() {
|
||||
llong cp = file.pos();
|
||||
file.seekToBegin();
|
||||
index.clear();
|
||||
index_pos.clear();
|
||||
parseLog(&file, &binfo, &index);
|
||||
file.seek(cp);
|
||||
is_indexed = !index.isEmpty();
|
||||
for (uint i=0; i<index.size(); i++) index_pos[index[i].pos] = i;
|
||||
return is_indexed;
|
||||
}
|
||||
|
||||
|
||||
void PIBinaryLog::seekTo(int rindex) {
|
||||
if (rindex < index.size_s() && rindex >= 0) {
|
||||
file.seek(index[rindex].pos);
|
||||
moveIndex(index_pos.value(file.pos(), -1));
|
||||
play_time = index[rindex].timestamp.toMilliseconds();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool PIBinaryLog::seek(const PISystemTime & time) {
|
||||
int ci = -1;
|
||||
for (uint i=0; i<index.size(); i++) {
|
||||
if (time <= index[i].timestamp && (filterID.contains(index[i].id) || filterID.isEmpty())) {
|
||||
ci = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ci >= 0) {
|
||||
seekTo(ci);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool PIBinaryLog::seek(llong filepos) {
|
||||
int ci = -1;
|
||||
for (uint i=0; i<index.size(); i++) {
|
||||
if (filepos <= index[i].pos && (filterID.contains(index[i].id) || filterID.isEmpty())) {
|
||||
ci = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ci >= 0) {
|
||||
seekTo(ci);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void PIBinaryLog::configureFromFullPath(const PIString & full_path) {
|
||||
PIStringList pl = full_path.split(":");
|
||||
for (int i = 0; i < pl.size_s(); ++i) {
|
||||
PIString p(pl[i]);
|
||||
switch (i) {
|
||||
case 0: setLogDir(p); break;
|
||||
case 1: setFilePrefix(p); break;
|
||||
case 2: setDefaultID(p.toInt()); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIBinaryLog::propertyChanged(const PIString &) {
|
||||
default_id = property("defaultID").toInt();
|
||||
rapid_start = property("rapidStart").toBool();
|
||||
play_mode = (PlayMode)property("playMode").toInt();
|
||||
play_speed = property("playSpeed").toDouble();
|
||||
play_delay = property("playDelay").toSystemTime();
|
||||
split_mode = (SplitMode)property("splitMode").toInt();
|
||||
split_time = property("splitTime").toSystemTime();
|
||||
split_size = property("splitFileSize").toLLong();
|
||||
split_count = property("splitRecordCount").toInt();
|
||||
}
|
||||
|
||||
333
src/io/pibinarylog.h
Normal file
333
src/io/pibinarylog.h
Normal file
@@ -0,0 +1,333 @@
|
||||
/*! \file pibinarylog.h
|
||||
* \brief Binary log
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Class for write binary data to logfile, and read or playback this data
|
||||
Copyright (C) 2014 Andrey Bychkov work.a.b@yandex.ru
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIBINARYLOG_H
|
||||
#define PIBINARYLOG_H
|
||||
|
||||
#include "pifile.h"
|
||||
|
||||
#define PIBINARYLOG_VERSION 0x31
|
||||
namespace __S__PIBinaryLog {
|
||||
static const uchar binlog_sig[] = {'B','I','N','L','O','G'};
|
||||
};
|
||||
#define PIBINARYLOG_SIGNATURE_SIZE sizeof(__S__PIBinaryLog::binlog_sig)
|
||||
|
||||
/// TODO: Create static functions to split and join binlog files
|
||||
/// TODO: Create functions to insert and delete records
|
||||
class PIBinaryLog: public PIIODevice
|
||||
{
|
||||
PIIODEVICE(PIBinaryLog)
|
||||
public:
|
||||
PIBinaryLog();
|
||||
~PIBinaryLog() {closeDevice();}
|
||||
|
||||
//! \brief Play modes for \a PIBinaryLog
|
||||
enum PlayMode {
|
||||
PlayRealTime /*! Play in system realtime, default mode */ ,
|
||||
PlayVariableSpeed /*! Play in software realtime with speed, set by \a setSpeed */ ,
|
||||
PlayStaticDelay /*! Play with custom static delay, ignoring timestamp */
|
||||
};
|
||||
|
||||
//! \brief Different split modes for writing \a PIBinaryLog, which can separate files by size, by time or by records count
|
||||
enum SplitMode {
|
||||
SplitNone /*! Without separate, default mode */ ,
|
||||
SplitTime /*! Separate files by record time */ ,
|
||||
SplitSize /*! Separate files by size */ ,
|
||||
SplitCount /*! Separate files by records count */
|
||||
};
|
||||
|
||||
//! \brief Struct contains information about all records with same ID
|
||||
struct BinLogRecordInfo {
|
||||
BinLogRecordInfo() {
|
||||
id = count = 0;
|
||||
minimum_size = maximum_size = 0;
|
||||
}
|
||||
int id;
|
||||
int count;
|
||||
int minimum_size;
|
||||
int maximum_size;
|
||||
PISystemTime start_time;
|
||||
PISystemTime end_time;
|
||||
};
|
||||
|
||||
//! \brief Struct contains full information about Binary Log file and about all Records using map of \a BinLogRecordInfo
|
||||
struct BinLogInfo {
|
||||
PIString path;
|
||||
int records_count;
|
||||
llong log_size;
|
||||
PISystemTime start_time;
|
||||
PISystemTime end_time;
|
||||
PIMap<int,BinLogRecordInfo> records;
|
||||
};
|
||||
|
||||
//! \brief Struct contains position, ID and timestamp of record in file
|
||||
struct BinLogIndex {
|
||||
int id;
|
||||
llong pos;
|
||||
PISystemTime timestamp;
|
||||
};
|
||||
|
||||
|
||||
//! Current \a PlayMode
|
||||
PlayMode playMode() const {return play_mode;}
|
||||
|
||||
//! Current \a SplitMode
|
||||
SplitMode splitMode() const {return split_mode;}
|
||||
|
||||
//! Current directory where billogs wiil be saved
|
||||
PIString logDir() const {return property("logDir").toString();}
|
||||
|
||||
//! Returns current file prefix
|
||||
PIString filePrefix() const {return property("filePrefix").toString();}
|
||||
|
||||
//! Current LogDir, returns directory where billogs wiil be saved
|
||||
int defaultID() const {return default_id;}
|
||||
|
||||
//! Returns current play speed
|
||||
double playSpeed() const {return play_speed;}
|
||||
|
||||
//! Returns current play delay
|
||||
PISystemTime playDelay() const {return play_delay;}
|
||||
|
||||
//! Returns current binlog file split time
|
||||
PISystemTime splitTime() const {return split_time;}
|
||||
|
||||
//! Returns current binlog file split size
|
||||
llong splitFileSize() const {return split_size;}
|
||||
|
||||
//! Returns current binlog file split records count
|
||||
int splitRecordCount() const {return split_count;}
|
||||
|
||||
//! Returns if rapid start enabled
|
||||
bool rapidStart() const {return rapid_start;}
|
||||
|
||||
|
||||
//! Set \a PlayMode
|
||||
void setPlayMode(PlayMode mode) {setProperty("playMode", (int)mode);}
|
||||
|
||||
//! Set \a SplitMode
|
||||
void setSplitMode(SplitMode mode) {setProperty("splitMode", (int)mode);}
|
||||
|
||||
//! Set path to directory where binlogs will be saved
|
||||
void setLogDir(const PIString & path) {setProperty("logDir", path);}
|
||||
|
||||
//! Set file prefix, used to
|
||||
void setFilePrefix(const PIString & prefix) {setProperty("filePrefix", prefix);}
|
||||
|
||||
//! Set defaultID, used in \a write function
|
||||
void setDefaultID(int id) {setProperty("defaultID", id);}
|
||||
|
||||
//! If enabled BinLog \a ThreadedRead starts without delay for first record, i.e. first record will be readed immediately
|
||||
void setRapidStart(bool enabled) {setProperty("rapidStart", enabled);}
|
||||
|
||||
//! Set play speed to "speed", default value is 1.0x
|
||||
//! Also this function set \a playMode to \a PlayVariableSpeed
|
||||
void setPlaySpeed(double speed) {setPlayMode(PlayVariableSpeed); setProperty("playSpeed", speed);}
|
||||
|
||||
//! Setting static delay between records, default value is 1 sec
|
||||
//! Also this function set \a playMode to \a PlayStaticDelay
|
||||
void setPlayDelay(const PISystemTime & delay) {setPlayMode(PlayStaticDelay); setProperty("playDelay", delay);}
|
||||
|
||||
//! Set \a playMode to \a PlayRealTime
|
||||
void setPlayRealTime() {setPlayMode(PlayRealTime);}
|
||||
|
||||
//! Set binlog file split time
|
||||
//! Also this function set \a splitMode to \a SplitTime
|
||||
void setSplitTime(const PISystemTime & time) {setSplitMode(SplitTime); setProperty("splitTime", time);}
|
||||
|
||||
//! Set binlog file split size
|
||||
//! Also this function set \a splitMode to \a SplitSize
|
||||
void setSplitFileSize(llong size) {setSplitMode(SplitSize); setProperty("splitFileSize", size);}
|
||||
|
||||
//! Set binlog file split records count
|
||||
//! Also this function set \a splitMode to \a SplitCount
|
||||
void setSplitRecordCount(int count) {setSplitMode(SplitCount); setProperty("splitRecordCount", count);}
|
||||
|
||||
|
||||
//! Write one record to BinLog file, with ID = id, id must be greather than 0
|
||||
int writeBinLog(int id, PIByteArray data) {return writeBinLog(id, data.data(), data.size_s());}
|
||||
|
||||
//! Write one record to BinLog file, with ID = id, id must be greather than 0
|
||||
int writeBinLog(int id, const void * data, int size);
|
||||
|
||||
//! Returns count of writed records
|
||||
int writeCount() const {return write_count;}
|
||||
|
||||
//! Read one record from BinLog file, with ID = id, if id = 0 than any id will be readed
|
||||
PIByteArray readBinLog(int id = 0);
|
||||
|
||||
//! Read one record from BinLog file, with ID = id, if id = 0 than any id will be readed
|
||||
int readBinLog(int id, void * read_to, int max_size);
|
||||
|
||||
//! Returns binary log file size
|
||||
llong logSize() const {return file.size();}
|
||||
|
||||
//! Return true, if position at the end of BinLog file
|
||||
bool isEnd() const {if (!opened_) return true; return file.isEnd();}
|
||||
|
||||
//! Returns if BinLog file is empty
|
||||
bool isEmpty() const {return (file.size() <= PIBINARYLOG_SIGNATURE_SIZE + 1);}
|
||||
|
||||
//! Returns if BinLog file is empty
|
||||
int lastReadedID() const {return lastrecord.id;}
|
||||
|
||||
//! Set position in file to reading/playing
|
||||
|
||||
//! Read one message from binlog file, with ID contains in "filterID" or any ID, if "filterID" is empty
|
||||
int read(void *read_to, int max_size);
|
||||
|
||||
//! Write one record to BinLog file, with ID = "defaultID"
|
||||
int write(const void * data, int size) {return writeBinLog(default_id, data, size);}
|
||||
|
||||
//! Array of ID, that BinLog can read from binlog file, when use \a read function, or in \a ThreadedRead
|
||||
PIVector<int> filterID;
|
||||
|
||||
//! Go to begin of BinLog file
|
||||
void restart();
|
||||
|
||||
PIString constructFullPath() const;
|
||||
|
||||
//! Get binlog info \a BinLogInfo
|
||||
BinLogInfo logInfo() const {if (is_indexed) return binfo; return getLogInfo(path());}
|
||||
|
||||
//! Get binlog index \a BinLogIndex, need \a createIndex before getting index
|
||||
const PIVector<BinLogIndex> & logIndex() const {return index;} /// TODO: Think about index positions
|
||||
|
||||
//! Create index of current binlog file
|
||||
bool createIndex();
|
||||
|
||||
//! Go to record #index
|
||||
void seekTo(int rindex);
|
||||
|
||||
//! Go to nearest record
|
||||
bool seek(const PISystemTime & time);
|
||||
bool seek(llong filepos);
|
||||
|
||||
//! Get current record index (position record in file)
|
||||
int pos() const {if (is_indexed) return current_index; return -1;} /// TODO: Think about index positions
|
||||
|
||||
//! \handlers
|
||||
//! \{
|
||||
|
||||
//! \fn PIString createNewFile()
|
||||
//! \brief Create new binlog file in \a logDir, if successful returns filename, else returns empty string.
|
||||
//! Filename is like \a filePrefix + "yyyy_MM_dd__hh_mm_ss.binlog"
|
||||
|
||||
//! \}
|
||||
//! \events
|
||||
//! \{
|
||||
|
||||
//! \fn void fileEnd()
|
||||
//! \brief Raise on file end while reading
|
||||
|
||||
//! \fn void fileError()
|
||||
//! \brief Raise on file creation error
|
||||
|
||||
//! \fn void newFile(const PIString & filename)
|
||||
//! \brief Raise on new file created
|
||||
|
||||
//! \}
|
||||
|
||||
EVENT_HANDLER(PIString, createNewFile);
|
||||
EVENT(fileEnd)
|
||||
EVENT(fileError)
|
||||
EVENT1(newFile, const PIString &, filename)
|
||||
EVENT1(posChanged, int, pos) /// TODO: Think about index positions
|
||||
|
||||
//! Get binlog info and statistic
|
||||
static BinLogInfo getLogInfo(const PIString & path);
|
||||
|
||||
protected:
|
||||
PIString fullPathPrefix() const {return "binlog";}
|
||||
void configureFromFullPath(const PIString & full_path);
|
||||
bool openDevice();
|
||||
bool closeDevice();
|
||||
void propertyChanged(const PIString &);
|
||||
bool threadedRead(uchar *readed, int size);
|
||||
|
||||
private:
|
||||
struct BinLogRecord {
|
||||
int id;
|
||||
int size;
|
||||
PISystemTime timestamp;
|
||||
PIByteArray data;
|
||||
};
|
||||
|
||||
bool writeFileHeader();
|
||||
bool checkFileHeader();
|
||||
BinLogRecord readRecord();
|
||||
static void parseLog(PIFile *f, BinLogInfo *info, PIVector<BinLogIndex> * index);
|
||||
void moveIndex(int i);
|
||||
|
||||
PIVector<BinLogIndex> index;
|
||||
PIMap<llong, int> index_pos;
|
||||
BinLogInfo binfo;
|
||||
|
||||
PlayMode play_mode;
|
||||
SplitMode split_mode;
|
||||
PIFile file;
|
||||
BinLogRecord lastrecord;
|
||||
PISystemTime startlogtime, play_delay, split_time;
|
||||
double play_time, play_speed;
|
||||
llong split_size;
|
||||
int write_count, split_count, default_id, current_index;
|
||||
bool is_started, is_thread_ok, is_indexed, rapid_start;
|
||||
};
|
||||
|
||||
//! \relatesalso PICout \relatesalso PIBinaryLog::BinLogInfo \brief Output operator to PICout
|
||||
inline PICout operator <<(PICout s, const PIBinaryLog::BinLogInfo & bi) {
|
||||
s.space();
|
||||
s.setControl(0, true);
|
||||
s << "[PIBinaryLog] " << bi.path << "\n";
|
||||
if (bi.log_size < 0) {
|
||||
s << "invalid file path";
|
||||
s.restoreControl();
|
||||
return s;
|
||||
}
|
||||
if (bi.log_size == 0) {
|
||||
s << "Invalid empty file";
|
||||
s.restoreControl();
|
||||
return s;
|
||||
} if (bi.records_count < 0 && bi.records_count > -4) {
|
||||
s << "Invalid file or corrupted signature";
|
||||
s.restoreControl();
|
||||
return s;
|
||||
}
|
||||
if (bi.records_count < -3) {
|
||||
s << "Invalid binlog version";
|
||||
s.restoreControl();
|
||||
return s;
|
||||
}
|
||||
s << "read records " << bi.records_count << " in " << bi.records.size() << " types, log size " << bi.log_size;
|
||||
s << "\nlog start " << bi.start_time << " , log end " << bi.end_time;
|
||||
PIVector<int> keys = bi.records.keys();
|
||||
piForeachC(int i, keys) {
|
||||
const PIBinaryLog::BinLogRecordInfo &bri(bi.records[i]);
|
||||
s << "\n record id " << bri.id << " , count " << bri.count;
|
||||
s << "\n record start " << bri.start_time << " , end " << bri.end_time;
|
||||
s << "\n record size " << bri.minimum_size << " - " << bri.maximum_size;
|
||||
}
|
||||
s.restoreControl();
|
||||
return s;
|
||||
}
|
||||
|
||||
#endif // PIBINARYLOG_H
|
||||
710
src/io/piconfig.cpp
Executable file
710
src/io/piconfig.cpp
Executable file
@@ -0,0 +1,710 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Config parser
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "piconfig.h"
|
||||
#include "pifile.h"
|
||||
#include "piiostring.h"
|
||||
|
||||
/*! \class PIConfig
|
||||
* \brief Configuration file
|
||||
* \details This class provide handle access to configuration file.
|
||||
*
|
||||
* \section PIConfig_sec0 Synopsis
|
||||
* PIConfig reads configuration file and create internal dendritic
|
||||
* representation of all entries of this file. You can easily read
|
||||
* some values and write new.
|
||||
* \image html piconfig.png
|
||||
*
|
||||
* %PIConfig supports also INI-style files with sections "[section]".
|
||||
* In this case line with section name interpret as prefix to the next
|
||||
* lines. For example, these configs are equal:
|
||||
* \code
|
||||
* ser.device = /dev/ttyS0
|
||||
* ser.speed = 115200
|
||||
* debug = true
|
||||
* \endcode
|
||||
* \code
|
||||
* [ser]
|
||||
* device = /dev/ttyS0
|
||||
* speed = 115200
|
||||
* []
|
||||
* debug = true
|
||||
* \endcode
|
||||
*
|
||||
* \section PIConfig_sec1 Concepts
|
||||
* Each node of internal tree has type PIConfig::Entry. %PIConfig
|
||||
* has one root element \a rootEntry(). Any entry of configuration file is a
|
||||
* child of this element.
|
||||
*
|
||||
*/
|
||||
|
||||
/*! \class PIConfig::Entry
|
||||
* \brief %Entry of configuration file
|
||||
* \details This class is node of internal PIConfig tree.
|
||||
* %Entry provide access to elements of PIConfig. Each entry has
|
||||
* children or next properties:
|
||||
* * name
|
||||
* * value
|
||||
* * type
|
||||
* * comment
|
||||
*
|
||||
* Each property is a PIString. These properties forms from text line with
|
||||
* format: \code{.cpp} <name> = <value> #<type> <comment> \endcode
|
||||
* Type and comment are optional fields. Type is a single letter immediately
|
||||
* after comment symbol "#". \n \n
|
||||
* %Entry has many implicit convertions to common types: boolean, integers,
|
||||
* float, double, PIString, PIStringList. \n \n
|
||||
* Generally there is no need to create instance of %PIConfig::Entry manually,
|
||||
* it returns by functions \a getValue() of \a PIConfig, \a PIConfig::Entry or
|
||||
* \a PIConfig::Branch. If there is no suitable entry to return, reference to
|
||||
* internal instance of %PIConfig::Entry with "default" value will be returned.
|
||||
* \snippet piconfig.cpp PIConfig::Entry
|
||||
*
|
||||
*/
|
||||
|
||||
/*! \class PIConfig::Branch
|
||||
* \brief %Branch is a list of entries of configuration file
|
||||
* \details %Branch provides some features to get entries lists.
|
||||
* \snippet piconfig.cpp PIConfig::Branch
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
PIConfig::Entry PIConfig::Branch::_empty;
|
||||
PIConfig::Entry PIConfig::Entry::_empty;
|
||||
|
||||
|
||||
PIConfig::Branch PIConfig::Branch::allLeaves() {
|
||||
Branch b;
|
||||
b.delim = delim;
|
||||
piForeach (Entry * i, *this) {
|
||||
if (i->isLeaf()) b << i;
|
||||
else allLeaves(b, i);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
PIConfig::Entry & PIConfig::Branch::getValue(const PIString & vname, const PIString & def, bool * exist) {
|
||||
if (vname.isEmpty()) {
|
||||
_empty.clear();
|
||||
_empty.delim = delim;
|
||||
if (exist != 0) *exist = false;
|
||||
return _empty;
|
||||
}
|
||||
PIStringList tree = vname.split(delim);
|
||||
PIString name = tree.front();
|
||||
tree.pop_front();
|
||||
Entry * ce = 0;
|
||||
piForeach (Entry * i, *this)
|
||||
if (i->_name == name) {
|
||||
ce = i;
|
||||
break;
|
||||
}
|
||||
if (ce == 0) {
|
||||
_empty._name = vname;
|
||||
_empty._value = def;
|
||||
_empty.delim = delim;
|
||||
if (exist != 0) *exist = false;
|
||||
return _empty;
|
||||
}
|
||||
piForeach (PIString & i, tree) {
|
||||
ce = ce->findChild(i);
|
||||
if (ce == 0) {
|
||||
_empty._name = vname;
|
||||
_empty._value = def;
|
||||
_empty.delim = delim;
|
||||
if (exist != 0) *exist = false;
|
||||
return _empty;
|
||||
}
|
||||
}
|
||||
if (exist != 0) *exist = true;
|
||||
return *ce;
|
||||
}
|
||||
|
||||
|
||||
PIConfig::Branch PIConfig::Branch::getValues(const PIString & name) {
|
||||
Branch b;
|
||||
b.delim = delim;
|
||||
piForeach (Entry * i, *this) {
|
||||
if (i->isLeaf()) {
|
||||
if (i->_name.find(name) >= 0)
|
||||
b << i;
|
||||
} else {
|
||||
piForeach (Entry * j, i->_children)
|
||||
if (j->_name.find(name) >= 0)
|
||||
b << j;
|
||||
}
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
PIConfig::Branch PIConfig::Branch::getLeaves() {
|
||||
Branch b;
|
||||
b.delim = delim;
|
||||
piForeach (Entry * i, *this)
|
||||
if (i->isLeaf())
|
||||
b << i;
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
PIConfig::Branch PIConfig::Branch::getBranches() {
|
||||
Branch b;
|
||||
b.delim = delim;
|
||||
piForeach (Entry * i, *this)
|
||||
if (!i->isLeaf())
|
||||
b << i;
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
PIConfig::Branch & PIConfig::Branch::filter(const PIString & f) {
|
||||
for (int i = 0; i < size_s(); ++i) {
|
||||
if (at(i)->_name.find(f) < 0) {
|
||||
remove(i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
bool PIConfig::Branch::entryExists(const Entry * e, const PIString & name) const {
|
||||
if (e->_children.isEmpty()) {
|
||||
return (e->_name == name);
|
||||
}
|
||||
piForeachC (Entry * i, e->_children)
|
||||
if (entryExists(i, name)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
PIConfig::Entry & PIConfig::Entry::getValue(const PIString & vname, const PIString & def, bool * exist) {
|
||||
PIStringList tree = vname.split(delim);
|
||||
Entry * ce = this;
|
||||
piForeach (PIString & i, tree) {
|
||||
ce = ce->findChild(i);
|
||||
if (ce == 0) {
|
||||
_empty._name = vname;
|
||||
_empty._value = def;
|
||||
_empty.delim = delim;
|
||||
if (exist != 0) *exist = false;
|
||||
return _empty;
|
||||
}
|
||||
}
|
||||
if (exist != 0) *exist = true;
|
||||
return *ce;
|
||||
}
|
||||
|
||||
|
||||
PIConfig::Branch PIConfig::Entry::getValues(const PIString & vname) {
|
||||
Branch b;
|
||||
b.delim = delim;
|
||||
piForeach (Entry * i, _children)
|
||||
if (i->_name.find(vname) >= 0)
|
||||
b << i;
|
||||
return b;
|
||||
};
|
||||
|
||||
|
||||
bool PIConfig::Entry::entryExists(const Entry * e, const PIString & name) const {
|
||||
if (e->_children.isEmpty()) {
|
||||
return (e->_name == name);
|
||||
}
|
||||
piForeachC (Entry * i, e->_children)
|
||||
if (entryExists(i, name)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
PIConfig::PIConfig(const PIString & path, PIIODevice::DeviceMode mode) {
|
||||
_init();
|
||||
own_dev = true;
|
||||
dev = new PIFile(path, mode);
|
||||
if (!dev->isOpened())
|
||||
dev->open(path, mode);
|
||||
parse();
|
||||
}
|
||||
|
||||
|
||||
PIConfig::PIConfig(PIString * string, PIIODevice::DeviceMode mode) {
|
||||
_init();
|
||||
own_dev = true;
|
||||
dev = new PIIOString(string, mode);
|
||||
parse();
|
||||
}
|
||||
|
||||
|
||||
PIConfig::PIConfig(PIIODevice * device, PIIODevice::DeviceMode mode) {
|
||||
_init();
|
||||
own_dev = false;
|
||||
dev = device;
|
||||
if (dev) dev->open(mode);
|
||||
parse();
|
||||
}
|
||||
|
||||
|
||||
PIConfig::~PIConfig() {
|
||||
root.deleteBranch();
|
||||
if (own_dev && dev) delete dev;
|
||||
dev = 0;
|
||||
}
|
||||
|
||||
|
||||
bool PIConfig::open(const PIString & path, PIIODevice::DeviceMode mode) {
|
||||
if (own_dev && dev) delete dev;
|
||||
own_dev = true;
|
||||
dev = new PIFile(path, mode);
|
||||
if (!dev->isOpened())
|
||||
dev->open(path, mode);
|
||||
parse();
|
||||
return dev->isOpened();
|
||||
}
|
||||
|
||||
|
||||
bool PIConfig::open(PIString * string, PIIODevice::DeviceMode mode) {
|
||||
if (own_dev && dev) delete dev;
|
||||
own_dev = true;
|
||||
dev = new PIIOString(string, mode);
|
||||
parse();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void PIConfig::_init() {
|
||||
delim = ".";
|
||||
root.delim = delim;
|
||||
empty.delim = delim;
|
||||
empty._parent = 0;
|
||||
}
|
||||
|
||||
|
||||
void PIConfig::_clearDev() {
|
||||
if (!dev) return;
|
||||
if (PIString(dev->className()) == "PIFile") {((PIFile*)dev)->clear(); return;}
|
||||
if (PIString(dev->className()) == "PIIOString") {((PIIOString*)dev)->clear(); return;}
|
||||
}
|
||||
|
||||
|
||||
void PIConfig::_flushDev() {
|
||||
if (!dev) return;
|
||||
if (PIString(dev->className()) == "PIFile") {((PIFile*)dev)->flush();}
|
||||
}
|
||||
|
||||
|
||||
bool PIConfig::_isEndDev() {
|
||||
if (!dev) return true;
|
||||
if (PIString(dev->className()) == "PIFile") {return ((PIFile*)dev)->isEnd();}
|
||||
if (PIString(dev->className()) == "PIIOString") {return ((PIIOString*)dev)->isEnd();}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void PIConfig::_seekToBeginDev() {
|
||||
if (!dev) return;
|
||||
if (PIString(dev->className()) == "PIFile") {((PIFile*)dev)->seekToBegin(); return;}
|
||||
if (PIString(dev->className()) == "PIIOString") {((PIIOString*)dev)->seekToBegin(); return;}
|
||||
}
|
||||
|
||||
|
||||
PIString PIConfig::_readLineDev() {
|
||||
if (!dev) return PIString();
|
||||
if (PIString(dev->className()) == "PIFile") {return ((PIFile*)dev)->readLine();}
|
||||
if (PIString(dev->className()) == "PIIOString") {return ((PIIOString*)dev)->readLine();}
|
||||
return PIString();
|
||||
}
|
||||
|
||||
|
||||
void PIConfig::_writeDev(const PIString & l) {
|
||||
//piCout << "write \"" << l << "\"";
|
||||
if (!dev) return;
|
||||
if (PIString(dev->className()) == "PIFile") {*((PIFile*)dev) << (l); return;}
|
||||
if (PIString(dev->className()) == "PIIOString") {((PIIOString*)dev)->writeString(l); return;}
|
||||
dev->write(l.toByteArray());
|
||||
}
|
||||
|
||||
|
||||
bool PIConfig::isOpened() const {
|
||||
if (dev) return dev->isOpened();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
PIConfig::Entry & PIConfig::getValue(const PIString & vname, const PIString & def, bool * exist) {
|
||||
PIStringList tree = vname.split(delim);
|
||||
Entry * ce = &root;
|
||||
piForeach (PIString & i, tree) {
|
||||
ce = ce->findChild(i);
|
||||
if (ce == 0) {
|
||||
if (exist != 0) *exist = false;
|
||||
empty._name = vname;
|
||||
empty._value = def;
|
||||
empty.delim = delim;
|
||||
return empty;
|
||||
}
|
||||
}
|
||||
if (exist != 0) *exist = true;
|
||||
return *ce;
|
||||
}
|
||||
|
||||
|
||||
PIConfig::Branch PIConfig::getValues(const PIString & vname) {
|
||||
Branch b;
|
||||
b.delim = delim;
|
||||
piForeach (Entry * i, root._children)
|
||||
if (i->_name.find(vname) >= 0)
|
||||
b << i;
|
||||
return b;
|
||||
};
|
||||
|
||||
|
||||
void PIConfig::addEntry(const PIString & name, const PIString & value, const PIString & type, bool write) {
|
||||
if (getValue(name)._parent != 0)
|
||||
return;
|
||||
bool toRoot = false;
|
||||
PIStringList tree = name.split(delim);
|
||||
PIString ename = tree.back();
|
||||
tree.pop_back();
|
||||
Entry * te, * ce, * entry = &root;
|
||||
if (tree.isEmpty()) toRoot = true;
|
||||
piForeach (PIString & i, tree) {
|
||||
te = entry->findChild(i);
|
||||
if (te == 0) {
|
||||
ce = new Entry();
|
||||
ce->delim = delim;
|
||||
ce->_tab = entry->_tab;
|
||||
ce->_line = entry->_line;
|
||||
ce->_name = i;
|
||||
ce->_parent = entry;
|
||||
entry->_children << ce;
|
||||
entry = ce;
|
||||
} else entry = te;
|
||||
}
|
||||
PIConfig::Branch ch = entry->_children;
|
||||
ch.sort(PIConfig::Entry::compare);
|
||||
te = (entry->isLeaf() ? 0 : ch.back());
|
||||
ce = new Entry();
|
||||
ce->delim = delim;
|
||||
ce->_name = ename;
|
||||
ce->_value = value;
|
||||
ce->_type = type;
|
||||
if (te == 0) {
|
||||
ce->_tab = entry->_tab;
|
||||
if (toRoot) ce->_line = other.size_s() - 1;
|
||||
else ce->_line = entry->_line;
|
||||
} else {
|
||||
ce->_tab = te->_tab;
|
||||
if (toRoot) ce->_line = other.size_s() - 1;
|
||||
else {
|
||||
ch = entry->_parent->_children;
|
||||
ch.sort(PIConfig::Entry::compare);
|
||||
ce->_line = ch.back()->_line + 1;
|
||||
}
|
||||
}
|
||||
ce->_parent = entry;
|
||||
entry->_children << ce;
|
||||
other.insert(ce->_line, "");
|
||||
Branch b = allLeaves();
|
||||
bool found = false;
|
||||
for (int i = 0; i < b.size_s(); ++i) {
|
||||
if (found) {
|
||||
b[i]->_line++;
|
||||
continue;
|
||||
}
|
||||
if (b[i] == ce) {
|
||||
found = true;
|
||||
if (i > 0)
|
||||
if (b[i - 1]->_line == b[i]->_line)
|
||||
b[i - 1]->_line++;
|
||||
}
|
||||
}
|
||||
if (write) writeAll();
|
||||
}
|
||||
|
||||
|
||||
void PIConfig::setValue(const PIString & name, const PIString & value, const PIString & type, bool write) {
|
||||
Entry & e(getValue(name));
|
||||
if (&e == &empty) {
|
||||
addEntry(name, value, type, write);
|
||||
return;
|
||||
}
|
||||
e._value = value;
|
||||
e._type = type;
|
||||
if (write) writeAll();
|
||||
}
|
||||
|
||||
|
||||
int PIConfig::entryIndex(const PIString & name) {
|
||||
PIStringList tree = name.split(delim);
|
||||
Entry * ce = &root;
|
||||
piForeach (PIString & i, tree) {
|
||||
ce = ce->findChild(i);
|
||||
if (ce == 0)
|
||||
return -1;
|
||||
}
|
||||
return allLeaves().indexOf(ce);
|
||||
}
|
||||
|
||||
|
||||
void PIConfig::setValue(uint number, const PIString & value, bool write) {
|
||||
Entry & e(entryByIndex(number));
|
||||
if (&e == &empty) return;
|
||||
e._value = value;
|
||||
if (write) writeAll();
|
||||
}
|
||||
|
||||
|
||||
void PIConfig::setName(uint number, const PIString & name, bool write) {
|
||||
Entry & e(entryByIndex(number));
|
||||
if (&e == &empty) return;
|
||||
e._name = name;
|
||||
if (write) writeAll();
|
||||
}
|
||||
|
||||
|
||||
void PIConfig::setType(uint number, const PIString & type, bool write) {
|
||||
Entry & e(entryByIndex(number));
|
||||
if (&e == &empty) return;
|
||||
e._type = type;
|
||||
if (write) writeAll();
|
||||
}
|
||||
|
||||
|
||||
void PIConfig::setComment(uint number, const PIString & comment, bool write) {
|
||||
Entry & e(entryByIndex(number));
|
||||
if (&e == &empty) return;
|
||||
e._comment = comment;
|
||||
if (write) writeAll();
|
||||
}
|
||||
|
||||
|
||||
void PIConfig::removeEntry(const PIString & name, bool write) {
|
||||
Entry & e(getValue(name));
|
||||
if (&e == &empty) return;
|
||||
Branch b = allLeaves();
|
||||
removeEntry(b, &e);
|
||||
if (write) writeAll();
|
||||
}
|
||||
|
||||
|
||||
void PIConfig::removeEntry(uint number, bool write) {
|
||||
Entry & e(entryByIndex(number));
|
||||
if (&e == &empty) return;
|
||||
Branch b = allLeaves();
|
||||
removeEntry(b, &e);
|
||||
if (write) writeAll();
|
||||
}
|
||||
|
||||
|
||||
void PIConfig::removeEntry(Branch & b, PIConfig::Entry * e) {
|
||||
bool leaf = true;
|
||||
if (e->isLeaf()) other.remove(e->_line);
|
||||
if (!e->isLeaf() && !e->_value.isEmpty()) {
|
||||
e->_value.clear();
|
||||
leaf = false;
|
||||
} else {
|
||||
int cc = e->_children.size_s();
|
||||
piForTimes (cc)
|
||||
removeEntry(b, e->_children.back());
|
||||
}
|
||||
bool found = false;
|
||||
for (int i = 0; i < b.size_s(); ++i) {
|
||||
if (found) {
|
||||
b[i]->_line--;
|
||||
continue;
|
||||
}
|
||||
if (b[i] == e) found = true;
|
||||
}
|
||||
if (!leaf) return;
|
||||
e->_parent->_children.removeOne(e);
|
||||
b.removeOne(e);
|
||||
delete e;
|
||||
}
|
||||
|
||||
|
||||
PIString PIConfig::getPrefixFromLine(PIString line, bool * exists) {
|
||||
line.trim();
|
||||
if (line.left(1) == "#") {if (exists) *exists = false; return PIString();}
|
||||
int ci = line.find("#");
|
||||
if (ci >= 0) line.cutRight(line.size() - ci);
|
||||
if (line.find("=") >= 0) {if (exists) *exists = false; return PIString();}
|
||||
if (line.find("[") >= 0 && line.find("]") >= 0) {
|
||||
if (exists) *exists = true;
|
||||
return line.takeRange('[', ']').trim();
|
||||
}
|
||||
if (exists) *exists = false;
|
||||
return PIString();
|
||||
}
|
||||
|
||||
|
||||
void PIConfig::writeAll() {
|
||||
//cout << this << " write < " << size() << endl;
|
||||
_clearDev();
|
||||
//*this << "1234567894132456798\n"; return;
|
||||
//writeEntry(&root);
|
||||
buildFullNames(&root);
|
||||
Branch b = allLeaves();
|
||||
PIString prefix, tprefix;
|
||||
bool isPrefix;
|
||||
//for (int i = 0; i < b.size_s(); ++i)
|
||||
// cout << b[i]->_name << " = " << b[i]->_value << endl;
|
||||
int j = 0;
|
||||
for (int i = 0; i < other.size_s(); ++i) {
|
||||
//cout << j << endl;
|
||||
if (j >= 0 && j < b.size_s()) {
|
||||
if (b[j]->_line == i) {
|
||||
b[j]->buildLine();
|
||||
_writeDev((b[j]->_all).cutLeft(prefix.size()) + "\n");
|
||||
//cout << this << " " << b[j]->_all << endl;
|
||||
++j;
|
||||
} else {
|
||||
_writeDev(other[i]);
|
||||
tprefix = getPrefixFromLine(other[i], &isPrefix);
|
||||
if (isPrefix) {
|
||||
prefix = tprefix;
|
||||
if (!prefix.isEmpty())
|
||||
prefix += delim;
|
||||
}
|
||||
if (i < other.size_s() - 1)
|
||||
_writeDev('\n');
|
||||
//cout << this << " " << other[i] << endl;
|
||||
}
|
||||
} else {
|
||||
_writeDev(other[i]);
|
||||
tprefix = getPrefixFromLine(other[i], &isPrefix);
|
||||
if (isPrefix) {
|
||||
prefix = tprefix;
|
||||
if (!prefix.isEmpty())
|
||||
prefix += delim;
|
||||
}
|
||||
if (i < other.size_s() - 1)
|
||||
_writeDev('\n');
|
||||
//cout << this << " " << other[i] << endl;
|
||||
}
|
||||
}
|
||||
_flushDev();
|
||||
readAll();
|
||||
//cout << this << " write > " << size() << endl;
|
||||
}
|
||||
|
||||
|
||||
void PIConfig::clear() {
|
||||
_clearDev();
|
||||
parse();
|
||||
}
|
||||
|
||||
|
||||
void PIConfig::readAll() {
|
||||
root.deleteBranch();
|
||||
root.clear();
|
||||
parse();
|
||||
}
|
||||
|
||||
|
||||
bool PIConfig::entryExists(const Entry * e, const PIString & name) const {
|
||||
if (e->_children.isEmpty()) {
|
||||
return (e->_name == name);
|
||||
}
|
||||
piForeachC (Entry * i, e->_children)
|
||||
if (entryExists(i, name)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void PIConfig::parse() {
|
||||
PIString src, str, tab, comm, all, name, type, prefix, tprefix;
|
||||
PIStringList tree;
|
||||
Entry * entry, * te, * ce;
|
||||
int ind, sind;
|
||||
bool isNew, isPrefix;
|
||||
if (!isOpened()) return;
|
||||
_seekToBeginDev();
|
||||
other.clear();
|
||||
lines = centry = 0;
|
||||
while (!_isEndDev()) {
|
||||
other.push_back(PIString());
|
||||
src = str = _readLineDev();
|
||||
tprefix = getPrefixFromLine(src, &isPrefix);
|
||||
if (isPrefix) {
|
||||
prefix = tprefix;
|
||||
if (!prefix.isEmpty())
|
||||
prefix += delim;
|
||||
}
|
||||
//piCout << "line \"" << str << "\"";
|
||||
tab = str.left(str.find(str.trimmed().left(1)));
|
||||
str.trim();
|
||||
//cout << endl << str << endl << endl;
|
||||
all = str;
|
||||
ind = str.find('=');
|
||||
if ((ind > 0) && (str[0] != '#')) {
|
||||
sind = str.find('#');
|
||||
if (sind > 0) {
|
||||
comm = str.right(str.length() - sind - 1).trimmed();
|
||||
if (comm.length() > 0) type = comm[0];
|
||||
else type = "s";
|
||||
comm = comm.right(comm.length() - 1).trimmed();
|
||||
str = str.left(sind);
|
||||
} else {
|
||||
type = "s";
|
||||
comm = "";
|
||||
}
|
||||
//name = str.left(ind).trimmed();
|
||||
tree = (prefix + str.left(ind).trimmed()).split(delim);
|
||||
name = tree.back();
|
||||
tree.pop_back();
|
||||
entry = &root;
|
||||
piForeachC (PIString & i, tree) {
|
||||
te = entry->findChild(i);
|
||||
if (te == 0) {
|
||||
ce = new Entry();
|
||||
ce->delim = delim;
|
||||
ce->_tab = tab;
|
||||
ce->_line = lines;
|
||||
ce->_name = i;
|
||||
ce->_parent = entry;
|
||||
entry->_children << ce;
|
||||
entry = ce;
|
||||
} else entry = te;
|
||||
}
|
||||
isNew = false;
|
||||
ce = entry->findChild(name);
|
||||
if (ce == 0) {
|
||||
ce = new Entry();
|
||||
isNew = true;
|
||||
}
|
||||
ce->delim = delim;
|
||||
ce->_tab = tab;
|
||||
ce->_name = name;
|
||||
ce->_value = str.right(str.length() - ind - 1).trimmed();
|
||||
ce->_type = type;
|
||||
ce->_comment = comm;
|
||||
ce->_line = lines;
|
||||
ce->_all = all;
|
||||
if (isNew) {
|
||||
ce->_parent = entry;
|
||||
entry->_children << ce;
|
||||
}
|
||||
} else other.back() = src;
|
||||
lines++;
|
||||
}
|
||||
setEntryDelim(&root, delim);
|
||||
buildFullNames(&root);
|
||||
}
|
||||
525
src/io/piconfig.h
Executable file
525
src/io/piconfig.h
Executable file
@@ -0,0 +1,525 @@
|
||||
/*! \file piconfig.h
|
||||
* \brief Configuration parser and writer
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Configuration parser and writer
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PICONFIG_H
|
||||
#define PICONFIG_H
|
||||
|
||||
#include "piiodevice.h"
|
||||
|
||||
#define PICONFIG_GET_VALUE \
|
||||
Entry & getValue(const PIString & vname, const char * def, bool * exists = 0) {return getValue(vname, PIString(def), exists);} \
|
||||
Entry & getValue(const PIString & vname, const PIStringList & def, bool * exists = 0) {return getValue(vname, def.join("%|%"), exists);} \
|
||||
Entry & getValue(const PIString & vname, const bool def, bool * exists = 0) {return getValue(vname, PIString::fromBool(def), exists);} \
|
||||
Entry & getValue(const PIString & vname, const short def, bool * exists = 0) {return getValue(vname, itos(def), exists);} \
|
||||
Entry & getValue(const PIString & vname, const int def, bool * exists = 0) {return getValue(vname, itos(def), exists);} \
|
||||
Entry & getValue(const PIString & vname, const long def, bool * exists = 0) {return getValue(vname, ltos(def), exists);} \
|
||||
Entry & getValue(const PIString & vname, const uchar def, bool * exists = 0) {return getValue(vname, uitos(def), exists);} \
|
||||
Entry & getValue(const PIString & vname, const ushort def, bool * exists = 0) {return getValue(vname, uitos(def), exists);} \
|
||||
Entry & getValue(const PIString & vname, const uint def, bool * exists = 0) {return getValue(vname, uitos(def), exists);} \
|
||||
Entry & getValue(const PIString & vname, const ulong def, bool * exists = 0) {return getValue(vname, ultos(def), exists);} \
|
||||
Entry & getValue(const PIString & vname, const float def, bool * exists = 0) {return getValue(vname, ftos(def), exists);} \
|
||||
Entry & getValue(const PIString & vname, const double def, bool * exists = 0) {return getValue(vname, dtos(def), exists);} \
|
||||
\
|
||||
Entry & getValue(const PIString & vname, const char * def, bool * exists = 0) const {return getValue(vname, PIString(def), exists);} \
|
||||
Entry & getValue(const PIString & vname, const PIStringList & def, bool * exists = 0) const {return getValue(vname, def.join("%|%"), exists);} \
|
||||
Entry & getValue(const PIString & vname, const bool def, bool * exists = 0) const {return getValue(vname, PIString::fromBool(def), exists);} \
|
||||
Entry & getValue(const PIString & vname, const short def, bool * exists = 0) const {return getValue(vname, itos(def), exists);} \
|
||||
Entry & getValue(const PIString & vname, const int def, bool * exists = 0) const {return getValue(vname, itos(def), exists);} \
|
||||
Entry & getValue(const PIString & vname, const long def, bool * exists = 0) const {return getValue(vname, ltos(def), exists);} \
|
||||
Entry & getValue(const PIString & vname, const uchar def, bool * exists = 0) const {return getValue(vname, uitos(def), exists);} \
|
||||
Entry & getValue(const PIString & vname, const ushort def, bool * exists = 0) const {return getValue(vname, uitos(def), exists);} \
|
||||
Entry & getValue(const PIString & vname, const uint def, bool * exists = 0) const {return getValue(vname, uitos(def), exists);} \
|
||||
Entry & getValue(const PIString & vname, const ulong def, bool * exists = 0) const {return getValue(vname, ultos(def), exists);} \
|
||||
Entry & getValue(const PIString & vname, const float def, bool * exists = 0) const {return getValue(vname, ftos(def), exists);} \
|
||||
Entry & getValue(const PIString & vname, const double def, bool * exists = 0) const {return getValue(vname, dtos(def), exists);}
|
||||
|
||||
class PIP_EXPORT PIConfig
|
||||
{
|
||||
friend class Entry;
|
||||
friend class Branch;
|
||||
public:
|
||||
|
||||
//! Contructs and read configuration file at path "path" in mode "mode"
|
||||
PIConfig(const PIString & path, PIIODevice::DeviceMode mode = PIIODevice::ReadWrite);
|
||||
|
||||
//! Contructs and read configuration string "string" in mode "mode"
|
||||
PIConfig(PIString * string, PIIODevice::DeviceMode mode = PIIODevice::ReadWrite);
|
||||
|
||||
//! Contructs and read configuration from custom device "device" in mode "mode"
|
||||
PIConfig(PIIODevice * device = 0, PIIODevice::DeviceMode mode = PIIODevice::ReadWrite);
|
||||
|
||||
~PIConfig();
|
||||
|
||||
class Entry;
|
||||
|
||||
|
||||
class PIP_EXPORT Branch: public PIVector<Entry * > {
|
||||
friend class PIConfig;
|
||||
friend class Entry;
|
||||
friend std::ostream & operator <<(std::ostream & s, const Branch & v);
|
||||
friend PICout operator <<(PICout s, const Branch & v);
|
||||
public:
|
||||
Branch() {;}
|
||||
|
||||
Entry & getValue(const PIString & vname, const PIString & def = PIString(), bool * exists = 0);
|
||||
Entry & getValue(const PIString & vname, const PIString & def = PIString(), bool * exists = 0) const {return const_cast<Branch * >(this)->getValue(vname, def, exists);}
|
||||
PICONFIG_GET_VALUE
|
||||
|
||||
Branch allLeaves();
|
||||
Branch getValues(const PIString & name);
|
||||
Branch getLeaves();
|
||||
Branch getBranches();
|
||||
Branch & filter(const PIString & f);
|
||||
bool isEntryExists(const PIString & name) const {piForeachC (Entry * i, *this) if (entryExists(i, name)) return true; return false;}
|
||||
int indexOf(const Entry * e) {for (int i = 0; i < size_s(); ++i) if (at(i) == e) return i; return -1;}
|
||||
|
||||
//void clear() {piForeach (Entry * i, *this) delete i; PIVector<Entry * >::clear();}
|
||||
|
||||
private:
|
||||
bool entryExists(const Entry * e, const PIString & name) const;
|
||||
void allLeaves(Branch & b, Entry * e) {piForeach (Entry * i, e->_children) {if (i->isLeaf()) b << i; else allLeaves(b, i);}}
|
||||
void coutt(std::ostream & s, const PIString & p) const {piForeachC (Entry * i, *this) i->coutt(s, p);}
|
||||
void piCoutt(PICout s, const PIString & p) const {piForeachC (Entry * i, *this) i->piCoutt(s, p);}
|
||||
|
||||
static Entry _empty;
|
||||
PIString delim;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class PIP_EXPORT Entry {
|
||||
friend class PIConfig;
|
||||
friend class Branch;
|
||||
public:
|
||||
Entry() {_parent = 0;}
|
||||
|
||||
//! Returns parent entry, or 0 if there is no parent (root of default value)
|
||||
Entry * parent() const {return _parent;}
|
||||
|
||||
//! Returns children count
|
||||
int childCount() const {return _children.size_s();}
|
||||
|
||||
//! Returns children as \a PIConfig::Branch
|
||||
Branch & children() const {_children.delim = delim; return _children;}
|
||||
|
||||
//! Returns child at index "index"
|
||||
Entry * child(const int index) const {return _children[index];}
|
||||
|
||||
//! Returns first child with name "name"
|
||||
Entry * findChild(const PIString & name) {piForeach (Entry * i, _children) if (i->_name == name) return i; return 0;}
|
||||
|
||||
//! Returns first child with name "name"
|
||||
const Entry * findChild(const PIString & name) const {piForeachC (Entry * i, _children) if (i->_name == name) return i; return 0;}
|
||||
|
||||
//! Returns \b true if there is no children
|
||||
bool isLeaf() const {return _children.isEmpty();}
|
||||
|
||||
|
||||
//! Returns name
|
||||
const PIString & name() const {return _name;}
|
||||
|
||||
//! Returns value
|
||||
const PIString & value() const {return _value;}
|
||||
|
||||
//! Returns type
|
||||
const PIString & type() const {return _type;}
|
||||
|
||||
//! Returns comment
|
||||
const PIString & comment() const {return _comment;}
|
||||
|
||||
/** \brief Returns full name, i.e. name as it looks in file
|
||||
* \details In case of default entry full name always is empty
|
||||
* \snippet piconfig.cpp fullName */
|
||||
const PIString & fullName() const {return _full_name;}
|
||||
|
||||
//! Set name to "value" and returns this
|
||||
Entry & setName(const PIString & value) {_name = value; return *this;}
|
||||
|
||||
//! Set type to "value" and returns this
|
||||
Entry & setType(const PIString & value) {_type = value; return *this;}
|
||||
|
||||
//! Set comment to "value" and returns this
|
||||
Entry & setComment(const PIString & value) {_comment = value; return *this;}
|
||||
|
||||
//! Set value to "value" and returns this
|
||||
Entry & setValue(const PIString & value) {_value = value; return *this;}
|
||||
|
||||
//! Set value to "value" and returns this. Type is set to "l"
|
||||
Entry & setValue(const PIStringList & value) {setValue(value.join("%|%")); setType("l"); return *this;}
|
||||
|
||||
//! Set value to "value" and returns this. Type is set to "s"
|
||||
Entry & setValue(const char * value) {setValue(PIString(value)); setType("s"); return *this;}
|
||||
|
||||
//! Set value to "value" and returns this. Type is set to "b"
|
||||
Entry & setValue(const bool value) {setValue(btos(value)); setType("b"); return *this;}
|
||||
|
||||
//! Set value to "value" and returns this. Type is set to "s"
|
||||
Entry & setValue(const char value) {setValue(PIString(1, value)); setType("s"); return *this;}
|
||||
|
||||
//! Set value to "value" and returns this. Type is set to "n"
|
||||
Entry & setValue(const short value) {setValue(itos(value)); setType("n"); return *this;}
|
||||
|
||||
//! Set value to "value" and returns this. Type is set to "n"
|
||||
Entry & setValue(const int value) {setValue(itos(value)); setType("n"); return *this;}
|
||||
|
||||
//! Set value to "value" and returns this. Type is set to "n"
|
||||
Entry & setValue(const long value) {setValue(ltos(value)); setType("n"); return *this;}
|
||||
|
||||
//! Set value to "value" and returns this. Type is set to "n"
|
||||
Entry & setValue(const uchar value) {setValue(uitos(value)); setType("n"); return *this;}
|
||||
|
||||
//! Set value to "value" and returns this. Type is set to "n"
|
||||
Entry & setValue(const ushort value) {setValue(uitos(value)); setType("n"); return *this;}
|
||||
|
||||
//! Set value to "value" and returns this. Type is set to "n"
|
||||
Entry & setValue(const uint value) {setValue(uitos(value)); setType("n"); return *this;}
|
||||
|
||||
//! Set value to "value" and returns this. Type is set to "n"
|
||||
Entry & setValue(const ulong value) {setValue(ultos(value)); setType("n"); return *this;}
|
||||
|
||||
//! Set value to "value" and returns this. Type is set to "f"
|
||||
Entry & setValue(const float value) {setValue(ftos(value)); setType("f"); return *this;}
|
||||
|
||||
//! Set value to "value" and returns this. Type is set to "f"
|
||||
Entry & setValue(const double value) {setValue(dtos(value)); setType("f"); return *this;}
|
||||
|
||||
|
||||
/** \brief Returns entry with name "vname" and default value "def"
|
||||
* \details If there is no suitable entry found, reference to default internal entry with
|
||||
* value = "def" will be returned, and if "exists" not null it will be set to \b false */
|
||||
Entry & getValue(const PIString & vname, const PIString & def = PIString(), bool * exists = 0);
|
||||
Entry & getValue(const PIString & vname, const PIString & def = PIString(), bool * exists = 0) const {return const_cast<Entry * >(this)->getValue(vname, def, exists);}
|
||||
PICONFIG_GET_VALUE
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const char * def, bool * exists = 0)
|
||||
//! \brief Returns entry with name "vname" and default value "def"
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const char * def, bool * exists = 0)
|
||||
//! \brief Returns entry with name "vname" and default value "def"
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const PIStringList & def, bool * exists = 0)
|
||||
//! \brief Returns entry with name "vname" and default value "def"
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const bool def, bool * exists = 0)
|
||||
//! \brief Returns entry with name "vname" and default value "def"
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const short def, bool * exists = 0)
|
||||
//! \brief Returns entry with name "vname" and default value "def"
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const int def, bool * exists = 0)
|
||||
//! \brief Returns entry with name "vname" and default value "def"
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const long def, bool * exists = 0)
|
||||
//! \brief Returns entry with name "vname" and default value "def"
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const uchar def, bool * exists = 0)
|
||||
//! \brief Returns entry with name "vname" and default value "def"
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const ushort def, bool * exists = 0)
|
||||
//! \brief Returns entry with name "vname" and default value "def"
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const uint def, bool * exists = 0)
|
||||
//! \brief Returns entry with name "vname" and default value "def"
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const ulong def, bool * exists = 0)
|
||||
//! \brief Returns entry with name "vname" and default value "def"
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const float def, bool * exists = 0)
|
||||
//! \brief Returns entry with name "vname" and default value "def"
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const double def, bool * exists = 0)
|
||||
//! \brief Returns entry with name "vname" and default value "def"
|
||||
|
||||
|
||||
//! Find all entries with names with substrings "vname" and returns them as \a PIConfig::Branch
|
||||
Branch getValues(const PIString & vname);
|
||||
|
||||
|
||||
//! If there is no children returns if name == "name". Else returns if any child has name == "name"
|
||||
bool isEntryExists(const PIString & name) const {return entryExists(this, name);}
|
||||
|
||||
|
||||
//! Convertion to boolean
|
||||
operator bool() {return _value.toBool();}
|
||||
|
||||
//! Convertion to char
|
||||
operator char() {return (_value.isEmpty() ? 0 : _value[0].toAscii());}
|
||||
|
||||
//! Convertion to short
|
||||
operator short() {return _value.toShort();}
|
||||
|
||||
//! Convertion to int
|
||||
operator int() {return _value.toInt();}
|
||||
|
||||
//! Convertion to long
|
||||
operator long() {return _value.toLong();}
|
||||
|
||||
//! Convertion to uchar
|
||||
operator uchar() {return _value.toInt();}
|
||||
|
||||
//! Convertion to ushort
|
||||
operator ushort() {return _value.toShort();}
|
||||
|
||||
//! Convertion to uint
|
||||
operator uint() {return _value.toInt();}
|
||||
|
||||
//! Convertion to ulong
|
||||
operator ulong() {return _value.toLong();}
|
||||
|
||||
//! Convertion to float
|
||||
operator float() {return _value.toFloat();}
|
||||
|
||||
//! Convertion to double
|
||||
operator double() {return _value.toDouble();}
|
||||
|
||||
//! Convertion to PIString
|
||||
operator PIString() {return _value;}
|
||||
|
||||
//! Convertion to PIStringList
|
||||
operator PIStringList() {return _value.split("%|%");}
|
||||
|
||||
private:
|
||||
typedef PIConfig::Entry * EntryPtr;
|
||||
static int compare(const EntryPtr * f, const EntryPtr * s) {return (*f)->_line == (*s)->_line ? 0 : (*f)->_line < (*s)->_line ? -1 : 1;}
|
||||
bool entryExists(const Entry * e, const PIString & name) const;
|
||||
void buildLine() {_all = _tab + _full_name + " = " + _value + " #" + _type + " " + _comment;}
|
||||
void clear() {_children.clear(); _name = _value = _type = _comment = _all = PIString(); _line = 0; _parent = 0;}
|
||||
void coutt(std::ostream & s, const PIString & p) const {PIString nl = p + " "; if (!_value.isEmpty()) s << p << _name << " = " << _value << endl; else cout << p << _name << endl; piForeachC (Entry * i, _children) i->coutt(s, nl);}
|
||||
void piCoutt(PICout s, const PIString & p) const {PIString nl = p + " "; if (!_value.isEmpty()) s << p << _name << " = " << _value << NewLine; else cout << p << _name << endl; piForeachC (Entry * i, _children) i->piCoutt(s, nl);}
|
||||
void deleteBranch() {piForeach (Entry * i, _children) {i->deleteBranch(); delete i;}}
|
||||
|
||||
static Entry _empty;
|
||||
Entry * _parent;
|
||||
mutable Branch _children;
|
||||
PIString _tab;
|
||||
PIString _name;
|
||||
PIString _value;
|
||||
PIString _type;
|
||||
PIString _comment;
|
||||
PIString _all;
|
||||
PIString _full_name;
|
||||
PIString delim;
|
||||
int _line;
|
||||
};
|
||||
|
||||
|
||||
//! Read configuration file at path "path" in mode "mode"
|
||||
bool open(const PIString & path, PIIODevice::DeviceMode mode = PIIODevice::ReadWrite);
|
||||
|
||||
//! Read configuration string "string" in mode "mode"
|
||||
bool open(PIString * string, PIIODevice::DeviceMode mode = PIIODevice::ReadWrite);\
|
||||
|
||||
bool isOpened() const;
|
||||
|
||||
//! Returns top-level entry with name "vname", if doesn`t exists return entry with value "def" and set *exist to false
|
||||
Entry & getValue(const PIString & vname, const PIString & def = PIString(), bool * exists = 0);
|
||||
Entry & getValue(const PIString & vname, const PIString & def = PIString(), bool * exists = 0) const {return const_cast<PIConfig * >(this)->getValue(vname, def, exists);}
|
||||
|
||||
PICONFIG_GET_VALUE
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const char * def, bool * exists = 0)
|
||||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const char * def, bool * exists = 0)
|
||||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const PIStringList & def, bool * exists = 0)
|
||||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const bool def, bool * exists = 0)
|
||||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const short def, bool * exists = 0)
|
||||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const int def, bool * exists = 0)
|
||||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const long def, bool * exists = 0)
|
||||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const uchar def, bool * exists = 0)
|
||||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const ushort def, bool * exists = 0)
|
||||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const uint def, bool * exists = 0)
|
||||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const ulong def, bool * exists = 0)
|
||||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const float def, bool * exists = 0)
|
||||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||||
|
||||
//! \fn Entry & getValue(const PIString & vname, const double def, bool * exists = 0)
|
||||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||||
|
||||
|
||||
//! Returns top-level entries with names with substrings "vname"
|
||||
Branch getValues(const PIString & vname);
|
||||
|
||||
|
||||
//! Set top-level entry with name "name" value to "value", type to "type" and if "write" immediate write to file. Add new entry if there is no suitable exists
|
||||
void setValue(const PIString & name, const PIString & value, const PIString & type = "s", bool write = true);
|
||||
|
||||
//! Set top-level entry with name "name" value to "value", type to "l" and if "write" immediate write to file. Add new entry if there is no suitable exists
|
||||
void setValue(const PIString & name, const PIStringList & value, bool write = true) {setValue(name, value.join("%|%"), "l", write);}
|
||||
|
||||
//! Set top-level entry with name "name" value to "value", type to "s" and if "write" immediate write to file. Add new entry if there is no suitable exists
|
||||
void setValue(const PIString & name, const char * value, bool write = true) {setValue(name, PIString(value), "s", write);}
|
||||
|
||||
//! Set top-level entry with name "name" value to "value", type to "b" and if "write" immediate write to file. Add new entry if there is no suitable exists
|
||||
void setValue(const PIString & name, const bool value, bool write = true) {setValue(name, btos(value), "b", write);}
|
||||
|
||||
//! Set top-level entry with name "name" value to "value", type to "n" and if "write" immediate write to file. Add new entry if there is no suitable exists
|
||||
void setValue(const PIString & name, const short value, bool write = true) {setValue(name, itos(value), "n", write);}
|
||||
|
||||
//! Set top-level entry with name "name" value to "value", type to "n" and if "write" immediate write to file. Add new entry if there is no suitable exists
|
||||
void setValue(const PIString & name, const int value, bool write = true) {setValue(name, itos(value), "n", write);}
|
||||
|
||||
//! Set top-level entry with name "name" value to "value", type to "n" and if "write" immediate write to file. Add new entry if there is no suitable exists
|
||||
void setValue(const PIString & name, const long value, bool write = true) {setValue(name, ltos(value), "n", write);}
|
||||
|
||||
//! Set top-level entry with name "name" value to "value", type to "n" and if "write" immediate write to file. Add new entry if there is no suitable exists
|
||||
void setValue(const PIString & name, const uchar value, bool write = true) {setValue(name, uitos(value), "n", write);}
|
||||
|
||||
//! Set top-level entry with name "name" value to "value", type to "n" and if "write" immediate write to file. Add new entry if there is no suitable exists
|
||||
void setValue(const PIString & name, const ushort value, bool write = true) {setValue(name, uitos(value), "n", write);}
|
||||
|
||||
//! Set top-level entry with name "name" value to "value", type to "n" and if "write" immediate write to file. Add new entry if there is no suitable exists
|
||||
void setValue(const PIString & name, const uint value, bool write = true) {setValue(name, uitos(value), "n", write);}
|
||||
|
||||
//! Set top-level entry with name "name" value to "value", type to "n" and if "write" immediate write to file. Add new entry if there is no suitable exists
|
||||
void setValue(const PIString & name, const ulong value, bool write = true) {setValue(name, ultos(value), "n", write);}
|
||||
|
||||
//! Set top-level entry with name "name" value to "value", type to "f" and if "write" immediate write to file. Add new entry if there is no suitable exists
|
||||
void setValue(const PIString & name, const float value, bool write = true) {setValue(name, ftos(value), "f", write);}
|
||||
|
||||
//! Set top-level entry with name "name" value to "value", type to "f" and if "write" immediate write to file. Add new entry if there is no suitable exists
|
||||
void setValue(const PIString & name, const double value, bool write = true) {setValue(name, dtos(value), "f", write);}
|
||||
|
||||
//! Returns root entry
|
||||
Entry & rootEntry() {return root;}
|
||||
|
||||
//! Returns top-level entries count
|
||||
int entriesCount() const {return childCount(&root);}
|
||||
|
||||
//! Returns if top-level entry with name "name" exists
|
||||
bool isEntryExists(const PIString & name) const {return entryExists(&root, name);}
|
||||
|
||||
//! Returns all top-level entries
|
||||
Branch allTree() {Branch b; piForeach (Entry * i, root._children) b << i; b.delim = delim; return b;}
|
||||
|
||||
//! Returns all entries without children
|
||||
Branch allLeaves() {Branch b; allLeaves(b, &root); b.sort(Entry::compare); b.delim = delim; return b;}
|
||||
|
||||
int entryIndex(const PIString & name);
|
||||
|
||||
PIString getName(uint number) {return entryByIndex(number)._name;}
|
||||
PIString getValue(uint number) {return entryByIndex(number)._value;}
|
||||
PIChar getType(uint number) {return entryByIndex(number)._type[0];}
|
||||
PIString getComment(uint number) {return entryByIndex(number)._comment;}
|
||||
|
||||
void addEntry(const PIString & name, const PIString & value, const PIString & type = "s", bool write = true);
|
||||
void setName(uint number, const PIString & name, bool write = true);
|
||||
void setValue(uint number, const PIString & value, bool write = true);
|
||||
void setType(uint number, const PIString & type, bool write = true);
|
||||
void setComment(uint number, const PIString & comment, bool write = true);
|
||||
|
||||
void removeEntry(const PIString & name, bool write = true);
|
||||
void removeEntry(uint number, bool write = true);
|
||||
|
||||
//! Remove all tree and device content
|
||||
void clear();
|
||||
|
||||
//! Parse device and build internal tree
|
||||
void readAll();
|
||||
|
||||
//! Write all internal tree to device
|
||||
void writeAll();
|
||||
|
||||
//! Returns current tree delimiter, default "."
|
||||
const PIString & delimiter() const {return delim;}
|
||||
|
||||
//! Set current tree delimiter
|
||||
void setDelimiter(const PIString & d) {delim = d; setEntryDelim(&root, d); readAll();}
|
||||
|
||||
private:
|
||||
void _init();
|
||||
void _clearDev();
|
||||
void _flushDev();
|
||||
bool _isEndDev();
|
||||
void _seekToBeginDev();
|
||||
PIString _readLineDev();
|
||||
void _writeDev(const PIString & l);
|
||||
int childCount(const Entry * e) const {int c = 0; piForeachC (Entry * i, e->_children) c += childCount(i); c += e->_children.size_s(); return c;}
|
||||
bool entryExists(const Entry * e, const PIString & name) const;
|
||||
void buildFullNames(Entry * e) {piForeach (Entry * i, e->_children) {if (e != &root) i->_full_name = e->_full_name + delim + i->_name; else i->_full_name = i->_name; buildFullNames(i);}}
|
||||
void allLeaves(Branch & b, Entry * e) {piForeach (Entry * i, e->_children) {if ((!i->_value.isEmpty() && !i->isLeaf()) || i->isLeaf()) b << i; allLeaves(b, i);}}
|
||||
void setEntryDelim(Entry * e, const PIString & d) {piForeach (Entry * i, e->_children) setEntryDelim(i, d); e->delim = d;}
|
||||
Entry & entryByIndex(const int index) {Branch b = allLeaves(); if (index < 0 || index >= b.size_s()) return empty; return *(b[index]);}
|
||||
void removeEntry(Branch & b, Entry * e);
|
||||
void deleteEntry(Entry * e) {piForeach (Entry * i, e->_children) deleteEntry(i); delete e;}
|
||||
PIString getPrefixFromLine(PIString line, bool * exists);
|
||||
void parse();
|
||||
|
||||
int centry;
|
||||
bool own_dev;
|
||||
PIIODevice * dev;
|
||||
PIString delim;
|
||||
Entry root, empty;
|
||||
uint lines;
|
||||
PIStringList other;
|
||||
|
||||
};
|
||||
|
||||
|
||||
inline std::ostream & operator <<(std::ostream & s, const PIConfig::Branch & v) {v.coutt(s, ""); return s;}
|
||||
inline std::ostream & operator <<(std::ostream & s, const PIConfig::Entry & v) {s << v.value(); return s;}
|
||||
inline PICout operator <<(PICout s, const PIConfig::Branch & v) {s.setControl(0, true); v.piCoutt(s, ""); s.restoreControl(); return s;}
|
||||
inline PICout operator <<(PICout s, const PIConfig::Entry & v) {s << v.value(); return s;}
|
||||
|
||||
|
||||
/** \relatesalso PIConfig \relatesalso PIIODevice
|
||||
* \brief Service function. useful for configuring devices
|
||||
* \details Function takes entry name "name", default value "def" and two
|
||||
* \a PIConfig::Entry sections: "em" and their parent "ep". If there is no
|
||||
* parent ep = 0. If "ep" is not null and entry "name" exists in "ep" function
|
||||
* returns this value. Else returns value of entry "name" in section "em" or
|
||||
* "def" if entry doesn`t exists. \n This function useful to read settings
|
||||
* from configuration file in implementation \a PIIODevice::configureDevice() function */
|
||||
template<typename T>
|
||||
T readDeviceSetting(const PIString & name, const T & def, const PIConfig::Entry * em, const PIConfig::Entry * ep) {
|
||||
if (ep != 0) {
|
||||
T ret;
|
||||
bool ex;
|
||||
ret = ep->getValue(name, def, &ex);
|
||||
if (!ex) ret = em->getValue(name, def);
|
||||
return ret;
|
||||
}
|
||||
return em->getValue(name, def);
|
||||
|
||||
}
|
||||
|
||||
#endif // PICONFIG_H
|
||||
1195
src/io/piconnection.cpp
Executable file
1195
src/io/piconnection.cpp
Executable file
@@ -0,0 +1,1195 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Complex I/O point
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "piconnection.h"
|
||||
#include "piconfig.h"
|
||||
|
||||
/** \class PIConnection
|
||||
* \brief Complex Input/Output point
|
||||
*
|
||||
* \section PIConnection_synopsis Synopsis
|
||||
* %PIConnection provides abstract layer over physical devices,
|
||||
* filtering and connecting data streams. Each %PIConnection
|
||||
* works through Device Pool, so several %PIConnections can
|
||||
* read from single physical device. General scheme:
|
||||
* \image html piconnection.png
|
||||
*
|
||||
* \section PIConnection_pool Device pool concept
|
||||
* Device pool is static object, single for each application, which
|
||||
* contains unique devices. Each %PIConnection works with real devices
|
||||
* through Device pool. Each device has assosiated thread for read
|
||||
* and it can be started or stopped with %PIConnection functions
|
||||
* \a startThreadedRead() and \a stopThreadedRead().
|
||||
*
|
||||
* \section PIConnection_filters Filters
|
||||
* %PIConnection filter is a PIPacketExtractor and assosiated
|
||||
* array of devices or other filters. When read thread is successfully read
|
||||
* from device this data can be passed to one or more filters. Each filter
|
||||
* has name and filter names should be unique. You can use this name for
|
||||
* access to PIPacketExtractor* with function \a filter(), or get array of
|
||||
* assosiated devices and filters with function \a filterBoundedDevices().
|
||||
* One filter can receive data from several sources, and can be bounded to
|
||||
* several filters.
|
||||
* \image html piconnection_filters.png
|
||||
*
|
||||
* \section PIConnection_diag Diagnostics
|
||||
* %PIConnection create PIDiagnostics for each device or filter. You can
|
||||
* access to these objects with functions \a diagnostic().
|
||||
*
|
||||
* \section PIConnection_sender Senders
|
||||
* %PIConnection can send data to devices with named timers ("senders").
|
||||
* You can create sender or add device to sender with function \a addSender().
|
||||
* Each sender has internal timer and every tick execute virtual function
|
||||
* \a senderData(). Returns value of this function sended to bounded devices.
|
||||
* You can assign fixed send data to sender with function \a setSenderFixedData().
|
||||
* In this case sender will NOT execute \a senderData(), but send assigned data.
|
||||
* \image html piconnection_senders.png
|
||||
*
|
||||
* \section PIConnection_config Configuration
|
||||
* You can create %PIConnection from config file section or configure
|
||||
* it later with function \a configureFromConfig(). Devices describes
|
||||
* with its full pathes, for details see \ref PIIODevice_sec7. Example:
|
||||
* \image html piconnection_conf.png
|
||||
* Also %PIConnection can create PIString with its configuration with
|
||||
* function \a makeConfig(). This string can be directly inserted into the
|
||||
* config file.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
PIVector<PIConnection * > PIConnection::_connections;
|
||||
|
||||
|
||||
PIConnection::PIConnection(): PIObject() {
|
||||
_connections << this;
|
||||
}
|
||||
|
||||
|
||||
PIConnection::PIConnection(const PIString & name): PIObject(name) {
|
||||
_connections << this;
|
||||
}
|
||||
|
||||
|
||||
PIConnection::PIConnection(const PIString & config, const PIString & name_): PIObject(name_) {
|
||||
_connections << this;
|
||||
configureFromConfig(config, name_);
|
||||
}
|
||||
|
||||
|
||||
PIConnection::PIConnection(PIString * string, const PIString & name_): PIObject(name_) {
|
||||
_connections << this;
|
||||
configureFromString(string, name_);
|
||||
}
|
||||
|
||||
|
||||
PIConnection::~PIConnection() {
|
||||
__device_pool__->unboundConnection(this);
|
||||
removeAllFilters();
|
||||
_connections.removeAll(this);
|
||||
}
|
||||
|
||||
|
||||
bool PIConnection::configureFromConfig(const PIString & conf_path, const PIString & name_) {
|
||||
PIConfig conf(conf_path, PIIODevice::ReadOnly);
|
||||
return configure(conf, name_);
|
||||
}
|
||||
|
||||
|
||||
bool PIConnection::configureFromString(PIString * string, const PIString & name_) {
|
||||
PIConfig conf(string, PIIODevice::ReadOnly);
|
||||
return configure(conf, name_);
|
||||
}
|
||||
|
||||
|
||||
bool PIConnection::configure(PIConfig & conf, const PIString & name_) {
|
||||
if (!conf.isOpened()) return false;
|
||||
__device_pool__->unboundConnection(this);
|
||||
removeAllSenders();
|
||||
removeAllChannels();
|
||||
removeAllFilters();
|
||||
removeAllDevices();
|
||||
setName(name_);
|
||||
PIConfig::Entry ce(conf.getValue(name_));
|
||||
PIConfig::Branch db(ce.getValue("device").children()), fb(ce.getValue("filter").children()),
|
||||
cb(ce.getValue("channel").children()), sb(ce.getValue("sender").children());
|
||||
PIStringList dev_list(ce.getValue("device").value());
|
||||
PIStringList name_list(ce.getValue("device").name());
|
||||
piForeachC (PIConfig::Entry * e, db) {
|
||||
dev_list << e->value();
|
||||
name_list << e->name();
|
||||
}
|
||||
PIMap<PIString, PIString> dev_aliases;
|
||||
for (int i = 0; i < dev_list.size_s(); ++i) {
|
||||
PIString fn(dev_list[i]);
|
||||
if (fn.isEmpty()) continue;
|
||||
PIString & n(name_list[i]);
|
||||
PIIODevice::DeviceMode dm = PIIODevice::ReadWrite;
|
||||
splitFullPathWithMode(fn, &fn, &dm);
|
||||
//piCout << fn;
|
||||
PIIODevice * dev = addDevice(fn, dm);
|
||||
if (!dev) continue;
|
||||
dev_aliases[n] = fn;
|
||||
device_names[n] = dev;
|
||||
setDeviceName(dev, n);
|
||||
dev->setName(name_ + ".device." + dev_list[i]);
|
||||
PIDiagnostics * diag = diags_.value(dev, 0);
|
||||
if (diag != 0)
|
||||
diag->setDisconnectTimeout(ce.getValue("device." + n + ".disconnectTimeout", diag->disconnectTimeout()));
|
||||
}
|
||||
int added(0), padded(-1), tries(0);
|
||||
bool pdebug = debug();
|
||||
setDebug(false);
|
||||
PIStringList filter_fails;
|
||||
while (added != padded && tries < 100) {
|
||||
padded = added;
|
||||
added = 0;
|
||||
++tries;
|
||||
piForeachC (PIConfig::Entry * e, fb) {
|
||||
PIPacketExtractor::SplitMode sm = PIPacketExtractor::None;
|
||||
PIString sms(e->getValue("splitMode").value());
|
||||
int smi = sms.toInt();
|
||||
if (smi >= 1 && smi <= 5) sm = (PIPacketExtractor::SplitMode)smi;
|
||||
else {
|
||||
sms = sms.trim().toLowerCase();
|
||||
if (sms.find("header") >= 0 && sms.find("footer") >= 0)
|
||||
sm = PIPacketExtractor::HeaderAndFooter;
|
||||
else {
|
||||
if (sms.find("header") >= 0)
|
||||
sm = PIPacketExtractor::Header;
|
||||
else {
|
||||
if (sms.find("footer") >= 0)
|
||||
sm = PIPacketExtractor::Footer;
|
||||
else {
|
||||
if (sms.find("time") >= 0)
|
||||
sm = PIPacketExtractor::Timeout;
|
||||
else {
|
||||
if (sms.find("size") >= 0)
|
||||
sm = PIPacketExtractor::Size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PIStringList devs(e->value());
|
||||
PIConfig::Branch db(e->getValue("device").children());
|
||||
devs << e->getValue("device", "").value();
|
||||
piForeachC (PIConfig::Entry * e2, db)
|
||||
devs << e2->value();
|
||||
devs.removeStrings("");
|
||||
if (devs.isEmpty()) continue;
|
||||
PIString dname = dev_aliases.value(devs.front(), devs.front());
|
||||
PIPacketExtractor * pe = addFilter(e->name(), dname, sm);
|
||||
if (pe == 0) {
|
||||
if (!filter_fails.contains(dname))
|
||||
filter_fails << dname;
|
||||
continue;
|
||||
} else {
|
||||
filter_fails.removeAll(dname);
|
||||
}
|
||||
++added;
|
||||
for (int i = 1; i < devs.size_s(); ++i) {
|
||||
dname = dev_aliases.value(devs[i], devs[i]);
|
||||
if (addFilter(e->name(), dname, sm) != 0) {
|
||||
filter_fails.removeAll(dname);
|
||||
++added;
|
||||
} else {
|
||||
if (!filter_fails.contains(dname))
|
||||
filter_fails << dname;
|
||||
}
|
||||
}
|
||||
PIDiagnostics * diag = diags_.value(pe, 0);
|
||||
if (diag != 0)
|
||||
diag->setDisconnectTimeout(e->getValue("disconnectTimeout", diag->disconnectTimeout()));
|
||||
pe->setPayloadSize(e->getValue("payloadSize", pe->payloadSize()));
|
||||
pe->setPacketSize(e->getValue("packetSize", pe->packetSize()));
|
||||
pe->setTimeout(e->getValue("timeout", pe->timeout()));
|
||||
pe->setHeader(PIByteArray::fromString(e->getValue("header", "").value()));
|
||||
pe->setFooter(PIByteArray::fromString(e->getValue("footer", "").value()));
|
||||
}
|
||||
}
|
||||
setDebug(pdebug);
|
||||
piForeachC (PIString & f, filter_fails)
|
||||
piCoutObj << "\"addFilter\" error: no such device \"" << f << "\"!";
|
||||
piForeachC (PIConfig::Entry * e, cb) {
|
||||
PIString f(e->getValue("from").value()), t(e->getValue("to").value());
|
||||
addChannel(dev_aliases.value(f, f), dev_aliases.value(t, t));
|
||||
}
|
||||
piForeachC (PIConfig::Entry * e, sb) {
|
||||
PIStringList devs(e->value());
|
||||
PIConfig::Branch db(e->getValue("device").children());
|
||||
devs << e->getValue("device", "").value();
|
||||
piForeachC (PIConfig::Entry * e2, db)
|
||||
devs << e2->value();
|
||||
devs.removeStrings("");
|
||||
if (devs.isEmpty()) continue;
|
||||
float freq = e->getValue("frequency");
|
||||
piForeachC (PIString & d, devs)
|
||||
addSender(e->name(), dev_aliases.value(d, d), freq);
|
||||
PIByteArray fd(PIByteArray::fromString(e->getValue("fixedData").value()));
|
||||
setSenderFixedData(e->name(), fd);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
PIString PIConnection::makeConfig() const {
|
||||
PIString ret;
|
||||
ret << "[" << name() << "]\n";
|
||||
PIVector<PIIODevice * > devs(boundedDevices());
|
||||
int dn(-1);
|
||||
piForeachC (PIIODevice * d, devs) {
|
||||
PIStringList dnl(deviceNames(d));
|
||||
if (dnl.isEmpty()) dnl << PIString::fromNumber(++dn);
|
||||
piForeachC (PIString & dname, dnl) {
|
||||
ret << "device." << dname << " = " << d->constructFullPath();
|
||||
if (d->mode() == PIIODevice::ReadOnly) ret << " (ro)";
|
||||
if (d->mode() == PIIODevice::WriteOnly) ret << " (wo)";
|
||||
ret << " #s\n";
|
||||
PIDiagnostics * diag = diags_.value(const_cast<PIIODevice * >(d), 0);
|
||||
if (diag != 0)
|
||||
ret << "device." << dname << ".disconnectTimeout = " << diag->disconnectTimeout() << " #f\n";
|
||||
}
|
||||
}
|
||||
piForeachC (PEPair & f, extractors) {
|
||||
if (f.second == 0) continue;
|
||||
if (f.second->extractor == 0) continue;
|
||||
PIString prefix = "filter." + f.first;
|
||||
for (int i = 0; i < f.second->devices.size_s(); ++i)
|
||||
ret << prefix << ".device." << i << " = " << devPath(f.second->devices[i]) << " #s\n";
|
||||
PIDiagnostics * diag = diags_.value(f.second->extractor, 0);
|
||||
if (diag != 0)
|
||||
ret << prefix << ".disconnectTimeout = " << diag->disconnectTimeout() << " #f\n";
|
||||
ret << prefix << ".splitMode = ";
|
||||
switch (f.second->extractor->splitMode()) {
|
||||
case PIPacketExtractor::None: ret << "none"; break;
|
||||
case PIPacketExtractor::Header: ret << "header"; break;
|
||||
case PIPacketExtractor::Footer: ret << "footer"; break;
|
||||
case PIPacketExtractor::HeaderAndFooter: ret << "header & footer"; break;
|
||||
case PIPacketExtractor::Size: ret << "size"; break;
|
||||
case PIPacketExtractor::Timeout: ret << "timeout"; break;
|
||||
}
|
||||
ret << " #s\n";
|
||||
ret << prefix << ".payloadSize = " << f.second->extractor->payloadSize() << " #n\n";
|
||||
ret << prefix << ".packetSize = " << f.second->extractor->packetSize() << " #n\n";
|
||||
ret << prefix << ".timeout = " << f.second->extractor->timeout() << " #f\n";
|
||||
ret << prefix << ".header = " << f.second->extractor->header().toString() << " #s\n";
|
||||
ret << prefix << ".footer = " << f.second->extractor->footer().toString() << " #s\n";
|
||||
}
|
||||
dn = 0;
|
||||
piForeachC (CPair & c, channels_) {
|
||||
piForeachC (PIIODevice * d, c.second) {
|
||||
PIString prefix = "channel." + PIString::fromNumber(dn); ++dn;
|
||||
ret << prefix << ".from = " << devPath(c.first) << " #s\n";
|
||||
ret << prefix << ".to = " << devPath(d) << " #s\n";
|
||||
}
|
||||
}
|
||||
piForeachC (SPair & s, senders) {
|
||||
if (s.second == 0) continue;
|
||||
PIString prefix = "sender." + s.second->name();
|
||||
for (int i = 0; i < s.second->devices.size_s(); ++i)
|
||||
ret << prefix << ".device." << i << " = " << devPath(s.second->devices[i]) << " #s\n";
|
||||
double int_ = s.second->int_;
|
||||
if (int_ > 0.)
|
||||
ret << prefix << ".frequency = " << (1000. / int_) << " #f\n";
|
||||
if (!s.second->sdata.isEmpty())
|
||||
ret << prefix << ".fixedData = " << s.second->sdata.toString() << " #s\n";
|
||||
}
|
||||
ret << "[]\n";
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIIODevice * PIConnection::addDevice(const PIString & full_path, PIIODevice::DeviceMode mode, bool start) {
|
||||
PIString fp(PIIODevice::normalizeFullPath(full_path));
|
||||
PIIODevice * dev = __device_pool__->addDevice(this, fp, mode, start);
|
||||
if (dev) {
|
||||
dev->setName(name() + ".device." + fp);
|
||||
device_modes[dev] = mode;
|
||||
__device_pool__->lock();
|
||||
if (diags_.value(dev, 0) == 0) {
|
||||
PIDiagnostics * d = new PIDiagnostics(false);
|
||||
diags_[dev] = d;
|
||||
CONNECTU(d, qualityChanged, this, diagQualityChanged);
|
||||
}
|
||||
__device_pool__->unlock();
|
||||
}
|
||||
return dev;
|
||||
}
|
||||
|
||||
|
||||
PIStringList PIConnection::deviceNames(const PIIODevice *dev) const {
|
||||
PIStringList ret;
|
||||
piForeachC (DNPair & s, device_names)
|
||||
if (s.second == dev)
|
||||
ret << s.first;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool PIConnection::removeDevice(const PIString & full_path) {
|
||||
PIString fp(PIIODevice::normalizeFullPath(full_path));
|
||||
PIIODevice * dev = __device_pool__->device(fp);
|
||||
if (dev == 0) return false;
|
||||
PIStringList dntd(deviceNames(dev));
|
||||
piForeachC (PIString & n, dntd)
|
||||
device_names.removeOne(n);
|
||||
piForeachC (SPair & s, senders) {
|
||||
if (s.second == 0) continue;
|
||||
s.second->lock();
|
||||
s.second->devices.removeAll(dev);
|
||||
s.second->unlock();
|
||||
}
|
||||
device_modes.remove(dev);
|
||||
piForeachC (PEPair & i, extractors) {
|
||||
if (i.second == 0) continue;
|
||||
i.second->devices.removeAll(dev);
|
||||
}
|
||||
bounded_extractors.remove(dev);
|
||||
channels_.remove(dev);
|
||||
for (PIMap<PIIODevice * , PIVector<PIIODevice * > >::iterator it = channels_.begin(); it != channels_.end(); ++it)
|
||||
it.value().removeAll(dev);
|
||||
__device_pool__->lock();
|
||||
if (diags_.value(dev, 0) != 0)
|
||||
delete diags_.value(dev);
|
||||
diags_.remove(dev);
|
||||
__device_pool__->unlock();
|
||||
return __device_pool__->removeDevice(this, fp);
|
||||
}
|
||||
|
||||
|
||||
void PIConnection::removeAllDevices() {
|
||||
device_names.clear();
|
||||
PIVector<PIIODevice * > bdevs(__device_pool__->boundedDevices(this));
|
||||
__device_pool__->lock();
|
||||
piForeach (PIIODevice * d, bdevs) {
|
||||
piForeachC (SPair & s, senders) {
|
||||
if (s.second == 0) continue;
|
||||
s.second->lock();
|
||||
s.second->devices.removeAll(d);
|
||||
s.second->unlock();
|
||||
}
|
||||
channels_.remove(d);
|
||||
for (PIMap<PIIODevice * , PIVector<PIIODevice * > >::iterator it = channels_.begin(); it != channels_.end(); ++it)
|
||||
it.value().removeAll(d);
|
||||
if (diags_.value(d, 0) != 0)
|
||||
delete diags_.value(d);
|
||||
diags_.remove(d);
|
||||
}
|
||||
__device_pool__->unboundConnection(this);
|
||||
__device_pool__->unlock();
|
||||
device_modes.clear();
|
||||
bounded_extractors.clear();
|
||||
piForeachC (PEPair & i, extractors) {
|
||||
if (i.second == 0) continue;
|
||||
i.second->devices.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PIIODevice * PIConnection::deviceByFullPath(const PIString & full_path) const {
|
||||
PIString fp(PIIODevice::normalizeFullPath(full_path));
|
||||
DevicePool::DeviceData * dd = __device_pool__->devices.value(fp);
|
||||
if (dd == 0) return 0;
|
||||
if (dd->dev == 0) return 0;
|
||||
if (!dd->listeners.contains(const_cast<PIConnection * >(this))) return 0;
|
||||
return dd->dev;
|
||||
}
|
||||
|
||||
|
||||
PIIODevice * PIConnection::deviceByName(const PIString & name) const {
|
||||
return device_names.value(name, 0);
|
||||
}
|
||||
|
||||
|
||||
PIVector<PIIODevice * > PIConnection::boundedDevices() const {
|
||||
return __device_pool__->boundedDevices(this);
|
||||
}
|
||||
|
||||
|
||||
PIPacketExtractor * PIConnection::addFilter(const PIString & name_, const PIString & full_path, PIPacketExtractor::SplitMode mode) {
|
||||
PIString fp(PIIODevice::normalizeFullPath(full_path));
|
||||
PIString fname_ = name_.trimmed();
|
||||
Extractor * e = extractors.value(fname_);
|
||||
if (full_path.isEmpty()) return (e == 0 ? 0 : e->extractor);
|
||||
PIIODevice * dev = deviceByFullPath(fp);
|
||||
PIPacketExtractor * pe(0);
|
||||
if (extractors.value(full_path) != 0) pe = extractors.value(full_path)->extractor;
|
||||
if (pe != 0) dev = pe;
|
||||
if (dev == 0) {
|
||||
piCoutObj << "\"addFilter\" error: no such device \"" << full_path << "\"!";
|
||||
return 0;
|
||||
}
|
||||
if (e == 0) {
|
||||
e = new Extractor();
|
||||
extractors[fname_] = e;
|
||||
}
|
||||
if (e->extractor == 0) {
|
||||
e->extractor = new PIPacketExtractor(0, mode);
|
||||
e->extractor->setName(fname_);
|
||||
e->extractor->setThreadedReadData(new PIPair<PIConnection * , PIString>(this, fname_));
|
||||
e->extractor->setHeaderCheckSlot(filterValidateHeaderS);
|
||||
e->extractor->setFooterCheckSlot(filterValidateFooterS);
|
||||
e->extractor->setPayloadCheckSlot(filterValidatePayloadS);
|
||||
__device_pool__->lock();
|
||||
if (diags_.value(e->extractor, 0) == 0) {
|
||||
PIDiagnostics * d = new PIDiagnostics(false);
|
||||
diags_[e->extractor] = d;
|
||||
CONNECTU(d, qualityChanged, this, diagQualityChanged);
|
||||
}
|
||||
__device_pool__->unlock();
|
||||
CONNECT2(void, uchar * , int, e->extractor, packetReceived, this, packetExtractorReceived)
|
||||
}
|
||||
if (!e->devices.contains(dev)) {
|
||||
bounded_extractors[dev] << e->extractor;
|
||||
//if (PIString(dev->className()) == "PIPacketExtractor") dev->setThreadSafe(false);
|
||||
e->devices << dev;
|
||||
}
|
||||
return e->extractor;
|
||||
}
|
||||
|
||||
|
||||
bool PIConnection::removeFilter(const PIString & name_, const PIString & full_path) {
|
||||
PIString fp(PIIODevice::normalizeFullPath(full_path));
|
||||
Extractor * p = extractors.value(name_.trimmed());
|
||||
if (p == 0) return false;
|
||||
bool ret = false;
|
||||
for (int i = 0; i < p->devices.size_s(); ++i) {
|
||||
if (devFPath(p->devices[i]) == fp || devFPath(p->devices[i]) == full_path) {
|
||||
bounded_extractors[p->devices[i]].removeAll(p->extractor);
|
||||
p->devices.remove(i);
|
||||
--i;
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
if (p->devices.isEmpty()) {
|
||||
unboundExtractor(p->extractor);
|
||||
delete p;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool PIConnection::removeFilter(const PIString & name, const PIIODevice * dev) {
|
||||
if (dev == 0) return false;
|
||||
return removeFilter(name, devFPath(dev));
|
||||
}
|
||||
|
||||
|
||||
bool PIConnection::removeFilter(const PIString & name_) {
|
||||
Extractor * p = extractors.value(name_.trimmed());
|
||||
if (p == 0) return false;
|
||||
unboundExtractor(p->extractor);
|
||||
delete p;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void PIConnection::removeAllFilters() {
|
||||
__device_pool__->lock();
|
||||
piForeachC (PEPair & i, extractors) {
|
||||
if (i.second == 0) continue;
|
||||
channels_.remove(i.second->extractor);
|
||||
for (PIMap<PIIODevice * , PIVector<PIIODevice * > >::iterator it = channels_.begin(); it != channels_.end(); ++it)
|
||||
it.value().removeAll(i.second->extractor);
|
||||
if (diags_.value(i.second->extractor, 0) != 0)
|
||||
delete diags_.value(i.second->extractor);
|
||||
diags_.remove(i.second->extractor);
|
||||
delete i.second;
|
||||
}
|
||||
extractors.clear();
|
||||
bounded_extractors.clear();
|
||||
__device_pool__->unlock();
|
||||
}
|
||||
|
||||
|
||||
PIVector<PIPacketExtractor * > PIConnection::filters() const {
|
||||
PIVector<PIPacketExtractor * > ret;
|
||||
piForeachC (PEPair & i, extractors)
|
||||
if (i.second != 0)
|
||||
if (i.second->extractor != 0) ret << i.second->extractor;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIStringList PIConnection::filterNames() const {
|
||||
PIStringList ret;
|
||||
piForeachC (PEPair & i, extractors)
|
||||
if (i.second != 0)
|
||||
if (i.second->extractor != 0) ret << i.first;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIPacketExtractor * PIConnection::filter(const PIString & name_) const {
|
||||
PIString fname_ = name_.trimmed();
|
||||
piForeachC (PEPair & i, extractors)
|
||||
if (i.second != 0)
|
||||
if (i.second->extractor != 0 && i.first == fname_)
|
||||
return i.second->extractor;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
PIVector<PIIODevice * > PIConnection::filterBoundedDevices(const PIString & name_) const {
|
||||
PIVector<PIIODevice * > ret;
|
||||
Extractor * p = extractors.value(name_.trimmed());
|
||||
if (p == 0) return ret;
|
||||
return p->devices;
|
||||
}
|
||||
|
||||
|
||||
bool PIConnection::addChannel(const PIString & name0, const PIString & name1) {
|
||||
//piCout << "addChannel" << name0 << name1;
|
||||
if (name0.isEmpty() || name1.isEmpty()) return false;
|
||||
PIIODevice * dev0 = deviceByFullPath(name0), * dev1 = deviceByFullPath(name1);
|
||||
PIPacketExtractor * pe0(0), * pe1(0);
|
||||
if (extractors.value(name0) != 0) pe0 = extractors.value(name0)->extractor;
|
||||
if (extractors.value(name1) != 0) pe1 = extractors.value(name1)->extractor;
|
||||
if (pe0 != 0) dev0 = pe0;
|
||||
if (pe1 != 0) dev1 = pe1;
|
||||
if (dev0 == 0 || dev1 == 0) {
|
||||
if (dev0 == 0) piCoutObj << "\"addChannel\" error: no such device \"" << name0 << "\"!";
|
||||
if (dev1 == 0) piCoutObj << "\"addChannel\" error: no such device \"" << name1 << "\"!";
|
||||
return false;
|
||||
}
|
||||
if (!channels_[dev0].contains(dev1))
|
||||
channels_[dev0] << dev1;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PIConnection::removeChannel(const PIString & name0, const PIString & name1) {
|
||||
PIIODevice * dev0 = deviceByFullPath(name0), * dev1 = deviceByFullPath(name1);
|
||||
PIPacketExtractor * pe0(0), * pe1(0);
|
||||
if (extractors.value(name0) != 0) pe0 = extractors.value(name0)->extractor;
|
||||
if (extractors.value(name1) != 0) pe1 = extractors.value(name1)->extractor;
|
||||
if (pe0 != 0) dev0 = pe0;
|
||||
if (pe1 != 0) dev1 = pe1;
|
||||
if (dev0 == 0 || dev1 == 0) return false;
|
||||
channels_[dev0].removeAll(dev1);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PIConnection::removeChannel(const PIString & name0) {
|
||||
PIIODevice * dev0 = deviceByFullPath(name0);
|
||||
PIPacketExtractor * pe0(0);
|
||||
if (extractors.value(name0) != 0) pe0 = extractors.value(name0)->extractor;
|
||||
if (pe0 != 0) dev0 = pe0;
|
||||
if (dev0 == 0) return false;
|
||||
channels_.remove(dev0);
|
||||
for (PIMap<PIIODevice * , PIVector<PIIODevice * > >::iterator it = channels_.begin(); it != channels_.end(); ++it)
|
||||
it.value().removeAll(dev0);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void PIConnection::removeAllChannels() {
|
||||
channels_.clear();
|
||||
}
|
||||
|
||||
|
||||
PIString PIConnection::devPath(const PIIODevice * d) const {
|
||||
if (d == 0) return PIString();
|
||||
if (strcmp(d->className(), "PIPacketExtractor") == 0) return d->name();
|
||||
return d->constructFullPath();
|
||||
}
|
||||
|
||||
|
||||
PIString PIConnection::devFPath(const PIIODevice * d) const {
|
||||
if (d == 0) return PIString();
|
||||
if (d->isPropertyExists("__fullPath__")) return d->property("__fullPath__").toString();
|
||||
return d->name();
|
||||
}
|
||||
|
||||
|
||||
PIVector<PIPair<PIString, PIString > > PIConnection::channels() const {
|
||||
PIVector<PIPair<PIString, PIString > > ret;
|
||||
piForeachC (CPair & i, channels_) {
|
||||
PIString fp0(devFPath(i.first));
|
||||
piForeachC (PIIODevice * d, i.second)
|
||||
ret << PIPair<PIString, PIString>(fp0, devFPath(d));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void PIConnection::addSender(const PIString & name_, const PIString & full_path, float frequency, bool start_) {
|
||||
PIString fp(PIIODevice::normalizeFullPath(full_path));
|
||||
PIString fname_ = name_.trimmed();
|
||||
if (fp.isEmpty() || frequency <= 0.) return;
|
||||
Sender * s = senders.value(fname_);
|
||||
PIIODevice * dev = deviceByFullPath(fp);
|
||||
if (s == 0) {
|
||||
s = new Sender(this);
|
||||
s->setName(fname_);
|
||||
s->int_ = 1000. / frequency;
|
||||
senders[fname_] = s;
|
||||
}
|
||||
if (dev == 0) {
|
||||
piCoutObj << "\"addSender\" error: no such device \"" << full_path << "\"!";
|
||||
return;
|
||||
}
|
||||
if (!s->isRunning() && start_) {
|
||||
//piCoutObj << name_ << "start" << 1000. / frequency;
|
||||
if (!__device_pool__->fake) s->start(s->int_);
|
||||
}
|
||||
s->lock();
|
||||
if (!s->devices.contains(dev))
|
||||
s->devices << dev;
|
||||
s->unlock();
|
||||
}
|
||||
|
||||
|
||||
bool PIConnection::removeSender(const PIString & name, const PIString & full_path) {
|
||||
PIString fp(PIIODevice::normalizeFullPath(full_path));
|
||||
Sender * s = senders.value(name, 0);
|
||||
PIIODevice * d = deviceByFullPath(fp);
|
||||
if (s == 0 || d == 0) return false;
|
||||
s->lock();
|
||||
bool ret = s->devices.contains(d);
|
||||
if (ret)
|
||||
s->devices.removeAll(d);
|
||||
s->unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool PIConnection::removeSender(const PIString & name) {
|
||||
Sender * s = senders.value(name, 0);
|
||||
if (s == 0) return false;
|
||||
delete s;
|
||||
senders.remove(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PIConnection::setSenderFixedData(const PIString & name, const PIByteArray & data) {
|
||||
Sender * s = senders.value(name, 0);
|
||||
if (s == 0) return false;
|
||||
s->lock();
|
||||
s->sdata = data;
|
||||
s->unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PIConnection::clearSenderFixedData(const PIString & name) {
|
||||
Sender * s = senders.value(name, 0);
|
||||
if (s == 0) return false;
|
||||
s->lock();
|
||||
s->sdata.clear();
|
||||
s->unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
PIByteArray PIConnection::senderFixedData(const PIString & name) const {
|
||||
Sender * s = senders.value(name, 0);
|
||||
if (s == 0) return PIByteArray();
|
||||
return s->sdata;
|
||||
}
|
||||
|
||||
|
||||
float PIConnection::senderFrequency(const PIString & name) const {
|
||||
Sender * s = senders.value(name, 0);
|
||||
if (s == 0) return -1.f;
|
||||
double i = s->interval();
|
||||
if (i == 0.) return 0.f;
|
||||
return 1000. / s->interval();
|
||||
}
|
||||
|
||||
|
||||
void PIConnection::removeAllSenders() {
|
||||
piForeachC (SPair & s, senders)
|
||||
if (s.second != 0)
|
||||
delete s.second;
|
||||
senders.clear();
|
||||
}
|
||||
|
||||
|
||||
void PIConnection::startThreadedRead(const PIString & full_path) {
|
||||
PIString fp(PIIODevice::normalizeFullPath(full_path));
|
||||
DevicePool::DeviceData * dd = __device_pool__->devices.value(fp, 0);
|
||||
if (dd == 0) return;
|
||||
if (dd->dev == 0) return;
|
||||
if (dd->started || dd->dev->mode() == PIIODevice::WriteOnly) return;
|
||||
if (!__device_pool__->fake) dd->rthread->start();
|
||||
dd->started = true;
|
||||
}
|
||||
|
||||
|
||||
void PIConnection::startAllThreadedReads() {
|
||||
piForeachC (DevicePool::DDPair & d, __device_pool__->devices)
|
||||
startThreadedRead(d.first);
|
||||
}
|
||||
|
||||
|
||||
void PIConnection::startSender(const PIString & name) {
|
||||
Sender * s = senders.value(name, 0);
|
||||
if (s == 0) return;
|
||||
if (!s->isRunning() && !__device_pool__->fake)
|
||||
s->start(s->int_);
|
||||
}
|
||||
|
||||
|
||||
void PIConnection::startAllSenders() {
|
||||
piForeachC (SPair & s, senders) {
|
||||
if (s.second == 0) continue;
|
||||
if (!s.second->isRunning() && !__device_pool__->fake)
|
||||
s.second->start(s.second->int_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIConnection::stopThreadedRead(const PIString & full_path) {
|
||||
PIString fp(PIIODevice::normalizeFullPath(full_path));
|
||||
DevicePool::DeviceData * dd = __device_pool__->devices.value(fp, 0);
|
||||
if (dd == 0) return;
|
||||
if (dd->dev == 0) return;
|
||||
if (!dd->started || dd->dev->mode() == PIIODevice::WriteOnly) return;
|
||||
dd->rthread->stop();
|
||||
dd->started = false;
|
||||
}
|
||||
|
||||
|
||||
void PIConnection::stopAllThreadedReads() {
|
||||
piForeachC (DevicePool::DDPair & d, __device_pool__->devices)
|
||||
stopThreadedRead(d.first);
|
||||
}
|
||||
|
||||
|
||||
void PIConnection::stopSender(const PIString & name) {
|
||||
Sender * s = senders.value(name, 0);
|
||||
if (s == 0) return;
|
||||
if (s->isRunning()) s->stop();
|
||||
}
|
||||
|
||||
|
||||
void PIConnection::stopAllSenders() {
|
||||
piForeachC (SPair & s, senders) {
|
||||
if (s.second == 0) continue;
|
||||
if (s.second->isRunning())
|
||||
s.second->stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PIDiagnostics * PIConnection::diagnostic(const PIString & full_path_name) const {
|
||||
PIIODevice * dev = deviceByFullPath(full_path_name);
|
||||
if (dev == 0) dev = device_names.value(full_path_name, 0);
|
||||
PIPacketExtractor * pe(0);
|
||||
if (extractors.value(full_path_name) != 0) pe = extractors.value(full_path_name)->extractor;
|
||||
if (pe != 0) dev = pe;
|
||||
if (dev == 0) return 0;
|
||||
return diags_.value(dev, 0);
|
||||
}
|
||||
|
||||
|
||||
int PIConnection::writeByFullPath(const PIString & full_path, const PIByteArray & data) {
|
||||
PIString fp(full_path);
|
||||
if (fp.endsWith(")"))
|
||||
splitFullPathWithMode(fp, &fp, 0);
|
||||
fp = PIIODevice::normalizeFullPath(fp);
|
||||
PIIODevice * dev = __device_pool__->device(fp);
|
||||
//piCout << "SEND" << full_path << fp;
|
||||
return write(dev, data);
|
||||
}
|
||||
|
||||
|
||||
int PIConnection::writeByName(const PIString & name, const PIByteArray & data) {
|
||||
PIIODevice * dev = deviceByName(name);
|
||||
return write(dev, data);
|
||||
}
|
||||
|
||||
|
||||
int PIConnection::write(PIIODevice * dev, const PIByteArray & data) {
|
||||
if (dev == 0) {
|
||||
piCoutObj << "Null Device!";
|
||||
return -1;
|
||||
}
|
||||
if (!dev->canWrite()) {
|
||||
piCoutObj << "Device \"" << dev->constructFullPath() << "\" can`t write!";
|
||||
return -1;
|
||||
}
|
||||
int ret = dev->write(data);
|
||||
PIDiagnostics * diag = diags_.value(dev);
|
||||
if (diag != 0 && ret > 0) diag->sended(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIVector< PIConnection * > PIConnection::allConnections() {
|
||||
return _connections;
|
||||
}
|
||||
|
||||
|
||||
PIVector< PIIODevice * > PIConnection::allDevices() {
|
||||
return __device_pool__->boundedDevices();
|
||||
}
|
||||
|
||||
|
||||
bool PIConnection::setFakeMode(bool yes) {
|
||||
bool ret = isFakeMode();
|
||||
__device_pool__->fake = yes;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool PIConnection::isFakeMode() {
|
||||
return __device_pool__->fake;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
PIIODevice * PIConnection::DevicePool::addDevice(PIConnection * parent, const PIString & fp, PIIODevice::DeviceMode mode, bool start) {
|
||||
DeviceData * dd = devices[fp];
|
||||
int pmode(0);
|
||||
bool need_start = false;
|
||||
if (dd == 0) {
|
||||
dd = new DeviceData();
|
||||
devices[fp] = dd;
|
||||
}
|
||||
if (dd->dev == 0) {
|
||||
//piCout << "new device" << fp;
|
||||
dd->dev = PIIODevice::createFromFullPath(fp);
|
||||
if (dd->dev == 0) {
|
||||
piCoutObj << "Error: can`t create device \"" << fp << "\"!"; //:" << errorString();
|
||||
return 0;
|
||||
}
|
||||
dd->dev->setProperty("__fullPath__", fp);
|
||||
} else
|
||||
pmode = dd->dev->mode();
|
||||
if (!dd->listeners.contains(parent))
|
||||
dd->listeners << parent;
|
||||
if (pmode == mode || pmode == PIIODevice::ReadWrite)
|
||||
return dd->dev;
|
||||
if ((mode & PIIODevice::ReadOnly) > 0) {
|
||||
if (dd->rthread != 0) {
|
||||
delete dd->rthread;
|
||||
dd->rthread = 0;
|
||||
dd->started = false;
|
||||
}
|
||||
dd->rthread = new PIThread(dd, threadReadDP);
|
||||
dd->rthread->setName("__S__connection_" + fp + "_read_thread");
|
||||
need_start = true;
|
||||
pmode |= PIIODevice::ReadOnly;
|
||||
}
|
||||
if ((mode & PIIODevice::WriteOnly) > 0)
|
||||
pmode |= PIIODevice::WriteOnly;
|
||||
if (!fake) {
|
||||
dd->dev->close();
|
||||
dd->dev->open((PIIODevice::DeviceMode)pmode);
|
||||
}
|
||||
if (need_start && start) {
|
||||
if (!fake) dd->rthread->start();
|
||||
dd->started = true;
|
||||
}
|
||||
return dd->dev;
|
||||
}
|
||||
|
||||
|
||||
bool PIConnection::DevicePool::removeDevice(PIConnection * parent, const PIString & fp) {
|
||||
DeviceData * dd = devices.value(fp);
|
||||
if (dd == 0)
|
||||
return false;
|
||||
if (dd->dev == 0)
|
||||
return false;
|
||||
bool ok = dd->listeners.contains(parent);
|
||||
dd->listeners.removeAll(parent);
|
||||
if (dd->listeners.isEmpty()) {
|
||||
delete dd;
|
||||
devices.remove(fp);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
void PIConnection::DevicePool::unboundConnection(PIConnection * parent) {
|
||||
PIStringList rem;
|
||||
piForeachC (DDPair & i, devices) {
|
||||
if (i.second == 0) {
|
||||
rem << i.first;
|
||||
continue;
|
||||
}
|
||||
i.second->listeners.removeAll(parent);
|
||||
if (i.second->listeners.isEmpty())
|
||||
rem << i.first;
|
||||
}
|
||||
piForeachC (PIString & i, rem) {
|
||||
DeviceData * dd = devices.value(i);
|
||||
if (dd == 0)
|
||||
continue;
|
||||
delete dd;
|
||||
devices.remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PIIODevice * PIConnection::DevicePool::device(const PIString & fp) const {
|
||||
DeviceData * dd = devices.value(fp);
|
||||
if (dd == 0) return 0;
|
||||
return dd->dev;
|
||||
}
|
||||
|
||||
|
||||
PIVector<PIConnection * > PIConnection::DevicePool::boundedConnections() const {
|
||||
PIVector<PIConnection * > ret;
|
||||
piForeachC (DDPair & i, devices) {
|
||||
if (i.second == 0)
|
||||
continue;
|
||||
ret << i.second->listeners;
|
||||
}
|
||||
for (int i = 0; i < ret.size_s(); ++i)
|
||||
for (int j = i + 1; j < ret.size_s(); ++j)
|
||||
if (ret[i] == ret[j]) {
|
||||
ret.remove(j);
|
||||
--j;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIVector< PIIODevice * > PIConnection::DevicePool::boundedDevices() const {
|
||||
PIVector<PIIODevice * > ret;
|
||||
piForeachC (DDPair & i, devices) {
|
||||
if (i.second == 0) continue;
|
||||
if (i.second->dev == 0) continue;
|
||||
ret << i.second->dev;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIVector<PIIODevice * > PIConnection::DevicePool::boundedDevices(const PIConnection * parent) const {
|
||||
PIVector<PIIODevice * > ret;
|
||||
piForeachC (DDPair & i, devices) {
|
||||
if (i.second == 0) continue;
|
||||
if (i.second->dev == 0) continue;
|
||||
if (i.second->listeners.contains(const_cast<PIConnection*>(parent)))
|
||||
ret << i.second->dev;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIConnection::DevicePool::DeviceData::~DeviceData() {
|
||||
if (rthread != 0) {
|
||||
rthread->stop();
|
||||
delete rthread;
|
||||
rthread = 0;
|
||||
}
|
||||
if (dev != 0) {
|
||||
delete dev;
|
||||
dev = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIConnection::DevicePool::run() {
|
||||
PIVector<PIConnection * > conns(PIConnection::allConnections());
|
||||
piForeach (PIConnection * c, conns) {
|
||||
piForeachC (PIConnection::DPair & d, c->diags_) {
|
||||
if (d.second == 0) continue;
|
||||
d.second->tick(0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIConnection::DevicePool::threadReadDP(void * ddp) {
|
||||
DeviceData * dd((DeviceData * )ddp);
|
||||
if (dd->dev == 0) {piMSleep(100); return;}
|
||||
PIByteArray ba;
|
||||
ba = dd->dev->read(dd->dev->threadedReadBufferSize());
|
||||
if (ba.isEmpty()) {piMSleep(10); return;}
|
||||
//piCout << "Readed from" << dd->dev->path() << Hex << ba;
|
||||
__device_pool__->deviceReaded(dd, ba);
|
||||
}
|
||||
|
||||
|
||||
void PIConnection::DevicePool::deviceReaded(PIConnection::DevicePool::DeviceData * dd, const PIByteArray & data) {
|
||||
PIString from = dd->dev->property("__fullPath__").toString();
|
||||
piForeach (PIConnection * ld, dd->listeners)
|
||||
ld->rawReceived(dd->dev, from, data);
|
||||
}
|
||||
|
||||
|
||||
void PIConnection::splitFullPathWithMode(PIString fpwm, PIString * full_path, PIIODevice::DeviceMode * mode) {
|
||||
PIIODevice::DeviceMode dm = PIIODevice::ReadWrite;
|
||||
if (fpwm.find("(") > 0 && fpwm.find(")") > 0) {
|
||||
PIString dms(fpwm.right(fpwm.length() - fpwm.find("(")).takeRange("(", ")").trim().toLowerCase().removeAll(" "));
|
||||
//piCout << dms;
|
||||
if (dms == "r" || dms == "ro" || dms == "read" || dms == "readonly")
|
||||
dm = PIIODevice::ReadOnly;
|
||||
if (dms == "w" || dms == "wo" || dms == "write" || dms == "writeonly")
|
||||
dm = PIIODevice::WriteOnly;
|
||||
fpwm.cutRight(fpwm.length() - fpwm.find("(") + 1).trim();
|
||||
}
|
||||
if (full_path) *full_path = fpwm;
|
||||
if (mode) *mode = dm;
|
||||
}
|
||||
|
||||
|
||||
bool PIConnection::filterValidateHeaderS(void * c, uchar * src, uchar * rec, int size) {
|
||||
PIPair<PIConnection * , PIString> * p((PIPair<PIConnection * , PIString> * )c);
|
||||
return p->first->filterValidateHeader(p->second, src, rec, size);
|
||||
}
|
||||
|
||||
|
||||
bool PIConnection::filterValidateFooterS(void * c, uchar * src, uchar * rec, int size) {
|
||||
PIPair<PIConnection * , PIString> * p((PIPair<PIConnection * , PIString> * )c);
|
||||
return p->first->filterValidateFooter(p->second, src, rec, size);
|
||||
}
|
||||
|
||||
|
||||
bool PIConnection::filterValidatePayloadS(void * c, uchar * rec, int size) {
|
||||
PIPair<PIConnection * , PIString> * p((PIPair<PIConnection * , PIString> * )c);
|
||||
return p->first->filterValidatePayload(p->second, rec, size);
|
||||
}
|
||||
|
||||
|
||||
void PIConnection::rawReceived(PIIODevice * dev, const PIString & from, const PIByteArray & data) {
|
||||
dataReceived(from, data);
|
||||
dataReceivedEvent(from, data);
|
||||
PIVector<PIPacketExtractor * > be(bounded_extractors.value(dev));
|
||||
//piCout << be;
|
||||
piForeach (PIPacketExtractor * i, be)
|
||||
i->threadedRead(const_cast<uchar * >(data.data()), data.size_s());
|
||||
PIVector<PIIODevice * > chd(channels_.value(dev));
|
||||
piForeach (PIIODevice * d, chd) {
|
||||
int ret = d->write(data);
|
||||
PIDiagnostics * diag = diags_.value(d);
|
||||
if (diag != 0 && ret > 0) diag->sended(ret);
|
||||
}
|
||||
PIDiagnostics * diag = diags_.value(dev);
|
||||
if (diag != 0) diag->received(data.size_s());
|
||||
}
|
||||
|
||||
|
||||
bool PIConnection::filterValidateHeader(const PIString & filter_name, uchar * src, uchar * rec, int size) {
|
||||
for (int i = 0; i < size; ++i)
|
||||
if (src[i] != rec[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PIConnection::filterValidateFooter(const PIString & filter_name, uchar * src, uchar * rec, int size) {
|
||||
for (int i = 0; i < size; ++i)
|
||||
if (src[i] != rec[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PIConnection::filterValidatePayload(const PIString & filter_name, uchar * rec, int size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
PIByteArray PIConnection::senderData(const PIString & sender_name) {
|
||||
return PIByteArray();
|
||||
}
|
||||
|
||||
|
||||
PIConnection::Extractor::~Extractor() {
|
||||
if (extractor != 0) {
|
||||
if (extractor->threadedReadData() != 0)
|
||||
delete (PIPair<PIConnection * , PIString> * )(extractor->threadedReadData());
|
||||
delete extractor;
|
||||
extractor = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIConnection::Sender::tick(void * , int) {
|
||||
if (parent == 0) return;
|
||||
PIByteArray data;
|
||||
if (!sdata.isEmpty()) data = sdata;
|
||||
else data = parent->senderData(name());
|
||||
if (data.isEmpty()) return;
|
||||
//piCoutObj << "write"<<data.size()<<"bytes to"<<devices.size()<<"devices";
|
||||
piForeach (PIIODevice * d, devices) {
|
||||
int ret = d->write(data);
|
||||
PIDiagnostics * diag = parent->diags_.value(d);
|
||||
if (diag != 0 && ret > 0) diag->sended(ret);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIConnection::unboundExtractor(PIPacketExtractor * pe) {
|
||||
if (pe == 0) return;
|
||||
channels_.remove(pe);
|
||||
for (PIMap<PIIODevice * , PIVector<PIIODevice * > >::iterator it = channels_.begin(); it != channels_.end(); ++it)
|
||||
it.value().removeAll(pe);
|
||||
bounded_extractors.remove(pe);
|
||||
PIVector<PIIODevice * > k = bounded_extractors.keys();
|
||||
piForeach (PIIODevice * i, k) {
|
||||
PIVector<PIPacketExtractor * > & be(bounded_extractors[i]);
|
||||
be.removeAll(pe);
|
||||
if (be.isEmpty())
|
||||
bounded_extractors.remove(i);
|
||||
}
|
||||
__device_pool__->lock();
|
||||
if (diags_.value(pe, 0) != 0)
|
||||
delete diags_.value(pe);
|
||||
diags_.remove(pe);
|
||||
extractors.remove(pe->name());
|
||||
__device_pool__->unlock();
|
||||
}
|
||||
|
||||
|
||||
void PIConnection::packetExtractorReceived(uchar * data, int size) {
|
||||
PIString from(emitter() == 0 ? "" : emitter()->name());
|
||||
packetReceived(from, PIByteArray(data, size));
|
||||
packetReceivedEvent(from, PIByteArray(data, size));
|
||||
PIIODevice * cd = (PIIODevice * )emitter();
|
||||
if (cd == 0) return;
|
||||
PIVector<PIPacketExtractor * > be(bounded_extractors.value(cd));
|
||||
//piCout << be << (void*)data << size;
|
||||
piForeach (PIPacketExtractor * i, be)
|
||||
i->threadedRead(data, size);
|
||||
PIVector<PIIODevice * > chd(channels_.value(cd));
|
||||
piForeach (PIIODevice * d, chd) {
|
||||
int ret = d->write(data, size);
|
||||
PIDiagnostics * diag = diags_.value(d);
|
||||
if (diag != 0) diag->sended(ret);
|
||||
}
|
||||
PIDiagnostics * diag = diags_.value(cd);
|
||||
if (diag != 0) diag->received(size);
|
||||
}
|
||||
|
||||
|
||||
void PIConnection::diagQualityChanged(PIDiagnostics::Quality new_quality, PIDiagnostics::Quality old_quality) {
|
||||
qualityChanged(diags_.key((PIDiagnostics*)emitter()), new_quality, old_quality);
|
||||
}
|
||||
|
||||
|
||||
|
||||
PIConnection::DevicePool * __device_pool__;
|
||||
|
||||
bool __DevicePoolContainer__::inited_(false);
|
||||
|
||||
__DevicePoolContainer__::__DevicePoolContainer__() {
|
||||
if (inited_) return;
|
||||
inited_ = true;
|
||||
__device_pool__ = new PIConnection::DevicePool();
|
||||
}
|
||||
417
src/io/piconnection.h
Executable file
417
src/io/piconnection.h
Executable file
@@ -0,0 +1,417 @@
|
||||
/*! \file piconnection.h
|
||||
* \brief Complex I/O point
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Complex I/O point
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PICONNECTION_H
|
||||
#define PICONNECTION_H
|
||||
|
||||
#include "pipacketextractor.h"
|
||||
#include "pidiagnostics.h"
|
||||
|
||||
class PIConfig;
|
||||
|
||||
class PIP_EXPORT PIConnection: public PIObject
|
||||
{
|
||||
PIOBJECT(PIConnection)
|
||||
public:
|
||||
|
||||
//! Constructs an empty connection
|
||||
PIConnection();
|
||||
|
||||
//! Constructs connection with name "name"
|
||||
PIConnection(const PIString & name);
|
||||
|
||||
//! Constructs connection and configure it from config file "config" from section "name"
|
||||
PIConnection(const PIString & config, const PIString & name);
|
||||
|
||||
//! Constructs connection and configure it from config content "string" from section "name"
|
||||
PIConnection(PIString * string, const PIString & name);
|
||||
|
||||
~PIConnection();
|
||||
|
||||
|
||||
/*! \brief Configure connection from config file "config" from section "name". Returns if configuration was successful
|
||||
* \details \b Warning: all devices, filters and channels removed before configure! */
|
||||
bool configureFromConfig(const PIString & config, const PIString & name);
|
||||
|
||||
/*! \brief Configure connection from config content "string" from section "name". Returns if configuration was successful
|
||||
* \details \b Warning: all devices, filters and channels removed before configure! */
|
||||
bool configureFromString(PIString * string, const PIString & name);
|
||||
|
||||
//! Returns config file section of current connection configuration
|
||||
PIString makeConfig() const;
|
||||
|
||||
|
||||
/*! \brief Add device with full path "full_path", open mode "mode" to Device pool and connection
|
||||
* \details Returns pointer to device or null if device can not be created. If "start" is true,
|
||||
* read thread is started immediately. Else, you can start read thread with functions \a startThreadedRead()
|
||||
* or \a startAllThreadedReads(). By default, read thread doesn`t start */
|
||||
PIIODevice * addDevice(const PIString & full_path, PIIODevice::DeviceMode mode = PIIODevice::ReadWrite, bool start = false);
|
||||
|
||||
void setDeviceName(PIIODevice * dev, const PIString & name) {device_names[name] = dev;}
|
||||
|
||||
PIStringList deviceNames(const PIIODevice * dev) const;
|
||||
|
||||
/*! \brief Remove device with full path "full_path" from connection
|
||||
* \details Returns if device was removed. If there is no connection bounded to this device,
|
||||
* it will be removed from Device pool */
|
||||
bool removeDevice(const PIString & full_path);
|
||||
|
||||
/*! \brief Remove all device from connection
|
||||
* \details If there is no connection bounded to there devices, they removed from Device pool */
|
||||
void removeAllDevices();
|
||||
|
||||
//! Returns device with full path "full_path" or null if there is no such device
|
||||
PIIODevice * deviceByFullPath(const PIString & full_path) const;
|
||||
|
||||
//! Returns device with name "name" or null if there is no such device
|
||||
PIIODevice * deviceByName(const PIString & name) const;
|
||||
|
||||
//! Returns all devices bounded to this connection
|
||||
PIVector<PIIODevice * > boundedDevices() const;
|
||||
|
||||
|
||||
/*! \brief Add filter with name "name" to device with full path "full_path_name" or filter "full_path_name"
|
||||
* \details If there is no filter with name "name", connection create new with split mode "mode" and bound
|
||||
* to it device "full_path_name" or filter "full_path_name". If filter with name "name" already exists,
|
||||
* device "full_path_name" or filter "full_path_name" add to this filter.
|
||||
* This function returns PIPacketExtractor * assosiated with this filter
|
||||
* \n \b Attention! "mode" is altual olny if new filter was created! */
|
||||
PIPacketExtractor * addFilter(const PIString & name, const PIString & full_path_name, PIPacketExtractor::SplitMode mode = PIPacketExtractor::None);
|
||||
|
||||
//! Add filter with name "name" to device "dev"
|
||||
PIPacketExtractor * addFilter(const PIString & name, const PIIODevice * dev, PIPacketExtractor::SplitMode mode = PIPacketExtractor::None) {return addFilter(name, devFPath(dev), mode);}
|
||||
|
||||
/*! \brief Remove from filter with name "name" device with full path "full_path_name" or filter "full_path_name"
|
||||
* \details If there is no devices bounded to this filter, it will be removed. Returns if device was removed */
|
||||
bool removeFilter(const PIString & name, const PIString & full_path_name);
|
||||
|
||||
//! Remove from filter with name "name" device or filter "dev"
|
||||
bool removeFilter(const PIString & name, const PIIODevice * dev);
|
||||
|
||||
//! Remove filter with name "name". Returns if filter was removed
|
||||
bool removeFilter(const PIString & name);
|
||||
|
||||
//! Remove all filters from connection
|
||||
void removeAllFilters();
|
||||
|
||||
|
||||
//! Returns all filters of connection
|
||||
PIVector<PIPacketExtractor * > filters() const;
|
||||
|
||||
//! Returns all filter names of connection
|
||||
PIStringList filterNames() const;
|
||||
|
||||
//! Returns PIPacketExtractor * assosiated with filter "name" or null if there is no such filter
|
||||
PIPacketExtractor * filter(const PIString & name) const;
|
||||
|
||||
//! Returns all devices bounded to filter "name"
|
||||
PIVector<PIIODevice * > filterBoundedDevices(const PIString & name) const;
|
||||
|
||||
|
||||
/*! \brief Add to connection channel from "name_from" to "name_to"
|
||||
* \details "name_from" and "name_to" can be full pathes of devices or filter names.
|
||||
* Returns \b false if there if no such device or filter, else create channel and returns \b true */
|
||||
bool addChannel(const PIString & name_from, const PIString & name_to);
|
||||
|
||||
//! Add to connection channel from "name_from" to "dev_to"
|
||||
bool addChannel(const PIString & name_from, const PIIODevice * dev_to) {return addChannel(name_from, devFPath(dev_to));}
|
||||
|
||||
//! Add to connection channel from "dev_from" to "name_to"
|
||||
bool addChannel(const PIIODevice * dev_from, const PIString & name_to) {return addChannel(devFPath(dev_from), name_to);}
|
||||
|
||||
//! Add to connection channel from "dev_from" to "dev_to"
|
||||
bool addChannel(const PIIODevice * dev_from, const PIIODevice * dev_to) {return addChannel(devFPath(dev_from), devFPath(dev_to));}
|
||||
|
||||
/*! \brief Remove from connection channel from "name_from" to "name_to"
|
||||
* \details "name_from" and "name_to" can be full pathes of devices or filter names.
|
||||
* Returns \b false if there if no such device or filter, else remove channel and returns \b true */
|
||||
bool removeChannel(const PIString & name_from, const PIString & name_to);
|
||||
|
||||
//! Remove from connection channel from "name_from" to "dev_to"
|
||||
bool removeChannel(const PIString & name_from, const PIIODevice * dev_to) {return removeChannel(name_from, devFPath(dev_to));}
|
||||
|
||||
//! Remove from connection channel from "dev_from" to "name_to"
|
||||
bool removeChannel(const PIIODevice * dev_from, const PIString & name_to) {return removeChannel(devFPath(dev_from), name_to);}
|
||||
|
||||
//! Remove from connection channel from "dev_from" to "dev_to"
|
||||
bool removeChannel(const PIIODevice * dev_from, const PIIODevice * dev_to) {return removeChannel(devFPath(dev_from), devFPath(dev_to));}
|
||||
|
||||
/*! \brief Remove from connection all channels from "name_from"
|
||||
* \details "name_from" can be full path of device or filter name.
|
||||
* Returns \b false if there if no such device or filter, else remove channels and returns \b true */
|
||||
bool removeChannel(const PIString & name_from);
|
||||
|
||||
//! Remove from connection all channels from "dev_from"
|
||||
bool removeChannel(const PIIODevice * dev_from) {return removeChannel(devFPath(dev_from));}
|
||||
|
||||
//! Remove from connection all channels
|
||||
void removeAllChannels();
|
||||
|
||||
//! Returns all channels of this connection as full pathes or filter names pair array (from, to)
|
||||
PIVector<PIPair<PIString, PIString> > channels() const;
|
||||
|
||||
|
||||
/*! \brief Add to connection sender with name "name" device with full path "full_path"
|
||||
* \details If there is no sender with name "name", connection create new, bound
|
||||
* to it device "full_path_name" and start sender timer with frequency "frequency".
|
||||
* If sender with name "name" already exists, device "full_path_name" add to this sender
|
||||
* If "start" is true, sender is started immediately. Else, you can start sender with
|
||||
* functions \a startSender()
|
||||
* \n \b Attention! "frequency" is actual olny if new sender was created! */
|
||||
void addSender(const PIString & name, const PIString & full_path, float frequency, bool start = false);
|
||||
|
||||
//! Add to connection sender with name "name" device "dev"
|
||||
void addSender(const PIString & name, const PIIODevice * dev, float frequency, bool start = false) {addSender(name, devFPath(dev), frequency, start);}
|
||||
|
||||
/*! \brief Remove from sender with name "name" device with full path "full_path_name"
|
||||
* \details If there is no devices bounded to this sender, it will be removed. Returns if sender was removed */
|
||||
bool removeSender(const PIString & name, const PIString & full_path);
|
||||
|
||||
//! Remove from sender with name "name" device "dev"
|
||||
bool removeSender(const PIString & name, const PIIODevice * dev) {return removeSender(name, devFPath(dev));}
|
||||
|
||||
//! Remove sender with name "name", returns if sender was removed
|
||||
bool removeSender(const PIString & name);
|
||||
|
||||
//! Set sender "name" fixed send data "data", returns if sender exists
|
||||
bool setSenderFixedData(const PIString & name, const PIByteArray & data);
|
||||
|
||||
//! Remove sender "name" fixed send data, returns if sender exists
|
||||
bool clearSenderFixedData(const PIString & name);
|
||||
|
||||
//! Returns sender "name" fixed send data
|
||||
PIByteArray senderFixedData(const PIString & name) const;
|
||||
|
||||
//! Returns sender "name" timer frequency, -1 if there is no such sender, or 0 if sender is not started yet
|
||||
float senderFrequency(const PIString & name) const;
|
||||
|
||||
//! Remove from connection all senders
|
||||
void removeAllSenders();
|
||||
|
||||
|
||||
//! Start read thread of device with full path "full_path"
|
||||
void startThreadedRead(const PIString & full_path);
|
||||
|
||||
//! Start read thread of device "dev"
|
||||
void startThreadedRead(const PIIODevice * dev) {startThreadedRead(devFPath(dev));}
|
||||
|
||||
//! Start read threads of all Device pool device
|
||||
void startAllThreadedReads();
|
||||
|
||||
//! Start sender "name" timer
|
||||
void startSender(const PIString & name);
|
||||
|
||||
//! Start all senders timers
|
||||
void startAllSenders();
|
||||
|
||||
//! Start all read threads and senders
|
||||
void start() {startAllThreadedReads(); startAllSenders();}
|
||||
|
||||
//! Stop read thread of device with full path "full_path"
|
||||
void stopThreadedRead(const PIString & full_path);
|
||||
|
||||
//! Stop read thread of device "dev"
|
||||
void stopThreadedRead(const PIIODevice * dev) {stopThreadedRead(devFPath(dev));}
|
||||
|
||||
//! Stop read threads of all Device pool device
|
||||
void stopAllThreadedReads();
|
||||
|
||||
//! Stop sender "name" timer
|
||||
void stopSender(const PIString & name);
|
||||
|
||||
//! Stop all senders timers
|
||||
void stopAllSenders();
|
||||
|
||||
//! Stop all read threads and senders
|
||||
void stop() {stopAllThreadedReads(); stopAllSenders();}
|
||||
|
||||
|
||||
//! Returns if there are no devices in this connection
|
||||
bool isEmpty() const {return device_modes.isEmpty();}
|
||||
|
||||
|
||||
//! Returns PIDiagnostics * assosiated with device with full path "full_path_name", name "full_path_name" or filter "full_path_name"
|
||||
PIDiagnostics * diagnostic(const PIString & full_path_name) const;
|
||||
|
||||
//! Returns PIDiagnostics * assosiated with device or filter "dev"
|
||||
PIDiagnostics * diagnostic(const PIIODevice * dev) const {return diags_.value(const_cast<PIIODevice * >(dev), 0);}
|
||||
|
||||
|
||||
//! Write data "data" to device with full path "full_path" and returns result of \a write() function of device
|
||||
int writeByFullPath(const PIString & full_path, const PIByteArray & data);
|
||||
|
||||
//! Write data "data" to device with name "name" and returns result of \a write() function of device
|
||||
int writeByName(const PIString & name, const PIByteArray & data);
|
||||
|
||||
//! Write data "data" to device "dev" and returns result of \a write() function of device
|
||||
int write(PIIODevice * dev, const PIByteArray & data);
|
||||
|
||||
|
||||
//! Returns all connections in application
|
||||
static PIVector<PIConnection * > allConnections();
|
||||
|
||||
//! Returns all devices in Device pool
|
||||
static PIVector<PIIODevice * > allDevices();
|
||||
|
||||
//! Set Device pool fake mode to \"yes\" and returns previous mode
|
||||
static bool setFakeMode(bool yes);
|
||||
|
||||
//! Returns if Device pool works in fake mode
|
||||
static bool isFakeMode();
|
||||
|
||||
class DevicePool: public PIThread {
|
||||
PIOBJECT_SUBCLASS(DevicePool, PIThread)
|
||||
friend class PIConnection;
|
||||
public:
|
||||
DevicePool(): PIThread(true, 10) {setName("PIConnection::DevicePool"); needLockRun(true); fake = false;}
|
||||
|
||||
PIIODevice * addDevice(PIConnection * parent, const PIString & fp, PIIODevice::DeviceMode mode = PIIODevice::ReadWrite, bool start = true);
|
||||
bool removeDevice(PIConnection * parent, const PIString & fp);
|
||||
void unboundConnection(PIConnection * parent);
|
||||
PIIODevice * device(const PIString & fp) const;
|
||||
PIVector<PIConnection * > boundedConnections() const;
|
||||
PIVector<PIIODevice * > boundedDevices() const;
|
||||
PIVector<PIIODevice * > boundedDevices(const PIConnection * parent) const;
|
||||
|
||||
protected:
|
||||
struct DeviceData {
|
||||
DeviceData(): dev(0), rthread(0), started(false) {}
|
||||
~DeviceData();
|
||||
PIIODevice * dev;
|
||||
PIThread * rthread;
|
||||
bool started;
|
||||
PIVector<PIConnection * > listeners;
|
||||
};
|
||||
|
||||
void run();
|
||||
|
||||
static void threadReadDP(void * ddp);
|
||||
void deviceReaded(DeviceData * dd, const PIByteArray & data);
|
||||
|
||||
typedef PIMap<PIString, DeviceData * >::value_type DDPair;
|
||||
PIMap<PIString, DeviceData * > devices;
|
||||
bool fake;
|
||||
};
|
||||
|
||||
EVENT2(dataReceivedEvent, const PIString &, from, const PIByteArray &, data)
|
||||
EVENT2(packetReceivedEvent, const PIString &, from, const PIByteArray &, data)
|
||||
EVENT3(qualityChanged, const PIIODevice * , dev, PIDiagnostics::Quality, new_quality, PIDiagnostics::Quality, old_quality)
|
||||
|
||||
static void splitFullPathWithMode(PIString fpwm, PIString * full_path, PIIODevice::DeviceMode * mode);
|
||||
|
||||
//! \events
|
||||
//! \{
|
||||
|
||||
//! \fn void dataReceivedEvent(const PIString & from, const PIByteArray & data)
|
||||
//! \brief Raise on data received from device with full path "from"
|
||||
|
||||
//! \fn void packetReceivedEvent(const PIString & from, const PIByteArray & data)
|
||||
//! \brief Raise on packet received from filter with name "from"
|
||||
|
||||
//! \fn void qualityChanged(const PIIODevice * device, PIDiagnostics::Quality new_quality, PIDiagnostics::Quality old_quality)
|
||||
//! \brief Raise on diagnostic quality of device "device" changed from "old_quality" to "new_quality"
|
||||
|
||||
//! \}
|
||||
|
||||
protected:
|
||||
|
||||
//! Executes on data received from device with full path "from"
|
||||
virtual void dataReceived(const PIString & from, const PIByteArray & data) {}
|
||||
|
||||
//! Executes on packet received from filter with name "from"
|
||||
virtual void packetReceived(const PIString & from, const PIByteArray & data) {}
|
||||
|
||||
//! Validate header "rec" with source header "src" and size "size", executes from filter "filter_name"
|
||||
virtual bool filterValidateHeader(const PIString & filter_name, uchar * src, uchar * rec, int size);
|
||||
|
||||
//! Validate footer "rec" with source footer "src" and size "size", executes from filter "filter_name"
|
||||
virtual bool filterValidateFooter(const PIString & filter_name, uchar * src, uchar * rec, int size);
|
||||
|
||||
//! Validate payload "rec" with size "size", executes from filter "filter_name"
|
||||
virtual bool filterValidatePayload(const PIString & filter_name, uchar * rec, int size);
|
||||
|
||||
//! You should returns data for sender "sender_name"
|
||||
virtual PIByteArray senderData(const PIString & sender_name);
|
||||
|
||||
private:
|
||||
static bool filterValidateHeaderS(void * c, uchar * src, uchar * rec, int size);
|
||||
static bool filterValidateFooterS(void * c, uchar * src, uchar * rec, int size);
|
||||
static bool filterValidatePayloadS(void * c, uchar * rec, int size);
|
||||
bool configure(PIConfig & conf, const PIString & name_);
|
||||
void rawReceived(PIIODevice * dev, const PIString & from, const PIByteArray & data);
|
||||
void unboundExtractor(PIPacketExtractor * pe);
|
||||
EVENT_HANDLER2(void, packetExtractorReceived, uchar * , data, int, size);
|
||||
EVENT_HANDLER2(void, diagQualityChanged, PIDiagnostics::Quality, new_quality, PIDiagnostics::Quality, old_quality);
|
||||
|
||||
PIString devPath(const PIIODevice * d) const;
|
||||
PIString devFPath(const PIIODevice * d) const;
|
||||
|
||||
struct Extractor {
|
||||
Extractor(): extractor(0) {}
|
||||
~Extractor();
|
||||
PIPacketExtractor * extractor;
|
||||
PIVector<PIIODevice * > devices;
|
||||
};
|
||||
|
||||
class Sender: public PITimer {
|
||||
PIOBJECT(Sender)
|
||||
public:
|
||||
Sender(PIConnection * parent_ = 0): parent(parent_), int_(0.f) {needLockRun(true);}
|
||||
~Sender() {stop();}
|
||||
PIConnection * parent;
|
||||
PIVector<PIIODevice * > devices;
|
||||
PIByteArray sdata;
|
||||
float int_;
|
||||
void tick(void * , int);
|
||||
};
|
||||
|
||||
typedef PIMap<PIString, Extractor * >::value_type PEPair;
|
||||
typedef PIMap<PIString, Sender * >::value_type SPair;
|
||||
typedef PIMap<PIString, PIIODevice * >::value_type DNPair;
|
||||
typedef PIMap<PIIODevice * , PIVector<PIPacketExtractor * > >::value_type BEPair;
|
||||
typedef PIMap<PIIODevice * , PIVector<PIIODevice * > >::value_type CPair;
|
||||
typedef PIMap<PIIODevice * , PIDiagnostics * >::value_type DPair;
|
||||
PIMap<PIString, Extractor * > extractors;
|
||||
PIMap<PIString, Sender * > senders;
|
||||
PIMap<PIString, PIIODevice * > device_names;
|
||||
PIMap<PIIODevice * , PIIODevice::DeviceMode> device_modes;
|
||||
PIMap<PIIODevice * , PIVector<PIPacketExtractor * > > bounded_extractors;
|
||||
PIMap<PIIODevice * , PIVector<PIIODevice * > > channels_;
|
||||
PIMap<PIIODevice * , PIDiagnostics * > diags_;
|
||||
|
||||
static PIVector<PIConnection * > _connections;
|
||||
|
||||
};
|
||||
|
||||
|
||||
extern PIConnection::DevicePool * __device_pool__;
|
||||
|
||||
class __DevicePoolContainer__ {
|
||||
public:
|
||||
__DevicePoolContainer__();
|
||||
static bool inited_;
|
||||
};
|
||||
|
||||
static __DevicePoolContainer__ __device_pool_container__;
|
||||
|
||||
|
||||
#endif // PICONNECTION_H
|
||||
374
src/io/pidatatransfer.cpp
Normal file
374
src/io/pidatatransfer.cpp
Normal file
@@ -0,0 +1,374 @@
|
||||
#include "pidatatransfer.h"
|
||||
|
||||
const uint PIBaseTransfer::signature = 0x54444950;
|
||||
|
||||
PIBaseTransfer::PIBaseTransfer(): crc(standardCRC_16()) {
|
||||
header.sig = signature;
|
||||
header.session_id = 0;
|
||||
packet_header_size = sizeof(PacketHeader);
|
||||
part_header_size = sizeof(Part) + sizeof(PIByteArray);
|
||||
is_sending = is_receiving = false;
|
||||
break_ = true;
|
||||
bytes_all = bytes_cur = 0;
|
||||
timeout_ = 1.;
|
||||
setPacketSize(4096);
|
||||
srand(PISystemTime::current().toMilliseconds());
|
||||
}
|
||||
|
||||
|
||||
PIBaseTransfer::~PIBaseTransfer() {
|
||||
break_ = true;
|
||||
session.clear();
|
||||
replies.clear();
|
||||
}
|
||||
|
||||
|
||||
void PIBaseTransfer::stopSend() {
|
||||
if (!is_sending) return;
|
||||
break_ = true;
|
||||
}
|
||||
|
||||
|
||||
void PIBaseTransfer::stopReceive() {
|
||||
if (!is_receiving) return;
|
||||
break_ = true;
|
||||
finish_receive(false);
|
||||
}
|
||||
|
||||
|
||||
void PIBaseTransfer::received(PIByteArray& data) {
|
||||
if (data.size() < sizeof(PacketHeader)) return;
|
||||
PacketHeader h;
|
||||
memcpy(&h, data.data(), sizeof(PacketHeader));
|
||||
PacketType pt = (PacketType)h.type;
|
||||
// piCoutObj << "receive" << h.session_id << h.type << h.id;
|
||||
switch (pt) {
|
||||
case pt_Unknown:
|
||||
break;
|
||||
case pt_Data:
|
||||
if (h.session_id != header.session_id || !is_receiving) {
|
||||
sendBreak(h.session_id);
|
||||
return;
|
||||
} else {
|
||||
uint rcrc = h.crc;
|
||||
uint ccrc = crc.calculate(data.data(sizeof(PacketHeader)), data.size_s() - sizeof(PacketHeader));
|
||||
if (rcrc != ccrc) {
|
||||
header.id = h.id;
|
||||
sendReply(pt_ReplyInvalid);
|
||||
} else {
|
||||
data >> h;
|
||||
processData(h.id, data);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case pt_ReplySuccess:
|
||||
case pt_ReplyInvalid:
|
||||
if (h.session_id != header.session_id) return;
|
||||
if (is_sending) {
|
||||
if (h.id >= 0 && h.id < replies.size())
|
||||
replies[h.id] = pt;
|
||||
}
|
||||
if (is_receiving && h.id == 0) {
|
||||
if (checkSession() == 0 && pt == pt_ReplySuccess) finish_receive(true);
|
||||
}
|
||||
break;
|
||||
case pt_Break:
|
||||
break_ = true;
|
||||
if (is_receiving) {
|
||||
stopReceive();
|
||||
return;
|
||||
}
|
||||
if (is_sending) {
|
||||
stopSend();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case pt_Start:
|
||||
if (is_sending) {
|
||||
sendBreak(h.session_id);
|
||||
return;
|
||||
}
|
||||
if (header.session_id != h.session_id && is_receiving) {
|
||||
sendBreak(h.session_id);
|
||||
return;
|
||||
}
|
||||
if (data.size() == sizeof(StartRequest) + sizeof(PacketHeader)) {
|
||||
StartRequest sr;
|
||||
memcpy(&sr, data.data(sizeof(PacketHeader)), sizeof(StartRequest));
|
||||
bytes_all = sr.size;
|
||||
header.session_id = h.session_id;
|
||||
header.id = 0;
|
||||
state_string = "start request";
|
||||
session.clear();
|
||||
replies.clear();
|
||||
session.resize(sr.packets);
|
||||
replies.resize(sr.packets + 1);
|
||||
replies.fill(pt_Unknown);
|
||||
is_receiving = true;
|
||||
break_ = false;
|
||||
state_string = "receiving";
|
||||
replies[0] = pt_ReplySuccess;
|
||||
sendReply(pt_ReplySuccess);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool PIBaseTransfer::send_process() {
|
||||
break_ = false;
|
||||
is_sending = true;
|
||||
startSend();
|
||||
replies.resize(session.size() + 1);
|
||||
replies.fill(pt_Unknown);
|
||||
PIByteArray ba;
|
||||
if (!getStartRequest()) return finish_send(false);
|
||||
for (int i = 0; i < session.size_s(); i++) {
|
||||
ba = build_packet(i);
|
||||
sendRequest(ba);
|
||||
if (break_) return finish_send(false);
|
||||
}
|
||||
// piCoutObj << "correct errors";
|
||||
PITimeMeasurer tm;
|
||||
int prev_chk = 0;
|
||||
while (tm.elapsed_s() < timeout_) {
|
||||
int chk = checkSession();
|
||||
if (chk != prev_chk) tm.reset();
|
||||
if (chk == 0) return finish_send(true);
|
||||
if (chk > 0) {
|
||||
ba = buildPacket(chk - 1);
|
||||
sendRequest(ba);
|
||||
}
|
||||
// if (chk == -1) return finish_send(false);
|
||||
if (break_) return finish_send(false);
|
||||
prev_chk = chk;
|
||||
piMSleep(1);
|
||||
}
|
||||
return finish_send(false);
|
||||
}
|
||||
|
||||
|
||||
int PIBaseTransfer::checkSession() {
|
||||
int miss = 0;
|
||||
for (int i = 1; i < replies.size_s(); i++) {
|
||||
if (replies[i] != pt_ReplySuccess) miss++;
|
||||
if (replies[i] == pt_ReplyInvalid) return i;
|
||||
}
|
||||
for (int i = 1; i < replies.size_s(); i++) {
|
||||
if (replies[i] != pt_ReplySuccess) return i;
|
||||
}
|
||||
if (miss > 0) {
|
||||
piCoutObj << "missing" << miss << "packets";
|
||||
return -miss;
|
||||
} else return 0;
|
||||
}
|
||||
|
||||
|
||||
void PIBaseTransfer::buildSession(PIVector<Part> parts) {
|
||||
state_string = "calculating files... ";
|
||||
session.clear();
|
||||
header.session_id = rand();
|
||||
bytes_all = 0;
|
||||
Part fi;
|
||||
int fi_index, fi_prts;
|
||||
PIVector<Part> lfi;
|
||||
int min_size = packet_header_size + part_header_size;
|
||||
int cur_size = min_size;
|
||||
for (int i = 0; i < parts.size_s(); i++) {
|
||||
state_string = "calculating files... " + PIString::fromNumber(i) + " of " + PIString::fromNumber(parts.size());
|
||||
fi.id = parts[i].id;
|
||||
bytes_all += fi.size;
|
||||
// fi.size = fi.entry.size;
|
||||
fi.start = 0;
|
||||
int rest = parts[i].size - (packet_size - cur_size);
|
||||
// piCout << i << fi.entry << rest;
|
||||
if (rest <= 0) {
|
||||
fi.size = parts[i].size;
|
||||
lfi << fi;
|
||||
cur_size += fi.size + part_header_size;
|
||||
} else {
|
||||
fi.size = parts[i].size - rest;
|
||||
fi_index = 1;
|
||||
fi_prts = 1 + 1 + piMaxi(1, rest / (packet_size - min_size));
|
||||
// piCout << fi_prts;
|
||||
lfi << fi;
|
||||
session << lfi;
|
||||
lfi.clear();
|
||||
cur_size = min_size;
|
||||
llong fs = fi.size;
|
||||
for (int j = 1; j < fi_prts; j++) {
|
||||
fi_index++;
|
||||
fi.start = fs;
|
||||
fi.size = piMin<ullong>(parts[i].size - fs, packet_size - min_size);
|
||||
lfi << fi;
|
||||
cur_size += fi.size + part_header_size;
|
||||
if (fi_index != fi_prts) {
|
||||
session << lfi;
|
||||
lfi.clear();
|
||||
cur_size = min_size;
|
||||
fs += fi.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (packet_size - cur_size < min_size) {
|
||||
session << lfi;
|
||||
lfi.clear();
|
||||
cur_size = min_size;
|
||||
}
|
||||
}
|
||||
if (cur_size > min_size) session << lfi;
|
||||
}
|
||||
|
||||
|
||||
void PIBaseTransfer::sendBreak(int session_id) {
|
||||
uint psid = header.session_id;
|
||||
header.session_id = session_id;
|
||||
sendReply(pt_Break);
|
||||
header.session_id = psid;
|
||||
}
|
||||
|
||||
|
||||
void PIBaseTransfer::sendReply(PacketType reply) {
|
||||
header.type = reply;
|
||||
PIByteArray ba;
|
||||
ba << header;
|
||||
sendRequest(ba);
|
||||
}
|
||||
|
||||
|
||||
bool PIBaseTransfer::getStartRequest() {
|
||||
PITimeMeasurer tm;
|
||||
header.type = pt_Start;
|
||||
header.id = 0;
|
||||
PIByteArray ba;
|
||||
ba << header;
|
||||
ba << (uint)session.size() << bytes_all;
|
||||
state_string = "send request";
|
||||
for (int i = 0; i < 3; i++) {
|
||||
tm.reset();
|
||||
sendRequest(ba);
|
||||
while (tm.elapsed_s() < timeout_) {
|
||||
if (break_) return false;
|
||||
//piCoutObj << send_replyes[0];
|
||||
if (replies[0] == pt_ReplySuccess) {
|
||||
state_string = "send permited!";
|
||||
return true;
|
||||
}
|
||||
piMSleep(10);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void PIBaseTransfer::processData(int id, PIByteArray & data) {
|
||||
// piCoutObj << "received packet" << id << ", size" << data.size();
|
||||
if (id < 1 || id > replies.size_s()) return;
|
||||
if (!session[id - 1].isEmpty()) {
|
||||
header.id = id;
|
||||
replies[id] = pt_ReplySuccess;
|
||||
sendReply(pt_ReplySuccess);
|
||||
if (checkSession() == 0) state_string = "receive ok";
|
||||
return;
|
||||
}
|
||||
Part fi;
|
||||
PIByteArray ba, pheader;
|
||||
pheader.resize(packet_header_size - sizeof(PacketHeader));
|
||||
if (!pheader.isEmpty()) {
|
||||
memcpy(pheader.data(), data.data(), pheader.size());
|
||||
data.remove(0, pheader.size_s());
|
||||
}
|
||||
while (!data.isEmpty()) {
|
||||
ba.clear();
|
||||
data >> fi;
|
||||
if (fi.size > 0) data >> ba;
|
||||
// fi.fsize = ba.size();
|
||||
bytes_cur += fi.size;
|
||||
// piCoutObj << "recv" << fi;
|
||||
session[id - 1] << fi;
|
||||
state_string = "receiving...";
|
||||
receivePart(fi, ba, pheader);
|
||||
}
|
||||
header.id = id;
|
||||
replies[id] = pt_ReplySuccess;
|
||||
if (checkSession() == 0) state_string = "receive ok";
|
||||
sendReply(pt_ReplySuccess);
|
||||
}
|
||||
|
||||
|
||||
PIByteArray PIBaseTransfer::build_packet(int id) {
|
||||
PIByteArray ret;
|
||||
PIByteArray ba;
|
||||
header.id = id + 1;
|
||||
header.type = pt_Data;
|
||||
//piCoutObj << "Packet" << header.id;
|
||||
//piCoutObj << "session id" << header.session_id;
|
||||
ret << header;
|
||||
ret << buildPacket(id);
|
||||
/*for (int i = 0; i < session[id].size_s(); i++) {
|
||||
EntryInfo fi = session[id][i];
|
||||
// piCoutObj << "send" << fi;
|
||||
bytes_total_cur += fi.fsize;
|
||||
ret << fi;
|
||||
if (fi.entry.size > 0) {
|
||||
PIString path = dir.absolutePath() + dir.separator + fi.entry.path;
|
||||
if (work_file.path() != path || !work_file.isOpened()) {
|
||||
if (!work_file.open(path, PIIODevice::ReadOnly)) {
|
||||
break_ = true;
|
||||
state_string = "ERROR! while open file " + fi.entry.path;
|
||||
piCoutObj << state_string;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
work_file.seek(fi.fstart);
|
||||
ba.resize(fi.fsize);
|
||||
int rs = work_file.read(ba.data(), ba.size());
|
||||
if (rs != fi.fsize) {
|
||||
break_ = true;
|
||||
state_string = "ERROR! while read file " + fi.entry.path + " (must " + PIString::fromNumber(fi.fsize) + ", but read " + PIString::fromNumber(rs) + ")";
|
||||
piCoutObj << state_string;
|
||||
return ret;
|
||||
}
|
||||
ret << ba;
|
||||
}
|
||||
}
|
||||
EntryInfo cfile = session[id].back();
|
||||
state_string = "sending: " + cfile.entry.path;
|
||||
bytes_file_all = cfile.entry.size;
|
||||
bytes_file_cur = cfile.fstart;
|
||||
uint scrc = crc.calculate(ret);
|
||||
ret << scrc;*/
|
||||
//piCoutObj << "packet" << header.id << "send crc" << scrc;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool PIBaseTransfer::finish_send(bool ok) {
|
||||
if (ok) state_string = "send done";
|
||||
else state_string = "send failed";
|
||||
// piCoutObj << state_string << PIString::readableSize(bytes_total_all);
|
||||
is_sending = false;
|
||||
bytes_all = bytes_cur = 0;
|
||||
header.id = 0;
|
||||
if (!ok) sendBreak(header.session_id);
|
||||
else sendReply(pt_ReplySuccess);
|
||||
finishSend(ok);
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
void PIBaseTransfer::finish_receive(bool ok) {
|
||||
if (ok) state_string = "receive done";
|
||||
else state_string = "receive failed";
|
||||
// piCoutObj << state_string << PIString::readableSize(bytes_total_all);
|
||||
is_receiving = false;
|
||||
bytes_all = bytes_cur = 0;
|
||||
if (!ok) sendBreak(header.session_id);
|
||||
finishReceive(ok);
|
||||
}
|
||||
PIByteArray PIDataTransfer::buildPacket(int id) {
|
||||
;
|
||||
}
|
||||
|
||||
116
src/io/pidatatransfer.h
Normal file
116
src/io/pidatatransfer.h
Normal file
@@ -0,0 +1,116 @@
|
||||
#ifndef PIDATATRANSFER_H
|
||||
#define PIDATATRANSFER_H
|
||||
|
||||
#include "picrc.h"
|
||||
#include "pitimer.h"
|
||||
|
||||
class PIBaseTransfer : public PIObject
|
||||
{
|
||||
PIOBJECT(PIBaseTransfer)
|
||||
public:
|
||||
PIBaseTransfer();
|
||||
~PIBaseTransfer();
|
||||
|
||||
struct PacketHeader {
|
||||
uint sig;
|
||||
int type; // PacketType
|
||||
uint session_id;
|
||||
uint id;
|
||||
uint crc;
|
||||
bool check_sig() {return (sig == signature);}
|
||||
};
|
||||
|
||||
struct Part {
|
||||
Part(uint id_ = 0, ullong size_ = 0, ullong start_ = 0) : id(id_), size(size_), start(start_) {}
|
||||
uint id;
|
||||
ullong size;
|
||||
ullong start;
|
||||
};
|
||||
|
||||
virtual void stopSend();
|
||||
virtual void stopReceive();
|
||||
|
||||
virtual bool isSending() const {return is_sending;}
|
||||
virtual bool isReceiving() const {return is_receiving;}
|
||||
|
||||
void setPacketSize(int size) {packet_size = size;}
|
||||
int packetSize() const {return packet_size;}
|
||||
|
||||
void setTimeout(double sec) {timeout_ = sec;}
|
||||
double timeout() const {return timeout_;}
|
||||
|
||||
const PIString & stateString() const {return state_string;}
|
||||
llong bytesAll() const {return bytes_all;}
|
||||
llong bytesCur() const {return bytes_cur;}
|
||||
const PIString * stateString_ptr() const {return &state_string;}
|
||||
const llong * bytesAll_ptr() const {return &bytes_all;}
|
||||
const llong * bytesCur_ptr() const {return &bytes_cur;}
|
||||
|
||||
EVENT(startReceive)
|
||||
EVENT1(finishReceive, bool, ok)
|
||||
EVENT(startSend)
|
||||
EVENT1(finishSend, bool, ok)
|
||||
EVENT1(sendRequest, PIByteArray &, data)
|
||||
EVENT_HANDLER1(void, received, PIByteArray &, data);
|
||||
|
||||
protected:
|
||||
uint packet_header_size, part_header_size;
|
||||
bool break_, is_sending, is_receiving;
|
||||
PIString state_string;
|
||||
llong bytes_all, bytes_cur;
|
||||
|
||||
void buildSession(PIVector<Part> parts);
|
||||
virtual PIByteArray buildPacket(int id) = 0;
|
||||
virtual void receivePart(Part fi, PIByteArray ba, PIByteArray header) = 0;
|
||||
|
||||
private:
|
||||
|
||||
enum PacketType {pt_Unknown, pt_Data, pt_ReplySuccess, pt_ReplyInvalid, pt_Break, pt_Start};
|
||||
|
||||
struct StartRequest {
|
||||
uint packets;
|
||||
ullong size;
|
||||
};
|
||||
|
||||
|
||||
static const uint signature;
|
||||
int packet_size;
|
||||
double timeout_;
|
||||
PIVector<PIVector<Part> > session;
|
||||
PIVector<PacketType> replies;
|
||||
PacketHeader header;
|
||||
CRC_16 crc;
|
||||
|
||||
void processData(int id, PIByteArray &data);
|
||||
PIByteArray build_packet(int id);
|
||||
int checkSession();
|
||||
bool send_process();
|
||||
void sendBreak(int session_id);
|
||||
void sendReply(PacketType reply);
|
||||
bool getStartRequest();
|
||||
bool finish_send(bool ok);
|
||||
void finish_receive(bool ok);
|
||||
};
|
||||
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PIBaseTransfer::PacketHeader & v) {s << v.sig << v.type << v.session_id << v.id; return s;}
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PIBaseTransfer::PacketHeader & v) {s >> v.sig >> v.type >> v.session_id >> v.id; return s;}
|
||||
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PIBaseTransfer::Part & v) {s << v.id << v.size << v.start; return s;}
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PIBaseTransfer::Part & v) {s >> v.id >> v.size >> v.start; return s;}
|
||||
|
||||
|
||||
class PIDataTransfer : public PIBaseTransfer
|
||||
{
|
||||
PIOBJECT_SUBCLASS(PIDataTransfer, PIBaseTransfer)
|
||||
public:
|
||||
PIDataTransfer() {;}
|
||||
~PIDataTransfer() {;}
|
||||
virtual PIByteArray buildPacket(int id);
|
||||
virtual void receivePart(Part fi, PIByteArray ba, PIByteArray header) {;}
|
||||
|
||||
private:
|
||||
PIByteArray data;
|
||||
};
|
||||
|
||||
|
||||
#endif // PIDATATRANSFER_H
|
||||
166
src/io/pidiagnostics.cpp
Executable file
166
src/io/pidiagnostics.cpp
Executable file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Speed and quality in/out diagnostics
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pidiagnostics.h"
|
||||
|
||||
|
||||
/** \class PIDiagnostics
|
||||
* \brief Connection quality diagnostics
|
||||
* \details
|
||||
* \section PIDiagnostics_sec0 Synopsis
|
||||
* This class provide abstract connection quality diagnostics and
|
||||
* counting. You should create instance of %PIDiagnostics and on
|
||||
* packet receive call function \a received(), on packet send call
|
||||
* function \a sended(). %PIDiagnostics calculates correct, wrong
|
||||
* and sended counters, packets per second, bytes per seconds,
|
||||
* immediate and integral receive frequencies and receive/send speeds
|
||||
* in human readable representation. There statistics are calculates
|
||||
* one time per period, by default 1 second. To calculate them you
|
||||
* should start %PIDiagnostics with function \a start() or pass \b true
|
||||
* to constructor.
|
||||
* */
|
||||
|
||||
|
||||
PIDiagnostics::PIDiagnostics(bool start_): PITimer() {
|
||||
reset();
|
||||
if (start_) start();
|
||||
}
|
||||
|
||||
|
||||
void PIDiagnostics::reset() {
|
||||
setDisconnectTimeout(3.);
|
||||
lock();
|
||||
qual = PIDiagnostics::Unknown;
|
||||
speedIn = speedOut = PIString::readableSize(0) + "/s";
|
||||
ifreq = immediate_freq = integral_freq = 0.f;
|
||||
cur_pckt = rec_once = 0;
|
||||
wrong_count = receive_count = send_count = 0;
|
||||
packets_in_sec = packets_out_sec = bytes_in_sec = bytes_out_sec = 0;
|
||||
unlock();
|
||||
}
|
||||
|
||||
|
||||
void PIDiagnostics::received(int size, bool correct) {
|
||||
lock();
|
||||
rec_once = 1;
|
||||
if (correct) {
|
||||
float el = tm.elapsed_s();
|
||||
tm.reset();
|
||||
if (el > 0.f) immediate_freq = ifreq = 1.f / el;
|
||||
else immediate_freq = ifreq = 0.f;
|
||||
receive_count++;
|
||||
} else {
|
||||
immediate_freq = ifreq = 0.f;
|
||||
wrong_count++;
|
||||
}
|
||||
addToHistory(history_rec, size, correct);
|
||||
unlock();
|
||||
}
|
||||
|
||||
|
||||
void PIDiagnostics::sended(int size) {
|
||||
lock();
|
||||
send_count++;
|
||||
addToHistory(history_send, size);
|
||||
unlock();
|
||||
}
|
||||
|
||||
|
||||
void PIDiagnostics::tick(void * data, int delimiter) {
|
||||
lock();
|
||||
checkHistory(history_rec);
|
||||
checkHistory(history_send);
|
||||
PIDiagnostics::Quality diag;
|
||||
immediate_freq = ifreq;
|
||||
ifreq = 0.f;
|
||||
int bps[2];
|
||||
int cpckt[2];
|
||||
bps[0] = bps[1] = 0;
|
||||
cpckt[0] = cpckt[1] = 0;
|
||||
packets_in_sec = packets_out_sec = 0;
|
||||
piForeachC (Entry & e, history_rec) {
|
||||
if (e.ok) {
|
||||
bps[0] += e.bytes;
|
||||
packets_in_sec++;
|
||||
}
|
||||
cpckt[e.ok ? 1 : 0]++;
|
||||
}
|
||||
piForeachC (Entry & e, history_send) {
|
||||
bps[1] += e.bytes;
|
||||
packets_out_sec++;
|
||||
}
|
||||
bytes_in_sec = bps[0] / disconn_;
|
||||
bytes_out_sec = bps[1] / disconn_;
|
||||
packets_in_sec /= disconn_;
|
||||
packets_out_sec /= disconn_;
|
||||
speedIn = PIString::readableSize(bytes_in_sec) + "/s";
|
||||
speedOut = PIString::readableSize(bytes_out_sec) + "/s";
|
||||
int arc = cpckt[0] + cpckt[1];
|
||||
float good_percents = 0.f;
|
||||
if (arc > 0) good_percents = (float)cpckt[1] / arc * 100.f;
|
||||
if (disconn_ > 0.) integral_freq = cpckt[1] / disconn_;
|
||||
else integral_freq = 0.;
|
||||
if (rec_once == 0) {
|
||||
diag = PIDiagnostics::Unknown;
|
||||
} else {
|
||||
if (good_percents == 0.f) diag = PIDiagnostics::Failure;
|
||||
else if (good_percents <= 20.f) diag = PIDiagnostics::Bad;
|
||||
else if (good_percents > 20.f && good_percents <= 80.f) diag = PIDiagnostics::Average;
|
||||
else diag = PIDiagnostics::Good;
|
||||
}
|
||||
if (diag != qual) {
|
||||
qualityChanged(diag, qual);
|
||||
qual = diag;
|
||||
}
|
||||
unlock();
|
||||
}
|
||||
|
||||
|
||||
void PIDiagnostics::addToHistory(PIVector<PIDiagnostics::Entry> & hist, int bytes, bool ok) {
|
||||
Entry e;
|
||||
e.time = PISystemTime::current(true);
|
||||
e.bytes = bytes;
|
||||
e.ok = ok;
|
||||
checkHistory(hist);
|
||||
hist << e;
|
||||
}
|
||||
|
||||
|
||||
void PIDiagnostics::checkHistory(PIVector< PIDiagnostics::Entry > & hist) {
|
||||
PISystemTime ctm = PISystemTime::current(true);
|
||||
for (int i = 0; i < hist.size_s(); ++i) {
|
||||
if ((ctm - hist[i].time).abs() > disconn_st) {
|
||||
hist.remove(i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIDiagnostics::propertyChanged(const PIString &) {
|
||||
disconn_ = property("disconnectTimeout").toFloat();
|
||||
changeDisconnectTimeout();
|
||||
}
|
||||
|
||||
|
||||
void PIDiagnostics::changeDisconnectTimeout() {
|
||||
lock();
|
||||
disconn_st = PISystemTime::fromSeconds(disconn_);
|
||||
unlock();
|
||||
}
|
||||
190
src/io/pidiagnostics.h
Executable file
190
src/io/pidiagnostics.h
Executable file
@@ -0,0 +1,190 @@
|
||||
/*! \file pidiagnostics.h
|
||||
* \brief Connection quality diagnostics
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Speed and quality in/out diagnostics
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com, Bychkov Andrey wapmobil@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIDIAGNOSTICS_H
|
||||
#define PIDIAGNOSTICS_H
|
||||
|
||||
#include "pitimer.h"
|
||||
|
||||
|
||||
class PIP_EXPORT PIDiagnostics: private PITimer
|
||||
{
|
||||
PIOBJECT_SUBCLASS(PIDiagnostics, PITimer)
|
||||
friend class PIConnection;
|
||||
public:
|
||||
|
||||
//! Constructs an empty diagnostics and if "strat_" start it
|
||||
PIDiagnostics(bool start_ = true);
|
||||
|
||||
virtual ~PIDiagnostics() {;}
|
||||
|
||||
//! Connection quality
|
||||
enum Quality {
|
||||
Unknown /** Unknown, no one packet received yet */ = 1,
|
||||
Failure /** No connection, no one correct packet received for last period */ = 2,
|
||||
Bad /** Bad connection, correct packets received <= 20% */ = 3,
|
||||
Average /** Average connection, correct packets received > 20% and <= 80% */ = 4,
|
||||
Good /** Good connection, correct packets received > 80% */ = 5
|
||||
};
|
||||
|
||||
|
||||
//! Returns period of full disconnect in seconds and period of averaging frequency
|
||||
float disconnectTimeout() const {return disconn_;}
|
||||
|
||||
//! Returns period of full disconnect in seconds and period of averaging frequency
|
||||
void setDisconnectTimeout(float s) {setProperty("disconnectTimeout", s); disconn_ = s; changeDisconnectTimeout();}
|
||||
|
||||
|
||||
//! Returns immediate receive frequency, packets/s
|
||||
float immediateFrequency() const {return immediate_freq;}
|
||||
|
||||
//! Returns integral receive frequency for \a disconnectTimeout() seconds, packets/s
|
||||
float integralFrequency() const {return integral_freq;}
|
||||
|
||||
//! Returns correct received packets per second
|
||||
ullong receiveCountPerSec() const {return packets_in_sec;}
|
||||
|
||||
//! Returns sended packets per second
|
||||
ullong sendCountPerSec() const {return packets_out_sec;}
|
||||
|
||||
//! Returns correct received bytes per second
|
||||
ullong receiveBytesPerSec() const {return bytes_in_sec;}
|
||||
|
||||
//! Returns sended bytes per second
|
||||
ullong sendBytesPerSec() const {return bytes_out_sec;}
|
||||
|
||||
//! Returns overall correct received packets count
|
||||
ullong receiveCount() const {return receive_count;}
|
||||
|
||||
//! Returns overall wrong received packets count
|
||||
ullong wrongCount() const {return wrong_count;}
|
||||
|
||||
//! Returns overall sended packets count
|
||||
ullong sendCount() const {return send_count;}
|
||||
|
||||
//! Returns connection quality
|
||||
PIDiagnostics::Quality quality() const {return qual;}
|
||||
|
||||
//! Returns receive speed in format "n {B|kB|MB|GB|TB}/s"
|
||||
PIString receiveSpeed() const {return speedIn;}
|
||||
|
||||
//! Returns send speed in format "n {B|kB|MB|GB|TB}/s"
|
||||
PIString sendSpeed() const {return speedOut;}
|
||||
|
||||
|
||||
//! Returns immediate receive frequency pointer, packets/s. Useful for output to PIConsole
|
||||
const float * immediateFrequency_ptr() const {return &immediate_freq;}
|
||||
|
||||
//! Returns integral receive frequency pointer for period, packets/s. Useful for output to PIConsole
|
||||
const float * integralFrequency_ptr() const {return &integral_freq;}
|
||||
|
||||
//! Returns correct received packets per second pointer. Useful for output to PIConsole
|
||||
const ullong * receiveCountPerSec_ptr() const {return &packets_in_sec;}
|
||||
|
||||
//! Returns sended packets per second pointer. Useful for output to PIConsole
|
||||
const ullong * sendCountPerSec_ptr() const {return &packets_out_sec;}
|
||||
|
||||
//! Returns correct received bytes per second pointer. Useful for output to PIConsole
|
||||
const ullong * receiveBytesPerSec_ptr() const {return &bytes_in_sec;}
|
||||
|
||||
//! Returns sended bytes per second pointer. Useful for output to PIConsole
|
||||
const ullong * sendBytesPerSec_ptr() const {return &bytes_out_sec;}
|
||||
|
||||
//! Returns overall correct received packets count pointer. Useful for output to PIConsole
|
||||
const ullong * receiveCount_ptr() const {return &receive_count;}
|
||||
|
||||
//! Returns overall wrong received packets count pointer. Useful for output to PIConsole
|
||||
const ullong * wrongCount_ptr() const {return &wrong_count;}
|
||||
|
||||
//! Returns overall sended packets count pointer. Useful for output to PIConsole
|
||||
const ullong * sendCount_ptr() const {return &send_count;}
|
||||
|
||||
//! Returns connection quality pointer. Useful for output to PIConsole
|
||||
const int * quality_ptr() const {return (int * )&qual;}
|
||||
|
||||
//! Returns receive speed pointer in format "n {B|kB|MB|GB|TB}/s". Useful for output to PIConsole
|
||||
const PIString * receiveSpeed_ptr() const {return &speedIn;}
|
||||
|
||||
//! Returns send speed pointer in format "n {B|kB|MB|GB|TB}/s". Useful for output to PIConsole
|
||||
const PIString * sendSpeed_ptr() const {return &speedOut;}
|
||||
|
||||
EVENT_HANDLER0(void, start) {start(1000.); changeDisconnectTimeout();}
|
||||
EVENT_HANDLER1(void, start, double, msecs) {if (msecs > 0.) {PITimer::start(msecs); changeDisconnectTimeout();}}
|
||||
EVENT_HANDLER0(void, reset);
|
||||
|
||||
EVENT_HANDLER1(void, received, int, size) {received(size, true);}
|
||||
EVENT_HANDLER2(void, received, int, size, bool, correct);
|
||||
EVENT_HANDLER1(void, sended, int, size);
|
||||
|
||||
EVENT2(qualityChanged, PIDiagnostics::Quality, new_quality, PIDiagnostics::Quality, old_quality)
|
||||
|
||||
//! \handlers
|
||||
//! \{
|
||||
|
||||
//! \fn void start(double msecs = 1000.)
|
||||
//! \brief Start diagnostics evaluations with period "msecs" milliseconds
|
||||
|
||||
//! \fn void reset()
|
||||
//! \brief Reset diagnostics counters
|
||||
|
||||
//! \fn void received(int size, bool correct = true)
|
||||
//! \brief Notify diagnostics about "correct" corected received packet
|
||||
|
||||
//! \fn void sended(int size)
|
||||
//! \brief Notify diagnostics about sended packet
|
||||
|
||||
//! \}
|
||||
//! \events
|
||||
//! \{
|
||||
|
||||
//! \fn void qualityChanged(PIDiagnostics::Quality new_quality, PIDiagnostics::Quality old_quality)
|
||||
//! \brief Raise on change receive quality from "old_quality" to "new_quality"
|
||||
|
||||
//! \}
|
||||
|
||||
private:
|
||||
struct Entry {
|
||||
Entry() {ok = true; bytes = 0;}
|
||||
PISystemTime time;
|
||||
bool ok;
|
||||
int bytes;
|
||||
};
|
||||
|
||||
void tick(void * data, int delimiter);
|
||||
void addToHistory(PIVector<Entry> & hist, int bytes, bool ok = true);
|
||||
void checkHistory(PIVector<Entry> & hist);
|
||||
void propertyChanged(const PIString & );
|
||||
void changeDisconnectTimeout();
|
||||
|
||||
PIDiagnostics::Quality qual;
|
||||
PIString speedIn, speedOut;
|
||||
float ifreq, immediate_freq, integral_freq, disconn_;
|
||||
PIVector<Entry> history_rec, history_send;
|
||||
PISystemTime disconn_st;
|
||||
PITimeMeasurer tm;
|
||||
char cur_pckt, rec_once;
|
||||
ullong wrong_count, receive_count, send_count;
|
||||
ullong packets_in_sec, packets_out_sec, bytes_in_sec, bytes_out_sec;
|
||||
|
||||
};
|
||||
|
||||
#endif // PIDIAGNOSTICS_H
|
||||
380
src/io/pidir.cpp
Executable file
380
src/io/pidir.cpp
Executable file
@@ -0,0 +1,380 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Directory
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pidir.h"
|
||||
|
||||
//#if !defined(ANDROID)
|
||||
const PIChar PIDir::separator = '/';
|
||||
#ifdef QNX
|
||||
# define _stat_struct_ struct stat
|
||||
# define _stat_call_ stat
|
||||
# define _stat_link_ lstat
|
||||
#else
|
||||
# define _stat_struct_ struct stat64
|
||||
# define _stat_call_ stat64
|
||||
# define _stat_link_ lstat64
|
||||
#endif
|
||||
#ifndef WINDOWS
|
||||
# ifdef ANDROID
|
||||
# include <sys/dirent.h>
|
||||
# else
|
||||
# include <sys/dir.h>
|
||||
# endif
|
||||
# include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
|
||||
/*! \class PIDir
|
||||
* \brief Local directory
|
||||
*
|
||||
* \section PIDir_sec0 Synopsis
|
||||
* This class provide access to local file. You can manipulate
|
||||
* binary content or use this class as text stream. To binary
|
||||
* access there are function \a read(), \a write(), and many
|
||||
* \a writeBinary() functions. For write variables to file in
|
||||
* their text representation threr are many "<<" operators.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
PIDir::PIDir(const PIString & dir) {
|
||||
path_ = dir;
|
||||
cleanPath();
|
||||
}
|
||||
|
||||
|
||||
PIDir::PIDir(const PIFile & file) {
|
||||
path_ = file.path();
|
||||
cleanPath();
|
||||
if (isExists()) return;
|
||||
int pos = path_.findLast(separator);
|
||||
path_.cutRight(path_.size_s() - pos);
|
||||
}
|
||||
|
||||
|
||||
bool PIDir::operator ==(const PIDir & d) const {
|
||||
return d.absolutePath() == absolutePath();
|
||||
}
|
||||
|
||||
|
||||
bool PIDir::isAbsolute() const {
|
||||
if (path_.isEmpty()) return false;
|
||||
#ifdef WINDOWS
|
||||
if (path_.size_s() < 2) return false;
|
||||
return (path_.mid(1, 2).contains(":"));
|
||||
#else
|
||||
return (path_[0] == separator);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
PIString PIDir::absolutePath() const {
|
||||
if (isAbsolute()) return path_;
|
||||
return PIDir(PIDir::current().path_ + separator + path_).path_;
|
||||
}
|
||||
|
||||
|
||||
PIDir & PIDir::cleanPath() {
|
||||
PIString p(path_);
|
||||
if (p.size() == 0) {
|
||||
path_ = ".";
|
||||
return *this;
|
||||
}
|
||||
path_.replaceAll(PIString(separator) + PIString(separator), PIString(separator));
|
||||
bool is_abs = isAbsolute();
|
||||
PIStringList l = PIString(p).split(separator);
|
||||
#ifdef WINDOWS
|
||||
PIString letter;
|
||||
if (is_abs) letter = l.take_front();
|
||||
#endif
|
||||
l.removeAll(".");
|
||||
l.removeAll("");
|
||||
for (int i = 0; i < l.size_s() - 1; ++i) {
|
||||
if (l[i] != ".." && l[i + 1] == "..") {
|
||||
l.remove(i, 2);
|
||||
i -= 2;
|
||||
if (i < -1) i = -1;
|
||||
}
|
||||
}
|
||||
if (is_abs)
|
||||
while (!l.isEmpty()) {
|
||||
if (l.front() == "..")
|
||||
l.pop_front();
|
||||
else
|
||||
break;
|
||||
}
|
||||
path_ = l.join(separator);
|
||||
if (is_abs) {
|
||||
path_.prepend(separator);
|
||||
#ifdef WINDOWS
|
||||
path_.prepend(letter);
|
||||
#endif
|
||||
}
|
||||
if (path_.isEmpty()) path_ = ".";
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
PIString PIDir::relative(const PIString & path) const {
|
||||
PIDir td(path);
|
||||
PIStringList dl(absolutePath().split(separator)), pl(td.absolutePath().split(separator)), rl;
|
||||
while (!dl.isEmpty() && !pl.isEmpty()) {
|
||||
if (dl.front() != pl.front()) break;
|
||||
dl.pop_front();
|
||||
pl.pop_front();
|
||||
}
|
||||
for (int i = 0; i < dl.size_s(); ++i)
|
||||
rl << "..";
|
||||
rl << pl;
|
||||
if (rl.isEmpty()) return ".";
|
||||
return rl.join(separator);
|
||||
}
|
||||
|
||||
|
||||
PIDir & PIDir::setDir(const PIString & path) {
|
||||
path_ = path;
|
||||
cleanPath();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
PIDir & PIDir::cd(const PIString & path) {
|
||||
if (path_.isEmpty()) return *this;
|
||||
if (path_.back() != separator) path_ += separator;
|
||||
path_ += path;
|
||||
return cleanPath();
|
||||
}
|
||||
|
||||
|
||||
bool PIDir::make(bool withParents) {
|
||||
PIDir d = cleanedPath();
|
||||
PIString tp;
|
||||
bool is_abs = isAbsolute();
|
||||
if (withParents) {
|
||||
PIStringList l = d.path_.split(separator);
|
||||
for (int i = l.size_s() - 1; i >= 0; --i) {
|
||||
if (i > 1) tp = PIStringList(l).remove(i, l.size_s() - i).join(separator);
|
||||
else {
|
||||
tp = separator;
|
||||
if (!is_abs) tp.push_front('.');
|
||||
}
|
||||
//cout << tp << endl;
|
||||
if (isExists(tp)) {
|
||||
for (int j = i + 1; j <= l.size_s(); ++j) {
|
||||
tp = PIStringList(l).remove(j, l.size_s() - j).join(separator);
|
||||
//cout << tp << endl;
|
||||
if (makeDir(tp)) continue;
|
||||
else return false;
|
||||
}
|
||||
break;
|
||||
};
|
||||
}
|
||||
} else
|
||||
if (makeDir(d.path_)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
PIVector<PIFile::FileInfo> PIDir::entries() {
|
||||
PIVector<PIFile::FileInfo> l;
|
||||
if (!isExists()) return l;
|
||||
PIString dp = absolutePath();
|
||||
PIString p(dp);
|
||||
if (dp == ".") dp.clear();
|
||||
else if (!dp.endsWith(separator)) dp += separator;
|
||||
//piCout << "entries from" << p;
|
||||
#ifdef WINDOWS
|
||||
WIN32_FIND_DATA fd; memset(&fd, 0, sizeof(fd));
|
||||
p += "\\*";
|
||||
void * hf = FindFirstFile((LPCTSTR)(p.data()), &fd);
|
||||
if (!hf) return l;
|
||||
do {
|
||||
l << PIFile::fileInfo(dp + PIString(fd.cFileName));
|
||||
memset(&fd, 0, sizeof(fd));
|
||||
} while (FindNextFile(hf, &fd) != 0);
|
||||
FindClose(hf);
|
||||
#else
|
||||
dirent ** list;
|
||||
int cnt = scandir(
|
||||
# ifndef QNX
|
||||
p.data(), &list
|
||||
# else
|
||||
const_cast<char*>(p.data()), 0
|
||||
# endif
|
||||
, 0, versionsort);
|
||||
for (int i = 0; i < cnt; ++i) {
|
||||
l << PIFile::fileInfo(dp + PIString(list[i]->d_name));
|
||||
delete list[i];
|
||||
}
|
||||
delete list;
|
||||
#endif
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
PIVector<PIFile::FileInfo> PIDir::allEntries() {
|
||||
return allEntries(absolutePath());
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool PIDir::isExists(const PIString & path) {
|
||||
#ifdef WINDOWS
|
||||
DWORD ret = GetFileAttributes((LPCTSTR)(path.data()));
|
||||
return (ret != 0xFFFFFFFF) && (ret & FILE_ATTRIBUTE_DIRECTORY);
|
||||
#else
|
||||
DIR * dir_ = opendir(path.data());
|
||||
if (dir_ == 0) return false;
|
||||
closedir(dir_);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
PIDir PIDir::current() {
|
||||
char rc[1024];
|
||||
#ifdef WINDOWS
|
||||
memset(rc, 0, 1024);
|
||||
if (GetCurrentDirectory(1024, (LPTSTR)rc) == 0) return PIString();
|
||||
PIString ret(rc);
|
||||
ret.replaceAll("\\", PIDir::separator);
|
||||
return PIDir(ret);
|
||||
#else
|
||||
if (getcwd(rc, 1024) == 0) return PIString();
|
||||
#endif
|
||||
return PIDir(rc);
|
||||
}
|
||||
|
||||
|
||||
PIDir PIDir::home() {
|
||||
char * rc = 0;
|
||||
#ifdef WINDOWS
|
||||
rc = new char[1024];
|
||||
memset(rc, 0, 1024);
|
||||
if (ExpandEnvironmentStrings((LPCTSTR)"%HOMEPATH%", (LPTSTR)rc, 1024) == 0) {
|
||||
delete[] rc;
|
||||
return PIDir();
|
||||
}
|
||||
PIString s(rc);
|
||||
s.replaceAll("\\", PIDir::separator);
|
||||
delete[] rc;
|
||||
return PIDir(s);
|
||||
#else
|
||||
rc = getenv("HOME");
|
||||
if (rc == 0) return PIDir();
|
||||
return PIDir(rc);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
PIDir PIDir::temporary() {
|
||||
char * rc = 0;
|
||||
#ifdef WINDOWS
|
||||
rc = new char[1024];
|
||||
memset(rc, 0, 1024);
|
||||
int ret = GetTempPath(1024, (LPTSTR)rc);
|
||||
if (ret == 0) {
|
||||
delete[] rc;
|
||||
return PIDir();
|
||||
}
|
||||
PIString s(rc);
|
||||
s.replaceAll("\\", PIDir::separator);
|
||||
delete[] rc;
|
||||
return PIDir(s);
|
||||
#else
|
||||
rc = tmpnam(0);
|
||||
if (rc == 0) return PIDir();
|
||||
PIString s(rc);
|
||||
return PIDir(s.left(s.findLast(PIDir::separator)));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
PIVector<PIFile::FileInfo> PIDir::allEntries(const PIString &path) {
|
||||
PIVector<PIFile::FileInfo> ret;
|
||||
PIVector<PIFile::FileInfo> dirs;
|
||||
PIDir root;
|
||||
root.setDir(path);
|
||||
PIVector<PIFile::FileInfo> cds = root.entries();
|
||||
piForeachC (PIFile::FileInfo & de, cds) {
|
||||
//piCout << " open" << de.name();
|
||||
if (de.name() == "." || de.name() == "..") continue;
|
||||
if (de.isSymbolicLink()) continue; /// TODO: resolve symlinks
|
||||
if (de.isDir()) {
|
||||
dirs.push_front(de);
|
||||
PIVector<PIFile::FileInfo> td = PIDir(de.path).allEntries();
|
||||
for (int i = 0; i < td.size_s(); i++) {
|
||||
if (td[i].isDir()) dirs.push_front(td[i]);
|
||||
else ret << td[i];
|
||||
}
|
||||
} else ret << de;
|
||||
}
|
||||
ret.insert(0, dirs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool PIDir::make(const PIString & path, bool withParents) {
|
||||
PIDir d(path);
|
||||
if (d.isExists()) return true;
|
||||
return d.make(withParents);
|
||||
}
|
||||
|
||||
|
||||
bool PIDir::setCurrent(const PIString & path) {
|
||||
#ifdef WINDOWS
|
||||
if (SetCurrentDirectory((LPCTSTR)(path.data())) != 0) return true;
|
||||
#else
|
||||
if (chdir(path.data()) == 0) return true;
|
||||
#endif
|
||||
printf("[PIDir] setCurrent(\"%s\") error: %s\n", path.data(), errorString().data());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool PIDir::makeDir(const PIString & path) {
|
||||
#ifdef WINDOWS
|
||||
if (CreateDirectory((LPCTSTR)(path.data()), NULL) != 0) return true;
|
||||
#else
|
||||
if (mkdir(path.data(), 16877) == 0) return true;
|
||||
#endif
|
||||
printf("[PIDir] makeDir(\"%s\") error: %s\n", path.data(), errorString().data());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool PIDir::removeDir(const PIString & path) {
|
||||
#ifdef WINDOWS
|
||||
if (RemoveDirectory((LPCTSTR)(path.data())) != 0) return true;
|
||||
#else
|
||||
if (rmdir(path.data()) == 0) return true;
|
||||
#endif
|
||||
printf("[PIDir] removeDir(\"%s\") error: %s\n", path.data(), errorString().data());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool PIDir::renameDir(const PIString & path, const PIString & new_name) {
|
||||
if (::rename(path.data(), new_name.data()) == 0) return true;
|
||||
printf("[PIDir] renameDir(\"%s\", \"%s\") error: %s\n", path.data(), new_name.data(), errorString().data());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//#endif
|
||||
130
src/io/pidir.h
Executable file
130
src/io/pidir.h
Executable file
@@ -0,0 +1,130 @@
|
||||
/*! \file pidir.h
|
||||
* \brief Local directory
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Directory
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIDIR_H
|
||||
#define PIDIR_H
|
||||
|
||||
#include "pifile.h"
|
||||
//#if !defined(ANDROID)
|
||||
|
||||
class PIP_EXPORT PIDir
|
||||
{
|
||||
public:
|
||||
|
||||
//! Constructs directory with path "path"
|
||||
PIDir(const PIString & dir = PIString());
|
||||
|
||||
//! Constructs directory with "file" directory path "path"
|
||||
PIDir(const PIFile & file);
|
||||
|
||||
|
||||
//! Returns if this directory is exists
|
||||
bool isExists() const {return PIDir::isExists(path_);}
|
||||
|
||||
//! Returns if path of this directory is absolute
|
||||
bool isAbsolute() const;
|
||||
|
||||
//! Returns if path of this directory is relative
|
||||
bool isRelative() const {return !isAbsolute();}
|
||||
|
||||
|
||||
//! Returns path of this directory
|
||||
const PIString & path() const {return path_;}
|
||||
|
||||
//! Returns absolute path of this directory
|
||||
PIString absolutePath() const;
|
||||
|
||||
/** \brief Simplify path of this directory
|
||||
* \details This function remove repeatedly separators and
|
||||
* resolve ".." in path. E.g. "/home/.//peri4/src/../.." will
|
||||
* become "/home" \n This function returns reference to this %PIDir */
|
||||
PIDir & cleanPath();
|
||||
|
||||
//! Returns %PIDir with simplified path of this directory
|
||||
PIDir cleanedPath() const {PIDir d(path_); d.cleanPath(); return d;}
|
||||
|
||||
//! Returns relative to this directory path "path"
|
||||
PIString relative(const PIString & path) const;
|
||||
|
||||
//! Set this directory path to simplified "path"
|
||||
PIDir & setDir(const PIString & path);
|
||||
|
||||
//! Set this directory path as current for application
|
||||
bool setCurrent() {return PIDir::setCurrent(path_);}
|
||||
|
||||
|
||||
/** \brief Returns this directory content
|
||||
* \details Scan this directory and returns all directories
|
||||
* and files in one list, sorted alphabetically. This list
|
||||
* contains also "." and ".." members.
|
||||
* \attention This function doesn`t scan content of inner
|
||||
* directories! */
|
||||
PIVector<PIFile::FileInfo> entries();
|
||||
|
||||
/** \brief Returns all this directory content
|
||||
* \details Scan this directory recursively and returns all
|
||||
* directories and files in one list, sorted alphabetically.
|
||||
* This list doesn`t contains "." and ".." members.
|
||||
* Files placed after directories in this list */
|
||||
PIVector<PIFile::FileInfo> allEntries();
|
||||
|
||||
bool make(bool withParents = true);
|
||||
bool remove() {return PIDir::remove(path_);}
|
||||
bool rename(const PIString & new_name) {if (!PIDir::rename(path_, new_name)) return false; path_ = new_name; return true;}
|
||||
PIDir & cd(const PIString & path);
|
||||
PIDir & up() {return cd("..");}
|
||||
|
||||
bool operator ==(const PIDir & d) const;
|
||||
bool operator !=(const PIDir & d) const {return !((*this) == d);}
|
||||
|
||||
static const PIChar separator;
|
||||
|
||||
static PIDir current();
|
||||
static PIDir home();
|
||||
static PIDir temporary();
|
||||
static PIVector<PIFile::FileInfo> allEntries(const PIString & path);
|
||||
static bool isExists(const PIString & path);
|
||||
static bool make(const PIString & path, bool withParents = true);
|
||||
static bool remove(const PIString & path) {return removeDir(path);}
|
||||
static bool rename(const PIString & path, const PIString & new_name) {return PIDir::renameDir(path, new_name);}
|
||||
static bool setCurrent(const PIString & path);
|
||||
static bool setCurrent(const PIDir & dir) {return setCurrent(dir.path_);}
|
||||
|
||||
private:
|
||||
static bool makeDir(const PIString & path);
|
||||
static bool removeDir(const PIString & path);
|
||||
static bool renameDir(const PIString & path, const PIString & new_name);
|
||||
|
||||
PIString path_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
inline bool operator <(const PIFile::FileInfo & v0, const PIFile::FileInfo & v1) {return (v0.path < v1.path);}
|
||||
inline bool operator >(const PIFile::FileInfo & v0, const PIFile::FileInfo & v1) {return (v0.path > v1.path);}
|
||||
inline bool operator ==(const PIFile::FileInfo & v0, const PIFile::FileInfo & v1) {return (v0.path == v1.path);}
|
||||
inline bool operator !=(const PIFile::FileInfo & v0, const PIFile::FileInfo & v1) {return (v0.path != v1.path);}
|
||||
|
||||
inline PICout operator <<(PICout s, const PIDir & v) {s.setControl(0, true); s << "PIDir(\"" << v.path() << "\")"; s.restoreControl(); return s;}
|
||||
|
||||
|
||||
#endif // PIDIR_H
|
||||
989
src/io/piethernet.cpp
Executable file
989
src/io/piethernet.cpp
Executable file
@@ -0,0 +1,989 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Ethernet, UDP/TCP Broadcast/Multicast
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "piethernet.h"
|
||||
#include "piconfig.h"
|
||||
#ifdef QNX
|
||||
# include <net/if_dl.h>
|
||||
# include <hw/nicinfo.h>
|
||||
# include <sys/dcmd_io-net.h>
|
||||
#endif
|
||||
#ifdef WINDOWS
|
||||
# include <ws2tcpip.h>
|
||||
# define ip_mreqn ip_mreq
|
||||
# define imr_address imr_interface
|
||||
#endif
|
||||
|
||||
|
||||
/** \class PIEthernet
|
||||
* \brief Ethernet device
|
||||
* \details
|
||||
* \section PIEthernet_sec0 Synopsis
|
||||
* %PIEthernet designed to work with IPv4 network via two protocols:
|
||||
* UDP and TCP. This class allow you send and receive packets to/from
|
||||
* another computer through network. Also it supports broadcast and
|
||||
* multicast extensions.
|
||||
*
|
||||
* \section PIEthernet_sec1 IPv4
|
||||
*
|
||||
*
|
||||
* \section PIEthernet_sec2 UDP
|
||||
* User Datagram Protocol
|
||||
*
|
||||
* \section PIEthernet_sec3 TCP
|
||||
* Transmission Control Protocol
|
||||
*
|
||||
* */
|
||||
|
||||
REGISTER_DEVICE(PIEthernet);
|
||||
|
||||
|
||||
PIEthernet::PIEthernet(): PIIODevice("", ReadWrite) {
|
||||
construct();
|
||||
setType(UDP);
|
||||
setParameters(PIEthernet::ReuseAddress | PIEthernet::MulticastLoop);
|
||||
//if (type_ != UDP) init();
|
||||
}
|
||||
|
||||
|
||||
PIEthernet::PIEthernet(PIEthernet::Type type_, const PIString & ip_port, const PIFlags<PIEthernet::Parameters> params_): PIIODevice(ip_port, ReadWrite) {
|
||||
construct();
|
||||
parseAddress(ip_port, &ip_, &port_);
|
||||
setType(type_);
|
||||
setParameters(params_);
|
||||
if (type_ != UDP) init();
|
||||
}
|
||||
|
||||
|
||||
PIEthernet::PIEthernet(int sock_, PIString ip_port): PIIODevice("", ReadWrite) {
|
||||
construct();
|
||||
parseAddress(ip_port, &ip_s, &port_s);
|
||||
sock = sock_;
|
||||
init_ = opened_ = connected_ = true;
|
||||
setParameters(PIEthernet::ReuseAddress | PIEthernet::MulticastLoop);
|
||||
setType(TCP_Client, false);
|
||||
setPath(ip_port);
|
||||
}
|
||||
|
||||
|
||||
PIEthernet::~PIEthernet() {
|
||||
piMonitor.ethernets--;
|
||||
stop();
|
||||
closeDevice();
|
||||
}
|
||||
|
||||
|
||||
void PIEthernet::construct() {
|
||||
piMonitor.ethernets++;
|
||||
connected_ = connecting_ = listen_threaded = server_bounded = false;
|
||||
port_ = port_s = port_r = 0;
|
||||
sock = sock_s = -1;
|
||||
setReadTimeout(10000.);
|
||||
setWriteTimeout(10000.);
|
||||
setTTL(64);
|
||||
setMulticastTTL(1);
|
||||
server_thread_.setData(this);
|
||||
server_thread_.setName("__S__server_thread");
|
||||
setThreadedReadBufferSize(65536);
|
||||
setPriority(piHigh);
|
||||
}
|
||||
|
||||
|
||||
bool PIEthernet::init() {
|
||||
//cout << "init " << type_ << endl;
|
||||
if (sock_s == sock)
|
||||
sock_s = -1;
|
||||
closeSocket(sock);
|
||||
closeSocket(sock_s);
|
||||
int st = 0, pr = 0;
|
||||
if (type() == UDP) {
|
||||
st = SOCK_DGRAM;
|
||||
pr = IPPROTO_UDP;
|
||||
} else {
|
||||
st = SOCK_STREAM;
|
||||
pr = IPPROTO_TCP;
|
||||
}
|
||||
PIFlags<Parameters> params = parameters();
|
||||
sock = socket(AF_INET, st, pr);
|
||||
if (params[SeparateSockets])
|
||||
sock_s = socket(AF_INET, st, pr);
|
||||
else
|
||||
sock_s = sock;
|
||||
if (sock == -1 || sock_s == -1) {
|
||||
piCoutObj << "Can`t create socket, " << ethErrorString();
|
||||
return false;
|
||||
}
|
||||
if (params[PIEthernet::ReuseAddress]) ethSetsockoptBool(sock, SOL_SOCKET, SO_REUSEADDR);
|
||||
if (params[PIEthernet::Broadcast]) ethSetsockoptBool(sock, SOL_SOCKET, SO_BROADCAST);
|
||||
applyTimeouts();
|
||||
applyOptInt(IPPROTO_IP, IP_TTL, TTL());
|
||||
//cout << "inited " << sock << ": bc = " << params << endl;
|
||||
//fcntl(sock, F_SETFL, 0/*O_NONBLOCK*/);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void PIEthernet::parseAddress(const PIString & ipp, PIString * ip, int * port) {
|
||||
if (ip != 0) *ip = ipp.left(ipp.find(":"));
|
||||
if (port != 0) *port = ipp.right(ipp.length() - ipp.find(":") - 1).toInt();
|
||||
}
|
||||
|
||||
|
||||
bool PIEthernet::openDevice() {
|
||||
if (connected_) return true;
|
||||
init();
|
||||
if (sock == -1 || path().isEmpty()) return false;
|
||||
parseAddress(path(), &ip_, &port_);
|
||||
if (type() == TCP_Client)
|
||||
connecting_ = true;
|
||||
if (type() != UDP || mode() == PIIODevice::WriteOnly)
|
||||
return true;
|
||||
memset(&addr_, 0, sizeof(addr_));
|
||||
addr_.sin_family = AF_INET;
|
||||
addr_.sin_port = htons(port_);
|
||||
PIFlags<Parameters> params = parameters();
|
||||
if (params[PIEthernet::Broadcast]) addr_.sin_addr.s_addr = INADDR_ANY;
|
||||
else addr_.sin_addr.s_addr = inet_addr(ip_.data());
|
||||
#ifdef QNX
|
||||
addr_.sin_len = sizeof(addr_);
|
||||
#endif
|
||||
//piCout << "bind to" << (params[PIEthernet::Broadcast] ? "255.255.255.255" : ip_) << ":" << port_ << " ...";
|
||||
int tries = 0;
|
||||
while ((bind(sock, (sockaddr * )&addr_, sizeof(addr_)) == -1) && (tries < 10)) {
|
||||
init();
|
||||
tries++;
|
||||
}
|
||||
if (tries == 10) {
|
||||
piCoutObj << "Can`t bind to " << ip_ << ":" << port_ << ", " << ethErrorString();
|
||||
return false;
|
||||
}
|
||||
opened_ = true;
|
||||
while (!mcast_queue.isEmpty())
|
||||
joinMulticastGroup(mcast_queue.dequeue());
|
||||
//cout << "!" << endl;
|
||||
applyTimeouts();
|
||||
applyOptInt(IPPROTO_IP, IP_TTL, TTL());
|
||||
port_r = 0;
|
||||
ip_r.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PIEthernet::closeDevice() {
|
||||
//cout << "close\n";
|
||||
if (server_thread_.isRunning()) {
|
||||
server_thread_.stop();
|
||||
if (!server_thread_.waitForFinish(100))
|
||||
server_thread_.terminate();
|
||||
}
|
||||
if (sock_s == sock)
|
||||
sock_s = -1;
|
||||
closeSocket(sock);
|
||||
closeSocket(sock_s);
|
||||
piForeach (PIEthernet * i, clients_)
|
||||
delete i;
|
||||
clients_.clear();
|
||||
if (connected_) disconnected(false);
|
||||
connected_ = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void PIEthernet::closeSocket(int & sd) {
|
||||
if (sd != -1)
|
||||
ethClosesocket(sd);
|
||||
sd = -1;
|
||||
}
|
||||
|
||||
|
||||
void PIEthernet::applyTimeouts() {
|
||||
if (sock < 0) return;
|
||||
double rtm = readTimeout(), wtm = writeTimeout();
|
||||
applyTimeout(sock, SO_RCVTIMEO, rtm);
|
||||
applyTimeout(sock, SO_SNDTIMEO, wtm);
|
||||
if (sock_s != sock && sock_s != -1) {
|
||||
applyTimeout(sock_s, SO_RCVTIMEO, rtm);
|
||||
applyTimeout(sock_s, SO_SNDTIMEO, wtm);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIEthernet::applyTimeout(int fd, int opt, double ms) {
|
||||
if (fd == 0) return;
|
||||
double s = ms / 1000.;
|
||||
timeval tm;
|
||||
tm.tv_sec = piFloord(s); s -= tm.tv_sec;
|
||||
tm.tv_usec = s * 1000000.;
|
||||
//piCoutObj << "setReadIsBlocking" << yes;
|
||||
ethSetsockopt(fd, SOL_SOCKET, opt, &tm, sizeof(tm));
|
||||
}
|
||||
|
||||
|
||||
void PIEthernet::applyOptInt(int level, int opt, int val) {
|
||||
if (sock < 0) return;
|
||||
ethSetsockoptInt(sock, level, opt, val);
|
||||
if (sock_s != sock && sock_s != -1)
|
||||
ethSetsockoptInt(sock_s, level, opt, val);
|
||||
}
|
||||
|
||||
|
||||
void PIEthernet::setParameter(PIEthernet::Parameters parameter, bool on) {
|
||||
PIFlags<Parameters> cp = (PIFlags<Parameters>)(property("parameters").toInt());
|
||||
cp.setFlag(parameter, on);
|
||||
setParameters(cp);
|
||||
}
|
||||
|
||||
|
||||
bool PIEthernet::joinMulticastGroup(const PIString & group) {
|
||||
if (sock == -1) init();
|
||||
if (sock == -1) return false;
|
||||
if (type() != UDP) {
|
||||
piCoutObj << "Only UDP sockets can join multicast groups";
|
||||
return false;
|
||||
}
|
||||
if (!opened_) {
|
||||
if (mcast_queue.contains(group))
|
||||
return false;
|
||||
mcast_queue.enqueue(group);
|
||||
if (!mcast_groups.contains(group)) mcast_groups << group;
|
||||
return true;
|
||||
}
|
||||
PIFlags<Parameters> params = parameters();
|
||||
#ifndef QNX
|
||||
if (!params[Broadcast])
|
||||
;//piCoutObj << "Warning: \"Broadcast\" parameter not set, \"joinMulticastGroup(\"" << group << "\")\" may be useless!";
|
||||
parseAddress(path(), &ip_, &port_);
|
||||
struct ip_mreqn mreq;
|
||||
memset(&mreq, 0, sizeof(mreq));
|
||||
#ifdef LINUX
|
||||
//mreq.imr_address.s_addr = INADDR_ANY;
|
||||
/*PIEthernet::InterfaceList il = interfaces();
|
||||
const PIEthernet::Interface * ci = il.getByAddress(ip_);
|
||||
if (ci != 0) mreq.imr_ifindex = ci->index;*/
|
||||
#endif
|
||||
if (params[PIEthernet::Broadcast]) mreq.imr_address.s_addr = INADDR_ANY;
|
||||
else mreq.imr_address.s_addr = inet_addr(ip_.data());
|
||||
/*#ifndef WINDOWS
|
||||
PIEthernet::InterfaceList il = interfaces();
|
||||
const PIEthernet::Interface * ci = il.getByAddress(ip_);
|
||||
if (ci != 0) mreq.imr_ifindex = ci->index;
|
||||
#endif*/
|
||||
//piCout << "join group" << group << "ip" << ip_ << "with index" << mreq.imr_ifindex << "socket" << sock;
|
||||
mreq.imr_multiaddr.s_addr = inet_addr(group.data());
|
||||
if (ethSetsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) != 0) {
|
||||
piCoutObj << "Can`t join multicast group " << group << ", " << ethErrorString();
|
||||
return false;
|
||||
}
|
||||
if (params[PIEthernet::MulticastLoop]) ethSetsockoptInt(sock, IPPROTO_IP, IP_MULTICAST_LOOP);
|
||||
applyOptInt(IPPROTO_IP, IP_MULTICAST_TTL, multicastTTL());
|
||||
if (!mcast_groups.contains(group)) mcast_groups << group;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PIEthernet::leaveMulticastGroup(const PIString & group) {
|
||||
if (sock == -1) init();
|
||||
if (sock == -1) return false;
|
||||
if (type() != UDP) {
|
||||
piCoutObj << "Only UDP sockets can leave multicast groups";
|
||||
return false;
|
||||
}
|
||||
PIFlags<Parameters> params = parameters();
|
||||
#ifndef QNX
|
||||
/// TODO windows
|
||||
parseAddress(path(), &ip_, &port_);
|
||||
struct ip_mreqn mreq;
|
||||
memset(&mreq, 0, sizeof(mreq));
|
||||
if (params[PIEthernet::Broadcast]) mreq.imr_address.s_addr = INADDR_ANY;
|
||||
else mreq.imr_address.s_addr = inet_addr(ip_.data());
|
||||
mreq.imr_multiaddr.s_addr = inet_addr(group.data());
|
||||
if (ethSetsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) {
|
||||
piCoutObj << "Can`t leave multicast group " << group << ", " << ethErrorString();
|
||||
return false;
|
||||
}
|
||||
mcast_groups.removeAll(group);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PIEthernet::connect() {
|
||||
connecting_ = true;
|
||||
return true;
|
||||
/*if (sock == -1) return false;
|
||||
memset(&addr_, 0, sizeof(addr_));
|
||||
parseAddress(path_, &ip_, &port_);
|
||||
addr_.sin_port = htons(port_);
|
||||
addr_.sin_addr.s_addr = inet_addr(ip_.data());
|
||||
addr_.sin_family = AF_INET;
|
||||
#ifdef QNX
|
||||
addr_.sin_len = sizeof(addr_);
|
||||
#endif
|
||||
//piCoutObj << "connect to " << ip << ":" << port_;
|
||||
connected_ = (::connect(sock, (sockaddr * )&addr_, sizeof(addr_)) == 0);
|
||||
if (!connected_)
|
||||
piCoutObj << "Can`t connect to " << ip_ << ":" << port_ << ", " << ethErrorString();
|
||||
opened_ = connected_;
|
||||
if (connected_) connected();
|
||||
return connected_;*/
|
||||
}
|
||||
|
||||
|
||||
bool PIEthernet::listen(bool threaded) {
|
||||
if (sock == -1) init();
|
||||
if (sock == -1) return false;
|
||||
if (threaded) {
|
||||
if (server_thread_.isRunning()) {
|
||||
if (!server_bounded) return true;
|
||||
server_thread_.stop();
|
||||
if (!server_thread_.waitForFinish(100))
|
||||
server_thread_.terminate();
|
||||
}
|
||||
listen_threaded = true;
|
||||
server_bounded = false;
|
||||
server_thread_.start(server_func);
|
||||
return true;
|
||||
}
|
||||
listen_threaded = server_bounded = false;
|
||||
parseAddress(path(), &ip_, &port_);
|
||||
memset(&addr_, 0, sizeof(addr_));
|
||||
addr_.sin_port = htons(port_);
|
||||
addr_.sin_addr.s_addr = inet_addr(ip_.data());
|
||||
addr_.sin_family = AF_INET;
|
||||
#ifdef QNX
|
||||
addr_.sin_len = sizeof(addr_);
|
||||
#endif
|
||||
opened_ = false;
|
||||
int tries = 0;
|
||||
while ((bind(sock, (sockaddr * )&addr_, sizeof(addr_)) == -1) && (tries < 10)) {
|
||||
init();
|
||||
tries++;
|
||||
}
|
||||
if (tries == 10) {
|
||||
piCoutObj << "Can`t bind to " << ip_ << ":" << port_ << ", " << ethErrorString();
|
||||
return false;
|
||||
}
|
||||
if (::listen(sock, 64) == -1) {
|
||||
piCoutObj << "Can`t listen on "<< ip_ << ":" << port_ << ", " << ethErrorString();
|
||||
return false;
|
||||
}
|
||||
opened_ = server_bounded = true;
|
||||
//piCoutObj << "listen on " << ip_ << ":" << port_;
|
||||
server_thread_.start(server_func);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int PIEthernet::read(void * read_to, int max_size) {
|
||||
//cout << "read " << sock << endl;
|
||||
if (sock == -1) init();
|
||||
if (sock == -1 || read_to == 0) return -1;
|
||||
int rs = 0, s = 0, lerr = 0;
|
||||
sockaddr_in client_addr;
|
||||
socklen_t slen = sizeof(client_addr);
|
||||
//piCoutObj << "read from " << ip_ << ":" << port_ << endl;
|
||||
switch (type()) {
|
||||
case TCP_SingleTCP:
|
||||
::listen(sock, 64);
|
||||
s = accept(sock, (sockaddr * )&client_addr, &slen);
|
||||
if (s == -1) {
|
||||
//piCoutObj << "Can`t accept new connection, " << ethErrorString();
|
||||
msleep(1);
|
||||
return -1;
|
||||
}
|
||||
rs = ethRecv(s, read_to, max_size);
|
||||
closeSocket(s);
|
||||
return rs;
|
||||
case TCP_Client:
|
||||
if (connecting_) {
|
||||
memset(&addr_, 0, sizeof(addr_));
|
||||
parseAddress(path(), &ip_, &port_);
|
||||
addr_.sin_port = htons(port_);
|
||||
addr_.sin_addr.s_addr = inet_addr(ip_.data());
|
||||
addr_.sin_family = AF_INET;
|
||||
#ifdef QNX
|
||||
addr_.sin_len = sizeof(addr_);
|
||||
#endif
|
||||
//piCoutObj << "connect to " << ip_ << ":" << port_ << "...";
|
||||
connected_ = (::connect(sock, (sockaddr * )&addr_, sizeof(addr_)) == 0);
|
||||
//piCoutObj << "connect to " << ip_ << ":" << port_ << connected_;
|
||||
if (!connected_)
|
||||
piCoutObj << "Can`t connect to " << ip_ << ":" << port_ << ", " << ethErrorString();
|
||||
opened_ = connected_;
|
||||
if (connected_) {
|
||||
connecting_ = false;
|
||||
connected();
|
||||
} else
|
||||
piMSleep(10);
|
||||
//piCout << "connected to" << path();
|
||||
}
|
||||
if (!connected_) return -1;
|
||||
errorClear();
|
||||
rs = ethRecv(sock, read_to, max_size);
|
||||
//piCoutObj << "readed" << rs;
|
||||
if (rs <= 0) {
|
||||
lerr = ethErrorCore();
|
||||
#ifdef WINDOWS
|
||||
if (lerr == WSAEWOULDBLOCK || /*lerr == NO_ERROR ||*/ lerr == WSAETIMEDOUT) {
|
||||
#else
|
||||
if (lerr == EAGAIN || lerr == EWOULDBLOCK) {
|
||||
#endif
|
||||
//piCoutObj << errorString();
|
||||
piMSleep(10);
|
||||
return -1;
|
||||
}
|
||||
if (connected_) {
|
||||
init();
|
||||
disconnected(rs < 0);
|
||||
}
|
||||
connected_ = false;
|
||||
//piCoutObj << "eth" << ip_ << "disconnected";
|
||||
}
|
||||
if (rs > 0) received(read_to, rs);
|
||||
return rs;
|
||||
case UDP:
|
||||
memset(&raddr_, 0, sizeof(raddr_));
|
||||
rs = ethRecvfrom(sock, read_to, max_size, 0, (sockaddr*)&raddr_);
|
||||
//piCout << "eth" << path() << "read return" << rs << errorString();
|
||||
if (rs > 0) {
|
||||
port_r = ntohs(raddr_.sin_port);
|
||||
ip_r = inet_ntoa(raddr_.sin_addr);
|
||||
//piCout << "received from" << lastReadAddress();
|
||||
received(read_to, rs);
|
||||
}
|
||||
return rs;
|
||||
//return ::read(sock, read_to, max_size);
|
||||
default: break;
|
||||
//return ::read(sock, (char * )read_to, max_size);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int PIEthernet::write(const void * data, int max_size) {
|
||||
if (sock == -1) init();
|
||||
if (sock == -1 || !isWriteable()) {
|
||||
//piCoutObj << "Can`t send to uninitialized socket";
|
||||
return -1;
|
||||
}
|
||||
//piCoutObj << "sending to " << ip_s << ":" << port_s << " " << max_size << " bytes";
|
||||
int ret = 0;
|
||||
switch (type()) {
|
||||
case TCP_SingleTCP:
|
||||
memset(&addr_, 0, sizeof(addr_));
|
||||
addr_.sin_port = htons(port_s);
|
||||
addr_.sin_addr.s_addr = inet_addr(ip_s.data());
|
||||
addr_.sin_family = AF_INET;
|
||||
#ifdef QNX
|
||||
addr_.sin_len = sizeof(addr_);
|
||||
#endif
|
||||
//piCoutObj << "connect SingleTCP" << ip_s << ":" << port_s << "...";
|
||||
if (::connect(sock, (sockaddr * )&addr_, sizeof(addr_)) != 0) {
|
||||
//piCoutObj << "Can`t connect to " << ip_s << ":" << port_s << ", " << ethErrorString();
|
||||
msleep(1);
|
||||
return -1;
|
||||
}
|
||||
//piCoutObj << "ok, write SingleTCP" << int(data) << max_size << "bytes ...";
|
||||
ret = ::send(sock, (const char *)data, max_size, 0);
|
||||
//piCoutObj << "ok, ret" << ret;
|
||||
closeSocket(sock);
|
||||
init();
|
||||
return ret;
|
||||
case UDP:
|
||||
saddr_.sin_port = htons(port_s);
|
||||
/*if (params[PIEthernet::Broadcast]) saddr_.sin_addr.s_addr = INADDR_BROADCAST;
|
||||
else*/ saddr_.sin_addr.s_addr = inet_addr(ip_s.data());
|
||||
saddr_.sin_family = AF_INET;
|
||||
//piCout << "[PIEth] write to" << ip_s << ":" << port_s << "socket" << sock_s << max_size << "bytes ...";
|
||||
return ethSendto(sock_s, data, max_size, 0, (sockaddr * )&saddr_, sizeof(saddr_));
|
||||
//piCout << "[PIEth] write to" << ip_s << ":" << port_s << "ok";
|
||||
case TCP_Client:
|
||||
if (connecting_) {
|
||||
memset(&addr_, 0, sizeof(addr_));
|
||||
parseAddress(path(), &ip_, &port_);
|
||||
addr_.sin_port = htons(port_);
|
||||
addr_.sin_addr.s_addr = inet_addr(ip_.data());
|
||||
addr_.sin_family = AF_INET;
|
||||
#ifdef QNX
|
||||
addr_.sin_len = sizeof(addr_);
|
||||
#endif
|
||||
//piCoutObj << "connect to " << ip << ":" << port_;
|
||||
connected_ = (::connect(sock, (sockaddr * )&addr_, sizeof(addr_)) == 0);
|
||||
if (!connected_)
|
||||
piCoutObj << "Can`t connect to " << ip_ << ":" << port_ << ", " << ethErrorString();
|
||||
opened_ = connected_;
|
||||
if (connected_) {
|
||||
connecting_ = false;
|
||||
connected();
|
||||
}
|
||||
}
|
||||
if (!connected_) return -1;
|
||||
ret = ::send(sock, (const char *)data, max_size, 0);
|
||||
if (ret < 0) {
|
||||
connected_ = false; {
|
||||
init();
|
||||
disconnected(true);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
default: break;
|
||||
//return ::read(sock, read_to, max_size);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void PIEthernet::server_func(void * eth) {
|
||||
PIEthernet * ce = (PIEthernet * )eth;
|
||||
if (ce->listen_threaded) {
|
||||
if (!ce->server_bounded) {
|
||||
if (!ce->listen(false)) {
|
||||
ce->listen_threaded = true;
|
||||
piMSleep(100);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
sockaddr_in client_addr;
|
||||
socklen_t slen = sizeof(client_addr);
|
||||
int s = accept(ce->sock, (sockaddr * )&client_addr, &slen);
|
||||
if (s == -1) {
|
||||
if (ce->debug()) piCout << "[PIEthernet] Can`t accept new connection, " << ethErrorString();
|
||||
piMSleep(10);
|
||||
return;
|
||||
}
|
||||
PIString ip(inet_ntoa(client_addr.sin_addr));
|
||||
ip += ":" + PIString::fromNumber(htons(client_addr.sin_port));
|
||||
ce->clients_ << new PIEthernet(s, ip);
|
||||
ce->newConnection(ce->clients_.back());
|
||||
//cout << "connected " << ip << endl;
|
||||
//char d[256];
|
||||
//cout << " recv " << recv(s, d, 256, 0) << endl;
|
||||
//cout << recv(ce->clients_.back()->sock, d, 256, 0) << endl;
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool PIEthernet::configureDevice(const void * e_main, const void * e_parent) {
|
||||
PIConfig::Entry * em = (PIConfig::Entry * )e_main;
|
||||
PIConfig::Entry * ep = (PIConfig::Entry * )e_parent;
|
||||
setReadIP(readDeviceSetting<PIString>("ip", readIP(), em, ep));
|
||||
setReadPort(readDeviceSetting<int>("port", readPort(), em, ep));
|
||||
setParameter(PIEthernet::Broadcast, readDeviceSetting<bool>("broadcast", isParameterSet(PIEthernet::Broadcast), em, ep));
|
||||
setParameter(PIEthernet::ReuseAddress, readDeviceSetting<bool>("reuseAddress", isParameterSet(PIEthernet::ReuseAddress), em, ep));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void PIEthernet::propertyChanged(const PIString & name) {
|
||||
if (name.endsWith("Timeout")) applyTimeouts();
|
||||
if (name == "TTL") applyOptInt(IPPROTO_IP, IP_TTL, TTL());
|
||||
if (name == "MulticastTTL") applyOptInt(IPPROTO_IP, IP_MULTICAST_TTL, multicastTTL());
|
||||
}
|
||||
|
||||
|
||||
PIString PIEthernet::constructFullPath() const {
|
||||
PIString ret(fullPathPrefix() + "://");
|
||||
ret << (type() == PIEthernet::UDP ? "UDP" : "TCP") << ":" << readIP() << ":" << readPort();
|
||||
if (type() == PIEthernet::UDP) {
|
||||
ret << ":" << sendIP() << ":" << sendPort();
|
||||
piForeachC (PIString & m, multicastGroups())
|
||||
ret << ":mcast:" << m;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void PIEthernet::configureFromFullPath(const PIString & full_path) {
|
||||
PIStringList pl = full_path.split(":");
|
||||
bool mcast = false;
|
||||
for (int i = 0; i < pl.size_s(); ++i) {
|
||||
PIString p(pl[i]);
|
||||
switch (i) {
|
||||
case 0:
|
||||
p = p.toLowerCase();
|
||||
if (p == "udp") setType(UDP);
|
||||
if (p == "tcp") setType(TCP_Client);
|
||||
break;
|
||||
case 1: setReadIP(p); setSendIP(p); break;
|
||||
case 2: setReadPort(p.toInt()); setSendPort(p.toInt()); break;
|
||||
case 3: setSendIP(p); break;
|
||||
case 4: setSendPort(p.toInt()); break;
|
||||
}
|
||||
if (i <= 4) continue;
|
||||
if (i % 2 == 1) {if (p.toLowerCase() == "mcast") mcast = true;}
|
||||
else {if (mcast) {joinMulticastGroup(p); mcast = false;}}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PIEthernet::InterfaceList PIEthernet::interfaces() {
|
||||
PIEthernet::InterfaceList il;
|
||||
Interface ci;
|
||||
ci.index = -1;
|
||||
ci.mtu = 1500;
|
||||
#ifdef WINDOWS
|
||||
PIP_ADAPTER_INFO pAdapterInfo, pAdapter = 0;
|
||||
int ret = 0;
|
||||
ulong ulOutBufLen = sizeof(IP_ADAPTER_INFO);
|
||||
pAdapterInfo = (IP_ADAPTER_INFO * ) HeapAlloc(GetProcessHeap(), 0, (sizeof (IP_ADAPTER_INFO)));
|
||||
if (pAdapterInfo == 0) {
|
||||
piCout << "[PIEthernet] Error allocating memory needed to call GetAdaptersinfo";
|
||||
return il;
|
||||
}
|
||||
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
|
||||
HeapFree(GetProcessHeap(), 0, (pAdapterInfo));
|
||||
pAdapterInfo = (IP_ADAPTER_INFO *) HeapAlloc(GetProcessHeap(), 0, (ulOutBufLen));
|
||||
if (pAdapterInfo == 0) {
|
||||
piCout << "[PIEthernet] Error allocating memory needed to call GetAdaptersinfo";
|
||||
return il;
|
||||
}
|
||||
}
|
||||
if ((ret = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) {
|
||||
pAdapter = pAdapterInfo;
|
||||
while (pAdapter) {
|
||||
ci.name = PIString(pAdapter->AdapterName);
|
||||
ci.index = pAdapter->Index;
|
||||
ci.address = PIString(pAdapter->IpAddressList.IpAddress.String);
|
||||
if (ci.address == "0.0.0.0") {
|
||||
pAdapter = pAdapter->Next;
|
||||
continue;
|
||||
}
|
||||
ci.mac = macFromBytes(PIByteArray(pAdapter->Address, pAdapter->AddressLength));
|
||||
ci.netmask = PIString(pAdapter->IpAddressList.IpMask.String);
|
||||
ci.flags = PIEthernet::ifActive | PIEthernet::ifRunning;
|
||||
//if (ret->ifa_flags & IFF_BROADCAST) ci.flags |= PIEthernet::ifBroadcast;
|
||||
//if (ret->ifa_flags & IFF_MULTICAST) ci.flags |= PIEthernet::ifMulticast;
|
||||
if (pAdapter->Type == MIB_IF_TYPE_PPP) ci.flags |= PIEthernet::ifPTP;
|
||||
if (pAdapter->Type == MIB_IF_TYPE_LOOPBACK) ci.flags |= PIEthernet::ifLoopback;
|
||||
ci.broadcast.clear();
|
||||
ci.ptp.clear();
|
||||
/*if (ci.flags[PIEthernet::ifBroadcast])
|
||||
ci.broadcast = getSockAddr(ret->ifa_broadaddr);
|
||||
if (ci.flags[PIEthernet::ifPTP])
|
||||
ci.ptp = getSockAddr(ret->ifa_dstaddr);*/
|
||||
il << ci;
|
||||
pAdapter = pAdapter->Next;
|
||||
}
|
||||
} else
|
||||
piCout << "[PIEthernet] GetAdaptersInfo failed with error: " << ret;
|
||||
if (pAdapterInfo)
|
||||
HeapFree(GetProcessHeap(), 0, (pAdapterInfo));
|
||||
#else
|
||||
/*# ifdef QNX
|
||||
PIStringList il, sl;
|
||||
PIProcess proc;
|
||||
proc.setGrabOutput(true);
|
||||
proc.exec(ifconfigPath.c_str(), "-l");
|
||||
if (!proc.waitForFinish(1000)) return sl;
|
||||
PIString out(proc.readOutput());
|
||||
il = out.split(" ");
|
||||
il.removeAll("");
|
||||
piForeachC (PIString & i, il) {
|
||||
proc.exec(ifconfigPath.c_str(), i);
|
||||
if (!proc.waitForFinish(1000)) return il;
|
||||
sl << i.trimmed();
|
||||
out = proc.readOutput();
|
||||
int al = out.length();
|
||||
al = (al - out.replaceAll("alias", "").length()) / 5;
|
||||
for (int j = 0; j < al; ++j)
|
||||
sl << i.trimmed() + ":" + PIString::fromNumber(j);
|
||||
}
|
||||
return sl;
|
||||
# else
|
||||
PIStringList sl;
|
||||
PIProcess proc;
|
||||
proc.setGrabOutput(true);
|
||||
proc.exec(ifconfigPath.c_str(), "-s");
|
||||
if (!proc.waitForFinish(1000)) return sl;
|
||||
PIString out(proc.readOutput());
|
||||
out.cutLeft(out.find('\n') + 1);
|
||||
while (!out.isEmpty()) {
|
||||
sl << out.left(out.find(' '));
|
||||
out.cutLeft(out.find('\n') + 1);
|
||||
}
|
||||
return sl;
|
||||
# endif*/
|
||||
# ifdef ANDROID
|
||||
struct ifconf ifc;
|
||||
int s = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
|
||||
ifc.ifc_len = 256;
|
||||
ifc.ifc_buf = new char[ifc.ifc_len];
|
||||
if (ioctl(s, SIOCGIFCONF, &ifc) < 0) {
|
||||
piCout << "[PIEthernet] Can`t get interfaces:" << errorString();
|
||||
delete ifc.ifc_buf;
|
||||
return il;
|
||||
}
|
||||
int icnt = ifc.ifc_len / sizeof(ifreq);
|
||||
PIStringList inl;
|
||||
struct ifreq ir;
|
||||
for (int i = 0; i < icnt; ++i) {
|
||||
PIString in(ifc.ifc_req[i].ifr_name);
|
||||
if (in.isEmpty()) continue;
|
||||
ci.name = in;
|
||||
strcpy(ir.ifr_name, in.data());
|
||||
if (ioctl(s, SIOCGIFHWADDR, &ir) == 0)
|
||||
ci.mac = macFromBytes(PIByteArray(ir.ifr_hwaddr.sa_data, 6));
|
||||
if (ioctl(s, SIOCGIFADDR, &ir) >= 0)
|
||||
ci.address = getSockAddr(&ir.ifr_addr);
|
||||
if (ioctl(s, SIOCGIFNETMASK, &ir) >= 0)
|
||||
ci.netmask = getSockAddr(&ir.ifr_addr);
|
||||
ioctl(s, SIOCGIFMTU, &ci.mtu);
|
||||
il << ci;
|
||||
}
|
||||
delete ifc.ifc_buf;
|
||||
# else
|
||||
struct ifaddrs * ret;
|
||||
int s = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
|
||||
if (getifaddrs(&ret) == 0) {
|
||||
while (ret != 0) {
|
||||
if (ret->ifa_addr == 0) {
|
||||
ret = ret->ifa_next;
|
||||
continue;
|
||||
}
|
||||
if (ret->ifa_addr->sa_family != AF_INET) {
|
||||
ret = ret->ifa_next;
|
||||
continue;
|
||||
}
|
||||
ci.name = PIString(ret->ifa_name);
|
||||
ci.address = getSockAddr(ret->ifa_addr);
|
||||
ci.netmask = getSockAddr(ret->ifa_netmask);
|
||||
ci.mac.clear();
|
||||
# ifdef QNX
|
||||
int fd = ::open((PIString("/dev/io-net/") + ci.name).data(), O_RDONLY);
|
||||
if (fd != 0) {
|
||||
nic_config_t nic;
|
||||
devctl(fd, DCMD_IO_NET_GET_CONFIG, &nic, sizeof(nic), 0);
|
||||
::close(fd);
|
||||
ci.mac = macFromBytes(PIByteArray(nic.permanent_address, 6));
|
||||
}
|
||||
# else
|
||||
# ifdef MAC_OS
|
||||
PIString req = PISystemInfo::instance()->ifconfigPath + " " + ci.name + " | grep ether";
|
||||
FILE * fp = popen(req.data(), "r");
|
||||
if (fp != 0) {
|
||||
char in[256];
|
||||
if (fgets(in, 256, fp) != 0) {
|
||||
req = PIString(in).trim();
|
||||
ci.mac = req.cutLeft(req.find(" ") + 1).trim().toUpperCase();
|
||||
}
|
||||
pclose(fp);
|
||||
}
|
||||
# else
|
||||
if (s != -1) {
|
||||
struct ifreq ir;
|
||||
strcpy(ir.ifr_name, ret->ifa_name);
|
||||
if (ioctl(s, SIOCGIFHWADDR, &ir) == 0) {
|
||||
ci.mac = macFromBytes(PIByteArray(ir.ifr_hwaddr.sa_data, 6));
|
||||
ci.mtu = ir.ifr_mtu;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
# endif
|
||||
ci.flags = 0;
|
||||
if (ret->ifa_flags & IFF_UP) ci.flags |= PIEthernet::ifActive;
|
||||
if (ret->ifa_flags & IFF_RUNNING) ci.flags |= PIEthernet::ifRunning;
|
||||
if (ret->ifa_flags & IFF_BROADCAST) ci.flags |= PIEthernet::ifBroadcast;
|
||||
if (ret->ifa_flags & IFF_MULTICAST) ci.flags |= PIEthernet::ifMulticast;
|
||||
if (ret->ifa_flags & IFF_LOOPBACK) ci.flags |= PIEthernet::ifLoopback;
|
||||
if (ret->ifa_flags & IFF_POINTOPOINT) ci.flags |= PIEthernet::ifPTP;
|
||||
ci.broadcast.clear();
|
||||
ci.ptp.clear();
|
||||
if (ci.flags[PIEthernet::ifBroadcast])
|
||||
ci.broadcast = getSockAddr(ret->ifa_broadaddr);
|
||||
if (ci.flags[PIEthernet::ifPTP])
|
||||
ci.ptp = getSockAddr(ret->ifa_dstaddr);
|
||||
ci.index = if_nametoindex(ret->ifa_name);
|
||||
il << ci;
|
||||
ret = ret->ifa_next;
|
||||
}
|
||||
freeifaddrs(ret);
|
||||
} else
|
||||
piCout << "[PIEthernet] Can`t get interfaces:" << errorString();
|
||||
if (s != -1) ::close(s);
|
||||
# endif
|
||||
#endif
|
||||
return il;
|
||||
}
|
||||
|
||||
|
||||
PIString PIEthernet::interfaceAddress(const PIString & interface_) {
|
||||
#ifdef WINDOWS
|
||||
piCout << "[PIEthernet] Not implemented on Windows, use \"PIEthernet::allAddresses\" or \"PIEthernet::interfaces\" instead";
|
||||
return PIString();
|
||||
#else
|
||||
struct ifreq ifr;
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
strcpy(ifr.ifr_name, interface_.data());
|
||||
int s = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
ioctl(s, SIOCGIFADDR, &ifr);
|
||||
::close(s);
|
||||
struct sockaddr_in * sa = (struct sockaddr_in * )&ifr.ifr_addr;
|
||||
return PIString(inet_ntoa(sa->sin_addr));
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
PIStringList PIEthernet::allAddresses() {
|
||||
/*#ifdef WINDOWS
|
||||
PIStringList al;
|
||||
PIString ca;
|
||||
PIP_ADAPTER_INFO pAdapterInfo, pAdapter = 0;
|
||||
int ret = 0;
|
||||
ulong ulOutBufLen = sizeof(IP_ADAPTER_INFO);
|
||||
pAdapterInfo = (IP_ADAPTER_INFO * ) HeapAlloc(GetProcessHeap(), 0, (sizeof (IP_ADAPTER_INFO)));
|
||||
if (pAdapterInfo == 0) {
|
||||
piCout << "[PIEthernet] Error allocating memory needed to call GetAdaptersinfo";
|
||||
return PIStringList();
|
||||
}
|
||||
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
|
||||
HeapFree(GetProcessHeap(), 0, (pAdapterInfo));
|
||||
pAdapterInfo = (IP_ADAPTER_INFO *) HeapAlloc(GetProcessHeap(), 0, (ulOutBufLen));
|
||||
if (pAdapterInfo == 0) {
|
||||
piCout << "[PIEthernet] Error allocating memory needed to call GetAdaptersinfo";
|
||||
return PIStringList();
|
||||
}
|
||||
}
|
||||
if ((ret = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) {
|
||||
pAdapter = pAdapterInfo;
|
||||
while (pAdapter) {
|
||||
ca = PIString(pAdapter->IpAddressList.IpAddress.String);
|
||||
if (ca != "0.0.0.0") al << ca;
|
||||
pAdapter = pAdapter->Next;
|
||||
}
|
||||
} else
|
||||
piCout << "[PIEthernet] GetAdaptersInfo failed with error: " << ret;
|
||||
if (pAdapterInfo)
|
||||
HeapFree(GetProcessHeap(), 0, (pAdapterInfo));
|
||||
return al;
|
||||
#else*/
|
||||
PIEthernet::InterfaceList il = interfaces();
|
||||
PIStringList al;
|
||||
piForeachC (PIEthernet::Interface & i, il)
|
||||
al << i.address;
|
||||
return al.removeStrings("0.0.0.0");
|
||||
//#endif
|
||||
}
|
||||
|
||||
|
||||
// System wrap
|
||||
|
||||
|
||||
int PIEthernet::ethErrorCore() {
|
||||
#ifdef WINDOWS
|
||||
return WSAGetLastError();
|
||||
#else
|
||||
return errno;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
PIString PIEthernet::ethErrorString() {
|
||||
#ifdef WINDOWS
|
||||
char * msg;
|
||||
int err = WSAGetLastError();
|
||||
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&msg, 0, NULL);
|
||||
return "code " + PIString::fromNumber(err) + " - " + PIString(msg);
|
||||
#else
|
||||
return errorString();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int PIEthernet::ethRecv(int sock, void * buf, int size, int flags) {
|
||||
if (sock < 0) return -1;
|
||||
return recv(sock,
|
||||
#ifdef WINDOWS
|
||||
(char*)
|
||||
#endif
|
||||
buf, size, flags);
|
||||
}
|
||||
|
||||
|
||||
int PIEthernet::ethRecvfrom(int sock, void * buf, int size, int flags, sockaddr * addr) {
|
||||
if (sock < 0) return -1;
|
||||
#ifdef QNX
|
||||
return recv(sock, buf, size, flags);
|
||||
#else
|
||||
socklen_t len = sizeof(sockaddr);
|
||||
return recvfrom(sock,
|
||||
# ifdef WINDOWS
|
||||
(char*)
|
||||
# endif
|
||||
buf, size, flags, addr, &len);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int PIEthernet::ethSendto(int sock, const void * buf, int size, int flags, sockaddr * addr, int addr_len) {
|
||||
if (sock < 0) return -1;
|
||||
return sendto(sock,
|
||||
#ifdef WINDOWS
|
||||
(const char*)
|
||||
#endif
|
||||
buf, size, flags, addr, addr_len);
|
||||
}
|
||||
|
||||
|
||||
void PIEthernet::ethClosesocket(int sock) {
|
||||
if (sock < 0) return;
|
||||
#ifdef WINDOWS
|
||||
shutdown(sock, SD_BOTH);
|
||||
closesocket(sock);
|
||||
#else
|
||||
shutdown(sock, SHUT_RDWR);
|
||||
::close(sock);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int PIEthernet::ethSetsockopt(int sock, int level, int optname, const void * optval, int optlen) {
|
||||
if (sock < 0) return -1;
|
||||
return setsockopt(sock, level, optname,
|
||||
#ifdef WINDOWS
|
||||
(char*)
|
||||
#endif
|
||||
optval, optlen);
|
||||
}
|
||||
|
||||
|
||||
int PIEthernet::ethSetsockoptInt(int sock, int level, int optname, int value) {
|
||||
if (sock < 0) return -1;
|
||||
#ifdef WINDOWS
|
||||
DWORD
|
||||
#else
|
||||
int
|
||||
#endif
|
||||
so = value;
|
||||
return ethSetsockopt(sock, level, optname, &so, sizeof(so));
|
||||
}
|
||||
|
||||
|
||||
int PIEthernet::ethSetsockoptBool(int sock, int level, int optname, bool value) {
|
||||
if (sock < 0) return -1;
|
||||
#ifdef WINDOWS
|
||||
BOOL
|
||||
#else
|
||||
int
|
||||
#endif
|
||||
so = (value ? 1 : 0);
|
||||
return ethSetsockopt(sock, level, optname, &so, sizeof(so));
|
||||
}
|
||||
424
src/io/piethernet.h
Executable file
424
src/io/piethernet.h
Executable file
@@ -0,0 +1,424 @@
|
||||
/*! \file piethernet.h
|
||||
* \brief Ethernet device
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Ethernet, UDP/TCP Broadcast/Multicast
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIETHERNET_H
|
||||
#define PIETHERNET_H
|
||||
|
||||
#include "pitimer.h"
|
||||
#include "piiodevice.h"
|
||||
#include "piprocess.h"
|
||||
|
||||
class PIP_EXPORT PIEthernet: public PIIODevice
|
||||
{
|
||||
PIIODEVICE(PIEthernet)
|
||||
friend class PIPeer;
|
||||
public:
|
||||
|
||||
//! Contructs UDP %PIEthernet with empty read address
|
||||
PIEthernet();
|
||||
|
||||
//! \brief Type of %PIEthernet
|
||||
enum PIP_EXPORT Type {
|
||||
UDP /** UDP - User Datagram Protocol */ ,
|
||||
TCP_Client /** TCP client - allow connection to TCP server */ ,
|
||||
TCP_Server /** TCP server - receive connections from TCP clients */ ,
|
||||
TCP_SingleTCP
|
||||
};
|
||||
|
||||
//! \brief Parameters of %PIEthernet
|
||||
enum PIP_EXPORT Parameters {
|
||||
ReuseAddress /** Rebind address if there is already binded. Enabled by default */ = 0x1,
|
||||
Broadcast /** Broadcast send. Disabled by default */ = 0x2,
|
||||
SeparateSockets /** If this parameter is set, %PIEthernet will initialize two different sockets,
|
||||
for receive and send, instead of single one. Disabled by default */ = 0x4,
|
||||
MulticastLoop /** Enable receiving multicast packets from same host. Enabled by default */ = 0x8
|
||||
};
|
||||
|
||||
//! Contructs %PIEthernet with type "type", read address "ip_port" and parameters "params"
|
||||
PIEthernet(Type type, const PIString & ip_port = PIString(), const PIFlags<Parameters> params = 0);
|
||||
|
||||
virtual ~PIEthernet();
|
||||
|
||||
|
||||
//! Set read address
|
||||
void setReadAddress(const PIString & ip, int port) {setPath(ip + ":" + PIString::fromNumber(port));}
|
||||
|
||||
//! Set read address in format "i.i.i.i:p"
|
||||
void setReadAddress(const PIString & ip_port) {setPath(ip_port);}
|
||||
|
||||
//! Set read IP
|
||||
void setReadIP(const PIString & ip) {parseAddress(path(), &ip_, &port_); setPath(ip + ":" + PIString::fromNumber(port_));}
|
||||
|
||||
//! Set read port
|
||||
void setReadPort(int port) {parseAddress(path(), &ip_, &port_); setPath(ip_ + ":" + PIString::fromNumber(port));}
|
||||
|
||||
|
||||
//! Set send address
|
||||
void setSendAddress(const PIString & ip, int port) {ip_s = ip; port_s = port;}
|
||||
|
||||
//! Set send address in format "i.i.i.i:p"
|
||||
void setSendAddress(const PIString & ip_port) {parseAddress(ip_port, &ip_s, &port_s);}
|
||||
|
||||
//! Set send IP
|
||||
void setSendIP(const PIString & ip) {ip_s = ip;}
|
||||
|
||||
//! Set send port
|
||||
void setSendPort(int port) {port_s = port;}
|
||||
|
||||
|
||||
//! Returns read address in format "i.i.i.i:p"
|
||||
PIString readAddress() const {return path();}
|
||||
|
||||
//! Returns read IP
|
||||
PIString readIP() const {parseAddress(path(), &ip_, &port_); return ip_;}
|
||||
|
||||
//! Returns read port
|
||||
int readPort() const {parseAddress(path(), &ip_, &port_); return port_;}
|
||||
|
||||
|
||||
//! Returns send address in format "i.i.i.i:p"
|
||||
PIString sendAddress() const {return ip_s + ":" + PIString::fromNumber(port_s);}
|
||||
|
||||
//! Returns send IP
|
||||
PIString sendIP() const {return ip_s;}
|
||||
|
||||
//! Returns send port
|
||||
int sendPort() const {return port_s;}
|
||||
|
||||
|
||||
//! Returns address of last received UDP packet in format "i.i.i.i:p"
|
||||
PIString lastReadAddress() const {return ip_r + ":" + PIString::fromNumber(port_r);}
|
||||
|
||||
//! Returns IP of last received UDP packet
|
||||
PIString lastReadIP() const {return ip_r;}
|
||||
|
||||
//! Returns port of last received UDP packet
|
||||
int lastReadPort() const {return port_r;}
|
||||
|
||||
|
||||
//! Set parameters to "parameters_". You should to reopen %PIEthernet to apply them
|
||||
void setParameters(PIFlags<PIEthernet::Parameters> parameters_) {setProperty("parameters", (int)parameters_);}
|
||||
|
||||
//! Set parameter "parameter" to state "on". You should to reopen %PIEthernet to apply this
|
||||
void setParameter(PIEthernet::Parameters parameter, bool on = true);
|
||||
|
||||
//! Returns if parameter "parameter" is set
|
||||
bool isParameterSet(PIEthernet::Parameters parameter) const {return ((PIFlags<PIEthernet::Parameters>)(property("parameters").toInt()))[parameter];}
|
||||
|
||||
//! Returns parameters
|
||||
PIFlags<PIEthernet::Parameters> parameters() const {return (PIFlags<PIEthernet::Parameters>)(property("parameters").toInt());}
|
||||
|
||||
//PIByteArray macAddress() {if (!init_) init(); struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); memcpy(ifr.ifr_name, "eth0", 5); ioctl(sock, SIOCSIFHWADDR, &ifr); return PIByteArray(&ifr.ifr_hwaddr.sa_data, 6);}
|
||||
|
||||
//! Returns %PIEthernet type
|
||||
Type type() const {return (Type)(property("type").toInt());}
|
||||
|
||||
//! Returns read timeout
|
||||
double readTimeout() const {return property("readTimeout").toDouble();}
|
||||
|
||||
//! Returns write timeout
|
||||
double writeTimeout() const {return property("writeTimeout").toDouble();}
|
||||
|
||||
//! Set timeout for read
|
||||
void setReadTimeout(double ms) {setProperty("readTimeout", ms);}
|
||||
|
||||
//! Set timeout for write
|
||||
void setWriteTimeout(double ms) {setProperty("writeTimeout", ms);}
|
||||
|
||||
|
||||
//! Returns TTL (Time To Live)
|
||||
int TTL() const {return property("TTL").toInt();}
|
||||
|
||||
//! Returns multicast TTL (Time To Live)
|
||||
int multicastTTL() const {return property("MulticastTTL").toInt();}
|
||||
|
||||
//! Set TTL (Time To Live), default is 64
|
||||
void setTTL(int ttl) {setProperty("TTL", ttl);}
|
||||
|
||||
//! Set multicast TTL (Time To Live), default is 1
|
||||
void setMulticastTTL(int ttl) {setProperty("MulticastTTL", ttl);}
|
||||
|
||||
|
||||
//! Join to multicast group with address "group". Use only for UDP
|
||||
bool joinMulticastGroup(const PIString & group);
|
||||
|
||||
//! Leave multicast group with address "group". Use only for UDP
|
||||
bool leaveMulticastGroup(const PIString & group);
|
||||
|
||||
//! Returns joined multicast groups. Use only for UDP
|
||||
const PIStringList & multicastGroups() const {return mcast_groups;}
|
||||
|
||||
|
||||
//! Connect to TCP server with address \a readAddress(). Use only for TCP_Client
|
||||
bool connect();
|
||||
|
||||
//! Connect to TCP server with address "ip":"port". Use only for TCP_Client
|
||||
bool connect(const PIString & ip, int port) {setPath(ip + ":" + PIString::fromNumber(port)); return connect();}
|
||||
|
||||
//! Connect to TCP server with address "ip_port". Use only for TCP_Client
|
||||
bool connect(const PIString & ip_port) {setPath(ip_port); return connect();}
|
||||
|
||||
//! Returns if %PIEthernet connected to TCP server. Use only for TCP_Client
|
||||
bool isConnected() const {return connected_;}
|
||||
|
||||
|
||||
//! Start listen for incoming TCP connections on address \a readAddress(). Use only for TCP_Server
|
||||
bool listen(bool threaded = false);
|
||||
|
||||
//! Start listen for incoming TCP connections on address "ip":"port". Use only for TCP_Server
|
||||
bool listen(const PIString & ip, int port, bool threaded = false) {setReadAddress(ip, port); return listen(threaded);}
|
||||
|
||||
//! Start listen for incoming TCP connections on address "ip_port". Use only for TCP_Server
|
||||
bool listen(const PIString & ip_port, bool threaded = false) {setReadAddress(ip_port); return listen(threaded);}
|
||||
|
||||
PIEthernet * client(int index) {return clients_[index];}
|
||||
int clientsCount() const {return clients_.size_s();}
|
||||
PIVector<PIEthernet * > clients() const {return clients_;}
|
||||
|
||||
|
||||
//! Send data "data" with size "size" to address \a sendAddress() for UDP or \a readAddress() for TCP_Client
|
||||
bool send(const void * data, int size, bool threaded = false) {if (threaded) {writeThreaded(data, size); return true;} return (write(data, size) == size);}
|
||||
|
||||
//! Send data "data" with size "size" to address "ip":"port"
|
||||
bool send(const PIString & ip, int port, const void * data, int size, bool threaded = false) {ip_s = ip; port_s = port; if (threaded) {writeThreaded(data, size); return true;} return send(data, size);}
|
||||
|
||||
//! Send data "data" with size "size" to address "ip_port"
|
||||
bool send(const PIString & ip_port, const void * data, int size, bool threaded = false) {parseAddress(ip_port, &ip_s, &port_s); if (threaded) {writeThreaded(data, size); return true;} return send(data, size);}
|
||||
|
||||
//! Send data "data" to address \a sendAddress() for UDP or \a readAddress() for TCP_Client
|
||||
bool send(const PIByteArray & data, bool threaded = false) {if (threaded) {writeThreaded(data); return true;} return (write(data) == data.size_s());}
|
||||
|
||||
//! Send data "data" to address "ip":"port" for UDP
|
||||
bool send(const PIString & ip, int port, const PIByteArray & data, bool threaded = false) {ip_s = ip; port_s = port; if (threaded) {writeThreaded(data); return true;} return send(data);}
|
||||
|
||||
//! Send data "data" to address "ip_port" for UDP
|
||||
bool send(const PIString & ip_port, const PIByteArray & data, bool threaded = false) {parseAddress(ip_port, &ip_s, &port_s); if (threaded) {writeThreaded(data); return true;} return (write(data) == data.size_s());}
|
||||
|
||||
|
||||
//! Wait for some data and read it to "read_to"
|
||||
int read(void * read_to, int max_size);
|
||||
|
||||
//! Send data "read_to" with size "max_size" to address \a sendAddress() for UDP or \a readAddress() for TCP_Client
|
||||
int write(const void * data, int max_size);
|
||||
|
||||
//! Send data "data" to address \a sendAddress() for UDP or \a readAddress() for TCP_Client
|
||||
int write(const PIByteArray & data) {return write(data.data(), data.size_s());}
|
||||
|
||||
virtual bool canWrite() const {return mode() & WriteOnly;}
|
||||
|
||||
PIString constructFullPath() const;
|
||||
|
||||
EVENT1(newConnection, PIEthernet * , client)
|
||||
EVENT0(connected)
|
||||
EVENT1(disconnected, bool, withError)
|
||||
|
||||
|
||||
//! Flags of network interface
|
||||
enum PIP_EXPORT InterfaceFlag {
|
||||
ifActive /** Is active */ = 0x1,
|
||||
ifRunning /** Is running */ = 0x2,
|
||||
ifBroadcast /** Support broadcast */ = 0x4,
|
||||
ifMulticast /** Support multicast */ = 0x8,
|
||||
ifLoopback /** Is loopback */ = 0x10,
|
||||
ifPTP /** Is point-to-point */ = 0x20
|
||||
};
|
||||
|
||||
//! %PIFlags of network interface flags
|
||||
typedef PIFlags<InterfaceFlag> InterfaceFlags;
|
||||
|
||||
|
||||
//! Network interface descriptor
|
||||
struct PIP_EXPORT Interface {
|
||||
|
||||
//! System index
|
||||
int index;
|
||||
|
||||
//! MTU
|
||||
int mtu;
|
||||
|
||||
//! System name
|
||||
PIString name;
|
||||
|
||||
//! MAC address in format "hh:hh:hh:hh:hh:hh" or empty if there is no MAC address
|
||||
PIString mac;
|
||||
|
||||
//! IP address in format "i.i.i.i" or empty if there is no IP address
|
||||
PIString address;
|
||||
|
||||
//! Netmask of IP address in format "i.i.i.i" or empty if there is no netmask
|
||||
PIString netmask;
|
||||
|
||||
//! Broadcast address in format "i.i.i.i" or empty if there is no broadcast address
|
||||
PIString broadcast;
|
||||
|
||||
//! Point-to-point address or empty if there is no point-to-point address
|
||||
PIString ptp;
|
||||
|
||||
//! Flags of interface
|
||||
InterfaceFlags flags;
|
||||
|
||||
//! Returns if interface is active
|
||||
bool isActive() const {return flags[PIEthernet::ifActive];}
|
||||
|
||||
//! Returns if interface is running
|
||||
bool isRunning() const {return flags[PIEthernet::ifRunning];}
|
||||
|
||||
//! Returns if interface support broadcast
|
||||
bool isBroadcast() const {return flags[PIEthernet::ifBroadcast];}
|
||||
|
||||
//! Returns if interface support multicast
|
||||
bool isMulticast() const {return flags[PIEthernet::ifMulticast];}
|
||||
|
||||
//! Returns if interface is loopback
|
||||
bool isLoopback() const {return flags[PIEthernet::ifLoopback];}
|
||||
|
||||
//! Returns if interface is point-to-point
|
||||
bool isPTP() const {return flags[PIEthernet::ifPTP];}
|
||||
};
|
||||
|
||||
|
||||
//! Array of \a Interface with some features
|
||||
class PIP_EXPORT InterfaceList: public PIVector<PIEthernet::Interface> {
|
||||
public:
|
||||
InterfaceList(): PIVector<PIEthernet::Interface>() {}
|
||||
|
||||
//! Get interface with system index "index" or 0 if there is no one
|
||||
const Interface * getByIndex(int index) const {for (int i = 0; i < size_s(); ++i) if ((*this)[i].index == index) return &((*this)[i]); return 0;}
|
||||
|
||||
//! Get interface with system name "name" or 0 if there is no one
|
||||
const Interface * getByName(const PIString & name) const {for (int i = 0; i < size_s(); ++i) if ((*this)[i].name == name) return &((*this)[i]); return 0;}
|
||||
|
||||
//! Get interface with IP address "address" or 0 if there is no one
|
||||
const Interface * getByAddress(const PIString & address) const {for (int i = 0; i < size_s(); ++i) if ((*this)[i].address == address) return &((*this)[i]); return 0;}
|
||||
|
||||
//! Get loopback interface or 0 if there is no one
|
||||
const Interface * getLoopback() const {for (int i = 0; i < size_s(); ++i) if ((*this)[i].isLoopback()) return &((*this)[i]); return 0;}
|
||||
};
|
||||
|
||||
|
||||
//! Returns all system network interfaces
|
||||
static InterfaceList interfaces();
|
||||
|
||||
static PIString interfaceAddress(const PIString & interface_);
|
||||
|
||||
//! Returns all system network IP addresses
|
||||
static PIStringList allAddresses();
|
||||
|
||||
static void parseAddress(const PIString & ipp, PIString * ip, int * port);
|
||||
static PIString macFromBytes(const PIByteArray & mac) {PIString r; for (int i = 0; i < mac.size_s(); ++i) {r += PIString::fromNumber(mac[i], 16).expandLeftTo(2, '0'); if (i < mac.size_s() - 1) r += ":";} return r;}
|
||||
static PIByteArray macToBytes(const PIString & mac) {PIByteArray r; PIStringList sl = mac.split(":"); piForeachC (PIString & i, sl) r << uchar(i.toInt(16)); return r;}
|
||||
static PIString applyMask(const PIString & ip, const PIString & mask) {struct in_addr ia; ia.s_addr = inet_addr(ip.data()) & inet_addr(mask.data()); return PIString(inet_ntoa(ia));}
|
||||
static PIString getBroadcast(const PIString & ip, const PIString & mask) {struct in_addr ia; ia.s_addr = inet_addr(ip.data()) | ~inet_addr(mask.data()); return PIString(inet_ntoa(ia));}
|
||||
|
||||
//! \events
|
||||
//! \{
|
||||
|
||||
//! \fn void newConnection(PIEthernet * client)
|
||||
//! \brief Raise on new TCP connection received
|
||||
|
||||
//! \fn void connected()
|
||||
//! \brief Raise if succesfull TCP connection
|
||||
|
||||
//! \fn void disconnected(bool withError)
|
||||
//! \brief Raise if TCP connection was closed
|
||||
|
||||
//! \}
|
||||
//! \ioparams
|
||||
//! \{
|
||||
#ifdef DOXYGEN
|
||||
//! \brief read ip, default ""
|
||||
string ip;
|
||||
|
||||
//! \brief read port, default 0
|
||||
int port;
|
||||
|
||||
//! \brief ethernet parameters
|
||||
int parameters;
|
||||
|
||||
//! \brief read timeout, default 1000 ms
|
||||
double readTimeout;
|
||||
|
||||
//! \brief write timeout, default 1000 ms
|
||||
double writeTimeout;
|
||||
|
||||
//! \brief time-to-live, default 64
|
||||
int TTL;
|
||||
|
||||
//! \brief time-to-live for multicast, default 1
|
||||
int multicastTTL;
|
||||
#endif
|
||||
//! \}
|
||||
|
||||
protected:
|
||||
PIEthernet(int sock, PIString ip_port);
|
||||
|
||||
void propertyChanged(const PIString & name);
|
||||
|
||||
PIString fullPathPrefix() const {return "eth";}
|
||||
void configureFromFullPath(const PIString & full_path);
|
||||
bool configureDevice(const void * e_main, const void * e_parent = 0);
|
||||
|
||||
//! Executes when any read function was successful. Default implementation does nothing
|
||||
virtual void received(const void * data, int size) {;}
|
||||
|
||||
void construct();
|
||||
bool init();
|
||||
bool openDevice();
|
||||
bool closeDevice();
|
||||
void closeSocket(int & sd);
|
||||
void applyTimeouts();
|
||||
void applyTimeout(int fd, int opt, double ms);
|
||||
void applyOptInt(int level, int opt, int val);
|
||||
#ifndef WINDOWS
|
||||
static PIString getSockAddr(sockaddr * s) {return s == 0 ? PIString() : PIString(inet_ntoa(((sockaddr_in*)s)->sin_addr));}
|
||||
#endif
|
||||
|
||||
|
||||
int sock, sock_s, wrote;
|
||||
mutable int port_, port_s, port_c, port_r;
|
||||
bool connected_, connecting_, listen_threaded, server_bounded;
|
||||
sockaddr_in addr_, saddr_, raddr_;
|
||||
mutable PIString ip_, ip_s, ip_c, ip_r;
|
||||
PIThread server_thread_;
|
||||
PIVector<PIEthernet * > clients_;
|
||||
PIQueue<PIString> mcast_queue;
|
||||
PIStringList mcast_groups;
|
||||
|
||||
private:
|
||||
static void server_func(void * eth);
|
||||
void setType(Type t, bool reopen = true) {setProperty("type", (int)t); if (reopen && isOpened()) {closeDevice(); init(); openDevice();}}
|
||||
|
||||
inline static int ethErrorCore();
|
||||
inline static PIString ethErrorString();
|
||||
inline static int ethRecv(int sock, void * buf, int size, int flags = 0);
|
||||
inline static int ethRecvfrom(int sock, void * buf, int size, int flags, sockaddr * addr);
|
||||
inline static int ethSendto(int sock, const void * buf, int size, int flags, sockaddr * addr, int addr_len);
|
||||
inline static void ethClosesocket(int sock);
|
||||
inline static int ethSetsockopt(int sock, int level, int optname, const void * optval, int optlen);
|
||||
inline static int ethSetsockoptInt(int sock, int level, int optname, int value = 1);
|
||||
inline static int ethSetsockoptBool(int sock, int level, int optname, bool value = true);
|
||||
|
||||
};
|
||||
|
||||
inline bool operator <(const PIEthernet::Interface & v0, const PIEthernet::Interface & v1) {return (v0.name < v1.name);}
|
||||
|
||||
#endif // PIETHERNET_H
|
||||
317
src/io/pifile.cpp
Executable file
317
src/io/pifile.cpp
Executable file
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
File
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pifile.h"
|
||||
#include "pidir.h"
|
||||
#ifdef WINDOWS
|
||||
# undef S_IFDIR
|
||||
# undef S_IFREG
|
||||
# undef S_IFLNK
|
||||
# undef S_IFBLK
|
||||
# undef S_IFCHR
|
||||
# undef S_IFSOCK
|
||||
# define S_IFDIR 0x01
|
||||
# define S_IFREG 0x02
|
||||
# define S_IFLNK 0x04
|
||||
# define S_IFBLK 0x08
|
||||
# define S_IFCHR 0x10
|
||||
# define S_IFSOCK 0x20
|
||||
#endif
|
||||
#define S_IFHDN 0x40
|
||||
#ifdef QNX
|
||||
# define _stat_struct_ struct stat
|
||||
# define _stat_call_ stat
|
||||
# define _stat_link_ lstat
|
||||
#else
|
||||
# define _stat_struct_ struct stat64
|
||||
# define _stat_call_ stat64
|
||||
# define _stat_link_ lstat64
|
||||
#endif
|
||||
|
||||
|
||||
/*! \class PIFile
|
||||
* \brief Local file
|
||||
*
|
||||
* \section PIFile_sec0 Synopsis
|
||||
* This class provide access to local file. You can manipulate
|
||||
* binary content or use this class as text stream. To binary
|
||||
* access there are function \a read(), \a write(), and many
|
||||
* \a writeBinary() functions. For write variables to file in
|
||||
* their text representation threr are many "<<" operators.
|
||||
*
|
||||
* \section PIFile_sec1 Position
|
||||
* Each opened file has a read/write position - logical position
|
||||
* in the file content you read from or you write to. You can
|
||||
* find out current position with function \a pos(). Function
|
||||
* \a seek(llong position) move position to position "position",
|
||||
* \a seekToBegin() move position to the begin of file,
|
||||
* \a seekToEnd() move position to the end of file.
|
||||
*
|
||||
*/
|
||||
|
||||
REGISTER_DEVICE(PIFile);
|
||||
|
||||
|
||||
PIString PIFile::FileInfo::name() const {
|
||||
if (path.isEmpty()) return PIString();
|
||||
return path.mid(path.findLast(PIDir::separator) + 1);
|
||||
}
|
||||
|
||||
|
||||
PIString PIFile::FileInfo::baseName() const {
|
||||
if (path.isEmpty()) return PIString();
|
||||
PIString n = name(), e = extension();
|
||||
if (e.isEmpty()) return n;
|
||||
return n.cutRight(e.size_s() + 1);
|
||||
}
|
||||
|
||||
|
||||
PIString PIFile::FileInfo::extension() const {
|
||||
PIString n = name();
|
||||
if (n.isEmpty()) return PIString();
|
||||
while (n.startsWith("."))
|
||||
n.pop_front();
|
||||
if (n.isEmpty()) return PIString();
|
||||
int i = n.find(".");
|
||||
if (i < 0) return PIString();
|
||||
return n.mid(i + 1);
|
||||
}
|
||||
|
||||
|
||||
PIString PIFile::FileInfo::dir() const {
|
||||
if (path.isEmpty()) return PIString();
|
||||
PIString ret = path.mid(0, path.findLast(PIDir::separator));
|
||||
if (ret.isEmpty()) ret = PIDir::separator;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
PIFile::PIFile(): PIIODevice() {
|
||||
setPrecision(5);
|
||||
}
|
||||
|
||||
|
||||
PIFile::PIFile(const PIString & path, PIIODevice::DeviceMode mode): PIIODevice(path, mode) {
|
||||
setPrecision(5);
|
||||
if (!path.isEmpty())
|
||||
openDevice();
|
||||
}
|
||||
|
||||
|
||||
bool PIFile::openDevice() {
|
||||
close();
|
||||
PIString p = path();
|
||||
if (p.isEmpty()) return false;
|
||||
//piCout << "fopen " << path_.data() << ": " << strType(mode_).data() << fd;
|
||||
if ((mode_ & PIIODevice::WriteOnly) == PIIODevice::WriteOnly) {
|
||||
if (!isExists(p)) {
|
||||
FILE * fd = fopen(p.data(), "w");
|
||||
if (fd != 0) fclose(fd);
|
||||
}
|
||||
}
|
||||
fd = fopen(p.data(), strType(mode_).data());
|
||||
opened_ = (fd != 0);
|
||||
#ifndef WINDOWS
|
||||
if (opened_) fcntl(fileno(fd), F_SETFL, O_NONBLOCK);
|
||||
#endif
|
||||
seekToBegin();
|
||||
//piCout << "open file" << fd << opened_;
|
||||
return opened_;
|
||||
}
|
||||
|
||||
|
||||
bool PIFile::closeDevice() {
|
||||
//piCout << "close file" << fd << opened_;
|
||||
if (!opened_ || fd == 0) return true;
|
||||
bool cs = (fclose(fd) == 0);
|
||||
if (cs) fd = 0;
|
||||
//piCout << "closed file" << fd << opened_;
|
||||
return cs;
|
||||
}
|
||||
|
||||
|
||||
PIString PIFile::readLine() {
|
||||
PIString str;
|
||||
if (!opened_) return str;
|
||||
int cc, cp = 0;
|
||||
while (!isEnd() && cp < 4095) {
|
||||
cc = fgetc(fd);
|
||||
if (char(cc) == '\n' || cc == EOF) break;
|
||||
str.push_back(char(cc));
|
||||
}
|
||||
//cout << "readline: " << str << endl;
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
llong PIFile::readAll(void * data) {
|
||||
llong cp = pos(), s = size(), i = -1;
|
||||
seekToBegin();
|
||||
if (s < 0) {
|
||||
while (!isEnd())
|
||||
read(&(((char*)data)[++i]), 1);
|
||||
} else
|
||||
read((char * )data, s);
|
||||
seek(cp);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
PIByteArray PIFile::readAll(bool forceRead) {
|
||||
PIByteArray a;
|
||||
llong cp = pos();
|
||||
if (forceRead) {
|
||||
seekToBegin();
|
||||
while (!isEnd())
|
||||
a.push_back(readChar());
|
||||
seek(cp);
|
||||
return a;
|
||||
}
|
||||
llong s = size();
|
||||
if (s < 0) return a;
|
||||
a.resize(s);
|
||||
s = readAll(a.data());
|
||||
seek(cp);
|
||||
if (s >= 0) a.resize(s);
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
llong PIFile::size() const {
|
||||
if (!opened_) return -1;
|
||||
llong s, cp = pos();
|
||||
fseek(fd, 0, SEEK_END); clearerr(fd);
|
||||
s = pos();
|
||||
fseek(fd, cp, SEEK_SET); clearerr(fd);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
void PIFile::resize(llong new_size, uchar fill_) {
|
||||
llong ds = new_size - size();
|
||||
if (ds == 0) return;
|
||||
if (ds > 0) {
|
||||
uchar * buff = new uchar[ds];
|
||||
memset(buff, fill_, ds);
|
||||
write(buff, ds);
|
||||
delete[] buff;
|
||||
return;
|
||||
}
|
||||
if (new_size == 0) {
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
piCoutObj << "Downsize is not supported yet :-(";
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool PIFile::isExists(const PIString & path) {
|
||||
FILE * f = fopen(PIString(path).data(), "r");
|
||||
bool ok = (f != 0);
|
||||
if (ok) fclose(f);
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
PIString PIFile::constructFullPath() const {
|
||||
return fullPathPrefix() + "://" + path();
|
||||
}
|
||||
|
||||
|
||||
void PIFile::configureFromFullPath(const PIString & full_path) {
|
||||
setPath(full_path);
|
||||
}
|
||||
|
||||
|
||||
PIString PIFile::strType(const PIIODevice::DeviceMode type) {
|
||||
switch (type) {
|
||||
case PIIODevice::ReadOnly: return "rb";
|
||||
case PIIODevice::ReadWrite:
|
||||
case PIIODevice::WriteOnly: return "r+b";
|
||||
}
|
||||
return "rb";
|
||||
}
|
||||
|
||||
|
||||
PIFile::FileInfo PIFile::fileInfo(const PIString & path) {
|
||||
FileInfo ret;
|
||||
if (path.isEmpty()) return ret;
|
||||
ret.path = path;
|
||||
PIString n = ret.name();
|
||||
//piCout << "open" << path;
|
||||
#ifdef WINDOWS
|
||||
DWORD attr = GetFileAttributes((LPCTSTR)(path.data()));
|
||||
if (attr == 0xFFFFFFFF) return ret;
|
||||
HANDLE hFile = 0;
|
||||
if ((attr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) {
|
||||
hFile = CreateFile(path.data(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
|
||||
} else {
|
||||
hFile = CreateFile(path.data(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
|
||||
}
|
||||
if (!hFile) return ret;
|
||||
BY_HANDLE_FILE_INFORMATION fi;
|
||||
memset(&fi, 0, sizeof(fi));
|
||||
if (GetFileInformationByHandle(hFile, &fi) != 0) {
|
||||
LARGE_INTEGER filesize;
|
||||
filesize.LowPart = filesize.HighPart = 0;
|
||||
if (fi.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ret.flags |= FileInfo::Hidden;
|
||||
if (fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ret.flags |= FileInfo::Dir;
|
||||
else {
|
||||
ret.flags |= FileInfo::File;
|
||||
filesize.LowPart = fi.nFileSizeLow;
|
||||
filesize.HighPart = fi.nFileSizeHigh;
|
||||
}
|
||||
PIString ext = ret.extension();
|
||||
ret.perm_user = FileInfo::Permissions(true, (attr & FILE_ATTRIBUTE_READONLY) != FILE_ATTRIBUTE_READONLY, ext == "bat" || ext == "exe");
|
||||
ret.perm_group = ret.perm_other = ret.perm_user;
|
||||
ret.size = filesize.QuadPart;
|
||||
ret.time_access = PIDateTime(fi.ftLastAccessTime);
|
||||
ret.time_modification = PIDateTime(fi.ftLastWriteTime);
|
||||
}
|
||||
CloseHandle(hFile);
|
||||
#else
|
||||
_stat_struct_ fs;
|
||||
memset(&fs, 0, sizeof(fs));
|
||||
_stat_call_(path.data(), &fs);
|
||||
int mode = fs.st_mode;
|
||||
ret.size = fs.st_size;
|
||||
ret.id_user = fs.st_uid;
|
||||
ret.id_group = fs.st_gid;
|
||||
ret.time_access = PIDateTime::fromSystemTime(PISystemTime(fs.st_atim.tv_sec, fs.st_atim.tv_nsec));
|
||||
ret.time_modification = PIDateTime::fromSystemTime(PISystemTime(fs.st_mtim.tv_sec, fs.st_mtim.tv_nsec));
|
||||
ret.perm_user = FileInfo::Permissions((mode & S_IRUSR) == S_IRUSR, (mode & S_IWUSR) == S_IWUSR, (mode & S_IXUSR) == S_IXUSR);
|
||||
ret.perm_group = FileInfo::Permissions((mode & S_IRGRP) == S_IRGRP, (mode & S_IWGRP) == S_IWGRP, (mode & S_IXGRP) == S_IXGRP);
|
||||
ret.perm_other = FileInfo::Permissions((mode & S_IROTH) == S_IROTH, (mode & S_IWOTH) == S_IWOTH, (mode & S_IXOTH) == S_IXOTH);
|
||||
memset(&fs, 0, sizeof(fs));
|
||||
_stat_link_(path.data(), &fs);
|
||||
mode &= ~S_IFLNK;
|
||||
mode |= S_IFLNK & fs.st_mode;
|
||||
if (n.startsWith(".")) mode |= S_IFHDN;
|
||||
if ((mode & S_IFDIR) == S_IFDIR) ret.flags |= FileInfo::Dir;
|
||||
if ((mode & S_IFREG) == S_IFREG) ret.flags |= FileInfo::File;
|
||||
if ((mode & S_IFLNK) == S_IFLNK) ret.flags |= FileInfo::SymbolicLink;
|
||||
if ((mode & S_IFHDN) == S_IFHDN) ret.flags |= FileInfo::Hidden;
|
||||
#endif
|
||||
if (n == ".") ret.flags = FileInfo::Dir | FileInfo::Dot;
|
||||
if (n == "..") ret.flags = FileInfo::Dir | FileInfo::DotDot;
|
||||
return ret;
|
||||
}
|
||||
309
src/io/pifile.h
Executable file
309
src/io/pifile.h
Executable file
@@ -0,0 +1,309 @@
|
||||
/*! \file pifile.h
|
||||
* \brief Local file
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
File
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIFILE_H
|
||||
#define PIFILE_H
|
||||
|
||||
#include "piiodevice.h"
|
||||
#include <cstdio>
|
||||
|
||||
class PIP_EXPORT PIFile: public PIIODevice
|
||||
{
|
||||
PIIODEVICE(PIFile)
|
||||
public:
|
||||
|
||||
//! Constructs an empty file
|
||||
PIFile();
|
||||
|
||||
struct FileInfo {
|
||||
FileInfo() {size = 0; id_group = id_user = 0;}
|
||||
|
||||
enum Flag {
|
||||
File = 0x01,
|
||||
Dir = 0x02,
|
||||
Dot = 0x04,
|
||||
DotDot = 0x08,
|
||||
SymbolicLink = 0x10,
|
||||
Hidden = 0x20
|
||||
};
|
||||
typedef PIFlags<FileInfo::Flag> Flags;
|
||||
struct Permissions {
|
||||
Permissions(uchar r = 0): raw(r) {}
|
||||
Permissions(bool r, bool w, bool e): raw(0) {read = w; write = w; exec = e;}
|
||||
PIString toString() const {return PIString(read ? "r" : "-") + PIString(write ? "w" : "-") + PIString(exec ? "x" : "-");}
|
||||
union {
|
||||
uchar raw;
|
||||
struct {
|
||||
uchar read : 1;
|
||||
uchar write: 1;
|
||||
uchar exec : 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
PIString path;
|
||||
llong size;
|
||||
PIDateTime time_access;
|
||||
PIDateTime time_modification;
|
||||
Flags flags;
|
||||
uint id_user;
|
||||
uint id_group;
|
||||
Permissions perm_user;
|
||||
Permissions perm_group;
|
||||
Permissions perm_other;
|
||||
|
||||
PIString name() const;
|
||||
PIString baseName() const;
|
||||
PIString extension() const;
|
||||
PIString dir() const;
|
||||
bool isDir() const {return flags[Dir];}
|
||||
bool isFile() const {return flags[File];}
|
||||
bool isSymbolicLink() const {return flags[SymbolicLink];}
|
||||
bool isHidden() const {return flags[Hidden];}
|
||||
};
|
||||
|
||||
//! Constructs a file with path "path" nad open mode "type"
|
||||
PIFile(const PIString & path, DeviceMode mode = ReadWrite);
|
||||
|
||||
~PIFile() {close();}
|
||||
|
||||
//PIFile & operator =(const PIFile & f) {path_ = f.path_; type_ = f.type_; return *this;}
|
||||
|
||||
|
||||
//! Immediate write all buffered data to disk
|
||||
void flush() {if (opened_) fflush(fd);}
|
||||
|
||||
//! Move read/write position to "position"
|
||||
void seek(llong position) {if (!opened_) return; fseek(fd, position, SEEK_SET); clearerr(fd);}
|
||||
|
||||
//! Move read/write position to the begin of the file
|
||||
void seekToBegin() {if (!opened_) return; fseek(fd, 0, SEEK_SET); clearerr(fd);}
|
||||
|
||||
//! Move read/write position to the end of the file
|
||||
void seekToEnd() {if (!opened_) return; fseek(fd, 0, SEEK_END); clearerr(fd);}
|
||||
|
||||
//! Move read/write position to text line number "line"
|
||||
void seekToLine(llong line) {if (!opened_) return; seekToBegin(); piForTimes (line) readLine(); clearerr(fd);} // line 0 - begin of file
|
||||
//void fill(char c) {stream.fill(c);}
|
||||
|
||||
//! Read one char and return it
|
||||
char readChar() {return (char)fgetc(fd);}
|
||||
|
||||
//! Read one text line and return it
|
||||
PIString readLine();
|
||||
|
||||
//! Read all file content to "data" and return readed bytes count. Position leaved unchanged
|
||||
llong readAll(void * data);
|
||||
|
||||
//! Read all file content to byte array and return it. Position leaved unchanged
|
||||
PIByteArray readAll(bool forceRead = false);
|
||||
|
||||
|
||||
//! Set file path to "path" and reopen file if need
|
||||
void setPath(const PIString & path) {PIIODevice::setPath(path); if (opened_) openDevice();}
|
||||
|
||||
//! Returns file size
|
||||
llong size() const;
|
||||
|
||||
//! Returns read/write position
|
||||
llong pos() const {if (!opened_) return -1; return ftell(fd);}
|
||||
|
||||
//! Returns if position is at the end of file
|
||||
bool isEnd() const {if (!opened_) return true; return (feof(fd) || ferror(fd));}
|
||||
|
||||
//! Returns if file is empty
|
||||
bool isEmpty() const {return (size() <= 0);}
|
||||
|
||||
//! Returns FileInfo of current file
|
||||
FileInfo fileInfo() const {return fileInfo(path());}
|
||||
|
||||
|
||||
//! Returns float numbers write precision
|
||||
int precision() const {return prec_;}
|
||||
|
||||
//! Set float numbers write precision to "prec_" digits
|
||||
void setPrecision(int prec) {prec_ = prec; if (prec_ >= 0) prec_str = "." + itos(prec_); else prec_str = "";}
|
||||
|
||||
|
||||
//! Read from file to "read_to" no more than "max_size" and return readed bytes count
|
||||
int read(void * read_to, int max_size) {if (!canRead() || fd == 0) return -1; return fread(read_to, 1, max_size, fd);}
|
||||
|
||||
//! Write to file "data" with size "max_size" and return written bytes count
|
||||
int write(const void * data, int max_size) {if (!canWrite() || fd == 0) return -1; return fwrite(data, 1, max_size, fd);}
|
||||
|
||||
PIFile & writeToBinLog(ushort id, const void * data, int size) {if (!isWriteable() || fd == 0) return *this; writeBinary(id).writeBinary((ushort)size); write(data, size); flush(); return *this;}
|
||||
|
||||
|
||||
//! Write to file binary content of "v"
|
||||
PIFile & writeBinary(const char v) {write(&v, sizeof(v)); return *this;}
|
||||
//! Write to file binary content of "v"
|
||||
PIFile & writeBinary(const short v) {write(&v, sizeof(v)); return *this;}
|
||||
//! Write to file binary content of "v"
|
||||
PIFile & writeBinary(const int v) {write(&v, sizeof(v)); return *this;}
|
||||
//! Write to file binary content of "v"
|
||||
PIFile & writeBinary(const long v) {write(&v, sizeof(v)); return *this;}
|
||||
//! Write to file binary content of "v"
|
||||
PIFile & writeBinary(const llong v) {write(&v, sizeof(v)); return *this;}
|
||||
//! Write to file binary content of "v"
|
||||
PIFile & writeBinary(const uchar v) {write(&v, sizeof(v)); return *this;}
|
||||
//! Write to file binary content of "v"
|
||||
PIFile & writeBinary(const ushort v) {write(&v, sizeof(v)); return *this;}
|
||||
//! Write to file binary content of "v"
|
||||
PIFile & writeBinary(const uint v) {write(&v, sizeof(v)); return *this;}
|
||||
//! Write to file binary content of "v"
|
||||
PIFile & writeBinary(const ulong v) {write(&v, sizeof(v)); return *this;}
|
||||
//! Write to file binary content of "v"
|
||||
PIFile & writeBinary(const ullong v) {write(&v, sizeof(v)); return *this;}
|
||||
//! Write to file binary content of "v"
|
||||
PIFile & writeBinary(const float v) {write(&v, sizeof(v)); return *this;}
|
||||
//! Write to file binary content of "v"
|
||||
PIFile & writeBinary(const double v) {write(&v, sizeof(v)); return *this;}
|
||||
|
||||
PIFile & operator =(const PIFile & f) {PIIODevice::setPath(f.path()); mode_ = f.mode_; return *this;}
|
||||
|
||||
//! Write to file text representation of "v"
|
||||
PIFile & operator <<(const char v) {if (canWrite() && fd != 0) write(&v, 1); return *this;}
|
||||
//PIFile & operator <<(const string & v) {write(v.c_str(), v.size()); return *this;}
|
||||
//! Write to file string "v"
|
||||
PIFile & operator <<(const PIString & v) {if (canWrite() && fd != 0) write(v.data(), v.lengthAscii()); return *this;}
|
||||
//! Write to file text representation of "v"
|
||||
PIFile & operator <<(const PIByteArray & v) {if (canWrite() && fd != 0) write(v.data(), v.size()); return *this;}
|
||||
//! Write to file text representation of "v"
|
||||
PIFile & operator <<(short v) {if (canWrite() && fd != 0) ret = fprintf(fd, "%hd", v); return *this;}
|
||||
//! Write to file text representation of "v"
|
||||
PIFile & operator <<(int v) {if (canWrite() && fd != 0) ret = fprintf(fd, "%d", v); return *this;}
|
||||
//! Write to file text representation of "v"
|
||||
PIFile & operator <<(long v) {if (canWrite() && fd != 0) ret = fprintf(fd, "%ld", v); return *this;}
|
||||
//! Write to file text representation of "v"
|
||||
PIFile & operator <<(llong v) {if (canWrite() && fd != 0) ret = fprintf(fd, "%lld", v); return *this;}
|
||||
//! Write to file text representation of "v"
|
||||
PIFile & operator <<(uchar v) {if (canWrite() && fd != 0) ret = fprintf(fd, "%u", int(v)); return *this;}
|
||||
//! Write to file text representation of "v"
|
||||
PIFile & operator <<(ushort v) {if (canWrite() && fd != 0) ret = fprintf(fd, "%hu", v); return *this;}
|
||||
//! Write to file text representation of "v"
|
||||
PIFile & operator <<(uint v) {if (canWrite() && fd != 0) ret = fprintf(fd, "%u", v); return *this;}
|
||||
//! Write to file text representation of "v"
|
||||
PIFile & operator <<(ulong v) {if (canWrite() && fd != 0) ret = fprintf(fd, "%lu", v); return *this;}
|
||||
//! Write to file text representation of "v"
|
||||
PIFile & operator <<(ullong v) {if (canWrite() && fd != 0) ret = fprintf(fd, "%llu", v); return *this;}
|
||||
//! Write to file text representation of "v" with precision \a precision()
|
||||
PIFile & operator <<(float v) {if (canWrite() && fd != 0) ret = fprintf(fd, ("%" + prec_str + "f").c_str(), v); return *this;}
|
||||
//! Write to file text representation of "v" with precision \a precision()
|
||||
PIFile & operator <<(double v) {if (canWrite() && fd != 0) ret = fprintf(fd, ("%" + prec_str + "lf").c_str(), v); return *this;}
|
||||
|
||||
|
||||
//! Read from file text representation of "v"
|
||||
PIFile & operator >>(char & v) {if (canRead() && fd != 0) ret = fscanf(fd, "%hhn", &v); return *this;}
|
||||
//! Read from file text representation of "v"
|
||||
PIFile & operator >>(short & v) {if (canRead() && fd != 0) ret = fscanf(fd, "%hn", &v); return *this;}
|
||||
//! Read from file text representation of "v"
|
||||
PIFile & operator >>(int & v) {if (canRead() && fd != 0) ret = fscanf(fd, "%n", &v); return *this;}
|
||||
//! Read from file text representation of "v"
|
||||
PIFile & operator >>(long & v) {if (canRead() && fd != 0) ret = fscanf(fd, "%ln", &v); return *this;}
|
||||
//! Read from file text representation of "v"
|
||||
PIFile & operator >>(llong & v) {if (canRead() && fd != 0) ret = fscanf(fd, "%lln", &v); return *this;}
|
||||
//! Read from file text representation of "v"
|
||||
PIFile & operator >>(uchar & v) {if (canRead() && fd != 0) ret = fscanf(fd, "%hhn", &v); return *this;}
|
||||
//! Read from file text representation of "v"
|
||||
PIFile & operator >>(ushort & v) {if (canRead() && fd != 0) ret = fscanf(fd, "%hn", &v); return *this;}
|
||||
//! Read from file text representation of "v"
|
||||
PIFile & operator >>(uint & v) {if (canRead() && fd != 0) ret = fscanf(fd, "%n", &v); return *this;}
|
||||
//! Read from file text representation of "v"
|
||||
PIFile & operator >>(ulong & v) {if (canRead() && fd != 0) ret = fscanf(fd, "%ln", &v); return *this;}
|
||||
//! Read from file text representation of "v"
|
||||
PIFile & operator >>(ullong & v) {if (canRead() && fd != 0) ret = fscanf(fd, "%lln", &v); return *this;}
|
||||
//! Read from file text representation of "v"
|
||||
PIFile & operator >>(float & v) {if (canRead() && fd != 0) ret = fscanf(fd, "%f", &v); return *this;}
|
||||
//! Read from file text representation of "v"
|
||||
PIFile & operator >>(double & v) {if (canRead() && fd != 0) ret = fscanf(fd, "%lf", &v); return *this;}
|
||||
|
||||
PIString constructFullPath() const;
|
||||
|
||||
EVENT_HANDLER(void, clear) {close(); fd = fopen(path().data(), "w"); if (fd != 0) fclose(fd); fd = 0; opened_ = false; open();}
|
||||
EVENT_HANDLER0(void, remove) {close(); std::remove(path().data());}
|
||||
EVENT_HANDLER1(void, resize, llong, new_size) {resize(new_size, 0);}
|
||||
EVENT_HANDLER2(void, resize, llong, new_size, uchar, fill);
|
||||
|
||||
|
||||
//! Returns not opened temporary file with open mode "mode"
|
||||
static PIFile openTemporary(PIIODevice::DeviceMode mode = PIIODevice::ReadWrite) {return PIFile(PIString(tmpnam(0)), mode);}
|
||||
|
||||
//! Returns if file with path "path" does exists
|
||||
static bool isExists(const PIString & path);
|
||||
|
||||
//! Remove file with path "path" and returns if remove was successful
|
||||
static bool remove(const PIString & path) {return std::remove(path.data()) == 0;}
|
||||
|
||||
//! Rename file with path "from" to path "to" and returns if rename was successful
|
||||
static bool rename(const PIString & from, const PIString & to) {return ::rename(from.data(), to.data()) == 0;}
|
||||
|
||||
//! Returns FileInfo of file or dir with path "path"
|
||||
static FileInfo fileInfo(const PIString & path);
|
||||
|
||||
//! \handlers
|
||||
//! \{
|
||||
|
||||
//! \fn void clear()
|
||||
//! \brief Clear content of file
|
||||
|
||||
//! \fn void resize(llong new_size)
|
||||
//! \brief Resize file to "new_size" with "fill" filling
|
||||
|
||||
//! \fn void resize(llong new_size, uchar fill)
|
||||
//! \brief Resize file to "new_size" with "fill" filling
|
||||
|
||||
//! \fn void remove()
|
||||
//! \brief Remove file
|
||||
|
||||
//! \}
|
||||
//! \ioparams
|
||||
//! \{
|
||||
#ifdef DOXYGEN
|
||||
#endif
|
||||
//! \}
|
||||
|
||||
protected:
|
||||
PIString fullPathPrefix() const {return "file";}
|
||||
void configureFromFullPath(const PIString & full_path);
|
||||
bool openDevice();
|
||||
bool closeDevice();
|
||||
|
||||
private:
|
||||
PIString strType(const PIIODevice::DeviceMode type);
|
||||
|
||||
FILE * fd;
|
||||
int ret, prec_;
|
||||
string prec_str;
|
||||
|
||||
};
|
||||
|
||||
inline PICout operator <<(PICout s, const PIFile::FileInfo & v) {
|
||||
s.setControl(0, true);
|
||||
s << "FileInfo(\"" << v.path << "\", " << PIString::readableSize(v.size) << ", "
|
||||
<< v.perm_user.toString() << " " << v.perm_group.toString() << " " << v.perm_other.toString() << ", "
|
||||
<< v.time_access.toString() << ", " << v.time_modification.toString()
|
||||
<< ", 0x" << Hex << v.flags << ")";
|
||||
s.restoreControl();
|
||||
return s;
|
||||
}
|
||||
|
||||
#endif // PIFILE_H
|
||||
470
src/io/pifiletransfer.cpp
Normal file
470
src/io/pifiletransfer.cpp
Normal file
@@ -0,0 +1,470 @@
|
||||
#include "pifiletransfer.h"
|
||||
|
||||
const char PIFileTransfer::sign[] = {'P', 'F', 'T'};
|
||||
|
||||
PIFileTransfer::PIFileTransfer(): crc(standardCRC_16()) {
|
||||
for (uint i = 0; i < sizeof(sign); i++)
|
||||
header.sig[i] = sign[i];
|
||||
header.version = PIFILETRANSFER_VERSION;
|
||||
header.session_id = 0;
|
||||
dir = PIDir::current();
|
||||
fileinfo_size = sizeof(EntryInfo) + sizeof(PIByteArray) + 100;
|
||||
min_packet_size = sizeof(PacketHeader) + sizeof(uint) + fileinfo_size;
|
||||
is_sending = is_receiving = false;
|
||||
bytes_file_all = bytes_file_cur = bytes_total_all = bytes_total_cur = 0;
|
||||
timeout_ = 1.;
|
||||
setPacketSize(4096);
|
||||
srand(PISystemTime::current().toMilliseconds());
|
||||
}
|
||||
|
||||
|
||||
PIFileTransfer::~PIFileTransfer() {
|
||||
break_ = true;
|
||||
session.clear();
|
||||
replies.clear();
|
||||
}
|
||||
|
||||
|
||||
bool PIFileTransfer::send(const PIString & file) {
|
||||
return sendFiles(PIVector<PIFile::FileInfo>() << PIFile::fileInfo(file));
|
||||
}
|
||||
|
||||
|
||||
bool PIFileTransfer::send(PIVector<PIFile::FileInfo> entries) {
|
||||
PIVector<PIFile::FileInfo> allEntries;
|
||||
for (int i = 0; i < entries.size_s(); i++) {
|
||||
allEntries << entries[i];
|
||||
if (entries[i].isDir())
|
||||
allEntries << PIDir::allEntries(dir.absolutePath() + dir.separator + entries[i].path);
|
||||
}
|
||||
return sendFiles(allEntries);
|
||||
}
|
||||
|
||||
|
||||
void PIFileTransfer::stopSend() {
|
||||
if (!is_sending) return;
|
||||
break_ = true;
|
||||
}
|
||||
|
||||
|
||||
void PIFileTransfer::stopReceive() {
|
||||
if (!is_receiving) return;
|
||||
break_ = true;
|
||||
finish_receive(false);
|
||||
}
|
||||
|
||||
|
||||
void PIFileTransfer::received(PIByteArray & ba) {
|
||||
if (ba.size() < sizeof(PacketHeader)) return;
|
||||
PacketHeader h;
|
||||
memcpy(&h, ba.data(), sizeof(PacketHeader));
|
||||
PacketType pt = (PacketType)h.type;
|
||||
// piCoutObj << "receive" << h.session_id << h.type << h.id;
|
||||
switch (pt) {
|
||||
case pt_Data:
|
||||
if (h.session_id != header.session_id || !is_receiving) {
|
||||
sendBreak(h.session_id);
|
||||
return;
|
||||
} else {
|
||||
uint rcrc = *(uint *)(ba.data(ba.size_s() - sizeof(uint)));
|
||||
uint ccrc = crc.calculate(ba.data(), ba.size_s() - sizeof(uint));
|
||||
if (rcrc != ccrc) {
|
||||
header.id = h.id;
|
||||
sendReply(Invalid);
|
||||
} else {
|
||||
ba >> h;
|
||||
ba.resize(ba.size_s() - sizeof(uint));
|
||||
processData(h.id, ba);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case pt_Reply:
|
||||
if (h.session_id != header.session_id) return;
|
||||
ba >> h;
|
||||
if (ba.size() == sizeof(int)) {
|
||||
int rci;
|
||||
ba >> rci;
|
||||
ReplyCode rc = (ReplyCode)rci;
|
||||
// piCoutObj << "reply received" << rci;
|
||||
if (rc == Break) {
|
||||
break_ = true;
|
||||
if (is_receiving) {
|
||||
stopReceive();
|
||||
return;
|
||||
}
|
||||
if (is_sending) {
|
||||
stopSend();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (is_sending) {
|
||||
if (h.id >= 0 && h.id < replies.size())
|
||||
replies[h.id] = rc;
|
||||
}
|
||||
if (is_receiving && h.id == 0) {
|
||||
if (checkSession() == 0) finish_receive(true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case pt_SendRequest:
|
||||
if (is_sending) {
|
||||
sendBreak(h.session_id);
|
||||
return;
|
||||
}
|
||||
if (header.session_id != h.session_id && is_receiving) {
|
||||
sendBreak(h.session_id);
|
||||
return;
|
||||
}
|
||||
ba >> h;
|
||||
if (ba.size() != sizeof(uint) + sizeof(llong)) return;
|
||||
uint pcts;
|
||||
ba >> pcts;
|
||||
bytes_file_all = pcts;
|
||||
ba >> bytes_total_all;
|
||||
header.session_id = h.session_id;
|
||||
header.id = 0;
|
||||
bool ok = true;
|
||||
state_string = "receive request";
|
||||
receiveRequest(&ok);
|
||||
if (ok) {
|
||||
session.clear();
|
||||
replies.clear();
|
||||
session.resize(pcts);
|
||||
replies.resize(pcts + 1);
|
||||
replies.fill(Unknown);
|
||||
is_receiving = true;
|
||||
state_string = "receiving";
|
||||
replies[0] = Success;
|
||||
sendReply(Success);
|
||||
} else {
|
||||
sendReply(Invalid);
|
||||
state_string = "wait user permit";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool PIFileTransfer::sendFiles(PIVector<PIFile::FileInfo> files) {
|
||||
PIStringList names;
|
||||
for(int i=0; i<files.size_s(); i++) {
|
||||
files[i].path = dir.relative(files[i].path);
|
||||
if (names.contains(files[i].path)) {files.remove(i); i--;}
|
||||
else names << files[i].path;
|
||||
}
|
||||
// piCoutObj << "prepare to send" << files.size() << "files";
|
||||
break_ = false;
|
||||
is_sending = true;
|
||||
buildSession(files);
|
||||
replies.resize(session.size() + 1);
|
||||
replies.fill(Unknown);
|
||||
work_file.setPath("");
|
||||
startSend();
|
||||
bytes_file_all = bytes_file_cur = 0;
|
||||
PIByteArray ba;
|
||||
if (!getSendRequest()) return finish_send(false);
|
||||
for (int i = 0; i < session.size_s(); i++) {
|
||||
ba = buildPacket(i);
|
||||
sendRequest(ba);
|
||||
if (break_) return finish_send(false);
|
||||
}
|
||||
// piCoutObj << "correct errors";
|
||||
PITimeMeasurer tm;
|
||||
int prev_chk = 0;
|
||||
while (tm.elapsed_s() < timeout_) {
|
||||
int chk = checkSession();
|
||||
if (chk != prev_chk) tm.reset();
|
||||
if (chk == 0) return finish_send(true);
|
||||
if (chk > 0) {
|
||||
ba = buildPacket(chk - 1);
|
||||
sendRequest(ba);
|
||||
}
|
||||
// if (chk == -1) return finish_send(false);
|
||||
if (break_) return finish_send(false);
|
||||
prev_chk = chk;
|
||||
piMSleep(1);
|
||||
}
|
||||
return finish_send(false);;
|
||||
}
|
||||
|
||||
|
||||
int PIFileTransfer::checkSession() {
|
||||
int miss = 0;
|
||||
for (int i = 1; i < replies.size_s(); i++) {
|
||||
if ((ReplyCode)replies[i] != Success) miss++;
|
||||
if ((ReplyCode)replies[i] == Invalid) return i;
|
||||
}
|
||||
for (int i = 1; i < replies.size_s(); i++) {
|
||||
if ((ReplyCode)replies[i] == Unknown) return i;
|
||||
}
|
||||
if (miss > 0) {
|
||||
piCoutObj << "missing" << miss << "packets";
|
||||
return -miss;
|
||||
} else return 0;
|
||||
}
|
||||
|
||||
|
||||
void PIFileTransfer::buildSession(PIVector<PIFile::FileInfo> files) {
|
||||
state_string = "calculating files...";
|
||||
session.clear();
|
||||
header.session_id = rand();
|
||||
bytes_file_cur = 0;
|
||||
bytes_file_all = files.size();
|
||||
bytes_total_all = bytes_total_cur = 0;
|
||||
// PIMap<llong, int> sizes;
|
||||
// for (int i=0; i<files.size(); i++) {
|
||||
// sizes[files[i].size] = i;
|
||||
// }
|
||||
// PIVector<int> indexes = sizes.values();
|
||||
EntryInfo fi;
|
||||
PIVector<EntryInfo> lfi;
|
||||
int cur_size = min_packet_size;
|
||||
for (int i = 0; i < files.size_s(); i++) {
|
||||
bytes_file_cur = i;
|
||||
fi.entry = files[i];
|
||||
bytes_total_all += fi.entry.size;
|
||||
// fi.size = fi.entry.size;
|
||||
fi.fstart = 0;
|
||||
fi.part_index = 1;
|
||||
int rest = fi.entry.size - (max_packet_size - cur_size);
|
||||
// piCout << i << fi.entry << rest;
|
||||
if (rest <= 0) {
|
||||
fi.parts = 1;
|
||||
fi.fsize = fi.entry.size;
|
||||
lfi << fi;
|
||||
cur_size += fi.fsize + fileinfo_size;
|
||||
} else {
|
||||
fi.fsize = fi.entry.size - rest;
|
||||
fi.parts = 1 + 1 + piMaxi(1, rest / (max_packet_size - min_packet_size));
|
||||
// piCout << fi.parts;
|
||||
lfi << fi;
|
||||
session << lfi;
|
||||
lfi.clear();
|
||||
cur_size = min_packet_size;
|
||||
llong fs = fi.fsize;
|
||||
for (uint j = 1; j < fi.parts; j++) {
|
||||
fi.part_index++;
|
||||
fi.fstart = fs;
|
||||
fi.fsize = piMinll(fi.entry.size - fs, max_packet_size - min_packet_size);
|
||||
lfi << fi;
|
||||
cur_size += fi.fsize + sizeof(PIByteArray);
|
||||
if (fi.part_index != fi.parts) {
|
||||
session << lfi;
|
||||
lfi.clear();
|
||||
cur_size = min_packet_size;
|
||||
fs += fi.fsize;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (max_packet_size - cur_size < min_packet_size) {
|
||||
session << lfi;
|
||||
lfi.clear();
|
||||
cur_size = min_packet_size;
|
||||
}
|
||||
}
|
||||
if (cur_size > min_packet_size) session << lfi;
|
||||
}
|
||||
|
||||
|
||||
void PIFileTransfer::sendBreak(int session_id) {
|
||||
uint psid = header.session_id;
|
||||
header.session_id = session_id;
|
||||
sendReply(Break);
|
||||
header.session_id = psid;
|
||||
}
|
||||
|
||||
|
||||
void PIFileTransfer::sendReply(PIFileTransfer::ReplyCode reply) {
|
||||
// if (is_send_result) header.type = pt_SendResult;
|
||||
// else
|
||||
header.type = pt_Reply;
|
||||
PIByteArray ba;
|
||||
ba << header << (int)reply;
|
||||
sendRequest(ba);
|
||||
}
|
||||
|
||||
|
||||
bool PIFileTransfer::getSendRequest() {
|
||||
PITimeMeasurer tm;
|
||||
header.type = pt_SendRequest;
|
||||
header.id = 0;
|
||||
PIByteArray ba;
|
||||
ba << header;
|
||||
ba << (uint)session.size() << bytes_total_all;
|
||||
state_string = "send request";
|
||||
for (int i = 0; i < 3; i++) {
|
||||
tm.reset();
|
||||
sendRequest(ba);
|
||||
while (tm.elapsed_s() < timeout_) {
|
||||
if (break_) return false;
|
||||
//piCoutObj << send_replyes[0];
|
||||
if (replies[0] == Success) {
|
||||
state_string = "send permited!";
|
||||
return true;
|
||||
}
|
||||
if (replies[0] == Invalid) {
|
||||
state_string = "waiting for permit";
|
||||
tm.reset();
|
||||
}
|
||||
piMSleep(10);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void PIFileTransfer::processData(int id, PIByteArray & data) {
|
||||
// piCoutObj << "received packet" << id << ", size" << data.size();
|
||||
if (id < 1 || id > replies.size_s()) return;
|
||||
if (!session[id - 1].isEmpty()) {
|
||||
header.id = id;
|
||||
replies[id] = Success;
|
||||
sendReply(Success);
|
||||
if (checkSession() == 0) state_string = "receive ok";
|
||||
return;
|
||||
}
|
||||
EntryInfo fi;
|
||||
PIByteArray ba;
|
||||
while (!data.isEmpty()) {
|
||||
ba.clear();
|
||||
data >> fi;
|
||||
if (fi.entry.size > 0) data >> ba;
|
||||
fi.fsize = ba.size();
|
||||
bytes_total_cur += fi.fsize;
|
||||
bytes_file_all = fi.entry.size;
|
||||
bytes_file_cur = fi.fstart;
|
||||
// piCoutObj << "recv" << fi;
|
||||
session[id - 1] << fi;
|
||||
state_string = "receiving " + fi.entry.path;
|
||||
PIString path = dir.absolutePath() + dir.separator + fi.entry.path;
|
||||
if (fi.entry.isDir()) {
|
||||
// piCoutObj << "make dir" << fi.entry.path;
|
||||
if (!PIDir::make(path, false)) {
|
||||
state_string = "ERROR! while create directory " + path;
|
||||
piCoutObj << state_string;
|
||||
finish_receive(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (fi.entry.isFile()) {
|
||||
if (work_file.path() != path || !work_file.isOpened()) {
|
||||
work_file.close();
|
||||
if (!work_file.open(path, PIIODevice::ReadWrite)) {
|
||||
state_string = "ERROR! while open file " + path;
|
||||
piCoutObj << state_string;
|
||||
finish_receive(false);
|
||||
return;
|
||||
}
|
||||
if (work_file.size() > fi.entry.size) {
|
||||
piCoutObj << "*** error size" << work_file.size() << fi.entry.size;
|
||||
work_file.clear();
|
||||
work_file.resize(fi.entry.size);
|
||||
piCoutObj << "*** correct size" << work_file.size() << fi.entry.size;
|
||||
}
|
||||
}
|
||||
// piCoutObj << "write file" << path << work_file.path() << work_file.size() << fi.entry.size << work_file.pos() << fi.fstart << fi.fsize;
|
||||
if (work_file.size() < fi.fstart) {
|
||||
// piCoutObj << "error pos size" << work_file.pos() << fi.fstart;
|
||||
work_file.resize(fi.fstart);
|
||||
// piCoutObj << "correct pos size" << work_file.pos() << fi.fstart;
|
||||
}
|
||||
if (work_file.size() > fi.entry.size) {
|
||||
piCoutObj << "!!! error size" << work_file.size() << fi.entry.size;
|
||||
work_file.clear();
|
||||
work_file.resize(fi.entry.size);
|
||||
piCoutObj << "!!! correct size" << work_file.size() << fi.entry.size;
|
||||
}
|
||||
// if (fi.fstart != work_file.pos()) piCoutObj << "error pos" << work_file.pos() << fi.fstart;
|
||||
work_file.seek(fi.fstart);
|
||||
int rs = work_file.write(ba.data(), ba.size());
|
||||
if (rs != fi.fsize) {
|
||||
state_string = "ERROR! while read file " + fi.entry.path + " (must " + PIString::fromNumber(fi.fsize) + ", but read " + PIString::fromNumber(rs) + ")";
|
||||
piCoutObj << state_string;
|
||||
finish_receive(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
header.id = id;
|
||||
replies[id] = Success;
|
||||
if (checkSession() == 0) state_string = "receive ok";
|
||||
sendReply(Success);
|
||||
}
|
||||
|
||||
|
||||
PIByteArray PIFileTransfer::buildPacket(int id) {
|
||||
PIByteArray ret;
|
||||
PIByteArray ba;
|
||||
header.id = id + 1;
|
||||
header.type = pt_Data;
|
||||
//piCoutObj << "Packet" << header.id;
|
||||
//piCoutObj << "session id" << header.session_id;
|
||||
ret << header;
|
||||
for (int i = 0; i < session[id].size_s(); i++) {
|
||||
EntryInfo fi = session[id][i];
|
||||
// piCoutObj << "send" << fi;
|
||||
bytes_total_cur += fi.fsize;
|
||||
ret << fi;
|
||||
if (fi.entry.size > 0) {
|
||||
PIString path = dir.absolutePath() + dir.separator + fi.entry.path;
|
||||
if (work_file.path() != path || !work_file.isOpened()) {
|
||||
if (!work_file.open(path, PIIODevice::ReadOnly)) {
|
||||
break_ = true;
|
||||
state_string = "ERROR! while open file " + fi.entry.path;
|
||||
piCoutObj << state_string;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
work_file.seek(fi.fstart);
|
||||
ba.resize(fi.fsize);
|
||||
int rs = work_file.read(ba.data(), ba.size());
|
||||
if (rs != fi.fsize) {
|
||||
break_ = true;
|
||||
state_string = "ERROR! while read file " + fi.entry.path + " (must " + PIString::fromNumber(fi.fsize) + ", but read " + PIString::fromNumber(rs) + ")";
|
||||
piCoutObj << state_string;
|
||||
return ret;
|
||||
}
|
||||
ret << ba;
|
||||
}
|
||||
}
|
||||
EntryInfo cfile = session[id].back();
|
||||
state_string = "sending: " + cfile.entry.path;
|
||||
bytes_file_all = cfile.entry.size;
|
||||
bytes_file_cur = cfile.fstart;
|
||||
uint scrc = crc.calculate(ret);
|
||||
ret << scrc;
|
||||
//piCoutObj << "packet" << header.id << "send crc" << scrc;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool PIFileTransfer::finish_send(bool ok) {
|
||||
if (ok) state_string = "send done";
|
||||
else state_string = "send failed";
|
||||
// piCoutObj << state_string << PIString::readableSize(bytes_total_all);
|
||||
is_sending = false;
|
||||
work_file.close();
|
||||
work_file.setPath("");
|
||||
bytes_file_all = bytes_file_cur = 0;
|
||||
bytes_total_all = bytes_total_cur = 0;
|
||||
header.id = 0;
|
||||
if (!ok) sendBreak(header.session_id);
|
||||
else sendReply(Success);
|
||||
finishSend(ok);
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
void PIFileTransfer::finish_receive(bool ok) {
|
||||
if (ok) state_string = "receive done";
|
||||
else state_string = "receive failed";
|
||||
// piCoutObj << state_string << PIString::readableSize(bytes_total_all);
|
||||
is_receiving = false;
|
||||
work_file.close();
|
||||
work_file.setPath("");
|
||||
bytes_file_all = bytes_file_cur = 0;
|
||||
bytes_total_all = bytes_total_cur = 0;
|
||||
if (!ok) sendBreak(header.session_id);
|
||||
finishReceive(ok);
|
||||
}
|
||||
|
||||
138
src/io/pifiletransfer.h
Normal file
138
src/io/pifiletransfer.h
Normal file
@@ -0,0 +1,138 @@
|
||||
#ifndef PIFILETRANSFER_H
|
||||
#define PIFILETRANSFER_H
|
||||
|
||||
#include "pidir.h"
|
||||
#include "picrc.h"
|
||||
|
||||
#define PIFILETRANSFER_VERSION 1
|
||||
|
||||
class PIFileTransfer : public PIObject
|
||||
{
|
||||
PIOBJECT(PIFileTransfer)
|
||||
public:
|
||||
PIFileTransfer();
|
||||
~PIFileTransfer();
|
||||
|
||||
enum ReplyCode {Unknown = 0, Success, Invalid, Break};
|
||||
enum PacketType {pt_Data = 1, pt_Reply, pt_SendRequest};//, pt_SendResult};
|
||||
|
||||
|
||||
struct PacketHeader {
|
||||
union {
|
||||
struct {
|
||||
char sig[3]; // PFT
|
||||
uchar version;
|
||||
};
|
||||
uint raw_sig;
|
||||
};
|
||||
int type; // PacketType
|
||||
uint session_id;
|
||||
uint id;
|
||||
bool check_sig() {
|
||||
if (sig[0] != sign[0] || sig[1] != sign[1] || sig[2] != sign[2] || version != PIFILETRANSFER_VERSION) return false;
|
||||
return true;
|
||||
}
|
||||
// uint crc;
|
||||
};
|
||||
|
||||
struct PartHeader {
|
||||
uint id;
|
||||
uint total_size;
|
||||
uint part_start;
|
||||
uint part_size;
|
||||
};
|
||||
|
||||
struct EntryInfo {
|
||||
EntryInfo() {
|
||||
fstart = fsize = 0;
|
||||
parts = part_index = 0;
|
||||
}
|
||||
PIFile::FileInfo entry;
|
||||
llong fstart;
|
||||
llong fsize;
|
||||
uint parts;
|
||||
uint part_index;
|
||||
};
|
||||
|
||||
//bool send(const PIFile & file);
|
||||
bool send(const PIString & file);
|
||||
// bool send(const PIStringList &files);
|
||||
bool send(PIFile::FileInfo entry) {return send(PIVector<PIFile::FileInfo>() << entry);}
|
||||
bool send(PIVector<PIFile::FileInfo> entries);
|
||||
|
||||
void stopSend();
|
||||
void stopReceive();
|
||||
|
||||
bool isSending() const {return is_sending;}
|
||||
bool isReceiving() const {return is_receiving;}
|
||||
|
||||
void setPacketSize(int size) {max_packet_size = size;}
|
||||
int packetSize() const {return max_packet_size;}
|
||||
|
||||
void setTimeout(double sec) {timeout_ = sec;}
|
||||
double timeout() const {return timeout_;}
|
||||
|
||||
void setDirectory(const PIDir &d) {dir = d;}
|
||||
void setDirectory(const PIString &path) {dir.setDir(path);}
|
||||
PIDir directory() const {return dir;}
|
||||
|
||||
const PIString & stateString() const {return state_string;}
|
||||
llong bytesTotalAll() const {return bytes_total_all;}
|
||||
llong bytesTotalCur() const {return bytes_total_cur;}
|
||||
llong bytesFileAll() const {return bytes_file_all;}
|
||||
llong bytesFileCur() const {return bytes_file_cur;}
|
||||
const PIString * stateString_ptr() const {return &state_string;}
|
||||
const llong * bytesTotalAll_ptr() const {return &bytes_total_all;}
|
||||
const llong * bytesTotalCur_ptr() const {return &bytes_total_cur;}
|
||||
const llong * bytesFileAll_ptr() const {return &bytes_file_all;}
|
||||
const llong * bytesFileCur_ptr() const {return &bytes_file_cur;}
|
||||
|
||||
EVENT(startReceive)
|
||||
EVENT1(receiveRequest, bool *, ok)
|
||||
EVENT1(finishReceive, bool, ok)
|
||||
EVENT(startSend)
|
||||
EVENT1(finishSend, bool, ok)
|
||||
|
||||
EVENT1(sendRequest, PIByteArray &, data)
|
||||
EVENT_HANDLER1(void, received, PIByteArray &, data);
|
||||
|
||||
private:
|
||||
static const char sign[];
|
||||
int max_packet_size, min_packet_size, fileinfo_size;
|
||||
bool break_, is_sending, is_receiving;
|
||||
double timeout_;
|
||||
|
||||
PIVector<PIVector<EntryInfo> > session;
|
||||
PIVector<ReplyCode> replies;
|
||||
PIString state_string;
|
||||
llong bytes_total_all, bytes_total_cur, bytes_file_all, bytes_file_cur;
|
||||
PacketHeader header;
|
||||
PIDir dir;
|
||||
PIFile work_file;
|
||||
CRC_16 crc;
|
||||
|
||||
bool sendFiles(PIVector<PIFile::FileInfo> files);
|
||||
int checkSession();
|
||||
void buildSession(PIVector<PIFile::FileInfo> files);
|
||||
void sendBreak(int session_id);
|
||||
void sendReply(ReplyCode reply);
|
||||
bool getSendRequest();
|
||||
void processData(int id, PIByteArray &data);
|
||||
PIByteArray buildPacket(int id);
|
||||
bool finish_send(bool ok);
|
||||
void finish_receive(bool ok);
|
||||
};
|
||||
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PIFileTransfer::PacketHeader & v) {s << v.raw_sig << v.type << v.session_id << v.id; return s;}
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PIFileTransfer::PacketHeader & v) {s >> v.raw_sig >> v.type >> v.session_id >> v.id; return s;}
|
||||
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PIFileTransfer::PartHeader & v) {s << v.id << v.total_size << v.part_start << v.part_size; return s;}
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PIFileTransfer::PartHeader & v) {s >> v.id >> v.total_size >> v.part_start >> v.part_size; return s;}
|
||||
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PIFileTransfer::EntryInfo & v) {s << v.entry.path << v.entry.size << v.entry.time_modification << v.entry.time_access << int(v.entry.flags)
|
||||
<< v.entry.id_user << v.entry.id_group << v.fstart; return s;}
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PIFileTransfer::EntryInfo & v) {s >> v.entry.path >> v.entry.size >> v.entry.time_modification >> v.entry.time_access >> (int&)(v.entry.flags)
|
||||
>> v.entry.id_user >> v.entry.id_group >> v.fstart; return s;}
|
||||
inline PICout operator <<(PICout s, const PIFileTransfer::EntryInfo & v) {s.setControl(0, true); s << "FileInfo(\"" << v.entry.path << "\", " << PIString::fromNumber(v.entry.flags, 16) << PIString::readableSize(v.entry.size) << " b | " << PIString::readableSize(v.fsize) << " b)"; s.restoreControl(); return s;}
|
||||
|
||||
#endif // PIFILETRANSFER_H
|
||||
315
src/io/piiodevice.cpp
Executable file
315
src/io/piiodevice.cpp
Executable file
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Abstract input/output device
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "piiodevice.h"
|
||||
#include "piconfig.h"
|
||||
|
||||
|
||||
/*! \class PIIODevice
|
||||
* \brief Base class for input/output classes
|
||||
*
|
||||
* \section PIIODevice_sec0 Synopsis
|
||||
* This class provide open/close logic, threaded read/write and virtual input/output
|
||||
* functions \a read() and \a write(). You should implement pure virtual
|
||||
* function \a openDevice() in your subclass.
|
||||
*
|
||||
* \section PIIODevice_sec1 Open and close
|
||||
* PIIODevice have boolean variable indicated open status. Returns of functions
|
||||
* \a openDevice() and \a closeDevice() change this variable.
|
||||
*
|
||||
* \section PIIODevice_sec2 Threaded read
|
||||
* PIIODevice based on PIThread, so it`s overload \a run() to exec \a read()
|
||||
* in background thread. If read is successful virtual function \a threadedRead()
|
||||
* is executed. Default implementation of this function execute external static
|
||||
* function set by \a setThreadedReadSlot() with data set by \a setThreadedReadData().
|
||||
* Extrenal static function should have format \n
|
||||
* bool func_name(void * Threaded_read_data, uchar * readed_data, int readed_size)\n
|
||||
* Threaded read starts with function \a startThreadedRead().
|
||||
*
|
||||
* \section PIIODevice_sec3 Threaded write
|
||||
* PIIODevice aggregate another PIThread to perform a threaded write by function
|
||||
* \a writeThreaded(). This function add task to internal queue and return
|
||||
* queue entry ID. You should start write thread by function \a startThreadedWrite.
|
||||
* On successful write event \a threadedWriteEvent is raised with two arguments -
|
||||
* task ID and written bytes count.
|
||||
*
|
||||
* \section PIIODevice_sec4 Internal buffer
|
||||
* PIIODevice have internal buffer for threaded read, and \a threadedRead() function
|
||||
* receive pointer to this buffer in first argument. You can adjust size of this buffer
|
||||
* by function \a setThreadedReadBufferSize() \n
|
||||
* Default size of this buffer is 4096 bytes.
|
||||
*
|
||||
* \section PIIODevice_sec5 Reopen
|
||||
* When threaded read is begin its call \a open() if device is closed. While threaded
|
||||
* read running PIIODevice check if device opened every read and if not call \a open()
|
||||
* every reopen timeout if reopen enabled. Reopen timeout is set by \a setReopenTimeout(),
|
||||
* reopen enable is set by \a setReopenEnabled().
|
||||
*
|
||||
* \section PIIODevice_sec6 Configuration
|
||||
* This is virtual function \a configureDevice() which executes when \a configure()
|
||||
* executes. This function takes two arguments: "e_main" and "e_parent" as void*. There
|
||||
* are pointers to PIConfig::Entry entries of section "section" and their parent. If
|
||||
* there is no parent "e_parent" = 0. Function \a configure() set three parameters of
|
||||
* device: "reopenEnabled", "reopenTimeout" and "threadedReadBufferSize", then execute
|
||||
* function \a configureDevice().
|
||||
* \n Each ancestor of %PIIODevice reimlements \a configureDevice() function to be able
|
||||
* to be confured from configuration file. This parameters described at section
|
||||
* "Configurable parameters" in the class reference. \n Usage example:
|
||||
* \snippet piiodevice.cpp configure
|
||||
* Implementation example:
|
||||
* \snippet piiodevice.cpp configureDevice
|
||||
*
|
||||
* \section PIIODevice_sec7 Creating devices by unambiguous string
|
||||
* There are some virtual functions to describe child class without its declaration.
|
||||
* \n \a fullPathPrefix() should returns unique prefix of device
|
||||
* \n \a constructFullPath() should returns full unambiguous string, contains prefix and all device parameters
|
||||
* \n \a configureFromFullPath() provide configuring device from full unambiguous string without prefix and "://"
|
||||
* \n Macro PIIODEVICE should be used instead of PIOBJECT
|
||||
* \n Macro REGISTER_DEVICE should be used after definition of class, i.e. at the last line of *.cpp file
|
||||
* \n \n If custom I/O device corresponds there rules, it can be returned by function \a createFromFullPath().
|
||||
* \n Each PIP I/O device has custom unambiguous string description:
|
||||
* * PIFile: "file://<path>"
|
||||
* * PIBinaryLog: "binlog://<logDir>[:<filePrefix>][:<defaultID>]"
|
||||
* * PISerial: "ser://<device>:<speed(50|...|115200)>[:<dataBitsCount(6|7|8)>][:<parity(N|E|O)>][:<stopBits(1|2)>]"
|
||||
* * PIEthernet: UDP "eth://UDP:<readIP>:<readPort>:<sendIP>:<sendPort>[:<multicast(mcast:<ip>)>]"
|
||||
* * PIEthernet: TCP "eth://TCP:<IP>:<Port>"
|
||||
* * PIUSB: "usb://<vid>:<pid>[:<deviceNumber>][:<readEndpointNumber>][:<writeEndpointNumber>]"
|
||||
* \n \n Examples:
|
||||
* * PIFile: "file://../text.txt"
|
||||
* * PIBinaryLog: "binlog://../logs/:mylog_:1"
|
||||
* * PISerial: "ser:///dev/ttyUSB0:9600:8:N:1", equivalent "ser:///dev/ttyUSB0:9600"
|
||||
* * PIEthernet: "eth://TCP:127.0.0.1:16666", "eth://UDP:192.168.0.5:16666:192.168.0.6:16667:mcast:234.0.2.1:mcast:234.0.2.2"
|
||||
* * PIUSB: "usb://0bb4:0c86:1:1:2"
|
||||
* \n \n
|
||||
* So, custom I/O device can be created with next call:
|
||||
* \code{cpp}
|
||||
* // creatring devices
|
||||
* PISerial * ser = (PISerial * )PIIODevice::createFromFullPath("ser://COM1:115200");
|
||||
* PIEthernet * eth = (PIEthernet * )PIIODevice::createFromFullPath("eth://UDP:127.0.0.1:4001:127.0.0.1:4002");
|
||||
* // examine devices
|
||||
* piCout << ser << ser->properties();
|
||||
* piCout << eth << eth->properties();
|
||||
* \endcode
|
||||
*
|
||||
* \section PIIODevice_ex0 Example
|
||||
* \snippet piiodevice.cpp 0
|
||||
*/
|
||||
|
||||
|
||||
PIIODevice::PIIODevice(): PIThread() {
|
||||
mode_ = ReadOnly;
|
||||
_init();
|
||||
setPath(PIString());
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Constructs a PIIODevice with path and mode
|
||||
* \param path path to device
|
||||
* \param type mode for open */
|
||||
PIIODevice::PIIODevice(const PIString & path, PIIODevice::DeviceMode mode): PIThread() {
|
||||
mode_ = mode;
|
||||
_init();
|
||||
setPath(path);
|
||||
}
|
||||
|
||||
|
||||
PIIODevice::~PIIODevice() {
|
||||
stop();
|
||||
if (opened_) {
|
||||
closeDevice();
|
||||
if (!opened_)
|
||||
closed();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIIODevice::_init() {
|
||||
opened_ = init_ = thread_started_ = false;
|
||||
raise_threaded_read_ = true;
|
||||
ret_func_ = 0;
|
||||
ret_data_ = 0;
|
||||
tri = 0;
|
||||
setReopenEnabled(true);
|
||||
setReopenTimeout(1000);
|
||||
setThreadedReadBufferSize(4096);
|
||||
timer.setName("__S__reopen_timer");
|
||||
write_thread.setName("__S__write_thread");
|
||||
CONNECT2(void, void * , int, &timer, tickEvent, this, check_start);
|
||||
CONNECT(void, &write_thread, started, this, write_func);
|
||||
}
|
||||
|
||||
|
||||
void PIIODevice::check_start(void * data, int delim) {
|
||||
//cout << "check " << tread_started_ << endl;
|
||||
if (open()) {
|
||||
thread_started_ = true;
|
||||
timer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIIODevice::write_func() {
|
||||
while (!write_thread.isStopping()) {
|
||||
while (!write_queue.isEmpty()) {
|
||||
if (write_thread.isStopping()) return;
|
||||
write_thread.lock();
|
||||
PIPair<PIByteArray, ullong> item(write_queue.dequeue());
|
||||
write_thread.unlock();
|
||||
int ret = write(item.first);
|
||||
threadedWriteEvent(item.second, ret);
|
||||
}
|
||||
msleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIIODevice::terminate() {
|
||||
thread_started_ = false;
|
||||
if (!isInitialized()) return;
|
||||
if (isRunning()) {
|
||||
stop();
|
||||
PIThread::terminate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIIODevice::begin() {
|
||||
//cout << " begin\n";
|
||||
thread_started_ = false;
|
||||
if (!opened_) {
|
||||
if (open()) {
|
||||
thread_started_ = true;
|
||||
//cout << " open && ok\n";
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
thread_started_ = true;
|
||||
//cout << " ok\n";
|
||||
return;
|
||||
}
|
||||
//init();
|
||||
if (!timer.isRunning() && isReopenEnabled()) timer.start(reopenTimeout());
|
||||
}
|
||||
|
||||
|
||||
void PIIODevice::run() {
|
||||
if (!isReadable()) {
|
||||
//cout << "not readable\n";
|
||||
PIThread::stop();
|
||||
return;
|
||||
}
|
||||
if (!thread_started_) {
|
||||
msleep(5);
|
||||
//cout << "not started\n";
|
||||
return;
|
||||
}
|
||||
readed_ = read(buffer_tr.data(), buffer_tr.size_s());
|
||||
if (readed_ <= 0) {
|
||||
msleep(10);
|
||||
//cout << readed_ << ", " << errno << ", " << errorString() << endl;
|
||||
return;
|
||||
}
|
||||
//piCoutObj << "readed" << readed_;// << ", " << errno << ", " << errorString();
|
||||
threadedRead(buffer_tr.data(), readed_);
|
||||
if (raise_threaded_read_) threadedReadEvent(buffer_tr.data(), readed_);
|
||||
}
|
||||
|
||||
|
||||
PIByteArray PIIODevice::readForTime(double timeout_ms) {
|
||||
PIByteArray str;
|
||||
if (timeout_ms <= 0.) return str;
|
||||
int ret;
|
||||
uchar * td = new uchar[threadedReadBufferSize()];
|
||||
tm.reset();
|
||||
while (tm.elapsed_m() < timeout_ms) {
|
||||
ret = read(td, threadedReadBufferSize());
|
||||
if (ret <= 0) msleep(1);
|
||||
else str.append(td, ret);
|
||||
}
|
||||
delete td;
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
ullong PIIODevice::writeThreaded(const PIByteArray & data) {
|
||||
write_thread.lock();
|
||||
write_queue.enqueue(PIPair<PIByteArray, ullong>(data, tri));
|
||||
++tri;
|
||||
write_thread.unlock();
|
||||
return tri - 1;
|
||||
}
|
||||
|
||||
|
||||
bool PIIODevice::configure(const PIString & config_file, const PIString & section, bool parent_section) {
|
||||
PIConfig conf(config_file, PIIODevice::ReadOnly);
|
||||
if (!conf.isOpened()) return false;
|
||||
bool ex = true;
|
||||
PIConfig::Entry em;
|
||||
if (section.isEmpty()) em = conf.rootEntry();
|
||||
else em = conf.getValue(section, PIString(), &ex);
|
||||
if (!ex) return false;
|
||||
PIConfig::Entry * ep = 0;
|
||||
if (parent_section) ep = em.parent();
|
||||
if (ep != 0) {
|
||||
setReopenEnabled(ep->getValue("reopenEnabled", isReopenEnabled(), &ex));
|
||||
if (!ex) setReopenEnabled(em.getValue("reopenEnabled", isReopenEnabled()));
|
||||
setReopenTimeout(ep->getValue("reopenTimeout", reopenTimeout(), &ex));
|
||||
if (!ex) setReopenTimeout(em.getValue("reopenTimeout", reopenTimeout()));
|
||||
setThreadedReadBufferSize(ep->getValue("threadedReadBufferSize", int(buffer_tr.size_s()), &ex));
|
||||
if (!ex) setThreadedReadBufferSize(em.getValue("threadedReadBufferSize", int(buffer_tr.size_s())));
|
||||
} else {
|
||||
setReopenEnabled(em.getValue("reopenEnabled", isReopenEnabled()));
|
||||
setReopenTimeout(em.getValue("reopenTimeout", reopenTimeout()));
|
||||
setThreadedReadBufferSize(em.getValue("threadedReadBufferSize", int(buffer_tr.size_s())));
|
||||
}
|
||||
return configureDevice(&em, ep);
|
||||
}
|
||||
|
||||
|
||||
PIIODevice * PIIODevice::createFromFullPath(const PIString & full_path) {
|
||||
PIString prefix = full_path.left(full_path.find(":"));
|
||||
if (prefix.isEmpty()) return 0;
|
||||
PIVector<const PIObject * > rd(PICollection::groupElements("__PIIODevices__"));
|
||||
piForeachC (PIObject * d, rd) {
|
||||
if (prefix == ((const PIIODevice * )d)->fullPathPrefix()) {
|
||||
PIIODevice * nd = ((const PIIODevice * )d)->copy();
|
||||
if (nd) nd->configureFromFullPath(full_path.mid(prefix.length() + 3));
|
||||
return nd;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
PIString PIIODevice::normalizeFullPath(const PIString & full_path) {
|
||||
static PIMutex nfp_mutex;
|
||||
static PIMap<PIString, PIString> nfp_cache;
|
||||
PIMutexLocker nfp_ml(nfp_mutex);
|
||||
PIString ret = nfp_cache.value(full_path);
|
||||
if (!ret.isEmpty())
|
||||
return ret;
|
||||
//piCout << "normalizeFullPath" << full_path;
|
||||
PIIODevice * d = createFromFullPath(full_path);
|
||||
//piCout << "normalizeFullPath" << d;
|
||||
if (d == 0) return PIString();
|
||||
ret = d->constructFullPath();
|
||||
delete d;
|
||||
nfp_cache[full_path] = ret;
|
||||
return ret;
|
||||
}
|
||||
333
src/io/piiodevice.h
Executable file
333
src/io/piiodevice.h
Executable file
@@ -0,0 +1,333 @@
|
||||
/*! \file piiodevice.h
|
||||
* \brief Abstract input/output device
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Abstract input/output device
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIIODEVICE_H
|
||||
#define PIIODEVICE_H
|
||||
|
||||
#include "piinit.h"
|
||||
#include "picollection.h"
|
||||
#include "pivariant.h"
|
||||
#include "pitimer.h"
|
||||
|
||||
// function executed from threaded read, pass ThreadedReadData, readedData, sizeOfData
|
||||
typedef bool (*ReadRetFunc)(void * , uchar * , int );
|
||||
|
||||
#ifdef DOXYGEN
|
||||
|
||||
//! \relatesalso PIIODevice \brief Use this macro to enable automatic creation instances of your class with \a createFromFullPath() function
|
||||
# define REGISTER_DEVICE(class)
|
||||
|
||||
//! \relatesalso PIIODevice \brief Use this macro instead of PIOBJECT when describe your own PIIODevice
|
||||
# define PIIODEVICE(class)
|
||||
|
||||
#else
|
||||
|
||||
# define REGISTER_DEVICE(class) ADD_NEW_TO_COLLECTION_WITH_NAME(__PIIODevices__, class, __S__collection_##class##__)
|
||||
# define PIIODEVICE(class) PIOBJECT_SUBCLASS(class, PIIODevice) PIIODevice * copy() const {return new class();}
|
||||
|
||||
#endif
|
||||
|
||||
class PIP_EXPORT PIIODevice: public PIThread
|
||||
{
|
||||
PIOBJECT_SUBCLASS(PIIODevice, PIThread)
|
||||
public:
|
||||
|
||||
//! Constructs a empty PIIODevice
|
||||
PIIODevice();
|
||||
|
||||
//! \brief Open modes for PIIODevice
|
||||
enum DeviceMode {
|
||||
ReadOnly /*! Device can only read */ = 0x01,
|
||||
WriteOnly /*! Device can only write */ = 0x02,
|
||||
ReadWrite /*! Device can both read and write */ = 0x03
|
||||
};
|
||||
|
||||
PIIODevice(const PIString & path, DeviceMode mode = ReadWrite);
|
||||
virtual ~PIIODevice();
|
||||
|
||||
//! Current open mode of device
|
||||
DeviceMode mode() const {return mode_;}
|
||||
|
||||
//! Current path of device
|
||||
PIString path() const {return property("path").toString();}
|
||||
|
||||
//! Set path of device
|
||||
void setPath(const PIString & path) {setProperty("path", path);}
|
||||
|
||||
//! Return \b true if mode is ReadOnly or ReadWrite
|
||||
bool isReadable() const {return (mode_ & ReadOnly);}
|
||||
|
||||
//! Return \b true if mode is WriteOnly or ReadWrite
|
||||
bool isWriteable() const {return (mode_ & WriteOnly);}
|
||||
|
||||
bool isInitialized() const {return init_;}
|
||||
|
||||
//! Return \b true if device is successfully opened
|
||||
bool isOpened() const {return opened_;}
|
||||
|
||||
//! Return \b true if device is closed
|
||||
bool isClosed() const {return !opened_;}
|
||||
|
||||
//! Return \b true if device can read \b now
|
||||
virtual bool canRead() const {return opened_ && (mode_ & ReadOnly);}
|
||||
|
||||
//! Return \b true if device can write \b now
|
||||
virtual bool canWrite() const {return opened_ && (mode_ & WriteOnly);}
|
||||
|
||||
|
||||
//! Set execution of \a open enabled while threaded read on closed device
|
||||
void setReopenEnabled(bool yes = true) {setProperty("reopenEnabled", yes);}
|
||||
|
||||
//! Set timeout in milliseconds between \a open tryings if reopen is enabled
|
||||
void setReopenTimeout(int msecs) {setProperty("reopenTimeout", msecs);}
|
||||
|
||||
|
||||
//! Return reopen enable
|
||||
bool isReopenEnabled() const {return property("reopenEnabled").toBool();}
|
||||
|
||||
//! Return reopen timeout
|
||||
int reopenTimeout() {return property("reopenTimeout").toInt();}
|
||||
|
||||
|
||||
/** \brief Set "threaded read slot"
|
||||
* \details Set external static function of threaded read that will be executed
|
||||
* at every successful threaded read. Function should have format
|
||||
* "bool func(void * data, uchar * readed, int size)" */
|
||||
void setThreadedReadSlot(ReadRetFunc func) {ret_func_ = func;}
|
||||
|
||||
//! Set custom data that will be passed to "threaded read slot"
|
||||
void setThreadedReadData(void * d) {ret_data_ = d;}
|
||||
|
||||
/** \brief Set size of threaded read buffer
|
||||
* \details Default size is 4096 bytes. If your device can read at single read
|
||||
* more than 4096 bytes you should use this function to adjust buffer size */
|
||||
void setThreadedReadBufferSize(int new_size) {buffer_tr.resize(new_size);}
|
||||
|
||||
//! Return size of threaded read buffer
|
||||
int threadedReadBufferSize() const {return buffer_tr.size_s();}
|
||||
|
||||
//! Return content of threaded read buffer
|
||||
const uchar * threadedReadBuffer() const {return buffer_tr.data();}
|
||||
|
||||
//! Return custom data that will be passed to "threaded read slot"
|
||||
void * threadedReadData() const {return ret_data_;}
|
||||
|
||||
|
||||
//! Return \b true if threaded read is started
|
||||
bool isThreadedRead() const {return isRunning();}
|
||||
|
||||
//! Start threaded read
|
||||
void startThreadedRead() {if (!isRunning()) PIThread::start();}
|
||||
|
||||
//! Start threaded read and assign "threaded read slot" to "func"
|
||||
void startThreadedRead(ReadRetFunc func) {ret_func_ = func; if (!isRunning()) PIThread::start();}
|
||||
|
||||
//! Stop threaded read
|
||||
void stopThreadedRead() {PIThread::terminate();}
|
||||
|
||||
|
||||
//! Return \b true if threaded write is started
|
||||
bool isThreadedWrite() const {return write_thread.isRunning();}
|
||||
|
||||
//! Start threaded write
|
||||
void startThreadedWrite() {if (!write_thread.isRunning()) write_thread.startOnce();}
|
||||
|
||||
//! Stop threaded write
|
||||
void stopThreadedWrite() {write_thread.terminate();}
|
||||
|
||||
//! Clear threaded write task queue
|
||||
void clearThreadedWriteQueue() {write_thread.lock(); write_queue.clear(); write_thread.unlock();}
|
||||
|
||||
|
||||
//! Start both threaded read and threaded write
|
||||
void start() {startThreadedRead(); startThreadedWrite();}
|
||||
|
||||
//! Stop both threaded read and threaded write and if "wait" block until both threads are stop
|
||||
void stop(bool wait = false) {stopThreadedRead(); stopThreadedWrite(); if (wait) while (write_thread.isRunning() || isRunning()) msleep(1);}
|
||||
|
||||
|
||||
//! Reimplement this function to read from your device
|
||||
virtual int read(void * read_to, int max_size) {piCoutObj << "\"read\" is not implemented!"; return -2;}
|
||||
|
||||
//! Reimplement this function to write to your device
|
||||
virtual int write(const void * data, int max_size) {piCoutObj << "\"write\" is not implemented!"; return -2;}
|
||||
|
||||
|
||||
//! Read from device maximum "max_size" bytes and return them as PIByteArray
|
||||
PIByteArray read(int max_size) {buffer_in.resize(max_size); int ret = read(buffer_in.data(), max_size); if (ret < 0) return PIByteArray(); return buffer_in.resized(ret);}
|
||||
|
||||
//! Read from device for "timeout_ms" milliseconds and return readed data as PIByteArray. Timeout should to be greater than 0
|
||||
PIByteArray readForTime(double timeout_ms);
|
||||
|
||||
//! Write "data" to device
|
||||
int write(const PIByteArray & data) {return write(data.data(), data.size_s());}
|
||||
|
||||
|
||||
//! Add task to threaded write queue and return task ID
|
||||
ullong writeThreaded(const void * data, int max_size) {return writeThreaded(PIByteArray(data, uint(max_size)));}
|
||||
|
||||
//! Add task to threaded write queue and return task ID
|
||||
ullong writeThreaded(const PIByteArray & data);
|
||||
|
||||
|
||||
//! Configure device from section "section" of file "config_file", if "parent_section" parent section also will be read
|
||||
bool configure(const PIString & config_file, const PIString & section, bool parent_section = false);
|
||||
|
||||
|
||||
//! Reimplement to construct full unambiguous string prefix. \ref PIIODevice_sec7
|
||||
virtual PIString fullPathPrefix() const {return PIString();}
|
||||
|
||||
//! Reimplement to construct full unambiguous string, describes this device, default returns \a fullPathPrefix() + "://" + \a path()
|
||||
virtual PIString constructFullPath() const {return fullPathPrefix() + "://" + path();}
|
||||
|
||||
//! \brief Try to determine suitable device, create new one, configure it with \a configureFromFullPath() and returns it.
|
||||
//! \details To function \a configureFromFullPath() "full_path" passed without \a fullPathPrefix() + "://".
|
||||
//! See \ref PIIODevice_sec7
|
||||
static PIIODevice * createFromFullPath(const PIString & full_path);
|
||||
|
||||
static PIString normalizeFullPath(const PIString & full_path);
|
||||
|
||||
|
||||
EVENT_HANDLER(bool, open) {if (!init_) init(); opened_ = openDevice(); if (opened_) opened(); return opened_;}
|
||||
EVENT_HANDLER1(bool, open, const PIString &, _path) {setPath(_path); if (!init_) init(); opened_ = openDevice(); if (opened_) opened(); return opened_;}
|
||||
EVENT_HANDLER1(bool, open, const DeviceMode &, _mode) {mode_ = _mode; if (!init_) init(); opened_ = openDevice(); if (opened_) opened(); return opened_;}
|
||||
EVENT_HANDLER2(bool, open, const PIString &, _path, const DeviceMode &, _mode) {setPath(_path); mode_ = _mode; if (!init_) init(); opened_ = openDevice(); if (opened_) opened(); return opened_;}
|
||||
EVENT_HANDLER(bool, close) {opened_ = !closeDevice(); if (!opened_) closed(); return !opened_;}
|
||||
EVENT_HANDLER(bool, initialize) {init_ = init(); return init_;}
|
||||
|
||||
EVENT_VHANDLER(void, flush) {;}
|
||||
|
||||
EVENT(opened)
|
||||
EVENT(closed)
|
||||
EVENT2(threadedReadEvent, uchar * , readed, int, size)
|
||||
EVENT2(threadedWriteEvent, ullong, id, int, written_size)
|
||||
|
||||
//! \handlers
|
||||
//! \{
|
||||
|
||||
//! \fn bool open()
|
||||
//! \brief Open device
|
||||
|
||||
//! \fn bool open(const PIString & path)
|
||||
//! \brief Open device with path "path"
|
||||
|
||||
//! \fn bool open(const DeviceMode & mode)
|
||||
//! \brief Open device with mode "mode"
|
||||
|
||||
//! \fn bool open(const PIString & path, const DeviceMode & mode)
|
||||
//! \brief Open device with path "path" and mode "mode"
|
||||
|
||||
//! \fn bool close()
|
||||
//! \brief Close device
|
||||
|
||||
//! \fn bool initialize()
|
||||
//! \brief Initialize device
|
||||
|
||||
//! \}
|
||||
//! \vhandlers
|
||||
//! \{
|
||||
|
||||
//! \fn void flush()
|
||||
//! \brief Immediate write all buffers
|
||||
|
||||
//! \}
|
||||
//! \events
|
||||
//! \{
|
||||
|
||||
//! \fn void opened()
|
||||
//! \brief Raise if succesfull open
|
||||
|
||||
//! \fn void closed()
|
||||
//! \brief Raise if succesfull close
|
||||
|
||||
//! \fn void threadedReadEvent(uchar * readed, int size)
|
||||
//! \brief Raise if read thread succesfull read some data
|
||||
|
||||
//! \fn void threadedWriteEvent(ullong id, int written_size)
|
||||
//! \brief Raise if write thread succesfull write some data of task with ID "id"
|
||||
|
||||
//! \}
|
||||
//! \ioparams
|
||||
//! \{
|
||||
#ifdef DOXYGEN
|
||||
//! \brief setReopenEnabled, default "true"
|
||||
bool reopenEnabled;
|
||||
|
||||
//! \brief setReopenTimeout in ms, default 1000
|
||||
int reopenTimeout;
|
||||
|
||||
//! \brief setThreadedReadBufferSize in bytes, default 4096
|
||||
int threadedReadBufferSize;
|
||||
#endif
|
||||
//! \}
|
||||
|
||||
protected:
|
||||
|
||||
//! Function executed before first \a openDevice() or from constructor
|
||||
virtual bool init() {return true;}
|
||||
|
||||
//! Reimplement to configure device from entries "e_main" and "e_parent", cast arguments to \a PIConfig::Entry*
|
||||
virtual bool configureDevice(const void * e_main, const void * e_parent = 0) {return true;}
|
||||
|
||||
//! Reimplement to open device, return value will be set to "opened_" variable
|
||||
virtual bool openDevice() = 0; // use path_, type_, opened_, init_ variables
|
||||
|
||||
//! Reimplement to close device, inverse return value will be set to "opened_" variable
|
||||
virtual bool closeDevice() {return true;} // use path_, type_, opened_, init_ variables
|
||||
|
||||
//! Function executed when thread read some data, default implementation execute external slot "ret_func_"
|
||||
virtual bool threadedRead(uchar * readed, int size) {if (ret_func_ != 0) return ret_func_(ret_data_, readed, size); return true;}
|
||||
|
||||
|
||||
//! Reimplement to configure your device with parameters of full unambiguous string. Default implementation does nothing
|
||||
virtual void configureFromFullPath(const PIString & full_path) {;}
|
||||
|
||||
|
||||
void terminate();
|
||||
|
||||
|
||||
DeviceMode mode_;
|
||||
ReadRetFunc ret_func_;
|
||||
bool init_, opened_, thread_started_, raise_threaded_read_;
|
||||
void * ret_data_;
|
||||
|
||||
private:
|
||||
EVENT_HANDLER2(void, check_start, void * , data, int, delim);
|
||||
EVENT_HANDLER(void, write_func);
|
||||
|
||||
virtual PIIODevice * copy() const {return 0;}
|
||||
void _init();
|
||||
void begin();
|
||||
void run();
|
||||
void end() {terminate();}
|
||||
|
||||
PITimer timer;
|
||||
PITimeMeasurer tm;
|
||||
PIThread write_thread;
|
||||
PIByteArray buffer_in, buffer_tr;
|
||||
PIQueue<PIPair<PIByteArray, ullong> > write_queue;
|
||||
ullong tri;
|
||||
int readed_;
|
||||
|
||||
};
|
||||
|
||||
#endif // PIIODEVICE_H
|
||||
106
src/io/piiostring.cpp
Normal file
106
src/io/piiostring.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
PIIODevice wrapper around PIString
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "piiostring.h"
|
||||
|
||||
|
||||
/*! \class PIIOString
|
||||
* \brief PIIODevice wrapper around PIString
|
||||
*
|
||||
* \section PIIOString_sec0 Synopsis
|
||||
* This class sllow you to use PIString as PIIODevice and pass it to, e.g. PIConfig
|
||||
*/
|
||||
|
||||
//REGISTER_DEVICE(PIIOString);
|
||||
|
||||
|
||||
PIIOString::PIIOString(PIString * string, PIIODevice::DeviceMode mode_) {
|
||||
open(string, mode_);
|
||||
}
|
||||
|
||||
|
||||
PIIOString::PIIOString(const PIString & string) {
|
||||
open(string);
|
||||
}
|
||||
|
||||
|
||||
PIIOString::~PIIOString() {
|
||||
}
|
||||
|
||||
|
||||
bool PIIOString::open(PIString * string, PIIODevice::DeviceMode mode_) {
|
||||
str = string;
|
||||
return PIIODevice::open(mode_);
|
||||
}
|
||||
|
||||
|
||||
bool PIIOString::open(const PIString & string) {
|
||||
str = const_cast<PIString*>(&string);
|
||||
return PIIODevice::open(PIIODevice::ReadOnly);
|
||||
}
|
||||
|
||||
|
||||
PIString PIIOString::readLine() {
|
||||
if (!canRead() || !str) return PIString();
|
||||
int np = pos;
|
||||
while (++np < str->size_s())
|
||||
if ((*str)[np] == '\n')
|
||||
break;
|
||||
PIString ret = str->mid(pos, np - pos);
|
||||
pos = piMini(np + 1, str->size_s());
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int PIIOString::read(void * read_to, int max_size) {
|
||||
if (!canRead() || !str) return -1;
|
||||
PIString rs = str->mid(pos, max_size);
|
||||
pos += max_size;
|
||||
if (pos > str->size_s()) pos = str->size_s();
|
||||
int ret = rs.lengthAscii();
|
||||
memcpy(read_to, rs.data(), rs.lengthAscii());
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int PIIOString::write(const void * data, int max_size) {
|
||||
if (!canWrite() || !str) return -1;
|
||||
//piCout << "write" << data;
|
||||
if (pos > str->size_s()) pos = str->size_s();
|
||||
PIString rs = PIString((const char *)data);
|
||||
if (rs.size_s() > max_size) rs.resize(max_size);
|
||||
str->insert(pos, rs);
|
||||
pos += rs.size_s();
|
||||
return rs.lengthAscii();
|
||||
}
|
||||
|
||||
|
||||
int PIIOString::writeString(const PIString & string) {
|
||||
if (!canWrite() || !str) return -1;
|
||||
if (pos > str->size_s()) pos = str->size_s();
|
||||
str->insert(pos, string);
|
||||
pos += string.size_s();
|
||||
return string.lengthAscii();
|
||||
}
|
||||
|
||||
|
||||
bool PIIOString::openDevice() {
|
||||
pos = 0;
|
||||
return (str != 0);
|
||||
}
|
||||
86
src/io/piiostring.h
Normal file
86
src/io/piiostring.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/*! \file piiostring.h
|
||||
* \brief PIIODevice wrapper around PIString
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
PIIODevice wrapper around PIString
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIIOSTRING_H
|
||||
#define PIIOSTRING_H
|
||||
|
||||
#include "piiodevice.h"
|
||||
|
||||
|
||||
class PIP_EXPORT PIIOString: public PIIODevice
|
||||
{
|
||||
PIIODEVICE(PIIOString)
|
||||
public:
|
||||
|
||||
//! Contructs %PIIOString with \"string\" content and \"mode\" open mode
|
||||
PIIOString(PIString * string = 0, PIIODevice::DeviceMode mode = PIIODevice::ReadWrite);
|
||||
|
||||
//! Contructs %PIIOString with \"string\" content only for read
|
||||
PIIOString(const PIString & string);
|
||||
|
||||
~PIIOString();
|
||||
|
||||
|
||||
//! Returns content
|
||||
PIString * string() const {return str;}
|
||||
|
||||
//! Clear content string
|
||||
void clear() {if (str) str->clear(); pos = 0;}
|
||||
|
||||
//! Open \"string\" content with \"mode\" open mode
|
||||
bool open(PIString * string, PIIODevice::DeviceMode mode = PIIODevice::ReadWrite);
|
||||
|
||||
//! Open \"string\" content only for read
|
||||
bool open(const PIString & string);
|
||||
|
||||
//! Returns if position is at the end of content
|
||||
bool isEnd() const {if (!str) return true; return pos >= str->size_s();}
|
||||
|
||||
|
||||
//! Move read/write position to \"position\"
|
||||
void seek(llong position) {pos = position;}
|
||||
|
||||
//! Move read/write position to the begin of the string
|
||||
void seekToBegin() {if (str) pos = 0;}
|
||||
|
||||
//! Move read/write position to the end of the string
|
||||
void seekToEnd() {if (str) pos = str->size_s();}
|
||||
|
||||
|
||||
//! Read one text line and return it
|
||||
PIString readLine();
|
||||
|
||||
int read(void * read_to, int max_size);
|
||||
int write(const void * data, int max_size);
|
||||
|
||||
//! Insert string \"string\" into content at current position
|
||||
int writeString(const PIString & string);
|
||||
|
||||
protected:
|
||||
bool openDevice();
|
||||
|
||||
ssize_t pos;
|
||||
PIString * str;
|
||||
|
||||
};
|
||||
|
||||
#endif // PIIOSTRING_H
|
||||
21
src/io/pimultiprotocol.cpp
Executable file
21
src/io/pimultiprotocol.cpp
Executable file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Multiprotocol
|
||||
Copyright (C) 2012 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pimultiprotocol.h"
|
||||
|
||||
93
src/io/pimultiprotocol.h
Executable file
93
src/io/pimultiprotocol.h
Executable file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Multiprotocol
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIMULTIPROTOCOL_H
|
||||
#define PIMULTIPROTOCOL_H
|
||||
|
||||
#include "piprotocol.h"
|
||||
|
||||
class PIMultiProtocol: public PIMultiProtocolBase
|
||||
{
|
||||
public:
|
||||
PIMultiProtocol() {;}
|
||||
virtual ~PIMultiProtocol() {clear();}
|
||||
|
||||
void addProtocol(PIProtocol & prot) {prots.push_back(&prot); prot.setMultiProtocolOwner(this); prot.new_mp_prot = false;}
|
||||
void addProtocol(PIProtocol * prot) {prots.push_back(prot); prot->setMultiProtocolOwner(this); prot->new_mp_prot = false;}
|
||||
void addProtocol(const PIString & config, const PIString & name, void * recHeaderPtr = 0, int recHeaderSize = 0,
|
||||
void * recDataPtr = 0, int recDataSize = 0, void * sendDataPtr = 0, int sendDataSize = 0) {;
|
||||
prots.push_back(new PIProtocol(config, name, recHeaderPtr, recHeaderSize, recDataPtr, recDataSize, sendDataPtr, sendDataSize));
|
||||
prots.back()->setMultiProtocolOwner(this);
|
||||
prots.back()->new_mp_prot = true;
|
||||
}
|
||||
PIProtocol * protocol(const PIString & name) {piForeach (PIProtocol * i, prots) if (i->name() == name) return i; return 0;}
|
||||
PIProtocol * protocol(const int index) {return prots[index];}
|
||||
PIProtocol * operator [](const int index) {return prots[index];}
|
||||
|
||||
void startSend() {piForeach (PIProtocol * i, prots) i->startSend();}
|
||||
void startReceive() {piForeach (PIProtocol * i, prots) i->startReceive();}
|
||||
void start() {piForeach (PIProtocol * i, prots) i->start();}
|
||||
|
||||
void stopSend() {piForeach (PIProtocol * i, prots) i->stopSend();}
|
||||
void stopReceive() {piForeach (PIProtocol * i, prots) i->stopReceive();}
|
||||
void stop() {piForeach (PIProtocol * i, prots) i->stop();}
|
||||
|
||||
PIProtocol::Quality worseQuality() const {PIProtocol::Quality cq = PIProtocol::Good; piForeachC (PIProtocol * i, prots) if (cq > i->quality()) cq = i->quality(); return cq;}
|
||||
PIProtocol::Quality bestQuality() const {PIProtocol::Quality cq = PIProtocol::Unknown; piForeachC (PIProtocol * i, prots) if (cq < i->quality()) cq = i->quality(); return cq;}
|
||||
|
||||
int count() const {return prots.size_s();}
|
||||
void clear() {stop(); piForeach (PIProtocol * i, prots) if (i->new_mp_prot) delete i; prots.clear();}
|
||||
|
||||
private:
|
||||
PIVector<PIProtocol * > prots;
|
||||
|
||||
};
|
||||
|
||||
class PIRepeater: public PIMultiProtocol {
|
||||
public:
|
||||
PIRepeater(const PIString & config, const PIString & name_) {
|
||||
PIConfig conf(config, PIIODevice::ReadOnly);
|
||||
if (!conf.isOpened()) {
|
||||
piCoutObj << "[PIRepeater \"" << name_ << "\"] Can`t open \"" << config << "\"!";
|
||||
return;
|
||||
}
|
||||
PIConfig::Entry & b(conf.getValue(name_));
|
||||
if (b.childCount() != 2) {
|
||||
piCoutObj << "[PIRepeater \"" << name_ << "\"] \"" << config << "\" should consist 2 nodes!";
|
||||
return;
|
||||
}
|
||||
addProtocol(config, b.child(0)->fullName());
|
||||
addProtocol(config, b.child(1)->fullName());
|
||||
start();
|
||||
}
|
||||
|
||||
PIString firstChannelName() {if (count() == 2) return protocol(0)->receiverDeviceName() + " -> " + protocol(1)->senderDeviceName(); return "Config error";}
|
||||
PIString secondChannelName() {if (count() == 2) return protocol(1)->receiverDeviceName() + " -> " + protocol(0)->senderDeviceName(); return "Config error";}
|
||||
|
||||
ullong receiveCount() {if (count() == 2) return protocol(0)->receiveCount(); return 0;}
|
||||
const ullong * receiveCount_ptr() {if (count() == 2) return protocol(0)->receiveCount_ptr(); return 0;}
|
||||
ullong sendCount() {if (count() == 2) return protocol(0)->sendCount(); return 0;}
|
||||
const ullong * sendCount_ptr() {if (count() == 2) return protocol(0)->sendCount_ptr(); return 0;}
|
||||
|
||||
private:
|
||||
void received(PIProtocol * prot, bool , uchar * data, int size) {if (prot == protocol(0)) protocol(1)->send(data, size); else protocol(0)->send(data, size);}
|
||||
|
||||
};
|
||||
|
||||
#endif // PIMULTIPROTOCOL_H
|
||||
301
src/io/pipacketextractor.cpp
Executable file
301
src/io/pipacketextractor.cpp
Executable file
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Packets extractor
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pipacketextractor.h"
|
||||
|
||||
|
||||
/** \class PIPacketExtractor
|
||||
* \brief Packets extractor
|
||||
* \details
|
||||
* \section PIPacketExtractor_main Synopsis
|
||||
* This class implements packet recognition by various algorithms and custom
|
||||
* validating from data stream. Stream is formed from child %PIIODevice
|
||||
* passed from contructor or with function \a setDevice().
|
||||
*
|
||||
* \section PIPacketExtractor_work Principle of work
|
||||
* %PIPacketExtractor works with child %PIIODevice. \a read and \a write
|
||||
* functions directly call child device functions. You should start threaded
|
||||
* read of \b extractor (not child device) to proper work. Extractor read data
|
||||
* from child device, try to detect packet from readed data and raise
|
||||
* \a packetReceived() event on success.
|
||||
*
|
||||
* \section PIPacketExtractor_algorithms Algorithms
|
||||
* There are 6 algorithms: \n
|
||||
* * PIPacketExtractor::None \n
|
||||
* Packet is successfully received on every read without any validation. \n \n
|
||||
* * PIPacketExtractor::Header \n
|
||||
* Wait for at least \a header() bytes + \a payloadSize(), then validate
|
||||
* header with virtual function \a validateHeader() and if it fail, shifts
|
||||
* for next 1 byte. If header is successfully validated check payload with
|
||||
* function \a validatePayload() and if it fail, shifts for next 1 byte. If
|
||||
* all validations were successful raise \a packetReceived() event. \n \n
|
||||
* * PIPacketExtractor::Footer \n
|
||||
* This algorithm similar to previous, but instead of \a header() first validate
|
||||
* \a footer() at after \a payloadSize() bytes with function \a validateFooter(). \n \n
|
||||
* * PIPacketExtractor::HeaderAndFooter \n
|
||||
* Wait for at least \a header() bytes + \a footer() bytes, then validate
|
||||
* header with virtual function \a validateHeader() and if it fail, shifts
|
||||
* for next 1 byte. If header is successfully validated check footer with
|
||||
* function \a validateFooter() and if it fail, shifts footer position for
|
||||
* next 1 byte. Then validate payload and if it fail, search header again,
|
||||
* starts from next byte of previous header. If all validations were successful
|
||||
* raise \a packetReceived() event. \n \n
|
||||
* * PIPacketExtractor::Size \n
|
||||
* Wait for at least \a packetSize() bytes, then validate packet with function
|
||||
* \a validatePayload() and if it fail, shifts for next 1 byte. If validating
|
||||
* was successfull raise \a packetReceived() event. \n \n
|
||||
* * PIPacketExtractor::Timeout \n
|
||||
* Wait for first read, then read for \a timeout() milliseconds and raise
|
||||
* \a packetReceived() event. \n
|
||||
*
|
||||
* \section PIPacketExtractor_control Control validating
|
||||
* There are three parameters:
|
||||
* * header content
|
||||
* * header size
|
||||
* * payload size
|
||||
*
|
||||
* Extractor can detect packet with compare your header with readed data.
|
||||
* It is default implementation of function \a packetHeaderValidate().
|
||||
* If header validating passed, function \a packetValidate() will be called.
|
||||
* If either of this function return \b false extractor shifts by one byte
|
||||
* and takes next header. If both functions returns \b true extractor shifts
|
||||
* by whole packet size.
|
||||
* \image html packet_detection.png
|
||||
*
|
||||
* */
|
||||
|
||||
REGISTER_DEVICE(PIPacketExtractor);
|
||||
|
||||
|
||||
PIPacketExtractor::PIPacketExtractor(PIIODevice * device_, PIPacketExtractor::SplitMode mode) {
|
||||
init_();
|
||||
setDevice(device_);
|
||||
setSplitMode(mode);
|
||||
}
|
||||
|
||||
|
||||
void PIPacketExtractor::init_() {
|
||||
ret_func_header = ret_func_footer = 0;
|
||||
setPayloadSize(0);
|
||||
setTimeout(100);
|
||||
setThreadedReadBufferSize(65536);
|
||||
setBufferSize(65536);
|
||||
setDevice(0);
|
||||
setPacketSize(0);
|
||||
setSplitMode(None);
|
||||
allReaded = addSize = curInd = missed = missed_packets = footerInd = 0;
|
||||
header_found = false;
|
||||
}
|
||||
|
||||
|
||||
void PIPacketExtractor::propertyChanged(const PIString &) {
|
||||
packetSize_ = property("packetSize").toInt();
|
||||
mode_ = (SplitMode)(property("splitMode").toInt());
|
||||
dataSize = property("payloadSize").toInt();
|
||||
src_header = property("header").toByteArray();
|
||||
src_footer = property("footer").toByteArray();
|
||||
packetSize_hf = src_header.size_s() + src_footer.size_s() + payloadSize();
|
||||
}
|
||||
|
||||
|
||||
void PIPacketExtractor::setDevice(PIIODevice * device_) {
|
||||
dev = device_;
|
||||
if (dev == 0) return;
|
||||
}
|
||||
|
||||
|
||||
void PIPacketExtractor::setPayloadSize(int size) {
|
||||
setProperty("payloadSize", size);
|
||||
dataSize = size;
|
||||
packetSize_hf = src_header.size_s() + src_footer.size_s() + payloadSize();
|
||||
}
|
||||
|
||||
|
||||
void PIPacketExtractor::setHeader(const PIByteArray & data) {
|
||||
setProperty("header", data);
|
||||
src_header = data;
|
||||
packetSize_hf = src_header.size_s() + src_footer.size_s() + payloadSize();
|
||||
}
|
||||
|
||||
|
||||
void PIPacketExtractor::setFooter(const PIByteArray & data) {
|
||||
setProperty("footer", data);
|
||||
src_footer = data;
|
||||
packetSize_hf = src_header.size_s() + src_footer.size_s() + payloadSize();
|
||||
}
|
||||
|
||||
|
||||
bool PIPacketExtractor::threadedRead(uchar * readed, int size_) {
|
||||
//piCoutObj << "readed" << size_;
|
||||
int ss;
|
||||
switch (mode_) {
|
||||
case PIPacketExtractor::None:
|
||||
if (validatePayload(readed, size_))
|
||||
packetReceived(readed, size_);
|
||||
break;
|
||||
case PIPacketExtractor::Header:
|
||||
tmpbuf.append(readed, size_);
|
||||
ss = src_header.size_s() + dataSize;
|
||||
while (tmpbuf.size_s() >= ss) {
|
||||
while (!validateHeader(src_header.data(), tmpbuf.data(), src_header.size_s())) {
|
||||
tmpbuf.pop_front();
|
||||
++missed;
|
||||
if (tmpbuf.size_s() < ss) return true;
|
||||
}
|
||||
while (!validatePayload(tmpbuf.data(src_header.size_s()), dataSize)) {
|
||||
tmpbuf.pop_front();
|
||||
++missed;
|
||||
if (tmpbuf.size_s() < ss) return true;
|
||||
}
|
||||
packetReceived(tmpbuf.data(), ss);
|
||||
tmpbuf.remove(0, ss);
|
||||
}
|
||||
break;
|
||||
case PIPacketExtractor::Footer:
|
||||
/*memcpy(buffer.data(allReaded), readed, size_);
|
||||
allReaded += size_;
|
||||
footer_ = (mode_ == PIPacketExtractor::Footer);
|
||||
while (allReaded >= packetSize_hf + addSize && allReaded > 0) {
|
||||
if (!src_header.isEmpty()) {
|
||||
if (allReaded + curInd >= buffer_size) {
|
||||
memcpy(sbuffer.data(), buffer.data(), buffer_size);
|
||||
memcpy(buffer.data(), sbuffer.data(buffer_size - packetSize_hf), allReaded);
|
||||
allReaded = packetSize_hf;
|
||||
addSize = curInd = 0;
|
||||
}
|
||||
bool brk = false;
|
||||
while (!validateHeader((uchar * )(footer_ ? src_footer.data() : src_header.data()), buffer.data(curInd + (footer_ ? dataSize : 0)), footer_ ? src_footer.size_s() : src_header.size_s())) {
|
||||
++curInd; ++missed;
|
||||
if (packetSize_hf > 0) missed_packets = missed / packetSize_hf;
|
||||
if (curInd > addSize) {
|
||||
addSize += packetSize_hf;
|
||||
brk = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (brk) continue;
|
||||
//memcpy(mheader.data(), buffer.data(curInd + (footer_ ? dataSize : 0)), src_header.size_s());
|
||||
if (!src_header.isEmpty()) memcpy(src_header.data(), buffer.data(curInd), src_header.size_s());
|
||||
if (!validatePayload(buffer.data(curInd + src_header.size_s()), dataSize)) {
|
||||
++curInd; ++missed;
|
||||
if (packetSize_hf > 0) missed_packets = missed / packetSize_hf;
|
||||
continue;
|
||||
}
|
||||
packetReceived(buffer.data(curInd), packetSize_hf);
|
||||
memcpy(sbuffer.data(), buffer.data(), allReaded);
|
||||
memcpy(buffer.data(), sbuffer.data(packetSize_hf + curInd), allReaded);
|
||||
allReaded -= packetSize_hf + curInd;
|
||||
curInd = addSize = 0;
|
||||
} else {
|
||||
if (dataSize == 0) {
|
||||
if (validatePayload(buffer.data(), size_))
|
||||
packetReceived(buffer.data(), size_);
|
||||
memcpy(sbuffer.data(), buffer.data(), allReaded);
|
||||
memcpy(buffer.data(), sbuffer.data(size_), allReaded);
|
||||
allReaded -= size_;
|
||||
} else {
|
||||
if (validatePayload(buffer.data(), dataSize))
|
||||
packetReceived(buffer.data(), dataSize);
|
||||
memcpy(sbuffer.data(), buffer.data(), allReaded);
|
||||
memcpy(buffer.data(), sbuffer.data(packetSize_hf), allReaded);
|
||||
allReaded -= packetSize_hf;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
tmpbuf.append(readed, size_);
|
||||
ss = src_footer.size_s() + dataSize;
|
||||
while (tmpbuf.size_s() >= ss) {
|
||||
while (!validateFooter(src_footer.data(), tmpbuf.data(dataSize), src_footer.size_s())) {
|
||||
tmpbuf.pop_front();
|
||||
++missed;
|
||||
if (tmpbuf.size_s() < ss) return true;
|
||||
}
|
||||
while (!validatePayload(tmpbuf.data(), dataSize)) {
|
||||
tmpbuf.pop_front();
|
||||
++missed;
|
||||
if (tmpbuf.size_s() < ss) return true;
|
||||
}
|
||||
packetReceived(tmpbuf.data(), ss);
|
||||
tmpbuf.remove(0, ss);
|
||||
}
|
||||
break;
|
||||
case PIPacketExtractor::HeaderAndFooter:
|
||||
tmpbuf.append(readed, size_);
|
||||
ss = src_header.size_s() + src_footer.size_s();
|
||||
while (tmpbuf.size_s() >= ss) {
|
||||
if (!header_found) {
|
||||
if (tmpbuf.size_s() < ss) return true;
|
||||
while (!validateHeader(src_header.data(), tmpbuf.data(), src_header.size_s())) {
|
||||
tmpbuf.pop_front();
|
||||
++missed;
|
||||
if (tmpbuf.size_s() < ss) return true;
|
||||
}
|
||||
header_found = true;
|
||||
footerInd = src_header.size_s();
|
||||
} else {
|
||||
if (tmpbuf.size_s() < footerInd + src_footer.size_s()) return true;
|
||||
while (!validateFooter(src_footer.data(), tmpbuf.data(footerInd), src_footer.size_s())) {
|
||||
++footerInd;
|
||||
if (tmpbuf.size_s() < footerInd + src_footer.size_s()) return true;
|
||||
}
|
||||
//piCout << "footer found at" << footerInd;
|
||||
header_found = false;
|
||||
if (!validatePayload(tmpbuf.data(src_header.size_s()), footerInd - src_header.size_s())) {
|
||||
tmpbuf.pop_front();
|
||||
++missed;
|
||||
continue;
|
||||
}
|
||||
packetReceived(tmpbuf.data(), footerInd + src_footer.size_s());
|
||||
tmpbuf.remove(0, footerInd + src_footer.size_s());
|
||||
footerInd = src_header.size_s();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PIPacketExtractor::Size:
|
||||
tmpbuf.append(readed, size_);
|
||||
if (packetSize_ <= 0) {
|
||||
tmpbuf.clear();
|
||||
return true;
|
||||
}
|
||||
while (tmpbuf.size_s() >= packetSize_) {
|
||||
if (!validatePayload(tmpbuf.data(), packetSize_)) {
|
||||
tmpbuf.pop_front();
|
||||
++missed;
|
||||
missed_packets = missed / packetSize_;
|
||||
continue;
|
||||
}
|
||||
packetReceived(tmpbuf.data(), packetSize_);
|
||||
tmpbuf.remove(0, packetSize_);
|
||||
}
|
||||
break;
|
||||
case PIPacketExtractor::Timeout:
|
||||
memcpy(buffer.data(), readed, size_);
|
||||
trbuf = dev->readForTime(time_);
|
||||
memcpy(buffer.data(size_), trbuf.data(), trbuf.size());
|
||||
if (size_ + trbuf.size() > 0)
|
||||
packetReceived(buffer.data(), size_ + trbuf.size());
|
||||
break;
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
PIString PIPacketExtractor::constructFullPath() const {
|
||||
return fullPathPrefix() + "://";
|
||||
}
|
||||
187
src/io/pipacketextractor.h
Executable file
187
src/io/pipacketextractor.h
Executable file
@@ -0,0 +1,187 @@
|
||||
/*! \file pipacketextractor.h
|
||||
* \brief Packets extractor
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Packets extractor
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PIPACKETEXTRACTOR_H
|
||||
#define PIPACKETEXTRACTOR_H
|
||||
|
||||
#include "piiodevice.h"
|
||||
|
||||
// Pass data, recHeaderPtr, received_data, recHeaderSize. Return true if packet is correct nor return false.
|
||||
typedef bool (*PacketExtractorCheckFunc)(void * , uchar * , uchar * , int );
|
||||
|
||||
class PIP_EXPORT PIPacketExtractor: public PIIODevice
|
||||
{
|
||||
PIIODEVICE(PIPacketExtractor)
|
||||
friend class PIConnection;
|
||||
public:
|
||||
|
||||
//! Extract algorithms
|
||||
enum SplitMode {
|
||||
None /** No data processing */ ,
|
||||
Header /** Detect packets with \a header() and following \a payloadSize() */ ,
|
||||
Footer /** Detect packets with \a footer() and leading \a payloadSize() */ ,
|
||||
HeaderAndFooter /** Detect packets with \a header() and \a footer() without \a payloadSize() */ ,
|
||||
Size /** Detect packets with \a packetSize() */ ,
|
||||
Timeout /** Wait for first read, then read for \a timeout() milliseconds */
|
||||
};
|
||||
|
||||
//! Contructs extractor with child device "device_" and extract algorithm "mode"
|
||||
PIPacketExtractor(PIIODevice * device_ = 0, SplitMode mode = None);
|
||||
|
||||
virtual ~PIPacketExtractor() {stop();}
|
||||
|
||||
|
||||
//! Returns child %device
|
||||
PIIODevice * device() {return dev;}
|
||||
|
||||
//! Set child %device to "device_"
|
||||
void setDevice(PIIODevice * device_);
|
||||
|
||||
|
||||
//! Returns buffer size
|
||||
int bufferSize() const {return buffer_size;}
|
||||
|
||||
//! Set buffer size to "new_size" bytes, should be at least greater than whole packet size
|
||||
void setBufferSize(int new_size) {buffer_size = new_size; buffer.resize(buffer_size); sbuffer.resize(buffer_size); memset(buffer.data(), 0, buffer.size()); memset(sbuffer.data(), 0, sbuffer.size());}
|
||||
|
||||
void setHeaderCheckSlot(PacketExtractorCheckFunc f) {ret_func_header = f;}
|
||||
void setFooterCheckSlot(PacketExtractorCheckFunc f) {ret_func_footer = f;}
|
||||
void setPayloadCheckSlot(ReadRetFunc f) {ret_func_ = f;}
|
||||
|
||||
|
||||
//! Set extract algorithm
|
||||
void setSplitMode(SplitMode mode) {setProperty("splitMode", int(mode)); mode_ = mode;}
|
||||
|
||||
//! Set payload size, used for PIPacketExtractor::Header and PIPacketExtractor::Footer algorithms
|
||||
void setPayloadSize(int size);
|
||||
|
||||
//! Set header data, used for PIPacketExtractor::Header and PIPacketExtractor::HeaderAndFooter algorithms
|
||||
void setHeader(const PIByteArray & data);
|
||||
|
||||
//! Set footer data, used for PIPacketExtractor::Footer and PIPacketExtractor::HeaderAndFooter algorithms
|
||||
void setFooter(const PIByteArray & data);
|
||||
|
||||
//! Set packet size, used for PIPacketExtractor::Size algorithm
|
||||
void setPacketSize(int size) {setProperty("packetSize", size); packetSize_ = size;}
|
||||
|
||||
//! Set timeout in milliseconds, used for PIPacketExtractor::Timeout algorithm
|
||||
void setTimeout(double msecs) {setProperty("timeout", msecs); time_ = msecs;}
|
||||
|
||||
|
||||
//! Returns current extract algorithm
|
||||
SplitMode splitMode() const {return (SplitMode)(property("splitMode").toInt());}
|
||||
|
||||
//! Returns current payload size, used for PIPacketExtractor::Header and PIPacketExtractor::Footer algorithms
|
||||
int payloadSize() const {return property("payloadSize").toInt();}
|
||||
|
||||
//! Returns current header data, used for PIPacketExtractor::Header and PIPacketExtractor::HeaderAndFooter algorithms
|
||||
PIByteArray header() const {return src_header;}
|
||||
|
||||
//! Returns current footer data, used for PIPacketExtractor::Footer and PIPacketExtractor::HeaderAndFooter algorithms
|
||||
PIByteArray footer() const {return src_footer;}
|
||||
|
||||
//! Returns current packet size, used for PIPacketExtractor::Size algorithm
|
||||
int packetSize() const {return property("packetSize").toInt();}
|
||||
|
||||
//! Returns current timeout in milliseconds, used for PIPacketExtractor::Timeout algorithm
|
||||
double timeout() const {return property("timeout").toDouble();}
|
||||
|
||||
|
||||
//! Returns missed by validating functions bytes count
|
||||
ullong missedBytes() const {return missed;}
|
||||
|
||||
// //! Returns missed by validating functions packets count, = missedBytes() / packetSize
|
||||
ullong missedPackets() const {/*if (packetSize_hf == 0) return missed; return missed / packetSize_hf*/; return missed_packets;}
|
||||
|
||||
//! Returns pointer to \a missedBytes() count. Useful for output to PIConsole
|
||||
const ullong * missedBytes_ptr() const {return &missed;}
|
||||
|
||||
// //! Returns pointer to \a missedPackets() count. Useful for output to PIConsole
|
||||
const ullong * missedPackets_ptr() const {return &missed_packets;}
|
||||
|
||||
|
||||
// //! Returns last successfully validated header as byte array
|
||||
PIByteArray lastHeader() {return mheader;}
|
||||
|
||||
|
||||
//! Directly call \a read() function of child %device
|
||||
int read(void * read_to, int max_size) {if (dev == 0) return -1; return dev->read(read_to, max_size);}
|
||||
|
||||
//! Directly call \a write() function of child %device
|
||||
int write(const void * data, int max_size) {if (dev == 0) return -1; return dev->write(data, max_size);}
|
||||
|
||||
PIString constructFullPath() const;
|
||||
|
||||
EVENT2(packetReceived, uchar * , data, int, size)
|
||||
|
||||
//! \events
|
||||
//! \{
|
||||
|
||||
//! \fn void packetReceived(uchar * data, int size)
|
||||
//! \brief Raise on successfull \a packetValidate() function
|
||||
|
||||
//! \}
|
||||
|
||||
protected:
|
||||
|
||||
/** \brief Function to validate header
|
||||
* \param src Your header content
|
||||
* \param rec Received header
|
||||
* \param size Header size
|
||||
* \details Default implementation returns by-byte "src" with "rec" compare result */
|
||||
virtual bool validateHeader(uchar * src, uchar * rec, int size) {if (ret_func_header != 0) return ret_func_header(ret_data_, src, rec, size); for (int i = 0; i < size; ++i) if (src[i] != rec[i]) return false; return true;}
|
||||
|
||||
/** \brief Function to validate footer
|
||||
* \param src Your footer content
|
||||
* \param rec Received footer
|
||||
* \param size Footer size
|
||||
* \details Default implementation returns by-byte "src" with "rec" compare result */
|
||||
virtual bool validateFooter(uchar * src, uchar * rec, int size) {if (ret_func_footer != 0) return ret_func_footer(ret_data_, src, rec, size); for (int i = 0; i < size; ++i) if (src[i] != rec[i]) return false; return true;}
|
||||
|
||||
/** \brief Function to validate payload
|
||||
* \param rec Received payload
|
||||
* \param size payload size
|
||||
* \details Default implementation returns \b true */
|
||||
virtual bool validatePayload(uchar * rec, int size) {if (ret_func_ != 0) return ret_func_(ret_data_, rec, size); return true;}
|
||||
|
||||
private:
|
||||
void init_();
|
||||
void propertyChanged(const PIString & );
|
||||
bool threadedRead(uchar * readed, int size);
|
||||
PIString fullPathPrefix() const {return "pckext";}
|
||||
bool openDevice() {if (dev == 0) return false; return dev->open();}
|
||||
|
||||
PIIODevice * dev;
|
||||
PIByteArray mheader, buffer, sbuffer, tmpbuf, src_header, src_footer, trbuf;
|
||||
PacketExtractorCheckFunc ret_func_header, ret_func_footer;
|
||||
SplitMode mode_;
|
||||
void * data;
|
||||
int buffer_size, dataSize, packetSize_hf, allReaded, addSize, curInd, footerInd, packetSize_;
|
||||
double time_;
|
||||
bool header_found;
|
||||
ullong missed, missed_packets;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // PIPACKETEXTRACTOR_H
|
||||
632
src/io/pipeer.cpp
Executable file
632
src/io/pipeer.cpp
Executable file
@@ -0,0 +1,632 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Peer - named I/O ethernet node
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pipeer.h"
|
||||
|
||||
#define _PIPEER_MSG_SIZE 8192
|
||||
#define _PIPEER_MULTICAST_TTL 4
|
||||
#define _PIPEER_MULTICAST_IP "232.13.3.12"
|
||||
#define _PIPEER_LOOPBACK_PORT_S 13313
|
||||
#define _PIPEER_LOOPBACK_PORT_E (13313+32)
|
||||
#define _PIPEER_MULTICAST_PORT 13360
|
||||
#define _PIPEER_BROADCAST_PORT 13361
|
||||
#define _PIPEER_TRAFFIC_PORT_S 13400
|
||||
#define _PIPEER_TRAFFIC_PORT_E 14000
|
||||
|
||||
|
||||
PIPeer::PeerInfo::Address::Address(const PIString & a, const PIString & m): address(a), netmask(m) {
|
||||
ping = -1.;
|
||||
wait_ping = false;
|
||||
last_ping = PISystemTime::current(true);
|
||||
}
|
||||
|
||||
|
||||
int PIPeer::PeerInfo::ping() const {
|
||||
int ret = -1;
|
||||
piForeachC (Address & a, addresses)
|
||||
if (a.ping > 0.)
|
||||
ret = piMaxi(ret, piRoundd(a.ping));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIPeer::PIPeer(const PIString & name_): PIObject() {
|
||||
setName(name_);
|
||||
self_info.name = name_;
|
||||
self_info.dist = 0;
|
||||
self_info.time = PISystemTime::current();
|
||||
//joinMulticastGroup("239.240.241.242");
|
||||
srand(uint(PISystemTime::current(true).toMicroseconds()));
|
||||
//id_ = self_info.name + "_" + PIString::fromNumber(rand());
|
||||
CONNECTU(&timer, tickEvent, this, timerEvent);
|
||||
PIStringList sl = PIEthernet::allAddresses();
|
||||
initEths(sl);
|
||||
sl.removeAll("127.0.0.1");
|
||||
initMBcasts(sl);
|
||||
sendSelfInfo();
|
||||
timer.addDelimiter(5);
|
||||
timer.start(200);
|
||||
}
|
||||
|
||||
|
||||
PIPeer::~PIPeer() {
|
||||
piForeach (PIEthernet * i, eths_traffic) {
|
||||
i->stopThreadedRead();
|
||||
delete i;
|
||||
}
|
||||
eths_traffic.clear();
|
||||
piForeach (PIEthernet * i, eths_mcast)
|
||||
i->stopThreadedRead();
|
||||
piForeach (PIEthernet * i, eths_bcast)
|
||||
i->stopThreadedRead();
|
||||
eth_send.stopThreadedRead();
|
||||
eth_lo.stopThreadedRead();
|
||||
sendSelfRemove();
|
||||
destroyMBcasts();
|
||||
}
|
||||
|
||||
|
||||
void PIPeer::timerEvent(void * data, int delim) {
|
||||
switch (delim) {
|
||||
case 5: // 1 s
|
||||
syncPeers();
|
||||
break;
|
||||
}
|
||||
//send("broadcast", 9);
|
||||
}
|
||||
|
||||
|
||||
void PIPeer::initEths(PIStringList al) {
|
||||
PIEthernet * ce;
|
||||
PIEthernet::InterfaceList il = PIEthernet::interfaces();
|
||||
const PIEthernet::Interface * cint = 0;
|
||||
piForeachC (PIString & a, al) {
|
||||
ce = new PIEthernet();
|
||||
ce->setDebug(false);
|
||||
ce->setName("__S__PIPeer_traffic_eth_rec_" + a);
|
||||
ce->setParameters(0);
|
||||
ce->setThreadSafe(true);
|
||||
bool ok = false;
|
||||
for (int p = _PIPEER_TRAFFIC_PORT_S; p < _PIPEER_TRAFFIC_PORT_E; ++p) {
|
||||
ce->setReadAddress(a, p);
|
||||
if (ce->open()) {
|
||||
eths_traffic << ce;
|
||||
cint = il.getByAddress(a);
|
||||
self_info.addresses << PeerInfo::Address(ce->path(), cint == 0 ? "255.255.255.0" : cint->netmask);
|
||||
CONNECTU(ce, threadedReadEvent, this, dataRead);
|
||||
ce->startThreadedRead();
|
||||
//piCout << "dc binded to" << ce->path();
|
||||
//piCout << "add eth" << ta;
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ok) delete ce;
|
||||
}
|
||||
eth_send.setDebug(false);
|
||||
eth_send.setName("__S__PIPeer_traffic_eth_send");
|
||||
eth_send.setParameters(0);
|
||||
}
|
||||
|
||||
|
||||
void PIPeer::initMBcasts(PIStringList al) {
|
||||
destroyMBcasts();
|
||||
PIEthernet * ce;
|
||||
PIEthernet::InterfaceList il = PIEthernet::interfaces();
|
||||
const PIEthernet::Interface * cint;
|
||||
PIString nm;
|
||||
al << _PIPEER_MULTICAST_IP;
|
||||
piForeachC (PIString & a, al) {
|
||||
piCout << "mcast try" << a;
|
||||
ce = new PIEthernet();
|
||||
ce->setDebug(false);
|
||||
ce->setName("__S__PIPeer_mcast_eth_" + a);
|
||||
ce->setParameters(0);
|
||||
ce->setSendAddress(_PIPEER_MULTICAST_IP, _PIPEER_MULTICAST_PORT);
|
||||
ce->setReadAddress(a, _PIPEER_MULTICAST_PORT);
|
||||
ce->setMulticastTTL(_PIPEER_MULTICAST_TTL);
|
||||
ce->joinMulticastGroup(_PIPEER_MULTICAST_IP);
|
||||
eths_mcast << ce;
|
||||
CONNECTU(ce, threadedReadEvent, this, mbcastRead);
|
||||
ce->startThreadedRead();
|
||||
}
|
||||
piForeachC (PIString & a, al) {
|
||||
ce = new PIEthernet();
|
||||
ce->setDebug(false);
|
||||
ce->setName("__S__PIPeer_bcast_eth_" + a);
|
||||
ce->setParameters(PIEthernet::Broadcast);
|
||||
cint = il.getByAddress(a);
|
||||
nm = (cint == 0) ? "255.255.255.0" : cint->netmask;
|
||||
ce->setSendAddress(PIEthernet::getBroadcast(a, nm), _PIPEER_BROADCAST_PORT);
|
||||
ce->setReadAddress(a, _PIPEER_BROADCAST_PORT);
|
||||
//piCout << "mc BC try" << a << nm << ce->sendIP();
|
||||
piCout << "bcast try" << a << nm;
|
||||
eths_bcast << ce;
|
||||
CONNECTU(ce, threadedReadEvent, this, mbcastRead);
|
||||
ce->startThreadedRead();
|
||||
}
|
||||
eth_lo.setDebug(false);
|
||||
eth_lo.setName("__S__PIPeer_eth_loopback");
|
||||
eth_lo.setParameters(0);
|
||||
cint = il.getByAddress("127.0.0.1");
|
||||
for (int p = _PIPEER_LOOPBACK_PORT_S; p <= _PIPEER_LOOPBACK_PORT_E; ++p) {
|
||||
eth_lo.setReadAddress("127.0.0.1", p);
|
||||
if (eth_lo.open()) {
|
||||
eth_lo.setSendIP("127.0.0.1");
|
||||
CONNECTU(ð_lo, threadedReadEvent, this, mbcastRead);
|
||||
eth_lo.startThreadedRead();
|
||||
piCout << "lo binded to" << eth_lo.readAddress();
|
||||
//piCout << "add eth" << ta;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (eths_mcast.isEmpty()) piCoutObj << "Warning! Can`t find suitable network interface for multicast receive, check for exists at least one interface with multicasting enabled!";
|
||||
if (eths_bcast.isEmpty()) piCoutObj << "Warning! Can`t find suitable network interface for broadcast receive, check for exists at least one interface with broadcasting enabled!";
|
||||
}
|
||||
|
||||
|
||||
void PIPeer::destroyMBcasts() {
|
||||
piForeach (PIEthernet * i, eths_mcast) {
|
||||
i->leaveMulticastGroup(_PIPEER_MULTICAST_IP);
|
||||
delete i;
|
||||
}
|
||||
piForeach (PIEthernet * i, eths_bcast)
|
||||
delete i;
|
||||
eth_lo.stop();
|
||||
eth_lo.close();
|
||||
eths_mcast.clear();
|
||||
eths_bcast.clear();
|
||||
}
|
||||
|
||||
|
||||
PIPeer::PeerInfo * PIPeer::quickestPeer(const PIString & to) {
|
||||
if (!addresses_map.contains(to)) return 0;
|
||||
//piCout << "*** search quickest peer" << to;
|
||||
PIVector<PeerInfo * > tp = addresses_map[to];
|
||||
PeerInfo * dp = 0;
|
||||
int mping = 0x7FFFFFFF;
|
||||
for (int i = 0; i < tp.size_s(); ++i) {
|
||||
if (mping > tp[i]->ping()) {
|
||||
mping = tp[i]->ping();
|
||||
dp = tp[i];
|
||||
}
|
||||
}
|
||||
//piCout << "*** search quickest peer: found" << dp->name;
|
||||
return dp;
|
||||
}
|
||||
|
||||
|
||||
bool PIPeer::send(const PIString & to, const void * data, int size) {
|
||||
PeerInfo * dp = quickestPeer(to);
|
||||
if (dp == 0) {
|
||||
//piCoutObj << "Can`t find peer \"" << to << "\"!";
|
||||
return false;
|
||||
}
|
||||
PIByteArray ba;
|
||||
ba << int(4) << self_info.name << to << int(0) << size;
|
||||
PIByteArray fmsg(data, size), cmsg;
|
||||
int msg_count = (size - 1) / _PIPEER_MSG_SIZE + 1;
|
||||
//piCout << "[PIPeer] send" << size << "bytes in" << msg_count << "packets ...";
|
||||
for (int i = 0; i < msg_count; ++i) {
|
||||
int csize = (i == msg_count - 1) ? ((size - 1) % _PIPEER_MSG_SIZE + 1) : _PIPEER_MSG_SIZE;
|
||||
cmsg = ba;
|
||||
cmsg << msg_count << i;
|
||||
cmsg.append(fmsg.data(i * _PIPEER_MSG_SIZE), csize);
|
||||
if (!sendToNeighbour(dp, cmsg)) return false;
|
||||
}
|
||||
//piCout << "[PIPeer] send" << size << "bytes ok";
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PIPeer::dataRead(uchar * readed, int size) {
|
||||
if (size < 16) return true;
|
||||
PIByteArray ba(readed, size), sba;
|
||||
int type, cnt, rec_size;
|
||||
PIString from, to;
|
||||
ba >> type;
|
||||
PIMutexLocker locker(eth_mutex);
|
||||
//piCout << "[PIPeer \"" + name_ + "\"] Received packet" << type;
|
||||
if (type == 5) { // ping
|
||||
PIString addr;
|
||||
PISystemTime time, ptime, ctime = PISystemTime::current(true);
|
||||
ba >> to >> from >> addr >> time;
|
||||
if (to == self_info.name) { // ping echo
|
||||
piForeach (PeerInfo & p, peers) {
|
||||
if (!p.isNeighbour()) continue;
|
||||
if (p.name != from) continue;
|
||||
piForeach (PeerInfo::Address & a, p.addresses) {
|
||||
if (a.address != addr) continue;
|
||||
if (a.last_ping >= time) piBreak;
|
||||
ptime = ctime - time;
|
||||
a.last_ping = time;
|
||||
a.wait_ping = false;
|
||||
if (a.ping < 0) a.ping = ptime.toMilliseconds();
|
||||
else a.ping = 0.6 * a.ping + 0.4 * ptime.toMilliseconds();
|
||||
piCout << "*** ping echo" << p.name << a.address << a.ping;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// send ping back
|
||||
piForeachC (PeerInfo & p, peers) {
|
||||
if (!p.isNeighbour()) continue;
|
||||
if (p.name != to) continue;
|
||||
sba = PIByteArray(readed, size);
|
||||
//piCout << "ping from" << to << addr << ", send back to" << p.name;
|
||||
piForeachC (PeerInfo::Address & a, p.addresses) {
|
||||
if (eth_send.send(a.address, sba))
|
||||
diag_s.received(sba.size_s());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//PIEthernet * eth = (PIEthernet*)emitter();
|
||||
//()->send();
|
||||
return true;
|
||||
}
|
||||
if (type != 4) return true;
|
||||
diag_d.received(size);
|
||||
ba >> from >> to >> cnt >> rec_size;
|
||||
//piCout << "[PIPeer \"" + name_ + "\"] Received packet" << /*type << from << to << cnt <<*/ rec_size;
|
||||
if (type == 4) { // data packet
|
||||
if (to == self_info.name) { // my packet
|
||||
int msg_count, cmsg;
|
||||
ba >> msg_count >> cmsg;
|
||||
//piCout << "[PIPeer \"" + name_ + "\"] Received packet" << type << from << to << cnt << rec_size << msg_count << cmsg;
|
||||
if (cmsg == 0 && msg_count == 1) {
|
||||
dataReceived(from, ba);
|
||||
dataReceivedEvent(from, ba);
|
||||
return true;
|
||||
}
|
||||
PeerInfo * fp = const_cast<PeerInfo * >(getPeerByName(from));
|
||||
if (fp == 0) return true;
|
||||
PeerData & pd(fp->_data);
|
||||
if (cmsg == 0) {
|
||||
//piCout << "[PIPeer \"" + name_ + "\"] Packet clear" << rec_size;
|
||||
pd.clear();
|
||||
pd.msg_count = msg_count;
|
||||
}
|
||||
//piCout << "[PIPeer \"" + name_ + "\"] Packet add" << cmsg << ba.size_s();
|
||||
pd.addData(ba);
|
||||
if (pd.isFullReceived()) {
|
||||
dataReceived(from, pd.data);
|
||||
dataReceivedEvent(from, pd.data);
|
||||
//piCout << "[PIPeer \"" + name_ + "\"] Packet received" << pd.data.size_s();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
PeerInfo * dp = quickestPeer(to);
|
||||
if (dp == 0) {
|
||||
//piCoutObj << "Can`t find peer \"" << to << "\"!";
|
||||
return true;
|
||||
}
|
||||
cnt++;
|
||||
if (cnt > 100 || from == dp->name) return true;
|
||||
sba << type << from << to << cnt << rec_size;
|
||||
sba.append(ba);
|
||||
//piCoutObj << "Translate data packet" << type << from << to << cnt << rec_size;
|
||||
sendToNeighbour(dp, sba);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PIPeer::mbcastRead(uchar * data, int size) {
|
||||
if (size < 8) return true;
|
||||
int type, dist;
|
||||
PIByteArray ba(data, size);
|
||||
ba >> type;
|
||||
if (type <= 0 || type >= 4) return true;
|
||||
PeerInfo pi;
|
||||
const PeerInfo * rpi = 0;
|
||||
PIVector<PeerInfo> rpeers;
|
||||
ba >> pi.name;
|
||||
//piCout << "read type" << type << "from" << pi.name;
|
||||
if (pi.name == self_info.name) return true;
|
||||
PIMutexLocker locker(mc_mutex);
|
||||
diag_s.received(size);
|
||||
//piCout << "analyz ...";
|
||||
switch (type) {
|
||||
case 1: // new peer
|
||||
//piCout << "new peer packet ...";
|
||||
if (hasPeer(pi.name)) break;
|
||||
ba >> pi;
|
||||
pi.sync = 0;
|
||||
if (pi.dist == 0) {
|
||||
pi.addNeighbour(self_info.name);
|
||||
self_info.addNeighbour(pi.name);
|
||||
}
|
||||
peers << pi;
|
||||
piCoutObj << "new peer \"" << pi.name << "\"" << " dist " << pi.dist;
|
||||
pi.dist++;
|
||||
sendSelfInfo();
|
||||
sendPeerInfo(pi);
|
||||
findNearestAddresses();
|
||||
peerConnected(pi.name);
|
||||
peerConnectedEvent(pi.name);
|
||||
//piCout << "new peer packet ok";
|
||||
break;
|
||||
case 2: // remove peer
|
||||
//piCout << "remove peer packet ..." << pi.name;
|
||||
rpi = getPeerByName(pi.name);
|
||||
if (!rpi) break;
|
||||
dist = rpi->dist;
|
||||
addToRemoved(*rpi);
|
||||
removePeer(pi.name);
|
||||
piCoutObj << "remove peer \"" << pi.name << "\"";
|
||||
if (dist == 0)
|
||||
self_info.removeNeighbour(pi.name);
|
||||
sendPeerRemove(pi.name);
|
||||
findNearestAddresses();
|
||||
peerDisconnected(pi.name);
|
||||
peerDisconnectedEvent(pi.name);
|
||||
//piCout << "remove peer packet ok";
|
||||
break;
|
||||
case 3: // sync peers
|
||||
//piCout << "sync packet ...";
|
||||
ba >> pi >> rpeers;
|
||||
rpeers << pi;
|
||||
//piCoutObj << "rec sync " << rpeers.size_s() << " peers";
|
||||
piForeach (PeerInfo & rpeer, rpeers) {
|
||||
//piCout << " to sync " << rpeer.name;
|
||||
if (rpeer.name == self_info.name) continue;
|
||||
bool exist = false;
|
||||
piForeach (PeerInfo & peer, peers) {
|
||||
if (peer.name == rpeer.name) exist = true;
|
||||
if (exist && isPeerRecent(peer, rpeer)) {
|
||||
//piCout << "synced " << peer.name;
|
||||
for (int z = 0; z < rpeer.addresses.size_s(); ++z) {
|
||||
PeerInfo::Address & ra(rpeer.addresses[z]);
|
||||
for (int k = 0; k < peer.addresses.size_s(); ++k) {
|
||||
PeerInfo::Address & a(peer.addresses[k]);
|
||||
if (ra.address == a.address) {
|
||||
ra.ping = a.ping;
|
||||
ra.wait_ping = a.wait_ping;
|
||||
ra.last_ping = a.last_ping;
|
||||
piBreak;
|
||||
}
|
||||
}
|
||||
}
|
||||
peer.addresses = rpeer.addresses;
|
||||
peer.cnt = rpeer.cnt;
|
||||
peer.time = rpeer.time;
|
||||
peer.addNeighbours(rpeer.neighbours);
|
||||
rpeer.neighbours = peer.neighbours;
|
||||
if (peer.name == pi.name) peer.sync = 0;
|
||||
piBreak;
|
||||
}
|
||||
}
|
||||
if (exist || isRemoved(rpeer)) continue;
|
||||
rpeer.dist++;
|
||||
peers << rpeer;
|
||||
findNearestAddresses();
|
||||
peerConnected(rpeer.name);
|
||||
peerConnectedEvent(rpeer.name);
|
||||
}
|
||||
//piCout << "***";
|
||||
//piCout << self_info.name << self_info.neighbours;
|
||||
piForeach (PeerInfo & i, peers) {
|
||||
if (i.dist == 0) {
|
||||
self_info.addNeighbour(i.name);
|
||||
i.addNeighbour(self_info.name);
|
||||
}
|
||||
//piCout << i.name << i.neighbours;
|
||||
}
|
||||
//piCoutObj << "after sync " << peers.size_s() << " peers";
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PIPeer::sendToNeighbour(PIPeer::PeerInfo * peer, const PIByteArray & ba) {
|
||||
//if (peer->_neth == 0) return false;
|
||||
piCout << "[PIPeer] sendToNeighbour" << peer->name << peer->_naddress << ba.size_s() << "bytes ...";
|
||||
//bool ok = peer->_neth->send(peer->_naddress, ba.data(), ba.size_s());
|
||||
bool ok = eth_send.send(peer->_naddress, ba);
|
||||
//piCout << "[PIPeer] sendToNeighbour" << (ok ? "ok" : "fail");
|
||||
if (ok) diag_d.sended(ba.size_s());
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
void PIPeer::sendMBcast(const PIByteArray & ba) {
|
||||
//piCout << "sendMBcast" << ba.size() << "bytes ...";
|
||||
piForeach (PIEthernet * e, eths_mcast) {
|
||||
//errorClear();
|
||||
//piCout << "send to" << e->path() << e->sendAddress();// << e->send(ba);
|
||||
//piCout << PIEthernet::ethErrorString();
|
||||
if (e->isOpened())
|
||||
if (e->send(ba))
|
||||
diag_s.sended(ba.size_s());
|
||||
}
|
||||
piForeach (PIEthernet * e, eths_bcast) {
|
||||
//errorClear();
|
||||
//piCout << "send to" << e->path() << e->sendAddress();// << e->send(ba);
|
||||
//piCout << PIEthernet::ethErrorString();
|
||||
if (e->isOpened())
|
||||
if (e->send(ba))
|
||||
diag_s.sended(ba.size_s());
|
||||
}
|
||||
for (int p = _PIPEER_LOOPBACK_PORT_S; p <= _PIPEER_LOOPBACK_PORT_E; ++p) {
|
||||
eth_lo.setSendPort(p);
|
||||
if (eth_lo.send(ba))
|
||||
diag_s.sended(ba.size_s());
|
||||
}
|
||||
//piCout << "send muticast ok";
|
||||
}
|
||||
|
||||
|
||||
void PIPeer::sendPeerInfo(const PeerInfo & info) {
|
||||
PIByteArray ba;
|
||||
ba << int(1) << info.name << info;
|
||||
sendMBcast(ba);
|
||||
}
|
||||
|
||||
|
||||
void PIPeer::sendPeerRemove(const PIString & peer) {
|
||||
PIByteArray ba;
|
||||
ba << int(2) << peer;
|
||||
sendMBcast(ba);
|
||||
}
|
||||
|
||||
|
||||
void PIPeer::pingNeighbours() {
|
||||
PIByteArray ba, sba;
|
||||
ba << int(5) << self_info.name;
|
||||
//piCout << "pingNeighbours" << peers.size();
|
||||
piForeach (PeerInfo & p, peers) {
|
||||
//piCout << " ping neighbour" << p.name;
|
||||
if (!p.isNeighbour()) continue;
|
||||
piForeach (PeerInfo::Address & a, p.addresses) {
|
||||
//piCout << " address" << a.address << a.wait_ping;
|
||||
if (a.wait_ping) continue;
|
||||
a.wait_ping = true;
|
||||
sba = ba;
|
||||
sba << p.name << a.address << PISystemTime::current(true);
|
||||
//piCout << "ping" << p.name << a.address << a.last_ping;
|
||||
if (eth_send.send(a.address, sba))
|
||||
diag_s.sended(sba.size_s());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIPeer::syncPeers() {
|
||||
//piCout << "[PIPeer \"" + self_info.name + "\"] sync " << peers.size_s() << " peers";
|
||||
PIMutexLocker locker(eth_mutex);
|
||||
PIString pn;
|
||||
bool change = false;
|
||||
for (uint i = 0; i < peers.size(); ++i) {
|
||||
PeerInfo & cp(peers[i]);
|
||||
if (cp.sync > 3 && cp.dist == 0) {
|
||||
pn = cp.name;
|
||||
//piCoutObj << "sync: remove " << pn;
|
||||
addToRemoved(cp);
|
||||
peers.remove(i);
|
||||
sendPeerRemove(pn);
|
||||
--i;
|
||||
piForeach (PeerInfo & p, peers)
|
||||
p.removeNeighbour(pn);
|
||||
self_info.removeNeighbour(pn);
|
||||
peerDisconnected(pn);
|
||||
peerDisconnectedEvent(pn);
|
||||
change = true;
|
||||
continue;
|
||||
}
|
||||
cp.sync++;
|
||||
}
|
||||
pingNeighbours();
|
||||
if (change) findNearestAddresses();
|
||||
self_info.cnt++;
|
||||
self_info.time = PISystemTime::current();
|
||||
PIByteArray ba;
|
||||
ba << int(3) << self_info.name << self_info << peers;
|
||||
sendMBcast(ba);
|
||||
}
|
||||
|
||||
|
||||
void PIPeer::findNearestAddresses() {
|
||||
//piCout << "[PIPeer \"" + name_ + "\"] findNearestAddresses";
|
||||
addresses_map.clear();
|
||||
int max_dist = -1;
|
||||
static PIMap<PIString, PeerInfo * > peers_;
|
||||
peers_.clear();
|
||||
self_info._nuses.resize(self_info.neighbours.size());
|
||||
self_info._nuses.fill(0);
|
||||
self_info._first = &self_info;
|
||||
peers_[self_info.name] = &self_info;
|
||||
piForeach (PeerInfo & i, peers) {
|
||||
i._nuses.resize(i.neighbours.size());
|
||||
i._nuses.fill(0);
|
||||
i._first = 0;
|
||||
peers_[i.name] = &i;
|
||||
if (max_dist < i.dist)
|
||||
max_dist = i.dist;
|
||||
if (i.dist > 0) continue;
|
||||
i._naddress.clear();
|
||||
i._neth = 0;
|
||||
PIString mma, ma;
|
||||
bool af = false;
|
||||
for (int mi = 0; mi < self_info.addresses.size_s(); ++mi) {
|
||||
PeerInfo::Address & a(self_info.addresses[mi]);
|
||||
if (af) break;
|
||||
ma = a.address;
|
||||
//mma = m.left(m.findLast("."));
|
||||
mma = PIEthernet::applyMask(a.address, a.netmask);
|
||||
for (int ii = 0; ii < i.addresses.size_s(); ++ii) {
|
||||
PeerInfo::Address & r(i.addresses[ii]);
|
||||
if (!r.isAvailable()) continue;
|
||||
if (mma == PIEthernet::applyMask(r.address, r.netmask)) {
|
||||
i._naddress = r.address;
|
||||
//piCout << "_naddress" << i.name << "=" << r;
|
||||
af = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!af) continue;
|
||||
//piCout << " peer" << i.name << ma;
|
||||
piForeach (PIEthernet * e, eths_traffic)
|
||||
if (e->readAddress() == ma) {
|
||||
i._neth = e;
|
||||
break;
|
||||
}
|
||||
//piCout << i.name << i._naddress;
|
||||
}
|
||||
PIVector<PeerInfo * > cwave, nwave;
|
||||
PeerInfo * npeer;
|
||||
cwave << &self_info;
|
||||
for (int d = 0; d <= max_dist; ++d) {
|
||||
if (cwave.isEmpty()) break;
|
||||
nwave.clear();
|
||||
piForeach (PeerInfo * p, cwave) {
|
||||
int ns = p->neighbours.size_s();
|
||||
for (int n = 0; n < ns; ++n) {
|
||||
if (p->_nuses[n] >= ns) continue;
|
||||
p->_nuses[n]++;
|
||||
npeer = peers_[p->neighbours[n]];
|
||||
if (npeer == 0) continue;
|
||||
if (d == 0) npeer->_first = npeer;
|
||||
else {
|
||||
if (d == 1) npeer->_first = p;
|
||||
else npeer->_first = p->_first;
|
||||
}
|
||||
nwave << npeer;
|
||||
}
|
||||
}
|
||||
cwave = nwave;
|
||||
//piCout << "wave" << d;
|
||||
for (int i = 0; i < cwave.size_s(); ++i) {
|
||||
//piCout << " peer" << cwave[i]->name << Hex << (uint)(cwave[i]->_first);
|
||||
if (cwave[i]->_first == 0) {cwave.remove(i); --i; continue;}
|
||||
if (addresses_map.contains(cwave[i]->name)) {cwave.remove(i); --i; continue;}
|
||||
}
|
||||
for (int i = 0; i < cwave.size_s(); ++i) {
|
||||
PIVector<PeerInfo * > & pl(addresses_map[cwave[i]->name]);
|
||||
if (!pl.contains(cwave[i]->_first))
|
||||
pl << cwave[i]->_first;
|
||||
}
|
||||
}
|
||||
/*piCout << " ** addresses map **";
|
||||
piForeachC (napair & i, addresses_map)
|
||||
piCout << i.first << i.second;
|
||||
piCout << " ** addresses map end **";*/
|
||||
}
|
||||
185
src/io/pipeer.h
Executable file
185
src/io/pipeer.h
Executable file
@@ -0,0 +1,185 @@
|
||||
/*! \file pipeer.h
|
||||
* \brief Peering net node
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Peer - named I/O ethernet node, forming self-organized peering network
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIPEER_H
|
||||
#define PIPEER_H
|
||||
|
||||
#include "piethernet.h"
|
||||
#include "pidiagnostics.h"
|
||||
|
||||
class PIP_EXPORT PIPeer: public PIObject
|
||||
{
|
||||
PIOBJECT(PIPeer)
|
||||
private:
|
||||
struct PeerData {
|
||||
PeerData() {msg_count = msg_rec = 0;}
|
||||
void clear() {msg_count = msg_rec = 0; data.clear();}
|
||||
bool isEmpty() const {return msg_count == 0;}
|
||||
bool isFullReceived() const {return msg_count == msg_rec;}
|
||||
void addData(const PIByteArray & ba) {data.append(ba); msg_rec++;}
|
||||
void setData(const PIByteArray & ba) {data = ba; msg_rec = 0; msg_count = (data.size_s() - 1) / 4096 + 1;}
|
||||
PIByteArray data;
|
||||
int msg_count;
|
||||
int msg_rec;
|
||||
};
|
||||
|
||||
public:
|
||||
PIPeer(const PIString & name);
|
||||
virtual ~PIPeer();
|
||||
|
||||
class PeerInfo {
|
||||
friend class PIPeer;
|
||||
friend PIByteArray & operator <<(PIByteArray & s, const PIPeer::PeerInfo & v);
|
||||
friend PIByteArray & operator >>(PIByteArray & s, PIPeer::PeerInfo & v);
|
||||
public:
|
||||
PeerInfo() {dist = sync = cnt = 0; _neth = 0; _first = 0;}
|
||||
|
||||
struct Address {
|
||||
Address(const PIString & a = PIString(), const PIString & m = "255.255.255.0");
|
||||
bool isAvailable() const {return ping > 0;}
|
||||
//inline const Address & operator =(const Address & v) {address = v.address; netmask = v.netmask; piCout << "!!!!!!!!!" << last_ping; return *this;}
|
||||
PIString address;
|
||||
PIString netmask;
|
||||
double ping; // ms
|
||||
bool wait_ping;
|
||||
PISystemTime last_ping;
|
||||
};
|
||||
|
||||
PIString name;
|
||||
PIVector<Address> addresses;
|
||||
int dist;
|
||||
|
||||
bool isNeighbour() const {return dist == 0;}
|
||||
int ping() const;
|
||||
|
||||
protected:
|
||||
void addNeighbour(const PIString & n) {if (!neighbours.contains(n)) neighbours << n;}
|
||||
void addNeighbours(const PIStringList & l) {piForeachC (PIString & n, l) if (!neighbours.contains(n)) neighbours << n;}
|
||||
void removeNeighbour(const PIString & n) {neighbours.removeAll(n);}
|
||||
|
||||
PIString nearest_address;
|
||||
PIStringList neighbours;
|
||||
int sync, cnt;
|
||||
PISystemTime time;
|
||||
PIString _naddress;
|
||||
PIEthernet * _neth;
|
||||
PIVector<uchar> _nuses;
|
||||
PeerInfo * _first;
|
||||
PeerData _data;
|
||||
|
||||
};
|
||||
|
||||
friend PIByteArray & operator <<(PIByteArray & s, const PIPeer::PeerInfo & v);
|
||||
friend PIByteArray & operator >>(PIByteArray & s, PIPeer::PeerInfo & v);
|
||||
|
||||
bool send(const PIString & to, const PIByteArray & data) {return send(to, data.data(), data.size_s());}
|
||||
bool send(const PIString & to, const PIString & data) {return send(to, data.data(), data.size_s());}
|
||||
bool send(const PIString & to, const void * data, int size);
|
||||
bool send(const PeerInfo & to, const PIByteArray & data) {return send(to.name, data.data(), data.size_s());}
|
||||
bool send(const PeerInfo & to, const PIString & data) {return send(to.name, data.data(), data.size_s());}
|
||||
bool send(const PeerInfo & to, const void * data, int size) {return send(to.name, data, size);}
|
||||
bool send(const PeerInfo * to, const PIByteArray & data) {if (to == 0) return false; return send(to->name, data.data(), data.size_s());}
|
||||
bool send(const PeerInfo * to, const PIString & data) {if (to == 0) return false; return send(to->name, data.data(), data.size_s());}
|
||||
bool send(const PeerInfo * to, const void * data, int size) {if (to == 0) return false; return send(to->name, data, size);}
|
||||
void sendToAll(const PIByteArray & data) {piForeachC (PeerInfo & i, peers) send(i.name, data.data(), data.size_s());}
|
||||
void sendToAll(const PIString & data) {piForeachC (PeerInfo & i, peers) send(i.name, data.data(), data.size_s());}
|
||||
void sendToAll(const void * data, int size) {piForeachC (PeerInfo & i, peers) send(i.name, data, size);}
|
||||
|
||||
bool isMulticastReceive() const {return !eths_mcast.isEmpty();}
|
||||
bool isBroadcastReceive() const {return !eths_bcast.isEmpty();}
|
||||
|
||||
PIDiagnostics & diagnosticService() {return diag_s;}
|
||||
PIDiagnostics & diagnosticData() {return diag_d;}
|
||||
|
||||
const PIVector<PIPeer::PeerInfo> & allPeers() const {return peers;}
|
||||
bool isPeerExists(const PIString & name) const {return getPeerByName(name) != 0;}
|
||||
|
||||
const PeerInfo * getPeerByName(const PIString & name) const {piForeachC (PeerInfo & i, peers) if (i.name == name) return &i; return 0;}
|
||||
|
||||
void lock() {mc_mutex.lock();}
|
||||
void unlock() {mc_mutex.unlock();}
|
||||
|
||||
EVENT2(dataReceivedEvent, const PIString &, from, const PIByteArray &, data);
|
||||
EVENT1(peerConnectedEvent, const PIString &, name);
|
||||
EVENT1(peerDisconnectedEvent, const PIString &, name);
|
||||
|
||||
protected:
|
||||
virtual void dataReceived(const PIString & from, const PIByteArray & data) {;}
|
||||
virtual void peerConnected(const PIString & name) {;}
|
||||
virtual void peerDisconnected(const PIString & name) {;}
|
||||
|
||||
EVENT_HANDLER2(bool, dataRead, uchar *, readed, int, size);
|
||||
EVENT_HANDLER2(bool, mbcastRead, uchar *, readed, int, size);
|
||||
|
||||
private:
|
||||
EVENT_HANDLER2(void, timerEvent, void * , data, int, delim);
|
||||
|
||||
bool hasPeer(const PIString & name) {piForeachC (PeerInfo & i, peers) if (i.name == name) return true; return false;}
|
||||
bool removePeer(const PIString & name) {for (uint i = 0; i < peers.size(); ++i) if (peers[i].name == name) {peers.remove(i); return true;} return false;}
|
||||
|
||||
void sendPeerInfo(const PeerInfo & info);
|
||||
void sendPeerRemove(const PIString & peer);
|
||||
void sendSelfInfo() {sendPeerInfo(self_info);}
|
||||
void sendSelfRemove() {sendPeerRemove(self_info.name);}
|
||||
void syncPeers();
|
||||
void findNearestAddresses();
|
||||
void initEths(PIStringList al);
|
||||
void initMBcasts(PIStringList al);
|
||||
void destroyMBcasts();
|
||||
void sendMBcast(const PIByteArray & ba);
|
||||
void pingNeighbours();
|
||||
void addToRemoved(const PeerInfo & pi) {removed[pi.name] = PIPair<int, PISystemTime>(pi.cnt, pi.time);}
|
||||
bool isRemoved(const PeerInfo & pi) const {return (removed.value(pi.name) == PIPair<int, PISystemTime>(pi.cnt, pi.time));}
|
||||
|
||||
PeerInfo * quickestPeer(const PIString & to);
|
||||
bool sendToNeighbour(PeerInfo * peer, const PIByteArray & ba);
|
||||
inline static bool isPeerRecent(const PeerInfo & my, const PeerInfo & income) {return (my.cnt < income.cnt) || (my.time < income.time);}
|
||||
|
||||
// 1 - new peer, 2 - remove peer, 3 - sync peers, 4 - data, 5 - ping
|
||||
// Data packet: 4, from, to, ticks, data_size, data
|
||||
|
||||
typedef PIPair<PIString, PIVector<PeerInfo * > > napair;
|
||||
|
||||
PIVector<PIEthernet * > eths_traffic, eths_mcast, eths_bcast;
|
||||
PIEthernet eth_send, eth_lo;
|
||||
PITimer timer;
|
||||
PIMutex mc_mutex, eth_mutex;
|
||||
|
||||
PeerInfo self_info;
|
||||
PIVector<PeerInfo> peers;
|
||||
PIMap<PIString, PIVector<PeerInfo * > > addresses_map; // map {"to" = list of nearest peers}
|
||||
PIMap<PIString, PIPair<int, PISystemTime> > removed;
|
||||
PIDiagnostics diag_s, diag_d;
|
||||
//PIString id_;
|
||||
|
||||
};
|
||||
|
||||
inline PICout operator <<(PICout c, const PIPeer::PeerInfo::Address & v) {c.space(); c << "PeerAddress(" << v.address << ", " << v.netmask << ", " << v.ping << ")"; return c;}
|
||||
inline PICout operator <<(PICout c, const PIPeer::PeerInfo & v) {c.space(); c << "PeerInfo(" << v.name << ", " << v.dist << ", " << v.addresses << ")"; return c;}
|
||||
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PIPeer::PeerInfo::Address & v) {s << v.address << v.netmask << v.ping; return s;}
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PIPeer::PeerInfo::Address & v) {s >> v.address >> v.netmask >> v.ping; return s;}
|
||||
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PIPeer::PeerInfo & v) {s << v.name << v.addresses << v.dist << v.neighbours << v.cnt << v.time; return s;}
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PIPeer::PeerInfo & v) {s >> v.name >> v.addresses >> v.dist >> v.neighbours >> v.cnt >> v.time; return s;}
|
||||
|
||||
#endif // PIPEER_H
|
||||
792
src/io/piprotocol.cpp
Executable file
792
src/io/piprotocol.cpp
Executable file
@@ -0,0 +1,792 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Protocol, input/output channel (COM, UDP)
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com, Bychkov Andrey wapmobil@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "piprotocol.h"
|
||||
|
||||
|
||||
/** \class PIProtocol
|
||||
* \brief
|
||||
* \details
|
||||
* \section PIProtocol_sec0 Synopsis
|
||||
*
|
||||
*
|
||||
*
|
||||
* */
|
||||
|
||||
|
||||
PIProtocol::PIProtocol(const PIString & config, const PIString & name_, void * recHeaderPtr, int recHeaderSize, void * recDataPtr, int recDataSize, void * sendDataPtr_, int sendDataSize_): PIObject() {
|
||||
init();
|
||||
protName = name_;
|
||||
PIObject::setName(name_);
|
||||
PIConfig conf(config, PIIODevice::ReadOnly);
|
||||
if (!conf.isOpened()) {
|
||||
piCoutObj << "Can`t open \"" << config << "\"!";
|
||||
devReceiverState = devSenderState = "Config error";
|
||||
return;
|
||||
}
|
||||
PIConfig::Entry & b(conf.getValue(name_)),
|
||||
& rb(b.getValue("receiver")),
|
||||
& sb(b.getValue("sender"));
|
||||
|
||||
init_receiver(b, rb, config);
|
||||
init_sender(b, sb, config);
|
||||
|
||||
headerPtr = (uchar * )recHeaderPtr;
|
||||
headerSize = recHeaderSize;
|
||||
dataPtr = (uchar * )recDataPtr;
|
||||
dataSize = recDataSize;
|
||||
sendDataPtr = (uchar * )sendDataPtr_;
|
||||
sendDataSize = sendDataSize_;
|
||||
packet_ext->setHeader(PIByteArray(recHeaderPtr, recHeaderSize));
|
||||
packet_ext->setPayloadSize(recDataSize);
|
||||
packet_ext->setPacketSize(recDataSize);
|
||||
packet_ext->setSplitMode(PIPacketExtractor::Header);
|
||||
bool null_h = (recHeaderPtr == 0 || recHeaderSize == 0), null_d = (recDataPtr == 0 || recDataSize == 0);
|
||||
if (null_h && null_d) packet_ext->setSplitMode(PIPacketExtractor::None);
|
||||
else {
|
||||
if (null_h) packet_ext->setSplitMode(PIPacketExtractor::Size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PIProtocol::~PIProtocol() {
|
||||
//cout << "prot " << protName << " delete\n";
|
||||
if (history_write_rec) {
|
||||
if (history_file_rec.isEmpty()) {
|
||||
history_file_rec.close();
|
||||
history_file_rec.remove();
|
||||
}
|
||||
history_file_rec.close();
|
||||
}
|
||||
if (history_write_send) {
|
||||
if (history_file_send.isEmpty()) {
|
||||
history_file_send.close();
|
||||
history_file_send.remove();
|
||||
}
|
||||
history_file_send.close();
|
||||
}
|
||||
delete diagTimer;
|
||||
delete sendTimer;
|
||||
delete secTimer;
|
||||
delete packet_ext;
|
||||
if (eth != 0) delete eth;
|
||||
if (ser != 0) delete ser;
|
||||
}
|
||||
|
||||
|
||||
void PIProtocol::init() {
|
||||
packet_ext = new PIPacketExtractor(0, PIPacketExtractor::None);
|
||||
packet_ext->setThreadedReadData(this);
|
||||
packet_ext->setThreadedReadSlot(receiveEvent);
|
||||
packet_ext->setHeaderCheckSlot(headerValidateEvent);
|
||||
packet_ext->setName("__S__PIProtocol::packet_ext");
|
||||
work = new_mp_prot = history_write_rec = history_write_send = false;
|
||||
eth = 0;
|
||||
ser = 0;
|
||||
ret_func = 0;
|
||||
mp_owner = 0;
|
||||
net_diag = PIProtocol::Unknown;
|
||||
cur_pckt = 0;
|
||||
packets[0] = packets[1] = pckt_cnt = pckt_cnt_max = 0;
|
||||
diagTimer = 0;
|
||||
timeout_ = 3.f;
|
||||
sendTimer = new PITimer(sendEvent, this);
|
||||
diagTimer = new PITimer(diagEvent, this);
|
||||
secTimer = new PITimer(secEvent, this);
|
||||
sendTimer->setName("__S__PIProtocol::sendTimer");
|
||||
diagTimer->setName("__S__PIProtocol::diagTimer");
|
||||
secTimer->setName("__S__PIProtocol::secTimer");
|
||||
wrong_count = receive_count = send_count = missed_count = 0;
|
||||
packets_in_sec = packets_out_sec = bytes_in_sec = bytes_out_sec = 0;
|
||||
immediate_freq = integral_freq = ifreq = 0.f;
|
||||
headerPtr = dataPtr = sendDataPtr = 0;
|
||||
headerSize = dataSize = sendDataSize = 0;
|
||||
type_rec = type_send = PIProtocol::None;
|
||||
devSenderState = devReceiverState = "Unknown";
|
||||
devSenderName = devReceiverName = "no device";
|
||||
history_rsize_rec = history_rsize_send = "no file";
|
||||
history_file_rec.setName("__S__PIProtocol::history_file_rec");
|
||||
history_file_send.setName("__S__PIProtocol::history_file_send");
|
||||
secTimer->start(1000.);
|
||||
/*addEvent("receiver started");
|
||||
addEvent("receiver stopped");
|
||||
addEvent("sender started");
|
||||
addEvent("sender stopped");
|
||||
addEvent<bool>("received");
|
||||
addEvent<PIProtocol::Quality>("quality changed");
|
||||
addEventHandler<float>(HANDLER(PIProtocol, startReceive));
|
||||
addEventHandler<float>(HANDLER(PIProtocol, startSend));
|
||||
addEventHandler(HANDLER(PIProtocol, start));
|
||||
addEventHandler(HANDLER(PIProtocol, stopReceive));
|
||||
addEventHandler(HANDLER(PIProtocol, stopSend));
|
||||
addEventHandler(HANDLER(PIProtocol, stop));*/
|
||||
}
|
||||
|
||||
|
||||
void PIProtocol::init_sender(PIConfig::Entry & b, PIConfig::Entry & sb, const PIString & config) {
|
||||
int ps, gps;
|
||||
bool ok, gok, flag, gflag, has_dev = false;
|
||||
float freq, gfreq;
|
||||
PIFlags<PISerial::Parameters> pp(0);
|
||||
PIString dev, gdev;
|
||||
|
||||
if (sb.isEntryExists("ip") && sb.isEntryExists("device")) {
|
||||
piCoutObj << "Ambiguous sender type in \"" << config << "\"!";
|
||||
devSenderState = "Config error";
|
||||
return;
|
||||
}
|
||||
dev = sb.getValue("ip", "", &ok);
|
||||
gdev = b.getValue("ip", "", &gok);
|
||||
has_dev = false;
|
||||
if (ok || gok) {
|
||||
if (gok && !ok) dev = gdev;
|
||||
if (gok && ok && (dev != gdev)) {
|
||||
piCoutObj << "Ambiguous sender type in \"" << config << "\"!";
|
||||
devSenderState = "Config error";
|
||||
return;
|
||||
}
|
||||
ps = sb.getValue("port", 0, &ok);
|
||||
gps = b.getValue("port", 0, &gok);
|
||||
if (ok || gok) {
|
||||
if (gok && !ok) ps = gps;
|
||||
if (gok && ok && (ps != gps)) {
|
||||
piCoutObj << "Ambiguous send port in \"" << config << "\"!";
|
||||
devSenderState = "Config error";
|
||||
return;
|
||||
}
|
||||
type_send = PIProtocol::Ethernet;
|
||||
if (eth == 0) eth = new PIEthernet();
|
||||
eth->setName("__S__PIProtocol::eth");
|
||||
setSenderAddress(dev, ps);
|
||||
//setReceiverAddress(dev, ps);
|
||||
has_dev = true;
|
||||
flag = sb.getValue("reconnectEnabled", true, &ok);
|
||||
gflag = b.getValue("reconnectEnabled", true, &gok);
|
||||
if (ok || gok) {
|
||||
if (gok && !ok) flag = gflag;
|
||||
if (gok && ok && (flag != gflag)) {
|
||||
piCoutObj << "Ambiguous \"reconnectEnabled\" flag in \"" << config << "\"!";
|
||||
devReceiverState = "Config error";
|
||||
return;
|
||||
}
|
||||
eth->setReopenEnabled(flag);
|
||||
}
|
||||
freq = sb.getValue("reconnectTimeout", 1., &ok);
|
||||
gfreq = b.getValue("reconnectTimeout", 1., &gok);
|
||||
if (ok || gok) {
|
||||
if (gok && !ok) freq = gfreq;
|
||||
if (gok && ok && (freq != gfreq)) {
|
||||
piCoutObj << "Ambiguous \"reconnectTimeout\" value in \"" << config << "\"!";
|
||||
devReceiverState = "Config error";
|
||||
return;
|
||||
}
|
||||
eth->setReopenTimeout(freq * 1000);
|
||||
}
|
||||
/*if (sendDataPtr_ == 0)
|
||||
piCoutObj << "Warning: null send data pointer!";
|
||||
if (sendDataSize_ == 0)
|
||||
piCoutObj << "Warning: null send data size!";*/
|
||||
} else {
|
||||
piCoutObj << "Can`t find \"" << name() << ".sender.port\" or \"" << name() << ".port\" in \"" << config << "\"!";
|
||||
devSenderState = "Config error";
|
||||
return;
|
||||
}
|
||||
}
|
||||
dev = sb.getValue("device", "", &ok);
|
||||
gdev = b.getValue("device", "", &gok);
|
||||
if (ok || gok) {
|
||||
if (gok && !ok) dev = gdev;
|
||||
if (gok && ok && (dev != gdev)) {
|
||||
piCoutObj << "Ambiguous sender type in \"" << config << "\"!";
|
||||
devSenderState = "Config error";
|
||||
return;
|
||||
}
|
||||
ps = sb.getValue("speed", 0, &ok);
|
||||
gps = b.getValue("speed", 0, &gok);
|
||||
if (ok || gok) {
|
||||
if (gok && !ok) ps = gps;
|
||||
if (gok && ok && (ps != gps)) {
|
||||
piCoutObj << "Ambiguous send \"speed\" in \"" << config << "\"!";
|
||||
devSenderState = "Config error";
|
||||
return;
|
||||
}
|
||||
flag = sb.getValue("parity", false, &ok);
|
||||
gflag = b.getValue("parity", false, &gok);
|
||||
if (ok || gok) {
|
||||
if (gok && !ok) flag = gflag;
|
||||
if (gok && ok && (flag != gflag)) {
|
||||
piCoutObj << "Ambiguous send \"parity\" in \"" << config << "\"!";
|
||||
devSenderState = "Config error";
|
||||
return;
|
||||
}
|
||||
pp.setFlag(PISerial::ParityControl, flag);
|
||||
}
|
||||
flag = sb.getValue("twoStopBits", false, &ok);
|
||||
gflag = b.getValue("twoStopBits", false, &gok);
|
||||
if (ok || gok) {
|
||||
if (gok && !ok) flag = gflag;
|
||||
if (gok && ok && (flag != gflag)) {
|
||||
piCoutObj << "Ambiguous send \"twoStopBits\" parity in \"" << config << "\"!";
|
||||
devSenderState = "Config error";
|
||||
return;
|
||||
}
|
||||
pp.setFlag(PISerial::TwoStopBits, flag);
|
||||
}
|
||||
} else {
|
||||
piCoutObj << "Can`t find \"" << name() << ".sender.speed\" or \"" << name() << ".speed\" in \"" << config << "\"!";
|
||||
devSenderState = "Config error";
|
||||
return;
|
||||
}
|
||||
type_send = PIProtocol::Serial;
|
||||
if (ser == 0) ser = new PISerial(dev);
|
||||
ser->setName("__S__PIProtocol::ser");
|
||||
setSenderDevice(dev, (PISerial::Speed)ps);
|
||||
ser->setOutSpeed((PISerial::Speed)ps);
|
||||
ser->setParameters(pp);
|
||||
has_dev = true;
|
||||
/*if (sendDataPtr_ == 0)
|
||||
piCoutObj << "Warning: null send data pointer!";
|
||||
if (sendDataSize_ == 0)
|
||||
piCoutObj << "Warning: null send data size!";*/
|
||||
}
|
||||
history_write_send = sb.getValue("writeHistory", false, &ok);
|
||||
bool ghist = b.getValue("writeHistory", false, &gok);
|
||||
if (ok || gok) {
|
||||
if (gok && !ok) history_write_send = ghist;
|
||||
if (gok && ok && (history_write_send != ghist)) {
|
||||
piCoutObj << "Ambiguous sender history in \"" << config << "\"!";
|
||||
devSenderState = "Config error";
|
||||
return;
|
||||
}
|
||||
if (history_write_send) {
|
||||
history_path_send = sb.getValue("historyFile", "./history_" + protName + "_send_" +
|
||||
PIDate::current().toString("__dd_mm_yyyy_") +
|
||||
PITime::current().toString("_hh_mm_ss_")).value();
|
||||
history_id_send = sb.getValue("historyID", 0, &ok);
|
||||
if (!ok) {
|
||||
history_id_send = ushort(protName.toByteArray().checksumPlain32()) + 1;
|
||||
piCoutObj << "Warning: no sender history ID defined, write with ID = " << history_id_send;
|
||||
}
|
||||
history_file_send.open(history_path_send, PIIODevice::WriteOnly);
|
||||
}
|
||||
}
|
||||
freq = sb.getValue("frequency", -1.f, &ok);
|
||||
gfreq = b.getValue("frequency", -1.f, &gok);
|
||||
if (gok && !ok) freq = gfreq;
|
||||
if (gok && ok && (freq != gfreq)) {
|
||||
piCoutObj << "Ambiguous sender frequency in \"" << config << "\"!";
|
||||
devSenderState = "Config error";
|
||||
return;
|
||||
}
|
||||
if (freq > 0.f && !has_dev)
|
||||
piCoutObj << "Warning: no sender device and not null send frequency!";
|
||||
setSenderFrequency(freq);
|
||||
}
|
||||
|
||||
|
||||
void PIProtocol::init_receiver(PIConfig::Entry & b, PIConfig::Entry & rb, const PIString & config) {
|
||||
int ps, gps;
|
||||
bool ok, gok, flag, gflag, has_dev = false;
|
||||
float freq, gfreq;
|
||||
PIFlags<PISerial::Parameters> pp(0);
|
||||
PIString dev, gdev;
|
||||
|
||||
if (rb.isEntryExists("ip") && rb.isEntryExists("device")) {
|
||||
piCoutObj << "Ambiguous receiver type in \"" << config << "\"!";
|
||||
devReceiverState = "Config error";
|
||||
return;
|
||||
}
|
||||
dev = rb.getValue("ip", "", &ok);
|
||||
gdev = b.getValue("ip", "", &gok);
|
||||
if (ok || gok) {
|
||||
if (gok && !ok) dev = gdev;
|
||||
if (gok && ok && (dev != gdev)) {
|
||||
piCoutObj << "Ambiguous receiver type in \"" << config << "\"!";
|
||||
devReceiverState = "Config error";
|
||||
return;
|
||||
}
|
||||
ps = rb.getValue("port", 0, &ok);
|
||||
gps = b.getValue("port", 0, &gok);
|
||||
if (ok || gok) {
|
||||
if (gok && !ok) ps = gps;
|
||||
if (gok && ok && (ps != gps)) {
|
||||
piCoutObj << "Ambiguous receive port in \"" << config << "\"!";
|
||||
devReceiverState = "Config error";
|
||||
return;
|
||||
}
|
||||
type_rec = PIProtocol::Ethernet;
|
||||
eth = new PIEthernet();
|
||||
eth->setName("__S__PIProtocol::eth");
|
||||
packet_ext->setDevice(eth);
|
||||
//setSenderAddress(dev, ps);
|
||||
setReceiverAddress(dev, ps);
|
||||
has_dev = true;
|
||||
flag = rb.getValue("reconnectEnabled", true, &ok);
|
||||
gflag = b.getValue("reconnectEnabled", true, &gok);
|
||||
if (ok || gok) {
|
||||
if (gok && !ok) flag = gflag;
|
||||
if (gok && ok && (flag != gflag)) {
|
||||
piCoutObj << "Ambiguous \"reconnectEnabled\" flag in \"" << config << "\"!";
|
||||
devReceiverState = "Config error";
|
||||
return;
|
||||
}
|
||||
eth->setReopenEnabled(flag);
|
||||
}
|
||||
freq = rb.getValue("reconnectTimeout", 1., &ok);
|
||||
gfreq = b.getValue("reconnectTimeout", 1., &gok);
|
||||
if (ok || gok) {
|
||||
if (gok && !ok) freq = gfreq;
|
||||
if (gok && ok && (freq != gfreq)) {
|
||||
piCoutObj << "Ambiguous \"reconnectTimeout\" value in \"" << config << "\"!";
|
||||
devReceiverState = "Config error";
|
||||
return;
|
||||
}
|
||||
eth->setReopenTimeout(freq * 1000);
|
||||
}
|
||||
/*if (recDataPtr == 0)
|
||||
piCoutObj << "Warning: null receive data pointer!";
|
||||
if (recDataSize == 0)
|
||||
piCoutObj << "Warning: null receive data size!";*/
|
||||
} else {
|
||||
piCoutObj << "Can`t find \"" << name() << ".receiver.port\" or \"" << name() << ".port\" in \"" << config << "\"!";
|
||||
devReceiverState = "Config error";
|
||||
return;
|
||||
}
|
||||
}
|
||||
dev = rb.getValue("device", "", &ok);
|
||||
gdev = b.getValue("device", "", &gok);
|
||||
if (ok || gok) {
|
||||
if (gok && !ok) dev = gdev;
|
||||
if (gok && ok && (dev != gdev)) {
|
||||
piCoutObj << "Ambiguous receiver type in \"" << config << "\"!";
|
||||
devReceiverState = "Config error";
|
||||
return;
|
||||
}
|
||||
ps = rb.getValue("speed", 0, &ok);
|
||||
gps = b.getValue("speed", 0, &gok);
|
||||
if (ok || gok) {
|
||||
if (gok && !ok) ps = gps;
|
||||
if (gok && ok && (ps != gps)) {
|
||||
piCoutObj << "Ambiguous receive \"speed\" in \"" << config << "\"!";
|
||||
devReceiverState = "Config error";
|
||||
return;
|
||||
}
|
||||
flag = rb.getValue("parity", false, &ok);
|
||||
gflag = b.getValue("parity", false, &gok);
|
||||
if (ok || gok) {
|
||||
if (gok && !ok) flag = gflag;
|
||||
if (gok && ok && (flag != gflag)) {
|
||||
piCoutObj << "Ambiguous receive \"parity\" in \"" << config << "\"!";
|
||||
devReceiverState = "Config error";
|
||||
return;
|
||||
}
|
||||
pp.setFlag(PISerial::ParityControl, flag);
|
||||
}
|
||||
flag = rb.getValue("twoStopBits", false, &ok);
|
||||
gflag = b.getValue("twoStopBits", false, &gok);
|
||||
if (ok || gok) {
|
||||
if (gok && !ok) flag = gflag;
|
||||
if (gok && ok && (flag != gflag)) {
|
||||
piCoutObj << "Ambiguous receive \"twoStopBits\" parity in \"" << config << "\"!";
|
||||
devReceiverState = "Config error";
|
||||
return;
|
||||
}
|
||||
pp.setFlag(PISerial::TwoStopBits, flag);
|
||||
}
|
||||
type_rec = PIProtocol::Serial;
|
||||
type_send = PIProtocol::Serial;
|
||||
ser = new PISerial(dev);
|
||||
ser->setName("__S__PIProtocol::ser");
|
||||
packet_ext->setDevice(ser);
|
||||
//setSenderDevice(dev, (PISerial::Speed)ps);
|
||||
setReceiverDevice(dev, (PISerial::Speed)ps);
|
||||
ser->setInSpeed((PISerial::Speed)ps);
|
||||
ser->setParameters(pp);
|
||||
ps = rb.getValue("vtime", 1, &ok);
|
||||
gps = b.getValue("vtime", 1, &gok);
|
||||
if (ok || gok) {
|
||||
if (gok && !ok) ps = gps;
|
||||
if (gok && ok && (ps != gps)) {
|
||||
piCoutObj << "Ambiguous receive \"vtime\" in \"" << config << "\"!";
|
||||
devReceiverState = "Config error";
|
||||
return;
|
||||
}
|
||||
ser->setVTime(ps);
|
||||
}
|
||||
has_dev = true;
|
||||
/*if (recDataPtr == 0)
|
||||
piCoutObj << "Warning: null receive data pointer!";
|
||||
if (recDataSize == 0)
|
||||
piCoutObj << "Warning: null receive data size!";*/
|
||||
} else {
|
||||
piCoutObj << "Can`t find \"" << name() << ".receiver.speed\" or \"" << name() << ".speed\" in \"" << config << "\"!";
|
||||
devReceiverState = "Config error";
|
||||
return;
|
||||
}
|
||||
}
|
||||
history_write_rec = rb.getValue("writeHistory", false, &ok);
|
||||
bool ghist = b.getValue("writeHistory", false, &gok);
|
||||
if (ok || gok) {
|
||||
if (gok && !ok) history_write_rec = ghist;
|
||||
if (gok && ok && (history_write_rec != ghist)) {
|
||||
piCoutObj << "Ambiguous receiver history in \"" << config << "\"!";
|
||||
devReceiverState = "Config error";
|
||||
return;
|
||||
}
|
||||
if (history_write_rec) {
|
||||
history_path_rec = rb.getValue("historyFile", "./history_" + protName + "_rec_" +
|
||||
PIDate::current().toString("__dd_mm_yyyy_") +
|
||||
PITime::current().toString("_hh_mm_ss_")).value();
|
||||
history_id_rec = rb.getValue("historyID", 0, &ok);
|
||||
if (!ok) {
|
||||
history_id_rec = ushort(protName.toByteArray().checksumPlain32());
|
||||
piCoutObj << "Warning: no receiver history ID defined, write with ID = " << history_id_rec;
|
||||
}
|
||||
history_file_rec.open(history_path_rec, PIIODevice::WriteOnly);
|
||||
}
|
||||
}
|
||||
freq = rb.getValue("frequency", -1.f, &ok);
|
||||
gfreq = b.getValue("frequency", -1.f, &gok);
|
||||
if (gok && !ok) freq = gfreq;
|
||||
if (gok && ok && (freq != gfreq)) {
|
||||
piCoutObj << "Ambiguous expected frequency in \"" << config << "\"!";
|
||||
devReceiverState = "Config error";
|
||||
return;
|
||||
}
|
||||
if (freq > 0.f && !has_dev)
|
||||
piCoutObj << "Warning: no receiver device and not null expected frequency!";
|
||||
float tm = b.getValue("disconnectTimeout", 3.f);
|
||||
if (tm <= 0.f)
|
||||
piCoutObj << "Warning: diconnect timeout <= 0 s!";
|
||||
timeout_ = (tm < 0.f) ? 0.f : tm;
|
||||
setExpectedFrequency(freq);
|
||||
}
|
||||
|
||||
|
||||
void PIProtocol::setReceiverDevice(const PIString & device, PISerial::Speed speed, bool force) {
|
||||
if (force) {
|
||||
type_send = type_rec = PIProtocol::Serial;
|
||||
if (ser == 0) {
|
||||
ser = new PISerial();
|
||||
ser->setName("__S__PIProtocol::ser");
|
||||
packet_ext->setDevice(ser);
|
||||
}
|
||||
}
|
||||
if (type_rec == PIProtocol::Serial && ser != 0) {
|
||||
ser->setDevice(device);
|
||||
ser->setSpeed(speed);
|
||||
devReceiverName = device;
|
||||
devSenderName = device;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIProtocol::setReceiverAddress(const PIString & ip, int port, bool force) {
|
||||
if (force) {
|
||||
type_rec = PIProtocol::Ethernet;
|
||||
if (eth == 0) {
|
||||
eth = new PIEthernet();
|
||||
eth->setName("__S__PIProtocol::eth");
|
||||
packet_ext->setDevice(eth);
|
||||
}
|
||||
}
|
||||
if (type_rec == PIProtocol::Ethernet && eth != 0) {
|
||||
eth->setReadAddress(ip, port);
|
||||
if (ip.trimmed().isEmpty()) devReceiverName = "no ip";
|
||||
else devReceiverName = ip + ":" + PIString::fromNumber(port);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIProtocol::setSenderDevice(const PIString & device, PISerial::Speed speed, bool force) {
|
||||
if (force) {
|
||||
type_send = type_rec = PIProtocol::Serial;
|
||||
if (ser == 0) ser = new PISerial();
|
||||
ser->setName("__S__PIProtocol::ser");
|
||||
}
|
||||
if (type_send == PIProtocol::Serial && ser != 0) {
|
||||
ser->setDevice(device);
|
||||
ser->setSpeed(speed);
|
||||
ser->open();
|
||||
devSenderName = device;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIProtocol::setSenderAddress(const PIString & ip, int port, bool force) {
|
||||
if (force) {
|
||||
type_send = PIProtocol::Ethernet;
|
||||
if (eth == 0) eth = new PIEthernet();
|
||||
eth->setName("__S__PIProtocol::eth");
|
||||
}
|
||||
if (type_send == PIProtocol::Ethernet && eth != 0) {
|
||||
eth->setSendAddress(ip, port);
|
||||
if (ip.isEmpty()) devSenderName = "no ip";
|
||||
else devSenderName = ip + ":" + PIString::fromNumber(port);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIProtocol::setSenderIP(const PIString & ip, bool force) {
|
||||
if (force) {
|
||||
type_send = PIProtocol::Ethernet;
|
||||
if (eth == 0) eth = new PIEthernet();
|
||||
}
|
||||
if (type_send == PIProtocol::Ethernet && eth != 0) {
|
||||
eth->setSendIP(ip);
|
||||
if (ip.isEmpty()) devSenderName = "no ip";
|
||||
else devSenderName = ip + ":" + PIString::fromNumber(eth->sendPort());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIProtocol::setSenderPort(int port, bool force) {
|
||||
if (force) {
|
||||
type_send = PIProtocol::Ethernet;
|
||||
if (eth == 0) eth = new PIEthernet();
|
||||
eth->setName("__S__PIProtocol::eth");
|
||||
}
|
||||
if (type_send == PIProtocol::Ethernet && eth != 0) {
|
||||
eth->setSendPort(port);
|
||||
if (eth->sendIP().isEmpty()) devSenderName = "no ip";
|
||||
else devSenderName = eth->sendIP() + ":" + PIString::fromNumber(port);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIProtocol::setExpectedFrequency(float frequency) {
|
||||
exp_freq = frequency;
|
||||
changeDisconnectTimeout();
|
||||
}
|
||||
|
||||
|
||||
void PIProtocol::changeDisconnectTimeout() {
|
||||
pckt_cnt_max = int(round(timeout_ * exp_freq));
|
||||
if (pckt_cnt_max < 3) pckt_cnt_max = 3;
|
||||
last_packets.resize(pckt_cnt_max);
|
||||
}
|
||||
|
||||
|
||||
void PIProtocol::startReceive(float exp_frequency) {
|
||||
if (exp_frequency > 0.f) exp_freq = exp_frequency;
|
||||
//if (type_rec == PIProtocol::Serial) ser->start();
|
||||
//if (type_rec == PIProtocol::Ethernet) eth->start();
|
||||
packet_ext->startThreadedRead();
|
||||
msleep(1);
|
||||
check_state();
|
||||
if (exp_freq <= 0.f) return;
|
||||
setExpectedFrequency(exp_freq);
|
||||
diagTimer->start(1000. / exp_freq);
|
||||
diag_tm.reset();
|
||||
receiverStarted();
|
||||
}
|
||||
|
||||
|
||||
void PIProtocol::startSend(float frequency) {
|
||||
//cout << "** start send " << send_freq << ", " << frequency << endl;
|
||||
if (frequency > 0.f) send_freq = frequency;
|
||||
msleep(1);
|
||||
check_state();
|
||||
if (send_freq <= 0.f) return;
|
||||
sendTimer->start(1000. / send_freq);
|
||||
diag_tm.reset();
|
||||
senderStarted();
|
||||
}
|
||||
|
||||
|
||||
void PIProtocol::stopReceive() {
|
||||
//if (type_rec == PIProtocol::Serial) ser->stop();
|
||||
//if (type_rec == PIProtocol::Ethernet) eth->stop();
|
||||
packet_ext->stop();
|
||||
diagTimer->stop();
|
||||
receiverStopped();
|
||||
}
|
||||
|
||||
|
||||
bool PIProtocol::receiveEvent(void * t, uchar * data, int size) {
|
||||
PIProtocol * p = (PIProtocol * )t;
|
||||
if (!p->receive(data, size)) return false;
|
||||
p->work = true;
|
||||
//p->lock();
|
||||
if (p->validate()) {
|
||||
if (p->history_write_rec) {
|
||||
p->history_file_rec.writeToBinLog(p->history_id_rec, data, size);
|
||||
p->history_rsize_rec.setReadableSize(p->history_file_rec.pos());
|
||||
}
|
||||
p->received(true);
|
||||
//p->unlock();
|
||||
p->ifreq = p->diag_tm.elapsed_m();
|
||||
if (p->ifreq > 0.) p->ifreq = 1000. / p->ifreq;
|
||||
p->diag_tm.reset();
|
||||
p->receive_count++;
|
||||
p->packets_in_sec++;
|
||||
p->bytes_in_sec += size;
|
||||
p->cur_pckt = 1;
|
||||
if (p->ret_func != 0) p->ret_func(p);
|
||||
if (p->mp_owner != 0) PIMultiProtocolBase::receiveEvent(p->mp_owner, p, true, data, size);
|
||||
return true;
|
||||
}
|
||||
p->received(false);
|
||||
//p->unlock();
|
||||
p->wrong_count++;
|
||||
if (p->mp_owner != 0) PIMultiProtocolBase::receiveEvent(p->mp_owner, p, false, data, size);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void PIProtocol::diagEvent(void * t, int) {
|
||||
PIProtocol * p = (PIProtocol * )t;
|
||||
p->calc_freq();
|
||||
p->calc_diag();
|
||||
p->check_state();
|
||||
if (p->ser != 0) p->missed_count = p->packet_ext->missedPackets();
|
||||
}
|
||||
|
||||
|
||||
void PIProtocol::secEvent(void * t, int ) {
|
||||
PIProtocol * p = (PIProtocol * )t;
|
||||
p->speedIn = PIString::readableSize(p->bytes_in_sec) + "/s";
|
||||
p->speedOut = PIString::readableSize(p->bytes_out_sec) + "/s";
|
||||
p->bytes_in_sec = p->bytes_out_sec = p->packets_in_sec = p->packets_out_sec = 0;
|
||||
if (p->ser != 0) p->missed_count = p->packet_ext->missedPackets();
|
||||
}
|
||||
|
||||
|
||||
void PIProtocol::calc_diag() {
|
||||
PIProtocol::Quality diag;
|
||||
if (!work) {
|
||||
diag = PIProtocol::Unknown;
|
||||
return;
|
||||
}
|
||||
if (pckt_cnt < pckt_cnt_max) {
|
||||
last_packets[pckt_cnt] = cur_pckt;
|
||||
pckt_cnt++;
|
||||
} else {
|
||||
packets[(int)last_packets.back()]--;
|
||||
if (!last_packets.isEmpty()) last_packets.pop_back();
|
||||
last_packets.push_front(cur_pckt);
|
||||
}
|
||||
packets[(int)cur_pckt]++;
|
||||
cur_pckt = 0;
|
||||
float good_percents;
|
||||
good_percents = (float)packets[1] / pckt_cnt * 100.f;
|
||||
if (good_percents == 0.f) diag = PIProtocol::Failure;
|
||||
else if (good_percents <= 20.f) diag = PIProtocol::Bad;
|
||||
else if (good_percents > 20.f && good_percents <= 80.f) diag = PIProtocol::Average;
|
||||
else diag = PIProtocol::Good;
|
||||
if (diag != net_diag) {
|
||||
qualityChanged(diag, net_diag);
|
||||
net_diag = diag;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIProtocol::calc_freq() {
|
||||
float tf;// = float(1000.f / diagTimer->elapsed_m());
|
||||
tf = immediate_freq = ifreq;
|
||||
ifreq = 0.f;
|
||||
if (last_freq.size_s() >= pckt_cnt_max && last_freq.size_s() > 0) last_freq.pop_front();
|
||||
last_freq.push_back(tf);
|
||||
tf = last_freq[0];
|
||||
for (uint i = 1; i < last_freq.size(); ++i)
|
||||
tf += last_freq[i];
|
||||
integral_freq = tf / last_freq.size();
|
||||
}
|
||||
|
||||
|
||||
void PIProtocol::check_state() {
|
||||
if (type_rec == PIProtocol::Serial) {
|
||||
if (ser != 0) {
|
||||
if (ser->isOpened()) devReceiverState = "Opened";
|
||||
else devReceiverState = "Not opened";
|
||||
}
|
||||
else devReceiverState = "Not exists";
|
||||
}
|
||||
if (type_rec == PIProtocol::Ethernet) {
|
||||
if (eth != 0) {
|
||||
if (eth->isOpened()) devReceiverState = "Opened";
|
||||
else devReceiverState = "Not opened";
|
||||
}
|
||||
else devReceiverState = "Not exists";
|
||||
}
|
||||
if (type_send == PIProtocol::Serial) {
|
||||
if (ser != 0) {
|
||||
if (ser->isOpened()) devSenderState = "Opened";
|
||||
else devSenderState = "Not opened";
|
||||
}
|
||||
else devSenderState = "Not exists";
|
||||
}
|
||||
if (type_send == PIProtocol::Ethernet) {
|
||||
if (eth != 0) {
|
||||
if (eth->isOpened()) devSenderState = "Opened";
|
||||
else devSenderState = "Not opened";
|
||||
}
|
||||
else devSenderState = "Not exists";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIProtocol::send(const void * data, int size, bool direct) {
|
||||
if (!direct) {
|
||||
if (data == 0 || size == 0) return;
|
||||
if (!aboutSend()) return;
|
||||
}
|
||||
if (history_write_send) {
|
||||
history_file_send.writeToBinLog(history_id_send, data, size);
|
||||
history_rsize_send.setReadableSize(history_file_send.pos());
|
||||
}
|
||||
if (type_send == PIProtocol::Serial)
|
||||
if (ser->send(data, size)) {
|
||||
send_count++;
|
||||
packets_out_sec++;
|
||||
bytes_out_sec += size;
|
||||
}
|
||||
if (type_send == PIProtocol::Ethernet)
|
||||
if (eth->send(data, size)) {
|
||||
send_count++;
|
||||
packets_out_sec++;
|
||||
bytes_out_sec += size;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIProtocol::send() {
|
||||
//lock();
|
||||
//memcpy(packet, sendDataPtr, sendDataSize);
|
||||
//unlock();
|
||||
//cout << "**send" << endl;
|
||||
if (!aboutSend()) return;
|
||||
if (sendDataPtr == 0 || sendDataSize == 0) return;
|
||||
if (history_write_send) {
|
||||
history_file_send.writeToBinLog(history_id_send, sendDataPtr, sendDataSize);
|
||||
history_rsize_send.setReadableSize(history_file_send.pos());
|
||||
}
|
||||
if (type_send == PIProtocol::Serial)
|
||||
if (ser->send(sendDataPtr, sendDataSize)) {
|
||||
send_count++;
|
||||
packets_out_sec++;
|
||||
bytes_out_sec += sendDataSize;
|
||||
}
|
||||
if (type_send == PIProtocol::Ethernet)
|
||||
if (eth->send(sendDataPtr, sendDataSize)) {
|
||||
send_count++;
|
||||
packets_out_sec++;
|
||||
bytes_out_sec += sendDataSize;
|
||||
}
|
||||
}
|
||||
249
src/io/piprotocol.h
Executable file
249
src/io/piprotocol.h
Executable file
@@ -0,0 +1,249 @@
|
||||
/*! \file piprotocol.h
|
||||
* \brief Highly configurable from file I/O channel
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Protocol, input/output channel (COM, UDP)
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com, Bychkov Andrey wapmobil@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIPROTOCOL_H
|
||||
#define PIPROTOCOL_H
|
||||
|
||||
#include "piserial.h"
|
||||
#include "piethernet.h"
|
||||
#include "pipacketextractor.h"
|
||||
#include "pitimer.h"
|
||||
#include "piconfig.h"
|
||||
#include "math.h"
|
||||
|
||||
class PIProtocol;
|
||||
|
||||
class PIP_EXPORT PIMultiProtocolBase: protected PIObject
|
||||
{
|
||||
PIOBJECT(PIMultiProtocolBase)
|
||||
friend class PIProtocol;
|
||||
public:
|
||||
PIMultiProtocolBase() {;}
|
||||
virtual ~PIMultiProtocolBase() {;}
|
||||
|
||||
protected:
|
||||
virtual void received(PIProtocol * prot, bool corrected, uchar * data, int size) {;}
|
||||
|
||||
private:
|
||||
static void receiveEvent(PIMultiProtocolBase * p, PIProtocol * prot, bool corrected, uchar * data, int size) {p->mutex_receive.lock(); p->received(prot, corrected, data, size); p->mutex_receive.unlock();}
|
||||
|
||||
PIMutex mutex_receive;
|
||||
|
||||
};
|
||||
|
||||
typedef void (*ReceiveFunc)(void * );
|
||||
|
||||
/// events:
|
||||
/// void receiverStarted()
|
||||
/// void receiverStopped()
|
||||
/// void senderStarted()
|
||||
/// void senderStopped()
|
||||
/// void received(bool validate_is_ok)
|
||||
/// void qualityChanged(PIProtocol::Quality old_quality, PIProtocol::Quality new_quality)
|
||||
///
|
||||
/// handlers:
|
||||
/// void startReceive(float exp_frequency = -1.f)
|
||||
/// void stopReceive()
|
||||
/// void startSend(float frequency = -1.f)
|
||||
/// void stopSend()
|
||||
/// void start()
|
||||
/// void stop()
|
||||
/// void send()
|
||||
/// void send(const void * data, int size, bool direct = false)
|
||||
class PIP_EXPORT PIProtocol: public PIObject
|
||||
{
|
||||
PIOBJECT(PIProtocol)
|
||||
friend class PIMultiProtocolBase;
|
||||
friend class PIMultiProtocol;
|
||||
enum Type {None, Serial, Ethernet};
|
||||
public:
|
||||
|
||||
//! Contructs an empty unconfigured protocol
|
||||
PIProtocol(): PIObject() {init();}
|
||||
|
||||
//! Contructs protocol configured from file "config", config file section "name"
|
||||
PIProtocol(const PIString & config, const PIString & name, void * recHeaderPtr = 0, int recHeaderSize = 0,
|
||||
void * recDataPtr = 0, int recDataSize = 0, void * sendDataPtr = 0, int sendDataSize = 0); // from config
|
||||
|
||||
virtual ~PIProtocol();
|
||||
|
||||
//! Connection quality
|
||||
enum Quality {
|
||||
Unknown /** Unknown, no one packet received yet */ = 1,
|
||||
Failure /** No connection, no one correct packet received for last period */ = 2,
|
||||
Bad /** Bad connection, correct packets received <= 20% */ = 3,
|
||||
Average /** Average connection, correct packets received > 20% and <= 80% */ = 4,
|
||||
Good /** Good connection, correct packets received > 80% */ = 5
|
||||
};
|
||||
|
||||
EVENT_HANDLER0(void, startReceive) {startReceive(-1.f);}
|
||||
EVENT_HANDLER1(void, startReceive, float, exp_frequency); // if "frequency = -1" used last passed value
|
||||
EVENT_HANDLER0(void, stopReceive);
|
||||
void setExpectedFrequency(float frequency); // for connection quality diagnostic
|
||||
void setReceiverDevice(const PIString & device, PISerial::Speed speed, bool force = false); // for Serial
|
||||
void setReceiverData(void * dataPtr, int dataSize) {this->dataPtr = (uchar * )dataPtr; this->dataSize = dataSize; packet_ext->setHeader(PIByteArray(headerPtr, headerSize)); packet_ext->setPayloadSize(dataSize); packet_ext->setPacketSize(dataSize);}
|
||||
void setReceiverDataHeader(void * headerPtr, int headerSize) {this->headerPtr = (uchar * )headerPtr; this->headerSize = headerSize; packet_ext->setHeader(PIByteArray(headerPtr, headerSize)); packet_ext->setPayloadSize(dataSize); packet_ext->setPacketSize(dataSize);}
|
||||
void setReceiverAddress(const PIString & ip, int port, bool force = false); // for Ethernet
|
||||
void setReceiverParameters(PIFlags<PISerial::Parameters> parameters) {if (type_rec == PIProtocol::Serial || type_send == PIProtocol::Serial) ser->setParameters(parameters);} // for Serial
|
||||
void setReceiveSlot(ReceiveFunc slot) {ret_func = slot;}
|
||||
float expectedFrequency() const {return exp_freq;}
|
||||
|
||||
EVENT_HANDLER0(void, startSend) {startSend(-1.f);} // if "frequency = -1" used last passed value
|
||||
EVENT_HANDLER1(void, startSend, float, frequency); // if "frequency = -1" used last passed value
|
||||
EVENT_HANDLER0(void, stopSend) {sendTimer->stop(); senderStopped();}
|
||||
void setSenderFrequency(float frequency) {send_freq = frequency;}
|
||||
void setSenderDevice(const PIString & device, PISerial::Speed speed, bool force = false); // for Serial
|
||||
void setSenderData(void * dataPtr, int dataSize) {sendDataPtr = (uchar * )dataPtr; sendDataSize = dataSize;}
|
||||
void setSenderAddress(const PIString & ip, int port, bool force = false); // for Ethernet
|
||||
void setSenderIP(const PIString & ip, bool force = false); // for Ethernet
|
||||
void setSenderPort(int port, bool force = false); // for Ethernet
|
||||
void setSenderParameters(PIFlags<PISerial::Parameters> parameters) {if (type_send == PIProtocol::Serial) ser->setParameters(parameters);} // for Serial
|
||||
float senderFrequency() const {return send_freq;}
|
||||
|
||||
EVENT_HANDLER0(void, start) {startReceive(); startSend();}
|
||||
EVENT_HANDLER0(void, stop) {stopReceive(); stopSend();}
|
||||
EVENT_HANDLER0(void, send);
|
||||
EVENT_HANDLER2(void, send, const void *, data, int, size) {send(data, size, false);}
|
||||
EVENT_HANDLER3(void, send, const void *, data, int, size, bool, direct);
|
||||
|
||||
void setName(const PIString & name) {protName = name; PIObject::setName(name);}
|
||||
PIString name() const {return protName;}
|
||||
void setDisconnectTimeout(float timeout) {timeout_ = timeout; changeDisconnectTimeout();}
|
||||
float disconnectTimeout() const {return timeout_;}
|
||||
const float * disconnectTimeout_ptr() const {return &timeout_;}
|
||||
float immediateFrequency() const {return immediate_freq;}
|
||||
float integralFrequency() const {return integral_freq;}
|
||||
const float * immediateFrequency_ptr() const {return &immediate_freq;}
|
||||
const float * integralFrequency_ptr() const {return &integral_freq;}
|
||||
ullong receiveCountPerSec() const {return packets_in_sec;}
|
||||
const ullong * receiveCountPerSec_ptr() const {return &packets_in_sec;}
|
||||
ullong sendCountPerSec() const {return packets_out_sec;}
|
||||
const ullong * sendCountPerSec_ptr() const {return &packets_out_sec;}
|
||||
ullong receiveBytesPerSec() const {return bytes_in_sec;}
|
||||
const ullong * receiveBytesPerSec_ptr() const {return &bytes_in_sec;}
|
||||
ullong sendBytesPerSec() const {return bytes_out_sec;}
|
||||
const ullong * sendBytesPerSec_ptr() const {return &bytes_out_sec;}
|
||||
ullong receiveCount() const {return receive_count;}
|
||||
const ullong * receiveCount_ptr() const {return &receive_count;}
|
||||
ullong wrongCount() const {return wrong_count;}
|
||||
const ullong * wrongCount_ptr() const {return &wrong_count;}
|
||||
ullong sendCount() const {return send_count;}
|
||||
const ullong * sendCount_ptr() const {return &send_count;}
|
||||
ullong missedCount() const {return missed_count;}
|
||||
const ullong * missedCount_ptr() const {return &missed_count;}
|
||||
PIProtocol::Quality quality() const {return net_diag;} // receive quality
|
||||
const int * quality_ptr() const {return (int * )&net_diag;} // receive quality pointer
|
||||
PIString receiverDeviceName() const {return devReceiverName;}
|
||||
PIString senderDeviceName() const {return devSenderName;}
|
||||
PIString receiverDeviceState() const {return devReceiverState;}
|
||||
const PIString * receiverDeviceState_ptr() const {return &devReceiverState;}
|
||||
PIString senderDeviceState() const {return devSenderState;}
|
||||
const PIString * senderDeviceState_ptr() const {return &devSenderState;}
|
||||
PIString receiveSpeed() const {return speedIn;}
|
||||
const PIString * receiveSpeed_ptr() const {return &speedIn;}
|
||||
PIString sendSpeed() const {return speedOut;}
|
||||
const PIString * sendSpeed_ptr() const {return &speedOut;}
|
||||
PIString receiverHistorySize() const {return history_rsize_rec;}
|
||||
const PIString * receiverHistorySize_ptr() const {return &history_rsize_rec;}
|
||||
PIString senderHistorySize() const {return history_rsize_send;}
|
||||
const PIString * senderHistorySize_ptr() const {return &history_rsize_send;}
|
||||
bool writeReceiverHistory() const {return history_write_rec;}
|
||||
const bool * writeReceiverHistory_ptr() const {return &history_write_rec;}
|
||||
bool writeSenderHistory() const {return history_write_send;}
|
||||
const bool * writeSenderHistory_ptr() const {return &history_write_send;}
|
||||
|
||||
void * receiveData() {return dataPtr;}
|
||||
void * sendData() {return sendDataPtr;}
|
||||
|
||||
PIPacketExtractor * packetExtractor() {return packet_ext;}
|
||||
PIByteArray lastHeader() {return packet_ext->lastHeader();}
|
||||
|
||||
EVENT0(receiverStarted)
|
||||
EVENT0(receiverStopped)
|
||||
EVENT0(senderStarted)
|
||||
EVENT0(senderStopped)
|
||||
EVENT1(received, bool, validate_is_ok)
|
||||
EVENT2(qualityChanged, PIProtocol::Quality, new_quality, PIProtocol::Quality, old_quality)
|
||||
|
||||
protected:
|
||||
virtual bool receive(uchar * data, int size) {if (dataPtr != 0) memcpy(dataPtr, data, size); return true;} // executed when raw data received, break if 'false' return
|
||||
virtual bool validate() {return true;} // function for validate algorithm and save data from dataPtr to external struct
|
||||
virtual bool headerValidate(uchar * src, uchar * rec, int size) {for (int i = 0; i < size; ++i) if (src[i] != rec[i]) return false; return true;} // function for validate header (COM-port and headerSize > 0)
|
||||
virtual uint checksum_i(void * data, int size) { // function for checksum (uint)
|
||||
uint c = 0;
|
||||
for (int i = 0; i < size; ++i)
|
||||
c += ((uchar*)data)[i];
|
||||
return ~(c + 1);
|
||||
}
|
||||
virtual uchar checksum_c(void * data, int size) { // function for checksum (uchar)
|
||||
uchar c = 0;
|
||||
for (int i = 0; i < size; ++i)
|
||||
c += ((uchar*)data)[i];
|
||||
return ~(c + 1);
|
||||
}
|
||||
virtual bool aboutSend() {return true;} // executed before send data, if return 'false' then data is not sending
|
||||
|
||||
void init();
|
||||
void init_sender(PIConfig::Entry & b, PIConfig::Entry & sb, const PIString & config);
|
||||
void init_receiver(PIConfig::Entry & b, PIConfig::Entry & rb, const PIString & config);
|
||||
void check_state();
|
||||
void calc_freq();
|
||||
void calc_diag();
|
||||
|
||||
PISerial * ser;
|
||||
PIEthernet * eth;
|
||||
uint dataSize, headerSize, sendDataSize;
|
||||
uchar * dataPtr, * headerPtr, * sendDataPtr;
|
||||
|
||||
private:
|
||||
static void sendEvent(void * e, int) {((PIProtocol * )e)->send();}
|
||||
static bool receiveEvent(void * t, uchar * data, int size);
|
||||
static bool headerValidateEvent(void * t, uchar * src, uchar * rec, int size) {return ((PIProtocol * )t)->headerValidate(src, rec, size);}
|
||||
static void diagEvent(void * t, int);
|
||||
static void secEvent(void * t, int);
|
||||
|
||||
void setMultiProtocolOwner(PIMultiProtocolBase * mp) {mp_owner = mp;}
|
||||
PIMultiProtocolBase * multiProtocolOwner() const {return mp_owner;}
|
||||
void changeDisconnectTimeout();
|
||||
|
||||
ReceiveFunc ret_func;
|
||||
PIPacketExtractor * packet_ext;
|
||||
PITimer * diagTimer, * sendTimer, * secTimer;
|
||||
PITimeMeasurer diag_tm;
|
||||
PIMultiProtocolBase * mp_owner;
|
||||
PIProtocol::Type type_send, type_rec;
|
||||
PIProtocol::Quality net_diag;
|
||||
PIDeque<float> last_freq;
|
||||
PIDeque<char> last_packets;
|
||||
PIString protName, devReceiverName, devReceiverState, devSenderName, devSenderState, speedIn, speedOut;
|
||||
PIString history_path_rec, history_path_send, history_rsize_rec, history_rsize_send;
|
||||
PIFile history_file_rec, history_file_send;
|
||||
ushort history_id_rec, history_id_send;
|
||||
bool work, new_mp_prot, history_write_rec, history_write_send;
|
||||
float exp_freq, send_freq, ifreq, immediate_freq, integral_freq, timeout_;
|
||||
int packets[2], pckt_cnt, pckt_cnt_max;
|
||||
char cur_pckt;
|
||||
ullong wrong_count, receive_count, send_count, missed_count, packets_in_sec, packets_out_sec, bytes_in_sec, bytes_out_sec;
|
||||
|
||||
};
|
||||
|
||||
#endif // PIPROTOCOL_H
|
||||
664
src/io/piserial.cpp
Executable file
664
src/io/piserial.cpp
Executable file
@@ -0,0 +1,664 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
COM
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com, Bychkov Andrey wapmobil@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "piserial.h"
|
||||
#include "piconfig.h"
|
||||
#include "pidir.h"
|
||||
|
||||
|
||||
/*! \class PISerial
|
||||
* \brief Serial device
|
||||
*
|
||||
* \section PISerial_sec0 Synopsis
|
||||
* This class provide access to serial device, e.g. COM port. It can read,
|
||||
* write, wait for write. There are several read and write functions.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
REGISTER_DEVICE(PISerial);
|
||||
|
||||
|
||||
PISerial::PISerial(): PIIODevice("", ReadWrite) {
|
||||
_init();
|
||||
}
|
||||
|
||||
|
||||
PISerial::PISerial(const PIString & device_, PISerial::Speed speed_, PIFlags<PISerial::Parameters> params_): PIIODevice(device_, ReadWrite) {
|
||||
_init();
|
||||
setPath(device_);
|
||||
setSpeed(speed_);
|
||||
setParameters(params_);
|
||||
}
|
||||
|
||||
|
||||
PISerial::~PISerial() {
|
||||
piMonitor.serials--;
|
||||
}
|
||||
|
||||
|
||||
void PISerial::_init() {
|
||||
fd = -1;
|
||||
piMonitor.serials++;
|
||||
setPriority(piHigh);
|
||||
block_read = true;
|
||||
vtime = 1;
|
||||
#ifdef WINDOWS
|
||||
block_write = true;
|
||||
hCom = 0;
|
||||
#endif
|
||||
setParameters(0);
|
||||
setSpeed(S115200);
|
||||
setDataBitsCount(8);
|
||||
//init();
|
||||
}
|
||||
|
||||
|
||||
void PISerial::setParameter(PISerial::Parameters parameter, bool on) {
|
||||
PIFlags<Parameters> cp = (PIFlags<Parameters>)(property("parameters").toInt());
|
||||
cp.setFlag(parameter, on);
|
||||
setParameters(cp);
|
||||
}
|
||||
|
||||
|
||||
bool PISerial::isParameterSet(PISerial::Parameters parameter) const {
|
||||
PIFlags<Parameters> cp = (PIFlags<Parameters>)(property("parameters").toInt());
|
||||
return cp[parameter];
|
||||
}
|
||||
|
||||
|
||||
bool PISerial::setPin(int number, bool on) {
|
||||
switch (number) {
|
||||
case 1: return setCAR(on); break;
|
||||
case 2: return setSR(on); break;
|
||||
case 3: return setST(on); break;
|
||||
case 4: return setDTR(on); break;
|
||||
case 5:
|
||||
piCoutObj << "Pin number 5 is ground";
|
||||
return false;
|
||||
case 6: return setDSR(on); break;
|
||||
case 7: return setRTS(on); break;
|
||||
case 8: return setCTS(on); break;
|
||||
case 9: return setRNG(on); break;
|
||||
default:
|
||||
piCoutObj << "Pin number " << number << " doesn`t exists!";
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool PISerial::isPin(int number) const {
|
||||
switch (number) {
|
||||
case 1: return isCAR(); break;
|
||||
case 2: return isSR(); break;
|
||||
case 3: return isST(); break;
|
||||
case 4: return isDTR(); break;
|
||||
case 5: return false;
|
||||
case 6: return isDSR(); break;
|
||||
case 7: return isRTS(); break;
|
||||
case 8: return isCTS(); break;
|
||||
case 9: return isRNG(); break;
|
||||
default:
|
||||
piCoutObj << "Pin number " << number << " doesn`t exists!";
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool PISerial::setBit(int bit, bool on, const PIString & bname) {
|
||||
#ifndef WINDOWS
|
||||
if (fd < 0) {
|
||||
piCoutObj << "setBit" << bname << " error: \"" << path() << "\" is not opened!";
|
||||
return false;
|
||||
}
|
||||
if (ioctl(fd, on ? TIOCMBIS : TIOCMBIC, &bit) < 0) {
|
||||
piCoutObj << "setBit" << bname << " error: " << errorString();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
piCoutObj << "setBit" << bname << " doesn`t implemented on Windows, sorry :-(";
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool PISerial::isBit(int bit, const PIString & bname) const {
|
||||
#ifndef WINDOWS
|
||||
if (fd < 0) {
|
||||
piCoutObj << "isBit" << bname << " error: \"" << path() << "\" is not opened!";
|
||||
return false;
|
||||
}
|
||||
int ret = 0;
|
||||
if (ioctl(fd, TIOCMGET, &ret) < 0)
|
||||
piCoutObj << "isBit" << bname << " error: " << errorString();
|
||||
return ret & bit;
|
||||
#else
|
||||
piCoutObj << "isBit" << bname << " doesn`t implemented on Windows, sorry :-(";
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool PISerial::closeDevice() {
|
||||
if (!isInitialized()) return true;
|
||||
if (isRunning()) {
|
||||
stop();
|
||||
PIThread::terminate();
|
||||
}
|
||||
if (fd != -1) {
|
||||
#ifdef WINDOWS
|
||||
SetCommState(hCom, &sdesc);
|
||||
SetCommMask(hCom, mask);
|
||||
CloseHandle(hCom);
|
||||
hCom = 0;
|
||||
#else
|
||||
tcsetattr(fd, TCSANOW, &sdesc);
|
||||
::close(fd);
|
||||
#endif
|
||||
fd = -1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int PISerial::convertSpeed(PISerial::Speed speed) {
|
||||
switch (speed) {
|
||||
case S50: return B50;
|
||||
case S75: return B75;
|
||||
case S110: return B110;
|
||||
case S300: return B300;
|
||||
case S600: return B600;
|
||||
case S1200: return B1200;
|
||||
case S2400: return B2400;
|
||||
case S4800: return B4800;
|
||||
case S9600: return B9600;
|
||||
case S19200: return B19200;
|
||||
case S38400: return B38400;
|
||||
case S57600: return B57600;
|
||||
case S115200: return B115200;
|
||||
case S1500000: return B1500000;
|
||||
case S2000000: return B2000000;
|
||||
case S2500000: return B2500000;
|
||||
case S3000000: return B3000000;
|
||||
case S3500000: return B3500000;
|
||||
case S4000000: return B4000000;
|
||||
default: break;
|
||||
}
|
||||
return B115200;
|
||||
}
|
||||
|
||||
|
||||
/** \brief Advanced read function
|
||||
* \details Read to pointer "read_to" no more than "max_size" and no longer
|
||||
* than "timeout_ms" milliseconds. If "timeout_ms" < 0 function will be
|
||||
* wait forever until "max_size" will be readed. If size <= 0 function
|
||||
* immediate returns \b false. For read data with unknown size use function
|
||||
* \a readData().
|
||||
* \returns \b True if readed bytes count = "max_size", else \b false
|
||||
* \sa \a readData() */
|
||||
bool PISerial::read(void * data, int size, double timeout_ms) {
|
||||
if (data == 0 || size <= 0) return false;
|
||||
int ret, all = 0;
|
||||
if (timeout_ms > 0.) {
|
||||
setReadIsBlocking(false);
|
||||
all = read(data, 1);
|
||||
tm_.reset();
|
||||
while (all < size && tm_.elapsed_m() < timeout_ms) {
|
||||
ret = read(&((uchar * )data)[all], size - all);
|
||||
if (ret > 0) all += ret;
|
||||
else msleep(1);
|
||||
}
|
||||
received(data, all);
|
||||
return (all == size);
|
||||
} else {
|
||||
setReadIsBlocking(true);
|
||||
all = read(data, 1);
|
||||
while (all < size) {
|
||||
ret = read(&((uchar * )data)[all], size - all);
|
||||
if (ret > 0) all += ret;
|
||||
}
|
||||
received(data, all);
|
||||
return (all == size);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** \brief Advanced read function
|
||||
* \details Read all or no more than "size" and no longer than
|
||||
* "timeout_ms" milliseconds. If "timeout_ms" < 0 function will be
|
||||
* wait forever until "size" will be readed. If "size" <= 0
|
||||
* function will be read all until "timeout_ms" elaped. \n If size <= 0
|
||||
* and "timeout_ms" <= 0 function immediate returns empty string.
|
||||
* \n This function similar to \a readData() but returns data as string.
|
||||
* \sa \a readData() */
|
||||
PIString PISerial::read(int size, double timeout_ms) {
|
||||
PIString str;
|
||||
if (size <= 0 && timeout_ms <= 0.) return str;
|
||||
int ret, all = 0;
|
||||
uchar td[1024];
|
||||
if (timeout_ms > 0.) {
|
||||
setReadIsBlocking(false);
|
||||
tm_.reset();
|
||||
if (size <= 0) {
|
||||
while (tm_.elapsed_m() < timeout_ms) {
|
||||
ret = read(td, 1024);
|
||||
if (ret <= 0) msleep(1);
|
||||
else str << PIString((char*)td, ret);
|
||||
}
|
||||
} else {
|
||||
while (all < size && tm_.elapsed_m() < timeout_ms) {
|
||||
ret = read(td, size - all);
|
||||
if (ret <= 0) msleep(1);
|
||||
else {
|
||||
str << PIString((char*)td, ret);
|
||||
all += ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setReadIsBlocking(true);
|
||||
all = read(td, 1);
|
||||
str << PIString((char*)td, all);
|
||||
while (all < size) {
|
||||
ret = read(td, size - all);
|
||||
if (ret <= 0) msleep(1);
|
||||
else {
|
||||
str << PIString((char*)td, ret);
|
||||
all += ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
received(str.data(), str.size_s());
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
/** \brief Advanced read function
|
||||
* \details Read all or no more than "size" and no longer than
|
||||
* "timeout_ms" milliseconds. If "timeout_ms" < 0 function will be
|
||||
* wait forever until "size" will be readed. If "size" <= 0
|
||||
* function will be read all until "timeout_ms" elaped. \n If size <= 0
|
||||
* and "timeout_ms" <= 0 function immediate returns empty byte array.
|
||||
* \n This function similar to \a read() but returns data as byte array.
|
||||
* \sa \a read() */
|
||||
PIByteArray PISerial::readData(int size, double timeout_ms) {
|
||||
PIByteArray str;
|
||||
if (size <= 0 && timeout_ms <= 0.) return str;
|
||||
int ret, all = 0;
|
||||
uchar td[1024];
|
||||
if (timeout_ms > 0.) {
|
||||
setReadIsBlocking(false);
|
||||
tm_.reset();
|
||||
if (size <= 0) {
|
||||
while (tm_.elapsed_m() < timeout_ms) {
|
||||
ret = read(td, 1024);
|
||||
if (ret <= 0) msleep(1);
|
||||
else str.append(td, ret);
|
||||
}
|
||||
} else {
|
||||
while (all < size && tm_.elapsed_m() < timeout_ms) {
|
||||
ret = read(td, size - all);
|
||||
if (ret <= 0) msleep(1);
|
||||
else {
|
||||
str.append(td, ret);
|
||||
all += ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setReadIsBlocking(true);
|
||||
all = read(td, 1);
|
||||
str.append(td, all);
|
||||
while (all < size) {
|
||||
ret = read(td, size - all);
|
||||
if (ret <= 0) msleep(1);
|
||||
else {
|
||||
str.append(td, ret);
|
||||
all += ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
received(str.data(), str.size_s());
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
bool PISerial::openDevice() {
|
||||
//piCout << "ser open" << path();
|
||||
if (path().isEmpty()) return false;
|
||||
#ifdef WINDOWS
|
||||
DWORD ds = 0, sm = 0;
|
||||
if (isReadable()) {ds |= GENERIC_READ; sm |= FILE_SHARE_READ;}
|
||||
if (isWriteable()) {ds |= GENERIC_WRITE; sm |= FILE_SHARE_WRITE;}
|
||||
PIString wp = "//./" + path();
|
||||
hCom = CreateFileA(wp.data(), ds, sm, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0);
|
||||
if (hCom == INVALID_HANDLE_VALUE) {
|
||||
piCoutObj << "Unable to open \"" << path() << "\"";
|
||||
fd = -1;
|
||||
return false;
|
||||
}
|
||||
fd = 0;
|
||||
#else
|
||||
int om = 0;
|
||||
switch (mode()) {
|
||||
case PIIODevice::ReadOnly: om = O_RDONLY; break;
|
||||
case PIIODevice::WriteOnly: om = O_WRONLY; break;
|
||||
case PIIODevice::ReadWrite: om = O_RDWR; break;
|
||||
}
|
||||
//cout << "init ser " << path_ << " mode " << om << " param " << params << endl;
|
||||
fd = ::open(path().data(), O_NOCTTY | om);
|
||||
if (fd == -1) {
|
||||
piCoutObj << "Unable to open \"" << path() << "\"";
|
||||
return false;
|
||||
}
|
||||
tcgetattr(fd, &desc);
|
||||
sdesc = desc;
|
||||
//piCoutObj << "Initialized " << path_;
|
||||
#endif
|
||||
applySettings();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void PISerial::applySettings() {
|
||||
#ifdef WINDOWS
|
||||
if (fd == -1) return;
|
||||
COMMTIMEOUTS times;
|
||||
times.ReadIntervalTimeout = block_read ? vtime : MAXDWORD;
|
||||
times.ReadTotalTimeoutConstant = block_read ? 0 : 1;
|
||||
times.ReadTotalTimeoutMultiplier = block_read ? 0 : MAXDWORD;
|
||||
times.WriteTotalTimeoutConstant = 0;
|
||||
times.WriteTotalTimeoutMultiplier = block_write ? 0 : 1;
|
||||
if (SetCommTimeouts(hCom, ×) == -1)
|
||||
piCoutObj << "Unable to set timeouts for \"" << path() << "\"";
|
||||
GetCommMask(hCom, &mask);
|
||||
SetCommMask(hCom, EV_RXCHAR);
|
||||
GetCommState(hCom, &sdesc);
|
||||
desc = sdesc;
|
||||
desc.DCBlength = sizeof(desc);
|
||||
desc.BaudRate = convertSpeed(outSpeed());
|
||||
if (dataBitsCount() >= 5 && dataBitsCount() <= 8)
|
||||
desc.ByteSize = dataBitsCount();
|
||||
else
|
||||
desc.ByteSize = 8;
|
||||
PIFlags<Parameters> params = parameters();
|
||||
if (params[PISerial::ParityControl]) {
|
||||
desc.fParity = 1;
|
||||
desc.Parity = params[PISerial::ParityOdd] ? 1 : 2;
|
||||
}
|
||||
desc.StopBits = params[PISerial::TwoStopBits] ? TWOSTOPBITS : ONESTOPBIT;
|
||||
if (SetCommState(hCom, &desc) == -1) {
|
||||
piCoutObj << "Unable to set comm state for \"" << path() << "\"";
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if (fd == -1) return;
|
||||
tcgetattr(fd, &desc);
|
||||
desc.c_oflag = desc.c_lflag = desc.c_cflag = 0;
|
||||
desc.c_iflag = IGNBRK;
|
||||
desc.c_cflag = CLOCAL | HUPCL;
|
||||
switch (dataBitsCount()) {
|
||||
case 5: desc.c_cflag |= (CSIZE & CS5); break;
|
||||
case 6: desc.c_cflag |= (CSIZE & CS6); break;
|
||||
case 7: desc.c_cflag |= (CSIZE & CS7); break;
|
||||
case 8: default: desc.c_cflag |= (CSIZE & CS8); break;
|
||||
};
|
||||
if (isReadable()) desc.c_cflag |= CREAD;
|
||||
PIFlags<Parameters> params = parameters();
|
||||
if (params[PISerial::TwoStopBits]) desc.c_cflag |= CSTOPB;
|
||||
if (params[PISerial::ParityControl]) {
|
||||
desc.c_iflag |= INPCK;
|
||||
desc.c_cflag |= PARENB;
|
||||
if (params[PISerial::ParityOdd]) desc.c_cflag |= PARODD;
|
||||
}
|
||||
desc.c_cc[VMIN] = 1;
|
||||
desc.c_cc[VTIME] = vtime;
|
||||
|
||||
cfsetispeed(&desc, convertSpeed(inSpeed()));
|
||||
cfsetospeed(&desc, convertSpeed(outSpeed()));
|
||||
|
||||
tcflush(fd, TCIOFLUSH);
|
||||
fcntl(fd, F_SETFL, block_read ? 0 : O_NONBLOCK);
|
||||
|
||||
if(tcsetattr(fd, TCSANOW, &desc) < 0) {
|
||||
piCoutObj << "Can`t set attributes for \"" << path() << "\"";
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void PISerial::setReadIsBlocking(bool yes) {
|
||||
block_read = yes;
|
||||
#ifdef WINDOWS
|
||||
COMMTIMEOUTS times;
|
||||
times.ReadIntervalTimeout = block_read ? vtime : MAXDWORD;
|
||||
times.ReadTotalTimeoutConstant = block_read ? 0 : 1;
|
||||
times.ReadTotalTimeoutMultiplier = block_read ? 0 : MAXDWORD;
|
||||
times.WriteTotalTimeoutConstant = 0;
|
||||
times.WriteTotalTimeoutMultiplier = block_write ? 0 : 1;
|
||||
if (isOpened()) SetCommTimeouts(hCom, ×);
|
||||
#else
|
||||
if (isOpened()) fcntl(fd, F_SETFL, yes ? 0 : O_NONBLOCK);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/** \brief Basic read function
|
||||
* \details Read to pointer "read_to" no more than "max_size". If read is
|
||||
* set to blocking this function will be wait at least one byte.
|
||||
* \returns Readed bytes count
|
||||
* \sa \a readData() */
|
||||
int PISerial::read(void * read_to, int max_size) {
|
||||
#ifdef WINDOWS
|
||||
if (!canRead()) return -1;
|
||||
WaitCommEvent(hCom, 0, 0);
|
||||
ReadFile(hCom, read_to, max_size, &readed, 0);
|
||||
return readed;
|
||||
#else
|
||||
if (!canRead()) return -1;
|
||||
return ::read(fd, read_to, max_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int PISerial::write(const void * data, int max_size, bool wait) {
|
||||
//piCoutObj << "send " << max_size << ": " << PIString((char*)data, max_size);
|
||||
if (fd == -1 || !canWrite()) {
|
||||
//piCoutObj << "Can`t write to uninitialized COM";
|
||||
return -1;
|
||||
}
|
||||
#ifdef WINDOWS
|
||||
if (block_write != wait) {
|
||||
block_write = wait;
|
||||
setReadIsBlocking(block_read);
|
||||
}
|
||||
DWORD wrote;
|
||||
WriteFile(hCom, data, max_size, &wrote, 0);
|
||||
#else
|
||||
int wrote;
|
||||
wrote = ::write(fd, data, max_size);
|
||||
if (wait) tcdrain(fd);
|
||||
#endif
|
||||
return (int)wrote;
|
||||
//piCoutObj << "Error while sending";
|
||||
//piCoutObj << "Wrote " << wrote << " bytes in " << path_;
|
||||
}
|
||||
|
||||
|
||||
bool PISerial::configureDevice(const void * e_main, const void * e_parent) {
|
||||
PIConfig::Entry * em = (PIConfig::Entry * )e_main;
|
||||
PIConfig::Entry * ep = (PIConfig::Entry * )e_parent;
|
||||
setDevice(readDeviceSetting<PIString>("device", device(), em, ep));
|
||||
setSpeed((PISerial::Speed)(readDeviceSetting<int>("speed", (int)outSpeed(), em, ep)));
|
||||
setDataBitsCount(readDeviceSetting<int>("dataBitsCount", dataBitsCount(), em, ep));
|
||||
setParameter(PISerial::ParityControl, readDeviceSetting<bool>("parityControl", isParameterSet(PISerial::ParityControl), em, ep));
|
||||
setParameter(PISerial::ParityOdd, readDeviceSetting<bool>("parityOdd", isParameterSet(PISerial::ParityOdd), em, ep));
|
||||
setParameter(PISerial::TwoStopBits, readDeviceSetting<bool>("twoStopBits", isParameterSet(PISerial::TwoStopBits), em, ep));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
PIString PISerial::constructFullPath() const {
|
||||
PIString ret(fullPathPrefix() + "://");
|
||||
ret << path() << ":" << int(inSpeed()) << ":" << dataBitsCount();
|
||||
if (parameters()[ParityControl]) {
|
||||
if (parameters()[ParityOdd]) ret << ":O";
|
||||
else ret << ":E";
|
||||
} else ret << ":N";
|
||||
if (parameters()[TwoStopBits]) ret << ":2";
|
||||
else ret << ":1";
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void PISerial::configureFromFullPath(const PIString & full_path) {
|
||||
PIStringList pl = full_path.split(":");
|
||||
for (int i = 0; i < pl.size_s(); ++i) {
|
||||
PIString p(pl[i]);
|
||||
switch (i) {
|
||||
case 0: setPath(p); break;
|
||||
case 1: setSpeed((Speed)(p.toInt())); break;
|
||||
case 2: setDataBitsCount(p.toInt()); break;
|
||||
case 3:
|
||||
p = p.toLowerCase();
|
||||
if (p != "n") setParameter(ParityControl);
|
||||
if (p == "o") setParameter(ParityOdd);
|
||||
break;
|
||||
case 4: if (p.toInt() == 2) setParameter(TwoStopBits); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PIVector<int> PISerial::availableSpeeds() {
|
||||
PIVector<int> spds;
|
||||
spds << 50 << 75 << 110 << 300 << 600 << 1200 << 2400 << 4800 <<
|
||||
9600 << 19200 << 38400 << 57600 << 115200 << 1500000 <<
|
||||
2000000 << 2500000 << 3000000 << 3500000 << 4000000;
|
||||
return spds;
|
||||
}
|
||||
|
||||
|
||||
PIStringList PISerial::availableDevices(bool test) {
|
||||
PIStringList dl;
|
||||
#ifdef WINDOWS
|
||||
HKEY key = 0;
|
||||
RegOpenKey(HKEY_LOCAL_MACHINE, (LPCTSTR)"HARDWARE\\DEVICEMAP\\SERIALCOMM", &key);
|
||||
if (key != 0) {
|
||||
char name[1024], data[1024];
|
||||
DWORD name_len = 1024, data_len = 1024, type = 0, index = 0;
|
||||
LONG ret;
|
||||
while ((ret = RegEnumValue(key, index, (LPTSTR)name, &name_len, NULL, &type, (uchar * )data, &data_len)) != ERROR_NO_MORE_ITEMS) {
|
||||
dl << PIString(data);
|
||||
index++;
|
||||
}
|
||||
RegCloseKey(key);
|
||||
}
|
||||
#else
|
||||
# ifndef ANDROID
|
||||
PIStringList prefixes;
|
||||
# ifdef QNX
|
||||
prefixes << "ser";
|
||||
# else
|
||||
prefixes << "ttyS" << "ttyO" << "ttyUSB" << "ttyACM" << "ttyGS"
|
||||
<< "ttyMI" << "ttymxc" << "ttyAMA" << "rfcomm" << "ircomm";
|
||||
# ifdef FREE_BSD
|
||||
prefixes << "cu";
|
||||
# endif
|
||||
PIFile file_prefixes("/proc/tty/drivers", PIIODevice::ReadOnly);
|
||||
if (file_prefixes.open()) {
|
||||
PIString fc = file_prefixes.readAll(true), line, cpref;
|
||||
PIStringList words;
|
||||
file_prefixes.close();
|
||||
while (!fc.isEmpty()) {
|
||||
words.clear();
|
||||
line = fc.takeLine();
|
||||
if (line.isEmpty()) break;
|
||||
while (!line.isEmpty())
|
||||
words << line.takeWord();
|
||||
if (words.size_s() < 2) break;
|
||||
if (words.back() != "serial") continue;
|
||||
cpref = words[1];
|
||||
int li = cpref.findLast("/");
|
||||
if (li > 0) cpref.cutLeft(li + 1);
|
||||
prefixes << cpref;
|
||||
}
|
||||
prefixes.removeDuplicates();
|
||||
}
|
||||
# endif
|
||||
PIDir dir("/dev");
|
||||
PIVector<PIFile::FileInfo> de = dir.entries();
|
||||
piForeachC (PIFile::FileInfo & e, de) { // TODO changes in FileInfo
|
||||
piForeachC (PIString & p, prefixes) {
|
||||
if (e.path.left(p.size_s()) != p) continue;
|
||||
dl << "/dev/" + e.path;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
if (test) {
|
||||
for (int i = 0; i < dl.size_s(); ++i) {
|
||||
#ifdef WINDOWS
|
||||
void * hCom = CreateFileA(dl[i].data(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0);
|
||||
if (hCom == INVALID_HANDLE_VALUE) {
|
||||
#else
|
||||
int fd = ::open(dl[i].data(), O_NOCTTY | O_RDONLY);
|
||||
if (fd == -1) {
|
||||
#endif
|
||||
dl.remove(i);
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
int void_ = 0;
|
||||
bool rok = true;
|
||||
#ifdef WINDOWS
|
||||
/*COMMTIMEOUTS times;
|
||||
times.ReadIntervalTimeout = MAXDWORD;
|
||||
times.ReadTotalTimeoutConstant = 0;
|
||||
times.ReadTotalTimeoutMultiplier = 0;
|
||||
times.WriteTotalTimeoutConstant = 1;
|
||||
times.WriteTotalTimeoutMultiplier = 0;
|
||||
SetCommTimeouts(hCom, ×);
|
||||
if (ReadFile(hCom, &void_, 1, &readed_, 0) == 0)
|
||||
rok = GetLastError() == ;*/
|
||||
#else
|
||||
fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
if (::read(fd, &void_, 1) == -1)
|
||||
rok = errno != EIO;
|
||||
|
||||
#endif
|
||||
if (!rok) {
|
||||
dl.remove(i);
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
#ifdef WINDOWS
|
||||
CloseHandle(hCom);
|
||||
#else
|
||||
::close(fd);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return dl;
|
||||
}
|
||||
310
src/io/piserial.h
Executable file
310
src/io/piserial.h
Executable file
@@ -0,0 +1,310 @@
|
||||
/*! \file piserial.h
|
||||
* \brief Serial device
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
COM
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com, Bychkov Andrey wapmobil@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PISERIAL_H
|
||||
#define PISERIAL_H
|
||||
|
||||
#include "pitimer.h"
|
||||
#include "piiodevice.h"
|
||||
#ifndef WINDOWS
|
||||
# include <termios.h>
|
||||
# include <fcntl.h>
|
||||
# include <sys/ioctl.h>
|
||||
# ifndef B50
|
||||
# define B50 0000001
|
||||
# endif
|
||||
# ifndef B75
|
||||
# define B75 0000002
|
||||
# endif
|
||||
# ifndef B1500000
|
||||
# define B1500000 0010012
|
||||
# endif
|
||||
# ifndef B2000000
|
||||
# define B2000000 0010013
|
||||
# endif
|
||||
# ifndef B2500000
|
||||
# define B2500000 0010014
|
||||
# endif
|
||||
# ifndef B3000000
|
||||
# define B3000000 0010015
|
||||
# endif
|
||||
# ifndef B3500000
|
||||
# define B3500000 0010016
|
||||
# endif
|
||||
# ifndef B4000000
|
||||
# define B4000000 0010017
|
||||
# endif
|
||||
#else
|
||||
# define TIOCM_LE 1
|
||||
# define TIOCM_DTR 4
|
||||
# define TIOCM_RTS 7
|
||||
# define TIOCM_CTS 8
|
||||
# define TIOCM_ST 3
|
||||
# define TIOCM_SR 2
|
||||
# define TIOCM_CAR 1
|
||||
# define TIOCM_RNG 9
|
||||
# define TIOCM_DSR 6
|
||||
# define B50 50
|
||||
# define B75 75
|
||||
# define B110 110
|
||||
# define B300 300
|
||||
# define B600 600
|
||||
# define B1200 1200
|
||||
# define B2400 2400
|
||||
# define B4800 4800
|
||||
# define B9600 9600
|
||||
# define B14400 14400
|
||||
# define B19200 19200
|
||||
# define B38400 38400
|
||||
# define B57600 57600
|
||||
# define B115200 115200
|
||||
# define B128000 128000
|
||||
# define B256000 256000
|
||||
# define B1500000 1500000
|
||||
# define B2000000 2000000
|
||||
# define B2500000 2500000
|
||||
# define B3000000 3000000
|
||||
# define B3500000 3500000
|
||||
# define B4000000 4000000
|
||||
#endif
|
||||
#ifndef CRTSCTS
|
||||
# define CRTSCTS 020000000000
|
||||
#endif
|
||||
|
||||
|
||||
class PIP_EXPORT PISerial: public PIIODevice
|
||||
{
|
||||
PIIODEVICE(PISerial)
|
||||
public:
|
||||
|
||||
//! Contructs an empty %PISerial
|
||||
PISerial();
|
||||
|
||||
//! \brief Parameters of PISerial
|
||||
enum Parameters {
|
||||
ParityControl /*! Enable parity check and generate */ = 0x1,
|
||||
ParityOdd /*! Parity is odd instead of even */ = 0x2,
|
||||
TwoStopBits /*! Two stop bits instead of one */ = 0x4
|
||||
};
|
||||
|
||||
//! \brief Speed of PISerial
|
||||
enum Speed {
|
||||
S50 /*! 50 baud */ = 50,
|
||||
S75 /*! 75 baud */ = 75,
|
||||
S110 /*! 110 baud */ = 110,
|
||||
S300 /*! 300 baud */ = 300,
|
||||
S600 /*! 600 baud */ = 600,
|
||||
S1200 /*! 1200 baud */ = 1200,
|
||||
S2400 /*! 2400 baud */ = 2400,
|
||||
S4800 /*! 4800 baud */ = 4800,
|
||||
S9600 /*! 9600 baud */ = 9600,
|
||||
S19200 /*! 19200 baud */ = 19200,
|
||||
S38400 /*! 38400 baud */ = 38400,
|
||||
S57600 /*! 57600 baud */ = 57600,
|
||||
S115200 /*! 115200 baud */ = 115200,
|
||||
S1500000 = 1500000, // Linux only
|
||||
S2000000 = 2000000, // Linux only
|
||||
S2500000 = 2500000, // Linux only
|
||||
S3000000 = 3000000, // Linux only
|
||||
S3500000 = 3500000, // Linux only
|
||||
S4000000 = 4000000 // Linux only
|
||||
};
|
||||
|
||||
//! Contructs %PISerial with device name "device", speed "speed" and parameters "params"
|
||||
PISerial(const PIString & device, PISerial::Speed speed = S115200, PIFlags<PISerial::Parameters> params = 0);
|
||||
|
||||
~PISerial();
|
||||
|
||||
|
||||
//! Set both input and output speed to "speed"
|
||||
void setSpeed(PISerial::Speed speed) {setProperty("outSpeed", (int)speed); setProperty("inSpeed", (int)speed); applySettings();}
|
||||
|
||||
//! Set output speed to "speed"
|
||||
void setOutSpeed(PISerial::Speed speed) {setProperty("outSpeed", (int)speed); applySettings();}
|
||||
|
||||
//! Set input speed to "speed"
|
||||
void setInSpeed(PISerial::Speed speed) {setProperty("inSpeed", (int)speed); applySettings();}
|
||||
|
||||
//! Set device name to "dev"
|
||||
void setDevice(const PIString & dev) {setPath(dev); if (isOpened()) {close(); open();};}
|
||||
|
||||
|
||||
//! Set parameters to "parameters_"
|
||||
void setParameters(PIFlags<PISerial::Parameters> parameters_) {setProperty("parameters", (int)parameters_); applySettings();}
|
||||
|
||||
//! Set parameter "parameter" to "on" state
|
||||
void setParameter(PISerial::Parameters parameter, bool on = true);
|
||||
|
||||
//! Returns if parameter "parameter" is set
|
||||
bool isParameterSet(PISerial::Parameters parameter) const;
|
||||
|
||||
//! Returns parameters
|
||||
PIFlags<PISerial::Parameters> parameters() const {return (PIFlags<Parameters>)(property("parameters").toInt());}
|
||||
|
||||
|
||||
//! Set data bits count. Valid range is from 5 to 8, befault is 8
|
||||
void setDataBitsCount(int bits) {setProperty("dataBitsCount", bits); applySettings();}
|
||||
|
||||
//! Returns data bits count
|
||||
int dataBitsCount() const {return property("dataBitsCount").toInt();}
|
||||
|
||||
|
||||
//! Set pin number "number" to logic level "on". Valid numbers are 4 (DTR) and 7 (RTS)
|
||||
bool setPin(int number, bool on);
|
||||
|
||||
//! Returns pin number "number" logic level. Valid numbers range is from 1 to 9
|
||||
bool isPin(int number) const;
|
||||
|
||||
bool setLE(bool on) {return setBit(TIOCM_LE, on, "LE");} // useless function, just formally
|
||||
bool setDTR(bool on) {return setBit(TIOCM_DTR, on, "DTR");}
|
||||
bool setRTS(bool on) {return setBit(TIOCM_RTS, on, "RTS");}
|
||||
bool setCTS(bool on) {return setBit(TIOCM_CTS, on, "CTS");} // useless function, just formally
|
||||
bool setST(bool on) {return setBit(TIOCM_ST, on, "ST");} // useless function, just formally
|
||||
bool setSR(bool on) {return setBit(TIOCM_SR, on, "SR");} // useless function, just formally
|
||||
bool setCAR(bool on) {return setBit(TIOCM_CAR, on, "CAR");} // useless function, just formally
|
||||
bool setRNG(bool on) {return setBit(TIOCM_RNG, on, "RNG");} // useless function, just formally
|
||||
bool setDSR(bool on) {return setBit(TIOCM_DSR, on, "DSR");} // useless function, just formally
|
||||
|
||||
bool isLE() const {return isBit(TIOCM_LE, "LE");}
|
||||
bool isDTR() const {return isBit(TIOCM_DTR, "DTR");}
|
||||
bool isRTS() const {return isBit(TIOCM_RTS, "RTS");}
|
||||
bool isCTS() const {return isBit(TIOCM_CTS, "CTS");}
|
||||
bool isST() const {return isBit(TIOCM_ST, "ST");}
|
||||
bool isSR() const {return isBit(TIOCM_SR, "SR");}
|
||||
bool isCAR() const {return isBit(TIOCM_CAR, "CAR");}
|
||||
bool isRNG() const {return isBit(TIOCM_RNG, "RNG");}
|
||||
bool isDSR() const {return isBit(TIOCM_DSR, "DSR");}
|
||||
|
||||
void setVTime(int t) {vtime = t; applySettings();}
|
||||
|
||||
|
||||
//! Set read is blocking for function read(void * read_to, int max_size)
|
||||
void setReadIsBlocking(bool yes);
|
||||
|
||||
|
||||
//! Returns device name
|
||||
PIString device() const {return path();}
|
||||
|
||||
//! Returns output speed
|
||||
PISerial::Speed outSpeed() const {return (PISerial::Speed)(property("outSpeed").toInt());}
|
||||
|
||||
//! Returns input speed
|
||||
PISerial::Speed inSpeed() const {return (PISerial::Speed)(property("inSpeed").toInt());}
|
||||
|
||||
int VTime() const {return vtime;}
|
||||
|
||||
|
||||
//! Discard all buffered input and output data
|
||||
void flush() {
|
||||
#ifndef WINDOWS
|
||||
if (fd != -1) tcflush(fd, TCIOFLUSH);
|
||||
#endif
|
||||
}
|
||||
|
||||
int read(void * read_to, int max_size);
|
||||
bool read(void * read_to, int max_size, double timeout_ms);
|
||||
PIString read(int size = -1, double timeout_ms = 1000.);
|
||||
PIByteArray readData(int size = -1, double timeout_ms = 1000.);
|
||||
|
||||
|
||||
//! \brief Write to device data "data" with maximum size "max_size" and wait for data written if "wait" is \b true.
|
||||
//! \returns sended bytes count
|
||||
int write(const void * data, int max_size, bool wait = false);
|
||||
|
||||
//! \brief Write to device data "data" with maximum size "size" and wait for data written if "wait" is \b true.
|
||||
//! \returns \b true if sended bytes count = "size"
|
||||
bool send(const void * data, int size, bool wait = false) {return (write(data, size, wait) == size);}
|
||||
|
||||
//! \brief Write to device string "data" and wait for data written if "wait" is \b true.
|
||||
//! \returns \b true if sended bytes count = size of string
|
||||
bool send(const PIString & data, bool wait = false) {return (write(data.data(), data.lengthAscii(), wait) == data.size_s());}
|
||||
|
||||
//! \brief Write to device byte array "data" and wait for data written if "wait" is \b true.
|
||||
//! \returns \b true if sended bytes count = size of string
|
||||
bool send(const PIByteArray & data, bool wait = false) {return (write(data.data(), data.size_s(), wait) == data.size_s());}
|
||||
|
||||
PIString constructFullPath() const;
|
||||
|
||||
|
||||
//! \brief Returns all available speeds for serial devices
|
||||
static PIVector<int> availableSpeeds();
|
||||
|
||||
//! \brief Returns all available system devices. If "test" each device will be tried to open
|
||||
static PIStringList availableDevices(bool test = false);
|
||||
|
||||
//! \ioparams
|
||||
//! \{
|
||||
#ifdef DOXYGEN
|
||||
//! \brief device, default ""
|
||||
string device;
|
||||
|
||||
//! \brief input/output speed, default 115200
|
||||
int speed;
|
||||
|
||||
//! \brief dataBitsCount, default 8
|
||||
int dataBitsCount;
|
||||
|
||||
//! \brief parityControl, default false
|
||||
bool parityControl;
|
||||
|
||||
//! \brief parityOdd, default false
|
||||
bool parityOdd;
|
||||
|
||||
//! \brief twoStopBits, default false
|
||||
bool twoStopBits;
|
||||
#endif
|
||||
//! \}
|
||||
|
||||
protected:
|
||||
PIString fullPathPrefix() const {return "ser";}
|
||||
void configureFromFullPath(const PIString & full_path);
|
||||
bool configureDevice(const void * e_main, const void * e_parent = 0);
|
||||
int write(const void * data, int max_size) {return write(data, max_size, true);}
|
||||
|
||||
//! Executes when any read function was successful. Default implementation does nothing
|
||||
virtual void received(const void * data, int size) {;}
|
||||
|
||||
void _init();
|
||||
void applySettings();
|
||||
int convertSpeed(PISerial::Speed speed);
|
||||
bool setBit(int bit, bool on, const PIString & bname);
|
||||
bool isBit(int bit, const PIString & bname) const;
|
||||
|
||||
bool openDevice();
|
||||
bool closeDevice();
|
||||
|
||||
#ifdef WINDOWS
|
||||
DCB desc, sdesc;
|
||||
void * hCom;
|
||||
DWORD readed, mask;
|
||||
bool block_write;
|
||||
#else
|
||||
termios desc, sdesc;
|
||||
uint readed;
|
||||
#endif
|
||||
int fd, vtime;
|
||||
bool block_read;
|
||||
PITimeMeasurer tm_;
|
||||
|
||||
};
|
||||
|
||||
#endif // PISERIAL_H
|
||||
410
src/io/piusb.cpp
Executable file
410
src/io/piusb.cpp
Executable file
@@ -0,0 +1,410 @@
|
||||
#include "piusb.h"
|
||||
#include "piconfig.h"
|
||||
|
||||
#ifdef PIP_USB
|
||||
# ifdef WINDOWS
|
||||
# include <lusb0_usb.h>
|
||||
# else
|
||||
# include <usb.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
REGISTER_DEVICE(PIUSB);
|
||||
|
||||
|
||||
PIUSB::PIUSB(ushort vid, ushort pid): PIIODevice("", ReadWrite) {
|
||||
vid_ = vid;
|
||||
pid_ = pid;
|
||||
intefrace_ = 0;
|
||||
hdev = 0;
|
||||
interface_claimed = -1;
|
||||
setPath(PIString::fromNumber(vid_, 16).expandLeftTo(4, "0") + ":" + PIString::fromNumber(pid_, 16).expandLeftTo(4, "0"));
|
||||
setDeviceNumber(1);
|
||||
setTimeoutRead(1000);
|
||||
setTimeoutWrite(1000);
|
||||
}
|
||||
|
||||
|
||||
void PIUSB::Endpoint::parse() {
|
||||
direction = Write;
|
||||
transfer_type = Control;
|
||||
synchronisation_type = NoSynchonisation;
|
||||
usage_type = DataEndpoint;
|
||||
direction = (Direction)((address >> 7) & 1);
|
||||
transfer_type = (TransferType)(attributes & 3);
|
||||
if (transfer_type == Isochronous) {
|
||||
synchronisation_type = (SynchronisationType)((attributes >> 2) & 3);
|
||||
usage_type = (UsageType)((attributes >> 4) & 3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PIUSB::Endpoint PIUSB::getEndpointByAddress(uchar address) {
|
||||
piForeachC (Endpoint & i, eps)
|
||||
if (i.address == address)
|
||||
return i;
|
||||
return Endpoint();
|
||||
}
|
||||
|
||||
|
||||
PIVector<PIUSB::Endpoint> PIUSB::endpointsRead() {
|
||||
PIVector<Endpoint> ret;
|
||||
piForeachC (Endpoint & i, eps)
|
||||
if (i.direction == Endpoint::Read)
|
||||
ret << i;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIVector<PIUSB::Endpoint> PIUSB::endpointsWrite() {
|
||||
PIVector<Endpoint> ret;
|
||||
piForeachC (Endpoint & i, eps)
|
||||
if (i.direction == Endpoint::Write)
|
||||
ret << i;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool PIUSB::setConfiguration(uchar value) {
|
||||
#ifdef PIP_USB
|
||||
if (hdev == 0) return false;
|
||||
bool found = false;
|
||||
piForeachC (Configuration & c, desc_.configurations)
|
||||
if (c.value_to_select == value) {found = true; conf_ = c; break;}
|
||||
if (!found) {
|
||||
piCoutObj << "Can`t find configuration with \"value_to_select\" =" << value;
|
||||
return false;
|
||||
}
|
||||
if (interface_claimed >= 0)
|
||||
usb_release_interface(hdev, interface_claimed);
|
||||
interface_claimed = -1;
|
||||
return setInterface(conf_.interfaces.front().value_to_select);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool PIUSB::setInterface(uchar value) {
|
||||
#ifdef PIP_USB
|
||||
if (hdev == 0) return false;
|
||||
bool found = false;
|
||||
piForeachC (Interface & i, conf_.interfaces)
|
||||
if (i.value_to_select == value) {found = true; iface_ = i; break;}
|
||||
if (!found) {
|
||||
piCoutObj << "Can`t find interface with \"value_to_select\" =" << value;
|
||||
return false;
|
||||
}
|
||||
if (interface_claimed >= 0)
|
||||
usb_release_interface(hdev, interface_claimed);
|
||||
interface_claimed = -1;
|
||||
if (usb_claim_interface(hdev, iface_.value_to_select) < 0) {
|
||||
piCoutObj << "Error: Cant`t claim interface!";
|
||||
return false;
|
||||
}
|
||||
eps.clear();
|
||||
eps = iface_.endpoints;
|
||||
ep_read = ep_write = Endpoint();
|
||||
for (int i = 0; i < eps.size_s(); ++i) {
|
||||
if (eps[i].direction == Endpoint::Read && ep_read.isNull())
|
||||
ep_read = eps[i];
|
||||
if (eps[i].direction == Endpoint::Write && ep_write.isNull())
|
||||
ep_write = eps[i];
|
||||
}
|
||||
interface_claimed = value;
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool PIUSB::configureDevice(const void * e_main, const void * e_parent) {
|
||||
#ifdef PIP_USB
|
||||
PIConfig::Entry * em = (PIConfig::Entry * )e_main;
|
||||
PIConfig::Entry * ep = (PIConfig::Entry * )e_parent;
|
||||
PIString vp = readDeviceSetting<PIString>("device", "", em, ep);
|
||||
ushort v, p;
|
||||
if (vp.isEmpty()) {
|
||||
v = readDeviceSetting<ushort>("vid", vendorID(), em, ep);
|
||||
p = readDeviceSetting<ushort>("pid", productID(), em, ep);
|
||||
} else {
|
||||
v = vp.left(vp.find(":")).toInt(16);
|
||||
p = vp.right(vp.length() - vp.find(":") - 1).toInt(16);
|
||||
}
|
||||
setVendorID(v);
|
||||
setProductID(p);
|
||||
setDeviceNumber(readDeviceSetting<int>("deviceNumber", deviceNumber(), em, ep));
|
||||
setConfiguration(readDeviceSetting<ushort>("configuration", currentConfiguration().value_to_select, em, ep));
|
||||
setInterface(readDeviceSetting<ushort>("interface", currentInterface().value_to_select, em, ep));
|
||||
setEndpointRead(Endpoint(readDeviceSetting<ushort>("endpointRead", endpointRead().address, em, ep)));
|
||||
setEndpointWrite(Endpoint(readDeviceSetting<ushort>("endpointWrite", endpointWrite().address, em, ep)));
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool PIUSB::openDevice() {
|
||||
#ifdef PIP_USB
|
||||
if (path().size_s() >= 8) {
|
||||
vid_ = path().left(4).toInt(16);
|
||||
pid_ = path().right(4).toInt(16);
|
||||
}
|
||||
if (hdev != 0) closeDevice();
|
||||
hdev = 0;
|
||||
interface_claimed = -1;
|
||||
ep_write = ep_read = Endpoint();
|
||||
usb_init();
|
||||
//usb_set_debug(4);
|
||||
if (usb_find_busses() < 0) {
|
||||
piCoutObj << "Error: Cant`t find busses!";
|
||||
return false;
|
||||
}
|
||||
if (usb_find_devices() < 0) {
|
||||
piCoutObj << "Error: Cant`t find devices!";
|
||||
return false;
|
||||
}
|
||||
|
||||
//piCoutObj << "Search for device ... " << flush;
|
||||
int cur_num = 1;
|
||||
bool found = false;
|
||||
struct usb_device * dev;
|
||||
struct usb_bus * bus;
|
||||
for (bus = usb_get_busses(); bus; bus = bus->next) {
|
||||
for (dev = bus->devices; dev; dev = dev->next) {
|
||||
if (dev->descriptor.idVendor == vid_ && dev->descriptor.idProduct == pid_) {
|
||||
if (cur_num == deviceNumber()) {
|
||||
struct usb_device_descriptor & dd(dev->descriptor);
|
||||
desc_.usb_spec_number = dd.bcdUSB;
|
||||
desc_.device_class = dd.bDeviceClass;
|
||||
desc_.device_subclass = dd.bDeviceSubClass;
|
||||
desc_.device_protocol = dd.bDeviceProtocol;
|
||||
desc_.max_packet_size = dd.bMaxPacketSize0;
|
||||
desc_.id_vendor = dd.idVendor;
|
||||
desc_.id_product = dd.idProduct;
|
||||
desc_.id_device_release = dd.bcdDevice;
|
||||
desc_.index_manufacturer = dd.iManufacturer;
|
||||
desc_.index_product = dd.iProduct;
|
||||
desc_.index_serial = dd.iSerialNumber;
|
||||
desc_.configurations.clear();
|
||||
for (int c = 0; c < dd.bNumConfigurations; ++c) {
|
||||
desc_.configurations << Configuration();
|
||||
Configuration & conf(desc_.configurations.back());
|
||||
struct usb_config_descriptor & dc(dev->config[c]);
|
||||
conf.index = c;
|
||||
conf.value_to_select = dc.bConfigurationValue;
|
||||
conf.attributes = dc.bmAttributes;
|
||||
conf.max_power = ushort(dc.MaxPower) * 2;
|
||||
conf.self_powered = (conf.attributes >> 6) & 1;
|
||||
conf.remote_wakeup = (conf.attributes >> 5) & 1;
|
||||
conf.interfaces.clear();
|
||||
for (int i = 0; i < dc.bNumInterfaces; ++i) {
|
||||
conf.interfaces << Interface();
|
||||
Interface & infc(conf.interfaces.back());
|
||||
struct usb_interface_descriptor * di(dc.interface[c].altsetting);
|
||||
infc.index = i;
|
||||
infc.value_to_select = di->bAlternateSetting;
|
||||
infc.class_code = di->bInterfaceClass;
|
||||
infc.subclass_code = di->bInterfaceSubClass;
|
||||
infc.protocol_code = di->bInterfaceProtocol;
|
||||
infc.endpoints.clear();
|
||||
for (int e = 0; e < di->bNumEndpoints; ++e) {
|
||||
infc.endpoints << Endpoint(di->endpoint[e].bEndpointAddress,
|
||||
di->endpoint[e].bmAttributes,
|
||||
di->endpoint[e].wMaxPacketSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!desc_.configurations.isEmpty())
|
||||
conf_ = desc_.configurations.front();
|
||||
|
||||
struct usb_interface_descriptor * is = dev->config->interface->altsetting;
|
||||
int epn = is->bNumEndpoints;
|
||||
eps.clear();
|
||||
for (int i = 0; i < epn; ++i) {
|
||||
eps << Endpoint(is->endpoint[i].bEndpointAddress,
|
||||
is->endpoint[i].bmAttributes,
|
||||
is->endpoint[i].wMaxPacketSize);
|
||||
if (eps.back().direction == Endpoint::Write && (eps.back().address == ep_write.address || ep_write.address == 0)) ep_write = eps.back();
|
||||
if (eps.back().direction == Endpoint::Read && (eps.back().address == ep_read.address || ep_read.address == 0)) ep_read = eps.back();
|
||||
}
|
||||
|
||||
//piCoutObj << "Device found at address:" << "Bus: " << dev->bus->dirname << ", Device: " << dev->filename;
|
||||
found = true;
|
||||
break;
|
||||
} else cur_num++;
|
||||
}
|
||||
}
|
||||
if (found) break;
|
||||
}
|
||||
if (!found) {
|
||||
piCoutObj << "Error: Cant`t find device!";
|
||||
return false;
|
||||
}
|
||||
//piCoutObj << "Open ... " << flush;
|
||||
hdev = usb_open(dev);
|
||||
if (hdev == 0) {
|
||||
piCoutObj << "Error: Cant`t open device:" << usb_strerror();
|
||||
return false;
|
||||
}// else piCoutObj << "ok";
|
||||
//usb_reset(hdev);
|
||||
|
||||
//usb_set_configuration(hdev, 1);
|
||||
//usb_set_altinterface(hdev, 0);
|
||||
|
||||
# ifndef WINDOWS
|
||||
char tbuff[256];
|
||||
//piCoutObj << "Check for bounded driver ... " << flush;
|
||||
if (usb_get_driver_np(hdev, intefrace_, tbuff, sizeof(tbuff) - 1) >= 0) {
|
||||
//piCoutObj << "yes" << "Found driver: " << tbuff;
|
||||
//piCoutObj << "Detach driver ... " << flush;
|
||||
if (usb_detach_kernel_driver_np(hdev, intefrace_)< 0) {
|
||||
piCoutObj << "Error: Cant`t detach bounded driver!";
|
||||
return false;
|
||||
}// else piCoutObj << "ok";
|
||||
}// else piCoutObj << "no";
|
||||
# endif
|
||||
|
||||
//piCoutObj << "Claim interface ... " << flush;
|
||||
if (usb_claim_interface(hdev, intefrace_) < 0) {
|
||||
piCoutObj << "Error: Cant`t claim interface:" << usb_strerror();
|
||||
return false;
|
||||
} // else piCoutObj << "ok";
|
||||
interface_claimed = intefrace_;
|
||||
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool PIUSB::closeDevice() {
|
||||
#ifdef PIP_USB
|
||||
if (hdev == 0) return true;
|
||||
//usb_reset(hdev);
|
||||
usb_release_interface(hdev, intefrace_);
|
||||
usb_close(hdev);
|
||||
hdev = 0;
|
||||
interface_claimed = -1;
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int PIUSB::read(void * read_to, int max_size) {
|
||||
#ifdef PIP_USB
|
||||
if (!opened_ || ep_read.isNull()) return -1;
|
||||
switch (ep_read.transfer_type) {
|
||||
case Endpoint::Bulk: /*piCoutObj << "bulk read" << max_size;*/ return usb_bulk_read(hdev, ep_read.address, (char * )read_to, max_size, timeout_r); break;
|
||||
case Endpoint::Interrupt: return usb_interrupt_read(hdev, ep_read.address, (char * )read_to, max_size, timeout_r); break;
|
||||
default: break;
|
||||
}
|
||||
return -1;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int PIUSB::write(const void * data, int max_size) {
|
||||
#ifdef PIP_USB
|
||||
if (!opened_ || ep_write.isNull()) return -1;
|
||||
switch (ep_read.transfer_type) {
|
||||
case Endpoint::Bulk: /*piCoutObj << "bulk write" << max_size;*/ return usb_bulk_write(hdev, ep_write.address, (char * )const_cast<void * >(data), max_size, timeout_w); break;
|
||||
case Endpoint::Interrupt: return usb_interrupt_write(hdev, ep_read.address, (char * )data, max_size, timeout_w); break;
|
||||
default: break;
|
||||
}
|
||||
return -1;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int PIUSB::controlWrite(const void * data, int max_size) {
|
||||
#ifdef PIP_USB
|
||||
if (!opened_) return -1;
|
||||
//return usb_control_msg(hdev, );
|
||||
return -1;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void PIUSB::flush() {
|
||||
#ifdef PIP_USB
|
||||
if (!opened_) return;
|
||||
if (!ep_read.isNull()) usb_resetep(hdev, ep_read.address);
|
||||
if (!ep_write.isNull()) usb_resetep(hdev, ep_write.address);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
PICout operator<<(PICout s, const PIUSB::Endpoint & v) {
|
||||
s.setControl(0, true);
|
||||
s << NewLine << "{" << NewLine;
|
||||
if (v.isNull())
|
||||
s << " " << "Null Endpoint";
|
||||
else {
|
||||
s << " " << "Address: " << v.address << NewLine;
|
||||
s << " " << "Attributes: " << v.attributes << NewLine;
|
||||
s << " " << "Direction: " << (v.direction == PIUSB::Endpoint::Write ? "Write" : "Read") << NewLine;
|
||||
s << " " << "Transfer Type: ";
|
||||
switch (v.transfer_type) {
|
||||
case PIUSB::Endpoint::Control: s << "Control" << NewLine; break;
|
||||
case PIUSB::Endpoint::Bulk: s << "Bulk" << NewLine; break;
|
||||
case PIUSB::Endpoint::Interrupt: s << "Interrupt" << NewLine; break;
|
||||
case PIUSB::Endpoint::Isochronous: s << "Isochronous" << NewLine; break;
|
||||
default: break;
|
||||
}
|
||||
if (v.transfer_type == PIUSB::Endpoint::Isochronous) {
|
||||
s << " " << "Synchronisation Type: ";
|
||||
switch (v.synchronisation_type) {
|
||||
case PIUSB::Endpoint::NoSynchonisation: s << "No Synchonisation" << NewLine; break;
|
||||
case PIUSB::Endpoint::Asynchronous: s << "Asynchronous" << NewLine; break;
|
||||
case PIUSB::Endpoint::Adaptive: s << "Adaptive" << NewLine; break;
|
||||
case PIUSB::Endpoint::Synchronous: s << "Synchronous" << NewLine; break;
|
||||
default: break;
|
||||
}
|
||||
s << " " << "Usage Type: ";
|
||||
switch (v.usage_type) {
|
||||
case PIUSB::Endpoint::DataEndpoint: s << "Data Endpoint" << NewLine; break;
|
||||
case PIUSB::Endpoint::FeedbackEndpoint: s << "Feedback Endpoint" << NewLine; break;
|
||||
case PIUSB::Endpoint::ExplicitFeedbackDataEndpoint: s << "Explicit Feedback Data Endpoint" << NewLine; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
s << " " << "Max Packet Size: " << v.max_packet_size << NewLine;
|
||||
}
|
||||
s << "}" << NewLine;
|
||||
s.restoreControl();
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
PIString PIUSB::constructFullPath() const {
|
||||
PIString ret(fullPathPrefix() + "://");
|
||||
ret << PIString::fromNumber(vendorID(), 16).toLowerCase() << ":" << PIString::fromNumber(productID(), 16).toLowerCase() << ":" << deviceNumber() << ":" << endpointRead().address << ":" << endpointWrite().address;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void PIUSB::configureFromFullPath(const PIString & full_path) {
|
||||
PIStringList pl = full_path.split(":");
|
||||
for (int i = 0; i < pl.size_s(); ++i) {
|
||||
PIString p(pl[i]);
|
||||
switch (i) {
|
||||
case 0: setVendorID(p.toUShort(16)); break;
|
||||
case 1: setProductID(p.toUShort(16)); break;
|
||||
case 2: setDeviceNumber(p.toInt()); break;
|
||||
case 3: setEndpointRead(Endpoint(p.toInt())); break;
|
||||
case 4: setEndpointWrite(Endpoint(p.toInt())); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
153
src/io/piusb.h
Executable file
153
src/io/piusb.h
Executable file
@@ -0,0 +1,153 @@
|
||||
/*! \file piusb.h
|
||||
* \brief USB device
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
USB, based on libusb
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIUSB_H
|
||||
#define PIUSB_H
|
||||
|
||||
#include "piiodevice.h"
|
||||
|
||||
struct usb_dev_handle;
|
||||
|
||||
class PIP_EXPORT PIUSB: public PIIODevice
|
||||
{
|
||||
PIIODEVICE(PIUSB)
|
||||
public:
|
||||
PIUSB(ushort vid = 0, ushort pid = 0);
|
||||
|
||||
struct Endpoint {
|
||||
Endpoint(uchar a = 0, uchar at = 0, ushort mps = 0) {address = a; attributes = at; max_packet_size = mps; parse();}
|
||||
|
||||
enum Direction {Write = 0, Read = 1};
|
||||
enum TransferType {Control = 0, Isochronous = 1, Bulk = 2, Interrupt = 3};
|
||||
enum SynchronisationType {NoSynchonisation= 0, Asynchronous = 2, Adaptive = 1, Synchronous = 3};
|
||||
enum UsageType {DataEndpoint = 0, FeedbackEndpoint = 2, ExplicitFeedbackDataEndpoint = 1};
|
||||
|
||||
void parse();
|
||||
bool isNull() const {return address == 0;}
|
||||
|
||||
uchar address;
|
||||
uchar attributes;
|
||||
ushort max_packet_size;
|
||||
Direction direction;
|
||||
TransferType transfer_type;
|
||||
SynchronisationType synchronisation_type;
|
||||
UsageType usage_type;
|
||||
};
|
||||
|
||||
struct Interface {
|
||||
Interface() {index = value_to_select = class_code = subclass_code = protocol_code = 0;}
|
||||
uchar index;
|
||||
uchar value_to_select;
|
||||
ushort class_code;
|
||||
ushort subclass_code;
|
||||
ushort protocol_code;
|
||||
PIVector<PIUSB::Endpoint> endpoints;
|
||||
};
|
||||
|
||||
struct Configuration {
|
||||
Configuration() {index = value_to_select = attributes = max_power = 0; self_powered = remote_wakeup = false;}
|
||||
uchar index;
|
||||
uchar value_to_select;
|
||||
uchar attributes;
|
||||
ushort max_power; // mA
|
||||
bool self_powered;
|
||||
bool remote_wakeup;
|
||||
PIVector<PIUSB::Interface> interfaces;
|
||||
};
|
||||
|
||||
struct Descriptor {
|
||||
Descriptor() {memset(this, 0, sizeof(Descriptor));}
|
||||
ushort usb_spec_number;
|
||||
uchar device_class;
|
||||
uchar device_subclass;
|
||||
uchar device_protocol;
|
||||
uchar max_packet_size;
|
||||
ushort id_vendor;
|
||||
ushort id_product;
|
||||
ushort id_device_release;
|
||||
uchar index_manufacturer;
|
||||
uchar index_product;
|
||||
uchar index_serial;
|
||||
PIVector<PIUSB::Configuration> configurations;
|
||||
};
|
||||
|
||||
const PIUSB::Descriptor & currentDescriptor() const {return desc_;}
|
||||
const PIUSB::Configuration & currentConfiguration() const {return conf_;}
|
||||
const PIUSB::Interface & currentInterface() const {return iface_;}
|
||||
|
||||
ushort vendorID() const {return vid_;}
|
||||
ushort productID() const {return pid_;}
|
||||
|
||||
int deviceNumber() const {return property("deviceNumber").toInt();}
|
||||
int timeoutRead() const {return property("timeoutRead").toInt();}
|
||||
int timeoutWrite() const {return property("timeoutWrite").toInt();}
|
||||
const PIUSB::Endpoint & endpointRead() const {return ep_read;}
|
||||
const PIUSB::Endpoint & endpointWrite() const {return ep_write;}
|
||||
|
||||
const PIVector<PIUSB::Endpoint> & endpoints() const {return eps;}
|
||||
PIVector<PIUSB::Endpoint> endpointsRead();
|
||||
PIVector<PIUSB::Endpoint> endpointsWrite();
|
||||
PIUSB::Endpoint getEndpointByAddress(uchar address);
|
||||
|
||||
void setVendorID(ushort vid) {vid_ = vid; setPath(PIString::fromNumber(vid_, 16).expandLeftTo(4, "0") + ":" + PIString::fromNumber(pid_, 16).expandLeftTo(4, "0"));}
|
||||
void setProductID(ushort pid) {pid_ = pid; setPath(PIString::fromNumber(vid_, 16).expandLeftTo(4, "0") + ":" + PIString::fromNumber(pid_, 16).expandLeftTo(4, "0"));}
|
||||
|
||||
bool setConfiguration(uchar value);
|
||||
bool setInterface(uchar value);
|
||||
|
||||
void setEndpointRead(const PIUSB::Endpoint & ep) {ep_read = ep;}
|
||||
void setEndpointWrite(const PIUSB::Endpoint & ep) {ep_write = ep;}
|
||||
void setDeviceNumber(int dn) {setProperty("deviceNumber", dn);}
|
||||
void setTimeoutRead(int t) {setProperty("timeoutRead", t);}
|
||||
void setTimeoutWrite(int t) {setProperty("timeoutWrite", t);}
|
||||
|
||||
int read(void * read_to, int max_size);
|
||||
int write(const void * data, int max_size);
|
||||
int controlWrite(const void * data, int max_size);
|
||||
|
||||
void flush();
|
||||
|
||||
PIString constructFullPath() const;
|
||||
|
||||
protected:
|
||||
PIString fullPathPrefix() const {return "usb";}
|
||||
void configureFromFullPath(const PIString & full_path);
|
||||
bool configureDevice(const void * e_main, const void * e_parent = 0);
|
||||
//bool init();
|
||||
bool openDevice();
|
||||
bool closeDevice();
|
||||
|
||||
PIVector<PIUSB::Endpoint> eps;
|
||||
ushort vid_, pid_;
|
||||
int intefrace_, timeout_r, timeout_w;
|
||||
int interface_claimed;
|
||||
PIUSB::Endpoint ep_read, ep_write;
|
||||
Descriptor desc_;
|
||||
Configuration conf_;
|
||||
Interface iface_;
|
||||
usb_dev_handle * hdev;
|
||||
|
||||
};
|
||||
|
||||
PICout operator <<(PICout s, const PIUSB::Endpoint & v);
|
||||
|
||||
#endif // PIUSB_H
|
||||
244
src/math/picrc.h
Executable file
244
src/math/picrc.h
Executable file
@@ -0,0 +1,244 @@
|
||||
/*! \file picrc.h
|
||||
* \brief CRC checksum calculator
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
CRC checksum calculator
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PICRC_H
|
||||
#define PICRC_H
|
||||
|
||||
#include "pistring.h"
|
||||
|
||||
template <int L>
|
||||
class PIP_EXPORT uint_cl {
|
||||
public:
|
||||
uint_cl() {for (int i = 0; i < L / 8; ++i) data_[i] = 0;}
|
||||
uint_cl(const uint_cl<L> & v) {for (int i = 0; i < L / 8; ++i) data_[i] = v.data_[i];}
|
||||
uint_cl(uchar v) {for (int i = 0; i < L / 8; ++i) data_[i] = (i == 0 ? v : 0);}
|
||||
uint_cl(char v) {for (int i = 0; i < L / 8; ++i) data_[i] = (i == 0 ? v : 0);}
|
||||
uint_cl(ushort v) {int l = piMin<uint>(L / 8, sizeof(v)); memcpy(data_, &v, l); for (int i = l; i < L / 8; ++i) data_[i] = 0;}
|
||||
uint_cl(short v) {int l = piMin<uint>(L / 8, sizeof(v)); memcpy(data_, &v, l); for (int i = l; i < L / 8; ++i) data_[i] = 0;}
|
||||
uint_cl(uint v) {int l = piMin<uint>(L / 8, sizeof(v)); memcpy(data_, &v, l); for (int i = l; i < L / 8; ++i) data_[i] = 0;}
|
||||
uint_cl(int v) {int l = piMin<uint>(L / 8, sizeof(v)); memcpy(data_, &v, l); for (int i = l; i < L / 8; ++i) data_[i] = 0;}
|
||||
uint_cl(ulong v) {int l = piMin<uint>(L / 8, sizeof(v)); memcpy(data_, &v, l); for (int i = l; i < L / 8; ++i) data_[i] = 0;}
|
||||
uint_cl(long v) {int l = piMin<uint>(L / 8, sizeof(v)); memcpy(data_, &v, l); for (int i = l; i < L / 8; ++i) data_[i] = 0;}
|
||||
uint_cl(ullong v) {int l = piMin<uint>(L / 8, sizeof(v)); memcpy(data_, &v, l); for (int i = l; i < L / 8; ++i) data_[i] = 0;}
|
||||
uint_cl(llong v) {int l = piMin<uint>(L / 8, sizeof(v)); memcpy(data_, &v, l); for (int i = l; i < L / 8; ++i) data_[i] = 0;}
|
||||
|
||||
operator bool() {for (int i = 0; i < L / 8; ++i) if (data_[i] > 0) return true; return false;}
|
||||
operator char() {return (char)data_[0];}
|
||||
operator short() {short t(0); int l = piMin<uint>(L / 8, sizeof(t)); memcpy(&t, data_, l); return t;}
|
||||
operator int() {int t(0); int l = piMin<uint>(L / 8, sizeof(t)); memcpy(&t, data_, l); return t;}
|
||||
operator long() {long t(0); int l = piMin<uint>(L / 8, sizeof(t)); memcpy(&t, data_, l); return t;}
|
||||
operator llong() {llong t(0); int l = piMin<uint>(L / 8, sizeof(t)); memcpy(&t, data_, l); return t;}
|
||||
operator uchar() {return data_[0];}
|
||||
operator ushort() {ushort t(0); int l = piMin<uint>(L / 8, sizeof(t)); memcpy(&t, data_, l); return t;}
|
||||
operator uint() {uint t(0); int l = piMin<uint>(L / 8, sizeof(t)); memcpy(&t, data_, l); return t;}
|
||||
operator ulong() {ulong t(0); int l = piMin<uint>(L / 8, sizeof(t)); memcpy(&t, data_, l); return t;}
|
||||
operator ullong() {ullong t(0); int l = piMin<uint>(L / 8, sizeof(t)); memcpy(&t, data_, l); return t;}
|
||||
|
||||
uint_cl<L> operator +(const uint_cl<L> & v) {
|
||||
uint_cl<L> t;
|
||||
uint cv;
|
||||
bool ov = false;
|
||||
for (int i = 0; i < L / 8; ++i) {
|
||||
cv = v.data_[i] + data_[i];
|
||||
if (ov) ++cv;
|
||||
ov = cv > 255;
|
||||
t.data_[i] = ov ? cv - 256 : cv;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
uint_cl<L> operator &(const uint_cl<L> & v) const {uint_cl<L> t; for (int i = 0; i < L / 8; ++i) t.data_[i] = v.data_[i] & data_[i]; return t;}
|
||||
uint_cl<L> operator &(const uchar & v) const {return *this & uint_cl<L>(v);}
|
||||
uint_cl<L> operator &(const ushort & v) const {return *this & uint_cl<L>(v);}
|
||||
uint_cl<L> operator &(const uint & v) const {return *this & uint_cl<L>(v);}
|
||||
uint_cl<L> operator &(const ulong & v) const {return *this & uint_cl<L>(v);}
|
||||
uint_cl<L> operator &(const ullong & v) const {return *this & uint_cl<L>(v);}
|
||||
uint_cl<L> operator &(const char & v) const {return *this & uint_cl<L>(v);}
|
||||
uint_cl<L> operator &(const short & v) const {return *this & uint_cl<L>(v);}
|
||||
uint_cl<L> operator &(const int & v) const {return *this & uint_cl<L>(v);}
|
||||
uint_cl<L> operator &(const long & v) const {return *this & uint_cl<L>(v);}
|
||||
uint_cl<L> operator &(const llong & v) const {return *this & uint_cl<L>(v);}
|
||||
|
||||
uint_cl<L> operator |(const uint_cl<L> & v) const {uint_cl<L> t; for (int i = 0; i < L / 8; ++i) t.data_[i] = v.data_[i] | data_[i]; return t;}
|
||||
uint_cl<L> operator |(const uchar & v) const {return *this | uint_cl<L>(v);}
|
||||
uint_cl<L> operator |(const ushort & v) const {return *this | uint_cl<L>(v);}
|
||||
uint_cl<L> operator |(const uint & v) const {return *this | uint_cl<L>(v);}
|
||||
uint_cl<L> operator |(const ulong & v) const {return *this | uint_cl<L>(v);}
|
||||
uint_cl<L> operator |(const ullong & v) const {return *this | uint_cl<L>(v);}
|
||||
uint_cl<L> operator |(const char & v) const {return *this | uint_cl<L>(v);}
|
||||
uint_cl<L> operator |(const short & v) const {return *this | uint_cl<L>(v);}
|
||||
uint_cl<L> operator |(const int & v) const {return *this | uint_cl<L>(v);}
|
||||
uint_cl<L> operator |(const long & v) const {return *this | uint_cl<L>(v);}
|
||||
uint_cl<L> operator |(const llong & v) const {return *this | uint_cl<L>(v);}
|
||||
|
||||
uint_cl<L> operator ^(const uint_cl<L> & v) const {uint_cl<L> t; for (int i = 0; i < L / 8; ++i) t.data_[i] = v.data_[i] ^ data_[i]; return t;}
|
||||
uint_cl<L> operator ^(const uchar & v) const {return *this ^ uint_cl<L>(v);}
|
||||
uint_cl<L> operator ^(const ushort & v) const {return *this ^ uint_cl<L>(v);}
|
||||
uint_cl<L> operator ^(const uint & v) const {return *this ^ uint_cl<L>(v);}
|
||||
uint_cl<L> operator ^(const ulong & v) const {return *this ^ uint_cl<L>(v);}
|
||||
uint_cl<L> operator ^(const ullong & v) const {return *this ^ uint_cl<L>(v);}
|
||||
uint_cl<L> operator ^(const char & v) const {return *this ^ uint_cl<L>(v);}
|
||||
uint_cl<L> operator ^(const short & v) const {return *this ^ uint_cl<L>(v);}
|
||||
uint_cl<L> operator ^(const int & v) const {return *this ^ uint_cl<L>(v);}
|
||||
uint_cl<L> operator ^(const long & v) const {return *this ^ uint_cl<L>(v);}
|
||||
uint_cl<L> operator ^(const llong & v) const {return *this ^ uint_cl<L>(v);}
|
||||
|
||||
bool operator <(const uint_cl<L> & v) const {for (int i = 0; i < L / 8; ++i) {if (v.data_[i] > data_[i]) return true; if (v.data_[i] < data_[i]) return false;} return false;}
|
||||
bool operator <=(const uint_cl<L> & v) const {for (int i = 0; i < L / 8; ++i) {if (v.data_[i] > data_[i]) return true; if (v.data_[i] < data_[i]) return false;} return true;}
|
||||
bool operator >(const uint_cl<L> & v) const {for (int i = 0; i < L / 8; ++i) {if (v.data_[i] < data_[i]) return true; if (v.data_[i] > data_[i]) return false;} return false;}
|
||||
bool operator >=(const uint_cl<L> & v) const {for (int i = 0; i < L / 8; ++i) {if (v.data_[i] < data_[i]) return true; if (v.data_[i] > data_[i]) return false;} return true;}
|
||||
bool operator ==(const uint_cl<L> & v) const {for (int i = 0; i < L / 8; ++i) if (v.data_[i] != data_[i]) return false; return true;}
|
||||
bool operator !=(const uint_cl<L> & v) const {for (int i = 0; i < L / 8; ++i) if (v.data_[i] != data_[i]) return true; return false;}
|
||||
bool operator <=(const uint_cl<8> & v1) {return (*(uchar*)data()) <= (*(uchar*)v1.data());}
|
||||
|
||||
uint_cl<L> operator >>(const int & c) const {
|
||||
uint_cl<L> t;
|
||||
int l = L - c;
|
||||
bool bit;
|
||||
if (l <= 0) return t;
|
||||
for (int i = 0; i < l; ++i) {
|
||||
bit = 1 & (data_[(i + c) / 8] >> ((i + c) % 8));
|
||||
if (bit) t.data_[i / 8] |= (1 << (i % 8));
|
||||
else t.data_[i / 8] &= ~(1 << (i % 8));
|
||||
}
|
||||
return t;
|
||||
}
|
||||
uint_cl<L> operator >>(const uint & c) const {return (*this << (int)c);}
|
||||
uint_cl<L> operator <<(const int & c) const {
|
||||
uint_cl<L> t;
|
||||
int l = L - c;
|
||||
bool bit;
|
||||
if (l <= 0) return t;
|
||||
for (int i = c; i < L; ++i) {
|
||||
bit = 1 & (data_[(i - c) / 8] >> ((i - c) % 8));
|
||||
if (bit) t.data_[i / 8] |= (1 << (i % 8));
|
||||
else t.data_[i / 8] &= ~(1 << (i % 8));
|
||||
}
|
||||
return t;
|
||||
}
|
||||
uint_cl<L> operator <<(const uint & c) const {return (*this >> (int)c);}
|
||||
|
||||
uint_cl<L> & inverse() const {for (int i = 0; i < L / 8; ++i) data_[i] = ~data_[i]; return *this;}
|
||||
uint_cl<L> inversed() const {uint_cl<L> t(*this); for (int i = 0; i < L / 8; ++i) t.data_[i] = ~t.data_[i]; return t;}
|
||||
uint_cl<L> reversed() const {
|
||||
uint_cl<L> t;
|
||||
bool bit;
|
||||
for (int i = 0; i < L; ++i) {
|
||||
bit = 1 & (data_[(L - i - 1) / 8] >> ((L - i - 1) % 8));
|
||||
if (bit) t.data_[i / 8] |= (1 << (i % 8));
|
||||
else t.data_[i / 8] &= ~(1 << (i % 8));
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
const uchar * data() const {return data_;}
|
||||
uchar * data() {return data_;}
|
||||
uint length() const {return L / 8;}
|
||||
|
||||
private:
|
||||
uchar data_[L / 8];
|
||||
|
||||
};
|
||||
|
||||
template <uint L>
|
||||
inline std::ostream & operator <<(std::ostream & s, const uint_cl<L> & v) {std::ios::fmtflags f = s.flags(); s << std::hex; for (uint i = 0; i < v.length(); ++i) {s << int(v.data()[i]); if (v.data()[i] < 0x10) s << '0'; s << ' ';} s.flags(f); return s;}
|
||||
|
||||
inline uchar reverseByte(uchar b) {
|
||||
uchar ret = 0;
|
||||
bool bit;
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
bit = 1 & (b >> (7 - i));
|
||||
if (bit) ret |= (1 << i);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <uint L, typename N = uint_cl<L> >
|
||||
class PIP_EXPORT PICRC {
|
||||
public:
|
||||
PICRC(const N & poly = N()) {poly_ = poly; reverse_poly = true; init_ = inversed(N(0)); out_ = inversed(N(0)); reverse_before_xor = reverse_data = false; initTable();}
|
||||
PICRC(const N & poly, bool reverse_poly_, const N & initial, const N & out_xor) {poly_ = poly; reverse_poly = reverse_poly_; init_ = initial; out_ = out_xor; reverse_before_xor = reverse_data = false; initTable();}
|
||||
|
||||
void setInitial(const N & v) {init_ = v;}
|
||||
void setOutXor(const N & v) {out_ = v;}
|
||||
void setReversePolynome(bool yes) {reverse_poly = yes; initTable();}
|
||||
void setReverseOutBeforeXOR(bool yes) {reverse_before_xor = yes;}
|
||||
void setReverseDataBytes(bool yes) {reverse_data = yes;}
|
||||
|
||||
void initTable() {
|
||||
N tmp, pol = reverse_poly ? reversed(poly_) : poly_;
|
||||
//cout << std::hex << "poly " << (uint)N(poly_) << " -> " << (uint)N(pol) << endl;
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
tmp = uchar(i);
|
||||
for (int j = 0; j < 8; ++j)
|
||||
tmp = ((tmp & 1) ? ((tmp >> 1) ^ pol) : (tmp >> 1));
|
||||
table[i] = tmp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
N calculate(const void * data, int size) {
|
||||
N crc = init_;
|
||||
uchar * data_ = (uchar * )data, cb;
|
||||
//cout << "process " << size << endl;
|
||||
uchar nTemp;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
cb = data_[i];
|
||||
if (reverse_data) cb = reverseByte(cb);
|
||||
nTemp = cb ^ uchar(crc);
|
||||
crc = crc >> 8;
|
||||
crc = crc ^ table[nTemp];
|
||||
}
|
||||
if (reverse_before_xor) crc = reversed(crc);
|
||||
return crc ^ out_;
|
||||
|
||||
}
|
||||
N calculate(const PIByteArray & d) {return calculate(d.data(), d.size());}
|
||||
N calculate(const char * str) {string s(str); return calculate((void * )s.data(), s.size());}
|
||||
|
||||
private:
|
||||
inline N reversed(const N & v) {return v.reversed();}
|
||||
inline N inversed(const N & v) {return v.inversed();}
|
||||
|
||||
N table[256];
|
||||
N poly_, init_, out_;
|
||||
bool reverse_poly, reverse_before_xor, reverse_data;
|
||||
|
||||
};
|
||||
|
||||
template <> inline uchar PICRC<8, uchar>::reversed(const uchar & v) {return reverseByte(v);}
|
||||
template <> inline ushort PICRC<16, ushort>::reversed(const ushort & v) {return uint_cl<16>(v).reversed();}
|
||||
template <> inline uint PICRC<32, uint>::reversed(const uint & v) {return uint_cl<32>(v).reversed();}
|
||||
template <> inline uchar PICRC<8, uchar>::inversed(const uchar & v) {return ~v;}
|
||||
template <> inline ushort PICRC<16, ushort>::inversed(const ushort & v) {return ~v;}
|
||||
template <> inline uint PICRC<32, uint>::inversed(const uint & v) {return ~v;}
|
||||
|
||||
typedef PICRC<32, uint> CRC_32;
|
||||
typedef PICRC<24> CRC_24;
|
||||
typedef PICRC<16, ushort> CRC_16;
|
||||
typedef PICRC<8, uchar> CRC_8;
|
||||
|
||||
inline CRC_32 standardCRC_32() {return CRC_32(0x04C11DB7, true, 0xFFFFFFFF, 0xFFFFFFFF);}
|
||||
inline CRC_16 standardCRC_16() {return CRC_16(0x8005, true, 0x0, 0x0);}
|
||||
inline CRC_8 standardCRC_8() {return CRC_8(0xD5, true, 0x0, 0x0);}
|
||||
|
||||
#endif // CRC_H
|
||||
1254
src/math/pievaluator.cpp
Executable file
1254
src/math/pievaluator.cpp
Executable file
@@ -0,0 +1,1254 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Evaluator designed for stream computing
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pievaluator.h"
|
||||
|
||||
|
||||
/*! \class PIEvaluator
|
||||
* \brief This class provide mathematical evaluations of custom expression
|
||||
*
|
||||
* \section PIEvaluator_sec0 Synopsis
|
||||
* %PIEvaluator developed for stream evaluations of once set expression.
|
||||
* It`s create internal list of instructions on function \a check() and
|
||||
* executes very fast on function \a evaluate(). Once given expression
|
||||
* can be evaluated any times with different variable values. Evaluator
|
||||
* supports many common mathematic functions described below. Also it`s
|
||||
* automatic puts unnecessarily signs and bracets. Processed expression
|
||||
* can be obtains with function \a expression(). If there is an error
|
||||
* in expression you can get it with function \a error(). Last evaluated
|
||||
* result you can get with function \a lastResult().
|
||||
* \section PIEvaluator_sec1 Using
|
||||
* First you should set your variables with function \a setVariable().
|
||||
* Next give your expression with function \a check() and check for error
|
||||
* with functions \a isCorrect() and \a error(). If expression is correct
|
||||
* you can get processed expression with function \a expression() and
|
||||
* evaluate it with function \a evaluate(). You can change variable values
|
||||
* without rechecking expression.
|
||||
*
|
||||
* \section PIEvaluator_sec2 Functions
|
||||
* %PIEvaluator supports arithmetical operations with complex numbers, this
|
||||
* is their list in priority order:
|
||||
* * ^ (power)
|
||||
* * * (multiply)
|
||||
* * / (divide)
|
||||
* * % (residue)
|
||||
* * + (add)
|
||||
* * - (subtract)
|
||||
*
|
||||
* In addition there are compare and logical operations:
|
||||
* * == (equal)
|
||||
* * != (not equal)
|
||||
* * > (greater)
|
||||
* * < (smaller)
|
||||
* * >= (greater or equal)
|
||||
* * <= (smaller or equal)
|
||||
* * && (and)
|
||||
* * || (or)
|
||||
*
|
||||
* Compare and logical functions works with real operators part and returns 0 or 1.
|
||||
*
|
||||
* Mathematical functions:
|
||||
* * sin(x) - sine
|
||||
* * cos(x) - cosine
|
||||
* * tg(x) - tangent
|
||||
* * ctg(x) - cotangent
|
||||
* * arcsin(x) - arcsine
|
||||
* * arccos(x) - arccosine
|
||||
* * arctg(x) -arccotangent
|
||||
* * arcctg(x) - arctangent
|
||||
* * sh(x) - hyperbolical sine
|
||||
* * ch(x) - hyperbolical cosine
|
||||
* * th(x) - hyperbolical tangent
|
||||
* * cth(x) - hyperbolical cotangent
|
||||
* * sqr(x) - square
|
||||
* * sqrt(x) - square root
|
||||
* * abs(x) - absolute value
|
||||
* * sign(x) - sign of real part (-1 or 1)
|
||||
* * exp(x) - exponent
|
||||
* * pow(x, p) - x in power p
|
||||
* * ln(x) - natural logarithm
|
||||
* * lg(x) - decimal logarithm
|
||||
* * log(x, b) - logarithm of x with base b
|
||||
* * im(x) - imaginary part of complex number
|
||||
* * re(x) - real part of complex number
|
||||
* * arg(x) - argument of complex number
|
||||
* * len(x) - length of complex number
|
||||
* * conj(x) - length of complex number
|
||||
* * rad(x) - convert degrees to radians
|
||||
* * deg(x) - convert radians to degrees
|
||||
* * j0(x) - Bessel function first kind order 0
|
||||
* * j1(x) - Bessel function first kind order 1
|
||||
* * jn(x, n) - Bessel function first kind order n
|
||||
* * y0(x) - Bessel function second kind order 0
|
||||
* * y1(x) - Bessel function second kind order 1
|
||||
* * yn(x, n) - Bessel function second kind order n
|
||||
* * random(s, f) - regular random number in range [s, f]
|
||||
* * min(x0, x1, ...) - minimum of x0, x1, ...
|
||||
* * max(x0, x1, ...) - maximum of x0, x1, ...
|
||||
* * clamp(x, a, b) - trim x on range [a, b]
|
||||
* * step(x, s) - 0 if x < s, else 1
|
||||
* * mix(x, a, b) - interpolate between a and b linear for x (a * (1 - x) + b * x)
|
||||
*
|
||||
* There are some built-in constans:
|
||||
* * i (imaginary 1)
|
||||
* * e
|
||||
* * pi
|
||||
*
|
||||
* All trigonometric functions takes angle in radians.
|
||||
*
|
||||
* \section PIEvaluator_sec3 Example
|
||||
* \snippet pievaluator.cpp main
|
||||
*/
|
||||
|
||||
|
||||
PIEvaluatorContent::PIEvaluatorContent() {
|
||||
addFunction("arcsin", 1);
|
||||
addFunction("arccos", 1);
|
||||
addFunction("arctg", 1);
|
||||
addFunction("arcctg", 1);
|
||||
addFunction("random", 2);
|
||||
addFunction("sin", 1);
|
||||
addFunction("cos", 1);
|
||||
addFunction("ctg", 1);
|
||||
addFunction("tg", 1);
|
||||
addFunction("exp", 1);
|
||||
addFunction("cth", 1);
|
||||
addFunction("sh", 1);
|
||||
addFunction("ch", 1);
|
||||
addFunction("th", 1);
|
||||
addFunction("sqrt", 1);
|
||||
addFunction("sqr", 1);
|
||||
addFunction("pow", 2);
|
||||
addFunction("abs", 1);
|
||||
addFunction("ln", 1);
|
||||
addFunction("lg", 1);
|
||||
addFunction("log", 2);
|
||||
addFunction("im", 1);
|
||||
addFunction("re", 1);
|
||||
addFunction("arg", 1);
|
||||
addFunction("len", 1);
|
||||
addFunction("conj", 1);
|
||||
addFunction("sign", 1);
|
||||
addFunction("rad", 1);
|
||||
addFunction("deg", 1);
|
||||
addFunction("j0", 1);
|
||||
addFunction("j1", 1);
|
||||
addFunction("jn", 2);
|
||||
addFunction("y0", 1);
|
||||
addFunction("y1", 1);
|
||||
addFunction("yn", 2);
|
||||
addFunction("min", -2); // (x0,x1,...)
|
||||
addFunction("max", -2); // (x0,x1,...)
|
||||
addFunction("clamp", 3); // (x,a,b) = x < a ? a : (x > b ? b : x)
|
||||
addFunction("step", 2); // (x,s) = x >= s ? 1. : 0. (1 if 'x' >= 's', else 0)
|
||||
addFunction("mix", 3); // (x,a,b) = a*(1.-x) + b*x (interpolate between 'a' and 'b' linear for 'x')
|
||||
addFunction("defined", 1);
|
||||
clearCustomVariables();
|
||||
//addVariable("n", 0.);
|
||||
//addVariable("x1", 123);
|
||||
}
|
||||
|
||||
|
||||
bool PIEvaluatorContent::setVariableValue(int index, complexd new_value) {
|
||||
if (index < 0 || index >= variables.size_s()) return false;
|
||||
variables[index].value = new_value;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PIEvaluatorContent::setVariableName(int index, const PIString & new_name) {
|
||||
if (index < 0 || index >= variables.size_s()) return false;
|
||||
variables[index].name = new_name;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void PIEvaluatorContent::clearCustomVariables() {
|
||||
variables.clear();
|
||||
addVariable("i", complexd_i);
|
||||
addVariable("pi", atan(1.) * 4.);
|
||||
addVariable("e", exp(1.));
|
||||
cv_count = variables.size();
|
||||
}
|
||||
|
||||
|
||||
void PIEvaluatorContent::sortVariables() {
|
||||
//PIEvaluatorTypes::Variable tv;
|
||||
for (uint i = 0; i < variables.size(); i++) {
|
||||
for (uint j = variables.size() - 1; j > i; j--) {
|
||||
if (variables[j].name.length() <= variables[i].name.length()) continue;
|
||||
piSwap<PIEvaluatorTypes::Variable>(variables[i], variables[j]);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* qDebug() << "---";
|
||||
* for (int i = 0; i < variables.size(); i++) {
|
||||
* qDebug() << variables[i].name;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
PIEvaluatorTypes::BaseFunctions PIEvaluatorContent::getBaseFunction(const PIString & name) {
|
||||
if (name == "sin") return PIEvaluatorTypes::bfSin;
|
||||
if (name == "cos") return PIEvaluatorTypes::bfCos;
|
||||
if (name == "tg") return PIEvaluatorTypes::bfTg;
|
||||
if (name == "ctg") return PIEvaluatorTypes::bfCtg;
|
||||
if (name == "arcsin") return PIEvaluatorTypes::bfArcsin;
|
||||
if (name == "arccos") return PIEvaluatorTypes::bfArccos;
|
||||
if (name == "arctg") return PIEvaluatorTypes::bfArctg;
|
||||
if (name == "arcctg") return PIEvaluatorTypes::bfArcctg;
|
||||
if (name == "exp") return PIEvaluatorTypes::bfExp;
|
||||
if (name == "random") return PIEvaluatorTypes::bfRandom;
|
||||
if (name == "sh") return PIEvaluatorTypes::bfSh;
|
||||
if (name == "ch") return PIEvaluatorTypes::bfCh;
|
||||
if (name == "th") return PIEvaluatorTypes::bfTh;
|
||||
if (name == "cth") return PIEvaluatorTypes::bfCth;
|
||||
if (name == "sqrt") return PIEvaluatorTypes::bfSqrt;
|
||||
if (name == "sqr") return PIEvaluatorTypes::bfSqr;
|
||||
if (name == "pow") return PIEvaluatorTypes::bfPow;
|
||||
if (name == "abs") return PIEvaluatorTypes::bfAbs;
|
||||
if (name == "ln") return PIEvaluatorTypes::bfLn;
|
||||
if (name == "lg") return PIEvaluatorTypes::bfLg;
|
||||
if (name == "log") return PIEvaluatorTypes::bfLog;
|
||||
if (name == "im") return PIEvaluatorTypes::bfIm;
|
||||
if (name == "re") return PIEvaluatorTypes::bfRe;
|
||||
if (name == "arg") return PIEvaluatorTypes::bfArg;
|
||||
if (name == "len") return PIEvaluatorTypes::bfLen;
|
||||
if (name == "conj") return PIEvaluatorTypes::bfConj;
|
||||
if (name == "sign") return PIEvaluatorTypes::bfSign;
|
||||
if (name == "rad") return PIEvaluatorTypes::bfRad;
|
||||
if (name == "deg") return PIEvaluatorTypes::bfDeg;
|
||||
if (name == "j0") return PIEvaluatorTypes::bfJ0;
|
||||
if (name == "j1") return PIEvaluatorTypes::bfJ1;
|
||||
if (name == "jn") return PIEvaluatorTypes::bfJN;
|
||||
if (name == "y0") return PIEvaluatorTypes::bfY0;
|
||||
if (name == "y1") return PIEvaluatorTypes::bfY1;
|
||||
if (name == "yn") return PIEvaluatorTypes::bfYN;
|
||||
if (name == "min") return PIEvaluatorTypes::bfMin;
|
||||
if (name == "max") return PIEvaluatorTypes::bfMax;
|
||||
if (name == "clamp") return PIEvaluatorTypes::bfClamp;
|
||||
if (name == "step") return PIEvaluatorTypes::bfStep;
|
||||
if (name == "mix") return PIEvaluatorTypes::bfMix;
|
||||
if (name == "defined") return PIEvaluatorTypes::bfDefined;
|
||||
return PIEvaluatorTypes::bfUnknown;
|
||||
}
|
||||
|
||||
const PIString & PIEvaluator::prepare(const PIString & string) {
|
||||
currentString = string.trimmed();
|
||||
if (currentString.isEmpty()) currentString = "0";
|
||||
replaceOperators();
|
||||
removeSpaces();
|
||||
checkBrackets();
|
||||
while (fillElements()) checkBrackets();
|
||||
while (setSignes()) fillElements();
|
||||
removeJunk();
|
||||
findUnknownVariables();
|
||||
return currentString;
|
||||
}
|
||||
|
||||
|
||||
void PIEvaluator::removeSpaces() {
|
||||
PIString tmps = currentString;
|
||||
for (int i = 0; i < tmps.length(); i++) {
|
||||
if (tmps[i] == ' ' || tmps[i] == '\t') {
|
||||
tmps.remove(i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
currentString = tmps;
|
||||
}
|
||||
|
||||
|
||||
void PIEvaluator::removeJunk() {
|
||||
PIChar cc;
|
||||
bool junk = true;
|
||||
int bcnt;
|
||||
while (junk) {
|
||||
if (currentString.left(1) != "(" || currentString.right(1) != ")") return;
|
||||
bcnt = 1;
|
||||
junk = false;
|
||||
for (int i = 1; i < currentString.length(); i++) {
|
||||
cc = currentString[i];
|
||||
if (cc == '(') bcnt++;
|
||||
if (cc == ')') bcnt--;
|
||||
if (bcnt == 0) {
|
||||
if (i == currentString.length() - 1) {
|
||||
currentString = currentString.mid(1, currentString.length() - 2);
|
||||
elements.pop_front();
|
||||
elements.pop_back();
|
||||
junk = true;
|
||||
break;
|
||||
} else break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIEvaluator::replaceOperators() {
|
||||
currentString.replaceAll("==", "=");
|
||||
currentString.replaceAll("!=", ":");
|
||||
currentString.replaceAll(">=", "}");
|
||||
currentString.replaceAll("<=", "{");
|
||||
currentString.replaceAll("&&", "&");
|
||||
currentString.replaceAll("||", "|");
|
||||
}
|
||||
|
||||
|
||||
void PIEvaluator::makeOutput(PIString & string) {
|
||||
string.replaceAll(":", "≠");
|
||||
string.replaceAll("}", "≥");
|
||||
string.replaceAll("{", "≤");
|
||||
string.replaceAll("&", "⋀");
|
||||
string.replaceAll("|", "⋁");
|
||||
}
|
||||
|
||||
|
||||
void PIEvaluator::findUnknownVariables() {
|
||||
PIString cvar;
|
||||
unknownVars.clear();
|
||||
for (int i = 0; i < currentString.length(); i++) {
|
||||
if (elements[i].var_num == -666) cvar += currentString[i];
|
||||
else {
|
||||
if (cvar.length() == 0) continue;
|
||||
unknownVars << cvar;
|
||||
cvar = "";
|
||||
}
|
||||
}
|
||||
if (cvar.length() > 0) unknownVars << cvar;
|
||||
unknownVars.removeDuplicates();
|
||||
}
|
||||
|
||||
|
||||
bool PIEvaluator::isSign(const PIChar & ch) {
|
||||
return ch == '+' || ch == '-' ||
|
||||
ch == '*' || ch == '/' ||
|
||||
ch == '%' || ch == '^' ||
|
||||
ch == '=' || ch == ':' ||
|
||||
ch == '>' || ch == '<' ||
|
||||
ch == '}' || ch == '{' ||
|
||||
ch == '&' || ch == '|';
|
||||
}
|
||||
|
||||
|
||||
void PIEvaluator::checkBrackets() {
|
||||
PIString tmps = currentString;
|
||||
PIChar fc, sc;
|
||||
int bcnt = 0, bpos = 0, inserted = 0;
|
||||
currentString = tmps;
|
||||
for (int i = 0; i < tmps.length(); i++) {
|
||||
if (tmps[i] == '(') {
|
||||
if (bcnt == 0) bpos = i;
|
||||
bcnt++;
|
||||
}
|
||||
if (tmps[i] == ')') {
|
||||
if (bcnt == 0) {
|
||||
currentString.insert(bpos + inserted, "(");
|
||||
inserted++;
|
||||
} else bcnt--;
|
||||
}
|
||||
}
|
||||
if (bcnt > 0) currentString += PIString(bcnt, ')');
|
||||
tmps = currentString;
|
||||
for (int i = 0; i < tmps.length() - 1; i++) {
|
||||
fc = tmps[i].toLower();
|
||||
sc = tmps[i + 1].toLower();
|
||||
if ((fc == ')' && sc == '(') ||
|
||||
(fc == ')' && sc >= '0' && sc <= '9') ||
|
||||
(fc == ')' && sc >= 'a' && sc <= 'z') ) tmps.insert(++i, '*');
|
||||
}
|
||||
currentString = tmps;
|
||||
}
|
||||
|
||||
|
||||
bool PIEvaluator::fillElements() {
|
||||
int fstart, flen, cnum = 0, cpart = 0, cfunc;
|
||||
PIChar cc, nc, pc, fc = '!';
|
||||
bool numFound = false;
|
||||
PIString curfind, tmps = currentString;
|
||||
elements.resize(tmps.length());
|
||||
for (uint i = 0; i < elements.size(); i++) {
|
||||
elements[i].type = PIEvaluatorTypes::etVariable;
|
||||
elements[i].var_num = -666;
|
||||
}
|
||||
currentVariables.clear();
|
||||
//qDebug().nospace() << "search for functions ...";
|
||||
for (int i = 0; i < content.functionsCount(); i++) {
|
||||
curfind = content.function(i).identifier;
|
||||
cfunc = i; //(int)content.function(i).type;
|
||||
flen = curfind.length();
|
||||
fstart = 0;
|
||||
while (fstart >= 0) {
|
||||
fstart = tmps.find(curfind, fstart);
|
||||
if (fstart < 0) break;
|
||||
if (tmps[fstart + flen] != '(') {
|
||||
//currentString.insert(fstart + flen, "(");
|
||||
//return true;
|
||||
fstart++;
|
||||
continue;
|
||||
}
|
||||
for (int j = fstart; j < fstart + flen; j++) {
|
||||
elements[j].set(PIEvaluatorTypes::etFunction, cnum, cfunc);
|
||||
tmps.replace(j, 1, fc);
|
||||
}
|
||||
cnum++;
|
||||
}
|
||||
}
|
||||
cnum = 0;
|
||||
//qDebug().nospace() << "search for variables ...";
|
||||
for (int i = 0; i < content.variablesCount(); i++) {
|
||||
curfind = content.variable(i).name;
|
||||
flen = curfind.length();
|
||||
fstart = 0;
|
||||
while (fstart >= 0) {
|
||||
fstart = tmps.find(curfind, fstart);
|
||||
if (fstart < 0) break;
|
||||
for (int j = fstart; j < fstart + flen; j++) {
|
||||
elements[j].set(PIEvaluatorTypes::etVariable, cnum, i);
|
||||
tmps.replace(j, 1, fc);
|
||||
}
|
||||
cnum++;
|
||||
}
|
||||
}
|
||||
curfind = "";
|
||||
cnum = 1;
|
||||
//qDebug().nospace() << "search for numbers ...";
|
||||
for (int i = 0; i < tmps.length(); i++) {
|
||||
cc = tmps[i];
|
||||
/*if (cc == " " || cc == "(" || cc == ")") {
|
||||
curfind = "";
|
||||
cpart = 0;
|
||||
numFound = false;
|
||||
continue;
|
||||
}*/
|
||||
switch (cpart) {
|
||||
case 0:
|
||||
if ((cc >= '0' && cc <= '9')) {// || cc == '-' || cc == '+') {
|
||||
curfind += cc;
|
||||
cpart = 1;
|
||||
continue;
|
||||
}
|
||||
if (cc == '.') {
|
||||
curfind += cc;
|
||||
cpart = 2;
|
||||
continue;
|
||||
}
|
||||
if (cc == 'E') {
|
||||
curfind += cc;
|
||||
cpart = 3;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (cc >= '0' && cc <= '9') {
|
||||
curfind += cc;
|
||||
continue;
|
||||
}
|
||||
if (cc == '.') {
|
||||
curfind += cc;
|
||||
cpart = 2;
|
||||
continue;
|
||||
}
|
||||
if (cc == 'E') {
|
||||
curfind += cc;
|
||||
cpart = 3;
|
||||
continue;
|
||||
}
|
||||
numFound = true;
|
||||
break;
|
||||
case 2:
|
||||
if (cc >= '0' && cc <= '9') {
|
||||
curfind += cc;
|
||||
continue;
|
||||
}
|
||||
if (cc == 'E') {
|
||||
curfind += cc;
|
||||
cpart = 3;
|
||||
continue;
|
||||
}
|
||||
numFound = true;
|
||||
break;
|
||||
case 3:
|
||||
if ((cc >= '0' && cc <= '9') || cc == '-' || cc == '+') {
|
||||
curfind += cc;
|
||||
cpart = 4;
|
||||
continue;
|
||||
}
|
||||
numFound = true;
|
||||
break;
|
||||
case 4:
|
||||
if (cc >= '0' && cc <= '9') {
|
||||
curfind += cc;
|
||||
continue;
|
||||
}
|
||||
numFound = true;
|
||||
break;
|
||||
}
|
||||
if (numFound) {
|
||||
//qDebug().nospace() << "add " << cnum << ": " << curfind << " = " << curfind.toDouble();
|
||||
currentVariables.push_back(PIEvaluatorTypes::Variable("tmp" + PIString::fromNumber(cnum), curfind.toDouble()));
|
||||
for (int j = i - curfind.length(); j < i; j++) {
|
||||
elements[j].set(PIEvaluatorTypes::etNumber, cnum, -cnum);
|
||||
tmps.replace(j, 1, fc);
|
||||
}
|
||||
curfind = "";
|
||||
cnum++;
|
||||
cpart = 0;
|
||||
numFound = false;
|
||||
}
|
||||
}
|
||||
if (cpart > 0) {
|
||||
//qDebug().nospace() << "add " << cnum << ": " << curfind << " = " << curfind.toDouble();
|
||||
currentVariables.push_back(PIEvaluatorTypes::Variable("tmp" + PIString::fromNumber(cnum), curfind.toDouble()));
|
||||
for (int j = tmps.length() - curfind.length(); j < tmps.length(); j++) {
|
||||
elements[j].set(PIEvaluatorTypes::etNumber, cnum, -cnum);
|
||||
tmps.replace(j, 1, fc);
|
||||
}
|
||||
}
|
||||
cc = nc = fc;
|
||||
//qDebug().nospace() << "search for signes ...";
|
||||
for (int i = 0; i < tmps.length(); i++) {
|
||||
cc = tmps[i];
|
||||
if (i > 0) pc = tmps[i - 1];
|
||||
else pc = fc;
|
||||
if (i < tmps.length() - 1) nc = tmps[i + 1];
|
||||
else nc = fc;
|
||||
if (cc == '(' || cc == ')' || cc == ',') {
|
||||
elements[i].set(PIEvaluatorTypes::etOperator, -1);
|
||||
continue;
|
||||
}
|
||||
if (cc == '-' || cc == '+') {
|
||||
elements[i].set(PIEvaluatorTypes::etOperator, -1);
|
||||
if (i < tmps.length() - 1) if (elements[i + 1].type == PIEvaluatorTypes::etVariable ||
|
||||
elements[i + 1].type == PIEvaluatorTypes::etFunction) continue;
|
||||
if ((pc == '(' || isSign(pc) || i == 0) && i < tmps.length() - 1) {
|
||||
if (elements[i + 1].type != PIEvaluatorTypes::etOperator) {
|
||||
cnum = elements[i + 1].num;
|
||||
elements[i].set(PIEvaluatorTypes::etNumber, cnum);
|
||||
tmps.replace(i, 1, fc);
|
||||
///cout << "found sign " << cc << " :" << cnum - 1 << endl;
|
||||
if (cc == '-' && currentVariables.size_s() >= cnum)
|
||||
currentVariables[cnum - 1].value = -currentVariables[cnum - 1].value;
|
||||
//i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isSign(cc)) {
|
||||
elements[i].set(PIEvaluatorTypes::etOperator, -1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/*
|
||||
qDebug().nospace() << tmps;
|
||||
cout << " ";
|
||||
for (int i = 0; i < elements.size(); i++) {
|
||||
switch (elements[i].type) {
|
||||
case etFunction: cout << "f"; break;
|
||||
case etNumber: cout << "n"; break;
|
||||
case etOperator: cout << "o"; break;
|
||||
case etVariable: cout << "v"; break;
|
||||
}
|
||||
}
|
||||
cout << endl;
|
||||
*/
|
||||
return false;
|
||||
//for (int i = 0; i < currentVariables.size(); i++) qDebug() << "var " << i << ": " << currentVariables[i].value.real();
|
||||
}
|
||||
|
||||
|
||||
bool PIEvaluator::setSignes() {
|
||||
int inserted = 0, ni, pi = 0, needInsert = 0;
|
||||
PIChar fc, sc, pc;
|
||||
PIString tmps = currentString;
|
||||
for (int i = 0; i < tmps.length() - 1; i++) {
|
||||
needInsert = 0;
|
||||
ni = i + 1;
|
||||
if (i > 0) pi = i - 1;
|
||||
fc = tmps[i].toLower();
|
||||
sc = tmps[ni].toLower();
|
||||
pc = tmps[pi].toLower();
|
||||
//if (elements[i].type == etOperator || elements[ni].type == etVariable) continue;
|
||||
if (fc == ',' || sc == ',') continue;
|
||||
if (elements[i].type == PIEvaluatorTypes::etOperator && elements[ni].type == PIEvaluatorTypes::etOperator) continue;
|
||||
if (fc == ')' && (elements[ni].type == PIEvaluatorTypes::etNumber || elements[ni].type == PIEvaluatorTypes::etVariable || elements[ni].type == PIEvaluatorTypes::etFunction)) needInsert = 1;
|
||||
if (sc == '(' && (elements[i].type == PIEvaluatorTypes::etNumber || elements[i].type == PIEvaluatorTypes::etVariable)) needInsert = 1;
|
||||
if (elements[i].type == PIEvaluatorTypes::etNumber && elements[ni].type == PIEvaluatorTypes::etNumber && elements[i].num != elements[ni].num) needInsert = 1;
|
||||
if (elements[i].type == PIEvaluatorTypes::etVariable && elements[ni].type == PIEvaluatorTypes::etVariable && elements[i].num != elements[ni].num) needInsert = 1;
|
||||
if ((elements[i].type == PIEvaluatorTypes::etNumber && elements[ni].type == PIEvaluatorTypes::etVariable) || (elements[i].type == PIEvaluatorTypes::etVariable && elements[ni].type == PIEvaluatorTypes::etNumber)) needInsert = 1;
|
||||
if ((elements[i].type == PIEvaluatorTypes::etNumber || elements[i].type == PIEvaluatorTypes::etVariable) && elements[ni].type == PIEvaluatorTypes::etFunction) needInsert = 1;
|
||||
if (elements[i].type == PIEvaluatorTypes::etFunction && elements[ni].type == PIEvaluatorTypes::etFunction && elements[i].num != elements[ni].num) needInsert = 2;
|
||||
if (elements[i].type == PIEvaluatorTypes::etFunction && elements[ni].type != PIEvaluatorTypes::etFunction && sc != '(') needInsert = 2;
|
||||
if (elements[pi].type == PIEvaluatorTypes::etOperator && (elements[ni].type == PIEvaluatorTypes::etFunction || elements[ni].type == PIEvaluatorTypes::etVariable) && fc == '-') needInsert = 3;
|
||||
switch (needInsert) {
|
||||
case 1:
|
||||
currentString.insert(ni + inserted, "*");
|
||||
elements.insert(ni + inserted, PIEvaluatorTypes::Element(PIEvaluatorTypes::etOperator, -1));
|
||||
//inserted++;
|
||||
//i++;
|
||||
return true;
|
||||
/*case 2:
|
||||
currentString.insert(ni + inserted, ")");
|
||||
currentString.insert(ni + inserted, "(");
|
||||
elements.insert(ni + inserted, Element(etOperator, -1));
|
||||
elements.insert(ni + inserted, Element(etOperator, -1));
|
||||
inserted++;
|
||||
i++;
|
||||
return true;*/
|
||||
case 3:
|
||||
currentString.insert(ni + inserted, "1*");
|
||||
elements.insert(ni + inserted, PIEvaluatorTypes::Element(PIEvaluatorTypes::etOperator, -1));
|
||||
//inserted;
|
||||
//i++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
/*if (elements[tmps.length() - 1].type == etFunction) {
|
||||
currentString.insert(tmps.length() + inserted, ")");
|
||||
currentString.insert(tmps.length() + inserted, "(");
|
||||
elements.insert(tmps.length() + inserted, Element(etOperator, -1));
|
||||
elements.insert(tmps.length() + inserted, Element(etOperator, -1));
|
||||
return true;
|
||||
}*/
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void PIEvaluator::convert() {
|
||||
int j;
|
||||
PIEvaluatorTypes::Element ce, pe;
|
||||
for (int i = 0; i < currentString.length(); i++) {
|
||||
pe = elements[i];
|
||||
if (pe.type != PIEvaluatorTypes::etFunction) continue;
|
||||
j = i + 1;
|
||||
while (j < currentString.length()) {
|
||||
ce = elements[j];
|
||||
if (ce != pe) break;
|
||||
j++;
|
||||
}
|
||||
currentString.replace(i, j - i, " ");
|
||||
for (int k = i + 1; k < j; k++) elements.remove(i);
|
||||
//i++;
|
||||
}
|
||||
for (int i = 0; i < currentString.length(); i++) {
|
||||
pe = elements[i];
|
||||
if (pe.type != PIEvaluatorTypes::etNumber) continue;
|
||||
j = i + 1;
|
||||
while (j < currentString.length()) {
|
||||
ce = elements[j];
|
||||
if (ce != pe) break;
|
||||
j++;
|
||||
}
|
||||
currentString.replace(i, j - i, " ");
|
||||
for (int k = i + 1; k < j; k++) elements.remove(i);
|
||||
//i++;
|
||||
}
|
||||
for (int i = 0; i < currentString.length(); i++) {
|
||||
pe = elements[i];
|
||||
if (pe.type != PIEvaluatorTypes::etVariable) continue;
|
||||
j = i + 1;
|
||||
while (j < currentString.length()) {
|
||||
ce = elements[j];
|
||||
if (ce != pe) break;
|
||||
j++;
|
||||
}
|
||||
currentString.replace(i, j - i, " ");
|
||||
for (int k = i + 1; k < j; k++) elements.remove(i);
|
||||
//i++;
|
||||
}
|
||||
/*qDebug().nospace() << currentString;
|
||||
cout << " ";
|
||||
for (int i = 0; i < elements.size(); i++) {
|
||||
switch (elements[i].type) {
|
||||
case etFunction: cout << "f"; break;
|
||||
case etNumber: cout << "n"; break;
|
||||
case etOperator: cout << "o"; break;
|
||||
case etVariable: cout << "v"; break;
|
||||
}
|
||||
}
|
||||
cout << endl;*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
const PIString & PIEvaluator::preprocess(const PIString & string) {
|
||||
static PIString ret;
|
||||
int lind;
|
||||
ret = prepare(string);
|
||||
convert();
|
||||
instructions.clear();
|
||||
//qDebug() << preproc->currentString;
|
||||
variables = currentVariables;
|
||||
lind = parse(currentString);
|
||||
if (instructions.size() == 0) {
|
||||
variables.push_back(PIEvaluatorTypes::Variable());
|
||||
instructions.push_back(PIEvaluatorTypes::Instruction(PIEvaluatorTypes::oNone, PIVector<int>(1, lind), -variables.size_s()));
|
||||
}
|
||||
kvars = &(content.variables);
|
||||
/*
|
||||
cout << endl << "variables:" << endl;
|
||||
for (int i = 0; i < variables.size(); i++)
|
||||
cout << i << " value = " << variables[i].value << endl;
|
||||
|
||||
cout << endl << "instructions:" << endl;
|
||||
for (int i = 0; i < instructions.size(); i++) {
|
||||
cout << i << endl;
|
||||
cout << " operation " << instructions[i].operation << endl;
|
||||
cout << " operators: ";
|
||||
for (int j = 0; j < instructions[i].operators.size(); j++)
|
||||
cout << instructions[i].operators[j] << "; ";
|
||||
cout << endl << " function " << instructions[i].function << endl;
|
||||
cout << " out " << instructions[i].out << endl;
|
||||
}
|
||||
*/
|
||||
makeOutput(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIEvaluatorTypes::Operation PIEvaluator::operationInOrder(const int & index) {
|
||||
switch (index) {
|
||||
case 0: return PIEvaluatorTypes::oPower;
|
||||
case 1: return PIEvaluatorTypes::oMultiply;
|
||||
case 2: return PIEvaluatorTypes::oDivide;
|
||||
case 3: return PIEvaluatorTypes::oResidue;
|
||||
case 4: return PIEvaluatorTypes::oAdd;
|
||||
case 5: return PIEvaluatorTypes::oSubtract;
|
||||
case 6: return PIEvaluatorTypes::oEqual;
|
||||
case 7: return PIEvaluatorTypes::oNotEqual;
|
||||
case 8: return PIEvaluatorTypes::oGreaterEqual;
|
||||
case 9: return PIEvaluatorTypes::oSmallerEqual;
|
||||
case 10: return PIEvaluatorTypes::oGreater;
|
||||
case 11: return PIEvaluatorTypes::oSmaller;
|
||||
case 12: return PIEvaluatorTypes::oAnd;
|
||||
case 13: return PIEvaluatorTypes::oOr;
|
||||
default: return PIEvaluatorTypes::oNone;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int PIEvaluator::parse(const PIString & string, int offset) {
|
||||
int slen = string.length(), /*facnt,*/ farg, bcnt, k;
|
||||
PIChar cc;
|
||||
PIEvaluatorTypes::Element ce;
|
||||
PIEvaluatorTypes::Function cfunc;
|
||||
PIEvaluatorTypes::Operation coper;
|
||||
PIString sbrackets, carg;
|
||||
PIVector<int> args, atmp;
|
||||
PIVector<PIEvaluatorTypes::Operation> opers;
|
||||
|
||||
///qDebug() << "to parse :" + string;
|
||||
///cout << " "; for (int i = 0; i < slen; i++) cout << preproc->elements[i + offset].type; cout << endl;
|
||||
|
||||
for (int i = 0; i < slen; i++) {
|
||||
ce = elements[i + offset];
|
||||
cc = string[i];
|
||||
switch (ce.type) {
|
||||
case PIEvaluatorTypes::etNumber:
|
||||
args.push_back(ce.var_num);
|
||||
continue;
|
||||
case PIEvaluatorTypes::etVariable:
|
||||
args.push_back(ce.var_num);
|
||||
continue;
|
||||
case PIEvaluatorTypes::etFunction:
|
||||
i++;
|
||||
cfunc = content.function(ce.var_num);
|
||||
//facnt = cfunc.arguments;
|
||||
atmp.clear();
|
||||
bcnt = farg = 1;
|
||||
///qDebug() << "function: " + cfunc.identifier;
|
||||
//for (int k = 0; k < facnt; k++) {
|
||||
bcnt = 1;
|
||||
carg = "";
|
||||
k = i + 1;
|
||||
//if (string.size_s() <= k || k < 0) return -666;
|
||||
while (bcnt > 0) {
|
||||
//if (k < facnt - 1) fcomma = string.indexOf(',', j);
|
||||
cc = string[k];
|
||||
switch (cc.toAscii()) {
|
||||
case '(': bcnt++; break;
|
||||
case ')':
|
||||
bcnt--;
|
||||
if (bcnt == 0) {
|
||||
///qDebug() << "arument: " << carg;
|
||||
atmp.push_back(parse(carg, k + offset - carg.length()));
|
||||
k++;
|
||||
carg = "";
|
||||
if (atmp.size_s() > 0) if (atmp.back() < 0 && farg > 0) farg = atmp.back();
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case ',':
|
||||
if (bcnt == 1) {
|
||||
///qDebug() << "arument: " << carg;
|
||||
atmp.push_back(parse(carg, k + offset - carg.length()));
|
||||
k++;
|
||||
carg = "";
|
||||
if (atmp.size_s() > 0) if (atmp.back() < 0 && farg > 0) farg = atmp.back();
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
carg += cc;
|
||||
k++;
|
||||
}
|
||||
i = k - 1;
|
||||
if (farg > 0) {
|
||||
variables.push_back(PIEvaluatorTypes::Variable());
|
||||
farg = -variables.size_s();
|
||||
}
|
||||
instructions.push_back(PIEvaluatorTypes::Instruction(PIEvaluatorTypes::oFunction, atmp, farg, ce.var_num));
|
||||
args.push_back(farg);
|
||||
//for (int i = 0; i < args.size_s(); i++) cout << preproc->currentVariables[-args[i]].value << endl;
|
||||
//i = j + 1;
|
||||
continue;
|
||||
case PIEvaluatorTypes::etOperator:
|
||||
//qDebug() << "operator: " << cc;
|
||||
if (cc == '(') {
|
||||
sbrackets = inBrackets(string.right(slen - i));
|
||||
args.push_back(parse(sbrackets, i + offset + 1));
|
||||
i += sbrackets.length() + 1;
|
||||
continue;
|
||||
}
|
||||
if (cc == '+') {opers.push_back(PIEvaluatorTypes::oAdd); continue;}
|
||||
if (cc == '-') {opers.push_back(PIEvaluatorTypes::oSubtract); continue;}
|
||||
if (cc == '*') {opers.push_back(PIEvaluatorTypes::oMultiply); continue;}
|
||||
if (cc == '/') {opers.push_back(PIEvaluatorTypes::oDivide); continue;}
|
||||
if (cc == '%') {opers.push_back(PIEvaluatorTypes::oResidue); continue;}
|
||||
if (cc == '^') {opers.push_back(PIEvaluatorTypes::oPower); continue;}
|
||||
if (cc == '=') {opers.push_back(PIEvaluatorTypes::oEqual); continue;}
|
||||
if (cc == ':') {opers.push_back(PIEvaluatorTypes::oNotEqual); continue;}
|
||||
if (cc == '}') {opers.push_back(PIEvaluatorTypes::oGreaterEqual); continue;}
|
||||
if (cc == '{') {opers.push_back(PIEvaluatorTypes::oSmallerEqual); continue;}
|
||||
if (cc == '>') {opers.push_back(PIEvaluatorTypes::oGreater); continue;}
|
||||
if (cc == '<') {opers.push_back(PIEvaluatorTypes::oSmaller); continue;}
|
||||
if (cc == '&') {opers.push_back(PIEvaluatorTypes::oAnd); continue;}
|
||||
if (cc == '|') {opers.push_back(PIEvaluatorTypes::oOr); continue;}
|
||||
}
|
||||
}
|
||||
/*
|
||||
cout << "stack: " << endl << "args: ";
|
||||
for (int i = 0; i < args.size_s(); i++) cout << args[i] << ", ";
|
||||
cout << endl << "opers: ";
|
||||
for (int i = 0; i < opers.size_s(); i++) cout << opers[i] << ", ";
|
||||
*/
|
||||
if (opers.size_s() == 0) {
|
||||
if (args.size_s() > 0) return args.back();
|
||||
else return -666;
|
||||
}
|
||||
for (int i = 0; i < PIEvaluatorTypes::operationCount; i++) {
|
||||
coper = operationInOrder(i);
|
||||
for (int j = 0; j < opers.size_s(); j++) {
|
||||
if (coper == PIEvaluatorTypes::oDivide || coper == PIEvaluatorTypes::oMultiply) {
|
||||
if (opers[j] != PIEvaluatorTypes::oDivide && opers[j] != PIEvaluatorTypes::oMultiply) continue;
|
||||
} else {
|
||||
if (opers[j] != coper) continue;
|
||||
}
|
||||
atmp.clear();
|
||||
if (j < args.size_s() && j >= 0) atmp.push_back(args[j]);
|
||||
else atmp.push_back(-666);
|
||||
if (j + 1 < args.size_s() && j >= -1) atmp.push_back(args[j + 1]);
|
||||
else atmp.push_back(-666);
|
||||
farg = 1;
|
||||
if (atmp[0] < 0) farg = atmp[0];
|
||||
else {
|
||||
if (atmp[1] < 0) farg = atmp[1];
|
||||
else {
|
||||
variables.push_back(PIEvaluatorTypes::Variable());
|
||||
farg = -variables.size_s();
|
||||
}
|
||||
}
|
||||
instructions.push_back(PIEvaluatorTypes::Instruction(opers[j], atmp, farg));
|
||||
if (j >= 0 && j < args.size_s()) {
|
||||
args.remove(j);
|
||||
if (j < args.size_s()) args[j] = farg;
|
||||
}
|
||||
opers.remove(j);
|
||||
j--;
|
||||
}
|
||||
}
|
||||
return instructions.back().out;
|
||||
///cout << endl;
|
||||
}
|
||||
|
||||
|
||||
bool PIEvaluator::check() {
|
||||
PIEvaluatorTypes::Instruction ci;
|
||||
bool error;
|
||||
if (unknownVars.size_s() > 0) {
|
||||
lastError = "Unknown variables: \"" + unknownVars.join("\", \"") + "\"";
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < instructions.size_s(); i++) {
|
||||
error = false;
|
||||
ci = instructions[i];
|
||||
PIEvaluatorTypes::Function cf;
|
||||
int fac, gac;
|
||||
switch (ci.operation) {
|
||||
case PIEvaluatorTypes::oNone: break;
|
||||
case PIEvaluatorTypes::oFunction:
|
||||
cf = content.function(ci.function);
|
||||
fac = cf.arguments;
|
||||
gac = ci.operators.size_s();
|
||||
for (int j = 0; j < ci.operators.size_s(); j++) {
|
||||
if (ci.operators[j] == -666) { //(ci.operators[j] < -variables.size_s() || ci.operators[j] >= kvars->size()) {
|
||||
error = true;
|
||||
gac--;
|
||||
}
|
||||
}
|
||||
if (fac > 0) {
|
||||
if (gac != fac) {
|
||||
lastError = "Invalid arguments count for function \"" + cf.identifier +
|
||||
"\", expected " + PIString::fromNumber(fac) + " but " +
|
||||
PIString::fromNumber(gac) + " given";
|
||||
return false;
|
||||
}
|
||||
if (error) {
|
||||
lastError = "Invalid at least one of function \"" + cf.identifier + "\" argument";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (fac < 0) {
|
||||
if (gac < -fac) {
|
||||
lastError = "Invalid arguments count for function \"" + cf.identifier +
|
||||
"\", expected at least " + PIString::fromNumber(-fac) + " but " +
|
||||
PIString::fromNumber(gac) + " given";
|
||||
return false;
|
||||
}
|
||||
if (error) {
|
||||
lastError = "Invalid at least one of function \"" + cf.identifier + "\" argument";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (ci.operators[0] == -666 || ci.operators[1] == -666) error = true;
|
||||
if (ci.operators.size_s() != 2 || error) {
|
||||
lastError = "Invalid arguments count for operation \" " + operationChar(ci.operation) + " \"";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (ci.out < -variables.size_s()) {
|
||||
lastError = "Invalid variable index \"" + PIString::fromNumber(ci.out) + "\"";
|
||||
return false;
|
||||
}
|
||||
for (int j = 0; j < ci.operators.size_s(); j++) {
|
||||
if (ci.operators[j] < -variables.size_s() || ci.operators[j] >= kvars->size_s()) {
|
||||
lastError = "Invalid variable index \"" + PIString::fromNumber(ci.operators[j]) + "\"";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
PIString PIEvaluator::inBrackets(const PIString & string) {
|
||||
int slen = string.length(), bcnt = 0;
|
||||
PIChar cc;
|
||||
for (int i = 0; i < slen; i++) {
|
||||
cc = string[i];
|
||||
if (cc == '(') bcnt++;
|
||||
if (cc == ')') {
|
||||
bcnt--;
|
||||
if (bcnt == 0) return string.mid(1, i - 1);
|
||||
}
|
||||
}
|
||||
return PIString();
|
||||
}
|
||||
|
||||
|
||||
PIString PIEvaluator::operationChar(const PIEvaluatorTypes::Operation & operation) {
|
||||
switch (operation) {
|
||||
case PIEvaluatorTypes::oAdd: return "+";
|
||||
case PIEvaluatorTypes::oSubtract: return "-";
|
||||
case PIEvaluatorTypes::oMultiply: return "*";
|
||||
case PIEvaluatorTypes::oDivide: return "/";
|
||||
case PIEvaluatorTypes::oPower: return "^";
|
||||
case PIEvaluatorTypes::oResidue: return "%";
|
||||
case PIEvaluatorTypes::oEqual: return "=";
|
||||
case PIEvaluatorTypes::oNotEqual: return ("≠");
|
||||
case PIEvaluatorTypes::oGreaterEqual: return ("≥");
|
||||
case PIEvaluatorTypes::oSmallerEqual: return ("≤");
|
||||
case PIEvaluatorTypes::oGreater: return ">";
|
||||
case PIEvaluatorTypes::oSmaller: return "<";
|
||||
case PIEvaluatorTypes::oAnd: return ("⋀");
|
||||
case PIEvaluatorTypes::oOr: return ("⋁");
|
||||
default: return "???";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline complexd PIEvaluator::residue(const complexd & f, const complexd & s) {
|
||||
complexd ret;
|
||||
if (s.real() != 0.) ret = complexd(f.real() - ((int)(f.real() / s.real())) * s.real(), 0.);
|
||||
if (s.imag() != 0.) ret = complexd(ret.real(), f.imag() - ((int)(f.imag() / s.imag())) * s.imag());
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
inline void PIEvaluator::execFunction(const PIEvaluatorTypes::Instruction & ci) {
|
||||
PIEvaluatorTypes::Function cfunc = content.function(ci.function);
|
||||
int oi = -ci.out - 1;
|
||||
complexd tmp, stmp, ttmp;
|
||||
//qDebug() << "function " << (int)cfunc.type;
|
||||
switch (cfunc.type) {
|
||||
case PIEvaluatorTypes::bfSin:
|
||||
tmpvars[oi].value = sin(value(ci.operators[0]));
|
||||
break;
|
||||
case PIEvaluatorTypes::bfCos:
|
||||
tmpvars[oi].value = cos(value(ci.operators[0]));
|
||||
break;
|
||||
case PIEvaluatorTypes::bfTg:
|
||||
tmpvars[oi].value = tan(value(ci.operators[0]));
|
||||
break;
|
||||
case PIEvaluatorTypes::bfCtg:
|
||||
tmp = tan(value(ci.operators[0]));
|
||||
if (tmp == complexd_0) tmpvars[oi].value = 0.;
|
||||
else tmpvars[oi].value = complexd_1 / tmp;
|
||||
break;
|
||||
case PIEvaluatorTypes::bfArcsin:
|
||||
tmpvars[oi].value = asinc(value(ci.operators[0]));
|
||||
break;
|
||||
case PIEvaluatorTypes::bfArccos:
|
||||
tmpvars[oi].value = acosc(value(ci.operators[0]));
|
||||
break;
|
||||
case PIEvaluatorTypes::bfArctg:
|
||||
tmpvars[oi].value = atanc(value(ci.operators[0]));
|
||||
break;
|
||||
case PIEvaluatorTypes::bfArcctg:
|
||||
tmp = atanc(value(ci.operators[0]));
|
||||
if (tmp == complexd_0) tmpvars[oi].value = 0.;
|
||||
else tmpvars[oi].value = complexd_1 / tmp;
|
||||
break;
|
||||
case PIEvaluatorTypes::bfSh:
|
||||
tmpvars[oi].value = sinh(value(ci.operators[0]));
|
||||
break;
|
||||
case PIEvaluatorTypes::bfCh:
|
||||
tmpvars[oi].value = cosh(value(ci.operators[0]));
|
||||
break;
|
||||
case PIEvaluatorTypes::bfTh:
|
||||
tmpvars[oi].value = tanh(value(ci.operators[0]));
|
||||
break;
|
||||
case PIEvaluatorTypes::bfCth:
|
||||
tmp = tanh(value(ci.operators[0]));
|
||||
if (tmp == complexd_0) tmpvars[oi].value = 0.;
|
||||
else tmpvars[oi].value = complexd_1 / tmp;
|
||||
break;
|
||||
case PIEvaluatorTypes::bfAbs:
|
||||
tmpvars[oi].value = abs(value(ci.operators[0]));
|
||||
break;
|
||||
case PIEvaluatorTypes::bfSqrt:
|
||||
tmpvars[oi].value = sqrt(value(ci.operators[0]));
|
||||
break;
|
||||
case PIEvaluatorTypes::bfSqr:
|
||||
tmpvars[oi].value = value(ci.operators[0]) * value(ci.operators[0]);
|
||||
break;
|
||||
case PIEvaluatorTypes::bfExp:
|
||||
tmpvars[oi].value = exp(value(ci.operators[0]));
|
||||
break;
|
||||
case PIEvaluatorTypes::bfPow:
|
||||
tmpvars[oi].value = pow(value(ci.operators[0]), value(ci.operators[1]));
|
||||
break;
|
||||
case PIEvaluatorTypes::bfLn:
|
||||
tmpvars[oi].value = log(value(ci.operators[0]));
|
||||
break;
|
||||
case PIEvaluatorTypes::bfLg:
|
||||
tmpvars[oi].value = log10(value(ci.operators[0]));
|
||||
break;
|
||||
case PIEvaluatorTypes::bfLog:
|
||||
tmp = log(value(ci.operators[1]));
|
||||
if (tmp == complexd_0) tmpvars[oi].value = 0.;
|
||||
else tmpvars[oi].value = log(value(ci.operators[0])) / tmp;
|
||||
break;
|
||||
case PIEvaluatorTypes::bfRe:
|
||||
tmpvars[oi].value = value(ci.operators[0]).real();
|
||||
break;
|
||||
case PIEvaluatorTypes::bfIm:
|
||||
tmpvars[oi].value = value(ci.operators[0]).imag();
|
||||
break;
|
||||
case PIEvaluatorTypes::bfArg:
|
||||
tmpvars[oi].value = arg(value(ci.operators[0]));
|
||||
break;
|
||||
case PIEvaluatorTypes::bfLen:
|
||||
tmpvars[oi].value = abs(value(ci.operators[0]));
|
||||
break;
|
||||
case PIEvaluatorTypes::bfConj:
|
||||
tmpvars[oi].value = conj(value(ci.operators[0]));
|
||||
break;
|
||||
case PIEvaluatorTypes::bfSign:
|
||||
tmpvars[oi].value = value(ci.operators[0]).real() >= 0. ? complexd_1 : -complexd_1;
|
||||
break;
|
||||
case PIEvaluatorTypes::bfRad:
|
||||
tmpvars[oi].value = value(ci.operators[0]) * complexd(deg2rad, 0.);
|
||||
break;
|
||||
case PIEvaluatorTypes::bfDeg:
|
||||
tmpvars[oi].value = value(ci.operators[0]) * complexd(rad2deg, 0.);
|
||||
break;
|
||||
case PIEvaluatorTypes::bfJ0:
|
||||
tmpvars[oi].value = piJ0(value(ci.operators[0]).real());
|
||||
break;
|
||||
case PIEvaluatorTypes::bfJ1:
|
||||
tmpvars[oi].value = piJ1(value(ci.operators[0]).real());
|
||||
break;
|
||||
case PIEvaluatorTypes::bfJN:
|
||||
tmpvars[oi].value = piJn(piRoundd(value(ci.operators[1]).real()), value(ci.operators[0]).real());
|
||||
break;
|
||||
case PIEvaluatorTypes::bfY0:
|
||||
tmpvars[oi].value = piY0(value(ci.operators[0]).real());
|
||||
break;
|
||||
case PIEvaluatorTypes::bfY1:
|
||||
tmpvars[oi].value = piY1(value(ci.operators[0]).real());
|
||||
break;
|
||||
case PIEvaluatorTypes::bfYN:
|
||||
tmpvars[oi].value = piYn(piRoundd(value(ci.operators[1]).real()), value(ci.operators[0]).real());
|
||||
break;
|
||||
case PIEvaluatorTypes::bfMin:
|
||||
tmp = value(ci.operators[0]);
|
||||
for (int i = 1; i < ci.operators.size_s(); ++i) {
|
||||
stmp = value(ci.operators[i]);
|
||||
tmp = complexd(piMind(tmp.real(), stmp.real()), piMind(tmp.imag(), stmp.imag()));
|
||||
}
|
||||
tmpvars[oi].value = tmp;
|
||||
break;
|
||||
case PIEvaluatorTypes::bfMax:
|
||||
tmp = value(ci.operators[0]);
|
||||
for (int i = 1; i < ci.operators.size_s(); ++i) {
|
||||
stmp = value(ci.operators[i]);
|
||||
tmp = complexd(piMaxd(tmp.real(), stmp.real()), piMaxd(tmp.imag(), stmp.imag()));
|
||||
}
|
||||
tmpvars[oi].value = tmp;
|
||||
break;
|
||||
case PIEvaluatorTypes::bfClamp:
|
||||
tmp = value(ci.operators[0]);
|
||||
stmp = value(ci.operators[1]);
|
||||
ttmp = value(ci.operators[2]);
|
||||
tmpvars[oi].value = complexd(piClampd(tmp.real(), stmp.real(), ttmp.real()), piClampd(tmp.imag(), stmp.imag(), ttmp.imag()));
|
||||
break;
|
||||
case PIEvaluatorTypes::bfStep:
|
||||
tmpvars[oi].value = complexd(value(ci.operators[0]).real() >= value(ci.operators[1]).real() ? complexld_1 : complexld_0);
|
||||
break;
|
||||
case PIEvaluatorTypes::bfMix:
|
||||
tmp = value(ci.operators[0]);
|
||||
stmp = value(ci.operators[1]);
|
||||
ttmp = value(ci.operators[2]);
|
||||
tmpvars[oi].value = stmp.real() * (1. - tmp.real()) + ttmp.real() * tmp.real();
|
||||
break;
|
||||
case PIEvaluatorTypes::bfDefined:
|
||||
tmpvars[oi].value = value(ci.operators[0]).real() > 0. ? complexd_1 : complexd_0;
|
||||
break;
|
||||
case PIEvaluatorTypes::bfRandom:
|
||||
tmp = static_cast<ldouble>(rand()) / RAND_MAX;
|
||||
stmp = value(ci.operators[1]) - value(ci.operators[0]);
|
||||
tmpvars[oi].value = value(ci.operators[0]) + tmp * stmp;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline bool PIEvaluator::execInstructions() {
|
||||
PIEvaluatorTypes::Instruction ci;
|
||||
int oi;
|
||||
complexd tmp;
|
||||
tmpvars = variables;
|
||||
//cout << "var count " << tmpvars.size_s() << endl;
|
||||
for (int i = 0; i < instructions.size_s(); i++) {
|
||||
ci = instructions[i];
|
||||
oi = -ci.out - 1;
|
||||
//cout << value(ci.operators[0]) << operationChar(ci.operation) << value(ci.operators[1]) << ", " << oi << endl;
|
||||
switch (ci.operation) {
|
||||
case PIEvaluatorTypes::oAdd:
|
||||
tmpvars[oi].value = value(ci.operators[0]) + value(ci.operators[1]);
|
||||
break;
|
||||
case PIEvaluatorTypes::oSubtract:
|
||||
tmpvars[oi].value = value(ci.operators[0]) - value(ci.operators[1]);
|
||||
break;
|
||||
case PIEvaluatorTypes::oMultiply:
|
||||
tmpvars[oi].value = value(ci.operators[0]) * value(ci.operators[1]);
|
||||
break;
|
||||
case PIEvaluatorTypes::oDivide:
|
||||
tmp = value(ci.operators[1]);
|
||||
if (tmp == complexd(0., 0.)) tmpvars[oi].value = 0.;
|
||||
else tmpvars[oi].value = value(ci.operators[0]) / tmp;
|
||||
break;
|
||||
case PIEvaluatorTypes::oResidue:
|
||||
tmpvars[oi].value = residue(value(ci.operators[0]), value(ci.operators[1]));
|
||||
break;
|
||||
case PIEvaluatorTypes::oPower:
|
||||
tmpvars[oi].value = pow(value(ci.operators[0]), value(ci.operators[1]));
|
||||
break;
|
||||
case PIEvaluatorTypes::oEqual:
|
||||
tmpvars[oi].value = value(ci.operators[0]) == value(ci.operators[1]);
|
||||
break;
|
||||
case PIEvaluatorTypes::oNotEqual:
|
||||
tmpvars[oi].value = value(ci.operators[0]) != value(ci.operators[1]);
|
||||
break;
|
||||
case PIEvaluatorTypes::oGreaterEqual:
|
||||
tmpvars[oi].value = value(ci.operators[0]).real() >= value(ci.operators[1]).real();
|
||||
break;
|
||||
case PIEvaluatorTypes::oSmallerEqual:
|
||||
tmpvars[oi].value = value(ci.operators[0]).real() <= value(ci.operators[1]).real();
|
||||
break;
|
||||
case PIEvaluatorTypes::oGreater:
|
||||
tmpvars[oi].value = value(ci.operators[0]).real() > value(ci.operators[1]).real();
|
||||
break;
|
||||
case PIEvaluatorTypes::oSmaller:
|
||||
tmpvars[oi].value = value(ci.operators[0]).real() < value(ci.operators[1]).real();
|
||||
break;
|
||||
case PIEvaluatorTypes::oAnd:
|
||||
tmpvars[oi].value = value(ci.operators[0]).real() > 0. && value(ci.operators[1]).real() > 0.;
|
||||
break;
|
||||
case PIEvaluatorTypes::oOr:
|
||||
tmpvars[oi].value = value(ci.operators[0]).real() > 0. || value(ci.operators[1]).real() > 0.;
|
||||
break;
|
||||
case PIEvaluatorTypes::oFunction:
|
||||
execFunction(ci);
|
||||
break;
|
||||
case PIEvaluatorTypes::oNone:
|
||||
tmpvars[oi].value = value(ci.operators[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!instructions.isEmpty())
|
||||
out = value(instructions.back().out);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PIEvaluator::check(const PIString & string) {
|
||||
currentString = preprocess(string);
|
||||
correct = check();
|
||||
if (!correct) {
|
||||
instructions.clear();
|
||||
return false;
|
||||
}
|
||||
lastError = "Correct";
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
complexd PIEvaluator::evaluate() {
|
||||
if (!execInstructions()) out = 0.;
|
||||
if (fabs(out.real()) < 1E-300) out = complexd(0., out.imag());
|
||||
if (fabs(out.imag()) < 1E-300) out = complexd(out.real(), 0.);
|
||||
return out;
|
||||
}
|
||||
227
src/math/pievaluator.h
Executable file
227
src/math/pievaluator.h
Executable file
@@ -0,0 +1,227 @@
|
||||
/*! \file pievaluator.h
|
||||
* \brief Mathematic expressions calculator
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Evaluator designed for stream calculations
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIEVALUATOR_H
|
||||
#define PIEVALUATOR_H
|
||||
|
||||
#include "pistring.h"
|
||||
#include "pimath.h"
|
||||
|
||||
typedef complexd (*FuncFunc)(void * , int, complexd * );
|
||||
|
||||
namespace PIEvaluatorTypes {
|
||||
static const int operationCount = 14;
|
||||
|
||||
enum eType {etNumber, etOperator, etVariable, etFunction};
|
||||
enum Operation {oNone, oAdd, oSubtract, oMultiply, oDivide, oResidue, oPower,
|
||||
oEqual, oNotEqual, oGreater, oSmaller, oGreaterEqual, oSmallerEqual,
|
||||
oAnd, oOr, oFunction
|
||||
};
|
||||
enum BaseFunctions {bfUnknown, bfSin, bfCos, bfTg, bfCtg,
|
||||
bfArcsin, bfArccos, bfArctg, bfArcctg,
|
||||
bfExp, bfRandom, bfSh, bfCh, bfTh, bfCth,
|
||||
bfSqrt, bfSqr, bfPow, bfAbs,
|
||||
bfLn, bfLg, bfLog, bfSign,
|
||||
bfIm, bfRe, bfArg, bfLen, bfConj,
|
||||
bfRad, bfDeg, bfJ0, bfJ1, bfJN,
|
||||
bfY0, bfY1, bfYN, bfMin, bfMax,
|
||||
bfClamp, bfStep, bfMix, bfDefined,
|
||||
bfCustom = 0xFFFF
|
||||
};
|
||||
|
||||
struct Instruction {
|
||||
Instruction() {;}
|
||||
Instruction(Operation oper, PIVector<int> opers, int out_ind, int func = -1) {
|
||||
operation = oper; operators = opers; out = out_ind; function = func;}
|
||||
Operation operation;
|
||||
PIVector<int> operators;
|
||||
int out;
|
||||
int function;
|
||||
};
|
||||
struct Element {
|
||||
Element() {;}
|
||||
Element(eType new_type, int new_num, int new_var_num = -1) {set(new_type, new_num, new_var_num);}
|
||||
void set(eType new_type, int new_num, int new_var_num = -1) {type = new_type; num = new_num; var_num = new_var_num;}
|
||||
eType type;
|
||||
int num;
|
||||
int var_num;
|
||||
};
|
||||
struct Function {
|
||||
Function() {arguments = 0; type = bfUnknown; handler = 0;}
|
||||
Function(const PIString & name, int args, BaseFunctions ftype) {identifier = name; arguments = args; type = ftype; handler = 0;}
|
||||
Function(const PIString & name, int args, FuncFunc h) {identifier = name; arguments = args; type = bfCustom; handler = h;}
|
||||
PIString identifier;
|
||||
BaseFunctions type;
|
||||
FuncFunc handler;
|
||||
int arguments;
|
||||
};
|
||||
struct Variable {
|
||||
Variable() {value = 0.;}
|
||||
Variable(const PIString & var_name, complexd val) {name = var_name; value = val;}
|
||||
PIString name;
|
||||
complexd value;
|
||||
};
|
||||
};
|
||||
/*
|
||||
≠ :
|
||||
≥ }
|
||||
≤ {
|
||||
⋀ &
|
||||
⋁ |
|
||||
*/
|
||||
|
||||
class PIP_EXPORT PIEvaluatorContent
|
||||
{
|
||||
friend class PIEvaluator;
|
||||
public:
|
||||
PIEvaluatorContent();
|
||||
~PIEvaluatorContent() {;}
|
||||
|
||||
void addFunction(const PIString & name, int args = 1) {functions.push_back(PIEvaluatorTypes::Function(name, args, getBaseFunction(name)));}
|
||||
void addVariable(const PIString & name, const complexd & val = 0.) {variables.push_back(PIEvaluatorTypes::Variable(name, val)); sortVariables();}
|
||||
void addCustomFunction(const PIString & name, int args_count, FuncFunc func) {functions << PIEvaluatorTypes::Function(name, args_count, func);}
|
||||
int functionsCount() const {return functions.size();}
|
||||
int variablesCount() const {return variables.size();}
|
||||
int customVariablesCount() const {return variables.size() - cv_count;}
|
||||
int findFunction(const PIString & name) const {for (uint i = 0; i < functions.size(); i++) if (functions[i].identifier == name) return i; return -1;}
|
||||
int findVariable(const PIString & var_name) const {for (uint i = 0; i < variables.size(); i++) if (variables[i].name == var_name) return i; return -1;}
|
||||
PIEvaluatorTypes::Function function(int index) {if (index < 0 || index >= functions.size_s()) return PIEvaluatorTypes::Function(); return functions[index];}
|
||||
PIEvaluatorTypes::Variable variable(int index) {if (index < 0 || index >= variables.size_s()) return PIEvaluatorTypes::Variable(); return variables[index];}
|
||||
PIEvaluatorTypes::Function function(const PIString & name) {return function(findFunction(name));}
|
||||
PIEvaluatorTypes::Variable variable(const PIString & name) {return variable(findVariable(name));}
|
||||
PIEvaluatorTypes::Variable customVariable(int index) {if (index < cv_count || index >= variables.size_s() + cv_count) return PIEvaluatorTypes::Variable(); return variables[index + cv_count];}
|
||||
bool setVariableValue(int index, complexd new_value);
|
||||
bool setVariableName(int index, const PIString & new_name);
|
||||
bool setVariableValue(const PIString & var_name, const complexd & new_value) {return setVariableValue(findVariable(var_name), new_value);}
|
||||
bool setVariableName(const PIString & var_name, const PIString & new_name) {return setVariableName(findVariable(var_name), new_name);}
|
||||
void removeVariable(int index) {variables.remove(index);}
|
||||
void removeVariable(const PIString & var_name) {removeVariable(findVariable(var_name));}
|
||||
void clearCustomVariables();
|
||||
void sortVariables();
|
||||
PIEvaluatorTypes::BaseFunctions getBaseFunction(const PIString & name);
|
||||
|
||||
private:
|
||||
PIVector<PIEvaluatorTypes::Function> functions;
|
||||
PIVector<PIEvaluatorTypes::Variable> variables;
|
||||
int cv_count;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class PIP_EXPORT PIEvaluator
|
||||
{
|
||||
public:
|
||||
|
||||
//! Constructs an empty evaluator
|
||||
PIEvaluator() {correct = false; data_ = 0;}
|
||||
|
||||
~PIEvaluator() {;}
|
||||
|
||||
|
||||
//! Returns custom data
|
||||
void * data() {return data_;}
|
||||
|
||||
//! Set custom data to "_data"
|
||||
void setData(void * _data) {data_ = _data;}
|
||||
|
||||
|
||||
//! Check mathematical expression and parse it to list of instructions
|
||||
bool check(const PIString & string);
|
||||
|
||||
//! Returns true if expression was checked succesfully
|
||||
bool isCorrect() const {return correct;}
|
||||
|
||||
//! Set variable value with name "name" to value "value". Add variable if it doesn`t exists
|
||||
int setVariable(const PIString & name, complexd value = 0.) {if (content.findVariable(name) < 0) content.addVariable(name, value); else content.setVariableValue(name, value); return content.findVariable(name);}
|
||||
|
||||
//! Set variable value with index "index" to value "value". Don`t add variable if it doesn`t exists
|
||||
void setVariable(int index, complexd value = 0.) {if (index >= 0 && index < content.variablesCount()) content.setVariableValue(index, value);}
|
||||
|
||||
void setCustomVariableValue(int index, complexd value = 0.) {content.variables[index + content.cv_count].value = value;}
|
||||
/*
|
||||
//! Add function "name" with arguments count "args_count" and handler "func". Three arguments will be passed to handler: \a data(), "args_count" and array of input values.
|
||||
void addFunction(const PIString & name, int args_count, FuncFunc func) {content.addCustomFunction(name, args_count, func);}
|
||||
*/
|
||||
//! Evaluate last successfully checked with function \a check() expression and returns result
|
||||
complexd evaluate();
|
||||
|
||||
//! Remove variable with name "name"
|
||||
void removeVariable(const PIString & name) {content.removeVariable(name);}
|
||||
|
||||
//! Remove all manually added variables
|
||||
void clearCustomVariables() {content.clearCustomVariables();}
|
||||
|
||||
//! Returns index of variable with name "name"
|
||||
int variableIndex(const PIString & name) const {return content.findVariable(name);}
|
||||
|
||||
//! Returns all unknown variables founded in last expression passed to \a check() function
|
||||
const PIStringList & unknownVariables() const {return unknownVars;}
|
||||
|
||||
//! Returns processed last expression passed to \a check() function
|
||||
const PIString & expression() const {return currentString;}
|
||||
|
||||
//! Returns last error description occured in \a check() function
|
||||
const PIString & error() const {return lastError;}
|
||||
|
||||
//! Returns last result of \a evaluate()
|
||||
const complexd & lastResult() const {return out;}
|
||||
|
||||
PIEvaluatorContent content;
|
||||
|
||||
private:
|
||||
const PIString & prepare(const PIString & string);
|
||||
const PIString & preprocess(const PIString & string);
|
||||
int parse(const PIString & string, int offset = 0);
|
||||
void convert();
|
||||
void checkBrackets();
|
||||
void removeSpaces();
|
||||
void findUnknownVariables();
|
||||
void removeJunk();
|
||||
void replaceOperators();
|
||||
void makeOutput(PIString & string);
|
||||
bool fillElements();
|
||||
bool setSignes();
|
||||
bool isSign(const PIChar & ch);
|
||||
PIString inverse(const PIString & string) {int len = string.length(); PIString s; for (int i = 0; i < len; i++) s += string[len - i - 1]; return s;}
|
||||
bool check();
|
||||
bool execInstructions();
|
||||
PIString inBrackets(const PIString & string);
|
||||
PIString operationChar(const PIEvaluatorTypes::Operation & operation);
|
||||
PIEvaluatorTypes::Operation operationInOrder(const int & index);
|
||||
complexd value(const int & index) {if (index < 0) return tmpvars[-index - 1].value; else return kvars->at(index).value;}
|
||||
inline complexd residue(const complexd & f, const complexd & s);
|
||||
inline void execFunction(const PIEvaluatorTypes::Instruction & ci);
|
||||
|
||||
PIVector<PIEvaluatorTypes::Element> elements;
|
||||
PIVector<PIEvaluatorTypes::Variable> currentVariables, variables, tmpvars, * kvars;
|
||||
PIVector<PIEvaluatorTypes::Instruction> instructions;
|
||||
PIStringList unknownVars;
|
||||
PIString currentString, lastError;
|
||||
complexd out;
|
||||
bool correct;
|
||||
void * data_;
|
||||
};
|
||||
|
||||
inline bool operator ==(PIEvaluatorTypes::Element e1, PIEvaluatorTypes::Element e2) {return (e1.type == e2.type && e1.num == e2.num);}
|
||||
inline bool operator !=(PIEvaluatorTypes::Element e1, PIEvaluatorTypes::Element e2) {return (e1.type != e2.type || e1.num != e2.num);}
|
||||
|
||||
#endif // PIEVALUATOR_H
|
||||
936
src/math/pifft.cpp
Normal file
936
src/math/pifft.cpp
Normal file
@@ -0,0 +1,936 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Class for FFT, IFFT and Hilbert transformations
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com, Andrey Bychkov work.a.b@yandex.ru
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pifft.h"
|
||||
|
||||
|
||||
PIFFT::PIFFT() {
|
||||
prepared = false;
|
||||
}
|
||||
|
||||
|
||||
PIVector<complexd> * PIFFT::calcFFT(const PIVector<complexd> & val) {
|
||||
result.clear();
|
||||
if (val.size_s() < 4) return &result;
|
||||
fftc1d(val, val.size());
|
||||
return &result;
|
||||
}
|
||||
|
||||
|
||||
PIVector<complexd> * PIFFT::calcFFTinverse(const PIVector<complexd> & val) {
|
||||
result.clear();
|
||||
if (val.size_s() < 4) return &result;
|
||||
fftc1dinv(val, val.size());
|
||||
return &result;
|
||||
}
|
||||
|
||||
|
||||
PIVector<complexd> * PIFFT::calcHilbert(const PIVector<double> & val) {
|
||||
result.clear();
|
||||
if (val.size_s() < 4) return &result;
|
||||
fftc1r(val, val.size());
|
||||
for (uint i = 0; i < result.size() / 2; i++) result[i] = result[i] * 2.;
|
||||
for (uint i = result.size() / 2; i < result.size(); i++) result[i] = 0;
|
||||
fftc1dinv(result, result.size());
|
||||
return &result;
|
||||
}
|
||||
|
||||
|
||||
PIVector<complexd> * PIFFT::calcFFT(const PIVector<double> & val) {
|
||||
result.clear();
|
||||
if (val.size_s() < 4) return &result;
|
||||
fftc1r(val, val.size());
|
||||
return &result;
|
||||
}
|
||||
|
||||
|
||||
PIVector<double> PIFFT::getAmplitude() {
|
||||
PIVector<double> a;
|
||||
double tmp;
|
||||
for (uint i = 0; i < result.size(); i++) {
|
||||
tmp = sqrt(result[i].real() * result[i].real() + result[i].imag() * result[i].imag());
|
||||
a.push_back(tmp);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
void PIFFT::fftc1d(const PIVector<complexd> & a, uint n) {
|
||||
createPlan(n);
|
||||
uint i;
|
||||
PIVector<double> buf;
|
||||
buf.resize(2 * n);
|
||||
for (i = 0; i < n; i++) {
|
||||
buf[2 * i + 0] = a[i].real();
|
||||
buf[2 * i + 1] = a[i].imag();
|
||||
}
|
||||
ftbaseexecuteplan(&buf, 0, n, &curplan);
|
||||
result.resize(n);
|
||||
for (i = 0; i < n; i++)
|
||||
result[i] = complexd(buf[2 * i + 0], buf[2 * i + 1]);
|
||||
}
|
||||
|
||||
|
||||
void PIFFT::fftc1r(const PIVector<double> & a, uint n) {
|
||||
uint i;
|
||||
if (n % 2 == 0) {
|
||||
PIVector<double> buf;
|
||||
uint n2 = n / 2;
|
||||
buf = a;
|
||||
createPlan(n2);
|
||||
ftbaseexecuteplan(&buf, 0, n2, &curplan);
|
||||
result.resize(n);
|
||||
uint idx;
|
||||
complexd hn, hmnc, v;
|
||||
for (i = 0; i <= n2; i++) {
|
||||
idx = 2 * (i % n2);
|
||||
hn = complexd(buf[idx + 0], buf[idx + 1]);
|
||||
idx = 2 * ((n2 - i) % n2);
|
||||
hmnc = complexd(buf[idx + 0], -buf[idx + 1]);
|
||||
v = complexd(sin(M_PI * i / n2), cos(M_PI * i / n2));
|
||||
result[i] = ((hn + hmnc) - (v * (hn - hmnc)));
|
||||
result[i] *= 0.5;
|
||||
}
|
||||
for (i = n2 + 1; i < n; i++)
|
||||
result[i] = conj(result[n - i]);
|
||||
} else {
|
||||
PIVector<complexd> cbuf;
|
||||
cbuf.resize(n);
|
||||
for (i = 0; i < n; i++)
|
||||
cbuf[i] = complexd(a[i], 0.);
|
||||
fftc1d(cbuf, n);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIFFT::fftc1dinv(const PIVector<complexd> & a, uint n) {
|
||||
PIVector<complexd> cbuf;
|
||||
cbuf.resize(n);
|
||||
uint i;
|
||||
for (i = 0; i < n; i++) {
|
||||
cbuf[i] = conj(a[i]);
|
||||
}
|
||||
fftc1d(cbuf, n);
|
||||
for (i = 0; i < n; i++) {
|
||||
result[i] = conj(result[i] / (double)n);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIFFT::createPlan(uint n) {
|
||||
curplan.plan.clear();
|
||||
curplan.precomputed.clear();
|
||||
curplan.stackbuf.clear();
|
||||
curplan.tmpbuf.clear();
|
||||
if (n < 2) return;
|
||||
ftbasegeneratecomplexfftplan(n, &curplan);
|
||||
prepared = true;
|
||||
}
|
||||
|
||||
|
||||
void PIFFT::ftbasegeneratecomplexfftplan(uint n, ftplan * plan) {
|
||||
int planarraysize;
|
||||
int plansize;
|
||||
int precomputedsize;
|
||||
int tmpmemsize;
|
||||
int stackmemsize;
|
||||
ae_int_t stackptr;
|
||||
planarraysize = 1;
|
||||
plansize = 0;
|
||||
precomputedsize = 0;
|
||||
stackmemsize = 0;
|
||||
stackptr = 0;
|
||||
tmpmemsize = 2 * n;
|
||||
curplan.plan.resize(planarraysize);
|
||||
int ftbase_ftbasecffttask = 0;
|
||||
ftbase_ftbasegenerateplanrec(n, ftbase_ftbasecffttask, plan, &plansize, &precomputedsize, &planarraysize, &tmpmemsize, &stackmemsize, stackptr);
|
||||
if (stackptr != 0) {
|
||||
return;//ae_assert(stackptr==0, "Internal error in FTBaseGenerateComplexFFTPlan: stack ptr!");
|
||||
}
|
||||
curplan.stackbuf.resize(piMax<int>(stackmemsize, 1)); //ae_vector_set_length(&curplan.stackbuf, ae_maxint(stackmemsize, 1));
|
||||
curplan.tmpbuf.resize(piMax<int>(tmpmemsize, 1)); //ae_vector_set_length(&(curplan.tmpbuf), ae_maxint(tmpmemsize, 1));
|
||||
curplan.precomputed.resize(piMax<int>(precomputedsize, 1)); //ae_vector_set_length(&curplan.precomputed, ae_maxint(precomputedsize, 1));
|
||||
stackptr = 0;
|
||||
ftbase_ftbaseprecomputeplanrec(plan, 0, stackptr);
|
||||
if (stackptr != 0) {
|
||||
return;//ae_assert(stackptr==0, "Internal error in FTBaseGenerateComplexFFTPlan: stack ptr!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
Recurrent subroutine for the FFTGeneratePlan:
|
||||
|
||||
PARAMETERS:
|
||||
N plan size
|
||||
IsReal whether input is real or not.
|
||||
subroutine MUST NOT ignore this flag because real
|
||||
inputs comes with non-initialized imaginary parts,
|
||||
so ignoring this flag will result in corrupted output
|
||||
HalfOut whether full output or only half of it from 0 to
|
||||
floor(N/2) is needed. This flag may be ignored if
|
||||
doing so will simplify calculations
|
||||
Plan plan array
|
||||
PlanSize size of used part (in integers)
|
||||
PrecomputedSize size of precomputed array allocated yet
|
||||
PlanArraySize plan array size (actual)
|
||||
TmpMemSize temporary memory required size
|
||||
BluesteinMemSize temporary memory required size
|
||||
|
||||
-- ALGLIB --
|
||||
Copyright 01.05.2009 by Bochkanov Sergey
|
||||
*************************************************************************/
|
||||
void PIFFT::ftbase_ftbasegenerateplanrec(
|
||||
int n,
|
||||
int tasktype,
|
||||
ftplan * plan,
|
||||
int * plansize,
|
||||
int * precomputedsize,
|
||||
int * planarraysize,
|
||||
int * tmpmemsize,
|
||||
int * stackmemsize,
|
||||
ae_int_t stackptr, int debugi) {
|
||||
int k, m, n1, n2, esize, entryoffset;
|
||||
int ftbase_ftbaseplanentrysize = 8;
|
||||
int ftbase_ftbasecffttask = 0;
|
||||
int ftbase_fftcooleytukeyplan = 0;
|
||||
int ftbase_fftbluesteinplan = 1;
|
||||
int ftbase_fftcodeletplan = 2;
|
||||
int ftbase_fftrealcooleytukeyplan = 5;
|
||||
int ftbase_fftemptyplan = 6;
|
||||
if (*plansize + ftbase_ftbaseplanentrysize > (*planarraysize)) {
|
||||
curplan.plan.resize(8 * (*planarraysize));
|
||||
*planarraysize = 8 * (*planarraysize);
|
||||
}
|
||||
entryoffset = *plansize;
|
||||
esize = ftbase_ftbaseplanentrysize;
|
||||
*plansize = *plansize + esize;
|
||||
if (n == 1) {
|
||||
curplan.plan[entryoffset + 0] = esize;
|
||||
curplan.plan[entryoffset + 1] = -1;
|
||||
curplan.plan[entryoffset + 2] = -1;
|
||||
curplan.plan[entryoffset + 3] = ftbase_fftemptyplan;
|
||||
curplan.plan[entryoffset + 4] = -1;
|
||||
curplan.plan[entryoffset + 5] = -1;
|
||||
curplan.plan[entryoffset + 6] = -1;
|
||||
curplan.plan[entryoffset + 7] = -1;
|
||||
return;
|
||||
}
|
||||
ftbasefactorize(n, &n1, &n2);
|
||||
if (n1 != 1) {
|
||||
*tmpmemsize = piMax<int>(*tmpmemsize, 2 * n1 * n2);
|
||||
curplan.plan[entryoffset + 0] = esize;
|
||||
curplan.plan[entryoffset + 1] = n1;
|
||||
curplan.plan[entryoffset + 2] = n2;
|
||||
if (tasktype == ftbase_ftbasecffttask)
|
||||
curplan.plan[entryoffset + 3] = ftbase_fftcooleytukeyplan;
|
||||
else
|
||||
curplan.plan[entryoffset + 3] = ftbase_fftrealcooleytukeyplan;
|
||||
curplan.plan[entryoffset + 4] = 0;
|
||||
curplan.plan[entryoffset + 5] = *plansize;
|
||||
debugi++;
|
||||
ftbase_ftbasegenerateplanrec(n1, ftbase_ftbasecffttask, plan, plansize, precomputedsize, planarraysize, tmpmemsize, stackmemsize, stackptr, debugi);
|
||||
curplan.plan[entryoffset + 6] = *plansize;
|
||||
ftbase_ftbasegenerateplanrec(n2, ftbase_ftbasecffttask, plan, plansize, precomputedsize, planarraysize, tmpmemsize, stackmemsize, stackptr, debugi);
|
||||
curplan.plan[entryoffset + 7] = -1;
|
||||
return;
|
||||
} else {
|
||||
if (n >= 2 && n <= 5) {
|
||||
curplan.plan[entryoffset + 0] = esize;
|
||||
curplan.plan[entryoffset + 1] = n1;
|
||||
curplan.plan[entryoffset + 2] = n2;
|
||||
curplan.plan[entryoffset + 3] = ftbase_fftcodeletplan;
|
||||
curplan.plan[entryoffset + 4] = 0;
|
||||
curplan.plan[entryoffset + 5] = -1;
|
||||
curplan.plan[entryoffset + 6] = -1;
|
||||
curplan.plan[entryoffset + 7] = *precomputedsize;
|
||||
if (n == 3)
|
||||
*precomputedsize = *precomputedsize + 2;
|
||||
if (n == 5)
|
||||
*precomputedsize = *precomputedsize + 5;
|
||||
return;
|
||||
} else {
|
||||
k = 2 * n2 - 1;
|
||||
m = ftbasefindsmooth(k);
|
||||
*tmpmemsize = piMax<int>(*tmpmemsize, 2 * m);
|
||||
curplan.plan[entryoffset + 0] = esize;
|
||||
curplan.plan[entryoffset + 1] = n2;
|
||||
curplan.plan[entryoffset + 2] = -1;
|
||||
curplan.plan[entryoffset + 3] = ftbase_fftbluesteinplan;
|
||||
curplan.plan[entryoffset + 4] = m;
|
||||
curplan.plan[entryoffset + 5] = *plansize;
|
||||
stackptr = stackptr + 2 * 2 * m;
|
||||
*stackmemsize = piMax<int>(*stackmemsize, stackptr);
|
||||
ftbase_ftbasegenerateplanrec(m, ftbase_ftbasecffttask, plan, plansize, precomputedsize, planarraysize, tmpmemsize, stackmemsize, stackptr);
|
||||
stackptr = stackptr - 2 * 2 * m;
|
||||
curplan.plan[entryoffset + 6] = -1;
|
||||
curplan.plan[entryoffset + 7] = *precomputedsize;
|
||||
*precomputedsize = *precomputedsize + 2 * m + 2 * n;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
Recurrent subroutine for precomputing FFT plans
|
||||
|
||||
-- ALGLIB --
|
||||
Copyright 01.05.2009 by Bochkanov Sergey
|
||||
*************************************************************************/
|
||||
void PIFFT::ftbase_ftbaseprecomputeplanrec(ftplan * plan,
|
||||
int entryoffset,
|
||||
ae_int_t stackptr) {
|
||||
int n1, n2, n, m, offs;
|
||||
double v, bx, by;
|
||||
int ftbase_fftcooleytukeyplan = 0;
|
||||
int ftbase_fftbluesteinplan = 1;
|
||||
int ftbase_fftcodeletplan = 2;
|
||||
int ftbase_fhtcooleytukeyplan = 3;
|
||||
int ftbase_fhtcodeletplan = 4;
|
||||
int ftbase_fftrealcooleytukeyplan = 5;
|
||||
if ((curplan.plan[entryoffset + 3] == ftbase_fftcooleytukeyplan || curplan.plan[entryoffset + 3] == ftbase_fftrealcooleytukeyplan) || curplan.plan[entryoffset + 3] == ftbase_fhtcooleytukeyplan) {
|
||||
ftbase_ftbaseprecomputeplanrec(plan, curplan.plan[entryoffset + 5], stackptr);
|
||||
ftbase_ftbaseprecomputeplanrec(plan, curplan.plan[entryoffset + 6], stackptr);
|
||||
return;
|
||||
}
|
||||
if (curplan.plan[entryoffset + 3] == ftbase_fftcodeletplan || curplan.plan[entryoffset + 3] == ftbase_fhtcodeletplan) {
|
||||
n1 = curplan.plan[entryoffset + 1];
|
||||
n2 = curplan.plan[entryoffset + 2];
|
||||
n = n1 * n2;
|
||||
if (n == 3) {
|
||||
offs = curplan.plan[entryoffset + 7];
|
||||
curplan.precomputed[offs + 0] = cos(2 * M_PI / 3) - 1;
|
||||
curplan.precomputed[offs + 1] = sin(2 * M_PI / 3);
|
||||
return;
|
||||
}
|
||||
if (n == 5) {
|
||||
offs = curplan.plan[entryoffset + 7];
|
||||
v = 2 * M_PI / 5;
|
||||
curplan.precomputed[offs + 0] = (cos(v) + cos(2 * v)) / 2 - 1;
|
||||
curplan.precomputed[offs + 1] = (cos(v) - cos(2 * v)) / 2;
|
||||
curplan.precomputed[offs + 2] = -sin(v);
|
||||
curplan.precomputed[offs + 3] = -(sin(v) + sin(2 * v));
|
||||
curplan.precomputed[offs + 4] = sin(v) - sin(2 * v);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (curplan.plan[entryoffset + 3] == ftbase_fftbluesteinplan) {
|
||||
ftbase_ftbaseprecomputeplanrec(plan, curplan.plan[entryoffset + 5], stackptr);
|
||||
n = curplan.plan[entryoffset + 1];
|
||||
m = curplan.plan[entryoffset + 4];
|
||||
offs = curplan.plan[entryoffset + 7];
|
||||
for (int i = 0; i <= 2 * m - 1; i++)
|
||||
curplan.precomputed[offs + i] = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
bx = cos(M_PI * sqr(i) / n);
|
||||
by = sin(M_PI * sqr(i) / n);
|
||||
curplan.precomputed[offs + 2 * i + 0] = bx;
|
||||
curplan.precomputed[offs + 2 * i + 1] = by;
|
||||
curplan.precomputed[offs + 2 * m + 2 * i + 0] = bx;
|
||||
curplan.precomputed[offs + 2 * m + 2 * i + 1] = by;
|
||||
if (i > 0) {
|
||||
curplan.precomputed[offs + 2 * (m - i) + 0] = bx;
|
||||
curplan.precomputed[offs + 2 * (m - i) + 1] = by;
|
||||
}
|
||||
}
|
||||
ftbaseexecuteplanrec(&curplan.precomputed, offs, plan, curplan.plan[entryoffset + 5], stackptr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIFFT::ftbasefactorize(int n, int * n1, int * n2) {
|
||||
*n1 = *n2 = 0;
|
||||
int ftbase_ftbasecodeletrecommended = 5;
|
||||
if ((*n1) * (*n2) != n) {
|
||||
for (int j = ftbase_ftbasecodeletrecommended; j >= 2; j--) {
|
||||
if (n % j == 0) {
|
||||
*n1 = j;
|
||||
*n2 = n / j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((*n1) * (*n2) != n) {
|
||||
for (int j = ftbase_ftbasecodeletrecommended + 1; j <= n - 1; j++) {
|
||||
if (n % j == 0) {
|
||||
*n1 = j;
|
||||
*n2 = n / j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((*n1) * (*n2) != n) {
|
||||
*n1 = 1;
|
||||
*n2 = n;
|
||||
}
|
||||
if ((*n2) == 1 && (*n1) != 1) {
|
||||
*n2 = *n1;
|
||||
*n1 = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
Is number smooth?
|
||||
|
||||
-- ALGLIB --
|
||||
Copyright 01.05.2009 by Bochkanov Sergey
|
||||
*************************************************************************/
|
||||
void PIFFT::ftbase_ftbasefindsmoothrec(int n, int seed, int leastfactor, int * best) {
|
||||
if (seed >= n) {
|
||||
*best = piMini(*best, seed);
|
||||
return;
|
||||
}
|
||||
if (leastfactor <= 2)
|
||||
ftbase_ftbasefindsmoothrec(n, seed * 2, 2, best);
|
||||
if (leastfactor <= 3)
|
||||
ftbase_ftbasefindsmoothrec(n, seed * 3, 3, best);
|
||||
if (leastfactor <= 5)
|
||||
ftbase_ftbasefindsmoothrec(n, seed * 5, 5, best);
|
||||
}
|
||||
|
||||
|
||||
int PIFFT::ftbasefindsmooth(int n) {
|
||||
int best, result;
|
||||
best = 2;
|
||||
while (best < n)
|
||||
best = 2 * best;
|
||||
ftbase_ftbasefindsmoothrec(n, 1, 2, &best);
|
||||
result = best;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void PIFFT::ftbase_internalreallintranspose(PIVector<double> * a, int m, int n, int astart, PIVector<double> * buf) {
|
||||
ftbase_fftirltrec(a, astart, n, buf, 0, m, m, n);
|
||||
for (int i = 0; i < 2 * m * n; i++) (*a)[astart + i] = (*buf)[i];
|
||||
}
|
||||
|
||||
|
||||
void PIFFT::ftbase_fftirltrec(PIVector<double> * a, int astart, int astride, PIVector<double> * b, int bstart, int bstride, int m, int n) {
|
||||
int idx1, idx2;
|
||||
int m1, n1;
|
||||
if (m == 0 || n == 0)
|
||||
return;
|
||||
if (piMaxi(m, n) <= 8) {
|
||||
for (int i = 0; i <= m - 1; i++) {
|
||||
idx1 = bstart + i;
|
||||
idx2 = astart + i * astride;
|
||||
for (int j = 0; j <= n - 1; j++) {
|
||||
(*b)[idx1] = a->at(idx2);
|
||||
idx1 = idx1 + bstride;
|
||||
idx2 = idx2 + 1;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (n > m) {
|
||||
n1 = n / 2;
|
||||
if (n - n1 >= 8 && n1 % 8 != 0)
|
||||
n1 = n1 + (8 - n1 % 8);
|
||||
ftbase_fftirltrec(a, astart, astride, b, bstart, bstride, m, n1);
|
||||
ftbase_fftirltrec(a, astart + n1, astride, b, bstart + n1 * bstride, bstride, m, n - n1);
|
||||
} else {
|
||||
m1 = m / 2;
|
||||
if (m - m1 >= 8 && m1 % 8 != 0)
|
||||
m1 = m1 + (8 - m1 % 8);
|
||||
ftbase_fftirltrec(a, astart, astride, b, bstart, bstride, m1, n);
|
||||
ftbase_fftirltrec(a, astart + m1 * astride, astride, b, bstart + m1, bstride, m - m1, n);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIFFT::ftbase_internalcomplexlintranspose(PIVector<double> * a, int m, int n, int astart, PIVector<double> * buf) {
|
||||
ftbase_ffticltrec(a, astart, n, buf, 0, m, m, n);
|
||||
for (int i = 0; i < 2 * m * n; i++)
|
||||
(*a)[astart + i] = (*buf)[i];
|
||||
}
|
||||
|
||||
|
||||
void PIFFT::ftbase_ffticltrec(PIVector<double> * a, int astart, int astride, PIVector<double> * b, int bstart, int bstride, int m, int n) {
|
||||
int idx1, idx2, m2, m1, n1;
|
||||
if (m == 0 || n == 0)
|
||||
return;
|
||||
if (piMax<int>(m, n) <= 8) {
|
||||
m2 = 2 * bstride;
|
||||
for (int i = 0; i <= m - 1; i++) {
|
||||
idx1 = bstart + 2 * i;
|
||||
idx2 = astart + 2 * i * astride;
|
||||
for (int j = 0; j <= n - 1; j++) {
|
||||
(*b)[idx1 + 0] = a->at(idx2 + 0);
|
||||
(*b)[idx1 + 1] = a->at(idx2 + 1);
|
||||
idx1 = idx1 + m2;
|
||||
idx2 = idx2 + 2;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (n > m) {
|
||||
n1 = n / 2;
|
||||
if (n - n1 >= 8 && n1 % 8 != 0)
|
||||
n1 = n1 + (8 - n1 % 8);
|
||||
ftbase_ffticltrec(a, astart, astride, b, bstart, bstride, m, n1);
|
||||
ftbase_ffticltrec(a, astart + 2 * n1, astride, b, bstart + 2 * n1 * bstride, bstride, m, n - n1);
|
||||
} else {
|
||||
m1 = m / 2;
|
||||
if (m - m1 >= 8 && m1 % 8 != 0)
|
||||
m1 = m1 + (8 - m1 % 8);
|
||||
ftbase_ffticltrec(a, astart, astride, b, bstart, bstride, m1, n);
|
||||
ftbase_ffticltrec(a, astart + 2 * m1 * astride, astride, b, bstart + 2 * m1, bstride, m - m1, n);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIFFT::ftbaseexecuteplan(PIVector<double> * a, int aoffset, int n, ftplan * plan) {
|
||||
ae_int_t stackptr;
|
||||
stackptr = 0;
|
||||
ftbaseexecuteplanrec(a, aoffset, plan, 0, stackptr);
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
Recurrent subroutine for the FTBaseExecutePlan
|
||||
|
||||
Parameters:
|
||||
A FFT'ed array
|
||||
AOffset offset of the FFT'ed part (distance is measured in doubles)
|
||||
|
||||
-- ALGLIB --
|
||||
Copyright 01.05.2009 by Bochkanov Sergey
|
||||
*************************************************************************/
|
||||
void PIFFT::ftbaseexecuteplanrec(PIVector<double> * a, int aoffset, ftplan * plan, int entryoffset, ae_int_t stackptr) {
|
||||
int n1, n2, n, m, offs, offs1, offs2, offsa, offsb, offsp;
|
||||
double hk, hnk, x, y, bx, by, v0, v1, v2, v3;
|
||||
double a0x, a0y, a1x, a1y, a2x, a2y, a3x, a3y;
|
||||
double t1x, t1y, t2x, t2y, t3x, t3y, t4x, t4y, t5x, t5y;
|
||||
double m1x, m1y, m2x, m2y, m3x, m3y, m4x, m4y, m5x, m5y;
|
||||
double s1x, s1y, s2x, s2y, s3x, s3y, s4x, s4y, s5x, s5y;
|
||||
double c1, c2, c3, c4, c5;
|
||||
int ftbase_fftcooleytukeyplan = 0;
|
||||
int ftbase_fftbluesteinplan = 1;
|
||||
int ftbase_fftcodeletplan = 2;
|
||||
int ftbase_fhtcooleytukeyplan = 3;
|
||||
int ftbase_fhtcodeletplan = 4;
|
||||
int ftbase_fftrealcooleytukeyplan = 5;
|
||||
int ftbase_fftemptyplan = 6;
|
||||
PIVector<double> & tmpb(curplan.tmpbuf);
|
||||
|
||||
if (curplan.plan[entryoffset + 3] == ftbase_fftemptyplan)
|
||||
return;
|
||||
if (curplan.plan[entryoffset + 3] == ftbase_fftcooleytukeyplan) {
|
||||
n1 = curplan.plan[entryoffset + 1];
|
||||
n2 = curplan.plan[entryoffset + 2];
|
||||
ftbase_internalcomplexlintranspose(a, n1, n2, aoffset, &(curplan.tmpbuf));
|
||||
for (int i = 0; i <= n2 - 1; i++)
|
||||
ftbaseexecuteplanrec(a, aoffset + i * n1 * 2, plan, curplan.plan[entryoffset + 5], stackptr);
|
||||
ftbase_ffttwcalc(a, aoffset, n1, n2);
|
||||
ftbase_internalcomplexlintranspose(a, n2, n1, aoffset, &(curplan.tmpbuf));
|
||||
for (int i = 0; i <= n1 - 1; i++)
|
||||
ftbaseexecuteplanrec(a, aoffset + i * n2 * 2, plan, curplan.plan[entryoffset + 6], stackptr);
|
||||
ftbase_internalcomplexlintranspose(a, n1, n2, aoffset, &(curplan.tmpbuf));
|
||||
return;
|
||||
}
|
||||
if (curplan.plan[entryoffset + 3] == ftbase_fftrealcooleytukeyplan) {
|
||||
n1 = curplan.plan[entryoffset + 1];
|
||||
n2 = curplan.plan[entryoffset + 2];
|
||||
ftbase_internalcomplexlintranspose(a, n2, n1, aoffset, &(curplan.tmpbuf));
|
||||
for (int i = 0; i <= n1 / 2 - 1; i++) {
|
||||
offs = aoffset + 2 * i * n2 * 2;
|
||||
for (int k = 0; k <= n2 - 1; k++)
|
||||
(*a)[offs + 2 * k + 1] = (*a)[offs + 2 * n2 + 2 * k + 0];
|
||||
ftbaseexecuteplanrec(a, offs, plan, curplan.plan[entryoffset + 6], stackptr);
|
||||
tmpb[0] = (*a)[offs + 0];
|
||||
tmpb[1] = 0;
|
||||
tmpb[2 * n2 + 0] = (*a)[offs + 1];
|
||||
tmpb[2 * n2 + 1] = 0;
|
||||
for (int k = 1; k <= n2 - 1; k++) {
|
||||
offs1 = 2 * k;
|
||||
offs2 = 2 * n2 + 2 * k;
|
||||
hk = (*a)[offs + 2 * k + 0];
|
||||
hnk = (*a)[offs + 2 * (n2 - k) + 0];
|
||||
tmpb[offs1 + 0] = 0.5 * (hk + hnk);
|
||||
tmpb[offs2 + 1] = -0.5 * (hk - hnk);
|
||||
hk = (*a)[offs + 2 * k + 1];
|
||||
hnk = (*a)[offs + 2 * (n2 - k) + 1];
|
||||
tmpb[offs2 + 0] = 0.5 * (hk + hnk);
|
||||
tmpb[offs1 + 1] = 0.5 * (hk - hnk);
|
||||
}
|
||||
for (int k = 0; k < 2 * n2 * 2; k++) (*a)[offs + k] = tmpb[k];
|
||||
}
|
||||
if (n1 % 2 != 0)
|
||||
ftbaseexecuteplanrec(a, aoffset + (n1 - 1)*n2 * 2, plan, curplan.plan[entryoffset + 6], stackptr);
|
||||
ftbase_ffttwcalc(a, aoffset, n2, n1);
|
||||
ftbase_internalcomplexlintranspose(a, n1, n2, aoffset, &(curplan.tmpbuf));
|
||||
for (int i = 0; i <= n2 - 1; i++)
|
||||
ftbaseexecuteplanrec(a, aoffset + i * n1 * 2, plan, curplan.plan[entryoffset + 5], stackptr);
|
||||
ftbase_internalcomplexlintranspose(a, n2, n1, aoffset, &(curplan.tmpbuf));
|
||||
return;
|
||||
}
|
||||
if (curplan.plan[entryoffset + 3] == ftbase_fhtcooleytukeyplan) {
|
||||
n1 = curplan.plan[entryoffset + 1];
|
||||
n2 = curplan.plan[entryoffset + 2];
|
||||
n = n1 * n2;
|
||||
ftbase_internalreallintranspose(a, n1, n2, aoffset, &(curplan.tmpbuf));
|
||||
for (int i = 0; i <= n2 - 1; i++)
|
||||
ftbaseexecuteplanrec(a, aoffset + i * n1, plan, curplan.plan[entryoffset + 5], stackptr);
|
||||
for (int i = 0; i <= n2 - 1; i++) {
|
||||
for (int j = 0; j <= n1 - 1; j++) {
|
||||
offsa = aoffset + i * n1;
|
||||
hk = (*a)[offsa + j];
|
||||
hnk = (*a)[offsa + (n1 - j) % n1];
|
||||
offs = 2 * (i * n1 + j);
|
||||
tmpb[offs + 0] = -0.5 * (hnk - hk);
|
||||
tmpb[offs + 1] = 0.5 * (hk + hnk);
|
||||
}
|
||||
}
|
||||
ftbase_ffttwcalc(&(curplan.tmpbuf), 0, n1, n2);
|
||||
for (int j = 0; j <= n1 - 1; j++)
|
||||
(*a)[aoffset + j] = tmpb[2 * j + 0] + tmpb[2 * j + 1];
|
||||
if (n2 % 2 == 0) {
|
||||
offs = 2 * (n2 / 2) * n1;
|
||||
offsa = aoffset + n2 / 2 * n1;
|
||||
for (int j = 0; j <= n1 - 1; j++)
|
||||
(*a)[offsa + j] = tmpb[offs + 2 * j + 0] + tmpb[offs + 2 * j + 1];
|
||||
}
|
||||
for (int i = 1; i <= (n2 + 1) / 2 - 1; i++) {
|
||||
offs = 2 * i * n1;
|
||||
offs2 = 2 * (n2 - i) * n1;
|
||||
offsa = aoffset + i * n1;
|
||||
for (int j = 0; j <= n1 - 1; j++)
|
||||
(*a)[offsa + j] = tmpb[offs + 2 * j + 1] + tmpb[offs2 + 2 * j + 0];
|
||||
offsa = aoffset + (n2 - i) * n1;
|
||||
for (int j = 0; j <= n1 - 1; j++)
|
||||
(*a)[offsa + j] = tmpb[offs + 2 * j + 0] + tmpb[offs2 + 2 * j + 1];
|
||||
}
|
||||
ftbase_internalreallintranspose(a, n2, n1, aoffset, &(curplan.tmpbuf));
|
||||
for (int i = 0; i <= n1 - 1; i++)
|
||||
ftbaseexecuteplanrec(a, aoffset + i * n2, plan, curplan.plan[entryoffset + 6], stackptr);
|
||||
ftbase_internalreallintranspose(a, n1, n2, aoffset, &(curplan.tmpbuf));
|
||||
return;
|
||||
}
|
||||
if (curplan.plan[entryoffset + 3] == ftbase_fftcodeletplan) {
|
||||
n1 = curplan.plan[entryoffset + 1];
|
||||
n2 = curplan.plan[entryoffset + 2];
|
||||
n = n1 * n2;
|
||||
if (n == 2) {
|
||||
a0x = (*a)[aoffset + 0];
|
||||
a0y = (*a)[aoffset + 1];
|
||||
a1x = (*a)[aoffset + 2];
|
||||
a1y = (*a)[aoffset + 3];
|
||||
v0 = a0x + a1x;
|
||||
v1 = a0y + a1y;
|
||||
v2 = a0x - a1x;
|
||||
v3 = a0y - a1y;
|
||||
(*a)[aoffset + 0] = v0;
|
||||
(*a)[aoffset + 1] = v1;
|
||||
(*a)[aoffset + 2] = v2;
|
||||
(*a)[aoffset + 3] = v3;
|
||||
return;
|
||||
}
|
||||
if (n == 3) {
|
||||
offs = curplan.plan[entryoffset + 7];
|
||||
c1 = curplan.precomputed[offs + 0];
|
||||
c2 = curplan.precomputed[offs + 1];
|
||||
a0x = (*a)[aoffset + 0];
|
||||
a0y = (*a)[aoffset + 1];
|
||||
a1x = (*a)[aoffset + 2];
|
||||
a1y = (*a)[aoffset + 3];
|
||||
a2x = (*a)[aoffset + 4];
|
||||
a2y = (*a)[aoffset + 5];
|
||||
t1x = a1x + a2x;
|
||||
t1y = a1y + a2y;
|
||||
a0x = a0x + t1x;
|
||||
a0y = a0y + t1y;
|
||||
m1x = c1 * t1x;
|
||||
m1y = c1 * t1y;
|
||||
m2x = c2 * (a1y - a2y);
|
||||
m2y = c2 * (a2x - a1x);
|
||||
s1x = a0x + m1x;
|
||||
s1y = a0y + m1y;
|
||||
a1x = s1x + m2x;
|
||||
a1y = s1y + m2y;
|
||||
a2x = s1x - m2x;
|
||||
a2y = s1y - m2y;
|
||||
(*a)[aoffset + 0] = a0x;
|
||||
(*a)[aoffset + 1] = a0y;
|
||||
(*a)[aoffset + 2] = a1x;
|
||||
(*a)[aoffset + 3] = a1y;
|
||||
(*a)[aoffset + 4] = a2x;
|
||||
(*a)[aoffset + 5] = a2y;
|
||||
return;
|
||||
}
|
||||
if (n == 4) {
|
||||
a0x = (*a)[aoffset + 0];
|
||||
a0y = (*a)[aoffset + 1];
|
||||
a1x = (*a)[aoffset + 2];
|
||||
a1y = (*a)[aoffset + 3];
|
||||
a2x = (*a)[aoffset + 4];
|
||||
a2y = (*a)[aoffset + 5];
|
||||
a3x = (*a)[aoffset + 6];
|
||||
a3y = (*a)[aoffset + 7];
|
||||
t1x = a0x + a2x;
|
||||
t1y = a0y + a2y;
|
||||
t2x = a1x + a3x;
|
||||
t2y = a1y + a3y;
|
||||
m2x = a0x - a2x;
|
||||
m2y = a0y - a2y;
|
||||
m3x = a1y - a3y;
|
||||
m3y = a3x - a1x;
|
||||
(*a)[aoffset + 0] = t1x + t2x;
|
||||
(*a)[aoffset + 1] = t1y + t2y;
|
||||
(*a)[aoffset + 4] = t1x - t2x;
|
||||
(*a)[aoffset + 5] = t1y - t2y;
|
||||
(*a)[aoffset + 2] = m2x + m3x;
|
||||
(*a)[aoffset + 3] = m2y + m3y;
|
||||
(*a)[aoffset + 6] = m2x - m3x;
|
||||
(*a)[aoffset + 7] = m2y - m3y;
|
||||
return;
|
||||
}
|
||||
if (n == 5) {
|
||||
offs = curplan.plan[entryoffset + 7];
|
||||
c1 = curplan.precomputed[offs + 0];
|
||||
c2 = curplan.precomputed[offs + 1];
|
||||
c3 = curplan.precomputed[offs + 2];
|
||||
c4 = curplan.precomputed[offs + 3];
|
||||
c5 = curplan.precomputed[offs + 4];
|
||||
t1x = (*a)[aoffset + 2] + (*a)[aoffset + 8];
|
||||
t1y = (*a)[aoffset + 3] + (*a)[aoffset + 9];
|
||||
t2x = (*a)[aoffset + 4] + (*a)[aoffset + 6];
|
||||
t2y = (*a)[aoffset + 5] + (*a)[aoffset + 7];
|
||||
t3x = (*a)[aoffset + 2] - (*a)[aoffset + 8];
|
||||
t3y = (*a)[aoffset + 3] - (*a)[aoffset + 9];
|
||||
t4x = (*a)[aoffset + 6] - (*a)[aoffset + 4];
|
||||
t4y = (*a)[aoffset + 7] - (*a)[aoffset + 5];
|
||||
t5x = t1x + t2x;
|
||||
t5y = t1y + t2y;
|
||||
(*a)[aoffset + 0] = (*a)[aoffset + 0] + t5x;
|
||||
(*a)[aoffset + 1] = (*a)[aoffset + 1] + t5y;
|
||||
m1x = c1 * t5x;
|
||||
m1y = c1 * t5y;
|
||||
m2x = c2 * (t1x - t2x);
|
||||
m2y = c2 * (t1y - t2y);
|
||||
m3x = -c3 * (t3y + t4y);
|
||||
m3y = c3 * (t3x + t4x);
|
||||
m4x = -c4 * t4y;
|
||||
m4y = c4 * t4x;
|
||||
m5x = -c5 * t3y;
|
||||
m5y = c5 * t3x;
|
||||
s3x = m3x - m4x;
|
||||
s3y = m3y - m4y;
|
||||
s5x = m3x + m5x;
|
||||
s5y = m3y + m5y;
|
||||
s1x = (*a)[aoffset + 0] + m1x;
|
||||
s1y = (*a)[aoffset + 1] + m1y;
|
||||
s2x = s1x + m2x;
|
||||
s2y = s1y + m2y;
|
||||
s4x = s1x - m2x;
|
||||
s4y = s1y - m2y;
|
||||
(*a)[aoffset + 2] = s2x + s3x;
|
||||
(*a)[aoffset + 3] = s2y + s3y;
|
||||
(*a)[aoffset + 4] = s4x + s5x;
|
||||
(*a)[aoffset + 5] = s4y + s5y;
|
||||
(*a)[aoffset + 6] = s4x - s5x;
|
||||
(*a)[aoffset + 7] = s4y - s5y;
|
||||
(*a)[aoffset + 8] = s2x - s3x;
|
||||
(*a)[aoffset + 9] = s2y - s3y;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (curplan.plan[entryoffset + 3] == ftbase_fhtcodeletplan) {
|
||||
n1 = curplan.plan[entryoffset + 1];
|
||||
n2 = curplan.plan[entryoffset + 2];
|
||||
n = n1 * n2;
|
||||
if (n == 2) {
|
||||
a0x = (*a)[aoffset + 0];
|
||||
a1x = (*a)[aoffset + 1];
|
||||
(*a)[aoffset + 0] = a0x + a1x;
|
||||
(*a)[aoffset + 1] = a0x - a1x;
|
||||
return;
|
||||
}
|
||||
if (n == 3) {
|
||||
offs = curplan.plan[entryoffset + 7];
|
||||
c1 = curplan.precomputed[offs + 0];
|
||||
c2 = curplan.precomputed[offs + 1];
|
||||
a0x = (*a)[aoffset + 0];
|
||||
a1x = (*a)[aoffset + 1];
|
||||
a2x = (*a)[aoffset + 2];
|
||||
t1x = a1x + a2x;
|
||||
a0x = a0x + t1x;
|
||||
m1x = c1 * t1x;
|
||||
m2y = c2 * (a2x - a1x);
|
||||
s1x = a0x + m1x;
|
||||
(*a)[aoffset + 0] = a0x;
|
||||
(*a)[aoffset + 1] = s1x - m2y;
|
||||
(*a)[aoffset + 2] = s1x + m2y;
|
||||
return;
|
||||
}
|
||||
if (n == 4) {
|
||||
a0x = (*a)[aoffset + 0];
|
||||
a1x = (*a)[aoffset + 1];
|
||||
a2x = (*a)[aoffset + 2];
|
||||
a3x = (*a)[aoffset + 3];
|
||||
t1x = a0x + a2x;
|
||||
t2x = a1x + a3x;
|
||||
m2x = a0x - a2x;
|
||||
m3y = a3x - a1x;
|
||||
(*a)[aoffset + 0] = t1x + t2x;
|
||||
(*a)[aoffset + 1] = m2x - m3y;
|
||||
(*a)[aoffset + 2] = t1x - t2x;
|
||||
(*a)[aoffset + 3] = m2x + m3y;
|
||||
return;
|
||||
}
|
||||
if (n == 5) {
|
||||
offs = curplan.plan[entryoffset + 7];
|
||||
c1 = curplan.precomputed[offs + 0];
|
||||
c2 = curplan.precomputed[offs + 1];
|
||||
c3 = curplan.precomputed[offs + 2];
|
||||
c4 = curplan.precomputed[offs + 3];
|
||||
c5 = curplan.precomputed[offs + 4];
|
||||
t1x = (*a)[aoffset + 1] + (*a)[aoffset + 4];
|
||||
t2x = (*a)[aoffset + 2] + (*a)[aoffset + 3];
|
||||
t3x = (*a)[aoffset + 1] - (*a)[aoffset + 4];
|
||||
t4x = (*a)[aoffset + 3] - (*a)[aoffset + 2];
|
||||
t5x = t1x + t2x;
|
||||
v0 = (*a)[aoffset + 0] + t5x;
|
||||
(*a)[aoffset + 0] = v0;
|
||||
m2x = c2 * (t1x - t2x);
|
||||
m3y = c3 * (t3x + t4x);
|
||||
s3y = m3y - c4 * t4x;
|
||||
s5y = m3y + c5 * t3x;
|
||||
s1x = v0 + c1 * t5x;
|
||||
s2x = s1x + m2x;
|
||||
s4x = s1x - m2x;
|
||||
(*a)[aoffset + 1] = s2x - s3y;
|
||||
(*a)[aoffset + 2] = s4x - s5y;
|
||||
(*a)[aoffset + 3] = s4x + s5y;
|
||||
(*a)[aoffset + 4] = s2x + s3y;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (curplan.plan[entryoffset + 3] == ftbase_fftbluesteinplan) {
|
||||
n = curplan.plan[entryoffset + 1];
|
||||
m = curplan.plan[entryoffset + 4];
|
||||
offs = curplan.plan[entryoffset + 7];
|
||||
for (int i = stackptr + 2 * n; i <= stackptr + 2 * m - 1; i++)
|
||||
curplan.stackbuf[i] = 0;
|
||||
offsp = offs + 2 * m;
|
||||
offsa = aoffset;
|
||||
offsb = stackptr;
|
||||
for (int i = 0; i < n; i++) {
|
||||
bx = curplan.precomputed[offsp + 0];
|
||||
by = curplan.precomputed[offsp + 1];
|
||||
x = (*a)[offsa + 0];
|
||||
y = (*a)[offsa + 1];
|
||||
curplan.stackbuf[offsb + 0] = x * bx - y * (-by);
|
||||
curplan.stackbuf[offsb + 1] = x * (-by) + y * bx;
|
||||
offsp = offsp + 2;
|
||||
offsa = offsa + 2;
|
||||
offsb = offsb + 2;
|
||||
}
|
||||
ftbaseexecuteplanrec(&curplan.stackbuf, stackptr, plan, curplan.plan[entryoffset + 5], stackptr + 2 * 2 * m);
|
||||
offsb = stackptr;
|
||||
offsp = offs;
|
||||
for (int i = 0; i <= m - 1; i++) {
|
||||
x = curplan.stackbuf[offsb + 0];
|
||||
y = curplan.stackbuf[offsb + 1];
|
||||
bx = curplan.precomputed[offsp + 0];
|
||||
by = curplan.precomputed[offsp + 1];
|
||||
curplan.stackbuf[offsb + 0] = x * bx - y * by;
|
||||
curplan.stackbuf[offsb + 1] = -(x * by + y * bx);
|
||||
offsb = offsb + 2;
|
||||
offsp = offsp + 2;
|
||||
}
|
||||
ftbaseexecuteplanrec(&curplan.stackbuf, stackptr, plan, curplan.plan[entryoffset + 5], stackptr + 2 * 2 * m);
|
||||
offsb = stackptr;
|
||||
offsp = offs + 2 * m;
|
||||
offsa = aoffset;
|
||||
for (int i = 0; i < n; i++) {
|
||||
x = curplan.stackbuf[offsb + 0] / m;
|
||||
y = -curplan.stackbuf[offsb + 1] / m;
|
||||
bx = curplan.precomputed[offsp + 0];
|
||||
by = curplan.precomputed[offsp + 1];
|
||||
(*a)[offsa + 0] = x * bx - y * (-by);
|
||||
(*a)[offsa + 1] = x * (-by) + y * bx;
|
||||
offsp = offsp + 2;
|
||||
offsa = offsa + 2;
|
||||
offsb = offsb + 2;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
Twiddle factors calculation
|
||||
|
||||
-- ALGLIB --
|
||||
Copyright 01.05.2009 by Bochkanov Sergey
|
||||
*************************************************************************/
|
||||
void PIFFT::ftbase_ffttwcalc(PIVector<double> * a, int aoffset, int n1, int n2) {
|
||||
int n, idx, offs;
|
||||
double x, y, twxm1, twy, twbasexm1, twbasey, twrowxm1, twrowy, tmpx, tmpy, v;
|
||||
int ftbase_ftbaseupdatetw = 4;
|
||||
n = n1 * n2;
|
||||
v = -2 * M_PI / n;
|
||||
twbasexm1 = -2 * sqr(sin(0.5 * v));
|
||||
twbasey = sin(v);
|
||||
twrowxm1 = 0;
|
||||
twrowy = 0;
|
||||
for (int i = 0, j = 0; i <= n2 - 1; i++) {
|
||||
twxm1 = 0;
|
||||
twy = 0;
|
||||
for (j = 0; j <= n1 - 1; j++) {
|
||||
idx = i * n1 + j;
|
||||
offs = aoffset + 2 * idx;
|
||||
x = (*a)[offs + 0];
|
||||
y = (*a)[offs + 1];
|
||||
tmpx = x * twxm1 - y * twy;
|
||||
tmpy = x * twy + y * twxm1;
|
||||
(*a)[offs + 0] = x + tmpx;
|
||||
(*a)[offs + 1] = y + tmpy;
|
||||
if (j < n1 - 1) {
|
||||
if (j % ftbase_ftbaseupdatetw == 0) {
|
||||
v = -2 * M_PI * i * (j + 1) / n;
|
||||
twxm1 = -2 * sqr(sin(0.5 * v));
|
||||
twy = sin(v);
|
||||
} else {
|
||||
tmpx = twrowxm1 + twxm1 * twrowxm1 - twy * twrowy;
|
||||
tmpy = twrowy + twxm1 * twrowy + twy * twrowxm1;
|
||||
twxm1 = twxm1 + tmpx;
|
||||
twy = twy + tmpy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i < n2 - 1) {
|
||||
if (j % ftbase_ftbaseupdatetw == 0) {
|
||||
v = -2 * M_PI * (i + 1) / n;
|
||||
twrowxm1 = -2 * sqr(sin(0.5 * v));
|
||||
twrowy = sin(v);
|
||||
} else {
|
||||
tmpx = twbasexm1 + twrowxm1 * twbasexm1 - twrowy * twbasey;
|
||||
tmpy = twbasey + twrowxm1 * twbasey + twrowy * twbasexm1;
|
||||
twrowxm1 = twrowxm1 + tmpx;
|
||||
twrowy = twrowy + tmpy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
77
src/math/pifft.h
Normal file
77
src/math/pifft.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/*! \file pifft.h
|
||||
* \brief Class for FFT, IFFT and Hilbert transformations
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Class for FFT, IFFT and Hilbert transformations
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com, Andrey Bychkov work.a.b@yandex.ru
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIFFT_H
|
||||
#define PIFFT_H
|
||||
|
||||
#include "pimathbase.h"
|
||||
|
||||
class PIP_EXPORT PIFFT
|
||||
{
|
||||
public:
|
||||
PIFFT();
|
||||
|
||||
PIVector<complexd> * calcFFT(const PIVector<complexd> &val);
|
||||
PIVector<complexd> * calcFFT(const PIVector<double> &val);
|
||||
PIVector<complexd> * calcFFTinverse(const PIVector<complexd> &val);
|
||||
PIVector<complexd> * calcHilbert(const PIVector<double> &val);
|
||||
PIVector<double> getAmplitude();
|
||||
|
||||
private:
|
||||
PIVector<complexd> result;
|
||||
bool prepared;
|
||||
typedef ptrdiff_t ae_int_t;
|
||||
void calc_coefs(uint cnt2);
|
||||
void calc_indexes(uint cnt2, uint deep2);
|
||||
complexd coef(uint n, uint k);
|
||||
|
||||
struct ftplan {
|
||||
PIVector<int> plan;
|
||||
PIVector<double> precomputed;
|
||||
PIVector<double> tmpbuf;
|
||||
PIVector<double> stackbuf;
|
||||
};
|
||||
|
||||
ftplan curplan;
|
||||
|
||||
void fftc1d(const PIVector<complexd> &a, uint n);
|
||||
void fftc1r(const PIVector<double> &a, uint n);
|
||||
void fftc1dinv(const PIVector<complexd> &a, uint n);
|
||||
|
||||
void createPlan(uint n);
|
||||
void ftbasegeneratecomplexfftplan(uint n, ftplan *plan);
|
||||
void ftbase_ftbasegenerateplanrec(int n, int tasktype, ftplan *plan, int *plansize, int *precomputedsize, int *planarraysize, int *tmpmemsize, int *stackmemsize, ae_int_t stackptr, int debugi=0);
|
||||
void ftbase_ftbaseprecomputeplanrec(ftplan *plan, int entryoffset, ae_int_t stackptr);
|
||||
void ftbasefactorize(int n, int *n1, int *n2);
|
||||
void ftbase_ftbasefindsmoothrec(int n, int seed, int leastfactor, int *best);
|
||||
int ftbasefindsmooth(int n);
|
||||
void ftbaseexecuteplan(PIVector<double> *a, int aoffset, int n, ftplan *plan);
|
||||
void ftbaseexecuteplanrec(PIVector<double> *a, int aoffset, ftplan *plan, int entryoffset, ae_int_t stackptr);
|
||||
void ftbase_internalcomplexlintranspose(PIVector<double> *a, int m, int n, int astart, PIVector<double> *buf);
|
||||
void ftbase_ffticltrec(PIVector<double> *a, int astart, int astride, PIVector<double> *b, int bstart, int bstride, int m, int n);
|
||||
void ftbase_internalreallintranspose(PIVector<double> *a, int m, int n, int astart, PIVector<double> *buf);
|
||||
void ftbase_fftirltrec(PIVector<double> *a, int astart, int astride, PIVector<double> *b, int bstart, int bstride, int m, int n);
|
||||
void ftbase_ffttwcalc(PIVector<double> *a, int aoffset, int n1, int n2);
|
||||
|
||||
};
|
||||
|
||||
#endif // PIFFT_H
|
||||
30
src/math/pimath.h
Normal file
30
src/math/pimath.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*! \file pimath.h
|
||||
* \brief Many mathematical functions and classes
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Many mathematical functions and classes
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIMATH_H
|
||||
#define PIMATH_H
|
||||
|
||||
#include "pimathsolver.h"
|
||||
#include "pistatistic.h"
|
||||
#include "pifft.h"
|
||||
|
||||
#endif // PIMATH_H
|
||||
468
src/math/pimathbase.cpp
Normal file
468
src/math/pimathbase.cpp
Normal file
@@ -0,0 +1,468 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Basic mathematical functions and defines
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pimathbase.h"
|
||||
|
||||
|
||||
double piJ0(const double & v) {
|
||||
#ifndef PIP_MATH_J0
|
||||
double x = v;
|
||||
double xsq;
|
||||
double nn;
|
||||
double pzero;
|
||||
double qzero;
|
||||
double p1;
|
||||
double q1;
|
||||
double result;
|
||||
if (x < 0) x = -x;
|
||||
if (x > 8.) {
|
||||
double xsq_;
|
||||
double p2;
|
||||
double q2;
|
||||
double p3;
|
||||
double q3;
|
||||
xsq_ = 64. / (x * x);
|
||||
p2 = 0.0;
|
||||
p2 = 2485.271928957404011288128951 + xsq_ * p2;
|
||||
p2 = 153982.6532623911470917825993 + xsq_ * p2;
|
||||
p2 = 2016135.283049983642487182349 + xsq_ * p2;
|
||||
p2 = 8413041.456550439208464315611 + xsq_ * p2;
|
||||
p2 = 12332384.76817638145232406055 + xsq_ * p2;
|
||||
p2 = 5393485.083869438325262122897 + xsq_ * p2;
|
||||
q2 = 1.0;
|
||||
q2 = 2615.700736920839685159081813 + xsq_ * q2;
|
||||
q2 = 156001.7276940030940592769933 + xsq_ * q2;
|
||||
q2 = 2025066.801570134013891035236 + xsq_ * q2;
|
||||
q2 = 8426449.050629797331554404810 + xsq_ * q2;
|
||||
q2 = 12338310.22786324960844856182 + xsq_ * q2;
|
||||
q2 = 5393485.083869438325560444960 + xsq_ * q2;
|
||||
p3 = -0.0;
|
||||
p3 = -4.887199395841261531199129300 +xsq_ * p3;
|
||||
p3 = -226.2630641933704113967255053 +xsq_ * p3;
|
||||
p3 = -2365.956170779108192723612816 +xsq_ * p3;
|
||||
p3 = -8239.066313485606568803548860 +xsq_ * p3;
|
||||
p3 = -10381.41698748464093880530341 +xsq_ * p3;
|
||||
p3 = -3984.617357595222463506790588 +xsq_ * p3;
|
||||
q3 = 1.0;
|
||||
q3 = 408.7714673983499223402830260 + xsq_ * q3;
|
||||
q3 = 15704.89191515395519392882766 + xsq_ * q3;
|
||||
q3 = 156021.3206679291652539287109 + xsq_ * q3;
|
||||
q3 = 533291.3634216897168722255057 + xsq_ * q3;
|
||||
q3 = 666745.4239319826986004038103 + xsq_ * q3;
|
||||
q3 = 255015.5108860942382983170882 + xsq_ * q3;
|
||||
pzero = p2 / q2;
|
||||
qzero = 8. * p3 / q3 / x;
|
||||
nn = x- M_PI / 4.;
|
||||
result = sqrt(2. / M_PI / x) * (pzero * cos(nn) - qzero * sin(nn));
|
||||
return result;
|
||||
}
|
||||
xsq = x * x;
|
||||
p1 = 26857.86856980014981415848441;
|
||||
p1 = -40504123.71833132706360663322 + xsq * p1;
|
||||
p1 = 25071582855.36881945555156435 + xsq * p1;
|
||||
p1 = -8085222034853.793871199468171 + xsq * p1;
|
||||
p1 = 1434354939140344.111664316553 + xsq * p1;
|
||||
p1 = -136762035308817138.6865416609 + xsq * p1;
|
||||
p1 = 6382059341072356562.289432465 + xsq * p1;
|
||||
p1 = -117915762910761053603.8440800 + xsq * p1;
|
||||
p1 = 493378725179413356181.6813446 + xsq * p1;
|
||||
q1 = 1.;
|
||||
q1 = 1363.063652328970604442810507 + xsq * q1;
|
||||
q1 = 1114636.098462985378182402543 + xsq * q1;
|
||||
q1 = 669998767.2982239671814028660 + xsq * q1;
|
||||
q1 = 312304311494.1213172572469442 + xsq * q1;
|
||||
q1 = 112775673967979.8507056031594 + xsq * q1;
|
||||
q1 = 30246356167094626.98627330784 + xsq * q1;
|
||||
q1 = 5428918384092285160.200195092 + xsq * q1;
|
||||
q1 = 493378725179413356211.3278438 + xsq * q1;
|
||||
return p1 / q1;
|
||||
#else
|
||||
return j0(v);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
double piJ1(const double & v) {
|
||||
#ifndef PIP_MATH_J1
|
||||
double x = v;
|
||||
double s;
|
||||
double xsq;
|
||||
double nn;
|
||||
double pzero;
|
||||
double qzero;
|
||||
double p1;
|
||||
double q1;
|
||||
double result;
|
||||
s = sign(x);
|
||||
if (x < 0)
|
||||
x = -x;
|
||||
if (x > 8.) {
|
||||
double xsq_;
|
||||
double p2;
|
||||
double q2;
|
||||
double p3;
|
||||
double q3;
|
||||
xsq_ = 64.0 / (x * x);
|
||||
p2 = -1611.616644324610116477412898;
|
||||
p2 = -109824.0554345934672737413139 + xsq_ * p2;
|
||||
p2 = -1523529.351181137383255105722 + xsq_ * p2;
|
||||
p2 = -6603373.248364939109255245434 + xsq_ * p2;
|
||||
p2 = -9942246.505077641195658377899 + xsq_ * p2;
|
||||
p2 = -4435757.816794127857114720794 + xsq_ * p2;
|
||||
q2 = 1.0;
|
||||
q2 = -1455.009440190496182453565068 + xsq_ * q2;
|
||||
q2 = -107263.8599110382011903063867 + xsq_ * q2;
|
||||
q2 = -1511809.506634160881644546358 + xsq_ * q2;
|
||||
q2 = -6585339.479723087072826915069 + xsq_ * q2;
|
||||
q2 = -9934124.389934585658967556309 + xsq_ * q2;
|
||||
q2 = -4435757.816794127856828016962 + xsq_ * q2;
|
||||
p3 = 35.26513384663603218592175580;
|
||||
p3 = 1706.375429020768002061283546 + xsq_ * p3;
|
||||
p3 = 18494.26287322386679652009819 + xsq_ * p3;
|
||||
p3 = 66178.83658127083517939992166 + xsq_ * p3;
|
||||
p3 = 85145.16067533570196555001171 + xsq_ * p3;
|
||||
p3 = 33220.91340985722351859704442 + xsq_ * p3;
|
||||
q3 = 1.0;
|
||||
q3 = 863.8367769604990967475517183 + xsq_ * q3;
|
||||
q3 = 37890.22974577220264142952256 + xsq_ * q3;
|
||||
q3 = 400294.4358226697511708610813 + xsq_ * q3;
|
||||
q3 = 1419460.669603720892855755253 + xsq_ * q3;
|
||||
q3 = 1819458.042243997298924553839 + xsq_ * q3;
|
||||
q3 = 708712.8194102874357377502472 + xsq_ * q3;
|
||||
pzero = p2 / q2;
|
||||
qzero = 8 * p3 / q3 / x;
|
||||
nn = x - 3 * M_PI / 4;
|
||||
result = sqrt(2 / M_PI / x) * (pzero * cos(nn) - qzero * sin(nn));
|
||||
if (s < 0)
|
||||
result = -result;
|
||||
return result;
|
||||
}
|
||||
xsq = sqr(x);
|
||||
p1 = 2701.122710892323414856790990;
|
||||
p1 = -4695753.530642995859767162166 + xsq * p1;
|
||||
p1 = 3413234182.301700539091292655 + xsq * p1;
|
||||
p1 = -1322983480332.126453125473247 + xsq * p1;
|
||||
p1 = 290879526383477.5409737601689 + xsq * p1;
|
||||
p1 = -35888175699101060.50743641413 + xsq * p1;
|
||||
p1 = 2316433580634002297.931815435 + xsq * p1;
|
||||
p1 = -66721065689249162980.20941484 + xsq * p1;
|
||||
p1 = 581199354001606143928.050809 + xsq * p1;
|
||||
q1 = 1.0;
|
||||
q1 = 1606.931573481487801970916749 + xsq * q1;
|
||||
q1 = 1501793.594998585505921097578 + xsq * q1;
|
||||
q1 = 1013863514.358673989967045588 + xsq * q1;
|
||||
q1 = 524371026216.7649715406728642 + xsq * q1;
|
||||
q1 = 208166122130760.7351240184229 + xsq * q1;
|
||||
q1 = 60920613989175217.46105196863 + xsq * q1;
|
||||
q1 = 11857707121903209998.37113348 + xsq * q1;
|
||||
q1 = 1162398708003212287858.529400 + xsq * q1;
|
||||
result = s * x * p1 / q1;
|
||||
return result;
|
||||
#else
|
||||
return j1(v);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
double piJn(int n, const double & v) {
|
||||
#ifndef PIP_MATH_JN
|
||||
double x = v;
|
||||
double pkm2;
|
||||
double pkm1;
|
||||
double pk;
|
||||
double xk;
|
||||
double r;
|
||||
double ans;
|
||||
int k;
|
||||
int sg;
|
||||
double result;
|
||||
if (n < 0) {
|
||||
n = -n;
|
||||
if (n % 2 == 0)
|
||||
sg = 1;
|
||||
else
|
||||
sg = -1;
|
||||
} else
|
||||
sg = 1;
|
||||
if (x < 0) {
|
||||
if (n % 2 != 0)
|
||||
sg = -sg;
|
||||
x = -x;
|
||||
}
|
||||
if (n == 0) {
|
||||
result = sg * piJ0(x);
|
||||
return result;
|
||||
}
|
||||
if (n == 1) {
|
||||
result = sg * piJ1(x);
|
||||
return result;
|
||||
}
|
||||
if (n == 2) {
|
||||
if (x == 0)
|
||||
result = 0;
|
||||
else
|
||||
result = sg * (2.0 * piJ1(x) / x - piJ0(x));
|
||||
return result;
|
||||
}
|
||||
if (x < 1E-16) {
|
||||
result = 0;
|
||||
return result;
|
||||
}
|
||||
k = 53;
|
||||
pk = 2 * (n + k);
|
||||
ans = pk;
|
||||
xk = x * x;
|
||||
do {
|
||||
pk = pk - 2.0;
|
||||
ans = pk - xk / ans;
|
||||
k = k - 1;
|
||||
} while (k != 0);
|
||||
ans = x / ans;
|
||||
pk = 1.0;
|
||||
pkm1 = 1.0 / ans;
|
||||
k = n - 1;
|
||||
r = 2 * k;
|
||||
do {
|
||||
pkm2 = (pkm1 * r - pk * x) / x;
|
||||
pk = pkm1;
|
||||
pkm1 = pkm2;
|
||||
r = r - 2.0;
|
||||
k = k - 1;
|
||||
} while (k != 0);
|
||||
if (fabs(pk) > fabs(pkm1))
|
||||
ans = piJ1(x) / pk;
|
||||
else
|
||||
ans = piJ0(x) / pkm1;
|
||||
result = sg * ans;
|
||||
return result;
|
||||
#else
|
||||
return jn(n, v);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
double piY0(const double & v) {
|
||||
#ifndef PIP_MATH_Y0
|
||||
double x = v;
|
||||
double nn;
|
||||
double xsq;
|
||||
double pzero;
|
||||
double qzero;
|
||||
double p4;
|
||||
double q4;
|
||||
double result;
|
||||
if (x > 8.) {
|
||||
double xsq_;
|
||||
double p2;
|
||||
double q2;
|
||||
double p3;
|
||||
double q3;
|
||||
xsq_ = 64.0 / (x * x);
|
||||
p2 = 0.0;
|
||||
p2 = 2485.271928957404011288128951 + xsq_ * p2;
|
||||
p2 = 153982.6532623911470917825993 + xsq_ * p2;
|
||||
p2 = 2016135.283049983642487182349 + xsq_ * p2;
|
||||
p2 = 8413041.456550439208464315611 + xsq_ * p2;
|
||||
p2 = 12332384.76817638145232406055 + xsq_ * p2;
|
||||
p2 = 5393485.083869438325262122897 + xsq_ * p2;
|
||||
q2 = 1.0;
|
||||
q2 = 2615.700736920839685159081813 + xsq_ * q2;
|
||||
q2 = 156001.7276940030940592769933 + xsq_ * q2;
|
||||
q2 = 2025066.801570134013891035236 + xsq_ * q2;
|
||||
q2 = 8426449.050629797331554404810 + xsq_ * q2;
|
||||
q2 = 12338310.22786324960844856182 + xsq_ * q2;
|
||||
q2 = 5393485.083869438325560444960 + xsq_ * q2;
|
||||
p3 = -0.0;
|
||||
p3 = -4.887199395841261531199129300 + xsq_ * p3;
|
||||
p3 = -226.2630641933704113967255053 + xsq_ * p3;
|
||||
p3 = -2365.956170779108192723612816 + xsq_ * p3;
|
||||
p3 = -8239.066313485606568803548860 + xsq_ * p3;
|
||||
p3 = -10381.41698748464093880530341 + xsq_ * p3;
|
||||
p3 = -3984.617357595222463506790588 + xsq_ * p3;
|
||||
q3 = 1.0;
|
||||
q3 = 408.7714673983499223402830260 + xsq_ * q3;
|
||||
q3 = 15704.89191515395519392882766 + xsq_ * q3;
|
||||
q3 = 156021.3206679291652539287109 + xsq_ * q3;
|
||||
q3 = 533291.3634216897168722255057 + xsq_ * q3;
|
||||
q3 = 666745.4239319826986004038103 + xsq_ * q3;
|
||||
q3 = 255015.5108860942382983170882 + xsq_ * q3;
|
||||
pzero = p2 / q2;
|
||||
qzero = 8 * p3 / q3 / x;
|
||||
nn = x - M_PI / 4;
|
||||
result = sqrt(2 / M_PI / x) * (pzero * sin(nn) + qzero * cos(nn));
|
||||
return result;
|
||||
}
|
||||
xsq = sqr(x);
|
||||
p4 = -41370.35497933148554125235152;
|
||||
p4 = 59152134.65686889654273830069 + xsq * p4;
|
||||
p4 = -34363712229.79040378171030138 + xsq * p4;
|
||||
p4 = 10255208596863.94284509167421 + xsq * p4;
|
||||
p4 = -1648605817185729.473122082537 + xsq * p4;
|
||||
p4 = 137562431639934407.8571335453 + xsq * p4;
|
||||
p4 = -5247065581112764941.297350814 + xsq * p4;
|
||||
p4 = 65874732757195549259.99402049 + xsq * p4;
|
||||
p4 = -27502866786291095837.01933175 + xsq * p4;
|
||||
q4 = 1.0;
|
||||
q4 = 1282.452772478993804176329391 + xsq * q4;
|
||||
q4 = 1001702.641288906265666651753 + xsq * q4;
|
||||
q4 = 579512264.0700729537480087915 + xsq * q4;
|
||||
q4 = 261306575504.1081249568482092 + xsq * q4;
|
||||
q4 = 91620380340751.85262489147968 + xsq * q4;
|
||||
q4 = 23928830434997818.57439356652 + xsq * q4;
|
||||
q4 = 4192417043410839973.904769661 + xsq * q4;
|
||||
q4 = 372645883898616588198.9980 + xsq * q4;
|
||||
result = p4 / q4 + 2 / M_PI * piJ0(x) * log(x);
|
||||
return result;
|
||||
#else
|
||||
return y0(v);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
double piY1(const double & v) {
|
||||
#ifndef PIP_MATH_Y1
|
||||
double x = v;
|
||||
double nn;
|
||||
double xsq;
|
||||
double pzero;
|
||||
double qzero;
|
||||
double p4;
|
||||
double q4;
|
||||
double result;
|
||||
if (x > 8.) {
|
||||
double xsq_;
|
||||
double p2;
|
||||
double q2;
|
||||
double p3;
|
||||
double q3;
|
||||
xsq_ = 64.0 / (x * x);
|
||||
p2 = -1611.616644324610116477412898;
|
||||
p2 = -109824.0554345934672737413139 + xsq_ * p2;
|
||||
p2 = -1523529.351181137383255105722 + xsq_ * p2;
|
||||
p2 = -6603373.248364939109255245434 + xsq_ * p2;
|
||||
p2 = -9942246.505077641195658377899 + xsq_ * p2;
|
||||
p2 = -4435757.816794127857114720794 + xsq_ * p2;
|
||||
q2 = 1.0;
|
||||
q2 = -1455.009440190496182453565068 + xsq_ * q2;
|
||||
q2 = -107263.8599110382011903063867 + xsq_ * q2;
|
||||
q2 = -1511809.506634160881644546358 + xsq_ * q2;
|
||||
q2 = -6585339.479723087072826915069 + xsq_ * q2;
|
||||
q2 = -9934124.389934585658967556309 + xsq_ * q2;
|
||||
q2 = -4435757.816794127856828016962 + xsq_ * q2;
|
||||
p3 = 35.26513384663603218592175580;
|
||||
p3 = 1706.375429020768002061283546 + xsq_ * p3;
|
||||
p3 = 18494.26287322386679652009819 + xsq_ * p3;
|
||||
p3 = 66178.83658127083517939992166 + xsq_ * p3;
|
||||
p3 = 85145.16067533570196555001171 + xsq_ * p3;
|
||||
p3 = 33220.91340985722351859704442 + xsq_ * p3;
|
||||
q3 = 1.0;
|
||||
q3 = 863.8367769604990967475517183 + xsq_ * q3;
|
||||
q3 = 37890.22974577220264142952256 + xsq_ * q3;
|
||||
q3 = 400294.4358226697511708610813 + xsq_ * q3;
|
||||
q3 = 1419460.669603720892855755253 + xsq_ * q3;
|
||||
q3 = 1819458.042243997298924553839 + xsq_ * q3;
|
||||
q3 = 708712.8194102874357377502472 + xsq_ * q3;
|
||||
pzero = p2 / q2;
|
||||
qzero = 8 * p3 / q3 / x;
|
||||
nn = x - 3 * M_PI / 4;
|
||||
result = sqrt(2 / M_PI / x) * (pzero * sin(nn) + qzero * cos(nn));
|
||||
return result;
|
||||
}
|
||||
xsq = sqr(x);
|
||||
p4 = -2108847.540133123652824139923;
|
||||
p4 = 3639488548.124002058278999428 + xsq * p4;
|
||||
p4 = -2580681702194.450950541426399 + xsq * p4;
|
||||
p4 = 956993023992168.3481121552788 + xsq * p4;
|
||||
p4 = -196588746272214065.8820322248 + xsq * p4;
|
||||
p4 = 21931073399177975921.11427556 + xsq * p4;
|
||||
p4 = -1212297555414509577913.561535 + xsq * p4;
|
||||
p4 = 26554738314348543268942.48968 + xsq * p4;
|
||||
p4 = -99637534243069222259967.44354 + xsq * p4;
|
||||
q4 = 1.0;
|
||||
q4 = 1612.361029677000859332072312 + xsq * q4;
|
||||
q4 = 1563282.754899580604737366452 + xsq * q4;
|
||||
q4 = 1128686837.169442121732366891 + xsq * q4;
|
||||
q4 = 646534088126.5275571961681500 + xsq * q4;
|
||||
q4 = 297663212564727.6729292742282 + xsq * q4;
|
||||
q4 = 108225825940881955.2553850180 + xsq * q4;
|
||||
q4 = 29549879358971486742.90758119 + xsq * q4;
|
||||
q4 = 5435310377188854170800.653097 + xsq * q4;
|
||||
q4 = 508206736694124324531442.4152 + xsq * q4;
|
||||
result = x * p4 / q4 + 2 / M_PI * (piJ1(x) * log(x) - 1 / x);
|
||||
return result;
|
||||
#else
|
||||
return y1(v);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
double piYn(int n, const double & v) {
|
||||
#ifndef PIP_MATH_YN
|
||||
int i;
|
||||
double x = v;
|
||||
double a;
|
||||
double b;
|
||||
double tmp;
|
||||
double s;
|
||||
double result;
|
||||
s = 1;
|
||||
if (n < 0) {
|
||||
n = -n;
|
||||
if (n % 2 != 0)
|
||||
s = -1;
|
||||
}
|
||||
if (n == 0) {
|
||||
result = piY0(x);
|
||||
return result;
|
||||
}
|
||||
if (n == 1) {
|
||||
result = s * piY1(x);
|
||||
return result;
|
||||
}
|
||||
a = piY0(x);
|
||||
b = piY1(x);
|
||||
for (i = 1; i <= n - 1; i++) {
|
||||
tmp = b;
|
||||
b = 2 * i / x * b - a;
|
||||
a = tmp;
|
||||
}
|
||||
result = s * b;
|
||||
return result;
|
||||
#else
|
||||
return yn(n, v);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
double randomn(double dv, double sv) {
|
||||
static bool agen = false;
|
||||
double s = 2., v0 = 0., v1 = 0.;
|
||||
if (agen) {
|
||||
agen = false;
|
||||
v1 = v1 * sqrt(-2 * log(s) / s);
|
||||
return v1 * sv + dv;
|
||||
}
|
||||
while (s > 1. || s == 0.) {
|
||||
v0 = randomd();
|
||||
v1 = randomd();
|
||||
s = v0*v0 + v1*v1;
|
||||
}
|
||||
v0 = v0 * sqrt(-2 * log(s) / s);
|
||||
return v0 * sv + dv;
|
||||
}
|
||||
189
src/math/pimathbase.h
Normal file
189
src/math/pimathbase.h
Normal file
@@ -0,0 +1,189 @@
|
||||
/*! \file pimathbase.h
|
||||
* \brief Basic mathematical functions and defines
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Basic mathematical functions and defines
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIMATHBASE_H
|
||||
#define PIMATHBASE_H
|
||||
|
||||
#include "piinit.h"
|
||||
#include "pibytearray.h"
|
||||
#ifdef QNX
|
||||
# undef PIP_MATH_J0
|
||||
# undef PIP_MATH_J1
|
||||
# undef PIP_MATH_JN
|
||||
# undef PIP_MATH_Y0
|
||||
# undef PIP_MATH_Y1
|
||||
# undef PIP_MATH_YN
|
||||
#endif
|
||||
|
||||
#ifndef M_LN2
|
||||
# define M_LN2 0.69314718055994530942
|
||||
#endif
|
||||
#ifndef M_LN10
|
||||
# define M_LN10 2.30258509299404568402
|
||||
#endif
|
||||
#ifndef M_SQRT2
|
||||
# define M_SQRT2 1.41421356237309514547
|
||||
#endif
|
||||
#ifndef M_SQRT3
|
||||
# define M_SQRT3 1.73205080756887719318
|
||||
#endif
|
||||
#ifndef M_1_SQRT2
|
||||
# define M_1_SQRT2 0.70710678118654746172
|
||||
#endif
|
||||
#ifndef M_1_SQRT3
|
||||
# define M_1_SQRT3 0.57735026918962584208
|
||||
#endif
|
||||
#ifndef M_PI
|
||||
# define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
#ifndef M_2PI
|
||||
# define M_2PI 6.28318530717958647692
|
||||
#endif
|
||||
#ifndef M_PI_3
|
||||
# define M_PI_3 1.04719755119659774615
|
||||
#endif
|
||||
#ifndef M_2PI_3
|
||||
# define M_2PI_3 2.0943951023931954923
|
||||
#endif
|
||||
#ifndef M_180_PI
|
||||
# define M_180_PI 57.2957795130823208768
|
||||
#endif
|
||||
#ifndef M_PI_180
|
||||
# define M_PI_180 1.74532925199432957692e-2
|
||||
#endif
|
||||
#ifndef M_E
|
||||
# define M_E 2.7182818284590452353602874713527
|
||||
#endif
|
||||
#ifndef M_LIGHT_SPEED
|
||||
# define M_LIGHT_SPEED 2.99792458e+8
|
||||
#endif
|
||||
|
||||
const double deg2rad = M_PI_180;
|
||||
const double rad2deg = M_180_PI;
|
||||
|
||||
inline int sign(const float & x) {return (x < 0.) ? -1 : (x > 0. ? 1 : 0);}
|
||||
inline int sign(const double & x) {return (x < 0.) ? -1 : (x > 0. ? 1 : 0);}
|
||||
inline complexd sign(const complexd & x) {return complexd(sign(x.real()), sign(x.imag()));}
|
||||
inline int pow2(const int p) {return 1 << p;}
|
||||
inline double sqr(const int v) {return v * v;}
|
||||
inline double sqr(const float & v) {return v * v;}
|
||||
inline double sqr(const double & v) {return v * v;}
|
||||
inline double sinc(const double & v) {if (v == 0.) return 1.; double t = M_PI * v; return sin(t) / t;}
|
||||
inline complexd round(const complexd & c) {return complexd(piRound<double>(c.real()), piRound<double>(c.imag()));}
|
||||
inline complexd floor(const complexd & c) {return complexd(floor(c.real()), floor(c.imag()));}
|
||||
inline complexd ceil(const complexd & c) {return complexd(ceil(c.real()), ceil(c.imag()));}
|
||||
inline complexd atanc(const complexd & c) {return -complexd(-0.5, 1.) * log((complexd_1 + complexd_i * c) / (complexd_1 - complexd_i * c));}
|
||||
inline complexd asinc(const complexd & c) {return -complexd_i * log(complexd_i * c + sqrt(complexd_1 - c * c));}
|
||||
inline complexd acosc(const complexd & c) {return -complexd_i * log(c + complexd_i * sqrt(complexd_1 - c * c));}
|
||||
#ifdef CC_GCC
|
||||
# if CC_GCC_VERSION <= 0x025F
|
||||
inline complexd tan(const complexd & c) {return sin(c) / cos(c);}
|
||||
inline complexd tanh(const complexd & c) {return sinh(c) / cosh(c);}
|
||||
inline complexd log2(const complexd & c) {return log(c) / M_LN2;}
|
||||
inline complexd log10(const complexd & c) {return log(c) / M_LN10;}
|
||||
# endif
|
||||
#endif
|
||||
double piJ0(const double & v);
|
||||
double piJ1(const double & v);
|
||||
double piJn(int n, const double & v);
|
||||
double piY0(const double & v);
|
||||
double piY1(const double & v);
|
||||
double piYn(int n, const double & v);
|
||||
inline double toDb(double val) {return 10. * log10(val);}
|
||||
inline double fromDb(double val) {return pow(10., val / 10.);}
|
||||
inline double toRad(double deg) {return deg * M_PI_180;}
|
||||
inline double toDeg(double rad) {return rad * M_180_PI;}
|
||||
|
||||
template<typename T>
|
||||
inline PICout operator <<(PICout s, const complex<T> & v) {s.space(); s.setControl(0, true); s << "(" << v.real() << "; " << v.imag() << ")"; s.restoreControl(); return s;}
|
||||
|
||||
// [-1 ; 1]
|
||||
inline double randomd() {return (double)random() / RAND_MAX * 2. - 1.;}
|
||||
// [-1 ; 1] normal
|
||||
double randomn(double dv = 0., double sv = 1.);
|
||||
|
||||
inline PIVector<double> abs(const PIVector<complexd> & v) {
|
||||
PIVector<double> result;
|
||||
result.resize(v.size());
|
||||
for (uint i = 0; i < v.size(); i++)
|
||||
result[i] = abs(v[i]);
|
||||
return result;
|
||||
}
|
||||
inline PIVector<double> abs(const PIVector<double> & v) {
|
||||
PIVector<double> result;
|
||||
result.resize(v.size());
|
||||
for (uint i = 0; i < v.size(); i++)
|
||||
result[i] = abs(v[i]);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
bool OLS_Linear(const PIVector<PIPair<T, T> > & input, T * out_a, T * out_b) {
|
||||
if (input.size_s() < 2)
|
||||
return false;
|
||||
int n = input.size_s();
|
||||
T a_t0 = T(), a_t1 = T(), a_t2 = T(), a_t3 = T(), a_t4 = T(), a = T(), b = T();
|
||||
for (int i = 0; i < n; ++i) {
|
||||
const PIPair<T, T> & cv(input[i]);
|
||||
a_t0 += cv.first * cv.second;
|
||||
a_t1 += cv.first;
|
||||
a_t2 += cv.second;
|
||||
a_t3 += cv.first * cv.first;
|
||||
}
|
||||
a_t4 = n * a_t3 - a_t1 * a_t1;
|
||||
if (a_t4 != T())
|
||||
a = (n * a_t0 - a_t1 * a_t2) / a_t4;
|
||||
b = (a_t2 - a * a_t1) / n;
|
||||
if (out_a != 0) *out_a = a;
|
||||
if (out_b != 0) *out_b = b;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
bool WLS_Linear(const PIVector<PIPair<T, T> > & input, const PIVector<T> & weights, T * out_a, T * out_b) {
|
||||
if (input.size_s() < 2)
|
||||
return false;
|
||||
if (input.size_s() != weights.size_s())
|
||||
return false;
|
||||
int n = input.size_s();
|
||||
T a_t0 = T(), a_t1 = T(), a_t2 = T(), a_t3 = T(), a_t4 = T(), a_n = T(), a = T(), b = T();
|
||||
for (int i = 0; i < n; ++i) {
|
||||
T cp = weights[i];
|
||||
const PIPair<T, T> & cv(input[i]);
|
||||
a_t0 += cv.first * cv.second * cp;
|
||||
a_t1 += cv.first * cp;
|
||||
a_t2 += cv.second * cp;
|
||||
a_t3 += cv.first * cv.first * cp;
|
||||
a_n += cp;
|
||||
}
|
||||
a_t4 = a_n * a_t3 - a_t1 * a_t1;
|
||||
if (a_t4 != T())
|
||||
a = (a_n * a_t0 - a_t1 * a_t2) / a_t4;
|
||||
b = (a_t2 - a * a_t1) / a_n;
|
||||
if (out_a != 0) *out_a = a;
|
||||
if (out_b != 0) *out_b = b;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // PIMATHBASE_H
|
||||
504
src/math/pimathmatrix.h
Normal file
504
src/math/pimathmatrix.h
Normal file
@@ -0,0 +1,504 @@
|
||||
/*! \file pimathmatrix.h
|
||||
* \brief PIMathMatrix
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
PIMathMatrix
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIMATHMATRIX_H
|
||||
#define PIMATHMATRIX_H
|
||||
|
||||
#include "pimathvector.h"
|
||||
|
||||
/// Matrix templated
|
||||
|
||||
#define PIMM_FOR(r, c) for (uint c = 0; c < Cols; ++c) { for (uint r = 0; r < Rows; ++r) {
|
||||
#define PIMM_FOR_WB(r, c) for (uint c = 0; c < Cols; ++c) for (uint r = 0; r < Rows; ++r) // without brakes
|
||||
#define PIMM_FOR_I(r, c) for (uint r = 0; r < Rows; ++r) { for (uint c = 0; c < Cols; ++c) {
|
||||
#define PIMM_FOR_I_WB(r, c) for (uint r = 0; r < Rows; ++r) for (uint c = 0; c < Cols; ++c) // without brakes
|
||||
#define PIMM_FOR_C(v) for (uint v = 0; v < Cols; ++v)
|
||||
#define PIMM_FOR_R(v) for (uint v = 0; v < Rows; ++v)
|
||||
|
||||
#pragma pack(push, 1)
|
||||
template<uint Rows, uint Cols = Rows, typename Type = double>
|
||||
class PIP_EXPORT PIMathMatrixT {
|
||||
typedef PIMathMatrixT<Rows, Cols, Type> _CMatrix;
|
||||
typedef PIMathMatrixT<Cols, Rows, Type> _CMatrixI;
|
||||
typedef PIMathVectorT<Rows, Type> _CMCol;
|
||||
typedef PIMathVectorT<Cols, Type> _CMRow;
|
||||
public:
|
||||
PIMathMatrixT() {resize(Rows, Cols);}
|
||||
PIMathMatrixT(Type fval, ...) {resize(Rows, Cols); va_list vl; va_start(vl, fval); PIMM_FOR_I_WB(r, c) m[r][c] = (r + c == 0 ? fval : va_arg(vl, Type)); va_end(vl);}
|
||||
PIMathMatrixT(const PIVector<Type> & val) {resize(Rows, Cols); int i = 0; PIMM_FOR_I_WB(r, c) m[r][c] = val[i++];}
|
||||
//PIMathMatrixT(const _CMatrix & o) {resize(Rows, Cols); int i = 0; PIMM_FOR_I_WB(r, c) m[r][c] = val[i++];}
|
||||
|
||||
static _CMatrix identity() {_CMatrix tm = _CMatrix(); PIMM_FOR_WB(r, c) tm.m[r][c] = (c == r ? Type(1) : Type(0)); return tm;}
|
||||
static _CMatrix rotation(double angle) {return _CMatrix();}
|
||||
static _CMatrix rotationX(double angle) {return _CMatrix();}
|
||||
static _CMatrix rotationY(double angle) {return _CMatrix();}
|
||||
static _CMatrix rotationZ(double angle) {return _CMatrix();}
|
||||
static _CMatrix scaleX(double factor) {return _CMatrix();}
|
||||
static _CMatrix scaleY(double factor) {return _CMatrix();}
|
||||
static _CMatrix scaleZ(double factor) {return _CMatrix();}
|
||||
|
||||
uint cols() const {return Cols;}
|
||||
uint rows() const {return Rows;}
|
||||
_CMCol col(uint index) {_CMCol tv; PIMM_FOR_R(i) tv[i] = m[i][index]; return tv;}
|
||||
_CMRow row(uint index) {_CMRow tv; PIMM_FOR_C(i) tv[i] = m[index][i]; return tv;}
|
||||
_CMatrix & setCol(uint index, const _CMCol & v) {PIMM_FOR_R(i) m[i][index] = v[i]; return *this;}
|
||||
_CMatrix & setRow(uint index, const _CMRow & v) {PIMM_FOR_C(i) m[index][i] = v[i]; return *this;}
|
||||
_CMatrix & swapRows(uint r0, uint r1) {Type t; PIMM_FOR_C(i) {t = m[r0][i]; m[r0][i] = m[r1][i]; m[r1][i] = t;} return *this;}
|
||||
_CMatrix & swapCols(uint c0, uint c1) {Type t; PIMM_FOR_R(i) {t = m[i][c0]; m[i][c0] = m[i][c1]; m[i][c1] = t;} return *this;}
|
||||
_CMatrix & fill(const Type & v) {PIMM_FOR_WB(r, c) m[r][c] = v; return *this;}
|
||||
//inline _CMatrix & set(Type fval, ...) {m[0] = fval; va_list vl; va_start(vl, fval); PIMV_FOR(i, 1) m[i] = va_arg(vl, Type); va_end(vl); return *this;}
|
||||
//inline void normalize() {Type tv = length(); if (tv == Type(1)) return; PIMV_FOR(i, 0) m[i] /= tv;}
|
||||
bool isSquare() const {return cols() == rows();}
|
||||
bool isIdentity() const {PIMM_FOR_WB(r, c) if ((c == r) ? m[r][c] != Type(1) : m[r][c] != Type(0)) return false; return true;}
|
||||
bool isNull() const {PIMM_FOR_WB(r, c) if (m[r][c] != Type(0)) return false; return true;}
|
||||
|
||||
Type & at(uint row, uint col) {return m[row][col];}
|
||||
Type at(uint row, uint col) const {return m[row][col];}
|
||||
Type * operator [](uint row) {return m[row];}
|
||||
const Type * operator [](uint row) const {return m[row];}
|
||||
void operator =(const _CMatrix & sm) {memcpy(m, sm.m, sizeof(Type) * Cols * Rows);}
|
||||
bool operator ==(const _CMatrix & sm) const {PIMM_FOR_WB(r, c) if (m[r][c] != sm.m[r][c]) return false; return true;}
|
||||
bool operator !=(const _CMatrix & sm) const {return !(*this == sm);}
|
||||
void operator +=(const _CMatrix & sm) {PIMM_FOR_WB(r, c) m[r][c] += sm.m[r][c];}
|
||||
void operator -=(const _CMatrix & sm) {PIMM_FOR_WB(r, c) m[r][c] -= sm.m[r][c];}
|
||||
void operator *=(const Type & v) {PIMM_FOR_WB(r, c) m[r][c] *= v;}
|
||||
void operator /=(const Type & v) {PIMM_FOR_WB(r, c) m[r][c] /= v;}
|
||||
_CMatrix operator -() {_CMatrix tm; PIMM_FOR_WB(r, c) tm.m[r][c] = -m[r][c]; return tm;}
|
||||
_CMatrix operator +(const _CMatrix & sm) {_CMatrix tm = _CMatrix(*this); PIMM_FOR_WB(r, c) tm.m[r][c] += sm.m[r][c]; return tm;}
|
||||
_CMatrix operator -(const _CMatrix & sm) {_CMatrix tm = _CMatrix(*this); PIMM_FOR_WB(r, c) tm.m[r][c] -= sm.m[r][c]; return tm;}
|
||||
_CMatrix operator *(const Type & v) {_CMatrix tm = _CMatrix(*this); PIMM_FOR_WB(r, c) tm.m[r][c] *= v; return tm;}
|
||||
_CMatrix operator /(const Type & v) {_CMatrix tm = _CMatrix(*this); PIMM_FOR_WB(r, c) tm.m[r][c] /= v; return tm;}
|
||||
|
||||
Type determinant(bool * ok = 0) const {
|
||||
_CMatrix m(*this);
|
||||
bool k;
|
||||
Type ret = Type(0);
|
||||
m.toUpperTriangular(&k);
|
||||
if (ok) *ok = k;
|
||||
if (!k) return ret;
|
||||
ret = Type(1);
|
||||
for (uint c = 0; c < Cols; ++c)
|
||||
for (uint r = 0; r < Rows; ++r)
|
||||
if (r == c)
|
||||
ret *= m[r][c];
|
||||
return ret;
|
||||
}
|
||||
|
||||
_CMatrix & toUpperTriangular(bool * ok = 0) {
|
||||
if (Cols != Rows) {
|
||||
if (ok != 0) *ok = false;
|
||||
return *this;
|
||||
}
|
||||
_CMatrix smat(*this);
|
||||
bool ndet;
|
||||
uint crow;
|
||||
Type mul;
|
||||
for (uint i = 0; i < Cols; ++i) {
|
||||
ndet = true;
|
||||
for (uint j = 0; j < Rows; ++j) if (smat.m[i][j] != 0) ndet = false;
|
||||
if (ndet) {
|
||||
if (ok != 0) *ok = false;
|
||||
return *this;
|
||||
}
|
||||
for (uint j = 0; j < Cols; ++j) if (smat.m[j][i] != 0) ndet = false;
|
||||
if (ndet) {
|
||||
if (ok != 0) *ok = false;
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
for (uint i = 0; i < Cols; ++i) {
|
||||
crow = i;
|
||||
while (smat.m[i][i] == Type(0))
|
||||
smat.swapRows(i, ++crow);
|
||||
for (uint j = i + 1; j < Rows; ++j) {
|
||||
mul = smat.m[i][j] / smat.m[i][i];
|
||||
for (uint k = i; k < Cols; ++k) smat.m[k][j] -= mul * smat.m[k][i];
|
||||
}
|
||||
if (i < Cols - 1) {
|
||||
if (fabs(smat.m[i+1][i+1]) < Type(1E-100)) {
|
||||
if (ok != 0) *ok = false;
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ok != 0) *ok = true;
|
||||
memcpy(m, smat.m, sizeof(Type) * Cols * Rows);
|
||||
return *this;
|
||||
}
|
||||
|
||||
_CMatrix & invert(bool * ok = 0) {
|
||||
if (Cols != Rows) {
|
||||
if (ok != 0) *ok = false;
|
||||
return *this;
|
||||
}
|
||||
_CMatrix mtmp = _CMatrix::identity(), smat(*this);
|
||||
bool ndet;
|
||||
uint crow;
|
||||
Type mul, iddiv;
|
||||
for (uint i = 0; i < Cols; ++i) {
|
||||
ndet = true;
|
||||
for (uint j = 0; j < Rows; ++j) if (smat.m[i][j] != 0) ndet = false;
|
||||
if (ndet) {
|
||||
if (ok != 0) *ok = false;
|
||||
return *this;
|
||||
}
|
||||
for (uint j = 0; j < Cols; ++j) if (smat.m[j][i] != 0) ndet = false;
|
||||
if (ndet) {
|
||||
if (ok != 0) *ok = false;
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
for (uint i = 0; i < Cols; ++i) {
|
||||
crow = i;
|
||||
while (smat.m[i][i] == Type(0)) {
|
||||
++crow;
|
||||
smat.swapRows(i, crow);
|
||||
mtmp.swapRows(i, crow);
|
||||
}
|
||||
for (uint j = i + 1; j < Rows; ++j) {
|
||||
mul = smat.m[i][j] / smat.m[i][i];
|
||||
for (uint k = i; k < Cols; ++k) smat.m[k][j] -= mul * smat.m[k][i];
|
||||
for (uint k = 0; k < Cols; ++k) mtmp.m[k][j] -= mul * mtmp.m[k][i];
|
||||
}
|
||||
//cout << i << endl << smat << endl;
|
||||
if (i < Cols - 1) {
|
||||
if (fabs(smat.m[i+1][i+1]) < Type(1E-100)) {
|
||||
if (ok != 0) *ok = false;
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
iddiv = smat.m[i][i];
|
||||
for (uint j = i; j < Cols; ++j) smat.m[j][i] /= iddiv;
|
||||
for (uint j = 0; j < Cols; ++j) mtmp.m[j][i] /= iddiv;
|
||||
}
|
||||
for (uint i = Cols - 1; i > 0; --i) {
|
||||
for (uint j = 0; j < i; ++j) {
|
||||
mul = smat.m[i][j];
|
||||
smat.m[i][j] -= mul;
|
||||
for (uint k = 0; k < Cols; ++k) mtmp.m[k][j] -= mtmp.m[k][i] * mul;
|
||||
}
|
||||
}
|
||||
if (ok != 0) *ok = true;
|
||||
memcpy(m, mtmp.m, sizeof(Type) * Cols * Rows);
|
||||
return *this;
|
||||
}
|
||||
_CMatrix inverted(bool * ok = 0) const {_CMatrix tm(*this); tm.invert(ok); return tm;}
|
||||
_CMatrixI transposed() const {_CMatrixI tm; PIMM_FOR_WB(r, c) tm[r][c] = m[r][c]; return tm;}
|
||||
|
||||
private:
|
||||
void resize(uint rows_, uint cols_, const Type & new_value = Type()) {r_ = rows_; c_ = cols_; PIMM_FOR_WB(r, c) m[r][c] = new_value;}
|
||||
int c_, r_;
|
||||
Type m[Rows][Cols];
|
||||
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
|
||||
template<> inline PIMathMatrixT<2u, 2u> PIMathMatrixT<2u, 2u>::rotation(double angle) {double c = cos(angle), s = sin(angle); PIMathMatrixT<2u, 2u> tm; tm[0][0] = tm[1][1] = c; tm[0][1] = -s; tm[1][0] = s; return tm;}
|
||||
template<> inline PIMathMatrixT<2u, 2u> PIMathMatrixT<2u, 2u>::scaleX(double factor) {PIMathMatrixT<2u, 2u> tm; tm[0][0] = factor; tm[1][1] = 1.; return tm;}
|
||||
template<> inline PIMathMatrixT<2u, 2u> PIMathMatrixT<2u, 2u>::scaleY(double factor) {PIMathMatrixT<2u, 2u> tm; tm[0][0] = 1.; tm[1][1] = factor; return tm;}
|
||||
|
||||
template<> inline PIMathMatrixT<3u, 3u> PIMathMatrixT<3u, 3u>::rotationX(double angle) {double c = cos(angle), s = sin(angle); PIMathMatrixT<3u, 3u> tm; tm[0][0] = 1.; tm[1][1] = tm[2][2] = c; tm[2][1] = s; tm[1][2] = -s; return tm;}
|
||||
template<> inline PIMathMatrixT<3u, 3u> PIMathMatrixT<3u, 3u>::rotationY(double angle) {double c = cos(angle), s = sin(angle); PIMathMatrixT<3u, 3u> tm; tm[1][1] = 1.; tm[0][0] = tm[2][2] = c; tm[2][0] = -s; tm[0][2] = s; return tm;}
|
||||
template<> inline PIMathMatrixT<3u, 3u> PIMathMatrixT<3u, 3u>::rotationZ(double angle) {double c = cos(angle), s = sin(angle); PIMathMatrixT<3u, 3u> tm; tm[2][2] = 1.; tm[0][0] = tm[1][1] = c; tm[1][0] = s; tm[0][1] = -s; return tm;}
|
||||
template<> inline PIMathMatrixT<3u, 3u> PIMathMatrixT<3u, 3u>::scaleX(double factor) {PIMathMatrixT<3u, 3u> tm; tm[1][1] = tm[2][2] = 1.; tm[0][0] = factor; return tm;}
|
||||
template<> inline PIMathMatrixT<3u, 3u> PIMathMatrixT<3u, 3u>::scaleY(double factor) {PIMathMatrixT<3u, 3u> tm; tm[0][0] = tm[2][2] = 1.; tm[1][1] = factor; return tm;}
|
||||
template<> inline PIMathMatrixT<3u, 3u> PIMathMatrixT<3u, 3u>::scaleZ(double factor) {PIMathMatrixT<3u, 3u> tm; tm[0][0] = tm[1][1] = 1.; tm[2][2] = factor; return tm;}
|
||||
|
||||
template<uint Rows, uint Cols, typename Type>
|
||||
inline std::ostream & operator <<(std::ostream & s, const PIMathMatrixT<Rows, Cols, Type> & m) {s << '{'; PIMM_FOR_I(r, c) s << m[r][c]; if (c < Cols - 1 || r < Rows - 1) s << ", ";} if (r < Rows - 1) s << endl << ' ';} s << '}'; return s;}
|
||||
template<uint Rows, uint Cols, typename Type>
|
||||
inline PICout operator <<(PICout s, const PIMathMatrixT<Rows, Cols, Type> & m) {s << '{'; PIMM_FOR_I(r, c) s << m[r][c]; if (c < Cols - 1 || r < Rows - 1) s << ", ";} if (r < Rows - 1) s << NewLine << ' ';} s << '}'; return s;}
|
||||
|
||||
/// Multiply matrices {Rows0 x CR} on {CR x Cols1}, result is {Rows0 x Cols1}
|
||||
template<uint CR, uint Rows0, uint Cols1, typename Type>
|
||||
inline PIMathMatrixT<Rows0, Cols1, Type> operator *(const PIMathMatrixT<Rows0, CR, Type> & fm,
|
||||
const PIMathMatrixT<CR, Cols1, Type> & sm) {
|
||||
PIMathMatrixT<Rows0, Cols1, Type> tm;
|
||||
Type t;
|
||||
for (uint j = 0; j < Rows0; ++j) {
|
||||
for (uint i = 0; i < Cols1; ++i) {
|
||||
t = Type(0);
|
||||
for (uint k = 0; k < CR; ++k)
|
||||
t += fm[j][k] * sm[k][i];
|
||||
tm[j][i] = t;
|
||||
}
|
||||
}
|
||||
return tm;
|
||||
}
|
||||
|
||||
/// Multiply matrix {Rows x Cols} on vector {Rows}, result is vector {Cols}
|
||||
template<uint Cols, uint Rows, typename Type>
|
||||
inline PIMathVectorT<Cols, Type> operator *(const PIMathMatrixT<Rows, Cols, Type> & fm,
|
||||
const PIMathVectorT<Rows, Type> & sv) {
|
||||
PIMathVectorT<Rows, Type> tv;
|
||||
Type t;
|
||||
for (uint j = 0; j < Rows; ++j) {
|
||||
t = Type(0);
|
||||
for (uint i = 0; i < Cols; ++i)
|
||||
t += fm[j][i] * sv[i];
|
||||
tv[j] = t;
|
||||
}
|
||||
return tv;
|
||||
}
|
||||
|
||||
typedef PIMathMatrixT<2u, 2u, int> PIMathMatrixT22i;
|
||||
typedef PIMathMatrixT<3u, 3u, int> PIMathMatrixT33i;
|
||||
typedef PIMathMatrixT<4u, 4u, int> PIMathMatrixT44i;
|
||||
typedef PIMathMatrixT<2u, 2u, double> PIMathMatrixT22d;
|
||||
typedef PIMathMatrixT<3u, 3u, double> PIMathMatrixT33d;
|
||||
typedef PIMathMatrixT<4u, 4u, double> PIMathMatrixT44d;
|
||||
|
||||
|
||||
template<typename Type>
|
||||
class PIMathMatrix;
|
||||
|
||||
#undef PIMV_FOR
|
||||
#undef PIMM_FOR
|
||||
#undef PIMM_FOR_WB
|
||||
#undef PIMM_FOR_I
|
||||
#undef PIMM_FOR_I_WB
|
||||
#undef PIMM_FOR_C
|
||||
#undef PIMM_FOR_R
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Matrix
|
||||
|
||||
#define PIMM_FOR(c, r) for (uint c = 0; c < cols_; ++c) { for (uint r = 0; r < rows_; ++r) {
|
||||
#define PIMM_FOR_WB(c, r) for (uint c = 0; c < cols_; ++c) for (uint r = 0; r < rows_; ++r) // without brakes
|
||||
#define PIMM_FOR_I(c, r) for (uint r = 0; r < rows_; ++r) { for (uint c = 0; c < cols_; ++c) {
|
||||
#define PIMM_FOR_I_WB(c, r) for (uint r = 0; r < rows_; ++r) for (uint c = 0; c < cols_; ++c) // without brakes
|
||||
#define PIMM_FOR_C(v) for (uint v = 0; v < cols_; ++v)
|
||||
#define PIMM_FOR_R(v) for (uint v = 0; v < rows_; ++v)
|
||||
|
||||
template<typename Type>
|
||||
class PIP_EXPORT PIMathMatrix {
|
||||
typedef PIMathMatrix<Type> _CMatrix;
|
||||
typedef PIMathVector<Type> _CMCol;
|
||||
typedef PIMathVector<Type> _CMRow;
|
||||
public:
|
||||
PIMathMatrix(const uint cols = 3, const uint rows = 3) {resize(cols, rows);}
|
||||
PIMathMatrix(const uint cols, const uint rows, Type fval, ...) {resize(cols, rows); va_list vl; va_start(vl, fval); PIMM_FOR_I_WB(c, r) m[c][r] = (r + c == 0 ? fval : va_arg(vl, Type)); va_end(vl);}
|
||||
PIMathMatrix(const uint cols, const uint rows, const PIVector<Type> & val) {resize(cols, rows); int i = 0; PIMM_FOR_I_WB(c, r) m[c][r] = val[i++];}
|
||||
|
||||
static _CMatrix identity(const uint cols_, const uint rows_) {_CMatrix tm(cols_, rows_); PIMM_FOR_WB(c, r) tm.m[c][r] = (c == r ? Type(1) : Type(0)); return tm;}
|
||||
|
||||
uint cols() const {return cols_;}
|
||||
uint rows() const {return rows_;}
|
||||
_CMCol col(uint index) {_CMCol tv; PIMM_FOR_R(i) tv[i] = m[index][i]; return tv;}
|
||||
_CMRow row(uint index) {_CMRow tv; PIMM_FOR_C(i) tv[i] = m[i][index]; return tv;}
|
||||
_CMatrix & resize(const uint cols, const uint rows, const Type & new_value = Type()) {cols_ = cols; rows_ = rows; m.resize(cols); PIMM_FOR_C(i) m[i].resize(rows, new_value); return *this;}
|
||||
_CMatrix & setCol(uint index, const _CMCol & v) {PIMM_FOR_R(i) m[index][i] = v[i]; return *this;}
|
||||
_CMatrix & setRow(uint index, const _CMRow & v) {PIMM_FOR_C(i) m[i][index] = v[i]; return *this;}
|
||||
_CMatrix & swapRows(uint r0, uint r1) {Type t; PIMM_FOR_C(i) {t = m[i][r0]; m[i][r0] = m[i][r1]; m[i][r1] = t;} return *this;}
|
||||
_CMatrix & swapCols(uint c0, uint c1) {Type t; PIMM_FOR_R(i) {t = m[c0][i]; m[c0][i] = m[c1][i]; m[c1][i] = t;} return *this;}
|
||||
_CMatrix & fill(const Type & v) {PIMM_FOR_WB(c, r) m[c][r] = v; return *this;}
|
||||
//inline _CMatrix & set(Type fval, ...) {m[0] = fval; va_list vl; va_start(vl, fval); PIMV_FOR(i, 1) m[i] = va_arg(vl, Type); va_end(vl); return *this;}
|
||||
//inline void normalize() {Type tv = length(); if (tv == Type(1)) return; PIMV_FOR(i, 0) m[i] /= tv;}
|
||||
bool isSquare() const {return cols() == rows();}
|
||||
bool isIdentity() const {PIMM_FOR_WB(c, r) if ((c == r) ? m[c][r] != Type(1) : m[c][r] != Type(0)) return false; return true;}
|
||||
bool isNull() const {PIMM_FOR_WB(c, r) if (m[c][r] != Type(0)) return false; return true;}
|
||||
|
||||
Type & at(uint col, uint row) {return m[col][row];}
|
||||
Type at(uint col, uint row) const {return m[col][row];}
|
||||
PIVector<Type> & operator [](uint col) {return m[col];}
|
||||
PIVector<Type> operator [](uint col) const {return m[col];}
|
||||
void operator =(const _CMatrix & sm) {m = sm.m;}
|
||||
bool operator ==(const _CMatrix & sm) const {PIMM_FOR_WB(c, r) if (m[c][r] != sm.m[c][r]) return false; return true;}
|
||||
bool operator !=(const _CMatrix & sm) const {return !(*this == sm);}
|
||||
void operator +=(const _CMatrix & sm) {PIMM_FOR_WB(c, r) m[c][r] += sm.m[c][r];}
|
||||
void operator -=(const _CMatrix & sm) {PIMM_FOR_WB(c, r) m[c][r] -= sm.m[c][r];}
|
||||
void operator *=(const Type & v) {PIMM_FOR_WB(c, r) m[c][r] *= v;}
|
||||
void operator /=(const Type & v) {PIMM_FOR_WB(c, r) m[c][r] /= v;}
|
||||
_CMatrix operator -() {_CMatrix tm(*this); PIMM_FOR_WB(c, r) tm.m[c][r] = -m[c][r]; return tm;}
|
||||
_CMatrix operator +(const _CMatrix & sm) {_CMatrix tm(*this); PIMM_FOR_WB(c, r) tm.m[c][r] += sm.m[c][r]; return tm;}
|
||||
_CMatrix operator -(const _CMatrix & sm) {_CMatrix tm(*this); PIMM_FOR_WB(c, r) tm.m[c][r] -= sm.m[c][r]; return tm;}
|
||||
_CMatrix operator *(const Type & v) {_CMatrix tm(*this); PIMM_FOR_WB(c, r) tm.m[c][r] *= v; return tm;}
|
||||
_CMatrix operator /(const Type & v) {_CMatrix tm(*this); PIMM_FOR_WB(c, r) tm.m[c][r] /= v; return tm;}
|
||||
|
||||
_CMatrix & toUpperTriangular(bool * ok = 0) {
|
||||
if (cols_ != rows_) {
|
||||
if (ok != 0) *ok = false;
|
||||
return *this;
|
||||
}
|
||||
_CMatrix smat(*this);
|
||||
bool ndet;
|
||||
uint crow;
|
||||
Type mul;
|
||||
for (uint i = 0; i < cols_; ++i) {
|
||||
ndet = true;
|
||||
for (uint j = 0; j < rows_; ++j) if (smat.m[i][j] != 0) ndet = false;
|
||||
if (ndet) {
|
||||
if (ok != 0) *ok = false;
|
||||
return *this;
|
||||
}
|
||||
for (uint j = 0; j < cols_; ++j) if (smat.m[j][i] != 0) ndet = false;
|
||||
if (ndet) {
|
||||
if (ok != 0) *ok = false;
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
for (uint i = 0; i < cols_; ++i) {
|
||||
crow = i;
|
||||
while (smat.m[i][i] == Type(0))
|
||||
smat.swapRows(i, ++crow);
|
||||
for (uint j = i + 1; j < rows_; ++j) {
|
||||
mul = smat.m[i][j] / smat.m[i][i];
|
||||
for (uint k = i; k < cols_; ++k) smat.m[k][j] -= mul * smat.m[k][i];
|
||||
}
|
||||
if (i < cols_ - 1) {
|
||||
if (fabs(smat.m[i+1][i+1]) < Type(1E-100)) {
|
||||
if (ok != 0) *ok = false;
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ok != 0) *ok = true;
|
||||
m = smat.m;
|
||||
return *this;
|
||||
}
|
||||
|
||||
_CMatrix & invert(bool * ok = 0, _CMCol * sv = 0) {
|
||||
if (cols_ != rows_) {
|
||||
if (ok != 0) *ok = false;
|
||||
return *this;
|
||||
}
|
||||
_CMatrix mtmp = _CMatrix::identity(cols_, rows_), smat(*this);
|
||||
bool ndet;
|
||||
uint crow;
|
||||
Type mul, iddiv;
|
||||
for (uint i = 0; i < cols_; ++i) {
|
||||
ndet = true;
|
||||
for (uint j = 0; j < rows_; ++j) if (smat.m[i][j] != 0) ndet = false;
|
||||
if (ndet) {
|
||||
if (ok != 0) *ok = false;
|
||||
return *this;
|
||||
}
|
||||
for (uint j = 0; j < cols_; ++j) if (smat.m[j][i] != 0) ndet = false;
|
||||
if (ndet) {
|
||||
if (ok != 0) *ok = false;
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
for (uint i = 0; i < cols_; ++i) {
|
||||
crow = i;
|
||||
while (smat.m[i][i] == Type(0)) {
|
||||
++crow;
|
||||
smat.swapRows(i, crow);
|
||||
mtmp.swapRows(i, crow);
|
||||
if (sv != 0) sv->swap(i, crow);
|
||||
}
|
||||
for (uint j = i + 1; j < rows_; ++j) {
|
||||
mul = smat.m[i][j] / smat.m[i][i];
|
||||
for (uint k = i; k < cols_; ++k) smat.m[k][j] -= mul * smat.m[k][i];
|
||||
for (uint k = 0; k < cols_; ++k) mtmp.m[k][j] -= mul * mtmp.m[k][i];
|
||||
if (sv != 0) (*sv)[j] -= mul * (*sv)[i];
|
||||
}
|
||||
//cout << i << endl << smat << endl;
|
||||
if (i < cols_ - 1) {
|
||||
if (fabs(smat.m[i+1][i+1]) < Type(1E-100)) {
|
||||
if (ok != 0) *ok = false;
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
iddiv = smat.m[i][i];
|
||||
for (uint j = i; j < cols_; ++j) smat.m[j][i] /= iddiv;
|
||||
for (uint j = 0; j < cols_; ++j) mtmp.m[j][i] /= iddiv;
|
||||
if (sv != 0) (*sv)[i] /= iddiv;
|
||||
}
|
||||
for (uint i = cols_ - 1; i > 0; --i) {
|
||||
for (uint j = 0; j < i; ++j) {
|
||||
mul = smat.m[i][j];
|
||||
smat.m[i][j] -= mul;
|
||||
for (uint k = 0; k < cols_; ++k) mtmp.m[k][j] -= mtmp.m[k][i] * mul;
|
||||
if (sv != 0) (*sv)[j] -= mul * (*sv)[i];
|
||||
}
|
||||
}
|
||||
if (ok != 0) *ok = true;
|
||||
m = mtmp.m;
|
||||
return *this;
|
||||
}
|
||||
_CMatrix inverted(bool * ok = 0) {_CMatrix tm(*this); tm.invert(ok); return tm;}
|
||||
_CMatrix transposed() {_CMatrix tm(rows_, cols_); PIMM_FOR_WB(c, r) tm[r][c] = m[c][r]; return tm;}
|
||||
|
||||
private:
|
||||
uint cols_, rows_;
|
||||
PIVector<PIVector<Type> > m;
|
||||
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
inline std::ostream & operator <<(std::ostream & s, const PIMathMatrix<Type> & m) {s << '{'; for (uint r = 0; r < m.rows(); ++r) { for (uint c = 0; c < m.cols(); ++c) { s << m[c][r]; if (c < m.cols() - 1 || r < m.rows() - 1) s << ", ";} if (r < m.rows() - 1) s << endl << ' ';} s << '}'; return s;}
|
||||
template<typename Type>
|
||||
inline PICout operator <<(PICout s, const PIMathMatrix<Type> & m) {s << '{'; for (uint r = 0; r < m.rows(); ++r) { for (uint c = 0; c < m.cols(); ++c) { s << m[c][r]; if (c < m.cols() - 1 || r < m.rows() - 1) s << ", ";} if (r < m.rows() - 1) s << NewLine << ' ';} s << '}'; return s;}
|
||||
|
||||
/// Multiply matrices {CR x Rows0} on {Cols1 x CR}, result is {Cols1 x Rows0}
|
||||
template<typename Type>
|
||||
inline PIMathMatrix<Type> operator *(const PIMathMatrix<Type> & fm,
|
||||
const PIMathMatrix<Type> & sm) {
|
||||
uint cr = fm.cols(), rows0 = fm.rows(), cols1 = sm.cols();
|
||||
PIMathMatrix<Type> tm(cols1, rows0);
|
||||
if (fm.cols() != sm.rows()) return tm;
|
||||
Type t;
|
||||
for (uint j = 0; j < rows0; ++j) {
|
||||
for (uint i = 0; i < cols1; ++i) {
|
||||
t = Type(0);
|
||||
for (uint k = 0; k < cr; ++k)
|
||||
t += fm[k][j] * sm[i][k];
|
||||
tm[i][j] = t;
|
||||
}
|
||||
}
|
||||
return tm;
|
||||
}
|
||||
|
||||
/// Multiply matrix {Cols x Rows} on vector {Cols}, result is vector {Rows}
|
||||
template<typename Type>
|
||||
inline PIMathVector<Type> operator *(const PIMathMatrix<Type> & fm,
|
||||
const PIMathVector<Type> & sv) {
|
||||
uint c = fm.cols(), r = fm.rows();
|
||||
PIMathVector<Type> tv(r);
|
||||
if (c != sv.size()) return tv;
|
||||
Type t;
|
||||
for (uint i = 0; i < r; ++i) {
|
||||
t = Type(0);
|
||||
for (uint j = 0; j < c; ++j)
|
||||
t += fm[j][i] * sv[j];
|
||||
tv[i] = t;
|
||||
}
|
||||
return tv;
|
||||
}
|
||||
|
||||
typedef PIMathMatrix<int> PIMathMatrixi;
|
||||
typedef PIMathMatrix<double> PIMathMatrixd;
|
||||
|
||||
#undef PIMV_FOR
|
||||
#undef PIMM_FOR
|
||||
#undef PIMM_FOR_WB
|
||||
#undef PIMM_FOR_I
|
||||
#undef PIMM_FOR_I_WB
|
||||
#undef PIMM_FOR_C
|
||||
#undef PIMM_FOR_R
|
||||
|
||||
#endif // PIMATHMATRIX_H
|
||||
248
src/math/pimathsolver.cpp
Normal file
248
src/math/pimathsolver.cpp
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
PIMathSolver
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pimathsolver.h"
|
||||
|
||||
const char PIMathSolver::methods_desc[] = "b{Methods:}\
|
||||
\n -1 - Global settings\
|
||||
\n 01 - Eyler 1\
|
||||
\n 02 - Eyler 2\
|
||||
\n 14 - Runge-Kutta 4\
|
||||
\n 23 - Adams-Bashfort-Moulton 3\
|
||||
\n 24 - Adams-Bashfort-Moulton 4\
|
||||
\n 32 - Polynomial Approximation 2\
|
||||
\n 33 - Polynomial Approximation 3\
|
||||
\n 34 - Polynomial Approximation 4\
|
||||
\n 35 - Polynomial Approximation 5";
|
||||
|
||||
PIMathSolver::Method PIMathSolver::method_global = PIMathSolver::Eyler_2;
|
||||
|
||||
|
||||
void PIMathSolver::solve(double u, double h) {
|
||||
switch (method) {
|
||||
case Global:
|
||||
switch (method_global) {
|
||||
case Eyler_1: solveEyler1(u, h); break;
|
||||
case Eyler_2: solveEyler2(u, h); break;
|
||||
case RungeKutta_4: solveRK4(u, h); break;
|
||||
case AdamsBashfortMoulton_2: solveABM2(u, h); break;
|
||||
case AdamsBashfortMoulton_3: solveABM3(u, h); break;
|
||||
case AdamsBashfortMoulton_4: default: solveABM4(u, h); break;
|
||||
case PolynomialApproximation_2: solvePA2(u, h); break;
|
||||
case PolynomialApproximation_3: solvePA3(u, h); break;
|
||||
case PolynomialApproximation_4: solvePA4(u, h); break;
|
||||
case PolynomialApproximation_5: solvePA5(u, h); break;
|
||||
}
|
||||
break;
|
||||
case Eyler_1: solveEyler1(u, h); break;
|
||||
case Eyler_2: solveEyler2(u, h); break;
|
||||
case RungeKutta_4: solveRK4(u, h); break;
|
||||
case AdamsBashfortMoulton_2: solveABM2(u, h); break;
|
||||
case AdamsBashfortMoulton_3: solveABM3(u, h); break;
|
||||
case AdamsBashfortMoulton_4: default: solveABM4(u, h); break;
|
||||
case PolynomialApproximation_2: solvePA2(u, h); break;
|
||||
case PolynomialApproximation_3: solvePA3(u, h); break;
|
||||
case PolynomialApproximation_4: solvePA4(u, h); break;
|
||||
case PolynomialApproximation_5: solvePA5(u, h); break;
|
||||
}
|
||||
step++;
|
||||
}
|
||||
|
||||
|
||||
void PIMathSolver::fromTF(const TransferFunction & TF) {
|
||||
if (TF.vector_An.size() >= TF.vector_Bm.size())
|
||||
size = TF.vector_An.size()-1;
|
||||
else {
|
||||
piCout << "PIMathSolver error: {A} should be greater than {B}";
|
||||
return;
|
||||
}
|
||||
if (size == 0) return;
|
||||
|
||||
step = 0;
|
||||
times.fill(0.);
|
||||
A.resize(size, size);
|
||||
d.resize(size + 1); d.fill(0.);
|
||||
a1.resize(size + 1); a1.fill(0.);
|
||||
b1.resize(size + 1); b1.fill(0.);
|
||||
X.resize(size); X.fill(0.);
|
||||
F.resize(5);
|
||||
for (uint i = 0; i < 5; ++i)
|
||||
F[i].resize(size), F[i].fill(0.);
|
||||
k1.resize(size); k1.fill(0.);
|
||||
k2.resize(size); k2.fill(0.);
|
||||
k3.resize(size); k3.fill(0.);
|
||||
k4.resize(size); k4.fill(0.);
|
||||
xx.resize(size); xx.fill(0.);
|
||||
XX.resize(size); XX.fill(0.);
|
||||
for (uint i = 0; i < size + 1; ++i)
|
||||
a1[size - i] = TF.vector_An[i];
|
||||
for (uint i = 0; i < TF.vector_Bm.size(); ++i)
|
||||
b1[size - i] = TF.vector_Bm[i];
|
||||
double a0 = a1[0];
|
||||
a1 /= a0;
|
||||
b1 /= a0;
|
||||
|
||||
d[0] = b1[0]; // Ðàññ÷èòûâàåì âåêòîð d
|
||||
for (uint i = 1; i < size + 1; ++i) {
|
||||
sum = 0.;
|
||||
for (uint m = 0; m < i; ++m)
|
||||
sum += a1[i - m] * d[m];
|
||||
d[i] = b1[i] - sum;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < size - 1; ++i) // Çàïîëíÿåì ìàòðèöó À
|
||||
for (uint j = 0; j < size; ++j)
|
||||
A[j][i] = (j == i + 1);
|
||||
for (uint i = 0; i < size; ++i)
|
||||
A[i][size - 1] = -a1[size - i];
|
||||
for (uint i = 0; i < size; ++i)
|
||||
d[i] = d[i + 1];
|
||||
}
|
||||
|
||||
|
||||
void PIMathSolver::solveEyler1(double u, double h) {
|
||||
/*for (uint i = 0; i < size; ++i) {
|
||||
* sum = 0.;
|
||||
* for (uint j = 0; j < size; ++j)
|
||||
* sum += A[j][i] * X[j];
|
||||
* xx[i] = sum + d[i] * u;
|
||||
}*/
|
||||
F[0] = A * X + d * u;
|
||||
X += F[0] * h;
|
||||
moveF();
|
||||
}
|
||||
|
||||
|
||||
void PIMathSolver::solveEyler2(double u, double h) {
|
||||
F[0] = A * X + d * u;
|
||||
X += (F[0] + F[1]) * h / 2.;
|
||||
moveF();
|
||||
}
|
||||
|
||||
|
||||
void PIMathSolver::solveRK4(double u, double h) {
|
||||
td = X[0] - F[0][0];
|
||||
k1 = A * X + d * u; xx = k1 * h / 2.; XX = X + xx;
|
||||
k2 = A * (XX + k1 * h / 2.) + d * (u + td/3.); xx = k2 * h / 2.; XX += xx;
|
||||
k3 = A * (XX + k2 * h / 2.) + d * (u + td*2./3.); xx = k3 * h; XX += xx;
|
||||
k4 = A * (XX + k3 * h) + d * (u + td);
|
||||
//cout << k1 << k2 << k3 << k4 << endl;
|
||||
X += (k1 + k2 * 2. + k3 * 2. + k4) * h / 6.;
|
||||
F[0] = X;
|
||||
}
|
||||
|
||||
|
||||
void PIMathSolver::solveABM2(double u, double h) {
|
||||
F[0] = A * X + d * u;
|
||||
XX = X + (F[0] * 3. - F[1]) * (h / 2.);
|
||||
F[1] = A * XX + d * u;
|
||||
X += (F[1] + F[0]) * (h / 2.);
|
||||
moveF();
|
||||
}
|
||||
|
||||
|
||||
void PIMathSolver::solveABM3(double u, double h) {
|
||||
F[0] = A * X + d * u;
|
||||
XX = X + (F[0] * 23. - F[1] * 16. + F[2] * 5.) * (h / 12.);
|
||||
F[2] = A * XX + d * u;
|
||||
X += (F[2] * 5. + F[0] * 8. - F[1]) * (h / 12.);
|
||||
moveF();
|
||||
}
|
||||
|
||||
|
||||
void PIMathSolver::solveABM4(double u, double h) {
|
||||
F[0] = A * X + d * u;
|
||||
XX = X + (F[0] * 55. - F[1] * 59. + F[2] * 37. - F[3] * 9.) * (h / 24.);
|
||||
F[3] = A * XX + d * u;
|
||||
X += (F[3] * 9. + F[0] * 19. - F[1] * 5. + F[2]) * (h / 24.);
|
||||
moveF();
|
||||
}
|
||||
|
||||
|
||||
void PIMathSolver::solvePA(double u, double h, uint deg) {
|
||||
F[0] = A * X + d * u;
|
||||
M.resize(deg, deg);
|
||||
Y.resize(deg);
|
||||
pY.resize(deg);
|
||||
|
||||
for (uint k = 0; k < size; ++k) {
|
||||
for (uint i = 0; i < deg; ++i) {
|
||||
td = 1.;
|
||||
ct = times[i];
|
||||
for (uint j = 0; j < deg; ++j) {
|
||||
M[j][i] = td;
|
||||
td *= ct;
|
||||
}
|
||||
}
|
||||
for (uint i = 0; i < deg; ++i)
|
||||
Y[i] = F[i][k];
|
||||
/// find polynom
|
||||
//if (step == 1) cout << M << endl << Y << endl;
|
||||
M.invert(&ok, &Y);
|
||||
//if (step == 1) cout << Y << endl;
|
||||
if (!ok) {
|
||||
solveEyler2(u, h);
|
||||
break;
|
||||
}
|
||||
/// calc last piece
|
||||
x0 = 0.;
|
||||
td = 1.;
|
||||
t = times[0];
|
||||
for (uint i = 0; i < deg; ++i) {
|
||||
x0 += Y[i] * td / (i + 1.);
|
||||
td *= t;
|
||||
}
|
||||
x0 *= t;
|
||||
|
||||
x1 = 0.;
|
||||
td = 1.;
|
||||
t = times[1];
|
||||
for (uint i = 0; i < deg; ++i) {
|
||||
x1 += Y[i] * td / (i + 1.);
|
||||
td *= t;
|
||||
}
|
||||
x1 *= t;
|
||||
lp = x0 - x1;
|
||||
|
||||
if (deg > 2) {
|
||||
/// calc prev piece
|
||||
x0 = 0.;
|
||||
td = 1.;
|
||||
dh = times[1] - times[2];
|
||||
if (dh != 0. && step > 1) {
|
||||
t = times[2];
|
||||
for (uint i = 0; i < deg; ++i) {
|
||||
x0 += Y[i] * td / (i + 1.);
|
||||
td *= t;
|
||||
}
|
||||
x0 *= t;
|
||||
ct = x1 - x0;
|
||||
/// calc correction
|
||||
ct -= pY[k];
|
||||
}
|
||||
/// calc final
|
||||
X[k] += lp - ct;
|
||||
pY[k] = lp;
|
||||
} else {
|
||||
X[k] += lp;
|
||||
pY[k] = lp;
|
||||
}
|
||||
}
|
||||
moveF();
|
||||
}
|
||||
94
src/math/pimathsolver.h
Normal file
94
src/math/pimathsolver.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/*! \file pimathsolver.h
|
||||
* \brief PIMathSolver
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
PIMathSolver
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIMATHSOLVER_H
|
||||
#define PIMATHSOLVER_H
|
||||
|
||||
#include "pimathmatrix.h"
|
||||
|
||||
/// Differential evaluations
|
||||
|
||||
struct TransferFunction { // Äëÿ çàäàíèÿ ïåðåäàòî÷íîé ôóíêöèè
|
||||
PIVector<double> vector_Bm, vector_An;
|
||||
};
|
||||
|
||||
// Êëàññ, ñëóæàùèé äëÿ ïåðåâîäà ïåðåäàòî÷íîé ôóíêöèè â ñèñòåìó ÎÄÓ ïåðâîãî ïîðÿäêà
|
||||
// ðåàëèçîâàíû ñëåä. ìåòîäû ðåøåíèÿ äèôô. óð-íèé:
|
||||
// Ýéëåðà
|
||||
// Ðóíãå-Êóòòà 4-ãî ïîðÿäêà
|
||||
// Àäàìñà-Áýøôîðòñà-Ìîóëòîíà 2, 3, 4 ïîðÿäêîâ
|
||||
class PIP_EXPORT PIMathSolver
|
||||
{
|
||||
public:
|
||||
enum Method {Global = -1,
|
||||
Eyler_1 = 01,
|
||||
Eyler_2 = 02,
|
||||
EylerKoshi = 03,
|
||||
RungeKutta_4 = 14,
|
||||
AdamsBashfortMoulton_2 = 22,
|
||||
AdamsBashfortMoulton_3 = 23,
|
||||
AdamsBashfortMoulton_4 = 24,
|
||||
PolynomialApproximation_2 = 32,
|
||||
PolynomialApproximation_3 = 33,
|
||||
PolynomialApproximation_4 = 34,
|
||||
PolynomialApproximation_5 = 35
|
||||
};
|
||||
|
||||
PIMathSolver() {times.resize(4); step = 0;}
|
||||
|
||||
void solve(double u, double h);
|
||||
void fromTF(const TransferFunction & TF);
|
||||
void setMethod(Method m) {method = m;}
|
||||
void setTime(double time) {times.pop_back(); times.push_front(time);}
|
||||
|
||||
void solveEyler1(double u, double h);
|
||||
void solveEyler2(double u, double h);
|
||||
void solveRK4(double u, double h);
|
||||
void solveABM2(double u, double h);
|
||||
void solveABM3(double u, double h);
|
||||
void solveABM4(double u, double h);
|
||||
void solvePA(double u, double h, uint deg);
|
||||
void solvePA2(double u, double h) {if (step > 0) solvePA(u, h, 2); else solveEyler1(u, h);}
|
||||
void solvePA3(double u, double h) {if (step > 1) solvePA(u, h, 3); else solvePA2(u, h);}
|
||||
void solvePA4(double u, double h) {if (step > 2) solvePA(u, h, 4); else solvePA3(u, h);}
|
||||
void solvePA5(double u, double h) {if (step > 3) solvePA(u, h, 5); else solvePA4(u, h);}
|
||||
|
||||
PIMathVectord X;
|
||||
static Method method_global;
|
||||
static const char methods_desc[];
|
||||
|
||||
private:
|
||||
void moveF() {for (uint i = F.size() - 1; i > 0; --i) F[i] = F[i - 1];}
|
||||
|
||||
PIMathMatrixd A, M;
|
||||
PIMathVectord d, a1, b1;
|
||||
PIMathVectord k1, k2, k3, k4, xx;
|
||||
PIMathVectord XX, Y, pY;
|
||||
PIVector<PIMathVectord> F;
|
||||
PIVector<double> times;
|
||||
uint size, step;
|
||||
Method method;
|
||||
double sum, td, ct, lp, dh, t, x1, x0;
|
||||
bool ok;
|
||||
};
|
||||
|
||||
#endif // PIMATHSOLVER_H
|
||||
229
src/math/pimathvector.h
Normal file
229
src/math/pimathvector.h
Normal file
@@ -0,0 +1,229 @@
|
||||
/*! \file pimathvector.h
|
||||
* \brief PIMathVector
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
PIMathVector
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIMATHVECTOR_H
|
||||
#define PIMATHVECTOR_H
|
||||
|
||||
#include "pimathbase.h"
|
||||
|
||||
template<uint Cols, uint Rows, typename Type>
|
||||
class PIMathMatrixT;
|
||||
|
||||
|
||||
/// Vector templated
|
||||
|
||||
#define PIMV_FOR(v, s) for (uint v = s; v < Size; ++v)
|
||||
|
||||
#pragma pack(push, 1)
|
||||
template<uint Size, typename Type = double>
|
||||
class PIP_EXPORT PIMathVectorT {
|
||||
typedef PIMathVectorT<Size, Type> _CVector;
|
||||
public:
|
||||
PIMathVectorT() {resize(Size);}
|
||||
//PIMathVectorT(Type val) {resize(Size); PIMV_FOR(i, 0) c[i] = val;}
|
||||
PIMathVectorT(Type fval, ...) {resize(Size); c[0] = fval; va_list vl; va_start(vl, fval); PIMV_FOR(i, 1) c[i] = va_arg(vl, Type); va_end(vl);}
|
||||
PIMathVectorT(const PIVector<Type> & val) {resize(Size); PIMV_FOR(i, 0) c[i] = val[i];}
|
||||
PIMathVectorT(const _CVector & st, const _CVector & fn) {resize(Size); set(st, fn);}
|
||||
|
||||
uint size() const {return Size;}
|
||||
_CVector & fill(const Type & v) {PIMV_FOR(i, 0) c[i] = v; return *this;}
|
||||
_CVector & set(Type fval, ...) {c[0] = fval; va_list vl; va_start(vl, fval); PIMV_FOR(i, 1) c[i] = va_arg(vl, Type); va_end(vl); return *this;}
|
||||
_CVector & set(const _CVector & st, const _CVector & fn) {PIMV_FOR(i, 0) c[i] = fn[i] - st[i]; return *this;}
|
||||
_CVector & move(const Type & v) {PIMV_FOR(i, 0) c[i] += v; return *this;}
|
||||
_CVector & move(const _CVector & v) {PIMV_FOR(i, 0) c[i] += v[i]; return *this;}
|
||||
_CVector & move(Type fval, ...) {c[0] += fval; va_list vl; va_start(vl, fval); PIMV_FOR(i, 1) c[i] += va_arg(vl, Type); va_end(vl); return *this;}
|
||||
Type lengthSqr() const {Type tv(0); PIMV_FOR(i, 0) tv += (c[i] * c[i]); return tv;}
|
||||
Type length() const {return sqrt(lengthSqr());}
|
||||
Type manhattanLength() const {Type tv(0); PIMV_FOR(i, 0) tv += fabs(c[i]); return tv;}
|
||||
Type angleCos(const _CVector & v) const {Type tv = v.length() * length(); return (tv == Type(0) ? Type(0) : ((*this) ^ v) / tv);}
|
||||
Type angleSin(const _CVector & v) const {Type tv = angleCos(v); return sqrt(Type(1) - tv * tv);}
|
||||
Type angleRad(const _CVector & v) const {return acos(angleCos(v));}
|
||||
Type angleDeg(const _CVector & v) const {return toDeg(acos(angleCos(v)));}
|
||||
_CVector projection(const _CVector & v) {Type tv = v.length(); return (tv == Type(0) ? _CVector() : v * (((*this) ^ v) / tv));}
|
||||
_CVector & normalize() {Type tv = length(); if (tv == Type(1)) return *this; if (piAbs<Type>(tv) <= Type(1E-100)) {fill(Type(0)); return *this;} PIMV_FOR(i, 0) c[i] /= tv; return *this;}
|
||||
_CVector normalized() {_CVector tv(*this); tv.normalize(); return tv;}
|
||||
bool isNull() const {PIMV_FOR(i, 0) if (c[i] != Type(0)) return false; return true;}
|
||||
bool isOrtho(const _CVector & v) const {return ((*this) ^ v) == Type(0);}
|
||||
|
||||
Type & at(uint index) {return c[index];}
|
||||
Type at(uint index) const {return c[index];}
|
||||
Type & operator [](uint index) {return c[index];}
|
||||
Type operator [](uint index) const {return c[index];}
|
||||
_CVector & operator =(const _CVector & v) {memcpy(c, v.c, sizeof(Type) * Size); return *this;}
|
||||
bool operator ==(const _CVector & v) const {PIMV_FOR(i, 0) if (c[i] != v[i]) return false; return true;}
|
||||
bool operator !=(const _CVector & v) const {return !(*this == c);}
|
||||
void operator +=(const _CVector & v) {PIMV_FOR(i, 0) c[i] += v[i];}
|
||||
void operator -=(const _CVector & v) {PIMV_FOR(i, 0) c[i] -= v[i];}
|
||||
void operator *=(const Type & v) {PIMV_FOR(i, 0) c[i] *= v;}
|
||||
void operator *=(const _CVector & v) {PIMV_FOR(i, 0) c[i] *= v[i];}
|
||||
void operator /=(const Type & v) {PIMV_FOR(i, 0) c[i] /= v;}
|
||||
void operator /=(const _CVector & v) {PIMV_FOR(i, 0) c[i] /= v[i];}
|
||||
_CVector operator -() const {_CVector tv; PIMV_FOR(i, 0) tv[i] = -c[i]; return tv;}
|
||||
_CVector operator +(const _CVector & v) const {_CVector tv = _CVector(*this); PIMV_FOR(i, 0) tv[i] += v[i]; return tv;}
|
||||
_CVector operator -(const _CVector & v) const {_CVector tv = _CVector(*this); PIMV_FOR(i, 0) tv[i] -= v[i]; return tv;}
|
||||
_CVector operator *(const Type & v) const {_CVector tv = _CVector(*this); PIMV_FOR(i, 0) tv[i] *= v; return tv;}
|
||||
_CVector operator /(const Type & v) const {_CVector tv = _CVector(*this); PIMV_FOR(i, 0) tv[i] /= v; return tv;}
|
||||
_CVector operator /(const _CVector & v) const {_CVector tv = _CVector(*this); PIMV_FOR(i, 0) tv[i] /= v[i]; return tv;}
|
||||
_CVector operator *(const _CVector & v) const {if (Size > 3) return _CVector(); _CVector tv; tv.fill(Type(1)); tv[0] = c[1]*v[2] - v[1]*c[2]; tv[1] = v[0]*c[2] - c[0]*v[2]; tv[2] = c[0]*v[1] - v[0]*c[1]; return tv;}
|
||||
Type operator ^(const _CVector & v) const {Type tv(0); PIMV_FOR(i, 0) tv += c[i] * v[i]; return tv;}
|
||||
|
||||
PIMathMatrixT<1, Size, Type> transposed() const {
|
||||
PIMathMatrixT<1, Size, Type> ret;
|
||||
PIMV_FOR(i, 0) ret[0][i] = c[i];
|
||||
return ret;
|
||||
}
|
||||
|
||||
operator PIMathMatrixT<1, Size, Type>() {return transposed();}
|
||||
operator PIMathMatrixT<Size, 1, Type>() {
|
||||
PIMathMatrixT<Size, 1, Type> ret;
|
||||
PIMV_FOR(i, 0) ret[i][0] = c[i];
|
||||
return ret;
|
||||
}
|
||||
Type distToLine(const _CVector & lp0, const _CVector & lp1) {
|
||||
_CVector a(lp0, lp1), b(lp0, *this), c(lp1, *this);
|
||||
Type f = fabs(a[0]*b[1] - a[1]*b[0]) / a.length();//, s = b.length() + c.length() - a.length();
|
||||
return f;}
|
||||
|
||||
template<uint Size1, typename Type1> /// vector {Size, Type} to vector {Size1, Type1}
|
||||
PIMathVectorT<Size1, Type1> turnTo() {PIMathVectorT<Size1, Type1> tv; uint sz = piMin<uint>(Size, Size1); for (uint i = 0; i < sz; ++i) tv[i] = c[i]; return tv;}
|
||||
|
||||
private:
|
||||
void resize(uint size, const Type & new_value = Type()) {s = size; for (int i = 0; i < s; ++i) c[i] = new_value;}
|
||||
|
||||
int s;
|
||||
Type c[Size];
|
||||
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
template<uint Size, typename Type>
|
||||
inline std::ostream & operator <<(std::ostream & s, const PIMathVectorT<Size, Type> & v) {s << '{'; PIMV_FOR(i, 0) {s << v[i]; if (i < Size - 1) s << ", ";} s << '}'; return s;}
|
||||
template<uint Size, typename Type>
|
||||
inline PICout operator <<(PICout s, const PIMathVectorT<Size, Type> & v) {s << '{'; PIMV_FOR(i, 0) {s << v[i]; if (i < Size - 1) s << ", ";} s << '}'; return s;}
|
||||
template<uint Size, typename Type>
|
||||
inline bool operator ||(const PIMathVectorT<Size, Type> & f, const PIMathVectorT<Size, Type> & s) {return (f * s).isNull();}
|
||||
template<uint Size, typename Type>
|
||||
inline PIMathVectorT<Size, Type> sqrt(const PIMathVectorT<Size, Type> & v) {PIMathVectorT<Size, Type> ret; PIMV_FOR(i, 0) {ret[i] = sqrt(v[i]);} return ret;}
|
||||
template<uint Size, typename Type>
|
||||
inline PIMathVectorT<Size, Type> sqr(const PIMathVectorT<Size, Type> & v) {PIMathVectorT<Size, Type> ret; PIMV_FOR(i, 0) {ret[i] = sqr(v[i]);} return ret;}
|
||||
|
||||
template<uint Size, typename Type>
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PIMathVectorT<Size, Type> & v) {for (uint i = 0; i < Size; ++i) s << v[i]; return s;}
|
||||
template<uint Size, typename Type>
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PIMathVectorT<Size, Type> & v) {for (uint i = 0; i < Size; ++i) s >> v[i]; return s;}
|
||||
|
||||
//template<uint Size0, typename Type0 = double, uint Size1 = Size0, typename Type1 = Type0> /// vector {Size0, Type0} to vector {Size1, Type1}
|
||||
//inline operator PIMathVectorT<Size1, Type1>(const PIMathVectorT<Size0, Type0> & v) {PIMathVectorT<Size1, Type1> tv; uint sz = piMin<uint>(Size0, Size1); for (uint i = 0; i < sz; ++i) tv[i] = v[i]; return tv;}
|
||||
|
||||
typedef PIMathVectorT<2u, int> PIMathVectorT2i;
|
||||
typedef PIMathVectorT<3u, int> PIMathVectorT3i;
|
||||
typedef PIMathVectorT<4u, int> PIMathVectorT4i;
|
||||
typedef PIMathVectorT<2u, double> PIMathVectorT2d;
|
||||
typedef PIMathVectorT<3u, double> PIMathVectorT3d;
|
||||
typedef PIMathVectorT<4u, double> PIMathVectorT4d;
|
||||
|
||||
#undef PIMV_FOR
|
||||
|
||||
/// Vector
|
||||
|
||||
#define PIMV_FOR(v, s) for (uint v = s; v < size_; ++v)
|
||||
|
||||
template<typename Type>
|
||||
class PIP_EXPORT PIMathVector {
|
||||
typedef PIMathVector<Type> _CVector;
|
||||
public:
|
||||
PIMathVector(const uint size = 3) {resize(size);}
|
||||
PIMathVector(const uint size, Type fval, ...) {resize(size); c[0] = fval; va_list vl; va_start(vl, fval); PIMV_FOR(i, 1) c[i] = va_arg(vl, Type); va_end(vl);}
|
||||
PIMathVector(const PIVector<Type> & val) {resize(val.size()); PIMV_FOR(i, 0) c[i] = val[i];}
|
||||
PIMathVector(const _CVector & st, const _CVector & fn) {resize(st.size()); PIMV_FOR(i, 0) c[i] = fn[i] - st[i];}
|
||||
|
||||
uint size() const {return size_;}
|
||||
_CVector & resize(uint size, const Type & new_value = Type()) {size_ = size; c.resize(size, new_value); return *this;}
|
||||
_CVector resized(uint size, const Type & new_value = Type()) {_CVector tv = _CVector(*this); tv.resize(size, new_value); return tv;}
|
||||
_CVector & fill(const Type & v) {PIMV_FOR(i, 0) c[i] = v; return *this;}
|
||||
_CVector & set(Type fval, ...) {c[0] = fval; va_list vl; va_start(vl, fval); PIMV_FOR(i, 1) c[i] = va_arg(vl, Type); va_end(vl); return *this;}
|
||||
_CVector & move(const Type & v) {PIMV_FOR(i, 0) c[i] += v; return *this;}
|
||||
_CVector & move(const _CVector & v) {PIMV_FOR(i, 0) c[i] += v[i]; return *this;}
|
||||
_CVector & move(Type fval, ...) {c[0] += fval; va_list vl; va_start(vl, fval); PIMV_FOR(i, 1) c[i] += va_arg(vl, Type); va_end(vl); return *this;}
|
||||
_CVector & swap(uint fe, uint se) {piSwap<Type>(c[fe], c[se]); return *this;}
|
||||
Type lengthSqr() const {Type tv(0); PIMV_FOR(i, 0) tv += (c[i] * c[i]); return tv;}
|
||||
Type length() const {return sqrt(lengthSqr());}
|
||||
Type manhattanLength() const {Type tv(0); PIMV_FOR(i, 0) tv += fabs(c[i]); return tv;}
|
||||
Type angleCos(const _CVector & v) const {Type tv = v.length() * length(); return (tv == Type(0) ? Type(0) : ((*this) ^ v) / tv);}
|
||||
Type angleSin(const _CVector & v) const {Type tv = angleCos(v); return sqrt(Type(1) - tv * tv);}
|
||||
Type angleRad(const _CVector & v) const {return acos(angleCos(v));}
|
||||
Type angleDeg(const _CVector & v) const {return toDeg(acos(angleCos(v)));}
|
||||
_CVector projection(const _CVector & v) {Type tv = v.length(); return (tv == Type(0) ? _CVector() : v * (((*this) ^ v) / tv));}
|
||||
_CVector & normalize() {Type tv = length(); if (tv == Type(1)) return *this; if (piAbs<Type>(tv) <= Type(1E-100)) {fill(Type(0)); return *this;} PIMV_FOR(i, 0) c[i] /= tv; return *this;}
|
||||
_CVector normalized() {_CVector tv(*this); tv.normalize(); return tv;}
|
||||
bool isNull() const {PIMV_FOR(i, 0) if (c[i] != Type(0)) return false; return true;}
|
||||
bool isOrtho(const _CVector & v) const {return ((*this) ^ v) == Type(0);}
|
||||
|
||||
Type & at(uint index) {return c[index];}
|
||||
Type at(uint index) const {return c[index];}
|
||||
Type & operator [](uint index) {return c[index];}
|
||||
Type operator [](uint index) const {return c[index];}
|
||||
void operator =(const _CVector & v) {c = v.c;}
|
||||
bool operator ==(const _CVector & v) const {PIMV_FOR(i, 0) if (c[i] != v[i]) return false; return true;}
|
||||
bool operator !=(const _CVector & v) const {return !(*this == c);}
|
||||
void operator +=(const _CVector & v) {PIMV_FOR(i, 0) c[i] += v[i];}
|
||||
void operator -=(const _CVector & v) {PIMV_FOR(i, 0) c[i] -= v[i];}
|
||||
void operator *=(const Type & v) {PIMV_FOR(i, 0) c[i] *= v;}
|
||||
void operator *=(const _CVector & v) {PIMV_FOR(i, 0) c[i] *= v[i];}
|
||||
void operator /=(const Type & v) {PIMV_FOR(i, 0) c[i] /= v;}
|
||||
void operator /=(const _CVector & v) {PIMV_FOR(i, 0) c[i] /= v[i];}
|
||||
_CVector operator -() {_CVector tv; PIMV_FOR(i, 0) tv[i] = -c[i]; return tv;}
|
||||
_CVector operator +(const _CVector & v) {_CVector tv = _CVector(*this); PIMV_FOR(i, 0) tv[i] += v[i]; return tv;}
|
||||
_CVector operator -(const _CVector & v) {_CVector tv = _CVector(*this); PIMV_FOR(i, 0) tv[i] -= v[i]; return tv;}
|
||||
_CVector operator *(const Type & v) {_CVector tv = _CVector(*this); PIMV_FOR(i, 0) tv[i] *= v; return tv;}
|
||||
_CVector operator /(const Type & v) {_CVector tv = _CVector(*this); PIMV_FOR(i, 0) tv[i] /= v; return tv;}
|
||||
_CVector operator *(const _CVector & v) {if (size_ < 3) return _CVector(); _CVector tv; tv.fill(Type(1)); tv[0] = c[1]*v[2] - v[1]*c[2]; tv[1] = v[0]*c[2] - c[0]*v[2]; tv[2] = c[0]*v[1] - v[0]*c[1]; return tv;}
|
||||
Type operator ^(const _CVector & v) const {Type tv(0); PIMV_FOR(i, 0) tv += c[i] * v[i]; return tv;}
|
||||
|
||||
//inline operator PIMathMatrix<1, Size, Type>() {return PIMathMatrix<1, Size, Type>(c);}
|
||||
Type distToLine(const _CVector & lp0, const _CVector & lp1) {
|
||||
_CVector a(lp0, lp1), b(lp0, *this), c(lp1, *this);
|
||||
Type f = fabs(a[0]*b[1] - a[1]*b[0]) / a.length();//, s = b.length() + c.length() - a.length();
|
||||
return f;}
|
||||
|
||||
template<typename Type1>
|
||||
PIMathVector turnTo(uint size) {PIMathVector<Type1> tv; uint sz = piMin<uint>(size_, size); for (uint i = 0; i < sz; ++i) tv[i] = c[i]; return tv;}
|
||||
|
||||
private:
|
||||
uint size_;
|
||||
PIVector<Type> c;
|
||||
|
||||
};
|
||||
|
||||
#undef PIMV_FOR
|
||||
|
||||
template<typename Type>
|
||||
inline std::ostream & operator <<(std::ostream & s, const PIMathVector<Type> & v) {s << '{'; for (uint i = 0; i < v.size(); ++i) {s << v[i]; if (i < v.size() - 1) s << ", ";} s << '}'; return s;}
|
||||
template<typename Type>
|
||||
inline PICout operator <<(PICout s, const PIMathVector<Type> & v) {s << '{'; for (uint i = 0; i < v.size(); ++i) {s << v[i]; if (i < v.size() - 1) s << ", ";} s << '}'; return s;}
|
||||
|
||||
typedef PIMathVector<int> PIMathVectori;
|
||||
typedef PIMathVector<double> PIMathVectord;
|
||||
|
||||
//#include "pimathmatrix.h"
|
||||
|
||||
#endif // PIMATHVECTOR_H
|
||||
89
src/math/pistatistic.h
Normal file
89
src/math/pistatistic.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/*! \file pistatistic.h
|
||||
* \brief Class for calculating math statistic in values array
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Class for calculacing math statistic in values array
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com, Andrey Bychkov work.a.b@yandex.ru
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PISTATISTIC_H
|
||||
#define PISTATISTIC_H
|
||||
|
||||
#include "pimathbase.h"
|
||||
|
||||
template <typename T>
|
||||
class PIP_EXPORT PIStatistic {
|
||||
public:
|
||||
PIStatistic() {mean = variance = skewness = kurtosis = T();}
|
||||
|
||||
static T calculateMean(const PIVector<T> & val) {
|
||||
T ret = T();
|
||||
int n = val.size();
|
||||
if (n < 1)
|
||||
return ret;
|
||||
for (int i = 0; i < n; i++)
|
||||
ret += val[i];
|
||||
return ret / n;
|
||||
}
|
||||
bool calculate(const PIVector<T> & val, const T & given_mean) {
|
||||
T v = T(), v1 = T(), v2 = T(), stddev = T(), var = T();
|
||||
int i, n = val.size();
|
||||
if (n < 2)
|
||||
return false;
|
||||
mean = given_mean;
|
||||
variance = skewness = kurtosis = T();
|
||||
/*
|
||||
* Variance (using corrected two-pass algorithm)
|
||||
*/
|
||||
for (i = 0; i < n; i++)
|
||||
v1 += sqr(val[i] - mean);
|
||||
for (i = 0; i < n; i++)
|
||||
v2 += val[i] - mean;
|
||||
v2 = sqr(v2) / n;
|
||||
variance = v1 / n;
|
||||
var = (v1 / n - v2) / (n - 1);
|
||||
if (var < T())
|
||||
var = T();
|
||||
stddev = sqrt(var);
|
||||
/*
|
||||
* Skewness and kurtosis
|
||||
*/
|
||||
if (stddev != T()) {
|
||||
for (i = 0; i < n; i++) {
|
||||
v = (val[i] - mean) / stddev;
|
||||
v2 = sqr(v);
|
||||
skewness = skewness + v2 * v;
|
||||
kurtosis = kurtosis + sqr(v2);
|
||||
}
|
||||
skewness /= n;
|
||||
kurtosis = kurtosis / n - 3.;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool calculate(const PIVector<T> & val) {return calculate(val, calculateMean(val));}
|
||||
|
||||
T mean;
|
||||
T variance;
|
||||
T skewness;
|
||||
T kurtosis;
|
||||
};
|
||||
|
||||
typedef PIStatistic<int> PIStatistici;
|
||||
typedef PIStatistic<float> PIStatisticf;
|
||||
typedef PIStatistic<double> PIStatisticd;
|
||||
|
||||
#endif // PISTATISTIC_H
|
||||
BIN
src/system/.piscreen.cpp.kate-swp
Normal file
BIN
src/system/.piscreen.cpp.kate-swp
Normal file
Binary file not shown.
41
src/system/picodec.cpp
Executable file
41
src/system/picodec.cpp
Executable file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Text codings coder, based on "iconv"
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "picodec.h"
|
||||
|
||||
|
||||
PIStringList PICodec::availableCodecs() {
|
||||
exec("/usr/bin/iconv", "-l");
|
||||
waitForFinish();
|
||||
PIString str(readOutput());
|
||||
str.cutLeft(str.find("\n "));
|
||||
str.replaceAll("\n", "");
|
||||
return str.split("//");
|
||||
}
|
||||
|
||||
|
||||
PIByteArray PICodec::exec_iconv(const PIString & from, const PIString & to, const PIByteArray & str) {
|
||||
tf.open();
|
||||
tf.clear();
|
||||
tf << str;
|
||||
tf.close();
|
||||
exec("/usr/bin/iconv", ("-f=" + from), ("-t=" + to), tf.path());
|
||||
waitForFinish();
|
||||
return readOutput();
|
||||
}
|
||||
51
src/system/picodec.h
Executable file
51
src/system/picodec.h
Executable file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Text codings coder, based on "iconv"
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PICODEC_H
|
||||
#define PICODEC_H
|
||||
|
||||
#include "piprocess.h"
|
||||
|
||||
class PIP_EXPORT PICodec: private PIProcess
|
||||
{
|
||||
PIOBJECT(PICodec)
|
||||
public:
|
||||
PICodec(): PIProcess() {setGrabOutput(true); tf = PIFile::openTemporary(PIIODevice::ReadWrite); tf.open();}
|
||||
PICodec(const PIString & from, const PIString & to): PIProcess() {setCodings(from, to); tf = PIFile::openTemporary(PIIODevice::ReadWrite);}
|
||||
~PICodec() {tf.remove();}
|
||||
|
||||
void setFromCoding(const PIString & from) {c_from = from;}
|
||||
void setToCoding(const PIString & to) {c_to = to;}
|
||||
void setCodings(const PIString & from, const PIString & to) {c_from = from; c_to = to;}
|
||||
|
||||
PIStringList availableCodecs();
|
||||
PIString encode(PIString & str) {return PIString(exec_iconv(c_from, c_to, str.toByteArray()));}
|
||||
PIString encode(const PIByteArray & str) {return PIString(exec_iconv(c_from, c_to, str));}
|
||||
PIString decode(PIString & str) {return PIString(exec_iconv(c_to, c_from, str.toByteArray()));}
|
||||
PIString decode(const PIByteArray & str) {return PIString(exec_iconv(c_to, c_from, str));}
|
||||
|
||||
private:
|
||||
PIByteArray exec_iconv(const PIString & from, const PIString & to, const PIByteArray & str);
|
||||
|
||||
PIString c_from, c_to;
|
||||
PIFile tf;
|
||||
|
||||
};
|
||||
|
||||
#endif // PICODEC_H
|
||||
1064
src/system/piconsole.cpp
Executable file
1064
src/system/piconsole.cpp
Executable file
@@ -0,0 +1,1064 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Console output/input
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "piconsole.h"
|
||||
#include "pipeer.h"
|
||||
|
||||
|
||||
/** \class PIConsole
|
||||
* \brief Console output class
|
||||
* \details
|
||||
* \section PIConsole_sec0 Synopsis
|
||||
* This class provides output to console with automatic alignment and update.
|
||||
* It supports tabs, keyboard listening, formats and colors.
|
||||
*
|
||||
* \section PIConsole_sec1 Layout
|
||||
* %PIConsole works with variable pointers. You should add your variables with
|
||||
* functions \a addVariable() which receives label name, pointer to variable
|
||||
* and optional column and format. Columns count is dynamically increased if
|
||||
* new column used. E.g. if you add variable to empty tab to column 3, columns
|
||||
* count will be increased to 3, but two firsts columns will be empty. Each column
|
||||
* filled from top to bottom, but you can add just string with function
|
||||
* \a addString() or add empty line with function \a addEmptyLine(). Layout scheme:
|
||||
* \image html piconsole_layout.png
|
||||
*
|
||||
* \section PIConsole_sec2 Keyboard usage
|
||||
* %PIConsole should to be single in application. %PIConsole aggregate PIKbdListener
|
||||
* which grab keyboard and automatic switch tabs by theirs bind keys. If there is no
|
||||
* tab binded to pressed key external function "slot" will be called
|
||||
*
|
||||
**/
|
||||
|
||||
|
||||
extern PIMutex __PICout_mutex__;
|
||||
|
||||
|
||||
PIConsole::PIConsole(bool startNow, KBFunc slot): PIThread() {
|
||||
setName("console");
|
||||
setPriority(piLow);
|
||||
needLockRun(true);
|
||||
ret_func = slot;
|
||||
num_format = systime_format = 0;
|
||||
vid = 0;
|
||||
cur_tab = width = height = pwidth = pheight = max_y = 0;
|
||||
def_align = Nothing;
|
||||
tabs.reserve(16);
|
||||
#ifdef WINDOWS
|
||||
ulcoord.X = 0;
|
||||
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
GetConsoleScreenBufferInfo(hOut, &sbi);
|
||||
dattr = sbi.wAttributes;
|
||||
width = sbi.srWindow.Right - sbi.srWindow.Left;
|
||||
height = sbi.srWindow.Bottom - sbi.srWindow.Top;
|
||||
ulcoord.Y = sbi.srWindow.Top;
|
||||
GetConsoleMode(hOut, &smode);
|
||||
GetConsoleCursorInfo(hOut, &curinfo);
|
||||
#else
|
||||
winsize ws;
|
||||
ioctl(0, TIOCGWINSZ, &ws);
|
||||
width = ws.ws_col;
|
||||
height = ws.ws_row;
|
||||
#endif
|
||||
tabs.reserve(16);
|
||||
addTab("main");
|
||||
listener = new PIKbdListener(key_event, this);
|
||||
peer = 0;
|
||||
server_mode = pause_ = false;
|
||||
state = Disconnected;
|
||||
peer_timer.addDelimiter(20);
|
||||
peer_timer.setName("__S__PIConsole::peer_timer");
|
||||
CONNECT2(void, void * , int, &peer_timer, tickEvent, this, peerTimer);
|
||||
if (startNow) start();
|
||||
}
|
||||
|
||||
|
||||
PIConsole::~PIConsole() {
|
||||
stopPeer();
|
||||
if (isRunning())
|
||||
stop();
|
||||
clearTabs(false);
|
||||
delete listener;
|
||||
#ifdef WINDOWS
|
||||
SetConsoleMode(hOut, smode);
|
||||
SetConsoleTextAttribute(hOut, dattr);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int PIConsole::addTab(const PIString & name, char bind_key) {
|
||||
if (isRunning()) lock();
|
||||
tabs.push_back(Tab(name, bind_key));
|
||||
cur_tab = tabs.size() - 1;
|
||||
if (isRunning()) unlock();
|
||||
return tabs.size();
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::removeTab(uint index) {
|
||||
if (index >= tabs.size()) return;
|
||||
if (isRunning()) lock();
|
||||
tabs.remove(index);
|
||||
if (cur_tab >= tabs.size()) cur_tab = tabs.size() - 1;
|
||||
if (isRunning()) unlock();
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::removeTab(const PIString & name) {
|
||||
uint index = tabs.size() + 1;
|
||||
for (uint i = 0; i < tabs.size(); ++i) {
|
||||
if (tabs[i].name == name) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
removeTab(index);
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::clearTab(uint index) {
|
||||
if (index >= tabs.size()) return;
|
||||
lock();
|
||||
tabs[index].columns.clear();
|
||||
if (cur_tab == index) {
|
||||
clearScreen();
|
||||
fillLabels();
|
||||
}
|
||||
if (cur_tab >= tabs.size()) cur_tab = tabs.size() - 1;
|
||||
unlock();
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::clearTab(const PIString & name) {
|
||||
uint index = tabs.size() + 1;
|
||||
for (uint i = 0; i < tabs.size(); ++i) {
|
||||
if (tabs[i].name == name) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
clearTab(index);
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::update() {
|
||||
lock();
|
||||
fillLabels();
|
||||
unlock();
|
||||
}
|
||||
|
||||
|
||||
bool PIConsole::setTab(uint index) {
|
||||
if (index >= tabs.size())
|
||||
return false;
|
||||
if (!isRunning()) {
|
||||
cur_tab = index;
|
||||
return true;
|
||||
}
|
||||
lock();
|
||||
__PICout_mutex__.lock();
|
||||
cur_tab = index;
|
||||
clearScreen();
|
||||
fillLabels();
|
||||
__PICout_mutex__.unlock();
|
||||
unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PIConsole::setTab(const PIString & name) {
|
||||
uint index = tabs.size() + 1;
|
||||
for (uint i = 0; i < tabs.size(); ++i) {
|
||||
if (tabs[i].name == name) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return setTab(index);
|
||||
}
|
||||
|
||||
|
||||
bool PIConsole::setTabBindKey(uint index, char bind_key) {
|
||||
if (index >= tabs.size())
|
||||
return false;
|
||||
tabs[index].key = bind_key;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PIConsole::setTabBindKey(const PIString & name, char bind_key) {
|
||||
uint index =tabs.size() + 1;
|
||||
for (uint i = 0; i < tabs.size(); ++i) {
|
||||
if (tabs[i].name == name) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return setTabBindKey(index, bind_key);
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::key_event(char key, void * t) {
|
||||
PIConsole * p = (PIConsole * )t;
|
||||
int ct = p->cur_tab;
|
||||
if (key == char(PIKbdListener::LeftArrow)) {
|
||||
do {
|
||||
ct--;
|
||||
if (ct < 0) return;
|
||||
} while (p->tabs[ct].key == 0);
|
||||
p->setTab(ct);
|
||||
return;
|
||||
}
|
||||
if (key == char(PIKbdListener::RightArrow)) {
|
||||
do {
|
||||
ct++;
|
||||
if (ct >= p->tabs.size_s()) return;
|
||||
} while (p->tabs[ct].key == 0);
|
||||
p->setTab(ct);
|
||||
return;
|
||||
}
|
||||
for (uint i = 0; i < p->tabsCount(); ++i) {
|
||||
if (p->tabs[i].key == key) {
|
||||
p->setTab(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (p->ret_func != 0) p->ret_func(key, t);
|
||||
p->keyPressed(key, t);
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::clearVariables(bool clearScreen) {
|
||||
if (isRunning()) lock();
|
||||
if (clearScreen && isRunning()) {
|
||||
toUpperLeft();
|
||||
clearScreenLower();
|
||||
}
|
||||
columns().clear();
|
||||
if (isRunning()) unlock();
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::stop(bool clear) {
|
||||
PIThread::stop(true);
|
||||
if (clear) clearScreen();
|
||||
moveTo(0, max_y + 4);
|
||||
showCursor();
|
||||
couts(fstr(Normal));
|
||||
#ifdef WINDOWS
|
||||
SetConsoleMode(hOut, smode);
|
||||
SetConsoleTextAttribute(hOut, dattr);
|
||||
#endif
|
||||
fflush(0);
|
||||
}
|
||||
|
||||
|
||||
PIString PIConsole::fstr(FormatFlags f) {
|
||||
num_format = systime_format = 0;
|
||||
if (f[PIConsole::Dec]) num_format = 0;
|
||||
if (f[PIConsole::Hex]) num_format = 1;
|
||||
if (f[PIConsole::Oct]) num_format = 2;
|
||||
if (f[PIConsole::Bin]) num_format = 4;
|
||||
if (f[PIConsole::Scientific]) num_format = 3;
|
||||
if (f[PIConsole::SystemTimeSplit]) systime_format = 0;
|
||||
if (f[PIConsole::SystemTimeSeconds]) systime_format = 1;
|
||||
|
||||
#ifdef WINDOWS
|
||||
WORD attr = 0;
|
||||
|
||||
if (f[PIConsole::Inverse]) {
|
||||
if (f[PIConsole::Red]) attr |= BACKGROUND_RED;
|
||||
if (f[PIConsole::Green]) attr |= BACKGROUND_GREEN;
|
||||
if (f[PIConsole::Blue]) attr |= BACKGROUND_BLUE;
|
||||
if (f[PIConsole::Yellow]) attr |= (BACKGROUND_RED | BACKGROUND_GREEN);
|
||||
if (f[PIConsole::Magenta]) attr |= (BACKGROUND_RED | BACKGROUND_BLUE);
|
||||
if (f[PIConsole::Cyan]) attr |= (BACKGROUND_GREEN | BACKGROUND_BLUE);
|
||||
if (f[PIConsole::White]) attr |= (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE);
|
||||
if (f[PIConsole::BackRed]) attr |= FOREGROUND_RED;
|
||||
if (f[PIConsole::BackGreen]) attr |= FOREGROUND_GREEN;
|
||||
if (f[PIConsole::BackBlue]) attr |= FOREGROUND_BLUE;
|
||||
if (f[PIConsole::BackYellow]) attr |= (FOREGROUND_RED | FOREGROUND_GREEN);
|
||||
if (f[PIConsole::BackMagenta]) attr |= (FOREGROUND_RED | FOREGROUND_BLUE);
|
||||
if (f[PIConsole::BackCyan]) attr |= (FOREGROUND_GREEN | FOREGROUND_BLUE);
|
||||
if (f[PIConsole::BackWhite]) attr |= (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
||||
if ((attr & BACKGROUND_RED) + (attr & BACKGROUND_GREEN) + (attr & BACKGROUND_BLUE) == 0)
|
||||
attr |= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||
} else {
|
||||
if (f[PIConsole::Red]) attr |= FOREGROUND_RED;
|
||||
if (f[PIConsole::Green]) attr |= FOREGROUND_GREEN;
|
||||
if (f[PIConsole::Blue]) attr |= FOREGROUND_BLUE;
|
||||
if (f[PIConsole::Yellow]) attr |= (FOREGROUND_RED | FOREGROUND_GREEN);
|
||||
if (f[PIConsole::Magenta]) attr |= (FOREGROUND_RED | FOREGROUND_BLUE);
|
||||
if (f[PIConsole::Cyan]) attr |= (FOREGROUND_GREEN | FOREGROUND_BLUE);
|
||||
if (f[PIConsole::White]) attr |= (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
||||
if (f[PIConsole::BackRed]) attr |= BACKGROUND_RED;
|
||||
if (f[PIConsole::BackGreen]) attr |= BACKGROUND_GREEN;
|
||||
if (f[PIConsole::BackBlue]) attr |= BACKGROUND_BLUE;
|
||||
if (f[PIConsole::BackYellow]) attr |= (BACKGROUND_RED | BACKGROUND_GREEN);
|
||||
if (f[PIConsole::BackMagenta]) attr |= (BACKGROUND_RED | BACKGROUND_BLUE);
|
||||
if (f[PIConsole::BackCyan]) attr |= (BACKGROUND_GREEN | BACKGROUND_BLUE);
|
||||
if (f[PIConsole::BackWhite]) attr |= (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE);
|
||||
if ((attr & FOREGROUND_RED) + (attr & FOREGROUND_GREEN) + (attr & FOREGROUND_BLUE) == 0)
|
||||
attr |= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||
}
|
||||
if (f[PIConsole::Bold]) attr |= FOREGROUND_INTENSITY;
|
||||
if (f[PIConsole::Underline]) attr |= COMMON_LVB_UNDERSCORE;
|
||||
|
||||
SetConsoleTextAttribute(hOut, attr);
|
||||
return PIString();
|
||||
#else
|
||||
PIString ts("\e[0");
|
||||
|
||||
if (f[PIConsole::Bold]) ts += ";1";
|
||||
if (f[PIConsole::Faint]) ts += ";2";
|
||||
if (f[PIConsole::Italic]) ts += ";3";
|
||||
if (f[PIConsole::Underline]) ts += ";4";
|
||||
if (f[PIConsole::Blink]) ts += ";5";
|
||||
if (f[PIConsole::Inverse]) ts += ";7";
|
||||
|
||||
if (f[PIConsole::Black]) ts += ";30";
|
||||
if (f[PIConsole::Red]) ts += ";31";
|
||||
if (f[PIConsole::Green]) ts += ";32";
|
||||
if (f[PIConsole::Yellow]) ts += ";33";
|
||||
if (f[PIConsole::Blue]) ts += ";34";
|
||||
if (f[PIConsole::Magenta]) ts += ";35";
|
||||
if (f[PIConsole::Cyan]) ts += ";36";
|
||||
if (f[PIConsole::White]) ts += ";37";
|
||||
|
||||
if (f[PIConsole::BackBlack]) ts += ";40";
|
||||
if (f[PIConsole::BackRed]) ts += ";41";
|
||||
if (f[PIConsole::BackGreen]) ts += ";42";
|
||||
if (f[PIConsole::BackYellow]) ts += ";43";
|
||||
if (f[PIConsole::BackBlue]) ts += ";44";
|
||||
if (f[PIConsole::BackMagenta]) ts += ";45";
|
||||
if (f[PIConsole::BackCyan]) ts += ";46";
|
||||
if (f[PIConsole::BackWhite]) ts += ";47";
|
||||
|
||||
return ts + "m";
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
inline int PIConsole::couts(const bool v) {return (v ? printf("true") : printf("false"));}
|
||||
inline int PIConsole::couts(const char v) {return printf("%c", v);}
|
||||
inline int PIConsole::couts(const short v) {
|
||||
switch (num_format) {case (1): return printf("0x%.4hX", v); break; case (2): return printf("%o", v); break; case (4): return printf("%s", toBin(&v, 2)); break; default: return printf("%hd", v); break;}
|
||||
}
|
||||
inline int PIConsole::couts(const int v) {
|
||||
switch (num_format) {case (1): return printf("0x%.8X", v); break; case (2): return printf("%o", v); break; case (4): return printf("%s", toBin(&v, 4)); break; default: return printf("%d", v); break;}
|
||||
}
|
||||
inline int PIConsole::couts(const long v) {
|
||||
switch (num_format) {case (1): return printf("0x%.16lX", v); break; case (2): return printf("%lo", v); break; case (4): return printf("%s", toBin(&v, sizeof(v))); break; default: return printf("%ld", v); break;}
|
||||
}
|
||||
inline int PIConsole::couts(const llong v) {
|
||||
switch (num_format) {case (1): return printf("0x%.16llX", v); break; case (2): return printf("%llo", v); break; case (4): return printf("%s", toBin(&v, sizeof(v))); break; default: return printf("%lld", v); break;}
|
||||
}
|
||||
inline int PIConsole::couts(const uchar v) {
|
||||
switch (num_format) {case (1): return printf("0x%.2X", v); break; case (2): return printf("%o", v); break; case (4): return printf("%s", toBin(&v, 1)); break; default: return printf("%u", v); break;}
|
||||
}
|
||||
inline int PIConsole::couts(const ushort v) {
|
||||
switch (num_format) {case (1): return printf("0x%.4hX", v); break; case (2): return printf("%o", v); break; case (4): return printf("%s", toBin(&v, 2)); break; default: return printf("%hu", v); break;}
|
||||
}
|
||||
inline int PIConsole::couts(const uint v) {
|
||||
switch (num_format) {case (1): return printf("0x%.8X", v); break; case (2): return printf("%o", v); break; case (4): return printf("%s", toBin(&v, 4)); break; default: return printf("%u", v); break;}
|
||||
}
|
||||
inline int PIConsole::couts(const ulong v) {
|
||||
switch (num_format) {case (1): return printf("0x%.16lX", v); break; case (2): return printf("%lo", v); break; case (4): return printf("%s", toBin(&v, sizeof(v))); break; default: return printf("%lu", v); break;}
|
||||
}
|
||||
inline int PIConsole::couts(const ullong v) {
|
||||
switch (num_format) {case (1): return printf("0x%.16llX", v); break; case (2): return printf("%llo", v); break; case (4): return printf("%s", toBin(&v, sizeof(v))); break; default: return printf("%llu", v); break;}
|
||||
}
|
||||
inline int PIConsole::couts(const float v) {
|
||||
switch (num_format) {case (3): return printf("%e", v); break; default: return printf("%.5g", v); break;}
|
||||
}
|
||||
inline int PIConsole::couts(const double v) {
|
||||
switch (num_format) {case (3): return printf("%le", v); break; default: return printf("%.5lg", v); break;}
|
||||
}
|
||||
inline int PIConsole::couts(const PISystemTime & v) {
|
||||
switch (systime_format) {case (1): return printf("%.6lg", v.toSeconds()); break;
|
||||
default: return couts(v.seconds) + printf(" s, ") + couts(v.nanoseconds) + printf(" ns"); break;}
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::begin() {
|
||||
#ifdef WINDOWS
|
||||
SetConsoleMode(hOut, ENABLE_WRAP_AT_EOL_OUTPUT);
|
||||
#endif
|
||||
max_y = 0;
|
||||
__PICout_mutex__.lock();
|
||||
clearScreen();
|
||||
hideCursor();
|
||||
fillLabels();
|
||||
__PICout_mutex__.unlock();
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::run() {
|
||||
if (pause_) return;
|
||||
uint cx, clen = 0;
|
||||
int j;
|
||||
#ifdef WINDOWS
|
||||
GetConsoleScreenBufferInfo(hOut, &sbi);
|
||||
width = sbi.srWindow.Right - sbi.srWindow.Left;
|
||||
height = sbi.srWindow.Bottom - sbi.srWindow.Top;
|
||||
#else
|
||||
winsize ws;
|
||||
ioctl(0, TIOCGWINSZ, &ws);
|
||||
width = ws.ws_col;
|
||||
height = ws.ws_row;
|
||||
#endif
|
||||
//fflush(0); return;
|
||||
__PICout_mutex__.lock();
|
||||
if (pwidth != width || pheight != height) {
|
||||
clearScreen();
|
||||
fillLabels();
|
||||
}
|
||||
pwidth = width;
|
||||
pheight = height;
|
||||
col_cnt = columns().size();
|
||||
col_wid = (col_cnt > 0) ? width / col_cnt : width;
|
||||
for (uint i = 0; i < col_cnt; ++i) {
|
||||
PIVector<Variable> & cvars(tabs[cur_tab].columns[i].variables);
|
||||
cx = col_wid * i;
|
||||
toUpperLeft();
|
||||
if (max_y < cvars.size()) max_y = cvars.size();
|
||||
j = 0;
|
||||
piForeachC (Variable & tv, cvars) {
|
||||
if (j > height - 3) continue;
|
||||
j++;
|
||||
moveRight(cx);
|
||||
if (tv.type == 15) {
|
||||
newLine();
|
||||
continue;
|
||||
}
|
||||
moveRight(tv.offset);
|
||||
const void * ptr = 0;
|
||||
if (tv.remote) {
|
||||
if (tv.type == 0) {
|
||||
rstr.clear();
|
||||
rba = tv.rdata;
|
||||
rba >> rstr;
|
||||
rstr.trim();
|
||||
ptr = &rstr;
|
||||
} else
|
||||
ptr = tv.rdata.data();
|
||||
} else
|
||||
ptr = tv.ptr;
|
||||
switch (tv.type) {
|
||||
case 0: clen = printValue(ptr != 0 ? *(const PIString*)ptr : PIString(), tv.format); break;
|
||||
case 1: clen = printValue(ptr != 0 ? *(const bool*)ptr : false, tv.format); break;
|
||||
case 2: clen = printValue(ptr != 0 ? *(const int*)ptr : 0, tv.format); break;
|
||||
case 3: clen = printValue(ptr != 0 ? *(const long*)ptr : 0l, tv.format); break;
|
||||
case 4: clen = printValue(ptr != 0 ? *(const char*)ptr : char(0), tv.format); break;
|
||||
case 5: clen = printValue(ptr != 0 ? *(const float*)ptr : 0.f, tv.format); break;
|
||||
case 6: clen = printValue(ptr != 0 ? *(const double*)ptr : 0., tv.format); break;
|
||||
case 7: clen = printValue(ptr != 0 ? *(const short*)ptr : short(0), tv.format); break;
|
||||
case 8: clen = printValue(ptr != 0 ? *(const uint*)ptr : 0u, tv.format); break;
|
||||
case 9: clen = printValue(ptr != 0 ? *(const ulong*)ptr : 0ul, tv.format); break;
|
||||
case 10: clen = printValue(ptr != 0 ? *(const ushort*)ptr : ushort(0), tv.format); break;
|
||||
case 11: clen = printValue(ptr != 0 ? *(const uchar*)ptr : uchar(0), tv.format); break;
|
||||
case 12: clen = printValue(ptr != 0 ? *(const llong*)ptr : 0l, tv.format); break;
|
||||
case 13: clen = printValue(ptr != 0 ? *(const ullong*)ptr: 0ull, tv.format); break;
|
||||
case 20: clen = printValue(ptr != 0 ? *(const PISystemTime*)ptr: PISystemTime(), tv.format); break;
|
||||
case 14: clen = printValue(bitsValue(ptr, tv.bitFrom, tv.bitCount), tv.format); break;
|
||||
}
|
||||
if (clen + tv.offset < (uint)col_wid) {
|
||||
PIString ts = PIString(
|
||||
#if defined(QNX) || defined(FREE_BSD)
|
||||
col_wid - clen - tv.offset - 1, ' ');
|
||||
#else
|
||||
col_wid - clen - tv.offset, ' ');
|
||||
#endif
|
||||
printf("%s", ts.data());
|
||||
}
|
||||
newLine();
|
||||
}
|
||||
}
|
||||
#ifdef WINDOWS
|
||||
moveTo(0, max_y + 1);
|
||||
#else
|
||||
moveTo(0, max_y + 2);
|
||||
#endif
|
||||
fflush(0);
|
||||
__PICout_mutex__.unlock();
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::fillLabels() {
|
||||
if (!isRunning()) return;
|
||||
uint cx, cy, mx = 0, dx;
|
||||
#ifdef WINDOWS
|
||||
GetConsoleScreenBufferInfo(hOut, &sbi);
|
||||
width = sbi.srWindow.Right - sbi.srWindow.Left;
|
||||
height = sbi.srWindow.Bottom - sbi.srWindow.Top;
|
||||
#else
|
||||
winsize ws;
|
||||
ioctl(0, TIOCGWINSZ, &ws);
|
||||
width = ws.ws_col;
|
||||
height = ws.ws_row;
|
||||
#endif
|
||||
max_y = 0;
|
||||
col_cnt = columns().size();
|
||||
col_wid = (col_cnt > 0) ? width / col_cnt : width;
|
||||
for (uint i = 0; i < col_cnt; ++i) {
|
||||
Column & ccol(tabs[cur_tab].columns[i]);
|
||||
PIVector<Variable> & cvars(ccol.variables);
|
||||
if (ccol.alignment != Nothing) {
|
||||
mx = 0;
|
||||
piForeachC (Variable & j, cvars)
|
||||
if (!j.isEmpty())
|
||||
if (mx < j.name.size())
|
||||
mx = j.name.size();
|
||||
mx += 2;
|
||||
}
|
||||
cx = col_wid * i;
|
||||
cy = 1;
|
||||
toUpperLeft();
|
||||
for (uint j = 0; j < cvars.size(); ++j) {
|
||||
if (int(j) > height - 3) continue;
|
||||
if (max_y < j) max_y = j;
|
||||
moveRight(cx);
|
||||
Variable & tv(cvars[j]);
|
||||
cvars[j].nx = cx;
|
||||
cvars[j].ny = cy;
|
||||
if (tv.name.isEmpty()) {
|
||||
cvars[j].offset = 0;
|
||||
clearLine();
|
||||
newLine();
|
||||
cy++;
|
||||
continue;
|
||||
}
|
||||
clearLine();
|
||||
//piCout << tv.name << tv.type << tv.ptr;
|
||||
if (tv.type == 15) {
|
||||
cvars[j].offset = cvars[j].name.length();
|
||||
cvars[j].nx += cvars[j].offset;
|
||||
printLine(tv.name, cx, tv.format);
|
||||
newLine();
|
||||
cy++;
|
||||
continue;
|
||||
}
|
||||
if (!tv.isEmpty()) {
|
||||
switch (ccol.alignment) {
|
||||
case Nothing:
|
||||
cvars[j].offset = (tv.name + ": ").length();
|
||||
cvars[j].nx += cvars[j].offset;
|
||||
printValue(tv.name + ": ", tv.format);
|
||||
break;
|
||||
case Left:
|
||||
cvars[j].offset = mx;
|
||||
cvars[j].nx += cvars[j].offset;
|
||||
printValue(tv.name + ": ", tv.format);
|
||||
break;
|
||||
case Right:
|
||||
cvars[j].offset = mx;
|
||||
cvars[j].nx += cvars[j].offset;
|
||||
dx = mx - (tv.name + ": ").length();
|
||||
moveRight(dx);
|
||||
printValue(tv.name + ": ", tv.format);
|
||||
moveLeft(dx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
newLine();
|
||||
cy++;
|
||||
}
|
||||
}
|
||||
#ifdef WINDOWS
|
||||
moveTo(0, max_y + 1);
|
||||
#else
|
||||
moveTo(0, max_y + 2);
|
||||
#endif
|
||||
if (!tabs[cur_tab].status.isEmpty()) {
|
||||
printValue(tabs[cur_tab].status);
|
||||
newLine();
|
||||
}
|
||||
status();
|
||||
//couts(fstr(Normal));
|
||||
//fflush(0);
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::status() {
|
||||
Tab * ctab;
|
||||
//clearLine();
|
||||
for (uint i = 0; i < tabsCount(); ++i) {
|
||||
ctab = &tabs[i];
|
||||
if (ctab->key == 0) continue;
|
||||
printValue(ctab->key, PIConsole::White | PIConsole::Bold);
|
||||
if (i == cur_tab)
|
||||
printValue(ctab->name + " ", PIConsole::BackYellow | PIConsole::Black);
|
||||
else
|
||||
printValue(ctab->name + " ", PIConsole::Cyan | PIConsole::Inverse);
|
||||
printValue(" ");
|
||||
}
|
||||
newLine();
|
||||
}
|
||||
|
||||
|
||||
int PIConsole::bitsValue(const void * src, int offset, int count) const {
|
||||
int ret = 0, stbyte = offset / 8, cbit = offset - stbyte * 8;
|
||||
char cbyte = reinterpret_cast<const char * >(src)[stbyte];
|
||||
for (int i = 0; i < count; i++) {
|
||||
ret |= ((cbyte >> cbit & 1) << i);
|
||||
cbit++;
|
||||
if (cbit == 8) {
|
||||
cbit = 0;
|
||||
stbyte++;
|
||||
cbyte = reinterpret_cast<const char * >(src)[stbyte];
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
const char * PIConsole::toBin(const void * d, int s) {
|
||||
binstr.clear();
|
||||
uchar cc, b;
|
||||
for (int i = 0; i < s; ++i) {
|
||||
cc = ((const uchar *)d)[i];
|
||||
b = 1;
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
binstr << (cc & b ? "1" : "0");
|
||||
b <<= 1;
|
||||
}
|
||||
if (i < s - 1) binstr << " ";
|
||||
}
|
||||
binstr.reverse();
|
||||
return binstr.data();
|
||||
}
|
||||
|
||||
|
||||
#define ADD_VAR_BODY vid++; tv.id = vid; tv.name = name; tv.bitFrom = tv.bitCount = 0; tv.format = format; tv.remote = false; checkColumn(col);
|
||||
|
||||
void PIConsole::addString(const PIString & name, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 15; tv.size = 0; tv.ptr = 0; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const PIString * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 0; tv.size = 0; tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const bool * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 1; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const int * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 2; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const long * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 3; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const char * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 4; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const float * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 5; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const double * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 6; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const short * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 7; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const uint * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 8; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const ulong * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 9; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const ushort * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 10; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const uchar * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 11; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const llong * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 12; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const ullong * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 13; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const PISystemTime * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 20; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
/** \brief Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
* \details This function add to column "column" next lines:
|
||||
* * "protocol <name>"
|
||||
* * "Rec - receiverDeviceName": \a PIProtocol::receiverDeviceState
|
||||
* * "Send - senderDeviceName": \a PIProtocol::senderDeviceState
|
||||
* * "Received count": \a PIProtocol::receiveCount
|
||||
* * "Invalid count": \a PIProtocol::wrongCount
|
||||
* * "Missed count": \a PIProtocol::missedCount
|
||||
* * "Sended count": \a PIProtocol::sendCount
|
||||
* * "Immediate Frequency, Hz": \a PIProtocol::immediateFrequency
|
||||
* * "Integral Frequency, Hz": \a PIProtocol::integralFrequency
|
||||
* * "Receive speed": \a PIProtocol::receiveSpeed
|
||||
* * "Send speed": \a PIProtocol::sendSpeed
|
||||
* * "Receiver history size": \a PIProtocol::receiverHistorySize
|
||||
* * "Sender history size": \a PIProtocol::senderHistorySize
|
||||
* * "Disconnect Timeout, s": \a PIProtocol::disconnectTimeout
|
||||
* * "Quality": \a PIProtocol::quality
|
||||
* */
|
||||
void PIConsole::addVariable(const PIString & name, const PIProtocol * ptr, int col, FormatFlags format) {
|
||||
addString("protocol " + name, col, format | PIConsole::Bold);
|
||||
addVariable("Rec - " + ptr->receiverDeviceName(), ptr->receiverDeviceState_ptr(), col, format);
|
||||
addVariable("Send - " + ptr->senderDeviceName(), ptr->senderDeviceState_ptr(), col, format);
|
||||
addVariable("Received count", ptr->receiveCount_ptr(), col, format);
|
||||
addVariable("Invalid count", ptr->wrongCount_ptr(), col, format);
|
||||
addVariable("Missed count", ptr->missedCount_ptr(), col, format);
|
||||
addVariable("Sended count", ptr->sendCount_ptr(), col, format);
|
||||
addVariable("Immediate Frequency, Hz", ptr->immediateFrequency_ptr(), col, format);
|
||||
addVariable("Integral Frequency, Hz", ptr->integralFrequency_ptr(), col, format);
|
||||
addVariable("Receive speed", ptr->receiveSpeed_ptr(), col, format);
|
||||
addVariable("Send speed", ptr->sendSpeed_ptr(), col, format);
|
||||
addVariable("Receiver history size", ptr->receiverHistorySize_ptr(), col, format);
|
||||
addVariable("Sender history size", ptr->senderHistorySize_ptr(), col, format);
|
||||
addVariable("Disconnect Timeout, s", ptr->disconnectTimeout_ptr(), col, format);
|
||||
addVariable("Quality", ptr->quality_ptr(), col, format);
|
||||
}
|
||||
/** \brief Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
* \details This function add to column "column" next lines:
|
||||
* * "<name> diagnostics"
|
||||
* * "Received count": \a PIDiagnostics::receiveCount
|
||||
* * "Invalid count": \a PIDiagnostics::wrongCount
|
||||
* * "Sended count": \a PIDiagnostics::sendCount
|
||||
* * "Immediate Frequency, Hz": \a PIDiagnostics::immediateFrequency
|
||||
* * "Integral Frequency, Hz": \a PIDiagnostics::integralFrequency
|
||||
* * "Receive speed": \a PIDiagnostics::receiveSpeed
|
||||
* * "Send speed": \a PIDiagnostics::sendSpeed
|
||||
* * "Quality": \a PIDiagnostics::quality
|
||||
* */
|
||||
void PIConsole::addVariable(const PIString & name, const PIDiagnostics * ptr, int col, FormatFlags format) {
|
||||
addString(name + " diagnostics", col, format | PIConsole::Bold);
|
||||
addVariable("Received count", ptr->receiveCount_ptr(), col, format);
|
||||
addVariable("Invalid count", ptr->wrongCount_ptr(), col, format);
|
||||
addVariable("Sended count", ptr->sendCount_ptr(), col, format);
|
||||
addVariable("Immediate Frequency, Hz", ptr->immediateFrequency_ptr(), col, format);
|
||||
addVariable("Integral Frequency, Hz", ptr->integralFrequency_ptr(), col, format);
|
||||
addVariable("Receive speed", ptr->receiveSpeed_ptr(), col, format);
|
||||
addVariable("Send speed", ptr->sendSpeed_ptr(), col, format);
|
||||
addVariable("Quality", ptr->quality_ptr(), col, format);
|
||||
}
|
||||
void PIConsole::addVariable(const PIString & name, const PISystemMonitor * ptr, int col, FormatFlags format) {
|
||||
addString("monitor " + name, col, format | PIConsole::Bold);
|
||||
addVariable("PID", &(ptr->statistic().ID), col, format);
|
||||
//addVariable("state", &(ptr->statistic().state), col, format);
|
||||
addVariable("threads", &(ptr->statistic().threads), col, format);
|
||||
addVariable("priority", &(ptr->statistic().priority), col, format);
|
||||
addVariable("memory physical", &(ptr->statistic().physical_memsize_readable), col, format);
|
||||
addVariable("memory shared", &(ptr->statistic().share_memsize_readable), col, format);
|
||||
addVariable("cpu load kernel", &(ptr->statistic().cpu_load_system), col, format);
|
||||
addVariable("cpu load user", &(ptr->statistic().cpu_load_user), col, format);
|
||||
}
|
||||
void PIConsole::addBitVariable(const PIString & name, const void * ptr, int fromBit, int bitCount, int col, FormatFlags format) {
|
||||
vid++; tv.id = vid; tv.size = sizeof(ullong); tv.name = name; tv.bitFrom = fromBit; tv.bitCount = bitCount; tv.type = 14; tv.ptr = ptr; tv.format = format;
|
||||
checkColumn(col); column(col).push_back(tv);}
|
||||
void PIConsole::addEmptyLine(int col, uint count) {
|
||||
tv.id = 0; tv.size = 0; tv.name = ""; tv.type = 0; tv.ptr = 0; tv.format = Normal;
|
||||
for (uint i = 0; i < count; ++i) {
|
||||
checkColumn(col);
|
||||
column(col).push_back(tv);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PIString PIConsole::getString(int x, int y) {
|
||||
bool run = isRunning();
|
||||
if (run) PIThread::stop(true);
|
||||
listener->setActive(false);
|
||||
msleep(50);
|
||||
#ifdef WINDOWS
|
||||
moveTo(x - 1, y - 1);
|
||||
#else
|
||||
moveTo(x, y);
|
||||
#endif
|
||||
showCursor();
|
||||
PIByteArray ba(4096);
|
||||
#ifdef CC_VC
|
||||
int ret = scanf_s(" %s", ba.data());
|
||||
#else
|
||||
int ret = scanf(" %s", ba.data());
|
||||
#endif
|
||||
listener->setActive(true);
|
||||
if (run) start();
|
||||
if (ret >= 1) return PIString(ba);
|
||||
else return PIString();
|
||||
}
|
||||
|
||||
|
||||
PIString PIConsole::getString(const PIString & name) {
|
||||
piForeachC (Column & i, tabs[cur_tab].columns)
|
||||
piForeachC (Variable & j, i.variables)
|
||||
if (j.name == name)
|
||||
return getString(j.nx + 1, j.ny);
|
||||
return PIString();
|
||||
}
|
||||
|
||||
|
||||
#define PRINT_VAR_BODY couts(fstr(format)); int ret = couts(value); couts(fstr(PIConsole::Dec)); return ret;
|
||||
|
||||
inline void PIConsole::printLine(const PIString & value, int dx, FormatFlags format) {
|
||||
int i = width - value.length() - dx;
|
||||
#if defined(QNX) || defined(FREE_BSD)
|
||||
--i;
|
||||
#endif
|
||||
PIString ts = fstr(format);
|
||||
couts(ts);
|
||||
if (i >= 0) ts = value + PIString(i, ' ');
|
||||
else ts = value.left(value.size() + i);
|
||||
couts(ts);
|
||||
couts(fstr(Dec));
|
||||
}
|
||||
inline int PIConsole::printValue(const PIString & value, FormatFlags format) {
|
||||
couts(fstr(format));
|
||||
int ret = couts(value);
|
||||
fstr(PIConsole::Dec);
|
||||
return ret;
|
||||
}
|
||||
inline int PIConsole::printValue(const char * value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const bool value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const int value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const long value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const llong value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const float value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const double value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const char value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const short value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const uchar value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const ushort value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const uint value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const ulong value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const ullong value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const PISystemTime & value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
|
||||
|
||||
|
||||
void PIConsole::startServer(const PIString & name) {
|
||||
stopPeer();
|
||||
server_mode = true;
|
||||
peer = new PIPeer("_rcs_:" + name);
|
||||
CONNECT2(void, const PIString & , const PIByteArray &, peer, dataReceivedEvent, this, peerReceived);
|
||||
CONNECT1(void, const PIString & , peer, peerDisconnectedEvent, this, peerDisconnectedEvent);
|
||||
peer_timer.start(50.);
|
||||
serverSendInfo();
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::stopPeer() {
|
||||
remote_clients.clear();
|
||||
peer_timer.stop();
|
||||
if (peer != 0) delete peer;
|
||||
peer = 0;
|
||||
state = Disconnected;
|
||||
}
|
||||
|
||||
|
||||
PIStringList PIConsole::clients() const {
|
||||
PIStringList sl;
|
||||
if (peer == 0) return sl;
|
||||
piForeachC (PIPeer::PeerInfo & i, peer->allPeers()) {
|
||||
if (i.name.left(6) != "_rcc_:") continue;
|
||||
sl << i.name.right(i.name.length() - 6);
|
||||
}
|
||||
return sl;
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::listenServers() {
|
||||
stopPeer();
|
||||
server_mode = false;
|
||||
server_name.clear();
|
||||
srand(PISystemTime::current().nanoseconds);
|
||||
peer = new PIPeer("_rcc_:" + PIDateTime::current().toString("hhmmssddMMyy_") + PIString::fromNumber(rand()));
|
||||
CONNECT2(void, const PIString & , const PIByteArray &, peer, dataReceivedEvent, this, peerReceived);
|
||||
peer_timer.start(100.);
|
||||
}
|
||||
|
||||
|
||||
PIStringList PIConsole::availableServers() const {
|
||||
PIStringList sl;
|
||||
if (peer == 0) return sl;
|
||||
piForeachC (PIPeer::PeerInfo & i, peer->allPeers()) {
|
||||
if (i.name.left(6) != "_rcs_:") continue;
|
||||
sl << i.name.right(i.name.length() - 6);
|
||||
}
|
||||
return sl;
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::connectToServer(const PIString & name) {
|
||||
if (peer == 0) listenServers();
|
||||
server_name = name;
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::disconnect() {
|
||||
stopPeer();
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::serverSendInfo() {
|
||||
if (peer == 0) return;
|
||||
PIByteArray ba;
|
||||
ba << int(0xAA);
|
||||
peer->sendToAll(ba);
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::serverSendData() {
|
||||
if (peer == 0) return;
|
||||
PIByteArray ba;
|
||||
PIVector<VariableContent> content;
|
||||
piForeach (Tab & t, tabs)
|
||||
piForeach (Column & c, t.columns)
|
||||
piForeach (Variable & v, c.variables)
|
||||
if (!v.isEmpty() && v.id > 0) {
|
||||
VariableContent vc;
|
||||
vc.id = v.id;
|
||||
v.writeData(vc.rdata);
|
||||
content << vc;
|
||||
}
|
||||
piForeach (RemoteClient & rc, remote_clients) {
|
||||
ba.clear();
|
||||
switch (rc.state) {
|
||||
case FetchingData:
|
||||
ba << int(0xCC) << tabs;
|
||||
//piCout << "server send const data" << rc.name << ba.size_s();
|
||||
break;
|
||||
case Committing:
|
||||
ba << int(0xDD);
|
||||
break;
|
||||
case Connected:
|
||||
ba << int(0xEE) << content;
|
||||
//piCout << "send data" << ba.size();
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
if (!ba.isEmpty())
|
||||
peer->send(rc.name, ba);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PIConsole::RemoteClient & PIConsole::remoteClient(const PIString & fname) {
|
||||
piForeach (RemoteClient & i, remote_clients)
|
||||
if (i.name == fname)
|
||||
return i;
|
||||
remote_clients << RemoteClient(fname);
|
||||
return remote_clients.back();
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::peerReceived(const PIString & from, const PIByteArray & data) {
|
||||
int type;
|
||||
PIByteArray ba(data);
|
||||
ba >> type;
|
||||
//piCout << "rec packet from" << from << "type" << PICoutManipulators::Hex << type;
|
||||
if (server_mode) {
|
||||
if (from.left(5) != "_rcc_") return;
|
||||
//PIString rcn = from.right(from.length() - 6);
|
||||
RemoteClient & rc(remoteClient(from));
|
||||
switch (type) {
|
||||
case 0xBB: // fetch const data request
|
||||
//piCout << "fetch data request from" << from << rc.state;
|
||||
if (rc.state != Connected)
|
||||
rc.state = FetchingData;
|
||||
break;
|
||||
case 0xCC: // const data commit
|
||||
//piCout << "commit from" << from;
|
||||
if (rc.state != Connected)
|
||||
rc.state = Connected;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
} else {
|
||||
PIVector<VariableContent> content;
|
||||
PIMap<int, Variable * > vids;
|
||||
if (from.left(5) != "_rcs_") return;
|
||||
//PIString rcn = from.right(from.length() - 6);
|
||||
switch (type) {
|
||||
case 0xAA: // new server
|
||||
//piCout << "new server" << rcn;
|
||||
break;
|
||||
case 0xCC: // const data
|
||||
//piCout << "received const data";
|
||||
state = Committing;
|
||||
ba >> tabs;
|
||||
cur_tab = tabs.isEmpty() ? -1 : 0;
|
||||
piForeach (Tab & t, tabs)
|
||||
piForeach (Column & c, t.columns)
|
||||
piForeach (Variable & v, c.variables)
|
||||
v.remote = true;
|
||||
break;
|
||||
case 0xDD: // const data commit
|
||||
//piCout << "received commit";
|
||||
state = Connected;
|
||||
break;
|
||||
case 0xEE: // dynamic data
|
||||
//piCout << "received data" << ba.size_s();
|
||||
piForeach (Tab & t, tabs)
|
||||
piForeach (Column & c, t.columns)
|
||||
piForeach (Variable & v, c.variables)
|
||||
if (!v.isEmpty() && v.id > 0)
|
||||
vids[v.id] = &v;
|
||||
ba >> content;
|
||||
piForeach (VariableContent & vc, content) {
|
||||
if (vc.id <= 0) continue;
|
||||
Variable * v = vids.at(vc.id);
|
||||
if (v == 0) continue;
|
||||
//piCout << "read" << v->name << vc.rdata.size_s();
|
||||
v->rdata = vc.rdata;
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::peerTimer(void * data, int delim) {
|
||||
if (peer == 0) return;
|
||||
//piCout << "timer" << delim;
|
||||
if (server_mode) {
|
||||
if (delim == 20)
|
||||
serverSendInfo();
|
||||
else
|
||||
serverSendData();
|
||||
} else {
|
||||
if (delim != 1 || server_name.isEmpty()) return;
|
||||
const PIPeer::PeerInfo * p = peer->getPeerByName("_rcs_:" + server_name);
|
||||
if (p == 0) return;
|
||||
PIByteArray ba;
|
||||
switch (state) {
|
||||
case Disconnected:
|
||||
peer_tm.reset();
|
||||
ba << int(0xBB);
|
||||
//piCout << "send to" << server_name << "fetch request disc";
|
||||
peer->send(p, ba);
|
||||
state = FetchingData;
|
||||
break;
|
||||
case FetchingData:
|
||||
if (peer_tm.elapsed_s() < 3.)
|
||||
return;
|
||||
peer_tm.reset();
|
||||
ba << int(0xBB);
|
||||
//piCout << "send to" << server_name << "fetch request fd";
|
||||
peer->send(p, ba);
|
||||
break;
|
||||
case Committing:
|
||||
peer_tm.reset();
|
||||
ba << int(0xCC);
|
||||
//piCout << "send to" << server_name << "committing";
|
||||
state = Connected;
|
||||
peer->send(p, ba);
|
||||
break;
|
||||
default: break;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::peerDisconnectedEvent(const PIString & name) {
|
||||
for (int i = 0; i < remote_clients.size_s(); ++i)
|
||||
if (remote_clients[i].name == name) {
|
||||
remote_clients.remove(i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user