# Thursday, May 06, 2010
What's new:
  • History Reader web service: historical data point areas
  • Alarm web service: alarm areas
  • Trend Control: color picker
  • Trend Control: load/save pen configuration
  • Trend Control: line/bar/HLC pens, cursor and tooltips
  • Alarm Summary: edit/load/save subscription configuration
  • Alarm Summary: ack/shelve buttons and hot keys
  • Alarm Summary: sort on-the-fly setting
  • LiveData items and live pens: ScaleFactor attribute
  • Pipes And Tanks Demo: customizable trend control
  • Alarm Demo: passing alarm events to subscribers

Sergey Sorokin   Thursday, May 06, 2010 2:54:50 PM (Pacific Daylight Time, UTC-07:00)  #     |  Comments [0]  | 
# Tuesday, April 20, 2010

In the latest release of CSWorks, we have improved Historical Data Server performance. Let's see what History Recorder is capable of now.

Environment

CSWorks 1.2.3730.0
Server: Intel Core 2 Quad Q6600 @ 2.40GHz, 4 GB RAM, Windows 7

Set-up

1. Install SQL Server 2008 Express on your server machine.

2. Create database "CSWorks"

3. Create HistoricalData table - see "createCommand" parameter of in CSWorks.Server.HistoryRecorderService.exe.config.

4. Configure SQL Server data source and make it active in CSWorks.Server.HistoryRecorderService.exe.config:

<dbTargetConfig>
  <dbTargets activeDbTarget="Standard SQLServer DbTarget">
    <dbTarget name="Standard SQLite DbTarget" ...
      ...
    />
    <dbTarget name="Standard SQLServer DbTarget"
      providerInvariantName="System.Data.SqlClient"
      connectionString="Data Source=localhost\sqlexpress; Initial Catalog=CSWorks;user id=sa;password=...;"
      createCommand="..."
      writeCommand="..."
      maintenanceCommand="delete top (300000) from HistoricalData ..."
      maxObservationAge="600"
      writeInterval="1"
      maintenanceInterval="30"
      maxQueryLength="65535"
      queryDelimiter=";"
      writeTxnBeginCommand="..."
      writeTxnCommitCommand="..."
    />
  </dbTargets>
</dbTargetConfig>

Please note we tell History Recorder to keep alarm event records in the database for 10 minutes only, and we perform record cleaning every 30 seconds. This only because our SQL Server is not capable of taking heavy load.

5. Restart History Recorder service. Make sure events are now written to CSWorks database (run "select * from HistoricalData" to confirm it).

6. Using cscript tool, run a script that generates 2000 historical data points:

function main()
{
  var itemCount = 2000;

  WScript.Echo("RecorderDataPoints.xml");
  for (i = 0; i < itemCount; i++)
  {
    var id = i.toString();
    while(id.length < 4)
    {
      id = "0" + id;
    }
    WScript.Echo("");
  }
}

main();

7. Copy generated historical data point descriptions to RecorderDataPoints.xml:

<dataPoints>
  ...
  <dataPoint id='{00000000-0000-0000-0000-000000000000}' description='Tank 1 fill - 0000' expression='tank1-0000'/>
  ...
  <dataPoint id='{00000000-0000-0000-0000-000000001999}' description='Tank 1 fill - 1999' expression='tank1-1999'/>
</dataPoints>

History Recorder will pick up the change in a couple of seconds and will start saving observation for those 2000 data points to the database. Give our setup some time to stabilize.

Results

After 10 minutes, History Recorder maintains about 3.5 million observation records in the database and writes about 6000 observation records every second on average. Database file size is between 1 and 1.5 gigabytes. Here is a screenshot with Performance Monitor and DbgView windows:

Since our test live data changes in a very predictable way, there is a clear pattern in observation recording on the top perfmon chart. History Recorder memory consumption is under control too. Tracing shows that History Recorder deletes between 120000 and 270000 "obsolete" observations every 30 seconds. As you may have noticed, the maximum number of record it is allowed to delete in one shot is 300K, see 'maintenanceCommand' parameter above. Our setup is properly balanced, so History Recorder does not reach this limit.

