namespace LaaProduction.Services { using LaaProduction.SQL.Interfaces; using LaaProduction.Services.Interfaces; using LaaProduction.Services.Models; using LaaProduction.Services.Models.Reports; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; public class ProtocolService : IProtocolService { private const string PROTOCOL = "PROTOCOL"; private readonly ISQLConnection sqlConnection; public ProtocolService(ISQLConnection sqlConnection) => this.sqlConnection = sqlConnection; public bool DeleteFile(int fileId) => this.sqlConnection .CreateCommand($@" DELETE FROM [File] WHERE [FileId] = @{nameof(fileId)}; DELETE FROM [FilesOrders] OUTPUT [Deleted].[FileId] WHERE [FileId] = @{nameof(fileId)};") .SetParameter(nameof(fileId), fileId) .ExecuteScalar(x => x.GetInt()) > 0; public string FileContent(int? fileId) { var fileContent = this.sqlConnection .CreateCommand($@" SELECT [dbo].[File].[FileContent] FROM [dbo].[File] WHERE [dbo].[File].[FileId] = @{nameof(fileId)}") .SetParameter(nameof(fileId), fileId) .FirstOrDefault(reader => reader.GetBytes()) ?? Array.Empty(); return string.Format("data:application/pdf;base64,{0}", Convert.ToBase64String(fileContent)); } public OrderClientModel FindOrder(int orderId) { /// Für ungültige auftrag nummer einfach leere objekt zurück geben. if (orderId <= 0) { return new OrderClientModel(); } /// Abfragen der algemeinen auftrag daten var model = this.sqlConnection .CreateCommand($@" SELECT [APG].[AuftragNr] , [K].[KundenNr] , [K].[Name] FROM [AuftragPosition_Gesamt] AS [APG] JOIN [Auftrag_Gesamt] AS [AG] ON [AG].[AuftragNr] = [APG].[AuftragNr] JOIN [IdentNr] AS [IN] ON [IN].[IdentNr] = [APG].[Identnr] AND [IN].[Typ] = 'GNS' JOIN [Kunde] AS [K] ON [K].[KundenNr] = [AG].[KundenNr] WHERE [APG].[AuftragNr] = @{nameof(orderId)}") .SetParameter(nameof(orderId), orderId) .FirstOrDefault(reader => new OrderClientModel { OrderId = reader.GetInt(), ClientId = reader.GetInt(), ClientName = reader.GetString() }); /// Wenn auftrag nicht gefunden ist einfach leere objekt zurück geben. if (model is null) { return new OrderClientModel(); } model.Results = this.LoadTestResults(orderId); model.Positions = model .Results .GroupBy(x => x.PosNr) .Where(x => x.Key > 0) .OrderBy(x => x.Key) .ToDictionary(x => x.Key, x => x.All(r => r.Succeeded)); /// Abfragen bestähende protokolle. model.Files = this.sqlConnection .CreateCommand($@" SELECT COUNT([FileId]) FROM [FilesOrders] WHERE [OrderId] = @{nameof(orderId)} AND [Usage] = '{PROTOCOL}'") .SetParameter(nameof(orderId), orderId) .FirstOrDefault(x => x.GetInt()); return model; } public IEnumerable> FindOrdersById(int orderno) => this.sqlConnection .CreateCommand($@" SELECT DISTINCT [APG].[AuftragNr] , [K].[Name] FROM [AuftragPosition_Gesamt] AS [APG] JOIN [Auftrag_Gesamt] AS [AG] ON [AG].[AuftragNr] = [APG].[AuftragNr] JOIN [IdentNr] AS [IN] ON [IN].[IdentNr] = [APG].[Identnr] AND [IN].[Typ] = 'GNS' JOIN [Kunde] AS [K] ON [K].[KundenNr] = [AG].[KundenNr] WHERE [APG].[AuftragNr] LIKE @{nameof(orderno)}") .SetParameter(nameof(orderno), $"%{orderno}%") .ExecuteReader(reader => new KeyValuePair( key: reader.GetInt(), value: reader.GetString())); public IEnumerable> FindOrdersByClient(string clientName) => this.sqlConnection .CreateCommand($@" SELECT DISTINCT [APG].[AuftragNr] , [K].[Name] FROM [AuftragPosition_Gesamt] AS [APG] JOIN [Auftrag_Gesamt] AS [AG] ON [AG].[AuftragNr] = [APG].[AuftragNr] JOIN [IdentNr] AS [IN] ON [IN].[IdentNr] = [APG].[Identnr] AND [IN].[Typ] = 'GNS' JOIN [Kunde] AS [K] ON [K].[KundenNr] = [AG].[KundenNr] WHERE [K].[Name] LIKE @{nameof(clientName)}") .SetParameter(nameof(clientName), $"%{clientName}%") .ExecuteReader(reader => new KeyValuePair( key: reader.GetInt(), value: reader.GetString())); public IDictionary GetFiles(int orderId) { var files = this.sqlConnection .CreateCommand($@" SELECT [FileId] , [FileContent] FROM [File] WHERE [FileId] IN (SELECT [FileId] FROM [FilesOrders] WHERE [OrderId] = @{nameof(orderId)}) ORDER BY [FileId] ASC") .SetParameter(nameof(orderId), orderId) .ExecuteReader(x => new { Key = x.GetInt(), Value = string.Format("data:application/pdf;base64,{0}", Convert.ToBase64String(x.GetBytes())) }) ?.ToDictionary(x => x.Key, x => x.Value) ?? new Dictionary(); return files; } public IEnumerable LoadTestResults(int orderId) => this.sqlConnection .CreateCommand($@" SELECT DISTINCT [APS].[PositionNr] AS [PosNr] -- 0 , ISNULL([APS].[KundeneigeneSerienNr], [APS].[SerienNr]) AS [SerialNr] -- 1 , [CPT].[CordonelPressureTest_Valid] AS [3TightnessTest] -- 2 , [DP].[Dicht] AS [4PressureAndTightness] -- 3 , MAX([APS].[StatusFertigung]) AS [5Metrologicaltesting] -- 4 , [CRRH].[Result] AS [6ConfigComparisonAndRadiotest] -- 5 FROM [AuftragPositionSerienNr] AS [APS] LEFT JOIN [Genesis_Meter] AS [GM] ON [GM].[Seriennummer] = [APS].[SerienNr] LEFT JOIN [MapPcbIdToSerialNumber] AS [PCB] ON [PCB].[MapPcbIdToSerialNumber_SerialNumber] = [GM].[Seriennummer] LEFT JOIN [Cordonel_Radio_RSSI_History] AS [CRRH] ON [CRRH].[RadioAdress] = [GM].[Adresse] - 10000000000 AND [CRRH].[IsLast] = 1 AND [CRRH].[Result] = 1 LEFT JOIN [Druckpruefung] AS [DP] ON [DP].[FabNr] = [PCB].[MapPcbIdToSerialNumber_PcbId] AND [DP].[Dicht] = 1 LEFT JOIN [Cordonel_PressureTest] AS [CPT] ON [CPT].[CordonelPressureTest_PcbId] = [PCB].[MapPcbIdToSerialNumber_PcbId] AND [CPT].[CordonelPressureTest_Valid] = 1 WHERE [APS].[AuftragNr] = @{nameof(orderId)} GROUP BY [APS].[PositionNr] , [APS].[SerienNr] , [APS].[KundeneigeneSerienNr] , [CPT].[CordonelPressureTest_Valid] , [DP].[Dicht] , [CRRH].[Result] ORDER BY [APS].[PositionNr] , ISNULL([APS].[KundeneigeneSerienNr], [APS].[SerienNr])") .SetParameter(nameof(orderId), orderId) .ExecuteReader(reader => new TestStatus { PosNr = reader.GetSmallint(), SerialNr = reader.GetString(), HeliumTest = reader.GetValue(), PressureTest = reader.GetValue(), MetroTest = this.MetrologicalValue(reader.GetValue()), RadioTest = reader.GetValue(), }) .OrderBy(x => !x.Succeeded) .ThenBy(x => x.PosNr); public bool OrderCompleted(int orderId) { var sumResults = this.sqlConnection .CreateCommand($@" SELECT CAST(CASE WHEN ISNULL([CPT].[CordonelPressureTest_Valid], 1) = 1 THEN 0 ELSE 1 END AS INT) + CAST(CASE WHEN ISNULL([DP].[Dicht], 1) = 1 THEN 0 ELSE 1 END AS INT) + ISNULL(MAX([APS].[StatusFertigung]) - 30, 0) + CAST(CASE WHEN ISNULL([CRRH].[Result], 1) = 1 THEN 0 ELSE 1 END AS INT) FROM [AuftragPositionSerienNr] AS [APS] LEFT JOIN [Genesis_Meter] AS [GM] ON [GM].[Seriennummer] = [APS].[SerienNr] LEFT JOIN [MapPcbIdToSerialNumber] AS [PCB] ON [PCB].[MapPcbIdToSerialNumber_SerialNumber] = [GM].[Seriennummer] LEFT JOIN [Cordonel_Radio_RSSI_History] AS [CRRH] ON [CRRH].[RadioAdress] = [GM].[Adresse] - 10000000000 AND [CRRH].[IsLast] = 1 AND [CRRH].[Result] = 1 LEFT JOIN [Druckpruefung] AS [DP] ON [DP].[FabNr] = [PCB].[MapPcbIdToSerialNumber_PcbId] AND [DP].[Dicht] = 1 LEFT JOIN [Cordonel_PressureTest] AS [CPT] ON [CPT].[CordonelPressureTest_PcbId] = [PCB].[MapPcbIdToSerialNumber_PcbId] AND [CPT].[CordonelPressureTest_Valid] = 1 WHERE [APS].[AuftragNr] = @{nameof(orderId)} GROUP BY [APS].[PositionNr] , [APS].[SerienNr] , [APS].[KundeneigeneSerienNr] , [CPT].[CordonelPressureTest_Valid] , [DP].[Dicht] , [CRRH].[Result]") .SetParameter(nameof(orderId), orderId) .ExecuteReader(reader => reader.GetInt()) .Sum(x => x); return sumResults == 0; } public int SaveFile(Stream fileStream, int clientId, int orderId) { var fileName = $"{clientId}_{orderId}.pdf"; var fileContent = this.GetBuffer(fileStream); var fileMD5Buffer = MD5.Create().ComputeHash(fileContent); var fileMD5 = BitConverter .ToString(fileMD5Buffer) .Replace("-", string.Empty) .ToLower(); var length = fileMD5?.Length; var exists = this.sqlConnection .CreateCommand($@" SELECT COUNT(1) FROM [FilesOrders] WHERE [Hash] = @{nameof(fileMD5)}") .SetParameter(nameof(fileMD5), fileMD5) .FirstOrDefault(x => x.GetInt()) > 0; if (exists) { return -1; } return this.sqlConnection .CreateCommand($@" INSERT INTO [dbo].[File] ( [FileName] , [FileContent]) OUTPUT @{nameof(orderId)}, [Inserted].[FileId], '{PROTOCOL}', @{nameof(fileMD5)} INTO [FilesOrders]([OrderId], [FileId], [Usage], [Hash]) OUTPUT [Inserted].[FileId] VALUES (@{nameof(fileName)} , @{nameof(fileContent)})") .SetParameter(nameof(orderId), orderId) .SetParameter(nameof(fileName), fileName) .SetParameter(nameof(fileContent), fileContent) .SetParameter(nameof(fileMD5), fileMD5) .ExecuteScalar(x => x.GetInt()); } private byte[] GetBuffer(Stream fileStream) { var fileContent = new byte[fileStream.Length]; using (var memoryStream = new MemoryStream()) { fileStream.CopyTo(memoryStream); fileContent = memoryStream.ToArray(); } return fileContent; } private bool? MetrologicalValue(byte? value) { if (value is null) { return null; } return value == 30; } } }