source: finroc_plugins_data_ports/tGenericPort.h @ 140:aca37ccb431b

14.08
Last change on this file since 140:aca37ccb431b was 140:aca37ccb431b, checked in by Max Reichardt <max.reichardt@…>, 3 months ago

Makes tGenericPort::GetUnusedBuffer() always reset buffer timestamp (to avoid accidental reuse of timestamps of recycled buffers)

File size: 12.4 KB
Line 
1//
2// You received this file as part of Finroc
3// A framework for intelligent robot control
4//
5// Copyright (C) Finroc GbR (finroc.org)
6//
7// This program is free software; you can redistribute it and/or modify
8// it under the terms of the GNU General Public License as published by
9// the Free Software Foundation; either version 2 of the License, or
10// (at your option) any later version.
11//
12// This program is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15// GNU General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License along
18// with this program; if not, write to the Free Software Foundation, Inc.,
19// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20//
21//----------------------------------------------------------------------
22/*!\file    plugins/data_ports/tGenericPort.h
23 *
24 * \author  Max Reichardt
25 *
26 * \date    2012-10-24
27 *
28 * \brief   Contains tGenericPort
29 *
30 * \b tGenericPort
31 *
32 * Wrapper class for ports whose type is not known at compile time.
33 *
34 * (note: Get and Publish are currently only implemented based on deep-copying data.
35 *  Therefore, Generic ports are only efficient for data types that can be copied cheaply)
36 */
37//----------------------------------------------------------------------
38#ifndef __plugins__data_ports__tGenericPort_h__
39#define __plugins__data_ports__tGenericPort_h__
40
41//----------------------------------------------------------------------
42// External includes (system with <>, local with "")
43//----------------------------------------------------------------------
44#include "core/port/tPortWrapperBase.h"
45
46//----------------------------------------------------------------------
47// Internal includes with ""
48//----------------------------------------------------------------------
49#include "plugins/data_ports/api/tGenericPortImplementation.h"
50
51//----------------------------------------------------------------------
52// Namespace declaration
53//----------------------------------------------------------------------
54namespace finroc
55{
56namespace data_ports
57{
58
59//----------------------------------------------------------------------
60// Forward declarations / typedefs / enums
61//----------------------------------------------------------------------
62
63namespace api
64{
65template <typename LISTENER, bool FIRST_LISTENER>
66class tPortListenerAdapterGeneric;
67template <typename LISTENER, bool FIRST_LISTENER>
68class tPortListenerAdapterGenericForPointer;
69}
70
71//----------------------------------------------------------------------
72// Class declaration
73//----------------------------------------------------------------------
74//! Generic port
75/*!
76 * Wrapper class for ports whose type is not known at compile time.
77 *
78 * (note: Get and Publish are currently only implemented based on deep-copying data.
79 *  Therefore, Generic ports are only efficient for data types that can be copied cheaply)
80 */
81class tGenericPort : public core::tPortWrapperBase
82{
83
84//----------------------------------------------------------------------
85// Public methods and typedefs
86//----------------------------------------------------------------------
87public:
88
89  /*!
90   * Empty constructor in case port is created later
91   */
92  tGenericPort() :
93    implementation()
94  {}
95
96  /*!
97   * Constructor takes variadic argument list... just any properties you want to assign to port.
98   *
99   * The first string is interpreted as port name, the second possibly as config entry (relevant for parameters only).
100   * A framework element pointer is interpreted as parent.
101   * unsigned int arguments are interpreted as flags.
102   * A tQueueSettings argument creates an input queue with the specified settings.
103   * tBounds<T> are port's bounds.
104   * tUnit argument is port's unit.
105   * tPortCreationBase argument is copied. This is only allowed as first argument.
106   */
107  template <typename ... ARGS>
108  tGenericPort(const ARGS&... args)
109  {
110    tConstructorArguments<common::tAbstractDataPortCreationInfo> creation_info(args...);
111    if ((creation_info.data_type.GetTypeTraits() & rrlib::rtti::trait_flags::cIS_BINARY_SERIALIZABLE) == 0)
112    {
113      throw std::runtime_error("Only binary serializable types may be used in data ports");
114    }
115    implementation = api::tGenericPortImplementation::GetImplementation(creation_info.data_type);
116    SetWrapped(implementation->CreatePort(creation_info));
117  }
118
119  /*!
120   * \param listener Listener to add
121   *
122   * \tparam LISTENER Listener class needs to implement a method
123   * void OnPortChange(const rrlib::rtti::tGenericObject& value, tChangeContext& change_context)
124   *
125   * (It's preferred to add listeners before port is initialized)
126   * (Note: Buffer in 'value' always has data type of port backend (e.g. tNumber instead of double)
127   */
128  template <typename TListener>
129  void AddPortListener(TListener& listener);
130
131  /*!
132   * \param listener Listener to add
133   *
134   * \tparam LISTENER Listener class needs to implement a method
135   * void OnPortChange(tPortDataPointer<const rrlib::rtti::tGenericObject>& value, tChangeContext& change_context)
136   *
137   * (It's preferred to add listeners before port is initialized)
138   * (Note: Buffer in 'value' always has data type of port backend (e.g. tNumber instead of double)
139   */
140  template <typename TListener>
141  void AddPortListenerForPointer(TListener& listener);
142
143  /*!
144   * \param listener Listener to add
145   *
146   * \tparam LISTENER Listener class needs to implement a method
147   * void OnPortChange(tChangeContext& change_context)
148   *
149   * (It's preferred to add listeners before port is initialized)
150   */
151  template <typename TListener>
152  void AddPortListenerSimple(TListener& listener);
153
154  /*!
155   * Publish buffer through port
156   * (not in normal operation, but from browser; difference: listeners on this port will be notified)
157   *
158   * \param pointer Buffer with data
159   * \param notify_listener_on_this_port Notify listener on this port?
160   * \param change_constant Change constant to use for publishing operation
161   * \return Error message if something did not work
162   */
163  inline std::string BrowserPublish(tPortDataPointer<rrlib::rtti::tGenericObject>& pointer, bool notify_listener_on_this_port = true,
164                                    tChangeStatus change_constant = tChangeStatus::CHANGED)
165  {
166    if (IsCheaplyCopiedType(pointer->GetType()))
167    {
168#ifndef RRLIB_SINGLE_THREADED
169      typename optimized::tCheapCopyPort::tUnusedManagerPointer pointer2(static_cast<optimized::tCheaplyCopiedBufferManager*>(pointer.implementation.Release()));
170      return static_cast<optimized::tCheapCopyPort*>(GetWrapped())->BrowserPublishRaw(pointer2, notify_listener_on_this_port, change_constant);
171#else
172      return static_cast<optimized::tSingleThreadedCheapCopyPortGeneric*>(GetWrapped())->BrowserPublishRaw(*pointer, pointer.GetTimestamp(), notify_listener_on_this_port, change_constant);
173#endif
174    }
175    else
176    {
177      typename standard::tStandardPort::tUnusedManagerPointer pointer2(static_cast<standard::tPortBufferManager*>(pointer.implementation.Release()));
178      static_cast<standard::tStandardPort*>(GetWrapped())->BrowserPublish(pointer2, notify_listener_on_this_port, change_constant);
179    }
180    return "";
181  }
182
183  /*!
184   * Dequeue all elements currently in input queue
185   *
186   * \param result_buffer Vector to fill with dequeued buffers (any contents in vector when calling this method are discarded)
187   */
188  void DequeueAllBuffers(std::vector<tPortDataPointer<const rrlib::rtti::tGenericObject>>& result_buffer)
189  {
190    implementation->DequeueAllBuffers(*GetWrapped(), result_buffer);
191  }
192
193  /*!
194   * Gets port's current value
195   *
196   * \param result Buffer to (deep) copy port's current value to
197   * \param timestamp Buffer to copy timestamp attached to data to (optional)
198   */
199  inline void Get(rrlib::rtti::tGenericObject& result)
200  {
201    rrlib::time::tTimestamp timestamp;
202    Get(result, timestamp);
203  }
204  inline void Get(rrlib::rtti::tGenericObject& result, rrlib::time::tTimestamp& timestamp)
205  {
206    implementation->Get(*GetWrapped(), result, timestamp);
207  }
208
209  /*!
210   * Gets Port's current value in buffer
211   *
212   * \param strategy Strategy to use for get operation
213   * \return Buffer with port's current value with read lock.
214   *
215   * Note: Buffer always has data type of port backend (e.g. tNumber instead of double)
216   * If this is not desired, use pass-by-value-Get Operation above.
217   */
218  inline tPortDataPointer<const rrlib::rtti::tGenericObject> GetPointer(tStrategy strategy = tStrategy::DEFAULT)
219  {
220    return implementation->GetPointer(*GetWrapped(), strategy);
221  }
222
223  /*!
224   * \return Port's default value (NULL if none has been set)
225   */
226  const rrlib::rtti::tGenericObject* GetDefaultValue()
227  {
228    return implementation->GetDefaultValue(*GetWrapped());
229  }
230
231  /*!
232   * Note: Buffer always has data type of port backend (e.g. tNumber instead of double)
233   * If this is not desired, use pass-by-value-Publish Operation below.
234   *
235   * \return Unused buffer.
236   * Buffers to be published using this port should be acquired using this function.
237   * The buffer might contain old data, so it should be cleared prior to using.
238   */
239  inline tPortDataPointer<rrlib::rtti::tGenericObject> GetUnusedBuffer()
240  {
241    auto result = implementation->GetUnusedBuffer(*GetWrapped());
242    result.SetTimestamp(rrlib::time::cNO_TIME);
243    return result;
244  }
245
246  /*!
247   * \return Wrapped port. For rare case that someone really needs to access ports.
248   */
249  inline common::tAbstractDataPort* GetWrapped() const
250  {
251    return static_cast<common::tAbstractDataPort*>(tPortWrapperBase::GetWrapped());
252  }
253
254  /*!
255   * \return Has port changed since last changed-flag-reset?
256   */
257  inline bool HasChanged() const
258  {
259    return this->GetWrapped()->HasChanged();
260  }
261
262  /*!
263   * Publish Data Buffer. This data will be forwarded to any connected ports.
264   * Should only be called on output ports.
265   *
266   * \param data Data to publish. It will be deep-copied.
267   * \param timestamp Timestamp to attach to data.
268   */
269  inline void Publish(const rrlib::rtti::tGenericObject& data, const rrlib::time::tTimestamp& timestamp = rrlib::time::cNO_TIME)
270  {
271    implementation->Publish(*GetWrapped(), data, timestamp);
272  }
273
274  /*!
275   * Set new bounds
276   * (This is not thread-safe and must only be done in "pause mode")
277   *
278   * \param b New Bounds
279   */
280  inline void SetBounds(const rrlib::rtti::tGenericObject& min, const rrlib::rtti::tGenericObject& max)
281  {
282    implementation->SetBounds(*GetWrapped(), min, max);
283  }
284
285  /*!
286   * \param pull_request_handler Object that handles any incoming pull requests - null if there is none (typical case)
287   */
288  void SetPullRequestHandler(tPullRequestHandler<rrlib::rtti::tGenericObject>* pull_request_handler)
289  {
290    implementation->SetPullRequestHandler(*GetWrapped(), pull_request_handler);
291  }
292
293  /*!
294   * Wraps raw port
295   * Throws std::runtime_error if port to wrap has invalid type.
296   *
297   * \param wrap Type-less tAbstractPort to wrap as tGenericPort
298   * \param use_backend_type_only Use only the internal data type that used in the port backend? (e.g. tNumber instead of double; relevant e.g. for Get() and Publish() methods)
299   */
300  static tGenericPort Wrap(core::tAbstractPort& wrap, bool use_backend_type_only = false)
301  {
302    if (!IsDataFlowType(wrap.GetDataType()))
303    {
304      throw rrlib::util::tTraceableException<std::runtime_error>(wrap.GetDataType().GetName() + " is no data flow type and cannot be wrapped.");
305    }
306    tGenericPort port;
307    port.SetWrapped(&wrap);
308    port.implementation = api::tGenericPortImplementation::GetImplementation(
309                            ((!use_backend_type_only) && wrap.GetWrapperDataType() != NULL) ? wrap.GetWrapperDataType() : wrap.GetDataType());
310    return port;
311  }
312
313//----------------------------------------------------------------------
314// Private fields and methods
315//----------------------------------------------------------------------
316private:
317
318  template <typename LISTENER, bool FIRST_LISTENER>
319  friend class api::tPortListenerAdapterGeneric;
320  template <typename LISTENER, bool FIRST_LISTENER>
321  friend class api::tPortListenerAdapterGenericForPointer;
322
323  /** Implementation of port functionality */
324  api::tGenericPortImplementation* implementation;
325
326};
327
328//----------------------------------------------------------------------
329// End of namespace declaration
330//----------------------------------------------------------------------
331}
332}
333
334#include "plugins/data_ports/tInputPort.h"
335#include "plugins/data_ports/tGenericPort.hpp"
336#include "plugins/data_ports/api/tPortListenerAdapter.h"
337
338#endif
Note: See TracBrowser for help on using the repository browser.