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 coresMarkedForSending = DBManager.Instance.GetMessageCoresByStatus(MessageCore.BSMDStatus.TOSEND); foreach (MessageCore core in coresMarkedForSending) { List messages = DBManager.Instance.GetMessagesForCore(core, DBManager.MessageLoad.ALL); List toSendMessageList = new List(); 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 to {1}", message.MessageNotificationClass.ToString(), 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 bsmd.hisnord.transmitter.CallTransmitter(); bsmd.hisnord.Request.ReadResponseFiles(); bsmd.hisnord.Response.ReadAnswers(); // external processing for dbh bsmd.dbh.MessageController.SendAndReceive(); List 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"); } } }