/* * GNU AFFERO GENERAL PUBLIC LICENSE * Version 3, 19 November 2007 * Copyright (C) 2007 Free Software Foundation, Inc. * Everyone is permitted to copy and distribute verbatim copies * of this license document, but changing it is not allowed. */ using System; namespace UVtools.Core.Objects; /// /// Represents a material to feed in the printer /// public class Material : BindableBase, ICloneable { #region Members private string _name = null!; private uint _bottleVolume = 1000; private decimal _density = 1; private decimal _bottleCost = 30; private int _bottlesInStock = 1; private decimal _bottleRemainingVolume = 1000; private decimal _consumedVolume; private double _printTime; #endregion #region Properties public string Name { get => _name; set => RaiseAndSetIfChanged(ref _name, value); } /// /// Gets or sets the bottle volume in milliliters /// public uint BottleVolume { get => _bottleVolume; set { if(!RaiseAndSetIfChanged(ref _bottleVolume, value)) return; RaisePropertyChanged(nameof(BottleWeight)); RaisePropertyChanged(nameof(ConsumedBottles)); RaisePropertyChanged(nameof(TotalCost)); RaisePropertyChanged(nameof(VolumeInStock)); } } /// /// Gets or sets the bottle weight in grams /// public decimal BottleWeight => _bottleVolume * _density; /// /// Gets or sets the material density in g/ml /// public decimal Density { get => _density; set { if(!RaiseAndSetIfChanged(ref _density, value)) return; RaisePropertyChanged(nameof(BottleWeight)); } } /// /// Gets or sets the bottle cost /// public decimal BottleCost { get => _bottleCost; set { if(!RaiseAndSetIfChanged(ref _bottleCost, value)) return; RaisePropertyChanged(nameof(TotalCost)); } } public decimal TotalCost => OwnedBottles * _bottleCost; /// /// Gets or sets the number of bottles in stock /// public int BottlesInStock { get => _bottlesInStock; set { if(!RaiseAndSetIfChanged(ref _bottlesInStock, value)) return; RaisePropertyChanged(nameof(OwnedBottles)); RaisePropertyChanged(nameof(TotalCost)); RaisePropertyChanged(nameof(VolumeInStock)); } } /// /// Gets or sets the current bottle remaining material in milliliters /// public decimal BottleRemainingVolume { get => Math.Round(_bottleRemainingVolume, 2); set { if(!RaiseAndSetIfChanged(ref _bottleRemainingVolume, value)) return; RaisePropertyChanged(nameof(VolumeInStock)); } } /// /// Gets the total available volume in stock in milliliters /// public decimal VolumeInStock => _bottlesInStock * _bottleVolume - (_bottleVolume - _bottleRemainingVolume); /// /// Gets the number of consumed bottles /// public uint ConsumedBottles => (uint)(_consumedVolume / _bottleVolume); /// /// Gets the total number of owned bottles /// public int OwnedBottles => (int) (_bottlesInStock + ConsumedBottles); /// /// Gets or sets the total number of consumed volume in milliliters /// public decimal ConsumedVolume { get => _consumedVolume; set { if(!RaiseAndSetIfChanged(ref _consumedVolume, value)) return; RaisePropertyChanged(nameof(ConsumedVolumeLiters)); } } /// /// Gets total number of consumed volume in liters /// public decimal ConsumedVolumeLiters => ConsumedVolume / 1000; /// /// Gets or sets the total print time using with material in hours /// public double PrintTime { get => _printTime; set { if(!RaiseAndSetIfChanged(ref _printTime, value)) return; RaisePropertyChanged(nameof(PrintTimeSpan)); } } public TimeSpan PrintTimeSpan => TimeSpan.FromHours(_printTime); #endregion #region Constructors public Material() { } public Material(string name, uint bottleVolume = 1000, decimal density = 1, decimal bottleCost = 30, int bottlesInStock = 1) { _name = name; _bottleVolume = bottleVolume; _density = density; _bottleCost = bottleCost; _bottlesInStock = bottlesInStock; _bottleRemainingVolume = bottleVolume; } #endregion #region Overrides protected bool Equals(Material other) { return _name == other._name; } public override bool Equals(object? obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != GetType()) return false; return Equals((Material) obj); } public override int GetHashCode() { return (_name != null ? _name.GetHashCode() : 0); } public override string ToString() { return $"{_name} ({_bottleRemainingVolume}/{VolumeInStock}ml)"; } public object Clone() { return MemberwiseClone(); } public Material CloneMaterial() { return (Material)Clone(); } #endregion #region Methods /// /// Gets the cost for a given volume /// /// Volume in ml /// public decimal GetVolumeCost(decimal volume) => _bottleVolume > 0 ? volume * _bottleCost / _bottleVolume : 0; /// /// Gets the grams for a given volume /// /// Volume in ml /// public decimal GetVolumeGrams(decimal volume) => volume * _density; /// /// Consume material from current bottle and manage stock /// /// Volume to consume in milliliters /// Time in seconds it took to print /// True if still have bottles in stock, otherwise false public bool Consume(decimal volume, double printSeconds = 0) { if (volume <= 0 || _bottleVolume == 0) return true; // Safe check int consumedBottles = (int)(volume / _bottleVolume); decimal remainder = volume % _bottleVolume; if (remainder > 0) { decimal remainingVolume = _bottleRemainingVolume - remainder; if (remainingVolume < 0) { consumedBottles++; remainingVolume += _bottleVolume; } BottleRemainingVolume = remainingVolume; } BottlesInStock -= consumedBottles; ConsumedVolume += volume; AddPrintTimeSeconds(printSeconds); return _bottlesInStock > 0; } /// /// Add print time with this material /// /// Seconds to add public void AddPrintTimeSeconds(double seconds) { if (seconds <= 0) return; PrintTime += seconds / 60 / 60; } #endregion }