git_bsmd/bsmd.POService/POService.cs

278 lines
13 KiB
C#

// Copyright (c) 2020-present schick Informatik
// Description:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Globalization;
using System.ServiceProcess;
using System.Text.RegularExpressions;
using System.Timers;
using bsmd.email;
using bsmd.database;
using log4net;
namespace bsmd.POService
{
public partial class POService : ServiceBase
{
#region Fields
private Timer _timer;
private readonly ILog _log = LogManager.GetLogger(typeof(POService));
#endregion
#region Construction
public POService()
{
Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
InitializeComponent();
}
#endregion
#region Start/Stop Service
protected override void OnStart(string[] args)
{
this.EventLog.Source = this.ServiceName;
this.EventLog.Log = "Application";
this.Init();
this.EventLog.WriteEntry("NSW PO Number Service started.", EventLogEntryType.Information);
System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
string version = fvi.FileVersion;
_log.InfoFormat("Starting PO Number Service. v.{0} -------------- ", version);
this.DoOnce();
}
protected override void OnStop()
{
this._log.Info("Stopping PO Number Service");
}
#endregion
#region helper
private void Init()
{
this._timer = new Timer
{
Interval = Properties.Settings.Default.SleepSeconds * 1000
};
this._timer.Elapsed += _timer_Elapsed;
this._timer.Enabled = true;
}
internal void DoOnce()
{
this._timer_Elapsed(null, null);
}
internal static bool ParsePO(string inputValue, ref string poNumber, ref string port,
ref string shipname, ref string something, ref string dateString)
{
bool result = false;
if (inputValue.IsNullOrEmpty()) return result;
// WuselString parsen. Ein Beispiel:
// "WG: PO:8204730095 DEWVNTM-ADELINA D-005E-310120";
// Hier kann man designen: https://regex101.com/
const string poPattern = @"PO:(\d+) ([A-Z]+)-(.*?)-(.*?)-(\d{6})";
Regex poRegex = new Regex(poPattern);
Match aMatch = poRegex.Match(inputValue);
if (aMatch.Success)
{
poNumber = aMatch.Groups[1].Captures[0].Value;
port = aMatch.Groups[2].Captures[0].Value;
shipname = aMatch.Groups[3].Captures[0].Value;
something = aMatch.Groups[4].Captures[0].Value;
dateString = aMatch.Groups[5].Captures[0].Value;
result = true;
}
return result;
}
#endregion
#region timer loop handler
private void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
if (DBManager.Instance.Connect(Properties.Settings.Default.ConnectionString))
{
try
{
string messageId = "";
string mailSender = "";
const string receiptSubject = "PO number service INFO";
string mailSubject = "";
string body = "";
using (BSMDPopClient bsmdPopClient = new BSMDPopClient())
{
if (bsmdPopClient.IsConnected)
{
// check and download next e-Mail, saving attachment
while (bsmdPopClient.ReceiveSingleMailText(out messageId, out mailSender, out mailSubject, out body))
{
string receiptText = "";
// only a valid sender gets a reply
bool isValidSender = false;
foreach (string aValidSender in Properties.Settings.Default.ValidSender)
{
if (mailSender.Equals(aValidSender, StringComparison.OrdinalIgnoreCase))
{
isValidSender = true;
break;
}
}
if (!isValidSender)
{
receiptText = string.Format("ignored e - mail from illegal sender: {0}", mailSender);
_log.Warn(receiptText);
}
else
{
try
{
string poNumber = null;
string port = null;
string dateString = null;
string shipname = null;
string something = null;
// Mail nach dem PO Nummerntext durchsuchen
// Der Nummerstring ist vielleicht im Betreff, im Body oder im Dateinamen des
// PDF Attachments. In den PDF's selbst habe ich den (kompletten) String aktuell nicht gefunden.
try
{
if (!ParsePO(mailSubject, ref poNumber, ref port, ref shipname, ref something, ref dateString))
{
if (!ParsePO(body, ref poNumber, ref port, ref shipname, ref something, ref dateString))
{
string attachmentFileName = bsmdPopClient.GetNameOfFirstMailEmailPDFAttachment(messageId);
if (attachmentFileName != null)
ParsePO(attachmentFileName, ref poNumber, ref port, ref shipname, ref something, ref dateString);
}
}
}
catch(Exception ex)
{
_log.WarnFormat("Exception looking for PO number: {0}", ex.ToString());
}
if (poNumber != null)
{
// aus (ungefährem) Datum, Name und Hafen den MessageCore suchen
uint? from = null, to = null;
if (DateTime.TryParseExact(dateString, "ddMMyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime etaDate))
{
// Alle Anmeldungen des betreffenden Tages laden
Dictionary<MessageCore.SearchFilterType, string> filterDict = new Dictionary<MessageCore.SearchFilterType, string>();
// Datum filtern
from = etaDate.ToUniversalTime().Subtract(new TimeSpan(2, 0, 0, 0)).ToUnixTimeStamp(); // 2 Tage vorher
to = etaDate.ToUniversalTime().Add(new TimeSpan(23, 59, 59)).ToUnixTimeStamp(); // 1 Tag später
filterDict.Add(MessageCore.SearchFilterType.FILTER_ETA, string.Format("{0}:{1}", from?.ToString() ?? "", to?.ToString() ?? ""));
List<MessageCore> anmeldungen = DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).GetMessageCoresWithFilters(filterDict, 100);
List<MessageCore> possibleMatches = new List<MessageCore>();
// nach Hafen und Schiffsnamen ausfiltern
if (port.Length > 5) port = port.Substring(0, 5);
foreach (MessageCore foundCore in anmeldungen)
{
if (foundCore.PoC.Equals(port) && foundCore.Shipname.Equals(shipname, StringComparison.OrdinalIgnoreCase))
possibleMatches.Add(foundCore);
}
if (possibleMatches.Count == 0)
{
// nix gefunden
receiptText = string.Format("no match found for PO number {0} ship {1} date {2} port {3}",
poNumber, shipname, etaDate, port);
_log.Warn(receiptText);
}
else if (possibleMatches.Count > 1)
{
// nicht eindeutig
receiptText = string.Format("{0} matches found for PO number {1} ship {2} date {3} port {4}",
possibleMatches.Count, poNumber, shipname, etaDate, port);
_log.Warn(receiptText);
}
else
{
// PO-Nummer und Schiffs/Anlaufklassifizierung eintragen
possibleMatches[0].PONumber = poNumber;
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(possibleMatches[0]);
_log.InfoFormat("MessageCore {0} updated with po number {1}", possibleMatches[0].Id, poNumber);
}
}
else
{
_log.WarnFormat("Error parsing date component of PO string: {0}", dateString);
}
}
else
{
_log.Warn("incoming e-mail did not contain a po number");
}
}
catch (Exception someException)
{
receiptText = string.Format("Error processing po mail: {0}", someException.Message);
_log.Error(receiptText);
}
}
if (receiptText.Length > 0)
{
_log.Debug("sending system info email");
BSMDMail.SendSystemInfo(receiptSubject, receiptText, mailSender);
}
// remove e-Mail
_log.InfoFormat("deleting mail with messageId {0}", messageId);
_log.InfoFormat("mail delete {0}", bsmdPopClient.DeleteMessageByMessageId(messageId) ? "successful" : "failed");
}
}
}
DBManager.Instance.Disconnect();
}
catch (Exception ex)
{
_log.ErrorFormat("Exception occurred: {0}", ex.ToString());
}
}
else
{
_log.Error("DB Connection failure");
}
}
#endregion
}
}