Merge branch 'release/1.2.0'

This commit is contained in:
Daniel Schick 2024-05-06 13:38:39 +02:00
commit 35765b97b1
79 changed files with 7762 additions and 4235 deletions

6
.vscode/launch.json vendored
View File

@ -15,9 +15,9 @@
"SECRET_KEY" : "zdiTz8P3jXOc7jztIQAoelK4zztyuCpJ" // https://randomkeygen.com/ "SECRET_KEY" : "zdiTz8P3jXOc7jztIQAoelK4zztyuCpJ" // https://randomkeygen.com/
}, },
"args": [ "args": [
"run" //, "run",
// "--no-debugger", // "--no-debugger",
//"--no-reload" "--no-reload"
], ],
"jinja": true, "jinja": true,
"justMyCode": true "justMyCode": true

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,95 @@
-- add notification handling columns to shipcall
-- 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_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`;
-- prepare notification table for historic notification data
-- removed reference to participant and times and dropped unnecessary columns
-- added reference to shipcall
ALTER TABLE `bremen_calling_test`.`notification`
DROP FOREIGN KEY `FK_NOT_TIMES`,
DROP FOREIGN KEY `FK_NOT_PART`;
ALTER TABLE `bremen_calling_test`.`notification`
DROP COLUMN `deleted`,
DROP COLUMN `acknowledged`,
DROP COLUMN `participant_id`,
DROP COLUMN `times_id`,
ADD COLUMN `shipcall_id` INT UNSIGNED NULL AFTER `id`,
ADD INDEX `FK_NOTIFICATION_SHIPCALL_idx` (`shipcall_id` ASC) VISIBLE,
DROP INDEX `FK_NOT_PART` ,
DROP INDEX `FK_NOT_TIMES` ;
;
ALTER TABLE `bremen_calling_test`.`notification`
ADD CONSTRAINT `FK_NOTIFICATION_SHIPCALL`
FOREIGN KEY (`shipcall_id`)
REFERENCES `bremen_calling_test`.`shipcall` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION;
-- added notification flags
-- participant reference is now mandatory
ALTER TABLE `bremen_calling_test`.`user`
DROP FOREIGN KEY `FK_USER_PART`;
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_test`.`user`
ADD CONSTRAINT `FK_USER_PART`
FOREIGN KEY (`participant_id`)
REFERENCES `bremen_calling_test`.`participant` (`id`);
-- History table for change tracking
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,
`shipcall_id` INT UNSIGNED NOT NULL,
`timestamp` DATETIME NOT NULL COMMENT 'Time of saving',
`eta` DATETIME NULL COMMENT 'Current ETA / ETD value (depends if shipcall or times were saved)',
`type` INT NOT NULL COMMENT 'shipcall or times',
`operation` INT NOT NULL COMMENT 'insert, update or delete',
PRIMARY KEY (`id`))
COMMENT = 'This table stores a history of changes made to shipcalls so that everyone can see who changed what and when';
-- and foreign keys
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_test`.`history`
ADD CONSTRAINT `FK_HISTORY_PARTICIPANT`
FOREIGN KEY (`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_test`.`shipcall` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
ADD CONSTRAINT `FK_HISTORY_USER`
FOREIGN KEY (`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_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 `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.1.6.0 1.2.0.0

View File

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

View File

@ -47,6 +47,24 @@
<setting name="W1Top" serializeAs="String"> <setting name="W1Top" serializeAs="String">
<value>0</value> <value>0</value>
</setting> </setting>
<setting name="W2Left" serializeAs="String">
<value>0</value>
</setting>
<setting name="W2Top" serializeAs="String">
<value>0</value>
</setting>
<setting name="W3Left" serializeAs="String">
<value>0</value>
</setting>
<setting name="W3Top" serializeAs="String">
<value>0</value>
</setting>
<setting name="W4Left" serializeAs="String">
<value>0</value>
</setting>
<setting name="W4Top" serializeAs="String">
<value>0</value>
</setting>
</BreCalClient.Properties.Settings> </BreCalClient.Properties.Settings>
</userSettings> </userSettings>
</configuration> </configuration>

View File

@ -2,16 +2,19 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BreCalClient" xmlns:local="clr-namespace:BreCalClient"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
StartupUri="MainWindow.xaml" Exit="Application_Exit" Startup="Application_Startup" > StartupUri="MainWindow.xaml" Exit="Application_Exit" Startup="Application_Startup" >
<Application.Resources> <Application.Resources>
<ResourceDictionary> <ResourceDictionary>
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources\StringResources.xaml"/> <ResourceDictionary Source="Resources\StringResources.xaml"/>
</ResourceDictionary.MergedDictionaries> </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> </ResourceDictionary>
</Application.Resources> </Application.Resources>
</Application> </Application>

View File

@ -8,8 +8,8 @@
<SignAssembly>True</SignAssembly> <SignAssembly>True</SignAssembly>
<StartupObject>BreCalClient.App</StartupObject> <StartupObject>BreCalClient.App</StartupObject>
<AssemblyOriginatorKeyFile>..\..\misc\brecal.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>..\..\misc\brecal.snk</AssemblyOriginatorKeyFile>
<AssemblyVersion>1.1.6.0</AssemblyVersion> <AssemblyVersion>1.2.0.10</AssemblyVersion>
<FileVersion>1.1.6.0</FileVersion> <FileVersion>1.2.0.0</FileVersion>
<Title>Bremen calling client</Title> <Title>Bremen calling client</Title>
<Description>A Windows WPF client for the Bremen calling API.</Description> <Description>A Windows WPF client for the Bremen calling API.</Description>
<ApplicationIcon>containership.ico</ApplicationIcon> <ApplicationIcon>containership.ico</ApplicationIcon>
@ -17,6 +17,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Remove="Resources\add.png" />
<None Remove="Resources\arrow_down_green.png" /> <None Remove="Resources\arrow_down_green.png" />
<None Remove="Resources\arrow_down_red.png" /> <None Remove="Resources\arrow_down_red.png" />
<None Remove="Resources\arrow_right_blue.png" /> <None Remove="Resources\arrow_right_blue.png" />
@ -31,8 +32,12 @@
<None Remove="Resources\containership.png" /> <None Remove="Resources\containership.png" />
<None Remove="Resources\delete.png" /> <None Remove="Resources\delete.png" />
<None Remove="Resources\delete2.png" /> <None Remove="Resources\delete2.png" />
<None Remove="Resources\edit.png" />
<None Remove="Resources\emergency_stop_button.png" /> <None Remove="Resources\emergency_stop_button.png" />
<None Remove="Resources\lock.png" />
<None Remove="Resources\lock_open.png" />
<None Remove="Resources\logo_bremen_calling.png" /> <None Remove="Resources\logo_bremen_calling.png" />
<None Remove="Resources\nav_refresh_green.png" />
<None Remove="Resources\ship2.png" /> <None Remove="Resources\ship2.png" />
<None Remove="Resources\sign_warning.png" /> <None Remove="Resources\sign_warning.png" />
<None Remove="Resources\trafficlight_green.png" /> <None Remove="Resources\trafficlight_green.png" />
@ -68,6 +73,7 @@
<Generator>OpenApiCodeGenerator</Generator> <Generator>OpenApiCodeGenerator</Generator>
<LastGenOutput>BreCalApi.cs</LastGenOutput> <LastGenOutput>BreCalApi.cs</LastGenOutput>
</None> </None>
<Resource Include="Resources\add.png" />
<Resource Include="Resources\arrow_down_green.png" /> <Resource Include="Resources\arrow_down_green.png" />
<Resource Include="Resources\arrow_down_red.png" /> <Resource Include="Resources\arrow_down_red.png" />
<Resource Include="Resources\arrow_right_blue.png" /> <Resource Include="Resources\arrow_right_blue.png" />
@ -82,8 +88,12 @@
<Resource Include="Resources\containership.png" /> <Resource Include="Resources\containership.png" />
<Resource Include="Resources\delete.png" /> <Resource Include="Resources\delete.png" />
<Resource Include="Resources\delete2.png" /> <Resource Include="Resources\delete2.png" />
<Resource Include="Resources\edit.png" />
<Resource Include="Resources\emergency_stop_button.png" /> <Resource Include="Resources\emergency_stop_button.png" />
<Resource Include="Resources\lock.png" />
<Resource Include="Resources\lock_open.png" />
<Resource Include="Resources\logo_bremen_calling.png" /> <Resource Include="Resources\logo_bremen_calling.png" />
<Resource Include="Resources\nav_refresh_green.png" />
<Resource Include="Resources\ship2.png" /> <Resource Include="Resources\ship2.png" />
<Resource Include="Resources\sign_warning.png" /> <Resource Include="Resources\sign_warning.png" />
<Resource Include="Resources\StringResources.de.xaml"> <Resource Include="Resources\StringResources.de.xaml">
@ -102,11 +112,11 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.5.1" /> <PackageReference Include="Extended.Wpf.Toolkit" Version="4.6.0" />
<PackageReference Include="JsonSubTypes" Version="2.0.1" /> <PackageReference Include="JsonSubTypes" Version="2.0.1" />
<PackageReference Include="log4net" Version="2.0.15" /> <PackageReference Include="log4net" Version="2.0.17" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Polly" Version="7.2.4" /> <PackageReference Include="Polly" Version="8.3.1" />
<PackageReference Include="RestSharp" Version="110.2.0" /> <PackageReference Include="RestSharp" Version="110.2.0" />
</ItemGroup> </ItemGroup>

View File

@ -1,12 +1,12 @@
extensions: designer.cs generated.cs extensions: designer.cs generated.cs
extensions: .cs .cpp .h extensions: .cs .cpp .h
// Copyright (c) 2023 schick Informatik // Copyright (c) 2024- schick Informatik
// Description: // Description:
// //
extensions: .aspx .ascx extensions: .aspx .ascx
<%-- <%--
Copyright (c) 2023 schick Informatik Copyright (c) 2024- schick Informatik
--%> --%>
extensions: .vb extensions: .vb
'Sample license text. 'Sample license text.

View File

@ -1,6 +1,6 @@
// Copyright (c) 2023 schick Informatik // Copyright (c) 2023 schick Informatik
// Description: Static lists used everywhere // Description: Static lists used everywhere
// //
using BreCalClient.misc.Model; using BreCalClient.misc.Model;
using System.Collections.Concurrent; using System.Collections.Concurrent;
@ -30,6 +30,17 @@ namespace BreCalClient
private readonly static ConcurrentDictionary<int, Berth> _berthLookupDict = new(); private readonly static ConcurrentDictionary<int, Berth> _berthLookupDict = new();
private readonly static Dictionary<int, Participant> _participantLookupDict = 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>
{
"ETB",
"Geeste",
"TN-Weser"
};
#endregion #endregion
#region Properties #region Properties
@ -90,6 +101,11 @@ namespace BreCalClient
/// </summary> /// </summary>
public static List<ShipModel> AllShips { get { return _allShips; } } public static List<ShipModel> AllShips { get { return _allShips; } }
/// <summary>
/// List of display values for TimeRef points
/// </summary>
public static List<string> TimeRefs { get { return _timeRefs; } }
#endregion #endregion
#region methods #region methods
@ -101,7 +117,7 @@ namespace BreCalClient
aList.Clear(); aList.Clear();
mList.Clear(); mList.Clear();
pList.Clear(); pList.Clear();
tList.Clear(); tList.Clear();
terList.Clear(); terList.Clear();
foreach (Participant p in participants) foreach (Participant p in participants)
@ -135,7 +151,7 @@ namespace BreCalClient
if (!ship.Deleted) if (!ship.Deleted)
_ships.Add(sm); _ships.Add(sm);
_allShips.Add(sm); _allShips.Add(sm);
} }
} }
#endregion #endregion

View File

@ -0,0 +1,257 @@
// Copyright (c) 2017 schick Informatik
// Description: DataGrid mit etwas "verbesserten" Funktionen
//
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Controls.Primitives;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
namespace BreCalClient
{
/// <summary>
/// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
///
/// Step 1a) Using this custom control in a XAML file that exists in the current project.
/// Add this XmlNamespace attribute to the root element of the markup file where it is
/// to be used:
///
/// xmlns:enictrl="clr-namespace:ENI2.Controls"
///
///
/// Step 1b) Using this custom control in a XAML file that exists in a different project.
/// Add this XmlNamespace attribute to the root element of the markup file where it is
/// to be used:
///
/// xmlns:enictrl="clr-namespace:ENI2.Controls;assembly=ENI2.Controls"
///
/// You will also need to add a project reference from the project where the XAML file lives
/// to this project and Rebuild to avoid compilation errors:
///
/// Right click on the target project in the Solution Explorer and
/// "Add Reference"->"Projects"->[Browse to and select this project]
///
///
/// Step 2)
/// Go ahead and use your control in the XAML file.
///
/// <MyNamespace:ENIDataGrid/>
///
/// </summary>
public class ENIDataGrid : DataGrid
{
public event Action<object>? EditRequested;
public event Action<object>? DeleteRequested;
public event Action? CreateRequested;
public event Action? RefreshGrid;
public event Action<object>? PrintRequested;
public event Action<object>? ExportRequested;
public event Action<object>? ShowTextRequested;
public void Initialize()
{
this.MouseDoubleClick += dataGrid_MouseDoubleClick;
this.PreviewKeyDown += ENIDataGrid_PreviewKeyDown;
this.ContextMenu = new ContextMenu();
this.CanUserAddRows = false;
this.IsReadOnly = false;
MenuItem addItem = new MenuItem();
addItem.Header = BreCalClient.Resources.Resources.textAdd;
addItem.Icon = new Image { Source = Util.LoadImage(BreCalClient.Resources.Resources.add) };
addItem.Click += new RoutedEventHandler(this.addItem);
this.ContextMenu.Items.Add(addItem);
MenuItem deleteItem = new MenuItem();
deleteItem.Header = BreCalClient.Resources.Resources.textDelete;
deleteItem.Icon = new Image { Source = Util.LoadImage(BreCalClient.Resources.Resources.delete) };
deleteItem.Click += this.deleteItem;
this.ContextMenu.Items.Add(deleteItem);
MenuItem editItem = new MenuItem();
editItem.Header = BreCalClient.Resources.Resources.textEdit;
editItem.Icon = new Image { Source = Util.LoadImage(BreCalClient.Resources.Resources.edit) };
editItem.Click += this.editItem;
this.ContextMenu.Items.Add(editItem);
}
private void ENIDataGrid_PreviewKeyDown(object sender, KeyEventArgs e)
{
if(sender is ENIDataGrid)
{
var grid = sender as ENIDataGrid;
if (Key.Delete == e.Key)
this.deleteItem(null, null);
}
}
#region public
public DataGridRow GetRow(int index)
{
DataGridRow row = (DataGridRow)this.ItemContainerGenerator.ContainerFromIndex(index);
if(row == null)
{
this.UpdateLayout();
this.ScrollIntoView(this.Items[index]);
row = (DataGridRow)this.ItemContainerGenerator.ContainerFromIndex(index);
}
return row;
}
public DataGridCell? GetCell(DataGridRow row, int column)
{
if (row != null)
{
DataGridCellsPresenter? presenter = GetVisualChild<DataGridCellsPresenter>(row);
if (presenter == null)
{
this.ScrollIntoView(row, this.Columns[column]);
presenter = GetVisualChild<DataGridCellsPresenter>(row);
}
DataGridCell? cell = (DataGridCell?)presenter?.ItemContainerGenerator.ContainerFromIndex(column);
return cell;
}
return null;
}
public DataGridCell? GetCell(int rowIndex, int columnIndex)
{
DataGridRow row = this.GetRow(rowIndex);
return this.GetCell(row, columnIndex);
}
#endregion
#region protected
protected void addItem(object sender, RoutedEventArgs e)
{
if (!this.IsReadOnly)
{
this.CreateRequested?.Invoke();
}
e.Handled = true;
}
protected void deleteItem(object? sender, RoutedEventArgs? e)
{
if((this.SelectedItems != null) && (this.SelectedItems.Count > 0) && !this.IsReadOnly)
{
MessageBoxResult result = MessageBox.Show("Are your sure?", "Please confirm", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
List<object> deleteList = new List<object>();
foreach (object deleteItem in this.SelectedItems)
deleteList.Add(deleteItem);
foreach (object deleteItem in deleteList)
{
if (deleteItem != null)
{
this.DeleteRequested?.Invoke(deleteItem);
}
}
this.RefreshGrid?.Invoke();
}
}
}
protected void editItem(object sender, RoutedEventArgs e)
{
if((this.SelectedItems != null) && (this.SelectedItems.Count == 1) && !this.IsReadOnly)
{
if (this.SelectedItems[0] is object selectedEntity)
this.EditRequested?.Invoke(selectedEntity);
}
}
protected void printItem(object sender, RoutedEventArgs e)
{
if ((this.SelectedItems != null) && (this.SelectedItems.Count == 1) )
{
if (this.SelectedItems[0] is object selectedEntity)
this.PrintRequested?.Invoke(selectedEntity);
}
}
protected void exportItem(object sender, RoutedEventArgs e)
{
if ((this.SelectedItems != null) && (this.SelectedItems.Count == 1))
{
if (this.SelectedItems[0] is object selectedEntity)
this.ExportRequested?.Invoke(selectedEntity);
}
}
protected void showTextItem(object sender, RoutedEventArgs e)
{
if ((this.SelectedItems != null) && (this.SelectedItems.Count == 1))
{
if (this.SelectedItems[0] is object selectedEntity)
this.ShowTextRequested?.Invoke(selectedEntity);
}
}
#endregion
#region private
private void dataGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (sender != null)
{
if ((sender is DataGrid grid) && (grid.SelectedItems != null) && (grid.SelectedItems.Count == 1) && !this.IsReadOnly)
{
DataGridRow? dgr = grid.ItemContainerGenerator.ContainerFromItem(grid.SelectedItem) as DataGridRow;
if (grid.SelectedItem is object selectedEntity)
this.EditRequested?.Invoke(selectedEntity);
}
}
}
#endregion
#region private static
private static T? GetVisualChild<T>(Visual parent) where T : Visual
{
T? child = default;
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual? v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
#endregion
}
}

View File

@ -0,0 +1,54 @@
<Window x:Class="BreCalClient.EditShipDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
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:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d"
Left="{local:SettingBinding W3Left}" Top="{local:SettingBinding W3Top}"
Title="{x:Static p:Resources.textEditShip}" Height="250" Width="500" Loaded="Window_Loaded" ResizeMode="NoResize">
<Grid x:Name="shipGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".3*" />
<ColumnDefinition Width=".6*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="*" />
<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}" 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>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="28" />
</Grid.ColumnDefinitions>
<ComboBox x:Name="comboBoxParticipants" Margin="2" DisplayMemberPath="Name" />
<Button x:Name="buttonResetParticipant" Grid.Column="1" Margin="2" Click="buttonResetParticipant_Click">
<Image Source="./Resources/delete2.png"/>
</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" 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}" 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" />
<xctk:DoubleUpDown Name="doubleUpDownWidth" Grid.Row="5" Grid.Column="1" Value="{Binding Width, Mode=OneWay}" Margin="2" Minimum="0"/>
<StackPanel Grid.Column="1" Grid.Row="7" Orientation="Horizontal" FlowDirection="RightToLeft">
<Button x:Name="buttonCancel" Width="80" Content="{x:Static p:Resources.textCancel}" Margin="2" Click="buttonCancel_Click" />
<Button x:Name="buttonOK" Width="80" Content="OK" Margin="2" Click="buttonOK_Click"/>
</StackPanel>
</Grid>
</Window>

View File

@ -0,0 +1,87 @@
using BreCalClient.misc.Model;
using System.Collections.Generic;
using System.Windows;
namespace BreCalClient
{
/// <summary>
/// Interaction logic for EditShipDialog.xaml
/// </summary>
public partial class EditShipDialog : Window
{
public EditShipDialog()
{
InitializeComponent();
}
public Ship Ship { get; set; } = new();
public List<Participant> Participants { get; } = new List<Participant>();
private void buttonCancel_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = false;
this.Close();
}
private void buttonOK_Click(object sender, RoutedEventArgs e)
{
this.Ship.Name = this.textBoxName.Text.ToUpper().Trim();
if (this.comboBoxParticipants.SelectedItem != null)
{
this.Ship.ParticipantId = ((Participant)this.comboBoxParticipants.SelectedItem).Id;
this.Ship.IsTug = true;
}
else
{
this.Ship.IsTug = false;
}
this.Ship.Imo = this.integerUpDownIMO.Value;
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;
this.Close();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = this.Ship;
this.comboBoxParticipants.ItemsSource = this.Participants;
if(this.Ship.ParticipantId != null)
{
foreach(Participant p in this.Participants)
{
if(this.Ship.ParticipantId == p.Id)
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:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BreCalClient" xmlns:local="clr-namespace:BreCalClient"
xmlns:p = "clr-namespace:BreCalClient.Resources" xmlns:p = "clr-namespace:BreCalClient.Resources"
xmlns:db="clr-namespace:BreCalClient;assembly=BreCalClient" xmlns:api="clr-namespace:BreCalClient.misc.Model"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d" Left="{local:SettingBinding W1Left}" Top="{local:SettingBinding W1Top}" mc:Ignorable="d" Left="{local:SettingBinding W1Left}" Top="{local:SettingBinding W1Top}"
Title="{x:Static p:Resources.textEditShipcall}" Height="242" Width="800" Loaded="Window_Loaded" ResizeMode="NoResize" Icon="Resources/containership.ico"> Title="{x:Static p:Resources.textEditShipcall}" Height="270" Width="800" Loaded="Window_Loaded" ResizeMode="NoResize" Icon="Resources/containership.ico">
<Window.Resources> <Window.Resources>
<local:BoolToIndexConverter x:Key="boolToIndexConverter" /> <local:BoolToIndexConverter x:Key="boolToIndexConverter" />
<local:EnumToStringConverter x:Key="enumToStringConverter" />
</Window.Resources> </Window.Resources>
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
@ -27,26 +28,26 @@
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="28" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Label Content="{x:Static p:Resources.textShip}" Grid.Column="0" Grid.Row="0" HorizontalContentAlignment="Right"/> <Label Content="{x:Static p:Resources.textShip}" Grid.Column="0" Grid.Row="0" HorizontalContentAlignment="Right"/>
<ComboBox x:Name="comboBoxShip" Margin="2" Grid.Column="1" Grid.Row="0" SelectionChanged="comboBoxShip_SelectionChanged" SelectedValuePath="Ship.Id" IsEditable="True" IsTextSearchEnabled="True" IsTextSearchCaseSensitive="False" /> <ComboBox x:Name="comboBoxShip" Margin="2" Grid.Column="1" Grid.Row="0" SelectionChanged="comboBoxShip_SelectionChanged" SelectedValuePath="Ship.Id" IsEditable="True" IsTextSearchEnabled="True" IsTextSearchCaseSensitive="False" />
<Label Content="IMO" Grid.Column="0" Grid.Row="1" HorizontalContentAlignment="Right"/> <Label Content="IMO" Grid.Column="0" Grid.Row="1" HorizontalContentAlignment="Right"/>
<xctk:IntegerUpDown x:Name="integerUpDownIMO" IsReadOnly="True" Margin="2" Grid.Column="1" Grid.Row="1" ShowButtonSpinner="False"/> <xctk:IntegerUpDown x:Name="integerUpDownIMO" IsReadOnly="True" Margin="2" Grid.Column="1" Grid.Row="1" ShowButtonSpinner="False" IsEnabled="False"/>
<Label Content="{x:Static p:Resources.textCallsign}" Grid.Column="0" Grid.Row="2" HorizontalContentAlignment="Right"/> <Label Content="{x:Static p:Resources.textCallsign}" Grid.Column="0" Grid.Row="2" HorizontalContentAlignment="Right"/>
<TextBox x:Name="textBoxCallsign" IsReadOnly="True" Grid.Column="1" Grid.Row="2" Margin="2" /> <TextBox x:Name="textBoxCallsign" IsReadOnly="True" Grid.Column="1" Grid.Row="2" Margin="2" IsEnabled="False"/>
<Label Content="{x:Static p:Resources.textLength}" Grid.Column="0" Grid.Row="3" HorizontalContentAlignment="Right"/> <Label Content="{x:Static p:Resources.textLength}" Grid.Column="0" Grid.Row="3" HorizontalContentAlignment="Right"/>
<xctk:DoubleUpDown x:Name="doubleUpDownLength" Margin="2" Grid.Column="1" Grid.Row="3" FormatString="N2" IsReadOnly="True" /> <xctk:DoubleUpDown x:Name="doubleUpDownLength" Margin="2" Grid.Column="1" Grid.Row="3" FormatString="N2" IsReadOnly="True" IsEnabled="False" ShowButtonSpinner="False"/>
<Label Content="{x:Static p:Resources.textWidth}" Grid.Column="0" Grid.Row="4" HorizontalContentAlignment="Right"/> <Label Content="{x:Static p:Resources.textWidth}" Grid.Column="0" Grid.Row="4" HorizontalContentAlignment="Right"/>
<xctk:DoubleUpDown x:Name="doubleUpDownWidth" Margin="2" Grid.Column="1" Grid.Row="4" FormatString="N2" IsReadOnly="True" /> <xctk:DoubleUpDown x:Name="doubleUpDownWidth" Margin="2" Grid.Column="1" Grid.Row="4" FormatString="N2" IsReadOnly="True" IsEnabled="False" ShowButtonSpinner="False"/>
<Label Content="{x:Static p:Resources.textCancelled}" Grid.Column="0" Grid.Row="5" HorizontalContentAlignment="Right" /> <Label Content="{x:Static p:Resources.textCancelled}" Grid.Column="0" Grid.Row="5" HorizontalContentAlignment="Right" />
<CheckBox x:Name="checkBoxCancelled" Grid.Column="1" Grid.Row="5" Margin="2" VerticalContentAlignment="Center" /> <CheckBox x:Name="checkBoxCancelled" Grid.Column="1" Grid.Row="5" Margin="2" VerticalContentAlignment="Center" />
<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" /> <Label Content="{x:Static p:Resources.textType}" Grid.Column="2" Grid.Row="0" HorizontalContentAlignment="Right" />
<Label Content="ETA" Grid.Column="2" Grid.Row="2" HorizontalContentAlignment="Right"/>
<Label Content="ETD" Grid.Column="2" Grid.Row="3" HorizontalContentAlignment="Right"/> <ComboBox ItemsSource="{local:Enumerate {x:Type api:ShipcallType}}" Grid.Column="3" Margin="2" Grid.Row="0" SelectionChanged="comboBoxCategories_SelectionChanged" x:Name="comboBoxCategories" />
<ComboBox x:Name="comboBoxCategories" Grid.Column="3" Margin="2" Grid.Row="0" SelectionChanged="comboBoxCategories_SelectionChanged"/>
<Label Content="{x:Static p:Resources.textBerth}" Grid.Column="2" Grid.Row="1" HorizontalContentAlignment="Right"/> <Label Content="{x:Static p:Resources.textBerth}" Grid.Column="2" Grid.Row="1" HorizontalContentAlignment="Right"/>
<Grid Grid.Row="1" Grid.Column="3"> <Grid Grid.Row="1" Grid.Column="3">
@ -60,12 +61,18 @@
</Grid> </Grid>
<xctk:DateTimePicker x:Name="datePickerETA" Grid.Column="3" Grid.Row="2" Margin="2" Format="Custom" FormatString="dd.MM. yyyy HH:mm" IsEnabled="False" ValueChanged="datePickerETA_ValueChanged"/>
<xctk:DateTimePicker x:Name="datePickerETD" Grid.Column="3" Grid.Row="3" Margin="2" Format="Custom" FormatString="dd.MM. yyyy HH:mm" IsEnabled="False" ValueChanged="datePickerETD_ValueChanged"/>
<Label Content="{x:Static p:Resources.textAgency}" Grid.Column="2" Grid.Row="4" HorizontalContentAlignment="Right"/> <Label Content="Zeit Ref." Grid.Column="2" Grid.Row="2" HorizontalContentAlignment="Right" />
<ComboBox Name="comboBoxAgency" Grid.Column="3" Grid.Row="4" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id" SelectionChanged="comboBoxAgency_SelectionChanged">
<Label Content="ETA" Grid.Column="2" Grid.Row="3" HorizontalContentAlignment="Right" Margin="0,2,0,26" Grid.RowSpan="2"/>
<Label Content="ETD" Grid.Column="2" Grid.Row="4" HorizontalContentAlignment="Right"/>
<ComboBox x:Name="comboBoxTimeRef" Grid.Column="3" Margin="2" Grid.Row="2" />
<xctk:DateTimePicker x:Name="datePickerETA" Grid.Column="3" Grid.Row="3" Margin="2" Format="Custom" FormatString="dd.MM. yyyy HH:mm" IsEnabled="False" ValueChanged="datePickerETA_ValueChanged"/>
<xctk:DateTimePicker x:Name="datePickerETD" Grid.Column="3" Grid.Row="4" Margin="2" Format="Custom" FormatString="dd.MM. yyyy HH:mm" IsEnabled="False" ValueChanged="datePickerETD_ValueChanged"/>
<Label Content="{x:Static p:Resources.textAgency}" Grid.Column="2" Grid.Row="5" HorizontalContentAlignment="Right"/>
<ComboBox Name="comboBoxAgency" Grid.Column="3" Grid.Row="5" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id" SelectionChanged="comboBoxAgency_SelectionChanged">
<ComboBox.ContextMenu> <ComboBox.ContextMenu>
<ContextMenu> <ContextMenu>
<MenuItem Header="{x:Static p:Resources.textClearAssignment}" Name="contextMenuItemClearAgency" Click="contextMenuItemClearAgency_Click" /> <MenuItem Header="{x:Static p:Resources.textClearAssignment}" Name="contextMenuItemClearAgency" Click="contextMenuItemClearAgency_Click" />
@ -73,8 +80,8 @@
</ComboBox.ContextMenu> </ComboBox.ContextMenu>
</ComboBox> </ComboBox>
<Label x:Name="labelBSMDGranted" Grid.Row="6" Grid.Column="1" Grid.ColumnSpan="2" Content="{x:Static p:Resources.textBSMDGranted}" Visibility="Hidden" FontWeight="DemiBold" /> <Label x:Name="labelBSMDGranted" Grid.Row="6" Grid.Column="3" Grid.ColumnSpan="1" Content="{x:Static p:Resources.textBSMDGranted}" Visibility="Hidden" FontWeight="DemiBold" />
<StackPanel Grid.Row="6" Grid.Column="3" Orientation="Horizontal" HorizontalAlignment="Right"> <StackPanel Grid.Row="7" 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.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"/> <Button Width="80" Margin="2" Content="{x:Static p:Resources.textCancel}" x:Name="buttonCancel" Click="buttonCancel_Click"/>
</StackPanel> </StackPanel>

View File

@ -2,8 +2,10 @@
// Description: Windows dialog to create / edit ship calls // Description: Windows dialog to create / edit ship calls
// //
using BreCalClient.misc.Api;
using BreCalClient.misc.Model; using BreCalClient.misc.Model;
using System; using System;
using System.Collections.Generic;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using static BreCalClient.Extensions; using static BreCalClient.Extensions;
@ -31,6 +33,14 @@ namespace BreCalClient
} }
} }
public bool ShipEditingEnabled
{
get { return this.buttonEditShips.Visibility == Visibility.Visible; }
set { this.buttonEditShips.Visibility = value ? Visibility.Visible : Visibility.Hidden; }
}
public ShipApi? ShipApi { get; set; }
#endregion #endregion
#region Event handler #region Event handler
@ -40,10 +50,20 @@ namespace BreCalClient
this.comboBoxAgency.ItemsSource = BreCalLists.Participants_Agent; this.comboBoxAgency.ItemsSource = BreCalLists.Participants_Agent;
this.comboBoxShip.ItemsSource = BreCalLists.Ships; this.comboBoxShip.ItemsSource = BreCalLists.Ships;
this.comboBoxCategories.ItemsSource = Enum.GetValues(typeof(TypeEnum)); 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.comboBoxArrivalBerth.ItemsSource = BreCalLists.Berths;
this.comboBoxDepartureBerth.ItemsSource = BreCalLists.Berths; this.comboBoxDepartureBerth.ItemsSource = BreCalLists.Berths;
this.comboBoxTimeRef.ItemsSource = BreCalLists.TimeRefs;
if (this.ShipcallModel.Shipcall == null) this.ShipcallModel.Shipcall = new(); if (this.ShipcallModel.Shipcall == null) this.ShipcallModel.Shipcall = new();
this.CopyToControls(); this.CopyToControls();
@ -71,14 +91,14 @@ namespace BreCalClient
this.integerUpDownIMO.Value = ship?.Ship.Imo; this.integerUpDownIMO.Value = ship?.Ship.Imo;
this.textBoxCallsign.Text = ship?.Ship.Callsign; this.textBoxCallsign.Text = ship?.Ship.Callsign;
this.doubleUpDownLength.Value = ship?.Ship.Length; this.doubleUpDownLength.Value = ship?.Ship.Length;
this.doubleUpDownWidth.Value = ship?.Ship.Width; this.doubleUpDownWidth.Value = ship?.Ship.Width;
} }
else else
{ {
this.integerUpDownIMO.Value = null; this.integerUpDownIMO.Value = null;
this.textBoxCallsign.Text = string.Empty; this.textBoxCallsign.Text = string.Empty;
this.doubleUpDownLength.Value = null; this.doubleUpDownLength.Value = null;
this.doubleUpDownWidth.Value = null; this.doubleUpDownWidth.Value = null;
} }
this.CheckForCompletion(); this.CheckForCompletion();
} }
@ -91,32 +111,37 @@ namespace BreCalClient
private void comboBoxCategories_SelectionChanged(object? sender, SelectionChangedEventArgs? e) private void comboBoxCategories_SelectionChanged(object? sender, SelectionChangedEventArgs? e)
{ {
TypeEnum? type = this.comboBoxCategories.SelectedItem as TypeEnum?; ShipcallType? type = GetShipcallTypeFromCombobox();
if (type != null) if (type != null)
{ {
switch (type) switch (type)
{ {
case TypeEnum.Incoming: case ShipcallType.Arrival:
this.datePickerETA.IsEnabled = true; this.datePickerETA.IsEnabled = true;
this.datePickerETD.IsEnabled = false; this.datePickerETD.IsEnabled = false;
this.datePickerETD.Value = null; this.datePickerETD.Value = null;
this.comboBoxDepartureBerth.SelectedIndex = -1; this.comboBoxDepartureBerth.SelectedIndex = -1;
this.comboBoxDepartureBerth.IsEnabled = false; this.comboBoxDepartureBerth.IsEnabled = false;
this.comboBoxArrivalBerth.IsEnabled = true; this.comboBoxArrivalBerth.IsEnabled = true;
this.comboBoxTimeRef.IsEnabled = true;
break; break;
case TypeEnum.Outgoing: case ShipcallType.Departure:
this.datePickerETA.IsEnabled = false; this.datePickerETA.IsEnabled = false;
this.datePickerETD.IsEnabled = true; this.datePickerETD.IsEnabled = true;
this.datePickerETA.Value = null; this.datePickerETA.Value = null;
this.comboBoxArrivalBerth.SelectedIndex = -1; this.comboBoxArrivalBerth.SelectedIndex = -1;
this.comboBoxArrivalBerth.IsEnabled = false; this.comboBoxArrivalBerth.IsEnabled = false;
this.comboBoxDepartureBerth.IsEnabled = true; this.comboBoxDepartureBerth.IsEnabled = true;
this.comboBoxTimeRef.IsEnabled = false;
this.comboBoxTimeRef.SelectedIndex = 0;
break; break;
case TypeEnum.Shifting: case ShipcallType.Shifting:
this.datePickerETA.IsEnabled = true; this.datePickerETA.IsEnabled = true;
this.datePickerETD.IsEnabled = true; this.datePickerETD.IsEnabled = true;
this.comboBoxArrivalBerth.IsEnabled = true; this.comboBoxArrivalBerth.IsEnabled = true;
this.comboBoxDepartureBerth.IsEnabled = true; this.comboBoxDepartureBerth.IsEnabled = true;
this.comboBoxTimeRef.IsEnabled = false;
this.comboBoxTimeRef.SelectedIndex = 0;
break; break;
} }
} }
@ -137,6 +162,12 @@ namespace BreCalClient
#region private methods #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() void CheckForCompletion()
{ {
bool isEnabled = true; bool isEnabled = true;
@ -150,18 +181,18 @@ namespace BreCalClient
} }
else else
{ {
TypeEnum callType = (TypeEnum)comboBoxCategories.SelectedItem; ShipcallType callType = GetShipcallTypeFromCombobox() ?? ShipcallType.Undefined;
switch (callType) switch (callType)
{ {
case TypeEnum.Outgoing: case ShipcallType.Departure:
isEnabled &= this.comboBoxDepartureBerth.SelectedItem != null; isEnabled &= this.comboBoxDepartureBerth.SelectedItem != null;
isEnabled &= this.datePickerETD.Value.HasValue; isEnabled &= this.datePickerETD.Value.HasValue;
break; break;
case TypeEnum.Incoming: case ShipcallType.Arrival:
isEnabled &= this.comboBoxArrivalBerth.SelectedItem != null; isEnabled &= this.comboBoxArrivalBerth.SelectedItem != null;
isEnabled &= this.datePickerETA.Value.HasValue; isEnabled &= this.datePickerETA.Value.HasValue;
break; break;
case TypeEnum.Shifting: case ShipcallType.Shifting:
isEnabled &= ((this.comboBoxDepartureBerth.SelectedItem != null) && (this.comboBoxArrivalBerth.SelectedItem != null)); isEnabled &= ((this.comboBoxDepartureBerth.SelectedItem != null) && (this.comboBoxArrivalBerth.SelectedItem != null));
isEnabled &= this.datePickerETD.Value.HasValue; isEnabled &= this.datePickerETD.Value.HasValue;
isEnabled &= this.datePickerETA.Value.HasValue; isEnabled &= this.datePickerETA.Value.HasValue;
@ -178,7 +209,7 @@ namespace BreCalClient
{ {
if (this.ShipcallModel.Shipcall != null) if (this.ShipcallModel.Shipcall != null)
{ {
this.ShipcallModel.Shipcall.Type = (int)this.comboBoxCategories.SelectedItem; this.ShipcallModel.Shipcall.Type = GetShipcallTypeFromCombobox() ?? ShipcallType.Undefined;
this.ShipcallModel.Shipcall.Eta = this.datePickerETA.Value; this.ShipcallModel.Shipcall.Eta = this.datePickerETA.Value;
this.ShipcallModel.Shipcall.Etd = this.datePickerETD.Value; this.ShipcallModel.Shipcall.Etd = this.datePickerETD.Value;
@ -186,7 +217,7 @@ namespace BreCalClient
this.ShipcallModel.Ship = ((ShipModel)this.comboBoxShip.SelectedItem).Ship; this.ShipcallModel.Ship = ((ShipModel)this.comboBoxShip.SelectedItem).Ship;
this.ShipcallModel.Shipcall.Canceled = this.checkBoxCancelled.IsChecked; this.ShipcallModel.Shipcall.Canceled = this.checkBoxCancelled.IsChecked;
if (this.ShipcallModel.Shipcall.Type != 3) // incoming, outgoing if (this.ShipcallModel.Shipcall.Type != ShipcallType.Shifting) // incoming, outgoing
{ {
this.ShipcallModel.Shipcall.ArrivalBerthId = (this.comboBoxArrivalBerth.SelectedItem != null) ? ((Berth)this.comboBoxArrivalBerth.SelectedItem).Id : null; this.ShipcallModel.Shipcall.ArrivalBerthId = (this.comboBoxArrivalBerth.SelectedItem != null) ? ((Berth)this.comboBoxArrivalBerth.SelectedItem).Id : null;
this.ShipcallModel.Shipcall.DepartureBerthId = (this.comboBoxDepartureBerth.SelectedItem != null) ? ((Berth)this.comboBoxDepartureBerth.SelectedItem).Id : null; this.ShipcallModel.Shipcall.DepartureBerthId = (this.comboBoxDepartureBerth.SelectedItem != null) ? ((Berth)this.comboBoxDepartureBerth.SelectedItem).Id : null;
@ -204,6 +235,7 @@ namespace BreCalClient
ParticipantAssignment pa = new() ParticipantAssignment pa = new()
{ {
ParticipantId = participant.Id, ParticipantId = participant.Id,
Type = (int)Extensions.ParticipantType.AGENCY Type = (int)Extensions.ParticipantType.AGENCY
}; };
this.ShipcallModel.AssignedParticipants[Extensions.ParticipantType.AGENCY] = pa; this.ShipcallModel.AssignedParticipants[Extensions.ParticipantType.AGENCY] = pa;
@ -245,6 +277,10 @@ namespace BreCalClient
}; };
this.ShipcallModel.AssignedParticipants[ParticipantType.BSMD] = pa; this.ShipcallModel.AssignedParticipants[ParticipantType.BSMD] = pa;
} }
// set the time reference value (which point do all times refer to?)
this.ShipcallModel.Shipcall.TimeRefPoint = this.comboBoxTimeRef.SelectedIndex;
} }
} }
@ -253,15 +289,22 @@ namespace BreCalClient
if (this.ShipcallModel == null) return; if (this.ShipcallModel == null) return;
if (this.ShipcallModel.Shipcall != null) if (this.ShipcallModel.Shipcall != null)
{ {
this.comboBoxCategories.SelectedItem = (TypeEnum)this.ShipcallModel.Shipcall.Type; this.comboBoxTimeRef.SelectedIndex = this.ShipcallModel.Shipcall.TimeRefPoint ?? 0;
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) if (this.ShipcallModel.Shipcall.Eta != DateTime.MinValue)
this.datePickerETA.Value = this.ShipcallModel.Shipcall.Eta; this.datePickerETA.Value = this.ShipcallModel.Shipcall.Eta;
// this.textBoxVoyage.Text = this.ShipcallModel.Shipcall.Voyage; // this.textBoxVoyage.Text = this.ShipcallModel.Shipcall.Voyage;
this.datePickerETD.Value = this.ShipcallModel.Shipcall.Etd; 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; this.checkBoxCancelled.IsChecked = this.ShipcallModel.Shipcall.Canceled ?? false;
if (this.ShipcallModel.Shipcall.Type != 3) // incoming, outgoing if (this.ShipcallModel.Shipcall.Type != ShipcallType.Shifting) // incoming, outgoing
{ {
this.comboBoxArrivalBerth.SelectedValue = this.ShipcallModel.Shipcall.ArrivalBerthId; this.comboBoxArrivalBerth.SelectedValue = this.ShipcallModel.Shipcall.ArrivalBerthId;
this.comboBoxDepartureBerth.SelectedValue = this.ShipcallModel.Shipcall.DepartureBerthId; this.comboBoxDepartureBerth.SelectedValue = this.ShipcallModel.Shipcall.DepartureBerthId;
@ -340,6 +383,20 @@ namespace BreCalClient
private void comboBoxDepartureBerth_SelectionChanged(object sender, SelectionChangedEventArgs e) private void comboBoxDepartureBerth_SelectionChanged(object sender, SelectionChangedEventArgs e)
{ {
this.CheckForCompletion(); this.CheckForCompletion();
}
private void buttonEditShips_Click(object sender, RoutedEventArgs e)
{
ShipListDialog shipListDialog = new()
{
ShipApi = this.ShipApi
};
shipListDialog.ShowDialog();
// reload combobox
this.comboBoxShip.ItemsSource = null;
this.comboBoxShip.ItemsSource = BreCalLists.Ships;
} }
#endregion #endregion

View File

@ -8,16 +8,16 @@
xmlns:db="clr-namespace:BreCalClient;assembly=BreCalClient" xmlns:db="clr-namespace:BreCalClient;assembly=BreCalClient"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d" Left="{local:SettingBinding W1Left}" Top="{local:SettingBinding W1Top}" mc:Ignorable="d" Left="{local:SettingBinding W1Left}" Top="{local:SettingBinding W1Top}"
Title="{x:Static p:Resources.textEditShipcall}" Height="403" Width="800" 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> <Window.Resources>
<local:BoolToIndexConverter x:Key="boolToIndexConverter" /> <local:BoolToIndexConverter x:Key="boolToIndexConverter" />
</Window.Resources> </Window.Resources>
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="0.2*"/> <ColumnDefinition Width="0.18*"/>
<ColumnDefinition Width=".3*" /> <ColumnDefinition Width=".4*" />
<ColumnDefinition Width="0.15*"/> <ColumnDefinition Width="0.15*"/>
<ColumnDefinition Width=".35*" /> <ColumnDefinition Width=".3*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="28" /> <RowDefinition Height="28" />
@ -31,7 +31,7 @@
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="28" /> <RowDefinition Height="*" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid Grid.Row="0" Grid.Column="0"> <Grid Grid.Row="0" Grid.Column="0">
@ -43,10 +43,17 @@
<Image Margin="2" Grid.Column="1" Source="Resources/arrow_down_red.png" /> <Image Margin="2" Grid.Column="1" Source="Resources/arrow_down_red.png" />
</Grid> </Grid>
<Label Content="ETA" Grid.Column="0" Grid.Row="1" HorizontalContentAlignment="Right" FontWeight="Bold"/> <Label Content="ETA" x:Name="labelETA" Grid.Column="0" Grid.Row="1" HorizontalContentAlignment="Right" FontWeight="Bold"/>
<xctk:DateTimePicker x:Name="datePickerETA" Grid.Column="1" Grid.Row="1" Margin="2" Format="Custom" FormatString="dd.MM. yyyy HH:mm"/> <Grid Grid.Column="1" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*" />
<ColumnDefinition Width=".5*" />
</Grid.ColumnDefinitions>
<xctk:DateTimePicker x:Name="datePickerETA" Grid.Column="0" Grid.Row="0" Margin="2" Format="Custom" FormatString="dd.MM. yyyy HH:mm" ValueChanged="datePickerETA_ValueChanged"/>
<xctk:DateTimePicker x:Name="datePickerETA_End" Grid.Column="1" Grid.Row="0" Margin="2" Format="Custom" FormatString="dd.MM. yyyy HH:mm" ValueChanged="datePickerETA_End_ValueChanged"/>
</Grid>
<Label Content="{x:Static p:Resources.textBerth}" Grid.Column="0" Grid.Row="2" HorizontalContentAlignment="Right" FontWeight="Bold"/> <Label Content="{x:Static p:Resources.textBerth}" Grid.Column="0" Grid.Row="2" HorizontalContentAlignment="Right" FontWeight="Bold"/>
<ComboBox Name="comboBoxArrivalBerth" Grid.Column="1" Grid.Row="2" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id"> <ComboBox Name="comboBoxArrivalBerth" Grid.Column="1" Grid.Row="2" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id" SelectionChanged="comboBoxArrivalBerth_SelectionChanged">
</ComboBox> </ComboBox>
<Label Content="{x:Static p:Resources.textPierside}" Grid.Column="0" Grid.Row="3" HorizontalContentAlignment="Right" /> <Label Content="{x:Static p:Resources.textPierside}" Grid.Column="0" Grid.Row="3" HorizontalContentAlignment="Right" />
@ -60,9 +67,9 @@
</ComboBox.ContextMenu> </ComboBox.ContextMenu>
</ComboBox> </ComboBox>
<Label Content="{x:Static p:Resources.textBerthRemarks}" Grid.Column="0" Grid.Row="4" HorizontalContentAlignment="Right" /> <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" /> <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"/> <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"/> <Label Content="{x:Static p:Resources.textTidalWindow}" FontWeight="DemiBold" Grid.Column="0" Grid.Row="7" HorizontalContentAlignment="Right"/>
<Label Content="{x:Static p:Resources.textFrom}" Grid.Column="0" Grid.Row="8" HorizontalContentAlignment="Right"/> <Label Content="{x:Static p:Resources.textFrom}" Grid.Column="0" Grid.Row="8" HorizontalContentAlignment="Right"/>
<Label Content="{x:Static p:Resources.textTo}" Grid.Column="0" Grid.Row="9" HorizontalContentAlignment="Right"/> <Label Content="{x:Static p:Resources.textTo}" Grid.Column="0" Grid.Row="9" HorizontalContentAlignment="Right"/>
@ -120,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="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" /> <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"/> <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"> <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" /> <Button Width= "80" Margin="2" Content="{x:Static p:Resources.textOK}" x:Name="buttonOK" Click="buttonOK_Click" IsEnabled="False" />

View File

@ -4,8 +4,6 @@
using BreCalClient.misc.Model; using BreCalClient.misc.Model;
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows; using System.Windows;
using static BreCalClient.Extensions; using static BreCalClient.Extensions;
@ -14,8 +12,14 @@ namespace BreCalClient
/// <summary> /// <summary>
/// Interaction logic for EditTimesAgencyIncomingControl.xaml /// Interaction logic for EditTimesAgencyIncomingControl.xaml
/// </summary> /// </summary>
public partial class EditTimesAgencyIncomingControl : Window, IEditShipcallTimesControl public partial class EditTimesAgencyIncomingControl : Window, IEditTimesControl
{ {
#region Fields
bool _editing = false;
#endregion
#region Construction #region Construction
public EditTimesAgencyIncomingControl() public EditTimesAgencyIncomingControl()
@ -29,9 +33,7 @@ namespace BreCalClient
public ShipcallControlModel ShipcallModel { get; set; } = new(); public ShipcallControlModel ShipcallModel { get; set; } = new();
public Times Times { get; set; } = new(); public Times Times { get; set; } = new();
public Extensions.TypeEnum CallType { get; set; }
#endregion #endregion
@ -59,10 +61,10 @@ namespace BreCalClient
allowBSMD = p.IsFlagSet(ParticipantFlag.ALLOW_BSMD); allowBSMD = p.IsFlagSet(ParticipantFlag.ALLOW_BSMD);
} }
bool enableControls = (this.Times.ParticipantId == App.Participant.Id) || _editing = (this.Times.ParticipantId == App.Participant.Id) ||
(App.Participant.IsTypeFlagSet(ParticipantType.BSMD) && allowBSMD); (App.Participant.IsTypeFlagSet(ParticipantType.BSMD) && allowBSMD);
this.EnableControls(enableControls); this.EnableControls();
} }
@ -88,6 +90,7 @@ namespace BreCalClient
if (this.ShipcallModel.Shipcall != null) if (this.ShipcallModel.Shipcall != null)
{ {
this.Times.EtaBerth = this.datePickerETA.Value; this.Times.EtaBerth = this.datePickerETA.Value;
this.Times.EtaIntervalEnd = this.datePickerETA_End.Value;
if (this.comboBoxPierside.SelectedIndex >= 0) if (this.comboBoxPierside.SelectedIndex >= 0)
{ {
@ -176,6 +179,8 @@ namespace BreCalClient
if (this.ShipcallModel.Shipcall.Eta != DateTime.MinValue) if (this.ShipcallModel.Shipcall.Eta != DateTime.MinValue)
this.datePickerETA.Value = this.ShipcallModel.Shipcall.Eta; this.datePickerETA.Value = this.ShipcallModel.Shipcall.Eta;
} }
this.datePickerETA_End.Value = this.Times.EtaIntervalEnd;
if (Times.BerthId.HasValue) if (Times.BerthId.HasValue)
this.comboBoxArrivalBerth.SelectedValue = Times.BerthId; this.comboBoxArrivalBerth.SelectedValue = Times.BerthId;
@ -204,6 +209,8 @@ namespace BreCalClient
this.checkBoxReplenishingLock.IsChecked = this.ShipcallModel.Shipcall.ReplenishingLock ?? false; this.checkBoxReplenishingLock.IsChecked = this.ShipcallModel.Shipcall.ReplenishingLock ?? false;
this.checkBoxReplenishingTerminal.IsChecked = this.ShipcallModel.Shipcall.ReplenishingTerminal ?? false; this.checkBoxReplenishingTerminal.IsChecked = this.ShipcallModel.Shipcall.ReplenishingTerminal ?? false;
this.labelETA.Content = string.Format("ETA {0}", BreCalLists.TimeRefs[this.ShipcallModel.Shipcall.TimeRefPoint ?? 0]);
if(!string.IsNullOrEmpty(this.Times.Remarks)) if(!string.IsNullOrEmpty(this.Times.Remarks))
this.textBoxRemarks.Text = this.Times.Remarks; this.textBoxRemarks.Text = this.Times.Remarks;
@ -242,37 +249,55 @@ namespace BreCalClient
} }
} }
private void EnableControls(bool isEnabled) private void EnableControls()
{ {
this.datePickerETA.IsEnabled = isEnabled; this.datePickerETA.IsEnabled = _editing;
this.comboBoxArrivalBerth.IsEnabled = isEnabled; this.datePickerETA_End.IsEnabled = _editing;
this.comboBoxPierside.IsEnabled = isEnabled; this.comboBoxArrivalBerth.IsEnabled = _editing;
this.textBoxBerthRemarks.IsEnabled = isEnabled; this.comboBoxPierside.IsEnabled = _editing;
this.doubleUpDownDraft.IsEnabled = isEnabled; this.textBoxBerthRemarks.IsReadOnly = !_editing;
this.datePickerTidalWindowFrom.IsEnabled = isEnabled; this.doubleUpDownDraft.IsEnabled = _editing;
this.datePickerTidalWindowTo.IsEnabled = isEnabled; this.datePickerTidalWindowFrom.IsEnabled = _editing;
this.checkBoxCanceled.IsEnabled = isEnabled; this.datePickerTidalWindowTo.IsEnabled = _editing;
this.checkBoxCanceled.IsEnabled = _editing;
this.checkBoxAnchored.IsEnabled = isEnabled; this.checkBoxAnchored.IsEnabled = _editing;
this.comboBoxTug.IsEnabled = isEnabled; this.comboBoxTug.IsEnabled = _editing;
this.integerUpDownRecommendedTugs.IsEnabled = isEnabled; this.integerUpDownRecommendedTugs.IsEnabled = _editing;
this.comboBoxPilot.IsEnabled = isEnabled; this.comboBoxPilot.IsEnabled = _editing;
this.comboBoxMooring.IsEnabled = isEnabled; this.comboBoxMooring.IsEnabled = _editing;
this.checkBoxMooredLock.IsEnabled = isEnabled; this.checkBoxMooredLock.IsEnabled = _editing;
this.comboBoxTerminal.IsEnabled = isEnabled; this.comboBoxTerminal.IsEnabled = _editing;
this.checkBoxBunkering.IsEnabled = isEnabled; this.checkBoxBunkering.IsEnabled = _editing;
this.checkBoxReplenishingTerminal.IsEnabled = isEnabled; this.checkBoxReplenishingTerminal.IsEnabled = _editing;
this.checkBoxReplenishingLock.IsEnabled = isEnabled; this.checkBoxReplenishingLock.IsEnabled = _editing;
this.textBoxRemarks.IsEnabled = isEnabled; this.textBoxRemarks.IsReadOnly = !_editing;
this.buttonOK.IsEnabled = isEnabled; CheckOKButton();
}
private bool RequiredFieldsSet()
{
bool areSet = this.datePickerETA.Value.HasValue &&
this.doubleUpDownDraft.Value.HasValue &&
this.comboBoxArrivalBerth.SelectedIndex >= 0;
if (areSet && this.datePickerETA_End.Value.HasValue)
areSet &= (this.datePickerETA.Value < this.datePickerETA_End.Value);
return areSet;
}
private void CheckOKButton()
{
this.buttonOK.IsEnabled = _editing && RequiredFieldsSet();
} }
#endregion #endregion
#region context menu handlers #region event handlers
private void contextMenuItemArrivalBerth_Click(object sender, RoutedEventArgs e) private void contextMenuItemArrivalBerth_Click(object sender, RoutedEventArgs e)
{ {
@ -307,6 +332,26 @@ namespace BreCalClient
private void contextMenuItemClearPierside_Click(object sender, RoutedEventArgs e) private void contextMenuItemClearPierside_Click(object sender, RoutedEventArgs e)
{ {
this.comboBoxPierside.SelectedIndex = -1; this.comboBoxPierside.SelectedIndex = -1;
}
private void doubleUpDownDraft_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
this.CheckOKButton();
}
private void datePickerETA_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
this.CheckOKButton();
}
private void comboBoxArrivalBerth_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
this.CheckOKButton();
}
private void datePickerETA_End_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
this.CheckOKButton();
} }
#endregion #endregion

View File

@ -8,13 +8,13 @@
xmlns:db="clr-namespace:BreCalClient;assembly=BreCalClient" xmlns:db="clr-namespace:BreCalClient;assembly=BreCalClient"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d" Left="{local:SettingBinding W1Left}" Top="{local:SettingBinding W1Top}" mc:Ignorable="d" Left="{local:SettingBinding W1Left}" Top="{local:SettingBinding W1Top}"
Title="{x:Static p:Resources.textEditShipcall}" Height="375" Width="800" 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>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="0.2*"/> <ColumnDefinition Width="0.18*"/>
<ColumnDefinition Width=".3*" /> <ColumnDefinition Width=".4*" />
<ColumnDefinition Width="0.15*"/> <ColumnDefinition Width="0.15*"/>
<ColumnDefinition Width=".35*" /> <ColumnDefinition Width=".3*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="28" /> <RowDefinition Height="28" />
@ -28,6 +28,7 @@
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="*" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid Grid.Row="0" Grid.Column="0"> <Grid Grid.Row="0" Grid.Column="0">
@ -39,11 +40,17 @@
<Image Margin="2" Grid.Column="1" Source="Resources/arrow_up_blue.png" /> <Image Margin="2" Grid.Column="1" Source="Resources/arrow_up_blue.png" />
</Grid> </Grid>
<Label Content="ETD" Grid.Column="0" Grid.Row="1" HorizontalContentAlignment="Right" FontWeight="Bold"/> <Label Content="ETD" x:Name="labelETD" Grid.Column="0" Grid.Row="1" HorizontalContentAlignment="Right" FontWeight="Bold"/>
<xctk:DateTimePicker x:Name="datePickerETD" Grid.Column="1" Grid.Row="1" Margin="2" Format="Custom" FormatString="dd.MM. yyyy HH:mm"/> <Grid Grid.Row="1" Grid.Column="1" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*" />
<ColumnDefinition Width=".5*" />
</Grid.ColumnDefinitions>
<xctk:DateTimePicker x:Name="datePickerETD" Grid.Column="0" Grid.Row="0" Margin="2" Format="Custom" FormatString="dd.MM. yyyy HH:mm" ValueChanged="datePickerETD_ValueChanged"/>
<xctk:DateTimePicker x:Name="datePickerETD_End" Grid.Column="1" Grid.Row="0" Margin="2" Format="Custom" FormatString="dd.MM. yyyy HH:mm" ValueChanged="datePickerETD_ValueChanged"/>
</Grid>
<Label Content="{x:Static p:Resources.textBerth}" Grid.Column="0" Grid.Row="2" HorizontalContentAlignment="Right" FontWeight="Bold"/> <Label Content="{x:Static p:Resources.textBerth}" Grid.Column="0" Grid.Row="2" HorizontalContentAlignment="Right" FontWeight="Bold"/>
<ComboBox Name="comboBoxDepartureBerth" Grid.Column="1" Grid.Row="2" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id"> <ComboBox Name="comboBoxDepartureBerth" Grid.Column="1" Grid.Row="2" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id" SelectionChanged="comboBoxDepartureBerth_SelectionChanged" />
</ComboBox>
<Label Content="{x:Static p:Resources.textPierside}" Grid.Column="0" Grid.Row="3" HorizontalContentAlignment="Right" /> <Label Content="{x:Static p:Resources.textPierside}" Grid.Column="0" Grid.Row="3" HorizontalContentAlignment="Right" />
<ComboBox x:Name="comboBoxPierside" Grid.Column="1" Grid.Row="3" Margin="2" > <ComboBox x:Name="comboBoxPierside" Grid.Column="1" Grid.Row="3" Margin="2" >
<ComboBoxItem Content="{x:Static p:Resources.textPort}" /> <ComboBoxItem Content="{x:Static p:Resources.textPort}" />
@ -55,15 +62,15 @@
</ComboBox.ContextMenu> </ComboBox.ContextMenu>
</ComboBox> </ComboBox>
<Label Content="{x:Static p:Resources.textBerthRemarks}" Grid.Column="0" Grid.Row="4" HorizontalContentAlignment="Right" /> <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" /> <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"/> <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"/> <Label Content="{x:Static p:Resources.textTidalWindow}" FontWeight="DemiBold" Grid.Column="0" Grid.Row="7" HorizontalContentAlignment="Right"/>
<Label Content="{x:Static p:Resources.textFrom}" Grid.Column="0" Grid.Row="8" HorizontalContentAlignment="Right"/> <Label Content="{x:Static p:Resources.textFrom}" Grid.Column="0" Grid.Row="8" HorizontalContentAlignment="Right"/>
<Label Content="{x:Static p:Resources.textTo}" Grid.Column="0" Grid.Row="9" HorizontalContentAlignment="Right"/> <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="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"/> <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" /> <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"/> <Label Content="{x:Static p:Resources.textTugRequired}" Grid.Column="2" Grid.Row="1" HorizontalContentAlignment="Right"/>
@ -109,9 +116,9 @@
<Label Content="{x:Static p:Resources.textRainSensitiveCargo}" Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="7" HorizontalAlignment="Right" /> <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" /> <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" /> <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.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"/> <Button Width="80" Margin="2" Content="{x:Static p:Resources.textCancel}" x:Name="buttonCancel" Click="buttonCancel_Click"/>
</StackPanel> </StackPanel>

View File

@ -12,9 +12,15 @@ namespace BreCalClient
/// <summary> /// <summary>
/// Interaction logic for EditTimesAgencyOutgoingControl.xaml /// Interaction logic for EditTimesAgencyOutgoingControl.xaml
/// </summary> /// </summary>
public partial class EditTimesAgencyOutgoingControl : Window, IEditShipcallTimesControl public partial class EditTimesAgencyOutgoingControl : Window, IEditTimesControl
{ {
#region Fields
bool _editing = false;
#endregion
#region Construction #region Construction
public EditTimesAgencyOutgoingControl() public EditTimesAgencyOutgoingControl()
@ -28,9 +34,7 @@ namespace BreCalClient
public ShipcallControlModel ShipcallModel { get; set; } = new(); public ShipcallControlModel ShipcallModel { get; set; } = new();
public Times Times { get; set; } = new(); public Times Times { get; set; } = new();
public Extensions.TypeEnum CallType { get; set; }
#endregion #endregion
@ -58,10 +62,10 @@ namespace BreCalClient
allowBSMD = p.IsFlagSet(ParticipantFlag.ALLOW_BSMD); allowBSMD = p.IsFlagSet(ParticipantFlag.ALLOW_BSMD);
} }
bool enableControls = (this.Times.ParticipantId == App.Participant.Id) || _editing = (this.Times.ParticipantId == App.Participant.Id) ||
(App.Participant.IsTypeFlagSet(ParticipantType.BSMD) && allowBSMD); (App.Participant.IsTypeFlagSet(ParticipantType.BSMD) && allowBSMD);
this.EnableControls(enableControls); this.EnableControls();
} }
@ -86,7 +90,9 @@ namespace BreCalClient
{ {
if (this.ShipcallModel.Shipcall != null) if (this.ShipcallModel.Shipcall != null)
{ {
this.Times.EtdBerth = this.datePickerETD.Value; this.Times.EtdBerth = this.datePickerETD.Value;
this.Times.EtdIntervalEnd = this.datePickerETD_End.Value;
if (this.comboBoxPierside.SelectedIndex >= 0) if (this.comboBoxPierside.SelectedIndex >= 0)
{ {
this.ShipcallModel.Shipcall.PierSide = (this.comboBoxPierside.SelectedIndex == 0) ? true : false; this.ShipcallModel.Shipcall.PierSide = (this.comboBoxPierside.SelectedIndex == 0) ? true : false;
@ -170,6 +176,8 @@ namespace BreCalClient
if (this.ShipcallModel.Shipcall.Etd != DateTime.MinValue) if (this.ShipcallModel.Shipcall.Etd != DateTime.MinValue)
this.datePickerETD.Value = this.ShipcallModel.Shipcall.Etd; this.datePickerETD.Value = this.ShipcallModel.Shipcall.Etd;
} }
this.datePickerETD_End.Value = this.Times.EtdIntervalEnd;
if (this.Times.BerthId.HasValue) if (this.Times.BerthId.HasValue)
this.comboBoxDepartureBerth.SelectedValue = this.Times.BerthId; this.comboBoxDepartureBerth.SelectedValue = this.Times.BerthId;
@ -193,7 +201,10 @@ namespace BreCalClient
this.checkBoxMooredLock.IsChecked = this.ShipcallModel.Shipcall.MooredLock ?? false; this.checkBoxMooredLock.IsChecked = this.ShipcallModel.Shipcall.MooredLock ?? false;
this.checkBoxRainsensitiveCargo.IsChecked = this.ShipcallModel.Shipcall.RainSensitiveCargo ?? false; this.checkBoxRainsensitiveCargo.IsChecked = this.ShipcallModel.Shipcall.RainSensitiveCargo ?? false;
if(!string.IsNullOrEmpty(this.Times.Remarks))
this.labelETD.Content = string.Format("ETD {0}", BreCalLists.TimeRefs[this.ShipcallModel.Shipcall.TimeRefPoint ?? 0]);
if (!string.IsNullOrEmpty(this.Times.Remarks))
this.textBoxRemarks.Text = this.Times.Remarks; this.textBoxRemarks.Text = this.Times.Remarks;
if (this.ShipcallModel.AssignedParticipants.ContainsKey(ParticipantType.MOORING)) if (this.ShipcallModel.AssignedParticipants.ContainsKey(ParticipantType.MOORING))
@ -227,37 +238,54 @@ namespace BreCalClient
this.comboBoxTug.SelectedValue = this.ShipcallModel.AssignedParticipants[ParticipantType.TUG].ParticipantId; this.comboBoxTug.SelectedValue = this.ShipcallModel.AssignedParticipants[ParticipantType.TUG].ParticipantId;
} }
} }
} }
} }
private void EnableControls(bool isEnabled) private void EnableControls()
{ {
this.datePickerETD.IsEnabled = isEnabled; this.datePickerETD.IsEnabled = _editing;
this.comboBoxDepartureBerth.IsEnabled = isEnabled; this.datePickerETD_End.IsEnabled = _editing;
this.comboBoxPierside.IsEnabled = isEnabled; this.comboBoxDepartureBerth.IsEnabled = _editing;
this.textBoxBerthRemarks.IsEnabled = isEnabled; this.comboBoxPierside.IsEnabled = _editing;
this.doubleUpDownDraft.IsEnabled = isEnabled; this.textBoxBerthRemarks.IsReadOnly = !_editing;
this.datePickerTidalWindowFrom.IsEnabled = isEnabled; this.doubleUpDownDraft.IsEnabled = _editing;
this.datePickerTidalWindowTo.IsEnabled = isEnabled; this.datePickerTidalWindowFrom.IsEnabled = _editing;
this.checkBoxCanceled.IsEnabled = isEnabled; this.datePickerTidalWindowTo.IsEnabled = _editing;
this.checkBoxCanceled.IsEnabled = _editing;
this.comboBoxTug.IsEnabled = isEnabled; this.comboBoxTug.IsEnabled = _editing;
this.integerUpDownRecommendedTugs.IsEnabled = isEnabled; this.integerUpDownRecommendedTugs.IsEnabled = _editing;
this.comboBoxPilot.IsEnabled = isEnabled; this.comboBoxPilot.IsEnabled = _editing;
this.comboBoxMooring.IsEnabled = isEnabled; this.comboBoxMooring.IsEnabled = _editing;
this.checkBoxMooredLock.IsEnabled = isEnabled; this.checkBoxMooredLock.IsEnabled = _editing;
this.comboBoxTerminal.IsEnabled = isEnabled; this.comboBoxTerminal.IsEnabled = _editing;
this.checkBoxRainsensitiveCargo.IsEnabled = isEnabled; this.checkBoxRainsensitiveCargo.IsEnabled = _editing;
this.textBoxRemarks.IsEnabled = isEnabled; this.textBoxRemarks.IsReadOnly = !_editing;
this.buttonOK.IsEnabled = isEnabled; CheckOKButton();
}
private bool RequiredFieldsSet()
{
bool areSet = this.datePickerETD.Value.HasValue &&
this.doubleUpDownDraft.Value.HasValue &&
this.comboBoxDepartureBerth.SelectedIndex >= 0;
if (areSet && this.datePickerETD_End.Value.HasValue)
areSet &= (this.datePickerETD_End.Value > this.datePickerETD.Value);
return areSet;
}
private void CheckOKButton()
{
this.buttonOK.IsEnabled = _editing && RequiredFieldsSet();
} }
#endregion #endregion
#region context menu handlers #region event handlers
private void contextMenuItemClearTug_Click(object sender, RoutedEventArgs e) private void contextMenuItemClearTug_Click(object sender, RoutedEventArgs e)
{ {
@ -292,6 +320,21 @@ namespace BreCalClient
private void contextMenuItemClearPierside_Click(object sender, RoutedEventArgs e) private void contextMenuItemClearPierside_Click(object sender, RoutedEventArgs e)
{ {
this.comboBoxPierside.SelectedIndex = -1; this.comboBoxPierside.SelectedIndex = -1;
}
private void datePickerETD_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
CheckOKButton();
}
private void comboBoxDepartureBerth_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
CheckOKButton();
}
private void doubleUpDownDraft_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
CheckOKButton();
} }
#endregion #endregion

View File

@ -8,12 +8,12 @@
xmlns:db="clr-namespace:BreCalClient;assembly=BreCalClient" xmlns:db="clr-namespace:BreCalClient;assembly=BreCalClient"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d" Left="{local:SettingBinding W1Left}" Top="{local:SettingBinding W1Top}" mc:Ignorable="d" Left="{local:SettingBinding W1Left}" Top="{local:SettingBinding W1Top}"
Title="{x:Static p:Resources.textEditShipcall}" Height="490" Width="800" 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>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="0.2*"/> <ColumnDefinition Width="0.18*"/>
<ColumnDefinition Width=".3*" /> <ColumnDefinition Width=".4*" />
<ColumnDefinition Width="0.2*"/> <ColumnDefinition Width="0.15*"/>
<ColumnDefinition Width=".3*" /> <ColumnDefinition Width=".3*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions> <Grid.RowDefinitions>
@ -30,7 +30,7 @@
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="28" /> <RowDefinition Height="*" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
@ -44,9 +44,16 @@
<Label Grid.Column="0" Grid.Row="0" Content="{x:Static p:Resources.textShiftingFrom}" FontWeight="DemiBold"/> <Label Grid.Column="0" Grid.Row="0" Content="{x:Static p:Resources.textShiftingFrom}" FontWeight="DemiBold"/>
<Image Margin="2" Grid.Column="1" Source="Resources/arrow_right_green.png" /> <Image Margin="2" Grid.Column="1" Source="Resources/arrow_right_green.png" />
</Grid> </Grid>
<Label Content="ETD" Grid.Column="0" Grid.Row="1" HorizontalContentAlignment="Right" FontWeight="Bold"/> <Label Content="ETD" x:Name="labelETD" Grid.Column="0" Grid.Row="1" HorizontalContentAlignment="Right" FontWeight="Bold"/>
<xctk:DateTimePicker x:Name="datePickerETD" Grid.Column="1" Grid.Row="1" Margin="2" Format="Custom" FormatString="dd.MM. yyyy HH:mm"/> <Grid Grid.Column="1" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*" />
<ColumnDefinition Width=".5*" />
</Grid.ColumnDefinitions>
<xctk:DateTimePicker x:Name="datePickerETD" Grid.Column="0" Grid.Row="0" Margin="2" Format="Custom" FormatString="dd.MM. yyyy HH:mm" ValueChanged="datePickerETD_ValueChanged"/>
<xctk:DateTimePicker x:Name="datePickerETD_End" Grid.Column="1" Grid.Row="0" Margin="2" Format="Custom" FormatString="dd.MM. yyyy HH:mm" ValueChanged="datePickerETD_ValueChanged"/>
</Grid>
<Label Content="{x:Static p:Resources.textTerminal}" Grid.Column="0" Grid.Row="2" HorizontalContentAlignment="Right"/> <Label Content="{x:Static p:Resources.textTerminal}" Grid.Column="0" Grid.Row="2" HorizontalContentAlignment="Right"/>
<ComboBox Name="comboBoxTerminal" Grid.Column="1" Grid.Row="2" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id"> <ComboBox Name="comboBoxTerminal" Grid.Column="1" Grid.Row="2" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id">
<ComboBox.ContextMenu> <ComboBox.ContextMenu>
@ -57,11 +64,11 @@
</ComboBox> </ComboBox>
<Label Content="{x:Static p:Resources.textBerth}" Grid.Column="0" Grid.Row="3" HorizontalContentAlignment="Right" FontWeight="Bold"/> <Label Content="{x:Static p:Resources.textBerth}" Grid.Column="0" Grid.Row="3" HorizontalContentAlignment="Right" FontWeight="Bold"/>
<ComboBox Name="comboBoxDepartureBerth" Grid.Column="1" Grid.Row="3" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id"> <ComboBox Name="comboBoxDepartureBerth" Grid.Column="1" Grid.Row="3" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id" SelectionChanged="comboBoxDepartureBerth_SelectionChanged">
</ComboBox> </ComboBox>
<Label Content="{x:Static p:Resources.textDraft}" Grid.Column="0" Grid.Row="4" HorizontalContentAlignment="Right" /> <Label Content="{x:Static p:Resources.textDraft}" Grid.Column="0" Grid.Row="4" HorizontalContentAlignment="Right" FontWeight="Bold"/>
<xctk:DoubleUpDown x:Name="doubleUpDownDraft" Grid.Column="1" Grid.Row="4" Margin="2" FormatString="N2" Minimum="0" Maximum="50" MaxLength="5"/> <xctk:DoubleUpDown x:Name="doubleUpDownDraft" Grid.Column="1" Grid.Row="4" 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="5" HorizontalContentAlignment="Right"/> <Label Content="{x:Static p:Resources.textTidalWindow}" FontWeight="DemiBold" Grid.Column="0" Grid.Row="5" HorizontalContentAlignment="Right"/>
<Label Content="{x:Static p:Resources.textFrom}" Grid.Column="0" Grid.Row="6" HorizontalContentAlignment="Right"/> <Label Content="{x:Static p:Resources.textFrom}" Grid.Column="0" Grid.Row="6" HorizontalContentAlignment="Right"/>
<Label Content="{x:Static p:Resources.textTo}" Grid.Column="0" Grid.Row="7" HorizontalContentAlignment="Right"/> <Label Content="{x:Static p:Resources.textTo}" Grid.Column="0" Grid.Row="7" HorizontalContentAlignment="Right"/>
@ -75,12 +82,19 @@
<Label Grid.Column="0" Grid.Row="0" Content="{x:Static p:Resources.textShiftingTo}" FontWeight="DemiBold"/> <Label Grid.Column="0" Grid.Row="0" Content="{x:Static p:Resources.textShiftingTo}" FontWeight="DemiBold"/>
<Image Margin="2" Grid.Column="1" Source="Resources/arrow_right_green.png" /> <Image Margin="2" Grid.Column="1" Source="Resources/arrow_right_green.png" />
</Grid> </Grid>
<Label Content="ETA" Grid.Column="0" Grid.Row="9" HorizontalContentAlignment="Right" FontWeight="Bold"/> <Label Content="ETA" x:Name="labelETA" Grid.Column="0" Grid.Row="9" HorizontalContentAlignment="Right" FontWeight="Bold"/>
<xctk:DateTimePicker x:Name="datePickerETA" Grid.Column="1" Grid.Row="9" Margin="2" Format="Custom" FormatString="dd.MM. yyyy HH:mm"/> <Grid Grid.Column="1" Grid.Row="9">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*" />
<ColumnDefinition Width=".5*" />
</Grid.ColumnDefinitions>
<xctk:DateTimePicker x:Name="datePickerETA" Grid.Column="0" Grid.Row="0" Margin="2" Format="Custom" FormatString="dd.MM. yyyy HH:mm" ValueChanged="datePickerETA_ValueChanged"/>
<xctk:DateTimePicker x:Name="datePickerETA_End" Grid.Column="1" Grid.Row="0" Margin="2" Format="Custom" FormatString="dd.MM. yyyy HH:mm" ValueChanged="datePickerETA_ValueChanged"/>
</Grid>
<Label Content="{x:Static p:Resources.textBerth}" Grid.Column="0" Grid.Row="10" HorizontalContentAlignment="Right" FontWeight="Bold"/> <Label Content="{x:Static p:Resources.textBerth}" Grid.Column="0" Grid.Row="10" HorizontalContentAlignment="Right" FontWeight="Bold"/>
<ComboBox Name="comboBoxArrivalBerth" Grid.Column="1" Grid.Row="10" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id"> <ComboBox Name="comboBoxArrivalBerth" Grid.Column="1" Grid.Row="10" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id" SelectionChanged="comboBoxArrivalBerth_SelectionChanged" />
</ComboBox>
<Label Content="{x:Static p:Resources.textPierside}" Grid.Column="0" Grid.Row="11" HorizontalContentAlignment="Right" /> <Label Content="{x:Static p:Resources.textPierside}" Grid.Column="0" Grid.Row="11" HorizontalContentAlignment="Right" />
<ComboBox x:Name="comboBoxPiersideArrival" Grid.Column="1" Grid.Row="11" Margin="2" > <ComboBox x:Name="comboBoxPiersideArrival" Grid.Column="1" Grid.Row="11" Margin="2" >
<ComboBoxItem Content="{x:Static p:Resources.textPort}" /> <ComboBoxItem Content="{x:Static p:Resources.textPort}" />
@ -92,7 +106,7 @@
</ComboBox.ContextMenu> </ComboBox.ContextMenu>
</ComboBox> </ComboBox>
<Label Content="{x:Static p:Resources.textBerthRemarks}" Grid.Column="0" Grid.Row="12" HorizontalContentAlignment="Right" /> <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" /> <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" /> <CheckBox x:Name="checkBoxCanceled" Grid.Column="1" Grid.Row="14" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="0,0,4,0" />
@ -133,7 +147,7 @@
<Label Content="{x:Static p:Resources.textRainSensitiveCargo}" Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="6" HorizontalAlignment="Right" /> <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" /> <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" /> <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"> <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"/> <Button Width= "80" Margin="2" Content="{x:Static p:Resources.textOK}" x:Name="buttonOK" Click="buttonOK_Click" IsEnabled="False"/>

View File

@ -12,8 +12,13 @@ namespace BreCalClient
/// <summary> /// <summary>
/// Interaction logic for EditTimesAgencyShiftingControl.xaml /// Interaction logic for EditTimesAgencyShiftingControl.xaml
/// </summary> /// </summary>
public partial class EditTimesAgencyShiftingControl : Window, IEditShipcallTimesControl public partial class EditTimesAgencyShiftingControl : Window, IEditTimesControl
{ {
#region Fields
bool _editing = false;
#endregion
#region Construction #region Construction
@ -28,9 +33,7 @@ namespace BreCalClient
public ShipcallControlModel ShipcallModel { get; set; } = new(); public ShipcallControlModel ShipcallModel { get; set; } = new();
public Times Times { get; set; } = new(); public Times Times { get; set; } = new();
public Extensions.TypeEnum CallType { get; set; }
#endregion #endregion
@ -59,10 +62,10 @@ namespace BreCalClient
allowBSMD = p.IsFlagSet(ParticipantFlag.ALLOW_BSMD); allowBSMD = p.IsFlagSet(ParticipantFlag.ALLOW_BSMD);
} }
bool enableControls = (this.Times.ParticipantId == App.Participant.Id) || _editing = (this.Times.ParticipantId == App.Participant.Id) ||
(App.Participant.IsTypeFlagSet(ParticipantType.BSMD) && allowBSMD); (App.Participant.IsTypeFlagSet(ParticipantType.BSMD) && allowBSMD);
this.EnableControls(enableControls); this.EnableControls();
} }
@ -89,6 +92,9 @@ namespace BreCalClient
{ {
this.Times.EtdBerth = this.datePickerETD.Value; this.Times.EtdBerth = this.datePickerETD.Value;
this.Times.EtaBerth = this.datePickerETA.Value; this.Times.EtaBerth = this.datePickerETA.Value;
this.Times.EtaIntervalEnd = this.datePickerETA_End.Value;
this.Times.EtdIntervalEnd = this.datePickerETD_End.Value;
this.ShipcallModel.Shipcall.DepartureBerthId = (int)this.comboBoxDepartureBerth.SelectedValue; this.ShipcallModel.Shipcall.DepartureBerthId = (int)this.comboBoxDepartureBerth.SelectedValue;
if (this.comboBoxPiersideArrival.SelectedIndex >= 0) if (this.comboBoxPiersideArrival.SelectedIndex >= 0)
{ {
@ -184,6 +190,9 @@ namespace BreCalClient
this.datePickerETD.Value = this.ShipcallModel.Shipcall.Etd; this.datePickerETD.Value = this.ShipcallModel.Shipcall.Etd;
} }
this.datePickerETA_End.Value = this.Times.EtaIntervalEnd;
this.datePickerETD_End.Value = this.Times.EtdIntervalEnd;
if (this.Times.BerthId.HasValue) if (this.Times.BerthId.HasValue)
this.comboBoxArrivalBerth.SelectedValue = this.Times.BerthId; this.comboBoxArrivalBerth.SelectedValue = this.Times.BerthId;
else if (this.ShipcallModel.Shipcall.ArrivalBerthId.HasValue) else if (this.ShipcallModel.Shipcall.ArrivalBerthId.HasValue)
@ -208,7 +217,11 @@ namespace BreCalClient
this.checkBoxMooredLock.IsChecked = this.ShipcallModel.Shipcall.MooredLock ?? false; this.checkBoxMooredLock.IsChecked = this.ShipcallModel.Shipcall.MooredLock ?? false;
this.checkBoxRainsensitiveCargo.IsChecked = this.ShipcallModel.Shipcall.RainSensitiveCargo ?? false; this.checkBoxRainsensitiveCargo.IsChecked = this.ShipcallModel.Shipcall.RainSensitiveCargo ?? false;
if(!string.IsNullOrEmpty(this.Times.Remarks))
this.labelETA.Content = string.Format("ETA {0}", BreCalLists.TimeRefs[this.ShipcallModel.Shipcall.TimeRefPoint ?? 0]);
this.labelETD.Content = string.Format("ETD {0}", BreCalLists.TimeRefs[this.ShipcallModel.Shipcall.TimeRefPoint ?? 0]);
if (!string.IsNullOrEmpty(this.Times.Remarks))
this.textBoxRemarks.Text = this.Times.Remarks; this.textBoxRemarks.Text = this.Times.Remarks;
if (this.ShipcallModel.AssignedParticipants.ContainsKey(ParticipantType.MOORING)) if (this.ShipcallModel.AssignedParticipants.ContainsKey(ParticipantType.MOORING))
@ -245,35 +258,59 @@ namespace BreCalClient
} }
} }
private void EnableControls(bool isEnabled) private void EnableControls()
{ {
this.datePickerETD.IsEnabled = isEnabled; this.datePickerETD_End.IsEnabled = _editing;
this.comboBoxArrivalBerth.IsEnabled = isEnabled; this.datePickerETA_End.IsEnabled = _editing;
this.doubleUpDownDraft.IsEnabled = isEnabled; this.datePickerETD.IsEnabled = _editing;
this.datePickerTidalWindowFrom.IsEnabled = isEnabled; this.comboBoxArrivalBerth.IsEnabled = _editing;
this.datePickerTidalWindowTo.IsEnabled = isEnabled; this.doubleUpDownDraft.IsEnabled = _editing;
this.datePickerETA.IsEnabled = isEnabled; this.datePickerTidalWindowFrom.IsEnabled = _editing;
this.comboBoxDepartureBerth.IsEnabled = isEnabled; this.datePickerTidalWindowTo.IsEnabled = _editing;
this.comboBoxPiersideArrival.IsEnabled = isEnabled; this.datePickerETA.IsEnabled = _editing;
this.textBoxBerthRemarksArrival.IsEnabled = isEnabled; this.comboBoxDepartureBerth.IsEnabled = _editing;
this.checkBoxCanceled.IsEnabled = isEnabled; this.comboBoxPiersideArrival.IsEnabled = _editing;
this.textBoxBerthRemarksArrival.IsReadOnly = !_editing;
this.checkBoxCanceled.IsEnabled = _editing;
this.comboBoxTug.IsEnabled = isEnabled; this.comboBoxTug.IsEnabled = _editing;
this.integerUpDownRecommendedTugs.IsEnabled = isEnabled; this.integerUpDownRecommendedTugs.IsEnabled = _editing;
this.comboBoxPilot.IsEnabled = isEnabled; this.comboBoxPilot.IsEnabled = _editing;
this.comboBoxMooring.IsEnabled = isEnabled; this.comboBoxMooring.IsEnabled = _editing;
this.checkBoxMooredLock.IsEnabled = isEnabled; this.checkBoxMooredLock.IsEnabled = _editing;
this.comboBoxTerminal.IsEnabled = isEnabled; this.comboBoxTerminal.IsEnabled = _editing;
this.checkBoxRainsensitiveCargo.IsEnabled = isEnabled; this.checkBoxRainsensitiveCargo.IsEnabled = _editing;
this.textBoxRemarks.IsEnabled = isEnabled; this.textBoxRemarks.IsReadOnly = !_editing;
this.buttonOK.IsEnabled = isEnabled; CheckOKButton();
}
private bool RequiredFieldsSet()
{
bool areSet = this.datePickerETA.Value.HasValue &&
this.datePickerETD.Value.HasValue &&
this.doubleUpDownDraft.Value.HasValue &&
(this.comboBoxArrivalBerth.SelectedIndex >= 0) &&
(this.comboBoxDepartureBerth.SelectedIndex >= 0);
if (this.datePickerETA_End.Value.HasValue)
areSet &= (this.datePickerETA_End.Value > this.datePickerETA.Value);
if (this.datePickerETD_End.Value.HasValue)
areSet &= (this.datePickerETD_End.Value > this.datePickerETD.Value);
return areSet;
}
private void CheckOKButton()
{
this.buttonOK.IsEnabled = _editing && RequiredFieldsSet();
} }
#endregion #endregion
#region context menu handlers #region event handlers
private void contextMenuItemDepartureBerth_Click(object sender, RoutedEventArgs e) private void contextMenuItemDepartureBerth_Click(object sender, RoutedEventArgs e)
{ {
@ -314,6 +351,31 @@ namespace BreCalClient
private void contextMenuItemClearPierside_Click(object sender, RoutedEventArgs e) private void contextMenuItemClearPierside_Click(object sender, RoutedEventArgs e)
{ {
this.comboBoxPiersideArrival.SelectedIndex = -1; this.comboBoxPiersideArrival.SelectedIndex = -1;
}
private void datePickerETD_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
CheckOKButton();
}
private void comboBoxDepartureBerth_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
CheckOKButton();
}
private void doubleUpDownDraft_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
CheckOKButton();
}
private void datePickerETA_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
CheckOKButton();
}
private void comboBoxArrivalBerth_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
CheckOKButton();
} }
#endregion #endregion

View File

@ -8,11 +8,11 @@
xmlns:db="clr-namespace:BreCalClient;assembly=BreCalClient" xmlns:db="clr-namespace:BreCalClient;assembly=BreCalClient"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d" Left="{local:SettingBinding W1Left}" Top="{local:SettingBinding W1Top}" mc:Ignorable="d" Left="{local:SettingBinding W1Left}" Top="{local:SettingBinding W1Top}"
Title="{x:Static p:Resources.textEditTimes}" Height="265" Width="400" 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>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width=".25*" /> <ColumnDefinition Width=".20*" />
<ColumnDefinition Width=".75*" /> <ColumnDefinition Width=".80*" />
<!--ColumnDefinition Width="40" /--> <!--ColumnDefinition Width="40" /-->
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions> <Grid.RowDefinitions>
@ -21,21 +21,92 @@
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="56" /> <RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="*" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Button x:Name="buttonFixedOrder" Grid.Row="0" Grid.Column="1" Margin="2" Click="buttonFixedOrder_Click" Width="28" HorizontalAlignment="Left">
<Image x:Name="imageFixedOrder" Source="Resources\lock_open.png" />
</Button>
<!-- Label Grid.Row="0" Grid.Column="2" Content="{x:Static p:Resources.textFixed}" /--> <!-- Label Grid.Row="0" Grid.Column="2" Content="{x:Static p:Resources.textFixed}" /-->
<Label Grid.Row="1" Grid.Column="0" Content="{x:Static p:Resources.textETABerth}" HorizontalContentAlignment="Right" x:Name="labelETA" /> <Label Grid.Row="1" Grid.Column="0" Content="{x:Static p:Resources.textETABerth}" HorizontalContentAlignment="Right" x:Name="labelETA" />
<Label Grid.Row="2" Grid.Column="0" Content="{x:Static p:Resources.textETDBerth}" HorizontalContentAlignment="Right" x:Name="labelETD" /> <Label Grid.Row="2" Grid.Column="0" Content="{x:Static p:Resources.textETDBerth}" HorizontalContentAlignment="Right" x:Name="labelETD" />
<Label Grid.Row="3" Grid.Column="0" Content="{x:Static p:Resources.textLockTime}" HorizontalContentAlignment="Right" /> <Label Grid.Row="3" Grid.Column="0" Content="ATA" HorizontalContentAlignment="Right" x:Name="labelATA" />
<Label Grid.Row="4" Grid.Column="0" Content="{x:Static p:Resources.textZoneEntryTime}" HorizontalContentAlignment="Right" /> <Label Grid.Row="4" Grid.Column="0" Content="ATD" HorizontalContentAlignment="Right" x:Name="labelATD" />
<Label Grid.Row="5" Grid.Column="0" Content="{x:Static p:Resources.textLockTime}" HorizontalContentAlignment="Right" />
<Label Grid.Row="6" Grid.Column="0" Content="{x:Static p:Resources.textZoneEntryTime}" HorizontalContentAlignment="Right" />
<Label Grid.Row="5" Grid.Column="0" Content="{x:Static p:Resources.textRemarks}" HorizontalContentAlignment="Right" /> <Label Grid.Row="7" Grid.Column="0" Content="{x:Static p:Resources.textRemarks}" HorizontalContentAlignment="Right" />
<xctk:DateTimePicker IsEnabled="False" Grid.Row="1" Grid.Column="1" Margin="2" Name="datePickerETABerth" Format="Custom" FormatString="dd.MM. yyyy HH:mm"> <Grid Grid.Row="1" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*" />
<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" ValueChanged="datePickerETABerth_ValueChanged">
<xctk:DateTimePicker.ContextMenu>
<ContextMenu>
<MenuItem Header="{x:Static p:Resources.textClearValue}" Name="contextMenuItemClearETA" Click="contextMenuItemClearETA_Click" >
<MenuItem.Icon>
<Image Source="Resources\delete.png" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</xctk:DateTimePicker.ContextMenu>
</xctk:DateTimePicker>
<xctk:DateTimePicker IsEnabled="False" Grid.Row="0" Grid.Column="1" Margin="2" Name="datePickerETABerth_End" Format="Custom" FormatString="dd.MM. yyyy HH:mm">
<xctk:DateTimePicker.ContextMenu>
<ContextMenu>
<MenuItem Header="{x:Static p:Resources.textClearValue}" Name="contextMenuItemClearETA_End" Click="contextMenuItemClearETA_End_Click" >
<MenuItem.Icon>
<Image Source="Resources\delete.png" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</xctk:DateTimePicker.ContextMenu>
</xctk:DateTimePicker>
</Grid>
<Grid Grid.Row="2" Grid.Column="1" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*" />
<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" ValueChanged="datePickerETDBerth_ValueChanged">
<xctk:DateTimePicker.ContextMenu>
<ContextMenu>
<MenuItem Header="{x:Static p:Resources.textClearValue}" Name="contextMenuItemClearETD" Click="contextMenuItemClearETD_Click" >
<MenuItem.Icon>
<Image Source="Resources\delete.png" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</xctk:DateTimePicker.ContextMenu>
</xctk:DateTimePicker>
<xctk:DateTimePicker IsEnabled="False" Grid.Row="0" Grid.Column="1" Margin="2" Name="datePickerETDBerth_End" Format="Custom" FormatString="dd.MM. yyyy HH:mm">
<xctk:DateTimePicker.ContextMenu>
<ContextMenu>
<MenuItem Header="{x:Static p:Resources.textClearValue}" Name="contextMenuItemClearETD_End" Click="contextMenuItemClearETD_End_Click" >
<MenuItem.Icon>
<Image Source="Resources\delete.png" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</xctk:DateTimePicker.ContextMenu>
</xctk:DateTimePicker>
</Grid>
<xctk:DateTimePicker IsEnabled="False" Grid.Row="3" Grid.Column="1" Margin="2" Name="datePickerATA" Format="Custom" FormatString="dd.MM. yyyy HH:mm">
<xctk:DateTimePicker.ContextMenu> <xctk:DateTimePicker.ContextMenu>
<ContextMenu> <ContextMenu>
<MenuItem Header="{x:Static p:Resources.textClearValue}" Name="contextMenuItemClearETA" Click="contextMenuItemClearETA_Click" > <MenuItem Header="{x:Static p:Resources.textClearValue}" Name="contextMenuItemClearATA" Click="contextMenuItemClearATA_Click" >
<MenuItem.Icon> <MenuItem.Icon>
<Image Source="Resources\delete.png" /> <Image Source="Resources\delete.png" />
</MenuItem.Icon> </MenuItem.Icon>
@ -43,11 +114,11 @@
</ContextMenu> </ContextMenu>
</xctk:DateTimePicker.ContextMenu> </xctk:DateTimePicker.ContextMenu>
</xctk:DateTimePicker> </xctk:DateTimePicker>
<!--CheckBox IsEnabled="False" Grid.Row="1" Grid.Column="2" Margin="4,0,0,0" Name="checkBoxEtaBerthFixed" VerticalAlignment="Center" /-->
<xctk:DateTimePicker IsEnabled="False" Grid.Row="2" Grid.Column="1" Margin="2" Name="datePickerETDBerth" Format="Custom" FormatString="dd.MM. yyyy HH:mm"> <xctk:DateTimePicker IsEnabled="False" Grid.Row="4" Grid.Column="1" Margin="2" Name="datePickerATD" Format="Custom" FormatString="dd.MM. yyyy HH:mm">
<xctk:DateTimePicker.ContextMenu> <xctk:DateTimePicker.ContextMenu>
<ContextMenu> <ContextMenu>
<MenuItem Header="{x:Static p:Resources.textClearValue}" Name="contextMenuItemClearETD" Click="contextMenuItemClearETD_Click" > <MenuItem Header="{x:Static p:Resources.textClearValue}" Name="contextMenuItemClearATD" Click="contextMenuItemClearATD_Click" >
<MenuItem.Icon> <MenuItem.Icon>
<Image Source="Resources\delete.png" /> <Image Source="Resources\delete.png" />
</MenuItem.Icon> </MenuItem.Icon>
@ -55,8 +126,8 @@
</ContextMenu> </ContextMenu>
</xctk:DateTimePicker.ContextMenu> </xctk:DateTimePicker.ContextMenu>
</xctk:DateTimePicker> </xctk:DateTimePicker>
<!--CheckBox IsEnabled="False" Grid.Row="2" Grid.Column="2" Margin="4,0,0,0" Name="checkBoxEtDBerthFixed" VerticalAlignment="Center" /-->
<xctk:DateTimePicker IsEnabled="False" Grid.Row="3" Grid.Column="1" Margin="2" Name="datePickerLockTime" Format="Custom" FormatString="dd.MM. yyyy HH:mm"> <xctk:DateTimePicker IsEnabled="False" Grid.Row="5" Grid.Column="1" Margin="2" Name="datePickerLockTime" Format="Custom" FormatString="dd.MM. yyyy HH:mm">
<xctk:DateTimePicker.ContextMenu> <xctk:DateTimePicker.ContextMenu>
<ContextMenu> <ContextMenu>
<MenuItem Header="{x:Static p:Resources.textClearValue}" Name="contextMenuItemClearLockTime" Click="contextMenuItemClearLockTime_Click" > <MenuItem Header="{x:Static p:Resources.textClearValue}" Name="contextMenuItemClearLockTime" Click="contextMenuItemClearLockTime_Click" >
@ -68,7 +139,7 @@
</xctk:DateTimePicker.ContextMenu> </xctk:DateTimePicker.ContextMenu>
</xctk:DateTimePicker> </xctk:DateTimePicker>
<!--CheckBox IsEnabled="False" Grid.Row="3" Grid.Column="2" Margin="4,0,0,0" Name="checkBoxLockTimeFixed" VerticalAlignment="Center" /--> <!--CheckBox IsEnabled="False" Grid.Row="3" Grid.Column="2" Margin="4,0,0,0" Name="checkBoxLockTimeFixed" VerticalAlignment="Center" /-->
<xctk:DateTimePicker IsEnabled="False" Grid.Row="4" Grid.Column="1" Margin="2" Name="datePickerZoneEntry" Format="Custom" FormatString="dd.MM. yyyy HH:mm"> <xctk:DateTimePicker IsEnabled="False" Grid.Row="6" Grid.Column="1" Margin="2" Name="datePickerZoneEntry" Format="Custom" FormatString="dd.MM. yyyy HH:mm">
<xctk:DateTimePicker.ContextMenu> <xctk:DateTimePicker.ContextMenu>
<ContextMenu> <ContextMenu>
<MenuItem Header="{x:Static p:Resources.textClearValue}" Name="contextMenuItemClearZoneEntry" Click="contextMenuItemClearZoneEntry_Click" > <MenuItem Header="{x:Static p:Resources.textClearValue}" Name="contextMenuItemClearZoneEntry" Click="contextMenuItemClearZoneEntry_Click" >
@ -81,8 +152,8 @@
</xctk:DateTimePicker> </xctk:DateTimePicker>
<!--CheckBox IsEnabled="False" Grid.Row="4" Grid.Column="2" Margin="4,0,0,0" Name="checkBoxZoneEntryFixed" VerticalAlignment="Center" /--> <!--CheckBox IsEnabled="False" Grid.Row="4" Grid.Column="2" Margin="4,0,0,0" Name="checkBoxZoneEntryFixed" VerticalAlignment="Center" /-->
<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="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="6" Grid.Column="1" Grid.ColumnSpan="2" Orientation="Horizontal" HorizontalAlignment="Right"> <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.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"/> <Button Width="80" Margin="2" Content="{x:Static p:Resources.textCancel}" x:Name="buttonCancel" Click="buttonCancel_Click"/>
</StackPanel> </StackPanel>

View File

@ -4,7 +4,9 @@
// //
using BreCalClient.misc.Model; using BreCalClient.misc.Model;
using System;
using System.Windows; using System.Windows;
using System.Windows.Media.Imaging;
namespace BreCalClient namespace BreCalClient
{ {
@ -25,9 +27,9 @@ namespace BreCalClient
#region Properties #region Properties
public Times Times { get; set; } = new(); public Times Times { get; set; } = new();
public Extensions.TypeEnum CallType { get; set; } public ShipcallControlModel ShipcallModel { get; set; } = new();
#endregion #endregion
@ -52,6 +54,24 @@ namespace BreCalClient
this.Close(); this.Close();
} }
private void buttonFixedOrder_Click(object sender, RoutedEventArgs e)
{
bool newValue = true;
if (this.Times.EtaBerthFixed ?? false)
newValue = false;
SetLockButton(newValue);
}
private void datePickerETABerth_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
CheckOKButton();
}
private void datePickerETDBerth_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
CheckOKButton();
}
#endregion #endregion
#region private methods #region private methods
@ -61,8 +81,12 @@ namespace BreCalClient
this.Times.Remarks = this.textBoxRemarks.Text.Trim().Truncate(512); this.Times.Remarks = this.textBoxRemarks.Text.Trim().Truncate(512);
this.Times.EtaBerth = this.datePickerETABerth.Value; this.Times.EtaBerth = this.datePickerETABerth.Value;
this.Times.EtdBerth = this.datePickerETDBerth.Value; this.Times.EtdBerth = this.datePickerETDBerth.Value;
this.Times.EtaIntervalEnd = this.datePickerETABerth_End.Value;
this.Times.EtdIntervalEnd = this.datePickerETDBerth_End.Value;
this.Times.LockTime = this.datePickerLockTime.Value; this.Times.LockTime = this.datePickerLockTime.Value;
this.Times.ZoneEntry = this.datePickerZoneEntry.Value; this.Times.ZoneEntry = this.datePickerZoneEntry.Value;
this.Times.Ata = this.datePickerATA.Value;
this.Times.Atd = this.datePickerATD.Value;
} }
private void CopyToControls() private void CopyToControls()
@ -72,54 +96,114 @@ namespace BreCalClient
this.datePickerETDBerth.Value = this.Times.EtdBerth; this.datePickerETDBerth.Value = this.Times.EtdBerth;
this.datePickerLockTime.Value = this.Times.LockTime; this.datePickerLockTime.Value = this.Times.LockTime;
this.datePickerZoneEntry.Value = this.Times.ZoneEntry; this.datePickerZoneEntry.Value = this.Times.ZoneEntry;
this.datePickerATA.Value = this.Times.Ata;
this.datePickerATD.Value = this.Times.Atd;
this.datePickerETABerth_End.Value = this.Times.EtaIntervalEnd;
this.datePickerETDBerth_End.Value = this.Times.EtdIntervalEnd;
switch (CallType) this.labelETA.Content = string.Format("ETA {0}", BreCalLists.TimeRefs[this.ShipcallModel.Shipcall?.TimeRefPoint ?? 0]);
this.labelETD.Content = string.Format("ETD {0}", BreCalLists.TimeRefs[this.ShipcallModel.Shipcall?.TimeRefPoint ?? 0]);
switch (ShipcallModel.Shipcall?.Type)
{ {
case Extensions.TypeEnum.Incoming: case ShipcallType.Arrival:
this.labelETA.FontWeight = FontWeights.Bold; this.labelETA.FontWeight = FontWeights.Bold;
this.datePickerETABerth.ContextMenu.IsEnabled = false; this.datePickerETABerth.ContextMenu.IsEnabled = false;
break; break;
case Extensions.TypeEnum.Outgoing: case ShipcallType.Departure:
case Extensions.TypeEnum.Shifting: case ShipcallType.Shifting:
this.labelETD.FontWeight = FontWeights.Bold; this.labelETD.FontWeight = FontWeights.Bold;
this.datePickerETDBerth.ContextMenu.IsEnabled = false; this.datePickerETDBerth.ContextMenu.IsEnabled = false;
break; break;
} }
this.SetLockButton(this.Times.EtaBerthFixed ?? false);
} }
private void EnableControls() private void EnableControls()
{ {
Extensions.ParticipantType pType = (Extensions.ParticipantType) (this.Times.ParticipantType ?? 0); Extensions.ParticipantType pType = (Extensions.ParticipantType) this.Times.ParticipantType;
if (this.Times.ParticipantId != App.Participant.Id) return; // if this is not "my" entry, there is no editing!
// setting visibility
if (pType != Extensions.ParticipantType.MOORING)
{
this.labelATA.Visibility = Visibility.Hidden;
this.datePickerATA.Visibility = Visibility.Hidden;
this.labelATD.Visibility = Visibility.Hidden;
this.datePickerATD.Visibility = Visibility.Hidden;
}
else
{
if(ShipcallModel.Shipcall?.Type == ShipcallType.Arrival)
{
this.labelATD.Visibility = Visibility.Hidden;
this.datePickerATD.Visibility = Visibility.Hidden;
}
if (ShipcallModel.Shipcall?.Type == ShipcallType.Departure)
{
this.labelATA.Visibility = Visibility.Hidden;
this.datePickerATA.Visibility = Visibility.Hidden;
}
}
// 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!
}
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) switch (pType)
{ {
case Extensions.ParticipantType.MOORING: case Extensions.ParticipantType.MOORING:
case Extensions.ParticipantType.PORT_ADMINISTRATION: this.datePickerATA.IsEnabled = true;
case Extensions.ParticipantType.TUG: this.datePickerATD.IsEnabled = true;
this.datePickerETABerth.IsEnabled = (CallType == Extensions.TypeEnum.Incoming);
//this.checkBoxEtaBerthFixed.IsEnabled = (CallType == Extensions.TypeEnum.Incoming || CallType == Extensions.TypeEnum.Shifting);
this.datePickerETDBerth.IsEnabled = (CallType == Extensions.TypeEnum.Outgoing || CallType == Extensions.TypeEnum.Shifting);
//this.checkBoxEtDBerthFixed.IsEnabled = (CallType == Extensions.TypeEnum.Outgoing || CallType == Extensions.TypeEnum.Shifting);
this.datePickerLockTime.IsEnabled = (CallType == Extensions.TypeEnum.Incoming || CallType == Extensions.TypeEnum.Shifting);
//this.checkBoxLockTimeFixed.IsEnabled = (CallType == Extensions.TypeEnum.Incoming || CallType == Extensions.TypeEnum.Shifting);
this.datePickerZoneEntry.IsEnabled = false;
//this.checkBoxZoneEntryFixed.IsEnabled = false;
this.textBoxRemarks.IsEnabled = true;
break; break;
case Extensions.ParticipantType.PILOT: case Extensions.ParticipantType.PORT_ADMINISTRATION:
this.datePickerETABerth.IsEnabled = (CallType == Extensions.TypeEnum.Incoming); this.datePickerLockTime.IsEnabled = true;
//this.checkBoxEtaBerthFixed.IsEnabled = (CallType == Extensions.TypeEnum.Incoming || CallType == Extensions.TypeEnum.Shifting); break;
this.datePickerETDBerth.IsEnabled = (CallType == Extensions.TypeEnum.Outgoing || CallType == Extensions.TypeEnum.Shifting); case Extensions.ParticipantType.TUG:
//this.checkBoxEtDBerthFixed.IsEnabled = (CallType == Extensions.TypeEnum.Outgoing || CallType == Extensions.TypeEnum.Shifting); case Extensions.ParticipantType.PILOT:
this.datePickerLockTime.IsEnabled = (CallType == Extensions.TypeEnum.Incoming || CallType == Extensions.TypeEnum.Shifting); this.datePickerZoneEntry.IsEnabled = (ShipcallModel.Shipcall?.Type == ShipcallType.Arrival);
//this.checkBoxLockTimeFixed.IsEnabled = (CallType == Extensions.TypeEnum.Incoming || CallType == Extensions.TypeEnum.Shifting);
this.datePickerZoneEntry.IsEnabled = (CallType == Extensions.TypeEnum.Incoming);
//this.checkBoxZoneEntryFixed.IsEnabled = (CallType == Extensions.TypeEnum.Incoming);
this.textBoxRemarks.IsEnabled = true;
break; break;
} }
this.buttonOK.IsEnabled = true;
CheckOKButton();
}
private void SetLockButton(bool newValue)
{
if (newValue)
{
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 #endregion
@ -146,7 +230,27 @@ namespace BreCalClient
this.datePickerZoneEntry.Value = null; this.datePickerZoneEntry.Value = null;
} }
#endregion private void contextMenuItemClearATA_Click(object sender, RoutedEventArgs e)
{
this.datePickerATA.Value = null;
}
private void contextMenuItemClearATD_Click(object sender, RoutedEventArgs e)
{
this.datePickerATD.Value = null;
}
private void contextMenuItemClearETA_End_Click(object sender, RoutedEventArgs e)
{
this.datePickerETABerth_End.Value = null;
}
private void contextMenuItemClearETD_End_Click(object sender, RoutedEventArgs e)
{
this.datePickerETDBerth_End.Value = null;
}
#endregion
} }
} }

View File

@ -7,11 +7,11 @@
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:local="clr-namespace:BreCalClient" xmlns:local="clr-namespace:BreCalClient"
mc:Ignorable="d" Left="{local:SettingBinding W1Left}" Top="{local:SettingBinding W1Top}" mc:Ignorable="d" Left="{local:SettingBinding W1Left}" Top="{local:SettingBinding W1Top}"
Title="{x:Static p:Resources.textEditTimes}" Loaded="Window_Loaded" Height="295" Width="400" > Title="{x:Static p:Resources.textEditTimes}" Loaded="Window_Loaded" Height="295" Width="500" ResizeMode="CanResizeWithGrip" >
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width=".4*" /> <ColumnDefinition Width=".3*" />
<ColumnDefinition Width=".6*" /> <ColumnDefinition Width=".7*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions> <Grid.RowDefinitions>
@ -20,7 +20,7 @@
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="56" /> <RowDefinition Height="56" />
<RowDefinition Height="56" /> <RowDefinition Height="*" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
@ -32,28 +32,70 @@
<Label Grid.Row="5" Grid.Column="0" Content="{x:Static p:Resources.textRemarks}" HorizontalContentAlignment="Right" /> <Label Grid.Row="5" Grid.Column="0" Content="{x:Static p:Resources.textRemarks}" HorizontalContentAlignment="Right" />
<xctk:DateTimePicker Grid.Row="0" Grid.Column="1" Margin="2" Name="datePickerOperationStart" Format="Custom" FormatString="dd.MM. yyyy HH:mm" IsEnabled="False"> <Grid Grid.Row="0" Grid.Column="1">
<xctk:DateTimePicker.ContextMenu> <Grid.ColumnDefinitions>
<ContextMenu> <ColumnDefinition Width=".5*" />
<MenuItem Header="{x:Static p:Resources.textClearValue}" Name="contextMenuItemClearOperationStart" Click="contextMenuItemClearOperationStart_Click" > <ColumnDefinition Width=".5*" />
<MenuItem.Icon> </Grid.ColumnDefinitions>
<Image Source="Resources\delete.png" />
</MenuItem.Icon> <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">
</MenuItem> <xctk:DateTimePicker.ContextMenu>
</ContextMenu> <ContextMenu>
</xctk:DateTimePicker.ContextMenu> <MenuItem Header="{x:Static p:Resources.textClearValue}" Name="contextMenuItemClearOperationStart" Click="contextMenuItemClearOperationStart_Click" >
</xctk:DateTimePicker> <MenuItem.Icon>
<xctk:DateTimePicker Grid.Row="1" Grid.Column="1" Margin="2" Name="datePickerOperationEnd" Format="Custom" FormatString="dd.MM. yyyy HH:mm" IsEnabled="False"> <Image Source="Resources\delete.png" />
<xctk:DateTimePicker.ContextMenu> </MenuItem.Icon>
<ContextMenu> </MenuItem>
<MenuItem Header="{x:Static p:Resources.textClearValue}" Name="contextMenuItemClearOperationEnd" Click="contextMenuItemClearOperationEnd_Click" > </ContextMenu>
<MenuItem.Icon> </xctk:DateTimePicker.ContextMenu>
<Image Source="Resources\delete.png" /> </xctk:DateTimePicker>
</MenuItem.Icon> <xctk:DateTimePicker Grid.Row="0" Grid.Column="1" Margin="2" Name="datePickerOperationStart_End" Format="Custom" FormatString="dd.MM. yyyy HH:mm" IsEnabled="False">
</MenuItem> <xctk:DateTimePicker.ContextMenu>
</ContextMenu> <ContextMenu>
</xctk:DateTimePicker.ContextMenu> <MenuItem Header="{x:Static p:Resources.textClearValue}" Name="contextMenuItemClearOperationStart_End" Click="contextMenuItemClearOperationStart_End_Click">
</xctk:DateTimePicker> <MenuItem.Icon>
<Image Source="Resources\delete.png" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</xctk:DateTimePicker.ContextMenu>
</xctk:DateTimePicker>
</Grid>
<Grid Grid.Row="1" Grid.Column="1" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*" />
<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" ValueChanged="datePickerOperationEnd_ValueChanged">
<xctk:DateTimePicker.ContextMenu>
<ContextMenu>
<MenuItem Header="{x:Static p:Resources.textClearValue}" Name="contextMenuItemClearOperationEnd" Click="contextMenuItemClearOperationEnd_Click" >
<MenuItem.Icon>
<Image Source="Resources\delete.png" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</xctk:DateTimePicker.ContextMenu>
</xctk:DateTimePicker>
<xctk:DateTimePicker Grid.Row="0" Grid.Column="1" Margin="2" Name="datePickerOperationEnd_End" Format="Custom" FormatString="dd.MM. yyyy HH:mm" IsEnabled="False">
<xctk:DateTimePicker.ContextMenu>
<ContextMenu>
<MenuItem Header="{x:Static p:Resources.textClearValue}" Name="contextMenuItemClearOperationEnd_End" Click="contextMenuItemClearOperationEnd_End_Click">
<MenuItem.Icon>
<Image Source="Resources\delete.png" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</xctk:DateTimePicker.ContextMenu>
</xctk:DateTimePicker>
</Grid>
<ComboBox Name="comboBoxBerth" Grid.Column="1" Grid.Row="2" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id" IsEnabled="False"> <ComboBox Name="comboBoxBerth" Grid.Column="1" Grid.Row="2" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id" IsEnabled="False">
<ComboBox.ContextMenu> <ComboBox.ContextMenu>
<ContextMenu> <ContextMenu>
@ -70,8 +112,8 @@
</ContextMenu> </ContextMenu>
</ComboBox.ContextMenu> </ComboBox.ContextMenu>
</ComboBox> </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="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" IsEnabled="False" MaxLength="512" /> <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"> <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.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"/> <Button Width="80" Margin="2" Content="{x:Static p:Resources.textCancel}" x:Name="buttonCancel" Click="buttonCancel_Click"/>

View File

@ -21,7 +21,7 @@ namespace BreCalClient
public Times Times { get; set; } = new(); public Times Times { get; set; } = new();
public Extensions.TypeEnum CallType { get; set; } public ShipcallControlModel ShipcallModel { get; set; } = new();
#endregion #endregion
@ -44,6 +44,16 @@ namespace BreCalClient
this.datePickerOperationEnd.Value = null; this.datePickerOperationEnd.Value = null;
} }
private void contextMenuItemClearOperationStart_End_Click(object sender, RoutedEventArgs e)
{
this.datePickerOperationStart_End.Value = null;
}
private void contextMenuItemClearOperationEnd_End_Click(object sender, RoutedEventArgs e)
{
this.datePickerOperationEnd_End.Value = null;
}
private void contextMenuItemBerth_Click(object sender, RoutedEventArgs e) private void contextMenuItemBerth_Click(object sender, RoutedEventArgs e)
{ {
this.comboBoxBerth.SelectedIndex -= 1; this.comboBoxBerth.SelectedIndex -= 1;
@ -66,20 +76,32 @@ namespace BreCalClient
this.comboBoxPierside.SelectedIndex = -1; 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 #endregion
#region private methods #region private methods
private void CopyToModel() private void CopyToModel()
{ {
switch(this.comboBoxPierside.SelectedIndex) this.Times.PierSide = this.comboBoxPierside.SelectedIndex switch
{ {
case 0: this.Times.PierSide = true; break; 0 => true,
case 1: this.Times.PierSide= false; break; 1 => false,
default: this.Times.PierSide = null; break; _ => null,
} };
this.Times.OperationsStart = this.datePickerOperationStart.Value; this.Times.OperationsStart = this.datePickerOperationStart.Value;
this.Times.OperationsEnd = this.datePickerOperationEnd.Value; this.Times.OperationsEnd = this.datePickerOperationEnd.Value;
this.Times.EtaIntervalEnd = this.datePickerOperationStart_End.Value;
this.Times.EtdIntervalEnd = this.datePickerOperationEnd_End.Value;
this.Times.BerthId = (this.comboBoxBerth.SelectedItem != null) ? ((Berth)this.comboBoxBerth.SelectedItem).Id : null; this.Times.BerthId = (this.comboBoxBerth.SelectedItem != null) ? ((Berth)this.comboBoxBerth.SelectedItem).Id : null;
this.Times.Remarks = this.textBoxRemarks.Text.Trim(); this.Times.Remarks = this.textBoxRemarks.Text.Trim();
this.Times.BerthInfo = this.textBoxBerthRemarks.Text.Trim(); this.Times.BerthInfo = this.textBoxBerthRemarks.Text.Trim();
@ -89,20 +111,22 @@ namespace BreCalClient
{ {
this.datePickerOperationStart.Value = this.Times.OperationsStart; this.datePickerOperationStart.Value = this.Times.OperationsStart;
this.datePickerOperationEnd.Value = this.Times.OperationsEnd; this.datePickerOperationEnd.Value = this.Times.OperationsEnd;
this.datePickerOperationStart_End.Value = this.Times.EtaIntervalEnd;
this.datePickerOperationEnd_End.Value = this.Times.EtdIntervalEnd;
if(this.Times.PierSide == null) { this.comboBoxPierside.SelectedIndex = -1; } if(this.Times.PierSide == null) { this.comboBoxPierside.SelectedIndex = -1; }
else this.comboBoxPierside.SelectedIndex = (this.Times.PierSide ?? false) ? 0 : 1; else this.comboBoxPierside.SelectedIndex = (this.Times.PierSide ?? false) ? 0 : 1;
this.comboBoxBerth.SelectedValue = this.Times.BerthId; this.comboBoxBerth.SelectedValue = this.Times.BerthId;
this.textBoxRemarks.Text = this.Times.Remarks; this.textBoxRemarks.Text = this.Times.Remarks;
this.textBoxBerthRemarks.Text = this.Times.BerthInfo; this.textBoxBerthRemarks.Text = this.Times.BerthInfo;
switch (CallType) switch (ShipcallModel.Shipcall?.Type)
{ {
case Extensions.TypeEnum.Incoming: case ShipcallType.Arrival:
this.labelStart.FontWeight = FontWeights.Bold; this.labelStart.FontWeight = FontWeights.Bold;
this.datePickerOperationStart.ContextMenu.IsEnabled = false; this.datePickerOperationStart.ContextMenu.IsEnabled = false;
break; break;
case Extensions.TypeEnum.Outgoing: case ShipcallType.Departure:
case Extensions.TypeEnum.Shifting: case ShipcallType.Shifting:
this.labelEnd.FontWeight = FontWeights.Bold; this.labelEnd.FontWeight = FontWeights.Bold;
this.datePickerOperationEnd.ContextMenu.IsEnabled = false; this.datePickerOperationEnd.ContextMenu.IsEnabled = false;
break; break;
@ -114,13 +138,21 @@ namespace BreCalClient
{ {
if (this.Times.ParticipantId != App.Participant.Id) return; if (this.Times.ParticipantId != App.Participant.Id) return;
this.datePickerOperationStart.IsEnabled = (CallType == Extensions.TypeEnum.Incoming); this.datePickerOperationStart.IsEnabled = ShipcallModel.Shipcall?.Type == ShipcallType.Arrival;
this.datePickerOperationEnd.IsEnabled = (CallType == Extensions.TypeEnum.Outgoing) || (CallType == Extensions.TypeEnum.Shifting); this.datePickerOperationStart_End.IsEnabled = ShipcallModel.Shipcall?.Type == ShipcallType.Arrival;
this.comboBoxBerth.IsEnabled = (CallType == Extensions.TypeEnum.Incoming); this.datePickerOperationEnd.IsEnabled = (ShipcallModel.Shipcall?.Type == ShipcallType.Departure) || (ShipcallModel.Shipcall?.Type == ShipcallType.Shifting);
this.comboBoxPierside.IsEnabled = (CallType == Extensions.TypeEnum.Incoming); this.datePickerOperationEnd_End.IsEnabled = (ShipcallModel.Shipcall?.Type == ShipcallType.Departure) || (ShipcallModel.Shipcall?.Type == ShipcallType.Shifting);
this.textBoxBerthRemarks.IsEnabled = (CallType == Extensions.TypeEnum.Incoming); this.comboBoxBerth.IsEnabled = ShipcallModel.Shipcall?.Type == ShipcallType.Arrival;
this.textBoxRemarks.IsEnabled = true; this.comboBoxPierside.IsEnabled = ShipcallModel.Shipcall?.Type == ShipcallType.Arrival;
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 #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

@ -1,17 +1,19 @@
// Copyright (c) 2023 schick Informatik // Copyright (c) 2023 schick Informatik
// Description: some helpers // Description: some helpers
// //
using BreCalClient.misc.Model; using BreCalClient.misc.Model;
using Newtonsoft.Json.Linq;
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace BreCalClient namespace BreCalClient
{ {
public static class Extensions public static class Extensions
{ {
#region Enum #region Enum
/// <summary> /// <summary>
/// Copied from models clunky I know /// Copied from models clunky I know
@ -47,16 +49,6 @@ namespace BreCalClient
ALLOW_BSMD = 1, ALLOW_BSMD = 1,
} }
/// <summary>
/// Should actually be defined in yaml
/// </summary>
public enum TypeEnum
{
Incoming = 1,
Outgoing = 2,
Shifting = 3
}
public enum SortOrder public enum SortOrder
{ {
SHIP_NAME, SHIP_NAME,
@ -76,20 +68,87 @@ namespace BreCalClient
public static bool IsFlagSet(this Participant participant, ParticipantFlag flag) public static bool IsFlagSet(this Participant participant, ParticipantFlag flag)
{ {
return (participant.Flags & (uint)flag) != 0; return (participant.Flags & (uint)flag) != 0;
} }
public static string Truncate(this string value, int maxLength) public static string Truncate(this string value, int maxLength)
{ {
if (string.IsNullOrEmpty(value)) return value; if (string.IsNullOrEmpty(value)) return value;
return value.Length <= maxLength ? value : value.Substring(0, maxLength); return value.Length <= maxLength ? value : value[..maxLength];
} }
public static string TruncateDots(this string value, int maxLength) public static string TruncateDots(this string value, int maxLength)
{ {
if(string.IsNullOrEmpty(value)) return value; if(string.IsNullOrEmpty(value)) return value;
if (value.Length <= maxLength) return value; if (value.Length <= maxLength) return value;
if (value.Length > (maxLength + 1)) return $"{value.Substring(0, maxLength)}.."; if (value.Length > (maxLength + 1))
return value.Substring(0, maxLength); {
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];
}
public static string DisplayTime(this Times times, bool isArrival)
{
if (isArrival)
{
if(times.ParticipantType == (int) ParticipantType.TERMINAL)
{
if(times.OperationsStart.HasValue)
{
string result = times.OperationsStart.Value.ToString("dd.MM.yyyy HH:mm");
if (times.EtaIntervalEnd.HasValue) result += " - " + times.EtaIntervalEnd.Value.ToString("HH:mm");
return result;
}
else
{
return "- / -";
}
}
else
{
if(times.EtaBerth.HasValue)
{
string result = times.EtaBerth.Value.ToString("dd.MM.yyyy HH:mm");
if (times.EtaIntervalEnd.HasValue) result += " - " + times.EtaIntervalEnd.Value.ToString("HH:mm");
return result;
}
else
{
return "- / -";
}
}
}
else
{
if (times.ParticipantType == (int)ParticipantType.TERMINAL)
{
if(times.OperationsEnd.HasValue)
{
string result = times.OperationsEnd.Value.ToString("dd.MM.yyyy HH:mm");
if (times.EtdIntervalEnd.HasValue) result += " - " + times.EtdIntervalEnd.Value.ToString("HH:mm");
return result;
}
else
{
return "- / -";
}
}
else
{
if(times.EtdBerth.HasValue)
{
string result = times.EtdBerth.Value.ToString("dd.MM.yyyy HH:mm");
if (times.EtdIntervalEnd.HasValue) result += " - " + times.EtdIntervalEnd.Value.ToString("HH:mm");
return result;
}
else
{
return "- / -";
}
}
}
} }
#endregion #endregion

View File

@ -0,0 +1,39 @@
<UserControl x:Class="BreCalClient.HistoryControl"
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:local="clr-namespace:BreCalClient"
xmlns:p = "clr-namespace:BreCalClient.Resources"
mc:Ignorable="d"
d:DesignHeight="46" d:DesignWidth="800">
<Border BorderBrush="Black" BorderThickness="0 0 0 .5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="14" />
<RowDefinition Height="25" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<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" 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

@ -0,0 +1,38 @@
// Copyright (c) 2024- schick Informatik
// Description: display single history element (later shown in a list)
//
using BreCalClient.misc.Model;
using System;
using System.Windows.Controls;
namespace BreCalClient
{
/// <summary>
/// Interaction logic for HistoryControl.xaml
/// </summary>
public partial class HistoryControl : UserControl
{
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.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

@ -0,0 +1,32 @@
<Window x:Class="BreCalClient.HistoryDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BreCalClient"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:p = "clr-namespace:BreCalClient.Resources"
mc:Ignorable="d" Left="{local:SettingBinding W4Left}" Top="{local:SettingBinding W4Top}"
Title="{x:Static p:Resources.textChangeHistory}" Height="450" Width="800" Loaded="Window_Loaded">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="28" />
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Auto" Margin="2">
<StackPanel x:Name="stackPanel"/>
</ScrollViewer>
<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

@ -0,0 +1,150 @@
// Copyright (c) 2024- schick Informatik
// Description: Window to show (complete) list of current shipcall histories
//
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
{
/// <summary>
/// Interaction logic for HistoryDialog.xaml
/// </summary>
public partial class HistoryDialog : Window
{
#region Fields
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
#region Construction
public HistoryDialog(ConcurrentDictionary<int, ShipcallControlModel> shipcalls, StaticApi staticApi)
{
InitializeComponent();
_shipcalls = shipcalls;
_staticApi = staticApi;
}
#endregion
#region event handler
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Mouse.OverrideCursor = Cursors.Wait;
RefreshHistory();
}
private void buttonClose_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
private void checkboxMyOwnOnly_Checked(object sender, RoutedEventArgs e)
{
Mouse.OverrideCursor = Cursors.Wait;
RefreshHistory();
}
#endregion
#region private methods
private async void RefreshHistory()
{
List<History> allHistories = new();
try
{
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());
}
}
bool FilterShipcall(int shipcallId)
{
bool result = true;
if (shipcallId < 0) return result;
if(_shipcalls.TryGetValue(shipcallId, out ShipcallControlModel? scm))
{
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

@ -13,15 +13,10 @@ namespace BreCalClient
string Title { get; set; } string Title { get; set; }
Extensions.TypeEnum CallType { get; set; } ShipcallControlModel ShipcallModel { get; set; }
bool? ShowDialog(); bool? ShowDialog();
} }
internal interface IEditShipcallTimesControl : IEditTimesControl
{
ShipcallControlModel ShipcallModel { get; set; }
}
} }

View File

@ -106,14 +106,19 @@
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="120" /> <ColumnDefinition Width="120" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="100" /> <ColumnDefinition Width="80" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="120" /> <ColumnDefinition Width="120" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="30" /> <ColumnDefinition Width="26" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="26" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="26" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="100" /> <ColumnDefinition Width="100" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
</Grid> </Grid>
@ -132,15 +137,30 @@
</StatusBarItem> </StatusBarItem>
<Separator Grid.Column="5"/> <Separator Grid.Column="5"/>
<StatusBarItem Grid.Column="6"> <StatusBarItem Grid.Column="6">
<Button x:Name="buttonInfo" Content="?" Click="buttonInfo_Click" Width="20" /> <Button x:Name="buttonInfo" Content="?" Click="buttonInfo_Click" Width="20" ToolTip="{x:Static p:Resources.textInfoChangePW}"/>
</StatusBarItem> </StatusBarItem>
<Separator Grid.Column="7"/> <Separator Grid.Column="7"/>
<StatusBarItem Grid.Column="8"> <StatusBarItem Grid.Column="8">
<TextBlock Name="labelStatusBar"></TextBlock> <Button x:Name="buttonHistory" Click="buttonHistory_Click" Width="20" ToolTip="{x:Static p:Resources.textShowHistory}">
<Image Source="./Resources/clock.png"/>
</Button>
</StatusBarItem> </StatusBarItem>
<Separator Grid.Column="9"/> <Separator Grid.Column="9"/>
<StatusBarItem Grid.Column="10"> <StatusBarItem Grid.Column="10">
<ProgressBar Name="generalProgressStatus" Width="90" Height="16"/> <TextBlock Name="labelStatusBar"></TextBlock>
</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="14"/>
<StatusBarItem Grid.Column="15">
<ProgressBar Name="generalProgressStatus" Width="90" Height="16" Foreground="LightGray"/>
</StatusBarItem> </StatusBarItem>
</StatusBar> </StatusBar>
</Grid> </Grid>

View File

@ -1,6 +1,6 @@
// Copyright (c) 2023 schick Informatik // Copyright (c) 2023 schick Informatik
// Description: Bremen calling main window // Description: Bremen calling main window
// //
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -18,11 +18,12 @@ using BreCalClient.misc.Model;
using static BreCalClient.Extensions; using static BreCalClient.Extensions;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Security.Principal;
using Polly; using Polly;
using System.Net.Http; using System.Net.Http;
using System.Net; using System.Net;
using Polly.Retry; using System.Windows.Input;
namespace BreCalClient namespace BreCalClient
{ {
@ -32,20 +33,25 @@ namespace BreCalClient
public partial class MainWindow : Window public partial class MainWindow : Window
{ {
private readonly ILog _log = LogManager.GetLogger(typeof(MainWindow)); private readonly ILog _log = LogManager.GetLogger(typeof(MainWindow));
private const int SHIPCALL_UPDATE_INTERVAL_SECONDS = 30; private const int SHIPCALL_UPDATE_INTERVAL_SECONDS = 30;
private const int PROGRESS_STEPS = 50;
#region Fields #region Fields
private static Int32 _uiUpdateRunning = 0; private static int _uiUpdateRunning = 0;
private Credentials? _credentials; private Credentials? _credentials;
private readonly ConcurrentDictionary<int, ShipcallControlModel> _allShipcallsDict = new(); private readonly ConcurrentDictionary<int, ShipcallControlModel> _allShipcallsDict = new();
private readonly ConcurrentDictionary<int, ShipcallControl> _allShipCallsControlDict = new(); private readonly ConcurrentDictionary<int, ShipcallControl> _allShipCallsControlDict = new();
private readonly List<ShipcallControlModel> _visibleControlModels = new(); private readonly List<ShipcallControlModel> _visibleControlModels = new();
private readonly ShipcallApi _shipcallApi;
private readonly UserApi _userApi;
private readonly TimesApi _timesApi;
private readonly StaticApi _staticApi;
private readonly ShipApi _shipApi;
private readonly DefaultApi _api;
private CancellationTokenSource _tokenSource = new(); private CancellationTokenSource _tokenSource = new();
private LoginResult? _loginResult; private LoginResult? _loginResult;
private bool _refreshImmediately = false; private bool _refreshImmediately = false;
@ -56,6 +62,7 @@ namespace BreCalClient
// private bool _filterChanged = false; // private bool _filterChanged = false;
// private bool _sequenceChanged = false; // private bool _sequenceChanged = false;
private HistoryDialog? _historyDialog;
#endregion #endregion
@ -75,8 +82,16 @@ namespace BreCalClient
public MainWindow() public MainWindow()
{ {
InitializeComponent(); InitializeComponent();
_api = new DefaultApi(Properties.Settings.Default.API_URL); _userApi = new UserApi(Properties.Settings.Default.API_URL);
_api.Configuration.ApiKeyPrefix["Authorization"] = "Bearer"; _userApi.Configuration.ApiKeyPrefix["Authorization"] = "Bearer";
_shipcallApi = new ShipcallApi(Properties.Settings.Default.API_URL);
_shipcallApi.Configuration.ApiKeyPrefix["Authorization"] = "Bearer";
_timesApi = new TimesApi(Properties.Settings.Default.API_URL);
_timesApi.Configuration.ApiKeyPrefix["Authorization"] = "Bearer";
_staticApi = new StaticApi(Properties.Settings.Default.API_URL);
_staticApi.Configuration.ApiKeyPrefix["Authorization"] = "Bearer";
_shipApi = new ShipApi(Properties.Settings.Default.API_URL);
_shipApi.Configuration.ApiKeyPrefix["Authorization"] = "Bearer";
const int maxDelayInMilliseconds = 32 * 1000; const int maxDelayInMilliseconds = 32 * 1000;
var jitterer = new Random(); var jitterer = new Random();
@ -95,10 +110,11 @@ namespace BreCalClient
}, },
onRetry: (resp, timespan, context) => onRetry: (resp, timespan, context) =>
{ {
this.RefreshToken(null); this.RefreshToken();
Trace.WriteLine("token refreshed"); Trace.WriteLine("token refreshed");
}); });
RetryConfiguration.AsyncRetryPolicy = retryPolicy; RetryConfiguration.AsyncRetryPolicy = retryPolicy;
this.generalProgressStatus.Maximum = PROGRESS_STEPS;
} }
#endregion #endregion
@ -111,14 +127,14 @@ namespace BreCalClient
labelVersion.Text = "V. " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; labelVersion.Text = "V. " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
if (!string.IsNullOrEmpty(Properties.Settings.Default.APP_TITLE)) if (!string.IsNullOrEmpty(Properties.Settings.Default.APP_TITLE))
this.Title = Properties.Settings.Default.APP_TITLE; this.Title = Properties.Settings.Default.APP_TITLE;
searchFilterControl.SearchFilterChanged += SearchFilterControl_SearchFilterChanged; searchFilterControl.SearchFilterChanged += SearchFilterControl_SearchFilterChanged;
searchFilterControl.LogoImageClicked += () => searchFilterControl.LogoImageClicked += () =>
{ {
Process.Start("explorer", Properties.Settings.Default.LOGO_IMAGE_URL); Process.Start("explorer", Properties.Settings.Default.LOGO_IMAGE_URL);
}; };
this.comboBoxSortOrder.ItemsSource = Enum.GetValues(typeof(Extensions.SortOrder)); this.comboBoxSortOrder.ItemsSource = Enum.GetValues(typeof(Extensions.SortOrder));
this.comboBoxSortOrder.SelectedIndex = (int)_sortOrder; this.comboBoxSortOrder.SelectedIndex = (int)_sortOrder;
} }
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{ {
@ -140,13 +156,18 @@ namespace BreCalClient
try try
{ {
_loginResult = await _api.LoginPostAsync(_credentials); _loginResult = await _userApi.LoginAsync(_credentials);
if (_loginResult != null) if (_loginResult != null)
{ {
if (_loginResult.Id > 0) if (_loginResult.Id > 0)
{ {
Mouse.OverrideCursor = Cursors.Wait;
this.busyIndicator.IsBusy = false; this.busyIndicator.IsBusy = false;
this._api.Configuration.ApiKey["Authorization"] = _loginResult.Token; this._userApi.Configuration.ApiKey["Authorization"] = _loginResult.Token;
this._shipcallApi.Configuration.ApiKey["Authorization"] = _loginResult.Token;
this._timesApi.Configuration.ApiKey["Authorization"] = _loginResult.Token;
this._staticApi.Configuration.ApiKey["Authorization"] = _loginResult.Token;
this._shipApi.Configuration.ApiKey["Authorization"] = _loginResult.Token;
this.LoadStaticLists(); this.LoadStaticLists();
this.labelUsername.Text = $"{_loginResult.FirstName} {_loginResult.LastName}"; this.labelUsername.Text = $"{_loginResult.FirstName} {_loginResult.LastName}";
} }
@ -174,17 +195,21 @@ namespace BreCalClient
} }
} }
private bool RefreshToken(object? state) private bool RefreshToken()
{ {
bool result = false; bool result = false;
try try
{ {
_loginResult = _api.LoginPost(_credentials); _loginResult = _userApi.Login(_credentials);
if (_loginResult != null) if (_loginResult != null)
{ {
if (_loginResult.Id > 0) if (_loginResult.Id > 0)
{ {
this._api.Configuration.ApiKey["Authorization"] = _loginResult.Token; this._userApi.Configuration.ApiKey["Authorization"] = _loginResult.Token;
this._timesApi.Configuration.ApiKey["Authorization"] = _loginResult.Token;
this._shipcallApi.Configuration.ApiKey["Authorization"] = _loginResult.Token;
this._staticApi.Configuration.ApiKey["Authorization"] = _loginResult.Token;
this._shipApi.Configuration.ApiKey["Authorization"] = _loginResult.Token;
result = true; result = true;
} }
} }
@ -207,12 +232,16 @@ namespace BreCalClient
private void buttonNew_Click(object sender, RoutedEventArgs e) private void buttonNew_Click(object sender, RoutedEventArgs e)
{ {
NewWithModel(null); NewWithModel(null);
} }
private void NewWithModel(ShipcallControlModel? model) private void NewWithModel(ShipcallControlModel? model)
{ {
EditShipcallControl esc = new(); EditShipcallControl esc = new()
{
ShipEditingEnabled = App.Participant.IsTypeFlagSet(Extensions.ParticipantType.BSMD),
ShipApi = _shipApi
};
if (model != null) if (model != null)
esc.ShipcallModel = model; esc.ShipcallModel = model;
@ -228,7 +257,7 @@ namespace BreCalClient
esc.ShipcallModel.Shipcall?.Participants.Add(pa); esc.ShipcallModel.Shipcall?.Participants.Add(pa);
try try
{ {
this._api.ShipcallsPost(esc.ShipcallModel.Shipcall); // save new ship call this._shipcallApi.ShipcallCreate(esc.ShipcallModel.Shipcall); // save new ship call
this.AddShipcall(esc.ShipcallModel); this.AddShipcall(esc.ShipcallModel);
} }
catch (Exception ex) catch (Exception ex)
@ -240,11 +269,15 @@ namespace BreCalClient
_tokenSource.Cancel(); // force timer loop end _tokenSource.Cancel(); // force timer loop end
// if this was an arrival, create the matching departure call and open it // if this was an arrival, create the matching departure call and open it
if (esc.ShipcallModel.Shipcall?.Type == (int)Extensions.TypeEnum.Incoming) if (esc.ShipcallModel.Shipcall?.Type == ShipcallType.Arrival)
{ {
ShipcallControlModel scmOut = new(); ShipcallControlModel scmOut = new()
scmOut.Shipcall = new(); {
scmOut.Shipcall.Type = (int)Extensions.TypeEnum.Outgoing; Shipcall = new()
{
Type = ShipcallType.Departure
}
};
scmOut.Shipcall.ShipId = esc.ShipcallModel.Shipcall.ShipId; scmOut.Shipcall.ShipId = esc.ShipcallModel.Shipcall.ShipId;
scmOut.Ship = esc.ShipcallModel.Ship; scmOut.Ship = esc.ShipcallModel.Ship;
DateTime eta = esc.ShipcallModel.Shipcall?.Eta ?? DateTime.Now; DateTime eta = esc.ShipcallModel.Shipcall?.Eta ?? DateTime.Now;
@ -257,7 +290,11 @@ namespace BreCalClient
foreach(ParticipantType pType in esc.ShipcallModel.AssignedParticipants.Keys) foreach(ParticipantType pType in esc.ShipcallModel.AssignedParticipants.Keys)
scmOut.AssignedParticipants[pType] = esc.ShipcallModel.AssignedParticipants[pType]; scmOut.AssignedParticipants[pType] = esc.ShipcallModel.AssignedParticipants[pType];
} }
NewWithModel(scmOut);
this.Dispatcher.Invoke(() =>
{
NewWithModel(scmOut);
});
} }
} }
} }
@ -265,8 +302,10 @@ namespace BreCalClient
private void buttonInfo_Click(object sender, RoutedEventArgs e) private void buttonInfo_Click(object sender, RoutedEventArgs e)
{ {
AboutDialog ad = new(); AboutDialog ad = new()
ad.LoginResult = this._loginResult; {
LoginResult = this._loginResult
};
ad.ChangePasswordRequested += async (oldPw, newPw) => ad.ChangePasswordRequested += async (oldPw, newPw) =>
{ {
if (_loginResult != null) if (_loginResult != null)
@ -283,7 +322,7 @@ namespace BreCalClient
}; };
try try
{ {
await _api.UserPutAsync(ud); await _userApi.UserUpdateAsync(ud);
MessageBox.Show(BreCalClient.Resources.Resources.textPasswordChanged, BreCalClient.Resources.Resources.textConfirmation, MessageBoxButton.OK, MessageBoxImage.Information); MessageBox.Show(BreCalClient.Resources.Resources.textPasswordChanged, BreCalClient.Resources.Resources.textConfirmation, MessageBoxButton.OK, MessageBoxImage.Information);
} }
catch (Exception ex) catch (Exception ex)
@ -324,25 +363,51 @@ namespace BreCalClient
this.UpdateUI(); this.UpdateUI();
} }
#endregion private void buttonHistory_Click(object sender, RoutedEventArgs e)
{
if(_historyDialog == null)
{
_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
{
_historyDialog.Activate();
}
}
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 #region network operations
private async void LoadStaticLists() private async void LoadStaticLists()
{ {
BreCalLists.InitializeBerths(await _api.BerthsGetAsync()); BreCalLists.InitializeBerths(await _staticApi.BerthsGetAsync());
BreCalLists.InitializeShips(await _api.ShipsGetAsync()); BreCalLists.InitializeShips(await _shipApi.ShipsGetAsync());
BreCalLists.InitializeParticipants(await _api.ParticipantsGetAsync()); BreCalLists.InitializeParticipants(await _staticApi.ParticipantsGetAsync());
this.searchFilterControl.SetBerths(BreCalLists.Berths); this.searchFilterControl.SetBerths(BreCalLists.Berths);
foreach (Participant participant in BreCalLists.Participants) foreach (Participant participant in BreCalLists.Participants)
{ {
if (_loginResult?.ParticipantId == participant.Id) if (_loginResult?.ParticipantId == participant.Id)
{ {
App.Participant = participant; App.Participant = participant;
EnableControlsForParticipant(); EnableControlsForParticipant();
} }
} }
this.searchFilterControl.SetAgencies(BreCalLists.Participants_Agent); this.searchFilterControl.SetAgencies(BreCalLists.Participants_Agent);
@ -355,7 +420,7 @@ namespace BreCalClient
} }
_ = Task.Run(() => RefreshShipcalls()); _ = Task.Run(() => RefreshShipcalls());
} }
public async Task RefreshShipcalls() public async Task RefreshShipcalls()
{ {
@ -372,18 +437,19 @@ namespace BreCalClient
try try
{ {
if(this.searchPastDays != 0) if(this.searchPastDays != 0)
shipcalls = await _api.ShipcallsGetAsync(this.searchPastDays); shipcalls = await _shipcallApi.ShipcallsGetAsync(this.searchPastDays);
else else
shipcalls = await _api.ShipcallsGetAsync(); shipcalls = await _shipcallApi.ShipcallsGetAsync();
this.Dispatcher.Invoke(new Action(() => this.Dispatcher.Invoke(new Action(() =>
{ {
labelGeneralStatus.Text = $"Connection {ConnectionStatus.SUCCESSFUL}"; labelGeneralStatus.Text = $"Connection {ConnectionStatus.SUCCESSFUL}";
labelGeneralStatus.Text = $"Ok"; labelLatestUpdate.Text = $"Last update: {DateTime.Now.ToLongTimeString()}";
generalProgressStatus.Value = 0;
})); }));
} }
catch (Exception ex) catch (Exception ex)
{ {
this.Dispatcher.Invoke(new Action(() => this.Dispatcher.Invoke(new Action(() =>
{ {
labelGeneralStatus.Text = $"Connection {ConnectionStatus.FAILED}"; labelGeneralStatus.Text = $"Connection {ConnectionStatus.FAILED}";
@ -392,7 +458,7 @@ namespace BreCalClient
if (ex.Message.Contains("access", StringComparison.OrdinalIgnoreCase)) if (ex.Message.Contains("access", StringComparison.OrdinalIgnoreCase))
{ {
this.RefreshToken(null); this.RefreshToken();
} }
} }
@ -401,7 +467,7 @@ namespace BreCalClient
foreach (Shipcall shipcall in shipcalls) foreach (Shipcall shipcall in shipcalls)
{ {
// load times for each shipcall // load times for each shipcall
List<Times> currentTimes = await _api.TimesGetAsync(shipcall.Id); List<Times> currentTimes = await _timesApi.TimesGetAsync(shipcall.Id);
if(!_allShipcallsDict.ContainsKey(shipcall.Id)) if(!_allShipcallsDict.ContainsKey(shipcall.Id))
{ {
@ -410,7 +476,7 @@ namespace BreCalClient
{ {
Shipcall = shipcall, Shipcall = shipcall,
Times = currentTimes Times = currentTimes
}; };
this.AddShipcall(scm); this.AddShipcall(scm);
} }
else else
@ -419,8 +485,8 @@ namespace BreCalClient
_allShipcallsDict[shipcall.Id].Shipcall = shipcall; _allShipcallsDict[shipcall.Id].Shipcall = shipcall;
_allShipcallsDict[shipcall.Id].Times = currentTimes; _allShipcallsDict[shipcall.Id].Times = currentTimes;
UpdateShipcall(_allShipcallsDict[shipcall.Id]); UpdateShipcall(_allShipcallsDict[shipcall.Id]);
} }
} }
List<int> existingIds = new(this._allShipcallsDict.Keys); List<int> existingIds = new(this._allShipcallsDict.Keys);
@ -433,12 +499,20 @@ namespace BreCalClient
} }
this.FilterShipcalls(); this.FilterShipcalls();
this.UpdateUI(); this.UpdateUI();
} }
try try
{ {
await Task.Delay(TimeSpan.FromSeconds(SHIPCALL_UPDATE_INTERVAL_SECONDS), _tokenSource.Token); double interval = (double) SHIPCALL_UPDATE_INTERVAL_SECONDS / PROGRESS_STEPS;
for (int i = 0; i < PROGRESS_STEPS; i++)
{
await Task.Delay(TimeSpan.FromSeconds(interval), _tokenSource.Token);
this.Dispatcher.Invoke(new Action(() =>
{
this.generalProgressStatus.Value = i;
}));
}
} }
catch(TaskCanceledException) { } catch(TaskCanceledException) { }
} }
@ -456,7 +530,8 @@ namespace BreCalClient
Shipcall shipcall = scm.Shipcall; Shipcall shipcall = scm.Shipcall;
if (BreCalLists.ShipLookupDict.ContainsKey(shipcall.ShipId)) if (BreCalLists.ShipLookupDict.ContainsKey(shipcall.ShipId))
scm.Ship = BreCalLists.ShipLookupDict[shipcall.ShipId].Ship; scm.Ship = BreCalLists.ShipLookupDict[shipcall.ShipId].Ship;
if (shipcall.Type == 1)
if (shipcall.Type == ShipcallType.Arrival)
{ {
if (BreCalLists.BerthLookupDict.ContainsKey(shipcall.ArrivalBerthId ?? 0)) if (BreCalLists.BerthLookupDict.ContainsKey(shipcall.ArrivalBerthId ?? 0))
scm.Berth = BreCalLists.BerthLookupDict[shipcall.ArrivalBerthId ?? 0].Name; scm.Berth = BreCalLists.BerthLookupDict[shipcall.ArrivalBerthId ?? 0].Name;
@ -472,8 +547,8 @@ namespace BreCalClient
{ {
ShipcallControl sc = new() ShipcallControl sc = new()
{ {
Height = 120, Height = 135,
ShipcallControlModel = scm ShipcallControlModel = scm
}; };
sc.EditTimesRequested += Sc_EditTimesRequested; sc.EditTimesRequested += Sc_EditTimesRequested;
sc.EditRequested += Sc_EditRequested; sc.EditRequested += Sc_EditRequested;
@ -481,7 +556,7 @@ namespace BreCalClient
sc.RefreshData(); sc.RefreshData();
this._allShipCallsControlDict[scm.Shipcall.Id] = sc; this._allShipCallsControlDict[scm.Shipcall.Id] = sc;
}); });
} }
private static void UpdateShipcall(ShipcallControlModel scm) private static void UpdateShipcall(ShipcallControlModel scm)
{ {
@ -489,7 +564,8 @@ namespace BreCalClient
Shipcall shipcall = scm.Shipcall; Shipcall shipcall = scm.Shipcall;
if (BreCalLists.ShipLookupDict.ContainsKey(shipcall.ShipId)) if (BreCalLists.ShipLookupDict.ContainsKey(shipcall.ShipId))
scm.Ship = BreCalLists.ShipLookupDict[shipcall.ShipId].Ship; scm.Ship = BreCalLists.ShipLookupDict[shipcall.ShipId].Ship;
if (shipcall.Type == 1)
if (shipcall.Type == ShipcallType.Arrival)
{ {
if (BreCalLists.BerthLookupDict.ContainsKey(shipcall.ArrivalBerthId ?? 0)) if (BreCalLists.BerthLookupDict.ContainsKey(shipcall.ArrivalBerthId ?? 0))
scm.Berth = BreCalLists.BerthLookupDict[shipcall.ArrivalBerthId ?? 0].Name; scm.Berth = BreCalLists.BerthLookupDict[shipcall.ArrivalBerthId ?? 0].Name;
@ -513,7 +589,7 @@ namespace BreCalClient
_visibleControlModels.Remove(removeModel); _visibleControlModels.Remove(removeModel);
this._allShipCallsControlDict.Remove(shipcallId, out _); this._allShipCallsControlDict.Remove(shipcallId, out _);
this._allShipcallsDict.Remove(shipcallId, out _); this._allShipcallsDict.Remove(shipcallId, out _);
} }
private void FilterShipcalls() private void FilterShipcalls()
@ -533,6 +609,8 @@ namespace BreCalClient
else else
{ {
searchPastDays = 0; searchPastDays = 0;
if (sfm.EtaFrom == null)
sfm.EtaFrom = DateTime.Now.AddDays(-2);
} }
this._visibleControlModels.Clear(); this._visibleControlModels.Clear();
@ -543,8 +621,8 @@ namespace BreCalClient
if(sfm.Berths.Count > 0 ) if(sfm.Berths.Count > 0 )
{ {
this._visibleControlModels.RemoveAll(x => (!sfm.Berths.Contains((x.Shipcall?.ArrivalBerthId) ?? -1) && (x.Shipcall?.Type == (int) Extensions.TypeEnum.Incoming)) || this._visibleControlModels.RemoveAll(x => (!sfm.Berths.Contains((x.Shipcall?.ArrivalBerthId) ?? -1) && (x.Shipcall?.Type == ShipcallType.Arrival)) ||
(!sfm.Berths.Contains((x.Shipcall?.DepartureBerthId) ?? -1) && (x.Shipcall?.Type != (int) Extensions.TypeEnum.Incoming))); (!sfm.Berths.Contains((x.Shipcall?.DepartureBerthId) ?? -1) && (x.Shipcall?.Type != ShipcallType.Arrival)));
} }
if(sfm.Agencies.Count > 0 ) if(sfm.Agencies.Count > 0 )
@ -557,12 +635,12 @@ namespace BreCalClient
return !sfm.Agencies.Contains(agency.Id); return !sfm.Agencies.Contains(agency.Id);
} }
return true; return true;
}); });
} }
if(sfm.Categories.Count > 0 ) if(sfm.Categories.Count > 0 )
{ {
_ = this._visibleControlModels.RemoveAll(x => !sfm.Categories.Contains((x.Shipcall?.Type) ?? -1)); _ = this._visibleControlModels.RemoveAll(x => { if (x.Shipcall == null) return false; else return !sfm.Categories.Contains(x.Shipcall.Type); });
} }
if(!string.IsNullOrEmpty(sfm.SearchString)) if(!string.IsNullOrEmpty(sfm.SearchString))
@ -582,40 +660,95 @@ namespace BreCalClient
if(sfm.EtaFrom != null) if(sfm.EtaFrom != null)
{ {
_ = this._visibleControlModels.RemoveAll(x => x.Shipcall?.Eta < sfm.EtaFrom); _ = this._visibleControlModels.RemoveAll(x =>
{
Times? t = x.GetTimesForParticipantType(ParticipantType.AGENCY);
switch (x.Shipcall?.Type)
{
case ShipcallType.Arrival:
{
if ((t != null) && t.EtaBerth.HasValue) return t.EtaBerth.Value < sfm.EtaFrom;
return x.Shipcall?.Eta < sfm.EtaFrom;
}
default: // Shifting / Departing
{
if ((t != null) && t.EtdBerth.HasValue) return t.EtdBerth.Value < sfm.EtaFrom;
return x.Shipcall?.Etd < sfm.EtaFrom;
}
}
});
} }
if(sfm.EtaTo != null) if(sfm.EtaTo != null)
{ {
_ = this._visibleControlModels.RemoveAll(x => x.Shipcall?.Eta > sfm.EtaTo); _ = this._visibleControlModels.RemoveAll(x =>
{
Times? t = x.GetTimesForParticipantType(ParticipantType.AGENCY);
DateTime refValue = sfm.EtaTo.Value.AddMinutes(1439); // 23:59
switch (x.Shipcall?.Type)
{
case ShipcallType.Arrival:
{
if ((t != null) && t.EtaBerth.HasValue) return t.EtaBerth.Value > refValue;
return x.Shipcall?.Eta > refValue;
}
default: // Shifting / Departing
{
if ((t != null) && t.EtdBerth.HasValue) return t.EtdBerth.Value > refValue;
return x.Shipcall?.Etd > refValue;
}
}
});
}
if(sfm.MineOnly ?? false)
{
_ = _visibleControlModels.RemoveAll(x =>
{
bool contained = false;
foreach(ParticipantAssignment p in x.AssignedParticipants.Values)
{
if(p.ParticipantId.Equals(App.Participant.Id))
{
contained = true; break;
}
}
return !contained;
});
} }
if(!_showCanceled ?? true) // canceled calls are filtered by default if(!_showCanceled ?? true) // canceled calls are filtered by default
{ {
_ = this._visibleControlModels.RemoveAll(x => x.Shipcall?.Canceled ?? false); _ = this._visibleControlModels.RemoveAll(x => x.Shipcall?.Canceled ?? false);
} }
switch(this._sortOrder) switch(this._sortOrder)
{ {
case Extensions.SortOrder.SHIP_NAME: case Extensions.SortOrder.SHIP_NAME:
this._visibleControlModels.Sort((x, y) => { if (x.Ship == null) return 0; if (y.Ship == null) return 0; return x.Ship.Name.CompareTo(y.Ship.Name); }); this._visibleControlModels.Sort((x, y) => { if (x.Ship == null) return 0; if (y.Ship == null) return 0; return x.Ship.Name.CompareTo(y.Ship.Name); });
break; break;
case Extensions.SortOrder.MODIFIED: case Extensions.SortOrder.MODIFIED:
this._visibleControlModels.Sort((x, y) => { if (x.Shipcall == null) return 0; if (y.Shipcall == null) return 0; return DateTime.Compare(x.Shipcall.Modified ?? x.Shipcall.Created, y.Shipcall.Modified ?? x.Shipcall.Created); }); this._visibleControlModels.Sort((x, y) => { if (x.Shipcall == null) return 0; if (y.Shipcall == null) return 0; return DateTime.Compare(x.Shipcall.Modified ?? x.Shipcall.Created, y.Shipcall.Modified ?? x.Shipcall.Created); });
break; break;
case Extensions.SortOrder.ETA_ETD: case Extensions.SortOrder.ETA_ETD:
this._visibleControlModels.Sort((x, y) => this._visibleControlModels.Sort((x, y) =>
{ {
if (x.Shipcall == null) return 0; if (x.Shipcall == null) return 0;
if (y.Shipcall == null) return 0; if (y.Shipcall == null) return 0;
DateTime xDate = (x.Shipcall.Type == (int) Extensions.TypeEnum.Incoming) ? x.Eta ?? DateTime.Now : x.Etd ?? DateTime.Now; DateTime xDate = (x.Shipcall.Type == ShipcallType.Arrival) ? x.Eta ?? DateTime.Now : x.Etd ?? DateTime.Now;
DateTime yDate = (y.Shipcall.Type == (int) Extensions.TypeEnum.Incoming) ? y.Eta ?? DateTime.Now : y.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); return DateTime.Compare(xDate, yDate);
}); });
break; break;
default: default:
break; break;
} }
} }
@ -652,6 +785,7 @@ namespace BreCalClient
_uiUpdateRunning = 0; _uiUpdateRunning = 0;
} }
Mouse.OverrideCursor = null;
})); }));
} }
@ -660,12 +794,14 @@ namespace BreCalClient
#region control event handler #region control event handler
private async void Sc_EditRequested(ShipcallControl obj) private async void Sc_EditRequested(ShipcallControl obj)
{ {
if (obj.ShipcallControlModel != null) if (obj.ShipcallControlModel != null)
{ {
EditShipcallControl esc = new() EditShipcallControl esc = new()
{ {
ShipcallModel = obj.ShipcallControlModel ShipcallModel = obj.ShipcallControlModel,
ShipApi = _shipApi,
ShipEditingEnabled = App.Participant.IsTypeFlagSet(Extensions.ParticipantType.BSMD)
}; };
if(esc.ShowDialog() ?? false) if(esc.ShowDialog() ?? false)
@ -673,10 +809,10 @@ namespace BreCalClient
try try
{ {
obj.ShipcallControlModel.Shipcall?.Participants.Clear(); obj.ShipcallControlModel.Shipcall?.Participants.Clear();
obj.ShipcallControlModel.UpdateTimesAssignments(this._api); obj.ShipcallControlModel.UpdateTimesAssignments(this._timesApi);
foreach(ParticipantAssignment pa in obj.ShipcallControlModel.AssignedParticipants.Values) foreach(ParticipantAssignment pa in obj.ShipcallControlModel.AssignedParticipants.Values)
obj.ShipcallControlModel.Shipcall?.Participants.Add(pa); obj.ShipcallControlModel.Shipcall?.Participants.Add(pa);
await _api.ShipcallsPutAsync(obj.ShipcallControlModel.Shipcall); await _shipcallApi.ShipcallUpdateAsync(obj.ShipcallControlModel.Shipcall);
obj.RefreshData(); obj.RefreshData();
_refreshImmediately = true; _refreshImmediately = true;
_tokenSource.Cancel(); _tokenSource.Cancel();
@ -689,7 +825,7 @@ namespace BreCalClient
} }
} }
private async void Sc_EditTimesRequested(ShipcallControl obj, Times? times, Extensions.ParticipantType participantType) private async void Sc_EditTimesRequested(ShipcallControl obj, Times? times, ParticipantType participantType)
{ {
if( obj.ShipcallControlModel == null) { return; } if( obj.ShipcallControlModel == null) { return; }
@ -698,15 +834,13 @@ namespace BreCalClient
// show a dialog that lets the user create / update times for the given shipcall // show a dialog that lets the user create / update times for the given shipcall
IEditTimesControl etc = (participantType == ParticipantType.TERMINAL) ? new EditTimesTerminalControl() : new EditTimesControl(); IEditTimesControl etc = (participantType == ParticipantType.TERMINAL) ? new EditTimesTerminalControl() : new EditTimesControl();
etc.Title = obj.ShipcallControlModel.Title; etc.Title = obj.ShipcallControlModel.Title;
etc.ShipcallModel = obj.ShipcallControlModel;
if(obj.ShipcallControlModel.Shipcall != null)
etc.CallType = (TypeEnum) obj.ShipcallControlModel.Shipcall.Type;
bool wasEdit = false; bool wasEdit = false;
if (times != null) if (times != null)
{ {
etc.Times = times; etc.Times = times;
wasEdit = true; wasEdit = true;
} }
else else
{ {
@ -725,7 +859,7 @@ namespace BreCalClient
{ {
if (wasEdit) if (wasEdit)
{ {
await _api.TimesPutAsync(etc.Times); await _timesApi.TimesUpdateAsync(etc.Times);
} }
else else
{ {
@ -734,7 +868,7 @@ namespace BreCalClient
{ {
etc.Times.ShipcallId = obj.ShipcallControlModel.Shipcall.Id; etc.Times.ShipcallId = obj.ShipcallControlModel.Shipcall.Id;
} }
Id apiResultId = await _api.TimesPostAsync(etc.Times); Id apiResultId = await _timesApi.TimesCreateAsync(etc.Times);
etc.Times.Id = apiResultId.VarId; etc.Times.Id = apiResultId.VarId;
obj.ShipcallControlModel?.Times.Add(etc.Times); obj.ShipcallControlModel?.Times.Add(etc.Times);
} }
@ -750,16 +884,16 @@ namespace BreCalClient
private async void Sc_EditAgencyRequested(ShipcallControl sc, Times? times) private async void Sc_EditAgencyRequested(ShipcallControl sc, Times? times)
{ {
IEditShipcallTimesControl? editControl = null; IEditTimesControl? editControl = null;
switch(sc.ShipcallControlModel?.Shipcall?.Type) switch(sc.ShipcallControlModel?.Shipcall?.Type)
{ {
case (int)TypeEnum.Incoming: case ShipcallType.Arrival:
editControl = new EditTimesAgencyIncomingControl(); editControl = new EditTimesAgencyIncomingControl();
break; break;
case (int)TypeEnum.Outgoing: case ShipcallType.Departure:
editControl = new EditTimesAgencyOutgoingControl(); editControl = new EditTimesAgencyOutgoingControl();
break; break;
case (int)TypeEnum.Shifting: case ShipcallType.Shifting:
editControl = new EditTimesAgencyShiftingControl(); editControl = new EditTimesAgencyShiftingControl();
break; break;
} }
@ -772,7 +906,7 @@ namespace BreCalClient
{ {
editControl.Times = times; editControl.Times = times;
wasEdit = true; wasEdit = true;
} }
else else
{ {
if(editControl.ShipcallModel.AssignedParticipants.ContainsKey(ParticipantType.AGENCY)) if(editControl.ShipcallModel.AssignedParticipants.ContainsKey(ParticipantType.AGENCY))
@ -783,7 +917,7 @@ namespace BreCalClient
{ {
try try
{ {
sc.ShipcallControlModel?.UpdateTimesAssignments(_api); // if the agent changed the assignment of the participant to another sc.ShipcallControlModel?.UpdateTimesAssignments(_timesApi); // if the agent changed the assignment of the participant to another
// always try to be the agent, even if we are BSMD // always try to be the agent, even if we are BSMD
if (editControl.ShipcallModel.AssignedParticipants.ContainsKey(ParticipantType.AGENCY)) if (editControl.ShipcallModel.AssignedParticipants.ContainsKey(ParticipantType.AGENCY))
@ -797,15 +931,15 @@ namespace BreCalClient
if (wasEdit) if (wasEdit)
{ {
await _api.TimesPutAsync(editControl.Times); await _timesApi.TimesUpdateAsync(editControl.Times);
} }
else else
{ {
if ((sc.ShipcallControlModel != null) && (sc.ShipcallControlModel.Shipcall != null)) if ((sc.ShipcallControlModel != null) && (sc.ShipcallControlModel.Shipcall != null))
{ {
editControl.Times.ShipcallId = sc.ShipcallControlModel.Shipcall.Id; editControl.Times.ShipcallId = sc.ShipcallControlModel.Shipcall.Id;
} }
Id resultAPI_Id = await _api.TimesPostAsync(editControl.Times); Id resultAPI_Id = await _timesApi.TimesCreateAsync(editControl.Times);
editControl.Times.Id = resultAPI_Id.VarId; editControl.Times.Id = resultAPI_Id.VarId;
sc.ShipcallControlModel?.Times.Add(editControl.Times); sc.ShipcallControlModel?.Times.Add(editControl.Times);
@ -813,7 +947,7 @@ namespace BreCalClient
editControl.ShipcallModel.Shipcall?.Participants.Clear(); editControl.ShipcallModel.Shipcall?.Participants.Clear();
foreach (ParticipantAssignment pa in editControl.ShipcallModel.AssignedParticipants.Values) foreach (ParticipantAssignment pa in editControl.ShipcallModel.AssignedParticipants.Values)
editControl.ShipcallModel.Shipcall?.Participants.Add(pa); editControl.ShipcallModel.Shipcall?.Participants.Add(pa);
await _api.ShipcallsPutAsync(editControl.ShipcallModel.Shipcall); await _shipcallApi.ShipcallUpdateAsync(editControl.ShipcallModel.Shipcall);
_refreshImmediately = true; _refreshImmediately = true;
_tokenSource.Cancel(); _tokenSource.Cancel();
} }
@ -846,7 +980,7 @@ namespace BreCalClient
if (App.Participant.IsTypeFlagSet(Extensions.ParticipantType.BSMD)) if (App.Participant.IsTypeFlagSet(Extensions.ParticipantType.BSMD))
this.buttonNew.Visibility = Visibility.Visible; this.buttonNew.Visibility = Visibility.Visible;
} }
private void Hyperlink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e) private void Hyperlink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
{ {
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri) { UseShellExecute = true }); Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri) { UseShellExecute = true });
@ -854,6 +988,6 @@ namespace BreCalClient
} }
#endregion #endregion
} }
} }

View File

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

View File

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

View File

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

View File

@ -142,5 +142,77 @@ namespace BreCalClient.Properties {
this["W1Top"] = value; this["W1Top"] = value;
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]
public double W2Left {
get {
return ((double)(this["W2Left"]));
}
set {
this["W2Left"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]
public double W2Top {
get {
return ((double)(this["W2Top"]));
}
set {
this["W2Top"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]
public double W3Left {
get {
return ((double)(this["W3Left"]));
}
set {
this["W3Left"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]
public double W3Top {
get {
return ((double)(this["W3Top"]));
}
set {
this["W3Top"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]
public double W4Left {
get {
return ((double)(this["W4Left"]));
}
set {
this["W4Left"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]
public double W4Top {
get {
return ((double)(this["W4Top"]));
}
set {
this["W4Top"] = value;
}
}
} }
} }

View File

@ -35,5 +35,23 @@
<Setting Name="W1Top" Type="System.Double" Scope="User"> <Setting Name="W1Top" Type="System.Double" Scope="User">
<Value Profile="(Default)">0</Value> <Value Profile="(Default)">0</Value>
</Setting> </Setting>
<Setting Name="W2Left" Type="System.Double" Scope="User">
<Value Profile="(Default)">0</Value>
</Setting>
<Setting Name="W2Top" Type="System.Double" Scope="User">
<Value Profile="(Default)">0</Value>
</Setting>
<Setting Name="W3Left" Type="System.Double" Scope="User">
<Value Profile="(Default)">0</Value>
</Setting>
<Setting Name="W3Top" Type="System.Double" Scope="User">
<Value Profile="(Default)">0</Value>
</Setting>
<Setting Name="W4Left" Type="System.Double" Scope="User">
<Value Profile="(Default)">0</Value>
</Setting>
<Setting Name="W4Top" Type="System.Double" Scope="User">
<Value Profile="(Default)">0</Value>
</Setting>
</Settings> </Settings>
</SettingsFile> </SettingsFile>

View File

@ -60,6 +60,35 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
public static byte[] _lock {
get {
object obj = ResourceManager.GetObject("_lock", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
public static byte[] add {
get {
object obj = ResourceManager.GetObject("add", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized string similar to Incoming.
/// </summary>
public static string Arrival {
get {
return ResourceManager.GetString("Arrival", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized resource of type System.Byte[]. /// Looks up a localized resource of type System.Byte[].
/// </summary> /// </summary>
@ -180,6 +209,25 @@ 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>
public static byte[] edit {
get {
object obj = ResourceManager.GetObject("edit", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary> /// <summary>
/// Looks up a localized resource of type System.Byte[]. /// Looks up a localized resource of type System.Byte[].
/// </summary> /// </summary>
@ -190,6 +238,16 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
public static byte[] lock_open {
get {
object obj = ResourceManager.GetObject("lock_open", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary> /// <summary>
/// Looks up a localized resource of type System.Byte[]. /// Looks up a localized resource of type System.Byte[].
/// </summary> /// </summary>
@ -200,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> /// <summary>
/// Looks up a localized resource of type System.Byte[]. /// Looks up a localized resource of type System.Byte[].
/// </summary> /// </summary>
@ -220,6 +297,15 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Add.
/// </summary>
public static string textAdd {
get {
return ResourceManager.GetString("textAdd", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Agencies. /// Looks up a localized string similar to Agencies.
/// </summary> /// </summary>
@ -355,6 +441,15 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Change history.
/// </summary>
public static string textChangeHistory {
get {
return ResourceManager.GetString("textChangeHistory", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Change password. /// Looks up a localized string similar to Change password.
/// </summary> /// </summary>
@ -409,6 +504,24 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Delete.
/// </summary>
public static string textDelete {
get {
return ResourceManager.GetString("textDelete", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Deleted.
/// </summary>
public static string textDeleted {
get {
return ResourceManager.GetString("textDeleted", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Departure terminal. /// Looks up a localized string similar to Departure terminal.
/// </summary> /// </summary>
@ -427,6 +540,24 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Edit.
/// </summary>
public static string textEdit {
get {
return ResourceManager.GetString("textEdit", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Edit ship.
/// </summary>
public static string textEditShip {
get {
return ResourceManager.GetString("textEditShip", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Edit ship call. /// Looks up a localized string similar to Edit ship call.
/// </summary> /// </summary>
@ -436,6 +567,15 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Edit ships.
/// </summary>
public static string textEditShips {
get {
return ResourceManager.GetString("textEditShips", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Edit times. /// Looks up a localized string similar to Edit times.
/// </summary> /// </summary>
@ -499,6 +639,15 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Marked as a fixed order.
/// </summary>
public static string textFixedOrder {
get {
return ResourceManager.GetString("textFixedOrder", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to from. /// Looks up a localized string similar to from.
/// </summary> /// </summary>
@ -517,6 +666,15 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Show app info and change user password.
/// </summary>
public static string textInfoChangePW {
get {
return ResourceManager.GetString("textInfoChangePW", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Interval. /// Looks up a localized string similar to Interval.
/// </summary> /// </summary>
@ -562,6 +720,15 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to mine only.
/// </summary>
public static string textMineOnly {
get {
return ResourceManager.GetString("textMineOnly", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Moored in lock. /// Looks up a localized string similar to Moored in lock.
/// </summary> /// </summary>
@ -625,6 +792,15 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Operation.
/// </summary>
public static string textOperation {
get {
return ResourceManager.GetString("textOperation", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Operations end. /// Looks up a localized string similar to Operations end.
/// </summary> /// </summary>
@ -652,6 +828,15 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Participant.
/// </summary>
public static string textParticipant {
get {
return ResourceManager.GetString("textParticipant", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Participants. /// Looks up a localized string similar to Participants.
/// </summary> /// </summary>
@ -841,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> /// <summary>
/// Looks up a localized string similar to Show cancelled calls. /// Looks up a localized string similar to Show cancelled calls.
/// </summary> /// </summary>
@ -850,6 +1044,15 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Show shipcall change history.
/// </summary>
public static string textShowHistory {
get {
return ResourceManager.GetString("textShowHistory", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Sort order. /// Looks up a localized string similar to Sort order.
/// </summary> /// </summary>
@ -886,6 +1089,15 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Timestamp.
/// </summary>
public static string textTimestamp {
get {
return ResourceManager.GetString("textTimestamp", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to to. /// Looks up a localized string similar to to.
/// </summary> /// </summary>
@ -895,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> /// <summary>
/// Looks up a localized string similar to Tug. /// Looks up a localized string similar to Tug.
/// </summary> /// </summary>
@ -904,6 +1143,15 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Tug company.
/// </summary>
public static string textTugCompany {
get {
return ResourceManager.GetString("textTugCompany", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Tug required. /// Looks up a localized string similar to Tug required.
/// </summary> /// </summary>
@ -1065,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> /// <summary>
/// Looks up a localized resource of type System.Byte[]. /// Looks up a localized resource of type System.Byte[].
/// </summary> /// </summary>

View File

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
Version 2.0 Version 2.0
The primary goals of this format is to allow a simple XML format The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes various data types are done through the TypeConverter classes
associated with the data types. associated with the data types.
Example: Example:
... ado.net/XML headers & schema ... ... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader> <resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader> <resheader name="version">2.0</resheader>
@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment> <comment>This is a comment</comment>
</data> </data>
There are any number of "resheader" rows that contain simple There are any number of "resheader" rows that contain simple
name/value pairs. name/value pairs.
Each data row contains a name, and value. The row also contains a Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture. text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the Classes that don't support this are serialized and stored with the
mimetype set. mimetype set.
The mimetype is used for serialized objects, and tells the The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly: extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below. read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64 mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64 mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64 mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter : using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
--> -->
@ -218,7 +218,7 @@
<value>Zeitraum</value> <value>Zeitraum</value>
</data> </data>
<data name="textLengthWidth" xml:space="preserve"> <data name="textLengthWidth" xml:space="preserve">
<value>L/B</value> <value>L/B (m)</value>
</data> </data>
<data name="textLogin" xml:space="preserve"> <data name="textLogin" xml:space="preserve">
<value>Anmelden</value> <value>Anmelden</value>
@ -427,4 +427,73 @@
<data name="textStarboard" xml:space="preserve"> <data name="textStarboard" xml:space="preserve">
<value>Steuerbord</value> <value>Steuerbord</value>
</data> </data>
<data name="textAdd" xml:space="preserve">
<value>Hinzufügen</value>
</data>
<data name="textDelete" xml:space="preserve">
<value>Löschen</value>
</data>
<data name="textEdit" xml:space="preserve">
<value>Bearbeiten</value>
</data>
<data name="textEditShips" xml:space="preserve">
<value>Schiffe anlegen / bearbeiten</value>
</data>
<data name="textDeleted" xml:space="preserve">
<value>Gelöscht</value>
</data>
<data name="textEditShip" xml:space="preserve">
<value>Schiff bearbeiten</value>
</data>
<data name="textTugCompany" xml:space="preserve">
<value>Schlepper-Reederei</value>
</data>
<data name="textChangeHistory" xml:space="preserve">
<value>Verlauf</value>
</data>
<data name="textInfoChangePW" xml:space="preserve">
<value>App Info anzeigen und Passwort ändern</value>
</data>
<data name="textShowHistory" xml:space="preserve">
<value>Änderungshistorie der Anläufe anzeigen</value>
</data>
<data name="textMineOnly" xml:space="preserve">
<value>nur eigene</value>
</data>
<data name="textOperation" xml:space="preserve">
<value>Vorgang</value>
</data>
<data name="textParticipant" xml:space="preserve">
<value>Teilnehmer</value>
</data>
<data name="textTimestamp" xml:space="preserve">
<value>Zeitpunkt</value>
</data>
<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> </root>

View File

@ -118,6 +118,12 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<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"> <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> <value>arrow_down_green.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data> </data>
@ -154,18 +160,36 @@
<data name="delete2" type="System.Resources.ResXFileRef, System.Windows.Forms"> <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> <value>delete2.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data> </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>
<data name="emergency_stop_button" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="emergency_stop_button" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>emergency_stop_button.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>emergency_stop_button.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data> </data>
<data name="lock_open" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>lock_open.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="logo_bremen_calling" type="System.Resources.ResXFileRef, System.Windows.Forms"> <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> <value>logo_bremen_calling.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data> </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"> <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> <value>ship2.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data> </data>
<data name="sign_warning" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="sign_warning" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>sign_warning.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>sign_warning.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data> </data>
<data name="textAdd" xml:space="preserve">
<value>Add</value>
</data>
<data name="textAgencies" xml:space="preserve"> <data name="textAgencies" xml:space="preserve">
<value>Agencies</value> <value>Agencies</value>
</data> </data>
@ -211,6 +235,9 @@
<data name="textChangeContactInfo" xml:space="preserve"> <data name="textChangeContactInfo" xml:space="preserve">
<value>Update contact info</value> <value>Update contact info</value>
</data> </data>
<data name="textChangeHistory" xml:space="preserve">
<value>Change history</value>
</data>
<data name="textChangePassword" xml:space="preserve"> <data name="textChangePassword" xml:space="preserve">
<value>Change password</value> <value>Change password</value>
</data> </data>
@ -229,15 +256,30 @@
<data name="textConfirmation" xml:space="preserve"> <data name="textConfirmation" xml:space="preserve">
<value>Confirmation</value> <value>Confirmation</value>
</data> </data>
<data name="textDelete" xml:space="preserve">
<value>Delete</value>
</data>
<data name="textDeleted" xml:space="preserve">
<value>Deleted</value>
</data>
<data name="textDepartureTerminal" xml:space="preserve"> <data name="textDepartureTerminal" xml:space="preserve">
<value>Departure terminal</value> <value>Departure terminal</value>
</data> </data>
<data name="textDraft" xml:space="preserve"> <data name="textDraft" xml:space="preserve">
<value>Draft (m)</value> <value>Draft (m)</value>
</data> </data>
<data name="textEdit" xml:space="preserve">
<value>Edit</value>
</data>
<data name="textEditShip" xml:space="preserve">
<value>Edit ship</value>
</data>
<data name="textEditShipcall" xml:space="preserve"> <data name="textEditShipcall" xml:space="preserve">
<value>Edit ship call</value> <value>Edit ship call</value>
</data> </data>
<data name="textEditShips" xml:space="preserve">
<value>Edit ships</value>
</data>
<data name="textEditTimes" xml:space="preserve"> <data name="textEditTimes" xml:space="preserve">
<value>Edit times</value> <value>Edit times</value>
</data> </data>
@ -259,12 +301,18 @@
<data name="textFixed" xml:space="preserve"> <data name="textFixed" xml:space="preserve">
<value>Fixed</value> <value>Fixed</value>
</data> </data>
<data name="textFixedOrder" xml:space="preserve">
<value>Marked as a fixed order</value>
</data>
<data name="textFrom" xml:space="preserve"> <data name="textFrom" xml:space="preserve">
<value>from</value> <value>from</value>
</data> </data>
<data name="textIncoming" xml:space="preserve"> <data name="textIncoming" xml:space="preserve">
<value>Incoming</value> <value>Incoming</value>
</data> </data>
<data name="textInfoChangePW" xml:space="preserve">
<value>Show app info and change user password</value>
</data>
<data name="textInterval" xml:space="preserve"> <data name="textInterval" xml:space="preserve">
<value>Interval</value> <value>Interval</value>
</data> </data>
@ -280,6 +328,9 @@
<data name="textLogin" xml:space="preserve"> <data name="textLogin" xml:space="preserve">
<value>Login</value> <value>Login</value>
</data> </data>
<data name="textMineOnly" xml:space="preserve">
<value>mine only</value>
</data>
<data name="textMooredLock" xml:space="preserve"> <data name="textMooredLock" xml:space="preserve">
<value>Moored in lock</value> <value>Moored in lock</value>
</data> </data>
@ -301,6 +352,9 @@
<data name="textOldPassword" xml:space="preserve"> <data name="textOldPassword" xml:space="preserve">
<value>Old password</value> <value>Old password</value>
</data> </data>
<data name="textOperation" xml:space="preserve">
<value>Operation</value>
</data>
<data name="textOperationsEnd" xml:space="preserve"> <data name="textOperationsEnd" xml:space="preserve">
<value>Operations end</value> <value>Operations end</value>
</data> </data>
@ -310,6 +364,9 @@
<data name="textOutgoing" xml:space="preserve"> <data name="textOutgoing" xml:space="preserve">
<value>Outgoing</value> <value>Outgoing</value>
</data> </data>
<data name="textParticipant" xml:space="preserve">
<value>Participant</value>
</data>
<data name="textParticipants" xml:space="preserve"> <data name="textParticipants" xml:space="preserve">
<value>Participants</value> <value>Participants</value>
</data> </data>
@ -373,9 +430,15 @@
<data name="textShipLength" xml:space="preserve"> <data name="textShipLength" xml:space="preserve">
<value>Ship length</value> <value>Ship length</value>
</data> </data>
<data name="textShips" xml:space="preserve">
<value>Ships</value>
</data>
<data name="textShowCancelledShipcalls" xml:space="preserve"> <data name="textShowCancelledShipcalls" xml:space="preserve">
<value>Show cancelled calls</value> <value>Show cancelled calls</value>
</data> </data>
<data name="textShowHistory" xml:space="preserve">
<value>Show shipcall change history</value>
</data>
<data name="textSortOrder" xml:space="preserve"> <data name="textSortOrder" xml:space="preserve">
<value>Sort order</value> <value>Sort order</value>
</data> </data>
@ -388,12 +451,27 @@
<data name="textTidalWindow" xml:space="preserve"> <data name="textTidalWindow" xml:space="preserve">
<value>Tidal window</value> <value>Tidal window</value>
</data> </data>
<data name="textTimestamp" xml:space="preserve">
<value>Timestamp</value>
</data>
<data name="textTo" xml:space="preserve"> <data name="textTo" xml:space="preserve">
<value>to</value> <value>to</value>
</data> </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"> <data name="textTug" xml:space="preserve">
<value>Tug</value> <value>Tug</value>
</data> </data>
<data name="textTugCompany" xml:space="preserve">
<value>Tug company</value>
</data>
<data name="textTugRequired" xml:space="preserve"> <data name="textTugRequired" xml:space="preserve">
<value>Tug required</value> <value>Tug required</value>
</data> </data>
@ -445,7 +523,13 @@
<data name="umbrella_open" type="System.Resources.ResXFileRef, System.Windows.Forms"> <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> <value>umbrella_open.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data> </data>
<data name="Undefined" xml:space="preserve">
<value>Undefined</value>
</data>
<data name="worker2" type="System.Resources.ResXFileRef, System.Windows.Forms"> <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> <value>worker2.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data> </data>
<data name="_lock" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>lock.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
</root> </root>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

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:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:p = "clr-namespace:BreCalClient.Resources" xmlns:p = "clr-namespace:BreCalClient.Resources"
xmlns:local="clr-namespace:BreCalClient" xmlns:local="clr-namespace:BreCalClient"
xmlns:api="clr-namespace:BreCalClient.misc.Model"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="56" d:DesignWidth="800" Loaded="UserControl_Loaded"> d:DesignHeight="56" d:DesignWidth="800" Loaded="UserControl_Loaded">
<UserControl.Resources>
<local:EnumToStringConverter x:Key="enumToStringConverter" />
</UserControl.Resources>
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="28" /> <RowDefinition Height="28" />
@ -63,7 +67,7 @@
<Label Grid.Column="1" Content="{x:Static p:Resources.textTo}" /> <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}"/> <DatePicker x:Name="datePickerETATo" Grid.Column="2" Margin="2" SelectedDateChanged="datePickerETATo_SelectedDateChanged" SelectedDate="{Binding Path=EtaTo}"/>
</Grid> </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 Grid.Column="6" Grid.Row="0">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*" /> <ColumnDefinition Width=".5*" />
@ -74,8 +78,17 @@
<Label Grid.Column="1" Content="{x:Static p:Resources.textTo}" /> <Label Grid.Column="1" Content="{x:Static p:Resources.textTo}" />
<xctk:DoubleUpDown x:Name="upDownShiplengthTo" Grid.Column="2" Margin="2" Minimum="0" Maximum="1000" ValueChanged="upDownShiplengthTo_ValueChanged" Value="{Binding Path=ShipLengthTo}"/> <xctk:DoubleUpDown x:Name="upDownShiplengthTo" Grid.Column="2" Margin="2" Minimum="0" Maximum="1000" ValueChanged="upDownShiplengthTo_ValueChanged" Value="{Binding Path=ShipLengthTo}"/>
</Grid> </Grid>
<xctk:WatermarkTextBox x:Name="textBoxSearch" Grid.Column="2" Grid.Row="1" Margin="2" Watermark="{x:Static p:Resources.textEnterKeyword}" PreviewTextInput="textBoxSearch_PreviewTextInput" <Grid Grid.Column="2" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*" />
<ColumnDefinition Width="30" />
<ColumnDefinition Width=".5*" />
</Grid.ColumnDefinitions>
<xctk:WatermarkTextBox x:Name="textBoxSearch" Grid.Column="0" Margin="2" Watermark="{x:Static p:Resources.textEnterKeyword}" PreviewTextInput="textBoxSearch_PreviewTextInput"
DataObject.Pasting="textBoxSearch_Pasting" TextChanged="textBoxSearch_TextChanged" /> DataObject.Pasting="textBoxSearch_Pasting" TextChanged="textBoxSearch_TextChanged" />
<CheckBox x:Name="checkBoxOwn" VerticalAlignment="Center" Grid.Column="1" HorizontalAlignment="Right" Margin="2" Checked="checkBoxOwn_Checked" Unchecked="checkBoxOwn_Checked" />
<Label Content="{x:Static p:Resources.textMineOnly}" Grid.Column="2" />
</Grid>
<xctk:CheckComboBox x:Name="comboBoxBerths" DisplayMemberPath="Name" Grid.Column="4" Grid.Row="1" Margin="2" ItemSelectionChanged="comboBoxBerths_ItemSelectionChanged" /> <xctk:CheckComboBox x:Name="comboBoxBerths" DisplayMemberPath="Name" Grid.Column="4" Grid.Row="1" Margin="2" ItemSelectionChanged="comboBoxBerths_ItemSelectionChanged" />
<xctk:CheckComboBox x:Name="comboBoxAgencies" DisplayMemberPath="Name" Grid.Column="6" Grid.Row="1" Margin="2" ItemSelectionChanged="comboBoxAgencies_ItemSelectionChanged" /> <xctk:CheckComboBox x:Name="comboBoxAgencies" DisplayMemberPath="Name" Grid.Column="6" Grid.Row="1" Margin="2" ItemSelectionChanged="comboBoxAgencies_ItemSelectionChanged" />
</Grid> </Grid>

View File

@ -72,11 +72,12 @@ namespace BreCalClient
this.comboBoxAgencies.UnSelectAll(); this.comboBoxAgencies.UnSelectAll();
this.comboBoxBerths.UnSelectAll(); this.comboBoxBerths.UnSelectAll();
this.comboBoxCategories.UnSelectAll(); this.comboBoxCategories.UnSelectAll();
this.datePickerETAFrom.SelectedDate = null; this.datePickerETAFrom.SelectedDate = DateTime.Now.AddDays(-2);
this.datePickerETATo.SelectedDate = null; this.datePickerETATo.SelectedDate = null;
this.textBoxSearch.Clear(); this.textBoxSearch.Clear();
this.upDownShiplengthFrom.Value = null; this.upDownShiplengthFrom.Value = null;
this.upDownShiplengthTo.Value = null; this.upDownShiplengthTo.Value = null;
this.checkBoxOwn.IsChecked = false;
} }
@ -100,14 +101,21 @@ namespace BreCalClient
} }
if(sfm.Categories != null) if(sfm.Categories != null)
{ {
foreach(int category in sfm.Categories) EnumToStringConverter enumToStringConverter = new();
this.comboBoxCategories.SelectedItems.Add((Extensions.TypeEnum)category); 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; if (sfm.SearchString != null) this.textBoxSearch.Text = sfm.SearchString;
this.upDownShiplengthFrom.Value = sfm.ShipLengthFrom; this.upDownShiplengthFrom.Value = sfm.ShipLengthFrom;
this.upDownShiplengthTo.Value = sfm.ShipLengthTo; 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.datePickerETATo.SelectedDate = sfm.EtaTo;
this.checkBoxOwn.IsChecked = sfm.MineOnly;
this._model = sfm; this._model = sfm;
SearchFilterChanged?.Invoke(); SearchFilterChanged?.Invoke();
@ -124,7 +132,7 @@ namespace BreCalClient
private void UserControl_Loaded(object sender, System.Windows.RoutedEventArgs e) private void UserControl_Loaded(object sender, System.Windows.RoutedEventArgs e)
{ {
this.comboBoxCategories.ItemsSource = Enum.GetValues(typeof(Extensions.TypeEnum));
} }
private void datePickerETAFrom_SelectedDateChanged(object sender, SelectionChangedEventArgs e) private void datePickerETAFrom_SelectedDateChanged(object sender, SelectionChangedEventArgs e)
@ -141,9 +149,15 @@ namespace BreCalClient
private void comboBoxCategories_ItemSelectionChanged(object sender, Xceed.Wpf.Toolkit.Primitives.ItemSelectionChangedEventArgs e) private void comboBoxCategories_ItemSelectionChanged(object sender, Xceed.Wpf.Toolkit.Primitives.ItemSelectionChangedEventArgs e)
{ {
EnumToStringConverter enumToStringConverter = new();
_model.Categories.Clear(); _model.Categories.Clear();
foreach(int category in comboBoxCategories.SelectedItems) foreach (string categoryString in comboBoxCategories.SelectedItems)
_model.Categories.Add(category); {
ShipcallType? type = (ShipcallType?)enumToStringConverter.ConvertBack(categoryString, typeof(ShipcallType), new object(), System.Globalization.CultureInfo.CurrentCulture);
if(type != null)
_model.Categories.Add(type.Value);
}
SearchFilterChanged?.Invoke(); SearchFilterChanged?.Invoke();
} }
@ -192,6 +206,12 @@ namespace BreCalClient
{ {
this.SearchFilter.SearchString = this.textBoxSearch.Text; this.SearchFilter.SearchString = this.textBoxSearch.Text;
SearchFilterChanged?.Invoke(); SearchFilterChanged?.Invoke();
}
private void checkBoxOwn_Checked(object sender, System.Windows.RoutedEventArgs e)
{
this._model.MineOnly = this.checkBoxOwn.IsChecked;
SearchFilterChanged?.Invoke();
} }
#endregion #endregion

View File

@ -19,7 +19,7 @@ namespace BreCalClient
public DateTime? EtaTo { get; set; } public DateTime? EtaTo { get; set; }
public List<int> Categories { get; set; } = new(); public List<ShipcallType> Categories { get; set; } = new();
public List<int> Agencies { get; set; } = new(); public List<int> Agencies { get; set; } = new();
@ -31,8 +31,12 @@ namespace BreCalClient
public double? ShipLengthTo { get; set; } public double? ShipLengthTo { get; set; }
public bool? MineOnly { get; set; }
#endregion #endregion
#region Serialisation
public static SearchFilterModel? Deserialize(string json) public static SearchFilterModel? Deserialize(string json)
{ {
return (SearchFilterModel?) JsonConvert.DeserializeObject(json, typeof(SearchFilterModel)); return (SearchFilterModel?) JsonConvert.DeserializeObject(json, typeof(SearchFilterModel));
@ -43,5 +47,7 @@ namespace BreCalClient
return JsonConvert.SerializeObject(this, Formatting.Indented); return JsonConvert.SerializeObject(this, Formatting.Indented);
} }
#endregion
} }
} }

View File

@ -0,0 +1,41 @@
<Window x:Class="BreCalClient.ShipListDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
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}" Title="{x:Static p:Resources.textShips}"
Height="490" Width="800" ResizeMode="CanResize" Icon="Resources/containership.ico" Loaded="Window_Loaded">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="28" />
</Grid.RowDefinitions>
<local:ENIDataGrid x:Name="dataGridShips" Grid.Row="0" SelectionMode="Single" IsReadOnly="True" AlternatingRowBackground="LightBlue" AutoGenerateColumns="False"
CanUserAddRows="False" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<local:ENIDataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding Ship.Deleted}" Value="True">
<Setter Property="Foreground" Value="DarkGray"/>
</DataTrigger>
</Style.Triggers>
</Style>
</local:ENIDataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Id" Binding="{Binding Path=Ship.Id}" IsReadOnly="True"/>
<DataGridTextColumn Header="Name" Binding="{Binding Path=Ship.Name}" IsReadOnly="True"/>
<DataGridCheckBoxColumn Header="{x:Static p:Resources.textTug}" Binding="{Binding Path=Ship.IsTug, Mode=OneWay}" IsReadOnly="True"/>
<DataGridTextColumn Header="{x:Static p:Resources.textTugCompany}" Binding="{Binding Path=TugCompany, Mode=OneWay}" IsReadOnly="True"/>
<DataGridTextColumn Header="IMO" Binding="{Binding Path=Ship.Imo}" IsReadOnly="True"/>
<DataGridTextColumn Header="{x:Static p:Resources.textCallsign}" Binding="{Binding Path=Ship.Callsign}" IsReadOnly="True"/>
<DataGridTextColumn Header="{x:Static p:Resources.textLength}" Binding="{Binding Path=Ship.Length}" IsReadOnly="True"/>
<DataGridTextColumn Header="{x:Static p:Resources.textWidth}" Binding="{Binding Path=Ship.Width}" IsReadOnly="True"/>
<DataGridCheckBoxColumn Header="{x:Static p:Resources.textDeleted}" Binding="{Binding Path=Ship.Deleted, Mode=OneWay}" IsReadOnly="True" />
</DataGrid.Columns>
</local:ENIDataGrid>
<Button Grid.Row="1" x:Name="buttonClose" Content="{x:Static p:Resources.textClose}" HorizontalAlignment="Right" Margin="2" Click="buttonClose_Click"/>
</Grid>
</Window>

View File

@ -0,0 +1,127 @@
// Copyright (c) 2023 schick Informatik
// Description: Administration screen for ships
//
using BreCalClient.misc.Api;
using BreCalClient.misc.Model;
using System;
using System.Windows;
namespace BreCalClient
{
/// <summary>
/// Interaction logic for ShipListDialog.xaml
/// </summary>
public partial class ShipListDialog : Window
{
public ShipListDialog()
{
InitializeComponent();
}
#region Properties
public ShipApi? ShipApi { get; set; }
#endregion
#region Event handler
private void buttonClose_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.dataGridShips.Initialize();
this.dataGridShips.ItemsSource = BreCalLists.AllShips;
this.dataGridShips.CreateRequested += DataGridShips_CreateRequested;
this.dataGridShips.EditRequested += DataGridShips_EditRequested;
this.dataGridShips.DeleteRequested += DataGridShips_DeleteRequested;
}
private async void DataGridShips_DeleteRequested(object obj)
{
if (obj is ShipModel shipmodel)
{
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;
}
}
}
private async void DataGridShips_EditRequested(object obj)
{
if (obj is ShipModel shipmodel)
{
EditShipDialog esd = new()
{
Ship = shipmodel.Ship
};
esd.Participants.AddRange(BreCalLists.Participants_Tug);
if (esd.ShowDialog() ?? false)
{
try
{
if (this.ShipApi != null)
{
Id tmpId = await this.ShipApi.ShipUpdateAsync(shipmodel.Ship);
}
this.dataGridShips.ItemsSource = null;
this.dataGridShips.ItemsSource = BreCalLists.AllShips;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
private async void DataGridShips_CreateRequested()
{
ShipModel shipModel = new(new Ship());
EditShipDialog esd = new()
{
Ship = shipModel.Ship
};
esd.Participants.AddRange(BreCalLists.Participants_Tug);
if(esd.ShowDialog() ?? false)
{
try
{
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)
{
MessageBox.Show(ex.Message);
}
}
}
#endregion
}
}

View File

@ -16,6 +16,16 @@ namespace BreCalClient
public Ship Ship { get; private set; } public Ship Ship { get; private set; }
public string TugCompany
{
get { if(this.Ship.ParticipantId.HasValue)
{
return BreCalLists.ParticipantLookupDict[this.Ship.ParticipantId.Value].Name;
}
return "";
}
}
public override string ToString() public override string ToString()
{ {
return String.Format("{0} ({1})", this.Ship.Name, this.Ship.Imo); return String.Format("{0} ({1})", this.Ship.Name, this.Ship.Imo);

View File

@ -7,7 +7,7 @@
xmlns:sets="clr-namespace:BreCalClient.Properties" xmlns:sets="clr-namespace:BreCalClient.Properties"
xmlns:db="clr-namespace:BreCalClient;assembly=BreCalClient" xmlns:db="clr-namespace:BreCalClient;assembly=BreCalClient"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="120" d:DesignWidth="800" Loaded="UserControl_Loaded"> d:DesignHeight="135" d:DesignWidth="800">
<Border BorderBrush="LightGray" Margin="1" BorderThickness="1"> <Border BorderBrush="LightGray" Margin="1" BorderThickness="1">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
@ -30,7 +30,7 @@
<RowDefinition Height=".125*"/> <RowDefinition Height=".125*"/>
<RowDefinition Height=".125*"/> <RowDefinition Height=".125*"/>
<RowDefinition Height=".05*"/> <RowDefinition Height=".125*"/>
<RowDefinition Height=".125*"/> <RowDefinition Height=".125*"/>
<RowDefinition Height=".125*"/> <RowDefinition Height=".125*"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
@ -71,6 +71,12 @@
<Viewbox Grid.Row="3" Grid.Column="1" HorizontalAlignment="Left"> <Viewbox Grid.Row="3" Grid.Column="1" HorizontalAlignment="Left">
<TextBlock x:Name="textBlockLengthWidth" Padding="0"/> <TextBlock x:Name="textBlockLengthWidth" Padding="0"/>
</Viewbox> </Viewbox>
<Viewbox Grid.Row="4" Grid.Column="0" HorizontalAlignment="Left">
<TextBlock Text="{x:Static p:Resources.textDraft}" Padding="0" />
</Viewbox>
<Viewbox Grid.Row="4" Grid.Column="1" HorizontalAlignment="Left">
<TextBlock x:Name="textBlockDraft" Padding="0"/>
</Viewbox>
<Viewbox Grid.Row="5" Grid.Column="0" HorizontalAlignment="Left"> <Viewbox Grid.Row="5" Grid.Column="0" HorizontalAlignment="Left">
<TextBlock Text="ETA" x:Name="labelETA"/> <TextBlock Text="ETA" x:Name="labelETA"/>
</Viewbox> </Viewbox>
@ -116,52 +122,67 @@
<Label Grid.Row="0" Grid.Column="0" Content = "ETA" x:Name="labelETAETDAgent" Padding="0" VerticalContentAlignment="Center" /> <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="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"/> <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"/> <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" /> <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"/> <TextBlock Grid.Row="3" Grid.Column="1" Grid.RowSpan="1" Padding="0" TextWrapping="Wrap" VerticalAlignment="Top" x:Name="textBlockAgencyBerthRemarks" FontSize="10"/>
</Grid> </Grid>
</Border> </Border>
<!-- MOORING --> <!-- MOORING -->
<Border Grid.Row="2" Grid.Column="2" BorderThickness="1, 0, 0, 0" BorderBrush="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" Padding="3,0,0,0"> <Border Grid.Row="2" Grid.Column="2" BorderThickness="1, 0, 0, 0" BorderBrush="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" Padding="3,0,0,0">
<Grid> <Grid x:Name="gridMooring">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="0.3*" /> <ColumnDefinition Width="0.3*" />
<ColumnDefinition Width="0.7*" /> <ColumnDefinition Width="0.7*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="20" /> <RowDefinition Height="20" />
<RowDefinition Height="*" /> <RowDefinition Height="0" x:Name="ataRowDefinition" />
<RowDefinition Height="0" x:Name="atdRowDefinition" />
<RowDefinition Height="*" x:Name="infoRowDefinition"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Label Grid.Row="0" x:Name="labelETAETDMooring" Grid.Column="0" Content="ETA" Padding="0" VerticalContentAlignment="Center" /> <Label Grid.Row="0" x:Name="labelETAETDMooring" Grid.Column="0" 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="0" Grid.Column="1" Padding="0" VerticalContentAlignment="Center" x:Name="labelMooringETAETDValue" FontWeight="DemiBold"/> <Label Grid.Row="0" Grid.Column="1" Padding="0" VerticalContentAlignment="Center" x:Name="labelMooringETAETDValue" FontWeight="DemiBold"/>
<TextBlock Grid.Row="1" Grid.Column="1" Padding="0" TextWrapping="Wrap" VerticalAlignment="Top" x:Name="textBlockMooringRemarks"/> <Label Grid.Row="1" Grid.Column="0" Content="ATA" Padding="0" VerticalContentAlignment="Center" />
<Label Grid.Row="1" Grid.Column="1" x:Name="labelTimesMooringATA" Padding="0" VerticalContentAlignment="Center" />
<Label Grid.Row="2" Grid.Column="0" Content="ATD" Padding="0" VerticalContentAlignment="Center" />
<Label Grid.Row="2" Grid.Column="1" x:Name="labelTimesMooringATD" Padding="0" VerticalContentAlignment="Center" />
<Label Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" Content="{x:Static p:Resources.textRemarks}" Padding="0" VerticalContentAlignment="Top" FontSize="9"/>
<Image Grid.Row="3" Grid.Column="0" x:Name="imageMooringLocked" 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="3" Grid.Column="1" Padding="0" TextWrapping="Wrap" VerticalAlignment="Top" x:Name="textBlockMooringRemarks"/>
</Grid> </Grid>
</Border> </Border>
<!-- PORT AUTHORITY --> <!-- PORT AUTHORITY -->
<Border Grid.Row="2" Grid.Column="3" BorderThickness="1, 0, 0, 0" BorderBrush="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" Padding="3,0,0,0"> <Border Grid.Row="2" Grid.Column="3" BorderThickness="1, 0, 0, 0" BorderBrush="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" Padding="3,0,0,0">
<Grid > <Grid x:Name="gridPortAuthority">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="0.3*" /> <ColumnDefinition Width="0.3*" />
<ColumnDefinition Width="0.7*" /> <ColumnDefinition Width="0.7*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="20" /> <RowDefinition Height="20" />
<RowDefinition Height="0" x:Name="lockTimeRowDefinition" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" x:Name="labelETAETDPortAuthority" Content="ETA" Padding="0" VerticalContentAlignment="Center" /> <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"/> <Label Grid.Row="0" Grid.Column="1" Padding="0" VerticalContentAlignment="Center" x:Name="labelPortAuthorityETAETDValue" FontWeight="DemiBold"/>
<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> </Grid>
</Border> </Border>
<!-- PILOT --> <!-- PILOT -->
<Border Grid.Row="2" Grid.Column="4" BorderThickness="1, 0, 0, 0" BorderBrush="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" Padding="3,0,0,0"> <Border Grid.Row="2" Grid.Column="4" BorderThickness="1, 0, 0, 0" BorderBrush="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" Padding="3,0,0,0">
<Grid > <Grid x:Name="gridPilot">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="0.3*" /> <ColumnDefinition Width="0.3*" />
<ColumnDefinition Width="0.7*" /> <ColumnDefinition Width="0.7*" />
@ -173,12 +194,13 @@
<Label Grid.Row="0" Grid.Column="0" x:Name="labelETAETDPilot" Content="ETA" Padding="0" VerticalContentAlignment="Center" /> <Label Grid.Row="0" Grid.Column="0" x:Name="labelETAETDPilot" 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="1" Grid.Column="0" Grid.ColumnSpan="2" Content="{x:Static p:Resources.textRemarks}" Padding="0" VerticalContentAlignment="Top" FontSize="9"/>
<Label Grid.Row="0" Grid.Column="1" Padding="0" VerticalContentAlignment="Center" x:Name="labelPilotETAETDValue" FontWeight="DemiBold"/> <Label Grid.Row="0" Grid.Column="1" Padding="0" VerticalContentAlignment="Center" x:Name="labelPilotETAETDValue" FontWeight="DemiBold"/>
<Image Grid.Row="1" Grid.Column="0" x:Name="imagePilotLocked" 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="textBlockPilotRemarks"/> <TextBlock Grid.Row="1" Grid.Column="1" Padding="0" TextWrapping="Wrap" VerticalAlignment="Top" x:Name="textBlockPilotRemarks"/>
</Grid> </Grid>
</Border> </Border>
<!-- TUG --> <!-- TUG -->
<Border Grid.Row="2" Grid.Column="5" BorderThickness="1, 0, 0, 0" BorderBrush="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" Padding="3,0,0,0"> <Border Grid.Row="2" Grid.Column="5" BorderThickness="1, 0, 0, 0" BorderBrush="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" Padding="3,0,0,0">
<Grid> <Grid x:Name="gridTug">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="0.3*" /> <ColumnDefinition Width="0.3*" />
<ColumnDefinition Width="0.7*" /> <ColumnDefinition Width="0.7*" />
@ -190,6 +212,7 @@
<Label Grid.Row="0" Grid.Column="0" x:Name="labelETAETDTug" Content="ETA" Padding="0" VerticalContentAlignment="Center" /> <Label Grid.Row="0" Grid.Column="0" x:Name="labelETAETDTug" 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="1" Grid.Column="0" Grid.ColumnSpan="2" Content="{x:Static p:Resources.textRemarks}" Padding="0" VerticalContentAlignment="Top" FontSize="9"/>
<Label Grid.Row="0" Grid.Column="1" Padding="0" VerticalContentAlignment="Center" x:Name="labelTugETAETDValue" FontWeight="DemiBold"/> <Label Grid.Row="0" Grid.Column="1" Padding="0" VerticalContentAlignment="Center" x:Name="labelTugETAETDValue" FontWeight="DemiBold"/>
<Image Grid.Row="1" Grid.Column="0" x:Name="imageTugLocked" 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="textBlockTugRemarks"/> <TextBlock Grid.Row="1" Grid.Column="1" Padding="0" TextWrapping="Wrap" VerticalAlignment="Top" x:Name="textBlockTugRemarks"/>
</Grid> </Grid>
</Border> </Border>
@ -210,9 +233,11 @@
<Label Grid.Row="0" Grid.Column="1" Padding="0" VerticalContentAlignment="Center" x:Name="labelOperationsStart" FontWeight="DemiBold"/> <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"/> <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"/> <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" /> <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"/> <TextBlock Grid.Row="3" Grid.Column="1" Padding="0" TextWrapping="Wrap" VerticalAlignment="Top" x:Name="textBlockTerminalBerthRemarks" FontSize="10"/>
</Grid> </Grid>
</Border> </Border>

View File

@ -26,6 +26,9 @@ namespace BreCalClient
Participant? _tug; Participant? _tug;
Participant? _port_administration; Participant? _port_administration;
private static readonly ILog _log = LogManager.GetLogger(typeof(ShipcallControl)); private static readonly ILog _log = LogManager.GetLogger(typeof(ShipcallControl));
bool ataAdded = false;
bool atdAdded = false;
bool lockTimeAdded = false;
#endregion #endregion
@ -78,6 +81,7 @@ namespace BreCalClient
this.labelAgencyETAETDValue.Content = ""; this.labelAgencyETAETDValue.Content = "";
this.textBlockAgencyRemarks.Text = ""; this.textBlockAgencyRemarks.Text = "";
this.textBlockAgencyBerthRemarks.Text = ""; this.textBlockAgencyBerthRemarks.Text = "";
this.textBlockDraft.Text = "";
} }
_mooring = this.ShipcallControlModel.GetParticipantForType(Extensions.ParticipantType.MOORING); _mooring = this.ShipcallControlModel.GetParticipantForType(Extensions.ParticipantType.MOORING);
@ -209,13 +213,13 @@ namespace BreCalClient
// this.labelShipName.Content = this.ShipcallControlModel?.Ship?.Name; // this.labelShipName.Content = this.ShipcallControlModel?.Ship?.Name;
switch (this.ShipcallControlModel?.Shipcall?.Type) switch (this.ShipcallControlModel?.Shipcall?.Type)
{ {
case 1: // incoming case ShipcallType.Arrival: // incoming
this.imageShipcallType.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalClient;component/Resources/arrow_down_red.png")); this.imageShipcallType.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalClient;component/Resources/arrow_down_red.png"));
break; break;
case 2: // outgoing case ShipcallType.Departure: // outgoing
this.imageShipcallType.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalClient;component/Resources/arrow_up_blue.png")); this.imageShipcallType.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalClient;component/Resources/arrow_up_blue.png"));
break; break;
case 3: // shifting case ShipcallType.Shifting: // shifting
this.imageShipcallType.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalClient;component/Resources/arrow_right_green.png")); this.imageShipcallType.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalClient;component/Resources/arrow_right_green.png"));
break; break;
default: default:
@ -224,13 +228,13 @@ namespace BreCalClient
switch(this.ShipcallControlModel?.LightMode) switch(this.ShipcallControlModel?.LightMode)
{ {
case ShipcallControlModel.TrafficLightMode.GREEN: case EvaluationType.Green:
this.imageEvaluation.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalClient;component/Resources/check.png")); this.imageEvaluation.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalClient;component/Resources/check.png"));
break; break;
case ShipcallControlModel.TrafficLightMode.YELLOW: case EvaluationType.Yellow:
this.imageEvaluation.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalClient;component/Resources/sign_warning.png")); this.imageEvaluation.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalClient;component/Resources/sign_warning.png"));
break; break;
case ShipcallControlModel.TrafficLightMode.RED: case EvaluationType.Red:
this.imageEvaluation.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalClient;component/Resources/delete2.png")); this.imageEvaluation.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalClient;component/Resources/delete2.png"));
break; break;
default: default:
@ -248,22 +252,13 @@ namespace BreCalClient
{ {
if (this.ShipcallControlModel?.Shipcall?.Evaluation != null) 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 this.Background = this.ShipcallControlModel.LightMode switch
switch (resultColor)
{ {
//case ShipcallControlModel.TrafficLightMode.GREEN: // ShipcallControlModel.TrafficLightMode.GREEN => this.Background = Brushes.LightGreen,
// this.Background = Brushes.LightGreen; EvaluationType.Yellow => Brushes.LightYellow,
// break; EvaluationType.Red => new SolidColorBrush(Color.FromArgb(200, 255, 100, 100)),
case ShipcallControlModel.TrafficLightMode.YELLOW: _ => Brushes.Transparent,
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;
}
} }
} }
@ -274,11 +269,11 @@ namespace BreCalClient
this.textBlockBerth.Text = this.ShipcallControlModel?.Berth; this.textBlockBerth.Text = this.ShipcallControlModel?.Berth;
this.textBlockCallsign.Text = this.ShipcallControlModel?.Ship?.Callsign; this.textBlockCallsign.Text = this.ShipcallControlModel?.Ship?.Callsign;
if (this.ShipcallControlModel?.Shipcall?.Type == 1) if (this.ShipcallControlModel?.Shipcall?.Type == ShipcallType.Arrival)
{ {
this.textBlockETA.Text = this.ShipcallControlModel?.Shipcall?.Eta?.ToString("dd.MM. HH:mm"); this.textBlockETA.Text = this.ShipcallControlModel?.Shipcall?.Eta?.ToString("dd.MM. HH:mm");
} }
if ((this.ShipcallControlModel?.Shipcall?.Type == 2) || (this.ShipcallControlModel?.Shipcall?.Type == 3)) if ((this.ShipcallControlModel?.Shipcall?.Type == ShipcallType.Departure) || (this.ShipcallControlModel?.Shipcall?.Type == ShipcallType.Shifting))
{ {
this.labelETA.Text = "ETD"; this.labelETA.Text = "ETD";
this.textBlockETA.Text = this.ShipcallControlModel?.Shipcall?.Etd?.ToString("dd.MM. HH:mm"); this.textBlockETA.Text = this.ShipcallControlModel?.Shipcall?.Etd?.ToString("dd.MM. HH:mm");
@ -290,7 +285,7 @@ namespace BreCalClient
// rename labels if this is not an incoming // rename labels if this is not an incoming
// must be here because there may not be a times record for each participant (yet) // must be here because there may not be a times record for each participant (yet)
if (this.ShipcallControlModel?.Shipcall?.Type != 1) if (this.ShipcallControlModel?.Shipcall?.Type != ShipcallType.Arrival)
{ {
this.labelETAETDAgent.Content = "ETD"; this.labelETAETDAgent.Content = "ETD";
this.labelETAETDMooring.Content = "ETD"; this.labelETAETDMooring.Content = "ETD";
@ -300,6 +295,17 @@ namespace BreCalClient
this.labelETAETDTerminal.Content = BreCalClient.Resources.Resources.textOperationsEnd; 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) if (this.ShipcallControlModel != null)
{ {
@ -307,13 +313,10 @@ namespace BreCalClient
if (agencyTimes != null) if (agencyTimes != null)
{ {
this.labelAgencyBerth.Content = this.ShipcallControlModel?.GetBerthText(agencyTimes); this.labelAgencyBerth.Content = this.ShipcallControlModel?.GetBerthText(agencyTimes);
this.labelAgencyETAETDValue.Content = agencyTimes.EtaBerth.HasValue ? agencyTimes.EtaBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -"; this.labelAgencyETAETDValue.Content = agencyTimes.DisplayTime(this.ShipcallControlModel?.Shipcall?.Type == ShipcallType.Arrival);
this.textBlockAgencyRemarks.Text = agencyTimes.Remarks; this.textBlockAgencyRemarks.Text = agencyTimes.Remarks.TruncateDots(50);
this.textBlockAgencyBerthRemarks.Text = agencyTimes.BerthInfo; this.textBlockAgencyBerthRemarks.Text = agencyTimes.BerthInfo.TruncateDots(50);
if (this.ShipcallControlModel?.Shipcall?.Type != 1) this.textBlockDraft.Text = ShipcallControlModel?.Shipcall?.Draft?.ToString("N2");
{
this.labelAgencyETAETDValue.Content = agencyTimes.EtdBerth.HasValue ? agencyTimes.EtdBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -";
}
} }
else else
{ {
@ -322,84 +325,105 @@ namespace BreCalClient
this.labelAgencyETAETDValue.Content = "- / -"; this.labelAgencyETAETDValue.Content = "- / -";
this.textBlockAgencyRemarks.Text = ""; this.textBlockAgencyRemarks.Text = "";
this.textBlockAgencyBerthRemarks.Text = ""; this.textBlockAgencyBerthRemarks.Text = "";
this.textBlockDraft.Text = "";
} }
Times? mooringTimes = this.ShipcallControlModel?.GetTimesForParticipantType(Extensions.ParticipantType.MOORING); Times? mooringTimes = this.ShipcallControlModel?.GetTimesForParticipantType(Extensions.ParticipantType.MOORING);
if (mooringTimes != null) if (mooringTimes != null)
{ {
this.labelMooringETAETDValue.Content = mooringTimes.EtaBerth.HasValue ? mooringTimes.EtaBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -"; this.labelMooringETAETDValue.Content = mooringTimes.DisplayTime(this.ShipcallControlModel?.Shipcall?.Type == ShipcallType.Arrival);
this.textBlockMooringRemarks.Text = mooringTimes.Remarks; this.textBlockMooringRemarks.Text = mooringTimes.Remarks.TruncateDots(50);
if (this.ShipcallControlModel?.Shipcall?.Type != 1) this.imageMooringLocked.Visibility = (mooringTimes.EtaBerthFixed ?? false) ? Visibility.Visible : Visibility.Hidden;
if(mooringTimes.Ata.HasValue)
{ {
this.labelMooringETAETDValue.Content = mooringTimes.EtdBerth.HasValue ? mooringTimes.EtdBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -"; if(!ataAdded)
{
ataRowDefinition.Height = new GridLength(15);
labelTimesMooringATA.Content = mooringTimes.Ata.Value.ToString("dd.MM.yyyy HH:mm");
ataAdded = true;
}
} }
if (mooringTimes.Atd.HasValue)
{
if (!atdAdded)
{
atdRowDefinition.Height = new GridLength(15);
labelTimesMooringATD.Content = mooringTimes.Atd.Value.ToString("dd.MM.yyyy HH:mm");
atdAdded = true;
}
}
} }
else else
{ {
this.labelMooringETAETDValue.Content = "- / "; this.labelMooringETAETDValue.Content = "- / ";
this.textBlockMooringRemarks.Text = ""; this.textBlockMooringRemarks.Text = "";
this.imageMooringLocked.Visibility = Visibility.Hidden;
} }
Times? portAuthorityTimes = this.ShipcallControlModel?.GetTimesForParticipantType(Extensions.ParticipantType.PORT_ADMINISTRATION); Times? portAuthorityTimes = this.ShipcallControlModel?.GetTimesForParticipantType(Extensions.ParticipantType.PORT_ADMINISTRATION);
if (portAuthorityTimes != null) if (portAuthorityTimes != null)
{ {
this.labelPortAuthorityETAETDValue.Content = portAuthorityTimes.EtaBerth.HasValue ? portAuthorityTimes.EtaBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -"; this.labelPortAuthorityETAETDValue.Content = portAuthorityTimes.DisplayTime(this.ShipcallControlModel?.Shipcall?.Type == ShipcallType.Arrival);
this.textBlockPortAuthorityRemarks.Text = portAuthorityTimes.Remarks; this.textBlockPortAuthorityRemarks.Text = portAuthorityTimes.Remarks.TruncateDots(50);
if (this.ShipcallControlModel?.Shipcall?.Type != 1) this.imagePortAuthorityLocked.Visibility = (portAuthorityTimes.EtaBerthFixed ?? false) ? Visibility.Visible : Visibility.Hidden;
if(portAuthorityTimes.LockTime.HasValue)
{ {
this.labelPortAuthorityETAETDValue.Content = portAuthorityTimes.EtdBerth.HasValue ? portAuthorityTimes.EtdBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -"; if(!lockTimeAdded)
{
lockTimeRowDefinition.Height = new GridLength(15);
labelPortAuthorityLockTime.Content = portAuthorityTimes.LockTime.Value.ToString("dd.MM.yyyy HH:mm");
lockTimeAdded = true;
}
} }
} }
else else
{ {
this.labelPortAuthorityETAETDValue.Content = "- / -"; this.labelPortAuthorityETAETDValue.Content = "- / -";
this.textBlockPortAuthorityRemarks.Text = ""; this.textBlockPortAuthorityRemarks.Text = "";
this.imagePortAuthorityLocked.Visibility = Visibility.Hidden;
} }
Times? pilotTimes = this.ShipcallControlModel?.GetTimesForParticipantType(Extensions.ParticipantType.PILOT); Times? pilotTimes = this.ShipcallControlModel?.GetTimesForParticipantType(Extensions.ParticipantType.PILOT);
if (pilotTimes != null) if (pilotTimes != null)
{ {
this.labelPilotETAETDValue.Content = pilotTimes.EtaBerth.HasValue ? pilotTimes.EtaBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -"; this.labelPilotETAETDValue.Content = pilotTimes.DisplayTime(this.ShipcallControlModel?.Shipcall?.Type == ShipcallType.Arrival);
this.textBlockPilotRemarks.Text = pilotTimes.Remarks; this.textBlockPilotRemarks.Text = pilotTimes.Remarks.TruncateDots(50);
if (this.ShipcallControlModel?.Shipcall?.Type != 1) this.imagePilotLocked.Visibility = (pilotTimes.EtaBerthFixed ?? false) ? Visibility.Visible : Visibility.Hidden;
{
this.labelPilotETAETDValue.Content = pilotTimes.EtdBerth.HasValue ? pilotTimes.EtdBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -";
}
} }
else else
{ {
this.labelPilotETAETDValue.Content = "- / -"; this.labelPilotETAETDValue.Content = "- / -";
this.textBlockPilotRemarks.Text = ""; this.textBlockPilotRemarks.Text = "";
this.imagePilotLocked.Visibility = Visibility.Hidden;
} }
Times? tugTimes = this.ShipcallControlModel?.GetTimesForParticipantType(Extensions.ParticipantType.TUG); Times? tugTimes = this.ShipcallControlModel?.GetTimesForParticipantType(Extensions.ParticipantType.TUG);
if (tugTimes != null) if (tugTimes != null)
{ {
this.labelTugETAETDValue.Content = tugTimes.EtaBerth.HasValue ? tugTimes.EtaBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -"; this.labelTugETAETDValue.Content = tugTimes.DisplayTime(this.ShipcallControlModel?.Shipcall?.Type == ShipcallType.Arrival);
this.textBlockTugRemarks.Text = tugTimes.Remarks; this.textBlockTugRemarks.Text = tugTimes.Remarks.TruncateDots(50);
if (this.ShipcallControlModel?.Shipcall?.Type != 1) this.imageTugLocked.Visibility = (tugTimes.EtaBerthFixed ?? false) ? Visibility.Visible : Visibility.Hidden;
{
this.labelTugETAETDValue.Content = tugTimes.EtdBerth.HasValue ? tugTimes.EtdBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -";
}
} }
else else
{ {
this.labelTugETAETDValue.Content = "- / -"; this.labelTugETAETDValue.Content = "- / -";
this.textBlockTugRemarks.Text = ""; this.textBlockTugRemarks.Text = "";
this.imageTugLocked.Visibility = Visibility.Hidden;
} }
Times? terminalTimes = this.ShipcallControlModel?.GetTimesForParticipantType(Extensions.ParticipantType.TERMINAL); Times? terminalTimes = this.ShipcallControlModel?.GetTimesForParticipantType(Extensions.ParticipantType.TERMINAL);
if (terminalTimes != null) if (terminalTimes != null)
{ {
this.labelTerminalBerth.Content = this.ShipcallControlModel?.GetBerthText(terminalTimes); this.labelTerminalBerth.Content = this.ShipcallControlModel?.GetBerthText(terminalTimes);
this.labelOperationsStart.Content = terminalTimes.OperationsStart.HasValue ? terminalTimes.OperationsStart.Value.ToString("dd.MM.yyyy HH:mm") : "- / -"; this.labelOperationsStart.Content = terminalTimes.DisplayTime(this.ShipcallControlModel?.Shipcall?.Type == ShipcallType.Arrival);
this.textBlockTerminalRemarks.Text = terminalTimes.Remarks; this.textBlockTerminalRemarks.Text = terminalTimes.Remarks.TruncateDots(40);
if (this.ShipcallControlModel?.Shipcall?.Type != 1) this.textBlockTerminalBerthRemarks.Text = terminalTimes.BerthInfo.TruncateDots(40);
{
this.labelOperationsStart.Content = terminalTimes.OperationsEnd.HasValue ? terminalTimes.OperationsEnd.Value.ToString("dd.MM.yyyy HH:mm") : "- / -";
}
this.textBlockTerminalBerthRemarks.Text = terminalTimes.BerthInfo;
} }
else else
{ {
@ -423,11 +447,6 @@ namespace BreCalClient
#region event handler #region event handler
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
// TBD
}
private void buttonEditShipcall_Click(object? sender, RoutedEventArgs? e) private void buttonEditShipcall_Click(object? sender, RoutedEventArgs? e)
{ {
if(App.Participant.IsTypeFlagSet(Extensions.ParticipantType.BSMD)) if(App.Participant.IsTypeFlagSet(Extensions.ParticipantType.BSMD))

View File

@ -1,6 +1,6 @@
// Copyright (c) 2023 schick Informatik // Copyright (c) 2023 schick Informatik
// Description: Container model for shipcall related info // Description: Container model for shipcall related info
// //
using BreCalClient.misc.Api; using BreCalClient.misc.Api;
using BreCalClient.misc.Model; using BreCalClient.misc.Model;
@ -16,17 +16,7 @@ namespace BreCalClient
public class ShipcallControlModel public class ShipcallControlModel
{ {
#region Enumerations #region Enumerations
public enum TrafficLightMode
{
OFF,
GREEN,
YELLOW,
RED,
RED_YELLOW,
ALL
};
[Flags] [Flags]
public enum StatusFlags public enum StatusFlags
@ -44,7 +34,7 @@ namespace BreCalClient
public Shipcall? Shipcall { get; set; } public Shipcall? Shipcall { get; set; }
public Ship? Ship { get; set; } public Ship? Ship { get; set; }
public string? Berth { get; set; } public string? Berth { get; set; }
@ -74,21 +64,21 @@ namespace BreCalClient
} }
} }
public TrafficLightMode LightMode public EvaluationType LightMode
{ {
get get
{ {
TrafficLightMode tlm = TrafficLightMode.OFF; EvaluationType elm = EvaluationType.Undefined;
if (this.Shipcall != null) if (this.Shipcall != null)
{ {
if(this.Shipcall.Evaluation.HasValue) if(this.Shipcall.Evaluation.HasValue)
{ {
tlm = (TrafficLightMode)this.Shipcall.Evaluation; elm = this.Shipcall.Evaluation.Value;
} }
} }
return tlm; return elm;
} }
} }
public string Title public string Title
@ -96,8 +86,7 @@ namespace BreCalClient
get get
{ {
if (this.Shipcall == null) return ""; if (this.Shipcall == null) return "";
Extensions.TypeEnum callType = (Extensions.TypeEnum) this.Shipcall.Type; return string.Format("{0} {1}", this.Shipcall.Type, this.Ship?.Name);
return string.Format("{0} {1}", callType, this.Ship?.Name);
} }
} }
@ -112,7 +101,7 @@ namespace BreCalClient
{ {
foreach (ParticipantAssignment participantAssignment in Shipcall.Participants) foreach (ParticipantAssignment participantAssignment in Shipcall.Participants)
{ {
AssignedParticipants[(Extensions.ParticipantType)participantAssignment.Type] = participantAssignment; AssignedParticipants[(Extensions.ParticipantType)participantAssignment.Type] = participantAssignment;
} }
} }
} }
@ -160,15 +149,15 @@ namespace BreCalClient
public string? GetBerthText(Times times) public string? GetBerthText(Times times)
{ {
string? berthText = null; string? berthText = null;
if ((BreCalLists.AllBerths != null) && times.BerthId.HasValue && (this.Shipcall?.Type != (int)Extensions.TypeEnum.Shifting)) if ((BreCalLists.Berths != null) && times.BerthId.HasValue && (this.Shipcall?.Type != ShipcallType.Shifting))
{ {
Berth? berth = BreCalLists.AllBerths.Find((x) => x.Id == times.BerthId); Berth? berth = BreCalLists.AllBerths.Find((x) => x.Id == times.BerthId);
berthText = berth?.Name; berthText = berth?.Name;
} }
if ((berthText == null) && (times.ParticipantType != (int)Extensions.ParticipantType.TERMINAL)) if ((berthText == null) && (times.ParticipantType != (int) Extensions.ParticipantType.TERMINAL))
{ {
if (this.Shipcall?.Type == (int)Extensions.TypeEnum.Incoming) if (this.Shipcall?.Type == ShipcallType.Arrival)
{ {
Berth? berth = BreCalLists.AllBerths?.Find((x) => x.Id == this.Shipcall?.ArrivalBerthId); Berth? berth = BreCalLists.AllBerths?.Find((x) => x.Id == this.Shipcall?.ArrivalBerthId);
berthText = berth?.Name; berthText = berth?.Name;
@ -183,13 +172,40 @@ namespace BreCalClient
return berthText; 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> /// <summary>
/// After closing the edit shipcall or edit agency dialogs, the times assignments may have changed. /// After closing the edit shipcall or edit agency dialogs, the times assignments may have changed.
/// This function updates the assignments for existing times records accordingly and saves them. /// This function updates the assignments for existing times records accordingly and saves them.
/// </summary> /// </summary>
/// <param name="_api">API reference to PUT eidted times</param> /// <param name="_api">API reference to PUT eidted times</param>
internal async void UpdateTimesAssignments(DefaultApi _api) internal async void UpdateTimesAssignments(TimesApi api)
{ {
foreach (Extensions.ParticipantType participantType in this.AssignedParticipants.Keys) foreach (Extensions.ParticipantType participantType in this.AssignedParticipants.Keys)
{ {
@ -198,7 +214,7 @@ namespace BreCalClient
if(times.ParticipantId != this.AssignedParticipants[participantType].ParticipantId) if(times.ParticipantId != this.AssignedParticipants[participantType].ParticipantId)
{ {
times.ParticipantId = this.AssignedParticipants[participantType].ParticipantId; times.ParticipantId = this.AssignedParticipants[participantType].ParticipantId;
await _api.TimesPutAsync(times); await api.TimesUpdateAsync(times);
} }
} }
@ -225,7 +241,7 @@ namespace BreCalClient
foreach(Times times in deleteTimes) foreach(Times times in deleteTimes)
{ {
_api.TimesDelete(times.Id); api.TimesDelete(times.Id);
this.Times.Remove(times); this.Times.Remove(times);
} }
} }
@ -236,11 +252,11 @@ namespace BreCalClient
internal Participant? GetParticipantForType(Extensions.ParticipantType participantType) internal Participant? GetParticipantForType(Extensions.ParticipantType participantType)
{ {
if(AssignedParticipants.ContainsKey(participantType) && BreCalLists.ParticipantLookupDict.ContainsKey(AssignedParticipants[participantType].ParticipantId)) if(AssignedParticipants.ContainsKey(participantType) && BreCalLists.ParticipantLookupDict.ContainsKey(AssignedParticipants[participantType].ParticipantId))
return BreCalLists.ParticipantLookupDict[AssignedParticipants[participantType].ParticipantId]; return BreCalLists.ParticipantLookupDict[AssignedParticipants[participantType].ParticipantId];
return null; return null;
} }
private bool IsFlagSet(StatusFlags flag) private bool IsFlagSet(StatusFlags flag)
{ {
@ -248,7 +264,7 @@ namespace BreCalClient
return (this.Shipcall.Flags & (int) flag) != 0; return (this.Shipcall.Flags & (int) flag) != 0;
} }
#endregion #endregion
} }
} }

31
src/BreCalClient/Util.cs Normal file
View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;
namespace BreCalClient
{
public static class Util
{
public static BitmapImage? LoadImage (byte[] imageData)
{
if (imageData == null || imageData.Length == 0) return null;
var image = new BitmapImage();
using (var mem = new MemoryStream(imageData))
{
mem.Position = 0;
image.BeginInit();
image.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = null;
image.StreamSource = mem;
image.EndInit();
}
image.Freeze();
return image;
}
}
}

View File

@ -12,6 +12,7 @@ from .api import berths
from .api import ships from .api import ships
from .api import login from .api import login
from .api import user from .api import user
from .api import history
from BreCal.brecal_utils.file_handling import get_project_root, ensure_path from BreCal.brecal_utils.file_handling import get_project_root, ensure_path
from BreCal.brecal_utils.test_handling import execute_test_with_pytest, execute_coverage_test from BreCal.brecal_utils.test_handling import execute_test_with_pytest, execute_coverage_test
@ -59,7 +60,7 @@ def create_app(test_config=None):
app.register_blueprint(ships.bp) app.register_blueprint(ships.bp)
app.register_blueprint(login.bp) app.register_blueprint(login.bp)
app.register_blueprint(user.bp) app.register_blueprint(user.bp)
app.register_blueprint(history.bp)
logging.basicConfig(filename='brecal.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)) local_db.initPool(os.path.dirname(app.instance_path))

View File

@ -0,0 +1,21 @@
from flask import Blueprint, request
from .. import impl
from ..services.auth_guard import auth_guard
import json
bp = Blueprint('history', __name__)
@bp.route('/history', methods=['get'])
@auth_guard() # no restriction by role
def GetParticipant():
if 'Authorization' in request.headers:
token = request.headers.get('Authorization')
options = {}
if not 'shipcall_id' in request.args:
return json.dumps("missing parameter"), 400
options["shipcall_id"] = request.args.get("shipcall_id")
return impl.history.GetHistory(options)
else:
return json.dumps("not authenticated"), 403

View File

@ -10,12 +10,10 @@ bp = Blueprint('notifications', __name__)
@bp.route('/notifications', methods=['get']) @bp.route('/notifications', methods=['get'])
@auth_guard() # no restriction by role @auth_guard() # no restriction by role
def GetNotifications(): def GetNotifications():
if 'shipcall_id' in request.args:
if 'participant_id' in request.args:
options = {} options = {}
options["participant_id"] = request.args.get("participant_id")
options["shipcall_id"] = request.args.get("shipcall_id") options["shipcall_id"] = request.args.get("shipcall_id")
return impl.notifications.GetNotifications(options) return impl.notifications.GetNotifications(options)
else: else:
logging.warning("attempt to load notifications without participant id") logging.warning("attempt to load notifications without shipcall id")
return json.dumps("missing argument"), 400 return json.dumps("missing argument"), 400

View File

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

View File

@ -1,7 +1,10 @@
from flask import Blueprint, request from flask import Blueprint, request
from .. import impl from .. import impl
from ..services.auth_guard import auth_guard from ..services.auth_guard import auth_guard
from marshmallow import EXCLUDE
from ..schemas import model
import json import json
import logging
bp = Blueprint('ships', __name__) bp = Blueprint('ships', __name__)
@ -14,3 +17,52 @@ def GetShips():
return impl.ships.GetShips(token) return impl.ships.GetShips(token)
else: else:
return json.dumps("not authenticated"), 403 return json.dumps("not authenticated"), 403
@bp.route('/ships', methods=['post'])
@auth_guard() # no restriction by role
def PostShip():
try:
content = request.get_json(force=True)
loadedModel = model.ShipSchema().load(data=content, many=False, partial=True)
except Exception as ex:
logging.error(ex)
print(ex)
return json.dumps("bad format"), 400
return impl.ships.PostShip(loadedModel)
@bp.route('/ships', methods=['put'])
@auth_guard() # no restriction by role
def PutShip():
try:
content = request.get_json(force=True)
loadedModel = model.ShipSchema().load(data=content, many=False, partial=True, unknown=EXCLUDE)
except Exception as ex:
logging.error(ex)
print(ex)
return json.dumps("bad format"), 400
return impl.ships.PutShip(loadedModel)
@bp.route('/ships', methods=['delete'])
@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:
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(options)

View File

@ -5,4 +5,5 @@ from . import shipcalls
from . import times from . import times
from . import ships from . import ships
from . import login from . import login
from . import user from . import user
from . import history

View File

@ -10,8 +10,6 @@ def GetBerths(token):
No parameters, gets all entries No parameters, gets all entries
""" """
# TODO: validate token
try: try:
pooledConnection = local_db.getPoolConnection() pooledConnection = local_db.getPoolConnection()
commands = pydapper.using(pooledConnection) commands = pydapper.using(pooledConnection)

View File

@ -0,0 +1,39 @@
import json
import logging
import pydapper
import pdb
from ..schemas import model
from ..schemas.model import History
from .. import local_db
def GetHistory(options):
"""
:param options: A dictionary containing all the paramters for the Operations
options["shipcall_id"]: **Id of shipcall**.
"""
try:
pooledConnection = local_db.getPoolConnection()
commands = pydapper.using(pooledConnection)
if "shipcall_id" in options and options["shipcall_id"]:
data = commands.query("SELECT id, participant_id, shipcall_id, timestamp, eta, type, operation FROM history WHERE shipcall_id = ?shipcallid?",
model=History.from_query_row,
param={"shipcallid" : options["shipcall_id"]})
pooledConnection.close()
except Exception as ex:
pdb.pm()
logging.error(ex)
print(ex)
result = {}
result["message"] = "call failed"
return json.dumps("call failed"), 500
return json.dumps(data, default=model.obj_dict), 200, {'Content-Type': 'application/json; charset=utf-8'}

View File

@ -14,7 +14,9 @@ def GetUser(options):
hash = bcrypt.hashpw(options["password"].encode('utf-8'), bcrypt.gensalt( 12 )).decode('utf8') hash = bcrypt.hashpw(options["password"].encode('utf-8'), bcrypt.gensalt( 12 )).decode('utf8')
pooledConnection = local_db.getPoolConnection() pooledConnection = local_db.getPoolConnection()
commands = pydapper.using(pooledConnection) commands = pydapper.using(pooledConnection)
data = commands.query("SELECT id, participant_id, first_name, last_name, user_name, user_email, user_phone, password_hash, api_key, created, modified FROM user WHERE user_name = ?username? OR user_email = ?username?", data = commands.query("SELECT id, participant_id, first_name, last_name, user_name, user_email, user_phone, password_hash, " +
"api_key, notify_email, notify_whatsapp, notify_signal, notify_popup, created, modified FROM user " +
"WHERE user_name = ?username? OR user_email = ?username?",
model=model.User, param={"username" : options["username"]}) model=model.User, param={"username" : options["username"]})
# print(data) # print(data)
if len(data) == 1: if len(data) == 1:

View File

@ -8,22 +8,25 @@ from .. import local_db
def GetNotifications(options): def GetNotifications(options):
""" """
:param options: A dictionary containing all the paramters for the Operations :param options: A dictionary containing all the paramters for the Operations
options["participant_id"]: **Id of participant**. *Example: 2*. Id returned through loading of participant options["shipcall_id"]: **Id**. *Example: 42*. Id of referenced ship call.
options["shipcall_id"]: **Id of ship call**. *Example: 52*. Id given in ship call list
""" """
# Implement your business logic here try:
# All the parameters are present in the options argument
return json.dumps({
"acknowledged": "<boolean>",
"id": "<integer>",
"notification_type": "<string>",
"participant_id": "<integer>",
"times_id": "<integer>",
"timestamp": "<date-time>",
}), 200, {'Content-Type': 'application/json; charset=utf-8'}
pooledConnection = local_db.getPoolConnection()
commands = pydapper.using(pooledConnection)
data = commands.query("SELECT id, shipcall_id, level, type, message, created, modified FROM notification " +
"WHERE shipcall_id = ?scid?", model=model.Notification.from_query_row, param={"scid" : options["shipcall_id"]})
pooledConnection.close()
except Exception as ex:
logging.error(ex)
print(ex)
result = {}
result["message"] = "call failed"
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}
return json.dumps(data, default=model.obj_dict), 200, {'Content-Type': 'application/json; charset=utf-8'}

View File

@ -5,6 +5,7 @@ import pydapper
from ..schemas import model from ..schemas import model
from .. import local_db from .. import local_db
from ..services.auth_guard import check_jwt
from BreCal.database.update_database import evaluate_shipcall_state from BreCal.database.update_database import evaluate_shipcall_state
@ -20,19 +21,15 @@ def GetShipcalls(options):
query = ("SELECT s.id as id, ship_id, type, eta, voyage, etd, arrival_berth_id, departure_berth_id, tug_required, pilot_required, " + 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, " + "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, " + "tidal_window_to, rain_sensitive_cargo, recommended_tugs, anchored, moored_lock, canceled, evaluation, " +
"evaluation_message, s.created as created, s.modified as modified " + "evaluation_message, evaluation_time, evaluation_notifications_sent, s.created as created, s.modified as modified, time_ref_point " +
"FROM shipcall s " + "FROM shipcall s " +
"LEFT JOIN times t ON t.shipcall_id = s.id AND t.participant_type = 8 " + "LEFT JOIN times t ON t.shipcall_id = s.id AND t.participant_type = 8 " +
"WHERE " + "WHERE " +
"(type = 1 AND " + "(type = 1 AND (COALESCE(t.eta_berth, eta) >= DATE(NOW() - INTERVAL %d DAY))) OR " +
"((t.id IS NOT NULL AND t.eta_berth >= DATE(NOW() - INTERVAL %d DAY)) OR " + "((type = 2 OR type = 3) AND (COALESCE(t.etd_berth, etd) >= DATE(NOW() - INTERVAL %d DAY)))" +
"(eta >= DATE(NOW() - INTERVAL %d DAY)))) OR " + "ORDER BY s.id") % (options["past_days"], options["past_days"])
"((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"])
data = commands.query(query, model=model.Shipcall) data = commands.query(query, model=model.Shipcall.from_query_row, buffered=True)
for shipcall in data: for shipcall in data:
participant_query = "SELECT participant_id, type FROM shipcall_participant_map WHERE shipcall_id=?shipcall_id?"; participant_query = "SELECT participant_id, type FROM shipcall_participant_map WHERE shipcall_id=?shipcall_id?";
for record in commands.query(participant_query, model=dict, param={"shipcall_id" : shipcall.id}, buffered=False): for record in commands.query(participant_query, model=dict, param={"shipcall_id" : shipcall.id}, buffered=False):
@ -84,6 +81,10 @@ def PostShipcalls(schemaModel):
continue continue
if key == "evaluation_message": if key == "evaluation_message":
continue continue
if key == "type_value":
continue
if key == "evaluation_value":
continue
if isNotFirst: if isNotFirst:
query += "," query += ","
isNotFirst = True isNotFirst = True
@ -91,6 +92,7 @@ def PostShipcalls(schemaModel):
query += ") VALUES (" query += ") VALUES ("
isNotFirst = False isNotFirst = False
for key in schemaModel.keys(): for key in schemaModel.keys():
param_key = key
if key == "id": if key == "id":
continue continue
if key == "participants": if key == "participants":
@ -103,22 +105,38 @@ def PostShipcalls(schemaModel):
continue continue
if key == "evaluation_message": if key == "evaluation_message":
continue continue
if key == "type":
param_key = "type_value"
if key == "type_value":
continue
if key == "evaluation":
param_key = "evaluation_value"
if key == "evaluation_value":
continue
if isNotFirst: if isNotFirst:
query += "," query += ","
isNotFirst = True isNotFirst = True
query += "?" + key + "?" query += "?" + param_key + "?"
query += ")" query += ")"
logging.info(query)
commands.execute(query, schemaModel) commands.execute(query, schemaModel)
new_id = commands.execute_scalar("select last_insert_id()") new_id = commands.execute_scalar("select last_insert_id()")
# add participant assignments # add participant assignments if we have a list of participants
pquery = "INSERT INTO shipcall_participant_map (shipcall_id, participant_id, type) VALUES (?shipcall_id?, ?participant_id?, ?type?)" if 'participants' in schemaModel:
for participant_assignment in schemaModel["participants"]: pquery = "INSERT INTO shipcall_participant_map (shipcall_id, participant_id, type) VALUES (?shipcall_id?, ?participant_id?, ?type?)"
commands.execute(pquery, param={"shipcall_id" : new_id, "participant_id" : participant_assignment["participant_id"], "type" : participant_assignment["type"]}) for participant_assignment in schemaModel["participants"]:
commands.execute(pquery, param={"shipcall_id" : new_id, "participant_id" : participant_assignment["participant_id"], "type" : participant_assignment["type"]})
# apply 'Traffic Light' evaluation to obtain 'GREEN', 'YELLOW' or 'RED' evaluation state. The function internally updates the mysql database # apply 'Traffic Light' evaluation to obtain 'GREEN', 'YELLOW' or 'RED' evaluation state. The function internally updates the mysql database
evaluate_shipcall_state(mysql_connector_instance=pooledConnection, shipcall_id=new_id) # new_id (last insert id) refers to the shipcall id # evaluate_shipcall_state(mysql_connector_instance=pooledConnection, shipcall_id=new_id) # new_id (last insert id) refers to the shipcall id
# save history data
# TODO: set ETA properly
user_data = check_jwt()
query = "INSERT INTO history (participant_id, shipcall_id, user_id, timestamp, eta, type, operation) VALUES (?pid?, ?scid?, ?uid?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 1, 1)"
commands.execute(query, {"scid" : new_id, "pid" : user_data["participant_id"], "uid" : user_data["id"]})
return json.dumps({"id" : new_id}), 201, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps({"id" : new_id}), 201, {'Content-Type': 'application/json; charset=utf-8'}
@ -158,6 +176,7 @@ def PutShipcalls(schemaModel):
query = "UPDATE shipcall SET " query = "UPDATE shipcall SET "
isNotFirst = False isNotFirst = False
for key in schemaModel.keys(): for key in schemaModel.keys():
param_key = key
if key == "id": if key == "id":
continue continue
if key == "participants": if key == "participants":
@ -170,10 +189,18 @@ def PutShipcalls(schemaModel):
continue continue
if key == "evaluation_message": if key == "evaluation_message":
continue continue
if key == "type":
param_key = "type_value"
if key == "type_value":
continue
if key == "evaluation":
param_key = "evaluation_value"
if key == "evaluation_value":
continue
if isNotFirst: if isNotFirst:
query += ", " query += ", "
isNotFirst = True isNotFirst = True
query += key + " = ?" + key + "? " query += key + " = ?" + param_key + "? "
query += "WHERE id = ?id?" query += "WHERE id = ?id?"
affected_rows = commands.execute(query, param=schemaModel) affected_rows = commands.execute(query, param=schemaModel)
@ -205,6 +232,13 @@ def PutShipcalls(schemaModel):
commands.execute(dquery, param={"existing_id" : elem["id"]}) commands.execute(dquery, param={"existing_id" : elem["id"]})
# apply 'Traffic Light' evaluation to obtain 'GREEN', 'YELLOW' or 'RED' evaluation state. The function internally updates the mysql database # apply 'Traffic Light' evaluation to obtain 'GREEN', 'YELLOW' or 'RED' evaluation state. The function internally updates the mysql database
# evaluate_shipcall_state(mysql_connector_instance=pooledConnection, shipcall_id=schemaModel["id"]) # schemaModel["id"] refers to the shipcall id
# save history data
# TODO: set ETA properly
user_data = check_jwt()
query = "INSERT INTO history (participant_id, shipcall_id, user_id, timestamp, eta, type, operation) VALUES (?pid?, ?scid?, ?uid?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 1, 2)"
commands.execute(query, {"scid" : schemaModel["id"], "pid" : user_data["participant_id"], "uid" : user_data["id"]})
return json.dumps({"id" : schemaModel["id"]}), 200 return json.dumps({"id" : schemaModel["id"]}), 200

View File

@ -10,8 +10,6 @@ def GetShips(token):
No parameters, gets all entries No parameters, gets all entries
""" """
# TODO: validate token
try: try:
pooledConnection = local_db.getPoolConnection() pooledConnection = local_db.getPoolConnection()
@ -35,3 +33,127 @@ def GetShips(token):
def PostShip(schemaModel):
"""
:param schemaModel: The deserialized model of the record to be inserted
"""
# TODO: Validate the incoming data
# This creates a *new* entry
try:
pooledConnection = local_db.getPoolConnection()
commands = pydapper.using(pooledConnection)
query = "INSERT INTO ship ("
isNotFirst = False
for key in schemaModel.keys():
if key == "id":
continue
if key == "created":
continue
if key == "modified":
continue
if isNotFirst:
query += ","
isNotFirst = True
query += key
query += ") VALUES ("
isNotFirst = False
for key in schemaModel.keys():
if key == "id":
continue
if key == "created":
continue
if key == "modified":
continue
if isNotFirst:
query += ","
isNotFirst = True
query += "?" + key + "?"
query += ")"
commands.execute(query, schemaModel)
new_id = commands.execute_scalar("select last_insert_id()")
pooledConnection.close()
return json.dumps({"id" : new_id}), 201, {'Content-Type': 'application/json; charset=utf-8'}
except Exception as ex:
logging.error(ex)
print(ex)
result = {}
result["message"] = "call failed"
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}
def PutShip(schemaModel):
"""
:param schemaModel: The deserialized model of the record to be inserted
"""
# This updates an *existing* entry
try:
pooledConnection = local_db.getPoolConnection()
commands = pydapper.using(pooledConnection)
query = "UPDATE ship SET "
isNotFirst = False
for key in schemaModel.keys():
if key == "id":
continue
if key == "created":
continue
if key == "modified":
continue
if isNotFirst:
query += ", "
isNotFirst = True
query += key + " = ?" + key + "? "
query += "WHERE id = ?id?"
affected_rows = commands.execute(query, param=schemaModel)
pooledConnection.close()
return json.dumps({"id" : schemaModel["id"]}), 200, {'Content-Type': 'application/json; charset=utf-8'}
except Exception as ex:
logging.error(ex)
print(ex)
result = {}
result["message"] = "call failed"
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}
def DeleteShip(options):
"""
:param options: A dictionary containing all the paramters for the Operations
options["id"]
"""
try:
pooledConnection = local_db.getPoolConnection()
commands = pydapper.using(pooledConnection)
affected_rows = commands.execute("UPDATE ship SET deleted = 1 WHERE id = ?id?", param={"id" : options["id"]})
pooledConnection.close()
if affected_rows == 1:
return json.dumps({"id" : options["id"]}), 200, {'Content-Type': 'application/json; charset=utf-8'}
result = {}
result["message"] = "no such record"
return json.dumps(result), 404, {'Content-Type': 'application/json; charset=utf-8'}
except Exception as ex:
logging.error(ex)
print(ex)
result = {}
result["message"] = "call failed"
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}

View File

@ -1,9 +1,11 @@
import json import json
import logging import logging
import traceback
import pydapper import pydapper
from ..schemas import model from ..schemas import model
from .. import local_db from .. import local_db
from ..services.auth_guard import check_jwt
from BreCal.database.update_database import evaluate_shipcall_state from BreCal.database.update_database import evaluate_shipcall_state
@ -14,19 +16,18 @@ def GetTimes(options):
""" """
# TODO: validate token
try: try:
pooledConnection = local_db.getPoolConnection() pooledConnection = local_db.getPoolConnection()
commands = pydapper.using(pooledConnection) commands = pydapper.using(pooledConnection)
data = commands.query("SELECT id, eta_berth, eta_berth_fixed, etd_berth, etd_berth_fixed, lock_time, lock_time_fixed, " + data = commands.query("SELECT id, eta_berth, eta_berth_fixed, etd_berth, etd_berth_fixed, lock_time, lock_time_fixed, " +
"zone_entry, zone_entry_fixed, operations_start, operations_end, remarks, shipcall_id, participant_id, " + "zone_entry, zone_entry_fixed, operations_start, operations_end, remarks, shipcall_id, participant_id, " +
"berth_id, berth_info, pier_side, participant_type, created, modified FROM times " + "berth_id, berth_info, pier_side, participant_type, created, modified, ata, atd, eta_interval_end, etd_interval_end FROM times " +
"WHERE times.shipcall_id = ?scid?", model=model.Times, param={"scid" : options["shipcall_id"]}) "WHERE times.shipcall_id = ?scid?", model=model.Times, param={"scid" : options["shipcall_id"]})
pooledConnection.close() pooledConnection.close()
except Exception as ex: except Exception as ex:
logging.error(traceback.format_exc())
logging.error(ex) logging.error(ex)
print(ex) print(ex)
result = {} result = {}
@ -85,9 +86,16 @@ def PostTimes(schemaModel):
# apply 'Traffic Light' evaluation to obtain 'GREEN', 'YELLOW' or 'RED' evaluation state. The function internally updates the mysql database 'shipcall' # apply 'Traffic Light' evaluation to obtain 'GREEN', 'YELLOW' or 'RED' evaluation state. The function internally updates the mysql database 'shipcall'
evaluate_shipcall_state(mysql_connector_instance=pooledConnection, shipcall_id=schemaModel["shipcall_id"]) # every times data object refers to the 'shipcall_id' evaluate_shipcall_state(mysql_connector_instance=pooledConnection, shipcall_id=schemaModel["shipcall_id"]) # every times data object refers to the 'shipcall_id'
# save history data
# TODO: set ETA properly
user_data = check_jwt()
query = "INSERT INTO history (participant_id, shipcall_id, user_id, timestamp, eta, type, operation) VALUES (?pid?, ?scid?, ?uid?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 2, 1)"
commands.execute(query, {"scid" : schemaModel["shipcall_id"], "pid" : user_data["participant_id"], "uid" : user_data["id"]})
return json.dumps({"id" : new_id}), 201, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps({"id" : new_id}), 201, {'Content-Type': 'application/json; charset=utf-8'}
except Exception as ex: except Exception as ex:
logging.error(traceback.format_exc())
logging.error(ex) logging.error(ex)
print(ex) print(ex)
result = {} result = {}
@ -132,13 +140,21 @@ def PutTimes(schemaModel):
# apply 'Traffic Light' evaluation to obtain 'GREEN', 'YELLOW' or 'RED' evaluation state. The function internally updates the mysql database 'shipcall' # apply 'Traffic Light' evaluation to obtain 'GREEN', 'YELLOW' or 'RED' evaluation state. The function internally updates the mysql database 'shipcall'
evaluate_shipcall_state(mysql_connector_instance=pooledConnection, shipcall_id=schemaModel["shipcall_id"]) # every times data object refers to the 'shipcall_id' evaluate_shipcall_state(mysql_connector_instance=pooledConnection, shipcall_id=schemaModel["shipcall_id"]) # every times data object refers to the 'shipcall_id'
# save history data
# TODO: set ETA properly
user_data = check_jwt()
if "participant_id" in user_data and "id" in user_data:
query = "INSERT INTO history (participant_id, shipcall_id, user_id, timestamp, eta, type, operation) VALUES (?pid?, ?scid?, ?uid?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 2, 2)"
commands.execute(query, {"pid" : user_data["participant_id"], "scid" : schemaModel["shipcall_id"], "uid" : user_data["id"]})
else:
logging.error("user_data does not contain participant_id or id")
# if affected_rows == 1: # this doesn't work as expected # if affected_rows == 1: # this doesn't work as expected
return json.dumps({"id" : schemaModel["id"]}), 200, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps({"id" : schemaModel["id"]}), 200, {'Content-Type': 'application/json; charset=utf-8'}
# return json.dumps("no such record"), 404, {'Content-Type': 'application/json; charset=utf-8'}
except Exception as ex: except Exception as ex:
logging.error(traceback.format_exc())
logging.error(ex) logging.error(ex)
print(ex) print(ex)
result = {} result = {}
@ -159,8 +175,16 @@ def DeleteTimes(options):
pooledConnection = local_db.getPoolConnection() pooledConnection = local_db.getPoolConnection()
commands = pydapper.using(pooledConnection) commands = pydapper.using(pooledConnection)
shipcall_id = commands.execute_scalar("SELECT shipcall_id FROM times WHERE id = ?id?", param={"id" : options["id"]})
affected_rows = commands.execute("DELETE FROM times WHERE id = ?id?", param={"id" : options["id"]}) affected_rows = commands.execute("DELETE FROM times WHERE id = ?id?", param={"id" : options["id"]})
# TODO: set ETA properly?
# save history data
user_data = check_jwt()
query = "INSERT INTO history (participant_id, shipcall_id, user_id, timestamp, eta, type, operation) VALUES (?pid?, ?shipcall_id?, ?uid?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 2, 3)"
commands.execute(query, {"pid" : user_data["participant_id"], "shipcall_id" : shipcall_id, "uid" : user_data["id"]})
if affected_rows == 1: if affected_rows == 1:
return json.dumps({"id" : options["id"]}), 200, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps({"id" : options["id"]}), 200, {'Content-Type': 'application/json; charset=utf-8'}

View File

@ -39,4 +39,4 @@ def getPoolConnection():
global config_path global config_path
f = open(config_path); f = open(config_path);
connection_data = json.load(f) connection_data = json.load(f)
return mysql.connector.connect(**connection_data) return mysql.connector.connect(**connection_data)

View File

@ -1,5 +1,8 @@
from dataclasses import field from dataclasses import field, dataclass
from marshmallow import Schema, fields, INCLUDE, ValidationError from marshmallow import Schema, fields, post_load, INCLUDE, ValidationError
from marshmallow.fields import Field
from marshmallow_enum import EnumField
from enum import IntEnum
from marshmallow_dataclass import dataclass from marshmallow_dataclass import dataclass
from typing import List from typing import List
@ -10,6 +13,8 @@ import datetime
def obj_dict(obj): def obj_dict(obj):
if isinstance(obj, datetime.datetime): if isinstance(obj, datetime.datetime):
return obj.isoformat() return obj.isoformat()
if hasattr(obj, 'to_json'):
return obj.to_json()
return obj.__dict__ return obj.__dict__
@dataclass @dataclass
@ -23,6 +28,81 @@ class Berth(Schema):
modified: datetime modified: datetime
deleted: bool deleted: bool
class OperationType(IntEnum):
undefined = 0
insert = 1
update = 2
delete = 3
class ObjectType(IntEnum):
undefined = 0
shipcall = 1
times = 2
class EvaluationType(IntEnum):
undefined = 0
green = 1
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
arrival = 1
departure = 2
shifting = 3
@classmethod
def _missing_(cls, value):
return cls.undefined
@dataclass
class History:
def __init__(self, id, participant_id, shipcall_id, timestamp, eta, type, operation):
self.id = id
self.participant_id = participant_id
self.shipcall_id = shipcall_id
self.timestamp = timestamp
self.eta = eta
self.type = type
self.operation = operation
pass
id: int
participant_id: int
shipcall_id: int
timestamp: datetime
eta: datetime
type: ObjectType
operation: OperationType
def to_json(self):
return {
"id": self.id,
"participant_id": self.participant_id,
"shipcall_id": self.shipcall_id,
"timestamp": self.timestamp.isoformat() if self.timestamp else "",
"eta": self.eta.isoformat() if self.eta else "",
"type": self.type.name,
"operation": self.operation.name
}
@classmethod
def from_query_row(self, id, participant_id, shipcall_id, timestamp, eta, type, operation):
return self(id, participant_id, shipcall_id, timestamp, eta, ObjectType(type), OperationType(operation))
class Error(Schema): class Error(Schema):
message = fields.String(required=True) message = fields.String(required=True)
@ -31,17 +111,30 @@ class GetVerifyInlineResp(Schema):
pass pass
@dataclass @dataclass
class Notification(Schema): class Notification:
id: int id: int
times_id: int shipcall_id: int
acknowledged: bool
level: int level: int
type: int type: NotificationType
message: str message: str
created: datetime created: datetime
modified: datetime modified: datetime
def to_json(self):
return {
"id": self.id,
"shipcall_id": self.shipcall_id,
"level": self.level,
"type": self.type.name,
"message": self.message,
"created": self.created.isoformat() if self.created else "",
"modified": self.modified.isoformat() if self.modified else ""
}
@classmethod
def from_query_row(self, id, shipcall_id, level, type, message, created, modified):
return self(id, shipcall_id, level, NotificationType(type), message, created, modified)
@dataclass @dataclass
class Participant(Schema): class Participant(Schema):
id: int id: int
@ -69,7 +162,7 @@ class ShipcallSchema(Schema):
id = fields.Int() id = fields.Int()
ship_id = fields.Int() ship_id = fields.Int()
type = fields.Int() type = fields.Enum(ShipcallType, required=True)
eta = fields.DateTime(Required = False, allow_none=True) eta = fields.DateTime(Required = False, allow_none=True)
voyage = fields.Str(allow_none=True, metadata={'Required':False}) # Solving: RemovedInMarshmallow4Warning: Passing field metadata as keyword arguments is deprecated. Use the explicit `metadata=...` argument instead. Additional metadata: {'Required': False} voyage = fields.Str(allow_none=True, metadata={'Required':False}) # Solving: RemovedInMarshmallow4Warning: Passing field metadata as keyword arguments is deprecated. Use the explicit `metadata=...` argument instead. Additional metadata: {'Required': False}
etd = fields.DateTime(Required = False, allow_none=True) etd = fields.DateTime(Required = False, allow_none=True)
@ -90,12 +183,28 @@ class ShipcallSchema(Schema):
anchored = fields.Bool(Required = False, allow_none=True) anchored = fields.Bool(Required = False, allow_none=True)
moored_lock = fields.Bool(Required = False, allow_none=True) moored_lock = fields.Bool(Required = False, allow_none=True)
canceled = fields.Bool(Required = False, allow_none=True) canceled = fields.Bool(Required = False, allow_none=True)
evaluation = fields.Int(Required = False, allow_none=True) evaluation = fields.Enum(EvaluationType, required=False, allow_none=True, default=EvaluationType.undefined)
evaluation_message = fields.Str(allow_none=True, metadata={'Required':False}) # Solving: RemovedInMarshmallow4Warning: Passing field metadata as keyword arguments is deprecated. Use the explicit `metadata=...` argument instead. Additional metadata: {'Required': False} evaluation_message = fields.Str(allow_none=True, metadata={'Required':False}) # Solving: RemovedInMarshmallow4Warning: Passing field metadata as keyword arguments is deprecated. Use the explicit `metadata=...` argument instead. Additional metadata: {'Required': False}
evaluation_time = fields.DateTime(Required = False, allow_none=True)
evaluation_notifications_sent = fields.Bool(Required = False, allow_none=True)
time_ref_point = fields.Int(Required = False, allow_none=True)
participants = fields.List(fields.Nested(ParticipantAssignmentSchema)) participants = fields.List(fields.Nested(ParticipantAssignmentSchema))
created = fields.DateTime(Required = False, allow_none=True) created = fields.DateTime(Required = False, allow_none=True)
modified = fields.DateTime(Required = False, allow_none=True) modified = fields.DateTime(Required = False, allow_none=True)
@post_load
def make_shipcall(self, data, **kwargs):
if 'type' in data:
data['type_value'] = data['type'].value
else:
data['type_value'] = ShipcallType.undefined
if 'evaluation' in data:
if data['evaluation']:
data['evaluation_value'] = data['evaluation'].value
else:
data['evaluation_value'] = EvaluationType.undefined
return data
@dataclass @dataclass
class Participant_Assignment: class Participant_Assignment:
def __init__(self, participant_id, type): def __init__(self, participant_id, type):
@ -104,14 +213,15 @@ class Participant_Assignment:
pass pass
participant_id: int participant_id: int
type: int type: int # a variant would be to use the IntFlag type (with appropriate serialization)
@dataclass @dataclass
class Shipcall: class Shipcall:
id: int id: int
ship_id: int ship_id: int
type: str type: ShipcallType
eta: datetime eta: datetime
voyage: str voyage: str
etd: datetime etd: datetime
@ -132,12 +242,56 @@ class Shipcall:
anchored: bool anchored: bool
moored_lock: bool moored_lock: bool
canceled: bool canceled: bool
evaluation: int evaluation: EvaluationType
evaluation_message: str evaluation_message: str
evaluation_time: datetime
evaluation_notifications_sent: bool
time_ref_point: int
created: datetime created: datetime
modified: datetime modified: datetime
participants: List[Participant_Assignment] = field(default_factory=list) participants: List[Participant_Assignment] = field(default_factory=list)
def to_json(self):
return {
"id": self.id,
"ship_id": self.ship_id,
"type": self.type.name,
"eta": self.eta.isoformat() if self.eta else "",
"voyage": self.voyage,
"etd": self.etd.isoformat() if self.etd else "",
"arrival_berth_id": self.arrival_berth_id,
"departure_berth_id": self.departure_berth_id,
"tug_required": self.tug_required,
"pilot_required": self.pilot_required,
"flags": self.flags,
"pier_side": self.pier_side,
"bunkering": self.bunkering,
"replenishing_terminal": self.replenishing_terminal,
"replenishing_lock": self.replenishing_lock,
"draft": self.draft,
"tidal_window_from": self.tidal_window_from.isoformat() if self.tidal_window_from else "",
"tidal_window_to": self.tidal_window_to.isoformat() if self.tidal_window_to else "",
"rain_sensitive_cargo": self.rain_sensitive_cargo,
"recommended_tugs": self.recommended_tugs,
"anchored": self.anchored,
"moored_lock": self.moored_lock,
"canceled": self.canceled,
"evaluation": self.evaluation.name,
"evaluation_message": self.evaluation_message,
"evaluation_time": self.evaluation_time.isoformat() if self.evaluation_time else "",
"evaluation_notifications_sent": self.evaluation_notifications_sent,
"time_ref_point": self.time_ref_point,
"created": self.created.isoformat() if self.created else "",
"modified": self.modified.isoformat() if self.modified else "",
"participants": [participant.__dict__ for participant in self.participants]
}
@classmethod
def from_query_row(self, 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_time, evaluation_notifications_sent, time_ref_point, created, modified):
return self(id, ship_id, ShipcallType(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, EvaluationType(evaluation), evaluation_message, evaluation_time, evaluation_notifications_sent, time_ref_point, created, modified)
class ShipcallId(Schema): class ShipcallId(Schema):
pass pass
@ -166,6 +320,10 @@ class TimesSchema(Schema):
pier_side = fields.Bool(Required = False, allow_none = True) pier_side = fields.Bool(Required = False, allow_none = True)
shipcall_id = fields.Int(Required = True) shipcall_id = fields.Int(Required = True)
participant_type = fields.Int(Required = False, allow_none=True) participant_type = fields.Int(Required = False, allow_none=True)
ata = fields.DateTime(Required = False, allow_none=True)
atd = fields.DateTime(Required = False, allow_none=True)
eta_interval_end = fields.DateTime(Required = False, allow_none=True)
etd_interval_end = fields.DateTime(Required = False, allow_none=True)
created = fields.DateTime(Required = False, allow_none=True) created = fields.DateTime(Required = False, allow_none=True)
modified = fields.DateTime(Required = False, allow_none=True) modified = fields.DateTime(Required = False, allow_none=True)
@ -185,7 +343,6 @@ class UserSchema(Schema):
@dataclass @dataclass
class Times: class Times:
id: int id: int
eta_berth: datetime eta_berth: datetime
eta_berth_fixed: bool eta_berth_fixed: bool
@ -204,6 +361,10 @@ class Times:
pier_side: bool pier_side: bool
participant_type: int participant_type: int
shipcall_id: int shipcall_id: int
ata: datetime
atd: datetime
eta_interval_end: datetime
etd_interval_end: datetime
created: datetime created: datetime
modified: datetime modified: datetime
@ -219,11 +380,15 @@ class User:
user_phone: str user_phone: str
password_hash: str password_hash: str
api_key: str api_key: str
notify_email: bool
notify_whatsapp: bool
notify_signal: bool
notify_popup: bool
created: datetime created: datetime
modified: datetime modified: datetime
@dataclass @dataclass
class Ship(Schema): class Ship:
id: int id: int
name: str name: str
imo: int imo: int
@ -233,11 +398,32 @@ class Ship(Schema):
width: float width: float
is_tug: bool is_tug: bool
bollard_pull: int bollard_pull: int
eni: str eni: int
created: datetime created: datetime
modified: datetime modified: datetime
deleted: bool deleted: bool
class ShipSchema(Schema):
def __init__(self):
super().__init__(unknown=None)
pass
id = fields.Int(Required=False)
name = fields.String(allow_none=False, metadata={'Required':True})
imo = fields.Int(allow_none=False, metadata={'Required':True})
callsign = fields.String(allow_none=True, metadata={'Required':False})
participant_id = fields.Int(allow_none=True, metadata={'Required':False})
length = fields.Float(allow_none=True, metadata={'Required':False})
width = fields.Float(allow_none=True, metadata={'Required':False})
is_tug = fields.Bool(allow_none=True, metadata={'Required':False}, default=False)
bollard_pull = fields.Int(allow_none=True, metadata={'Required':False})
eni = fields.Int(allow_none=True, metadata={'Required':False})
created = fields.DateTime(allow_none=True, metadata={'Required':False})
modified = fields.DateTime(allow_none=True, metadata={'Required':False})
deleted = fields.Bool(allow_none=True, metadata={'Required':False}, default=False)
class TimesId(Schema): class TimesId(Schema):
pass pass

View File

@ -25,11 +25,15 @@ def UpdateShipcalls(options:dict = {'past_days':2}):
try: try:
pooledConnection = getPoolConnection() pooledConnection = getPoolConnection()
commands = pydapper.using(pooledConnection) 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, " query = ("SELECT s.id as id, ship_id, type, eta, voyage, etd, arrival_berth_id, departure_berth_id, tug_required, pilot_required, "
"anchored, moored_lock, canceled, evaluation, evaluation_message, created, modified FROM shipcall WHERE ((type = 1 OR type = 3) AND eta >= DATE(NOW() - INTERVAL %d DAY)" "flags, s.pier_side, bunkering, replenishing_terminal, replenishing_lock, draft, tidal_window_from, tidal_window_to, rain_sensitive_cargo, recommended_tugs, "
"OR (type = 2 AND etd >= DATE(NOW() - INTERVAL %d DAY))) " "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 " +
"ORDER BY eta") % (options["past_days"], options["past_days"]) "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 # obtain data from the MYSQL database
data = commands.query(query, model=model.Shipcall) data = commands.query(query, model=model.Shipcall)
@ -54,6 +58,9 @@ def add_function_to_schedule__update_shipcalls(interval_in_minutes:int, options:
return return
def setup_schedule(update_shipcalls_interval_in_minutes:int=60): 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 schedule.clear() # clear all routine jobs. This prevents jobs from being created multiple times
# update the evaluation state in every recent shipcall # update the evaluation state in every recent shipcall

View File

@ -41,40 +41,44 @@ def get_shipcall_simple():
canceled = False canceled = False
evaluation = None evaluation = None
evaluation_message = "" evaluation_message = ""
evaluation_time = None
evaluation_notifications_sent = False
created = datetime.datetime.now() created = datetime.datetime.now()
modified = created+datetime.timedelta(seconds=10) modified = created+datetime.timedelta(seconds=10)
participants = [generate_uuid1_int(), generate_uuid1_int(), generate_uuid1_int(), generate_uuid1_int()] # field(default_factory=[generate_uuid1_int(), generate_uuid1_int(), generate_uuid1_int(), generate_uuid1_int()]) # list participants = [generate_uuid1_int(), generate_uuid1_int(), generate_uuid1_int(), generate_uuid1_int()] # field(default_factory=[generate_uuid1_int(), generate_uuid1_int(), generate_uuid1_int(), generate_uuid1_int()]) # list
shipcall = Shipcall( shipcall = Shipcall(
shipcall_id, shipcall_id,
ship_id, ship_id,
role_type, role_type,
eta, eta,
voyage, voyage,
etd, etd,
arrival_berth_id, arrival_berth_id,
departure_berth_id, departure_berth_id,
tug_required, tug_required,
pilot_required, pilot_required,
flags, flags,
pier_side, pier_side,
bunkering, bunkering,
replenishing_terminal, replenishing_terminal,
replenishing_lock, replenishing_lock,
draft, draft,
tidal_window_from, tidal_window_from,
tidal_window_to, tidal_window_to,
rain_sensitive_cargo, rain_sensitive_cargo,
recommended_tugs, recommended_tugs,
anchored, anchored,
moored_lock, moored_lock,
canceled, canceled,
evaluation, evaluation,
evaluation_message, evaluation_message,
created, evaluation_time,
modified, evaluation_notifications_sent,
participants, created,
modified,
participants,
) )
return shipcall return shipcall

View File

@ -57,6 +57,7 @@ class ValidationRuleBaseFunctions():
self.error_message_dict = error_message_dict self.error_message_dict = error_message_dict
# as of 23 dec. 2023 port authority validation is temporarily disabled # 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_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: def describe_error_message(self, key)->str:
""" """
@ -106,7 +107,7 @@ class ValidationRuleBaseFunctions():
violation_state = (delta<=threshold) violation_state = (delta<=threshold)
return violation_state 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 # 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 - the shipcall belongs to a different type than the rule expects
- there are no matching times for the provided {query} (e.g., "eta_berth") - 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. This method computes the absolute time difference between all time entries. A threshold (in seconds) is used
When there is not only one unique value, there are deviating time estimates, and a violation occurs 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) returns: violation_state (bool)
""" """
@ -136,14 +139,14 @@ class ValidationRuleBaseFunctions():
participant_types = [ParticipantType.AGENCY.value, ParticipantType.MOORING.value, ParticipantType.PILOT.value, ParticipantType.TUG.value] 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,:] agency_times = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value,:]
df_times = df_times.loc[df_times["participant_type"].isin(participant_types),:] if len(agency_times)==0:
agency_time = [time_ for time_ in agency_times.loc[:,query].tolist() if isinstance(time_, pd.Timestamp)]
if not len(agency_time):
violation_state = False violation_state = False
return violation_state 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) # 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(),:] 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: if len(estimated_times)==0:
violation_state = False violation_state = False
return violation_state return violation_state
# this (current) solution compares times to the reference (agency) time and checks if the difference is greater than 15 minutes # for the given query, e.g., 'eta_berth', sample all times from the pandas DataFrame
violation_state = ((np.max(estimated_times) - agency_time[0]) > pd.Timedelta("15min")) or ((agency_time[0] - np.min(estimated_times)) > pd.Timedelta("15min")) 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 # 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 # Consequently, it treats all times as equally important
@ -161,7 +180,7 @@ class ValidationRuleBaseFunctions():
# violation_state = difference > pd.Timedelta("15min") # 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 # 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 # 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 # 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. - Checks, if times_terminal.operations_start is filled in.
- Measures the difference between 'now' and 'times_agency.eta_berth'. - 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]: if not shipcall.type in [ShipcallType.INCOMING.value]:
return self.get_no_violation_default_output() return self.get_no_violation_default_output()
@ -615,6 +637,9 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
- Checks, if times_terminal.operations_end is filled in. - Checks, if times_terminal.operations_end is filled in.
- Measures the difference between 'now' and 'times_agency.etd_berth'. - 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]: if not shipcall.type in [ShipcallType.OUTGOING.value, ShipcallType.SHIFTING.value]:
return self.get_no_violation_default_output() return self.get_no_violation_default_output()
@ -730,6 +755,9 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
query time: eta_berth (times_agency) query time: eta_berth (times_agency)
start_time & end_time: operations_start & operations_end (times_terminal) 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]: if not shipcall.type in [ShipcallType.INCOMING.value]:
return self.get_no_violation_default_output() return self.get_no_violation_default_output()
@ -770,6 +798,9 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
query time: eta_berth (times_agency) query time: eta_berth (times_agency)
start_time & end_time: operations_start & operations_end (times_terminal) 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]: if not shipcall.type in [ShipcallType.OUTGOING.value, ShipcallType.SHIFTING.value]:
return self.get_no_violation_default_output() return self.get_no_violation_default_output()

View File

@ -1,4 +1,5 @@
import copy import copy
import logging
import re import re
import numpy as np import numpy as np
import pandas as pd 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. # 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) # self.notification_state = self.determine_notification_state() # (state:str, should_notify:bool)
return return
def evaluate(self, shipcall): def evaluate(self, shipcall):
""" """
1.) prepare df_times, which every validation rule tends to use 1.) prepare df_times, which every validation rule tends to use
@ -34,7 +35,7 @@ class ValidationRules(ValidationRuleFunctions):
if len(df_times)==0: if len(df_times)==0:
return (StatusFlags.GREEN.value, []) return (StatusFlags.GREEN.value, [])
spm = self.sql_handler.df_dict["shipcall_participant_map"] spm = self.sql_handler.df_dict["shipcall_participant_map"]
if len(spm.loc[spm["shipcall_id"]==shipcall.id])==0: if len(spm.loc[spm["shipcall_id"]==shipcall.id])==0:
return (StatusFlags.GREEN.value, []) return (StatusFlags.GREEN.value, [])
@ -51,12 +52,14 @@ class ValidationRules(ValidationRuleFunctions):
# 'translate' all error codes into readable, human-understandable format. # 'translate' all error codes into readable, human-understandable format.
evaluation_results = [(state, self.describe_error_message(msg)) for (state, msg) in evaluation_results] 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 # 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_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] evaluation_verbosity = [result[1] for result in evaluation_results]
return (evaluation_state, evaluation_verbosity) return (evaluation_state, evaluation_verbosity)
def evaluation_verbosity(self, evaluation_state, evaluation_results): 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.""" """This function suggestions verbosity for the evaluation results. Based on 'True'/'False' evaluation outcome, the returned string is different."""
if evaluation_state: if evaluation_state:
@ -64,17 +67,17 @@ class ValidationRules(ValidationRuleFunctions):
else: 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 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}" return f"FAILED VALIDATION. There have been {len(evaluation_results)} violations. {verbose_string}"
def evaluate_shipcall_from_df(self, x): def evaluate_shipcall_from_df(self, x):
shipcall = Shipcall(**{**{'id':x.name}, **x.to_dict()}) shipcall = Shipcall(**{**{'id':x.name}, **x.to_dict()})
evaluation_state, violations = self.evaluate(shipcall) evaluation_state, violations = self.evaluate(shipcall)
return evaluation_state, violations return evaluation_state, violations
def evaluate_shipcalls(self, shipcall_df:pd.DataFrame)->pd.DataFrame: 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)""" """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 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] 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 = [",\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] violations = [self.concise_evaluation_message_if_too_long(violation) for violation in violations]
@ -90,31 +93,31 @@ class ValidationRules(ValidationRuleFunctions):
""" """
if violation is None: if violation is None:
return violation return violation
if len(violation)>=512: if len(violation)>=512:
concise = re.findall(r'{(.*?)\}', violation) 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'] # 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}" violation = f"Evaluation message too long. Violated Rules: {concise}"
return violation return violation
def determine_validation_state(self) -> str: def determine_validation_state(self) -> str:
""" """
this method determines the validation state of a shipcall. The state is either ['green', 'yellow', 'red'] and signals, 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) returns: validation_state_new (str)
""" """
(validation_state_new, description) = self.undefined_method() (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 self.validation_state = validation_state_new
return validation_state_new return validation_state_new
def determine_notification_state(self) -> (str, bool): 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, 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, 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) returns: notification_state_new (str), should_notify (bool)
""" """
@ -122,10 +125,10 @@ class ValidationRules(ValidationRuleFunctions):
should_notify = self.identify_notification_state_change(state_new) should_notify = self.identify_notification_state_change(state_new)
self.notification_state = state_new # overwrite the predecessor self.notification_state = state_new # overwrite the predecessor
return state_new, should_notify return state_new, should_notify
def identify_notification_state_change(self, state_new) -> bool: 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. 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: state changes trigger a notification in the following cases:
@ -135,14 +138,14 @@ class ValidationRules(ValidationRuleFunctions):
(none -> yellow) or (none -> red) (none -> yellow) or (none -> red)
due to the values in the enumeration objects, the states are mapped to provide this function. 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 returns bool, whether a notification should be triggered
""" """
# state_old is always considered at least 'Green' (1) # 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) 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 return state_new.value > state_old.value
def undefined_method(self) -> str: def undefined_method(self) -> str:
"""this function should apply the ValidationRules to the respective .shipcall, in regards to .times""" """this function should apply the ValidationRules to the respective .shipcall, in regards to .times"""
# #TODO_traffic_state # #TODO_traffic_state

View File

@ -2,7 +2,7 @@ from setuptools import find_packages, setup
setup( setup(
name='BreCal', name='BreCal',
version='1.1.6', version='1.2.0',
packages=find_packages(), packages=find_packages(),
include_package_data=True, include_package_data=True,
zip_safe=False, zip_safe=False,