mirror of
https://github.com/scottlamb/moonfire-nvr.git
synced 2025-12-03 06:22:32 -05:00
more flexible signals
Now there's room to add arbitrary configuration to signals and types. Several things are no longer fixed columns/tables but instead within the configuration types.
This commit is contained in:
@@ -159,7 +159,7 @@ fn fill_recording(tx: &rusqlite::Transaction) -> Result<HashMap<i32, CameraState
|
||||
let video_samples: i32 = row.get(5)?;
|
||||
let video_sync_samples: i32 = row.get(6)?;
|
||||
let video_sample_entry_id: i32 = row.get(7)?;
|
||||
let sample_file_uuid: db::FromSqlUuid = row.get(8)?;
|
||||
let sample_file_uuid: db::SqlUuid = row.get(8)?;
|
||||
let sample_file_sha1: Vec<u8> = row.get(9)?;
|
||||
let video_index: Vec<u8> = row.get(10)?;
|
||||
let old_id: i32 = row.get(11)?;
|
||||
|
||||
@@ -335,7 +335,7 @@ fn verify_dir_contents(
|
||||
let mut stmt = tx.prepare(r"select sample_file_uuid from recording_playback")?;
|
||||
let mut rows = stmt.query(params![])?;
|
||||
while let Some(row) = rows.next()? {
|
||||
let uuid: crate::db::FromSqlUuid = row.get(0)?;
|
||||
let uuid: crate::db::SqlUuid = row.get(0)?;
|
||||
if !files.remove(&uuid.0) {
|
||||
bail!(
|
||||
"{} is missing from dir {}!",
|
||||
@@ -349,7 +349,7 @@ fn verify_dir_contents(
|
||||
let mut stmt = tx.prepare(r"select uuid from reserved_sample_files")?;
|
||||
let mut rows = stmt.query(params![])?;
|
||||
while let Some(row) = rows.next()? {
|
||||
let uuid: crate::db::FromSqlUuid = row.get(0)?;
|
||||
let uuid: crate::db::SqlUuid = row.get(0)?;
|
||||
if files.remove(&uuid.0) {
|
||||
// Also remove the garbage file. For historical reasons (version 2 was originally
|
||||
// defined as not having a garbage table so still is), do this here rather than with
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
/// Upgrades a version 2 schema to a version 3 schema.
|
||||
/// Note that a version 2 schema is never actually used; so we know the upgrade from version 1 was
|
||||
/// completed, and possibly an upgrade from 2 to 3 is half-finished.
|
||||
use crate::db::{self, FromSqlUuid};
|
||||
use crate::db::{self, SqlUuid};
|
||||
use crate::dir;
|
||||
use crate::schema;
|
||||
use failure::Error;
|
||||
@@ -19,8 +19,8 @@ use std::sync::Arc;
|
||||
/// * there's only one dir.
|
||||
/// * it has a last completed open.
|
||||
fn open_sample_file_dir(tx: &rusqlite::Transaction) -> Result<Arc<dir::SampleFileDir>, Error> {
|
||||
let (p, s_uuid, o_id, o_uuid, db_uuid): (String, FromSqlUuid, i32, FromSqlUuid, FromSqlUuid) =
|
||||
tx.query_row(
|
||||
let (p, s_uuid, o_id, o_uuid, db_uuid): (String, SqlUuid, i32, SqlUuid, SqlUuid) = tx
|
||||
.query_row(
|
||||
r#"
|
||||
select
|
||||
s.path, s.uuid, s.last_complete_open_id, o.uuid, m.uuid
|
||||
@@ -65,7 +65,7 @@ pub fn run(_args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error>
|
||||
let mut rows = stmt.query(params![])?;
|
||||
while let Some(row) = rows.next()? {
|
||||
let id = db::CompositeId(row.get(0)?);
|
||||
let sample_file_uuid: FromSqlUuid = row.get(1)?;
|
||||
let sample_file_uuid: SqlUuid = row.get(1)?;
|
||||
let from_path = super::UuidPath::from(sample_file_uuid.0);
|
||||
let to_path = crate::dir::CompositeIdPath::from(id);
|
||||
if let Err(e) = nix::fcntl::renameat(
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
///
|
||||
/// This just handles the directory meta files. If they're already in the new format, great.
|
||||
/// Otherwise, verify they are consistent with the database then upgrade them.
|
||||
use crate::db::FromSqlUuid;
|
||||
use crate::db::SqlUuid;
|
||||
use crate::{dir, schema};
|
||||
use cstr::cstr;
|
||||
use failure::{bail, Error, Fail};
|
||||
@@ -111,7 +111,7 @@ fn maybe_cleanup_garbage_uuids(dir: &dir::Fd) -> Result<bool, Error> {
|
||||
}
|
||||
|
||||
pub fn run(_args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error> {
|
||||
let db_uuid: FromSqlUuid =
|
||||
let db_uuid: SqlUuid =
|
||||
tx.query_row_and_then(r"select uuid from meta", params![], |row| row.get(0))?;
|
||||
let mut stmt = tx.prepare(
|
||||
r#"
|
||||
@@ -129,9 +129,9 @@ pub fn run(_args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error>
|
||||
while let Some(row) = rows.next()? {
|
||||
let path = row.get_ref(0)?.as_str()?;
|
||||
info!("path: {}", path);
|
||||
let dir_uuid: FromSqlUuid = row.get(1)?;
|
||||
let dir_uuid: SqlUuid = row.get(1)?;
|
||||
let open_id: Option<u32> = row.get(2)?;
|
||||
let open_uuid: Option<FromSqlUuid> = row.get(3)?;
|
||||
let open_uuid: Option<SqlUuid> = row.get(3)?;
|
||||
let mut db_meta = schema::DirMeta::new();
|
||||
db_meta.db_uuid.extend_from_slice(&db_uuid.0.as_bytes()[..]);
|
||||
db_meta
|
||||
|
||||
@@ -3,11 +3,138 @@
|
||||
// SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception
|
||||
|
||||
/// Upgrades a version 6 schema to a version 7 schema.
|
||||
use failure::Error;
|
||||
use failure::{format_err, Error};
|
||||
use fnv::FnvHashMap;
|
||||
use rusqlite::{named_params, params};
|
||||
use std::convert::TryFrom;
|
||||
use url::Url;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{json::CameraConfig, FromSqlUuid};
|
||||
use crate::{
|
||||
json::{CameraConfig, GlobalConfig, SignalConfig, SignalTypeConfig},
|
||||
SqlUuid,
|
||||
};
|
||||
|
||||
fn copy_meta(tx: &rusqlite::Transaction) -> Result<(), Error> {
|
||||
let mut stmt = tx.prepare("select uuid, max_signal_changes from old_meta")?;
|
||||
let mut insert = tx.prepare("insert into meta (uuid, config) values (:uuid, :config)")?;
|
||||
let mut rows = stmt.query(params![])?;
|
||||
while let Some(row) = rows.next()? {
|
||||
let uuid: SqlUuid = row.get(0)?;
|
||||
let max_signal_changes: Option<i64> = row.get(1)?;
|
||||
let config = GlobalConfig {
|
||||
max_signal_changes: max_signal_changes
|
||||
.map(|s| {
|
||||
u32::try_from(s).map_err(|_| format_err!("max_signal_changes out of range"))
|
||||
})
|
||||
.transpose()?,
|
||||
..Default::default()
|
||||
};
|
||||
insert.execute(named_params! {
|
||||
":uuid": uuid,
|
||||
":config": &config,
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy_signal_types(tx: &rusqlite::Transaction) -> Result<(), Error> {
|
||||
let mut types_ = FnvHashMap::default();
|
||||
let mut stmt = tx.prepare("select type_uuid, value, name from signal_type_enum")?;
|
||||
let mut rows = stmt.query(params![])?;
|
||||
while let Some(row) = rows.next()? {
|
||||
let type_uuid: SqlUuid = row.get(0)?;
|
||||
let value: i32 = row.get(1)?;
|
||||
let name: Option<String> = row.get(2)?;
|
||||
let type_ = types_
|
||||
.entry(type_uuid.0)
|
||||
.or_insert_with(SignalTypeConfig::default);
|
||||
let value = u8::try_from(value).map_err(|_| format_err!("bad signal type value"))?;
|
||||
let value_config = type_.values.entry(value).or_insert_with(Default::default);
|
||||
if let Some(n) = name {
|
||||
value_config.name = n;
|
||||
}
|
||||
}
|
||||
let mut insert = tx.prepare("insert into signal_type (uuid, config) values (?, ?)")?;
|
||||
for (&uuid, config) in &types_ {
|
||||
insert.execute(params![SqlUuid(uuid), config])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct Signal {
|
||||
uuid: Uuid,
|
||||
type_uuid: Uuid,
|
||||
config: SignalConfig,
|
||||
}
|
||||
|
||||
fn copy_signals(tx: &rusqlite::Transaction) -> Result<(), Error> {
|
||||
let mut signals = FnvHashMap::default();
|
||||
|
||||
// Read from signal table.
|
||||
{
|
||||
let mut stmt =
|
||||
tx.prepare("select id, source_uuid, type_uuid, short_name from old_signal")?;
|
||||
let mut rows = stmt.query(params![])?;
|
||||
while let Some(row) = rows.next()? {
|
||||
let id: i32 = row.get(0)?;
|
||||
let id = u32::try_from(id)?;
|
||||
let source_uuid: SqlUuid = row.get(1)?;
|
||||
let type_uuid: SqlUuid = row.get(2)?;
|
||||
let short_name: String = row.get(3)?;
|
||||
signals.insert(
|
||||
id,
|
||||
Signal {
|
||||
uuid: source_uuid.0,
|
||||
type_uuid: type_uuid.0,
|
||||
config: SignalConfig {
|
||||
short_name,
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Read from the signal_camera table.
|
||||
{
|
||||
let mut stmt = tx.prepare("select signal_id, camera_id, type from signal_camera")?;
|
||||
let mut rows = stmt.query(params![])?;
|
||||
while let Some(row) = rows.next()? {
|
||||
let signal_id: i32 = row.get(0)?;
|
||||
let signal_id = u32::try_from(signal_id)?;
|
||||
let camera_id: i32 = row.get(1)?;
|
||||
let type_: i32 = row.get(2)?;
|
||||
let signal = signals.get_mut(&signal_id).unwrap();
|
||||
signal.config.camera_associations.insert(
|
||||
camera_id,
|
||||
match type_ {
|
||||
0 => "direct",
|
||||
_ => "indirect",
|
||||
}
|
||||
.to_owned(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let mut insert = tx.prepare(
|
||||
r#"
|
||||
insert into signal (id, uuid, type_uuid, config)
|
||||
values (:id, :uuid, :type_uuid, :config)
|
||||
"#,
|
||||
)?;
|
||||
for (&id, signal) in &signals {
|
||||
insert.execute(named_params! {
|
||||
":id": id,
|
||||
":uuid": SqlUuid(signal.uuid),
|
||||
":type_uuid": SqlUuid(signal.type_uuid),
|
||||
":config": &signal.config,
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy_cameras(tx: &rusqlite::Transaction) -> Result<(), Error> {
|
||||
let mut insert = tx.prepare(
|
||||
@@ -34,7 +161,7 @@ fn copy_cameras(tx: &rusqlite::Transaction) -> Result<(), Error> {
|
||||
let mut rows = stmt.query(params![])?;
|
||||
while let Some(row) = rows.next()? {
|
||||
let id: i32 = row.get(0)?;
|
||||
let uuid: FromSqlUuid = row.get(1)?;
|
||||
let uuid: SqlUuid = row.get(1)?;
|
||||
let uuid_bytes = &uuid.0.as_bytes()[..];
|
||||
let short_name: String = row.get(2)?;
|
||||
let mut description: Option<String> = row.get(3)?;
|
||||
@@ -134,6 +261,13 @@ pub fn run(_args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error>
|
||||
alter table user add preferences text;
|
||||
alter table camera rename to old_camera;
|
||||
alter table stream rename to old_stream;
|
||||
alter table signal rename to old_signal;
|
||||
alter table meta rename to old_meta;
|
||||
|
||||
create table meta (
|
||||
uuid blob not null check (length(uuid) = 16),
|
||||
config text
|
||||
);
|
||||
|
||||
create table camera (
|
||||
id integer primary key,
|
||||
@@ -153,9 +287,25 @@ pub fn run(_args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error>
|
||||
cum_runs integer not null check (cum_runs >= 0),
|
||||
unique (camera_id, type)
|
||||
);
|
||||
|
||||
create table signal (
|
||||
id integer primary key,
|
||||
uuid blob unique not null check (length(uuid) = 16),
|
||||
type_uuid blob not null references signal_type (uuid)
|
||||
check (length(type_uuid) = 16),
|
||||
config text
|
||||
);
|
||||
|
||||
create table signal_type (
|
||||
uuid blob primary key check (length(uuid) = 16),
|
||||
config text
|
||||
) without rowid;
|
||||
"#,
|
||||
)?;
|
||||
copy_meta(tx)?;
|
||||
copy_cameras(tx)?;
|
||||
copy_signal_types(tx)?;
|
||||
copy_signals(tx)?;
|
||||
copy_streams(tx)?;
|
||||
tx.execute_batch(
|
||||
r#"
|
||||
@@ -224,6 +374,10 @@ pub fn run(_args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error>
|
||||
drop table old_recording;
|
||||
drop table old_stream;
|
||||
drop table old_camera;
|
||||
drop table old_meta;
|
||||
drop table old_signal;
|
||||
drop table signal_type_enum;
|
||||
drop table signal_camera;
|
||||
"#,
|
||||
)?;
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user