-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSFOCInterface.cpp
More file actions
377 lines (323 loc) · 13.3 KB
/
Copy pathSFOCInterface.cpp
File metadata and controls
377 lines (323 loc) · 13.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
#include "SFOCInterface.hpp"
#include "Parse.hpp"
#include "SimpleFOCRegisters.hpp"
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <memory>
#include <thread>
#include <utility>
#include <vector>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
/* define local struct for tty
* load state into that struct
* do stuff to it
* set the state of the tty from the struct */
static int set_interface_attribs(int fd, int speed)
{
struct termios tty;
if (tcgetattr(fd, &tty) < 0) {
printf("Error from tcgetattr: %s\n", strerror(errno));
return -1;
}
cfsetospeed(&tty, (speed_t)speed);
cfsetispeed(&tty, (speed_t)speed);
tty.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8; /* 8-bit characters */
tty.c_cflag &= ~PARENB; /* no parity bit */
tty.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
tty.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
/* setup for non-canonical mode */
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
tty.c_oflag &= ~OPOST;
/* fetch bytes as they become available */
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 1;
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
printf("Error from tcsetattr: %s\n", strerror(errno));
return -1;
}
return 0;
}
static void print_ops(Sample * const sample) {
for (auto num : sample->operands) {
uint8_t reg_type = SimpleFOCRegisters::regs->typeOfRegister(num.reg);
switch(reg_type)
{
case RegType::FLOAT:
printf("%.3f ", num.f);
break;
case RegType::UINT32:
printf("%u ", num.u32);
break;
case RegType::INT32:
printf("%i ", num.i32);
break;
case RegType::UINT8:
printf("%u ", num.u8);
break;
} // switch(reg_type)
} // for num : operands
printf("\n");
} // print_ops
SFOC::SFOC(ParseType parse_type) : regs(SimpleFOCRegisters()) {
(void) parse_type; // do something with this when relevant
telem_conf = new TelemetryConfig(®s);
parser = new BinaryIOParser(®s, *telem_conf);
}
int SFOC::run_serial(int serial_fd) {
typedef std::chrono::high_resolution_clock Time;
typedef std::chrono::milliseconds ms;
auto t0 = Time::now();
auto t1 = Time::now();
// instance and manually set the telemetry configuration
// TODO - ask the device for it's config and set this from that
// ask for header frame
// send 2 sync frames
telem_conf->num_operands = 5;
telem_conf->operand_registers.push_back(SimpleFOCRegister::REG_TARGET);
telem_conf->operand_registers.push_back(SimpleFOCRegister::REG_ANGLE);
telem_conf->operand_registers.push_back(SimpleFOCRegister::REG_VELOCITY);
telem_conf->operand_registers.push_back(SimpleFOCRegister::REG_CURRENT_Q);
telem_conf->operand_registers.push_back(SimpleFOCRegister::REG_CURRENT_D);
telem_conf->update_total();
// setup some stuff to use in the main loop
std::vector<uint8_t> bytes_to_parse;
float fps=0;
size_t frame_counter = 0;
Sample sample(telem_conf->num_operands);
ParseResult parse_result;
std::vector<std::unique_ptr<DataReq>> requests_to_service;
// BinaryIOParser parser(®s, telem_conf);
std::vector<uint8_t> write_buf;
write_buf.reserve(512);
do {
size_t samples_to_write = write_queue.get_length();
// printf("have %lu samples to send!\n", samples_to_write);
for (size_t i=0;i<samples_to_write;i++) {
auto write_sample = write_queue.dequeue(); // shouldn't block because we just checked that there was something to dequeue
auto bytes = parser->encode_frame(write_sample);
if (bytes.size() != 0) {
write_buf.insert(
write_buf.end(),
std::make_move_iterator(bytes.begin()),
std::make_move_iterator(bytes.end())
);
}
}
// printf("encoded samples and wrote to send buffer\n");
size_t write_len = 0;
// printf("have %lu bytes to send!\n", write_buf.size());
size_t bytes_to_write = write_buf.size();
if (bytes_to_write > 0) {
while (write_len != bytes_to_write) {
// printf("starting serial write\n");
write_len = write(serial_fd, &write_buf[0], write_buf.size());
printf("wrote %lu bytes on the wire\n",write_len);
if (write_len == write_buf.size()) {
write_buf.clear();
// printf("cleared write buffer\n");
} else {
printf("didn't write all the bytes to the serial port!\n");
// printf("starting to remove bytes from buffer\n");
write_buf.erase(
write_buf.begin(),
write_buf.begin()+write_len);
// printf("removed bytes from buffer\n");
}
}
}
// get requests out of the incoming queue
while (request_queue.get_length() > 0) {
auto req = request_queue.try_dequeue();
if (req) {
requests_to_service.push_back(std::move(req));
}
}
// printf("starting read from serial port\n");
uint8_t read_buf[512];
int rdlen;
rdlen = read(serial_fd, read_buf, sizeof(read_buf) - 1);
// printf("read from serial port\n");
if (rdlen > 0)
{ // we have some data to work with
// put all the bytes from the read buffer into the end of the parse buffer
bytes_to_parse.insert(bytes_to_parse.end(), read_buf, read_buf+rdlen);
while (bytes_to_parse.size() > 3) {
// clear out our previous sample
sample.clear();
// try to parse a frame
parse_result = parser->parse_frame(bytes_to_parse, sample);
if (parse_result.bytes_used != 0) {
// this is sloppy and inefficient because vectors aren't meant to be used this way
bytes_to_parse.erase(bytes_to_parse.begin(),bytes_to_parse.begin()+parse_result.bytes_used);
}
long long et; // = std::chrono::duration_cast<ms>(t1-t0).count();
// I'm not sure I like using continue inside the switch
switch (parse_result.status) {
case ParseResult::SUCCESS:
et = std::chrono::duration_cast<ms>(t1-t0).count();
t1 = Time::now();
if ( et >= 100) {
fps = (float)frame_counter/((float)et/1000);
// printf("got %.3f frames per sec\n", fps);
(void) fps;
// print_ops(&sample);
t0=t1;
frame_counter = 0;
} // if (et ..... timer for prints
frame_counter+=1;
service_requests(sample, requests_to_service);
// printf("servicing reqs\n");
// for (size_t i=0; i<requests_to_service.size(); i++) {
// auto& req = requests_to_service[i];
// if (req->n_samples < req->result_data->size()) {
// req->result_data->push_back(sample);
// } else { // req is fulfilled and can be returned
// // idx_to_remove.push_back(i);
// auto req_to_move = std::move(requests_to_service[i]);
// data_queue.enqueue(std::move(req_to_move));
// }
// }
//
// // this can't be the right way to remove a set of stuff from a vector, can it?
// bool run = true;
// while (run) {
// run=false;
// for (size_t i=0; i<requests_to_service.size(); i++) {
// if (!requests_to_service[i]) {
// printf("removing req\n");
// requests_to_service.erase(requests_to_service.begin()+i);
// run=true;
// break;
// }
// } // for
// } // while
continue;
case ParseResult::CONF_MISMATCH:
case ParseResult::BAD_START_BYTE:
case ParseResult::UNHANDLED_FRAME:
continue;
case ParseResult::NOT_ENOUGH_BYTES:
default:
break;
} // switch
break; // break the while loop if we get here
} // while (bytes_to_parse.size() > 0)
} // if (rdlen > 0)
else if (rdlen < 0) {
printf("Error from read: %d: %s\n", rdlen, strerror(errno));
} else { /* rdlen == 0 */
printf("Timeout from read\n");
}
} while (run_thread);
return 0;
}
int SFOC::connect(std::string port) {
// open and configure the serial port
int fd;
fd = open(port.c_str(), O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0) {
printf("Error opening %s: %s\n", port.c_str(), strerror(errno));
return -1;
}
/*baudrate 115200, 8 bits, no parity, 1 stop bit */
// baud doesn't matter because using USB right now
set_interface_attribs(fd, B115200);
std::thread t1(&SFOC::run_serial, this, fd);
active_threads.push_back(std::move(t1));
return 0;
}
int SFOC::disconnect() {
run_thread = false;
for (auto &t : active_threads) {
t.join();
}
active_threads.clear();
return 0;
}
int SFOC::send_frame(Sample sample) {
write_queue.enqueue(sample);
return 0;
}
void SFOC::service_requests(Sample sample, std::vector<std::unique_ptr<DataReq>> &requests_to_service) {
size_t sz = requests_to_service.size();
for (size_t i=0; i<sz; i++) {
auto& req = requests_to_service[i];
if (req->n_samples > req->result_data->size()) {
req->result_data->push_back(sample);
} else { // req is fulfilled and can be returned
// idx_to_remove.push_back(i);
auto req_to_move = std::move(requests_to_service[i]);
data_queue.enqueue(std::move(req_to_move));
}
}
// this can't be the right way to remove a set of stuff from a vector, can it?
bool run = true;
while (run) {
run=false;
for (size_t i=0; i<requests_to_service.size(); i++) {
if (!requests_to_service[i]) {
printf("removing req\n");
requests_to_service.erase(requests_to_service.begin()+i);
run=true;
break;
}
} // for
} // while
}
uint64_t SFOC::req_data(size_t n_samples) {
// construct a (shared/unique ptr?) DataReq
// pass it by value to the req queue
//
// producer thread will remove it from the queue and add it to a list of reqs it's servicing
// producer will continue servicing reqs until they're completed
// producer will then pass the req and data back to main thread where it can be retrieved
//
// main thread will retrieve the data it's after from another function
if (n_samples == 0) return 0;
uint64_t req_handle = msg_id_cnt++;
auto request = std::make_unique<DataReq>(DataReq());
request->req_type = DataReq::NUM;
request->n_samples = n_samples;
request->handle = req_handle;
request_queue.enqueue(std::move(request));
return req_handle;
}
void SFOC::get_data_from_queue() {
while (data_queue.get_length() > 0) {
auto req = data_queue.try_dequeue();
if (req) {
completed_reqs.insert(std::make_pair(req->handle, std::move(req)));
}
}
}
std::expected<std::unique_ptr<DataReq>, int> SFOC::get_req_data(uint64_t data_handle) {
// this function will attempt to retrieve the data for the supplied request handle
//
// (in a function call)
// first it removes all data in the producer return queue and puts it into
// a map with it's handle as the key
//
// then it uses the supplied handle to check if there's a matching record in
// the map.
// if not matching handle, return unexpected error
// if there is a matching handle, pop and return it
if (data_handle==0) return std::unexpected(-1);
get_data_from_queue();
if (completed_reqs.find(data_handle) != completed_reqs.end()) {
auto req = std::move(completed_reqs[data_handle]);
completed_reqs.erase(data_handle);
return req;
}
return std::unexpected(-2);
}