Skip to content

Commit

Permalink
[HOTFIX] Cleanup can and bus helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
EvanLF6768 committed Jun 18, 2024
1 parent ae5003b commit 61db19e
Show file tree
Hide file tree
Showing 9 changed files with 886 additions and 837 deletions.
463 changes: 0 additions & 463 deletions common/Inc/obc/bus.hpp

This file was deleted.

44 changes: 27 additions & 17 deletions common/Inc/obc/bus/can.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
#include <stm32h7xx_hal_fdcan.h>
#include <units/time.h>

#include "bus.hpp"
#include "bus/helpers.hpp"
#include "bus/types.hpp"
#include "ipc/mutex.hpp"
#include "scheduling/delay.hpp"
#include "scheduling/task.hpp"
Expand All @@ -46,29 +47,34 @@ class CanFd : public scheduling::StackTask<>, bus::ListenBusMixin<> {
public:
static constexpr size_t kMaxPayloadSize = 64;

using SendHandle = std::monostate;
// TODO(evan): Switch to error type with string repr
enum class SendError { kTimeout, kInvalidSize };
enum class SendDispatchError { kInvalidSize, kTimeout };
using SendCallbackError = utils::Never;

// handle is init by the autogenerated code
inline CanFd(FDCAN_HandleTypeDef* handle)
: StackTask("CAN-FD Driver"), m_handle {handle} {}

inline auto Send(
const BasicMessage& msg, const units::milliseconds<float> timeout
) -> std::expected<std::monostate, SendError> {
const BasicMessage& msg,
ipc::Callback<
void, const std::expected<BasicMessage, SendCallbackError>&>&& cb
) -> std::expected<std::monostate, SendDispatchError> {
std::lock_guard lock {m_send_lock};

// Wait for a free slot in the message queue
scheduling::Timeout timeout_block(timeout);
// TODO(evan): Don't busy wait here
scheduling::Timeout timeout_block(units::milliseconds<float>(100));
if (!timeout_block.Poll([&]() {
return HAL_FDCAN_GetTxFifoFreeLevel(m_handle);
}))
return std::unexpected {SendError::kTimeout};
return std::unexpected {SendDispatchError::kTimeout};

// Pad the payload so that it fits within a valid DLC size if required
auto [dlc, padding] {
UNWRAP_TAGGED(EncodeDlc(msg.data.size()), SendError::kInvalidSize)
};
auto [dlc, padding] {UNWRAP_TAGGED(
EncodeDlc(msg.data.size()), SendDispatchError::kInvalidSize
)};
// Don't waste time zeroing memory if this buffer is not used
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
std::array<std::byte, kMaxPayloadSize> padded_payload;
Expand Down Expand Up @@ -102,6 +108,7 @@ class CanFd : public scheduling::StackTask<>, bus::ListenBusMixin<> {
HAL_FDCAN_AddMessageToTxFifoQ(m_handle, &header, payload_ptr),
utils::IsHalOk
);
cb(msg);
return {};
}

Expand All @@ -122,14 +129,14 @@ class CanFd : public scheduling::StackTask<>, bus::ListenBusMixin<> {
),
utils::IsHalOk
);
FeedListeners(
{.address = header.Identifier,
.data = std::span<std::byte>(
payload.data(),
// Only well formed CAN frames should exist in the FIFO
utils::UnwrapOrPanic(DecodeDlc(header.DataLength))
)}
);
FeedListeners(BasicMessage {
.address = header.Identifier,
.data = std::span<std::byte>(
payload.data(),
// Only well formed CAN frames should exist in the FIFO
utils::UnwrapOrPanic(DecodeDlc(header.DataLength))
)
});
}
}

Expand Down Expand Up @@ -172,4 +179,7 @@ class CanFd : public scheduling::StackTask<>, bus::ListenBusMixin<> {
FDCAN_HandleTypeDef* m_handle;
ipc::Mutex m_send_lock {};
};

static_assert(SendBus<CanFd>);
static_assert(ListenBus<CanFd>);
} // namespace obc::bus
128 changes: 128 additions & 0 deletions common/Inc/obc/bus/helpers.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/* USER CODE BEGIN Header */
/*
* 401 Ballon OBC
* Copyright (C) 2024 Bluesat and contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
/* USER CODE END Header */

#pragma once

#include "obc/bus/types.hpp"

namespace obc::bus {
static_assert(Message<BasicMessage>);
static_assert(Buffer<std::span<std::byte>>);

template<typename T>
struct NullFilter {
auto Filter(T /*arg*/) -> bool { return true; }
};

template<typename T>
constexpr NullFilter<T> kNullFilter {};

/**
* @brief Convert a trivial struct into a readonly byte buffer.
*
* Can be used to facilitate trivial deserialization of C-style structs sent on
* a bus.
*
* @warning For portability, the struct should be tightly packed (containing
* explicit padding fields if required) with fixed sized integers.
*
* @tparam T type of the struct to translate.
*/
template<typename T>
requires std::is_trivially_copyable_v<T>
auto StructAsBuffer(const T& s) -> std::span<const std::byte, sizeof(T)> {
// This function is constrained to only operate on POD structs
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
return {reinterpret_cast<const std::byte*>(&s), sizeof(T)};
}

/**
* @brief Convert a trivial struct into a read-write byte buffer.
*
* Can be used to facilitate trivial deserialization of C-style structs sent on
* a bus.
*
* @warning For portability, the struct should be tightly packed (containing
* explicit padding fields if required) with fixed sized integers.
*
* @tparam T type of the struct to translate.
*/
template<typename T>
requires std::is_trivially_copyable_v<T>
auto StructAsBuffer(T& s) -> std::span<std::byte, sizeof(T)> {
// This function is constrained to only operate on POD structs
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
return {reinterpret_cast<std::byte*>(&s), sizeof(T)};
}

/**
* @brief A helper class for implementing listening functionality.
*
* `FeedListeners` should be called for each incoming message. Typically
* this should be invoked before feeding requesters and processors to
* avoid them invalidating the data buffer which the message is potentially
* stored in.
*
* @tparam M The type of message received.
*/
template<utils::MaybeError E = utils::Never, Message M = BasicMessage>
class ListenBusMixin {
using ListenCallback = ipc::Callback<void, const std::expected<M, E>&>;
using FilterCallback = ipc::Callback<bool, const std::expected<M, E>&>;

public:
using ListenHandle =
utils::Handle<std::tuple<ListenCallback, FilterCallback>>;
using ListenDispatchError = utils::Never;
using ListenCallbackError = E;

/**
* @brief Adds a listener to be notified upon receiving a message.
*
* @param cb The listener callback to add.
* @return An opaque handle that must be retained for the listener to remain
* active.
*/
auto Listen(
ListenCallback&& cb,
FilterCallback&& flt = OBC_CALLBACK_METHOD(
(kNullFilter<const std::expected<M, E>&>), Filter
)
) -> std::expected<ListenHandle, ListenDispatchError> {
return ListenHandle(m_listeners, {std::move(cb), std::move(flt)});
}

protected:
/**
* @brief Forwards a message to all listeners.
*
* Should be called upon receiving any incoming message.
*
* @param msg The message to be forwarded.
*/
auto FeedListeners(const std::expected<M, E>& msg) -> void {
for (const auto& [cb, flt] : m_listeners)
if (flt()) cb(msg);
}

private:
utils::HandleChainRoot<ListenCallback> m_listeners {};
};
} // namespace obc::bus
Loading

0 comments on commit 61db19e

Please sign in to comment.