Compare commits

...

62 Commits

Author SHA1 Message Date
80e35223e0 Increased version to 1.2.1.2 2024-05-15 09:43:41 +02:00
dd61368233 avoid thread termination by catching exception thrown on failed times GET 2024-05-14 17:53:53 +02:00
5bf5a2c8fa Bugfix for update deadlock and increased version to 1.2.1.1 2024-05-14 10:48:11 +02:00
a289b014a3 Increased version to 1.2.1.0 2024-05-14 08:26:42 +02:00
f26465398a reduced retry to single request and reset the error text line 2024-05-13 19:32:41 +02:00
cc743cd602 Bugfix: Only continue on refresh timer if ui Update has been completed 2024-05-13 17:18:47 +02:00
fe2499707d fixed database connection reference 2024-05-06 11:25:35 +02:00
ccef84e119 Increased version to 1.2.0.10 in prod. version 2024-05-06 09:21:11 +02:00
1f9a3876ad Bugfix mixup enabled/readonly 2024-05-05 11:38:15 +02:00
4007459e93 increased 1 hour comparison by 60 seconds to avoid seconds deviation 2024-05-05 11:37:53 +02:00
c5a88cb8f2 created release (blue) version from test version 2024-05-02 14:26:49 +02:00
5edfa0522f created release (blue) version from test version 2024-05-02 14:26:24 +02:00
1164c8055d fixed another SQL typo 2024-05-02 13:19:38 +02:00
dc62bd005a fixed schedule sql 2024-05-02 10:49:01 +02:00
70d8f053bb Maximum threshold should still be allowed (e.g. 1 hour) 2024-05-02 10:43:09 +02:00
4cacc4809c increased version 2024-05-02 09:41:08 +02:00
3734e672fc set schedule logging to INFO and using correct query for schedule test calls 2024-05-02 09:10:37 +02:00
db0bcea485 fixed wrong offset of evaluation lights (traffic) due to erroneous cast to int 2024-05-02 09:09:24 +02:00
Max Metz
29618fbf93 fixing: generator didn't stop after throw() 2024-04-30 07:01:13 +02:00
e28739561f fixed labels for test version 1.2.0.8 2024-04-30 06:56:32 +02:00
Max Metz
a92338c92e adapting validation rules for version 1.2. Rules 0002: the time-difference threshold for 'disagreement' is expanded to 1 hour. There is now element-wise comparison of elements to circumvent instabilities from rounding pd.Timestamp objects. Rules 0001 L&M and 0003: created a feature flag, which skips the evaluation of Terminal times altogether. For version 1.2, this feature flag is enabled. 2024-04-29 13:45:27 +02:00
f690387be8 improved sorting and using COALESCE instead of not null 2024-04-29 11:32:45 +02:00
3579d779e8 Catch exceptions on history updates 2024-04-29 07:56:57 +02:00
c705b4396f fixed display of remarks text fields across all input editors 2024-04-28 13:36:40 +02:00
3e4eebfb7b Created DELETE script for old data used in a stored procedure 2024-04-28 13:34:36 +02:00
032f0ebba8 Increased labels for test version 1.2.0.7 2024-04-25 10:33:35 +02:00
7abbd190b5 Calling dialog for outgoing shipcall async to show incoming call asap 2024-04-25 09:07:39 +02:00
afac489299 Optics adjustment: Scrollbar width, text cutoff with ... and set textareas to readonly but enabled for better viewing 2024-04-25 08:11:14 +02:00
8e5b20995d Fixed bug where app was accidentally switched to en-us locale by old ENI code 2024-04-24 11:21:59 +02:00
f2328fb18c Increased labels for test version 1.2.0.6 2024-04-23 17:35:48 +02:00
212e76f7cb Make lock time editable only for port authority and show it in the overview grid 2024-04-23 12:01:50 +02:00
f58688499a Added history filter for own shipcalls only, added wait cursor for longer reloading ops 2024-04-22 10:46:34 +02:00
9f80f2cf5f Removed seconds from ATA/ATD display 2024-04-22 09:26:39 +02:00
f87901e432 Fixed a bug where complete shiplist was selectable instead of only non-deleted ships after closing the shiplist editor 2024-04-22 09:18:54 +02:00
ec65355473 localized the shipcall type combobox selection 2024-04-22 09:11:02 +02:00
f232285e76 intermediate client version 1.2.0.5 2024-04-20 08:37:43 +02:00
b0c6b639be Fixed bug where ship was not initially shown in overview 2024-04-19 16:55:29 +02:00
634c638e27 pimped up the optics of a shipcall cell (a bit) 2024-04-19 16:07:00 +02:00
829b7d9c3c Resized agent controls to not cut off a label 2024-04-18 15:45:29 +02:00
56d9346f9d Avoid crash if delete is tried on deleted object 2024-04-18 15:36:15 +02:00
8b2b454f97 increased version for test system 2024-04-13 15:55:42 +02:00
7147b92c75 Added a manual refresh button because.. well.. they asked for it 2024-04-13 12:55:47 +02:00
f0cc749026 Bugfix for creating new shipcalls 2024-04-13 12:32:28 +02:00
1cf2f3b8de Remove old shipcalls if time filter ETA FROM is cleared again 2024-04-12 09:44:56 +02:00
16e244e757 Only allow non deleted ships to be selected and safeguard the create ship dialog 2024-04-12 09:17:59 +02:00
3455139c74 fix for ship logical delete server-side 2024-04-12 08:40:36 +02:00
1a3146cf85 Set a default value for time range filter minimum 2024-04-10 14:57:49 +02:00
7432e58b6a increased test version in pub. profile 2024-04-10 14:55:48 +02:00
16b8b6366b Improved on history display, showing type and eta/etd.
Also using a link label style to allow to move overview grid by clicking on the element
2024-04-10 13:46:10 +02:00
d25eea2f92 Make sorting take agency times as more important into account if they are set 2024-04-10 12:04:29 +02:00
004908e9c0 Allow other time ref points only when shipcall is an arrival 2024-04-10 11:51:49 +02:00
dc39a62293 log shipcall post query 2024-04-10 11:02:40 +02:00
504e36d97b Fixed small issues when creating a new ship 2024-04-10 10:21:34 +02:00
32a1d93840 Made all edit dialogs resizable and added scrollviewers to text inputs 2024-04-09 15:44:06 +02:00
7d196957d3 fixed POST when a null evaluation enum is sent 2024-04-08 13:49:52 +02:00
28767fb4c3 fixed conn. data 2024-04-08 13:31:00 +02:00
0a8e78e6d2 fixed serialization in case of null values read 2024-04-08 13:27:33 +02:00
6a05e7494b log incoming data on shipcall POST 2024-04-08 13:12:30 +02:00
e7586b9747 do not show undefined entry in shipcall type combobox 2024-04-08 12:38:37 +02:00
7056f1f4d2 Prevent adding of empty records by checking if required fields have been set 2024-04-08 11:26:36 +02:00
931b81d5e4 Added Tooltip for fixed order button in edit dialog 2024-04-08 10:43:53 +02:00
79ff161b4e Changed paths and colors for Test version 2024-04-03 20:05:22 +02:00
59 changed files with 993 additions and 379 deletions

View File

@ -4833,7 +4833,7 @@ namespace BreCalClient.misc.Client
{
Proxy = null;
UserAgent = WebUtility.UrlEncode("OpenAPI-Generator/1.0.0/csharp");
BasePath = "https://brecaldevel.bsmd-emswe.eu";
BasePath = "https://brecal.bsmd-emswe.eu";
DefaultHeaders = new ConcurrentDictionary<string, string>();
ApiKey = new ConcurrentDictionary<string, string>();
ApiKeyPrefix = new ConcurrentDictionary<string, string>();
@ -4841,7 +4841,7 @@ namespace BreCalClient.misc.Client
{
{
new Dictionary<string, object> {
{"url", "https://brecaldevel.bsmd-emswe.eu"},
{"url", "https://brecal.bsmd-emswe.eu"},
{"description", "Development server hosted on vcup"},
}
}
@ -4860,7 +4860,7 @@ namespace BreCalClient.misc.Client
IDictionary<string, string> defaultHeaders,
IDictionary<string, string> apiKey,
IDictionary<string, string> apiKeyPrefix,
string basePath = "https://brecaldevel.bsmd-emswe.eu") : this()
string basePath = "https://brecal.bsmd-emswe.eu") : this()
{
if (string.IsNullOrWhiteSpace(basePath))
throw new ArgumentException("The provided basePath is invalid.", "basePath");

View File

@ -14,7 +14,7 @@ info:
name: Use at your own risk
url: 'https://www.bsmd.de/license'
servers:
- url: 'https://brecaldevel.bsmd-emswe.eu'
- url: 'https://brecal.bsmd-emswe.eu'
description: Development server hosted on vcup
tags:
- name: user

31
misc/delete_data.sql Normal file
View File

@ -0,0 +1,31 @@
CREATE DEFINER=`ds`@`localhost` PROCEDURE `delete_data`()
BEGIN
DECLARE shipcall_id_var int;
DECLARE done INT DEFAULT FALSE;
DECLARE shipcall_iter CURSOR FOR
SELECT shipcall.id FROM shipcall
LEFT JOIN times ON
times.shipcall_id = shipcall.id AND times.participant_type = 8
WHERE
-- ARRIVAL
(type = 1 AND GREATEST(shipcall.eta, COALESCE(times.eta_berth, 0)) <= CURRENT_DATE() - INTERVAL 1 MONTH) OR
-- DEPARTURE / SHIFTING
(type != 1 AND GREATEST(shipcall.etd, COALESCE(times.etd_berth, 0)) <= CURRENT_DATE() - INTERVAL 1 MONTH);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN shipcall_iter;
delete_loop: LOOP
FETCH shipcall_iter INTO shipcall_id_var;
IF done THEN
LEAVE delete_loop;
END IF;
DELETE FROM shipcall_participant_map WHERE shipcall_id = shipcall_id_var;
DELETE FROM shipcall_tug_map WHERE shipcall_id = shipcall_id_var;
DELETE FROM times WHERE shipcall_id = shipcall_id_var;
DELETE FROM shipcall WHERE id = shipcall_id_var;
END LOOP;
CLOSE shipcall_iter;
END

View File

@ -2,7 +2,7 @@
-- evaluation_time: Time when the "traffic light" was last changed
-- evaluation_notifications_sent: Flag to indicate if notifications were sent for the current evaluation
ALTER TABLE `bremen_calling_devel`.`shipcall`
ALTER TABLE `bremen_calling_test`.`shipcall`
ADD COLUMN `evaluation_time` DATETIME NULL DEFAULT NULL AFTER `evaluation_message`,
ADD COLUMN `evaluation_notifications_sent` BIT NULL AFTER `evaluation_time`,
ADD COLUMN `time_ref_point` INT NULL DEFAULT 0 COMMENT 'Index of a location which is the reference point for all time value entries, e.g. berth or Geeste' AFTER `modified`;
@ -12,10 +12,10 @@ ADD COLUMN `time_ref_point` INT NULL DEFAULT 0 COMMENT 'Index of a location whic
-- removed reference to participant and times and dropped unnecessary columns
-- added reference to shipcall
ALTER TABLE `bremen_calling_devel`.`notification`
ALTER TABLE `bremen_calling_test`.`notification`
DROP FOREIGN KEY `FK_NOT_TIMES`,
DROP FOREIGN KEY `FK_NOT_PART`;
ALTER TABLE `bremen_calling_devel`.`notification`
ALTER TABLE `bremen_calling_test`.`notification`
DROP COLUMN `deleted`,
DROP COLUMN `acknowledged`,
DROP COLUMN `participant_id`,
@ -25,10 +25,10 @@ ADD INDEX `FK_NOTIFICATION_SHIPCALL_idx` (`shipcall_id` ASC) VISIBLE,
DROP INDEX `FK_NOT_PART` ,
DROP INDEX `FK_NOT_TIMES` ;
;
ALTER TABLE `bremen_calling_devel`.`notification`
ALTER TABLE `bremen_calling_test`.`notification`
ADD CONSTRAINT `FK_NOTIFICATION_SHIPCALL`
FOREIGN KEY (`shipcall_id`)
REFERENCES `bremen_calling_devel`.`shipcall` (`id`)
REFERENCES `bremen_calling_test`.`shipcall` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION;
@ -36,22 +36,22 @@ ADD CONSTRAINT `FK_NOTIFICATION_SHIPCALL`
-- added notification flags
-- participant reference is now mandatory
ALTER TABLE `bremen_calling_devel`.`user`
ALTER TABLE `bremen_calling_test`.`user`
DROP FOREIGN KEY `FK_USER_PART`;
ALTER TABLE `bremen_calling_devel`.`user`
ALTER TABLE `bremen_calling_test`.`user`
ADD COLUMN `notify_email` BIT NULL DEFAULT NULL AFTER `api_key`,
ADD COLUMN `notify_whatsapp` BIT NULL DEFAULT NULL AFTER `notify_email`,
ADD COLUMN `notify_signal` BIT NULL DEFAULT NULL AFTER `notify_whatsapp`,
ADD COLUMN `notify_popup` BIT NULL DEFAULT NULL AFTER `notify_signal`,
CHANGE COLUMN `participant_id` `participant_id` INT UNSIGNED NOT NULL ;
ALTER TABLE `bremen_calling_devel`.`user`
ALTER TABLE `bremen_calling_test`.`user`
ADD CONSTRAINT `FK_USER_PART`
FOREIGN KEY (`participant_id`)
REFERENCES `bremen_calling_devel`.`participant` (`id`);
REFERENCES `bremen_calling_test`.`participant` (`id`);
-- History table for change tracking
CREATE TABLE `bremen_calling_devel`.`history` (
CREATE TABLE `bremen_calling_test`.`history` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`participant_id` INT UNSIGNED NOT NULL,
`user_id` INT UNSIGNED NOT NULL,
@ -65,31 +65,31 @@ COMMENT = 'This table stores a history of changes made to shipcalls so that ever
-- and foreign keys
ALTER TABLE `bremen_calling_devel`.`history`
ALTER TABLE `bremen_calling_test`.`history`
ADD INDEX `FK_HISTORY_PARTICIPANT_idx` (`participant_id` ASC) VISIBLE,
ADD INDEX `FK_HISTORY_SHIPCALL_idx` (`shipcall_id` ASC) VISIBLE;
;
ALTER TABLE `bremen_calling_devel`.`history`
ALTER TABLE `bremen_calling_test`.`history`
ADD CONSTRAINT `FK_HISTORY_PARTICIPANT`
FOREIGN KEY (`participant_id`)
REFERENCES `bremen_calling_devel`.`participant` (`id`)
REFERENCES `bremen_calling_test`.`participant` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
ADD CONSTRAINT `FK_HISTORY_SHIPCALL`
FOREIGN KEY (`shipcall_id`)
REFERENCES `bremen_calling_devel`.`shipcall` (`id`)
REFERENCES `bremen_calling_test`.`shipcall` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
ADD CONSTRAINT `FK_HISTORY_USER`
FOREIGN KEY (`user_id`)
REFERENCES `bremen_calling_devel`.`user` (`id`)
REFERENCES `bremen_calling_test`.`user` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION;
-- add additional fields to times
ALTER TABLE `bremen_calling_devel`.`times`
ALTER TABLE `bremen_calling_test`.`times`
ADD COLUMN `ata` DATETIME NULL DEFAULT NULL COMMENT 'Relevant only for mooring, this field can be used to record actual ATA' AFTER `participant_type`,
ADD COLUMN `atd` DATETIME NULL DEFAULT NULL COMMENT 'Relevant only for mooring, this field can be used to record actual ATD' AFTER `ata`,
ADD COLUMN `eta_interval_end` DATETIME NULL DEFAULT NULL COMMENT 'If this value is set the times are given as interval instead of a single point in time. The start time value depends on the participant type.' AFTER `atd`;
ADD COLUMN `eta_interval_end` DATETIME NULL DEFAULT NULL COMMENT 'If this value is set the times are given as interval instead of a single point in time. The start time value depends on the participant type.' AFTER `atd`,
ADD COLUMN `etd_interval_end` DATETIME NULL DEFAULT NULL COMMENT 'If this value is set the times are given as interval instead of a single point in time. The start time value depends on the participant type.' AFTER `eta_interval_end`;

View File

@ -1 +1 @@
1.2.0.0
1.2.1.2

View File

@ -25,7 +25,7 @@
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="*" />
<RowDefinition Height="28" />
</Grid.RowDefinitions>

View File

@ -11,16 +11,16 @@
<applicationSettings>
<BreCalClient.Properties.Settings>
<setting name="BG_COLOR" serializeAs="String">
<value>#1D751F</value>
<value>#203864</value>
</setting>
<setting name="APP_TITLE" serializeAs="String">
<value>!!Bremen calling Testversion!!</value>
<value>Bremen calling</value>
</setting>
<setting name="LOGO_IMAGE_URL" serializeAs="String">
<value>https://www.textbausteine.net/</value>
</setting>
<setting name="API_URL" serializeAs="String">
<value>https://brecaldevel.bsmd-emswe.eu</value>
<value>https://brecal.bsmd-emswe.eu</value>
</setting>
</BreCalClient.Properties.Settings>
</applicationSettings>

View File

@ -2,16 +2,19 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BreCalClient"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
StartupUri="MainWindow.xaml" Exit="Application_Exit" Startup="Application_Startup" >
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources\StringResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
<sys:Double x:Key="{x:Static SystemParameters.VerticalScrollBarWidthKey}">10</sys:Double>
<sys:Double x:Key="{x:Static SystemParameters.HorizontalScrollBarHeightKey}">10</sys:Double>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@ -8,12 +8,12 @@
<SignAssembly>True</SignAssembly>
<StartupObject>BreCalClient.App</StartupObject>
<AssemblyOriginatorKeyFile>..\..\misc\brecal.snk</AssemblyOriginatorKeyFile>
<AssemblyVersion>1.2.0.0</AssemblyVersion>
<FileVersion>1.2.0.0</FileVersion>
<AssemblyVersion>1.2.1.2</AssemblyVersion>
<FileVersion>1.2.1.2</FileVersion>
<Title>Bremen calling client</Title>
<Description>A Windows WPF client for the Bremen calling API.</Description>
<ApplicationIcon>containership.ico</ApplicationIcon>
<AssemblyName>BreCalDevelClient</AssemblyName>
<AssemblyName>BreCalClient</AssemblyName>
</PropertyGroup>
<ItemGroup>
@ -37,6 +37,7 @@
<None Remove="Resources\lock.png" />
<None Remove="Resources\lock_open.png" />
<None Remove="Resources\logo_bremen_calling.png" />
<None Remove="Resources\nav_refresh_green.png" />
<None Remove="Resources\ship2.png" />
<None Remove="Resources\sign_warning.png" />
<None Remove="Resources\trafficlight_green.png" />
@ -92,6 +93,7 @@
<Resource Include="Resources\lock.png" />
<Resource Include="Resources\lock_open.png" />
<Resource Include="Resources\logo_bremen_calling.png" />
<Resource Include="Resources\nav_refresh_green.png" />
<Resource Include="Resources\ship2.png" />
<Resource Include="Resources\sign_warning.png" />
<Resource Include="Resources\StringResources.de.xaml">

View File

@ -1,6 +1,6 @@
// Copyright (c) 2023 schick Informatik
// Description: Static lists used everywhere
//
//
using BreCalClient.misc.Model;
using System.Collections.Concurrent;
@ -30,9 +30,13 @@ namespace BreCalClient
private readonly static ConcurrentDictionary<int, Berth> _berthLookupDict = new();
private readonly static Dictionary<int, Participant> _participantLookupDict = new();
/// <summary>
/// List of TimeRef points
/// </summary>
// TODO: To make this portable the list of texts should come from a configuration file
private readonly static List<string> _timeRefs = new List<string>
{
"Liegeplatz",
"ETB",
"Geeste",
"TN-Weser"
};
@ -113,7 +117,7 @@ namespace BreCalClient
aList.Clear();
mList.Clear();
pList.Clear();
tList.Clear();
tList.Clear();
terList.Clear();
foreach (Participant p in participants)
@ -147,7 +151,7 @@ namespace BreCalClient
if (!ship.Deleted)
_ships.Add(sm);
_allShips.Add(sm);
}
}
}
#endregion

