CSWorks: web-based industrial automation

Of CSWorks and software development

CSWorks 2.5.4770.1 released

clock April 23, 2013 21:31 by author Sergey Sorokin

What's new:

  • Minor fixes in HTML5 client and setup


CSWorks 2.5.4770.0 released

clock February 6, 2013 13:25 by author Sergey Sorokin

What's new:

  • HTML5 support
  • RESTful LiveData web service

CSWorks LiveData client now runs on any platform that supports HTML5 browsing with SVG graphics and animations.



CSWorks 2.1.4560.0 released

clock September 1, 2012 09:49 by author Sergey Sorokin

What's new:

  • OPC provider: ignoreOpcQuality setting
  • BACnet IP provider: whoIsInterval, networkId, serverDeviceId, vendorId, keepAliveTimeout settings
  • DTO: template parameter strings now can be escaped to allow backslash, equal signs and semicolons in the parameter values
  • LiveData Service: no need to restart the service after changing data source configuration
  • LiveData Service: sampleBufferLength setting is optional now

 



CSWorks 2.1.4386.0 released

clock January 16, 2012 02:00 by author Sergey Sorokin

What's new:

  • SQL LiveData provider tested with PostgreSQL 9.0.4, npgsql provider 2.0.11.91
  • Server: Modbus TCP support
  • Client: Modbus thermostat integration demo

 



CSWorks 2.0.4115.0 released

clock May 31, 2011 01:00 by author Sergey Sorokin

What's new:

  • SQL LiveData provider: improved boolean data type support
  • Server: analogDeadband property for LiveData sources
  • Server: revised thread concurrency model in LiveData and Alarm servers (better data source error handling performance)
  • OPC LiveData provider: OPC group size limit
  • Server: BACnet IP support
  • Client: LiveData Manager - improved connectivity error handling
  • Client: BACnet Provider demo
  • Client: BACnet thermostat integration demo

 



CSWorks 1.7.4050.0 released

clock February 11, 2011 14:28 by author Sergey Sorokin

What's new:

  • Windows Phone 7 client

The following video shows CSWorks Windows Phone client in action:




CSWorks 1.4.4000.0 released

clock November 18, 2010 11:57 by author Sergey Sorokin

What's new:

  • 2-way remote alarm notification: email and SMS

The following video shows CSWorks SMS and email alarm notification in action:

  • a user triggers an alarm by closing all intake valves;
  • CSWorks Alarm Notification Service detects the active alarm and sends notification email (using SMTP server) and notification SMS (using GSM modem);
  • pipeline operator gets notificatons by email and SMS;
  • the operator sends acknowledgement SMS to the alarm notification server;
  • the alarm gets acknowledged;
  • the alarm goes inactive.

 



About resource hierarchy in web.config files

clock November 12, 2010 19:55 by author Sergey Sorokin


CSWorks stores resource hierarchy in web.config files, so:

  • LiveData Web Service can find LiveData Service instance that collects data from some specific data source;
  • Alarm Web Service can find Alarm Service that handles some specific alarm group;
  • History Reader Web Service can find database that holds historical data for a specific data point.


Let’s take History Reader Web Service for example. The following piece of web.config tells web service that data history for data point “7cc32718-0da0-42b3-ae55-0acbb3593671" (also known as "Tank1") is stored in the database “partition1”:

<historyTopology>
  <historyPartitions>

    <historyPartition name="partition1" primaryDbTarget="…" secondaryDbTarget="…">

      <areas>

        <area id="…" name="By Equipment/Location">

          <areas>

            <area id="…" name="Tanks">

              <areas>

                <area id="…" name="West Side">

                  <dataPoints>

                    <dataPoint id="{7cc32718-0da0-42b3-ae55-0acbb3593671}" description="Tank1"/>


One potential problem is that some solutions can record thousands of historical data points, and all of them must be referenced in a single web.config file, and by default, ASP.NET limits the size of web.config file by 256kb. Consider the following setup.


Run a script that generates 10,000 historical datapoint references (you can download it here) and insert them into <historyPartition name="partition1"…> node of the web.config of History Reader Web Service. Web.config now has size around 1mb (see screenshot below). Run any client application that uses this web service (CSWorks TrendDemo for example) and get a web service error that doesn’t leave any trace: no exceptions in ASP.Net worker process, no error in the Event Log.


Now tell ASP.NET that it should accept bigger web.config files. Create DWORD registry value HKLM\Software\Microsoft\InetStp\Configuration\MaxWebConfigFileSizeInKB and set its value to, say 1024kb (see screenshot below).


Now restart the client application and start accessing those 10,000 historical data points. The screenshot below shows:

  • registry editor with MaxWebConfigFileSizeInKB value;
  • web.config file size;
  • a fragment of web.config that references historical data points that belong to area “#0199”;
  • a fragment of CSWorks Trend Control that allows to choose any historical data point from area “#0199” and add it to the chart.


