Replacing events in RTT 2.0 & script state machines

One of the more drastic things to change in RTT 2.0 is that RTT::Events as
they were will no longer exist. Their drawback were that events could get
lost, (it had a fifo buffer of only size one) and especially, it could trash the
event emitter if 'hostile' callbacks were attached synchronously.

We (at least some of us) agreed to replace them by dataflow ports. You can
specify a buffering policy, they are thread-safe and real-time, distributed and
the callback is handling is simple and efficient and they can wake up task
contexts.

Ports solve event delivery across components, but one convenience of the old
event implementation was that our script state machines could react to them.
The question is how to do this in 2.0.

Clearly, we'll need a way to react to data delivery on ports on one hand
(events generated by peer components) but also allow the algorithms *inside*
the component to emit an event which is handled by its own state machines
(local delivery). If you used this before, you know what I mean.

Proposed solution:
1) Create a 'LoopBackPort' next to InputPort and OutputPort. It is a data flow
port that reads what you wrote into it. you can connect it to itself and as
such set a buffering policy etc. The scripting state machine react to data on
this port, like they react to data from InputPorts. I'm a bit uneasy about the
'LoopBackPort' name though.

2) Change the event transition syntax from:

 transition event_name(args) 
   if ( guard(args) ) then { /* transition code */ } select NEXT_STATE

to
 transition port_name(arg) 
   if ( guard(arg) ) then { /* transition code */ } select NEXT_STATE

Where the above code snippet is equivalent to writing:
 transition if( port_name.read(arg) == NewData && guard(arg) )
  then { /* transition code */ } select NEXT_STATE

but the short version would be allowed for avoiding repetitive code.

The only thing the user then has to add is using addEventPort() for
registering the InputPort or LoopBackPort to the dataflow interface. These will
trigger the TaskContext when data arrives, which will execute the script,
which will read the port and get NewData.

The major difference between old and new a user will feel is that old events
allowed multipe arguments (ie event_name(a,b,c)) while ports only allow one
argument (ie port_name(a) ). This means you need to rewrite multiple arguments
into a C struct and register it with the type system. This shouldn't be a big
issue once typekits can be generated on the fly (we're working on this,
remember), but for large applications that rely on this, it's a painful
change.

Thoughts ?

Peter

Replacing events in RTT 2.0 & script state machines

I'm picking up this thread to report on the current status on this issue...

On Thursday 21 January 2010 16:24:19 Peter Soetens wrote:
> One of the more drastic things to change in RTT 2.0 is that RTT::Events as
> they were will no longer exist. Their drawback were that events could get
> lost, (it had a fifo buffer of only size one) and especially, it could
> trash the event emitter if 'hostile' callbacks were attached
> synchronously.
>
> We (at least some of us) agreed to replace them by dataflow ports. You can
> specify a buffering policy, they are thread-safe and real-time, distributed
> and the callback is handling is simple and efficient and they can wake up
> task contexts.

This is no longer completely true. System level events will have to go through
ports and responding to such an event will require an input port. However, for
coupling a component's interface stimulus to a component's state machine or C
code, another mechanism came in place.

>
> Ports solve event delivery across components, but one convenience of the
> old event implementation was that our script state machines could react to
> them. The question is how to do this in 2.0.

The answer was not to force everything into data ports but to extend the
Operation class. Operation is an object that represents a function in your
component and does all management that surrounds it. One 2.0 possibility you
have is to add a 'signal' handler function to it. This allows any function in
the component to react to the call to a certain operation in the interface.

An example. Say that we have the operation 'setParam' that ties to a C++
function that changes a certain parameter. We register the operation as such:

class ATask : public TaskContext {
public:
  bool setParam(string name, double value);
 
  ATask() : TaskContext("ATask") {
     this->provides()->addOperation("setParam",&ATask::setParam, this);
  }
};

The return value of setParam will be the return value of each method
invocation of the "setParam" operation. Now if a state machine or C function
wishes to respond to such a call too (ie be notified when a parameter was
updated), it goes like this:
class ATask : public TaskContext {
  bool param_handler(string name, double value);
public:
  bool setParam(string name, double value);
 
  ATask() : TaskContext("ATask") {
     this->provides()->addOperation("setParam",&ATask::setParam, this)
        .signals(&Task::param_handler, this);
  }
};

Of course setParam could have called param_handler itself, but if
param_handler was defined in a sub-class of ATask or in a script, setParam
would not know that this needed to be done.

The state machine language will be able to attach these same signal hooks too,
and respond to the calling of an operation. Operations may choose not to call
a function and may exist to only signal. For example, for internal management,
we create a new operation 'paramViolation' that is used internally to notify a
scripting state machine:

class ATask : public TaskContext {
public:
  // note similarity to RTT::Event
  Operation<bool(string name, double value)> paramViolation;
 
  ATask() : TaskContext("ATask"), paramViolation("paramViolation") {
     // note: added to 'internal' service provider
     this->provides("internal")->addOperation( paramViolation );
  }
};

