RIT VEXU Core API
Loading...
Searching...
No Matches
vdb_device.h
1#pragma once
2#include <cstdint>
3#include <cstdio>
4#include <deque>
5#include <functional>
6#include <tuple>
7
8#include "core/device/cobs_device.h"
9#include "core/device/vdb/protocol.h"
10
14namespace VDB {
15template <typename... Fields>
16class Device : public COBSSerialDevice {
17 public:
18 static constexpr int32_t NO_ACTIVITY_DELAY = 2; // ms
19 static constexpr std::size_t MAX_OUT_QUEUE_SIZE = 50;
20 static constexpr std::size_t MAX_IN_QUEUE_SIZE = 50;
21
22 enum SEND_PACKET_STATE { SUCCESS, NONE_QUEUED, ERROR };
23
29 explicit Device(int32_t port, int32_t baud_rate, VDP::Channel<Fields>&... channels)
30 : COBSSerialDevice(port, baud_rate), channels_(channels...) {
31 static_assert(sizeof...(channels) <= VDP::MAX_CHANNELS,
32 "There can be no more than 256 Channels sent to a VDB Device");
33 serial_task = vex::task(Device::serial_thread, (void*)this, vex::thread::threadPriorityHigh);
34 }
35
40 bool add_to_queue(const VDP::Packet& packet) {
41 if (packet.empty()) {
42 printf("VDP WARNING: Empty Packets are not allowed\n");
43 return false;
44 }
45 outbound_mutex.lock();
46 if (outbound_packets.size() >= MAX_OUT_QUEUE_SIZE) {
47 outbound_mutex.unlock();
48 return false;
49 }
50 outbound_packets.push_back(packet);
51 outbound_mutex.unlock();
52 return true;
53 }
54
56 SEND_PACKET_STATE write_packet_from_queue() {
57 // packet to write to the device
58 VDP::Packet outbound_packet = {};
59 // lock the serial port
60 outbound_mutex.lock();
61 // check if we have a packet to write
62 if (outbound_packets.size() > 0) {
63 // of we do take the latest packet out of the vector of packets we have
64 outbound_packet = std::move(outbound_packets.front());
65 outbound_packets.pop_front();
66 }
67 // unlock
68 outbound_mutex.unlock();
69 if (outbound_packet.size() == 0) {
70 return NONE_QUEUED;
71 }
72
73 int sent = send_cobs_packet_blocking(outbound_packet.data(), outbound_packet.size());
74
75 if (sent >= 0) {
76 return SUCCESS;
77 } else {
78 printf("Failed to send packet (%d):\n", sent);
79 hexdump(outbound_packet.data(), outbound_packet.size());
80 return ERROR;
81 }
82 }
83
85 static int serial_thread(void* vself) {
86 // defines itself within the thread
87 Device& self = *(Device*)vself;
88
89 // serial thread loop
90 while (true) {
91 bool did_something = false;
92
93 // Writing
94 SEND_PACKET_STATE send_state = self.write_packet_from_queue();
95 if (send_state != NONE_QUEUED) {
96 did_something = true;
97 }
98 // Reading
99 if (self.poll_incoming_data_once()) {
100 Packet decoded = {};
101 decoded = self.get_last_decoded_packet();
102 self.apply_packet(decoded);
103 did_something = true;
104 }
105 if (!did_something) {
106 vexDelay(NO_ACTIVITY_DELAY);
107 }
108 }
109 return 0;
110 }
111
116 void send_channel(VDP::ChannelID id) {
117 std::apply(
118 [&](auto&... channel) {
119 (
120 [&] {
121 if (id == channel.get_id()) {
122 if (channel.acked) {
123 this->add_to_queue(channel.serialize(VDP::PacketType::Data));
124 } else {
125 this->add_to_queue(channel.serialize(VDP::PacketType::Schema));
126 }
127 }
128 }(),
129 ...);
130 },
131 channels_);
132 }
133
138 void apply_packet(VDP::Packet in) {
139 const VDP::PacketValidity status = VDP::validate_packet(in);
140
141 // check if the packet is valid;
142 if (status == VDP::PacketValidity::BadChecksum) {
143 VDPWarnf("Controller: Bad packet checksum. Skipping");
144 return;
145 } else if (status == VDP::PacketValidity::TooSmall) {
146 VDPWarnf("Controller: Packet too small to be valid (%d bytes). Skipping", (int)in.size());
147 return;
148 } else if (status != VDP::PacketValidity::Ok) {
149 VDPWarnf("Controller: Unknown validity of packet (BAD). Skipping");
150 return;
151 }
152
153 // decode the header byte and route it accordingly
154 VDP::PacketHeader header = VDP::decode_header_byte(in[0]);
155 if (header.func == VDP::PacketFunction::Send) {
156 if (header.type == VDP::PacketType::Data) {
157 // decode packet and apply to channel
158 VDP::ChannelID id_to_update = in[1];
159 std::apply(
160 [&](auto&... channel) {
161 (
162 [&] {
163 if (id_to_update == channel.get_id()) {
164 channel.apply_update(VDP::PacketSpan(in).subspan(2));
165 }
166 }(),
167 ...);
168 },
169 channels_);
170 // acknowledge packet
171 VDP::Packet data_ack;
172 data_ack.push_back(
173 VDP::make_header_byte({VDP::PacketType::Data, VDP::PacketFunction::Acknowledge}));
174 data_ack.push_back(id_to_update);
175 VDP::Packet checksum = VDP::checksum_pac(data_ack);
176 data_ack.insert(data_ack.end(), checksum.begin(), checksum.end());
177 add_to_queue(data_ack);
178
179 } else if (header.type == VDP::PacketType::Schema) {
180 VDP::ChannelID acked_id = in[1];
181 std::apply(
182 [&](auto&... channel) {
183 (
184 [&] {
185 if (acked_id == channel.get_id()) {
186 channel.acked = false;
187 }
188 }(),
189 ...);
190 },
191 channels_);
192 }
193 } else if (header.func == VDP::PacketFunction::Acknowledge) {
194 // mark channel as acknowledged, dont do anything if data packet
195 if (header.type == VDP::PacketType::Schema) {
196 VDP::ChannelID acked_id = in[1];
197 std::apply(
198 [&](auto&... channel) {
199 (
200 [&] {
201 if (acked_id == channel.get_id()) {
202 channel.acked = true;
203 }
204 }(),
205 ...);
206 },
207 channels_);
208 }
209 }
210 }
211
212 private:
217 std::deque<VDP::Packet> outbound_packets{};
218 vex::mutex outbound_mutex;
219 std::tuple<VDP::Channel<Fields>...> channels_;
220
221 bool waiting_to_receive = false;
226 std::deque<WirePacket> inbound_packets;
227
232 WirePacket inbound_buffer;
233
235 vex::task serial_task;
236
237 std::function<void(const VDP::Packet& packet)> callback;
238};
239template <typename... Fields>
240Device(int32_t, int32_t, VDP::Channel<Fields>...) -> Device<Fields...>;
241
242} // namespace VDB
Definition protocol.h:13