Skip to content
Closed
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
32 changes: 32 additions & 0 deletions core/include/librmcs/data/datas.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ enum class DataId : uint8_t {
kUart3 = 14,

kImu = 15,

kI2c0 = 16,
};

struct CanDataView {
Expand Down Expand Up @@ -79,6 +81,28 @@ struct GyroscopeDataView {
int16_t z;
};

struct I2cDataView {
uint8_t slave_address;
std::span<const std::byte> payload;
bool has_register = false;
uint8_t reg_address = 0;
};

struct I2cReadConfigView {
uint8_t slave_address;
uint16_t read_length = 0;
bool has_register = false;
uint8_t reg_address = 0;
};

struct I2cErrorView {
uint8_t slave_address;
uint16_t data_length = 0;
bool has_register = false;
uint8_t reg_address = 0;
bool is_read = false;
};

class DataCallback {
public:
DataCallback() = default;
Expand All @@ -100,6 +124,14 @@ class DataCallback {
virtual void accelerometer_receive_callback(const AccelerometerDataView& data) = 0;

virtual void gyroscope_receive_callback(const GyroscopeDataView& data) = 0;

virtual bool i2c_receive_callback(DataId id, const I2cDataView& data) = 0;

virtual void i2c_error_callback(DataId id, const I2cErrorView& data) = 0;

void i2c_error_from_slave_address(DataId id, uint8_t slave_address) {
i2c_error_callback(id, I2cErrorView{.slave_address = slave_address});
}
};

} // namespace librmcs::data
12 changes: 12 additions & 0 deletions core/include/librmcs/protocol/i2c.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

#include <cstddef>
#include <cstdint>

namespace librmcs::protocol {

inline constexpr std::size_t kI2cDataLengthBits = 9;
inline constexpr std::uint16_t kI2cMaxDataLength =
static_cast<std::uint16_t>((1U << kI2cDataLengthBits) - 1U);

} // namespace librmcs::protocol
106 changes: 106 additions & 0 deletions core/src/protocol/deserializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ coroutine::LifoTask<void> Deserializer::process_stream() {
case FieldId::kUart3: success = co_await process_uart_field(id); break;
case FieldId::kGpio: success = co_await process_gpio_field(id); break;
case FieldId::kImu: success = co_await process_imu_field(id); break;
case FieldId::kI2c0: success = co_await process_i2c_field(id); break;
default: break;
}
if (!success)
Expand Down Expand Up @@ -289,4 +290,109 @@ coroutine::LifoTask<bool> Deserializer::process_imu_field(FieldId) {
co_return true;
}

coroutine::LifoTask<bool> Deserializer::process_i2c_field(FieldId field_id) {
I2cHeader::PayloadEnum payload_type;
uint8_t slave_address = 0;
uint16_t data_length = 0;
bool has_register = false;
bool error_flag = false;
{
const auto* header_bytes = co_await peek_bytes(sizeof(I2cHeader));
if (!header_bytes) [[unlikely]]
co_return false;

auto header = I2cHeader::CRef{header_bytes};
payload_type = header.get<I2cHeader::PayloadType>();
has_register = header.get<I2cHeader::HasRegister>();
error_flag = header.get<I2cHeader::ErrorFlag>();
slave_address = header.get<I2cHeader::SlaveAddress>();
data_length = header.get<I2cHeader::DataLength>();
consume_peeked();
}

if (slave_address > 0x7F) [[unlikely]]
co_return false;

switch (payload_type) {
case I2cHeader::PayloadEnum::kWrite:
case I2cHeader::PayloadEnum::kReadResult: {
if (error_flag) [[unlikely]]
co_return false;
if (payload_type == I2cHeader::PayloadEnum::kWrite && data_length == 0) [[unlikely]]
co_return false;

data::I2cDataView data_view{};
data_view.slave_address = slave_address;
data_view.has_register = has_register;

if (has_register) {
const auto* register_bytes = co_await peek_bytes(1);
if (!register_bytes) [[unlikely]]
co_return false;
data_view.reg_address = std::to_integer<uint8_t>(register_bytes[0]);
consume_peeked();
}

if (data_length) {
const auto* payload_bytes = co_await peek_bytes(data_length);
if (!payload_bytes) [[unlikely]]
co_return false;
data_view.payload = std::span<const std::byte>{payload_bytes, data_length};
consume_peeked();
} else {
data_view.payload = std::span<const std::byte>{};
}

if (payload_type == I2cHeader::PayloadEnum::kWrite) {
callback_.i2c_write_deserialized_callback(field_id, data_view);
} else {
callback_.i2c_read_result_deserialized_callback(field_id, data_view);
}
break;
}
case I2cHeader::PayloadEnum::kReadRequest: {
if (error_flag) [[unlikely]]
co_return false;
if (data_length == 0) [[unlikely]]
co_return false;

data::I2cReadConfigView data_view{};
data_view.slave_address = slave_address;
data_view.read_length = data_length;
data_view.has_register = has_register;

if (has_register) {
const auto* register_bytes = co_await peek_bytes(1);
if (!register_bytes) [[unlikely]]
co_return false;
data_view.reg_address = std::to_integer<uint8_t>(register_bytes[0]);
consume_peeked();
}

callback_.i2c_read_config_deserialized_callback(field_id, data_view);
break;
}
case I2cHeader::PayloadEnum::kError: {
data::I2cErrorView data_view{};
data_view.slave_address = slave_address;
data_view.data_length = data_length;
data_view.has_register = has_register;
data_view.is_read = error_flag;

if (has_register) {
const auto* register_bytes = co_await peek_bytes(1);
if (!register_bytes) [[unlikely]]
co_return false;
data_view.reg_address = std::to_integer<uint8_t>(register_bytes[0]);
consume_peeked();
}
callback_.i2c_error_deserialized_callback(field_id, data_view);
break;
}
default: co_return false;
}

co_return true;
}

} // namespace librmcs::core::protocol
12 changes: 12 additions & 0 deletions core/src/protocol/deserializer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ class DeserializeCallback {

virtual void gyroscope_deserialized_callback(const data::GyroscopeDataView& data) = 0;

virtual void i2c_write_deserialized_callback(FieldId id, const data::I2cDataView& data) = 0;

virtual void
i2c_read_config_deserialized_callback(FieldId id, const data::I2cReadConfigView& data) = 0;

virtual void
i2c_read_result_deserialized_callback(FieldId id, const data::I2cDataView& data) = 0;

virtual void i2c_error_deserialized_callback(FieldId id, const data::I2cErrorView& data) = 0;
Comment on lines +44 to +52
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 检查两个 Vendor 类是否 override 了新加的 4 个 i2c_*_deserialized_callback 方法。
fd -a 'vendor.hpp' firmware | while read -r f; do
  echo "=== $f ==="
  rg -n 'i2c_(write|read_config|read_result|error)_deserialized_callback' "$f" || echo "  (未实现)"
done

Repository: Alliance-Algorithm/librmcs

Length of output: 410


🏁 Script executed:

#!/bin/bash
# 检查 rmcs_board vendor.hpp 的内容和继承关系
echo "=== rmcs_board vendor.hpp 内容 ==="
cat -n firmware/rmcs_board/app/src/usb/vendor.hpp | head -100

echo -e "\n=== rmcs_board Vendor 类定义 ==="
rg -A 5 'class Vendor' firmware/rmcs_board/app/src/usb/vendor.hpp

echo -e "\n=== c_board 四个 callback 实现的内容(示例) ==="
rg -A 8 'i2c_write_deserialized_callback' firmware/c_board/app/src/usb/vendor.hpp | head -20

Repository: Alliance-Algorithm/librmcs

Length of output: 4810


🏁 Script executed:

#!/bin/bash
# 检查是否存在其他继承 DeserializeCallback 的类
echo "=== 所有继承 DeserializeCallback 的类 ==="
rg 'class \w+.*:\s*.*DeserializeCallback' firmware --type hpp --type cpp

Repository: Alliance-Algorithm/librmcs

Length of output: 150


🏁 Script executed:

#!/bin/bash
# 读取 rmcs_board vendor.hpp 的全部内容,确认是否有 i2c 回调实现
wc -l firmware/rmcs_board/app/src/usb/vendor.hpp
echo "=== 完整文件内容 ==="
cat -n firmware/rmcs_board/app/src/usb/vendor.hpp

echo -e "\n=== 检查是否含有 i2c_write_deserialized_callback ==="
grep -n 'i2c_write_deserialized_callback' firmware/rmcs_board/app/src/usb/vendor.hpp || echo "未找到"

Repository: Alliance-Algorithm/librmcs

Length of output: 6532


🏁 Script executed:

#!/bin/bash
# 正确的方式:用 --type-list 查看支持的类型,或直接用 rg 的默认设置
echo "=== 所有继承 DeserializeCallback 的类 ==="
rg 'class \w+.*:\s*.*DeserializeCallback' firmware

Repository: Alliance-Algorithm/librmcs

Length of output: 105


🏁 Script executed:

#!/bin/bash
# 查看 DeserializeCallback 中新增的 i2c 方法签名
echo "=== DeserializeCallback 中的 i2c 方法 ==="
rg -A 2 'i2c_(write|read_config|read_result|error)_deserialized_callback' core/src/protocol/deserializer.hpp

# 检查 rmcs_board 是否有 I2C 支持
echo -e "\n=== rmcs_board 是否有 I2C 支持 ==="
find firmware/rmcs_board -name '*i2c*' -type f 2>/dev/null || echo "(未找到 I2C 文件)"

Repository: Alliance-Algorithm/librmcs

Length of output: 604


🏁 Script executed:

#!/bin/bash
# 检查 c_board vendor.hpp 中 i2c 回调的完整实现
echo "=== c_board i2c_write_deserialized_callback 完整实现 ==="
rg -A 6 'i2c_write_deserialized_callback' firmware/c_board/app/src/usb/vendor.hpp

echo -e "\n=== c_board i2c_read_result_deserialized_callback 完整实现 ==="
rg -A 6 'i2c_read_result_deserialized_callback' firmware/c_board/app/src/usb/vendor.hpp

echo -e "\n=== c_board i2c_error_deserialized_callback 完整实现 ==="
rg -A 6 'i2c_error_deserialized_callback' firmware/c_board/app/src/usb/vendor.hpp

Repository: Alliance-Algorithm/librmcs

Length of output: 1048


firmware/rmcs_board/app/src/usb/vendor.hpp 中缺少四个 I2C 回调实现,导致构建失败。

Vendor 类继承自 DeserializeCallback(私有继承),必须实现所有纯虚方法。firmware/c_board/app/src/usb/vendor.hpp 中的实现已齐全(downlink 写操作路由到 i2c0,读结果和错误帧作为仅上行数据类型被忽略)。但 firmware/rmcs_board/app/src/usb/vendor.hpp 缺失下述四个 override:

  • i2c_write_deserialized_callback
  • i2c_read_config_deserialized_callback
  • i2c_read_result_deserialized_callback
  • i2c_error_deserialized_callback

rmcs_board 没有 I2C 硬件,这些方法应为 no-op(可参考 rmcs_board 现有的 GPIO 或加速度计回调实现),以便解除编译阻塞。

🧰 Tools
🪛 GitHub Actions: Lint

[error] 44-52: Pure virtual functions in DeserializeCallback: i2c_write_deserialized_callback, i2c_read_config_deserialized_callback, i2c_read_result_deserialized_callback, i2c_error_deserialized_callback (unimplemented by Vendor).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/src/protocol/deserializer.hpp` around lines 44 - 52, Add no-op overrides
for the four missing I2C callbacks in the Vendor class so it fulfills the
DeserializeCallback interface: implement i2c_write_deserialized_callback(FieldId
id, const data::I2cDataView& data) override,
i2c_read_config_deserialized_callback(FieldId id, const data::I2cReadConfigView&
data) override, i2c_read_result_deserialized_callback(FieldId id, const
data::I2cDataView& data) override, and i2c_error_deserialized_callback(FieldId
id, const data::I2cErrorView& data) override in
firmware/rmcs_board/app/src/usb/vendor.hpp; each should have an empty body
(no-op), similar to existing rmcs_board GPIO/accelerometer callbacks, to satisfy
the pure-virtual methods in DeserializeCallback and unblock compilation.


virtual void error_callback() = 0;
};

Expand Down Expand Up @@ -115,6 +125,8 @@ class Deserializer : private coroutine::InlineLifoContext<1024> {

coroutine::LifoTask<bool> process_imu_field(FieldId field_id);

coroutine::LifoTask<bool> process_i2c_field(FieldId field_id);

// Await until at least `size` contiguous bytes are available at the current read position.
// Returns a pointer to a contiguous region of at least `size` bytes.
// (from input buffer or pending cache)
Expand Down
25 changes: 25 additions & 0 deletions core/src/protocol/protocol.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#pragma once

#include <cstddef>
#include <cstdint>

#include "core/include/librmcs/data/datas.hpp"
#include "core/include/librmcs/protocol/i2c.hpp"
#include "core/src/utility/bitfield.hpp"

namespace librmcs::core::protocol {
Expand Down Expand Up @@ -130,4 +132,27 @@ struct ImuGyroscopePayload : utility::Bitfield<6> {
using Z = utility::BitfieldMember<32, 16, int16_t>;
};

struct I2cHeader : utility::Bitfield<3> {
enum class PayloadEnum : uint8_t {
kWrite = 0,
kReadRequest = 1,
kReadResult = 2,
kError = 3,
};

using PayloadType = utility::BitfieldMember<4, 2, PayloadEnum>;
using HasRegister = utility::BitfieldMember<6, 1>;
using ErrorFlag = utility::BitfieldMember<7, 1>;
using SlaveAddress = utility::BitfieldMember<8, 7>;
using DataLength = utility::BitfieldMember<15, 9>;

static constexpr std::size_t kSlaveAddressBits = SlaveAddress::kBitWidth;
static constexpr std::size_t kDataLengthBits = DataLength::kBitWidth;
static constexpr std::uint16_t kMaxDataLength = librmcs::protocol::kI2cMaxDataLength;

static_assert(kSlaveAddressBits == 7);
static_assert(kDataLengthBits == librmcs::protocol::kI2cDataLengthBits);
static_assert(kMaxDataLength == ((1U << kDataLengthBits) - 1U));
};

} // namespace librmcs::core::protocol
Loading
Loading