Click to enlarge


There are few other potential problems with large amount of data in web.config files. First, Silverlight Tree Control has problems with displaying more than a few thousand nodes simultaneously. Second, CSWorks History Reader Web Service handles data point hierarchy more efficiently if every area doesn’t contain more than a few hundred data points.


The recommendation is obvious: when planning historical data point areas hierarchy, do not put more than a few hundred data points in a single area, avoid “plain” hierarchies with thousands of data points in the same area. The script used in this demo (you can download it here) generates 200 areas each containing 50 historical data points.


This discussion also applies to LiveData and Alarm web services, although the problem of large web.config files is not that relevant for live data and alarming: only extremely large CSWorks solutions use more than thousand data sources and more than thousand alarm groups.



CSWorks user authorization using third-party access management system - Lab 04

clock October 22, 2010 13:38 by author Sergey Sorokin

Introduction

In CSWorks 1.4.3900.0 we have added a possibility to restrict user access to specific data sources, alarm groups and historical data points. Inquiring users will find compelling answers awaiting them in CSWorks documentation that explains new security model in detail. In this article, I will focus on CSWorks integration with a third-party access rights management system. For the purpose of demonstration, I will be using SecureAccess 4.2 by PortSight - .NET security component for user management and application access rights management. This product meets all of the requirements to be used as an underlying access management system:

  • it can be accessed from .NET applications
  • it allows custom privileges like LiveData Read, LiveData Write etc.
  • it allows storing of resource identifiers: strings (data sources) and guids (alarm groups, historical data points)
  • it allows retrieval of all resource identifiers of a specific type (data sources, alarm groups, historical data points) available to a specific user for specific action (read, write, acknowledge)

I will walk through the process of creating a custom authorization provider for CSWorks and using it with Secure Demo application that comes as part of the CSWorks setup.

Setting up SecureAccess

Here are the steps:

  • install SecureAccess on a server computer;
  • provide it with database server access and create a new database using SecureAccess Catalog Manager, call this database "SecureAccess";
  • create the following application hierarchy: Applications->AlarmSecurity->Alarm Groups->Pipeline alarms;
  • create a role "PipelineAdmins", create a user "JohnDoe" and add him to the role;

The following screenshot of the SecureAccess administration panel shows what has been done so far:

Roles

Now make sure that Description field of the application part "PipelineAlarms" contains the id of the alarm group "All Pipes" used by CSWorks Alarm Service and defined in Alarms.xml:

...
<alarmGroup id="{4709B095-BBB6-4e48-97B8-AF15C5F19DD6}" description="All Pipes">
...

The following screenshot displays "PipelineAlarms" details:

Parts

At the level of "AlarmSecurity" application, create custom permission types ReadAlarms and AckAlarms and make sure they are available in "PipelineAlarms" application part:

Permission Types

Now the most important thing: on "PipelineAlarms" application part level, give ReadAlarms and AckAlarms permissions "PipelineAdmins" role. Permission matrix for "PipelineAlarms" will look as follows:

Permission Matrix

Now SecureAccess is ready to perform authorization for users who want to access CSWorks alarms in "All Pipes" alarm group.

Developing authorization provider

Using Microsoft Visual Studio, create a new .NET library CSWorks.Server.SecureAccessAuthorizationProvider.dll that references CSWorks.Server.WebSecurity.dll (it comes as part of the CSWorks setup) and the following SecureAccess libraries:

  • ARDataServices.dll
  • ARObjects.dll
  • ARWebSecurity.dll
  • SecureAccess.dll

Create a class SecureAccessAuthorizationProvider that implements IAuthorizationProvider interface defined in CSWorks.Server.WebSecurity:

using System;
using System.Collections.Generic;
using System.Linq;
using PortSight.SecureAccess.ARObjects;
using CSWorks.Server.Diagnostics;

namespace CSWorks.Server.WebSecurity
{
  public class SecureAccessAuthorizationProvider: IAuthorizationProvider
  {
    public void Open(string connectionString, string userName)
    {
      // Nothing to do
    }

    public void Close()
    {
      // Nothing to do
    }

    public IEnumerable GetPrivilegeGuids(string userName, Privilege privilege)
    {
      List ids = new List();
      if (privilege == Privilege.AlarmRead || privilege == Privilege.AlarmAck)
      {
        IEnumerable alarmGroupAppParts = ARHelper.GetARObjects(ARObjectTypesEnum.ARApplicationPart).OfType().Where(ap => ap.ObjectAlias.StartsWith("AlarmSecurity.AlarmGroups.", StringComparison.InvariantCultureIgnoreCase));
        string secureAccessPrivilegeName = (privilege == Privilege.AlarmRead ? "ReadAlarms" : "AckAlarms");

        // Build a list of all group ids this user is authorized to READ or ACK
        foreach (ARApplicationPart ap in alarmGroupAppParts)
        {
          if (ARHelper.IsAuthorized(userName, ap.ObjectAlias, secureAccessPrivilegeName))
          {
            // Take group id from the app part description
            ids.Add(new Guid(ap.ObjectDescription));
          }
        }
      }
      else
      {
        // Report error: privilege not supported
      }

      return ids;
    }

