├── .gitignore ├── Docs ├── imgs │ ├── log.png │ ├── ls.png │ ├── plot.png │ ├── rhio.gif │ ├── set.png │ ├── tree.png │ ├── tune.png │ ├── plot2d.png │ ├── prompt.png │ ├── persist.png │ ├── architecture.png │ └── relative_absolute.png ├── joypad.md ├── getting_started.md ├── how_does_it_works.md ├── binding.md └── api.md ├── wks.yml ├── Shell ├── src │ ├── Client.h │ ├── commands │ │ ├── ClearCommand.h │ │ ├── HelpCommand.h │ │ ├── SyncCommand.h │ │ ├── RepeatCommand.h │ │ ├── CdCommand.h │ │ ├── DelayCommand.h │ │ ├── TuneCommand.h │ │ ├── LsCommand.h │ │ ├── ErrCommand.h │ │ ├── LoadCommand.h │ │ ├── SaveCommand.h │ │ ├── DiffCommand.h │ │ ├── LogCommand.h │ │ ├── WatchCommand.h │ │ ├── SyncCommand.cpp │ │ ├── TreeCommand.h │ │ ├── CatCommand.h │ │ ├── PlotCommand.h │ │ ├── Plot2DCommand.h │ │ ├── Plot3DCommand.h │ │ ├── RemoteCommand.h │ │ ├── ClearCommand.cpp │ │ ├── LogImgCommand.h │ │ ├── ViewCommand.h │ │ ├── LoadCommand.cpp │ │ ├── ErrCommand.cpp │ │ ├── SaveCommand.cpp │ │ ├── TuneCommand.cpp │ │ ├── PadCommand.h │ │ ├── CdCommand.cpp │ │ ├── RepeatCommand.cpp │ │ ├── Command.h │ │ ├── DelayCommand.cpp │ │ ├── RemoteCommand.cpp │ │ ├── LogCommand.cpp │ │ ├── CatCommand.cpp │ │ ├── HelpCommand.cpp │ │ ├── WatchCommand.cpp │ │ ├── DiffCommand.cpp │ │ ├── TreeCommand.cpp │ │ ├── ViewCommand.cpp │ │ ├── PlotCommand.cpp │ │ ├── Plot2DCommand.cpp │ │ ├── Plot3DCommand.cpp │ │ ├── LsCommand.cpp │ │ ├── LogImgCommand.cpp │ │ └── Command.cpp │ ├── Completion.h │ ├── NodePool.h │ ├── CSV.h │ ├── FrameStreamViewer.cpp │ ├── joystick │ │ ├── Joystick.h │ │ └── Joystick.cpp │ ├── Curse.h │ ├── FrameStreamViewer.hpp │ ├── CSV.cpp │ ├── Terminal.h │ ├── utils.h │ ├── NodePool.cpp │ ├── Completion.cpp │ ├── StreamManager.h │ ├── GnuPlot.h │ ├── Terminal.cpp │ ├── main.cpp │ ├── Node.h │ ├── Shell.h │ └── StreamManager.cpp ├── package.xml └── CMakeLists.txt ├── Common ├── src │ ├── Protocol.cpp │ ├── Frame.hpp │ ├── Stream.hpp │ ├── DataBuffer.hpp │ ├── DataBuffer.cpp │ └── Value.hpp ├── CMakeLists.txt └── package.xml ├── scripts └── requirements.sh ├── Client ├── src │ ├── RhIOClient.hpp │ └── ClientSub.hpp ├── CMakeLists.txt └── package.xml ├── Skeleton ├── CMakeLists.txt ├── README.md └── main.cpp ├── Server ├── src │ ├── autostart.cpp │ ├── Stream.cpp │ ├── RhIO.hpp │ ├── Filesystem.hpp │ ├── BaseNode.hpp │ ├── StreamNode.hpp │ ├── CommandNode.hpp │ ├── FrameNode.hpp │ ├── CommandNode.cpp │ ├── Filesystem.cpp │ ├── IONode.hpp │ ├── ServerPub.hpp │ ├── StreamNode.cpp │ ├── RhIO.cpp │ ├── ServerRep.hpp │ └── FrameNode.cpp ├── Examples │ ├── CMakeLists.txt │ ├── pad.json │ ├── exampleFrames.cpp │ ├── exampleIntro.cpp │ ├── exampleStreamsAndCommands.cpp │ └── exampleValues.cpp ├── CMakeLists.txt └── package.xml ├── deps.json ├── CMakeLists.txt ├── Tests ├── testThreadSafeValueUpdate.cpp ├── testServerStress.cpp ├── testPersistSave.cpp ├── testServerRep.cpp ├── testCommands.cpp ├── CMakeLists.txt ├── testServer.cpp ├── testClientSub.cpp ├── testServerPub.cpp ├── testTree.cpp ├── testImgs.cpp ├── testPersistLoad.cpp ├── testThreadSafeTree.cpp └── testBind.cpp ├── LICENSE ├── README.md └── BUILD.bazel /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | **.swp 3 | ZeroMQ 4 | cppzmq 5 | -------------------------------------------------------------------------------- /Docs/imgs/log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rhoban/RhIO/HEAD/Docs/imgs/log.png -------------------------------------------------------------------------------- /Docs/imgs/ls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rhoban/RhIO/HEAD/Docs/imgs/ls.png -------------------------------------------------------------------------------- /Docs/imgs/plot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rhoban/RhIO/HEAD/Docs/imgs/plot.png -------------------------------------------------------------------------------- /Docs/imgs/rhio.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rhoban/RhIO/HEAD/Docs/imgs/rhio.gif -------------------------------------------------------------------------------- /Docs/imgs/set.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rhoban/RhIO/HEAD/Docs/imgs/set.png -------------------------------------------------------------------------------- /Docs/imgs/tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rhoban/RhIO/HEAD/Docs/imgs/tree.png -------------------------------------------------------------------------------- /Docs/imgs/tune.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rhoban/RhIO/HEAD/Docs/imgs/tune.png -------------------------------------------------------------------------------- /Docs/imgs/plot2d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rhoban/RhIO/HEAD/Docs/imgs/plot2d.png -------------------------------------------------------------------------------- /Docs/imgs/prompt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rhoban/RhIO/HEAD/Docs/imgs/prompt.png -------------------------------------------------------------------------------- /Docs/imgs/persist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rhoban/RhIO/HEAD/Docs/imgs/persist.png -------------------------------------------------------------------------------- /Docs/imgs/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rhoban/RhIO/HEAD/Docs/imgs/architecture.png -------------------------------------------------------------------------------- /wks.yml: -------------------------------------------------------------------------------- 1 | 2 | deps: 3 | - rhobandeps/cppzmq 4 | 5 | requirements: 6 | - bash scripts/requirements.sh -------------------------------------------------------------------------------- /Docs/imgs/relative_absolute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rhoban/RhIO/HEAD/Docs/imgs/relative_absolute.png -------------------------------------------------------------------------------- /Shell/src/Client.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace RhIO 4 | { 5 | class Client 6 | { 7 | } 8 | } // namespace RhIO 9 | -------------------------------------------------------------------------------- /Common/src/Protocol.cpp: -------------------------------------------------------------------------------- 1 | #include "Protocol.hpp" 2 | 3 | namespace RhIO 4 | { 5 | unsigned int ServersPortBase = 9998; 6 | } 7 | -------------------------------------------------------------------------------- /scripts/requirements.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Installing apt dependencies 4 | sudo apt install -qqy libjsoncpp-dev libncurses5-dev 5 | -------------------------------------------------------------------------------- /Client/src/RhIOClient.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RHIOCLIENT_HPP 2 | #define RHIOCLIENT_HPP 3 | 4 | #include "ClientReq.hpp" 5 | #include "ClientSub.hpp" 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /Skeleton/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.8) 2 | project (skeleton) 3 | find_package(Deps) 4 | 5 | deps_add_library("rhoban/rhio") 6 | 7 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") 8 | 9 | add_executable (skeleton main.cpp) 10 | target_link_libraries (skeleton ${DEPS_LIBRARIES}) 11 | -------------------------------------------------------------------------------- /Shell/src/commands/ClearCommand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Command.h" 4 | 5 | namespace RhIO 6 | { 7 | class ClearCommand : public Command 8 | { 9 | public: 10 | virtual std::string getName(); 11 | virtual std::string getDesc(); 12 | virtual void process(std::vector args); 13 | }; 14 | } // namespace RhIO 15 | -------------------------------------------------------------------------------- /Shell/src/commands/HelpCommand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Command.h" 4 | 5 | namespace RhIO 6 | { 7 | class HelpCommand : public Command 8 | { 9 | public: 10 | virtual std::string getName(); 11 | virtual std::string getDesc(); 12 | virtual void process(std::vector args); 13 | }; 14 | } // namespace RhIO 15 | -------------------------------------------------------------------------------- /Shell/src/commands/SyncCommand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Command.h" 4 | 5 | namespace RhIO 6 | { 7 | class SyncCommand : public Command 8 | { 9 | public: 10 | virtual std::string getName(); 11 | virtual std::string getDesc(); 12 | virtual void process(std::vector args); 13 | }; 14 | } // namespace RhIO 15 | -------------------------------------------------------------------------------- /Shell/src/commands/RepeatCommand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Command.h" 4 | 5 | namespace RhIO 6 | { 7 | class RepeatCommand : public Command 8 | { 9 | public: 10 | virtual std::string getName(); 11 | virtual std::string getDesc(); 12 | virtual void process(std::vector args); 13 | }; 14 | } // namespace RhIO 15 | -------------------------------------------------------------------------------- /Server/src/autostart.cpp: -------------------------------------------------------------------------------- 1 | #include "RhIO.hpp" 2 | 3 | namespace RhIO 4 | { 5 | /** 6 | * Create a new thread at program start 7 | * for Server reply and another thread for 8 | * Streaming Server 9 | * (GCC specific) 10 | */ 11 | static void __attribute__((constructor)) initThreadServer() 12 | { 13 | start(); 14 | } 15 | 16 | } // namespace RhIO 17 | -------------------------------------------------------------------------------- /Shell/src/commands/CdCommand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Command.h" 4 | 5 | namespace RhIO 6 | { 7 | class CdCommand : public Command 8 | { 9 | public: 10 | virtual std::string getName(); 11 | virtual std::string getDesc(); 12 | virtual std::string getUsage(); 13 | virtual void process(std::vector args); 14 | }; 15 | } // namespace RhIO 16 | -------------------------------------------------------------------------------- /Shell/src/commands/DelayCommand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Command.h" 4 | 5 | namespace RhIO 6 | { 7 | class DelayCommand : public Command 8 | { 9 | public: 10 | virtual std::string getName(); 11 | virtual std::string getDesc(); 12 | virtual std::string getUsage(); 13 | virtual void process(std::vector args); 14 | }; 15 | } // namespace RhIO 16 | -------------------------------------------------------------------------------- /Shell/src/commands/TuneCommand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Command.h" 4 | 5 | namespace RhIO 6 | { 7 | class TuneCommand : public Command 8 | { 9 | public: 10 | virtual std::string getName(); 11 | virtual std::string getDesc(); 12 | virtual std::string getUsage(); 13 | virtual void process(std::vector args); 14 | }; 15 | } // namespace RhIO 16 | -------------------------------------------------------------------------------- /Shell/src/commands/LsCommand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Command.h" 5 | #include 6 | 7 | namespace RhIO 8 | { 9 | class LsCommand : public Command 10 | { 11 | public: 12 | virtual std::string getName(); 13 | virtual std::string getDesc(); 14 | virtual void process(std::vector args); 15 | }; 16 | } // namespace RhIO 17 | -------------------------------------------------------------------------------- /Skeleton/README.md: -------------------------------------------------------------------------------- 1 | # Skeleton 2 | 3 | This is a simple demo that can be used to bootstrap your RhIO application 4 | writing. 5 | 6 | This demo will link against RhIO and expose variables which are time (`test/t`), 7 | amplitude (`test/aplitude`) and a sine wave (`test/s`). 8 | 9 | For more information, read the [Getting started](../Docs/getting_started.md) 10 | documentation. 11 | -------------------------------------------------------------------------------- /Shell/src/commands/ErrCommand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Command.h" 5 | #include 6 | 7 | namespace RhIO 8 | { 9 | class ErrCommand : public Command 10 | { 11 | public: 12 | virtual std::string getName(); 13 | virtual std::string getDesc(); 14 | virtual void process(std::vector args); 15 | }; 16 | } // namespace RhIO 17 | -------------------------------------------------------------------------------- /Shell/src/commands/LoadCommand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Command.h" 5 | #include 6 | 7 | namespace RhIO 8 | { 9 | class Node; 10 | class LoadCommand : public Command 11 | { 12 | public: 13 | virtual std::string getName(); 14 | virtual std::string getDesc(); 15 | virtual void process(std::vector args); 16 | }; 17 | } // namespace RhIO 18 | -------------------------------------------------------------------------------- /Shell/src/commands/SaveCommand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Command.h" 5 | #include 6 | 7 | namespace RhIO 8 | { 9 | class Node; 10 | class SaveCommand : public Command 11 | { 12 | public: 13 | virtual std::string getName(); 14 | virtual std::string getDesc(); 15 | virtual void process(std::vector args); 16 | }; 17 | } // namespace RhIO 18 | -------------------------------------------------------------------------------- /Shell/src/commands/DiffCommand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Command.h" 5 | #include 6 | 7 | namespace RhIO 8 | { 9 | class Node; 10 | class DiffCommand : public Command 11 | { 12 | public: 13 | virtual std::string getName(); 14 | virtual std::string getDesc(); 15 | virtual void process(std::vector args); 16 | 17 | int showDiff(Node* node); 18 | }; 19 | } // namespace RhIO 20 | -------------------------------------------------------------------------------- /Shell/src/commands/LogCommand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Command.h" 4 | 5 | namespace RhIO 6 | { 7 | class CSV; 8 | class NodePool; 9 | class LogCommand : public Command 10 | { 11 | public: 12 | virtual std::string getName(); 13 | virtual std::string getDesc(); 14 | virtual std::string getUsage(); 15 | virtual void process(std::vector args); 16 | void update(CSV* csv, NodePool* pool); 17 | }; 18 | } // namespace RhIO 19 | -------------------------------------------------------------------------------- /Shell/src/commands/WatchCommand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Command.h" 5 | #include 6 | 7 | namespace RhIO 8 | { 9 | class NodePool; 10 | class WatchCommand : public Command 11 | { 12 | public: 13 | virtual std::string getName(); 14 | virtual std::string getDesc(); 15 | virtual void process(std::vector args); 16 | 17 | void update(NodePool* pool); 18 | }; 19 | } // namespace RhIO 20 | -------------------------------------------------------------------------------- /Shell/src/commands/SyncCommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Shell.h" 3 | #include "SyncCommand.h" 4 | 5 | namespace RhIO 6 | { 7 | std::string SyncCommand::getName() 8 | { 9 | return "sync"; 10 | } 11 | 12 | std::string SyncCommand::getDesc() 13 | { 14 | return "Synchronize the local tree with remote one"; 15 | } 16 | 17 | void SyncCommand::process(std::vector args) 18 | { 19 | (void)args; 20 | shell->sync(); 21 | } 22 | } // namespace RhIO 23 | -------------------------------------------------------------------------------- /Shell/src/commands/TreeCommand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Command.h" 5 | #include 6 | 7 | namespace RhIO 8 | { 9 | class TreeCommand : public Command 10 | { 11 | public: 12 | virtual std::string getName(); 13 | virtual std::string getDesc(); 14 | virtual void process(std::vector args); 15 | 16 | void showTree(Node* node, std::string left = "", std::string name = "."); 17 | }; 18 | } // namespace RhIO 19 | -------------------------------------------------------------------------------- /Common/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ### 2 | ### Rhoban Input Output Library: Common part 3 | ### Rhoban 2015 4 | ### 5 | 6 | cmake_minimum_required(VERSION 3.16.3) 7 | project(RhIOCommon) 8 | 9 | #Build Server and Client as shared library 10 | add_library(RhIOCommon SHARED 11 | src/DataBuffer.cpp 12 | src/Protocol.cpp 13 | ) 14 | 15 | target_compile_features(RhIOCommon PRIVATE cxx_std_17) 16 | target_include_directories(RhIOCommon PUBLIC 17 | $ 18 | ) 19 | -------------------------------------------------------------------------------- /Shell/src/Completion.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace RhIO 9 | { 10 | class Completion 11 | { 12 | public: 13 | static std::string getSubstring(std::deque matches); 14 | static std::vector& split(const std::string& s, char delim, std::vector& elems); 15 | static std::vector split(const std::string& s, char delim); 16 | }; 17 | } // namespace RhIO 18 | -------------------------------------------------------------------------------- /Shell/src/commands/CatCommand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Command.h" 4 | 5 | namespace RhIO 6 | { 7 | class CSV; 8 | class NodePool; 9 | class CatCommand : public Command 10 | { 11 | public: 12 | virtual std::string getName(); 13 | virtual std::string getDesc(); 14 | virtual std::string getUsage(); 15 | virtual void process(std::vector args); 16 | void update(std::string name, std::string message); 17 | 18 | protected: 19 | std::ostream* os; 20 | }; 21 | } // namespace RhIO 22 | -------------------------------------------------------------------------------- /Client/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16.3) 2 | project(RhIOClient) 3 | 4 | #Build Server and Client as shared library 5 | add_library(RhIOClient SHARED 6 | src/ClientReq.cpp 7 | src/ClientSub.cpp 8 | ) 9 | 10 | target_include_directories(RhIOClient PUBLIC 11 | ${OpenCV_INCLUDE_DIRS} 12 | $ 13 | ) 14 | target_link_libraries(RhIOClient PUBLIC 15 | ${OpenCV_LIBS} 16 | cppzmq 17 | Threads::Threads 18 | RhIOCommon 19 | ) 20 | -------------------------------------------------------------------------------- /Shell/src/commands/PlotCommand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Command.h" 5 | #include 6 | 7 | namespace RhIO 8 | { 9 | class GnuPlot; 10 | class NodePool; 11 | class PlotCommand : public Command 12 | { 13 | public: 14 | virtual std::string getName(); 15 | virtual std::string getDesc(); 16 | virtual void process(std::vector args); 17 | 18 | void update(GnuPlot* plot, NodePool* pool); 19 | 20 | protected: 21 | bool paused; 22 | }; 23 | } // namespace RhIO 24 | -------------------------------------------------------------------------------- /Common/src/Frame.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RHIO_FRAME_HPP 2 | #define RHIO_FRAME_HPP 3 | 4 | #include 5 | 6 | namespace RhIO 7 | { 8 | /** 9 | * Frame 10 | * 11 | * Structure for frame 12 | * image streaming. 13 | */ 14 | struct Frame 15 | { 16 | /** 17 | * Frame stream name 18 | * and description 19 | */ 20 | std::string name; 21 | std::string comment; 22 | 23 | /** 24 | * Frame stream current watchers count 25 | */ 26 | int countWatchers; 27 | }; 28 | 29 | } // namespace RhIO 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /Shell/src/NodePool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Node.h" 5 | 6 | namespace RhIO 7 | { 8 | class NodePool : public std::vector 9 | { 10 | public: 11 | typedef std::function PoolUpdateHandler; 12 | 13 | NodePool(); 14 | void setCallback(PoolUpdateHandler handler); 15 | void update(); 16 | bool dirty; 17 | long timestamp; 18 | 19 | void draw(bool fullName = false); 20 | 21 | protected: 22 | PoolUpdateHandler handler; 23 | }; 24 | } // namespace RhIO 25 | -------------------------------------------------------------------------------- /Shell/src/commands/Plot2DCommand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Command.h" 5 | #include 6 | 7 | namespace RhIO 8 | { 9 | class GnuPlot; 10 | class NodePool; 11 | class Plot2DCommand : public Command 12 | { 13 | public: 14 | virtual std::string getName(); 15 | virtual std::string getDesc(); 16 | std::string getUsage(); 17 | virtual void process(std::vector args); 18 | 19 | void update(GnuPlot* plot, NodePool* pool); 20 | 21 | protected: 22 | bool paused; 23 | }; 24 | } // namespace RhIO 25 | -------------------------------------------------------------------------------- /Shell/src/commands/Plot3DCommand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Command.h" 5 | #include 6 | 7 | namespace RhIO 8 | { 9 | class GnuPlot; 10 | class NodePool; 11 | class Plot3DCommand : public Command 12 | { 13 | public: 14 | virtual std::string getName(); 15 | virtual std::string getDesc(); 16 | std::string getUsage(); 17 | virtual void process(std::vector args); 18 | 19 | void update(GnuPlot* plot, NodePool* pool); 20 | 21 | protected: 22 | bool paused; 23 | }; 24 | } // namespace RhIO 25 | -------------------------------------------------------------------------------- /Shell/src/commands/RemoteCommand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Command.h" 4 | 5 | namespace RhIO 6 | { 7 | class RemoteCommand : public Command 8 | { 9 | public: 10 | RemoteCommand(std::string fullName, std::string desc); 11 | 12 | virtual std::string getName(); 13 | virtual std::string getDesc(); 14 | virtual std::string getOrigin(); 15 | virtual void process(std::vector args); 16 | 17 | protected: 18 | std::string origin; 19 | std::string name; 20 | std::string fullName; 21 | std::string desc; 22 | }; 23 | } // namespace RhIO 24 | -------------------------------------------------------------------------------- /Server/Examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16.3) 2 | 3 | add_executable(exampleIntro exampleIntro.cpp) 4 | target_link_libraries(exampleIntro RhIO) 5 | 6 | add_executable(exampleValues exampleValues.cpp) 7 | target_link_libraries(exampleValues RhIO) 8 | 9 | add_executable(exampleStreamsAndCommands exampleStreamsAndCommands.cpp) 10 | target_link_libraries(exampleStreamsAndCommands RhIO) 11 | 12 | add_executable(exampleFrames exampleFrames.cpp) 13 | target_link_libraries(exampleFrames RhIO) 14 | 15 | add_executable(exampleBind exampleBind.cpp) 16 | target_link_libraries(exampleBind RhIO) 17 | -------------------------------------------------------------------------------- /Shell/src/CSV.h: -------------------------------------------------------------------------------- 1 | #ifndef _RHOBAN_CSV_H 2 | #define _RHOBAN_CSV_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace RhIO 10 | { 11 | class CSV 12 | { 13 | public: 14 | CSV(std::ostream* os); 15 | 16 | void push(std::string column, double value); 17 | void newLine(); 18 | 19 | protected: 20 | std::ostream* os; 21 | bool header; 22 | std::map columns; 23 | std::map columnIndexes; 24 | std::map values; 25 | 26 | void produceHeader(); 27 | }; 28 | } // namespace RhIO 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /Shell/src/commands/ClearCommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Shell.h" 3 | #include "ClearCommand.h" 4 | #include 5 | 6 | namespace RhIO 7 | { 8 | std::string ClearCommand::getName() 9 | { 10 | return "clear"; 11 | } 12 | 13 | std::string ClearCommand::getDesc() 14 | { 15 | return "Erase the screen"; 16 | } 17 | 18 | void ClearCommand::process(std::vector args) 19 | { 20 | (void)args; 21 | int result = system("clear"); 22 | if (result != 0) 23 | { 24 | std::cerr << "RhIO::ClearCommand:process: Failed to clear" << std::endl; 25 | } 26 | } 27 | } // namespace RhIO 28 | -------------------------------------------------------------------------------- /Docs/joypad.md: -------------------------------------------------------------------------------- 1 | # Using a joypad with the shell 2 | 3 | * pad [GAMEPAD_CONFIG.json]: 4 | If run without argument it will display the buttons of the plugged gamepad (useful to configure a new gamepad) 5 | 6 | If run with a filename it will run the gamepad control. 7 | 8 | Example of configuration files are located in workspace/env/rhio/ (https://github.com/Rhoban/workspace) 9 | 10 | To use the configuration file you should put it into the directory ~/.rhio/ (create it if it doesn't exist) 11 | 12 | example: pad xbox_walk.json (have a look at the content of the file for more explanations) 13 | -------------------------------------------------------------------------------- /Shell/src/FrameStreamViewer.cpp: -------------------------------------------------------------------------------- 1 | #include "FrameStreamViewer.hpp" 2 | 3 | namespace RhIO 4 | { 5 | FrameStreamViewer::FrameStreamViewer(const std::string& name) : _name(name) 6 | { 7 | } 8 | 9 | void FrameStreamViewer::start() 10 | { 11 | cv::namedWindow(_name, cv::WINDOW_NORMAL); 12 | } 13 | 14 | void FrameStreamViewer::stop() 15 | { 16 | cv::destroyWindow(_name); 17 | } 18 | 19 | void FrameStreamViewer::pushFrame(const cv::Mat& frame) 20 | { 21 | cv::resizeWindow(_name, frame.cols, frame.rows); 22 | cv::imshow(_name, frame); 23 | cv::imwrite("/tmp/out.png", frame); 24 | cv::waitKey(1); 25 | } 26 | } // namespace RhIO 27 | -------------------------------------------------------------------------------- /deps.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rhoban/rhio", 3 | "build": [ 4 | "mkdir -p build", 5 | "cd build", 6 | "cmake -DCMAKE_BUILD_TYPE=Release ..", 7 | "make", 8 | "cd ../Shell/", 9 | "mkdir -p build", 10 | "cd build", 11 | "cmake -DCMAKE_BUILD_TYPE=Release ..", 12 | "make" 13 | ], 14 | "binaries": "Shell/build", 15 | "links": { 16 | "server": "build/libRhIO.so", 17 | "client": "build/libRhIOClient.so" 18 | }, 19 | "includes": ".", 20 | "deps": [ 21 | "rhobandeps/libzmq", 22 | "rhobandeps/jsoncpp" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /Shell/src/commands/LogImgCommand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "Command.h" 7 | 8 | namespace RhIO 9 | { 10 | class LogImgCommand : public Command 11 | { 12 | public: 13 | virtual std::string getName(); 14 | virtual std::string getDesc(); 15 | virtual std::string getUsage(); 16 | virtual void process(std::vector args); 17 | void update(std::string name, size_t width, size_t height, unsigned char* data, size_t size); 18 | 19 | protected: 20 | std::map ids; 21 | std::map names; 22 | }; 23 | } // namespace RhIO 24 | -------------------------------------------------------------------------------- /Server/src/Stream.cpp: -------------------------------------------------------------------------------- 1 | #include "Stream.hpp" 2 | #include "RhIO.hpp" 3 | #include "ServerPub.hpp" 4 | 5 | namespace RhIO 6 | { 7 | StreamBuffer::StreamBuffer(const std::string& name) : _streamWatchers(0), _pwd(name) 8 | { 9 | } 10 | 11 | int StreamBuffer::sync() 12 | { 13 | // Publish the stream 14 | if (_streamWatchers > 0) 15 | { 16 | ServerStream->publishStream( 17 | _pwd, str(), 18 | std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()) 19 | .count()); 20 | } 21 | // Clear buffer 22 | str(""); 23 | return 0; 24 | } 25 | 26 | } // namespace RhIO 27 | -------------------------------------------------------------------------------- /Shell/src/commands/ViewCommand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "Command.h" 6 | #include "FrameStreamViewer.hpp" 7 | 8 | namespace RhIO 9 | { 10 | class CSV; 11 | class NodePool; 12 | class ViewCommand : public Command 13 | { 14 | public: 15 | virtual std::string getName(); 16 | virtual std::string getDesc(); 17 | virtual std::string getUsage(); 18 | virtual void process(std::vector args); 19 | void update(std::string name, const cv::Mat& frame); 20 | 21 | protected: 22 | std::ostream* os; 23 | 24 | std::vector> _viewers; 25 | }; 26 | } // namespace RhIO 27 | -------------------------------------------------------------------------------- /Shell/src/commands/LoadCommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "Shell.h" 4 | #include "LoadCommand.h" 5 | #include 6 | #include 7 | 8 | namespace RhIO 9 | { 10 | std::string LoadCommand::getName() 11 | { 12 | return "load"; 13 | } 14 | 15 | std::string LoadCommand::getDesc() 16 | { 17 | return "Load the values"; 18 | } 19 | 20 | void LoadCommand::process(std::vector args) 21 | { 22 | auto node = getNode(args); 23 | std::string target = "rhio"; 24 | auto name = node->getPath(); 25 | if (name != "") 26 | { 27 | target += "/" + name; 28 | } 29 | 30 | shell->getClient()->load(node->getPath(), target); 31 | shell->sync(); 32 | } 33 | } // namespace RhIO 34 | -------------------------------------------------------------------------------- /Shell/src/commands/ErrCommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "Shell.h" 5 | #include "ErrCommand.h" 6 | #include "Node.h" 7 | #include "NodePool.h" 8 | 9 | namespace RhIO 10 | { 11 | std::string ErrCommand::getName() 12 | { 13 | return "err"; 14 | } 15 | 16 | std::string ErrCommand::getDesc() 17 | { 18 | return "Lists the unread errors"; 19 | } 20 | 21 | void ErrCommand::process(std::vector args) 22 | { 23 | Terminal::setColor("red", true); 24 | 25 | std::vector errors = shell->getUnreadErrors(); 26 | 27 | for (auto& error : errors) 28 | { 29 | std::cout << error << std::endl; 30 | } 31 | 32 | Terminal::clear(); 33 | } 34 | } // namespace RhIO 35 | -------------------------------------------------------------------------------- /Shell/src/commands/SaveCommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "Shell.h" 4 | #include "SaveCommand.h" 5 | #include 6 | #include 7 | 8 | namespace RhIO 9 | { 10 | std::string SaveCommand::getName() 11 | { 12 | return "save"; 13 | } 14 | 15 | std::string SaveCommand::getDesc() 16 | { 17 | return "Save (persist) the values"; 18 | } 19 | 20 | void SaveCommand::process(std::vector args) 21 | { 22 | auto node = getNode(args); 23 | std::string target = "rhio"; 24 | auto name = node->getPath(); 25 | if (name != "") 26 | { 27 | target += "/" + name; 28 | } 29 | 30 | shell->getClient()->save(node->getPath(), target); 31 | shell->sync(); 32 | } 33 | } // namespace RhIO 34 | -------------------------------------------------------------------------------- /Server/src/RhIO.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RHIO_HPP 2 | #define RHIO_HPP 3 | 4 | #include "Protocol.hpp" 5 | #include "IONode.hpp" 6 | #include "Bind.hpp" 7 | 8 | namespace RhIO 9 | { 10 | /** 11 | * Main RhIO global instance 12 | * Root of ParameterNode tree 13 | */ 14 | extern IONode Root; 15 | 16 | /** 17 | * Internal pointer to the instance of 18 | * the publisher server runngin in its thread 19 | */ 20 | class ServerPub; 21 | extern ServerPub* ServerStream; 22 | 23 | /** 24 | * Starts the RhIO server 25 | */ 26 | void start(unsigned int port = ServersPortBase); 27 | void stop(); 28 | /** 29 | * Has RhIO started? 30 | */ 31 | bool started(); 32 | 33 | /** 34 | * Publish an error message 35 | */ 36 | void errorMessage(const std::string& message); 37 | 38 | } // namespace RhIO 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /Shell/src/joystick/Joystick.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define JOYSTICK_DEVNAME "/dev/input/js0" 6 | 7 | #define JS_EVENT_BUTTON 0x01 /* button pressed/released */ 8 | #define JS_EVENT_AXIS 0x02 /* joystick moved */ 9 | #define JS_EVENT_INIT 0x80 /* initial state of device */ 10 | 11 | namespace RhIO 12 | { 13 | class Joystick 14 | { 15 | public: 16 | struct JoystickEvent 17 | { 18 | unsigned int time; 19 | short value; 20 | unsigned char type; 21 | unsigned char number; 22 | 23 | bool isPressed(); 24 | float getValue(); 25 | }; 26 | 27 | Joystick(); 28 | std::string getDeviceName(); 29 | bool open(); 30 | void close(); 31 | bool getEvent(JoystickEvent* evt); 32 | 33 | protected: 34 | int fd; 35 | }; 36 | } // namespace RhIO 37 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16.3) 2 | project(RhIO 3 | DESCRIPTION "Rhoban Input Output Library, Rhoban 2015" 4 | LANGUAGES CXX 5 | ) 6 | 7 | option(RHIO_BUILD_TESTS "Build RhIO Tests" OFF) 8 | option(RHIO_BUILD_EXAMPLES "Build RhIO Examples" OFF) 9 | 10 | find_package(OpenCV REQUIRED) 11 | find_package(Threads REQUIRED) 12 | 13 | #Enable C++17 14 | set(CMAKE_CXX_STANDARD 17) 15 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 16 | set(CMAKE_CXX_EXTENSIONS OFF) 17 | #set(BUILD_SHARED_LIBS ON) 18 | #Enable compiler Warning 19 | add_compile_options(-W -Wall -Wno-reorder) 20 | 21 | add_subdirectory(Common) 22 | add_subdirectory(Server) 23 | add_subdirectory(Client) 24 | add_subdirectory(Shell) 25 | #add_subdirectory(Skeleton) 26 | 27 | if(BUILD_TESTING AND RHIO_BUILD_TESTS) 28 | add_subdirectory(Tests) 29 | endif() 30 | -------------------------------------------------------------------------------- /Shell/src/commands/TuneCommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "Shell.h" 5 | #include "TuneCommand.h" 6 | #include "Curse.h" 7 | 8 | namespace RhIO 9 | { 10 | std::string TuneCommand::getName() 11 | { 12 | return "tune"; 13 | } 14 | 15 | std::string TuneCommand::getDesc() 16 | { 17 | return "Run the tuner"; 18 | } 19 | 20 | std::string TuneCommand::getUsage() 21 | { 22 | return "tune [var1] [var2] [var3]..."; 23 | } 24 | 25 | void TuneCommand::process(std::vector args) 26 | { 27 | Curse curse(this); 28 | curse.values = shell->getPool(args); 29 | if (curse.values.size()) 30 | { 31 | curse.shell = shell; 32 | curse.run(); 33 | } 34 | else 35 | { 36 | throw std::runtime_error("Nothing to tune"); 37 | } 38 | } 39 | } // namespace RhIO 40 | -------------------------------------------------------------------------------- /Tests/testThreadSafeValueUpdate.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "RhIO.hpp" 6 | 7 | void function1() 8 | { 9 | for (size_t i = 0; i < 10000; i++) 10 | { 11 | RhIO::Root.setInt("test/int", RhIO::Root.getInt("test/int") + 1); 12 | } 13 | } 14 | 15 | void function2() 16 | { 17 | for (size_t i = 0; i < 10000; i++) 18 | { 19 | RhIO::Root.setInt("test/int", RhIO::Root.getInt("test/int") - 1); 20 | } 21 | } 22 | 23 | int main() 24 | { 25 | RhIO::Root.newChild("test"); 26 | RhIO::Root.newInt("test/int"); 27 | RhIO::Root.setInt("test/int", 0); 28 | 29 | std::thread t1(function1); 30 | std::thread t2(function2); 31 | 32 | t1.join(); 33 | t2.join(); 34 | 35 | std::cout << RhIO::Root.getInt("test/int") << std::endl; 36 | 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /Shell/src/Curse.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "NodePool.h" 6 | #include "Node.h" 7 | 8 | namespace RhIO 9 | { 10 | class Command; 11 | class Shell; 12 | struct ValueBase; 13 | class Curse 14 | { 15 | public: 16 | Curse(Command* command); 17 | 18 | void run(); 19 | void init(); 20 | void loop(); 21 | void end(); 22 | void draw(int x, int y, std::string s); 23 | 24 | int selected; 25 | int row, col; 26 | int granularity; 27 | int maxSliders, offset; 28 | 29 | void getMinMax(ValueBase* value, float* min, float* max); 30 | void increment(ValueBase* value, int delta); 31 | void bound(ValueBase* value); 32 | 33 | void update(NodePool*); 34 | 35 | Shell* shell; 36 | Command* command; 37 | NodePool values; 38 | 39 | bool streamUpdated; 40 | }; 41 | } // namespace RhIO 42 | -------------------------------------------------------------------------------- /Shell/src/commands/PadCommand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "Command.h" 7 | 8 | #define PAD_AXIS 1 9 | #define PAD_BUTTON 2 10 | #define PAD_TOGGLE 3 11 | #define PAD_INCREMENT 4 12 | #define PAD_DECREMENT 5 13 | #define PAD_COMMAND 6 14 | 15 | namespace RhIO 16 | { 17 | struct PadIO 18 | { 19 | NodeValue node; 20 | std::string param; 21 | std::string command; 22 | int id; 23 | int state; 24 | int type; 25 | int value; 26 | 27 | float min, max; 28 | float step; 29 | float fvalue; 30 | }; 31 | 32 | class PadCommand : public Command 33 | { 34 | public: 35 | virtual std::string getName(); 36 | virtual std::string getDesc(); 37 | virtual void process(std::vector args); 38 | std::map import(std::string name); 39 | void update(PadIO& pad); 40 | }; 41 | } // namespace RhIO 42 | -------------------------------------------------------------------------------- /Shell/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | RhIOShell 4 | 0.0.0 5 | 6 | RhIOShell provide a shell environment to communicate with RhIOServers 7 | 8 | 9 | Quentin Rouxel 10 | Grégoire Passault 11 | 12 | MIT 13 | 14 | https://github.com/Rhoban/RhIO 15 | 16 | Quentin Rouxel 17 | Grégoire Passault 18 | 19 | catkin 20 | 21 | RhIOClient 22 | jsoncpp 23 | 24 | RhIOClient 25 | jsoncpp 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Docs/getting_started.md: -------------------------------------------------------------------------------- 1 | # Getting started 2 | 3 | ## Installing 4 | 5 | RhIO now uses [deps](https://github.com/rhoban/deps), which is a lightweight package 6 | manager used to handle dependencies. 7 | 8 | First, you should [install deps](https://github.com/rhoban/deps#installation), then type: 9 | 10 | deps install rhoban/rhio 11 | 12 | RhIO and its dependencies will be downloaded and installed. 13 | 14 | ## Building the skeleton example 15 | 16 | There is an example in the `Skeleton` directory, you can build it to try RhIO: 17 | 18 | cd deps/packages/rhoban_rhio/Skeleton/ 19 | mkdir build 20 | cd build 21 | cmake .. 22 | make 23 | 24 | And then run the skeleton: 25 | 26 | ./skeleton 27 | 28 | And run RhIO in another shell: 29 | 30 | rhio 31 | 32 | Have a look to the [source of skeleton](/Skeleton) to understand how it works. 33 | -------------------------------------------------------------------------------- /Shell/src/commands/CdCommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "Shell.h" 5 | #include "CdCommand.h" 6 | 7 | namespace RhIO 8 | { 9 | std::string CdCommand::getName() 10 | { 11 | return "cd"; 12 | } 13 | 14 | std::string CdCommand::getDesc() 15 | { 16 | return "Changing the directory"; 17 | } 18 | 19 | std::string CdCommand::getUsage() 20 | { 21 | return "cd [path]"; 22 | } 23 | 24 | void CdCommand::process(std::vector args) 25 | { 26 | if (args.size() == 0) 27 | { 28 | shell->goToPath("/"); 29 | } 30 | else if (args.size() != 1) 31 | { 32 | errorUsage(); 33 | } 34 | else 35 | { 36 | auto target = args.front(); 37 | 38 | if (!shell->goToPath(target)) 39 | { 40 | std::stringstream ss; 41 | ss << "Unknown node: " << target; 42 | throw std::runtime_error(ss.str()); 43 | } 44 | } 45 | } 46 | } // namespace RhIO 47 | -------------------------------------------------------------------------------- /Shell/src/commands/RepeatCommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Shell.h" 3 | #include "RepeatCommand.h" 4 | #include "RemoteCommand.h" 5 | #include 6 | 7 | namespace RhIO 8 | { 9 | std::string RepeatCommand::getName() 10 | { 11 | return "repeat"; 12 | } 13 | 14 | std::string RepeatCommand::getDesc() 15 | { 16 | return "Repeats a command"; 17 | } 18 | 19 | void RepeatCommand::process(std::vector args) 20 | { 21 | std::string command = ""; 22 | for (auto part : args) 23 | { 24 | command += part; 25 | command += " "; 26 | } 27 | 28 | while (!shell->hasInput()) 29 | { 30 | int result = system("clear"); 31 | if (result != 0) 32 | { 33 | std::cerr << "RhIO::ClearCommand:process: Failed to clear" << std::endl; 34 | } 35 | shell->parse(command); 36 | 37 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 38 | } 39 | } 40 | } // namespace RhIO 41 | -------------------------------------------------------------------------------- /Tests/testServerStress.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "RhIO.hpp" 8 | 9 | int values = 0; 10 | 11 | void generate(int depth = 0, std::string prefix = "") 12 | { 13 | if (prefix != "") 14 | { 15 | prefix += "/"; 16 | } 17 | 18 | if (depth < 4) 19 | { 20 | for (int k = 0; k < 9; k++) 21 | { 22 | values++; 23 | std::stringstream ss, ssc; 24 | ss << "float" << k; 25 | RhIO::Root.newFloat(prefix + ss.str()); 26 | 27 | ssc << "child" << k; 28 | std::string name = prefix + ssc.str(); 29 | generate(depth + 1, name); 30 | } 31 | } 32 | } 33 | 34 | int main() 35 | { 36 | generate(); 37 | printf("Generated %d values\n", values); 38 | 39 | while (true) 40 | { 41 | std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 42 | } 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /Docs/how_does_it_works.md: -------------------------------------------------------------------------------- 1 | # How does it works? 2 | 3 | ## Server architecture 4 | 5 | RhIO uses internally two ZMQ servers: one REP/REQ and one PUB/SUB. Basically, the 6 | REQ/REP is used to answer commands and the PUB/SUB is used to subscribe to parameter 7 | changes (think for instance of the ``watch`` or ``plot`` command in the [shell](shell.md)). 8 | 9 | You can notify RhIO that you want a parameter to be streamed to its PUB/SUB server 10 | using a command in the REQ/REP one. In the same way, you can tell it that you no 11 | longer want to receive this value. Internally, RhIO counts how many clients are 12 | interrested in getting a parameter streamed, and stream it if at least one asks 13 | for it. 14 | 15 | ## Protocol 16 | 17 | RhIO is based on a custom binary protocol, that can be found under the repository ``Common/`` 18 | directory. 19 | 20 | ## Internal types 21 | 22 | All floating numbers are internally represented using double precision floating 23 | points, and all ints are 64-bits. 24 | 25 | 26 | -------------------------------------------------------------------------------- /Shell/src/FrameStreamViewer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace RhIO 13 | { 14 | /** 15 | * FrameStreamViewer 16 | * 17 | * Use ffplay to display real time 18 | * frame in raw RGB24 format 19 | */ 20 | class FrameStreamViewer 21 | { 22 | public: 23 | /** 24 | * Initialization with image size 25 | */ 26 | FrameStreamViewer(const std::string& name); 27 | 28 | /** 29 | * Start and stop player instance. 30 | */ 31 | void start(); 32 | void stop(); 33 | 34 | /** 35 | * Display the given image with the Player. 36 | * Raw image data and its size are given. 37 | * Image width and height size are given. 38 | */ 39 | void pushFrame(const cv::Mat& frame); 40 | 41 | private: 42 | /** 43 | * Windows name 44 | */ 45 | std::string _name; 46 | }; 47 | 48 | } // namespace RhIO 49 | -------------------------------------------------------------------------------- /Shell/src/commands/Command.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace RhIO 10 | { 11 | class Node; 12 | class Shell; 13 | class Command 14 | { 15 | public: 16 | virtual ~Command(); 17 | virtual std::string getName() = 0; 18 | virtual std::string getDesc() = 0; 19 | virtual std::string getUsage(); 20 | virtual void die(); 21 | virtual void process(std::vector args) = 0; 22 | 23 | void setShell(Shell* shell_); 24 | void errorUsage(); 25 | 26 | /** 27 | * Get the node corresponding to given args 28 | */ 29 | Node* getNode(std::vector args); 30 | 31 | /** 32 | * Getting the stream where data should be put 33 | */ 34 | std::ostream* getStream(std::vector& args); 35 | void clearStream(); 36 | 37 | bool dead; 38 | 39 | int waitChar(); 40 | 41 | protected: 42 | std::ofstream ofs; 43 | Shell* shell; 44 | }; 45 | } // namespace RhIO 46 | -------------------------------------------------------------------------------- /Shell/src/commands/DelayCommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "Shell.h" 4 | #include "DelayCommand.h" 5 | #include "RemoteCommand.h" 6 | #include 7 | 8 | namespace RhIO 9 | { 10 | std::string DelayCommand::getName() 11 | { 12 | return "delay"; 13 | } 14 | 15 | std::string DelayCommand::getDesc() 16 | { 17 | return "Delays a command"; 18 | } 19 | 20 | std::string DelayCommand::getUsage() 21 | { 22 | return "delay [n secs] [command]"; 23 | } 24 | 25 | void DelayCommand::process(std::vector args) 26 | { 27 | if (args.size() < 2) 28 | { 29 | errorUsage(); 30 | } 31 | else 32 | { 33 | float duration = atof(args[0].c_str()); 34 | std::this_thread::sleep_for(std::chrono::milliseconds((int)(1000 * duration))); 35 | 36 | std::string command = ""; 37 | for (unsigned int k = 1; k < args.size(); k++) 38 | { 39 | command += args[k]; 40 | command += " "; 41 | } 42 | 43 | shell->parse(command); 44 | } 45 | } 46 | } // namespace RhIO 47 | -------------------------------------------------------------------------------- /Shell/src/CSV.cpp: -------------------------------------------------------------------------------- 1 | #include "CSV.h" 2 | 3 | namespace RhIO 4 | { 5 | CSV::CSV(std::ostream* os_) : os(os_) 6 | { 7 | header = false; 8 | } 9 | 10 | void CSV::push(std::string column, double value) 11 | { 12 | if (columns.count(column)) 13 | { 14 | values[columns[column]] = value; 15 | } 16 | else if (!header) 17 | { 18 | int index = columns.size(); 19 | columns[column] = index; 20 | columnIndexes[index] = column; 21 | values[index] = value; 22 | } 23 | } 24 | 25 | void CSV::newLine() 26 | { 27 | if (!header) 28 | { 29 | produceHeader(); 30 | header = true; 31 | } 32 | 33 | for (unsigned int index = 0; index < columns.size(); index++) 34 | { 35 | *os << values[index] << " "; 36 | } 37 | *os << std::endl; 38 | os->flush(); 39 | } 40 | 41 | void CSV::produceHeader() 42 | { 43 | for (unsigned int index = 0; index < columnIndexes.size(); index++) 44 | { 45 | *os << "#" << (index + 1) << ":" << columnIndexes[index] << " "; 46 | } 47 | *os << std::endl; 48 | } 49 | } // namespace RhIO 50 | -------------------------------------------------------------------------------- /Server/src/Filesystem.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RHIO_FILESYSTEM_HPP 2 | #define RHIO_FILESYSTEM_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace RhIO 8 | { 9 | /** 10 | * Return true of given directory path exist 11 | * or false if an error occurs (not exist or not a directory) 12 | */ 13 | bool isDirectory(const std::string& path); 14 | 15 | /** 16 | * List and return all forders name in 17 | * given directory path 18 | * Throw std::runtime_error is given path 19 | * does not exist 20 | */ 21 | std::vector listDirectories(const std::string& path); 22 | 23 | /** 24 | * List and return all regular file name in 25 | * given directory path 26 | * Throw std::runtime_error is given path 27 | * does not exist 28 | */ 29 | std::vector listFiles(const std::string& path); 30 | 31 | /** 32 | * Create a new folder inside given directory path 33 | * Throw std::runtime_error is given path 34 | * does not exist 35 | */ 36 | void createDirectory(const std::string& path, const std::string& name); 37 | 38 | } // namespace RhIO 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /Shell/src/commands/RemoteCommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Shell.h" 3 | #include "RemoteCommand.h" 4 | 5 | namespace RhIO 6 | { 7 | RemoteCommand::RemoteCommand(std::string fullName_, std::string desc_) 8 | : fullName(fullName_), desc(desc_), origin(""), name("") 9 | { 10 | auto found = fullName.find_last_of("/"); 11 | if (found != std::string::npos) 12 | { 13 | origin = fullName.substr(0, found); 14 | name = fullName.substr(found + 1); 15 | } 16 | else 17 | { 18 | name = fullName; 19 | } 20 | } 21 | 22 | std::string RemoteCommand::getName() 23 | { 24 | return name; 25 | } 26 | 27 | std::string RemoteCommand::getDesc() 28 | { 29 | return desc; 30 | } 31 | 32 | std::string RemoteCommand::getOrigin() 33 | { 34 | return origin; 35 | } 36 | 37 | void RemoteCommand::process(std::vector args) 38 | { 39 | auto response = shell->getClient()->call(fullName, args); 40 | std::cout << response; 41 | if (response.size() && response[response.size() - 1] != '\n') 42 | { 43 | std::cout << std::endl; 44 | } 45 | } 46 | } // namespace RhIO 47 | -------------------------------------------------------------------------------- /Common/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | RhIOCommon 4 | 0.0.0 5 | 6 | RhIO is a lightweight library that can be linked against your application 7 | in order to interract with your program on-the-fly, through its integrated server. 8 | 9 | Main feature are parameters that can be exposed in order to be changed, monitored 10 | or persisted to configuration files. 11 | 12 | It also provide a shell client that will provide a bash-like interface to walk 13 | your nodes and parameters, and even trigger methods that are in your code. 14 | 15 | 16 | Quentin Rouxel 17 | Grégoire Passault 18 | 19 | MIT 20 | 21 | https://github.com/Rhoban/RhIO 22 | 23 | Quentin Rouxel 24 | Grégoire Passault 25 | 26 | catkin 27 | 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) <2015> Rhoban Team 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Shell/src/commands/LogCommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Shell.h" 3 | #include "LogCommand.h" 4 | #include "CSV.h" 5 | #include "NodePool.h" 6 | #include "StreamManager.h" 7 | 8 | using namespace std::placeholders; 9 | 10 | namespace RhIO 11 | { 12 | std::string LogCommand::getName() 13 | { 14 | return "log"; 15 | } 16 | 17 | std::string LogCommand::getDesc() 18 | { 19 | return "Log values to a csv file"; 20 | } 21 | 22 | std::string LogCommand::getUsage() 23 | { 24 | return "log [param1 [param2 [param3 ...]]] [> filename]"; 25 | } 26 | 27 | void LogCommand::process(std::vector args) 28 | { 29 | auto output = getStream(args); 30 | NodePool pool = shell->getPool(args); 31 | 32 | CSV csv(output); 33 | pool.setCallback(std::bind(&LogCommand::update, this, &csv, _1)); 34 | shell->streamWait(&pool, this); 35 | clearStream(); 36 | } 37 | 38 | void LogCommand::update(CSV* csv, NodePool* pool) 39 | { 40 | csv->push("timestamp", pool->timestamp); 41 | for (auto val : *pool) 42 | { 43 | csv->push(val.getName(), Node::toNumber(val.value)); 44 | } 45 | csv->newLine(); 46 | } 47 | } // namespace RhIO 48 | -------------------------------------------------------------------------------- /Tests/testPersistSave.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "RhIO.hpp" 4 | #include "Filesystem.hpp" 5 | 6 | int main() 7 | { 8 | RhIO::Root.newChild("test"); 9 | RhIO::Root.newChild("test2/test3"); 10 | 11 | RhIO::Root.newBool("paramBool")->comment("bool parameter")->persisted(true)->defaultValue(true); 12 | RhIO::Root.newInt("test/paramInt")->persisted(true)->minimum(-1)->maximum(1024)->defaultValue(42); 13 | RhIO::Root.newFloat("test/paramFloat")->comment("float parameter")->minimum(0.0)->persisted(true); 14 | RhIO::Root.newStr("test2/test3/paramStr")->comment("str parameter")->persisted(true)->defaultValue("off"); 15 | 16 | RhIO::Root.setFloat("test/paramFloat", 3.14); 17 | assert(RhIO::Root.getValueFloat("test/paramFloat").valuePersisted == 0.0); 18 | RhIO::Root.save("/tmp/testRhIO"); 19 | assert(RhIO::Root.getValueFloat("test/paramFloat").valuePersisted == 3.14); 20 | 21 | RhIO::Root.child("test").save("/tmp/testRhIO/test"); 22 | 23 | RhIO::Root.newChild("test4/test5"); 24 | RhIO::Root.newFloat("test4/test5/paramFloat")->persisted(true)->defaultValue(123); 25 | RhIO::Root.child("test4/test5").save("/tmp/testRhIOTest4/test4/test5"); 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /Shell/src/Terminal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // normal 6 | #define NRM "\x1B[0m" 7 | 8 | //%d for bold 9 | // foreground 10 | #define F_GRE "\x1B[%d;30m" 11 | #define F_RED "\x1B[%d;31m" 12 | #define F_GRN "\x1B[%d;32m" 13 | #define F_YEL "\x1B[%d;33m" 14 | #define F_BLU "\x1B[%d;34m" 15 | #define F_MAG "\x1B[%d;35m" 16 | #define F_CYN "\x1B[%d;36m" 17 | #define F_WHT "\x1B[%d;37m" 18 | 19 | // background 20 | #define B_GRE "\x1B[%d;40m" 21 | #define B_RED "\x1B[%d;41m" 22 | #define B_GRN "\x1B[%d;42m" 23 | #define B_YEL "\x1B[%d;43m" 24 | #define B_BLU "\x1B[%d;44m" 25 | #define B_MAG "\x1B[%d;45m" 26 | #define B_CYN "\x1B[%d;46m" 27 | #define B_WHT "\x1B[%d;47m" 28 | 29 | namespace RhIO 30 | { 31 | class Terminal 32 | { 33 | public: 34 | /** 35 | * Handle format of the terminal 36 | */ 37 | static void clear(); 38 | static void initCursor(); 39 | static void clearScreen(); 40 | static void clearLine(); 41 | static void cursorRight(); 42 | static void cursorLeft(); 43 | static void cursorNLeft(int n); 44 | static void cursorNRight(int n); 45 | static void setColor(std::string name, bool bold = false); 46 | static void setBColor(std::string name, bool bold = false); 47 | }; 48 | } // namespace RhIO 49 | -------------------------------------------------------------------------------- /Tests/testServerRep.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "RhIO.hpp" 6 | 7 | int main() 8 | { 9 | RhIO::Root.newChild("test"); 10 | RhIO::Root.newChild("test2/pouet"); 11 | RhIO::Root.newBool("test/paramBool"); 12 | RhIO::Root.newInt("test/test3/paramInt")->minimum(-1)->maximum(10); 13 | RhIO::Root.child("test").newFloat("paramFloat")->comment("this is a test float")->defaultValue(42.0)->persisted(true); 14 | RhIO::Root.child("test/test3").newStr("paramStr"); 15 | 16 | RhIO::Root.newCommand("test/command1", "command1", 17 | [](const std::vector& args) -> std::string { return "OK " + args[0]; }); 18 | RhIO::Root.newCommand("test/test3/command2", "command2", 19 | [](const std::vector& args) -> std::string { return "OK " + args[0]; }); 20 | 21 | RhIO::Root.newStream("test/stream1", "stream1"); 22 | 23 | RhIO::Root.newFrame("test/frame1", "frame1", RhIO::FrameFormat::RGB); 24 | RhIO::Root.child("test").newFrame("frame2", "frame2", RhIO::FrameFormat::BGR); 25 | 26 | std::cout << "Waiting" << std::endl; 27 | std::this_thread::sleep_for(std::chrono::milliseconds(5000)); 28 | 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /Server/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16.3) 2 | project(RhIOServer) 3 | 4 | #The server automatically start on program startup (RhIO::start()) 5 | option(RHIO_SERVER_AUTOSTART "Autostart RhIO server" OFF) 6 | option (BUILD_KID_SIZE_VISION "Build the Vision" ON) 7 | 8 | find_package(jsoncpp REQUIRED) 9 | 10 | add_library(RhIO SHARED) 11 | target_link_libraries(RhIO PUBLIC 12 | RhIOCommon 13 | jsoncpp_lib 14 | cppzmq 15 | Threads::Threads 16 | ${OpenCV_LIBS} 17 | ) 18 | target_include_directories(RhIO PUBLIC 19 | ${OpenCV_INCLUDE_DIRS} 20 | $ 21 | ) 22 | 23 | 24 | if (BUILD_KID_SIZE_VISION) 25 | target_compile_definitions(RhIO PRIVATE -DVISION_COMPONENT) 26 | endif () 27 | 28 | target_sources(RhIO PRIVATE 29 | src/Bind.cpp 30 | src/CommandNode.cpp 31 | src/IONode.cpp 32 | src/Filesystem.cpp 33 | src/RhIO.cpp 34 | src/ServerPub.cpp 35 | src/ServerRep.cpp 36 | src/Stream.cpp 37 | src/StreamNode.cpp 38 | src/FrameNode.cpp 39 | src/ValueNode.cpp 40 | ) 41 | 42 | if(RHIO_SERVER_AUTOSTART) 43 | target_sources(RhIO PRIVATE src/autostart.cpp) 44 | endif() 45 | 46 | #Build Examples 47 | if(RHIO_BUILD_EXAMPLES) 48 | add_subdirectory(Examples) 49 | endif() 50 | -------------------------------------------------------------------------------- /Client/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | RhIOClient 4 | 0.0.0 5 | 6 | RhIO is a lightweight library that can be linked against your application 7 | in order to interract with your program on-the-fly, through its integrated server. 8 | 9 | Main feature are parameters that can be exposed in order to be changed, monitored 10 | or persisted to configuration files. 11 | 12 | It also provide a shell client that will provide a bash-like interface to walk 13 | your nodes and parameters, and even trigger methods that are in your code. 14 | 15 | 16 | Quentin Rouxel 17 | Grégoire Passault 18 | 19 | MIT 20 | 21 | https://github.com/Rhoban/RhIO 22 | 23 | Quentin Rouxel 24 | Grégoire Passault 25 | 26 | catkin 27 | 28 | ZeroMQ 29 | RhIOCommon 30 | 31 | ZeroMQ 32 | RhIOCommon 33 | 34 | -------------------------------------------------------------------------------- /Server/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | RhIOServer 4 | 0.0.0 5 | 6 | RhIO is a lightweight library that can be linked against your application 7 | in order to interract with your program on-the-fly, through its integrated server. 8 | 9 | Main feature are parameters that can be exposed in order to be changed, monitored 10 | or persisted to configuration files. 11 | 12 | It also provide a shell client that will provide a bash-like interface to walk 13 | your nodes and parameters, and even trigger methods that are in your code. 14 | 15 | 16 | Quentin Rouxel 17 | Grégoire Passault 18 | 19 | MIT 20 | 21 | https://github.com/Rhoban/RhIO 22 | 23 | Quentin Rouxel 24 | Grégoire Passault 25 | 26 | catkin 27 | 28 | ZeroMQ 29 | RhIOCommon 30 | 31 | ZeroMQ 32 | RhIOCommon 33 | 34 | -------------------------------------------------------------------------------- /Shell/src/commands/CatCommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Shell.h" 3 | #include "CatCommand.h" 4 | #include "CSV.h" 5 | #include "NodePool.h" 6 | #include "StreamManager.h" 7 | 8 | using namespace std::placeholders; 9 | 10 | namespace RhIO 11 | { 12 | std::string CatCommand::getName() 13 | { 14 | return "cat"; 15 | } 16 | 17 | std::string CatCommand::getDesc() 18 | { 19 | return "Cat a stream"; 20 | } 21 | 22 | std::string CatCommand::getUsage() 23 | { 24 | return "cat stream [> filename]"; 25 | } 26 | 27 | void CatCommand::process(std::vector args) 28 | { 29 | if (!args.size()) 30 | { 31 | errorUsage(); 32 | } 33 | else 34 | { 35 | os = getStream(args); 36 | auto client = shell->getClient(); 37 | auto nodeStream = shell->getNodeStream(args[0]); 38 | auto stream = shell->getStream(); 39 | 40 | client->enableStreamingStream(nodeStream.getName()); 41 | stream->setStreamCallback(std::bind(&CatCommand::update, this, _1, _2)); 42 | shell->wait(this); 43 | stream->unsetStreamCallback(); 44 | clearStream(); 45 | client->disableStreamingStream(nodeStream.getName()); 46 | } 47 | } 48 | 49 | void CatCommand::update(std::string name, std::string message) 50 | { 51 | *os << "[" << name << "] " << message; 52 | os->flush(); 53 | } 54 | } // namespace RhIO 55 | -------------------------------------------------------------------------------- /Shell/src/commands/HelpCommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Shell.h" 3 | #include "HelpCommand.h" 4 | #include "RemoteCommand.h" 5 | 6 | namespace RhIO 7 | { 8 | std::string HelpCommand::getName() 9 | { 10 | return "help"; 11 | } 12 | 13 | std::string HelpCommand::getDesc() 14 | { 15 | return "Lists all commands"; 16 | } 17 | 18 | void HelpCommand::process(std::vector args) 19 | { 20 | (void)args; 21 | auto commands = shell->getCommands(); 22 | 23 | for (auto entry : commands) 24 | { 25 | auto command = entry.second; 26 | bool isRemote = (NULL != dynamic_cast(command)); 27 | 28 | Terminal::setColor(isRemote ? "green" : "white", true); 29 | std::cout << command->getName() << ": "; 30 | Terminal::clear(); 31 | 32 | if (auto remote = dynamic_cast(command)) 33 | { 34 | std::cout << "(from "; 35 | Terminal::setColor("grey", true); 36 | std::cout << "/" << remote->getOrigin() << "/"; 37 | Terminal::clear(); 38 | std::cout << ")"; 39 | } 40 | 41 | std::cout << std::endl; 42 | if (command->getUsage() != "") 43 | { 44 | std::cout << " Usage: " << command->getUsage() << std::endl; 45 | } 46 | std::cout << " " << command->getDesc() << std::endl; 47 | } 48 | } 49 | } // namespace RhIO 50 | -------------------------------------------------------------------------------- /Common/src/Stream.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RHIO_STREAM_HPP 2 | #define RHIO_STREAM_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace RhIO 11 | { 12 | /** 13 | * Custom string buffer to catch 14 | * stream flush operation 15 | */ 16 | struct StreamBuffer : public std::stringbuf 17 | { 18 | public: 19 | /** 20 | * Initialization with stream absolute name 21 | */ 22 | StreamBuffer(const std::string& name); 23 | 24 | /** 25 | * Override to catch ostream flush 26 | */ 27 | virtual int sync() override; 28 | 29 | /** 30 | * The number of registered watcher. 31 | * Streaming is enabled while at least 32 | * one watcher is registered 33 | */ 34 | int64_t _streamWatchers; 35 | 36 | private: 37 | /** 38 | * Stream absolute name 39 | */ 40 | std::string _pwd; 41 | }; 42 | 43 | /** 44 | * Stream 45 | * 46 | * Hold all Stream 47 | * internal information 48 | */ 49 | struct Stream 50 | { 51 | /** 52 | * Textual description 53 | */ 54 | std::string comment; 55 | 56 | /** 57 | * External ostream instance 58 | */ 59 | std::shared_ptr stream; 60 | 61 | /** 62 | * Internal custom string buffer 63 | */ 64 | std::shared_ptr buffer; 65 | }; 66 | 67 | } // namespace RhIO 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /Shell/src/joystick/Joystick.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "Joystick.h" 11 | 12 | namespace RhIO 13 | { 14 | bool Joystick::JoystickEvent::isPressed() 15 | { 16 | return value; 17 | } 18 | 19 | float Joystick::JoystickEvent::getValue() 20 | { 21 | return value / (float(1 << 15)); 22 | } 23 | 24 | Joystick::Joystick() : fd(-1) 25 | { 26 | } 27 | 28 | std::string Joystick::getDeviceName() 29 | { 30 | char* e = getenv("JOYSTICK"); 31 | std::string pad; 32 | if (e == NULL) 33 | { 34 | pad = JOYSTICK_DEVNAME; 35 | } 36 | else 37 | { 38 | pad = std::string(e); 39 | } 40 | 41 | return pad; 42 | } 43 | 44 | bool Joystick::open() 45 | { 46 | std::string pad = getDeviceName(); 47 | fd = ::open(pad.c_str(), O_RDONLY | O_NONBLOCK); /* read write for force feedback? */ 48 | 49 | return fd > 0; 50 | } 51 | 52 | bool Joystick::getEvent(JoystickEvent* evt) 53 | { 54 | int bytes = read(fd, evt, sizeof(evt)); 55 | 56 | if (bytes <= 0) 57 | { 58 | usleep(10000); 59 | } 60 | 61 | return (bytes == sizeof(*evt)); 62 | } 63 | 64 | void Joystick::close() 65 | { 66 | if (fd > 0) 67 | { 68 | ::close(fd); 69 | fd = -1; 70 | } 71 | } 72 | } // namespace RhIO 73 | -------------------------------------------------------------------------------- /Skeleton/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | int main() 10 | { 11 | float amplitude, s, t, freq; 12 | 13 | RhIO::Bind bind("test"); 14 | // Uncomment to enable persisting, you'll have to create a "rhio" directory 15 | // in the same directory as the binary 16 | // RhIO::Root.load("rhio"); 17 | 18 | // Parameters that can be changed by the user 19 | bind.bindNew("amplitude", amplitude, RhIO::Bind::PullOnly) 20 | ->defaultValue(1.0) 21 | ->persisted(true) 22 | ->minimum(0) 23 | ->maximum(15); 24 | bind.bindNew("freq", freq, RhIO::Bind::PullOnly)->defaultValue(1.0)->persisted(true)->minimum(0); 25 | 26 | // Adding a "status" command that will be available in the shell 27 | bind.node().newCommand("status", "Demo status command", [&litude, &freq](std::vector) -> std::string { 28 | std::stringstream ss; 29 | ss << "The amplitude is " << amplitude << " and the freq is " << freq; 30 | return ss.str(); 31 | }); 32 | 33 | // Parameters that are monitored only 34 | bind.bindNew("t", t, RhIO::Bind::PushOnly); 35 | bind.bindNew("s", s, RhIO::Bind::PushOnly); 36 | 37 | t = 0.0; 38 | while (true) 39 | { 40 | bind.pull(); 41 | t += 0.02 * freq; 42 | s = sin(t) * amplitude; 43 | usleep(20000); 44 | bind.push(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Shell/src/commands/WatchCommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Shell.h" 3 | #include "WatchCommand.h" 4 | #include "Node.h" 5 | #include "NodePool.h" 6 | #include "StreamManager.h" 7 | 8 | using namespace std::placeholders; 9 | 10 | namespace RhIO 11 | { 12 | std::string WatchCommand::getName() 13 | { 14 | return "watch"; 15 | } 16 | 17 | std::string WatchCommand::getDesc() 18 | { 19 | return "Watch parameters from the current node"; 20 | } 21 | 22 | void WatchCommand::process(std::vector args) 23 | { 24 | NodePool pool; 25 | if (args.size() == 0) 26 | { 27 | pool = shell->poolForNode(shell->getCurrentNode()); 28 | } 29 | else 30 | { 31 | pool = shell->getPool(args); 32 | } 33 | 34 | pool.setCallback(std::bind(&WatchCommand::update, this, _1)); 35 | 36 | int result = system("clear"); 37 | if (result != 0) 38 | { 39 | std::cerr << "RhIO::ClearCommand:process: Failed to clear" << std::endl; 40 | } 41 | pool.draw(true); 42 | 43 | // Reducing frequency when watching values, avoiding shell flickering. 44 | // We can't see more than 25 fps anyway. 45 | shell->getStream()->setFrequency(25); 46 | shell->streamWait(&pool, this); 47 | } 48 | 49 | void WatchCommand::update(NodePool* pool) 50 | { 51 | int result = system("clear"); 52 | if (result != 0) 53 | { 54 | std::cerr << "RhIO::ClearCommand:process: Failed to clear" << std::endl; 55 | } 56 | pool->draw(true); 57 | } 58 | } // namespace RhIO 59 | -------------------------------------------------------------------------------- /Shell/src/utils.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Trim from start 4 | static inline std::string& ltrim(std::string& s) 5 | { 6 | s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(std::isspace)))); 7 | return s; 8 | } 9 | 10 | // Trim from end 11 | static inline std::string& rtrim(std::string& s) 12 | { 13 | s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); 14 | return s; 15 | } 16 | 17 | // Trim from both ends 18 | static inline std::string& trim(std::string& s) 19 | { 20 | return ltrim(rtrim(s)); 21 | } 22 | 23 | // Split a string 24 | static inline std::vector split(const std::string& s, char delim, int limit = -1) 25 | { 26 | if (limit > 0) 27 | limit--; 28 | std::vector parts; 29 | 30 | std::stringstream ss(s); 31 | std::string item; 32 | while (limit != 0 && getline(ss, item, delim)) 33 | { 34 | parts.push_back(item); 35 | limit--; 36 | } 37 | unsigned int c; 38 | std::string end = ""; 39 | while ((c = ss.get()) <= 256) 40 | { 41 | end += (char)c; 42 | } 43 | if (end != "") 44 | { 45 | parts.push_back(end); 46 | } 47 | return parts; 48 | } 49 | 50 | template 51 | void inline unique_vect(T& k) 52 | { 53 | auto w = k.begin(); 54 | std::set tmpset; 55 | for (auto r = k.begin(); r != k.end(); ++r) 56 | { 57 | if (tmpset.insert(*r).second) 58 | { 59 | *w++ = *r; 60 | } 61 | } 62 | k.erase(w, k.end()); 63 | } 64 | -------------------------------------------------------------------------------- /Tests/testCommands.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "RhIO.hpp" 4 | 5 | int main() 6 | { 7 | RhIO::Root.newChild("test"); 8 | RhIO::Root.newChild("test2/pouet"); 9 | 10 | assert(RhIO::Root.commandExist("test") == false); 11 | assert(RhIO::Root.commandExist("test/command1") == false); 12 | assert(RhIO::Root.commandExist("test2/pouet/command2") == false); 13 | assert(RhIO::Root.child("test").listCommands().size() == 0); 14 | 15 | RhIO::Root.newCommand("test/command1", "command1", 16 | [](const std::vector& args) -> std::string { return "OK " + args[0]; }); 17 | RhIO::Root.newCommand("test2/pouet/command2", "command2", 18 | [](const std::vector& args) -> std::string { return "KO " + args[0]; }); 19 | 20 | assert(RhIO::Root.commandExist("test") == false); 21 | assert(RhIO::Root.commandExist("test/command1") == true); 22 | assert(RhIO::Root.commandExist("test2/pouet/command2") == true); 23 | assert(RhIO::Root.commandDescription("test/command1") == "command1"); 24 | assert(RhIO::Root.commandDescription("test2/pouet/command2") == "command2"); 25 | assert(RhIO::Root.listCommands().size() == 0); 26 | assert(RhIO::Root.child("test").listCommands().size() == 1); 27 | assert(RhIO::Root.child("test").listCommands()[0] == "command1"); 28 | 29 | assert(RhIO::Root.call("test/command1", { "test1" }) == "OK test1"); 30 | assert(RhIO::Root.call("test2/pouet/command2", { "test2" }) == "KO test2"); 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rhoban Input/Output Library 2 | 3 | ![RhIO](Docs/imgs/rhio.gif) 4 | 5 | RhIO is a lightweight library that can be linked against your application 6 | in order to interract with your program on-the-fly, through its integrated server. 7 | 8 | Main feature are parameters that can be exposed in order to be changed, monitored 9 | or persisted to configuration files. 10 | 11 | We also provide a shell client that will provide a bash-like interface to walk 12 | your nodes and parameters, and even trigger methods that are in your code. 13 | 14 | ## Documentation 15 | 16 | * [Example Video](https://youtu.be/MOizgXYENLc) 17 | * [Getting started](/Docs/getting_started.md) 18 | * [Installing](/Docs/getting_started.md#installing) 19 | * [Building the skeleton example](/Docs/getting_started.md#skeleton) 20 | * [The API (server-side)](/Docs/api.md) 21 | * [Parameters](/Docs/api.md#parameters) 22 | * [Persistence](/Docs/api.md#persistence) 23 | * [Commands](/Docs/api.md#commands) 24 | * [Streams](/Docs/api.md#streams) 25 | * [Frames](/Docs/api.md#frames) 26 | * [Using binding (server-side)](/Docs/binding.md) 27 | * [Shell commands and features (client-side)](/Docs/shell.md) 28 | * [Using a joypad with the shell](/Docs/joypad.md) 29 | * [How does it works?](/Docs/how_does_it_works.md) 30 | 31 | ## License 32 | 33 | RhIO is under MIT License, please read the LICENSE file for further details. 34 | Do not hesitate to fork this repository and customize it! 35 | 36 | ## Overall Architecture 37 | 38 | ![RhIO](Docs/imgs/architecture.png) 39 | 40 | -------------------------------------------------------------------------------- /Shell/src/NodePool.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "Terminal.h" 4 | #include "NodePool.h" 5 | 6 | namespace RhIO 7 | { 8 | NodePool::NodePool() : dirty(false), handler(PoolUpdateHandler()), timestamp(0) 9 | { 10 | } 11 | 12 | void NodePool::setCallback(PoolUpdateHandler handler_) 13 | { 14 | handler = handler_; 15 | } 16 | 17 | void NodePool::update() 18 | { 19 | if (handler) 20 | { 21 | handler(this); 22 | } 23 | dirty = false; 24 | } 25 | 26 | void NodePool::draw(bool fullName) 27 | { 28 | for (auto nodeVal : *this) 29 | { 30 | auto val = nodeVal.value; 31 | 32 | std::cout << std::left; 33 | Terminal::setColor("white", val->persisted); 34 | if (fullName) 35 | { 36 | std::cout << std::setw(35) << nodeVal.getName(); 37 | } 38 | else 39 | { 40 | std::cout << std::setw(20) << nodeVal.value->name; 41 | } 42 | std::cout << " "; 43 | 44 | Terminal::setColor("magenta", false); 45 | std::cout << std::setw(7) << Node::getType(val); 46 | Terminal::clear(); 47 | 48 | std::cout << std::setw(9) << Node::toString(val); 49 | 50 | std::cout << " "; 51 | Terminal::setColor("magenta", false); 52 | std::cout << std::setw(25) << val->comment; 53 | Terminal::clear(); 54 | 55 | if (val->persisted) 56 | { 57 | Terminal::setColor("magenta", false); 58 | std::cout << " persisted: "; 59 | Terminal::clear(); 60 | std::cout << Node::persistedToString(val); 61 | } 62 | std::cout << std::endl; 63 | } 64 | } 65 | } // namespace RhIO 66 | -------------------------------------------------------------------------------- /Common/src/DataBuffer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RHIO_DATABUFFER_HPP 2 | #define RHIO_DATABUFFER_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace RhIO 8 | { 9 | /** 10 | * DataBuffer 11 | */ 12 | class DataBuffer 13 | { 14 | public: 15 | /** 16 | * Initialization with given already allocated 17 | * data and size. No deallocation is done. 18 | */ 19 | DataBuffer(void* data, size_t size); 20 | 21 | /** 22 | * Return current data size and 23 | * current offset cursor 24 | */ 25 | size_t size() const; 26 | size_t offset() const; 27 | 28 | /** 29 | * Write given each type value inside 30 | * given data buffer end and update cursor 31 | */ 32 | void writeType(uint8_t val); 33 | void writeBool(bool val); 34 | void writeInt(int64_t val); 35 | void writeFloat(double val); 36 | void writeStr(const std::string& val); 37 | void writeData(const unsigned char* data, size_t size); 38 | 39 | /** 40 | * Read each type into data buffer 41 | * at current offset and update cursor 42 | */ 43 | uint8_t readType(); 44 | bool readBool(); 45 | int64_t readInt(); 46 | double readFloat(); 47 | std::string readStr(); 48 | unsigned char* readData(size_t& size); 49 | 50 | /** 51 | * Return internal data pointer 52 | */ 53 | void* data(); 54 | 55 | private: 56 | /** 57 | * Data buffer 58 | */ 59 | uint8_t* _data; 60 | 61 | /** 62 | * Allocated data size 63 | */ 64 | size_t _size; 65 | 66 | /** 67 | * Cursor data offset 68 | */ 69 | size_t _offset; 70 | }; 71 | 72 | } // namespace RhIO 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /Server/src/BaseNode.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RHIO_BASENODE_HPP 2 | #define RHIO_BASENODE_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace RhIO 8 | { 9 | /** 10 | * BaseNode 11 | * 12 | * Code factorisation of 13 | * forward function for derive Node classes 14 | * 15 | * Forward function is needed for specific Node 16 | * to call themself across the tree hierarchy 17 | * controled by IONode 18 | */ 19 | template 20 | class BaseNode 21 | { 22 | public: 23 | /** 24 | * Typedef for forward function 25 | * to be defined by IONode 26 | * 27 | * name is the asked relative path element in tree. 28 | * If given name is referring to this Node, nullptr 29 | * is returned. 30 | * If given name is referring to a child Node, a pointer 31 | * to this Node is returned and newName is set to the 32 | * given name converted relatively to returned Node. 33 | * If given name is invalid, throw logic_error exception unless 34 | * createBranch is true and missing Node are created. 35 | */ 36 | typedef std::function ForwardFunc; 37 | 38 | /** 39 | * Initialize and store the forward function 40 | */ 41 | BaseNode(ForwardFunc func) : forwardFunc(func) 42 | { 43 | } 44 | 45 | /** 46 | * Virtual destructor 47 | */ 48 | virtual ~BaseNode() 49 | { 50 | } 51 | 52 | protected: 53 | /** 54 | * Hold forward function for derived class 55 | */ 56 | ForwardFunc forwardFunc; 57 | 58 | /** 59 | * Hold Node absolute name 60 | */ 61 | std::string pwd; 62 | }; 63 | 64 | } // namespace RhIO 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /Shell/src/commands/DiffCommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "Shell.h" 5 | #include "DiffCommand.h" 6 | #include 7 | #include 8 | 9 | namespace RhIO 10 | { 11 | std::string DiffCommand::getName() 12 | { 13 | return "diff"; 14 | } 15 | 16 | std::string DiffCommand::getDesc() 17 | { 18 | return "Shows the diff"; 19 | } 20 | 21 | void DiffCommand::process(std::vector args) 22 | { 23 | shell->sync(); 24 | auto node = getNode(args); 25 | 26 | if (!showDiff(node)) 27 | { 28 | Terminal::setColor("green", true); 29 | std::cout << "Everything is clean" << std::endl; 30 | Terminal::clear(); 31 | } 32 | } 33 | 34 | int DiffCommand::showDiff(Node* node) 35 | { 36 | int diff = 0; 37 | std::cout << std::left; 38 | 39 | for (auto nodeVal : node->getAll()) 40 | { 41 | auto value = nodeVal.value; 42 | if (value->persisted && Node::isDiff(value)) 43 | { 44 | Terminal::clear(); 45 | diff++; 46 | std::string name = std::string("/") + nodeVal.getName() + ":"; 47 | std::cout << std::setw(35) << name; 48 | 49 | Terminal::setColor("red", true); 50 | std::cout << std::setw(5) << Node::persistedToString(value); 51 | Terminal::setColor("white", false); 52 | std::cout << " → "; 53 | Terminal::setColor("green", true); 54 | std::cout << std::setw(5) << Node::toString(value); 55 | 56 | std::cout << std::endl; 57 | } 58 | } 59 | 60 | for (auto name : node->getChildren()) 61 | { 62 | diff += showDiff(node->getChild(name)); 63 | } 64 | return diff; 65 | } 66 | } // namespace RhIO 67 | -------------------------------------------------------------------------------- /Shell/src/commands/TreeCommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "Shell.h" 4 | #include "TreeCommand.h" 5 | #include "Node.h" 6 | #include "NodePool.h" 7 | 8 | namespace RhIO 9 | { 10 | std::string TreeCommand::getName() 11 | { 12 | return "tree"; 13 | } 14 | 15 | std::string TreeCommand::getDesc() 16 | { 17 | return "Lists the remote entries"; 18 | } 19 | 20 | void TreeCommand::process(std::vector args) 21 | { 22 | auto node = getNode(args); 23 | 24 | showTree(node); 25 | } 26 | 27 | void TreeCommand::showTree(Node* node, std::string left, std::string name) 28 | { 29 | Terminal::setColor("blue", true); 30 | std::cout << name << std::endl; 31 | 32 | auto all = node->getAll(); 33 | auto children = node->getChildren(); 34 | int k = 0, n = children.size() + all.size(); 35 | for (auto name : children) 36 | { 37 | auto child = node->getChild(name); 38 | 39 | std::string subleft; 40 | k++; 41 | Terminal::setColor("white", true); 42 | if (k == n) 43 | { 44 | subleft = left + " "; 45 | std::cout << left << "└── "; 46 | } 47 | else 48 | { 49 | subleft = left + "│ "; 50 | std::cout << left << "├── "; 51 | } 52 | showTree(child, subleft, name); 53 | } 54 | for (auto nodeVal : all) 55 | { 56 | k++; 57 | Terminal::setColor("white", true); 58 | if (k == n) 59 | { 60 | std::cout << left << "└── "; 61 | } 62 | else 63 | { 64 | std::cout << left << "├── "; 65 | } 66 | Terminal::setColor("white", false); 67 | std::cout << nodeVal.value->name << std::endl; 68 | } 69 | } 70 | } // namespace RhIO 71 | -------------------------------------------------------------------------------- /Tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16.3) 2 | 3 | add_executable(testTree testTree.cpp) 4 | target_link_libraries(testTree RhIO) 5 | 6 | add_executable(testValue testValue.cpp) 7 | target_link_libraries(testValue RhIO) 8 | 9 | add_executable(testThreadSafeTree testThreadSafeTree.cpp) 10 | target_link_libraries(testThreadSafeTree RhIO) 11 | add_executable(testThreadSafeValue testThreadSafeValue.cpp) 12 | target_link_libraries(testThreadSafeValue RhIO) 13 | add_executable(testThreadSafeValueUpdate testThreadSafeValueUpdate.cpp) 14 | target_link_libraries(testThreadSafeValueUpdate RhIO) 15 | 16 | add_executable(testPersistSave testPersistSave.cpp) 17 | target_link_libraries(testPersistSave RhIO) 18 | add_executable(testPersistLoad testPersistLoad.cpp) 19 | target_link_libraries(testPersistLoad RhIO) 20 | 21 | add_executable(testServerRep testServerRep.cpp) 22 | target_link_libraries(testServerRep RhIO) 23 | add_executable(testClientReq testClientReq.cpp) 24 | target_link_libraries(testClientReq RhIOClient) 25 | 26 | add_executable(testServerPub testServerPub.cpp) 27 | target_link_libraries(testServerPub RhIO) 28 | add_executable(testClientSub testClientSub.cpp) 29 | target_link_libraries(testClientSub RhIOClient) 30 | 31 | add_executable(testServer testServer.cpp) 32 | target_link_libraries(testServer RhIO) 33 | 34 | add_executable(testServerStress testServerStress.cpp) 35 | target_link_libraries(testServerStress RhIO) 36 | 37 | add_executable(testCommands testCommands.cpp) 38 | target_link_libraries(testCommands RhIO) 39 | 40 | add_executable(testBind testBind.cpp) 41 | target_link_libraries(testBind RhIO) 42 | 43 | add_executable(testImgs testImgs.cpp) 44 | target_link_libraries(testImgs RhIO) 45 | -------------------------------------------------------------------------------- /BUILD.bazel: -------------------------------------------------------------------------------- 1 | licenses(["unencumbered"]) # Public Domain or MIT 2 | 3 | exports_files(["LICENSE"]) 4 | 5 | cc_library( 6 | visibility = ["//visibility:public"], 7 | name = "rhio", 8 | srcs = [ 9 | "Server/src/Bind.cpp", 10 | "Server/src/CommandNode.cpp", 11 | "Server/src/Filesystem.cpp", 12 | "Server/src/FrameNode.cpp", 13 | "Server/src/IONode.cpp", 14 | "Server/src/RhIO.cpp", 15 | "Server/src/ServerPub.cpp", 16 | "Server/src/ServerRep.cpp", 17 | "Server/src/Stream.cpp", 18 | "Server/src/StreamNode.cpp", 19 | "Server/src/ValueNode.cpp", 20 | "Server/src/autostart.cpp", 21 | ], 22 | hdrs = [ 23 | "Server/src/BaseNode.hpp", 24 | "Server/src/Bind.hpp", 25 | "Server/src/BindFunction.hpp", 26 | "Server/src/CommandNode.hpp", 27 | "Server/src/Filesystem.hpp", 28 | "Server/src/FrameNode.hpp", 29 | "Server/src/IONode.hpp", 30 | "Server/src/RhIO.hpp", 31 | "Server/src/ServerPub.hpp", 32 | "Server/src/ServerRep.hpp", 33 | "Server/src/StreamNode.hpp", 34 | "Server/src/ValueNode.hpp", 35 | ], 36 | deps = [ 37 | ":rhio_common", 38 | "@cppzmq", 39 | "@opencv", 40 | ], 41 | strip_include_prefix = "Server/src", 42 | linkopts = ["-pthread"], 43 | ) 44 | 45 | cc_library( 46 | name = "rhio_common", 47 | srcs = [ 48 | "Common/src/DataBuffer.cpp", 49 | "Common/src/Protocol.cpp", 50 | ], 51 | hdrs = [ 52 | "Common/src/DataBuffer.hpp", 53 | "Common/src/Frame.hpp", 54 | "Common/src/Protocol.hpp", 55 | "Common/src/Stream.hpp", 56 | "Common/src/Value.hpp", 57 | ], 58 | strip_include_prefix = "Common/src", 59 | ) 60 | -------------------------------------------------------------------------------- /Shell/src/commands/ViewCommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Shell.h" 3 | #include "ViewCommand.h" 4 | #include "CSV.h" 5 | #include "NodePool.h" 6 | #include "StreamManager.h" 7 | 8 | using namespace std::placeholders; 9 | 10 | namespace RhIO 11 | { 12 | std::string ViewCommand::getName() 13 | { 14 | return "view"; 15 | } 16 | 17 | std::string ViewCommand::getDesc() 18 | { 19 | return "View a frame stream"; 20 | } 21 | 22 | std::string ViewCommand::getUsage() 23 | { 24 | return "view frame ..."; 25 | } 26 | 27 | void ViewCommand::process(std::vector args) 28 | { 29 | if (!args.size()) 30 | { 31 | errorUsage(); 32 | } 33 | else 34 | { 35 | auto client = shell->getClient(); 36 | auto stream = shell->getStream(); 37 | 38 | _viewers.clear(); 39 | for (size_t i = 0; i < args.size(); i++) 40 | { 41 | auto nodeFrame = shell->getNodeFrame(args[i]); 42 | _viewers.push_back({ nodeFrame.getName(), FrameStreamViewer(nodeFrame.getName()) }); 43 | _viewers[i].second.start(); 44 | client->enableStreamingFrame(nodeFrame.getName()); 45 | } 46 | 47 | stream->setFrameCallback(std::bind(&ViewCommand::update, this, _1, _2)); 48 | shell->wait(this); 49 | stream->unsetFrameCallback(); 50 | clearStream(); 51 | 52 | for (size_t i = 0; i < args.size(); i++) 53 | { 54 | auto nodeFrame = shell->getNodeFrame(args[i]); 55 | client->disableStreamingFrame(nodeFrame.getName()); 56 | _viewers[i].second.stop(); 57 | } 58 | } 59 | } 60 | 61 | void ViewCommand::update(std::string name, const cv::Mat& frame) 62 | { 63 | for (size_t i = 0; i < _viewers.size(); i++) 64 | { 65 | if (name == _viewers[i].first) 66 | { 67 | _viewers[i].second.pushFrame(frame); 68 | } 69 | } 70 | } 71 | } // namespace RhIO 72 | -------------------------------------------------------------------------------- /Shell/src/Completion.cpp: -------------------------------------------------------------------------------- 1 | #include "Completion.h" 2 | #include 3 | 4 | namespace RhIO 5 | { 6 | // probably a very unefficient way to do that 7 | std::string Completion::getSubstring(std::deque matches) 8 | { 9 | std::string line = ""; 10 | // so there are at least two elements but lets check anyway... 11 | if (matches.size() > 1) 12 | { 13 | size_t len = 9999; 14 | std::string tmpcmd; 15 | for (std::deque::iterator it = matches.begin(); it != matches.end(); ++it) 16 | { 17 | if ((*it).length() < len) // get the smallest 18 | { 19 | len = (*it).length(); 20 | tmpcmd = *it; 21 | } 22 | } 23 | 24 | // ckeck char by char... 25 | for (size_t i = 1; i < tmpcmd.length() + 1; i++) //+1? 26 | { 27 | bool iscommon = true; 28 | std::string tmp(tmpcmd, 0, i); 29 | 30 | for (std::deque::iterator it = matches.begin(); it != matches.end(); ++it) 31 | { 32 | if ((*it).compare(tmpcmd) != 0) // dont compare with ourself 33 | { 34 | if ((*it).compare(0, i, tmp) != 0) 35 | iscommon = false; 36 | } 37 | } 38 | if (iscommon) 39 | line = tmp; 40 | } 41 | } 42 | 43 | return line; 44 | } 45 | 46 | std::vector& Completion::split(const std::string& s, char delim, std::vector& elems) 47 | { 48 | std::stringstream ss(s); 49 | std::string item; 50 | if (s.size() > 0) 51 | { 52 | while (std::getline(ss, item, delim)) 53 | { 54 | elems.push_back(item); 55 | } 56 | } 57 | return elems; 58 | } 59 | 60 | std::vector Completion::split(const std::string& s, char delim) 61 | { 62 | std::vector elems; 63 | split(s, delim, elems); 64 | return elems; 65 | } 66 | } // namespace RhIO 67 | -------------------------------------------------------------------------------- /Shell/src/StreamManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "Shell.h" 7 | 8 | #define DEFAULT_FREQ 100 9 | 10 | namespace RhIO 11 | { 12 | class NodePool; 13 | class StreamManager 14 | { 15 | public: 16 | StreamManager(Shell*); 17 | ~StreamManager(); 18 | 19 | /** 20 | * Handler to update the stream 21 | */ 22 | typedef std::function StreamUpdateHandler; 23 | void setStreamCallback(StreamUpdateHandler handler); 24 | void unsetStreamCallback(); 25 | 26 | /** 27 | * Handle to update the frame 28 | */ 29 | typedef std::function FrameUpdateHandler; 30 | void setFrameCallback(FrameUpdateHandler handler); 31 | void unsetFrameCallback(); 32 | 33 | /** 34 | * Handlers 35 | */ 36 | void boolHandler(const std::string& name, long timestamp, bool val); 37 | void intHandler(const std::string& name, long timestamp, int val); 38 | void floatHandler(const std::string& name, long timestamp, float val); 39 | void stringHandler(const std::string& name, long timestamp, const std::string& val); 40 | void streamHandler(const std::string& name, long timestamp, const std::string& str); 41 | void frameHandler(const std::string& name, long timestamp, const cv::Mat& frame); 42 | 43 | /** 44 | * Add a node pool to monitor 45 | */ 46 | void addPool(Shell* shell, NodePool* pool); 47 | void removePool(Shell* shell, NodePool* pool); 48 | 49 | /** 50 | * Sets the frequency limit for the flush 51 | */ 52 | void setFrequency(int frequency = DEFAULT_FREQ); 53 | 54 | void update(); 55 | 56 | protected: 57 | int frequency; 58 | bool alive; 59 | std::thread* worker; 60 | std::mutex mutex; 61 | std::set pools; 62 | StreamUpdateHandler handlerStream; 63 | FrameUpdateHandler handlerFrame; 64 | }; 65 | } // namespace RhIO 66 | -------------------------------------------------------------------------------- /Shell/src/commands/PlotCommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Shell.h" 3 | #include "PlotCommand.h" 4 | #include "Node.h" 5 | #include "NodePool.h" 6 | #include "StreamManager.h" 7 | #include 8 | 9 | using namespace std::placeholders; 10 | 11 | namespace RhIO 12 | { 13 | std::string PlotCommand::getName() 14 | { 15 | return "plot"; 16 | } 17 | 18 | std::string PlotCommand::getDesc() 19 | { 20 | return "Plot parameters"; 21 | } 22 | 23 | void PlotCommand::process(std::vector args) 24 | { 25 | NodePool pool; 26 | pool = shell->getPool(args); 27 | 28 | Terminal::setColor("white", true); 29 | std::cout << "Plotting." << std::endl; 30 | std::cout << " q: quit" << std::endl; 31 | std::cout << " h: change history" << std::endl; 32 | std::cout << " p: pause/unpause" << std::endl; 33 | Terminal::clear(); 34 | 35 | paused = false; 36 | GnuPlot plot; 37 | pool.setCallback(std::bind(&PlotCommand::update, this, &plot, _1)); 38 | 39 | auto stream = shell->getStream(); 40 | stream->addPool(shell, &pool); 41 | while (!dead) 42 | { 43 | char c; 44 | if ((c = waitChar()) > 0) 45 | { 46 | if (c == 'q') 47 | { 48 | break; 49 | } 50 | if (c == 'h') 51 | { 52 | plot.changeHistory(); 53 | } 54 | if (c == 'p') 55 | { 56 | paused = !paused; 57 | if (paused) 58 | { 59 | printf("Paused\n"); 60 | } 61 | else 62 | { 63 | printf("Unpaused\n"); 64 | } 65 | } 66 | } 67 | } 68 | stream->removePool(shell, &pool); 69 | 70 | plot.closeWindow(); 71 | } 72 | 73 | void PlotCommand::update(GnuPlot* plot, NodePool* pool) 74 | { 75 | if (!paused) 76 | { 77 | plot->setX(pool->timestamp); 78 | for (auto node : *pool) 79 | { 80 | plot->push(node.getName(), Node::toNumber(node.value)); 81 | } 82 | plot->render(); 83 | } 84 | } 85 | } // namespace RhIO 86 | -------------------------------------------------------------------------------- /Docs/binding.md: -------------------------------------------------------------------------------- 1 | # Binding 2 | 3 | ## Parameters 4 | 5 | Binding is a convenient way to use the [RhIO API](api.md) with a 6 | simplified workflow. 7 | 8 | What you'll have to do is first instantiate a ``RhIO::Bind``, 9 | that will be related with a node, and then declare your parameters 10 | using the ``bindNew`` method: 11 | 12 | ```c++ 13 | float t = 0.0; 14 | RhIO::Bind binder("path"); 15 | 16 | binder.bindNew("t", t) 17 | ->comment("Elapsed time"); 18 | ``` 19 | 20 | The type of the parameter will be automatically resolved using the 21 | compiler overload (since the second parameter is actually a reference 22 | to a variable with a known type). 23 | The metadata are exactly the same as the [API "new" methods](api.md). 24 | 25 | You'll then be able to get data from RhIO (pull) or send data to RhIO (push): 26 | 27 | ```c++ 28 | while (true) { 29 | binder.pull(); 30 | t += 0.02; 31 | usleep(20000); 32 | binder.push(); 33 | } 34 | ``` 35 | 36 | ## PushOnly and PullOnly 37 | 38 | In a lot of situations, you'll only want a parameter to be pulled (imported from 39 | RhIO world) or pushed (exported to RhIO). You can use the third parameter to do it: 40 | 41 | ```c++ 42 | // The t parameter will be exported from RhIO, but never imported 43 | binder.bindNew("t", t, RhIO::Bind::PushOnly); 44 | 45 | // The t parameter will be imported from RhIO, but never exported 46 | binder.bindNew("amplitude", amplitude, RhIO::Bind::PullOnly); 47 | ``` 48 | 49 | ## Commands 50 | 51 | Commands can be also bound using RhIO binding, but only works with class methods. 52 | To do it, use the ``bindFunc`` method: 53 | 54 | ```c++ 55 | // In your class (for instance in your constructor) 56 | binder.bindFunc("command", "command description", 57 | &MyObject::myCommand, *this) 58 | ``` 59 | 60 | Note that the type of the command will automatically define the arguments constraint, 61 | so the following code: 62 | 63 | ```c++ 64 | int MyObject::myCommand(int a) 65 | { 66 | return a + 1; 67 | } 68 | ``` 69 | 70 | Will be a valid command. 71 | -------------------------------------------------------------------------------- /Tests/testServer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "RhIO.hpp" 7 | 8 | int main() 9 | { 10 | RhIO::Root.newChild("test"); 11 | RhIO::Root.newChild("test2/pouet"); 12 | RhIO::Root.newBool("test/paramBool"); 13 | RhIO::Root.newFloat("test/freq"); 14 | RhIO::Root.newFloat("test/sin")->minimum(-1)->maximum(1); 15 | RhIO::Root.newInt("test/test3/paramInt")->minimum(-1)->maximum(10); 16 | RhIO::Root.child("test").newFloat("paramFloat")->comment("this is a test float")->defaultValue(42.0)->persisted(true); 17 | RhIO::Root.child("test/test3").newStr("paramStr"); 18 | 19 | RhIO::Root.newChild("server"); 20 | RhIO::Root.child("server").newStr("hostname")->defaultValue("testServ")->persisted(true); 21 | 22 | RhIO::Root.newChild("foo"); 23 | RhIO::Root.newChild("foo/bar"); 24 | RhIO::Root.newFloat("foo/bar/x"); 25 | RhIO::Root.newChild("foo/barz"); 26 | RhIO::Root.newFloat("foo/barz/x"); 27 | 28 | RhIO::Root.newStream("test/stream1", "Some stream"); 29 | 30 | RhIO::Root.newFrame("test/frame1", "Some frame", RhIO::FrameFormat::RGB); 31 | 32 | RhIO::Root.newCommand("test/command1", "command1", [](const std::vector& args) -> std::string { 33 | if (args.size() != 1) 34 | { 35 | return "Usage: command1 arg"; 36 | } 37 | else 38 | { 39 | return "OK " + args[0]; 40 | } 41 | }); 42 | 43 | std::cout << "Waiting" << std::endl; 44 | 45 | float angle = 0.0; 46 | RhIO::Bind bind("lowlevel/servos/ChevilleG"); 47 | bind.bindNew("angle", angle); 48 | 49 | auto& out = RhIO::Root.out("test/stream1"); 50 | 51 | float t = 0.0; 52 | while (true) 53 | { 54 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 55 | float f = RhIO::Root.getFloat("test/freq"); 56 | t += 0.01 * f; 57 | 58 | RhIO::Root.setFloat("test/sin", sin(t * 2 * M_PI)); 59 | out << "Debug, t=" << t << ", sin(t) = " << sin(t * 2 * M_PI) << std::endl << std::flush; 60 | 61 | angle = sin(t) * 1.0; 62 | bind.push(); 63 | } 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /Shell/src/commands/Plot2DCommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Shell.h" 3 | #include "Plot2DCommand.h" 4 | #include "Node.h" 5 | #include "NodePool.h" 6 | #include "StreamManager.h" 7 | #include 8 | 9 | using namespace std::placeholders; 10 | 11 | namespace RhIO 12 | { 13 | std::string Plot2DCommand::getName() 14 | { 15 | return "plot2d"; 16 | } 17 | 18 | std::string Plot2DCommand::getDesc() 19 | { 20 | return "Plot 2D parameters"; 21 | } 22 | 23 | std::string Plot2DCommand::getUsage() 24 | { 25 | return "x-var y1 [y2 [y3...]]"; 26 | } 27 | 28 | void Plot2DCommand::process(std::vector args) 29 | { 30 | if (args.size() < 2) 31 | { 32 | errorUsage(); 33 | } 34 | 35 | NodePool pool; 36 | pool = shell->getPool(args); 37 | 38 | Terminal::setColor("white", true); 39 | std::cout << "Plotting." << std::endl; 40 | std::cout << " q: quit" << std::endl; 41 | std::cout << " h: change history" << std::endl; 42 | std::cout << " p: pause/unpause" << std::endl; 43 | Terminal::clear(); 44 | 45 | paused = false; 46 | GnuPlot plot(2); 47 | pool.setCallback(std::bind(&Plot2DCommand::update, this, &plot, _1)); 48 | 49 | auto stream = shell->getStream(); 50 | stream->addPool(shell, &pool); 51 | while (true) 52 | { 53 | char c; 54 | if ((c = getchar()) > 0) 55 | { 56 | if (c == 'q') 57 | { 58 | break; 59 | } 60 | if (c == 'h') 61 | { 62 | plot.changeHistory(); 63 | } 64 | if (c == 'p') 65 | { 66 | paused = !paused; 67 | if (paused) 68 | { 69 | printf("Paused\n"); 70 | } 71 | else 72 | { 73 | printf("Unpaused\n"); 74 | } 75 | } 76 | } 77 | } 78 | stream->removePool(shell, &pool); 79 | 80 | plot.closeWindow(); 81 | } 82 | 83 | void Plot2DCommand::update(GnuPlot* plot, NodePool* pool) 84 | { 85 | if (!paused) 86 | { 87 | plot->setX(pool->timestamp); 88 | for (auto node : *pool) 89 | { 90 | plot->push(node.getName(), Node::toNumber(node.value)); 91 | } 92 | plot->render(); 93 | } 94 | } 95 | } // namespace RhIO 96 | -------------------------------------------------------------------------------- /Shell/src/commands/Plot3DCommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Shell.h" 3 | #include "Plot3DCommand.h" 4 | #include "Node.h" 5 | #include "NodePool.h" 6 | #include "StreamManager.h" 7 | #include 8 | 9 | using namespace std::placeholders; 10 | 11 | namespace RhIO 12 | { 13 | std::string Plot3DCommand::getName() 14 | { 15 | return "plot3d"; 16 | } 17 | 18 | std::string Plot3DCommand::getDesc() 19 | { 20 | return "Plot 3D parameters"; 21 | } 22 | 23 | std::string Plot3DCommand::getUsage() 24 | { 25 | return "x-var y1 y2 [y3 [y4...]]"; 26 | } 27 | 28 | void Plot3DCommand::process(std::vector args) 29 | { 30 | if (args.size() < 3) 31 | { 32 | errorUsage(); 33 | } 34 | 35 | NodePool pool; 36 | pool = shell->getPool(args); 37 | 38 | Terminal::setColor("white", true); 39 | std::cout << "Plotting." << std::endl; 40 | std::cout << " q: quit" << std::endl; 41 | std::cout << " h: change history" << std::endl; 42 | std::cout << " p: pause/unpause" << std::endl; 43 | Terminal::clear(); 44 | 45 | paused = false; 46 | GnuPlot plot(3); 47 | pool.setCallback(std::bind(&Plot3DCommand::update, this, &plot, _1)); 48 | 49 | auto stream = shell->getStream(); 50 | stream->addPool(shell, &pool); 51 | while (true) 52 | { 53 | char c; 54 | if ((c = getchar()) > 0) 55 | { 56 | if (c == 'q') 57 | { 58 | break; 59 | } 60 | if (c == 'h') 61 | { 62 | plot.changeHistory(); 63 | } 64 | if (c == 'p') 65 | { 66 | paused = !paused; 67 | if (paused) 68 | { 69 | printf("Paused\n"); 70 | } 71 | else 72 | { 73 | printf("Unpaused\n"); 74 | } 75 | } 76 | } 77 | } 78 | stream->removePool(shell, &pool); 79 | 80 | plot.closeWindow(); 81 | } 82 | 83 | void Plot3DCommand::update(GnuPlot* plot, NodePool* pool) 84 | { 85 | if (!paused) 86 | { 87 | plot->setX(pool->timestamp); 88 | for (auto node : *pool) 89 | { 90 | plot->push(node.getName(), Node::toNumber(node.value)); 91 | } 92 | plot->render(); 93 | } 94 | } 95 | } // namespace RhIO 96 | -------------------------------------------------------------------------------- /Shell/src/commands/LsCommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "Shell.h" 5 | #include "LsCommand.h" 6 | #include "Node.h" 7 | #include "NodePool.h" 8 | 9 | namespace RhIO 10 | { 11 | std::string LsCommand::getName() 12 | { 13 | return "ls"; 14 | } 15 | 16 | std::string LsCommand::getDesc() 17 | { 18 | return "Lists the remote entries"; 19 | } 20 | 21 | void LsCommand::process(std::vector args) 22 | { 23 | auto node = getNode(args); 24 | 25 | // Listing sub directories 26 | Terminal::setColor("blue", true); 27 | for (auto name : node->getChildren()) 28 | { 29 | std::cout << name; 30 | std::cout << "/" << std::endl; 31 | } 32 | Terminal::clear(); 33 | 34 | // Listing commands 35 | /* 36 | for (auto command : node->getCommands()) { 37 | Terminal::setColor("green", true); 38 | printf("%-23s", command.c_str()); 39 | 40 | if (auto shellCommand = shell->getCommand(command)) { 41 | Terminal::setColor("blue", false); 42 | std::cout << "desc: "; 43 | Terminal::clear(); 44 | std::cout << shellCommand->getDesc(); 45 | } 46 | std::cout << std::endl; 47 | Terminal::clear(); 48 | } 49 | */ 50 | 51 | // Listing streams 52 | for (auto stream : node->getStreams()) 53 | { 54 | std::cout << std::left; 55 | Terminal::setColor("darkblue", true); 56 | std::cout << std::setw(21) << stream.name; 57 | 58 | if (stream.desc != "") 59 | { 60 | Terminal::setColor("magenta", false); 61 | std::cout << stream.desc; 62 | Terminal::clear(); 63 | } 64 | std::cout << std::endl; 65 | Terminal::clear(); 66 | } 67 | 68 | // Listing frames 69 | for (auto frame : node->getFrames()) 70 | { 71 | std::cout << std::left; 72 | Terminal::setColor("green", true); 73 | std::cout << std::setw(21) << frame.name; 74 | 75 | std::cout << std::left; 76 | Terminal::setColor("magenta", false); 77 | std::cout << std::setw(21) << frame.desc; 78 | 79 | std::cout << std::endl; 80 | Terminal::clear(); 81 | } 82 | 83 | NodePool pool = shell->poolForNode(node); 84 | pool.draw(); 85 | } 86 | } // namespace RhIO 87 | -------------------------------------------------------------------------------- /Server/src/StreamNode.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RHIO_STREAMNODE_HPP 2 | #define RHIO_STREAMNODE_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "BaseNode.hpp" 11 | #include "Stream.hpp" 12 | 13 | namespace RhIO 14 | { 15 | /** 16 | * StreamNode 17 | * 18 | * Implement output stream feature for IONode 19 | */ 20 | class StreamNode : public BaseNode 21 | { 22 | public: 23 | /** 24 | * Inherit BaseNode constructor 25 | */ 26 | using BaseNode::BaseNode; 27 | 28 | /** 29 | * Custom assignement operator 30 | * (because std::mutex is non copyable) 31 | */ 32 | StreamNode& operator=(const StreamNode& node); 33 | 34 | /** 35 | * Return true if given stream name is registered 36 | */ 37 | bool streamExist(const std::string& name) const; 38 | 39 | /** 40 | * Return stream textual description 41 | * Throw std::logic_error if given name does not 42 | * exist 43 | */ 44 | std::string streamDescription(const std::string& name) const; 45 | 46 | /** 47 | * Enable or disable (increase or decrease stream watchers) 48 | * for given stream name 49 | */ 50 | void enableStreamingStream(const std::string& name); 51 | void disableStreamingStream(const std::string& name); 52 | 53 | /** 54 | * Return the std::ostream output stream instance 55 | * of given relative stream name. 56 | * Throw std::logic_error if given name does not 57 | * exist 58 | */ 59 | std::ostream& out(const std::string& name); 60 | 61 | /** 62 | * Register a new stream with given name and 63 | * textual description 64 | */ 65 | void newStream(const std::string& name, const std::string& comment); 66 | 67 | /** 68 | * Return the relative name list 69 | * of all registered streams 70 | */ 71 | std::vector listStreams() const; 72 | 73 | private: 74 | /** 75 | * Container map for output streams instance 76 | * and textual description 77 | */ 78 | std::map _streams; 79 | 80 | /** 81 | * Mutex protecting concurent stream creation 82 | */ 83 | mutable std::mutex _mutex; 84 | }; 85 | 86 | } // namespace RhIO 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /Shell/src/commands/LogImgCommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Shell.h" 3 | #include "LogImgCommand.h" 4 | #include "CSV.h" 5 | #include "NodePool.h" 6 | #include "StreamManager.h" 7 | 8 | using namespace std::placeholders; 9 | 10 | namespace RhIO 11 | { 12 | std::string LogImgCommand::getName() 13 | { 14 | return "logimg"; 15 | } 16 | 17 | std::string LogImgCommand::getDesc() 18 | { 19 | return "Log images to files"; 20 | } 21 | 22 | std::string LogImgCommand::getUsage() 23 | { 24 | return "logimg frame ..."; 25 | } 26 | 27 | void LogImgCommand::process(std::vector args) 28 | { 29 | if (!args.size()) 30 | { 31 | errorUsage(); 32 | } 33 | else 34 | { 35 | auto client = shell->getClient(); 36 | auto stream = shell->getStream(); 37 | 38 | for (size_t i = 0; i < args.size(); i++) 39 | { 40 | auto nodeFrame = shell->getNodeFrame(args[i]); 41 | ids[nodeFrame.getName()] = 0; 42 | names[nodeFrame.getName()] = nodeFrame.name; 43 | client->enableStreamingFrame(nodeFrame.getName()); 44 | } 45 | 46 | stream->setFrameCallback(std::bind(&LogImgCommand::update, this, _1, _2, _3, _4, _5)); 47 | shell->wait(this); 48 | stream->unsetFrameCallback(); 49 | clearStream(); 50 | 51 | for (size_t i = 0; i < args.size(); i++) 52 | { 53 | auto nodeFrame = shell->getNodeFrame(args[i]); 54 | client->disableStreamingFrame(nodeFrame.getName()); 55 | } 56 | } 57 | } 58 | 59 | void LogImgCommand::update(std::string name, size_t width, size_t height, unsigned char* data, size_t size) 60 | { 61 | if (ids.count(name)) 62 | { 63 | char buffer[name.size() + 10]; 64 | sprintf(buffer, "%s-%05d.png", names[name].c_str(), ids[name]); 65 | ids[name]++; 66 | 67 | (void)size; 68 | int k = 0; 69 | auto img = gdImageCreateTrueColor(width, height); 70 | for (unsigned int y = 0; y < height; y++) 71 | { 72 | for (unsigned int x = 0; x < width; x++) 73 | { 74 | int c = 0; 75 | c |= ((data[k++] & 0xff) << 16); 76 | c |= ((data[k++] & 0xff) << 8); 77 | c |= ((data[k++] & 0xff) << 0); 78 | gdImageSetPixel(img, x, y, c); 79 | } 80 | } 81 | 82 | if (FILE* output = fopen(buffer, "w")) 83 | { 84 | gdImagePng(img, output); 85 | fclose(output); 86 | } 87 | gdImageDestroy(img); 88 | } 89 | } 90 | } // namespace RhIO 91 | -------------------------------------------------------------------------------- /Shell/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16.3) 2 | project (RhIOShell) 3 | 4 | # Enable colors ? 5 | option (COLORS "Enable colors" ON) 6 | 7 | # Enable curses 8 | option (CURSES "Enable curses (libncurses5-dev)" ON) 9 | 10 | # Enable log img 11 | option (LOGIMG "Enable log img (libgd-dev required)" OFF) 12 | 13 | find_package(jsoncpp REQUIRED) 14 | 15 | add_executable(rhio) 16 | target_include_directories(rhio PRIVATE ${OpenCV_INCLUDE_DIRS} src/) 17 | target_link_libraries(rhio PRIVATE 18 | jsoncpp_lib 19 | Threads::Threads 20 | RhIOClient 21 | ${OpenCV_LIBS} 22 | ) 23 | target_sources(rhio PRIVATE 24 | src/commands/Command.cpp 25 | src/commands/ClearCommand.cpp 26 | src/commands/HelpCommand.cpp 27 | src/commands/LsCommand.cpp 28 | src/commands/ErrCommand.cpp 29 | src/commands/WatchCommand.cpp 30 | src/commands/CdCommand.cpp 31 | src/commands/LogCommand.cpp 32 | src/commands/SyncCommand.cpp 33 | src/commands/RemoteCommand.cpp 34 | src/commands/PlotCommand.cpp 35 | src/commands/Plot2DCommand.cpp 36 | src/commands/Plot3DCommand.cpp 37 | src/commands/DiffCommand.cpp 38 | src/commands/SaveCommand.cpp 39 | src/commands/LoadCommand.cpp 40 | src/commands/TreeCommand.cpp 41 | src/commands/CatCommand.cpp 42 | src/commands/ViewCommand.cpp 43 | src/commands/RepeatCommand.cpp 44 | src/commands/DelayCommand.cpp 45 | src/commands/PadCommand.cpp 46 | src/joystick/Joystick.cpp 47 | src/GnuPlot.cpp 48 | src/FrameStreamViewer.cpp 49 | src/Shell.cpp 50 | src/CSV.cpp 51 | src/Terminal.cpp 52 | src/NodePool.cpp 53 | src/StreamManager.cpp 54 | src/Completion.cpp 55 | src/Node.cpp 56 | src/main.cpp 57 | ) 58 | 59 | if (COLORS) 60 | target_compile_definitions(rhio PRIVATE -DHAS_COLORS) 61 | endif () 62 | 63 | # Logging img 64 | if (LOGIMG) 65 | target_compile_definitions(rhio PRIVATE -DHAS_LOGIMG) 66 | target_sources(rhio PRIVATE src/commands/LogImgCommand.cpp) 67 | target_link_libraries(rhio PRIVATE gd) 68 | endif () 69 | 70 | # Adding curses sources and library 71 | if (CURSES) 72 | target_compile_definitions(rhio PRIVATE -DHAS_CURSES) 73 | target_sources(rhio PRIVATE 74 | src/commands/TuneCommand.cpp 75 | src/Curse.cpp 76 | ) 77 | target_link_libraries(rhio PRIVATE ncurses form panel) 78 | endif () 79 | 80 | install(TARGETS rhio DESTINATION bin) 81 | -------------------------------------------------------------------------------- /Server/src/CommandNode.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RHIO_COMMANDNODE_HPP 2 | #define RHIO_COMMANDNODE_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "BaseNode.hpp" 11 | 12 | namespace RhIO 13 | { 14 | /** 15 | * CommandNode 16 | * 17 | * Implement Commands feature for IONode 18 | */ 19 | class CommandNode : public BaseNode 20 | { 21 | public: 22 | /** 23 | * Typedef for Command function 24 | */ 25 | typedef std::function)> CommandFunc; 26 | 27 | /** 28 | * Inherit BaseNode constructor 29 | */ 30 | using BaseNode::BaseNode; 31 | 32 | /** 33 | * Custom assignement operator 34 | * (because std::mutex is non copyable) 35 | */ 36 | CommandNode& operator=(const CommandNode& node); 37 | 38 | /** 39 | * Return true if given command name is registered 40 | */ 41 | bool commandExist(const std::string& name) const; 42 | 43 | /** 44 | * Call given command name with given set of 45 | * arguments and return command result. 46 | * Throw std::logic_error if given name does not 47 | * exist 48 | */ 49 | std::string call(const std::string& name, const std::vector& arguments); 50 | 51 | /** 52 | * Return the textual description of given relative 53 | * command name. 54 | * Throw std::logic_error if given name does not 55 | * exist 56 | */ 57 | std::string commandDescription(const std::string& name) const; 58 | 59 | /** 60 | * Register a new command with given name, textual 61 | * description and callback function. 62 | * WARNING: command must not call another command 63 | * neither request method on this Command node 64 | * (mutex deadlock). 65 | */ 66 | void newCommand(const std::string& name, const std::string& comment, CommandFunc func); 67 | 68 | /** 69 | * Return the relative name list of 70 | * all registered commands 71 | */ 72 | std::vector listCommands() const; 73 | 74 | private: 75 | /** 76 | * Container map for commands functions 77 | * and commands descriptions 78 | */ 79 | std::map _commands; 80 | std::map _descriptions; 81 | 82 | /** 83 | * Mutex protecting concurent commands creation 84 | */ 85 | mutable std::mutex _mutex; 86 | }; 87 | 88 | } // namespace RhIO 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /Tests/testClientSub.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "RhIO.hpp" 6 | #include "RhIOClient.hpp" 7 | 8 | int main() 9 | { 10 | RhIO::ClientSub client(std::string("tcp://localhost:") + RhIO::ServerPubPort); 11 | 12 | client.setHandlerBool([](const std::string name, int64_t timestamp, bool val) { 13 | std::cout << "Receiving Bool:" << std::endl; 14 | std::cout << name << " " << timestamp << " " << val << std::endl; 15 | assert(name == "test/paramBool"); 16 | assert(val == true); 17 | }); 18 | client.setHandlerInt([](const std::string name, int64_t timestamp, int64_t val) { 19 | std::cout << "Receiving Int:" << std::endl; 20 | std::cout << name << " " << timestamp << " " << val << std::endl; 21 | assert(name == "test/test3/paramInt"); 22 | assert(val == 2); 23 | }); 24 | client.setHandlerFloat([](const std::string name, int64_t timestamp, double val) { 25 | std::cout << "Receiving Float:" << std::endl; 26 | std::cout << name << " " << timestamp << " " << val << std::endl; 27 | assert(name == "test/paramFloat"); 28 | assert(val == 3.0); 29 | }); 30 | client.setHandlerStr([](const std::string name, int64_t timestamp, const std::string& val) { 31 | std::cout << "Receiving Str:" << std::endl; 32 | std::cout << name << " " << timestamp << " " << val << std::endl; 33 | assert(name == "test/test3/paramStr"); 34 | assert(val == "4"); 35 | }); 36 | client.setHandlerStream([](const std::string name, int64_t timestamp, const std::string& val) { 37 | std::cout << "Receiving Stream:" << std::endl; 38 | std::cout << name << " " << timestamp << " " << val << std::endl; 39 | assert(name == "test/stream1"); 40 | assert(val == "test stream1\n"); 41 | }); 42 | client.setHandlerFrame( 43 | [](const std::string name, int64_t timestamp, size_t width, size_t height, unsigned char* data, size_t size) { 44 | (void)data; 45 | std::cout << "Receiving Frame:" << std::endl; 46 | std::cout << name << " " << timestamp << " " << width << "x" << height << " size=" << size << std::endl; 47 | assert(name == "test/frame1"); 48 | assert(width == 300); 49 | assert(height == 200); 50 | assert(size == 3 * 300 * 200); 51 | }); 52 | 53 | std::cout << "Waiting" << std::endl; 54 | std::this_thread::sleep_for(std::chrono::milliseconds(5000)); 55 | 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /Shell/src/GnuPlot.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace RhIO 17 | { 18 | class GnuPlotSignal 19 | { 20 | public: 21 | GnuPlotSignal(std::string name); 22 | std::string name; 23 | std::deque values; 24 | }; 25 | 26 | /** 27 | * Plot 28 | * 29 | * Programming interface with 30 | * the Linux ploting utility 31 | * Gnuplot using VectorLabel 32 | */ 33 | class GnuPlot 34 | { 35 | public: 36 | GnuPlot(int mode = 1); 37 | ~GnuPlot(); 38 | 39 | /** 40 | * Change the X 41 | */ 42 | void setX(int x); 43 | 44 | /** 45 | * Push a value 46 | */ 47 | void push(std::string name, double value); 48 | 49 | /** 50 | * Render the plot 51 | * Wait until plot window is closed 52 | */ 53 | void render(); 54 | 55 | /** 56 | * Close gnuplot 57 | */ 58 | void closeWindow(); 59 | 60 | /** 61 | * Change the history size 62 | */ 63 | void changeHistory(); 64 | 65 | private: 66 | /** 67 | * Replot? 68 | */ 69 | bool replot; 70 | 71 | /** 72 | * Current history size 73 | */ 74 | int history; 75 | 76 | /** 77 | * Gnuplot pipe file descriptor 78 | */ 79 | int plotFd; 80 | 81 | /** 82 | * Time reference offset 83 | */ 84 | int timeRefOffset; 85 | 86 | /** 87 | * Time reference 88 | */ 89 | std::deque timeRef; 90 | 91 | /** 92 | * Values to plot 93 | */ 94 | std::vector signals; 95 | std::map signalsByName; 96 | 97 | /** 98 | * Fork current process to create a new GnuPlot window 99 | */ 100 | void createGnuplotInstance(); 101 | 102 | /** 103 | * Wait for end of gnuplot session and 104 | * close the openned pipe to GnuPlot window instance 105 | */ 106 | void waitCloseGnuplotInstance(); 107 | 108 | /** 109 | * Generate and return Gnuplot commands and data 110 | */ 111 | std::string generatePlotting(); 112 | 113 | bool mode2D; 114 | bool mode3D; 115 | }; 116 | } // namespace RhIO 117 | -------------------------------------------------------------------------------- /Server/Examples/pad.json: -------------------------------------------------------------------------------- 1 | [ 2 | /** 3 | * To make a joypad work, first type in rhio: 4 | * 5 | * pad 6 | * 7 | * This will dump you all the events from your joypad. You can then write 8 | * a json file in your ~/.rhio/ directory. 9 | * This is an example. 10 | * 11 | * When your json is ready, type: 12 | * 13 | * pad my_file.json 14 | * 15 | * To run the binding 16 | */ 17 | 18 | 19 | /** 20 | * Here, the button number 3 will toggle the enabled variable. It will be 21 | * set to 1 on the first push, then 0 and so. 22 | */ 23 | { 24 | "param": "/moves/walk/walkEnable", 25 | "type": "toggle", 26 | "number": 3 27 | }, 28 | 29 | /** 30 | * The axis 1 will set step in the range 38 to -38 31 | */ 32 | { 33 | "param": "/moves/walk/walkStep", 34 | "type": "axis", 35 | "number": 1, 36 | "range": [50, -20] 37 | }, 38 | { 39 | "param": "/moves/walk/walkLateral", 40 | "type": "axis", 41 | "number": 0, 42 | "range": [20, -20] 43 | }, 44 | { 45 | "param": "/moves/walk/walkTurn", 46 | "type": "axis", 47 | "number": 2, 48 | "range": [15, -15] 49 | }, 50 | 51 | /** 52 | * Pressing the button 4 will increment the frequency, and 53 | * pressing the button 6 will decrement it. 54 | */ 55 | { 56 | "param": "/moves/walk/freq", 57 | "type": "increment", 58 | "number": 4, 59 | "step": 0.1 60 | }, 61 | { 62 | "param": "/moves/walk/freq", 63 | "type": "decrement", 64 | "number": 6, 65 | "step": 0.1 66 | }, 67 | 68 | /** 69 | * Standup 70 | */ 71 | { 72 | "command": "standup", 73 | "type": "button", 74 | "number": 12 75 | }, 76 | { 77 | "command": "start walk", 78 | "type": "button", 79 | "number": 15 80 | }, 81 | { 82 | "command": "stop walk", 83 | "type": "button", 84 | "number": 13 85 | }, 86 | 87 | /** 88 | * Kick 89 | */ 90 | { 91 | "param": "/moves/walk/walkKickLeft", 92 | "type": "toggle", 93 | "number": 10 94 | }, 95 | 96 | { 97 | "param": "/moves/walk/walkKickRight", 98 | "type": "toggle", 99 | "number": 11 100 | } 101 | ] 102 | -------------------------------------------------------------------------------- /Tests/testServerPub.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "RhIO.hpp" 6 | 7 | int main() 8 | { 9 | RhIO::Root.newChild("test"); 10 | RhIO::Root.newChild("test2/pouet"); 11 | RhIO::Root.newBool("test/paramBool"); 12 | RhIO::Root.newInt("test/test3/paramInt")->minimum(-1)->maximum(10); 13 | RhIO::Root.child("test").newFloat("paramFloat")->comment("this is a test float")->defaultValue(42.0)->persisted(true); 14 | RhIO::Root.child("test/test3").newStr("paramStr"); 15 | 16 | RhIO::Root.newStream("test/stream1", "stream1"); 17 | 18 | RhIO::Root.newFrame("test/frame1", "frame1", RhIO::FrameFormat::RGB); 19 | 20 | std::cout << "Waiting" << std::endl; 21 | for (size_t k = 0; k < 50; k++) 22 | { 23 | RhIO::Root.setBool("test/paramBool", true); 24 | RhIO::Root.setInt("test/test3/paramInt", 2); 25 | RhIO::Root.setFloat("test/paramFloat", 3.0); 26 | RhIO::Root.setStr("test/test3/paramStr", "4"); 27 | RhIO::Root.setBool("test/paramBool", true); 28 | RhIO::Root.setInt("test/test3/paramInt", 2); 29 | RhIO::Root.setFloat("test/paramFloat", 3.0); 30 | RhIO::Root.setStr("test/test3/paramStr", "4"); 31 | RhIO::Root.out("test/stream1") << "test stream1" << std::endl; 32 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 33 | if (k == 20) 34 | { 35 | RhIO::Root.enableStreamingValue("test/paramBool"); 36 | RhIO::Root.enableStreamingStream("test/stream1"); 37 | } 38 | if (k == 30) 39 | { 40 | RhIO::Root.enableStreamingValue("test/test3/paramInt"); 41 | RhIO::Root.enableStreamingValue("test/paramFloat"); 42 | RhIO::Root.enableStreamingValue("test/test3/paramStr"); 43 | RhIO::Root.disableStreamingStream("test/stream1"); 44 | } 45 | if (k == 40) 46 | { 47 | RhIO::Root.disableStreamingValue("test/paramBool"); 48 | RhIO::Root.disableStreamingValue("test/test3/paramStr"); 49 | } 50 | if (k == 42) 51 | { 52 | RhIO::Root.enableStreamingFrame("test/frame1"); 53 | } 54 | if (k == 47) 55 | { 56 | RhIO::Root.disableStreamingFrame("test/frame1"); 57 | } 58 | if (RhIO::Root.frameIsStreaming("test/frame1")) 59 | { 60 | unsigned char* data = new unsigned char[3 * 300 * 200]; 61 | for (size_t i = 0; i < 3 * 300 * 200; i++) 62 | { 63 | data[i] = 0; 64 | } 65 | RhIO::Root.framePush("/test/frame1", 300, 200, data, 3 * 300 * 200); 66 | delete[] data; 67 | } 68 | } 69 | 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /Server/src/FrameNode.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RHIO_FRAMENODE_HPP 2 | #define RHIO_FRAMENODE_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "BaseNode.hpp" 12 | #include "Frame.hpp" 13 | 14 | namespace RhIO 15 | { 16 | /** 17 | * FrameNode 18 | * 19 | * Implemennt frame image streaming 20 | * for vision display feature for IONode 21 | */ 22 | class FrameNode : public BaseNode 23 | { 24 | public: 25 | /** 26 | * Inherit BaseNode constructor 27 | */ 28 | using BaseNode::BaseNode; 29 | 30 | /** 31 | * Custom assignement operator 32 | * (because std::mutex is non copyable) 33 | */ 34 | FrameNode& operator=(const FrameNode& node); 35 | 36 | /** 37 | * Return true if given frame name is registered 38 | */ 39 | bool frameExist(const std::string& name) const; 40 | 41 | /** 42 | * Return true if the given frame name is 43 | * currently streamed and watched by clients. 44 | */ 45 | bool frameIsStreaming(const std::string& name) const; 46 | 47 | /** 48 | * Return frame meta information 49 | * of given frame name 50 | */ 51 | const Frame& getFrame(const std::string& name) const; 52 | 53 | /** 54 | * Send to watchers the raw frame with 55 | * given name with given data of given size. 56 | * Frame width and height size are also given. 57 | * The given data is immediatly copied. 58 | */ 59 | void framePush(const std::string& name, const cv::Mat& frame, const std::string& encoding = ".jpg", 60 | std::chrono::steady_clock::time_point timestamp = std::chrono::steady_clock::now()); 61 | 62 | /** 63 | * Enable or disable (increase or decrease watchers 64 | * count) for given frame stream name 65 | */ 66 | void enableStreamingFrame(const std::string& name); 67 | void disableStreamingFrame(const std::string& name); 68 | 69 | /** 70 | * Register a new frame with given name, 71 | * textual description and format 72 | */ 73 | void newFrame(const std::string& name, const std::string& comment); 74 | 75 | /** 76 | * Return the relative name list 77 | * of all registered frames 78 | */ 79 | std::vector listFrames() const; 80 | 81 | private: 82 | /** 83 | * Container map for frame stream instance 84 | * and textual description 85 | */ 86 | std::map _frames; 87 | 88 | /** 89 | * Mutex protecting concurent stream creation 90 | */ 91 | mutable std::mutex _mutex; 92 | }; 93 | 94 | } // namespace RhIO 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /Shell/src/Terminal.cpp: -------------------------------------------------------------------------------- 1 | #include "Terminal.h" 2 | 3 | namespace RhIO 4 | { 5 | void Terminal::clear() 6 | { 7 | #ifdef HAS_COLORS 8 | printf(NRM); 9 | #endif 10 | } 11 | 12 | void Terminal::clearScreen() 13 | { 14 | printf("\033[2J"); 15 | } 16 | 17 | void Terminal::initCursor() 18 | { 19 | printf("\033[1;1H"); 20 | } 21 | 22 | void Terminal::setColor(std::string name, bool bold) 23 | { 24 | #ifdef HAS_COLORS 25 | if (name == "white") 26 | { 27 | printf(F_WHT, bold); 28 | } 29 | else if (name == "red") 30 | { 31 | printf(F_RED, bold); 32 | } 33 | else if (name == "green") 34 | { 35 | printf(F_GRN, bold); 36 | } 37 | else if (name == "yellow") 38 | { 39 | printf(F_YEL, bold); 40 | } 41 | else if (name == "blue") 42 | { 43 | printf(F_BLU, bold); 44 | } 45 | else if (name == "magenta") 46 | { 47 | printf(F_MAG, bold); 48 | } 49 | else if (name == "darkblue") 50 | { 51 | printf(F_CYN, bold); 52 | } 53 | else if (name == "grey") 54 | { 55 | printf(F_GRE, bold); 56 | } 57 | else 58 | { 59 | printf("\x1b[%dm", bold); 60 | } 61 | #endif 62 | } 63 | 64 | void Terminal::setBColor(std::string name, bool bold) 65 | { 66 | #ifdef HAS_COLORS 67 | if (name == "white") 68 | { 69 | printf(B_WHT, bold); 70 | } 71 | else if (name == "red") 72 | { 73 | printf(B_RED, bold); 74 | } 75 | else if (name == "green") 76 | { 77 | printf(B_GRN, bold); 78 | } 79 | else if (name == "yellow") 80 | { 81 | printf(B_YEL, bold); 82 | } 83 | else if (name == "blue") 84 | { 85 | printf(B_BLU, bold); 86 | } 87 | else if (name == "magenta") 88 | { 89 | printf(B_MAG, bold); 90 | } 91 | else if (name == "darkblue") 92 | { 93 | printf(B_CYN, bold); 94 | } 95 | else if (name == "grey") 96 | { 97 | printf(B_GRE, bold); 98 | } 99 | else 100 | { 101 | printf("\x1b[%dm", bold); 102 | } 103 | #endif 104 | } 105 | 106 | void Terminal::clearLine() 107 | { 108 | printf("%c[2K\r", 27); 109 | } 110 | 111 | // - Move the cursor forward N columns: 112 | // \033[C 113 | // - Move the cursor backward N columns: 114 | // \033[D 115 | 116 | void Terminal::cursorRight() 117 | { 118 | printf("\033[C"); 119 | } 120 | 121 | void Terminal::cursorLeft() 122 | { 123 | printf("\033[D"); 124 | } 125 | void Terminal::cursorNLeft(int n) 126 | { 127 | printf("\033[%dD", n); 128 | } 129 | void Terminal::cursorNRight(int n) 130 | { 131 | printf("\033[%dC", n); 132 | } 133 | } // namespace RhIO 134 | -------------------------------------------------------------------------------- /Server/Examples/exampleFrames.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "RhIO.hpp" 4 | #include 5 | 6 | int main() 7 | { 8 | RhIO::start(); 9 | // RhIO can also be used to stream real time image 10 | // frames from server to client. Image are non compressed 11 | // in raw RGB, BGR or YUV format. Each pixels is defined by 12 | // 24 bits, 8 bits per channel. 13 | 14 | // Simple RGB 300x200 image. 15 | size_t width = 300; 16 | size_t height = 200; 17 | cv::Mat frame(height, width, CV_8UC3, cv::Scalar(0, 0, 0)); 18 | 19 | // Frame have to be created before use. First argument is 20 | // frame relative new name. The other arguments are the 21 | // frame textual description, 22 | // and its pixel format (RGB, BGR or YUV). 23 | RhIO::Root.newFrame("frames/frame1", "description of frame1"); 24 | 25 | // All frames in a node can be listed by 26 | // a vector of frame names. 27 | std::vector list1 = RhIO::Root.child("frames").listFrames(); 28 | for (size_t i = 0; i < list1.size(); i++) 29 | { 30 | std::cout << "Frame name: " << list1[i] << std::endl; 31 | } 32 | 33 | int xPos = 0; 34 | while (true) 35 | { 36 | xPos += 1; 37 | if (xPos >= width) 38 | { 39 | xPos = 0; 40 | } 41 | for (int k = 0; k < xPos / 13; k++) 42 | { 43 | std::cout << " "; 44 | } 45 | std::cout << "|" << std::endl; 46 | 47 | for (int x = 0; x < width; x++) 48 | { 49 | for (int y = 0; y < height; y++) 50 | { 51 | cv::Vec3b rgb = frame.at(y, x); 52 | 53 | rgb.val[0] = 0; 54 | rgb.val[1] = 0; 55 | rgb.val[2] = 255; 56 | 57 | if (x == xPos) 58 | { 59 | rgb.val[0] = 255; 60 | } 61 | 62 | frame.at(y, x) = rgb; 63 | } 64 | } 65 | 66 | cv::imwrite("/tmp/source.png", frame); 67 | 68 | // For performance optimization, the requested frame 69 | // is generated only if a client is watching it. 70 | if (RhIO::Root.frameIsStreaming("frames/frame1")) 71 | { 72 | // The frame data is immedialy copied from given 73 | // memory pointer and given size. 74 | //(the given size should be 3*wifth*height). 75 | RhIO::Root.framePush("frames/frame1", frame); 76 | } 77 | usleep(10000); 78 | } 79 | 80 | // Meta frame information can be retrieve 81 | // using following method. 82 | std::cout << "Frame description: " << RhIO::Root.getFrame("path/in/tree/frame1").comment << std::endl; 83 | // std::cout << "Frame format: " << (int)RhIO::Root.getFrame("path/in/tree/frame1").format << std::endl; 84 | } 85 | -------------------------------------------------------------------------------- /Tests/testTree.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "RhIO.hpp" 4 | 5 | void printTree(const RhIO::IONode& node, std::string prefix = "") 6 | { 7 | std::cout << prefix << "--" << node.name() << std::endl; 8 | for (const auto& child : node.listChildren()) 9 | { 10 | printTree(node.child(child), prefix + " "); 11 | } 12 | } 13 | 14 | int main() 15 | { 16 | assert(RhIO::Root.name() == "ROOT"); 17 | assert(RhIO::Root.listChildren().size() == 0); 18 | assert(RhIO::Root.parent().name() == "ROOT"); 19 | assert(RhIO::Root.root().name() == "ROOT"); 20 | assert(RhIO::Root.childExist("test") == false); 21 | assert(RhIO::Root.childExist("test/test2/pouet") == false); 22 | assert(RhIO::Root.pwd() == ""); 23 | 24 | RhIO::Root.newChild("test"); 25 | assert(RhIO::Root.name() == "ROOT"); 26 | assert(RhIO::Root.listChildren().size() == 1); 27 | assert(RhIO::Root.parent().name() == "ROOT"); 28 | assert(RhIO::Root.root().name() == "ROOT"); 29 | assert(RhIO::Root.childExist("test") == true); 30 | assert(RhIO::Root.child("test").name() == "test"); 31 | assert(RhIO::Root.child("test").parent().name() == "ROOT"); 32 | assert(RhIO::Root.child("test").root().name() == "ROOT"); 33 | assert(RhIO::Root.child("test").listChildren().size() == 0); 34 | assert(RhIO::Root.child("test").pwd() == "test"); 35 | 36 | RhIO::Root.newChild("test2/pouet"); 37 | assert(RhIO::Root.name() == "ROOT"); 38 | assert(RhIO::Root.listChildren().size() == 2); 39 | assert(RhIO::Root.childExist("test") == true); 40 | assert(RhIO::Root.childExist("test2") == true); 41 | assert(RhIO::Root.childExist("test2/pouet") == true); 42 | assert(RhIO::Root.child("test").name() == "test"); 43 | assert(RhIO::Root.child("test").parent().name() == "ROOT"); 44 | assert(RhIO::Root.child("test").root().name() == "ROOT"); 45 | assert(RhIO::Root.child("test").listChildren().size() == 0); 46 | 47 | assert(RhIO::Root.child("test2").name() == "test2"); 48 | assert(RhIO::Root.child("test2").parent().name() == "ROOT"); 49 | assert(RhIO::Root.child("test2").root().name() == "ROOT"); 50 | assert(RhIO::Root.child("test2").listChildren().size() == 1); 51 | 52 | assert(RhIO::Root.child("test2/pouet").name() == "pouet"); 53 | assert(RhIO::Root.child("test2").child("pouet").name() == "pouet"); 54 | assert(RhIO::Root.child("test2/pouet").parent().name() == "test2"); 55 | assert(RhIO::Root.child("test2/pouet").root().name() == "ROOT"); 56 | assert(RhIO::Root.child("test2/pouet").listChildren().size() == 0); 57 | assert(RhIO::Root.child("test2/pouet").pwd() == "test2/pouet"); 58 | 59 | std::cout << "Display Tree" << std::endl; 60 | printTree(RhIO::Root); 61 | 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /Server/Examples/exampleIntro.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "RhIO.hpp" 3 | 4 | int main() 5 | { 6 | // RhIO is automatically started and initialized 7 | // at program startup before the main(). 8 | // The global main instance RhIO::IONode Root is defined 9 | // on startup and contains shared tree and all values, 10 | // commands and stream. 11 | // ZeroMQ Rep and Pub server are also start in their 12 | // own thread. 13 | 14 | // Values are created for each type with 15 | // newBool, newInt, newFloat, newStr 16 | // and providing a name 17 | RhIO::Root.newInt("firstValueInt"); 18 | 19 | // Values can then be update with 20 | // setBool, setInt, setFloat and setStr 21 | RhIO::Root.setInt("firstValueInt", 42); 22 | 23 | // Values can then be accessed with 24 | // getBool, getInt, getFloat and getStr 25 | std::cout << "firstValueInt: " << RhIO::Root.getInt("firstValueInt") << std::endl; 26 | 27 | // We can check if a value has been defined 28 | // by requesting its type which can be either 29 | // TypeBool, TypeInt, TypeFloat, TypeStr or NoValue. 30 | if (RhIO::Root.getValueType("firstValueInt") == RhIO::TypeInt) 31 | { 32 | std::cout << "firstValueInt is an Int" << std::endl; 33 | } 34 | else if (RhIO::Root.getValueType("firstValueInt") == RhIO::NoValue) 35 | { 36 | std::cout << "firstValueInt is not yet registered" << std::endl; 37 | } 38 | 39 | // A value can also be define with an 40 | // absolute path in the tree 41 | // Sub tree nodes are created if they 42 | // do not exist 43 | RhIO::Root.newBool("path/in/tree/secondValueBool"); 44 | 45 | // A specific tree node can be accessed or store 46 | // directly through a IONode pointer and 47 | // child() method 48 | RhIO::IONode* node = &(RhIO::Root.child("path/in")); 49 | 50 | // Value can then be retrieve with relative path 51 | std::cout << "secondValueFloat: " << node->getBool("tree/secondValueBool") << std::endl; 52 | 53 | // All children of a node can be retrieve with 54 | // listChildren(). A vector of string of relative node 55 | // name is returned. 56 | std::vector list1 = RhIO::Root.listChildren(); 57 | for (size_t i = 0; i < list1.size(); i++) 58 | { 59 | std::cout << "Child name: " << list1[i] << std::endl; 60 | } 61 | 62 | // For values, all relative value names can also be retrieved 63 | // in a vector of string for each types listValuesBool(), 64 | // listValuesInt(), listValuesFloat() and listValuesStr() 65 | std::vector list2 = node->child("tree").listValuesBool(); 66 | for (size_t i = 0; i < list2.size(); i++) 67 | { 68 | std::cout << "Values Bool name: " << list2[i] << std::endl; 69 | } 70 | 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /Shell/src/commands/Command.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "Shell.h" 9 | #include "Command.h" 10 | #include "Terminal.h" 11 | 12 | namespace RhIO 13 | { 14 | Command::~Command() 15 | { 16 | } 17 | 18 | void Command::setShell(Shell* shell_) 19 | { 20 | shell = shell_; 21 | } 22 | 23 | std::string Command::getUsage() 24 | { 25 | return ""; 26 | } 27 | 28 | void Command::errorUsage() 29 | { 30 | std::stringstream ss; 31 | ss << "Usage: " << getUsage(); 32 | throw std::runtime_error(ss.str()); 33 | } 34 | 35 | Node* Command::getNode(std::vector args) 36 | { 37 | std::string dir = ""; 38 | 39 | if (args.size()) 40 | { 41 | dir = args[0]; 42 | } 43 | 44 | Node* node = shell->getNode(dir); 45 | if (node == NULL) 46 | { 47 | std::stringstream ss; 48 | ss << "Unable to get node " << dir; 49 | throw std::runtime_error(ss.str()); 50 | } 51 | 52 | return node; 53 | } 54 | 55 | std::ostream* Command::getStream(std::vector& args) 56 | { 57 | int n = args.size(); 58 | if (n >= 2) 59 | { 60 | if (args[n - 2] == ">") 61 | { 62 | auto name = args.back(); 63 | args.pop_back(); 64 | args.pop_back(); 65 | if (ofs.is_open()) 66 | { 67 | ofs.close(); 68 | } 69 | ofs.open(name); 70 | ofs.precision(10); 71 | return &ofs; 72 | } 73 | } 74 | std::cout.precision(10); 75 | return &std::cout; 76 | } 77 | 78 | void Command::clearStream() 79 | { 80 | if (ofs.is_open()) 81 | { 82 | ofs.close(); 83 | ofs.close(); 84 | } 85 | } 86 | 87 | void Command::die() 88 | { 89 | dead = true; 90 | } 91 | 92 | int kbhit(void) 93 | { 94 | struct termios oldt, newt; 95 | int ch; 96 | int oldf; 97 | 98 | tcgetattr(STDIN_FILENO, &oldt); 99 | newt = oldt; 100 | newt.c_lflag &= ~(ICANON | ECHO); 101 | tcsetattr(STDIN_FILENO, TCSANOW, &newt); 102 | oldf = fcntl(STDIN_FILENO, F_GETFL, 0); 103 | fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); 104 | 105 | ch = getchar(); 106 | 107 | tcsetattr(STDIN_FILENO, TCSANOW, &oldt); 108 | fcntl(STDIN_FILENO, F_SETFL, oldf); 109 | 110 | if (ch != EOF) 111 | { 112 | ungetc(ch, stdin); 113 | return 1; 114 | } 115 | 116 | return 0; 117 | } 118 | 119 | int Command::waitChar() 120 | { 121 | while (!kbhit() && !dead) 122 | { 123 | usleep(1000); 124 | } 125 | 126 | if (dead) 127 | { 128 | return 1024; 129 | } 130 | 131 | return getchar(); 132 | } 133 | } // namespace RhIO 134 | -------------------------------------------------------------------------------- /Client/src/ClientSub.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RHIO_CLIENTSUB_HPP 2 | #define RHIO_CLIENTSUB_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace RhIO 12 | { 13 | /** 14 | * ClientSub 15 | */ 16 | class ClientSub 17 | { 18 | public: 19 | /** 20 | * Typedef for stream custom handlers for each type 21 | */ 22 | typedef std::function StreamBoolHandler; 23 | typedef std::function StreamIntHandler; 24 | typedef std::function StreamFloatHandler; 25 | typedef std::function StreamStrHandler; 26 | typedef std::function StreamFrameHandler; 27 | typedef std::function StreamErrorHandler; 28 | 29 | /** 30 | * Initialization with the bind 31 | * endpoint string 32 | * Starting subscriber thread 33 | */ 34 | ClientSub(const std::string& endpoint); 35 | 36 | /** 37 | * Stop running streaming thread 38 | */ 39 | ~ClientSub(); 40 | 41 | /** 42 | * Setup custom handler for value streaming of each 43 | * type Bool, Int, Float, Str, Stream 44 | * Default argument is an empty handler 45 | */ 46 | void setHandlerBool(StreamBoolHandler handler = StreamBoolHandler()); 47 | void setHandlerInt(StreamIntHandler handler = StreamIntHandler()); 48 | void setHandlerFloat(StreamFloatHandler handler = StreamFloatHandler()); 49 | void setHandlerStr(StreamStrHandler handler = StreamStrHandler()); 50 | void setHandlerStream(StreamStrHandler handler = StreamStrHandler()); 51 | void setHandlerFrame(StreamFrameHandler handler = StreamFrameHandler()); 52 | void setHandlerError(StreamErrorHandler handler = StreamErrorHandler()); 53 | 54 | private: 55 | /** 56 | * Mutex protecting update on 57 | * handler function 58 | */ 59 | std::mutex _mutex; 60 | 61 | /** 62 | * False if subscriber thread must be stop 63 | */ 64 | bool _isContinue; 65 | 66 | /** 67 | * Handle callback for stream values 68 | */ 69 | StreamBoolHandler _handlerBool; 70 | StreamIntHandler _handlerInt; 71 | StreamFloatHandler _handlerFloat; 72 | StreamStrHandler _handlerStr; 73 | StreamStrHandler _handlerStream; 74 | StreamFrameHandler _handlerFrame; 75 | StreamErrorHandler _handlerError; 76 | 77 | /** 78 | * Receiver thread 79 | */ 80 | std::thread _thread; 81 | 82 | /** 83 | * Receiver thread main loop 84 | */ 85 | void subscriberThread(const std::string& endpoint); 86 | }; 87 | 88 | } // namespace RhIO 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /Server/src/CommandNode.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "CommandNode.hpp" 3 | #include "RhIO.hpp" 4 | 5 | namespace RhIO 6 | { 7 | CommandNode& CommandNode::operator=(const CommandNode& node) 8 | { 9 | _commands = node._commands; 10 | _descriptions = node._descriptions; 11 | 12 | return *this; 13 | } 14 | 15 | bool CommandNode::commandExist(const std::string& name) const 16 | { 17 | // Forward to subtree 18 | std::string tmpName; 19 | CommandNode* child = BaseNode::forwardFunc(name, tmpName, false); 20 | if (child != nullptr) 21 | return child->commandExist(tmpName); 22 | 23 | std::lock_guard lock(_mutex); 24 | return (_commands.count(name) > 0); 25 | } 26 | 27 | std::string CommandNode::call(const std::string& name, const std::vector& arguments) 28 | { 29 | // Forward to subtree 30 | std::string tmpName; 31 | CommandNode* child = BaseNode::forwardFunc(name, tmpName, false); 32 | if (child != nullptr) 33 | return child->call(tmpName, arguments); 34 | 35 | std::lock_guard lock(_mutex); 36 | if (_commands.count(name) > 0) 37 | { 38 | return _commands.at(name)(arguments); 39 | } 40 | else 41 | { 42 | throw std::logic_error("RhIO unknown command name: " + name); 43 | } 44 | } 45 | 46 | std::string CommandNode::commandDescription(const std::string& name) const 47 | { 48 | // Forward to subtree 49 | std::string tmpName; 50 | CommandNode* child = BaseNode::forwardFunc(name, tmpName, false); 51 | if (child != nullptr) 52 | return child->commandDescription(tmpName); 53 | 54 | std::lock_guard lock(_mutex); 55 | if (_descriptions.count(name) > 0) 56 | { 57 | return _descriptions.at(name); 58 | } 59 | else 60 | { 61 | throw std::logic_error("RhIO unknown command name: " + name); 62 | } 63 | } 64 | 65 | void CommandNode::newCommand(const std::string& name, const std::string& comment, CommandFunc func) 66 | { 67 | // Forward to subtree 68 | std::string tmpName; 69 | CommandNode* child = BaseNode::forwardFunc(name, tmpName, true); 70 | if (child != nullptr) 71 | { 72 | child->newCommand(tmpName, comment, func); 73 | return; 74 | } 75 | 76 | std::lock_guard lock(_mutex); 77 | if (_commands.count(name) == 0) 78 | { 79 | _commands[name] = func; 80 | _descriptions[name] = comment; 81 | } 82 | else 83 | { 84 | throw std::logic_error("RhIO already register command name: " + name); 85 | } 86 | } 87 | 88 | std::vector CommandNode::listCommands() const 89 | { 90 | std::lock_guard lock(_mutex); 91 | std::vector list; 92 | for (const auto& c : _commands) 93 | { 94 | list.push_back(c.first); 95 | } 96 | 97 | return list; 98 | } 99 | 100 | } // namespace RhIO 101 | -------------------------------------------------------------------------------- /Tests/testImgs.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "RhIO.hpp" 7 | 8 | int main() 9 | { 10 | RhIO::Root.newFrame("test/frame1", "Some frame stream", RhIO::FrameFormat::RGB); 11 | RhIO::IONode& node = RhIO::Root.child("test"); 12 | node.newFrame("frame2", "Some frame stream", RhIO::FrameFormat::RGB); 13 | 14 | RhIO::Root.newInt("test/color")->defaultValue(0)->minimum(0)->maximum(255); 15 | RhIO::Root.newInt("test/line")->defaultValue(10)->minimum(0)->maximum(200); 16 | RhIO::Root.newInt("test/width")->defaultValue(300)->minimum(1)->maximum(800); 17 | 18 | size_t line = 50; 19 | while (true) 20 | { 21 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); 22 | 23 | const uint32_t width1 = 300; 24 | const uint32_t height1 = 200; 25 | const size_t size1 = 3 * width1 * height1; 26 | const uint32_t width2 = RhIO::Root.getInt("test/width"); 27 | const uint32_t height2 = 200; 28 | const size_t size2 = 3 * width2 * height2; 29 | unsigned char data1[size1]; 30 | unsigned char data2[size2]; 31 | 32 | for (size_t i = 0; i < size1; i++) 33 | { 34 | data1[i] = 0; 35 | } 36 | for (size_t i = 0; i < size2; i++) 37 | { 38 | data2[i] = 0; 39 | } 40 | for (size_t i = 0; i < height1; i++) 41 | { 42 | for (size_t j = 0; j < width1; j++) 43 | { 44 | if (i == (size_t)RhIO::Root.getInt("test/line") || i == 10 || i == 100) 45 | { 46 | data1[3 * (i * width1 + j) + 0] = 0; 47 | data1[3 * (i * width1 + j) + 1] = 255; 48 | data1[3 * (i * width1 + j) + 2] = 0; 49 | } 50 | else 51 | { 52 | data1[3 * (i * width1 + j) + 0] = 0; 53 | data1[3 * (i * width1 + j) + 1] = 0; 54 | data1[3 * (i * width1 + j) + 2] = 200; 55 | } 56 | } 57 | } 58 | for (size_t i = 0; i < height2; i++) 59 | { 60 | for (size_t j = 0; j < width2; j++) 61 | { 62 | if (i == line || i == 10 || i == 100) 63 | { 64 | data2[3 * (i * width2 + j) + 0] = 255; 65 | data2[3 * (i * width2 + j) + 1] = 0; 66 | data2[3 * (i * width2 + j) + 2] = 0; 67 | } 68 | else 69 | { 70 | data2[3 * (i * width2 + j) + 0] = 0; 71 | data2[3 * (i * width2 + j) + 1] = RhIO::Root.getInt("test/color"); 72 | data2[3 * (i * width2 + j) + 2] = 0; 73 | } 74 | } 75 | } 76 | if (RhIO::Root.frameIsStreaming("test/frame1")) 77 | { 78 | RhIO::Root.framePush("test/frame1", width1, height1, data1, size1); 79 | } 80 | if (RhIO::Root.frameIsStreaming("test/frame2")) 81 | { 82 | RhIO::Root.framePush("test/frame2", width2, height2, data2, size2); 83 | } 84 | line++; 85 | if (line > 100) 86 | { 87 | line = 10; 88 | } 89 | } 90 | 91 | return 0; 92 | } 93 | -------------------------------------------------------------------------------- /Server/Examples/exampleStreamsAndCommands.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "RhIO.hpp" 3 | 4 | int main() 5 | { 6 | // Like values, streams are associated with a name 7 | // and stored into tree nodes. They are representing 8 | // a write-only output string stream (std:ostream) 9 | // equivalent to standart C++ iostream (cout, cerr). 10 | // Their main purpose is debug. All streams are 11 | // received by network remote client in order to be displayed. 12 | 13 | // Like values, they have to be created before use. 14 | // First argument is a relative new name for the steam 15 | // and the second is an helpful string descripbing the 16 | // purpose of the stream. 17 | RhIO::Root.newStream("path/in/tree/stream1", "description of stream1"); 18 | RhIO::Root.child("path/in/tree").newStream("stream2", "description of stream2"); 19 | 20 | // All streams in a node can also be listed by 21 | // a vector of stream names. 22 | std::vector list1 = RhIO::Root.child("path/in/tree").listStreams(); 23 | for (size_t i = 0; i < list1.size(); i++) 24 | { 25 | std::cout << "Stream name: " << list1[i] << std::endl; 26 | } 27 | 28 | // Finnaly, write to a stream is equivalent as writing 29 | // into std::cout or std::cerr. Data are sent to client 30 | // once the stream is flused (for example by std::endl). 31 | RhIO::Root.out("path/in/tree/stream1") << "This is a debug message" << std::endl; 32 | RhIO::Root.child("path/in/tree").out("stream2") << "Print test: " << 42 << " " << 3.14 << std::endl; 33 | 34 | // Commands are server side function which can be called 35 | // by remote client. They are associated with a name and stored 36 | // inside tree nodes. All arguments are given through a vector 37 | // of string and the function have also to return a string 38 | // as return value type. 39 | 40 | // First argument is a new relative name. Second argument 41 | // is also an helpful description message and the third argument 42 | // is the function. The function can be a C++11 lambda function 43 | // or a more classical function pointer with following prototype: 44 | // std::string function(const std::vector&); 45 | RhIO::Root.newCommand("path/in/tree/command1", "description of command1", 46 | [](const std::vector& args) -> std::string { return "Hello " + args[0]; }); 47 | 48 | // Commands can be listed in the same way as streams and values 49 | std::vector list2 = RhIO::Root.child("path/in/tree").listCommands(); 50 | for (size_t i = 0; i < list2.size(); i++) 51 | { 52 | std::cout << "Commands name: " << list2[i] << std::endl; 53 | } 54 | 55 | // Finally, a command can also be called from server side 56 | // by providing textual arguments. 57 | // First argument is the called command name in tree and the second 58 | // argument is the vector of string arguments. 59 | std::cout << RhIO::Root.call("path/in/tree/command1", { "World !" }) << std::endl; 60 | 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /Server/src/Filesystem.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "Filesystem.hpp" 6 | #include "IONode.hpp" 7 | 8 | namespace RhIO 9 | { 10 | bool isDirectory(const std::string& path) 11 | { 12 | DIR* dir = opendir(path.c_str()); 13 | if (dir == nullptr) 14 | { 15 | return false; 16 | } 17 | else 18 | { 19 | closedir(dir); 20 | return true; 21 | } 22 | } 23 | 24 | std::vector listDirectories(const std::string& path) 25 | { 26 | DIR* dir = opendir(path.c_str()); 27 | if (dir == nullptr) 28 | { 29 | throw std::runtime_error("RhIO unable to open path directory: " + path); 30 | } 31 | 32 | std::vector list; 33 | struct dirent* dirList; 34 | while ((dirList = readdir(dir)) != nullptr) 35 | { 36 | if (dirList->d_type == DT_DIR) 37 | { 38 | list.push_back(std::string(dirList->d_name)); 39 | } 40 | } 41 | 42 | closedir(dir); 43 | return list; 44 | } 45 | 46 | std::vector listFiles(const std::string& path) 47 | { 48 | DIR* dir = opendir(path.c_str()); 49 | if (dir == nullptr) 50 | { 51 | throw std::runtime_error("RhIO unable to open path directory: " + path); 52 | } 53 | 54 | std::vector list; 55 | struct dirent* dirList; 56 | while ((dirList = readdir(dir)) != nullptr) 57 | { 58 | if (dirList->d_type == DT_REG) 59 | { 60 | list.push_back(std::string(dirList->d_name)); 61 | } 62 | } 63 | 64 | closedir(dir); 65 | return list; 66 | } 67 | 68 | void createDirectory(const std::string& path, const std::string& name) 69 | { 70 | std::string newPath; 71 | if (name.length() > 0 && name[0] != separator && path.length() > 0 && path[path.length() - 1] != separator) 72 | { 73 | newPath = std::string(path + separator + name); 74 | } 75 | else 76 | { 77 | newPath = std::string(path + name); 78 | } 79 | 80 | // Build path from deepest requested 81 | // folder to "root" requested folder 82 | std::vector parts; 83 | size_t pos = newPath.length(); 84 | while (true) 85 | { 86 | parts.push_back(newPath.substr(0, pos)); 87 | pos = newPath.find_last_of("/", pos - 1); 88 | if (pos == 0 || pos == std::string::npos) 89 | { 90 | break; 91 | } 92 | } 93 | 94 | // Find all non existing directories in path 95 | int index = 0; 96 | for (; index < (int)parts.size(); index++) 97 | { 98 | if (isDirectory(parts[index])) 99 | { 100 | break; 101 | } 102 | } 103 | index--; 104 | if (index < 0) 105 | { 106 | return; 107 | } 108 | // Create all non existing directories 109 | for (; index >= 0; index--) 110 | { 111 | int result = mkdir(parts[index].c_str(), 0777); 112 | if (result != 0) 113 | { 114 | throw std::runtime_error("RhIO unable to create directory in: " + path); 115 | } 116 | } 117 | } 118 | 119 | } // namespace RhIO 120 | -------------------------------------------------------------------------------- /Shell/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "commands/HelpCommand.h" 3 | #include "commands/LsCommand.h" 4 | #include "commands/ErrCommand.h" 5 | #include "commands/WatchCommand.h" 6 | #include "commands/CdCommand.h" 7 | #include "commands/ClearCommand.h" 8 | #include "commands/LogCommand.h" 9 | #include "commands/SyncCommand.h" 10 | #include "commands/PlotCommand.h" 11 | #include "commands/Plot2DCommand.h" 12 | #include "commands/Plot3DCommand.h" 13 | #include "commands/DiffCommand.h" 14 | #include "commands/LoadCommand.h" 15 | #include "commands/SaveCommand.h" 16 | #include "commands/TreeCommand.h" 17 | #include "commands/CatCommand.h" 18 | #include "commands/ViewCommand.h" 19 | #ifdef HAS_LOGIMG 20 | #include "commands/LogImgCommand.h" 21 | #endif 22 | #include "commands/RepeatCommand.h" 23 | #include "commands/DelayCommand.h" 24 | #include "commands/PadCommand.h" 25 | #ifdef HAS_CURSES 26 | #include "commands/TuneCommand.h" 27 | #endif 28 | #include "Shell.h" 29 | 30 | using namespace RhIO; 31 | 32 | Shell* shell = NULL; 33 | 34 | void shell_quit(int s) 35 | { 36 | (void)s; 37 | if (shell != NULL) 38 | { 39 | if (shell->quit()) 40 | { 41 | exit(1); 42 | } 43 | } 44 | } 45 | 46 | int main(int argc, char* argv[]) 47 | { 48 | signal(SIGINT, shell_quit); 49 | 50 | std::string server = "localhost"; 51 | 52 | if (argc > 1) 53 | { 54 | server = std::string(argv[1]); 55 | } 56 | 57 | shell = new Shell(server); 58 | shell->registerCommand(new HelpCommand); 59 | shell->registerCommand(new LsCommand); 60 | shell->registerCommand(new CdCommand); 61 | shell->registerCommand(new ClearCommand); 62 | shell->registerCommand(new WatchCommand); 63 | shell->registerCommand(new LogCommand); 64 | shell->registerCommand(new SyncCommand); 65 | shell->registerCommand(new PlotCommand); 66 | shell->registerCommand(new Plot2DCommand); 67 | shell->registerCommand(new Plot3DCommand); 68 | shell->registerCommand(new DiffCommand); 69 | shell->registerCommand(new LoadCommand); 70 | shell->registerCommand(new SaveCommand); 71 | shell->registerCommand(new TreeCommand); 72 | shell->registerCommand(new CatCommand); 73 | shell->registerCommand(new ViewCommand); 74 | shell->registerCommand(new ErrCommand); 75 | #ifdef HAS_LOGIMG 76 | shell->registerCommand(new LogImgCommand); 77 | #endif 78 | shell->registerCommand(new RepeatCommand); 79 | shell->registerCommand(new DelayCommand); 80 | shell->registerCommand(new PadCommand); 81 | #ifdef HAS_CURSES 82 | shell->registerCommand(new TuneCommand); 83 | #endif 84 | 85 | shell->addAlias("ll", "ls"); 86 | shell->addAlias("rep", "repeat"); 87 | shell->addAlias("del", "delay"); 88 | 89 | if (argc > 2) 90 | { 91 | std::string args = ""; 92 | for (int k = 2; k < argc; k++) 93 | { 94 | args += argv[k]; 95 | args += " "; 96 | } 97 | shell->run(args); 98 | } 99 | else 100 | { 101 | shell->run(); 102 | } 103 | shell->quit(); 104 | delete shell; 105 | } 106 | -------------------------------------------------------------------------------- /Tests/testPersistLoad.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "RhIO.hpp" 4 | #include "Filesystem.hpp" 5 | 6 | void printTree(const RhIO::IONode& node, std::string prefix = "") 7 | { 8 | std::cout << prefix << "--" << node.name() << std::endl; 9 | for (const auto& param : node.listValuesBool()) 10 | { 11 | std::cout << prefix << "(bool) " << param << std::endl; 12 | } 13 | for (const auto& param : node.listValuesInt()) 14 | { 15 | std::cout << prefix << "(int) " << param << std::endl; 16 | } 17 | for (const auto& param : node.listValuesFloat()) 18 | { 19 | std::cout << prefix << "(float) " << param << std::endl; 20 | } 21 | for (const auto& param : node.listValuesStr()) 22 | { 23 | std::cout << prefix << "(str) " << param << std::endl; 24 | } 25 | for (const auto& child : node.listChildren()) 26 | { 27 | printTree(node.child(child), prefix + " "); 28 | } 29 | } 30 | 31 | int main() 32 | { 33 | RhIO::Root.newChild("test2"); 34 | RhIO::Root.load("/tmp/testRhIO"); 35 | 36 | assert(RhIO::Root.getValueType("paramBool") == RhIO::TypeBool); 37 | assert(RhIO::Root.getValueType("test/paramInt") == RhIO::TypeInt); 38 | assert(RhIO::Root.getValueType("test/paramFloat") == RhIO::TypeFloat); 39 | assert(RhIO::Root.getValueType("test2/test3/paramStr") == RhIO::TypeStr); 40 | 41 | assert(RhIO::Root.getBool("paramBool") == true); 42 | assert(RhIO::Root.getInt("test/paramInt") == 42); 43 | assert(RhIO::Root.getFloat("test/paramFloat") == 3.14); 44 | assert(RhIO::Root.getStr("test2/test3/paramStr") == "off"); 45 | 46 | assert(RhIO::Root.getValueBool("paramBool").name == "paramBool"); 47 | assert(RhIO::Root.getValueInt("test/paramInt").name == "paramInt"); 48 | assert(RhIO::Root.getValueFloat("test/paramFloat").name == "paramFloat"); 49 | assert(RhIO::Root.getValueStr("test2/test3/paramStr").name == "paramStr"); 50 | 51 | assert(RhIO::Root.getValueBool("paramBool").persisted == false); 52 | assert(RhIO::Root.getValueInt("test/paramInt").persisted == false); 53 | assert(RhIO::Root.getValueFloat("test/paramFloat").persisted == false); 54 | assert(RhIO::Root.getValueStr("test2/test3/paramStr").persisted == false); 55 | 56 | assert(RhIO::Root.getValueBool("paramBool").comment == "bool parameter"); 57 | assert(RhIO::Root.getValueInt("test/paramInt").comment == ""); 58 | assert(RhIO::Root.getValueFloat("test/paramFloat").comment == "float parameter"); 59 | assert(RhIO::Root.getValueStr("test2/test3/paramStr").comment == "str parameter"); 60 | 61 | assert(RhIO::Root.getValueBool("paramBool").hasMin == false); 62 | assert(RhIO::Root.getValueBool("paramBool").hasMax == false); 63 | assert(RhIO::Root.getValueInt("test/paramInt").hasMin == false); 64 | assert(RhIO::Root.getValueInt("test/paramInt").hasMax == false); 65 | assert(RhIO::Root.getValueFloat("test/paramFloat").hasMin == false); 66 | assert(RhIO::Root.getValueFloat("test/paramFloat").hasMax == false); 67 | assert(RhIO::Root.getValueStr("test2/test3/paramStr").hasMin == false); 68 | assert(RhIO::Root.getValueStr("test2/test3/paramStr").hasMax == false); 69 | 70 | RhIO::Root.setInt("test/paramInt", 0); 71 | RhIO::Root.setFloat("test/paramFloat", 0.0); 72 | assert(RhIO::Root.getValueBool("paramBool").valuePersisted == true); 73 | assert(RhIO::Root.getValueInt("test/paramInt").valuePersisted == 42); 74 | assert(RhIO::Root.getValueFloat("test/paramFloat").valuePersisted == 3.14); 75 | assert(RhIO::Root.getValueStr("test2/test3/paramStr").valuePersisted == "off"); 76 | 77 | std::cout << "Printing tree:" << std::endl; 78 | printTree(RhIO::Root); 79 | 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /Server/Examples/exampleValues.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "RhIO.hpp" 3 | 4 | int main() 5 | { 6 | // Values are typed variables which can 7 | // be get or set, accessed and update across 8 | // network. Supported types are Bool, Int, 9 | // Float and String. 10 | // Values (any number for each type) are associated 11 | // with a textual name and are stored into any tree node. 12 | 13 | // On creation, optionnal meta information 14 | // can be added to values by using the ValueBuilder 15 | // returned by newBool, newInt, newFloat and newStr 16 | RhIO::Root 17 | .newInt("path/paramInt") 18 | // Set the value non temporary. 19 | // When save is called, this value will be writen 20 | // in config files 21 | // Default is false. 22 | ->persisted(true) 23 | // Optional meta are chained. 24 | // A description text message describing 25 | // the value use. 26 | // Default is empty string. 27 | ->comment("this is an kind and helpful description") 28 | // Set a default value at initialization. 29 | // Override by manual set or config files value. 30 | // Default is false, 0 or empty string. 31 | ->defaultValue(42) 32 | // Set optional minimum value 33 | // Default is no minimum defined 34 | ->minimum(-2) 35 | // Set optional maximum value 36 | // Default is no minimum defined 37 | ->maximum(100); 38 | 39 | // Direct access to the structure 40 | // with value, name and meta information 41 | // Since this call return a reference, this is 42 | // not thread safe 43 | std::cout << "paramInt name: " << RhIO::Root.getValueInt("path/paramInt").name << std::endl; 44 | std::cout << "paramInt value: " << RhIO::Root.getValueInt("path/paramInt").value << std::endl; 45 | std::cout << "paramInt is persisted: " << RhIO::Root.getValueInt("path/paramInt").persisted << std::endl; 46 | std::cout << "paramInt has minimum: " << RhIO::Root.getValueInt("path/paramInt").hasMin << std::endl; 47 | std::cout << "paramInt minimum value: " << RhIO::Root.getValueInt("path/paramInt").min << std::endl; 48 | std::cout << "paramInt has maximum: " << RhIO::Root.getValueInt("path/paramInt").hasMax << std::endl; 49 | std::cout << "paramInt minimum value: " << RhIO::Root.getValueInt("path/paramInt").max << std::endl; 50 | 51 | // When a value is set, an internal timestamp is update. 52 | // If the real value update has occured before the call, a timestamp 53 | //(std::chrono::time_point) can be given. 54 | RhIO::Root.setInt("path/paramInt", 42, false, std::chrono::steady_clock::now()); 55 | 56 | // The entire tree hierarchy and all persisted values 57 | //(persisted flag set to true) can be saved into a directory 58 | // with save() call. A path to a folder is given as argument. 59 | // If the directiry or the path to the directory does not exist, 60 | // needed folders are created. 61 | RhIO::Root.save("/tmp/testRhIOExample"); 62 | 63 | // Each Node can also save individually its subtree 64 | // into given path in same way as Root. 65 | RhIO::Root.child("path").save("/tmp/testRhIOExample/path"); 66 | 67 | // Load() operation is the dual of save(). The given directory 68 | // path is read and the tree and value are created or updated from 69 | // config file. 70 | RhIO::Root.load("/tmp/testRhIOExample"); 71 | 72 | // Same restricted on a node 73 | RhIO::Root.child("path").load("/tmp/testRhIOExample/path"); 74 | 75 | // A callback can be set to be 76 | // trigger on value update 77 | RhIO::Root.setCallbackInt("path/paramInt", [](int val) { std::cout << "Callback on set: " << val << std::endl; }); 78 | RhIO::Root.setInt("path/paramInt", 5); 79 | 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /Tests/testThreadSafeTree.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "RhIO.hpp" 7 | 8 | void printTree(const RhIO::IONode& node, std::string prefix = "") 9 | { 10 | std::cout << prefix << "--" << node.name() << std::endl; 11 | for (const auto& child : node.listChildren()) 12 | { 13 | printTree(node.child(child), prefix + " "); 14 | } 15 | } 16 | 17 | void function1() 18 | { 19 | RhIO::Root.newChild("l1"); 20 | RhIO::Root.newChild("l2"); 21 | RhIO::Root.newChild("l3"); 22 | RhIO::Root.newChild("l4"); 23 | RhIO::Root.newChild("l5"); 24 | RhIO::Root.newChild("l6"); 25 | RhIO::Root.newChild("l7"); 26 | RhIO::Root.newChild("l8"); 27 | RhIO::Root.newChild("l9"); 28 | } 29 | 30 | void function2() 31 | { 32 | RhIO::Root.newChild("l9/ll1"); 33 | RhIO::Root.newChild("l8/ll1"); 34 | RhIO::Root.newChild("l7/ll1"); 35 | RhIO::Root.newChild("l6/ll1"); 36 | RhIO::Root.newChild("l5/ll1"); 37 | RhIO::Root.newChild("l4/ll1"); 38 | RhIO::Root.newChild("l3/ll1"); 39 | RhIO::Root.newChild("l2/ll1"); 40 | RhIO::Root.newChild("l1/ll1"); 41 | } 42 | 43 | void function3() 44 | { 45 | RhIO::Root.newChild("l1/ll1/lll1"); 46 | RhIO::Root.newChild("l2/ll1/lll1"); 47 | RhIO::Root.newChild("l3/ll1/lll1"); 48 | RhIO::Root.newChild("l4/ll1/lll1"); 49 | RhIO::Root.newChild("l5/ll1/lll1"); 50 | RhIO::Root.newChild("l6/ll1/lll1"); 51 | RhIO::Root.newChild("l7/ll1/lll1"); 52 | RhIO::Root.newChild("l8/ll1/lll1"); 53 | RhIO::Root.newChild("l9/ll1/lll1"); 54 | } 55 | 56 | void function4() 57 | { 58 | std::vector list; 59 | 60 | std::vector tmp; 61 | tmp = RhIO::Root.listChildren(); 62 | for (const auto n : tmp) 63 | { 64 | list.push_back("1: " + n); 65 | } 66 | tmp = RhIO::Root.listChildren(); 67 | for (const auto n : tmp) 68 | { 69 | list.push_back("2: " + n); 70 | } 71 | tmp = RhIO::Root.listChildren(); 72 | for (const auto n : tmp) 73 | { 74 | list.push_back("3: " + n); 75 | } 76 | 77 | for (size_t i = 0; i < list.size(); i++) 78 | { 79 | std::cout << "- " << list[i] << std::endl; 80 | } 81 | } 82 | 83 | void function5() 84 | { 85 | std::vector list; 86 | if (RhIO::Root.childExist("l1/ll1")) 87 | { 88 | list.push_back(RhIO::Root.child("l1/ll1").name()); 89 | } 90 | if (RhIO::Root.childExist("l2/ll1")) 91 | { 92 | list.push_back(RhIO::Root.child("l2/ll1").name()); 93 | } 94 | if (RhIO::Root.childExist("l3/ll1")) 95 | { 96 | list.push_back(RhIO::Root.child("l3/ll1").name()); 97 | } 98 | if (RhIO::Root.childExist("l4/ll1")) 99 | { 100 | list.push_back(RhIO::Root.child("l4/ll1").name()); 101 | } 102 | if (RhIO::Root.childExist("l5/ll1")) 103 | { 104 | list.push_back(RhIO::Root.child("l5/ll1").name()); 105 | } 106 | if (RhIO::Root.childExist("l6/ll1")) 107 | { 108 | list.push_back(RhIO::Root.child("l6/ll1").name()); 109 | } 110 | if (RhIO::Root.childExist("l7/ll1")) 111 | { 112 | list.push_back(RhIO::Root.child("l7/ll1").name()); 113 | } 114 | for (size_t i = 0; i < list.size(); i++) 115 | { 116 | std::cout << "- " << list[i] << std::endl; 117 | } 118 | } 119 | 120 | int main() 121 | { 122 | std::thread t1(function1); 123 | std::thread t2(function2); 124 | std::thread t3(function3); 125 | std::thread t4(function4); 126 | std::thread t5(function5); 127 | 128 | t1.join(); 129 | t2.join(); 130 | t3.join(); 131 | t4.join(); 132 | t5.join(); 133 | 134 | std::cout << "Display Tree" << std::endl; 135 | printTree(RhIO::Root); 136 | 137 | return 0; 138 | } 139 | -------------------------------------------------------------------------------- /Shell/src/Node.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace RhIO 8 | { 9 | class Node; 10 | 11 | /** 12 | * A node value is a representation of a value that belongs to a node 13 | */ 14 | class NodeValue 15 | { 16 | public: 17 | NodeValue(); 18 | NodeValue(Node* node_, ValueBase* value_); 19 | 20 | /** 21 | * Pointer to the corresponding ValueBase 22 | */ 23 | ValueBase* value; 24 | 25 | /** 26 | * Node that this value belongs to 27 | */ 28 | Node* node; 29 | 30 | /** 31 | * Returns the full name of the node, this can be used to 32 | * set or get the node with the server 33 | */ 34 | std::string getName(); 35 | }; 36 | 37 | class NodeItem 38 | { 39 | public: 40 | Node* node; 41 | std::string name; 42 | std::string desc; 43 | 44 | std::string getPath(); 45 | std::string getName(); 46 | }; 47 | 48 | class NodeCommand : public NodeItem 49 | { 50 | }; 51 | 52 | class NodeStream : public NodeItem 53 | { 54 | }; 55 | 56 | class NodeFrame : public NodeItem 57 | { 58 | }; 59 | 60 | class Node 61 | { 62 | public: 63 | Node(ClientReq* client, std::string Path); 64 | ~Node(); 65 | 66 | /** 67 | * Getting the path of the node 68 | */ 69 | std::string getPath(); 70 | 71 | /** 72 | * Accessing children 73 | */ 74 | Node* getChild(std::string name, bool loadIt = true); 75 | Node* getParent(); 76 | NodeValue getNodeValue(std::string name); 77 | std::vector getCommands(); 78 | std::vector getStreams(); 79 | std::vector getFrames(); 80 | std::vector getChildren(); 81 | 82 | /** 83 | * Getting all nodes 84 | * 85 | * /!\ Warning: the NodeValue will contains pointers to the elements 86 | * that are in the above vectors, you should not change these vectors 87 | * while iterating on this. /!\ 88 | */ 89 | std::vector getAll(); 90 | 91 | /** 92 | * Helpers to get a value as corresponding subtype, returns NULL 93 | * if the type is not an instance (can be used to test and/or access 94 | * fields) 95 | */ 96 | static ValueBool* asBool(ValueBase* value); 97 | static ValueInt* asInt(ValueBase* value); 98 | static ValueFloat* asFloat(ValueBase* value); 99 | static ValueStr* asString(ValueBase* value); 100 | 101 | /** 102 | * Converts a value base to a string representing it 103 | */ 104 | static std::string toString(ValueBase* value); 105 | 106 | /** 107 | * Converts a value to a number representing it 108 | */ 109 | static double toNumber(ValueBase* value); 110 | 111 | /** 112 | * Getting a string corresponding to the typpe of the value 113 | */ 114 | static std::string getType(ValueBase* value); 115 | 116 | /** 117 | * Tells if the value has changed (if value is different from persisted 118 | * value) 119 | */ 120 | static bool isDiff(ValueBase* value); 121 | 122 | /** 123 | * Getting a string representing the persisted value of a base 124 | */ 125 | static std::string persistedToString(ValueBase* value); 126 | 127 | protected: 128 | /** 129 | * All the values of the node and its children 130 | */ 131 | std::vector bools; 132 | std::vector ints; 133 | std::vector floats; 134 | std::vector strings; 135 | std::map children; 136 | std::vector commands; 137 | std::vector streams; 138 | std::vector frames; 139 | 140 | /** 141 | * Parent node, NULL if root 142 | */ 143 | Node* parent; 144 | std::string name, slashed; 145 | ClientReq* client; 146 | }; 147 | } // namespace RhIO 148 | -------------------------------------------------------------------------------- /Server/src/IONode.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RHIO_IONODE_HPP 2 | #define RHIO_IONODE_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "ValueNode.hpp" 9 | #include "CommandNode.hpp" 10 | #include "StreamNode.hpp" 11 | #include "FrameNode.hpp" 12 | 13 | namespace RhIO 14 | { 15 | /** 16 | * Tree name separator 17 | */ 18 | constexpr char separator = '/'; 19 | 20 | /** 21 | * IONode 22 | * 23 | * Main class representing an Node in 24 | * virtual input/output hierarchy name tree 25 | * with children Nodes 26 | */ 27 | class IONode final : public ValueNode, public CommandNode, public StreamNode, public FrameNode 28 | { 29 | public: 30 | /** 31 | * Empty initialization 32 | */ 33 | IONode(); 34 | 35 | /** 36 | * Initialization with node name and parent 37 | */ 38 | IONode(const std::string& name, IONode* parent); 39 | 40 | /** 41 | * Destructor 42 | */ 43 | ~IONode(); 44 | 45 | /** 46 | * Return Node name 47 | */ 48 | const std::string& name() const; 49 | 50 | /** 51 | * Return absolute node name 52 | */ 53 | const std::string& pwd() const; 54 | 55 | /** 56 | * Return Node parent and return self 57 | * if this is the root 58 | */ 59 | const IONode& parent() const; 60 | IONode& parent(); 61 | 62 | /** 63 | * Return Root Node 64 | */ 65 | const IONode& root() const; 66 | IONode& root(); 67 | 68 | /** 69 | * Return true if given subnode exist 70 | */ 71 | bool childExist(const std::string& name) const; 72 | 73 | /** 74 | * Find and return child Node with given relative name 75 | * Throw logic_error exception if the asked node name 76 | * does not exist 77 | */ 78 | const IONode& child(const std::string& name) const; 79 | IONode& child(const std::string& name); 80 | 81 | /** 82 | * Create a new child or complete branch with 83 | * given relative name 84 | * Do nothing if given hierarchy already exist 85 | */ 86 | void newChild(const std::string& name); 87 | 88 | /** 89 | * Return a list of Node children relative names 90 | */ 91 | std::vector listChildren() const; 92 | 93 | /** 94 | * Save recursively the subtree into given 95 | * path directory 96 | */ 97 | void save(const std::string& path); 98 | 99 | /** 100 | * Load recursively the subtree into given 101 | * path directory 102 | */ 103 | void load(const std::string& path); 104 | 105 | private: 106 | /** 107 | * Node name 108 | */ 109 | std::string _name; 110 | 111 | /** 112 | * Node absolute name 113 | */ 114 | std::string _pwd; 115 | 116 | /** 117 | * Pointer to parent Node 118 | * nullptr if root 119 | */ 120 | IONode* _parent; 121 | 122 | /** 123 | * Children Nodes container pointer 124 | * (pointer to prevent address change when 125 | * container update) 126 | */ 127 | std::map _children; 128 | 129 | /** 130 | * Mutex protecting concurent branch children 131 | * modification 132 | */ 133 | mutable std::mutex _mutex; 134 | 135 | /** 136 | * Copy and assignment operator 137 | * should not called by user. 138 | * (Update BaseNode forwardFunc) 139 | */ 140 | IONode(const IONode& node); 141 | IONode& operator=(const IONode& node); 142 | 143 | /** 144 | * If given name is referring to this Node, nullptr 145 | * is returned. 146 | * If given name is referring to a child Node, a pointer 147 | * to this Node is returned and newName is set to the 148 | * given name converted relatively to returned Node. 149 | * If given name is invalid, throw logic_error exception. 150 | * unless createBranch is true. Missing Nodes are then 151 | * created. 152 | */ 153 | IONode* forwardChildren(const std::string& name, std::string& newName, bool createBranch); 154 | }; 155 | 156 | } // namespace RhIO 157 | 158 | #endif 159 | -------------------------------------------------------------------------------- /Common/src/DataBuffer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "DataBuffer.hpp" 5 | 6 | namespace RhIO 7 | { 8 | DataBuffer::DataBuffer(void* data, size_t size) : _data((uint8_t*)data), _size(size), _offset(0) 9 | { 10 | } 11 | 12 | size_t DataBuffer::size() const 13 | { 14 | return _size; 15 | } 16 | size_t DataBuffer::offset() const 17 | { 18 | return _offset; 19 | } 20 | 21 | void DataBuffer::writeType(uint8_t val) 22 | { 23 | if (_offset + sizeof(uint8_t) > _size) 24 | { 25 | throw std::logic_error("RhIO buffer size overflow"); 26 | } 27 | 28 | uint8_t* p = (uint8_t*)(_data + _offset); 29 | *p = val; 30 | _offset += sizeof(uint8_t); 31 | } 32 | void DataBuffer::writeBool(bool val) 33 | { 34 | if (_offset + sizeof(uint8_t) > _size) 35 | { 36 | throw std::logic_error("RhIO buffer size overflow"); 37 | } 38 | 39 | uint8_t* p = _data + _offset; 40 | *p = (val) ? 1 : 0; 41 | _offset += sizeof(uint8_t); 42 | } 43 | void DataBuffer::writeInt(int64_t val) 44 | { 45 | if (_offset + sizeof(int64_t) > _size) 46 | { 47 | throw std::logic_error("RhIO buffer size overflow"); 48 | } 49 | 50 | int64_t* p = (int64_t*)(_data + _offset); 51 | *p = val; 52 | _offset += sizeof(int64_t); 53 | } 54 | void DataBuffer::writeFloat(double val) 55 | { 56 | if (_offset + sizeof(double) > _size) 57 | { 58 | throw std::logic_error("RhIO buffer size overflow"); 59 | } 60 | 61 | double* p = (double*)(_data + _offset); 62 | *p = val; 63 | _offset += sizeof(double); 64 | } 65 | void DataBuffer::writeStr(const std::string& val) 66 | { 67 | writeInt(val.length()); 68 | 69 | if (_offset + val.length() > _size) 70 | { 71 | throw std::logic_error("RhIO buffer size overflow"); 72 | } 73 | 74 | memcpy(_data + _offset, val.c_str(), val.length()); 75 | _offset += val.length(); 76 | } 77 | void DataBuffer::writeData(const unsigned char* data, size_t size) 78 | { 79 | writeInt(size); 80 | 81 | if (_offset + size > _size) 82 | { 83 | throw std::logic_error("RhIO buffer size overflow"); 84 | } 85 | 86 | memcpy(_data + _offset, data, size); 87 | _offset += size; 88 | } 89 | 90 | uint8_t DataBuffer::readType() 91 | { 92 | if (_offset + sizeof(uint8_t) > _size) 93 | { 94 | throw std::logic_error("RhIO buffer size overflow"); 95 | } 96 | 97 | uint8_t* p = (uint8_t*)(_data + _offset); 98 | _offset += sizeof(uint8_t); 99 | 100 | return *p; 101 | } 102 | bool DataBuffer::readBool() 103 | { 104 | if (_offset + sizeof(uint8_t) > _size) 105 | { 106 | throw std::logic_error("RhIO buffer size overflow"); 107 | } 108 | 109 | uint8_t* p = _data + _offset; 110 | _offset += sizeof(uint8_t); 111 | 112 | return (*p > 0) ? true : false; 113 | } 114 | int64_t DataBuffer::readInt() 115 | { 116 | if (_offset + sizeof(int64_t) > _size) 117 | { 118 | throw std::logic_error("RhIO buffer size overflow"); 119 | } 120 | 121 | int64_t* p = (int64_t*)(_data + _offset); 122 | _offset += sizeof(int64_t); 123 | 124 | return *p; 125 | } 126 | double DataBuffer::readFloat() 127 | { 128 | if (_offset + sizeof(double) > _size) 129 | { 130 | throw std::logic_error("RhIO buffer size overflow"); 131 | } 132 | 133 | double* p = (double*)(_data + _offset); 134 | _offset += sizeof(double); 135 | 136 | return *p; 137 | } 138 | std::string DataBuffer::readStr() 139 | { 140 | int64_t len = readInt(); 141 | 142 | if (_offset + len > _size) 143 | { 144 | throw std::logic_error("RhIO buffer size overflow"); 145 | } 146 | 147 | std::string str((char*)(_data + _offset), len); 148 | _offset += len; 149 | 150 | return str; 151 | } 152 | unsigned char* DataBuffer::readData(size_t& size) 153 | { 154 | size = readInt(); 155 | 156 | if (_offset + size > _size) 157 | { 158 | throw std::logic_error("RhIO buffer size overflow"); 159 | } 160 | 161 | unsigned char* data = _data + _offset; 162 | _offset += size; 163 | 164 | return data; 165 | } 166 | 167 | void* DataBuffer::data() 168 | { 169 | return _data; 170 | } 171 | 172 | } // namespace RhIO 173 | -------------------------------------------------------------------------------- /Server/src/ServerPub.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RHIO_SERVERPUB_HPP 2 | #define RHIO_SERVERPUB_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "RhIO.hpp" 9 | 10 | namespace RhIO 11 | { 12 | /** 13 | * ServerPub 14 | * 15 | * Implement Streaming publisher 16 | * using ZMQ network library 17 | */ 18 | class ServerPub 19 | { 20 | public: 21 | /** 22 | * Initialization with the bind 23 | * endpoint string 24 | */ 25 | ServerPub(std::string endpoint = "",zmq::context_t *context=nullptr); 26 | 27 | /** 28 | * Append to publish buffer value for type 29 | * Bool, Int, Float, Str with given absolute 30 | * name and timestamp 31 | */ 32 | void publishBool(const std::string& name, bool val, int64_t timestamp); 33 | void publishInt(const std::string& name, int64_t val, int64_t timestamp); 34 | void publishFloat(const std::string& name, double val, int64_t timestamp); 35 | void publishStr(const std::string& name, const std::string& val, int64_t timestamp); 36 | 37 | /** 38 | * Append to publish buffer stream 39 | * given absolute name, string and timestamp 40 | */ 41 | void publishStream(const std::string& name, const std::string& val, int64_t timestamp); 42 | 43 | /** 44 | * Append to publish buffer frame 45 | * given absolute name, timestamp, 46 | * frame data and size. 47 | * The data is immediatly copied. 48 | */ 49 | void publishFrame(const std::string& name, const cv::Mat& frame, const std::string& encoding, int64_t timestamp); 50 | 51 | /** 52 | * Publishes some error message 53 | */ 54 | void publishError(const std::string& message); 55 | 56 | /** 57 | * Switch values buffer and publish to Client 58 | * all registered values in former writing buffer. 59 | */ 60 | void sendToClient(); 61 | 62 | private: 63 | /** 64 | * Structure for typed values to 65 | * publish 66 | */ 67 | template 68 | struct PubValue 69 | { 70 | std::string name; 71 | T value; 72 | int64_t timestamp; 73 | }; 74 | 75 | /** 76 | * Typedef for typed values 77 | */ 78 | typedef PubValue PubValBool; 79 | typedef PubValue PubValInt; 80 | typedef PubValue PubValFloat; 81 | typedef PubValue PubValStr; 82 | 83 | /** 84 | * ZMQ context 85 | */ 86 | zmq::context_t *_context; 87 | 88 | /** 89 | * ZMQ socket 90 | */ 91 | zmq::socket_t _socket; 92 | 93 | /** 94 | * If true, the external writing buffer 95 | * is the 1 and the outputing network buffer is the 2. 96 | * If false, the external writing buffer 97 | * is the 2 and the outputing network buffer is the 1. 98 | */ 99 | bool _isWritingTo1; 100 | 101 | /** 102 | * First double buffer values to 103 | * publish container 104 | */ 105 | std::list _queue1Bool; 106 | std::list _queue1Int; 107 | std::list _queue1Float; 108 | std::list _queue1Str; 109 | std::list _queue1Stream; 110 | std::list _queue1Frame; 111 | 112 | /** 113 | * Second double buffer values to 114 | * publish container 115 | */ 116 | std::list _queue2Bool; 117 | std::list _queue2Int; 118 | std::list _queue2Float; 119 | std::list _queue2Str; 120 | std::list _queue2Stream; 121 | std::list _queue2Frame; 122 | 123 | /** 124 | * Error messages queue 125 | */ 126 | std::list _queue1Errors; 127 | std::list _queue2Errors; 128 | 129 | /** 130 | * Mutex protecting external values container 131 | * from concurent access for each type 132 | */ 133 | std::mutex _mutexQueueBool; 134 | std::mutex _mutexQueueInt; 135 | std::mutex _mutexQueueFloat; 136 | std::mutex _mutexQueueStr; 137 | std::mutex _mutexQueueStream; 138 | std::mutex _mutexQueueFrame; 139 | std::mutex _mutexQueueError; 140 | 141 | /** 142 | * Swap double buffer for publishing values 143 | */ 144 | void swapBuffer(); 145 | }; 146 | 147 | } // namespace RhIO 148 | 149 | #endif 150 | -------------------------------------------------------------------------------- /Server/src/StreamNode.cpp: -------------------------------------------------------------------------------- 1 | #include "StreamNode.hpp" 2 | #include "IONode.hpp" 3 | 4 | namespace RhIO 5 | { 6 | StreamNode& StreamNode::operator=(const StreamNode& node) 7 | { 8 | _streams = node._streams; 9 | 10 | return *this; 11 | } 12 | 13 | bool StreamNode::streamExist(const std::string& name) const 14 | { 15 | // Forward to subtree 16 | std::string tmpName; 17 | StreamNode* child = BaseNode::forwardFunc(name, tmpName, false); 18 | if (child != nullptr) 19 | return child->streamExist(tmpName); 20 | 21 | std::lock_guard lock(_mutex); 22 | return (_streams.count(name) > 0); 23 | } 24 | 25 | std::string StreamNode::streamDescription(const std::string& name) const 26 | { 27 | // Forward to subtree 28 | std::string tmpName; 29 | StreamNode* child = BaseNode::forwardFunc(name, tmpName, false); 30 | if (child != nullptr) 31 | return child->streamDescription(tmpName); 32 | 33 | std::lock_guard lock(_mutex); 34 | if (_streams.count(name) > 0) 35 | { 36 | return _streams.at(name).comment; 37 | } 38 | else 39 | { 40 | throw std::logic_error("RhIO unknown stream name: " + name); 41 | } 42 | } 43 | 44 | void StreamNode::enableStreamingStream(const std::string& name) 45 | { 46 | // Forward to subtree 47 | std::string tmpName; 48 | StreamNode* child = BaseNode::forwardFunc(name, tmpName, false); 49 | if (child != nullptr) 50 | { 51 | child->enableStreamingStream(tmpName); 52 | return; 53 | } 54 | 55 | std::lock_guard lock(_mutex); 56 | if (_streams.count(name) > 0) 57 | { 58 | _streams.at(name).buffer->_streamWatchers++; 59 | } 60 | else 61 | { 62 | throw std::logic_error("RhIO unknown stream name: " + name); 63 | } 64 | } 65 | void StreamNode::disableStreamingStream(const std::string& name) 66 | { 67 | // Forward to subtree 68 | std::string tmpName; 69 | StreamNode* child = BaseNode::forwardFunc(name, tmpName, false); 70 | if (child != nullptr) 71 | { 72 | child->disableStreamingStream(tmpName); 73 | return; 74 | } 75 | 76 | std::lock_guard lock(_mutex); 77 | if (_streams.count(name) > 0) 78 | { 79 | _streams.at(name).buffer->_streamWatchers--; 80 | if (_streams.at(name).buffer->_streamWatchers < 0) 81 | { 82 | _streams.at(name).buffer->_streamWatchers = 0; 83 | } 84 | } 85 | else 86 | { 87 | throw std::logic_error("RhIO unknown stream name: " + name); 88 | } 89 | } 90 | 91 | std::ostream& StreamNode::out(const std::string& name) 92 | { 93 | // Forward to subtree 94 | std::string tmpName; 95 | StreamNode* child = BaseNode::forwardFunc(name, tmpName, false); 96 | if (child != nullptr) 97 | return child->out(tmpName); 98 | 99 | std::lock_guard lock(_mutex); 100 | if (_streams.count(name) > 0) 101 | { 102 | return *(_streams.at(name).stream); 103 | } 104 | else 105 | { 106 | throw std::logic_error("RhIO unknown stream name: " + name); 107 | } 108 | } 109 | 110 | void StreamNode::newStream(const std::string& name, const std::string& comment) 111 | { 112 | // Forward to subtree 113 | std::string tmpName; 114 | StreamNode* child = BaseNode::forwardFunc(name, tmpName, true); 115 | if (child != nullptr) 116 | { 117 | child->newStream(tmpName, comment); 118 | return; 119 | } 120 | 121 | std::lock_guard lock(_mutex); 122 | if (_streams.count(name) == 0) 123 | { 124 | _streams[name] = Stream(); 125 | _streams.at(name).comment = comment; 126 | _streams.at(name).buffer = std::make_shared(BaseNode::pwd + separator + name); 127 | _streams.at(name).stream = std::make_shared(_streams.at(name).buffer.get()); 128 | } 129 | else 130 | { 131 | throw std::logic_error("RhIO already register stream name: " + name); 132 | } 133 | } 134 | 135 | std::vector StreamNode::listStreams() const 136 | { 137 | std::lock_guard lock(_mutex); 138 | std::vector list; 139 | for (const auto& c : _streams) 140 | { 141 | list.push_back(c.first); 142 | } 143 | 144 | return list; 145 | } 146 | 147 | } // namespace RhIO 148 | -------------------------------------------------------------------------------- /Shell/src/Shell.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "commands/Command.h" 8 | #include "Terminal.h" 9 | #include "Node.h" 10 | #include "NodePool.h" 11 | #include 12 | #include 13 | #include 14 | 15 | #define MAX_HISTORY 1000 16 | 17 | namespace RhIO 18 | { 19 | class StreamManager; 20 | class Shell 21 | { 22 | public: 23 | Shell(std::string server); 24 | ~Shell(); 25 | 26 | /** 27 | * Updates the local node tree 28 | */ 29 | void sync(bool display = true); 30 | void updateCommands(); 31 | 32 | /** 33 | * Runs the interactive shell, will get lines from stdin 34 | */ 35 | void run(std::string cmd = ""); 36 | bool quit(); 37 | void terminal_set_ioconfig(); 38 | void terminal_set_config(); 39 | 40 | /** 41 | * Read the history file in HOME/.rhio_history 42 | */ 43 | void readHistory(); 44 | 45 | /** 46 | * Append a line in history file in HOME/.rhio_history 47 | */ 48 | void writeHistory(std::string line); 49 | 50 | /** 51 | * Display the prompt... 52 | */ 53 | void displayPrompt(); 54 | 55 | /** 56 | * Read the command line 57 | */ 58 | std::string getLine(); 59 | 60 | /** 61 | * Parse a command line typed by the user 62 | */ 63 | void parse(std::string line); 64 | 65 | /** 66 | * Process a command typed by the user 67 | */ 68 | void process(std::string command, std::list args); 69 | 70 | /** 71 | * Sets a value 72 | */ 73 | void set(std::string lvalue, std::string rvalue); 74 | 75 | /** 76 | * Commands handling 77 | */ 78 | void registerCommand(Command* command); 79 | std::map getCommands(); 80 | Command* getCommand(std::string command); 81 | 82 | /** 83 | * Get the remote client 84 | */ 85 | ClientReq* getClient(); 86 | ClientSub* getClientSub(); 87 | 88 | /** 89 | * Get Node for a given path 90 | */ 91 | Node* getNode(std::string path = ""); 92 | Node* getCurrentNode(); 93 | ValueBase* getValue(std::string path); 94 | NodeValue getNodeValue(std::string path); 95 | NodeStream getNodeStream(std::string path); 96 | NodeFrame getNodeFrame(std::string path); 97 | 98 | /** 99 | * Current directory 100 | */ 101 | void enterPath(std::string path); 102 | void upPath(); 103 | std::vector pathToParts(std::string spath); 104 | bool goToPath(std::string path); 105 | std::string getPath(); 106 | 107 | /** 108 | * Add the poll to the stream, and wait the user to press enter, then 109 | * remove the pool from the stream 110 | */ 111 | void streamWait(NodePool* pool, Command* command); 112 | void wait(Command* command); 113 | StreamManager* getStream(); 114 | NodePool poolForNode(Node* node); 115 | NodePool getPool(std::vector names, int start = 0); 116 | 117 | Node* tree; 118 | struct termios termsave; 119 | struct termios termshell; 120 | std::deque shell_history; 121 | std::fstream history_file; 122 | std::string history_path; 123 | /** 124 | * Get all the possibilities at a certain point 125 | */ 126 | std::vector getPossibilities(std::string prefix); 127 | 128 | /** 129 | * Adds an alias 130 | */ 131 | void addAlias(std::string from, std::string to); 132 | 133 | void getFromServer(NodeValue value); 134 | void setToServer(NodeValue value); 135 | void setFromString(NodeValue value, std::string str); 136 | void setFromNumber(NodeValue value, float number); 137 | 138 | bool hasInput(); 139 | 140 | std::vector getUnreadErrors(); 141 | 142 | protected: 143 | std::vector chars; 144 | Command* currentCommand; 145 | std::map aliases; 146 | std::string hostname; 147 | ClientReq* client; 148 | ClientSub* clientSub; 149 | StreamManager* stream; 150 | bool terminate; 151 | std::map commands; 152 | std::list path; 153 | std::string server; 154 | 155 | std::mutex errorsMutex; 156 | std::vector unreadErrors; 157 | }; 158 | } // namespace RhIO 159 | -------------------------------------------------------------------------------- /Server/src/RhIO.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "RhIO.hpp" 5 | #include "ServerRep.hpp" 6 | #include "ServerPub.hpp" 7 | 8 | namespace RhIO 9 | { 10 | /** 11 | * Main instance global allocation 12 | */ 13 | IONode Root("ROOT", nullptr); 14 | 15 | /** 16 | * Default initialization of 17 | * ServerStream server 18 | */ 19 | ServerPub* ServerStream = nullptr; 20 | 21 | /** 22 | * Atomic register storing the number 23 | * of initialized Server 24 | * (for main thread waiting) 25 | */ 26 | static std::atomic initServerCount; 27 | 28 | /** 29 | * Server Pub and Rep thread instance 30 | * and quit state 31 | */ 32 | static std::thread* serverThreadRep = nullptr; 33 | static std::thread* serverThreadPub = nullptr; 34 | static zmq::context_t *serverRepContext = nullptr; 35 | static zmq::context_t *serverPubContext = nullptr; 36 | static bool serverThreadRepOver = false; 37 | static bool serverThreadPubOver = false; 38 | static unsigned int port = ServersPortBase; 39 | static bool serverStarting = false; 40 | 41 | /** 42 | * Reply Server main loop handling 43 | * incomming Client request 44 | */ 45 | static void runServerRep() 46 | { 47 | std::stringstream ss; 48 | ss << "tcp://*:" << (port + 1); 49 | ServerRep server(ss.str(),serverRepContext); 50 | // Notify main thread 51 | // for initialization ready 52 | initServerCount++; 53 | 54 | while (!serverThreadRepOver) 55 | { 56 | server.handleRequest(); 57 | } 58 | } 59 | 60 | /** 61 | * Publisher Server main loop handling 62 | * streaming values 63 | */ 64 | static void runServerPub() 65 | { 66 | // Allocating ServerStream 67 | std::stringstream ss; 68 | ss << "tcp://*:" << port; 69 | ServerPub server(ss.str(),serverPubContext); 70 | ServerStream = &server; 71 | // Notify main thread 72 | // for initialization ready 73 | initServerCount++; 74 | 75 | while (!serverThreadPubOver) 76 | { 77 | int64_t tsStart = 78 | std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()) 79 | .count(); 80 | server.sendToClient(); 81 | int64_t tsEnd = 82 | std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()) 83 | .count(); 84 | int64_t duration = tsEnd - tsStart; 85 | // Streaming value at 50Hz 86 | if (duration < 10) 87 | { 88 | std::this_thread::sleep_for(std::chrono::milliseconds(10 - duration)); 89 | } 90 | } 91 | } 92 | 93 | bool started() 94 | { 95 | return serverStarting; 96 | } 97 | 98 | void start(unsigned int port_) 99 | { 100 | serverStarting = true; 101 | port = port_; 102 | 103 | // Init atomic counter 104 | initServerCount = 0; 105 | // Start Server threads 106 | serverThreadRepOver = false; 107 | serverThreadPubOver = false; 108 | serverPubContext = new zmq::context_t(1); 109 | serverRepContext = new zmq::context_t(1); 110 | serverThreadRep = new std::thread(runServerRep); 111 | serverThreadPub = new std::thread(runServerPub); 112 | 113 | // Wait until both Server are initialized 114 | while (initServerCount != 2) 115 | { 116 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 117 | } 118 | } 119 | 120 | void stop() 121 | { 122 | if (initServerCount > 0) 123 | { 124 | // Wait the end of server thread 125 | serverThreadRepOver = true; 126 | serverThreadPubOver = true; 127 | serverThreadPub->join(); 128 | serverThreadRep->join(); 129 | delete serverThreadPub; 130 | delete serverThreadRep; 131 | serverPubContext->shutdown(); 132 | serverPubContext->close(); 133 | serverRepContext->shutdown(); 134 | serverRepContext->close(); 135 | delete serverPubContext; 136 | delete serverRepContext; 137 | } 138 | } 139 | 140 | /** 141 | * Ask and wait Server thread ending 142 | */ 143 | static void __attribute__((destructor)) stopThreadServer() 144 | { 145 | if (initServerCount > 0) 146 | { 147 | // Wait the end of server thread 148 | serverThreadRepOver = true; 149 | serverThreadPubOver = true; 150 | serverThreadPub->join(); 151 | serverThreadRep->join(); 152 | delete serverThreadPub; 153 | delete serverThreadRep; 154 | } 155 | } 156 | 157 | void errorMessage(const std::string& message) 158 | { 159 | if (ServerStream != nullptr) 160 | { 161 | ServerStream->publishError(message); 162 | } 163 | } 164 | 165 | } // namespace RhIO 166 | -------------------------------------------------------------------------------- /Tests/testBind.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "RhIO.hpp" 4 | 5 | class Test 6 | { 7 | public: 8 | Test() : _bind("test"), _bind2("test5"), _bind3() 9 | { 10 | _bind.bindNew("valueBool", _valueBool)->comment("bool value")->persisted(true); 11 | _bind.bind("valueInt1", _valueInt); 12 | _bind.bind("test2/valueFloat1", _valueFloat); 13 | _bind.bind("valueInt2", _valueInt2, RhIO::Bind::PullOnly); 14 | _bind.bindNew("test2/test4/valueFloat2", _valueFloat2, RhIO::Bind::PushOnly)->minimum(-100.0); 15 | _valueFloat2 = -2.0; 16 | _bind.bind("test2/test3/valueStr", _valueStr); 17 | assert(RhIO::Root.getValueType("test/valueBool") == RhIO::TypeBool); 18 | assert(RhIO::Root.getValueBool("test/valueBool").comment == "bool value"); 19 | _bind.newStream("output", "a test stream"); 20 | 21 | _bind2.bindFunc("command1", "test command1", &Test::command1, *this, { "", "42" }); 22 | assert(RhIO::Root.call("test5/command1", { "2", "3" }) == "5"); 23 | assert(RhIO::Root.call("test5/command1", { "2" }) == "44"); 24 | assert(RhIO::Root.call("test5/command1", {}).find("USAGE: command1 --> ") != 25 | std::string::npos); 26 | _bind.bindFunc("command2", "test command2", &Test::command2, *this); 27 | assert(RhIO::Root.call("test/command2", {}) == "1"); 28 | _bind.bindFunc("command3", "test command3", &Test::command3, *this); 29 | assert(RhIO::Root.call("test/command3", { "3.0", "2", "toto" }) > "8.99"); 30 | assert(RhIO::Root.call("test/command3", { "3.0", "2", "toto" }) < "9.01"); 31 | assert( 32 | RhIO::Root.call("test/command3", { "3.0", "2" }).find("USAGE: command3 --> ") != 33 | std::string::npos); 34 | 35 | _bind.bindFunc("command4", "test command4", &Test::command4, *this); 36 | assert(RhIO::Root.call("test/command4", {}) == "pouet"); 37 | 38 | _bind.bindFunc("command5", "test command5", &Test::command5, *this); 39 | assert(RhIO::Root.call("test/command5", {}) == ""); 40 | 41 | _bind3.bindNew("root3", _valueFloat3); 42 | } 43 | 44 | inline void tick1() 45 | { 46 | RhIO::Root.setBool("test/valueBool", true); 47 | _bind.pull(); 48 | assert(_valueBool == true); 49 | assert(_valueInt == 2); 50 | assert(_valueInt2 == 50); 51 | assert(_valueFloat == 42.0); 52 | assert(_valueFloat2 == -2.0); 53 | assert(_valueStr == "str1"); 54 | _valueInt = 3; 55 | _valueInt2 = 60; 56 | _valueFloat2 = -3.0; 57 | _bind.push(); 58 | _valueFloat = 1.0; 59 | _bind.out("output") << "test" << std::endl; 60 | } 61 | 62 | inline void tick2() 63 | { 64 | assert(_valueFloat == 1.0); 65 | _bind.pull(); 66 | assert(_valueBool == true); 67 | assert(_valueInt == 3); 68 | assert(_valueFloat == 42.0); 69 | assert(_valueStr == "str2"); 70 | } 71 | 72 | inline int command1(int a, unsigned int b) 73 | { 74 | return a + b; 75 | } 76 | inline bool command2(void) 77 | { 78 | return true; 79 | } 80 | inline float command3(float a, long b, std::string c) 81 | { 82 | return a + b + c.length(); 83 | } 84 | inline std::string command4() 85 | { 86 | return "pouet"; 87 | } 88 | inline void command5() 89 | { 90 | } 91 | 92 | private: 93 | bool _valueBool; 94 | long _valueInt; 95 | int _valueInt2; 96 | double _valueFloat; 97 | float _valueFloat2; 98 | float _valueFloat3; 99 | std::string _valueStr; 100 | RhIO::Bind _bind; 101 | RhIO::Bind _bind2; 102 | RhIO::Bind _bind3; 103 | }; 104 | 105 | int main() 106 | { 107 | RhIO::Root.newChild("test"); 108 | RhIO::Root.newInt("test/valueInt1"); 109 | RhIO::Root.newInt("test/valueInt2"); 110 | RhIO::Root.newFloat("test/test2/valueFloat1"); 111 | RhIO::Root.newStr("test/test2/test3/valueStr"); 112 | 113 | RhIO::Root.setInt("test/valueInt1", 2); 114 | RhIO::Root.setInt("test/valueInt2", 50); 115 | RhIO::Root.setFloat("test/test2/valueFloat1", 42.0); 116 | RhIO::Root.setStr("test/test2/test3/valueStr", "str1"); 117 | 118 | Test test; 119 | test.tick1(); 120 | assert(RhIO::Root.getInt("test/valueInt1") == 3); 121 | assert(RhIO::Root.getInt("test/valueInt2") == 50); 122 | assert(RhIO::Root.getFloat("test/test2/valueFloat1") == 42.0); 123 | assert(RhIO::Root.getFloat("test/test2/test4/valueFloat2") == -3.0); 124 | RhIO::Root.setStr("test/test2/test3/valueStr", "str2"); 125 | test.tick2(); 126 | 127 | assert(RhIO::Root.childExist("root3") == false); 128 | 129 | return 0; 130 | } 131 | -------------------------------------------------------------------------------- /Server/src/ServerRep.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RHIO_SERVERREP_HPP 2 | #define RHIO_SERVERREP_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include "RhIO.hpp" 8 | #include "IONode.hpp" 9 | #include "DataBuffer.hpp" 10 | 11 | namespace RhIO 12 | { 13 | /** 14 | * ServerRep 15 | * 16 | * Implement Client request answer 17 | * using ZMQ network library 18 | */ 19 | class ServerRep 20 | { 21 | public: 22 | /** 23 | * Initialization with the bind 24 | * endpoint string 25 | */ 26 | ServerRep(std::string endpoint = "",zmq::context_t *context=nullptr); 27 | 28 | /** 29 | * Wait for next Client request 30 | * and reply it 31 | */ 32 | void handleRequest(); 33 | 34 | private: 35 | /** 36 | * ZMQ context 37 | */ 38 | zmq::context_t *_context; 39 | 40 | /** 41 | * ZMQ socket 42 | */ 43 | zmq::socket_t _socket; 44 | 45 | /** 46 | * Reply server thread 47 | */ 48 | std::thread _serverThread; 49 | 50 | /** 51 | * Implement MsgAskChildren reply (MsgListNames) 52 | */ 53 | void listChildren(DataBuffer& buffer); 54 | 55 | /** 56 | * Implement MsgAskValues Bool, Int, Float and Str 57 | * (MsgListNames) 58 | */ 59 | void listValuesBool(DataBuffer& buffer); 60 | void listValuesInt(DataBuffer& buffer); 61 | void listValuesFloat(DataBuffer& buffer); 62 | void listValuesStr(DataBuffer& buffer); 63 | 64 | /** 65 | * Implement MsgGet for Bool, Int, Float, Str 66 | * (MsgVal) 67 | */ 68 | void getBool(DataBuffer& buffer); 69 | void getInt(DataBuffer& buffer); 70 | void getFloat(DataBuffer& buffer); 71 | void getStr(DataBuffer& buffer); 72 | 73 | /** 74 | * Implement MsgSet for Bool, Int, Float, Str 75 | * (MsgSetOk) 76 | */ 77 | void setBool(DataBuffer& buffer); 78 | void setInt(DataBuffer& buffer); 79 | void setFloat(DataBuffer& buffer); 80 | void setStr(DataBuffer& buffer); 81 | 82 | /** 83 | * Implement MsgAskMeta for Bool, Int, Float, Str 84 | * (MsgValMeta) 85 | */ 86 | void valMetaBool(DataBuffer& buffer); 87 | void valMetaInt(DataBuffer& buffer); 88 | void valMetaFloat(DataBuffer& buffer); 89 | void valMetaStr(DataBuffer& buffer); 90 | 91 | /** 92 | * Implement MsgEnableStreamingValue and MsgDisableStreamingValue 93 | * (MsgStreamingOK) 94 | */ 95 | void enableStreamingValue(DataBuffer& buffer); 96 | void disableStreamingValue(DataBuffer& buffer); 97 | 98 | /** 99 | * Implement MsgEnableStreamingStream and MsgDisableStreamingStream 100 | * (MsgStreamingOK) 101 | */ 102 | void enableStreamingStream(DataBuffer& buffer); 103 | void disableStreamingStream(DataBuffer& buffer); 104 | 105 | /** 106 | * Implement MsgEnableStreamingFrame and MsgDisableStreamingFrame 107 | * (MsgStreamingOK) 108 | */ 109 | void enableStreamingFrame(DataBuffer& buffer); 110 | void disableStreamingFrame(DataBuffer& buffer); 111 | 112 | /** 113 | * Implement MsgAskSave ans MsgAskLoad (MsgPersistOK) 114 | */ 115 | void save(DataBuffer& buffer); 116 | void load(DataBuffer& buffer); 117 | 118 | /** 119 | * Implement MsgAskCommands (MsgListNames) 120 | */ 121 | void listCommands(DataBuffer& buffer); 122 | 123 | /** 124 | * Implement MsgAskAllCommands (MsgListNames) 125 | */ 126 | void listAllCommands(DataBuffer& buffer); 127 | 128 | /** 129 | * Implement MsgAskCommandDescription (MsgCommandDescription) 130 | */ 131 | void commandDescription(DataBuffer& buffer); 132 | 133 | /** 134 | * Implement MsgAskCall (MsgCallResult) 135 | */ 136 | void callResult(DataBuffer& buffer); 137 | 138 | /** 139 | * Implement MsgAskStreams (MsgListNames) 140 | */ 141 | void listStreams(DataBuffer& buffer); 142 | 143 | /** 144 | * Implement MsgAskDescriptionStream 145 | * (MsgDescriptionStream) 146 | */ 147 | void descriptionStream(DataBuffer& buffer); 148 | 149 | /** 150 | * Implement MsgAskFrames (MsgListNames) 151 | */ 152 | void listFrames(DataBuffer& buffer); 153 | 154 | /** 155 | * Implement MsgAskMetaFrame 156 | * (MsgValMetaFrame) 157 | */ 158 | void valMetaFrame(DataBuffer& buffer); 159 | 160 | /** 161 | * Implement MsgError with given error message 162 | */ 163 | void error(const std::string& msg); 164 | 165 | /** 166 | * Return the Node mapped with given absolute name. 167 | * If given name is invalid, nullptr is returned 168 | * and an error reply is send to the Client. 169 | */ 170 | RhIO::IONode* getNode(const std::string& name); 171 | }; 172 | 173 | } // namespace RhIO 174 | 175 | #endif 176 | -------------------------------------------------------------------------------- /Common/src/Value.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RHIO_VALUE_HPP 2 | #define RHIO_VALUE_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace RhIO 10 | { 11 | /** 12 | * ValueBase 13 | * 14 | * Factorize non type specific 15 | * field of Value 16 | */ 17 | struct ValueBase 18 | { 19 | virtual ~ValueBase(){}; 20 | 21 | /** 22 | * Value name 23 | */ 24 | std::string name; 25 | 26 | /** 27 | * A friendly and helpful comment message 28 | */ 29 | std::string comment; 30 | 31 | /** 32 | * Flags indicating min & max presence 33 | */ 34 | bool hasMin; 35 | bool hasMax; 36 | 37 | /** 38 | * Last updated value timestamp 39 | */ 40 | std::chrono::steady_clock::time_point timestamp; 41 | 42 | /** 43 | * If false, the value will not be 44 | * saved in config files 45 | */ 46 | bool persisted; 47 | 48 | /** 49 | * The number of registered watcher 50 | * Streaming is enabled while at least 51 | * one watcher is registered 52 | */ 53 | int64_t streamWatchers; 54 | }; 55 | 56 | /** 57 | * Value 58 | * 59 | * Generic value holder 60 | * for parameters and monitor 61 | */ 62 | template 63 | struct Value : public ValueBase 64 | { 65 | /** 66 | * Typedef value type 67 | */ 68 | typedef T Type; 69 | 70 | /** 71 | * Value value 72 | */ 73 | T value; 74 | 75 | /** 76 | * Value min and max bounds 77 | */ 78 | T min; 79 | T max; 80 | 81 | /** 82 | * The value stored in read config file 83 | * (used to compute values diff) 84 | */ 85 | T valuePersisted; 86 | 87 | /** 88 | * Callback called on value update. 89 | * Take the new value as argument. 90 | */ 91 | std::function callback; 92 | }; 93 | 94 | /** 95 | * Typedef for used value types 96 | */ 97 | typedef Value ValueBool; 98 | typedef Value ValueInt; 99 | typedef Value ValueFloat; 100 | typedef Value ValueStr; 101 | 102 | /** 103 | * Enum for used value type 104 | */ 105 | enum ValueType 106 | { 107 | NoValue = 0, 108 | TypeBool = 1, 109 | TypeInt = 2, 110 | TypeFloat = 3, 111 | TypeStr = 4 112 | }; 113 | 114 | /** 115 | * Proxy struct used to configure optional 116 | * value parameters 117 | */ 118 | template 119 | struct ValueBuilder final 120 | { 121 | public: 122 | /** 123 | * Initialization with builded 124 | * value reference and flag indicating if 125 | * the underlying value is brand new or not 126 | */ 127 | ValueBuilder(Value& val, bool isExisting) : _isExisting(isExisting), _value(val) 128 | { 129 | // Default value parameters 130 | if (!isExisting) 131 | { 132 | _value.comment = ""; 133 | _value.hasMin = false; 134 | _value.hasMax = false; 135 | _value.value = T(); 136 | _value.valuePersisted = T(); 137 | _value.persisted = false; 138 | _value.streamWatchers = 0; 139 | _value.callback = [](T t) { (void)t; }; 140 | } 141 | } 142 | 143 | /** 144 | * This callback is called when the value builder is finished (destructed) 145 | */ 146 | std::function callback; 147 | 148 | /** 149 | * Optional parameters setters 150 | */ 151 | ValueBuilder* comment(const std::string& str) 152 | { 153 | _value.comment = str; 154 | return this; 155 | } 156 | ValueBuilder* minimum(T val) 157 | { 158 | _value.hasMin = true; 159 | _value.min = val; 160 | return this; 161 | } 162 | ValueBuilder* maximum(T val) 163 | { 164 | _value.hasMax = true; 165 | _value.max = val; 166 | return this; 167 | } 168 | ValueBuilder* defaultValue(T val) 169 | { 170 | if (!_isExisting) 171 | { 172 | _value.value = val; 173 | if (callback && !_value.persisted) 174 | { 175 | callback(val); 176 | } 177 | _value.valuePersisted = val; 178 | } 179 | return this; 180 | } 181 | ValueBuilder* persisted(bool flag) 182 | { 183 | _value.persisted = flag; 184 | return this; 185 | } 186 | 187 | private: 188 | /** 189 | * True if we are dealing with already 190 | * existing value. Thus no update. 191 | */ 192 | bool _isExisting; 193 | 194 | /** 195 | * Internal builded reference instance 196 | */ 197 | Value& _value; 198 | }; 199 | 200 | /** 201 | * Typedef for ValueBuilder 202 | */ 203 | typedef ValueBuilder ValueBuilderBool; 204 | typedef ValueBuilder ValueBuilderInt; 205 | typedef ValueBuilder ValueBuilderFloat; 206 | typedef ValueBuilder ValueBuilderStr; 207 | 208 | } // namespace RhIO 209 | 210 | #endif 211 | -------------------------------------------------------------------------------- /Docs/api.md: -------------------------------------------------------------------------------- 1 | # RhIO API 2 | 3 | RhIO will automatically run a server on startup when it is linked into your app, 4 | you don't have to do it manually (it uses compiler constructors). 5 | 6 | You'll first have to include the ``RhIO.hpp`` file: 7 | 8 | ```cpp 9 | #include 10 | ``` 11 | 12 | Then, you can access the root node of RhIO using ``RhIO::Root`̀`. There is several 13 | examples in the `Examples/` directory of this repository. 14 | 15 | ## Parameters 16 | 17 | RhIO can deal with ints, floats/doubles, strings and bools parameters. 18 | 19 | ### Declaring parameters 20 | 21 | You can define values using ``newInt``, ``newFloat``, ``newString`` or 22 | ``newBool``. Additional metadata can be passed using method chaining, just like this: 23 | 24 | ```cpp 25 | RhIO::Root.newInt("paramInt") 26 | ->comment("My first RhIO int"); 27 | ``` 28 | The available methods are: 29 | 30 | * `comment(string)`: a comment text describing the parameter 31 | * `minimum(value)` and `maximum(value)`: minimum and maximum boundaries for the value 32 | of this parameter 33 | * `defaultValue(value)`: the default value of the parameter 34 | * `persisted(bool)`: wether to persist or not this parameter (see [below](#persistence)) 35 | 36 | The parameter name can be a hierarchical path: 37 | 38 | ```cpp 39 | RhIO::Root.newInt("/path/to/paramInt"); 40 | ``` 41 | 42 | Note that these methods can't be called twice with the same parameter name, since it is 43 | used to **declare** the parameter. 44 | 45 | ### Setting parameters 46 | 47 | Parameters can be set using ``setInt``, ``setFloat``, ``setString`` or ``setBool``: 48 | 49 | ```cpp 50 | RhIO::Root.setInt("/path/to/paramInt", 42); 51 | ``` 52 | 53 | The third parameter of these methods is the timestamp that will be used (will default to 54 | the current timestamp): 55 | 56 | ```cpp 57 | RhIO::Root.setInt("/path/to/paramInt", 42, std::chrono::steady_clock::now()); 58 | ``` 59 | 60 | ### Getting parameters 61 | 62 | Parameters can be get using ``getInt``, ``getFloat``, ``getString`` or ``getBool``: 63 | 64 | ```cpp 65 | std::cout << "a=" << RhIO::Root.getInt("/path/to/paramInt") << std::endl; 66 | ``` 67 | 68 | ### Nodes 69 | 70 | For convenience, you may want to get access to nodes other than root. This can be done 71 | using ``newChild``, for node creation and ``getChild`` method, to access the node: 72 | 73 | ```cpp 74 | RhIO::Root.newChild("/hello/world"); 75 | auto node = RhIO::Root.getChild("/hello/world"); 76 | node.newInt("test"); // Will declare /hello/world/test 77 | ``` 78 | 79 | One can also test the existence of a node using ``nodeExist`` method. 80 | 81 | ## Persistence 82 | 83 | Any node can be persisted and/or loaded to filesystem using ``load`` and ``save`` methods. 84 | Note that only the persisted parameters (i.e those with the ``persisted(true)`` metadata) 85 | will be concerned by these operations. 86 | 87 | If you want to save all your parameters to the `rhio/` directory (for instance when your 88 | program quit): 89 | 90 | ```c++ 91 | RhIO::Root.save("rhio"); 92 | ``` 93 | 94 | In the same way, you can load it (for instance, on the start-up of your program): 95 | 96 | ```c++ 97 | RhIO::Root.load("rhio"); 98 | ``` 99 | 100 | ## Commands 101 | 102 | Commands are custom application-side methods that can be called with arguments passed by 103 | the user. 104 | 105 | Here is an example of "echo" command: 106 | 107 | ```c++ 108 | RhIO::Root.newCommand("echo", 109 | "An example echo command", 110 | [](const std::vector& args) -> std::string 111 | { 112 | return "ECHO: " + args[0]; 113 | }); 114 | ``` 115 | 116 | ## Streams 117 | 118 | Streams are special nodes working like standard C++ output streams (std::ostream). 119 | They enable to create multiple debuging channel integrated into the RhIO tree. 120 | They can be display in the client Shell using the ``cat`` command. 121 | 122 | After beeing declared, data are output to streams using follwing syntax: 123 | ```c++ 124 | RhIO::Root.newStream("path/in/tree/stream1", "description of stream1"); 125 | RhIO::Root.out("path/in/tree/stream1") << "This is a debug message" << std::endl; 126 | ``` 127 | 128 | (See Examples) 129 | 130 | ## Frames 131 | 132 | Frames are special nodes that enables to stream at real time raw images from server to the client. 133 | Raw RGB, BGR, YUV are currently supported. The image stream can then be display in the shell using the ``view`` command. 134 | 135 | ```c++ 136 | RhIO::Root.newFrame("path/in/tree/frame1", "description of frame1", RhIO::FrameFormat::RGB); 137 | if (RhIO::Root.frameIsStreaming("path/in/tree/frame1")) { 138 | //The frame data is immedialy copied from given 139 | //memory pointer and given size. 140 | RhIO::Root.framePush("path/in/tree/frame1", width, height, frame, size); 141 | } 142 | ``` 143 | 144 | (See Examples) 145 | 146 | -------------------------------------------------------------------------------- /Server/src/FrameNode.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "FrameNode.hpp" 4 | #include "ServerPub.hpp" 5 | #include "RhIO.hpp" 6 | 7 | namespace RhIO 8 | { 9 | FrameNode& FrameNode::operator=(const FrameNode& node) 10 | { 11 | _frames = node._frames; 12 | 13 | return *this; 14 | } 15 | 16 | bool FrameNode::frameExist(const std::string& name) const 17 | { 18 | // Forward to subtree 19 | std::string tmpName; 20 | FrameNode* child = BaseNode::forwardFunc(name, tmpName, false); 21 | if (child != nullptr) 22 | return child->frameExist(tmpName); 23 | 24 | std::lock_guard lock(_mutex); 25 | return (_frames.count(name) > 0); 26 | } 27 | 28 | bool FrameNode::frameIsStreaming(const std::string& name) const 29 | { 30 | // Forward to subtree 31 | std::string tmpName; 32 | FrameNode* child = BaseNode::forwardFunc(name, tmpName, false); 33 | if (child != nullptr) 34 | return child->frameIsStreaming(tmpName); 35 | 36 | std::lock_guard lock(_mutex); 37 | if (_frames.count(name) > 0) 38 | { 39 | return (_frames.at(name).countWatchers > 0); 40 | } 41 | else 42 | { 43 | throw std::logic_error("RhIO unknown frame name: " + name); 44 | } 45 | } 46 | 47 | const Frame& FrameNode::getFrame(const std::string& name) const 48 | { 49 | // Forward to subtree 50 | std::string tmpName; 51 | FrameNode* child = BaseNode::forwardFunc(name, tmpName, false); 52 | if (child != nullptr) 53 | return child->getFrame(tmpName); 54 | 55 | std::lock_guard lock(_mutex); 56 | if (_frames.count(name) > 0) 57 | { 58 | return _frames.at(name); 59 | } 60 | else 61 | { 62 | throw std::logic_error("RhIO unknown frame name: " + name); 63 | } 64 | } 65 | 66 | void FrameNode::framePush(const std::string& name, const cv::Mat& frame, const std::string& encoding, 67 | std::chrono::steady_clock::time_point timestamp) 68 | { 69 | // Forward to subtree 70 | std::string tmpName; 71 | FrameNode* child = BaseNode::forwardFunc(name, tmpName, false); 72 | if (child != nullptr) 73 | { 74 | child->framePush(tmpName, frame, encoding, timestamp); 75 | return; 76 | } 77 | 78 | std::lock_guard lock(_mutex); 79 | if (_frames.count(name) > 0) 80 | { 81 | if (_frames.at(name).countWatchers > 0) 82 | { 83 | ServerStream->publishFrame( 84 | BaseNode::pwd + separator + name, frame, encoding, 85 | std::chrono::duration_cast(timestamp.time_since_epoch()).count()); 86 | } 87 | } 88 | else 89 | { 90 | throw std::logic_error("RhIO unknown frame name: " + name); 91 | } 92 | } 93 | 94 | void FrameNode::enableStreamingFrame(const std::string& name) 95 | { 96 | // Forward to subtree 97 | std::string tmpName; 98 | FrameNode* child = BaseNode::forwardFunc(name, tmpName, false); 99 | if (child != nullptr) 100 | { 101 | child->enableStreamingFrame(tmpName); 102 | return; 103 | } 104 | 105 | std::lock_guard lock(_mutex); 106 | if (_frames.count(name) > 0) 107 | { 108 | _frames.at(name).countWatchers++; 109 | } 110 | else 111 | { 112 | throw std::logic_error("RhIO unknown frame name: " + name); 113 | } 114 | } 115 | void FrameNode::disableStreamingFrame(const std::string& name) 116 | { 117 | // Forward to subtree 118 | std::string tmpName; 119 | FrameNode* child = BaseNode::forwardFunc(name, tmpName, false); 120 | if (child != nullptr) 121 | { 122 | child->disableStreamingFrame(tmpName); 123 | return; 124 | } 125 | 126 | std::lock_guard lock(_mutex); 127 | if (_frames.count(name) > 0) 128 | { 129 | _frames.at(name).countWatchers--; 130 | if (_frames.at(name).countWatchers < 0) 131 | { 132 | _frames.at(name).countWatchers = 0; 133 | } 134 | } 135 | else 136 | { 137 | throw std::logic_error("RhIO unknown frame name: " + name); 138 | } 139 | } 140 | 141 | void FrameNode::newFrame(const std::string& name, const std::string& comment) 142 | { 143 | // Forward to subtree 144 | std::string tmpName; 145 | FrameNode* child = BaseNode::forwardFunc(name, tmpName, true); 146 | if (child != nullptr) 147 | { 148 | child->newFrame(tmpName, comment); 149 | return; 150 | } 151 | 152 | std::lock_guard lock(_mutex); 153 | if (_frames.count(name) == 0) 154 | { 155 | _frames[name] = Frame(); 156 | _frames.at(name).name = name; 157 | _frames.at(name).comment = comment; 158 | _frames.at(name).countWatchers = 0; 159 | } 160 | else 161 | { 162 | throw std::logic_error("RhIO already register frame name: '" + BaseNode::pwd + "/" + name + "'"); 163 | } 164 | } 165 | 166 | std::vector FrameNode::listFrames() const 167 | { 168 | std::lock_guard lock(_mutex); 169 | std::vector list; 170 | for (const auto& c : _frames) 171 | { 172 | list.push_back(c.first); 173 | } 174 | 175 | return list; 176 | } 177 | 178 | } // namespace RhIO 179 | -------------------------------------------------------------------------------- /Shell/src/StreamManager.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "StreamManager.h" 3 | #include "NodePool.h" 4 | #include "Node.h" 5 | 6 | using namespace std::placeholders; 7 | 8 | namespace RhIO 9 | { 10 | StreamManager::StreamManager(Shell* shell) : alive(true), frequency(DEFAULT_FREQ) 11 | { 12 | auto sub = shell->getClientSub(); 13 | 14 | sub->setHandlerBool(std::bind(&StreamManager::boolHandler, this, _1, _2, _3)); 15 | sub->setHandlerInt(std::bind(&StreamManager::intHandler, this, _1, _2, _3)); 16 | sub->setHandlerFloat(std::bind(&StreamManager::floatHandler, this, _1, _2, _3)); 17 | sub->setHandlerStr(std::bind(&StreamManager::stringHandler, this, _1, _2, _3)); 18 | sub->setHandlerStream(std::bind(&StreamManager::streamHandler, this, _1, _2, _3)); 19 | sub->setHandlerFrame(std::bind(&StreamManager::frameHandler, this, _1, _2, _3)); 20 | 21 | worker = new std::thread(&StreamManager::update, this); 22 | } 23 | 24 | void StreamManager::setStreamCallback(StreamUpdateHandler handler_) 25 | { 26 | handlerStream = handler_; 27 | } 28 | 29 | void StreamManager::unsetStreamCallback() 30 | { 31 | handlerStream = StreamUpdateHandler(); 32 | } 33 | 34 | void StreamManager::setFrameCallback(FrameUpdateHandler handler_) 35 | { 36 | handlerFrame = handler_; 37 | } 38 | 39 | void StreamManager::unsetFrameCallback() 40 | { 41 | handlerFrame = FrameUpdateHandler(); 42 | } 43 | 44 | StreamManager::~StreamManager() 45 | { 46 | alive = false; 47 | worker->join(); 48 | delete worker; 49 | } 50 | 51 | void StreamManager::boolHandler(const std::string& name, long timestamp, bool val) 52 | { 53 | mutex.lock(); 54 | for (auto pool : pools) 55 | { 56 | for (auto node : *pool) 57 | { 58 | if (auto var = Node::asBool(node.value)) 59 | { 60 | if (node.getName() == name) 61 | { 62 | var->value = val; 63 | pool->dirty = true; 64 | pool->timestamp = timestamp; 65 | } 66 | } 67 | } 68 | } 69 | mutex.unlock(); 70 | } 71 | 72 | void StreamManager::intHandler(const std::string& name, long timestamp, int val) 73 | { 74 | mutex.lock(); 75 | for (auto pool : pools) 76 | { 77 | for (auto node : *pool) 78 | { 79 | if (auto var = Node::asInt(node.value)) 80 | { 81 | if (node.getName() == name) 82 | { 83 | var->value = val; 84 | pool->dirty = true; 85 | pool->timestamp = timestamp; 86 | } 87 | } 88 | } 89 | } 90 | mutex.unlock(); 91 | } 92 | 93 | void StreamManager::floatHandler(const std::string& name, long timestamp, float val) 94 | { 95 | mutex.lock(); 96 | for (auto pool : pools) 97 | { 98 | for (auto node : *pool) 99 | { 100 | if (auto var = Node::asFloat(node.value)) 101 | { 102 | if (node.getName() == name) 103 | { 104 | var->value = val; 105 | pool->dirty = true; 106 | pool->timestamp = timestamp; 107 | } 108 | } 109 | } 110 | } 111 | mutex.unlock(); 112 | } 113 | 114 | void StreamManager::stringHandler(const std::string& name, long timestamp, const std::string& val) 115 | { 116 | mutex.lock(); 117 | for (auto pool : pools) 118 | { 119 | for (auto node : *pool) 120 | { 121 | if (auto var = Node::asString(node.value)) 122 | { 123 | if (node.getName() == name) 124 | { 125 | var->value = val; 126 | pool->dirty = true; 127 | pool->timestamp = timestamp; 128 | } 129 | } 130 | } 131 | } 132 | mutex.unlock(); 133 | } 134 | 135 | void StreamManager::streamHandler(const std::string& name, long timestamp, const std::string& str) 136 | { 137 | (void)timestamp; 138 | if (handlerStream) 139 | { 140 | handlerStream(name, str); 141 | } 142 | } 143 | 144 | void StreamManager::frameHandler(const std::string& name, long timestamp, const cv::Mat& frame) 145 | { 146 | (void)timestamp; 147 | if (handlerFrame) 148 | { 149 | handlerFrame(name, frame); 150 | } 151 | } 152 | 153 | void StreamManager::addPool(Shell* shell, NodePool* pool) 154 | { 155 | mutex.lock(); 156 | auto client = shell->getClient(); 157 | for (auto entry : *pool) 158 | { 159 | try 160 | { 161 | client->enableStreamingValue(entry.getName()); 162 | } 163 | catch (...) 164 | { 165 | } 166 | } 167 | pools.insert(pool); 168 | mutex.unlock(); 169 | } 170 | 171 | void StreamManager::removePool(Shell* shell, NodePool* pool) 172 | { 173 | mutex.lock(); 174 | auto client = shell->getClient(); 175 | for (auto entry : *pool) 176 | { 177 | try 178 | { 179 | client->disableStreamingValue(entry.getName()); 180 | } 181 | catch (...) 182 | { 183 | } 184 | } 185 | pools.erase(pool); 186 | mutex.unlock(); 187 | } 188 | 189 | void StreamManager::update() 190 | { 191 | while (alive) 192 | { 193 | mutex.lock(); 194 | for (auto pool : pools) 195 | { 196 | if (pool->dirty) 197 | { 198 | pool->update(); 199 | } 200 | } 201 | mutex.unlock(); 202 | 203 | std::this_thread::sleep_for(std::chrono::milliseconds(1000 / frequency)); 204 | } 205 | } 206 | 207 | void StreamManager::setFrequency(int frequency_) 208 | { 209 | frequency = frequency_; 210 | } 211 | } // namespace RhIO 212 | --------------------------------------------------------------------------------