This commit is contained in:
nutchayut
2024-05-31 00:38:31 +07:00
commit 88ddddd7c2
234 changed files with 37557 additions and 0 deletions

View File

@@ -0,0 +1,105 @@
/*
* OCPP.Core - https://github.com/dallmann-consulting/OCPP.Core
* Copyright (C) 2020-2021 dallmann consulting GmbH.
* All Rights Reserved.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using OCPP.Core.Database;
using OCPP.Core.Management.Models;
namespace OCPP.Core.Management.Controllers
{
[Authorize]
public class AccountController : BaseController
{
public AccountController(
UserManager userManager,
ILoggerFactory loggerFactory,
IConfiguration config,
OCPPCoreContext dbContext) : base(userManager, loggerFactory, config, dbContext)
{
Logger = loggerFactory.CreateLogger<AccountController>();
}
// GET: /Account/Login
[HttpGet]
[AllowAnonymous]
public IActionResult Login(string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
return View();
}
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(UserModel userModel, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
await UserManager.SignIn(this.HttpContext, userModel, false);
if (userModel != null && !string.IsNullOrWhiteSpace(userModel.Username))
{
Logger.LogInformation("User '{0}' logged in", userModel.Username);
return RedirectToLocal(returnUrl);
}
else
{
Logger.LogInformation("Invalid login attempt: User '{0}'", userModel.Username);
ModelState.AddModelError(string.Empty, "Invalid login attempt");
return View(userModel);
}
}
// If we got this far, something failed, redisplay form
return View(userModel);
}
[AllowAnonymous]
public async Task<IActionResult> Logout(UserModel userModel)
{
Logger.LogInformation("Signing our user '{0}'", userModel.Username);
await UserManager.SignOut(this.HttpContext);
return RedirectToAction(nameof(AccountController.Login), "Account");
}
private IActionResult RedirectToLocal(string returnUrl)
{
if (!string.IsNullOrWhiteSpace(returnUrl) && Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction(nameof(HomeController.Index), Constants.HomeController);
}
}
}
}

View File

@@ -0,0 +1,177 @@
/*
* OCPP.Core - https://github.com/dallmann-consulting/OCPP.Core
* Copyright (C) 2020-2021 dallmann consulting GmbH.
* All Rights Reserved.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using OCPP.Core.Database;
namespace OCPP.Core.Management.Controllers
{
public partial class ApiController : BaseController
{
private readonly IStringLocalizer<ApiController> _localizer;
public ApiController(
UserManager userManager,
IStringLocalizer<ApiController> localizer,
ILoggerFactory loggerFactory,
IConfiguration config,
OCPPCoreContext dbContext) : base(userManager, loggerFactory, config, dbContext)
{
_localizer = localizer;
Logger = loggerFactory.CreateLogger<ApiController>();
}
[Authorize]
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public async Task<IActionResult> Reset(string Id)
{
if (User != null && !User.IsInRole(Constants.AdminRoleName))
{
Logger.LogWarning("Reset: Request by non-administrator: {0}", User?.Identity?.Name);
return StatusCode((int)HttpStatusCode.Unauthorized);
}
int httpStatuscode = (int)HttpStatusCode.OK;
string resultContent = string.Empty;
Logger.LogTrace("Reset: Request to restart chargepoint '{0}'", Id);
if (!string.IsNullOrEmpty(Id))
{
try
{
ChargePoint chargePoint = DbContext.ChargePoints.Find(Id);
if (chargePoint != null)
{
string serverApiUrl = base.Config.GetValue<string>("ServerApiUrl");
string apiKeyConfig = base.Config.GetValue<string>("ApiKey");
if (!string.IsNullOrEmpty(serverApiUrl))
{
try
{
using (var httpClient = new HttpClient())
{
if (!serverApiUrl.EndsWith('/'))
{
serverApiUrl += "/";
}
Uri uri = new Uri(serverApiUrl);
uri = new Uri(uri, $"Reset/{Uri.EscapeDataString(Id)}");
httpClient.Timeout = new TimeSpan(0, 0, 4); // use short timeout
// API-Key authentication?
if (!string.IsNullOrWhiteSpace(apiKeyConfig))
{
httpClient.DefaultRequestHeaders.Add("X-API-Key", apiKeyConfig);
}
else
{
Logger.LogWarning("Reset: No API-Key configured!");
}
HttpResponseMessage response = await httpClient.GetAsync(uri);
if (response.StatusCode == HttpStatusCode.OK)
{
string jsonResult = await response.Content.ReadAsStringAsync();
if (!string.IsNullOrEmpty(jsonResult))
{
try
{
dynamic jsonObject = JsonConvert.DeserializeObject(jsonResult);
Logger.LogInformation("Reset: Result of API request is '{0}'", jsonResult);
string status = jsonObject.status;
switch (status)
{
case "Accepted":
resultContent = _localizer["ResetAccepted"];
break;
case "Rejected":
resultContent = _localizer["ResetRejected"];
break;
case "Scheduled":
resultContent = _localizer["ResetScheduled"];
break;
default:
resultContent = string.Format(_localizer["ResetUnknownStatus"], status);
break;
}
}
catch (Exception exp)
{
Logger.LogError(exp, "Reset: Error in JSON result => {0}", exp.Message);
httpStatuscode = (int)HttpStatusCode.OK;
resultContent = _localizer["ResetError"];
}
}
else
{
Logger.LogError("Reset: Result of API request is empty");
httpStatuscode = (int)HttpStatusCode.OK;
resultContent = _localizer["ResetError"];
}
}
else if (response.StatusCode == HttpStatusCode.NotFound)
{
// Chargepoint offline
httpStatuscode = (int)HttpStatusCode.OK;
resultContent = _localizer["ResetOffline"];
}
else
{
Logger.LogError("Reset: Result of API request => httpStatus={0}", response.StatusCode);
httpStatuscode = (int)HttpStatusCode.OK;
resultContent = _localizer["ResetError"];
}
}
}
catch (Exception exp)
{
Logger.LogError(exp, "Reset: Error in API request => {0}", exp.Message);
httpStatuscode = (int)HttpStatusCode.OK;
resultContent = _localizer["ResetError"];
}
}
}
else
{
Logger.LogWarning("Reset: Error loading charge point '{0}' from database", Id);
httpStatuscode = (int)HttpStatusCode.OK;
resultContent = _localizer["UnknownChargepoint"];
}
}
catch (Exception exp)
{
Logger.LogError(exp, "Reset: Error loading charge point from database");
httpStatuscode = (int)HttpStatusCode.OK;
resultContent = _localizer["ResetError"];
}
}
return StatusCode(httpStatuscode, resultContent);
}
}
}

View File

@@ -0,0 +1,165 @@
/*
* OCPP.Core - https://github.com/dallmann-consulting/OCPP.Core
* Copyright (C) 2020-2021 dallmann consulting GmbH.
* All Rights Reserved.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using OCPP.Core.Database;
namespace OCPP.Core.Management.Controllers
{
public partial class ApiController : BaseController
{
[Authorize]
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public async Task<IActionResult> UnlockConnector(string Id)
{
if (User != null && !User.IsInRole(Constants.AdminRoleName))
{
Logger.LogWarning("UnlockConnector: Request by non-administrator: {0}", User?.Identity?.Name);
return StatusCode((int)HttpStatusCode.Unauthorized);
}
int httpStatuscode = (int)HttpStatusCode.OK;
string resultContent = string.Empty;
Logger.LogTrace("UnlockConnector: Request to unlock chargepoint '{0}'", Id);
if (!string.IsNullOrEmpty(Id))
{
try
{
ChargePoint chargePoint = DbContext.ChargePoints.Find(Id);
if (chargePoint != null)
{
string serverApiUrl = base.Config.GetValue<string>("ServerApiUrl");
string apiKeyConfig = base.Config.GetValue<string>("ApiKey");
if (!string.IsNullOrEmpty(serverApiUrl))
{
try
{
using (var httpClient = new HttpClient())
{
if (!serverApiUrl.EndsWith('/'))
{
serverApiUrl += "/";
}
Uri uri = new Uri(serverApiUrl);
uri = new Uri(uri, $"UnlockConnector/{Uri.EscapeDataString(Id)}");
httpClient.Timeout = new TimeSpan(0, 0, 4); // use short timeout
// API-Key authentication?
if (!string.IsNullOrWhiteSpace(apiKeyConfig))
{
httpClient.DefaultRequestHeaders.Add("X-API-Key", apiKeyConfig);
}
else
{
Logger.LogWarning("UnlockConnector: No API-Key configured!");
}
HttpResponseMessage response = await httpClient.GetAsync(uri);
if (response.StatusCode == HttpStatusCode.OK)
{
string jsonResult = await response.Content.ReadAsStringAsync();
if (!string.IsNullOrEmpty(jsonResult))
{
try
{
dynamic jsonObject = JsonConvert.DeserializeObject(jsonResult);
Logger.LogInformation("UnlockConnector: Result of API request is '{0}'", jsonResult);
string status = jsonObject.status;
switch (status)
{
case "Unlocked":
resultContent = _localizer["UnlockConnectorAccepted"];
break;
case "UnlockFailed":
case "OngoingAuthorizedTransaction":
case "UnknownConnector":
resultContent = _localizer["UnlockConnectorFailed"];
break;
case "NotSupported":
resultContent = _localizer["UnlockConnectorNotSupported"];
break;
default:
resultContent = string.Format(_localizer["UnlockConnectorUnknownStatus"], status);
break;
}
}
catch (Exception exp)
{
Logger.LogError(exp, "UnlockConnector: Error in JSON result => {0}", exp.Message);
httpStatuscode = (int)HttpStatusCode.OK;
resultContent = _localizer["UnlockConnectorError"];
}
}
else
{
Logger.LogError("UnlockConnector: Result of API request is empty");
httpStatuscode = (int)HttpStatusCode.OK;
resultContent = _localizer["UnlockConnectorError"];
}
}
else if (response.StatusCode == HttpStatusCode.NotFound)
{
// Chargepoint offline
httpStatuscode = (int)HttpStatusCode.OK;
resultContent = _localizer["UnlockConnectorOffline"];
}
else
{
Logger.LogError("UnlockConnector: Result of API request => httpStatus={0}", response.StatusCode);
httpStatuscode = (int)HttpStatusCode.OK;
resultContent = _localizer["UnlockConnectorError"];
}
}
}
catch (Exception exp)
{
Logger.LogError(exp, "UnlockConnector: Error in API request => {0}", exp.Message);
httpStatuscode = (int)HttpStatusCode.OK;
resultContent = _localizer["UnlockConnectorError"];
}
}
}
else
{
Logger.LogWarning("UnlockConnector: Error loading charge point '{0}' from database", Id);
httpStatuscode = (int)HttpStatusCode.OK;
resultContent = _localizer["UnknownChargepoint"];
}
}
catch (Exception exp)
{
Logger.LogError(exp, "UnlockConnector: Error loading charge point from database");
httpStatuscode = (int)HttpStatusCode.OK;
resultContent = _localizer["UnlockConnectorError"];
}
}
return StatusCode(httpStatuscode, resultContent);
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* OCPP.Core - https://github.com/dallmann-consulting/OCPP.Core
* Copyright (C) 2020-2021 dallmann consulting GmbH.
* All Rights Reserved.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using OCPP.Core.Database;
using OCPP.Core.Management.Models;
namespace OCPP.Core.Management.Controllers
{
public class BaseController : Controller
{
protected UserManager UserManager { get; private set; }
protected ILogger Logger { get; set; }
protected IConfiguration Config { get; private set; }
protected OCPPCoreContext DbContext { get; private set; }
public BaseController(
UserManager userManager,
ILoggerFactory loggerFactory,
IConfiguration config,
OCPPCoreContext dbContext)
{
UserManager = userManager;
Config = config;
DbContext = dbContext;
}
}
}

View File

@@ -0,0 +1,162 @@
/*
* OCPP.Core - https://github.com/dallmann-consulting/OCPP.Core
* Copyright (C) 2020-2021 dallmann consulting GmbH.
* All Rights Reserved.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using OCPP.Core.Database;
using OCPP.Core.Management.Models;
namespace OCPP.Core.Management.Controllers
{
public partial class HomeController : BaseController
{
[Authorize]
public IActionResult ChargePoint(string Id, ChargePointViewModel cpvm)
{
try
{
if (User != null && !User.IsInRole(Constants.AdminRoleName))
{
Logger.LogWarning("ChargePoint: Request by non-administrator: {0}", User?.Identity?.Name);
TempData["ErrMsgKey"] = "AccessDenied";
return RedirectToAction("Error", new { Id = "" });
}
cpvm.CurrentId = Id;
Logger.LogTrace("ChargePoint: Loading charge points...");
List<ChargePoint> dbChargePoints = DbContext.ChargePoints.ToList<ChargePoint>();
Logger.LogInformation("ChargePoint: Found {0} charge points", dbChargePoints.Count);
ChargePoint currentChargePoint = null;
if (!string.IsNullOrEmpty(Id))
{
foreach (ChargePoint cp in dbChargePoints)
{
if (cp.ChargePointId.Equals(Id, StringComparison.InvariantCultureIgnoreCase))
{
currentChargePoint = cp;
Logger.LogTrace("ChargePoint: Current charge point: {0} / {1}", cp.ChargePointId, cp.Name);
break;
}
}
}
if (Request.Method == "POST")
{
string errorMsg = null;
if (Id == "@")
{
Logger.LogTrace("ChargePoint: Creating new charge point...");
// Create new tag
if (string.IsNullOrWhiteSpace(cpvm.ChargePointId))
{
errorMsg = _localizer["ChargePointIdRequired"].Value;
Logger.LogInformation("ChargePoint: New => no charge point ID entered");
}
if (string.IsNullOrEmpty(errorMsg))
{
// check if duplicate
foreach (ChargePoint cp in dbChargePoints)
{
if (cp.ChargePointId.Equals(cpvm.ChargePointId, StringComparison.InvariantCultureIgnoreCase))
{
// id already exists
errorMsg = _localizer["ChargePointIdExists"].Value;
Logger.LogInformation("ChargePoint: New => charge point ID already exists: {0}", cpvm.ChargePointId);
break;
}
}
}
if (string.IsNullOrEmpty(errorMsg))
{
// Save tag in DB
ChargePoint newChargePoint = new ChargePoint();
newChargePoint.ChargePointId = cpvm.ChargePointId;
newChargePoint.Name = cpvm.Name;
newChargePoint.Comment = cpvm.Comment;
newChargePoint.Username = cpvm.Username;
newChargePoint.Password = cpvm.Password;
newChargePoint.ClientCertThumb = cpvm.ClientCertThumb;
DbContext.ChargePoints.Add(newChargePoint);
DbContext.SaveChanges();
Logger.LogInformation("ChargePoint: New => charge point saved: {0} / {1}", cpvm.ChargePointId, cpvm.Name);
}
else
{
ViewBag.ErrorMsg = errorMsg;
return View("ChargePointDetail", cpvm);
}
}
else if (currentChargePoint.ChargePointId == Id)
{
// Save existing charge point
Logger.LogTrace("ChargePoint: Saving charge point '{0}'", Id);
currentChargePoint.Name = cpvm.Name;
currentChargePoint.Comment = cpvm.Comment;
currentChargePoint.Username = cpvm.Username;
currentChargePoint.Password = cpvm.Password;
currentChargePoint.ClientCertThumb = cpvm.ClientCertThumb;
DbContext.SaveChanges();
Logger.LogInformation("ChargePoint: Edit => charge point saved: {0} / {1}", cpvm.ChargePointId, cpvm.Name);
}
return RedirectToAction("ChargePoint", new { Id = "" });
}
else
{
// Display charge point
cpvm = new ChargePointViewModel();
cpvm.ChargePoints = dbChargePoints;
cpvm.CurrentId = Id;
if (currentChargePoint!= null)
{
cpvm = new ChargePointViewModel();
cpvm.ChargePointId = currentChargePoint.ChargePointId;
cpvm.Name = currentChargePoint.Name;
cpvm.Comment = currentChargePoint.Comment;
cpvm.Username = currentChargePoint.Username;
cpvm.Password = currentChargePoint.Password;
cpvm.ClientCertThumb = currentChargePoint.ClientCertThumb;
}
string viewName = (!string.IsNullOrEmpty(cpvm.ChargePointId) || Id == "@") ? "ChargePointDetail" : "ChargePointList";
return View(viewName, cpvm);
}
}
catch (Exception exp)
{
Logger.LogError(exp, "ChargePoint: Error loading charge points from database");
TempData["ErrMessage"] = exp.Message;
return RedirectToAction("Error", new { Id = "" });
}
}
}
}

View File

@@ -0,0 +1,163 @@
/*
* OCPP.Core - https://github.com/dallmann-consulting/OCPP.Core
* Copyright (C) 2020-2021 dallmann consulting GmbH.
* All Rights Reserved.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using OCPP.Core.Database;
using OCPP.Core.Management.Models;
namespace OCPP.Core.Management.Controllers
{
public partial class HomeController : BaseController
{
[Authorize]
public IActionResult ChargeTag(string Id, ChargeTagViewModel ctvm)
{
try
{
if (User != null && !User.IsInRole(Constants.AdminRoleName))
{
Logger.LogWarning("ChargeTag: Request by non-administrator: {0}", User?.Identity?.Name);
TempData["ErrMsgKey"] = "AccessDenied";
return RedirectToAction("Error", new { Id = "" });
}
ViewBag.DatePattern = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;
ViewBag.Language = CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
ctvm.CurrentTagId = Id;
Logger.LogTrace("ChargeTag: Loading charge tags...");
List<ChargeTag> dbChargeTags = DbContext.ChargeTags.ToList<ChargeTag>();
Logger.LogInformation("ChargeTag: Found {0} charge tags", dbChargeTags.Count);
ChargeTag currentChargeTag = null;
if (!string.IsNullOrEmpty(Id))
{
foreach (ChargeTag tag in dbChargeTags)
{
if (tag.TagId.Equals(Id, StringComparison.InvariantCultureIgnoreCase))
{
currentChargeTag = tag;
Logger.LogTrace("ChargeTag: Current charge tag: {0} / {1}", tag.TagId, tag.TagName);
break;
}
}
}
if (Request.Method == "POST")
{
string errorMsg = null;
if (Id == "@")
{
Logger.LogTrace("ChargeTag: Creating new charge tag...");
// Create new tag
if (string.IsNullOrWhiteSpace(ctvm.TagId))
{
errorMsg = _localizer["ChargeTagIdRequired"].Value;
Logger.LogInformation("ChargeTag: New => no charge tag ID entered");
}
if (string.IsNullOrEmpty(errorMsg))
{
// check if duplicate
foreach (ChargeTag tag in dbChargeTags)
{
if (tag.TagId.Equals(ctvm.TagId, StringComparison.InvariantCultureIgnoreCase))
{
// tag-id already exists
errorMsg = _localizer["ChargeTagIdExists"].Value;
Logger.LogInformation("ChargeTag: New => charge tag ID already exists: {0}", ctvm.TagId);
break;
}
}
}
if (string.IsNullOrEmpty(errorMsg))
{
// Save tag in DB
ChargeTag newTag = new ChargeTag();
newTag.TagId = ctvm.TagId;
newTag.TagName = ctvm.TagName;
newTag.ParentTagId = ctvm.ParentTagId;
newTag.ExpiryDate = ctvm.ExpiryDate;
newTag.Blocked = ctvm.Blocked;
DbContext.ChargeTags.Add(newTag);
DbContext.SaveChanges();
Logger.LogInformation("ChargeTag: New => charge tag saved: {0} / {1}", ctvm.TagId, ctvm.TagName);
}
else
{
ViewBag.ErrorMsg = errorMsg;
return View("ChargeTagDetail", ctvm);
}
}
else if (currentChargeTag.TagId == Id)
{
// Save existing tag
currentChargeTag.TagName = ctvm.TagName;
currentChargeTag.ParentTagId = ctvm.ParentTagId;
currentChargeTag.ExpiryDate = ctvm.ExpiryDate;
currentChargeTag.Blocked = ctvm.Blocked;
DbContext.SaveChanges();
Logger.LogInformation("ChargeTag: Edit => charge tag saved: {0} / {1}", ctvm.TagId, ctvm.TagName);
}
return RedirectToAction("ChargeTag", new { Id = "" });
}
else
{
// List all charge tags
ctvm = new ChargeTagViewModel();
ctvm.ChargeTags = dbChargeTags;
ctvm.CurrentTagId = Id;
if (currentChargeTag != null)
{
ctvm.TagId = currentChargeTag.TagId;
ctvm.TagName = currentChargeTag.TagName;
ctvm.ParentTagId = currentChargeTag.ParentTagId;
ctvm.ExpiryDate = currentChargeTag.ExpiryDate;
ctvm.Blocked = (currentChargeTag.Blocked != null) && currentChargeTag.Blocked.Value;
}
string viewName = (!string.IsNullOrEmpty(ctvm.TagId) || Id=="@") ? "ChargeTagDetail" : "ChargeTagList";
return View(viewName, ctvm);
}
}
catch (Exception exp)
{
Logger.LogError(exp, "ChargeTag: Error loading charge tags from database");
TempData["ErrMessage"] = exp.Message;
return RedirectToAction("Error", new { Id = "" });
}
}
}
}

View File

@@ -0,0 +1,113 @@
/*
* OCPP.Core - https://github.com/dallmann-consulting/OCPP.Core
* Copyright (C) 2020-2021 dallmann consulting GmbH.
* All Rights Reserved.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using OCPP.Core.Database;
using OCPP.Core.Management.Models;
namespace OCPP.Core.Management.Controllers
{
public partial class HomeController : BaseController
{
[Authorize]
public IActionResult Connector(string Id, string ConnectorId, ConnectorStatusViewModel csvm)
{
try
{
if (User != null && !User.IsInRole(Constants.AdminRoleName))
{
Logger.LogWarning("Connector: Request by non-administrator: {0}", User?.Identity?.Name);
TempData["ErrMsgKey"] = "AccessDenied";
return RedirectToAction("Error", new { Id = "" });
}
ViewBag.DatePattern = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;
ViewBag.Language = CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
Logger.LogTrace("Connector: Loading connectors...");
List<ConnectorStatus> dbConnectorStatuses = DbContext.ConnectorStatuses.ToList<ConnectorStatus>();
Logger.LogInformation("Connector: Found {0} connectors", dbConnectorStatuses.Count);
ConnectorStatus currentConnectorStatus = null;
if (!string.IsNullOrEmpty(Id) && !string.IsNullOrEmpty(ConnectorId))
{
foreach (ConnectorStatus cs in dbConnectorStatuses)
{
if (cs.ChargePointId.Equals(Id, StringComparison.InvariantCultureIgnoreCase) &&
cs.ConnectorId.ToString().Equals(ConnectorId, StringComparison.InvariantCultureIgnoreCase))
{
currentConnectorStatus = cs;
Logger.LogTrace("Connector: Current connector: {0} / {1}", cs.ChargePointId, cs.ConnectorId);
break;
}
}
}
if (Request.Method == "POST")
{
if (currentConnectorStatus.ChargePointId == Id)
{
// Save connector
currentConnectorStatus.ConnectorName = csvm.ConnectorName;
DbContext.SaveChanges();
Logger.LogInformation("Connector: Edit => Connector saved: {0} / {1} => '{2}'", csvm.ChargePointId, csvm.ConnectorId, csvm.ConnectorName);
}
return RedirectToAction("Connector", new { Id = "" });
}
else
{
// List all charge tags
csvm = new ConnectorStatusViewModel();
csvm.ConnectorStatuses = dbConnectorStatuses;
if (currentConnectorStatus != null)
{
csvm.ChargePointId = currentConnectorStatus.ChargePointId;
csvm.ConnectorId = currentConnectorStatus.ConnectorId;
csvm.ConnectorName = currentConnectorStatus.ConnectorName;
csvm.LastStatus = currentConnectorStatus.LastStatus;
csvm.LastStatusTime = currentConnectorStatus.LastStatusTime;
csvm.LastMeter = currentConnectorStatus.LastMeter;
csvm.LastMeterTime = currentConnectorStatus.LastMeterTime;
}
string viewName = (currentConnectorStatus != null) ? "ConnectorDetail" : "ConnectorList";
return View(viewName, csvm);
}
}
catch (Exception exp)
{
Logger.LogError(exp, "Connector: Error loading connectors from database");
TempData["ErrMessage"] = exp.Message;
return RedirectToAction("Error", new { Id = "" });
}
}
}
}

View File

@@ -0,0 +1,234 @@
/*
* OCPP.Core - https://github.com/dallmann-consulting/OCPP.Core
* Copyright (C) 2020-2021 dallmann consulting GmbH.
* All Rights Reserved.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using OCPP.Core.Database;
using OCPP.Core.Management.Models;
namespace OCPP.Core.Management.Controllers
{
public partial class HomeController : BaseController
{
const char CSV_Seperator = ';';
[Authorize]
public IActionResult Export(string Id, string ConnectorId)
{
Logger.LogTrace("Export: Loading charge point transactions...");
int currentConnectorId = -1;
int.TryParse(ConnectorId, out currentConnectorId);
TransactionListViewModel tlvm = new TransactionListViewModel();
tlvm.CurrentChargePointId = Id;
tlvm.CurrentConnectorId = currentConnectorId;
tlvm.ConnectorStatuses = new List<ConnectorStatus>();
tlvm.Transactions = new List<Transaction>();
try
{
string ts = Request.Query["t"];
int days = 30;
if (ts == "2")
{
// 90 days
days = 90;
tlvm.Timespan = 2;
}
else if (ts == "3")
{
// 365 days
days = 365;
tlvm.Timespan = 3;
}
else
{
// 30 days
days = 30;
tlvm.Timespan = 1;
}
string currentConnectorName = string.Empty;
Logger.LogTrace("Export: Loading charge points...");
tlvm.ConnectorStatuses = DbContext.ConnectorStatuses.ToList<ConnectorStatus>();
// Preferred: use specific connector name
foreach (ConnectorStatus cs in tlvm.ConnectorStatuses)
{
if (cs.ChargePointId == Id && cs.ConnectorId == currentConnectorId)
{
currentConnectorName = cs.ConnectorName;
/*
if (string.IsNullOrEmpty(tlvm.CurrentConnectorName))
{
currentConnectorName = $"{Id}:{cs.ConnectorId}";
}
*/
break;
}
}
// default: combined name with charge point and connector
if (string.IsNullOrEmpty(currentConnectorName))
{
tlvm.ChargePoints = DbContext.ChargePoints.ToList<ChargePoint>();
foreach(ChargePoint cp in tlvm.ChargePoints)
{
if (cp.ChargePointId == Id)
{
currentConnectorName = $"{cp.Name}:{currentConnectorId}";
break;
}
}
if (string.IsNullOrEmpty(currentConnectorName))
{
// Fallback: ID + connector
currentConnectorName = $"{Id}:{currentConnectorId}";
}
}
// load charge tags for name resolution
Logger.LogTrace("Export: Loading charge tags...");
List<ChargeTag> chargeTags = DbContext.ChargeTags.ToList<ChargeTag>();
tlvm.ChargeTags = new Dictionary<string, ChargeTag>();
if (chargeTags != null)
{
foreach (ChargeTag tag in chargeTags)
{
tlvm.ChargeTags.Add(tag.TagId, tag);
}
}
if (!string.IsNullOrEmpty(tlvm.CurrentChargePointId))
{
Logger.LogTrace("Export: Loading charge point transactions...");
tlvm.Transactions = DbContext.Transactions
.Where(t => t.ChargePointId == tlvm.CurrentChargePointId &&
t.ConnectorId == tlvm.CurrentConnectorId &&
t.StartTime >= DateTime.UtcNow.AddDays(-1 * days))
.OrderByDescending(t => t.TransactionId)
.ToList<Transaction>();
}
StringBuilder connectorName = new StringBuilder(currentConnectorName);
foreach (char c in Path.GetInvalidFileNameChars())
{
connectorName.Replace(c, '_');
}
string filename = string.Format("Transactions_{0}.csv", connectorName);
string csv = CreateCsv(tlvm, currentConnectorName);
Logger.LogInformation("Export: File => {0} Chars / Name '{1}'", csv.Length, filename);
return File(Encoding.GetEncoding("ISO-8859-1").GetBytes(csv), "text/csv", filename);
}
catch (Exception exp)
{
Logger.LogError(exp, "Export: Error loading data from database");
}
return View(tlvm);
}
private string CreateCsv(TransactionListViewModel tlvm, string currentConnectorName)
{
StringBuilder csv = new StringBuilder(8192);
csv.Append(EscapeCsvValue(_localizer["Connector"]));
csv.Append(CSV_Seperator);
csv.Append(EscapeCsvValue(_localizer["StartTime"]));
csv.Append(CSV_Seperator);
csv.Append(EscapeCsvValue(_localizer["StartTag"]));
csv.Append(CSV_Seperator);
csv.Append(EscapeCsvValue(_localizer["StartMeter"]));
csv.Append(CSV_Seperator);
csv.Append(EscapeCsvValue(_localizer["StopTime"]));
csv.Append(CSV_Seperator);
csv.Append(EscapeCsvValue(_localizer["StopTag"]));
csv.Append(CSV_Seperator);
csv.Append(EscapeCsvValue(_localizer["StopMeter"]));
csv.Append(CSV_Seperator);
csv.Append(EscapeCsvValue(_localizer["ChargeSum"]));
if (tlvm != null && tlvm.Transactions != null)
{
foreach (Transaction t in tlvm.Transactions)
{
string startTag = t.StartTagId;
string stopTag = t.StopTagId;
if (!string.IsNullOrEmpty(t.StartTagId) && tlvm.ChargeTags != null && tlvm.ChargeTags.ContainsKey(t.StartTagId))
{
startTag = tlvm.ChargeTags[t.StartTagId]?.TagName;
}
if (!string.IsNullOrEmpty(t.StopTagId) && tlvm.ChargeTags != null && tlvm.ChargeTags.ContainsKey(t.StopTagId))
{
stopTag = tlvm.ChargeTags[t.StopTagId]?.TagName;
}
csv.AppendLine();
csv.Append(EscapeCsvValue(currentConnectorName));
csv.Append(CSV_Seperator);
csv.Append(EscapeCsvValue(t.StartTime.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss")));
csv.Append(CSV_Seperator);
csv.Append(EscapeCsvValue(startTag));
csv.Append(CSV_Seperator);
csv.Append(EscapeCsvValue(string.Format("{0:0.0##}", t.MeterStart)));
csv.Append(CSV_Seperator);
csv.Append(EscapeCsvValue(((t.StopTime != null) ? t.StopTime.Value.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss") : string.Empty)));
csv.Append(CSV_Seperator);
csv.Append(EscapeCsvValue(stopTag));
csv.Append(CSV_Seperator);
csv.Append(EscapeCsvValue(((t.MeterStop != null) ? string.Format("{0:0.0##}", t.MeterStop) : string.Empty)));
csv.Append(CSV_Seperator);
csv.Append(EscapeCsvValue(((t.MeterStop != null) ? string.Format("{0:0.0##}", (t.MeterStop - t.MeterStart)) : string.Empty)));
}
}
return csv.ToString();
}
private string EscapeCsvValue(string value)
{
if (!string.IsNullOrEmpty(value))
{
if (value.Contains(CSV_Seperator))
{
if (value.Contains('"'))
{
// replace '"' with '""'
value.Replace("\"", "\"\"");
}
// put value in "
value = string.Format("\"{0}\"", value);
}
}
return value;
}
}
}

View File

@@ -0,0 +1,334 @@
/*
* OCPP.Core - https://github.com/dallmann-consulting/OCPP.Core
* Copyright (C) 2020-2021 dallmann consulting GmbH.
* All Rights Reserved.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using OCPP.Core.Database;
using OCPP.Core.Management.Models;
namespace OCPP.Core.Management.Controllers
{
public partial class HomeController : BaseController
{
private readonly IStringLocalizer<HomeController> _localizer;
public HomeController(
UserManager userManager,
IStringLocalizer<HomeController> localizer,
ILoggerFactory loggerFactory,
IConfiguration config,
OCPPCoreContext dbContext) : base(userManager, loggerFactory, config, dbContext)
{
_localizer = localizer;
Logger = loggerFactory.CreateLogger<HomeController>();
}
[Authorize]
public async Task<IActionResult> Index()
{
Logger.LogTrace("Index: Loading charge points with latest transactions...");
OverviewViewModel overviewModel = new OverviewViewModel();
overviewModel.ChargePoints = new List<ChargePointsOverviewViewModel>();
try
{
Dictionary<string, ChargePointStatus> dictOnlineStatus = new Dictionary<string, ChargePointStatus>();
#region Load online status from OCPP server
string serverApiUrl = base.Config.GetValue<string>("ServerApiUrl");
string apiKeyConfig = base.Config.GetValue<string>("ApiKey");
if (!string.IsNullOrEmpty(serverApiUrl))
{
bool serverError = false;
try
{
ChargePointStatus[] onlineStatusList = null;
using (var httpClient = new HttpClient())
{
if (!serverApiUrl.EndsWith('/'))
{
serverApiUrl += "/";
}
Uri uri = new Uri(serverApiUrl);
uri = new Uri(uri, "Status");
httpClient.Timeout = new TimeSpan(0, 0, 4); // use short timeout
// API-Key authentication?
if (!string.IsNullOrWhiteSpace(apiKeyConfig))
{
httpClient.DefaultRequestHeaders.Add("X-API-Key", apiKeyConfig);
}
else
{
Logger.LogWarning("Index: No API-Key configured!");
}
HttpResponseMessage response = await httpClient.GetAsync(uri);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
string jsonData = await response.Content.ReadAsStringAsync();
if (!string.IsNullOrEmpty(jsonData))
{
onlineStatusList = JsonConvert.DeserializeObject<ChargePointStatus[]>(jsonData);
overviewModel.ServerConnection = true;
if (onlineStatusList != null)
{
foreach(ChargePointStatus cps in onlineStatusList)
{
if (!dictOnlineStatus.TryAdd(cps.Id, cps))
{
Logger.LogError("Index: Online charge point status (ID={0}) could not be added to dictionary", cps.Id);
}
}
}
}
else
{
Logger.LogError("Index: Result of status web request is empty");
serverError = true;
}
}
else
{
Logger.LogError("Index: Result of status web request => httpStatus={0}", response.StatusCode);
serverError = true;
}
}
Logger.LogInformation("Index: Result of status web request => Length={0}", onlineStatusList?.Length);
}
catch (Exception exp)
{
Logger.LogError(exp, "Index: Error in status web request => {0}", exp.Message);
serverError = true;
}
if (serverError)
{
ViewBag.ErrorMsg = _localizer["ErrorOCPPServer"];
}
}
#endregion
// List of charge point status (OCPP messages) with latest transaction (if one exist)
var connectorStatusViewList = DbContext.ConnectorStatuses
.GroupJoin(
DbContext.Transactions,
cs => new { cs.ChargePointId, cs.ConnectorId },
t => new { t.ChargePointId, t.ConnectorId },
(cs, transactions) => new { cs, transactions }
)
.SelectMany(
x => x.transactions.DefaultIfEmpty(),
(x, transaction) => new ConnectorStatusView
{
ChargePointId = x.cs.ChargePointId,
ConnectorId = x.cs.ConnectorId,
ConnectorName = x.cs.ConnectorName,
LastStatus = x.cs.LastStatus,
LastStatusTime = x.cs.LastStatusTime,
LastMeter = x.cs.LastMeter,
LastMeterTime = x.cs.LastMeterTime,
TransactionId = (int?)transaction.TransactionId,
StartTagId = transaction.StartTagId,
StartTime = transaction.StartTime,
MeterStart = transaction.MeterStart,
StartResult = transaction.StartResult,
StopTagId = transaction.StopTagId,
StopTime = transaction.StopTime,
MeterStop = transaction.MeterStop,
StopReason = transaction.StopReason
}
)
.Where(x => x.TransactionId == null ||
x.TransactionId == DbContext.Transactions
.Where(t => t.ChargePointId == x.ChargePointId && t.ConnectorId == x.ConnectorId)
.Select(t => t.TransactionId)
.Max())
.ToList();
// Count connectors for every charge point (=> naming scheme)
Dictionary<string, int> dictConnectorCount = new Dictionary<string, int>();
foreach (ConnectorStatusView csv in connectorStatusViewList)
{
if (dictConnectorCount.ContainsKey(csv.ChargePointId))
{
// > 1 connector
dictConnectorCount[csv.ChargePointId] = dictConnectorCount[csv.ChargePointId] + 1;
}
else
{
// first connector
dictConnectorCount.Add(csv.ChargePointId, 1);
}
}
// List of configured charge points
List<ChargePoint> dbChargePoints = DbContext.ChargePoints.ToList<ChargePoint>();
if (dbChargePoints != null)
{
// Iterate through all charge points in database
foreach (ChargePoint cp in dbChargePoints)
{
ChargePointStatus cpOnlineStatus = null;
dictOnlineStatus.TryGetValue(cp.ChargePointId, out cpOnlineStatus);
// Preference: Check for connectors status in database
bool foundConnectorStatus = false;
if (connectorStatusViewList != null)
{
foreach (ConnectorStatusView connStatus in connectorStatusViewList)
{
if (string.Equals(cp.ChargePointId, connStatus.ChargePointId, StringComparison.InvariantCultureIgnoreCase))
{
foundConnectorStatus = true;
ChargePointsOverviewViewModel cpovm = new ChargePointsOverviewViewModel();
cpovm.ChargePointId = cp.ChargePointId;
cpovm.ConnectorId = connStatus.ConnectorId;
if (string.IsNullOrWhiteSpace(connStatus.ConnectorName))
{
// No connector name specified => use default
if (dictConnectorCount.ContainsKey(cp.ChargePointId) &&
dictConnectorCount[cp.ChargePointId] > 1)
{
// more than 1 connector => "<charge point name>:<connector no.>"
cpovm.Name = $"{cp.Name}:{connStatus.ConnectorId}";
}
else
{
// only 1 connector => "<charge point name>"
cpovm.Name = cp.Name;
}
}
else
{
// Connector has name override name specified
cpovm.Name = connStatus.ConnectorName;
}
cpovm.Online = cpOnlineStatus != null;
cpovm.ConnectorStatus = ConnectorStatusEnum.Undefined;
OnlineConnectorStatus onlineConnectorStatus = null;
if (cpOnlineStatus != null &&
cpOnlineStatus.OnlineConnectors != null &&
cpOnlineStatus.OnlineConnectors.ContainsKey(connStatus.ConnectorId))
{
onlineConnectorStatus = cpOnlineStatus.OnlineConnectors[connStatus.ConnectorId];
cpovm.ConnectorStatus = onlineConnectorStatus.Status;
Logger.LogTrace("Index: Found online status for CP='{0}' / Connector='{1}' / Status='{2}'", cpovm.ChargePointId, cpovm.ConnectorId, cpovm.ConnectorStatus);
}
if (connStatus.TransactionId.HasValue)
{
cpovm.MeterStart = connStatus.MeterStart.Value;
cpovm.MeterStop = connStatus.MeterStop;
cpovm.StartTime = connStatus.StartTime;
cpovm.StopTime = connStatus.StopTime;
if (cpovm.ConnectorStatus == ConnectorStatusEnum.Undefined)
{
// default status: active transaction or not?
cpovm.ConnectorStatus = (cpovm.StopTime.HasValue) ? ConnectorStatusEnum.Available : ConnectorStatusEnum.Occupied;
}
}
else
{
cpovm.MeterStart = -1;
cpovm.MeterStop = -1;
cpovm.StartTime = null;
cpovm.StopTime = null;
if (cpovm.ConnectorStatus == ConnectorStatusEnum.Undefined)
{
// default status: Available
cpovm.ConnectorStatus = ConnectorStatusEnum.Available;
}
}
// Add current charge data to view model
if (cpovm.ConnectorStatus == ConnectorStatusEnum.Occupied &&
onlineConnectorStatus != null)
{
string currentCharge = string.Empty;
if (onlineConnectorStatus.ChargeRateKW != null)
{
currentCharge = string.Format("{0:0.0}kW", onlineConnectorStatus.ChargeRateKW.Value);
}
if (onlineConnectorStatus.SoC != null)
{
if (!string.IsNullOrWhiteSpace(currentCharge)) currentCharge += " | ";
currentCharge += string.Format("{0:0}%", onlineConnectorStatus.SoC.Value);
}
if (!string.IsNullOrWhiteSpace(currentCharge))
{
cpovm.CurrentChargeData = currentCharge;
}
}
overviewModel.ChargePoints.Add(cpovm);
}
}
}
// Fallback: assume 1 connector and show main charge point
if (foundConnectorStatus == false)
{
// no connector status found in DB => show configured charge point in overview
ChargePointsOverviewViewModel cpovm = new ChargePointsOverviewViewModel();
cpovm.ChargePointId = cp.ChargePointId;
cpovm.ConnectorId = 0;
cpovm.Name = cp.Name;
cpovm.Comment = cp.Comment;
cpovm.Online = cpOnlineStatus != null;
cpovm.ConnectorStatus = ConnectorStatusEnum.Undefined;
overviewModel.ChargePoints.Add(cpovm);
}
}
}
Logger.LogInformation("Index: Found {0} charge points / connectors", overviewModel.ChargePoints?.Count);
}
catch (Exception exp)
{
Logger.LogError(exp, "Index: Error loading charge points from database");
TempData["ErrMessage"] = exp.Message;
return RedirectToAction("Error", new { Id = "" });
}
return View(overviewModel);
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View();
}
}
}

View File

@@ -0,0 +1,127 @@
/*
* OCPP.Core - https://github.com/dallmann-consulting/OCPP.Core
* Copyright (C) 2020-2021 dallmann consulting GmbH.
* All Rights Reserved.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using OCPP.Core.Database;
using OCPP.Core.Management.Models;
namespace OCPP.Core.Management.Controllers
{
public partial class HomeController : BaseController
{
[Authorize]
public IActionResult Transactions(string Id, string ConnectorId)
{
Logger.LogTrace("Transactions: Loading charge point transactions...");
int currentConnectorId = -1;
int.TryParse(ConnectorId, out currentConnectorId);
TransactionListViewModel tlvm = new TransactionListViewModel();
tlvm.CurrentChargePointId = Id;
tlvm.CurrentConnectorId = currentConnectorId;
tlvm.ConnectorStatuses = new List<ConnectorStatus>();
tlvm.Transactions = new List<Transaction>();
try
{
string ts = Request.Query["t"];
int days = 30;
if (ts == "2")
{
// 90 days
days = 90;
tlvm.Timespan = 2;
}
else if (ts == "3")
{
// 365 days
days = 365;
tlvm.Timespan = 3;
}
else
{
// 30 days
days = 30;
tlvm.Timespan = 1;
}
Logger.LogTrace("Transactions: Loading charge points...");
tlvm.ChargePoints = DbContext.ChargePoints.ToList<ChargePoint>();
Logger.LogTrace("Transactions: Loading charge points connectors...");
tlvm.ConnectorStatuses = DbContext.ConnectorStatuses.ToList<ConnectorStatus>();
// Count connectors for every charge point (=> naming scheme)
Dictionary<string, int> dictConnectorCount = new Dictionary<string, int>();
foreach (ConnectorStatus cs in tlvm.ConnectorStatuses)
{
if (dictConnectorCount.ContainsKey(cs.ChargePointId))
{
// > 1 connector
dictConnectorCount[cs.ChargePointId] = dictConnectorCount[cs.ChargePointId] + 1;
}
else
{
// first connector
dictConnectorCount.Add(cs.ChargePointId, 1);
}
}
// load charge tags for name resolution
Logger.LogTrace("Transactions: Loading charge tags...");
List<ChargeTag> chargeTags = DbContext.ChargeTags.ToList<ChargeTag>();
tlvm.ChargeTags = new Dictionary<string, ChargeTag>();
if (chargeTags != null)
{
foreach(ChargeTag tag in chargeTags)
{
tlvm.ChargeTags.Add(tag.TagId, tag);
}
}
if (!string.IsNullOrEmpty(tlvm.CurrentChargePointId))
{
Logger.LogTrace("Transactions: Loading charge point transactions...");
tlvm.Transactions = DbContext.Transactions
.Where(t => t.ChargePointId == tlvm.CurrentChargePointId &&
t.ConnectorId == tlvm.CurrentConnectorId &&
t.StartTime >= DateTime.UtcNow.AddDays(-1 * days))
.OrderByDescending(t => t.TransactionId)
.ToList<Transaction>();
}
}
catch (Exception exp)
{
Logger.LogError(exp, "Transactions: Error loading charge points from database");
}
return View(tlvm);
}
}
}