Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 1 addition & 29 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -57,34 +57,6 @@
ctest --output-on-failure
'';
};
Simo = pkgs.clangStdenv.mkDerivation {
pname = "Simo";
version = "0.0.1";
src = ./.;

nativeBuildInputs = with pkgs; [
# Real build dependencies
cmake
ninja
doxygen
];

buildInputs = with pkgs; [
boost
glaze
];

cmakeFlags = [
"-DPORTABLE_BUILD=ON"
"-DENABLE_RELEASE_LTO=OFF"
"-DCMAKE_CXX_SCAN_FOR_MODULES=OFF"
];

doCheck = true;
checkPhase = ''
ctest --output-on-failure
'';
};

derivationAttributes = {
default = pkgs.clangStdenv.mkDerivation SimoBaseAttributes;
Expand Down Expand Up @@ -114,7 +86,7 @@
llvmPackages.llvm
];
shellHook = ''
export SHELL="${pkgs.bashInteractive}/bin/bash"
export SHELL="${pkgs.bashInteractive}/bin/bash"
'';
};
}
Expand Down
37 changes: 36 additions & 1 deletion include/Simo/module/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,43 @@ class SIMO_PUBLIC Module {
/// Record a statistic in a StatMapper to dump statistics
void record_statistics(Statistics::StatMapper& mapper);

/// Get a single port
[[nodiscard]] Port* get_port(std::string_view);

/// Get a single port
///
/// Return nullptr if the type does not match
template <typename T>
[[nodiscard]]
T get_port(std::string_view name)
requires std::is_pointer_v<T>
{
Port* port = get_port(name);
if (port == nullptr) {
return nullptr;
}
return boost::typeindex::runtime_cast<T>(port);
}

/// Get a single port
///
/// Return nullptr if the type does not match
template <typename T>
requires(!std::is_pointer_v<T>)
[[nodiscard]]
T* get_port(std::string_view name) {
return get_port<T*>(name);
}

struct PortWithFullName {
std::string full_name;
Port* port;
};

/// Return all the unconnected ports that a module exposes
std::vector<PortWithFullName> get_unconnected_ports(
bool include_nested_components) const;

Time current_time() const;

template <typename Stat>
Expand Down Expand Up @@ -200,4 +235,4 @@ class SIMO_PUBLIC Module {
};
} // namespace Simo

#endif // SIMO_MODULE_HH
#endif // SIMO_MODULE_HH
37 changes: 37 additions & 0 deletions include/Simo/port/Port.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,15 @@ class SIMO_PUBLIC Port {
return boost::typeindex::type_id<Self>();
}

TypeId get_runtime_type() const {
return boost::typeindex::type_id_runtime(*this);
}

[[nodiscard]]
virtual bool connect(Port* other) = 0;

virtual bool connected() const = 0;

[[nodiscard]] std::string_view name() const { return name_; }
void name(const std::string_view name) { name_ = name; }

