Skip to content

Commit cb581d3

Browse files
Merge pull request #8 from unofficial-rev-port/fix/canwrapper
Revert changes made to `canWrapper.cc`
2 parents 0ef46b6 + 9827e48 commit cb581d3

File tree

1 file changed

+107
-66
lines changed

1 file changed

+107
-66
lines changed

src/canWrapper.cc

Lines changed: 107 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,16 @@
33
#include <rev/CANMessage.h>
44
#include <rev/CANStatus.h>
55
#include <rev/CANBridgeUtils.h>
6-
#ifdef _WIN32
76
#include <rev/Drivers/CandleWinUSB/CandleWinUSBDriver.h>
87
#include <rev/Drivers/CandleWinUSB/CandleWinUSBDevice.h>
9-
#else
10-
11-
#include "rev/Drivers/SerialPort/SerialDriver.h"
12-
#endif
13-
14-
158
#include <utils/ThreadUtils.h>
169
#include <hal/HAL.h>
1710
#include <hal/CAN.h>
1811
#include <napi.h>
1912
#include <thread>
2013
#include <chrono>
2114
#include <map>
15+
#include <array>
2216
#include <vector>
2317
#include <set>
2418
#include <exception>
@@ -27,25 +21,34 @@
2721
#include "canWrapper.h"
2822
#include "DfuSeFile.h"
2923

30-
#define DEVICE_NOT_FOUND_ERROR "Device not found. Make sure to run getDevices()"
24+
#define DEVICE_NOT_FOUND_ERROR "Device not found. Make sure to run getDevices()"
3125

32-
rev::usb::CANDriver* driver =
33-
#ifdef _WIN32
34-
new rev::usb::CandleWinUSBDriver();
35-
#else
36-
new rev::usb::SerialDriver();
37-
#endif
26+
#define REV_COMMON_HEARTBEAT_ID 0x00502C0
27+
#define SPARK_HEARTBEAT_ID 0x2052C80
28+
#define HEARTBEAT_PERIOD_MS 20
29+
30+
#define SPARK_HEARTBEAT_LENGTH 8
31+
#define REV_COMMON_HEARTBEAT_LENGTH 1
32+
uint8_t disabledSparkHeartbeat[] = {0, 0, 0, 0, 0, 0, 0, 0};
33+
uint8_t disabledRevCommonHeartbeat[] = {0};
34+
35+
rev::usb::CandleWinUSBDriver* driver = new rev::usb::CandleWinUSBDriver();
3836

3937
std::set<std::string> devicesRegisteredToHal; // TODO(Noah): Protect with mutex
4038
bool halInitialized = false;
4139
uint32_t m_notifier;
4240

4341
std::mutex canDevicesMtx;
42+
// These values should only be accessed while holding canDevicesMtx
4443
std::map<std::string, std::shared_ptr<rev::usb::CANDevice>> canDeviceMap;
4544

4645
std::mutex watchdogMtx;
46+
// These values should only be accessed while holding watchdogMtx
4747
std::vector<std::string> heartbeatsRunning;
48-
auto latestHeartbeatAck = std::chrono::system_clock::now();
48+
bool heartbeatTimeoutExpired = false; // Should only be changed in heartbeatsWatchdog()
49+
std::map<std::string, std::array<uint8_t, REV_COMMON_HEARTBEAT_LENGTH>> revCommonHeartbeatMap;
50+
std::map<std::string, std::array<uint8_t, SPARK_HEARTBEAT_LENGTH>> sparkHeartbeatMap;
51+
auto latestHeartbeatAck = std::chrono::steady_clock::now();
4952