Note that instead of adding paramViolation to the public API, it is relocated
in the 'internal' service of the component. clients of the component are
expected not to use this interface (but we don't force them).

We can't 'call' operations directly (contrast to RTT 1.x), we always need a
Method object to do that for us:

class ATask : public TaskContext {
public:
  // note similarity to RTT::Event
  Operation<void(string name, double value)> paramViolation;
  Method<void(string name, double value)> signalViolation;
 
  ATask() : TaskContext("ATask"), paramViolation("paramViolation) {
     this->provides("internal")->addOperation( paramViolation );
     signalViolation = paramViolation.getImplementation();
  }
  void updateHook() {
      if (...) signalViolation("foo", -1.1);
  }
};

/Note: It's possible to add helper functions to skip creating this extra
Method, such that calling an operation from the component itself could be
reduced to (relies on TLSF):

  void updateHook() {
      if (...) paramViolation.method().call("foo", -1.1);
  }

/EndNote

Finally, a state machine script can react to this violation event by having:

StateMachine sm
  {
     var string param;
     var double param_arg;
     transition paramViolation(param, param_arg) 
            if ( param == "foo" ) select ParamErrorState
     // ... state declarations below
   }

Which is identical to the RTT 1.x Event handling. You can not react to an
operation of a peer component. This last syntax is not yet in beta1.

Regarding port based event interception, these mechanisms stay in place for
reacting to system level events.

Makes sense ?

Peter

Replacing events in RTT 2.0 & script state machines

On Thu, Jan 21, 2010 at 4:24 PM, Peter Soetens <peter [dot] soetens [..] ...> wrote:
> One of the more drastic things to change in RTT 2.0 is that RTT::Events as
> they were will no longer exist. Their drawback were that events could get
> lost, (it had a fifo buffer of only size one) and especially, it could trash the
> event emitter if 'hostile' callbacks were attached synchronously.
>
> We (at least some of us) agreed to replace them by dataflow ports. You can
> specify a buffering policy, they are thread-safe and real-time, distributed and
> the callback is handling is simple and efficient and they can wake up task
> contexts.
>
> Ports solve event delivery across components, but one convenience of the old
> event implementation was that our script state machines could react to them.
> The question is how to do this in 2.0.
>
> Clearly, we'll need a way to react to data delivery on ports on one hand
> (events generated by peer components) but also allow the algorithms *inside*
> the component to emit an event which is handled by its own state machines
> (local delivery). If you used this before, you know what I mean.

I know what you did last summer? :-)

Seriously, if I read the above, I get the impression that your only
concern for 2.0 is to offer the user the same capabilities as he had
with 1.x.
For me (and others around here), statemachines in 1.x are a PITA.
When I have to explain FSMs to somebody, I always get "red cheeks".
<quote>
You had a method in the interface of your component, but now you want
also to react on that "methodevent" in your FSM? Ha, you'll have to
create an event now and add that to the components API.
<quote>

[I guess I advocated this already before :-)]
Basically I would like to see the (internal) event used in the
statemachine _decoupled_ from the "thing" that triggers the event!

> Proposed solution:
> 1) Create a 'LoopBackPort' next to InputPort and OutputPort. It is a data flow
> port that reads what you wrote into it. you can connect it to itself and as
> such set a buffering policy etc. The scripting state machine react to data on
> this port, like they react to data from InputPorts. I'm a bit uneasy about the
> 'LoopBackPort' name though.

I think you need to set this "uneasy" step exactly because of the fact
that you don't distinguish between the above 2 concepts...

> 2) Change the event transition syntax from:
[...]
> The only thing the user then has to add is using addEventPort() for
> registering the InputPort or LoopBackPort to the dataflow interface. These will
> trigger the TaskContext when data arrives, which will execute the script,
> which will read the port and get NewData.
>
> The major difference between old and new a user will feel is that old events
> allowed multipe arguments (ie event_name(a,b,c)) while ports only allow one
> argument (ie port_name(a) ). This means you need to rewrite multiple arguments
> into a C struct and register it with the type system. This shouldn't be a big
> issue once typekits can be generated on the fly (we're working on this,
> remember), but for large applications that rely on this, it's a painful
> change.
>
> Thoughts ?

I don't know wether migration from 1.x legacy should be the primary concern.

Thx for working on this!

Klaas

Replacing events in RTT 2.0 & script state machines

On Monday 25 January 2010 17:09:24 Klaas Gadeyne wrote:
> On Thu, Jan 21, 2010 at 4:24 PM, Peter Soetens <peter [dot] soetens [..] ...>
wrote:
> > One of the more drastic things to change in RTT 2.0 is that RTT::Events
> > as they were will no longer exist. Their drawback were that events could
> > get lost, (it had a fifo buffer of only size one) and especially, it
> > could trash the event emitter if 'hostile' callbacks were attached
> > synchronously.
> >
> > We (at least some of us) agreed to replace them by dataflow ports. You
> > can specify a buffering policy, they are thread-safe and real-time,
> > distributed and the callback is handling is simple and efficient and they
> > can wake up task contexts.
> >
> > Ports solve event delivery across components, but one convenience of the
> > old event implementation was that our script state machines could react
> > to them. The question is how to do this in 2.0.
> >
> > Clearly, we'll need a way to react to data delivery on ports on one hand
> > (events generated by peer components) but also allow the algorithms
> > *inside* the component to emit an event which is handled by its own state
> > machines (local delivery). If you used this before, you know what I mean.
>
> I know what you did last summer? :-)
>
> Seriously, if I read the above, I get the impression that your only
> concern for 2.0 is to offer the user the same capabilities as he had
> with 1.x.

I'll come back to that later.

> For me (and others around here), statemachines in 1.x are a PITA.
> When I have to explain FSMs to somebody, I always get "red cheeks".

I don't. I even make a living out of it :-)

> <quote>
> You had a method in the interface of your component, but now you want
> also to react on that "methodevent" in your FSM? Ha, you'll have to
> create an event now and add that to the components API.
> <quote>

What you're aiming at is a UML-like protocol state machine, where transitions
define the valid operations of the component ? Can you propose script syntax +
semantics ? Take into account that the operation may(?) be called multiple
times before the SM had a chance to run.

We can add operations to the interface of a component by writing down
functions in program scripts already, but I can see that's not what you're
talking about.

>
> [I guess I advocated this already before :-)]
> Basically I would like to see the (internal) event used in the
> statemachine _decoupled_ from the "thing" that triggers the event!
>
> > Proposed solution:
> > 1) Create a 'LoopBackPort' next to InputPort and OutputPort. It is a data
> > flow port that reads what you wrote into it. you can connect it to itself
> > and as such set a buffering policy etc. The scripting state machine react
> > to data on this port, like they react to data from InputPorts. I'm a bit
> > uneasy about the 'LoopBackPort' name though.
>
> I think you need to set this "uneasy" step exactly because of the fact
> that you don't distinguish between the above 2 concepts...

