Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<None Remove="Resources\logo-modbus.png" />
<None Remove="Resources\logo-mqtt.png" />
<None Remove="Resources\logo-opc-ua.png" />
<None Remove="Resources\logo-bacnet.png" />
</ItemGroup>
<ItemGroup>
<None Update="AasxPluginAssetInterfaceDesc.options.json">
Expand All @@ -33,6 +34,7 @@
<EmbeddedResource Include="Resources\logo-modbus.png" />
<EmbeddedResource Include="Resources\logo-mqtt.png" />
<EmbeddedResource Include="Resources\logo-opc-ua.png" />
<EmbeddedResource Include="Resources\logo-bacnet.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AasxIntegrationBaseGdi\AasxIntegrationBaseGdi.csproj" />
Expand All @@ -42,6 +44,7 @@
<ProjectReference Include="..\AasxPredefinedConcepts\AasxPredefinedConcepts.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BACnet" Version="3.0.1-beta6" />
<PackageReference Include="FluentModbus" Version="5.0.3" />
<PackageReference Include="MQTTnet" Version="4.1.1.318" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"UseHttp": false,
"UseModbus": false,
"UseMqtt": false,
"UseOpcUa": true
"UseOpcUa": true,
"UseBacnet": true
},
{
"IsDescription": false,
Expand Down
152 changes: 152 additions & 0 deletions src/AasxPluginAssetInterfaceDesc/AidBacnetConnection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
using AasxIntegrationBase;
using AasxIntegrationBase.AdminShellEvents;
using AasxPluginAssetInterfaceDescription;
using AasxPredefinedConcepts;
using AasxPredefinedConcepts.AssetInterfacesDescription;
using AdminShellNS;
using AdminShellNS.DiaryData;
using Extensions;
using FluentModbus;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO.BACnet;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Services.Description;
using Aas = AasCore.Aas3_0;

namespace AasxPluginAssetInterfaceDescription
{
public class AidBacnetConnection : AidBaseConnection
{
public BacnetClient Client;
private Dictionary<uint, BacnetAddress> DeviceAddresses = new Dictionary<uint, BacnetAddress>();
override public async Task<bool> Open()
{
try
{
Client = new BacnetClient();
Client.OnIam += OnIamHandler;

if (TimeOutMs >= 10)
{
Client.Timeout = (int)TimeOutMs;
}

Client.Start();
LastActive = DateTime.Now;

await Task.Yield();
return true;
}
catch (Exception ex)
{
Client = null;
return false;
}
}

private void OnIamHandler(BacnetClient sender, BacnetAddress adr, uint deviceId, uint maxAPDU, BacnetSegmentations segmentation, ushort vendorId)
{
// Store the device address from I-Am response
DeviceAddresses[deviceId] = adr;
}

override public bool IsConnected()
{
// nothing to do, this simple bacnet connection is stateless
return Client != null;
}

override public void Close()
{
// Dispose client
if (Client != null)
{
Client.Dispose();
Client = null;
}
}

override public async Task<int> UpdateItemValueAsync(AidIfxItemStatus item)
{
// access
if (item?.FormData?.Href?.HasContent() != true
|| item.FormData.Bacv_useService?.HasContent() != true
|| !IsConnected())
return 0;
int res = 0;

// GET?
if (item.FormData.Bacv_useService.Trim().ToLower() == "readproperty")
{
try
{
// Extract device ID
uint deviceId = uint.Parse(TargetUri.Host);

// Find device address
BacnetAddress deviceAddress;
if (!DeviceAddresses.ContainsKey(deviceId))
{
// Perform WhoIs request
Client.WhoIs((int)deviceId, (int)deviceId);
await Task.Delay(1000); // Wait for response
}

if (!DeviceAddresses.TryGetValue(deviceId, out deviceAddress))
{
throw new Exception($"Device {deviceId} not found.");
}


// get object type,instance, and property
var href = item.FormData.Href.TrimStart('/');
string[] mainParts = href.Split('/');
string[] objectParts = mainParts[0].Split(',');

// Create objectId
var objectType = (BacnetObjectTypes)int.Parse(objectParts[0]);
uint instance = uint.Parse(objectParts[1]);
BacnetObjectId objectId = new BacnetObjectId(objectType, instance);

// Get property from href
var propertyId = (BacnetPropertyIds)int.Parse(mainParts[1]);

// Read Property
IList<BacnetValue> values;
bool result = Client.ReadPropertyRequest(
deviceAddress,
objectId,
propertyId,
out values
);

if (result)
{
var value = values[0].Value.ToString();
item.Value = value;
res = 1;
NotifyOutputItems(item, value);
}
}
catch (Exception ex)
{
// set breakpoint in order to get failed connections!
}
}

return res;
}


}
}
42 changes: 26 additions & 16 deletions src/AasxPluginAssetInterfaceDesc/AidInterfaceStatus.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public class AidIfxItemStatus
public AnyUiUIElement RenderedUiElement = null;
}

public enum AidInterfaceTechnology { HTTP, Modbus, MQTT, OPCUA }
public enum AidInterfaceTechnology { HTTP, Modbus, MQTT, OPCUA, BACNET }

public class AidInterfaceStatus
{
Expand All @@ -120,7 +120,7 @@ public class AidInterfaceStatus
/// <summary>
/// The information items (properties, actions, events)
/// </summary>
public MultiValueDictionary<string, AidIfxItemStatus> Items =
public MultiValueDictionary<string, AidIfxItemStatus> Items =
new MultiValueDictionary<string, AidIfxItemStatus>();

/// <summary>
Expand Down Expand Up @@ -201,7 +201,7 @@ protected string ComputeKey(string key)
return key;
}

