Device: WiVI: Add VIN support

pull/76/merge
Emily Brooks 2025-10-08 19:49:03 +00:00 committed by Kyle Schwarz
parent ec350b522a
commit 99a2ca4f0d
5 changed files with 181 additions and 10 deletions

View File

@ -31,24 +31,46 @@ std::shared_ptr<WiVI::ResponseMessage> WiVI::CommandPacket::DecodeToMessage(cons
break; break;
} }
case WiVI::Command::GetAll: { case WiVI::Command::GetAll: {
if(bytestream.size() < sizeof(WiVI::CommandPacket::GetAll)) if(bytestream.size() < sizeof(WiVI::CommandPacket::GetAllHeader))
return {}; return {};
const auto& getAll = *reinterpret_cast<const WiVI::CommandPacket::GetAll*>(bytestream.data()); const auto& getAll = *reinterpret_cast<const WiVI::CommandPacket::GetAllHeader*>(bytestream.data());
msg->responseTo = WiVI::Command::GetAll; msg->responseTo = WiVI::Command::GetAll;
msg->info.emplace(); msg->info.emplace();
msg->info->sleepRequest = getAll.sleepRequest; msg->info->sleepRequest = getAll.sleepRequest;
msg->info->connectionTimeoutMinutes = getAll.connectionTimeoutMinutes; msg->info->connectionTimeoutMinutes = getAll.connectionTimeoutMinutes;
// Check that we have enough data for the capture infos // Check that we have enough data for the capture infos
if(bytestream.size() < sizeof(WiVI::CommandPacket::GetAll) + (sizeof(WiVI::CaptureInfo) * getAll.numCaptureInfos)) size_t captureInfosSize = sizeof(WiVI::CaptureInfo) * getAll.numCaptureInfos;
if(bytestream.size() < sizeof(WiVI::CommandPacket::GetAllHeader) + captureInfosSize)
return {}; return {};
const WiVI::CaptureInfo* const captureInfos = (const WiVI::CaptureInfo*)(bytestream.data() + sizeof(WiVI::CommandPacket::GetAllHeader));
msg->info->captures.resize(getAll.numCaptureInfos); msg->info->captures.resize(getAll.numCaptureInfos);
for(uint16_t i = 0; i < getAll.numCaptureInfos; i++) for(uint16_t i = 0; i < getAll.numCaptureInfos; i++)
msg->info->captures[i] = getAll.captureInfos[i]; msg->info->captures[i] = captureInfos[i];
// New field vinAvail was added - check if it is present:
if(bytestream.size() >= sizeof(WiVI::CommandPacket::GetAllHeader) + captureInfosSize + 2) {
msg->info->vinAvailable = *(bytestream.data() + sizeof(WiVI::CommandPacket::GetAllHeader) + captureInfosSize);
} else {
msg->info->vinAvailable = 0;
}
break; break;
} }
case WiVI::Command::GetVIN: {
if (bytestream.size() < sizeof(WiVI::CommandPacket::GetVIN))
return {};
const auto& getVIN = *reinterpret_cast<const WiVI::CommandPacket::GetVIN*>(bytestream.data());
msg->responseTo = WiVI::Command::GetVIN;
msg->vin = getVIN.VIN;
break;
}
default: // Unknown command response default: // Unknown command response
return {}; return {};
} }
@ -78,9 +100,9 @@ std::vector<uint8_t> WiVI::CommandPacket::SetSignal::Encode(WiVI::SignalType typ
return ret; return ret;
} }
std::vector<uint8_t> WiVI::CommandPacket::GetAll::Encode() { std::vector<uint8_t> WiVI::CommandPacket::GetAllHeader::Encode() {
std::vector<uint8_t> ret(sizeof(WiVI::CommandPacket::GetAll)); std::vector<uint8_t> ret(sizeof(WiVI::CommandPacket::GetAllHeader));
auto& frame = *reinterpret_cast<WiVI::CommandPacket::GetAll*>(ret.data()); auto& frame = *reinterpret_cast<WiVI::CommandPacket::GetAllHeader*>(ret.data());
frame.header.cmd = WiVI::Command::GetAll; frame.header.cmd = WiVI::Command::GetAll;
frame.header.length = sizeof(frame) - sizeof(frame.header); frame.header.length = sizeof(frame) - sizeof(frame.header);
@ -98,3 +120,15 @@ std::vector<uint8_t> WiVI::CommandPacket::ClearUploads::Encode(const std::vector
return ret; return ret;
} }
std::vector<uint8_t> WiVI::CommandPacket::GetVIN::Encode()
{
std::vector<uint8_t> ret(sizeof(WiVI::CommandPacket::GetVIN));
auto& frame = *reinterpret_cast<WiVI::CommandPacket::GetVIN*>(ret.data());
frame.header.cmd = WiVI::Command::GetVIN;
frame.header.length = sizeof(frame) - sizeof(frame.header);
return ret;
}

View File

@ -1178,7 +1178,7 @@ void Device::wiviThreadBody() {
// Use the command GetAll to get a WiVI::Info structure from the device // Use the command GetAll to get a WiVI::Info structure from the device
const auto generic = com->waitForMessageSync([this]() { const auto generic = com->waitForMessageSync([this]() {
return com->sendCommand(Command::WiVICommand, WiVI::CommandPacket::GetAll::Encode()); return com->sendCommand(Command::WiVICommand, WiVI::CommandPacket::GetAllHeader::Encode());
}, filter, std::chrono::milliseconds(1000)); }, filter, std::chrono::milliseconds(1000));
if(!generic || generic->type != Message::Type::WiVICommandResponse) { if(!generic || generic->type != Message::Type::WiVICommandResponse) {
@ -1290,6 +1290,22 @@ void Device::wiviThreadBody() {
for(auto& cb : sleepRequestedCallbacks) for(auto& cb : sleepRequestedCallbacks)
cb.second = false; cb.second = false;
} }
// Process vin available callbacks
if (resp->info->vinAvailable & 1) {
for (auto& cb : vinAvailableCallbacks) {
if (!cb.second && cb.first) {
cb.second = true;
lk.unlock();
try {
cb.first();
} catch(...) {
report(APIEvent::Type::Unknown, APIEvent::Severity::Error);
}
lk.lock();
}
}
}
} }
} }
@ -1464,6 +1480,110 @@ bool Device::allowSleep(bool remoteWakeup) {
return true; return true;
} }
Lifetime Device::addVINAvailableCallback(VINAvailableCallback cb)
{
if(!isOpen()) {
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
return {};
}
if(!supportsWiVI()) {
report(APIEvent::Type::WiVINotSupported, APIEvent::Severity::Error);
return {};
}
std::lock_guard<std::mutex> lk(wiviMutex);
if(!wiviThread.joinable()) {
// Start the thread
stopWiVIThread = false;
wiviThread = std::thread([this]() { wiviThreadBody(); });
}
size_t idx = 0;
for(; idx < vinAvailableCallbacks.size(); idx++) {
if(!vinAvailableCallbacks[idx].first) // Empty space (previously erased callback)
break;
}
if(idx == vinAvailableCallbacks.size()) // Create a new space
vinAvailableCallbacks.emplace_back(std::move(cb), false);
else
vinAvailableCallbacks[idx] = { std::move(cb), false };
// Cleanup function to remove this sleep requested callback
return Lifetime([this, idx]() {
// TODO: Hold a weak ptr to the `this` instead of relying on the user to keep `this` valid
std::unique_lock<std::mutex> lk2(wiviMutex);
vinAvailableCallbacks[idx].first = VINAvailableCallback();
stopWiVIThreadIfNecessary(std::move(lk2));
});
}
std::optional<bool> Device::isVINEnabled() const
{
if(!isOpen()) {
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
return std::nullopt;
}
if(!supportsWiVI()) {
report(APIEvent::Type::WiVINotSupported, APIEvent::Severity::Error);
return std::nullopt;
}
static std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Message::Type::WiVICommandResponse);
// Hold this lock so the WiVI stack doesn't issue a WiVICommand at the same time as us
std::lock_guard<std::mutex> lk(wiviMutex);
const auto generic = com->waitForMessageSync([this]() {
return com->sendCommand(Command::WiVICommand, WiVI::CommandPacket::GetSignal::Encode(WiVI::SignalType::VINEnabled));
}, filter, std::chrono::milliseconds(1000));
if(!generic || generic->type != Message::Type::WiVICommandResponse) {
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error);
return std::nullopt;
}
const auto resp = std::static_pointer_cast<WiVI::ResponseMessage>(generic);
if(!resp->success || !resp->value.has_value()) {
report(APIEvent::Type::ValueNotYetPresent, APIEvent::Severity::Error);
return std::nullopt;
}
return *resp->value;
}
std::optional<std::string> Device::getVIN() const
{
if(!isOpen()) {
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
return std::nullopt;
}
if(!supportsWiVI()) {
report(APIEvent::Type::WiVINotSupported, APIEvent::Severity::Error);
return std::nullopt;
}
static std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Message::Type::WiVICommandResponse);
std::lock_guard<std::mutex> lk(wiviMutex);
const auto generic = com->waitForMessageSync([this]() {
return com->sendCommand(Command::WiVICommand, WiVI::CommandPacket::GetVIN::Encode());
}, filter, std::chrono::milliseconds(1000));
if(!generic || generic->type != Message::Type::WiVICommandResponse) {
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error);
return std::nullopt;
}
const auto resp = std::static_pointer_cast<WiVI::ResponseMessage>(generic);
if(!resp->success || !resp->vin.has_value()) {
report(APIEvent::Type::ValueNotYetPresent, APIEvent::Severity::Error);
return std::nullopt;
}
return *resp->vin;
}
void Device::scriptStatusThreadBody() void Device::scriptStatusThreadBody()
{ {
std::unique_lock<std::mutex> lk(scriptStatusMutex); std::unique_lock<std::mutex> lk(scriptStatusMutex);

View File

@ -17,6 +17,7 @@ struct Info {
uint8_t sleepRequest; uint8_t sleepRequest;
uint16_t connectionTimeoutMinutes; uint16_t connectionTimeoutMinutes;
std::vector<CaptureInfo> captures; std::vector<CaptureInfo> captures;
uint8_t vinAvailable;
}; };
// The response for Command::WiVICommand // The response for Command::WiVICommand
@ -27,6 +28,7 @@ public:
std::optional<Command> responseTo; std::optional<Command> responseTo;
std::optional<int32_t> value; std::optional<int32_t> value;
std::optional<Info> info; std::optional<Info> info;
std::optional<std::string> vin;
}; };
} // namespace WiVI } // namespace WiVI

