diff --git a/MP-TAB-SERV/Components/AlarmsMan.razor b/MP-TAB-SERV/Components/AlarmsMan.razor index f88c457c..60caf372 100644 --- a/MP-TAB-SERV/Components/AlarmsMan.razor +++ b/MP-TAB-SERV/Components/AlarmsMan.razor @@ -1,5 +1,72 @@ -

AlarmsMan

- -@code { + +@if (isProcessing) +{ + } +else +{ + + + + + + + + @foreach (var item in ListPaged) + { + + + + } + +
+ Elenco Allarmi +
+
+
+
+ Mem: @($"{item.MemAddress}.{@item.MemIndex}") +
+
+ @item.ValDecoded +
+
+
+
+
+
+ @($"{item.DtRif:ddd dd.MM.yy HH:mm:ss}") +
+
+ @($"{item.Duration:N2} min") +
+
+
+ @if (!string.IsNullOrEmpty(item.UserAck)) + { + @item.UserAck + } +
+
+ @if (item.ReqNotify != 0) + { + + } + @if (item.ReqAck != 0) + { + + } + @*
+ Invia +
*@ + @*
+ Set +
*@ +
+
+
+
+
+ +} \ No newline at end of file diff --git a/MP-TAB-SERV/Components/AlarmsMan.razor.cs b/MP-TAB-SERV/Components/AlarmsMan.razor.cs new file mode 100644 index 00000000..da71a876 --- /dev/null +++ b/MP-TAB-SERV/Components/AlarmsMan.razor.cs @@ -0,0 +1,158 @@ +using global::Microsoft.AspNetCore.Components; +using Microsoft.JSInterop; +using MP.Data; +using MP.Data.DatabaseModels; +using MP.Data.Services; +using static EgwCoreLib.Utils.DtUtils; + +namespace MP_TAB_SERV.Components +{ + public partial class AlarmsMan + { + #region Public Properties + + [Parameter] + public MappaStatoExpl? RecMSE { get; set; } = null; + + #endregion Public Properties + + #region Public Methods + + /// + /// Aggiorno valori produzione alla data richiesta... + /// + /// + public async Task doUpdate() + { + isProcessing = true; + await Task.Delay(1); + if (!string.IsNullOrEmpty(IdxMaccSel)) + { + ListComplete = await TabDServ.AlarmLogListFilt(IdxMaccSel, CurrPeriodo.Inizio, CurrPeriodo.Fine, false); + TotalCount = ListComplete.Count; + // esegue paginazione + UpdateTable(); + } + isProcessing = false; + await Task.Delay(1); + } + + #endregion Public Methods + + #region Protected Fields + + protected bool isProcessing = false; + protected int NumRecPage = 10; + protected int PageNum = 1; + + protected int TotalCount = 0; + + #endregion Protected Fields + + #region Protected Properties + + protected List ListComplete { get; set; } = new List(); + + protected List ListPaged { get; set; } = new List(); + + [Inject] + protected TabDataService TabDServ { get; set; } = null!; + + + [Inject] + protected MailService MailServ { get; set; } = null!; + + #endregion Protected Properties + + #region Protected Methods + + protected override async Task OnInitializedAsync() + { + await Task.Delay(1); + if (RecMSE != null) + { + IdxMaccSel = RecMSE.IdxMacchina; + await doUpdate(); + } + } + + protected void SaveNumRec(int newNum) + { + NumRecPage = newNum; + UpdateTable(); + } + + protected void SavePage(int newNum) + { + PageNum = newNum; + UpdateTable(); + } + + protected async Task SetMacc(string selIdxMacc) + { + isProcessing = true; + await Task.Delay(10); + IdxMaccSel = selIdxMacc; + await doUpdate(); + isProcessing = false; + await Task.Delay(10); + } + + protected async Task SetPeriodo(Periodo newPeriodo) + { + CurrPeriodo = newPeriodo; + await doUpdate(); + } + + protected void UpdateTable() + { + // esegue paginazione + if (TotalCount > NumRecPage) + { + ListPaged = ListComplete.Skip((PageNum - 1) * NumRecPage).Take(NumRecPage).ToList(); + } + else + { + ListPaged = ListComplete; + } + } + + #endregion Protected Methods + + #region Private Properties + + private Periodo CurrPeriodo { get; set; } = new Periodo(); + private string IdxMaccSel { get; set; } = ""; + + #endregion Private Properties + + protected async Task SendNotify(AlarmLogModel currAlarm) + { + if (!await JSRuntime.InvokeAsync("confirm", $"Sicuro di voler inviare notifica allarme?{Environment.NewLine}[{currAlarm.ValDecoded}]")) + return; + + isProcessing = true; + List dest = new List(); + dest.Add("samuele@steamware.net"); + string body = $"Registrato allarme {currAlarm.ValDecoded}"; + MailKitMailData mData = new MailKitMailData() + { + To = dest, + Subject = "MAPO Alarm Notification", + Body = body + }; + await MailServ.SendAsync(mData); + isProcessing = false; + await Task.Delay(1); + } + protected async Task DoAck(AlarmLogModel currAlarm) + { + if (!await JSRuntime.InvokeAsync("confirm", $"Sicuro di voler registrare ACK dell'allarme?{Environment.NewLine}[{currAlarm.ValDecoded}]")) + return; + await Task.Delay(1); + } + + [Inject] + protected IJSRuntime JSRuntime { get; set; } = null!; + } +} \ No newline at end of file diff --git a/MP-TAB-SERV/Pages/Alarms.razor b/MP-TAB-SERV/Pages/Alarms.razor index 2c43a4e6..f57a87a9 100644 --- a/MP-TAB-SERV/Pages/Alarms.razor +++ b/MP-TAB-SERV/Pages/Alarms.razor @@ -6,8 +6,6 @@ } else { -
- -
- + + } diff --git a/MP-TAB-SERV/Program.cs b/MP-TAB-SERV/Program.cs index 00bb3068..31f145a1 100644 --- a/MP-TAB-SERV/Program.cs +++ b/MP-TAB-SERV/Program.cs @@ -2,6 +2,7 @@ using Blazored.LocalStorage; using Blazored.SessionStorage; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; +using MP.Data; using MP.Data.Services; using StackExchange.Redis; @@ -32,6 +33,10 @@ builder.Services.AddScoped(); builder.Services.AddBlazoredLocalStorage(); builder.Services.AddBlazoredSessionStorage(); +// gestione email +builder.Services.Configure(builder.Configuration.GetSection(nameof(MailKitMailSettings))); +builder.Services.AddScoped(); + builder.Services.AddHttpContextAccessor(); diff --git a/MP-TAB-SERV/appsettings.json b/MP-TAB-SERV/appsettings.json index dc8a9a46..56779bcd 100644 --- a/MP-TAB-SERV/appsettings.json +++ b/MP-TAB-SERV/appsettings.json @@ -22,5 +22,27 @@ "BaseUrl": "/MP/TAB3", "ImgBasePath": "C:\\Steamware\\macchine", "CodModulo": "MoonPro" + }, + "MailKitMailSettings": { + "DisplayName": "MAPO EgalWare Email BOT", + "From": "steamwarebot@outlook.it", + "Host": "smtp-mail.outlook.com", + "Password": "siamoInViaNazionale93", + "Port": 587, + "UserName": "steamwarebot@outlook.it", + "UseSSL": false, + "UseStartTls": true } + //"ExternalProviders": { + // "MailKit": { + // "SMTP": { + // "Address": "smtp.gmail.com", + // "Port": "465", + // "Account": "steamwarebot@gmail.com", + // "Password": "drmfsls16", + // "SenderEmail": "steamwarebot@gmail.com", + // "SenderName": "Steamware Email BOT" + // } + // } + //} } diff --git a/MP.Data/Controllers/MpTabController.cs b/MP.Data/Controllers/MpTabController.cs index 42a71178..75aed825 100644 --- a/MP.Data/Controllers/MpTabController.cs +++ b/MP.Data/Controllers/MpTabController.cs @@ -25,6 +25,33 @@ namespace MP.Data.Controllers #region Public Methods + /// + /// Elenco allarmi macchina + /// + /// Macchina + /// Inizio periodo + /// Fine periodo + /// + /// + public List AlarmLogListFilt(string idxMacchina, DateTime dtFrom, DateTime dtTo, bool showMulti) + { + List dbResult = new List(); + using (var dbCtx = new MoonProContext(_configuration)) + { + var IdxMacc = new SqlParameter("@IdxMacchina", idxMacchina); + var DtFrom = new SqlParameter("@dtFrom", dtFrom); + var DtTo = new SqlParameter("@dtTo", dtTo); + var ShowMulti = new SqlParameter("@showMulti", showMulti); + + dbResult = dbCtx + .DbSetAlarmLog + .FromSqlRaw("EXEC stp_AL_getFilt @IdxMacchina, @dtFrom, @DtTo, @showMulti", IdxMacc, DtFrom, DtTo, ShowMulti) + .AsNoTracking() + .ToList(); + } + return dbResult; + } + /// /// Restituisce l'anagrafica EVENTI per intero /// diff --git a/MP.Data/DatabaseModels/AlarmLogModel.cs b/MP.Data/DatabaseModels/AlarmLogModel.cs new file mode 100644 index 00000000..71bd0c1b --- /dev/null +++ b/MP.Data/DatabaseModels/AlarmLogModel.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +#nullable disable +// +// This is here so CodeMaid doesn't reorganize this document +// +namespace MP.Data.DatabaseModels +{ + [Table("AlarmLog")] + public partial class AlarmLogModel + { + #region Public Properties + + [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int AlarmLogId { get; set; } = 0; + public DateTime DtRif { get; set; } = DateTime.Now; + public decimal Duration { get; set; } = 0; + public string MachineId { get; set; } = ""; + public string MemAddress { get; set; } = ""; + public int MemIndex { get; set; } = 0; + public int StatusVal { get; set; } = 0; + public string ValDecoded { get; set; } = ""; + public DateTime DtNotify { get; set; } = DateTime.Now; + public string UserAck { get; set; } = ""; + public DateTime DtAck { get; set; } = DateTime.Now; + + [NotMapped] + public int ReqNotify + { + get => Duration > 1 && DtNotify < DtRif ? 1 : 0; + } + [NotMapped] + public int ReqAck + { + get => DtNotify > DtRif && DtAck < DtRif ? 1 : 0; + } + + #endregion Public Properties + } +} \ No newline at end of file diff --git a/MP.Data/MP.Data.csproj b/MP.Data/MP.Data.csproj index 5ed841ee..457668ad 100644 --- a/MP.Data/MP.Data.csproj +++ b/MP.Data/MP.Data.csproj @@ -9,6 +9,8 @@ + + @@ -18,6 +20,8 @@ + + diff --git a/MP.Data/MailKitMailData.cs b/MP.Data/MailKitMailData.cs new file mode 100644 index 00000000..286c54d8 --- /dev/null +++ b/MP.Data/MailKitMailData.cs @@ -0,0 +1,35 @@ +using Microsoft.AspNetCore.Http; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MP.Data +{ + public class MailKitMailData + { + // Receiver + public List To { get; set; } + public List Bcc { get; set; } + + public List Cc { get; set; } + + // Sender + public string? From { get; set; } + + public string? DisplayName { get; set; } + + public string? ReplyTo { get; set; } + + public string? ReplyToName { get; set; } + + // Content + public string Subject { get; set; } + + public string? Body { get; set; } + + // Attach (opzionale) + public IFormFileCollection? Attachments { get; set; } + } +} diff --git a/MP.Data/MailKitMailSettings.cs b/MP.Data/MailKitMailSettings.cs new file mode 100644 index 00000000..ed3a9afa --- /dev/null +++ b/MP.Data/MailKitMailSettings.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MP.Data +{ + public class MailKitMailSettings + { + public string? DisplayName { get; set; } + public string? From { get; set; } + public string? UserName { get; set; } + public string? Password { get; set; } + public string? Host { get; set; } + public int Port { get; set; } + public bool UseSSL { get; set; } + public bool UseStartTls { get; set; } + } +} diff --git a/MP.Data/MoonProContext.cs b/MP.Data/MoonProContext.cs index 237e0da4..8b59ca5c 100644 --- a/MP.Data/MoonProContext.cs +++ b/MP.Data/MoonProContext.cs @@ -37,6 +37,8 @@ namespace MP.Data #region Public Properties + + public virtual DbSet DbSetAlarmLog { get; set; } public virtual DbSet DbSetStatArticoli { get; set; } public virtual DbSet DbSetArticoli { get; set; } public virtual DbSet DbSetAnagEventi { get; set; } diff --git a/MP.Data/Services/IMailService.cs b/MP.Data/Services/IMailService.cs new file mode 100644 index 00000000..11f29aef --- /dev/null +++ b/MP.Data/Services/IMailService.cs @@ -0,0 +1,15 @@ +using Org.BouncyCastle.Asn1.Pkcs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace MP.Data.Services +{ + public interface IMailService + { + Task SendAsync(MailKitMailData mailData, CancellationToken ct); + } +} diff --git a/MP.Data/Services/MailService.cs b/MP.Data/Services/MailService.cs new file mode 100644 index 00000000..01e3f7b3 --- /dev/null +++ b/MP.Data/Services/MailService.cs @@ -0,0 +1,139 @@ +using MailKit.Net.Smtp; +using MailKit.Security; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; +using MimeKit; +using NLog; +using System; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MP.Data.Services +{ + /// + /// Implementazione MailKit come descritto qui + /// https://blog.christian-schou.dk/send-emails-with-asp-net-core-with-mailkit/ + /// + public class MailService : IMailService + { + #region Public Constructors + + public MailService(IOptions settings) + { + _settings = settings.Value; + } + + #endregion Public Constructors + + #region Public Methods + + public async Task SendAsync(MailKitMailData mailData, CancellationToken ct = default) + { + try + { + // Initialize a new instance of the MimeKit.MimeMessage class + var mail = new MimeMessage(); + + #region Sender / Receiver + + // Sender + mail.From.Add(new MailboxAddress(_settings.DisplayName, mailData.From ?? _settings.From)); + mail.Sender = new MailboxAddress(mailData.DisplayName ?? _settings.DisplayName, mailData.From ?? _settings.From); + + // Receiver + foreach (string mailAddress in mailData.To) + mail.To.Add(MailboxAddress.Parse(mailAddress)); + + // Set Reply to if specified in mail data + if (!string.IsNullOrEmpty(mailData.ReplyTo)) + mail.ReplyTo.Add(new MailboxAddress(mailData.ReplyToName, mailData.ReplyTo)); + + // BCC Check if a BCC was supplied in the request + if (mailData.Bcc != null) + { + // Get only addresses where value is not null or with whitespace. x = value of address + foreach (string mailAddress in mailData.Bcc.Where(x => !string.IsNullOrWhiteSpace(x))) + mail.Bcc.Add(MailboxAddress.Parse(mailAddress.Trim())); + } + + // CC Check if a CC address was supplied in the request + if (mailData.Cc != null) + { + foreach (string mailAddress in mailData.Cc.Where(x => !string.IsNullOrWhiteSpace(x))) + mail.Cc.Add(MailboxAddress.Parse(mailAddress.Trim())); + } + + #endregion Sender / Receiver + + #region Content + + // Add Content to Mime Message + var body = new BodyBuilder(); + mail.Subject = mailData.Subject; + body.HtmlBody = mailData.Body; + mail.Body = body.ToMessageBody(); + + // Check if we got any attachments and add the to the builder for our message + if (mailData.Attachments != null) + { + byte[] attachmentFileByteArray; + + foreach (IFormFile attachment in mailData.Attachments) + { + // Check if length of the file in bytes is larger than 0 + if (attachment.Length > 0) + { + // Create a new memory stream and attach attachment to mail body + using (MemoryStream memoryStream = new MemoryStream()) + { + // Copy the attachment to the stream + attachment.CopyTo(memoryStream); + attachmentFileByteArray = memoryStream.ToArray(); + } + // Add the attachment from the byte array + body.Attachments.Add(attachment.FileName, attachmentFileByteArray, ContentType.Parse(attachment.ContentType)); + } + } + } + + #endregion Content + + #region Send Mail + + using var smtp = new SmtpClient(); + + if (_settings.UseSSL) + { + await smtp.ConnectAsync(_settings.Host, _settings.Port, SecureSocketOptions.SslOnConnect, ct); + } + else if (_settings.UseStartTls) + { + await smtp.ConnectAsync(_settings.Host, _settings.Port, SecureSocketOptions.StartTls, ct); + } + await smtp.AuthenticateAsync(_settings.UserName, _settings.Password, ct); + await smtp.SendAsync(mail, ct); + await smtp.DisconnectAsync(true, ct); + + #endregion Send Mail + + return true; + } + catch (Exception exc) + { + Log.Error($"Eccezione in SendAsync{Environment.NewLine}{exc}"); + return false; + } + } + + #endregion Public Methods + + #region Private Fields + + private readonly MailKitMailSettings _settings; + private static Logger Log = LogManager.GetCurrentClassLogger(); + + #endregion Private Fields + } +} \ No newline at end of file diff --git a/MP.Data/Services/TabDataService.cs b/MP.Data/Services/TabDataService.cs index 4a5b154b..b1cedb4c 100644 --- a/MP.Data/Services/TabDataService.cs +++ b/MP.Data/Services/TabDataService.cs @@ -890,6 +890,44 @@ namespace MP.Data.Services return result; } + /// + /// Elenco allarmi macchina + /// + /// Macchina + /// Inizio periodo + /// Fine periodo + /// + /// + public async Task> AlarmLogListFilt(string idxMacchina, DateTime dtFrom, DateTime dtTo, bool showMulti) + { + string source = "DB"; + Stopwatch sw = new Stopwatch(); + sw.Start(); + List? result = new List(); + // cerco in redis... + string currKey = $"{redisBaseKey}:AlarmLog:{idxMacchina}:{dtFrom:yyyyMMdd-HHmmss}:{dtTo::yyyyMMdd-HHmmss}:{showMulti}"; + RedisValue rawData = await redisDb.StringGetAsync(currKey); + if (rawData.HasValue) + { + result = JsonConvert.DeserializeObject>($"{rawData}"); + source = "REDIS"; + } + else + { + result = await Task.FromResult(dbTabController.AlarmLogListFilt(idxMacchina, dtFrom, dtTo, showMulti)); + // serializzp e salvo... + rawData = JsonConvert.SerializeObject(result); + await redisDb.StringSetAsync(currKey, rawData, UltraFastCache); + } + if (result == null) + { + result = new List(); + } + sw.Stop(); + Log.Debug($"AlarmLogListFilt | {source} | {sw.Elapsed.TotalMilliseconds}ms"); + return result; + } + /// Elenco PODL macchina /// /// Macchina