Lets forget that 'solution' completely. But my humble solutions don't deny the
fact that 'reacting asynchronously to an event' needs a buffering policy.

>
> > 2) Change the event transition syntax from:
>
> [...]
>
> > The only thing the user then has to add is using addEventPort() for
> > registering the InputPort or LoopBackPort to the dataflow interface.
> > These will trigger the TaskContext when data arrives, which will execute
> > the script, which will read the port and get NewData.
> >
> > The major difference between old and new a user will feel is that old
> > events allowed multipe arguments (ie event_name(a,b,c)) while ports only
> > allow one argument (ie port_name(a) ). This means you need to rewrite
> > multiple arguments into a C struct and register it with the type system.
> > This shouldn't be a big issue once typekits can be generated on the fly
> > (we're working on this, remember), but for large applications that rely
> > on this, it's a painful change.
> >
> > Thoughts ?
>
> I don't know wether migration from 1.x legacy should be the primary
> concern.

It _is_, with respect to the scripting syntax. It's not my aim to invent a
new, better scripting language. I'm bad at it anyway, or at least, I was so in
the past. The major objective is to keep the 1.x scripting syntax in 2.x with
the minimal ammount of changes, such that existing users can at least reason
about the cost of transitioning to 2.x. If you want to write a script from
scratch, I recommend to use Lua, which Markus will offer us for endless joy in
our lives.

Peter

Replacing events in RTT 2.0 & script state machines

On Jan 21, 2010, at 10:24 , Peter Soetens wrote:

> One of the more drastic things to change in RTT 2.0 is that RTT::Events as
> they were will no longer exist. Their drawback were that events could get
> lost, (it had a fifo buffer of only size one) and especially, it could trash the
> event emitter if 'hostile' callbacks were attached synchronously.
>
> We (at least some of us) agreed to replace them by dataflow ports. You can
> specify a buffering policy, they are thread-safe and real-time, distributed and
> the callback is handling is simple and efficient and they can wake up task
> contexts.

This concept sounds really odd, and it might be hard to educate new users that ports communicate data between components, _and_ they also can be used like events to trigger changes in a state machine (ie the state machine is reacting to the arrival of data, but the actual data is irrelevant - for transitions without associated data). Having said that, I don't have a complete enough perspective on all the v2.0 changes yet, so I don't have anything else to suggest.

> Ports solve event delivery across components, but one convenience of the old
> event implementation was that our script state machines could react to them.
> The question is how to do this in 2.0.

Yes, we use events a _lot_ to do this.

> Clearly, we'll need a way to react to data delivery on ports on one hand
> (events generated by peer components) but also allow the algorithms *inside*
> the component to emit an event which is handled by its own state machines
> (local delivery). If you used this before, you know what I mean.

Yep.

> Proposed solution:
> 1) Create a 'LoopBackPort' next to InputPort and OutputPort. It is a data flow
> port that reads what you wrote into it. you can connect it to itself and as
> such set a buffering policy etc. The scripting state machine react to data on
> this port, like they react to data from InputPorts. I'm a bit uneasy about the
> 'LoopBackPort' name though.

Sounds kind of ugly unfortunately. I guess the LoopBackPort is essentially a local-only, In/Out port then (in concept, not implementation)?

> 2) Change the event transition syntax from:
>

> transition event_name(args) 
>   if ( guard(args) ) then { /* transition code */ } select NEXT_STATE
> 

> to
>
> transition port_name(arg) 
>   if ( guard(arg) ) then { /* transition code */ } select NEXT_STATE
> 

> Where the above code snippet is equivalent to writing:
>
> transition if( port_name.read(arg) == NewData && guard(arg) )
>  then { /* transition code */ } select NEXT_STATE
> 

> but the short version would be allowed for avoiding repetitive code.
>
> The only thing the user then has to add is using addEventPort() for
> registering the InputPort or LoopBackPort to the dataflow interface. These will
> trigger the TaskContext when data arrives, which will execute the script,
> which will read the port and get NewData.

So you replace your RTT::Event<> with a RTT::LoopBackPort<> or a RTT::InputPort<>, and then just write data to that port and the state machine reacts the same as RTT v1? No other changes (bar the multiple args situation)? For events without data, would the port just carry an int (or similar) whose value is simply ignored?

> The major difference between old and new a user will feel is that old events
> allowed multipe arguments (ie event_name(a,b,c)) while ports only allow one
> argument (ie port_name(a) ). This means you need to rewrite multiple arguments
> into a C struct and register it with the type system. This shouldn't be a big
> issue once typekits can be generated on the fly (we're working on this,
> remember), but for large applications that rely on this, it's a painful
> change.

ARG!! Yuck! We use multiple arguments a lot with events (and I think some of the existing OCL code does too, IIRC). We will have to have auto-generated typekits to make this at all useable. And still it seems a hell of change to have to generate a specialist structure on a per event basis (for those events with multiple args). So that leads to the question: what would it take to provide a data port that supports multiple pieces of data? :-) For synchronization reasons, I can think of other uses for this.

> Thoughts ?

This still needs work. A lot of the other changes proposed for v2 sound good and have been thrashed through enough by now, to feel like reasonable, desireable changes. I'm not convinced yet by this one though.

Stephen

Replacing events in RTT 2.0 & script state machines

