ACM API

ACM API


API Documentation

The API documentation is included with each installer as a Compiled HTML Help file (.chm).


Opening the .chm file

The file is named ‘AutoSol.ACM.Client.chm’ and is found in the install path:

'C:\Program Files\AutoSol\Communication Manager\Server'.

 

If you don’t see the path in ‘C:\Program Files’, you installed ACM in a different path. The .chm file will be found at ‘{ACM Install Location}\AutoSol\Communication Manager\Server’.

image-20250407-173801.png
Notice the '?' in the file icon for .chm files

Double clicking the file will likely open the .chm file with the Microsoft HTML Help Executable. You can also right click the file, select ‘Open with…’, and select the Microsoft HTML Help Executable or a different .chm file viewer you prefer.

 

If you don’t see page contents on the right panel view, open the properties of the .chm file, and you will see an “unblock” checkbox at the bottom of the ‘General’ tab. Click the “unblock” checkbox, Apply, and reopen the .chm file.

image-20250407-182254.png
The default viewer on Windows

 

Searching and reading the .chm file

image-20250407-204821.png
View you should see when you open the .chm file
  • The Back and Forward buttons in the toolbar at the top can be used to move between visited pages.

  • The Home button on the toolbar takes you back to the Classes page of AutoSol.ACM.Client Namespace.

  • The thinner left panel has three sections: Contents, Index, and Search. Contents allows you to find the pages existing in the separate namespaces provided, Index can be used to find a specific function if you know part of the name, and Search can be used if you just know a keyword.

  • The larger right panel shows the content of the selected page. The colored text (with the exception of code snippets) signifies that text will take you to a new page. The text is blue if If the page hasn’t been visited before, and purple if it has.

 

Provided examples of C# implementation

image-20250407-220547.png
Examples are at the bottom of the list on the Contents tab


The .chm file contains 4 useful code API implementation in C# examples.

  • Example 1: Retrieve some Objects and Properties by using ConfigurationService class

  • Example 2: Create some Objects and Properties by using ConfigurationService class

  • Example 3: View OPC Tags in real-time using the ItemService class

  • Example 4: Send Commands to an ACM Object using the ControlService class

 


Make a .NET Framework project with Visual Studio

This section will guide you through the setup of your project.

For your project, the appropriate .NET Framework version will depend on the version of ACM you are utilizing:

  • Use .NET Framework 4.8 for ACM Version 9.2 or later.

  • Use .NET Framework 4.7.2 for ACM Versions earlier than 9.2.


Initial Setup of Visual Studio Components

You will need to make sure that Visual Studio can access the appropriate .NET Framework version (see note above). This will only need to be done once.

image-20250407-223900.png
View when you open the Visual Studio Installer
  1. Open your Visual Studio Installer if you already have Visual Studio downloaded OR download and Install Visual Studio from the Microsoft website. Visual Studio Community 2022 is free. https://visualstudio.microsoft.com/downloads/

    1. Search ‘Visual Studio Installer’ on the windows search bar if it is closed

 

  1. Select the Installed tab and Click the Modify button.

  1. Select the Individual Components tab and find the appropriate .NET Framework you need (Check the note at the start of this chapter)

If the .NET Framework 4.x SDK you need does not show up on the list, you will need to download the framework from Microsoft. https://dotnet.microsoft.com/en-us/download/dotnet-framework and download the Developers Pack version.

 

  1. Click on Modify at the bottom right of the installer screen to finish. From this point on, you will be able to select the appropriate .NET Framework when starting a project.

image-20250409-172936.png
Search for the appropriate .NET Framework you need based on ACM Version.

 

Create a new project

  1. Select Create a new project

  2. Enter .NET Framework in the search bar. Select a template that includes (.NET Framework) in the name. We use Console App (.NET Framework) for this guide.

  3. Make sure the Correct .NET Framework is selected (see note at the start of this chapter).

image-20250409-181833.png
Step 1.
image-20250409-182430.png
Step 2.
image-20250409-194304.png
Step 3.
  1. Click on Create at the bottom right. You now have a .NET Framework project!

 


API Implementation overview

This section is designed to guide your preliminary steps and recommended conventions to ensure a smooth start to implementation.


First Build of the Project

Now that your project has been made, we will setup the project and build it. The goal is to build a simple project and get the correct output when we run from the command line. I will build in Release mode for this demo, but building in Debug also works.

More information on building in Debug: Project Settings for a .NET C# debug config - Visual Studio (Windows)

1. Right click ‘References’ and select ‘Add Reference…' in the Solution Explorer:

image-20250409-210418.png
  1. Click on Browse at the bottom of the Reference Manager window that pops up. The reference you need is in the location “{InstalllLocation}\AutoSol\Communication Manager\Client\AutoSol.ACM.Config.Client.dll

