F func params parser

This commit is contained in:
Felix Weiß
2023-07-26 22:47:35 +02:00
parent 4d6eee5585
commit 1f33ebb8d3
3 changed files with 206 additions and 33 deletions

View File

@@ -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();

View File

@@ -116,6 +116,10 @@ namespace MewtocolNet {
}
#endregion
#region Program Read / Write
public async Task<PlcBinaryProgram> ReadProgramAsync () {
var steps = new List<byte[]>();
@@ -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();

View File

@@ -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<ushort, string> stepCommands = new Dictionary<ushort, string> {
{ 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);
if(foundFunc != null) {
ParseFuncParams(paramsStartIndex, foundFunc, stepString);
}
}
if (stepHeader != null) Console.WriteLine(stepHeader);
Console.WriteLine(stepString.ToString());
}
}
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++;
}
//if (stepBytesString.StartsWith("F")) {
// Console.Write(" STEP COMMAND");
sb.Append($"Words: {paramsWordCount}");
//if (func.ParametersIn != null) {
// for (int i = 0; i < func.ParametersIn.Count; i++) {
// sb.Append($"PI_{i}:");
// }
//}
Console.WriteLine();
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) {
Console.WriteLine();
sb.Append($"K{paramStepH[0]:X2}{paramStepL[0]:X2}, ");
}
private string GetFunctionName (string funcName) {
//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 GetFunction (string funcName, out FPFunction foundFunc) {
foundFunc = null;
var found = FPFunction.functions.FirstOrDefault(x => x.Key.StartsWith(funcName));
if (!found.Equals(default(KeyValuePair<string, FPFunction>))) {
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();