On Thursday 21 January 2010 16:39:52 S Roderick wrote:
> On Jan 21, 2010, at 10:24 , Peter Soetens wrote:
> > One of the more drastic things to change in RTT 2.0 is that RTT::Events
> > as they were will no longer exist. Their drawback were that events could
> > get lost, (it had a fifo buffer of only size one) and especially, it
> > could trash the event emitter if 'hostile' callbacks were attached
> > synchronously.
> >
> > We (at least some of us) agreed to replace them by dataflow ports. You
> > can specify a buffering policy, they are thread-safe and real-time,
> > distributed and the callback is handling is simple and efficient and they
> > can wake up task contexts.
>
> This concept sounds really odd, and it might be hard to educate new users
> that ports communicate data between components, _and_ they also can be
> used like events to trigger changes in a state machine (ie the state
> machine is reacting to the arrival of data, but the actual data is
> irrelevant - for transitions without associated data). Having said that, I
> don't have a complete enough perspective on all the v2.0 changes yet, so I
> don't have anything else to suggest.

Remind that in most frameworks, a data sample sent/broadcasted *is* what they
call an event, ie the CORBA event service, but also ROS attaches callbacks
('event handlers') to 'topics'. So we thought that it would be more natural to
have one kind of event instead of a data flow event and an execution
flow/callback event. The main purpose here is to find a unified way to react to
events, local or remote and to be able to specify (at deployment or script
loading time) how the buffering (if any) is done. Reacting to a local event
should be the same as reacting to an event of another component.

>
> > Ports solve event delivery across components, but one convenience of the
> > old event implementation was that our script state machines could react
> > to them. The question is how to do this in 2.0.
>
> Yes, we use events a _lot_ to do this.
>
> > Clearly, we'll need a way to react to data delivery on ports on one hand
> > (events generated by peer components) but also allow the algorithms
> > *inside* the component to emit an event which is handled by its own state
> > machines (local delivery). If you used this before, you know what I mean.
>
> Yep.
>
> > Proposed solution:
> > 1) Create a 'LoopBackPort' next to InputPort and OutputPort. It is a data
> > flow port that reads what you wrote into it. you can connect it to itself
> > and as such set a buffering policy etc. The scripting state machine react
> > to data on this port, like they react to data from InputPorts. I'm a bit
> > uneasy about the 'LoopBackPort' name though.
>
> Sounds kind of ugly unfortunately. I guess the LoopBackPort is essentially
> a local-only, In/Out port then (in concept, not implementation)?

Yes. It would implement OutputPortInterface and InputPortInterface. My
reasoning is indeed flawed. What if two state machines react to data on this
port, both would need to receive the same set of events? A buffer+policy per
subscriber would be needed to provide this.

>
> > 2) Change the event transition syntax from:
> >

> > transition event_name(args)
> >   if ( guard(args) ) then { /* transition code */ } select NEXT_STATE
> > 

> > to
> >
> > transition port_name(arg)
> >   if ( guard(arg) ) then { /* transition code */ } select NEXT_STATE
> > 

> > Where the above code snippet is equivalent to writing:
> >
> > transition if( port_name.read(arg) == NewData && guard(arg) )
> >  then { /* transition code */ } select NEXT_STATE
> > 

> > but the short version would be allowed for avoiding repetitive code.
> >
> > The only thing the user then has to add is using addEventPort() for
> > registering the InputPort or LoopBackPort to the dataflow interface.
> > These will trigger the TaskContext when data arrives, which will execute
> > the script, which will read the port and get NewData.
>
> So you replace your RTT::Event<> with a RTT::LoopBackPort<> or a
> RTT::InputPort<>, and then just write data to that port and the state
> machine reacts the same as RTT v1?

Yes, except that you won't have overruns.

> No other changes (bar the multiple args
> situation)?

None intended nor forseen.

> For events without data, would the port just carry an int (or
> similar) whose value is simply ignored?

We could use a special type (unused_type) which signifies a 'void'. You
wouldn't need to specify an argument in that case in the script.

>
> > The major difference between old and new a user will feel is that old
> > events allowed multipe arguments (ie event_name(a,b,c)) while ports only
> > allow one argument (ie port_name(a) ). This means you need to rewrite
> > multiple arguments into a C struct and register it with the type system.
> > This shouldn't be a big issue once typekits can be generated on the fly
> > (we're working on this, remember), but for large applications that rely
> > on this, it's a painful change.
>
> ARG!! Yuck! We use multiple arguments a lot with events (and I think some
> of the existing OCL code does too, IIRC). We will have to have
> auto-generated typekits to make this at all useable. And still it seems a
> hell of change to have to generate a specialist structure on a per event
> basis (for those events with multiple args). So that leads to the
> question: what would it take to provide a data port that supports multiple
> pieces of data? :-) For synchronization reasons, I can think of other uses
> for this.

There are 'theoretical' solutions for this. especially if you generate the
typekits. For example, ROS allows to publish a composed topic by only
specifying the components, such as in "rostopic pub Vector -- 1.0 2.0 3.0"
The inverse could be thought of too, so having

struct Vector {double x,y,z;};
//in script:
var double a,b,c;
transition port_name(a, b, c) if ( a < b < c) then  select NEXT
// is equivalent to:
var Vector v;
transition port_name(v) if (v.a < v.b < v.c) then select NEXT

This could be possible in scripting, I'm less sure of it in C++ code,
overloading read() that is. We'd need boost::fusion for this, and I believe
the Orocos users is not yet ready for fusion's compile errors when
accidentally providing the wrong argument.

>
> > Thoughts ?
>
> This still needs work. A lot of the other changes proposed for v2 sound
> good and have been thrashed through enough by now, to feel like
> reasonable, desireable changes. I'm not convinced yet by this one though.

The 'true' solution I'm looking for is to provide what you need from a design
perspective. Meaning, the architecture of your application remains the same,
but the way you express it in code can be different. If a change requires
redesiging an application, I think upgrading to 2.0 will be unfeasible. If
it's more a matter of syntax, I think it is doable.

Peter

Replacing events in RTT 2.0 & script state machines