image-20250409-210801.png
  1. Select OK once you have found the correct .dll file:

image-20250409-211410.png
  1. Add ‘using AutoSol.ACM.Client;’, a log line, and an exit into the Program.cs file:

Intellisense should try to autocomplete ‘AutoSol’ when you begin typing the ‘using’ statement. That is another indicator that the reference worked.
'Environment.Exit(0)' → terminates the process immediately, even if other threads exist and are running, and returns an exit code to the operating system. More information is available here: Environment.Exit(Int32) Method (System)

image-20250410-143452.png
  1. Select 'Release' from the drop down on the tool bar.

image-20250409-213752.png
the default toolbar at the top left of Visual Studio. Clicking on the downward arrow will reveal the options.
  1. Select ‘Build' then 'Build Solution’ on the tool bar. Your Build should succeed.

If the build fails because of a .NET Framework version error, you can either 1) remake the project with the correct .NET Framework, or 2) select ‘Project' at the top of the tool bar, then 'x Properties’ (where x is the name of your project) image-20250409-220325.png, then modify the target framework for the project on the drop down: image-20250409-220229.png

  1. Right Click the Project in the solution explorer, and select ‘Open Folder in File Explorer’.

image-20250409-221150.png
  1. Navigate to \bin\Release\ in the open File Explorer window. We are doing this to open a command line prompt using a shortcut from the File Explorer. Now click the line that shows the current directory like you are going to type. Type 'cmd' and press enter. You should have a command line open up already at that path.

image-20250409-222625.png
We are in ..\App1\App1\bin\Release because we want to run the executable

 

 

 

image-20250410-153142.png
Type ‘cmd’ and press enter
  1. Type in the project name (App1 in my example) to run the executable. You will see an the logline we added if everything worked correctly.

C:\Users\user\demofolder\App1\App1\bin\Release> App1.exe Hello from inside of Main!

 

