12
12
#include < thread>
13
13
#include < chrono>
14
14
#include < map>
15
+ #include < array>
15
16
#include < vector>
16
17
#include < set>
17
18
#include < exception>
20
21
#include " canWrapper.h"
21
22
#include " DfuSeFile.h"
22
23
23
- #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()"
25
+
26
+ #define REV_COMMON_HEARTBEAT_ID 0x00502C0
27
+ #define SPARK_HEARTBEAT_ID 0x2052C80
28
+ #define HEARTBEAT_PERIOD_MS 20
24
29
25
30
rev::usb::CandleWinUSBDriver* driver = new rev::usb::CandleWinUSBDriver();
26
31
@@ -29,11 +34,16 @@ bool halInitialized = false;
29
34
uint32_t m_notifier;
30
35
31
36
std::mutex canDevicesMtx;
37
+ // These values should only be accessed while holding canDevicesMtx
32
38
std::map<std::string, std::shared_ptr<rev::usb::CANDevice>> canDeviceMap;
33
39
34
40
std::mutex watchdogMtx;
41
+ // These values should only be accessed while holding watchdogMtx
35
42
std::vector<std::string> heartbeatsRunning;
36
- auto latestHeartbeatAck = std::chrono::system_clock::now();
43
+ bool heartbeatTimeoutExpired = false ; // Should only be changed in heartbeatsWatchdog()
44
+ std::map<std::string, std::array<uint8_t , 1 >> revCommonHeartbeatMap;
45
+ std::map<std::string, std::array<uint8_t , 8 >> sparkHeartbeatMap;
46
+ auto latestHeartbeatAck = std::chrono::steady_clock::now();
37
47
38
48
// Only call when holding canDevicesMtx
39
49
void removeExtraDevicesFromDeviceMap (std::vector<std::string> descriptors) {
@@ -617,6 +627,7 @@ void waitForNotifierAlarm(const Napi::CallbackInfo& info) {
617
627
int32_t status;
618
628
619
629
HAL_UpdateNotifierAlarm (m_notifier, HAL_GetFPGATime (&status) + time, &status);
630
+ // TODO(Noah): Don't discard the returned value (this function is marked as [nodiscard])
620
631
HAL_WaitForNotifierAlarm (m_notifier, &status);
621
632
cb.Call (info.Env ().Global (), {info.Env ().Null (), Napi::Number::New (info.Env (), status)});
622
633
}
@@ -644,7 +655,7 @@ void writeDfuToBin(const Napi::CallbackInfo& info) {
644
655
645
656
void heartbeatsWatchdog () {
646
657
while (true ) {
647
- std::this_thread::sleep_for (std::chrono::seconds ( 1 ));
658
+ std::this_thread::sleep_for (std::chrono::milliseconds ( 250 ));
648
659
649
660
{
650
661
// Erase removed CAN buses from heartbeatsRunning
@@ -661,22 +672,51 @@ void heartbeatsWatchdog() {
661
672
662
673
if (heartbeatsRunning.size () < 1 ) { break ; }
663
674
664
- auto now = std::chrono::system_clock ::now ();
675
+ auto now = std::chrono::steady_clock ::now ();
665
676
std::chrono::duration<double > elapsed_seconds = now-latestHeartbeatAck;
666
- if (elapsed_seconds.count () > 1 ) {
667
- uint8_t sparkMaxHeartbeat[] = {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 };
668
- uint8_t revCommonHeartbeat[] = {0 };
677
+ if (elapsed_seconds.count () >= 1 && !heartbeatTimeoutExpired) {
678
+ // The heartbeat timeout just expired
679
+ heartbeatTimeoutExpired = true ;
680
+ uint8_t disabledSparkHeartbeat[] = {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 };
681
+ uint8_t disabledRevCommonHeartbeat[] = {0 };
669
682
for (int i = 0 ; i < heartbeatsRunning.size (); i++) {
670
- _sendCANMessage (heartbeatsRunning[i], 0x2052C80 , sparkMaxHeartbeat, 8 , -1 );
671
- _sendCANMessage (heartbeatsRunning[i], 0x00502C0 , revCommonHeartbeat, 1 , -1 );
683
+ if (sparkHeartbeatMap.contains (heartbeatsRunning[i])) {
684
+ // Clear the scheduled heartbeat that has outdated data so that the updated one gets sent out immediately
685
+ _sendCANMessage (descriptor, SPARK_HEARTBEAT_ID, disabledSparkHeartbeat, 8 , -1 );
686
+
687
+ _sendCANMessage (heartbeatsRunning[i], SPARK_HEARTBEAT_ID, disabledSparkHeartbeat, 8 , HEARTBEAT_PERIOD_MS);
688
+ }
689
+ if (revCommonHeartbeatMap.contains (heartbeatsRunning[i])) {
690
+ // Clear the scheduled heartbeat that has outdated data so that the updated one gets sent out immediately
691
+ _sendCANMessage (descriptor, REV_COMMON_HEARTBEAT_ID, disabledRevCommonHeartbeat, 1 , -1 );
692
+
693
+ _sendCANMessage (heartbeatsRunning[i], REV_COMMON_HEARTBEAT_ID, disabledRevCommonHeartbeat, 1 , HEARTBEAT_PERIOD_MS);
694
+ }
695
+ }
696
+ } else if (elapsed_seconds.count () < 1 && heartbeatTimeoutExpired) {
697
+ // The heartbeat timeout is newly un-expired
698
+ heartbeatTimeoutExpired = false ;
699
+ for (int i = 0 ; i < heartbeatsRunning.size (); i++) {
700
+ if (auto heartbeatEntry = sparkHeartbeatMap.find (heartbeatsRunning[i]); heartbeatEntry != sparkHeartbeatMap.end ()) {
701
+ // Clear the scheduled heartbeat that has outdated data so that the updated one gets sent out immediately
702
+ _sendCANMessage (descriptor, SPARK_HEARTBEAT_ID, heartbeatEntry->second .data (), 8 , -1 );
703
+
704
+ _sendCANMessage (heartbeatsRunning[i], SPARK_HEARTBEAT_ID, heartbeatEntry->second .data (), 8 , HEARTBEAT_PERIOD_MS);
705
+ }
706
+ if (auto heartbeatEntry = revCommonHeartbeatMap.find (heartbeatsRunning[i]); heartbeatEntry != revCommonHeartbeatMap.end ()) {
707
+ // Clear the scheduled heartbeat that has outdated data so that the updated one gets sent out immediately
708
+ _sendCANMessage (descriptor, REV_COMMON_HEARTBEAT_ID, heartbeatEntry->second .data (), 1 , -1 );
709
+
710
+ _sendCANMessage (heartbeatsRunning[i], REV_COMMON_HEARTBEAT_ID, heartbeatEntry->second .data (), 1 , HEARTBEAT_PERIOD_MS);
711
+ }
672
712
}
673
713
}
674
714
}
675
715
}
676
716
677
717
void ackHeartbeats (const Napi::CallbackInfo& info) {
678
718
std::scoped_lock lock{watchdogMtx};
679
- latestHeartbeatAck = std::chrono::system_clock ::now ();
719
+ latestHeartbeatAck = std::chrono::steady_clock ::now ();
680
720
}
681
721
682
722
// Params:
@@ -691,14 +731,19 @@ void startRevCommonHeartbeat(const Napi::CallbackInfo& info) {
691
731
if (deviceIterator == canDeviceMap.end ()) return ;
692
732
}
693
733
694
- uint8_t payload[] = {1 };
695
- _sendCANMessage (descriptor, 0x00502C0 , payload, 1 , 20 );
734
+ std::array<uint8_t , 1 > payload = {1 };
696
735
697
736
std::scoped_lock lock{watchdogMtx};
698
737
738
+ if (!heartbeatTimeoutExpired) {
739
+ _sendCANMessage (descriptor, REV_COMMON_HEARTBEAT_ID, payload.data (), 1 , HEARTBEAT_PERIOD_MS);
740
+ }
741
+
742
+ revCommonHeartbeatMap[descriptor] = payload;
743
+
699
744
if (heartbeatsRunning.size () == 0 ) {
700
745
heartbeatsRunning.push_back (descriptor);
701
- latestHeartbeatAck = std::chrono::system_clock ::now ();
746
+ latestHeartbeatAck = std::chrono::steady_clock ::now ();
702
747
std::thread hb (heartbeatsWatchdog);
703
748
hb.detach ();
704
749
} else {
@@ -717,41 +762,40 @@ void setSparkMaxHeartbeatData(const Napi::CallbackInfo& info) {
717
762
std::string descriptor = info[0 ].As <Napi::String>().Utf8Value ();
718
763
Napi::Array dataParam = info[1 ].As <Napi::Array>();
719
764
720
- uint8_t heartbeat[] = {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 };
765
+ std::array< uint8_t , 8 > heartbeat = {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 };
721
766
722
767
{
723
768
std::scoped_lock lock{canDevicesMtx};
724
769
auto deviceIterator = canDeviceMap.find (descriptor);
725
770
if (deviceIterator == canDeviceMap.end ()) return ;
726
771
}
727
772
728
- _sendCANMessage (descriptor, 0x2052C80 , heartbeat, 8 , -1 );
729
- std::this_thread::sleep_for (std::chrono::milliseconds (50 ));
730
-
731
773
int sum = 0 ;
732
774
for (uint32_t i = 0 ; i < dataParam.Length (); i++) {
733
775
heartbeat[i] = dataParam.Get (i).As <Napi::Number>().Uint32Value ();
734
776
sum+= heartbeat[i];
735
777
}
736
778
737
- if (sum == 0 ) {
738
- _sendCANMessage (descriptor, 0x2052C80 , heartbeat, 8 , -1 );
779
+ std::scoped_lock lock{watchdogMtx};
780
+
781
+ if (!heartbeatTimeoutExpired) {
782
+ // Clear the scheduled heartbeat that has outdated data so that the updated one gets sent out immediately
783
+ _sendCANMessage (descriptor, SPARK_HEARTBEAT_ID, heartbeat.data (), 8 , -1 );
784
+
785
+ _sendCANMessage (descriptor, SPARK_HEARTBEAT_ID, heartbeat.data (), 8 , HEARTBEAT_PERIOD_MS);
739
786
}
740
- else {
741
- _sendCANMessage (descriptor, 0x2052C80 , heartbeat, 8 , 10 );
742
787
743
- std::scoped_lock lock{watchdogMtx} ;
788
+ sparkHeartbeatMap[descriptor] = heartbeat ;
744
789
745
- if (heartbeatsRunning.size () == 0 ) {
746
- heartbeatsRunning.push_back (descriptor);
747
- latestHeartbeatAck = std::chrono::system_clock::now ();
748
- std::thread hb (heartbeatsWatchdog);
749
- hb.detach ();
750
- } else {
751
- for (int i = 0 ; i < heartbeatsRunning.size (); i++) {
752
- if (heartbeatsRunning[i].compare (descriptor) == 0 ) return ;
753
- }
754
- heartbeatsRunning.push_back (descriptor);
790
+ if (heartbeatsRunning.size () == 0 ) {
791
+ heartbeatsRunning.push_back (descriptor);
792
+ latestHeartbeatAck = std::chrono::steady_clock::now ();
793
+ std::thread hb (heartbeatsWatchdog);
794
+ hb.detach ();
795
+ } else {
796
+ for (int i = 0 ; i < heartbeatsRunning.size (); i++) {
797
+ if (heartbeatsRunning[i].compare (descriptor) == 0 ) return ;
755
798
}
799
+ heartbeatsRunning.push_back (descriptor);
756
800
}
757
801
}
0 commit comments