public void SetLogLine (StoredPrint.Color color, string line)
public void SetLogLine(StoredPrint.Color color, string line)
{
LogColor = color;
LogLine = line;
Expand All @@ -219,7 +219,7 @@ public void AddItem(AidIfxItemStatus item)

// compute key
var key = ComputeKey(item?.FormData?.Href);

// now add
Items.Add(key, item);
}
Expand Down Expand Up @@ -401,7 +401,7 @@ public void NotifyOutputItems(AidIfxItemStatus item, string strval)
}
}


}
}
}
Expand Down Expand Up @@ -451,7 +451,7 @@ public class AidAllInterfaceStatus
/// <summary>
/// Current setting, which technologies shall be used.
/// </summary>
public bool[] UseTech = { false, false, false, true };
public bool[] UseTech = { false, false, false, true, true };

/// <summary>
/// Will hold connections steady and continously update values, either by
Expand All @@ -464,7 +464,7 @@ public class AidAllInterfaceStatus
public AidGenericConnections<AidHttpConnection> HttpConnections =
new AidGenericConnections<AidHttpConnection>();

public AidGenericConnections<AidModbusConnection> ModbusConnections =
public AidGenericConnections<AidModbusConnection> ModbusConnections =
new AidGenericConnections<AidModbusConnection>();

public AidGenericConnections<AidMqttConnection> MqttConnections =
Expand All @@ -473,6 +473,9 @@ public class AidAllInterfaceStatus
public AidGenericConnections<AidOpcUaConnection> OpcUaConnections =
new AidGenericConnections<AidOpcUaConnection>();

public AidGenericConnections<AidBacnetConnection> BacnetConnections =
new AidGenericConnections<AidBacnetConnection>();

public AidAllInterfaceStatus(LogInstance log = null)
{
_log = log;
Expand Down Expand Up @@ -503,6 +506,7 @@ public void RememberAidSubmodel(Aas.ISubmodel sm, AssetInterfaceOptionsRecord op
UseTech[(int)AidInterfaceTechnology.Modbus] = optRec.UseModbus;
UseTech[(int)AidInterfaceTechnology.MQTT] = optRec.UseMqtt;
UseTech[(int)AidInterfaceTechnology.OPCUA] = optRec.UseOpcUa;
UseTech[(int)AidInterfaceTechnology.BACNET] = optRec.UseBacnet;
}
}

Expand Down Expand Up @@ -539,6 +543,11 @@ protected AidBaseConnection GetOrCreate(
case AidInterfaceTechnology.OPCUA:
conn = OpcUaConnections.GetOrCreate(endpointBase, log);
break;

case AidInterfaceTechnology.BACNET:
conn = BacnetConnections.GetOrCreate(endpointBase, log);
break;

}

conn.UpdateFreqMs = ifcStatus.UpdateFreqMs;
Expand Down Expand Up @@ -762,17 +771,17 @@ public async Task UpdateValuesContinousByTickAsyc()

// go thru all items (sync)
foreach (var item in ifc.Items.Values)
ifc.ValueChanges += (UInt64) ifc.Connection.UpdateItemValue(item);
ifc.ValueChanges += (UInt64)ifc.Connection.UpdateItemValue(item);

// go thru all items (async)
// see: https://www.hanselman.com/blog/parallelforeachasync-in-net-6
await Parallel.ForEachAsync(
ifc.Items.Values,
new ParallelOptions() { MaxDegreeOfParallelism = 10 },
ifc.Items.Values,
new ParallelOptions() { MaxDegreeOfParallelism = 10 },
async (item, token) =>
{
ifc.ValueChanges += (UInt64) (await ifc.Connection.UpdateItemValueAsync(item));
});
{
ifc.ValueChanges += (UInt64)(await ifc.Connection.UpdateItemValueAsync(item));
});
}
}
}
Expand Down Expand Up @@ -821,6 +830,7 @@ public void PrepareAidInformation(Aas.ISubmodel smAid, Aas.ISubmodel smMapping =
if (tech == AidInterfaceTechnology.Modbus) ifxs = dataAid?.InterfaceMODBUS;
if (tech == AidInterfaceTechnology.MQTT) ifxs = dataAid?.InterfaceMQTT;
if (tech == AidInterfaceTechnology.OPCUA) ifxs = dataAid?.InterfaceOPCUA;
if (tech == AidInterfaceTechnology.BACNET) ifxs = dataAid?.InterfaceBACNET;
if (ifxs == null || ifxs.Count < 1)
continue;
foreach (var ifx in ifxs)
Expand Down Expand Up @@ -853,7 +863,7 @@ public void PrepareAidInformation(Aas.ISubmodel smAid, Aas.ISubmodel smMapping =
FormData = propName.Forms,
Value = "???"
};


// does (some) mapping have a source with this property name?
var lst = new List<AidMappingOutputItem>();
Expand All @@ -874,7 +884,7 @@ public void PrepareAidInformation(Aas.ISubmodel smAid, Aas.ISubmodel smMapping =
aidIfx.AddItem(ifcItem);
ifcItem.MapOutputItems = lst;
}


// directly recurse?
if (propName?.Properties?.Property != null)
Expand All @@ -884,7 +894,7 @@ public void PrepareAidInformation(Aas.ISubmodel smAid, Aas.ISubmodel smMapping =
child.Forms = propName.Forms;
recurseProp(location + " . " + ifcItem.DisplayName, child);
}

};

if (ifx.InteractionMetadata?.Properties?.Property == null)
Expand Down
Loading