Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exposing clock and adding timer features #124

Merged
merged 20 commits into from
Dec 5, 2023

Conversation

Chootin
Copy link
Contributor

@Chootin Chootin commented Oct 23, 2023

This PR exposes the clock to users of rcldotnet, removing the static clock reference and providing one on each node. This is important as later we will need a clock on each node for the purpose of implementing the use_sim_time parameter.

User facing duration objects have been substituded with TimeSpan in order to play nicely with other C# APIs.

rcldotnet_examples/RCLDotnetTalker.cs has been updated to demonstrate the use of the clock and timer features.

I also added RegisterNativeFunction to DllLoadUtils as I grew tired of making mistakes copy-pasting boilerplate code.

@hoffmann-stefan
Copy link
Member

Hi @Chootin,

did start the review today, but couldn't finish yet.
Trying to understand how this all fits together takes some time.
Will try to finish this wednesday, there is a public holiday where I should have move time :)

@Chootin
Copy link
Contributor Author

Chootin commented Oct 29, 2023

@hoffmann-stefan No worries mate. Take your time, I'm in no great hurry.

Copy link
Member

@hoffmann-stefan hoffmann-stefan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This took some time, haven't finished everything yet, but wanted to post to avoid loosing the review comments. Haven't looked at the c code and the PInvoke definitions for example.

Could you rebase the branch on top of origin/main and force-push? I tested this myself, there is only one small merge conflict. It makes the history somewhat nicer to review as there are quite some merge commits.

I mostly concentrated on the public API for now using advice from the Framework Design Guidelines book and trying to be similar to rclcpp and rclpy.
Getting public API right the first time avoids having to break others when changing it later on.

Overall the implementation looks good 👍 But I am especially picky about public API and thread/memory-safty, so there are quite some comments ;)

I know this causes some work to address the review comments, so I would like to offer my support to implement them if it gets to much.

rcldotnet/Clock.cs Outdated Show resolved Hide resolved
rcldotnet/Clock.cs Outdated Show resolved Hide resolved
rcldotnet/Clock.cs Outdated Show resolved Hide resolved
rcldotnet/Clock.cs Outdated Show resolved Hide resolved
rcldotnet/Clock.cs Outdated Show resolved Hide resolved
}
}

public sealed class Clock
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Framework Design Guidelines recommends not to use to generic names for types.
I'm wondering if we should use something like RclClock here.

Copy link
Contributor Author

@Chootin Chootin Nov 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's an interesting proposition, it does have the potential to eliminate confusion. I am finding I'm fully qualifying my usages of rcldotnet message types for this reason.

If we did make this change though, it would be a deviation from both rclcpp and rclpy. It would also be a deviation from other objects in rcldotnet like Client, Service, Subscription, etc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, sometimes we have to choose if we follow .NET practices or if we are consistent with rclcpp and rclpy. There is no right answer in most of the cases, so it is quite subjective most of the times.

I don't have a strong opinion here, this was only a suggestion.

}
}

public sealed class Timer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Framework Design Guidelines recommends not to use to generic names for types.
I'm wondering if we should use something like RclTimer here.

rcldotnet/Node.cs Show resolved Hide resolved
rcldotnet/Timer.cs Show resolved Hide resolved
rcldotnet/Clock.cs Outdated Show resolved Hide resolved
Copy link
Member

@hoffmann-stefan hoffmann-stefan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did try to solve the CreateJumpCallback/JumpHandler puzzle.
Please take a look at the open comments again.
After they are resolved I will take another look at everything to be sure, but I don't expect any mayor comments right now.

}
}

public sealed class Clock
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, sometimes we have to choose if we follow .NET practices or if we are consistent with rclcpp and rclpy. There is no right answer in most of the cases, so it is quite subjective most of the times.

I don't have a strong opinion here, this was only a suggestion.

rcldotnet/Node.cs Show resolved Hide resolved
rcldotnet/Timer.cs Show resolved Hide resolved
rcldotnet/Clock.cs Outdated Show resolved Hide resolved
rcldotnet/Clock.cs Outdated Show resolved Hide resolved
@Chootin
Copy link
Contributor Author

Chootin commented Nov 20, 2023

I downloaded your branch and tested your improved IDisposable implementation of the JumpHandler using some reflection to simulate the use_sim_time parameter and /clock topic handling. The solution works well and I was unable to get garbage collection to cause any problems. I also tested the disposing of the JumpHandler and didn't have any issues. I have added your commit to this PR. 👍

Copy link
Member

@hoffmann-stefan hoffmann-stefan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @Chootin for the patience and the nice work 🎉

Merging this now as all review comments where addressed, the two open ones about naming classes are only suggestions, but given that there is already work done as a follow up let's keep it how it is now.

Looking forward to use this functionality in our codebase so that we can remove the workaround we had for timers 👍

This was the public API that was added/changed by this PR:

namespace ROS2
{
    public enum ClockType
    {
        RosTime = 1,
        SystemTime = 2,
        SteadyTime = 3
    }
    
    public enum ClockChange
    {
        RosTimeNoChange = 1,
        RosTimeActivated = 2,
        RosTimeDeactivated = 3,
        SystemTimeNoChange = 4
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public readonly struct TimeJump
    {
        public ClockChange ClockChange => throw null;
        public TimeSpan Delta => throw null;
    }
    
    public delegate void PreJumpCallback();
    public delegate void PostJumpCallback(TimeJump timeJump);
    
    [StructLayout(LayoutKind.Sequential)]
    public readonly struct JumpThreshold
    {
        public JumpThreshold(bool onClockChange, TimeSpan minForward, TimeSpan minBackward) {}

        public bool OnClockChange => throw null;
        public TimeSpan MinForward => throw null;
        public TimeSpan MinBackward => throw null;
    }
    
    public class CallbackAlreadyRegisteredException : Exception
    {
        public CallbackAlreadyRegisteredException(string message) : base(message) {}
    }
    
    public sealed class Clock
    {
        public builtin_interfaces.msg.Time Now() => throw null;
	public IDisposable CreateJumpCallback(JumpThreshold threshold, PreJumpCallback preJumpCallback, PostJumpCallback postJumpCallback) => throw null;
    }
    
    public partial class Node
    {
        public Clock Clock => throw null;
	public IList<Timer> Timers => throw null;
	
	public Timer CreateTimer(TimeSpan period, Action<TimeSpan> callback) => throw null;
    }
    
    public partial static class RCLdotnet
    {
	public static Clock CreateClock(ClockType type = ClockType.RosTime) => throw null;
    }
    
    public sealed class Timer
    {
    }
}

@hoffmann-stefan hoffmann-stefan merged commit ac35e40 into ros2-dotnet:main Dec 5, 2023
5 checks passed
@Chootin
Copy link
Contributor Author

Chootin commented Dec 6, 2023

Cheers for your feedback @hoffmann-stefan - it has certainly made my work better!

I'll get on to the next PR soon 👍.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants