"Hänger" der Transmitter-Situation geklärt (umgeleitete Streams und vollgelaufene Buffer führen hier zu einem (obskuren!) Deadlock.
529 lines
29 KiB
C#
529 lines
29 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 object _timerlock = new object();
|
|
private bool processRunning = false;
|
|
private ILog _log = LogManager.GetLogger(typeof(NSWSendService));
|
|
|
|
public NSWSendService()
|
|
{
|
|
Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
|
|
InitializeComponent();
|
|
|
|
System.Net.ServicePointManager.ServerCertificateValidationCallback += delegate (object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate,
|
|
System.Security.Cryptography.X509Certificates.X509Chain chain,
|
|
System.Net.Security.SslPolicyErrors sslPolicyErrors)
|
|
{
|
|
return true; // **** Immer OK weil wir nur mit einem dedizierten Endpoint reden..
|
|
};
|
|
|
|
}
|
|
|
|
public void Commandline(string[] args)
|
|
{
|
|
// 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(args); // 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(args);
|
|
|
|
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();
|
|
}
|
|
|
|
public void Init(string[] args)
|
|
{
|
|
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);
|
|
}
|
|
|
|
void _timer_Elapsed(object sender, ElapsedEventArgs e)
|
|
{
|
|
lock (this._timerlock)
|
|
{
|
|
if (this.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;
|
|
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);
|
|
}
|
|
}
|
|
|
|
foreach (Message message in toSendMessageList)
|
|
{
|
|
try
|
|
{
|
|
bool sendSucceeded = false;
|
|
|
|
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;
|
|
}
|
|
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);
|
|
|
|
|
|
/*
|
|
if ((core.InitialHIS == Message.NSWProvider.DUDR) || (core.InitialHIS == Message.NSWProvider.DUDR_TEST))
|
|
{
|
|
|
|
bool sendSucceeded = true;
|
|
bool didSendSomething = false;
|
|
|
|
// Fall: keine Meldeklasse aber trotzdem auf TO_SEND. Entweder beim Beantragen von Visit-Id's oder beim Storno der gesamten
|
|
// Anmeldung
|
|
if ((core.DisplayId.Length == 0) || (core.Cancelled ?? false))
|
|
{
|
|
sendSucceeded = bsmd.hisnord.Request.CreateSendFile(core, messages, (core.InitialHIS == Message.NSWProvider.DUDR_TEST)) ?? false;
|
|
}
|
|
else
|
|
{
|
|
foreach (Message message in messages)
|
|
{
|
|
bool? sendResult = bsmd.hisnord.Request.CreateSendFile(core, message, (core.InitialHIS == Message.NSWProvider.DUDR_TEST));
|
|
if (sendResult.HasValue)
|
|
{
|
|
didSendSomething = true;
|
|
if (!sendResult.Value) sendSucceeded = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!didSendSomething) core.BSMDStatusInternal = MessageCore.BSMDStatus.PREPARE; // zurück zu neutral
|
|
else
|
|
{
|
|
if (!sendSucceeded) core.BSMDStatusInternal = MessageCore.BSMDStatus.FAILURE;
|
|
else core.BSMDStatusInternal = MessageCore.BSMDStatus.SENT;
|
|
}
|
|
|
|
_log.InfoFormat("HIS-Nord Send: Core {0} [{1}] new Status {2}", core.Id, core.IMO, core.BSMDStatusInternal.ToString());
|
|
|
|
DBManager.Instance.Save(core);
|
|
}
|
|
|
|
else
|
|
{
|
|
bool sendSucceeded;
|
|
|
|
#region DBH / Dakosy Logik
|
|
|
|
List<Message> toSendMessageList = new List<Message>();
|
|
|
|
if ((core.IsTransit && core.TransitId.IsNullOrEmpty()) ||
|
|
(!core.IsTransit && core.VisitId.IsNullOrEmpty()))
|
|
{
|
|
foreach (Message message in messages)
|
|
{
|
|
if ((message.MessageNotificationClass == Message.NotificationClass.VISIT) ||
|
|
(message.MessageNotificationClass == Message.NotificationClass.TRANSIT))
|
|
{
|
|
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;
|
|
toSendMessageList.Add(message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else // eine VISIT/TRANSIT Id ist vorhanden, die Daten können gesendet werden
|
|
{
|
|
// Änderung Sept 15: versendet werden alle Nachrichten, die *nicht* bestätigt sind
|
|
// also auch alles was auf in bearbeitung / Korrektur etc steht
|
|
// Einzig bleibt die Frage offen, was mit Nachrichten passiert, die über das UI
|
|
// nachträglich geändert werden. Ich denke hier muss in dashface ggf. der Status
|
|
// wieder auf PREPARE gesetzt werden
|
|
_log.Debug("Visit/Transit found, SENDING DATA messages");
|
|
|
|
foreach (Message message in messages)
|
|
{
|
|
// Eine Nachricht mit dem Status "Suspended / Zurückgestellt" wird so lange nicht
|
|
// versendet, bis im ENI der Status wieder explizit zurückgesetzt wird (16.4.16)
|
|
|
|
// Update 5.10.17: Alles was nicht explizit auf "TOSEND" steht wird nicht verschickt!
|
|
if (message.InternalStatus != Message.BSMDStatus.TOSEND) continue;
|
|
|
|
// "virtuelle" Messages nicht versenden (DK, EU)
|
|
if ((message.MessageNotificationClass == Message.NotificationClass.STO) ||
|
|
(message.MessageNotificationClass == Message.NotificationClass.CREWD) ||
|
|
(message.MessageNotificationClass == Message.NotificationClass.PASD))
|
|
continue;
|
|
|
|
// Wenn das ein Transit ist, werden nicht erforderliche Meldeklassen übersprungen
|
|
if (core.IsTransit)
|
|
{
|
|
if ((message.MessageNotificationClass == Message.NotificationClass.BKRD) ||
|
|
(message.MessageNotificationClass == Message.NotificationClass.PRE72H) ||
|
|
(message.MessageNotificationClass == Message.NotificationClass.TIEFD) ||
|
|
(message.MessageNotificationClass == Message.NotificationClass.NAME) ||
|
|
(message.MessageNotificationClass == Message.NotificationClass.INFO) ||
|
|
(message.MessageNotificationClass == Message.NotificationClass.ATA) ||
|
|
(message.MessageNotificationClass == Message.NotificationClass.ATD) ||
|
|
(message.MessageNotificationClass == Message.NotificationClass.LADG) ||
|
|
(message.MessageNotificationClass == Message.NotificationClass.SERV) ||
|
|
(message.MessageNotificationClass == Message.NotificationClass.WAS) ||
|
|
(message.MessageNotificationClass == Message.NotificationClass.TOWD))
|
|
continue;
|
|
}
|
|
|
|
// Visit/Transit Meldeklassen werden nicht erneut übertragen
|
|
if ((message.MessageNotificationClass == Message.NotificationClass.VISIT) ||
|
|
(message.MessageNotificationClass == Message.NotificationClass.TRANSIT))
|
|
continue;
|
|
|
|
//if ((message.InternalStatus != Message.BSMDStatus.CONFIRMED) &&
|
|
//(message.InternalStatus != Message.BSMDStatus.SENT))
|
|
//{
|
|
|
|
// 28.12.2015: Das über "Overview" eingestellte HIS ist immer "führend" (zumindest aktuell zum Testen)
|
|
// 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);
|
|
//}
|
|
}
|
|
}
|
|
|
|
foreach (Message message in toSendMessageList)
|
|
{
|
|
try
|
|
{
|
|
_log.InfoFormat("Sending {0} message to {1}",
|
|
message.MessageNotificationClass.ToString(), message.HIS.ToString());
|
|
|
|
sendSucceeded = false;
|
|
|
|
// switch über passendes HIS / Schnittstelle
|
|
switch (message.HIS)
|
|
{
|
|
case Message.NSWProvider.DBH:
|
|
case Message.NSWProvider.DBH_TEST:
|
|
if (!XtraSendLogic.ShouldSendMessage(message))
|
|
{
|
|
message.InternalStatus = Message.BSMDStatus.SUSPENDED;
|
|
}
|
|
else
|
|
{
|
|
sendSucceeded = bsmd.dbh.Request.SendMessage(message, (message.HIS == Message.NSWProvider.DBH_TEST));
|
|
if (!sendSucceeded)
|
|
message.InternalStatus = Message.BSMDStatus.SEND_FAILED;
|
|
}
|
|
|
|
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;
|
|
break;
|
|
|
|
default:
|
|
_log.WarnFormat("Initial HIS not specified for message {0}", message.Id);
|
|
break;
|
|
}
|
|
|
|
if (sendSucceeded)
|
|
{
|
|
// alte Fehlerliste entfernen (die Antwort kann praktisch noch nicht da sein)
|
|
// vor dem Versenden zu entfernen halte ich für doof, wenn das Versenden scheitert
|
|
|
|
foreach (MessageError messageError in message.ErrorList)
|
|
DBManager.Instance.Delete(messageError);
|
|
foreach (MessageViolation messageViolation in message.ViolationList)
|
|
DBManager.Instance.Delete(messageViolation);
|
|
|
|
_log.Info("send successful, saving message.");
|
|
message.InternalStatus = Message.BSMDStatus.SENT;
|
|
message.SentAt = DateTime.Now;
|
|
}
|
|
|
|
DBManager.Instance.Save(message);
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_log.ErrorFormat("SENDING message {0}: {1}", message.Id.ToString(), ex.Message);
|
|
}
|
|
}
|
|
|
|
if (toSendMessageList.Count > 0)
|
|
{
|
|
core.BSMDStatusInternal = MessageCore.BSMDStatus.SENT;
|
|
}
|
|
else
|
|
{
|
|
core.BSMDStatusInternal = MessageCore.BSMDStatus.PREPARE;
|
|
}
|
|
DBManager.Instance.Save(core);
|
|
|
|
#endregion
|
|
|
|
}
|
|
}
|
|
*/
|
|
|
|
}
|
|
|
|
bsmd.hisnord.transmitter.CallTransmitter(true);
|
|
// bsmd.hisnord.transmitter.CallTransmitter(false); // wird aktuell nicht funktionieren
|
|
|
|
// ob test oder nicht ist in stat. dict gespeicher
|
|
bsmd.hisnord.Request.ReadResponseFiles();
|
|
|
|
bsmd.hisnord.Response.ReadAnswers(true);
|
|
// bsmd.hisnord.Response.ReadAnswers(false);
|
|
|
|
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");
|
|
}
|
|
}
|
|
}
|