View File

@ -141,8 +141,7 @@ namespace BreCalClient
#region protected
protected void addItem(object sender, RoutedEventArgs e)
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-us");
{
if (!this.IsReadOnly)
{
this.CreateRequested?.Invoke();

View File

@ -25,7 +25,7 @@
<RowDefinition Height="28" />
</Grid.RowDefinitions>
<Label Content="Name" HorizontalAlignment="Right" />
<TextBox x:Name="textBoxName" Grid.Column="1" Margin="2" VerticalContentAlignment="Center" Text="{Binding Name, Mode=OneWay}" />
<TextBox x:Name="textBoxName" Grid.Column="1" Margin="2" VerticalContentAlignment="Center" Text="{Binding Name, Mode=OneWay}" TextChanged="textBoxName_TextChanged" MaxLength="64"/>
<Label Content="{x:Static p:Resources.textTugCompany}" HorizontalAlignment="Right" Grid.Row="1" />
<Grid Grid.Row="1" Grid.Column="1">
<Grid.ColumnDefinitions>
@ -38,9 +38,9 @@
</Button>
</Grid>
<Label Content="IMO" HorizontalAlignment="Right" Grid.Row="2" />
<xctk:IntegerUpDown Name="integerUpDownIMO" Grid.Column="1" Grid.Row="2" Value="{Binding Imo, Mode=OneWay}" Margin="2" Minimum="1000000" Maximum="9999999" ShowButtonSpinner="False"/>
<xctk:IntegerUpDown Name="integerUpDownIMO" Grid.Column="1" Grid.Row="2" Value="{Binding Imo, Mode=OneWay}" Margin="2" Minimum="1000000" Maximum="9999999" ShowButtonSpinner="False" ValueChanged="integerUpDownIMO_ValueChanged"/>
<Label Content="{x:Static p:Resources.textCallsign}" HorizontalAlignment="Right" Grid.Row="3" />
<TextBox x:Name="textBoxCallsign" Grid.Column="1" Grid.Row="3" Margin="2" VerticalContentAlignment="Center" Text="{Binding Callsign, Mode=OneWay}" />
<TextBox x:Name="textBoxCallsign" Grid.Column="1" Grid.Row="3" Margin="2" VerticalContentAlignment="Center" Text="{Binding Callsign, Mode=OneWay}" MaxLength="8"/>
<Label Content="{x:Static p:Resources.textLength}" HorizontalAlignment="Right" Grid.Row="4" />
<xctk:DoubleUpDown Name="doubleUpDownLength" Grid.Row="4" Grid.Column="1" Value="{Binding Length, Mode=OneWay}" Margin="2" Minimum="0" />
<Label Content="{x:Static p:Resources.textWidth}" HorizontalAlignment="Right" Grid.Row="5" />

View File

@ -28,7 +28,7 @@ namespace BreCalClient
private void buttonOK_Click(object sender, RoutedEventArgs e)
{
this.Ship.Name = this.textBoxName.Text.Trim();
this.Ship.Name = this.textBoxName.Text.ToUpper().Trim();
if (this.comboBoxParticipants.SelectedItem != null)
{
@ -40,7 +40,7 @@ namespace BreCalClient
this.Ship.IsTug = false;
}
this.Ship.Imo = this.integerUpDownIMO.Value;
this.Ship.Callsign = this.textBoxCallsign.Text.Trim();
this.Ship.Callsign = this.textBoxCallsign.Text.ToUpper().Trim();
this.Ship.Length = (float?) this.doubleUpDownLength.Value;
this.Ship.Width = (float?) this.doubleUpDownWidth.Value;
this.DialogResult = true;
@ -61,11 +61,27 @@ namespace BreCalClient
this.comboBoxParticipants.SelectedItem = p;
}
}
this.EnableOK();
}
private void buttonResetParticipant_Click(object sender, RoutedEventArgs e)
{
this.comboBoxParticipants.SelectedItem = null;
}
private void textBoxName_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
this.EnableOK();
}
private void integerUpDownIMO_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
this.EnableOK();
}
private void EnableOK()
{
this.buttonOK.IsEnabled = (this.textBoxName.Text.Length > 2) && (this.integerUpDownIMO.Value.HasValue);
}
}
}

View File

@ -4,13 +4,14 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BreCalClient"
xmlns:p = "clr-namespace:BreCalClient.Resources"
xmlns:db="clr-namespace:BreCalClient;assembly=BreCalClient"
xmlns:p = "clr-namespace:BreCalClient.Resources"
xmlns:api="clr-namespace:BreCalClient.misc.Model"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d" Left="{local:SettingBinding W1Left}" Top="{local:SettingBinding W1Top}"
Title="{x:Static p:Resources.textEditShipcall}" Height="270" Width="800" Loaded="Window_Loaded" ResizeMode="NoResize" Icon="Resources/containership.ico">
<Window.Resources>
<local:BoolToIndexConverter x:Key="boolToIndexConverter" />
<local:EnumToStringConverter x:Key="enumToStringConverter" />
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
@ -45,8 +46,8 @@
<Button x:Name="buttonEditShips" Grid.Column="1" Grid.Row="6" Margin="2" Content="{x:Static p:Resources.textEditShips}" Click="buttonEditShips_Click" Visibility="Hidden" />
<Label Content="{x:Static p:Resources.textType}" Grid.Column="2" Grid.Row="0" HorizontalContentAlignment="Right" />
<ComboBox x:Name="comboBoxCategories" Grid.Column="3" Margin="2" Grid.Row="0" SelectionChanged="comboBoxCategories_SelectionChanged"/>
<ComboBox ItemsSource="{local:Enumerate {x:Type api:ShipcallType}}" Grid.Column="3" Margin="2" Grid.Row="0" SelectionChanged="comboBoxCategories_SelectionChanged" x:Name="comboBoxCategories" />
<Label Content="{x:Static p:Resources.textBerth}" Grid.Column="2" Grid.Row="1" HorizontalContentAlignment="Right"/>
<Grid Grid.Row="1" Grid.Column="3">

View File

