stattdessen wird ein open command gesendet. Gleichzeitig wird die Ausgabe nicht mehr zum Ende gelesen sondern per Events abgefragt. Die Hoffnung ist hier, dass der Prozess nicht mehr auf dem ReadToEnd() blockiert, wobei das nie bestätigt werden konnte.
340 lines
17 KiB
C#
340 lines
17 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 Timer _filesTimer;
|
|
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;
|
|
|
|
this._filesTimer = new Timer();
|
|
this._filesTimer.Interval = Properties.Settings.Default.PurgeFilesTimerIntervalHours * 60 * 60 * 1000; // hours to millisecs
|
|
this._filesTimer.Elapsed += _filesTimer_Elapsed;
|
|
this._filesTimer.Enabled = true;
|
|
}
|
|
|
|
private void _filesTimer_Elapsed(object sender, ElapsedEventArgs e)
|
|
{
|
|
bsmd.dbh.MessageController.PurgeOldFiles(Properties.Settings.Default.TempFilesMaxAgeDays);
|
|
bsmd.hisnord.transmitter.PurgeOldFiles(Properties.Settings.Default.TempFilesMaxAgeDays);
|
|
}
|
|
|
|
public void DoOnce()
|
|
{
|
|
this._filesTimer_Elapsed(null, null);
|
|
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) continue;
|
|
|
|
// muss zum Go Live wieder einkommentiert werden, da CREWD und PASD vorerst nicht versendet werden
|
|
|
|
// XXX : TODO herausnehmen
|
|
if((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 {1} to {2}", message.MessageNotificationClass.ToString(), message.Id, message.HIS.ToString());
|
|
|
|
// switch über passendes HIS / Schnittstelle
|
|
switch (message.HIS)
|
|
{
|
|
|
|
case Message.NSWProvider.DBH:
|
|
case Message.NSWProvider.DBH_MAERSK:
|
|
sendSucceeded = bsmd.dbh.MessageController.SendMessage(core, message);
|
|
if (!sendSucceeded)
|
|
message.InternalStatus = Message.BSMDStatus.SEND_FAILED;
|
|
else
|
|
didSendSomething = true;
|
|
break;
|
|
case Message.NSWProvider.DAKOSY:
|
|
sendSucceeded = bsmd.dakosy.Request.Send(message);
|
|
if (!sendSucceeded) message.InternalStatus = Message.BSMDStatus.SEND_FAILED;
|
|
else didSendSomething = true;
|
|
break;
|
|
case Message.NSWProvider.DUDR:
|
|
bool? sendResult = bsmd.hisnord.Request.CreateSendFile(core, message);
|
|
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;
|
|
didSendSomething = true;
|
|
break;
|
|
case Message.NSWProvider.DBH:
|
|
case Message.NSWProvider.DBH_MAERSK:
|
|
coreSendSucceeded = bsmd.dbh.MessageController.SendCancelCore(core);
|
|
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);
|
|
|
|
}
|
|
|
|
// external processing for HIS-Nord
|
|
if (bsmd.hisnord.transmitter.Transmit())
|
|
{
|
|
bsmd.hisnord.Request.ReadResponseFiles();
|
|
bsmd.hisnord.Response.ReadAnswers();
|
|
}
|
|
|
|
// external processing for dbh
|
|
bsmd.dbh.MessageController.SendAndReceive();
|
|
|
|
foreach (MessageCore core in DBManager.Instance.GetMessageCoresWithNSWStatusFlag())
|
|
{
|
|
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();
|
|
this._filesTimer.Stop();
|
|
}
|
|
|
|
protected override void OnContinue()
|
|
{
|
|
this._timer.Start();
|
|
this._filesTimer.Start();
|
|
}
|
|
|
|
protected override void OnStop()
|
|
{
|
|
this._timer.Enabled = false;
|
|
this._filesTimer.Enabled = false;
|
|
this.EventLog.WriteEntry("NSW Send Service stopped.", EventLogEntryType.Information);
|
|
_log.Info("NSW Send Service stopped");
|
|
}
|
|
}
|
|
}
|