From 1f33ebb8d38ebbf7ca1470987fcdaa0f032144d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Wei=C3=9F?= <72068105+Sandoun@users.noreply.github.com> Date: Wed, 26 Jul 2023 22:47:35 +0200 Subject: [PATCH] F func params parser --- Examples.ProgramReadWrite/Program.cs | 2 +- MewtocolNet/MewtocolInterfaceRequests.cs | 6 +- .../ProgramParsing/PlcBinaryProgram.cs | 231 +++++++++++++++--- 3 files changed, 206 insertions(+), 33 deletions(-) diff --git a/Examples.ProgramReadWrite/Program.cs b/Examples.ProgramReadWrite/Program.cs index 2cf4381..66f5bbe 100644 --- a/Examples.ProgramReadWrite/Program.cs +++ b/Examples.ProgramReadWrite/Program.cs @@ -13,7 +13,7 @@ internal class Program { Logger.LogLevel = LogLevel.Error; - using (var plc = Mewtocol.Ethernet("192.168.115.210").Build()) { + using (var plc = Mewtocol.Ethernet("192.168.178.55").Build()) { await plc.ConnectAsync(); var prog = await plc.ReadProgramAsync(); diff --git a/MewtocolNet/MewtocolInterfaceRequests.cs b/MewtocolNet/MewtocolInterfaceRequests.cs index 5c15481..8630dd8 100644 --- a/MewtocolNet/MewtocolInterfaceRequests.cs +++ b/MewtocolNet/MewtocolInterfaceRequests.cs @@ -116,6 +116,10 @@ namespace MewtocolNet { } + #endregion + + #region Program Read / Write + public async Task ReadProgramAsync () { var steps = new List(); @@ -136,7 +140,7 @@ namespace MewtocolNet { if (res.Success) { var bytes = res.Response.ParseDTRawStringAsBytes(); - var foundEndPattern = bytes.SearchBytePattern(new byte[] { 0xFA, 0xF8, 0xFF, 0xFF }); + var foundEndPattern = bytes.SearchBytePattern(new byte[] { 0xF8, 0xFF, 0xFF }); for (int j = 0; j < bytes.Length; j += 2) { var split = bytes.Skip(j).Take(2).ToArray(); diff --git a/MewtocolNet/ProgramParsing/PlcBinaryProgram.cs b/MewtocolNet/ProgramParsing/PlcBinaryProgram.cs index 5d9c7ed..7ec2ba6 100644 --- a/MewtocolNet/ProgramParsing/PlcBinaryProgram.cs +++ b/MewtocolNet/ProgramParsing/PlcBinaryProgram.cs @@ -5,20 +5,27 @@ using System.Collections.Generic; using System.Linq; using System.Globalization; using MewtocolNet.AutoGeneratedData; +using static System.Collections.Specialized.BitVector32; namespace MewtocolNet.ProgramParsing { + public enum ProgramSection { + + CheckSumTest = 0, //check if the program was changed and overwrite self reliant registers with 0x00 if so + InitGlobalVars = 1, //setup all global vars that are not 0x00 + Program = 2, + Subroutine = 3, + + } + public class PlcBinaryProgram { static readonly Dictionary stepCommands = new Dictionary { - { 0x00FF, "SUB 0" }, - { 0xB0FF, "DF" }, - { 0xFAF8, "ED" }, - { 0xFDF8, "RET" }, //return - { 0xF6F8, "CALL" }, - { 0xEEF8, "SET" }, + //routine entry flank conditions + { 0xB0FF, "DF (Rising flank condition)" }, + { 0xEEF8, "SET" }, //has parameter needs custom analyzer { 0x21CC, "OT R501" }, //{ 0x21AC, "ST R501" }, //{ 0xF7FF, "ST R9010" }, @@ -108,28 +115,45 @@ namespace MewtocolNet.ProgramParsing { public void AnalyzeProgram () { + int sectionCounter = -1, programCounter = 0, subRoutineCounter = 0; + var currentSection = ProgramSection.CheckSumTest; + for (int i = 0; i < rawSteps.Count; i++) { + //step data var step = rawSteps[i]; - - var stepAscii = Encoding.ASCII.GetString(step); var stepBytesString = string.Join(" ", step.Select(x => x.ToString("X2"))); - Console.Write($"{i,3} => {stepBytesString} "); + //step debug writing + string stepHeader = null; + var stepString = new StringBuilder(); + stepString.Append($"{i,3} => {stepBytesString} "); var stepKey = BitConverter.ToUInt16(step.Reverse().ToArray(), 0); byte[] nextStep = null; - if(i + 1 < rawSteps.Count - 1) - nextStep = rawSteps[i + 1]; + if(i + 1 < rawSteps.Count - 1) nextStep = rawSteps[i + 1]; if (stepCommands.ContainsKey(stepKey)) { - Console.Write($"{stepCommands[stepKey]}"); + stepString.Append($"{stepCommands[stepKey]}"); + + } else if (nextStep != null && step[0] != 0xF7 && step[1] == 0xFF && nextStep[1] != 0xA9) { + + //SUB (Subroutine start) step + + var callCode = step[0]; + stepString.Append($"SUB {callCode}"); + + sectionCounter++; + subRoutineCounter++; + + stepHeader = $"\n{ProgramSection.Subroutine} | {subRoutineCounter}\n"; + currentSection = ProgramSection.Subroutine; } else if (nextStep != null && step[0] == 0xF7 && step[1] == 0xFF && nextStep[1] == 0xA9) { - //ST step + //ST (Start normal program) step var area = nextStep[0].ToString("X2").Substring(0, 1); var specialArea = nextStep[0].ToString("X2").Substring(1, 1); @@ -137,7 +161,31 @@ namespace MewtocolNet.ProgramParsing { var stepID = 896 + int.Parse(area, NumberStyles.HexNumber); var stCondition = $"R{stepID}{specialArea}"; - Console.Write($"ST {GetSysRegisterName(stCondition)}"); + if(subRoutineCounter == 0) sectionCounter++; + + if (sectionCounter > 1) { + + if(subRoutineCounter == 0) { + + programCounter++; + + stepHeader = $"\n{ProgramSection.Program} | {programCounter}\n"; + currentSection = ProgramSection.Program; + + } + + } else if (sectionCounter > 0) { + + stepHeader = $"\n{ProgramSection.InitGlobalVars}\n"; + currentSection = ProgramSection.InitGlobalVars; + + } else { + + stepHeader = $"\n{ProgramSection.CheckSumTest}\n"; + + } + + stepString.Append($"ST {GetSysRegisterName(stCondition)}"); } else if (nextStep != null && step[0] == 0xF7 && step[1] == 0xFF && nextStep[1] == 0x49) { @@ -149,64 +197,185 @@ namespace MewtocolNet.ProgramParsing { var stepID = 896 + int.Parse(area, NumberStyles.HexNumber); var stCondition = $"R{stepID}{specialArea}"; - Console.Write($"AN/ {GetSysRegisterName(stCondition)}"); + stepString.Append($"AN/ {GetSysRegisterName(stCondition)}"); + + } else if (nextStep != null && step[0] == 0xF6 && step[1] == 0xF8 && nextStep[1] == 0xFA) { + + //CALL step + + var callCode = nextStep[0]; + stepString.Append($"CALL {callCode}"); + + } else if (step[1] == 0xCC) { + + //OT step + + var callCode = step[0]; + stepString.Append($"OT {callCode}"); + + } else if (step[0] == 0xFA && step[1] == 0xF8) { + + //ED step + stepString.Append($"ED (End of main prog loop)"); + + } else if (step[0] == 0xFD && step[1] == 0xF8) { + + //RET step + stepString.Append($"RET (End of subroutine {subRoutineCounter})"); } else if (step[1] == 0xF8) { + //standard lib F function + int functionID = 0; + int paramsStartIndex = i + 1; if (step[0] == 0xFF) { //custom function that goes over FF, the F instruction number is calced by //the next step first byte plus 190 functionID = nextStep[0] + 190; + paramsStartIndex = i + 2; } else { functionID = step[0]; } - string funcName = GetFunctionName($"F{functionID}"); - Console.Write(funcName); + string funcName = GetFunction($"F{functionID}", out var foundFunc); + stepString.Append(funcName); - //get the params and outs of the function + if(foundFunc != null) { + ParseFuncParams(paramsStartIndex, foundFunc, stepString); + + } } - //if (stepBytesString.StartsWith("F")) { - // Console.Write(" STEP COMMAND"); - //} - - Console.WriteLine(); + if (stepHeader != null) Console.WriteLine(stepHeader); + Console.WriteLine(stepString.ToString()); } + } - Console.WriteLine(); + private void ParseFuncParams(int stepIndex, FPFunction func, StringBuilder sb) { + + //get the params and outs of the function + var allowedParamSuffixes = new byte[] { + 0xFA, + 0xFB, + 0xFD, + }; + + //count words + + int paramsWordCount = 0; + + while (true) { + + if (stepIndex + 1 >= rawSteps.Count) break; + + var paramStepL = rawSteps[stepIndex]; + var paramStepH = rawSteps[stepIndex + 1]; + + if (!allowedParamSuffixes.Contains(paramStepL[1])) break; + + stepIndex += 2; + paramsWordCount++; + + } + + sb.Append($"Words: {paramsWordCount}"); + + //if (func.ParametersIn != null) { + + // for (int i = 0; i < func.ParametersIn.Count; i++) { + + // sb.Append($"PI_{i}:"); + + + + // } + + //} + + int currentParam = 0; + + while (true) { + + if (stepIndex + 1 >= rawSteps.Count) break; + + var paramStepL = rawSteps[stepIndex]; + var paramStepH = rawSteps[stepIndex + 1]; + + //as DT address + if (paramStepL[1] == 0xFA && paramStepH[1] == 0xFB) { + + var address = BitConverter.ToUInt16(new byte[] { paramStepL[0], paramStepH[0] }, 0); + + sb.Append($"DT{address}, "); + + currentParam++; + stepIndex += 2; + continue; + + } + + //16 bit WORD/INT/UINT + if (paramStepL[1] == 0xFA && paramStepH[1] == 0xFC) { + + sb.Append($"K{paramStepH[0]:X2}{paramStepL[0]:X2}, "); + + } + + //32 bit REAL + if (paramStepL[1] == 0xFD && paramStepH[1] == 0xFB) { + + sb.Append($"F{paramStepH[0]:X2}{paramStepL[0]:X2}, "); + + //skipFrames = 4; + + } + + //end / return? + if (paramStepL[1] == 0xFD && paramStepH[1] == 0xFD) { + + } + + if (!allowedParamSuffixes.Contains(paramStepL[1])) break; + + stepIndex += 2; + + } } - private string GetFunctionName (string funcName) { + private string GetFunction (string funcName, out FPFunction foundFunc) { + + foundFunc = null; var found = FPFunction.functions.FirstOrDefault(x => x.Key.StartsWith(funcName)); if (!found.Equals(default(KeyValuePair))) { + foundFunc = found.Value; + var sb = new StringBuilder(); var splt = found.Key.Split('_'); sb.Append($"{splt[0]} (*{splt[1]}*)"); - if(found.Value.ParametersIn.Count > 0) { + //if(found.Value.ParametersIn.Count > 0) { - sb.Append($" IN: {found.Value.ParametersIn.Count}"); + // sb.Append($" IN: {found.Value.ParametersIn.Count}"); - } + //} - if (found.Value.ParametersOut.Count > 0) { + //if (found.Value.ParametersOut.Count > 0) { - sb.Append($" OUT: {found.Value.ParametersOut.Count}"); + // sb.Append($" OUT: {found.Value.ParametersOut.Count}"); - } + //} return sb.ToString();