@ -5,6 +5,7 @@
using BreCalClient.misc.Api;
using BreCalClient.misc.Model;
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using static BreCalClient.Extensions;
@ -48,8 +49,16 @@ namespace BreCalClient
{
this.comboBoxAgency.ItemsSource = BreCalLists.Participants_Agent;
this.comboBoxShip.ItemsSource = BreCalLists.AllShips;
this.comboBoxCategories.ItemsSource = Enum.GetValues(typeof(ShipcallType));
this.comboBoxShip.ItemsSource = BreCalLists.Ships;
Array types = Enum.GetValues(typeof(ShipcallType));
List<ShipcallType> shipcallTypes = new();
bool first = true;
foreach(ShipcallType shipcallType in types)
{
if (!first) shipcallTypes.Add(shipcallType);
else first = false;
}
this.comboBoxArrivalBerth.ItemsSource = BreCalLists.Berths;
this.comboBoxDepartureBerth.ItemsSource = BreCalLists.Berths;
@ -102,7 +111,7 @@ namespace BreCalClient
private void comboBoxCategories_SelectionChanged(object? sender, SelectionChangedEventArgs? e)
{
ShipcallType? type = this.comboBoxCategories.SelectedItem as ShipcallType?;
ShipcallType? type = GetShipcallTypeFromCombobox();
if (type != null)
{
switch (type)
@ -114,6 +123,7 @@ namespace BreCalClient
this.comboBoxDepartureBerth.SelectedIndex = -1;
this.comboBoxDepartureBerth.IsEnabled = false;
this.comboBoxArrivalBerth.IsEnabled = true;
this.comboBoxTimeRef.IsEnabled = true;
break;
case ShipcallType.Departure:
this.datePickerETA.IsEnabled = false;
@ -122,12 +132,16 @@ namespace BreCalClient
this.comboBoxArrivalBerth.SelectedIndex = -1;
this.comboBoxArrivalBerth.IsEnabled = false;
this.comboBoxDepartureBerth.IsEnabled = true;
this.comboBoxTimeRef.IsEnabled = false;
this.comboBoxTimeRef.SelectedIndex = 0;
break;
case ShipcallType.Shifting:
this.datePickerETA.IsEnabled = true;
this.datePickerETD.IsEnabled = true;
this.comboBoxArrivalBerth.IsEnabled = true;
this.comboBoxDepartureBerth.IsEnabled = true;
this.comboBoxTimeRef.IsEnabled = false;
this.comboBoxTimeRef.SelectedIndex = 0;
break;
}
}
@ -148,6 +162,12 @@ namespace BreCalClient
#region private methods
ShipcallType? GetShipcallTypeFromCombobox()
{
EnumToStringConverter enumToStringConverter = new();
return (ShipcallType?)enumToStringConverter.ConvertBack(this.comboBoxCategories.SelectedItem, typeof(ShipcallType), new object(), System.Globalization.CultureInfo.CurrentCulture);
}
void CheckForCompletion()
{
bool isEnabled = true;
@ -161,7 +181,7 @@ namespace BreCalClient
}
else
{
ShipcallType callType = (ShipcallType)comboBoxCategories.SelectedItem;
ShipcallType callType = GetShipcallTypeFromCombobox() ?? ShipcallType.Undefined;
switch (callType)
{
case ShipcallType.Departure:
@ -189,7 +209,7 @@ namespace BreCalClient
{
if (this.ShipcallModel.Shipcall != null)
{
this.ShipcallModel.Shipcall.Type = (ShipcallType) this.comboBoxCategories.SelectedItem;
this.ShipcallModel.Shipcall.Type = GetShipcallTypeFromCombobox() ?? ShipcallType.Undefined;
this.ShipcallModel.Shipcall.Eta = this.datePickerETA.Value;
this.ShipcallModel.Shipcall.Etd = this.datePickerETD.Value;
@ -270,12 +290,18 @@ namespace BreCalClient
if (this.ShipcallModel.Shipcall != null)
{
this.comboBoxTimeRef.SelectedIndex = this.ShipcallModel.Shipcall.TimeRefPoint ?? 0;
this.comboBoxCategories.SelectedItem = this.ShipcallModel.Shipcall.Type;
this.comboBoxCategories.SelectedItem = new EnumToStringConverter().Convert(this.ShipcallModel.Shipcall.Type, typeof(ShipcallType), new object(), System.Globalization.CultureInfo.CurrentCulture);
if (this.ShipcallModel.Shipcall.Eta != DateTime.MinValue)
this.datePickerETA.Value = this.ShipcallModel.Shipcall.Eta;
// this.textBoxVoyage.Text = this.ShipcallModel.Shipcall.Voyage;
this.datePickerETD.Value = this.ShipcallModel.Shipcall.Etd;
this.comboBoxShip.SelectedValue = this.ShipcallModel.Shipcall.ShipId;
if (BreCalLists.Ships.Find(x => x.Ship.Id == this.ShipcallModel.Shipcall.ShipId) != null)
{
this.comboBoxShip.SelectedValue = this.ShipcallModel.Shipcall.ShipId;
} else
{
this.comboBoxShip.IsEnabled = false;
}
this.checkBoxCancelled.IsChecked = this.ShipcallModel.Shipcall.Canceled ?? false;
if (this.ShipcallModel.Shipcall.Type != ShipcallType.Shifting) // incoming, outgoing
@ -370,7 +396,7 @@ namespace BreCalClient
// reload combobox
this.comboBoxShip.ItemsSource = null;
this.comboBoxShip.ItemsSource = BreCalLists.AllShips;
this.comboBoxShip.ItemsSource = BreCalLists.Ships;
}
#endregion

View File

@ -8,13 +8,13 @@
xmlns:db="clr-namespace:BreCalClient;assembly=BreCalClient"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d" Left="{local:SettingBinding W1Left}" Top="{local:SettingBinding W1Top}"
Title="{x:Static p:Resources.textEditShipcall}" Height="403" Width="900" Loaded="Window_Loaded" ResizeMode="NoResize" Icon="Resources/containership.ico">
Title="{x:Static p:Resources.textEditShipcall}" Height="403" Width="900" Loaded="Window_Loaded" ResizeMode="CanResizeWithGrip" Icon="Resources/containership.ico">
<Window.Resources>
<local:BoolToIndexConverter x:Key="boolToIndexConverter" />
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.15*"/>
<ColumnDefinition Width="0.18*"/>
<ColumnDefinition Width=".4*" />
<ColumnDefinition Width="0.15*"/>
<ColumnDefinition Width=".3*" />
@ -31,7 +31,7 @@
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="*" />
<RowDefinition Height="28" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Grid.Column="0">
@ -67,7 +67,7 @@
</ComboBox.ContextMenu>
</ComboBox>
<Label Content="{x:Static p:Resources.textBerthRemarks}" Grid.Column="0" Grid.Row="4" HorizontalContentAlignment="Right" />
<TextBox x:Name="textBoxBerthRemarks" Grid.Column="1" Grid.Row="4" Margin="2" Grid.RowSpan="2" VerticalContentAlignment="Top" AcceptsReturn="True" MaxLength="512"/>
<TextBox x:Name="textBoxBerthRemarks" Grid.Column="1" Grid.Row="4" Margin="2" Grid.RowSpan="2" VerticalContentAlignment="Top" AcceptsReturn="True" MaxLength="512" TextWrapping="Wrap" SpellCheck.IsEnabled="True" AcceptsTab="False" ScrollViewer.VerticalScrollBarVisibility="Auto" />
<Label Content="{x:Static p:Resources.textDraft}" Grid.Column="0" Grid.Row="6" HorizontalContentAlignment="Right" FontWeight="Bold" />
<xctk:DoubleUpDown x:Name="doubleUpDownDraft" Grid.Column="1" Grid.Row="6" Margin="2" FormatString="N2" Minimum="0" Maximum="50" MaxLength="5" ValueChanged="doubleUpDownDraft_ValueChanged"/>
<Label Content="{x:Static p:Resources.textTidalWindow}" FontWeight="DemiBold" Grid.Column="0" Grid.Row="7" HorizontalContentAlignment="Right"/>
@ -127,7 +127,7 @@
<CheckBox x:Name="checkBoxReplenishingTerminal" Grid.Column="2" Grid.Row="8" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,4,0" />
<CheckBox x:Name="checkBoxReplenishingLock" Grid.Column="2" Grid.Row="9" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,4,0" />
<Label Content="{x:Static p:Resources.textRemarks}" Grid.Row="10" Grid.Column="2" HorizontalAlignment="Right"/>
<TextBox x:Name="textBoxRemarks" Grid.Column="3" Grid.Row="10" Margin="2" Grid.RowSpan="2" VerticalContentAlignment="Top" AcceptsReturn="True" MaxLength="512"/>
<TextBox x:Name="textBoxRemarks" Grid.Column="3" Grid.Row="10" Margin="2" Grid.RowSpan="2" VerticalContentAlignment="Top" AcceptsReturn="True" MaxLength="512" TextWrapping="Wrap" SpellCheck.IsEnabled="True" AcceptsTab="False" ScrollViewer.VerticalScrollBarVisibility="Auto"/>
<StackPanel Grid.Row="14" Grid.Column="3" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Width= "80" Margin="2" Content="{x:Static p:Resources.textOK}" x:Name="buttonOK" Click="buttonOK_Click" IsEnabled="False" />

View File

@ -255,7 +255,7 @@ namespace BreCalClient
this.datePickerETA_End.IsEnabled = _editing;
this.comboBoxArrivalBerth.IsEnabled = _editing;
this.comboBoxPierside.IsEnabled = _editing;
this.textBoxBerthRemarks.IsEnabled = _editing;
this.textBoxBerthRemarks.IsReadOnly = !_editing;
this.doubleUpDownDraft.IsEnabled = _editing;
this.datePickerTidalWindowFrom.IsEnabled = _editing;
this.datePickerTidalWindowTo.IsEnabled = _editing;
@ -273,7 +273,7 @@ namespace BreCalClient
this.checkBoxBunkering.IsEnabled = _editing;
this.checkBoxReplenishingTerminal.IsEnabled = _editing;
this.checkBoxReplenishingLock.IsEnabled = _editing;
this.textBoxRemarks.IsEnabled = _editing;
this.textBoxRemarks.IsReadOnly = !_editing;
CheckOKButton();
}

View File

@ -8,10 +8,10 @@
xmlns:db="clr-namespace:BreCalClient;assembly=BreCalClient"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d" Left="{local:SettingBinding W1Left}" Top="{local:SettingBinding W1Top}"
Title="{x:Static p:Resources.textEditShipcall}" Height="375" Width="900" Loaded="Window_Loaded" ResizeMode="NoResize" Icon="Resources/containership.ico">
Title="{x:Static p:Resources.textEditShipcall}" Height="375" Width="900" Loaded="Window_Loaded" ResizeMode="CanResizeWithGrip" Icon="Resources/containership.ico">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.15*"/>
<ColumnDefinition Width="0.18*"/>
<ColumnDefinition Width=".4*" />
<ColumnDefinition Width="0.15*"/>
<ColumnDefinition Width=".3*" />
@ -28,6 +28,7 @@
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="*" />
<RowDefinition Height="28" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Grid.Column="0">
@ -61,7 +62,7 @@
</ComboBox.ContextMenu>
</ComboBox>
<Label Content="{x:Static p:Resources.textBerthRemarks}" Grid.Column="0" Grid.Row="4" HorizontalContentAlignment="Right" />
<TextBox x:Name="textBoxBerthRemarks" Grid.Column="1" Grid.Row="4" Margin="2" Grid.RowSpan="2" VerticalContentAlignment="Top" AcceptsReturn="True" MaxLength="512"/>
<TextBox x:Name="textBoxBerthRemarks" Grid.Column="1" Grid.Row="4" Margin="2" Grid.RowSpan="2" VerticalContentAlignment="Top" TextWrapping="Wrap" AcceptsReturn="True" SpellCheck.IsEnabled="True" AcceptsTab="False" MaxLength="512" ScrollViewer.VerticalScrollBarVisibility="Auto"/>
<Label Content="{x:Static p:Resources.textDraft}" Grid.Column="0" Grid.Row="6" HorizontalContentAlignment="Right" FontWeight="Bold"/>
<xctk:DoubleUpDown x:Name="doubleUpDownDraft" Grid.Column="1" Grid.Row="6" Margin="2" FormatString="N2" Minimum="0" Maximum="50" MaxLength="5" ValueChanged="doubleUpDownDraft_ValueChanged"/>
<Label Content="{x:Static p:Resources.textTidalWindow}" FontWeight="DemiBold" Grid.Column="0" Grid.Row="7" HorizontalContentAlignment="Right"/>
@ -69,7 +70,7 @@
<Label Content="{x:Static p:Resources.textTo}" Grid.Column="0" Grid.Row="9" HorizontalContentAlignment="Right"/>
<xctk:DateTimePicker Name="datePickerTidalWindowFrom" Grid.Column="1" Grid.Row="8" Margin="2" Format="Custom" FormatString="dd.MM. yyyy HH:mm"/>
<xctk:DateTimePicker Name="datePickerTidalWindowTo" Grid.Column="1" Grid.Row="9" Margin="2" Format="Custom" FormatString="dd.MM. yyyy HH:mm"/>
<Label Content="{x:Static p:Resources.textCancelled}" Grid.Column="0" Grid.Row="10" HorizontalContentAlignment="Right" />
<Label Content="{x:Static p:Resources.textCancelled}" Grid.Column="0" Grid.Row="10" HorizontalContentAlignment="Right" VerticalAlignment="Center" />
<CheckBox x:Name="checkBoxCanceled" Grid.Column="1" Grid.Row="10" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="0,0,4,0" />
<Label Content="{x:Static p:Resources.textTugRequired}" Grid.Column="2" Grid.Row="1" HorizontalContentAlignment="Right"/>
@ -115,9 +116,9 @@
<Label Content="{x:Static p:Resources.textRainSensitiveCargo}" Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="7" HorizontalAlignment="Right" />
<CheckBox x:Name="checkBoxRainsensitiveCargo" Grid.Column="3" Grid.Row="7" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="0,0,4,0" />
<Label Content="{x:Static p:Resources.textRemarks}" Grid.Column="2" Grid.Row="8" HorizontalContentAlignment="Right" />
<TextBox x:Name="textBoxRemarks" Grid.Column="3" Grid.Row="8" Margin="2" Grid.RowSpan="3" VerticalContentAlignment="Top" AcceptsReturn="True" MaxLength="512"/>
<TextBox x:Name="textBoxRemarks" Grid.Column="3" Grid.Row="8" Margin="2" Grid.RowSpan="4" VerticalContentAlignment="Top" TextWrapping="Wrap" AcceptsReturn="True" SpellCheck.IsEnabled="True" AcceptsTab="False" MaxLength="512" ScrollViewer.VerticalScrollBarVisibility="Auto"/>
<StackPanel Grid.Row="11" Grid.Column="3" Orientation="Horizontal" HorizontalAlignment="Right">
<StackPanel Grid.Row="12" Grid.Column="3" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Width= "80" Margin="2" Content="{x:Static p:Resources.textOK}" x:Name="buttonOK" Click="buttonOK_Click" IsEnabled="False" />
<Button Width="80" Margin="2" Content="{x:Static p:Resources.textCancel}" x:Name="buttonCancel" Click="buttonCancel_Click"/>
</StackPanel>

View File

@ -247,7 +247,7 @@ namespace BreCalClient
this.datePickerETD_End.IsEnabled = _editing;
this.comboBoxDepartureBerth.IsEnabled = _editing;
this.comboBoxPierside.IsEnabled = _editing;
this.textBoxBerthRemarks.IsEnabled = _editing;
this.textBoxBerthRemarks.IsReadOnly = !_editing;
this.doubleUpDownDraft.IsEnabled = _editing;
this.datePickerTidalWindowFrom.IsEnabled = _editing;
this.datePickerTidalWindowTo.IsEnabled = _editing;
@ -261,7 +261,7 @@ namespace BreCalClient
this.checkBoxMooredLock.IsEnabled = _editing;
this.comboBoxTerminal.IsEnabled = _editing;
this.checkBoxRainsensitiveCargo.IsEnabled = _editing;
this.textBoxRemarks.IsEnabled = _editing;
this.textBoxRemarks.IsReadOnly = !_editing;
CheckOKButton();
}

View File

@ -8,10 +8,10 @@
xmlns:db="clr-namespace:BreCalClient;assembly=BreCalClient"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d" Left="{local:SettingBinding W1Left}" Top="{local:SettingBinding W1Top}"
Title="{x:Static p:Resources.textEditShipcall}" Height="490" Width="900" Loaded="Window_Loaded" ResizeMode="NoResize" Icon="Resources/containership.ico">
Title="{x:Static p:Resources.textEditShipcall}" Height="490" Width="900" Loaded="Window_Loaded" ResizeMode="CanResizeWithGrip" Icon="Resources/containership.ico">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.15*"/>
<ColumnDefinition Width="0.18*"/>
<ColumnDefinition Width=".4*" />
<ColumnDefinition Width="0.15*"/>
<ColumnDefinition Width=".3*" />
@ -30,7 +30,7 @@
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="*" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
@ -106,7 +106,7 @@
</ComboBox.ContextMenu>
</ComboBox>
<Label Content="{x:Static p:Resources.textBerthRemarks}" Grid.Column="0" Grid.Row="12" HorizontalContentAlignment="Right" />
<TextBox x:Name="textBoxBerthRemarksArrival" Grid.Column="1" Grid.Row="12" Margin="2,1,2,3" Grid.RowSpan="2" VerticalContentAlignment="Top" AcceptsReturn="True" MaxLength="512"/>
<TextBox x:Name="textBoxBerthRemarksArrival" Grid.Column="1" Grid.Row="12" Margin="2,1,2,3" Grid.RowSpan="2" VerticalContentAlignment="Top" TextWrapping="Wrap" AcceptsReturn="True" SpellCheck.IsEnabled="True" AcceptsTab="False" MaxLength="512" ScrollViewer.VerticalScrollBarVisibility="Auto"/>
<Label Content="{x:Static p:Resources.textCancelled}" Grid.Column="0" Grid.Row="14" HorizontalContentAlignment="Right" />
<CheckBox x:Name="checkBoxCanceled" Grid.Column="1" Grid.Row="14" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="0,0,4,0" />
@ -147,7 +147,7 @@
<Label Content="{x:Static p:Resources.textRainSensitiveCargo}" Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="6" HorizontalAlignment="Right" />
<CheckBox x:Name="checkBoxRainsensitiveCargo" Grid.Column="3" Grid.Row="6" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="0,0,4,0" />
<Label Content="{x:Static p:Resources.textRemarks}" Grid.Column="2" Grid.Row="7" HorizontalContentAlignment="Right" />
<TextBox x:Name="textBoxRemarks" Grid.Column="3" Grid.Row="7" Margin="2,1,2,3" Grid.RowSpan="7" VerticalContentAlignment="Top" AcceptsReturn="True" MaxLength="512"/>
<TextBox x:Name="textBoxRemarks" Grid.Column="3" Grid.Row="7" Margin="2,1,2,3" Grid.RowSpan="7" VerticalContentAlignment="Top" TextWrapping="Wrap" AcceptsReturn="True" SpellCheck.IsEnabled="True" AcceptsTab="False" MaxLength="512" ScrollViewer.VerticalScrollBarVisibility="Auto"/>
<StackPanel Grid.Row="15" Grid.Column="3" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Width= "80" Margin="2" Content="{x:Static p:Resources.textOK}" x:Name="buttonOK" Click="buttonOK_Click" IsEnabled="False"/>

View File

@ -270,7 +270,7 @@ namespace BreCalClient
this.datePickerETA.IsEnabled = _editing;
this.comboBoxDepartureBerth.IsEnabled = _editing;
this.comboBoxPiersideArrival.IsEnabled = _editing;
this.textBoxBerthRemarksArrival.IsEnabled = _editing;
this.textBoxBerthRemarksArrival.IsReadOnly = !_editing;
this.checkBoxCanceled.IsEnabled = _editing;
@ -281,7 +281,7 @@ namespace BreCalClient
this.checkBoxMooredLock.IsEnabled = _editing;
this.comboBoxTerminal.IsEnabled = _editing;
this.checkBoxRainsensitiveCargo.IsEnabled = _editing;
this.textBoxRemarks.IsEnabled = _editing;
this.textBoxRemarks.IsReadOnly = !_editing;
CheckOKButton();
}

View File

@ -8,7 +8,7 @@
xmlns:db="clr-namespace:BreCalClient;assembly=BreCalClient"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d" Left="{local:SettingBinding W1Left}" Top="{local:SettingBinding W1Top}"
Title="{x:Static p:Resources.textEditTimes}" Height="331" Width="500" Loaded="Window_Loaded" ResizeMode="NoResize" Icon="Resources/containership.ico">
Title="{x:Static p:Resources.textEditTimes}" Height="331" Width="500" Loaded="Window_Loaded" ResizeMode="CanResizeWithGrip" Icon="Resources/containership.ico">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".20*" />
@ -23,7 +23,7 @@
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="56" />
<RowDefinition Height="*" />
<RowDefinition Height="28" />
</Grid.RowDefinitions>
<Button x:Name="buttonFixedOrder" Grid.Row="0" Grid.Column="1" Margin="2" Click="buttonFixedOrder_Click" Width="28" HorizontalAlignment="Left">
@ -45,7 +45,7 @@
<ColumnDefinition Width=".5*" />
</Grid.ColumnDefinitions>
<xctk:DateTimePicker IsEnabled="False" Grid.Row="0" Grid.Column="0" Margin="2" Name="datePickerETABerth" Format="Custom" FormatString="dd.MM. yyyy HH:mm">
<xctk:DateTimePicker IsEnabled="False" Grid.Row="0" Grid.Column="0" Margin="2" Name="datePickerETABerth" Format="Custom" FormatString="dd.MM. yyyy HH:mm" ValueChanged="datePickerETABerth_ValueChanged">
<xctk:DateTimePicker.ContextMenu>
<ContextMenu>
<MenuItem Header="{x:Static p:Resources.textClearValue}" Name="contextMenuItemClearETA" Click="contextMenuItemClearETA_Click" >
@ -77,7 +77,7 @@
<ColumnDefinition Width=".5*" />
</Grid.ColumnDefinitions>
<xctk:DateTimePicker IsEnabled="False" Grid.Row="0" Grid.Column="0" Margin="2" Name="datePickerETDBerth" Format="Custom" FormatString="dd.MM. yyyy HH:mm">
<xctk:DateTimePicker IsEnabled="False" Grid.Row="0" Grid.Column="0" Margin="2" Name="datePickerETDBerth" Format="Custom" FormatString="dd.MM. yyyy HH:mm" ValueChanged="datePickerETDBerth_ValueChanged">
<xctk:DateTimePicker.ContextMenu>
<ContextMenu>
<MenuItem Header="{x:Static p:Resources.textClearValue}" Name="contextMenuItemClearETD" Click="contextMenuItemClearETD_Click" >
@ -152,7 +152,7 @@
</xctk:DateTimePicker>
<!--CheckBox IsEnabled="False" Grid.Row="4" Grid.Column="2" Margin="4,0,0,0" Name="checkBoxZoneEntryFixed" VerticalAlignment="Center" /-->
<TextBox Grid.Row="7" Grid.Column="1" Margin="2" Name="textBoxRemarks" TextWrapping="Wrap" AcceptsReturn="True" SpellCheck.IsEnabled="True" AcceptsTab="False" IsEnabled="False" MaxLength="512"/>
<TextBox Grid.Row="7" Grid.Column="1" Margin="2" Name="textBoxRemarks" TextWrapping="Wrap" AcceptsReturn="True" SpellCheck.IsEnabled="True" AcceptsTab="False" IsReadOnly="True" MaxLength="512" ScrollViewer.VerticalScrollBarVisibility="Auto"/>
<StackPanel Grid.Row="8" Grid.Column="1" Grid.ColumnSpan="2" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Width= "80" Margin="2" Content="{x:Static p:Resources.textOK}" x:Name="buttonOK" Click="buttonOK_Click" IsEnabled="False" />
<Button Width="80" Margin="2" Content="{x:Static p:Resources.textCancel}" x:Name="buttonCancel" Click="buttonCancel_Click"/>

View File

@ -62,6 +62,16 @@ namespace BreCalClient
SetLockButton(newValue);
}
private void datePickerETABerth_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
CheckOKButton();
}
private void datePickerETDBerth_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
CheckOKButton();
}
#endregion
#region private methods
@ -114,6 +124,8 @@ namespace BreCalClient
{
Extensions.ParticipantType pType = (Extensions.ParticipantType) this.Times.ParticipantType;
// setting visibility
if (pType != Extensions.ParticipantType.MOORING)
{
this.labelATA.Visibility = Visibility.Hidden;
@ -135,45 +147,37 @@ namespace BreCalClient
}
}
// setting en/dis-abled
if (this.Times.ParticipantId != App.Participant.Id)
{
this.buttonFixedOrder.IsEnabled = false;
return; // if this is not "my" entry, there is no editing!
}
if(pType == Extensions.ParticipantType.MOORING)
{
this.datePickerATA.IsEnabled = true;
this.datePickerATD.IsEnabled = true;
}
this.datePickerETABerth.IsEnabled = (ShipcallModel.Shipcall?.Type == ShipcallType.Arrival);
this.datePickerETABerth_End.IsEnabled = (ShipcallModel.Shipcall?.Type == ShipcallType.Arrival);
this.datePickerETDBerth.IsEnabled = (ShipcallModel.Shipcall?.Type == ShipcallType.Departure || ShipcallModel.Shipcall?.Type == ShipcallType.Shifting);
this.datePickerETDBerth_End.IsEnabled = (ShipcallModel.Shipcall?.Type == ShipcallType.Departure || ShipcallModel.Shipcall?.Type == ShipcallType.Shifting);
this.textBoxRemarks.IsReadOnly = false;
switch (pType)
{
case Extensions.ParticipantType.MOORING:
case Extensions.ParticipantType.PORT_ADMINISTRATION:
case Extensions.ParticipantType.TUG:
this.datePickerETABerth.IsEnabled = (ShipcallModel.Shipcall?.Type == ShipcallType.Arrival);
this.datePickerETABerth_End.IsEnabled = (ShipcallModel.Shipcall?.Type == ShipcallType.Arrival);
this.datePickerETDBerth.IsEnabled = (ShipcallModel.Shipcall?.Type == ShipcallType.Departure || ShipcallModel.Shipcall?.Type == ShipcallType.Shifting);
this.datePickerETDBerth_End.IsEnabled = (ShipcallModel.Shipcall?.Type == ShipcallType.Departure || ShipcallModel.Shipcall?.Type == ShipcallType.Shifting);
this.datePickerLockTime.IsEnabled = (ShipcallModel.Shipcall?.Type == ShipcallType.Arrival || ShipcallModel.Shipcall?.Type == ShipcallType.Shifting);
this.datePickerZoneEntry.IsEnabled = false;
this.textBoxRemarks.IsEnabled = true;
this.datePickerATA.IsEnabled = true;
this.datePickerATD.IsEnabled = true;
break;
case Extensions.ParticipantType.PILOT:
this.datePickerETABerth.IsEnabled = (ShipcallModel.Shipcall?.Type == ShipcallType.Arrival);
this.datePickerETABerth_End.IsEnabled = (ShipcallModel.Shipcall?.Type == ShipcallType.Arrival);
this.datePickerETDBerth.IsEnabled = (ShipcallModel.Shipcall?.Type == ShipcallType.Departure || ShipcallModel.Shipcall?.Type == ShipcallType.Shifting);
this.datePickerETDBerth_End.IsEnabled = (ShipcallModel.Shipcall?.Type == ShipcallType.Departure || ShipcallModel.Shipcall?.Type == ShipcallType.Shifting);
this.datePickerLockTime.IsEnabled = (ShipcallModel.Shipcall?.Type == ShipcallType.Arrival || ShipcallModel.Shipcall?.Type == ShipcallType.Shifting);
this.datePickerZoneEntry.IsEnabled = (ShipcallModel.Shipcall?.Type == ShipcallType.Arrival);
this.textBoxRemarks.IsEnabled = true;
case Extensions.ParticipantType.PORT_ADMINISTRATION:
this.datePickerLockTime.IsEnabled = true;
break;
case Extensions.ParticipantType.TUG:
case Extensions.ParticipantType.PILOT:
this.datePickerZoneEntry.IsEnabled = (ShipcallModel.Shipcall?.Type == ShipcallType.Arrival);
break;
}
this.buttonOK.IsEnabled = true;
CheckOKButton();
}
private void SetLockButton(bool newValue)
@ -182,14 +186,26 @@ namespace BreCalClient
{
this.Times.EtaBerthFixed = true;
this.imageFixedOrder.Source = new BitmapImage(new Uri(@"pack://application:,,,/Resources/lock.png", UriKind.RelativeOrAbsolute));
this.buttonFixedOrder.ToolTip = BreCalClient.Resources.Resources.textTooltipUnSetFixedOrder;
}
else
{
this.Times.EtaBerthFixed = false;
this.imageFixedOrder.Source = new BitmapImage(new Uri(@"pack://application:,,,/Resources/lock_open.png", UriKind.RelativeOrAbsolute));
this.buttonFixedOrder.ToolTip = BreCalClient.Resources.Resources.textTooltipSetFixedOrder;
}
}
private void CheckOKButton()
{
Extensions.ParticipantType pType = (Extensions.ParticipantType)this.Times.ParticipantType;
if (pType != Extensions.ParticipantType.PORT_ADMINISTRATION)
this.buttonOK.IsEnabled = (ShipcallModel.Shipcall?.Type == ShipcallType.Arrival) ?
this.datePickerETABerth.Value.HasValue : this.datePickerETDBerth.Value.HasValue;
else
this.buttonOK.IsEnabled = true;
}
#endregion
#region clear value event handler
@ -235,6 +251,6 @@ namespace BreCalClient
}
#endregion
}
}

