What are the time tracking capabilities in Ceu?

28 views
Skip to first unread message

daniel....@gmail.com

unread,
Feb 6, 2019, 7:24:51 PM2/6/19
to The Programming Language Céu
Hi. I discovered Ceu today and am very interested, so sorry if this is a deluge of questions. It hits many of the pain points I wanted to address in my own someday-I'll-make-it-I-swear programming language.

One thing that's not clear to me from reading the resources I've found and essential in many of the applications I have encountered in my career is measuring or processing the time an event occurred. Sometimes this is the same as the time the event was "measured" and sometimes the notice of the event could be delayed. For example, GPS receivers can take a significant amount of time to calculate the position solution, so when the data is received by the control program, the position information indicates where the receiver was X msec ago.

Concretely, how would the following scenarios be handled in Ceu?

- Pass a timestamp along with data in an event with ceu_sys_go? (My guess is tuple or struct to hold the stamp and data.)
- Get the wall clock time to add time stamp data to events.
- Application: Event Data(double 5.0) arrives at time t_1 of 1000 msec. Event Data(double 10.0) arrives at time t_2 of 2000 msec. Event Trigger arrives at t_3 of 3000 msec. How to correctly extrapolate the data of the Data event to a value of 15.0 at time t_3?

At least in control and sensor fusion applications, knowing when something happened is just as important as knowing what value it had at the time. If there isn't yet any language support for time keeping already, I think it would be a great feature to add because time has special significance compared to other kinds of data.