If you add more historical datapoints and make total count, say 5000, you may end up in a situation when History Recorder simply cannot write all collected observations in a timely manner, and they will accumulate in the memory buffer. Major symptom will be growing memory consumption by History Recorder. CSWorks 1.2.3800.0 introduces "Write Buffer Size" performance counter that shows current number observations to be written to the database by HistoryRecorder, so this overload scenario becomes more obvious.

Summary

Please plan your historical data management carefully. Use scalable database engine, and give it a lot of spare CPU resources. Use multiple History Recorder machines if needed. If the amount of data is extremely big, use multiple databases and apply partitioning technique described in CSWorks documentation.

Sergey Sorokin   Tuesday, April 20, 2010 10:09:46 AM (Pacific Daylight Time, UTC-07:00)  #     |  Comments [0]  | 
# Monday, April 19, 2010

Resizable controls - what's the problem?

To save HMI screen space, application developers may want to make some controls resizable. Applying ScaleTransform is not a problem and is a good solution in many cases. See Pipes and Tanks Demo for example - it uses ScaleTransform and redraws screen content when a user changes browser window size.

Unfortunately, ScaleTransform is not an optimal solution when dealing with complex UI elements, especially those containing some amount of text - some pieces may look ugly, some may become too small to be useful. The only solution is to make complex controls able to react to the 'SizeChanged' event and recalculate internal layout at runtime. Latest version of Alarm Summary and Trend Control can do that, and this post shows how application developers can use this functionality.

We will need a container control that lets user change its size, and a piece of code that passes the changes to CSWorks controls. You can develop your own container control (there are a lot of examples available in the web) or use an existing third-party container control for that. I will use RadWindow from Telerik in this example - it serves the purpose quite well.

Resizable Alarm Demo

1. Download Rad Controls for Silverlight trial package from www.telerik.com (DLLs only).
2. Unzip downloaded archive to "C:\Program Files\CSWorks\Demo\Src\tps\Telerik". Telerik DLLs will be unpacked to "C:\Program Files\CSWorks\Demo\Src\tps\Telerik\Binaries\Silverlight\".
3. Open Alarm Demo project ("C:\Program Files\CSWorks\Demo\Src\AlarmDemo\") in Visual Studio.
4. We will use RadWindow control implemented in Telerik.Windows.Controls.Navigation.dll. Add Telerik.Windows.Controls.Navigation.dll and Telerik.Windows.Controls.dll to the reference list for Alarm Demo project.
5. Open Page.xaml file. Add 'telerik' namespace to the UserControl element:

<UserControl ...
  xmlns:telerik="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Navigation"
... >


6. Replace control content with a grid that contains RawWindow that contains CSWorks Alarm Summary, and add SizeChanged event handler for RadWindow:

<Grid>
  <telerik:RadWindow x:Name="radWindow" SizeChanged="OnSizeChanged">
    <telerik:RadWindow.Header>
      <TextBlock Text="Alarm Demo"/>
    </telerik:RadWindow.Header>
    <alarm:AlarmSummary ... />
  </telerik:RadWindow>
</Grid>


7. Open Page.xaml.cs file, implement SizeChanged event handler that adjusts AlarmSummary height and width when RadWindow size is changed: 

private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
  GeneralTransform gtGlobal = alarmSummary.TransformToVisual(radWindow);
  Point leftTopGlobal = gtGlobal.Transform(new Point(0, 0));

  alarmSummary.Height = radWindow.Height - radWindow.BorderThickness.Bottom - leftTopGlobal.Y - 4.0;
  alarmSummary.Width = radWindow.Width - leftTopGlobal.X * 2;
}


8. Update Loaded event handler so RadWindow shows itself after page is loaded:

private void Page_Loaded(object sender, RoutedEventArgs e)
{
  ...
  radWindow.Show();
}


9. Rebuild the project and run Alarm Demo from Start Menu. Resize RadWindow and watch Alarm Summary changing its layout:

Resizable Trend

You can do the same to CSWorks Trend control.

1. Open Trend Demo project ("C:\Program Files\CSWorks\Demo\Src\TrendDemo\TrendDemo.Sample.csproj")
2. Add Telerik.Windows.Controls.Navigation.dll and Telerik.Windows.Controls.dll to the reference list.
3. Open MainPage.xaml file. Add 'telerik' namespace as shown above.
4. Change xaml so RadWindow contains TrendControl:

<Grid>
  <telerik:RadWindow x:Name="radWindow" SizeChanged="OnSizeChanged">
    <telerik:RadWindow.Header>
      <TextBlock Text="Trend Demo"/>
    </telerik:RadWindow.Header>
    <t:Trend ...>
      ...
    </t:Trend>
  </telerik:RadWindow>
</Grid>


5. Open MainPage.xanl.cs. Add OnSizeChanged handler:

private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
  GeneralTransform gtGlobal = _demoTrend.TransformToVisual(radWindow);
  Point leftTopGlobal = gtGlobal.Transform(new Point(0, 0));

  _demoTrend.Height = radWindow.Height - radWindow.BorderThickness.Bottom - leftTopGlobal.Y - 4.0;
  _demoTrend.Width = radWindow.Width - leftTopGlobal.X * 2;
}


6. Show RadWindow on startup:

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
  ...
  radWindow.Show();
}


7. Rebuild the project and run Trend Demo from Start Menu. Resize RadWindow and watch Trend control changing its layout: 


Sergey Sorokin   Monday, April 19, 2010 2:11:23 PM (Pacific Daylight Time, UTC-07:00)  #     |  Comments [0]  | 
# Tuesday, April 13, 2010

From now on, Alarm Summary and Trend Control can have localized menus, buttons and messages. Norwegian happened to be the first non-English language - thanks to our partner company in Norway.

To set Silverlight application culture explicitly, you can create a 'localized' html file (say, 'MyAlarmSummary0234.no.html')that hosts your application and add 'culture' and 'UIculture' parameters to the Silverlight object. The following piece of Javascript code shows how to instantiate a Norwegian Silverlight application:

Silverlight.createObject("....xap",parentElement,"myPlugin", {width:'100%', height:'100%', ..., culture:'no', UIculture:'no'},{onError:null, onLoad:null }, null, null);

And here is what Alarm Summary looks like:

Update:

Starting from 1.4.3850, there is no more need to edit demo html files to run CSWorks demo application using specific cultures. Use "culture" URL parameter as described in this post.

Sergey Sorokin   Tuesday, April 13, 2010 1:45:38 PM (Pacific Daylight Time, UTC-07:00)  #     |  Comments [0]  | 

What's new:

  • Downsampling performed by History Recorder, Downsampler Service is gone
  • Changes in web service security model
  • Documentation changes
  • Trend control: added right scale
  • Trend control: chart configuration load/save
  • Localized Alarm Summary and Trend Control
  • Signed setup package

Sergey Sorokin   Tuesday, April 13, 2010 1:25:19 PM (Pacific Daylight Time, UTC-07:00)  #     |  Comments [0]  | 
# Wednesday, March 17, 2010

When CSWorks is not working as expected the first thing to do is to see trace messages using a tool of your choice. I use DebugView by www.systeminternals.com. You can download the tool for free as part of their SysInternals Suite or as a standalone tool here.

No installation needed, just run dbgview.exe. Please keep in mind that Vista, Server 2008 and Windows 7 users should run this tool with elevated privileges, otherwise it won't be able to catch trace messages. When prompted for a filter, enter "CSWorks":



Make sure you have checked "Capture Global Win32" and "Capture Events" items of "Capture" menu:



Now you are ready to capture CSWorks trace messages. In most cases, this is all you have to do in order to find a problem and take action. The following screenshot shows the case when LiveData Service is denied access to an OPC server:



- when you see this, you may want to run dcomcnfg utility and adjust OPC server permission or run service manager and change the account LiveData Service runs under.

By default, all CSWorks server components send only error and warning messages, as displayed on the screenshot above. Sometimes this is not enough and you may want to see more details. Then you should increase tracing verbosity level of specific component(s).

For instance, if you need detailed tracing for LiveData Service, edit CSWorks.Server.LiveDataService.exe.config - set tracing level to "Info" or "Verbose":

  <system.diagnostics>
    <switches>
      <add name="GeneralTraceSwitch" value="Verbose"/>
    </switches>
  </system.diagnostics>

After you restart LiveData Service, you will start getting much more output in DebugView window.

If you change tracing level for a web service, edit "GeneralTraceSwitch" value in correspondent web.config file. After you save updated web.config, changes will take effect immediately.

Sergey Sorokin   Wednesday, March 17, 2010 12:46:36 PM (Pacific Standard Time, UTC-08:00)  #     |  Comments [0]  | 
# Monday, March 15, 2010

I am always curious about my system performance. In this lab I will give CSWorks Alarming subsystem a hard time and see how it can handle it.

Environment

CSWorks 1.1.3700.0
Server: Intel Core 2 Quad Q6600 @ 2.40GHz, 3.25 GB RAM, Windows XP SP3
Client: Intel Core 2 Duo T5300 @ 1.73GHz (notebook), 2 GB RAM, Windows XP SP3
Network: Wireless 54 Mbps

Set-up

1. Install SQL Server 2008 Express on your server machine.

2. Create database "CSWorks"

3. Create AlarmEvents table - see "createCommand" parameter of <dbTarget name="Standard SQLServer DbTarget".../> in CSWorks.Server.AlarmService.exe.config.

4. Configure SQL Server data source in CSWorks.Server.AlarmService.exe.config:

<dbTargets activeDbTarget="Standard SQLServer DbTarget">
  <dbTarget name="Standard SQLServer DbTarget"  ...  connectionString="Data Source=myserverbox\SQLEXPRESS; Initial Catalog=CSWorks;user id=sa;password=youknowit" ...  maxEventAge="3600" .../>


Please note we tell CSWorks Alarm Service to keep alarm event records in the database for an hour so we can accumulate some amount of load data.

5. Restart CSWorks Alarm Service. Make sure events are now written to CSWorks database (run "select * from AlarmEvents" to confirm it).

6. Configure Silverlight web services URL for Alarm Demo application, so it can run from remote machine - make sure that ServiceReferences.ClientConfig in

C:\Program Files\CSWorks\Demo\Web\ClientBin\CSWorks.Client.AlarmDemo.xap

says

<endpoint address="http://myserverbox/CSWorksDemo/AlarmWebService/Service.asmx" ...>

If you need more details, see this post.

7. Run Alarm Demo from from remote client and make sure it works properly.

8. Create a script that generates 3000 alarm descriptions in 10 alarm groups (download it here),  run "cscript genalarms.js > alarms.txt", copy generated alarm groups to Alarms.xml, alarmGroups element. Here is an excerpt:

<alarmGroup id='00000000-AAAA-0000-0000-000000000000' description='Alarm group #0000'>
  <alarms>
    <alarm id='{00000000-0000-0000-0000-000000000000}' description='Total intake tank load, id 00000000' expression='tank1 + tank2 + tank3 + tank4' saveHistory='true' ... >
      <thresholds>
        <threshold ... message='Total intake tank load too high: {0:#,##0.00}, exceeds {1:#,##0.00}, id 00000000' ... />

        ...

    <alarm id='{00000000-0000-0000-0000-000000090299}' description='Total intake tank load, id 00090299' expression='tank1 + tank2 + tank3 + tank4' saveHistory='true' ... >
      <thresholds>
        <threshold ... message='Total intake tank load too high: {0:#,##0.00}, exceeds {1:#,##0.00}, id 00090299' ... />

        ...

      </thresholds>
    </alarm>
  </alarms>
</alarmGroup>