View File

@ -56,6 +56,7 @@ enum class Command : uint16_t {
GetSignal = 0x0013, GetSignal = 0x0013,
Result = 0x0014, Result = 0x0014,
GetPhysicalSignal = 0x0015, GetPhysicalSignal = 0x0015,
GetVIN = 0x0016,
}; };
enum class SignalType : uint16_t { // enumCoreMiniValueMiscValueType enum class SignalType : uint16_t { // enumCoreMiniValueMiscValueType
@ -66,6 +67,7 @@ enum class SignalType : uint16_t { // enumCoreMiniValueMiscValueType
ConnectionTimeout = 0x006e, ConnectionTimeout = 0x006e,
TimeSinceLastMessageMs = 0x006f, TimeSinceLastMessageMs = 0x006f,
UploadsPending = 0x0077, UploadsPending = 0x0077,
VINEnabled = 0x007D,
}; };
struct Upload { struct Upload {
@ -125,7 +127,7 @@ struct CommandPacket {
CoreMiniFixedPointValue value; CoreMiniFixedPointValue value;
}; };
struct GetAll { struct GetAllHeader {
static std::vector<uint8_t> Encode(); static std::vector<uint8_t> Encode();
Header header; Header header;
@ -133,7 +135,6 @@ struct CommandPacket {
uint8_t sleepRequest; uint8_t sleepRequest;
uint16_t connectionTimeoutMinutes; uint16_t connectionTimeoutMinutes;
uint16_t numCaptureInfos; uint16_t numCaptureInfos;
CaptureInfo captureInfos[0];
}; };
struct ClearUploads { struct ClearUploads {
@ -142,6 +143,13 @@ struct CommandPacket {
Header header; Header header;
uint8_t bitmask[0]; uint8_t bitmask[0];
}; };
struct GetVIN {
static std::vector<uint8_t> Encode();
Header header;
char VIN[17];
};
}; };
} // namespace WiVI } // namespace WiVI

