git_bsmd/SendNSWMessageService/NSWSendService.cs

327 lines
16 KiB
C#

using bsmd.database;
using bsmd.status;
using log4net;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.ServiceProcess;
using System.Timers;
namespace SendNSWMessageService
{
public partial class NSWSendService : ServiceBase
{
private Timer _timer;
private readonly object _timerlock = new object();
private bool processRunning = false;
private readonly ILog _log = LogManager.GetLogger(typeof(NSWSendService));
public NSWSendService()
{
Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
InitializeComponent();
System.Net.ServicePointManager.ServerCertificateValidationCallback += (object _, System.Security.Cryptography.X509Certificates.X509Certificate ____,
System.Security.Cryptography.X509Certificates.X509Chain __,
System.Net.Security.SslPolicyErrors ___) =>
{
return true; // **** Immer OK weil wir nur mit einem dedizierten Endpoint reden..
};
}
public void Commandline()
{
// Dieser Modus dient allein dazu, eine separate Instanz "nebenher" laufen zu lassen, ohne
// sie als Service zu installieren. Dies ist sinnvoll um für spezielle HIS Einstellungen Tests zu machen
// ohne den laufenden Betrieb zu stören (hoffentlich!)
this.Init(); // setup timer
this.DoOnce();
while (true)
System.Threading.Thread.Sleep(1000); // sleep until someone kills me :)
}
protected override void OnStart(string[] args)
{
this.EventLog.Source = this.ServiceName;
this.EventLog.Log = "Application";
this.Init();
System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
string version = fvi.FileVersion;
this.EventLog.WriteEntry("NSW Send Service started", EventLogEntryType.Information);
_log.InfoFormat("NSW Send Service started. v.{0}", version);
this.DoOnce();
}
private void Init()
{
this._timer = new Timer();
this._timer.Interval = Properties.Settings.Default.SleepSeconds * 1000;
this._timer.Elapsed += _timer_Elapsed;
this._timer.Enabled = true;
}
public void DoOnce()
{
this._timer_Elapsed(null, null);
}
private void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
lock (this._timerlock)
{
if (processRunning) return;
else this.processRunning = true;
}
if (DBManager.Instance.Connect(Properties.Settings.Default.ConnectionString))
{
// Datenbank auf zu sendende Objekte überprüfen und laden
List<MessageCore> coresMarkedForSending = DBManager.Instance.GetMessageCoresByStatus(MessageCore.BSMDStatus.TOSEND);
foreach (MessageCore core in coresMarkedForSending)
{
List<Message> messages = DBManager.Instance.GetMessagesForCore(core, DBManager.MessageLoad.ALL);
List<Message> toSendMessageList = new List<Message>();
bool coreSendSucceeded = true;
bool didSendSomething = false;
// Wenn es noch keine Visit-ID gibt können keine Meldeklassen versendet werden. Die Visit/Transit Meldung wird daher dann
// "leer" versendet
if ((core.IsTransit && core.TransitId.IsNullOrEmpty()) || (!core.IsTransit && core.VisitId.IsNullOrEmpty()))
{
foreach (Message message in messages)
{
if (((message.MessageNotificationClass == Message.NotificationClass.VISIT) && (!core.IsTransit)) ||
((message.MessageNotificationClass == Message.NotificationClass.TRANSIT) && (core.IsTransit)))
{
if ((message.InternalStatus == Message.BSMDStatus.UNDEFINED) ||
(message.InternalStatus == Message.BSMDStatus.PREPARE) ||
(message.InternalStatus == Message.BSMDStatus.TOSEND))
{
_log.Debug("Visit/Transit not found, sending VISIT/TRANSIT message");
if (message.HIS == Message.NSWProvider.UNDEFINED)
message.HIS = core.InitialHIS;
if (core.DefaultReportingPartyId.HasValue)
{
message.ReportingPartyId = core.DefaultReportingPartyId; // Referenz umbiegen
if (DBManager.Instance.GetReportingPartyDict().ContainsKey(core.DefaultReportingPartyId.Value)) // geladenes Objekt ersetzen
message.ReportingParty = DBManager.Instance.GetReportingPartyDict()[core.DefaultReportingPartyId.Value];
}
toSendMessageList.Add(message);
}
}
}
}
else
{
// VISIT / TRANSIT Id vorhanden, Daten können übertragen werden
foreach (Message message in messages)
{
// Update 5.10.17: Alles was nicht explizit auf "TOSEND" steht wird nicht verschickt!
if (message.InternalStatus != Message.BSMDStatus.TOSEND) continue;
// "Virtuelle" Meldeklassen (bisher: DK) werden hier nicht versendet!
if ((message.MessageNotificationClass == Message.NotificationClass.STO) ||
(message.MessageNotificationClass == Message.NotificationClass.CREWD) ||
(message.MessageNotificationClass == Message.NotificationClass.PASD))
continue;
if ((message.MessageNotificationClass == Message.NotificationClass.VISIT) ||
(message.MessageNotificationClass == Message.NotificationClass.TRANSIT))
continue;
if (!XtraSendLogic.ShouldSendMessage(message))
{
message.InternalStatus = Message.BSMDStatus.SUSPENDED;
message.ChangedBy = ""; // zurücksetzen für Overview
DBManager.Instance.Save(message);
continue;
}
if (core.DefaultReportingPartyId.HasValue)
{
message.ReportingPartyId = core.DefaultReportingPartyId; // Referenz umbiegen
if (DBManager.Instance.GetReportingPartyDict().ContainsKey(core.DefaultReportingPartyId.Value)) // geladenes Objekt ersetzen
message.ReportingParty = DBManager.Instance.GetReportingPartyDict()[core.DefaultReportingPartyId.Value];
}
toSendMessageList.Add(message);
}
}
// nach NotificationClass sortieren
// das ist wichtig für 861,881,882: NOA_NOD vor ATA vor ATD, falls alles gleichzeitig gesendet wird.
toSendMessageList.Sort();
foreach (Message message in toSendMessageList)
{
try
{
bool sendSucceeded = false;
if(message.HIS == Message.NSWProvider.UNDEFINED)
message.HIS = core.InitialHIS;
_log.InfoFormat("Sending {0} message to {1}", message.MessageNotificationClass.ToString(), message.HIS.ToString());
// switch über passendes HIS / Schnittstelle
switch (message.HIS)
{
case Message.NSWProvider.DBH:
case Message.NSWProvider.DBH_TEST:
sendSucceeded = bsmd.dbh.Request.SendMessage(message, message.HIS == Message.NSWProvider.DBH_TEST);
if (!sendSucceeded)
message.InternalStatus = Message.BSMDStatus.SEND_FAILED;
else
didSendSomething = true;
break;
case Message.NSWProvider.DAKOSY:
case Message.NSWProvider.DAKOSY_TEST:
sendSucceeded = bsmd.dakosy.Request.Send(message, true);
if (!sendSucceeded) message.InternalStatus = Message.BSMDStatus.SEND_FAILED;
else didSendSomething = true;
break;
case Message.NSWProvider.DUDR:
case Message.NSWProvider.DUDR_TEST:
bool? sendResult = bsmd.hisnord.Request.CreateSendFile(core, message, core.InitialHIS == Message.NSWProvider.DUDR_TEST);
if (sendResult.HasValue)
{
didSendSomething = true;
sendSucceeded = sendResult.Value;
}
break;
default:
_log.WarnFormat("Initial HIS not specified for message {0}", message.Id);
break;
}
coreSendSucceeded &= sendSucceeded;
if(sendSucceeded)
{
message.ChangedBy = ""; // Leeren nach RS mit CH: Sie möchte das Feld als Indikator "zu versenden" verwenden (ich war dagegen ;-)
message.InternalStatus = Message.BSMDStatus.SENT;
if(message.ReportingPartyId.HasValue && DBManager.Instance.GetReportingPartyDict().ContainsKey(message.ReportingPartyId.Value))
{
bsmd.database.ReportingParty rp = DBManager.Instance.GetReportingPartyDict()[message.ReportingPartyId.Value];
message.SentBy = rp.Logon;
}
}
else
{
message.InternalStatus = Message.BSMDStatus.SEND_FAILED;
}
message.StatusInfo = "";
message.SentAt = DateTime.Now;
DBManager.Instance.DeleteSystemErrors(message);
DBManager.Instance.Save(message);
}
catch (Exception ex)
{
_log.ErrorFormat("SENDING message {0}: {1}", message.Id.ToString(), ex.Message);
}
}
if(!didSendSomething) // bisher nichts passiert, aber auf "TO_SEND", ist das ein Storno?
{
if (core.Cancelled ?? false)
{
switch(core.InitialHIS)
{
case Message.NSWProvider.DUDR:
coreSendSucceeded = bsmd.hisnord.Request.CreateSendFile(core, null, false) ?? false;
didSendSomething = true;
break;
case Message.NSWProvider.DUDR_TEST:
coreSendSucceeded = bsmd.hisnord.Request.CreateSendFile(core, null, true) ?? false;
didSendSomething = true;
break;
case Message.NSWProvider.DBH:
coreSendSucceeded = bsmd.dbh.Request.SendCancelCore(core, false);
break;
case Message.NSWProvider.DBH_TEST:
coreSendSucceeded = bsmd.dbh.Request.SendCancelCore(core, true);
break;
default:
_log.WarnFormat("Cancelling for HIS {0} is not supported", core.InitialHIS);
break;
}
}
}
if (didSendSomething)
{
// falls nur eine Nachricht gescheitert ist geht der Core auf SEND_FAILED, sonst FAILURE
core.BSMDStatusInternal = coreSendSucceeded ? MessageCore.BSMDStatus.SENT : MessageCore.BSMDStatus.FAILURE;
}
else
{
core.BSMDStatusInternal = MessageCore.BSMDStatus.PREPARE; // aus irgendwelchen Gründen wurde nichts verschickt -> zurück auf PREPARE
}
DBManager.Instance.Save(core);
}
bsmd.hisnord.transmitter.CallTransmitter();
// ob test oder nicht ist in stat. dict gespeichert
bsmd.hisnord.Request.ReadResponseFiles();
bsmd.hisnord.Response.ReadAnswers();
List<MessageCore> coresMarkedForStatusQuery = DBManager.Instance.GetMessageCoresWithNSWStatusFlag();
foreach (MessageCore core in coresMarkedForStatusQuery)
{
core.QueryNSWStatus = false; // reset flag
Status aStatus = new Status(core);
aStatus.PerformQuery();
}
DBManager.Instance.Disconnect();
}
else
{
// _log.Fatal("database connection failure, stopping service");
this.EventLog.WriteEntry("NSW Send Service DB connection failure", EventLogEntryType.Warning);
// this.Stop();
}
lock (this._timerlock)
{
this.processRunning = false;
}
}
protected override void OnPause()
{
this._timer.Stop();
}
protected override void OnContinue()
{
this._timer.Start();
}
protected override void OnStop()
{
this._timer.Enabled = false;
this.EventLog.WriteEntry("NSW Send Service stopped.", EventLogEntryType.Information);
_log.Info("NSW Send Service stopped");
}
}
}