Topology.hpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. ///
  2. /// \file Framework/Topology.hpp
  3. ///
  4. /// This file contains the interface for creating a topology of blocks.
  5. ///
  6. /// \copyright
  7. /// Copyright (c) 2014-2019 Josh Blum
  8. /// SPDX-License-Identifier: BSL-1.0
  9. ///
  10. #pragma once
  11. #include <Pothos/Config.hpp>
  12. #include <Pothos/Framework/Connectable.hpp>
  13. #include <Pothos/Framework/ThreadPool.hpp>
  14. #include <Pothos/Object/Object.hpp>
  15. #include <string>
  16. #include <memory>
  17. #include <iosfwd>
  18. namespace Pothos {
  19. /*!
  20. * The Topology class maintains a list of data flows.
  21. * The source of a flow is an output port on a Block or Topology.
  22. * The destination of a flow is an input port on a Block or Topology.
  23. * To create and destroy flows, make calls to connect and disconnect.
  24. * To create hierarchy, a topology's connections can be made with itself;
  25. * this action creates input and output ports for the topology.
  26. */
  27. class POTHOS_API Topology : public Connectable
  28. {
  29. public:
  30. /*!
  31. * Create a new empty topology in a shared ptr.
  32. * This is a convenience factory for Topology.
  33. */
  34. static std::shared_ptr<Topology> make(void);
  35. /*!
  36. * Create a topology from a JSON description.
  37. *
  38. * <h2>JSON fields</h2>
  39. *
  40. * The topology is a JSON object with fields to describe
  41. * thread pools, global variables, blocks, and connections.
  42. *
  43. * <h3>Thread pools</h3>
  44. * The "threadPools" field is an optional JSON object
  45. * where each entry contains thread pool arguments which are
  46. * documented by the ThreadPoolArgs JSON markup constructor.
  47. * A block can be associated to a particular thread pool
  48. * using the optional "threadPool" key and a pool name.
  49. * The special thread pool with empty name "" will apply
  50. * to all blocks that do not specify the "threadPool" key.
  51. *
  52. * <h3>Global variables</h3>
  53. * The "globals" field is an optional JSON array
  54. * where each entry is an object containing a variable name
  55. * and value. The order of global variables matters here,
  56. * because one global can reference another in an expression.
  57. *
  58. * <h3>Blocks</h3>
  59. * The "blocks" field is an array of JSON objects,
  60. * each of which describes a block by path,
  61. * constructor args, and method calls.
  62. * - The "id" of each block must be unique
  63. * and will be referenced by the connections.
  64. * - The "path" is the registered block factory path.
  65. * - The "args" is a list of constructor arguments.
  66. * - The "calls" is a list of ordered method calls.
  67. * Each specified by the call name then arguments.
  68. * - The "threadPool" specifies an optional thread pool by name
  69. *
  70. * <h3>Connections</h3>
  71. * The "connections" field is an array of JSON arrays,
  72. * where each array specifies a connection with source and destination ID and port name.
  73. * The IDs are strings, but the port names can either be numbers or strings.
  74. * Each connection entry has the following fields in order:
  75. * - source ID
  76. * - source port
  77. * - destination ID
  78. * - destination port
  79. *
  80. * <h2>Using expressions</h2>
  81. *
  82. * Global variable values and block arguments support expression parsing.
  83. * The parser allows these values to be native JSON types:
  84. * integers, floats, string, booleans, arrays, and objects.
  85. * But they can also be strings containing expressions that
  86. * make use of the global variables.
  87. *
  88. * Because string can ambiguously represent actual strings
  89. * and expressions. Every string is parsed as an expression
  90. * and will fall-back to a regular unparsed string type
  91. * if the parser fails to perform the evaluation.
  92. * To literally pass a string argument that contains an expression
  93. * that would be evaluated by the parser, simply use escaped quotes:
  94. *
  95. * \code {.json}
  96. * {"name" : "escapedExpr", "value" : "\"1+1\""}
  97. * \endcode
  98. *
  99. * <h2>Example markup</h2>
  100. *
  101. * Example JSON markup for a topology description:
  102. * \code {.json}
  103. * {
  104. * "threadPools" : {
  105. * "default" : {"priority" : 0.5},
  106. * "myPool0" : {"yieldMode" : "SPIN"}
  107. * },
  108. * "globals" : [
  109. * {"name" : "fiz", "value" : 3.14},
  110. * {"name" : "baz", "value" : "-fiz"}
  111. * ],
  112. * "blocks" : [
  113. * {
  114. * "id" : "id0",
  115. * "path" : "/blocks/foo",
  116. * "args" : [1, "hello"],
  117. * "calls" : [
  118. * ["setFoo", true],
  119. * ["updateBaz", "baz"]
  120. * ]
  121. * },
  122. * {
  123. * "id" : "id1",
  124. * "path" : "/blocks/bar",
  125. * "threadPool" : "myPool0",
  126. * "args" : [],
  127. * "calls" : [
  128. * ["setBar", "OK"],
  129. * ]
  130. * }
  131. * ],
  132. * "connections", [
  133. * ["self", "inputX", "id0", "in0"],
  134. * ["id0", "out0", "id1", "in0"],
  135. * ["id1", "out0", "self", "outputY"],
  136. * ]
  137. * }
  138. * \endcode
  139. * \param json a JSON formatted string
  140. */
  141. static std::shared_ptr<Topology> make(const std::string &json);
  142. //! Create a new empty topology
  143. Topology(void);
  144. /*!
  145. * Cleanup and destroy a topology.
  146. * This call simply disconnects all data flows and commits the changes.
  147. */
  148. ~Topology(void);
  149. //! Set the thread pool used by all blocks in this topology.
  150. void setThreadPool(const ThreadPool &threadPool);
  151. //! Get the thread pool used by all blocks in this topology.
  152. const ThreadPool &getThreadPool(void) const;
  153. /*!
  154. * Set the displayable alias for the specified input port.
  155. */
  156. void setInputAlias(const std::string &portName, const std::string &alias);
  157. /*!
  158. * Set the displayable alias for the specified output port.
  159. */
  160. void setOutputAlias(const std::string &portName, const std::string &alias);
  161. /*!
  162. * Get a vector of info about all of the input ports available.
  163. */
  164. std::vector<PortInfo> inputPortInfo(void);
  165. /*!
  166. * Get a vector of info about all of the output ports available.
  167. */
  168. std::vector<PortInfo> outputPortInfo(void);
  169. /*!
  170. * Commit changes made to the topology.
  171. * Actual data flows created by connect and disconnect
  172. * are not changed until a call to commit() is performed.
  173. * Once commit is called, actual data flow processing begins.
  174. * At this point the scheduler will call the block's work()
  175. * functions when the data at its inputs becomes available.
  176. */
  177. void commit(void);
  178. /*!
  179. * Wait for a period of data flow inactivity.
  180. * This call blocks until all flows become inactive for at least idleDuration seconds.
  181. * This call is intended primarily for unit testing purposes to allow the topology
  182. * to propagate test data through the entire flow from sources to sinks.
  183. * Use a timeout value of 0.0 to wait forever for topology to become idle.
  184. * \param idleDuration the maximum number of seconds that flows may idle
  185. * \param timeout the maximum number of seconds to wait in this call
  186. * \return true if the flow graph became inactive before the timeout
  187. */
  188. bool waitInactive(const double idleDuration = 0.1, const double timeout = 1.0);
  189. /*!
  190. * Create a connection between a source port and a destination port.
  191. * \param src the data source (local/remote block/topology)
  192. * \param srcPort an identifier for the source port (string or index)
  193. * \param dst the data destination (local/remote block/topology)
  194. * \param dstPort an identifier for the destination port (string or index)
  195. */
  196. template <
  197. typename SrcType, typename SrcPortType,
  198. typename DstType, typename DstPortType>
  199. void connect(
  200. SrcType &&src, const SrcPortType &srcPort,
  201. DstType &&dst, const DstPortType &dstPort);
  202. /*!
  203. * Remove a connection between a source port and a destination port.
  204. * \param src the data source (local/remote block/topology)
  205. * \param srcPort an identifier for the source port (string or index)
  206. * \param dst the data destination (local/remote block/topology)
  207. * \param dstPort an identifier for the destination port (string or index)
  208. */
  209. template <
  210. typename SrcType, typename SrcPortType,
  211. typename DstType, typename DstPortType>
  212. void disconnect(
  213. SrcType &&src, const SrcPortType &srcPort,
  214. DstType &&dst, const DstPortType &dstPort);
  215. /*!
  216. * Disconnect all data flows inside this topology.
  217. * This call can be recursive and will disconnect all
  218. * on the other sub-topologies within this data flow.
  219. * No changes to the data flow occur until commit().
  220. * \param recursive true to recurse through sub-topologies
  221. */
  222. void disconnectAll(const bool recursive = false);
  223. //! Create a connection between a source port and a destination port.
  224. void _connect(
  225. const Object &src, const std::string &srcPort,
  226. const Object &dst, const std::string &dstPort);
  227. //! Remove a connection between a source port and a destination port.
  228. void _disconnect(
  229. const Object &src, const std::string &srcPort,
  230. const Object &dst, const std::string &dstPort);
  231. /*!
  232. * Export a function call on this topology to set/get parameters.
  233. * This call will automatically register a slot of the same name.
  234. * \param name the name of the callable
  235. * \param call the bound callable method
  236. */
  237. void registerCallable(const std::string &name, const Callable &call);
  238. /*!
  239. * Query performance statistics for all blocks in the topology.
  240. *
  241. * Example JSON markup for stats reporting:
  242. * (The actual stats markup has many more fields.)
  243. * \code {.json}
  244. * {
  245. * "unique_id_of_blockA" : {
  246. * "blockName" : "blockA",
  247. * "numWorkCalls" : 12345,
  248. * "outputStats" : [
  249. * {"portName" : "0", totalElements : 42},
  250. * ]
  251. * },
  252. * "unique_id_of_blockB" : {
  253. * "blockName" : "blockB",
  254. * "numWorkCalls" : 6789,
  255. * "inputStats" : [
  256. * {"portName" : "0", totalElements : 0},
  257. * {"portName" : "1", totalElements : 100}
  258. * ]
  259. * }
  260. * }
  261. * \endcode
  262. *
  263. * \return a JSON formatted object string
  264. */
  265. std::string queryJSONStats(void);
  266. /*!
  267. * Dump the topology state to a JSON formatted string.
  268. * This call provides a structured view of the hierarchy.
  269. *
  270. * Example request object {"mode" : "flat"}
  271. *
  272. * Mode options:
  273. * - "flat": Flattened hierarchies - only processing blocks.
  274. * - "top": Only top-level blocks without hierarchy traversal.
  275. * - "rendered": Flattened hierarchies with traversal blocks.
  276. *
  277. * Example JSON markup for presenting the topology:
  278. * \code {.json}
  279. * {
  280. * "blocks" : {
  281. * "uidblockA" : {
  282. * "name" : "blockA",
  283. * "outputs" : [
  284. * {"name": "outx", "isSigSlot": false}
  285. * ]
  286. * },
  287. * "uidblockB" : {
  288. * "name" : "blockB",
  289. * "inputs" : [
  290. * {"name": "in0", "isSigSlot": false},
  291. * {"name": "setFoo", "isSigSlot": true}
  292. * ],
  293. * "outputs" : [
  294. * {"name": "out0", "isSigSlot": false},
  295. * {"name": "barChanged", "isSigSlot": true}
  296. * ]
  297. * },
  298. * "uidblockC" : {
  299. * "name" : "blockC",
  300. * "inputs" : [
  301. * {"name": "iny", "isSigSlot": false}
  302. * ],
  303. * "blocks" : {#this is a hierarchy of blocks#},
  304. * "connections" : {#this is a hierarchy of blocks#},
  305. * }
  306. * },
  307. * "connections", [
  308. * {"srcId": "uidblockA", "srcName": "outx", "dstId": "uidblockB", "srcName": "in0"},
  309. * {"srcId": "uidblockB", "srcName": "out0", "dstId": "uidblockC", "srcName": "iny"}
  310. * ]
  311. * }
  312. * \endcode
  313. *
  314. * \param request a JSON object string with key/value arguments
  315. * \return a JSON formatted object string
  316. */
  317. std::string dumpJSON(const std::string &request = "{}");
  318. /*!
  319. * Convert the topology to a string containing dot markup.
  320. * This markup can be passed into the dot tool to create a visual graph.
  321. * The markup can represent the connections as specified by the user,
  322. * or if flat is true, the complete flattened topology with
  323. * network blocks for crossing process/computer boundaries.
  324. *
  325. * Example request string {"mode" : "flat", "port" : "all"}
  326. *
  327. * Mode options:
  328. * - "flat": Flattened hierarchies - only processing blocks.
  329. * - "top": Only top-level blocks without hierarchy traversal.
  330. * - "rendered": Flattened hierarchies with traversal blocks.
  331. *
  332. * Port options:
  333. * - "all" Show all available IO ports.
  334. * - "connected" Show connected ports only.
  335. *
  336. * \param request a JSON object string with configuration parameters
  337. * \return the dot markup as a string
  338. */
  339. std::string toDotMarkup(const std::string &request = "{}");
  340. /*!
  341. * Call a method on a derived instance with opaque input and return types.
  342. * \param name the name of the method as a string
  343. * \param inputArgs an array of input arguments
  344. * \param numArgs the size of the input array
  345. * \return the return value as type Object
  346. */
  347. Object opaqueCallMethod(const std::string &name, const Object *inputArgs, const size_t numArgs) const;
  348. protected:
  349. /*!
  350. * The opaque call handler handles dispatching calls to registered methods.
  351. * The user may overload this call to install their own custom handler.
  352. * \throws BlockCallNotFound when no call registered for the provided name
  353. * \throws Exception when the registered call itself throws an exception
  354. * \param name the name of a call registered to this Block with registerCall()
  355. * \param inputArgs an array of input arguments wrapped in type Object
  356. * \param numArgs the number of arguments in the array inputArgs
  357. * \return the result of making the registered call, wrapped in type Object
  358. */
  359. virtual Object opaqueCallHandler(const std::string &name, const Object *inputArgs, const size_t numArgs);
  360. private:
  361. Topology(const Topology &){} // non construction-copyable
  362. Topology &operator=(const Topology &){return *this;} // non copyable
  363. public:
  364. struct Impl;
  365. std::shared_ptr<Impl> _impl;
  366. };
  367. } //namespace Pothos