View File

@ -568,6 +568,12 @@ public:
NODISCARD("If the Lifetime is not held, the callback will be immediately removed") NODISCARD("If the Lifetime is not held, the callback will be immediately removed")
Lifetime addLoggingCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::Logging, std::move(cb)); } Lifetime addLoggingCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::Logging, std::move(cb)); }
typedef std::function<void(void)> VINAvailableCallback;
NODISCARD("If the Lifetime is not held, the callback will be immediately removed")
Lifetime addVINAvailableCallback(VINAvailableCallback cb);
std::optional<bool> isVINEnabled() const;
std::optional<std::string> getVIN() const;
virtual std::vector<std::shared_ptr<FlexRay::Controller>> getFlexRayControllers() const { return {}; } virtual std::vector<std::shared_ptr<FlexRay::Controller>> getFlexRayControllers() const { return {}; }
void addExtension(std::shared_ptr<DeviceExtension>&& extension); void addExtension(std::shared_ptr<DeviceExtension>&& extension);
@ -931,6 +937,7 @@ private:
std::atomic<bool> wiviSleepRequested{false}; std::atomic<bool> wiviSleepRequested{false};
std::vector<NewCaptureCallback> newCaptureCallbacks; std::vector<NewCaptureCallback> newCaptureCallbacks;
std::vector< std::pair<SleepRequestedCallback, bool /* notified */> > sleepRequestedCallbacks; std::vector< std::pair<SleepRequestedCallback, bool /* notified */> > sleepRequestedCallbacks;
std::vector<std::pair<VINAvailableCallback, bool /* notified */>> vinAvailableCallbacks;
void wiviThreadBody(); void wiviThreadBody();
void stopWiVIThreadIfNecessary(std::unique_lock<std::mutex> lk); void stopWiVIThreadIfNecessary(std::unique_lock<std::mutex> lk);