View on GitHub


Simple Web web api and signalr server hosting OPC client

Download this project as a .zip file Download this project as a tar.gz file


Data from Opc-ua/Opc-da (or plug in your own source) servers exposed via Web api and signalr

REST interface to add modify and remove devices nodes and scan classes, set and read nodes

subscribe to nodes with SignalR, x-sockets (or plug in your own technology)

opc-da and opc-ua access use OPCLabs opc client s/w



PUT api/OpcScan send an array of node ids and get their current values


PUT api/OpcWrite send an array of node ids and write their values


PUT api/Node/{zone} Add/modify nodes to a zone

GET api/Node/{zone} Get nodes in a zone

DELETE api/Node/{zone}?ids[0]={ids[0]}&ids[1]={ids[1]} Delete a node from a zone


PUT api/OpcWrite/{zone} write an array of nodes


PUT api/Device/{zone} Add/modify a Device in a zone(Opc server&device)

GET api/Device/{zone} Get Devices is a zone

DELETE api/Device/{zone}?ids[0]={ids[0]}&ids[1]={ids[1]} Delete a Device from a zone


PUT api/ScanClass/{zone} Add/modify scanclasses to a zone

GET api/ScanClass/{zone} Get scan classes in a zone

DELETE api/ScanClass/{zone}?ids[0]={ids[0]}&ids[1]={ids[1]} Delete scan classes from a zone


PUT api/Zone Reset all zones

PUT api/Zone/{zone} Reset a zone

GET api/Zone Get zones

Knockout binding

Knockout binding to display the value of a node on the serve live (if you don't like it write your own)


Enter node id and get data

view model is

<script data-main="/Scripts/App/SimpleApp" type="text/javascript" src="/Scripts/require.js"></script>
<div style="display:none" data-bind="attr:{style:'display:block'}">
    enter id:<input type="text" data-bind="value:id" />
        <li>The value is......{{$nodeValue().Value}}</li>
        <li>The Uom is......{{$nodeValue().Uom}}</li>
        <li>The attribute data is......{{$nodeValue().AttributeData}}</li>
        <li>The error message is......{{$nodeValue().ErrorMessage}}</li>

Implement an interface to make your own data source

    public interface IDeviceInterface: IDisposable
        IEnumerable<MonitoredValue> Read(IEnumerable<Node> values);
        IEnumerable<MonitoredValue> Write(IEnumerable<WriteValue> values);
        IDeviceInterface SetDevice(Device device);
        void Subscribe(IEnumerable<Node> nodes);
        void Unsubscribe(IEnumerable<Node> nodes);
        Action<string, object, object, string> Changed { get; set; }

and plug it in..


Updaters are fired when subscribed to data changes

    Updaters.Add(async (id, mv) => await HubContext.Clients.Group(id.ToString()).UpdateDataValue(mv));


Self hosted server converting OPC to web sockets and web api calls for use by browsers and other clients

example startup.cs

using Microsoft.AspNet.SignalR;
using Microsoft.Owin;
using Microsoft.Owin.Cors;
using Owin;
using SiGyl.Opc.Base;
using SiGyl.Opc.Base.Statuses;
using SiGyl.Opc.DeviceInterfacers.Local; 
using SiGyl.Opc.DeviceInterfacers.OpcLabs;
using SiGyl.Opc.Hubs.SignalR;
using System.Web.Http;
using System.Web.Http.Cors;
using XSockets.Core.Common.Socket;
using XSockets.Owin.Host;
using XSockets.Plugin.Framework;

[assembly: OwinStartup(typeof(SiGyl.Opc.SelfHost.Startup))]

namespace SiGyl.Opc.SelfHost
public class Startup
    // This code configures Web API. The Startup class is specified as a type
    // parameter in the WebApp.Start method.
    public void Configuration(IAppBuilder app)
        // Branch the pipeline here for requests that start with "/signalr"
        app.Map("/signalr", map =>
            // Setup the CORS middleware to run before SignalR.
            // By default this will allow all origins. You can
            // configure the set of origins and/or http verbs by
            // providing a cors options with a different policy.
            var hubConfiguration = new HubConfiguration
                // You can enable JSONP by uncommenting line below.
                // JSONP requests are insecure but some older browsers (and some
                // versions of IE) require JSONP to work cross domain
                // EnableJSONP = true
            // Run the SignalR pipeline. We're not using MapSignalR
            // since this branch already runs under the "/signalr"
            // path.

        // Configure Web API for self-host.
        HttpConfiguration config = new HttpConfiguration();

        config.EnableCors(new EnableCorsAttribute("*", "*", "*"));

            name: "DefaultApi",
            routeTemplate: "api/{controller}/{zone}/{id}",
            defaults: new { zone = RouteParameter.Optional, id = RouteParameter.Optional }



        var container = Composable.GetExport<IXSocketServerContainer>();

        Status.AddStatus(new SiGyl.Opc.SignalR.Status { StatusHub = GlobalHost.ConnectionManager.GetHubContext<StatusHub>() });
        Status.AddStatus(new ConsoleStatus());
        //Status.AddStatus(new SiGyl.Opc.XSockets.Status { Publish = StatusController.Publish });


Example Program.cs

using Microsoft.Owin.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SiGyl.Opc.SelfHost
class Program
    static void Main()
        string baseAddress = "http://localhost:4501/";

        // Start OWIN host 
        using (WebApp.Start<Startup>(url: baseAddress))
            Console.WriteLine("I am running on port 4501 press enter to finish");