From a30a9a545363c53efda1f2ff1f3ad39ac4e872a1 Mon Sep 17 00:00:00 2001 From: "chris.watts90@outlook.com" Date: Fri, 12 May 2017 17:38:14 +0100 Subject: [PATCH] Add updatede nancy api modules and views. --- .gitignore | 1 + RaceLapTimer/Interfaces/Pilot.cs | 2 + .../ApiControllers/InfoApiModule.cs | 12 +- .../ApiControllers/LapTrackApiModule.cs | 46 +++++- .../ApiControllers/MonitorApiModule.cs | 156 +++++++++++++++++- .../ApiControllers/PilotApiModule.cs | 22 ++- .../ApiControllers/PilotsApiModule.cs | 19 +++ .../ApiControllers/RaceSessionApiModule.cs | 21 ++- .../ApiControllers/SatelliteApiModule.cs | 29 +++- .../ApiControllers/SystemApiModule.cs | 19 ++- .../AuthenticationBootstrapper.cs | 11 ++ RaceLapTimer/RaceLapTimer/RaceLapTimer.csproj | 7 +- .../RaceLapTimer/www/Views/Monitor.cshtml | 64 +++---- .../RaceLapTimer/www/Views/Pilots.cshtml | 69 ++++---- .../www/Views/razor-layout.cshtml | 63 ++++--- .../RaceLapTimer/www/images/pilotPic.png | Bin 0 -> 5046 bytes 16 files changed, 439 insertions(+), 102 deletions(-) create mode 100644 RaceLapTimer/RaceLapTimer/ApiControllers/PilotsApiModule.cs create mode 100644 RaceLapTimer/RaceLapTimer/www/images/pilotPic.png diff --git a/.gitignore b/.gitignore index 9ef596f..52a509f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ **/obj/** **/packages/** **/.vs/** +**dotsettings diff --git a/RaceLapTimer/Interfaces/Pilot.cs b/RaceLapTimer/Interfaces/Pilot.cs index 9a092a5..82a371c 100644 --- a/RaceLapTimer/Interfaces/Pilot.cs +++ b/RaceLapTimer/Interfaces/Pilot.cs @@ -2,6 +2,8 @@ { public class Pilot { + public int Id { get; set; } + public string ImageUrl { get; set; } public string Name { get; set; } public int TransponderToken { get; set; } public string ThingyName { get; set; } diff --git a/RaceLapTimer/RaceLapTimer/ApiControllers/InfoApiModule.cs b/RaceLapTimer/RaceLapTimer/ApiControllers/InfoApiModule.cs index 73cc6af..492d6eb 100644 --- a/RaceLapTimer/RaceLapTimer/ApiControllers/InfoApiModule.cs +++ b/RaceLapTimer/RaceLapTimer/ApiControllers/InfoApiModule.cs @@ -4,9 +4,17 @@ namespace RaceLapTimer.ApiControllers { public class InfoApiModule:NancyModule { - public InfoApiModule() : base("/api/info") + private IDbProvider _provider; + public InfoApiModule(IDbProvider provider) : base("/api/info") { - + _provider = provider; + + Get["/lastscanned"] = args => GetLastScannedId(); + } + + private dynamic GetLastScannedId() + { + return Response.AsJson(_provider.GetLastScannedId()); } } } diff --git a/RaceLapTimer/RaceLapTimer/ApiControllers/LapTrackApiModule.cs b/RaceLapTimer/RaceLapTimer/ApiControllers/LapTrackApiModule.cs index dc55c35..8bb37dd 100644 --- a/RaceLapTimer/RaceLapTimer/ApiControllers/LapTrackApiModule.cs +++ b/RaceLapTimer/RaceLapTimer/ApiControllers/LapTrackApiModule.cs @@ -1,12 +1,52 @@ -using Nancy; +using System; +using Nancy; namespace RaceLapTimer.ApiControllers { public class LapTrackApiModule:NancyModule { - public LapTrackApiModule() : base("/api/laptrack") + private IDbProvider _dbProvider; + public LapTrackApiModule(IDbProvider provider) : base("/api/laptrack") { - + _dbProvider = provider; + Get["/create"] = args => LogLapTime(args); } + + private dynamic LogLapTime(dynamic args) + { + var transponderToken = Convert.ToInt32(Request.Query["transponder_token"]); + var lapTimeInMs = Convert.ToInt32(Request.Query["lap_time_in_ms"]); + var log = new SatelliteLog + { + TransponderToken = transponderToken, + LapTimeMS = lapTimeInMs + }; + _dbProvider.StoreTransponderLog(log); + return HttpStatusCode.OK; + } + /* + a non-competition race will result in: 66f92adaf43f8f6ad96204ec being returned? + a competition race will result in: + { + "id": 7, + "created_at": "2017-05-10T19:31:27.517Z", + "created_at_timestamp": 1494444687, + "lap_num": 2, + "lap_time": 95000, + "pilot_id": 2, + "pilot": { + "name": "test2", + "quad": "testste", + "team": "tetststs" + }, + "race_session_id": 6, + "race_session": { + "title": "Competition 1", + "mode": "competition" + } + } + + */ + } } diff --git a/RaceLapTimer/RaceLapTimer/ApiControllers/MonitorApiModule.cs b/RaceLapTimer/RaceLapTimer/ApiControllers/MonitorApiModule.cs index 08bbddd..9e5e84d 100644 --- a/RaceLapTimer/RaceLapTimer/ApiControllers/MonitorApiModule.cs +++ b/RaceLapTimer/RaceLapTimer/ApiControllers/MonitorApiModule.cs @@ -1,12 +1,160 @@ -using Nancy; +using System; +using System.Collections.Generic; +using Interfaces; +using Nancy; namespace RaceLapTimer.ApiControllers { - public class MonitorApiModule:NancyModule + public class MonitorApiModule : NancyModule { - public MonitorApiModule() : base("api/monitor") + private IDbProvider _provider; + public MonitorApiModule(IDbProvider provider) : base("api/monitor") { - + _provider = provider; + + Get[""] = args => GetRootMonitorData(args); + } + + private dynamic GetRootMonitorData(dynamic args) + { + return Response.AsJson(new { data=_provider.GetRaceSessions(true)}); + } + } + + public interface IDbProvider + { + List GetRaceSessions(bool getActive); + + List GetPilotsList(); + + Pilot GetPilot(int pilotId); + + bool CreatePilot(Pilot pilot); + + int GetLastScannedId(); + + void StoreTransponderLog(SatelliteLog log); + } + + public class TestProvider : IDbProvider + { + public List GetRaceSessions(bool getActive) + { + return new List + { + #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 GetPilotsList() + { + return new List + { + 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. } } } diff --git a/RaceLapTimer/RaceLapTimer/ApiControllers/PilotApiModule.cs b/RaceLapTimer/RaceLapTimer/ApiControllers/PilotApiModule.cs index 8601923..d255ad2 100644 --- a/RaceLapTimer/RaceLapTimer/ApiControllers/PilotApiModule.cs +++ b/RaceLapTimer/RaceLapTimer/ApiControllers/PilotApiModule.cs @@ -1,13 +1,31 @@ using System.Web.Http; +using Interfaces; using Nancy; +using Nancy.ModelBinding; namespace RaceLapTimer.ApiControllers { public class PilotApiModule:NancyModule { - public PilotApiModule() : base("/api/pilot") + private IDbProvider _provider; + public PilotApiModule(IDbProvider provider) : base("/api/pilot") { - + _provider = provider; + Get["/{id}"] = args => GetPilot(args); + Post["/edit"] = args => EditCreatePilot(args); + } + + private dynamic GetPilot(dynamic args) + { + int pilotId = args.Id; + return Response.AsJson(_provider.GetPilot(pilotId)); + } + + private dynamic EditCreatePilot(dynamic args) + { + var pilotObject = this.Bind(); + var resp = _provider.CreatePilot(pilotObject); + return Response.AsRedirect("/pilots"); } } } diff --git a/RaceLapTimer/RaceLapTimer/ApiControllers/PilotsApiModule.cs b/RaceLapTimer/RaceLapTimer/ApiControllers/PilotsApiModule.cs new file mode 100644 index 0000000..f42ef4e --- /dev/null +++ b/RaceLapTimer/RaceLapTimer/ApiControllers/PilotsApiModule.cs @@ -0,0 +1,19 @@ +using Nancy; + +namespace RaceLapTimer.ApiControllers +{ + public class PilotsApiModule : NancyModule + { + private readonly IDbProvider _provider; + public PilotsApiModule(IDbProvider provider) : base("/api/pilots") + { + _provider = provider; + Get[""] = args => GetPilotsList(); + } + + private dynamic GetPilotsList() + { + return Response.AsJson(new { data = _provider.GetPilotsList() }); + } + } +} \ No newline at end of file diff --git a/RaceLapTimer/RaceLapTimer/ApiControllers/RaceSessionApiModule.cs b/RaceLapTimer/RaceLapTimer/ApiControllers/RaceSessionApiModule.cs index 05e11a7..114cebf 100644 --- a/RaceLapTimer/RaceLapTimer/ApiControllers/RaceSessionApiModule.cs +++ b/RaceLapTimer/RaceLapTimer/ApiControllers/RaceSessionApiModule.cs @@ -1,4 +1,5 @@ -using Interfaces; +using System.Collections.Generic; +using Interfaces; using Nancy; using Nancy.ModelBinding; @@ -6,9 +7,25 @@ namespace RaceLapTimer.ApiControllers { public class RaceSessionApiModule:NancyModule { - public RaceSessionApiModule() : base("/api/racesession") + private readonly IDbProvider _provider; + public RaceSessionApiModule(IDbProvider provider) : base("/api/racesession") { + _provider = provider; + Get[""] = args => GetRaceSessions(); Post["/create"] = args => CreateRaceSession(args); + Get["/historic"] = args => GetHistoricRaceSessions(); + } + + private dynamic GetHistoricRaceSessions() + { + var sessions = _provider.GetRaceSessions(false); + return Response.AsJson(new {data= sessions}); + } + + private dynamic GetRaceSessions() + { + var sessions = _provider.GetRaceSessions(true); + return Response.AsJson(new {data= sessions}); } private dynamic CreateRaceSession(dynamic args) diff --git a/RaceLapTimer/RaceLapTimer/ApiControllers/SatelliteApiModule.cs b/RaceLapTimer/RaceLapTimer/ApiControllers/SatelliteApiModule.cs index 0a0c65d..27bc244 100644 --- a/RaceLapTimer/RaceLapTimer/ApiControllers/SatelliteApiModule.cs +++ b/RaceLapTimer/RaceLapTimer/ApiControllers/SatelliteApiModule.cs @@ -1,12 +1,35 @@ -using Nancy; +using System; +using Nancy; namespace RaceLapTimer.ApiControllers { public class SatelliteApiModule:NancyModule { - public SatelliteApiModule() : base("/api/satellite") + private IDbProvider _provider; + public SatelliteApiModule(IDbProvider provider) : base("/api/satellite") { - + _provider = provider; + + Get[""] = args => LogNewLapSatelliteTime(args); + } + + private dynamic LogNewLapSatelliteTime(dynamic args) + { + var transponderToken = Convert.ToInt32(Request.Query["transponder_token"]); + var lapTimeMs = Convert.ToInt32(Request.Query["lap_time_in_ms"]); + var logObj = new SatelliteLog + { + TransponderToken = transponderToken, + LapTimeMS = lapTimeMs + }; + _provider.StoreTransponderLog(logObj); + return HttpStatusCode.OK; } } + + public class SatelliteLog + { + public int TransponderToken { get; set; } + public int LapTimeMS { get; set; } + } } diff --git a/RaceLapTimer/RaceLapTimer/ApiControllers/SystemApiModule.cs b/RaceLapTimer/RaceLapTimer/ApiControllers/SystemApiModule.cs index 8c65d33..028608d 100644 --- a/RaceLapTimer/RaceLapTimer/ApiControllers/SystemApiModule.cs +++ b/RaceLapTimer/RaceLapTimer/ApiControllers/SystemApiModule.cs @@ -1,17 +1,32 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Text; using System.Threading.Tasks; using Nancy; namespace RaceLapTimer.ApiControllers { - public class SystemApiModule:NancyModule + public class SystemApiModule : NancyModule { public SystemApiModule() : base("/api/system") { - + Get["info"] = args => GetSystemInfo(); + } + + private dynamic GetSystemInfo() + { + return Response.AsJson( + new SystemInfo + { + Version = Assembly.GetEntryAssembly().GetName().Version.ToString() + }); } } + + public class SystemInfo + { + public string Version { get; set; } + } } diff --git a/RaceLapTimer/RaceLapTimer/AuthenticationBootstrapper.cs b/RaceLapTimer/RaceLapTimer/AuthenticationBootstrapper.cs index 30baeb6..003312c 100644 --- a/RaceLapTimer/RaceLapTimer/AuthenticationBootstrapper.cs +++ b/RaceLapTimer/RaceLapTimer/AuthenticationBootstrapper.cs @@ -2,8 +2,10 @@ using Nancy; using Nancy.Authentication.Forms; using Nancy.Bootstrapper; +using Nancy.Conventions; using Nancy.Diagnostics; using Nancy.TinyIoc; +using RaceLapTimer.ApiControllers; namespace RaceLapTimer { @@ -22,6 +24,7 @@ namespace RaceLapTimer // Here we register our user mapper as a per-request singleton. // As this is now per-request we could inject a request scoped // database "context" or other request scoped services. + container.Register(); container.Register(); //register the storage provider.. container.Register().AsSingleton(); } @@ -53,5 +56,13 @@ namespace RaceLapTimer } protected override IRootPathProvider RootPathProvider { get {return new NancyRootPathProvider();} } + + protected override void ConfigureConventions(NancyConventions conventions) + { + base.ConfigureConventions(conventions); + conventions.StaticContentsConventions.Add( + StaticContentConventionBuilder.AddDirectory("images","images") + ); + } } } diff --git a/RaceLapTimer/RaceLapTimer/RaceLapTimer.csproj b/RaceLapTimer/RaceLapTimer/RaceLapTimer.csproj index 1704b25..b82e0c7 100644 --- a/RaceLapTimer/RaceLapTimer/RaceLapTimer.csproj +++ b/RaceLapTimer/RaceLapTimer/RaceLapTimer.csproj @@ -101,6 +101,7 @@ + @@ -158,7 +159,6 @@ - @@ -171,6 +171,11 @@ Interfaces + + + Always + + diff --git a/RaceLapTimer/RaceLapTimer/www/Views/Monitor.cshtml b/RaceLapTimer/RaceLapTimer/www/Views/Monitor.cshtml index 7be1e9c..df76d4d 100644 --- a/RaceLapTimer/RaceLapTimer/www/Views/Monitor.cshtml +++ b/RaceLapTimer/RaceLapTimer/www/Views/Monitor.cshtml @@ -5,34 +5,42 @@ }

@ViewBag.Title

- +
- - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + -
Race NamePilot CountLap Count
Race NameIdle Time (s)Lap Count
None010
None010
None010
None010
\ No newline at end of file + + \ No newline at end of file diff --git a/RaceLapTimer/RaceLapTimer/www/Views/Pilots.cshtml b/RaceLapTimer/RaceLapTimer/www/Views/Pilots.cshtml index 2833553..25c8223 100644 --- a/RaceLapTimer/RaceLapTimer/www/Views/Pilots.cshtml +++ b/RaceLapTimer/RaceLapTimer/www/Views/Pilots.cshtml @@ -5,39 +5,50 @@ }

@ViewBag.Title

- +
- - - - + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + -
NameQuadTotal RacesTotal LapsPilot NameTransponder NumberKart NameTeam Name
None01010
None01010
None01010
None01010pilotLogo---
\ No newline at end of file + + \ No newline at end of file diff --git a/RaceLapTimer/RaceLapTimer/www/Views/razor-layout.cshtml b/RaceLapTimer/RaceLapTimer/www/Views/razor-layout.cshtml index d46f145..daf6cf6 100644 --- a/RaceLapTimer/RaceLapTimer/www/Views/razor-layout.cshtml +++ b/RaceLapTimer/RaceLapTimer/www/Views/razor-layout.cshtml @@ -2,10 +2,21 @@ - - + @**@ + @* - + *@ + @* + + + + *@ + + + + + + Razor Localization Demo @@ -65,32 +76,32 @@ -
+
+
+
+ @RenderBody() +
+
+ -
+
\ No newline at end of file diff --git a/RaceLapTimer/RaceLapTimer/www/images/pilotPic.png b/RaceLapTimer/RaceLapTimer/www/images/pilotPic.png new file mode 100644 index 0000000000000000000000000000000000000000..db3fe77956c4094ee85b6652a4c7a2fdfa9d19e6 GIT binary patch literal 5046 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 F{SO0_zmNa` literal 0 HcmV?d00001