Now that we have the ability to build and run the executable, let's add a way to pass in arguments from the command line.

  1. Copy this code into the appropriate section of your code, build the project, and run the executable. Arguments can be parsed in the string array parameter (args) in Main by comparing the element against expected values - like in line 11. For this example, we will make a simple print statement that will only be run by passing in the argument ‘test1’.

    namespace App1 { internal class Program { static void Main(string[] args) { ServerConnection asiServer = new ServerConnection(); asiServer.SetHostAddress("localhost"); asiServer.ConfigurationService.Connect(); // WORKING WITH ARGS if (args[0] == "test1") { Test1(asiServer); } Environment.Exit(0); } private static void Test1(ServerConnection conn) { Console.WriteLine("Hello from inside of Test1!"); } } }

    Running this code with the command line argument ‘test1’ will output “Hello from inside of Test1!":

    C:\Users\user\demofolder\App1\App1\bin\Release>App1.exe test1 Hello from inside of Test1!

 

  1. Replace the code inside of the Test1 function with:

    asiObject obj = conn.ConfigurationService.FindObjectByFullName("Demo ROC Protocol", true); Console.WriteLine("Found object: " + obj.Id);

    Line 1 finds the object and returns all of its properties. Check the .chm file for more details.

 

 

 

image-20250415-215330.png
Before you begin, add a folder named ‘Demo Folder’ and a Roc protocol named ‘Demo ROC Protocol’ through the ACM Configuration app.
image-20250415-215041.png
The object Id of my Demo ROC Protocol is 288
  1. Build and run the code again. You should see the correct object id output on the command line.

 

Test Run and Viewing Logger Output

Replace your code with the code provided in the Appendix at the bottom of this page. Build it as release, and run it from the command line. The code has WriteLine calls to print to the command line, and some of the methods will make logs that can be seen in the ACM Logger.
You can run it test by test:

C:\Users\user\demofolder\App1\App1\bin\Release>App1.exe test1 C:\Users\user\demofolder\App1\App1\bin\Release>App1.exe test2 C:\Users\user\demofolder\App1\App1\bin\Release>App1.exe test3 C:\Users\user\demofolder\App1\App1\bin\Release>App1.exe test4 C:\Users\user\demofolder\App1\App1\bin\Release>App1.exe test5

Or run the tests all at once:

C:\Users\user\demofolder\App1\App1\bin\Release>App1.exe all

 

Once you have completed all test runs, Open the ClassROC object in your ACM Configuration so you can view the logger:

image-20250416-202047.png
Before running test 5

 

image-20250416-202213.png
After running test 5 while logger was open. Your output likely won’t match the output in this image.

 

Tips

These are useful reminders when working with the AUTOSOL API code!

  • You can see what the valid values you can send into any property are by looking at property definitions. We may want to set the Series type to be REGFLO, but the API wants to see a numerical value. By seeing the property definitions, we can see what REFGLO maps too so that we can use that mapped value. Look at ‘Test4’ as an example of this.

  • The GUID of the ACM Objects can be used by the API to create objects. Access them through the ACM Configuration:

image-20250416-174526.png
Go to Configuration → Tools → Get Object Types

image-20250416-175341.png
The GUID should be stored as a string with capital letters and no spaces
image-20250416-174706.png
Copy the Type Identifier to use in C# Code
  • Use try-catch-finally blocks around AUTOSOL API Code. For example, if we did not include the try catch block in this code, we would see Connected to asiServer! even though the connection failed.

ServerConnection ser = new ServerConnection(); try { ser.SetHostAddress("localhost"); ser.ConfigurationService.Connect(); Consol.WriteLine("Connected to asiServer!"); } catch (Exception ex) { Console.WriteLine("ERROR from API:" + ex.Message) } finally { ser.Disconnect(); }
  • The root path for ACM Objects always begins in $System:

    image-20250415-211215.png
  • Use double backslashes ‘\\' in order to represent a single backslash ‘\’ in C#. A single ‘\’ is a special escape character used to indicate other character like new lines (’\n') or tabs ('\t'). The path C:\Users\user\demofolder\App1\App1\bin\Release would be C:\\Users\\user\\demofolder\\App1\\App1\\bin\\Release in C# code.

  • Changes to asi objects from C# need to be saved by a call to the Method ApplyChanges(bool Force).

  • If you want to call commands from the C# code, you will need to use the Control Service, and you will have to connect to the Control Service, similarly to the Configuration Service. Look at ‘Test5’ as an example of this.

    using AutoSol.ACM.Client.ControlService; asiServer.ConfigurationService.Connect(); asiServer.ControlService.Connect();

Appendix


Code to use in the Big Test:

using System; using System.Linq; using AutoSol.ACM.Client; using AutoSol.ACM.Client.ControlService; namespace App1 { internal class Program { static void Main(string[] args) { String endofline = "======================================\n"; ServerConnection asiServer = new ServerConnection(); try { asiServer.SetHostAddress("localhost"); asiServer.ConfigurationService.Connect(); asiServer.ControlService.Connect(); Console.WriteLine("Connected!"); // WORKING WITH ARGS if (args.ElementAt(0) == "test1") { Test1(asiServer); } else if (args.ElementAt(0) == "test2") { Test2(asiServer); } else if (args.ElementAt(0) == "test3") { Test3(asiServer); } else if (args.ElementAt(0) == "test4") { Test4(asiServer); } else if (args.ElementAt(0) == "test5") { Test5(asiServer); } else if (args.ElementAt(0) == "all") { Test1(asiServer); Test2(asiServer); Test3(asiServer); Test4(asiServer); Test5(asiServer); } else { Console.WriteLine("Invalid command line argument."); } } catch (Exception ex) { Console.WriteLine("ERROR: " + ex.Message); } finally { asiServer.Disconnect(); } Environment.Exit(0); } // Getting, Setting, and Saving changes to object properties private static void Test1(ServerConnection conn) { Console.WriteLine(startofline(1)); // Find the object "Demo ROC Protocol" inside of ACM, and then report its id asiObject obj = conn.ConfigurationService.FindObjectByFullName("Demo ROC Protocol", true); Console.WriteLine("Found Object Id: " + obj.Id); // GET the value of city from the Configuration of "Demo ROC Protocol" string sCity = obj.GetPropertyValue("city").ToString(); Console.WriteLine("Object City: " + sCity); // SET state = "Texas", and then SAVE the change with ApplyChanges() obj.SetPropertyValue("state", "Texas"); obj.ApplyChanges(true); Console.WriteLine("Object State: " + obj.GetPropertyValue("state").ToString()); Console.WriteLine(endofline); } // Listing all objects in a folder private static void Test2(ServerConnection conn) { Console.WriteLine(startofline(2)); // Find the folder object "Demo Folder" inside of ACM, and then report its id asiObject FolderObj = conn.ConfigurationService.FindObjectByFullName("Demo Folder", true); Console.WriteLine("Folder Object ID: " + FolderObj.Id); // List all of the objects and print Console.WriteLine("Contents:"); asiObjects objs = conn.ConfigurationService.ListObjects(FolderObj.Id); for (int i = 0; i < objs.Count; i++) { string sName = objs[i].Name; Console.WriteLine("\t" + sName); } Console.WriteLine(endofline); } // Create a new folder and object in that folder private static void Test3(ServerConnection conn) { Console.WriteLine(startofline(3)); // Found in the ACM Configuration string FolderTypeId = "{48E6B01F-B96B-42CF-AF66-DBC8D54446FD}"; string ROCTypeId = "{6450EDD7-4706-4DF3-9E97-998F3F1F865E}"; string MeterTypeId = "{9636C9B4-80BB-4DE2-A7AE-DF9A2A66FD26}"; string TCPIPId = "{7BC12E0D-0462-4A64-9DFB-0E4BE2A6921C}"; // Create a new Folder and create a ROC Device object in it. // Get the 'root' object to use as a parent object asiObject objRoot = conn.ConfigurationService.GetRootObject(); // Check if ClassFolder already exists. Exit if it exists asiObject oldfolder = null; try { oldfolder = conn.ConfigurationService.FindObjectByFullName("ClassFolder", false); } catch (Exception ex) { // Dont error because we want to make the folder } if (oldfolder != null) { Console.WriteLine("ClassFolder already existed!"); Console.WriteLine(endofline); return; } // Create a folder in the root asiObject objFolder = conn.ConfigurationService.CreateObject(null, FolderTypeId, objRoot.Id, "ClassFolder"); Console.WriteLine("Created ClassFolder object"); // Create a ROC Device object in our folder asiObject objROC = conn.ConfigurationService.CreateObject(null, ROCTypeId, objFolder.Id, "ClassROC"); Console.WriteLine("Created ClassROC object"); // Set the IP in the ROC objROC.SetPropertyValue("ConnectString1", "0.0.0.0:4000"); // Set the Device Type in the ROC objROC.SetPropertyValue("Series", "6"); // Create a Meter object in our folder asiObject objMeter = conn.ConfigurationService.CreateObject(null, MeterTypeId, objFolder.Id, "ClassMeter1"); Console.WriteLine("Created Meter object"); // Link the new meter to the ROC objROC.SetPropertyValue("Meter1", objMeter.Id); // Set the ROC Enabled objROC.SetPropertyValue("Enabled", 1); // Create a Port object in our folder asiObject objPort = conn.ConfigurationService.CreateObject(null, TCPIPId, objFolder.Id, "ClassPort1"); Console.WriteLine("Created Meter object"); // Link the new port to the ROC objROC.SetPropertyValue("ConnectionId1", objPort.Id); objROC.ApplyChanges(false); Console.WriteLine(endofline); } // Get Property Definitions and print results for ROC created in Test4 private static void Test4(ServerConnection conn) { Console.WriteLine(startofline(4)); // Getting an object's property definitions. asiObject objROC = conn.ConfigurationService.FindObjectByFullName("ClassROC", true); Console.WriteLine("Found ROC with object ID: " + objROC.Id); // Get Property Definitions to see what are valid values that can be assigned asiPropertyDefs pDefs = objROC.GetPropertyDefs(); // all property defs for (int i = 0; i < pDefs.Count; i++) { asiPropertyDef pd = pDefs[i]; // individual property def string sName = pd.Name; Console.WriteLine("Name of the Property is " + sName); if (pd.PropertyType == asiPropertyType.Enum) { if (sName == "Series") { for (int j = 0; j < pd.EnumValues.Count; j++) { Console.WriteLine("\t\tENUM VALUE " + pd.EnumValues[j].ToString() + " and value is " + pd.EnumValues[j].Value); } } } } Console.WriteLine(endofline); } // Use the control service to find and execute a command private static void Test5(ServerConnection conn) { Console.WriteLine(startofline(5)); // Sending commands to objects. asiObject deviceObj = conn.ConfigurationService.FindObjectByFullName("ClassROC", true); asiCommands commandsForObject = null; // find all available commands based on object commandsForObject = conn.ControlService.ListObjectCommands(deviceObj.Id); // Display available commands. asiCommand theCommandToExecute = null; for (int i = 0; i < commandsForObject.Count; i++) { asiCommand singleCommand = commandsForObject[i]; if (singleCommand.Name == "Retrieve History") { Console.WriteLine("Found the Command!"); theCommandToExecute = singleCommand; break; } } // Tell the server to execute the requested command. // Some commands have parameters and the nulls would be replaced with the parameters. // Retrieve History doesn't take any parameters, so we use null. conn.ControlService.ExecuteObjectCommand(deviceObj.Id, theCommandToExecute.Id, null, null); Console.WriteLine("Executed the Command!"); Console.WriteLine(endofline); } public static String startofline(int testnumber) { return "=== Test" + testnumber + " ======================================"; } public static String endofline = "================================================\n"; } }

For assistance, please submit a ticket via our Support Portal, email autosol.support@autosoln.com or call 281.286.6017 to speak to a support team member.