cropped-header_image

SCADA Alarm History - 2/2

In the [first post](https://francotiveron.wordpress.com/2017/09/02/47/) I wrote on how to send alarm events in real-time from a Citect SCADA application to a remote MSMQ queue. In this one I will sho

September 3, 2017

In the first post I wrote on how to send alarm events in real-time from a Citect SCADA application to a remote MSMQ queue. In this one I will show the destination part of the complete application. Messages are fetched from the queue, filtered according to business rules, formatted as per the destination SQL server table, and finally inserted.

Few notes on SQL

Structured Query Language is probably the first programming language created to manage RDBMS(Relational Database Management System)s. SQL is one of those languages that have been able to resist all the innovation winds that programming has been battered with in the history.

Mainstream RDBMS vendors have adopted and adapted it to their needs, hence there is not an universal standard. Microsoft version is called TSQL (Transact SQL).

Along the way, many tentatives have been rolled out to abstract the access to SQL Server so to give a common library platform to application developers: native libraries, ODBC, DAO, ADO, various ORMs, until LINQ, just to remain in the Microsoft realm. Yet, all those approaches did never intend to replace SQL, but were instead built on top of it. LINQ queries are translated to TSQL before being submitted to the database engine.

Does this mean that a solid database related application should always rely on SQL when it comes to interact do the database? Well, this question would open to infinite debate, and it's not what this post is all about, so I'll give my opinion:

SQL is the "assembly" RDBMS language. Applications should be written using the best available technology, but with the best possible knowledge on how that technology works.

No new computer application is today written entirely in machine language (or its human readable form, assembly), but knowing that your high level language program is going to be translated to the CPU machine language and even better knowing how your CPU and chip-set work gives an edge and ultimately leads to a better high level code. Similarly, few computer applications are today written in C and use the native operating system API, but knowing that your java, C#, python and whateveryouwant modern language if ultimately translated into direct OS calls and even better knowing how your OS API works does ultimately leads to a better high level code.

Eventually, these are the main reasons why the artifacts in this post do not use plain SQL, but SQL server .NET integration, F# and LINQ.

Overview

The application component in this post is essentially a F# .NET DLL, hosted in the SQL server engine (named DT in the previous post). The logic operates in an infinite loop as follows:

  • Read the next message in the MSMQ queue

  • Convert the message to a custom record type

  • Filter the message contents against validation rules

  • Insert messages in the database table if validated, discard them otherwise

Solution Workflow

The steps here below are to be performed in the destination SQL server. The complete application requires to execute the first post workflow as well, though.

F#.NET NCF.AlarmHistory.DLL

This DLL exports a function as SQL stored procedure. Later in the post I show how to install it.

The [] .NET attribute marks this function as stored procedure, the compiler will add the proper meta-data to make it recognizable by SQL server.

Now the main processLoop

The MSMQ queue is called NCFCitectAlarmHistory. It's a private queue in the target server. The message is expected to be a simple string, with fields separated by a tilde (~).

To be noted:

  • MessageQueue.Receive is used here, which is a blocking call. The thread will then sleep when no messages are available in the queue.

  • The infinite loop is implemented as a tail-recursive F# function.

  • The loop can be stopped by sending a plain "STOP" message to the queue (I normally use a trivial powershell command when i need); see GitHub for the actual code.

  • In case of error in the validation, an error message is logged in a different SQL table (full code in GitHub).

The message is then validated and converted to a record type value by the call to msg2record, that returns F# Result value, recently added to F# 4.1:

The coming message is expected to fulfill some business rules (see the when clause in the pattern matching code), otherwise an error is returned. If the conversion succeeds, the new record is returned and processed by the DB.manage function:

 

Important to note:

  • To ease the development at design time, the SQL Data Provider is used (see SqlDataConnection).

  • At run-time, though, the connection string is changed to the contextual ("context connection=true"); according to the documentation, this gives the better run-time performances, as the connection is managed internally by the SQL server engine.

  • To avoid duplicate alarm records (Citect servers can be restarted at any time and when they do the full cache is re-sent to the queue, so duplicate messages are in order), the target table is queried using LINQ and the handy F# query expression.

Deploy NCF.AlarmHistory.DLL to SQL Server

To run CLR code in SQL Server, the DLL assembly must be registered with SQL Server, along with all the possible dependencies. SQL Server 2014 has most of the .NET framework 4 registered already, but not all the dependencies needed for our DLL. Here the SQL script to be run (as you can see, knowing the "assembly" always comes useful):

 

The first part can be run only once if the dependencies do not change, while the second part (marked by /* Package (after any change) */) must be rerun every time our assembly is rebuilt.

Database Schema

Last but not least, here the SQL script to create the necessary database objects.

The Visual Studio project is available on GitHub. Visual Studio 2017 community has been used for this development.

Conclusion

This post ends the 2-posts series "SCADA Alarm History"; the application is running in production since about a month and proved to be stable. We are beginning to build excel add-ins and Power BI reports on top of it.

Full fledged solutions are available in the market to do the job, but:

  • they introduce additional capital licensing and operational maintenance costs

  • they can require a significant time and cost effort to be successfully integrated in existing infrastructures

  • They need anyway a customization work to be carried out to reflect the specific business requirements and customs

The solution proposed here addresses these points:

  • No licensing costs, as all the resources used are already available or free.

  • Being a fully owned application, maintenance can be managed internally, which usually has significantly lower costs.

  • Owning the source code gives the maximum flexibility, allowing to adapt it to the specific business infrastructure.

  • Customization is fully open as we have a business owned SQL database. Data can be put on spreadsheet seamlessly (without sometimes annoying export workflows) and reporting can be managed by what we want. I have serious doubts that any of the third-party packages have better reporting features than many open source or much cheaper commercial frameworks (like Power BI for example).

Related articles