From 28dc6049062a8c0a0dc997659ed0ed7cfa06a8f8 Mon Sep 17 00:00:00 2001 From: Luca Domenichini Date: Thu, 14 Dec 2023 09:59:31 +0100 Subject: [PATCH 1/3] ADD reduce memory allocation for read/write operations (Snap7, SnapModBus). More work to be done on todo list. --- .../smartiot-config.json | 10 ++++----- .../Snap7Driver.cs | 15 ++++++------- .../SmartIOT.Connector.Plc.Snap7/Snap7Plc.cs | 6 +++--- .../SnapModBusDriver.cs | 21 ++++++++++--------- .../SnapModBusNode.cs | 4 ++-- LICENSE | 2 +- README.md | 3 +++ .../MainWindow.xaml | 2 -- .../MainWindow.xaml.cs | 12 ++--------- 9 files changed, 35 insertions(+), 40 deletions(-) diff --git a/Apps/SmartIOT.Connector.ConsoleApp/smartiot-config.json b/Apps/SmartIOT.Connector.ConsoleApp/smartiot-config.json index f00f7da..f8da15a 100644 --- a/Apps/SmartIOT.Connector.ConsoleApp/smartiot-config.json +++ b/Apps/SmartIOT.Connector.ConsoleApp/smartiot-config.json @@ -1,11 +1,11 @@ { "Configuration": { "ConnectorConnectionStrings": [ - "tcpServer://port=1885" + "mqttServer://ServerId=MyServer;Port=1883;Serializer=json" ], "DeviceConfigurations": [ { - "ConnectionString": "snap7://ip=localhost;rack=0;slot=2", + "ConnectionString": "snapmodbus://ip=127.0.0.1;SwapBytes=true", "DeviceId": "1", "Enabled": true, "Name": "Test Device", @@ -16,14 +16,14 @@ "TagId": "DB2", "TagType": "READ", "ByteOffset": 0, - "Size": 100, + "Size": 10, "Weight": 1 }, { "TagId": "DB3", "TagType": "WRITE", "ByteOffset": 0, - "Size": 100, + "Size": 20, "Weight": 1 } ] @@ -40,7 +40,7 @@ } }, "PrometheusConfiguration": { - "HostName": "+", + "HostName": "\u002B", "Port": 0, "Url": "metrics/", "MetricsPrefix": "smartiot_connector", diff --git a/Devices/SmartIOT.Connector.Plc.Snap7/Snap7Driver.cs b/Devices/SmartIOT.Connector.Plc.Snap7/Snap7Driver.cs index b04bf11..706208c 100644 --- a/Devices/SmartIOT.Connector.Plc.Snap7/Snap7Driver.cs +++ b/Devices/SmartIOT.Connector.Plc.Snap7/Snap7Driver.cs @@ -11,9 +11,13 @@ public class Snap7Driver : IDeviceDriver public string Name => $"{nameof(Snap7Driver)}.{Device.Name}"; public Device Device { get; } + private byte[] _tmp; + public Snap7Driver(Snap7Plc plc) { Device = plc; + + _tmp = new byte[plc.Tags.Max(x => x.TagConfiguration.Size)]; } public int StartInterface() @@ -48,25 +52,22 @@ public int ReadTag(Device device, Tag tag, byte[] data, int startOffset, int len { Snap7Plc p = (Snap7Plc)device; - var bytes = new byte[length]; - - int ret = p.ReadBytes(tag.TagId, startOffset, bytes, length); + int ret = p.ReadBytes(tag.TagId, startOffset, _tmp, length); if (ret != 0) return ret; - Array.Copy(bytes, 0, data, startOffset - tag.ByteOffset, length); + Array.Copy(_tmp, 0, data, startOffset - tag.ByteOffset, length); return 0; } public int WriteTag(Device device, Tag tag, byte[] data, int startOffset, int length) { - byte[] bytes = new byte[length]; - Array.Copy(data, startOffset - tag.ByteOffset, bytes, 0, length); + Array.Copy(data, startOffset - tag.ByteOffset, _tmp, 0, length); Snap7Plc p = (Snap7Plc)device; - return p.WriteBytes(tag.TagId, startOffset, bytes); + return p.WriteBytes(tag.TagId, startOffset, _tmp, length); } public string GetErrorMessage(int errorNumber) diff --git a/Devices/SmartIOT.Connector.Plc.Snap7/Snap7Plc.cs b/Devices/SmartIOT.Connector.Plc.Snap7/Snap7Plc.cs index eecbad3..fd6d3b2 100644 --- a/Devices/SmartIOT.Connector.Plc.Snap7/Snap7Plc.cs +++ b/Devices/SmartIOT.Connector.Plc.Snap7/Snap7Plc.cs @@ -60,11 +60,11 @@ public int ReadBytes(string tagId, int startOffset, byte[] data, int length) } } - public int WriteBytes(string tagId, int startOffset, byte[] data) + public int WriteBytes(string tagId, int startOffset, byte[] data, int length) { if (int.TryParse(tagId, out int t)) { - return S7Client.DBWrite(t, startOffset, data.Length, data); + return S7Client.DBWrite(t, startOffset, length, data); } else { @@ -72,7 +72,7 @@ public int WriteBytes(string tagId, int startOffset, byte[] data) if (match.Success) { t = int.Parse(match.Groups["tag"].Value); - return S7Client.DBWrite(t, startOffset, data.Length, data); + return S7Client.DBWrite(t, startOffset, length, data); } // other tag types can be supported here.. diff --git a/Devices/SmartIOT.Connector.Plc.SnapModBus/SnapModBusDriver.cs b/Devices/SmartIOT.Connector.Plc.SnapModBus/SnapModBusDriver.cs index 82da63b..22373a4 100644 --- a/Devices/SmartIOT.Connector.Plc.SnapModBus/SnapModBusDriver.cs +++ b/Devices/SmartIOT.Connector.Plc.SnapModBus/SnapModBusDriver.cs @@ -10,9 +10,13 @@ public class SnapModBusDriver : IDeviceDriver public string Name => $"{nameof(SnapModBusDriver)}.{Device.Name}"; public Device Device { get; } + private ushort[] _tmp; + public SnapModBusDriver(SnapModBusNode node) { Device = node; + + _tmp = new ushort[node.Tags.Max(x => x.TagConfiguration.Size / 2 + 1)]; // allocating words from bytes, +1 to handle odd sizes.. } public int StartInterface() @@ -68,17 +72,15 @@ public int ReadTag(Device device, Tag tag, byte[] data, int startOffset, int len int endAddress = (startOffset + length - 1) / 2; ushort amount = (ushort)(endAddress - address + 1); - var words = new ushort[amount]; - // address is 1-based - int ret = node.ReadRegisters((ushort)(address + 1), amount, words); + int ret = node.ReadRegisters((ushort)(address + 1), amount, _tmp); if (ret != 0) return ret; if (node.Configuration.SwapBytes) - CopyArrayAndSwapBytes(words, startOffset % 2, data, startOffset - tag.ByteOffset, length); + CopyArrayAndSwapBytes(_tmp, startOffset % 2, data, startOffset - tag.ByteOffset, length); else - Buffer.BlockCopy(words, startOffset % 2, data, startOffset - tag.ByteOffset, length); + Buffer.BlockCopy(_tmp, startOffset % 2, data, startOffset - tag.ByteOffset, length); return 0; } @@ -111,21 +113,20 @@ public int WriteTag(Device device, Tag tag, byte[] data, int startOffset, int le int startRemainder = startOffset % 2; int endRemainder = (startOffset + length) % 2; - var words = new ushort[amount]; if (node.Configuration.SwapBytes) - CopyArrayAndSwapBytes(data, startOffset - tag.ByteOffset - startRemainder, words, 0, length + startRemainder + endRemainder); + CopyArrayAndSwapBytes(data, startOffset - tag.ByteOffset - startRemainder, _tmp, 0, length + startRemainder + endRemainder); else - Buffer.BlockCopy(data, startOffset - tag.ByteOffset - startRemainder, words, 0, length + startRemainder + endRemainder); + Buffer.BlockCopy(data, startOffset - tag.ByteOffset - startRemainder, _tmp, 0, length + startRemainder + endRemainder); // address is 1-based - return node.WriteRegisters((ushort)(address + 1), words); + return node.WriteRegisters((ushort)(address + 1), _tmp, amount); } private void CopyArrayAndSwapBytes(byte[] source, int srcIndex, ushort[] target, int targetIndex, int length) { for (int i = 0; i < length; i += 2) { - target[targetIndex + i] = (ushort)((source[srcIndex + i] << 8) + source[srcIndex + i + 1]); + target[targetIndex + i / 2] = (ushort)((source[srcIndex + i] << 8) + source[srcIndex + i + 1]); } } diff --git a/Devices/SmartIOT.Connector.Plc.SnapModBus/SnapModBusNode.cs b/Devices/SmartIOT.Connector.Plc.SnapModBus/SnapModBusNode.cs index 9b7306d..d7fe4a2 100644 --- a/Devices/SmartIOT.Connector.Plc.SnapModBus/SnapModBusNode.cs +++ b/Devices/SmartIOT.Connector.Plc.SnapModBus/SnapModBusNode.cs @@ -43,8 +43,8 @@ public int ReadRegisters(ushort address, ushort amount, ushort[] data) return Client.ReadInputRegisters(Configuration.NodeId, address, amount, data); } - public int WriteRegisters(ushort address, ushort[] data) + public int WriteRegisters(ushort address, ushort[] data, ushort amount) { - return Client.WriteMultipleRegisters(Configuration.NodeId, address, (ushort)data.Length, data); + return Client.WriteMultipleRegisters(Configuration.NodeId, address, amount, data); } } diff --git a/LICENSE b/LICENSE index 82963b4..22f8648 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Luca Domenichini +Copyright (c) 2022-2023 Luca Domenichini Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 095b3ad..8c7c431 100644 --- a/README.md +++ b/README.md @@ -151,3 +151,6 @@ I will do my best to keep the interfaces stable, but there are possibilities to - [ ] The proto files should be part of SmartIOT.Connector.Messages project - [ ] Build and push docker image with github workflow - [ ] Use ActivatorUtilities from DI container to create device factories instead of default constructor +- [ ] Reduce memory allocation on read/write operations. The aim is to use the underlying byte[] as source or target directly, without further allocations. + - [ ] use existing ReadOnlySpan aware methods on S7Net + - [ ] introduce new method ReadOnlySpan aware on Snap7 and SnapModBus diff --git a/Testers/SmartIOT.Connector.MqttClient.Tester/MainWindow.xaml b/Testers/SmartIOT.Connector.MqttClient.Tester/MainWindow.xaml index d4e18c7..e3405ab 100644 --- a/Testers/SmartIOT.Connector.MqttClient.Tester/MainWindow.xaml +++ b/Testers/SmartIOT.Connector.MqttClient.Tester/MainWindow.xaml @@ -35,7 +35,5 @@