- @(dev.Notes.Contains("discovered", StringComparison.InvariantCultureIgnoreCase) ? Html.Raw("
") : "")
- @(dev.Notes.Contains("static", StringComparison.InvariantCultureIgnoreCase) ? Html.Raw("
") : "")
- @dev.Notes
+
+
+
+
+ @* Accordion Header *@
+
+
+
+
+ @* Accordion Body *@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Under Construction
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Under Construction
+
+
+ @*
*@
+
+
+
+
+
+
+
+
+ @* Camera Cards Body *@
+
+
+ @* Getting Model.Devices *@
+ @foreach (var dev in @Model.Devices.OrderBy(d => d.DeviceActiveStatus))
+ {
+ @* Creating Container as a row double cam *@
+
+
+ @* Creating a CameraCard*@
+
+
+
+ @* Thumbnail *@
+
+ @{
+ var baseUrl = "http://localhost:90";
+ var imagePath = $"/thumbnails/{dev.UUID}/stream_0__1.jpeg";
+ var absolutePath = baseUrl + imagePath;
+ var imagePathRelative = $"./thumbnails/{dev.UUID}/stream_0__1.jpeg";
+ bool imageExists = ImageExists(absolutePath);
+ }
+
+ @if (imageExists)
{
- @switch (dev.Streams[i].Status)
+

+ }
+ else
+ {
+
+

+ }
+
+ @functions {
+ private bool ImageExists(string imageUrl)
+ {
+ try
+ {
+ var request = (HttpWebRequest)WebRequest.Create(imageUrl);
+ request.Method = "HEAD";
+
+ using (var response = (HttpWebResponse)request.GetResponse())
+ {
+ return response.StatusCode == HttpStatusCode.OK;
+ }
+ }
+ catch (WebException)
{
- case Models.StreamStatus.OK:
-
- break;
- case Models.StreamStatus.ERROR:
-
- break;
- default:
-
- break;
+ return false;
}
}
- catch { }
+ }
+ @* Creating Camera Image in a Column*@
+
- }
- }
+
+
+
+
+ @*LastCheckTimeDate*@
+
+
Last Checked
+
@dev.LastCheckTime.Split(' ')[0]
+
+
+ @*LastCheckHours*@
+
+
+
@dev.LastCheckTime.Split(' ')[1]
+
+
+ @*Ip*@
+
+ @*Hostname*@
+ @if (!string.IsNullOrEmpty(dev.Hostname))
+ {
+
+
Hostname
+
@dev.Hostname
+
+ }
+
+ @*Hostname*@
+ @if (!string.IsNullOrEmpty(dev.SerialNumber))
+ {
+
+
Serial Number
+
@dev.SerialNumber
+
+ }
+
+ @*FriendlyName*@
+ @if (!string.IsNullOrEmpty(dev.FriendlyName))
+ {
+
+
FriendlyName
+
@dev.FriendlyName
+
+ }
+
+ @*Model*@
+ @if (!string.IsNullOrEmpty(dev.Model))
+ {
+
+ }
+
+ @*Location*@
+ @if (!string.IsNullOrEmpty(dev.DescriptionLocation))
+ {
+
+
Description Location
+
+
+ }
+
+ @*Presentation URL*@
+ @if (!string.IsNullOrEmpty(dev.PresentationURL))
+ {
+
+ }
+
+ @*Presentation User*@
+ @if (!string.IsNullOrEmpty(dev.User))
+ {
+
+ }
+
+ @*Password*@
+ @if (!string.IsNullOrEmpty(dev.Password))
+ {
+
+
Password
+
@dev.Password
+
+ }
+
+ @*Notes*@
+ @if (!string.IsNullOrEmpty(dev.Notes))
+ {
+
+
Notes
+
+ @(dev.Notes.Contains("discovered", StringComparison.InvariantCultureIgnoreCase) ? Html.Raw(" ") : "")
+ @(dev.Notes.Contains("static", StringComparison.InvariantCultureIgnoreCase) ? Html.Raw(" ") : "")
+ @dev.Notes
+
+
+ }
+
+ @*DeviceStatus*@
+
+
Status
+
+ @switch (dev.DeviceActiveStatus)
+ {
+ case Models.DeviceActiveStatus.ONLINE:
+ Online
+ break;
+ case Models.DeviceActiveStatus.OFFLINE:
+ Offline
+ break;
+ default:
+ Unknown
+ break;
+ }
+
+
+
+ @*DeviceStreams*@
+ @if (dev.Streams.Count > 0)
+ {
+
+
+ @for (int i = 0; i < dev.Streams.Count; i++)
+ {
+
+
Stream@(i)
+ @try
+ {
+ @switch (dev.Streams[i].Status)
+ {
+ case Models.StreamStatus.OK:
+
+ break;
+ case Models.StreamStatus.ERROR:
+
+ break;
+ default:
+
+ break;
+ }
+ }
+ catch
+ {
+
+ }
+
+ }
+
+ }
+
+
+
-
- }
+ }
+
-
+
+
@section Scripts {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-}
+ function copyDevicesJSON() {
+ var htmlToCopy = $('#modalJSONResult').text();
+ navigator.clipboard.writeText(htmlToCopy).then(() => {
+ alert("Content is copied to clipboard")
+ }).catch((error) => {
+ alert("Unable to copy the content", error);
+ });
+ }
+
+ function checkVisibleFilteredDevices() {
+ $('#cameraCardsBody .form-check input[type="checkbox"]:visible').prop('checked', true);
+ }
+
+ function resetInput(propertyName) {
+ var renameInputToReset = $('#btnRenameInput_' + propertyName);
+ renameInputToReset.val(null);
+ }
+
+ function exportSelectedDevicesAdvanced() {
+ var selectedCheckboxIds = [];
+ $('#cameraCardsBody .form-check input[type="checkbox"]').each(function () {
+ // Check if the checkbox is checked
+ if ($(this).is(':checked')) {
+ // Add the ID to the array
+ selectedCheckboxIds.push(this.id.split("_")[1]);
+ }
+ });
+ const userTemplate = $('#templateInput').val();
+ const encodedTemplate = btoa(userTemplate);
+
+ $.ajax({
+ url: `/Index?handler=ExportDevices&tpl=${encodedTemplate}&selectedCheckboxIdsParam=${btoa(JSON.stringify(selectedCheckboxIds))}`,
+ contentType: 'application/json',
+ success: function (result) {
+ jsonDeviceArray = JSON.parse(result);
+ var jsonString = JSON.stringify(jsonDeviceArray, null, 2);
+
+ $('#templateOutputAdvanced').removeAttr('data-highlighted');
+ $('#templateOutputAdvanced').html(jsonString);
+
+ //IMPORTANT, When adding highlight.js
+ //you should use JS DOM selector not JQuery selector,
+ //it is not working with jquery
+ hljs.highlightElement(document.querySelector('#templateOutputAdvanced'));
+ },
+ error: function (xhr, textStatus, errorThrown) {
+ console.error("Export error:", textStatus, errorThrown);
+
+ // Check if the response has an 'error' property
+ if (xhr.responseJSON && xhr.responseJSON.error) {
+ alert("Export error: " + xhr.responseJSON.error);
+ } else {
+ // If no specific error message, show a generic message
+ alert("Export error occurred.");
+ }
+ }
+ });
+ }
+
+ function toggleVisibility(elementId) {
+ $(elementId).toggleClass('d-none');
+ }
+
+ function toggleExportOptions() {
+ var exportOptionsButton = $("#exportOptionsButton");
+
+ exportOptionsButton.text(exportOptionsButton.text() === 'Simple Export Options' ? 'Advanced Export Options' : 'Simple Export Options');
+ toggleVisibility('#dynamicKeys');
+ toggleVisibility('#advancedExportInput');
+
+ var exportButton = $('#exportButton');
+ exportButton.attr('onclick', (exportButton.attr('onclick') === 'exportSelectedDevicesAdvanced()' ? 'exportSelectedDevices()' : 'exportSelectedDevicesAdvanced()'));
+ }
+
+ function showCreateDeviceModal() {
+ $('#createDeviceModal').modal('show');
+ }
+
+ function showPublishDeviceModal() {
+ $('#publishDeviceModal').modal('show');
+ }
+
+ function publishDevices() {
+ var url = $('#urlInput').val();
+ var username = $('#usernameInput').val();
+ var password = $('#passwordInput').val();
+ var deviceData = jsonDeviceArray;
+
+ $.ajax({
+ type: "POST",
+ url: url,
+ data: JSON.stringify({
+ username: username,
+ password: password,
+ deviceData: deviceData
+ }),
+ contentType: 'application/json',
+ success: function (res) {
+ console.log("Success", res);
+ },
+ error: function (err) {
+ console.error("Error", err);
+ }
+ });
+ }
+
+
+
+}
diff --git a/Pages/Index.cshtml.cs b/Pages/Index.cshtml.cs
index 97e4b81..efab6ad 100644
--- a/Pages/Index.cshtml.cs
+++ b/Pages/Index.cshtml.cs
@@ -2,20 +2,37 @@
using DeviceStatusCheckerService.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
+using System.Runtime.Serialization;
+using Scriban;
+using Onvif.Core.Client.Device;
+using Scriban.Runtime;
+using System.Text;
+using System.Net;
+using System.Xml.Linq;
namespace DeviceStatusCheckerService.Pages
{
public class IndexModel : PageModel
{
+ //FIELDS
+
+ //initializing instances
private readonly ILogger
_logger;
private readonly DeviceManager _deviceManager;
+ //Contructor to initialize fields for dependency injection
public IndexModel(ILogger logger, DeviceManager deviceManager)
{
_logger = logger;
_deviceManager = deviceManager;
}
+ public class JsonHTML
+ {
+ public string HTMLContent { get; set; }
+ }
+
+ //PROPERTIES
public List Devices
{
get
@@ -23,5 +40,185 @@ public List Devices
return _deviceManager.Devices;
}
}
+
+ //METHODS
+ private string RenderScribanTemplate(List selectedDevices, List selectedDynamicKeysArray, List renamedKeysArray)
+ {
+ try
+ {
+
+ //List selectedProperties = selectedDynamicKeysArray;
+ List selectedProperties = selectedDynamicKeysArray.Select(MemberRenamer).ToList();
+
+ var _deviceTemplate = Template.Parse(@"
+ [
+
+ {{ for model in models }}
+ {
+ {{- index = 0 -}}
+ {{ for selectedproperty in selectedproperties }}
+ {{if index < arraysize}}
+
+ {{ if selectedproperty == ""streams"" }}
+ ""Streams"": [
+ {{for stream in model.streams}}
+ {
+ ""Status"": ""{{ stream.status }}"",
+ ""Uri"": ""{{ stream.uri }}""
+ },
+ {{end}}
+ ]
+ {{else}}
+ ""{{renamedkeysarray[index]}}"": ""{{ model[selectedproperty]}}"",
+ {{end}}
+ {{index = index + 1}}
+ {{end}}
+ {{ end }}
+ },
+ {{ end }}
+ ]
+ ");
+
+ var renderedDevice = _deviceTemplate.Render(new
+ {
+ selectedproperties = selectedProperties,
+ selecteddynamickeysarray = selectedDynamicKeysArray,
+ models = selectedDevices,
+ renamedkeysarray = renamedKeysArray,
+ arraysize = selectedDynamicKeysArray.Count(),
+ });
+ return string.Join("", renderedDevice);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error rendering template for device");
+ return "Error rendering template for device";
+ }
+ }
+
+
+ public JsonResult OnGetExportTemplateDevices(string selectedCheckboxIdsParam, string selectedDynamicKeys, string renamedKeys)
+ {
+
+ try
+ {
+ if (selectedCheckboxIdsParam != null)
+ {
+ var selectedDynamicKeysArray = System.Text.Json.JsonSerializer.Deserialize>(selectedDynamicKeys);
+ if (selectedDynamicKeysArray.Count == 0)
+ {
+ return new JsonResult(new { error = "No labels are selected." })
+ {
+ StatusCode = (int)HttpStatusCode.BadRequest
+ };
+ }
+
+ var renamedKeysArray = System.Text.Json.JsonSerializer.Deserialize>(renamedKeys);
+ byte[] data = Convert.FromBase64String(selectedCheckboxIdsParam);
+ string selectedUUIDs = System.Text.Encoding.UTF8.GetString(data);
+ string[] uuidArray = System.Text.Json.JsonSerializer.Deserialize(selectedUUIDs);
+
+ var selectedDevices = _deviceManager.Devices
+ .Where(device => uuidArray.Contains(device.UUID))
+ .ToList();
+
+
+ // Check if selectedDevices is null or empty
+ if (selectedDevices.Count == 0)
+ {
+ return new JsonResult(new { error = "Please select devices." })
+ {
+ StatusCode = (int)HttpStatusCode.BadRequest
+ };
+ }
+ var selectedDevicesHTML = RenderScribanTemplate(selectedDevices, selectedDynamicKeysArray, renamedKeysArray);
+
+ var jsonHTML = new JsonHTML
+ {
+ HTMLContent = selectedDevicesHTML
+ };
+
+ return new JsonResult(jsonHTML);
+ }
+ else
+ {
+ return new JsonResult("No data provided");
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error exporting data");
+ return new JsonResult("Error exporting data");
+ }
+
+ }
+
+
+ public JsonResult OnGetExportDevices(string tpl, string selectedCheckboxIdsParam)
+ {
+ if (string.IsNullOrEmpty(tpl))
+ {
+ // Handle the error, throw an exception, or return an appropriate response
+ return new JsonResult(new { error = "Please fill the template." })
+ {
+ StatusCode = (int)HttpStatusCode.BadRequest
+ };
+ }
+ try
+ {
+ byte[] data = Convert.FromBase64String(selectedCheckboxIdsParam);
+ string selectedUUIDs = System.Text.Encoding.UTF8.GetString(data);
+ string[] uuidArray = System.Text.Json.JsonSerializer.Deserialize(selectedUUIDs);
+ var selectedDevices = _deviceManager.Devices
+ .Where(device => uuidArray.Contains(device.UUID))
+ .ToList();
+
+ if (selectedDevices.Count == 0)
+ {
+ return new JsonResult(new { error = "Please select devices." })
+ {
+ StatusCode = (int)HttpStatusCode.BadRequest
+ };
+ }
+
+ //TEMPLATE WORKS
+ tpl = Encoding.UTF8.GetString(Convert.FromBase64String(tpl));
+ var template = Template.Parse(tpl);
+ var result = template.Render(new { models = selectedDevices });
+ return new JsonResult(result);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error exporting data");
+ return new JsonResult("Error exporting data");
+ }
+ }
+
+
+ //MemberRenamer function for scriban
+ private static string MemberRenamer(string name)
+ {
+ var builder = new StringBuilder();
+ var previousUpper = false;
+ for (var i = 0; i < name.Length; i++)
+ {
+ var c = name[i];
+ if (char.IsUpper(c))
+ {
+ if (i > 0 && !previousUpper)
+ {
+ builder.Append("_");
+ }
+ builder.Append(char.ToLowerInvariant(c));
+ previousUpper = true;
+ }
+ else
+ {
+ builder.Append(c);
+ previousUpper = false;
+ }
+ }
+ return builder.ToString();
+ }
}
}
\ No newline at end of file