mirror of
https://github.com/scottlamb/moonfire-nvr.git
synced 2024-12-27 15:45:55 -05:00
Add helper for adjusting date-to-duration map.
The helper isn't used yet. The goal is to export this on /camera/<uuid>/ as described in a TODO in design/api.md. The next step is to keep MoonfireDatabase::CameraData::days up-to-date: * Init: call on every recording (replacing the current aggregated query with a recording-by-recording query) * InsertRecording, DeleteRecordings: call for added/removed recordings then return it from GetCamera and pass it along to the client in WebInterface::HandleJsonCameraDetail.
This commit is contained in:
parent
a7bfb00083
commit
292bcbaad5
@ -28,6 +28,8 @@
|
|||||||
//
|
//
|
||||||
// moonfire-db-test.cc: tests of the moonfire-db.h interface.
|
// moonfire-db-test.cc: tests of the moonfire-db.h interface.
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <gflags/gflags.h>
|
#include <gflags/gflags.h>
|
||||||
@ -232,6 +234,63 @@ class MoonfireDbTest : public testing::Test {
|
|||||||
std::unique_ptr<MoonfireDatabase> mdb_;
|
std::unique_ptr<MoonfireDatabase> mdb_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TEST(AdjustDaysMapTest, Basic) {
|
||||||
|
std::map<std::string, int64_t> days;
|
||||||
|
|
||||||
|
// Create a day.
|
||||||
|
const int64_t kTestTime = INT64_C(130647162600000); // 2015-12-31 23:59:00
|
||||||
|
moonfire_nvr::internal::AdjustDaysMap(
|
||||||
|
kTestTime, kTestTime + 60 * kTimeUnitsPerSecond, 1, &days);
|
||||||
|
EXPECT_THAT(days, testing::ElementsAre(std::make_pair(
|
||||||
|
"2015-12-31", 60 * kTimeUnitsPerSecond)));
|
||||||
|
|
||||||
|
// Add to a day.
|
||||||
|
moonfire_nvr::internal::AdjustDaysMap(
|
||||||
|
kTestTime, kTestTime + 60 * kTimeUnitsPerSecond, 1, &days);
|
||||||
|
EXPECT_THAT(days, testing::ElementsAre(std::make_pair(
|
||||||
|
"2015-12-31", 120 * kTimeUnitsPerSecond)));
|
||||||
|
|
||||||
|
// Subtract from a day.
|
||||||
|
moonfire_nvr::internal::AdjustDaysMap(
|
||||||
|
kTestTime, kTestTime + 60 * kTimeUnitsPerSecond, -1, &days);
|
||||||
|
EXPECT_THAT(days, testing::ElementsAre(std::make_pair(
|
||||||
|
"2015-12-31", 60 * kTimeUnitsPerSecond)));
|
||||||
|
|
||||||
|
// Remove a day.
|
||||||
|
moonfire_nvr::internal::AdjustDaysMap(
|
||||||
|
kTestTime, kTestTime + 60 * kTimeUnitsPerSecond, -1, &days);
|
||||||
|
EXPECT_THAT(days, testing::ElementsAre());
|
||||||
|
|
||||||
|
// Create two days.
|
||||||
|
moonfire_nvr::internal::AdjustDaysMap(
|
||||||
|
kTestTime, kTestTime + 3 * 60 * kTimeUnitsPerSecond, 1, &days);
|
||||||
|
EXPECT_THAT(days,
|
||||||
|
testing::ElementsAre(
|
||||||
|
std::make_pair("2015-12-31", 1 * 60 * kTimeUnitsPerSecond),
|
||||||
|
std::make_pair("2016-01-01", 2 * 60 * kTimeUnitsPerSecond)));
|
||||||
|
|
||||||
|
// Add to two days.
|
||||||
|
moonfire_nvr::internal::AdjustDaysMap(
|
||||||
|
kTestTime, kTestTime + 3 * 60 * kTimeUnitsPerSecond, 1, &days);
|
||||||
|
EXPECT_THAT(days,
|
||||||
|
testing::ElementsAre(
|
||||||
|
std::make_pair("2015-12-31", 2 * 60 * kTimeUnitsPerSecond),
|
||||||
|
std::make_pair("2016-01-01", 4 * 60 * kTimeUnitsPerSecond)));
|
||||||
|
|
||||||
|
// Subtract from two days.
|
||||||
|
moonfire_nvr::internal::AdjustDaysMap(
|
||||||
|
kTestTime, kTestTime + 3 * 60 * kTimeUnitsPerSecond, -1, &days);
|
||||||
|
EXPECT_THAT(days,
|
||||||
|
testing::ElementsAre(
|
||||||
|
std::make_pair("2015-12-31", 1 * 60 * kTimeUnitsPerSecond),
|
||||||
|
std::make_pair("2016-01-01", 2 * 60 * kTimeUnitsPerSecond)));
|
||||||
|
|
||||||
|
// Remove two days.
|
||||||
|
moonfire_nvr::internal::AdjustDaysMap(
|
||||||
|
kTestTime, kTestTime + 3 * 60 * kTimeUnitsPerSecond, -1, &days);
|
||||||
|
EXPECT_THAT(days, testing::ElementsAre());
|
||||||
|
}
|
||||||
|
|
||||||
// Basic test of running some queries on an empty database.
|
// Basic test of running some queries on an empty database.
|
||||||
TEST_F(MoonfireDbTest, EmptyDatabase) {
|
TEST_F(MoonfireDbTest, EmptyDatabase) {
|
||||||
std::string error_message;
|
std::string error_message;
|
||||||
@ -337,7 +396,6 @@ TEST_F(MoonfireDbTest, FullLifecycle) {
|
|||||||
EXPECT_TRUE(mdb_->ListReservedSampleFiles(&reserved, &error_message))
|
EXPECT_TRUE(mdb_->ListReservedSampleFiles(&reserved, &error_message))
|
||||||
<< error_message;
|
<< error_message;
|
||||||
EXPECT_THAT(reserved, testing::UnorderedElementsAre(uuids[0], uuids[1]));
|
EXPECT_THAT(reserved, testing::UnorderedElementsAre(uuids[0], uuids[1]));
|
||||||
LOG(INFO) << "after delete";
|
|
||||||
ExpectNoRecordings(camera_uuid);
|
ExpectNoRecordings(camera_uuid);
|
||||||
|
|
||||||
EXPECT_TRUE(mdb_->MarkSampleFilesDeleted(uuids, &error_message))
|
EXPECT_TRUE(mdb_->MarkSampleFilesDeleted(uuids, &error_message))
|
||||||
@ -355,5 +413,10 @@ int main(int argc, char **argv) {
|
|||||||
google::ParseCommandLineFlags(&argc, &argv, true);
|
google::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
testing::InitGoogleTest(&argc, argv);
|
testing::InitGoogleTest(&argc, argv);
|
||||||
google::InitGoogleLogging(argv[0]);
|
google::InitGoogleLogging(argv[0]);
|
||||||
|
|
||||||
|
// The calendar day math assumes this timezone.
|
||||||
|
CHECK_EQ(0, setenv("TZ", "America/Los_Angeles", 1)) << strerror(errno);
|
||||||
|
tzset();
|
||||||
|
|
||||||
return RUN_ALL_TESTS();
|
return RUN_ALL_TESTS();
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,8 @@
|
|||||||
|
|
||||||
#include "moonfire-db.h"
|
#include "moonfire-db.h"
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <glog/logging.h>
|
#include <glog/logging.h>
|
||||||
@ -43,6 +45,78 @@
|
|||||||
|
|
||||||
namespace moonfire_nvr {
|
namespace moonfire_nvr {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Helper for AdjustDaysMap.
|
||||||
|
void AdjustDay(const std::string &day, int64_t delta,
|
||||||
|
std::map<std::string, int64_t> *days) {
|
||||||
|
auto it = days->find(day);
|
||||||
|
if (it != days->end()) {
|
||||||
|
it->second += delta;
|
||||||
|
DCHECK_GE(it->second, 0) << day << ", " << delta;
|
||||||
|
if (it->second == 0) {
|
||||||
|
days->erase(it);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
days->insert(it, std::make_pair(day, delta));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
void AdjustDaysMap(int64_t start_time_90k, int64_t end_time_90k, int sign,
|
||||||
|
std::map<std::string, int64_t> *days) {
|
||||||
|
// There will always be at most two days adjusted, because
|
||||||
|
// kMaxRecordingDuration is less than a day (even during spring forward).
|
||||||
|
DCHECK_LT(start_time_90k, end_time_90k);
|
||||||
|
DCHECK_LE(end_time_90k - start_time_90k, kMaxRecordingDuration);
|
||||||
|
static_assert(kMaxRecordingDuration <= 23 * 60 * kTimeUnitsPerSecond,
|
||||||
|
"max duration should be less than a (spring-forward) day");
|
||||||
|
|
||||||
|
// Fill |buf| with the first day.
|
||||||
|
struct tm mytm;
|
||||||
|
memset(&mytm, 0, sizeof(mytm));
|
||||||
|
time_t start_ts = start_time_90k / kTimeUnitsPerSecond;
|
||||||
|
localtime_r(&start_ts, &mytm);
|
||||||
|
const char kFmt[] = "%Y-%m-%d";
|
||||||
|
char buf[sizeof("YYYY-mm-DD")];
|
||||||
|
strftime(buf, sizeof(buf), kFmt, &mytm);
|
||||||
|
|
||||||
|
// Determine the start of the next day.
|
||||||
|
// Note that mktime(3) normalizes tm_mday, so this should work on the last
|
||||||
|
// day of the month/year.
|
||||||
|
mytm.tm_isdst = -1;
|
||||||
|
mytm.tm_hour = 0;
|
||||||
|
mytm.tm_min = 0;
|
||||||
|
mytm.tm_sec = 0;
|
||||||
|
++mytm.tm_mday;
|
||||||
|
auto boundary_90k = kTimeUnitsPerSecond * static_cast<int64_t>(mktime(&mytm));
|
||||||
|
|
||||||
|
// Adjust the first day.
|
||||||
|
auto first_day_delta =
|
||||||
|
sign * (std::min(end_time_90k, boundary_90k) - start_time_90k);
|
||||||
|
DCHECK_NE(first_day_delta, 0) << "start=" << start_time_90k
|
||||||
|
<< ", end=" << end_time_90k;
|
||||||
|
AdjustDay(buf, first_day_delta, days);
|
||||||
|
|
||||||
|
if (end_time_90k <= boundary_90k) {
|
||||||
|
return; // no second day.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill |buf| with the second day.
|
||||||
|
strftime(buf, sizeof(buf), kFmt, &mytm);
|
||||||
|
|
||||||
|
// Adjust the second day.
|
||||||
|
auto second_day_delta = sign * (end_time_90k - boundary_90k);
|
||||||
|
DCHECK_NE(second_day_delta, 0) << "start=" << start_time_90k
|
||||||
|
<< ", end=" << end_time_90k;
|
||||||
|
AdjustDay(buf, second_day_delta, days);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
bool MoonfireDatabase::Init(Database *db, std::string *error_message) {
|
bool MoonfireDatabase::Init(Database *db, std::string *error_message) {
|
||||||
CHECK(db_ == nullptr);
|
CHECK(db_ == nullptr);
|
||||||
db_ = db;
|
db_ = db;
|
||||||
|
@ -225,6 +225,12 @@ class MoonfireDatabase {
|
|||||||
int64_t max_end_time_90k = -1;
|
int64_t max_end_time_90k = -1;
|
||||||
int64_t total_sample_file_bytes = -1;
|
int64_t total_sample_file_bytes = -1;
|
||||||
int64_t total_duration_90k = -1;
|
int64_t total_duration_90k = -1;
|
||||||
|
|
||||||
|
// A map of calendar days (in the local timezone, "YYYY-mm-DD") to the
|
||||||
|
// total duration (in 90k units) of recorded data in the day. A day is
|
||||||
|
// present in the map ff the value is non-zero.
|
||||||
|
// TODO: actually fill this.
|
||||||
|
std::map<std::string, int64_t> days;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ReservationState { kWriting = 0, kDeleting = 1 };
|
enum class ReservationState { kWriting = 0, kDeleting = 1 };
|
||||||
@ -253,6 +259,15 @@ class MoonfireDatabase {
|
|||||||
std::map<int64_t, VideoSampleEntry> video_sample_entries_;
|
std::map<int64_t, VideoSampleEntry> video_sample_entries_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// Adjust a day-to-duration map (see MoonfireDatabase::CameraData::days_)
|
||||||
|
// to reflect a recording.
|
||||||
|
void AdjustDaysMap(int64_t start_time_90k, int64_t end_time_90k, int sign,
|
||||||
|
std::map<std::string, int64_t> *days);
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
} // namespace moonfire_nvr
|
} // namespace moonfire_nvr
|
||||||
|
|
||||||
#endif // MOONFIRE_NVR_MOONFIRE_DB_H
|
#endif // MOONFIRE_NVR_MOONFIRE_DB_H
|
||||||
|
Loading…
Reference in New Issue
Block a user