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

[Core]: Detached message handling #462

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

GwnDaan
Copy link
Member

@GwnDaan GwnDaan commented Apr 7, 2024

Describe your changes

Introduced 3 classes that will remove the the dependency on the CANNetworkManager for all classes that want to receive / send messages. namely:

  • CANMessagingProvider: a simple interface that provides the "send can message" function, currently this will always be the CANNetworkManager, but it also allows us to exchange it easily, e.g. if we want some unit test without the network manager.
  • CANMessagingConsumer: a base class to extend from. It provides the send message function and allows for handling tx/rx messages. I.e. any class that wants to interact with can messages can extend from this one and have everything ready.
  • CANMessageHandler: connects the CANMessagingProvider with CANMessagingConsumer's. And it's "process tx/rx message" function can be called to automatically distribute a particular message across consumers.

I think this will be a better alternative than passing the network manager around everywhere when we are refactoring away from the singleton. And it might especially be useful for unit testing, where we can isolate a class we wish to test and thereby remove interference from others. Though I'd like to hear any thoughts on this, as it does surely add an extra layer off complexity to the stack.

I see 3 ways a consumer can be set-up;

  • We can pas the CANMessageHandler to the consumer class and let it register itself, i.e.:
void initialize(CANMessageHandler messageHandler)
{
	// This class must extend enabled_shared_from_this, unlikely and unintuitive...
	messageHandler.add_consumer(std::shared_from_this());

	// If we decide to allow raw-pointers, but note that also
	// introduces the risk of forgetting the removal on cleanup
	messageHandler.add_consumer(this);
}
  • We add an extra function to the CANNetworkManager that sets up the messaging for the passed in consumer:
void CANNetworkManager::initialize_messaging(std::shared_ptr<CANMessagingConsumer> consumer)
{
	messageHandler.add_consumer(consumer);
}
  • Since the above is really simple, it might instead make more sense to just let the end user/application call that function of the message handler, i.e.:
auto interfaceUnderTest = std::make_shared<ShortcutButtonInterface>(internalECU, true);
CANNetworkManager::CANNetwork.get_can_message_handler().add_consumer(interfaceUnderTest);

I think I prefer the last one. To start off, I switched over the ShortcutButtonInterface and HeartbeatInterface to this messaging system so we can see what it's like.

What's next

Once we find and agree on an approach, all the other classes that send/receive messages have to be adopted to this method.
Furthermore, I'd like some unit-tests that will be run first in line to make sure the system remains correctly in place and it doesn't cause other tests to fail.

@GwnDaan GwnDaan added the enhancement New feature or request label Apr 7, 2024
@GwnDaan GwnDaan requested a review from ad3154 April 7, 2024 20:16
@GwnDaan GwnDaan self-assigned this Apr 7, 2024
@GwnDaan GwnDaan changed the title Daan/detached message handling [Core]: Detached message handling Apr 7, 2024
Copy link
Member

@ad3154 ad3154 left a comment

Choose a reason for hiding this comment

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

Well, it certainly is more abstraction, as we'd now be abstracting the entire network manager from the higher level interfaces. This could be interesting for testing I suppose. It maybe has some weird implications in the app, where now you can't store some interfaces on the stack, you have to make shared_ptr's if you want to really use them, which IDK if is amazing, but I also understand... Trying to think of how someone could, in the future, maybe put a network manager on their local stack, plus all their various interfaces, seems like we'd need references or raw pointers, which I also don't love... so maybe all the shared_ptrs is fine. We've been going that route for some time now.

Adding a vtable to each of these interfaces is the same size as a reference or something to the network manager, so that's nice - for about the same class size you can abstract more stuff away.

I guess what I'm saying is I don't dislike it. I think it will get the job done. I think we do keep getting further from the ability to ever really make the stack static allocation friendly, which is maybe fine? I know some levels of functional safety forbid any dynamic memory - which would be pretty painful for us to deal with, so maybe it's not worth worrying about especially with nobody explicitly asking for it or sponsoring that kind of thing?

So I suppose I'm interested in your thoughts in return, but I think it's a fine approach.

isobus/src/can_network_manager.cpp Outdated Show resolved Hide resolved
@GwnDaan
Copy link
Member Author

GwnDaan commented Jun 4, 2024

I think we do keep getting further from the ability to ever really make the stack static allocation friendly, which is maybe fine? I know some levels of functional safety forbid any dynamic memory - which would be pretty painful for us to deal with, so maybe it's not worth worrying about especially with nobody explicitly asking for it or sponsoring that kind of thing?

Yeah I know... Even though it would have been super nice to also be able to support those kind of strict rule sets, I think static allocation is out of our scope at the moment. Especially since we have always worked with the dynamic allocation and expanded on that. The shared pointers already help a lot to manage the lifecycle of those dynamically allocated objects. Converting the whole stack to be static allocation friendly I imagine will complicate so many things, it would be almost a rewrite...

Edit: hmm, it might be doable with some clever bit of macro usage to replace all shared pointers with raw pointers, similar to what we do with the threaded stuff, but it will still definitely complicate things. I don't think this PR will make it any harder than it was before, instead it might even make it easier as we abstract things, it can be easier to (progressively) refactor things later on.

@GwnDaan GwnDaan force-pushed the daan/detached-message-handling branch from 3fc63a7 to e55177e Compare June 4, 2024 09:54
@ad3154
Copy link
Member

ad3154 commented Jun 5, 2024

Yeah... but I do think we should worry about our current stuff and releasing 1.0 before we think too hard about it the static allocation stuff I think (if ever) I was mostly just thinking out loud 😊

@GwnDaan GwnDaan force-pushed the daan/detached-message-handling branch from e636772 to bccc405 Compare June 25, 2024 20:05
Copy link

sonarcloud bot commented Jun 25, 2024

@GwnDaan GwnDaan force-pushed the daan/detached-message-handling branch from 9fbb934 to 13593af Compare July 1, 2024 07:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants