<?php
/**
* @license SILK SOFTWARE HOUSE SP Z O O
*/
namespace App\Entity;
use App\Entity\Traits\IdTrait;
use App\Entity\Traits\TimePreUpdateTrait;
use App\Entity\Traits\TimeTrait;
use App\Model\DefaultStrengthPoints;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use DateTime;
use Symfony\Component\Validator\Constraints as Assert;
use App\Validator\Constraints as AppAssert;
/**
* Class ChannelSetting.
* Simple channel consists of measurement channel. Calculated channel consist of sum of children channel settings.
*
* @ORM\Table("channel_settings")
* @ORM\Entity(repositoryClass="App\Repository\ChannelSettingRepository")
* @ORM\HasLifecycleCallbacks()
*/
class ChannelSetting
{
use IdTrait;
use TimeTrait;
use TimePreUpdateTrait;
/**
* Measurement setting.
*
* @var MeasurementSetting|null
*
* @ORM\ManyToOne(targetEntity="App\Entity\MeasurementSetting", inversedBy="channelSettings")
* @ORM\JoinColumn(name="measurement_setting_id", nullable=true)
*/
private $measurementSetting;
/**
* Channel has to be set for simple channel display - it can be empty when this channel is calculated from different ones.
*
* @var Channel|null
*
* @ORM\ManyToOne(targetEntity="App\Entity\Channel", inversedBy="channelSettings")
* @ORM\JoinColumn(name="channel_id", nullable=true)
*/
private $channel;
/**
* Priority.
*
* @var int|null
*
* @ORM\Column(name="priority", type="integer", nullable=true, options={"comment": "Channel priority."})
*/
private $priority;
/**
* Channel name.
*
* @Assert\Length(max=255)
*
* @var string|null
*
* @ORM\Column(name="name", type="string", length=255, nullable=true, options={"comment": "Channel name."})
*/
private $name;
/**
* @var int|null
*
* @ORM\Column(name="number", type="integer", nullable=true, options={"comment": "Channel number."})
*/
private $number;
/**
* Recipe.
*
* @var Recipe|null
*
* @ORM\ManyToOne(targetEntity="App\Entity\Recipe")
* @ORM\JoinColumn(name="recipe_id", nullable=true)
*/
private $recipe;
/**
* When set it means that this channel will be calculated from few others.
* Watch out for circular reference!!!
*
* @var ChannelSettingCoefficient[]|ArrayCollection
*
* @ORM\OneToMany(targetEntity="App\Entity\ChannelSettingCoefficient", cascade={"persist", "remove"}, mappedBy="parentSetting", orphanRemoval=true)
*
* @Assert\Valid()
*
* @AppAssert\NotCircular()
*/
private $channelSettingCoefficients;
/**
* Is average weighted.
*
* @var bool
*
* @ORM\Column(name="is_average_weighted", type="boolean", nullable=false, options={"default": false, "comment": "Is average weighted."})
*/
private $isAverageWeighted = false;
/**
* Mixing time point.
*
* @var DateTime|null
*
* @Assert\LessThan(
* message="This value should be less than measurement starting time {{ compared_value }}.",
* propertyPath="measurementStartedAt"
* )
*
* @ORM\Column(name="mixing_time_point", type="datetime", nullable=true, options={"comment": "Mixing time point."})
*/
private $mixingTimePoint;
/**
* Mixing temperature.
*
* @var float|null
*
* @Assert\Range(min="-273.15", max="1416833")
*
* @ORM\Column(name="mixing_temperature", type="float", nullable=true, options={"comment": "Mixing temperature"})
*/
private $mixingTemperature;
/**
* Casting time point.
*
* @var DateTime|null
*
* @ORM\Column(name="casting_time_point", type="datetime", nullable=true, options={"comment": "Casting time point."})
*/
private $castingTimePoint;
/**
* Starting time point.
*
* @var DateTime|null
*
* @ORM\Column(name="starting_time_point", type="datetime", nullable=true, options={"comment": "Starting time point."})
*/
private $startingTimePoint;
/**
* Array of strings (times in YmdHis format).
*
* @var string[]
*
* @ORM\Column(name="hidden_probes", type="json", nullable=true, options={"default": "{}", "comment": "Hidden probes."})
*/
private $hiddenProbes = [];
/**
* Strength points.
*
* @var array
*
* @ORM\Column(name="strength_points", type="json", nullable=false, options={"comment": "Strength points.", "default": "{}"})
*/
private $strengthPoints = [];
/**
* @var bool
*
* @ORM\Column(name="skip_recipe", type="boolean", nullable=false, options={"comment": "Is channel skiping recipe or standard recipe"})
*/
private $skipRecipe = false;
/**
* @var bool
*
* @ORM\Column(name="is_added_by_user", type="boolean", nullable=false, options={"comment": "Is added by user"})
*/
private $isAddedByUser = false;
/**
* @var bool
*
* @ORM\Column(name="is_deleted", type="boolean", nullable=false, options={"comment": "Is channel deleted"})
*/
private $isDeleted = false;
/**
* @var int
*
* @ORM\Column(name="diagram_group", type="integer", nullable=false, options={"comment": "Temperature diagram group.", "default": 1})
*/
private $diagramGroup = 1;
/**
* ChannelSetting constructor.
*/
public function __construct()
{
$this->channelSettingCoefficients = new ArrayCollection();
$this->strengthPoints = DefaultStrengthPoints::get();
}
/**
* ChannelSetting clone
*/
public function __clone()
{
$this->id = null;
}
/**
* Get measurement setting.
*
* @return MeasurementSetting|null
*/
public function getMeasurementSetting(): ?MeasurementSetting
{
return $this->measurementSetting;
}
/**
* Set measurement setting.
*
* @param MeasurementSetting|null $measurementSetting
*
* @return ChannelSetting
*/
public function setMeasurementSetting(?MeasurementSetting $measurementSetting): ChannelSetting
{
$this->measurementSetting = $measurementSetting;
return $this;
}
/**
* Get channel setting.
*
* @return Channel|null
*/
public function getChannel(): ?Channel
{
return $this->channel;
}
/**
* Channel setting.
*
* @param Channel|null $channel
*
* @return ChannelSetting
*/
public function setChannel($channel): ChannelSetting
{
$this->channel = $channel;
return $this;
}
/**
* Get priority.
*
* @return int|null
*/
public function getPriority(): ?int
{
return $this->priority;
}
/**
* Set priority.
*
* @param int|null $priority
*
* @return ChannelSetting
*/
public function setPriority(?int $priority): ChannelSetting
{
$this->priority = $priority;
return $this;
}
/**
* Get channel name.
*
* @return string|null
*/
public function getName(): ?string
{
return $this->name;
}
/**
* Set channel name.
*
* @param string|null $name
*
* @return ChannelSetting
*/
public function setName(?string $name): ChannelSetting
{
$this->name = $name;
return $this;
}
/**
* Get recipe.
*
* @return Recipe|null
*/
public function getRecipe(): ?Recipe
{
return $this->recipe;
}
/**
* Set recipe.
*
* @param Recipe|null $recipe
*
* @return ChannelSetting
*/
public function setRecipe(?Recipe $recipe): ChannelSetting
{
$this->recipe = $recipe;
return $this;
}
/**
* Get is average weighted.
*
* @return bool
*/
public function getIsAverageWeighted(): bool
{
return $this->isAverageWeighted;
}
/**
* Set is average weighted.
*
* @param bool $isAverageWeighted
*
* @return ChannelSetting
*/
public function setIsAverageWeighted(bool $isAverageWeighted): ChannelSetting
{
$this->isAverageWeighted = $isAverageWeighted;
return $this;
}
/**
* Get number.
* Note: please don't add static values here ie. '?? 1'; it will break channelViewFactory.
*
* @return int|null
*/
public function getNumber(): ?int
{
return $this->number;
}
/**
* Set number.
*
* @param int|null $number
*
* @return ChannelSetting
*/
public function setNumber(?int $number): ChannelSetting
{
$this->number = $number;
return $this;
}
/**
* Get type.
*
* @return string
*/
public function getType(): string
{
return $this->channelSettingCoefficients->count() ? 'calculated' : 'default';
}
/**
* Set type. Todo: it does nothing.
*
* @param string $type
*
* @return ChannelSetting
*/
public function setType(string $type): ChannelSetting
{
return $this;
}
/**
* Get mixing time point.
*
* @return DateTime|null
*/
public function getMixingTimePoint(): ?DateTime
{
return $this->mixingTimePoint;
}
/**
* Set mixing time point.
*
* @param DateTime|null $mixingTimePoint
*
* @return ChannelSetting
*/
public function setMixingTimePoint(?DateTime $mixingTimePoint): ChannelSetting
{
$this->mixingTimePoint = $mixingTimePoint;
return $this;
}
/**
* Set mixing temperature.
*
* @param float|null $mixingTemperature
*
* @return ChannelSetting
*/
public function setMixingTemperature(?float $mixingTemperature): ChannelSetting
{
$this->mixingTemperature = $mixingTemperature;
return $this;
}
/**
* Get mixing temperature.
*
* @return float|null
*/
public function getMixingTemperature(): ?float
{
return $this->mixingTemperature;
}
/**
* Get casting time point.
*
* @return DateTime|null
*/
public function getCastingTimePoint(): ?DateTime
{
return $this->castingTimePoint;
}
/**
* Set casting time point.
*
* @param DateTime|null $castingTimePoint
*
* @return ChannelSetting
*/
public function setCastingTimePoint(?DateTime $castingTimePoint): ChannelSetting
{
$this->castingTimePoint = $castingTimePoint;
return $this;
}
/**
* Get starting time point.
*
* @return DateTime|null
*/
public function getStartingTimePoint(): ?DateTime
{
return $this->startingTimePoint;
}
/**
* Set starting time point.
*
* @param DateTime|null $startingTimePoint
*
* @return ChannelSetting
*/
public function setStartingTimePoint(?DateTime $startingTimePoint): ChannelSetting
{
$this->startingTimePoint = $startingTimePoint;
return $this;
}
/**
* @return ChannelSettingCoefficient[]|ArrayCollection
*/
public function getChannelSettingCoefficients()
{
return $this->channelSettingCoefficients;
}
/**
* Add channel setting coefficient.
*
* @param ChannelSettingCoefficient $channelSettingCoefficient
*
* @return ChannelSetting
*/
public function addChannelSettingCoefficient(ChannelSettingCoefficient $channelSettingCoefficient): ChannelSetting
{
if (!$this->channelSettingCoefficients->contains($channelSettingCoefficient)) {
$this->channelSettingCoefficients->add($channelSettingCoefficient->setParentSetting($this));
}
return $this;
}
/**
* Remove channel setting coefficient.
*
* @param ChannelSettingCoefficient $channelSettingCoefficient
*
* @return ChannelSetting
*/
public function removeChannelSettingCoefficient(ChannelSettingCoefficient $channelSettingCoefficient): ChannelSetting
{
if ($this->channelSettingCoefficients->contains($channelSettingCoefficient)) {
$this->channelSettingCoefficients->removeElement($channelSettingCoefficient);
}
return $this;
}
/**
* To string.
*
* @return string|null
*/
public function __toString(): string
{
if (!$this->channel || null === $this->channel->getMeasurement() || null === $this->getChannel()) {
return $this->getFullName(false, false);
}
return $this->getFullName() ?? '';
}
/**
* Get full name.
*
* @param bool $withMeasurement
* @param bool $withTraceability
*
* @return string
*/
public function getFullName(bool $withMeasurement = true, bool $withTraceability = false): string
{
$traceability = $withTraceability ? $this->getTraceabilityName() : '';
$defaultMeasurement = null;
if (!$withTraceability && $withMeasurement) {
$defaultMeasurement = $this->measurementSetting->getMeasurement();
}
$name = $this->getName();
if (!$name) {
if ('default' !== $this->getType()) {
$name = '{{calculated-channel}}';
} elseif ($this->isAddedByUser()) {
$name = '{{added-channel}}';
if ($withMeasurement) {
$name .= ': '.$this->channel->getMeasurement()->getNameWithFallback()
.' ({{Sequence}} '.($this->getChannel()->getNumber()).')';
}
return $name;
//return '{{added-channel}}: '.$this->channel->getMeasurement()->getName(true).' ({{Sequence}} '.($this->getChannel()->getNumber()+1).')';
} else {
$name = '{{Channel}} '.$this->getNumber();
}
}
$name = ('default' !== $this->getType() && true === $withTraceability) ? '' : $name;
$measurement = ($withMeasurement && $this->channel) ? $this->channel->getMeasurement() : $defaultMeasurement;
if ($measurement) {
$name .= ' '.$measurement->getName() ?? '{{Measurement}} '.$this->getId();
}
return ($this->name ?? $name).$traceability;
}
/**
* Is duplicate.
*
* @Assert\IsFalse(message="This channel already exists.")
*
* @return bool
*/
public function isDuplicate()
{
if (!$this->channel) {
return false;
}
foreach ($this->measurementSetting->getChannelSettings() as $channelSetting) {
if ($channelSetting->getId() !== $this->id && $channelSetting->getChannel()
&& $channelSetting->getChannel()->getId() === $this->channel->getId()
&& $channelSetting->getRecipe() == $this->getRecipe()) {
return true;
}
}
return false;
}
/**
* @Assert\IsTrue(message="Please select channel.")
*
* @return bool
*/
public function hasChannelSettingCoefficients(): bool
{
return $this->channel || $this->channelSettingCoefficients->count() > 0;
}
/**
* Get measurement started at.
*
* @return DateTime
*/
public function getMeasurementStartedAt(): ?DateTime
{
return $this->getMeasurement() ? $this->getMeasurement()->getStartedAt() : null;
}
/**
* Get measurement.
*
* @return Measurement|null
*/
public function getMeasurement(): ?Measurement
{
return $this->channel ? $this->channel->getMeasurement()
: ($this->getMeasurementSetting() ? $this->getMeasurementSetting()->getMeasurement() : null);
}
/**
* Get is stopped.
*
* @param int|null $depth
*
* @return bool
*/
public function getIsStopped($depth = 0): bool
{
if (++$depth > 5) {
return false;
}
if ($this->channel && $this->channel->getMeasurement() && $this->channel->getMeasurement()->getIsFinished()) {
return true;
}
$isStopped = false;
foreach ($this->channelSettingCoefficients as $relation) {
if (!$relation->getChildrenSetting()->getIsStopped($depth)) {
return false;
}
$isStopped = true;
}
return $isStopped;
}
/**
* Hide probe.
*
* @param DateTime $probeTime
*
* @return ChannelSetting
*/
public function hideProbe(DateTime $probeTime)
{
if (!in_array($probeTime->format('YmdHis'), $this->hiddenProbes)) {
$this->hiddenProbes[] = $probeTime->format('YmdHis');
}
return $this;
}
/**
* Show probe.
*
* @param DateTime $probeTime
*
* @return ChannelSetting
*/
public function showProbe(DateTime $probeTime): ChannelSetting
{
if (false !== ($key = array_search($probeTime->format('YmdHis'), $this->hiddenProbes))) {
unset($this->hiddenProbes[$key]);
}
return $this;
}
/**
* Get is probe hidden.
*
* @param DateTime $probeTime
*
* @return bool
*/
public function getIsProbeHidden(DateTime $probeTime): bool
{
return in_array($probeTime->format('YmdHis'), $this->hiddenProbes);
}
/**
* @return $this
*/
public function clearHiddenProbes()
{
$this->hiddenProbes = [];
return $this;
}
/**
* Get channel short name.
*
* @return string
*/
public function getChannelShortName(): string
{
return $this->getFullName(false);
}
/**
* Get traceability name.
*
* @return string
*/
public function getTraceabilityName()
{
if (!$this->channel || !$this->channel->getId()) {
$name = null;
$sequencesArray = [];
foreach ($this->channelSettingCoefficients as $coefficient) {
$name = $coefficient->getChildrenSetting()->getMeasurement()->getNameWithFallback();
$sequencesArray[] = $name.' ('.$coefficient->getCoefficient().' * '.$coefficient->getName().')';
}
$sequence = implode(' + ', $sequencesArray);
return '{{Calculated from}}: '.($this->isAverageWeighted
? '('.$sequence.') / '.$this->channelSettingCoefficients->count() : $sequence);
}
$device = $this->getMeasurement()->getDevice();
$deviceId = $device->getSerialNumber();
$calibratedAt = $device->getCalibratedAt() ? $device->getCalibratedAt()->format('Y-m-d') : '-';
return ' {{Device}} '.$deviceId.', '
.'{{Sequence}}'.' '.($this->channel->getNumber()).', {{Calibrated at}}: '.$calibratedAt;
}
/**
* Get traceability tree.
*
* @return array
*/
public function getTraceabilityTree()
{
$nodes = $this->getTraceabilityNodes();
$name = (!$this->getNumber() && $this->channel)
? $this->channel->getSequenceName()
: (($this->channel) ? $this->channel->getSequenceName() : (string) $this);
/* Traceability channel name changed: task #261 */
$name = str_replace('Sequence', 'traceability_channel', $name);
$nodes = [
'text' => str_replace(
['{{', '}}'],
'--',
"<strong>".(strlen($name) > 105 ? substr($name, 0, 105)."..." : $name).",
</strong>".$this->getTraceabilityName()
),
] + (count($nodes) ? ['nodes' => $nodes] : []);
return $nodes;
}
/**
* Get traceability nodes.
*
* @return array
*/
public function getTraceabilityNodes()
{
$nodes = [];
foreach ($this->channelSettingCoefficients as $coefficient) {
$nodes[] = $coefficient->getChildrenSetting()->getTraceabilityTree();
}
return $nodes;
}
/**
* Get strength points.
*
* @return array
*/
public function getStrengthPoints(): array
{
return $this->strengthPoints;
}
/**
* Set strength points.
*
* @param array $strengthPoints
*
* @return ChannelSetting
*/
public function setStrengthPoints(array $strengthPoints): ChannelSetting
{
$this->strengthPoints = $strengthPoints;
return $this;
}
/**
* Get active strength points.
*
* @return array
*/
public function getActiveStrengthPoints()
{
return array_keys(array_filter($this->strengthPoints, function ($element) {
return true === $element;
}));
}
/**
* @return bool
*/
public function isDeleted(): bool
{
return $this->isDeleted;
}
/**
* @param bool $isDeleted
*
* @return self
*/
public function setIsDeleted(bool $isDeleted): ChannelSetting
{
$this->isDeleted = $isDeleted;
return $this;
}
/**
* @return bool
*/
public function isAddedByUser(): bool
{
return $this->isAddedByUser;
}
/**
* @param bool $isAddedByUser
*
* @return self
*/
public function setIsAddedByUser(bool $isAddedByUser): ChannelSetting
{
$this->isAddedByUser = $isAddedByUser;
return $this;
}
public function getDiagramGroup(): int
{
return $this->diagramGroup ?: 1;
}
public function setDiagramGroup(int $diagramGroup): ChannelSetting
{
$this->diagramGroup = max(1, min(2, $diagramGroup));
return $this;
}
/**
* @return bool
*/
public function isSkipRecipe(): bool
{
return $this->skipRecipe;
}
/**
* @param bool $skipRecipe
*
* @return self
*/
public function setIsSkipRecipe(bool $skipRecipe): ChannelSetting
{
$this->skipRecipe = $skipRecipe;
return $this;
}
}