diff --git a/libs/main/pip.h b/libs/main/pip.h index 7b7d5908..3cbfea91 100644 --- a/libs/main/pip.h +++ b/libs/main/pip.h @@ -29,6 +29,7 @@ #include "piiodevicesmodule.h" #include "piioutilsmodule.h" #include "pimathmodule.h" +#include "pistatemachinemodule.h" #include "pisystemmodule.h" #include "pithreadmodule.h" diff --git a/libs/main/state_machine/pistatemachine.cpp b/libs/main/state_machine/pistatemachine.cpp new file mode 100644 index 00000000..eae8a494 --- /dev/null +++ b/libs/main/state_machine/pistatemachine.cpp @@ -0,0 +1,31 @@ +/* + PIP - Platform Independent Primitives + State machine + Ivan Pelipenko peri4ko@yandex.ru, 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 Lesser 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#include "pistatemachine.h" + + +PIStateMachine::PIStateMachine(const PIString & n): PIStateBase(n) { + is_root = true; +} + + +bool PIStateMachine::start() { + setActiveRecursive(false); + return PIStateBase::start(); +} diff --git a/libs/main/state_machine/pistatemachine.h b/libs/main/state_machine/pistatemachine.h new file mode 100644 index 00000000..03f9be76 --- /dev/null +++ b/libs/main/state_machine/pistatemachine.h @@ -0,0 +1,61 @@ +/*! \file pistatemachine.h + * \ingroup StateMachine + * \~\brief + * \~english State machine. + * \~russian Машина состояний. + */ +/* + PIP - Platform Independent Primitives + State machine + Ivan Pelipenko peri4ko@yandex.ru, 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 Lesser 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef pistatemachine_H +#define pistatemachine_H + +#include "pistatemachine_state.h" +#include "pistatemachine_transition.h" + + +//! \ingroup StateMachine +//! \~\brief +//! \~english +//! \~russian +class PIP_EXPORT PIStateMachine: public PIStateBase { +public: + PIStateMachine(const PIString & n = {}); + + bool start(); + + template + bool postEvent(int event_id, Args... args) { + PIVector active_states; + gatherActiveStates(active_states); + for (auto * s: active_states) { + for (auto * t: s->transitions) { + if (t->eventID != event_id) continue; + if (t->testGuard(args...)) { + t->trigger(); + return true; + } + } + } + return false; + } +}; + + +#endif diff --git a/libs/main/state_machine/pistatemachine_base.h b/libs/main/state_machine/pistatemachine_base.h new file mode 100644 index 00000000..e1c8f71f --- /dev/null +++ b/libs/main/state_machine/pistatemachine_base.h @@ -0,0 +1,79 @@ +/*! \file pistatemachine_base.h + * \ingroup StateMachine + * \~\brief + * \~english Some template helpers for PIStateMachine + * \~russian Несколько шаблонов для PIStateMachine + */ +/* + PIP - Platform Independent Primitives + Some template helpers for PIStateMachine + Ivan Pelipenko peri4ko@yandex.ru, 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 Lesser 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef pistatemachine_base_H +#define pistatemachine_base_H + +#include "piconstchars.h" + + +namespace PIStateMachineHelpers { + +template +struct FunctionType { + using Type = void; +}; + +template +struct FunctionType { + using Type = std::function; +}; + +template +typename FunctionType::Type toStdFunction(L const & func) { + return func; +} + +class FunctionBase { +public: + virtual ~FunctionBase() {} + virtual uint formatHash() = 0; +}; + +template +class Function: public FunctionBase { +public: + uint formatHash() override { + static uint ret = PIConstChars(typeid(std::function).name()).hash(); + return ret; + } + std::function func; +}; + +template +FunctionBase * makeFunction(std::function func) { + auto * ret = new Function(); + ret->func = func; + return ret; +} + +} // namespace PIStateMachineHelpers + +class PITransitionBase; +class PIStateBase; +class PIStateMachine; + + +#endif diff --git a/libs/main/state_machine/pistatemachine_state.cpp b/libs/main/state_machine/pistatemachine_state.cpp new file mode 100644 index 00000000..2d80745d --- /dev/null +++ b/libs/main/state_machine/pistatemachine_state.cpp @@ -0,0 +1,154 @@ +/* + PIP - Platform Independent Primitives + State machine + Ivan Pelipenko peri4ko@yandex.ru, 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 Lesser 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#include "pistatemachine_state.h" + +#include "pistatemachine_transition.h" + + +PIStateBase::~PIStateBase() { + piDeleteAll(children); + piDeleteAll(transitions); +} + + +void PIStateBase::addState(PIStateBase * s) { + children << s; + s->parent_state = this; + propagateRoot(root); +} + + +void PIStateBase::addStates(PIVector s) { + children << s; + for (auto * i: s) + i->parent_state = this; + propagateRoot(root); +} + + +void PIStateBase::setInitialState(PIStateBase * s) { + if (children.contains(s)) + initial_state = s; + else + initial_state = nullptr; +} + + +PITransitionBase * PIStateBase::addTransition(PIStateBase * target, int event_id) { + PITransitionBase * ret = new PITransitionBase(this, target, event_id); + ret->root = root; + transitions << ret; + return ret; +} + + +void PIStateBase::print(PIString prefix) { + PICout(PICoutManipulators::AddNewLine) << prefix << "[" << (isActive() ? "*" : " ") << "] " << getName(); + prefix += " "; + for (auto * c: children) + c->print(prefix); +} + + +bool PIStateBase::start() { + if (isAtomic()) { + setActive(true); + return true; + } else { + if (initial_state) { + setActive(true); + return initial_state->start(); + } else + piCout << "error:" << getName() << "no initial state!"; + } + return false; +} + + +void PIStateBase::setActive(bool yes) { + bool prev_active = is_active; + is_active = yes; + if (!prev_active && is_active) onEnter(); + if (prev_active && !is_active) onExit(); +} + + +void PIStateBase::setActiveRecursive(bool yes) { + if (yes) setActive(true); + for (auto * c: children) + c->setActiveRecursive(yes); + if (!yes) setActive(false); +} + + +void PIStateBase::setChildActiveRecursive(bool yes) { + for (auto * c: children) + c->setActiveRecursive(yes); +} + + +void PIStateBase::setChildActive(PIStateBase * s) { + if (isAtomic()) return; + if (!s) s = initial_state; + if (!s) { + piCout << "error:" << getName() << "no initial state!"; + return; + } + for (auto * c: children) + c->setActive(false); + if (active_state) { + if (active_state != s) active_state->setActive(false); + } + active_state = s; + if (active_state) active_state->setActive(true); +} + + +void PIStateBase::propagateRoot(PIStateMachine * r) { + if (is_root) + root = reinterpret_cast(this); + else + root = r; + for (auto * t: transitions) + t->root = root; + for (auto * c: children) + c->propagateRoot(root); +} + + +void PIStateBase::gatherActiveStates(PIVector & output) { + for (auto * c: children) + c->gatherActiveStates(output); + if (is_active) output << this; +} + + +void PIStateBase::gatherPathToMachine(PIVector & output) { + if (isStateMachine()) return; + output << this; + if (parent()) parent()->gatherPathToMachine(output); +} + + +PIVector PIStateBase::pathToMachine() { + PIVector ret; + gatherPathToMachine(ret); + return ret; +} diff --git a/libs/main/state_machine/pistatemachine_state.h b/libs/main/state_machine/pistatemachine_state.h new file mode 100644 index 00000000..6906f464 --- /dev/null +++ b/libs/main/state_machine/pistatemachine_state.h @@ -0,0 +1,126 @@ +/*! \file pistatemachine_state.h + * \ingroup StateMachine + * \~\brief + * \~english State machine node + * \~russian Узел машины состояний + */ +/* + PIP - Platform Independent Primitives + State machine node + Ivan Pelipenko peri4ko@yandex.ru, 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 Lesser 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef pistatemachine_state_H +#define pistatemachine_state_H + +#include "pistatemachine_base.h" +#include "pistring.h" + + +//! \ingroup StateMachine +//! \~\brief +//! \~english +//! \~russian +class PIP_EXPORT PIStateBase { + friend class PIStateMachine; + friend class PITransitionBase; + +public: + PIStateBase(const PIString & n = {}): name_(n) { ; } + virtual ~PIStateBase(); + + virtual void onEnter() {} + virtual void onExit() {} + + PIStateMachine * machine() const { return root; } + PIStateBase * parent() const { return parent_state; } + + void addState(PIStateBase * s); + void addStates(PIVector s); + void setInitialState(PIStateBase * s); + + PITransitionBase * addTransition(PIStateBase * target, int event_id); + + void setParallel(bool yes) { is_parallel = yes; } + + const PIString & getName() const { return name_; } + bool isStateMachine() const { return is_root; } + bool isActive() const { return is_active; } + bool isParallel() const { return is_parallel; } + bool isAtomic() const { return children.isEmpty(); } + bool isCompound() const { return children.isNotEmpty(); } + const PIVector & getChildren() const { return children; } + const PIVector & getTransitions() const { return transitions; } + + void print(PIString prefix = {}); + +protected: + bool start(); + void setActive(bool yes); + void setActiveRecursive(bool yes); + void setChildActiveRecursive(bool yes); + void setChildActive(PIStateBase * s); + void propagateRoot(PIStateMachine * r); + void gatherActiveStates(PIVector & output); + void gatherPathToMachine(PIVector & output); + PIVector pathToMachine(); + + bool is_active = false, is_root = false, is_parallel = false; + PIVector children; + PIVector transitions; + PIStateBase *active_state = nullptr, *initial_state = nullptr, *parent_state = nullptr; + PIStateMachine * root = nullptr; + PIString name_; +}; + + +class PIP_EXPORT PIStateLambda: public PIStateBase { +public: + PIStateLambda(std::function on_enter, std::function on_exit = nullptr, const PIString & n = {}): PIStateBase(n) { + enter = on_enter; + exit = on_exit; + } + void onEnter() override { + if (enter) enter(); + } + void onExit() override { + if (exit) exit(); + } + +private: + std::function enter, exit; +}; + + +class PIP_EXPORT PIStateFinal: public PIStateBase { +public: + PIStateFinal(std::function on_enter, std::function on_exit = nullptr, const PIString & n = {}): PIStateBase(n) { + enter = on_enter; + exit = on_exit; + } + void onEnter() override { + if (enter) enter(); + } + void onExit() override { + if (exit) exit(); + } + +private: + std::function enter, exit; +}; + + +#endif diff --git a/libs/main/state_machine/pistatemachine_transition.cpp b/libs/main/state_machine/pistatemachine_transition.cpp new file mode 100644 index 00000000..5f3445fb --- /dev/null +++ b/libs/main/state_machine/pistatemachine_transition.cpp @@ -0,0 +1,81 @@ +/* + PIP - Platform Independent Primitives + State machine + Ivan Pelipenko peri4ko@yandex.ru, 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 Lesser 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#include "pistatemachine_transition.h" + +#include "pistatemachine_state.h" + + +PITransitionBase::PITransitionBase(PIStateBase * source, PIStateBase * target, int event_id) { + target_state = target; + source_state = source; + eventID = event_id; +} + + +PITransitionBase::~PITransitionBase() { + piDeleteSafety(guard); +} + + +PITransitionBase * PITransitionBase::addAction(std::function a) { + action = a; + return this; +} + + +void PITransitionBase::makeAction() { + if (action) action(); +} + + +void PITransitionBase::trigger() { + if (!target_state || (source_state == target_state)) { + makeAction(); + return; + } + // source_state->setActiveRecursive(false); + auto source_path = source_state->pathToMachine(); + auto target_path = target_state->pathToMachine(); + auto source_target_path = target_path; + source_target_path.reverse(); + // piCout << "source_path" << source_path; + // piCout << "target_path" << target_path; + ssize_t common_count = 0; + for (common_count = 0; common_count < piMini(source_path.size_s(), target_path.size_s()); ++common_count) { + // piCout << "check" << source_path[source_path.size_s() - common_count - 1] << target_path[target_path.size_s() - common_count - + // 1]; + if (source_path[source_path.size_s() - common_count - 1] != target_path[target_path.size_s() - common_count - 1]) { + // piCout << "diff" << common_count; + break; + } + } + // piCout << "common" << common_count; + source_path.remove(source_path.size_s() - common_count, common_count); + target_path.remove(target_path.size_s() - common_count, common_count); + // piCout << "source_path" << source_path; + // piCout << "target_path" << target_path; + source_state->setChildActiveRecursive(false); + for (auto * s: source_path) + s->setActive(false); + makeAction(); + for (auto * s: source_target_path) + s->setActive(true); + if (target_state->isCompound()) target_state->start(); +} diff --git a/libs/main/state_machine/pistatemachine_transition.h b/libs/main/state_machine/pistatemachine_transition.h new file mode 100644 index 00000000..d539fa84 --- /dev/null +++ b/libs/main/state_machine/pistatemachine_transition.h @@ -0,0 +1,84 @@ +/*! \file pistatemachine_transition.h + * \ingroup StateMachine + * \~\brief + * \~english State machine transition + * \~russian Переход машины состояний + */ +/* + PIP - Platform Independent Primitives + State machine transition + Ivan Pelipenko peri4ko@yandex.ru, 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 Lesser 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef pistatemachine_transition_H +#define pistatemachine_transition_H + +#include "pistatemachine_base.h" + + +//! \ingroup StateMachine +//! \~\brief +//! \~english +//! \~russian +class PIP_EXPORT PITransitionBase { + friend class PIStateMachine; + friend class PIStateBase; + +public: + PITransitionBase(PIStateBase * source, PIStateBase * target, int event_id); + ~PITransitionBase(); + + PIStateMachine * machine() const { return root; } + PIStateBase * source() const { return source_state; } + PIStateBase * target() const { return target_state; } + + template + PITransitionBase * addGuard(std::function f) { + static_assert(std::is_same::value, "guard function should return bool!"); + piDeleteSafety(guard); + guard = PIStateMachineHelpers::makeFunction(f); + return this; + } + + template + PITransitionBase * addGuard(L f) { + return addGuard(PIStateMachineHelpers::toStdFunction(f)); + } + + template + bool testGuard(Args... args) { + if (!guard) return true; + if (guard->formatHash() != PIStateMachineHelpers::Function().formatHash()) { + piCout << "invalid arguments format!"; + return false; + } + return reinterpret_cast *>(guard)->func(args...); + } + + PITransitionBase * addAction(std::function a); + void makeAction(); + + void trigger(); + +protected: + int eventID = 0; + PIStateBase *source_state = nullptr, *target_state = nullptr; + PIStateMachine * root = nullptr; + PIStateMachineHelpers::FunctionBase * guard = nullptr; + std::function action; +}; + +#endif diff --git a/libs/main/state_machine/pistatemachinemodule.h b/libs/main/state_machine/pistatemachinemodule.h new file mode 100644 index 00000000..44b62a6d --- /dev/null +++ b/libs/main/state_machine/pistatemachinemodule.h @@ -0,0 +1,56 @@ +/* + PIP - Platform Independent Primitives + Module includes + Ivan Pelipenko peri4ko@yandex.ru, 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 Lesser 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ +//! \defgroup StateMachine StateMachine +//! \~\brief +//! \~english State machine. +//! \~russian Машина состояний. +//! +//! \~\details +//! \~english \section cmake_module_StateMachine Building with CMake +//! \~russian \section cmake_module_StateMachine Сборка с использованием CMake +//! +//! \~\code +//! find_package(PIP REQUIRED) +//! target_link_libraries([target] PIP) +//! \endcode +//! +//! \~english \par Common +//! \~russian \par Общее +//! +//! \~english +//! +//! \~russian +//! +//! \~\authors +//! \~english +//! Ivan Pelipenko peri4ko@yandex.ru; +//! Andrey Bychkov work.a.b@yandex.ru; +//! \~russian +//! Иван Пелипенко peri4ko@yandex.ru; +//! Андрей Бычков work.a.b@yandex.ru; +//! + +#ifndef PISTATEMACHINEMODULE_H +#define PISTATEMACHINEMODULE_H + +#include "pistatemachine.h" +#include "pistatemachine_state.h" +#include "pistatemachine_transition.h" + +#endif diff --git a/main.cpp b/main.cpp index 538bf967..c8349868 100644 --- a/main.cpp +++ b/main.cpp @@ -9,7 +9,80 @@ using namespace PICoutManipulators; + +enum MyEvent { + meVoid, + meInt, + meString, + meIntString, +}; + int main(int argc, char * argv[]) { + bool posted; + PIStateMachine * root = new PIStateMachine("Machine"); + + PIStateLambda * s1 = new PIStateLambda([] { piCout << "+ enter s1"; }, [] { piCout << "- exit s1"; }, "s1"); + PIStateLambda * s2 = new PIStateLambda([] { piCout << "+ enter s2"; }, [] { piCout << "- exit s2"; }, "s2"); + PIStateLambda * s3 = new PIStateLambda([] { piCout << "+ enter s3"; }, [] { piCout << "- exit s3"; }, "s3"); + PIStateLambda * s11 = new PIStateLambda([] { piCout << " + enter s11"; }, [] { piCout << " - exit s11"; }, "s11"); + PIStateLambda * s12 = new PIStateLambda([] { piCout << " + enter s12"; }, [] { piCout << " - exit s12"; }, "s12"); + PIStateLambda * s13 = new PIStateLambda([] { piCout << " + enter s13"; }, [] { piCout << " - exit s13"; }, "s13"); + PIStateLambda * s21 = new PIStateLambda([] { piCout << " + enter s21"; }, [] { piCout << " - exit s21"; }, "s21"); + PIStateLambda * s22 = new PIStateLambda([] { piCout << " + enter s22"; }, [] { piCout << " - exit s22"; }, "s22"); + PIStateLambda * s23 = new PIStateLambda([] { piCout << " + enter s23"; }, [] { piCout << " - exit s23"; }, "s23"); + PIStateLambda * s211 = new PIStateLambda([] { piCout << " + enter s211"; }, [] { piCout << " - exit s211"; }, "s211"); + PIStateLambda * s212 = new PIStateLambda([] { piCout << " + enter s212"; }, [] { piCout << " - exit s212"; }, "s212"); + PIStateLambda * s213 = new PIStateLambda([] { piCout << " + enter s213"; }, [] { piCout << " - exit s213"; }, "s213"); + + root->addStates({s1, s2, s3}); + s1->addStates({s11, s12, s13}); + s2->addStates({s21, s22, s23}); + s21->addStates({s211, s212, s213}); + s1->setInitialState(s11); + s2->setInitialState(s22); + s21->setInitialState(s213); + + s1->addTransition(s213, meVoid)->addAction([] { piCout << "action transition s1 -> s213"; }); + s21->addTransition(s22, meVoid)->addAction([] { piCout << "action transition s21 -> s22"; }); + // s2->addTransition(s3, meInt)->addGuard([](int i) { return i == 1; }); + // s3->addTransition(s1, meIntString)->addGuard([](int i, PIString str) { return i == 2 && str == "hello"; }); + + root->setInitialState(s1); + root->start(); + piCout << "initial\n"; + root->print(); + + // piCout << root->postEvent(meVoid); + // piCout << root->postEvent(meInt, 1); + + piCout << "\npost event"; + posted = root->postEvent(meVoid); + piCout << "posted" << posted << "\n"; + root->print(); + + piCout << "\npost event"; + posted = root->postEvent(meVoid); + piCout << "posted" << posted << "\n"; + root->print(); + + /*root->addStates({s1, s2, s3}); + root->setInitialState(s1); + root->start(); + + piCout << root->postEvent(meVoid); + piCout << ""; + piCout << root->postEvent(meInt, 0.5f); + piCout << ""; + piCout << root->postEvent(meInt, 0); + piCout << ""; + piCout << root->postEvent(meInt, 1); + piCout << ""; + piCout << root->postEvent(meIntString, 2, "hello"); + piCout << ""; + piCout << root->postEvent(meIntString, 2, PIString("hello")); + piCout << "";*/ + delete root; + /*PISerial ser; ser.setSpeed(PISerial::S115200); piCout << ser.open("COM15", PIIODevice::ReadWrite); @@ -62,9 +135,14 @@ int main(int argc, char * argv[]) { test_str("1hz 1hz");*/ // PIPair p = createPIPair(0, ""); - PIVector> pv; - pv << createPIPair(0, PIString()); + /*PIConfig conf("model.conf"); + piCout << "****** config\n" << conf.allLeaves() << "******\n"; + PIValueTree vt = PIValueTreeConversions::fromTextFile("model.conf"); + piCout << "****** tree"; + vt.forEachRecursive( + [](const PIValueTree & v, const PIString & fn) { piCout << fn << "=" << v.value().toString() << "#" << v.comment(); }); + piCout << "******";*/ return 0; }