View File

@ -7,7 +7,7 @@
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:local="clr-namespace:BreCalClient"
mc:Ignorable="d" Left="{local:SettingBinding W1Left}" Top="{local:SettingBinding W1Top}"
Title="{x:Static p:Resources.textEditTimes}" Loaded="Window_Loaded" Height="295" Width="500" >
Title="{x:Static p:Resources.textEditTimes}" Loaded="Window_Loaded" Height="295" Width="500" ResizeMode="CanResizeWithGrip" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".3*" />
@ -20,7 +20,7 @@
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="56" />
<RowDefinition Height="56" />
<RowDefinition Height="*" />
<RowDefinition Height="28" />
</Grid.RowDefinitions>
@ -38,7 +38,7 @@
<ColumnDefinition Width=".5*" />
</Grid.ColumnDefinitions>
<xctk:DateTimePicker Grid.Row="0" Grid.Column="0" Margin="2" Name="datePickerOperationStart" Format="Custom" FormatString="dd.MM. yyyy HH:mm" IsEnabled="False">
<xctk:DateTimePicker Grid.Row="0" Grid.Column="0" Margin="2" Name="datePickerOperationStart" Format="Custom" FormatString="dd.MM. yyyy HH:mm" IsEnabled="False" ValueChanged="datePickerOperationStart_ValueChanged">
<xctk:DateTimePicker.ContextMenu>
<ContextMenu>
<MenuItem Header="{x:Static p:Resources.textClearValue}" Name="contextMenuItemClearOperationStart" Click="contextMenuItemClearOperationStart_Click" >
@ -69,7 +69,7 @@
<ColumnDefinition Width=".5*" />
</Grid.ColumnDefinitions>
<xctk:DateTimePicker Grid.Row="0" Grid.Column="0" Margin="2" Name="datePickerOperationEnd" Format="Custom" FormatString="dd.MM. yyyy HH:mm" IsEnabled="False">
<xctk:DateTimePicker Grid.Row="0" Grid.Column="0" Margin="2" Name="datePickerOperationEnd" Format="Custom" FormatString="dd.MM. yyyy HH:mm" IsEnabled="False" ValueChanged="datePickerOperationEnd_ValueChanged">
<xctk:DateTimePicker.ContextMenu>
<ContextMenu>
<MenuItem Header="{x:Static p:Resources.textClearValue}" Name="contextMenuItemClearOperationEnd" Click="contextMenuItemClearOperationEnd_Click" >
@ -112,8 +112,8 @@
</ContextMenu>
</ComboBox.ContextMenu>
</ComboBox>
<TextBox Grid.Row="4" Grid.Column="1" Margin="2" Name="textBoxBerthRemarks" TextWrapping="Wrap" AcceptsReturn="True" SpellCheck.IsEnabled="True" AcceptsTab="False" IsEnabled="False" MaxLength="512" />
<TextBox Grid.Row="5" Grid.Column="1" Margin="2" Name="textBoxRemarks" TextWrapping="Wrap" AcceptsReturn="True" SpellCheck.IsEnabled="True" AcceptsTab="False" IsEnabled="False" MaxLength="512" />
<TextBox Grid.Row="4" Grid.Column="1" Margin="2" Name="textBoxBerthRemarks" TextWrapping="Wrap" AcceptsReturn="True" SpellCheck.IsEnabled="True" AcceptsTab="False" IsReadOnly="True" MaxLength="512" ScrollViewer.VerticalScrollBarVisibility="Auto"/>
<TextBox Grid.Row="5" Grid.Column="1" Margin="2" Name="textBoxRemarks" TextWrapping="Wrap" AcceptsReturn="True" SpellCheck.IsEnabled="True" AcceptsTab="False" IsReadOnly="True" MaxLength="512" ScrollViewer.VerticalScrollBarVisibility="Auto"/>
<StackPanel Grid.Row="6" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Width= "80" Margin="2" Content="{x:Static p:Resources.textOK}" x:Name="buttonOK" Click="buttonOK_Click" IsEnabled="False"/>
<Button Width="80" Margin="2" Content="{x:Static p:Resources.textCancel}" x:Name="buttonCancel" Click="buttonCancel_Click"/>

View File

@ -76,6 +76,16 @@ namespace BreCalClient
this.comboBoxPierside.SelectedIndex = -1;
}
private void datePickerOperationStart_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
this.CheckOKButton();
}
private void datePickerOperationEnd_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
this.CheckOKButton();
}
#endregion
#region private methods
@ -134,9 +144,15 @@ namespace BreCalClient
this.datePickerOperationEnd_End.IsEnabled = (ShipcallModel.Shipcall?.Type == ShipcallType.Departure) || (ShipcallModel.Shipcall?.Type == ShipcallType.Shifting);
this.comboBoxBerth.IsEnabled = ShipcallModel.Shipcall?.Type == ShipcallType.Arrival;
this.comboBoxPierside.IsEnabled = ShipcallModel.Shipcall?.Type == ShipcallType.Arrival;
this.textBoxBerthRemarks.IsEnabled = ShipcallModel.Shipcall?.Type == ShipcallType.Arrival;
this.textBoxRemarks.IsEnabled = true;
this.buttonOK.IsEnabled = true;
this.textBoxBerthRemarks.IsReadOnly = ShipcallModel.Shipcall?.Type != ShipcallType.Arrival;
this.textBoxRemarks.IsReadOnly = false;
this.CheckOKButton();
}
private void CheckOKButton()
{
this.buttonOK.IsEnabled = (ShipcallModel.Shipcall?.Type == ShipcallType.Arrival) ? this.datePickerOperationStart.Value.HasValue :
this.datePickerOperationEnd.Value.HasValue;
}
#endregion

View File

@ -0,0 +1,71 @@
// Copyright (c) 2024- schick Informatik
// Description: Helpers to display localized Enum values in Comboboxes
// https://stackoverflow.com/questions/29658721/enum-in-wpf-comboxbox-with-localized-names
//
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;
namespace BreCalClient
{
#region class EnumToStringConverter
public sealed class EnumToStringConverter : IValueConverter
{
public object? Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
{ return null; }
return Resources.Resources.ResourceManager.GetString(value.ToString() ?? "");
}
public object? ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string str = (string)value;
foreach (object enumValue in Enum.GetValues(targetType))
{
if (str == Resources.Resources.ResourceManager.GetString(enumValue.ToString() ?? ""))
{ return enumValue; }
}
return null;
}
}
#endregion
#region class EnumerateExtension
public sealed class EnumerateExtension : MarkupExtension
{
public Type Type { get; set; }
public EnumerateExtension(Type type)
{
this.Type = type;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
string[] names = Enum.GetNames(Type);
// skip value "0" == "Unknown" (we dont want this selectable in the Combobox)
// NOTE: This will only work in the future if the first element is always "undefined" aka unused
string[] values = new string[names.Length - 1];
for (int i = 0; i < names.Length - 1; i++)
{
values[i] = Resources.Resources.ResourceManager.GetString(names[i + 1]) ?? names[i];
}
return values;
}
}
#endregion
}

View File

@ -80,7 +80,12 @@ namespace BreCalClient
{
if(string.IsNullOrEmpty(value)) return value;
if (value.Length <= maxLength) return value;
if (value.Length > (maxLength + 1)) return $"{value[..maxLength]}..";
if (value.Length > (maxLength + 1))
{
int i = maxLength - 2;
for (; (i > 0) && !(char.IsWhiteSpace(value[i])); i--) ; // try to put the "..." at a word break
return value.Substring(0, i) + " ...";
}
return value[..maxLength];
}

View File

@ -14,19 +14,26 @@
<RowDefinition Height="25" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".25*" />
<ColumnDefinition Width=".25*" />
<ColumnDefinition Width=".25*" />
<ColumnDefinition Width=".25*" />
<ColumnDefinition Width=".2*" />
<ColumnDefinition Width=".2*" />
<ColumnDefinition Width=".2*" />
<ColumnDefinition Width=".2*" />
<ColumnDefinition Width=".2*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" FontSize="10" FontWeight="DemiBold" Text="{x:Static p:Resources.textShip}" VerticalAlignment="Center" Foreground="Gray" />
<TextBlock Grid.Row="0" Grid.Column="1" FontSize="10" FontWeight="DemiBold" Text="{x:Static p:Resources.textTimestamp}" VerticalAlignment="Center" Foreground="Gray"/>
<TextBlock Grid.Row="0" Grid.Column="2" FontSize="10" FontWeight="DemiBold" Text="{x:Static p:Resources.textOperation}" VerticalAlignment="Center" Foreground="Gray"/>
<TextBlock Grid.Row="0" Grid.Column="3" FontSize="10" FontWeight="DemiBold" Text="{x:Static p:Resources.textParticipant}" VerticalAlignment="Center" Foreground="Gray"/>
<TextBlock Grid.Row="1" Grid.Column="0" x:Name="textBlockShip" FontWeight="DemiBold" />
<TextBlock Grid.Row="1" Grid.Column="1" x:Name="textBlockTimestamp" />
<TextBlock Grid.Row="1" Grid.Column="2" x:Name="textBlockOperation" />
<TextBlock Grid.Row="1" Grid.Column="3" x:Name="textBlockParticipant" />
<TextBlock Grid.Row="0" Grid.Column="1" FontSize="10" FontWeight="DemiBold" VerticalAlignment="Center" x:Name="textBlockShipcallType" />
<TextBlock Grid.Row="0" Grid.Column="2" FontSize="10" FontWeight="DemiBold" Text="{x:Static p:Resources.textTimestamp}" VerticalAlignment="Center" Foreground="Gray"/>
<TextBlock Grid.Row="0" Grid.Column="3" FontSize="10" FontWeight="DemiBold" Text="{x:Static p:Resources.textOperation}" VerticalAlignment="Center" Foreground="Gray"/>
<TextBlock Grid.Row="0" Grid.Column="4" FontSize="10" FontWeight="DemiBold" Text="{x:Static p:Resources.textParticipant}" VerticalAlignment="Center" Foreground="Gray"/>
<TextBlock Grid.Row="1" Grid.Column="0" x:Name="textBlockShip" FontWeight="DemiBold">
<Hyperlink Click="textBlockShip_Click">
<TextBlock x:Name="hyperLinkShip" />
</Hyperlink>
</TextBlock>
<TextBlock Grid.Row="1" Grid.Column="1" x:Name="textBlockEta" />
<TextBlock Grid.Row="1" Grid.Column="2" x:Name="textBlockTimestamp" />
<TextBlock Grid.Row="1" Grid.Column="3" x:Name="textBlockOperation" />
<TextBlock Grid.Row="1" Grid.Column="4" x:Name="textBlockParticipant" />
</Grid>
</Border>
</UserControl>

View File

@ -3,6 +3,7 @@
//
using BreCalClient.misc.Model;
using System;
using System.Windows.Controls;
namespace BreCalClient
@ -12,16 +13,26 @@ namespace BreCalClient
/// </summary>
public partial class HistoryControl : UserControl
{
public HistoryControl(string ship, History history)
private readonly History _history;
public event Action<int>? HistorySelected;
public HistoryControl(string ship, History history, string callType, string etaetd)
{
InitializeComponent();
_history = history;
this.textBlockOperation.Text = $"{history.Operation} on {history.Type}";
this.textBlockShip.Text = ship;
this.hyperLinkShip.Text = ship;
if(BreCalLists.ParticipantLookupDict.ContainsKey(history.ParticipantId))
this.textBlockParticipant.Text = BreCalLists.ParticipantLookupDict[history.ParticipantId].Name;
this.textBlockTimestamp.Text = history.Timestamp.ToString();
this.textBlockEta.Text = etaetd;
this.textBlockShipcallType.Text = callType;
}
private void textBlockShip_Click(object sender, System.Windows.RoutedEventArgs e)
{
this.HistorySelected?.Invoke(_history.ShipcallId);
}
}
}

View File

@ -16,6 +16,17 @@
<ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Auto" Margin="2">
<StackPanel x:Name="stackPanel"/>
</ScrollViewer>
<Button x:Name="buttonClose" Click="buttonClose_Click" Content="{x:Static p:Resources.textClose}" Width="80" Margin="2" Grid.Row="1" HorizontalAlignment="Right" />
<Grid Grid.Row="1" Grid.Column="0" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="22" />
<ColumnDefinition Width="80" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width=".2*" />
</Grid.ColumnDefinitions>
<CheckBox x:Name="checkboxMyOwnOnly" VerticalContentAlignment="Center" Grid.Column="0" Margin="2" Checked="checkboxMyOwnOnly_Checked" Unchecked="checkboxMyOwnOnly_Checked" />
<Label Content="{x:Static p:Resources.textMineOnly}" Grid.Column="1" />
<Button x:Name="buttonClose" Click="buttonClose_Click" Content="{x:Static p:Resources.textClose}" Width="80" Margin="2" Grid.Row="0" Grid.Column="3" HorizontalAlignment="Right" />
</Grid>
</Grid>
</Window>

View File