5053
// Only call when holding canDevicesMtx
5154
void removeExtraDevicesFromDeviceMap(std::vector<std::string> descriptors) {
@@ -239,7 +242,7 @@ Napi::Object receiveMessage(const Napi::CallbackInfo& info) {
239242
size_t messageSize = message->GetSize();
240243
const uint8_t* messageData = message->GetData();
241244
Napi::Array napiMessage = Napi::Array::New(env, messageSize);
242-
for (size_t i = 0; i < messageSize; i++) {
245+
for (int i = 0; i < messageSize; i++) {
243246
napiMessage[i] = messageData[i];
244247
}
245248
Napi::Object messageInfo = Napi::Object::New(env);
@@ -332,7 +335,7 @@ Napi::Number openStreamSession(const Napi::CallbackInfo& info) {
332335
try {
333336
rev::usb::CANStatus status = device->OpenStreamSession(&sessionHandle, filter, maxSize);
334337
if (status != rev::usb::CANStatus::kOk) {
335-
Napi::Error::New(env, "Opening stream session failed with error code "+std::to_string((int)status)).ThrowAsJavaScriptException();
338+
Napi::Error::New(env, "Opening stream session failed with error code "+(int)status).ThrowAsJavaScriptException();
336339
} else {
337340
return Napi::Number::New(env, sessionHandle);
338341
}
@@ -629,6 +632,7 @@ void waitForNotifierAlarm(const Napi::CallbackInfo& info) {
629632
int32_t status;
630633

631634
HAL_UpdateNotifierAlarm(m_notifier, HAL_GetFPGATime(&status) + time, &status);
635+
// TODO(Noah): Don't discard the returned value (this function is marked as [nodiscard])
632636
HAL_WaitForNotifierAlarm(m_notifier, &status);
633637
cb.Call(info.Env().Global(), {info.Env().Null(), Napi::Number::New(info.Env(), status)});
634638
}
@@ -654,41 +658,70 @@ void writeDfuToBin(const Napi::CallbackInfo& info) {
654658
cb.Call(info.Env().Global(), {info.Env().Null(), Napi::Number::New(info.Env(), status)});
655659
}
656660

661+
void cleanupHeartbeatsRunning() {
662+
// Erase removed CAN buses from heartbeatsRunning
663+
std::scoped_lock lock{watchdogMtx, canDevicesMtx};
664+
for(int i = 0; i < heartbeatsRunning.size(); i++) {
665+
auto deviceIterator = canDeviceMap.find(heartbeatsRunning[i]);
666+
if (deviceIterator == canDeviceMap.end()) {
667+
heartbeatsRunning.erase(heartbeatsRunning.begin() + i);
668+
}
669+
}
670+
}
671+
657672
void heartbeatsWatchdog() {
658673
while (true) {
659-
std::this_thread::sleep_for (std::chrono::seconds(1));
660-
661-
{
662-
// Erase removed CAN buses from heartbeatsRunning
663-
std::scoped_lock lock{watchdogMtx, canDevicesMtx};
664-
for(size_t i = 0; i < heartbeatsRunning.size(); i++) {
665-
auto deviceIterator = canDeviceMap.find(heartbeatsRunning[i]);
666-
if (deviceIterator == canDeviceMap.end()) {
667-
heartbeatsRunning.erase(heartbeatsRunning.begin() + i);
668-
}
669-
}
670-
}
674+
std::this_thread::sleep_for (std::chrono::milliseconds(250));
675+
676+
cleanupHeartbeatsRunning();
671677

672678
std::scoped_lock lock{watchdogMtx};
673679

674680
if (heartbeatsRunning.size() < 1) { break; }
675681

676-
auto now = std::chrono::system_clock::now();
682+
auto now = std::chrono::steady_clock::now();
677683
std::chrono::duration<double> elapsed_seconds = now-latestHeartbeatAck;
678-
if (elapsed_seconds.count() > 1) {
679-
uint8_t sparkMaxHeartbeat[] = {0, 0, 0, 0, 0, 0, 0, 0};
680-
uint8_t revCommonHeartbeat[] = {0};
681-
for(size_t i = 0; i < heartbeatsRunning.size(); i++) {
682-
_sendCANMessage(heartbeatsRunning[i], 0x2052C80, sparkMaxHeartbeat, 8, -1);
683-
_sendCANMessage(heartbeatsRunning[i], 0x00502C0, revCommonHeartbeat, 1, -1);
684+
if (elapsed_seconds.count() >= 1 && !heartbeatTimeoutExpired) {
685+
// The heartbeat timeout just expired
686+
heartbeatTimeoutExpired = true;
687+
for(int i = 0; i < heartbeatsRunning.size(); i++) {
688+
if (sparkHeartbeatMap.contains(heartbeatsRunning[i])) {
689+
// Clear the scheduled heartbeat that has outdated data so that the updated one gets sent out immediately
690+
_sendCANMessage(heartbeatsRunning[i], SPARK_HEARTBEAT_ID, disabledSparkHeartbeat, SPARK_HEARTBEAT_LENGTH, -1);
691+
692+
_sendCANMessage(heartbeatsRunning[i], SPARK_HEARTBEAT_ID, disabledSparkHeartbeat, SPARK_HEARTBEAT_LENGTH, HEARTBEAT_PERIOD_MS);
693+
}
694+
if (revCommonHeartbeatMap.contains(heartbeatsRunning[i])) {
695+
// Clear the scheduled heartbeat that has outdated data so that the updated one gets sent out immediately
696+
_sendCANMessage(heartbeatsRunning[i], REV_COMMON_HEARTBEAT_ID, disabledRevCommonHeartbeat, REV_COMMON_HEARTBEAT_LENGTH, -1);
697+
698+
_sendCANMessage(heartbeatsRunning[i], REV_COMMON_HEARTBEAT_ID, disabledRevCommonHeartbeat, REV_COMMON_HEARTBEAT_LENGTH, HEARTBEAT_PERIOD_MS);
699+
}
700+
}
701+
} else if (elapsed_seconds.count() < 1 && heartbeatTimeoutExpired) {
702+
// The heartbeat timeout is newly un-expired
703+
heartbeatTimeoutExpired = false;
704+
for(int i = 0; i < heartbeatsRunning.size(); i++) {
705+
if (auto heartbeatEntry = sparkHeartbeatMap.find(heartbeatsRunning[i]); heartbeatEntry != sparkHeartbeatMap.end()) {
706+
// Clear the scheduled heartbeat that has outdated data so that the updated one gets sent out immediately
707+
_sendCANMessage(heartbeatsRunning[i], SPARK_HEARTBEAT_ID, heartbeatEntry->second.data(), SPARK_HEARTBEAT_LENGTH, -1);
708+
709+
_sendCANMessage(heartbeatsRunning[i], SPARK_HEARTBEAT_ID, heartbeatEntry->second.data(), SPARK_HEARTBEAT_LENGTH, HEARTBEAT_PERIOD_MS);
710+
}
711+
if (auto heartbeatEntry = revCommonHeartbeatMap.find(heartbeatsRunning[i]); heartbeatEntry != revCommonHeartbeatMap.end()) {
712+
// Clear the scheduled heartbeat that has outdated data so that the updated one gets sent out immediately
713+
_sendCANMessage(heartbeatsRunning[i], REV_COMMON_HEARTBEAT_ID, heartbeatEntry->second.data(), REV_COMMON_HEARTBEAT_LENGTH, -1);
714+
715+
_sendCANMessage(heartbeatsRunning[i], REV_COMMON_HEARTBEAT_ID, heartbeatEntry->second.data(), REV_COMMON_HEARTBEAT_LENGTH, HEARTBEAT_PERIOD_MS);
716+
}
684717
}
685718
}
686719
}
687720
}
688721

689722
void ackHeartbeats(const Napi::CallbackInfo& info) {
690723
std::scoped_lock lock{watchdogMtx};
691-
latestHeartbeatAck = std::chrono::system_clock::now();
724+
latestHeartbeatAck = std::chrono::steady_clock::now();
692725
}
693726

694727
// Params:
@@ -703,18 +736,23 @@ void startRevCommonHeartbeat(const Napi::CallbackInfo& info) {
703736
if (deviceIterator == canDeviceMap.end()) return;
704737
}
705738

706-
uint8_t payload[] = {1};
707-
_sendCANMessage(descriptor, 0x00502C0, payload, 1, 20);
739+
std::array<uint8_t, REV_COMMON_HEARTBEAT_LENGTH> payload = {1};
708740

709741
std::scoped_lock lock{watchdogMtx};
710742

743+
if (!heartbeatTimeoutExpired) {
744+
_sendCANMessage(descriptor, REV_COMMON_HEARTBEAT_ID, payload.data(), REV_COMMON_HEARTBEAT_LENGTH, HEARTBEAT_PERIOD_MS);
745+
}
746+
747+
revCommonHeartbeatMap[descriptor] = payload;
748+
711749
if (heartbeatsRunning.size() == 0) {
712750
heartbeatsRunning.push_back(descriptor);
713-
latestHeartbeatAck = std::chrono::system_clock::now();
751+
latestHeartbeatAck = std::chrono::steady_clock::now();
714752
std::thread hb(heartbeatsWatchdog);
715753
hb.detach();
716754
} else {
717-
for(size_t i = 0; i < heartbeatsRunning.size(); i++) {
755+
for(int i = 0; i < heartbeatsRunning.size(); i++) {
718756
if (heartbeatsRunning[i].compare(descriptor) == 0) return;
719757
}
720758
heartbeatsRunning.push_back(descriptor);
@@ -729,50 +767,53 @@ void setSparkMaxHeartbeatData(const Napi::CallbackInfo& info) {
729767
std::string descriptor = info[0].As<Napi::String>().Utf8Value();
730768
Napi::Array dataParam = info[1].As<Napi::Array>();
731769

732-
uint8_t heartbeat[] = {0, 0, 0, 0, 0, 0, 0, 0};
770+
std::array<uint8_t, SPARK_HEARTBEAT_LENGTH> heartbeat = {0, 0, 0, 0, 0, 0, 0, 0};
733771

734772
{
735773
std::scoped_lock lock{canDevicesMtx};
736774
auto deviceIterator = canDeviceMap.find(descriptor);
737775
if (deviceIterator == canDeviceMap.end()) return;
738776
}
739777

740-
_sendCANMessage(descriptor, 0x2052C80, heartbeat, 8, -1);
741-
std::this_thread::sleep_for(std::chrono::milliseconds(50));
742-
743778
int sum = 0;
744779
for (uint32_t i = 0; i < dataParam.Length(); i++) {
745780
heartbeat[i] = dataParam.Get(i).As<Napi::Number>().Uint32Value();
746781
sum+= heartbeat[i];
747782
}
748783

749-
if (sum == 0) {
750-
_sendCANMessage(descriptor, 0x2052C80, heartbeat, 8, -1);
784+
std::scoped_lock lock{watchdogMtx};
785+
786+
if (!heartbeatTimeoutExpired) {
787+
// Clear the scheduled heartbeat that has outdated data so that the updated one gets sent out immediately
788+
_sendCANMessage(descriptor, SPARK_HEARTBEAT_ID, heartbeat.data(), SPARK_HEARTBEAT_LENGTH, -1);
789+
790+
_sendCANMessage(descriptor, SPARK_HEARTBEAT_ID, heartbeat.data(), SPARK_HEARTBEAT_LENGTH, HEARTBEAT_PERIOD_MS);
751791
}
752-
else {
753-
_sendCANMessage(descriptor, 0x2052C80, heartbeat, 8, 10);
754792

755-
std::scoped_lock lock{watchdogMtx};
793+
sparkHeartbeatMap[descriptor] = heartbeat;
756794

757-
if (heartbeatsRunning.size() == 0) {
758-
heartbeatsRunning.push_back(descriptor);
759-
latestHeartbeatAck = std::chrono::system_clock::now();
760-
std::thread hb(heartbeatsWatchdog);
761-
hb.detach();
762-
} else {
763-
for(size_t i = 0; i < heartbeatsRunning.size(); i++) {
764-
if (heartbeatsRunning[i].compare(descriptor) == 0) return;
765-
}
766-
heartbeatsRunning.push_back(descriptor);
795+
if (heartbeatsRunning.size() == 0) {
796+
heartbeatsRunning.push_back(descriptor);
797+
latestHeartbeatAck = std::chrono::steady_clock::now();
798+
std::thread hb(heartbeatsWatchdog);
799+
hb.detach();
800+
} else {
801+
for(int i = 0; i < heartbeatsRunning.size(); i++) {
802+
if (heartbeatsRunning[i].compare(descriptor) == 0) return;
767803
}
804+
heartbeatsRunning.push_back(descriptor);
768805
}
769806
}
770807

771-
/**
772-
* This function was removed from commit b0ca096624286b1e975eaaa816e38599933b7e84, which broke MSVC builds.
773-
* It has been re-added here to fix the build.
774-
*/
775808
void stopHeartbeats(const Napi::CallbackInfo& info) {
776-
//! TODO: Reimplement this function with current codebase
777-
Napi::Env env = info.Env();
809+
std::string descriptor = info[0].As<Napi::String>().Utf8Value();
810+
bool sendDisabledHeartbeatsFirst = info[1].As<Napi::Boolean>().Value();
811+
812+
// 0 sends and then cancels, -1 cancels without sending
813+
const int repeatPeriod = sendDisabledHeartbeatsFirst ? 0 : -1;
814+
815+
std::scoped_lock lock{watchdogMtx};
816+
// Send disabled SPARK and REV common heartbeats and un-schedule them for the future
817+
_sendCANMessage(descriptor, SPARK_HEARTBEAT_ID, disabledSparkHeartbeat, SPARK_HEARTBEAT_LENGTH, repeatPeriod);
818+
_sendCANMessage(descriptor, REV_COMMON_HEARTBEAT_ID, disabledRevCommonHeartbeat, REV_COMMON_HEARTBEAT_LENGTH, repeatPeriod);
778819
}

0 commit comments

Comments
 (0)