// Copyright (c) 2020-present schick Informatik // Description: Verarbeitung von empfangenen Rückmeldungen using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Xml.Serialization; using log4net; using bsmd.database; namespace bsmd.dbh { internal static class ResponseUtil { private static readonly ILog _log = LogManager.GetLogger(typeof(ResponseUtil)); private static readonly Regex _regexFilename = new Regex(@".*NSW\.DBH\.BSMD\.(.*)\.xml"); internal static bool Read(string inputFile) { bool result = false; if(!inputFile.EndsWith(".xml", StringComparison.OrdinalIgnoreCase)) { _log.ErrorFormat("no xml file: {0}", inputFile); return result; } Match m = _regexFilename.Match(inputFile); if(!m.Success) { _log.WarnFormat("returned file doesn't follow naming convention NSW.DBH.BSMD.*:{0}", inputFile); return result; } // Achtung! Die laufende Nummer der rücklaufenden Datei ist _nicht_ dieselbe und kann zur Identifikation der Meldeklasse // nicht verwendet werden string fileSeqString = m.Groups[1].Value; if(!Int32.TryParse(fileSeqString, out int fileSeqNum)) { _log.ErrorFormat("matched file sequence number couldn't be parsed: {0}", fileSeqString); return result; } bsmd.dbh.Response.Root root = null; try { XmlSerializer serializer = new XmlSerializer(typeof(bsmd.dbh.Response.Root)); using (Stream s = new FileStream(inputFile, FileMode.Open)) { root = (bsmd.dbh.Response.Root) serializer.Deserialize(s); } Message sentMessage = null; MessageCore aCore = null; if(Guid.TryParse(root.SenderReference, out Guid refGuid)) { // load message(s?) by file seq string sentMessage = DBManager.Instance.GetMessageById(refGuid) as Message; if (sentMessage == null) { _log.ErrorFormat("cannot find a message for file sequence number {0}", fileSeqNum); return result; } aCore = DBManager.Instance.GetMessageCoreById(sentMessage.MessageCoreId.Value); if (aCore == null) { _log.ErrorFormat("There is no core with id {0}", sentMessage.MessageCoreId.Value); return result; } } else { _log.ErrorFormat("sender ref {0} is no guid", root.SenderReference); return result; } switch(root.Type) { case Response.RootType.VISIT: if (aCore.VisitId.IsNullOrEmpty() && !root.VisitId.IsNullOrEmpty()) { aCore.VisitId = root.VisitId; sentMessage.SendSuccess = true; sentMessage.Status = Message.MessageStatus.ACCEPTED; sentMessage.InternalStatus = Message.BSMDStatus.CONFIRMED; _log.InfoFormat("Received Visit-Id {0} for core {1}", root.VisitId, aCore.Id); } break; case Response.RootType.TRANSIT: if (aCore.TransitId.IsNullOrEmpty() && !root.TransitId.IsNullOrEmpty()) { aCore.TransitId = root.TransitId; sentMessage.SendSuccess = true; sentMessage.Status = Message.MessageStatus.ACCEPTED; sentMessage.InternalStatus = Message.BSMDStatus.CONFIRMED; _log.InfoFormat("Received Transit-Id {0} for core {1}", root.TransitId, aCore.Id); } break; case Response.RootType.DATA: if(root.ReportingClassesFull?.ReportingClass.Length > 0) { _log.InfoFormat("Message {0} confirmed (full), {1} messages", sentMessage.MessageNotificationClassDisplay, root.Messages?.Length); sentMessage.SendSuccess = true; sentMessage.Status = Message.MessageStatus.ACCEPTED; sentMessage.InternalStatus = Message.BSMDStatus.CONFIRMED; } if (root.ReportingClassesPartial?.ReportingClass.Length > 0) { _log.WarnFormat("Message {0} confirmed (partial), {1} messages", sentMessage.MessageNotificationClassDisplay, root.Messages?.Length); sentMessage.SendSuccess = true; sentMessage.Status = Message.MessageStatus.ACCEPTED; sentMessage.InternalStatus = Message.BSMDStatus.VIOLATION; } if (root.ReportingClassesError?.ReportingClass.Length > 0) { _log.ErrorFormat("Message {0} rejected, {1} messages", sentMessage.MessageNotificationClassDisplay, root.Messages?.Length); sentMessage.SendSuccess = false; sentMessage.Status = Message.MessageStatus.REJECTED; sentMessage.InternalStatus = Message.BSMDStatus.ERROR; sentMessage.StatusInfo = "Errors reported"; } break; case Response.RootType.RESET: if(root.ReportingClassesResetted?.ReportingClass.Length > 0) { _log.InfoFormat("Message {0} RESET confirmed, {1} messages", sentMessage.MessageNotificationClassDisplay, root.Messages.Length); ; sentMessage.SendSuccess = false; // bestätigter Reset setzt grünen Buppel zurück sentMessage.Status = Message.MessageStatus.ACCEPTED; sentMessage.InternalStatus = Message.BSMDStatus.CONFIRMED; } break; case Response.RootType.CANCEL: _log.InfoFormat("Core {0} CANCEL confirmed", aCore.DisplayId); aCore.BSMDStatusInternal = MessageCore.BSMDStatus.RESPONDED; break; } // "alte" Meldungen entfernen foreach (MessageError existingError in sentMessage.ErrorList) DBManager.Instance.Delete(existingError); foreach (MessageViolation existingViolation in sentMessage.ViolationList) DBManager.Instance.Delete(existingViolation); if (root.Messages != null) { foreach (Response.RootMessage rootMessage in root.Messages) { _log.InfoFormat("Message[{0}]: {1} {2} {3}", rootMessage.ID, rootMessage.Type, rootMessage.Location, rootMessage.Text); switch (rootMessage.Type) { case Response.RootMessageType.ERROR: MessageError me = new MessageError(); me.MessageHeaderId = sentMessage.Id.Value; me.MessageHeader = sentMessage; me.ErrorText = rootMessage.Text; DBManager.Instance.Save(me); break; case Response.RootMessageType.VIOLATION: MessageViolation mv = new MessageViolation(); mv.MessageHeaderId = sentMessage.Id.Value; mv.MessageHeader = sentMessage; mv.ViolationText = rootMessage.Text; DBManager.Instance.Save(mv); break; case Response.RootMessageType.XSD_ERROR: // TODO break; case Response.RootMessageType.INFO: // TODO break; case Response.RootMessageType.WARNING: // TODO break; } } } DBManager.Instance.Save(sentMessage); if (!(aCore.Cancelled ?? false)) aCore.BSMDStatusInternal = MessageCore.BSMDStatus.RESPONDED; DBManager.Instance.Save(aCore); result = true; } catch(Exception ex) { _log.ErrorFormat("Failed to deserialize return message: {0}", ex.Message); return result; } return result; } } }