Skip to content

Commit 3034fba

Browse files
committed
cashcode: extract the DB handling code
1 parent e8f8440 commit 3034fba

3 files changed

Lines changed: 78 additions & 61 deletions

File tree

src/cashcode.rs

Lines changed: 9 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use log::{debug, error, info, warn};
2-
use rusqlite::{Connection, Result as SqlResult};
32
use serialport::SerialPort;
43
use std::io::{Read, Write};
5-
use std::sync::{Arc, Mutex};
64
use std::thread;
75
use std::time::Duration;
86
use thiserror::Error;
97

8+
use crate::cashcode_db::CashCodeDb;
9+
1010
// protocol constants
1111
const COMMAND_POLL: &[u8] = &[0x02, 0x03, 0x06, 0x33, 0xDA, 0x81];
1212
const COMMAND_RESET: &[u8] = &[0x02, 0x03, 0x06, 0x30, 0x41, 0xB3];
@@ -97,7 +97,7 @@ impl BillNominal {
9797
}
9898
}
9999

100-
fn value(&self) -> i32 {
100+
pub fn value(&self) -> i32 {
101101
*self as i32
102102
}
103103
}
@@ -118,7 +118,7 @@ pub enum BillEvent {
118118
pub struct CashCode {
119119
port: Box<dyn SerialPort>,
120120
stacker_removed: bool,
121-
db: Arc<Mutex<Connection>>,
121+
db: CashCodeDb,
122122
}
123123

124124
impl CashCode {
@@ -130,38 +130,15 @@ impl CashCode {
130130
.open()?;
131131

132132
info!("opening database: {}", db_path);
133-
let db = Connection::open(db_path)?;
134-
135-
// initialize database
136-
Self::init_database(&db)?;
133+
let db = CashCodeDb::open(db_path)?;
137134

138135
Ok(CashCode {
139136
port,
140137
stacker_removed: false,
141-
db: Arc::new(Mutex::new(db)),
138+
db,
142139
})
143140
}
144141

145-
fn init_database(db: &Connection) -> SqlResult<()> {
146-
db.execute(
147-
"CREATE TABLE IF NOT EXISTS accepted_bills (
148-
nominal INTEGER PRIMARY KEY,
149-
quantity INTEGER NOT NULL
150-
)",
151-
[],
152-
)?;
153-
154-
let nominals = [1000, 2000, 5000, 10000, 20000];
155-
for nominal in nominals {
156-
db.execute(
157-
"INSERT OR IGNORE INTO accepted_bills (nominal, quantity) VALUES (?1, 0)",
158-
[nominal],
159-
)?;
160-
}
161-
162-
Ok(())
163-
}
164-
165142
fn send_command(&mut self, command: &[u8]) -> Result<(), CashCodeError> {
166143
self.port.write_all(command)?;
167144
thread::sleep(Duration::from_millis(20));
@@ -381,7 +358,7 @@ impl CashCode {
381358

382359
if let Some(nominal) = BillNominal::from_code(nominal_code) {
383360
info!("bill accepted: {} dram", nominal.value());
384-
self.record_bill(nominal)?;
361+
self.db.record_bill(nominal)?;
385362
Some(BillEvent::Accepted(nominal))
386363
} else {
387364
warn!("bill accepted with unknown nominal: 0x{:02X}", nominal_code);
@@ -404,41 +381,12 @@ impl CashCode {
404381
Ok(event)
405382
}
406383

407-
fn record_bill(&self, nominal: BillNominal) -> Result<(), CashCodeError> {
408-
let db = self.db.lock().unwrap();
409-
db.execute(
410-
"UPDATE accepted_bills SET quantity = quantity + 1 WHERE nominal = ?1",
411-
[nominal.value()],
412-
)?;
413-
Ok(())
414-
}
415-
416384
#[allow(dead_code)]
417385
pub fn get_bill_counts(&self) -> Result<Vec<(i32, i32)>, CashCodeError> {
418-
let db = self.db.lock().unwrap();
419-
let mut stmt =
420-
db.prepare("SELECT nominal, quantity FROM accepted_bills ORDER BY nominal")?;
421-
422-
let rows = stmt.query_map([], |row| Ok((row.get(0)?, row.get(1)?)))?;
423-
424-
let mut results = Vec::new();
425-
for row in rows {
426-
results.push(row?);
427-
}
428-
429-
Ok(results)
386+
self.db.get_bill_counts()
430387
}
431388

432389
pub fn get_total_amount(&self) -> Result<i32, CashCodeError> {
433-
let db = self.db.lock().unwrap();
434-
let total: i32 = db
435-
.query_row(
436-
"SELECT SUM(nominal * quantity) FROM accepted_bills",
437-
[],
438-
|row| row.get(0),
439-
)
440-
.unwrap_or(0);
441-
442-
Ok(total)
390+
self.db.get_total_amount()
443391
}
444392
}

src/cashcode_db.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use rusqlite::{Connection, Result as SqlResult};
2+
use std::sync::{Arc, Mutex};
3+
4+
use crate::cashcode::{BillNominal, CashCodeError};
5+
6+
#[derive(Clone)]
7+
pub struct CashCodeDb {
8+
pub conn: Arc<Mutex<Connection>>,
9+
}
10+
11+
impl CashCodeDb {
12+
pub fn open(db_path: &str) -> Result<Self, CashCodeError> {
13+
let conn = Connection::open(db_path)?;
14+
Self::init(&conn)?;
15+
Ok(Self {
16+
conn: Arc::new(Mutex::new(conn)),
17+
})
18+
}
19+
20+
fn init(db: &Connection) -> SqlResult<()> {
21+
db.execute(
22+
"CREATE TABLE IF NOT EXISTS accepted_bills (
23+
nominal INTEGER PRIMARY KEY,
24+
quantity INTEGER NOT NULL
25+
)",
26+
[],
27+
)?;
28+
29+
for nominal in [1000, 2000, 5000, 10000, 20000] {
30+
db.execute(
31+
"INSERT OR IGNORE INTO accepted_bills (nominal, quantity) VALUES (?1, 0)",
32+
[nominal],
33+
)?;
34+
}
35+
36+
Ok(())
37+
}
38+
39+
pub fn record_bill(&self, nominal: BillNominal) -> Result<(), CashCodeError> {
40+
let db = self.conn.lock().unwrap();
41+
db.execute(
42+
"UPDATE accepted_bills SET quantity = quantity + 1 WHERE nominal = ?1",
43+
[nominal.value()],
44+
)?;
45+
Ok(())
46+
}
47+
48+
#[allow(dead_code)]
49+
pub fn get_bill_counts(&self) -> Result<Vec<(i32, i32)>, CashCodeError> {
50+
let db = self.conn.lock().unwrap();
51+
let mut stmt =
52+
db.prepare("SELECT nominal, quantity FROM accepted_bills ORDER BY nominal")?;
53+
let rows = stmt.query_map([], |row| Ok((row.get(0)?, row.get(1)?)))?;
54+
rows.collect::<SqlResult<Vec<_>>>().map_err(Into::into)
55+
}
56+
57+
pub fn get_total_amount(&self) -> Result<i32, CashCodeError> {
58+
let db = self.conn.lock().unwrap();
59+
let total: i32 = db
60+
.query_row(
61+
"SELECT SUM(nominal * quantity) FROM accepted_bills",
62+
[],
63+
|row| row.get(0),
64+
)
65+
.unwrap_or(0);
66+
Ok(total)
67+
}
68+
}

src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
slint::include_modules!();
55

66
mod cashcode;
7+
mod cashcode_db;
78
mod cctalk;
89
mod config;
910
mod diag_logger;

0 commit comments

Comments
 (0)