/*
* Copyright 2014 Bevbot LLC <info@bevbot.com>
*
* This file is part of the Kegtab package from the Kegbot project. For
* more information on Kegtab or Kegbot, see <http://kegbot.org/>.
*
* Kegtab is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
* Kegtab is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with Kegtab. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kegbot.backend;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns;
import android.util.Log;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import org.kegbot.app.util.TimeSeries;
import org.kegbot.proto.Models.Beverage;
import org.kegbot.proto.Models.BeverageProducer;
import org.kegbot.proto.Models.Controller;
import org.kegbot.proto.Models.Drink;
import org.kegbot.proto.Models.FlowMeter;
import org.kegbot.proto.Models.FlowToggle;
import org.kegbot.proto.Models.Keg;
import org.kegbot.proto.Models.KegTap;
import java.util.List;
import javax.annotation.Nullable;
/**
* @author mike wakerly (mike@wakerly.com)
*/
public class LocalBackendDbHelper extends SQLiteOpenHelper {
private static final String TAG = LocalBackendDbHelper.class.getSimpleName();
private static final int DATABASE_VERSION = 3;
@VisibleForTesting
static final String DATABASE_NAME = "local_backend.db";
private static final String COLUMN_ID = BaseColumns._ID;
private static final String TABLE_KEGS = "kegs";
private static final String COLUMN_KEG_FULL_VOLUME_ML = "full_volume_ml";
private static final String COLUMN_KEG_SERVED_VOLUME_ML = "served_volume_ml";
private static final String COLUMN_KEG_START_TIME = "start_time";
private static final String COLUMN_KEG_END_TIME = "end_time";
private static final String COLUMN_KEG_ONLINE = "online";
private static final String COLUMN_KEG_KEG_TYPE = "keg_type";
private static final String COLUMN_KEG_BEER_TYPE_NAME = "beer_name";
private static final String COLUMN_KEG_BEER_BREWER_NAME = "brewer_name";
private static final String COLUMN_KEG_BEER_STYLE_NAME = "style_name";
private static final String TABLE_TAPS = "taps";
private static final String COLUMN_TAP_TAP_NAME = "tap_name";
private static final String COLUMN_TAP_CURRENT_KEG = "current_keg_id";
private static final String COLUMN_TAP_SORT_ORDER = "sort_order";
private static final String COLUMN_TAP_FLOW_METER_ID = "flow_meter";
private static final String TABLE_DRINKS = "drinks";
private static final String COLUMN_DRINK_KEG_ID = "keg_id";
private static final String COLUMN_DRINK_TICKS = "ticks";
private static final String COLUMN_DRINK_VOLUME_ML = "volume_ml";
private static final String COLUMN_DRINK_TIME = "time";
private static final String COLUMN_DRINK_USERNAME = "username";
private static final String COLUMN_DRINK_PICTURE_URL = "picture_url";
private static final String COLUMN_DRINK_SHOUT = "shout";
private static final String TABLE_CONTROLLERS = "controllers";
private static final String COLUMN_CONTROLLER_NAME = "name";
private static final String COLUMN_CONTROLLER_MODEL_NAME = "model_name";
private static final String COLUMN_CONTROLLER_SERIAL_NUMBER = "serial_number";
private static final String TABLE_FLOW_METERS = "flow_meters";
private static final String COLUMN_FLOW_METER_CONTROLLER_ID = "controller_id";
private static final String COLUMN_FLOW_METER_PORT_NAME = "port_name";
private static final String COLUMN_FLOW_METER_TICKS_PER_ML = "ticks_per_ml";
private static final String TABLE_FLOW_TOGGLES = "flow_toggles";
private static final String COLUMN_FLOW_TOGGLE_CONTROLLER_ID = "controller_id";
private static final String COLUMN_FLOW_TOGGLE_PORT_NAME = "port_name";
/**
* @param context
*/
public LocalBackendDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
upgrade(db, 0);
setDefaults(db);
}
private void upgrade(SQLiteDatabase db, int currentVersion) {
if (currentVersion < 2) {
currentVersion = 2;
Log.i(TAG, "Updating to schema version " + currentVersion + " ...");
Log.d(TAG, "Creating table " + TABLE_KEGS);
db.execSQL("CREATE TABLE " + TABLE_KEGS + " ("
+ COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ COLUMN_KEG_START_TIME + " DATETIME DEFAULT CURRENT_TIMESTAMP, "
+ COLUMN_KEG_END_TIME + " DATETIME DEFAULT CURRENT_TIMESTAMP, "
+ COLUMN_KEG_BEER_TYPE_NAME + " TEXT NOT NULL, "
+ COLUMN_KEG_BEER_BREWER_NAME + " TEXT NOT NULL, "
+ COLUMN_KEG_BEER_STYLE_NAME + " TEXT NOT NULL, "
+ COLUMN_KEG_ONLINE + " INTEGER NOT NULL DEFAULT 1, "
+ COLUMN_KEG_KEG_TYPE + " TEXT NOT NULL, "
+ COLUMN_KEG_FULL_VOLUME_ML + " REAL NOT NULL, "
+ COLUMN_KEG_SERVED_VOLUME_ML + " REAL NOT NULL)");
Log.d(TAG, "Creating table " + TABLE_TAPS);
db.execSQL("CREATE TABLE " + TABLE_TAPS + " ("
+ COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ COLUMN_TAP_TAP_NAME + " TEXT NOT NULL, "
+ COLUMN_TAP_SORT_ORDER + " INTEGER NOT NULL DEFAULT 0, "
+ COLUMN_TAP_FLOW_METER_ID + " INTEGER, "
+ COLUMN_TAP_CURRENT_KEG + " INTEGER)");
Log.d(TAG, "Creating table " + TABLE_DRINKS);
db.execSQL("CREATE TABLE " + TABLE_DRINKS + " ("
+ COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ COLUMN_DRINK_KEG_ID + " INTEGER NOT NULL, "
+ COLUMN_DRINK_TICKS + " INTEGER NOT NULL, "
+ COLUMN_DRINK_VOLUME_ML + " INTEGER NOT NULL, "
+ COLUMN_DRINK_TIME + " DATETIME DEFAULT CURRENT_TIMESTAMP, "
+ COLUMN_DRINK_USERNAME + " STRING, "
+ COLUMN_DRINK_PICTURE_URL + " STRING, "
+ COLUMN_DRINK_SHOUT + " TEXT)");
Log.d(TAG, "Creating table " + TABLE_CONTROLLERS);
db.execSQL("CREATE TABLE " + TABLE_CONTROLLERS + " ("
+ COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ COLUMN_CONTROLLER_NAME + " STRING UNIQUE NOT NULL, "
+ COLUMN_CONTROLLER_MODEL_NAME + " STRING, "
+ COLUMN_CONTROLLER_SERIAL_NUMBER + " STRING)");
Log.d(TAG, "Creating table " + TABLE_FLOW_METERS);
db.execSQL("CREATE TABLE " + TABLE_FLOW_METERS + " ("
+ COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ COLUMN_FLOW_METER_CONTROLLER_ID + " INTEGER NOT NULL, "
+ COLUMN_FLOW_METER_PORT_NAME + " STRING NOT NULL, "
+ COLUMN_FLOW_METER_TICKS_PER_ML + " REAL)");
}
if (currentVersion < 3) {
currentVersion = 3;
Log.i(TAG, "Updating to schema version " + currentVersion + " ...");
Log.d(TAG, "Creating table " + TABLE_FLOW_TOGGLES);
db.execSQL("CREATE TABLE " + TABLE_FLOW_TOGGLES + " ("
+ COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ COLUMN_FLOW_TOGGLE_CONTROLLER_ID + " INTEGER NOT NULL, "
+ COLUMN_FLOW_TOGGLE_PORT_NAME + " STRING NOT NULL)");
}
}
private void setDefaults(SQLiteDatabase db) {
Controller controller = Controller.newBuilder()
.setId(1)
.setName("kegboard")
.build();
controller = createOrUpdateController(controller, db);
Log.d(TAG, "Created controller: " + controller);
FlowMeter meter = FlowMeter.newBuilder()
.setId(1)
.setName("kegboard")
.setPortName("flow0")
.setTicksPerMl(2.2f)
.setController(controller)
.build();
meter = createOrUpdateFlowMeter(meter, db);
Log.d(TAG, "Created meter: " + meter);
KegTap tap = KegTap.newBuilder()
.setName("Main Tap")
.setMeter(meter)
.setId(1)
.build();
tap = createOrUpdateTap(tap, db);
Log.d(TAG, "Created tap: " + tap);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion);
if (oldVersion >= DATABASE_VERSION) {
Log.wtf(TAG, "Unknown database version, maximum is " + DATABASE_VERSION);
return;
}
upgrade(db, oldVersion);
}
List<Controller> getAllControllers() {
final List<Controller> result = Lists.newArrayList();
final SQLiteDatabase db = getReadableDatabase();
try {
final Cursor cursor =
db.query(TABLE_CONTROLLERS,
null, null, null, null, null, COLUMN_ID + " ASC");
try {
if (cursor.getCount() == 0) {
return result;
}
Log.d(TAG, "getAllControllers: count=" + cursor.getCount());
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
final Controller controller = controllerFromCursor(cursor);
result.add(controller);
cursor.moveToNext();
}
} finally {
cursor.close();
}
} finally {
}
return result;
}
Controller getController(int controllerId) {
final SQLiteDatabase db = getReadableDatabase();
try {
return getController(controllerId, db);
} finally {
}
}
List<FlowMeter> getAllFlowMeters() {
final SQLiteDatabase db = getReadableDatabase();
final List<FlowMeter> result = Lists.newArrayList();
try {
final Cursor cursor =
db.query(TABLE_FLOW_METERS,
null, null, null, null, null, COLUMN_ID + " ASC");
try {
if (cursor.getCount() == 0) {
return result;
}
Log.d(TAG, "getAllFlowMeters: count=" + cursor.getCount());
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
final FlowMeter tap = flowMeterFromCursor(cursor, db);
result.add(tap);
cursor.moveToNext();
}
} finally {
cursor.close();
}
} finally {
}
return result;
}
FlowMeter getFlowMeter(int flowMeterId) {
final SQLiteDatabase db = getReadableDatabase();
return getFlowMeter(flowMeterId, db);
}
List<FlowToggle> getAllFlowToggles() {
final SQLiteDatabase db = getReadableDatabase();
final List<FlowToggle> result = Lists.newArrayList();
try {
final Cursor cursor =
db.query(TABLE_FLOW_TOGGLES,
null, null, null, null, null, COLUMN_ID + " ASC");
try {
if (cursor.getCount() == 0) {
return result;
}
Log.d(TAG, "getAllFlowToggles: count=" + cursor.getCount());
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
final FlowToggle toggle = flowToggleFromCursor(cursor, db);
result.add(toggle);
cursor.moveToNext();
}
} finally {
cursor.close();
}
} finally {
}
return result;
}
FlowToggle getFlowToggle(int flowToggleId) {
final SQLiteDatabase db = getReadableDatabase();
return getFlowToggle(flowToggleId, db);
}
List<KegTap> getAllTaps() {
final SQLiteDatabase db = getReadableDatabase();
final List<KegTap> result = Lists.newArrayList();
try {
final Cursor cursor =
db.query(TABLE_TAPS,
null, null, null, null, null, COLUMN_TAP_SORT_ORDER + " ASC");
try {
if (cursor.getCount() == 0) {
return result;
}
Log.d(TAG, "getTaps: count=" + cursor.getCount());
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
final KegTap tap = kegTapFromCursor(cursor, db);
result.add(tap);
cursor.moveToNext();
}
} finally {
cursor.close();
}
} finally {
}
return result;
}
KegTap getTap(int tapId) {
final SQLiteDatabase db = getReadableDatabase();
try {
return getTap(tapId, db);
} finally {
}
}
KegTap getTap(String meterName) {
for (final KegTap tap : getAllTaps()) {
if (tap.getMeter().getName().equals(meterName)) {
return tap;
}
}
return null;
}
Keg getKeg(long kegId) {
final SQLiteDatabase db = getReadableDatabase();
try {
final Cursor cursor = getRow(db, TABLE_KEGS, (int) kegId);
try {
return kegFromCursor(cursor);
} finally {
cursor.close();
}
} finally {
}
}
Drink getDrink(int drinkId) {
final SQLiteDatabase db = getReadableDatabase();
try {
final Cursor cursor = getRow(db, TABLE_DRINKS, drinkId);
try {
return drinkFromCursor(cursor);
} finally {
cursor.close();
}
} finally {
}
}
Drink recordDrink(String tapName, long volumeMl, long ticks, @Nullable String shout,
@Nullable String username, @Nullable String recordDate, long durationMillis,
@Nullable TimeSeries timeSeries, @Nullable String pictureUrl) throws NotFoundException {
if (!Strings.isNullOrEmpty(username)) {
Log.w(TAG, "recordDrink: Ignoring username.");
}
final KegTap tap = getTap(tapName);
if (tap == null) {
throw new NotFoundException("Unknown tap: " + tapName);
}
final int kegId = tap.getCurrentKegId();
if (kegId == 0) {
throw new NotFoundException("Tap does not have an active keg.");
}
final Keg keg = getKeg(kegId);
// Subtract volume from keg.
final double servedMl = keg.getServedVolumeMl() + volumeMl;
final double remainMl = keg.getRemainingVolumeMl() - volumeMl;
final double percentFull = remainMl / keg.getFullVolumeMl() * 100.0;
final Keg updatedKeg = Keg.newBuilder(keg)
.setServedVolumeMl(servedMl)
.setRemainingVolumeMl(remainMl)
.setPercentFull(percentFull)
.build();
createOrUpdateKeg(updatedKeg);
// Create the drink.
final SQLiteDatabase db = getWritableDatabase();
final ContentValues values = new ContentValues();
values.put(COLUMN_DRINK_KEG_ID, Integer.valueOf(keg.getId()));
values.put(COLUMN_DRINK_TICKS, Long.valueOf(ticks));
values.put(COLUMN_DRINK_SHOUT, shout);
values.put(COLUMN_DRINK_USERNAME, username); // TODO
values.put(COLUMN_DRINK_VOLUME_ML, Long.valueOf(volumeMl));
if (!Strings.isNullOrEmpty(pictureUrl)) {
values.put(COLUMN_DRINK_PICTURE_URL, pictureUrl);
}
final long drinkId = db.insert(TABLE_DRINKS, null, values);
if (drinkId < 0) {
// TODO: handle differently?
throw new SQLiteException("Error inserting drink");
}
return getDrink((int) drinkId);
}
Keg createOrUpdateKeg(final Keg keg) {
final SQLiteDatabase db = getWritableDatabase();
final long result = db.insertWithOnConflict(TABLE_KEGS, null, toContentValues(keg),
SQLiteDatabase.CONFLICT_REPLACE);
if (result < 0) {
throw new SQLiteException("Error during insert: " + result);
}
return getKeg(result);
}
KegTap createOrUpdateTap(KegTap tap) {
final SQLiteDatabase db = getWritableDatabase();
return createOrUpdateTap(tap, db);
}
private KegTap createOrUpdateTap(KegTap tap, SQLiteDatabase db) {
final long result = db.insertWithOnConflict(TABLE_TAPS, null, toContentValues(tap),
SQLiteDatabase.CONFLICT_REPLACE);
if (result < 0) {
throw new SQLiteException("Error during insert: " + result);
}
Log.d(TAG, "Created/updated tap, id: " + result);
return getTap((int) result, db);
}
Controller createOrUpdateController(Controller controller, SQLiteDatabase db) {
final long result = db.insertWithOnConflict(TABLE_CONTROLLERS, null, toContentValues(controller),
SQLiteDatabase.CONFLICT_REPLACE);
if (result < 0) {
throw new SQLiteException("Error during insert: " + result);
}
Log.d(TAG, "Created/updated controller, id: " + result);
return getController((int) result, db);
}
Controller createOrUpdateController(Controller controller) {
final SQLiteDatabase db = getWritableDatabase();
return createOrUpdateController(controller, db);
}
FlowMeter createOrUpdateFlowMeter(FlowMeter meter, SQLiteDatabase db) {
final long result = db.insertWithOnConflict(TABLE_FLOW_METERS, null, toContentValues(meter),
SQLiteDatabase.CONFLICT_REPLACE);
if (result < 0) {
throw new SQLiteException("Error during insert: " + result);
}
Log.d(TAG, "Created/updated meter, id: " + result);
return getFlowMeter((int) result, db);
}
FlowMeter createOrUpdateFlowMeter(FlowMeter meter) {
final SQLiteDatabase db = getWritableDatabase();
return createOrUpdateFlowMeter(meter, db);
}
FlowToggle createOrUpdateFlowToggle(FlowToggle toggle, SQLiteDatabase db) {
final long result = db.insertWithOnConflict(TABLE_FLOW_METERS, null, toContentValues(toggle),
SQLiteDatabase.CONFLICT_REPLACE);
if (result < 0) {
throw new SQLiteException("Error during insert: " + result);
}
Log.d(TAG, "Created/updated toggle, id: " + result);
return getFlowToggle((int) result, db);
}
FlowToggle createOrUpdateFlowToggle(FlowToggle toggle) {
final SQLiteDatabase db = getWritableDatabase();
return createOrUpdateFlowToggle(toggle, db);
}
KegTap connectTapToMeter(final KegTap tap, @Nullable final FlowMeter meter) {
Log.d(TAG, "connectTapToMeter: " + tap.getName() + " meter: " + (meter != null ? meter.getId() : null));
// Unlink a meter.
if (meter == null) {
return createOrUpdateTap(KegTap.newBuilder(tap)
.clearMeter()
.clearMeterName()
.build());
}
Log.d(TAG, "Assigning tap " + tap.getName() + " to meter " + meter.getName());
// Unlink meter on any other taps (should be just one).
for (final KegTap otherTap : getAllTaps()) {
if (!otherTap.hasMeter()) {
continue;
}
if (otherTap.getMeter().getId() != meter.getId()) {
continue;
}
Log.d(TAG, "-- unlinking meter on tap " + otherTap.getName());
connectTapToMeter(otherTap, null);
}
return createOrUpdateTap(KegTap.newBuilder(tap)
.setMeter(meter)
.setMeterName(meter.getName())
.build());
}
KegTap connectTapToToggle(final KegTap tap, @Nullable final FlowToggle toggle) {
Log.d(TAG, "connectTapToToggle: " + tap.getName() + " toggle: " +
(toggle != null ? toggle.getId() : null));
// Unlink a toggle.
if (toggle == null) {
return createOrUpdateTap(KegTap.newBuilder(tap)
.clearToggle()
.clearRelayName()
.build());
}
Log.d(TAG, "Assigning tap " + tap.getName() + " to toggle " + toggle.getName());
// Unlink toggle on any other taps (should be just one).
for (final KegTap otherTap : getAllTaps()) {
if (!otherTap.hasToggle()) {
continue;
}
if (otherTap.getToggle().getId() != toggle.getId()) {
continue;
}
Log.d(TAG, "-- unlinking toggle on tap " + otherTap.getName());
connectTapToToggle(otherTap, null);
}
return createOrUpdateTap(KegTap.newBuilder(tap)
.setToggle(toggle)
.setRelayName(toggle.getName())
.build());
}
boolean deleteTap(KegTap tap) {
Preconditions.checkArgument(tap.getId() != 0);
return deleteRow(TABLE_TAPS, tap.getId());
}
private boolean deleteRow(final String tableName, final int rowId) {
final SQLiteDatabase db = getWritableDatabase();
final int numRows = db.delete(tableName, COLUMN_ID + " = ?",
new String[]{String.valueOf(rowId)});
if (numRows > 1) {
throw new IllegalStateException("Too many rows deleted!");
}
return numRows == 1;
}
private Cursor getRow(SQLiteDatabase db, String tableName, int rowId) {
Log.d(TAG, "querying: " + tableName + ", row: " + rowId);
final Cursor cursor =
db.query(tableName, null, COLUMN_ID + " = ?",
new String[]{String.valueOf(rowId)}, null, null, null);
final int count = cursor.getCount();
if (count == 0) {
throw new SQLiteException("No matching keg found.");
} else if (count > 1) {
throw new SQLiteException("Multiple records found: " + count);
}
cursor.moveToFirst();
return cursor;
}
private Controller getController(int controllerId, SQLiteDatabase db) {
final Cursor cursor = getRow(db, TABLE_CONTROLLERS, controllerId);
try {
return controllerFromCursor(cursor);
} finally {
cursor.close();
}
}
private FlowMeter getFlowMeter(int flowMeterId, SQLiteDatabase db) {
final Cursor cursor = getRow(db, TABLE_FLOW_METERS, flowMeterId);
try {
return flowMeterFromCursor(cursor, db);
} finally {
cursor.close();
}
}
private FlowToggle getFlowToggle(int flowToggleId, SQLiteDatabase db) {
final Cursor cursor = getRow(db, TABLE_FLOW_TOGGLES, flowToggleId);
try {
return flowToggleFromCursor(cursor, db);
} finally {
cursor.close();
}
}
private KegTap getTap(int tapId, SQLiteDatabase db) {
final Cursor cursor = getRow(db, TABLE_TAPS, tapId);
try {
return kegTapFromCursor(cursor, db);
} finally {
cursor.close();
}
}
private Controller controllerFromCursor(Cursor cursor) {
final int id = cursor.getInt(cursor.getColumnIndex(COLUMN_ID));
if (id < 0) {
throw new IllegalStateException("Bad column id: " + id);
}
final Controller controller = Controller.newBuilder()
.setId(id)
.setName(cursor.getString(cursor.getColumnIndex(COLUMN_CONTROLLER_NAME)))
.setModelName(cursor.getString(cursor.getColumnIndex(COLUMN_CONTROLLER_MODEL_NAME)))
.setSerialNumber(cursor.getString(cursor.getColumnIndex(COLUMN_CONTROLLER_SERIAL_NUMBER)))
.build();
return controller;
}
private FlowMeter flowMeterFromCursor(Cursor cursor, SQLiteDatabase db) {
final int id = cursor.getInt(cursor.getColumnIndex(COLUMN_ID));
if (id < 0) {
throw new IllegalStateException("Bad column id: " + id);
}
final int controllerId = cursor.getInt(cursor.getColumnIndex(COLUMN_FLOW_METER_CONTROLLER_ID));
final String portName = cursor.getString(cursor.getColumnIndex(COLUMN_FLOW_METER_PORT_NAME));
final Controller controller = getController(controllerId, db);
final FlowMeter flowMeter = FlowMeter.newBuilder()
.setId(id)
.setPortName(portName)
.setTicksPerMl(cursor.getFloat(cursor.getColumnIndex(COLUMN_FLOW_METER_TICKS_PER_ML)))
.setController(controller)
.setName(String.format("%s.%s", controller.getName(), portName))
.build();
return flowMeter;
}
private FlowToggle flowToggleFromCursor(Cursor cursor, SQLiteDatabase db) {
final int id = cursor.getInt(cursor.getColumnIndex(COLUMN_ID));
if (id < 0) {
throw new IllegalStateException("Bad column id: " + id);
}
final int controllerId = cursor.getInt(cursor.getColumnIndex(COLUMN_FLOW_TOGGLE_CONTROLLER_ID));
final String portName = cursor.getString(cursor.getColumnIndex(COLUMN_FLOW_TOGGLE_PORT_NAME));
final Controller controller = getController(controllerId, db);
final FlowToggle flowToggle = FlowToggle.newBuilder()
.setId(id)
.setPortName(portName)
.setController(controller)
.setName(String.format("%s.%s", controller.getName(), portName))
.build();
return flowToggle;
}
private Keg kegFromCursor(final Cursor cursor) {
final int kegId = cursor.getInt(cursor.getColumnIndex(COLUMN_ID));
final double fullMl = cursor.getDouble(cursor.getColumnIndex(COLUMN_KEG_FULL_VOLUME_ML));
final double servedMl = cursor.getDouble(cursor.getColumnIndex(COLUMN_KEG_SERVED_VOLUME_ML));
final double remainMl = fullMl - servedMl;
final double percentFull = remainMl / fullMl * 100.0f;
final String beerName = cursor.getString(cursor.getColumnIndex(COLUMN_KEG_BEER_TYPE_NAME));
final String brewerName = cursor.getString(cursor.getColumnIndex(COLUMN_KEG_BEER_BREWER_NAME));
final String beerStyle = cursor.getString(cursor.getColumnIndex(COLUMN_KEG_BEER_STYLE_NAME));
final Keg keg = Keg.newBuilder()
.setId(kegId)
.setStartTime(formatDatetime(cursor.getString(cursor.getColumnIndex(COLUMN_KEG_START_TIME))))
.setEndTime(formatDatetime(cursor.getString(cursor.getColumnIndex(COLUMN_KEG_END_TIME))))
.setPercentFull(percentFull)
.setFullVolumeMl(fullMl)
.setRemainingVolumeMl(remainMl)
.setServedVolumeMl(servedMl)
.setSpilledVolumeMl(0.0)
.setKegType(cursor.getString(cursor.getColumnIndex(COLUMN_KEG_KEG_TYPE)))
.setOnline(cursor.getInt(cursor.getColumnIndex(COLUMN_KEG_ONLINE)) != 0)
.setBeverage(Beverage.newBuilder()
.setId(0)
.setBeverageType("beer")
.setName(beerName)
.setStyle(beerStyle)
.setProducer(BeverageProducer.newBuilder()
.setId(0)
.setName(brewerName)
.build())
.build())
.build();
return keg;
}
private KegTap kegTapFromCursor(Cursor cursor, SQLiteDatabase db) {
final int id = cursor.getInt(cursor.getColumnIndex(COLUMN_ID));
if (id < 0) {
throw new IllegalStateException("Bad column id: " + id);
}
final int flowMeterId = cursor.getInt(cursor.getColumnIndex(COLUMN_TAP_FLOW_METER_ID));
FlowMeter meter = null;
if (flowMeterId > 0) {
Log.d(TAG, "Getting flow meter, ID: " + flowMeterId);
meter = getFlowMeter(flowMeterId, db);
Log.d(TAG, "Got meter: " + meter);
}
final int currentKegId = cursor.getInt(cursor.getColumnIndex(COLUMN_TAP_CURRENT_KEG));
Keg currentKeg = null;
if (currentKegId > 0) {
currentKeg = getKeg(currentKegId);
}
final KegTap.Builder builder = KegTap.newBuilder()
.setId(id)
.setName(cursor.getString(cursor.getColumnIndex(COLUMN_TAP_TAP_NAME)));
if (currentKegId > 0) {
builder.setCurrentKegId(currentKegId);
}
if (meter != null) {
builder.setMeter(meter);
}
if (currentKeg != null) {
builder.setCurrentKeg(currentKeg);
}
return builder.build();
}
private Drink drinkFromCursor(final Cursor cursor) {
final int drinkId = cursor.getInt(cursor.getColumnIndex(COLUMN_ID));
final int kegId = cursor.getInt(cursor.getColumnIndex(COLUMN_DRINK_KEG_ID));
final Drink drink = Drink.newBuilder()
.setId(drinkId)
.setSessionId(0)
.setTime("") // TODO
.setVolumeMl(cursor.getFloat(cursor.getColumnIndex(COLUMN_DRINK_VOLUME_ML)))
.setTicks((int) cursor.getLong(cursor.getColumnIndex(COLUMN_DRINK_TICKS)))
.setShout(cursor.getString(cursor.getColumnIndex(COLUMN_DRINK_SHOUT)))
.setUserId(cursor.getString(cursor.getColumnIndex(COLUMN_DRINK_USERNAME)))
.setKegId(kegId)
.build();
return drink;
}
private static ContentValues toContentValues(final Keg keg) {
final ContentValues values = new ContentValues();
if (keg.getId() != 0) {
values.put(COLUMN_ID, Integer.valueOf(keg.getId()));
}
values.put(COLUMN_KEG_ONLINE, Integer.valueOf(keg.getOnline() ? 1 : 0));
values.put(COLUMN_KEG_KEG_TYPE, keg.getKegType());
values.put(COLUMN_KEG_START_TIME, keg.getStartTime());
values.put(COLUMN_KEG_END_TIME, keg.getStartTime());
values.put(COLUMN_KEG_FULL_VOLUME_ML, Double.valueOf(keg.getFullVolumeMl()));
values.put(COLUMN_KEG_SERVED_VOLUME_ML, Double.valueOf(keg.getServedVolumeMl()));
values.put(COLUMN_KEG_BEER_TYPE_NAME, keg.getBeverage().getName());
values.put(COLUMN_KEG_BEER_BREWER_NAME, keg.getBeverage().getProducer().getName());
values.put(COLUMN_KEG_BEER_STYLE_NAME, keg.getBeverage().getStyle());
return values;
}
private static ContentValues toContentValues(final Controller controller) {
final ContentValues values = new ContentValues();
if (controller.getId() != 0) {
values.put(COLUMN_ID, Integer.valueOf(controller.getId()));
}
values.put(COLUMN_CONTROLLER_NAME, controller.getName());
values.put(COLUMN_CONTROLLER_MODEL_NAME, controller.getModelName());
values.put(COLUMN_CONTROLLER_SERIAL_NUMBER, controller.getSerialNumber());
return values;
}
private static ContentValues toContentValues(final FlowMeter meter) {
final ContentValues values = new ContentValues();
if (meter.getId() != 0) {
values.put(COLUMN_ID, Integer.valueOf(meter.getId()));
}
values.put(COLUMN_FLOW_METER_CONTROLLER_ID, Integer.valueOf(meter.getController().getId()));
values.put(COLUMN_FLOW_METER_PORT_NAME, meter.getPortName());
values.put(COLUMN_FLOW_METER_TICKS_PER_ML, Double.valueOf(meter.getTicksPerMl()));
return values;
}
private static ContentValues toContentValues(final FlowToggle toggle) {
final ContentValues values = new ContentValues();
if (toggle.getId() != 0) {
values.put(COLUMN_ID, Integer.valueOf(toggle.getId()));
}
values.put(COLUMN_FLOW_TOGGLE_CONTROLLER_ID, Integer.valueOf(toggle.getController().getId()));
values.put(COLUMN_FLOW_TOGGLE_PORT_NAME, toggle.getPortName());
return values;
}
private static ContentValues toContentValues(final KegTap tap) {
final ContentValues values = new ContentValues();
if (tap.getId() != 0) {
values.put(COLUMN_ID, Integer.valueOf(tap.getId()));
}
values.put(COLUMN_TAP_TAP_NAME, tap.getName());
values.put(COLUMN_TAP_SORT_ORDER, Integer.valueOf(0)); // TODO
values.put(COLUMN_TAP_CURRENT_KEG, Integer.valueOf(tap.getCurrentKegId()));
if (tap.hasMeter() && tap.getMeter().getId() > 0) {
values.put(COLUMN_TAP_FLOW_METER_ID, Integer.valueOf(tap.getMeter().getId()));
} else {
values.put(COLUMN_TAP_FLOW_METER_ID, Integer.valueOf(0));
}
return values;
}
private static String formatDatetime(String original) {
return "";
}
}