OpcWeb
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
API
OpcScan
PUT api/OpcScan send an array of node ids and get their current values
OpcWrite
PUT api/OpcWrite send an array of node ids and write their values
Node
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
OpcWrite
PUT api/OpcWrite/{zone} write an array of nodes
Device
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
ScanClass
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
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)
{{#nodeValue:100}}
{{$nodeValue().Value}}
{{/nodeValue}}
Enter node id and get data
view model is
{
id:ko.observable()
}
<script data-main="/Scripts/App/SimpleApp" type="text/javascript" src="/Scripts/require.js"></script>
<h1>Simple</h1>
<div style="display:none" data-bind="attr:{style:'display:block'}">
enter id:<input type="text" data-bind="value:id" />
{{#if:id}}
<ul>
{{#nodeValue: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>
{{/nodeValue}}
</ul>
{{/if}}
</div>
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..
DeviceInterfacers.Add<OpcUADeviceInterface>("opc-ua");
Updaters are fired when subscribed to data changes
Updaters.Add(async (id, mv) => await HubContext.Clients.Group(id.ToString()).UpdateDataValue(mv));
SiGyl.Opc.SelfHost
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.
map.UseCors(CorsOptions.AllowAll);
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.
map.RunSignalR(hubConfiguration);
});
// Configure Web API for self-host.
HttpConfiguration config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
config.EnableCors(new EnableCorsAttribute("*", "*", "*"));
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{zone}/{id}",
defaults: new { zone = RouteParameter.Optional, id = RouteParameter.Optional }
);
app.UseWebApi(config);
app.UseXSockets(true);
Ioc.Standard();
Ioc.Add<LocalDeviceInterface>("local");
Ioc.Add<OpcUADeviceInterface>("opc-ua");
Ioc.Add<OpcDADeviceInterface>("opc-da");
Ioc.Build();
var container = Composable.GetExport<IXSocketServerContainer>();
container.Start();
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");
Console.ReadLine();
}
}
}
}