Skip to content

Commit

Permalink
Added more to SimaticML API. Implemented Custom Variable to alarm gen…
Browse files Browse the repository at this point in the history
…eration.

SImaticML implemented branches to SimaticPart to allow to add branch (Before there was only OR). Also added the MovePart, to allow to use the block MOVE.

TiaUtilities added a new custom variables to alarm generation to have a costant that is moved inside a defined variable for various uses. (Implemented all the fields, configuration and columns).
  • Loading branch information
Parozzz committed Nov 6, 2024
1 parent 5929e24 commit 45577f8
Show file tree
Hide file tree
Showing 20 changed files with 696 additions and 39 deletions.
9 changes: 8 additions & 1 deletion SimaticML/API/SimaticLADSegment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,17 @@ private void ParsePart(CompileUnit compileUnit, SegmentPart previousPart)
}
else
{
compileUnit.CreateWire()
var wire = compileUnit.CreateWire()
.CreateNameCon(previousPart.Part, previousPart.OutputConName)
.CreateNameCon(part.Part, part.InputConName);

foreach (var branchSimaticPart in previousPart.SimaticPart.Branches)
{
var branchPart = this.ComputePart(compileUnit, branchSimaticPart) ?? throw new ArgumentNullException(nameof(branchSimaticPart));
wire.CreateNameCon(branchPart.Part, branchPart.InputConName);
ParsePart(compileUnit, branchPart);
}

ParsePart(compileUnit, part);
}
}
Expand Down
59 changes: 49 additions & 10 deletions SimaticML/API/SimaticPart.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,24 @@
using SimaticML.Blocks.FlagNet;
using SimaticML.Blocks.FlagNet.nPart;
using SimaticML.Enums;
using System.Collections.Generic;

