create database interface and TestProvider

start padding out Pilots screen with create/edit/delete of Pilot
pad out monitor page.
pad out RaceDirector page, add create race session code for a standard race.
start splitting code into separate js files.
This commit is contained in:
chris.watts90@outlook.com 2017-06-17 10:43:56 +01:00
parent a07923d214
commit c0bbf5c0fb
15 changed files with 352 additions and 184 deletions

View File

@ -1,6 +1,4 @@
using System; using System.Runtime.InteropServices;
using System.Collections.Generic;
using Interfaces;
using Nancy; using Nancy;
namespace RaceLapTimer.ApiControllers namespace RaceLapTimer.ApiControllers
@ -20,141 +18,4 @@ namespace RaceLapTimer.ApiControllers
return Response.AsJson(new { data=_provider.GetRaceSessions(true)}); return Response.AsJson(new { data=_provider.GetRaceSessions(true)});
} }
} }
public interface IDbProvider
{
List<RaceSession> GetRaceSessions(bool getActive);
List<Pilot> GetPilotsList();
Pilot GetPilot(int pilotId);
bool CreatePilot(Pilot pilot);
int GetLastScannedId();
void StoreTransponderLog(SatelliteLog log);
}
public class TestProvider : IDbProvider
{
public List<RaceSession> GetRaceSessions(bool getActive)
{
return new List<RaceSession>
{
#region Session 1
new RaceSession
{
Active = true,
CreatedAt = DateTime.UtcNow,
HotSeatEnabled = false,
Id = 1,
IdleTimeSeconds = 400,
Mode = RaceMode.STANDARD,
MaxLaps = 20,
SatelliteCount = 1,
TimePenaltyPerSatellite = 100,
Title = "TEST Session 1"
},
#endregion
#region Session 2
new RaceSession
{
Active = true,
CreatedAt = DateTime.UtcNow,
HotSeatEnabled = false,
Id = 2,
IdleTimeSeconds = 400,
Mode = RaceMode.STANDARD,
MaxLaps = 10,
SatelliteCount = 1,
TimePenaltyPerSatellite = 100,
Title = "TEST Session 2"
},
#endregion
#region Session 3
new RaceSession
{
Active = getActive,
CreatedAt = DateTime.UtcNow,
HotSeatEnabled = false,
Id = 3,
IdleTimeSeconds = 400,
Mode = RaceMode.STANDARD,
MaxLaps = 10,
SatelliteCount = 1,
TimePenaltyPerSatellite = 100,
Title = "TEST Session 3"
},
#endregion
#region Session 3
new RaceSession
{
Active = getActive,
CreatedAt = DateTime.UtcNow,
HotSeatEnabled = false,
Id = 4,
IdleTimeSeconds = 400,
Mode = RaceMode.STANDARD,
MaxLaps = 10,
SatelliteCount = 1,
TimePenaltyPerSatellite = 100,
Title = "TEST Session 4"
}
#endregion
};
}
public List<Pilot> GetPilotsList()
{
return new List<Pilot>
{
new Pilot
{
Name = "Pilot1",
TeamName = "PilotTeam 1",
ThingyName = "Kart1",
TransponderToken = 222,
ImageUrl = "images/pilotPic.png"
},
new Pilot
{
Name = "Pilot 2",
TeamName = "Pilot Team 2",
ThingyName = "Kart2",
TransponderToken = 200
}
};
}
public Pilot GetPilot(int pilotId)
{
return new Pilot
{
Id = pilotId,
Name = "Pilot " + pilotId,
TeamName = "Team " + pilotId,
ThingyName = "Id:" + pilotId,
TransponderToken = pilotId
};
}
public bool CreatePilot(Pilot pilot)
{
var rand = new Random();
var numb = rand.NextDouble();
return numb > 0.5;
}
public int GetLastScannedId()
{
var rand = new Random();
return rand.Next(0, 100);
}
public void StoreTransponderLog(SatelliteLog log)
{
//do nothing.
}
}
} }

View File

@ -7,12 +7,21 @@ namespace RaceLapTimer.ApiControllers
{ {
public class PilotApiModule:NancyModule public class PilotApiModule:NancyModule
{ {
private IDbProvider _provider; private readonly IDbProvider _provider;
public PilotApiModule(IDbProvider provider) : base("/api/pilot") public PilotApiModule(IDbProvider provider) : base("/api/pilot")
{ {
_provider = provider; _provider = provider;
Get["/{id}"] = args => GetPilot(args); Get["/{id}"] = args => GetPilot(args);
Post["/edit"] = args => EditCreatePilot(args); Post["/edit"] = args => EditCreatePilot(args);
Post["/create"] = args => EditCreatePilot(args);
Get["/delete/{id}"] = args => DeletePilot(args);
}
private dynamic DeletePilot(dynamic args)
{
var res = _provider.DeletePilot(args.Id);
return Response.AsRedirect("/pilots");
} }
private dynamic GetPilot(dynamic args) private dynamic GetPilot(dynamic args)
@ -25,7 +34,9 @@ namespace RaceLapTimer.ApiControllers
{ {
var pilotObject = this.Bind<Pilot>(); var pilotObject = this.Bind<Pilot>();
var resp = _provider.CreatePilot(pilotObject); var resp = _provider.CreatePilot(pilotObject);
return Response.AsRedirect("/pilots"); if(resp)
return new {url = "/pilots"};
return "error";
} }
} }
} }

View File

@ -2,6 +2,7 @@
using Interfaces; using Interfaces;
using Nancy; using Nancy;
using Nancy.ModelBinding; using Nancy.ModelBinding;
using Nancy.Responses;
namespace RaceLapTimer.ApiControllers namespace RaceLapTimer.ApiControllers
{ {
@ -31,7 +32,7 @@ namespace RaceLapTimer.ApiControllers
private dynamic CreateRaceSession(dynamic args) private dynamic CreateRaceSession(dynamic args)
{ {
var postObject = this.Bind<RaceSession>(); var postObject = this.Bind<RaceSession>();
return Response.AsRedirect("/racedirector"); return new {url="/racedirector"};
} }
} }
} }

View File

@ -16,6 +16,12 @@ namespace RaceLapTimer
{ {
public class FormsAuthBootstrapper : NinjectNancyBootstrapper public class FormsAuthBootstrapper : NinjectNancyBootstrapper
{ {
protected override void ApplicationStartup(IKernel container, IPipelines pipelines)
{
base.ApplicationStartup(container, pipelines);
Nancy.Security.Csrf.Enable(pipelines);
}
protected override void ConfigureApplicationContainer(IKernel container) protected override void ConfigureApplicationContainer(IKernel container)
{ {
// We don't call "base" here to prevent auto-discovery of // We don't call "base" here to prevent auto-discovery of
@ -25,7 +31,7 @@ namespace RaceLapTimer
container.Bind<IConfigFilePathProvider>().To<ConfigFilePathProvider>(); container.Bind<IConfigFilePathProvider>().To<ConfigFilePathProvider>();
container.Bind<IContainerHelper>().To<ContainerHelper>(); container.Bind<IContainerHelper>().To<ContainerHelper>();
container.Bind<INotifierManager>().To<NotificationManager>().InSingletonScope(); container.Bind<INotifierManager>().To<NotificationManager>().InSingletonScope();
container.Bind<IDbProvider>().To<TestProvider>(); container.Bind<IDbProvider>().To<TestProvider>().InSingletonScope();
//load dynamic plugins..: //load dynamic plugins..:
var cfgFilePath = Path.Combine(container.Get<IConfigFilePathProvider>().GetPath, "NinjectConfig.xml"); var cfgFilePath = Path.Combine(container.Get<IConfigFilePathProvider>().GetPath, "NinjectConfig.xml");
@ -81,6 +87,9 @@ namespace RaceLapTimer
conventions.StaticContentsConventions.Add( conventions.StaticContentsConventions.Add(
StaticContentConventionBuilder.AddDirectory("images","images") StaticContentConventionBuilder.AddDirectory("images","images")
); );
conventions.StaticContentsConventions.Add(
StaticContentConventionBuilder.AddDirectory("js", "js")
);
} }
} }
} }

View File

@ -0,0 +1,22 @@
using System.Collections.Generic;
using Interfaces;
namespace RaceLapTimer.ApiControllers
{
public interface IDbProvider
{
List<RaceSession> GetRaceSessions(bool getActive);
List<Pilot> GetPilotsList();
Pilot GetPilot(int pilotId);
bool CreatePilot(Pilot pilot);
int GetLastScannedId();
void StoreTransponderLog(SatelliteLog log);
bool DeletePilot(int id);
}
}

View File

@ -13,6 +13,12 @@ namespace RaceLapTimer.Modules
public PilotModule() : base("/pilot") public PilotModule() : base("/pilot")
{ {
this.RequiresAuthentication(); this.RequiresAuthentication();
Get["/edit/{id}"] = args => EditPilot(args);
}
private dynamic EditPilot(dynamic args)
{
return View["EditPilot.cshtml"];
} }
} }
} }

View File

@ -114,6 +114,7 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="IDbProvider.cs" />
<Compile Include="ApiControllers\LapTrackApiModule.cs" /> <Compile Include="ApiControllers\LapTrackApiModule.cs" />
<Compile Include="ApiControllers\MonitorApiModule.cs" /> <Compile Include="ApiControllers\MonitorApiModule.cs" />
<Compile Include="ApiControllers\PilotApiModule.cs" /> <Compile Include="ApiControllers\PilotApiModule.cs" />
@ -121,6 +122,7 @@
<Compile Include="ApiControllers\SatelliteApiModule.cs" /> <Compile Include="ApiControllers\SatelliteApiModule.cs" />
<Compile Include="ApiControllers\SoundApiModule.cs" /> <Compile Include="ApiControllers\SoundApiModule.cs" />
<Compile Include="ApiControllers\SystemApiModule.cs" /> <Compile Include="ApiControllers\SystemApiModule.cs" />
<Compile Include="TestProvider.cs" />
<Compile Include="AuthenticationBootstrapper.cs" /> <Compile Include="AuthenticationBootstrapper.cs" />
<Compile Include="ConfigFilePathProvider.cs" /> <Compile Include="ConfigFilePathProvider.cs" />
<Compile Include="Extensions\Notifications\ContainerHelper.cs" /> <Compile Include="Extensions\Notifications\ContainerHelper.cs" />
@ -179,7 +181,6 @@
<ItemGroup> <ItemGroup>
<Folder Include="www\css\" /> <Folder Include="www\css\" />
<Folder Include="www\fonts\" /> <Folder Include="www\fonts\" />
<Folder Include="www\js\" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Helpers.Encrytion\Helpers.Encryption.csproj"> <ProjectReference Include="..\Helpers.Encrytion\Helpers.Encryption.csproj">
@ -201,6 +202,15 @@
<Content Include="www\images\pilotPic.png"> <Content Include="www\images\pilotPic.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<Content Include="www\js\pilots.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="www\js\raceDirector.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="www\js\supportingPages.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\Nancy.Viewengines.Razor.1.4.3\build\Nancy.ViewEngines.Razor.targets" Condition="Exists('..\packages\Nancy.Viewengines.Razor.1.4.3\build\Nancy.ViewEngines.Razor.targets')" /> <Import Project="..\packages\Nancy.Viewengines.Razor.1.4.3\build\Nancy.ViewEngines.Razor.targets" Condition="Exists('..\packages\Nancy.Viewengines.Razor.1.4.3\build\Nancy.ViewEngines.Razor.targets')" />

View File

@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Interfaces;
namespace RaceLapTimer.ApiControllers
{
public class TestProvider : IDbProvider
{
readonly List<Pilot> _pilots;
public TestProvider()
{
#region Pilots
_pilots = new List<Pilot>
{
new Pilot
{
Id=1,
Name = "Pilot1",
TeamName = "PilotTeam 1",
ThingyName = "Kart1",
TransponderToken = 222,
ImageUrl = "images/pilotPic.png"
},
new Pilot
{
Id=2,
Name = "Pilot 2",
TeamName = "Pilot Team 2",
ThingyName = "Kart2",
TransponderToken = 200
}
};
#endregion
}
public List<RaceSession> GetRaceSessions(bool getActive)
{
return new List<RaceSession>
{
#region Session 1
new RaceSession
{
Active = true,
CreatedAt = DateTime.UtcNow,
HotSeatEnabled = false,
Id = 1,
IdleTimeSeconds = 400,
Mode = RaceMode.STANDARD,
MaxLaps = 20,
SatelliteCount = 1,
TimePenaltyPerSatellite = 100,
Title = "TEST Session 1"
},
#endregion
#region Session 2
new RaceSession
{
Active = true,
CreatedAt = DateTime.UtcNow,
HotSeatEnabled = false,
Id = 2,
IdleTimeSeconds = 400,
Mode = RaceMode.STANDARD,
MaxLaps = 10,
SatelliteCount = 1,
TimePenaltyPerSatellite = 100,
Title = "TEST Session 2"
},
#endregion
#region Session 3
new RaceSession
{
Active = getActive,
CreatedAt = DateTime.UtcNow,
HotSeatEnabled = false,
Id = 3,
IdleTimeSeconds = 400,
Mode = RaceMode.STANDARD,
MaxLaps = 10,
SatelliteCount = 1,
TimePenaltyPerSatellite = 100,
Title = "TEST Session 3"
},
#endregion
#region Session 3
new RaceSession
{
Active = getActive,
CreatedAt = DateTime.UtcNow,
HotSeatEnabled = false,
Id = 4,
IdleTimeSeconds = 400,
Mode = RaceMode.STANDARD,
MaxLaps = 10,
SatelliteCount = 1,
TimePenaltyPerSatellite = 100,
Title = "TEST Session 4"
}
#endregion
};
}
public List<Pilot> GetPilotsList()
{
return _pilots;
}
public Pilot GetPilot(int pilotId)
{
return _pilots.FirstOrDefault(x => x.Id == pilotId);
}
public bool CreatePilot(Pilot pilot)
{
if (_pilots.Any(x => x.TransponderToken == pilot.TransponderToken))
return false;
pilot.Id= _pilots.Max(x => x.Id) + 1;
_pilots.Add(pilot);
return true;
}
public bool DeletePilot(int id)
{
return _pilots.Remove(_pilots.FirstOrDefault(x => x.Id == id));
}
public int GetLastScannedId()
{
var rand = new Random();
return rand.Next(0, 100);
}
public void StoreTransponderLog(SatelliteLog log)
{
//do nothing.
}
}
}

View File

@ -7,18 +7,18 @@
<table data-bind="with: raceSessions" class="table table-striped"> <table data-bind="with: raceSessions" class="table table-striped">
<thead> <thead>
<tr> <tr>
<td>Race Name</td> <td>Race Name</td>
<td>Idle Time (s)</td> <td>Idle Time (s)</td>
<td>Lap Count</td> <td>Lap Count</td>
</tr> </tr>
</thead> </thead>
<tbody data-bind="foreach: data"> <tbody data-bind="foreach: data">
<tr> <tr>
<td data-bind="text: title"></td> <td data-bind="text: title"></td>
<td data-bind="text: idleTimeSeconds"></td> <td data-bind="text: idleTimeSeconds"></td>
<td data-bind="text: maxLaps"></td> <td data-bind="text: maxLaps"></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<script type="text/javascript"> <script type="text/javascript">
@ -28,12 +28,19 @@
self.raceSessions = ko.observable(); self.raceSessions = ko.observable();
$.get("/api/monitor", {}, self.raceSessions); $.get("/api/monitor", {}, self.raceSessions);
self.tmrHandle = setInterval(function () { self.tmrHandle = setTimeout(function () {
self.getData(); self.getData();
}, 2000); }, 2000);
self.getData = function() { self.getData = function () {
$.get("/api/monitor", {}, self.raceSessions); $.get("/api/monitor", {}, self.raceSessions)
.done(function () {
console.log("done");
})
.always(function() {
self.tmrHandle = setTimeout(self.getData, 2000);
})
;
}; };
}; };

View File

@ -2,33 +2,60 @@
@{ @{
Layout = "razor-layout.cshtml"; Layout = "razor-layout.cshtml";
ViewBag.Title = "Pilots"; ViewBag.Title = "Pilots";
ViewBag.CreateUserEndpoint = "/api/pilot/create";
} }
<h3>@ViewBag.Title</h3> <h3>@ViewBag.Title</h3>
<button class="pull-right btn btn-default">Create</button>
<form class="form-inline" id="createPilot" action="@ViewBag.CreateUserEndpoint" method="post"
onsubmit="return jsonPostForm('createPilot', '@ViewBag.CreateUserEndpoint')">
@Html.AntiForgeryToken()
<div class="form-group">
<label for="pilotName">Name</label>
<input placeholder="Jon Doe" class="form-control" required="required" type="text" name="Name" id="pilotName">
</div>
<div class="form-group">
<label for="pilotTransponderToken">Transponder Token</label>
<input placeholder="XXXXXX" class="form-control" required="required" type="number" name="TransponderToken" id="pilotTransponderToken">
</div>
<div class="form-group">
<label for="pilotKartName">Kart</label>
<input placeholder="Kart Attack" class="form-control" required="required" type="text" name="ThingyName" id="pilotKartName">
</div>
<div class="form-group">
<label for="pilotTeamName">Team</label>
<input placeholder="" class="form-control" type="text" name="TeamName" id="pilotTeamName">
</div>
<button class="btn btn-primary" type="submit">Create</button>
<div></div>
</form>
<table class="table table-striped" data-bind="with: pilotsList"> <table class="table table-striped" data-bind="with: pilotsList">
<thead> <thead>
<tr> <tr>
<th></th> <th></th>
<th>Pilot Name</th> <th>Pilot Name</th>
<th>Transponder Number</th> <th>Transponder Number</th>
<th>Kart Name</th> <th>Kart Name</th>
<th>Team Name</th> <th>Team Name</th>
<th></th> <th class="col-md-1"></th>
<th></th> <th class="col-md-1"></th>
</tr> <th class="col-md-1"></th>
</tr>
</thead> </thead>
<tbody data-bind="foreach: data"> <tbody data-bind="foreach: data">
<tr> <tr>
<td><img data-bind="attr:{src: imageUrl}" alt="pilotLogo" height="50" width="50"/></td> <td><img data-bind="attr:{src: imageUrl}" alt="pilotLogo" height="50" width="50"/></td>
<td data-bind="text: name"></td> <td data-bind="text: name"></td>
<td data-bind="text: transponderToken">-</td> <td data-bind="text: transponderToken">-</td>
<td data-bind="text: thingyName">-</td> <td data-bind="text: thingyName">-</td>
<td data-bind="text: teamName">-</td> <td data-bind="text: teamName">-</td>
<td><button class="btn btn-default" type="button">Edit</button></td> <td class="col-md-1 text-center"><button class="btn btn-danger" type="button">Remove Token</button></td>
<td><button class="btn btn-default" type="button">Delete</button></td> <td class="col-md-1 text-center"><a class="btn btn-warning" data-bind="attr:{href: '/pilot/edit/'+id}">Edit</a></td>
</tr> <td class="col-md-1 text-center"><a class="btn btn-info" data-bind="attr:{href: '/api/pilot/delete/'+id}">Delete</a></td>
</tr>
</tbody> </tbody>
</table> </table>
<script type="text/javascript" src="/js/supportingPages.js"></script>
<script type="text/javascript"> <script type="text/javascript">
function ViewModel() { function ViewModel() {
"use strict"; "use strict";

View File

@ -15,14 +15,16 @@
<h4>Last Lap Times</h4> <h4>Last Lap Times</h4>
<label>None</label> <label>None</label>
<form action="@ViewBag.CreateRaceApiEndpoint" method="post" class="form-group"> <form id="createStandardRace" action="@ViewBag.CreateRaceApiEndpoint" method="POST" class="form-group"
onsubmit="return jsonPostForm('createStandardRace', '@ViewBag.CreateRaceApiEndpoint')">
<div class="form-group"> <div class="form-group">
<label>Race Title</label> <label for="Title">Race Title</label>
<input for="RaceTitle" type="text" class="form-control" id="raceTitle" placeholder="Awesome Race"/> <input name="Title" id="Title" type="text" class="form-control" placeholder="Awesome Race" autocomplete="off"/>
</div> </div>
<div class="form-group"> <div class="form-group">
<label>Idle Time (s)</label> <label for="IdleTimeSeconds">Idle Time (s)</label>
<input for="RaceIdleTime" type="text" class="form-control" id="raceIdleTime" placeholder="0"/> <input name="IdleTimeSeconds" id="IdleTimeSeconds" type="number" class="form-control" placeholder="0" autocomplete="off"/>
</div> </div>
<button type="submit" >Submit</button> <button type="submit">Submit</button>
</form> </form>
<script type="text/javascript" src="js/supportingPages.js"></script>

View File

@ -77,7 +77,7 @@
</div> </div>
</navbar> </navbar>
<div class='container-fluid'> <div class='container-fluid'>
<div class='row' style="margin-top: 40px;"> <div class='row' style="margin-top: 55px;">
<div class='col-xs-12 center'> <div class='col-xs-12 center'>
@RenderBody() @RenderBody()
</div> </div>

View File

@ -0,0 +1,16 @@
function ViewModel() {
"use strict";
var self = this;
self.pilotsList = ko.observable();
$.get("/api/pilots", {}, self.pilotsList);
self.getData = function () {
$.get("/api/pilots", {}, self.pilotsList);
};
};
ViewModel.prototype.dispose = function () {
"use strict";
};
ko.applyBindings(new ViewModel());

View File

@ -0,0 +1,27 @@
function ViewModel() {
"use strict";
var self = this;
self.raceSessions = ko.observable();
$.get("/api/monitor", {}, self.raceSessions);
self.tmrHandle = setTimeout(function () {
self.getData();
}, 2000);
self.getData = function () {
$.get("/api/monitor", {}, self.raceSessions)
.done(function () {
console.log("done");
})
.always(function () {
self.tmrHandle = setTimeout(self.getData, 2000);
});
};
};
ViewModel.prototype.dispose = function () {
"use strict";
window.clearInterval(self.tmrHandle);
console.log("disposed of timer");
};
ko.applyBindings(new ViewModel());

View File

@ -0,0 +1,29 @@
function jsonPostForm(formName, url) {
var formData = JSON.stringify($(("#" + formName)).serializeObject());
console.log(formData + "url: " + url);
$.ajax({
type: "POST",
url: url,
data: formData,
dataType: "json",
contentType: "application/json"
}).done(function(a) {
window.location.replace(a.url);
});
return false;
};
$.fn.serializeObject = function () {
var o = {};
var a = this.serializeArray();
$.each(a, function () {
if (o[this.name] !== undefined) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || "");
} else {
o[this.name] = this.value || "";
}
});
return o;
};