Is there any kind of Ceu standard library of organisms? If not (and assuming it's possible), would the organisms need to be parameterized on event types within their "with" clause initialization?

Also just FYI the Ceu Try webpage is broken for me on Firefox.

Thanks and very impressive work on Ceu.

Dan

Johnicholas Hines

unread,
Feb 7, 2019, 10:26:50 AM2/7/19
to ceu-...@googlegroups.com
I am a Ceu user, not a Ceu author; this is absolutely not an authoritative answer.



The Ceu language and runtime has support for events, including a particular kind of event which is "some specific amount of time has passed".

The difference between ordinary events like "a fd has become readable" or "a button was clicked" or "a message has arrived in a mailbox", and "some time has passed" events is that the latter has a magnitude - how much time has passed.
In the case of "await 1000ms;" a sequence of 2 events each conveying "500ms has passed" will cause control flow to go through that await and on to whatever comes next - or one event conveying "15s has passed" will also do cause control flow to go on.

Some lightweight uses of Ceu may not use any of the time-has-passed events at all, and instead use something like a "frame" event, meaning "approximately 1/50th of a second, but we're not picky, it's just time to draw a new frame to the screen".

Other uses of Ceu may have a fixed-size "tick" event, which conveys both control flow and also a specific fixed amount of time into the Ceu environment - maybe 1s, maybe 10ms.
Of course, that means that when you write "await 10s", then in the worst case you might actually wait closer to 11s, if the tick flowing in from the environment is every 1s.

Event-driven uses of Ceu might block on specific set of event sources (maybe fds) and react to an event by reading whatever the current time is, and feeding the delta time since the last event into Ceu, and only then sending the actual event into Ceu.
That would mean that timers set like "await 1s" might not go off until immediately before the next external event - which might be fine if the behavior triggered by the timer is not externally observable except via Ceu's reaction to external events - and that might be less CPU cost than sending a tick into Ceu every 10ms.

Generally speaking, I think spending at least a couple weeks learning how to accomplish things with the language, how Ceu programs are structured, and also how to write a C environment surrounding a Ceu program, will help you see how Ceu understands time. It's easy to imagine, from reading about it, that Ceu is magic - it is amazing, but it's not magic.
The O(1) reactivity guarantee is subject to limitations, including for example, the constant hidden by the big-O may be different for different Ceu programs, and there are trapdoors within Ceu that will allow you to break the O(1) reactivity guarantee, and O(1) is not necessarily the same as fast and so on.
It still leans on the usual assumption of event-driven programming - your CPU will have to be faster than the amount of processing necessary to deal with each event as it comes in.
If you expect to handle bursts of events, one per second, when each one requires two seconds of processing to deal with correctly, you will be disappointed.
Either the amount of processing per event needs to be lower, or the max rate of arrival of events needs to be lower, or it needs to be okay to push off some of the processing to a later date, merely recording the fact of the event, for example, and having the cpu-intensive processing of the event occur at a best-effort rate at the other side of a mailbox or queue.

To specifically answer your questions.
1. Yes, you can absolutely pass timestamps along with events; those timestamps will not interact with the "await 1000ms" syntax at all.
2. Getting the wall clock time is probably something like binding the C system call man(2) getttimeofday, though it depends on the specific environment that your Ceu program is embedded in.
3. If you wanted to wait for two data events and then a trigger event, and do linear extrapolation based on the first two, then you might do something like this.
(This is pseudocode, I have not run it, and you can probably write something much better for your particular environment if and when you understand Ceu better.)

var double d1 = await DATA;
var timestamp t1 = _gettimeofday();
var double d2 = await DATA;
var timestamp d2 = _gettimeofday();
await TRIGGER;
var double extrapolated = ((d2 - d1) / (t2 - t1)) (_gettimeofday() - d1) + t1;
_printf("%d\n", extrapolated);

4. There is not a standard library of organisms; due to the wide variety of environments that Ceu can be applied to, and the relatively few Ceu applications, there are relatively few opportunities to generalize "this structure was useful in this environment, and is similar to that structure which was useful in that environment" to form a library.
Probably the best Ceu library is Ceu-Media: https://github.com/rodrimc/ceu-media
Each Ceu environment (such as the SDL environment) probably has a few Ceu organisms that help with working with that environment.



I hope this helps,

Johnicholas





--

---
You received this message because you are subscribed to the Google Groups "The Programming Language Céu" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ceu-lang+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Daniel Austin

unread,
Feb 7, 2019, 1:32:16 PM2/7/19
to ceu-...@googlegroups.com
Hi, Johnicholus.  Thanks for your reply.  Your preamble to the answers of my questions did a good job of confirming my understanding of how Ceu works.  I've read the manual and several of the academic papers linked on the website, including Matthias Terber's PhD thesis, which did a good job explaining the full implementation of a Ceu environment and Ceu code for an application.  I think I understand Ceu just fine without any notions of magic, and I do admire its philosophy and design.  I've been pondering what a viable alternative to Simulink and "model-based development" would be for my usual application domain, and I think Ceu has a lot of potential in that regard.

Your pseudo code was what I was honestly worried of in terms of time-keeping in Ceu.  Ceu has it's own internal time-keeping clock or other mechanism (I presume) in order to dispatch time-based events to the right trails as you described, but there's no way to tap-in to that clock within Ceu code itself.  The time returned by gettimeofday is not necessarily the same as the internal Ceu clock, which likely starts at 0 on bootup I'm guessing.  In "simulation" mode, where normal- and time-based events are emitted in sequence in async block to simulate a real environment in faster than real time, the internal Ceu clock would necessarily diverge from the value returned from getttimeofday. 

The only workaround I see is getting the value of the Ceu clock within the Ceu environment via a C function and then passing it in to Ceu as part of the event payload, although I don't know whether a C function to do so currently exists (I know there's already a way to get the remaining time before the next Ceu timer expires).  It would be burdensome to add timestamp data to so many possible types in this fashion, though.

I'm not sure exactly what a change would look like, but I have some ideas.  Even something as simple as this below would be valuable.

var u64 ts = (now)ms; // get the current Ceu clock time rounded to integer milliseconds
var r64 ts = (now)s; // get the current Ceu clock time in double precision number of seconds

