From 1f79afe22d0803d6b18145e2fe7f4443a0259368 Mon Sep 17 00:00:00 2001 From: NoahAndrews <10224994+NoahAndrews@users.noreply.github.com.> Date: Wed, 6 Nov 2024 19:23:47 -0600 Subject: [PATCH 1/3] Add getTimestampsForAllReceivedMessages() function --- lib/binding.ts | 27 +++++--------------------- scripts/download-CanBridge.mjs | 2 +- src/addon.cc | 2 ++ src/canWrapper.cc | 35 ++++++++++++++++++++++++++++++++++ src/canWrapper.h | 1 + 5 files changed, 44 insertions(+), 23 deletions(-) diff --git a/lib/binding.ts b/lib/binding.ts index 56de42c..dd7e912 100644 --- a/lib/binding.ts +++ b/lib/binding.ts @@ -74,6 +74,10 @@ export class CanBridge { startRevCommonHeartbeat: (descriptor: string) => void; stopHeartbeats: (descriptor: string, sendDisabledHeartbeatsFirst: boolean) => void; ackHeartbeats: () => void; + /** + * @return Object that maps arbitration IDs to the timestamp a message with that ID was last received at + */ + getTimestampsForAllReceivedMessages: (descriptor: string) => Record; constructor() { try { @@ -103,30 +107,9 @@ export class CanBridge { this.startRevCommonHeartbeat = addon.startRevCommonHeartbeat; this.ackHeartbeats = addon.ackHeartbeats; this.stopHeartbeats = addon.stopHeartbeats; + this.getTimestampsForAllReceivedMessages = addon.getTimestampsForAllReceivedMessages; } catch (e: any) { throw new CanBridgeInitializationError(e); } } } - - - - - - - - - - - - - - - - - - - - - - diff --git a/scripts/download-CanBridge.mjs b/scripts/download-CanBridge.mjs index c26898a..7530a91 100644 --- a/scripts/download-CanBridge.mjs +++ b/scripts/download-CanBridge.mjs @@ -3,7 +3,7 @@ import * as path from "path"; import axios from 'axios'; import AdmZip from 'adm-zip'; -const canBridgeTag = "v2.5.1"; +const canBridgeTag = "v2.6.0"; const canBridgeReleaseAssetUrlPrefix = `https://github.com/REVrobotics/CANBridge/releases/download/${canBridgeTag}`; const externalCompileTimeDepsPath = 'externalCompileTimeDeps'; diff --git a/src/addon.cc b/src/addon.cc index 10bc0bf..6ecc3e0 100644 --- a/src/addon.cc +++ b/src/addon.cc @@ -50,6 +50,8 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) { Napi::Function::New(env, stopHeartbeats)); exports.Set(Napi::String::New(env, "ackHeartbeats"), Napi::Function::New(env, ackHeartbeats)); + exports.Set(Napi::String::New(env, "getTimestampsForAllReceivedMessages"), + Napi::Function::New(env, getTimestampsForAllReceivedMessages)); return exports; } diff --git a/src/canWrapper.cc b/src/canWrapper.cc index c8fb1ae..6c75d50 100644 --- a/src/canWrapper.cc +++ b/src/canWrapper.cc @@ -724,6 +724,41 @@ Napi::Array getImageElements(const Napi::CallbackInfo& info) { return elements; } +Napi::Object getTimestampsForAllReceivedMessages(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + std::string descriptor = info[0].As().Utf8Value(); + + std::shared_ptr device; + + { // This block exists to define how long we hold canDevicesMtx + std::scoped_lock lock{canDevicesMtx}; + auto deviceIterator = canDeviceMap.find(descriptor); + if (deviceIterator == canDeviceMap.end()) { + if (devicesRegisteredToHal.find(descriptor) != devicesRegisteredToHal.end()) return receiveHalMessage(info); + Napi::Error::New(env, DEVICE_NOT_FOUND_ERROR).ThrowAsJavaScriptException(); + return Napi::Object::New(env); + } + device = deviceIterator->second; + } + + std::map> messages; + bool success = device->CopyReceivedMessagesMap(messages); + if (!success) { + Napi::Error::New(env, "Failed to copy the map of received messages").ThrowAsJavaScriptException(); + return Napi::Object::New(env); + } + + Napi::Object result = Napi::Object::New(env); + for (auto& m: messages) { + uint32_t arbId = m.first; + // GetTimestampUs() actually returns timestamps in milliseconds + uint32_t timestampMs = m.second->GetTimestampUs(); + result.Set(arbId, timestampMs); + } + + return result; +} + void cleanupHeartbeatsRunning() { // Erase removed CAN buses from heartbeatsRunning std::scoped_lock lock{watchdogMtx, canDevicesMtx}; diff --git a/src/canWrapper.h b/src/canWrapper.h index 6629980..6de1b79 100644 --- a/src/canWrapper.h +++ b/src/canWrapper.h @@ -28,4 +28,5 @@ void setSparkMaxHeartbeatData(const Napi::CallbackInfo& info); void startRevCommonHeartbeat(const Napi::CallbackInfo& info); void stopHeartbeats(const Napi::CallbackInfo& info); void ackHeartbeats(const Napi::CallbackInfo& info); +Napi::Object getTimestampsForAllReceivedMessages(const Napi::CallbackInfo& info); #endif From f98f27759d72685d1cbd625348bc51db1339e009 Mon Sep 17 00:00:00 2001 From: NoahAndrews <10224994+NoahAndrews@users.noreply.github.com.> Date: Thu, 7 Nov 2024 19:23:02 -0600 Subject: [PATCH 2/3] Replace previous function with getLatestMessageOfEveryReceivedArbId() --- lib/binding.ts | 6 +++--- src/addon.cc | 4 ++-- src/canWrapper.cc | 18 ++++++++++++++---- src/canWrapper.h | 2 +- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/lib/binding.ts b/lib/binding.ts index dd7e912..1910844 100644 --- a/lib/binding.ts +++ b/lib/binding.ts @@ -75,9 +75,9 @@ export class CanBridge { stopHeartbeats: (descriptor: string, sendDisabledHeartbeatsFirst: boolean) => void; ackHeartbeats: () => void; /** - * @return Object that maps arbitration IDs to the timestamp a message with that ID was last received at + * @return Object that maps arbitration IDs to the last-received message with that ID */ - getTimestampsForAllReceivedMessages: (descriptor: string) => Record; + getLatestMessageOfEveryReceivedArbId: (descriptor: string) => Record; constructor() { try { @@ -107,7 +107,7 @@ export class CanBridge { this.startRevCommonHeartbeat = addon.startRevCommonHeartbeat; this.ackHeartbeats = addon.ackHeartbeats; this.stopHeartbeats = addon.stopHeartbeats; - this.getTimestampsForAllReceivedMessages = addon.getTimestampsForAllReceivedMessages; + this.getLatestMessageOfEveryReceivedArbId = addon.getLatestMessageOfEveryReceivedArbId; } catch (e: any) { throw new CanBridgeInitializationError(e); } diff --git a/src/addon.cc b/src/addon.cc index 6ecc3e0..296db51 100644 --- a/src/addon.cc +++ b/src/addon.cc @@ -50,8 +50,8 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) { Napi::Function::New(env, stopHeartbeats)); exports.Set(Napi::String::New(env, "ackHeartbeats"), Napi::Function::New(env, ackHeartbeats)); - exports.Set(Napi::String::New(env, "getTimestampsForAllReceivedMessages"), - Napi::Function::New(env, getTimestampsForAllReceivedMessages)); + exports.Set(Napi::String::New(env, "getLatestMessageOfEveryReceivedArbId"), + Napi::Function::New(env, getLatestMessageOfEveryReceivedArbId)); return exports; } diff --git a/src/canWrapper.cc b/src/canWrapper.cc index 6c75d50..f7cc7a9 100644 --- a/src/canWrapper.cc +++ b/src/canWrapper.cc @@ -724,7 +724,7 @@ Napi::Array getImageElements(const Napi::CallbackInfo& info) { return elements; } -Napi::Object getTimestampsForAllReceivedMessages(const Napi::CallbackInfo& info) { +Napi::Object getLatestMessageOfEveryReceivedArbId(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); std::string descriptor = info[0].As().Utf8Value(); @@ -751,9 +751,19 @@ Napi::Object getTimestampsForAllReceivedMessages(const Napi::CallbackInfo& info) Napi::Object result = Napi::Object::New(env); for (auto& m: messages) { uint32_t arbId = m.first; - // GetTimestampUs() actually returns timestamps in milliseconds - uint32_t timestampMs = m.second->GetTimestampUs(); - result.Set(arbId, timestampMs); + auto message = m.second; + + size_t messageSize = message->GetSize(); + const uint8_t* messageData = message->GetData(); + Napi::Array napiMessage = Napi::Array::New(env, messageSize); + for (int i = 0; i < messageSize; i++) { + napiMessage[i] = messageData[i]; + } + Napi::Object messageInfo = Napi::Object::New(env); + messageInfo.Set("messageID", message->GetMessageId()); + messageInfo.Set("timeStamp", message->GetTimestampUs()); + messageInfo.Set("data", napiMessage); + result.Set(arbId, messageInfo); } return result; diff --git a/src/canWrapper.h b/src/canWrapper.h index 6de1b79..ced4460 100644 --- a/src/canWrapper.h +++ b/src/canWrapper.h @@ -28,5 +28,5 @@ void setSparkMaxHeartbeatData(const Napi::CallbackInfo& info); void startRevCommonHeartbeat(const Napi::CallbackInfo& info); void stopHeartbeats(const Napi::CallbackInfo& info); void ackHeartbeats(const Napi::CallbackInfo& info); -Napi::Object getTimestampsForAllReceivedMessages(const Napi::CallbackInfo& info); +Napi::Object getLatestMessageOfEveryReceivedArbId(const Napi::CallbackInfo& info); #endif From 11ed247baa045d845d2adfb6ef9648b70e647827 Mon Sep 17 00:00:00 2001 From: NoahAndrews <10224994+NoahAndrews@users.noreply.github.com.> Date: Mon, 11 Nov 2024 11:24:56 -0600 Subject: [PATCH 3/3] Add maxAgeMs parameter to getLatestMessageOfEveryReceivedArbId() This was necessary to achieve acceptable performance --- lib/binding.ts | 2 +- src/canWrapper.cc | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/binding.ts b/lib/binding.ts index 1910844..5a710f2 100644 --- a/lib/binding.ts +++ b/lib/binding.ts @@ -77,7 +77,7 @@ export class CanBridge { /** * @return Object that maps arbitration IDs to the last-received message with that ID */ - getLatestMessageOfEveryReceivedArbId: (descriptor: string) => Record; + getLatestMessageOfEveryReceivedArbId: (descriptor: string, maxAgeMs: number) => Record; constructor() { try { diff --git a/src/canWrapper.cc b/src/canWrapper.cc index f7cc7a9..6209652 100644 --- a/src/canWrapper.cc +++ b/src/canWrapper.cc @@ -727,6 +727,7 @@ Napi::Array getImageElements(const Napi::CallbackInfo& info) { Napi::Object getLatestMessageOfEveryReceivedArbId(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); std::string descriptor = info[0].As().Utf8Value(); + uint32_t maxAgeMs = info[1].As().Uint32Value(); std::shared_ptr device; @@ -748,10 +749,18 @@ Napi::Object getLatestMessageOfEveryReceivedArbId(const Napi::CallbackInfo& info return Napi::Object::New(env); } + // TODO(Harper): Use HAL clock + const auto nowMs = std::chrono::time_point_cast(std::chrono::steady_clock::now()).time_since_epoch().count(); + Napi::Object result = Napi::Object::New(env); for (auto& m: messages) { uint32_t arbId = m.first; auto message = m.second; + uint32_t timestampMs = message->GetTimestampUs(); + + if (nowMs - timestampMs > maxAgeMs) { + continue; + } size_t messageSize = message->GetSize(); const uint8_t* messageData = message->GetData(); @@ -761,7 +770,7 @@ Napi::Object getLatestMessageOfEveryReceivedArbId(const Napi::CallbackInfo& info } Napi::Object messageInfo = Napi::Object::New(env); messageInfo.Set("messageID", message->GetMessageId()); - messageInfo.Set("timeStamp", message->GetTimestampUs()); + messageInfo.Set("timeStamp", timestampMs); messageInfo.Set("data", napiMessage); result.Set(arbId, messageInfo); }