@ -4,9 +4,14 @@
using BreCalClient.misc.Api;
using BreCalClient.misc.Model;
using log4net;
using log4net.Core;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Printing;
using System.Windows;
using System.Windows.Input;
namespace BreCalClient
{
@ -19,6 +24,13 @@ namespace BreCalClient
private readonly ConcurrentDictionary<int, ShipcallControlModel> _shipcalls;
private readonly StaticApi _staticApi;
private readonly static ILog _log = LogManager.GetLogger(typeof(HistoryDialog));
#endregion
#region delegate/event to react to history item selection
public event Action<int>? HistoryItemSelected;
#endregion
@ -37,6 +49,7 @@ namespace BreCalClient
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Mouse.OverrideCursor = Cursors.Wait;
RefreshHistory();
}
@ -45,6 +58,12 @@ namespace BreCalClient
this.Close();
}
private void checkboxMyOwnOnly_Checked(object sender, RoutedEventArgs e)
{
Mouse.OverrideCursor = Cursors.Wait;
RefreshHistory();
}
#endregion
#region private methods
@ -52,30 +71,80 @@ namespace BreCalClient
private async void RefreshHistory()
{
List<History> allHistories = new();
foreach (int shipcall_id in _shipcalls.Keys)
try
{
List<History> shipcallHistory = await _staticApi.HistoryGetAsync(shipcall_id);
System.Diagnostics.Trace.WriteLine($"{shipcallHistory.Count} history elements loaded for shipcall {shipcall_id}");
allHistories.AddRange( shipcallHistory );
foreach (int shipcall_id in _shipcalls.Keys)
{
List<History> shipcallHistory = await _staticApi.HistoryGetAsync(shipcall_id);
System.Diagnostics.Trace.WriteLine($"{shipcallHistory.Count} history elements loaded for shipcall {shipcall_id}");
allHistories.AddRange(shipcallHistory);
}
this.stackPanel.Children.Clear();
// sort all entries
allHistories.Sort((x, y) => { return y.Timestamp.CompareTo(x.Timestamp); });
EnumToStringConverter enumToStringConverter = new();
// create controls for all entries
foreach (History history in allHistories)
{
if (FilterShipcall(history.ShipcallId)) continue;
string shipname = "";
Ship? ship = this._shipcalls[history.ShipcallId].Ship;
if (ship != null)
shipname = ship.Name;
string etaetd = "", calltype = "";
if (_shipcalls.ContainsKey(history.ShipcallId))
{
etaetd = _shipcalls[history.ShipcallId].GetETAETD();
if (_shipcalls[history.ShipcallId].Shipcall != null)
{
ShipcallType? type = _shipcalls[history.ShipcallId].Shipcall?.Type;
if (type != null) calltype = (string)(enumToStringConverter.Convert(type ?? ShipcallType.Undefined, typeof(ShipcallType), new(), System.Globalization.CultureInfo.CurrentCulture) ?? "");
}
}
HistoryControl hc = new(shipname, history, calltype, etaetd);
hc.HistorySelected += (x) => { HistoryItemSelected?.Invoke(x); }; // bubble event
this.stackPanel.Children.Add(hc);
}
Mouse.OverrideCursor = null;
}
catch (Exception e)
{
// Here we rather not show a dialog box since it may confuse the user
_log.Error(e.ToString());
}
// sort all entries
allHistories.Sort((x, y) => { return y.Timestamp.CompareTo(x.Timestamp); });
}
// create controls for all entries
foreach (History history in allHistories)
bool FilterShipcall(int shipcallId)
{
bool result = true;
if (shipcallId < 0) return result;
if(_shipcalls.TryGetValue(shipcallId, out ShipcallControlModel? scm))
{
string shipname = "";
Ship? ship = this._shipcalls[history.ShipcallId].Ship;
if (ship != null)
shipname = ship.Name;
HistoryControl hc = new(shipname, history);
this.stackPanel.Children.Add(hc);
}
if(this.checkboxMyOwnOnly.IsChecked ?? false)
{
foreach(ParticipantAssignment p in scm.AssignedParticipants.Values)
{
if (p.ParticipantId.Equals(App.Participant.Id)) return false;
}
}
else
{
return false;
}
}
return result;
}
#endregion
}
}

View File

@ -116,6 +116,7 @@
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="26" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="100" />
@ -150,10 +151,15 @@
</StatusBarItem>
<Separator Grid.Column="11"/>
<StatusBarItem Grid.Column="12">
<Button x:Name="buttonManualRefresh" Width="20" Click="buttonManualRefresh_Click" ToolTip="{x:Static p:Resources.textTriggerManualRefresh}">
<Image Source="./Resources/nav_refresh_green.png"/>
</Button>
</StatusBarItem>
<StatusBarItem Grid.Column="13">
<TextBlock x:Name="labelLatestUpdate" />
</StatusBarItem>
<Separator Grid.Column="13"/>
<StatusBarItem Grid.Column="14">
<Separator Grid.Column="14"/>
<StatusBarItem Grid.Column="15">
<ProgressBar Name="generalProgressStatus" Width="90" Height="16" Foreground="LightGray"/>
</StatusBarItem>
</StatusBar>

View File

@ -22,6 +22,7 @@ using Newtonsoft.Json;
using Polly;
using System.Net.Http;
using System.Net;
using System.Windows.Input;
namespace BreCalClient
@ -37,7 +38,8 @@ namespace BreCalClient
#region Fields
private static Int32 _uiUpdateRunning = 0;
//private static int _uiUpdateRunning = 0;
private static SemaphoreSlim uiLock = new SemaphoreSlim(1);
private Credentials? _credentials;
@ -96,9 +98,10 @@ namespace BreCalClient
var jitterer = new Random();
var retryPolicy =
Policy.Handle<HttpRequestException>()
.OrResult<RestSharp.RestResponse>(resp => resp.StatusCode == HttpStatusCode.Unauthorized)
.WaitAndRetryAsync(3,
// Policy.Handle<HttpRequestException>()
Policy.HandleResult<RestSharp.RestResponse>(resp => resp.StatusCode == HttpStatusCode.Unauthorized)
//.OrResult<RestSharp.RestResponse>
.WaitAndRetryAsync(1,
retryAttempt =>
{
var calculatedDelayInMilliseconds = Math.Pow(2, retryAttempt) * 1000;
@ -113,6 +116,7 @@ namespace BreCalClient
Trace.WriteLine("token refreshed");
});
RetryConfiguration.AsyncRetryPolicy = retryPolicy;
this.generalProgressStatus.Maximum = PROGRESS_STEPS;
}
@ -160,6 +164,7 @@ namespace BreCalClient
{
if (_loginResult.Id > 0)
{
Mouse.OverrideCursor = Cursors.Wait;
this.busyIndicator.IsBusy = false;
this._userApi.Configuration.ApiKey["Authorization"] = _loginResult.Token;
this._shipcallApi.Configuration.ApiKey["Authorization"] = _loginResult.Token;
@ -233,7 +238,7 @@ namespace BreCalClient
NewWithModel(null);
}
private void NewWithModel(ShipcallControlModel? model)
private async void NewWithModel(ShipcallControlModel? model)
{
EditShipcallControl esc = new()
{
@ -248,7 +253,9 @@ namespace BreCalClient
// create UI & save new dialog model
if (esc.ShipcallModel.Shipcall != null)
{
await uiLock.WaitAsync();
this.UpdateUI();
uiLock.Release();
esc.ShipcallModel.Shipcall?.Participants.Clear();
foreach (ParticipantAssignment pa in esc.ShipcallModel.AssignedParticipants.Values)
@ -269,9 +276,13 @@ namespace BreCalClient
// if this was an arrival, create the matching departure call and open it
if (esc.ShipcallModel.Shipcall?.Type == ShipcallType.Arrival)
{
ShipcallControlModel scmOut = new();
scmOut.Shipcall = new();
scmOut.Shipcall.Type = ShipcallType.Departure;
ShipcallControlModel scmOut = new()
{
Shipcall = new()
{
Type = ShipcallType.Departure
}
};
scmOut.Shipcall.ShipId = esc.ShipcallModel.Shipcall.ShipId;
scmOut.Ship = esc.ShipcallModel.Ship;
DateTime eta = esc.ShipcallModel.Shipcall?.Eta ?? DateTime.Now;
@ -284,7 +295,11 @@ namespace BreCalClient
foreach(ParticipantType pType in esc.ShipcallModel.AssignedParticipants.Keys)
scmOut.AssignedParticipants[pType] = esc.ShipcallModel.AssignedParticipants[pType];
}
NewWithModel(scmOut);
this.Dispatcher.Invoke(() =>
{
NewWithModel(scmOut);
});
}
}
}
@ -292,8 +307,10 @@ namespace BreCalClient
private void buttonInfo_Click(object sender, RoutedEventArgs e)
{
AboutDialog ad = new();
ad.LoginResult = this._loginResult;
AboutDialog ad = new()
{
LoginResult = this._loginResult
};
ad.ChangePasswordRequested += async (oldPw, newPw) =>
{
if (_loginResult != null)
@ -332,10 +349,12 @@ namespace BreCalClient
this.FilterShipcalls();
}
private void SearchFilterControl_SearchFilterChanged()
private async void SearchFilterControl_SearchFilterChanged()
{
this.FilterShipcalls();
await uiLock.WaitAsync();
this.UpdateUI();
uiLock.Release();
}
private void checkboxShowCancelledCalls_Checked(object sender, RoutedEventArgs e)
@ -344,11 +363,13 @@ namespace BreCalClient
this.SearchFilterControl_SearchFilterChanged();
}
private void comboBoxSortOrder_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
private async void comboBoxSortOrder_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
_sortOrder = (Extensions.SortOrder) this.comboBoxSortOrder.SelectedIndex;
this.FilterShipcalls();
await uiLock.WaitAsync();
this.UpdateUI();
uiLock.Release();
}
private void buttonHistory_Click(object sender, RoutedEventArgs e)
@ -357,6 +378,11 @@ namespace BreCalClient
{
_historyDialog = new HistoryDialog(_allShipcallsDict, _staticApi);
_historyDialog.Closed += (sender, e) => { this._historyDialog = null; };
_historyDialog.HistoryItemSelected += (x) =>
{
if(_allShipCallsControlDict.ContainsKey(x))
_allShipCallsControlDict[x].BringIntoView();
};
_historyDialog.Show();
}
else
@ -365,6 +391,13 @@ namespace BreCalClient
}
}
private void buttonManualRefresh_Click(object sender, RoutedEventArgs e)
{
_refreshImmediately = true; // set flag to avoid timer loop termination
_tokenSource.Cancel(); // force timer loop end
Mouse.OverrideCursor = Cursors.Wait;
}
#endregion
#region network operations
@ -421,6 +454,7 @@ namespace BreCalClient
{
labelGeneralStatus.Text = $"Connection {ConnectionStatus.SUCCESSFUL}";
labelLatestUpdate.Text = $"Last update: {DateTime.Now.ToLongTimeString()}";
labelStatusBar.Text = "";
generalProgressStatus.Value = 0;
}));
}
@ -438,49 +472,68 @@ namespace BreCalClient
}
}
if (shipcalls != null)
try
{
foreach (Shipcall shipcall in shipcalls)
{
// load times for each shipcall
List<Times> currentTimes = await _timesApi.TimesGetAsync(shipcall.Id);
if(!_allShipcallsDict.ContainsKey(shipcall.Id))
if (shipcalls != null)
{
foreach (Shipcall shipcall in shipcalls)
{
// add entry
ShipcallControlModel scm = new()
// load times for each shipcall
List<Times> currentTimes = await _timesApi.TimesGetAsync(shipcall.Id);
if (!_allShipcallsDict.ContainsKey(shipcall.Id))
{
Shipcall = shipcall,
Times = currentTimes
};
this.AddShipcall(scm);
// add entry
ShipcallControlModel scm = new()
{
Shipcall = shipcall,
Times = currentTimes
};
this.AddShipcall(scm);
}
else
{
// update entry
_allShipcallsDict[shipcall.Id].Shipcall = shipcall;
_allShipcallsDict[shipcall.Id].Times = currentTimes;
UpdateShipcall(_allShipcallsDict[shipcall.Id]);
}
}
else
List<int> existingIds = new(this._allShipcallsDict.Keys);
foreach (int existingId in existingIds)
{
// update entry
_allShipcallsDict[shipcall.Id].Shipcall = shipcall;
_allShipcallsDict[shipcall.Id].Times = currentTimes;
UpdateShipcall(_allShipcallsDict[shipcall.Id]);
if (shipcalls.Find(s => s.Id == existingId) == null) // the model is no longer in the search result
{
this.RemoveShipcall(existingId);
}
}
this.FilterShipcalls();
await uiLock.WaitAsync();
this.UpdateUI();
}
List<int> existingIds = new(this._allShipcallsDict.Keys);
foreach (int existingId in existingIds)
{
if (shipcalls.Find(s => s.Id == existingId) == null) // the model is no longer in the search result
{
this.RemoveShipcall(existingId);
}
}
this.FilterShipcalls();
this.UpdateUI();
}
catch(Exception ex)
{
_log.Error(ex);
}
finally
{
uiLock.Release();
}
try
{
double interval = (double) SHIPCALL_UPDATE_INTERVAL_SECONDS / PROGRESS_STEPS;
//if (Interlocked.CompareExchange(ref _uiUpdateRunning, 1, 0) == 1) // do not restart progress unless UI update has completed
// await Task.Delay(TimeSpan.FromSeconds(interval));
System.Diagnostics.Trace.WriteLine("restarting refresh countdown");
for (int i = 0; i < PROGRESS_STEPS; i++)
{
await Task.Delay(TimeSpan.FromSeconds(interval), _tokenSource.Token);
@ -585,6 +638,8 @@ namespace BreCalClient
else
{
searchPastDays = 0;
if (sfm.EtaFrom == null)
sfm.EtaFrom = DateTime.Now.AddDays(-2);
}
this._visibleControlModels.Clear();
@ -710,7 +765,13 @@ namespace BreCalClient
if (x.Shipcall == null) return 0;
if (y.Shipcall == null) return 0;
DateTime xDate = (x.Shipcall.Type == ShipcallType.Arrival) ? x.Eta ?? DateTime.Now : x.Etd ?? DateTime.Now;
Times? xTimes = x.GetTimesForParticipantType(ParticipantType.AGENCY);
if(xTimes != null)
xDate = (x.Shipcall.Type == ShipcallType.Arrival) ? xTimes.EtaBerth ?? DateTime.Now : xTimes.EtdBerth ?? DateTime.Now;
DateTime yDate = (y.Shipcall.Type == ShipcallType.Arrival) ? y.Eta ?? DateTime.Now : y.Etd ?? DateTime.Now;
Times? yTimes = y.GetTimesForParticipantType(ParticipantType.AGENCY);
if (yTimes != null)
yDate = (y.Shipcall.Type == ShipcallType.Arrival) ? yTimes.EtaBerth ?? DateTime.Now : yTimes.EtdBerth ?? DateTime.Now;
return DateTime.Compare(xDate, yDate);
});
break;
@ -730,7 +791,7 @@ namespace BreCalClient
this.Dispatcher.Invoke(new Action(() =>
{
if (Interlocked.CompareExchange(ref _uiUpdateRunning, 1, 0) == 1) return;
//if (Interlocked.CompareExchange(ref _uiUpdateRunning, 1, 0) == 1) return;
try
{
@ -750,9 +811,10 @@ namespace BreCalClient
}
finally
{
_uiUpdateRunning = 0;
// _uiUpdateRunning = 0;
}
Mouse.OverrideCursor = null;
}));
}
@ -955,6 +1017,6 @@ namespace BreCalClient
}
#endregion
}
}

View File

@ -5,7 +5,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<Project>
<PropertyGroup>
<ApplicationRevision>1</ApplicationRevision>
<ApplicationVersion>1.2.0.1</ApplicationVersion>
<ApplicationVersion>1.2.0.2</ApplicationVersion>
<BootstrapperEnabled>True</BootstrapperEnabled>
<Configuration>Debug</Configuration>
<CreateDesktopShortcut>True</CreateDesktopShortcut>

View File

@ -4,8 +4,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>6.0</ApplicationVersion>
<ApplicationRevision>10</ApplicationRevision>
<ApplicationVersion>1.2.1.2</ApplicationVersion>
<BootstrapperEnabled>False</BootstrapperEnabled>
<Configuration>Release</Configuration>
<CreateWebPageOnPublish>True</CreateWebPageOnPublish>

View File

@ -4,8 +4,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.2.0.0</ApplicationVersion>
<ApplicationRevision>2</ApplicationRevision>
<ApplicationVersion>1.2.0.10</ApplicationVersion>
<BootstrapperEnabled>True</BootstrapperEnabled>
<Configuration>Debug</Configuration>
<CreateDesktopShortcut>True</CreateDesktopShortcut>

View File

