This repository has been archived on 2025-02-17. You can view files and clone it, but cannot push or open issues or pull requests.
BreCal/src/BreCalClient/ShipcallControlModel.cs

374 lines
12 KiB
C#

// Copyright (c) 2023 schick Informatik
// Description: Container model for shipcall related info
//
using BreCalClient.misc.Api;
using BreCalClient.misc.Model;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace BreCalClient
{
/// <summary>
/// Container model to aggregate separate models for the Shipcall control data binding
/// </summary>
public class ShipcallControlModel
{
#region Enumerations
[Flags]
public enum StatusFlags
{
RED,
GREEN,
YELLOW,
BLINK_1,
BLINK_2
};
#endregion
#region Properties
public Shipcall? Shipcall { get; set; }
public Ship? Ship { get; set; }
public string? Berth { get; set; }
public string LastErrorMessage { get; private set; } = "";
internal Dictionary<Extensions.ParticipantType, ParticipantAssignment> AssignedParticipants { get; } = new();
public List<Times> Times { get; set; } = new();
public DateTime? Eta
{
get
{
Times? agencyTimes = this.GetTimesForParticipantType(Extensions.ParticipantType.AGENCY);
if((agencyTimes != null) && (agencyTimes.EtaBerth != null))
return agencyTimes.EtaBerth;
return Shipcall?.Eta;
}
}
public DateTime? Etd
{
get
{
Times? agencyTimes = this.GetTimesForParticipantType(Extensions.ParticipantType.AGENCY);
if ((agencyTimes != null) && (agencyTimes.EtdBerth != null))
return agencyTimes.EtdBerth;
return Shipcall?.Etd;
}
}
public EvaluationType LightMode
{
get
{
EvaluationType elm = EvaluationType.Undefined;
if (this.Shipcall != null)
{
if(this.Shipcall.Evaluation.HasValue)
{
elm = this.Shipcall.Evaluation.Value;
}
}
return elm;
}
}
public string Title
{
get
{
if (this.Shipcall == null) return "";
return string.Format("{0} {1}", this.Shipcall.Type, this.Ship?.Name);
}
}
/// <summary>
/// This property attempts to store the (hopefully short) shifting sequence in the topmost
/// byte of the "flags" integer which will not be used for the forseeable future
///
/// This is a workaround to avoid adding another field to the interface and is only used in the
/// client display anyway. You could say hack as well.
/// </summary>
public byte? ShiftSequence
{
get
{
if((this.Shipcall == null) || (this.Shipcall.Flags == null)) return null;
return (byte?) (this.Shipcall?.Flags >> 24);
}
set
{
if ((value != null) && (this.Shipcall != null))
{
int currentFlag = this.Shipcall.Flags ?? 0;
int moveUp = ((value ?? 0) << 24);
this.Shipcall.Flags = (currentFlag & 0xffffff) | moveUp;
}
}
}
public bool AllowPortChange { get; set; } = true;
#endregion
#region public methods
public void AssignParticipants()
{
this.AssignedParticipants.Clear();
if (Shipcall != null)
{
foreach (ParticipantAssignment participantAssignment in Shipcall.Participants)
{
AssignedParticipants[(Extensions.ParticipantType)participantAssignment.Type] = participantAssignment;
}
}
}
internal Times? GetTimesForParticipantType(Extensions.ParticipantType type)
{
if (AssignedParticipants.ContainsKey(type)) {
int participantId = AssignedParticipants[type].ParticipantId;
foreach (Times times in this.Times)
{
if ((times.ParticipantId == participantId) && (times.ParticipantType == (int) type))
return times;
}
if(type == Extensions.ParticipantType.AGENCY)
{
// if I am BSMD and no agency entry was found this means we are editing the agency entry
if(App.Participant.Type == (int) Extensions.ParticipantType.BSMD)
{
foreach(Times times in this.Times)
{
if ((times.ParticipantId == App.Participant.Id) && (times.ParticipantType == (int) Extensions.ParticipantType.AGENCY))
return times;
}
}
}
}
return null;
}
public bool ContainsRemarkText(string text)
{
if(Shipcall != null)
{
foreach(Times times in this.Times)
{
if (times.Remarks == null) continue;
if(times.Remarks.Contains(text)) return true;
}
}
return false;
}
/// <summary>
/// Get berth display text for columns AGENT and TERMINAL
/// </summary>
public string? GetBerthText(Times? times)
{
string? timesBerthText = null;
string? scArrivalBerthText = null;
string? scDepartureBerthText = null;
string? berthText = null;
Berth? berth;
if((times != null) && times.BerthId.HasValue)
{
berth = BreCalLists.AllBerths.Find((x) => x.Id == times.BerthId.Value);
timesBerthText = berth?.Name;
}
berth = BreCalLists.AllBerths?.Find((x) => x.Id == this.Shipcall?.ArrivalBerthId);
scArrivalBerthText = berth?.Name;
berth = BreCalLists.AllBerths?.Find((x) => x.Id == this.Shipcall?.DepartureBerthId);
scDepartureBerthText= berth?.Name;
switch(this.Shipcall?.Type)
{
case ShipcallType.Arrival:
berthText = timesBerthText ?? scArrivalBerthText;
break;
case ShipcallType.Departure:
berthText = timesBerthText ?? scDepartureBerthText;
break;
case ShipcallType.Shifting:
if (times?.ParticipantType != (int)Extensions.ParticipantType.TERMINAL)
berthText = (scDepartureBerthText ?? "") + " / " + (timesBerthText ?? scArrivalBerthText);
else
berthText = scDepartureBerthText ?? "";
break;
default: break;
}
return berthText;
}
public string GetETAETD(bool useShortVersion = false)
{
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;
}
}
if(useShortVersion)
return theDate.ToString("dd.MM. HH:mm");
return theDate.ToString();
}
/// <summary>
/// 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.
/// </summary>
/// <param name="_api">API reference to PUT eidted times</param>
internal async Task<bool> UpdateTimesAssignments(TimesApi api)
{
foreach (Extensions.ParticipantType participantType in this.AssignedParticipants.Keys)
{
Times? times = this.GetTimesForParticipantType(participantType);
if(times == null) continue;
if(times.ParticipantId != this.AssignedParticipants[participantType].ParticipantId)
{
times.ParticipantId = this.AssignedParticipants[participantType].ParticipantId;
try
{
await api.TimesUpdateAsync(times);
}
catch (Exception ex)
{
LastErrorMessage = ex.Message;
return false;
}
}
}
// if somebody just removed an assignment it is gone fom AssignedParticipants, but there is still a
// times record left which must be removed
List<Times> deleteTimes = new();
foreach (Times times in this.Times)
{
bool foundTimes = false;
foreach (ParticipantAssignment pa in this.AssignedParticipants.Values)
{
if((pa.ParticipantId == times.ParticipantId) && (pa.Type == times.ParticipantType))
{
foundTimes = true;
break;
}
}
if (!foundTimes)
{
deleteTimes.Add(times);
}
}
foreach(Times times in deleteTimes)
{
try
{
api.TimesDelete(times.Id);
this.Times.Remove(times);
}
catch (Exception ex)
{
LastErrorMessage = ex.Message;
return false;
}
}
return true;
}
public DateTime? GetLastChangeForType(Extensions.ParticipantType type)
{
DateTime? lastChange = null;
Times? times = GetTimesForParticipantType(type);
if(times != null)
{
if (times.Modified.HasValue) lastChange = times.Modified.Value;
else lastChange = times.Created;
}
return lastChange;
}
public Extensions.ParticipantType? LastParticipantTypeToUpdate()
{
Extensions.ParticipantType? last = null;
DateTime min = DateTime.MinValue;
foreach(Times times in this.Times)
{
if (times.Modified.HasValue)
{
if (times.Modified.Value > min)
{
min = times.Modified.Value;
last = (Extensions.ParticipantType)times.ParticipantType;
continue;
}
}
if(times.Created > min)
{
min = times.Created;
last = (Extensions.ParticipantType)times.ParticipantType;
}
}
return last;
}
#endregion
#region helper
internal Participant? GetParticipantForType(Extensions.ParticipantType participantType)
{
if(AssignedParticipants.ContainsKey(participantType) && BreCalLists.ParticipantLookupDict.ContainsKey(AssignedParticipants[participantType].ParticipantId))
return BreCalLists.ParticipantLookupDict[AssignedParticipants[participantType].ParticipantId];
return null;
}
private bool IsFlagSet(StatusFlags flag)
{
if(this.Shipcall == null) return false;
return (this.Shipcall.Flags & (int) flag) != 0;
}
#endregion
}
}