diff --git a/.github/workflows/dotnet-master.yml b/.github/workflows/dotnet-master.yml index 33f1588..01940af 100644 --- a/.github/workflows/dotnet-master.yml +++ b/.github/workflows/dotnet-master.yml @@ -4,12 +4,25 @@ on: push: branches: [ "master" ] +# master branch does a dotnet pack just to test the pipeline, without actually pushing anything to nuget.org. +# version numbers here do not matters. +env: + VERSION_MAJOR: 0 + VERSION_MINOR: 0 + VERSION_SUFFIX: "-anything" + jobs: build: runs-on: windows-latest steps: + - name: Get current date + id: date + run: echo "::set-output name=date::$(date +'%Y%m%d')" + - name: Set Version variable + id: version + run: echo "::set-output name=PRODUCT_VERSION::${{ env.VERSION_MAJOR }}.${{ env.VERSION_MINOR }}.${{ steps.date.outputs.date }}.${{ github.run_attempt }}${{ env.VERSION_SUFFIX }}" - uses: actions/checkout@v3 - name: Setup .NET6 uses: actions/setup-dotnet@v2 @@ -25,3 +38,5 @@ jobs: run: dotnet build -c Release --no-restore SmartIOT.Connector.sln - name: Test run: dotnet test -c Release --no-build --verbosity normal SmartIOT.Connector.sln + - name: Pack + run: dotnet pack -c Release /p:version=${{ steps.version.outputs.PRODUCT_VERSION }} SmartIOT.Connector.sln diff --git a/Apps/SmartIOT.Connector.App/Program.cs b/Apps/SmartIOT.Connector.App/Program.cs index 9bcc1fa..b424e97 100644 --- a/Apps/SmartIOT.Connector.App/Program.cs +++ b/Apps/SmartIOT.Connector.App/Program.cs @@ -50,6 +50,7 @@ public static void Main(string[] args) builder.Logging.AddSerilog(dispose: true); // Add SmartIOT.Connector services to the container. + builder.Services.AddSingleton(configuration.Configuration); builder.Services.AddSmartIOTConnector(cfg => { cfg.WithAutoDiscoverConnectorFactories() diff --git a/Core/SmartIOT.Connector.Core/Model/Tag.cs b/Core/SmartIOT.Connector.Core/Model/Tag.cs index 6e20227..4d09208 100644 --- a/Core/SmartIOT.Connector.Core/Model/Tag.cs +++ b/Core/SmartIOT.Connector.Core/Model/Tag.cs @@ -1,4 +1,5 @@ using SmartIOT.Connector.Core.Conf; +using System.Net.Http.Headers; namespace SmartIOT.Connector.Core.Model; @@ -39,24 +40,43 @@ public Tag(TagConfiguration tagConfiguration) IsInitialized = false; } +#pragma warning disable S2551 // Shared resources should not be used for locking: we purposefully lock on "this" to avoid races between tag-scheduler and services that request tag write. + /// - /// Questo metodo copia i dati ricevuti in argomento allo startOffset indicato. - /// Lo startOffset deve essere passato in valore assoluto, quindi se un tag inizia al byte 100 - /// e il metodo intende scrivere i byte dal 110 al 120 passerà come argomenti startOffset = 110 e un array di lunghezza = 11. - /// In ogni caso, il metodo imposta il flag di richiesta scrittura: se l'array passato in argomento non provoca nessuna variazione - /// dei dati, il flag viene alzato comunque (e come effetto verrà scritto l'intero tag). + /// This method copies the data received as an argument to the specified startOffset. + /// The startOffset must be passed as an absolute value, so if a tag starts at byte 100 + /// and the method intends to write bytes from 110 to 120, it will pass startOffset = 110 and an array of length = 11 as arguments. + /// The method performs a range check to determine the intersection of the passed range, compared to the definition + /// of the tag. If there is no intersection, false is returned and nothing is copied. + /// If there at least 1 byte of intersection, that range is copied, flag is set and true is returned. + /// Be aware the flag IsWriteSynchronizationRequested is set even if the underlying data is not changed: no byte compare is performed to check for modifications. /// - public void RequestTagWrite(byte[] data, int startOffset) + public bool TryMergeData(ReadOnlySpan data, int startOffset, int size) { -#pragma warning disable S2551 // Shared resources should not be used for locking: we purposefully lock on "this" to avoid races between tag-scheduler and services that request tag write. - lock (this) + if (startOffset + size > ByteOffset && startOffset < ByteOffset + Size) { - Array.Copy(data, 0, Data, startOffset - ByteOffset, data.Length); - IsWriteSynchronizationRequested = true; + int start = Math.Max(startOffset, ByteOffset); + int end = Math.Min(startOffset + size, ByteOffset + Data.Length); + + lock (this) + { + data.Slice(start - startOffset, end - start).CopyTo(Data.AsSpan().Slice(start - ByteOffset)); + IsWriteSynchronizationRequested = true; + } + + return true; } -#pragma warning restore S2551 // Shared resources should not be used for locking + + return false; + } + + public bool TryMergeData(ReadOnlySpan data, int startOffset) + { + return TryMergeData(data, startOffset, data.Length); } +#pragma warning restore S2551 // Shared resources should not be used for locking + /// /// This method returns a copy of the current bytes stored in the tag /// diff --git a/Core/SmartIOT.Connector.Core/SmartIotConnector.cs b/Core/SmartIOT.Connector.Core/SmartIotConnector.cs index 22258cf..6e4cdcd 100644 --- a/Core/SmartIOT.Connector.Core/SmartIotConnector.cs +++ b/Core/SmartIOT.Connector.Core/SmartIotConnector.cs @@ -271,40 +271,10 @@ public void RequestTagWrite(string deviceId, string tagId, int startOffset, byte .Where(tag => string.Equals(tag.TagId, tagId, StringComparison.InvariantCultureIgnoreCase) && tag.TagType == TagType.WRITE) ) { - lock (tag) - { - bool changes = MergeData(tag, startOffset, data); - if (changes) - tag.IsWriteSynchronizationRequested = true; - } + tag.TryMergeData(data, startOffset, data.Length); } } - private bool MergeData(Model.Tag tag, int startOffset, byte[] data) - { - var somethingChanged = false; - - if (startOffset + data.Length > tag.ByteOffset && startOffset < tag.ByteOffset + tag.Size) - { - int start = Math.Max(startOffset, tag.ByteOffset); - int end = Math.Min(startOffset + data.Length, tag.ByteOffset + tag.Data.Length); - - for (int i = start; i < end; i++) - { - byte newValue = data[i - startOffset]; - if (!somethingChanged) - { - byte oldValue = tag.Data[i - tag.ByteOffset]; - if (oldValue != newValue) - somethingChanged = true; - } - tag.Data[i - tag.ByteOffset] = newValue; - } - } - - return somethingChanged; - } - public async Task RunInitializationActionAsync(Func, IList, Task> initAction) { foreach (var scheduler in _schedulers) diff --git a/Core/SmartIOT.Connector.RestApi/Services/DeviceService.cs b/Core/SmartIOT.Connector.RestApi/Services/DeviceService.cs index 2f53873..cf6e013 100644 --- a/Core/SmartIOT.Connector.RestApi/Services/DeviceService.cs +++ b/Core/SmartIOT.Connector.RestApi/Services/DeviceService.cs @@ -105,7 +105,7 @@ public void SetTagData(string deviceId, string tagId, TagData tagData) if (tagData.StartOffset + tagData.Bytes.Length > tag.ByteOffset + tag.Size) throw new DeviceException($"Data packet is too big. Requested: [{tagData.StartOffset}..{tagData.StartOffset + tagData.Bytes.Length - 1}], accepted: [{tag.ByteOffset}..{tag.ByteOffset + tag.Size - 1}]"); - tag.RequestTagWrite(tagData.Bytes, tagData.StartOffset); + tag.TryMergeData(tagData.Bytes, tagData.StartOffset); } public Tag? GetTag(string deviceId, string tagId) diff --git a/Tests/SmartIOT.Connector.Core.Tests/TagSchedulerEngineTests.cs b/Tests/SmartIOT.Connector.Core.Tests/TagSchedulerEngineTests.cs index bad6b5a..a7905d5 100644 --- a/Tests/SmartIOT.Connector.Core.Tests/TagSchedulerEngineTests.cs +++ b/Tests/SmartIOT.Connector.Core.Tests/TagSchedulerEngineTests.cs @@ -225,7 +225,7 @@ public void Test_partial_reads() // quando viene letto il tag20, simulo la richiesta di scrittura driver.ReadTagCallback = (data, startOffset, length) => { - tag22.RequestTagWrite(new byte[] { 100, 101 }, 10); + Assert.True(tag22.TryMergeData(new byte[] { 100, 101 }, 10)); driver.ReadTagCallback = null; // autoreset alla prima invocazione }; @@ -466,7 +466,7 @@ public void Test_read_write_cycle(bool pduWriteOptimizationEnabled, int singlePd } // verifica di scrittura in errore - tag22.RequestTagWrite(new byte[] { 33 }, 10); // richiesta di modifica dati + Assert.True(tag22.TryMergeData(new byte[] { 33 }, 10)); // richiesta di modifica dati driver.WriteReturns = 1; @@ -529,8 +529,8 @@ public void Test_read_write_cycle(bool pduWriteOptimizationEnabled, int singlePd // verifica di scrittura ottimizzata a seconda della dimensione della PDU driver.ResetInvocations(); - tag22.RequestTagWrite(new byte[] { 100, 101, 102 }, 10); - tag22.RequestTagWrite(new byte[] { 200, 201 }, 20); + Assert.True(tag22.TryMergeData(new byte[] { 100, 101, 102 }, 10)); + Assert.True(tag22.TryMergeData(new byte[] { 200, 201 }, 20)); t22 = engine.GetNextTagSchedule(); Assert.Equal(tag22, t22.Tag);