Skip to content

Commit 1307481

Browse files
committed
chore: initial commit
0 parents  commit 1307481

5 files changed

Lines changed: 454 additions & 0 deletions

File tree

.github/workflows/build.yml

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
name: XRobot Module Build Test
2+
3+
on:
4+
push:
5+
pull_request:
6+
schedule:
7+
- cron: '0 3 1 * *' # 每月1日凌晨3点(UTC)自动触发
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
container:
13+
image: ghcr.io/xrobot-org/docker-image-linux:main
14+
options: --user 0
15+
16+
env:
17+
XR_MODULE_NAME: ${{ github.event.repository.name }}
18+
19+
steps:
20+
- name: Checkout current module repo to ./Modules/${{ env.XR_MODULE_NAME }}
21+
uses: actions/checkout@v4
22+
with:
23+
path: Modules/${{ env.XR_MODULE_NAME }}
24+
25+
- name: Create main.cpp
26+
run: |
27+
cat > main.cpp <<'EOF'
28+
#include <iostream>
29+
#include "xrobot_main.hpp"
30+
#include "libxr.hpp"
31+
32+
int main() {
33+
LibXR::PlatformInit();
34+
LibXR::STDIO::Printf<"This is XRobot Module Template Test\n">();
35+
LibXR::HardwareContainer hw;
36+
XRobotMain(hw);
37+
return 0;
38+
}
39+
EOF
40+
41+
- name: Create minimal CMakeLists.txt
42+
run: |
43+
cat > CMakeLists.txt <<'EOF'
44+
project(xrobot_mod_test CXX)
45+
set(CMAKE_CXX_STANDARD 20)
46+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
47+
add_executable(xr_test main.cpp)
48+
set(XROBOT_MODULES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Modules/)
49+
add_subdirectory(libxr)
50+
target_include_directories(xr_test PUBLIC $<TARGET_PROPERTY:xr,INTERFACE_INCLUDE_DIRECTORIES> ${CMAKE_SOURCE_DIR}/User)
51+
target_link_libraries(xr_test PUBLIC xr)
52+
EOF
53+
54+
- name: Pull libxr to ./libxr
55+
run: git clone --depth=1 https://github.com/Jiu-xiao/libxr ./libxr
56+
57+
- name: Setup Python & Install deps
58+
run: |
59+
python3 -m pip install --upgrade pip
60+
pip3 install pyyaml requests
61+
62+
- name: Add XRobot tools to PATH
63+
run: |
64+
echo "$HOME/.local/bin" >> $GITHUB_PATH
65+
66+
- name: Install xrobot toolchain
67+
run: |
68+
pip3 install xrobot libxr
69+
70+
- name: Run xrobot_setup
71+
run: |
72+
xrobot_setup || true
73+
74+
- name: Run xrobot_init_mod
75+
run: |
76+
xrobot_init_mod
77+
78+
- name: Add BlinkLED module
79+
run: |
80+
xrobot_add_mod BlinkLED --instance-id BlinkLED_0
81+
82+
- name: Add this repo module
83+
run: |
84+
xrobot_add_mod ${{ env.XR_MODULE_NAME }} && cat User/xrobot.yaml
85+
86+
- name: Generate main again
87+
run: |
88+
xrobot_setup
89+
90+
- name: Build
91+
run: |
92+
mkdir -p build
93+
cd build
94+
cmake ..
95+
make
96+
97+
- name: Create Tag via GitHub API
98+
if: ${{ success() && github.event_name != 'pull_request' }}
99+
uses: actions/github-script@v7
100+
with:
101+
github-token: ${{ secrets.GITHUB_TOKEN }}
102+
script: |
103+
const tagPrefix = 'auto-'
104+
const date = new Date();
105+
const yyyy = date.getUTCFullYear();
106+
const mm = String(date.getUTCMonth() + 1).padStart(2, '0');
107+
const dd = String(date.getUTCDate()).padStart(2, '0');
108+
const HH = String(date.getUTCHours()).padStart(2, '0');
109+
const MM = String(date.getUTCMinutes()).padStart(2, '0');
110+
const SS = String(date.getUTCSeconds()).padStart(2, '0');
111+
const tagName = `${tagPrefix}${yyyy}${mm}${dd}-${HH}${MM}${SS}`;
112+
113+
// 获取当前 commit sha
114+
const sha = process.env.GITHUB_SHA;
115+
116+
// 创建 tag reference
117+
await github.rest.git.createRef({
118+
owner: context.repo.owner,
119+
repo: context.repo.repo,
120+
ref: `refs/tags/${tagName}`,
121+
sha: sha
122+
});
123+
console.log(`Created tag: ${tagName} on sha: ${sha}`);

CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# CMakeLists.txt for GreySensor
2+
3+
target_include_directories(xr PUBLIC ${CMAKE_CURRENT_LIST_DIR})
4+
5+
file(GLOB MODULE_GREYSENSOR_SRC
6+
"${CMAKE_CURRENT_LIST_DIR}/*.cpp"
7+
"${CMAKE_CURRENT_LIST_DIR}/*.c"
8+
)
9+
10+
target_sources(xr PRIVATE ${MODULE_GREYSENSOR_SRC})