Give CSWorks Alarm Service a couple of seconds to digest the changes (this piece doesn't scale of course, so only one server CPU is involved).

9. Add newly created alarm group ids to AlarmWebService web.config file:

<alarmGroups>
  ...
  <alarmGroup alarmGroupId='
00000000-AAAA-0000-0000-000000000000' description='Alarm group #0000'/>
  ...
</alarmGroups>

10. Subscribe to newly created alarm groups in Alarm Demo application, either using "Subscribe..." menu command, or in XAML:

<alarm:AlarmSummary ... AlarmGroupIds="...;00000000-AAAA-0000-0000-000000000000;..." />

11. After starting Alarm Demo on your client machine you will have 12000 new alarm events and considerable spikes in network usage:



Let It Brew

Go grab a coffee and have a chat with your colleagues while CSWorks Alarm Service generates some alarm events.

Analyze This

Let's look at the alarm activity: System Monitor -> CSWorksAlarmService performance object -> Alarm Events per second:



~730 alarm events per second on average with spikes up to 9000 events per second. This correlates with our setup, remember: an alarm becoming inactive is an event too, and we have 4 thresholds per alarm. So, each spike is basically a burst of 3000*6=18000 events generated in 5 seconds. Now let's look at the CPU usage on the server box:



- not bad, the load is distributed properly. Let's make sure that all those events actually ended up in the database:



- good, those 700 000 events definitely did. Now back to the client.

With filtering turned off, the client seems to handle the spikes pretty well: Alarm Summary items are updated with 1-3 seconds delays. Things are getting worse if you apply a filter (say, show only active alarms): delay bumps up to 15 seconds. And of course, one of the processors on the client box is overloaded by filtering and data presentation activities. And it's not easy to scale them out: Silverlight dispatcher is single-threaded. Ok, there is some space for improvement on the client side.

And a word about network traffic for the inquiring minds. Fiddler tool tells all the truth and it ain't that ugly:



- each 2.3 MB per spike translates into ~130 bytes per event. Keep in mind that every event carries a unique text message. Not bad.

Back to Normal

Restore Alarms.xml - you have made a copy of the original file before inserting 3000 alarm descriptions, didn't you?  Again, let CSWorks Alarm Service digest the changes and in a couple of seconds, alarm summary is almost clear, showing only a couple of events. The storm is over.

Summary

Server load is scaled properly, stable and predictable. Network traffic is predictable too, those tuning freaks out there can gain some bandwidth using HTTP compression (yes, Silverlight supports it). Database performance doesn't seem to be an issue at all. The only bottleneck in this scenario is client CPU consumption when processing bursts of 18000 events. So, the recommendation is: if your operator needs to keep an eye on 18000 events fired in five seconds, buy him/her a better client box. 

Sergey Sorokin   Monday, March 15, 2010 10:13:25 PM (Pacific Standard Time, UTC-08:00)  #     |  Comments [0]  | 

CSWorks client framework is all about third-party UI components. As of this writing, all major Silverlight component providers have come up with their versions of the Gauge control. In this example, I will show how you can integrate Telerik and/or ComponentOne gauge controls into CSWorks Pipes and Tanks Demo application. The result will look like this:



Here are the steps to make it work.

1. Download Telerik Silverlight controls (dlls only) and ComponentOne Studio Trial Package for Silverlight.
2. Create a folder for third-party controls - C:\Program Files\CSWorks\ThirdParty.
3. Unpack downloaded Telerik archive and install ComponentOne package.
4. Copy Telerik assemblies (Telerik.Windows.Controls.Gauge.dll, Telerik.Windows.Controls.dll, Telerik.Windows.Data.dll) and ComponentOne assemblies (C1.Silverlight.dll, C1.Silverlight.Gauge.dll) to ThirdParty folder.
5. Open C:\Program Files\CSWorks\Demo\Src\PipesAndTanksDemo\PipesAndTanksDemo.Sample.csproj in Visual Studio.
6. Add all copied third party assemblies to the project reference list.
7. Open Page.xaml and add third-party namespaces to the namespace list:

<UserControl x:Class="CSWorks.Client.PipesAndTanksDemo.Page"
  ...
  xmlns:tcontrol="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Gauge"
  xmlns:tgauge="clr-namespace:Telerik.Windows.Controls.Gauges;assembly=Telerik.Windows.Controls.Gauge"
  xmlns:c1gauge="clr-namespace:C1.Silverlight.Gauge;assembly=C1.Silverlight.Gauge"

  ...>


8. Add gauge controls to the page and bind them to LeftPumpSpeed and RightPumpSpeed data items:

<controls:TabItem Header="HMI Controls View">
    <Canvas>
        ...
        <Grid ...>
            ...
            <Border ...>
              ...
            </Border>

            <!-- Telerik Gauge for left pump -->
            <Border Grid.Row="1" Grid.Column="5" Grid.RowSpan="1" Grid.ColumnSpan="3" Background="White" BorderBrush="Black" BorderThickness="1" CornerRadius="5" Padding="5,0,5,0" Margin="0,10,0,-10">
                <StackPanel Orientation="Vertical">
                    <TextBlock Text="Telerik Gauge" Foreground="Black" FontSize="8" HorizontalAlignment="Center"/>
                    <tcontrol:RadGauge x:Name="radGauge" VerticalAlignment="Bottom" >
                        <tgauge:RadialGauge  >
                            <tgauge:RadialScale Min="0" Max="200" LabelRotationMode="None" FontSize="7">
                                <tgauge:RadialScale.Label>
                                    <tgauge:LabelProperties FontSize="4.5"/>
                                </tgauge:RadialScale.Label>
                                <tgauge:RangeList>
                                    <tgauge:RadialRange Min="0" Max="100" StartWidth="0.05" EndWidth="0.05" Background="Green"/>
                                    <tgauge:RadialRange Min="100" Max="160" StartWidth="0.05" EndWidth="0.05" Background="Orange"/>
                                    <tgauge:RadialRange Min="160" Max="200" StartWidth="0.05" EndWidth="0.05" Background="Red" />
                                </tgauge:RangeList>
                                <tgauge:IndicatorList>
                                    <tgauge:Needle Value="{Binding Value, Mode=OneWay, Source={StaticResource LeftPumpSpeed}}"/>
                                </tgauge:IndicatorList>
                            </tgauge:RadialScale>
                        </tgauge:RadialGauge>
                    </tcontrol:RadGauge>
                    <TextBlock Text="rpm" Foreground="White" FontSize="7" HorizontalAlignment="Center" VerticalAlignment="Bottom"  Margin="0,-30,0,30"/>
                </StackPanel>
            </Border>

            <!-- ComponentOne Gauge for right pump -->
            <Border Grid.Row="1" Grid.Column="9" Grid.RowSpan="1" Grid.ColumnSpan="3" Background="White" BorderBrush="Black" BorderThickness="1" CornerRadius="5" Padding="5,0,5,0" Margin="0,10,0,-10">
                <StackPanel Orientation="Vertical">
                    <TextBlock Text="C1 Gauge" Foreground="Black" FontSize="8" HorizontalAlignment="Center"/>
                    <c1gauge:C1RadialGauge  Minimum="0" Maximum="200" Value="{Binding Value, Mode=OneWay, Source={StaticResource RightPumpSpeed}}">
                        <c1gauge:C1GaugeLabel Interval="20" Foreground="Black" Format="#" FontSize="6" FontWeight="200" Location="0.85"/>
                        <c1gauge:C1GaugeRange From="0" To="100"  Fill="Green" Location="0.45"/>
                        <c1gauge:C1GaugeRange From="100" To="160"  Fill="Orange" Location="0.45"/>
                        <c1gauge:C1GaugeRange From="160" To="200"  Fill="Red" Location="0.45"/>
                        <c1gauge:C1GaugeMark Interval="20" />
                    </c1gauge:C1RadialGauge>
                    <TextBlock Text="rpm" Foreground="Black" FontSize="7" HorizontalAlignment="Center" VerticalAlignment="Bottom"  Margin="0,-30,0,30"/>
                </StackPanel>
            </Border>


            <TextBlock Text="All UI elements ..." .../>
          </Grid>
          ...
    </Canvas>
</controls:TabItem>


9. Build the project and start Pipes and Tanks Demo from your start menu. Enjoy professional-looking controls in action.

Sergey Sorokin   Monday, March 15, 2010 9:47:19 AM (Pacific Standard Time, UTC-08:00)  #     |  Comments [1]  | 
# Tuesday, March 09, 2010

Symptoms

A few customers were getting the following message when running CSWorks installation:

Failed while processing WebVirtualDirs. (-2147024894 ) (see screenshot)

Setup log file contains the following entry:

ConfigureIIsExec:  Error 0x80070002: Failed to find Web base

Why does this happen?

CSWorks installer installs demo application to IIS (Internet Information Services) website with following properties:

  • IP address: all unassigned;
  • Port: 80;
  • Header: <empty>

These are the default settings for “Default Web Site” created by IIS installer, and they usually remain unchanged unless system administrator edits them manually. CSWorks installer assumes there is a web site with those properties running on this machine, otherwise it throws the error above.

A similar issue is discussed with WiX installer developers at http://n2.nabble.com/WriteMetabaseChanges-Error-0x80070057-failed-to-create-web-application-td3190173.html, here is an excerpt:>

“Make sure the IP, Port and Header all exactly match the website on the machine. These are the 3 things we use to determine if we've found the right website or not.”

Is it possible to improve CSWorks installer so it can handle more complex scenarios? We probably can do that, but:

  • it will increase installer complexity and decrease its reliability;
  • installer will have to make some changes in demo applications’ settings on-the-fly which is a risky thing.

We may consider adding support for those complex scenarios, but this is not a top priority at the moment. We are sorry about the inconvenience caused and we appreciate your understanding in this regard.

Workaround

Before installing CSWorks, make sure that “Default Web Site” is configured to run on port 80 for all unassigned IP addresses, and host header name for this configuration is empty, as shown at this screenshot (Windows XP). When done, run CSWorks installer and make sure CSWorks demo applications work properly. All demo applications are configured to run from http://localhost/CSWorksDemo, and they won’t run if you change port or header for the web site that hosts CSWorksDemo virtual folder.

Sergey Sorokin   Tuesday, March 09, 2010 10:02:44 PM (Pacific Standard Time, UTC-08:00)  #     |  Comments [0]  | 
# Monday, March 08, 2010

After installing CSWorks, many of you are tempted to connect it to your OPC data source. Here is a quick step-by-step guide how you can do that in five minutes.

In this example, I will assume we are dealing with Matrikon OPC Simulation Server - you can download it for free from Matrikon website. After installing Matrikon components, you can start tweaking CSWorks Pipes and Tank Demo so it uses data from Matrikon OPC Server.

Add new data source to LiveData Service configuration

Add a few lines to C:\Program Files\CSWorks\Framework\Server\CSWorks.Server.LiveDataService.exe.config:

    <opcLiveDataSources type="CSWorks.Server.DataSource.Opc.OpcLiveDataSource, CSWorks.Server.OpcProvider">
      ...
      <opcLiveDataSource name="OpcMatrikon" sampleBufferLength="16" hostName="localhost" progId="Matrikon.OPC.Simulation.1" subscriptionUpdateRate="250">
        <templates>
          <template name="matrikonBoolTag" type="Boolean" readPath="Random.Boolean" canWrite="true"/>
        </templates>
      </opcLiveDataSource>

    </opcLiveDataSources>


Restart LiveData Service so your changes take effect and the service connects to the data source.

Add new data source to LiveData Server topology configuration

Add one line to C:\Program Files\CSWorks\Demo\Web\LiveDataWebService\web.config:

  <liveDataTopology>
    <liveDataPartitions>
      <liveDataPartition name="partition1" primaryLiveDataServer="liveDataServer_1_primary" secondaryLiveDataServer="">
        <dataSources>
          ...
          <dataSource name="OpcMatrikon"/>
        </dataSources>
      </liveDataPartition>
    </liveDataPartitions>
  </liveDataTopology>


Now LiveData Web Service knows where to get data for OpcMatrikon data source.

Modify demo application

Run Microsoft Visual Studio and open Pipes and Tanks Demo project at C:\Program Files\CSWorks\Demo\Src\PipesAndTanksDemo\PipesAndTanksDemo.Sample.csproj. Let's make several additions to the Page.xaml file.

Add a new data item that gets data from the boolean tag configured for Matrikon OPC Server:

    <UserControl.Resources>
        ...
        <d:BoolDataItem x:Key="TestValve" Id="type in new guid here" DataSource="OpcMatrikon" TemplateName="matrikonBoolTag" Parameters=""/>
    </UserControl.Resources>



Add a few visual elements to the upper left corner of the screen and bind them to the new data item:

        <controls:TabControl...>
            <controls:TabItem Header="HMI Controls View">
                <Canvas>
                    ...
                    <Grid ...>
                          ...
                      </Grid.ColumnDefinitions>

                        <pipes:RightValve Grid.Row="0" Grid.Column="3" IsOpen="{Binding Value, Mode=OneWay, Source={StaticResource TestValve}}" OpenColor="Black" ClosedColor="Black" IsEnabled="False" Margin="-16,0,16,0"/>
                        <PipesAndTanksDemo:Wireframe Grid.Row="0" Grid.Column="3" Status="{Binding Status, Mode=OneWay, Source={StaticResource TestValve}}" Margin="-16,0,16,0"/>
                        <pipes:RightTWay Grid.Row="0" Grid.Column="2" IsFilled="{Binding Value, Mode=OneWay, Source={StaticResource TestValve}}" FillColor="Coral" Margin="-16,0,16,0"/>
                        <PipesAndTanksDemo:Wireframe Grid.Row="0" Grid.Column="2" Status="{Binding Status, Mode=OneWay, Source={StaticResource TestValve}}" Margin="-16,0,16,0"/>

                        ...


Now we have a valve and a t-way joint that supply some coral-colored liquid to Tank 1. Also, we have added a couple of Wireframe elements that will tell us if something is wrong with Matrikon boolean tag.

Time to tell DataManager that we have a new data item. Add a command to the Page_Loaded() handler in Page.xaml.cs:

        void Page_Loaded(object sender, RoutedEventArgs e)
        {
          ...
          dataManager.DataItems.Add(Resources["TestValve"] as DataItem);


Do not forget to update the id of the datamanager in the Page.xaml, otherwise LiveData Service will keep using cached list of required data items and Matrikon tag value will never be returned:

        <d:DataManager x:Key="DataManager" Id="type in new guid here" />

We are done. Build client application and run it at http://localhost/CSWorksDemo/PipesAndTanksDemo.html (or click Start menu and choose All Programs -> CSWorks ->  Client Samples -> Pipes and Tanks Demo). The left upper corner of the screen will have some new visual elements - see screenshot.

Security Issues

In the case of Matrikon Simulation OPC Server, we did not have any issues with COM/DCOM security, because Matrikon installer lets everyone access this OPC Server. You can verify it by running dcomcnfg.exe tool and checking out Matrikon Simulation OPC Server configuration - it explicitly says that "Everyone" has "Launch and Activation" and "Access" permissions for this component, see screenshot.

This cannot be considered safe practice, but makes casual user's life a bit easier. Not all OPC server providers write installers that allow the whole world to access their components. In some cases, you may get notorious "Access Denied" COM/DCOM error 0x80070005 when CSWorks accesses OPC server. Stay tuned - there will be a post dedicated to COM/DCOM security.

demo | opc
Sergey Sorokin   Monday, March 08, 2010 11:48:03 AM (Pacific Standard Time, UTC-08:00)  #     |  Comments [0]  |