This makes some assumptions about Ceu's internal clock mechanism like it is monotonically non-decreasing, which I'll investigate or get feedback from the mailing list as to whether that is true.  Then your pseudocode example would merely replace __gettimeofday with Ceu calls to now and work splendidly without any of the complications I mentioned earlier.

Thanks again for the detailed reply,
Dan

You received this message because you are subscribed to a topic in the Google Groups "The Programming Language Céu" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/ceu-lang/7tOL-wQSpxI/unsubscribe.
To unsubscribe from this group and all its topics, send an email to ceu-lang+u...@googlegroups.com.

Francisco Sant'anna

unread,
Feb 7, 2019, 5:40:47 PM2/7/19
to ceu-...@googlegroups.com
Hi,

On Thu, Feb 7, 2019 at 4:32 PM Daniel Austin <daniel....@gmail.com> wrote:
I'm not sure exactly what a change would look like, but I have some ideas.  Even something as simple as this below would be valuable.

var u64 ts = (now)ms; // get the current Ceu clock time rounded to integer milliseconds
var r64 ts = (now)s; // get the current Ceu clock time in double precision number of seconds

This is definitely in the TODO list, but I don't really remember why it was not implemented (only the keyword `now` in us, I didn't think about the units).

You can more or less simulate it with a `spawn` and a global `now` at the top of the file:

var u32 now = 0;
spawn do
    loop do
        var int dt = await 10ms;
        now = now + 10 + dt;
    end
end

You still need to choose a sensible (long enough) polling period.

Even with primitive support, there's still a caveat because these two trails can possibly yield the same value:

par do
    await 1s;
    _print(now);
with
    await 1s;
    await SOME_INPUT;
    _print(now);
end

In theory, the second trail should output (now+SOME_INPUT), but WCLOCK and SOME_INPUT have different "types" and cannot be added, so we just assume SOME_INPUT to be 0.
I'm not sure if this is a practical problem though.

On Wed, Feb 6, 2019 at 7:24 PM <daniel....@gmail.com> wrote:
Hi.  I discovered Ceu today and am very interested, so sorry if this is a deluge of questions.

Just out of curiosity, how did you hear about it?

Also just FYI the Ceu Try webpage is broken for me on Firefox.

Which OS/version are you using?

Thanks,
Francisco

Daniel Austin

unread,
Feb 8, 2019, 12:18:56 AM2/8/19
to ceu-...@googlegroups.com
In theory, the second trail should output (now+SOME_INPUT), but WCLOCK and SOME_INPUT have different "types" and cannot be added, so we just assume SOME_INPUT to be 0

Hi, Francisco.  I don't understand this part.  Why would the output be now+SOME_INPUT?  Where is the addition operation coming from?

only the keyword `now` in us, I didn't think about the units

Regarding units, perhaps another approach is to define a new standard type called e.g., "wclock".  Example: var wclock ts = now;  Then have no arithmetic operators defined on the type, but allow the expressions like (ts)ms to convert to an integer (real?) number of milliseconds at the usage site.  This would allow the payload of an event to be the wclock type, and trails awaiting on the event would not need to know whether the event payload is in usec or msec or whatever, as would be required with an integer payload.  E.g.

var wclock ts = await ts_payload
var r64 value = 2*(ts)ms

There could be some confusion here because the timestamp value follows similar usage to the timer syntax, like await 1s, and in that case the numeric value is a delta on the wall clock time and not an absolute value.  Following that line of thought, in my use cases, rarely do I care about the absolute wall clock time and instead care about the timing of events in relation to each other i.e., a wclock delta.  The math is a lot of (stop_time - start_time) calculation.  Brainstorming here: maybe some kind of stopwatch functionality.  You could declare a variable of type stopwatch and "start" it, then whenever it is read, the elapsed wclock time since starting it is returned.  Other operations on the stopwatch could be pause/resume and reset.  Having the math and updates be handled by the runtime in this way would be a nice convenience and would declutter the variable noise of