@ -9,38 +9,38 @@
//------------------------------------------------------------------------------
namespace BreCalClient.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.5.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("#1D751F")]
[global::System.Configuration.DefaultSettingValueAttribute("#203864")]
public string BG_COLOR {
get {
return ((string)(this["BG_COLOR"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("!!Bremen calling Testversion!!")]
[global::System.Configuration.DefaultSettingValueAttribute("Bremen calling")]
public string APP_TITLE {
get {
return ((string)(this["APP_TITLE"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("https://www.textbausteine.net/")]
@ -49,7 +49,7 @@ namespace BreCalClient.Properties {
return ((string)(this["LOGO_IMAGE_URL"]));
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("")]
@ -61,16 +61,16 @@ namespace BreCalClient.Properties {
this["FilterCriteria"] = value;
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("https://brecaldevel.bsmd-emswe.eu")]
[global::System.Configuration.DefaultSettingValueAttribute("https://brecal.bsmd-emswe.eu")]
public string API_URL {
get {
return ((string)(this["API_URL"]));
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("800")]
@ -82,7 +82,7 @@ namespace BreCalClient.Properties {
this["Width"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("450")]
@ -94,7 +94,7 @@ namespace BreCalClient.Properties {
this["Height"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]
@ -106,7 +106,7 @@ namespace BreCalClient.Properties {
this["Left"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]
@ -118,7 +118,7 @@ namespace BreCalClient.Properties {
this["Top"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]
@ -130,7 +130,7 @@ namespace BreCalClient.Properties {
this["W1Left"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]
@ -142,7 +142,7 @@ namespace BreCalClient.Properties {
this["W1Top"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]
@ -154,7 +154,7 @@ namespace BreCalClient.Properties {
this["W2Left"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]
@ -166,7 +166,7 @@ namespace BreCalClient.Properties {
this["W2Top"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]
@ -178,7 +178,7 @@ namespace BreCalClient.Properties {
this["W3Left"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]
@ -190,7 +190,7 @@ namespace BreCalClient.Properties {
this["W3Top"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]
@ -202,7 +202,7 @@ namespace BreCalClient.Properties {
this["W4Left"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]

View File

@ -3,10 +3,10 @@
<Profiles />
<Settings>
<Setting Name="BG_COLOR" Type="System.String" Scope="Application">
<Value Profile="(Default)">#1D751F</Value>
<Value Profile="(Default)">#203864</Value>
</Setting>
<Setting Name="APP_TITLE" Type="System.String" Scope="Application">
<Value Profile="(Default)">!!Bremen calling Testversion!!</Value>
<Value Profile="(Default)">Bremen calling</Value>
</Setting>
<Setting Name="LOGO_IMAGE_URL" Type="System.String" Scope="Application">
<Value Profile="(Default)">https://www.textbausteine.net/</Value>
@ -15,7 +15,7 @@
<Value Profile="(Default)" />
</Setting>
<Setting Name="API_URL" Type="System.String" Scope="Application">
<Value Profile="(Default)">https://brecaldevel.bsmd-emswe.eu</Value>
<Value Profile="(Default)">https://brecal.bsmd-emswe.eu</Value>
</Setting>
<Setting Name="Width" Type="System.Double" Scope="User">
<Value Profile="(Default)">800</Value>

View File

@ -80,6 +80,15 @@ namespace BreCalClient.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Incoming.
/// </summary>
public static string Arrival {
get {
return ResourceManager.GetString("Arrival", resourceCulture);
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
@ -200,6 +209,15 @@ namespace BreCalClient.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Outgoing.
/// </summary>
public static string Departure {
get {
return ResourceManager.GetString("Departure", resourceCulture);
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
@ -240,6 +258,25 @@ namespace BreCalClient.Resources {
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
public static byte[] nav_refresh_green {
get {
object obj = ResourceManager.GetObject("nav_refresh_green", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized string similar to Shifting.
/// </summary>
public static string Shifting {
get {
return ResourceManager.GetString("Shifting", resourceCulture);
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
@ -989,6 +1026,15 @@ namespace BreCalClient.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Ships.
/// </summary>
public static string textShips {
get {
return ResourceManager.GetString("textShips", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Show cancelled calls.
/// </summary>
@ -1061,6 +1107,33 @@ namespace BreCalClient.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Set as a fixed order.
/// </summary>
public static string textTooltipSetFixedOrder {
get {
return ResourceManager.GetString("textTooltipSetFixedOrder", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Unset as a a fixed order.
/// </summary>
public static string textTooltipUnSetFixedOrder {
get {
return ResourceManager.GetString("textTooltipUnSetFixedOrder", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Trigger a manual refresh of all shipcalls.
/// </summary>
public static string textTriggerManualRefresh {
get {
return ResourceManager.GetString("textTriggerManualRefresh", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Tug.
/// </summary>
@ -1240,6 +1313,15 @@ namespace BreCalClient.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Undefined.
/// </summary>
public static string Undefined {
get {
return ResourceManager.GetString("Undefined", resourceCulture);
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>

View File

@ -472,4 +472,28 @@
<data name="textFixedOrder" xml:space="preserve">
<value>Als feste Bestellung vermerkt</value>
</data>
<data name="textTooltipSetFixedOrder" xml:space="preserve">
<value>Als feste Bestellung vermerken</value>
</data>
<data name="textTooltipUnSetFixedOrder" xml:space="preserve">
<value>Feste Bestellung zurücknehmen</value>
</data>
<data name="textTriggerManualRefresh" xml:space="preserve">
<value>Manuelle Aktualisierung der Anläufe auslösen</value>
</data>
<data name="Arrival" xml:space="preserve">
<value>Einkommend</value>
</data>
<data name="Departure" xml:space="preserve">
<value>Ausgehend</value>
</data>
<data name="Shifting" xml:space="preserve">
<value>Verholung</value>
</data>
<data name="Undefined" xml:space="preserve">
<value>Unbekannt</value>
</data>
<data name="textShips" xml:space="preserve">
<value>Schiffe</value>
</data>
</root>

View File

@ -121,6 +121,9 @@
<data name="add" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>add.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="Arrival" xml:space="preserve">
<value>Incoming</value>
</data>
<data name="arrow_down_green" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>arrow_down_green.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
@ -157,6 +160,9 @@
<data name="delete2" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>delete2.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="Departure" xml:space="preserve">
<value>Outgoing</value>
</data>
<data name="edit" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>edit.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
@ -169,6 +175,12 @@
<data name="logo_bremen_calling" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>logo_bremen_calling.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="nav_refresh_green" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>nav_refresh_green.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="Shifting" xml:space="preserve">
<value>Shifting</value>
</data>
<data name="ship2" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>ship2.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
@ -418,6 +430,9 @@
<data name="textShipLength" xml:space="preserve">
<value>Ship length</value>
</data>
<data name="textShips" xml:space="preserve">
<value>Ships</value>
</data>
<data name="textShowCancelledShipcalls" xml:space="preserve">
<value>Show cancelled calls</value>
</data>
@ -442,6 +457,15 @@
<data name="textTo" xml:space="preserve">
<value>to</value>
</data>
<data name="textTooltipSetFixedOrder" xml:space="preserve">
<value>Set as a fixed order</value>
</data>
<data name="textTooltipUnSetFixedOrder" xml:space="preserve">
<value>Unset as a a fixed order</value>
</data>
<data name="textTriggerManualRefresh" xml:space="preserve">
<value>Trigger a manual refresh of all shipcalls</value>
</data>
<data name="textTug" xml:space="preserve">
<value>Tug</value>
</data>
@ -499,6 +523,9 @@
<data name="umbrella_open" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>umbrella_open.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="Undefined" xml:space="preserve">
<value>Undefined</value>
</data>
<data name="worker2" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>worker2.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -5,9 +5,13 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:p = "clr-namespace:BreCalClient.Resources"
xmlns:local="clr-namespace:BreCalClient"
xmlns:api="clr-namespace:BreCalClient.misc.Model"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d"
d:DesignHeight="56" d:DesignWidth="800" Loaded="UserControl_Loaded">
<UserControl.Resources>
<local:EnumToStringConverter x:Key="enumToStringConverter" />
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="28" />
@ -63,7 +67,7 @@
<Label Grid.Column="1" Content="{x:Static p:Resources.textTo}" />
<DatePicker x:Name="datePickerETATo" Grid.Column="2" Margin="2" SelectedDateChanged="datePickerETATo_SelectedDateChanged" SelectedDate="{Binding Path=EtaTo}"/>
</Grid>
<xctk:CheckComboBox x:Name="comboBoxCategories" Grid.Column="4" Margin="2" ItemSelectionChanged="comboBoxCategories_ItemSelectionChanged" />
<xctk:CheckComboBox x:Name="comboBoxCategories" Grid.Column="4" Margin="2" ItemSelectionChanged="comboBoxCategories_ItemSelectionChanged" ItemsSource="{local:Enumerate {x:Type api:ShipcallType}}" />
<Grid Grid.Column="6" Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*" />

View File

@ -72,7 +72,7 @@ namespace BreCalClient
this.comboBoxAgencies.UnSelectAll();
this.comboBoxBerths.UnSelectAll();
this.comboBoxCategories.UnSelectAll();
this.datePickerETAFrom.SelectedDate = null;
this.datePickerETAFrom.SelectedDate = DateTime.Now.AddDays(-2);
this.datePickerETATo.SelectedDate = null;
this.textBoxSearch.Clear();
this.upDownShiplengthFrom.Value = null;
@ -101,13 +101,19 @@ namespace BreCalClient
}
if(sfm.Categories != null)
{
foreach(ShipcallType category in sfm.Categories)
this.comboBoxCategories.SelectedItems.Add(category);
EnumToStringConverter enumToStringConverter = new();
foreach (ShipcallType category in sfm.Categories)
{
this.comboBoxCategories.SelectedItems.Add(enumToStringConverter.Convert(category, typeof(ShipcallControl), new object(), System.Globalization.CultureInfo.CurrentCulture));
}
}
if (sfm.SearchString != null) this.textBoxSearch.Text = sfm.SearchString;
this.upDownShiplengthFrom.Value = sfm.ShipLengthFrom;
this.upDownShiplengthTo.Value = sfm.ShipLengthTo;
this.datePickerETAFrom.SelectedDate = sfm.EtaFrom;
if (sfm.EtaFrom != null)
this.datePickerETAFrom.SelectedDate = sfm.EtaFrom;
else
this.datePickerETAFrom.SelectedDate = DateTime.Now.AddDays(-2);
this.datePickerETATo.SelectedDate = sfm.EtaTo;
this.checkBoxOwn.IsChecked = sfm.MineOnly;
@ -126,7 +132,7 @@ namespace BreCalClient
private void UserControl_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
this.comboBoxCategories.ItemsSource = Enum.GetValues(typeof(ShipcallType));
}
private void datePickerETAFrom_SelectedDateChanged(object sender, SelectionChangedEventArgs e)
@ -143,9 +149,15 @@ namespace BreCalClient
private void comboBoxCategories_ItemSelectionChanged(object sender, Xceed.Wpf.Toolkit.Primitives.ItemSelectionChangedEventArgs e)
{
EnumToStringConverter enumToStringConverter = new();
_model.Categories.Clear();
foreach(ShipcallType category in comboBoxCategories.SelectedItems)
_model.Categories.Add(category);
foreach (string categoryString in comboBoxCategories.SelectedItems)
{
ShipcallType? type = (ShipcallType?)enumToStringConverter.ConvertBack(categoryString, typeof(ShipcallType), new object(), System.Globalization.CultureInfo.CurrentCulture);
if(type != null)
_model.Categories.Add(type.Value);
}
SearchFilterChanged?.Invoke();
}

View File

@ -5,7 +5,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:p = "clr-namespace:BreCalClient.Resources"
xmlns:local="clr-namespace:BreCalClient"
mc:Ignorable="d" Left="{local:SettingBinding W2Left}" Top="{local:SettingBinding W2Top}"
mc:Ignorable="d" Left="{local:SettingBinding W2Left}" Top="{local:SettingBinding W2Top}" Title="{x:Static p:Resources.textShips}"
Height="490" Width="800" ResizeMode="CanResize" Icon="Resources/containership.ico" Loaded="Window_Loaded">
<Grid>

View File

@ -38,7 +38,7 @@ namespace BreCalClient
this.dataGridShips.Initialize();
this.dataGridShips.ItemsSource = BreCalLists.AllShips;
this.dataGridShips.CreateRequested += DataGridShips_CreateRequested; ;
this.dataGridShips.CreateRequested += DataGridShips_CreateRequested;
this.dataGridShips.EditRequested += DataGridShips_EditRequested;
this.dataGridShips.DeleteRequested += DataGridShips_DeleteRequested;
}
@ -47,10 +47,15 @@ namespace BreCalClient
{
if (obj is ShipModel shipmodel)
{
if(this.ShipApi != null)
await this.ShipApi.ShipDeleteAsync(shipmodel.Ship.Id);
BreCalLists.Ships.Remove(shipmodel); // remove from "selectable" ships
shipmodel.Ship.Deleted = true; // set deleted marker on working instance
if (!shipmodel.Ship.Deleted)
{
if (this.ShipApi != null)
await this.ShipApi.ShipDeleteAsync(shipmodel.Ship.Id);
BreCalLists.Ships.Remove(shipmodel); // remove from "selectable" ships
shipmodel.Ship.Deleted = true; // set deleted marker on working instance
this.dataGridShips.ItemsSource = null;
this.dataGridShips.ItemsSource = BreCalLists.AllShips;
}
}
}
@ -83,9 +88,9 @@ namespace BreCalClient
}
}
private void DataGridShips_CreateRequested()
private async void DataGridShips_CreateRequested()
{
ShipModel shipModel = new ShipModel(new Ship());
ShipModel shipModel = new(new Ship());
EditShipDialog esd = new()
{
Ship = shipModel.Ship
@ -96,11 +101,17 @@ namespace BreCalClient
{
try
{
this.ShipApi?.ShipsCreateAsync(shipModel.Ship);
this.dataGridShips.ItemsSource = null;
BreCalLists.AllShips.Add(shipModel);
BreCalLists.Ships.Add(shipModel);
this.dataGridShips.ItemsSource = BreCalLists.AllShips;
if (this.ShipApi != null)
{
Id id = await this.ShipApi.ShipsCreateAsync(shipModel.Ship);
shipModel.Ship.Id = id.VarId;
this.dataGridShips.ItemsSource = null;
BreCalLists.AllShips.Add(shipModel);
BreCalLists.Ships.Add(shipModel);
if(!BreCalLists.ShipLookupDict.TryAdd(id.VarId, shipModel))
BreCalLists.ShipLookupDict[id.VarId] = shipModel;
this.dataGridShips.ItemsSource = BreCalLists.AllShips;
}
}
catch (Exception ex)
{

View File

@ -1,12 +1,12 @@
<UserControl x:Class="BreCalClient.ShipcallControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:p = "clr-namespace:BreCalClient.Resources"
xmlns:sets="clr-namespace:BreCalClient.Properties"
xmlns:db="clr-namespace:BreCalClient;assembly=BreCalDevelClient"
mc:Ignorable="d"
xmlns:db="clr-namespace:BreCalClient;assembly=BreCalClient"
mc:Ignorable="d"
d:DesignHeight="135" d:DesignWidth="800">
<Border BorderBrush="LightGray" Margin="1" BorderThickness="1">
<Grid>
@ -45,7 +45,7 @@
<ColumnDefinition Width="32" />
</Grid.ColumnDefinitions>
<Image Margin="2" Grid.Column="0" PreviewMouseUp="Image_PreviewMouseUp" x:Name="imageShipcallType" />
<Label Grid.Column="1" FontSize="12" x:Name="labelShipName" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
<Label Grid.Column="1" FontSize="12" x:Name="labelShipName" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" PreviewMouseUp="Image_PreviewMouseUp">
<TextBlock Name="textBlockShipName" />
</Label>
@ -89,23 +89,23 @@
<Viewbox Grid.Row="6" Grid.Column="1" HorizontalAlignment="Left">
<TextBlock x:Name="textBlockBerth" Padding="0" FontWeight="DemiBold" />
</Viewbox>
</Grid>
<Label Grid.Row="0" Grid.Column="1" Grid.RowSpan="1" FontSize="12" Content="- / -" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
<Label Grid.Row="0" Grid.Column="1" Grid.RowSpan="1" FontSize="12" Content="- / -" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Name="labelAgent" PreviewMouseUp="labelAgent_PreviewMouseUp"/>
<Label Grid.Row="0" Grid.Column="2" Grid.RowSpan="1" FontSize="12" Content="- / -" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
<Label Grid.Row="0" Grid.Column="2" Grid.RowSpan="1" FontSize="12" Content="- / -" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Name="labelMooring" PreviewMouseUp="labelMooring_PreviewMouseUp"/>
<Label Grid.Row="0" Grid.Column="3" Grid.RowSpan="1" FontSize="12" Content="- / -" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
<Label Grid.Row="0" Grid.Column="3" Grid.RowSpan="1" FontSize="12" Content="- / -" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Name="labelPortAuthority" PreviewMouseUp="labelPortAuthority_PreviewMouseUp" />
<Label Grid.Row="0" Grid.Column="4" Grid.RowSpan="1" FontSize="12" Content="- / -" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
<Label Grid.Row="0" Grid.Column="4" Grid.RowSpan="1" FontSize="12" Content="- / -" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Name="labelPilot" PreviewMouseUp="labelPilot_PreviewMouseUp"/>
<Label Grid.Row="0" Grid.Column="5" Grid.RowSpan="1" FontSize="12" Content="- / -" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
<Label Grid.Row="0" Grid.Column="5" Grid.RowSpan="1" FontSize="12" Content="- / -" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Name="labelTug" PreviewMouseUp="labelTug_PreviewMouseUp"/>
<Label Grid.Row="0" Grid.Column="6" Grid.RowSpan="1" FontSize="12" Content="- / -" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
<Label Grid.Row="0" Grid.Column="6" Grid.RowSpan="1" FontSize="12" Content="- / -" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Name="labelTerminal" PreviewMouseUp="labelTerminal_PreviewMouseUp" />
<!-- AGENCY -->
<Border Grid.Row="2" Grid.Column="1" BorderThickness="1, 0, 0, 0" BorderBrush="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" Padding="3,0,0,0">
<Grid Grid.Row="2" Grid.Column="1">
@ -119,14 +119,18 @@
<RowDefinition Height="14" />
<RowDefinition Height=".5*" />
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content = "ETA" x:Name="labelETAETDAgent" Padding="0" VerticalContentAlignment="Center" />
<Label Grid.Row="1" Grid.Column="0" Content="{x:Static p:Resources.textRemarks}" Padding="0" VerticalContentAlignment="Center" FontSize="9"/>
<Label Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Content="{x:Static p:Resources.textBerth}" Padding="0" VerticalContentAlignment="Center" FontSize="9"/>
<Border Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" BorderThickness="0,1,0,0" BorderBrush="Gray" >
<Label Content="{x:Static p:Resources.textBerth}" Padding="0" VerticalContentAlignment="Center" FontSize="9"/>
</Border>
<Label Grid.Row="0" Grid.Column="1" Padding="0" VerticalContentAlignment="Center" x:Name="labelAgencyETAETDValue" FontWeight="DemiBold"/>
<TextBlock Grid.Row="1" Grid.Column="1" Grid.RowSpan="1" Padding="0" TextWrapping="Wrap" VerticalAlignment="Top" x:Name="textBlockAgencyRemarks" FontSize="10"/>
<Label Grid.Row="2" Grid.Column="1" HorizontalContentAlignment="Left" x:Name="labelAgencyBerth" Padding="0" VerticalContentAlignment="Center" FontSize="11" FontWeight="SemiBold" />
<TextBlock Grid.Row="3" Grid.Column="0" Text="{x:Static p:Resources.textBerthRemarks}" Padding="0" VerticalAlignment="Top" TextWrapping="Wrap" FontSize="9"/>
<TextBlock Grid.Row="3" Grid.Column="0" Text="{x:Static p:Resources.textRemarks}" Padding="0" VerticalAlignment="Top" TextWrapping="Wrap" FontSize="9"/>
<TextBlock Grid.Row="3" Grid.Column="1" Grid.RowSpan="1" Padding="0" TextWrapping="Wrap" VerticalAlignment="Top" x:Name="textBlockAgencyBerthRemarks" FontSize="10"/>
</Grid>
</Border>
@ -164,13 +168,16 @@
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="0" x:Name="lockTimeRowDefinition" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" x:Name="labelETAETDPortAuthority" Content="ETA" Padding="0" VerticalContentAlignment="Center" />
<Label Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Content="{x:Static p:Resources.textRemarks}" Padding="0" VerticalContentAlignment="Top" FontSize="9"/>
<Label Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Content="{x:Static p:Resources.textRemarks}" Padding="0" VerticalContentAlignment="Top" FontSize="9"/>
<Label Grid.Row="1" Grid.Column="0" Content="{x:Static p:Resources.textLockTime}" VerticalContentAlignment="Center" Padding="0"/>
<Label Grid.Row="1" Grid.Column="1" x:Name="labelPortAuthorityLockTime" VerticalContentAlignment="Center" Padding="0" />
<Label Grid.Row="0" Grid.Column="1" Padding="0" VerticalContentAlignment="Center" x:Name="labelPortAuthorityETAETDValue" FontWeight="DemiBold"/>
<Image Grid.Row="1" Grid.Column="0" x:Name="imagePortAuthorityLocked" VerticalAlignment="Top" Margin="0 20 0 0" HorizontalAlignment="Left" Source="./Resources/lock.png" Width="16" Height="16" ToolTip="{x:Static p:Resources.textFixedOrder}"/>
<TextBlock Grid.Row="1" Grid.Column="1" Padding="0" TextWrapping="Wrap" VerticalAlignment="Top" x:Name="textBlockPortAuthorityRemarks"/>
<Image Grid.Row="2" Grid.Column="0" x:Name="imagePortAuthorityLocked" VerticalAlignment="Top" Margin="0 20 0 0" HorizontalAlignment="Left" Source="./Resources/lock.png" Width="16" Height="16" ToolTip="{x:Static p:Resources.textFixedOrder}"/>
<TextBlock Grid.Row="2" Grid.Column="1" Padding="0" TextWrapping="Wrap" VerticalAlignment="Top" x:Name="textBlockPortAuthorityRemarks"/>
</Grid>
</Border>
<!-- PILOT -->
@ -226,9 +233,11 @@
<Label Grid.Row="0" Grid.Column="1" Padding="0" VerticalContentAlignment="Center" x:Name="labelOperationsStart" FontWeight="DemiBold"/>
<Label Grid.Row="1" Grid.Column="0" Content="{x:Static p:Resources.textRemarks}" Padding="0" VerticalContentAlignment="Top" FontSize="9"/>
<TextBlock Grid.Row="1" Grid.Column="1" Padding="0" TextWrapping="Wrap" VerticalAlignment="Top" x:Name="textBlockTerminalRemarks" FontSize="10"/>
<Label Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Content="{x:Static p:Resources.textBerth}" Padding="0" VerticalContentAlignment="Center" FontSize="9"/>
<Border BorderThickness="0,1,0,0" BorderBrush="Gray" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2">
<Label Content="{x:Static p:Resources.textBerth}" Padding="0" VerticalContentAlignment="Center" FontSize="9"/>
</Border>
<Label Grid.Row="2" Grid.Column="1" HorizontalContentAlignment="Left" x:Name="labelTerminalBerth" Padding="0" VerticalContentAlignment="Center" FontSize="11" FontWeight="SemiBold" />
<TextBlock Grid.Row="3" Grid.Column="0" Text="{x:Static p:Resources.textBerthRemarks}" TextWrapping="Wrap" Padding="0" VerticalAlignment="Top" FontSize="9"/>
<TextBlock Grid.Row="3" Grid.Column="0" Text="{x:Static p:Resources.textRemarks}" TextWrapping="Wrap" Padding="0" VerticalAlignment="Top" FontSize="9"/>
<TextBlock Grid.Row="3" Grid.Column="1" Padding="0" TextWrapping="Wrap" VerticalAlignment="Top" x:Name="textBlockTerminalBerthRemarks" FontSize="10"/>
</Grid>
</Border>

View File

@ -28,6 +28,7 @@ namespace BreCalClient
private static readonly ILog _log = LogManager.GetLogger(typeof(ShipcallControl));
bool ataAdded = false;
bool atdAdded = false;
bool lockTimeAdded = false;
#endregion
@ -213,13 +214,13 @@ namespace BreCalClient
switch (this.ShipcallControlModel?.Shipcall?.Type)
{
case ShipcallType.Arrival: // incoming
this.imageShipcallType.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalDevelClient;component/Resources/arrow_down_red.png"));
this.imageShipcallType.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalClient;component/Resources/arrow_down_red.png"));
break;
case ShipcallType.Departure: // outgoing
this.imageShipcallType.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalDevelClient;component/Resources/arrow_up_blue.png"));
this.imageShipcallType.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalClient;component/Resources/arrow_up_blue.png"));
break;
case ShipcallType.Shifting: // shifting
this.imageShipcallType.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalDevelClient;component/Resources/arrow_right_green.png"));
this.imageShipcallType.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalClient;component/Resources/arrow_right_green.png"));
break;
default:
break;
@ -227,14 +228,14 @@ namespace BreCalClient
switch(this.ShipcallControlModel?.LightMode)
{
case ShipcallControlModel.TrafficLightMode.GREEN:
this.imageEvaluation.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalDevelClient;component/Resources/check.png"));
case EvaluationType.Green:
this.imageEvaluation.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalClient;component/Resources/check.png"));
break;
case ShipcallControlModel.TrafficLightMode.YELLOW:
this.imageEvaluation.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalDevelClient;component/Resources/sign_warning.png"));
case EvaluationType.Yellow:
this.imageEvaluation.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalClient;component/Resources/sign_warning.png"));
break;
case ShipcallControlModel.TrafficLightMode.RED:
this.imageEvaluation.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalDevelClient;component/Resources/delete2.png"));
case EvaluationType.Red:
this.imageEvaluation.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalClient;component/Resources/delete2.png"));
break;
default:
break;
@ -251,22 +252,13 @@ namespace BreCalClient
{
if (this.ShipcallControlModel?.Shipcall?.Evaluation != null)
{
ShipcallControlModel.TrafficLightMode resultColor = (ShipcallControlModel.TrafficLightMode)(this.ShipcallControlModel?.Shipcall?.Evaluation ?? 0); // der nullable Operator hier ist so doof, die VS validation blickts einfach nicht
switch (resultColor)
this.Background = this.ShipcallControlModel.LightMode switch
{
//case ShipcallControlModel.TrafficLightMode.GREEN:
// this.Background = Brushes.LightGreen;
// break;
case ShipcallControlModel.TrafficLightMode.YELLOW:
this.Background = Brushes.LightYellow;
break;
case ShipcallControlModel.TrafficLightMode.RED:
this.Background = new SolidColorBrush(Color.FromArgb(200, 255, 100, 100));
break;
default:
this.Background = Brushes.Transparent;
break;
}
// ShipcallControlModel.TrafficLightMode.GREEN => this.Background = Brushes.LightGreen,
EvaluationType.Yellow => Brushes.LightYellow,
EvaluationType.Red => new SolidColorBrush(Color.FromArgb(200, 255, 100, 100)),
_ => Brushes.Transparent,
};
}
}
@ -303,6 +295,17 @@ namespace BreCalClient
this.labelETAETDTerminal.Content = BreCalClient.Resources.Resources.textOperationsEnd;
}
if((this.ShipcallControlModel?.Shipcall?.Type == ShipcallType.Arrival) && (this.ShipcallControlModel?.Shipcall.TimeRefPoint != null))
{
int timeRefPointIndex = this.ShipcallControlModel?.Shipcall?.TimeRefPoint ?? 0;
this.labelETAETDAgent.Content = BreCalLists.TimeRefs[timeRefPointIndex];
this.labelETAETDMooring.Content = BreCalLists.TimeRefs[timeRefPointIndex];
this.labelETAETDPilot.Content = BreCalLists.TimeRefs[timeRefPointIndex];
this.labelETAETDPortAuthority.Content = BreCalLists.TimeRefs[timeRefPointIndex];
this.labelETAETDTug.Content = BreCalLists.TimeRefs[timeRefPointIndex];
}
if (this.ShipcallControlModel != null)
{
@ -311,8 +314,8 @@ namespace BreCalClient
{
this.labelAgencyBerth.Content = this.ShipcallControlModel?.GetBerthText(agencyTimes);
this.labelAgencyETAETDValue.Content = agencyTimes.DisplayTime(this.ShipcallControlModel?.Shipcall?.Type == ShipcallType.Arrival);
this.textBlockAgencyRemarks.Text = agencyTimes.Remarks;
this.textBlockAgencyBerthRemarks.Text = agencyTimes.BerthInfo;
this.textBlockAgencyRemarks.Text = agencyTimes.Remarks.TruncateDots(50);
this.textBlockAgencyBerthRemarks.Text = agencyTimes.BerthInfo.TruncateDots(50);
this.textBlockDraft.Text = ShipcallControlModel?.Shipcall?.Draft?.ToString("N2");
}
else
@ -325,22 +328,22 @@ namespace BreCalClient
this.textBlockDraft.Text = "";
}
Times? mooringTimes = this.ShipcallControlModel?.GetTimesForParticipantType(Extensions.ParticipantType.MOORING);
Times? mooringTimes = this.ShipcallControlModel?.GetTimesForParticipantType(Extensions.ParticipantType.MOORING);
if (mooringTimes != null)
{
this.labelMooringETAETDValue.Content = mooringTimes.DisplayTime(this.ShipcallControlModel?.Shipcall?.Type == ShipcallType.Arrival);
this.textBlockMooringRemarks.Text = mooringTimes.Remarks;
this.textBlockMooringRemarks.Text = mooringTimes.Remarks.TruncateDots(50);
this.imageMooringLocked.Visibility = (mooringTimes.EtaBerthFixed ?? false) ? Visibility.Visible : Visibility.Hidden;
if(mooringTimes.Ata.HasValue)
{
if(!ataAdded)
{
if(!ataAdded)
{
ataRowDefinition.Height = new GridLength(15);
labelTimesMooringATA.Content = mooringTimes.Ata.ToString();
labelTimesMooringATA.Content = mooringTimes.Ata.Value.ToString("dd.MM.yyyy HH:mm");
ataAdded = true;
}
}
@ -350,7 +353,7 @@ namespace BreCalClient
if (!atdAdded)
{
atdRowDefinition.Height = new GridLength(15);
labelTimesMooringATD.Content = mooringTimes.Atd.ToString();
labelTimesMooringATD.Content = mooringTimes.Atd.Value.ToString("dd.MM.yyyy HH:mm");
atdAdded = true;
}
}
@ -367,8 +370,17 @@ namespace BreCalClient
if (portAuthorityTimes != null)
{
this.labelPortAuthorityETAETDValue.Content = portAuthorityTimes.DisplayTime(this.ShipcallControlModel?.Shipcall?.Type == ShipcallType.Arrival);
this.textBlockPortAuthorityRemarks.Text = portAuthorityTimes.Remarks;
this.textBlockPortAuthorityRemarks.Text = portAuthorityTimes.Remarks.TruncateDots(50);
this.imagePortAuthorityLocked.Visibility = (portAuthorityTimes.EtaBerthFixed ?? false) ? Visibility.Visible : Visibility.Hidden;
if(portAuthorityTimes.LockTime.HasValue)
{
if(!lockTimeAdded)
{
lockTimeRowDefinition.Height = new GridLength(15);
labelPortAuthorityLockTime.Content = portAuthorityTimes.LockTime.Value.ToString("dd.MM.yyyy HH:mm");
lockTimeAdded = true;
}
}
}
else
{
@ -381,7 +393,7 @@ namespace BreCalClient
if (pilotTimes != null)
{
this.labelPilotETAETDValue.Content = pilotTimes.DisplayTime(this.ShipcallControlModel?.Shipcall?.Type == ShipcallType.Arrival);
this.textBlockPilotRemarks.Text = pilotTimes.Remarks;
this.textBlockPilotRemarks.Text = pilotTimes.Remarks.TruncateDots(50);
this.imagePilotLocked.Visibility = (pilotTimes.EtaBerthFixed ?? false) ? Visibility.Visible : Visibility.Hidden;
}
else
@ -395,7 +407,7 @@ namespace BreCalClient
if (tugTimes != null)
{
this.labelTugETAETDValue.Content = tugTimes.DisplayTime(this.ShipcallControlModel?.Shipcall?.Type == ShipcallType.Arrival);
this.textBlockTugRemarks.Text = tugTimes.Remarks;
this.textBlockTugRemarks.Text = tugTimes.Remarks.TruncateDots(50);
this.imageTugLocked.Visibility = (tugTimes.EtaBerthFixed ?? false) ? Visibility.Visible : Visibility.Hidden;
}
else
@ -410,8 +422,8 @@ namespace BreCalClient
{
this.labelTerminalBerth.Content = this.ShipcallControlModel?.GetBerthText(terminalTimes);
this.labelOperationsStart.Content = terminalTimes.DisplayTime(this.ShipcallControlModel?.Shipcall?.Type == ShipcallType.Arrival);
this.textBlockTerminalRemarks.Text = terminalTimes.Remarks;
this.textBlockTerminalBerthRemarks.Text = terminalTimes.BerthInfo;
this.textBlockTerminalRemarks.Text = terminalTimes.Remarks.TruncateDots(40);
this.textBlockTerminalBerthRemarks.Text = terminalTimes.BerthInfo.TruncateDots(40);
}
else
{
@ -433,7 +445,7 @@ namespace BreCalClient
#endregion
#region event handler
#region event handler
private void buttonEditShipcall_Click(object? sender, RoutedEventArgs? e)
{

View File

@ -16,17 +16,7 @@ namespace BreCalClient
public class ShipcallControlModel
{
#region Enumerations
public enum TrafficLightMode
{
OFF,
GREEN,
YELLOW,
RED,
RED_YELLOW,
ALL
};
#region Enumerations
[Flags]
public enum StatusFlags
@ -74,20 +64,20 @@ namespace BreCalClient
}
}
public TrafficLightMode LightMode
public EvaluationType LightMode
{
get
{
TrafficLightMode tlm = TrafficLightMode.OFF;
EvaluationType elm = EvaluationType.Undefined;
if (this.Shipcall != null)
{
if(this.Shipcall.Evaluation.HasValue)
{
tlm = (TrafficLightMode)this.Shipcall.Evaluation;
elm = this.Shipcall.Evaluation.Value;
}
}
return tlm;
return elm;
}
}
@ -182,6 +172,33 @@ namespace BreCalClient
return berthText;
}
public string GetETAETD()
{
DateTime theDate = DateTime.Now;
if(this.Shipcall != null)
{
if (this.Shipcall.Type == ShipcallType.Arrival)
theDate = this.Shipcall.Eta ?? DateTime.Now;
else
theDate = this.Shipcall.Etd ?? DateTime.Now;
}
Times? agentTimes = this.GetTimesForParticipantType(Extensions.ParticipantType.AGENCY);
if(agentTimes != null)
{
if(this.Shipcall?.Type == ShipcallType.Arrival)
{
if (agentTimes.EtaBerth != null)
theDate = agentTimes.EtaBerth.Value;
}
else
{
if (agentTimes.EtdBerth != null)
theDate = agentTimes.EtdBerth.Value;
}
}
return theDate.ToString();
}
/// <summary>
/// After closing the edit shipcall or edit agency dialogs, the times assignments may have changed.

View File

@ -62,7 +62,7 @@ def create_app(test_config=None):
app.register_blueprint(user.bp)
app.register_blueprint(history.bp)
logging.basicConfig(filename='brecaldevel.log', level=logging.DEBUG, format='%(asctime)s | %(name)s | %(levelname)s | %(message)s')
logging.basicConfig(filename='brecal.log', level=logging.DEBUG, format='%(asctime)s | %(name)s | %(levelname)s | %(message)s')
local_db.initPool(os.path.dirname(app.instance_path))
logging.info('App started')

View File

@ -30,6 +30,7 @@ def PostShipcalls():
try:
content = request.get_json(force=True)
logging.info(content)
loadedModel = model.ShipcallSchema().load(data=content, many=False, partial=True)
except Exception as ex:
logging.error(ex)

View File

@ -53,12 +53,16 @@ def PutShip():
@auth_guard() # no restriction by role
def DeleteShip():
# TODO check if I am allowed to delete this thing by deriving the participant from the bearer token
try:
content = request.get_json(force=True)
loadedModel = model.ShipSchema().load(data=content, many=False, partial=True, unknown=EXCLUDE)
if 'id' in request.args:
options = {}
options["id"] = request.args.get("id")
else:
return json.dumps("no id provided"), 400
except Exception as ex:
logging.error(ex)
print(ex)
return json.dumps("bad format"), 400
return impl.ships.DeleteShip(loadedModel)
return impl.ships.DeleteShip(options)

View File

@ -25,13 +25,9 @@ def GetShipcalls(options):
"FROM shipcall s " +
"LEFT JOIN times t ON t.shipcall_id = s.id AND t.participant_type = 8 " +
"WHERE " +
"(type = 1 AND " +
"((t.id IS NOT NULL AND t.eta_berth >= DATE(NOW() - INTERVAL %d DAY)) OR " +
"(eta >= DATE(NOW() - INTERVAL %d DAY)))) OR " +
"((type = 2 OR type = 3) AND " +
"((t.id IS NOT NULL AND t.etd_berth >= DATE(NOW() - INTERVAL %d DAY)) OR " +
"(etd >= DATE(NOW() - INTERVAL %d DAY)))) " +
"ORDER BY eta") % (options["past_days"], options["past_days"], options["past_days"], options["past_days"])
"(type = 1 AND (COALESCE(t.eta_berth, eta) >= DATE(NOW() - INTERVAL %d DAY))) OR " +
"((type = 2 OR type = 3) AND (COALESCE(t.etd_berth, etd) >= DATE(NOW() - INTERVAL %d DAY)))" +
"ORDER BY s.id") % (options["past_days"], options["past_days"])
data = commands.query(query, model=model.Shipcall.from_query_row, buffered=True)
for shipcall in data:
@ -84,9 +80,9 @@ def PostShipcalls(schemaModel):
if key == "evaluation":
continue
if key == "evaluation_message":
continue
continue
if key == "type_value":
continue
continue
if key == "evaluation_value":
continue
if isNotFirst:
@ -96,6 +92,7 @@ def PostShipcalls(schemaModel):
query += ") VALUES ("
isNotFirst = False
for key in schemaModel.keys():
param_key = key
if key == "id":
continue
if key == "participants":
@ -119,9 +116,10 @@ def PostShipcalls(schemaModel):
if isNotFirst:
query += ","
isNotFirst = True
query += "?" + key + "?"
query += "?" + param_key + "?"
query += ")"
logging.info(query)
commands.execute(query, schemaModel)
new_id = commands.execute_scalar("select last_insert_id()")

View File

@ -11,7 +11,7 @@ def initPool(instancePath):
try:
global config_path
if(config_path == None):
config_path = os.path.join(instancePath,'../../../secure/connection_data_devel.json');
config_path = os.path.join(instancePath,'../../../secure/connection_data_prod.json');
print (config_path)
@ -39,4 +39,4 @@ def getPoolConnection():
global config_path
f = open(config_path);
connection_data = json.load(f)
return mysql.connector.connect(**connection_data)
return mysql.connector.connect(**connection_data)

View File

@ -45,10 +45,17 @@ class EvaluationType(IntEnum):
yellow = 2
red = 3
@classmethod
def _missing_(cls, value):
return cls.undefined
class NotificationType(IntEnum):
undefined = 0
email = 1
push = 2
@classmethod
def _missing_(cls, value):
return cls.undefined
class ShipcallType(IntEnum):
undefined = 0
@ -56,6 +63,10 @@ class ShipcallType(IntEnum):
departure = 2
shifting = 3
@classmethod
def _missing_(cls, value):
return cls.undefined
@dataclass
class History:
@ -183,9 +194,13 @@ class ShipcallSchema(Schema):
@post_load
def make_shipcall(self, data, **kwargs):
data['type_value'] = data['type'].value
if 'type' in data:
data['type_value'] = data['type'].value
else:
data['type_value'] = ShipcallType.undefined
if 'evaluation' in data:
data['evaluation_value'] = data['evaluation'].value
if data['evaluation']:
data['evaluation_value'] = data['evaluation'].value
else:
data['evaluation_value'] = EvaluationType.undefined
return data

View File

@ -25,11 +25,15 @@ def UpdateShipcalls(options:dict = {'past_days':2}):
try:
pooledConnection = getPoolConnection()
commands = pydapper.using(pooledConnection)
query = ("SELECT id, ship_id, type, eta, voyage, etd, arrival_berth_id, departure_berth_id, tug_required, pilot_required, "
"flags, pier_side, bunkering, replenishing_terminal, replenishing_lock, draft, tidal_window_from, tidal_window_to, rain_sensitive_cargo, recommended_tugs, "
"anchored, moored_lock, canceled, evaluation, evaluation_message, evaluation_notifications_sent, evaluation_time, created, modified FROM shipcall WHERE ((type = 1 OR type = 3) AND eta >= DATE(NOW() - INTERVAL %d DAY)"
"OR (type = 2 AND etd >= DATE(NOW() - INTERVAL %d DAY))) "
"ORDER BY eta") % (options["past_days"], options["past_days"])
query = ("SELECT s.id as id, ship_id, type, eta, voyage, etd, arrival_berth_id, departure_berth_id, tug_required, pilot_required, "
"flags, s.pier_side, bunkering, replenishing_terminal, replenishing_lock, draft, tidal_window_from, tidal_window_to, rain_sensitive_cargo, recommended_tugs, "
"anchored, moored_lock, canceled, evaluation, evaluation_message, evaluation_notifications_sent, evaluation_time, s.created as created, s.modified as modified, time_ref_point FROM shipcall s " +
"LEFT JOIN times t ON t.shipcall_id = s.id AND t.participant_type = 8 "
"WHERE "
"(type = 1 AND (COALESCE(t.eta_berth, eta) >= DATE(NOW() - INTERVAL %d DAY))) OR "
"((type = 2 OR type = 3) AND (COALESCE(t.etd_berth, etd) >= DATE(NOW() - INTERVAL %d DAY)))"
"ORDER BY s.id") % (options["past_days"], options["past_days"])
# obtain data from the MYSQL database
data = commands.query(query, model=model.Shipcall)
@ -54,6 +58,9 @@ def add_function_to_schedule__update_shipcalls(interval_in_minutes:int, options:
return
def setup_schedule(update_shipcalls_interval_in_minutes:int=60):
logging.getLogger('schedule').setLevel(logging.INFO); # set the logging level of the schedule module to INFO
schedule.clear() # clear all routine jobs. This prevents jobs from being created multiple times
# update the evaluation state in every recent shipcall

View File

@ -57,6 +57,7 @@ class ValidationRuleBaseFunctions():
self.error_message_dict = error_message_dict
# as of 23 dec. 2023 port authority validation is temporarily disabled
self.ignore_port_administration_flag = True # flag to disable all port administration validation rules
self.ignore_terminal_flag = True # flag to disable Terminal validation rules 0001-L & 0001-M
def describe_error_message(self, key)->str:
"""
@ -106,7 +107,7 @@ class ValidationRuleBaseFunctions():
violation_state = (delta<=threshold)
return violation_state
def check_participants_agree_on_estimated_time(self, shipcall, query, df_times, applicable_shipcall_type)->bool:
def check_participants_agree_on_estimated_time(self, shipcall, query, df_times, applicable_shipcall_type, threshold:int=3660)->bool:
"""
# base function for all validation rules in the group {0002} A-C
@ -117,10 +118,12 @@ class ValidationRuleBaseFunctions():
- the shipcall belongs to a different type than the rule expects
- there are no matching times for the provided {query} (e.g., "eta_berth")
Instead of comparing each individual result, this function counts the amount of unique instances.
When there is not only one unique value, there are deviating time estimates, and a violation occurs
This method computes the absolute time difference between all time entries. A threshold (in seconds) is used
to identify, when the time differences are so large, that participants essentially disagree on the times.
This circumvents previous instabilities, which stem from rounding the pd.Timestamp elements.
To reduce the potential of false violations, the agreement is rounded (e.g., by minute).
options:
threshold: integer. Determines the threshold in seconds, when two Timestamps differ 'too much'
returns: violation_state (bool)
"""
@ -136,14 +139,14 @@ class ValidationRuleBaseFunctions():
participant_types = [ParticipantType.AGENCY.value, ParticipantType.MOORING.value, ParticipantType.PILOT.value, ParticipantType.TUG.value]
agency_times = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value,:]
df_times = df_times.loc[df_times["participant_type"].isin(participant_types),:]
agency_time = [time_ for time_ in agency_times.loc[:,query].tolist() if isinstance(time_, pd.Timestamp)]
if not len(agency_time):
if len(agency_times)==0:
violation_state = False
return violation_state
df_times = df_times.loc[df_times["participant_type"].isin(participant_types),:]
agency_time = [time_ for time_ in agency_times.loc[:,query].tolist() if isinstance(time_, pd.Timestamp)]
# for the given query, e.g., 'eta_berth', sample all times from the pandas DataFrame
# exclude missing entries and consider only pd.Timestamp entries (which ignores pd.NaT/null entries)
estimated_times = [time_ for time_ in df_times.loc[:,query].tolist() if isinstance(time_, pd.Timestamp)] # df_times = df_times.loc[~df_times[query].isnull(),:]
@ -151,9 +154,25 @@ class ValidationRuleBaseFunctions():
if len(estimated_times)==0:
violation_state = False
return violation_state
# this (current) solution compares times to the reference (agency) time and checks if the difference is greater than 15 minutes
violation_state = ((np.max(estimated_times) - agency_time[0]) > pd.Timedelta("15min")) or ((agency_time[0] - np.min(estimated_times)) > pd.Timedelta("15min"))
# for the given query, e.g., 'eta_berth', sample all times from the pandas DataFrame
estimated_times = [time_ for time_ in df_times.loc[:,query].tolist() if isinstance(time_, pd.Timestamp)] # consider only pandas Timestamp objects
# measure the time difference between all pairs.
# for each pair of times, the absolute timedifference in seconds (float) is measured
time_absolute_differences = [[abs(time_.to_pydatetime()-time__.to_pydatetime()).total_seconds() for j_, time__ in enumerate(estimated_times) if j_ != i_] for i_, time_ in enumerate(estimated_times)]
# list of lists: for each element in the list, create a boolean that indicates, whether the threshold is exceeded
time_difference_exceeds_threshold = [[time__ > threshold for time__ in time_] for time_ in time_absolute_differences]
# list of booleans for each time entry separately
time_difference_exceeds_threshold = [any(time_) for time_ in time_difference_exceeds_threshold]
# if *any* of these entries exceeds the threshold, the times are too distinct. In those case, a rule violation occurs
violation_state = any(time_difference_exceeds_threshold)
# this (previous) solution compares times to the reference (agency) time and checks if the difference is greater than 15 minutes
# violation_state = ((np.max(estimated_times) - agency_time[0]) > pd.Timedelta("15min")) or ((agency_time[0] - np.min(estimated_times)) > pd.Timedelta("15min"))
# this solution to the rule compares all times to each other. When there is a total difference of more than 15 minutes, a violation occurs
# Consequently, it treats all times as equally important
@ -161,7 +180,7 @@ class ValidationRuleBaseFunctions():
# violation_state = difference > pd.Timedelta("15min")
# this solution clamps the times to 15 minute intervals and compares these values. When there is a single time difference, a violation occurs
# the drawback is that in some cases if there is a minimal difference say of 1 minute (:22 and :23 minutes after the hour) the violation is
# the drawback is that in some cases if there is a minimal difference say of 1 minute (:22 and :23 minutes after the hour) the violation is
# triggered even though the times are very close to each other
# apply rounding. For example, the agreement of different participants may be required to match minute-wise
@ -581,6 +600,9 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
- Checks, if times_terminal.operations_start is filled in.
- Measures the difference between 'now' and 'times_agency.eta_berth'.
"""
if self.ignore_terminal_flag: # this feature flag may disable the validation rule for Terminals
return self.get_no_violation_default_output()
if not shipcall.type in [ShipcallType.INCOMING.value]:
return self.get_no_violation_default_output()
@ -615,6 +637,9 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
- Checks, if times_terminal.operations_end is filled in.
- Measures the difference between 'now' and 'times_agency.etd_berth'.
"""
if self.ignore_terminal_flag: # this feature flag may disable the validation rule for Terminals
return self.get_no_violation_default_output()
if not shipcall.type in [ShipcallType.OUTGOING.value, ShipcallType.SHIFTING.value]:
return self.get_no_violation_default_output()
@ -730,6 +755,9 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
query time: eta_berth (times_agency)
start_time & end_time: operations_start & operations_end (times_terminal)
"""
if self.ignore_terminal_flag: # this feature flag may disable the validation rule for Terminals
return self.get_no_violation_default_output()
if not shipcall.type in [ShipcallType.INCOMING.value]:
return self.get_no_violation_default_output()
@ -770,6 +798,9 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
query time: eta_berth (times_agency)
start_time & end_time: operations_start & operations_end (times_terminal)
"""
if self.ignore_terminal_flag: # this feature flag may disable the validation rule for Terminals
return self.get_no_violation_default_output()
if not shipcall.type in [ShipcallType.OUTGOING.value, ShipcallType.SHIFTING.value]:
return self.get_no_violation_default_output()

View File

@ -1,4 +1,5 @@
import copy
import logging
import re
import numpy as np
import pandas as pd
@ -21,7 +22,7 @@ class ValidationRules(ValidationRuleFunctions):
# currently flagged: notification_state initially was based on using one ValidationRules object for each query. This is deprecated.
# self.notification_state = self.determine_notification_state() # (state:str, should_notify:bool)
return
def evaluate(self, shipcall):
"""
1.) prepare df_times, which every validation rule tends to use
@ -34,7 +35,7 @@ class ValidationRules(ValidationRuleFunctions):
if len(df_times)==0:
return (StatusFlags.GREEN.value, [])
spm = self.sql_handler.df_dict["shipcall_participant_map"]
if len(spm.loc[spm["shipcall_id"]==shipcall.id])==0:
return (StatusFlags.GREEN.value, [])
@ -51,12 +52,14 @@ class ValidationRules(ValidationRuleFunctions):
# 'translate' all error codes into readable, human-understandable format.
evaluation_results = [(state, self.describe_error_message(msg)) for (state, msg) in evaluation_results]
logging.info(f"Validation results for shipcall {shipcall.id}: {evaluation_results}")
# check, what the maximum state flag is and return it
evaluation_state = np.max(np.array([result[0].value for result in evaluation_results])) if len(evaluation_results)>0 else StatusFlags.GREEN.value
evaluation_verbosity = [result[1] for result in evaluation_results]
return (evaluation_state, evaluation_verbosity)
def evaluation_verbosity(self, evaluation_state, evaluation_results):
"""This function suggestions verbosity for the evaluation results. Based on 'True'/'False' evaluation outcome, the returned string is different."""
if evaluation_state:
@ -64,17 +67,17 @@ class ValidationRules(ValidationRuleFunctions):
else:
verbose_string = "These are:" + "\n\t".join(evaluation_results) # every element of the list will be displayed in a new line with a tab
return f"FAILED VALIDATION. There have been {len(evaluation_results)} violations. {verbose_string}"
def evaluate_shipcall_from_df(self, x):
shipcall = Shipcall(**{**{'id':x.name}, **x.to_dict()})
evaluation_state, violations = self.evaluate(shipcall)
return evaluation_state, violations
def evaluate_shipcalls(self, shipcall_df:pd.DataFrame)->pd.DataFrame:
"""apply 'evaluate_shipcall_from_df' to each individual shipcall in {shipcall_df}. Returns shipcall_df ('evaluation' and 'evaluation_message' are updated)"""
results = shipcall_df.apply(lambda x: self.evaluate_shipcall_from_df(x), axis=1).values
# unbundle individual results. evaluation_state becomes an integer, violation
# unbundle individual results. evaluation_state becomes an integer, violation
evaluation_state = [StatusFlags(res[0]).value for res in results]
violations = [",\r\n".join(res[1]) if len(res[1])>0 else None for res in results]
violations = [self.concise_evaluation_message_if_too_long(violation) for violation in violations]
@ -90,31 +93,31 @@ class ValidationRules(ValidationRuleFunctions):
"""
if violation is None:
return violation
if len(violation)>=512:
concise = re.findall(r'{(.*?)\}', violation)
# e.g.: Evaluation message too long. Violated Rules: ['Rule #0001C', 'Rule #0001H', 'Rule #0001F', 'Rule #0001G', 'Rule #0001L', 'Rule #0001M', 'Rule #0001J', 'Rule #0001K']
violation = f"Evaluation message too long. Violated Rules: {concise}"
return violation
def determine_validation_state(self) -> str:
"""
this method determines the validation state of a shipcall. The state is either ['green', 'yellow', 'red'] and signals,
whether an entry causes issues within the workflow of users.
whether an entry causes issues within the workflow of users.
returns: validation_state_new (str)
"""
(validation_state_new, description) = self.undefined_method()
# should there also be notifications for critical validation states? In principle, the traffic light itself provides that notification.
# should there also be notifications for critical validation states? In principle, the traffic light itself provides that notification.
self.validation_state = validation_state_new
return validation_state_new
def determine_notification_state(self) -> (str, bool):
"""
this method determines state changes in the notification state. When the state is changed to yellow or red,
a user is notified about it. The only exception for this rule is when the state was yellow or red before,
as the user has then already been notified.
as the user has then already been notified.
returns: notification_state_new (str), should_notify (bool)
"""
@ -122,10 +125,10 @@ class ValidationRules(ValidationRuleFunctions):
should_notify = self.identify_notification_state_change(state_new)
self.notification_state = state_new # overwrite the predecessor
return state_new, should_notify
def identify_notification_state_change(self, state_new) -> bool:
"""
determines, whether the observed state change should trigger a notification.
determines, whether the observed state change should trigger a notification.
internally, this function maps a color string to an integer and determines, if the successor state is more severe than the predecessor.
state changes trigger a notification in the following cases:
@ -135,14 +138,14 @@ class ValidationRules(ValidationRuleFunctions):
(none -> yellow) or (none -> red)
due to the values in the enumeration objects, the states are mapped to provide this function.
green=1, yellow=2, red=3, none=1. Hence, critical changes can be observed by simply checking with "greater than".
green=1, yellow=2, red=3, none=1. Hence, critical changes can be observed by simply checking with "greater than".
returns bool, whether a notification should be triggered
"""
# state_old is always considered at least 'Green' (1)
state_old = max(copy.copy(self.notification_state) if "notification_state" in list(self.__dict__.keys()) else StatusFlags.NONE, StatusFlags.GREEN.value)
return state_new.value > state_old.value
def undefined_method(self) -> str:
"""this function should apply the ValidationRules to the respective .shipcall, in regards to .times"""
# #TODO_traffic_state

View File

@ -2,7 +2,7 @@ import os
import sys
import logging
sys.path.insert(0, '/var/www/brecal_devel/src/server')
sys.path.insert(0, '/var/www/brecal/src/server')
sys.path.insert(0, '/var/www/venv/lib/python3.10/site-packages/')
import schedule