GreySensor.cpp

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
#include "GreySensor.hpp"
2+
3+
#include "libxr_def.hpp"
4+
#include "thread.hpp"
5+
6+
uint8_t GreySensor::BuildBit(size_t channel)
7+
{
8+
return static_cast<uint8_t>(1U << channel);
9+
}
10+
11+
int16_t GreySensor::BuildPosition(size_t channel, size_t channel_count)
12+
{
13+
const int32_t centered =
14+
(static_cast<int32_t>(channel) * 2) - static_cast<int32_t>(channel_count - 1);
15+
return static_cast<int16_t>((centered * POSITION_SCALE) / 2);
16+
}
17+
18+
uint8_t GreySensor::GetLostSide(int16_t position)
19+
{
20+
if (position < 0)
21+
{
22+
return LOST_SIDE_LEFT;
23+
}
24+
25+
if (position > 0)
26+
{
27+
return LOST_SIDE_RIGHT;
28+
}
29+
30+
return LOST_SIDE_UNKNOWN;
31+
}
32+
33+
GreySensor::GreySensor(LibXR::HardwareContainer& hw, LibXR::ApplicationManager& app,
34+
std::initializer_list<const char*> channel_names,
35+
bool active_low, const char* topic_name,
36+
uint32_t publish_period_ms)
37+
: channel_count_(channel_names.size()),
38+
topic_(LibXR::Topic::CreateTopic<Sample>(topic_name, nullptr, false, true,
39+
true)),
40+
active_low_(active_low),
41+
publish_period_ms_(publish_period_ms)
42+
{
43+
ASSERT(channel_count_ > 0);
44+
ASSERT(channel_count_ <= MAX_CHANNEL_COUNT);
45+
46+
size_t i = 0;
47+
for (const char* name : channel_names)
48+
{
49+
ASSERT(name != nullptr);
50+
channels_[i] = hw.FindOrExit<LibXR::GPIO>({name});
51+
const LibXR::ErrorCode err = channels_[i]->SetConfig(
52+
{LibXR::GPIO::Direction::INPUT, LibXR::GPIO::Pull::NONE});
53+
ASSERT(err == LibXR::ErrorCode::OK);
54+
i++;
55+
}
56+
57+
last_active_mask_ = ReadActiveMask();
58+
app.Register(*this);
59+
}
60+
61+
GreySensor::Sample GreySensor::ReadDigital() const
62+
{
63+
Sample sample;
64+
sample.channel_count = static_cast<uint8_t>(channel_count_);
65+
66+
for (size_t i = 0; i < channel_count_; i++)
67+
{
68+
const bool raw_high = channels_[i]->Read();
69+
const bool active = active_low_ ? !raw_high : raw_high;
70+
71+
sample.raw[i] = raw_high ? 1U : 0U;
72+
sample.active[i] = active ? 1U : 0U;
73+
74+
if (raw_high)
75+
{
76+
sample.raw_mask |= BuildBit(i);
77+
}
78+
79+
if (active)
80+
{
81+
sample.active_mask |= BuildBit(i);
82+
sample.active_count++;
83+
}
84+
}
85+
86+
return sample;
87+
}
88+
89+
void GreySensor::UpdatePositionState(Sample& sample)
90+
{
91+
int32_t weighted_sum = 0;
92+
93+
for (size_t i = 0; i < channel_count_; i++)
94+
{
95+
if (sample.active[i] != 0U)
96+
{
97+
weighted_sum += BuildPosition(i, channel_count_);
98+
}
99+
}
100+
101+
if (sample.active_count != 0U)
102+
{
103+
sample.line_detected = 1;
104+
sample.line_lost = 0;
105+
sample.weighted_position =
106+
static_cast<int16_t>(weighted_sum / sample.active_count);
107+
sample.position = sample.weighted_position;
108+
109+
remembered_position_ = sample.position;
110+
has_position_memory_ = true;
111+
lost_count_ = 0;
112+
}
113+
else
114+
{
115+
sample.line_detected = 0;
116+
sample.line_lost = 1;
117+
lost_count_++;
118+
119+
sample.position = has_position_memory_ ? remembered_position_ : 0;
120+
sample.lost_side =
121+
has_position_memory_ ? GetLostSide(remembered_position_) : LOST_SIDE_UNKNOWN;
122+
sample.lost_count = lost_count_;
123+
}
124+
125+
sample.remembered_position = has_position_memory_ ? remembered_position_ : 0;
126+
}
127+
128+
GreySensor::Sample GreySensor::Read()
129+
{
130+
Sample sample = ReadDigital();
131+
UpdatePositionState(sample);
132+
return sample;
133+
}
134+
135+
uint8_t GreySensor::ReadRawMask() const { return ReadDigital().raw_mask; }
136+
137+
uint8_t GreySensor::ReadActiveMask() const { return ReadDigital().active_mask; }
138+
139+
int16_t GreySensor::ReadPosition() { return Read().position; }
140+
141+
size_t GreySensor::ChannelCount() const { return channel_count_; }
142+
143+
void GreySensor::OnMonitor()
144+
{
145+
const uint32_t now_ms = LibXR::Thread::GetTime();
146+
if (has_published_ && publish_period_ms_ != 0U &&
147+
static_cast<uint32_t>(now_ms - last_publish_ms_) < publish_period_ms_)
148+
{
149+
return;
150+
}
151+
152+
Sample sample = Read();
153+
sample.changed_mask = static_cast<uint8_t>(sample.active_mask ^ last_active_mask_);
154+
sample.sequence = sequence_++;
155+
156+
last_active_mask_ = sample.active_mask;
157+
last_publish_ms_ = now_ms;
158+
has_published_ = true;
159+
160+
topic_.Publish(sample);
161+
}

0 commit comments

Comments
 (0)