/// <summary> /// Compiles the stated zenon Logic project. /// </summary> /// <param name="zenonLogicProject"></param> /// <param name="compilerOutputText"> /// Contains the output messages of the compilation process. Null if the if retrieving the compiler output failed. /// </param> internal void CompileZenonLogicProject(LogicProject zenonLogicProject, out IEnumerable <string> compilerOutputText) { ProcessStartInfo startInfo = new ProcessStartInfo(K5BexeFilePath, $"BUILD {this.ZenonLogicProjectDirectory}") { CreateNoWindow = false, WindowStyle = ProcessWindowStyle.Hidden }; using (Process stratonCompileProcess = new Process { StartInfo = startInfo }) { try { stratonCompileProcess.Start(); stratonCompileProcess.WaitForExit(); compilerOutputText = ReadCompileLogFileOfZenonLogicProject(zenonLogicProject); } catch (Exception e) { throw new InvalidOperationException(string.Format(Strings.K5BCompileFailedException, zenonLogicProject.Path), e); } } }
/// <summary> /// Writes the appli.xml content directly into the file. /// </summary> /// <remarks> /// This is done because import via k5b.exe omitts the folder structures in the application tree. /// </remarks> /// <param name="zenonLogicProject"></param> private void WriteAppliXmlFile(LogicProject zenonLogicProject) { string appliXmlFilePathToImport = TemporaryFileCreator.GetRandomTemporaryFilePathWithExtension("xml"); zenonLogicProject.ApplicationTree.ExportAsFile(appliXmlFilePathToImport, LogicXmlEncoding); var appliFilePath = Path.Combine(this.ZenonLogicProjectDirectory, "appli.xml"); try { if (File.Exists(appliFilePath)) { File.Delete(appliFilePath); } File.Copy(appliXmlFilePathToImport, appliFilePath); } catch (IOException e) { throw new InvalidOperationException(string.Format(Strings.AppliFileWriteIOException, appliFilePath), e); } catch (Exception e) { throw new InvalidOperationException(String.Format(Strings.AppliFileWriteException, appliFilePath), e); } }
private string SerializeZenonLogicProjectToXmlFile(LogicProject zenonLogicProject) { string xmlFilePathToImport = TemporaryFileCreator.GetRandomTemporaryFilePathWithExtension("xml"); zenonLogicProject.ExportAsFile(xmlFilePathToImport, LogicXmlEncoding); return(xmlFilePathToImport); }
internal bool TryApplyCompilerSettings(LogicProject zenonLogicProject, ImportOptions options = ImportOptions.Default) { if (zenonLogicProject?.Settings == null) { return(false); } var settings = zenonLogicProject.Settings.CompilerSettings.CompilerOptions.OptionTuples ?? Enumerable.Empty <LogicOptionTuple>(); bool allSucceeded = TryApplyCompilerSettings(settings); // According to CD-FR the following three setting sections shall never be set manually. //allSucceeded // = TryApplySettings(zenonLogicProject.Settings.CompilerSettings.SimulationCodeOptions.OptionTuples ?? Enumerable.Empty<LogicOptionTuple>()) // && allSucceeded; //allSucceeded // = TryApplySettings(zenonLogicProject.Settings.CompilerSettings.TargetCodeOptions.OptionTuples ?? Enumerable.Empty<LogicOptionTuple>()) // && allSucceeded; uint cycleTime = zenonLogicProject.Settings.TriggerTime.CycleTime; if (cycleTime == 0) { cycleTime = 10000; // 10 seconds as the default for invalid values } allSucceeded = SetOption("CycleTime", cycleTime.ToString()) && allSucceeded; if (!options.HasFlag(ImportOptions.ApplyOnlineSettings)) { return(allSucceeded); } return(allSucceeded); }
/// <summary> /// Compiles the stated zenon Logic project. /// </summary> /// <param name="zenonLogicProjectToCompile"></param> /// <param name="compilerOutputText"> /// Contains the output messages of the compilation process. Null if the if retrieving the compiler output failed. /// </param> public void CompileZenonLogicProject(LogicProject zenonLogicProjectToCompile, out IEnumerable <string> compilerOutputText) { if (!Directory.Exists(zenonLogicProjectToCompile.Path)) { throw new DirectoryNotFoundException ($"{Strings.ZenonLogicProjectDirectoryNotFound} {Strings.ZenonLogicHintForMissingDirectory}"); } K5ToolSet k5ToolSet = new K5ToolSet(zenonLogicProjectToCompile.Path); k5ToolSet.CompileZenonLogicProject(zenonLogicProjectToCompile, out compilerOutputText); }
/// <summary> /// Imports the stated <see cref="LogicProject"/> into zenon Logic. /// </summary> /// <param name="zenonLogicProject">The project to import into zenon.</param> /// <param name="options">Specifies options on how to import the <paramref name="zenonLogicProject"/> into zenon.</param> internal bool ImportZenonLogicProject(LogicProject zenonLogicProject, ImportOptions options) { string xmlFilePathToImport = SerializeZenonLogicProjectToXmlFile(zenonLogicProject); string option = "XMLMERGE"; if (options.HasFlag(ImportOptions.ReCreateVariables)) { option += "-RV"; } if (options.HasFlag(ImportOptions.DoNotMerge)) { option += "-NM"; } string arguments = $"{option} {this.ZenonLogicProjectDirectory} {xmlFilePathToImport}"; // Import via K5B.exe ProcessStartInfo startInfo = new ProcessStartInfo(K5BexeFilePath, arguments) { CreateNoWindow = false, WindowStyle = ProcessWindowStyle.Hidden }; using (Process stratonXmlImportProcess = new Process { StartInfo = startInfo }) { try { stratonXmlImportProcess.Start(); stratonXmlImportProcess.WaitForExit(); } catch (Exception e) { throw new InvalidOperationException(Strings.K5BXmlImportFailedException, e); } } // needed for import using K5B.exe as these parts get not imported WriteAppliXmlFile(zenonLogicProject); WriteGlobalDefinesFile(zenonLogicProject); return(true); }
/// <summary> /// Reads the __build.log file of the stated zenon Logic project. /// The file contains the output messages of the last compilation process. /// </summary> /// <param name="zenonLogicProject"></param> /// <returns>Returns null if __build.log does not exist. Happens if the projects was never compiled.</returns> private IEnumerable <string> ReadCompileLogFileOfZenonLogicProject(LogicProject zenonLogicProject) { string compilerOutputFilePath = Path.Combine(ZenonLogicProjectDirectory, ZenonLogicCompileLogFileName); if (!File.Exists(compilerOutputFilePath)) { return(null); } try { return(File.ReadAllLines(compilerOutputFilePath)); } catch (Exception e) { throw new FileLoadException(string.Format(Strings.ZenonLogicCompileLogFileReadException, zenonLogicProject.Path), e); } }
/// <summary> /// Writes the appli.EQV content directly into the file. /// </summary> /// <remarks> /// This is done because the import via k5b.exe omitts global definitions. /// </remarks> /// <param name="zenonLogicProject"></param> private void WriteGlobalDefinesFile(LogicProject zenonLogicProject) { string globalDefinesFilePath = TemporaryFileCreator.GetRandomTemporaryFilePathWithExtension("EQV"); LogicDefine globalDefine = zenonLogicProject.LogicDefinitions.Defines .FirstOrDefault(define => define.Name.Equals(Strings.GlobalDefineName)); if (globalDefine == null || string.IsNullOrWhiteSpace(globalDefine.DefineContent)) { return; } File.WriteAllText(globalDefinesFilePath, globalDefine.DefineContent); var appliEqvFilePath = Path.Combine(this.ZenonLogicProjectDirectory, "appli.EQV"); if (File.Exists(appliEqvFilePath)) { File.Delete(appliEqvFilePath); } File.Copy(globalDefinesFilePath, appliEqvFilePath); }
private static void Main(string[] args) { // NOTE: No error handling etc. is included here, this sample is just intended to give you a starting point on // handling zenon Logic projects via code, // IMPORTANT: To avoid side effects, you should make sure that the Logic workbench is not running, // which is not shown here. // This sample accesses zenon via COM, therefore we need to connect first. var zenonEditor = Marshal.GetActiveObject(ZenonRuntimeComObjectName) as zenOn.ApplicationED; if (zenonEditor == null) { Console.WriteLine("Cannot connect to an instance of zenon Editor."); return; } // The active zenon editor project is used for our sample. var zenonProject = zenonEditor.MyWorkspace.ActiveDocument; if (zenonProject == null) { Console.WriteLine("No active instance of a zenon Editor project can be received."); return; } // NOTE: For this example, you need a reference to zenon.Interop.dll in your .csproj. // We added it from version 7.60 to a binaries folder. // // You can use alternative approaches to the one which is shown here, i.e.: // - modifying the project as pure XML // Required: References to zenonApi.Core and zenonApi.Logic. // Advantage: No dependency on Windows or zenon. // Disadvantage: If you want to import the object model to Logic, you need to do this on your own. // - modifying the project via a COM reference to zenon, as done in this example // Required: Reference to zenon.Interop.dll, references to zenonApi.Core, zenonApi.Logic and zenonApi.Zenon // Advantage: Easy to import and modify Logic projects from within a zenon context. // Disadvantage: Can only be done with a running zenon Editor instance on Windows // - modifying the project not via the Add-In framework // Required: Reference to Scada.AddIn.Contracts, zenonApi.Core, zenonApi.Logic, zenonApi.Zenon // Advantage: Same as for COM // Disadvantage: Same as for COM // - Just create projects and do basic modifications without any of our APIs // Required: - // Advantage: No dependencies, except K5B.exe and/or K5Prp.dll // Disadvantage: Just XML, no typed object model, hard to modify/maintain, etc. // What you choose simply depends on what you are developing and using. // The easiest way might be to work with the provided APIs we use here for COM and Add-In. // If you use the Add-In Framework, use "new zenonApi.Zenon.Zenon(zenonProject) var wrapper = new zenonApi.Zenon.ZenonCom(zenonProject); // We want to modify or create a Logic project named "Sample": var lazyLogicProjects = wrapper.LazyLogicProjects; var sampleProjectToBeEdited = lazyLogicProjects.FirstOrDefault(x => x.ProjectName == "Sample")?.Value; if (sampleProjectToBeEdited == null) { // "Sample" does not exist, we need to create it with the following. // All changes you make in the API will only take affect, after you call ImportLogicProjectsIntoZenon // (see the end of this file) sampleProjectToBeEdited = new LogicProject("Sample"); lazyLogicProjects.Add(new LazyLogicProject(sampleProjectToBeEdited)); } // Access the logic options etc. via a object model. The following shows some examples. sampleProjectToBeEdited.Settings.TriggerTime.CycleTime = 10000; sampleProjectToBeEdited.Settings.CompilerSettings.CompilerOptions["warniserr"] = "OFF"; // Get the first global variable group ("(GLOBAL)" and "(RETAIN)" always exist). var variableGroup = sampleProjectToBeEdited.GlobalVariables.VariableGroups.FirstOrDefault(); // The appropriate group can also be accessed directly like this: variableGroup = sampleProjectToBeEdited.GlobalVariables[LogicVariableKind.Global]; // Sample for creating some string variables in our "Sample" project: for (int i = 0; i < 10; i++) { var variableSample = new LogicVariable() { InitialValue = "5", MaxStringLength = "255", Type = "STRING", Name = "MyVariable" + i, }; variableSample.VariableInfos.Add(new LogicVariableInfo() { // Set to be visible in zenon (SYB Flag) Data = "<syb>", Type = LogicVariableInformationTypeKind.Embed }); variableSample.VariableInfos.Add(new LogicVariableInfo() { Data = "STRATON", Type = LogicVariableInformationTypeKind.Profile }); variableGroup.Variables.Add(variableSample); } // Get the first folder of your application tree in Logic and rename it LogicFolder folder = sampleProjectToBeEdited.ApplicationTree.Folders.FirstOrDefault(); if (folder == null) { // Does not exist, create it instead. folder = new LogicFolder("RenamedTestFolder"); sampleProjectToBeEdited.ApplicationTree.Folders.Add(folder); } // Renaming is possible. folder.Name = "RenamedTestFolder"; // Same for the first program: LogicProgram program = folder.Programs.FirstOrDefault(); if (program == null) { program = new LogicProgram("RenamedTestProgram"); folder.Programs.Add(program); } program.Name = "RenamedMyProgram"; // Modify the source code of a program: program.SourceCode += "\n// Some Comment"; // Navigate back to the application tree when only having the program (useful when working with multiple logic // projects at once): var folderAgain = program.Parent; // Change the cycle timing and other project settings sampleProjectToBeEdited.Settings.TriggerTime.CycleTime = 12345; sampleProjectToBeEdited.Settings.CompilerSettings.CompilerOptions["warniserr"] = "OFF"; // Modify variables (if it exists) var variable = program.VariableGroups.FirstOrDefault()?.Variables.FirstOrDefault(); if (variable != null) { variable.Name = "RenamedVariable"; variable.Attributes.In = true; variable.Attributes.Out = true; variable.VariableInfos.Add(new LogicVariableInfo() { Type = LogicVariableInformationTypeKind.Embed, Data = "<syb>" }); } // Remove a folder if it exists sampleProjectToBeEdited.ApplicationTree.Folders.FirstOrDefault(x => x.Name == "Signals")?.Remove(); // Sample for exporting a project object model to a file: string sampleFile = $@"C:\Users\{Environment.UserName}\Desktop\DemoProjectModified.xml"; sampleProjectToBeEdited.ExportAsFile(sampleFile, Encoding.GetEncoding("iso-8859-1")); // Sample for reading a project object model from a file: var projectFromXmlFile = LogicProject.Import(XElement.Load(sampleFile)); // Sample for convert the project object model to XElements and save it manually to the same file XElement modifiedProject = sampleProjectToBeEdited.ExportAsXElement(); XDocument document = new XDocument { Declaration = new XDeclaration("1.0", "iso-8859-1", "yes") }; document.Add(modifiedProject); using (XmlTextWriter writer = new XmlTextWriter( $@"C:\Users\{Environment.UserName}\Desktop\DemoProjectModified.xml", Encoding.GetEncoding("iso-8859-1"))) { writer.Indentation = 3; writer.Formatting = Formatting.Indented; document.Save(writer); } // Import and commit logic projects with the changes we made: wrapper.ImportLogicProjectsIntoZenon(); // For zenon version 10 or higher, additional import options are available, e.g.: // wrapper.ImportLogicProjectsIntoZenon(true, ImportOptions.ReCreateVariables | ImportOptions.ApplyOnlineSettings); wrapper.Dispose(); }
internal void TryApplyOnlineChangeSettings(LogicProject zenonLogicProject, ImportOptions options = ImportOptions.Default) { if (!options.HasFlag(ImportOptions.ApplyOnlineSettings)) { return; } var optionTuples = zenonLogicProject.Settings.OnlineChangeSettings.OptionTuples ?? Enumerable.Empty <LogicOptionTuple>(); const uint callback = 0x400; IntPtr hwnd = Process.GetCurrentProcess().MainWindowHandle; string clientName = System.Reflection.Assembly.GetEntryAssembly().GetName().Name; string hotSizeBuffer = string.Empty; bool enableHot = false; bool hasHotOptionDefined = false; using (K5SrvWrapper srv = K5SrvWrapper.TryConnect(hwnd, callback, zenonLogicProject.Path, clientName, K5SrvConstants.K5DbSelfNotif)) { while (!srv.IsReady) { Thread.Sleep(100); } foreach (var optionTuple in optionTuples) { if (!string.IsNullOrWhiteSpace(optionTuple.Name) && !string.IsNullOrWhiteSpace(optionTuple.Value)) { if (optionTuple.Name.StartsWith("size_")) { hotSizeBuffer = hotSizeBuffer + optionTuple.Name.Replace("size_", "") + "=" + optionTuple.Value + ","; } else if (optionTuple.Name.Equals("enable")) { hasHotOptionDefined = true; if (int.TryParse(optionTuple.Value, out var i)) { enableHot = i > 0; } else if (bool.TryParse(optionTuple.Value, out var b)) { enableHot = b; } else if (optionTuple.Value.Equals("ON", StringComparison.OrdinalIgnoreCase)) { enableHot = true; } else if (optionTuple.Value.Equals("OFF", StringComparison.OrdinalIgnoreCase)) { enableHot = false; } } } } if (hasHotOptionDefined) { srv.SetHot(enableHot); } if (hotSizeBuffer.Length > 0) { srv.SetHotSizing(hotSizeBuffer); } } }