blob: 09fb8c974e01b2296337ffff5fa70711fcfce90b [file] [log] [blame]
// Copyright 2024 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/sensors/playback/serialization.h"
#include <fcntl.h>
#include <lib/fdio/io.h>
#include <stdio.h>
#include "src/sensors/playback/proto/dataset.pb.h"
#include "src/sensors/playback/proto_conversion.h"
namespace sensors::playback::io {
namespace {
using fuchsia_sensors_types::SensorEvent;
using fuchsia_sensors_types::SensorId;
using fuchsia_sensors_types::SensorInfo;
using fuchsia_sensors_types::SensorReportingMode;
using fuchsia_sensors_types::SensorType;
using fuchsia_sensors_types::SensorWakeUpType;
using RecordedSensorEvent = sensors::playback::io::Deserializer::RecordedSensorEvent;
} // namespace
FileDescriptor::FileDescriptor(FileDescriptor&& other) { std::swap(value_, other.value_); }
FileDescriptor::~FileDescriptor() {
if (value_ >= 0) {
::close(value_);
}
}
FileDescriptor& FileDescriptor::operator=(FileDescriptor&& other) {
std::swap(value_, other.value_);
return *this;
}
zx_status_t Serializer::Open(const std::string& dataset_name,
const std::vector<SensorInfo>& sensor_list) {
FileDescriptor fd = ::open(file_path_.c_str(), O_WRONLY | O_CREAT);
if (fd < 0) {
FX_LOGS(ERROR) << "Serializer: Could not open file: " << file_path_ << ", error: " << errno;
return ZX_ERR_IO;
}
fuchsia::sensors::DatasetHeader header = CreateDatasetHeader(dataset_name, sensor_list);
std::string header_bytes;
if (!header.SerializeToString(&header_bytes)) {
FX_LOGS(ERROR) << "Serializer: Failed to serialize header.";
return ZX_ERR_BAD_STATE;
}
size_t header_size = header_bytes.size();
size_t bytes_written = ::write(fd, &header_size, sizeof(header_size));
if (bytes_written < 0) {
FX_LOGS(ERROR) << "Serializer: Failed to write header size with error: " << errno;
return ZX_ERR_IO;
} else if (bytes_written < sizeof(header_size)) {
FX_LOGS(ERROR) << "Serializer: Failed to write header size.";
return ZX_ERR_IO;
}
bytes_written = ::write(fd, header_bytes.data(), header_size);
if (bytes_written < 0) {
FX_LOGS(ERROR) << "Serializer: Failed to write header bytes with error: " << errno;
return ZX_ERR_IO;
} else if (bytes_written < header_size) {
FX_LOGS(ERROR) << "Serializer: Failed to write all header bytes.";
return ZX_ERR_IO;
}
fd_ = std::move(fd);
return ZX_OK;
}
zx_status_t Serializer::WriteEvent(const SensorEvent& event,
std::optional<int64_t> receipt_timestamp) {
if (!fd_) {
FX_LOGS(ERROR) << "Serializer: Attempt to write an event when the file is not open.";
return ZX_ERR_BAD_STATE;
}
fuchsia::sensors::RecordedSensorEvent proto_event;
*proto_event.mutable_event() = FidlToProtoSensorEvent(event);
if (receipt_timestamp)
proto_event.set_receipt_timestamp(*receipt_timestamp);
std::string event_bytes;
if (!proto_event.SerializeToString(&event_bytes)) {
FX_LOGS(ERROR) << "Serializer: Failed to serialize event.";
return ZX_ERR_BAD_STATE;
}
size_t event_size = event_bytes.size();
size_t bytes_written = ::write(*fd_, &event_size, sizeof(event_size));
if (bytes_written < 0) {
FX_LOGS(ERROR) << "Serializer: Failed to write event size with error: " << errno;
return ZX_ERR_IO;
} else if (bytes_written < sizeof(event_size)) {
FX_LOGS(ERROR) << "Serializer: Failed to write event size.";
return ZX_ERR_IO;
}
bytes_written = ::write(*fd_, event_bytes.data(), event_size);
if (bytes_written < 0) {
FX_LOGS(ERROR) << "Serializer: Failed to write event bytes with error: " << errno;
return ZX_ERR_IO;
} else if (bytes_written < event_size) {
FX_LOGS(ERROR) << "Serializer: Failed to write all event bytes.";
return ZX_ERR_IO;
}
return ZX_OK;
}
zx_status_t Deserializer::Open(std::string& dataset_name, std::vector<SensorInfo>& sensor_list) {
FileDescriptor fd = ::open(file_path_.c_str(), O_RDONLY);
if (fd < 0) {
FX_LOGS(ERROR) << "Deserializer: Could not open file: " << file_path_ << ", error: " << errno;
return ZX_ERR_IO;
}
size_t header_size;
size_t bytes_read = ::read(fd, &header_size, sizeof(header_size));
if (bytes_read < 0) {
FX_LOGS(ERROR) << "Deserializer: Failed to read header size with error: " << errno;
return ZX_ERR_IO;
} else if (bytes_read < sizeof(header_size)) {
FX_LOGS(ERROR) << "Deserializer: Failed to read header size.";
return ZX_ERR_BAD_STATE;
}
std::string header_bytes;
header_bytes.resize(header_size);
bytes_read = ::read(fd, header_bytes.data(), header_size);
if (bytes_read < 0) {
FX_LOGS(ERROR) << "Deserializer: Failed to read header bytes with error: " << errno;
return ZX_ERR_IO;
} else if (bytes_read < header_size) {
FX_LOGS(ERROR) << "Deserializer: Failed to read all header bytes.";
return ZX_ERR_BAD_STATE;
}
fuchsia::sensors::DatasetHeader header;
if (!header.ParseFromString(header_bytes)) {
FX_LOGS(ERROR) << "Deserializer: Failed to parse data file header.";
return ZX_ERR_BAD_STATE;
}
dataset_name = header.name();
for (const auto& header_sensor : header.sensors()) {
SensorInfo sensor;
ProtoToFidlSensorInfo(header_sensor, sensor);
sensor_list.push_back(sensor);
}
fd_ = std::move(fd);
header_size_ = header_size;
return ZX_OK;
}
fit::result<zx_status_t, RecordedSensorEvent> Deserializer::ReadEvent() {
if (!fd_) {
FX_LOGS(ERROR) << "Deserializer: Attempt to read an event when the file is not open.";
return fit::error(ZX_ERR_BAD_STATE);
}
size_t event_size;
size_t bytes_read = ::read(*fd_, &event_size, sizeof(event_size));
if (bytes_read == 0) {
// End of file, return "error" ZX_OK.
return fit::error(ZX_OK);
} else if (bytes_read < 0) {
FX_LOGS(ERROR) << "Deserializer: Failed to read event size with error: " << errno;
return fit::error(ZX_ERR_IO);
} else if (bytes_read < sizeof(event_size)) {
FX_LOGS(ERROR) << "Deserializer: Couldn't read complete event size.";
return fit::error(ZX_ERR_BAD_STATE);
}
std::string event_bytes;
event_bytes.resize(event_size);
bytes_read = ::read(*fd_, event_bytes.data(), event_size);
if (bytes_read == 0) {
// There shouldn't be an EOF after an event size.
FX_LOGS(ERROR) << "Deserializer: Encountered EOF after reading event size.";
return fit::error(ZX_ERR_BAD_STATE);
} else if (bytes_read < 0) {
FX_LOGS(ERROR) << "Deserializer: Failed to read event with error: " << errno;
return fit::error(ZX_ERR_IO);
} else if (bytes_read < event_size) {
FX_LOGS(ERROR) << "Deserializer: Failed to read all event bytes.";
return fit::error(ZX_ERR_BAD_STATE);
}
fuchsia::sensors::RecordedSensorEvent proto_event;
if (!proto_event.ParseFromString(event_bytes)) {
FX_LOGS(ERROR) << "Deserializer: Failed to parse a sensor event.";
return fit::error(ZX_ERR_BAD_STATE);
}
std::optional<int64_t> receipt_timestamp;
if (proto_event.has_receipt_timestamp())
receipt_timestamp = proto_event.receipt_timestamp();
return fit::ok(
RecordedSensorEvent{ProtoToFidlSensorEvent(proto_event.event()), receipt_timestamp});
}
zx_status_t Deserializer::SeekToFirstEvent() {
if (!fd_) {
FX_LOGS(ERROR) << "Deserializer: Attempt to seek when the file is not open.";
return ZX_ERR_BAD_STATE;
}
ssize_t desired_offset = sizeof(header_size_) + header_size_;
off_t new_offset = lseek(*fd_, desired_offset, SEEK_SET);
if (new_offset < 0) {
FX_LOGS(ERROR) << "Deserializer: Seek failed with error: " << errno;
return ZX_ERR_IO;
}
return new_offset == desired_offset ? ZX_OK : ZX_ERR_BAD_STATE;
}
} // namespace sensors::playback::io