    public IEnumerable GetPrivilegeStrings(string userName, Privilege privilege)
    {
      throw new NotImplementedException();
    }
  }
}

The idea is very straightforward: when given a user name and alarm privilege, GetPrivilegeGuids() method should return all alarm group ids this user has correspondent access to. GetARObjects() returns all application parts under "AlarmGroups", and IsAuthorized() method filters out those alarm groups that are not accessible.

Using authorization provider with your application

Copy CSWorks.Server.SecureAccessAuthorizationProvider.dll together with all SecureAccess dependencies to the bin folder of SecurityDemo application (CSWorks\Demo\Web\bin) so secure CSWorks web services can access it. Add the following lines to the web.config of SecureAlarmWebService located at CSWorks\Demo\Web\SecureAlarmWebService (make sure you have specified proper SQL Server name or address, SQL Server user name and password, and a valid SecureAccess license key):

<configuration>
  <configSections>
    <sectionGroup name="authorizationProviderConfig">
      <section name="authorizationProviders" type="CSWorks.Server.WebSecurity.AuthorizationProviderConfigurationSection, CSWorks.Server.WebSecurity" />
    </sectionGroup>
    ...
  </configSections>
  <appSettings>
    <add key="SecureAccessConnectionString" value="data source=sqlserver;initial catalog=SecureAccess;user id=sqluser;password=sqlpassword;packet size=4096" />
    <add key="SecureAccessDefaultCulture" value="en-US" />
    <add key="SecureAccessParentFrameName" value="" />
    <add key="SecureAccessLicenseKey" value="XXXX-XXXX-XXXX-XXXX-XXXX" />
    <add key="SecureAccessVirtualPath" value="" />
    <add key="SecureAccessTopRecords" value="1000" />
    <add key="SecureAccessCacheExpiration" value="30" />
    <add key="SecureAccessApplicationAlias" value="SecureAccess" />
    <add key="SecureAccessLogonFormRedirectsTo" value="" />
  </appSettings>
  <authorizationProviderConfig>
    <authorizationProviders activeAuthorizationProvider="secureAccessAuthorizationProvider">
      ...
      <authorizationProvider name="secureAccessAuthorizationProvider" type="CSWorks.Server.WebSecurity.SecureAccessAuthorizationProvider, CSWorks.Server.SecureAccessAuthorizationProvider" connectionString="" allowCaching="true"/>
    </authorizationProviders>
  </authorizationProviderConfig>
  ...
</configuration>

SecureAlarmWebService is ready to use the provider you have just crafted. Now modify SecurityDemo.aspx.cs source file of the SecurityDemo sample application by replacing "demooperator@acme.com" with "johndoe" in the AllowAlarmPipes_Click() method as follows:

namespace CSWorks.Server.SecurityDemoWebApplication.WebApplication
{
  public partial class SecurityDemo : System.Web.UI.Page
  {
    ...

    protected void AllowAlarmPipes_Click(object sender, EventArgs e)
    {
      //System.Web.Security.FormsAuthentication.SetAuthCookie("demooperator@acme.com", true);
      System.Web.Security.FormsAuthentication.SetAuthCookie("johndoe", true);
      Response.Redirect("SecurityDemoAlarm.aspx");
    }

    ...
  }
}

From now on, when a user clicks the "Allow 'All Pipes' alarm group only (no acks)" he/she is impersonated as "John Doe" and gets read/ack access to all alarms in the group "All Pipes". All other alarm groups are not available to this user.

Running the demo

Build updated SecurityDemo application, run it and click the "Allow 'All Pipes' alarm group only (no acks)" button. Make sure that "JohnDoe" user name appears in the message on top of the page and you have read/ack access only to those alarms that belong to "All Pipes" alarm group. You can do that by running "Pipes and tanks" demo application and closing all valves that feed the mixing tank: this eventually will trigger an alarm called "Both lines are empty" in "All Pipes" group, and you will be able to see it and acknowledge it from the Security Demo.

The discussed example deals with CSWorks alarming only, but you can easily do the same for LiveData sources (use data source name for SecureAccess application part description, and implement provider's GetPrivilegeStrings() method) and historical data points (use data point id for SecureAccess application part description, and add some code to GetPrivilegeGuids() method).



CSWorks 1.4.3900.0 released

clock October 20, 2010 21:53 by author Sergey Sorokin

What's new:

  • Security: item-level authorization, XmlAuthorizationProvider, using User.Identity
  • Setup: better component granularity, friendly ASP.NET/IIS dialogs
  • Security Demo: now uses LiveData, Alarm and Historical Data