Expand Down Expand Up @@ -81,6 +87,11 @@ class SIMO_PUBLIC OutPort : public Port {
[[nodiscard]]
bool connect(Port* other) override;

[[nodiscard]]
bool connected() const override {
return connecting_port != nullptr;
}

SEND_OUTCOME send(Payload&& payload) {
storage = std::move(payload);
switch (state_) {
Expand Down Expand Up @@ -114,6 +125,7 @@ class SIMO_PUBLIC OutPort : public Port {
protected:
Payload storage;
PORT_STATE state_ = PORT_STATE::EMPTY;
InPort<Payload>* connecting_port = nullptr;
};

/// Templated port that can received payloads from an OutPort of the same type
Expand All @@ -128,6 +140,11 @@ class SIMO_PUBLIC InPort : public Port {
[[nodiscard]]
bool connect(Port* other) override;

[[nodiscard]]
bool connected() const override {
return connecting_port != nullptr;
}

Payload receive() {
SIMO_ASSERT(connecting_port != nullptr);
SIMO_ASSERT(connecting_port->state() !=
Expand Down Expand Up @@ -171,6 +188,11 @@ class SIMO_PUBLIC CallbackInPort : public Port {
[[nodiscard]]
bool connect(Port* other) override;

[[nodiscard]]
bool connected() const override {
return connecting_port != nullptr;
}

void callback(Callback callback) { callback_ = std::move(callback); }

[[nodiscard]]
Expand Down Expand Up @@ -198,6 +220,7 @@ class SIMO_PUBLIC CallbackInPort : public Port {
}

Callback callback_;
CallbackOutPort<Payload, ReturnType>* connecting_port = nullptr;
};

enum struct SIMO_PUBLIC VERIFY_CONTRACT_ERROR : std::uint8_t {
Expand Down Expand Up @@ -285,6 +308,11 @@ class SIMO_PUBLIC CallbackOutPort : public Port {
[[nodiscard]]
bool connect(Port* other) override;

[[nodiscard]]
bool connected() const override {
return connecting_port != nullptr;
}

template <typename Arg>
requires std::constructible_from<Payload, Arg&&> &&
(!std::is_void_v<ReturnType>)
Expand Down Expand Up @@ -321,6 +349,7 @@ bool OutPort<Payload>::connect(Port* other) {
if (other_casted == nullptr) {
return false;
}
connecting_port = other_casted;
other_casted->connecting_port = this;
return true;
}
Expand All @@ -335,6 +364,7 @@ bool InPort<Payload>::connect(Port* other) {
return false;
}
connecting_port = other_casted;
other_casted->connecting_port = this;
return true;
}

Expand All @@ -350,6 +380,7 @@ bool CallbackOutPort<Payload, ReturnType>::connect(Port* other) {
return false;
}
connecting_port = other_casted;
other_casted->connecting_port = this;
return true;
}

Expand All @@ -364,6 +395,7 @@ bool CallbackInPort<Payload, ReturnType>::connect(Port* other) {
if (other_casted == nullptr) {
return false;
}
connecting_port = other_casted;
other_casted->connecting_port = this;
return true;
}
Expand Down Expand Up @@ -409,6 +441,11 @@ class SIMO_PUBLIC BidirectionalPortTyped : public Port {
BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(Port)
bool connect(Port* other) override;

[[nodiscard]]
bool connected() const override {
return out_port.connected() && in_port.connected();
}

/// Push a payload on the out port
OutPort<OutPayload>::SEND_OUTCOME send_out(OutPayload&& payload) {
return out_port.send(std::move(payload));
Expand Down
25 changes: 25 additions & 0 deletions src/SimoSim/SimoSim.cc
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,29 @@ std::optional<ModulePortName> parse_module_port_name(
};
}

void print_system_ports(
const std::unordered_map<ModuleName, ModuleParameterPair>& module_map) {
bool ports_printed = false;
std::cout << "Ports exposed by the system:\n";
for (const auto& [module_name, module_param_pair] : module_map) {
const auto& module = module_param_pair.module;
const auto ports = module->get_unconnected_ports(true);
ports_printed = !ports.empty();
for (const auto& port : ports) {
std::cout << " " << port.full_name << " - "
<< port.port->get_runtime_type().pretty_name() << "\n";
}
}
if (!ports_printed) {
std::cout << " No ports\n";
}
}

int main(const int argc, char* argv[]) {
std::filesystem::path config_path;
std::vector<std::string> collection_search_paths;
int verbosity = 0;
bool print_ports = false;

CLI::App app{"Simulation with Simo"};

Expand All @@ -87,6 +106,8 @@ int main(const int argc, char* argv[]) {
->check(CLI::ExistingDirectory);
app.add_flag("-v,--verbose", verbosity,
"Increase verbosity level (e.g., -v, -vv, -vvv)");
app.add_flag("--print-ports", print_ports,
"Print available ports before port binding phase");

CLI11_PARSE(app, argc, argv);

Expand Down Expand Up @@ -200,6 +221,10 @@ int main(const int argc, char* argv[]) {
return INITIALIZATION_FAILED;
}

if (print_ports) {
print_system_ports(module_map);
}

for (const auto& [left_endpoint, right_endpoint] : cfg.connections) {
const auto left = parse_module_port_name(left_endpoint);
const auto right = parse_module_port_name(right_endpoint);
Expand Down
19 changes: 19 additions & 0 deletions src/module/Module.cc
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,25 @@ Port* Module::get_port(const std::string_view name) {
: nullptr;
}

std::vector<Module::PortWithFullName> Module::get_unconnected_ports(
bool include_nested_components) const {
std::vector<PortWithFullName> ret;
for (const auto& p : ports) {
if (!p.second->connected()) {
ret.emplace_back(name_of_child(p.first), p.second.get());
}
}
if (!include_nested_components) {
return ret;
}
for (const auto& child : children) {
const auto child_ret =
child->get_unconnected_ports(include_nested_components);
ret.insert(ret.end(), child_ret.begin(), child_ret.end());
}
return ret;
}

InitializationStatus Module::log_setup(const std::filesystem::path& out_file) {
logger = {};
return logger.initialize(out_file);
Expand Down
61 changes: 61 additions & 0 deletions tests/module/ModuleTest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,65 @@ BOOST_AUTO_TEST_CASE(ModuleChild) {
BOOST_CHECK_EQUAL(child_status.success(), true);
BOOST_CHECK_EQUAL(p_child.name(), "root/child");
}

BOOST_AUTO_TEST_CASE(ModuleGetsPortAsRequestedType) {
Simo::Module module;
auto& port =
module.create_port<Ports::CallbackOutPort<int, bool>>("callback");

auto* explicitly_typed_port =
module.get_port<Ports::CallbackOutPort<int, bool>*>("callback");
auto* inferred_pointer_port =
module.get_port<Ports::CallbackOutPort<int, bool>>("callback");

BOOST_CHECK_EQUAL(explicitly_typed_port, &port);
BOOST_CHECK_EQUAL(inferred_pointer_port, &port);
}

BOOST_AUTO_TEST_CASE(ModuleTypedGetPortReturnsNullForMissingOrWrongType) {
Simo::Module module;
module.create_port<Ports::CallbackOutPort<int, bool>>("callback");

auto* missing_port =
module.get_port<Ports::CallbackOutPort<int, bool>>("missing");
auto* wrong_type_port =
module.get_port<Ports::CallbackInPort<int, bool>>("callback");

BOOST_CHECK_EQUAL(missing_port, nullptr);
BOOST_CHECK_EQUAL(wrong_type_port, nullptr);
}

BOOST_AUTO_TEST_CASE(ModuleGetsUnconnectedPorts) {
Simo::Context context;
Simo::Module module;
Simo::Parameters parameters;
parameters.name("root");
BOOST_REQUIRE(module.initialize(context, parameters).success());

auto& connected_out =
module.create_port<Ports::OutPort<int>>("connected_out");
auto& connected_in = module.create_port<Ports::InPort<int>>("connected_in");
BOOST_REQUIRE(connected_out.connect(&connected_in));
auto& root_unconnected =
module.create_port<Ports::CallbackOutPort<int, bool>>("unconnected");

auto& child = module.create_child<Simo::Module>();
Simo::Parameters child_parameters;
child_parameters.name(module.name_of_child("child"));
BOOST_REQUIRE(child.initialize(context, child_parameters).success());
auto& child_unconnected =
child.create_port<Ports::CallbackInPort<int, bool>>("unconnected");

const auto root_ports = module.get_unconnected_ports(false);
BOOST_REQUIRE_EQUAL(root_ports.size(), 1);
BOOST_CHECK_EQUAL(root_ports.front().full_name, "root/unconnected");
BOOST_CHECK_EQUAL(root_ports.front().port, &root_unconnected);

const auto all_ports = module.get_unconnected_ports(true);
BOOST_REQUIRE_EQUAL(all_ports.size(), 2);
BOOST_CHECK_EQUAL(all_ports[0].full_name, "root/unconnected");
BOOST_CHECK_EQUAL(all_ports[0].port, &root_unconnected);
BOOST_CHECK_EQUAL(all_ports[1].full_name, "root/child/unconnected");
BOOST_CHECK_EQUAL(all_ports[1].port, &child_unconnected);
}
} // namespace Simo::Tests
Loading