/// <summary> /// Generate code from pinout /// </summary> /// <param name="pins"></param> /// <param name="codeBuffer"></param> internal static void CompilePinout(IEnumerable <LDPin> pins, CompilerBuffer codeBuffer) { if (pins.Count(x => x.Pin == "NONE") > 0) { throw new InvalidOperationException("Can't Compile with unassigned pins in diagram"); } codeBuffer.SetupContent.Add("//Inputs"); foreach (var pin in pins.Where(x => x.Type == PinType.Input)) { codeBuffer.SetupContent.Add("pinMode(" + pin.Pin + ", INPUT);//" + pin.Variable); codeBuffer.InputRefreshContent.Add(pin.Variable + " = digitalRead(" + pin.Pin + ");"); } foreach (var pin in pins.Where(x => x.Type == PinType.Analog)) { codeBuffer.SetupContent.Add("//Pin " + pin.Pin + " used as analog input to " + pin.Variable); codeBuffer.Defines.Add("#define " + pin.Variable + " " + pin.Pin + " //Analog input pin"); } codeBuffer.SetupContent.Add("//Outputs"); foreach (var pin in pins.Where(x => x.Type == PinType.Output)) { codeBuffer.SetupContent.Add("pinMode(" + pin.Pin + ", OUTPUT);//" + pin.Variable); codeBuffer.OutputRefreshContent.Add("digitalWrite(" + pin.Pin + ", " + pin.Variable + ");"); } foreach (var pin in pins.Where(x => x.Type == PinType.PWM)) { codeBuffer.SetupContent.Add("pinMode(" + pin.Pin + ", OUTPUT);//PWM to " + pin.Variable); codeBuffer.Defines.Add("#define " + pin.Variable + " " + pin.Pin + " //PWM pin"); } }
/// <summary> /// Compile a Ladder Diagram to Arduino C++ /// </summary> /// <param name="diagram"></param> /// <returns></returns> public static string CompileDiagram(Diagram diagram) { if (diagram == null) { throw new ArgumentNullException("diagram", "Null Diagram"); } CompilerBuffer codeBuffer = new CompilerBuffer(); CompileDataTable(diagram.DataTable, codeBuffer); CompilePinout(diagram.Pins, codeBuffer); CompileRungs(diagram.Rungs, codeBuffer); return(CodeBuilder(codeBuffer)); }
/// <summary> /// Compile single input function /// </summary> /// <param name="component">Component to be compiled</param> /// <param name="input">Function logical input</param> /// <param name="codeBuffer">Compiler's code buffer</param> /// <returns></returns> internal static string CompileInputFunctionComponent(ComponentBase component, string input, CompilerBuffer codeBuffer) { string buffer = string.Empty; if (component is OSF) { buffer = OSF_FN + "(" + codeBuffer.OSRCount + ", " + input + ")"; codeBuffer.OSFCount++; } else if (component is OSR) { buffer = OSR_FN + "(" + codeBuffer.OSRCount + ", " + input + ")"; codeBuffer.OSRCount++; } else if (component is CounterComponent) { CounterComponent counter = component as CounterComponent; if (component is CTD) { buffer = CTD_FN + "(" + counter.Limit + ", &" + counter.FullName + ", " + codeBuffer.OSRCount + ", " + input + ")"; codeBuffer.OSRCount++; } else if (component is CTU) { buffer = CTU_FN + "(" + counter.Limit + ", &" + counter.FullName + ", " + codeBuffer.OSRCount + ", " + input + ")"; codeBuffer.OSRCount++; } } return(buffer); }
/// <summary> /// Compile a single output component /// </summary> /// <param name="component">Component to be compiled</param> /// <param name="codeBuffer">Compiler's code buffer</param> /// <returns>If and else statements</returns> internal static Tuple <string, string> CompileOutputComponent(ComponentBase component, CompilerBuffer codeBuffer) { string ifCommand = string.Empty, elseCommand = string.Empty; if (component is Coil) { Coil coil = component as Coil; switch (coil.Mode) { case Coil.CoilMode.Negated: ifCommand = coil.FullName + " = false;"; elseCommand = coil.FullName + " = true;"; break; case Coil.CoilMode.Normal: ifCommand = coil.FullName + " = true;"; elseCommand = coil.FullName + " = false;"; break; case Coil.CoilMode.Reset: ifCommand = coil.FullName + " = false;"; break; case Coil.CoilMode.Set: ifCommand = coil.FullName + " = true;"; break; } } else if (component is ADC) { ifCommand = (component as ADC).Destination + " = analogRead(" + (component as ADC).FullName + ");"; } else if (component is PWM) { ifCommand = "analogWrite(" + (component as PWM).FullName + ", " + (component as PWM).DudyCycle + ");"; elseCommand = "analogWrite(" + (component as PWM).FullName + ", 0);"; } else if (component is RES) { ifCommand = (component as RES).FullName + " = 0;"; } else if (component is CTC) { CTC ctc = component as CTC; ifCommand = "if (" + OSR_FN + "(" + codeBuffer.OSRCount + ", true)) " + ctc.FullName + " = (" + ctc.FullName + " >= " + ctc.Limit + ") ? 0 : " + ctc.FullName + " + 1;"; elseCommand = OSR_FN + "(" + codeBuffer.OSRCount + ", false));"; codeBuffer.OSRCount++; } else if (component is MathComponent) { MathComponent mc = component as MathComponent; if (component is ADD) { ifCommand = mc.Destination + " = " + mc.VarA + " + " + mc.VarB + ";"; } else if (component is DIV) { ifCommand = mc.Destination + " = " + mc.VarA + " / " + mc.VarB + ";"; } else if (component is MUL) { ifCommand = mc.Destination + " = " + mc.VarA + " * " + mc.VarB + ";"; } else if (component is SUB) { ifCommand = mc.Destination + " = " + mc.VarA + " - " + mc.VarB + ";"; } else if (component is MOV) { ifCommand = mc.Destination + " = " + mc.VarA + ";"; } } else if (component is ELF) { ifCommand = (component as ELF).Name + "();"; } return(new Tuple <string, string>(ifCommand, elseCommand)); }
/// <summary> /// Generate code from data table variables /// </summary> /// <param name="table"></param> /// <param name="codeBuffer"></param> internal static void CompileDataTable(LadderDataTable table, CompilerBuffer codeBuffer) { List <Tuple <string, Type, LDVarClass, object> > tuples = table.ListAllData().OrderBy(x => x.Item1).ToList(); codeBuffer.Globals.Add("//Inputs"); StringBuilder inputList = new StringBuilder(); foreach (var tuple in tuples.Where(x => x.Item3 == LDVarClass.Input)) { codeBuffer.Globals.Add("boolean " + tuple.Item1 + ";"); } codeBuffer.Globals.Add(string.Empty); codeBuffer.Globals.Add("//Outputs"); foreach (var tuple in tuples.Where(x => x.Item3 == LDVarClass.Output)) { codeBuffer.Globals.Add("boolean " + tuple.Item1 + ";"); } codeBuffer.Globals.Add(string.Empty); codeBuffer.Globals.Add("//Data"); foreach (var tuple in tuples.Where(x => x.Item3 == LDVarClass.Data)) { switch (tuple.Item2.ToString().Replace("System.", string.Empty)) { case "Boolean": codeBuffer.Globals.Add("boolean " + tuple.Item1 + " = " + (((bool)tuple.Item4) ? "true;" : "false;")); break; case "Int16": codeBuffer.Globals.Add("int " + tuple.Item1 + " = " + tuple.Item4.ToString() + ";"); break; case "Byte": codeBuffer.Globals.Add("byte " + tuple.Item1 + " = " + tuple.Item4.ToString() + ";"); break; default: throw new FormatException("Unrecognized variable type in data table"); } } foreach (var tuple in tuples.Where(x => x.Item3 == LDVarClass.InFunction)) { List <string> buffer = new List <string>(); buffer.Add("boolean " + tuple.Item1 + "(boolean input)"); buffer.Add("{"); foreach (string line in tuple.Item4.ToString().Split('\n')) { buffer.Add(INDENT + line); } buffer.Add("}"); codeBuffer.Functions.Add(buffer); } foreach (var tuple in tuples.Where(x => x.Item3 == LDVarClass.OutFunction)) { List <string> buffer = new List <string>(); buffer.Add("void " + tuple.Item1 + "()"); buffer.Add("{"); foreach (string line in tuple.Item4.ToString().Split('\n')) { buffer.Add(INDENT + line); } buffer.Add("}"); codeBuffer.Functions.Add(buffer); } }
/// <summary> /// Compile the diagram's rung collection /// </summary> /// <param name="rungs"></param> /// <param name="codeBuffer"></param> internal static void CompileRungs(IEnumerable <Rung> rungs, CompilerBuffer codeBuffer) { foreach (Rung rung in rungs) { #region Buffers List <string> tempStatements = new List <string>(); List <NodeExpression> nodes = new List <NodeExpression>().RunAnalysis(rung); List <NodeOutputs> outputs = new List <NodeOutputs>(); #endregion #region Analysis Loop foreach (ComponentBase component in rung.Components) { NodeExpression NodeA = nodes.GetNodeConnections(component.LeftLide); NodeExpression NodeB = nodes.GetNodeConnections(component.RightLide); if (component.GetCompilerClass() == ComponentCompilerClass.Input) { if (!string.IsNullOrEmpty(NodeA.Expression)) { NodeB += string.Format(NODE_NUMBER_MARKER + "{0} && {1}", nodes.IndexOf(NodeA), CompileInputComponent(component)); } else { NodeB += CompileInputComponent(component); } } else { NodeExpression lastParallel = nodes.GetLastParallel(NodeA); if (!string.IsNullOrEmpty(NodeA.Expression) && !lastParallel.IsTempValue) { tempStatements.Add(string.Format(RATD + "[{0}]" + " = {1};", tempStatements.Count, lastParallel.Expression)); lastParallel.Expression = string.Format(RATD + "[{0}]", tempStatements.Count - 1); lastParallel.IsTempValue = true; } if (component.GetCompilerClass() == ComponentCompilerClass.InputFunction) { if (!string.IsNullOrEmpty(NodeA.Expression)) { NodeB += CompileInputFunctionComponent(component, NODE_NUMBER_MARKER + nodes.IndexOf(NodeA), codeBuffer); } else { NodeB += CompileInputFunctionComponent(component, TRUE, codeBuffer); } } else { outputs.AddOutputs(NodeA.Node, CompileOutputComponent(component, codeBuffer)); } } } #endregion if (codeBuffer.BoolTempCount < tempStatements.Count) { codeBuffer.BoolTempCount = tempStatements.Count; } List <string> sRung = BuildRung(tempStatements, nodes, outputs); if (!string.IsNullOrEmpty(rung.Comment)) { sRung.Insert(0, "/*" + rung.Comment + "*/"); } codeBuffer.Rungs.Add(sRung); } }
/// <summary> /// Generate the automatic ladder functions /// </summary> /// <param name="codeBuffer"></param> /// <param name="codeBuilder"></param> private static void LadderFunctionBuilder(CompilerBuffer codeBuffer, StringBuilder codeBuilder) { #region OSF if (codeBuffer.OSFCount > 0) { codeBuilder.AppendLine("boolean " + OSF_FN + "(int n, boolean input){"); codeBuilder.AppendLine(INDENT + "byte *osf = &" + OSF_DATA + "[n / 8];"); codeBuilder.AppendLine(INDENT + "int j = (n % 8);"); codeBuilder.AppendLine(INDENT + "boolean prev = bitRead(*osf, j);"); codeBuilder.AppendLine(INDENT + "bitWrite(*osf, j, input);"); codeBuilder.AppendLine(INDENT + "return prev && !input;"); codeBuilder.AppendLine("}"); codeBuilder.AppendLine(); } #endregion #region OSR if (codeBuffer.OSRCount + codeBuffer.CTDCount + codeBuffer.CTUCount > 0) { codeBuilder.AppendLine("boolean " + OSR_FN + "(int n, boolean input){"); codeBuilder.AppendLine(INDENT + "byte *osr = &" + OSR_DATA + "[n / 8];"); codeBuilder.AppendLine(INDENT + "int j = (n % 8);"); codeBuilder.AppendLine(INDENT + "boolean prev = bitRead(*osr, j);"); codeBuilder.AppendLine(INDENT + "bitWrite(*osr, j, input);"); codeBuilder.AppendLine(INDENT + "return !prev && input;"); codeBuilder.AppendLine("}"); codeBuilder.AppendLine(); } #endregion #region CTD if (codeBuffer.CTDCount > 0) { codeBuilder.AppendLine("boolean " + CTD_FN + "(int limit, int *dest, int osr, boolean input){"); codeBuilder.AppendLine(INDENT + "if (" + OSR_FN + "(osr, input)) *dest -= 1;"); codeBuilder.AppendLine(INDENT + "return *dest >= limit;"); codeBuilder.AppendLine("}"); codeBuilder.AppendLine(); } #endregion #region CTU if (codeBuffer.CTUCount > 0) { codeBuilder.AppendLine("boolean " + CTU_FN + "(int limit, int *dest, int osr, boolean input){"); codeBuilder.AppendLine(INDENT + "if (" + OSR_FN + "(osr, input)) *dest += 1;"); codeBuilder.AppendLine(INDENT + "return *dest >= limit;"); codeBuilder.AppendLine("}"); codeBuilder.AppendLine(); } #endregion #region Refresh Inputs if (codeBuffer.InputRefreshContent.Count > 0) { codeBuilder.AppendLine("void " + REFRESH_INPUT_FN + "(){"); foreach (string item in codeBuffer.InputRefreshContent) { codeBuilder.AppendLine(INDENT + item); } codeBuilder.AppendLine("}"); codeBuilder.AppendLine(); } #endregion #region Refresh Outputs if (codeBuffer.OutputRefreshContent.Count > 0) { codeBuilder.AppendLine("void " + REFRESH_OUTPUT_FN + "(){"); foreach (string item in codeBuffer.OutputRefreshContent) { codeBuilder.AppendLine(INDENT + item); } codeBuilder.AppendLine("}"); codeBuilder.AppendLine(); } #endregion }
/// <summary> /// Assemble the code parts into a finished compiled code /// </summary> /// <param name="codeBuffer"></param> /// <returns></returns> internal static string CodeBuilder(CompilerBuffer codeBuffer) { StringBuilder codeBuilder = new StringBuilder(); codeBuilder.AppendLine("// Code generated by Ladderino V " + COMPILER_VERSION); codeBuilder.AppendLine("// Developed by: Allan Leon"); codeBuilder.AppendLine(); #region Defines if (codeBuffer.Defines.Count > 0) { codeBuilder.AppendLine("/* ================================[ Defines ]=============================== */"); foreach (string item in codeBuffer.Defines) { codeBuilder.AppendLine(item); } codeBuilder.AppendLine("/* ========================================================================== */"); codeBuilder.AppendLine(); } #endregion Defines #region Includes if (codeBuffer.Includes.Count > 0) { codeBuilder.AppendLine("/* ===============================[ Includes ]=============================== */"); foreach (string item in codeBuffer.Includes) { codeBuilder.AppendLine(item); } codeBuilder.AppendLine("/* ========================================================================== */"); codeBuilder.AppendLine(); } #endregion Includes #region Global Variables codeBuilder.AppendLine("/* ================================[ Globals ]=============================== */"); foreach (string item in codeBuffer.Globals) { codeBuilder.AppendLine(item); } if (codeBuffer.OSFCount > 0) { codeBuilder.AppendLine("byte " + OSF_DATA + "[" + ((int)(codeBuffer.OSFCount / 8) + 1) + "];"); } if (codeBuffer.OSRCount > 0) { codeBuilder.AppendLine("byte " + OSR_DATA + "[" + ((int)(codeBuffer.OSRCount / 8) + 1) + "];"); } codeBuilder.AppendLine("/* ========================================================================== */"); codeBuilder.AppendLine(); #endregion Global Variables #region Ladder Functions codeBuilder.AppendLine("/* ===========================[ Ladder Functions ]=========================== */"); LadderFunctionBuilder(codeBuffer, codeBuilder); codeBuilder.AppendLine("/* ========================================================================== */"); codeBuilder.AppendLine(); #endregion Ladder Functions #region User Functions if (codeBuffer.Functions.Count > 0) { codeBuilder.AppendLine("/* ===============================[ Functions ]============================== */"); foreach (var item in codeBuffer.Functions) { foreach (string line in item) { codeBuilder.AppendLine(line); } codeBuilder.AppendLine(); } codeBuilder.AppendLine("/* ========================================================================== */"); codeBuilder.AppendLine(); } #endregion User Functions #region Main codeBuilder.AppendLine("/* =================================[ Main ]================================= */"); #region Setup codeBuilder.AppendLine("void setup() {"); foreach (string item in codeBuffer.SetupContent) { codeBuilder.AppendLine(INDENT + item); } codeBuilder.AppendLine("}"); codeBuilder.AppendLine(); #endregion #region Loop codeBuilder.AppendLine("void loop() {"); if (codeBuffer.BoolTempCount > 0) { codeBuilder.AppendLine(INDENT + "boolean " + RATD + "[" + codeBuffer.BoolTempCount + "]; //Rung Activation Temporary Data"); } if (codeBuffer.InputRefreshContent.Count > 0) { codeBuilder.AppendLine(INDENT + REFRESH_INPUT_FN + "();"); } codeBuilder.AppendLine(); for (int c = 0; c < codeBuffer.Rungs.Count; c++) { codeBuilder.AppendLine(INDENT + "//Rung " + c + " Code:"); foreach (string item in codeBuffer.Rungs[c]) { codeBuilder.AppendLine(INDENT + item); } codeBuilder.AppendLine(); } if (codeBuffer.OutputRefreshContent.Count > 0) { codeBuilder.AppendLine(INDENT + REFRESH_OUTPUT_FN + "();"); } codeBuilder.AppendLine("}"); codeBuilder.AppendLine(); #endregion codeBuilder.AppendLine("/* ========================================================================== */"); codeBuilder.AppendLine(); #endregion Main return(codeBuilder.ToString()); }