Skip to content

Commit

Permalink
Finished debugging data store changes.
Browse files Browse the repository at this point in the history
  • Loading branch information
MatthewGerber committed Sep 22, 2015
1 parent d02d532 commit ccbdeea
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 82 deletions.
14 changes: 3 additions & 11 deletions SensusService/DataStores/DataStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ namespace SensusService.DataStores
/// </summary>
public abstract class DataStore
{
private string _name;
private int _commitDelayMS;
private bool _running;
private Protocol _protocol;
Expand All @@ -37,13 +36,6 @@ public abstract class DataStore

private readonly object _locker = new object();

[EntryStringUiProperty("Name:", true, 1)]
public string Name
{
get { return _name; }
set { _name = value; }
}

[EntryIntegerUiProperty("Commit Delay (MS):", true, 2)]
public int CommitDelayMS
{
Expand Down Expand Up @@ -74,15 +66,15 @@ public bool Running
{
get { return _running; }
}

protected abstract string DisplayName { get; }

[DisplayStringUiProperty("Type:", 0)]
public abstract string DisplayName { get; }

[JsonIgnore]
public abstract bool Clearable { get; }

protected DataStore()
{
_name = DisplayName;
_commitDelayMS = 10000;
_running = false;
_mostRecentCommitTimestamp = DateTimeOffset.MinValue;
Expand Down
79 changes: 40 additions & 39 deletions SensusService/DataStores/Local/FileLocalDataStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using System.Collections.Generic;
using System.IO;
using System.Threading;
using SensusUI.UiProperties;

namespace SensusService.DataStores.Local
{
Expand Down Expand Up @@ -53,7 +54,7 @@ private string StorageDirectory
}
}

protected override string DisplayName
public override string DisplayName
{
get { return "File"; }
}
Expand All @@ -78,11 +79,11 @@ public FileLocalDataStore()
}

public override void Start()
{
// file needs to be ready to accept data immediately
{
lock (_locker)
{
FindNewPath();
// file needs to be ready to accept data immediately, so set file path before calling base.Start
WriteToNewPath();

base.Start();
}
Expand Down Expand Up @@ -124,9 +125,10 @@ protected override List<Datum> CommitData(List<Datum> data, CancellationToken ca
{
SensusServiceHelper.Get().Logger.Log("Failed to write datum JSON to local file: " + ex.Message, LoggingLevel.Normal, GetType());

// something went wrong with file write...switch to a new file in the hope that it will work better
try
{
FindNewPath();
WriteToNewPath();
SensusServiceHelper.Get().Logger.Log("Initialized new local file.", LoggingLevel.Normal, GetType());
}
catch (Exception ex2)
Expand Down Expand Up @@ -163,14 +165,13 @@ public override List<Datum> GetDataForRemoteDataStore(CancellationToken cancella
using (StreamReader file = new StreamReader(path))
{
string line;
while (!cancellationToken.IsCancellationRequested && (line = file.ReadLine()) != null)
if (!string.IsNullOrWhiteSpace(line))
{
localData.Add(Datum.FromJSON(line));
while (!cancellationToken.IsCancellationRequested && !string.IsNullOrWhiteSpace(line = file.ReadLine()))
{
localData.Add(Datum.FromJSON(line));

if (progressCallback != null && _numDataStoredInFiles >= 10 && (localData.Count % (_numDataStoredInFiles / 10)) == 0)
progressCallback(localData.Count / (double)_numDataStoredInFiles);
}
if (progressCallback != null && _numDataStoredInFiles >= 10 && (localData.Count % (_numDataStoredInFiles / 10)) == 0)
progressCallback(localData.Count / (double)_numDataStoredInFiles);
}

file.Close();
}
Expand All @@ -186,7 +187,7 @@ public override List<Datum> GetDataForRemoteDataStore(CancellationToken cancella

// start writing to new path if we're still running
if (Running)
FindNewPath();
WriteToNewPath();

return localData;
}
Expand All @@ -200,7 +201,7 @@ public override void ClearDataCommittedToRemoteDataStore(List<Datum> dataCommitt

HashSet<Datum> hashDataCommittedToRemote = new HashSet<Datum>(dataCommittedToRemote); // for quick access via hashing

// clear remote-committed data from all local files
// clear remote-committed data from all files
foreach (string path in Directory.GetFiles(StorageDirectory))
{
SensusServiceHelper.Get().Logger.Log("Clearing remote-committed data from \"" + path + "\".", LoggingLevel.Debug, GetType());
Expand All @@ -227,54 +228,54 @@ public override void ClearDataCommittedToRemoteDataStore(List<Datum> dataCommitt
file.Close();
}

if (uncommittedDataCount == 0) // all data in local file were committed to remote data store -- delete local and filtered files
File.Delete(path);

// if there were no uncommitted data in the file, the uncommitted data file will be empty -- delete it
if (uncommittedDataCount == 0)
{
SensusServiceHelper.Get().Logger.Log("Cleared all data from local file. Deleting file.", LoggingLevel.Debug, GetType());

File.Delete(path);
File.Delete(uncommittedDataPath);
}
else // data from local file were not committed to the remote data store -- move filtered path to local path and retry sending to remote store next time
// if there were uncommitted data in the file, replace it with the file holding the uncommitted data -- it will be committed next time
else
{
SensusServiceHelper.Get().Logger.Log(uncommittedDataCount + " data elements in local file were not committed to remote data store.", LoggingLevel.Debug, GetType());

File.Delete(path);
File.Move(uncommittedDataPath, path);
}
}

// reinitialize file if we're running
if (Running)
FindNewPath();
WriteToNewPath();

SensusServiceHelper.Get().Logger.Log("Finished clearing remote-committed data elements.", LoggingLevel.Normal, GetType());
}
}

/// <summary>
/// Finds a new path to write to. Should be called from a locked context.
/// </summary>
private void FindNewPath()
private void WriteToNewPath()
{
_path = null;
int pathNumber = 0;
while (pathNumber++ < int.MaxValue && _path == null)
lock (_locker)
{
try
_path = null;
int pathNumber = 0;
while (pathNumber++ < int.MaxValue && _path == null)
{
_path = Path.Combine(StorageDirectory, pathNumber.ToString());
}
catch (Exception ex)
{
throw new DataStoreException("Failed to get path to local file: " + ex.Message);
try
{
_path = Path.Combine(StorageDirectory, pathNumber.ToString());
}
catch (Exception ex)
{
throw new DataStoreException("Failed to get path to local file: " + ex.Message);
}

if (File.Exists(_path))
_path = null;
}

if (File.Exists(_path))
_path = null;
if (_path == null)
throw new DataStoreException("Failed to find new path.");
}

if (_path == null)
throw new DataStoreException("Failed to find new path.");
}

public override void Clear()
Expand Down
3 changes: 2 additions & 1 deletion SensusService/DataStores/Local/RamLocalDataStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using System.Linq;
using System;
using System.Threading;
using SensusUI.UiProperties;

namespace SensusService.DataStores.Local
{
Expand All @@ -26,7 +27,7 @@ public class RamLocalDataStore : LocalDataStore

private readonly object _locker = new object();

protected override string DisplayName
public override string DisplayName
{
get { return "RAM"; }
}
Expand Down
2 changes: 1 addition & 1 deletion SensusService/DataStores/Remote/AmazonS3RemoteDataStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public string CognitoIdentityPoolId
}
}

protected override string DisplayName
public override string DisplayName
{
get
{
Expand Down
3 changes: 2 additions & 1 deletion SensusService/DataStores/Remote/ConsoleRemoteDataStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Threading;
using SensusUI.UiProperties;

namespace SensusService.DataStores.Remote
{
public class ConsoleRemoteDataStore : RemoteDataStore
{
protected override string DisplayName
public override string DisplayName
{
get { return "Console"; }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public string Key
set { _key = value; }
}

protected override string DisplayName
public override string DisplayName
{
get { return "Microsoft Azure"; }
}
Expand Down
33 changes: 16 additions & 17 deletions SensusUI/DataStorePage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace SensusUI
/// Displays a data store.
/// </summary>
public class DataStorePage : ContentPage
{
{
/// <summary>
/// Initializes a new instance of the <see cref="SensusUI.DataStorePage"/> class.
/// </summary>
Expand Down Expand Up @@ -63,8 +63,7 @@ public DataStorePage(Protocol protocol, DataStore dataStore, bool local, bool ne

clearButton.Clicked += async (o, e) =>
{
// display the name as it's currently shown on the form (i.e., in the passed-in data store rather than the protocols)
if (await DisplayAlert("Clear data from " + dataStore.Name + "?", "This action cannot be undone.", "Clear", "Cancel"))
if (await DisplayAlert("Clear data from " + protocol.LocalDataStore.DisplayName + "?", "This action cannot be undone.", "Clear", "Cancel"))
protocol.LocalDataStore.Clear(); // clear the protocol's local data store
};

Expand All @@ -82,13 +81,13 @@ public DataStorePage(Protocol protocol, DataStore dataStore, bool local, bool ne
};

shareLocalDataButton.Clicked += async (o, e) =>
{
// share the protocol's local data store if it has data in it
if(protocol.LocalDataStore.DataCount > 0)
await Navigation.PushAsync(new ShareLocalDataStorePage(protocol.LocalDataStore));
else
UiBoundSensusServiceHelper.Get(true).FlashNotificationAsync("Local data store contains no data to share.");
};
{
// share the protocol's local data store if it has data in it
if (protocol.LocalDataStore.DataCount > 0)
await Navigation.PushAsync(new ShareLocalDataStorePage(protocol.LocalDataStore));
else
UiBoundSensusServiceHelper.Get(true).FlashNotificationAsync("Local data store contains no data to share.");
};

buttonStack.Children.Add(shareLocalDataButton);
}
Expand All @@ -101,14 +100,14 @@ public DataStorePage(Protocol protocol, DataStore dataStore, bool local, bool ne
};

okayButton.Clicked += async (o, e) =>
{
if (local)
protocol.LocalDataStore = dataStore as LocalDataStore;
else
protocol.RemoteDataStore = dataStore as RemoteDataStore;
{
if (local)
protocol.LocalDataStore = dataStore as LocalDataStore;
else
protocol.RemoteDataStore = dataStore as RemoteDataStore;

await Navigation.PopAsync();
};
await Navigation.PopAsync();
};

buttonStack.Children.Add(okayButton);

Expand Down
4 changes: 2 additions & 2 deletions SensusUI/ProtocolPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -231,11 +231,11 @@ private async void CreateDataStore(bool local)
.Where(t => !t.IsAbstract && t.IsSubclassOf(dataStoreType))
.Select(t => Activator.CreateInstance(t))
.Cast<DataStore>()
.OrderBy(d => d.Name)
.OrderBy(d => d.DisplayName)
.ToList();

string cancelButtonName = "Cancel";
string selected = await DisplayActionSheet("Select " + (local ? "Local" : "Remote") + " Data Store", cancelButtonName, null, dataStores.Select((d, i) => (i + 1) + ") " + d.Name).ToArray());
string selected = await DisplayActionSheet("Select " + (local ? "Local" : "Remote") + " Data Store", cancelButtonName, null, dataStores.Select((d, i) => (i + 1) + ") " + d.DisplayName).ToArray());
if (!string.IsNullOrWhiteSpace(selected) && selected != cancelButtonName)
await Navigation.PushAsync(new DataStorePage(_protocol, dataStores[int.Parse(selected.Substring(0, selected.IndexOf(")"))) - 1], local, true));
}
Expand Down
45 changes: 36 additions & 9 deletions SensusUI/UiProperties/UiProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,32 @@ namespace SensusUI.UiProperties
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public abstract class UiProperty : Attribute
{
/// <summary>
/// Gets the UiProperty attribute associated with a property. For some reason, PropertyInfo.GetCustomAttributes doesn't return the
/// UiProperty attribute placed on abstract properties that are overridden.
/// </summary>
/// <returns>The user interface property attribute.</returns>
/// <param name="property">Property.</param>
public static UiProperty GetUiPropertyAttribute(PropertyInfo property)
{
if (property == null)
return null;

UiProperty attribute = property.GetCustomAttribute<UiProperty>();

if (attribute == null)
{
Type parentType = property.ReflectedType.BaseType;

if (parentType == null)
return null;
else
return GetUiPropertyAttribute(parentType.GetProperty(property.Name));
}
else
return attribute;
}

/// <summary>
/// Gets a list of StackLayout objects associated with properties in an object that have been
/// decorated with a UiProperty attribute.
Expand All @@ -36,11 +62,12 @@ public static List<StackLayout> GetPropertyStacks(object o)
{
List<StackLayout> propertyStacks = new List<StackLayout>();

List<Tuple<PropertyInfo, UiProperty>> propertyUiElements = o.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Select(p => new Tuple<PropertyInfo, UiProperty>(p, Attribute.GetCustomAttribute(p, typeof(UiProperty), true) as UiProperty))
.Where(pp => pp.Item2 != null)
.OrderBy(pp => pp.Item2._order).ToList();
List<Tuple<PropertyInfo, UiProperty>> propertyUiElements =
o.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Select(p => new Tuple<PropertyInfo, UiProperty>(p, GetUiPropertyAttribute(p)))
.Where(p => p.Item2 != null)
.OrderBy(p => p.Item2._order).ToList();

foreach (Tuple<PropertyInfo, UiProperty> propertyUiElement in propertyUiElements)
{
Expand Down Expand Up @@ -175,10 +202,10 @@ public static List<StackLayout> GetPropertyStacks(object o)
picker.SelectedIndex = items.IndexOf(propertyUiElement.Item1.GetValue(o));

picker.SelectedIndexChanged += (oo, ee) =>
{
if (picker.SelectedIndex >= 0)
propertyUiElement.Item1.SetValue(o, items[picker.SelectedIndex]);
};
{
if (picker.SelectedIndex >= 0)
propertyUiElement.Item1.SetValue(o, items[picker.SelectedIndex]);
};

view = picker;
}
Expand Down

0 comments on commit ccbdeea

Please sign in to comment.