namespace SimaticML.API
{
public abstract class SimaticPart
public abstract class SimaticPart(PartType partType)
{
//all LADDER blocks have input / output connections ("in" and "out" for contact, "en" and "eno" for blocks).
//Some, like a call for an FC, have more named connections.
public abstract string InputConName { get; }
public abstract string OutputConName { get; }

public PartType PartType { get; init; }
public SimaticMultilingualText Comment { get; init; }
public PartType PartType { get; init; } = partType;
public SimaticMultilingualText Comment { get; init; } = new();

//A part can only have 1 previous part but multiple next
//(The OR is considered one single part when connection something AFTER. Before the OR, the part will have multiple connections)
public SimaticPart? Next { get; set; }

public SimaticPart(PartType partType)
{
this.PartType = partType;
this.Comment = new();
}
public List<SimaticPart> Branches { get; init; } = [];

public virtual bool IsCloser() => false;

Expand All @@ -49,14 +45,19 @@ public virtual SimaticPart AND(SimaticPart nextPart)
return this;
}

public virtual SimaticPart Branch(SimaticPart branch)
{
this.Branches.Add(branch);
return branch;
}

public SimaticPart FindLast() => this.Next == null ? this : this.Next.FindLast();

public virtual SimaticPart OR(SimaticPart nextPart)
{
var orPart = new OrPart();
orPart.PartList.Add(this);
orPart.PartList.Add(nextPart);

orPart.PreviousPartAndConnections.AddRange(orPart.PartList);

return orPart;
Expand Down Expand Up @@ -177,6 +178,44 @@ public class ResetCoilPart() : OperandPart(PartType.RESET_COIL)
public override bool IsCloser() => true;
}

public class MovePart() : SimaticPart(PartType.MOVE)
{
public override string InputConName => "en";
public override string OutputConName => "eno";

public SimaticVariable? IN { get; set; }
public List<SimaticVariable> OUT { get; init; } = [];

public override Part GetPart(CompileUnit compileUnit)
{
var part = base.GetPart(compileUnit);

part.TemplateValueName = "Card";
part.TemplateValueType = "Cardinality";
part.TemplateValue = OUT.Count.ToString();

if(IN != default)
{
compileUnit.CreateWire().CreateIdentCon(IN, part, "in");
}

if (OUT.Count > 0)
{
for (int x = 0; x < OUT.Count; x++)
{
var outVariable = OUT[x];
compileUnit.CreateWire().CreateIdentCon(outVariable, part, "out" + (x + 1));
}
}

return part;
}

public override bool IsCloser() => true;

public override string ToString() => $"{base.ToString()}, IN=[{IN}], OUT=[{string.Join(" ", OUT)}]";
}

public class TimerPart(PartType partType) : SimaticPart(partType)
{
public override string InputConName => "IN";
Expand Down
1 change: 1 addition & 0 deletions SimaticML/Blocks/FlagNet/nPart/Part.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public enum PartType
[SimaticEnum("Not")] NOT,
[SimaticEnum("TON")] TON,
[SimaticEnum("TOF")] TOF,
[SimaticEnum("Move")] MOVE,
[SimaticEnum("O")] OR,
UNKNOWN = 0 //Default value. Do not implement SimaticEnum so an exception is thrown!
}
Expand Down
132 changes: 132 additions & 0 deletions TiaUtilities/DebugRendered.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
using Svg;
using System;
using System.Collections.Generic;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TiaUtilities
{
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
class DebugRenderer : ISvgRenderer
{
private readonly Stack<ISvgBoundable> _boundables = new Stack<ISvgBoundable>();

private Region _clip = new Region();
private Matrix _transform = new Matrix();

public void SetBoundable(ISvgBoundable boundable)
{
_boundables.Push(boundable);
}
public ISvgBoundable GetBoundable()
{
return _boundables.Peek();
}
public ISvgBoundable PopBoundable()
{
return _boundables.Pop();
}

public float DpiY
{
get { return 96; }
}

public void DrawImage(Image image, RectangleF destRect, RectangleF srcRect, GraphicsUnit graphicsUnit)
{
}

public void DrawImage(Image image, RectangleF destRect, RectangleF srcRect, GraphicsUnit graphicsUnit, float opacity)
{
}

public void DrawImageUnscaled(Image image, Point location)
{
}

public void DrawPath(Pen pen, GraphicsPath path)
{
var newPath = (GraphicsPath)path.Clone();
newPath.Transform(_transform);
}

public void FillPath(Brush brush, GraphicsPath path)
{
var newPath = (GraphicsPath)path.Clone();
newPath.Transform(_transform);
}

public Region GetClip()
{
return _clip;
}

public void RotateTransform(float fAngle, MatrixOrder order = MatrixOrder.Append)
{
_transform.Rotate(fAngle, order);
}

public void ScaleTransform(float sx, float sy, MatrixOrder order = MatrixOrder.Append)
{
_transform.Scale(sx, sy, order);
}

public void SetClip(Region region, CombineMode combineMode = CombineMode.Replace)
{
switch (combineMode)
{
case CombineMode.Intersect:
_clip.Intersect(region);
break;
case CombineMode.Complement:
_clip.Complement(region);
break;
case CombineMode.Exclude:
_clip.Exclude(region);
break;
case CombineMode.Union:
_clip.Union(region);
break;
case CombineMode.Xor:
_clip.Xor(region);
break;
default:
if (_clip != null)
_clip.Dispose();
_clip = region;
break;
}
}
public void TranslateTransform(float dx, float dy, MatrixOrder order = MatrixOrder.Append)
{
_transform.Translate(dx, dy, order);
}

public SmoothingMode SmoothingMode
{
get { return SmoothingMode.Default; }
set { /* Do Nothing */ }
}

public Matrix Transform
{
get { return _transform?.Clone(); }
set
{
if (_transform != null)
_transform.Dispose();
_transform = value?.Clone();
}
}

public void Dispose()
{
if (_clip != null)
_clip.Dispose();
if (_transform != null)
_transform.Dispose();
}
}
}
13 changes: 10 additions & 3 deletions TiaUtilities/Generation/Alarms/AlarmData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ public class AlarmData : IGridData
//THESE IS THE ORDER IN WHICH THEY APPEAR!
public static readonly GridDataColumn ENABLE;
public static readonly GridDataColumn ALARM_VARIABLE;
public static readonly GridDataColumn CUSTOM_VARIABLE_ADDRESS;
public static readonly GridDataColumn CUSTOM_VARIABLE_VALUE;
public static readonly GridDataColumn COIL1_ADDRESS;
public static readonly GridDataColumn COIL1_TYPE;
public static readonly GridDataColumn COIL2_ADDRESS;
Expand All @@ -26,7 +28,9 @@ static AlarmData()
{
var type = typeof(AlarmData);
ENABLE = GridDataColumn.GetFromReflection(type, COLUMN_COUNT++, nameof(AlarmData.Enable));
ALARM_VARIABLE = GridDataColumn.GetFromReflection(type, COLUMN_COUNT++, nameof(AlarmData.AlarmVariable), "alarmVariable");
ALARM_VARIABLE = GridDataColumn.GetFromReflection(type, COLUMN_COUNT++, nameof(AlarmData.AlarmVariable));
CUSTOM_VARIABLE_ADDRESS = GridDataColumn.GetFromReflection(type, COLUMN_COUNT++, nameof(AlarmData.CustomVariableAddress), "customVarAddress");
CUSTOM_VARIABLE_VALUE = GridDataColumn.GetFromReflection(type, COLUMN_COUNT++, nameof(AlarmData.CustomVariableValue), "customVarValue");
COIL1_ADDRESS = GridDataColumn.GetFromReflection(type, COLUMN_COUNT++, nameof(AlarmData.Coil1Address), "coil1Address");
COIL1_TYPE = GridDataColumn.GetFromReflection(type, COLUMN_COUNT++, nameof(AlarmData.Coil1Type), "coil1Type");
COIL2_ADDRESS = GridDataColumn.GetFromReflection(type, COLUMN_COUNT++, nameof(AlarmData.Coil2Address), "coil2Address");
Expand All @@ -43,6 +47,8 @@ static AlarmData()

[JsonProperty][Locale(nameof(Locale.ALARM_DATA_ENABLE))] public bool Enable { get; set; }
[JsonProperty][Locale(nameof(Locale.ALARM_DATA_ALM_VARIABLE))] public string? AlarmVariable { get; set; }
[JsonProperty][Locale(nameof(Locale.ALARM_DATA_CUSTOM_VAR_ADDR))] public string? CustomVariableAddress { get; set; }
[JsonProperty][Locale(nameof(Locale.ALARM_DATA_CUSTOM_VAR_VALUE))] public string? CustomVariableValue { get; set; }
[JsonProperty][Locale(nameof(Locale.ALARM_DATA_COIL1_ADDRESS))] public string? Coil1Address { get; set; }
[JsonProperty][Locale(nameof(Locale.ALARM_DATA_COIL1_TYPE))] public string? Coil1Type { get; set; }
[JsonProperty][Locale(nameof(Locale.ALARM_DATA_COIL2_ADDRESS))] public string? Coil2Address { get; set; }
Expand Down Expand Up @@ -83,13 +89,14 @@ public static bool IsAddressValid(string? str)
public void Clear()
{
this.Enable = false;
this.AlarmVariable = this.Coil1Address = this.Coil1Type = this.Coil2Address = this.Coil2Type =
this.TimerAddress = this.TimerType = this.TimerValue = this.Description = null;
this.AlarmVariable = this.CustomVariableAddress = this.CustomVariableValue = this.Coil1Address = this.Coil1Type
= this.Coil2Address = this.Coil2Type = this.TimerAddress = this.TimerType = this.TimerValue = this.Description = null;
}

public bool IsEmpty()
{
return string.IsNullOrEmpty(this.AlarmVariable) &&
string.IsNullOrEmpty(this.CustomVariableAddress) && string.IsNullOrEmpty(this.CustomVariableValue) &&
string.IsNullOrEmpty(this.Coil1Address) && string.IsNullOrEmpty(this.Coil1Type) &&
string.IsNullOrEmpty(this.Coil2Address) && string.IsNullOrEmpty(this.Coil2Type) &&
string.IsNullOrEmpty(this.TimerAddress) && string.IsNullOrEmpty(this.TimerType) && string.IsNullOrEmpty(this.TimerValue) &&
Expand Down
4 changes: 4 additions & 0 deletions TiaUtilities/Generation/Alarms/AlarmTabConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public class AlarmTabConfiguration : ObservableConfiguration
[JsonProperty] public string EmptyAlarmTimerType { get => this.GetAs<string>(); set => this.Set(value); }
[JsonProperty] public string EmptyAlarmTimerValue { get => this.GetAs<string>(); set => this.Set(value); }

[JsonProperty] public string DefaultCustomVarAddress { get => this.GetAs<string>(); set => this.Set(value); }
[JsonProperty] public string DefaultCustomVarValue { get => this.GetAs<string>(); set => this.Set(value); }
[JsonProperty] public string DefaultCoil1Address { get => this.GetAs<string>(); set => this.Set(value); }
[JsonProperty] public AlarmCoilType DefaultCoil1Type { get => this.GetAs<AlarmCoilType>(); set => this.Set(value); }
[JsonProperty] public string DefaultCoil2Address { get => this.GetAs<string>(); set => this.Set(value); }
Expand Down Expand Up @@ -52,6 +54,8 @@ public AlarmTabConfiguration()
this.EmptyAlarmTimerType = "TON";
this.EmptyAlarmTimerValue = "T#0s";

this.DefaultCustomVarAddress = $"Alm.Level[{GenPlaceholders.Alarms.ALARM_NUM}]";
this.DefaultCustomVarValue = "0";
this.DefaultCoil1Address = $"Alm.Act.Alm{GenPlaceholders.Alarms.ALARM_NUM}";
this.DefaultCoil1Type = AlarmCoilType.COIL;
this.DefaultCoil2Address = $"Alm.Mem.Alm{GenPlaceholders.Alarms.ALARM_NUM}";
Expand Down
31 changes: 30 additions & 1 deletion TiaUtilities/Generation/Alarms/AlarmXmlGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,8 @@ private static AlarmData ReplaceAlarmDataWithDefaultAndPrefix(AlarmTabConfigurat
return new AlarmData()
{
AlarmVariable = tabConfig.AlarmAddressPrefix + alarmData.AlarmVariable,
CustomVariableAddress = string.IsNullOrEmpty(alarmData.CustomVariableAddress) ? tabConfig.DefaultCustomVarAddress : alarmData.CustomVariableAddress,
CustomVariableValue = string.IsNullOrEmpty(alarmData.CustomVariableValue) ? tabConfig.DefaultCustomVarValue : alarmData.CustomVariableValue,
Coil1Address = string.IsNullOrEmpty(alarmData.Coil1Address) ? tabConfig.DefaultCoil1Address : (tabConfig.Coil1AddressPrefix + alarmData.Coil1Address),
Coil1Type = coil1Type.ToString(),
Coil2Address = string.IsNullOrEmpty(alarmData.Coil2Address) ? tabConfig.DefaultCoil2Address : (tabConfig.Coil2AddressPrefix + alarmData.Coil2Address),
Expand All @@ -304,7 +306,7 @@ private static void FillAlarmSegment(AlarmTabConfiguration tabConfig, SimaticLAD
}

var parsedContactAddress = placeholders.Parse(alarmData.AlarmVariable);
var contact = new ContactPart()
SimaticPart contact = new ContactPart()
{
Operand = parsedContactAddress.ToLower() switch
{
Expand All @@ -316,6 +318,33 @@ private static void FillAlarmSegment(AlarmTabConfiguration tabConfig, SimaticLAD
}
};

var parsedCustomVarAddress = placeholders.Parse(alarmData.CustomVariableAddress);
var parsedCustomVarValue = placeholders.Parse(alarmData.CustomVariableValue);
if (!string.IsNullOrEmpty(parsedCustomVarAddress) && AlarmData.IsAddressValid(parsedCustomVarAddress)
&& !string.IsNullOrEmpty(parsedCustomVarValue) && AlarmData.IsAddressValid(parsedCustomVarValue))
{

SimaticVariable inVar;
if (parsedCustomVarValue.Contains('.') && float.TryParse(parsedCustomVarValue, out _))
{
inVar = new SimaticLiteralConstant(SimaticDataType.REAL, parsedCustomVarValue);
}
else if (long.TryParse(parsedCustomVarValue, out _))
{
inVar = new SimaticLiteralConstant(SimaticDataType.DINT, parsedCustomVarValue);
}
else
{
inVar = new SimaticTypedConstant(parsedCustomVarValue);
}

contact.Branch(new MovePart()
{
IN = inVar,
OUT = { new SimaticGlobalVariable(parsedCustomVarAddress) }
});
}

TimerPart? timer = null;
if (!string.IsNullOrEmpty(alarmData.TimerAddress)
&& !string.IsNullOrEmpty(alarmData.TimerType)
Expand Down
10 changes: 10 additions & 0 deletions TiaUtilities/Generation/Alarms/Module/Tab/AlarmGenTab.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ public void Init()

alarmGridHandler.AddCheckBoxColumn(AlarmData.ENABLE, 40);
alarmGridHandler.AddTextBoxColumn(AlarmData.ALARM_VARIABLE, 200);
alarmGridHandler.AddTextBoxColumn(AlarmData.CUSTOM_VARIABLE_ADDRESS, 145);
alarmGridHandler.AddTextBoxColumn(AlarmData.CUSTOM_VARIABLE_VALUE, 50);
alarmGridHandler.AddTextBoxColumn(AlarmData.COIL1_ADDRESS, 145);
alarmGridHandler.AddComboBoxColumn(AlarmData.COIL1_TYPE, 55, ALARM_COIL_TYPE_ITEMS);
alarmGridHandler.AddTextBoxColumn(AlarmData.COIL2_ADDRESS, 145);
Expand Down Expand Up @@ -95,6 +97,14 @@ public void Init()
{
return new() { Prefix = TabConfig.AlarmAddressPrefix, Value = alarmData.AlarmVariable };
}
else if(column == AlarmData.CUSTOM_VARIABLE_ADDRESS)
{
return new() { DefaultValue = TabConfig.DefaultCustomVarAddress, Value = alarmData.CustomVariableAddress };
}
else if (column == AlarmData.CUSTOM_VARIABLE_VALUE)
{
return new() { DefaultValue = TabConfig.DefaultCustomVarValue, Value = alarmData.CustomVariableValue };
}
else if (column == AlarmData.COIL1_ADDRESS)
{
return new() { Prefix = TabConfig.Coil1AddressPrefix, DefaultValue = TabConfig.DefaultCoil1Address, Value = alarmData.Coil1Address };
Expand Down
Loading

0 comments on commit 45577f8

Please sign in to comment.