On Jan 21, 2010, at 11:18 , Peter Soetens wrote:

> On Thursday 21 January 2010 16:39:52 S Roderick wrote:
>> On Jan 21, 2010, at 10:24 , Peter Soetens wrote:
>>> One of the more drastic things to change in RTT 2.0 is that RTT::Events
>>> as they were will no longer exist. Their drawback were that events could
>>> get lost, (it had a fifo buffer of only size one) and especially, it
>>> could trash the event emitter if 'hostile' callbacks were attached
>>> synchronously.
>>>
>>> We (at least some of us) agreed to replace them by dataflow ports. You
>>> can specify a buffering policy, they are thread-safe and real-time,
>>> distributed and the callback is handling is simple and efficient and they
>>> can wake up task contexts.
>>
>> This concept sounds really odd, and it might be hard to educate new users
>> that ports communicate data between components, _and_ they also can be
>> used like events to trigger changes in a state machine (ie the state
>> machine is reacting to the arrival of data, but the actual data is
>> irrelevant - for transitions without associated data). Having said that, I
>> don't have a complete enough perspective on all the v2.0 changes yet, so I
>> don't have anything else to suggest.
>
> Remind that in most frameworks, a data sample sent/broadcasted *is* what they
> call an event, ie the CORBA event service, but also ROS attaches callbacks
> ('event handlers') to 'topics'. So we thought that it would be more natural to
> have one kind of event instead of a data flow event and an execution
> flow/callback event. The main purpose here is to find a unified way to react to
> events, local or remote and to be able to specify (at deployment or script
> loading time) how the buffering (if any) is done. Reacting to a local event
> should be the same as reacting to an event of another component.

Agreed.

>>> Proposed solution:
>>> 1) Create a 'LoopBackPort' next to InputPort and OutputPort. It is a data
>>> flow port that reads what you wrote into it. you can connect it to itself
>>> and as such set a buffering policy etc. The scripting state machine react
>>> to data on this port, like they react to data from InputPorts. I'm a bit
>>> uneasy about the 'LoopBackPort' name though.
>>
>> Sounds kind of ugly unfortunately. I guess the LoopBackPort is essentially
>> a local-only, In/Out port then (in concept, not implementation)?
>
> Yes. It would implement OutputPortInterface and InputPortInterface. My
> reasoning is indeed flawed. What if two state machines react to data on this
> port, both would need to receive the same set of events? A buffer+policy per
> subscriber would be needed to provide this.

We do sometimes have state machines in separate components react to the same event. We don't (I think) have any components with multiple state machines reacting to the same event, but I can easily see how someone might do that.

>> For events without data, would the port just carry an int (or
>> similar) whose value is simply ignored?
>
> We could use a special type (unused_type) which signifies a 'void'. You
> wouldn't need to specify an argument in that case in the script.

So in C++ that would be RTT::LoopBackPort<void> and the script would be "transition port_name() ...?

>>> The major difference between old and new a user will feel is that old
>>> events allowed multipe arguments (ie event_name(a,b,c)) while ports only
>>> allow one argument (ie port_name(a) ). This means you need to rewrite
>>> multiple arguments into a C struct and register it with the type system.
>>> This shouldn't be a big issue once typekits can be generated on the fly
>>> (we're working on this, remember), but for large applications that rely
>>> on this, it's a painful change.
>>
>> ARG!! Yuck! We use multiple arguments a lot with events (and I think some
>> of the existing OCL code does too, IIRC). We will have to have
>> auto-generated typekits to make this at all useable. And still it seems a
>> hell of change to have to generate a specialist structure on a per event
>> basis (for those events with multiple args). So that leads to the
>> question: what would it take to provide a data port that supports multiple
>> pieces of data? :-) For synchronization reasons, I can think of other uses
>> for this.
>
> There are 'theoretical' solutions for this. especially if you generate the
> typekits. For example, ROS allows to publish a composed topic by only
> specifying the components, such as in "rostopic pub Vector -- 1.0 2.0 3.0"
> The inverse could be thought of too, so having
>

> struct Vector {double x,y,z;};
> //in script:
> var double a,b,c;
> transition port_name(a, b, c) if ( a < b < c) then  select NEXT
> // is equivalent to:
> var Vector v;
> transition port_name(v) if (v.a < v.b < v.c) then select NEXT
> 

>
> This could be possible in scripting, I'm less sure of it in C++ code,
> overloading read() that is. We'd need boost::fusion for this, and I believe
> the Orocos users is not yet ready for fusion's compile errors when
> accidentally providing the wrong argument.