await start_event;
var int start_time = now;
await stop_event;
var int stop_time = now;
//Time between events
var int delta = stop_time - start_time;

or

await start_event;
var int start_time = now;
every stop_event do
   _print("%f\n", now - start_time)
end
 

Which OS/version are you using?

I'm using Win 7 firefox 65.0.  When I click the Run button, I get a compilation message, then the Output and Debug headers show up but have no content.  In the developer pane, I don't see any new errors after clicking Run, but I do see this one when I first load the page: "Source map error: request failed with status 404 Resource URL: http://ceu-lang.org/assets/js/bootstrap.min.js Source Map URL: bootstrap.min.js.map"

Just out of curiosity, how did you hear about it?

I was researching Esterel, Lustre, Signal, etc. in an effort to find new ways to solve the annoyances I had when writing control loops or FSMs in vanilla C or C++ or Simulink.  I came across the reddit post below in which a comment mentions Esterel and Ceu together.  That's where I first heard about Ceu.  https://www.reddit.com/r/arduino/comments/2s2jx1/embrio_version_10_now_available_crosspost_from/



Francisco Sant'anna

unread,
Feb 8, 2019, 7:18:37 AM2/8/19
to ceu-...@googlegroups.com
On Fri, Feb 8, 2019 at 3:18 AM Daniel Austin <daniel....@gmail.com> wrote:
In theory, the second trail should output (now+SOME_INPUT), but WCLOCK and SOME_INPUT have different "types" and cannot be added, so we just assume SOME_INPUT to be 0

Hi, Francisco.  I don't understand this part.  Why would the output be now+SOME_INPUT?  Where is the addition operation coming from?

If you put "<x> ; await 1s ; await 2s ; <y>", <y> is guaranteed to occur 3s after <x> (1+2).
What about "<x> ; await 1s ; await 2s ; await I ; <z>"?
Could <y> and <z> ever be simultaneous considering a notion of `now`?


Daniel Austin

unread,
Feb 8, 2019, 12:31:57 PM2/8/19
to ceu-...@googlegroups.com
Thanks for the rephrase, Francisco, but I'm still not getting it.  Nothing about the situation seems subtle or like a caveat.  Would you check my understanding?

Could <y> and <z> ever be simultaneous considering a notion of `now`?

Let's assume your two lines are in different trails.  Yes, they could be "simultaneous" in the sense that the wclock time in <y> and <z> could be the same.  After 3s elapses, <y> runs in trail 1 at wclock = time1, and trail 2 suspends awaiting I.  At this point, the wclock as been paused waiting for a new time update event or event input from the environment. 

The non-simultaneous case:  If a non-zero time update occurs before event I, then when <z> executes, the wclock = time2 > time1, so <z> is said to occur "after" <y>. 

Simultaneous case: If there is no time update prior to event I, then when <z> executes, the wclock has not been advanced since <y>, so wclock = t2 = t1, so <z> is said to occur at the same time (literally same wclock time) as <y>, even though <y> occurred in a preceding reaction than <z>.  If I understand correctly, this point seems intuitive.



Francisco Sant'anna

unread,
Feb 8, 2019, 7:51:57 PM2/8/19
to ceu-...@googlegroups.com
On Fri, Feb 8, 2019 at 3:31 PM Daniel Austin <daniel....@gmail.com> wrote:

Simultaneous case: If there is no time update prior to event I, then when <z> executes, the wclock has not been advanced since <y>, so wclock = t2 = t1, so <z> is said to occur at the same time (literally same wclock time) as <y>, even though <y> occurred in a preceding reaction than <z>.  If I understand correctly, this point seems intuitive.

OK, good.
For me it's not that intuitive since two subsequent events cannot happen at the same time by definition (they are subsequent).



Reply all
Reply to author
Forward
0 new messages