diff --git a/Stundensheet.xlsx b/Stundensheet.xlsx index 65328b84..5b4c842b 100644 Binary files a/Stundensheet.xlsx and b/Stundensheet.xlsx differ diff --git a/nsw/Source/bsmd.ReportGenerator/App.config b/nsw/Source/bsmd.ReportGenerator/App.config index 96b98d8b..d40bb9be 100644 --- a/nsw/Source/bsmd.ReportGenerator/App.config +++ b/nsw/Source/bsmd.ReportGenerator/App.config @@ -45,6 +45,9 @@ + + False + \ No newline at end of file diff --git a/nsw/Source/bsmd.ReportGenerator/BSMDDocument.cs b/nsw/Source/bsmd.ReportGenerator/BSMDDocument.cs index 02c8f3d0..27626c0e 100644 --- a/nsw/Source/bsmd.ReportGenerator/BSMDDocument.cs +++ b/nsw/Source/bsmd.ReportGenerator/BSMDDocument.cs @@ -51,6 +51,23 @@ namespace bsmd.ReportGenerator return document; } + internal static Document CreateSingleClassDocument(MessageCore reportCore, List reportMessages, + Dictionary coverInfos, string classes, ReportingParty rp) + { + + Document document = new Document(); + document.Info.Title = string.Format("{0} for {1}", classes, reportCore.DisplayId); + document.Info.Subject = "Reporting class info"; + document.Info.Author = Properties.Settings.Default.ReportAuthor; + + BSMDDocument.DefineSingleStyle(document); + BSMDDocument.DefineSingleHeader(document, coverInfos, rp); + BSMDDocument.DefineContentSectionInitial(document); + + + return document; + } + /// /// create the final output document /// @@ -130,9 +147,43 @@ namespace bsmd.ReportGenerator style.ParagraphFormat.Font.Color = Colors.Blue; } + static void DefineSingleStyle(Document doc) + { + Style style = doc.Styles["Normal"]; + style.Font.Name = "Verdana"; + + Style tableStyle = doc.Styles.AddStyle("Table", "Normal"); + tableStyle.Font.Name = "Verdana"; + tableStyle.Font.Size = 9; + + // usw + Style tableHeaderStyle = doc.Styles.AddStyle("TableHeader", "Normal"); + //tableHeaderStyle.Font.Name = "Verdana"; + tableHeaderStyle.Font.Size = 10; + tableHeaderStyle.Font.Bold = true; + + Style tableValueStyle = doc.Styles.AddStyle("TableValue", "Normal"); + tableValueStyle.Font.Size = 9; + tableValueStyle.Font.Bold = true; + + style = doc.Styles["Heading2"]; + style.Font.Size = 12; + style.Font.Bold = true; + style.ParagraphFormat.SpaceBefore = 6; + style.ParagraphFormat.SpaceAfter = 6; + + style = doc.Styles["Heading3"]; + style.Font.Size = 10; + style.Font.Bold = true; + style.Font.Italic = true; + style.ParagraphFormat.SpaceBefore = 6; + style.ParagraphFormat.SpaceAfter = 3; + + } + #endregion - #region Cover + #region Cover (alt, EU-NOAD) /// /// Defines the cover page. @@ -177,13 +228,110 @@ namespace bsmd.ReportGenerator row.Cells[0].AddParagraph(key); row.Cells[1].AddParagraph(coverInfos[key] ?? string.Empty); } - } #endregion + #region "Neuer" EUREPORT Header Block für Einzelnachrichten + + internal static void DefineSingleHeader(Document document, Dictionary coverInfos, ReportingParty rp) + { + Section section = document.AddSection(); + section.PageSetup.StartingNumber = 1; + + Table table = document.LastSection.AddTable(); + table.Style = "Table"; + table.Borders.Color = Colors.LightGray; + table.Borders.Width = 0.25; + + // Define Colums + Column col = table.AddColumn(80); // die Bildspalte + col.Format.Alignment = ParagraphAlignment.Center; + + col = table.AddColumn(70); + col = table.AddColumn(140); + col = table.AddColumn(60); + col = table.AddColumn(140); + + Row row = table.AddRow(); + Image logoImage = row.Cells[0].AddImage(Properties.Settings.Default.LogoPath); + logoImage.Width = 80; + row.Cells[0].MergeDown = 6; + + Paragraph paragraph = row.Cells[1].AddParagraph("Visit Details"); + row.Cells[1].MergeRight = 3; + row.Cells[1].Shading.Color = Colors.LightGray; + paragraph.Style = "TableHeader"; + + row = table.AddRow(); + row.Cells[1].AddParagraph("Visit-ID"); + paragraph = row.Cells[2].AddParagraph(coverInfos["Visit-ID"]); + paragraph.Style = "TableValue"; + row.Cells[3].AddParagraph("Name"); + paragraph = row.Cells[4].AddParagraph(coverInfos["Ship"]); + paragraph.Style = "TableValue"; + + row = table.AddRow(); + row.Cells[1].AddParagraph("Port of call"); + paragraph = row.Cells[2].AddParagraph(coverInfos["Port of call"]); + paragraph.Style = "TableValue"; + row.Cells[3].AddParagraph("ETA"); + paragraph = row.Cells[4].AddParagraph(coverInfos["ETA"]); + paragraph.Style = "TableValue"; + + row = table.AddRow(); + row.Shading.Color = Colors.LightGray; + paragraph = row.Cells[1].AddParagraph("Reporting Party"); + row.Cells[1].MergeRight = 3; + paragraph.Style = "TableHeader"; + + row = table.AddRow(); + row.Cells[1].AddParagraph("Organization"); + paragraph = row.Cells[2].AddParagraph(rp.Name); + paragraph.Style = "TableValue"; + row.Cells[2].MergeRight = 2; + + row = table.AddRow(); + row.Cells[1].AddParagraph("Last name"); + paragraph = row.Cells[2].AddParagraph(rp.LastName); + paragraph.Style = "TableValue"; + + row.Cells[3].AddParagraph("City"); + paragraph = row.Cells[4].AddParagraph(rp.City); + paragraph.Style = "TableValue"; + + row = table.AddRow(); + row.Cells[1].AddParagraph("Phone"); + paragraph = row.Cells[2].AddParagraph(rp.Phone); + paragraph.Style = "TableValue"; + + row.Cells[3].AddParagraph("Email"); + paragraph = row.Cells[4].AddParagraph(rp.EMail); + paragraph.Style = "TableValue"; + + } + + #endregion + #region setup, header and footers + public static void DefineContentSectionInitial(Document document) + { + Section section = document.LastSection; + section.PageSetup.OddAndEvenPagesHeaderFooter = false; + section.PageSetup.Orientation = Orientation.Portrait; + HeaderFooter header = section.Headers.Primary; + header.AddParagraph(string.Format("EUREPORT - created {0}", DateTime.Now.ToString())); + + Paragraph paragraph = new Paragraph(); + paragraph.AddTab(); + paragraph.AddPageField(); + paragraph.AddText(" / "); + paragraph.AddNumPagesField(); + section.Footers.Primary.Add(paragraph); + + } + /// /// Defines page setup, headers, and footers. /// @@ -211,7 +359,7 @@ namespace bsmd.ReportGenerator // Add clone of paragraph to footer for odd pages. Cloning is necessary because an object must // not belong to more than one other object. If you forget cloning an exception is thrown. section.Footers.EvenPage.Add(paragraph.Clone()); - } + } #endregion @@ -301,7 +449,7 @@ namespace bsmd.ReportGenerator BSMDDocument.AddActualTableParagraph(document, childParagraph.MessageText, true); } } - } + } #region CREW private static void CreateCrewTable(Document document, Message message) @@ -778,16 +926,16 @@ namespace bsmd.ReportGenerator private static void AddActualTableParagraph(Document document, List> messageText, bool isSubTable) { Table table = document.LastSection.AddTable(); - + table.Borders.Color = Colors.LightGray; // table.LeftPadding = new Unit(0.5, UnitType.Centimeter); - Column leadColumn = table.AddColumn(Unit.FromCentimeter(8)); + Column leadColumn = table.AddColumn(200); leadColumn.Format.Alignment = ParagraphAlignment.Left; - Column mainColumn = table.AddColumn(Unit.FromCentimeter(8)); + Column mainColumn = table.AddColumn(290); mainColumn.Format.Alignment = ParagraphAlignment.Left; for (int i = 0; i < messageText.Count; i++) diff --git a/nsw/Source/bsmd.ReportGenerator/Properties/Settings.Designer.cs b/nsw/Source/bsmd.ReportGenerator/Properties/Settings.Designer.cs index 52fd5d2d..abf8e5aa 100644 --- a/nsw/Source/bsmd.ReportGenerator/Properties/Settings.Designer.cs +++ b/nsw/Source/bsmd.ReportGenerator/Properties/Settings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.34209 +// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -12,7 +12,7 @@ namespace bsmd.ReportGenerator.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); @@ -114,5 +114,14 @@ namespace bsmd.ReportGenerator.Properties { return ((string)(this["LogoPath"])); } } + + [global::System.Configuration.ApplicationScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool DeleteFileAfterSend { + get { + return ((bool)(this["DeleteFileAfterSend"])); + } + } } } diff --git a/nsw/Source/bsmd.ReportGenerator/Properties/Settings.settings b/nsw/Source/bsmd.ReportGenerator/Properties/Settings.settings index a7368bb1..da3a5781 100644 --- a/nsw/Source/bsmd.ReportGenerator/Properties/Settings.settings +++ b/nsw/Source/bsmd.ReportGenerator/Properties/Settings.settings @@ -35,5 +35,8 @@ + + False + \ No newline at end of file diff --git a/nsw/Source/bsmd.ReportGenerator/ReportService.cs b/nsw/Source/bsmd.ReportGenerator/ReportService.cs index cdec4ba3..d79126c8 100644 --- a/nsw/Source/bsmd.ReportGenerator/ReportService.cs +++ b/nsw/Source/bsmd.ReportGenerator/ReportService.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) 2015-2017 schick Informatik +// Service entry point + +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -10,6 +13,7 @@ using MigraDoc.DocumentObjectModel; using bsmd.database; using bsmd.email; +using System.Text; namespace bsmd.ReportGenerator { @@ -27,6 +31,8 @@ namespace bsmd.ReportGenerator InitializeComponent(); } + #region windows service overrides + protected override void OnStart(string[] args) { this.EventLog.Source = this.ServiceName; @@ -40,6 +46,15 @@ namespace bsmd.ReportGenerator this.DoOnce(); } + protected override void OnStop() + { + _log.Info("Report Service stopping."); + } + + #endregion + + #region timer functions + private void Init(string[] args) { this._timer = new Timer(); @@ -96,19 +111,16 @@ namespace bsmd.ReportGenerator { this.processRunning = false; } - } - - protected override void OnStop() - { - _log.Info("Report Service stopping."); - } + } internal void DoOnce() { this._timer_Elapsed(null, null); } - #region create and send report + #endregion + + #region create and send "old" EU-NOAD report private void CreateReport(MessageCore reportCore) { @@ -118,7 +130,7 @@ namespace bsmd.ReportGenerator bool isReportUpdate = reportCore.ReportStatus != MessageCore.ReportStatusEnum.COMPLETE; coverInfos.Add("Type", reportCore.HerbergReportType); - coverInfos.Add("Ship", DBManager.Instance.GetShipNameFromCore(reportCore)); + coverInfos.Add("Ship", DBManager.Instance.GetShipNameFromCore(reportCore)); coverInfos.Add("E-Mail Ship", reportCore.HerbergEmailContactReportingVessel); coverInfos.Add("IMO", reportCore.IMO); DateTime eta = reportCore.ETA ?? (reportCore.ETAKielCanal ?? new DateTime(0)); @@ -213,19 +225,93 @@ namespace bsmd.ReportGenerator } } + #region create (and send) "single" report (a report per message class) + internal void CreateSingleReport(MessageCore reportCore) { List messages = DBManager.Instance.GetMessagesForCore(reportCore, DBManager.MessageLoad.ALL); + List reportMessages = new List(); messages.Sort(new ANSWMessageComparer()); - Dictionary coverInfos = new Dictionary(); + StringBuilder sb = new StringBuilder(); + Guid reportingPartyId = Guid.Empty; - string subject = string.Format("NEW EU-NOAD message IMO {0}", reportCore.IMO); + foreach (Message aMessage in messages) + { + if (aMessage.InternalStatus == Message.BSMDStatus.REPORT) + { + reportMessages.Add(aMessage); + aMessage.InternalStatus = Message.BSMDStatus.PREPARE; + DBManager.Instance.Save(aMessage); + sb.Append(aMessage.MessageNotificationClassDisplay); + sb.Append(" "); + if (aMessage.ReportingPartyId.HasValue) + reportingPartyId = aMessage.ReportingPartyId.Value; + } + } + string classes = sb.ToString(); + if (!classes.IsNullOrEmpty()) + { + ReportingParty rp = null; + if (DBManager.Instance.GetReportingPartyDict().ContainsKey(reportingPartyId)) + { + rp = DBManager.Instance.GetReportingPartyDict()[reportingPartyId]; + + Dictionary coverInfos = new Dictionary(); + coverInfos.Add("Ship", DBManager.Instance.GetShipNameFromCore(reportCore)); + coverInfos.Add("ETA", reportCore.ETADisplay.ToString()); + coverInfos.Add("Port of call", reportCore.PoC); + coverInfos.Add("Visit-ID", reportCore.DisplayId); + coverInfos.Add("Class", classes); + + string subject = string.Format("PDF report IMO {0} class(es) {1}", reportCore.IMO, classes); + + Document migraDocument = BSMDDocument.CreateSingleClassDocument(reportCore, reportMessages, coverInfos, classes, rp); + + // print messages to document + foreach (Message message in reportMessages) + { + BSMDDocument.AddNSWMessageParagraph(migraDocument, message); + } + + // prepare and send E-Mail with generated attachment + string fullPath = string.Format("{0}\\{1}.pdf", Properties.Settings.Default.OutputDirectory, reportCore.Id); + + BSMDDocument.RenderDocument(migraDocument, fullPath); + _log.InfoFormat("Document created for MessageCoreId {0}, IMO {1}", reportCore.Id, reportCore.IMO); + List attachments = new List(); + attachments.Add(fullPath); + + BSMDMail.SendNSWReportWithAttachments(subject, attachments, rp.EMail); + + // remove + if (Properties.Settings.Default.DeleteFileAfterSend) + { + try + { + File.Delete(fullPath); + } + catch (Exception ex) + { + _log.ErrorFormat("Cannot delete {0}: {1}", fullPath, ex.Message); + } + } + } + else + { + _log.WarnFormat("Default reporting party not set on core {0}", reportCore.Id); + } + } + else + { + _log.WarnFormat("Core {0} set for report, but no message classes!", reportCore.Id); + } // reset report status reportCore.ReportStatus = MessageCore.ReportStatusEnum.NONE; DBManager.Instance.Save(reportCore); } + #endregion #endregion diff --git a/nsw/Source/bsmd.ReportGenerator/bsmd.ReportGenerator.csproj b/nsw/Source/bsmd.ReportGenerator/bsmd.ReportGenerator.csproj index 35e5c4b5..a98526c6 100644 --- a/nsw/Source/bsmd.ReportGenerator/bsmd.ReportGenerator.csproj +++ b/nsw/Source/bsmd.ReportGenerator/bsmd.ReportGenerator.csproj @@ -108,6 +108,7 @@ + SettingsSingleFileGenerator @@ -132,6 +133,9 @@ bsmd.email + + + \ No newline at end of file diff --git a/nsw/Source/bsmd.ReportGenerator/readme.txt b/nsw/Source/bsmd.ReportGenerator/readme.txt new file mode 100644 index 00000000..32f75e98 --- /dev/null +++ b/nsw/Source/bsmd.ReportGenerator/readme.txt @@ -0,0 +1,17 @@ +Es gibt zwei unterschiedliche Verfahren: + +1) MigraDoc Document (MigraDoc.*.dll) +Das erfolgt im HTML Style bei dem man einfach Paragrafen an ein Dokument anfügt und der +Flow wird dann vom Renderer berechnet. Es gehen auch absolute Positionen aber keine +grafischen Elemente außer Bildern und Tabellen + +2) PdfDocument (PdfSharp.*.dll) +Das ist der "Drawing" Style, hier können auch MigraDoc Dokumente eingebettet werden wenn +man beides machen will. Es geht aber über eine graph. Context (XGraphics) und absolute +Koordinaten. Man kann damit z.B. auch mehrere MigraDoc's in kleine Rectangles "reinrendern". + +Auf dieser Seite wird das sehr gut an einem Beispiel gegenübergestellt: +http://www.pdfsharp.net/wiki/MixMigraDocAndPdfSharp-sample.ashx + +Koordinatensystem ist "Points" (72pt = 1 inch), es gibt Umrechnungsfunktionen für cm etc. +In Points hat eine DIN-A4 Seite dann die Ausdehnung von 595 x 842. diff --git a/nsw/Source/bsmd.dbh.ResponseService/packages.config b/nsw/Source/bsmd.dbh.ResponseService/packages.config new file mode 100644 index 00000000..9a0551c5 --- /dev/null +++ b/nsw/Source/bsmd.dbh.ResponseService/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/nsw/Source/bsmd.dbh/Response.cs b/nsw/Source/bsmd.dbh/Response.cs index e86c36ec..584a7270 100644 --- a/nsw/Source/bsmd.dbh/Response.cs +++ b/nsw/Source/bsmd.dbh/Response.cs @@ -71,11 +71,13 @@ namespace bsmd.dbh // neue VISIT - ID aMessage.MessageCore.VisitId = VisitId; aMessage.MessageCore.BSMDStatusInternal = MessageCore.BSMDStatus.PREPARE; + aMessage.SendSuccess = true; DBManager.Instance.Save(aMessage.MessageCore); break; case dbh.response.RootType.TRANSIT: aMessage.MessageCore.TransitId = TransitId; aMessage.MessageCore.BSMDStatusInternal = MessageCore.BSMDStatus.PREPARE; + aMessage.SendSuccess = true; DBManager.Instance.Save(aMessage.MessageCore); break; case dbh.response.RootType.CANCEL: @@ -99,6 +101,7 @@ namespace bsmd.dbh (int) ReportingClassesFull[0].ReportingClass[0] == (int)aMessage.MessageNotificationClass) { // this was successful, save status to MessageHeader + aMessage.SendSuccess = true; aMessage.InternalStatus = Message.BSMDStatus.CONFIRMED; aMessage.Status = Message.MessageStatus.ACCEPTED; } @@ -125,7 +128,7 @@ namespace bsmd.dbh error.MessageHeaderId = aMessage.Id.Value; aMessage.InternalStatus = Message.BSMDStatus.ERROR; DBManager.Instance.Save(error); - + aMessage.SendSuccess = false; break; case dbh.response.RootMessageType.VIOLATION: diff --git a/nsw/Source/bsmd.hisnord/transmitter.cs b/nsw/Source/bsmd.hisnord/transmitter.cs index 7f352c45..89181858 100644 --- a/nsw/Source/bsmd.hisnord/transmitter.cs +++ b/nsw/Source/bsmd.hisnord/transmitter.cs @@ -45,7 +45,23 @@ namespace bsmd.hisnord { _log.WarnFormat("Transmitter process not exited within {0} minute", Properties.Settings.Default.BatchTimeoutMins); // kill Kill KILLLL!!! - process.Kill(); + try + { + process.Kill(); + process.WaitForExit(500); + if (process.HasExited) + { + _log.Info("Transmitter killed, process exited"); + } + else + { + _log.Warn("Killing Transmitter failed, this thing hangs and we're in trouble now"); + } + } + catch (Exception e) + { + _log.WarnFormat("Killing Transmitter failed: {0}", e.Message); + } } string errorString = process.StandardError.ReadToEnd(); if(!errorString.IsNullOrEmpty())