Hmmm ... boost::fusion, not unless we absolutely have to. :-(

I see your point above, and I think that we would definitely prefer the same syntax in scripts as in C++ (as close as reasonably possible).

>>> Thoughts ?
>>
>> This still needs work. A lot of the other changes proposed for v2 sound
>> good and have been thrashed through enough by now, to feel like
>> reasonable, desireable changes. I'm not convinced yet by this one though.
>
> The 'true' solution I'm looking for is to provide what you need from a design
> perspective. Meaning, the architecture of your application remains the same,
> but the way you express it in code can be different. If a change requires
> redesiging an application, I think upgrading to 2.0 will be unfeasible. If
> it's more a matter of syntax, I think it is doable.

+1. You are taking the right approach IMHO. We just need to keep working this one ...

We are getting close enough to a stable v2, that I'm almost ready to try compiling an existing system and see just what is required to upgrade. Almost ready ... :-)
S

Replacing events in RTT 2.0 & script state machines

On Thu, Jan 21, 2010 at 17:28, Stephen Roderick <kiwi [dot] net [..] ...> wrote:
> On Jan 21, 2010, at 11:18 , Peter Soetens wrote:
>
>> On Thursday 21 January 2010 16:39:52 S Roderick wrote:
>>> On Jan 21, 2010, at 10:24 , Peter Soetens wrote:
>>>> One of the more drastic things to change in RTT 2.0 is that RTT::Events
>>>> as they were will no longer exist. Their drawback were that events could
>>>> get lost, (it had a fifo buffer of only size one) and especially, it
>>>> could trash the event emitter if 'hostile' callbacks were attached
>>>> synchronously.
>>>>
>>>> We (at least some of us) agreed to replace them by dataflow ports. You
>>>> can specify a buffering policy, they are thread-safe and real-time,
>>>> distributed and the callback is handling is simple and efficient and they
>>>> can wake up task contexts.
>>>
>>> This concept sounds really odd, and it might be hard to educate new users
>>> that ports communicate data between components, _and_ they also can be
>>> used like events to trigger changes in a state machine (ie the state
>>> machine is reacting to the arrival of data, but the actual data is
>>> irrelevant - for transitions without associated data). Having said that, I
>>> don't have a complete enough perspective on all the v2.0 changes yet, so I
>>> don't have anything else to suggest.
>>
>> Remind that in most frameworks, a data sample sent/broadcasted *is* what they
>> call an event, ie the CORBA event service, but also ROS attaches callbacks
>> ('event handlers') to 'topics'. So we thought that it would be more natural to
>> have one kind of event instead of a data flow event and an execution
>> flow/callback event. The main purpose here is to find a unified way to react to
>> events, local or remote and to be able to specify (at deployment or script
>> loading time) how the buffering (if any) is done. Reacting to a local event
>> should be the same as reacting to an event of another component.
>
> Agreed.

I'd better take one step at a time. I want to tackle first how we'll
react to data arriving on ports. From a component's perspective, it
wishes to react to a new sample on its input port. The component
doesn't connect the port, it's deployment doing this and setting up a
connection policy.

So all that rests is a state machine that defines a transition on the
arrival of new data on the input port. As in my previous mail, this
can be expressed as in:

transition inport(arg) if (guard(arg)) then{ } select STATE

I think this case is clear and there is not much discussion about it:
if you want to emit an event, define an OutputPort, if you want to
react to it define an InputPort, and decide during deployment the
event delivery policy.

What is not clear is how to emit an event (using an OutputPort) and
how to react to this in the same component. Strictly speaking, you
need to define an InputPort and connect the output to this input
during the configuration phase. The script would then listen on the
input port, like it listens to any other input port.

Such explicit structure leaves no doubts, albeit it's more setup work
than with the old Event. As a simplification, you could allow that the
additional input port is automatically created by the state machine
parser, but we would need to install a system to allow to specify the
policy (in the script itself). For example:

// new syntax:
// define at init time a connection policy from outport to this state machine:
init connect outport ConnPolicy(Buffered,10); // buffer of 10.
//...
// react to emissions of outport at runtime:
transition outport(arg) if (guard(arg)) then {} select STATE

Note the difference that in the first snippet, we were reacting to an
input port, but here the name of an output port appears. If no
connection policy is specfied, the default one is used, a circular
buffer of size 1.

If we can agree on the first part, but still discuss the second part,
I'm already more than happy.

Peter

Replacing events in RTT 2.0 & script state machines

On Thursday 21 January 2010 17:28:50 Stephen Roderick wrote:
...
>
> >> For events without data, would the port just carry an int (or
> >> similar) whose value is simply ignored?
> >
> > We could use a special type (unused_type) which signifies a 'void'. You
> > wouldn't need to specify an argument in that case in the script.
>
> So in C++ that would be RTT::LoopBackPort<void> and the script would be
> "transition port_name() ...?

Yes. We'd need a specialisation for implementing read(void) and write(void)
but that's not an issue/visible for the user.

>
> >>> The major difference between old and new a user will feel is that old
> >>> events allowed multipe arguments (ie event_name(a,b,c)) while ports
> >>> only allow one argument (ie port_name(a) ). This means you need to
> >>> rewrite multiple arguments into a C struct and register it with the
> >>> type system. This shouldn't be a big issue once typekits can be
> >>> generated on the fly (we're working on this, remember), but for large
> >>> applications that rely on this, it's a painful change.
> >>
> >> ARG!! Yuck! We use multiple arguments a lot with events (and I think
> >> some of the existing OCL code does too, IIRC). We will have to have
> >> auto-generated typekits to make this at all useable. And still it seems
> >> a hell of change to have to generate a specialist structure on a per
> >> event basis (for those events with multiple args). So that leads to the
> >> question: what would it take to provide a data port that supports
> >> multiple pieces of data? :-) For synchronization reasons, I can think of
> >> other uses for this.
> >
> > There are 'theoretical' solutions for this. especially if you generate
> > the typekits. For example, ROS allows to publish a composed topic by only
> > specifying the components, such as in "rostopic pub Vector -- 1.0 2.0
> > 3.0" The inverse could be thought of too, so having
> >

> > struct Vector {double x,y,z;};
> > //in script:
> > var double a,b,c;
> > transition port_name(a, b, c) if ( a < b < c) then  select NEXT
> > // is equivalent to:
> > var Vector v;
> > transition port_name(v) if (v.a < v.b < v.c) then select NEXT
> > 

> >
> > This could be possible in scripting, I'm less sure of it in C++ code,
> > overloading read() that is. We'd need boost::fusion for this, and I
> > believe the Orocos users is not yet ready for fusion's compile errors
> > when accidentally providing the wrong argument.
>
> Hmmm ... boost::fusion, not unless we absolutely have to. :-(
>
> I see your point above, and I think that we would definitely prefer the
> same syntax in scripts as in C++ (as close as reasonably possible).
>
> >>> Thoughts ?
> >>
> >> This still needs work. A lot of the other changes proposed for v2 sound
> >> good and have been thrashed through enough by now, to feel like
> >> reasonable, desireable changes. I'm not convinced yet by this one
> >> though.
> >
> > The 'true' solution I'm looking for is to provide what you need from a
> > design perspective. Meaning, the architecture of your application remains
> > the same, but the way you express it in code can be different. If a
> > change requires redesiging an application, I think upgrading to 2.0 will
> > be unfeasible. If it's more a matter of syntax, I think it is doable.
>
> +1. You are taking the right approach IMHO. We just need to keep working
> this one ...
>
> We are getting close enough to a stable v2, that I'm almost ready to try
> compiling an existing system and see just what is required to upgrade.
> Almost ready ... :-) S

Stable ? Just to give you an idea of the code-shattering that's going on, see
this branch I'm working on:
http://github.com/psoetens/orocos-rtt/commits/rtt-2.0-services-collection
(don't fork it :-)

And I still have to start fixing up CORBA.

Peter

$ git diff --stat --summary -M "HEAD@{2 weeks ago}" HEAD
rtt/CMakeLists.txt | 3 +
rtt/ExecutionEngine.cpp | 361 ++++--------
rtt/ExecutionEngine.hpp | 225 +++++---
rtt/InputPort.hpp | 10 +-
rtt/Method.hpp | 20 +-
rtt/Operation.cpp | 9 +
rtt/Operation.hpp | 75 +++
rtt/OutputPort.hpp | 11 +-
rtt/SendHandle.hpp | 64 +--
rtt/TaskContext.cpp | 166 +-----
rtt/TaskContext.hpp | 136 +-----
rtt/base/DisposableInterface.hpp | 2 +
rtt/base/ExecutableInterface.hpp | 84 +++
rtt/base/InputPortInterface.cpp | 2 -
rtt/base/MethodBase.hpp | 11 +-
rtt/base/OperationBase.cpp | 37 ++
rtt/base/OperationBase.hpp | 61 ++
rtt/base/OutputPortInterface.cpp | 2 -
rtt/base/PortInterface.cpp | 14 +-
rtt/base/PortInterface.hpp | 2 +-
rtt/base/ProgramInterface.cpp | 14 +-
rtt/base/ProgramInterface.hpp | 24 +-
rtt/base/RunnableInterface.hpp | 4 +-
rtt/base/TaskCore.cpp | 39 +-
rtt/base/TaskCore.hpp | 29 +-
rtt/extras/FileDescriptorActivity.cpp | 4 +-
rtt/extras/IRQActivity.cpp | 3 +-
rtt/interface/AttributeRepository.hpp | 14 +
rtt/interface/DataFlowInterface.cpp | 29 +-
rtt/interface/DataFlowInterface.hpp | 10 +-
rtt/interface/ExecutionAccess.cpp | 239 --------
rtt/interface/ExecutionAccess.hpp | 248 --------
rtt/interface/MarshallingAccess.cpp | 43 +-
rtt/interface/MethodRepository.cpp | 74 ---
rtt/interface/MethodRepository.hpp | 453 ---------------
rtt/interface/OperationInterface.cpp | 123 ----
rtt/interface/OperationInterface.hpp | 195 -------
rtt/interface/ServiceProvider.cpp | 91 +++
rtt/interface/ServiceProvider.hpp | 295 ++++++++++
rtt/interface/rtt-interface-fwd.hpp | 1 +
rtt/internal/BindStorage.hpp | 264 +++++----
rtt/internal/Collect.hpp | 51 ++-
rtt/internal/CollectBase.hpp | 1 +
rtt/internal/CollectSignature.hpp | 84 ++-
rtt/internal/CreateSequence.hpp | 218 +++++++
rtt/internal/DataSourceArgsMethod.hpp | 63 +--
rtt/internal/FusedFunctorDataSource.hpp | 199 +++++++
rtt/internal/Invoker.hpp | 54 ++-
rtt/internal/InvokerBase.hpp | 15 +-
rtt/internal/InvokerSignature.hpp | 67 ++-
rtt/internal/LocalMethod.hpp | 219 +++++---
rtt/internal/MessageProcessor.cpp | 60 --
rtt/internal/MessageProcessor.hpp | 70 ---
rtt/internal/MethodBinder.hpp | 659 +---------------------
rtt/internal/MethodC.cpp | 16 +-
rtt/internal/OperationFactory.hpp | 477 ++++++----------
rtt/internal/TaskObject.cpp | 70 ---
rtt/internal/TaskObject.hpp | 94 ---
rtt/internal/quickbind.hpp | 342 -----------
rtt/internal/rtt-internal-fwd.hpp | 19 +-
rtt/os/Condition.hpp | 176 ++++++
rtt/os/MainThread.cpp | 2 +
rtt/os/MainThread.hpp | 2 +
rtt/os/Mutex.hpp | 3 +
rtt/os/Thread.hpp | 5 +
rtt/os/ThreadInterface.cpp | 6 +
rtt/os/fosi_interface.h | 8 +
rtt/os/fosi_internal_interface.hpp | 9 +
rtt/os/gnulinux/fosi.h | 27 +
rtt/os/gnulinux/fosi_internal.cpp | 7 +
rtt/os/lxrt/fosi.c | 39 ++
rtt/os/lxrt/fosi.h | 52 ++-
rtt/os/lxrt/fosi_internal.cpp | 9 +
rtt/os/macosx/fosi_internal.cpp | 8 +
rtt/os/rtt-os-fwd.hpp | 1 +
rtt/os/targets/target.in | 3 +
rtt/os/win32/fosi_internal.cpp | 7 +
rtt/os/xenomai/fosi.h | 40 ++
rtt/os/xenomai/fosi_internal.cpp | 10 +
rtt/rtt-fwd.hpp | 2 +
rtt/scripting/ArgumentsParser.cpp | 2 +-
rtt/scripting/ArgumentsParser.hpp | 6 +-
rtt/scripting/CMakeLists.txt | 4 +-
rtt/scripting/CommandExecFunction.cpp | 6 +-
rtt/scripting/CommandExecFunction.hpp | 10 +-
rtt/scripting/CommandParser.cpp | 8 +-
rtt/scripting/DumpObject.cpp | 28 +-
rtt/scripting/DumpObject.hpp | 2 +-
rtt/scripting/ExpressionParser.cpp | 10 +-
rtt/scripting/FunctionFactory.cpp | 4 +-
rtt/scripting/FunctionFactory.hpp | 12 +-
rtt/scripting/FunctionGraph.cpp | 26 +-
rtt/scripting/FunctionGraph.hpp | 9 +-
rtt/scripting/ParsedStateMachine.cpp | 31 +-
rtt/scripting/ParsedStateMachine.hpp | 6 +-
rtt/scripting/Parser.hpp | 2 +-
rtt/scripting/ParserExecutionAccess.cpp | 129 ----
rtt/scripting/ParserExecutionAccess.hpp | 64 --
rtt/scripting/ParserScriptingAccess.cpp | 380 ------------
rtt/scripting/ParserScriptingAccess.hpp | 93 ---
rtt/scripting/PeerParser.cpp | 12 +-
rtt/scripting/PeerParser.hpp | 6 +-
rtt/scripting/ProgramGraphParser.cpp | 22 +-
rtt/scripting/ProgramGraphParser.hpp | 6 +-
rtt/scripting/ProgramLoader.cpp | 98 ----
rtt/scripting/ProgramLoader.hpp | 130 -----
rtt/scripting/ProgramProcessor.cpp | 269 ---------
rtt/scripting/ProgramProcessor.hpp | 171 ------
rtt/scripting/ProgramTask.cpp | 39 +-
rtt/scripting/ProgramTask.hpp | 9 +-
rtt/scripting/ScriptingAccess.cpp | 899
+++++++++++++++++++++++++----
rtt/scripting/ScriptingAccess.hpp | 387 +++++++++++--
rtt/scripting/StateDescription.cpp | 4 +-
rtt/scripting/StateDescription.hpp | 13 +-
rtt/scripting/StateGraphParser.cpp | 41 +-
rtt/scripting/StateGraphParser.hpp | 4 +-
rtt/scripting/StateMachine.cpp | 22 +-
rtt/scripting/StateMachine.hpp | 17 +-
rtt/scripting/StateMachineProcessor.cpp | 325 -----------
rtt/scripting/StateMachineProcessor.hpp | 153 -----
rtt/scripting/StateMachineTask.cpp | 84 +--
rtt/scripting/StateMachineTask.hpp | 14 +-
rtt/scripting/StatementProcessor.cpp | 4 +-
rtt/scripting/ValueChangeParser.cpp | 32 +-
rtt/scripting/ValueChangeParser.hpp | 8 +-
rtt/scripting/ValueParser.cpp | 10 +-
rtt/transports/corba/ControlTaskI.cpp | 3 +-
rtt/transports/corba/ScriptingAccessI.cpp | 1 -
rtt/types/RealTimeToolkit.cpp | 3 +-
rtt/types/TemplateTypeInfo.hpp | 3 +-
src/LocalMessage.hpp | 158 -----
src/Message.cpp | 8 -
src/Message.hpp | 225 -------
src/MessageBase.hpp | 26 -
src/MessageStorage.hpp | 257 --------
tests/method_test.cpp | 209 +++++---
137 files changed, 4135 insertions(+), 7061 deletions(-)
create mode 100644 rtt/Operation.cpp
create mode 100644 rtt/Operation.hpp
create mode 100644 rtt/base/ExecutableInterface.hpp
create mode 100644 rtt/base/OperationBase.cpp
create mode 100644 rtt/base/OperationBase.hpp
delete mode 100644 rtt/interface/ExecutionAccess.cpp
delete mode 100644 rtt/interface/ExecutionAccess.hpp
delete mode 100644 rtt/interface/MethodRepository.cpp
delete mode 100644 rtt/interface/MethodRepository.hpp
delete mode 100644 rtt/interface/OperationInterface.cpp
delete mode 100644 rtt/interface/OperationInterface.hpp
create mode 100644 rtt/interface/ServiceProvider.cpp
create mode 100644 rtt/interface/ServiceProvider.hpp
create mode 100644 rtt/internal/CreateSequence.hpp
create mode 100644 rtt/internal/FusedFunctorDataSource.hpp
delete mode 100644 rtt/internal/MessageProcessor.cpp
delete mode 100644 rtt/internal/MessageProcessor.hpp
delete mode 100644 rtt/internal/TaskObject.cpp
delete mode 100644 rtt/internal/TaskObject.hpp
delete mode 100644 rtt/internal/quickbind.hpp
create mode 100644 rtt/os/Condition.hpp
delete mode 100644 rtt/scripting/ParserExecutionAccess.cpp
delete mode 100644 rtt/scripting/ParserExecutionAccess.hpp
delete mode 100644 rtt/scripting/ParserScriptingAccess.cpp
delete mode 100644 rtt/scripting/ParserScriptingAccess.hpp
delete mode 100644 rtt/scripting/ProgramLoader.cpp
delete mode 100644 rtt/scripting/ProgramLoader.hpp
delete mode 100644 rtt/scripting/ProgramProcessor.cpp
delete mode 100644 rtt/scripting/ProgramProcessor.hpp
delete mode 100644 rtt/scripting/StateMachineProcessor.cpp
delete mode 100644 rtt/scripting/StateMachineProcessor.hpp
delete mode 100644 src/LocalMessage.hpp
delete mode 100644 src/Message.cpp
delete mode 100644 src/Message.hpp
delete mode 100644 src/MessageBase.hpp
delete mode 100644 src/MessageStorage.hpp