using BS.Shared; using BS.Shared.AppSender; using BS.Shared.Core; using BS.Shared.DataContracts; using BS.Shared.DataContracts.GkvAbrechnung; using BS.Shared.Exceptions; using System; using System.Collections.Generic; using System.Configuration; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace DakotaSender { class Program { static bool time_in_line = false; static bool is_dakota_running = false; static Dictionary> ten2user2req; static void Main(string[] args) { if (Config.QueueFolder.GetFiles("*.auf", SearchOption.TopDirectoryOnly).Length == 0) { return; } var logfolderpath = Config.LogPath; var logfilename = Config.LogSingleFile ? Config.LogFileName : $"log_{DateTime.Today:yyyyMMdd}.txt"; var log4day = Path.Combine(logfolderpath, logfilename); FileStream filestream = new FileStream(log4day, FileMode.Append); var streamwriter = new StreamWriter(filestream); streamwriter.AutoFlush = true; Console.SetOut(streamwriter); Console.SetError(streamwriter); try { PrintSeperator(); HandleQueueFiles(); PrintSeperator(); HandleResults(); } catch (Exception e) { HandleException(e, null); if (is_dakota_running) { PrintSeperator(); PrintLine($"Verarbeitung im Silent Modus fehlgeschlagen. dakota30.exe läuft (bereits)."); PrintLine("Ignoriere Verarbeitung aktuell. Versuche im nächsten Programm Aufruf erneut."); } PrintSeperator(); } PrintLine(); } internal static void PrintTitle() { PrintLine(); PrintSeperator(); PrintLine("Dakota Sender Modul"); PrintSeperator(); } internal static void PrintSeperator() => PrintLine("-------------------"); internal static void PrintEnd() { PrintLine("Ende"); PrintSeperator(); } internal static void PrintLine() => PrintWithTime("\n", false); internal static void PrintLine(string line) { PrintWithTime(line + "\n", !time_in_line); time_in_line = false; } internal static void Print(string line) { PrintWithTime(line, !time_in_line); time_in_line = true; } internal static void PrintWithTime(string line, bool showTime) { if (showTime) Console.Write($"[{DateTime.Now:dd.MM.yy HH:mm:ss.fff}] {line}"); else Console.Write(line); } internal static void PrintFileInfo(DakotaSystemFile file) { Print($"Verarbeite Datei: {file.SourceNutzdatendatei.Name}" ); PrintLine($"(T:{file.Tenant}, U:{file.ApplicationUserOid}, P:{file.ProtokollOid}, D:{file.Datenannahmestelle})"); } internal static void HandleException(Exception e, string tenant) { MailUtils.SendGkvModuleErrorMail("GkvSender Exception", e, tenant); PrintLine(e.ToString()); } static void CheckDakotaStatus(bool sub = false) { if (sub) Print("- "); Print("Überprüfe Dakota Status: "); var code = ExecuteDakota("-s"); Returncodes.EvaluateStatusCode(code, out is_dakota_running, out bool accept, out string message); if (accept) { PrintLine("Ok."); } else { throw new Exception($"Statuscode {code} wird nicht akzeptiert. {message}"); } } static Dictionary>> ScanQueueFolder() { var queue = Config.QueueFolder; var files = queue.GetFiles("*.auf", SearchOption.TopDirectoryOnly) .OrderBy(x => x.FullName); var ten2dat2files = new Dictionary>>(); foreach (var auft in files) { var file = ScanQueueFile(auft); if (file is null) continue; var tenant = file.Tenant; var datenannahmestelle = file.Datenannahmestelle; if (!ten2dat2files.ContainsKey(tenant)) ten2dat2files.Add(tenant, new Dictionary>()); var dat2files = ten2dat2files[tenant]; if (!dat2files.ContainsKey(datenannahmestelle)) dat2files.Add(datenannahmestelle, new List()); ten2dat2files[tenant][datenannahmestelle].Add(file); } if (ten2dat2files.Any()) return ten2dat2files; PrintLine("Keine gültigen Dakota Dateien in Queue gefunden."); return null; } static DakotaSystemFile ScanQueueFile(FileInfo auft) { string tenant = null; try { var nutz_fullname = auft.FullName.Substring(0, auft.FullName.Length - 4); var nutz = new FileInfo(nutz_fullname); if (!nutz.Exists) { PrintLine($"{auft.FullName} wird ignoriert. {nutz.FullName} nicht vorhanden"); return null; } PrintLine($"- {auft.FullName}"); var split = nutz.Name.Split('_'); tenant = split[0]; var app_oid = long.Parse(split[1]); var p_oid = long.Parse(split[2]); var datenannahmestelle = split[3]; var transfername = split[4]; var test = DakotaSenderUtils.IsTenantTest(tenant); var allow = DakotaSenderUtils.IsTenantAllowed(tenant); if(!test && !allow) { PrintLine($"{auft.FullName} wird ignoriert. Tenant \'{tenant}\' ungültig."); return null; } var file = new DakotaSystemFile() { TestCase = test, SourceAuftragsdatei = auft, SourceNutzdatendatei = nutz, Tenant = tenant, ProtokollOid = p_oid, ApplicationUserOid = app_oid, Datenannahmestelle = datenannahmestelle, Transfername = transfername, }; return file; } catch(Exception e) { HandleException(e, tenant); } return null; } static void HandleQueueFiles() { CheckDakotaStatus(); PrintSeperator(); PrintLine("Scanne Dakota Dateien:"); var ten2dat2files = ScanQueueFolder(); PrintSeperator(); if (ten2dat2files is null) { return; } ten2user2req = new Dictionary>(); foreach (var kv in ten2dat2files) { var ten = kv.Key; var dat2files = kv.Value; foreach (var kv2 in dat2files) { var dat = kv2.Key; var files = kv2.Value; //PrintLine($"Bearbeite Datenannahmestelle \'{dat}\' für Tenant \'{ten}\':"); HandleQueueFiles(files); CheckDakotaStatus(true); } } } static void HandleQueueFiles(List files) { bool error = false; foreach (var file in files) { if (!error) { var msg = HandleQueueFile(file, out bool success); if (success) { PrintLine(msg); Add2Results(file, GkvTransferResultType.Success); FileController.CopyFileToCopy(file); FileController.CopyFileToSuccesss(file); } else { error = true; PrintLine("Fehler gefunden. " + msg); Add2Results(file, GkvTransferResultType.FailSoft, msg, "DS:Fehler"); FileController.CopyFileToCopy(file); FileController.CopyFileToFailed(file); } } else { PrintLine($"Folgefehler: {file.SourceNutzdatendatei.Name}"); Add2Results(file, GkvTransferResultType.FailFollowUp, "Wird neu erstellt und übertragen.", "DS:Folgefehler"); } FileController.DeleteQueueFile(file); } } /// /// Bearbeitet eine File. Wenn dakota.exe während der Bearbeitung gestartet wird, wird das Dakota Verzeichnis gesäubert und die weitere Bearbeitung durch throw Exception unterbrochen. /// /// /// /// static string HandleQueueFile(DakotaSystemFile file, out bool success) { success = false; PrintFileInfo(file); if (file.TestCase) { success = true; return "- TestCase. "; } if (!Config.IsDatenannahmestelleValid(file.Datenannahmestelle)) return $"- Datenannahmestelle {file.Datenannahmestelle} unbekannt."; FileController.CopyFileToDestination(file); Print("- Sende via Dakota. "); var rc = ExecuteDakota("-x"); Returncodes.EvaluateSendCode(rc, out is_dakota_running, out success, out string message); if (!success) { FileController.DeleteDakotaFile(file); } if (is_dakota_running) { throw new Exception(); } if (success) return "Ok."; else return message; } static void Add2Results(DakotaSystemFile file, GkvTransferResultType type, string nachricht = null, string title = null) { var tenant = file.Tenant; var user = file.ApplicationUserOid; if (!ten2user2req.ContainsKey(tenant)) ten2user2req.Add(tenant, new Dictionary()); var user2req = ten2user2req[tenant]; if (!user2req.ContainsKey(user)) user2req.Add(user, new GkvClientResultRequestDC() { UserOid = file.ApplicationUserOid, Tenant = file.Tenant, GkvSecretKey = Config.GkvSecretKey }); var req = user2req[user]; if (req.ResultDCs is null) req.ResultDCs = new List(); var item = new GkvServerResultDC(); item.Datum = DateTime.Now; item.Nachricht = nachricht; item.ProtokollOid = file.ProtokollOid; item.ResultType = type; item.Titel = title; req.ResultDCs.Add(item); } static void HandleResults() { if (ten2user2req is null || !ten2user2req.Any()) return; PrintLine("Sende Ergebnisse:"); PrintSeperator(); var task = HandleResultsAsync(); task.Wait(); if (task.IsFaulted) throw task.Exception; } static async Task HandleResultsAsync() { var tasks = new List(); foreach (var tenant in ten2user2req.Keys) { foreach (var user in ten2user2req[tenant].Keys) { var req = ten2user2req[tenant][user]; var task = HandleResult(req, tenant, user); tasks.Add(task); } } await Task.WhenAll(tasks); } static async Task HandleResult(GkvClientResultRequestDC req, string tenant, long user) { var res = new GkvResponseDC(false, "empty"); try { PrintLine($"Sende Ergebnis an: Tenant: {req.Tenant}, User; {req.UserOid}"); res = await GkvSender.SendKundenAppClientRequestAsync(req, tenant, user); if (res.Success) PrintLine("Erfolgreich!"); else PrintLine("Fehler: " + res.Message); } catch (GkvException e) { HandleException(e, tenant); } catch (Exception e) { HandleException(e, tenant); } PrintSeperator(); if (res.Success) return; foreach (var result in req.ResultDCs) { FileController.SaveAsXmlFileInResultFolder(result, tenant, user); } PrintSeperator(); } static int ExecuteDakota(string arg) { var fileinfo = Config.DakotaExe; var process = Process.Start(Config.DakotaExe.FullName, arg); var success = process.WaitForExit(Config.ProcessWaitForExitMs); if (success) { return process.ExitCode; } else { PrintLine($"{fileinfo.Name} {arg} wird ausgeführt"); throw new Exception($"dakota30.exe - Timeout exceeded ({Config.ProcessWaitForExitMs} ms). "); } } } }